Browse Source

Merge remote-tracking branch 'purple/openssl'

pull/628/head
Jeff Becker 8 years ago
parent
commit
289cae4213
No known key found for this signature in database
GPG Key ID: AB950234D6EA286B
  1. 322
      AddressBook.cpp
  2. 8
      AddressBook.h
  3. 110
      BOB.cpp
  4. 8
      BOB.h
  5. 3
      Config.cpp
  6. 5
      HTTP.cpp
  7. 67
      Reseed.cpp
  8. 25
      RouterContext.cpp
  9. 56
      RouterInfo.cpp
  10. 3
      RouterInfo.h
  11. 170
      UPnP.cpp
  12. 22
      UPnP.h
  13. 2
      docs/configuration.md
  14. 27
      util.cpp
  15. 7
      util.h

322
AddressBook.cpp

@ -11,6 +11,7 @@
#include "Identity.h" #include "Identity.h"
#include "FS.h" #include "FS.h"
#include "Log.h" #include "Log.h"
#include "HTTP.h"
#include "NetDb.h" #include "NetDb.h"
#include "ClientContext.h" #include "ClientContext.h"
#include "AddressBook.h" #include "AddressBook.h"
@ -339,12 +340,12 @@ namespace client
std::ifstream f (i2p::fs::DataDirPath("hosts.txt"), std::ifstream::in); // in text mode std::ifstream f (i2p::fs::DataDirPath("hosts.txt"), std::ifstream::in); // in text mode
if (f.is_open ()) if (f.is_open ())
{ {
LoadHostsFromStream (f); LoadHostsFromStream (f, false);
m_IsLoaded = true; m_IsLoaded = true;
} }
} }
bool AddressBook::LoadHostsFromStream (std::istream& f) bool AddressBook::LoadHostsFromStream (std::istream& f, bool is_update)
{ {
std::unique_lock<std::mutex> l(m_AddressBookMutex); std::unique_lock<std::mutex> l(m_AddressBookMutex);
int numAddresses = 0; int numAddresses = 0;
@ -365,17 +366,18 @@ namespace client
std::string addr = s.substr(pos); std::string addr = s.substr(pos);
auto ident = std::make_shared<i2p::data::IdentityEx> (); auto ident = std::make_shared<i2p::data::IdentityEx> ();
if (ident->FromBase64(addr)) if (!ident->FromBase64(addr)) {
{
m_Addresses[name] = ident->GetIdentHash ();
m_Storage->AddAddress (ident);
numAddresses++;
}
else
{
LogPrint (eLogError, "Addressbook: malformed address ", addr, " for ", name); LogPrint (eLogError, "Addressbook: malformed address ", addr, " for ", name);
incomplete = f.eof (); incomplete = f.eof ();
continue;
} }
numAddresses++;
if (m_Addresses.count(name) > 0)
continue; /* already exists */
m_Addresses[name] = ident->GetIdentHash ();
m_Storage->AddAddress (ident);
if (is_update)
LogPrint(eLogInfo, "Addressbook: added new host: ", name);
} }
else else
incomplete = f.eof (); incomplete = f.eof ();
@ -516,14 +518,15 @@ namespace client
if (!m_DefaultSubscription) if (!m_DefaultSubscription)
m_DefaultSubscription.reset (new AddressBookSubscription (*this, DEFAULT_SUBSCRIPTION_ADDRESS)); m_DefaultSubscription.reset (new AddressBookSubscription (*this, DEFAULT_SUBSCRIPTION_ADDRESS));
m_IsDownloading = true; m_IsDownloading = true;
m_DefaultSubscription->CheckSubscription (); m_DefaultSubscription->CheckUpdates ();
} }
else if (!m_Subscriptions.empty ()) else if (!m_Subscriptions.empty ())
{ {
// pick random subscription // pick random subscription
auto ind = rand () % m_Subscriptions.size(); auto ind = rand () % m_Subscriptions.size();
m_IsDownloading = true; m_IsDownloading = true;
m_Subscriptions[ind]->CheckSubscription (); std::thread load_hosts(&AddressBookSubscription::CheckUpdates, m_Subscriptions[ind]);
load_hosts.detach(); // TODO: use join
} }
} }
else else
@ -569,7 +572,7 @@ namespace client
ident = FindAddress (address.substr (dot + 1)); ident = FindAddress (address.substr (dot + 1));
if (!ident) if (!ident)
{ {
LogPrint (eLogError, "AddressBook: Can't find domain for ", address); LogPrint (eLogError, "Addressbook: Can't find domain for ", address);
return; return;
} }
@ -585,7 +588,7 @@ namespace client
std::unique_lock<std::mutex> l(m_LookupsMutex); std::unique_lock<std::mutex> l(m_LookupsMutex);
m_Lookups[nonce] = address; m_Lookups[nonce] = address;
} }
LogPrint (eLogDebug, "AddressBook: Lookup of ", address, " to ", ident->ToBase32 (), " nonce=", nonce); LogPrint (eLogDebug, "Addressbook: Lookup of ", address, " to ", ident->ToBase32 (), " nonce=", nonce);
size_t len = address.length () + 9; size_t len = address.length () + 9;
uint8_t * buf = new uint8_t[len]; uint8_t * buf = new uint8_t[len];
memset (buf, 0, 4); memset (buf, 0, 4);
@ -602,11 +605,11 @@ namespace client
{ {
if (len < 44) if (len < 44)
{ {
LogPrint (eLogError, "AddressBook: Lookup response is too short ", len); LogPrint (eLogError, "Addressbook: Lookup response is too short ", len);
return; return;
} }
uint32_t nonce = bufbe32toh (buf + 4); uint32_t nonce = bufbe32toh (buf + 4);
LogPrint (eLogDebug, "AddressBook: Lookup response received from ", from.GetIdentHash ().ToBase32 (), " nonce=", nonce); LogPrint (eLogDebug, "Addressbook: Lookup response received from ", from.GetIdentHash ().ToBase32 (), " nonce=", nonce);
std::string address; std::string address;
{ {
std::unique_lock<std::mutex> l(m_LookupsMutex); std::unique_lock<std::mutex> l(m_LookupsMutex);
@ -629,168 +632,153 @@ namespace client
{ {
} }
void AddressBookSubscription::CheckSubscription () void AddressBookSubscription::CheckUpdates ()
{ {
std::thread load_hosts(&AddressBookSubscription::Request, this); bool result = MakeRequest ();
load_hosts.detach(); // TODO: use join m_Book.DownloadComplete (result, m_Ident, m_Etag, m_LastModified);
} }
void AddressBookSubscription::Request () bool AddressBookSubscription::MakeRequest ()
{ {
i2p::http::URL url;
// must be run in separate thread // must be run in separate thread
LogPrint (eLogInfo, "Addressbook: Downloading hosts database from ", m_Link, " ETag: ", m_Etag, " Last-Modified: ", m_LastModified); LogPrint (eLogInfo, "Addressbook: Downloading hosts database from ", m_Link);
bool success = false; if (!url.parse(m_Link)) {
i2p::util::http::url u (m_Link); LogPrint(eLogError, "Addressbook: failed to parse url: ", m_Link);
i2p::data::IdentHash ident; return false;
if (m_Book.GetIdentHash (u.host_, ident)) }
if (!m_Book.GetIdentHash (url.host, m_Ident)) {
LogPrint (eLogError, "Addressbook: Can't resolve ", url.host);
return false;
}
/* this code block still needs some love */
std::condition_variable newDataReceived;
std::mutex newDataReceivedMutex;
auto leaseSet = i2p::client::context.GetSharedLocalDestination ()->FindLeaseSet (m_Ident);
if (!leaseSet)
{ {
if (!m_Etag.length ()) std::unique_lock<std::mutex> l(newDataReceivedMutex);
{ i2p::client::context.GetSharedLocalDestination ()->RequestDestination (m_Ident,
// load ETag [&newDataReceived, &leaseSet](std::shared_ptr<i2p::data::LeaseSet> ls)
m_Book.GetEtag (ident, m_Etag, m_LastModified); {
LogPrint (eLogInfo, "Addressbook: set ", m_Link, " ETag: ", m_Etag, " Last-Modified: ", m_LastModified); leaseSet = ls;
} newDataReceived.notify_all ();
std::condition_variable newDataReceived; });
std::mutex newDataReceivedMutex; if (newDataReceived.wait_for (l, std::chrono::seconds (SUBSCRIPTION_REQUEST_TIMEOUT)) == std::cv_status::timeout)
auto leaseSet = i2p::client::context.GetSharedLocalDestination ()->FindLeaseSet (ident);
if (!leaseSet)
{ {
std::unique_lock<std::mutex> l(newDataReceivedMutex); LogPrint (eLogError, "Addressbook: Subscription LeaseSet request timeout expired");
i2p::client::context.GetSharedLocalDestination ()->RequestDestination (ident, i2p::client::context.GetSharedLocalDestination ()->CancelDestinationRequest (m_Ident);
[&newDataReceived, &leaseSet](std::shared_ptr<i2p::data::LeaseSet> ls) return false;
{
leaseSet = ls;
newDataReceived.notify_all ();
});
if (newDataReceived.wait_for (l, std::chrono::seconds (SUBSCRIPTION_REQUEST_TIMEOUT)) == std::cv_status::timeout)
{
LogPrint (eLogError, "Addressbook: Subscription LeaseSet request timeout expired");
i2p::client::context.GetSharedLocalDestination ()->CancelDestinationRequest (ident);
}
} }
if (leaseSet) }
{ if (!leaseSet) {
std::stringstream request, response; /* still no leaseset found */
// standard header LogPrint (eLogError, "Addressbook: LeaseSet for address ", url.host, " not found");
request << "GET " << u.path_ << " HTTP/1.1\r\n" return false;
<< "Host: " << u.host_ << "\r\n" }
<< "Accept: */*\r\n" if (m_Etag.empty() && m_LastModified.empty()) {
<< "User-Agent: Wget/1.11.4\r\n" m_Book.GetEtag (m_Ident, m_Etag, m_LastModified);
//<< "Accept-Encoding: gzip\r\n" LogPrint (eLogDebug, "Addressbook: loaded for ", url.host, ": ETag: ", m_Etag, ", Last-Modified: ", m_LastModified);
<< "X-Accept-Encoding: x-i2p-gzip;q=1.0, identity;q=0.5, deflate;q=0, gzip;q=0, *;q=0\r\n" }
<< "Connection: close\r\n"; /* save url parts for later use */
if (m_Etag.length () > 0) // etag std::string dest_host = url.host;
request << i2p::util::http::IF_NONE_MATCH << ": " << m_Etag << "\r\n"; int dest_port = url.port ? url.port : 80;
if (m_LastModified.length () > 0) // if-modfief-since /* create http request & send it */
request << i2p::util::http::IF_MODIFIED_SINCE << ": " << m_LastModified << "\r\n"; i2p::http::HTTPReq req;
request << "\r\n"; // end of header req.add_header("Host", dest_host);
auto stream = i2p::client::context.GetSharedLocalDestination ()->CreateStream (leaseSet, u.port_); req.add_header("User-Agent", "Wget/1.11.4");
stream->Send ((uint8_t *)request.str ().c_str (), request.str ().length ()); req.add_header("Connection", "close");
if (!m_Etag.empty())
uint8_t buf[4096]; req.add_header("If-None-Match", m_Etag);
bool end = false; if (!m_LastModified.empty())
while (!end) req.add_header("If-Modified-Since", m_LastModified);
{ /* convert url to relative */
stream->AsyncReceive (boost::asio::buffer (buf, 4096), url.schema = "";
[&](const boost::system::error_code& ecode, std::size_t bytes_transferred) url.host = "";
{ req.uri = url.to_string();
if (bytes_transferred) auto stream = i2p::client::context.GetSharedLocalDestination ()->CreateStream (leaseSet, dest_port);
response.write ((char *)buf, bytes_transferred); std::string request = req.to_string();
if (ecode == boost::asio::error::timed_out || !stream->IsOpen ()) stream->Send ((const uint8_t *) request.data(), request.length());
end = true; /* read response */
newDataReceived.notify_all (); std::string response;
}, uint8_t recv_buf[4096];
30); // wait for 30 seconds bool end = false;
std::unique_lock<std::mutex> l(newDataReceivedMutex); while (!end) {
if (newDataReceived.wait_for (l, std::chrono::seconds (SUBSCRIPTION_REQUEST_TIMEOUT)) == std::cv_status::timeout) stream->AsyncReceive (boost::asio::buffer (recv_buf, 4096),
LogPrint (eLogError, "Addressbook: subscriptions request timeout expired"); [&](const boost::system::error_code& ecode, std::size_t bytes_transferred)
}
// process remaining buffer
while (size_t len = stream->ReadSome (buf, 4096))
response.write ((char *)buf, len);
// parse response
std::string version;
response >> version; // HTTP version
int status = 0;
response >> status; // status
if (status == 200) // OK
{
bool isChunked = false, isGzip = false;
m_Etag = ""; m_LastModified = "";
std::string header, statusMessage;
std::getline (response, statusMessage);
// read until new line meaning end of header
while (!response.eof () && header != "\r")
{
std::getline (response, header);
if (response.fail ()) break;
auto colon = header.find (':');
if (colon != std::string::npos)
{
std::string field = header.substr (0, colon);
boost::to_lower (field); // field are not case-sensitive
colon++;
header.resize (header.length () - 1); // delete \r
if (field == i2p::util::http::ETAG)
m_Etag = header.substr (colon + 1);
else if (field == i2p::util::http::LAST_MODIFIED)
m_LastModified = header.substr (colon + 1);
else if (field == i2p::util::http::TRANSFER_ENCODING)
isChunked = !header.compare (colon + 1, std::string::npos, "chunked");
else if (field == i2p::util::http::CONTENT_ENCODING)
isGzip = !header.compare (colon + 1, std::string::npos, "gzip") ||
!header.compare (colon + 1, std::string::npos, "x-i2p-gzip");
}
}
LogPrint (eLogInfo, "Addressbook: received ", m_Link, " ETag: ", m_Etag, " Last-Modified: ", m_LastModified);
if (!response.eof () && !response.fail ())
{
if (!isChunked)
success = ProcessResponse (response, isGzip);
else
{
// merge chunks
std::stringstream merged;
i2p::util::http::MergeChunkedResponse (response, merged);
success = ProcessResponse (merged, isGzip);
}
}
}
else if (status == 304)
{ {
success = true; if (bytes_transferred)
LogPrint (eLogInfo, "Addressbook: no updates from ", m_Link); response.append ((char *)recv_buf, bytes_transferred);
} if (ecode == boost::asio::error::timed_out || !stream->IsOpen ())
else end = true;
LogPrint (eLogWarning, "Adressbook: HTTP response ", status); newDataReceived.notify_all ();
} },
else 30); // wait for 30 seconds
LogPrint (eLogError, "Addressbook: address ", u.host_, " not found"); std::unique_lock<std::mutex> l(newDataReceivedMutex);
if (newDataReceived.wait_for (l, std::chrono::seconds (SUBSCRIPTION_REQUEST_TIMEOUT)) == std::cv_status::timeout)
LogPrint (eLogError, "Addressbook: subscriptions request timeout expired");
}
// process remaining buffer
while (size_t len = stream->ReadSome (recv_buf, sizeof(recv_buf))) {
response.append ((char *)recv_buf, len);
}
/* parse response */
i2p::http::HTTPRes res;
int res_head_len = res.parse(response);
if (res_head_len < 0) {
LogPrint(eLogError, "Addressbook: can't parse http response from ", dest_host);
return false;
} }
else if (res_head_len == 0) {
LogPrint (eLogError, "Addressbook: Can't resolve ", u.host_); LogPrint(eLogError, "Addressbook: incomplete http response from ", dest_host, ", interrupted by timeout");
return false;
if (!success) }
LogPrint (eLogError, "Addressbook: download hosts.txt from ", m_Link, " failed"); /* assert: res_head_len > 0 */
response.erase(0, res_head_len);
m_Book.DownloadComplete (success, ident, m_Etag, m_LastModified); if (res.code == 304) {
} LogPrint (eLogInfo, "Addressbook: no updates from ", dest_host, ", code 304");
return false;
bool AddressBookSubscription::ProcessResponse (std::stringstream& s, bool isGzip) }
{ if (res.code != 200) {
if (isGzip) LogPrint (eLogWarning, "Adressbook: can't get updates from ", dest_host, ", response code ", res.code);
{ return false;
std::stringstream uncompressed; }
int len = res.content_length();
if (response.empty()) {
LogPrint(eLogError, "Addressbook: empty response from ", dest_host, ", expected ", len, " bytes");
return false;
}
if (len > 0 && len != (int) response.length()) {
LogPrint(eLogError, "Addressbook: response size mismatch, expected: ", response.length(), ", got: ", len, "bytes");
return false;
}
/* assert: res.code == 200 */
auto it = res.headers.find("ETag");
if (it != res.headers.end()) {
m_Etag = it->second;
}
it = res.headers.find("If-Modified-Since");
if (it != res.headers.end()) {
m_LastModified = it->second;
}
if (res.is_chunked()) {
std::stringstream in(response), out;
i2p::http::MergeChunkedResponse (in, out);
response = out.str();
} else if (res.is_gzipped()) {
std::stringstream out;
i2p::data::GzipInflator inflator; i2p::data::GzipInflator inflator;
inflator.Inflate (s, uncompressed); inflator.Inflate ((const uint8_t *) response.data(), response.length(), out);
if (!uncompressed.fail ()) if (out.fail()) {
return m_Book.LoadHostsFromStream (uncompressed); LogPrint(eLogError, "Addressbook: can't gunzip http response");
else
return false; return false;
}
response = out.str();
} }
else std::stringstream ss(response);
return m_Book.LoadHostsFromStream (s); LogPrint (eLogInfo, "Addressbook: got update from ", dest_host);
m_Book.LoadHostsFromStream (ss, true);
return true;
} }
AddressResolver::AddressResolver (std::shared_ptr<ClientDestination> destination): AddressResolver::AddressResolver (std::shared_ptr<ClientDestination> destination):
@ -821,7 +809,7 @@ namespace client
{ {
if (len < 9 || len < buf[8] + 9U) if (len < 9 || len < buf[8] + 9U)
{ {
LogPrint (eLogError, "AddressBook: Address request is too short ", len); LogPrint (eLogError, "Addressbook: Address request is too short ", len);
return; return;
} }
// read requested address // read requested address
@ -829,7 +817,7 @@ namespace client
char address[255]; char address[255];
memcpy (address, buf + 9, l); memcpy (address, buf + 9, l);
address[l] = 0; address[l] = 0;
LogPrint (eLogDebug, "AddressBook: Address request ", address); LogPrint (eLogDebug, "Addressbook: Address request ", address);
// send response // send response
uint8_t response[44]; uint8_t response[44];
memset (response, 0, 4); // reserved memset (response, 0, 4); // reserved

8
AddressBook.h

@ -70,7 +70,7 @@ namespace client
void InsertAddress (const std::string& address, const std::string& base64); // for jump service void InsertAddress (const std::string& address, const std::string& base64); // for jump service
void InsertAddress (std::shared_ptr<const i2p::data::IdentityEx> address); void InsertAddress (std::shared_ptr<const i2p::data::IdentityEx> address);
bool LoadHostsFromStream (std::istream& f); bool LoadHostsFromStream (std::istream& f, bool is_update);
void DownloadComplete (bool success, const i2p::data::IdentHash& subscription, const std::string& etag, const std::string& lastModified); void DownloadComplete (bool success, const i2p::data::IdentHash& subscription, const std::string& etag, const std::string& lastModified);
//This method returns the ".b32.i2p" address //This method returns the ".b32.i2p" address
std::string ToAddress(const i2p::data::IdentHash& ident) { return GetB32Address(ident); } std::string ToAddress(const i2p::data::IdentHash& ident) { return GetB32Address(ident); }
@ -112,17 +112,17 @@ namespace client
public: public:
AddressBookSubscription (AddressBook& book, const std::string& link); AddressBookSubscription (AddressBook& book, const std::string& link);
void CheckSubscription (); void CheckUpdates ();
private: private:
void Request (); bool MakeRequest ();
bool ProcessResponse (std::stringstream& s, bool isGzip = false);
private: private:
AddressBook& m_Book; AddressBook& m_Book;
std::string m_Link, m_Etag, m_LastModified; std::string m_Link, m_Etag, m_LastModified;
i2p::data::IdentHash m_Ident;
// m_Etag must be surrounded by "" // m_Etag must be surrounded by ""
}; };

110
BOB.cpp

@ -202,8 +202,8 @@ namespace client
} }
BOBCommandSession::BOBCommandSession (BOBCommandChannel& owner): BOBCommandSession::BOBCommandSession (BOBCommandChannel& owner):
m_Owner (owner), m_Socket (m_Owner.GetService ()), m_Timer (m_Owner.GetService ()), m_Owner (owner), m_Socket (m_Owner.GetService ()),
m_ReceiveBufferOffset (0), m_IsOpen (true), m_IsQuiet (false), m_ReceiveBufferOffset (0), m_IsOpen (true), m_IsQuiet (false), m_IsActive (false),
m_InPort (0), m_OutPort (0), m_CurrentDestination (nullptr) m_InPort (0), m_OutPort (0), m_CurrentDestination (nullptr)
{ {
} }
@ -354,6 +354,11 @@ namespace client
void BOBCommandSession::StartCommandHandler (const char * operand, size_t len) void BOBCommandSession::StartCommandHandler (const char * operand, size_t len)
{ {
LogPrint (eLogDebug, "BOB: start ", m_Nickname); LogPrint (eLogDebug, "BOB: start ", m_Nickname);
if (m_IsActive)
{
SendReplyError ("tunnel is active");
return;
}
if (!m_CurrentDestination) if (!m_CurrentDestination)
{ {
m_CurrentDestination = new BOBDestination (i2p::client::context.CreateNewLocalDestination (m_Keys, true, &m_Options)); m_CurrentDestination = new BOBDestination (i2p::client::context.CreateNewLocalDestination (m_Keys, true, &m_Options));
@ -364,41 +369,27 @@ namespace client
if (m_OutPort && !m_Address.empty ()) if (m_OutPort && !m_Address.empty ())
m_CurrentDestination->CreateOutboundTunnel (m_Address, m_OutPort, m_IsQuiet); m_CurrentDestination->CreateOutboundTunnel (m_Address, m_OutPort, m_IsQuiet);
m_CurrentDestination->Start (); m_CurrentDestination->Start ();
if (m_CurrentDestination->GetLocalDestination ()->IsReady ()) SendReplyOK ("Tunnel starting");
SendReplyOK ("tunnel starting"); m_IsActive = true;
else
{
m_Timer.expires_from_now (boost::posix_time::seconds(BOB_SESSION_READINESS_CHECK_INTERVAL));
m_Timer.async_wait (std::bind (&BOBCommandSession::HandleSessionReadinessCheckTimer,
shared_from_this (), std::placeholders::_1));
}
} }
void BOBCommandSession::HandleSessionReadinessCheckTimer (const boost::system::error_code& ecode) void BOBCommandSession::StopCommandHandler (const char * operand, size_t len)
{ {
if (ecode != boost::asio::error::operation_aborted) LogPrint (eLogDebug, "BOB: stop ", m_Nickname);
if (!m_IsActive)
{ {
if (m_CurrentDestination->GetLocalDestination ()->IsReady ()) SendReplyError ("tunnel is inactive");
SendReplyOK ("tunnel starting"); return;
else
{
m_Timer.expires_from_now (boost::posix_time::seconds(BOB_SESSION_READINESS_CHECK_INTERVAL));
m_Timer.async_wait (std::bind (&BOBCommandSession::HandleSessionReadinessCheckTimer,
shared_from_this (), std::placeholders::_1));
}
} }
}
void BOBCommandSession::StopCommandHandler (const char * operand, size_t len)
{
auto dest = m_Owner.FindDestination (m_Nickname); auto dest = m_Owner.FindDestination (m_Nickname);
if (dest) if (dest)
{ {
dest->StopTunnels (); dest->StopTunnels ();
SendReplyOK ("tunnel stopping"); SendReplyOK ("Tunnel stopping");
} }
else else
SendReplyError ("tunnel not found"); SendReplyError ("tunnel not found");
m_IsActive = false;
} }
void BOBCommandSession::SetNickCommandHandler (const char * operand, size_t len) void BOBCommandSession::SetNickCommandHandler (const char * operand, size_t len)
@ -406,7 +397,7 @@ namespace client
LogPrint (eLogDebug, "BOB: setnick ", operand); LogPrint (eLogDebug, "BOB: setnick ", operand);
m_Nickname = operand; m_Nickname = operand;
std::string msg ("Nickname set to "); std::string msg ("Nickname set to ");
msg += operand; msg += m_Nickname;
SendReplyOK (msg.c_str ()); SendReplyOK (msg.c_str ());
} }
@ -418,12 +409,15 @@ namespace client
{ {
m_Keys = m_CurrentDestination->GetKeys (); m_Keys = m_CurrentDestination->GetKeys ();
m_Nickname = operand; m_Nickname = operand;
}
if (m_Nickname == operand)
{
std::string msg ("Nickname set to "); std::string msg ("Nickname set to ");
msg += operand; msg += m_Nickname;
SendReplyOK (msg.c_str ()); SendReplyOK (msg.c_str ());
} }
else else
SendReplyError ("tunnel not found"); SendReplyError ("no nickname has been set");
} }
void BOBCommandSession::NewkeysCommandHandler (const char * operand, size_t len) void BOBCommandSession::NewkeysCommandHandler (const char * operand, size_t len)
@ -463,7 +457,10 @@ namespace client
{ {
LogPrint (eLogDebug, "BOB: outport ", operand); LogPrint (eLogDebug, "BOB: outport ", operand);
m_OutPort = boost::lexical_cast<int>(operand); m_OutPort = boost::lexical_cast<int>(operand);
SendReplyOK ("outbound port set"); if (m_OutPort >= 0)
SendReplyOK ("outbound port set");
else
SendReplyError ("port out of range");
} }
void BOBCommandSession::InhostCommandHandler (const char * operand, size_t len) void BOBCommandSession::InhostCommandHandler (const char * operand, size_t len)
@ -477,14 +474,27 @@ namespace client
{ {
LogPrint (eLogDebug, "BOB: inport ", operand); LogPrint (eLogDebug, "BOB: inport ", operand);
m_InPort = boost::lexical_cast<int>(operand); m_InPort = boost::lexical_cast<int>(operand);
SendReplyOK ("inbound port set"); if (m_InPort >= 0)
SendReplyOK ("inbound port set");
else
SendReplyError ("port out of range");
} }
void BOBCommandSession::QuietCommandHandler (const char * operand, size_t len) void BOBCommandSession::QuietCommandHandler (const char * operand, size_t len)
{ {
LogPrint (eLogDebug, "BOB: quiet"); LogPrint (eLogDebug, "BOB: quiet");
m_IsQuiet = true; if (m_Nickname.length () > 0)
SendReplyOK ("quiet"); {
if (!m_IsActive)
{
m_IsQuiet = true;
SendReplyOK ("Quiet set");
}
else
SendReplyError ("tunnel is active");
}
else
SendReplyError ("no nickname has been set");
} }
void BOBCommandSession::LookupCommandHandler (const char * operand, size_t len) void BOBCommandSession::LookupCommandHandler (const char * operand, size_t len)
@ -519,6 +529,7 @@ namespace client
{ {
LogPrint (eLogDebug, "BOB: clear"); LogPrint (eLogDebug, "BOB: clear");
m_Owner.DeleteDestination (m_Nickname); m_Owner.DeleteDestination (m_Nickname);
m_Nickname = "";
SendReplyOK ("cleared"); SendReplyOK ("cleared");
} }
@ -537,15 +548,47 @@ namespace client
const char * value = strchr (operand, '='); const char * value = strchr (operand, '=');
if (value) if (value)
{ {
std::string msg ("option ");
*(const_cast<char *>(value)) = 0; *(const_cast<char *>(value)) = 0;
m_Options[operand] = value + 1; m_Options[operand] = value + 1;
msg += operand;
*(const_cast<char *>(value)) = '='; *(const_cast<char *>(value)) = '=';
SendReplyOK ("option"); msg += " set to ";
msg += value;
SendReplyOK (msg.c_str ());
} }
else else
SendReplyError ("malformed"); SendReplyError ("malformed");
} }
void BOBCommandSession::StatusCommandHandler (const char * operand, size_t len)
{
LogPrint (eLogDebug, "BOB: status ", operand);
if (m_Nickname == operand)
{
std::stringstream s;
s << "DATA"; s << " NICKNAME:"; s << m_Nickname;
if (m_CurrentDestination->GetLocalDestination ()->IsReady ())
s << " STARTING:false RUNNING:true STOPPING:false";
else
s << " STARTING:true RUNNING:false STOPPING:false";
s << " KEYS: true"; s << " QUIET:"; s << (m_IsQuiet ? "true":"false");
if (m_InPort)
{
s << " INPORT:" << m_InPort;
s << " INHOST:" << (m_Address.length () > 0 ? m_Address : "127.0.0.1");
}
if (m_OutPort)
{
s << " OUTPORT:" << m_OutPort;
s << " OUTHOST:" << (m_Address.length () > 0 ? m_Address : "127.0.0.1");
}
SendReplyOK (s.str().c_str());
}
else
SendReplyError ("no nickname has been set");
}
BOBCommandChannel::BOBCommandChannel (const std::string& address, int port): BOBCommandChannel::BOBCommandChannel (const std::string& address, int port):
m_IsRunning (false), m_Thread (nullptr), m_IsRunning (false), m_Thread (nullptr),
m_Acceptor (m_Service, boost::asio::ip::tcp::endpoint(boost::asio::ip::address::from_string(address), port)) m_Acceptor (m_Service, boost::asio::ip::tcp::endpoint(boost::asio::ip::address::from_string(address), port))
@ -570,6 +613,7 @@ namespace client
m_CommandHandlers[BOB_COMMAND_CLEAR] = &BOBCommandSession::ClearCommandHandler; m_CommandHandlers[BOB_COMMAND_CLEAR] = &BOBCommandSession::ClearCommandHandler;
m_CommandHandlers[BOB_COMMAND_LIST] = &BOBCommandSession::ListCommandHandler; m_CommandHandlers[BOB_COMMAND_LIST] = &BOBCommandSession::ListCommandHandler;
m_CommandHandlers[BOB_COMMAND_OPTION] = &BOBCommandSession::OptionCommandHandler; m_CommandHandlers[BOB_COMMAND_OPTION] = &BOBCommandSession::OptionCommandHandler;
m_CommandHandlers[BOB_COMMAND_STATUS] = &BOBCommandSession::StatusCommandHandler;
} }
BOBCommandChannel::~BOBCommandChannel () BOBCommandChannel::~BOBCommandChannel ()

8
BOB.h

@ -36,14 +36,13 @@ namespace client
const char BOB_COMMAND_CLEAR[] = "clear"; const char BOB_COMMAND_CLEAR[] = "clear";
const char BOB_COMMAND_LIST[] = "list"; const char BOB_COMMAND_LIST[] = "list";
const char BOB_COMMAND_OPTION[] = "option"; const char BOB_COMMAND_OPTION[] = "option";
const char BOB_COMMAND_STATUS[] = "status";
const char BOB_VERSION[] = "BOB 00.00.10\nOK\n"; const char BOB_VERSION[] = "BOB 00.00.10\nOK\n";
const char BOB_REPLY_OK[] = "OK %s\n"; const char BOB_REPLY_OK[] = "OK %s\n";
const char BOB_REPLY_ERROR[] = "ERROR %s\n"; const char BOB_REPLY_ERROR[] = "ERROR %s\n";
const char BOB_DATA[] = "NICKNAME %s\n"; const char BOB_DATA[] = "NICKNAME %s\n";
const int BOB_SESSION_READINESS_CHECK_INTERVAL = 5; // in seconds
class BOBI2PTunnel: public I2PService class BOBI2PTunnel: public I2PService
{ {
public: public:
@ -170,12 +169,12 @@ namespace client
void ClearCommandHandler (const char * operand, size_t len); void ClearCommandHandler (const char * operand, size_t len);
void ListCommandHandler (const char * operand, size_t len); void ListCommandHandler (const char * operand, size_t len);
void OptionCommandHandler (const char * operand, size_t len); void OptionCommandHandler (const char * operand, size_t len);
void StatusCommandHandler (const char * operand, size_t len);
private: private:
void Receive (); void Receive ();
void HandleReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred); void HandleReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred);
void HandleSessionReadinessCheckTimer (const boost::system::error_code& ecode);
void Send (size_t len); void Send (size_t len);
void HandleSent (const boost::system::error_code& ecode, std::size_t bytes_transferred); void HandleSent (const boost::system::error_code& ecode, std::size_t bytes_transferred);
@ -187,10 +186,9 @@ namespace client
BOBCommandChannel& m_Owner; BOBCommandChannel& m_Owner;
boost::asio::ip::tcp::socket m_Socket; boost::asio::ip::tcp::socket m_Socket;
boost::asio::deadline_timer m_Timer;
char m_ReceiveBuffer[BOB_COMMAND_BUFFER_SIZE + 1], m_SendBuffer[BOB_COMMAND_BUFFER_SIZE + 1]; char m_ReceiveBuffer[BOB_COMMAND_BUFFER_SIZE + 1], m_SendBuffer[BOB_COMMAND_BUFFER_SIZE + 1];
size_t m_ReceiveBufferOffset; size_t m_ReceiveBufferOffset;
bool m_IsOpen, m_IsQuiet; bool m_IsOpen, m_IsQuiet, m_IsActive;
std::string m_Nickname, m_Address; std::string m_Nickname, m_Address;
int m_InPort, m_OutPort; int m_InPort, m_OutPort;
i2p::data::PrivateKeys m_Keys; i2p::data::PrivateKeys m_Keys;

3
Config.cpp

@ -128,12 +128,13 @@ namespace config {
; ;
bool upnp_default = false; bool upnp_default = false;
#if (defined(USE_UPNP) && ((defined(WIN32) && defined(USE_WIN32_APP)) || defined(ANDROID))) #if (defined(USE_UPNP) && (defined(WIN32_APP) || defined(ANDROID)))
upnp_default = true; // enable UPNP for windows GUI and android by default upnp_default = true; // enable UPNP for windows GUI and android by default
#endif #endif
options_description upnp("UPnP options"); options_description upnp("UPnP options");
upnp.add_options() upnp.add_options()
("upnp.enabled", value<bool>()->default_value(upnp_default), "Enable or disable UPnP: automatic port forwarding") ("upnp.enabled", value<bool>()->default_value(upnp_default), "Enable or disable UPnP: automatic port forwarding")
("upnp.name", value<std::string>()->default_value("I2Pd"), "Name i2pd appears in UPnP forwardings list")
; ;
options_description precomputation("Precomputation options"); options_description precomputation("Precomputation options");

5
HTTP.cpp

@ -279,10 +279,7 @@ namespace http {
} }
bool HTTPRes::is_gzipped() { bool HTTPRes::is_gzipped() {
auto it = headers.find("x-i2p-gzip"); auto it = headers.find("Content-Encoding");
if (it == headers.end())
return true; /* i2p-specific header */
it = headers.find("Content-Encoding");
if (it == headers.end()) if (it == headers.end())
return false; /* no header */ return false; /* no header */
if (it->second.find("gzip") != std::string::npos) if (it->second.find("gzip") != std::string::npos)

67
Reseed.cpp

@ -14,6 +14,7 @@
#include "Log.h" #include "Log.h"
#include "Identity.h" #include "Identity.h"
#include "NetDb.h" #include "NetDb.h"
#include "HTTP.h"
#include "util.h" #include "util.h"
namespace i2p namespace i2p
@ -372,13 +373,19 @@ namespace data
std::string Reseeder::HttpsRequest (const std::string& address) std::string Reseeder::HttpsRequest (const std::string& address)
{ {
i2p::util::http::url u(address); i2p::http::URL url;
if (u.port_ == 80) u.port_ = 443; if (!url.parse(address)) {
LogPrint(eLogError, "Reseed: failed to parse url: ", address);
return "";
}
url.schema = "https";
if (!url.port)
url.port = 443;
boost::asio::io_service service; boost::asio::io_service service;
boost::system::error_code ecode; boost::system::error_code ecode;
auto it = boost::asio::ip::tcp::resolver(service).resolve ( auto it = boost::asio::ip::tcp::resolver(service).resolve (
boost::asio::ip::tcp::resolver::query (u.host_, std::to_string (u.port_)), ecode); boost::asio::ip::tcp::resolver::query (url.host, std::to_string(url.port)), ecode);
if (!ecode) if (!ecode)
{ {
boost::asio::ssl::context ctx(service, boost::asio::ssl::context::sslv23); boost::asio::ssl::context ctx(service, boost::asio::ssl::context::sslv23);
@ -390,32 +397,52 @@ namespace data
s.handshake (boost::asio::ssl::stream_base::client, ecode); s.handshake (boost::asio::ssl::stream_base::client, ecode);
if (!ecode) if (!ecode)
{ {
LogPrint (eLogInfo, "Reseed: Connected to ", u.host_, ":", u.port_); LogPrint (eLogDebug, "Reseed: Connected to ", url.host, ":", url.port);
// send request i2p::http::HTTPReq req;
std::stringstream ss; req.uri = url.to_string();
ss << "GET " << u.path_ << " HTTP/1.1\r\nHost: " << u.host_ req.add_header("User-Agent", "Wget/1.11.4");
<< "\r\nAccept: */*\r\n" << "User-Agent: Wget/1.11.4\r\n" << "Connection: close\r\n\r\n"; req.add_header("Connection", "close");
s.write_some (boost::asio::buffer (ss.str ())); s.write_some (boost::asio::buffer (req.to_string()));
// read response // read response
std::stringstream rs; std::stringstream rs;
char response[1024]; size_t l = 0; char recv_buf[1024]; size_t l = 0;
do do {
{ l = s.read_some (boost::asio::buffer (recv_buf, sizeof(recv_buf)), ecode);
l = s.read_some (boost::asio::buffer (response, 1024), ecode); if (l) rs.write (recv_buf, l);
if (l) rs.write (response, l); } while (!ecode && l);
}
while (!ecode && l);
// process response // process response
return i2p::util::http::GetHttpContent (rs); std::string data = rs.str();
i2p::http::HTTPRes res;
int len = res.parse(data);
if (len <= 0) {
LogPrint(eLogWarning, "Reseed: incomplete/broken response from ", url.host);
return "";
}
if (res.code != 200) {
LogPrint(eLogError, "Reseed: failed to reseed from ", url.host, ", http code ", res.code);
return "";
}
data.erase(0, len); /* drop http headers from response */
LogPrint(eLogDebug, "Reseed: got ", data.length(), " bytes of data from ", url.host);
if (res.is_chunked()) {
std::stringstream in(data), out;
if (!i2p::http::MergeChunkedResponse(in, out)) {
LogPrint(eLogWarning, "Reseed: failed to merge chunked response from ", url.host);
return "";
}
LogPrint(eLogDebug, "Reseed: got ", data.length(), "(", out.tellg(), ") bytes of data from ", url.host);
data = out.str();
}
return data;
} }
else else
LogPrint (eLogError, "Reseed: SSL handshake failed: ", ecode.message ()); LogPrint (eLogError, "Reseed: SSL handshake failed: ", ecode.message ());
} }
else else
LogPrint (eLogError, "Reseed: Couldn't connect to ", u.host_, ": ", ecode.message ()); LogPrint (eLogError, "Reseed: Couldn't connect to ", url.host, ": ", ecode.message ());
} }
else else
LogPrint (eLogError, "Reseed: Couldn't resolve address ", u.host_, ": ", ecode.message ()); LogPrint (eLogError, "Reseed: Couldn't resolve address ", url.host, ": ", ecode.message ());
return ""; return "";
} }
} }

25
RouterContext.cpp

@ -51,7 +51,16 @@ namespace i2p
port = rand () % (30777 - 9111) + 9111; // I2P network ports range port = rand () % (30777 - 9111) + 9111; // I2P network ports range
bool ipv4; i2p::config::GetOption("ipv4", ipv4); bool ipv4; i2p::config::GetOption("ipv4", ipv4);
bool ipv6; i2p::config::GetOption("ipv6", ipv6); bool ipv6; i2p::config::GetOption("ipv6", ipv6);
std::string host = i2p::util::config::GetHost(ipv4, ipv6); bool nat; i2p::config::GetOption("nat", nat);
std::string ifname; i2p::config::GetOption("ifname", ifname);
std::string host = ipv6 ? "::" : "127.0.0.1";
if (nat) {
if (!i2p::config::IsDefault("host"))
i2p::config::GetOption("host", host);
} else if (!ifname.empty()) {
/* bind to interface, we have no NAT so set external address too */
host = i2p::util::net::GetInterfaceAddress(ifname, ipv6).to_string();
}
routerInfo.AddSSUAddress (host.c_str(), port, routerInfo.GetIdentHash ()); routerInfo.AddSSUAddress (host.c_str(), port, routerInfo.GetIdentHash ());
routerInfo.AddNTCPAddress (host.c_str(), port); routerInfo.AddNTCPAddress (host.c_str(), port);
routerInfo.SetCaps (i2p::data::RouterInfo::eReachable | routerInfo.SetCaps (i2p::data::RouterInfo::eReachable |
@ -224,11 +233,12 @@ namespace i2p
m_RouterInfo.SetCaps (i2p::data::RouterInfo::eUnreachable | i2p::data::RouterInfo::eSSUTesting); // LU, B m_RouterInfo.SetCaps (i2p::data::RouterInfo::eUnreachable | i2p::data::RouterInfo::eSSUTesting); // LU, B
// remove NTCP address // remove NTCP address
auto& addresses = m_RouterInfo.GetAddresses (); auto& addresses = m_RouterInfo.GetAddresses ();
for (size_t i = 0; i < addresses.size (); i++) for (auto it = addresses.begin (); it != addresses.end (); it++)
{ {
if (addresses[i]->transportStyle == i2p::data::RouterInfo::eTransportNTCP) if ((*it)->transportStyle == i2p::data::RouterInfo::eTransportNTCP &&
(*it)->host.is_v4 ())
{ {
addresses.erase (addresses.begin () + i); addresses.erase (it);
break; break;
} }
} }
@ -253,12 +263,13 @@ namespace i2p
// insert NTCP back // insert NTCP back
auto& addresses = m_RouterInfo.GetAddresses (); auto& addresses = m_RouterInfo.GetAddresses ();
for (size_t i = 0; i < addresses.size (); i++) for (auto addr : addresses)
{ {
if (addresses[i]->transportStyle == i2p::data::RouterInfo::eTransportSSU) if (addr->transportStyle == i2p::data::RouterInfo::eTransportSSU &&
addr->host.is_v4 ())
{ {
// insert NTCP address with host/port from SSU // insert NTCP address with host/port from SSU
m_RouterInfo.AddNTCPAddress (addresses[i]->host.to_string ().c_str (), addresses[i]->port); m_RouterInfo.AddNTCPAddress (addr->host.to_string ().c_str (), addr->port);
break; break;
} }
} }

56
RouterInfo.cpp

@ -700,28 +700,14 @@ namespace data
{ {
if (IsV6 ()) if (IsV6 ())
{ {
// NTCP m_SupportedTransports &= ~(eNTCPV6 | eSSUV6);
m_SupportedTransports &= ~eNTCPV6; for (auto it = m_Addresses->begin (); it != m_Addresses->end ();)
for (size_t i = 0; i < m_Addresses->size (); i++)
{ {
if ((*m_Addresses)[i]->transportStyle == i2p::data::RouterInfo::eTransportNTCP && auto addr = *it;
(*m_Addresses)[i]->host.is_v6 ()) if (addr->host.is_v6 ())
{ it = m_Addresses->erase (it);
m_Addresses->erase (m_Addresses->begin () + i); else
break; it++;
}
}
// SSU
m_SupportedTransports &= ~eSSUV6;
for (size_t i = 0; i < m_Addresses->size (); i++)
{
if ((*m_Addresses)[i]->transportStyle == i2p::data::RouterInfo::eTransportSSU &&
(*m_Addresses)[i]->host.is_v6 ())
{
m_Addresses->erase (m_Addresses->begin () + i);
break;
}
} }
} }
} }
@ -730,28 +716,14 @@ namespace data
{ {
if (IsV4 ()) if (IsV4 ())
{ {
// NTCP m_SupportedTransports &= ~(eNTCPV4 | eSSUV4);
m_SupportedTransports &= ~eNTCPV4; for (auto it = m_Addresses->begin (); it != m_Addresses->end ();)
for (size_t i = 0; i < m_Addresses->size (); i++)
{ {
if ((*m_Addresses)[i]->transportStyle == i2p::data::RouterInfo::eTransportNTCP && auto addr = *it;
(*m_Addresses)[i]->host.is_v4 ()) if (addr->host.is_v4 ())
{ it = m_Addresses->erase (it);
m_Addresses->erase (m_Addresses->begin () + i); else
break; it++;
}
}
// SSU
m_SupportedTransports &= ~eSSUV4;
for (size_t i = 0; i < m_Addresses->size (); i++)
{
if ((*m_Addresses)[i]->transportStyle == i2p::data::RouterInfo::eTransportSSU &&
(*m_Addresses)[i]->host.is_v4 ())
{
m_Addresses->erase (m_Addresses->begin () + i);
break;
}
} }
} }
} }

3
RouterInfo.h

@ -5,6 +5,7 @@
#include <string> #include <string>
#include <map> #include <map>
#include <vector> #include <vector>
#include <list>
#include <iostream> #include <iostream>
#include <boost/asio.hpp> #include <boost/asio.hpp>
#include "Identity.h" #include "Identity.h"
@ -105,7 +106,7 @@ namespace data
return !(*this == other); return !(*this == other);
} }
}; };
typedef std::vector<std::shared_ptr<Address> > Addresses; typedef std::list<std::shared_ptr<Address> > Addresses;
RouterInfo (); RouterInfo ();
RouterInfo (const std::string& fullPath); RouterInfo (const std::string& fullPath);

170
UPnP.cpp

@ -13,6 +13,7 @@
#include "NetDb.h" #include "NetDb.h"
#include "util.h" #include "util.h"
#include "RouterInfo.h" #include "RouterInfo.h"
#include "Config.h"
#include <miniupnpc/miniupnpc.h> #include <miniupnpc/miniupnpc.h>
#include <miniupnpc/upnpcommands.h> #include <miniupnpc/upnpcommands.h>
@ -21,49 +22,54 @@ namespace i2p
{ {
namespace transport namespace transport
{ {
UPnP::UPnP () : m_Thread (nullptr) UPnP::UPnP () : m_IsRunning(false), m_Thread (nullptr), m_Timer (m_Service)
{ {
} }
void UPnP::Stop () void UPnP::Stop ()
{ {
LogPrint(eLogInfo, "UPnP: stopping"); if (m_IsRunning)
if (m_Thread) {
{ LogPrint(eLogInfo, "UPnP: stopping");
m_Thread->join (); m_IsRunning = false;
delete m_Thread; m_Timer.cancel ();
m_Thread = nullptr; m_Service.stop ();
} if (m_Thread)
{
m_Thread->join ();
m_Thread.reset (nullptr);
}
CloseMapping ();
Close ();
}
} }
void UPnP::Start() void UPnP::Start()
{ {
m_IsRunning = true;
LogPrint(eLogInfo, "UPnP: starting"); LogPrint(eLogInfo, "UPnP: starting");
m_Thread = new std::thread (std::bind (&UPnP::Run, this)); m_Service.post (std::bind (&UPnP::Discover, this));
m_Thread.reset (new std::thread (std::bind (&UPnP::Run, this)));
} }
UPnP::~UPnP () UPnP::~UPnP ()
{ {
Stop ();
} }
void UPnP::Run () void UPnP::Run ()
{ {
const std::vector<std::shared_ptr<i2p::data::RouterInfo::Address> > a = context.GetRouterInfo().GetAddresses(); while (m_IsRunning)
for (auto address : a) {
{ try
if (!address->host.is_v6 ()) {
{ m_Service.run ();
Discover (); }
if (address->transportStyle == data::RouterInfo::eTransportSSU ) catch (std::exception& ex)
{ {
TryPortMapping (I2P_UPNP_UDP, address->port); LogPrint (eLogError, "UPnP: runtime exception: ", ex.what ());
} }
else if (address->transportStyle == data::RouterInfo::eTransportNTCP ) }
{
TryPortMapping (I2P_UPNP_TCP, address->port);
}
}
}
} }
void UPnP::Discover () void UPnP::Discover ()
@ -87,73 +93,74 @@ namespace transport
} }
else else
{ {
if (m_externalIPAddress[0]) if (!m_externalIPAddress[0])
{
LogPrint (eLogDebug, "UPnP: ExternalIPAddress is ", m_externalIPAddress);
i2p::context.UpdateAddress (boost::asio::ip::address::from_string (m_externalIPAddress));
return;
}
else
{ {
LogPrint (eLogError, "UPnP: GetExternalIPAddress() failed."); LogPrint (eLogError, "UPnP: GetExternalIPAddress() failed.");
return; return;
} }
} }
} }
else
{
LogPrint (eLogError, "UPnP: GetValidIGD() failed.");
return;
}
// UPnP discovered
LogPrint (eLogDebug, "UPnP: ExternalIPAddress is ", m_externalIPAddress);
i2p::context.UpdateAddress (boost::asio::ip::address::from_string (m_externalIPAddress));
// port mapping
PortMapping ();
} }
void UPnP::TryPortMapping (int type, int port) void UPnP::PortMapping ()
{ {
std::string strType, strPort (std::to_string (port)); auto a = context.GetRouterInfo().GetAddresses();
switch (type) for (auto address : a)
{ {
case I2P_UPNP_TCP: if (!address->host.is_v6 ())
strType = "TCP"; TryPortMapping (address);
break;
case I2P_UPNP_UDP:
default:
strType = "UDP";
} }
m_Timer.expires_from_now (boost::posix_time::minutes(20)); // every 20 minutes
m_Timer.async_wait ([this](const boost::system::error_code& ecode)
{
if (ecode != boost::asio::error::operation_aborted)
PortMapping ();
});
}
void UPnP::CloseMapping ()
{
auto a = context.GetRouterInfo().GetAddresses();
for (auto address : a)
{
if (!address->host.is_v6 ())
CloseMapping (address);
}
}
void UPnP::TryPortMapping (std::shared_ptr<i2p::data::RouterInfo::Address> address)
{
std::string strType (GetProto (address)), strPort (std::to_string (address->port));
int r; int r;
std::string strDesc = "I2Pd"; std::string strDesc; i2p::config::GetOption("upnp.name", strDesc);
try { r = UPNP_AddPortMapping (m_upnpUrls.controlURL, m_upnpData.first.servicetype, strPort.c_str (), strPort.c_str (), m_NetworkAddr, strDesc.c_str (), strType.c_str (), 0, "0");
for (;;) { if (r!=UPNPCOMMAND_SUCCESS)
r = UPNP_AddPortMapping (m_upnpUrls.controlURL, m_upnpData.first.servicetype, strPort.c_str (), strPort.c_str (), m_NetworkAddr, strDesc.c_str (), strType.c_str (), 0, "0"); {
if (r!=UPNPCOMMAND_SUCCESS) LogPrint (eLogError, "UPnP: AddPortMapping (", m_NetworkAddr, ":", strPort, ") failed with code ", r);
{ return;
LogPrint (eLogError, "UPnP: AddPortMapping (", m_NetworkAddr, ":", strPort, ") failed with code ", r);
return;
}
else
{
LogPrint (eLogDebug, "UPnP: Port Mapping successful. (", m_NetworkAddr ,":", strPort, " type ", strType, " -> ", m_externalIPAddress ,":", strPort ,")");
return;
}
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) else
{ {
CloseMapping(type, port); LogPrint (eLogDebug, "UPnP: Port Mapping successful. (", m_NetworkAddr ,":", strPort, " type ", strType, " -> ", m_externalIPAddress ,":", strPort ,")");
Close(); return;
throw;
} }
} }
void UPnP::CloseMapping (int type, int port) void UPnP::CloseMapping (std::shared_ptr<i2p::data::RouterInfo::Address> address)
{ {
std::string strType, strPort (std::to_string (port)); std::string strType (GetProto (address)), strPort (std::to_string (address->port));
switch (type)
{
case I2P_UPNP_TCP:
strType = "TCP";
break;
case I2P_UPNP_UDP:
default:
strType = "UDP";
}
int r = 0; int r = 0;
r = UPNP_DeletePortMapping (m_upnpUrls.controlURL, m_upnpData.first.servicetype, strPort.c_str (), strType.c_str (), 0); r = UPNP_DeletePortMapping (m_upnpUrls.controlURL, m_upnpData.first.servicetype, strPort.c_str (), strType.c_str (), 0);
LogPrint (eLogError, "UPnP: DeletePortMapping() returned : ", r); LogPrint (eLogError, "UPnP: DeletePortMapping() returned : ", r);
@ -165,6 +172,19 @@ namespace transport
m_Devlist = 0; m_Devlist = 0;
FreeUPNPUrls (&m_upnpUrls); FreeUPNPUrls (&m_upnpUrls);
} }
std::string UPnP::GetProto (std::shared_ptr<i2p::data::RouterInfo::Address> address)
{
switch (address->transportStyle)
{
case i2p::data::RouterInfo::eTransportNTCP:
return "TCP";
break;
case i2p::data::RouterInfo::eTransportSSU:
default:
return "UDP";
}
}
} }
} }
#else /* USE_UPNP */ #else /* USE_UPNP */

22
UPnP.h

@ -4,6 +4,7 @@
#ifdef USE_UPNP #ifdef USE_UPNP
#include <string> #include <string>
#include <thread> #include <thread>
#include <memory>
#include <miniupnpc/miniwget.h> #include <miniupnpc/miniwget.h>
#include <miniupnpc/miniupnpc.h> #include <miniupnpc/miniupnpc.h>
@ -14,9 +15,6 @@
#include "util.h" #include "util.h"
#define I2P_UPNP_TCP 1
#define I2P_UPNP_UDP 2
namespace i2p namespace i2p
{ {
namespace transport namespace transport
@ -32,13 +30,23 @@ namespace transport
void Start (); void Start ();
void Stop (); void Stop ();
void Discover ();
void TryPortMapping (int type, int port);
void CloseMapping (int type, int port);
private: private:
void Discover ();
void PortMapping ();
void TryPortMapping (std::shared_ptr<i2p::data::RouterInfo::Address> address);
void CloseMapping ();
void CloseMapping (std::shared_ptr<i2p::data::RouterInfo::Address> address);
void Run (); void Run ();
std::string GetProto (std::shared_ptr<i2p::data::RouterInfo::Address> address);
private:
std::thread * m_Thread; bool m_IsRunning;
std::unique_ptr<std::thread> m_Thread;
boost::asio::io_service m_Service;
boost::asio::deadline_timer m_Timer;
struct UPNPUrls m_upnpUrls; struct UPNPUrls m_upnpUrls;
struct IGDdatas m_upnpData; struct IGDdatas m_upnpData;

2
docs/configuration.md

@ -69,7 +69,7 @@ All options below still possible in cmdline, but better write it in config file:
* --i2pcontrol.enabled= - If I2P control is enabled. false by default * --i2pcontrol.enabled= - If I2P control is enabled. false by default
* --upnp.enabled= - Enable or disable UPnP, false by default for CLI and true for GUI (Windows, Android) * --upnp.enabled= - Enable or disable UPnP, false by default for CLI and true for GUI (Windows, Android)
* --upnp.name= - Name i2pd appears in UPnP forwardings list. I2Pd by default
* --precomputation.elgamal= - Use ElGamal precomputated tables. false for x64 and true for other platforms by default * --precomputation.elgamal= - Use ElGamal precomputated tables. false for x64 and true for other platforms by default
* --limits.transittunnels= - Override maximum number of transit tunnels. 2500 by default * --limits.transittunnels= - Override maximum number of transit tunnels. 2500 by default

27
util.cpp

@ -7,7 +7,6 @@
#include <set> #include <set>
#include <boost/asio.hpp> #include <boost/asio.hpp>
#include <boost/lexical_cast.hpp> #include <boost/lexical_cast.hpp>
#include "Config.h"
#include "util.h" #include "util.h"
#include "Log.h" #include "Log.h"
@ -460,31 +459,5 @@ namespace net
#endif #endif
} }
} }
namespace config
{
std::string GetHost(bool ipv4, bool ipv6)
{
std::string host;
if(ipv6)
host = "::";
else if(ipv4)
host = "127.0.0.1";
bool nat; i2p::config::GetOption("nat", nat);
if (nat)
{
if (!i2p::config::IsDefault("host"))
i2p::config::GetOption("host", host);
}
else
{
// we are not behind nat
std::string ifname; i2p::config::GetOption("ifname", ifname);
if (ifname.size())
host = i2p::util::net::GetInterfaceAddress(ifname, ipv6).to_string(); // bind to interface, we have no NAT so set external address too
}
return host;
}
} // config
} // util } // util
} // i2p } // i2p

7
util.h

@ -68,14 +68,7 @@ namespace util
int GetMTU (const boost::asio::ip::address& localAddress); int GetMTU (const boost::asio::ip::address& localAddress);
const boost::asio::ip::address GetInterfaceAddress(const std::string & ifname, bool ipv6=false); const boost::asio::ip::address GetInterfaceAddress(const std::string & ifname, bool ipv6=false);
} }
namespace config
{
/** get the host to use from out config, for use in RouterContext.cpp */
std::string GetHost(bool ipv4=true, bool ipv6=true);
}
} }
} }
#endif #endif

Loading…
Cancel
Save