Browse Source

Merge remote-tracking branch 'purple/openssl'

pull/436/head
Jeff Becker 9 years ago
parent
commit
897cc7d355
No known key found for this signature in database
GPG Key ID: AB950234D6EA286B
  1. 190
      AddressBook.cpp
  2. 32
      AddressBook.h
  3. 4
      Base.cpp
  4. 1
      Config.cpp
  5. 2
      Daemon.cpp
  6. 7
      FS.cpp
  7. 17
      FS.h
  8. 2
      HTTPProxy.cpp
  9. 10
      HTTPServer.cpp
  10. 16
      NTCPSession.cpp
  11. 41
      NetDb.cpp
  12. 3
      NetDb.h
  13. 5
      Reseed.cpp
  14. 36
      RouterContext.cpp
  15. 83
      RouterInfo.cpp
  16. 12
      RouterInfo.h
  17. 1
      SSUSession.cpp
  18. 50
      Transports.cpp
  19. 14
      UPnP.cpp
  20. 5
      UPnP.h
  21. BIN
      Win32/Anke_2200px.jpg
  22. BIN
      Win32/Anke_700px.bmp
  23. 6
      Win32/Resource.rc
  24. 50
      Win32/Win32App.cpp
  25. BIN
      Win32/anke.ico
  26. 1
      Win32/resource.h
  27. 75
      docs/build_notes_cross.md
  28. 2
      docs/build_requirements.md
  29. 19
      docs/configuration.md
  30. 111
      docs/i2pd.conf
  31. 8
      util.cpp
  32. 2
      version.h

190
AddressBook.cpp

@ -24,7 +24,7 @@ namespace client
{ {
private: private:
i2p::fs::HashedStorage storage; i2p::fs::HashedStorage storage;
std::string indexPath; std::string etagsPath, indexPath, localPath;
public: public:
AddressBookFilesystemStorage (): storage("addressbook", "b", "", "b32") {}; AddressBookFilesystemStorage (): storage("addressbook", "b", "", "b32") {};
@ -34,14 +34,34 @@ namespace client
bool Init (); bool Init ();
int Load (std::map<std::string, i2p::data::IdentHash>& addresses); int Load (std::map<std::string, i2p::data::IdentHash>& addresses);
int LoadLocal (std::map<std::string, i2p::data::IdentHash>& addresses);
int Save (const std::map<std::string, i2p::data::IdentHash>& addresses); int Save (const std::map<std::string, i2p::data::IdentHash>& addresses);
void SaveEtag (const i2p::data::IdentHash& subsciption, const std::string& etag, const std::string& lastModified);
bool GetEtag (const i2p::data::IdentHash& subscription, std::string& etag, std::string& lastModified);
private:
int LoadFromFile (const std::string& filename, std::map<std::string, i2p::data::IdentHash>& addresses); // returns -1 if can't open file, otherwise number of records
}; };
bool AddressBookFilesystemStorage::Init() bool AddressBookFilesystemStorage::Init()
{ {
storage.SetPlace(i2p::fs::GetDataDir()); storage.SetPlace(i2p::fs::GetDataDir());
indexPath = storage.GetRoot() + i2p::fs::dirSep + "addresses.csv"; // init storage
return storage.Init(i2p::data::GetBase32SubstitutionTable(), 32); if (storage.Init(i2p::data::GetBase32SubstitutionTable(), 32))
{
// init ETags
etagsPath = i2p::fs::StorageRootPath (storage, "etags");
if (!i2p::fs::Exists (etagsPath))
i2p::fs::CreateDirectory (etagsPath);
// init address files
indexPath = i2p::fs::StorageRootPath (storage, "addresses.csv");
localPath = i2p::fs::StorageRootPath (storage, "local.csv");
return true;
}
return false;
} }
std::shared_ptr<const i2p::data::IdentityEx> AddressBookFilesystemStorage::GetAddress (const i2p::data::IdentHash& ident) const std::shared_ptr<const i2p::data::IdentityEx> AddressBookFilesystemStorage::GetAddress (const i2p::data::IdentHash& ident) const
@ -87,24 +107,18 @@ namespace client
storage.Remove( ident.ToBase32() ); storage.Remove( ident.ToBase32() );
} }
int AddressBookFilesystemStorage::Load (std::map<std::string, i2p::data::IdentHash>& addresses) int AddressBookFilesystemStorage::LoadFromFile (const std::string& filename, std::map<std::string, i2p::data::IdentHash>& addresses)
{ {
int num = 0; int num = 0;
std::string s; std::ifstream f (filename, std::ifstream::in); // in text mode
std::ifstream f (indexPath, std::ifstream::in); // in text mode if (!f) return -1;
if (f.is_open ()) {
LogPrint(eLogInfo, "Addressbook: using index file ", indexPath);
} else {
LogPrint(eLogWarning, "Addressbook: Can't open ", indexPath);
return 0;
}
addresses.clear (); addresses.clear ();
while (!f.eof ()) { while (!f.eof ())
{
std::string s;
getline(f, s); getline(f, s);
if (!s.length()) if (!s.length()) continue; // skip empty line
continue; // skip empty line
std::size_t pos = s.find(','); std::size_t pos = s.find(',');
if (pos != std::string::npos) if (pos != std::string::npos)
@ -118,8 +132,28 @@ namespace client
num++; num++;
} }
} }
return num;
}
int AddressBookFilesystemStorage::Load (std::map<std::string, i2p::data::IdentHash>& addresses)
{
int num = LoadFromFile (indexPath, addresses);
if (num < 0)
{
LogPrint(eLogWarning, "Addressbook: Can't open ", indexPath);
return 0;
}
LogPrint(eLogInfo, "Addressbook: using index file ", indexPath);
LogPrint (eLogInfo, "Addressbook: ", num, " addresses loaded from storage"); LogPrint (eLogInfo, "Addressbook: ", num, " addresses loaded from storage");
return num;
}
int AddressBookFilesystemStorage::LoadLocal (std::map<std::string, i2p::data::IdentHash>& addresses)
{
int num = LoadFromFile (localPath, addresses);
if (num < 0) return 0;
LogPrint (eLogInfo, "Addressbook: ", num, " local addresses loaded");
return num; return num;
} }
@ -146,6 +180,28 @@ namespace client
return num; return num;
} }
void AddressBookFilesystemStorage::SaveEtag (const i2p::data::IdentHash& subscription, const std::string& etag, const std::string& lastModified)
{
std::string fname = etagsPath + i2p::fs::dirSep + subscription.ToBase32 () + ".txt";
std::ofstream f (fname, std::ofstream::out | std::ofstream::trunc);
if (f)
{
f << etag << std::endl;
f<< lastModified << std::endl;
}
}
bool AddressBookFilesystemStorage::GetEtag (const i2p::data::IdentHash& subscription, std::string& etag, std::string& lastModified)
{
std::string fname = etagsPath + i2p::fs::dirSep + subscription.ToBase32 () + ".txt";
std::ifstream f (fname, std::ofstream::in);
if (!f || f.eof ()) return false;
std::getline (f, etag);
if (f.eof ()) return false;
std::getline (f, lastModified);
return true;
}
//--------------------------------------------------------------------- //---------------------------------------------------------------------
AddressBook::AddressBook (): m_Storage(new AddressBookFilesystemStorage), m_IsLoaded (false), m_IsDownloading (false), AddressBook::AddressBook (): m_Storage(new AddressBookFilesystemStorage), m_IsLoaded (false), m_IsDownloading (false),
m_DefaultSubscription (nullptr), m_SubscriptionsUpdateTimer (nullptr) m_DefaultSubscription (nullptr), m_SubscriptionsUpdateTimer (nullptr)
@ -274,6 +330,7 @@ namespace client
LoadHostsFromStream (f); LoadHostsFromStream (f);
m_IsLoaded = true; m_IsLoaded = true;
} }
LoadLocal ();
} }
void AddressBook::LoadHostsFromStream (std::istream& f) void AddressBook::LoadHostsFromStream (std::istream& f)
@ -337,7 +394,49 @@ namespace client
LogPrint (eLogError, "Addressbook: subscriptions already loaded"); LogPrint (eLogError, "Addressbook: subscriptions already loaded");
} }
void AddressBook::DownloadComplete (bool success) void AddressBook::LoadLocal ()
{
std::map<std::string, i2p::data::IdentHash> localAddresses;
m_Storage->LoadLocal (localAddresses);
for (auto it: localAddresses)
{
auto dot = it.first.find ('.');
if (dot != std::string::npos)
{
auto domain = it.first.substr (dot + 1);
auto it1 = m_Addresses.find (domain); // find domain in our addressbook
if (it1 != m_Addresses.end ())
{
auto dest = context.FindLocalDestination (it1->second);
if (dest)
{
// address is ours
std::shared_ptr<AddressResolver> resolver;
auto it2 = m_Resolvers.find (it1->second);
if (it2 != m_Resolvers.end ())
resolver = it2->second; // resolver exists
else
{
// create new resolver
resolver = std::make_shared<AddressResolver>(dest);
m_Resolvers.insert (std::make_pair(it1->second, resolver));
}
resolver->AddAddress (it.first, it.second);
}
}
}
}
}
bool AddressBook::GetEtag (const i2p::data::IdentHash& subscription, std::string& etag, std::string& lastModified)
{
if (m_Storage)
return m_Storage->GetEtag (subscription, etag, lastModified);
else
return false;
}
void AddressBook::DownloadComplete (bool success, const i2p::data::IdentHash& subscription, const std::string& etag, const std::string& lastModified)
{ {
m_IsDownloading = false; m_IsDownloading = false;
int nextUpdateTimeout = CONTINIOUS_SUBSCRIPTION_RETRY_TIMEOUT; int nextUpdateTimeout = CONTINIOUS_SUBSCRIPTION_RETRY_TIMEOUT;
@ -348,6 +447,7 @@ namespace client
nextUpdateTimeout = CONTINIOUS_SUBSCRIPTION_UPDATE_TIMEOUT; nextUpdateTimeout = CONTINIOUS_SUBSCRIPTION_UPDATE_TIMEOUT;
else else
m_IsLoaded = true; m_IsLoaded = true;
if (m_Storage) m_Storage->SaveEtag (subscription, etag, lastModified);
} }
if (m_SubscriptionsUpdateTimer) if (m_SubscriptionsUpdateTimer)
{ {
@ -438,6 +538,12 @@ namespace client
i2p::data::IdentHash ident; i2p::data::IdentHash ident;
if (m_Book.GetIdentHash (u.host_, ident)) if (m_Book.GetIdentHash (u.host_, ident))
{ {
if (!m_Etag.length ())
{
// load ETag
m_Book.GetEtag (ident, m_Etag, m_LastModified);
LogPrint (eLogInfo, "Addressbook: set ", m_Link, " ETag: ", m_Etag, " Last-Modified: ", m_LastModified);
}
std::condition_variable newDataReceived; std::condition_variable newDataReceived;
std::mutex newDataReceivedMutex; std::mutex newDataReceivedMutex;
auto leaseSet = i2p::client::context.GetSharedLocalDestination ()->FindLeaseSet (ident); auto leaseSet = i2p::client::context.GetSharedLocalDestination ()->FindLeaseSet (ident);
@ -468,7 +574,7 @@ namespace client
<< "X-Accept-Encoding: x-i2p-gzip;q=1.0, identity;q=0.5, deflate;q=0, gzip;q=0, *;q=0\r\n" << "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"; << "Connection: close\r\n";
if (m_Etag.length () > 0) // etag if (m_Etag.length () > 0) // etag
request << i2p::util::http::IF_NONE_MATCH << ": \"" << m_Etag << "\"\r\n"; request << i2p::util::http::IF_NONE_MATCH << ": " << m_Etag << "\r\n";
if (m_LastModified.length () > 0) // if-modfief-since if (m_LastModified.length () > 0) // if-modfief-since
request << i2p::util::http::IF_MODIFIED_SINCE << ": " << m_LastModified << "\r\n"; request << i2p::util::http::IF_MODIFIED_SINCE << ": " << m_LastModified << "\r\n";
request << "\r\n"; // end of header request << "\r\n"; // end of header
@ -529,7 +635,7 @@ namespace client
!header.compare (colon + 1, std::string::npos, "x-i2p-gzip"); !header.compare (colon + 1, std::string::npos, "x-i2p-gzip");
} }
} }
LogPrint (eLogInfo, "Addressbook: ", m_Link, " ETag: ", m_Etag, " Last-Modified: ", m_LastModified); LogPrint (eLogInfo, "Addressbook: received ", m_Link, " ETag: ", m_Etag, " Last-Modified: ", m_LastModified);
if (!response.eof ()) if (!response.eof ())
{ {
success = true; success = true;
@ -561,7 +667,7 @@ namespace client
if (!success) if (!success)
LogPrint (eLogError, "Addressbook: download hosts.txt from ", m_Link, " failed"); LogPrint (eLogError, "Addressbook: download hosts.txt from ", m_Link, " failed");
m_Book.DownloadComplete (success); m_Book.DownloadComplete (success, ident, m_Etag, m_LastModified);
} }
bool AddressBookSubscription::ProcessResponse (std::stringstream& s, bool isGzip) bool AddressBookSubscription::ProcessResponse (std::stringstream& s, bool isGzip)
@ -580,6 +686,50 @@ namespace client
m_Book.LoadHostsFromStream (s); m_Book.LoadHostsFromStream (s);
return true; return true;
} }
AddressResolver::AddressResolver (std::shared_ptr<ClientDestination> destination):
m_LocalDestination (destination)
{
if (m_LocalDestination)
{
auto datagram = m_LocalDestination->GetDatagramDestination ();
if (!datagram)
datagram = m_LocalDestination->CreateDatagramDestination ();
datagram->SetReceiver (std::bind (&AddressResolver::HandleRequest, this,
std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5),
ADDRESS_RESOLVER_DATAGRAM_PORT);
}
}
void AddressResolver::HandleRequest (const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len)
{
if (len < 9 || len < buf[8] + 9U)
{
LogPrint (eLogError, "Address request is too short ", len);
return;
}
// read requested address
uint8_t l = buf[8];
char address[255];
memcpy (address, buf + 9, l);
address[l] = 0;
// send response
uint8_t response[40];
memset (response, 0, 4); // reserved
memcpy (response + 4, buf + 4, 4); // nonce
auto it = m_LocalAddresses.find (address); // address lookup
if (it != m_LocalAddresses.end ())
memcpy (response + 8, it->second, 32); // ident
else
memset (response + 8, 0, 32); // not found
m_LocalDestination->GetDatagramDestination ()->SendDatagramTo (response, 40, from.GetIdentHash (), toPort, fromPort);
}
void AddressResolver::AddAddress (const std::string& name, const i2p::data::IdentHash& ident)
{
m_LocalAddresses[name] = ident;
}
} }
} }

32
AddressBook.h

@ -12,6 +12,7 @@
#include "Base.h" #include "Base.h"
#include "Identity.h" #include "Identity.h"
#include "Log.h" #include "Log.h"
#include "Destination.h"
namespace i2p namespace i2p
{ {
@ -37,10 +38,15 @@ namespace client
virtual bool Init () = 0; virtual bool Init () = 0;
virtual int Load (std::map<std::string, i2p::data::IdentHash>& addresses) = 0; virtual int Load (std::map<std::string, i2p::data::IdentHash>& addresses) = 0;
virtual int LoadLocal (std::map<std::string, i2p::data::IdentHash>& addresses) = 0;
virtual int Save (const std::map<std::string, i2p::data::IdentHash>& addresses) = 0; virtual int Save (const std::map<std::string, i2p::data::IdentHash>& addresses) = 0;
virtual void SaveEtag (const i2p::data::IdentHash& subscription, const std::string& etag, const std::string& lastModified) = 0;
virtual bool GetEtag (const i2p::data::IdentHash& subscription, std::string& etag, std::string& lastModified) = 0;
}; };
class AddressBookSubscription; class AddressBookSubscription;
class AddressResolver;
class AddressBook class AddressBook
{ {
public: public:
@ -56,10 +62,13 @@ namespace client
void InsertAddress (std::shared_ptr<const i2p::data::IdentityEx> address); void InsertAddress (std::shared_ptr<const i2p::data::IdentityEx> address);
void LoadHostsFromStream (std::istream& f); void LoadHostsFromStream (std::istream& f);
void DownloadComplete (bool success); 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); }
std::string ToAddress(std::shared_ptr<const i2p::data::IdentityEx> ident) { return ToAddress(ident->GetIdentHash ()); } std::string ToAddress(std::shared_ptr<const i2p::data::IdentityEx> ident) { return ToAddress(ident->GetIdentHash ()); }
bool GetEtag (const i2p::data::IdentHash& subscription, std::string& etag, std::string& lastModified);
private: private:
void StartSubscriptions (); void StartSubscriptions ();
@ -67,6 +76,7 @@ namespace client
void LoadHosts (); void LoadHosts ();
void LoadSubscriptions (); void LoadSubscriptions ();
void LoadLocal ();
void HandleSubscriptionsUpdateTimer (const boost::system::error_code& ecode); void HandleSubscriptionsUpdateTimer (const boost::system::error_code& ecode);
@ -74,6 +84,7 @@ namespace client
std::mutex m_AddressBookMutex; std::mutex m_AddressBookMutex;
std::map<std::string, i2p::data::IdentHash> m_Addresses; std::map<std::string, i2p::data::IdentHash> m_Addresses;
std::map<i2p::data::IdentHash, std::shared_ptr<AddressResolver> > m_Resolvers; // local destination->resolver
AddressBookStorage * m_Storage; AddressBookStorage * m_Storage;
volatile bool m_IsLoaded, m_IsDownloading; volatile bool m_IsLoaded, m_IsDownloading;
std::vector<AddressBookSubscription *> m_Subscriptions; std::vector<AddressBookSubscription *> m_Subscriptions;
@ -97,6 +108,25 @@ namespace client
AddressBook& m_Book; AddressBook& m_Book;
std::string m_Link, m_Etag, m_LastModified; std::string m_Link, m_Etag, m_LastModified;
// m_Etag must be surrounded by ""
};
const uint16_t ADDRESS_RESOLVER_DATAGRAM_PORT = 53;
class AddressResolver
{
public:
AddressResolver (std::shared_ptr<ClientDestination> destination);
void AddAddress (const std::string& name, const i2p::data::IdentHash& ident);
private:
void HandleRequest (const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len);
private:
std::shared_ptr<ClientDestination> m_LocalDestination;
std::map<std::string, i2p::data::IdentHash> m_LocalAddresses;
}; };
} }
} }

4
Base.cpp

@ -8,8 +8,8 @@ namespace data
{ {
static const char T32[32] = { static const char T32[32] = {
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
'i', 'k', 'k', 'l', 'm', 'n', 'o', 'p', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p',
'q', 'r', 't', 't', 'u', 'v', 'w', 'x', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x',
'y', 'z', '2', '3', '4', '5', '6', '7', 'y', 'z', '2', '3', '4', '5', '6', '7',
}; };

1
Config.cpp

@ -126,6 +126,7 @@ namespace config {
#ifdef _WIN32 #ifdef _WIN32
("svcctl", value<std::string>()->default_value(""), "Windows service management ('install' or 'remove')") ("svcctl", value<std::string>()->default_value(""), "Windows service management ('install' or 'remove')")
("insomnia", value<bool>()->zero_tokens()->default_value(false), "Prevent system from sleeping") ("insomnia", value<bool>()->zero_tokens()->default_value(false), "Prevent system from sleeping")
("close", value<std::string>()->default_value("ask"), "On close action") // minimize, exit, ask TODO: add custom validator or something
#endif #endif
; ;

2
Daemon.cpp

@ -72,7 +72,7 @@ namespace i2p
if (config == "") if (config == "")
{ {
config = i2p::fs::DataDirPath("i2p.conf"); config = i2p::fs::DataDirPath("i2p.conf");
// use i2p.cong only if exists // use i2p.conf only if exists
if (!i2p::fs::Exists (config)) config = ""; /* reset */ if (!i2p::fs::Exists (config)) config = ""; /* reset */
} }

7
FS.cpp

@ -102,6 +102,13 @@ namespace fs {
return boost::filesystem::remove(path); return boost::filesystem::remove(path);
} }
bool CreateDirectory (const std::string& path)
{
if (boost::filesystem::exists(path) &&
boost::filesystem::is_directory (boost::filesystem::status (path))) return true;
return boost::filesystem::create_directory(path);
}
void HashedStorage::SetPlace(const std::string &path) { void HashedStorage::SetPlace(const std::string &path) {
root = path + i2p::fs::dirSep + name; root = path + i2p::fs::dirSep + name;
} }

17
FS.h

@ -48,8 +48,8 @@ namespace fs {
/** create subdirs in storage */ /** create subdirs in storage */
bool Init(const char* chars, size_t cnt); bool Init(const char* chars, size_t cnt);
const std::string & GetRoot() const { return this->root; } const std::string & GetRoot() const { return root; }
const std::string & GetName() const { return this->name; } const std::string & GetName() const { return name; }
/** set directory where to place storage directory */ /** set directory where to place storage directory */
void SetPlace(const std::string & path); void SetPlace(const std::string & path);
/** path to file with given ident */ /** path to file with given ident */
@ -109,6 +109,8 @@ namespace fs {
*/ */
bool Exists(const std::string & path); bool Exists(const std::string & path);
bool CreateDirectory (const std::string& path);
template<typename T> template<typename T>
void _ExpandPath(std::stringstream & path, T c) { void _ExpandPath(std::stringstream & path, T c) {
path << i2p::fs::dirSep << c; path << i2p::fs::dirSep << c;
@ -136,6 +138,17 @@ namespace fs {
return s.str(); return s.str();
} }
template<typename Storage, typename... Filename>
std::string StorageRootPath (const Storage& storage, Filename... filenames)
{
std::stringstream s("");
s << storage.GetRoot ();
_ExpandPath(s, filenames...);
return s.str();
}
} // fs } // fs
} // i2p } // i2p

2
HTTPProxy.cpp

@ -204,7 +204,7 @@ namespace proxy
if (eol) if (eol)
{ {
*eol = 0; eol++; *eol = 0; eol++;
if (strncmp ((const char *)http_buff, "Referer", 7)) // strip out referer if (strncmp ((const char *)http_buff, "Referer", 7) && strncmp ((const char *)http_buff, "Connection", 10)) // strip out referer and connection
{ {
if (!strncmp ((const char *)http_buff, "User-Agent", 10)) // replace UserAgent if (!strncmp ((const char *)http_buff, "User-Agent", 10)) // replace UserAgent
m_request.append("User-Agent: MYOB/6.66 (AN/ON)"); m_request.append("User-Agent: MYOB/6.66 (AN/ON)");

10
HTTPServer.cpp

@ -427,18 +427,18 @@ namespace util
s << " (" << i2p::transport::transports.GetOutBandwidth () <<" Bps)<br>\r\n"; s << " (" << i2p::transport::transports.GetOutBandwidth () <<" Bps)<br>\r\n";
s << "<b>Data path:</b> " << i2p::fs::GetDataDir() << "<br>\r\n<br>\r\n"; s << "<b>Data path:</b> " << i2p::fs::GetDataDir() << "<br>\r\n<br>\r\n";
s << "<b>Our external address:</b>" << "<br>\r\n" ; s << "<b>Our external address:</b>" << "<br>\r\n" ;
for (auto& address : i2p::context.GetRouterInfo().GetAddresses()) for (auto address : i2p::context.GetRouterInfo().GetAddresses())
{ {
switch (address.transportStyle) switch (address->transportStyle)
{ {
case i2p::data::RouterInfo::eTransportNTCP: case i2p::data::RouterInfo::eTransportNTCP:
if (address.host.is_v6 ()) if (address->host.is_v6 ())
s << "NTCP6&nbsp;&nbsp;"; s << "NTCP6&nbsp;&nbsp;";
else else
s << "NTCP&nbsp;&nbsp;"; s << "NTCP&nbsp;&nbsp;";
break; break;
case i2p::data::RouterInfo::eTransportSSU: case i2p::data::RouterInfo::eTransportSSU:
if (address.host.is_v6 ()) if (address->host.is_v6 ())
s << "SSU6&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"; s << "SSU6&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;";
else else
s << "SSU&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"; s << "SSU&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;";
@ -446,7 +446,7 @@ namespace util
default: default:
s << "Unknown&nbsp;&nbsp;"; s << "Unknown&nbsp;&nbsp;";
} }
s << address.host.to_string() << ":" << address.port << "<br>\r\n"; s << address->host.to_string() << ":" << address->port << "<br>\r\n";
} }
s << "<br>\r\n<b>Routers:</b> " << i2p::data::netdb.GetNumRouters () << " "; s << "<br>\r\n<b>Routers:</b> " << i2p::data::netdb.GetNumRouters () << " ";
s << "<b>Floodfills:</b> " << i2p::data::netdb.GetNumFloodfills () << " "; s << "<b>Floodfills:</b> " << i2p::data::netdb.GetNumFloodfills () << " ";

16
NTCPSession.cpp

@ -94,8 +94,6 @@ namespace transport
m_DHKeysPair = nullptr; m_DHKeysPair = nullptr;
SendTimeSyncMessage (); SendTimeSyncMessage ();
m_SendQueue.push_back (CreateDatabaseStoreMsg ()); // we tell immediately who we are
transports.PeerConnected (shared_from_this ()); transports.PeerConnected (shared_from_this ());
} }
@ -761,15 +759,15 @@ namespace transport
m_IsRunning = true; m_IsRunning = true;
m_Thread = new std::thread (std::bind (&NTCPServer::Run, this)); m_Thread = new std::thread (std::bind (&NTCPServer::Run, this));
// create acceptors // create acceptors
auto addresses = context.GetRouterInfo ().GetAddresses (); auto& addresses = context.GetRouterInfo ().GetAddresses ();
for (auto& address : addresses) for (auto address: addresses)
{ {
if (address.transportStyle == i2p::data::RouterInfo::eTransportNTCP && address.host.is_v4 ()) if (address->transportStyle == i2p::data::RouterInfo::eTransportNTCP && address->host.is_v4 ())
{ {
m_NTCPAcceptor = new boost::asio::ip::tcp::acceptor (m_Service, m_NTCPAcceptor = new boost::asio::ip::tcp::acceptor (m_Service,
boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), address.port)); boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), address->port));
LogPrint (eLogInfo, "NTCP: Start listening TCP port ", address.port); LogPrint (eLogInfo, "NTCP: Start listening TCP port ", address->port);
auto conn = std::make_shared<NTCPSession>(*this); auto conn = std::make_shared<NTCPSession>(*this);
m_NTCPAcceptor->async_accept(conn->GetSocket (), std::bind (&NTCPServer::HandleAccept, this, m_NTCPAcceptor->async_accept(conn->GetSocket (), std::bind (&NTCPServer::HandleAccept, this,
conn, std::placeholders::_1)); conn, std::placeholders::_1));
@ -779,10 +777,10 @@ namespace transport
m_NTCPV6Acceptor = new boost::asio::ip::tcp::acceptor (m_Service); m_NTCPV6Acceptor = new boost::asio::ip::tcp::acceptor (m_Service);
m_NTCPV6Acceptor->open (boost::asio::ip::tcp::v6()); m_NTCPV6Acceptor->open (boost::asio::ip::tcp::v6());
m_NTCPV6Acceptor->set_option (boost::asio::ip::v6_only (true)); m_NTCPV6Acceptor->set_option (boost::asio::ip::v6_only (true));
m_NTCPV6Acceptor->bind (boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v6(), address.port)); m_NTCPV6Acceptor->bind (boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v6(), address->port));
m_NTCPV6Acceptor->listen (); m_NTCPV6Acceptor->listen ();
LogPrint (eLogInfo, "NTCP: Start listening V6 TCP port ", address.port); LogPrint (eLogInfo, "NTCP: Start listening V6 TCP port ", address->port);
auto conn = std::make_shared<NTCPSession> (*this); auto conn = std::make_shared<NTCPSession> (*this);
m_NTCPV6Acceptor->async_accept(conn->GetSocket (), std::bind (&NTCPServer::HandleAcceptV6, m_NTCPV6Acceptor->async_accept(conn->GetSocket (), std::bind (&NTCPServer::HandleAcceptV6,
this, conn, std::placeholders::_1)); this, conn, std::placeholders::_1));

41
NetDb.cpp

@ -118,6 +118,7 @@ namespace data
{ {
SaveUpdated (); SaveUpdated ();
ManageLeaseSets (); ManageLeaseSets ();
ManageLookupResponses ();
} }
lastSave = ts; lastSave = ts;
} }
@ -671,13 +672,31 @@ namespace data
if (!replyMsg) if (!replyMsg)
{ {
LogPrint (eLogWarning, "NetDb: Requested ", key, " not found. ", numExcluded, " excluded"); LogPrint (eLogWarning, "NetDb: Requested ", key, " not found. ", numExcluded, " excluded");
std::set<IdentHash> excludedRouters; // find or cleate response
for (int i = 0; i < numExcluded; i++) std::vector<IdentHash> closestFloodfills;
bool found = false;
if (!numExcluded)
{ {
excludedRouters.insert (excluded); auto it = m_LookupResponses.find (ident);
excluded += 32; if (it != m_LookupResponses.end ())
{
closestFloodfills = it->second.first;
found = true;
}
} }
replyMsg = CreateDatabaseSearchReply (ident, GetClosestFloodfills (ident, 3, excludedRouters, true)); if (!found)
{
std::set<IdentHash> excludedRouters;
for (int i = 0; i < numExcluded; i++)
{
excludedRouters.insert (excluded);
excluded += 32;
}
closestFloodfills = GetClosestFloodfills (ident, 3, excludedRouters, true);
if (!numExcluded) // save if no excluded
m_LookupResponses[ident] = std::make_pair(closestFloodfills, i2p::util::GetSecondsSinceEpoch ());
}
replyMsg = CreateDatabaseSearchReply (ident, closestFloodfills);
} }
} }
@ -972,5 +991,17 @@ namespace data
it++; it++;
} }
} }
void NetDb::ManageLookupResponses ()
{
auto ts = i2p::util::GetSecondsSinceEpoch ();
for (auto it = m_LookupResponses.begin (); it != m_LookupResponses.end ();)
{
if (ts > it->second.second + 180) // 3 minutes
it = m_LookupResponses.erase (it);
else
it++;
}
}
} }
} }

3
NetDb.h

@ -84,6 +84,7 @@ namespace data
void Publish (); void Publish ();
void ManageLeaseSets (); void ManageLeaseSets ();
void ManageRequests (); void ManageRequests ();
void ManageLookupResponses ();
template<typename Filter> template<typename Filter>
std::shared_ptr<const RouterInfo> GetRandomRouter (Filter filter) const; std::shared_ptr<const RouterInfo> GetRandomRouter (Filter filter) const;
@ -108,6 +109,8 @@ namespace data
friend class NetDbRequests; friend class NetDbRequests;
NetDbRequests m_Requests; NetDbRequests m_Requests;
std::map<IdentHash, std::pair<std::vector<IdentHash>, uint64_t> > m_LookupResponses; // ident->(closest FFs, timestamp)
}; };
extern NetDb netdb; extern NetDb netdb;

5
Reseed.cpp

@ -26,13 +26,12 @@ namespace data
static std::vector<std::string> httpsReseedHostList = static std::vector<std::string> httpsReseedHostList =
{ {
"https://reseed.i2p-projekt.de/", // Only HTTPS "https://reseed.i2p-projekt.de/", // Only HTTPS
//"https://i2pseed.zarrenspry.info/", // Only HTTPS and SU3 (v3) support
"https://i2p.mooo.com/netDb/", "https://i2p.mooo.com/netDb/",
"https://netdb.i2p2.no/", // Only SU3 (v3) support, SNI required "https://netdb.i2p2.no/", // Only SU3 (v3) support, SNI required
"https://us.reseed.i2p2.no:444/", "https://us.reseed.i2p2.no:444/",
"https://uk.reseed.i2p2.no:444/", "https://uk.reseed.i2p2.no:444/",
"https://www.torontocrypto.org:8443/", "https://i2p.manas.ca:8443/",
"https://i2p-0.manas.ca:8443/" "https://i2p-0.manas.ca:8443/",
"https://reseed.i2p.vzaws.com:8443/", // Only SU3 (v3) support "https://reseed.i2p.vzaws.com:8443/", // Only SU3 (v3) support
"https://user.mx24.eu/", // Only HTTPS and SU3 (v3) support "https://user.mx24.eu/", // Only HTTPS and SU3 (v3) support
"https://download.xxlspeed.com/" // Only HTTPS and SU3 (v3) support "https://download.xxlspeed.com/" // Only HTTPS and SU3 (v3) support

36
RouterContext.cpp

@ -92,11 +92,11 @@ namespace i2p
void RouterContext::UpdatePort (int port) void RouterContext::UpdatePort (int port)
{ {
bool updated = false; bool updated = false;
for (auto& address : m_RouterInfo.GetAddresses ()) for (auto address : m_RouterInfo.GetAddresses ())
{ {
if (address.port != port) if (address->port != port)
{ {
address.port = port; address->port = port;
updated = true; updated = true;
} }
} }
@ -107,11 +107,11 @@ namespace i2p
void RouterContext::UpdateAddress (const boost::asio::ip::address& host) void RouterContext::UpdateAddress (const boost::asio::ip::address& host)
{ {
bool updated = false; bool updated = false;
for (auto& address : m_RouterInfo.GetAddresses ()) for (auto address : m_RouterInfo.GetAddresses ())
{ {
if (address.host != host && address.IsCompatible (host)) if (address->host != host && address->IsCompatible (host))
{ {
address.host = host; address->host = host;
updated = true; updated = true;
} }
} }
@ -206,15 +206,15 @@ namespace i2p
auto& addresses = m_RouterInfo.GetAddresses (); auto& addresses = m_RouterInfo.GetAddresses ();
for (size_t i = 0; i < addresses.size (); i++) for (size_t i = 0; i < addresses.size (); i++)
{ {
if (addresses[i].transportStyle == i2p::data::RouterInfo::eTransportNTCP) if (addresses[i]->transportStyle == i2p::data::RouterInfo::eTransportNTCP)
{ {
addresses.erase (addresses.begin () + i); addresses.erase (addresses.begin () + i);
break; break;
} }
} }
// delete previous introducers // delete previous introducers
for (auto& addr : addresses) for (auto addr : addresses)
addr.introducers.clear (); addr->introducers.clear ();
// update // update
UpdateRouterInfo (); UpdateRouterInfo ();
@ -235,16 +235,16 @@ namespace i2p
auto& addresses = m_RouterInfo.GetAddresses (); auto& addresses = m_RouterInfo.GetAddresses ();
for (size_t i = 0; i < addresses.size (); i++) for (size_t i = 0; i < addresses.size (); i++)
{ {
if (addresses[i].transportStyle == i2p::data::RouterInfo::eTransportSSU) if (addresses[i]->transportStyle == i2p::data::RouterInfo::eTransportSSU)
{ {
// 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 (addresses[i]->host.to_string ().c_str (), addresses[i]->port);
break; break;
} }
} }
// delete previous introducers // delete previous introducers
for (auto& addr : addresses) for (auto addr : addresses)
addr.introducers.clear (); addr->introducers.clear ();
// update // update
UpdateRouterInfo (); UpdateRouterInfo ();
@ -264,19 +264,19 @@ namespace i2p
bool updated = false, found = false; bool updated = false, found = false;
int port = 0; int port = 0;
auto& addresses = m_RouterInfo.GetAddresses (); auto& addresses = m_RouterInfo.GetAddresses ();
for (auto& addr : addresses) for (auto addr: addresses)
{ {
if (addr.host.is_v6 () && addr.transportStyle == i2p::data::RouterInfo::eTransportNTCP) if (addr->host.is_v6 () && addr->transportStyle == i2p::data::RouterInfo::eTransportNTCP)
{ {
if (addr.host != host) if (addr->host != host)
{ {
addr.host = host; addr->host = host;
updated = true; updated = true;
} }
found = true; found = true;
} }
else else
port = addr.port; port = addr->port;
} }
if (!found) if (!found)
{ {

83
RouterInfo.cpp

@ -232,7 +232,7 @@ namespace data
} }
if (isValidAddress) if (isValidAddress)
{ {
m_Addresses.push_back(address); m_Addresses.push_back(std::make_shared<Address>(address));
m_SupportedTransports |= supportedTransports; m_SupportedTransports |= supportedTransports;
} }
} }
@ -359,8 +359,9 @@ namespace data
// addresses // addresses
uint8_t numAddresses = m_Addresses.size (); uint8_t numAddresses = m_Addresses.size ();
s.write ((char *)&numAddresses, sizeof (numAddresses)); s.write ((char *)&numAddresses, sizeof (numAddresses));
for (auto& address : m_Addresses) for (auto addr : m_Addresses)
{ {
Address& address = *addr;
s.write ((char *)&address.cost, sizeof (address.cost)); s.write ((char *)&address.cost, sizeof (address.cost));
s.write ((char *)&address.date, sizeof (address.date)); s.write ((char *)&address.date, sizeof (address.date));
std::stringstream properties; std::stringstream properties;
@ -543,46 +544,46 @@ namespace data
void RouterInfo::AddNTCPAddress (const char * host, int port) void RouterInfo::AddNTCPAddress (const char * host, int port)
{ {
Address addr; auto addr = std::make_shared<Address>();
addr.host = boost::asio::ip::address::from_string (host); addr->host = boost::asio::ip::address::from_string (host);
addr.port = port; addr->port = port;
addr.transportStyle = eTransportNTCP; addr->transportStyle = eTransportNTCP;
addr.cost = 2; addr->cost = 2;
addr.date = 0; addr->date = 0;
addr.mtu = 0; addr->mtu = 0;
for (auto it: m_Addresses) // don't insert same address twice for (auto it: m_Addresses) // don't insert same address twice
if (it == addr) return; if (*it == *addr) return;
m_Addresses.push_back(addr); m_Addresses.push_back(addr);
m_SupportedTransports |= addr.host.is_v6 () ? eNTCPV6 : eNTCPV4; m_SupportedTransports |= addr->host.is_v6 () ? eNTCPV6 : eNTCPV4;
} }
void RouterInfo::AddSSUAddress (const char * host, int port, const uint8_t * key, int mtu) void RouterInfo::AddSSUAddress (const char * host, int port, const uint8_t * key, int mtu)
{ {
Address addr; auto addr = std::make_shared<Address>();
addr.host = boost::asio::ip::address::from_string (host); addr->host = boost::asio::ip::address::from_string (host);
addr.port = port; addr->port = port;
addr.transportStyle = eTransportSSU; addr->transportStyle = eTransportSSU;
addr.cost = 10; // NTCP should have priority over SSU addr->cost = 10; // NTCP should have priority over SSU
addr.date = 0; addr->date = 0;
addr.mtu = mtu; addr->mtu = mtu;
memcpy (addr.key, key, 32); memcpy (addr->key, key, 32);
for (auto it: m_Addresses) // don't insert same address twice for (auto it: m_Addresses) // don't insert same address twice
if (it == addr) return; if (*it == *addr) return;
m_Addresses.push_back(addr); m_Addresses.push_back(addr);
m_SupportedTransports |= addr.host.is_v6 () ? eSSUV6 : eSSUV4; m_SupportedTransports |= addr->host.is_v6 () ? eSSUV6 : eSSUV4;
m_Caps |= eSSUTesting; m_Caps |= eSSUTesting;
m_Caps |= eSSUIntroducer; m_Caps |= eSSUIntroducer;
} }
bool RouterInfo::AddIntroducer (const Introducer& introducer) bool RouterInfo::AddIntroducer (const Introducer& introducer)
{ {
for (auto& addr : m_Addresses) for (auto addr : m_Addresses)
{ {
if (addr.transportStyle == eTransportSSU && addr.host.is_v4 ()) if (addr->transportStyle == eTransportSSU && addr->host.is_v4 ())
{ {
for (auto intro: addr.introducers) for (auto intro: addr->introducers)
if (intro.iTag == introducer.iTag) return false; // already presented if (intro.iTag == introducer.iTag) return false; // already presented
addr.introducers.push_back (introducer); addr->introducers.push_back (introducer);
return true; return true;
} }
} }
@ -591,14 +592,14 @@ namespace data
bool RouterInfo::RemoveIntroducer (const boost::asio::ip::udp::endpoint& e) bool RouterInfo::RemoveIntroducer (const boost::asio::ip::udp::endpoint& e)
{ {
for (auto& addr : m_Addresses) for (auto addr: m_Addresses)
{ {
if (addr.transportStyle == eTransportSSU && addr.host.is_v4 ()) if (addr->transportStyle == eTransportSSU && addr->host.is_v4 ())
{ {
for (std::vector<Introducer>::iterator it = addr.introducers.begin (); it != addr.introducers.end (); it++) for (std::vector<Introducer>::iterator it = addr->introducers.begin (); it != addr->introducers.end (); it++)
if ( boost::asio::ip::udp::endpoint (it->iHost, it->iPort) == e) if ( boost::asio::ip::udp::endpoint (it->iHost, it->iPort) == e)
{ {
addr.introducers.erase (it); addr->introducers.erase (it);
return true; return true;
} }
} }
@ -664,8 +665,8 @@ namespace data
m_SupportedTransports &= ~eNTCPV6; m_SupportedTransports &= ~eNTCPV6;
for (size_t i = 0; i < m_Addresses.size (); i++) for (size_t i = 0; i < m_Addresses.size (); i++)
{ {
if (m_Addresses[i].transportStyle == i2p::data::RouterInfo::eTransportNTCP && if (m_Addresses[i]->transportStyle == i2p::data::RouterInfo::eTransportNTCP &&
m_Addresses[i].host.is_v6 ()) m_Addresses[i]->host.is_v6 ())
{ {
m_Addresses.erase (m_Addresses.begin () + i); m_Addresses.erase (m_Addresses.begin () + i);
break; break;
@ -676,8 +677,8 @@ namespace data
m_SupportedTransports &= ~eSSUV6; m_SupportedTransports &= ~eSSUV6;
for (size_t i = 0; i < m_Addresses.size (); i++) for (size_t i = 0; i < m_Addresses.size (); i++)
{ {
if (m_Addresses[i].transportStyle == i2p::data::RouterInfo::eTransportSSU && if (m_Addresses[i]->transportStyle == i2p::data::RouterInfo::eTransportSSU &&
m_Addresses[i].host.is_v6 ()) m_Addresses[i]->host.is_v6 ())
{ {
m_Addresses.erase (m_Addresses.begin () + i); m_Addresses.erase (m_Addresses.begin () + i);
break; break;
@ -691,29 +692,29 @@ namespace data
return m_Caps & Caps::eUnreachable; // non-reachable return m_Caps & Caps::eUnreachable; // non-reachable
} }
const RouterInfo::Address * RouterInfo::GetNTCPAddress (bool v4only) const std::shared_ptr<const RouterInfo::Address> RouterInfo::GetNTCPAddress (bool v4only) const
{ {
return GetAddress (eTransportNTCP, v4only); return GetAddress (eTransportNTCP, v4only);
} }
const RouterInfo::Address * RouterInfo::GetSSUAddress (bool v4only) const std::shared_ptr<const RouterInfo::Address> RouterInfo::GetSSUAddress (bool v4only) const
{ {
return GetAddress (eTransportSSU, v4only); return GetAddress (eTransportSSU, v4only);
} }
const RouterInfo::Address * RouterInfo::GetSSUV6Address () const std::shared_ptr<const RouterInfo::Address> RouterInfo::GetSSUV6Address () const
{ {
return GetAddress (eTransportSSU, false, true); return GetAddress (eTransportSSU, false, true);
} }
const RouterInfo::Address * RouterInfo::GetAddress (TransportStyle s, bool v4only, bool v6only) const std::shared_ptr<const RouterInfo::Address> RouterInfo::GetAddress (TransportStyle s, bool v4only, bool v6only) const
{ {
for (auto& address : m_Addresses) for (auto address : m_Addresses)
{ {
if (address.transportStyle == s) if (address->transportStyle == s)
{ {
if ((!v4only || address.host.is_v4 ()) && (!v6only || address.host.is_v6 ())) if ((!v4only || address->host.is_v4 ()) && (!v6only || address->host.is_v6 ()))
return &address; return address;
} }
} }
return nullptr; return nullptr;

12
RouterInfo.h

@ -116,10 +116,10 @@ namespace data
void SetRouterIdentity (std::shared_ptr<const IdentityEx> identity); void SetRouterIdentity (std::shared_ptr<const IdentityEx> identity);
std::string GetIdentHashBase64 () const { return GetIdentHash ().ToBase64 (); }; std::string GetIdentHashBase64 () const { return GetIdentHash ().ToBase64 (); };
uint64_t GetTimestamp () const { return m_Timestamp; }; uint64_t GetTimestamp () const { return m_Timestamp; };
std::vector<Address>& GetAddresses () { return m_Addresses; }; std::vector<std::shared_ptr<Address> >& GetAddresses () { return m_Addresses; };
const Address * GetNTCPAddress (bool v4only = true) const; std::shared_ptr<const Address> GetNTCPAddress (bool v4only = true) const;
const Address * GetSSUAddress (bool v4only = true) const; std::shared_ptr<const Address> GetSSUAddress (bool v4only = true) const;
const Address * GetSSUV6Address () const; std::shared_ptr<const Address> GetSSUV6Address () const;
void AddNTCPAddress (const char * host, int port); void AddNTCPAddress (const char * host, int port);
void AddSSUAddress (const char * host, int port, const uint8_t * key, int mtu = 0); void AddSSUAddress (const char * host, int port, const uint8_t * key, int mtu = 0);
@ -182,7 +182,7 @@ namespace data
size_t ReadString (char * str, std::istream& s); size_t ReadString (char * str, std::istream& s);
void WriteString (const std::string& str, std::ostream& s); void WriteString (const std::string& str, std::ostream& s);
void ExtractCaps (const char * value); void ExtractCaps (const char * value);
const Address * GetAddress (TransportStyle s, bool v4only, bool v6only = false) const; std::shared_ptr<const Address> GetAddress (TransportStyle s, bool v4only, bool v6only = false) const;
void UpdateCapsProperty (); void UpdateCapsProperty ();
private: private:
@ -192,7 +192,7 @@ namespace data
uint8_t * m_Buffer; uint8_t * m_Buffer;
size_t m_BufferLen; size_t m_BufferLen;
uint64_t m_Timestamp; uint64_t m_Timestamp;
std::vector<Address> m_Addresses; std::vector<std::shared_ptr<Address> > m_Addresses;
std::map<std::string, std::string> m_Properties; std::map<std::string, std::string> m_Properties;
bool m_IsUpdated, m_IsUnreachable; bool m_IsUpdated, m_IsUnreachable;
uint8_t m_SupportedTransports, m_Caps; uint8_t m_SupportedTransports, m_Caps;

1
SSUSession.cpp

@ -855,7 +855,6 @@ namespace transport
m_DHKeysPair = nullptr; m_DHKeysPair = nullptr;
m_SignedData = nullptr; m_SignedData = nullptr;
m_Data.Start (); m_Data.Start ();
m_Data.Send (CreateDatabaseStoreMsg ());
transports.PeerConnected (shared_from_this ()); transports.PeerConnected (shared_from_this ());
if (m_IsPeerTest) if (m_IsPeerTest)
SendPeerTest (); SendPeerTest ();

50
Transports.cpp

@ -112,8 +112,8 @@ namespace transport
m_IsRunning = true; m_IsRunning = true;
m_Thread = new std::thread (std::bind (&Transports::Run, this)); m_Thread = new std::thread (std::bind (&Transports::Run, this));
// create acceptors // create acceptors
auto addresses = context.GetRouterInfo ().GetAddresses (); auto& addresses = context.GetRouterInfo ().GetAddresses ();
for (auto& address : addresses) for (auto address : addresses)
{ {
if (!m_NTCPServer) if (!m_NTCPServer)
{ {
@ -121,12 +121,12 @@ namespace transport
m_NTCPServer->Start (); m_NTCPServer->Start ();
} }
if (address.transportStyle == RouterInfo::eTransportSSU && address.host.is_v4 ()) if (address->transportStyle == RouterInfo::eTransportSSU && address->host.is_v4 ())
{ {
if (!m_SSUServer) if (!m_SSUServer)
{ {
m_SSUServer = new SSUServer (address.port); m_SSUServer = new SSUServer (address->port);
LogPrint (eLogInfo, "Transports: Start listening UDP port ", address.port); LogPrint (eLogInfo, "Transports: Start listening UDP port ", address->port);
m_SSUServer->Start (); m_SSUServer->Start ();
DetectExternalIP (); DetectExternalIP ();
} }
@ -378,13 +378,18 @@ namespace transport
{ {
auto address = (*it).endpoint ().address (); auto address = (*it).endpoint ().address ();
LogPrint (eLogDebug, "Transports: ", (*it).host_name (), " has been resolved to ", address); LogPrint (eLogDebug, "Transports: ", (*it).host_name (), " has been resolved to ", address);
auto addr = peer.router->GetNTCPAddress (); if (address.is_v4 () || context.SupportsV6 ())
if (addr)
{ {
auto s = std::make_shared<NTCPSession> (*m_NTCPServer, peer.router); auto addr = peer.router->GetNTCPAddress (); // TODO: take one we requested
m_NTCPServer->Connect (address, addr->port, s); if (addr)
return; {
auto s = std::make_shared<NTCPSession> (*m_NTCPServer, peer.router);
m_NTCPServer->Connect (address, addr->port, s);
return;
}
} }
else
LogPrint (eLogInfo, "Can't connect to NTCP ", address, " ipv6 is not supported");
} }
LogPrint (eLogError, "Transports: Unable to resolve NTCP address: ", ecode.message ()); LogPrint (eLogError, "Transports: Unable to resolve NTCP address: ", ecode.message ());
std::unique_lock<std::mutex> l(m_PeersMutex); std::unique_lock<std::mutex> l(m_PeersMutex);
@ -411,12 +416,17 @@ namespace transport
{ {
auto address = (*it).endpoint ().address (); auto address = (*it).endpoint ().address ();
LogPrint (eLogDebug, "Transports: ", (*it).host_name (), " has been resolved to ", address); LogPrint (eLogDebug, "Transports: ", (*it).host_name (), " has been resolved to ", address);
auto addr = peer.router->GetSSUAddress (!context.SupportsV6 ());; if (address.is_v4 () || context.SupportsV6 ())
if (addr)
{ {
m_SSUServer->CreateSession (peer.router, address, addr->port); auto addr = peer.router->GetSSUAddress (); // TODO: take one we requested
return; if (addr)
{
m_SSUServer->CreateSession (peer.router, address, addr->port);
return;
}
} }
else
LogPrint (eLogInfo, "Can't connect to SSU ", address, " ipv6 is not supported");
} }
LogPrint (eLogError, "Transports: Unable to resolve SSU address: ", ecode.message ()); LogPrint (eLogError, "Transports: Unable to resolve SSU address: ", ecode.message ());
std::unique_lock<std::mutex> l(m_PeersMutex); std::unique_lock<std::mutex> l(m_PeersMutex);
@ -505,12 +515,24 @@ namespace transport
auto it = m_Peers.find (ident); auto it = m_Peers.find (ident);
if (it != m_Peers.end ()) if (it != m_Peers.end ())
{ {
bool sendDatabaseStore = true;
if (it->second.delayedMessages.size () > 0)
{
// check if first message is our DatabaseStore (publishing)
auto firstMsg = it->second.delayedMessages[0];
if (firstMsg && firstMsg->GetTypeID () == eI2NPDatabaseStore &&
i2p::data::IdentHash(firstMsg->GetPayload () + DATABASE_STORE_KEY_OFFSET) == i2p::context.GetIdentHash ())
sendDatabaseStore = false; // we have it in the list already
}
if (sendDatabaseStore)
session->SendI2NPMessages ({ CreateDatabaseStoreMsg () });
it->second.sessions.push_back (session); it->second.sessions.push_back (session);
session->SendI2NPMessages (it->second.delayedMessages); session->SendI2NPMessages (it->second.delayedMessages);
it->second.delayedMessages.clear (); it->second.delayedMessages.clear ();
} }
else // incoming connection else // incoming connection
{ {
session->SendI2NPMessages ({ CreateDatabaseStoreMsg () }); // send DatabaseStore
std::unique_lock<std::mutex> l(m_PeersMutex); std::unique_lock<std::mutex> l(m_PeersMutex);
m_Peers.insert (std::make_pair (ident, Peer{ 0, nullptr, { session }, i2p::util::GetSecondsSinceEpoch (), {} })); m_Peers.insert (std::make_pair (ident, Peer{ 0, nullptr, { session }, i2p::util::GetSecondsSinceEpoch (), {} }));
} }

14
UPnP.cpp

@ -101,19 +101,19 @@ namespace transport
void UPnP::Run () void UPnP::Run ()
{ {
std::vector<data::RouterInfo::Address> a = context.GetRouterInfo().GetAddresses(); const std::vector<std::shared_ptr<i2p::data::RouterInfo::Address> > a = context.GetRouterInfo().GetAddresses();
for (auto& address : a) for (auto address : a)
{ {
if (!address.host.is_v6 ()) if (!address->host.is_v6 ())
{ {
Discover (); Discover ();
if (address.transportStyle == data::RouterInfo::eTransportSSU ) if (address->transportStyle == data::RouterInfo::eTransportSSU )
{ {
TryPortMapping (I2P_UPNP_UDP, address.port); TryPortMapping (I2P_UPNP_UDP, address->port);
} }
else if (address.transportStyle == data::RouterInfo::eTransportNTCP ) else if (address->transportStyle == data::RouterInfo::eTransportNTCP )
{ {
TryPortMapping (I2P_UPNP_TCP, address.port); TryPortMapping (I2P_UPNP_TCP, address->port);
} }
} }
} }

5
UPnP.h

@ -58,6 +58,5 @@ namespace transport
} }
} }
#endif #endif // USE_UPNP
#endif // __UPNP_H__
#endif

BIN
Win32/Anke_2200px.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 146 KiB

BIN
Win32/Anke_700px.bmp

Binary file not shown.

After

Width:  |  Height:  |  Size: 730 KiB

6
Win32/Resource.rc

@ -52,7 +52,11 @@ END
// Icon with lowest ID value placed first to ensure application icon // Icon with lowest ID value placed first to ensure application icon
// remains consistent on all systems. // remains consistent on all systems.
MAINICON ICON "ictoopie.ico" //MAINICON ICON "ictoopie.ico"
MAINICON ICON "anke.ico"
MASCOT BITMAP "Anke_700px.bmp"
#endif // English (United States) resources #endif // English (United States) resources
///////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////

50
Win32/Win32App.cpp

@ -2,8 +2,14 @@
#include <windows.h> #include <windows.h>
#include <shellapi.h> #include <shellapi.h>
#include "../Config.h" #include "../Config.h"
#include "../version.h"
#include "resource.h" #include "resource.h"
#include "Win32App.h" #include "Win32App.h"
#include <stdio.h>
#if defined(_MSC_VER) && _MSC_VER < 1900
#define snprintf _snprintf
#endif
#define ID_ABOUT 2000 #define ID_ABOUT 2000
#define ID_EXIT 2001 #define ID_EXIT 2001
@ -85,7 +91,9 @@ namespace win32
{ {
case ID_ABOUT: case ID_ABOUT:
{ {
MessageBox( hWnd, TEXT("i2pd"), TEXT("About"), MB_ICONINFORMATION | MB_OK ); std::stringstream text;
text << "Version: " << I2PD_VERSION << " " << CODENAME;
MessageBox( hWnd, TEXT(text.str ().c_str ()), TEXT("i2pd"), MB_ICONINFORMATION | MB_OK );
return 0; return 0;
} }
case ID_EXIT: case ID_EXIT:
@ -98,7 +106,7 @@ namespace win32
char buf[30]; char buf[30];
std::string httpAddr; i2p::config::GetOption("http.address", httpAddr); std::string httpAddr; i2p::config::GetOption("http.address", httpAddr);
uint16_t httpPort; i2p::config::GetOption("http.port", httpPort); uint16_t httpPort; i2p::config::GetOption("http.port", httpPort);
std::snprintf(buf, 30, "http://%s:%d", httpAddr.c_str(), httpPort); snprintf(buf, 30, "http://%s:%d", httpAddr.c_str(), httpPort);
ShellExecute(NULL, "open", buf, NULL, NULL, SW_SHOWNORMAL); ShellExecute(NULL, "open", buf, NULL, NULL, SW_SHOWNORMAL);
return 0; return 0;
} }
@ -119,6 +127,29 @@ namespace win32
ShowWindow(hWnd, SW_HIDE); ShowWindow(hWnd, SW_HIDE);
return 0; return 0;
} }
case SC_CLOSE:
{
std::string close; i2p::config::GetOption("close", close);
if (0 == close.compare("ask"))
switch(::MessageBox(hWnd, "Would you like to minimize instead of exiting?"
" You can add 'close' configuration option. Valid values are: ask, minimize, exit.",
"Minimize instead of exiting?", MB_ICONQUESTION | MB_YESNOCANCEL | MB_DEFBUTTON1))
{
case IDYES: close = "minimize"; break;
case IDNO: close = "exit"; break;
default: return 0;
}
if (0 == close.compare("minimize"))
{
ShowWindow(hWnd, SW_HIDE);
return 0;
}
if (0 != close.compare("exit"))
{
::MessageBox(hWnd, close.c_str(), "Unknown close action in config", MB_OK | MB_ICONWARNING);
return 0;
}
}
} }
} }
case WM_TRAYICON: case WM_TRAYICON:
@ -136,6 +167,19 @@ namespace win32
} }
break; break;
} }
case WM_PAINT:
{
PAINTSTRUCT ps;
auto hDC = BeginPaint (hWnd, &ps);
auto mascot = LoadBitmap (GetModuleHandle(NULL), MAKEINTRESOURCE (MASCOT));
auto mascotDC = CreateCompatibleDC (hDC);
SelectObject (mascotDC, mascot);
BitBlt (hDC, 0,0, 533, 700, mascotDC, 0, 0, SRCCOPY);
DeleteDC (mascotDC);
DeleteObject (mascot);
EndPaint (hWnd, &ps);
break;
}
} }
return DefWindowProc( hWnd, uMsg, wParam, lParam); return DefWindowProc( hWnd, uMsg, wParam, lParam);
} }
@ -164,7 +208,7 @@ namespace win32
wclx.lpszClassName = I2PD_WIN32_CLASSNAME; wclx.lpszClassName = I2PD_WIN32_CLASSNAME;
RegisterClassEx (&wclx); RegisterClassEx (&wclx);
// create new window // create new window
if (!CreateWindow(I2PD_WIN32_CLASSNAME, TEXT("i2pd"), WS_OVERLAPPEDWINDOW, 100, 100, 250, 150, NULL, NULL, hInst, NULL)) if (!CreateWindow(I2PD_WIN32_CLASSNAME, TEXT("i2pd"), WS_OVERLAPPEDWINDOW, 100, 100, 549, 738, NULL, NULL, hInst, NULL))
{ {
MessageBox(NULL, "Failed to create main window", TEXT("Warning!"), MB_ICONERROR | MB_OK | MB_TOPMOST); MessageBox(NULL, "Failed to create main window", TEXT("Warning!"), MB_ICONERROR | MB_OK | MB_TOPMOST);
return false; return false;

BIN
Win32/anke.ico

Binary file not shown.

After

Width:  |  Height:  |  Size: 119 KiB

1
Win32/resource.h

@ -3,6 +3,7 @@
// Used by Resource.rc // Used by Resource.rc
// //
#define MAINICON 101 #define MAINICON 101
#define MASCOT 201
// Next default values for new objects // Next default values for new objects
// //

75
docs/build_notes_cross.md

@ -0,0 +1,75 @@
Cross compilation notes
=======================
Static 64 bit windows binary on Ubuntu 15.10 (Wily Werewolf)
---------------------------------------------------------------------
Install cross compiler and friends
```sh
sudo apt-get install g++-mingw-w64-x86-64
```
Default is to use Win32 threading model which lacks std::mutex and such. So we change defaults
```sh
sudo update-alternatives --set x86_64-w64-mingw32-g++ /usr/bin/x86_64-w64-mingw32-g++-posix
```
From now on we assume we have everything in `~/dev/`. Get Boost sources unpacked into `~/dev/boost_1_60_0/`
and change directory to it.
Now add out cross compiler configuration. Warning: the following will wipe out whatever you had in there.
```sh
echo "using gcc : mingw : x86_64-w64-mingw32-g++ ;" > ~/user-config.jam
```
Proceed with building Boost normal way, but let's define dedicated staging directory
```sh
./bootstrap.sh
./b2 toolset=gcc-mingw target-os=windows variant=release link=static runtime-link=static address-model=64 \
--build-type=minimal --with-filesystem --with-program_options --with-regex --with-date_time \
--stagedir=stage-mingw-64
cd ..
```
Now we get & build OpenSSL
```sh
git clone https://github.com/openssl/openssl
cd openssl
git checkout OpenSSL_1_0_2g
./Configure mingw64 no-rc2 no-rc4 no-rc5 no-idea no-bf no-cast no-whirlpool no-md2 no-md4 no-ripemd no-mdc2 \
no-camellia no-seed no-comp no-krb5 no-gmp no-rfc3779 no-ec2m no-ssl2 no-jpake no-srp no-sctp no-srtp \
--prefix=~/dev/stage --cross-compile-prefix=x86_64-w64-mingw32-
make depend
make
make install
cd ..
```
and Zlib
```sh
git clone https://github.com/madler/zlib
cd zlib
git checkout v1.2.8
CC=x86_64-w64-mingw32-gcc CFLAGS=-O3 ./configure --static --64 --prefix=~/dev/stage
make
make install
cd ..
```
Now we prepare cross toolchain hint file for CMake, let's name it `~/dev/toolchain-mingw.cmake`
```cmake
SET(CMAKE_SYSTEM_NAME Windows)
SET(CMAKE_C_COMPILER x86_64-w64-mingw32-gcc)
SET(CMAKE_CXX_COMPILER x86_64-w64-mingw32-g++)
SET(CMAKE_RC_COMPILER x86_64-w64-mingw32-windres)
SET(CMAKE_FIND_ROOT_PATH /usr/x86_64-w64-mingw32)
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
```
Download miniupnpc, unpack, and symlink it into `~/dev/miniupnpc/`.
Finally, we can build i2pd with all that goodness
```sh
git clone https://github.com/PurpleI2P/i2pd
mkdir i2pd-mingw-64-build
cd i2pd-mingw-64-build
BOOST_ROOT=~/dev/boost_1_60_0 cmake -G 'Unix Makefiles' ~/dev/i2pd/build -DBUILD_TYPE=Release \
-DCMAKE_TOOLCHAIN_FILE=~/dev/toolchain-mingw.cmake -DWITH_AESNI=ON -DWITH_UPNP=ON -DWITH_STATIC=ON \
-DWITH_HARDENING=ON -DCMAKE_INSTALL_PREFIX:PATH=~/dev/i2pd-mingw-64-static \
-DZLIB_ROOT=~/dev/stage -DBOOST_LIBRARYDIR:PATH=~/dev/boost_1_60_0/stage-mingw-64/lib \
-DOPENSSL_ROOT_DIR:PATH=~/dev/stage
make
x86_64-w64-mingw32-strip i2pd.exe
```
By now, you should have a release build with stripped symbols.

2
docs/build_requirements.md

@ -4,7 +4,7 @@ Build requirements
Linux/FreeBSD/OSX Linux/FreeBSD/OSX
----------------- -----------------
GCC 4.6 or newer, Boost 1.46 or newer, openssl, zlib. Clang can be used instead of GCC. GCC 4.8 or newer, Boost 1.49 or newer, openssl, zlib. Clang can be used instead of GCC.
Windows Windows
------- -------

19
docs/configuration.md

@ -17,9 +17,9 @@ Command line options
* --port= - The port to listen on * --port= - The port to listen on
* --daemon - Router will go to background after start * --daemon - Router will go to background after start
* --service - Router will use system folders like '/var/lib/i2pd' * --service - Router will use system folders like '/var/lib/i2pd'
* --ipv6 - Enable communication through ipv6 * --ipv6 - Enable communication through ipv6. false by default
* --notransit - Router will not accept transit tunnels at startup * --notransit - Router will not accept transit tunnels at startup. false by default
* --floodfill - Router will be floodfill * --floodfill - Router will be floodfill. false by default
* --bandwidth= - L if bandwidth is limited to 32Kbs/sec, O - to 256Kbs/sec, P - unlimited * --bandwidth= - L if bandwidth is limited to 32Kbs/sec, O - to 256Kbs/sec, P - unlimited
* --family= - Name of a family, router belongs to * --family= - Name of a family, router belongs to
* --svcctl= - Windows service management (--svcctl="install" or --svcctl="remove") * --svcctl= - Windows service management (--svcctl="install" or --svcctl="remove")
@ -30,21 +30,26 @@ Command line options
* --httpproxy.address= - The address to listen on (HTTP Proxy) * --httpproxy.address= - The address to listen on (HTTP Proxy)
* --httpproxy.port= - The port to listen on (HTTP Proxy) 4446 by default * --httpproxy.port= - The port to listen on (HTTP Proxy) 4446 by default
* --httpproxy.keys= - optional keys file for proxy local destination (both HTTP and SOCKS) * --httpproxy.keys= - optional keys file for proxy local destination (both HTTP and SOCKS)
* --httpproxy.enabled= - If HTTP proxy is enabled. true by default
* --socksproxy.address= - The address to listen on (SOCKS Proxy) * --socksproxy.address= - The address to listen on (SOCKS Proxy)
* --socksproxy.port= - The port to listen on (SOCKS Proxy). 4447 by default * --socksproxy.port= - The port to listen on (SOCKS Proxy). 4447 by default
* --socksproxy.keys= - optional keys file for proxy local destination (both HTTP and SOCKS) * --socksproxy.keys= - optional keys file for proxy local destination (both HTTP and SOCKS)
* --socksproxy.outproxy= - Address of outproxy. requests outside i2p will go there * --socksproxy.enabled= - If SOCKS proxy is enabled. true by default
* --socksproxy.outproxy= - Address of outproxy. requests outside i2p will go there
* --socksproxy.outproxyport= - Outproxy remote port * --socksproxy.outproxyport= - Outproxy remote port
* --sam.address= - The address to listen on (SAM bridge) * --sam.address= - The address to listen on (SAM bridge)
* --sam.port= - Port of SAM bridge. Usually 7656. SAM is off if not specified * --sam.port= - Port of SAM bridge. Usually 7656. SAM is off if not specified
* --sam.enabled= - If SAM is enabled. false by default
* --bob.address= - The address to listen on (BOB command channel) * --bob.address= - The address to listen on (BOB command channel)
* --bob.port= - Port of BOB command channel. Usually 2827. BOB is off if not specified * --bob.port= - Port of BOB command channel. Usually 2827. BOB is off if not specified
* --sam.enabled= - If BOB is enabled. false by default
* --i2pcontrol.address= - The address to listen on (I2P control service) * --i2pcontrol.address= - The address to listen on (I2P control service)
* --i2pcontrol.port= - Port of I2P control service. Usually 7650. I2PControl is off if not specified * --i2pcontrol.port= - Port of I2P control service. Usually 7650. I2PControl is off if not specified
* --i2pcontrol.enabled= - If I2P control is enabled. false by default
Config files Config files
------------ ------------
@ -58,15 +63,15 @@ For example:
i2p.conf: i2p.conf:
# comment # comment
log = yes log = true
ipv6 = yes ipv6 = true
# settings for specific module # settings for specific module
[httpproxy] [httpproxy]
port = 4444 port = 4444
# ^^ this will be --httproxy.port= in cmdline # ^^ this will be --httproxy.port= in cmdline
# another one # another one
[sam] [sam]
enabled = yes enabled = true
tunnels.cfg (filename of this config is subject of change): tunnels.cfg (filename of this config is subject of change):

111
docs/i2pd.conf

@ -0,0 +1,111 @@
## Configuration file for a typical i2pd user
## See https://i2pd.readthedocs.org/en/latest/configuration.html
## for more options you can use in this file.
## Lines that begin with "## " try to explain what's going on. Lines
## that begin with just "#" are disabled commands: you can enable them
## by removing the "#" symbol.
## Tunnels config file
## Default: ~/.i2pd/tunnels.cfg or /var/lib/i2pd/tunnels.cfg
#tunconf = /var/lib/i2pd/tunnels.cfg
## Where to write pidfile (don't write by default)
#pidfile = /var/run/i2pd.pid
## Logging configuration section
## By default logs go to stdout with level info
##
## Logs destination (stdout, file)
#log = file
## Path to logfile (default - autodetect)
#logfile = /var/log/i2pd.log
## Log messages above this level (debug, *info, warn, error)
#loglevel = info
## Path to storage of i2pd data (RI, keys, peer profiles, ...)
## Default: ~/.i2pd or /var/lib/i2pd
#datadir = /var/lib/i2pd
## Daemon mode. Router will go to background after start
#daemon
## Run as a service. Router will use system folders like ‘/var/lib/i2pd’
#service
## External IP address to listen for connections
## By default i2pd sets IP automatically
#host = 1.2.3.4
## Port to listen for connections
## By default i2pd picks random port. You MUST pick a random number too,
## don't just uncomment this
#port = 4321
##Enable communication through ipv6
ipv6
## Bandwidth configuration
## L limit bandwidth to 32Kbs/sec, O - to 256Kbs/sec, P - unlimited
## Default is P for floodfill, L for regular node
#bandwidth = L
## Router will not accept transit tunnels at startup
#notransit
## Router will be floodfill
#floodfill
## Section for Web Console
## By default it's available at 127.0.0.1:7070 even if it's not configured
[http]
## The address to listen on
address = 127.0.0.1
## The port to listen on
port = 7070
## Section for HTTP proxy
## By default it's available at 127.0.0.1:4444 even if it's not configured
[httpproxy]
## The address to listen on
address = 127.0.0.1
## The port to listen on
port = 4444
## Optional keys file for proxy local destination
#keys = http-proxy-keys.dat
## Uncomment if you want to disable HTTP proxy
#enabled=false
## Section for Socks proxy
## By default it's available at 127.0.0.1:4447 even if it's not configured
#[socksproxy]
## The address to listen on
#address = 127.0.0.1
## The port to listen on
#port = 4447
## Optional keys file for proxy local destination
#keys = socks-proxy-keys.dat
## Uncomment if you want to disable Socks proxy
#enabled=false
## Socks outproxy. Example below is set to use Tor for all connections except i2p
## Address of outproxy
#outproxy = 127.0.0.1
## Outproxy remote port
#outproxyport = 9050
## Section for SAM bridge
#[sam]
## The address to listen on
#address = 127.0.0.1
## Port of SAM bridge
#port = 7656
## Section for BOB command channel
#[bob]
## The address to listen on
#address = 127.0.0.1
## Port of BOB command channel. Usually 2827. BOB is off if not specified
#port = 2827
## Section for I2PControl protocol
#[i2pcontrol]
## The address to listen on
#address = 127.0.0.1
## Port of I2P control service
#port = 7650

8
util.cpp

@ -106,11 +106,15 @@ namespace http
while (!response.eof ()) while (!response.eof ())
{ {
std::string hexLen; std::string hexLen;
int len; size_t len;
std::getline (response, hexLen); std::getline (response, hexLen);
std::istringstream iss (hexLen); std::istringstream iss (hexLen);
iss >> std::hex >> len; iss >> std::hex >> len;
if (!len) break; if (!len || len > 10000000L) // 10M
{
LogPrint (eLogError, "Unexpected chunk length ", len);
break;
}
char * buf = new char[len]; char * buf = new char[len];
response.read (buf, len); response.read (buf, len);
merged.write (buf, len); merged.write (buf, len);

2
version.h

@ -16,7 +16,7 @@
#define I2P_VERSION_MAJOR 0 #define I2P_VERSION_MAJOR 0
#define I2P_VERSION_MINOR 9 #define I2P_VERSION_MINOR 9
#define I2P_VERSION_MICRO 24 #define I2P_VERSION_MICRO 25
#define I2P_VERSION_PATCH 0 #define I2P_VERSION_PATCH 0
#define I2P_VERSION MAKE_VERSION(I2P_VERSION_MAJOR, I2P_VERSION_MINOR, I2P_VERSION_MICRO) #define I2P_VERSION MAKE_VERSION(I2P_VERSION_MAJOR, I2P_VERSION_MINOR, I2P_VERSION_MICRO)

Loading…
Cancel
Save