diff --git a/AddressBook.cpp b/AddressBook.cpp index eba7f6b7..6d2893d4 100644 --- a/AddressBook.cpp +++ b/AddressBook.cpp @@ -21,549 +21,549 @@ namespace i2p namespace client { - class AddressBookFilesystemStorage: public AddressBookStorage - { - public: + class AddressBookFilesystemStorage: public AddressBookStorage + { + public: - AddressBookFilesystemStorage (); - bool GetAddress (const i2p::data::IdentHash& ident, i2p::data::IdentityEx& address) const; - void AddAddress (const i2p::data::IdentityEx& address); - void RemoveAddress (const i2p::data::IdentHash& ident); + AddressBookFilesystemStorage (); + bool GetAddress (const i2p::data::IdentHash& ident, i2p::data::IdentityEx& address) const; + void AddAddress (const i2p::data::IdentityEx& address); + void RemoveAddress (const i2p::data::IdentHash& ident); - int Load (std::map& addresses); - int Save (const std::map& addresses); + int Load (std::map& addresses); + int Save (const std::map& addresses); - private: - - boost::filesystem::path GetPath () const { return i2p::util::filesystem::GetDefaultDataDir() / "addressbook"; }; + private: + + boost::filesystem::path GetPath () const { return i2p::util::filesystem::GetDefaultDataDir() / "addressbook"; }; - }; + }; - AddressBookFilesystemStorage::AddressBookFilesystemStorage () - { - auto path = GetPath (); - if (!boost::filesystem::exists (path)) - { - // Create directory is necessary - if (!boost::filesystem::create_directory (path)) - LogPrint (eLogError, "Failed to create addressbook directory"); - } - } + AddressBookFilesystemStorage::AddressBookFilesystemStorage () + { + auto path = GetPath (); + if (!boost::filesystem::exists (path)) + { + // Create directory is necessary + if (!boost::filesystem::create_directory (path)) + LogPrint (eLogError, "Failed to create addressbook directory"); + } + } - bool AddressBookFilesystemStorage::GetAddress (const i2p::data::IdentHash& ident, i2p::data::IdentityEx& address) const - { - auto filename = GetPath () / (ident.ToBase32() + ".b32"); - std::ifstream f(filename.c_str (), std::ifstream::binary); - if (f.is_open ()) - { - f.seekg (0,std::ios::end); - size_t len = f.tellg (); - if (len < i2p::data::DEFAULT_IDENTITY_SIZE) - { - LogPrint (eLogError, "File ", filename, " is too short. ", len); - return false; - } - f.seekg(0, std::ios::beg); - uint8_t * buf = new uint8_t[len]; - f.read((char *)buf, len); - address.FromBuffer (buf, len); - delete[] buf; - return true; - } - else - return false; - } + bool AddressBookFilesystemStorage::GetAddress (const i2p::data::IdentHash& ident, i2p::data::IdentityEx& address) const + { + auto filename = GetPath () / (ident.ToBase32() + ".b32"); + std::ifstream f(filename.c_str (), std::ifstream::binary); + if (f.is_open ()) + { + f.seekg (0,std::ios::end); + size_t len = f.tellg (); + if (len < i2p::data::DEFAULT_IDENTITY_SIZE) + { + LogPrint (eLogError, "File ", filename, " is too short. ", len); + return false; + } + f.seekg(0, std::ios::beg); + uint8_t * buf = new uint8_t[len]; + f.read((char *)buf, len); + address.FromBuffer (buf, len); + delete[] buf; + return true; + } + else + return false; + } - void AddressBookFilesystemStorage::AddAddress (const i2p::data::IdentityEx& address) - { - auto filename = GetPath () / (address.GetIdentHash ().ToBase32() + ".b32"); - std::ofstream f (filename.c_str (), std::ofstream::binary | std::ofstream::out); - if (f.is_open ()) - { - size_t len = address.GetFullLen (); - uint8_t * buf = new uint8_t[len]; - address.ToBuffer (buf, len); - f.write ((char *)buf, len); - delete[] buf; - } - else - LogPrint (eLogError, "Can't open file ", filename); - } + void AddressBookFilesystemStorage::AddAddress (const i2p::data::IdentityEx& address) + { + auto filename = GetPath () / (address.GetIdentHash ().ToBase32() + ".b32"); + std::ofstream f (filename.c_str (), std::ofstream::binary | std::ofstream::out); + if (f.is_open ()) + { + size_t len = address.GetFullLen (); + uint8_t * buf = new uint8_t[len]; + address.ToBuffer (buf, len); + f.write ((char *)buf, len); + delete[] buf; + } + else + LogPrint (eLogError, "Can't open file ", filename); + } - void AddressBookFilesystemStorage::RemoveAddress (const i2p::data::IdentHash& ident) - { - auto filename = GetPath () / (ident.ToBase32() + ".b32"); - if (boost::filesystem::exists (filename)) - boost::filesystem::remove (filename); - } + void AddressBookFilesystemStorage::RemoveAddress (const i2p::data::IdentHash& ident) + { + auto filename = GetPath () / (ident.ToBase32() + ".b32"); + if (boost::filesystem::exists (filename)) + boost::filesystem::remove (filename); + } - int AddressBookFilesystemStorage::Load (std::map& addresses) - { - int num = 0; - auto filename = GetPath () / "addresses.csv"; - std::ifstream f (filename.c_str (), std::ofstream::in); // in text mode - if (f.is_open ()) - { - addresses.clear (); - while (!f.eof ()) - { - std::string s; - getline(f, s); - if (!s.length()) - continue; // skip empty line + int AddressBookFilesystemStorage::Load (std::map& addresses) + { + int num = 0; + auto filename = GetPath () / "addresses.csv"; + std::ifstream f (filename.c_str (), std::ofstream::in); // in text mode + if (f.is_open ()) + { + addresses.clear (); + while (!f.eof ()) + { + std::string s; + getline(f, s); + if (!s.length()) + continue; // skip empty line - size_t pos = s.find(','); - if (pos != std::string::npos) - { - std::string name = s.substr(0, pos++); - std::string addr = s.substr(pos); + size_t pos = s.find(','); + if (pos != std::string::npos) + { + std::string name = s.substr(0, pos++); + std::string addr = s.substr(pos); - i2p::data::IdentHash ident; - ident.FromBase32 (addr); - addresses[name] = ident; - num++; - } - } - LogPrint (eLogInfo, num, " addresses loaded"); - } - else - LogPrint (eLogWarning, filename, " not found"); - return num; - } + i2p::data::IdentHash ident; + ident.FromBase32 (addr); + addresses[name] = ident; + num++; + } + } + LogPrint (eLogInfo, num, " addresses loaded"); + } + else + LogPrint (eLogWarning, filename, " not found"); + return num; + } - int AddressBookFilesystemStorage::Save (const std::map& addresses) - { - int num = 0; - auto filename = GetPath () / "addresses.csv"; - std::ofstream f (filename.c_str (), std::ofstream::out); // in text mode - if (f.is_open ()) - { - for (auto it: addresses) - { - f << it.first << "," << it.second.ToBase32 () << std::endl; - num++; - } - LogPrint (eLogInfo, num, " addresses saved"); - } - else - LogPrint (eLogError, "Can't open file ", filename); - return num; - } + int AddressBookFilesystemStorage::Save (const std::map& addresses) + { + int num = 0; + auto filename = GetPath () / "addresses.csv"; + std::ofstream f (filename.c_str (), std::ofstream::out); // in text mode + if (f.is_open ()) + { + for (auto it: addresses) + { + f << it.first << "," << it.second.ToBase32 () << std::endl; + num++; + } + LogPrint (eLogInfo, num, " addresses saved"); + } + else + LogPrint (eLogError, "Can't open file ", filename); + return num; + } //--------------------------------------------------------------------- - AddressBook::AddressBook (): m_Storage (nullptr), m_IsLoaded (false), m_IsDownloading (false), - m_DefaultSubscription (nullptr), m_SubscriptionsUpdateTimer (nullptr) - { - } + AddressBook::AddressBook (): m_Storage (nullptr), m_IsLoaded (false), m_IsDownloading (false), + m_DefaultSubscription (nullptr), m_SubscriptionsUpdateTimer (nullptr) + { + } - AddressBook::~AddressBook () - { - Stop (); - } + AddressBook::~AddressBook () + { + Stop (); + } - void AddressBook::Start () - { - StartSubscriptions (); - } - - void AddressBook::Stop () - { - StopSubscriptions (); - if (m_SubscriptionsUpdateTimer) - { - delete m_SubscriptionsUpdateTimer; - m_SubscriptionsUpdateTimer = nullptr; - } - if (m_IsDownloading) - { - LogPrint (eLogInfo, "Subscription is downloading. Waiting for temination..."); - for (int i = 0; i < 30; i++) - { - if (!m_IsDownloading) - { - LogPrint (eLogInfo, "Subscription download complete"); - break; - } - std::this_thread::sleep_for (std::chrono::seconds (1)); // wait for 1 seconds - } - LogPrint (eLogError, "Subscription download hangs"); - m_IsDownloading = false; - } - if (m_Storage) - { - m_Storage->Save (m_Addresses); - delete m_Storage; - m_Storage = nullptr; - } - if (m_DefaultSubscription) - { - delete m_DefaultSubscription; - m_DefaultSubscription = nullptr; - } - for (auto it: m_Subscriptions) - delete it; - m_Subscriptions.clear (); - } - - AddressBookStorage * AddressBook::CreateStorage () - { - return new AddressBookFilesystemStorage (); - } + void AddressBook::Start () + { + StartSubscriptions (); + } + + void AddressBook::Stop () + { + StopSubscriptions (); + if (m_SubscriptionsUpdateTimer) + { + delete m_SubscriptionsUpdateTimer; + m_SubscriptionsUpdateTimer = nullptr; + } + if (m_IsDownloading) + { + LogPrint (eLogInfo, "Subscription is downloading. Waiting for temination..."); + for (int i = 0; i < 30; i++) + { + if (!m_IsDownloading) + { + LogPrint (eLogInfo, "Subscription download complete"); + break; + } + std::this_thread::sleep_for (std::chrono::seconds (1)); // wait for 1 seconds + } + LogPrint (eLogError, "Subscription download hangs"); + m_IsDownloading = false; + } + if (m_Storage) + { + m_Storage->Save (m_Addresses); + delete m_Storage; + m_Storage = nullptr; + } + if (m_DefaultSubscription) + { + delete m_DefaultSubscription; + m_DefaultSubscription = nullptr; + } + for (auto it: m_Subscriptions) + delete it; + m_Subscriptions.clear (); + } + + AddressBookStorage * AddressBook::CreateStorage () + { + return new AddressBookFilesystemStorage (); + } - bool AddressBook::GetIdentHash (const std::string& address, i2p::data::IdentHash& ident) - { - auto pos = address.find(".b32.i2p"); - if (pos != std::string::npos) - { - Base32ToByteStream (address.c_str(), pos, ident, 32); - return true; - } - else - { - pos = address.find (".i2p"); - if (pos != std::string::npos) - { - auto identHash = FindAddress (address); - if (identHash) - { - ident = *identHash; - return true; - } - else - return false; - } - } - // if not .b32 we assume full base64 address - i2p::data::IdentityEx dest; - if (!dest.FromBase64 (address)) - return false; - ident = dest.GetIdentHash (); - return true; - } - - const i2p::data::IdentHash * AddressBook::FindAddress (const std::string& address) - { - if (!m_IsLoaded) - LoadHosts (); - if (m_IsLoaded) - { - auto it = m_Addresses.find (address); - if (it != m_Addresses.end ()) - return &it->second; - } - return nullptr; - } + bool AddressBook::GetIdentHash (const std::string& address, i2p::data::IdentHash& ident) + { + auto pos = address.find(".b32.i2p"); + if (pos != std::string::npos) + { + Base32ToByteStream (address.c_str(), pos, ident, 32); + return true; + } + else + { + pos = address.find (".i2p"); + if (pos != std::string::npos) + { + auto identHash = FindAddress (address); + if (identHash) + { + ident = *identHash; + return true; + } + else + return false; + } + } + // if not .b32 we assume full base64 address + i2p::data::IdentityEx dest; + if (!dest.FromBase64 (address)) + return false; + ident = dest.GetIdentHash (); + return true; + } + + const i2p::data::IdentHash * AddressBook::FindAddress (const std::string& address) + { + if (!m_IsLoaded) + LoadHosts (); + if (m_IsLoaded) + { + auto it = m_Addresses.find (address); + if (it != m_Addresses.end ()) + return &it->second; + } + return nullptr; + } - void AddressBook::InsertAddress (const std::string& address, const std::string& base64) - { - i2p::data::IdentityEx ident; - ident.FromBase64 (base64); - if (!m_Storage) - m_Storage = CreateStorage (); - m_Storage->AddAddress (ident); - m_Addresses[address] = ident.GetIdentHash (); - LogPrint (address,"->", ToAddress(ident.GetIdentHash ()), " added"); - } + void AddressBook::InsertAddress (const std::string& address, const std::string& base64) + { + i2p::data::IdentityEx ident; + ident.FromBase64 (base64); + if (!m_Storage) + m_Storage = CreateStorage (); + m_Storage->AddAddress (ident); + m_Addresses[address] = ident.GetIdentHash (); + LogPrint (address,"->", ToAddress(ident.GetIdentHash ()), " added"); + } - void AddressBook::InsertAddress (const i2p::data::IdentityEx& address) - { - if (!m_Storage) - m_Storage = CreateStorage (); - m_Storage->AddAddress (address); - } + void AddressBook::InsertAddress (const i2p::data::IdentityEx& address) + { + if (!m_Storage) + m_Storage = CreateStorage (); + m_Storage->AddAddress (address); + } - bool AddressBook::GetAddress (const std::string& address, i2p::data::IdentityEx& identity) - { - if (!m_Storage) - m_Storage = CreateStorage (); - i2p::data::IdentHash ident; - if (!GetIdentHash (address, ident)) return false; - return m_Storage->GetAddress (ident, identity); - } + bool AddressBook::GetAddress (const std::string& address, i2p::data::IdentityEx& identity) + { + if (!m_Storage) + m_Storage = CreateStorage (); + i2p::data::IdentHash ident; + if (!GetIdentHash (address, ident)) return false; + return m_Storage->GetAddress (ident, identity); + } - void AddressBook::LoadHosts () - { - if (!m_Storage) - m_Storage = CreateStorage (); - if (m_Storage->Load (m_Addresses) > 0) - { - m_IsLoaded = true; - return; - } - - // try hosts.txt first - std::ifstream f (i2p::util::filesystem::GetFullPath ("hosts.txt").c_str (), std::ofstream::in); // in text mode - if (f.is_open ()) - { - LoadHostsFromStream (f); - m_IsLoaded = true; - } - else - { - // if not found download it from http://i2p-projekt.i2p/hosts.txt - LogPrint (eLogInfo, "hosts.txt not found. Try to download it from default subscription..."); - if (!m_IsDownloading) - { - m_IsDownloading = true; - if (!m_DefaultSubscription) - m_DefaultSubscription = new AddressBookSubscription (*this, DEFAULT_SUBSCRIPTION_ADDRESS); - m_DefaultSubscription->CheckSubscription (); - } - } - - } + void AddressBook::LoadHosts () + { + if (!m_Storage) + m_Storage = CreateStorage (); + if (m_Storage->Load (m_Addresses) > 0) + { + m_IsLoaded = true; + return; + } + + // try hosts.txt first + std::ifstream f (i2p::util::filesystem::GetFullPath ("hosts.txt").c_str (), std::ofstream::in); // in text mode + if (f.is_open ()) + { + LoadHostsFromStream (f); + m_IsLoaded = true; + } + else + { + // if not found download it from http://i2p-projekt.i2p/hosts.txt + LogPrint (eLogInfo, "hosts.txt not found. Try to download it from default subscription..."); + if (!m_IsDownloading) + { + m_IsDownloading = true; + if (!m_DefaultSubscription) + m_DefaultSubscription = new AddressBookSubscription (*this, DEFAULT_SUBSCRIPTION_ADDRESS); + m_DefaultSubscription->CheckSubscription (); + } + } + + } - void AddressBook::LoadHostsFromStream (std::istream& f) - { - std::unique_lock l(m_AddressBookMutex); - int numAddresses = 0; - std::string s; - while (!f.eof ()) - { - getline(f, s); + void AddressBook::LoadHostsFromStream (std::istream& f) + { + std::unique_lock l(m_AddressBookMutex); + int numAddresses = 0; + std::string s; + while (!f.eof ()) + { + getline(f, s); - if (!s.length()) - continue; // skip empty line + if (!s.length()) + continue; // skip empty line - size_t pos = s.find('='); + size_t pos = s.find('='); - if (pos != std::string::npos) - { - std::string name = s.substr(0, pos++); - std::string addr = s.substr(pos); + if (pos != std::string::npos) + { + std::string name = s.substr(0, pos++); + std::string addr = s.substr(pos); - i2p::data::IdentityEx ident; - if (ident.FromBase64(addr)) - { - m_Addresses[name] = ident.GetIdentHash (); - m_Storage->AddAddress (ident); - numAddresses++; - } - else - LogPrint (eLogError, "Malformed address ", addr, " for ", name); - } - } - LogPrint (eLogInfo, numAddresses, " addresses processed"); - if (numAddresses > 0) - { - m_IsLoaded = true; - m_Storage->Save (m_Addresses); - } - } - - void AddressBook::LoadSubscriptions () - { - if (!m_Subscriptions.size ()) - { - std::ifstream f (i2p::util::filesystem::GetFullPath ("subscriptions.txt").c_str (), std::ofstream::in); // in text mode - if (f.is_open ()) - { - std::string s; - while (!f.eof ()) - { - getline(f, s); - if (!s.length()) continue; // skip empty line - m_Subscriptions.push_back (new AddressBookSubscription (*this, s)); - } - LogPrint (eLogInfo, m_Subscriptions.size (), " subscriptions loaded"); - } - else - LogPrint (eLogWarning, "subscriptions.txt not found"); - } - else - LogPrint (eLogError, "Subscriptions already loaded"); - } + i2p::data::IdentityEx ident; + if (ident.FromBase64(addr)) + { + m_Addresses[name] = ident.GetIdentHash (); + m_Storage->AddAddress (ident); + numAddresses++; + } + else + LogPrint (eLogError, "Malformed address ", addr, " for ", name); + } + } + LogPrint (eLogInfo, numAddresses, " addresses processed"); + if (numAddresses > 0) + { + m_IsLoaded = true; + m_Storage->Save (m_Addresses); + } + } + + void AddressBook::LoadSubscriptions () + { + if (!m_Subscriptions.size ()) + { + std::ifstream f (i2p::util::filesystem::GetFullPath ("subscriptions.txt").c_str (), std::ofstream::in); // in text mode + if (f.is_open ()) + { + std::string s; + while (!f.eof ()) + { + getline(f, s); + if (!s.length()) continue; // skip empty line + m_Subscriptions.push_back (new AddressBookSubscription (*this, s)); + } + LogPrint (eLogInfo, m_Subscriptions.size (), " subscriptions loaded"); + } + else + LogPrint (eLogWarning, "subscriptions.txt not found"); + } + else + LogPrint (eLogError, "Subscriptions already loaded"); + } - void AddressBook::DownloadComplete (bool success) - { - m_IsDownloading = false; - if (m_SubscriptionsUpdateTimer) - { - m_SubscriptionsUpdateTimer->expires_from_now (boost::posix_time::minutes( - success ? CONTINIOUS_SUBSCRIPTION_UPDATE_TIMEOUT : CONTINIOUS_SUBSCRIPTION_RETRY_TIMEOUT)); - m_SubscriptionsUpdateTimer->async_wait (std::bind (&AddressBook::HandleSubscriptionsUpdateTimer, - this, std::placeholders::_1)); - } - } + void AddressBook::DownloadComplete (bool success) + { + m_IsDownloading = false; + if (m_SubscriptionsUpdateTimer) + { + m_SubscriptionsUpdateTimer->expires_from_now (boost::posix_time::minutes( + success ? CONTINIOUS_SUBSCRIPTION_UPDATE_TIMEOUT : CONTINIOUS_SUBSCRIPTION_RETRY_TIMEOUT)); + m_SubscriptionsUpdateTimer->async_wait (std::bind (&AddressBook::HandleSubscriptionsUpdateTimer, + this, std::placeholders::_1)); + } + } - void AddressBook::StartSubscriptions () - { - LoadSubscriptions (); - if (!m_Subscriptions.size ()) return; + void AddressBook::StartSubscriptions () + { + LoadSubscriptions (); + if (!m_Subscriptions.size ()) return; - auto dest = i2p::client::context.GetSharedLocalDestination (); - if (dest) - { - m_SubscriptionsUpdateTimer = new boost::asio::deadline_timer (dest->GetService ()); - m_SubscriptionsUpdateTimer->expires_from_now (boost::posix_time::minutes(INITIAL_SUBSCRIPTION_UPDATE_TIMEOUT)); - m_SubscriptionsUpdateTimer->async_wait (std::bind (&AddressBook::HandleSubscriptionsUpdateTimer, - this, std::placeholders::_1)); - } - else - LogPrint (eLogError, "Can't start subscriptions: missing shared local destination"); - } + auto dest = i2p::client::context.GetSharedLocalDestination (); + if (dest) + { + m_SubscriptionsUpdateTimer = new boost::asio::deadline_timer (dest->GetService ()); + m_SubscriptionsUpdateTimer->expires_from_now (boost::posix_time::minutes(INITIAL_SUBSCRIPTION_UPDATE_TIMEOUT)); + m_SubscriptionsUpdateTimer->async_wait (std::bind (&AddressBook::HandleSubscriptionsUpdateTimer, + this, std::placeholders::_1)); + } + else + LogPrint (eLogError, "Can't start subscriptions: missing shared local destination"); + } - void AddressBook::StopSubscriptions () - { - if (m_SubscriptionsUpdateTimer) - m_SubscriptionsUpdateTimer->cancel (); - } + void AddressBook::StopSubscriptions () + { + if (m_SubscriptionsUpdateTimer) + m_SubscriptionsUpdateTimer->cancel (); + } - void AddressBook::HandleSubscriptionsUpdateTimer (const boost::system::error_code& ecode) - { - if (ecode != boost::asio::error::operation_aborted) - { - auto dest = i2p::client::context.GetSharedLocalDestination (); - if (!dest) return; - if (m_IsLoaded && !m_IsDownloading && dest->IsReady ()) - { - // pick random subscription - CryptoPP::AutoSeededRandomPool rnd; - auto ind = rnd.GenerateWord32 (0, m_Subscriptions.size() - 1); - m_IsDownloading = true; - m_Subscriptions[ind]->CheckSubscription (); - } - else - { - if (!m_IsLoaded) - LoadHosts (); - // try it again later - m_SubscriptionsUpdateTimer->expires_from_now (boost::posix_time::minutes(INITIAL_SUBSCRIPTION_RETRY_TIMEOUT)); - m_SubscriptionsUpdateTimer->async_wait (std::bind (&AddressBook::HandleSubscriptionsUpdateTimer, - this, std::placeholders::_1)); - } - } - } + void AddressBook::HandleSubscriptionsUpdateTimer (const boost::system::error_code& ecode) + { + if (ecode != boost::asio::error::operation_aborted) + { + auto dest = i2p::client::context.GetSharedLocalDestination (); + if (!dest) return; + if (m_IsLoaded && !m_IsDownloading && dest->IsReady ()) + { + // pick random subscription + CryptoPP::AutoSeededRandomPool rnd; + auto ind = rnd.GenerateWord32 (0, m_Subscriptions.size() - 1); + m_IsDownloading = true; + m_Subscriptions[ind]->CheckSubscription (); + } + else + { + if (!m_IsLoaded) + LoadHosts (); + // try it again later + m_SubscriptionsUpdateTimer->expires_from_now (boost::posix_time::minutes(INITIAL_SUBSCRIPTION_RETRY_TIMEOUT)); + m_SubscriptionsUpdateTimer->async_wait (std::bind (&AddressBook::HandleSubscriptionsUpdateTimer, + this, std::placeholders::_1)); + } + } + } - AddressBookSubscription::AddressBookSubscription (AddressBook& book, const std::string& link): - m_Book (book), m_Link (link) - { - } + AddressBookSubscription::AddressBookSubscription (AddressBook& book, const std::string& link): + m_Book (book), m_Link (link) + { + } - void AddressBookSubscription::CheckSubscription () - { - std::thread load_hosts(&AddressBookSubscription::Request, this); - load_hosts.detach(); // TODO: use join - } + void AddressBookSubscription::CheckSubscription () + { + std::thread load_hosts(&AddressBookSubscription::Request, this); + load_hosts.detach(); // TODO: use join + } - void AddressBookSubscription::Request () - { - // must be run in separate thread - LogPrint (eLogInfo, "Downloading hosts from ", m_Link, " ETag: ", m_Etag, " Last-Modified: ", m_LastModified); - bool success = false; - i2p::util::http::url u (m_Link); - i2p::data::IdentHash ident; - if (m_Book.GetIdentHash (u.host_, ident)) - { - std::condition_variable newDataReceived; - std::mutex newDataReceivedMutex; - auto leaseSet = i2p::client::context.GetSharedLocalDestination ()->FindLeaseSet (ident); - if (!leaseSet) - { - std::unique_lock l(newDataReceivedMutex); - i2p::client::context.GetSharedLocalDestination ()->RequestDestination (ident, - [&newDataReceived, &leaseSet](std::shared_ptr ls) - { - leaseSet = ls; - newDataReceived.notify_all (); - }); - if (newDataReceived.wait_for (l, std::chrono::seconds (SUBSCRIPTION_REQUEST_TIMEOUT)) == std::cv_status::timeout) - LogPrint (eLogError, "Subscription LeseseSet request timeout expired"); - } - if (leaseSet) - { - std::stringstream request, response; - // standard header - request << "GET " << u.path_ << " HTTP/1.1\r\nHost: " << u.host_ - << "\r\nAccept: */*\r\n" << "User-Agent: Wget/1.11.4\r\n" << "Connection: close\r\n"; - if (m_Etag.length () > 0) // etag - request << i2p::util::http::IF_NONE_MATCH << ": \"" << m_Etag << "\"\r\n"; - if (m_LastModified.length () > 0) // if-modfief-since - request << i2p::util::http::IF_MODIFIED_SINCE << ": " << m_LastModified << "\r\n"; - request << "\r\n"; // end of header - auto stream = i2p::client::context.GetSharedLocalDestination ()->CreateStream (leaseSet, u.port_); - stream->Send ((uint8_t *)request.str ().c_str (), request.str ().length ()); - - uint8_t buf[4096]; - bool end = false; - while (!end) - { - stream->AsyncReceive (boost::asio::buffer (buf, 4096), - [&](const boost::system::error_code& ecode, std::size_t bytes_transferred) - { - if (bytes_transferred) - response.write ((char *)buf, bytes_transferred); - if (ecode == boost::asio::error::timed_out || !stream->IsOpen ()) - end = true; - newDataReceived.notify_all (); - }, - 30); // wait for 30 seconds - std::unique_lock l(newDataReceivedMutex); - if (newDataReceived.wait_for (l, std::chrono::seconds (SUBSCRIPTION_REQUEST_TIMEOUT)) == std::cv_status::timeout) - LogPrint (eLogError, "Subscription timeout expired"); - } - // 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; - 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); - auto colon = header.find (':'); - if (colon != std::string::npos) - { - std::string field = header.substr (0, 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"); - } - } - LogPrint (eLogInfo, m_Link, " ETag: ", m_Etag, " Last-Modified: ", m_LastModified); - if (!response.eof ()) - { - success = true; - if (!isChunked) - m_Book.LoadHostsFromStream (response); - else - { - // merge chunks - std::stringstream merged; - i2p::util::http::MergeChunkedResponse (response, merged); - m_Book.LoadHostsFromStream (merged); - } - } - } - else if (status == 304) - { - success = true; - LogPrint (eLogInfo, "No updates from ", m_Link); - } - else - LogPrint (eLogWarning, "Adressbook HTTP response ", status); - } - else - LogPrint (eLogError, "Address ", u.host_, " not found"); - } - else - LogPrint (eLogError, "Can't resolve ", u.host_); - LogPrint (eLogInfo, "Download complete ", success ? "Success" : "Failed"); - m_Book.DownloadComplete (success); - } + void AddressBookSubscription::Request () + { + // must be run in separate thread + LogPrint (eLogInfo, "Downloading hosts from ", m_Link, " ETag: ", m_Etag, " Last-Modified: ", m_LastModified); + bool success = false; + i2p::util::http::url u (m_Link); + i2p::data::IdentHash ident; + if (m_Book.GetIdentHash (u.host_, ident)) + { + std::condition_variable newDataReceived; + std::mutex newDataReceivedMutex; + auto leaseSet = i2p::client::context.GetSharedLocalDestination ()->FindLeaseSet (ident); + if (!leaseSet) + { + std::unique_lock l(newDataReceivedMutex); + i2p::client::context.GetSharedLocalDestination ()->RequestDestination (ident, + [&newDataReceived, &leaseSet](std::shared_ptr ls) + { + leaseSet = ls; + newDataReceived.notify_all (); + }); + if (newDataReceived.wait_for (l, std::chrono::seconds (SUBSCRIPTION_REQUEST_TIMEOUT)) == std::cv_status::timeout) + LogPrint (eLogError, "Subscription LeseseSet request timeout expired"); + } + if (leaseSet) + { + std::stringstream request, response; + // standard header + request << "GET " << u.path_ << " HTTP/1.1\r\nHost: " << u.host_ + << "\r\nAccept: */*\r\n" << "User-Agent: Wget/1.11.4\r\n" << "Connection: close\r\n"; + if (m_Etag.length () > 0) // etag + request << i2p::util::http::IF_NONE_MATCH << ": \"" << m_Etag << "\"\r\n"; + if (m_LastModified.length () > 0) // if-modfief-since + request << i2p::util::http::IF_MODIFIED_SINCE << ": " << m_LastModified << "\r\n"; + request << "\r\n"; // end of header + auto stream = i2p::client::context.GetSharedLocalDestination ()->CreateStream (leaseSet, u.port_); + stream->Send ((uint8_t *)request.str ().c_str (), request.str ().length ()); + + uint8_t buf[4096]; + bool end = false; + while (!end) + { + stream->AsyncReceive (boost::asio::buffer (buf, 4096), + [&](const boost::system::error_code& ecode, std::size_t bytes_transferred) + { + if (bytes_transferred) + response.write ((char *)buf, bytes_transferred); + if (ecode == boost::asio::error::timed_out || !stream->IsOpen ()) + end = true; + newDataReceived.notify_all (); + }, + 30); // wait for 30 seconds + std::unique_lock l(newDataReceivedMutex); + if (newDataReceived.wait_for (l, std::chrono::seconds (SUBSCRIPTION_REQUEST_TIMEOUT)) == std::cv_status::timeout) + LogPrint (eLogError, "Subscription timeout expired"); + } + // 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; + 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); + auto colon = header.find (':'); + if (colon != std::string::npos) + { + std::string field = header.substr (0, 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"); + } + } + LogPrint (eLogInfo, m_Link, " ETag: ", m_Etag, " Last-Modified: ", m_LastModified); + if (!response.eof ()) + { + success = true; + if (!isChunked) + m_Book.LoadHostsFromStream (response); + else + { + // merge chunks + std::stringstream merged; + i2p::util::http::MergeChunkedResponse (response, merged); + m_Book.LoadHostsFromStream (merged); + } + } + } + else if (status == 304) + { + success = true; + LogPrint (eLogInfo, "No updates from ", m_Link); + } + else + LogPrint (eLogWarning, "Adressbook HTTP response ", status); + } + else + LogPrint (eLogError, "Address ", u.host_, " not found"); + } + else + LogPrint (eLogError, "Can't resolve ", u.host_); + LogPrint (eLogInfo, "Download complete ", success ? "Success" : "Failed"); + m_Book.DownloadComplete (success); + } } } diff --git a/AddressBook.h b/AddressBook.h index 6fdae9b1..0de1bbfd 100644 --- a/AddressBook.h +++ b/AddressBook.h @@ -17,86 +17,86 @@ namespace i2p { namespace client { - const char DEFAULT_SUBSCRIPTION_ADDRESS[] = "http://udhdrtrcetjm5sxzskjyr5ztpeszydbh4dpl3pl4utgqqw2v4jna.b32.i2p/hosts.txt"; - const int INITIAL_SUBSCRIPTION_UPDATE_TIMEOUT = 3; // in minutes - const int INITIAL_SUBSCRIPTION_RETRY_TIMEOUT = 1; // in minutes - const int CONTINIOUS_SUBSCRIPTION_UPDATE_TIMEOUT = 720; // in minutes (12 hours) - const int CONTINIOUS_SUBSCRIPTION_RETRY_TIMEOUT = 5; // in minutes - const int SUBSCRIPTION_REQUEST_TIMEOUT = 60; //in second - - inline std::string GetB32Address(const i2p::data::IdentHash& ident) { return ident.ToBase32().append(".b32.i2p"); } + const char DEFAULT_SUBSCRIPTION_ADDRESS[] = "http://udhdrtrcetjm5sxzskjyr5ztpeszydbh4dpl3pl4utgqqw2v4jna.b32.i2p/hosts.txt"; + const int INITIAL_SUBSCRIPTION_UPDATE_TIMEOUT = 3; // in minutes + const int INITIAL_SUBSCRIPTION_RETRY_TIMEOUT = 1; // in minutes + const int CONTINIOUS_SUBSCRIPTION_UPDATE_TIMEOUT = 720; // in minutes (12 hours) + const int CONTINIOUS_SUBSCRIPTION_RETRY_TIMEOUT = 5; // in minutes + const int SUBSCRIPTION_REQUEST_TIMEOUT = 60; //in second + + inline std::string GetB32Address(const i2p::data::IdentHash& ident) { return ident.ToBase32().append(".b32.i2p"); } - class AddressBookStorage // interface for storage - { - public: + class AddressBookStorage // interface for storage + { + public: - virtual ~AddressBookStorage () {}; - virtual bool GetAddress (const i2p::data::IdentHash& ident, i2p::data::IdentityEx& address) const = 0; - virtual void AddAddress (const i2p::data::IdentityEx& address) = 0; - virtual void RemoveAddress (const i2p::data::IdentHash& ident) = 0; - - virtual int Load (std::map& addresses) = 0; - virtual int Save (const std::map& addresses) = 0; - }; + virtual ~AddressBookStorage () {}; + virtual bool GetAddress (const i2p::data::IdentHash& ident, i2p::data::IdentityEx& address) const = 0; + virtual void AddAddress (const i2p::data::IdentityEx& address) = 0; + virtual void RemoveAddress (const i2p::data::IdentHash& ident) = 0; + + virtual int Load (std::map& addresses) = 0; + virtual int Save (const std::map& addresses) = 0; + }; - class AddressBookSubscription; - class AddressBook - { - public: + class AddressBookSubscription; + class AddressBook + { + public: - AddressBook (); - ~AddressBook (); - void Start (); - void Stop (); - bool GetIdentHash (const std::string& address, i2p::data::IdentHash& ident); - bool GetAddress (const std::string& address, i2p::data::IdentityEx& identity); - const i2p::data::IdentHash * FindAddress (const std::string& address); - void InsertAddress (const std::string& address, const std::string& base64); // for jump service - void InsertAddress (const i2p::data::IdentityEx& address); + AddressBook (); + ~AddressBook (); + void Start (); + void Stop (); + bool GetIdentHash (const std::string& address, i2p::data::IdentHash& ident); + bool GetAddress (const std::string& address, i2p::data::IdentityEx& identity); + const i2p::data::IdentHash * FindAddress (const std::string& address); + void InsertAddress (const std::string& address, const std::string& base64); // for jump service + void InsertAddress (const i2p::data::IdentityEx& address); - void LoadHostsFromStream (std::istream& f); - void DownloadComplete (bool success); - //This method returns the ".b32.i2p" address - std::string ToAddress(const i2p::data::IdentHash& ident) { return GetB32Address(ident); } - std::string ToAddress(const i2p::data::IdentityEx& ident) { return ToAddress(ident.GetIdentHash ()); } - private: + void LoadHostsFromStream (std::istream& f); + void DownloadComplete (bool success); + //This method returns the ".b32.i2p" address + std::string ToAddress(const i2p::data::IdentHash& ident) { return GetB32Address(ident); } + std::string ToAddress(const i2p::data::IdentityEx& ident) { return ToAddress(ident.GetIdentHash ()); } + private: - void StartSubscriptions (); - void StopSubscriptions (); - - AddressBookStorage * CreateStorage (); - void LoadHosts (); - void LoadSubscriptions (); + void StartSubscriptions (); + void StopSubscriptions (); + + AddressBookStorage * CreateStorage (); + void LoadHosts (); + void LoadSubscriptions (); - void HandleSubscriptionsUpdateTimer (const boost::system::error_code& ecode); + void HandleSubscriptionsUpdateTimer (const boost::system::error_code& ecode); - private: + private: - std::mutex m_AddressBookMutex; - std::map m_Addresses; - AddressBookStorage * m_Storage; - volatile bool m_IsLoaded, m_IsDownloading; - std::vector m_Subscriptions; - AddressBookSubscription * m_DefaultSubscription; // in case if we don't know any addresses yet - boost::asio::deadline_timer * m_SubscriptionsUpdateTimer; - }; + std::mutex m_AddressBookMutex; + std::map m_Addresses; + AddressBookStorage * m_Storage; + volatile bool m_IsLoaded, m_IsDownloading; + std::vector m_Subscriptions; + AddressBookSubscription * m_DefaultSubscription; // in case if we don't know any addresses yet + boost::asio::deadline_timer * m_SubscriptionsUpdateTimer; + }; - class AddressBookSubscription - { - public: + class AddressBookSubscription + { + public: - AddressBookSubscription (AddressBook& book, const std::string& link); - void CheckSubscription (); + AddressBookSubscription (AddressBook& book, const std::string& link); + void CheckSubscription (); - private: + private: - void Request (); - - private: + void Request (); + + private: - AddressBook& m_Book; - std::string m_Link, m_Etag, m_LastModified; - }; + AddressBook& m_Book; + std::string m_Link, m_Etag, m_LastModified; + }; } } diff --git a/BOB.cpp b/BOB.cpp index f5f11d39..724933d9 100644 --- a/BOB.cpp +++ b/BOB.cpp @@ -8,636 +8,636 @@ namespace i2p { namespace client { - BOBI2PInboundTunnel::BOBI2PInboundTunnel (int port, std::shared_ptr localDestination): - BOBI2PTunnel (localDestination), - m_Acceptor (localDestination->GetService (), boost::asio::ip::tcp::endpoint (boost::asio::ip::tcp::v4(), port)) - { - } + BOBI2PInboundTunnel::BOBI2PInboundTunnel (int port, std::shared_ptr localDestination): + BOBI2PTunnel (localDestination), + m_Acceptor (localDestination->GetService (), boost::asio::ip::tcp::endpoint (boost::asio::ip::tcp::v4(), port)) + { + } - BOBI2PInboundTunnel::~BOBI2PInboundTunnel () - { - Stop (); - } + BOBI2PInboundTunnel::~BOBI2PInboundTunnel () + { + Stop (); + } - void BOBI2PInboundTunnel::Start () - { - m_Acceptor.listen (); - Accept (); - } + void BOBI2PInboundTunnel::Start () + { + m_Acceptor.listen (); + Accept (); + } - void BOBI2PInboundTunnel::Stop () - { - m_Acceptor.close(); - ClearHandlers (); - } + void BOBI2PInboundTunnel::Stop () + { + m_Acceptor.close(); + ClearHandlers (); + } - void BOBI2PInboundTunnel::Accept () - { - auto receiver = std::make_shared (); - receiver->socket = std::make_shared (GetService ()); - m_Acceptor.async_accept (*receiver->socket, std::bind (&BOBI2PInboundTunnel::HandleAccept, this, - std::placeholders::_1, receiver)); - } + void BOBI2PInboundTunnel::Accept () + { + auto receiver = std::make_shared (); + receiver->socket = std::make_shared (GetService ()); + m_Acceptor.async_accept (*receiver->socket, std::bind (&BOBI2PInboundTunnel::HandleAccept, this, + std::placeholders::_1, receiver)); + } - void BOBI2PInboundTunnel::HandleAccept (const boost::system::error_code& ecode, std::shared_ptr receiver) - { - if (!ecode) - { - Accept (); - ReceiveAddress (receiver); - } - } - - void BOBI2PInboundTunnel::ReceiveAddress (std::shared_ptr receiver) - { - receiver->socket->async_read_some (boost::asio::buffer( - receiver->buffer + receiver->bufferOffset, - BOB_COMMAND_BUFFER_SIZE - receiver->bufferOffset), - std::bind(&BOBI2PInboundTunnel::HandleReceivedAddress, this, - std::placeholders::_1, std::placeholders::_2, receiver)); - } - - void BOBI2PInboundTunnel::HandleReceivedAddress (const boost::system::error_code& ecode, std::size_t bytes_transferred, - std::shared_ptr receiver) - { - if (ecode) - LogPrint ("BOB inbound tunnel read error: ", ecode.message ()); - else - { - receiver->bufferOffset += bytes_transferred; - receiver->buffer[receiver->bufferOffset] = 0; - char * eol = strchr (receiver->buffer, '\n'); - if (eol) - { - *eol = 0; - - receiver->data = (uint8_t *)eol + 1; - receiver->dataLen = receiver->bufferOffset - (eol - receiver->buffer + 1); - i2p::data::IdentHash ident; - if (!context.GetAddressBook ().GetIdentHash (receiver->buffer, ident)) - { - LogPrint (eLogError, "BOB address ", receiver->buffer, " not found"); - return; - } - auto leaseSet = GetLocalDestination ()->FindLeaseSet (ident); - if (leaseSet) - CreateConnection (receiver, leaseSet); - else - GetLocalDestination ()->RequestDestination (ident, - std::bind (&BOBI2PInboundTunnel::HandleDestinationRequestComplete, - this, std::placeholders::_1, receiver)); - } - else - { - if (receiver->bufferOffset < BOB_COMMAND_BUFFER_SIZE) - ReceiveAddress (receiver); - else - LogPrint ("BOB missing inbound address "); - } - } - } - - void BOBI2PInboundTunnel::HandleDestinationRequestComplete (std::shared_ptr leaseSet, std::shared_ptr receiver) - { - if (leaseSet) - CreateConnection (receiver, leaseSet); - else - LogPrint ("LeaseSet for BOB inbound destination not found"); - } - - void BOBI2PInboundTunnel::CreateConnection (std::shared_ptr receiver, std::shared_ptr leaseSet) - { - LogPrint ("New BOB inbound connection"); - auto connection = std::make_shared(this, receiver->socket, leaseSet); - AddHandler (connection); - connection->I2PConnect (receiver->data, receiver->dataLen); - } - - BOBI2POutboundTunnel::BOBI2POutboundTunnel (const std::string& address, int port, - std::shared_ptr localDestination, bool quiet): BOBI2PTunnel (localDestination), - m_Endpoint (boost::asio::ip::address::from_string (address), port), m_IsQuiet (quiet) - { - } - - void BOBI2POutboundTunnel::Start () - { - Accept (); - } - - void BOBI2POutboundTunnel::Stop () - { - ClearHandlers (); - } - - void BOBI2POutboundTunnel::Accept () - { - auto localDestination = GetLocalDestination (); - if (localDestination) - localDestination->AcceptStreams (std::bind (&BOBI2POutboundTunnel::HandleAccept, this, std::placeholders::_1)); - else - LogPrint ("Local destination not set for server tunnel"); - } - - void BOBI2POutboundTunnel::HandleAccept (std::shared_ptr stream) - { - if (stream) - { - auto conn = std::make_shared (this, stream, std::make_shared (GetService ()), m_Endpoint, m_IsQuiet); - AddHandler (conn); - conn->Connect (); - } - } - - BOBDestination::BOBDestination (std::shared_ptr localDestination): - m_LocalDestination (localDestination), - m_OutboundTunnel (nullptr), m_InboundTunnel (nullptr) - { - } - - BOBDestination::~BOBDestination () - { - delete m_OutboundTunnel; - delete m_InboundTunnel; - i2p::client::context.DeleteLocalDestination (m_LocalDestination); - } - - void BOBDestination::Start () - { - if (m_OutboundTunnel) m_OutboundTunnel->Start (); - if (m_InboundTunnel) m_InboundTunnel->Start (); - } - - void BOBDestination::Stop () - { - StopTunnels (); - m_LocalDestination->Stop (); - } - - void BOBDestination::StopTunnels () - { - if (m_OutboundTunnel) - { - m_OutboundTunnel->Stop (); - delete m_OutboundTunnel; - m_OutboundTunnel = nullptr; - } - if (m_InboundTunnel) - { - m_InboundTunnel->Stop (); - delete m_InboundTunnel; - m_InboundTunnel = nullptr; - } - } - - void BOBDestination::CreateInboundTunnel (int port) - { - if (!m_InboundTunnel) - m_InboundTunnel = new BOBI2PInboundTunnel (port, m_LocalDestination); - } - - void BOBDestination::CreateOutboundTunnel (const std::string& address, int port, bool quiet) - { - if (!m_OutboundTunnel) - m_OutboundTunnel = new BOBI2POutboundTunnel (address, port, m_LocalDestination, quiet); - } - - BOBCommandSession::BOBCommandSession (BOBCommandChannel& owner): - m_Owner (owner), m_Socket (m_Owner.GetService ()), m_ReceiveBufferOffset (0), - m_IsOpen (true), m_IsQuiet (false), m_InPort (0), m_OutPort (0), - m_CurrentDestination (nullptr) - { - } - - BOBCommandSession::~BOBCommandSession () - { - } - - void BOBCommandSession::Terminate () - { - m_Socket.close (); - m_IsOpen = false; - } - - void BOBCommandSession::Receive () - { - m_Socket.async_read_some (boost::asio::buffer(m_ReceiveBuffer + m_ReceiveBufferOffset, BOB_COMMAND_BUFFER_SIZE - m_ReceiveBufferOffset), - std::bind(&BOBCommandSession::HandleReceived, shared_from_this (), - std::placeholders::_1, std::placeholders::_2)); - } - - void BOBCommandSession::HandleReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred) - { - if (ecode) - { - LogPrint ("BOB command channel read error: ", ecode.message ()); - if (ecode != boost::asio::error::operation_aborted) - Terminate (); - } - else - { - size_t size = m_ReceiveBufferOffset + bytes_transferred; - m_ReceiveBuffer[size] = 0; - char * eol = strchr (m_ReceiveBuffer, '\n'); - if (eol) - { - *eol = 0; - char * operand = strchr (m_ReceiveBuffer, ' '); - if (operand) - { - *operand = 0; - operand++; - } - else - operand = eol; - // process command - auto& handlers = m_Owner.GetCommandHandlers (); - auto it = handlers.find (m_ReceiveBuffer); - if (it != handlers.end ()) - (this->*(it->second))(operand, eol - operand); - else - { - LogPrint (eLogError, "BOB unknown command ", m_ReceiveBuffer); - SendReplyError ("unknown command"); - } - - m_ReceiveBufferOffset = size - (eol - m_ReceiveBuffer) - 1; - memmove (m_ReceiveBuffer, eol + 1, m_ReceiveBufferOffset); - } - else - { - if (size < BOB_COMMAND_BUFFER_SIZE) - m_ReceiveBufferOffset = size; - else - { - LogPrint (eLogError, "Malformed input of the BOB command channel"); - Terminate (); - } - } - } - } - - void BOBCommandSession::Send (size_t len) - { - boost::asio::async_write (m_Socket, boost::asio::buffer (m_SendBuffer, len), - boost::asio::transfer_all (), - std::bind(&BOBCommandSession::HandleSent, shared_from_this (), - std::placeholders::_1, std::placeholders::_2)); - } - - void BOBCommandSession::HandleSent (const boost::system::error_code& ecode, std::size_t bytes_transferred) - { - if (ecode) + void BOBI2PInboundTunnel::HandleAccept (const boost::system::error_code& ecode, std::shared_ptr receiver) + { + if (!ecode) { - LogPrint ("BOB command channel send error: ", ecode.message ()); - if (ecode != boost::asio::error::operation_aborted) - Terminate (); - } - else - { - if (m_IsOpen) - Receive (); - else - Terminate (); - } - } + Accept (); + ReceiveAddress (receiver); + } + } - void BOBCommandSession::SendReplyOK (const char * msg) - { + void BOBI2PInboundTunnel::ReceiveAddress (std::shared_ptr receiver) + { + receiver->socket->async_read_some (boost::asio::buffer( + receiver->buffer + receiver->bufferOffset, + BOB_COMMAND_BUFFER_SIZE - receiver->bufferOffset), + std::bind(&BOBI2PInboundTunnel::HandleReceivedAddress, this, + std::placeholders::_1, std::placeholders::_2, receiver)); + } + + void BOBI2PInboundTunnel::HandleReceivedAddress (const boost::system::error_code& ecode, std::size_t bytes_transferred, + std::shared_ptr receiver) + { + if (ecode) + LogPrint ("BOB inbound tunnel read error: ", ecode.message ()); + else + { + receiver->bufferOffset += bytes_transferred; + receiver->buffer[receiver->bufferOffset] = 0; + char * eol = strchr (receiver->buffer, '\n'); + if (eol) + { + *eol = 0; + + receiver->data = (uint8_t *)eol + 1; + receiver->dataLen = receiver->bufferOffset - (eol - receiver->buffer + 1); + i2p::data::IdentHash ident; + if (!context.GetAddressBook ().GetIdentHash (receiver->buffer, ident)) + { + LogPrint (eLogError, "BOB address ", receiver->buffer, " not found"); + return; + } + auto leaseSet = GetLocalDestination ()->FindLeaseSet (ident); + if (leaseSet) + CreateConnection (receiver, leaseSet); + else + GetLocalDestination ()->RequestDestination (ident, + std::bind (&BOBI2PInboundTunnel::HandleDestinationRequestComplete, + this, std::placeholders::_1, receiver)); + } + else + { + if (receiver->bufferOffset < BOB_COMMAND_BUFFER_SIZE) + ReceiveAddress (receiver); + else + LogPrint ("BOB missing inbound address "); + } + } + } + + void BOBI2PInboundTunnel::HandleDestinationRequestComplete (std::shared_ptr leaseSet, std::shared_ptr receiver) + { + if (leaseSet) + CreateConnection (receiver, leaseSet); + else + LogPrint ("LeaseSet for BOB inbound destination not found"); + } + + void BOBI2PInboundTunnel::CreateConnection (std::shared_ptr receiver, std::shared_ptr leaseSet) + { + LogPrint ("New BOB inbound connection"); + auto connection = std::make_shared(this, receiver->socket, leaseSet); + AddHandler (connection); + connection->I2PConnect (receiver->data, receiver->dataLen); + } + + BOBI2POutboundTunnel::BOBI2POutboundTunnel (const std::string& address, int port, + std::shared_ptr localDestination, bool quiet): BOBI2PTunnel (localDestination), + m_Endpoint (boost::asio::ip::address::from_string (address), port), m_IsQuiet (quiet) + { + } + + void BOBI2POutboundTunnel::Start () + { + Accept (); + } + + void BOBI2POutboundTunnel::Stop () + { + ClearHandlers (); + } + + void BOBI2POutboundTunnel::Accept () + { + auto localDestination = GetLocalDestination (); + if (localDestination) + localDestination->AcceptStreams (std::bind (&BOBI2POutboundTunnel::HandleAccept, this, std::placeholders::_1)); + else + LogPrint ("Local destination not set for server tunnel"); + } + + void BOBI2POutboundTunnel::HandleAccept (std::shared_ptr stream) + { + if (stream) + { + auto conn = std::make_shared (this, stream, std::make_shared (GetService ()), m_Endpoint, m_IsQuiet); + AddHandler (conn); + conn->Connect (); + } + } + + BOBDestination::BOBDestination (std::shared_ptr localDestination): + m_LocalDestination (localDestination), + m_OutboundTunnel (nullptr), m_InboundTunnel (nullptr) + { + } + + BOBDestination::~BOBDestination () + { + delete m_OutboundTunnel; + delete m_InboundTunnel; + i2p::client::context.DeleteLocalDestination (m_LocalDestination); + } + + void BOBDestination::Start () + { + if (m_OutboundTunnel) m_OutboundTunnel->Start (); + if (m_InboundTunnel) m_InboundTunnel->Start (); + } + + void BOBDestination::Stop () + { + StopTunnels (); + m_LocalDestination->Stop (); + } + + void BOBDestination::StopTunnels () + { + if (m_OutboundTunnel) + { + m_OutboundTunnel->Stop (); + delete m_OutboundTunnel; + m_OutboundTunnel = nullptr; + } + if (m_InboundTunnel) + { + m_InboundTunnel->Stop (); + delete m_InboundTunnel; + m_InboundTunnel = nullptr; + } + } + + void BOBDestination::CreateInboundTunnel (int port) + { + if (!m_InboundTunnel) + m_InboundTunnel = new BOBI2PInboundTunnel (port, m_LocalDestination); + } + + void BOBDestination::CreateOutboundTunnel (const std::string& address, int port, bool quiet) + { + if (!m_OutboundTunnel) + m_OutboundTunnel = new BOBI2POutboundTunnel (address, port, m_LocalDestination, quiet); + } + + BOBCommandSession::BOBCommandSession (BOBCommandChannel& owner): + m_Owner (owner), m_Socket (m_Owner.GetService ()), m_ReceiveBufferOffset (0), + m_IsOpen (true), m_IsQuiet (false), m_InPort (0), m_OutPort (0), + m_CurrentDestination (nullptr) + { + } + + BOBCommandSession::~BOBCommandSession () + { + } + + void BOBCommandSession::Terminate () + { + m_Socket.close (); + m_IsOpen = false; + } + + void BOBCommandSession::Receive () + { + m_Socket.async_read_some (boost::asio::buffer(m_ReceiveBuffer + m_ReceiveBufferOffset, BOB_COMMAND_BUFFER_SIZE - m_ReceiveBufferOffset), + std::bind(&BOBCommandSession::HandleReceived, shared_from_this (), + std::placeholders::_1, std::placeholders::_2)); + } + + void BOBCommandSession::HandleReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred) + { + if (ecode) + { + LogPrint ("BOB command channel read error: ", ecode.message ()); + if (ecode != boost::asio::error::operation_aborted) + Terminate (); + } + else + { + size_t size = m_ReceiveBufferOffset + bytes_transferred; + m_ReceiveBuffer[size] = 0; + char * eol = strchr (m_ReceiveBuffer, '\n'); + if (eol) + { + *eol = 0; + char * operand = strchr (m_ReceiveBuffer, ' '); + if (operand) + { + *operand = 0; + operand++; + } + else + operand = eol; + // process command + auto& handlers = m_Owner.GetCommandHandlers (); + auto it = handlers.find (m_ReceiveBuffer); + if (it != handlers.end ()) + (this->*(it->second))(operand, eol - operand); + else + { + LogPrint (eLogError, "BOB unknown command ", m_ReceiveBuffer); + SendReplyError ("unknown command"); + } + + m_ReceiveBufferOffset = size - (eol - m_ReceiveBuffer) - 1; + memmove (m_ReceiveBuffer, eol + 1, m_ReceiveBufferOffset); + } + else + { + if (size < BOB_COMMAND_BUFFER_SIZE) + m_ReceiveBufferOffset = size; + else + { + LogPrint (eLogError, "Malformed input of the BOB command channel"); + Terminate (); + } + } + } + } + + void BOBCommandSession::Send (size_t len) + { + boost::asio::async_write (m_Socket, boost::asio::buffer (m_SendBuffer, len), + boost::asio::transfer_all (), + std::bind(&BOBCommandSession::HandleSent, shared_from_this (), + std::placeholders::_1, std::placeholders::_2)); + } + + void BOBCommandSession::HandleSent (const boost::system::error_code& ecode, std::size_t bytes_transferred) + { + if (ecode) + { + LogPrint ("BOB command channel send error: ", ecode.message ()); + if (ecode != boost::asio::error::operation_aborted) + Terminate (); + } + else + { + if (m_IsOpen) + Receive (); + else + Terminate (); + } + } + + void BOBCommandSession::SendReplyOK (const char * msg) + { #ifdef _MSC_VER - size_t len = sprintf_s (m_SendBuffer, BOB_COMMAND_BUFFER_SIZE, BOB_REPLY_OK, msg); -#else - size_t len = snprintf (m_SendBuffer, BOB_COMMAND_BUFFER_SIZE, BOB_REPLY_OK, msg); + size_t len = sprintf_s (m_SendBuffer, BOB_COMMAND_BUFFER_SIZE, BOB_REPLY_OK, msg); +#else + size_t len = snprintf (m_SendBuffer, BOB_COMMAND_BUFFER_SIZE, BOB_REPLY_OK, msg); #endif - Send (len); - } + Send (len); + } - void BOBCommandSession::SendReplyError (const char * msg) - { + void BOBCommandSession::SendReplyError (const char * msg) + { #ifdef _MSC_VER - size_t len = sprintf_s (m_SendBuffer, BOB_COMMAND_BUFFER_SIZE, BOB_REPLY_ERROR, msg); -#else - size_t len = snprintf (m_SendBuffer, BOB_COMMAND_BUFFER_SIZE, BOB_REPLY_ERROR, msg); + size_t len = sprintf_s (m_SendBuffer, BOB_COMMAND_BUFFER_SIZE, BOB_REPLY_ERROR, msg); +#else + size_t len = snprintf (m_SendBuffer, BOB_COMMAND_BUFFER_SIZE, BOB_REPLY_ERROR, msg); #endif - Send (len); - } - - void BOBCommandSession::SendVersion () - { - size_t len = strlen (BOB_VERSION); - memcpy (m_SendBuffer, BOB_VERSION, len); - Send (len); - } + Send (len); + } + + void BOBCommandSession::SendVersion () + { + size_t len = strlen (BOB_VERSION); + memcpy (m_SendBuffer, BOB_VERSION, len); + Send (len); + } - void BOBCommandSession::SendData (const char * nickname) - { + void BOBCommandSession::SendData (const char * nickname) + { #ifdef _MSC_VER - size_t len = sprintf_s (m_SendBuffer, BOB_COMMAND_BUFFER_SIZE, BOB_DATA, nickname); -#else - size_t len = snprintf (m_SendBuffer, BOB_COMMAND_BUFFER_SIZE, BOB_DATA, nickname); + size_t len = sprintf_s (m_SendBuffer, BOB_COMMAND_BUFFER_SIZE, BOB_DATA, nickname); +#else + size_t len = snprintf (m_SendBuffer, BOB_COMMAND_BUFFER_SIZE, BOB_DATA, nickname); #endif - Send (len); - } - - void BOBCommandSession::ZapCommandHandler (const char * operand, size_t len) - { - LogPrint (eLogDebug, "BOB: zap"); - Terminate (); - } + Send (len); + } + + void BOBCommandSession::ZapCommandHandler (const char * operand, size_t len) + { + LogPrint (eLogDebug, "BOB: zap"); + Terminate (); + } - void BOBCommandSession::QuitCommandHandler (const char * operand, size_t len) - { - LogPrint (eLogDebug, "BOB: quit"); - m_IsOpen = false; - SendReplyOK ("Bye!"); - } + void BOBCommandSession::QuitCommandHandler (const char * operand, size_t len) + { + LogPrint (eLogDebug, "BOB: quit"); + m_IsOpen = false; + SendReplyOK ("Bye!"); + } - void BOBCommandSession::StartCommandHandler (const char * operand, size_t len) - { - LogPrint (eLogDebug, "BOB: start ", m_Nickname); - if (!m_CurrentDestination) - { - m_CurrentDestination = new BOBDestination (i2p::client::context.CreateNewLocalDestination (m_Keys, true, &m_Options)); - m_Owner.AddDestination (m_Nickname, m_CurrentDestination); - } - if (m_InPort) - m_CurrentDestination->CreateInboundTunnel (m_InPort); - if (m_OutPort && !m_Address.empty ()) - m_CurrentDestination->CreateOutboundTunnel (m_Address, m_OutPort, m_IsQuiet); - m_CurrentDestination->Start (); - SendReplyOK ("tunnel starting"); - } - - void BOBCommandSession::StopCommandHandler (const char * operand, size_t len) - { - auto dest = m_Owner.FindDestination (m_Nickname); - if (dest) - { - dest->StopTunnels (); - SendReplyOK ("tunnel stopping"); - } - else - SendReplyError ("tunnel not found"); - } - - void BOBCommandSession::SetNickCommandHandler (const char * operand, size_t len) - { - LogPrint (eLogDebug, "BOB: setnick ", operand); - m_Nickname = operand; - std::string msg ("Nickname set to "); - msg += operand; - SendReplyOK (msg.c_str ()); - } + void BOBCommandSession::StartCommandHandler (const char * operand, size_t len) + { + LogPrint (eLogDebug, "BOB: start ", m_Nickname); + if (!m_CurrentDestination) + { + m_CurrentDestination = new BOBDestination (i2p::client::context.CreateNewLocalDestination (m_Keys, true, &m_Options)); + m_Owner.AddDestination (m_Nickname, m_CurrentDestination); + } + if (m_InPort) + m_CurrentDestination->CreateInboundTunnel (m_InPort); + if (m_OutPort && !m_Address.empty ()) + m_CurrentDestination->CreateOutboundTunnel (m_Address, m_OutPort, m_IsQuiet); + m_CurrentDestination->Start (); + SendReplyOK ("tunnel starting"); + } + + void BOBCommandSession::StopCommandHandler (const char * operand, size_t len) + { + auto dest = m_Owner.FindDestination (m_Nickname); + if (dest) + { + dest->StopTunnels (); + SendReplyOK ("tunnel stopping"); + } + else + SendReplyError ("tunnel not found"); + } + + void BOBCommandSession::SetNickCommandHandler (const char * operand, size_t len) + { + LogPrint (eLogDebug, "BOB: setnick ", operand); + m_Nickname = operand; + std::string msg ("Nickname set to "); + msg += operand; + SendReplyOK (msg.c_str ()); + } - void BOBCommandSession::GetNickCommandHandler (const char * operand, size_t len) - { - LogPrint (eLogDebug, "BOB: getnick ", operand); - m_CurrentDestination = m_Owner.FindDestination (operand); - if (m_CurrentDestination) - { - m_Keys = m_CurrentDestination->GetKeys (); - m_Nickname = operand; - std::string msg ("Nickname set to "); - msg += operand; - SendReplyOK (msg.c_str ()); - } - else - SendReplyError ("tunnel not found"); - } + void BOBCommandSession::GetNickCommandHandler (const char * operand, size_t len) + { + LogPrint (eLogDebug, "BOB: getnick ", operand); + m_CurrentDestination = m_Owner.FindDestination (operand); + if (m_CurrentDestination) + { + m_Keys = m_CurrentDestination->GetKeys (); + m_Nickname = operand; + std::string msg ("Nickname set to "); + msg += operand; + SendReplyOK (msg.c_str ()); + } + else + SendReplyError ("tunnel not found"); + } - void BOBCommandSession::NewkeysCommandHandler (const char * operand, size_t len) - { - LogPrint (eLogDebug, "BOB: newkeys"); - m_Keys = i2p::data::PrivateKeys::CreateRandomKeys (); - SendReplyOK (m_Keys.GetPublic ().ToBase64 ().c_str ()); - } + void BOBCommandSession::NewkeysCommandHandler (const char * operand, size_t len) + { + LogPrint (eLogDebug, "BOB: newkeys"); + m_Keys = i2p::data::PrivateKeys::CreateRandomKeys (); + SendReplyOK (m_Keys.GetPublic ().ToBase64 ().c_str ()); + } - void BOBCommandSession::SetkeysCommandHandler (const char * operand, size_t len) - { - LogPrint (eLogDebug, "BOB: setkeys ", operand); - m_Keys.FromBase64 (operand); - SendReplyOK (m_Keys.GetPublic ().ToBase64 ().c_str ()); - } - - void BOBCommandSession::GetkeysCommandHandler (const char * operand, size_t len) - { - LogPrint (eLogDebug, "BOB: getkeys"); - SendReplyOK (m_Keys.ToBase64 ().c_str ()); - } + void BOBCommandSession::SetkeysCommandHandler (const char * operand, size_t len) + { + LogPrint (eLogDebug, "BOB: setkeys ", operand); + m_Keys.FromBase64 (operand); + SendReplyOK (m_Keys.GetPublic ().ToBase64 ().c_str ()); + } + + void BOBCommandSession::GetkeysCommandHandler (const char * operand, size_t len) + { + LogPrint (eLogDebug, "BOB: getkeys"); + SendReplyOK (m_Keys.ToBase64 ().c_str ()); + } - void BOBCommandSession::GetdestCommandHandler (const char * operand, size_t len) - { - LogPrint (eLogDebug, "BOB: getdest"); - SendReplyOK (m_Keys.GetPublic ().ToBase64 ().c_str ()); - } - - void BOBCommandSession::OuthostCommandHandler (const char * operand, size_t len) - { - LogPrint (eLogDebug, "BOB: outhost ", operand); - m_Address = operand; - SendReplyOK ("outhost set"); - } - - void BOBCommandSession::OutportCommandHandler (const char * operand, size_t len) - { - LogPrint (eLogDebug, "BOB: outport ", operand); - m_OutPort = boost::lexical_cast(operand); - SendReplyOK ("outbound port set"); - } + void BOBCommandSession::GetdestCommandHandler (const char * operand, size_t len) + { + LogPrint (eLogDebug, "BOB: getdest"); + SendReplyOK (m_Keys.GetPublic ().ToBase64 ().c_str ()); + } + + void BOBCommandSession::OuthostCommandHandler (const char * operand, size_t len) + { + LogPrint (eLogDebug, "BOB: outhost ", operand); + m_Address = operand; + SendReplyOK ("outhost set"); + } + + void BOBCommandSession::OutportCommandHandler (const char * operand, size_t len) + { + LogPrint (eLogDebug, "BOB: outport ", operand); + m_OutPort = boost::lexical_cast(operand); + SendReplyOK ("outbound port set"); + } - void BOBCommandSession::InhostCommandHandler (const char * operand, size_t len) - { - LogPrint (eLogDebug, "BOB: inhost ", operand); - m_Address = operand; - SendReplyOK ("inhost set"); - } - - void BOBCommandSession::InportCommandHandler (const char * operand, size_t len) - { - LogPrint (eLogDebug, "BOB: inport ", operand); - m_InPort = boost::lexical_cast(operand); - SendReplyOK ("inbound port set"); - } + void BOBCommandSession::InhostCommandHandler (const char * operand, size_t len) + { + LogPrint (eLogDebug, "BOB: inhost ", operand); + m_Address = operand; + SendReplyOK ("inhost set"); + } + + void BOBCommandSession::InportCommandHandler (const char * operand, size_t len) + { + LogPrint (eLogDebug, "BOB: inport ", operand); + m_InPort = boost::lexical_cast(operand); + SendReplyOK ("inbound port set"); + } - void BOBCommandSession::QuietCommandHandler (const char * operand, size_t len) - { - LogPrint (eLogDebug, "BOB: quiet"); - m_IsQuiet = true; - SendReplyOK ("quiet"); - } - - void BOBCommandSession::LookupCommandHandler (const char * operand, size_t len) - { - LogPrint (eLogDebug, "BOB: lookup ", operand); - i2p::data::IdentHash ident; - if (!context.GetAddressBook ().GetIdentHash (operand, ident) || !m_CurrentDestination) - { - SendReplyError ("Address Not found"); - return; - } - auto localDestination = m_CurrentDestination->GetLocalDestination (); - auto leaseSet = localDestination->FindLeaseSet (ident); - if (leaseSet) - SendReplyOK (leaseSet->GetIdentity ().ToBase64 ().c_str ()); - else - { - auto s = shared_from_this (); - localDestination->RequestDestination (ident, - [s](std::shared_ptr ls) - { - if (ls) - s->SendReplyOK (ls->GetIdentity ().ToBase64 ().c_str ()); - else - s->SendReplyError ("LeaseSet Not found"); - } - ); - } - } + void BOBCommandSession::QuietCommandHandler (const char * operand, size_t len) + { + LogPrint (eLogDebug, "BOB: quiet"); + m_IsQuiet = true; + SendReplyOK ("quiet"); + } + + void BOBCommandSession::LookupCommandHandler (const char * operand, size_t len) + { + LogPrint (eLogDebug, "BOB: lookup ", operand); + i2p::data::IdentHash ident; + if (!context.GetAddressBook ().GetIdentHash (operand, ident) || !m_CurrentDestination) + { + SendReplyError ("Address Not found"); + return; + } + auto localDestination = m_CurrentDestination->GetLocalDestination (); + auto leaseSet = localDestination->FindLeaseSet (ident); + if (leaseSet) + SendReplyOK (leaseSet->GetIdentity ().ToBase64 ().c_str ()); + else + { + auto s = shared_from_this (); + localDestination->RequestDestination (ident, + [s](std::shared_ptr ls) + { + if (ls) + s->SendReplyOK (ls->GetIdentity ().ToBase64 ().c_str ()); + else + s->SendReplyError ("LeaseSet Not found"); + } + ); + } + } - void BOBCommandSession::ClearCommandHandler (const char * operand, size_t len) - { - LogPrint (eLogDebug, "BOB: clear"); - m_Owner.DeleteDestination (m_Nickname); - SendReplyOK ("cleared"); - } + void BOBCommandSession::ClearCommandHandler (const char * operand, size_t len) + { + LogPrint (eLogDebug, "BOB: clear"); + m_Owner.DeleteDestination (m_Nickname); + SendReplyOK ("cleared"); + } - void BOBCommandSession::ListCommandHandler (const char * operand, size_t len) - { - LogPrint (eLogDebug, "BOB: list"); - auto& destinations = m_Owner.GetDestinations (); - for (auto it: destinations) - SendData (it.first.c_str ()); - SendReplyOK ("Listing done"); - } + void BOBCommandSession::ListCommandHandler (const char * operand, size_t len) + { + LogPrint (eLogDebug, "BOB: list"); + auto& destinations = m_Owner.GetDestinations (); + for (auto it: destinations) + SendData (it.first.c_str ()); + SendReplyOK ("Listing done"); + } - void BOBCommandSession::OptionCommandHandler (const char * operand, size_t len) - { - LogPrint (eLogDebug, "BOB: option ", operand); - const char * value = strchr (operand, '='); - if (value) - { - *(const_cast(value)) = 0; - m_Options[operand] = value + 1; - *(const_cast(value)) = '='; - SendReplyOK ("option"); - } - else - SendReplyError ("malformed"); - } - - BOBCommandChannel::BOBCommandChannel (int port): - m_IsRunning (false), m_Thread (nullptr), - m_Acceptor (m_Service, boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), port)) - { - // command -> handler - m_CommandHandlers[BOB_COMMAND_ZAP] = &BOBCommandSession::ZapCommandHandler; - m_CommandHandlers[BOB_COMMAND_QUIT] = &BOBCommandSession::QuitCommandHandler; - m_CommandHandlers[BOB_COMMAND_START] = &BOBCommandSession::StartCommandHandler; - m_CommandHandlers[BOB_COMMAND_STOP] = &BOBCommandSession::StopCommandHandler; - m_CommandHandlers[BOB_COMMAND_SETNICK] = &BOBCommandSession::SetNickCommandHandler; - m_CommandHandlers[BOB_COMMAND_GETNICK] = &BOBCommandSession::GetNickCommandHandler; - m_CommandHandlers[BOB_COMMAND_NEWKEYS] = &BOBCommandSession::NewkeysCommandHandler; - m_CommandHandlers[BOB_COMMAND_GETKEYS] = &BOBCommandSession::GetkeysCommandHandler; - m_CommandHandlers[BOB_COMMAND_SETKEYS] = &BOBCommandSession::SetkeysCommandHandler; - m_CommandHandlers[BOB_COMMAND_GETDEST] = &BOBCommandSession::GetdestCommandHandler; - m_CommandHandlers[BOB_COMMAND_OUTHOST] = &BOBCommandSession::OuthostCommandHandler; - m_CommandHandlers[BOB_COMMAND_OUTPORT] = &BOBCommandSession::OutportCommandHandler; - m_CommandHandlers[BOB_COMMAND_INHOST] = &BOBCommandSession::InhostCommandHandler; - m_CommandHandlers[BOB_COMMAND_INPORT] = &BOBCommandSession::InportCommandHandler; - m_CommandHandlers[BOB_COMMAND_QUIET] = &BOBCommandSession::QuietCommandHandler; - m_CommandHandlers[BOB_COMMAND_LOOKUP] = &BOBCommandSession::LookupCommandHandler; - m_CommandHandlers[BOB_COMMAND_CLEAR] = &BOBCommandSession::ClearCommandHandler; - m_CommandHandlers[BOB_COMMAND_LIST] = &BOBCommandSession::ListCommandHandler; - m_CommandHandlers[BOB_COMMAND_OPTION] = &BOBCommandSession::OptionCommandHandler; - } + void BOBCommandSession::OptionCommandHandler (const char * operand, size_t len) + { + LogPrint (eLogDebug, "BOB: option ", operand); + const char * value = strchr (operand, '='); + if (value) + { + *(const_cast(value)) = 0; + m_Options[operand] = value + 1; + *(const_cast(value)) = '='; + SendReplyOK ("option"); + } + else + SendReplyError ("malformed"); + } + + BOBCommandChannel::BOBCommandChannel (int port): + m_IsRunning (false), m_Thread (nullptr), + m_Acceptor (m_Service, boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), port)) + { + // command -> handler + m_CommandHandlers[BOB_COMMAND_ZAP] = &BOBCommandSession::ZapCommandHandler; + m_CommandHandlers[BOB_COMMAND_QUIT] = &BOBCommandSession::QuitCommandHandler; + m_CommandHandlers[BOB_COMMAND_START] = &BOBCommandSession::StartCommandHandler; + m_CommandHandlers[BOB_COMMAND_STOP] = &BOBCommandSession::StopCommandHandler; + m_CommandHandlers[BOB_COMMAND_SETNICK] = &BOBCommandSession::SetNickCommandHandler; + m_CommandHandlers[BOB_COMMAND_GETNICK] = &BOBCommandSession::GetNickCommandHandler; + m_CommandHandlers[BOB_COMMAND_NEWKEYS] = &BOBCommandSession::NewkeysCommandHandler; + m_CommandHandlers[BOB_COMMAND_GETKEYS] = &BOBCommandSession::GetkeysCommandHandler; + m_CommandHandlers[BOB_COMMAND_SETKEYS] = &BOBCommandSession::SetkeysCommandHandler; + m_CommandHandlers[BOB_COMMAND_GETDEST] = &BOBCommandSession::GetdestCommandHandler; + m_CommandHandlers[BOB_COMMAND_OUTHOST] = &BOBCommandSession::OuthostCommandHandler; + m_CommandHandlers[BOB_COMMAND_OUTPORT] = &BOBCommandSession::OutportCommandHandler; + m_CommandHandlers[BOB_COMMAND_INHOST] = &BOBCommandSession::InhostCommandHandler; + m_CommandHandlers[BOB_COMMAND_INPORT] = &BOBCommandSession::InportCommandHandler; + m_CommandHandlers[BOB_COMMAND_QUIET] = &BOBCommandSession::QuietCommandHandler; + m_CommandHandlers[BOB_COMMAND_LOOKUP] = &BOBCommandSession::LookupCommandHandler; + m_CommandHandlers[BOB_COMMAND_CLEAR] = &BOBCommandSession::ClearCommandHandler; + m_CommandHandlers[BOB_COMMAND_LIST] = &BOBCommandSession::ListCommandHandler; + m_CommandHandlers[BOB_COMMAND_OPTION] = &BOBCommandSession::OptionCommandHandler; + } - BOBCommandChannel::~BOBCommandChannel () - { - Stop (); - for (auto it: m_Destinations) - delete it.second; - } + BOBCommandChannel::~BOBCommandChannel () + { + Stop (); + for (auto it: m_Destinations) + delete it.second; + } - void BOBCommandChannel::Start () - { - Accept (); - m_IsRunning = true; - m_Thread = new std::thread (std::bind (&BOBCommandChannel::Run, this)); - } + void BOBCommandChannel::Start () + { + Accept (); + m_IsRunning = true; + m_Thread = new std::thread (std::bind (&BOBCommandChannel::Run, this)); + } - void BOBCommandChannel::Stop () - { - m_IsRunning = false; - for (auto it: m_Destinations) - it.second->Stop (); - m_Acceptor.cancel (); - m_Service.stop (); - if (m_Thread) - { - m_Thread->join (); - delete m_Thread; - m_Thread = nullptr; - } - } - - void BOBCommandChannel::Run () - { - while (m_IsRunning) - { - try - { - m_Service.run (); - } - catch (std::exception& ex) - { - LogPrint (eLogError, "BOB: ", ex.what ()); - } - } - } + void BOBCommandChannel::Stop () + { + m_IsRunning = false; + for (auto it: m_Destinations) + it.second->Stop (); + m_Acceptor.cancel (); + m_Service.stop (); + if (m_Thread) + { + m_Thread->join (); + delete m_Thread; + m_Thread = nullptr; + } + } + + void BOBCommandChannel::Run () + { + while (m_IsRunning) + { + try + { + m_Service.run (); + } + catch (std::exception& ex) + { + LogPrint (eLogError, "BOB: ", ex.what ()); + } + } + } - void BOBCommandChannel::AddDestination (const std::string& name, BOBDestination * dest) - { - m_Destinations[name] = dest; - } + void BOBCommandChannel::AddDestination (const std::string& name, BOBDestination * dest) + { + m_Destinations[name] = dest; + } - void BOBCommandChannel::DeleteDestination (const std::string& name) - { - auto it = m_Destinations.find (name); - if (it != m_Destinations.end ()) - { - it->second->Stop (); - delete it->second; - m_Destinations.erase (it); - } - } - - BOBDestination * BOBCommandChannel::FindDestination (const std::string& name) - { - auto it = m_Destinations.find (name); - if (it != m_Destinations.end ()) - return it->second; - return nullptr; - } - - void BOBCommandChannel::Accept () - { - auto newSession = std::make_shared (*this); - m_Acceptor.async_accept (newSession->GetSocket (), std::bind (&BOBCommandChannel::HandleAccept, this, - std::placeholders::_1, newSession)); - } + void BOBCommandChannel::DeleteDestination (const std::string& name) + { + auto it = m_Destinations.find (name); + if (it != m_Destinations.end ()) + { + it->second->Stop (); + delete it->second; + m_Destinations.erase (it); + } + } + + BOBDestination * BOBCommandChannel::FindDestination (const std::string& name) + { + auto it = m_Destinations.find (name); + if (it != m_Destinations.end ()) + return it->second; + return nullptr; + } + + void BOBCommandChannel::Accept () + { + auto newSession = std::make_shared (*this); + m_Acceptor.async_accept (newSession->GetSocket (), std::bind (&BOBCommandChannel::HandleAccept, this, + std::placeholders::_1, newSession)); + } - void BOBCommandChannel::HandleAccept(const boost::system::error_code& ecode, std::shared_ptr session) - { - if (ecode != boost::asio::error::operation_aborted) - Accept (); + void BOBCommandChannel::HandleAccept(const boost::system::error_code& ecode, std::shared_ptr session) + { + if (ecode != boost::asio::error::operation_aborted) + Accept (); - if (!ecode) - { - LogPrint (eLogInfo, "New BOB command connection from ", session->GetSocket ().remote_endpoint ()); - session->SendVersion (); - } - else - LogPrint (eLogError, "BOB accept error: ", ecode.message ()); - } + if (!ecode) + { + LogPrint (eLogInfo, "New BOB command connection from ", session->GetSocket ().remote_endpoint ()); + session->SendVersion (); + } + else + LogPrint (eLogError, "BOB accept error: ", ecode.message ()); + } } } diff --git a/BOB.h b/BOB.h index ddc64707..801571cb 100644 --- a/BOB.h +++ b/BOB.h @@ -16,220 +16,220 @@ namespace i2p { namespace client { - const size_t BOB_COMMAND_BUFFER_SIZE = 1024; - const char BOB_COMMAND_ZAP[] = "zap"; - const char BOB_COMMAND_QUIT[] = "quit"; - const char BOB_COMMAND_START[] = "start"; - const char BOB_COMMAND_STOP[] = "stop"; - const char BOB_COMMAND_SETNICK[] = "setnick"; - const char BOB_COMMAND_GETNICK[] = "getnick"; - const char BOB_COMMAND_NEWKEYS[] = "newkeys"; - const char BOB_COMMAND_GETKEYS[] = "getkeys"; - const char BOB_COMMAND_SETKEYS[] = "setkeys"; - const char BOB_COMMAND_GETDEST[] = "getdest"; - const char BOB_COMMAND_OUTHOST[] = "outhost"; - const char BOB_COMMAND_OUTPORT[] = "outport"; - const char BOB_COMMAND_INHOST[] = "inhost"; - const char BOB_COMMAND_INPORT[] = "inport"; - const char BOB_COMMAND_QUIET[] = "quiet"; - const char BOB_COMMAND_LOOKUP[] = "lookup"; - const char BOB_COMMAND_CLEAR[] = "clear"; - const char BOB_COMMAND_LIST[] = "list"; - const char BOB_COMMAND_OPTION[] = "option"; - - const char BOB_VERSION[] = "BOB 00.00.10\nOK\n"; - const char BOB_REPLY_OK[] = "OK %s\n"; - const char BOB_REPLY_ERROR[] = "ERROR %s\n"; - const char BOB_DATA[] = "NICKNAME %s\n"; + const size_t BOB_COMMAND_BUFFER_SIZE = 1024; + const char BOB_COMMAND_ZAP[] = "zap"; + const char BOB_COMMAND_QUIT[] = "quit"; + const char BOB_COMMAND_START[] = "start"; + const char BOB_COMMAND_STOP[] = "stop"; + const char BOB_COMMAND_SETNICK[] = "setnick"; + const char BOB_COMMAND_GETNICK[] = "getnick"; + const char BOB_COMMAND_NEWKEYS[] = "newkeys"; + const char BOB_COMMAND_GETKEYS[] = "getkeys"; + const char BOB_COMMAND_SETKEYS[] = "setkeys"; + const char BOB_COMMAND_GETDEST[] = "getdest"; + const char BOB_COMMAND_OUTHOST[] = "outhost"; + const char BOB_COMMAND_OUTPORT[] = "outport"; + const char BOB_COMMAND_INHOST[] = "inhost"; + const char BOB_COMMAND_INPORT[] = "inport"; + const char BOB_COMMAND_QUIET[] = "quiet"; + const char BOB_COMMAND_LOOKUP[] = "lookup"; + const char BOB_COMMAND_CLEAR[] = "clear"; + const char BOB_COMMAND_LIST[] = "list"; + const char BOB_COMMAND_OPTION[] = "option"; + + const char BOB_VERSION[] = "BOB 00.00.10\nOK\n"; + const char BOB_REPLY_OK[] = "OK %s\n"; + const char BOB_REPLY_ERROR[] = "ERROR %s\n"; + const char BOB_DATA[] = "NICKNAME %s\n"; - class BOBI2PTunnel: public I2PService - { - public: + class BOBI2PTunnel: public I2PService + { + public: - BOBI2PTunnel (std::shared_ptr localDestination): - I2PService (localDestination) {}; + BOBI2PTunnel (std::shared_ptr localDestination): + I2PService (localDestination) {}; - virtual void Start () {}; - virtual void Stop () {}; - }; - - class BOBI2PInboundTunnel: public BOBI2PTunnel - { - struct AddressReceiver - { - std::shared_ptr socket; - char buffer[BOB_COMMAND_BUFFER_SIZE + 1]; // for destination base64 address - uint8_t * data; // pointer to buffer - size_t dataLen, bufferOffset; + virtual void Start () {}; + virtual void Stop () {}; + }; + + class BOBI2PInboundTunnel: public BOBI2PTunnel + { + struct AddressReceiver + { + std::shared_ptr socket; + char buffer[BOB_COMMAND_BUFFER_SIZE + 1]; // for destination base64 address + uint8_t * data; // pointer to buffer + size_t dataLen, bufferOffset; - AddressReceiver (): data (nullptr), dataLen (0), bufferOffset (0) {}; - }; - - public: + AddressReceiver (): data (nullptr), dataLen (0), bufferOffset (0) {}; + }; + + public: - BOBI2PInboundTunnel (int port, std::shared_ptr localDestination); - ~BOBI2PInboundTunnel (); + BOBI2PInboundTunnel (int port, std::shared_ptr localDestination); + ~BOBI2PInboundTunnel (); - void Start (); - void Stop (); + void Start (); + void Stop (); - private: + private: - void Accept (); - void HandleAccept (const boost::system::error_code& ecode, std::shared_ptr receiver); + void Accept (); + void HandleAccept (const boost::system::error_code& ecode, std::shared_ptr receiver); - void ReceiveAddress (std::shared_ptr receiver); - void HandleReceivedAddress (const boost::system::error_code& ecode, std::size_t bytes_transferred, - std::shared_ptr receiver); + void ReceiveAddress (std::shared_ptr receiver); + void HandleReceivedAddress (const boost::system::error_code& ecode, std::size_t bytes_transferred, + std::shared_ptr receiver); - void HandleDestinationRequestComplete (std::shared_ptr leaseSet, std::shared_ptr receiver); + void HandleDestinationRequestComplete (std::shared_ptr leaseSet, std::shared_ptr receiver); - void CreateConnection (std::shared_ptr receiver, std::shared_ptr leaseSet); + void CreateConnection (std::shared_ptr receiver, std::shared_ptr leaseSet); - private: + private: - boost::asio::ip::tcp::acceptor m_Acceptor; - }; + boost::asio::ip::tcp::acceptor m_Acceptor; + }; - class BOBI2POutboundTunnel: public BOBI2PTunnel - { - public: + class BOBI2POutboundTunnel: public BOBI2PTunnel + { + public: - BOBI2POutboundTunnel (const std::string& address, int port, std::shared_ptr localDestination, bool quiet); + BOBI2POutboundTunnel (const std::string& address, int port, std::shared_ptr localDestination, bool quiet); - void Start (); - void Stop (); + void Start (); + void Stop (); - void SetQuiet () { m_IsQuiet = true; }; + void SetQuiet () { m_IsQuiet = true; }; - private: + private: - void Accept (); - void HandleAccept (std::shared_ptr stream); + void Accept (); + void HandleAccept (std::shared_ptr stream); - private: + private: - boost::asio::ip::tcp::endpoint m_Endpoint; - bool m_IsQuiet; - }; + boost::asio::ip::tcp::endpoint m_Endpoint; + bool m_IsQuiet; + }; - class BOBDestination - { - public: + class BOBDestination + { + public: - BOBDestination (std::shared_ptr localDestination); - ~BOBDestination (); + BOBDestination (std::shared_ptr localDestination); + ~BOBDestination (); - void Start (); - void Stop (); - void StopTunnels (); - void CreateInboundTunnel (int port); - void CreateOutboundTunnel (const std::string& address, int port, bool quiet); - const i2p::data::PrivateKeys& GetKeys () const { return m_LocalDestination->GetPrivateKeys (); }; - std::shared_ptr GetLocalDestination () const { return m_LocalDestination; }; - - private: + void Start (); + void Stop (); + void StopTunnels (); + void CreateInboundTunnel (int port); + void CreateOutboundTunnel (const std::string& address, int port, bool quiet); + const i2p::data::PrivateKeys& GetKeys () const { return m_LocalDestination->GetPrivateKeys (); }; + std::shared_ptr GetLocalDestination () const { return m_LocalDestination; }; + + private: - std::shared_ptr m_LocalDestination; - BOBI2POutboundTunnel * m_OutboundTunnel; - BOBI2PInboundTunnel * m_InboundTunnel; - }; - - class BOBCommandChannel; - class BOBCommandSession: public std::enable_shared_from_this - { - public: + std::shared_ptr m_LocalDestination; + BOBI2POutboundTunnel * m_OutboundTunnel; + BOBI2PInboundTunnel * m_InboundTunnel; + }; + + class BOBCommandChannel; + class BOBCommandSession: public std::enable_shared_from_this + { + public: - BOBCommandSession (BOBCommandChannel& owner); - ~BOBCommandSession (); - void Terminate (); + BOBCommandSession (BOBCommandChannel& owner); + ~BOBCommandSession (); + void Terminate (); - boost::asio::ip::tcp::socket& GetSocket () { return m_Socket; }; - void SendVersion (); + boost::asio::ip::tcp::socket& GetSocket () { return m_Socket; }; + void SendVersion (); - // command handlers - void ZapCommandHandler (const char * operand, size_t len); - void QuitCommandHandler (const char * operand, size_t len); - void StartCommandHandler (const char * operand, size_t len); - void StopCommandHandler (const char * operand, size_t len); - void SetNickCommandHandler (const char * operand, size_t len); - void GetNickCommandHandler (const char * operand, size_t len); - void NewkeysCommandHandler (const char * operand, size_t len); - void SetkeysCommandHandler (const char * operand, size_t len); - void GetkeysCommandHandler (const char * operand, size_t len); - void GetdestCommandHandler (const char * operand, size_t len); - void OuthostCommandHandler (const char * operand, size_t len); - void OutportCommandHandler (const char * operand, size_t len); - void InhostCommandHandler (const char * operand, size_t len); - void InportCommandHandler (const char * operand, size_t len); - void QuietCommandHandler (const char * operand, size_t len); - void LookupCommandHandler (const char * operand, size_t len); - void ClearCommandHandler (const char * operand, size_t len); - void ListCommandHandler (const char * operand, size_t len); - void OptionCommandHandler (const char * operand, size_t len); - - private: + // command handlers + void ZapCommandHandler (const char * operand, size_t len); + void QuitCommandHandler (const char * operand, size_t len); + void StartCommandHandler (const char * operand, size_t len); + void StopCommandHandler (const char * operand, size_t len); + void SetNickCommandHandler (const char * operand, size_t len); + void GetNickCommandHandler (const char * operand, size_t len); + void NewkeysCommandHandler (const char * operand, size_t len); + void SetkeysCommandHandler (const char * operand, size_t len); + void GetkeysCommandHandler (const char * operand, size_t len); + void GetdestCommandHandler (const char * operand, size_t len); + void OuthostCommandHandler (const char * operand, size_t len); + void OutportCommandHandler (const char * operand, size_t len); + void InhostCommandHandler (const char * operand, size_t len); + void InportCommandHandler (const char * operand, size_t len); + void QuietCommandHandler (const char * operand, size_t len); + void LookupCommandHandler (const char * operand, size_t len); + void ClearCommandHandler (const char * operand, size_t len); + void ListCommandHandler (const char * operand, size_t len); + void OptionCommandHandler (const char * operand, size_t len); + + private: - void Receive (); - void HandleReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred); + void Receive (); + void HandleReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred); - void Send (size_t len); - void HandleSent (const boost::system::error_code& ecode, std::size_t bytes_transferred); - void SendReplyOK (const char * msg); - void SendReplyError (const char * msg); - void SendData (const char * nickname); + void Send (size_t len); + void HandleSent (const boost::system::error_code& ecode, std::size_t bytes_transferred); + void SendReplyOK (const char * msg); + void SendReplyError (const char * msg); + void SendData (const char * nickname); - private: + private: - BOBCommandChannel& m_Owner; - boost::asio::ip::tcp::socket m_Socket; - char m_ReceiveBuffer[BOB_COMMAND_BUFFER_SIZE + 1], m_SendBuffer[BOB_COMMAND_BUFFER_SIZE + 1]; - size_t m_ReceiveBufferOffset; - bool m_IsOpen, m_IsQuiet; - std::string m_Nickname, m_Address; - int m_InPort, m_OutPort; - i2p::data::PrivateKeys m_Keys; - std::map m_Options; - BOBDestination * m_CurrentDestination; - }; - typedef void (BOBCommandSession::*BOBCommandHandler)(const char * operand, size_t len); + BOBCommandChannel& m_Owner; + boost::asio::ip::tcp::socket m_Socket; + char m_ReceiveBuffer[BOB_COMMAND_BUFFER_SIZE + 1], m_SendBuffer[BOB_COMMAND_BUFFER_SIZE + 1]; + size_t m_ReceiveBufferOffset; + bool m_IsOpen, m_IsQuiet; + std::string m_Nickname, m_Address; + int m_InPort, m_OutPort; + i2p::data::PrivateKeys m_Keys; + std::map m_Options; + BOBDestination * m_CurrentDestination; + }; + typedef void (BOBCommandSession::*BOBCommandHandler)(const char * operand, size_t len); - class BOBCommandChannel - { - public: + class BOBCommandChannel + { + public: - BOBCommandChannel (int port); - ~BOBCommandChannel (); + BOBCommandChannel (int port); + ~BOBCommandChannel (); - void Start (); - void Stop (); + void Start (); + void Stop (); - boost::asio::io_service& GetService () { return m_Service; }; - void AddDestination (const std::string& name, BOBDestination * dest); - void DeleteDestination (const std::string& name); - BOBDestination * FindDestination (const std::string& name); - - private: + boost::asio::io_service& GetService () { return m_Service; }; + void AddDestination (const std::string& name, BOBDestination * dest); + void DeleteDestination (const std::string& name); + BOBDestination * FindDestination (const std::string& name); + + private: - void Run (); - void Accept (); - void HandleAccept(const boost::system::error_code& ecode, std::shared_ptr session); + void Run (); + void Accept (); + void HandleAccept(const boost::system::error_code& ecode, std::shared_ptr session); - private: + private: - bool m_IsRunning; - std::thread * m_Thread; - boost::asio::io_service m_Service; - boost::asio::ip::tcp::acceptor m_Acceptor; - std::map m_Destinations; - std::map m_CommandHandlers; + bool m_IsRunning; + std::thread * m_Thread; + boost::asio::io_service m_Service; + boost::asio::ip::tcp::acceptor m_Acceptor; + std::map m_Destinations; + std::map m_CommandHandlers; - public: + public: - const decltype(m_CommandHandlers)& GetCommandHandlers () const { return m_CommandHandlers; }; - const decltype(m_Destinations)& GetDestinations () const { return m_Destinations; }; - }; + const decltype(m_CommandHandlers)& GetCommandHandlers () const { return m_CommandHandlers; }; + const decltype(m_Destinations)& GetDestinations () const { return m_Destinations; }; + }; } } diff --git a/ClientContext.cpp b/ClientContext.cpp index 9f14da90..c7514a3b 100644 --- a/ClientContext.cpp +++ b/ClientContext.cpp @@ -11,334 +11,334 @@ namespace i2p { namespace client { - ClientContext context; + ClientContext context; - ClientContext::ClientContext (): m_SharedLocalDestination (nullptr), - m_HttpProxy (nullptr), m_SocksProxy (nullptr), m_SamBridge (nullptr), - m_BOBCommandChannel (nullptr), m_I2PControlService (nullptr) - { - } - - ClientContext::~ClientContext () - { - delete m_HttpProxy; - delete m_SocksProxy; - delete m_SamBridge; - delete m_BOBCommandChannel; - delete m_I2PControlService; - } - - void ClientContext::Start () - { - if (!m_SharedLocalDestination) - { - m_SharedLocalDestination = CreateNewLocalDestination (); // non-public, DSA - m_Destinations[m_SharedLocalDestination->GetIdentity ().GetIdentHash ()] = m_SharedLocalDestination; - m_SharedLocalDestination->Start (); - } + ClientContext::ClientContext (): m_SharedLocalDestination (nullptr), + m_HttpProxy (nullptr), m_SocksProxy (nullptr), m_SamBridge (nullptr), + m_BOBCommandChannel (nullptr), m_I2PControlService (nullptr) + { + } + + ClientContext::~ClientContext () + { + delete m_HttpProxy; + delete m_SocksProxy; + delete m_SamBridge; + delete m_BOBCommandChannel; + delete m_I2PControlService; + } + + void ClientContext::Start () + { + if (!m_SharedLocalDestination) + { + m_SharedLocalDestination = CreateNewLocalDestination (); // non-public, DSA + m_Destinations[m_SharedLocalDestination->GetIdentity ().GetIdentHash ()] = m_SharedLocalDestination; + m_SharedLocalDestination->Start (); + } - std::shared_ptr localDestination; - // proxies - std::string proxyKeys = i2p::util::config::GetArg("-proxykeys", ""); - if (proxyKeys.length () > 0) - localDestination = LoadLocalDestination (proxyKeys, false); - m_HttpProxy = new i2p::proxy::HTTPProxy(i2p::util::config::GetArg("-httpproxyport", 4446), localDestination); - m_HttpProxy->Start(); - LogPrint("HTTP Proxy started"); - m_SocksProxy = new i2p::proxy::SOCKSProxy(i2p::util::config::GetArg("-socksproxyport", 4447), localDestination); - m_SocksProxy->Start(); - LogPrint("SOCKS Proxy Started"); - - // I2P tunnels - std::string ircDestination = i2p::util::config::GetArg("-ircdest", ""); - if (ircDestination.length () > 0) // ircdest is presented - { - localDestination = nullptr; - std::string ircKeys = i2p::util::config::GetArg("-irckeys", ""); - if (ircKeys.length () > 0) - localDestination = LoadLocalDestination (ircKeys, false); - auto ircPort = i2p::util::config::GetArg("-ircport", 6668); - auto ircTunnel = new I2PClientTunnel (ircDestination, ircPort, localDestination); - ircTunnel->Start (); - m_ClientTunnels.insert (std::make_pair(ircPort, std::unique_ptr(ircTunnel))); - LogPrint("IRC tunnel started"); - } - std::string eepKeys = i2p::util::config::GetArg("-eepkeys", ""); - if (eepKeys.length () > 0) // eepkeys file is presented - { - localDestination = LoadLocalDestination (eepKeys, true); - auto serverTunnel = new I2PServerTunnel (i2p::util::config::GetArg("-eephost", "127.0.0.1"), - i2p::util::config::GetArg("-eepport", 80), localDestination); - serverTunnel->Start (); - m_ServerTunnels.insert (std::make_pair(localDestination->GetIdentHash (), std::unique_ptr(serverTunnel))); - LogPrint("Server tunnel started"); - } - ReadTunnels (); + std::shared_ptr localDestination; + // proxies + std::string proxyKeys = i2p::util::config::GetArg("-proxykeys", ""); + if (proxyKeys.length () > 0) + localDestination = LoadLocalDestination (proxyKeys, false); + m_HttpProxy = new i2p::proxy::HTTPProxy(i2p::util::config::GetArg("-httpproxyport", 4446), localDestination); + m_HttpProxy->Start(); + LogPrint("HTTP Proxy started"); + m_SocksProxy = new i2p::proxy::SOCKSProxy(i2p::util::config::GetArg("-socksproxyport", 4447), localDestination); + m_SocksProxy->Start(); + LogPrint("SOCKS Proxy Started"); + + // I2P tunnels + std::string ircDestination = i2p::util::config::GetArg("-ircdest", ""); + if (ircDestination.length () > 0) // ircdest is presented + { + localDestination = nullptr; + std::string ircKeys = i2p::util::config::GetArg("-irckeys", ""); + if (ircKeys.length () > 0) + localDestination = LoadLocalDestination (ircKeys, false); + auto ircPort = i2p::util::config::GetArg("-ircport", 6668); + auto ircTunnel = new I2PClientTunnel (ircDestination, ircPort, localDestination); + ircTunnel->Start (); + m_ClientTunnels.insert (std::make_pair(ircPort, std::unique_ptr(ircTunnel))); + LogPrint("IRC tunnel started"); + } + std::string eepKeys = i2p::util::config::GetArg("-eepkeys", ""); + if (eepKeys.length () > 0) // eepkeys file is presented + { + localDestination = LoadLocalDestination (eepKeys, true); + auto serverTunnel = new I2PServerTunnel (i2p::util::config::GetArg("-eephost", "127.0.0.1"), + i2p::util::config::GetArg("-eepport", 80), localDestination); + serverTunnel->Start (); + m_ServerTunnels.insert (std::make_pair(localDestination->GetIdentHash (), std::unique_ptr(serverTunnel))); + LogPrint("Server tunnel started"); + } + ReadTunnels (); - // SAM - int samPort = i2p::util::config::GetArg("-samport", 0); - if (samPort) - { - m_SamBridge = new SAMBridge (samPort); - m_SamBridge->Start (); - LogPrint("SAM bridge started"); - } + // SAM + int samPort = i2p::util::config::GetArg("-samport", 0); + if (samPort) + { + m_SamBridge = new SAMBridge (samPort); + m_SamBridge->Start (); + LogPrint("SAM bridge started"); + } - // BOB - int bobPort = i2p::util::config::GetArg("-bobport", 0); - if (bobPort) - { - m_BOBCommandChannel = new BOBCommandChannel (bobPort); - m_BOBCommandChannel->Start (); - LogPrint("BOB command channel started"); - } + // BOB + int bobPort = i2p::util::config::GetArg("-bobport", 0); + if (bobPort) + { + m_BOBCommandChannel = new BOBCommandChannel (bobPort); + m_BOBCommandChannel->Start (); + LogPrint("BOB command channel started"); + } - // I2P Control - int i2pcontrolPort = i2p::util::config::GetArg("-i2pcontrolport", 0); - if (i2pcontrolPort) - { - m_I2PControlService = new I2PControlService (i2pcontrolPort); - m_I2PControlService->Start (); - LogPrint("I2PControl started"); - } - m_AddressBook.Start (); - } - - void ClientContext::Stop () - { - m_HttpProxy->Stop(); - delete m_HttpProxy; - m_HttpProxy = nullptr; - LogPrint("HTTP Proxy stopped"); - m_SocksProxy->Stop(); - delete m_SocksProxy; - m_SocksProxy = nullptr; - LogPrint("SOCKS Proxy stopped"); - for (auto& it: m_ClientTunnels) - { - it.second->Stop (); - LogPrint("I2P client tunnel on port ", it.first, " stopped"); - } - m_ClientTunnels.clear (); - for (auto& it: m_ServerTunnels) - { - it.second->Stop (); - LogPrint("I2P server tunnel stopped"); - } - m_ServerTunnels.clear (); - if (m_SamBridge) - { - m_SamBridge->Stop (); - delete m_SamBridge; - m_SamBridge = nullptr; - LogPrint("SAM brdige stopped"); - } - if (m_BOBCommandChannel) - { - m_BOBCommandChannel->Stop (); - delete m_BOBCommandChannel; - m_BOBCommandChannel = nullptr; - LogPrint("BOB command channel stopped"); - } - if (m_I2PControlService) - { - m_I2PControlService->Stop (); - delete m_I2PControlService; - m_I2PControlService = nullptr; - LogPrint("I2PControl stopped"); - } - m_AddressBook.Stop (); - for (auto it: m_Destinations) - it.second->Stop (); - m_Destinations.clear (); - m_SharedLocalDestination = nullptr; - } - - std::shared_ptr ClientContext::LoadLocalDestination (const std::string& filename, bool isPublic) - { - i2p::data::PrivateKeys keys; - std::string fullPath = i2p::util::filesystem::GetFullPath (filename); - std::ifstream s(fullPath.c_str (), std::ifstream::binary); - if (s.is_open ()) - { - s.seekg (0, std::ios::end); - size_t len = s.tellg(); - s.seekg (0, std::ios::beg); - uint8_t * buf = new uint8_t[len]; - s.read ((char *)buf, len); - keys.FromBuffer (buf, len); - delete[] buf; - LogPrint ("Local address ", m_AddressBook.ToAddress(keys.GetPublic ().GetIdentHash ()), " loaded"); - } - else - { - LogPrint ("Can't open file ", fullPath, " Creating new one"); - keys = i2p::data::PrivateKeys::CreateRandomKeys (i2p::data::SIGNING_KEY_TYPE_ECDSA_SHA256_P256); - std::ofstream f (fullPath, std::ofstream::binary | std::ofstream::out); - size_t len = keys.GetFullLen (); - uint8_t * buf = new uint8_t[len]; - len = keys.ToBuffer (buf, len); - f.write ((char *)buf, len); - delete[] buf; - - LogPrint ("New private keys file ", fullPath, " for ", m_AddressBook.ToAddress(keys.GetPublic ().GetIdentHash ()), " created"); - } + // I2P Control + int i2pcontrolPort = i2p::util::config::GetArg("-i2pcontrolport", 0); + if (i2pcontrolPort) + { + m_I2PControlService = new I2PControlService (i2pcontrolPort); + m_I2PControlService->Start (); + LogPrint("I2PControl started"); + } + m_AddressBook.Start (); + } + + void ClientContext::Stop () + { + m_HttpProxy->Stop(); + delete m_HttpProxy; + m_HttpProxy = nullptr; + LogPrint("HTTP Proxy stopped"); + m_SocksProxy->Stop(); + delete m_SocksProxy; + m_SocksProxy = nullptr; + LogPrint("SOCKS Proxy stopped"); + for (auto& it: m_ClientTunnels) + { + it.second->Stop (); + LogPrint("I2P client tunnel on port ", it.first, " stopped"); + } + m_ClientTunnels.clear (); + for (auto& it: m_ServerTunnels) + { + it.second->Stop (); + LogPrint("I2P server tunnel stopped"); + } + m_ServerTunnels.clear (); + if (m_SamBridge) + { + m_SamBridge->Stop (); + delete m_SamBridge; + m_SamBridge = nullptr; + LogPrint("SAM brdige stopped"); + } + if (m_BOBCommandChannel) + { + m_BOBCommandChannel->Stop (); + delete m_BOBCommandChannel; + m_BOBCommandChannel = nullptr; + LogPrint("BOB command channel stopped"); + } + if (m_I2PControlService) + { + m_I2PControlService->Stop (); + delete m_I2PControlService; + m_I2PControlService = nullptr; + LogPrint("I2PControl stopped"); + } + m_AddressBook.Stop (); + for (auto it: m_Destinations) + it.second->Stop (); + m_Destinations.clear (); + m_SharedLocalDestination = nullptr; + } + + std::shared_ptr ClientContext::LoadLocalDestination (const std::string& filename, bool isPublic) + { + i2p::data::PrivateKeys keys; + std::string fullPath = i2p::util::filesystem::GetFullPath (filename); + std::ifstream s(fullPath.c_str (), std::ifstream::binary); + if (s.is_open ()) + { + s.seekg (0, std::ios::end); + size_t len = s.tellg(); + s.seekg (0, std::ios::beg); + uint8_t * buf = new uint8_t[len]; + s.read ((char *)buf, len); + keys.FromBuffer (buf, len); + delete[] buf; + LogPrint ("Local address ", m_AddressBook.ToAddress(keys.GetPublic ().GetIdentHash ()), " loaded"); + } + else + { + LogPrint ("Can't open file ", fullPath, " Creating new one"); + keys = i2p::data::PrivateKeys::CreateRandomKeys (i2p::data::SIGNING_KEY_TYPE_ECDSA_SHA256_P256); + std::ofstream f (fullPath, std::ofstream::binary | std::ofstream::out); + size_t len = keys.GetFullLen (); + uint8_t * buf = new uint8_t[len]; + len = keys.ToBuffer (buf, len); + f.write ((char *)buf, len); + delete[] buf; + + LogPrint ("New private keys file ", fullPath, " for ", m_AddressBook.ToAddress(keys.GetPublic ().GetIdentHash ()), " created"); + } - std::shared_ptr localDestination = nullptr; - std::unique_lock l(m_DestinationsMutex); - auto it = m_Destinations.find (keys.GetPublic ().GetIdentHash ()); - if (it != m_Destinations.end ()) - { - LogPrint (eLogWarning, "Local destination ", m_AddressBook.ToAddress(keys.GetPublic ().GetIdentHash ()), " alreday exists"); - localDestination = it->second; - } - else - { - localDestination = std::make_shared (keys, isPublic); - m_Destinations[localDestination->GetIdentHash ()] = localDestination; - localDestination->Start (); - } - return localDestination; - } + std::shared_ptr localDestination = nullptr; + std::unique_lock l(m_DestinationsMutex); + auto it = m_Destinations.find (keys.GetPublic ().GetIdentHash ()); + if (it != m_Destinations.end ()) + { + LogPrint (eLogWarning, "Local destination ", m_AddressBook.ToAddress(keys.GetPublic ().GetIdentHash ()), " alreday exists"); + localDestination = it->second; + } + else + { + localDestination = std::make_shared (keys, isPublic); + m_Destinations[localDestination->GetIdentHash ()] = localDestination; + localDestination->Start (); + } + return localDestination; + } - std::shared_ptr ClientContext::CreateNewLocalDestination (bool isPublic, i2p::data::SigningKeyType sigType, - const std::map * params) - { - i2p::data::PrivateKeys keys = i2p::data::PrivateKeys::CreateRandomKeys (sigType); - auto localDestination = std::make_shared (keys, isPublic, params); - std::unique_lock l(m_DestinationsMutex); - m_Destinations[localDestination->GetIdentHash ()] = localDestination; - localDestination->Start (); - return localDestination; - } + std::shared_ptr ClientContext::CreateNewLocalDestination (bool isPublic, i2p::data::SigningKeyType sigType, + const std::map * params) + { + i2p::data::PrivateKeys keys = i2p::data::PrivateKeys::CreateRandomKeys (sigType); + auto localDestination = std::make_shared (keys, isPublic, params); + std::unique_lock l(m_DestinationsMutex); + m_Destinations[localDestination->GetIdentHash ()] = localDestination; + localDestination->Start (); + return localDestination; + } - void ClientContext::DeleteLocalDestination (std::shared_ptr destination) - { - if (!destination) return; - auto it = m_Destinations.find (destination->GetIdentHash ()); - if (it != m_Destinations.end ()) - { - auto d = it->second; - { - std::unique_lock l(m_DestinationsMutex); - m_Destinations.erase (it); - } - d->Stop (); - } - } + void ClientContext::DeleteLocalDestination (std::shared_ptr destination) + { + if (!destination) return; + auto it = m_Destinations.find (destination->GetIdentHash ()); + if (it != m_Destinations.end ()) + { + auto d = it->second; + { + std::unique_lock l(m_DestinationsMutex); + m_Destinations.erase (it); + } + d->Stop (); + } + } - std::shared_ptr ClientContext::CreateNewLocalDestination (const i2p::data::PrivateKeys& keys, bool isPublic, - const std::map * params) - { - auto it = m_Destinations.find (keys.GetPublic ().GetIdentHash ()); - if (it != m_Destinations.end ()) - { - LogPrint ("Local destination ", m_AddressBook.ToAddress(keys.GetPublic ().GetIdentHash ()), " exists"); - if (!it->second->IsRunning ()) - { - it->second->Start (); - return it->second; - } - return nullptr; - } - auto localDestination = std::make_shared (keys, isPublic, params); - std::unique_lock l(m_DestinationsMutex); - m_Destinations[keys.GetPublic ().GetIdentHash ()] = localDestination; - localDestination->Start (); - return localDestination; - } - - std::shared_ptr ClientContext::FindLocalDestination (const i2p::data::IdentHash& destination) const - { - auto it = m_Destinations.find (destination); - if (it != m_Destinations.end ()) - return it->second; - return nullptr; - } + std::shared_ptr ClientContext::CreateNewLocalDestination (const i2p::data::PrivateKeys& keys, bool isPublic, + const std::map * params) + { + auto it = m_Destinations.find (keys.GetPublic ().GetIdentHash ()); + if (it != m_Destinations.end ()) + { + LogPrint ("Local destination ", m_AddressBook.ToAddress(keys.GetPublic ().GetIdentHash ()), " exists"); + if (!it->second->IsRunning ()) + { + it->second->Start (); + return it->second; + } + return nullptr; + } + auto localDestination = std::make_shared (keys, isPublic, params); + std::unique_lock l(m_DestinationsMutex); + m_Destinations[keys.GetPublic ().GetIdentHash ()] = localDestination; + localDestination->Start (); + return localDestination; + } + + std::shared_ptr ClientContext::FindLocalDestination (const i2p::data::IdentHash& destination) const + { + auto it = m_Destinations.find (destination); + if (it != m_Destinations.end ()) + return it->second; + return nullptr; + } - void ClientContext::ReadTunnels () - { - boost::property_tree::ptree pt; - try - { - boost::property_tree::read_ini (i2p::util::filesystem::GetFullPath (TUNNELS_CONFIG_FILENAME), pt); - } - catch (std::exception& ex) - { - LogPrint (eLogWarning, "Can't read ", TUNNELS_CONFIG_FILENAME, ": ", ex.what ()); - return; - } - - int numClientTunnels = 0, numServerTunnels = 0; - for (auto& section: pt) - { - std::string name = section.first; - try - { - std::string type = section.second.get (I2P_TUNNELS_SECTION_TYPE); - if (type == I2P_TUNNELS_SECTION_TYPE_CLIENT) - { - // mandatory params - std::string dest = section.second.get (I2P_CLIENT_TUNNEL_DESTINATION); - int port = section.second.get (I2P_CLIENT_TUNNEL_PORT); - // optional params - std::string keys = section.second.get (I2P_CLIENT_TUNNEL_KEYS, ""); - int destinationPort = section.second.get (I2P_CLIENT_TUNNEL_DESTINATION_PORT, 0); + void ClientContext::ReadTunnels () + { + boost::property_tree::ptree pt; + try + { + boost::property_tree::read_ini (i2p::util::filesystem::GetFullPath (TUNNELS_CONFIG_FILENAME), pt); + } + catch (std::exception& ex) + { + LogPrint (eLogWarning, "Can't read ", TUNNELS_CONFIG_FILENAME, ": ", ex.what ()); + return; + } + + int numClientTunnels = 0, numServerTunnels = 0; + for (auto& section: pt) + { + std::string name = section.first; + try + { + std::string type = section.second.get (I2P_TUNNELS_SECTION_TYPE); + if (type == I2P_TUNNELS_SECTION_TYPE_CLIENT) + { + // mandatory params + std::string dest = section.second.get (I2P_CLIENT_TUNNEL_DESTINATION); + int port = section.second.get (I2P_CLIENT_TUNNEL_PORT); + // optional params + std::string keys = section.second.get (I2P_CLIENT_TUNNEL_KEYS, ""); + int destinationPort = section.second.get (I2P_CLIENT_TUNNEL_DESTINATION_PORT, 0); - std::shared_ptr localDestination = nullptr; - if (keys.length () > 0) - localDestination = LoadLocalDestination (keys, false); - auto clientTunnel = new I2PClientTunnel (dest, port, localDestination, destinationPort); - if (m_ClientTunnels.insert (std::make_pair (port, std::unique_ptr(clientTunnel))).second) - clientTunnel->Start (); - else - LogPrint (eLogError, "I2P client tunnel with port ", port, " already exists"); - numClientTunnels++; - } - else if (type == I2P_TUNNELS_SECTION_TYPE_SERVER || type == I2P_TUNNELS_SECTION_TYPE_HTTP) - { - // mandatory params - std::string host = section.second.get (I2P_SERVER_TUNNEL_HOST); - int port = section.second.get (I2P_SERVER_TUNNEL_PORT); - std::string keys = section.second.get (I2P_SERVER_TUNNEL_KEYS); - // optional params - int inPort = section.second.get (I2P_SERVER_TUNNEL_INPORT, 0); - std::string accessList = section.second.get (I2P_SERVER_TUNNEL_ACCESS_LIST, ""); + std::shared_ptr localDestination = nullptr; + if (keys.length () > 0) + localDestination = LoadLocalDestination (keys, false); + auto clientTunnel = new I2PClientTunnel (dest, port, localDestination, destinationPort); + if (m_ClientTunnels.insert (std::make_pair (port, std::unique_ptr(clientTunnel))).second) + clientTunnel->Start (); + else + LogPrint (eLogError, "I2P client tunnel with port ", port, " already exists"); + numClientTunnels++; + } + else if (type == I2P_TUNNELS_SECTION_TYPE_SERVER || type == I2P_TUNNELS_SECTION_TYPE_HTTP) + { + // mandatory params + std::string host = section.second.get (I2P_SERVER_TUNNEL_HOST); + int port = section.second.get (I2P_SERVER_TUNNEL_PORT); + std::string keys = section.second.get (I2P_SERVER_TUNNEL_KEYS); + // optional params + int inPort = section.second.get (I2P_SERVER_TUNNEL_INPORT, 0); + std::string accessList = section.second.get (I2P_SERVER_TUNNEL_ACCESS_LIST, ""); - auto localDestination = LoadLocalDestination (keys, true); - I2PServerTunnel * serverTunnel = (type == I2P_TUNNELS_SECTION_TYPE_HTTP) ? new I2PServerTunnelHTTP (host, port, localDestination, inPort) : new I2PServerTunnel (host, port, localDestination, inPort); - if (accessList.length () > 0) - { - std::set idents; - size_t pos = 0, comma; - do - { - comma = accessList.find (',', pos); - i2p::data::IdentHash ident; - ident.FromBase32 (accessList.substr (pos, comma != std::string::npos ? comma - pos : std::string::npos)); - idents.insert (ident); - pos = comma + 1; - } - while (comma != std::string::npos); - serverTunnel->SetAccessList (idents); - } - if (m_ServerTunnels.insert (std::make_pair (localDestination->GetIdentHash (), std::unique_ptr(serverTunnel))).second) - serverTunnel->Start (); - else - LogPrint (eLogError, "I2P server tunnel for destination ", m_AddressBook.ToAddress(localDestination->GetIdentHash ()), " already exists"); - numServerTunnels++; - } - else - LogPrint (eLogWarning, "Unknown section type=", type, " of ", name, " in ", TUNNELS_CONFIG_FILENAME); - - } - catch (std::exception& ex) - { - LogPrint (eLogError, "Can't read tunnel ", name, " params: ", ex.what ()); - } - } - LogPrint (eLogInfo, numClientTunnels, " I2P client tunnels created"); - LogPrint (eLogInfo, numServerTunnels, " I2P server tunnels created"); - } -} -} + auto localDestination = LoadLocalDestination (keys, true); + I2PServerTunnel * serverTunnel = (type == I2P_TUNNELS_SECTION_TYPE_HTTP) ? new I2PServerTunnelHTTP (host, port, localDestination, inPort) : new I2PServerTunnel (host, port, localDestination, inPort); + if (accessList.length () > 0) + { + std::set idents; + size_t pos = 0, comma; + do + { + comma = accessList.find (',', pos); + i2p::data::IdentHash ident; + ident.FromBase32 (accessList.substr (pos, comma != std::string::npos ? comma - pos : std::string::npos)); + idents.insert (ident); + pos = comma + 1; + } + while (comma != std::string::npos); + serverTunnel->SetAccessList (idents); + } + if (m_ServerTunnels.insert (std::make_pair (localDestination->GetIdentHash (), std::unique_ptr(serverTunnel))).second) + serverTunnel->Start (); + else + LogPrint (eLogError, "I2P server tunnel for destination ", m_AddressBook.ToAddress(localDestination->GetIdentHash ()), " already exists"); + numServerTunnels++; + } + else + LogPrint (eLogWarning, "Unknown section type=", type, " of ", name, " in ", TUNNELS_CONFIG_FILENAME); + + } + catch (std::exception& ex) + { + LogPrint (eLogError, "Can't read tunnel ", name, " params: ", ex.what ()); + } + } + LogPrint (eLogInfo, numClientTunnels, " I2P client tunnels created"); + LogPrint (eLogInfo, numServerTunnels, " I2P server tunnels created"); + } +} +} diff --git a/ClientContext.h b/ClientContext.h index 0fe89fb8..0506b945 100644 --- a/ClientContext.h +++ b/ClientContext.h @@ -17,70 +17,70 @@ namespace i2p { namespace client { - const char I2P_TUNNELS_SECTION_TYPE[] = "type"; - const char I2P_TUNNELS_SECTION_TYPE_CLIENT[] = "client"; - const char I2P_TUNNELS_SECTION_TYPE_SERVER[] = "server"; - const char I2P_TUNNELS_SECTION_TYPE_HTTP[] = "http"; - const char I2P_CLIENT_TUNNEL_PORT[] = "port"; - const char I2P_CLIENT_TUNNEL_DESTINATION[] = "destination"; - const char I2P_CLIENT_TUNNEL_KEYS[] = "keys"; - const char I2P_CLIENT_TUNNEL_DESTINATION_PORT[] = "destinationport"; - const char I2P_SERVER_TUNNEL_HOST[] = "host"; - const char I2P_SERVER_TUNNEL_PORT[] = "port"; - const char I2P_SERVER_TUNNEL_KEYS[] = "keys"; - const char I2P_SERVER_TUNNEL_INPORT[] = "inport"; - const char I2P_SERVER_TUNNEL_ACCESS_LIST[] = "accesslist"; - const char TUNNELS_CONFIG_FILENAME[] = "tunnels.cfg"; + const char I2P_TUNNELS_SECTION_TYPE[] = "type"; + const char I2P_TUNNELS_SECTION_TYPE_CLIENT[] = "client"; + const char I2P_TUNNELS_SECTION_TYPE_SERVER[] = "server"; + const char I2P_TUNNELS_SECTION_TYPE_HTTP[] = "http"; + const char I2P_CLIENT_TUNNEL_PORT[] = "port"; + const char I2P_CLIENT_TUNNEL_DESTINATION[] = "destination"; + const char I2P_CLIENT_TUNNEL_KEYS[] = "keys"; + const char I2P_CLIENT_TUNNEL_DESTINATION_PORT[] = "destinationport"; + const char I2P_SERVER_TUNNEL_HOST[] = "host"; + const char I2P_SERVER_TUNNEL_PORT[] = "port"; + const char I2P_SERVER_TUNNEL_KEYS[] = "keys"; + const char I2P_SERVER_TUNNEL_INPORT[] = "inport"; + const char I2P_SERVER_TUNNEL_ACCESS_LIST[] = "accesslist"; + const char TUNNELS_CONFIG_FILENAME[] = "tunnels.cfg"; - class ClientContext - { - public: + class ClientContext + { + public: - ClientContext (); - ~ClientContext (); + ClientContext (); + ~ClientContext (); - void Start (); - void Stop (); + void Start (); + void Stop (); - std::shared_ptr GetSharedLocalDestination () const { return m_SharedLocalDestination; }; - std::shared_ptr CreateNewLocalDestination (bool isPublic = false, i2p::data::SigningKeyType sigType = i2p::data::SIGNING_KEY_TYPE_DSA_SHA1, - const std::map * params = nullptr); // transient - std::shared_ptr CreateNewLocalDestination (const i2p::data::PrivateKeys& keys, bool isPublic = true, - const std::map * params = nullptr); - void DeleteLocalDestination (std::shared_ptr destination); - std::shared_ptr FindLocalDestination (const i2p::data::IdentHash& destination) const; - std::shared_ptr LoadLocalDestination (const std::string& filename, bool isPublic); + std::shared_ptr GetSharedLocalDestination () const { return m_SharedLocalDestination; }; + std::shared_ptr CreateNewLocalDestination (bool isPublic = false, i2p::data::SigningKeyType sigType = i2p::data::SIGNING_KEY_TYPE_DSA_SHA1, + const std::map * params = nullptr); // transient + std::shared_ptr CreateNewLocalDestination (const i2p::data::PrivateKeys& keys, bool isPublic = true, + const std::map * params = nullptr); + void DeleteLocalDestination (std::shared_ptr destination); + std::shared_ptr FindLocalDestination (const i2p::data::IdentHash& destination) const; + std::shared_ptr LoadLocalDestination (const std::string& filename, bool isPublic); - AddressBook& GetAddressBook () { return m_AddressBook; }; - const SAMBridge * GetSAMBridge () const { return m_SamBridge; }; - - private: + AddressBook& GetAddressBook () { return m_AddressBook; }; + const SAMBridge * GetSAMBridge () const { return m_SamBridge; }; + + private: - void ReadTunnels (); - - private: + void ReadTunnels (); + + private: - std::mutex m_DestinationsMutex; - std::map > m_Destinations; - std::shared_ptr m_SharedLocalDestination; + std::mutex m_DestinationsMutex; + std::map > m_Destinations; + std::shared_ptr m_SharedLocalDestination; - AddressBook m_AddressBook; + AddressBook m_AddressBook; - i2p::proxy::HTTPProxy * m_HttpProxy; - i2p::proxy::SOCKSProxy * m_SocksProxy; - std::map > m_ClientTunnels; // port->tunnel - std::map > m_ServerTunnels; // destination->tunnel - SAMBridge * m_SamBridge; - BOBCommandChannel * m_BOBCommandChannel; - I2PControlService * m_I2PControlService; + i2p::proxy::HTTPProxy * m_HttpProxy; + i2p::proxy::SOCKSProxy * m_SocksProxy; + std::map > m_ClientTunnels; // port->tunnel + std::map > m_ServerTunnels; // destination->tunnel + SAMBridge * m_SamBridge; + BOBCommandChannel * m_BOBCommandChannel; + I2PControlService * m_I2PControlService; - public: - // for HTTP - const decltype(m_Destinations)& GetDestinations () const { return m_Destinations; }; - }; - - extern ClientContext context; -} -} + public: + // for HTTP + const decltype(m_Destinations)& GetDestinations () const { return m_Destinations; }; + }; + + extern ClientContext context; +} +} #endif diff --git a/CryptoConst.cpp b/CryptoConst.cpp index a8868988..3ee378b7 100644 --- a/CryptoConst.cpp +++ b/CryptoConst.cpp @@ -5,69 +5,69 @@ namespace i2p { namespace crypto { - const uint8_t elgp_[256]= - { - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34, - 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1, 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, - 0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22, 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD, - 0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, 0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37, - 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45, 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6, - 0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B, 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED, - 0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5, 0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6, - 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D, 0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05, - 0x98, 0xDA, 0x48, 0x36, 0x1C, 0x55, 0xD3, 0x9A, 0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F, - 0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96, 0x1C, 0x62, 0xF3, 0x56, 0x20, 0x85, 0x52, 0xBB, - 0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D, 0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04, - 0xF1, 0x74, 0x6C, 0x08, 0xCA, 0x18, 0x21, 0x7C, 0x32, 0x90, 0x5E, 0x46, 0x2E, 0x36, 0xCE, 0x3B, - 0xE3, 0x9E, 0x77, 0x2C, 0x18, 0x0E, 0x86, 0x03, 0x9B, 0x27, 0x83, 0xA2, 0xEC, 0x07, 0xA2, 0x8F, - 0xB5, 0xC5, 0x5D, 0xF0, 0x6F, 0x4C, 0x52, 0xC9, 0xDE, 0x2B, 0xCB, 0xF6, 0x95, 0x58, 0x17, 0x18, - 0x39, 0x95, 0x49, 0x7C, 0xEA, 0x95, 0x6A, 0xE5, 0x15, 0xD2, 0x26, 0x18, 0x98, 0xFA, 0x05, 0x10, - 0x15, 0x72, 0x8E, 0x5A, 0x8A, 0xAC, 0xAA, 0x68, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF - }; - - const uint8_t dsap_[128]= - { - 0x9c, 0x05, 0xb2, 0xaa, 0x96, 0x0d, 0x9b, 0x97, 0xb8, 0x93, 0x19, 0x63, 0xc9, 0xcc, 0x9e, 0x8c, - 0x30, 0x26, 0xe9, 0xb8, 0xed, 0x92, 0xfa, 0xd0, 0xa6, 0x9c, 0xc8, 0x86, 0xd5, 0xbf, 0x80, 0x15, - 0xfc, 0xad, 0xae, 0x31, 0xa0, 0xad, 0x18, 0xfa, 0xb3, 0xf0, 0x1b, 0x00, 0xa3, 0x58, 0xde, 0x23, - 0x76, 0x55, 0xc4, 0x96, 0x4a, 0xfa, 0xa2, 0xb3, 0x37, 0xe9, 0x6a, 0xd3, 0x16, 0xb9, 0xfb, 0x1c, - 0xc5, 0x64, 0xb5, 0xae, 0xc5, 0xb6, 0x9a, 0x9f, 0xf6, 0xc3, 0xe4, 0x54, 0x87, 0x07, 0xfe, 0xf8, - 0x50, 0x3d, 0x91, 0xdd, 0x86, 0x02, 0xe8, 0x67, 0xe6, 0xd3, 0x5d, 0x22, 0x35, 0xc1, 0x86, 0x9c, - 0xe2, 0x47, 0x9c, 0x3b, 0x9d, 0x54, 0x01, 0xde, 0x04, 0xe0, 0x72, 0x7f, 0xb3, 0x3d, 0x65, 0x11, - 0x28, 0x5d, 0x4c, 0xf2, 0x95, 0x38, 0xd9, 0xe3, 0xb6, 0x05, 0x1f, 0x5b, 0x22, 0xcc, 0x1c, 0x93 - }; + const uint8_t elgp_[256]= + { + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34, + 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1, 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, + 0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22, 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD, + 0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, 0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37, + 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45, 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6, + 0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B, 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED, + 0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5, 0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6, + 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D, 0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05, + 0x98, 0xDA, 0x48, 0x36, 0x1C, 0x55, 0xD3, 0x9A, 0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F, + 0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96, 0x1C, 0x62, 0xF3, 0x56, 0x20, 0x85, 0x52, 0xBB, + 0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D, 0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04, + 0xF1, 0x74, 0x6C, 0x08, 0xCA, 0x18, 0x21, 0x7C, 0x32, 0x90, 0x5E, 0x46, 0x2E, 0x36, 0xCE, 0x3B, + 0xE3, 0x9E, 0x77, 0x2C, 0x18, 0x0E, 0x86, 0x03, 0x9B, 0x27, 0x83, 0xA2, 0xEC, 0x07, 0xA2, 0x8F, + 0xB5, 0xC5, 0x5D, 0xF0, 0x6F, 0x4C, 0x52, 0xC9, 0xDE, 0x2B, 0xCB, 0xF6, 0x95, 0x58, 0x17, 0x18, + 0x39, 0x95, 0x49, 0x7C, 0xEA, 0x95, 0x6A, 0xE5, 0x15, 0xD2, 0x26, 0x18, 0x98, 0xFA, 0x05, 0x10, + 0x15, 0x72, 0x8E, 0x5A, 0x8A, 0xAC, 0xAA, 0x68, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF + }; + + const uint8_t dsap_[128]= + { + 0x9c, 0x05, 0xb2, 0xaa, 0x96, 0x0d, 0x9b, 0x97, 0xb8, 0x93, 0x19, 0x63, 0xc9, 0xcc, 0x9e, 0x8c, + 0x30, 0x26, 0xe9, 0xb8, 0xed, 0x92, 0xfa, 0xd0, 0xa6, 0x9c, 0xc8, 0x86, 0xd5, 0xbf, 0x80, 0x15, + 0xfc, 0xad, 0xae, 0x31, 0xa0, 0xad, 0x18, 0xfa, 0xb3, 0xf0, 0x1b, 0x00, 0xa3, 0x58, 0xde, 0x23, + 0x76, 0x55, 0xc4, 0x96, 0x4a, 0xfa, 0xa2, 0xb3, 0x37, 0xe9, 0x6a, 0xd3, 0x16, 0xb9, 0xfb, 0x1c, + 0xc5, 0x64, 0xb5, 0xae, 0xc5, 0xb6, 0x9a, 0x9f, 0xf6, 0xc3, 0xe4, 0x54, 0x87, 0x07, 0xfe, 0xf8, + 0x50, 0x3d, 0x91, 0xdd, 0x86, 0x02, 0xe8, 0x67, 0xe6, 0xd3, 0x5d, 0x22, 0x35, 0xc1, 0x86, 0x9c, + 0xe2, 0x47, 0x9c, 0x3b, 0x9d, 0x54, 0x01, 0xde, 0x04, 0xe0, 0x72, 0x7f, 0xb3, 0x3d, 0x65, 0x11, + 0x28, 0x5d, 0x4c, 0xf2, 0x95, 0x38, 0xd9, 0xe3, 0xb6, 0x05, 0x1f, 0x5b, 0x22, 0xcc, 0x1c, 0x93 + }; - const uint8_t dsaq_[20]= - { - 0xa5, 0xdf, 0xc2, 0x8f, 0xef, 0x4c, 0xa1, 0xe2, 0x86, 0x74, 0x4c, 0xd8, 0xee, 0xd9, 0xd2, 0x9d, - 0x68, 0x40, 0x46, 0xb7 - }; + const uint8_t dsaq_[20]= + { + 0xa5, 0xdf, 0xc2, 0x8f, 0xef, 0x4c, 0xa1, 0xe2, 0x86, 0x74, 0x4c, 0xd8, 0xee, 0xd9, 0xd2, 0x9d, + 0x68, 0x40, 0x46, 0xb7 + }; - const uint8_t dsag_[128]= - { - 0x0c, 0x1f, 0x4d, 0x27, 0xd4, 0x00, 0x93, 0xb4, 0x29, 0xe9, 0x62, 0xd7, 0x22, 0x38, 0x24, 0xe0, - 0xbb, 0xc4, 0x7e, 0x7c, 0x83, 0x2a, 0x39, 0x23, 0x6f, 0xc6, 0x83, 0xaf, 0x84, 0x88, 0x95, 0x81, - 0x07, 0x5f, 0xf9, 0x08, 0x2e, 0xd3, 0x23, 0x53, 0xd4, 0x37, 0x4d, 0x73, 0x01, 0xcd, 0xa1, 0xd2, - 0x3c, 0x43, 0x1f, 0x46, 0x98, 0x59, 0x9d, 0xda, 0x02, 0x45, 0x18, 0x24, 0xff, 0x36, 0x97, 0x52, - 0x59, 0x36, 0x47, 0xcc, 0x3d, 0xdc, 0x19, 0x7d, 0xe9, 0x85, 0xe4, 0x3d, 0x13, 0x6c, 0xdc, 0xfc, - 0x6b, 0xd5, 0x40, 0x9c, 0xd2, 0xf4, 0x50, 0x82, 0x11, 0x42, 0xa5, 0xe6, 0xf8, 0xeb, 0x1c, 0x3a, - 0xb5, 0xd0, 0x48, 0x4b, 0x81, 0x29, 0xfc, 0xf1, 0x7b, 0xce, 0x4f, 0x7f, 0x33, 0x32, 0x1c, 0x3c, - 0xb3, 0xdb, 0xb1, 0x4a, 0x90, 0x5e, 0x7b, 0x2b, 0x3e, 0x93, 0xbe, 0x47, 0x08, 0xcb, 0xcc, 0x82 - }; + const uint8_t dsag_[128]= + { + 0x0c, 0x1f, 0x4d, 0x27, 0xd4, 0x00, 0x93, 0xb4, 0x29, 0xe9, 0x62, 0xd7, 0x22, 0x38, 0x24, 0xe0, + 0xbb, 0xc4, 0x7e, 0x7c, 0x83, 0x2a, 0x39, 0x23, 0x6f, 0xc6, 0x83, 0xaf, 0x84, 0x88, 0x95, 0x81, + 0x07, 0x5f, 0xf9, 0x08, 0x2e, 0xd3, 0x23, 0x53, 0xd4, 0x37, 0x4d, 0x73, 0x01, 0xcd, 0xa1, 0xd2, + 0x3c, 0x43, 0x1f, 0x46, 0x98, 0x59, 0x9d, 0xda, 0x02, 0x45, 0x18, 0x24, 0xff, 0x36, 0x97, 0x52, + 0x59, 0x36, 0x47, 0xcc, 0x3d, 0xdc, 0x19, 0x7d, 0xe9, 0x85, 0xe4, 0x3d, 0x13, 0x6c, 0xdc, 0xfc, + 0x6b, 0xd5, 0x40, 0x9c, 0xd2, 0xf4, 0x50, 0x82, 0x11, 0x42, 0xa5, 0xe6, 0xf8, 0xeb, 0x1c, 0x3a, + 0xb5, 0xd0, 0x48, 0x4b, 0x81, 0x29, 0xfc, 0xf1, 0x7b, 0xce, 0x4f, 0x7f, 0x33, 0x32, 0x1c, 0x3c, + 0xb3, 0xdb, 0xb1, 0x4a, 0x90, 0x5e, 0x7b, 0x2b, 0x3e, 0x93, 0xbe, 0x47, 0x08, 0xcb, 0xcc, 0x82 + }; - const CryptoConstants& GetCryptoConstants () - { - static CryptoConstants cryptoConstants = - { - {elgp_, 256}, // elgp - {2}, // elgg - {dsap_, 128}, // dsap - {dsaq_, 20}, // dsaq - {dsag_, 128} // dsag - }; - return cryptoConstants; - } - + const CryptoConstants& GetCryptoConstants () + { + static CryptoConstants cryptoConstants = + { + {elgp_, 256}, // elgp + {2}, // elgg + {dsap_, 128}, // dsap + {dsaq_, 20}, // dsaq + {dsag_, 128} // dsag + }; + return cryptoConstants; + } + } } diff --git a/CryptoConst.h b/CryptoConst.h index ba48a35d..fc817167 100644 --- a/CryptoConst.h +++ b/CryptoConst.h @@ -7,32 +7,32 @@ namespace i2p { namespace crypto { - struct CryptoConstants - { - // DH/ElGamal - const CryptoPP::Integer elgp; - const CryptoPP::Integer elgg; + struct CryptoConstants + { + // DH/ElGamal + const CryptoPP::Integer elgp; + const CryptoPP::Integer elgg; - // DSA - const CryptoPP::Integer dsap; - const CryptoPP::Integer dsaq; - const CryptoPP::Integer dsag; - }; - - const CryptoConstants& GetCryptoConstants (); - - // DH/ElGamal - #define elgp GetCryptoConstants ().elgp - #define elgg GetCryptoConstants ().elgg + // DSA + const CryptoPP::Integer dsap; + const CryptoPP::Integer dsaq; + const CryptoPP::Integer dsag; + }; + + const CryptoConstants& GetCryptoConstants (); + + // DH/ElGamal + #define elgp GetCryptoConstants ().elgp + #define elgg GetCryptoConstants ().elgg - // DSA - #define dsap GetCryptoConstants ().dsap - #define dsaq GetCryptoConstants ().dsaq - #define dsag GetCryptoConstants ().dsag + // DSA + #define dsap GetCryptoConstants ().dsap + #define dsaq GetCryptoConstants ().dsaq + #define dsag GetCryptoConstants ().dsag - // RSA - const int rsae = 65537; -} -} + // RSA + const int rsae = 65537; +} +} #endif diff --git a/Daemon.cpp b/Daemon.cpp index 77cd0899..599c192a 100644 --- a/Daemon.cpp +++ b/Daemon.cpp @@ -20,125 +20,125 @@ namespace i2p { - namespace util - { - class Daemon_Singleton::Daemon_Singleton_Private - { - public: - Daemon_Singleton_Private() : httpServer(nullptr) - {}; - ~Daemon_Singleton_Private() - { - delete httpServer; - }; + namespace util + { + class Daemon_Singleton::Daemon_Singleton_Private + { + public: + Daemon_Singleton_Private() : httpServer(nullptr) + {}; + ~Daemon_Singleton_Private() + { + delete httpServer; + }; - i2p::util::HTTPServer *httpServer; - }; + i2p::util::HTTPServer *httpServer; + }; - Daemon_Singleton::Daemon_Singleton() : running(1), d(*new Daemon_Singleton_Private()) {}; - Daemon_Singleton::~Daemon_Singleton() { - delete &d; - }; + Daemon_Singleton::Daemon_Singleton() : running(1), d(*new Daemon_Singleton_Private()) {}; + Daemon_Singleton::~Daemon_Singleton() { + delete &d; + }; - bool Daemon_Singleton::IsService () const - { + bool Daemon_Singleton::IsService () const + { #ifndef _WIN32 - return i2p::util::config::GetArg("-service", 0); + return i2p::util::config::GetArg("-service", 0); #else - return false; + return false; #endif - } + } - bool Daemon_Singleton::init(int argc, char* argv[]) - { - i2p::util::config::OptionParser(argc, argv); - i2p::context.Init (); + bool Daemon_Singleton::init(int argc, char* argv[]) + { + i2p::util::config::OptionParser(argc, argv); + i2p::context.Init (); - LogPrint("\n\n\n\ni2pd starting\n"); - LogPrint("Version ", VERSION); - LogPrint("data directory: ", i2p::util::filesystem::GetDataDir().string()); - i2p::util::filesystem::ReadConfigFile(i2p::util::config::mapArgs, i2p::util::config::mapMultiArgs); + LogPrint("\n\n\n\ni2pd starting\n"); + LogPrint("Version ", VERSION); + LogPrint("data directory: ", i2p::util::filesystem::GetDataDir().string()); + i2p::util::filesystem::ReadConfigFile(i2p::util::config::mapArgs, i2p::util::config::mapMultiArgs); - isDaemon = i2p::util::config::GetArg("-daemon", 0); - isLogging = i2p::util::config::GetArg("-log", 1); + isDaemon = i2p::util::config::GetArg("-daemon", 0); + isLogging = i2p::util::config::GetArg("-log", 1); - int port = i2p::util::config::GetArg("-port", 0); - if (port) - i2p::context.UpdatePort (port); - const char * host = i2p::util::config::GetCharArg("-host", ""); - if (host && host[0]) - i2p::context.UpdateAddress (boost::asio::ip::address::from_string (host)); + int port = i2p::util::config::GetArg("-port", 0); + if (port) + i2p::context.UpdatePort (port); + const char * host = i2p::util::config::GetCharArg("-host", ""); + if (host && host[0]) + i2p::context.UpdateAddress (boost::asio::ip::address::from_string (host)); - i2p::context.SetSupportsV6 (i2p::util::config::GetArg("-v6", 0)); - i2p::context.SetFloodfill (i2p::util::config::GetArg("-floodfill", 0)); - auto bandwidth = i2p::util::config::GetArg("-bandwidth", ""); - if (bandwidth.length () > 0) - { - if (bandwidth[0] > 'L') - i2p::context.SetHighBandwidth (); - else - i2p::context.SetLowBandwidth (); - } + i2p::context.SetSupportsV6 (i2p::util::config::GetArg("-v6", 0)); + i2p::context.SetFloodfill (i2p::util::config::GetArg("-floodfill", 0)); + auto bandwidth = i2p::util::config::GetArg("-bandwidth", ""); + if (bandwidth.length () > 0) + { + if (bandwidth[0] > 'L') + i2p::context.SetHighBandwidth (); + else + i2p::context.SetLowBandwidth (); + } - LogPrint("CMD parameters:"); - for (int i = 0; i < argc; ++i) - LogPrint(i, " ", argv[i]); + LogPrint("CMD parameters:"); + for (int i = 0; i < argc; ++i) + LogPrint(i, " ", argv[i]); - return true; - } - - bool Daemon_Singleton::start() - { - // initialize log - if (isLogging) - { - if (isDaemon) - { - std::string logfile_path = IsService () ? "/var/log" : i2p::util::filesystem::GetDataDir().string(); + return true; + } + + bool Daemon_Singleton::start() + { + // initialize log + if (isLogging) + { + if (isDaemon) + { + std::string logfile_path = IsService () ? "/var/log" : i2p::util::filesystem::GetDataDir().string(); #ifndef _WIN32 - logfile_path.append("/i2pd.log"); + logfile_path.append("/i2pd.log"); #else - logfile_path.append("\\i2pd.log"); + logfile_path.append("\\i2pd.log"); #endif - StartLog (logfile_path); - } - else - StartLog (""); // write to stdout - } + StartLog (logfile_path); + } + else + StartLog (""); // write to stdout + } - d.httpServer = new i2p::util::HTTPServer(i2p::util::config::GetArg("-httpport", 7070)); - d.httpServer->Start(); - LogPrint("HTTP Server started"); - i2p::data::netdb.Start(); - LogPrint("NetDB started"); - i2p::transport::transports.Start(); - LogPrint("Transports started"); - i2p::tunnel::tunnels.Start(); - LogPrint("Tunnels started"); - i2p::client::context.Start (); - LogPrint("Client started"); - return true; - } + d.httpServer = new i2p::util::HTTPServer(i2p::util::config::GetArg("-httpport", 7070)); + d.httpServer->Start(); + LogPrint("HTTP Server started"); + i2p::data::netdb.Start(); + LogPrint("NetDB started"); + i2p::transport::transports.Start(); + LogPrint("Transports started"); + i2p::tunnel::tunnels.Start(); + LogPrint("Tunnels started"); + i2p::client::context.Start (); + LogPrint("Client started"); + return true; + } - bool Daemon_Singleton::stop() - { - LogPrint("Shutdown started."); - i2p::client::context.Stop(); - LogPrint("Client stopped"); - i2p::tunnel::tunnels.Stop(); - LogPrint("Tunnels stopped"); - i2p::transport::transports.Stop(); - LogPrint("Transports stopped"); - i2p::data::netdb.Stop(); - LogPrint("NetDB stopped"); - d.httpServer->Stop(); - LogPrint("HTTP Server stopped"); + bool Daemon_Singleton::stop() + { + LogPrint("Shutdown started."); + i2p::client::context.Stop(); + LogPrint("Client stopped"); + i2p::tunnel::tunnels.Stop(); + LogPrint("Tunnels stopped"); + i2p::transport::transports.Stop(); + LogPrint("Transports stopped"); + i2p::data::netdb.Stop(); + LogPrint("NetDB stopped"); + d.httpServer->Stop(); + LogPrint("HTTP Server stopped"); - StopLog (); + StopLog (); - delete d.httpServer; d.httpServer = nullptr; + delete d.httpServer; d.httpServer = nullptr; - return true; - } - } + return true; + } + } } diff --git a/Daemon.h b/Daemon.h index 4d9b70fd..303ee2a2 100644 --- a/Daemon.h +++ b/Daemon.h @@ -9,65 +9,65 @@ namespace i2p { - namespace util - { - class Daemon_Singleton_Private; - class Daemon_Singleton - { - public: - virtual bool init(int argc, char* argv[]); - virtual bool start(); - virtual bool stop(); + namespace util + { + class Daemon_Singleton_Private; + class Daemon_Singleton + { + public: + virtual bool init(int argc, char* argv[]); + virtual bool start(); + virtual bool stop(); - int isLogging; - int isDaemon; - - int running; + int isLogging; + int isDaemon; + + int running; - protected: - Daemon_Singleton(); - virtual ~Daemon_Singleton(); + protected: + Daemon_Singleton(); + virtual ~Daemon_Singleton(); - bool IsService () const; + bool IsService () const; - // d-pointer for httpServer, httpProxy, etc. - class Daemon_Singleton_Private; - Daemon_Singleton_Private &d; - }; + // d-pointer for httpServer, httpProxy, etc. + class Daemon_Singleton_Private; + Daemon_Singleton_Private &d; + }; #ifdef _WIN32 - class DaemonWin32 : public Daemon_Singleton - { - public: - static DaemonWin32& Instance() - { - static DaemonWin32 instance; - return instance; - } + class DaemonWin32 : public Daemon_Singleton + { + public: + static DaemonWin32& Instance() + { + static DaemonWin32 instance; + return instance; + } - virtual bool init(int argc, char* argv[]); - virtual bool start(); - virtual bool stop(); - }; + virtual bool init(int argc, char* argv[]); + virtual bool start(); + virtual bool stop(); + }; #else - class DaemonLinux : public Daemon_Singleton - { - public: - DeamonLinux() = default; + class DaemonLinux : public Daemon_Singleton + { + public: + DaemonLinux() = default; - static DaemonLinux& Instance() - { - static DaemonLinux instance; - return instance; - } + static DaemonLinux& Instance() + { + static DaemonLinux instance; + return instance; + } - virtual bool start(); - virtual bool stop(); + virtual bool start(); + virtual bool stop(); private: std::string pidfile; int pidFilehandle; - }; + }; #endif - } + } } diff --git a/DaemonLinux.cpp b/DaemonLinux.cpp index ac7a3402..15e30e2f 100644 --- a/DaemonLinux.cpp +++ b/DaemonLinux.cpp @@ -14,106 +14,106 @@ void handle_signal(int sig) { - switch (sig) - { - case SIGHUP: - if (i2p::util::config::GetArg("daemon", 0) == 1) - { - static bool first=true; - if (first) - { - first=false; - return; - } - } - LogPrint("Reloading config."); - i2p::util::filesystem::ReadConfigFile(i2p::util::config::mapArgs, i2p::util::config::mapMultiArgs); - break; - case SIGABRT: - case SIGTERM: - case SIGINT: - Daemon.running = 0; // Exit loop - break; - } + switch (sig) + { + case SIGHUP: + if (i2p::util::config::GetArg("daemon", 0) == 1) + { + static bool first=true; + if (first) + { + first=false; + return; + } + } + LogPrint("Reloading config."); + i2p::util::filesystem::ReadConfigFile(i2p::util::config::mapArgs, i2p::util::config::mapMultiArgs); + break; + case SIGABRT: + case SIGTERM: + case SIGINT: + Daemon.running = 0; // Exit loop + break; + } } namespace i2p { - namespace util - { - bool DaemonLinux::start() - { - if (isDaemon == 1) - { - pid_t pid; - pid = fork(); - if (pid > 0) // parent - ::exit (EXIT_SUCCESS); + namespace util + { + bool DaemonLinux::start() + { + if (isDaemon == 1) + { + pid_t pid; + pid = fork(); + if (pid > 0) // parent + ::exit (EXIT_SUCCESS); - if (pid < 0) // error - return false; + if (pid < 0) // error + return false; - // child - umask(0); - int sid = setsid(); - if (sid < 0) - { - LogPrint("Error, could not create process group."); - return false; - } - std::string d(i2p::util::filesystem::GetDataDir().string ()); // make a copy - chdir(d.c_str()); + // child + umask(0); + int sid = setsid(); + if (sid < 0) + { + LogPrint("Error, could not create process group."); + return false; + } + std::string d(i2p::util::filesystem::GetDataDir().string ()); // make a copy + chdir(d.c_str()); - // close stdin/stdout/stderr descriptors - ::close (0); - ::open ("/dev/null", O_RDWR); - ::close (1); - ::open ("/dev/null", O_RDWR); - ::close (2); - ::open ("/dev/null", O_RDWR); - } + // close stdin/stdout/stderr descriptors + ::close (0); + ::open ("/dev/null", O_RDWR); + ::close (1); + ::open ("/dev/null", O_RDWR); + ::close (2); + ::open ("/dev/null", O_RDWR); + } - // Pidfile - pidfile = IsService () ? "/var/run" : i2p::util::filesystem::GetDataDir().string(); - pidfile.append("/i2pd.pid"); - pidFilehandle = open(pidfile.c_str(), O_RDWR | O_CREAT, 0600); - if (pidFilehandle == -1) - { - LogPrint("Error, could not create pid file (", pidfile, ")\nIs an instance already running?"); - return false; - } - if (lockf(pidFilehandle, F_TLOCK, 0) == -1) - { - LogPrint("Error, could not lock pid file (", pidfile, ")\nIs an instance already running?"); - return false; - } - char pid[10]; - sprintf(pid, "%d\n", getpid()); - write(pidFilehandle, pid, strlen(pid)); + // Pidfile + pidfile = IsService () ? "/var/run" : i2p::util::filesystem::GetDataDir().string(); + pidfile.append("/i2pd.pid"); + pidFilehandle = open(pidfile.c_str(), O_RDWR | O_CREAT, 0600); + if (pidFilehandle == -1) + { + LogPrint("Error, could not create pid file (", pidfile, ")\nIs an instance already running?"); + return false; + } + if (lockf(pidFilehandle, F_TLOCK, 0) == -1) + { + LogPrint("Error, could not lock pid file (", pidfile, ")\nIs an instance already running?"); + return false; + } + char pid[10]; + sprintf(pid, "%d\n", getpid()); + write(pidFilehandle, pid, strlen(pid)); - // Signal handler - struct sigaction sa; - sa.sa_handler = handle_signal; - sigemptyset(&sa.sa_mask); - sa.sa_flags = SA_RESTART; - sigaction(SIGHUP, &sa, 0); - sigaction(SIGABRT, &sa, 0); - sigaction(SIGTERM, &sa, 0); - sigaction(SIGINT, &sa, 0); + // Signal handler + struct sigaction sa; + sa.sa_handler = handle_signal; + sigemptyset(&sa.sa_mask); + sa.sa_flags = SA_RESTART; + sigaction(SIGHUP, &sa, 0); + sigaction(SIGABRT, &sa, 0); + sigaction(SIGTERM, &sa, 0); + sigaction(SIGINT, &sa, 0); - return Daemon_Singleton::start(); - } + return Daemon_Singleton::start(); + } - bool DaemonLinux::stop() - { - close(pidFilehandle); - unlink(pidfile.c_str()); + bool DaemonLinux::stop() + { + close(pidFilehandle); + unlink(pidfile.c_str()); - return Daemon_Singleton::stop(); - } + return Daemon_Singleton::stop(); + } - } + } } #endif diff --git a/DaemonWin32.cpp b/DaemonWin32.cpp index 96b28076..6d8139de 100644 --- a/DaemonWin32.cpp +++ b/DaemonWin32.cpp @@ -8,76 +8,76 @@ namespace i2p { - namespace util - { - bool DaemonWin32::init(int argc, char* argv[]) - { - setlocale(LC_CTYPE, ""); - SetConsoleCP(1251); - SetConsoleOutputCP(1251); - setlocale(LC_ALL, "Russian"); + namespace util + { + bool DaemonWin32::init(int argc, char* argv[]) + { + setlocale(LC_CTYPE, ""); + SetConsoleCP(1251); + SetConsoleOutputCP(1251); + setlocale(LC_ALL, "Russian"); - if (!Daemon_Singleton::init(argc, argv)) return false; - if (I2PService::isService()) - isDaemon = 1; - else - isDaemon = 0; + if (!Daemon_Singleton::init(argc, argv)) return false; + if (I2PService::isService()) + isDaemon = 1; + else + isDaemon = 0; - std::string serviceControl = i2p::util::config::GetArg("-service", "none"); - if (serviceControl == "install") - { - InstallService( - SERVICE_NAME, // Name of service - SERVICE_DISPLAY_NAME, // Name to display - SERVICE_START_TYPE, // Service start type - SERVICE_DEPENDENCIES, // Dependencies - SERVICE_ACCOUNT, // Service running account - SERVICE_PASSWORD // Password of the account - ); - exit(0); - } - else if (serviceControl == "remove") - { - UninstallService(SERVICE_NAME); - exit(0); - } - else if (serviceControl != "none") - { - printf(" --service=install to install the service.\n"); - printf(" --service=remove to remove the service.\n"); - } - - if (isDaemon == 1) - { - LogPrint("Service session"); - I2PService service(SERVICE_NAME); - if (!I2PService::Run(service)) - { - LogPrint("Service failed to run w/err 0x%08lx\n", GetLastError()); - exit(EXIT_FAILURE); - } - exit(EXIT_SUCCESS); - } - else - LogPrint("User session"); + std::string serviceControl = i2p::util::config::GetArg("-service", "none"); + if (serviceControl == "install") + { + InstallService( + SERVICE_NAME, // Name of service + SERVICE_DISPLAY_NAME, // Name to display + SERVICE_START_TYPE, // Service start type + SERVICE_DEPENDENCIES, // Dependencies + SERVICE_ACCOUNT, // Service running account + SERVICE_PASSWORD // Password of the account + ); + exit(0); + } + else if (serviceControl == "remove") + { + UninstallService(SERVICE_NAME); + exit(0); + } + else if (serviceControl != "none") + { + printf(" --service=install to install the service.\n"); + printf(" --service=remove to remove the service.\n"); + } + + if (isDaemon == 1) + { + LogPrint("Service session"); + I2PService service(SERVICE_NAME); + if (!I2PService::Run(service)) + { + LogPrint("Service failed to run w/err 0x%08lx\n", GetLastError()); + exit(EXIT_FAILURE); + } + exit(EXIT_SUCCESS); + } + else + LogPrint("User session"); - return true; - } - bool DaemonWin32::start() - { - setlocale(LC_CTYPE, ""); - SetConsoleCP(1251); - SetConsoleOutputCP(1251); - setlocale(LC_ALL, "Russian"); + return true; + } + bool DaemonWin32::start() + { + setlocale(LC_CTYPE, ""); + SetConsoleCP(1251); + SetConsoleOutputCP(1251); + setlocale(LC_ALL, "Russian"); - return Daemon_Singleton::start(); - } + return Daemon_Singleton::start(); + } - bool DaemonWin32::stop() - { - return Daemon_Singleton::stop(); - } - } + bool DaemonWin32::stop() + { + return Daemon_Singleton::stop(); + } + } } #endif \ No newline at end of file diff --git a/Datagram.cpp b/Datagram.cpp index 63b487ff..4b08e25c 100644 --- a/Datagram.cpp +++ b/Datagram.cpp @@ -12,140 +12,140 @@ namespace i2p { namespace datagram { - DatagramDestination::DatagramDestination (i2p::client::ClientDestination& owner): - m_Owner (owner), m_Receiver (nullptr) - { - } - - void DatagramDestination::SendDatagramTo (const uint8_t * payload, size_t len, const i2p::data::IdentHash& ident, uint16_t fromPort, uint16_t toPort) - { - uint8_t buf[MAX_DATAGRAM_SIZE]; - auto identityLen = m_Owner.GetIdentity ().ToBuffer (buf, MAX_DATAGRAM_SIZE); - uint8_t * signature = buf + identityLen; - auto signatureLen = m_Owner.GetIdentity ().GetSignatureLen (); - uint8_t * buf1 = signature + signatureLen; - size_t headerLen = identityLen + signatureLen; - - memcpy (buf1, payload, len); - if (m_Owner.GetIdentity ().GetSigningKeyType () == i2p::data::SIGNING_KEY_TYPE_DSA_SHA1) - { - uint8_t hash[32]; - CryptoPP::SHA256().CalculateDigest (hash, buf1, len); - m_Owner.Sign (hash, 32, signature); - } - else - m_Owner.Sign (buf1, len, signature); + DatagramDestination::DatagramDestination (i2p::client::ClientDestination& owner): + m_Owner (owner), m_Receiver (nullptr) + { + } + + void DatagramDestination::SendDatagramTo (const uint8_t * payload, size_t len, const i2p::data::IdentHash& ident, uint16_t fromPort, uint16_t toPort) + { + uint8_t buf[MAX_DATAGRAM_SIZE]; + auto identityLen = m_Owner.GetIdentity ().ToBuffer (buf, MAX_DATAGRAM_SIZE); + uint8_t * signature = buf + identityLen; + auto signatureLen = m_Owner.GetIdentity ().GetSignatureLen (); + uint8_t * buf1 = signature + signatureLen; + size_t headerLen = identityLen + signatureLen; + + memcpy (buf1, payload, len); + if (m_Owner.GetIdentity ().GetSigningKeyType () == i2p::data::SIGNING_KEY_TYPE_DSA_SHA1) + { + uint8_t hash[32]; + CryptoPP::SHA256().CalculateDigest (hash, buf1, len); + m_Owner.Sign (hash, 32, signature); + } + else + m_Owner.Sign (buf1, len, signature); - auto msg = CreateDataMessage (buf, len + headerLen, fromPort, toPort); - auto remote = m_Owner.FindLeaseSet (ident); - if (remote) - m_Owner.GetService ().post (std::bind (&DatagramDestination::SendMsg, this, msg, remote)); - else - m_Owner.RequestDestination (ident, std::bind (&DatagramDestination::HandleLeaseSetRequestComplete, this, std::placeholders::_1, msg)); - } + auto msg = CreateDataMessage (buf, len + headerLen, fromPort, toPort); + auto remote = m_Owner.FindLeaseSet (ident); + if (remote) + m_Owner.GetService ().post (std::bind (&DatagramDestination::SendMsg, this, msg, remote)); + else + m_Owner.RequestDestination (ident, std::bind (&DatagramDestination::HandleLeaseSetRequestComplete, this, std::placeholders::_1, msg)); + } - void DatagramDestination::HandleLeaseSetRequestComplete (std::shared_ptr remote, I2NPMessage * msg) - { - if (remote) - SendMsg (msg, remote); - else - DeleteI2NPMessage (msg); - } - - void DatagramDestination::SendMsg (I2NPMessage * msg, std::shared_ptr remote) - { - auto outboundTunnel = m_Owner.GetTunnelPool ()->GetNextOutboundTunnel (); - auto leases = remote->GetNonExpiredLeases (); - if (!leases.empty () && outboundTunnel) - { - std::vector msgs; - uint32_t i = i2p::context.GetRandomNumberGenerator ().GenerateWord32 (0, leases.size () - 1); - auto garlic = m_Owner.WrapMessage (remote, ToSharedI2NPMessage (msg), true); - msgs.push_back (i2p::tunnel::TunnelMessageBlock - { - i2p::tunnel::eDeliveryTypeTunnel, - leases[i].tunnelGateway, leases[i].tunnelID, - garlic - }); - outboundTunnel->SendTunnelDataMsg (msgs); - } - else - { - if (outboundTunnel) - LogPrint (eLogWarning, "Failed to send datagram. All leases expired"); - else - LogPrint (eLogWarning, "Failed to send datagram. No outbound tunnels"); - DeleteI2NPMessage (msg); - } - } + void DatagramDestination::HandleLeaseSetRequestComplete (std::shared_ptr remote, I2NPMessage * msg) + { + if (remote) + SendMsg (msg, remote); + else + DeleteI2NPMessage (msg); + } + + void DatagramDestination::SendMsg (I2NPMessage * msg, std::shared_ptr remote) + { + auto outboundTunnel = m_Owner.GetTunnelPool ()->GetNextOutboundTunnel (); + auto leases = remote->GetNonExpiredLeases (); + if (!leases.empty () && outboundTunnel) + { + std::vector msgs; + uint32_t i = i2p::context.GetRandomNumberGenerator ().GenerateWord32 (0, leases.size () - 1); + auto garlic = m_Owner.WrapMessage (remote, ToSharedI2NPMessage (msg), true); + msgs.push_back (i2p::tunnel::TunnelMessageBlock + { + i2p::tunnel::eDeliveryTypeTunnel, + leases[i].tunnelGateway, leases[i].tunnelID, + garlic + }); + outboundTunnel->SendTunnelDataMsg (msgs); + } + else + { + if (outboundTunnel) + LogPrint (eLogWarning, "Failed to send datagram. All leases expired"); + else + LogPrint (eLogWarning, "Failed to send datagram. No outbound tunnels"); + DeleteI2NPMessage (msg); + } + } - void DatagramDestination::HandleDatagram (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len) - { - i2p::data::IdentityEx identity; - size_t identityLen = identity.FromBuffer (buf, len); - const uint8_t * signature = buf + identityLen; - size_t headerLen = identityLen + identity.GetSignatureLen (); + void DatagramDestination::HandleDatagram (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len) + { + i2p::data::IdentityEx identity; + size_t identityLen = identity.FromBuffer (buf, len); + const uint8_t * signature = buf + identityLen; + size_t headerLen = identityLen + identity.GetSignatureLen (); - bool verified = false; - if (identity.GetSigningKeyType () == i2p::data::SIGNING_KEY_TYPE_DSA_SHA1) - { - uint8_t hash[32]; - CryptoPP::SHA256().CalculateDigest (hash, buf + headerLen, len - headerLen); - verified = identity.Verify (hash, 32, signature); - } - else - verified = identity.Verify (buf + headerLen, len - headerLen, signature); - - if (verified) - { - auto it = m_ReceiversByPorts.find (toPort); - if (it != m_ReceiversByPorts.end ()) - it->second (identity, fromPort, toPort, buf + headerLen, len -headerLen); - else if (m_Receiver != nullptr) - m_Receiver (identity, fromPort, toPort, buf + headerLen, len -headerLen); - else - LogPrint (eLogWarning, "Receiver for datagram is not set"); - } - else - LogPrint (eLogWarning, "Datagram signature verification failed"); - } + bool verified = false; + if (identity.GetSigningKeyType () == i2p::data::SIGNING_KEY_TYPE_DSA_SHA1) + { + uint8_t hash[32]; + CryptoPP::SHA256().CalculateDigest (hash, buf + headerLen, len - headerLen); + verified = identity.Verify (hash, 32, signature); + } + else + verified = identity.Verify (buf + headerLen, len - headerLen, signature); + + if (verified) + { + auto it = m_ReceiversByPorts.find (toPort); + if (it != m_ReceiversByPorts.end ()) + it->second (identity, fromPort, toPort, buf + headerLen, len -headerLen); + else if (m_Receiver != nullptr) + m_Receiver (identity, fromPort, toPort, buf + headerLen, len -headerLen); + else + LogPrint (eLogWarning, "Receiver for datagram is not set"); + } + else + LogPrint (eLogWarning, "Datagram signature verification failed"); + } - void DatagramDestination::HandleDataMessagePayload (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len) - { - // unzip it - CryptoPP::Gunzip decompressor; - decompressor.Put (buf, len); - decompressor.MessageEnd(); - uint8_t uncompressed[MAX_DATAGRAM_SIZE]; - auto uncompressedLen = decompressor.MaxRetrievable (); - if (uncompressedLen <= MAX_DATAGRAM_SIZE) - { - decompressor.Get (uncompressed, uncompressedLen); - HandleDatagram (fromPort, toPort, uncompressed, uncompressedLen); - } - else - LogPrint ("Received datagram size ", uncompressedLen, " exceeds max size"); + void DatagramDestination::HandleDataMessagePayload (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len) + { + // unzip it + CryptoPP::Gunzip decompressor; + decompressor.Put (buf, len); + decompressor.MessageEnd(); + uint8_t uncompressed[MAX_DATAGRAM_SIZE]; + auto uncompressedLen = decompressor.MaxRetrievable (); + if (uncompressedLen <= MAX_DATAGRAM_SIZE) + { + decompressor.Get (uncompressed, uncompressedLen); + HandleDatagram (fromPort, toPort, uncompressed, uncompressedLen); + } + else + LogPrint ("Received datagram size ", uncompressedLen, " exceeds max size"); - } + } - I2NPMessage * DatagramDestination::CreateDataMessage (const uint8_t * payload, size_t len, uint16_t fromPort, uint16_t toPort) - { - I2NPMessage * msg = NewI2NPMessage (); - CryptoPP::Gzip compressor; // default level - compressor.Put (payload, len); - compressor.MessageEnd(); - int size = compressor.MaxRetrievable (); - uint8_t * buf = msg->GetPayload (); - htobe32buf (buf, size); // length - buf += 4; - compressor.Get (buf, size); - htobe16buf (buf + 4, fromPort); // source port - htobe16buf (buf + 6, toPort); // destination port - buf[9] = i2p::client::PROTOCOL_TYPE_DATAGRAM; // datagram protocol - msg->len += size + 4; - msg->FillI2NPMessageHeader (eI2NPData); - return msg; - } + I2NPMessage * DatagramDestination::CreateDataMessage (const uint8_t * payload, size_t len, uint16_t fromPort, uint16_t toPort) + { + I2NPMessage * msg = NewI2NPMessage (); + CryptoPP::Gzip compressor; // default level + compressor.Put (payload, len); + compressor.MessageEnd(); + int size = compressor.MaxRetrievable (); + uint8_t * buf = msg->GetPayload (); + htobe32buf (buf, size); // length + buf += 4; + compressor.Get (buf, size); + htobe16buf (buf + 4, fromPort); // source port + htobe16buf (buf + 6, toPort); // destination port + buf[9] = i2p::client::PROTOCOL_TYPE_DATAGRAM; // datagram protocol + msg->len += size + 4; + msg->FillI2NPMessageHeader (eI2NPData); + return msg; + } } } diff --git a/Datagram.h b/Datagram.h index f2e31304..a6dbb52d 100644 --- a/Datagram.h +++ b/Datagram.h @@ -13,43 +13,43 @@ namespace i2p { namespace client { - class ClientDestination; + class ClientDestination; } namespace datagram { - const size_t MAX_DATAGRAM_SIZE = 32768; - class DatagramDestination - { - typedef std::function Receiver; + const size_t MAX_DATAGRAM_SIZE = 32768; + class DatagramDestination + { + typedef std::function Receiver; - public: + public: - DatagramDestination (i2p::client::ClientDestination& owner); - ~DatagramDestination () {}; + DatagramDestination (i2p::client::ClientDestination& owner); + ~DatagramDestination () {}; - void SendDatagramTo (const uint8_t * payload, size_t len, const i2p::data::IdentHash& ident, uint16_t fromPort = 0, uint16_t toPort = 0); - void HandleDataMessagePayload (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len); + void SendDatagramTo (const uint8_t * payload, size_t len, const i2p::data::IdentHash& ident, uint16_t fromPort = 0, uint16_t toPort = 0); + void HandleDataMessagePayload (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len); - void SetReceiver (const Receiver& receiver) { m_Receiver = receiver; }; - void ResetReceiver () { m_Receiver = nullptr; }; + void SetReceiver (const Receiver& receiver) { m_Receiver = receiver; }; + void ResetReceiver () { m_Receiver = nullptr; }; - void SetReceiver (const Receiver& receiver, uint16_t port) { m_ReceiversByPorts[port] = receiver; }; - void ResetReceiver (uint16_t port) { m_ReceiversByPorts.erase (port); }; + void SetReceiver (const Receiver& receiver, uint16_t port) { m_ReceiversByPorts[port] = receiver; }; + void ResetReceiver (uint16_t port) { m_ReceiversByPorts.erase (port); }; - private: + private: - void HandleLeaseSetRequestComplete (std::shared_ptr leaseSet, I2NPMessage * msg); - - I2NPMessage * CreateDataMessage (const uint8_t * payload, size_t len, uint16_t fromPort, uint16_t toPort); - void SendMsg (I2NPMessage * msg, std::shared_ptr remote); - void HandleDatagram (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len); + void HandleLeaseSetRequestComplete (std::shared_ptr leaseSet, I2NPMessage * msg); + + I2NPMessage * CreateDataMessage (const uint8_t * payload, size_t len, uint16_t fromPort, uint16_t toPort); + void SendMsg (I2NPMessage * msg, std::shared_ptr remote); + void HandleDatagram (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len); - private: + private: - i2p::client::ClientDestination& m_Owner; - Receiver m_Receiver; // default - std::map m_ReceiversByPorts; - }; + i2p::client::ClientDestination& m_Owner; + Receiver m_Receiver; // default + std::map m_ReceiversByPorts; + }; } } diff --git a/Destination.cpp b/Destination.cpp index c7ceb86a..aa88a3ea 100644 --- a/Destination.cpp +++ b/Destination.cpp @@ -13,655 +13,655 @@ namespace i2p { namespace client { - ClientDestination::ClientDestination (const i2p::data::PrivateKeys& keys, bool isPublic, - const std::map * params): - m_IsRunning (false), m_Thread (nullptr), m_Work (m_Service), - m_Keys (keys), m_IsPublic (isPublic), m_PublishReplyToken (0), - m_DatagramDestination (nullptr), m_PublishConfirmationTimer (m_Service), m_CleanupTimer (m_Service) - { - i2p::crypto::GenerateElGamalKeyPair(i2p::context.GetRandomNumberGenerator (), m_EncryptionPrivateKey, m_EncryptionPublicKey); - int inboundTunnelLen = DEFAULT_INBOUND_TUNNEL_LENGTH; - int outboundTunnelLen = DEFAULT_OUTBOUND_TUNNEL_LENGTH; - int inboundTunnelsQuantity = DEFAULT_INBOUND_TUNNELS_QUANTITY; - int outboundTunnelsQuantity = DEFAULT_OUTBOUND_TUNNELS_QUANTITY; - std::shared_ptr > explicitPeers; - if (params) - { - auto it = params->find (I2CP_PARAM_INBOUND_TUNNEL_LENGTH); - if (it != params->end ()) - { - int len = boost::lexical_cast(it->second); - if (len > 0) - { - inboundTunnelLen = len; - LogPrint (eLogInfo, "Inbound tunnel length set to ", len); - } - } - it = params->find (I2CP_PARAM_OUTBOUND_TUNNEL_LENGTH); - if (it != params->end ()) - { - int len = boost::lexical_cast(it->second); - if (len > 0) - { - outboundTunnelLen = len; - LogPrint (eLogInfo, "Outbound tunnel length set to ", len); - } - } - it = params->find (I2CP_PARAM_INBOUND_TUNNELS_QUANTITY); - if (it != params->end ()) - { - int quantity = boost::lexical_cast(it->second); - if (quantity > 0) - { - inboundTunnelsQuantity = quantity; - LogPrint (eLogInfo, "Inbound tunnels quantity set to ", quantity); - } - } - it = params->find (I2CP_PARAM_OUTBOUND_TUNNELS_QUANTITY); - if (it != params->end ()) - { - int quantity = boost::lexical_cast(it->second); - if (quantity > 0) - { - outboundTunnelsQuantity = quantity; - LogPrint (eLogInfo, "Outbound tunnels quantity set to ", quantity); - } - } - it = params->find (I2CP_PARAM_EXPLICIT_PEERS); - if (it != params->end ()) - { - explicitPeers = std::make_shared >(); - std::stringstream ss(it->second); - std::string b64; - while (std::getline (ss, b64, ',')) - { - i2p::data::IdentHash ident; - ident.FromBase64 (b64); - explicitPeers->push_back (ident); - } - LogPrint (eLogInfo, "Explicit peers set to ", it->second); - } - } - m_Pool = i2p::tunnel::tunnels.CreateTunnelPool (this, inboundTunnelLen, outboundTunnelLen, inboundTunnelsQuantity, outboundTunnelsQuantity); - if (explicitPeers) - m_Pool->SetExplicitPeers (explicitPeers); - if (m_IsPublic) - LogPrint (eLogInfo, "Local address ", i2p::client::GetB32Address(GetIdentHash()), " created"); - m_StreamingDestination = std::make_shared (*this); // TODO: - } + ClientDestination::ClientDestination (const i2p::data::PrivateKeys& keys, bool isPublic, + const std::map * params): + m_IsRunning (false), m_Thread (nullptr), m_Work (m_Service), + m_Keys (keys), m_IsPublic (isPublic), m_PublishReplyToken (0), + m_DatagramDestination (nullptr), m_PublishConfirmationTimer (m_Service), m_CleanupTimer (m_Service) + { + i2p::crypto::GenerateElGamalKeyPair(i2p::context.GetRandomNumberGenerator (), m_EncryptionPrivateKey, m_EncryptionPublicKey); + int inboundTunnelLen = DEFAULT_INBOUND_TUNNEL_LENGTH; + int outboundTunnelLen = DEFAULT_OUTBOUND_TUNNEL_LENGTH; + int inboundTunnelsQuantity = DEFAULT_INBOUND_TUNNELS_QUANTITY; + int outboundTunnelsQuantity = DEFAULT_OUTBOUND_TUNNELS_QUANTITY; + std::shared_ptr > explicitPeers; + if (params) + { + auto it = params->find (I2CP_PARAM_INBOUND_TUNNEL_LENGTH); + if (it != params->end ()) + { + int len = boost::lexical_cast(it->second); + if (len > 0) + { + inboundTunnelLen = len; + LogPrint (eLogInfo, "Inbound tunnel length set to ", len); + } + } + it = params->find (I2CP_PARAM_OUTBOUND_TUNNEL_LENGTH); + if (it != params->end ()) + { + int len = boost::lexical_cast(it->second); + if (len > 0) + { + outboundTunnelLen = len; + LogPrint (eLogInfo, "Outbound tunnel length set to ", len); + } + } + it = params->find (I2CP_PARAM_INBOUND_TUNNELS_QUANTITY); + if (it != params->end ()) + { + int quantity = boost::lexical_cast(it->second); + if (quantity > 0) + { + inboundTunnelsQuantity = quantity; + LogPrint (eLogInfo, "Inbound tunnels quantity set to ", quantity); + } + } + it = params->find (I2CP_PARAM_OUTBOUND_TUNNELS_QUANTITY); + if (it != params->end ()) + { + int quantity = boost::lexical_cast(it->second); + if (quantity > 0) + { + outboundTunnelsQuantity = quantity; + LogPrint (eLogInfo, "Outbound tunnels quantity set to ", quantity); + } + } + it = params->find (I2CP_PARAM_EXPLICIT_PEERS); + if (it != params->end ()) + { + explicitPeers = std::make_shared >(); + std::stringstream ss(it->second); + std::string b64; + while (std::getline (ss, b64, ',')) + { + i2p::data::IdentHash ident; + ident.FromBase64 (b64); + explicitPeers->push_back (ident); + } + LogPrint (eLogInfo, "Explicit peers set to ", it->second); + } + } + m_Pool = i2p::tunnel::tunnels.CreateTunnelPool (this, inboundTunnelLen, outboundTunnelLen, inboundTunnelsQuantity, outboundTunnelsQuantity); + if (explicitPeers) + m_Pool->SetExplicitPeers (explicitPeers); + if (m_IsPublic) + LogPrint (eLogInfo, "Local address ", i2p::client::GetB32Address(GetIdentHash()), " created"); + m_StreamingDestination = std::make_shared (*this); // TODO: + } - ClientDestination::~ClientDestination () - { - if (m_IsRunning) - Stop (); - for (auto it: m_LeaseSetRequests) - delete it.second; - if (m_Pool) - i2p::tunnel::tunnels.DeleteTunnelPool (m_Pool); - if (m_DatagramDestination) - delete m_DatagramDestination; - } + ClientDestination::~ClientDestination () + { + if (m_IsRunning) + Stop (); + for (auto it: m_LeaseSetRequests) + delete it.second; + if (m_Pool) + i2p::tunnel::tunnels.DeleteTunnelPool (m_Pool); + if (m_DatagramDestination) + delete m_DatagramDestination; + } - void ClientDestination::Run () - { - while (m_IsRunning) - { - try - { - m_Service.run (); - } - catch (std::exception& ex) - { - LogPrint ("Destination: ", ex.what ()); - } - } - } + void ClientDestination::Run () + { + while (m_IsRunning) + { + try + { + m_Service.run (); + } + catch (std::exception& ex) + { + LogPrint ("Destination: ", ex.what ()); + } + } + } - void ClientDestination::Start () - { - if (!m_IsRunning) - { - m_IsRunning = true; - m_Pool->SetLocalDestination (this); - m_Pool->SetActive (true); - m_Thread = new std::thread (std::bind (&ClientDestination::Run, this)); - m_StreamingDestination->Start (); - for (auto it: m_StreamingDestinationsByPorts) - it.second->Start (); - - m_CleanupTimer.expires_from_now (boost::posix_time::minutes (DESTINATION_CLEANUP_TIMEOUT)); - m_CleanupTimer.async_wait (std::bind (&ClientDestination::HandleCleanupTimer, - this, std::placeholders::_1)); - } - } - - void ClientDestination::Stop () - { - if (m_IsRunning) - { - m_CleanupTimer.cancel (); - m_IsRunning = false; - m_StreamingDestination->Stop (); - for (auto it: m_StreamingDestinationsByPorts) - it.second->Stop (); - if (m_DatagramDestination) - { - auto d = m_DatagramDestination; - m_DatagramDestination = nullptr; - delete d; - } - if (m_Pool) - { - m_Pool->SetLocalDestination (nullptr); - i2p::tunnel::tunnels.StopTunnelPool (m_Pool); - } - m_Service.stop (); - if (m_Thread) - { - m_Thread->join (); - delete m_Thread; - m_Thread = 0; - } - } - } + void ClientDestination::Start () + { + if (!m_IsRunning) + { + m_IsRunning = true; + m_Pool->SetLocalDestination (this); + m_Pool->SetActive (true); + m_Thread = new std::thread (std::bind (&ClientDestination::Run, this)); + m_StreamingDestination->Start (); + for (auto it: m_StreamingDestinationsByPorts) + it.second->Start (); + + m_CleanupTimer.expires_from_now (boost::posix_time::minutes (DESTINATION_CLEANUP_TIMEOUT)); + m_CleanupTimer.async_wait (std::bind (&ClientDestination::HandleCleanupTimer, + this, std::placeholders::_1)); + } + } + + void ClientDestination::Stop () + { + if (m_IsRunning) + { + m_CleanupTimer.cancel (); + m_IsRunning = false; + m_StreamingDestination->Stop (); + for (auto it: m_StreamingDestinationsByPorts) + it.second->Stop (); + if (m_DatagramDestination) + { + auto d = m_DatagramDestination; + m_DatagramDestination = nullptr; + delete d; + } + if (m_Pool) + { + m_Pool->SetLocalDestination (nullptr); + i2p::tunnel::tunnels.StopTunnelPool (m_Pool); + } + m_Service.stop (); + if (m_Thread) + { + m_Thread->join (); + delete m_Thread; + m_Thread = 0; + } + } + } - std::shared_ptr ClientDestination::FindLeaseSet (const i2p::data::IdentHash& ident) - { - auto it = m_RemoteLeaseSets.find (ident); - if (it != m_RemoteLeaseSets.end ()) - { - if (it->second->HasNonExpiredLeases ()) - return it->second; - else - LogPrint ("All leases of remote LeaseSet expired"); - } - else - { - auto ls = i2p::data::netdb.FindLeaseSet (ident); - if (ls) - { - m_RemoteLeaseSets[ident] = ls; - return ls; - } - } - return nullptr; - } + std::shared_ptr ClientDestination::FindLeaseSet (const i2p::data::IdentHash& ident) + { + auto it = m_RemoteLeaseSets.find (ident); + if (it != m_RemoteLeaseSets.end ()) + { + if (it->second->HasNonExpiredLeases ()) + return it->second; + else + LogPrint ("All leases of remote LeaseSet expired"); + } + else + { + auto ls = i2p::data::netdb.FindLeaseSet (ident); + if (ls) + { + m_RemoteLeaseSets[ident] = ls; + return ls; + } + } + return nullptr; + } - std::shared_ptr ClientDestination::GetLeaseSet () - { - if (!m_Pool) return nullptr; - if (!m_LeaseSet) - UpdateLeaseSet (); - return m_LeaseSet; - } + std::shared_ptr ClientDestination::GetLeaseSet () + { + if (!m_Pool) return nullptr; + if (!m_LeaseSet) + UpdateLeaseSet (); + return m_LeaseSet; + } - void ClientDestination::UpdateLeaseSet () - { - m_LeaseSet.reset (new i2p::data::LeaseSet (*m_Pool)); - } + void ClientDestination::UpdateLeaseSet () + { + m_LeaseSet.reset (new i2p::data::LeaseSet (*m_Pool)); + } - bool ClientDestination::SubmitSessionKey (const uint8_t * key, const uint8_t * tag) - { - struct - { - uint8_t k[32], t[32]; - } data; - memcpy (data.k, key, 32); - memcpy (data.t, tag, 32); - m_Service.post ([this,data](void) - { - this->AddSessionKey (data.k, data.t); - }); - return true; - } + bool ClientDestination::SubmitSessionKey (const uint8_t * key, const uint8_t * tag) + { + struct + { + uint8_t k[32], t[32]; + } data; + memcpy (data.k, key, 32); + memcpy (data.t, tag, 32); + m_Service.post ([this,data](void) + { + this->AddSessionKey (data.k, data.t); + }); + return true; + } - void ClientDestination::ProcessGarlicMessage (std::shared_ptr msg) - { - m_Service.post (std::bind (&ClientDestination::HandleGarlicMessage, this, msg)); - } + void ClientDestination::ProcessGarlicMessage (std::shared_ptr msg) + { + m_Service.post (std::bind (&ClientDestination::HandleGarlicMessage, this, msg)); + } - void ClientDestination::ProcessDeliveryStatusMessage (std::shared_ptr msg) - { - m_Service.post (std::bind (&ClientDestination::HandleDeliveryStatusMessage, this, msg)); - } + void ClientDestination::ProcessDeliveryStatusMessage (std::shared_ptr msg) + { + m_Service.post (std::bind (&ClientDestination::HandleDeliveryStatusMessage, this, msg)); + } - void ClientDestination::HandleI2NPMessage (const uint8_t * buf, size_t len, std::shared_ptr from) - { - uint8_t typeID = buf[I2NP_HEADER_TYPEID_OFFSET]; - switch (typeID) - { - case eI2NPData: - HandleDataMessage (buf + I2NP_HEADER_SIZE, bufbe16toh (buf + I2NP_HEADER_SIZE_OFFSET)); - break; - case eI2NPDeliveryStatus: - // we assume tunnel tests non-encrypted - HandleDeliveryStatusMessage (CreateI2NPMessage (buf, GetI2NPMessageLength (buf), from)); - break; - case eI2NPDatabaseStore: - HandleDatabaseStoreMessage (buf + I2NP_HEADER_SIZE, bufbe16toh (buf + I2NP_HEADER_SIZE_OFFSET)); - break; - case eI2NPDatabaseSearchReply: - HandleDatabaseSearchReplyMessage (buf + I2NP_HEADER_SIZE, bufbe16toh (buf + I2NP_HEADER_SIZE_OFFSET)); - break; - default: - i2p::HandleI2NPMessage (CreateI2NPMessage (buf, GetI2NPMessageLength (buf), from)); - } - } + void ClientDestination::HandleI2NPMessage (const uint8_t * buf, size_t len, std::shared_ptr from) + { + uint8_t typeID = buf[I2NP_HEADER_TYPEID_OFFSET]; + switch (typeID) + { + case eI2NPData: + HandleDataMessage (buf + I2NP_HEADER_SIZE, bufbe16toh (buf + I2NP_HEADER_SIZE_OFFSET)); + break; + case eI2NPDeliveryStatus: + // we assume tunnel tests non-encrypted + HandleDeliveryStatusMessage (CreateI2NPMessage (buf, GetI2NPMessageLength (buf), from)); + break; + case eI2NPDatabaseStore: + HandleDatabaseStoreMessage (buf + I2NP_HEADER_SIZE, bufbe16toh (buf + I2NP_HEADER_SIZE_OFFSET)); + break; + case eI2NPDatabaseSearchReply: + HandleDatabaseSearchReplyMessage (buf + I2NP_HEADER_SIZE, bufbe16toh (buf + I2NP_HEADER_SIZE_OFFSET)); + break; + default: + i2p::HandleI2NPMessage (CreateI2NPMessage (buf, GetI2NPMessageLength (buf), from)); + } + } - void ClientDestination::HandleDatabaseStoreMessage (const uint8_t * buf, size_t len) - { - uint32_t replyToken = bufbe32toh (buf + DATABASE_STORE_REPLY_TOKEN_OFFSET); - size_t offset = DATABASE_STORE_HEADER_SIZE; - if (replyToken) - { - LogPrint (eLogInfo, "Reply token is ignored for DatabaseStore"); - offset += 36; - } - std::shared_ptr leaseSet; - if (buf[DATABASE_STORE_TYPE_OFFSET] == 1) // LeaseSet - { - LogPrint (eLogDebug, "Remote LeaseSet"); - auto it = m_RemoteLeaseSets.find (buf + DATABASE_STORE_KEY_OFFSET); - if (it != m_RemoteLeaseSets.end ()) - { - leaseSet = it->second; - leaseSet->Update (buf + offset, len - offset); - if (leaseSet->IsValid ()) - LogPrint (eLogDebug, "Remote LeaseSet updated"); - else - { - LogPrint (eLogDebug, "Remote LeaseSet update failed"); - m_RemoteLeaseSets.erase (it); - leaseSet = nullptr; - } - } - else - { - leaseSet = std::make_shared (buf + offset, len - offset); - if (leaseSet->IsValid ()) - { - LogPrint (eLogDebug, "New remote LeaseSet added"); - m_RemoteLeaseSets[buf + DATABASE_STORE_KEY_OFFSET] = leaseSet; - } - else - { - LogPrint (eLogError, "New remote LeaseSet verification failed"); - leaseSet = nullptr; - } - } - } - else - LogPrint (eLogError, "Unexpected client's DatabaseStore type ", buf[DATABASE_STORE_TYPE_OFFSET], ". Dropped"); - - auto it1 = m_LeaseSetRequests.find (buf + DATABASE_STORE_KEY_OFFSET); - if (it1 != m_LeaseSetRequests.end ()) - { - it1->second->requestTimeoutTimer.cancel (); - if (it1->second->requestComplete) it1->second->requestComplete (leaseSet); - delete it1->second; - m_LeaseSetRequests.erase (it1); - } - } + void ClientDestination::HandleDatabaseStoreMessage (const uint8_t * buf, size_t len) + { + uint32_t replyToken = bufbe32toh (buf + DATABASE_STORE_REPLY_TOKEN_OFFSET); + size_t offset = DATABASE_STORE_HEADER_SIZE; + if (replyToken) + { + LogPrint (eLogInfo, "Reply token is ignored for DatabaseStore"); + offset += 36; + } + std::shared_ptr leaseSet; + if (buf[DATABASE_STORE_TYPE_OFFSET] == 1) // LeaseSet + { + LogPrint (eLogDebug, "Remote LeaseSet"); + auto it = m_RemoteLeaseSets.find (buf + DATABASE_STORE_KEY_OFFSET); + if (it != m_RemoteLeaseSets.end ()) + { + leaseSet = it->second; + leaseSet->Update (buf + offset, len - offset); + if (leaseSet->IsValid ()) + LogPrint (eLogDebug, "Remote LeaseSet updated"); + else + { + LogPrint (eLogDebug, "Remote LeaseSet update failed"); + m_RemoteLeaseSets.erase (it); + leaseSet = nullptr; + } + } + else + { + leaseSet = std::make_shared (buf + offset, len - offset); + if (leaseSet->IsValid ()) + { + LogPrint (eLogDebug, "New remote LeaseSet added"); + m_RemoteLeaseSets[buf + DATABASE_STORE_KEY_OFFSET] = leaseSet; + } + else + { + LogPrint (eLogError, "New remote LeaseSet verification failed"); + leaseSet = nullptr; + } + } + } + else + LogPrint (eLogError, "Unexpected client's DatabaseStore type ", buf[DATABASE_STORE_TYPE_OFFSET], ". Dropped"); + + auto it1 = m_LeaseSetRequests.find (buf + DATABASE_STORE_KEY_OFFSET); + if (it1 != m_LeaseSetRequests.end ()) + { + it1->second->requestTimeoutTimer.cancel (); + if (it1->second->requestComplete) it1->second->requestComplete (leaseSet); + delete it1->second; + m_LeaseSetRequests.erase (it1); + } + } - void ClientDestination::HandleDatabaseSearchReplyMessage (const uint8_t * buf, size_t len) - { - i2p::data::IdentHash key (buf); - int num = buf[32]; // num - LogPrint ("DatabaseSearchReply for ", key.ToBase64 (), " num=", num); - auto it = m_LeaseSetRequests.find (key); - if (it != m_LeaseSetRequests.end ()) - { - LeaseSetRequest * request = it->second; - bool found = false; - if (request->excluded.size () < MAX_NUM_FLOODFILLS_PER_REQUEST) - { - for (int i = 0; i < num; i++) - { - i2p::data::IdentHash peerHash (buf + 33 + i*32); - auto floodfill = i2p::data::netdb.FindRouter (peerHash); - if (floodfill) - { - LogPrint (eLogInfo, "Requesting ", key.ToBase64 (), " at ", peerHash.ToBase64 ()); - if (SendLeaseSetRequest (key, floodfill, request)) - found = true; - } - else - { - LogPrint (eLogInfo, "Found new floodfill. Request it"); - i2p::data::netdb.RequestDestination (peerHash); - } - } - if (!found) - LogPrint (eLogError, "Suggested floodfills are not presented in netDb"); - } - else - LogPrint (eLogInfo, key.ToBase64 (), " was not found on ", MAX_NUM_FLOODFILLS_PER_REQUEST," floodfills"); - if (!found) - { - if (request->requestComplete) request->requestComplete (nullptr); - delete request; - m_LeaseSetRequests.erase (key); - } - } - else - LogPrint ("Request for ", key.ToBase64 (), " not found"); - } - - void ClientDestination::HandleDeliveryStatusMessage (std::shared_ptr msg) - { - uint32_t msgID = bufbe32toh (msg->GetPayload () + DELIVERY_STATUS_MSGID_OFFSET); - if (msgID == m_PublishReplyToken) - { - LogPrint (eLogDebug, "Publishing confirmed"); - m_ExcludedFloodfills.clear (); - m_PublishReplyToken = 0; - } - else - i2p::garlic::GarlicDestination::HandleDeliveryStatusMessage (msg); - } + void ClientDestination::HandleDatabaseSearchReplyMessage (const uint8_t * buf, size_t len) + { + i2p::data::IdentHash key (buf); + int num = buf[32]; // num + LogPrint ("DatabaseSearchReply for ", key.ToBase64 (), " num=", num); + auto it = m_LeaseSetRequests.find (key); + if (it != m_LeaseSetRequests.end ()) + { + LeaseSetRequest * request = it->second; + bool found = false; + if (request->excluded.size () < MAX_NUM_FLOODFILLS_PER_REQUEST) + { + for (int i = 0; i < num; i++) + { + i2p::data::IdentHash peerHash (buf + 33 + i*32); + auto floodfill = i2p::data::netdb.FindRouter (peerHash); + if (floodfill) + { + LogPrint (eLogInfo, "Requesting ", key.ToBase64 (), " at ", peerHash.ToBase64 ()); + if (SendLeaseSetRequest (key, floodfill, request)) + found = true; + } + else + { + LogPrint (eLogInfo, "Found new floodfill. Request it"); + i2p::data::netdb.RequestDestination (peerHash); + } + } + if (!found) + LogPrint (eLogError, "Suggested floodfills are not presented in netDb"); + } + else + LogPrint (eLogInfo, key.ToBase64 (), " was not found on ", MAX_NUM_FLOODFILLS_PER_REQUEST," floodfills"); + if (!found) + { + if (request->requestComplete) request->requestComplete (nullptr); + delete request; + m_LeaseSetRequests.erase (key); + } + } + else + LogPrint ("Request for ", key.ToBase64 (), " not found"); + } + + void ClientDestination::HandleDeliveryStatusMessage (std::shared_ptr msg) + { + uint32_t msgID = bufbe32toh (msg->GetPayload () + DELIVERY_STATUS_MSGID_OFFSET); + if (msgID == m_PublishReplyToken) + { + LogPrint (eLogDebug, "Publishing confirmed"); + m_ExcludedFloodfills.clear (); + m_PublishReplyToken = 0; + } + else + i2p::garlic::GarlicDestination::HandleDeliveryStatusMessage (msg); + } - void ClientDestination::SetLeaseSetUpdated () - { - i2p::garlic::GarlicDestination::SetLeaseSetUpdated (); - UpdateLeaseSet (); - if (m_IsPublic) - Publish (); - } - - void ClientDestination::Publish () - { - if (!m_LeaseSet || !m_Pool) - { - LogPrint (eLogError, "Can't publish non-existing LeaseSet"); - return; - } - if (m_PublishReplyToken) - { - LogPrint (eLogInfo, "Publishing is pending"); - return; - } - auto outbound = m_Pool->GetNextOutboundTunnel (); - if (!outbound) - { - LogPrint ("Can't publish LeaseSet. No outbound tunnels"); - return; - } - std::set excluded; - auto floodfill = i2p::data::netdb.GetClosestFloodfill (m_LeaseSet->GetIdentHash (), m_ExcludedFloodfills); - if (!floodfill) - { - LogPrint ("Can't publish LeaseSet. No more floodfills found"); - m_ExcludedFloodfills.clear (); - return; - } - m_ExcludedFloodfills.insert (floodfill->GetIdentHash ()); - LogPrint (eLogDebug, "Publish LeaseSet of ", GetIdentHash ().ToBase32 ()); - m_PublishReplyToken = i2p::context.GetRandomNumberGenerator ().GenerateWord32 (); - auto msg = WrapMessage (floodfill, i2p::CreateDatabaseStoreMsg (m_LeaseSet, m_PublishReplyToken)); - m_PublishConfirmationTimer.expires_from_now (boost::posix_time::seconds(PUBLISH_CONFIRMATION_TIMEOUT)); - m_PublishConfirmationTimer.async_wait (std::bind (&ClientDestination::HandlePublishConfirmationTimer, - this, std::placeholders::_1)); - outbound->SendTunnelDataMsg (floodfill->GetIdentHash (), 0, msg); - } + void ClientDestination::SetLeaseSetUpdated () + { + i2p::garlic::GarlicDestination::SetLeaseSetUpdated (); + UpdateLeaseSet (); + if (m_IsPublic) + Publish (); + } + + void ClientDestination::Publish () + { + if (!m_LeaseSet || !m_Pool) + { + LogPrint (eLogError, "Can't publish non-existing LeaseSet"); + return; + } + if (m_PublishReplyToken) + { + LogPrint (eLogInfo, "Publishing is pending"); + return; + } + auto outbound = m_Pool->GetNextOutboundTunnel (); + if (!outbound) + { + LogPrint ("Can't publish LeaseSet. No outbound tunnels"); + return; + } + std::set excluded; + auto floodfill = i2p::data::netdb.GetClosestFloodfill (m_LeaseSet->GetIdentHash (), m_ExcludedFloodfills); + if (!floodfill) + { + LogPrint ("Can't publish LeaseSet. No more floodfills found"); + m_ExcludedFloodfills.clear (); + return; + } + m_ExcludedFloodfills.insert (floodfill->GetIdentHash ()); + LogPrint (eLogDebug, "Publish LeaseSet of ", GetIdentHash ().ToBase32 ()); + m_PublishReplyToken = i2p::context.GetRandomNumberGenerator ().GenerateWord32 (); + auto msg = WrapMessage (floodfill, i2p::CreateDatabaseStoreMsg (m_LeaseSet, m_PublishReplyToken)); + m_PublishConfirmationTimer.expires_from_now (boost::posix_time::seconds(PUBLISH_CONFIRMATION_TIMEOUT)); + m_PublishConfirmationTimer.async_wait (std::bind (&ClientDestination::HandlePublishConfirmationTimer, + this, std::placeholders::_1)); + outbound->SendTunnelDataMsg (floodfill->GetIdentHash (), 0, msg); + } - void ClientDestination::HandlePublishConfirmationTimer (const boost::system::error_code& ecode) - { - if (ecode != boost::asio::error::operation_aborted) - { - if (m_PublishReplyToken) - { - LogPrint (eLogWarning, "Publish confirmation was not received in ", PUBLISH_CONFIRMATION_TIMEOUT, "seconds. Try again"); - m_PublishReplyToken = 0; - Publish (); - } - } - } + void ClientDestination::HandlePublishConfirmationTimer (const boost::system::error_code& ecode) + { + if (ecode != boost::asio::error::operation_aborted) + { + if (m_PublishReplyToken) + { + LogPrint (eLogWarning, "Publish confirmation was not received in ", PUBLISH_CONFIRMATION_TIMEOUT, "seconds. Try again"); + m_PublishReplyToken = 0; + Publish (); + } + } + } - void ClientDestination::HandleDataMessage (const uint8_t * buf, size_t len) - { - uint32_t length = bufbe32toh (buf); - buf += 4; - // we assume I2CP payload - uint16_t fromPort = bufbe16toh (buf + 4), // source - toPort = bufbe16toh (buf + 6); // destination - switch (buf[9]) - { - case PROTOCOL_TYPE_STREAMING: - { - // streaming protocol - auto dest = GetStreamingDestination (toPort); - if (dest) - dest->HandleDataMessagePayload (buf, length); - else - LogPrint ("Missing streaming destination"); - } - break; - case PROTOCOL_TYPE_DATAGRAM: - // datagram protocol - if (m_DatagramDestination) - m_DatagramDestination->HandleDataMessagePayload (fromPort, toPort, buf, length); - else - LogPrint ("Missing streaming destination"); - break; - default: - LogPrint ("Data: unexpected protocol ", buf[9]); - } - } + void ClientDestination::HandleDataMessage (const uint8_t * buf, size_t len) + { + uint32_t length = bufbe32toh (buf); + buf += 4; + // we assume I2CP payload + uint16_t fromPort = bufbe16toh (buf + 4), // source + toPort = bufbe16toh (buf + 6); // destination + switch (buf[9]) + { + case PROTOCOL_TYPE_STREAMING: + { + // streaming protocol + auto dest = GetStreamingDestination (toPort); + if (dest) + dest->HandleDataMessagePayload (buf, length); + else + LogPrint ("Missing streaming destination"); + } + break; + case PROTOCOL_TYPE_DATAGRAM: + // datagram protocol + if (m_DatagramDestination) + m_DatagramDestination->HandleDataMessagePayload (fromPort, toPort, buf, length); + else + LogPrint ("Missing streaming destination"); + break; + default: + LogPrint ("Data: unexpected protocol ", buf[9]); + } + } - void ClientDestination::CreateStream (StreamRequestComplete streamRequestComplete, const i2p::data::IdentHash& dest, int port) { - assert(streamRequestComplete); - auto leaseSet = FindLeaseSet (dest); - if (leaseSet) - streamRequestComplete(CreateStream (leaseSet, port)); - else - { - RequestDestination (dest, - [this, streamRequestComplete, port](std::shared_ptr ls) - { - if (ls) - streamRequestComplete(CreateStream (ls, port)); - else - streamRequestComplete (nullptr); - }); - } - } + void ClientDestination::CreateStream (StreamRequestComplete streamRequestComplete, const i2p::data::IdentHash& dest, int port) { + assert(streamRequestComplete); + auto leaseSet = FindLeaseSet (dest); + if (leaseSet) + streamRequestComplete(CreateStream (leaseSet, port)); + else + { + RequestDestination (dest, + [this, streamRequestComplete, port](std::shared_ptr ls) + { + if (ls) + streamRequestComplete(CreateStream (ls, port)); + else + streamRequestComplete (nullptr); + }); + } + } - std::shared_ptr ClientDestination::CreateStream (std::shared_ptr remote, int port) - { - if (m_StreamingDestination) - return m_StreamingDestination->CreateNewOutgoingStream (remote, port); - else - return nullptr; - } + std::shared_ptr ClientDestination::CreateStream (std::shared_ptr remote, int port) + { + if (m_StreamingDestination) + return m_StreamingDestination->CreateNewOutgoingStream (remote, port); + else + return nullptr; + } - std::shared_ptr ClientDestination::GetStreamingDestination (int port) const - { - if (port) - { - auto it = m_StreamingDestinationsByPorts.find (port); - if (it != m_StreamingDestinationsByPorts.end ()) - return it->second; - } - // if port is zero or not found, use default destination - return m_StreamingDestination; - } - - void ClientDestination::AcceptStreams (const i2p::stream::StreamingDestination::Acceptor& acceptor) - { - if (m_StreamingDestination) - m_StreamingDestination->SetAcceptor (acceptor); - } + std::shared_ptr ClientDestination::GetStreamingDestination (int port) const + { + if (port) + { + auto it = m_StreamingDestinationsByPorts.find (port); + if (it != m_StreamingDestinationsByPorts.end ()) + return it->second; + } + // if port is zero or not found, use default destination + return m_StreamingDestination; + } + + void ClientDestination::AcceptStreams (const i2p::stream::StreamingDestination::Acceptor& acceptor) + { + if (m_StreamingDestination) + m_StreamingDestination->SetAcceptor (acceptor); + } - void ClientDestination::StopAcceptingStreams () - { - if (m_StreamingDestination) - m_StreamingDestination->ResetAcceptor (); - } - - bool ClientDestination::IsAcceptingStreams () const - { - if (m_StreamingDestination) - return m_StreamingDestination->IsAcceptorSet (); - return false; - } + void ClientDestination::StopAcceptingStreams () + { + if (m_StreamingDestination) + m_StreamingDestination->ResetAcceptor (); + } + + bool ClientDestination::IsAcceptingStreams () const + { + if (m_StreamingDestination) + return m_StreamingDestination->IsAcceptorSet (); + return false; + } - std::shared_ptr ClientDestination::CreateStreamingDestination (int port) - { - auto dest = std::make_shared (*this, port); - if (port) - m_StreamingDestinationsByPorts[port] = dest; - else // update default - m_StreamingDestination = dest; - return dest; - } - - i2p::datagram::DatagramDestination * ClientDestination::CreateDatagramDestination () - { - if (!m_DatagramDestination) - m_DatagramDestination = new i2p::datagram::DatagramDestination (*this); - return m_DatagramDestination; - } + std::shared_ptr ClientDestination::CreateStreamingDestination (int port) + { + auto dest = std::make_shared (*this, port); + if (port) + m_StreamingDestinationsByPorts[port] = dest; + else // update default + m_StreamingDestination = dest; + return dest; + } + + i2p::datagram::DatagramDestination * ClientDestination::CreateDatagramDestination () + { + if (!m_DatagramDestination) + m_DatagramDestination = new i2p::datagram::DatagramDestination (*this); + return m_DatagramDestination; + } - bool ClientDestination::RequestDestination (const i2p::data::IdentHash& dest, RequestComplete requestComplete) - { - if (!m_Pool || !IsReady ()) - { - if (requestComplete) requestComplete (false); - return false; - } - m_Service.post (std::bind (&ClientDestination::RequestLeaseSet, this, dest, requestComplete)); - return true; - } + bool ClientDestination::RequestDestination (const i2p::data::IdentHash& dest, RequestComplete requestComplete) + { + if (!m_Pool || !IsReady ()) + { + if (requestComplete) requestComplete (false); + return false; + } + m_Service.post (std::bind (&ClientDestination::RequestLeaseSet, this, dest, requestComplete)); + return true; + } - void ClientDestination::RequestLeaseSet (const i2p::data::IdentHash& dest, RequestComplete requestComplete) - { - std::set excluded; - auto floodfill = i2p::data::netdb.GetClosestFloodfill (dest, excluded); - if (floodfill) - { - LeaseSetRequest * request = new LeaseSetRequest (m_Service); - request->requestComplete = requestComplete; - auto ret = m_LeaseSetRequests.insert (std::pair(dest,request)); - if (ret.second) // inserted - { - if (!SendLeaseSetRequest (dest, floodfill, request)) - { - // request failed - if (request->requestComplete) request->requestComplete (nullptr); - delete request; - m_LeaseSetRequests.erase (dest); - } - } - else // duplicate - { - LogPrint (eLogError, "Request of ", dest.ToBase64 (), " is pending already"); - // TODO: queue up requests - if (request->requestComplete) request->requestComplete (nullptr); - delete request; - } - } - else - LogPrint (eLogError, "No floodfills found"); - } - - bool ClientDestination::SendLeaseSetRequest (const i2p::data::IdentHash& dest, - std::shared_ptr nextFloodfill, LeaseSetRequest * request) - { - auto replyTunnel = m_Pool->GetNextInboundTunnel (); - if (!replyTunnel) LogPrint (eLogError, "No inbound tunnels found"); - - auto outboundTunnel = m_Pool->GetNextOutboundTunnel (); - if (!outboundTunnel) LogPrint (eLogError, "No outbound tunnels found"); - - if (replyTunnel && outboundTunnel) - { - request->excluded.insert (nextFloodfill->GetIdentHash ()); - request->requestTime = i2p::util::GetSecondsSinceEpoch (); - request->requestTimeoutTimer.cancel (); + void ClientDestination::RequestLeaseSet (const i2p::data::IdentHash& dest, RequestComplete requestComplete) + { + std::set excluded; + auto floodfill = i2p::data::netdb.GetClosestFloodfill (dest, excluded); + if (floodfill) + { + LeaseSetRequest * request = new LeaseSetRequest (m_Service); + request->requestComplete = requestComplete; + auto ret = m_LeaseSetRequests.insert (std::pair(dest,request)); + if (ret.second) // inserted + { + if (!SendLeaseSetRequest (dest, floodfill, request)) + { + // request failed + if (request->requestComplete) request->requestComplete (nullptr); + delete request; + m_LeaseSetRequests.erase (dest); + } + } + else // duplicate + { + LogPrint (eLogError, "Request of ", dest.ToBase64 (), " is pending already"); + // TODO: queue up requests + if (request->requestComplete) request->requestComplete (nullptr); + delete request; + } + } + else + LogPrint (eLogError, "No floodfills found"); + } + + bool ClientDestination::SendLeaseSetRequest (const i2p::data::IdentHash& dest, + std::shared_ptr nextFloodfill, LeaseSetRequest * request) + { + auto replyTunnel = m_Pool->GetNextInboundTunnel (); + if (!replyTunnel) LogPrint (eLogError, "No inbound tunnels found"); + + auto outboundTunnel = m_Pool->GetNextOutboundTunnel (); + if (!outboundTunnel) LogPrint (eLogError, "No outbound tunnels found"); + + if (replyTunnel && outboundTunnel) + { + request->excluded.insert (nextFloodfill->GetIdentHash ()); + request->requestTime = i2p::util::GetSecondsSinceEpoch (); + request->requestTimeoutTimer.cancel (); - CryptoPP::AutoSeededRandomPool rnd; - uint8_t replyKey[32], replyTag[32]; - rnd.GenerateBlock (replyKey, 32); // random session key - rnd.GenerateBlock (replyTag, 32); // random session tag - AddSessionKey (replyKey, replyTag); + CryptoPP::AutoSeededRandomPool rnd; + uint8_t replyKey[32], replyTag[32]; + rnd.GenerateBlock (replyKey, 32); // random session key + rnd.GenerateBlock (replyTag, 32); // random session tag + AddSessionKey (replyKey, replyTag); - auto msg = WrapMessage (nextFloodfill, - CreateLeaseSetDatabaseLookupMsg (dest, request->excluded, - replyTunnel.get (), replyKey, replyTag)); - outboundTunnel->SendTunnelDataMsg ( - { - i2p::tunnel::TunnelMessageBlock - { - i2p::tunnel::eDeliveryTypeRouter, - nextFloodfill->GetIdentHash (), 0, msg - } - }); - request->requestTimeoutTimer.expires_from_now (boost::posix_time::seconds(LEASESET_REQUEST_TIMEOUT)); - request->requestTimeoutTimer.async_wait (std::bind (&ClientDestination::HandleRequestTimoutTimer, - this, std::placeholders::_1, dest)); - } - else - return false; - return true; - } + auto msg = WrapMessage (nextFloodfill, + CreateLeaseSetDatabaseLookupMsg (dest, request->excluded, + replyTunnel.get (), replyKey, replyTag)); + outboundTunnel->SendTunnelDataMsg ( + { + i2p::tunnel::TunnelMessageBlock + { + i2p::tunnel::eDeliveryTypeRouter, + nextFloodfill->GetIdentHash (), 0, msg + } + }); + request->requestTimeoutTimer.expires_from_now (boost::posix_time::seconds(LEASESET_REQUEST_TIMEOUT)); + request->requestTimeoutTimer.async_wait (std::bind (&ClientDestination::HandleRequestTimoutTimer, + this, std::placeholders::_1, dest)); + } + else + return false; + return true; + } - void ClientDestination::HandleRequestTimoutTimer (const boost::system::error_code& ecode, const i2p::data::IdentHash& dest) - { - if (ecode != boost::asio::error::operation_aborted) - { - auto it = m_LeaseSetRequests.find (dest); - if (it != m_LeaseSetRequests.end ()) - { - bool done = false; - uint64_t ts = i2p::util::GetSecondsSinceEpoch (); - if (ts < it->second->requestTime + MAX_LEASESET_REQUEST_TIMEOUT) - { - auto floodfill = i2p::data::netdb.GetClosestFloodfill (dest, it->second->excluded); - if (floodfill) - done = !SendLeaseSetRequest (dest, floodfill, it->second); - else - done = true; - } - else - { - LogPrint (eLogInfo, dest.ToBase64 (), " was not found within ", MAX_LEASESET_REQUEST_TIMEOUT, " seconds"); - done = true; - } - - if (done) - { - if (it->second->requestComplete) it->second->requestComplete (false); - delete it->second; - m_LeaseSetRequests.erase (it); - } - } - } - } + void ClientDestination::HandleRequestTimoutTimer (const boost::system::error_code& ecode, const i2p::data::IdentHash& dest) + { + if (ecode != boost::asio::error::operation_aborted) + { + auto it = m_LeaseSetRequests.find (dest); + if (it != m_LeaseSetRequests.end ()) + { + bool done = false; + uint64_t ts = i2p::util::GetSecondsSinceEpoch (); + if (ts < it->second->requestTime + MAX_LEASESET_REQUEST_TIMEOUT) + { + auto floodfill = i2p::data::netdb.GetClosestFloodfill (dest, it->second->excluded); + if (floodfill) + done = !SendLeaseSetRequest (dest, floodfill, it->second); + else + done = true; + } + else + { + LogPrint (eLogInfo, dest.ToBase64 (), " was not found within ", MAX_LEASESET_REQUEST_TIMEOUT, " seconds"); + done = true; + } + + if (done) + { + if (it->second->requestComplete) it->second->requestComplete (false); + delete it->second; + m_LeaseSetRequests.erase (it); + } + } + } + } - void ClientDestination::HandleCleanupTimer (const boost::system::error_code& ecode) - { - if (ecode != boost::asio::error::operation_aborted) - { - CleanupRoutingSessions (); - CleanupRemoteLeaseSets (); - m_CleanupTimer.expires_from_now (boost::posix_time::minutes (DESTINATION_CLEANUP_TIMEOUT)); - m_CleanupTimer.async_wait (std::bind (&ClientDestination::HandleCleanupTimer, - this, std::placeholders::_1)); - } - } + void ClientDestination::HandleCleanupTimer (const boost::system::error_code& ecode) + { + if (ecode != boost::asio::error::operation_aborted) + { + CleanupRoutingSessions (); + CleanupRemoteLeaseSets (); + m_CleanupTimer.expires_from_now (boost::posix_time::minutes (DESTINATION_CLEANUP_TIMEOUT)); + m_CleanupTimer.async_wait (std::bind (&ClientDestination::HandleCleanupTimer, + this, std::placeholders::_1)); + } + } - void ClientDestination::CleanupRemoteLeaseSets () - { - for (auto it = m_RemoteLeaseSets.begin (); it != m_RemoteLeaseSets.end ();) - { - if (!it->second->HasNonExpiredLeases ()) // all leases expired - { - LogPrint ("Remote LeaseSet ", it->second->GetIdentHash ().ToBase64 (), " expired"); - it = m_RemoteLeaseSets.erase (it); - } - else - it++; - } - } + void ClientDestination::CleanupRemoteLeaseSets () + { + for (auto it = m_RemoteLeaseSets.begin (); it != m_RemoteLeaseSets.end ();) + { + if (!it->second->HasNonExpiredLeases ()) // all leases expired + { + LogPrint ("Remote LeaseSet ", it->second->GetIdentHash ().ToBase64 (), " expired"); + it = m_RemoteLeaseSets.erase (it); + } + else + it++; + } + } } } diff --git a/Destination.h b/Destination.h index c41ee9ca..7e73af95 100644 --- a/Destination.h +++ b/Destination.h @@ -22,135 +22,135 @@ namespace i2p { namespace client { - const uint8_t PROTOCOL_TYPE_STREAMING = 6; - const uint8_t PROTOCOL_TYPE_DATAGRAM = 17; - const uint8_t PROTOCOL_TYPE_RAW = 18; - const int PUBLISH_CONFIRMATION_TIMEOUT = 5; // in seconds - const int LEASESET_REQUEST_TIMEOUT = 5; // in seconds - const int MAX_LEASESET_REQUEST_TIMEOUT = 40; // in seconds - const int MAX_NUM_FLOODFILLS_PER_REQUEST = 7; - const int DESTINATION_CLEANUP_TIMEOUT = 20; // in minutes - - // I2CP - const char I2CP_PARAM_INBOUND_TUNNEL_LENGTH[] = "inbound.length"; - const int DEFAULT_INBOUND_TUNNEL_LENGTH = 3; - const char I2CP_PARAM_OUTBOUND_TUNNEL_LENGTH[] = "outbound.length"; - const int DEFAULT_OUTBOUND_TUNNEL_LENGTH = 3; - const char I2CP_PARAM_INBOUND_TUNNELS_QUANTITY[] = "inbound.quantity"; - const int DEFAULT_INBOUND_TUNNELS_QUANTITY = 5; - const char I2CP_PARAM_OUTBOUND_TUNNELS_QUANTITY[] = "outbound.quantity"; - const int DEFAULT_OUTBOUND_TUNNELS_QUANTITY = 5; - const char I2CP_PARAM_EXPLICIT_PEERS[] = "explicitPeers"; - const int STREAM_REQUEST_TIMEOUT = 60; //in seconds + const uint8_t PROTOCOL_TYPE_STREAMING = 6; + const uint8_t PROTOCOL_TYPE_DATAGRAM = 17; + const uint8_t PROTOCOL_TYPE_RAW = 18; + const int PUBLISH_CONFIRMATION_TIMEOUT = 5; // in seconds + const int LEASESET_REQUEST_TIMEOUT = 5; // in seconds + const int MAX_LEASESET_REQUEST_TIMEOUT = 40; // in seconds + const int MAX_NUM_FLOODFILLS_PER_REQUEST = 7; + const int DESTINATION_CLEANUP_TIMEOUT = 20; // in minutes + + // I2CP + const char I2CP_PARAM_INBOUND_TUNNEL_LENGTH[] = "inbound.length"; + const int DEFAULT_INBOUND_TUNNEL_LENGTH = 3; + const char I2CP_PARAM_OUTBOUND_TUNNEL_LENGTH[] = "outbound.length"; + const int DEFAULT_OUTBOUND_TUNNEL_LENGTH = 3; + const char I2CP_PARAM_INBOUND_TUNNELS_QUANTITY[] = "inbound.quantity"; + const int DEFAULT_INBOUND_TUNNELS_QUANTITY = 5; + const char I2CP_PARAM_OUTBOUND_TUNNELS_QUANTITY[] = "outbound.quantity"; + const int DEFAULT_OUTBOUND_TUNNELS_QUANTITY = 5; + const char I2CP_PARAM_EXPLICIT_PEERS[] = "explicitPeers"; + const int STREAM_REQUEST_TIMEOUT = 60; //in seconds - typedef std::function stream)> StreamRequestComplete; + typedef std::function stream)> StreamRequestComplete; - class ClientDestination: public i2p::garlic::GarlicDestination - { - typedef std::function leaseSet)> RequestComplete; - // leaseSet = nullptr means not found - struct LeaseSetRequest - { - LeaseSetRequest (boost::asio::io_service& service): requestTime (0), requestTimeoutTimer (service) {}; - std::set excluded; - uint64_t requestTime; - boost::asio::deadline_timer requestTimeoutTimer; - RequestComplete requestComplete; - }; - - - public: + class ClientDestination: public i2p::garlic::GarlicDestination + { + typedef std::function leaseSet)> RequestComplete; + // leaseSet = nullptr means not found + struct LeaseSetRequest + { + LeaseSetRequest (boost::asio::io_service& service): requestTime (0), requestTimeoutTimer (service) {}; + std::set excluded; + uint64_t requestTime; + boost::asio::deadline_timer requestTimeoutTimer; + RequestComplete requestComplete; + }; + + + public: - ClientDestination (const i2p::data::PrivateKeys& keys, bool isPublic, const std::map * params = nullptr); - ~ClientDestination (); + ClientDestination (const i2p::data::PrivateKeys& keys, bool isPublic, const std::map * params = nullptr); + ~ClientDestination (); - virtual void Start (); - virtual void Stop (); - bool IsRunning () const { return m_IsRunning; }; - boost::asio::io_service& GetService () { return m_Service; }; - std::shared_ptr GetTunnelPool () { return m_Pool; }; - bool IsReady () const { return m_LeaseSet && m_LeaseSet->HasNonExpiredLeases () && m_Pool->GetOutboundTunnels ().size () > 0; }; - std::shared_ptr FindLeaseSet (const i2p::data::IdentHash& ident); - bool RequestDestination (const i2p::data::IdentHash& dest, RequestComplete requestComplete = nullptr); - - // streaming - std::shared_ptr CreateStreamingDestination (int port); // additional - std::shared_ptr GetStreamingDestination (int port = 0) const; - // following methods operate with default streaming destination - void CreateStream (StreamRequestComplete streamRequestComplete, const i2p::data::IdentHash& dest, int port = 0); - std::shared_ptr CreateStream (std::shared_ptr remote, int port = 0); - void AcceptStreams (const i2p::stream::StreamingDestination::Acceptor& acceptor); - void StopAcceptingStreams (); - bool IsAcceptingStreams () const; - - // datagram - i2p::datagram::DatagramDestination * GetDatagramDestination () const { return m_DatagramDestination; }; - i2p::datagram::DatagramDestination * CreateDatagramDestination (); + virtual void Start (); + virtual void Stop (); + bool IsRunning () const { return m_IsRunning; }; + boost::asio::io_service& GetService () { return m_Service; }; + std::shared_ptr GetTunnelPool () { return m_Pool; }; + bool IsReady () const { return m_LeaseSet && m_LeaseSet->HasNonExpiredLeases () && m_Pool->GetOutboundTunnels ().size () > 0; }; + std::shared_ptr FindLeaseSet (const i2p::data::IdentHash& ident); + bool RequestDestination (const i2p::data::IdentHash& dest, RequestComplete requestComplete = nullptr); + + // streaming + std::shared_ptr CreateStreamingDestination (int port); // additional + std::shared_ptr GetStreamingDestination (int port = 0) const; + // following methods operate with default streaming destination + void CreateStream (StreamRequestComplete streamRequestComplete, const i2p::data::IdentHash& dest, int port = 0); + std::shared_ptr CreateStream (std::shared_ptr remote, int port = 0); + void AcceptStreams (const i2p::stream::StreamingDestination::Acceptor& acceptor); + void StopAcceptingStreams (); + bool IsAcceptingStreams () const; + + // datagram + i2p::datagram::DatagramDestination * GetDatagramDestination () const { return m_DatagramDestination; }; + i2p::datagram::DatagramDestination * CreateDatagramDestination (); - // implements LocalDestination - const i2p::data::PrivateKeys& GetPrivateKeys () const { return m_Keys; }; - const uint8_t * GetEncryptionPrivateKey () const { return m_EncryptionPrivateKey; }; - const uint8_t * GetEncryptionPublicKey () const { return m_EncryptionPublicKey; }; - - // implements GarlicDestination - std::shared_ptr GetLeaseSet (); - std::shared_ptr GetTunnelPool () const { return m_Pool; } - void HandleI2NPMessage (const uint8_t * buf, size_t len, std::shared_ptr from); + // implements LocalDestination + const i2p::data::PrivateKeys& GetPrivateKeys () const { return m_Keys; }; + const uint8_t * GetEncryptionPrivateKey () const { return m_EncryptionPrivateKey; }; + const uint8_t * GetEncryptionPublicKey () const { return m_EncryptionPublicKey; }; + + // implements GarlicDestination + std::shared_ptr GetLeaseSet (); + std::shared_ptr GetTunnelPool () const { return m_Pool; } + void HandleI2NPMessage (const uint8_t * buf, size_t len, std::shared_ptr from); - // override GarlicDestination - bool SubmitSessionKey (const uint8_t * key, const uint8_t * tag); - void ProcessGarlicMessage (std::shared_ptr msg); - void ProcessDeliveryStatusMessage (std::shared_ptr msg); - void SetLeaseSetUpdated (); + // override GarlicDestination + bool SubmitSessionKey (const uint8_t * key, const uint8_t * tag); + void ProcessGarlicMessage (std::shared_ptr msg); + void ProcessDeliveryStatusMessage (std::shared_ptr msg); + void SetLeaseSetUpdated (); - // I2CP - void HandleDataMessage (const uint8_t * buf, size_t len); + // I2CP + void HandleDataMessage (const uint8_t * buf, size_t len); - private: - - void Run (); - void UpdateLeaseSet (); - void Publish (); - void HandlePublishConfirmationTimer (const boost::system::error_code& ecode); - void HandleDatabaseStoreMessage (const uint8_t * buf, size_t len); - void HandleDatabaseSearchReplyMessage (const uint8_t * buf, size_t len); - void HandleDeliveryStatusMessage (std::shared_ptr msg); + private: + + void Run (); + void UpdateLeaseSet (); + void Publish (); + void HandlePublishConfirmationTimer (const boost::system::error_code& ecode); + void HandleDatabaseStoreMessage (const uint8_t * buf, size_t len); + void HandleDatabaseSearchReplyMessage (const uint8_t * buf, size_t len); + void HandleDeliveryStatusMessage (std::shared_ptr msg); - void RequestLeaseSet (const i2p::data::IdentHash& dest, RequestComplete requestComplete); - bool SendLeaseSetRequest (const i2p::data::IdentHash& dest, std::shared_ptr nextFloodfill, LeaseSetRequest * request); - void HandleRequestTimoutTimer (const boost::system::error_code& ecode, const i2p::data::IdentHash& dest); - void HandleCleanupTimer (const boost::system::error_code& ecode); - void CleanupRemoteLeaseSets (); - - private: + void RequestLeaseSet (const i2p::data::IdentHash& dest, RequestComplete requestComplete); + bool SendLeaseSetRequest (const i2p::data::IdentHash& dest, std::shared_ptr nextFloodfill, LeaseSetRequest * request); + void HandleRequestTimoutTimer (const boost::system::error_code& ecode, const i2p::data::IdentHash& dest); + void HandleCleanupTimer (const boost::system::error_code& ecode); + void CleanupRemoteLeaseSets (); + + private: - volatile bool m_IsRunning; - std::thread * m_Thread; - boost::asio::io_service m_Service; - boost::asio::io_service::work m_Work; - i2p::data::PrivateKeys m_Keys; - uint8_t m_EncryptionPublicKey[256], m_EncryptionPrivateKey[256]; - std::map > m_RemoteLeaseSets; - std::map m_LeaseSetRequests; + volatile bool m_IsRunning; + std::thread * m_Thread; + boost::asio::io_service m_Service; + boost::asio::io_service::work m_Work; + i2p::data::PrivateKeys m_Keys; + uint8_t m_EncryptionPublicKey[256], m_EncryptionPrivateKey[256]; + std::map > m_RemoteLeaseSets; + std::map m_LeaseSetRequests; - std::shared_ptr m_Pool; - std::shared_ptr m_LeaseSet; - bool m_IsPublic; - uint32_t m_PublishReplyToken; - std::set m_ExcludedFloodfills; // for publishing - - std::shared_ptr m_StreamingDestination; // default - std::map > m_StreamingDestinationsByPorts; - i2p::datagram::DatagramDestination * m_DatagramDestination; - - boost::asio::deadline_timer m_PublishConfirmationTimer, m_CleanupTimer; + std::shared_ptr m_Pool; + std::shared_ptr m_LeaseSet; + bool m_IsPublic; + uint32_t m_PublishReplyToken; + std::set m_ExcludedFloodfills; // for publishing + + std::shared_ptr m_StreamingDestination; // default + std::map > m_StreamingDestinationsByPorts; + i2p::datagram::DatagramDestination * m_DatagramDestination; + + boost::asio::deadline_timer m_PublishConfirmationTimer, m_CleanupTimer; - public: - - // for HTTP only - int GetNumRemoteLeaseSets () const { return m_RemoteLeaseSets.size (); }; - }; -} -} + public: + + // for HTTP only + int GetNumRemoteLeaseSets () const { return m_RemoteLeaseSets.size (); }; + }; +} +} #endif diff --git a/ElGamal.h b/ElGamal.h index 359de358..9f90da53 100644 --- a/ElGamal.h +++ b/ElGamal.h @@ -14,74 +14,74 @@ namespace i2p namespace crypto { - class ElGamalEncryption - { - public: + class ElGamalEncryption + { + public: - ElGamalEncryption (const uint8_t * key) - { - CryptoPP::AutoSeededRandomPool rnd; - CryptoPP::Integer y (key, 256), k (rnd, CryptoPP::Integer::One(), elgp-1); - a = a_exp_b_mod_c (elgg, k, elgp); - b1 = a_exp_b_mod_c (y, k, elgp); - } + ElGamalEncryption (const uint8_t * key) + { + CryptoPP::AutoSeededRandomPool rnd; + CryptoPP::Integer y (key, 256), k (rnd, CryptoPP::Integer::One(), elgp-1); + a = a_exp_b_mod_c (elgg, k, elgp); + b1 = a_exp_b_mod_c (y, k, elgp); + } - void Encrypt (const uint8_t * data, int len, uint8_t * encrypted, bool zeroPadding = false) const - { - // calculate b = b1*m mod p - uint8_t m[255]; - m[0] = 0xFF; - memcpy (m+33, data, len); - CryptoPP::SHA256().CalculateDigest(m+1, m+33, 222); - CryptoPP::Integer b (a_times_b_mod_c (b1, CryptoPP::Integer (m, 255), elgp)); + void Encrypt (const uint8_t * data, int len, uint8_t * encrypted, bool zeroPadding = false) const + { + // calculate b = b1*m mod p + uint8_t m[255]; + m[0] = 0xFF; + memcpy (m+33, data, len); + CryptoPP::SHA256().CalculateDigest(m+1, m+33, 222); + CryptoPP::Integer b (a_times_b_mod_c (b1, CryptoPP::Integer (m, 255), elgp)); - // copy a and b - if (zeroPadding) - { - encrypted[0] = 0; - a.Encode (encrypted + 1, 256); - encrypted[257] = 0; - b.Encode (encrypted + 258, 256); - } - else - { - a.Encode (encrypted, 256); - b.Encode (encrypted + 256, 256); - } - } + // copy a and b + if (zeroPadding) + { + encrypted[0] = 0; + a.Encode (encrypted + 1, 256); + encrypted[257] = 0; + b.Encode (encrypted + 258, 256); + } + else + { + a.Encode (encrypted, 256); + b.Encode (encrypted + 256, 256); + } + } - private: + private: - CryptoPP::Integer a, b1; - }; + CryptoPP::Integer a, b1; + }; - inline bool ElGamalDecrypt (const uint8_t * key, const uint8_t * encrypted, - uint8_t * data, bool zeroPadding = false) - { - CryptoPP::Integer x(key, 256), a(zeroPadding? encrypted +1 : encrypted, 256), - b(zeroPadding? encrypted + 258 :encrypted + 256, 256); - uint8_t m[255]; - a_times_b_mod_c (b, a_exp_b_mod_c (a, elgp - x - 1, elgp), elgp).Encode (m, 255); - if (!CryptoPP::SHA256().VerifyDigest (m + 1, m + 33, 222)) - { - LogPrint ("ElGamal decrypt hash doesn't match"); - return false; - } - memcpy (data, m + 33, 222); - return true; - } + inline bool ElGamalDecrypt (const uint8_t * key, const uint8_t * encrypted, + uint8_t * data, bool zeroPadding = false) + { + CryptoPP::Integer x(key, 256), a(zeroPadding? encrypted +1 : encrypted, 256), + b(zeroPadding? encrypted + 258 :encrypted + 256, 256); + uint8_t m[255]; + a_times_b_mod_c (b, a_exp_b_mod_c (a, elgp - x - 1, elgp), elgp).Encode (m, 255); + if (!CryptoPP::SHA256().VerifyDigest (m + 1, m + 33, 222)) + { + LogPrint ("ElGamal decrypt hash doesn't match"); + return false; + } + memcpy (data, m + 33, 222); + return true; + } - inline void GenerateElGamalKeyPair (CryptoPP::RandomNumberGenerator& rnd, uint8_t * priv, uint8_t * pub) - { -#if defined(__x86_64__) || defined(__i386__) || defined(_MSC_VER) - rnd.GenerateBlock (priv, 256); - a_exp_b_mod_c (elgg, CryptoPP::Integer (priv, 256), elgp).Encode (pub, 256); + inline void GenerateElGamalKeyPair (CryptoPP::RandomNumberGenerator& rnd, uint8_t * priv, uint8_t * pub) + { +#if defined(__x86_64__) || defined(__i386__) || defined(_MSC_VER) + rnd.GenerateBlock (priv, 256); + a_exp_b_mod_c (elgg, CryptoPP::Integer (priv, 256), elgp).Encode (pub, 256); #else - CryptoPP::DH dh (elgp, elgg); - dh.GenerateKeyPair(rnd, priv, pub); -#endif - } + CryptoPP::DH dh (elgp, elgg); + dh.GenerateKeyPair(rnd, priv, pub); +#endif + } } -} +} #endif diff --git a/Garlic.cpp b/Garlic.cpp index 0063be31..0fdc6473 100644 --- a/Garlic.cpp +++ b/Garlic.cpp @@ -14,604 +14,604 @@ namespace i2p { namespace garlic { - GarlicRoutingSession::GarlicRoutingSession (GarlicDestination * owner, - std::shared_ptr destination, int numTags, bool attachLeaseSet): - m_Owner (owner), m_Destination (destination), m_NumTags (numTags), - m_LeaseSetUpdateStatus (attachLeaseSet ? eLeaseSetUpdated : eLeaseSetDoNotSend) - { - // create new session tags and session key - m_Rnd.GenerateBlock (m_SessionKey, 32); - m_Encryption.SetKey (m_SessionKey); - } + GarlicRoutingSession::GarlicRoutingSession (GarlicDestination * owner, + std::shared_ptr destination, int numTags, bool attachLeaseSet): + m_Owner (owner), m_Destination (destination), m_NumTags (numTags), + m_LeaseSetUpdateStatus (attachLeaseSet ? eLeaseSetUpdated : eLeaseSetDoNotSend) + { + // create new session tags and session key + m_Rnd.GenerateBlock (m_SessionKey, 32); + m_Encryption.SetKey (m_SessionKey); + } - GarlicRoutingSession::GarlicRoutingSession (const uint8_t * sessionKey, const SessionTag& sessionTag): - m_Owner (nullptr), m_Destination (nullptr), m_NumTags (1), m_LeaseSetUpdateStatus (eLeaseSetDoNotSend) - { - memcpy (m_SessionKey, sessionKey, 32); - m_Encryption.SetKey (m_SessionKey); - m_SessionTags.push_back (sessionTag); - m_SessionTags.back ().creationTime = i2p::util::GetSecondsSinceEpoch (); - } + GarlicRoutingSession::GarlicRoutingSession (const uint8_t * sessionKey, const SessionTag& sessionTag): + m_Owner (nullptr), m_Destination (nullptr), m_NumTags (1), m_LeaseSetUpdateStatus (eLeaseSetDoNotSend) + { + memcpy (m_SessionKey, sessionKey, 32); + m_Encryption.SetKey (m_SessionKey); + m_SessionTags.push_back (sessionTag); + m_SessionTags.back ().creationTime = i2p::util::GetSecondsSinceEpoch (); + } - GarlicRoutingSession::~GarlicRoutingSession () - { - for (auto it: m_UnconfirmedTagsMsgs) - delete it.second; - m_UnconfirmedTagsMsgs.clear (); - } - - GarlicRoutingSession::UnconfirmedTags * GarlicRoutingSession::GenerateSessionTags () - { - auto tags = new UnconfirmedTags (m_NumTags); - tags->tagsCreationTime = i2p::util::GetSecondsSinceEpoch (); - for (int i = 0; i < m_NumTags; i++) - { - m_Rnd.GenerateBlock (tags->sessionTags[i], 32); - tags->sessionTags[i].creationTime = tags->tagsCreationTime; - } - return tags; - } + GarlicRoutingSession::~GarlicRoutingSession () + { + for (auto it: m_UnconfirmedTagsMsgs) + delete it.second; + m_UnconfirmedTagsMsgs.clear (); + } + + GarlicRoutingSession::UnconfirmedTags * GarlicRoutingSession::GenerateSessionTags () + { + auto tags = new UnconfirmedTags (m_NumTags); + tags->tagsCreationTime = i2p::util::GetSecondsSinceEpoch (); + for (int i = 0; i < m_NumTags; i++) + { + m_Rnd.GenerateBlock (tags->sessionTags[i], 32); + tags->sessionTags[i].creationTime = tags->tagsCreationTime; + } + return tags; + } - void GarlicRoutingSession::MessageConfirmed (uint32_t msgID) - { - TagsConfirmed (msgID); - if (msgID == m_LeaseSetUpdateMsgID) - { - m_LeaseSetUpdateStatus = eLeaseSetUpToDate; - LogPrint (eLogInfo, "LeaseSet update confirmed"); - } - else - CleanupExpiredTags (); - } - - void GarlicRoutingSession::TagsConfirmed (uint32_t msgID) - { - auto it = m_UnconfirmedTagsMsgs.find (msgID); - if (it != m_UnconfirmedTagsMsgs.end ()) - { - uint32_t ts = i2p::util::GetSecondsSinceEpoch (); - UnconfirmedTags * tags = it->second; - if (ts < tags->tagsCreationTime + OUTGOING_TAGS_EXPIRATION_TIMEOUT) - { - for (int i = 0; i < tags->numTags; i++) - m_SessionTags.push_back (tags->sessionTags[i]); - } - m_UnconfirmedTagsMsgs.erase (it); - delete tags; - } - } + void GarlicRoutingSession::MessageConfirmed (uint32_t msgID) + { + TagsConfirmed (msgID); + if (msgID == m_LeaseSetUpdateMsgID) + { + m_LeaseSetUpdateStatus = eLeaseSetUpToDate; + LogPrint (eLogInfo, "LeaseSet update confirmed"); + } + else + CleanupExpiredTags (); + } + + void GarlicRoutingSession::TagsConfirmed (uint32_t msgID) + { + auto it = m_UnconfirmedTagsMsgs.find (msgID); + if (it != m_UnconfirmedTagsMsgs.end ()) + { + uint32_t ts = i2p::util::GetSecondsSinceEpoch (); + UnconfirmedTags * tags = it->second; + if (ts < tags->tagsCreationTime + OUTGOING_TAGS_EXPIRATION_TIMEOUT) + { + for (int i = 0; i < tags->numTags; i++) + m_SessionTags.push_back (tags->sessionTags[i]); + } + m_UnconfirmedTagsMsgs.erase (it); + delete tags; + } + } - bool GarlicRoutingSession::CleanupExpiredTags () - { - uint32_t ts = i2p::util::GetSecondsSinceEpoch (); - for (auto it = m_SessionTags.begin (); it != m_SessionTags.end ();) - { - if (ts >= it->creationTime + OUTGOING_TAGS_EXPIRATION_TIMEOUT) - it = m_SessionTags.erase (it); - else - it++; - } - // delete expired unconfirmed tags - for (auto it = m_UnconfirmedTagsMsgs.begin (); it != m_UnconfirmedTagsMsgs.end ();) - { - if (ts >= it->second->tagsCreationTime + OUTGOING_TAGS_EXPIRATION_TIMEOUT) - { - if (m_Owner) - m_Owner->RemoveCreatedSession (it->first); - delete it->second; - it = m_UnconfirmedTagsMsgs.erase (it); - } - else - it++; - } - return !m_SessionTags.empty () || m_UnconfirmedTagsMsgs.empty (); - } + bool GarlicRoutingSession::CleanupExpiredTags () + { + uint32_t ts = i2p::util::GetSecondsSinceEpoch (); + for (auto it = m_SessionTags.begin (); it != m_SessionTags.end ();) + { + if (ts >= it->creationTime + OUTGOING_TAGS_EXPIRATION_TIMEOUT) + it = m_SessionTags.erase (it); + else + it++; + } + // delete expired unconfirmed tags + for (auto it = m_UnconfirmedTagsMsgs.begin (); it != m_UnconfirmedTagsMsgs.end ();) + { + if (ts >= it->second->tagsCreationTime + OUTGOING_TAGS_EXPIRATION_TIMEOUT) + { + if (m_Owner) + m_Owner->RemoveCreatedSession (it->first); + delete it->second; + it = m_UnconfirmedTagsMsgs.erase (it); + } + else + it++; + } + return !m_SessionTags.empty () || m_UnconfirmedTagsMsgs.empty (); + } - std::shared_ptr GarlicRoutingSession::WrapSingleMessage (std::shared_ptr msg) - { - auto m = ToSharedI2NPMessage(NewI2NPMessage ()); - m->Align (12); // in order to get buf aligned to 16 (12 + 4) - size_t len = 0; - uint8_t * buf = m->GetPayload () + 4; // 4 bytes for length + std::shared_ptr GarlicRoutingSession::WrapSingleMessage (std::shared_ptr msg) + { + auto m = ToSharedI2NPMessage(NewI2NPMessage ()); + m->Align (12); // in order to get buf aligned to 16 (12 + 4) + size_t len = 0; + uint8_t * buf = m->GetPayload () + 4; // 4 bytes for length - // find non-expired tag - bool tagFound = false; - SessionTag tag; - if (m_NumTags > 0) - { - uint32_t ts = i2p::util::GetSecondsSinceEpoch (); - while (!m_SessionTags.empty ()) - { - if (ts < m_SessionTags.front ().creationTime + OUTGOING_TAGS_EXPIRATION_TIMEOUT) - { - tag = m_SessionTags.front (); - m_SessionTags.pop_front (); // use same tag only once - tagFound = true; - break; - } - else - m_SessionTags.pop_front (); // remove expired tag - } - } - // create message - if (!tagFound) // new session - { - LogPrint ("No garlic tags available. Use ElGamal"); - if (!m_Destination) - { - LogPrint ("Can't use ElGamal for unknown destination"); - return nullptr; - } - // create ElGamal block - ElGamalBlock elGamal; - memcpy (elGamal.sessionKey, m_SessionKey, 32); - m_Rnd.GenerateBlock (elGamal.preIV, 32); // Pre-IV - uint8_t iv[32]; // IV is first 16 bytes - CryptoPP::SHA256().CalculateDigest(iv, elGamal.preIV, 32); - m_Destination->GetElGamalEncryption ()->Encrypt ((uint8_t *)&elGamal, sizeof(elGamal), buf, true); - m_Encryption.SetIV (iv); - buf += 514; - len += 514; - } - else // existing session - { - // session tag - memcpy (buf, tag, 32); - uint8_t iv[32]; // IV is first 16 bytes - CryptoPP::SHA256().CalculateDigest(iv, tag, 32); - m_Encryption.SetIV (iv); - buf += 32; - len += 32; - } - // AES block - len += CreateAESBlock (buf, msg); - htobe32buf (m->GetPayload (), len); - m->len += len + 4; - m->FillI2NPMessageHeader (eI2NPGarlic); - return m; - } + // find non-expired tag + bool tagFound = false; + SessionTag tag; + if (m_NumTags > 0) + { + uint32_t ts = i2p::util::GetSecondsSinceEpoch (); + while (!m_SessionTags.empty ()) + { + if (ts < m_SessionTags.front ().creationTime + OUTGOING_TAGS_EXPIRATION_TIMEOUT) + { + tag = m_SessionTags.front (); + m_SessionTags.pop_front (); // use same tag only once + tagFound = true; + break; + } + else + m_SessionTags.pop_front (); // remove expired tag + } + } + // create message + if (!tagFound) // new session + { + LogPrint ("No garlic tags available. Use ElGamal"); + if (!m_Destination) + { + LogPrint ("Can't use ElGamal for unknown destination"); + return nullptr; + } + // create ElGamal block + ElGamalBlock elGamal; + memcpy (elGamal.sessionKey, m_SessionKey, 32); + m_Rnd.GenerateBlock (elGamal.preIV, 32); // Pre-IV + uint8_t iv[32]; // IV is first 16 bytes + CryptoPP::SHA256().CalculateDigest(iv, elGamal.preIV, 32); + m_Destination->GetElGamalEncryption ()->Encrypt ((uint8_t *)&elGamal, sizeof(elGamal), buf, true); + m_Encryption.SetIV (iv); + buf += 514; + len += 514; + } + else // existing session + { + // session tag + memcpy (buf, tag, 32); + uint8_t iv[32]; // IV is first 16 bytes + CryptoPP::SHA256().CalculateDigest(iv, tag, 32); + m_Encryption.SetIV (iv); + buf += 32; + len += 32; + } + // AES block + len += CreateAESBlock (buf, msg); + htobe32buf (m->GetPayload (), len); + m->len += len + 4; + m->FillI2NPMessageHeader (eI2NPGarlic); + return m; + } - size_t GarlicRoutingSession::CreateAESBlock (uint8_t * buf, std::shared_ptr msg) - { - size_t blockSize = 0; - bool createNewTags = m_Owner && m_NumTags && ((int)m_SessionTags.size () <= m_NumTags*2/3); - UnconfirmedTags * newTags = createNewTags ? GenerateSessionTags () : nullptr; - htobuf16 (buf, newTags ? htobe16 (newTags->numTags) : 0); // tag count - blockSize += 2; - if (newTags) // session tags recreated - { - for (int i = 0; i < newTags->numTags; i++) - { - memcpy (buf + blockSize, newTags->sessionTags[i], 32); // tags - blockSize += 32; - } - } - uint32_t * payloadSize = (uint32_t *)(buf + blockSize); - blockSize += 4; - uint8_t * payloadHash = buf + blockSize; - blockSize += 32; - buf[blockSize] = 0; // flag - blockSize++; - size_t len = CreateGarlicPayload (buf + blockSize, msg, newTags); - htobe32buf (payloadSize, len); - CryptoPP::SHA256().CalculateDigest(payloadHash, buf + blockSize, len); - blockSize += len; - size_t rem = blockSize % 16; - if (rem) - blockSize += (16-rem); //padding - m_Encryption.Encrypt(buf, blockSize, buf); - return blockSize; - } + size_t GarlicRoutingSession::CreateAESBlock (uint8_t * buf, std::shared_ptr msg) + { + size_t blockSize = 0; + bool createNewTags = m_Owner && m_NumTags && ((int)m_SessionTags.size () <= m_NumTags*2/3); + UnconfirmedTags * newTags = createNewTags ? GenerateSessionTags () : nullptr; + htobuf16 (buf, newTags ? htobe16 (newTags->numTags) : 0); // tag count + blockSize += 2; + if (newTags) // session tags recreated + { + for (int i = 0; i < newTags->numTags; i++) + { + memcpy (buf + blockSize, newTags->sessionTags[i], 32); // tags + blockSize += 32; + } + } + uint32_t * payloadSize = (uint32_t *)(buf + blockSize); + blockSize += 4; + uint8_t * payloadHash = buf + blockSize; + blockSize += 32; + buf[blockSize] = 0; // flag + blockSize++; + size_t len = CreateGarlicPayload (buf + blockSize, msg, newTags); + htobe32buf (payloadSize, len); + CryptoPP::SHA256().CalculateDigest(payloadHash, buf + blockSize, len); + blockSize += len; + size_t rem = blockSize % 16; + if (rem) + blockSize += (16-rem); //padding + m_Encryption.Encrypt(buf, blockSize, buf); + return blockSize; + } - size_t GarlicRoutingSession::CreateGarlicPayload (uint8_t * payload, std::shared_ptr msg, UnconfirmedTags * newTags) - { - uint64_t ts = i2p::util::GetMillisecondsSinceEpoch () + 5000; // 5 sec - uint32_t msgID = m_Rnd.GenerateWord32 (); - size_t size = 0; - uint8_t * numCloves = payload + size; - *numCloves = 0; - size++; + size_t GarlicRoutingSession::CreateGarlicPayload (uint8_t * payload, std::shared_ptr msg, UnconfirmedTags * newTags) + { + uint64_t ts = i2p::util::GetMillisecondsSinceEpoch () + 5000; // 5 sec + uint32_t msgID = m_Rnd.GenerateWord32 (); + size_t size = 0; + uint8_t * numCloves = payload + size; + *numCloves = 0; + size++; - if (m_Owner) - { - // resubmit non-confirmed LeaseSet - if (m_LeaseSetUpdateStatus == eLeaseSetSubmitted && - i2p::util::GetMillisecondsSinceEpoch () > m_LeaseSetSubmissionTime + LEASET_CONFIRMATION_TIMEOUT) - m_LeaseSetUpdateStatus = eLeaseSetUpdated; + if (m_Owner) + { + // resubmit non-confirmed LeaseSet + if (m_LeaseSetUpdateStatus == eLeaseSetSubmitted && + i2p::util::GetMillisecondsSinceEpoch () > m_LeaseSetSubmissionTime + LEASET_CONFIRMATION_TIMEOUT) + m_LeaseSetUpdateStatus = eLeaseSetUpdated; - // attach DeviveryStatus if necessary - if (newTags || m_LeaseSetUpdateStatus == eLeaseSetUpdated) // new tags created or leaseset updated - { - // clove is DeliveryStatus - auto cloveSize = CreateDeliveryStatusClove (payload + size, msgID); - if (cloveSize > 0) // successive? - { - size += cloveSize; - (*numCloves)++; - if (newTags) // new tags created - m_UnconfirmedTagsMsgs[msgID] = newTags; - m_Owner->DeliveryStatusSent (shared_from_this (), msgID); - } - else - LogPrint ("DeliveryStatus clove was not created"); - } - // attach LeaseSet - if (m_LeaseSetUpdateStatus == eLeaseSetUpdated) - { - m_LeaseSetUpdateStatus = eLeaseSetSubmitted; - m_LeaseSetUpdateMsgID = msgID; - m_LeaseSetSubmissionTime = i2p::util::GetMillisecondsSinceEpoch (); - // clove if our leaseSet must be attached - auto leaseSet = CreateDatabaseStoreMsg (m_Owner->GetLeaseSet ()); - size += CreateGarlicClove (payload + size, leaseSet, false); - (*numCloves)++; - } - } - if (msg) // clove message ifself if presented - { - size += CreateGarlicClove (payload + size, msg, m_Destination ? m_Destination->IsDestination () : false); - (*numCloves)++; - } - - memset (payload + size, 0, 3); // certificate of message - size += 3; - htobe32buf (payload + size, msgID); // MessageID - size += 4; - htobe64buf (payload + size, ts); // Expiration of message - size += 8; - return size; - } + // attach DeviveryStatus if necessary + if (newTags || m_LeaseSetUpdateStatus == eLeaseSetUpdated) // new tags created or leaseset updated + { + // clove is DeliveryStatus + auto cloveSize = CreateDeliveryStatusClove (payload + size, msgID); + if (cloveSize > 0) // successive? + { + size += cloveSize; + (*numCloves)++; + if (newTags) // new tags created + m_UnconfirmedTagsMsgs[msgID] = newTags; + m_Owner->DeliveryStatusSent (shared_from_this (), msgID); + } + else + LogPrint ("DeliveryStatus clove was not created"); + } + // attach LeaseSet + if (m_LeaseSetUpdateStatus == eLeaseSetUpdated) + { + m_LeaseSetUpdateStatus = eLeaseSetSubmitted; + m_LeaseSetUpdateMsgID = msgID; + m_LeaseSetSubmissionTime = i2p::util::GetMillisecondsSinceEpoch (); + // clove if our leaseSet must be attached + auto leaseSet = CreateDatabaseStoreMsg (m_Owner->GetLeaseSet ()); + size += CreateGarlicClove (payload + size, leaseSet, false); + (*numCloves)++; + } + } + if (msg) // clove message ifself if presented + { + size += CreateGarlicClove (payload + size, msg, m_Destination ? m_Destination->IsDestination () : false); + (*numCloves)++; + } + + memset (payload + size, 0, 3); // certificate of message + size += 3; + htobe32buf (payload + size, msgID); // MessageID + size += 4; + htobe64buf (payload + size, ts); // Expiration of message + size += 8; + return size; + } - size_t GarlicRoutingSession::CreateGarlicClove (uint8_t * buf, std::shared_ptr msg, bool isDestination) - { - uint64_t ts = i2p::util::GetMillisecondsSinceEpoch () + 5000; // 5 sec - size_t size = 0; - if (isDestination && m_Destination) - { - buf[size] = eGarlicDeliveryTypeDestination << 5;// delivery instructions flag destination - size++; - memcpy (buf + size, m_Destination->GetIdentHash (), 32); - size += 32; - } - else - { - buf[size] = 0;// delivery instructions flag local - size++; - } - - memcpy (buf + size, msg->GetBuffer (), msg->GetLength ()); - size += msg->GetLength (); - htobe32buf (buf + size, m_Rnd.GenerateWord32 ()); // CloveID - size += 4; - htobe64buf (buf + size, ts); // Expiration of clove - size += 8; - memset (buf + size, 0, 3); // certificate of clove - size += 3; - return size; - } + size_t GarlicRoutingSession::CreateGarlicClove (uint8_t * buf, std::shared_ptr msg, bool isDestination) + { + uint64_t ts = i2p::util::GetMillisecondsSinceEpoch () + 5000; // 5 sec + size_t size = 0; + if (isDestination && m_Destination) + { + buf[size] = eGarlicDeliveryTypeDestination << 5;// delivery instructions flag destination + size++; + memcpy (buf + size, m_Destination->GetIdentHash (), 32); + size += 32; + } + else + { + buf[size] = 0;// delivery instructions flag local + size++; + } + + memcpy (buf + size, msg->GetBuffer (), msg->GetLength ()); + size += msg->GetLength (); + htobe32buf (buf + size, m_Rnd.GenerateWord32 ()); // CloveID + size += 4; + htobe64buf (buf + size, ts); // Expiration of clove + size += 8; + memset (buf + size, 0, 3); // certificate of clove + size += 3; + return size; + } - size_t GarlicRoutingSession::CreateDeliveryStatusClove (uint8_t * buf, uint32_t msgID) - { - size_t size = 0; - if (m_Owner) - { - auto inboundTunnel = m_Owner->GetTunnelPool ()->GetNextInboundTunnel (); - if (inboundTunnel) - { - buf[size] = eGarlicDeliveryTypeTunnel << 5; // delivery instructions flag tunnel - size++; - // hash and tunnelID sequence is reversed for Garlic - memcpy (buf + size, inboundTunnel->GetNextIdentHash (), 32); // To Hash - size += 32; - htobe32buf (buf + size, inboundTunnel->GetNextTunnelID ()); // tunnelID - size += 4; - // create msg - auto msg = CreateDeliveryStatusMsg (msgID); - if (m_Owner) - { - //encrypt - uint8_t key[32], tag[32]; - m_Rnd.GenerateBlock (key, 32); // random session key - m_Rnd.GenerateBlock (tag, 32); // random session tag - m_Owner->SubmitSessionKey (key, tag); - GarlicRoutingSession garlic (key, tag); - msg = garlic.WrapSingleMessage (msg); - } - memcpy (buf + size, msg->GetBuffer (), msg->GetLength ()); - size += msg->GetLength (); - // fill clove - uint64_t ts = i2p::util::GetMillisecondsSinceEpoch () + 5000; // 5 sec - htobe32buf (buf + size, m_Rnd.GenerateWord32 ()); // CloveID - size += 4; - htobe64buf (buf + size, ts); // Expiration of clove - size += 8; - memset (buf + size, 0, 3); // certificate of clove - size += 3; - } - else - LogPrint (eLogError, "No inbound tunnels in the pool for DeliveryStatus"); - } - else - LogPrint ("Missing local LeaseSet"); + size_t GarlicRoutingSession::CreateDeliveryStatusClove (uint8_t * buf, uint32_t msgID) + { + size_t size = 0; + if (m_Owner) + { + auto inboundTunnel = m_Owner->GetTunnelPool ()->GetNextInboundTunnel (); + if (inboundTunnel) + { + buf[size] = eGarlicDeliveryTypeTunnel << 5; // delivery instructions flag tunnel + size++; + // hash and tunnelID sequence is reversed for Garlic + memcpy (buf + size, inboundTunnel->GetNextIdentHash (), 32); // To Hash + size += 32; + htobe32buf (buf + size, inboundTunnel->GetNextTunnelID ()); // tunnelID + size += 4; + // create msg + auto msg = CreateDeliveryStatusMsg (msgID); + if (m_Owner) + { + //encrypt + uint8_t key[32], tag[32]; + m_Rnd.GenerateBlock (key, 32); // random session key + m_Rnd.GenerateBlock (tag, 32); // random session tag + m_Owner->SubmitSessionKey (key, tag); + GarlicRoutingSession garlic (key, tag); + msg = garlic.WrapSingleMessage (msg); + } + memcpy (buf + size, msg->GetBuffer (), msg->GetLength ()); + size += msg->GetLength (); + // fill clove + uint64_t ts = i2p::util::GetMillisecondsSinceEpoch () + 5000; // 5 sec + htobe32buf (buf + size, m_Rnd.GenerateWord32 ()); // CloveID + size += 4; + htobe64buf (buf + size, ts); // Expiration of clove + size += 8; + memset (buf + size, 0, 3); // certificate of clove + size += 3; + } + else + LogPrint (eLogError, "No inbound tunnels in the pool for DeliveryStatus"); + } + else + LogPrint ("Missing local LeaseSet"); - return size; - } - - GarlicDestination::~GarlicDestination () - { - } + return size; + } + + GarlicDestination::~GarlicDestination () + { + } - void GarlicDestination::AddSessionKey (const uint8_t * key, const uint8_t * tag) - { - if (key) - { - uint32_t ts = i2p::util::GetSecondsSinceEpoch (); - auto decryption = std::make_shared(); - decryption->SetKey (key); - m_Tags[SessionTag(tag, ts)] = decryption; - } - } + void GarlicDestination::AddSessionKey (const uint8_t * key, const uint8_t * tag) + { + if (key) + { + uint32_t ts = i2p::util::GetSecondsSinceEpoch (); + auto decryption = std::make_shared(); + decryption->SetKey (key); + m_Tags[SessionTag(tag, ts)] = decryption; + } + } - bool GarlicDestination::SubmitSessionKey (const uint8_t * key, const uint8_t * tag) - { - AddSessionKey (key, tag); - return true; - } + bool GarlicDestination::SubmitSessionKey (const uint8_t * key, const uint8_t * tag) + { + AddSessionKey (key, tag); + return true; + } - void GarlicDestination::HandleGarlicMessage (std::shared_ptr msg) - { - uint8_t * buf = msg->GetPayload (); - uint32_t length = bufbe32toh (buf); - if (length > msg->GetLength ()) - { - LogPrint (eLogError, "Garlic message length ", length, " exceeds I2NP message length ", msg->GetLength ()); - return; - } - buf += 4; // length - auto it = m_Tags.find (SessionTag(buf)); - if (it != m_Tags.end ()) - { - // tag found. Use AES - if (length >= 32) - { - uint8_t iv[32]; // IV is first 16 bytes - CryptoPP::SHA256().CalculateDigest(iv, buf, 32); - it->second->SetIV (iv); - it->second->Decrypt (buf + 32, length - 32, buf + 32); - HandleAESBlock (buf + 32, length - 32, it->second, msg->from); - } - else - LogPrint (eLogError, "Garlic message length ", length, " is less than 32 bytes"); - m_Tags.erase (it); // tag might be used only once - } - else - { - // tag not found. Use ElGamal - ElGamalBlock elGamal; - if (length >= 514 && i2p::crypto::ElGamalDecrypt (GetEncryptionPrivateKey (), buf, (uint8_t *)&elGamal, true)) - { - auto decryption = std::make_shared(); - decryption->SetKey (elGamal.sessionKey); - uint8_t iv[32]; // IV is first 16 bytes - CryptoPP::SHA256().CalculateDigest(iv, elGamal.preIV, 32); - decryption->SetIV (iv); - decryption->Decrypt(buf + 514, length - 514, buf + 514); - HandleAESBlock (buf + 514, length - 514, decryption, msg->from); - } - else - LogPrint (eLogError, "Failed to decrypt garlic"); - } + void GarlicDestination::HandleGarlicMessage (std::shared_ptr msg) + { + uint8_t * buf = msg->GetPayload (); + uint32_t length = bufbe32toh (buf); + if (length > msg->GetLength ()) + { + LogPrint (eLogError, "Garlic message length ", length, " exceeds I2NP message length ", msg->GetLength ()); + return; + } + buf += 4; // length + auto it = m_Tags.find (SessionTag(buf)); + if (it != m_Tags.end ()) + { + // tag found. Use AES + if (length >= 32) + { + uint8_t iv[32]; // IV is first 16 bytes + CryptoPP::SHA256().CalculateDigest(iv, buf, 32); + it->second->SetIV (iv); + it->second->Decrypt (buf + 32, length - 32, buf + 32); + HandleAESBlock (buf + 32, length - 32, it->second, msg->from); + } + else + LogPrint (eLogError, "Garlic message length ", length, " is less than 32 bytes"); + m_Tags.erase (it); // tag might be used only once + } + else + { + // tag not found. Use ElGamal + ElGamalBlock elGamal; + if (length >= 514 && i2p::crypto::ElGamalDecrypt (GetEncryptionPrivateKey (), buf, (uint8_t *)&elGamal, true)) + { + auto decryption = std::make_shared(); + decryption->SetKey (elGamal.sessionKey); + uint8_t iv[32]; // IV is first 16 bytes + CryptoPP::SHA256().CalculateDigest(iv, elGamal.preIV, 32); + decryption->SetIV (iv); + decryption->Decrypt(buf + 514, length - 514, buf + 514); + HandleAESBlock (buf + 514, length - 514, decryption, msg->from); + } + else + LogPrint (eLogError, "Failed to decrypt garlic"); + } - // cleanup expired tags - uint32_t ts = i2p::util::GetSecondsSinceEpoch (); - if (ts > m_LastTagsCleanupTime + INCOMING_TAGS_EXPIRATION_TIMEOUT) - { - if (m_LastTagsCleanupTime) - { - int numExpiredTags = 0; - for (auto it = m_Tags.begin (); it != m_Tags.end ();) - { - if (ts > it->first.creationTime + INCOMING_TAGS_EXPIRATION_TIMEOUT) - { - numExpiredTags++; - it = m_Tags.erase (it); - } - else - it++; - } - LogPrint (numExpiredTags, " tags expired for ", GetIdentHash().ToBase64 ()); - } - m_LastTagsCleanupTime = ts; - } - } + // cleanup expired tags + uint32_t ts = i2p::util::GetSecondsSinceEpoch (); + if (ts > m_LastTagsCleanupTime + INCOMING_TAGS_EXPIRATION_TIMEOUT) + { + if (m_LastTagsCleanupTime) + { + int numExpiredTags = 0; + for (auto it = m_Tags.begin (); it != m_Tags.end ();) + { + if (ts > it->first.creationTime + INCOMING_TAGS_EXPIRATION_TIMEOUT) + { + numExpiredTags++; + it = m_Tags.erase (it); + } + else + it++; + } + LogPrint (numExpiredTags, " tags expired for ", GetIdentHash().ToBase64 ()); + } + m_LastTagsCleanupTime = ts; + } + } - void GarlicDestination::HandleAESBlock (uint8_t * buf, size_t len, std::shared_ptr decryption, - std::shared_ptr from) - { - uint16_t tagCount = bufbe16toh (buf); - buf += 2; len -= 2; - if (tagCount > 0) - { - if (tagCount*32 > len) - { - LogPrint (eLogError, "Tag count ", tagCount, " exceeds length ", len); - return ; - } - uint32_t ts = i2p::util::GetSecondsSinceEpoch (); - for (int i = 0; i < tagCount; i++) - m_Tags[SessionTag(buf + i*32, ts)] = decryption; - } - buf += tagCount*32; - len -= tagCount*32; - uint32_t payloadSize = bufbe32toh (buf); - if (payloadSize > len) - { - LogPrint (eLogError, "Unexpected payload size ", payloadSize); - return; - } - buf += 4; - uint8_t * payloadHash = buf; - buf += 32;// payload hash. - if (*buf) // session key? - buf += 32; // new session key - buf++; // flag + void GarlicDestination::HandleAESBlock (uint8_t * buf, size_t len, std::shared_ptr decryption, + std::shared_ptr from) + { + uint16_t tagCount = bufbe16toh (buf); + buf += 2; len -= 2; + if (tagCount > 0) + { + if (tagCount*32 > len) + { + LogPrint (eLogError, "Tag count ", tagCount, " exceeds length ", len); + return ; + } + uint32_t ts = i2p::util::GetSecondsSinceEpoch (); + for (int i = 0; i < tagCount; i++) + m_Tags[SessionTag(buf + i*32, ts)] = decryption; + } + buf += tagCount*32; + len -= tagCount*32; + uint32_t payloadSize = bufbe32toh (buf); + if (payloadSize > len) + { + LogPrint (eLogError, "Unexpected payload size ", payloadSize); + return; + } + buf += 4; + uint8_t * payloadHash = buf; + buf += 32;// payload hash. + if (*buf) // session key? + buf += 32; // new session key + buf++; // flag - // payload - if (!CryptoPP::SHA256().VerifyDigest (payloadHash, buf, payloadSize)) // payload hash doesn't match - { - LogPrint ("Wrong payload hash"); - return; - } - HandleGarlicPayload (buf, payloadSize, from); - } + // payload + if (!CryptoPP::SHA256().VerifyDigest (payloadHash, buf, payloadSize)) // payload hash doesn't match + { + LogPrint ("Wrong payload hash"); + return; + } + HandleGarlicPayload (buf, payloadSize, from); + } - void GarlicDestination::HandleGarlicPayload (uint8_t * buf, size_t len, std::shared_ptr from) - { - const uint8_t * buf1 = buf; - int numCloves = buf[0]; - LogPrint (numCloves," cloves"); - buf++; - for (int i = 0; i < numCloves; i++) - { - // delivery instructions - uint8_t flag = buf[0]; - buf++; // flag - if (flag & 0x80) // encrypted? - { - // TODO: implement - LogPrint ("Clove encrypted"); - buf += 32; - } - GarlicDeliveryType deliveryType = (GarlicDeliveryType)((flag >> 5) & 0x03); - switch (deliveryType) - { - case eGarlicDeliveryTypeLocal: - LogPrint ("Garlic type local"); - HandleI2NPMessage (buf, len, from); - break; - case eGarlicDeliveryTypeDestination: - LogPrint ("Garlic type destination"); - buf += 32; // destination. check it later or for multiple destinations - HandleI2NPMessage (buf, len, from); - break; - case eGarlicDeliveryTypeTunnel: - { - LogPrint ("Garlic type tunnel"); - // gwHash and gwTunnel sequence is reverted - uint8_t * gwHash = buf; - buf += 32; - uint32_t gwTunnel = bufbe32toh (buf); - buf += 4; - std::shared_ptr tunnel; - if (from && from->GetTunnelPool ()) - tunnel = from->GetTunnelPool ()->GetNextOutboundTunnel (); - if (tunnel) // we have send it through an outbound tunnel - { - auto msg = CreateI2NPMessage (buf, GetI2NPMessageLength (buf), from); - tunnel->SendTunnelDataMsg (gwHash, gwTunnel, msg); - } - else - LogPrint ("No outbound tunnels available for garlic clove"); - break; - } - case eGarlicDeliveryTypeRouter: - LogPrint ("Garlic type router not supported"); - buf += 32; - break; - default: - LogPrint ("Unknow garlic delivery type ", (int)deliveryType); - } - buf += GetI2NPMessageLength (buf); // I2NP - buf += 4; // CloveID - buf += 8; // Date - buf += 3; // Certificate - if (buf - buf1 > (int)len) - { - LogPrint (eLogError, "Garlic clove is too long"); - break; - } - } - } - - std::shared_ptr GarlicDestination::WrapMessage (std::shared_ptr destination, - std::shared_ptr msg, bool attachLeaseSet) - { - auto session = GetRoutingSession (destination, attachLeaseSet); // 32 tags by default - return session->WrapSingleMessage (msg); - } + void GarlicDestination::HandleGarlicPayload (uint8_t * buf, size_t len, std::shared_ptr from) + { + const uint8_t * buf1 = buf; + int numCloves = buf[0]; + LogPrint (numCloves," cloves"); + buf++; + for (int i = 0; i < numCloves; i++) + { + // delivery instructions + uint8_t flag = buf[0]; + buf++; // flag + if (flag & 0x80) // encrypted? + { + // TODO: implement + LogPrint ("Clove encrypted"); + buf += 32; + } + GarlicDeliveryType deliveryType = (GarlicDeliveryType)((flag >> 5) & 0x03); + switch (deliveryType) + { + case eGarlicDeliveryTypeLocal: + LogPrint ("Garlic type local"); + HandleI2NPMessage (buf, len, from); + break; + case eGarlicDeliveryTypeDestination: + LogPrint ("Garlic type destination"); + buf += 32; // destination. check it later or for multiple destinations + HandleI2NPMessage (buf, len, from); + break; + case eGarlicDeliveryTypeTunnel: + { + LogPrint ("Garlic type tunnel"); + // gwHash and gwTunnel sequence is reverted + uint8_t * gwHash = buf; + buf += 32; + uint32_t gwTunnel = bufbe32toh (buf); + buf += 4; + std::shared_ptr tunnel; + if (from && from->GetTunnelPool ()) + tunnel = from->GetTunnelPool ()->GetNextOutboundTunnel (); + if (tunnel) // we have send it through an outbound tunnel + { + auto msg = CreateI2NPMessage (buf, GetI2NPMessageLength (buf), from); + tunnel->SendTunnelDataMsg (gwHash, gwTunnel, msg); + } + else + LogPrint ("No outbound tunnels available for garlic clove"); + break; + } + case eGarlicDeliveryTypeRouter: + LogPrint ("Garlic type router not supported"); + buf += 32; + break; + default: + LogPrint ("Unknow garlic delivery type ", (int)deliveryType); + } + buf += GetI2NPMessageLength (buf); // I2NP + buf += 4; // CloveID + buf += 8; // Date + buf += 3; // Certificate + if (buf - buf1 > (int)len) + { + LogPrint (eLogError, "Garlic clove is too long"); + break; + } + } + } + + std::shared_ptr GarlicDestination::WrapMessage (std::shared_ptr destination, + std::shared_ptr msg, bool attachLeaseSet) + { + auto session = GetRoutingSession (destination, attachLeaseSet); // 32 tags by default + return session->WrapSingleMessage (msg); + } - std::shared_ptr GarlicDestination::GetRoutingSession ( - std::shared_ptr destination, bool attachLeaseSet) - { - auto it = m_Sessions.find (destination->GetIdentHash ()); - std::shared_ptr session; - if (it != m_Sessions.end ()) - session = it->second; - if (!session) - { - session = std::make_shared (this, destination, - attachLeaseSet ? 40 : 4, attachLeaseSet); // 40 tags for connections and 4 for LS requests - std::unique_lock l(m_SessionsMutex); - m_Sessions[destination->GetIdentHash ()] = session; - } - return session; - } - - void GarlicDestination::CleanupRoutingSessions () - { - std::unique_lock l(m_SessionsMutex); - for (auto it = m_Sessions.begin (); it != m_Sessions.end ();) - { - if (!it->second->CleanupExpiredTags ()) - { - LogPrint (eLogInfo, "Routing session to ", it->first.ToBase32 (), " deleted"); - it = m_Sessions.erase (it); - } - else - it++; - } - } - - void GarlicDestination::RemoveCreatedSession (uint32_t msgID) - { - m_CreatedSessions.erase (msgID); - } + std::shared_ptr GarlicDestination::GetRoutingSession ( + std::shared_ptr destination, bool attachLeaseSet) + { + auto it = m_Sessions.find (destination->GetIdentHash ()); + std::shared_ptr session; + if (it != m_Sessions.end ()) + session = it->second; + if (!session) + { + session = std::make_shared (this, destination, + attachLeaseSet ? 40 : 4, attachLeaseSet); // 40 tags for connections and 4 for LS requests + std::unique_lock l(m_SessionsMutex); + m_Sessions[destination->GetIdentHash ()] = session; + } + return session; + } + + void GarlicDestination::CleanupRoutingSessions () + { + std::unique_lock l(m_SessionsMutex); + for (auto it = m_Sessions.begin (); it != m_Sessions.end ();) + { + if (!it->second->CleanupExpiredTags ()) + { + LogPrint (eLogInfo, "Routing session to ", it->first.ToBase32 (), " deleted"); + it = m_Sessions.erase (it); + } + else + it++; + } + } + + void GarlicDestination::RemoveCreatedSession (uint32_t msgID) + { + m_CreatedSessions.erase (msgID); + } - void GarlicDestination::DeliveryStatusSent (std::shared_ptr session, uint32_t msgID) - { - m_CreatedSessions[msgID] = session; - } + void GarlicDestination::DeliveryStatusSent (std::shared_ptr session, uint32_t msgID) + { + m_CreatedSessions[msgID] = session; + } - void GarlicDestination::HandleDeliveryStatusMessage (std::shared_ptr msg) - { - uint32_t msgID = bufbe32toh (msg->GetPayload ()); - { - auto it = m_CreatedSessions.find (msgID); - if (it != m_CreatedSessions.end ()) - { - it->second->MessageConfirmed (msgID); - m_CreatedSessions.erase (it); - LogPrint (eLogInfo, "Garlic message ", msgID, " acknowledged"); - } - } - } + void GarlicDestination::HandleDeliveryStatusMessage (std::shared_ptr msg) + { + uint32_t msgID = bufbe32toh (msg->GetPayload ()); + { + auto it = m_CreatedSessions.find (msgID); + if (it != m_CreatedSessions.end ()) + { + it->second->MessageConfirmed (msgID); + m_CreatedSessions.erase (it); + LogPrint (eLogInfo, "Garlic message ", msgID, " acknowledged"); + } + } + } - void GarlicDestination::SetLeaseSetUpdated () - { - std::unique_lock l(m_SessionsMutex); - for (auto it: m_Sessions) - it.second->SetLeaseSetUpdated (); - } + void GarlicDestination::SetLeaseSetUpdated () + { + std::unique_lock l(m_SessionsMutex); + for (auto it: m_Sessions) + it.second->SetLeaseSetUpdated (); + } - void GarlicDestination::ProcessGarlicMessage (std::shared_ptr msg) - { - HandleGarlicMessage (msg); - } + void GarlicDestination::ProcessGarlicMessage (std::shared_ptr msg) + { + HandleGarlicMessage (msg); + } - void GarlicDestination::ProcessDeliveryStatusMessage (std::shared_ptr msg) - { - HandleDeliveryStatusMessage (msg); - } + void GarlicDestination::ProcessDeliveryStatusMessage (std::shared_ptr msg) + { + HandleDeliveryStatusMessage (msg); + } -} +} } diff --git a/Garlic.h b/Garlic.h index 73c8309d..7d02c21a 100644 --- a/Garlic.h +++ b/Garlic.h @@ -16,154 +16,154 @@ #include "Identity.h" namespace i2p -{ +{ namespace garlic { - - enum GarlicDeliveryType - { - eGarlicDeliveryTypeLocal = 0, - eGarlicDeliveryTypeDestination = 1, - eGarlicDeliveryTypeRouter = 2, - eGarlicDeliveryTypeTunnel = 3 - }; + + enum GarlicDeliveryType + { + eGarlicDeliveryTypeLocal = 0, + eGarlicDeliveryTypeDestination = 1, + eGarlicDeliveryTypeRouter = 2, + eGarlicDeliveryTypeTunnel = 3 + }; #pragma pack(1) - struct ElGamalBlock - { - uint8_t sessionKey[32]; - uint8_t preIV[32]; - uint8_t padding[158]; - }; -#pragma pack() + struct ElGamalBlock + { + uint8_t sessionKey[32]; + uint8_t preIV[32]; + uint8_t padding[158]; + }; +#pragma pack() - const int INCOMING_TAGS_EXPIRATION_TIMEOUT = 960; // 16 minutes - const int OUTGOING_TAGS_EXPIRATION_TIMEOUT = 720; // 12 minutes - const int LEASET_CONFIRMATION_TIMEOUT = 4000; // in milliseconds - - struct SessionTag: public i2p::data::Tag<32> - { - SessionTag (const uint8_t * buf, uint32_t ts = 0): Tag<32>(buf), creationTime (ts) {}; - SessionTag () = default; - SessionTag (const SessionTag& ) = default; - SessionTag& operator= (const SessionTag& ) = default; + const int INCOMING_TAGS_EXPIRATION_TIMEOUT = 960; // 16 minutes + const int OUTGOING_TAGS_EXPIRATION_TIMEOUT = 720; // 12 minutes + const int LEASET_CONFIRMATION_TIMEOUT = 4000; // in milliseconds + + struct SessionTag: public i2p::data::Tag<32> + { + SessionTag (const uint8_t * buf, uint32_t ts = 0): Tag<32>(buf), creationTime (ts) {}; + SessionTag () = default; + SessionTag (const SessionTag& ) = default; + SessionTag& operator= (const SessionTag& ) = default; #ifndef _WIN32 - SessionTag (SessionTag&& ) = default; - SessionTag& operator= (SessionTag&& ) = default; + SessionTag (SessionTag&& ) = default; + SessionTag& operator= (SessionTag&& ) = default; #endif - uint32_t creationTime; // seconds since epoch - }; - - class GarlicDestination; - class GarlicRoutingSession: public std::enable_shared_from_this - { - enum LeaseSetUpdateStatus - { - eLeaseSetUpToDate = 0, - eLeaseSetUpdated, - eLeaseSetSubmitted, - eLeaseSetDoNotSend - }; - - struct UnconfirmedTags - { - UnconfirmedTags (int n): numTags (n), tagsCreationTime (0) { sessionTags = new SessionTag[numTags]; }; - ~UnconfirmedTags () { delete[] sessionTags; }; - int numTags; - SessionTag * sessionTags; - uint32_t tagsCreationTime; - }; + uint32_t creationTime; // seconds since epoch + }; + + class GarlicDestination; + class GarlicRoutingSession: public std::enable_shared_from_this + { + enum LeaseSetUpdateStatus + { + eLeaseSetUpToDate = 0, + eLeaseSetUpdated, + eLeaseSetSubmitted, + eLeaseSetDoNotSend + }; + + struct UnconfirmedTags + { + UnconfirmedTags (int n): numTags (n), tagsCreationTime (0) { sessionTags = new SessionTag[numTags]; }; + ~UnconfirmedTags () { delete[] sessionTags; }; + int numTags; + SessionTag * sessionTags; + uint32_t tagsCreationTime; + }; - public: + public: - GarlicRoutingSession (GarlicDestination * owner, std::shared_ptr destination, - int numTags, bool attachLeaseSet); - GarlicRoutingSession (const uint8_t * sessionKey, const SessionTag& sessionTag); // one time encryption - ~GarlicRoutingSession (); - std::shared_ptr WrapSingleMessage (std::shared_ptr msg); - void MessageConfirmed (uint32_t msgID); - bool CleanupExpiredTags (); // returns true if something left + GarlicRoutingSession (GarlicDestination * owner, std::shared_ptr destination, + int numTags, bool attachLeaseSet); + GarlicRoutingSession (const uint8_t * sessionKey, const SessionTag& sessionTag); // one time encryption + ~GarlicRoutingSession (); + std::shared_ptr WrapSingleMessage (std::shared_ptr msg); + void MessageConfirmed (uint32_t msgID); + bool CleanupExpiredTags (); // returns true if something left - void SetLeaseSetUpdated () - { - if (m_LeaseSetUpdateStatus != eLeaseSetDoNotSend) m_LeaseSetUpdateStatus = eLeaseSetUpdated; - }; - - private: + void SetLeaseSetUpdated () + { + if (m_LeaseSetUpdateStatus != eLeaseSetDoNotSend) m_LeaseSetUpdateStatus = eLeaseSetUpdated; + }; + + private: - size_t CreateAESBlock (uint8_t * buf, std::shared_ptr msg); - size_t CreateGarlicPayload (uint8_t * payload, std::shared_ptr msg, UnconfirmedTags * newTags); - size_t CreateGarlicClove (uint8_t * buf, std::shared_ptr msg, bool isDestination); - size_t CreateDeliveryStatusClove (uint8_t * buf, uint32_t msgID); + size_t CreateAESBlock (uint8_t * buf, std::shared_ptr msg); + size_t CreateGarlicPayload (uint8_t * payload, std::shared_ptr msg, UnconfirmedTags * newTags); + size_t CreateGarlicClove (uint8_t * buf, std::shared_ptr msg, bool isDestination); + size_t CreateDeliveryStatusClove (uint8_t * buf, uint32_t msgID); - void TagsConfirmed (uint32_t msgID); - UnconfirmedTags * GenerateSessionTags (); + void TagsConfirmed (uint32_t msgID); + UnconfirmedTags * GenerateSessionTags (); - private: + private: - GarlicDestination * m_Owner; - std::shared_ptr m_Destination; - i2p::crypto::AESKey m_SessionKey; - std::list m_SessionTags; - int m_NumTags; - std::map m_UnconfirmedTagsMsgs; - - LeaseSetUpdateStatus m_LeaseSetUpdateStatus; - uint32_t m_LeaseSetUpdateMsgID; - uint64_t m_LeaseSetSubmissionTime; // in milliseconds - - i2p::crypto::CBCEncryption m_Encryption; - CryptoPP::AutoSeededRandomPool m_Rnd; - }; - - class GarlicDestination: public i2p::data::LocalDestination - { - public: + GarlicDestination * m_Owner; + std::shared_ptr m_Destination; + i2p::crypto::AESKey m_SessionKey; + std::list m_SessionTags; + int m_NumTags; + std::map m_UnconfirmedTagsMsgs; + + LeaseSetUpdateStatus m_LeaseSetUpdateStatus; + uint32_t m_LeaseSetUpdateMsgID; + uint64_t m_LeaseSetSubmissionTime; // in milliseconds + + i2p::crypto::CBCEncryption m_Encryption; + CryptoPP::AutoSeededRandomPool m_Rnd; + }; + + class GarlicDestination: public i2p::data::LocalDestination + { + public: - GarlicDestination (): m_LastTagsCleanupTime (0) {}; - ~GarlicDestination (); + GarlicDestination (): m_LastTagsCleanupTime (0) {}; + ~GarlicDestination (); - std::shared_ptr GetRoutingSession (std::shared_ptr destination, bool attachLeaseSet); - void CleanupRoutingSessions (); - void RemoveCreatedSession (uint32_t msgID); - std::shared_ptr WrapMessage (std::shared_ptr destination, - std::shared_ptr msg, bool attachLeaseSet = false); + std::shared_ptr GetRoutingSession (std::shared_ptr destination, bool attachLeaseSet); + void CleanupRoutingSessions (); + void RemoveCreatedSession (uint32_t msgID); + std::shared_ptr WrapMessage (std::shared_ptr destination, + std::shared_ptr msg, bool attachLeaseSet = false); - void AddSessionKey (const uint8_t * key, const uint8_t * tag); // one tag - virtual bool SubmitSessionKey (const uint8_t * key, const uint8_t * tag); // from different thread - void DeliveryStatusSent (std::shared_ptr session, uint32_t msgID); - - virtual void ProcessGarlicMessage (std::shared_ptr msg); - virtual void ProcessDeliveryStatusMessage (std::shared_ptr msg); - virtual void SetLeaseSetUpdated (); - - virtual std::shared_ptr GetLeaseSet () = 0; // TODO - virtual std::shared_ptr GetTunnelPool () const = 0; - virtual void HandleI2NPMessage (const uint8_t * buf, size_t len, std::shared_ptr from) = 0; - - protected: + void AddSessionKey (const uint8_t * key, const uint8_t * tag); // one tag + virtual bool SubmitSessionKey (const uint8_t * key, const uint8_t * tag); // from different thread + void DeliveryStatusSent (std::shared_ptr session, uint32_t msgID); + + virtual void ProcessGarlicMessage (std::shared_ptr msg); + virtual void ProcessDeliveryStatusMessage (std::shared_ptr msg); + virtual void SetLeaseSetUpdated (); + + virtual std::shared_ptr GetLeaseSet () = 0; // TODO + virtual std::shared_ptr GetTunnelPool () const = 0; + virtual void HandleI2NPMessage (const uint8_t * buf, size_t len, std::shared_ptr from) = 0; + + protected: - void HandleGarlicMessage (std::shared_ptr msg); - void HandleDeliveryStatusMessage (std::shared_ptr msg); - - private: + void HandleGarlicMessage (std::shared_ptr msg); + void HandleDeliveryStatusMessage (std::shared_ptr msg); + + private: - void HandleAESBlock (uint8_t * buf, size_t len, std::shared_ptr decryption, - std::shared_ptr from); - void HandleGarlicPayload (uint8_t * buf, size_t len, std::shared_ptr from); + void HandleAESBlock (uint8_t * buf, size_t len, std::shared_ptr decryption, + std::shared_ptr from); + void HandleGarlicPayload (uint8_t * buf, size_t len, std::shared_ptr from); - private: - - // outgoing sessions - std::mutex m_SessionsMutex; - std::map > m_Sessions; - // incoming - std::map> m_Tags; - uint32_t m_LastTagsCleanupTime; - // DeliveryStatus - std::map > m_CreatedSessions; // msgID -> session - }; -} + private: + + // outgoing sessions + std::mutex m_SessionsMutex; + std::map > m_Sessions; + // incoming + std::map> m_Tags; + uint32_t m_LastTagsCleanupTime; + // DeliveryStatus + std::map > m_CreatedSessions; // msgID -> session + }; +} } #endif diff --git a/HTTPProxy.cpp b/HTTPProxy.cpp index 8e0ef9ad..25f9b3f1 100644 --- a/HTTPProxy.cpp +++ b/HTTPProxy.cpp @@ -17,283 +17,283 @@ namespace i2p { namespace proxy { - static const size_t http_buffer_size = 8192; - class HTTPProxyHandler: public i2p::client::I2PServiceHandler, public std::enable_shared_from_this - { - private: - enum state - { - GET_METHOD, - GET_HOSTNAME, - GET_HTTPV, - GET_HTTPVNL, //TODO: fallback to finding HOst: header if needed - DONE - }; + static const size_t http_buffer_size = 8192; + class HTTPProxyHandler: public i2p::client::I2PServiceHandler, public std::enable_shared_from_this + { + private: + enum state + { + GET_METHOD, + GET_HOSTNAME, + GET_HTTPV, + GET_HTTPVNL, //TODO: fallback to finding HOst: header if needed + DONE + }; - void EnterState(state nstate); - bool HandleData(uint8_t *http_buff, std::size_t len); - void HandleSockRecv(const boost::system::error_code & ecode, std::size_t bytes_transfered); - void Terminate(); - void AsyncSockRead(); - void HTTPRequestFailed(/*std::string message*/); - void ExtractRequest(); - bool ValidateHTTPRequest(); - void HandleJumpServices(); - bool CreateHTTPRequest(uint8_t *http_buff, std::size_t len); - void SentHTTPFailed(const boost::system::error_code & ecode); - void HandleStreamRequestComplete (std::shared_ptr stream); + void EnterState(state nstate); + bool HandleData(uint8_t *http_buff, std::size_t len); + void HandleSockRecv(const boost::system::error_code & ecode, std::size_t bytes_transfered); + void Terminate(); + void AsyncSockRead(); + void HTTPRequestFailed(/*std::string message*/); + void ExtractRequest(); + bool ValidateHTTPRequest(); + void HandleJumpServices(); + bool CreateHTTPRequest(uint8_t *http_buff, std::size_t len); + void SentHTTPFailed(const boost::system::error_code & ecode); + void HandleStreamRequestComplete (std::shared_ptr stream); - uint8_t m_http_buff[http_buffer_size]; - std::shared_ptr m_sock; - std::string m_request; //Data left to be sent - std::string m_url; //URL - std::string m_method; //Method - std::string m_version; //HTTP version - std::string m_address; //Address - std::string m_path; //Path - int m_port; //Port - state m_state;//Parsing state + uint8_t m_http_buff[http_buffer_size]; + std::shared_ptr m_sock; + std::string m_request; //Data left to be sent + std::string m_url; //URL + std::string m_method; //Method + std::string m_version; //HTTP version + std::string m_address; //Address + std::string m_path; //Path + int m_port; //Port + state m_state;//Parsing state - public: + public: - HTTPProxyHandler(HTTPProxyServer * parent, std::shared_ptr sock) : - I2PServiceHandler(parent), m_sock(sock) - { EnterState(GET_METHOD); } - ~HTTPProxyHandler() { Terminate(); } - void Handle () { AsyncSockRead(); } - }; + HTTPProxyHandler(HTTPProxyServer * parent, std::shared_ptr sock) : + I2PServiceHandler(parent), m_sock(sock) + { EnterState(GET_METHOD); } + ~HTTPProxyHandler() { Terminate(); } + void Handle () { AsyncSockRead(); } + }; - void HTTPProxyHandler::AsyncSockRead() - { - LogPrint(eLogDebug,"--- HTTP Proxy async sock read"); - if(m_sock) { - m_sock->async_receive(boost::asio::buffer(m_http_buff, http_buffer_size), - std::bind(&HTTPProxyHandler::HandleSockRecv, shared_from_this(), - std::placeholders::_1, std::placeholders::_2)); - } else { - LogPrint(eLogError,"--- HTTP Proxy no socket for read"); - } - } + void HTTPProxyHandler::AsyncSockRead() + { + LogPrint(eLogDebug,"--- HTTP Proxy async sock read"); + if(m_sock) { + m_sock->async_receive(boost::asio::buffer(m_http_buff, http_buffer_size), + std::bind(&HTTPProxyHandler::HandleSockRecv, shared_from_this(), + std::placeholders::_1, std::placeholders::_2)); + } else { + LogPrint(eLogError,"--- HTTP Proxy no socket for read"); + } + } - void HTTPProxyHandler::Terminate() { - if (Kill()) return; - if (m_sock) - { - LogPrint(eLogDebug,"--- HTTP Proxy close sock"); - m_sock->close(); - m_sock = nullptr; - } - Done(shared_from_this()); - } + void HTTPProxyHandler::Terminate() { + if (Kill()) return; + if (m_sock) + { + LogPrint(eLogDebug,"--- HTTP Proxy close sock"); + m_sock->close(); + m_sock = nullptr; + } + Done(shared_from_this()); + } - /* All hope is lost beyond this point */ - //TODO: handle this apropriately - void HTTPProxyHandler::HTTPRequestFailed(/*HTTPProxyHandler::errTypes error*/) - { - static std::string response = "HTTP/1.0 500 Internal Server Error\r\nContent-type: text/html\r\nContent-length: 0\r\n"; - boost::asio::async_write(*m_sock, boost::asio::buffer(response,response.size()), - std::bind(&HTTPProxyHandler::SentHTTPFailed, shared_from_this(), std::placeholders::_1)); - } + /* All hope is lost beyond this point */ + //TODO: handle this apropriately + void HTTPProxyHandler::HTTPRequestFailed(/*HTTPProxyHandler::errTypes error*/) + { + static std::string response = "HTTP/1.0 500 Internal Server Error\r\nContent-type: text/html\r\nContent-length: 0\r\n"; + boost::asio::async_write(*m_sock, boost::asio::buffer(response,response.size()), + std::bind(&HTTPProxyHandler::SentHTTPFailed, shared_from_this(), std::placeholders::_1)); + } - void HTTPProxyHandler::EnterState(HTTPProxyHandler::state nstate) - { - m_state = nstate; - } + void HTTPProxyHandler::EnterState(HTTPProxyHandler::state nstate) + { + m_state = nstate; + } - void HTTPProxyHandler::ExtractRequest() - { - LogPrint(eLogDebug,"--- HTTP Proxy method is: ", m_method, "\nRequest is: ", m_url); - std::string server=""; - std::string port="80"; - boost::regex rHTTP("http://(.*?)(:(\\d+))?(/.*)"); - boost::smatch m; - std::string path; - if(boost::regex_search(m_url, m, rHTTP, boost::match_extra)) - { - server=m[1].str(); - if (m[2].str() != "") port=m[3].str(); - path=m[4].str(); - } - LogPrint(eLogDebug,"--- HTTP Proxy server is: ",server, " port is: ", port, "\n path is: ",path); - m_address = server; - m_port = boost::lexical_cast(port); - m_path = path; - } + void HTTPProxyHandler::ExtractRequest() + { + LogPrint(eLogDebug,"--- HTTP Proxy method is: ", m_method, "\nRequest is: ", m_url); + std::string server=""; + std::string port="80"; + boost::regex rHTTP("http://(.*?)(:(\\d+))?(/.*)"); + boost::smatch m; + std::string path; + if(boost::regex_search(m_url, m, rHTTP, boost::match_extra)) + { + server=m[1].str(); + if (m[2].str() != "") port=m[3].str(); + path=m[4].str(); + } + LogPrint(eLogDebug,"--- HTTP Proxy server is: ",server, " port is: ", port, "\n path is: ",path); + m_address = server; + m_port = boost::lexical_cast(port); + m_path = path; + } - bool HTTPProxyHandler::ValidateHTTPRequest() - { - if ( m_version != "HTTP/1.0" && m_version != "HTTP/1.1" ) - { - LogPrint(eLogError,"--- HTTP Proxy unsupported version: ", m_version); - HTTPRequestFailed(); //TODO: send right stuff - return false; - } - return true; - } + bool HTTPProxyHandler::ValidateHTTPRequest() + { + if ( m_version != "HTTP/1.0" && m_version != "HTTP/1.1" ) + { + LogPrint(eLogError,"--- HTTP Proxy unsupported version: ", m_version); + HTTPRequestFailed(); //TODO: send right stuff + return false; + } + return true; + } - void HTTPProxyHandler::HandleJumpServices() - { - static const char * helpermark1 = "?i2paddresshelper="; - static const char * helpermark2 = "&i2paddresshelper="; - size_t addressHelperPos1 = m_path.rfind (helpermark1); - size_t addressHelperPos2 = m_path.rfind (helpermark2); - size_t addressHelperPos; - if (addressHelperPos1 == std::string::npos) - { - if (addressHelperPos2 == std::string::npos) - return; //Not a jump service - else - addressHelperPos = addressHelperPos2; - } - else - { - if (addressHelperPos2 == std::string::npos) - addressHelperPos = addressHelperPos1; - else if ( addressHelperPos1 > addressHelperPos2 ) - addressHelperPos = addressHelperPos1; - else - addressHelperPos = addressHelperPos2; - } - auto base64 = m_path.substr (addressHelperPos + strlen(helpermark1)); - base64 = i2p::util::http::urlDecode(base64); //Some of the symbols may be urlencoded - LogPrint (eLogDebug,"Jump service for ", m_address, " found at ", base64, ". Inserting to address book"); - //TODO: this is very dangerous and broken. We should ask the user before doing anything see http://pastethis.i2p/raw/pn5fL4YNJL7OSWj3Sc6N/ - //TODO: we could redirect the user again to avoid dirtiness in the browser - i2p::client::context.GetAddressBook ().InsertAddress (m_address, base64); - m_path.erase(addressHelperPos); - } + void HTTPProxyHandler::HandleJumpServices() + { + static const char * helpermark1 = "?i2paddresshelper="; + static const char * helpermark2 = "&i2paddresshelper="; + size_t addressHelperPos1 = m_path.rfind (helpermark1); + size_t addressHelperPos2 = m_path.rfind (helpermark2); + size_t addressHelperPos; + if (addressHelperPos1 == std::string::npos) + { + if (addressHelperPos2 == std::string::npos) + return; //Not a jump service + else + addressHelperPos = addressHelperPos2; + } + else + { + if (addressHelperPos2 == std::string::npos) + addressHelperPos = addressHelperPos1; + else if ( addressHelperPos1 > addressHelperPos2 ) + addressHelperPos = addressHelperPos1; + else + addressHelperPos = addressHelperPos2; + } + auto base64 = m_path.substr (addressHelperPos + strlen(helpermark1)); + base64 = i2p::util::http::urlDecode(base64); //Some of the symbols may be urlencoded + LogPrint (eLogDebug,"Jump service for ", m_address, " found at ", base64, ". Inserting to address book"); + //TODO: this is very dangerous and broken. We should ask the user before doing anything see http://pastethis.i2p/raw/pn5fL4YNJL7OSWj3Sc6N/ + //TODO: we could redirect the user again to avoid dirtiness in the browser + i2p::client::context.GetAddressBook ().InsertAddress (m_address, base64); + m_path.erase(addressHelperPos); + } - bool HTTPProxyHandler::CreateHTTPRequest(uint8_t *http_buff, std::size_t len) - { - ExtractRequest(); //TODO: parse earlier - if (!ValidateHTTPRequest()) return false; - HandleJumpServices(); - m_request = m_method; - m_request.push_back(' '); - m_request += m_path; - m_request.push_back(' '); - m_request += m_version; - m_request.push_back('\r'); - m_request.push_back('\n'); - m_request.append("Connection: close\r\n"); - m_request.append(reinterpret_cast(http_buff),len); - return true; - } + bool HTTPProxyHandler::CreateHTTPRequest(uint8_t *http_buff, std::size_t len) + { + ExtractRequest(); //TODO: parse earlier + if (!ValidateHTTPRequest()) return false; + HandleJumpServices(); + m_request = m_method; + m_request.push_back(' '); + m_request += m_path; + m_request.push_back(' '); + m_request += m_version; + m_request.push_back('\r'); + m_request.push_back('\n'); + m_request.append("Connection: close\r\n"); + m_request.append(reinterpret_cast(http_buff),len); + return true; + } - bool HTTPProxyHandler::HandleData(uint8_t *http_buff, std::size_t len) - { - assert(len); // This should always be called with a least a byte left to parse - while (len > 0) - { - //TODO: fallback to finding HOst: header if needed - switch (m_state) - { - case GET_METHOD: - switch (*http_buff) - { - case ' ': EnterState(GET_HOSTNAME); break; - default: m_method.push_back(*http_buff); break; - } - break; - case GET_HOSTNAME: - switch (*http_buff) - { - case ' ': EnterState(GET_HTTPV); break; - default: m_url.push_back(*http_buff); break; - } - break; - case GET_HTTPV: - switch (*http_buff) - { - case '\r': EnterState(GET_HTTPVNL); break; - default: m_version.push_back(*http_buff); break; - } - break; - case GET_HTTPVNL: - switch (*http_buff) - { - case '\n': EnterState(DONE); break; - default: - LogPrint(eLogError,"--- HTTP Proxy rejected invalid request ending with: ", ((int)*http_buff)); - HTTPRequestFailed(); //TODO: add correct code - return false; - } - break; - default: - LogPrint(eLogError,"--- HTTP Proxy invalid state: ", m_state); - HTTPRequestFailed(); //TODO: add correct code 500 - return false; - } - http_buff++; - len--; - if (m_state == DONE) - return CreateHTTPRequest(http_buff,len); - } - return true; - } + bool HTTPProxyHandler::HandleData(uint8_t *http_buff, std::size_t len) + { + assert(len); // This should always be called with a least a byte left to parse + while (len > 0) + { + //TODO: fallback to finding HOst: header if needed + switch (m_state) + { + case GET_METHOD: + switch (*http_buff) + { + case ' ': EnterState(GET_HOSTNAME); break; + default: m_method.push_back(*http_buff); break; + } + break; + case GET_HOSTNAME: + switch (*http_buff) + { + case ' ': EnterState(GET_HTTPV); break; + default: m_url.push_back(*http_buff); break; + } + break; + case GET_HTTPV: + switch (*http_buff) + { + case '\r': EnterState(GET_HTTPVNL); break; + default: m_version.push_back(*http_buff); break; + } + break; + case GET_HTTPVNL: + switch (*http_buff) + { + case '\n': EnterState(DONE); break; + default: + LogPrint(eLogError,"--- HTTP Proxy rejected invalid request ending with: ", ((int)*http_buff)); + HTTPRequestFailed(); //TODO: add correct code + return false; + } + break; + default: + LogPrint(eLogError,"--- HTTP Proxy invalid state: ", m_state); + HTTPRequestFailed(); //TODO: add correct code 500 + return false; + } + http_buff++; + len--; + if (m_state == DONE) + return CreateHTTPRequest(http_buff,len); + } + return true; + } - void HTTPProxyHandler::HandleSockRecv(const boost::system::error_code & ecode, std::size_t len) - { - LogPrint(eLogDebug,"--- HTTP Proxy sock recv: ", len); - if(ecode) - { - LogPrint(eLogWarning," --- HTTP Proxy sock recv got error: ", ecode); + void HTTPProxyHandler::HandleSockRecv(const boost::system::error_code & ecode, std::size_t len) + { + LogPrint(eLogDebug,"--- HTTP Proxy sock recv: ", len); + if(ecode) + { + LogPrint(eLogWarning," --- HTTP Proxy sock recv got error: ", ecode); Terminate(); - return; - } + return; + } - if (HandleData(m_http_buff, len)) - { - if (m_state == DONE) - { - LogPrint(eLogInfo,"--- HTTP Proxy requested: ", m_url); - GetOwner()->CreateStream (std::bind (&HTTPProxyHandler::HandleStreamRequestComplete, - shared_from_this(), std::placeholders::_1), m_address, m_port); - } - else - AsyncSockRead(); - } + if (HandleData(m_http_buff, len)) + { + if (m_state == DONE) + { + LogPrint(eLogInfo,"--- HTTP Proxy requested: ", m_url); + GetOwner()->CreateStream (std::bind (&HTTPProxyHandler::HandleStreamRequestComplete, + shared_from_this(), std::placeholders::_1), m_address, m_port); + } + else + AsyncSockRead(); + } - } + } - void HTTPProxyHandler::SentHTTPFailed(const boost::system::error_code & ecode) - { - if (!ecode) - Terminate(); - else - { - LogPrint (eLogError,"--- HTTP Proxy Closing socket after sending failure because: ", ecode.message ()); - Terminate(); - } - } + void HTTPProxyHandler::SentHTTPFailed(const boost::system::error_code & ecode) + { + if (!ecode) + Terminate(); + else + { + LogPrint (eLogError,"--- HTTP Proxy Closing socket after sending failure because: ", ecode.message ()); + Terminate(); + } + } - void HTTPProxyHandler::HandleStreamRequestComplete (std::shared_ptr stream) - { - if (stream) - { - if (Kill()) return; - LogPrint (eLogInfo,"--- HTTP Proxy New I2PTunnel connection"); - auto connection = std::make_shared(GetOwner(), m_sock, stream); - GetOwner()->AddHandler (connection); - connection->I2PConnect (reinterpret_cast(m_request.data()), m_request.size()); - Done(shared_from_this()); - } - else - { - LogPrint (eLogError,"--- HTTP Proxy Issue when creating the stream, check the previous warnings for more info."); - HTTPRequestFailed(); // TODO: Send correct error message host unreachable - } - } + void HTTPProxyHandler::HandleStreamRequestComplete (std::shared_ptr stream) + { + if (stream) + { + if (Kill()) return; + LogPrint (eLogInfo,"--- HTTP Proxy New I2PTunnel connection"); + auto connection = std::make_shared(GetOwner(), m_sock, stream); + GetOwner()->AddHandler (connection); + connection->I2PConnect (reinterpret_cast(m_request.data()), m_request.size()); + Done(shared_from_this()); + } + else + { + LogPrint (eLogError,"--- HTTP Proxy Issue when creating the stream, check the previous warnings for more info."); + HTTPRequestFailed(); // TODO: Send correct error message host unreachable + } + } - HTTPProxyServer::HTTPProxyServer(int port, std::shared_ptr localDestination): - TCPIPAcceptor(port, localDestination ? localDestination : i2p::client::context.GetSharedLocalDestination ()) - { - } - - std::shared_ptr HTTPProxyServer::CreateHandler(std::shared_ptr socket) - { - return std::make_shared (this, socket); - } + HTTPProxyServer::HTTPProxyServer(int port, std::shared_ptr localDestination): + TCPIPAcceptor(port, localDestination ? localDestination : i2p::client::context.GetSharedLocalDestination ()) + { + } + + std::shared_ptr HTTPProxyServer::CreateHandler(std::shared_ptr socket) + { + return std::make_shared (this, socket); + } } } diff --git a/HTTPProxy.h b/HTTPProxy.h index 6c0d0a6f..33dc898e 100644 --- a/HTTPProxy.h +++ b/HTTPProxy.h @@ -12,20 +12,20 @@ namespace i2p { namespace proxy { - class HTTPProxyServer: public i2p::client::TCPIPAcceptor - { - public: + class HTTPProxyServer: public i2p::client::TCPIPAcceptor + { + public: - HTTPProxyServer(int port, std::shared_ptr localDestination = nullptr); - ~HTTPProxyServer() {}; + HTTPProxyServer(int port, std::shared_ptr localDestination = nullptr); + ~HTTPProxyServer() {}; - protected: - // Implements TCPIPAcceptor - std::shared_ptr CreateHandler(std::shared_ptr socket); - const char* GetName() { return "HTTP Proxy"; } - }; + protected: + // Implements TCPIPAcceptor + std::shared_ptr CreateHandler(std::shared_ptr socket); + const char* GetName() { return "HTTP Proxy"; } + }; - typedef HTTPProxyServer HTTPProxy; + typedef HTTPProxyServer HTTPProxy; } } diff --git a/HTTPServer.cpp b/HTTPServer.cpp index db0a1338..cb1cafd6 100644 --- a/HTTPServer.cpp +++ b/HTTPServer.cpp @@ -22,1084 +22,1084 @@ namespace i2p namespace util { - const std::string HTTPConnection::itoopieImage = - "\"ICToopie"; + const std::string HTTPConnection::itoopieImage = + "\"ICToopie"; - const std::string HTTPConnection::itoopieFavicon = - "data:image/vnd.microsoft.icon;base64," - "AAABAAEAQEAAAAEAIAAoQgAAFgAAACgAAABAAAAAgAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" - "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" - "AAAAAAAAAAAAAAAAAAAAAAAABsAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgQAAAIMAAACEAAAAg" - "wAAAIAAAACCAAAASQAAACUAAAAmAAAAOwAAAFIAAABgAAAAVAAAACcAAAAVAAAADQAAAAcAAAADAAAA" - "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" - "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" - "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACjAAAA/wAAAP4AAAD+AAAA/gAAAP4AAAD+A" - "AAA/gAAAP4AAAD+AAAA/gAAAP4AAAD+AAAA/gAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/" - "AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAACbAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" - "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" - "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAZAAAAP8AA" - "AD+AAAA/gAAAP4AAAD+AAAA/gAAAP4AAAD+AAAA/gAAAP4AAAD+AAAA/gAAAP4AAAD+AAAA/gAAAP4A" - "AAD+AAAA/gAAAP4AAAD+AAAA/gAAAP4AAAD+AAAA/gAAAP4AAAD+AAAA/gAAAP4AAAD+AAAA/wAAAAA" - "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" - "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" - "AAAKQAAAAAAAAAAAAAAAADbAAAA/gAAAP4AAAD+AAAA/gAAAP4AAAD+AAAA/gAAAP8AAAD/AAAA/gAA" - "AP4AAAD+AAAA/gAAAP4AAAD+AAAA/gAAAP4AAAD+AAIA/gAAAPwAAAD/AAAA/gAAAP4AAAD+AAAA/gA" - "AAP4AAAD+AAAA/wAAAFkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" - "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" - "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAApAAAAAAAAAAAAHAAAAP8AAAD+AAAA/gAAAPwACwD9ABEA" - "/QASAPgAFgD4ABUA+AATAP0ADQD8AAIA/AAAAP8AAAD+AAAA/gAAAP8ADQD7ACMA/QAlAP8AJwD/ACI" - "A/QATAPwAAAD8AAAA/gAAAP4AAAD+AAAA/gAAAKIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" - "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" - "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADe" - "AAAA/gAAAPwAJAD+AAwA+wAAAP8AAAD/AAAA/wAEAPwAEgD8ACQA/gAmAP8AHAD8AAEA/gAAAP4ACwD" - "8ACUA/wAkAP8AFwD8AAkA/AAQAP0AIwD+ACQA/wANAPsAAAD+AAAA/gAAAP8AAAAAAAAAAAAAAAAAAA" - "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" - "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" - "AAAAAAAAAAAAAAEAAABNAAAA/wAAAP8AGAD8AAAA/QAYAPMARQDyAFEA9wBOAPIAPAD0ABEA8QAAAP8" - "AGgD7ACMA/wAVAP0AAAD9ACMA/QAkAP8ADwD9AAgA+ABBAPUALADyAAAA/AAUAPwAJgD/ABgA/AAAAP" - "0AAAD/AAAAdgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" - "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" - "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA+AAAAP4ADQD8AAgA/wAqAPAAcAD/AGoA/wB" - "qAP8AagD/AGoA/wBvAP8ATwDvAAAA/QAbAP0AEwD8AAYA/QAkAP8AGAD8AAsA9wBuAPgAagD/AGsA/w" - "BjAPkADAD3AAQA/QAiAP4AGgD9AAAA/gAAAP8AAABgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" - "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" - "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAZgAAAP8AAAD+ABY" - "A+wAQAPQAcQD/AGoA/wBqAP8AagD/AGoA/wBqAP8AagD/AGoA/wBTAPEAAAD/AAcA/AAMAPwAIQD9AA" - "AA+gBoAPgAagD/AGoA/wBqAP8AagD/AHAA/gAuAO0AAAD/AB8A/QAPAPwAAAD+AAAA+AAAAAAAAAAAA" - "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" - "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" - "AAAAAAAAAANIAAAD+AAwA+QAAAP0AZQD1AGoA/wBqAP8AagD/AGoA/wBqAP8AagD/AGoA/wBqAP8Abg" - "D/ABcA8QAAAP4ACAD9AAAA+QBeAPkAagD/AGoA/wBqAP8AagD/AGoA/wBqAP8AbQD/ADkA7wAAAP8AJ" - "AD9AAAA/gAAAP8AAAAfAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" - "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" - "AAAAAAAAAAAAAAAAALi8AAAAAAAAAAAAAAAD/AAAA/QAAAP8AKwD1AGsA/wBqAP8AagD/AGoA/wBqAP" - "8AagD/AGoA/wBqAP8AagD/AGoA/wA9APUAAAD/AAAA/wBKAPMAagD/AGoA/wBqAP8AagD/AGoA/wBqA" - "P8AagD/AGoA/wBxAP8ADgD1ABMA/AAHAPsAAAD/AAAARQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" - "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" - "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAzPAIAAAAgAAAA/wAAAPwAAAD/AGQA9A" - "BqAP8AagD/AGoA/wBqAP8AagD/AGoA/wBqAP8AagD/AGoA/wBqAP8ASQD1AAAA/wASAPMAcAD/AGoA/" - "wBqAP8AagD/AGoA/wBqAP8AagD/AGoA/wBqAP8AawD/ACsA+QAOAP4ADQD8AAAA/wAAAEUAAAAAAAAA" - "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" - "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA7gAAAAAAAADgwAQAYLwAA//8AAA" - "ICIwAAAP8AAAD+AAYA8wBwAP8AagD/AGoA/wBqAP8AagD/AGoA/wBqAP8AagD/AGoA/wBqAP8AagD/A" - "EIA9QAAAP8AMADvAGoA/wBqAP8AagD/AGoA/wBqAP8AagD/AGoA/wBqAP8AagD/AGsA/wApAP0AEQD8" - "AAIA/QAAAP8AAAAoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" - "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAVgEAAA" - "AAACJ5AQA2ygAAND8BAHV+AgAAAAAAAAH/AAAA/gAcAPQAbAD/AGoA/wBqAP8AagD/AGoA/wBqAP8Aa" - "gD/AGoA/wBqAP8AagD/AGsA/wApAPYAAAD+AB4A+gBsAP8AagD/AGoA/wBqAP8AagD/AGoA/wBqAP8A" - "agD/AGoA/wBrAP8ALADxAAAA/AAAAP4AAAD/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" - "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" - "AAAAAAAAAAABYAhQAfAE8ABgBWAQAAAAAAIoABAAAAAAA5UgEAAAAAAAAA+AAAAP4AJQD2AGsA/wBqA" - "P8AagD/AGoA/wBqAP8AagD/AGoA/wBqAP8AagD/AGoA/wBsAPgAAAD6AAAA/gANAPAAbgD/AGoA/wBq" - "AP8AagD/AGoA/wBqAP8AagD/AGoA/wBqAP8AagD/ADsA+QAAAP8AAAD+AAAA4AAAAAAAAAAAAAAAAAA" - "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" - "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAtQAAAAAAAABYABgAZgEGAIMBAAAAAAA+fgEAAAAAAAAAA" - "AACA8wAAAH+ACkA9wBgAPgARQDwADoA9gA5APYASgD0AHAA9wBrAP8AagD/AGoA/wBxAP0AFQD0AAAA" - "/gAAAP4AAAD5AHMA/QBqAP8AagD/AGoA/wBqAP8AagD/AGoA/wBvAP0ATgDxAEwA9AA1AOkAAAD/AAA" - "A/gAAAP8AAAB3AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" - "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAvAAzAKcBAADXAAAAAAAcAFYBF" - "gB4ASAAZwEAXT8BADpdAgAAAAAAAQG+AAEB/gAAAf8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAPsAJAD1" - "AEQA9ABEAPMACQD3AAAA/wAAAP4AAAD+AAAA/wAiAPMASAD4AE8A9gBuAPgAbQD/AGoA/wBvAP8AFQD" - "yAAAA/wAAAP8AAAD/AAAA/gAAAP4AAAD+AAAA/wAAABoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" - "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" - "AAABgCuAQAAAAAAAAAAAAAAACEAkQIWAJMBGQBXAQCWlAIAAAAAAAAA7gABAf4AAQD+AAAA/wAAAP8A" - "AAD0AAAA/gAAAP8AAAD/AAAA/gAAAP8AAAD/AAAA/gAAAP4IAAn9JwAs/TsARP5CAEz+PQBG/ioAMP4" - "EAAX6ABkA9gBAAPUAUwDrAAAA/wADEfUAFXztAA5U9wAAAPcAAAD/AAAA/gAAAP4AAACYAAAAAAAAAA" - "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" - "AAAAAAAAAAAAAAAAAAAAAAAAAAAABcAugAkALIAAAAAAAAAAAAJABQAHwCaAR8AhwMNKUwCAAMDRgAB" - "Af8AAAD+AAAA+QAbn/MAK/zwACz//wAs//cAIMDkAAAA+QAAAP4AAAD+CAAK/V0Aav6TAKn+nQC0/5c" - "Arf+VAKv/lACq/5QAq/+WAK3/nwC3/4IAlv5FAE/+AAAA/wAAAP8AHKnzACnx/wAq9v8ALP/5AAo+9A" - "AAAP4AAAD+AAAA7QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" - "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACtAQAAAAAAAKoACABt" - "AQwAYwAAAAAAAQAERQAAAP8AAAD+AAAA/wAfuPEAKfL/ACnz/wAq+v8AIsrwAAEH9gAAAP4AAAD+JAA" - "p/ZoAsf+TAKj/kwCp/5MAqf+TAKn/kwCp/5MAqf+TAKn/kwCp/5MAqf+TAKn/lACq/0sAVv4AAAD/AC" - "PQ8gAp8/8AKfP/ACny/wAYke4AAAD/AAAA/gAAAP8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" - "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" - "AAAAAAAAgAAAAK0AAAAAAGsA/wAAAAAAAAACSgAAAf8AAQH+AAAA/gAAAP8AGZHnACfm+AAdqvIACjz" - "tAAAA/wAAAP4AAAD+CgAL/ZwAtP+TAKn/kwCp/5MAqf+TAKn/kwCp/5MAqf+TAKn/kwCp/5MAqf+TAK" - "n/kwCp/5IAqP9OAFn+AAAA/wAetPMAKfP/ACny/wAq+PcAAQfyAAAA/gAAAP4AAAD/AAAAEgAAAAAAA" - "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" - "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKkAAAAAAAUAFwAAAAAAAAABeQAAAP8AAAH+AAAA/wMBBf0" - "AAAH+AAAA/wAAAP8AAAD/AAEB/gAAAP4AAAD9AAAB/k4AWv6SAKj/kwCp/5MAqf+TAKn/kwCp/5MAqf" - "+TAKn/kwCp/5MAqf+TAKn/kwCp/5MAqf+TAKn/ZwB2/gAAAP4ACC32ACHC9AAZk/AAAQX3AAAA/gAAA" - "P4AAAD+AAAA/wAAACgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" - "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABUAYQEAAAAAAAAAwAA" - "AAP8BAAH+EwAV/YkAnfJ6AIzzBQAH/QAAAP4AAAD+AAEB/gARF/0Aa5P9AMb//gACAv4oAC7+lwCu/5" - "MAqf+TAKn/kwCp/5MAqf+TAKn/kwCp/5MAqf+TAKn/kwCp/5MAqf+TAKn/kwCp/5oAsv8YABv9AAAA/" - "gAAAP8AAAD/AAAA/ksAVv0SABX9AAAA/gAAAP8AAABmAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" - "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" - "AAAAAAAEAAAATAAAA6AAAAP0AAAD+IgAo+5oAsfCSAKnxlQCr8RYAGvwAAQH+AAAA/gAAAP4AQFf9AL" - "r//wC3/P8AXn79AAAA/o8ApP6TAKn/kwCp/5MAqf+TAKn/kwCp/5MAqf+TAKn/kwCp/5MAqf+TAKn/k" - "wCp/5MAqf+TAKn/mgCx/0QATv4AAAD+AAAA/gAAAP2gALj/WQBn/gAAAP4AAAD+AAAAtwAAAAAAAAAA" - "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" - "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOAAAA9gAAAP0AAAD+HwAk+5wAs/CSAKnxkwCp8VEAXfcAAA" - "D+AAAB/gAAAP4AAQH+AAAA/QDD//8At/v/ALDz/gAAAP5aAGf+kwCo/5MAqf+TAKn/kwCp/5MAqf+TA" - "Kn/kwCp/5MAqf+TAKn/kwCp/5MAqf+TAKn/kwCp/5MAqf9sAHz+AAAA/gAAAP4AAAD9nwC3/5YArf8A" - "AAD+AAAA/gAAAP8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" - "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA2AAAAP0AAAD+AAAA/pgAr/" - "CTAKnxkwCp8ZIAqPGHAJvyAAAA/wAAAP4AAAD+AAEB/gAAAP4Ae6X9ALb7/wC+//8AJjX9KgAw/ZcAr" - "f+TAKn/kwCp/5MAqf+TAKn/kwCp/5MAqf+TAKn/kwCp/5MAqf+TAKn/kwCp/5MAqf+TAKn/mQCv/wAA" - "AP4AAAD+AAAA/Z8Atv+YAK//HwAk/QAAAP4AAAD/AAAAZwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" - "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHA" - "AAAP8AAAD+AAAA/gAAAP+RAKbxkgCo8ZMAqfGTAKnxkgCo8XYAiPQEAAX+AAAA/yUAK/sBAAH+ACg3/" - "QC+//8Atvr/AG6W/QAAAP6YAK//kwCp/5MAqf+TAKn/kwCp/5MAqf+TAKn/kwCp/5MAqf+TAKn/kwCp" - "/5MAqf+TAKn/kwCp/5MAqf9oAHf+AAAA/kAASf6UAKv/kwCp/0cAUv4AAAD+AAAA/gAAAMkAAAAAAAA" - "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" - "AAAAAAAAAAAAAAAAAAAAAAAAAAAACRAAAA/wAAAP4AAAD+BQAG/nsAjfOUAKrxkwCp8ZMAqfGTAKjxn" - "QC08JcArvCYAK/xSQBU9wAAAP4Aqen+ALf7/wC3+v4AAAD+WwBo/pMAqf+TAKn/kwCp/5MAqf+TAKn/" - "kwCp/5MAqf+TAKn/kwCp/5MAqf+TAKn/kwCp/5MAqf+TAKn/kwCp/5wAs/6ZAK//kwCp/5MAqf9bAGj" - "+AAAA/gAAAP4AAAD/AAAABgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" - "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAE0AAAD7AAAA/QAAAP4AA" - "AD/TABY+J0AtPCTAKnxkwCp8ZMAqfGTAKnxkwCp8Y4ApPEAAAD/AFRx/QC4/P8Avf//AC0+/RQAGP2c" - "ALP/kwCp/5MAqf+TAKn/kwCp/5MAqf+TAKn/kwCp/5MAqf+TAKn/kwCp/5MAqf+TAKn/kwCp/5MAqf+" - "TAKn/kwCp/5MAqf+TAKn/YQBv/wAAAP4AAAD+AAAA/wAAAG0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" - "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" - "AAAAAABAAAAAAAAALgAAAD/AAAA/gABAP4bAB78jwCk8pMAqfGTAKnxkwCp8ZMAqfGXAK3xKwAy+wAC" - "BP0Axv//ALb6/wBvlf0AAAD+dwCJ/p8Atv+dALT+lgCt/p4Atv6eALb/lwCu/5MAqf+TAKn/kwCp/5M" - "Aqf+TAKn/kwCp/5MAqf+TAKn/kwCp/5MAqf+TAKn/kwCo/1IAXv0AAAD+AAAA/gAAAP4AAACRAAAAAA" - "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" - "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATgAAAP8AAAD9AAAB/gAAAP5ZAGf2ngC1" - "8JMAqfGTAKnxkwCp8WAAb/UAAAD+AJLH/gC3+/8As/X+AAAA/gsADP0AAAD9AAAA/gAAAP4AAAD+AQA" - "C/SMAKP1NAFj+gQCU/pwAs/+TAKn/kwCp/5MAqf+TAKn/kwCp/5MAqf+TAKn/kwCp/5sAsv8cACH9AA" - "AA/gAAAP4AAAD/AAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" - "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAO" - "AAAA1QAAAP8AAAD+AAAA/hMAFfxzAIT0nQC08JMAqfF5AIrzAAAA/gA6T/0Au///ALn//wBkh/4AERj" - "9ACs7/QBFX/0AT2z9AElk/gAxQ/0AAgT9AAAA/gAAAP4UABf9bwCA/pcArv+TAKn/kwCp/5MAqf+TAK" - "n/kwCp/5MAqv9rAHr+AAAA/gAAAP4AAAD/AAAAhgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" - "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" - "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB+AAAA/wAAAP4AAAD+AAAA/hQAF/xJAFP4GwAf/AAAAP4" - "ABgn9AMP//wC3+/8Auf7/AML//wC8//8Auf7/ALj9/wC5/v8AvP//AMb//wCj4P0AWnj9AAAA/QAAAP" - "42AD/9mgCx/5MAqP+TAKn/lACq/54Atv9YAGX+AAAA/gAAAP4AAAD/AAAArAAAAAAAAAAAAAAAAAAAA" - "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" - "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAh4AAADVAAAA/gA" - "AAP4AAAD+AAAA/gAAAP4AEhr9AJ3V/gC3+v8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/w" - "C3+/8At/v/ALf8/wDB//8AYYT9AAAA/ggACv1cAGn+bAB9/UEAS/4CAAL+AAAA/gAAAP4AAAD/AAAAl" - "gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" - "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" - "AAAAAAAAAAAABAAEBFAAAAP8AAAD+AAAA/gAAAP4AUGz9AML//wC2+/8At/v/ALf7/wC3+/8At/v/AL" - "f7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf8/wCq5v4AERj+AAEA/gAAAP4AAAD+A" - "AAA/gAAAP4AAAD/AAAAdAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" - "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" - "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOgAAAD+AAAA/gAAAP4Aeqf9ALz//wC3+/8At/" - "v/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8At" - "/r/ALz//gAcJvwAAQH+AAAA/gAAAP4AAAD/AAAAfwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" - "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" - "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAI0AAAD/AAAA/gAAAP" - "4Ac579ALn+/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wC3+" - "/8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8Avf/+ABcg/QAAAf4AAAD+AAAA/wAAAAAAAAAAAAAAAAAA" - "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" - "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" - "AAABIAAAD/AAAA/gAAAP4AP1X9AL7//wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/" - "wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wCy8v4AAAD+AAAA" - "/gAAAP8AAACKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" - "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" - "AAAAAAAAAAAAAAAAAAAAAAAAAAAACoAAAA/QAAAP4ABAb9AL7//wC3+/8At/v/ALf7/wC3+/8At/v/A" - "Lf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/" - "ALf7/wC3+/8Atvr/AHmm/QAAAP4AAAD+AAAA/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" - "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" - "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAA/wAAAP4AAAD+AHWh/QC2+v8At" - "/v/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8A" - "t/v/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wDD//8AGSP9AAAA/gAAAP8AAABuAAAAAAAAAAA" - "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" - "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAbQAAA" - "P8AAAD+AAUH/QDE//8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wC3" - "+/8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8Atvv/AH+s/gA" - "AAP4AAAD+AAAA2AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" - "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" - "AAAAAAAAAAAAAAAAAAAALMAAAD+AAAA/gBZd/0At/z/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7" - "/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf" - "7/wC3+/8At/v/ALf7/wDF//8AAAD9AAAA/gAAAP8AAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" - "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" - "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADwAAAA/gAAAP4Al9D9ALf7/wC3+/8At/v/" - "ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v" - "/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8Auf7/AERe/QAAAP4AAAD/AAAAUQAAAAAAAA" - "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" - "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/wAAAP4A" - "AAD9AMb//gC3+/8At/v/ALf7/wC3+/8Auf7/AMb//gC2+f4AwP/+AMT//wC4/f8At/v/ALf7/wC3+/8" - "At/v/ALf7/wC3+/8At/v/ALf7/wC8//8Avf//ALn+/wC3+/8At/v/ALf7/wC3+/8At/v/ALb7/wBvlf" - "4AAAD+AAAA/gAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" - "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" - "AAAAAAAAAAAACgAAAP8AAAD+ABgg/QDA//8At/v/ALf7/wC2+v8AuPz+AEVe/QAAAP0AAAD+AAAA/gA" - "EBv0AU2/9ALz//wC3+/8At/v/ALf7/wC3+/8Atvr/AL7//wBmi/0ALj/9ACQx/QBJZP0Ai739AMD//w" - "C3+/8At/v/ALf7/wC3+/8Aksj+AAAA/gAAAP4AAACZAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" - "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" - "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABcAAAD/AAAA/gAtPf4AvP//ALf7/wC3+/8As/T+AAo" - "O/QAAAP0SEhLtAAAA/gAAAP4AAAD+AAAA/gAWH/0Av///ALf7/wC3+/8At/v/ALHx/gANEv0AAAD+AA" - "AA/gAAAP4AAAD/AAAA/wAuP/0Avv//ALf7/wC3+/8At/v/AKjm/QAAAP4AAAD+AAAApwAAAAAAAAAAA" - "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" - "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATAAAA/wAAAP4AL0H" - "+ALz//wC3+/8AvP//ADJE/QAAAPfc3Nz1TExM6wAAAP4AAAD+AAAA/gAAAP8AAAD+AGeN/gC3+/8At/" - "v/AML//wAeKf0AAAH/AAAA/gAAAP4AAAD+cXFx3YSEhOoAAAD9AEVe/QC6//8At/v/ALf7/wCs7P8AA" - "AD+AAAA/gAAAKEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" - "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" - "AAAAAAAAAAAAAAP8AAAD+ACAt/QC+//8At/v/ALP1/gAAAP95eXns/////2FhYeoBAQH/AAAA/gEBAf" - "9eXl7lDQ0N9gAgLP0Av///ALf7/wCNwf4AAAD/NDQ03AAAAP8AAAD+AAAA/6Ojo+b/////eXl55AAAA" - "P8AtPf+ALf7/wC3+/8AoNv9AAAA/gAAAP4AAACHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" - "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" - "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/AAAA/gAAAP0Ax///ALf7/wCIu/0AAAD/ysrK6/" - "/////8/Pz6Hx8f6wAAAP8kJCTm/f39/WVlZe4ABAX8AMT//wC3/P8AV3X9ERER7/////9MTEzrAAAA6" - "ElJSeb///////////X19e8AAAD/AHuo/QC3+/8At/v/AIW2/gAAAP4AAAD/AAAAZAAAAAAAAAAAAAAA" - "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" - "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA0wAAAP4AAAD+AK" - "fl/gC3+/8AjcH+AAAA/83Nze/////////////////////w//////////9gYGDsAA8V/QDC//8Auv//A" - "EJa/UFBQeb////////////////////////////////////vAAAA/wBvlfwAt/v/ALf7/wBdff0AAAD+" - "AAAA/wAAAC0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" - "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" - "AAAAAAAAAAAI4AAAD+AAAA/gBnjP0At/v/ALz//gAAAP+CgoLx/////////////////////////////" - "///CAkJ8QBCXP4Auf7/ALj8/wBScf0DAwPw////////////////////////////////tra26AAAAP8A" - "hrf+ALf7/wDA//8AHCf9AAAA/gAAAP8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" - "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" - "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA3AAAA/wAAAP4AEBb9AMT//wC6//8AQVn+AAAA9+Pj4" - "/L/////////////////////cnJy1gAAAP8AlMr9ALf7/wC3+/8AhLT+AAAA/5mZmeL/////////////" - "/////////////R0dHesAAAD9AL///gC3+/8ApuX+AAAA/gAAAP4AAAC1AAAAAAAAAAAAAAAAAAAAAAA" - "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" - "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPMAAAD+AAAA/" - "gCGtv0Atvr/ALn+/wARF/0AAAD3cHBw7bGxseqioqLuOjo67QAAAP8ARFz9ALz//wC3+/8At/v/AML/" - "/wAbJf0AAAD/jo6O5v////b//////f397zw8PPEAAAD/AGCC/QC4/P8Auv//AEJb/QAAAP4AAAD/AAA" - "AUQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" - "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" - "AAAAAAAAAAAAABpAAAA/wAAAP4AERf9AMP//wC3+/8Auv/+AEVf/AAAAP8AAAD/AAAA/wAAAP0AWnn9" - "AMH//wC3+/8At/v/ALf7/wC3+/8AtPf+ABsm/QAAAP8AAADwDg4O+QAAAPwAAAD+AD1T/QDB//8At/v" - "/AKLd/gAAAP4AAAD+AAAA8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" - "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" - "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPIAAAD+AAAA/gBfgP0Auf7/ALf7/wC5/v8A" - "wv//AJnS/gCWzv4Awf//ALj9/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wDC//8Ahbb+AFBt/QA3TP0" - "ATGn9AI/D/gC+//8At/v/AMD//wATGv0AAAD+AAAA/wAAAE4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" - "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" - "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAA9AAAA/wAA" - "AP4AAAD+AJPJ/QC2+v8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wC" - "3+/8At/v/ALf7/wC4/f8Au///ALj9/wC3+/8At/v/AML//wA0SP0AAAD+AAAA/QAAAMMAAAAAAAAAAA" - "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" - "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" - "AAAAAAAAAAAAAAAAAAAAAJYAAAD/AAAA/gAAAP4Andj9ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf" - "7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/AML//wA+Vf0AAAD+AA" - "AA/gAAAPYAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" - "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" - "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwwAAAP4AAAD+AAAA/gCGt/0AvP/" - "/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8Atv" - "r/ALr//gAoNv0AAAD+AAAA/gAAAP8AAAAeAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" - "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" - "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" - "AAAC9AAAA/wAAAP4AAAD+AEFY/QC6/f4At/z/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/" - "8At/v/ALf7/wC2+v8Aw///AICv/gAEBv0AAAH+AAAA/QAAAP8AAAAiAAAAAQAAAAAAAAAAAAAAAAAAA" - "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" - "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" - "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIMAAAD/AAAA/gAAAP4AAAD9AFl3/QCt7/4AwP//ALj9/w" - "C2+/8At/v/ALf7/wC3+/8At/v/ALz//wDB//4AeKP+ABki/QAAAP4AAAD+AAAA/wAAAOEAAAASAAAAA" - "QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" - "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" - "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALAAAAOUAAAD/AA" - "AA/gAAAP4AAAD+ABcg/QBQbv0AbJD9AH2s/gCBsP0Ac5v+AFt6/QAtPv0AAAD9AAAA/gAAAP4AAAD+A" - "AAA/wAAAIQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" - "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" - "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" - "AAAAAAAAAAAAAAAAAAAAAAWgAAAOIAAAD/AAAA/gAAAP4AAAD+AAAA/gAAAP4AAAD+AAAA/gAAAP4AA" - "AD+AAAA/gAAAP8AAAD/AAAAmQAAAAsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" - "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" - "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" - "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKAAAAIgAAADRAAAA/wAAA" - "P8AAAD/AAAA/wAAAP8AAAD/AAAA5QAAAJoAAABWAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" - "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" - "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" - "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" - "AAAAAAAAAAAAAAAAAAAAAAAAAAHAAAAIAAAACQAAAAUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" - "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" - "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA///gAf///////4AAAAD/////wAAAAP////" - "/AAAAB/////+AAAAH/////4AAAA//////gAAAD/////8AAAAH/////wAAAAP////+AAAAA/////4AAA" - "AD/////gAAAAP////+AAAAA/////4AAAAD/////gAAAAP////+AAAAA/////4AAAAB/////gAAAAD//" - "//+AAAAAP////wAAAAA////+AAAAAD////wAAAAAP///8AAAAAA////gAAAAAB///8AAAAAAH///gAA" - "AAAAf//+AAAAAAA///4AAAAAAD///4AAAAAAP///wAAAAAAf///wAAAAAD////gAAAAAP////gAAAAB" - "/////AAAAAP////+AAAAD/////wAAAAf////+AAAAB/////4AAAAD/////AAAAAP////8AAAAA/////" - "wAAAAB////+AAAAAH////4AAAAAf////gAAAAA////+AAAAAD////4AAAAAP////gAAAAA////+AAAA" - "AD////4AAAAAf////gAAAAB////+AAAAAH////8AAAAAf////wAAAAD/////gAAAAP////+AAAAB///" - "//8AAAAH/////wAAAA//////gAAAH//////AAAA//////+AAAH//////+AAA///////+AAP///////+" - "AH//////////////8="; + const std::string HTTPConnection::itoopieFavicon = + "data:image/vnd.microsoft.icon;base64," + "AAABAAEAQEAAAAEAIAAoQgAAFgAAACgAAABAAAAAgAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAABsAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgQAAAIMAAACEAAAAg" + "wAAAIAAAACCAAAASQAAACUAAAAmAAAAOwAAAFIAAABgAAAAVAAAACcAAAAVAAAADQAAAAcAAAADAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACjAAAA/wAAAP4AAAD+AAAA/gAAAP4AAAD+A" + "AAA/gAAAP4AAAD+AAAA/gAAAP4AAAD+AAAA/gAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/" + "AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAACbAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAZAAAAP8AA" + "AD+AAAA/gAAAP4AAAD+AAAA/gAAAP4AAAD+AAAA/gAAAP4AAAD+AAAA/gAAAP4AAAD+AAAA/gAAAP4A" + "AAD+AAAA/gAAAP4AAAD+AAAA/gAAAP4AAAD+AAAA/gAAAP4AAAD+AAAA/gAAAP4AAAD+AAAA/wAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAKQAAAAAAAAAAAAAAAADbAAAA/gAAAP4AAAD+AAAA/gAAAP4AAAD+AAAA/gAAAP8AAAD/AAAA/gAA" + "AP4AAAD+AAAA/gAAAP4AAAD+AAAA/gAAAP4AAAD+AAIA/gAAAPwAAAD/AAAA/gAAAP4AAAD+AAAA/gA" + "AAP4AAAD+AAAA/wAAAFkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAApAAAAAAAAAAAAHAAAAP8AAAD+AAAA/gAAAPwACwD9ABEA" + "/QASAPgAFgD4ABUA+AATAP0ADQD8AAIA/AAAAP8AAAD+AAAA/gAAAP8ADQD7ACMA/QAlAP8AJwD/ACI" + "A/QATAPwAAAD8AAAA/gAAAP4AAAD+AAAA/gAAAKIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADe" + "AAAA/gAAAPwAJAD+AAwA+wAAAP8AAAD/AAAA/wAEAPwAEgD8ACQA/gAmAP8AHAD8AAEA/gAAAP4ACwD" + "8ACUA/wAkAP8AFwD8AAkA/AAQAP0AIwD+ACQA/wANAPsAAAD+AAAA/gAAAP8AAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAEAAABNAAAA/wAAAP8AGAD8AAAA/QAYAPMARQDyAFEA9wBOAPIAPAD0ABEA8QAAAP8" + "AGgD7ACMA/wAVAP0AAAD9ACMA/QAkAP8ADwD9AAgA+ABBAPUALADyAAAA/AAUAPwAJgD/ABgA/AAAAP" + "0AAAD/AAAAdgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA+AAAAP4ADQD8AAgA/wAqAPAAcAD/AGoA/wB" + "qAP8AagD/AGoA/wBvAP8ATwDvAAAA/QAbAP0AEwD8AAYA/QAkAP8AGAD8AAsA9wBuAPgAagD/AGsA/w" + "BjAPkADAD3AAQA/QAiAP4AGgD9AAAA/gAAAP8AAABgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAZgAAAP8AAAD+ABY" + "A+wAQAPQAcQD/AGoA/wBqAP8AagD/AGoA/wBqAP8AagD/AGoA/wBTAPEAAAD/AAcA/AAMAPwAIQD9AA" + "AA+gBoAPgAagD/AGoA/wBqAP8AagD/AHAA/gAuAO0AAAD/AB8A/QAPAPwAAAD+AAAA+AAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAANIAAAD+AAwA+QAAAP0AZQD1AGoA/wBqAP8AagD/AGoA/wBqAP8AagD/AGoA/wBqAP8Abg" + "D/ABcA8QAAAP4ACAD9AAAA+QBeAPkAagD/AGoA/wBqAP8AagD/AGoA/wBqAP8AbQD/ADkA7wAAAP8AJ" + "AD9AAAA/gAAAP8AAAAfAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAALi8AAAAAAAAAAAAAAAD/AAAA/QAAAP8AKwD1AGsA/wBqAP8AagD/AGoA/wBqAP" + "8AagD/AGoA/wBqAP8AagD/AGoA/wA9APUAAAD/AAAA/wBKAPMAagD/AGoA/wBqAP8AagD/AGoA/wBqA" + "P8AagD/AGoA/wBxAP8ADgD1ABMA/AAHAPsAAAD/AAAARQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAzPAIAAAAgAAAA/wAAAPwAAAD/AGQA9A" + "BqAP8AagD/AGoA/wBqAP8AagD/AGoA/wBqAP8AagD/AGoA/wBqAP8ASQD1AAAA/wASAPMAcAD/AGoA/" + "wBqAP8AagD/AGoA/wBqAP8AagD/AGoA/wBqAP8AawD/ACsA+QAOAP4ADQD8AAAA/wAAAEUAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA7gAAAAAAAADgwAQAYLwAA//8AAA" + "ICIwAAAP8AAAD+AAYA8wBwAP8AagD/AGoA/wBqAP8AagD/AGoA/wBqAP8AagD/AGoA/wBqAP8AagD/A" + "EIA9QAAAP8AMADvAGoA/wBqAP8AagD/AGoA/wBqAP8AagD/AGoA/wBqAP8AagD/AGsA/wApAP0AEQD8" + "AAIA/QAAAP8AAAAoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAVgEAAA" + "AAACJ5AQA2ygAAND8BAHV+AgAAAAAAAAH/AAAA/gAcAPQAbAD/AGoA/wBqAP8AagD/AGoA/wBqAP8Aa" + "gD/AGoA/wBqAP8AagD/AGsA/wApAPYAAAD+AB4A+gBsAP8AagD/AGoA/wBqAP8AagD/AGoA/wBqAP8A" + "agD/AGoA/wBrAP8ALADxAAAA/AAAAP4AAAD/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAABYAhQAfAE8ABgBWAQAAAAAAIoABAAAAAAA5UgEAAAAAAAAA+AAAAP4AJQD2AGsA/wBqA" + "P8AagD/AGoA/wBqAP8AagD/AGoA/wBqAP8AagD/AGoA/wBsAPgAAAD6AAAA/gANAPAAbgD/AGoA/wBq" + "AP8AagD/AGoA/wBqAP8AagD/AGoA/wBqAP8AagD/ADsA+QAAAP8AAAD+AAAA4AAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAtQAAAAAAAABYABgAZgEGAIMBAAAAAAA+fgEAAAAAAAAAA" + "AACA8wAAAH+ACkA9wBgAPgARQDwADoA9gA5APYASgD0AHAA9wBrAP8AagD/AGoA/wBxAP0AFQD0AAAA" + "/gAAAP4AAAD5AHMA/QBqAP8AagD/AGoA/wBqAP8AagD/AGoA/wBvAP0ATgDxAEwA9AA1AOkAAAD/AAA" + "A/gAAAP8AAAB3AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAvAAzAKcBAADXAAAAAAAcAFYBF" + "gB4ASAAZwEAXT8BADpdAgAAAAAAAQG+AAEB/gAAAf8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAPsAJAD1" + "AEQA9ABEAPMACQD3AAAA/wAAAP4AAAD+AAAA/wAiAPMASAD4AE8A9gBuAPgAbQD/AGoA/wBvAP8AFQD" + "yAAAA/wAAAP8AAAD/AAAA/gAAAP4AAAD+AAAA/wAAABoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAABgCuAQAAAAAAAAAAAAAAACEAkQIWAJMBGQBXAQCWlAIAAAAAAAAA7gABAf4AAQD+AAAA/wAAAP8A" + "AAD0AAAA/gAAAP8AAAD/AAAA/gAAAP8AAAD/AAAA/gAAAP4IAAn9JwAs/TsARP5CAEz+PQBG/ioAMP4" + "EAAX6ABkA9gBAAPUAUwDrAAAA/wADEfUAFXztAA5U9wAAAPcAAAD/AAAA/gAAAP4AAACYAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAABcAugAkALIAAAAAAAAAAAAJABQAHwCaAR8AhwMNKUwCAAMDRgAB" + "Af8AAAD+AAAA+QAbn/MAK/zwACz//wAs//cAIMDkAAAA+QAAAP4AAAD+CAAK/V0Aav6TAKn+nQC0/5c" + "Arf+VAKv/lACq/5QAq/+WAK3/nwC3/4IAlv5FAE/+AAAA/wAAAP8AHKnzACnx/wAq9v8ALP/5AAo+9A" + "AAAP4AAAD+AAAA7QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACtAQAAAAAAAKoACABt" + "AQwAYwAAAAAAAQAERQAAAP8AAAD+AAAA/wAfuPEAKfL/ACnz/wAq+v8AIsrwAAEH9gAAAP4AAAD+JAA" + "p/ZoAsf+TAKj/kwCp/5MAqf+TAKn/kwCp/5MAqf+TAKn/kwCp/5MAqf+TAKn/lACq/0sAVv4AAAD/AC" + "PQ8gAp8/8AKfP/ACny/wAYke4AAAD/AAAA/gAAAP8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAgAAAAK0AAAAAAGsA/wAAAAAAAAACSgAAAf8AAQH+AAAA/gAAAP8AGZHnACfm+AAdqvIACjz" + "tAAAA/wAAAP4AAAD+CgAL/ZwAtP+TAKn/kwCp/5MAqf+TAKn/kwCp/5MAqf+TAKn/kwCp/5MAqf+TAK" + "n/kwCp/5IAqP9OAFn+AAAA/wAetPMAKfP/ACny/wAq+PcAAQfyAAAA/gAAAP4AAAD/AAAAEgAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKkAAAAAAAUAFwAAAAAAAAABeQAAAP8AAAH+AAAA/wMBBf0" + "AAAH+AAAA/wAAAP8AAAD/AAEB/gAAAP4AAAD9AAAB/k4AWv6SAKj/kwCp/5MAqf+TAKn/kwCp/5MAqf" + "+TAKn/kwCp/5MAqf+TAKn/kwCp/5MAqf+TAKn/ZwB2/gAAAP4ACC32ACHC9AAZk/AAAQX3AAAA/gAAA" + "P4AAAD+AAAA/wAAACgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABUAYQEAAAAAAAAAwAA" + "AAP8BAAH+EwAV/YkAnfJ6AIzzBQAH/QAAAP4AAAD+AAEB/gARF/0Aa5P9AMb//gACAv4oAC7+lwCu/5" + "MAqf+TAKn/kwCp/5MAqf+TAKn/kwCp/5MAqf+TAKn/kwCp/5MAqf+TAKn/kwCp/5oAsv8YABv9AAAA/" + "gAAAP8AAAD/AAAA/ksAVv0SABX9AAAA/gAAAP8AAABmAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAEAAAATAAAA6AAAAP0AAAD+IgAo+5oAsfCSAKnxlQCr8RYAGvwAAQH+AAAA/gAAAP4AQFf9AL" + "r//wC3/P8AXn79AAAA/o8ApP6TAKn/kwCp/5MAqf+TAKn/kwCp/5MAqf+TAKn/kwCp/5MAqf+TAKn/k" + "wCp/5MAqf+TAKn/mgCx/0QATv4AAAD+AAAA/gAAAP2gALj/WQBn/gAAAP4AAAD+AAAAtwAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOAAAA9gAAAP0AAAD+HwAk+5wAs/CSAKnxkwCp8VEAXfcAAA" + "D+AAAB/gAAAP4AAQH+AAAA/QDD//8At/v/ALDz/gAAAP5aAGf+kwCo/5MAqf+TAKn/kwCp/5MAqf+TA" + "Kn/kwCp/5MAqf+TAKn/kwCp/5MAqf+TAKn/kwCp/5MAqf9sAHz+AAAA/gAAAP4AAAD9nwC3/5YArf8A" + "AAD+AAAA/gAAAP8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA2AAAAP0AAAD+AAAA/pgAr/" + "CTAKnxkwCp8ZIAqPGHAJvyAAAA/wAAAP4AAAD+AAEB/gAAAP4Ae6X9ALb7/wC+//8AJjX9KgAw/ZcAr" + "f+TAKn/kwCp/5MAqf+TAKn/kwCp/5MAqf+TAKn/kwCp/5MAqf+TAKn/kwCp/5MAqf+TAKn/mQCv/wAA" + "AP4AAAD+AAAA/Z8Atv+YAK//HwAk/QAAAP4AAAD/AAAAZwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHA" + "AAAP8AAAD+AAAA/gAAAP+RAKbxkgCo8ZMAqfGTAKnxkgCo8XYAiPQEAAX+AAAA/yUAK/sBAAH+ACg3/" + "QC+//8Atvr/AG6W/QAAAP6YAK//kwCp/5MAqf+TAKn/kwCp/5MAqf+TAKn/kwCp/5MAqf+TAKn/kwCp" + "/5MAqf+TAKn/kwCp/5MAqf9oAHf+AAAA/kAASf6UAKv/kwCp/0cAUv4AAAD+AAAA/gAAAMkAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAACRAAAA/wAAAP4AAAD+BQAG/nsAjfOUAKrxkwCp8ZMAqfGTAKjxn" + "QC08JcArvCYAK/xSQBU9wAAAP4Aqen+ALf7/wC3+v4AAAD+WwBo/pMAqf+TAKn/kwCp/5MAqf+TAKn/" + "kwCp/5MAqf+TAKn/kwCp/5MAqf+TAKn/kwCp/5MAqf+TAKn/kwCp/5wAs/6ZAK//kwCp/5MAqf9bAGj" + "+AAAA/gAAAP4AAAD/AAAABgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAE0AAAD7AAAA/QAAAP4AA" + "AD/TABY+J0AtPCTAKnxkwCp8ZMAqfGTAKnxkwCp8Y4ApPEAAAD/AFRx/QC4/P8Avf//AC0+/RQAGP2c" + "ALP/kwCp/5MAqf+TAKn/kwCp/5MAqf+TAKn/kwCp/5MAqf+TAKn/kwCp/5MAqf+TAKn/kwCp/5MAqf+" + "TAKn/kwCp/5MAqf+TAKn/YQBv/wAAAP4AAAD+AAAA/wAAAG0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAABAAAAAAAAALgAAAD/AAAA/gABAP4bAB78jwCk8pMAqfGTAKnxkwCp8ZMAqfGXAK3xKwAy+wAC" + "BP0Axv//ALb6/wBvlf0AAAD+dwCJ/p8Atv+dALT+lgCt/p4Atv6eALb/lwCu/5MAqf+TAKn/kwCp/5M" + "Aqf+TAKn/kwCp/5MAqf+TAKn/kwCp/5MAqf+TAKn/kwCo/1IAXv0AAAD+AAAA/gAAAP4AAACRAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATgAAAP8AAAD9AAAB/gAAAP5ZAGf2ngC1" + "8JMAqfGTAKnxkwCp8WAAb/UAAAD+AJLH/gC3+/8As/X+AAAA/gsADP0AAAD9AAAA/gAAAP4AAAD+AQA" + "C/SMAKP1NAFj+gQCU/pwAs/+TAKn/kwCp/5MAqf+TAKn/kwCp/5MAqf+TAKn/kwCp/5sAsv8cACH9AA" + "AA/gAAAP4AAAD/AAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAO" + "AAAA1QAAAP8AAAD+AAAA/hMAFfxzAIT0nQC08JMAqfF5AIrzAAAA/gA6T/0Au///ALn//wBkh/4AERj" + "9ACs7/QBFX/0AT2z9AElk/gAxQ/0AAgT9AAAA/gAAAP4UABf9bwCA/pcArv+TAKn/kwCp/5MAqf+TAK" + "n/kwCp/5MAqv9rAHr+AAAA/gAAAP4AAAD/AAAAhgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB+AAAA/wAAAP4AAAD+AAAA/hQAF/xJAFP4GwAf/AAAAP4" + "ABgn9AMP//wC3+/8Auf7/AML//wC8//8Auf7/ALj9/wC5/v8AvP//AMb//wCj4P0AWnj9AAAA/QAAAP" + "42AD/9mgCx/5MAqP+TAKn/lACq/54Atv9YAGX+AAAA/gAAAP4AAAD/AAAArAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAh4AAADVAAAA/gA" + "AAP4AAAD+AAAA/gAAAP4AEhr9AJ3V/gC3+v8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/w" + "C3+/8At/v/ALf8/wDB//8AYYT9AAAA/ggACv1cAGn+bAB9/UEAS/4CAAL+AAAA/gAAAP4AAAD/AAAAl" + "gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAABAAEBFAAAAP8AAAD+AAAA/gAAAP4AUGz9AML//wC2+/8At/v/ALf7/wC3+/8At/v/AL" + "f7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf8/wCq5v4AERj+AAEA/gAAAP4AAAD+A" + "AAA/gAAAP4AAAD/AAAAdAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOgAAAD+AAAA/gAAAP4Aeqf9ALz//wC3+/8At/" + "v/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8At" + "/r/ALz//gAcJvwAAQH+AAAA/gAAAP4AAAD/AAAAfwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAI0AAAD/AAAA/gAAAP" + "4Ac579ALn+/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wC3+" + "/8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8Avf/+ABcg/QAAAf4AAAD+AAAA/wAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAABIAAAD/AAAA/gAAAP4AP1X9AL7//wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/" + "wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wCy8v4AAAD+AAAA" + "/gAAAP8AAACKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAACoAAAA/QAAAP4ABAb9AL7//wC3+/8At/v/ALf7/wC3+/8At/v/A" + "Lf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/" + "ALf7/wC3+/8Atvr/AHmm/QAAAP4AAAD+AAAA/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAA/wAAAP4AAAD+AHWh/QC2+v8At" + "/v/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8A" + "t/v/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wDD//8AGSP9AAAA/gAAAP8AAABuAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAbQAAA" + "P8AAAD+AAUH/QDE//8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wC3" + "+/8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8Atvv/AH+s/gA" + "AAP4AAAD+AAAA2AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAALMAAAD+AAAA/gBZd/0At/z/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7" + "/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf" + "7/wC3+/8At/v/ALf7/wDF//8AAAD9AAAA/gAAAP8AAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADwAAAA/gAAAP4Al9D9ALf7/wC3+/8At/v/" + "ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v" + "/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8Auf7/AERe/QAAAP4AAAD/AAAAUQAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/wAAAP4A" + "AAD9AMb//gC3+/8At/v/ALf7/wC3+/8Auf7/AMb//gC2+f4AwP/+AMT//wC4/f8At/v/ALf7/wC3+/8" + "At/v/ALf7/wC3+/8At/v/ALf7/wC8//8Avf//ALn+/wC3+/8At/v/ALf7/wC3+/8At/v/ALb7/wBvlf" + "4AAAD+AAAA/gAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAACgAAAP8AAAD+ABgg/QDA//8At/v/ALf7/wC2+v8AuPz+AEVe/QAAAP0AAAD+AAAA/gA" + "EBv0AU2/9ALz//wC3+/8At/v/ALf7/wC3+/8Atvr/AL7//wBmi/0ALj/9ACQx/QBJZP0Ai739AMD//w" + "C3+/8At/v/ALf7/wC3+/8Aksj+AAAA/gAAAP4AAACZAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABcAAAD/AAAA/gAtPf4AvP//ALf7/wC3+/8As/T+AAo" + "O/QAAAP0SEhLtAAAA/gAAAP4AAAD+AAAA/gAWH/0Av///ALf7/wC3+/8At/v/ALHx/gANEv0AAAD+AA" + "AA/gAAAP4AAAD/AAAA/wAuP/0Avv//ALf7/wC3+/8At/v/AKjm/QAAAP4AAAD+AAAApwAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATAAAA/wAAAP4AL0H" + "+ALz//wC3+/8AvP//ADJE/QAAAPfc3Nz1TExM6wAAAP4AAAD+AAAA/gAAAP8AAAD+AGeN/gC3+/8At/" + "v/AML//wAeKf0AAAH/AAAA/gAAAP4AAAD+cXFx3YSEhOoAAAD9AEVe/QC6//8At/v/ALf7/wCs7P8AA" + "AD+AAAA/gAAAKEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAP8AAAD+ACAt/QC+//8At/v/ALP1/gAAAP95eXns/////2FhYeoBAQH/AAAA/gEBAf" + "9eXl7lDQ0N9gAgLP0Av///ALf7/wCNwf4AAAD/NDQ03AAAAP8AAAD+AAAA/6Ojo+b/////eXl55AAAA" + "P8AtPf+ALf7/wC3+/8AoNv9AAAA/gAAAP4AAACHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/AAAA/gAAAP0Ax///ALf7/wCIu/0AAAD/ysrK6/" + "/////8/Pz6Hx8f6wAAAP8kJCTm/f39/WVlZe4ABAX8AMT//wC3/P8AV3X9ERER7/////9MTEzrAAAA6" + "ElJSeb///////////X19e8AAAD/AHuo/QC3+/8At/v/AIW2/gAAAP4AAAD/AAAAZAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA0wAAAP4AAAD+AK" + "fl/gC3+/8AjcH+AAAA/83Nze/////////////////////w//////////9gYGDsAA8V/QDC//8Auv//A" + "EJa/UFBQeb////////////////////////////////////vAAAA/wBvlfwAt/v/ALf7/wBdff0AAAD+" + "AAAA/wAAAC0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAI4AAAD+AAAA/gBnjP0At/v/ALz//gAAAP+CgoLx/////////////////////////////" + "///CAkJ8QBCXP4Auf7/ALj8/wBScf0DAwPw////////////////////////////////tra26AAAAP8A" + "hrf+ALf7/wDA//8AHCf9AAAA/gAAAP8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA3AAAA/wAAAP4AEBb9AMT//wC6//8AQVn+AAAA9+Pj4" + "/L/////////////////////cnJy1gAAAP8AlMr9ALf7/wC3+/8AhLT+AAAA/5mZmeL/////////////" + "/////////////R0dHesAAAD9AL///gC3+/8ApuX+AAAA/gAAAP4AAAC1AAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPMAAAD+AAAA/" + "gCGtv0Atvr/ALn+/wARF/0AAAD3cHBw7bGxseqioqLuOjo67QAAAP8ARFz9ALz//wC3+/8At/v/AML/" + "/wAbJf0AAAD/jo6O5v////b//////f397zw8PPEAAAD/AGCC/QC4/P8Auv//AEJb/QAAAP4AAAD/AAA" + "AUQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAABpAAAA/wAAAP4AERf9AMP//wC3+/8Auv/+AEVf/AAAAP8AAAD/AAAA/wAAAP0AWnn9" + "AMH//wC3+/8At/v/ALf7/wC3+/8AtPf+ABsm/QAAAP8AAADwDg4O+QAAAPwAAAD+AD1T/QDB//8At/v" + "/AKLd/gAAAP4AAAD+AAAA8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPIAAAD+AAAA/gBfgP0Auf7/ALf7/wC5/v8A" + "wv//AJnS/gCWzv4Awf//ALj9/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wDC//8Ahbb+AFBt/QA3TP0" + "ATGn9AI/D/gC+//8At/v/AMD//wATGv0AAAD+AAAA/wAAAE4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAA9AAAA/wAA" + "AP4AAAD+AJPJ/QC2+v8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wC" + "3+/8At/v/ALf7/wC4/f8Au///ALj9/wC3+/8At/v/AML//wA0SP0AAAD+AAAA/QAAAMMAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAJYAAAD/AAAA/gAAAP4Andj9ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf" + "7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/AML//wA+Vf0AAAD+AA" + "AA/gAAAPYAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwwAAAP4AAAD+AAAA/gCGt/0AvP/" + "/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8Atv" + "r/ALr//gAoNv0AAAD+AAAA/gAAAP8AAAAeAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAC9AAAA/wAAAP4AAAD+AEFY/QC6/f4At/z/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/" + "8At/v/ALf7/wC2+v8Aw///AICv/gAEBv0AAAH+AAAA/QAAAP8AAAAiAAAAAQAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIMAAAD/AAAA/gAAAP4AAAD9AFl3/QCt7/4AwP//ALj9/w" + "C2+/8At/v/ALf7/wC3+/8At/v/ALz//wDB//4AeKP+ABki/QAAAP4AAAD+AAAA/wAAAOEAAAASAAAAA" + "QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALAAAAOUAAAD/AA" + "AA/gAAAP4AAAD+ABcg/QBQbv0AbJD9AH2s/gCBsP0Ac5v+AFt6/QAtPv0AAAD9AAAA/gAAAP4AAAD+A" + "AAA/wAAAIQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAWgAAAOIAAAD/AAAA/gAAAP4AAAD+AAAA/gAAAP4AAAD+AAAA/gAAAP4AA" + "AD+AAAA/gAAAP8AAAD/AAAAmQAAAAsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKAAAAIgAAADRAAAA/wAAA" + "P8AAAD/AAAA/wAAAP8AAAD/AAAA5QAAAJoAAABWAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAHAAAAIAAAACQAAAAUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA///gAf///////4AAAAD/////wAAAAP////" + "/AAAAB/////+AAAAH/////4AAAA//////gAAAD/////8AAAAH/////wAAAAP////+AAAAA/////4AAA" + "AD/////gAAAAP////+AAAAA/////4AAAAD/////gAAAAP////+AAAAA/////4AAAAB/////gAAAAD//" + "//+AAAAAP////wAAAAA////+AAAAAD////wAAAAAP///8AAAAAA////gAAAAAB///8AAAAAAH///gAA" + "AAAAf//+AAAAAAA///4AAAAAAD///4AAAAAAP///wAAAAAAf///wAAAAAD////gAAAAAP////gAAAAB" + "/////AAAAAP////+AAAAD/////wAAAAf////+AAAAB/////4AAAAD/////AAAAAP////8AAAAA/////" + "wAAAAB////+AAAAAH////4AAAAAf////gAAAAA////+AAAAAD////4AAAAAP////gAAAAA////+AAAA" + "AD////4AAAAAf////gAAAAB////+AAAAAH////8AAAAAf////wAAAAD/////gAAAAP////+AAAAB///" + "//8AAAAH/////wAAAA//////gAAAH//////AAAA//////+AAAH//////+AAA///////+AAP///////+" + "AH//////////////8="; - const char HTTP_COMMAND_TUNNELS[] = "tunnels"; - const char HTTP_COMMAND_TRANSIT_TUNNELS[] = "transit_tunnels"; - const char HTTP_COMMAND_TRANSPORTS[] = "transports"; - const char HTTP_COMMAND_START_ACCEPTING_TUNNELS[] = "start_accepting_tunnels"; - const char HTTP_COMMAND_STOP_ACCEPTING_TUNNELS[] = "stop_accepting_tunnels"; - const char HTTP_COMMAND_LOCAL_DESTINATIONS[] = "local_destinations"; - const char HTTP_COMMAND_LOCAL_DESTINATION[] = "local_destination"; - const char HTTP_PARAM_BASE32_ADDRESS[] = "b32"; - const char HTTP_COMMAND_SAM_SESSIONS[] = "sam_sessions"; - const char HTTP_COMMAND_SAM_SESSION[] = "sam_session"; - const char HTTP_PARAM_SAM_SESSION_ID[] = "id"; - - namespace misc_strings - { + const char HTTP_COMMAND_TUNNELS[] = "tunnels"; + const char HTTP_COMMAND_TRANSIT_TUNNELS[] = "transit_tunnels"; + const char HTTP_COMMAND_TRANSPORTS[] = "transports"; + const char HTTP_COMMAND_START_ACCEPTING_TUNNELS[] = "start_accepting_tunnels"; + const char HTTP_COMMAND_STOP_ACCEPTING_TUNNELS[] = "stop_accepting_tunnels"; + const char HTTP_COMMAND_LOCAL_DESTINATIONS[] = "local_destinations"; + const char HTTP_COMMAND_LOCAL_DESTINATION[] = "local_destination"; + const char HTTP_PARAM_BASE32_ADDRESS[] = "b32"; + const char HTTP_COMMAND_SAM_SESSIONS[] = "sam_sessions"; + const char HTTP_COMMAND_SAM_SESSION[] = "sam_session"; + const char HTTP_PARAM_SAM_SESSION_ID[] = "id"; + + namespace misc_strings + { - const char name_value_separator[] = { ':', ' ' }; - const char crlf[] = { '\r', '\n' }; + const char name_value_separator[] = { ':', ' ' }; + const char crlf[] = { '\r', '\n' }; - } // namespace misc_strings + } // namespace misc_strings - std::vector HTTPConnection::reply::to_buffers(int status) - { - std::vector buffers; - if (headers.size () > 0) - { - switch (status) - { - case 105: buffers.push_back(boost::asio::buffer("HTTP/1.1 105 Name Not Resolved\r\n")); break; - case 200: buffers.push_back(boost::asio::buffer("HTTP/1.1 200 OK\r\n")); break; - case 400: buffers.push_back(boost::asio::buffer("HTTP/1.1 400 Bad Request\r\n")); break; - case 404: buffers.push_back(boost::asio::buffer("HTTP/1.1 404 Not Found\r\n")); break; - case 408: buffers.push_back(boost::asio::buffer("HTTP/1.1 408 Request Timeout\r\n")); break; - case 500: buffers.push_back(boost::asio::buffer("HTTP/1.1 500 Internal Server Error\r\n")); break; - case 502: buffers.push_back(boost::asio::buffer("HTTP/1.1 502 Bad Gateway\r\n")); break; - case 503: buffers.push_back(boost::asio::buffer("HTTP/1.1 503 Not Implemented\r\n")); break; - case 504: buffers.push_back(boost::asio::buffer("HTTP/1.1 504 Gateway Timeout\r\n")); break; - default: - buffers.push_back(boost::asio::buffer("HTTP/1.1 200 OK\r\n")); - } + std::vector HTTPConnection::reply::to_buffers(int status) + { + std::vector buffers; + if (headers.size () > 0) + { + switch (status) + { + case 105: buffers.push_back(boost::asio::buffer("HTTP/1.1 105 Name Not Resolved\r\n")); break; + case 200: buffers.push_back(boost::asio::buffer("HTTP/1.1 200 OK\r\n")); break; + case 400: buffers.push_back(boost::asio::buffer("HTTP/1.1 400 Bad Request\r\n")); break; + case 404: buffers.push_back(boost::asio::buffer("HTTP/1.1 404 Not Found\r\n")); break; + case 408: buffers.push_back(boost::asio::buffer("HTTP/1.1 408 Request Timeout\r\n")); break; + case 500: buffers.push_back(boost::asio::buffer("HTTP/1.1 500 Internal Server Error\r\n")); break; + case 502: buffers.push_back(boost::asio::buffer("HTTP/1.1 502 Bad Gateway\r\n")); break; + case 503: buffers.push_back(boost::asio::buffer("HTTP/1.1 503 Not Implemented\r\n")); break; + case 504: buffers.push_back(boost::asio::buffer("HTTP/1.1 504 Gateway Timeout\r\n")); break; + default: + buffers.push_back(boost::asio::buffer("HTTP/1.1 200 OK\r\n")); + } - for (std::size_t i = 0; i < headers.size(); ++i) - { - header& h = headers[i]; - buffers.push_back(boost::asio::buffer(h.name)); - buffers.push_back(boost::asio::buffer(misc_strings::name_value_separator)); - buffers.push_back(boost::asio::buffer(h.value)); - buffers.push_back(boost::asio::buffer(misc_strings::crlf)); - } - buffers.push_back(boost::asio::buffer(misc_strings::crlf)); - } - buffers.push_back(boost::asio::buffer(content)); - return buffers; - } + for (std::size_t i = 0; i < headers.size(); ++i) + { + header& h = headers[i]; + buffers.push_back(boost::asio::buffer(h.name)); + buffers.push_back(boost::asio::buffer(misc_strings::name_value_separator)); + buffers.push_back(boost::asio::buffer(h.value)); + buffers.push_back(boost::asio::buffer(misc_strings::crlf)); + } + buffers.push_back(boost::asio::buffer(misc_strings::crlf)); + } + buffers.push_back(boost::asio::buffer(content)); + return buffers; + } - void HTTPConnection::Terminate () - { - if (!m_Stream) return; - m_Socket->close (); - m_Stream->Close (); - - m_Socket->get_io_service ().post ([=](void) - { - m_Stream.reset (); - m_Stream = nullptr; - }); - } + void HTTPConnection::Terminate () + { + if (!m_Stream) return; + m_Socket->close (); + m_Stream->Close (); + + m_Socket->get_io_service ().post ([=](void) + { + m_Stream.reset (); + m_Stream = nullptr; + }); + } - void HTTPConnection::Receive () - { - m_Socket->async_read_some (boost::asio::buffer (m_Buffer, HTTP_CONNECTION_BUFFER_SIZE), - std::bind(&HTTPConnection::HandleReceive, shared_from_this (), - std::placeholders::_1, std::placeholders::_2)); - } + void HTTPConnection::Receive () + { + m_Socket->async_read_some (boost::asio::buffer (m_Buffer, HTTP_CONNECTION_BUFFER_SIZE), + std::bind(&HTTPConnection::HandleReceive, shared_from_this (), + std::placeholders::_1, std::placeholders::_2)); + } - void HTTPConnection::HandleReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred) - { - if (!ecode) - { - if (!m_Stream) // new request - { - m_Buffer[bytes_transferred] = 0; - m_BufferLen = bytes_transferred; - RunRequest(); - } - else // follow-on - m_Stream->Send ((uint8_t *)m_Buffer, bytes_transferred); - Receive (); - } - else if (ecode != boost::asio::error::operation_aborted) - Terminate (); - } + void HTTPConnection::HandleReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred) + { + if (!ecode) + { + if (!m_Stream) // new request + { + m_Buffer[bytes_transferred] = 0; + m_BufferLen = bytes_transferred; + RunRequest(); + } + else // follow-on + m_Stream->Send ((uint8_t *)m_Buffer, bytes_transferred); + Receive (); + } + else if (ecode != boost::asio::error::operation_aborted) + Terminate (); + } - void HTTPConnection::RunRequest () - { - auto address = ExtractAddress (); - if (address.length () > 1 && address[1] != '?') // not just '/' or '/?' - { - std::string uri ("/"), b32; - size_t pos = address.find ('/', 1); - if (pos == std::string::npos) - b32 = address.substr (1); // excluding leading '/' to end of line - else - { - b32 = address.substr (1, pos - 1); // excluding leading '/' to next '/' - uri = address.substr (pos); // rest of line - } + void HTTPConnection::RunRequest () + { + auto address = ExtractAddress (); + if (address.length () > 1 && address[1] != '?') // not just '/' or '/?' + { + std::string uri ("/"), b32; + size_t pos = address.find ('/', 1); + if (pos == std::string::npos) + b32 = address.substr (1); // excluding leading '/' to end of line + else + { + b32 = address.substr (1, pos - 1); // excluding leading '/' to next '/' + uri = address.substr (pos); // rest of line + } - HandleDestinationRequest (b32, uri); - } - else - HandleRequest (address); - } + HandleDestinationRequest (b32, uri); + } + else + HandleRequest (address); + } - std::string HTTPConnection::ExtractAddress () - { - char * get = strstr (m_Buffer, "GET"); - if (get) - { - char * http = strstr (get, "HTTP"); - if (http) - return std::string (get + 4, http - get - 5); - } - return ""; - } + std::string HTTPConnection::ExtractAddress () + { + char * get = strstr (m_Buffer, "GET"); + if (get) + { + char * http = strstr (get, "HTTP"); + if (http) + return std::string (get + 4, http - get - 5); + } + return ""; + } - void HTTPConnection::ExtractParams (const std::string& str, std::map& params) - { - if (str[0] != '&') return; - size_t pos = 1, end; - do - { - end = str.find ('&', pos); - std::string param = str.substr (pos, end - pos); - LogPrint (param); - size_t e = param.find ('='); - if (e != std::string::npos) - params[param.substr(0, e)] = param.substr(e+1); - pos = end + 1; - } - while (end != std::string::npos); - } - - void HTTPConnection::HandleWriteReply (const boost::system::error_code& ecode) - { - if (ecode != boost::asio::error::operation_aborted) - { - boost::system::error_code ignored_ec; - m_Socket->shutdown(boost::asio::ip::tcp::socket::shutdown_both, ignored_ec); - Terminate (); - } - } + void HTTPConnection::ExtractParams (const std::string& str, std::map& params) + { + if (str[0] != '&') return; + size_t pos = 1, end; + do + { + end = str.find ('&', pos); + std::string param = str.substr (pos, end - pos); + LogPrint (param); + size_t e = param.find ('='); + if (e != std::string::npos) + params[param.substr(0, e)] = param.substr(e+1); + pos = end + 1; + } + while (end != std::string::npos); + } + + void HTTPConnection::HandleWriteReply (const boost::system::error_code& ecode) + { + if (ecode != boost::asio::error::operation_aborted) + { + boost::system::error_code ignored_ec; + m_Socket->shutdown(boost::asio::ip::tcp::socket::shutdown_both, ignored_ec); + Terminate (); + } + } - void HTTPConnection::HandleWrite (const boost::system::error_code& ecode) - { - if (ecode || (m_Stream && !m_Stream->IsOpen ())) - { - if (ecode != boost::asio::error::operation_aborted) - Terminate (); - } - else // data keeps coming - AsyncStreamReceive (); - } + void HTTPConnection::HandleWrite (const boost::system::error_code& ecode) + { + if (ecode || (m_Stream && !m_Stream->IsOpen ())) + { + if (ecode != boost::asio::error::operation_aborted) + Terminate (); + } + else // data keeps coming + AsyncStreamReceive (); + } - void HTTPConnection::HandleRequest (const std::string& address) - { - std::stringstream s; - // Html5 head start - s << "\n"; // TODO: Add support for locale. - s << ""; // TODO: Find something to parse html/template system. This is horrible. - s << "Purple I2P " << VERSION " Webconsole"; - // Head end - if (address.length () > 1) - HandleCommand (address.substr (2), s); - else - FillContent (s); - s << ""; - SendReply (s.str ()); - } + void HTTPConnection::HandleRequest (const std::string& address) + { + std::stringstream s; + // Html5 head start + s << "\n"; // TODO: Add support for locale. + s << ""; // TODO: Find something to parse html/template system. This is horrible. + s << "Purple I2P " << VERSION " Webconsole"; + // Head end + if (address.length () > 1) + HandleCommand (address.substr (2), s); + else + FillContent (s); + s << ""; + SendReply (s.str ()); + } - void HTTPConnection::FillContent (std::stringstream& s) - { - s << "

Welcome to the Webconsole!


"; - s << "Uptime: " << boost::posix_time::to_simple_string ( - boost::posix_time::time_duration (boost::posix_time::seconds ( - i2p::context.GetUptime ()))) << "
"; - s << "Status: "; - switch (i2p::context.GetStatus ()) - { - case eRouterStatusOK: s << "OK"; break; - case eRouterStatusTesting: s << "Testing"; break; - case eRouterStatusFirewalled: s << "Firewalled"; break; - default: s << "Unknown"; - } - s << "
"; - s << "Tunnel creation success rate: " << i2p::tunnel::tunnels.GetTunnelCreationSuccessRate () << "%
"; - s << "Received: " << i2p::transport::transports.GetTotalReceivedBytes ()/1000 << "K"; - s << " (" << i2p::transport::transports.GetInBandwidth () <<" Bps)
"; - s << "Sent: " << i2p::transport::transports.GetTotalSentBytes ()/1000 << "K"; - s << " (" << i2p::transport::transports.GetOutBandwidth () <<" Bps)
"; - s << "Data path: " << i2p::util::filesystem::GetDataDir().string() << "

"; - s << "Our external address:" << "
" ; - for (auto& address : i2p::context.GetRouterInfo().GetAddresses()) - { - switch (address.transportStyle) - { - case i2p::data::RouterInfo::eTransportNTCP: - if (address.host.is_v6 ()) - s << "NTCP6  "; - else - s << "NTCP  "; - break; - case i2p::data::RouterInfo::eTransportSSU: - if (address.host.is_v6 ()) - s << "SSU6     "; - else - s << "SSU     "; - break; - default: - s << "Unknown  "; - } - s << address.host.to_string() << ":" << address.port << "
"; - } - s << "
Routers: " << i2p::data::netdb.GetNumRouters () << " "; - s << "Floodfills: " << i2p::data::netdb.GetNumFloodfills () << " "; - s << "LeaseSets: " << i2p::data::netdb.GetNumLeaseSets () << "
"; + void HTTPConnection::FillContent (std::stringstream& s) + { + s << "

Welcome to the Webconsole!


"; + s << "Uptime: " << boost::posix_time::to_simple_string ( + boost::posix_time::time_duration (boost::posix_time::seconds ( + i2p::context.GetUptime ()))) << "
"; + s << "Status: "; + switch (i2p::context.GetStatus ()) + { + case eRouterStatusOK: s << "OK"; break; + case eRouterStatusTesting: s << "Testing"; break; + case eRouterStatusFirewalled: s << "Firewalled"; break; + default: s << "Unknown"; + } + s << "
"; + s << "Tunnel creation success rate: " << i2p::tunnel::tunnels.GetTunnelCreationSuccessRate () << "%
"; + s << "Received: " << i2p::transport::transports.GetTotalReceivedBytes ()/1000 << "K"; + s << " (" << i2p::transport::transports.GetInBandwidth () <<" Bps)
"; + s << "Sent: " << i2p::transport::transports.GetTotalSentBytes ()/1000 << "K"; + s << " (" << i2p::transport::transports.GetOutBandwidth () <<" Bps)
"; + s << "Data path: " << i2p::util::filesystem::GetDataDir().string() << "

"; + s << "Our external address:" << "
" ; + for (auto& address : i2p::context.GetRouterInfo().GetAddresses()) + { + switch (address.transportStyle) + { + case i2p::data::RouterInfo::eTransportNTCP: + if (address.host.is_v6 ()) + s << "NTCP6  "; + else + s << "NTCP  "; + break; + case i2p::data::RouterInfo::eTransportSSU: + if (address.host.is_v6 ()) + s << "SSU6     "; + else + s << "SSU     "; + break; + default: + s << "Unknown  "; + } + s << address.host.to_string() << ":" << address.port << "
"; + } + s << "
Routers: " << i2p::data::netdb.GetNumRouters () << " "; + s << "Floodfills: " << i2p::data::netdb.GetNumFloodfills () << " "; + s << "LeaseSets: " << i2p::data::netdb.GetNumLeaseSets () << "
"; - s << "
Local destinations"; - s << "
Tunnels"; - s << "
Transit tunnels"; - s << "
Transports"; - if (i2p::client::context.GetSAMBridge ()) - s << "
SAM sessions"; - s << "
"; - - if (i2p::context.AcceptsTunnels ()) - s << "
Stop accepting tunnels
"; - else - s << "
Start accepting tunnels
"; + s << "
Local destinations"; + s << "
Tunnels"; + s << "
Transit tunnels"; + s << "
Transports"; + if (i2p::client::context.GetSAMBridge ()) + s << "
SAM sessions"; + s << "
"; + + if (i2p::context.AcceptsTunnels ()) + s << "
Stop accepting tunnels
"; + else + s << "
Start accepting tunnels
"; - s << "

Flibusta

"; - } + s << "

Flibusta

"; + } - void HTTPConnection::HandleCommand (const std::string& command, std::stringstream& s) - { - size_t paramsPos = command.find('&'); - std::string cmd = command.substr (0, paramsPos); - if (cmd == HTTP_COMMAND_TRANSPORTS) - ShowTransports (s); - else if (cmd == HTTP_COMMAND_TUNNELS) - ShowTunnels (s); - else if (cmd == HTTP_COMMAND_TRANSIT_TUNNELS) - ShowTransitTunnels (s); - else if (cmd == HTTP_COMMAND_START_ACCEPTING_TUNNELS) - StartAcceptingTunnels (s); - else if (cmd == HTTP_COMMAND_STOP_ACCEPTING_TUNNELS) - StopAcceptingTunnels (s); - else if (cmd == HTTP_COMMAND_LOCAL_DESTINATIONS) - ShowLocalDestinations (s); - else if (cmd == HTTP_COMMAND_LOCAL_DESTINATION) - { - std::map params; - ExtractParams (command.substr (paramsPos), params); - auto b32 = params[HTTP_PARAM_BASE32_ADDRESS]; - ShowLocalDestination (b32, s); - } - else if (cmd == HTTP_COMMAND_SAM_SESSIONS) - ShowSAMSessions (s); - else if (cmd == HTTP_COMMAND_SAM_SESSION) - { - std::map params; - ExtractParams (command.substr (paramsPos), params); - auto id = params[HTTP_PARAM_SAM_SESSION_ID]; - ShowSAMSession (id, s); - } - } + void HTTPConnection::HandleCommand (const std::string& command, std::stringstream& s) + { + size_t paramsPos = command.find('&'); + std::string cmd = command.substr (0, paramsPos); + if (cmd == HTTP_COMMAND_TRANSPORTS) + ShowTransports (s); + else if (cmd == HTTP_COMMAND_TUNNELS) + ShowTunnels (s); + else if (cmd == HTTP_COMMAND_TRANSIT_TUNNELS) + ShowTransitTunnels (s); + else if (cmd == HTTP_COMMAND_START_ACCEPTING_TUNNELS) + StartAcceptingTunnels (s); + else if (cmd == HTTP_COMMAND_STOP_ACCEPTING_TUNNELS) + StopAcceptingTunnels (s); + else if (cmd == HTTP_COMMAND_LOCAL_DESTINATIONS) + ShowLocalDestinations (s); + else if (cmd == HTTP_COMMAND_LOCAL_DESTINATION) + { + std::map params; + ExtractParams (command.substr (paramsPos), params); + auto b32 = params[HTTP_PARAM_BASE32_ADDRESS]; + ShowLocalDestination (b32, s); + } + else if (cmd == HTTP_COMMAND_SAM_SESSIONS) + ShowSAMSessions (s); + else if (cmd == HTTP_COMMAND_SAM_SESSION) + { + std::map params; + ExtractParams (command.substr (paramsPos), params); + auto id = params[HTTP_PARAM_SAM_SESSION_ID]; + ShowSAMSession (id, s); + } + } - void HTTPConnection::ShowTransports (std::stringstream& s) - { - auto ntcpServer = i2p::transport::transports.GetNTCPServer (); - if (ntcpServer) - { - s << "NTCP
"; - for (auto it: ntcpServer->GetNTCPSessions ()) - { - if (it.second && it.second->IsEstablished ()) - { - // incoming connection doesn't have remote RI - auto outgoing = it.second->GetRemoteRouter (); - if (outgoing) s << "-->"; - s << it.second->GetRemoteIdentity ().GetIdentHash ().ToBase64 ().substr (0, 4) << ": " - << it.second->GetSocket ().remote_endpoint().address ().to_string (); - if (!outgoing) s << "-->"; - s << " [" << it.second->GetNumSentBytes () << ":" << it.second->GetNumReceivedBytes () << "]"; - s << "
"; - } - s << std::endl; - } - } - auto ssuServer = i2p::transport::transports.GetSSUServer (); - if (ssuServer) - { - s << "
SSU
"; - for (auto it: ssuServer->GetSessions ()) - { - // incoming connections don't have remote router - auto outgoing = it.second->GetRemoteRouter (); - auto endpoint = it.second->GetRemoteEndpoint (); - if (outgoing) s << "-->"; - s << endpoint.address ().to_string () << ":" << endpoint.port (); - if (!outgoing) s << "-->"; - s << " [" << it.second->GetNumSentBytes () << ":" << it.second->GetNumReceivedBytes () << "]"; - if (it.second->GetRelayTag ()) - s << " [itag:" << it.second->GetRelayTag () << "]"; - s << "
"; - s << std::endl; - } - } - } - - void HTTPConnection::ShowTunnels (std::stringstream& s) - { - s << "Queue size:" << i2p::tunnel::tunnels.GetQueueSize () << "
"; + void HTTPConnection::ShowTransports (std::stringstream& s) + { + auto ntcpServer = i2p::transport::transports.GetNTCPServer (); + if (ntcpServer) + { + s << "NTCP
"; + for (auto it: ntcpServer->GetNTCPSessions ()) + { + if (it.second && it.second->IsEstablished ()) + { + // incoming connection doesn't have remote RI + auto outgoing = it.second->GetRemoteRouter (); + if (outgoing) s << "-->"; + s << it.second->GetRemoteIdentity ().GetIdentHash ().ToBase64 ().substr (0, 4) << ": " + << it.second->GetSocket ().remote_endpoint().address ().to_string (); + if (!outgoing) s << "-->"; + s << " [" << it.second->GetNumSentBytes () << ":" << it.second->GetNumReceivedBytes () << "]"; + s << "
"; + } + s << std::endl; + } + } + auto ssuServer = i2p::transport::transports.GetSSUServer (); + if (ssuServer) + { + s << "
SSU
"; + for (auto it: ssuServer->GetSessions ()) + { + // incoming connections don't have remote router + auto outgoing = it.second->GetRemoteRouter (); + auto endpoint = it.second->GetRemoteEndpoint (); + if (outgoing) s << "-->"; + s << endpoint.address ().to_string () << ":" << endpoint.port (); + if (!outgoing) s << "-->"; + s << " [" << it.second->GetNumSentBytes () << ":" << it.second->GetNumReceivedBytes () << "]"; + if (it.second->GetRelayTag ()) + s << " [itag:" << it.second->GetRelayTag () << "]"; + s << "
"; + s << std::endl; + } + } + } + + void HTTPConnection::ShowTunnels (std::stringstream& s) + { + s << "Queue size:" << i2p::tunnel::tunnels.GetQueueSize () << "
"; - for (auto it: i2p::tunnel::tunnels.GetOutboundTunnels ()) - { - it->GetTunnelConfig ()->Print (s); - auto state = it->GetState (); - if (state == i2p::tunnel::eTunnelStateFailed) - s << " " << "Failed"; - else if (state == i2p::tunnel::eTunnelStateExpiring) - s << " " << "Exp"; - s << " " << (int)it->GetNumSentBytes () << "
"; - s << std::endl; - } + for (auto it: i2p::tunnel::tunnels.GetOutboundTunnels ()) + { + it->GetTunnelConfig ()->Print (s); + auto state = it->GetState (); + if (state == i2p::tunnel::eTunnelStateFailed) + s << " " << "Failed"; + else if (state == i2p::tunnel::eTunnelStateExpiring) + s << " " << "Exp"; + s << " " << (int)it->GetNumSentBytes () << "
"; + s << std::endl; + } - for (auto it: i2p::tunnel::tunnels.GetInboundTunnels ()) - { - it.second->GetTunnelConfig ()->Print (s); - auto state = it.second->GetState (); - if (state == i2p::tunnel::eTunnelStateFailed) - s << " " << "Failed"; - else if (state == i2p::tunnel::eTunnelStateExpiring) - s << " " << "Exp"; - s << " " << (int)it.second->GetNumReceivedBytes () << "
"; - s << std::endl; - } - } + for (auto it: i2p::tunnel::tunnels.GetInboundTunnels ()) + { + it.second->GetTunnelConfig ()->Print (s); + auto state = it.second->GetState (); + if (state == i2p::tunnel::eTunnelStateFailed) + s << " " << "Failed"; + else if (state == i2p::tunnel::eTunnelStateExpiring) + s << " " << "Exp"; + s << " " << (int)it.second->GetNumReceivedBytes () << "
"; + s << std::endl; + } + } - void HTTPConnection::ShowTransitTunnels (std::stringstream& s) - { - for (auto it: i2p::tunnel::tunnels.GetTransitTunnels ()) - { - if (dynamic_cast(it.second)) - s << it.second->GetTunnelID () << "-->"; - else if (dynamic_cast(it.second)) - s << "-->" << it.second->GetTunnelID (); - else - s << "-->" << it.second->GetTunnelID () << "-->"; - s << " " << it.second->GetNumTransmittedBytes () << "
"; - } - } + void HTTPConnection::ShowTransitTunnels (std::stringstream& s) + { + for (auto it: i2p::tunnel::tunnels.GetTransitTunnels ()) + { + if (dynamic_cast(it.second)) + s << it.second->GetTunnelID () << "-->"; + else if (dynamic_cast(it.second)) + s << "-->" << it.second->GetTunnelID (); + else + s << "-->" << it.second->GetTunnelID () << "-->"; + s << " " << it.second->GetNumTransmittedBytes () << "
"; + } + } - void HTTPConnection::ShowLocalDestinations (std::stringstream& s) - { - for (auto& it: i2p::client::context.GetDestinations ()) - { - auto ident = it.second->GetIdentHash ();; - s << ""; - s << i2p::client::context.GetAddressBook ().ToAddress(ident) << "
" << std::endl; - } - } + void HTTPConnection::ShowLocalDestinations (std::stringstream& s) + { + for (auto& it: i2p::client::context.GetDestinations ()) + { + auto ident = it.second->GetIdentHash ();; + s << ""; + s << i2p::client::context.GetAddressBook ().ToAddress(ident) << "
" << std::endl; + } + } - void HTTPConnection::ShowLocalDestination (const std::string& b32, std::stringstream& s) - { - i2p::data::IdentHash ident; - ident.FromBase32 (b32); - auto dest = i2p::client::context.FindLocalDestination (ident); - if (dest) - { - s << "Base64:
" << dest->GetIdentity ().ToBase64 () << "

"; - s << "LeaseSets: " << dest->GetNumRemoteLeaseSets () << "
"; - auto pool = dest->GetTunnelPool (); - if (pool) - { - s << "Tunnels:
"; - for (auto it: pool->GetOutboundTunnels ()) - { - it->GetTunnelConfig ()->Print (s); - auto state = it->GetState (); - if (state == i2p::tunnel::eTunnelStateFailed) - s << " " << "Failed"; - else if (state == i2p::tunnel::eTunnelStateExpiring) - s << " " << "Exp"; - s << "
" << std::endl; - } - for (auto it: pool->GetInboundTunnels ()) - { - it->GetTunnelConfig ()->Print (s); - auto state = it->GetState (); - if (state == i2p::tunnel::eTunnelStateFailed) - s << " " << "Failed"; - else if (state == i2p::tunnel::eTunnelStateExpiring) - s << " " << "Exp"; - s << "
" << std::endl; - } - } - s << "
Streams:
"; - for (auto it: dest->GetStreamingDestination ()->GetStreams ()) - { - s << it.first << "->" << i2p::client::context.GetAddressBook ().ToAddress(it.second->GetRemoteIdentity ()) << " "; - s << " [" << it.second->GetNumSentBytes () << ":" << it.second->GetNumReceivedBytes () << "]"; - s << " [out:" << it.second->GetSendQueueSize () << "][in:" << it.second->GetReceiveQueueSize () << "]"; - s << "[buf:" << it.second->GetSendBufferSize () << "]"; - s << "[RTT:" << it.second->GetRTT () << "]"; - s << "[Window:" << it.second->GetWindowSize () << "]"; - s << "[Status:" << (int)it.second->GetStatus () << "]"; - s << "
"<< std::endl; - } - } - } + void HTTPConnection::ShowLocalDestination (const std::string& b32, std::stringstream& s) + { + i2p::data::IdentHash ident; + ident.FromBase32 (b32); + auto dest = i2p::client::context.FindLocalDestination (ident); + if (dest) + { + s << "Base64:
" << dest->GetIdentity ().ToBase64 () << "

"; + s << "LeaseSets: " << dest->GetNumRemoteLeaseSets () << "
"; + auto pool = dest->GetTunnelPool (); + if (pool) + { + s << "Tunnels:
"; + for (auto it: pool->GetOutboundTunnels ()) + { + it->GetTunnelConfig ()->Print (s); + auto state = it->GetState (); + if (state == i2p::tunnel::eTunnelStateFailed) + s << " " << "Failed"; + else if (state == i2p::tunnel::eTunnelStateExpiring) + s << " " << "Exp"; + s << "
" << std::endl; + } + for (auto it: pool->GetInboundTunnels ()) + { + it->GetTunnelConfig ()->Print (s); + auto state = it->GetState (); + if (state == i2p::tunnel::eTunnelStateFailed) + s << " " << "Failed"; + else if (state == i2p::tunnel::eTunnelStateExpiring) + s << " " << "Exp"; + s << "
" << std::endl; + } + } + s << "
Streams:
"; + for (auto it: dest->GetStreamingDestination ()->GetStreams ()) + { + s << it.first << "->" << i2p::client::context.GetAddressBook ().ToAddress(it.second->GetRemoteIdentity ()) << " "; + s << " [" << it.second->GetNumSentBytes () << ":" << it.second->GetNumReceivedBytes () << "]"; + s << " [out:" << it.second->GetSendQueueSize () << "][in:" << it.second->GetReceiveQueueSize () << "]"; + s << "[buf:" << it.second->GetSendBufferSize () << "]"; + s << "[RTT:" << it.second->GetRTT () << "]"; + s << "[Window:" << it.second->GetWindowSize () << "]"; + s << "[Status:" << (int)it.second->GetStatus () << "]"; + s << "
"<< std::endl; + } + } + } - void HTTPConnection::ShowSAMSessions (std::stringstream& s) - { - auto sam = i2p::client::context.GetSAMBridge (); - if (sam) - { - for (auto& it: sam->GetSessions ()) - { - s << ""; - s << it.first << "
" << std::endl; - } - } - } + void HTTPConnection::ShowSAMSessions (std::stringstream& s) + { + auto sam = i2p::client::context.GetSAMBridge (); + if (sam) + { + for (auto& it: sam->GetSessions ()) + { + s << ""; + s << it.first << "
" << std::endl; + } + } + } - void HTTPConnection::ShowSAMSession (const std::string& id, std::stringstream& s) - { - auto sam = i2p::client::context.GetSAMBridge (); - if (sam) - { - auto session = sam->FindSession (id); - if (session) - { - auto& ident = session->localDestination->GetIdentHash(); - s << ""; - s << i2p::client::context.GetAddressBook ().ToAddress(ident) << "
" << std::endl; - s << "Streams:
"; - for (auto it: session->sockets) - { - switch (it->GetSocketType ()) - { - case i2p::client::eSAMSocketTypeSession: - s << "session"; - break; - case i2p::client::eSAMSocketTypeStream: - s << "stream"; - break; - case i2p::client::eSAMSocketTypeAcceptor: - s << "acceptor"; - break; - default: - s << "unknown"; - } - s << " [" << it->GetSocket ().remote_endpoint() << "]"; - s << "
" << std::endl; - } - } - } - } - - void HTTPConnection::StartAcceptingTunnels (std::stringstream& s) - { - i2p::context.SetAcceptsTunnels (true); - s << "Accepting tunnels started" << std::endl; - } + void HTTPConnection::ShowSAMSession (const std::string& id, std::stringstream& s) + { + auto sam = i2p::client::context.GetSAMBridge (); + if (sam) + { + auto session = sam->FindSession (id); + if (session) + { + auto& ident = session->localDestination->GetIdentHash(); + s << ""; + s << i2p::client::context.GetAddressBook ().ToAddress(ident) << "
" << std::endl; + s << "Streams:
"; + for (auto it: session->sockets) + { + switch (it->GetSocketType ()) + { + case i2p::client::eSAMSocketTypeSession: + s << "session"; + break; + case i2p::client::eSAMSocketTypeStream: + s << "stream"; + break; + case i2p::client::eSAMSocketTypeAcceptor: + s << "acceptor"; + break; + default: + s << "unknown"; + } + s << " [" << it->GetSocket ().remote_endpoint() << "]"; + s << "
" << std::endl; + } + } + } + } + + void HTTPConnection::StartAcceptingTunnels (std::stringstream& s) + { + i2p::context.SetAcceptsTunnels (true); + s << "Accepting tunnels started" << std::endl; + } - void HTTPConnection::StopAcceptingTunnels (std::stringstream& s) - { - i2p::context.SetAcceptsTunnels (false); - s << "Accepting tunnels stopped" << std::endl; - } + void HTTPConnection::StopAcceptingTunnels (std::stringstream& s) + { + i2p::context.SetAcceptsTunnels (false); + s << "Accepting tunnels stopped" << std::endl; + } - void HTTPConnection::HandleDestinationRequest (const std::string& address, const std::string& uri) - { - std::string request = "GET " + uri + " HTTP/1.1\r\nHost:" + address + "\r\n"; - LogPrint("HTTP Client Request: ", request); - SendToAddress (address, 80, request.c_str (), request.size ()); - } + void HTTPConnection::HandleDestinationRequest (const std::string& address, const std::string& uri) + { + std::string request = "GET " + uri + " HTTP/1.1\r\nHost:" + address + "\r\n"; + LogPrint("HTTP Client Request: ", request); + SendToAddress (address, 80, request.c_str (), request.size ()); + } - void HTTPConnection::SendToAddress (const std::string& address, int port, const char * buf, size_t len) - { - i2p::data::IdentHash destination; - if (!i2p::client::context.GetAddressBook ().GetIdentHash (address, destination)) - { - LogPrint ("Unknown address ", address); - SendReply ("" + itoopieImage + "
Unknown address " + address + "", 404); - return; - } + void HTTPConnection::SendToAddress (const std::string& address, int port, const char * buf, size_t len) + { + i2p::data::IdentHash destination; + if (!i2p::client::context.GetAddressBook ().GetIdentHash (address, destination)) + { + LogPrint ("Unknown address ", address); + SendReply ("" + itoopieImage + "
Unknown address " + address + "", 404); + return; + } - auto leaseSet = i2p::client::context.GetSharedLocalDestination ()->FindLeaseSet (destination); - if (leaseSet && leaseSet->HasNonExpiredLeases ()) - SendToDestination (leaseSet, port, buf, len); - else - { - memcpy (m_Buffer, buf, len); - m_BufferLen = len; - i2p::client::context.GetSharedLocalDestination ()->RequestDestination (destination); - m_Timer.expires_from_now (boost::posix_time::seconds(HTTP_DESTINATION_REQUEST_TIMEOUT)); - m_Timer.async_wait (std::bind (&HTTPConnection::HandleDestinationRequestTimeout, - shared_from_this (), std::placeholders::_1, destination, port, m_Buffer, m_BufferLen)); - } - } - - void HTTPConnection::HandleDestinationRequestTimeout (const boost::system::error_code& ecode, - i2p::data::IdentHash destination, int port, const char * buf, size_t len) - { - if (ecode != boost::asio::error::operation_aborted) - { - auto leaseSet = i2p::client::context.GetSharedLocalDestination ()->FindLeaseSet (destination); - if (leaseSet && leaseSet->HasNonExpiredLeases ()) - SendToDestination (leaseSet, port, buf, len); - else - // still no LeaseSet - SendReply (leaseSet ? "" + itoopieImage + "
Leases expired" : "" + itoopieImage + "LeaseSet not found", 504); - } - } - - void HTTPConnection::SendToDestination (std::shared_ptr remote, int port, const char * buf, size_t len) - { - if (!m_Stream) - m_Stream = i2p::client::context.GetSharedLocalDestination ()->CreateStream (remote, port); - if (m_Stream) - { - m_Stream->Send ((uint8_t *)buf, len); - AsyncStreamReceive (); - } - } + auto leaseSet = i2p::client::context.GetSharedLocalDestination ()->FindLeaseSet (destination); + if (leaseSet && leaseSet->HasNonExpiredLeases ()) + SendToDestination (leaseSet, port, buf, len); + else + { + memcpy (m_Buffer, buf, len); + m_BufferLen = len; + i2p::client::context.GetSharedLocalDestination ()->RequestDestination (destination); + m_Timer.expires_from_now (boost::posix_time::seconds(HTTP_DESTINATION_REQUEST_TIMEOUT)); + m_Timer.async_wait (std::bind (&HTTPConnection::HandleDestinationRequestTimeout, + shared_from_this (), std::placeholders::_1, destination, port, m_Buffer, m_BufferLen)); + } + } + + void HTTPConnection::HandleDestinationRequestTimeout (const boost::system::error_code& ecode, + i2p::data::IdentHash destination, int port, const char * buf, size_t len) + { + if (ecode != boost::asio::error::operation_aborted) + { + auto leaseSet = i2p::client::context.GetSharedLocalDestination ()->FindLeaseSet (destination); + if (leaseSet && leaseSet->HasNonExpiredLeases ()) + SendToDestination (leaseSet, port, buf, len); + else + // still no LeaseSet + SendReply (leaseSet ? "" + itoopieImage + "
Leases expired" : "" + itoopieImage + "LeaseSet not found", 504); + } + } + + void HTTPConnection::SendToDestination (std::shared_ptr remote, int port, const char * buf, size_t len) + { + if (!m_Stream) + m_Stream = i2p::client::context.GetSharedLocalDestination ()->CreateStream (remote, port); + if (m_Stream) + { + m_Stream->Send ((uint8_t *)buf, len); + AsyncStreamReceive (); + } + } - void HTTPConnection::AsyncStreamReceive () - { - if (m_Stream) - m_Stream->AsyncReceive (boost::asio::buffer (m_StreamBuffer, 8192), - std::bind (&HTTPConnection::HandleStreamReceive, shared_from_this (), - std::placeholders::_1, std::placeholders::_2), - 45); // 45 seconds timeout - } + void HTTPConnection::AsyncStreamReceive () + { + if (m_Stream) + m_Stream->AsyncReceive (boost::asio::buffer (m_StreamBuffer, 8192), + std::bind (&HTTPConnection::HandleStreamReceive, shared_from_this (), + std::placeholders::_1, std::placeholders::_2), + 45); // 45 seconds timeout + } - void HTTPConnection::HandleStreamReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred) - { - if (!ecode) - { - boost::asio::async_write (*m_Socket, boost::asio::buffer (m_StreamBuffer, bytes_transferred), - std::bind (&HTTPConnection::HandleWrite, shared_from_this (), std::placeholders::_1)); - } - else - { - if (ecode == boost::asio::error::timed_out) - SendReply ("" + itoopieImage + "
Not responding", 504); - else if (ecode != boost::asio::error::operation_aborted) - Terminate (); - } - } + void HTTPConnection::HandleStreamReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred) + { + if (!ecode) + { + boost::asio::async_write (*m_Socket, boost::asio::buffer (m_StreamBuffer, bytes_transferred), + std::bind (&HTTPConnection::HandleWrite, shared_from_this (), std::placeholders::_1)); + } + else + { + if (ecode == boost::asio::error::timed_out) + SendReply ("" + itoopieImage + "
Not responding", 504); + else if (ecode != boost::asio::error::operation_aborted) + Terminate (); + } + } - void HTTPConnection::SendReply (const std::string& content, int status) - { - m_Reply.content = content; - m_Reply.headers.resize(2); - m_Reply.headers[0].name = "Content-Length"; - m_Reply.headers[0].value = boost::lexical_cast(m_Reply.content.size()); - m_Reply.headers[1].name = "Content-Type"; - m_Reply.headers[1].value = "text/html"; + void HTTPConnection::SendReply (const std::string& content, int status) + { + m_Reply.content = content; + m_Reply.headers.resize(2); + m_Reply.headers[0].name = "Content-Length"; + m_Reply.headers[0].value = boost::lexical_cast(m_Reply.content.size()); + m_Reply.headers[1].name = "Content-Type"; + m_Reply.headers[1].value = "text/html"; - boost::asio::async_write (*m_Socket, m_Reply.to_buffers(status), - std::bind (&HTTPConnection::HandleWriteReply, shared_from_this (), std::placeholders::_1)); - } + boost::asio::async_write (*m_Socket, m_Reply.to_buffers(status), + std::bind (&HTTPConnection::HandleWriteReply, shared_from_this (), std::placeholders::_1)); + } - HTTPServer::HTTPServer (int port): - m_Thread (nullptr), m_Work (m_Service), - m_Acceptor (m_Service, boost::asio::ip::tcp::endpoint (boost::asio::ip::tcp::v4 (), port)), - m_NewSocket (nullptr) - { + HTTPServer::HTTPServer (int port): + m_Thread (nullptr), m_Work (m_Service), + m_Acceptor (m_Service, boost::asio::ip::tcp::endpoint (boost::asio::ip::tcp::v4 (), port)), + m_NewSocket (nullptr) + { - } + } - HTTPServer::~HTTPServer () - { - Stop (); - } + HTTPServer::~HTTPServer () + { + Stop (); + } - void HTTPServer::Start () - { - m_Thread = new std::thread (std::bind (&HTTPServer::Run, this)); - m_Acceptor.listen (); - Accept (); - } + void HTTPServer::Start () + { + m_Thread = new std::thread (std::bind (&HTTPServer::Run, this)); + m_Acceptor.listen (); + Accept (); + } - void HTTPServer::Stop () - { - m_Acceptor.close(); - m_Service.stop (); - if (m_Thread) + void HTTPServer::Stop () + { + m_Acceptor.close(); + m_Service.stop (); + if (m_Thread) { m_Thread->join (); delete m_Thread; m_Thread = nullptr; } - } + } - void HTTPServer::Run () - { - m_Service.run (); - } + void HTTPServer::Run () + { + m_Service.run (); + } - void HTTPServer::Accept () - { - m_NewSocket = new boost::asio::ip::tcp::socket (m_Service); - m_Acceptor.async_accept (*m_NewSocket, boost::bind (&HTTPServer::HandleAccept, this, - boost::asio::placeholders::error)); - } + void HTTPServer::Accept () + { + m_NewSocket = new boost::asio::ip::tcp::socket (m_Service); + m_Acceptor.async_accept (*m_NewSocket, boost::bind (&HTTPServer::HandleAccept, this, + boost::asio::placeholders::error)); + } - void HTTPServer::HandleAccept(const boost::system::error_code& ecode) - { - if (!ecode) - { - CreateConnection(m_NewSocket); - Accept (); - } - } + void HTTPServer::HandleAccept(const boost::system::error_code& ecode) + { + if (!ecode) + { + CreateConnection(m_NewSocket); + Accept (); + } + } - void HTTPServer::CreateConnection(boost::asio::ip::tcp::socket * m_NewSocket) - { - auto conn = std::make_shared (m_NewSocket); - conn->Receive (); - } + void HTTPServer::CreateConnection(boost::asio::ip::tcp::socket * m_NewSocket) + { + auto conn = std::make_shared (m_NewSocket); + conn->Receive (); + } } } diff --git a/HTTPServer.h b/HTTPServer.h index b289cbc5..a32f8e7a 100644 --- a/HTTPServer.h +++ b/HTTPServer.h @@ -13,123 +13,123 @@ namespace i2p { namespace util { - const size_t HTTP_CONNECTION_BUFFER_SIZE = 8192; - const int HTTP_DESTINATION_REQUEST_TIMEOUT = 10; // in seconds - class HTTPConnection: public std::enable_shared_from_this - { - protected: + const size_t HTTP_CONNECTION_BUFFER_SIZE = 8192; + const int HTTP_DESTINATION_REQUEST_TIMEOUT = 10; // in seconds + class HTTPConnection: public std::enable_shared_from_this + { + protected: - struct header - { - std::string name; - std::string value; - }; + struct header + { + std::string name; + std::string value; + }; - struct request - { - std::string method; - std::string uri; - std::string host; - int port; - int http_version_major; - int http_version_minor; - std::vector
headers; - }; + struct request + { + std::string method; + std::string uri; + std::string host; + int port; + int http_version_major; + int http_version_minor; + std::vector
headers; + }; - struct reply - { - std::vector
headers; - std::string content; + struct reply + { + std::vector
headers; + std::string content; - std::vector to_buffers (int status); - }; + std::vector to_buffers (int status); + }; - public: + public: - HTTPConnection (boost::asio::ip::tcp::socket * socket): - m_Socket (socket), m_Timer (socket->get_io_service ()), - m_Stream (nullptr), m_BufferLen (0) {}; - ~HTTPConnection() { delete m_Socket; } - void Receive (); - - private: + HTTPConnection (boost::asio::ip::tcp::socket * socket): + m_Socket (socket), m_Timer (socket->get_io_service ()), + m_Stream (nullptr), m_BufferLen (0) {}; + ~HTTPConnection() { delete m_Socket; } + void Receive (); + + private: - void Terminate (); - void HandleReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred); - void AsyncStreamReceive (); - void HandleStreamReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred); - void HandleWriteReply(const boost::system::error_code& ecode); - void HandleWrite (const boost::system::error_code& ecode); - void SendReply (const std::string& content, int status = 200); + void Terminate (); + void HandleReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred); + void AsyncStreamReceive (); + void HandleStreamReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred); + void HandleWriteReply(const boost::system::error_code& ecode); + void HandleWrite (const boost::system::error_code& ecode); + void SendReply (const std::string& content, int status = 200); - void HandleRequest (const std::string& address); - void HandleCommand (const std::string& command, std::stringstream& s); - void ShowTransports (std::stringstream& s); - void ShowTunnels (std::stringstream& s); - void ShowTransitTunnels (std::stringstream& s); - void ShowLocalDestinations (std::stringstream& s); - void ShowLocalDestination (const std::string& b32, std::stringstream& s); - void ShowSAMSessions (std::stringstream& s); - void ShowSAMSession (const std::string& id, std::stringstream& s); - void StartAcceptingTunnels (std::stringstream& s); - void StopAcceptingTunnels (std::stringstream& s); - void FillContent (std::stringstream& s); - std::string ExtractAddress (); - void ExtractParams (const std::string& str, std::map& params); - - - protected: + void HandleRequest (const std::string& address); + void HandleCommand (const std::string& command, std::stringstream& s); + void ShowTransports (std::stringstream& s); + void ShowTunnels (std::stringstream& s); + void ShowTransitTunnels (std::stringstream& s); + void ShowLocalDestinations (std::stringstream& s); + void ShowLocalDestination (const std::string& b32, std::stringstream& s); + void ShowSAMSessions (std::stringstream& s); + void ShowSAMSession (const std::string& id, std::stringstream& s); + void StartAcceptingTunnels (std::stringstream& s); + void StopAcceptingTunnels (std::stringstream& s); + void FillContent (std::stringstream& s); + std::string ExtractAddress (); + void ExtractParams (const std::string& str, std::map& params); + + + protected: - boost::asio::ip::tcp::socket * m_Socket; - boost::asio::deadline_timer m_Timer; - std::shared_ptr m_Stream; - char m_Buffer[HTTP_CONNECTION_BUFFER_SIZE + 1], m_StreamBuffer[HTTP_CONNECTION_BUFFER_SIZE + 1]; - size_t m_BufferLen; - request m_Request; - reply m_Reply; + boost::asio::ip::tcp::socket * m_Socket; + boost::asio::deadline_timer m_Timer; + std::shared_ptr m_Stream; + char m_Buffer[HTTP_CONNECTION_BUFFER_SIZE + 1], m_StreamBuffer[HTTP_CONNECTION_BUFFER_SIZE + 1]; + size_t m_BufferLen; + request m_Request; + reply m_Reply; - protected: - - virtual void RunRequest (); - void HandleDestinationRequest(const std::string& address, const std::string& uri); - void SendToAddress (const std::string& address, int port, const char * buf, size_t len); - void HandleDestinationRequestTimeout (const boost::system::error_code& ecode, - i2p::data::IdentHash destination, int port, const char * buf, size_t len); - void SendToDestination (std::shared_ptr remote, int port, const char * buf, size_t len); + protected: + + virtual void RunRequest (); + void HandleDestinationRequest(const std::string& address, const std::string& uri); + void SendToAddress (const std::string& address, int port, const char * buf, size_t len); + void HandleDestinationRequestTimeout (const boost::system::error_code& ecode, + i2p::data::IdentHash destination, int port, const char * buf, size_t len); + void SendToDestination (std::shared_ptr remote, int port, const char * buf, size_t len); - public: + public: - static const std::string itoopieImage; - static const std::string itoopieFavicon; - }; + static const std::string itoopieImage; + static const std::string itoopieFavicon; + }; - class HTTPServer - { - public: + class HTTPServer + { + public: - HTTPServer (int port); - virtual ~HTTPServer (); + HTTPServer (int port); + virtual ~HTTPServer (); - void Start (); - void Stop (); + void Start (); + void Stop (); - private: + private: - void Run (); - void Accept (); - void HandleAccept(const boost::system::error_code& ecode); - - private: + void Run (); + void Accept (); + void HandleAccept(const boost::system::error_code& ecode); + + private: - std::thread * m_Thread; - boost::asio::io_service m_Service; - boost::asio::io_service::work m_Work; - boost::asio::ip::tcp::acceptor m_Acceptor; - boost::asio::ip::tcp::socket * m_NewSocket; + std::thread * m_Thread; + boost::asio::io_service m_Service; + boost::asio::io_service::work m_Work; + boost::asio::ip::tcp::acceptor m_Acceptor; + boost::asio::ip::tcp::socket * m_NewSocket; - protected: - virtual void CreateConnection(boost::asio::ip::tcp::socket * m_NewSocket); - }; + protected: + virtual void CreateConnection(boost::asio::ip::tcp::socket * m_NewSocket); + }; } } diff --git a/I2NPProtocol.cpp b/I2NPProtocol.cpp index 8ee80000..82e7a0be 100644 --- a/I2NPProtocol.cpp +++ b/I2NPProtocol.cpp @@ -16,610 +16,610 @@ using namespace i2p::transport; namespace i2p { - I2NPMessage * NewI2NPMessage () - { - return new I2NPMessageBuffer(); - } - - I2NPMessage * NewI2NPShortMessage () - { - return new I2NPMessageBuffer(); - } + I2NPMessage * NewI2NPMessage () + { + return new I2NPMessageBuffer(); + } + + I2NPMessage * NewI2NPShortMessage () + { + return new I2NPMessageBuffer(); + } - I2NPMessage * NewI2NPMessage (size_t len) - { - return (len < I2NP_MAX_SHORT_MESSAGE_SIZE/2) ? NewI2NPShortMessage () : NewI2NPMessage (); - } - - void DeleteI2NPMessage (I2NPMessage * msg) - { - delete msg; - } + I2NPMessage * NewI2NPMessage (size_t len) + { + return (len < I2NP_MAX_SHORT_MESSAGE_SIZE/2) ? NewI2NPShortMessage () : NewI2NPMessage (); + } + + void DeleteI2NPMessage (I2NPMessage * msg) + { + delete msg; + } - std::shared_ptr ToSharedI2NPMessage (I2NPMessage * msg) - { - return std::shared_ptr(msg, DeleteI2NPMessage); - } + std::shared_ptr ToSharedI2NPMessage (I2NPMessage * msg) + { + return std::shared_ptr(msg, DeleteI2NPMessage); + } - void I2NPMessage::FillI2NPMessageHeader (I2NPMessageType msgType, uint32_t replyMsgID) - { - SetTypeID (msgType); - if (replyMsgID) // for tunnel creation - SetMsgID (replyMsgID); - else - SetMsgID (i2p::context.GetRandomNumberGenerator ().GenerateWord32 ()); - SetExpiration (i2p::util::GetMillisecondsSinceEpoch () + 5000); // TODO: 5 secs is a magic number - UpdateSize (); - UpdateChks (); - } - - void I2NPMessage::RenewI2NPMessageHeader () - { - SetMsgID (i2p::context.GetRandomNumberGenerator ().GenerateWord32 ()); - SetExpiration (i2p::util::GetMillisecondsSinceEpoch () + 5000); - } + void I2NPMessage::FillI2NPMessageHeader (I2NPMessageType msgType, uint32_t replyMsgID) + { + SetTypeID (msgType); + if (replyMsgID) // for tunnel creation + SetMsgID (replyMsgID); + else + SetMsgID (i2p::context.GetRandomNumberGenerator ().GenerateWord32 ()); + SetExpiration (i2p::util::GetMillisecondsSinceEpoch () + 5000); // TODO: 5 secs is a magic number + UpdateSize (); + UpdateChks (); + } + + void I2NPMessage::RenewI2NPMessageHeader () + { + SetMsgID (i2p::context.GetRandomNumberGenerator ().GenerateWord32 ()); + SetExpiration (i2p::util::GetMillisecondsSinceEpoch () + 5000); + } - I2NPMessage * CreateI2NPMessage (I2NPMessageType msgType, const uint8_t * buf, int len, uint32_t replyMsgID) - { - I2NPMessage * msg = NewI2NPMessage (len); - if (msg->len + len < msg->maxLen) - { - memcpy (msg->GetPayload (), buf, len); - msg->len += len; - } - else - LogPrint (eLogError, "I2NP message length ", len, " exceeds max length"); - msg->FillI2NPMessageHeader (msgType, replyMsgID); - return msg; - } + I2NPMessage * CreateI2NPMessage (I2NPMessageType msgType, const uint8_t * buf, int len, uint32_t replyMsgID) + { + I2NPMessage * msg = NewI2NPMessage (len); + if (msg->len + len < msg->maxLen) + { + memcpy (msg->GetPayload (), buf, len); + msg->len += len; + } + else + LogPrint (eLogError, "I2NP message length ", len, " exceeds max length"); + msg->FillI2NPMessageHeader (msgType, replyMsgID); + return msg; + } - std::shared_ptr CreateI2NPMessage (const uint8_t * buf, int len, std::shared_ptr from) - { - I2NPMessage * msg = NewI2NPMessage (); - if (msg->offset + len < msg->maxLen) - { - memcpy (msg->GetBuffer (), buf, len); - msg->len = msg->offset + len; - msg->from = from; - } - else - LogPrint (eLogError, "I2NP message length ", len, " exceeds max length"); - return ToSharedI2NPMessage(msg); - } - - std::shared_ptr CreateDeliveryStatusMsg (uint32_t msgID) - { - I2NPMessage * m = NewI2NPShortMessage (); - uint8_t * buf = m->GetPayload (); - if (msgID) - { - htobe32buf (buf + DELIVERY_STATUS_MSGID_OFFSET, msgID); - htobe64buf (buf + DELIVERY_STATUS_TIMESTAMP_OFFSET, i2p::util::GetMillisecondsSinceEpoch ()); - } - else // for SSU establishment - { - htobe32buf (buf + DELIVERY_STATUS_MSGID_OFFSET, i2p::context.GetRandomNumberGenerator ().GenerateWord32 ()); - htobe64buf (buf + DELIVERY_STATUS_TIMESTAMP_OFFSET, 2); // netID = 2 - } - m->len += DELIVERY_STATUS_SIZE; - m->FillI2NPMessageHeader (eI2NPDeliveryStatus); - return ToSharedI2NPMessage (m); - } + std::shared_ptr CreateI2NPMessage (const uint8_t * buf, int len, std::shared_ptr from) + { + I2NPMessage * msg = NewI2NPMessage (); + if (msg->offset + len < msg->maxLen) + { + memcpy (msg->GetBuffer (), buf, len); + msg->len = msg->offset + len; + msg->from = from; + } + else + LogPrint (eLogError, "I2NP message length ", len, " exceeds max length"); + return ToSharedI2NPMessage(msg); + } + + std::shared_ptr CreateDeliveryStatusMsg (uint32_t msgID) + { + I2NPMessage * m = NewI2NPShortMessage (); + uint8_t * buf = m->GetPayload (); + if (msgID) + { + htobe32buf (buf + DELIVERY_STATUS_MSGID_OFFSET, msgID); + htobe64buf (buf + DELIVERY_STATUS_TIMESTAMP_OFFSET, i2p::util::GetMillisecondsSinceEpoch ()); + } + else // for SSU establishment + { + htobe32buf (buf + DELIVERY_STATUS_MSGID_OFFSET, i2p::context.GetRandomNumberGenerator ().GenerateWord32 ()); + htobe64buf (buf + DELIVERY_STATUS_TIMESTAMP_OFFSET, 2); // netID = 2 + } + m->len += DELIVERY_STATUS_SIZE; + m->FillI2NPMessageHeader (eI2NPDeliveryStatus); + return ToSharedI2NPMessage (m); + } - std::shared_ptr CreateRouterInfoDatabaseLookupMsg (const uint8_t * key, const uint8_t * from, - uint32_t replyTunnelID, bool exploratory, std::set * excludedPeers) - { - auto m = ToSharedI2NPMessage (excludedPeers ? NewI2NPMessage () : NewI2NPShortMessage ()); - uint8_t * buf = m->GetPayload (); - memcpy (buf, key, 32); // key - buf += 32; - memcpy (buf, from, 32); // from - buf += 32; - uint8_t flag = exploratory ? DATABASE_LOOKUP_TYPE_EXPLORATORY_LOOKUP : DATABASE_LOOKUP_TYPE_ROUTERINFO_LOOKUP; - if (replyTunnelID) - { - *buf = flag | DATABASE_LOOKUP_DELIVERY_FLAG; // set delivery flag - htobe32buf (buf+1, replyTunnelID); - buf += 5; - } - else - { - *buf = flag; // flag - buf++; - } - - if (excludedPeers) - { - int cnt = excludedPeers->size (); - htobe16buf (buf, cnt); - buf += 2; - for (auto& it: *excludedPeers) - { - memcpy (buf, it, 32); - buf += 32; - } - } - else - { - // nothing to exclude - htobuf16 (buf, 0); - buf += 2; - } - - m->len += (buf - m->GetPayload ()); - m->FillI2NPMessageHeader (eI2NPDatabaseLookup); - return m; - } + std::shared_ptr CreateRouterInfoDatabaseLookupMsg (const uint8_t * key, const uint8_t * from, + uint32_t replyTunnelID, bool exploratory, std::set * excludedPeers) + { + auto m = ToSharedI2NPMessage (excludedPeers ? NewI2NPMessage () : NewI2NPShortMessage ()); + uint8_t * buf = m->GetPayload (); + memcpy (buf, key, 32); // key + buf += 32; + memcpy (buf, from, 32); // from + buf += 32; + uint8_t flag = exploratory ? DATABASE_LOOKUP_TYPE_EXPLORATORY_LOOKUP : DATABASE_LOOKUP_TYPE_ROUTERINFO_LOOKUP; + if (replyTunnelID) + { + *buf = flag | DATABASE_LOOKUP_DELIVERY_FLAG; // set delivery flag + htobe32buf (buf+1, replyTunnelID); + buf += 5; + } + else + { + *buf = flag; // flag + buf++; + } + + if (excludedPeers) + { + int cnt = excludedPeers->size (); + htobe16buf (buf, cnt); + buf += 2; + for (auto& it: *excludedPeers) + { + memcpy (buf, it, 32); + buf += 32; + } + } + else + { + // nothing to exclude + htobuf16 (buf, 0); + buf += 2; + } + + m->len += (buf - m->GetPayload ()); + m->FillI2NPMessageHeader (eI2NPDatabaseLookup); + return m; + } - std::shared_ptr CreateLeaseSetDatabaseLookupMsg (const i2p::data::IdentHash& dest, - const std::set& excludedFloodfills, - const i2p::tunnel::InboundTunnel * replyTunnel, const uint8_t * replyKey, const uint8_t * replyTag) - { - int cnt = excludedFloodfills.size (); - auto m = ToSharedI2NPMessage (cnt > 0 ? NewI2NPMessage () : NewI2NPShortMessage ()); - uint8_t * buf = m->GetPayload (); - memcpy (buf, dest, 32); // key - buf += 32; - memcpy (buf, replyTunnel->GetNextIdentHash (), 32); // reply tunnel GW - buf += 32; - *buf = DATABASE_LOOKUP_DELIVERY_FLAG | DATABASE_LOOKUP_ENCYPTION_FLAG | DATABASE_LOOKUP_TYPE_LEASESET_LOOKUP; // flags - htobe32buf (buf + 1, replyTunnel->GetNextTunnelID ()); // reply tunnel ID - buf += 5; - - // excluded - htobe16buf (buf, cnt); - buf += 2; - if (cnt > 0) - { - for (auto& it: excludedFloodfills) - { - memcpy (buf, it, 32); - buf += 32; - } - } - // encryption - memcpy (buf, replyKey, 32); - buf[32] = 1; // 1 tag - memcpy (buf + 33, replyTag, 32); - buf += 65; + std::shared_ptr CreateLeaseSetDatabaseLookupMsg (const i2p::data::IdentHash& dest, + const std::set& excludedFloodfills, + const i2p::tunnel::InboundTunnel * replyTunnel, const uint8_t * replyKey, const uint8_t * replyTag) + { + int cnt = excludedFloodfills.size (); + auto m = ToSharedI2NPMessage (cnt > 0 ? NewI2NPMessage () : NewI2NPShortMessage ()); + uint8_t * buf = m->GetPayload (); + memcpy (buf, dest, 32); // key + buf += 32; + memcpy (buf, replyTunnel->GetNextIdentHash (), 32); // reply tunnel GW + buf += 32; + *buf = DATABASE_LOOKUP_DELIVERY_FLAG | DATABASE_LOOKUP_ENCYPTION_FLAG | DATABASE_LOOKUP_TYPE_LEASESET_LOOKUP; // flags + htobe32buf (buf + 1, replyTunnel->GetNextTunnelID ()); // reply tunnel ID + buf += 5; + + // excluded + htobe16buf (buf, cnt); + buf += 2; + if (cnt > 0) + { + for (auto& it: excludedFloodfills) + { + memcpy (buf, it, 32); + buf += 32; + } + } + // encryption + memcpy (buf, replyKey, 32); + buf[32] = 1; // 1 tag + memcpy (buf + 33, replyTag, 32); + buf += 65; - m->len += (buf - m->GetPayload ()); - m->FillI2NPMessageHeader (eI2NPDatabaseLookup); - return m; - } + m->len += (buf - m->GetPayload ()); + m->FillI2NPMessageHeader (eI2NPDatabaseLookup); + return m; + } - std::shared_ptr CreateDatabaseSearchReply (const i2p::data::IdentHash& ident, - std::vector routers) - { - auto m = ToSharedI2NPMessage (NewI2NPShortMessage ()); - uint8_t * buf = m->GetPayload (); - size_t len = 0; - memcpy (buf, ident, 32); - len += 32; - buf[len] = routers.size (); - len++; - for (auto it: routers) - { - memcpy (buf + len, it, 32); - len += 32; - } - memcpy (buf + len, i2p::context.GetRouterInfo ().GetIdentHash (), 32); - len += 32; - m->len += len; - m->FillI2NPMessageHeader (eI2NPDatabaseSearchReply); - return m; - } - - std::shared_ptr CreateDatabaseStoreMsg (std::shared_ptr router, uint32_t replyToken) - { - if (!router) // we send own RouterInfo - router = context.GetSharedRouterInfo (); + std::shared_ptr CreateDatabaseSearchReply (const i2p::data::IdentHash& ident, + std::vector routers) + { + auto m = ToSharedI2NPMessage (NewI2NPShortMessage ()); + uint8_t * buf = m->GetPayload (); + size_t len = 0; + memcpy (buf, ident, 32); + len += 32; + buf[len] = routers.size (); + len++; + for (auto it: routers) + { + memcpy (buf + len, it, 32); + len += 32; + } + memcpy (buf + len, i2p::context.GetRouterInfo ().GetIdentHash (), 32); + len += 32; + m->len += len; + m->FillI2NPMessageHeader (eI2NPDatabaseSearchReply); + return m; + } + + std::shared_ptr CreateDatabaseStoreMsg (std::shared_ptr router, uint32_t replyToken) + { + if (!router) // we send own RouterInfo + router = context.GetSharedRouterInfo (); - auto m = ToSharedI2NPMessage (NewI2NPShortMessage ()); - uint8_t * payload = m->GetPayload (); + auto m = ToSharedI2NPMessage (NewI2NPShortMessage ()); + uint8_t * payload = m->GetPayload (); - memcpy (payload + DATABASE_STORE_KEY_OFFSET, router->GetIdentHash (), 32); - payload[DATABASE_STORE_TYPE_OFFSET] = 0; // RouterInfo - htobe32buf (payload + DATABASE_STORE_REPLY_TOKEN_OFFSET, replyToken); - uint8_t * buf = payload + DATABASE_STORE_HEADER_SIZE; - if (replyToken) - { - memset (buf, 0, 4); // zero tunnelID means direct reply - buf += 4; - memcpy (buf, router->GetIdentHash (), 32); - buf += 32; - } + memcpy (payload + DATABASE_STORE_KEY_OFFSET, router->GetIdentHash (), 32); + payload[DATABASE_STORE_TYPE_OFFSET] = 0; // RouterInfo + htobe32buf (payload + DATABASE_STORE_REPLY_TOKEN_OFFSET, replyToken); + uint8_t * buf = payload + DATABASE_STORE_HEADER_SIZE; + if (replyToken) + { + memset (buf, 0, 4); // zero tunnelID means direct reply + buf += 4; + memcpy (buf, router->GetIdentHash (), 32); + buf += 32; + } - CryptoPP::Gzip compressor; - compressor.Put (router->GetBuffer (), router->GetBufferLen ()); - compressor.MessageEnd(); - auto size = compressor.MaxRetrievable (); - htobe16buf (buf, size); // size - buf += 2; - m->len += (buf - payload); // payload size - if (m->len + size > m->maxLen) - { - LogPrint (eLogInfo, "DatabaseStore message size is not enough for ", m->len + size); - auto newMsg = ToSharedI2NPMessage (NewI2NPMessage ()); - *newMsg = *m; - m = newMsg; - buf = m->buf + m->len; - } - compressor.Get (buf, size); - m->len += size; - m->FillI2NPMessageHeader (eI2NPDatabaseStore); - - return m; - } + CryptoPP::Gzip compressor; + compressor.Put (router->GetBuffer (), router->GetBufferLen ()); + compressor.MessageEnd(); + auto size = compressor.MaxRetrievable (); + htobe16buf (buf, size); // size + buf += 2; + m->len += (buf - payload); // payload size + if (m->len + size > m->maxLen) + { + LogPrint (eLogInfo, "DatabaseStore message size is not enough for ", m->len + size); + auto newMsg = ToSharedI2NPMessage (NewI2NPMessage ()); + *newMsg = *m; + m = newMsg; + buf = m->buf + m->len; + } + compressor.Get (buf, size); + m->len += size; + m->FillI2NPMessageHeader (eI2NPDatabaseStore); + + return m; + } - std::shared_ptr CreateDatabaseStoreMsg (std::shared_ptr leaseSet, uint32_t replyToken) - { - if (!leaseSet) return nullptr; - auto m = ToSharedI2NPMessage (NewI2NPShortMessage ()); - uint8_t * payload = m->GetPayload (); - memcpy (payload + DATABASE_STORE_KEY_OFFSET, leaseSet->GetIdentHash (), 32); - payload[DATABASE_STORE_TYPE_OFFSET] = 1; // LeaseSet - htobe32buf (payload + DATABASE_STORE_REPLY_TOKEN_OFFSET, replyToken); - size_t size = DATABASE_STORE_HEADER_SIZE; - if (replyToken) - { - auto leases = leaseSet->GetNonExpiredLeases (); - if (leases.size () > 0) - { - htobe32buf (payload + size, leases[0].tunnelID); - size += 4; // reply tunnelID - memcpy (payload + size, leases[0].tunnelGateway, 32); - size += 32; // reply tunnel gateway - } - else - htobe32buf (payload + DATABASE_STORE_REPLY_TOKEN_OFFSET, 0); - } - memcpy (payload + size, leaseSet->GetBuffer (), leaseSet->GetBufferLen ()); - size += leaseSet->GetBufferLen (); - m->len += size; - m->FillI2NPMessageHeader (eI2NPDatabaseStore); - return m; - } - - bool HandleBuildRequestRecords (int num, uint8_t * records, uint8_t * clearText) - { - for (int i = 0; i < num; i++) - { - uint8_t * record = records + i*TUNNEL_BUILD_RECORD_SIZE; - if (!memcmp (record + BUILD_REQUEST_RECORD_TO_PEER_OFFSET, (const uint8_t *)i2p::context.GetRouterInfo ().GetIdentHash (), 16)) - { - LogPrint ("Record ",i," is ours"); - - i2p::crypto::ElGamalDecrypt (i2p::context.GetEncryptionPrivateKey (), record + BUILD_REQUEST_RECORD_ENCRYPTED_OFFSET, clearText); - // replace record to reply - if (i2p::context.AcceptsTunnels () && - i2p::tunnel::tunnels.GetTransitTunnels ().size () <= MAX_NUM_TRANSIT_TUNNELS && - !i2p::transport::transports.IsBandwidthExceeded ()) - { - i2p::tunnel::TransitTunnel * transitTunnel = - i2p::tunnel::CreateTransitTunnel ( - bufbe32toh (clearText + BUILD_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET), - clearText + BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET, - bufbe32toh (clearText + BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET), - clearText + BUILD_REQUEST_RECORD_LAYER_KEY_OFFSET, - clearText + BUILD_REQUEST_RECORD_IV_KEY_OFFSET, - clearText[BUILD_REQUEST_RECORD_FLAG_OFFSET] & 0x80, - clearText[BUILD_REQUEST_RECORD_FLAG_OFFSET ] & 0x40); - i2p::tunnel::tunnels.AddTransitTunnel (transitTunnel); - record[BUILD_RESPONSE_RECORD_RET_OFFSET] = 0; - } - else - record[BUILD_RESPONSE_RECORD_RET_OFFSET] = 30; // always reject with bandwidth reason (30) - - //TODO: fill filler - CryptoPP::SHA256().CalculateDigest(record + BUILD_RESPONSE_RECORD_HASH_OFFSET, - record + BUILD_RESPONSE_RECORD_PADDING_OFFSET, BUILD_RESPONSE_RECORD_PADDING_SIZE + 1); // + 1 byte of ret - // encrypt reply - i2p::crypto::CBCEncryption encryption; - for (int j = 0; j < num; j++) - { - encryption.SetKey (clearText + BUILD_REQUEST_RECORD_REPLY_KEY_OFFSET); - encryption.SetIV (clearText + BUILD_REQUEST_RECORD_REPLY_IV_OFFSET); - uint8_t * reply = records + j*TUNNEL_BUILD_RECORD_SIZE; - encryption.Encrypt(reply, TUNNEL_BUILD_RECORD_SIZE, reply); - } - return true; - } - } - return false; - } + std::shared_ptr CreateDatabaseStoreMsg (std::shared_ptr leaseSet, uint32_t replyToken) + { + if (!leaseSet) return nullptr; + auto m = ToSharedI2NPMessage (NewI2NPShortMessage ()); + uint8_t * payload = m->GetPayload (); + memcpy (payload + DATABASE_STORE_KEY_OFFSET, leaseSet->GetIdentHash (), 32); + payload[DATABASE_STORE_TYPE_OFFSET] = 1; // LeaseSet + htobe32buf (payload + DATABASE_STORE_REPLY_TOKEN_OFFSET, replyToken); + size_t size = DATABASE_STORE_HEADER_SIZE; + if (replyToken) + { + auto leases = leaseSet->GetNonExpiredLeases (); + if (leases.size () > 0) + { + htobe32buf (payload + size, leases[0].tunnelID); + size += 4; // reply tunnelID + memcpy (payload + size, leases[0].tunnelGateway, 32); + size += 32; // reply tunnel gateway + } + else + htobe32buf (payload + DATABASE_STORE_REPLY_TOKEN_OFFSET, 0); + } + memcpy (payload + size, leaseSet->GetBuffer (), leaseSet->GetBufferLen ()); + size += leaseSet->GetBufferLen (); + m->len += size; + m->FillI2NPMessageHeader (eI2NPDatabaseStore); + return m; + } + + bool HandleBuildRequestRecords (int num, uint8_t * records, uint8_t * clearText) + { + for (int i = 0; i < num; i++) + { + uint8_t * record = records + i*TUNNEL_BUILD_RECORD_SIZE; + if (!memcmp (record + BUILD_REQUEST_RECORD_TO_PEER_OFFSET, (const uint8_t *)i2p::context.GetRouterInfo ().GetIdentHash (), 16)) + { + LogPrint ("Record ",i," is ours"); + + i2p::crypto::ElGamalDecrypt (i2p::context.GetEncryptionPrivateKey (), record + BUILD_REQUEST_RECORD_ENCRYPTED_OFFSET, clearText); + // replace record to reply + if (i2p::context.AcceptsTunnels () && + i2p::tunnel::tunnels.GetTransitTunnels ().size () <= MAX_NUM_TRANSIT_TUNNELS && + !i2p::transport::transports.IsBandwidthExceeded ()) + { + i2p::tunnel::TransitTunnel * transitTunnel = + i2p::tunnel::CreateTransitTunnel ( + bufbe32toh (clearText + BUILD_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET), + clearText + BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET, + bufbe32toh (clearText + BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET), + clearText + BUILD_REQUEST_RECORD_LAYER_KEY_OFFSET, + clearText + BUILD_REQUEST_RECORD_IV_KEY_OFFSET, + clearText[BUILD_REQUEST_RECORD_FLAG_OFFSET] & 0x80, + clearText[BUILD_REQUEST_RECORD_FLAG_OFFSET ] & 0x40); + i2p::tunnel::tunnels.AddTransitTunnel (transitTunnel); + record[BUILD_RESPONSE_RECORD_RET_OFFSET] = 0; + } + else + record[BUILD_RESPONSE_RECORD_RET_OFFSET] = 30; // always reject with bandwidth reason (30) + + //TODO: fill filler + CryptoPP::SHA256().CalculateDigest(record + BUILD_RESPONSE_RECORD_HASH_OFFSET, + record + BUILD_RESPONSE_RECORD_PADDING_OFFSET, BUILD_RESPONSE_RECORD_PADDING_SIZE + 1); // + 1 byte of ret + // encrypt reply + i2p::crypto::CBCEncryption encryption; + for (int j = 0; j < num; j++) + { + encryption.SetKey (clearText + BUILD_REQUEST_RECORD_REPLY_KEY_OFFSET); + encryption.SetIV (clearText + BUILD_REQUEST_RECORD_REPLY_IV_OFFSET); + uint8_t * reply = records + j*TUNNEL_BUILD_RECORD_SIZE; + encryption.Encrypt(reply, TUNNEL_BUILD_RECORD_SIZE, reply); + } + return true; + } + } + return false; + } - void HandleVariableTunnelBuildMsg (uint32_t replyMsgID, uint8_t * buf, size_t len) - { - int num = buf[0]; - LogPrint ("VariableTunnelBuild ", num, " records"); + void HandleVariableTunnelBuildMsg (uint32_t replyMsgID, uint8_t * buf, size_t len) + { + int num = buf[0]; + LogPrint ("VariableTunnelBuild ", num, " records"); - auto tunnel = i2p::tunnel::tunnels.GetPendingInboundTunnel (replyMsgID); - if (tunnel) - { - // endpoint of inbound tunnel - LogPrint ("VariableTunnelBuild reply for tunnel ", tunnel->GetTunnelID ()); - if (tunnel->HandleTunnelBuildResponse (buf, len)) - { - LogPrint ("Inbound tunnel ", tunnel->GetTunnelID (), " has been created"); - tunnel->SetState (i2p::tunnel::eTunnelStateEstablished); - i2p::tunnel::tunnels.AddInboundTunnel (tunnel); - } - else - { - LogPrint ("Inbound tunnel ", tunnel->GetTunnelID (), " has been declined"); - tunnel->SetState (i2p::tunnel::eTunnelStateBuildFailed); - } - } - else - { - uint8_t clearText[BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE] = {}; - if (HandleBuildRequestRecords (num, buf + 1, clearText)) - { - if (clearText[BUILD_REQUEST_RECORD_FLAG_OFFSET] & 0x40) // we are endpoint of outboud tunnel - { - // so we send it to reply tunnel - transports.SendMessage (clearText + BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET, - ToSharedI2NPMessage (CreateTunnelGatewayMsg (bufbe32toh (clearText + BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET), - eI2NPVariableTunnelBuildReply, buf, len, - bufbe32toh (clearText + BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET)))); - } - else - transports.SendMessage (clearText + BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET, - ToSharedI2NPMessage (CreateI2NPMessage (eI2NPVariableTunnelBuild, buf, len, - bufbe32toh (clearText + BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET)))); - } - } - } + auto tunnel = i2p::tunnel::tunnels.GetPendingInboundTunnel (replyMsgID); + if (tunnel) + { + // endpoint of inbound tunnel + LogPrint ("VariableTunnelBuild reply for tunnel ", tunnel->GetTunnelID ()); + if (tunnel->HandleTunnelBuildResponse (buf, len)) + { + LogPrint ("Inbound tunnel ", tunnel->GetTunnelID (), " has been created"); + tunnel->SetState (i2p::tunnel::eTunnelStateEstablished); + i2p::tunnel::tunnels.AddInboundTunnel (tunnel); + } + else + { + LogPrint ("Inbound tunnel ", tunnel->GetTunnelID (), " has been declined"); + tunnel->SetState (i2p::tunnel::eTunnelStateBuildFailed); + } + } + else + { + uint8_t clearText[BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE] = {}; + if (HandleBuildRequestRecords (num, buf + 1, clearText)) + { + if (clearText[BUILD_REQUEST_RECORD_FLAG_OFFSET] & 0x40) // we are endpoint of outboud tunnel + { + // so we send it to reply tunnel + transports.SendMessage (clearText + BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET, + ToSharedI2NPMessage (CreateTunnelGatewayMsg (bufbe32toh (clearText + BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET), + eI2NPVariableTunnelBuildReply, buf, len, + bufbe32toh (clearText + BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET)))); + } + else + transports.SendMessage (clearText + BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET, + ToSharedI2NPMessage (CreateI2NPMessage (eI2NPVariableTunnelBuild, buf, len, + bufbe32toh (clearText + BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET)))); + } + } + } - void HandleTunnelBuildMsg (uint8_t * buf, size_t len) - { - uint8_t clearText[BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE]; - if (HandleBuildRequestRecords (NUM_TUNNEL_BUILD_RECORDS, buf, clearText)) - { - if (clearText[BUILD_REQUEST_RECORD_FLAG_OFFSET] & 0x40) // we are endpoint of outbound tunnel - { - // so we send it to reply tunnel - transports.SendMessage (clearText + BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET, - ToSharedI2NPMessage (CreateTunnelGatewayMsg (bufbe32toh (clearText + BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET), - eI2NPTunnelBuildReply, buf, len, - bufbe32toh (clearText + BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET)))); - } - else - transports.SendMessage (clearText + BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET, - ToSharedI2NPMessage (CreateI2NPMessage (eI2NPTunnelBuild, buf, len, - bufbe32toh (clearText + BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET)))); - } - } + void HandleTunnelBuildMsg (uint8_t * buf, size_t len) + { + uint8_t clearText[BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE]; + if (HandleBuildRequestRecords (NUM_TUNNEL_BUILD_RECORDS, buf, clearText)) + { + if (clearText[BUILD_REQUEST_RECORD_FLAG_OFFSET] & 0x40) // we are endpoint of outbound tunnel + { + // so we send it to reply tunnel + transports.SendMessage (clearText + BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET, + ToSharedI2NPMessage (CreateTunnelGatewayMsg (bufbe32toh (clearText + BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET), + eI2NPTunnelBuildReply, buf, len, + bufbe32toh (clearText + BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET)))); + } + else + transports.SendMessage (clearText + BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET, + ToSharedI2NPMessage (CreateI2NPMessage (eI2NPTunnelBuild, buf, len, + bufbe32toh (clearText + BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET)))); + } + } - void HandleVariableTunnelBuildReplyMsg (uint32_t replyMsgID, uint8_t * buf, size_t len) - { - LogPrint ("VariableTunnelBuildReplyMsg replyMsgID=", replyMsgID); - auto tunnel = i2p::tunnel::tunnels.GetPendingOutboundTunnel (replyMsgID); - if (tunnel) - { - // reply for outbound tunnel - if (tunnel->HandleTunnelBuildResponse (buf, len)) - { - LogPrint ("Outbound tunnel ", tunnel->GetTunnelID (), " has been created"); - tunnel->SetState (i2p::tunnel::eTunnelStateEstablished); - i2p::tunnel::tunnels.AddOutboundTunnel (tunnel); - } - else - { - LogPrint ("Outbound tunnel ", tunnel->GetTunnelID (), " has been declined"); - tunnel->SetState (i2p::tunnel::eTunnelStateBuildFailed); - } - } - else - LogPrint ("Pending tunnel for message ", replyMsgID, " not found"); - } + void HandleVariableTunnelBuildReplyMsg (uint32_t replyMsgID, uint8_t * buf, size_t len) + { + LogPrint ("VariableTunnelBuildReplyMsg replyMsgID=", replyMsgID); + auto tunnel = i2p::tunnel::tunnels.GetPendingOutboundTunnel (replyMsgID); + if (tunnel) + { + // reply for outbound tunnel + if (tunnel->HandleTunnelBuildResponse (buf, len)) + { + LogPrint ("Outbound tunnel ", tunnel->GetTunnelID (), " has been created"); + tunnel->SetState (i2p::tunnel::eTunnelStateEstablished); + i2p::tunnel::tunnels.AddOutboundTunnel (tunnel); + } + else + { + LogPrint ("Outbound tunnel ", tunnel->GetTunnelID (), " has been declined"); + tunnel->SetState (i2p::tunnel::eTunnelStateBuildFailed); + } + } + else + LogPrint ("Pending tunnel for message ", replyMsgID, " not found"); + } - I2NPMessage * CreateTunnelDataMsg (const uint8_t * buf) - { - I2NPMessage * msg = NewI2NPShortMessage (); - memcpy (msg->GetPayload (), buf, i2p::tunnel::TUNNEL_DATA_MSG_SIZE); - msg->len += i2p::tunnel::TUNNEL_DATA_MSG_SIZE; - msg->FillI2NPMessageHeader (eI2NPTunnelData); - return msg; - } + I2NPMessage * CreateTunnelDataMsg (const uint8_t * buf) + { + I2NPMessage * msg = NewI2NPShortMessage (); + memcpy (msg->GetPayload (), buf, i2p::tunnel::TUNNEL_DATA_MSG_SIZE); + msg->len += i2p::tunnel::TUNNEL_DATA_MSG_SIZE; + msg->FillI2NPMessageHeader (eI2NPTunnelData); + return msg; + } - I2NPMessage * CreateTunnelDataMsg (uint32_t tunnelID, const uint8_t * payload) - { - I2NPMessage * msg = NewI2NPShortMessage (); - memcpy (msg->GetPayload () + 4, payload, i2p::tunnel::TUNNEL_DATA_MSG_SIZE - 4); - htobe32buf (msg->GetPayload (), tunnelID); - msg->len += i2p::tunnel::TUNNEL_DATA_MSG_SIZE; - msg->FillI2NPMessageHeader (eI2NPTunnelData); - return msg; - } + I2NPMessage * CreateTunnelDataMsg (uint32_t tunnelID, const uint8_t * payload) + { + I2NPMessage * msg = NewI2NPShortMessage (); + memcpy (msg->GetPayload () + 4, payload, i2p::tunnel::TUNNEL_DATA_MSG_SIZE - 4); + htobe32buf (msg->GetPayload (), tunnelID); + msg->len += i2p::tunnel::TUNNEL_DATA_MSG_SIZE; + msg->FillI2NPMessageHeader (eI2NPTunnelData); + return msg; + } - std::shared_ptr CreateEmptyTunnelDataMsg () - { - I2NPMessage * msg = NewI2NPShortMessage (); - msg->len += i2p::tunnel::TUNNEL_DATA_MSG_SIZE; - return ToSharedI2NPMessage (msg); - } - - I2NPMessage * CreateTunnelGatewayMsg (uint32_t tunnelID, const uint8_t * buf, size_t len) - { - I2NPMessage * msg = NewI2NPMessage (len); - uint8_t * payload = msg->GetPayload (); - htobe32buf (payload + TUNNEL_GATEWAY_HEADER_TUNNELID_OFFSET, tunnelID); - htobe16buf (payload + TUNNEL_GATEWAY_HEADER_LENGTH_OFFSET, len); - memcpy (payload + TUNNEL_GATEWAY_HEADER_SIZE, buf, len); - msg->len += TUNNEL_GATEWAY_HEADER_SIZE + len; - msg->FillI2NPMessageHeader (eI2NPTunnelGateway); - return msg; - } + std::shared_ptr CreateEmptyTunnelDataMsg () + { + I2NPMessage * msg = NewI2NPShortMessage (); + msg->len += i2p::tunnel::TUNNEL_DATA_MSG_SIZE; + return ToSharedI2NPMessage (msg); + } + + I2NPMessage * CreateTunnelGatewayMsg (uint32_t tunnelID, const uint8_t * buf, size_t len) + { + I2NPMessage * msg = NewI2NPMessage (len); + uint8_t * payload = msg->GetPayload (); + htobe32buf (payload + TUNNEL_GATEWAY_HEADER_TUNNELID_OFFSET, tunnelID); + htobe16buf (payload + TUNNEL_GATEWAY_HEADER_LENGTH_OFFSET, len); + memcpy (payload + TUNNEL_GATEWAY_HEADER_SIZE, buf, len); + msg->len += TUNNEL_GATEWAY_HEADER_SIZE + len; + msg->FillI2NPMessageHeader (eI2NPTunnelGateway); + return msg; + } - std::shared_ptr CreateTunnelGatewayMsg (uint32_t tunnelID, std::shared_ptr msg) - { - if (msg->offset >= I2NP_HEADER_SIZE + TUNNEL_GATEWAY_HEADER_SIZE) - { - // message is capable to be used without copying - uint8_t * payload = msg->GetBuffer () - TUNNEL_GATEWAY_HEADER_SIZE; - htobe32buf (payload + TUNNEL_GATEWAY_HEADER_TUNNELID_OFFSET, tunnelID); - int len = msg->GetLength (); - htobe16buf (payload + TUNNEL_GATEWAY_HEADER_LENGTH_OFFSET, len); - msg->offset -= (I2NP_HEADER_SIZE + TUNNEL_GATEWAY_HEADER_SIZE); - msg->len = msg->offset + I2NP_HEADER_SIZE + TUNNEL_GATEWAY_HEADER_SIZE +len; - msg->FillI2NPMessageHeader (eI2NPTunnelGateway); - return msg; - } - else - { - I2NPMessage * msg1 = CreateTunnelGatewayMsg (tunnelID, msg->GetBuffer (), msg->GetLength ()); - return ToSharedI2NPMessage (msg1); - } - } + std::shared_ptr CreateTunnelGatewayMsg (uint32_t tunnelID, std::shared_ptr msg) + { + if (msg->offset >= I2NP_HEADER_SIZE + TUNNEL_GATEWAY_HEADER_SIZE) + { + // message is capable to be used without copying + uint8_t * payload = msg->GetBuffer () - TUNNEL_GATEWAY_HEADER_SIZE; + htobe32buf (payload + TUNNEL_GATEWAY_HEADER_TUNNELID_OFFSET, tunnelID); + int len = msg->GetLength (); + htobe16buf (payload + TUNNEL_GATEWAY_HEADER_LENGTH_OFFSET, len); + msg->offset -= (I2NP_HEADER_SIZE + TUNNEL_GATEWAY_HEADER_SIZE); + msg->len = msg->offset + I2NP_HEADER_SIZE + TUNNEL_GATEWAY_HEADER_SIZE +len; + msg->FillI2NPMessageHeader (eI2NPTunnelGateway); + return msg; + } + else + { + I2NPMessage * msg1 = CreateTunnelGatewayMsg (tunnelID, msg->GetBuffer (), msg->GetLength ()); + return ToSharedI2NPMessage (msg1); + } + } - I2NPMessage * CreateTunnelGatewayMsg (uint32_t tunnelID, I2NPMessageType msgType, - const uint8_t * buf, size_t len, uint32_t replyMsgID) - { - I2NPMessage * msg = NewI2NPMessage (len); - size_t gatewayMsgOffset = I2NP_HEADER_SIZE + TUNNEL_GATEWAY_HEADER_SIZE; - msg->offset += gatewayMsgOffset; - msg->len += gatewayMsgOffset; - memcpy (msg->GetPayload (), buf, len); - msg->len += len; - msg->FillI2NPMessageHeader (msgType, replyMsgID); // create content message - len = msg->GetLength (); - msg->offset -= gatewayMsgOffset; - uint8_t * payload = msg->GetPayload (); - htobe32buf (payload + TUNNEL_GATEWAY_HEADER_TUNNELID_OFFSET, tunnelID); - htobe16buf (payload + TUNNEL_GATEWAY_HEADER_LENGTH_OFFSET, len); - msg->FillI2NPMessageHeader (eI2NPTunnelGateway); // gateway message - return msg; - } + I2NPMessage * CreateTunnelGatewayMsg (uint32_t tunnelID, I2NPMessageType msgType, + const uint8_t * buf, size_t len, uint32_t replyMsgID) + { + I2NPMessage * msg = NewI2NPMessage (len); + size_t gatewayMsgOffset = I2NP_HEADER_SIZE + TUNNEL_GATEWAY_HEADER_SIZE; + msg->offset += gatewayMsgOffset; + msg->len += gatewayMsgOffset; + memcpy (msg->GetPayload (), buf, len); + msg->len += len; + msg->FillI2NPMessageHeader (msgType, replyMsgID); // create content message + len = msg->GetLength (); + msg->offset -= gatewayMsgOffset; + uint8_t * payload = msg->GetPayload (); + htobe32buf (payload + TUNNEL_GATEWAY_HEADER_TUNNELID_OFFSET, tunnelID); + htobe16buf (payload + TUNNEL_GATEWAY_HEADER_LENGTH_OFFSET, len); + msg->FillI2NPMessageHeader (eI2NPTunnelGateway); // gateway message + return msg; + } - size_t GetI2NPMessageLength (const uint8_t * msg) - { - return bufbe16toh (msg + I2NP_HEADER_SIZE_OFFSET) + I2NP_HEADER_SIZE; - } - - void HandleI2NPMessage (uint8_t * msg, size_t len) - { - uint8_t typeID = msg[I2NP_HEADER_TYPEID_OFFSET]; - uint32_t msgID = bufbe32toh (msg + I2NP_HEADER_MSGID_OFFSET); - LogPrint ("I2NP msg received len=", len,", type=", (int)typeID, ", msgID=", (unsigned int)msgID); + size_t GetI2NPMessageLength (const uint8_t * msg) + { + return bufbe16toh (msg + I2NP_HEADER_SIZE_OFFSET) + I2NP_HEADER_SIZE; + } + + void HandleI2NPMessage (uint8_t * msg, size_t len) + { + uint8_t typeID = msg[I2NP_HEADER_TYPEID_OFFSET]; + uint32_t msgID = bufbe32toh (msg + I2NP_HEADER_MSGID_OFFSET); + LogPrint ("I2NP msg received len=", len,", type=", (int)typeID, ", msgID=", (unsigned int)msgID); - uint8_t * buf = msg + I2NP_HEADER_SIZE; - int size = bufbe16toh (msg + I2NP_HEADER_SIZE_OFFSET); - switch (typeID) - { - case eI2NPVariableTunnelBuild: - LogPrint ("VariableTunnelBuild"); - HandleVariableTunnelBuildMsg (msgID, buf, size); - break; - case eI2NPVariableTunnelBuildReply: - LogPrint ("VariableTunnelBuildReply"); - HandleVariableTunnelBuildReplyMsg (msgID, buf, size); - break; - case eI2NPTunnelBuild: - LogPrint ("TunnelBuild"); - HandleTunnelBuildMsg (buf, size); - break; - case eI2NPTunnelBuildReply: - LogPrint ("TunnelBuildReply"); - // TODO: - break; - default: - LogPrint ("Unexpected message ", (int)typeID); - } - } + uint8_t * buf = msg + I2NP_HEADER_SIZE; + int size = bufbe16toh (msg + I2NP_HEADER_SIZE_OFFSET); + switch (typeID) + { + case eI2NPVariableTunnelBuild: + LogPrint ("VariableTunnelBuild"); + HandleVariableTunnelBuildMsg (msgID, buf, size); + break; + case eI2NPVariableTunnelBuildReply: + LogPrint ("VariableTunnelBuildReply"); + HandleVariableTunnelBuildReplyMsg (msgID, buf, size); + break; + case eI2NPTunnelBuild: + LogPrint ("TunnelBuild"); + HandleTunnelBuildMsg (buf, size); + break; + case eI2NPTunnelBuildReply: + LogPrint ("TunnelBuildReply"); + // TODO: + break; + default: + LogPrint ("Unexpected message ", (int)typeID); + } + } - void HandleI2NPMessage (std::shared_ptr msg) - { - if (msg) - { - switch (msg->GetTypeID ()) - { - case eI2NPTunnelData: - LogPrint ("TunnelData"); - i2p::tunnel::tunnels.PostTunnelData (msg); - break; - case eI2NPTunnelGateway: - LogPrint ("TunnelGateway"); - i2p::tunnel::tunnels.PostTunnelData (msg); - break; - case eI2NPGarlic: - { - LogPrint ("Garlic"); - if (msg->from) - { - if (msg->from->GetTunnelPool ()) - msg->from->GetTunnelPool ()->ProcessGarlicMessage (msg); - else - LogPrint (eLogInfo, "Local destination for garlic doesn't exist anymore"); - } - else - i2p::context.ProcessGarlicMessage (msg); - break; - } - case eI2NPDatabaseStore: - case eI2NPDatabaseSearchReply: - case eI2NPDatabaseLookup: - // forward to netDb - i2p::data::netdb.PostI2NPMsg (msg); - break; - case eI2NPDeliveryStatus: - { - LogPrint ("DeliveryStatus"); - if (msg->from && msg->from->GetTunnelPool ()) - msg->from->GetTunnelPool ()->ProcessDeliveryStatus (msg); - else - i2p::context.ProcessDeliveryStatusMessage (msg); - break; - } - case eI2NPVariableTunnelBuild: - case eI2NPVariableTunnelBuildReply: - case eI2NPTunnelBuild: - case eI2NPTunnelBuildReply: - // forward to tunnel thread - i2p::tunnel::tunnels.PostTunnelData (msg); - break; - default: - HandleI2NPMessage (msg->GetBuffer (), msg->GetLength ()); - } - } - } + void HandleI2NPMessage (std::shared_ptr msg) + { + if (msg) + { + switch (msg->GetTypeID ()) + { + case eI2NPTunnelData: + LogPrint ("TunnelData"); + i2p::tunnel::tunnels.PostTunnelData (msg); + break; + case eI2NPTunnelGateway: + LogPrint ("TunnelGateway"); + i2p::tunnel::tunnels.PostTunnelData (msg); + break; + case eI2NPGarlic: + { + LogPrint ("Garlic"); + if (msg->from) + { + if (msg->from->GetTunnelPool ()) + msg->from->GetTunnelPool ()->ProcessGarlicMessage (msg); + else + LogPrint (eLogInfo, "Local destination for garlic doesn't exist anymore"); + } + else + i2p::context.ProcessGarlicMessage (msg); + break; + } + case eI2NPDatabaseStore: + case eI2NPDatabaseSearchReply: + case eI2NPDatabaseLookup: + // forward to netDb + i2p::data::netdb.PostI2NPMsg (msg); + break; + case eI2NPDeliveryStatus: + { + LogPrint ("DeliveryStatus"); + if (msg->from && msg->from->GetTunnelPool ()) + msg->from->GetTunnelPool ()->ProcessDeliveryStatus (msg); + else + i2p::context.ProcessDeliveryStatusMessage (msg); + break; + } + case eI2NPVariableTunnelBuild: + case eI2NPVariableTunnelBuildReply: + case eI2NPTunnelBuild: + case eI2NPTunnelBuildReply: + // forward to tunnel thread + i2p::tunnel::tunnels.PostTunnelData (msg); + break; + default: + HandleI2NPMessage (msg->GetBuffer (), msg->GetLength ()); + } + } + } - I2NPMessagesHandler::~I2NPMessagesHandler () - { - Flush (); - } - - void I2NPMessagesHandler::PutNextMessage (std::shared_ptr msg) - { - if (msg) - { - switch (msg->GetTypeID ()) - { - case eI2NPTunnelData: - m_TunnelMsgs.push_back (msg); - break; - case eI2NPTunnelGateway: - m_TunnelGatewayMsgs.push_back (msg); - break; - default: - HandleI2NPMessage (msg); - } - } - } - - void I2NPMessagesHandler::Flush () - { - if (!m_TunnelMsgs.empty ()) - { - i2p::tunnel::tunnels.PostTunnelData (m_TunnelMsgs); - m_TunnelMsgs.clear (); - } - if (!m_TunnelGatewayMsgs.empty ()) - { - i2p::tunnel::tunnels.PostTunnelData (m_TunnelGatewayMsgs); - m_TunnelGatewayMsgs.clear (); - } - } + I2NPMessagesHandler::~I2NPMessagesHandler () + { + Flush (); + } + + void I2NPMessagesHandler::PutNextMessage (std::shared_ptr msg) + { + if (msg) + { + switch (msg->GetTypeID ()) + { + case eI2NPTunnelData: + m_TunnelMsgs.push_back (msg); + break; + case eI2NPTunnelGateway: + m_TunnelGatewayMsgs.push_back (msg); + break; + default: + HandleI2NPMessage (msg); + } + } + } + + void I2NPMessagesHandler::Flush () + { + if (!m_TunnelMsgs.empty ()) + { + i2p::tunnel::tunnels.PostTunnelData (m_TunnelMsgs); + m_TunnelMsgs.clear (); + } + if (!m_TunnelGatewayMsgs.empty ()) + { + i2p::tunnel::tunnels.PostTunnelData (m_TunnelGatewayMsgs); + m_TunnelGatewayMsgs.clear (); + } + } } diff --git a/I2NPProtocol.h b/I2NPProtocol.h index 943a8d2f..28a0e111 100644 --- a/I2NPProtocol.h +++ b/I2NPProtocol.h @@ -12,241 +12,241 @@ #include "LeaseSet.h" namespace i2p -{ - // I2NP header - const size_t I2NP_HEADER_TYPEID_OFFSET = 0; - const size_t I2NP_HEADER_MSGID_OFFSET = I2NP_HEADER_TYPEID_OFFSET + 1; - const size_t I2NP_HEADER_EXPIRATION_OFFSET = I2NP_HEADER_MSGID_OFFSET + 4; - const size_t I2NP_HEADER_SIZE_OFFSET = I2NP_HEADER_EXPIRATION_OFFSET + 8; - const size_t I2NP_HEADER_CHKS_OFFSET = I2NP_HEADER_SIZE_OFFSET + 2; - const size_t I2NP_HEADER_SIZE = I2NP_HEADER_CHKS_OFFSET + 1; +{ + // I2NP header + const size_t I2NP_HEADER_TYPEID_OFFSET = 0; + const size_t I2NP_HEADER_MSGID_OFFSET = I2NP_HEADER_TYPEID_OFFSET + 1; + const size_t I2NP_HEADER_EXPIRATION_OFFSET = I2NP_HEADER_MSGID_OFFSET + 4; + const size_t I2NP_HEADER_SIZE_OFFSET = I2NP_HEADER_EXPIRATION_OFFSET + 8; + const size_t I2NP_HEADER_CHKS_OFFSET = I2NP_HEADER_SIZE_OFFSET + 2; + const size_t I2NP_HEADER_SIZE = I2NP_HEADER_CHKS_OFFSET + 1; - // I2NP short header - const size_t I2NP_SHORT_HEADER_TYPEID_OFFSET = 0; - const size_t I2NP_SHORT_HEADER_EXPIRATION_OFFSET = I2NP_SHORT_HEADER_TYPEID_OFFSET + 1; - const size_t I2NP_SHORT_HEADER_SIZE = I2NP_SHORT_HEADER_EXPIRATION_OFFSET + 4; - - // Tunnel Gateway header - const size_t TUNNEL_GATEWAY_HEADER_TUNNELID_OFFSET = 0; - const size_t TUNNEL_GATEWAY_HEADER_LENGTH_OFFSET = TUNNEL_GATEWAY_HEADER_TUNNELID_OFFSET + 4; - const size_t TUNNEL_GATEWAY_HEADER_SIZE = TUNNEL_GATEWAY_HEADER_LENGTH_OFFSET + 2; + // I2NP short header + const size_t I2NP_SHORT_HEADER_TYPEID_OFFSET = 0; + const size_t I2NP_SHORT_HEADER_EXPIRATION_OFFSET = I2NP_SHORT_HEADER_TYPEID_OFFSET + 1; + const size_t I2NP_SHORT_HEADER_SIZE = I2NP_SHORT_HEADER_EXPIRATION_OFFSET + 4; + + // Tunnel Gateway header + const size_t TUNNEL_GATEWAY_HEADER_TUNNELID_OFFSET = 0; + const size_t TUNNEL_GATEWAY_HEADER_LENGTH_OFFSET = TUNNEL_GATEWAY_HEADER_TUNNELID_OFFSET + 4; + const size_t TUNNEL_GATEWAY_HEADER_SIZE = TUNNEL_GATEWAY_HEADER_LENGTH_OFFSET + 2; - // DeliveryStatus - const size_t DELIVERY_STATUS_MSGID_OFFSET = 0; - const size_t DELIVERY_STATUS_TIMESTAMP_OFFSET = DELIVERY_STATUS_MSGID_OFFSET + 4; - const size_t DELIVERY_STATUS_SIZE = DELIVERY_STATUS_TIMESTAMP_OFFSET + 8; + // DeliveryStatus + const size_t DELIVERY_STATUS_MSGID_OFFSET = 0; + const size_t DELIVERY_STATUS_TIMESTAMP_OFFSET = DELIVERY_STATUS_MSGID_OFFSET + 4; + const size_t DELIVERY_STATUS_SIZE = DELIVERY_STATUS_TIMESTAMP_OFFSET + 8; - // DatabaseStore - const size_t DATABASE_STORE_KEY_OFFSET = 0; - const size_t DATABASE_STORE_TYPE_OFFSET = DATABASE_STORE_KEY_OFFSET + 32; - const size_t DATABASE_STORE_REPLY_TOKEN_OFFSET = DATABASE_STORE_TYPE_OFFSET + 1; - const size_t DATABASE_STORE_HEADER_SIZE = DATABASE_STORE_REPLY_TOKEN_OFFSET + 4; + // DatabaseStore + const size_t DATABASE_STORE_KEY_OFFSET = 0; + const size_t DATABASE_STORE_TYPE_OFFSET = DATABASE_STORE_KEY_OFFSET + 32; + const size_t DATABASE_STORE_REPLY_TOKEN_OFFSET = DATABASE_STORE_TYPE_OFFSET + 1; + const size_t DATABASE_STORE_HEADER_SIZE = DATABASE_STORE_REPLY_TOKEN_OFFSET + 4; - // TunnelBuild - const size_t TUNNEL_BUILD_RECORD_SIZE = 528; + // TunnelBuild + const size_t TUNNEL_BUILD_RECORD_SIZE = 528; - //BuildRequestRecordClearText - const size_t BUILD_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET = 0; - const size_t BUILD_REQUEST_RECORD_OUR_IDENT_OFFSET = BUILD_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET + 4; - const size_t BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET = BUILD_REQUEST_RECORD_OUR_IDENT_OFFSET + 32; - const size_t BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET = BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET + 4; - const size_t BUILD_REQUEST_RECORD_LAYER_KEY_OFFSET = BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET + 32; - const size_t BUILD_REQUEST_RECORD_IV_KEY_OFFSET = BUILD_REQUEST_RECORD_LAYER_KEY_OFFSET + 32; - const size_t BUILD_REQUEST_RECORD_REPLY_KEY_OFFSET = BUILD_REQUEST_RECORD_IV_KEY_OFFSET + 32; - const size_t BUILD_REQUEST_RECORD_REPLY_IV_OFFSET = BUILD_REQUEST_RECORD_REPLY_KEY_OFFSET + 32; - const size_t BUILD_REQUEST_RECORD_FLAG_OFFSET = BUILD_REQUEST_RECORD_REPLY_IV_OFFSET + 16; - const size_t BUILD_REQUEST_RECORD_REQUEST_TIME_OFFSET = BUILD_REQUEST_RECORD_FLAG_OFFSET + 1; - const size_t BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET = BUILD_REQUEST_RECORD_REQUEST_TIME_OFFSET + 4; - const size_t BUILD_REQUEST_RECORD_PADDING_OFFSET = BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET + 4; - const size_t BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE = 222; - - // BuildRequestRecordEncrypted - const size_t BUILD_REQUEST_RECORD_TO_PEER_OFFSET = 0; - const size_t BUILD_REQUEST_RECORD_ENCRYPTED_OFFSET = BUILD_REQUEST_RECORD_TO_PEER_OFFSET + 16; - - // BuildResponseRecord - const size_t BUILD_RESPONSE_RECORD_HASH_OFFSET = 0; - const size_t BUILD_RESPONSE_RECORD_PADDING_OFFSET = 32; - const size_t BUILD_RESPONSE_RECORD_PADDING_SIZE = 495; - const size_t BUILD_RESPONSE_RECORD_RET_OFFSET = BUILD_RESPONSE_RECORD_PADDING_OFFSET + BUILD_RESPONSE_RECORD_PADDING_SIZE; + //BuildRequestRecordClearText + const size_t BUILD_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET = 0; + const size_t BUILD_REQUEST_RECORD_OUR_IDENT_OFFSET = BUILD_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET + 4; + const size_t BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET = BUILD_REQUEST_RECORD_OUR_IDENT_OFFSET + 32; + const size_t BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET = BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET + 4; + const size_t BUILD_REQUEST_RECORD_LAYER_KEY_OFFSET = BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET + 32; + const size_t BUILD_REQUEST_RECORD_IV_KEY_OFFSET = BUILD_REQUEST_RECORD_LAYER_KEY_OFFSET + 32; + const size_t BUILD_REQUEST_RECORD_REPLY_KEY_OFFSET = BUILD_REQUEST_RECORD_IV_KEY_OFFSET + 32; + const size_t BUILD_REQUEST_RECORD_REPLY_IV_OFFSET = BUILD_REQUEST_RECORD_REPLY_KEY_OFFSET + 32; + const size_t BUILD_REQUEST_RECORD_FLAG_OFFSET = BUILD_REQUEST_RECORD_REPLY_IV_OFFSET + 16; + const size_t BUILD_REQUEST_RECORD_REQUEST_TIME_OFFSET = BUILD_REQUEST_RECORD_FLAG_OFFSET + 1; + const size_t BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET = BUILD_REQUEST_RECORD_REQUEST_TIME_OFFSET + 4; + const size_t BUILD_REQUEST_RECORD_PADDING_OFFSET = BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET + 4; + const size_t BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE = 222; + + // BuildRequestRecordEncrypted + const size_t BUILD_REQUEST_RECORD_TO_PEER_OFFSET = 0; + const size_t BUILD_REQUEST_RECORD_ENCRYPTED_OFFSET = BUILD_REQUEST_RECORD_TO_PEER_OFFSET + 16; + + // BuildResponseRecord + const size_t BUILD_RESPONSE_RECORD_HASH_OFFSET = 0; + const size_t BUILD_RESPONSE_RECORD_PADDING_OFFSET = 32; + const size_t BUILD_RESPONSE_RECORD_PADDING_SIZE = 495; + const size_t BUILD_RESPONSE_RECORD_RET_OFFSET = BUILD_RESPONSE_RECORD_PADDING_OFFSET + BUILD_RESPONSE_RECORD_PADDING_SIZE; - enum I2NPMessageType - { - eI2NPDatabaseStore = 1, - eI2NPDatabaseLookup = 2, - eI2NPDatabaseSearchReply = 3, - eI2NPDeliveryStatus = 10, - eI2NPGarlic = 11, - eI2NPTunnelData = 18, - eI2NPTunnelGateway = 19, - eI2NPData = 20, - eI2NPTunnelBuild = 21, - eI2NPTunnelBuildReply = 22, - eI2NPVariableTunnelBuild = 23, - eI2NPVariableTunnelBuildReply = 24 - }; + enum I2NPMessageType + { + eI2NPDatabaseStore = 1, + eI2NPDatabaseLookup = 2, + eI2NPDatabaseSearchReply = 3, + eI2NPDeliveryStatus = 10, + eI2NPGarlic = 11, + eI2NPTunnelData = 18, + eI2NPTunnelGateway = 19, + eI2NPData = 20, + eI2NPTunnelBuild = 21, + eI2NPTunnelBuildReply = 22, + eI2NPVariableTunnelBuild = 23, + eI2NPVariableTunnelBuildReply = 24 + }; - const int NUM_TUNNEL_BUILD_RECORDS = 8; + const int NUM_TUNNEL_BUILD_RECORDS = 8; - // DatabaseLookup flags - const uint8_t DATABASE_LOOKUP_DELIVERY_FLAG = 0x01; - const uint8_t DATABASE_LOOKUP_ENCYPTION_FLAG = 0x02; - const uint8_t DATABASE_LOOKUP_TYPE_FLAGS_MASK = 0x0C; - const uint8_t DATABASE_LOOKUP_TYPE_NORMAL_LOOKUP = 0; - const uint8_t DATABASE_LOOKUP_TYPE_LEASESET_LOOKUP = 0x04; // 0100 - const uint8_t DATABASE_LOOKUP_TYPE_ROUTERINFO_LOOKUP = 0x08; // 1000 - const uint8_t DATABASE_LOOKUP_TYPE_EXPLORATORY_LOOKUP = 0x0C; // 1100 + // DatabaseLookup flags + const uint8_t DATABASE_LOOKUP_DELIVERY_FLAG = 0x01; + const uint8_t DATABASE_LOOKUP_ENCYPTION_FLAG = 0x02; + const uint8_t DATABASE_LOOKUP_TYPE_FLAGS_MASK = 0x0C; + const uint8_t DATABASE_LOOKUP_TYPE_NORMAL_LOOKUP = 0; + const uint8_t DATABASE_LOOKUP_TYPE_LEASESET_LOOKUP = 0x04; // 0100 + const uint8_t DATABASE_LOOKUP_TYPE_ROUTERINFO_LOOKUP = 0x08; // 1000 + const uint8_t DATABASE_LOOKUP_TYPE_EXPLORATORY_LOOKUP = 0x0C; // 1100 - const int MAX_NUM_TRANSIT_TUNNELS = 2500; + const int MAX_NUM_TRANSIT_TUNNELS = 2500; namespace tunnel -{ - class InboundTunnel; - class TunnelPool; +{ + class InboundTunnel; + class TunnelPool; } - const size_t I2NP_MAX_MESSAGE_SIZE = 32768; - const size_t I2NP_MAX_SHORT_MESSAGE_SIZE = 4096; - struct I2NPMessage - { - uint8_t * buf; - size_t len, offset, maxLen; - std::shared_ptr from; - - I2NPMessage (): buf (nullptr),len (I2NP_HEADER_SIZE + 2), - offset(2), maxLen (0), from (nullptr) {}; // reserve 2 bytes for NTCP header - - // header accessors - uint8_t * GetHeader () { return GetBuffer (); }; - const uint8_t * GetHeader () const { return GetBuffer (); }; - void SetTypeID (uint8_t typeID) { GetHeader ()[I2NP_HEADER_TYPEID_OFFSET] = typeID; }; - uint8_t GetTypeID () const { return GetHeader ()[I2NP_HEADER_TYPEID_OFFSET]; }; - void SetMsgID (uint32_t msgID) { htobe32buf (GetHeader () + I2NP_HEADER_MSGID_OFFSET, msgID); }; - uint32_t GetMsgID () const { return bufbe32toh (GetHeader () + I2NP_HEADER_MSGID_OFFSET); }; - void SetExpiration (uint64_t expiration) { htobe64buf (GetHeader () + I2NP_HEADER_EXPIRATION_OFFSET, expiration); }; - uint64_t GetExpiration () const { return bufbe64toh (GetHeader () + I2NP_HEADER_EXPIRATION_OFFSET); }; - void SetSize (uint16_t size) { htobe16buf (GetHeader () + I2NP_HEADER_SIZE_OFFSET, size); }; - uint16_t GetSize () const { return bufbe16toh (GetHeader () + I2NP_HEADER_SIZE_OFFSET); }; - void UpdateSize () { SetSize (GetPayloadLength ()); }; - void SetChks (uint8_t chks) { GetHeader ()[I2NP_HEADER_CHKS_OFFSET] = chks; }; - void UpdateChks () - { - uint8_t hash[32]; - CryptoPP::SHA256().CalculateDigest(hash, GetPayload (), GetPayloadLength ()); - GetHeader ()[I2NP_HEADER_CHKS_OFFSET] = hash[0]; - } - - // payload - uint8_t * GetPayload () { return GetBuffer () + I2NP_HEADER_SIZE; }; - const uint8_t * GetPayload () const { return GetBuffer () + I2NP_HEADER_SIZE; }; - uint8_t * GetBuffer () { return buf + offset; }; - const uint8_t * GetBuffer () const { return buf + offset; }; - size_t GetLength () const { return len - offset; }; - size_t GetPayloadLength () const { return GetLength () - I2NP_HEADER_SIZE; }; - - void Align (size_t alignment) - { - if (len + alignment > maxLen) return; - size_t rem = ((size_t)GetBuffer ()) % alignment; - if (rem) - { - offset += (alignment - rem); - len += (alignment - rem); - } - } + const size_t I2NP_MAX_MESSAGE_SIZE = 32768; + const size_t I2NP_MAX_SHORT_MESSAGE_SIZE = 4096; + struct I2NPMessage + { + uint8_t * buf; + size_t len, offset, maxLen; + std::shared_ptr from; + + I2NPMessage (): buf (nullptr),len (I2NP_HEADER_SIZE + 2), + offset(2), maxLen (0), from (nullptr) {}; // reserve 2 bytes for NTCP header + + // header accessors + uint8_t * GetHeader () { return GetBuffer (); }; + const uint8_t * GetHeader () const { return GetBuffer (); }; + void SetTypeID (uint8_t typeID) { GetHeader ()[I2NP_HEADER_TYPEID_OFFSET] = typeID; }; + uint8_t GetTypeID () const { return GetHeader ()[I2NP_HEADER_TYPEID_OFFSET]; }; + void SetMsgID (uint32_t msgID) { htobe32buf (GetHeader () + I2NP_HEADER_MSGID_OFFSET, msgID); }; + uint32_t GetMsgID () const { return bufbe32toh (GetHeader () + I2NP_HEADER_MSGID_OFFSET); }; + void SetExpiration (uint64_t expiration) { htobe64buf (GetHeader () + I2NP_HEADER_EXPIRATION_OFFSET, expiration); }; + uint64_t GetExpiration () const { return bufbe64toh (GetHeader () + I2NP_HEADER_EXPIRATION_OFFSET); }; + void SetSize (uint16_t size) { htobe16buf (GetHeader () + I2NP_HEADER_SIZE_OFFSET, size); }; + uint16_t GetSize () const { return bufbe16toh (GetHeader () + I2NP_HEADER_SIZE_OFFSET); }; + void UpdateSize () { SetSize (GetPayloadLength ()); }; + void SetChks (uint8_t chks) { GetHeader ()[I2NP_HEADER_CHKS_OFFSET] = chks; }; + void UpdateChks () + { + uint8_t hash[32]; + CryptoPP::SHA256().CalculateDigest(hash, GetPayload (), GetPayloadLength ()); + GetHeader ()[I2NP_HEADER_CHKS_OFFSET] = hash[0]; + } + + // payload + uint8_t * GetPayload () { return GetBuffer () + I2NP_HEADER_SIZE; }; + const uint8_t * GetPayload () const { return GetBuffer () + I2NP_HEADER_SIZE; }; + uint8_t * GetBuffer () { return buf + offset; }; + const uint8_t * GetBuffer () const { return buf + offset; }; + size_t GetLength () const { return len - offset; }; + size_t GetPayloadLength () const { return GetLength () - I2NP_HEADER_SIZE; }; + + void Align (size_t alignment) + { + if (len + alignment > maxLen) return; + size_t rem = ((size_t)GetBuffer ()) % alignment; + if (rem) + { + offset += (alignment - rem); + len += (alignment - rem); + } + } - I2NPMessage& operator=(const I2NPMessage& other) - { - memcpy (buf + offset, other.buf + other.offset, other.GetLength ()); - len = offset + other.GetLength (); - from = other.from; + I2NPMessage& operator=(const I2NPMessage& other) + { + memcpy (buf + offset, other.buf + other.offset, other.GetLength ()); + len = offset + other.GetLength (); + from = other.from; maxLen = other.maxLen; - return *this; - } + return *this; + } - // for SSU only - uint8_t * GetSSUHeader () { return buf + offset + I2NP_HEADER_SIZE - I2NP_SHORT_HEADER_SIZE; }; - void FromSSU (uint32_t msgID) // we have received SSU message and convert it to regular - { - const uint8_t * ssu = GetSSUHeader (); - GetHeader ()[I2NP_HEADER_TYPEID_OFFSET] = ssu[I2NP_SHORT_HEADER_TYPEID_OFFSET]; // typeid - SetMsgID (msgID); - SetExpiration (bufbe32toh (ssu + I2NP_SHORT_HEADER_EXPIRATION_OFFSET)*1000LL); - SetSize (len - offset - I2NP_HEADER_SIZE); - SetChks (0); - } - uint32_t ToSSU () // return msgID - { - uint8_t header[I2NP_HEADER_SIZE]; - memcpy (header, GetHeader (), I2NP_HEADER_SIZE); - uint8_t * ssu = GetSSUHeader (); - ssu[I2NP_SHORT_HEADER_TYPEID_OFFSET] = header[I2NP_HEADER_TYPEID_OFFSET]; // typeid - htobe32buf (ssu + I2NP_SHORT_HEADER_EXPIRATION_OFFSET, bufbe64toh (header + I2NP_HEADER_EXPIRATION_OFFSET)/1000LL); - len = offset + I2NP_SHORT_HEADER_SIZE + bufbe16toh (header + I2NP_HEADER_SIZE_OFFSET); - return bufbe32toh (header + I2NP_HEADER_MSGID_OFFSET); - } + // for SSU only + uint8_t * GetSSUHeader () { return buf + offset + I2NP_HEADER_SIZE - I2NP_SHORT_HEADER_SIZE; }; + void FromSSU (uint32_t msgID) // we have received SSU message and convert it to regular + { + const uint8_t * ssu = GetSSUHeader (); + GetHeader ()[I2NP_HEADER_TYPEID_OFFSET] = ssu[I2NP_SHORT_HEADER_TYPEID_OFFSET]; // typeid + SetMsgID (msgID); + SetExpiration (bufbe32toh (ssu + I2NP_SHORT_HEADER_EXPIRATION_OFFSET)*1000LL); + SetSize (len - offset - I2NP_HEADER_SIZE); + SetChks (0); + } + uint32_t ToSSU () // return msgID + { + uint8_t header[I2NP_HEADER_SIZE]; + memcpy (header, GetHeader (), I2NP_HEADER_SIZE); + uint8_t * ssu = GetSSUHeader (); + ssu[I2NP_SHORT_HEADER_TYPEID_OFFSET] = header[I2NP_HEADER_TYPEID_OFFSET]; // typeid + htobe32buf (ssu + I2NP_SHORT_HEADER_EXPIRATION_OFFSET, bufbe64toh (header + I2NP_HEADER_EXPIRATION_OFFSET)/1000LL); + len = offset + I2NP_SHORT_HEADER_SIZE + bufbe16toh (header + I2NP_HEADER_SIZE_OFFSET); + return bufbe32toh (header + I2NP_HEADER_MSGID_OFFSET); + } - void FillI2NPMessageHeader (I2NPMessageType msgType, uint32_t replyMsgID = 0); - void RenewI2NPMessageHeader (); - }; + void FillI2NPMessageHeader (I2NPMessageType msgType, uint32_t replyMsgID = 0); + void RenewI2NPMessageHeader (); + }; - template - struct I2NPMessageBuffer: public I2NPMessage - { - I2NPMessageBuffer () { buf = m_Buffer; maxLen = sz; }; - uint8_t m_Buffer[sz + 16] = {}; - }; + template + struct I2NPMessageBuffer: public I2NPMessage + { + I2NPMessageBuffer () { buf = m_Buffer; maxLen = sz; }; + uint8_t m_Buffer[sz + 16] = {}; + }; - I2NPMessage * NewI2NPMessage (); - I2NPMessage * NewI2NPShortMessage (); - I2NPMessage * NewI2NPMessage (size_t len); - void DeleteI2NPMessage (I2NPMessage * msg); - std::shared_ptr ToSharedI2NPMessage (I2NPMessage * msg); - - I2NPMessage * CreateI2NPMessage (I2NPMessageType msgType, const uint8_t * buf, int len, uint32_t replyMsgID = 0); - std::shared_ptr CreateI2NPMessage (const uint8_t * buf, int len, std::shared_ptr from = nullptr); - - std::shared_ptr CreateDeliveryStatusMsg (uint32_t msgID); - std::shared_ptr CreateRouterInfoDatabaseLookupMsg (const uint8_t * key, const uint8_t * from, - uint32_t replyTunnelID, bool exploratory = false, std::set * excludedPeers = nullptr); - std::shared_ptr CreateLeaseSetDatabaseLookupMsg (const i2p::data::IdentHash& dest, - const std::set& excludedFloodfills, - const i2p::tunnel::InboundTunnel * replyTunnel, const uint8_t * replyKey, const uint8_t * replyTag); - std::shared_ptr CreateDatabaseSearchReply (const i2p::data::IdentHash& ident, std::vector routers); - - std::shared_ptr CreateDatabaseStoreMsg (std::shared_ptr router = nullptr, uint32_t replyToken = 0); - std::shared_ptr CreateDatabaseStoreMsg (std::shared_ptr leaseSet, uint32_t replyToken = 0); - - bool HandleBuildRequestRecords (int num, uint8_t * records, uint8_t * clearText); - void HandleVariableTunnelBuildMsg (uint32_t replyMsgID, uint8_t * buf, size_t len); - void HandleVariableTunnelBuildReplyMsg (uint32_t replyMsgID, uint8_t * buf, size_t len); - void HandleTunnelBuildMsg (uint8_t * buf, size_t len); + I2NPMessage * NewI2NPMessage (); + I2NPMessage * NewI2NPShortMessage (); + I2NPMessage * NewI2NPMessage (size_t len); + void DeleteI2NPMessage (I2NPMessage * msg); + std::shared_ptr ToSharedI2NPMessage (I2NPMessage * msg); + + I2NPMessage * CreateI2NPMessage (I2NPMessageType msgType, const uint8_t * buf, int len, uint32_t replyMsgID = 0); + std::shared_ptr CreateI2NPMessage (const uint8_t * buf, int len, std::shared_ptr from = nullptr); + + std::shared_ptr CreateDeliveryStatusMsg (uint32_t msgID); + std::shared_ptr CreateRouterInfoDatabaseLookupMsg (const uint8_t * key, const uint8_t * from, + uint32_t replyTunnelID, bool exploratory = false, std::set * excludedPeers = nullptr); + std::shared_ptr CreateLeaseSetDatabaseLookupMsg (const i2p::data::IdentHash& dest, + const std::set& excludedFloodfills, + const i2p::tunnel::InboundTunnel * replyTunnel, const uint8_t * replyKey, const uint8_t * replyTag); + std::shared_ptr CreateDatabaseSearchReply (const i2p::data::IdentHash& ident, std::vector routers); + + std::shared_ptr CreateDatabaseStoreMsg (std::shared_ptr router = nullptr, uint32_t replyToken = 0); + std::shared_ptr CreateDatabaseStoreMsg (std::shared_ptr leaseSet, uint32_t replyToken = 0); + + bool HandleBuildRequestRecords (int num, uint8_t * records, uint8_t * clearText); + void HandleVariableTunnelBuildMsg (uint32_t replyMsgID, uint8_t * buf, size_t len); + void HandleVariableTunnelBuildReplyMsg (uint32_t replyMsgID, uint8_t * buf, size_t len); + void HandleTunnelBuildMsg (uint8_t * buf, size_t len); - I2NPMessage * CreateTunnelDataMsg (const uint8_t * buf); - I2NPMessage * CreateTunnelDataMsg (uint32_t tunnelID, const uint8_t * payload); - std::shared_ptr CreateEmptyTunnelDataMsg (); - - I2NPMessage * CreateTunnelGatewayMsg (uint32_t tunnelID, const uint8_t * buf, size_t len); - I2NPMessage * CreateTunnelGatewayMsg (uint32_t tunnelID, I2NPMessageType msgType, - const uint8_t * buf, size_t len, uint32_t replyMsgID = 0); - std::shared_ptr CreateTunnelGatewayMsg (uint32_t tunnelID, std::shared_ptr msg); + I2NPMessage * CreateTunnelDataMsg (const uint8_t * buf); + I2NPMessage * CreateTunnelDataMsg (uint32_t tunnelID, const uint8_t * payload); + std::shared_ptr CreateEmptyTunnelDataMsg (); + + I2NPMessage * CreateTunnelGatewayMsg (uint32_t tunnelID, const uint8_t * buf, size_t len); + I2NPMessage * CreateTunnelGatewayMsg (uint32_t tunnelID, I2NPMessageType msgType, + const uint8_t * buf, size_t len, uint32_t replyMsgID = 0); + std::shared_ptr CreateTunnelGatewayMsg (uint32_t tunnelID, std::shared_ptr msg); - size_t GetI2NPMessageLength (const uint8_t * msg); - void HandleI2NPMessage (uint8_t * msg, size_t len); - void HandleI2NPMessage (std::shared_ptr msg); + size_t GetI2NPMessageLength (const uint8_t * msg); + void HandleI2NPMessage (uint8_t * msg, size_t len); + void HandleI2NPMessage (std::shared_ptr msg); - class I2NPMessagesHandler - { - public: + class I2NPMessagesHandler + { + public: - ~I2NPMessagesHandler (); - void PutNextMessage (std::shared_ptr msg); - void Flush (); - - private: + ~I2NPMessagesHandler (); + void PutNextMessage (std::shared_ptr msg); + void Flush (); + + private: - std::vector > m_TunnelMsgs, m_TunnelGatewayMsgs; - }; -} + std::vector > m_TunnelMsgs, m_TunnelGatewayMsgs; + }; +} #endif diff --git a/I2PControl.cpp b/I2PControl.cpp index 1dae4997..3e62f947 100644 --- a/I2PControl.cpp +++ b/I2PControl.cpp @@ -22,395 +22,395 @@ namespace i2p { namespace client { - I2PControlService::I2PControlService (int port): - m_Password (I2P_CONTROL_DEFAULT_PASSWORD), m_IsRunning (false), m_Thread (nullptr), - m_Acceptor (m_Service, boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), port)), - m_ShutdownTimer (m_Service) - { - m_MethodHandlers[I2P_CONTROL_METHOD_AUTHENTICATE] = &I2PControlService::AuthenticateHandler; - m_MethodHandlers[I2P_CONTROL_METHOD_ECHO] = &I2PControlService::EchoHandler; - m_MethodHandlers[I2P_CONTROL_METHOD_I2PCONTROL] = &I2PControlService::I2PControlHandler; - m_MethodHandlers[I2P_CONTROL_METHOD_ROUTER_INFO] = &I2PControlService::RouterInfoHandler; - m_MethodHandlers[I2P_CONTROL_METHOD_ROUTER_MANAGER] = &I2PControlService::RouterManagerHandler; - m_MethodHandlers[I2P_CONTROL_METHOD_NETWORK_SETTING] = &I2PControlService::NetworkSettingHandler; + I2PControlService::I2PControlService (int port): + m_Password (I2P_CONTROL_DEFAULT_PASSWORD), m_IsRunning (false), m_Thread (nullptr), + m_Acceptor (m_Service, boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), port)), + m_ShutdownTimer (m_Service) + { + m_MethodHandlers[I2P_CONTROL_METHOD_AUTHENTICATE] = &I2PControlService::AuthenticateHandler; + m_MethodHandlers[I2P_CONTROL_METHOD_ECHO] = &I2PControlService::EchoHandler; + m_MethodHandlers[I2P_CONTROL_METHOD_I2PCONTROL] = &I2PControlService::I2PControlHandler; + m_MethodHandlers[I2P_CONTROL_METHOD_ROUTER_INFO] = &I2PControlService::RouterInfoHandler; + m_MethodHandlers[I2P_CONTROL_METHOD_ROUTER_MANAGER] = &I2PControlService::RouterManagerHandler; + m_MethodHandlers[I2P_CONTROL_METHOD_NETWORK_SETTING] = &I2PControlService::NetworkSettingHandler; - // RouterInfo - m_RouterInfoHandlers[I2P_CONTROL_ROUTER_INFO_UPTIME] = &I2PControlService::UptimeHandler; - m_RouterInfoHandlers[I2P_CONTROL_ROUTER_INFO_VERSION] = &I2PControlService::VersionHandler; - m_RouterInfoHandlers[I2P_CONTROL_ROUTER_INFO_STATUS] = &I2PControlService::StatusHandler; - m_RouterInfoHandlers[I2P_CONTROL_ROUTER_INFO_NETDB_KNOWNPEERS] = &I2PControlService::NetDbKnownPeersHandler; - m_RouterInfoHandlers[I2P_CONTROL_ROUTER_INFO_NETDB_ACTIVEPEERS] = &I2PControlService::NetDbActivePeersHandler; - m_RouterInfoHandlers[I2P_CONTROL_ROUTER_INFO_NET_STATUS] = &I2PControlService::NetStatusHandler; - m_RouterInfoHandlers[I2P_CONTROL_ROUTER_INFO_TUNNELS_PARTICIPATING] = &I2PControlService::TunnelsParticipatingHandler; - m_RouterInfoHandlers[I2P_CONTROL_ROUTER_INFO_BW_IB_1S] = &I2PControlService::InboundBandwidth1S ; - m_RouterInfoHandlers[I2P_CONTROL_ROUTER_INFO_BW_OB_1S] = &I2PControlService::OutboundBandwidth1S ; + // RouterInfo + m_RouterInfoHandlers[I2P_CONTROL_ROUTER_INFO_UPTIME] = &I2PControlService::UptimeHandler; + m_RouterInfoHandlers[I2P_CONTROL_ROUTER_INFO_VERSION] = &I2PControlService::VersionHandler; + m_RouterInfoHandlers[I2P_CONTROL_ROUTER_INFO_STATUS] = &I2PControlService::StatusHandler; + m_RouterInfoHandlers[I2P_CONTROL_ROUTER_INFO_NETDB_KNOWNPEERS] = &I2PControlService::NetDbKnownPeersHandler; + m_RouterInfoHandlers[I2P_CONTROL_ROUTER_INFO_NETDB_ACTIVEPEERS] = &I2PControlService::NetDbActivePeersHandler; + m_RouterInfoHandlers[I2P_CONTROL_ROUTER_INFO_NET_STATUS] = &I2PControlService::NetStatusHandler; + m_RouterInfoHandlers[I2P_CONTROL_ROUTER_INFO_TUNNELS_PARTICIPATING] = &I2PControlService::TunnelsParticipatingHandler; + m_RouterInfoHandlers[I2P_CONTROL_ROUTER_INFO_BW_IB_1S] = &I2PControlService::InboundBandwidth1S ; + m_RouterInfoHandlers[I2P_CONTROL_ROUTER_INFO_BW_OB_1S] = &I2PControlService::OutboundBandwidth1S ; - // RouterManager - m_RouterManagerHandlers[I2P_CONTROL_ROUTER_MANAGER_SHUTDOWN] = &I2PControlService::ShutdownHandler; - m_RouterManagerHandlers[I2P_CONTROL_ROUTER_MANAGER_SHUTDOWN_GRACEFUL] = &I2PControlService::ShutdownGracefulHandler; - m_RouterManagerHandlers[I2P_CONTROL_ROUTER_MANAGER_RESEED] = &I2PControlService::ReseedHandler; - } + // RouterManager + m_RouterManagerHandlers[I2P_CONTROL_ROUTER_MANAGER_SHUTDOWN] = &I2PControlService::ShutdownHandler; + m_RouterManagerHandlers[I2P_CONTROL_ROUTER_MANAGER_SHUTDOWN_GRACEFUL] = &I2PControlService::ShutdownGracefulHandler; + m_RouterManagerHandlers[I2P_CONTROL_ROUTER_MANAGER_RESEED] = &I2PControlService::ReseedHandler; + } - I2PControlService::~I2PControlService () - { - Stop (); - } + I2PControlService::~I2PControlService () + { + Stop (); + } - void I2PControlService::Start () - { - if (!m_IsRunning) - { - Accept (); - m_IsRunning = true; - m_Thread = new std::thread (std::bind (&I2PControlService::Run, this)); - } - } + void I2PControlService::Start () + { + if (!m_IsRunning) + { + Accept (); + m_IsRunning = true; + m_Thread = new std::thread (std::bind (&I2PControlService::Run, this)); + } + } - void I2PControlService::Stop () - { - if (m_IsRunning) - { - m_IsRunning = false; - m_Acceptor.cancel (); - m_Service.stop (); - if (m_Thread) - { - m_Thread->join (); - delete m_Thread; - m_Thread = nullptr; - } - } - } + void I2PControlService::Stop () + { + if (m_IsRunning) + { + m_IsRunning = false; + m_Acceptor.cancel (); + m_Service.stop (); + if (m_Thread) + { + m_Thread->join (); + delete m_Thread; + m_Thread = nullptr; + } + } + } - void I2PControlService::Run () - { - while (m_IsRunning) - { - try - { - m_Service.run (); - } - catch (std::exception& ex) - { - LogPrint (eLogError, "I2PControl: ", ex.what ()); - } - } - } + void I2PControlService::Run () + { + while (m_IsRunning) + { + try + { + m_Service.run (); + } + catch (std::exception& ex) + { + LogPrint (eLogError, "I2PControl: ", ex.what ()); + } + } + } - void I2PControlService::Accept () - { - auto newSocket = std::make_shared (m_Service); - m_Acceptor.async_accept (*newSocket, std::bind (&I2PControlService::HandleAccept, this, - std::placeholders::_1, newSocket)); - } + void I2PControlService::Accept () + { + auto newSocket = std::make_shared (m_Service); + m_Acceptor.async_accept (*newSocket, std::bind (&I2PControlService::HandleAccept, this, + std::placeholders::_1, newSocket)); + } - void I2PControlService::HandleAccept(const boost::system::error_code& ecode, std::shared_ptr socket) - { - if (ecode != boost::asio::error::operation_aborted) - Accept (); + void I2PControlService::HandleAccept(const boost::system::error_code& ecode, std::shared_ptr socket) + { + if (ecode != boost::asio::error::operation_aborted) + Accept (); - if (!ecode) - { - LogPrint (eLogInfo, "New I2PControl request from ", socket->remote_endpoint ()); - std::this_thread::sleep_for (std::chrono::milliseconds(5)); - ReadRequest (socket); - } - else - LogPrint (eLogError, "I2PControl accept error: ", ecode.message ()); - } + if (!ecode) + { + LogPrint (eLogInfo, "New I2PControl request from ", socket->remote_endpoint ()); + std::this_thread::sleep_for (std::chrono::milliseconds(5)); + ReadRequest (socket); + } + else + LogPrint (eLogError, "I2PControl accept error: ", ecode.message ()); + } - void I2PControlService::ReadRequest (std::shared_ptr socket) - { - auto request = std::make_shared(); - socket->async_read_some ( + void I2PControlService::ReadRequest (std::shared_ptr socket) + { + auto request = std::make_shared(); + socket->async_read_some ( #if BOOST_VERSION >= 104900 - boost::asio::buffer (*request), + boost::asio::buffer (*request), #else - boost::asio::buffer (request->data (), request->size ()), + boost::asio::buffer (request->data (), request->size ()), #endif - std::bind(&I2PControlService::HandleRequestReceived, this, - std::placeholders::_1, std::placeholders::_2, socket, request)); - } + std::bind(&I2PControlService::HandleRequestReceived, this, + std::placeholders::_1, std::placeholders::_2, socket, request)); + } - void I2PControlService::HandleRequestReceived (const boost::system::error_code& ecode, - size_t bytes_transferred, std::shared_ptr socket, - std::shared_ptr buf) - { - if (ecode) - { - LogPrint (eLogError, "I2PControl read error: ", ecode.message ()); - } - else - { - try - { - bool isHtml = !memcmp (buf->data (), "POST", 4); - std::stringstream ss; - ss.write (buf->data (), bytes_transferred); - if (isHtml) - { - std::string header; - while (!ss.eof () && header != "\r") - std::getline(ss, header); - if (ss.eof ()) - { - LogPrint (eLogError, "Malformed I2PControl request. HTTP header expected"); - return; // TODO: - } - } + void I2PControlService::HandleRequestReceived (const boost::system::error_code& ecode, + size_t bytes_transferred, std::shared_ptr socket, + std::shared_ptr buf) + { + if (ecode) + { + LogPrint (eLogError, "I2PControl read error: ", ecode.message ()); + } + else + { + try + { + bool isHtml = !memcmp (buf->data (), "POST", 4); + std::stringstream ss; + ss.write (buf->data (), bytes_transferred); + if (isHtml) + { + std::string header; + while (!ss.eof () && header != "\r") + std::getline(ss, header); + if (ss.eof ()) + { + LogPrint (eLogError, "Malformed I2PControl request. HTTP header expected"); + return; // TODO: + } + } #if GCC47_BOOST149 - LogPrint (eLogError, "json_read is not supported due bug in boost 1.49 with gcc 4.7"); + LogPrint (eLogError, "json_read is not supported due bug in boost 1.49 with gcc 4.7"); #else - boost::property_tree::ptree pt; - boost::property_tree::read_json (ss, pt); + boost::property_tree::ptree pt; + boost::property_tree::read_json (ss, pt); - std::string method = pt.get(I2P_CONTROL_PROPERTY_METHOD); - auto it = m_MethodHandlers.find (method); - if (it != m_MethodHandlers.end ()) - { - std::ostringstream response; - response << "{\"id\":" << pt.get(I2P_CONTROL_PROPERTY_ID) << ",\"result\":{"; + std::string method = pt.get(I2P_CONTROL_PROPERTY_METHOD); + auto it = m_MethodHandlers.find (method); + if (it != m_MethodHandlers.end ()) + { + std::ostringstream response; + response << "{\"id\":" << pt.get(I2P_CONTROL_PROPERTY_ID) << ",\"result\":{"; - (this->*(it->second))(pt.get_child (I2P_CONTROL_PROPERTY_PARAMS), response); - response << "},\"jsonrpc\":\"2.0\"}"; - SendResponse (socket, buf, response, isHtml); - } - else - LogPrint (eLogWarning, "Unknown I2PControl method ", method); + (this->*(it->second))(pt.get_child (I2P_CONTROL_PROPERTY_PARAMS), response); + response << "},\"jsonrpc\":\"2.0\"}"; + SendResponse (socket, buf, response, isHtml); + } + else + LogPrint (eLogWarning, "Unknown I2PControl method ", method); #endif - } - catch (std::exception& ex) - { - LogPrint (eLogError, "I2PControl handle request: ", ex.what ()); - } - catch (...) - { - LogPrint (eLogError, "I2PControl handle request unknown exception"); - } - } - } + } + catch (std::exception& ex) + { + LogPrint (eLogError, "I2PControl handle request: ", ex.what ()); + } + catch (...) + { + LogPrint (eLogError, "I2PControl handle request unknown exception"); + } + } + } - void I2PControlService::InsertParam (std::ostringstream& ss, const std::string& name, int value) const - { - ss << "\"" << name << "\":" << value; - } + void I2PControlService::InsertParam (std::ostringstream& ss, const std::string& name, int value) const + { + ss << "\"" << name << "\":" << value; + } - void I2PControlService::InsertParam (std::ostringstream& ss, const std::string& name, const std::string& value) const - { - ss << "\"" << name << "\":"; - if (value.length () > 0) - ss << "\"" << value << "\""; - else - ss << "null"; - } - - void I2PControlService::InsertParam (std::ostringstream& ss, const std::string& name, double value) const - { - ss << "\"" << name << "\":" << std::fixed << std::setprecision(2) << value; - } + void I2PControlService::InsertParam (std::ostringstream& ss, const std::string& name, const std::string& value) const + { + ss << "\"" << name << "\":"; + if (value.length () > 0) + ss << "\"" << value << "\""; + else + ss << "null"; + } + + void I2PControlService::InsertParam (std::ostringstream& ss, const std::string& name, double value) const + { + ss << "\"" << name << "\":" << std::fixed << std::setprecision(2) << value; + } - void I2PControlService::SendResponse (std::shared_ptr socket, - std::shared_ptr buf, std::ostringstream& response, bool isHtml) - { - size_t len = response.str ().length (), offset = 0; - if (isHtml) - { - std::ostringstream header; - header << "HTTP/1.1 200 OK\r\n"; - header << "Connection: close\r\n"; - header << "Content-Length: " << boost::lexical_cast(len) << "\r\n"; - header << "Content-Type: application/json\r\n"; - header << "Date: "; - auto facet = new boost::local_time::local_time_facet ("%a, %d %b %Y %H:%M:%S GMT"); - header.imbue(std::locale (header.getloc(), facet)); - header << boost::posix_time::second_clock::local_time() << "\r\n"; - header << "\r\n"; - offset = header.str ().size (); - memcpy (buf->data (), header.str ().c_str (), offset); - } - memcpy (buf->data () + offset, response.str ().c_str (), len); - boost::asio::async_write (*socket, boost::asio::buffer (buf->data (), offset + len), - boost::asio::transfer_all (), - std::bind(&I2PControlService::HandleResponseSent, this, - std::placeholders::_1, std::placeholders::_2, socket, buf)); - } + void I2PControlService::SendResponse (std::shared_ptr socket, + std::shared_ptr buf, std::ostringstream& response, bool isHtml) + { + size_t len = response.str ().length (), offset = 0; + if (isHtml) + { + std::ostringstream header; + header << "HTTP/1.1 200 OK\r\n"; + header << "Connection: close\r\n"; + header << "Content-Length: " << boost::lexical_cast(len) << "\r\n"; + header << "Content-Type: application/json\r\n"; + header << "Date: "; + auto facet = new boost::local_time::local_time_facet ("%a, %d %b %Y %H:%M:%S GMT"); + header.imbue(std::locale (header.getloc(), facet)); + header << boost::posix_time::second_clock::local_time() << "\r\n"; + header << "\r\n"; + offset = header.str ().size (); + memcpy (buf->data (), header.str ().c_str (), offset); + } + memcpy (buf->data () + offset, response.str ().c_str (), len); + boost::asio::async_write (*socket, boost::asio::buffer (buf->data (), offset + len), + boost::asio::transfer_all (), + std::bind(&I2PControlService::HandleResponseSent, this, + std::placeholders::_1, std::placeholders::_2, socket, buf)); + } - void I2PControlService::HandleResponseSent (const boost::system::error_code& ecode, std::size_t bytes_transferred, - std::shared_ptr socket, std::shared_ptr buf) - { - if (ecode) - LogPrint (eLogError, "I2PControl write error: ", ecode.message ()); - socket->close (); - } + void I2PControlService::HandleResponseSent (const boost::system::error_code& ecode, std::size_t bytes_transferred, + std::shared_ptr socket, std::shared_ptr buf) + { + if (ecode) + LogPrint (eLogError, "I2PControl write error: ", ecode.message ()); + socket->close (); + } // handlers - void I2PControlService::AuthenticateHandler (const boost::property_tree::ptree& params, std::ostringstream& results) - { - int api = params.get (I2P_CONTROL_PARAM_API); - auto password = params.get (I2P_CONTROL_PARAM_PASSWORD); - LogPrint (eLogDebug, "I2PControl Authenticate API=", api, " Password=", password); - if (password != m_Password) - LogPrint (eLogError, "I2PControl Authenticate Invalid password ", password, " expected ", m_Password); - InsertParam (results, I2P_CONTROL_PARAM_API, api); - results << ","; - std::string token = boost::lexical_cast(i2p::util::GetSecondsSinceEpoch ()); - m_Tokens.insert (token); - InsertParam (results, I2P_CONTROL_PARAM_TOKEN, token); - } + void I2PControlService::AuthenticateHandler (const boost::property_tree::ptree& params, std::ostringstream& results) + { + int api = params.get (I2P_CONTROL_PARAM_API); + auto password = params.get (I2P_CONTROL_PARAM_PASSWORD); + LogPrint (eLogDebug, "I2PControl Authenticate API=", api, " Password=", password); + if (password != m_Password) + LogPrint (eLogError, "I2PControl Authenticate Invalid password ", password, " expected ", m_Password); + InsertParam (results, I2P_CONTROL_PARAM_API, api); + results << ","; + std::string token = boost::lexical_cast(i2p::util::GetSecondsSinceEpoch ()); + m_Tokens.insert (token); + InsertParam (results, I2P_CONTROL_PARAM_TOKEN, token); + } - void I2PControlService::EchoHandler (const boost::property_tree::ptree& params, std::ostringstream& results) - { - auto echo = params.get (I2P_CONTROL_PARAM_ECHO); - LogPrint (eLogDebug, "I2PControl Echo Echo=", echo); - InsertParam (results, I2P_CONTROL_PARAM_RESULT, echo); - } + void I2PControlService::EchoHandler (const boost::property_tree::ptree& params, std::ostringstream& results) + { + auto echo = params.get (I2P_CONTROL_PARAM_ECHO); + LogPrint (eLogDebug, "I2PControl Echo Echo=", echo); + InsertParam (results, I2P_CONTROL_PARAM_RESULT, echo); + } // I2PControl - void I2PControlService::I2PControlHandler (const boost::property_tree::ptree& params, std::ostringstream& results) - { - LogPrint (eLogDebug, "I2PControl I2PControl"); - for (auto& it: params) - { - LogPrint (eLogDebug, it.first); - auto it1 = m_I2PControlHandlers.find (it.first); - if (it1 != m_I2PControlHandlers.end ()) - (this->*(it1->second))(it.second.data ()); - else - LogPrint (eLogError, "I2PControl NetworkSetting unknown request ", it.first); - } - } + void I2PControlService::I2PControlHandler (const boost::property_tree::ptree& params, std::ostringstream& results) + { + LogPrint (eLogDebug, "I2PControl I2PControl"); + for (auto& it: params) + { + LogPrint (eLogDebug, it.first); + auto it1 = m_I2PControlHandlers.find (it.first); + if (it1 != m_I2PControlHandlers.end ()) + (this->*(it1->second))(it.second.data ()); + else + LogPrint (eLogError, "I2PControl NetworkSetting unknown request ", it.first); + } + } // RouterInfo - void I2PControlService::RouterInfoHandler (const boost::property_tree::ptree& params, std::ostringstream& results) - { - LogPrint (eLogDebug, "I2PControl RouterInfo"); - for (auto it = params.begin (); it != params.end (); it++) - { - if (it != params.begin ()) results << ","; - LogPrint (eLogDebug, it->first); - auto it1 = m_RouterInfoHandlers.find (it->first); - if (it1 != m_RouterInfoHandlers.end ()) - (this->*(it1->second))(results); - else - LogPrint (eLogError, "I2PControl RouterInfo unknown request ", it->first); - } - } + void I2PControlService::RouterInfoHandler (const boost::property_tree::ptree& params, std::ostringstream& results) + { + LogPrint (eLogDebug, "I2PControl RouterInfo"); + for (auto it = params.begin (); it != params.end (); it++) + { + if (it != params.begin ()) results << ","; + LogPrint (eLogDebug, it->first); + auto it1 = m_RouterInfoHandlers.find (it->first); + if (it1 != m_RouterInfoHandlers.end ()) + (this->*(it1->second))(results); + else + LogPrint (eLogError, "I2PControl RouterInfo unknown request ", it->first); + } + } - void I2PControlService::UptimeHandler (std::ostringstream& results) - { - InsertParam (results, I2P_CONTROL_ROUTER_INFO_UPTIME, (int)i2p::context.GetUptime ()*1000); - } + void I2PControlService::UptimeHandler (std::ostringstream& results) + { + InsertParam (results, I2P_CONTROL_ROUTER_INFO_UPTIME, (int)i2p::context.GetUptime ()*1000); + } - void I2PControlService::VersionHandler (std::ostringstream& results) - { - InsertParam (results, I2P_CONTROL_ROUTER_INFO_VERSION, VERSION); - } + void I2PControlService::VersionHandler (std::ostringstream& results) + { + InsertParam (results, I2P_CONTROL_ROUTER_INFO_VERSION, VERSION); + } - void I2PControlService::StatusHandler (std::ostringstream& results) - { - InsertParam (results, I2P_CONTROL_ROUTER_INFO_STATUS, "???"); // TODO: - } - - void I2PControlService::NetDbKnownPeersHandler (std::ostringstream& results) - { - InsertParam (results, I2P_CONTROL_ROUTER_INFO_NETDB_KNOWNPEERS, i2p::data::netdb.GetNumRouters ()); - } + void I2PControlService::StatusHandler (std::ostringstream& results) + { + InsertParam (results, I2P_CONTROL_ROUTER_INFO_STATUS, "???"); // TODO: + } + + void I2PControlService::NetDbKnownPeersHandler (std::ostringstream& results) + { + InsertParam (results, I2P_CONTROL_ROUTER_INFO_NETDB_KNOWNPEERS, i2p::data::netdb.GetNumRouters ()); + } - void I2PControlService::NetDbActivePeersHandler (std::ostringstream& results) - { - InsertParam (results, I2P_CONTROL_ROUTER_INFO_NETDB_ACTIVEPEERS, (int)i2p::transport::transports.GetPeers ().size ()); - } + void I2PControlService::NetDbActivePeersHandler (std::ostringstream& results) + { + InsertParam (results, I2P_CONTROL_ROUTER_INFO_NETDB_ACTIVEPEERS, (int)i2p::transport::transports.GetPeers ().size ()); + } - void I2PControlService::NetStatusHandler (std::ostringstream& results) - { - InsertParam (results, I2P_CONTROL_ROUTER_INFO_NET_STATUS, (int)i2p::context.GetStatus ()); - } + void I2PControlService::NetStatusHandler (std::ostringstream& results) + { + InsertParam (results, I2P_CONTROL_ROUTER_INFO_NET_STATUS, (int)i2p::context.GetStatus ()); + } - void I2PControlService::TunnelsParticipatingHandler (std::ostringstream& results) - { - InsertParam (results, I2P_CONTROL_ROUTER_INFO_TUNNELS_PARTICIPATING, (int)i2p::tunnel::tunnels.GetTransitTunnels ().size ()); - } + void I2PControlService::TunnelsParticipatingHandler (std::ostringstream& results) + { + InsertParam (results, I2P_CONTROL_ROUTER_INFO_TUNNELS_PARTICIPATING, (int)i2p::tunnel::tunnels.GetTransitTunnels ().size ()); + } - void I2PControlService::InboundBandwidth1S (std::ostringstream& results) - { - InsertParam (results, I2P_CONTROL_ROUTER_INFO_BW_IB_1S, (double)i2p::transport::transports.GetInBandwidth ()); - } + void I2PControlService::InboundBandwidth1S (std::ostringstream& results) + { + InsertParam (results, I2P_CONTROL_ROUTER_INFO_BW_IB_1S, (double)i2p::transport::transports.GetInBandwidth ()); + } - void I2PControlService::OutboundBandwidth1S (std::ostringstream& results) - { - InsertParam (results, I2P_CONTROL_ROUTER_INFO_BW_OB_1S, (double)i2p::transport::transports.GetOutBandwidth ()); - } + void I2PControlService::OutboundBandwidth1S (std::ostringstream& results) + { + InsertParam (results, I2P_CONTROL_ROUTER_INFO_BW_OB_1S, (double)i2p::transport::transports.GetOutBandwidth ()); + } // RouterManager - - void I2PControlService::RouterManagerHandler (const boost::property_tree::ptree& params, std::ostringstream& results) - { - LogPrint (eLogDebug, "I2PControl RouterManager"); - for (auto it = params.begin (); it != params.end (); it++) - { - if (it != params.begin ()) results << ","; - LogPrint (eLogDebug, it->first); - auto it1 = m_RouterManagerHandlers.find (it->first); - if (it1 != m_RouterManagerHandlers.end ()) - (this->*(it1->second))(results); - else - LogPrint (eLogError, "I2PControl RouterManager unknown request ", it->first); - } - } + + void I2PControlService::RouterManagerHandler (const boost::property_tree::ptree& params, std::ostringstream& results) + { + LogPrint (eLogDebug, "I2PControl RouterManager"); + for (auto it = params.begin (); it != params.end (); it++) + { + if (it != params.begin ()) results << ","; + LogPrint (eLogDebug, it->first); + auto it1 = m_RouterManagerHandlers.find (it->first); + if (it1 != m_RouterManagerHandlers.end ()) + (this->*(it1->second))(results); + else + LogPrint (eLogError, "I2PControl RouterManager unknown request ", it->first); + } + } - void I2PControlService::ShutdownHandler (std::ostringstream& results) - { - LogPrint (eLogInfo, "Shutdown requested"); - InsertParam (results, I2P_CONTROL_ROUTER_MANAGER_SHUTDOWN, ""); - m_ShutdownTimer.expires_from_now (boost::posix_time::seconds(1)); // 1 second to make sure response has been sent - m_ShutdownTimer.async_wait ( - [](const boost::system::error_code& ecode) - { - Daemon.running = 0; - }); - } + void I2PControlService::ShutdownHandler (std::ostringstream& results) + { + LogPrint (eLogInfo, "Shutdown requested"); + InsertParam (results, I2P_CONTROL_ROUTER_MANAGER_SHUTDOWN, ""); + m_ShutdownTimer.expires_from_now (boost::posix_time::seconds(1)); // 1 second to make sure response has been sent + m_ShutdownTimer.async_wait ( + [](const boost::system::error_code& ecode) + { + Daemon.running = 0; + }); + } - void I2PControlService::ShutdownGracefulHandler (std::ostringstream& results) - { - i2p::context.SetAcceptsTunnels (false); - int timeout = i2p::tunnel::tunnels.GetTransitTunnelsExpirationTimeout (); - LogPrint (eLogInfo, "Graceful shutdown requested. Will shutdown after ", timeout, " seconds"); - InsertParam (results, I2P_CONTROL_ROUTER_MANAGER_SHUTDOWN_GRACEFUL, ""); - m_ShutdownTimer.expires_from_now (boost::posix_time::seconds(timeout + 1)); // + 1 second - m_ShutdownTimer.async_wait ( - [](const boost::system::error_code& ecode) - { - Daemon.running = 0; - }); - } + void I2PControlService::ShutdownGracefulHandler (std::ostringstream& results) + { + i2p::context.SetAcceptsTunnels (false); + int timeout = i2p::tunnel::tunnels.GetTransitTunnelsExpirationTimeout (); + LogPrint (eLogInfo, "Graceful shutdown requested. Will shutdown after ", timeout, " seconds"); + InsertParam (results, I2P_CONTROL_ROUTER_MANAGER_SHUTDOWN_GRACEFUL, ""); + m_ShutdownTimer.expires_from_now (boost::posix_time::seconds(timeout + 1)); // + 1 second + m_ShutdownTimer.async_wait ( + [](const boost::system::error_code& ecode) + { + Daemon.running = 0; + }); + } - void I2PControlService::ReseedHandler (std::ostringstream& results) - { - LogPrint (eLogInfo, "Reseed requested"); - InsertParam (results, I2P_CONTROL_ROUTER_MANAGER_SHUTDOWN, ""); - i2p::data::netdb.Reseed (); - } + void I2PControlService::ReseedHandler (std::ostringstream& results) + { + LogPrint (eLogInfo, "Reseed requested"); + InsertParam (results, I2P_CONTROL_ROUTER_MANAGER_SHUTDOWN, ""); + i2p::data::netdb.Reseed (); + } // network setting - void I2PControlService::NetworkSettingHandler (const boost::property_tree::ptree& params, std::ostringstream& results) - { - LogPrint (eLogDebug, "I2PControl NetworkSetting"); - for (auto it = params.begin (); it != params.end (); it++) - { - if (it != params.begin ()) results << ","; - LogPrint (eLogDebug, it->first); - auto it1 = m_NetworkSettingHandlers.find (it->first); - if (it1 != m_NetworkSettingHandlers.end ()) - (this->*(it1->second))(it->second.data (), results); - else - LogPrint (eLogError, "I2PControl NetworkSetting unknown request ", it->first); - } - } + void I2PControlService::NetworkSettingHandler (const boost::property_tree::ptree& params, std::ostringstream& results) + { + LogPrint (eLogDebug, "I2PControl NetworkSetting"); + for (auto it = params.begin (); it != params.end (); it++) + { + if (it != params.begin ()) results << ","; + LogPrint (eLogDebug, it->first); + auto it1 = m_NetworkSettingHandlers.find (it->first); + if (it1 != m_NetworkSettingHandlers.end ()) + (this->*(it1->second))(it->second.data (), results); + else + LogPrint (eLogError, "I2PControl NetworkSetting unknown request ", it->first); + } + } } } diff --git a/I2PControl.h b/I2PControl.h index 0c3e36a1..47ed0d3f 100644 --- a/I2PControl.h +++ b/I2PControl.h @@ -16,132 +16,132 @@ namespace i2p { namespace client { - const size_t I2P_CONTROL_MAX_REQUEST_SIZE = 1024; - typedef std::array I2PControlBuffer; + const size_t I2P_CONTROL_MAX_REQUEST_SIZE = 1024; + typedef std::array I2PControlBuffer; - const char I2P_CONTROL_DEFAULT_PASSWORD[] = "itoopie"; + const char I2P_CONTROL_DEFAULT_PASSWORD[] = "itoopie"; - const char I2P_CONTROL_PROPERTY_ID[] = "id"; - const char I2P_CONTROL_PROPERTY_METHOD[] = "method"; - const char I2P_CONTROL_PROPERTY_PARAMS[] = "params"; - const char I2P_CONTROL_PROPERTY_RESULT[] = "result"; + const char I2P_CONTROL_PROPERTY_ID[] = "id"; + const char I2P_CONTROL_PROPERTY_METHOD[] = "method"; + const char I2P_CONTROL_PROPERTY_PARAMS[] = "params"; + const char I2P_CONTROL_PROPERTY_RESULT[] = "result"; - // methods - const char I2P_CONTROL_METHOD_AUTHENTICATE[] = "Authenticate"; - const char I2P_CONTROL_METHOD_ECHO[] = "Echo"; - const char I2P_CONTROL_METHOD_I2PCONTROL[] = "I2PControl"; - const char I2P_CONTROL_METHOD_ROUTER_INFO[] = "RouterInfo"; - const char I2P_CONTROL_METHOD_ROUTER_MANAGER[] = "RouterManager"; - const char I2P_CONTROL_METHOD_NETWORK_SETTING[] = "NetworkSetting"; + // methods + const char I2P_CONTROL_METHOD_AUTHENTICATE[] = "Authenticate"; + const char I2P_CONTROL_METHOD_ECHO[] = "Echo"; + const char I2P_CONTROL_METHOD_I2PCONTROL[] = "I2PControl"; + const char I2P_CONTROL_METHOD_ROUTER_INFO[] = "RouterInfo"; + const char I2P_CONTROL_METHOD_ROUTER_MANAGER[] = "RouterManager"; + const char I2P_CONTROL_METHOD_NETWORK_SETTING[] = "NetworkSetting"; - // params - const char I2P_CONTROL_PARAM_API[] = "API"; - const char I2P_CONTROL_PARAM_PASSWORD[] = "Password"; - const char I2P_CONTROL_PARAM_TOKEN[] = "Token"; - const char I2P_CONTROL_PARAM_ECHO[] = "Echo"; - const char I2P_CONTROL_PARAM_RESULT[] = "Result"; + // params + const char I2P_CONTROL_PARAM_API[] = "API"; + const char I2P_CONTROL_PARAM_PASSWORD[] = "Password"; + const char I2P_CONTROL_PARAM_TOKEN[] = "Token"; + const char I2P_CONTROL_PARAM_ECHO[] = "Echo"; + const char I2P_CONTROL_PARAM_RESULT[] = "Result"; - // I2PControl - const char I2P_CONTROL_I2PCONTROL_ADDRESS[] = "i2pcontrol.address"; - const char I2P_CONTROL_I2PCONTROL_PASSWORD[] = "i2pcontrol.password"; - const char I2P_CONTROL_I2PCONTROL_PORT[] = "i2pcontrol.port"; + // I2PControl + const char I2P_CONTROL_I2PCONTROL_ADDRESS[] = "i2pcontrol.address"; + const char I2P_CONTROL_I2PCONTROL_PASSWORD[] = "i2pcontrol.password"; + const char I2P_CONTROL_I2PCONTROL_PORT[] = "i2pcontrol.port"; - // RouterInfo requests - const char I2P_CONTROL_ROUTER_INFO_UPTIME[] = "i2p.router.uptime"; - const char I2P_CONTROL_ROUTER_INFO_VERSION[] = "i2p.router.version"; - const char I2P_CONTROL_ROUTER_INFO_STATUS[] = "i2p.router.status"; - const char I2P_CONTROL_ROUTER_INFO_NETDB_KNOWNPEERS[] = "i2p.router.netdb.knownpeers"; - const char I2P_CONTROL_ROUTER_INFO_NETDB_ACTIVEPEERS[] = "i2p.router.netdb.activepeers"; - const char I2P_CONTROL_ROUTER_INFO_NET_STATUS[] = "i2p.router.net.status"; - const char I2P_CONTROL_ROUTER_INFO_TUNNELS_PARTICIPATING[] = "i2p.router.net.tunnels.participating"; - const char I2P_CONTROL_ROUTER_INFO_BW_IB_1S[] = "i2p.router.net.bw.inbound.1s"; - const char I2P_CONTROL_ROUTER_INFO_BW_OB_1S[] = "i2p.router.net.bw.outbound.1s"; + // RouterInfo requests + const char I2P_CONTROL_ROUTER_INFO_UPTIME[] = "i2p.router.uptime"; + const char I2P_CONTROL_ROUTER_INFO_VERSION[] = "i2p.router.version"; + const char I2P_CONTROL_ROUTER_INFO_STATUS[] = "i2p.router.status"; + const char I2P_CONTROL_ROUTER_INFO_NETDB_KNOWNPEERS[] = "i2p.router.netdb.knownpeers"; + const char I2P_CONTROL_ROUTER_INFO_NETDB_ACTIVEPEERS[] = "i2p.router.netdb.activepeers"; + const char I2P_CONTROL_ROUTER_INFO_NET_STATUS[] = "i2p.router.net.status"; + const char I2P_CONTROL_ROUTER_INFO_TUNNELS_PARTICIPATING[] = "i2p.router.net.tunnels.participating"; + const char I2P_CONTROL_ROUTER_INFO_BW_IB_1S[] = "i2p.router.net.bw.inbound.1s"; + const char I2P_CONTROL_ROUTER_INFO_BW_OB_1S[] = "i2p.router.net.bw.outbound.1s"; - // RouterManager requests - const char I2P_CONTROL_ROUTER_MANAGER_SHUTDOWN[] = "Shutdown"; - const char I2P_CONTROL_ROUTER_MANAGER_SHUTDOWN_GRACEFUL[] = "ShutdownGraceful"; - const char I2P_CONTROL_ROUTER_MANAGER_RESEED[] = "Reseed"; + // RouterManager requests + const char I2P_CONTROL_ROUTER_MANAGER_SHUTDOWN[] = "Shutdown"; + const char I2P_CONTROL_ROUTER_MANAGER_SHUTDOWN_GRACEFUL[] = "ShutdownGraceful"; + const char I2P_CONTROL_ROUTER_MANAGER_RESEED[] = "Reseed"; - class I2PControlService - { - public: + class I2PControlService + { + public: - I2PControlService (int port); - ~I2PControlService (); + I2PControlService (int port); + ~I2PControlService (); - void Start (); - void Stop (); + void Start (); + void Stop (); - private: + private: - void Run (); - void Accept (); - void HandleAccept(const boost::system::error_code& ecode, std::shared_ptr socket); - void ReadRequest (std::shared_ptr socket); - void HandleRequestReceived (const boost::system::error_code& ecode, size_t bytes_transferred, - std::shared_ptr socket, std::shared_ptr buf); - void SendResponse (std::shared_ptr socket, - std::shared_ptr buf, std::ostringstream& response, bool isHtml); - void HandleResponseSent (const boost::system::error_code& ecode, std::size_t bytes_transferred, - std::shared_ptr socket, std::shared_ptr buf); + void Run (); + void Accept (); + void HandleAccept(const boost::system::error_code& ecode, std::shared_ptr socket); + void ReadRequest (std::shared_ptr socket); + void HandleRequestReceived (const boost::system::error_code& ecode, size_t bytes_transferred, + std::shared_ptr socket, std::shared_ptr buf); + void SendResponse (std::shared_ptr socket, + std::shared_ptr buf, std::ostringstream& response, bool isHtml); + void HandleResponseSent (const boost::system::error_code& ecode, std::size_t bytes_transferred, + std::shared_ptr socket, std::shared_ptr buf); - private: + private: - void InsertParam (std::ostringstream& ss, const std::string& name, int value) const; - void InsertParam (std::ostringstream& ss, const std::string& name, double value) const; - void InsertParam (std::ostringstream& ss, const std::string& name, const std::string& value) const; + void InsertParam (std::ostringstream& ss, const std::string& name, int value) const; + void InsertParam (std::ostringstream& ss, const std::string& name, double value) const; + void InsertParam (std::ostringstream& ss, const std::string& name, const std::string& value) const; - // methods - typedef void (I2PControlService::*MethodHandler)(const boost::property_tree::ptree& params, std::ostringstream& results); + // methods + typedef void (I2PControlService::*MethodHandler)(const boost::property_tree::ptree& params, std::ostringstream& results); - void AuthenticateHandler (const boost::property_tree::ptree& params, std::ostringstream& results); - void EchoHandler (const boost::property_tree::ptree& params, std::ostringstream& results); - void I2PControlHandler (const boost::property_tree::ptree& params, std::ostringstream& results); - void RouterInfoHandler (const boost::property_tree::ptree& params, std::ostringstream& results); - void RouterManagerHandler (const boost::property_tree::ptree& params, std::ostringstream& results); - void NetworkSettingHandler (const boost::property_tree::ptree& params, std::ostringstream& results); + void AuthenticateHandler (const boost::property_tree::ptree& params, std::ostringstream& results); + void EchoHandler (const boost::property_tree::ptree& params, std::ostringstream& results); + void I2PControlHandler (const boost::property_tree::ptree& params, std::ostringstream& results); + void RouterInfoHandler (const boost::property_tree::ptree& params, std::ostringstream& results); + void RouterManagerHandler (const boost::property_tree::ptree& params, std::ostringstream& results); + void NetworkSettingHandler (const boost::property_tree::ptree& params, std::ostringstream& results); - // I2PControl - typedef void (I2PControlService::*I2PControlRequestHandler)(const std::string& value); + // I2PControl + typedef void (I2PControlService::*I2PControlRequestHandler)(const std::string& value); - // RouterInfo - typedef void (I2PControlService::*RouterInfoRequestHandler)(std::ostringstream& results); - void UptimeHandler (std::ostringstream& results); - void VersionHandler (std::ostringstream& results); - void StatusHandler (std::ostringstream& results); - void NetDbKnownPeersHandler (std::ostringstream& results); - void NetDbActivePeersHandler (std::ostringstream& results); - void NetStatusHandler (std::ostringstream& results); - void TunnelsParticipatingHandler (std::ostringstream& results); - void InboundBandwidth1S (std::ostringstream& results); - void OutboundBandwidth1S (std::ostringstream& results); + // RouterInfo + typedef void (I2PControlService::*RouterInfoRequestHandler)(std::ostringstream& results); + void UptimeHandler (std::ostringstream& results); + void VersionHandler (std::ostringstream& results); + void StatusHandler (std::ostringstream& results); + void NetDbKnownPeersHandler (std::ostringstream& results); + void NetDbActivePeersHandler (std::ostringstream& results); + void NetStatusHandler (std::ostringstream& results); + void TunnelsParticipatingHandler (std::ostringstream& results); + void InboundBandwidth1S (std::ostringstream& results); + void OutboundBandwidth1S (std::ostringstream& results); - // RouterManager - typedef void (I2PControlService::*RouterManagerRequestHandler)(std::ostringstream& results); - void ShutdownHandler (std::ostringstream& results); - void ShutdownGracefulHandler (std::ostringstream& results); - void ReseedHandler (std::ostringstream& results); + // RouterManager + typedef void (I2PControlService::*RouterManagerRequestHandler)(std::ostringstream& results); + void ShutdownHandler (std::ostringstream& results); + void ShutdownGracefulHandler (std::ostringstream& results); + void ReseedHandler (std::ostringstream& results); - // NetworkSetting - typedef void (I2PControlService::*NetworkSettingRequestHandler)(const std::string& value, std::ostringstream& results); + // NetworkSetting + typedef void (I2PControlService::*NetworkSettingRequestHandler)(const std::string& value, std::ostringstream& results); - private: + private: - std::string m_Password; - bool m_IsRunning; - std::thread * m_Thread; + std::string m_Password; + bool m_IsRunning; + std::thread * m_Thread; - boost::asio::io_service m_Service; - boost::asio::ip::tcp::acceptor m_Acceptor; - boost::asio::deadline_timer m_ShutdownTimer; - std::set m_Tokens; - - std::map m_MethodHandlers; - std::map m_I2PControlHandlers; - std::map m_RouterInfoHandlers; - std::map m_RouterManagerHandlers; - std::map m_NetworkSettingHandlers; - }; + boost::asio::io_service m_Service; + boost::asio::ip::tcp::acceptor m_Acceptor; + boost::asio::deadline_timer m_ShutdownTimer; + std::set m_Tokens; + + std::map m_MethodHandlers; + std::map m_I2PControlHandlers; + std::map m_RouterInfoHandlers; + std::map m_RouterManagerHandlers; + std::map m_NetworkSettingHandlers; + }; } } diff --git a/I2PEndian.cpp b/I2PEndian.cpp index b8a041d8..f0465d7a 100644 --- a/I2PEndian.cpp +++ b/I2PEndian.cpp @@ -8,38 +8,38 @@ #ifdef NEEDS_LOCAL_ENDIAN uint16_t htobe16(uint16_t int16) { - BigEndian u16(int16); - return u16.raw_value; + BigEndian u16(int16); + return u16.raw_value; } uint32_t htobe32(uint32_t int32) { - BigEndian u32(int32); - return u32.raw_value; + BigEndian u32(int32); + return u32.raw_value; } uint64_t htobe64(uint64_t int64) { - BigEndian u64(int64); - return u64.raw_value; + BigEndian u64(int64); + return u64.raw_value; } uint16_t be16toh(uint16_t big16) { - LittleEndian u16(big16); - return u16.raw_value; + LittleEndian u16(big16); + return u16.raw_value; } uint32_t be32toh(uint32_t big32) { - LittleEndian u32(big32); - return u32.raw_value; + LittleEndian u32(big32); + return u32.raw_value; } uint64_t be64toh(uint64_t big64) { - LittleEndian u64(big64); - return u64.raw_value; + LittleEndian u64(big64); + return u64.raw_value; } #endif @@ -48,36 +48,36 @@ uint64_t be64toh(uint64_t big64) uint16_t htobe16(uint16_t int16) { - return htons(int16); + return htons(int16); } uint32_t htobe32(uint32_t int32) { - return htonl(int32); + return htonl(int32); } uint64_t htobe64(uint64_t int64) { - // http://msdn.microsoft.com/en-us/library/windows/desktop/jj710199%28v=vs.85%29.aspx - //return htonll(int64); - return 0; + // http://msdn.microsoft.com/en-us/library/windows/desktop/jj710199%28v=vs.85%29.aspx + //return htonll(int64); + return 0; } uint16_t be16toh(uint16_t big16) { - return ntohs(big16); + return ntohs(big16); } uint32_t be32toh(uint32_t big32) { - return ntohl(big32); + return ntohl(big32); } uint64_t be64toh(uint64_t big64) { - // http://msdn.microsoft.com/en-us/library/windows/desktop/jj710199%28v=vs.85%29.aspx - //return ntohll(big64); - return 0; + // http://msdn.microsoft.com/en-us/library/windows/desktop/jj710199%28v=vs.85%29.aspx + //return ntohll(big64); + return 0; } */ \ No newline at end of file diff --git a/I2PEndian.h b/I2PEndian.h index 687dbc63..9b42836d 100644 --- a/I2PEndian.h +++ b/I2PEndian.h @@ -49,68 +49,68 @@ uint64_t be64toh(uint64_t big64); inline uint16_t buf16toh(const void *buf) { - uint16_t b16; - memcpy(&b16, buf, sizeof(uint16_t)); - return b16; + uint16_t b16; + memcpy(&b16, buf, sizeof(uint16_t)); + return b16; } inline uint32_t buf32toh(const void *buf) { - uint32_t b32; - memcpy(&b32, buf, sizeof(uint32_t)); - return b32; + uint32_t b32; + memcpy(&b32, buf, sizeof(uint32_t)); + return b32; } inline uint64_t buf64toh(const void *buf) { - uint64_t b64; - memcpy(&b64, buf, sizeof(uint64_t)); - return b64; + uint64_t b64; + memcpy(&b64, buf, sizeof(uint64_t)); + return b64; } inline uint16_t bufbe16toh(const void *buf) { - return be16toh(buf16toh(buf)); + return be16toh(buf16toh(buf)); } inline uint32_t bufbe32toh(const void *buf) { - return be32toh(buf32toh(buf)); + return be32toh(buf32toh(buf)); } inline uint64_t bufbe64toh(const void *buf) { - return be64toh(buf64toh(buf)); + return be64toh(buf64toh(buf)); } inline void htobuf16(void *buf, uint16_t b16) { - memcpy(buf, &b16, sizeof(uint16_t)); + memcpy(buf, &b16, sizeof(uint16_t)); } inline void htobuf32(void *buf, uint32_t b32) { - memcpy(buf, &b32, sizeof(uint32_t)); + memcpy(buf, &b32, sizeof(uint32_t)); } inline void htobuf64(void *buf, uint64_t b64) { - memcpy(buf, &b64, sizeof(uint64_t)); + memcpy(buf, &b64, sizeof(uint64_t)); } inline void htobe16buf(void *buf, uint16_t big16) { - htobuf16(buf, htobe16(big16)); + htobuf16(buf, htobe16(big16)); } inline void htobe32buf(void *buf, uint32_t big32) { - htobuf32(buf, htobe32(big32)); + htobuf32(buf, htobe32(big32)); } inline void htobe64buf(void *buf, uint64_t big64) { - htobuf64(buf, htobe64(big64)); + htobuf64(buf, htobe64(big64)); } diff --git a/I2PService.cpp b/I2PService.cpp index 0715a39f..eaa7dc52 100644 --- a/I2PService.cpp +++ b/I2PService.cpp @@ -8,72 +8,72 @@ namespace i2p { namespace client { - static const i2p::data::SigningKeyType I2P_SERVICE_DEFAULT_KEY_TYPE = i2p::data::SIGNING_KEY_TYPE_ECDSA_SHA256_P256; + static const i2p::data::SigningKeyType I2P_SERVICE_DEFAULT_KEY_TYPE = i2p::data::SIGNING_KEY_TYPE_ECDSA_SHA256_P256; - I2PService::I2PService (std::shared_ptr localDestination): - m_LocalDestination (localDestination ? localDestination : - i2p::client::context.CreateNewLocalDestination (false, I2P_SERVICE_DEFAULT_KEY_TYPE)) - { - } - - I2PService::I2PService (i2p::data::SigningKeyType kt): - m_LocalDestination (i2p::client::context.CreateNewLocalDestination (false, kt)) - { - } - - void I2PService::CreateStream (StreamRequestComplete streamRequestComplete, const std::string& dest, int port) { - assert(streamRequestComplete); - i2p::data::IdentHash identHash; - if (i2p::client::context.GetAddressBook ().GetIdentHash (dest, identHash)) - m_LocalDestination->CreateStream (streamRequestComplete, identHash, port); - else - { - LogPrint (eLogWarning, "Remote destination ", dest, " not found"); - streamRequestComplete (nullptr); - } - } + I2PService::I2PService (std::shared_ptr localDestination): + m_LocalDestination (localDestination ? localDestination : + i2p::client::context.CreateNewLocalDestination (false, I2P_SERVICE_DEFAULT_KEY_TYPE)) + { + } + + I2PService::I2PService (i2p::data::SigningKeyType kt): + m_LocalDestination (i2p::client::context.CreateNewLocalDestination (false, kt)) + { + } + + void I2PService::CreateStream (StreamRequestComplete streamRequestComplete, const std::string& dest, int port) { + assert(streamRequestComplete); + i2p::data::IdentHash identHash; + if (i2p::client::context.GetAddressBook ().GetIdentHash (dest, identHash)) + m_LocalDestination->CreateStream (streamRequestComplete, identHash, port); + else + { + LogPrint (eLogWarning, "Remote destination ", dest, " not found"); + streamRequestComplete (nullptr); + } + } - void TCPIPAcceptor::Start () - { - m_Acceptor.listen (); - Accept (); - } + void TCPIPAcceptor::Start () + { + m_Acceptor.listen (); + Accept (); + } - void TCPIPAcceptor::Stop () - { - m_Acceptor.close(); - m_Timer.cancel (); - ClearHandlers(); - } + void TCPIPAcceptor::Stop () + { + m_Acceptor.close(); + m_Timer.cancel (); + ClearHandlers(); + } - void TCPIPAcceptor::Accept () - { - auto newSocket = std::make_shared (GetService ()); - m_Acceptor.async_accept (*newSocket, std::bind (&TCPIPAcceptor::HandleAccept, this, - std::placeholders::_1, newSocket)); - } + void TCPIPAcceptor::Accept () + { + auto newSocket = std::make_shared (GetService ()); + m_Acceptor.async_accept (*newSocket, std::bind (&TCPIPAcceptor::HandleAccept, this, + std::placeholders::_1, newSocket)); + } - void TCPIPAcceptor::HandleAccept (const boost::system::error_code& ecode, std::shared_ptr socket) - { - if (!ecode) - { - LogPrint(eLogDebug,"--- ",GetName()," accepted"); - auto handler = CreateHandler(socket); - if (handler) - { - AddHandler(handler); - handler->Handle(); - } - else - socket->close(); - Accept(); - } - else - { - if (ecode != boost::asio::error::operation_aborted) - LogPrint (eLogError,"--- ",GetName()," Closing socket on accept because: ", ecode.message ()); - } - } + void TCPIPAcceptor::HandleAccept (const boost::system::error_code& ecode, std::shared_ptr socket) + { + if (!ecode) + { + LogPrint(eLogDebug,"--- ",GetName()," accepted"); + auto handler = CreateHandler(socket); + if (handler) + { + AddHandler(handler); + handler->Handle(); + } + else + socket->close(); + Accept(); + } + else + { + if (ecode != boost::asio::error::operation_aborted) + LogPrint (eLogError,"--- ",GetName()," Closing socket on accept because: ", ecode.message ()); + } + } } } diff --git a/I2PService.h b/I2PService.h index afac4ea4..78fad26b 100644 --- a/I2PService.h +++ b/I2PService.h @@ -13,96 +13,96 @@ namespace i2p { namespace client { - class I2PServiceHandler; - class I2PService - { - public: - I2PService (std::shared_ptr localDestination = nullptr); - I2PService (i2p::data::SigningKeyType kt); - virtual ~I2PService () { ClearHandlers (); } + class I2PServiceHandler; + class I2PService + { + public: + I2PService (std::shared_ptr localDestination = nullptr); + I2PService (i2p::data::SigningKeyType kt); + virtual ~I2PService () { ClearHandlers (); } - inline void AddHandler (std::shared_ptr conn) - { - std::unique_lock l(m_HandlersMutex); - m_Handlers.insert(conn); - } - inline void RemoveHandler (std::shared_ptr conn) - { - std::unique_lock l(m_HandlersMutex); - m_Handlers.erase(conn); - } - inline void ClearHandlers () - { - std::unique_lock l(m_HandlersMutex); - m_Handlers.clear(); - } + inline void AddHandler (std::shared_ptr conn) + { + std::unique_lock l(m_HandlersMutex); + m_Handlers.insert(conn); + } + inline void RemoveHandler (std::shared_ptr conn) + { + std::unique_lock l(m_HandlersMutex); + m_Handlers.erase(conn); + } + inline void ClearHandlers () + { + std::unique_lock l(m_HandlersMutex); + m_Handlers.clear(); + } - inline std::shared_ptr GetLocalDestination () { return m_LocalDestination; } - inline void SetLocalDestination (std::shared_ptr dest) { m_LocalDestination = dest; } - void CreateStream (StreamRequestComplete streamRequestComplete, const std::string& dest, int port = 0); + inline std::shared_ptr GetLocalDestination () { return m_LocalDestination; } + inline void SetLocalDestination (std::shared_ptr dest) { m_LocalDestination = dest; } + void CreateStream (StreamRequestComplete streamRequestComplete, const std::string& dest, int port = 0); - inline boost::asio::io_service& GetService () { return m_LocalDestination->GetService (); } + inline boost::asio::io_service& GetService () { return m_LocalDestination->GetService (); } - virtual void Start () = 0; - virtual void Stop () = 0; + virtual void Start () = 0; + virtual void Stop () = 0; - virtual const char* GetName() { return "Generic I2P Service"; } - private: + virtual const char* GetName() { return "Generic I2P Service"; } + private: - std::shared_ptr m_LocalDestination; - std::unordered_set > m_Handlers; - std::mutex m_HandlersMutex; - }; + std::shared_ptr m_LocalDestination; + std::unordered_set > m_Handlers; + std::mutex m_HandlersMutex; + }; - /*Simple interface for I2PHandlers, allows detection of finalization amongst other things */ - class I2PServiceHandler - { - public: - I2PServiceHandler(I2PService * parent) : m_Service(parent), m_Dead(false) { } - virtual ~I2PServiceHandler() { } - //If you override this make sure you call it from the children - virtual void Handle() {}; //Start handling the socket - protected: - // Call when terminating or handing over to avoid race conditions - inline bool Kill () { return m_Dead.exchange(true); } - // Call to know if the handler is dead - inline bool Dead () { return m_Dead; } - // Call when done to clean up (make sure Kill is called first) - inline void Done (std::shared_ptr me) { if(m_Service) m_Service->RemoveHandler(me); } - // Call to talk with the owner - inline I2PService * GetOwner() { return m_Service; } - private: - I2PService *m_Service; - std::atomic m_Dead; //To avoid cleaning up multiple times - }; + /*Simple interface for I2PHandlers, allows detection of finalization amongst other things */ + class I2PServiceHandler + { + public: + I2PServiceHandler(I2PService * parent) : m_Service(parent), m_Dead(false) { } + virtual ~I2PServiceHandler() { } + //If you override this make sure you call it from the children + virtual void Handle() {}; //Start handling the socket + protected: + // Call when terminating or handing over to avoid race conditions + inline bool Kill () { return m_Dead.exchange(true); } + // Call to know if the handler is dead + inline bool Dead () { return m_Dead; } + // Call when done to clean up (make sure Kill is called first) + inline void Done (std::shared_ptr me) { if(m_Service) m_Service->RemoveHandler(me); } + // Call to talk with the owner + inline I2PService * GetOwner() { return m_Service; } + private: + I2PService *m_Service; + std::atomic m_Dead; //To avoid cleaning up multiple times + }; - /* TODO: support IPv6 too */ - //This is a service that listens for connections on the IP network and interacts with I2P - class TCPIPAcceptor: public I2PService - { - public: - TCPIPAcceptor (int port, std::shared_ptr localDestination = nullptr) : - I2PService(localDestination), + /* TODO: support IPv6 too */ + //This is a service that listens for connections on the IP network and interacts with I2P + class TCPIPAcceptor: public I2PService + { + public: + TCPIPAcceptor (int port, std::shared_ptr localDestination = nullptr) : + I2PService(localDestination), m_Acceptor (GetService (), boost::asio::ip::tcp::endpoint (boost::asio::ip::tcp::v4 (), port)), - m_Timer (GetService ()) {} - TCPIPAcceptor (int port, i2p::data::SigningKeyType kt) : - I2PService(kt), - m_Acceptor (GetService (), boost::asio::ip::tcp::endpoint (boost::asio::ip::tcp::v4 (), port)), - m_Timer (GetService ()) {} - virtual ~TCPIPAcceptor () { TCPIPAcceptor::Stop(); } - //If you override this make sure you call it from the children - void Start (); - //If you override this make sure you call it from the children - void Stop (); - protected: - virtual std::shared_ptr CreateHandler(std::shared_ptr socket) = 0; - virtual const char* GetName() { return "Generic TCP/IP accepting daemon"; } - private: - void Accept(); - void HandleAccept(const boost::system::error_code& ecode, std::shared_ptr socket); - boost::asio::ip::tcp::acceptor m_Acceptor; - boost::asio::deadline_timer m_Timer; - }; + m_Timer (GetService ()) {} + TCPIPAcceptor (int port, i2p::data::SigningKeyType kt) : + I2PService(kt), + m_Acceptor (GetService (), boost::asio::ip::tcp::endpoint (boost::asio::ip::tcp::v4 (), port)), + m_Timer (GetService ()) {} + virtual ~TCPIPAcceptor () { TCPIPAcceptor::Stop(); } + //If you override this make sure you call it from the children + void Start (); + //If you override this make sure you call it from the children + void Stop (); + protected: + virtual std::shared_ptr CreateHandler(std::shared_ptr socket) = 0; + virtual const char* GetName() { return "Generic TCP/IP accepting daemon"; } + private: + void Accept(); + void HandleAccept(const boost::system::error_code& ecode, std::shared_ptr socket); + boost::asio::ip::tcp::acceptor m_Acceptor; + boost::asio::deadline_timer m_Timer; + }; } } diff --git a/I2PTunnel.cpp b/I2PTunnel.cpp index a33a536d..e0e84d6a 100644 --- a/I2PTunnel.cpp +++ b/I2PTunnel.cpp @@ -9,394 +9,394 @@ namespace i2p { namespace client { - I2PTunnelConnection::I2PTunnelConnection (I2PService * owner, std::shared_ptr socket, - std::shared_ptr leaseSet, int port): - I2PServiceHandler(owner), m_Socket (socket), m_RemoteEndpoint (socket->remote_endpoint ()), - m_IsQuiet (true) - { - m_Stream = GetOwner()->GetLocalDestination ()->CreateStream (leaseSet, port); - } + I2PTunnelConnection::I2PTunnelConnection (I2PService * owner, std::shared_ptr socket, + std::shared_ptr leaseSet, int port): + I2PServiceHandler(owner), m_Socket (socket), m_RemoteEndpoint (socket->remote_endpoint ()), + m_IsQuiet (true) + { + m_Stream = GetOwner()->GetLocalDestination ()->CreateStream (leaseSet, port); + } - I2PTunnelConnection::I2PTunnelConnection (I2PService * owner, - std::shared_ptr socket, std::shared_ptr stream): - I2PServiceHandler(owner), m_Socket (socket), m_Stream (stream), - m_RemoteEndpoint (socket->remote_endpoint ()), m_IsQuiet (true) - { - } + I2PTunnelConnection::I2PTunnelConnection (I2PService * owner, + std::shared_ptr socket, std::shared_ptr stream): + I2PServiceHandler(owner), m_Socket (socket), m_Stream (stream), + m_RemoteEndpoint (socket->remote_endpoint ()), m_IsQuiet (true) + { + } - I2PTunnelConnection::I2PTunnelConnection (I2PService * owner, std::shared_ptr stream, - std::shared_ptr socket, const boost::asio::ip::tcp::endpoint& target, bool quiet): - I2PServiceHandler(owner), m_Socket (socket), m_Stream (stream), - m_RemoteEndpoint (target), m_IsQuiet (quiet) - { - } + I2PTunnelConnection::I2PTunnelConnection (I2PService * owner, std::shared_ptr stream, + std::shared_ptr socket, const boost::asio::ip::tcp::endpoint& target, bool quiet): + I2PServiceHandler(owner), m_Socket (socket), m_Stream (stream), + m_RemoteEndpoint (target), m_IsQuiet (quiet) + { + } - I2PTunnelConnection::~I2PTunnelConnection () - { - } + I2PTunnelConnection::~I2PTunnelConnection () + { + } - void I2PTunnelConnection::I2PConnect (const uint8_t * msg, size_t len) - { - if (m_Stream) - { - if (msg) - m_Stream->Send (msg, len); // connect and send - else - m_Stream->Send (m_Buffer, 0); // connect - } - StreamReceive (); - Receive (); - } - - void I2PTunnelConnection::Connect () - { - if (m_Socket) - m_Socket->async_connect (m_RemoteEndpoint, std::bind (&I2PTunnelConnection::HandleConnect, - shared_from_this (), std::placeholders::_1)); - } - - void I2PTunnelConnection::Terminate () - { - if (Kill()) return; - if (m_Stream) - { - m_Stream->Close (); - m_Stream.reset (); - } - m_Socket->close (); - Done(shared_from_this ()); - } + void I2PTunnelConnection::I2PConnect (const uint8_t * msg, size_t len) + { + if (m_Stream) + { + if (msg) + m_Stream->Send (msg, len); // connect and send + else + m_Stream->Send (m_Buffer, 0); // connect + } + StreamReceive (); + Receive (); + } + + void I2PTunnelConnection::Connect () + { + if (m_Socket) + m_Socket->async_connect (m_RemoteEndpoint, std::bind (&I2PTunnelConnection::HandleConnect, + shared_from_this (), std::placeholders::_1)); + } + + void I2PTunnelConnection::Terminate () + { + if (Kill()) return; + if (m_Stream) + { + m_Stream->Close (); + m_Stream.reset (); + } + m_Socket->close (); + Done(shared_from_this ()); + } - void I2PTunnelConnection::Receive () - { - m_Socket->async_read_some (boost::asio::buffer(m_Buffer, I2P_TUNNEL_CONNECTION_BUFFER_SIZE), - std::bind(&I2PTunnelConnection::HandleReceived, shared_from_this (), - std::placeholders::_1, std::placeholders::_2)); - } - - void I2PTunnelConnection::HandleReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred) - { - if (ecode) - { - LogPrint ("I2PTunnel read error: ", ecode.message ()); - if (ecode != boost::asio::error::operation_aborted) - Terminate (); - } - else - { - if (m_Stream) - { - auto s = shared_from_this (); - m_Stream->AsyncSend (m_Buffer, bytes_transferred, - [s](const boost::system::error_code& ecode) - { - if (!ecode) - s->Receive (); - else - s->Terminate (); - }); - } - } - } + void I2PTunnelConnection::Receive () + { + m_Socket->async_read_some (boost::asio::buffer(m_Buffer, I2P_TUNNEL_CONNECTION_BUFFER_SIZE), + std::bind(&I2PTunnelConnection::HandleReceived, shared_from_this (), + std::placeholders::_1, std::placeholders::_2)); + } + + void I2PTunnelConnection::HandleReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred) + { + if (ecode) + { + LogPrint ("I2PTunnel read error: ", ecode.message ()); + if (ecode != boost::asio::error::operation_aborted) + Terminate (); + } + else + { + if (m_Stream) + { + auto s = shared_from_this (); + m_Stream->AsyncSend (m_Buffer, bytes_transferred, + [s](const boost::system::error_code& ecode) + { + if (!ecode) + s->Receive (); + else + s->Terminate (); + }); + } + } + } - void I2PTunnelConnection::HandleWrite (const boost::system::error_code& ecode) - { - if (ecode) - { - LogPrint ("I2PTunnel write error: ", ecode.message ()); - if (ecode != boost::asio::error::operation_aborted) - Terminate (); - } - else - StreamReceive (); - } + void I2PTunnelConnection::HandleWrite (const boost::system::error_code& ecode) + { + if (ecode) + { + LogPrint ("I2PTunnel write error: ", ecode.message ()); + if (ecode != boost::asio::error::operation_aborted) + Terminate (); + } + else + StreamReceive (); + } - void I2PTunnelConnection::StreamReceive () - { - if (m_Stream) - m_Stream->AsyncReceive (boost::asio::buffer (m_StreamBuffer, I2P_TUNNEL_CONNECTION_BUFFER_SIZE), - std::bind (&I2PTunnelConnection::HandleStreamReceive, shared_from_this (), - std::placeholders::_1, std::placeholders::_2), - I2P_TUNNEL_CONNECTION_MAX_IDLE); - } + void I2PTunnelConnection::StreamReceive () + { + if (m_Stream) + m_Stream->AsyncReceive (boost::asio::buffer (m_StreamBuffer, I2P_TUNNEL_CONNECTION_BUFFER_SIZE), + std::bind (&I2PTunnelConnection::HandleStreamReceive, shared_from_this (), + std::placeholders::_1, std::placeholders::_2), + I2P_TUNNEL_CONNECTION_MAX_IDLE); + } - void I2PTunnelConnection::HandleStreamReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred) - { - if (ecode) - { - LogPrint ("I2PTunnel stream read error: ", ecode.message ()); - if (ecode != boost::asio::error::operation_aborted) - Terminate (); - } - else - Write (m_StreamBuffer, bytes_transferred); - } + void I2PTunnelConnection::HandleStreamReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred) + { + if (ecode) + { + LogPrint ("I2PTunnel stream read error: ", ecode.message ()); + if (ecode != boost::asio::error::operation_aborted) + Terminate (); + } + else + Write (m_StreamBuffer, bytes_transferred); + } - void I2PTunnelConnection::Write (const uint8_t * buf, size_t len) - { - m_Socket->async_send (boost::asio::buffer (buf, len), - std::bind (&I2PTunnelConnection::HandleWrite, shared_from_this (), std::placeholders::_1)); - } + void I2PTunnelConnection::Write (const uint8_t * buf, size_t len) + { + m_Socket->async_send (boost::asio::buffer (buf, len), + std::bind (&I2PTunnelConnection::HandleWrite, shared_from_this (), std::placeholders::_1)); + } - void I2PTunnelConnection::HandleConnect (const boost::system::error_code& ecode) - { - if (ecode) - { - LogPrint ("I2PTunnel connect error: ", ecode.message ()); - Terminate (); - } - else - { - LogPrint ("I2PTunnel connected"); - if (m_IsQuiet) - StreamReceive (); - else - { - // send destination first like received from I2P - std::string dest = m_Stream->GetRemoteIdentity ().ToBase64 (); - dest += "\n"; - memcpy (m_StreamBuffer, dest.c_str (), dest.size ()); - HandleStreamReceive (boost::system::error_code (), dest.size ()); - } - Receive (); - } - } + void I2PTunnelConnection::HandleConnect (const boost::system::error_code& ecode) + { + if (ecode) + { + LogPrint ("I2PTunnel connect error: ", ecode.message ()); + Terminate (); + } + else + { + LogPrint ("I2PTunnel connected"); + if (m_IsQuiet) + StreamReceive (); + else + { + // send destination first like received from I2P + std::string dest = m_Stream->GetRemoteIdentity ().ToBase64 (); + dest += "\n"; + memcpy (m_StreamBuffer, dest.c_str (), dest.size ()); + HandleStreamReceive (boost::system::error_code (), dest.size ()); + } + Receive (); + } + } - I2PTunnelConnectionHTTP::I2PTunnelConnectionHTTP (I2PService * owner, std::shared_ptr stream, - std::shared_ptr socket, - const boost::asio::ip::tcp::endpoint& target, const std::string& host): - I2PTunnelConnection (owner, stream, socket, target), m_Host (host), m_HeaderSent (false) - { - } + I2PTunnelConnectionHTTP::I2PTunnelConnectionHTTP (I2PService * owner, std::shared_ptr stream, + std::shared_ptr socket, + const boost::asio::ip::tcp::endpoint& target, const std::string& host): + I2PTunnelConnection (owner, stream, socket, target), m_Host (host), m_HeaderSent (false) + { + } - void I2PTunnelConnectionHTTP::Write (const uint8_t * buf, size_t len) - { - if (m_HeaderSent) - I2PTunnelConnection::Write (buf, len); - else - { - m_InHeader.clear (); - m_InHeader.write ((const char *)buf, len); - std::string line; - bool endOfHeader = false; - while (!endOfHeader) - { - std::getline(m_InHeader, line); - if (!m_InHeader.fail ()) - { - if (line.find ("Host:") != std::string::npos) - m_OutHeader << "Host: " << m_Host << "\r\n"; - else - m_OutHeader << line << "\n"; - if (line == "\r") endOfHeader = true; - } - else - break; - } + void I2PTunnelConnectionHTTP::Write (const uint8_t * buf, size_t len) + { + if (m_HeaderSent) + I2PTunnelConnection::Write (buf, len); + else + { + m_InHeader.clear (); + m_InHeader.write ((const char *)buf, len); + std::string line; + bool endOfHeader = false; + while (!endOfHeader) + { + std::getline(m_InHeader, line); + if (!m_InHeader.fail ()) + { + if (line.find ("Host:") != std::string::npos) + m_OutHeader << "Host: " << m_Host << "\r\n"; + else + m_OutHeader << line << "\n"; + if (line == "\r") endOfHeader = true; + } + else + break; + } - if (endOfHeader) - { - m_OutHeader << m_InHeader.str (); // data right after header - m_HeaderSent = true; - I2PTunnelConnection::Write ((uint8_t *)m_OutHeader.str ().c_str (), m_OutHeader.str ().length ()); - } - } - } + if (endOfHeader) + { + m_OutHeader << m_InHeader.str (); // data right after header + m_HeaderSent = true; + I2PTunnelConnection::Write ((uint8_t *)m_OutHeader.str ().c_str (), m_OutHeader.str ().length ()); + } + } + } - /* This handler tries to stablish a connection with the desired server and dies if it fails to do so */ - class I2PClientTunnelHandler: public I2PServiceHandler, public std::enable_shared_from_this - { - public: - I2PClientTunnelHandler (I2PClientTunnel * parent, i2p::data::IdentHash destination, - int destinationPort, std::shared_ptr socket): - I2PServiceHandler(parent), m_DestinationIdentHash(destination), - m_DestinationPort (destinationPort), m_Socket(socket) {}; - void Handle(); - void Terminate(); - private: - void HandleStreamRequestComplete (std::shared_ptr stream); - i2p::data::IdentHash m_DestinationIdentHash; - int m_DestinationPort; - std::shared_ptr m_Socket; - }; + /* This handler tries to stablish a connection with the desired server and dies if it fails to do so */ + class I2PClientTunnelHandler: public I2PServiceHandler, public std::enable_shared_from_this + { + public: + I2PClientTunnelHandler (I2PClientTunnel * parent, i2p::data::IdentHash destination, + int destinationPort, std::shared_ptr socket): + I2PServiceHandler(parent), m_DestinationIdentHash(destination), + m_DestinationPort (destinationPort), m_Socket(socket) {}; + void Handle(); + void Terminate(); + private: + void HandleStreamRequestComplete (std::shared_ptr stream); + i2p::data::IdentHash m_DestinationIdentHash; + int m_DestinationPort; + std::shared_ptr m_Socket; + }; - void I2PClientTunnelHandler::Handle() - { - GetOwner()->GetLocalDestination ()->CreateStream ( - std::bind (&I2PClientTunnelHandler::HandleStreamRequestComplete, shared_from_this(), std::placeholders::_1), - m_DestinationIdentHash, m_DestinationPort); - } + void I2PClientTunnelHandler::Handle() + { + GetOwner()->GetLocalDestination ()->CreateStream ( + std::bind (&I2PClientTunnelHandler::HandleStreamRequestComplete, shared_from_this(), std::placeholders::_1), + m_DestinationIdentHash, m_DestinationPort); + } - void I2PClientTunnelHandler::HandleStreamRequestComplete (std::shared_ptr stream) - { - if (stream) - { - if (Kill()) return; - LogPrint (eLogInfo,"New I2PTunnel connection"); - auto connection = std::make_shared(GetOwner(), m_Socket, stream); - GetOwner()->AddHandler (connection); - connection->I2PConnect (); - Done(shared_from_this()); - } - else - { - LogPrint (eLogError,"I2P Client Tunnel Issue when creating the stream, check the previous warnings for more info."); - Terminate(); - } - } + void I2PClientTunnelHandler::HandleStreamRequestComplete (std::shared_ptr stream) + { + if (stream) + { + if (Kill()) return; + LogPrint (eLogInfo,"New I2PTunnel connection"); + auto connection = std::make_shared(GetOwner(), m_Socket, stream); + GetOwner()->AddHandler (connection); + connection->I2PConnect (); + Done(shared_from_this()); + } + else + { + LogPrint (eLogError,"I2P Client Tunnel Issue when creating the stream, check the previous warnings for more info."); + Terminate(); + } + } - void I2PClientTunnelHandler::Terminate() - { - if (Kill()) return; - if (m_Socket) - { - m_Socket->close(); - m_Socket = nullptr; - } - Done(shared_from_this()); - } + void I2PClientTunnelHandler::Terminate() + { + if (Kill()) return; + if (m_Socket) + { + m_Socket->close(); + m_Socket = nullptr; + } + Done(shared_from_this()); + } - I2PClientTunnel::I2PClientTunnel (const std::string& destination, int port, std::shared_ptr localDestination, int destinationPort): - TCPIPAcceptor (port,localDestination), m_Destination (destination), m_DestinationIdentHash (nullptr), m_DestinationPort (destinationPort) - {} + I2PClientTunnel::I2PClientTunnel (const std::string& destination, int port, std::shared_ptr localDestination, int destinationPort): + TCPIPAcceptor (port,localDestination), m_Destination (destination), m_DestinationIdentHash (nullptr), m_DestinationPort (destinationPort) + {} - void I2PClientTunnel::Start () - { - TCPIPAcceptor::Start (); - GetIdentHash(); - } + void I2PClientTunnel::Start () + { + TCPIPAcceptor::Start (); + GetIdentHash(); + } - void I2PClientTunnel::Stop () - { - TCPIPAcceptor::Stop(); - auto *originalIdentHash = m_DestinationIdentHash; - m_DestinationIdentHash = nullptr; - delete originalIdentHash; - } + void I2PClientTunnel::Stop () + { + TCPIPAcceptor::Stop(); + auto *originalIdentHash = m_DestinationIdentHash; + m_DestinationIdentHash = nullptr; + delete originalIdentHash; + } - /* HACK: maybe we should create a caching IdentHash provider in AddressBook */ - const i2p::data::IdentHash * I2PClientTunnel::GetIdentHash () - { - if (!m_DestinationIdentHash) - { - i2p::data::IdentHash identHash; - if (i2p::client::context.GetAddressBook ().GetIdentHash (m_Destination, identHash)) - m_DestinationIdentHash = new i2p::data::IdentHash (identHash); - else - LogPrint (eLogWarning,"Remote destination ", m_Destination, " not found"); - } - return m_DestinationIdentHash; - } + /* HACK: maybe we should create a caching IdentHash provider in AddressBook */ + const i2p::data::IdentHash * I2PClientTunnel::GetIdentHash () + { + if (!m_DestinationIdentHash) + { + i2p::data::IdentHash identHash; + if (i2p::client::context.GetAddressBook ().GetIdentHash (m_Destination, identHash)) + m_DestinationIdentHash = new i2p::data::IdentHash (identHash); + else + LogPrint (eLogWarning,"Remote destination ", m_Destination, " not found"); + } + return m_DestinationIdentHash; + } - std::shared_ptr I2PClientTunnel::CreateHandler(std::shared_ptr socket) - { - const i2p::data::IdentHash *identHash = GetIdentHash(); - if (identHash) - return std::make_shared(this, *identHash, m_DestinationPort, socket); - else - return nullptr; - } + std::shared_ptr I2PClientTunnel::CreateHandler(std::shared_ptr socket) + { + const i2p::data::IdentHash *identHash = GetIdentHash(); + if (identHash) + return std::make_shared(this, *identHash, m_DestinationPort, socket); + else + return nullptr; + } - I2PServerTunnel::I2PServerTunnel (const std::string& address, int port, - std::shared_ptr localDestination, int inport): - I2PService (localDestination), m_Address (address), m_Port (port), m_IsAccessList (false) - { - m_PortDestination = localDestination->CreateStreamingDestination (inport > 0 ? inport : port); - } - - void I2PServerTunnel::Start () - { - m_Endpoint.port (m_Port); - boost::system::error_code ec; - auto addr = boost::asio::ip::address::from_string (m_Address, ec); - if (!ec) - { - m_Endpoint.address (addr); - Accept (); - } - else - { - auto resolver = std::make_shared(GetService ()); - resolver->async_resolve (boost::asio::ip::tcp::resolver::query (m_Address, ""), - std::bind (&I2PServerTunnel::HandleResolve, this, - std::placeholders::_1, std::placeholders::_2, resolver)); - } - } + I2PServerTunnel::I2PServerTunnel (const std::string& address, int port, + std::shared_ptr localDestination, int inport): + I2PService (localDestination), m_Address (address), m_Port (port), m_IsAccessList (false) + { + m_PortDestination = localDestination->CreateStreamingDestination (inport > 0 ? inport : port); + } + + void I2PServerTunnel::Start () + { + m_Endpoint.port (m_Port); + boost::system::error_code ec; + auto addr = boost::asio::ip::address::from_string (m_Address, ec); + if (!ec) + { + m_Endpoint.address (addr); + Accept (); + } + else + { + auto resolver = std::make_shared(GetService ()); + resolver->async_resolve (boost::asio::ip::tcp::resolver::query (m_Address, ""), + std::bind (&I2PServerTunnel::HandleResolve, this, + std::placeholders::_1, std::placeholders::_2, resolver)); + } + } - void I2PServerTunnel::Stop () - { - ClearHandlers (); - } + void I2PServerTunnel::Stop () + { + ClearHandlers (); + } - void I2PServerTunnel::HandleResolve (const boost::system::error_code& ecode, boost::asio::ip::tcp::resolver::iterator it, - std::shared_ptr resolver) - { - if (!ecode) - { - auto addr = (*it).endpoint ().address (); - LogPrint (eLogInfo, "server tunnel ", (*it).host_name (), " has been resolved to ", addr); - m_Endpoint.address (addr); - Accept (); - } - else - LogPrint (eLogError, "Unable to resolve server tunnel address: ", ecode.message ()); - } + void I2PServerTunnel::HandleResolve (const boost::system::error_code& ecode, boost::asio::ip::tcp::resolver::iterator it, + std::shared_ptr resolver) + { + if (!ecode) + { + auto addr = (*it).endpoint ().address (); + LogPrint (eLogInfo, "server tunnel ", (*it).host_name (), " has been resolved to ", addr); + m_Endpoint.address (addr); + Accept (); + } + else + LogPrint (eLogError, "Unable to resolve server tunnel address: ", ecode.message ()); + } - void I2PServerTunnel::SetAccessList (const std::set& accessList) - { - m_AccessList = accessList; - m_IsAccessList = true; - } + void I2PServerTunnel::SetAccessList (const std::set& accessList) + { + m_AccessList = accessList; + m_IsAccessList = true; + } - void I2PServerTunnel::Accept () - { - if (m_PortDestination) - m_PortDestination->SetAcceptor (std::bind (&I2PServerTunnel::HandleAccept, this, std::placeholders::_1)); + void I2PServerTunnel::Accept () + { + if (m_PortDestination) + m_PortDestination->SetAcceptor (std::bind (&I2PServerTunnel::HandleAccept, this, std::placeholders::_1)); - auto localDestination = GetLocalDestination (); - if (localDestination) - { - if (!localDestination->IsAcceptingStreams ()) // set it as default if not set yet - localDestination->AcceptStreams (std::bind (&I2PServerTunnel::HandleAccept, this, std::placeholders::_1)); - } - else - LogPrint ("Local destination not set for server tunnel"); - } + auto localDestination = GetLocalDestination (); + if (localDestination) + { + if (!localDestination->IsAcceptingStreams ()) // set it as default if not set yet + localDestination->AcceptStreams (std::bind (&I2PServerTunnel::HandleAccept, this, std::placeholders::_1)); + } + else + LogPrint ("Local destination not set for server tunnel"); + } - void I2PServerTunnel::HandleAccept (std::shared_ptr stream) - { - if (stream) - { - if (m_IsAccessList) - { - if (!m_AccessList.count (stream->GetRemoteIdentity ().GetIdentHash ())) - { - LogPrint (eLogWarning, "Address ", stream->GetRemoteIdentity ().GetIdentHash ().ToBase32 (), " is not in white list. Incoming connection dropped"); - stream->Close (); - return; - } - } - CreateI2PConnection (stream); - } - } + void I2PServerTunnel::HandleAccept (std::shared_ptr stream) + { + if (stream) + { + if (m_IsAccessList) + { + if (!m_AccessList.count (stream->GetRemoteIdentity ().GetIdentHash ())) + { + LogPrint (eLogWarning, "Address ", stream->GetRemoteIdentity ().GetIdentHash ().ToBase32 (), " is not in white list. Incoming connection dropped"); + stream->Close (); + return; + } + } + CreateI2PConnection (stream); + } + } - void I2PServerTunnel::CreateI2PConnection (std::shared_ptr stream) - { - auto conn = std::make_shared (this, stream, std::make_shared (GetService ()), GetEndpoint ()); - AddHandler (conn); - conn->Connect (); - } + void I2PServerTunnel::CreateI2PConnection (std::shared_ptr stream) + { + auto conn = std::make_shared (this, stream, std::make_shared (GetService ()), GetEndpoint ()); + AddHandler (conn); + conn->Connect (); + } - I2PServerTunnelHTTP::I2PServerTunnelHTTP (const std::string& address, int port, std::shared_ptr localDestination, int inport): - I2PServerTunnel (address, port, localDestination, inport) - { - } + I2PServerTunnelHTTP::I2PServerTunnelHTTP (const std::string& address, int port, std::shared_ptr localDestination, int inport): + I2PServerTunnel (address, port, localDestination, inport) + { + } - void I2PServerTunnelHTTP::CreateI2PConnection (std::shared_ptr stream) - { - auto conn = std::make_shared (this, stream, std::make_shared (GetService ()), GetEndpoint (), GetAddress ()); - AddHandler (conn); - conn->Connect (); - } -} -} + void I2PServerTunnelHTTP::CreateI2PConnection (std::shared_ptr stream) + { + auto conn = std::make_shared (this, stream, std::make_shared (GetService ()), GetEndpoint (), GetAddress ()); + AddHandler (conn); + conn->Connect (); + } +} +} diff --git a/I2PTunnel.h b/I2PTunnel.h index 2071b89d..c8fe92d8 100644 --- a/I2PTunnel.h +++ b/I2PTunnel.h @@ -16,138 +16,138 @@ namespace i2p { namespace client { - const size_t I2P_TUNNEL_CONNECTION_BUFFER_SIZE = 8192; - const int I2P_TUNNEL_CONNECTION_MAX_IDLE = 3600; // in seconds - const int I2P_TUNNEL_DESTINATION_REQUEST_TIMEOUT = 10; // in seconds + const size_t I2P_TUNNEL_CONNECTION_BUFFER_SIZE = 8192; + const int I2P_TUNNEL_CONNECTION_MAX_IDLE = 3600; // in seconds + const int I2P_TUNNEL_DESTINATION_REQUEST_TIMEOUT = 10; // in seconds - class I2PTunnelConnection: public I2PServiceHandler, public std::enable_shared_from_this - { - public: + class I2PTunnelConnection: public I2PServiceHandler, public std::enable_shared_from_this + { + public: - I2PTunnelConnection (I2PService * owner, std::shared_ptr socket, - std::shared_ptr leaseSet, int port = 0); // to I2P - I2PTunnelConnection (I2PService * owner, std::shared_ptr socket, - std::shared_ptr stream); // to I2P using simplified API - I2PTunnelConnection (I2PService * owner, std::shared_ptr stream, std::shared_ptr socket, - const boost::asio::ip::tcp::endpoint& target, bool quiet = true); // from I2P - ~I2PTunnelConnection (); - void I2PConnect (const uint8_t * msg = nullptr, size_t len = 0); - void Connect (); - - protected: + I2PTunnelConnection (I2PService * owner, std::shared_ptr socket, + std::shared_ptr leaseSet, int port = 0); // to I2P + I2PTunnelConnection (I2PService * owner, std::shared_ptr socket, + std::shared_ptr stream); // to I2P using simplified API + I2PTunnelConnection (I2PService * owner, std::shared_ptr stream, std::shared_ptr socket, + const boost::asio::ip::tcp::endpoint& target, bool quiet = true); // from I2P + ~I2PTunnelConnection (); + void I2PConnect (const uint8_t * msg = nullptr, size_t len = 0); + void Connect (); + + protected: - void Terminate (); + void Terminate (); - void Receive (); - void HandleReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred); - virtual void Write (const uint8_t * buf, size_t len); // can be overloaded - void HandleWrite (const boost::system::error_code& ecode); + void Receive (); + void HandleReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred); + virtual void Write (const uint8_t * buf, size_t len); // can be overloaded + void HandleWrite (const boost::system::error_code& ecode); - void StreamReceive (); - void HandleStreamReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred); - void HandleConnect (const boost::system::error_code& ecode); + void StreamReceive (); + void HandleStreamReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred); + void HandleConnect (const boost::system::error_code& ecode); - private: + private: - uint8_t m_Buffer[I2P_TUNNEL_CONNECTION_BUFFER_SIZE], m_StreamBuffer[I2P_TUNNEL_CONNECTION_BUFFER_SIZE]; - std::shared_ptr m_Socket; - std::shared_ptr m_Stream; - boost::asio::ip::tcp::endpoint m_RemoteEndpoint; - bool m_IsQuiet; // don't send destination - }; + uint8_t m_Buffer[I2P_TUNNEL_CONNECTION_BUFFER_SIZE], m_StreamBuffer[I2P_TUNNEL_CONNECTION_BUFFER_SIZE]; + std::shared_ptr m_Socket; + std::shared_ptr m_Stream; + boost::asio::ip::tcp::endpoint m_RemoteEndpoint; + bool m_IsQuiet; // don't send destination + }; - class I2PTunnelConnectionHTTP: public I2PTunnelConnection - { - public: - - I2PTunnelConnectionHTTP (I2PService * owner, std::shared_ptr stream, - std::shared_ptr socket, - const boost::asio::ip::tcp::endpoint& target, const std::string& host); + class I2PTunnelConnectionHTTP: public I2PTunnelConnection + { + public: + + I2PTunnelConnectionHTTP (I2PService * owner, std::shared_ptr stream, + std::shared_ptr socket, + const boost::asio::ip::tcp::endpoint& target, const std::string& host); - protected: + protected: - void Write (const uint8_t * buf, size_t len); + void Write (const uint8_t * buf, size_t len); - private: - - std::string m_Host; - std::stringstream m_InHeader, m_OutHeader; - bool m_HeaderSent; - }; + private: + + std::string m_Host; + std::stringstream m_InHeader, m_OutHeader; + bool m_HeaderSent; + }; - class I2PClientTunnel: public TCPIPAcceptor - { - protected: + class I2PClientTunnel: public TCPIPAcceptor + { + protected: - // Implements TCPIPAcceptor - std::shared_ptr CreateHandler(std::shared_ptr socket); - const char* GetName() { return "I2P Client Tunnel"; } + // Implements TCPIPAcceptor + std::shared_ptr CreateHandler(std::shared_ptr socket); + const char* GetName() { return "I2P Client Tunnel"; } - public: + public: - I2PClientTunnel (const std::string& destination, int port, std::shared_ptr localDestination, int destinationPort = 0); - ~I2PClientTunnel () {} - - void Start (); - void Stop (); + I2PClientTunnel (const std::string& destination, int port, std::shared_ptr localDestination, int destinationPort = 0); + ~I2PClientTunnel () {} + + void Start (); + void Stop (); - private: + private: - const i2p::data::IdentHash * GetIdentHash (); + const i2p::data::IdentHash * GetIdentHash (); - std::string m_Destination; - const i2p::data::IdentHash * m_DestinationIdentHash; - int m_DestinationPort; - }; + std::string m_Destination; + const i2p::data::IdentHash * m_DestinationIdentHash; + int m_DestinationPort; + }; - class I2PServerTunnel: public I2PService - { - public: + class I2PServerTunnel: public I2PService + { + public: - I2PServerTunnel (const std::string& address, int port, - std::shared_ptr localDestination, int inport = 0); + I2PServerTunnel (const std::string& address, int port, + std::shared_ptr localDestination, int inport = 0); - void Start (); - void Stop (); + void Start (); + void Stop (); - void SetAccessList (const std::set& accessList); + void SetAccessList (const std::set& accessList); - const std::string& GetAddress() const { return m_Address; } - int GetPort () const { return m_Port; }; - const boost::asio::ip::tcp::endpoint& GetEndpoint () const { return m_Endpoint; } + const std::string& GetAddress() const { return m_Address; } + int GetPort () const { return m_Port; }; + const boost::asio::ip::tcp::endpoint& GetEndpoint () const { return m_Endpoint; } - private: + private: - void HandleResolve (const boost::system::error_code& ecode, boost::asio::ip::tcp::resolver::iterator it, - std::shared_ptr resolver); + void HandleResolve (const boost::system::error_code& ecode, boost::asio::ip::tcp::resolver::iterator it, + std::shared_ptr resolver); - void Accept (); - void HandleAccept (std::shared_ptr stream); - virtual void CreateI2PConnection (std::shared_ptr stream); + void Accept (); + void HandleAccept (std::shared_ptr stream); + virtual void CreateI2PConnection (std::shared_ptr stream); - private: + private: - std::string m_Address; - int m_Port; - boost::asio::ip::tcp::endpoint m_Endpoint; - std::shared_ptr m_PortDestination; - std::set m_AccessList; - bool m_IsAccessList; - }; + std::string m_Address; + int m_Port; + boost::asio::ip::tcp::endpoint m_Endpoint; + std::shared_ptr m_PortDestination; + std::set m_AccessList; + bool m_IsAccessList; + }; - class I2PServerTunnelHTTP: public I2PServerTunnel - { - public: + class I2PServerTunnelHTTP: public I2PServerTunnel + { + public: - I2PServerTunnelHTTP (const std::string& address, int port, - std::shared_ptr localDestination, int inport = 0); + I2PServerTunnelHTTP (const std::string& address, int port, + std::shared_ptr localDestination, int inport = 0); - private: + private: - void CreateI2PConnection (std::shared_ptr stream); - }; + void CreateI2PConnection (std::shared_ptr stream); + }; } -} +} #endif diff --git a/Identity.cpp b/Identity.cpp index 9dc96d01..6f17eca6 100644 --- a/Identity.cpp +++ b/Identity.cpp @@ -14,553 +14,553 @@ namespace i2p { namespace data { - Identity& Identity::operator=(const Keys& keys) - { - // copy public and signing keys together - memcpy (publicKey, keys.publicKey, sizeof (publicKey) + sizeof (signingKey)); - memset (&certificate, 0, sizeof (certificate)); - return *this; - } + Identity& Identity::operator=(const Keys& keys) + { + // copy public and signing keys together + memcpy (publicKey, keys.publicKey, sizeof (publicKey) + sizeof (signingKey)); + memset (&certificate, 0, sizeof (certificate)); + return *this; + } - size_t Identity::FromBuffer (const uint8_t * buf, size_t len) - { - memcpy (publicKey, buf, DEFAULT_IDENTITY_SIZE); - return DEFAULT_IDENTITY_SIZE; - } + size_t Identity::FromBuffer (const uint8_t * buf, size_t len) + { + memcpy (publicKey, buf, DEFAULT_IDENTITY_SIZE); + return DEFAULT_IDENTITY_SIZE; + } - IdentHash Identity::Hash () const - { - IdentHash hash; - CryptoPP::SHA256().CalculateDigest(hash, publicKey, DEFAULT_IDENTITY_SIZE); - return hash; - } - - IdentityEx::IdentityEx (): - m_Verifier (nullptr), m_ExtendedLen (0), m_ExtendedBuffer (nullptr) - { - } + IdentHash Identity::Hash () const + { + IdentHash hash; + CryptoPP::SHA256().CalculateDigest(hash, publicKey, DEFAULT_IDENTITY_SIZE); + return hash; + } + + IdentityEx::IdentityEx (): + m_Verifier (nullptr), m_ExtendedLen (0), m_ExtendedBuffer (nullptr) + { + } - IdentityEx::IdentityEx(const uint8_t * publicKey, const uint8_t * signingKey, SigningKeyType type) - { - memcpy (m_StandardIdentity.publicKey, publicKey, sizeof (m_StandardIdentity.publicKey)); - if (type != SIGNING_KEY_TYPE_DSA_SHA1) - { - size_t excessLen = 0; - uint8_t * excessBuf = nullptr; - switch (type) - { - case SIGNING_KEY_TYPE_ECDSA_SHA256_P256: - { - size_t padding = 128 - i2p::crypto::ECDSAP256_KEY_LENGTH; // 64 = 128 - 64 - i2p::context.GetRandomNumberGenerator ().GenerateBlock (m_StandardIdentity.signingKey, padding); - memcpy (m_StandardIdentity.signingKey + padding, signingKey, i2p::crypto::ECDSAP256_KEY_LENGTH); - break; - } - case SIGNING_KEY_TYPE_ECDSA_SHA384_P384: - { - size_t padding = 128 - i2p::crypto::ECDSAP384_KEY_LENGTH; // 32 = 128 - 96 - i2p::context.GetRandomNumberGenerator ().GenerateBlock (m_StandardIdentity.signingKey, padding); - memcpy (m_StandardIdentity.signingKey + padding, signingKey, i2p::crypto::ECDSAP384_KEY_LENGTH); - break; - } - case SIGNING_KEY_TYPE_ECDSA_SHA512_P521: - { - memcpy (m_StandardIdentity.signingKey, signingKey, 128); - excessLen = i2p::crypto::ECDSAP521_KEY_LENGTH - 128; // 4 = 132 - 128 - excessBuf = new uint8_t[excessLen]; - memcpy (excessBuf, signingKey + 128, excessLen); - break; - } - case SIGNING_KEY_TYPE_RSA_SHA256_2048: - { - memcpy (m_StandardIdentity.signingKey, signingKey, 128); - excessLen = i2p::crypto::RSASHA2562048_KEY_LENGTH - 128; // 128 = 256 - 128 - excessBuf = new uint8_t[excessLen]; - memcpy (excessBuf, signingKey + 128, excessLen); - break; - } - case SIGNING_KEY_TYPE_RSA_SHA384_3072: - { - memcpy (m_StandardIdentity.signingKey, signingKey, 128); - excessLen = i2p::crypto::RSASHA3843072_KEY_LENGTH - 128; // 256 = 384 - 128 - excessBuf = new uint8_t[excessLen]; - memcpy (excessBuf, signingKey + 128, excessLen); - break; - } - case SIGNING_KEY_TYPE_RSA_SHA512_4096: - { - memcpy (m_StandardIdentity.signingKey, signingKey, 128); - excessLen = i2p::crypto::RSASHA5124096_KEY_LENGTH - 128; // 384 = 512 - 128 - excessBuf = new uint8_t[excessLen]; - memcpy (excessBuf, signingKey + 128, excessLen); - break; - } - case SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519: - { - size_t padding = 128 - i2p::crypto::EDDSA25519_PUBLIC_KEY_LENGTH; // 96 = 128 - 32 - i2p::context.GetRandomNumberGenerator ().GenerateBlock (m_StandardIdentity.signingKey, padding); - memcpy (m_StandardIdentity.signingKey + padding, signingKey, i2p::crypto::EDDSA25519_PUBLIC_KEY_LENGTH); - break; - } - default: - LogPrint ("Signing key type ", (int)type, " is not supported"); - } - m_ExtendedLen = 4 + excessLen; // 4 bytes extra + excess length - // fill certificate - m_StandardIdentity.certificate.type = CERTIFICATE_TYPE_KEY; - m_StandardIdentity.certificate.length = htobe16 (m_ExtendedLen); - // fill extended buffer - m_ExtendedBuffer = new uint8_t[m_ExtendedLen]; - htobe16buf (m_ExtendedBuffer, type); - htobe16buf (m_ExtendedBuffer + 2, CRYPTO_KEY_TYPE_ELGAMAL); - if (excessLen && excessBuf) - { - memcpy (m_ExtendedBuffer + 4, excessBuf, excessLen); - delete[] excessBuf; - } - // calculate ident hash - uint8_t * buf = new uint8_t[GetFullLen ()]; - ToBuffer (buf, GetFullLen ()); - CryptoPP::SHA256().CalculateDigest(m_IdentHash, buf, GetFullLen ()); - delete[] buf; - } - else // DSA-SHA1 - { - memcpy (m_StandardIdentity.signingKey, signingKey, sizeof (m_StandardIdentity.signingKey)); - memset (&m_StandardIdentity.certificate, 0, sizeof (m_StandardIdentity.certificate)); - m_IdentHash = m_StandardIdentity.Hash (); - m_ExtendedLen = 0; - m_ExtendedBuffer = nullptr; - } - CreateVerifier (); - } - - IdentityEx::IdentityEx (const uint8_t * buf, size_t len): - m_Verifier (nullptr), m_ExtendedLen (0), m_ExtendedBuffer (nullptr) - { - FromBuffer (buf, len); - } + IdentityEx::IdentityEx(const uint8_t * publicKey, const uint8_t * signingKey, SigningKeyType type) + { + memcpy (m_StandardIdentity.publicKey, publicKey, sizeof (m_StandardIdentity.publicKey)); + if (type != SIGNING_KEY_TYPE_DSA_SHA1) + { + size_t excessLen = 0; + uint8_t * excessBuf = nullptr; + switch (type) + { + case SIGNING_KEY_TYPE_ECDSA_SHA256_P256: + { + size_t padding = 128 - i2p::crypto::ECDSAP256_KEY_LENGTH; // 64 = 128 - 64 + i2p::context.GetRandomNumberGenerator ().GenerateBlock (m_StandardIdentity.signingKey, padding); + memcpy (m_StandardIdentity.signingKey + padding, signingKey, i2p::crypto::ECDSAP256_KEY_LENGTH); + break; + } + case SIGNING_KEY_TYPE_ECDSA_SHA384_P384: + { + size_t padding = 128 - i2p::crypto::ECDSAP384_KEY_LENGTH; // 32 = 128 - 96 + i2p::context.GetRandomNumberGenerator ().GenerateBlock (m_StandardIdentity.signingKey, padding); + memcpy (m_StandardIdentity.signingKey + padding, signingKey, i2p::crypto::ECDSAP384_KEY_LENGTH); + break; + } + case SIGNING_KEY_TYPE_ECDSA_SHA512_P521: + { + memcpy (m_StandardIdentity.signingKey, signingKey, 128); + excessLen = i2p::crypto::ECDSAP521_KEY_LENGTH - 128; // 4 = 132 - 128 + excessBuf = new uint8_t[excessLen]; + memcpy (excessBuf, signingKey + 128, excessLen); + break; + } + case SIGNING_KEY_TYPE_RSA_SHA256_2048: + { + memcpy (m_StandardIdentity.signingKey, signingKey, 128); + excessLen = i2p::crypto::RSASHA2562048_KEY_LENGTH - 128; // 128 = 256 - 128 + excessBuf = new uint8_t[excessLen]; + memcpy (excessBuf, signingKey + 128, excessLen); + break; + } + case SIGNING_KEY_TYPE_RSA_SHA384_3072: + { + memcpy (m_StandardIdentity.signingKey, signingKey, 128); + excessLen = i2p::crypto::RSASHA3843072_KEY_LENGTH - 128; // 256 = 384 - 128 + excessBuf = new uint8_t[excessLen]; + memcpy (excessBuf, signingKey + 128, excessLen); + break; + } + case SIGNING_KEY_TYPE_RSA_SHA512_4096: + { + memcpy (m_StandardIdentity.signingKey, signingKey, 128); + excessLen = i2p::crypto::RSASHA5124096_KEY_LENGTH - 128; // 384 = 512 - 128 + excessBuf = new uint8_t[excessLen]; + memcpy (excessBuf, signingKey + 128, excessLen); + break; + } + case SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519: + { + size_t padding = 128 - i2p::crypto::EDDSA25519_PUBLIC_KEY_LENGTH; // 96 = 128 - 32 + i2p::context.GetRandomNumberGenerator ().GenerateBlock (m_StandardIdentity.signingKey, padding); + memcpy (m_StandardIdentity.signingKey + padding, signingKey, i2p::crypto::EDDSA25519_PUBLIC_KEY_LENGTH); + break; + } + default: + LogPrint ("Signing key type ", (int)type, " is not supported"); + } + m_ExtendedLen = 4 + excessLen; // 4 bytes extra + excess length + // fill certificate + m_StandardIdentity.certificate.type = CERTIFICATE_TYPE_KEY; + m_StandardIdentity.certificate.length = htobe16 (m_ExtendedLen); + // fill extended buffer + m_ExtendedBuffer = new uint8_t[m_ExtendedLen]; + htobe16buf (m_ExtendedBuffer, type); + htobe16buf (m_ExtendedBuffer + 2, CRYPTO_KEY_TYPE_ELGAMAL); + if (excessLen && excessBuf) + { + memcpy (m_ExtendedBuffer + 4, excessBuf, excessLen); + delete[] excessBuf; + } + // calculate ident hash + uint8_t * buf = new uint8_t[GetFullLen ()]; + ToBuffer (buf, GetFullLen ()); + CryptoPP::SHA256().CalculateDigest(m_IdentHash, buf, GetFullLen ()); + delete[] buf; + } + else // DSA-SHA1 + { + memcpy (m_StandardIdentity.signingKey, signingKey, sizeof (m_StandardIdentity.signingKey)); + memset (&m_StandardIdentity.certificate, 0, sizeof (m_StandardIdentity.certificate)); + m_IdentHash = m_StandardIdentity.Hash (); + m_ExtendedLen = 0; + m_ExtendedBuffer = nullptr; + } + CreateVerifier (); + } + + IdentityEx::IdentityEx (const uint8_t * buf, size_t len): + m_Verifier (nullptr), m_ExtendedLen (0), m_ExtendedBuffer (nullptr) + { + FromBuffer (buf, len); + } - IdentityEx::IdentityEx (const IdentityEx& other): - m_Verifier (nullptr), m_ExtendedBuffer (nullptr) - { - *this = other; - } - - IdentityEx::~IdentityEx () - { - delete m_Verifier; - delete[] m_ExtendedBuffer; - } + IdentityEx::IdentityEx (const IdentityEx& other): + m_Verifier (nullptr), m_ExtendedBuffer (nullptr) + { + *this = other; + } + + IdentityEx::~IdentityEx () + { + delete m_Verifier; + delete[] m_ExtendedBuffer; + } - IdentityEx& IdentityEx::operator=(const IdentityEx& other) - { - memcpy (&m_StandardIdentity, &other.m_StandardIdentity, DEFAULT_IDENTITY_SIZE); - m_IdentHash = other.m_IdentHash; - - delete[] m_ExtendedBuffer; - m_ExtendedLen = other.m_ExtendedLen; - if (m_ExtendedLen > 0) - { - m_ExtendedBuffer = new uint8_t[m_ExtendedLen]; - memcpy (m_ExtendedBuffer, other.m_ExtendedBuffer, m_ExtendedLen); - } - else - m_ExtendedBuffer = nullptr; - - delete m_Verifier; - m_Verifier = nullptr; - - return *this; - } + IdentityEx& IdentityEx::operator=(const IdentityEx& other) + { + memcpy (&m_StandardIdentity, &other.m_StandardIdentity, DEFAULT_IDENTITY_SIZE); + m_IdentHash = other.m_IdentHash; + + delete[] m_ExtendedBuffer; + m_ExtendedLen = other.m_ExtendedLen; + if (m_ExtendedLen > 0) + { + m_ExtendedBuffer = new uint8_t[m_ExtendedLen]; + memcpy (m_ExtendedBuffer, other.m_ExtendedBuffer, m_ExtendedLen); + } + else + m_ExtendedBuffer = nullptr; + + delete m_Verifier; + m_Verifier = nullptr; + + return *this; + } - IdentityEx& IdentityEx::operator=(const Identity& standard) - { - m_StandardIdentity = standard; - m_IdentHash = m_StandardIdentity.Hash (); - - delete[] m_ExtendedBuffer; - m_ExtendedBuffer = nullptr; - m_ExtendedLen = 0; + IdentityEx& IdentityEx::operator=(const Identity& standard) + { + m_StandardIdentity = standard; + m_IdentHash = m_StandardIdentity.Hash (); + + delete[] m_ExtendedBuffer; + m_ExtendedBuffer = nullptr; + m_ExtendedLen = 0; - delete m_Verifier; - m_Verifier = nullptr; - - return *this; - } - - size_t IdentityEx::FromBuffer (const uint8_t * buf, size_t len) - { - if (len < DEFAULT_IDENTITY_SIZE) - { - LogPrint (eLogError, "Identity buffer length ", len, " is too small"); - return 0; - } - memcpy (&m_StandardIdentity, buf, DEFAULT_IDENTITY_SIZE); + delete m_Verifier; + m_Verifier = nullptr; + + return *this; + } + + size_t IdentityEx::FromBuffer (const uint8_t * buf, size_t len) + { + if (len < DEFAULT_IDENTITY_SIZE) + { + LogPrint (eLogError, "Identity buffer length ", len, " is too small"); + return 0; + } + memcpy (&m_StandardIdentity, buf, DEFAULT_IDENTITY_SIZE); - delete[] m_ExtendedBuffer; - if (m_StandardIdentity.certificate.length) - { - m_ExtendedLen = be16toh (m_StandardIdentity.certificate.length); - if (m_ExtendedLen + DEFAULT_IDENTITY_SIZE <= len) - { - m_ExtendedBuffer = new uint8_t[m_ExtendedLen]; - memcpy (m_ExtendedBuffer, buf + DEFAULT_IDENTITY_SIZE, m_ExtendedLen); - } - else - { - LogPrint (eLogError, "Certificate length ", m_ExtendedLen, " exceeds buffer length ", len - DEFAULT_IDENTITY_SIZE); - return 0; - } - } - else - { - m_ExtendedLen = 0; - m_ExtendedBuffer = nullptr; - } - CryptoPP::SHA256().CalculateDigest(m_IdentHash, buf, GetFullLen ()); - - delete m_Verifier; - m_Verifier = nullptr; - - return GetFullLen (); - } + delete[] m_ExtendedBuffer; + if (m_StandardIdentity.certificate.length) + { + m_ExtendedLen = be16toh (m_StandardIdentity.certificate.length); + if (m_ExtendedLen + DEFAULT_IDENTITY_SIZE <= len) + { + m_ExtendedBuffer = new uint8_t[m_ExtendedLen]; + memcpy (m_ExtendedBuffer, buf + DEFAULT_IDENTITY_SIZE, m_ExtendedLen); + } + else + { + LogPrint (eLogError, "Certificate length ", m_ExtendedLen, " exceeds buffer length ", len - DEFAULT_IDENTITY_SIZE); + return 0; + } + } + else + { + m_ExtendedLen = 0; + m_ExtendedBuffer = nullptr; + } + CryptoPP::SHA256().CalculateDigest(m_IdentHash, buf, GetFullLen ()); + + delete m_Verifier; + m_Verifier = nullptr; + + return GetFullLen (); + } - size_t IdentityEx::ToBuffer (uint8_t * buf, size_t len) const - { - memcpy (buf, &m_StandardIdentity, DEFAULT_IDENTITY_SIZE); - if (m_ExtendedLen > 0 && m_ExtendedBuffer) - memcpy (buf + DEFAULT_IDENTITY_SIZE, m_ExtendedBuffer, m_ExtendedLen); - return GetFullLen (); - } + size_t IdentityEx::ToBuffer (uint8_t * buf, size_t len) const + { + memcpy (buf, &m_StandardIdentity, DEFAULT_IDENTITY_SIZE); + if (m_ExtendedLen > 0 && m_ExtendedBuffer) + memcpy (buf + DEFAULT_IDENTITY_SIZE, m_ExtendedBuffer, m_ExtendedLen); + return GetFullLen (); + } - size_t IdentityEx::FromBase64(const std::string& s) - { - uint8_t buf[1024]; - auto len = Base64ToByteStream (s.c_str(), s.length(), buf, 1024); - return FromBuffer (buf, len); - } - - std::string IdentityEx::ToBase64 () const - { - uint8_t buf[1024]; - char str[1536]; - size_t l = ToBuffer (buf, 1024); - size_t l1 = i2p::data::ByteStreamToBase64 (buf, l, str, 1536); - str[l1] = 0; - return std::string (str); - } - - size_t IdentityEx::GetSigningPublicKeyLen () const - { - if (!m_Verifier) CreateVerifier (); - if (m_Verifier) - return m_Verifier->GetPublicKeyLen (); - return 128; - } + size_t IdentityEx::FromBase64(const std::string& s) + { + uint8_t buf[1024]; + auto len = Base64ToByteStream (s.c_str(), s.length(), buf, 1024); + return FromBuffer (buf, len); + } + + std::string IdentityEx::ToBase64 () const + { + uint8_t buf[1024]; + char str[1536]; + size_t l = ToBuffer (buf, 1024); + size_t l1 = i2p::data::ByteStreamToBase64 (buf, l, str, 1536); + str[l1] = 0; + return std::string (str); + } + + size_t IdentityEx::GetSigningPublicKeyLen () const + { + if (!m_Verifier) CreateVerifier (); + if (m_Verifier) + return m_Verifier->GetPublicKeyLen (); + return 128; + } - size_t IdentityEx::GetSigningPrivateKeyLen () const - { - if (!m_Verifier) CreateVerifier (); - if (m_Verifier) - return m_Verifier->GetPrivateKeyLen (); - return GetSignatureLen ()/2; - } - - size_t IdentityEx::GetSignatureLen () const - { - if (!m_Verifier) CreateVerifier (); - if (m_Verifier) - return m_Verifier->GetSignatureLen (); - return 40; - } - bool IdentityEx::Verify (const uint8_t * buf, size_t len, const uint8_t * signature) const - { - if (!m_Verifier) CreateVerifier (); - if (m_Verifier) - return m_Verifier->Verify (buf, len, signature); - return false; - } + size_t IdentityEx::GetSigningPrivateKeyLen () const + { + if (!m_Verifier) CreateVerifier (); + if (m_Verifier) + return m_Verifier->GetPrivateKeyLen (); + return GetSignatureLen ()/2; + } + + size_t IdentityEx::GetSignatureLen () const + { + if (!m_Verifier) CreateVerifier (); + if (m_Verifier) + return m_Verifier->GetSignatureLen (); + return 40; + } + bool IdentityEx::Verify (const uint8_t * buf, size_t len, const uint8_t * signature) const + { + if (!m_Verifier) CreateVerifier (); + if (m_Verifier) + return m_Verifier->Verify (buf, len, signature); + return false; + } - SigningKeyType IdentityEx::GetSigningKeyType () const - { - if (m_StandardIdentity.certificate.type == CERTIFICATE_TYPE_KEY && m_ExtendedBuffer) - return bufbe16toh (m_ExtendedBuffer); // signing key - return SIGNING_KEY_TYPE_DSA_SHA1; - } + SigningKeyType IdentityEx::GetSigningKeyType () const + { + if (m_StandardIdentity.certificate.type == CERTIFICATE_TYPE_KEY && m_ExtendedBuffer) + return bufbe16toh (m_ExtendedBuffer); // signing key + return SIGNING_KEY_TYPE_DSA_SHA1; + } - CryptoKeyType IdentityEx::GetCryptoKeyType () const - { - if (m_StandardIdentity.certificate.type == CERTIFICATE_TYPE_KEY && m_ExtendedBuffer) - return bufbe16toh (m_ExtendedBuffer + 2); // crypto key - return CRYPTO_KEY_TYPE_ELGAMAL; - } - - void IdentityEx::CreateVerifier () const - { - auto keyType = GetSigningKeyType (); - switch (keyType) - { - case SIGNING_KEY_TYPE_DSA_SHA1: - m_Verifier = new i2p::crypto::DSAVerifier (m_StandardIdentity.signingKey); - break; - case SIGNING_KEY_TYPE_ECDSA_SHA256_P256: - { - size_t padding = 128 - i2p::crypto::ECDSAP256_KEY_LENGTH; // 64 = 128 - 64 - m_Verifier = new i2p::crypto::ECDSAP256Verifier (m_StandardIdentity.signingKey + padding); - break; - } - case SIGNING_KEY_TYPE_ECDSA_SHA384_P384: - { - size_t padding = 128 - i2p::crypto::ECDSAP384_KEY_LENGTH; // 32 = 128 - 96 - m_Verifier = new i2p::crypto::ECDSAP384Verifier (m_StandardIdentity.signingKey + padding); - break; - } - case SIGNING_KEY_TYPE_ECDSA_SHA512_P521: - { - uint8_t signingKey[i2p::crypto::ECDSAP521_KEY_LENGTH]; - memcpy (signingKey, m_StandardIdentity.signingKey, 128); - size_t excessLen = i2p::crypto::ECDSAP521_KEY_LENGTH - 128; // 4 = 132- 128 - memcpy (signingKey + 128, m_ExtendedBuffer + 4, excessLen); // right after signing and crypto key types - m_Verifier = new i2p::crypto::ECDSAP521Verifier (signingKey); - break; - } - case SIGNING_KEY_TYPE_RSA_SHA256_2048: - { - uint8_t signingKey[i2p::crypto::RSASHA2562048_KEY_LENGTH]; - memcpy (signingKey, m_StandardIdentity.signingKey, 128); - size_t excessLen = i2p::crypto::RSASHA2562048_KEY_LENGTH - 128; // 128 = 256- 128 - memcpy (signingKey + 128, m_ExtendedBuffer + 4, excessLen); // right after signing and crypto key types - m_Verifier = new i2p::crypto:: RSASHA2562048Verifier (signingKey); - break; - } - case SIGNING_KEY_TYPE_RSA_SHA384_3072: - { - uint8_t signingKey[i2p::crypto::RSASHA3843072_KEY_LENGTH]; - memcpy (signingKey, m_StandardIdentity.signingKey, 128); - size_t excessLen = i2p::crypto::RSASHA3843072_KEY_LENGTH - 128; // 256 = 384- 128 - memcpy (signingKey + 128, m_ExtendedBuffer + 4, excessLen); // right after signing and crypto key types - m_Verifier = new i2p::crypto:: RSASHA3843072Verifier (signingKey); - break; - } - case SIGNING_KEY_TYPE_RSA_SHA512_4096: - { - uint8_t signingKey[i2p::crypto::RSASHA5124096_KEY_LENGTH]; - memcpy (signingKey, m_StandardIdentity.signingKey, 128); - size_t excessLen = i2p::crypto::RSASHA5124096_KEY_LENGTH - 128; // 384 = 512- 128 - memcpy (signingKey + 128, m_ExtendedBuffer + 4, excessLen); // right after signing and crypto key types - m_Verifier = new i2p::crypto:: RSASHA5124096Verifier (signingKey); - break; - } - case SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519: - { - size_t padding = 128 - i2p::crypto::EDDSA25519_PUBLIC_KEY_LENGTH; // 96 = 128 - 32 - m_Verifier = new i2p::crypto::EDDSA25519Verifier (m_StandardIdentity.signingKey + padding); - break; - } - default: - LogPrint ("Signing key type ", (int)keyType, " is not supported"); - } - } - - void IdentityEx::DropVerifier () - { - auto verifier = m_Verifier; - m_Verifier = nullptr; // TODO: make this atomic - delete verifier; - } + CryptoKeyType IdentityEx::GetCryptoKeyType () const + { + if (m_StandardIdentity.certificate.type == CERTIFICATE_TYPE_KEY && m_ExtendedBuffer) + return bufbe16toh (m_ExtendedBuffer + 2); // crypto key + return CRYPTO_KEY_TYPE_ELGAMAL; + } + + void IdentityEx::CreateVerifier () const + { + auto keyType = GetSigningKeyType (); + switch (keyType) + { + case SIGNING_KEY_TYPE_DSA_SHA1: + m_Verifier = new i2p::crypto::DSAVerifier (m_StandardIdentity.signingKey); + break; + case SIGNING_KEY_TYPE_ECDSA_SHA256_P256: + { + size_t padding = 128 - i2p::crypto::ECDSAP256_KEY_LENGTH; // 64 = 128 - 64 + m_Verifier = new i2p::crypto::ECDSAP256Verifier (m_StandardIdentity.signingKey + padding); + break; + } + case SIGNING_KEY_TYPE_ECDSA_SHA384_P384: + { + size_t padding = 128 - i2p::crypto::ECDSAP384_KEY_LENGTH; // 32 = 128 - 96 + m_Verifier = new i2p::crypto::ECDSAP384Verifier (m_StandardIdentity.signingKey + padding); + break; + } + case SIGNING_KEY_TYPE_ECDSA_SHA512_P521: + { + uint8_t signingKey[i2p::crypto::ECDSAP521_KEY_LENGTH]; + memcpy (signingKey, m_StandardIdentity.signingKey, 128); + size_t excessLen = i2p::crypto::ECDSAP521_KEY_LENGTH - 128; // 4 = 132- 128 + memcpy (signingKey + 128, m_ExtendedBuffer + 4, excessLen); // right after signing and crypto key types + m_Verifier = new i2p::crypto::ECDSAP521Verifier (signingKey); + break; + } + case SIGNING_KEY_TYPE_RSA_SHA256_2048: + { + uint8_t signingKey[i2p::crypto::RSASHA2562048_KEY_LENGTH]; + memcpy (signingKey, m_StandardIdentity.signingKey, 128); + size_t excessLen = i2p::crypto::RSASHA2562048_KEY_LENGTH - 128; // 128 = 256- 128 + memcpy (signingKey + 128, m_ExtendedBuffer + 4, excessLen); // right after signing and crypto key types + m_Verifier = new i2p::crypto:: RSASHA2562048Verifier (signingKey); + break; + } + case SIGNING_KEY_TYPE_RSA_SHA384_3072: + { + uint8_t signingKey[i2p::crypto::RSASHA3843072_KEY_LENGTH]; + memcpy (signingKey, m_StandardIdentity.signingKey, 128); + size_t excessLen = i2p::crypto::RSASHA3843072_KEY_LENGTH - 128; // 256 = 384- 128 + memcpy (signingKey + 128, m_ExtendedBuffer + 4, excessLen); // right after signing and crypto key types + m_Verifier = new i2p::crypto:: RSASHA3843072Verifier (signingKey); + break; + } + case SIGNING_KEY_TYPE_RSA_SHA512_4096: + { + uint8_t signingKey[i2p::crypto::RSASHA5124096_KEY_LENGTH]; + memcpy (signingKey, m_StandardIdentity.signingKey, 128); + size_t excessLen = i2p::crypto::RSASHA5124096_KEY_LENGTH - 128; // 384 = 512- 128 + memcpy (signingKey + 128, m_ExtendedBuffer + 4, excessLen); // right after signing and crypto key types + m_Verifier = new i2p::crypto:: RSASHA5124096Verifier (signingKey); + break; + } + case SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519: + { + size_t padding = 128 - i2p::crypto::EDDSA25519_PUBLIC_KEY_LENGTH; // 96 = 128 - 32 + m_Verifier = new i2p::crypto::EDDSA25519Verifier (m_StandardIdentity.signingKey + padding); + break; + } + default: + LogPrint ("Signing key type ", (int)keyType, " is not supported"); + } + } + + void IdentityEx::DropVerifier () + { + auto verifier = m_Verifier; + m_Verifier = nullptr; // TODO: make this atomic + delete verifier; + } - PrivateKeys& PrivateKeys::operator=(const Keys& keys) - { - m_Public = Identity (keys); - memcpy (m_PrivateKey, keys.privateKey, 256); // 256 - memcpy (m_SigningPrivateKey, keys.signingPrivateKey, m_Public.GetSigningPrivateKeyLen ()); - delete m_Signer; - m_Signer = nullptr; - CreateSigner (); - return *this; - } + PrivateKeys& PrivateKeys::operator=(const Keys& keys) + { + m_Public = Identity (keys); + memcpy (m_PrivateKey, keys.privateKey, 256); // 256 + memcpy (m_SigningPrivateKey, keys.signingPrivateKey, m_Public.GetSigningPrivateKeyLen ()); + delete m_Signer; + m_Signer = nullptr; + CreateSigner (); + return *this; + } - PrivateKeys& PrivateKeys::operator=(const PrivateKeys& other) - { - m_Public = other.m_Public; - memcpy (m_PrivateKey, other.m_PrivateKey, 256); // 256 - memcpy (m_SigningPrivateKey, other.m_SigningPrivateKey, m_Public.GetSigningPrivateKeyLen ()); - delete m_Signer; - m_Signer = nullptr; - CreateSigner (); - return *this; - } - - size_t PrivateKeys::FromBuffer (const uint8_t * buf, size_t len) - { - size_t ret = m_Public.FromBuffer (buf, len); - memcpy (m_PrivateKey, buf + ret, 256); // private key always 256 - ret += 256; - size_t signingPrivateKeySize = m_Public.GetSigningPrivateKeyLen (); - memcpy (m_SigningPrivateKey, buf + ret, signingPrivateKeySize); - ret += signingPrivateKeySize; - delete m_Signer; - m_Signer = nullptr; - CreateSigner (); - return ret; - } - - size_t PrivateKeys::ToBuffer (uint8_t * buf, size_t len) const - { - size_t ret = m_Public.ToBuffer (buf, len); - memcpy (buf + ret, m_PrivateKey, 256); // private key always 256 - ret += 256; - size_t signingPrivateKeySize = m_Public.GetSigningPrivateKeyLen (); - memcpy (buf + ret, m_SigningPrivateKey, signingPrivateKeySize); - ret += signingPrivateKeySize; - return ret; - } + PrivateKeys& PrivateKeys::operator=(const PrivateKeys& other) + { + m_Public = other.m_Public; + memcpy (m_PrivateKey, other.m_PrivateKey, 256); // 256 + memcpy (m_SigningPrivateKey, other.m_SigningPrivateKey, m_Public.GetSigningPrivateKeyLen ()); + delete m_Signer; + m_Signer = nullptr; + CreateSigner (); + return *this; + } + + size_t PrivateKeys::FromBuffer (const uint8_t * buf, size_t len) + { + size_t ret = m_Public.FromBuffer (buf, len); + memcpy (m_PrivateKey, buf + ret, 256); // private key always 256 + ret += 256; + size_t signingPrivateKeySize = m_Public.GetSigningPrivateKeyLen (); + memcpy (m_SigningPrivateKey, buf + ret, signingPrivateKeySize); + ret += signingPrivateKeySize; + delete m_Signer; + m_Signer = nullptr; + CreateSigner (); + return ret; + } + + size_t PrivateKeys::ToBuffer (uint8_t * buf, size_t len) const + { + size_t ret = m_Public.ToBuffer (buf, len); + memcpy (buf + ret, m_PrivateKey, 256); // private key always 256 + ret += 256; + size_t signingPrivateKeySize = m_Public.GetSigningPrivateKeyLen (); + memcpy (buf + ret, m_SigningPrivateKey, signingPrivateKeySize); + ret += signingPrivateKeySize; + return ret; + } - size_t PrivateKeys::FromBase64(const std::string& s) - { - uint8_t * buf = new uint8_t[s.length ()]; - size_t l = i2p::data::Base64ToByteStream (s.c_str (), s.length (), buf, s.length ()); - size_t ret = FromBuffer (buf, l); - delete[] buf; - return ret; - } - - std::string PrivateKeys::ToBase64 () const - { - uint8_t * buf = new uint8_t[GetFullLen ()]; - char * str = new char[GetFullLen ()*2]; - size_t l = ToBuffer (buf, GetFullLen ()); - size_t l1 = i2p::data::ByteStreamToBase64 (buf, l, str, GetFullLen ()*2); - str[l1] = 0; - delete[] buf; - std::string ret(str); - delete[] str; - return ret; - } + size_t PrivateKeys::FromBase64(const std::string& s) + { + uint8_t * buf = new uint8_t[s.length ()]; + size_t l = i2p::data::Base64ToByteStream (s.c_str (), s.length (), buf, s.length ()); + size_t ret = FromBuffer (buf, l); + delete[] buf; + return ret; + } + + std::string PrivateKeys::ToBase64 () const + { + uint8_t * buf = new uint8_t[GetFullLen ()]; + char * str = new char[GetFullLen ()*2]; + size_t l = ToBuffer (buf, GetFullLen ()); + size_t l1 = i2p::data::ByteStreamToBase64 (buf, l, str, GetFullLen ()*2); + str[l1] = 0; + delete[] buf; + std::string ret(str); + delete[] str; + return ret; + } - void PrivateKeys::Sign (const uint8_t * buf, int len, uint8_t * signature) const - { - if (m_Signer) - m_Signer->Sign (i2p::context.GetRandomNumberGenerator (), buf, len, signature); - } + void PrivateKeys::Sign (const uint8_t * buf, int len, uint8_t * signature) const + { + if (m_Signer) + m_Signer->Sign (i2p::context.GetRandomNumberGenerator (), buf, len, signature); + } - void PrivateKeys::CreateSigner () - { - switch (m_Public.GetSigningKeyType ()) - { - case SIGNING_KEY_TYPE_DSA_SHA1: - m_Signer = new i2p::crypto::DSASigner (m_SigningPrivateKey); - break; - case SIGNING_KEY_TYPE_ECDSA_SHA256_P256: - m_Signer = new i2p::crypto::ECDSAP256Signer (m_SigningPrivateKey); - break; - case SIGNING_KEY_TYPE_ECDSA_SHA384_P384: - m_Signer = new i2p::crypto::ECDSAP384Signer (m_SigningPrivateKey); - break; - case SIGNING_KEY_TYPE_ECDSA_SHA512_P521: - m_Signer = new i2p::crypto::ECDSAP521Signer (m_SigningPrivateKey); - break; - case SIGNING_KEY_TYPE_RSA_SHA256_2048: - m_Signer = new i2p::crypto::RSASHA2562048Signer (m_SigningPrivateKey); - break; - case SIGNING_KEY_TYPE_RSA_SHA384_3072: - m_Signer = new i2p::crypto::RSASHA3843072Signer (m_SigningPrivateKey); - break; - case SIGNING_KEY_TYPE_RSA_SHA512_4096: - m_Signer = new i2p::crypto::RSASHA5124096Signer (m_SigningPrivateKey); - break; - case SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519: - m_Signer = new i2p::crypto::EDDSA25519Signer (m_SigningPrivateKey); - break; - default: - LogPrint ("Signing key type ", (int)m_Public.GetSigningKeyType (), " is not supported"); - } - } - - PrivateKeys PrivateKeys::CreateRandomKeys (SigningKeyType type) - { - if (type != SIGNING_KEY_TYPE_DSA_SHA1) - { - PrivateKeys keys; - auto& rnd = i2p::context.GetRandomNumberGenerator (); - // signature - uint8_t signingPublicKey[512]; // signing public key is 512 bytes max - switch (type) - { - case SIGNING_KEY_TYPE_ECDSA_SHA256_P256: - i2p::crypto::CreateECDSAP256RandomKeys (rnd, keys.m_SigningPrivateKey, signingPublicKey); - break; - case SIGNING_KEY_TYPE_ECDSA_SHA384_P384: - i2p::crypto::CreateECDSAP384RandomKeys (rnd, keys.m_SigningPrivateKey, signingPublicKey); - break; - case SIGNING_KEY_TYPE_ECDSA_SHA512_P521: - i2p::crypto::CreateECDSAP521RandomKeys (rnd, keys.m_SigningPrivateKey, signingPublicKey); - break; - case SIGNING_KEY_TYPE_RSA_SHA256_2048: - i2p::crypto::CreateRSARandomKeys (rnd, i2p::crypto::RSASHA2562048_KEY_LENGTH, keys.m_SigningPrivateKey, signingPublicKey); - break; - case SIGNING_KEY_TYPE_RSA_SHA384_3072: - i2p::crypto::CreateRSARandomKeys (rnd, i2p::crypto::RSASHA3843072_KEY_LENGTH, keys.m_SigningPrivateKey, signingPublicKey); - break; - case SIGNING_KEY_TYPE_RSA_SHA512_4096: - i2p::crypto::CreateRSARandomKeys (rnd, i2p::crypto::RSASHA5124096_KEY_LENGTH, keys.m_SigningPrivateKey, signingPublicKey); - break; - default: - LogPrint ("Signing key type ", (int)type, " is not supported. Create DSA-SHA1"); - return PrivateKeys (i2p::data::CreateRandomKeys ()); // DSA-SHA1 - } - // encryption - uint8_t publicKey[256]; - CryptoPP::DH dh (i2p::crypto::elgp, i2p::crypto::elgg); - dh.GenerateKeyPair(rnd, keys.m_PrivateKey, publicKey); - // identity - keys.m_Public = IdentityEx (publicKey, signingPublicKey, type); + void PrivateKeys::CreateSigner () + { + switch (m_Public.GetSigningKeyType ()) + { + case SIGNING_KEY_TYPE_DSA_SHA1: + m_Signer = new i2p::crypto::DSASigner (m_SigningPrivateKey); + break; + case SIGNING_KEY_TYPE_ECDSA_SHA256_P256: + m_Signer = new i2p::crypto::ECDSAP256Signer (m_SigningPrivateKey); + break; + case SIGNING_KEY_TYPE_ECDSA_SHA384_P384: + m_Signer = new i2p::crypto::ECDSAP384Signer (m_SigningPrivateKey); + break; + case SIGNING_KEY_TYPE_ECDSA_SHA512_P521: + m_Signer = new i2p::crypto::ECDSAP521Signer (m_SigningPrivateKey); + break; + case SIGNING_KEY_TYPE_RSA_SHA256_2048: + m_Signer = new i2p::crypto::RSASHA2562048Signer (m_SigningPrivateKey); + break; + case SIGNING_KEY_TYPE_RSA_SHA384_3072: + m_Signer = new i2p::crypto::RSASHA3843072Signer (m_SigningPrivateKey); + break; + case SIGNING_KEY_TYPE_RSA_SHA512_4096: + m_Signer = new i2p::crypto::RSASHA5124096Signer (m_SigningPrivateKey); + break; + case SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519: + m_Signer = new i2p::crypto::EDDSA25519Signer (m_SigningPrivateKey); + break; + default: + LogPrint ("Signing key type ", (int)m_Public.GetSigningKeyType (), " is not supported"); + } + } + + PrivateKeys PrivateKeys::CreateRandomKeys (SigningKeyType type) + { + if (type != SIGNING_KEY_TYPE_DSA_SHA1) + { + PrivateKeys keys; + auto& rnd = i2p::context.GetRandomNumberGenerator (); + // signature + uint8_t signingPublicKey[512]; // signing public key is 512 bytes max + switch (type) + { + case SIGNING_KEY_TYPE_ECDSA_SHA256_P256: + i2p::crypto::CreateECDSAP256RandomKeys (rnd, keys.m_SigningPrivateKey, signingPublicKey); + break; + case SIGNING_KEY_TYPE_ECDSA_SHA384_P384: + i2p::crypto::CreateECDSAP384RandomKeys (rnd, keys.m_SigningPrivateKey, signingPublicKey); + break; + case SIGNING_KEY_TYPE_ECDSA_SHA512_P521: + i2p::crypto::CreateECDSAP521RandomKeys (rnd, keys.m_SigningPrivateKey, signingPublicKey); + break; + case SIGNING_KEY_TYPE_RSA_SHA256_2048: + i2p::crypto::CreateRSARandomKeys (rnd, i2p::crypto::RSASHA2562048_KEY_LENGTH, keys.m_SigningPrivateKey, signingPublicKey); + break; + case SIGNING_KEY_TYPE_RSA_SHA384_3072: + i2p::crypto::CreateRSARandomKeys (rnd, i2p::crypto::RSASHA3843072_KEY_LENGTH, keys.m_SigningPrivateKey, signingPublicKey); + break; + case SIGNING_KEY_TYPE_RSA_SHA512_4096: + i2p::crypto::CreateRSARandomKeys (rnd, i2p::crypto::RSASHA5124096_KEY_LENGTH, keys.m_SigningPrivateKey, signingPublicKey); + break; + default: + LogPrint ("Signing key type ", (int)type, " is not supported. Create DSA-SHA1"); + return PrivateKeys (i2p::data::CreateRandomKeys ()); // DSA-SHA1 + } + // encryption + uint8_t publicKey[256]; + CryptoPP::DH dh (i2p::crypto::elgp, i2p::crypto::elgg); + dh.GenerateKeyPair(rnd, keys.m_PrivateKey, publicKey); + // identity + keys.m_Public = IdentityEx (publicKey, signingPublicKey, type); - keys.CreateSigner (); - return keys; - } - return PrivateKeys (i2p::data::CreateRandomKeys ()); // DSA-SHA1 - } - - Keys CreateRandomKeys () - { - Keys keys; - auto& rnd = i2p::context.GetRandomNumberGenerator (); - // encryption - i2p::crypto::GenerateElGamalKeyPair(rnd, keys.privateKey, keys.publicKey); - // signing - i2p::crypto::CreateDSARandomKeys (rnd, keys.signingPrivateKey, keys.signingKey); - return keys; - } + keys.CreateSigner (); + return keys; + } + return PrivateKeys (i2p::data::CreateRandomKeys ()); // DSA-SHA1 + } + + Keys CreateRandomKeys () + { + Keys keys; + auto& rnd = i2p::context.GetRandomNumberGenerator (); + // encryption + i2p::crypto::GenerateElGamalKeyPair(rnd, keys.privateKey, keys.publicKey); + // signing + i2p::crypto::CreateDSARandomKeys (rnd, keys.signingPrivateKey, keys.signingKey); + return keys; + } - IdentHash CreateRoutingKey (const IdentHash& ident) - { - uint8_t buf[41]; // ident + yyyymmdd - memcpy (buf, (const uint8_t *)ident, 32); - time_t t = time (nullptr); - struct tm tm; + IdentHash CreateRoutingKey (const IdentHash& ident) + { + uint8_t buf[41]; // ident + yyyymmdd + memcpy (buf, (const uint8_t *)ident, 32); + time_t t = time (nullptr); + struct tm tm; #ifdef _WIN32 - gmtime_s(&tm, &t); - sprintf_s((char *)(buf + 32), 9, "%04i%02i%02i", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday); + gmtime_s(&tm, &t); + sprintf_s((char *)(buf + 32), 9, "%04i%02i%02i", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday); #else - gmtime_r(&t, &tm); - sprintf((char *)(buf + 32), "%04i%02i%02i", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday); -#endif - IdentHash key; - CryptoPP::SHA256().CalculateDigest((uint8_t *)key, buf, 40); - return key; - } - - XORMetric operator^(const IdentHash& key1, const IdentHash& key2) - { - XORMetric m; - const uint64_t * hash1 = key1.GetLL (), * hash2 = key2.GetLL (); - m.metric_ll[0] = hash1[0] ^ hash2[0]; - m.metric_ll[1] = hash1[1] ^ hash2[1]; - m.metric_ll[2] = hash1[2] ^ hash2[2]; - m.metric_ll[3] = hash1[3] ^ hash2[3]; - return m; - } + gmtime_r(&t, &tm); + sprintf((char *)(buf + 32), "%04i%02i%02i", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday); +#endif + IdentHash key; + CryptoPP::SHA256().CalculateDigest((uint8_t *)key, buf, 40); + return key; + } + + XORMetric operator^(const IdentHash& key1, const IdentHash& key2) + { + XORMetric m; + const uint64_t * hash1 = key1.GetLL (), * hash2 = key2.GetLL (); + m.metric_ll[0] = hash1[0] ^ hash2[0]; + m.metric_ll[1] = hash1[1] ^ hash2[1]; + m.metric_ll[2] = hash1[2] ^ hash2[2]; + m.metric_ll[3] = hash1[3] ^ hash2[3]; + return m; + } } } diff --git a/Identity.h b/Identity.h index 632c414a..4502448b 100644 --- a/Identity.h +++ b/Identity.h @@ -13,261 +13,261 @@ namespace i2p { namespace data { - template - class Tag - { - public: + template + class Tag + { + public: - Tag (const uint8_t * buf) { memcpy (m_Buf, buf, sz); }; - Tag (const Tag& ) = default; + Tag (const uint8_t * buf) { memcpy (m_Buf, buf, sz); }; + Tag (const Tag& ) = default; #ifndef _WIN32 // FIXME!!! msvs 2013 can't compile it - Tag (Tag&& ) = default; + Tag (Tag&& ) = default; #endif - Tag () = default; - - Tag& operator= (const Tag& ) = default; + Tag () = default; + + Tag& operator= (const Tag& ) = default; #ifndef _WIN32 - Tag& operator= (Tag&& ) = default; + Tag& operator= (Tag&& ) = default; #endif - - uint8_t * operator()() { return m_Buf; }; - const uint8_t * operator()() const { return m_Buf; }; + + uint8_t * operator()() { return m_Buf; }; + const uint8_t * operator()() const { return m_Buf; }; - operator uint8_t * () { return m_Buf; }; - operator const uint8_t * () const { return m_Buf; }; - - const uint64_t * GetLL () const { return ll; }; + operator uint8_t * () { return m_Buf; }; + operator const uint8_t * () const { return m_Buf; }; + + const uint64_t * GetLL () const { return ll; }; - bool operator== (const Tag& other) const { return !memcmp (m_Buf, other.m_Buf, sz); }; - bool operator< (const Tag& other) const { return memcmp (m_Buf, other.m_Buf, sz) < 0; }; + bool operator== (const Tag& other) const { return !memcmp (m_Buf, other.m_Buf, sz); }; + bool operator< (const Tag& other) const { return memcmp (m_Buf, other.m_Buf, sz) < 0; }; - bool IsZero () const - { - for (int i = 0; i < sz/8; i++) - if (ll[i]) return false; - return true; - } - - std::string ToBase64 () const - { - char str[sz*2]; - int l = i2p::data::ByteStreamToBase64 (m_Buf, sz, str, sz*2); - str[l] = 0; - return std::string (str); - } + bool IsZero () const + { + for (int i = 0; i < sz/8; i++) + if (ll[i]) return false; + return true; + } + + std::string ToBase64 () const + { + char str[sz*2]; + int l = i2p::data::ByteStreamToBase64 (m_Buf, sz, str, sz*2); + str[l] = 0; + return std::string (str); + } - std::string ToBase32 () const - { - char str[sz*2]; - int l = i2p::data::ByteStreamToBase32 (m_Buf, sz, str, sz*2); - str[l] = 0; - return std::string (str); - } + std::string ToBase32 () const + { + char str[sz*2]; + int l = i2p::data::ByteStreamToBase32 (m_Buf, sz, str, sz*2); + str[l] = 0; + return std::string (str); + } - void FromBase32 (const std::string& s) - { - i2p::data::Base32ToByteStream (s.c_str (), s.length (), m_Buf, sz); - } + void FromBase32 (const std::string& s) + { + i2p::data::Base32ToByteStream (s.c_str (), s.length (), m_Buf, sz); + } - void FromBase64 (const std::string& s) - { - i2p::data::Base64ToByteStream (s.c_str (), s.length (), m_Buf, sz); - } + void FromBase64 (const std::string& s) + { + i2p::data::Base64ToByteStream (s.c_str (), s.length (), m_Buf, sz); + } - private: + private: - union // 8 bytes alignment - { - uint8_t m_Buf[sz]; - uint64_t ll[sz/8]; - }; - }; - typedef Tag<32> IdentHash; + union // 8 bytes alignment + { + uint8_t m_Buf[sz]; + uint64_t ll[sz/8]; + }; + }; + typedef Tag<32> IdentHash; #pragma pack(1) - struct Keys - { - uint8_t privateKey[256]; - uint8_t signingPrivateKey[20]; - uint8_t publicKey[256]; - uint8_t signingKey[128]; - }; - - const uint8_t CERTIFICATE_TYPE_NULL = 0; - const uint8_t CERTIFICATE_TYPE_HASHCASH = 1; - const uint8_t CERTIFICATE_TYPE_HIDDEN = 2; - const uint8_t CERTIFICATE_TYPE_SIGNED = 3; - const uint8_t CERTIFICATE_TYPE_MULTIPLE = 4; - const uint8_t CERTIFICATE_TYPE_KEY = 5; + struct Keys + { + uint8_t privateKey[256]; + uint8_t signingPrivateKey[20]; + uint8_t publicKey[256]; + uint8_t signingKey[128]; + }; + + const uint8_t CERTIFICATE_TYPE_NULL = 0; + const uint8_t CERTIFICATE_TYPE_HASHCASH = 1; + const uint8_t CERTIFICATE_TYPE_HIDDEN = 2; + const uint8_t CERTIFICATE_TYPE_SIGNED = 3; + const uint8_t CERTIFICATE_TYPE_MULTIPLE = 4; + const uint8_t CERTIFICATE_TYPE_KEY = 5; - struct Identity - { - uint8_t publicKey[256]; - uint8_t signingKey[128]; - struct - { - uint8_t type; - uint16_t length; - } certificate; + struct Identity + { + uint8_t publicKey[256]; + uint8_t signingKey[128]; + struct + { + uint8_t type; + uint16_t length; + } certificate; - Identity () = default; - Identity (const Keys& keys) { *this = keys; }; - Identity& operator=(const Keys& keys); - size_t FromBuffer (const uint8_t * buf, size_t len); - IdentHash Hash () const; - }; + Identity () = default; + Identity (const Keys& keys) { *this = keys; }; + Identity& operator=(const Keys& keys); + size_t FromBuffer (const uint8_t * buf, size_t len); + IdentHash Hash () const; + }; #pragma pack() - Keys CreateRandomKeys (); - - const size_t DEFAULT_IDENTITY_SIZE = sizeof (Identity); // 387 bytes - - const uint16_t CRYPTO_KEY_TYPE_ELGAMAL = 0; - const uint16_t SIGNING_KEY_TYPE_DSA_SHA1 = 0; - const uint16_t SIGNING_KEY_TYPE_ECDSA_SHA256_P256 = 1; - const uint16_t SIGNING_KEY_TYPE_ECDSA_SHA384_P384 = 2; - const uint16_t SIGNING_KEY_TYPE_ECDSA_SHA512_P521 = 3; - const uint16_t SIGNING_KEY_TYPE_RSA_SHA256_2048 = 4; - const uint16_t SIGNING_KEY_TYPE_RSA_SHA384_3072 = 5; - const uint16_t SIGNING_KEY_TYPE_RSA_SHA512_4096 = 6; - const uint16_t SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519 = 7; - typedef uint16_t SigningKeyType; - typedef uint16_t CryptoKeyType; - - class IdentityEx - { - public: + Keys CreateRandomKeys (); + + const size_t DEFAULT_IDENTITY_SIZE = sizeof (Identity); // 387 bytes + + const uint16_t CRYPTO_KEY_TYPE_ELGAMAL = 0; + const uint16_t SIGNING_KEY_TYPE_DSA_SHA1 = 0; + const uint16_t SIGNING_KEY_TYPE_ECDSA_SHA256_P256 = 1; + const uint16_t SIGNING_KEY_TYPE_ECDSA_SHA384_P384 = 2; + const uint16_t SIGNING_KEY_TYPE_ECDSA_SHA512_P521 = 3; + const uint16_t SIGNING_KEY_TYPE_RSA_SHA256_2048 = 4; + const uint16_t SIGNING_KEY_TYPE_RSA_SHA384_3072 = 5; + const uint16_t SIGNING_KEY_TYPE_RSA_SHA512_4096 = 6; + const uint16_t SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519 = 7; + typedef uint16_t SigningKeyType; + typedef uint16_t CryptoKeyType; + + class IdentityEx + { + public: - IdentityEx (); - IdentityEx (const uint8_t * publicKey, const uint8_t * signingKey, - SigningKeyType type = SIGNING_KEY_TYPE_DSA_SHA1); - IdentityEx (const uint8_t * buf, size_t len); - IdentityEx (const IdentityEx& other); - ~IdentityEx (); - IdentityEx& operator=(const IdentityEx& other); - IdentityEx& operator=(const Identity& standard); + IdentityEx (); + IdentityEx (const uint8_t * publicKey, const uint8_t * signingKey, + SigningKeyType type = SIGNING_KEY_TYPE_DSA_SHA1); + IdentityEx (const uint8_t * buf, size_t len); + IdentityEx (const IdentityEx& other); + ~IdentityEx (); + IdentityEx& operator=(const IdentityEx& other); + IdentityEx& operator=(const Identity& standard); - size_t FromBuffer (const uint8_t * buf, size_t len); - size_t ToBuffer (uint8_t * buf, size_t len) const; - size_t FromBase64(const std::string& s); - std::string ToBase64 () const; - const Identity& GetStandardIdentity () const { return m_StandardIdentity; }; - const IdentHash& GetIdentHash () const { return m_IdentHash; }; - size_t GetFullLen () const { return m_ExtendedLen + DEFAULT_IDENTITY_SIZE; }; - size_t GetSigningPublicKeyLen () const; - size_t GetSigningPrivateKeyLen () const; - size_t GetSignatureLen () const; - bool Verify (const uint8_t * buf, size_t len, const uint8_t * signature) const; - SigningKeyType GetSigningKeyType () const; - CryptoKeyType GetCryptoKeyType () const; - void DropVerifier (); // to save memory + size_t FromBuffer (const uint8_t * buf, size_t len); + size_t ToBuffer (uint8_t * buf, size_t len) const; + size_t FromBase64(const std::string& s); + std::string ToBase64 () const; + const Identity& GetStandardIdentity () const { return m_StandardIdentity; }; + const IdentHash& GetIdentHash () const { return m_IdentHash; }; + size_t GetFullLen () const { return m_ExtendedLen + DEFAULT_IDENTITY_SIZE; }; + size_t GetSigningPublicKeyLen () const; + size_t GetSigningPrivateKeyLen () const; + size_t GetSignatureLen () const; + bool Verify (const uint8_t * buf, size_t len, const uint8_t * signature) const; + SigningKeyType GetSigningKeyType () const; + CryptoKeyType GetCryptoKeyType () const; + void DropVerifier (); // to save memory - private: + private: - void CreateVerifier () const; - - private: + void CreateVerifier () const; + + private: - Identity m_StandardIdentity; - IdentHash m_IdentHash; - mutable i2p::crypto::Verifier * m_Verifier; - size_t m_ExtendedLen; - uint8_t * m_ExtendedBuffer; - }; - - class PrivateKeys // for eepsites - { - public: - - PrivateKeys (): m_Signer (nullptr) {}; - PrivateKeys (const PrivateKeys& other): m_Signer (nullptr) { *this = other; }; - PrivateKeys (const Keys& keys): m_Signer (nullptr) { *this = keys; }; - PrivateKeys& operator=(const Keys& keys); - PrivateKeys& operator=(const PrivateKeys& other); - ~PrivateKeys () { delete m_Signer; }; - - const IdentityEx& GetPublic () const { return m_Public; }; - const uint8_t * GetPrivateKey () const { return m_PrivateKey; }; - const uint8_t * GetSigningPrivateKey () const { return m_SigningPrivateKey; }; - void Sign (const uint8_t * buf, int len, uint8_t * signature) const; + Identity m_StandardIdentity; + IdentHash m_IdentHash; + mutable i2p::crypto::Verifier * m_Verifier; + size_t m_ExtendedLen; + uint8_t * m_ExtendedBuffer; + }; + + class PrivateKeys // for eepsites + { + public: + + PrivateKeys (): m_Signer (nullptr) {}; + PrivateKeys (const PrivateKeys& other): m_Signer (nullptr) { *this = other; }; + PrivateKeys (const Keys& keys): m_Signer (nullptr) { *this = keys; }; + PrivateKeys& operator=(const Keys& keys); + PrivateKeys& operator=(const PrivateKeys& other); + ~PrivateKeys () { delete m_Signer; }; + + const IdentityEx& GetPublic () const { return m_Public; }; + const uint8_t * GetPrivateKey () const { return m_PrivateKey; }; + const uint8_t * GetSigningPrivateKey () const { return m_SigningPrivateKey; }; + void Sign (const uint8_t * buf, int len, uint8_t * signature) const; - size_t GetFullLen () const { return m_Public.GetFullLen () + 256 + m_Public.GetSigningPrivateKeyLen (); }; - size_t FromBuffer (const uint8_t * buf, size_t len); - size_t ToBuffer (uint8_t * buf, size_t len) const; + size_t GetFullLen () const { return m_Public.GetFullLen () + 256 + m_Public.GetSigningPrivateKeyLen (); }; + size_t FromBuffer (const uint8_t * buf, size_t len); + size_t ToBuffer (uint8_t * buf, size_t len) const; - size_t FromBase64(const std::string& s); - std::string ToBase64 () const; + size_t FromBase64(const std::string& s); + std::string ToBase64 () const; - static PrivateKeys CreateRandomKeys (SigningKeyType type = SIGNING_KEY_TYPE_DSA_SHA1); - - private: + static PrivateKeys CreateRandomKeys (SigningKeyType type = SIGNING_KEY_TYPE_DSA_SHA1); + + private: - void CreateSigner (); - - private: + void CreateSigner (); + + private: - IdentityEx m_Public; - uint8_t m_PrivateKey[256]; - uint8_t m_SigningPrivateKey[1024]; // assume private key doesn't exceed 1024 bytes - i2p::crypto::Signer * m_Signer; - }; + IdentityEx m_Public; + uint8_t m_PrivateKey[256]; + uint8_t m_SigningPrivateKey[1024]; // assume private key doesn't exceed 1024 bytes + i2p::crypto::Signer * m_Signer; + }; - // kademlia - struct XORMetric - { - union - { - uint8_t metric[32]; - uint64_t metric_ll[4]; - }; + // kademlia + struct XORMetric + { + union + { + uint8_t metric[32]; + uint64_t metric_ll[4]; + }; - void SetMin () { memset (metric, 0, 32); }; - void SetMax () { memset (metric, 0xFF, 32); }; - bool operator< (const XORMetric& other) const { return memcmp (metric, other.metric, 32) < 0; }; - }; + void SetMin () { memset (metric, 0, 32); }; + void SetMax () { memset (metric, 0xFF, 32); }; + bool operator< (const XORMetric& other) const { return memcmp (metric, other.metric, 32) < 0; }; + }; - IdentHash CreateRoutingKey (const IdentHash& ident); - XORMetric operator^(const IdentHash& key1, const IdentHash& key2); - - // destination for delivery instuctions - class RoutingDestination - { - public: + IdentHash CreateRoutingKey (const IdentHash& ident); + XORMetric operator^(const IdentHash& key1, const IdentHash& key2); + + // destination for delivery instuctions + class RoutingDestination + { + public: - RoutingDestination () {}; - virtual ~RoutingDestination () {}; - - virtual const IdentHash& GetIdentHash () const = 0; - virtual const uint8_t * GetEncryptionPublicKey () const = 0; - virtual bool IsDestination () const = 0; // for garlic + RoutingDestination () {}; + virtual ~RoutingDestination () {}; + + virtual const IdentHash& GetIdentHash () const = 0; + virtual const uint8_t * GetEncryptionPublicKey () const = 0; + virtual bool IsDestination () const = 0; // for garlic - std::unique_ptr& GetElGamalEncryption () const - { - if (!m_ElGamalEncryption) - m_ElGamalEncryption.reset (new i2p::crypto::ElGamalEncryption (GetEncryptionPublicKey ())); - return m_ElGamalEncryption; - } - - private: + std::unique_ptr& GetElGamalEncryption () const + { + if (!m_ElGamalEncryption) + m_ElGamalEncryption.reset (new i2p::crypto::ElGamalEncryption (GetEncryptionPublicKey ())); + return m_ElGamalEncryption; + } + + private: - mutable std::unique_ptr m_ElGamalEncryption; // use lazy initialization - }; + mutable std::unique_ptr m_ElGamalEncryption; // use lazy initialization + }; - class LocalDestination - { - public: + class LocalDestination + { + public: - virtual ~LocalDestination() {}; - virtual const PrivateKeys& GetPrivateKeys () const = 0; - virtual const uint8_t * GetEncryptionPrivateKey () const = 0; - virtual const uint8_t * GetEncryptionPublicKey () const = 0; + virtual ~LocalDestination() {}; + virtual const PrivateKeys& GetPrivateKeys () const = 0; + virtual const uint8_t * GetEncryptionPrivateKey () const = 0; + virtual const uint8_t * GetEncryptionPublicKey () const = 0; - const IdentityEx& GetIdentity () const { return GetPrivateKeys ().GetPublic (); }; - const IdentHash& GetIdentHash () const { return GetIdentity ().GetIdentHash (); }; - void Sign (const uint8_t * buf, int len, uint8_t * signature) const - { - GetPrivateKeys ().Sign (buf, len, signature); - }; - }; + const IdentityEx& GetIdentity () const { return GetPrivateKeys ().GetPublic (); }; + const IdentHash& GetIdentHash () const { return GetIdentity ().GetIdentHash (); }; + void Sign (const uint8_t * buf, int len, uint8_t * signature) const + { + GetPrivateKeys ().Sign (buf, len, signature); + }; + }; } } diff --git a/LeaseSet.cpp b/LeaseSet.cpp index 77d11e1a..f648a44c 100644 --- a/LeaseSet.cpp +++ b/LeaseSet.cpp @@ -13,145 +13,145 @@ namespace i2p { namespace data { - - LeaseSet::LeaseSet (const uint8_t * buf, size_t len): - m_IsValid (true) - { - m_Buffer = new uint8_t[len]; - memcpy (m_Buffer, buf, len); - m_BufferLen = len; - ReadFromBuffer (); - } + + LeaseSet::LeaseSet (const uint8_t * buf, size_t len): + m_IsValid (true) + { + m_Buffer = new uint8_t[len]; + memcpy (m_Buffer, buf, len); + m_BufferLen = len; + ReadFromBuffer (); + } - LeaseSet::LeaseSet (const i2p::tunnel::TunnelPool& pool): - m_IsValid (true) - { - // header - const i2p::data::LocalDestination * localDestination = pool.GetLocalDestination (); - if (!localDestination) - { - m_Buffer = nullptr; - m_BufferLen = 0; - m_IsValid = false; - LogPrint (eLogError, "Destination for local LeaseSet doesn't exist"); - return; - } - m_Buffer = new uint8_t[MAX_LS_BUFFER_SIZE]; - m_BufferLen = localDestination->GetIdentity ().ToBuffer (m_Buffer, MAX_LS_BUFFER_SIZE); - memcpy (m_Buffer + m_BufferLen, localDestination->GetEncryptionPublicKey (), 256); - m_BufferLen += 256; - auto signingKeyLen = localDestination->GetIdentity ().GetSigningPublicKeyLen (); - memset (m_Buffer + m_BufferLen, 0, signingKeyLen); - m_BufferLen += signingKeyLen; - auto tunnels = pool.GetInboundTunnels (5); // 5 tunnels maximum - m_Buffer[m_BufferLen] = tunnels.size (); // num leases - m_BufferLen++; - // leases - CryptoPP::AutoSeededRandomPool rnd; - for (auto it: tunnels) - { - memcpy (m_Buffer + m_BufferLen, it->GetNextIdentHash (), 32); - m_BufferLen += 32; // gateway id - htobe32buf (m_Buffer + m_BufferLen, it->GetNextTunnelID ()); - m_BufferLen += 4; // tunnel id - uint64_t ts = it->GetCreationTime () + i2p::tunnel::TUNNEL_EXPIRATION_TIMEOUT - i2p::tunnel::TUNNEL_EXPIRATION_THRESHOLD; // 1 minute before expiration - ts *= 1000; // in milliseconds - ts += rnd.GenerateWord32 (0, 5); // + random milliseconds - htobe64buf (m_Buffer + m_BufferLen, ts); - m_BufferLen += 8; // end date - } - // signature - localDestination->Sign (m_Buffer, m_BufferLen, m_Buffer + m_BufferLen); - m_BufferLen += localDestination->GetIdentity ().GetSignatureLen (); - LogPrint ("Local LeaseSet of ", tunnels.size (), " leases created"); + LeaseSet::LeaseSet (const i2p::tunnel::TunnelPool& pool): + m_IsValid (true) + { + // header + const i2p::data::LocalDestination * localDestination = pool.GetLocalDestination (); + if (!localDestination) + { + m_Buffer = nullptr; + m_BufferLen = 0; + m_IsValid = false; + LogPrint (eLogError, "Destination for local LeaseSet doesn't exist"); + return; + } + m_Buffer = new uint8_t[MAX_LS_BUFFER_SIZE]; + m_BufferLen = localDestination->GetIdentity ().ToBuffer (m_Buffer, MAX_LS_BUFFER_SIZE); + memcpy (m_Buffer + m_BufferLen, localDestination->GetEncryptionPublicKey (), 256); + m_BufferLen += 256; + auto signingKeyLen = localDestination->GetIdentity ().GetSigningPublicKeyLen (); + memset (m_Buffer + m_BufferLen, 0, signingKeyLen); + m_BufferLen += signingKeyLen; + auto tunnels = pool.GetInboundTunnels (5); // 5 tunnels maximum + m_Buffer[m_BufferLen] = tunnels.size (); // num leases + m_BufferLen++; + // leases + CryptoPP::AutoSeededRandomPool rnd; + for (auto it: tunnels) + { + memcpy (m_Buffer + m_BufferLen, it->GetNextIdentHash (), 32); + m_BufferLen += 32; // gateway id + htobe32buf (m_Buffer + m_BufferLen, it->GetNextTunnelID ()); + m_BufferLen += 4; // tunnel id + uint64_t ts = it->GetCreationTime () + i2p::tunnel::TUNNEL_EXPIRATION_TIMEOUT - i2p::tunnel::TUNNEL_EXPIRATION_THRESHOLD; // 1 minute before expiration + ts *= 1000; // in milliseconds + ts += rnd.GenerateWord32 (0, 5); // + random milliseconds + htobe64buf (m_Buffer + m_BufferLen, ts); + m_BufferLen += 8; // end date + } + // signature + localDestination->Sign (m_Buffer, m_BufferLen, m_Buffer + m_BufferLen); + m_BufferLen += localDestination->GetIdentity ().GetSignatureLen (); + LogPrint ("Local LeaseSet of ", tunnels.size (), " leases created"); - ReadFromBuffer (); - } + ReadFromBuffer (); + } - void LeaseSet::Update (const uint8_t * buf, size_t len) - { - m_Leases.clear (); - if (len > m_BufferLen) - { - auto oldBuffer = m_Buffer; - m_Buffer = new uint8_t[len]; - delete[] oldBuffer; - } - memcpy (m_Buffer, buf, len); - m_BufferLen = len; - ReadFromBuffer (); - } - - void LeaseSet::ReadFromBuffer () - { - size_t size = m_Identity.FromBuffer (m_Buffer, m_BufferLen); - memcpy (m_EncryptionKey, m_Buffer + size, 256); - size += 256; // encryption key - size += m_Identity.GetSigningPublicKeyLen (); // unused signing key - uint8_t num = m_Buffer[size]; - size++; // num - LogPrint ("LeaseSet num=", (int)num); - if (!num) m_IsValid = false; + void LeaseSet::Update (const uint8_t * buf, size_t len) + { + m_Leases.clear (); + if (len > m_BufferLen) + { + auto oldBuffer = m_Buffer; + m_Buffer = new uint8_t[len]; + delete[] oldBuffer; + } + memcpy (m_Buffer, buf, len); + m_BufferLen = len; + ReadFromBuffer (); + } + + void LeaseSet::ReadFromBuffer () + { + size_t size = m_Identity.FromBuffer (m_Buffer, m_BufferLen); + memcpy (m_EncryptionKey, m_Buffer + size, 256); + size += 256; // encryption key + size += m_Identity.GetSigningPublicKeyLen (); // unused signing key + uint8_t num = m_Buffer[size]; + size++; // num + LogPrint ("LeaseSet num=", (int)num); + if (!num) m_IsValid = false; - // process leases - const uint8_t * leases = m_Buffer + size; - for (int i = 0; i < num; i++) - { - Lease lease; - lease.tunnelGateway = leases; - leases += 32; // gateway - lease.tunnelID = bufbe32toh (leases); - leases += 4; // tunnel ID - lease.endDate = bufbe64toh (leases); - leases += 8; // end date - m_Leases.push_back (lease); + // process leases + const uint8_t * leases = m_Buffer + size; + for (int i = 0; i < num; i++) + { + Lease lease; + lease.tunnelGateway = leases; + leases += 32; // gateway + lease.tunnelID = bufbe32toh (leases); + leases += 4; // tunnel ID + lease.endDate = bufbe64toh (leases); + leases += 8; // end date + m_Leases.push_back (lease); - // check if lease's gateway is in our netDb - if (!netdb.FindRouter (lease.tunnelGateway)) - { - // if not found request it - LogPrint (eLogInfo, "Lease's tunnel gateway not found. Requested"); - netdb.RequestDestination (lease.tunnelGateway); - } - } - - // verify - if (!m_Identity.Verify (m_Buffer, leases - m_Buffer, leases)) - { - LogPrint (eLogWarning, "LeaseSet verification failed"); - m_IsValid = false; - } - } - - const std::vector LeaseSet::GetNonExpiredLeases (bool withThreshold) const - { - auto ts = i2p::util::GetMillisecondsSinceEpoch (); - std::vector leases; - for (auto& it: m_Leases) - { - auto endDate = it.endDate; - if (!withThreshold) - endDate -= i2p::tunnel::TUNNEL_EXPIRATION_THRESHOLD*1000; - if (ts < endDate) - leases.push_back (it); - } - return leases; - } + // check if lease's gateway is in our netDb + if (!netdb.FindRouter (lease.tunnelGateway)) + { + // if not found request it + LogPrint (eLogInfo, "Lease's tunnel gateway not found. Requested"); + netdb.RequestDestination (lease.tunnelGateway); + } + } + + // verify + if (!m_Identity.Verify (m_Buffer, leases - m_Buffer, leases)) + { + LogPrint (eLogWarning, "LeaseSet verification failed"); + m_IsValid = false; + } + } + + const std::vector LeaseSet::GetNonExpiredLeases (bool withThreshold) const + { + auto ts = i2p::util::GetMillisecondsSinceEpoch (); + std::vector leases; + for (auto& it: m_Leases) + { + auto endDate = it.endDate; + if (!withThreshold) + endDate -= i2p::tunnel::TUNNEL_EXPIRATION_THRESHOLD*1000; + if (ts < endDate) + leases.push_back (it); + } + return leases; + } - bool LeaseSet::HasExpiredLeases () const - { - auto ts = i2p::util::GetMillisecondsSinceEpoch (); - for (auto& it: m_Leases) - if (ts >= it.endDate) return true; - return false; - } + bool LeaseSet::HasExpiredLeases () const + { + auto ts = i2p::util::GetMillisecondsSinceEpoch (); + for (auto& it: m_Leases) + if (ts >= it.endDate) return true; + return false; + } - bool LeaseSet::HasNonExpiredLeases () const - { - auto ts = i2p::util::GetMillisecondsSinceEpoch (); - for (auto& it: m_Leases) - if (ts < it.endDate) return true; - return false; - } -} -} + bool LeaseSet::HasNonExpiredLeases () const + { + auto ts = i2p::util::GetMillisecondsSinceEpoch (); + for (auto& it: m_Leases) + if (ts < it.endDate) return true; + return false; + } +} +} diff --git a/LeaseSet.h b/LeaseSet.h index fee130ab..2b40e3e9 100644 --- a/LeaseSet.h +++ b/LeaseSet.h @@ -11,64 +11,64 @@ namespace i2p namespace tunnel { - class TunnelPool; + class TunnelPool; } namespace data -{ - struct Lease - { - IdentHash tunnelGateway; - uint32_t tunnelID; - uint64_t endDate; +{ + struct Lease + { + IdentHash tunnelGateway; + uint32_t tunnelID; + uint64_t endDate; - bool operator< (const Lease& other) const - { - if (endDate != other.endDate) - return endDate > other.endDate; - else - return tunnelID < other.tunnelID; - } - }; + bool operator< (const Lease& other) const + { + if (endDate != other.endDate) + return endDate > other.endDate; + else + return tunnelID < other.tunnelID; + } + }; - const int MAX_LS_BUFFER_SIZE = 3072; - class LeaseSet: public RoutingDestination - { - public: + const int MAX_LS_BUFFER_SIZE = 3072; + class LeaseSet: public RoutingDestination + { + public: - LeaseSet (const uint8_t * buf, size_t len); - LeaseSet (const i2p::tunnel::TunnelPool& pool); - ~LeaseSet () { delete[] m_Buffer; }; - void Update (const uint8_t * buf, size_t len); - const IdentityEx& GetIdentity () const { return m_Identity; }; + LeaseSet (const uint8_t * buf, size_t len); + LeaseSet (const i2p::tunnel::TunnelPool& pool); + ~LeaseSet () { delete[] m_Buffer; }; + void Update (const uint8_t * buf, size_t len); + const IdentityEx& GetIdentity () const { return m_Identity; }; - const uint8_t * GetBuffer () const { return m_Buffer; }; - size_t GetBufferLen () const { return m_BufferLen; }; - bool IsValid () const { return m_IsValid; }; + const uint8_t * GetBuffer () const { return m_Buffer; }; + size_t GetBufferLen () const { return m_BufferLen; }; + bool IsValid () const { return m_IsValid; }; - // implements RoutingDestination - const IdentHash& GetIdentHash () const { return m_Identity.GetIdentHash (); }; - const std::vector& GetLeases () const { return m_Leases; }; - const std::vector GetNonExpiredLeases (bool withThreshold = true) const; - bool HasExpiredLeases () const; - bool HasNonExpiredLeases () const; - const uint8_t * GetEncryptionPublicKey () const { return m_EncryptionKey; }; - bool IsDestination () const { return true; }; + // implements RoutingDestination + const IdentHash& GetIdentHash () const { return m_Identity.GetIdentHash (); }; + const std::vector& GetLeases () const { return m_Leases; }; + const std::vector GetNonExpiredLeases (bool withThreshold = true) const; + bool HasExpiredLeases () const; + bool HasNonExpiredLeases () const; + const uint8_t * GetEncryptionPublicKey () const { return m_EncryptionKey; }; + bool IsDestination () const { return true; }; - private: + private: - void ReadFromBuffer (); - - private: + void ReadFromBuffer (); + + private: - bool m_IsValid; - std::vector m_Leases; - IdentityEx m_Identity; - uint8_t m_EncryptionKey[256]; - uint8_t * m_Buffer; - size_t m_BufferLen; - }; -} -} + bool m_IsValid; + std::vector m_Leases; + IdentityEx m_Identity; + uint8_t m_EncryptionKey[256]; + uint8_t * m_Buffer; + size_t m_BufferLen; + }; +} +} #endif diff --git a/LittleBigEndian.h b/LittleBigEndian.h index 69f10ee9..3c25a6ee 100644 --- a/LittleBigEndian.h +++ b/LittleBigEndian.h @@ -59,12 +59,12 @@ struct LittleEndian return t; } - const T operator = (const T t) - { - for (unsigned i = 0; i < sizeof(T); i++) - bytes[sizeof(T)-1 - i] = static_cast(t >> (i << 3)); - return t; - } + const T operator = (const T t) + { + for (unsigned i = 0; i < sizeof(T); i++) + bytes[sizeof(T)-1 - i] = static_cast(t >> (i << 3)); + return t; + } // operators diff --git a/Log.cpp b/Log.cpp index 0a561e0c..07730b82 100644 --- a/Log.cpp +++ b/Log.cpp @@ -5,58 +5,58 @@ Log * g_Log = nullptr; static const char * g_LogLevelStr[eNumLogLevels] = { - "error", // eLogError - "warn", // eLogWarning - "info", // eLogInfo - "debug" // eLogDebug + "error", // eLogError + "warn", // eLogWarning + "info", // eLogInfo + "debug" // eLogDebug }; void LogMsg::Process() { - auto& output = (log && log->GetLogStream ()) ? *log->GetLogStream () : std::cerr; - if (log) - output << log->GetTimestamp (); - else - output << boost::posix_time::second_clock::local_time().time_of_day (); - output << "/" << g_LogLevelStr[level] << " - "; - output << s.str(); + auto& output = (log && log->GetLogStream ()) ? *log->GetLogStream () : std::cerr; + if (log) + output << log->GetTimestamp (); + else + output << boost::posix_time::second_clock::local_time().time_of_day (); + output << "/" << g_LogLevelStr[level] << " - "; + output << s.str(); } const std::string& Log::GetTimestamp () { -#if (__GNUC__ == 4) && (__GNUC_MINOR__ <= 6) - auto ts = std::chrono::monotonic_clock::now (); -#else - auto ts = std::chrono::steady_clock::now (); -#endif - if (ts > m_LastTimestampUpdate + std::chrono::milliseconds (500)) // 0.5 second - { - m_LastTimestampUpdate = ts; - m_Timestamp = boost::posix_time::to_simple_string (boost::posix_time::second_clock::local_time().time_of_day ()); - } - return m_Timestamp; +#if (__GNUC__ == 4) && (__GNUC_MINOR__ <= 6) + auto ts = std::chrono::monotonic_clock::now (); +#else + auto ts = std::chrono::steady_clock::now (); +#endif + if (ts > m_LastTimestampUpdate + std::chrono::milliseconds (500)) // 0.5 second + { + m_LastTimestampUpdate = ts; + m_Timestamp = boost::posix_time::to_simple_string (boost::posix_time::second_clock::local_time().time_of_day ()); + } + return m_Timestamp; } void Log::Flush () { - if (m_LogStream) - m_LogStream->flush(); + if (m_LogStream) + m_LogStream->flush(); } void Log::SetLogFile (const std::string& fullFilePath) { - auto logFile = new std::ofstream (fullFilePath, std::ofstream::out | std::ofstream::binary | std::ofstream::trunc); - if (logFile->is_open ()) - { - SetLogStream (logFile); - LogPrint("Logging to file ", fullFilePath, " enabled."); - } - else - delete logFile; + auto logFile = new std::ofstream (fullFilePath, std::ofstream::out | std::ofstream::binary | std::ofstream::trunc); + if (logFile->is_open ()) + { + SetLogStream (logFile); + LogPrint("Logging to file ", fullFilePath, " enabled."); + } + else + delete logFile; } void Log::SetLogStream (std::ostream * logStream) { - if (m_LogStream) delete m_LogStream; - m_LogStream = logStream; + if (m_LogStream) delete m_LogStream; + m_LogStream = logStream; } diff --git a/Log.h b/Log.h index ea821954..cd82dec6 100644 --- a/Log.h +++ b/Log.h @@ -11,119 +11,119 @@ enum LogLevel { - eLogError = 0, - eLogWarning, - eLogInfo, - eLogDebug, - eNumLogLevels + eLogError = 0, + eLogWarning, + eLogInfo, + eLogDebug, + eNumLogLevels }; class Log; struct LogMsg { - std::stringstream s; - Log * log; - LogLevel level; + std::stringstream s; + Log * log; + LogLevel level; - LogMsg (Log * l = nullptr, LogLevel lv = eLogInfo): log (l), level (lv) {}; - - void Process(); + LogMsg (Log * l = nullptr, LogLevel lv = eLogInfo): log (l), level (lv) {}; + + void Process(); }; class Log: public i2p::util::MsgQueue { - public: + public: - Log (): m_LogStream (nullptr) { SetOnEmpty (std::bind (&Log::Flush, this)); }; - ~Log () { delete m_LogStream; }; + Log (): m_LogStream (nullptr) { SetOnEmpty (std::bind (&Log::Flush, this)); }; + ~Log () { delete m_LogStream; }; - void SetLogFile (const std::string& fullFilePath); - void SetLogStream (std::ostream * logStream); - std::ostream * GetLogStream () const { return m_LogStream; }; - const std::string& GetTimestamp (); + void SetLogFile (const std::string& fullFilePath); + void SetLogStream (std::ostream * logStream); + std::ostream * GetLogStream () const { return m_LogStream; }; + const std::string& GetTimestamp (); - private: + private: - void Flush (); + void Flush (); - private: - - std::ostream * m_LogStream; - std::string m_Timestamp; + private: + + std::ostream * m_LogStream; + std::string m_Timestamp; #if (__GNUC__ == 4) && (__GNUC_MINOR__ <= 6) // gcc 4.6 - std::chrono::monotonic_clock::time_point m_LastTimestampUpdate; -#else - std::chrono::steady_clock::time_point m_LastTimestampUpdate; -#endif + std::chrono::monotonic_clock::time_point m_LastTimestampUpdate; +#else + std::chrono::steady_clock::time_point m_LastTimestampUpdate; +#endif }; extern Log * g_Log; inline void StartLog (const std::string& fullFilePath) { - if (!g_Log) - { - auto log = new Log (); - if (fullFilePath.length () > 0) - log->SetLogFile (fullFilePath); - g_Log = log; - } + if (!g_Log) + { + auto log = new Log (); + if (fullFilePath.length () > 0) + log->SetLogFile (fullFilePath); + g_Log = log; + } } inline void StartLog (std::ostream * s) { - if (!g_Log) - { - auto log = new Log (); - if (s) - log->SetLogStream (s); - g_Log = log; - } + if (!g_Log) + { + auto log = new Log (); + if (s) + log->SetLogStream (s); + g_Log = log; + } } inline void StopLog () { - if (g_Log) - { - auto log = g_Log; - g_Log = nullptr; - log->Stop (); - delete log; - } + if (g_Log) + { + auto log = g_Log; + g_Log = nullptr; + log->Stop (); + delete log; + } } template void LogPrint (std::stringstream& s, TValue arg) { - s << arg; + s << arg; } template void LogPrint (std::stringstream& s, TValue arg, TArgs... args) { - LogPrint (s, arg); - LogPrint (s, args...); + LogPrint (s, arg); + LogPrint (s, args...); } template void LogPrint (LogLevel level, TArgs... args) { - LogMsg * msg = new LogMsg (g_Log, level); - LogPrint (msg->s, args...); - msg->s << std::endl; - if (g_Log) - g_Log->Put (msg); - else - { - msg->Process (); - delete msg; - } + LogMsg * msg = new LogMsg (g_Log, level); + LogPrint (msg->s, args...); + msg->s << std::endl; + if (g_Log) + g_Log->Put (msg); + else + { + msg->Process (); + delete msg; + } } template void LogPrint (TArgs... args) { - LogPrint (eLogInfo, args...); -} + LogPrint (eLogInfo, args...); +} #endif diff --git a/NTCPSession.cpp b/NTCPSession.cpp index 878beab7..c3d64221 100644 --- a/NTCPSession.cpp +++ b/NTCPSession.cpp @@ -19,918 +19,918 @@ namespace i2p { namespace transport { - NTCPSession::NTCPSession (NTCPServer& server, std::shared_ptr in_RemoteRouter): - TransportSession (in_RemoteRouter), m_Server (server), m_Socket (m_Server.GetService ()), - m_TerminationTimer (m_Server.GetService ()), m_IsEstablished (false), m_IsTerminated (false), - m_ReceiveBufferOffset (0), m_NextMessage (nullptr), m_IsSending (false) - { - m_DHKeysPair = transports.GetNextDHKeysPair (); - m_Establisher = new Establisher; - } - - NTCPSession::~NTCPSession () - { - delete m_Establisher; - } + NTCPSession::NTCPSession (NTCPServer& server, std::shared_ptr in_RemoteRouter): + TransportSession (in_RemoteRouter), m_Server (server), m_Socket (m_Server.GetService ()), + m_TerminationTimer (m_Server.GetService ()), m_IsEstablished (false), m_IsTerminated (false), + m_ReceiveBufferOffset (0), m_NextMessage (nullptr), m_IsSending (false) + { + m_DHKeysPair = transports.GetNextDHKeysPair (); + m_Establisher = new Establisher; + } + + NTCPSession::~NTCPSession () + { + delete m_Establisher; + } - void NTCPSession::CreateAESKey (uint8_t * pubKey, i2p::crypto::AESKey& key) - { - CryptoPP::DH dh (elgp, elgg); - uint8_t sharedKey[256]; - if (!dh.Agree (sharedKey, m_DHKeysPair->privateKey, pubKey)) - { - LogPrint (eLogError, "Couldn't create shared key"); - Terminate (); - return; - }; + void NTCPSession::CreateAESKey (uint8_t * pubKey, i2p::crypto::AESKey& key) + { + CryptoPP::DH dh (elgp, elgg); + uint8_t sharedKey[256]; + if (!dh.Agree (sharedKey, m_DHKeysPair->privateKey, pubKey)) + { + LogPrint (eLogError, "Couldn't create shared key"); + Terminate (); + return; + }; - uint8_t * aesKey = key; - if (sharedKey[0] & 0x80) - { - aesKey[0] = 0; - memcpy (aesKey + 1, sharedKey, 31); - } - else if (sharedKey[0]) - memcpy (aesKey, sharedKey, 32); - else - { - // find first non-zero byte - uint8_t * nonZero = sharedKey + 1; - while (!*nonZero) - { - nonZero++; - if (nonZero - sharedKey > 32) - { - LogPrint (eLogWarning, "First 32 bytes of shared key is all zeros. Ignored"); - return; - } - } - memcpy (aesKey, nonZero, 32); - } - } - - void NTCPSession::Done () - { - m_Server.GetService ().post (std::bind (&NTCPSession::Terminate, shared_from_this ())); - } - - void NTCPSession::Terminate () - { - if (!m_IsTerminated) - { - m_IsTerminated = true; - m_IsEstablished = false; - m_Socket.close (); - transports.PeerDisconnected (shared_from_this ()); - m_Server.RemoveNTCPSession (shared_from_this ()); - m_SendQueue.clear (); - m_NextMessage = nullptr; - m_TerminationTimer.cancel (); - LogPrint (eLogInfo, "NTCP session terminated"); - } - } - - void NTCPSession::Connected () - { - m_IsEstablished = true; - - delete m_Establisher; - m_Establisher = nullptr; - - delete m_DHKeysPair; - m_DHKeysPair = nullptr; - - SendTimeSyncMessage (); - m_SendQueue.push_back (CreateDatabaseStoreMsg ()); // we tell immediately who we are - - transports.PeerConnected (shared_from_this ()); - } - - void NTCPSession::ClientLogin () - { - if (!m_DHKeysPair) - m_DHKeysPair = transports.GetNextDHKeysPair (); - // send Phase1 - const uint8_t * x = m_DHKeysPair->publicKey; - memcpy (m_Establisher->phase1.pubKey, x, 256); - CryptoPP::SHA256().CalculateDigest(m_Establisher->phase1.HXxorHI, x, 256); - const uint8_t * ident = m_RemoteIdentity.GetIdentHash (); - for (int i = 0; i < 32; i++) - m_Establisher->phase1.HXxorHI[i] ^= ident[i]; - - boost::asio::async_write (m_Socket, boost::asio::buffer (&m_Establisher->phase1, sizeof (NTCPPhase1)), boost::asio::transfer_all (), - std::bind(&NTCPSession::HandlePhase1Sent, shared_from_this (), std::placeholders::_1, std::placeholders::_2)); - ScheduleTermination (); - } - - void NTCPSession::ServerLogin () - { - boost::system::error_code ec; - auto ep = m_Socket.remote_endpoint(ec); - if (!ec) - { - m_ConnectedFrom = ep.address (); - // receive Phase1 - boost::asio::async_read (m_Socket, boost::asio::buffer(&m_Establisher->phase1, sizeof (NTCPPhase1)), boost::asio::transfer_all (), - std::bind(&NTCPSession::HandlePhase1Received, shared_from_this (), - std::placeholders::_1, std::placeholders::_2)); - ScheduleTermination (); - } - } - - void NTCPSession::HandlePhase1Sent (const boost::system::error_code& ecode, std::size_t bytes_transferred) - { - if (ecode) + uint8_t * aesKey = key; + if (sharedKey[0] & 0x80) { - LogPrint (eLogError, "Couldn't send Phase 1 message: ", ecode.message ()); - if (ecode != boost::asio::error::operation_aborted) - Terminate (); - } - else - { - boost::asio::async_read (m_Socket, boost::asio::buffer(&m_Establisher->phase2, sizeof (NTCPPhase2)), boost::asio::transfer_all (), - std::bind(&NTCPSession::HandlePhase2Received, shared_from_this (), - std::placeholders::_1, std::placeholders::_2)); - } - } - - void NTCPSession::HandlePhase1Received (const boost::system::error_code& ecode, std::size_t bytes_transferred) - { - if (ecode) + aesKey[0] = 0; + memcpy (aesKey + 1, sharedKey, 31); + } + else if (sharedKey[0]) + memcpy (aesKey, sharedKey, 32); + else { - LogPrint (eLogError, "Phase 1 read error: ", ecode.message ()); - if (ecode != boost::asio::error::operation_aborted) - Terminate (); - } - else - { - // verify ident - uint8_t digest[32]; - CryptoPP::SHA256().CalculateDigest(digest, m_Establisher->phase1.pubKey, 256); - const uint8_t * ident = i2p::context.GetRouterInfo ().GetIdentHash (); - for (int i = 0; i < 32; i++) - { - if ((m_Establisher->phase1.HXxorHI[i] ^ ident[i]) != digest[i]) - { - LogPrint (eLogError, "Wrong ident"); - Terminate (); - return; - } - } - - SendPhase2 (); - } - } + // find first non-zero byte + uint8_t * nonZero = sharedKey + 1; + while (!*nonZero) + { + nonZero++; + if (nonZero - sharedKey > 32) + { + LogPrint (eLogWarning, "First 32 bytes of shared key is all zeros. Ignored"); + return; + } + } + memcpy (aesKey, nonZero, 32); + } + } - void NTCPSession::SendPhase2 () - { - if (!m_DHKeysPair) - m_DHKeysPair = transports.GetNextDHKeysPair (); - const uint8_t * y = m_DHKeysPair->publicKey; - memcpy (m_Establisher->phase2.pubKey, y, 256); - uint8_t xy[512]; - memcpy (xy, m_Establisher->phase1.pubKey, 256); - memcpy (xy + 256, y, 256); - CryptoPP::SHA256().CalculateDigest(m_Establisher->phase2.encrypted.hxy, xy, 512); - uint32_t tsB = htobe32 (i2p::util::GetSecondsSinceEpoch ()); - m_Establisher->phase2.encrypted.timestamp = tsB; - // TODO: fill filler + void NTCPSession::Done () + { + m_Server.GetService ().post (std::bind (&NTCPSession::Terminate, shared_from_this ())); + } + + void NTCPSession::Terminate () + { + if (!m_IsTerminated) + { + m_IsTerminated = true; + m_IsEstablished = false; + m_Socket.close (); + transports.PeerDisconnected (shared_from_this ()); + m_Server.RemoveNTCPSession (shared_from_this ()); + m_SendQueue.clear (); + m_NextMessage = nullptr; + m_TerminationTimer.cancel (); + LogPrint (eLogInfo, "NTCP session terminated"); + } + } - i2p::crypto::AESKey aesKey; - CreateAESKey (m_Establisher->phase1.pubKey, aesKey); - m_Encryption.SetKey (aesKey); - m_Encryption.SetIV (y + 240); - m_Decryption.SetKey (aesKey); - m_Decryption.SetIV (m_Establisher->phase1.HXxorHI + 16); - - m_Encryption.Encrypt ((uint8_t *)&m_Establisher->phase2.encrypted, sizeof(m_Establisher->phase2.encrypted), (uint8_t *)&m_Establisher->phase2.encrypted); - boost::asio::async_write (m_Socket, boost::asio::buffer (&m_Establisher->phase2, sizeof (NTCPPhase2)), boost::asio::transfer_all (), - std::bind(&NTCPSession::HandlePhase2Sent, shared_from_this (), std::placeholders::_1, std::placeholders::_2, tsB)); + void NTCPSession::Connected () + { + m_IsEstablished = true; - } - - void NTCPSession::HandlePhase2Sent (const boost::system::error_code& ecode, std::size_t bytes_transferred, uint32_t tsB) - { - if (ecode) + delete m_Establisher; + m_Establisher = nullptr; + + delete m_DHKeysPair; + m_DHKeysPair = nullptr; + + SendTimeSyncMessage (); + m_SendQueue.push_back (CreateDatabaseStoreMsg ()); // we tell immediately who we are + + transports.PeerConnected (shared_from_this ()); + } + + void NTCPSession::ClientLogin () + { + if (!m_DHKeysPair) + m_DHKeysPair = transports.GetNextDHKeysPair (); + // send Phase1 + const uint8_t * x = m_DHKeysPair->publicKey; + memcpy (m_Establisher->phase1.pubKey, x, 256); + CryptoPP::SHA256().CalculateDigest(m_Establisher->phase1.HXxorHI, x, 256); + const uint8_t * ident = m_RemoteIdentity.GetIdentHash (); + for (int i = 0; i < 32; i++) + m_Establisher->phase1.HXxorHI[i] ^= ident[i]; + + boost::asio::async_write (m_Socket, boost::asio::buffer (&m_Establisher->phase1, sizeof (NTCPPhase1)), boost::asio::transfer_all (), + std::bind(&NTCPSession::HandlePhase1Sent, shared_from_this (), std::placeholders::_1, std::placeholders::_2)); + ScheduleTermination (); + } + + void NTCPSession::ServerLogin () + { + boost::system::error_code ec; + auto ep = m_Socket.remote_endpoint(ec); + if (!ec) + { + m_ConnectedFrom = ep.address (); + // receive Phase1 + boost::asio::async_read (m_Socket, boost::asio::buffer(&m_Establisher->phase1, sizeof (NTCPPhase1)), boost::asio::transfer_all (), + std::bind(&NTCPSession::HandlePhase1Received, shared_from_this (), + std::placeholders::_1, std::placeholders::_2)); + ScheduleTermination (); + } + } + + void NTCPSession::HandlePhase1Sent (const boost::system::error_code& ecode, std::size_t bytes_transferred) + { + if (ecode) { - LogPrint (eLogError, "Couldn't send Phase 2 message: ", ecode.message ()); - if (ecode != boost::asio::error::operation_aborted) - Terminate (); - } - else - { - boost::asio::async_read (m_Socket, boost::asio::buffer(m_ReceiveBuffer, NTCP_DEFAULT_PHASE3_SIZE), boost::asio::transfer_all (), - std::bind(&NTCPSession::HandlePhase3Received, shared_from_this (), - std::placeholders::_1, std::placeholders::_2, tsB)); - } - } - - void NTCPSession::HandlePhase2Received (const boost::system::error_code& ecode, std::size_t bytes_transferred) - { - if (ecode) + LogPrint (eLogError, "Couldn't send Phase 1 message: ", ecode.message ()); + if (ecode != boost::asio::error::operation_aborted) + Terminate (); + } + else + { + boost::asio::async_read (m_Socket, boost::asio::buffer(&m_Establisher->phase2, sizeof (NTCPPhase2)), boost::asio::transfer_all (), + std::bind(&NTCPSession::HandlePhase2Received, shared_from_this (), + std::placeholders::_1, std::placeholders::_2)); + } + } + + void NTCPSession::HandlePhase1Received (const boost::system::error_code& ecode, std::size_t bytes_transferred) + { + if (ecode) { - LogPrint (eLogError, "Phase 2 read error: ", ecode.message (), ". Wrong ident assumed"); - if (ecode != boost::asio::error::operation_aborted) - { - // this RI is not valid - i2p::data::netdb.SetUnreachable (GetRemoteIdentity ().GetIdentHash (), true); - transports.ReuseDHKeysPair (m_DHKeysPair); - m_DHKeysPair = nullptr; - Terminate (); - } - } - else - { - i2p::crypto::AESKey aesKey; - CreateAESKey (m_Establisher->phase2.pubKey, aesKey); - m_Decryption.SetKey (aesKey); - m_Decryption.SetIV (m_Establisher->phase2.pubKey + 240); - m_Encryption.SetKey (aesKey); - m_Encryption.SetIV (m_Establisher->phase1.HXxorHI + 16); - - m_Decryption.Decrypt((uint8_t *)&m_Establisher->phase2.encrypted, sizeof(m_Establisher->phase2.encrypted), (uint8_t *)&m_Establisher->phase2.encrypted); - // verify - uint8_t xy[512]; - memcpy (xy, m_DHKeysPair->publicKey, 256); - memcpy (xy + 256, m_Establisher->phase2.pubKey, 256); - if (!CryptoPP::SHA256().VerifyDigest(m_Establisher->phase2.encrypted.hxy, xy, 512)) - { - LogPrint (eLogError, "Incorrect hash"); - transports.ReuseDHKeysPair (m_DHKeysPair); - m_DHKeysPair = nullptr; - Terminate (); - return ; - } - SendPhase3 (); - } - } + LogPrint (eLogError, "Phase 1 read error: ", ecode.message ()); + if (ecode != boost::asio::error::operation_aborted) + Terminate (); + } + else + { + // verify ident + uint8_t digest[32]; + CryptoPP::SHA256().CalculateDigest(digest, m_Establisher->phase1.pubKey, 256); + const uint8_t * ident = i2p::context.GetRouterInfo ().GetIdentHash (); + for (int i = 0; i < 32; i++) + { + if ((m_Establisher->phase1.HXxorHI[i] ^ ident[i]) != digest[i]) + { + LogPrint (eLogError, "Wrong ident"); + Terminate (); + return; + } + } + + SendPhase2 (); + } + } - void NTCPSession::SendPhase3 () - { - auto keys = i2p::context.GetPrivateKeys (); - uint8_t * buf = m_ReceiveBuffer; - htobe16buf (buf, keys.GetPublic ().GetFullLen ()); - buf += 2; - buf += i2p::context.GetIdentity ().ToBuffer (buf, NTCP_BUFFER_SIZE); - uint32_t tsA = htobe32 (i2p::util::GetSecondsSinceEpoch ()); - htobuf32(buf,tsA); - buf += 4; - size_t signatureLen = keys.GetPublic ().GetSignatureLen (); - size_t len = (buf - m_ReceiveBuffer) + signatureLen; - size_t paddingSize = len & 0x0F; // %16 - if (paddingSize > 0) - { - paddingSize = 16 - paddingSize; - // TODO: fill padding with random data - buf += paddingSize; - len += paddingSize; - } + void NTCPSession::SendPhase2 () + { + if (!m_DHKeysPair) + m_DHKeysPair = transports.GetNextDHKeysPair (); + const uint8_t * y = m_DHKeysPair->publicKey; + memcpy (m_Establisher->phase2.pubKey, y, 256); + uint8_t xy[512]; + memcpy (xy, m_Establisher->phase1.pubKey, 256); + memcpy (xy + 256, y, 256); + CryptoPP::SHA256().CalculateDigest(m_Establisher->phase2.encrypted.hxy, xy, 512); + uint32_t tsB = htobe32 (i2p::util::GetSecondsSinceEpoch ()); + m_Establisher->phase2.encrypted.timestamp = tsB; + // TODO: fill filler - SignedData s; - s.Insert (m_Establisher->phase1.pubKey, 256); // x - s.Insert (m_Establisher->phase2.pubKey, 256); // y - s.Insert (m_RemoteIdentity.GetIdentHash (), 32); // ident - s.Insert (tsA); // tsA - s.Insert (m_Establisher->phase2.encrypted.timestamp); // tsB - s.Sign (keys, buf); + i2p::crypto::AESKey aesKey; + CreateAESKey (m_Establisher->phase1.pubKey, aesKey); + m_Encryption.SetKey (aesKey); + m_Encryption.SetIV (y + 240); + m_Decryption.SetKey (aesKey); + m_Decryption.SetIV (m_Establisher->phase1.HXxorHI + 16); + + m_Encryption.Encrypt ((uint8_t *)&m_Establisher->phase2.encrypted, sizeof(m_Establisher->phase2.encrypted), (uint8_t *)&m_Establisher->phase2.encrypted); + boost::asio::async_write (m_Socket, boost::asio::buffer (&m_Establisher->phase2, sizeof (NTCPPhase2)), boost::asio::transfer_all (), + std::bind(&NTCPSession::HandlePhase2Sent, shared_from_this (), std::placeholders::_1, std::placeholders::_2, tsB)); - m_Encryption.Encrypt(m_ReceiveBuffer, len, m_ReceiveBuffer); - boost::asio::async_write (m_Socket, boost::asio::buffer (m_ReceiveBuffer, len), boost::asio::transfer_all (), - std::bind(&NTCPSession::HandlePhase3Sent, shared_from_this (), std::placeholders::_1, std::placeholders::_2, tsA)); - } - - void NTCPSession::HandlePhase3Sent (const boost::system::error_code& ecode, std::size_t bytes_transferred, uint32_t tsA) - { - if (ecode) + } + + void NTCPSession::HandlePhase2Sent (const boost::system::error_code& ecode, std::size_t bytes_transferred, uint32_t tsB) + { + if (ecode) { - LogPrint (eLogError, "Couldn't send Phase 3 message: ", ecode.message ()); - if (ecode != boost::asio::error::operation_aborted) - Terminate (); - } - else - { - // wait for phase4 - auto signatureLen = m_RemoteIdentity.GetSignatureLen (); - size_t paddingSize = signatureLen & 0x0F; // %16 - if (paddingSize > 0) signatureLen += (16 - paddingSize); - boost::asio::async_read (m_Socket, boost::asio::buffer(m_ReceiveBuffer, signatureLen), boost::asio::transfer_all (), - std::bind(&NTCPSession::HandlePhase4Received, shared_from_this (), - std::placeholders::_1, std::placeholders::_2, tsA)); - } - } - - void NTCPSession::HandlePhase3Received (const boost::system::error_code& ecode, std::size_t bytes_transferred, uint32_t tsB) - { - if (ecode) + LogPrint (eLogError, "Couldn't send Phase 2 message: ", ecode.message ()); + if (ecode != boost::asio::error::operation_aborted) + Terminate (); + } + else + { + boost::asio::async_read (m_Socket, boost::asio::buffer(m_ReceiveBuffer, NTCP_DEFAULT_PHASE3_SIZE), boost::asio::transfer_all (), + std::bind(&NTCPSession::HandlePhase3Received, shared_from_this (), + std::placeholders::_1, std::placeholders::_2, tsB)); + } + } + + void NTCPSession::HandlePhase2Received (const boost::system::error_code& ecode, std::size_t bytes_transferred) + { + if (ecode) { - LogPrint (eLogError, "Phase 3 read error: ", ecode.message ()); - if (ecode != boost::asio::error::operation_aborted) - Terminate (); - } - else - { - m_Decryption.Decrypt (m_ReceiveBuffer, bytes_transferred, m_ReceiveBuffer); - uint8_t * buf = m_ReceiveBuffer; - uint16_t size = bufbe16toh (buf); - m_RemoteIdentity.FromBuffer (buf + 2, size); - if (m_Server.FindNTCPSession (m_RemoteIdentity.GetIdentHash ())) - { - LogPrint (eLogError, "NTCP session already exists"); - Terminate (); - } - size_t expectedSize = size + 2/*size*/ + 4/*timestamp*/ + m_RemoteIdentity.GetSignatureLen (); - size_t paddingLen = expectedSize & 0x0F; - if (paddingLen) paddingLen = (16 - paddingLen); - if (expectedSize > NTCP_DEFAULT_PHASE3_SIZE) - { - // we need more bytes for Phase3 - expectedSize += paddingLen; - boost::asio::async_read (m_Socket, boost::asio::buffer(m_ReceiveBuffer + NTCP_DEFAULT_PHASE3_SIZE, expectedSize), boost::asio::transfer_all (), - std::bind(&NTCPSession::HandlePhase3ExtraReceived, shared_from_this (), - std::placeholders::_1, std::placeholders::_2, tsB, paddingLen)); - } - else - HandlePhase3 (tsB, paddingLen); - } - } + LogPrint (eLogError, "Phase 2 read error: ", ecode.message (), ". Wrong ident assumed"); + if (ecode != boost::asio::error::operation_aborted) + { + // this RI is not valid + i2p::data::netdb.SetUnreachable (GetRemoteIdentity ().GetIdentHash (), true); + transports.ReuseDHKeysPair (m_DHKeysPair); + m_DHKeysPair = nullptr; + Terminate (); + } + } + else + { + i2p::crypto::AESKey aesKey; + CreateAESKey (m_Establisher->phase2.pubKey, aesKey); + m_Decryption.SetKey (aesKey); + m_Decryption.SetIV (m_Establisher->phase2.pubKey + 240); + m_Encryption.SetKey (aesKey); + m_Encryption.SetIV (m_Establisher->phase1.HXxorHI + 16); + + m_Decryption.Decrypt((uint8_t *)&m_Establisher->phase2.encrypted, sizeof(m_Establisher->phase2.encrypted), (uint8_t *)&m_Establisher->phase2.encrypted); + // verify + uint8_t xy[512]; + memcpy (xy, m_DHKeysPair->publicKey, 256); + memcpy (xy + 256, m_Establisher->phase2.pubKey, 256); + if (!CryptoPP::SHA256().VerifyDigest(m_Establisher->phase2.encrypted.hxy, xy, 512)) + { + LogPrint (eLogError, "Incorrect hash"); + transports.ReuseDHKeysPair (m_DHKeysPair); + m_DHKeysPair = nullptr; + Terminate (); + return ; + } + SendPhase3 (); + } + } - void NTCPSession::HandlePhase3ExtraReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred, uint32_t tsB, size_t paddingLen) - { - if (ecode) + void NTCPSession::SendPhase3 () + { + auto keys = i2p::context.GetPrivateKeys (); + uint8_t * buf = m_ReceiveBuffer; + htobe16buf (buf, keys.GetPublic ().GetFullLen ()); + buf += 2; + buf += i2p::context.GetIdentity ().ToBuffer (buf, NTCP_BUFFER_SIZE); + uint32_t tsA = htobe32 (i2p::util::GetSecondsSinceEpoch ()); + htobuf32(buf,tsA); + buf += 4; + size_t signatureLen = keys.GetPublic ().GetSignatureLen (); + size_t len = (buf - m_ReceiveBuffer) + signatureLen; + size_t paddingSize = len & 0x0F; // %16 + if (paddingSize > 0) { - LogPrint (eLogError, "Phase 3 extra read error: ", ecode.message ()); - if (ecode != boost::asio::error::operation_aborted) - Terminate (); - } - else - { - m_Decryption.Decrypt (m_ReceiveBuffer + NTCP_DEFAULT_PHASE3_SIZE, bytes_transferred, m_ReceiveBuffer+ NTCP_DEFAULT_PHASE3_SIZE); - HandlePhase3 (tsB, paddingLen); - } - } + paddingSize = 16 - paddingSize; + // TODO: fill padding with random data + buf += paddingSize; + len += paddingSize; + } - void NTCPSession::HandlePhase3 (uint32_t tsB, size_t paddingLen) - { - uint8_t * buf = m_ReceiveBuffer + m_RemoteIdentity.GetFullLen () + 2 /*size*/; - uint32_t tsA = buf32toh(buf); - buf += 4; - buf += paddingLen; + SignedData s; + s.Insert (m_Establisher->phase1.pubKey, 256); // x + s.Insert (m_Establisher->phase2.pubKey, 256); // y + s.Insert (m_RemoteIdentity.GetIdentHash (), 32); // ident + s.Insert (tsA); // tsA + s.Insert (m_Establisher->phase2.encrypted.timestamp); // tsB + s.Sign (keys, buf); - SignedData s; - s.Insert (m_Establisher->phase1.pubKey, 256); // x - s.Insert (m_Establisher->phase2.pubKey, 256); // y - s.Insert (i2p::context.GetRouterInfo ().GetIdentHash (), 32); // ident - s.Insert (tsA); // tsA - s.Insert (tsB); // tsB - if (!s.Verify (m_RemoteIdentity, buf)) - { - LogPrint (eLogError, "signature verification failed"); - Terminate (); - return; - } - m_RemoteIdentity.DropVerifier (); - - SendPhase4 (tsA, tsB); - } - - void NTCPSession::SendPhase4 (uint32_t tsA, uint32_t tsB) - { - SignedData s; - s.Insert (m_Establisher->phase1.pubKey, 256); // x - s.Insert (m_Establisher->phase2.pubKey, 256); // y - s.Insert (m_RemoteIdentity.GetIdentHash (), 32); // ident - s.Insert (tsA); // tsA - s.Insert (tsB); // tsB - auto keys = i2p::context.GetPrivateKeys (); - auto signatureLen = keys.GetPublic ().GetSignatureLen (); - s.Sign (keys, m_ReceiveBuffer); - size_t paddingSize = signatureLen & 0x0F; // %16 - if (paddingSize > 0) signatureLen += (16 - paddingSize); - m_Encryption.Encrypt (m_ReceiveBuffer, signatureLen, m_ReceiveBuffer); - - boost::asio::async_write (m_Socket, boost::asio::buffer (m_ReceiveBuffer, signatureLen), boost::asio::transfer_all (), - std::bind(&NTCPSession::HandlePhase4Sent, shared_from_this (), std::placeholders::_1, std::placeholders::_2)); - } - - void NTCPSession::HandlePhase4Sent (const boost::system::error_code& ecode, std::size_t bytes_transferred) - { - if (ecode) + m_Encryption.Encrypt(m_ReceiveBuffer, len, m_ReceiveBuffer); + boost::asio::async_write (m_Socket, boost::asio::buffer (m_ReceiveBuffer, len), boost::asio::transfer_all (), + std::bind(&NTCPSession::HandlePhase3Sent, shared_from_this (), std::placeholders::_1, std::placeholders::_2, tsA)); + } + + void NTCPSession::HandlePhase3Sent (const boost::system::error_code& ecode, std::size_t bytes_transferred, uint32_t tsA) + { + if (ecode) { - LogPrint (eLogWarning, "Couldn't send Phase 4 message: ", ecode.message ()); - if (ecode != boost::asio::error::operation_aborted) - Terminate (); - } - else - { - LogPrint (eLogInfo, "NTCP server session from ", m_Socket.remote_endpoint (), " connected"); - m_Server.AddNTCPSession (shared_from_this ()); + LogPrint (eLogError, "Couldn't send Phase 3 message: ", ecode.message ()); + if (ecode != boost::asio::error::operation_aborted) + Terminate (); + } + else + { + // wait for phase4 + auto signatureLen = m_RemoteIdentity.GetSignatureLen (); + size_t paddingSize = signatureLen & 0x0F; // %16 + if (paddingSize > 0) signatureLen += (16 - paddingSize); + boost::asio::async_read (m_Socket, boost::asio::buffer(m_ReceiveBuffer, signatureLen), boost::asio::transfer_all (), + std::bind(&NTCPSession::HandlePhase4Received, shared_from_this (), + std::placeholders::_1, std::placeholders::_2, tsA)); + } + } - Connected (); - m_ReceiveBufferOffset = 0; - m_NextMessage = nullptr; - Receive (); - } - } - - void NTCPSession::HandlePhase4Received (const boost::system::error_code& ecode, std::size_t bytes_transferred, uint32_t tsA) - { - if (ecode) + void NTCPSession::HandlePhase3Received (const boost::system::error_code& ecode, std::size_t bytes_transferred, uint32_t tsB) + { + if (ecode) { - LogPrint (eLogError, "Phase 4 read error: ", ecode.message (), ". Check your clock"); - if (ecode != boost::asio::error::operation_aborted) - { - // this router doesn't like us - i2p::data::netdb.SetUnreachable (GetRemoteIdentity ().GetIdentHash (), true); - Terminate (); - } - } - else - { - m_Decryption.Decrypt(m_ReceiveBuffer, bytes_transferred, m_ReceiveBuffer); + LogPrint (eLogError, "Phase 3 read error: ", ecode.message ()); + if (ecode != boost::asio::error::operation_aborted) + Terminate (); + } + else + { + m_Decryption.Decrypt (m_ReceiveBuffer, bytes_transferred, m_ReceiveBuffer); + uint8_t * buf = m_ReceiveBuffer; + uint16_t size = bufbe16toh (buf); + m_RemoteIdentity.FromBuffer (buf + 2, size); + if (m_Server.FindNTCPSession (m_RemoteIdentity.GetIdentHash ())) + { + LogPrint (eLogError, "NTCP session already exists"); + Terminate (); + } + size_t expectedSize = size + 2/*size*/ + 4/*timestamp*/ + m_RemoteIdentity.GetSignatureLen (); + size_t paddingLen = expectedSize & 0x0F; + if (paddingLen) paddingLen = (16 - paddingLen); + if (expectedSize > NTCP_DEFAULT_PHASE3_SIZE) + { + // we need more bytes for Phase3 + expectedSize += paddingLen; + boost::asio::async_read (m_Socket, boost::asio::buffer(m_ReceiveBuffer + NTCP_DEFAULT_PHASE3_SIZE, expectedSize), boost::asio::transfer_all (), + std::bind(&NTCPSession::HandlePhase3ExtraReceived, shared_from_this (), + std::placeholders::_1, std::placeholders::_2, tsB, paddingLen)); + } + else + HandlePhase3 (tsB, paddingLen); + } + } - // verify signature - SignedData s; - s.Insert (m_Establisher->phase1.pubKey, 256); // x - s.Insert (m_Establisher->phase2.pubKey, 256); // y - s.Insert (i2p::context.GetRouterInfo ().GetIdentHash (), 32); // ident - s.Insert (tsA); // tsA - s.Insert (m_Establisher->phase2.encrypted.timestamp); // tsB - - if (!s.Verify (m_RemoteIdentity, m_ReceiveBuffer)) - { - LogPrint (eLogError, "signature verification failed"); - Terminate (); - return; - } - m_RemoteIdentity.DropVerifier (); - LogPrint (eLogInfo, "NTCP session to ", m_Socket.remote_endpoint (), " connected"); - Connected (); - - m_ReceiveBufferOffset = 0; - m_NextMessage = nullptr; - Receive (); - } - } - - void NTCPSession::Receive () - { - m_Socket.async_read_some (boost::asio::buffer(m_ReceiveBuffer + m_ReceiveBufferOffset, NTCP_BUFFER_SIZE - m_ReceiveBufferOffset), - std::bind(&NTCPSession::HandleReceived, shared_from_this (), - std::placeholders::_1, std::placeholders::_2)); - } - - void NTCPSession::HandleReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred) - { - if (ecode) + void NTCPSession::HandlePhase3ExtraReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred, uint32_t tsB, size_t paddingLen) + { + if (ecode) { - LogPrint (eLogError, "Read error: ", ecode.message ()); - if (!m_NumReceivedBytes) m_Server.Ban (m_ConnectedFrom); - //if (ecode != boost::asio::error::operation_aborted) - Terminate (); - } - else - { - m_NumReceivedBytes += bytes_transferred; - i2p::transport::transports.UpdateReceivedBytes (bytes_transferred); - m_ReceiveBufferOffset += bytes_transferred; - - if (m_ReceiveBufferOffset >= 16) - { - int numReloads = 0; - do - { - uint8_t * nextBlock = m_ReceiveBuffer; - while (m_ReceiveBufferOffset >= 16) - { - if (!DecryptNextBlock (nextBlock)) // 16 bytes - { - Terminate (); - return; - } - nextBlock += 16; - m_ReceiveBufferOffset -= 16; - } - if (m_ReceiveBufferOffset > 0) - memcpy (m_ReceiveBuffer, nextBlock, m_ReceiveBufferOffset); - - // try to read more - if (numReloads < 5) - { - boost::system::error_code ec; - size_t moreBytes = m_Socket.available(ec); - if (moreBytes) - { - if (moreBytes > NTCP_BUFFER_SIZE - m_ReceiveBufferOffset) - moreBytes = NTCP_BUFFER_SIZE - m_ReceiveBufferOffset; - moreBytes = m_Socket.read_some (boost::asio::buffer (m_ReceiveBuffer + m_ReceiveBufferOffset, moreBytes)); - if (ec) - { - LogPrint (eLogError, "Read more bytes error: ", ec.message ()); - Terminate (); - return; - } - m_NumReceivedBytes += moreBytes; - m_ReceiveBufferOffset += moreBytes; - numReloads++; - } - } - } - while (m_ReceiveBufferOffset >= 16); - m_Handler.Flush (); - } - - ScheduleTermination (); // reset termination timer - Receive (); - } - } - - bool NTCPSession::DecryptNextBlock (const uint8_t * encrypted) // 16 bytes - { - if (!m_NextMessage) // new message, header expected - { - // descrypt header and extract length - uint8_t buf[16]; - m_Decryption.Decrypt (encrypted, buf); - uint16_t dataSize = bufbe16toh (buf); - if (dataSize) - { - // new message - if (dataSize > NTCP_MAX_MESSAGE_SIZE) - { - LogPrint (eLogError, "NTCP data size ", dataSize, " exceeds max size"); - return false; - } - auto msg = dataSize <= I2NP_MAX_SHORT_MESSAGE_SIZE - 2 ? NewI2NPShortMessage () : NewI2NPMessage (); - m_NextMessage = ToSharedI2NPMessage (msg); - memcpy (m_NextMessage->buf, buf, 16); - m_NextMessageOffset = 16; - m_NextMessage->offset = 2; // size field - m_NextMessage->len = dataSize + 2; - } - else - { - // timestamp - LogPrint ("Timestamp"); - return true; - } - } - else // message continues - { - m_Decryption.Decrypt (encrypted, m_NextMessage->buf + m_NextMessageOffset); - m_NextMessageOffset += 16; - } - - if (m_NextMessageOffset >= m_NextMessage->len + 4) // +checksum - { - // we have a complete I2NP message - if (CryptoPP::Adler32().VerifyDigest (m_NextMessage->buf + m_NextMessageOffset - 4, m_NextMessage->buf, m_NextMessageOffset - 4)) - m_Handler.PutNextMessage (m_NextMessage); - else - LogPrint (eLogWarning, "Incorrect adler checksum of NTCP message. Dropped"); - m_NextMessage = nullptr; - } - return true; - } - - void NTCPSession::Send (std::shared_ptr msg) - { - m_IsSending = true; - boost::asio::async_write (m_Socket, CreateMsgBuffer (msg), boost::asio::transfer_all (), - std::bind(&NTCPSession::HandleSent, shared_from_this (), std::placeholders::_1, std::placeholders::_2, std::vector >{ msg })); - } - - boost::asio::const_buffers_1 NTCPSession::CreateMsgBuffer (std::shared_ptr msg) - { - uint8_t * sendBuffer; - int len; - - if (msg) - { - // regular I2NP - if (msg->offset < 2) - LogPrint (eLogError, "Malformed I2NP message"); // TODO: - sendBuffer = msg->GetBuffer () - 2; - len = msg->GetLength (); - htobe16buf (sendBuffer, len); - } - else - { - // prepare timestamp - sendBuffer = m_TimeSyncBuffer; - len = 4; - htobuf16(sendBuffer, 0); - htobe32buf (sendBuffer + 2, time (0)); - } - int rem = (len + 6) & 0x0F; // %16 - int padding = 0; - if (rem > 0) padding = 16 - rem; - // TODO: fill padding - CryptoPP::Adler32().CalculateDigest (sendBuffer + len + 2 + padding, sendBuffer, len + 2+ padding); - - int l = len + padding + 6; - m_Encryption.Encrypt(sendBuffer, l, sendBuffer); - return boost::asio::buffer ((const uint8_t *)sendBuffer, l); - } - - - void NTCPSession::Send (const std::vector >& msgs) - { - m_IsSending = true; - std::vector bufs; - for (auto it: msgs) - bufs.push_back (CreateMsgBuffer (it)); - boost::asio::async_write (m_Socket, bufs, boost::asio::transfer_all (), - std::bind(&NTCPSession::HandleSent, shared_from_this (), std::placeholders::_1, std::placeholders::_2, msgs)); - } - - void NTCPSession::HandleSent (const boost::system::error_code& ecode, std::size_t bytes_transferred, std::vector > msgs) - { - m_IsSending = false; - if (ecode) + LogPrint (eLogError, "Phase 3 extra read error: ", ecode.message ()); + if (ecode != boost::asio::error::operation_aborted) + Terminate (); + } + else { - LogPrint (eLogWarning, "Couldn't send msgs: ", ecode.message ()); - // we shouldn't call Terminate () here, because HandleReceive takes care - // TODO: 'delete this' statement in Terminate () must be eliminated later - // Terminate (); - } - else - { - m_NumSentBytes += bytes_transferred; - i2p::transport::transports.UpdateSentBytes (bytes_transferred); - if (!m_SendQueue.empty()) - { - Send (m_SendQueue); - m_SendQueue.clear (); - } - else - ScheduleTermination (); // reset termination timer - } - } + m_Decryption.Decrypt (m_ReceiveBuffer + NTCP_DEFAULT_PHASE3_SIZE, bytes_transferred, m_ReceiveBuffer+ NTCP_DEFAULT_PHASE3_SIZE); + HandlePhase3 (tsB, paddingLen); + } + } - - void NTCPSession::SendTimeSyncMessage () - { - Send (nullptr); - } + void NTCPSession::HandlePhase3 (uint32_t tsB, size_t paddingLen) + { + uint8_t * buf = m_ReceiveBuffer + m_RemoteIdentity.GetFullLen () + 2 /*size*/; + uint32_t tsA = buf32toh(buf); + buf += 4; + buf += paddingLen; + + SignedData s; + s.Insert (m_Establisher->phase1.pubKey, 256); // x + s.Insert (m_Establisher->phase2.pubKey, 256); // y + s.Insert (i2p::context.GetRouterInfo ().GetIdentHash (), 32); // ident + s.Insert (tsA); // tsA + s.Insert (tsB); // tsB + if (!s.Verify (m_RemoteIdentity, buf)) + { + LogPrint (eLogError, "signature verification failed"); + Terminate (); + return; + } + m_RemoteIdentity.DropVerifier (); + + SendPhase4 (tsA, tsB); + } + + void NTCPSession::SendPhase4 (uint32_t tsA, uint32_t tsB) + { + SignedData s; + s.Insert (m_Establisher->phase1.pubKey, 256); // x + s.Insert (m_Establisher->phase2.pubKey, 256); // y + s.Insert (m_RemoteIdentity.GetIdentHash (), 32); // ident + s.Insert (tsA); // tsA + s.Insert (tsB); // tsB + auto keys = i2p::context.GetPrivateKeys (); + auto signatureLen = keys.GetPublic ().GetSignatureLen (); + s.Sign (keys, m_ReceiveBuffer); + size_t paddingSize = signatureLen & 0x0F; // %16 + if (paddingSize > 0) signatureLen += (16 - paddingSize); + m_Encryption.Encrypt (m_ReceiveBuffer, signatureLen, m_ReceiveBuffer); + + boost::asio::async_write (m_Socket, boost::asio::buffer (m_ReceiveBuffer, signatureLen), boost::asio::transfer_all (), + std::bind(&NTCPSession::HandlePhase4Sent, shared_from_this (), std::placeholders::_1, std::placeholders::_2)); + } + + void NTCPSession::HandlePhase4Sent (const boost::system::error_code& ecode, std::size_t bytes_transferred) + { + if (ecode) + { + LogPrint (eLogWarning, "Couldn't send Phase 4 message: ", ecode.message ()); + if (ecode != boost::asio::error::operation_aborted) + Terminate (); + } + else + { + LogPrint (eLogInfo, "NTCP server session from ", m_Socket.remote_endpoint (), " connected"); + m_Server.AddNTCPSession (shared_from_this ()); + + Connected (); + m_ReceiveBufferOffset = 0; + m_NextMessage = nullptr; + Receive (); + } + } + + void NTCPSession::HandlePhase4Received (const boost::system::error_code& ecode, std::size_t bytes_transferred, uint32_t tsA) + { + if (ecode) + { + LogPrint (eLogError, "Phase 4 read error: ", ecode.message (), ". Check your clock"); + if (ecode != boost::asio::error::operation_aborted) + { + // this router doesn't like us + i2p::data::netdb.SetUnreachable (GetRemoteIdentity ().GetIdentHash (), true); + Terminate (); + } + } + else + { + m_Decryption.Decrypt(m_ReceiveBuffer, bytes_transferred, m_ReceiveBuffer); + + // verify signature + SignedData s; + s.Insert (m_Establisher->phase1.pubKey, 256); // x + s.Insert (m_Establisher->phase2.pubKey, 256); // y + s.Insert (i2p::context.GetRouterInfo ().GetIdentHash (), 32); // ident + s.Insert (tsA); // tsA + s.Insert (m_Establisher->phase2.encrypted.timestamp); // tsB + + if (!s.Verify (m_RemoteIdentity, m_ReceiveBuffer)) + { + LogPrint (eLogError, "signature verification failed"); + Terminate (); + return; + } + m_RemoteIdentity.DropVerifier (); + LogPrint (eLogInfo, "NTCP session to ", m_Socket.remote_endpoint (), " connected"); + Connected (); + + m_ReceiveBufferOffset = 0; + m_NextMessage = nullptr; + Receive (); + } + } + + void NTCPSession::Receive () + { + m_Socket.async_read_some (boost::asio::buffer(m_ReceiveBuffer + m_ReceiveBufferOffset, NTCP_BUFFER_SIZE - m_ReceiveBufferOffset), + std::bind(&NTCPSession::HandleReceived, shared_from_this (), + std::placeholders::_1, std::placeholders::_2)); + } + + void NTCPSession::HandleReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred) + { + if (ecode) + { + LogPrint (eLogError, "Read error: ", ecode.message ()); + if (!m_NumReceivedBytes) m_Server.Ban (m_ConnectedFrom); + //if (ecode != boost::asio::error::operation_aborted) + Terminate (); + } + else + { + m_NumReceivedBytes += bytes_transferred; + i2p::transport::transports.UpdateReceivedBytes (bytes_transferred); + m_ReceiveBufferOffset += bytes_transferred; + + if (m_ReceiveBufferOffset >= 16) + { + int numReloads = 0; + do + { + uint8_t * nextBlock = m_ReceiveBuffer; + while (m_ReceiveBufferOffset >= 16) + { + if (!DecryptNextBlock (nextBlock)) // 16 bytes + { + Terminate (); + return; + } + nextBlock += 16; + m_ReceiveBufferOffset -= 16; + } + if (m_ReceiveBufferOffset > 0) + memcpy (m_ReceiveBuffer, nextBlock, m_ReceiveBufferOffset); + + // try to read more + if (numReloads < 5) + { + boost::system::error_code ec; + size_t moreBytes = m_Socket.available(ec); + if (moreBytes) + { + if (moreBytes > NTCP_BUFFER_SIZE - m_ReceiveBufferOffset) + moreBytes = NTCP_BUFFER_SIZE - m_ReceiveBufferOffset; + moreBytes = m_Socket.read_some (boost::asio::buffer (m_ReceiveBuffer + m_ReceiveBufferOffset, moreBytes)); + if (ec) + { + LogPrint (eLogError, "Read more bytes error: ", ec.message ()); + Terminate (); + return; + } + m_NumReceivedBytes += moreBytes; + m_ReceiveBufferOffset += moreBytes; + numReloads++; + } + } + } + while (m_ReceiveBufferOffset >= 16); + m_Handler.Flush (); + } + + ScheduleTermination (); // reset termination timer + Receive (); + } + } + + bool NTCPSession::DecryptNextBlock (const uint8_t * encrypted) // 16 bytes + { + if (!m_NextMessage) // new message, header expected + { + // descrypt header and extract length + uint8_t buf[16]; + m_Decryption.Decrypt (encrypted, buf); + uint16_t dataSize = bufbe16toh (buf); + if (dataSize) + { + // new message + if (dataSize > NTCP_MAX_MESSAGE_SIZE) + { + LogPrint (eLogError, "NTCP data size ", dataSize, " exceeds max size"); + return false; + } + auto msg = dataSize <= I2NP_MAX_SHORT_MESSAGE_SIZE - 2 ? NewI2NPShortMessage () : NewI2NPMessage (); + m_NextMessage = ToSharedI2NPMessage (msg); + memcpy (m_NextMessage->buf, buf, 16); + m_NextMessageOffset = 16; + m_NextMessage->offset = 2; // size field + m_NextMessage->len = dataSize + 2; + } + else + { + // timestamp + LogPrint ("Timestamp"); + return true; + } + } + else // message continues + { + m_Decryption.Decrypt (encrypted, m_NextMessage->buf + m_NextMessageOffset); + m_NextMessageOffset += 16; + } + + if (m_NextMessageOffset >= m_NextMessage->len + 4) // +checksum + { + // we have a complete I2NP message + if (CryptoPP::Adler32().VerifyDigest (m_NextMessage->buf + m_NextMessageOffset - 4, m_NextMessage->buf, m_NextMessageOffset - 4)) + m_Handler.PutNextMessage (m_NextMessage); + else + LogPrint (eLogWarning, "Incorrect adler checksum of NTCP message. Dropped"); + m_NextMessage = nullptr; + } + return true; + } + + void NTCPSession::Send (std::shared_ptr msg) + { + m_IsSending = true; + boost::asio::async_write (m_Socket, CreateMsgBuffer (msg), boost::asio::transfer_all (), + std::bind(&NTCPSession::HandleSent, shared_from_this (), std::placeholders::_1, std::placeholders::_2, std::vector >{ msg })); + } + + boost::asio::const_buffers_1 NTCPSession::CreateMsgBuffer (std::shared_ptr msg) + { + uint8_t * sendBuffer; + int len; + + if (msg) + { + // regular I2NP + if (msg->offset < 2) + LogPrint (eLogError, "Malformed I2NP message"); // TODO: + sendBuffer = msg->GetBuffer () - 2; + len = msg->GetLength (); + htobe16buf (sendBuffer, len); + } + else + { + // prepare timestamp + sendBuffer = m_TimeSyncBuffer; + len = 4; + htobuf16(sendBuffer, 0); + htobe32buf (sendBuffer + 2, time (0)); + } + int rem = (len + 6) & 0x0F; // %16 + int padding = 0; + if (rem > 0) padding = 16 - rem; + // TODO: fill padding + CryptoPP::Adler32().CalculateDigest (sendBuffer + len + 2 + padding, sendBuffer, len + 2+ padding); + + int l = len + padding + 6; + m_Encryption.Encrypt(sendBuffer, l, sendBuffer); + return boost::asio::buffer ((const uint8_t *)sendBuffer, l); + } - void NTCPSession::SendI2NPMessages (const std::vector >& msgs) - { - m_Server.GetService ().post (std::bind (&NTCPSession::PostI2NPMessages, shared_from_this (), msgs)); - } + void NTCPSession::Send (const std::vector >& msgs) + { + m_IsSending = true; + std::vector bufs; + for (auto it: msgs) + bufs.push_back (CreateMsgBuffer (it)); + boost::asio::async_write (m_Socket, bufs, boost::asio::transfer_all (), + std::bind(&NTCPSession::HandleSent, shared_from_this (), std::placeholders::_1, std::placeholders::_2, msgs)); + } + + void NTCPSession::HandleSent (const boost::system::error_code& ecode, std::size_t bytes_transferred, std::vector > msgs) + { + m_IsSending = false; + if (ecode) + { + LogPrint (eLogWarning, "Couldn't send msgs: ", ecode.message ()); + // we shouldn't call Terminate () here, because HandleReceive takes care + // TODO: 'delete this' statement in Terminate () must be eliminated later + // Terminate (); + } + else + { + m_NumSentBytes += bytes_transferred; + i2p::transport::transports.UpdateSentBytes (bytes_transferred); + if (!m_SendQueue.empty()) + { + Send (m_SendQueue); + m_SendQueue.clear (); + } + else + ScheduleTermination (); // reset termination timer + } + } - void NTCPSession::PostI2NPMessages (std::vector > msgs) - { - if (m_IsTerminated) return; - if (m_IsSending) - { - for (auto it: msgs) - m_SendQueue.push_back (it); - } - else - Send (msgs); - } - - void NTCPSession::ScheduleTermination () - { - m_TerminationTimer.cancel (); - m_TerminationTimer.expires_from_now (boost::posix_time::seconds(NTCP_TERMINATION_TIMEOUT)); - m_TerminationTimer.async_wait (std::bind (&NTCPSession::HandleTerminationTimer, - shared_from_this (), std::placeholders::_1)); - } + + void NTCPSession::SendTimeSyncMessage () + { + Send (nullptr); + } - void NTCPSession::HandleTerminationTimer (const boost::system::error_code& ecode) - { - if (ecode != boost::asio::error::operation_aborted) - { - LogPrint ("No activity fo ", NTCP_TERMINATION_TIMEOUT, " seconds"); - //Terminate (); - m_Socket.close ();// invoke Terminate () from HandleReceive - } - } + + void NTCPSession::SendI2NPMessages (const std::vector >& msgs) + { + m_Server.GetService ().post (std::bind (&NTCPSession::PostI2NPMessages, shared_from_this (), msgs)); + } + + void NTCPSession::PostI2NPMessages (std::vector > msgs) + { + if (m_IsTerminated) return; + if (m_IsSending) + { + for (auto it: msgs) + m_SendQueue.push_back (it); + } + else + Send (msgs); + } + + void NTCPSession::ScheduleTermination () + { + m_TerminationTimer.cancel (); + m_TerminationTimer.expires_from_now (boost::posix_time::seconds(NTCP_TERMINATION_TIMEOUT)); + m_TerminationTimer.async_wait (std::bind (&NTCPSession::HandleTerminationTimer, + shared_from_this (), std::placeholders::_1)); + } + + void NTCPSession::HandleTerminationTimer (const boost::system::error_code& ecode) + { + if (ecode != boost::asio::error::operation_aborted) + { + LogPrint ("No activity fo ", NTCP_TERMINATION_TIMEOUT, " seconds"); + //Terminate (); + m_Socket.close ();// invoke Terminate () from HandleReceive + } + } //----------------------------------------- - NTCPServer::NTCPServer (int port): - m_IsRunning (false), m_Thread (nullptr), m_Work (m_Service), - m_NTCPAcceptor (nullptr), m_NTCPV6Acceptor (nullptr) - { - } - - NTCPServer::~NTCPServer () - { - Stop (); - } + NTCPServer::NTCPServer (int port): + m_IsRunning (false), m_Thread (nullptr), m_Work (m_Service), + m_NTCPAcceptor (nullptr), m_NTCPV6Acceptor (nullptr) + { + } + + NTCPServer::~NTCPServer () + { + Stop (); + } - void NTCPServer::Start () - { - if (!m_IsRunning) - { - m_IsRunning = true; - m_Thread = new std::thread (std::bind (&NTCPServer::Run, this)); - // create acceptors - auto addresses = context.GetRouterInfo ().GetAddresses (); - for (auto& address : addresses) - { - if (address.transportStyle == i2p::data::RouterInfo::eTransportNTCP && address.host.is_v4 ()) - { - m_NTCPAcceptor = new boost::asio::ip::tcp::acceptor (m_Service, - boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), address.port)); + void NTCPServer::Start () + { + if (!m_IsRunning) + { + m_IsRunning = true; + m_Thread = new std::thread (std::bind (&NTCPServer::Run, this)); + // create acceptors + auto addresses = context.GetRouterInfo ().GetAddresses (); + for (auto& address : addresses) + { + if (address.transportStyle == i2p::data::RouterInfo::eTransportNTCP && address.host.is_v4 ()) + { + m_NTCPAcceptor = new boost::asio::ip::tcp::acceptor (m_Service, + boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), address.port)); - LogPrint (eLogInfo, "Start listening TCP port ", address.port); - auto conn = std::make_shared(*this); - m_NTCPAcceptor->async_accept(conn->GetSocket (), std::bind (&NTCPServer::HandleAccept, this, - conn, std::placeholders::_1)); - - if (context.SupportsV6 ()) - { - m_NTCPV6Acceptor = new boost::asio::ip::tcp::acceptor (m_Service); - m_NTCPV6Acceptor->open (boost::asio::ip::tcp::v6()); - 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->listen (); + LogPrint (eLogInfo, "Start listening TCP port ", address.port); + auto conn = std::make_shared(*this); + m_NTCPAcceptor->async_accept(conn->GetSocket (), std::bind (&NTCPServer::HandleAccept, this, + conn, std::placeholders::_1)); + + if (context.SupportsV6 ()) + { + m_NTCPV6Acceptor = new boost::asio::ip::tcp::acceptor (m_Service); + m_NTCPV6Acceptor->open (boost::asio::ip::tcp::v6()); + 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->listen (); - LogPrint (eLogInfo, "Start listening V6 TCP port ", address.port); - auto conn = std::make_shared (*this); - m_NTCPV6Acceptor->async_accept(conn->GetSocket (), std::bind (&NTCPServer::HandleAcceptV6, - this, conn, std::placeholders::_1)); - } - } - } - } - } - - void NTCPServer::Stop () - { - m_NTCPSessions.clear (); + LogPrint (eLogInfo, "Start listening V6 TCP port ", address.port); + auto conn = std::make_shared (*this); + m_NTCPV6Acceptor->async_accept(conn->GetSocket (), std::bind (&NTCPServer::HandleAcceptV6, + this, conn, std::placeholders::_1)); + } + } + } + } + } + + void NTCPServer::Stop () + { + m_NTCPSessions.clear (); - if (m_IsRunning) - { - m_IsRunning = false; - delete m_NTCPAcceptor; - m_NTCPAcceptor = nullptr; - delete m_NTCPV6Acceptor; - m_NTCPV6Acceptor = nullptr; + if (m_IsRunning) + { + m_IsRunning = false; + delete m_NTCPAcceptor; + m_NTCPAcceptor = nullptr; + delete m_NTCPV6Acceptor; + m_NTCPV6Acceptor = nullptr; - m_Service.stop (); - if (m_Thread) - { - m_Thread->join (); - delete m_Thread; - m_Thread = nullptr; - } - } - } + m_Service.stop (); + if (m_Thread) + { + m_Thread->join (); + delete m_Thread; + m_Thread = nullptr; + } + } + } - - void NTCPServer::Run () - { - while (m_IsRunning) - { - try - { - m_Service.run (); - } - catch (std::exception& ex) - { - LogPrint ("NTCP server: ", ex.what ()); - } - } - } - - void NTCPServer::AddNTCPSession (std::shared_ptr session) - { - if (session) - { - std::unique_lock l(m_NTCPSessionsMutex); - m_NTCPSessions[session->GetRemoteIdentity ().GetIdentHash ()] = session; - } - } - - void NTCPServer::RemoveNTCPSession (std::shared_ptr session) - { - if (session) - { - std::unique_lock l(m_NTCPSessionsMutex); - m_NTCPSessions.erase (session->GetRemoteIdentity ().GetIdentHash ()); - } - } - - std::shared_ptr NTCPServer::FindNTCPSession (const i2p::data::IdentHash& ident) - { - std::unique_lock l(m_NTCPSessionsMutex); - auto it = m_NTCPSessions.find (ident); - if (it != m_NTCPSessions.end ()) - return it->second; - return nullptr; - } - - void NTCPServer::HandleAccept (std::shared_ptr conn, const boost::system::error_code& error) - { - if (!error) - { - boost::system::error_code ec; - auto ep = conn->GetSocket ().remote_endpoint(ec); - if (!ec) - { - LogPrint (eLogInfo, "Connected from ", ep); - auto it = m_BanList.find (ep.address ()); - if (it != m_BanList.end ()) - { - uint32_t ts = i2p::util::GetSecondsSinceEpoch (); - if (ts < it->second) - { - LogPrint (eLogInfo, ep.address (), " is banned for ", it->second - ts, " more seconds"); - conn = nullptr; - } - else - m_BanList.erase (it); - } - if (conn) - conn->ServerLogin (); - } - else - LogPrint (eLogError, "Connected from error ", ec.message ()); - } - - - if (error != boost::asio::error::operation_aborted) - { - conn = std::make_shared (*this); - m_NTCPAcceptor->async_accept(conn->GetSocket (), std::bind (&NTCPServer::HandleAccept, this, - conn, std::placeholders::_1)); - } - } - - void NTCPServer::HandleAcceptV6 (std::shared_ptr conn, const boost::system::error_code& error) - { - if (!error) - { - boost::system::error_code ec; - auto ep = conn->GetSocket ().remote_endpoint(ec); - if (!ec) - { - LogPrint (eLogInfo, "Connected from ", ep); - auto it = m_BanList.find (ep.address ()); - if (it != m_BanList.end ()) - { - uint32_t ts = i2p::util::GetSecondsSinceEpoch (); - if (ts < it->second) - { - LogPrint (eLogInfo, ep.address (), " is banned for ", it->second - ts, " more seconds"); - conn = nullptr; - } - else - m_BanList.erase (it); - } - if (conn) - conn->ServerLogin (); - } - else - LogPrint (eLogError, "Connected from error ", ec.message ()); - } - - if (error != boost::asio::error::operation_aborted) - { - conn = std::make_shared (*this); - m_NTCPV6Acceptor->async_accept(conn->GetSocket (), std::bind (&NTCPServer::HandleAcceptV6, this, - conn, std::placeholders::_1)); - } - } - - void NTCPServer::Connect (const boost::asio::ip::address& address, int port, std::shared_ptr conn) - { - LogPrint (eLogInfo, "Connecting to ", address ,":", port); - m_Service.post([conn, this]() - { - this->AddNTCPSession (conn); - }); - conn->GetSocket ().async_connect (boost::asio::ip::tcp::endpoint (address, port), - std::bind (&NTCPServer::HandleConnect, this, std::placeholders::_1, conn)); - } - - void NTCPServer::HandleConnect (const boost::system::error_code& ecode, std::shared_ptr conn) - { - if (ecode) + + void NTCPServer::Run () + { + while (m_IsRunning) { - LogPrint (eLogError, "Connect error: ", ecode.message ()); - if (ecode != boost::asio::error::operation_aborted) - i2p::data::netdb.SetUnreachable (conn->GetRemoteIdentity ().GetIdentHash (), true); - conn->Terminate (); - } - else - { - LogPrint (eLogInfo, "Connected to ", conn->GetSocket ().remote_endpoint ()); - if (conn->GetSocket ().local_endpoint ().protocol () == boost::asio::ip::tcp::v6()) // ipv6 - context.UpdateNTCPV6Address (conn->GetSocket ().local_endpoint ().address ()); - conn->ClientLogin (); - } - } + try + { + m_Service.run (); + } + catch (std::exception& ex) + { + LogPrint ("NTCP server: ", ex.what ()); + } + } + } - void NTCPServer::Ban (const boost::asio::ip::address& addr) - { - uint32_t ts = i2p::util::GetSecondsSinceEpoch (); - m_BanList[addr] = ts + NTCP_BAN_EXPIRATION_TIMEOUT; - LogPrint (eLogInfo, addr, " has been banned for ", NTCP_BAN_EXPIRATION_TIMEOUT, " seconds"); - } -} -} + void NTCPServer::AddNTCPSession (std::shared_ptr session) + { + if (session) + { + std::unique_lock l(m_NTCPSessionsMutex); + m_NTCPSessions[session->GetRemoteIdentity ().GetIdentHash ()] = session; + } + } + + void NTCPServer::RemoveNTCPSession (std::shared_ptr session) + { + if (session) + { + std::unique_lock l(m_NTCPSessionsMutex); + m_NTCPSessions.erase (session->GetRemoteIdentity ().GetIdentHash ()); + } + } + + std::shared_ptr NTCPServer::FindNTCPSession (const i2p::data::IdentHash& ident) + { + std::unique_lock l(m_NTCPSessionsMutex); + auto it = m_NTCPSessions.find (ident); + if (it != m_NTCPSessions.end ()) + return it->second; + return nullptr; + } + + void NTCPServer::HandleAccept (std::shared_ptr conn, const boost::system::error_code& error) + { + if (!error) + { + boost::system::error_code ec; + auto ep = conn->GetSocket ().remote_endpoint(ec); + if (!ec) + { + LogPrint (eLogInfo, "Connected from ", ep); + auto it = m_BanList.find (ep.address ()); + if (it != m_BanList.end ()) + { + uint32_t ts = i2p::util::GetSecondsSinceEpoch (); + if (ts < it->second) + { + LogPrint (eLogInfo, ep.address (), " is banned for ", it->second - ts, " more seconds"); + conn = nullptr; + } + else + m_BanList.erase (it); + } + if (conn) + conn->ServerLogin (); + } + else + LogPrint (eLogError, "Connected from error ", ec.message ()); + } + + + if (error != boost::asio::error::operation_aborted) + { + conn = std::make_shared (*this); + m_NTCPAcceptor->async_accept(conn->GetSocket (), std::bind (&NTCPServer::HandleAccept, this, + conn, std::placeholders::_1)); + } + } + + void NTCPServer::HandleAcceptV6 (std::shared_ptr conn, const boost::system::error_code& error) + { + if (!error) + { + boost::system::error_code ec; + auto ep = conn->GetSocket ().remote_endpoint(ec); + if (!ec) + { + LogPrint (eLogInfo, "Connected from ", ep); + auto it = m_BanList.find (ep.address ()); + if (it != m_BanList.end ()) + { + uint32_t ts = i2p::util::GetSecondsSinceEpoch (); + if (ts < it->second) + { + LogPrint (eLogInfo, ep.address (), " is banned for ", it->second - ts, " more seconds"); + conn = nullptr; + } + else + m_BanList.erase (it); + } + if (conn) + conn->ServerLogin (); + } + else + LogPrint (eLogError, "Connected from error ", ec.message ()); + } + + if (error != boost::asio::error::operation_aborted) + { + conn = std::make_shared (*this); + m_NTCPV6Acceptor->async_accept(conn->GetSocket (), std::bind (&NTCPServer::HandleAcceptV6, this, + conn, std::placeholders::_1)); + } + } + + void NTCPServer::Connect (const boost::asio::ip::address& address, int port, std::shared_ptr conn) + { + LogPrint (eLogInfo, "Connecting to ", address ,":", port); + m_Service.post([conn, this]() + { + this->AddNTCPSession (conn); + }); + conn->GetSocket ().async_connect (boost::asio::ip::tcp::endpoint (address, port), + std::bind (&NTCPServer::HandleConnect, this, std::placeholders::_1, conn)); + } + + void NTCPServer::HandleConnect (const boost::system::error_code& ecode, std::shared_ptr conn) + { + if (ecode) + { + LogPrint (eLogError, "Connect error: ", ecode.message ()); + if (ecode != boost::asio::error::operation_aborted) + i2p::data::netdb.SetUnreachable (conn->GetRemoteIdentity ().GetIdentHash (), true); + conn->Terminate (); + } + else + { + LogPrint (eLogInfo, "Connected to ", conn->GetSocket ().remote_endpoint ()); + if (conn->GetSocket ().local_endpoint ().protocol () == boost::asio::ip::tcp::v6()) // ipv6 + context.UpdateNTCPV6Address (conn->GetSocket ().local_endpoint ().address ()); + conn->ClientLogin (); + } + } + + void NTCPServer::Ban (const boost::asio::ip::address& addr) + { + uint32_t ts = i2p::util::GetSecondsSinceEpoch (); + m_BanList[addr] = ts + NTCP_BAN_EXPIRATION_TIMEOUT; + LogPrint (eLogInfo, addr, " has been banned for ", NTCP_BAN_EXPIRATION_TIMEOUT, " seconds"); + } +} +} diff --git a/NTCPSession.h b/NTCPSession.h index 2921e923..7b99483e 100644 --- a/NTCPSession.h +++ b/NTCPSession.h @@ -21,163 +21,163 @@ namespace transport { #pragma pack(1) - struct NTCPPhase1 - { - uint8_t pubKey[256]; - uint8_t HXxorHI[32]; - }; - - struct NTCPPhase2 - { - uint8_t pubKey[256]; - struct - { - uint8_t hxy[32]; - uint32_t timestamp; - uint8_t filler[12]; - } encrypted; - }; - -#pragma pack() + struct NTCPPhase1 + { + uint8_t pubKey[256]; + uint8_t HXxorHI[32]; + }; + + struct NTCPPhase2 + { + uint8_t pubKey[256]; + struct + { + uint8_t hxy[32]; + uint32_t timestamp; + uint8_t filler[12]; + } encrypted; + }; + +#pragma pack() - const size_t NTCP_MAX_MESSAGE_SIZE = 16384; - const size_t NTCP_BUFFER_SIZE = 4160; // fits 4 tunnel messages (4*1028) - const int NTCP_TERMINATION_TIMEOUT = 120; // 2 minutes - const size_t NTCP_DEFAULT_PHASE3_SIZE = 2/*size*/ + i2p::data::DEFAULT_IDENTITY_SIZE/*387*/ + 4/*ts*/ + 15/*padding*/ + 40/*signature*/; // 448 - const int NTCP_BAN_EXPIRATION_TIMEOUT = 70; // in second + const size_t NTCP_MAX_MESSAGE_SIZE = 16384; + const size_t NTCP_BUFFER_SIZE = 4160; // fits 4 tunnel messages (4*1028) + const int NTCP_TERMINATION_TIMEOUT = 120; // 2 minutes + const size_t NTCP_DEFAULT_PHASE3_SIZE = 2/*size*/ + i2p::data::DEFAULT_IDENTITY_SIZE/*387*/ + 4/*ts*/ + 15/*padding*/ + 40/*signature*/; // 448 + const int NTCP_BAN_EXPIRATION_TIMEOUT = 70; // in second - class NTCPServer; - class NTCPSession: public TransportSession, public std::enable_shared_from_this - { - public: + class NTCPServer; + class NTCPSession: public TransportSession, public std::enable_shared_from_this + { + public: - NTCPSession (NTCPServer& server, std::shared_ptr in_RemoteRouter = nullptr); - ~NTCPSession (); - void Terminate (); - void Done (); + NTCPSession (NTCPServer& server, std::shared_ptr in_RemoteRouter = nullptr); + ~NTCPSession (); + void Terminate (); + void Done (); - boost::asio::ip::tcp::socket& GetSocket () { return m_Socket; }; - bool IsEstablished () const { return m_IsEstablished; }; - - void ClientLogin (); - void ServerLogin (); - void SendI2NPMessages (const std::vector >& msgs); - - private: + boost::asio::ip::tcp::socket& GetSocket () { return m_Socket; }; + bool IsEstablished () const { return m_IsEstablished; }; + + void ClientLogin (); + void ServerLogin (); + void SendI2NPMessages (const std::vector >& msgs); + + private: - void PostI2NPMessages (std::vector > msgs); - void Connected (); - void SendTimeSyncMessage (); - void SetIsEstablished (bool isEstablished) { m_IsEstablished = isEstablished; } + void PostI2NPMessages (std::vector > msgs); + void Connected (); + void SendTimeSyncMessage (); + void SetIsEstablished (bool isEstablished) { m_IsEstablished = isEstablished; } - void CreateAESKey (uint8_t * pubKey, i2p::crypto::AESKey& key); - - // client - void SendPhase3 (); - void HandlePhase1Sent (const boost::system::error_code& ecode, std::size_t bytes_transferred); - void HandlePhase2Received (const boost::system::error_code& ecode, std::size_t bytes_transferred); - void HandlePhase3Sent (const boost::system::error_code& ecode, std::size_t bytes_transferred, uint32_t tsA); - void HandlePhase4Received (const boost::system::error_code& ecode, std::size_t bytes_transferred, uint32_t tsA); + void CreateAESKey (uint8_t * pubKey, i2p::crypto::AESKey& key); + + // client + void SendPhase3 (); + void HandlePhase1Sent (const boost::system::error_code& ecode, std::size_t bytes_transferred); + void HandlePhase2Received (const boost::system::error_code& ecode, std::size_t bytes_transferred); + void HandlePhase3Sent (const boost::system::error_code& ecode, std::size_t bytes_transferred, uint32_t tsA); + void HandlePhase4Received (const boost::system::error_code& ecode, std::size_t bytes_transferred, uint32_t tsA); - //server - void SendPhase2 (); - void SendPhase4 (uint32_t tsA, uint32_t tsB); - void HandlePhase1Received (const boost::system::error_code& ecode, std::size_t bytes_transferred); - void HandlePhase2Sent (const boost::system::error_code& ecode, std::size_t bytes_transferred, uint32_t tsB); - void HandlePhase3Received (const boost::system::error_code& ecode, std::size_t bytes_transferred, uint32_t tsB); - void HandlePhase3ExtraReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred, uint32_t tsB, size_t paddingLen); - void HandlePhase3 (uint32_t tsB, size_t paddingLen); - void HandlePhase4Sent (const boost::system::error_code& ecode, std::size_t bytes_transferred); - - // common - void Receive (); - void HandleReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred); - bool DecryptNextBlock (const uint8_t * encrypted); - - void Send (std::shared_ptr msg); - boost::asio::const_buffers_1 CreateMsgBuffer (std::shared_ptr msg); - void Send (const std::vector >& msgs); - void HandleSent (const boost::system::error_code& ecode, std::size_t bytes_transferred, std::vector > msgs); - - - // timer - void ScheduleTermination (); - void HandleTerminationTimer (const boost::system::error_code& ecode); - - private: + //server + void SendPhase2 (); + void SendPhase4 (uint32_t tsA, uint32_t tsB); + void HandlePhase1Received (const boost::system::error_code& ecode, std::size_t bytes_transferred); + void HandlePhase2Sent (const boost::system::error_code& ecode, std::size_t bytes_transferred, uint32_t tsB); + void HandlePhase3Received (const boost::system::error_code& ecode, std::size_t bytes_transferred, uint32_t tsB); + void HandlePhase3ExtraReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred, uint32_t tsB, size_t paddingLen); + void HandlePhase3 (uint32_t tsB, size_t paddingLen); + void HandlePhase4Sent (const boost::system::error_code& ecode, std::size_t bytes_transferred); + + // common + void Receive (); + void HandleReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred); + bool DecryptNextBlock (const uint8_t * encrypted); + + void Send (std::shared_ptr msg); + boost::asio::const_buffers_1 CreateMsgBuffer (std::shared_ptr msg); + void Send (const std::vector >& msgs); + void HandleSent (const boost::system::error_code& ecode, std::size_t bytes_transferred, std::vector > msgs); + + + // timer + void ScheduleTermination (); + void HandleTerminationTimer (const boost::system::error_code& ecode); + + private: - NTCPServer& m_Server; - boost::asio::ip::tcp::socket m_Socket; - boost::asio::deadline_timer m_TerminationTimer; - bool m_IsEstablished, m_IsTerminated; - - i2p::crypto::CBCDecryption m_Decryption; - i2p::crypto::CBCEncryption m_Encryption; + NTCPServer& m_Server; + boost::asio::ip::tcp::socket m_Socket; + boost::asio::deadline_timer m_TerminationTimer; + bool m_IsEstablished, m_IsTerminated; + + i2p::crypto::CBCDecryption m_Decryption; + i2p::crypto::CBCEncryption m_Encryption; - struct Establisher - { - NTCPPhase1 phase1; - NTCPPhase2 phase2; - } * m_Establisher; - - i2p::crypto::AESAlignedBuffer m_ReceiveBuffer; - i2p::crypto::AESAlignedBuffer<16> m_TimeSyncBuffer; - int m_ReceiveBufferOffset; + struct Establisher + { + NTCPPhase1 phase1; + NTCPPhase2 phase2; + } * m_Establisher; + + i2p::crypto::AESAlignedBuffer m_ReceiveBuffer; + i2p::crypto::AESAlignedBuffer<16> m_TimeSyncBuffer; + int m_ReceiveBufferOffset; - std::shared_ptr m_NextMessage; - size_t m_NextMessageOffset; - i2p::I2NPMessagesHandler m_Handler; + std::shared_ptr m_NextMessage; + size_t m_NextMessageOffset; + i2p::I2NPMessagesHandler m_Handler; - bool m_IsSending; - std::vector > m_SendQueue; - - boost::asio::ip::address m_ConnectedFrom; // for ban - }; + bool m_IsSending; + std::vector > m_SendQueue; + + boost::asio::ip::address m_ConnectedFrom; // for ban + }; - // TODO: move to NTCP.h/.cpp - class NTCPServer - { - public: + // TODO: move to NTCP.h/.cpp + class NTCPServer + { + public: - NTCPServer (int port); - ~NTCPServer (); + NTCPServer (int port); + ~NTCPServer (); - void Start (); - void Stop (); + void Start (); + void Stop (); - void AddNTCPSession (std::shared_ptr session); - void RemoveNTCPSession (std::shared_ptr session); - std::shared_ptr FindNTCPSession (const i2p::data::IdentHash& ident); - void Connect (const boost::asio::ip::address& address, int port, std::shared_ptr conn); - - boost::asio::io_service& GetService () { return m_Service; }; - void Ban (const boost::asio::ip::address& addr); + void AddNTCPSession (std::shared_ptr session); + void RemoveNTCPSession (std::shared_ptr session); + std::shared_ptr FindNTCPSession (const i2p::data::IdentHash& ident); + void Connect (const boost::asio::ip::address& address, int port, std::shared_ptr conn); + + boost::asio::io_service& GetService () { return m_Service; }; + void Ban (const boost::asio::ip::address& addr); - private: + private: - void Run (); - void HandleAccept (std::shared_ptr conn, const boost::system::error_code& error); - void HandleAcceptV6 (std::shared_ptr conn, const boost::system::error_code& error); + void Run (); + void HandleAccept (std::shared_ptr conn, const boost::system::error_code& error); + void HandleAcceptV6 (std::shared_ptr conn, const boost::system::error_code& error); - void HandleConnect (const boost::system::error_code& ecode, std::shared_ptr conn); - - private: + void HandleConnect (const boost::system::error_code& ecode, std::shared_ptr conn); + + private: - bool m_IsRunning; - std::thread * m_Thread; - boost::asio::io_service m_Service; - boost::asio::io_service::work m_Work; - boost::asio::ip::tcp::acceptor * m_NTCPAcceptor, * m_NTCPV6Acceptor; - std::mutex m_NTCPSessionsMutex; - std::map > m_NTCPSessions; - std::map m_BanList; // IP -> ban expiration time in seconds + bool m_IsRunning; + std::thread * m_Thread; + boost::asio::io_service m_Service; + boost::asio::io_service::work m_Work; + boost::asio::ip::tcp::acceptor * m_NTCPAcceptor, * m_NTCPV6Acceptor; + std::mutex m_NTCPSessionsMutex; + std::map > m_NTCPSessions; + std::map m_BanList; // IP -> ban expiration time in seconds - public: + public: - // for HTTP/I2PControl - const decltype(m_NTCPSessions)& GetNTCPSessions () const { return m_NTCPSessions; }; - }; -} -} + // for HTTP/I2PControl + const decltype(m_NTCPSessions)& GetNTCPSessions () const { return m_NTCPSessions; }; + }; +} +} #endif diff --git a/NetDb.cpp b/NetDb.cpp index 46e23a8a..474f9d2d 100644 --- a/NetDb.cpp +++ b/NetDb.cpp @@ -20,988 +20,988 @@ using namespace i2p::transport; namespace i2p { namespace data -{ - const char NetDb::m_NetDbPath[] = "netDb"; - NetDb netdb; +{ + const char NetDb::m_NetDbPath[] = "netDb"; + NetDb netdb; - NetDb::NetDb (): m_IsRunning (false), m_Thread (nullptr), m_Reseeder (nullptr) - { - } - - NetDb::~NetDb () - { - Stop (); - delete m_Reseeder; - } + NetDb::NetDb (): m_IsRunning (false), m_Thread (nullptr), m_Reseeder (nullptr) + { + } + + NetDb::~NetDb () + { + Stop (); + delete m_Reseeder; + } - void NetDb::Start () - { - Load (); - if (m_RouterInfos.size () < 25) // reseed if # of router less than 50 - { - // try SU3 first - Reseed (); + void NetDb::Start () + { + Load (); + if (m_RouterInfos.size () < 25) // reseed if # of router less than 50 + { + // try SU3 first + Reseed (); - // deprecated - if (m_Reseeder) - { - // if still not enough download .dat files - int reseedRetries = 0; - while (m_RouterInfos.size () < 25 && reseedRetries < 5) - { - m_Reseeder->reseedNow(); - reseedRetries++; - Load (); - } - } - } - m_IsRunning = true; - m_Thread = new std::thread (std::bind (&NetDb::Run, this)); - } - - void NetDb::Stop () - { - if (m_IsRunning) - { - for (auto it: m_RouterInfos) - it.second->SaveProfile (); - DeleteObsoleteProfiles (); - m_RouterInfos.clear (); - m_Floodfills.clear (); - if (m_Thread) - { - m_IsRunning = false; - m_Queue.WakeUp (); - m_Thread->join (); - delete m_Thread; - m_Thread = 0; - } - m_LeaseSets.clear(); - m_Requests.Stop (); - } - } - - void NetDb::Run () - { - uint32_t lastSave = 0, lastPublish = 0, lastExploratory = 0, lastManageRequest = 0; - while (m_IsRunning) - { - try - { - auto msg = m_Queue.GetNextWithTimeout (15000); // 15 sec - if (msg) - { - int numMsgs = 0; - while (msg) - { - switch (msg->GetTypeID ()) - { - case eI2NPDatabaseStore: - LogPrint ("DatabaseStore"); - HandleDatabaseStoreMsg (msg); - break; - case eI2NPDatabaseSearchReply: - LogPrint ("DatabaseSearchReply"); - HandleDatabaseSearchReplyMsg (msg); - break; - case eI2NPDatabaseLookup: - LogPrint ("DatabaseLookup"); - HandleDatabaseLookupMsg (msg); - break; - default: // WTF? - LogPrint (eLogError, "NetDb: unexpected message type ", msg->GetTypeID ()); - //i2p::HandleI2NPMessage (msg); - } - if (numMsgs > 100) break; - msg = m_Queue.Get (); - numMsgs++; - } - } - if (!m_IsRunning) break; + // deprecated + if (m_Reseeder) + { + // if still not enough download .dat files + int reseedRetries = 0; + while (m_RouterInfos.size () < 25 && reseedRetries < 5) + { + m_Reseeder->reseedNow(); + reseedRetries++; + Load (); + } + } + } + m_IsRunning = true; + m_Thread = new std::thread (std::bind (&NetDb::Run, this)); + } + + void NetDb::Stop () + { + if (m_IsRunning) + { + for (auto it: m_RouterInfos) + it.second->SaveProfile (); + DeleteObsoleteProfiles (); + m_RouterInfos.clear (); + m_Floodfills.clear (); + if (m_Thread) + { + m_IsRunning = false; + m_Queue.WakeUp (); + m_Thread->join (); + delete m_Thread; + m_Thread = 0; + } + m_LeaseSets.clear(); + m_Requests.Stop (); + } + } + + void NetDb::Run () + { + uint32_t lastSave = 0, lastPublish = 0, lastExploratory = 0, lastManageRequest = 0; + while (m_IsRunning) + { + try + { + auto msg = m_Queue.GetNextWithTimeout (15000); // 15 sec + if (msg) + { + int numMsgs = 0; + while (msg) + { + switch (msg->GetTypeID ()) + { + case eI2NPDatabaseStore: + LogPrint ("DatabaseStore"); + HandleDatabaseStoreMsg (msg); + break; + case eI2NPDatabaseSearchReply: + LogPrint ("DatabaseSearchReply"); + HandleDatabaseSearchReplyMsg (msg); + break; + case eI2NPDatabaseLookup: + LogPrint ("DatabaseLookup"); + HandleDatabaseLookupMsg (msg); + break; + default: // WTF? + LogPrint (eLogError, "NetDb: unexpected message type ", msg->GetTypeID ()); + //i2p::HandleI2NPMessage (msg); + } + if (numMsgs > 100) break; + msg = m_Queue.Get (); + numMsgs++; + } + } + if (!m_IsRunning) break; - uint64_t ts = i2p::util::GetSecondsSinceEpoch (); - if (ts - lastManageRequest >= 15) // manage requests every 15 seconds - { - m_Requests.ManageRequests (); - lastManageRequest = ts; - } - if (ts - lastSave >= 60) // save routers, manage leasesets and validate subscriptions every minute - { - if (lastSave) - { - SaveUpdated (); - ManageLeaseSets (); - } - lastSave = ts; - } - if (ts - lastPublish >= 2400) // publish every 40 minutes - { - Publish (); - lastPublish = ts; - } - if (ts - lastExploratory >= 30) // exploratory every 30 seconds - { - auto numRouters = m_RouterInfos.size (); - if (numRouters < 2500 || ts - lastExploratory >= 90) - { - numRouters = 800/numRouters; - if (numRouters < 1) numRouters = 1; - if (numRouters > 9) numRouters = 9; - m_Requests.ManageRequests (); - Explore (numRouters); - lastExploratory = ts; - } - } - } - catch (std::exception& ex) - { - LogPrint ("NetDb: ", ex.what ()); - } - } - } - - void NetDb::AddRouterInfo (const uint8_t * buf, int len) - { - IdentityEx identity; - if (identity.FromBuffer (buf, len)) - AddRouterInfo (identity.GetIdentHash (), buf, len); - } + uint64_t ts = i2p::util::GetSecondsSinceEpoch (); + if (ts - lastManageRequest >= 15) // manage requests every 15 seconds + { + m_Requests.ManageRequests (); + lastManageRequest = ts; + } + if (ts - lastSave >= 60) // save routers, manage leasesets and validate subscriptions every minute + { + if (lastSave) + { + SaveUpdated (); + ManageLeaseSets (); + } + lastSave = ts; + } + if (ts - lastPublish >= 2400) // publish every 40 minutes + { + Publish (); + lastPublish = ts; + } + if (ts - lastExploratory >= 30) // exploratory every 30 seconds + { + auto numRouters = m_RouterInfos.size (); + if (numRouters < 2500 || ts - lastExploratory >= 90) + { + numRouters = 800/numRouters; + if (numRouters < 1) numRouters = 1; + if (numRouters > 9) numRouters = 9; + m_Requests.ManageRequests (); + Explore (numRouters); + lastExploratory = ts; + } + } + } + catch (std::exception& ex) + { + LogPrint ("NetDb: ", ex.what ()); + } + } + } + + void NetDb::AddRouterInfo (const uint8_t * buf, int len) + { + IdentityEx identity; + if (identity.FromBuffer (buf, len)) + AddRouterInfo (identity.GetIdentHash (), buf, len); + } - void NetDb::AddRouterInfo (const IdentHash& ident, const uint8_t * buf, int len) - { - auto r = FindRouter (ident); - if (r) - { - auto ts = r->GetTimestamp (); - r->Update (buf, len); - if (r->GetTimestamp () > ts) - LogPrint ("RouterInfo updated"); - } - else - { - LogPrint ("New RouterInfo added"); - r = std::make_shared (buf, len); - { - std::unique_lock l(m_RouterInfosMutex); - m_RouterInfos[r->GetIdentHash ()] = r; - } - if (r->IsFloodfill ()) - { - std::unique_lock l(m_FloodfillsMutex); - m_Floodfills.push_back (r); - } - } - // take care about requested destination - m_Requests.RequestComplete (ident, r); - } + void NetDb::AddRouterInfo (const IdentHash& ident, const uint8_t * buf, int len) + { + auto r = FindRouter (ident); + if (r) + { + auto ts = r->GetTimestamp (); + r->Update (buf, len); + if (r->GetTimestamp () > ts) + LogPrint ("RouterInfo updated"); + } + else + { + LogPrint ("New RouterInfo added"); + r = std::make_shared (buf, len); + { + std::unique_lock l(m_RouterInfosMutex); + m_RouterInfos[r->GetIdentHash ()] = r; + } + if (r->IsFloodfill ()) + { + std::unique_lock l(m_FloodfillsMutex); + m_Floodfills.push_back (r); + } + } + // take care about requested destination + m_Requests.RequestComplete (ident, r); + } - void NetDb::AddLeaseSet (const IdentHash& ident, const uint8_t * buf, int len, - std::shared_ptr from) - { - if (!from) // unsolicited LS must be received directly - { - auto it = m_LeaseSets.find(ident); - if (it != m_LeaseSets.end ()) - { - it->second->Update (buf, len); - if (it->second->IsValid ()) - LogPrint (eLogInfo, "LeaseSet updated"); - else - { - LogPrint (eLogInfo, "LeaseSet update failed"); - m_LeaseSets.erase (it); - } - } - else - { - auto leaseSet = std::make_shared (buf, len); - if (leaseSet->IsValid ()) - { - LogPrint (eLogInfo, "New LeaseSet added"); - m_LeaseSets[ident] = leaseSet; - } - else - LogPrint (eLogError, "New LeaseSet validation failed"); - } - } - } + void NetDb::AddLeaseSet (const IdentHash& ident, const uint8_t * buf, int len, + std::shared_ptr from) + { + if (!from) // unsolicited LS must be received directly + { + auto it = m_LeaseSets.find(ident); + if (it != m_LeaseSets.end ()) + { + it->second->Update (buf, len); + if (it->second->IsValid ()) + LogPrint (eLogInfo, "LeaseSet updated"); + else + { + LogPrint (eLogInfo, "LeaseSet update failed"); + m_LeaseSets.erase (it); + } + } + else + { + auto leaseSet = std::make_shared (buf, len); + if (leaseSet->IsValid ()) + { + LogPrint (eLogInfo, "New LeaseSet added"); + m_LeaseSets[ident] = leaseSet; + } + else + LogPrint (eLogError, "New LeaseSet validation failed"); + } + } + } - std::shared_ptr NetDb::FindRouter (const IdentHash& ident) const - { - std::unique_lock l(m_RouterInfosMutex); - auto it = m_RouterInfos.find (ident); - if (it != m_RouterInfos.end ()) - return it->second; - else - return nullptr; - } + std::shared_ptr NetDb::FindRouter (const IdentHash& ident) const + { + std::unique_lock l(m_RouterInfosMutex); + auto it = m_RouterInfos.find (ident); + if (it != m_RouterInfos.end ()) + return it->second; + else + return nullptr; + } - std::shared_ptr NetDb::FindLeaseSet (const IdentHash& destination) const - { - auto it = m_LeaseSets.find (destination); - if (it != m_LeaseSets.end ()) - return it->second; - else - return nullptr; - } + std::shared_ptr NetDb::FindLeaseSet (const IdentHash& destination) const + { + auto it = m_LeaseSets.find (destination); + if (it != m_LeaseSets.end ()) + return it->second; + else + return nullptr; + } - void NetDb::SetUnreachable (const IdentHash& ident, bool unreachable) - { - auto it = m_RouterInfos.find (ident); - if (it != m_RouterInfos.end ()) - return it->second->SetUnreachable (unreachable); - } + void NetDb::SetUnreachable (const IdentHash& ident, bool unreachable) + { + auto it = m_RouterInfos.find (ident); + if (it != m_RouterInfos.end ()) + return it->second->SetUnreachable (unreachable); + } - // TODO: Move to reseed and/or scheduled tasks. (In java version, scheduler fix this as well as sort RIs.) - bool NetDb::CreateNetDb(boost::filesystem::path directory) - { - LogPrint (directory.string(), " doesn't exist, trying to create it."); - if (!boost::filesystem::create_directory (directory)) - { - LogPrint("Failed to create directory ", directory.string()); - return false; - } + // TODO: Move to reseed and/or scheduled tasks. (In java version, scheduler fix this as well as sort RIs.) + bool NetDb::CreateNetDb(boost::filesystem::path directory) + { + LogPrint (directory.string(), " doesn't exist, trying to create it."); + if (!boost::filesystem::create_directory (directory)) + { + LogPrint("Failed to create directory ", directory.string()); + return false; + } - // list of chars might appear in base64 string - const char * chars = GetBase64SubstitutionTable (); // 64 bytes - boost::filesystem::path suffix; - for (int i = 0; i < 64; i++) - { + // list of chars might appear in base64 string + const char * chars = GetBase64SubstitutionTable (); // 64 bytes + boost::filesystem::path suffix; + for (int i = 0; i < 64; i++) + { #ifndef _WIN32 - suffix = std::string ("/r") + chars[i]; + suffix = std::string ("/r") + chars[i]; #else - suffix = std::string ("\\r") + chars[i]; + suffix = std::string ("\\r") + chars[i]; #endif - if (!boost::filesystem::create_directory( boost::filesystem::path (directory / suffix) )) return false; - } - return true; - } + if (!boost::filesystem::create_directory( boost::filesystem::path (directory / suffix) )) return false; + } + return true; + } - void NetDb::Reseed () - { - if (!m_Reseeder) - { - m_Reseeder = new Reseeder (); - m_Reseeder->LoadCertificates (); // we need certificates for SU3 verification - } - int reseedRetries = 0; - while (reseedRetries < 10 && !m_Reseeder->ReseedNowSU3 ()) - reseedRetries++; - if (reseedRetries >= 10) - LogPrint (eLogWarning, "Failed to reseed after 10 attempts"); - } + void NetDb::Reseed () + { + if (!m_Reseeder) + { + m_Reseeder = new Reseeder (); + m_Reseeder->LoadCertificates (); // we need certificates for SU3 verification + } + int reseedRetries = 0; + while (reseedRetries < 10 && !m_Reseeder->ReseedNowSU3 ()) + reseedRetries++; + if (reseedRetries >= 10) + LogPrint (eLogWarning, "Failed to reseed after 10 attempts"); + } - void NetDb::Load () - { - boost::filesystem::path p(i2p::util::filesystem::GetDataDir() / m_NetDbPath); - if (!boost::filesystem::exists (p)) - { - // seems netDb doesn't exist yet - if (!CreateNetDb(p)) return; - } - // make sure we cleanup netDb from previous attempts - m_RouterInfos.clear (); - m_Floodfills.clear (); + void NetDb::Load () + { + boost::filesystem::path p(i2p::util::filesystem::GetDataDir() / m_NetDbPath); + if (!boost::filesystem::exists (p)) + { + // seems netDb doesn't exist yet + if (!CreateNetDb(p)) return; + } + // make sure we cleanup netDb from previous attempts + m_RouterInfos.clear (); + m_Floodfills.clear (); - // load routers now - uint64_t ts = i2p::util::GetMillisecondsSinceEpoch (); - int numRouters = 0; - boost::filesystem::directory_iterator end; - for (boost::filesystem::directory_iterator it (p); it != end; ++it) - { - if (boost::filesystem::is_directory (it->status())) - { - for (boost::filesystem::directory_iterator it1 (it->path ()); it1 != end; ++it1) - { + // load routers now + uint64_t ts = i2p::util::GetMillisecondsSinceEpoch (); + int numRouters = 0; + boost::filesystem::directory_iterator end; + for (boost::filesystem::directory_iterator it (p); it != end; ++it) + { + if (boost::filesystem::is_directory (it->status())) + { + for (boost::filesystem::directory_iterator it1 (it->path ()); it1 != end; ++it1) + { #if BOOST_VERSION > 10500 - const std::string& fullPath = it1->path().string(); + const std::string& fullPath = it1->path().string(); #else - const std::string& fullPath = it1->path(); + const std::string& fullPath = it1->path(); #endif - auto r = std::make_shared(fullPath); - if (!r->IsUnreachable () && (!r->UsesIntroducer () || ts < r->GetTimestamp () + 3600*1000LL)) // 1 hour - { - r->DeleteBuffer (); - r->ClearProperties (); // properties are not used for regular routers - m_RouterInfos[r->GetIdentHash ()] = r; - if (r->IsFloodfill ()) - m_Floodfills.push_back (r); - numRouters++; - } - else - { - if (boost::filesystem::exists (fullPath)) - boost::filesystem::remove (fullPath); - } - } - } - } - LogPrint (numRouters, " routers loaded"); - LogPrint (m_Floodfills.size (), " floodfills loaded"); - } + auto r = std::make_shared(fullPath); + if (!r->IsUnreachable () && (!r->UsesIntroducer () || ts < r->GetTimestamp () + 3600*1000LL)) // 1 hour + { + r->DeleteBuffer (); + r->ClearProperties (); // properties are not used for regular routers + m_RouterInfos[r->GetIdentHash ()] = r; + if (r->IsFloodfill ()) + m_Floodfills.push_back (r); + numRouters++; + } + else + { + if (boost::filesystem::exists (fullPath)) + boost::filesystem::remove (fullPath); + } + } + } + } + LogPrint (numRouters, " routers loaded"); + LogPrint (m_Floodfills.size (), " floodfills loaded"); + } - void NetDb::SaveUpdated () - { - auto GetFilePath = [](const boost::filesystem::path& directory, const RouterInfo * routerInfo) - { - std::string s(routerInfo->GetIdentHashBase64()); - return directory / (std::string("r") + s[0]) / ("routerInfo-" + s + ".dat"); - }; + void NetDb::SaveUpdated () + { + auto GetFilePath = [](const boost::filesystem::path& directory, const RouterInfo * routerInfo) + { + std::string s(routerInfo->GetIdentHashBase64()); + return directory / (std::string("r") + s[0]) / ("routerInfo-" + s + ".dat"); + }; - boost::filesystem::path fullDirectory (i2p::util::filesystem::GetDataDir() / m_NetDbPath); - int count = 0, deletedCount = 0; - auto total = m_RouterInfos.size (); - uint64_t ts = i2p::util::GetMillisecondsSinceEpoch (); - for (auto it: m_RouterInfos) - { - if (it.second->IsUpdated ()) - { - std::string f = GetFilePath(fullDirectory, it.second.get()).string(); - it.second->SaveToFile (f); - it.second->SetUpdated (false); - it.second->SetUnreachable (false); - it.second->DeleteBuffer (); - count++; - } - else - { - // RouterInfo expires after 1 hour if uses introducer - if (it.second->UsesIntroducer () && ts > it.second->GetTimestamp () + 3600*1000LL) // 1 hour - it.second->SetUnreachable (true); - else if (total > 75 && ts > (i2p::context.GetStartupTime () + 600)*1000LL) // routers don't expire if less than 25 or uptime is less than 10 minutes - { - if (i2p::context.IsFloodfill ()) - { - if (ts > it.second->GetTimestamp () + 3600*1000LL) // 1 hours - { - it.second->SetUnreachable (true); - total--; - } - } - else if (total > 300) - { - if (ts > it.second->GetTimestamp () + 30*3600*1000LL) // 30 hours - { - it.second->SetUnreachable (true); - total--; - } - } - else if (total > 120) - { - if (ts > it.second->GetTimestamp () + 72*3600*1000LL) // 72 hours - { - it.second->SetUnreachable (true); - total--; - } - } - } - - if (it.second->IsUnreachable ()) - { - total--; - // delete RI file - if (boost::filesystem::exists (GetFilePath (fullDirectory, it.second.get ()))) - { - boost::filesystem::remove (GetFilePath (fullDirectory, it.second.get ())); - deletedCount++; - } - // delete from floodfills list - if (it.second->IsFloodfill ()) - { - std::unique_lock l(m_FloodfillsMutex); - m_Floodfills.remove (it.second); - } - } - } - } - if (count > 0) - LogPrint (count," new/updated routers saved"); - if (deletedCount > 0) - { - LogPrint (deletedCount," routers deleted"); - // clean up RouterInfos table - std::unique_lock l(m_RouterInfosMutex); - for (auto it = m_RouterInfos.begin (); it != m_RouterInfos.end ();) - { - if (it->second->IsUnreachable ()) - { - it->second->SaveProfile (); - it = m_RouterInfos.erase (it); - } - else - it++; - } - } - } + boost::filesystem::path fullDirectory (i2p::util::filesystem::GetDataDir() / m_NetDbPath); + int count = 0, deletedCount = 0; + auto total = m_RouterInfos.size (); + uint64_t ts = i2p::util::GetMillisecondsSinceEpoch (); + for (auto it: m_RouterInfos) + { + if (it.second->IsUpdated ()) + { + std::string f = GetFilePath(fullDirectory, it.second.get()).string(); + it.second->SaveToFile (f); + it.second->SetUpdated (false); + it.second->SetUnreachable (false); + it.second->DeleteBuffer (); + count++; + } + else + { + // RouterInfo expires after 1 hour if uses introducer + if (it.second->UsesIntroducer () && ts > it.second->GetTimestamp () + 3600*1000LL) // 1 hour + it.second->SetUnreachable (true); + else if (total > 75 && ts > (i2p::context.GetStartupTime () + 600)*1000LL) // routers don't expire if less than 25 or uptime is less than 10 minutes + { + if (i2p::context.IsFloodfill ()) + { + if (ts > it.second->GetTimestamp () + 3600*1000LL) // 1 hours + { + it.second->SetUnreachable (true); + total--; + } + } + else if (total > 300) + { + if (ts > it.second->GetTimestamp () + 30*3600*1000LL) // 30 hours + { + it.second->SetUnreachable (true); + total--; + } + } + else if (total > 120) + { + if (ts > it.second->GetTimestamp () + 72*3600*1000LL) // 72 hours + { + it.second->SetUnreachable (true); + total--; + } + } + } + + if (it.second->IsUnreachable ()) + { + total--; + // delete RI file + if (boost::filesystem::exists (GetFilePath (fullDirectory, it.second.get ()))) + { + boost::filesystem::remove (GetFilePath (fullDirectory, it.second.get ())); + deletedCount++; + } + // delete from floodfills list + if (it.second->IsFloodfill ()) + { + std::unique_lock l(m_FloodfillsMutex); + m_Floodfills.remove (it.second); + } + } + } + } + if (count > 0) + LogPrint (count," new/updated routers saved"); + if (deletedCount > 0) + { + LogPrint (deletedCount," routers deleted"); + // clean up RouterInfos table + std::unique_lock l(m_RouterInfosMutex); + for (auto it = m_RouterInfos.begin (); it != m_RouterInfos.end ();) + { + if (it->second->IsUnreachable ()) + { + it->second->SaveProfile (); + it = m_RouterInfos.erase (it); + } + else + it++; + } + } + } - void NetDb::RequestDestination (const IdentHash& destination, RequestedDestination::RequestComplete requestComplete) - { - auto dest = m_Requests.CreateRequest (destination, false, requestComplete); // non-exploratory - if (!dest) - { - LogPrint (eLogWarning, "Destination ", destination.ToBase64(), " is requested already"); - return; - } + void NetDb::RequestDestination (const IdentHash& destination, RequestedDestination::RequestComplete requestComplete) + { + auto dest = m_Requests.CreateRequest (destination, false, requestComplete); // non-exploratory + if (!dest) + { + LogPrint (eLogWarning, "Destination ", destination.ToBase64(), " is requested already"); + return; + } - auto floodfill = GetClosestFloodfill (destination, dest->GetExcludedPeers ()); - if (floodfill) - transports.SendMessage (floodfill->GetIdentHash (), dest->CreateRequestMessage (floodfill->GetIdentHash ())); - else - { - LogPrint (eLogError, "No floodfills found"); - m_Requests.RequestComplete (destination, nullptr); - } - } - - void NetDb::HandleDatabaseStoreMsg (std::shared_ptr m) - { - const uint8_t * buf = m->GetPayload (); - size_t len = m->GetSize (); - IdentHash ident (buf + DATABASE_STORE_KEY_OFFSET); - if (ident.IsZero ()) - { - LogPrint (eLogError, "Database store with zero ident. Dropped"); - return; - } - uint32_t replyToken = bufbe32toh (buf + DATABASE_STORE_REPLY_TOKEN_OFFSET); - size_t offset = DATABASE_STORE_HEADER_SIZE; - if (replyToken) - { - auto deliveryStatus = CreateDeliveryStatusMsg (replyToken); - uint32_t tunnelID = bufbe32toh (buf + offset); - offset += 4; - if (!tunnelID) // send response directly - transports.SendMessage (buf + offset, deliveryStatus); - else - { - auto pool = i2p::tunnel::tunnels.GetExploratoryPool (); - auto outbound = pool ? pool->GetNextOutboundTunnel () : nullptr; - if (outbound) - outbound->SendTunnelDataMsg (buf + offset, tunnelID, deliveryStatus); - else - LogPrint (eLogError, "No outbound tunnels for DatabaseStore reply found"); - } - offset += 32; + auto floodfill = GetClosestFloodfill (destination, dest->GetExcludedPeers ()); + if (floodfill) + transports.SendMessage (floodfill->GetIdentHash (), dest->CreateRequestMessage (floodfill->GetIdentHash ())); + else + { + LogPrint (eLogError, "No floodfills found"); + m_Requests.RequestComplete (destination, nullptr); + } + } + + void NetDb::HandleDatabaseStoreMsg (std::shared_ptr m) + { + const uint8_t * buf = m->GetPayload (); + size_t len = m->GetSize (); + IdentHash ident (buf + DATABASE_STORE_KEY_OFFSET); + if (ident.IsZero ()) + { + LogPrint (eLogError, "Database store with zero ident. Dropped"); + return; + } + uint32_t replyToken = bufbe32toh (buf + DATABASE_STORE_REPLY_TOKEN_OFFSET); + size_t offset = DATABASE_STORE_HEADER_SIZE; + if (replyToken) + { + auto deliveryStatus = CreateDeliveryStatusMsg (replyToken); + uint32_t tunnelID = bufbe32toh (buf + offset); + offset += 4; + if (!tunnelID) // send response directly + transports.SendMessage (buf + offset, deliveryStatus); + else + { + auto pool = i2p::tunnel::tunnels.GetExploratoryPool (); + auto outbound = pool ? pool->GetNextOutboundTunnel () : nullptr; + if (outbound) + outbound->SendTunnelDataMsg (buf + offset, tunnelID, deliveryStatus); + else + LogPrint (eLogError, "No outbound tunnels for DatabaseStore reply found"); + } + offset += 32; - if (context.IsFloodfill ()) - { - // flood it - auto floodMsg = ToSharedI2NPMessage (NewI2NPShortMessage ()); - uint8_t * payload = floodMsg->GetPayload (); - memcpy (payload, buf, 33); // key + type - htobe32buf (payload + DATABASE_STORE_REPLY_TOKEN_OFFSET, 0); // zero reply token - memcpy (payload + DATABASE_STORE_HEADER_SIZE, buf + offset, len - offset); - floodMsg->len += DATABASE_STORE_HEADER_SIZE + len -offset; - floodMsg->FillI2NPMessageHeader (eI2NPDatabaseStore); - std::set excluded; - for (int i = 0; i < 3; i++) - { - auto floodfill = GetClosestFloodfill (ident, excluded); - if (floodfill) - transports.SendMessage (floodfill->GetIdentHash (), floodMsg); - } - } - } - - if (buf[DATABASE_STORE_TYPE_OFFSET]) // type - { - LogPrint ("LeaseSet"); - AddLeaseSet (ident, buf + offset, len - offset, m->from); - } - else - { - LogPrint ("RouterInfo"); - size_t size = bufbe16toh (buf + offset); - offset += 2; - if (size > 2048 || size > len - offset) - { - LogPrint ("Invalid RouterInfo length ", (int)size); - return; - } - try - { - CryptoPP::Gunzip decompressor; - decompressor.Put (buf + offset, size); - decompressor.MessageEnd(); - uint8_t uncompressed[2048]; - size_t uncomressedSize = decompressor.MaxRetrievable (); - if (uncomressedSize <= 2048) - { - decompressor.Get (uncompressed, uncomressedSize); - AddRouterInfo (ident, uncompressed, uncomressedSize); - } - else - LogPrint ("Invalid RouterInfo uncomressed length ", (int)uncomressedSize); - } - catch (CryptoPP::Exception& ex) - { - LogPrint (eLogError, "DatabaseStore: ", ex.what ()); - } - } - } + if (context.IsFloodfill ()) + { + // flood it + auto floodMsg = ToSharedI2NPMessage (NewI2NPShortMessage ()); + uint8_t * payload = floodMsg->GetPayload (); + memcpy (payload, buf, 33); // key + type + htobe32buf (payload + DATABASE_STORE_REPLY_TOKEN_OFFSET, 0); // zero reply token + memcpy (payload + DATABASE_STORE_HEADER_SIZE, buf + offset, len - offset); + floodMsg->len += DATABASE_STORE_HEADER_SIZE + len -offset; + floodMsg->FillI2NPMessageHeader (eI2NPDatabaseStore); + std::set excluded; + for (int i = 0; i < 3; i++) + { + auto floodfill = GetClosestFloodfill (ident, excluded); + if (floodfill) + transports.SendMessage (floodfill->GetIdentHash (), floodMsg); + } + } + } + + if (buf[DATABASE_STORE_TYPE_OFFSET]) // type + { + LogPrint ("LeaseSet"); + AddLeaseSet (ident, buf + offset, len - offset, m->from); + } + else + { + LogPrint ("RouterInfo"); + size_t size = bufbe16toh (buf + offset); + offset += 2; + if (size > 2048 || size > len - offset) + { + LogPrint ("Invalid RouterInfo length ", (int)size); + return; + } + try + { + CryptoPP::Gunzip decompressor; + decompressor.Put (buf + offset, size); + decompressor.MessageEnd(); + uint8_t uncompressed[2048]; + size_t uncomressedSize = decompressor.MaxRetrievable (); + if (uncomressedSize <= 2048) + { + decompressor.Get (uncompressed, uncomressedSize); + AddRouterInfo (ident, uncompressed, uncomressedSize); + } + else + LogPrint ("Invalid RouterInfo uncomressed length ", (int)uncomressedSize); + } + catch (CryptoPP::Exception& ex) + { + LogPrint (eLogError, "DatabaseStore: ", ex.what ()); + } + } + } - void NetDb::HandleDatabaseSearchReplyMsg (std::shared_ptr msg) - { - const uint8_t * buf = msg->GetPayload (); - char key[48]; - int l = i2p::data::ByteStreamToBase64 (buf, 32, key, 48); - key[l] = 0; - int num = buf[32]; // num - LogPrint ("DatabaseSearchReply for ", key, " num=", num); - IdentHash ident (buf); - auto dest = m_Requests.FindRequest (ident); - if (dest) - { - bool deleteDest = true; - if (num > 0) - { - auto pool = i2p::tunnel::tunnels.GetExploratoryPool (); - auto outbound = pool ? pool->GetNextOutboundTunnel () : nullptr; - auto inbound = pool ? pool->GetNextInboundTunnel () : nullptr; - if (!dest->IsExploratory ()) - { - // reply to our destination. Try other floodfills - if (outbound && inbound ) - { - std::vector msgs; - auto count = dest->GetExcludedPeers ().size (); - if (count < 7) - { - auto nextFloodfill = GetClosestFloodfill (dest->GetDestination (), dest->GetExcludedPeers ()); - if (nextFloodfill) - { - // tell floodfill about us - msgs.push_back (i2p::tunnel::TunnelMessageBlock - { - i2p::tunnel::eDeliveryTypeRouter, - nextFloodfill->GetIdentHash (), 0, - CreateDatabaseStoreMsg () - }); - - // request destination - LogPrint ("Try ", key, " at ", count, " floodfill ", nextFloodfill->GetIdentHash ().ToBase64 ()); - auto msg = dest->CreateRequestMessage (nextFloodfill, inbound); - msgs.push_back (i2p::tunnel::TunnelMessageBlock - { - i2p::tunnel::eDeliveryTypeRouter, - nextFloodfill->GetIdentHash (), 0, msg - }); - deleteDest = false; - } - } - else - LogPrint (key, " was not found on 7 floodfills"); + void NetDb::HandleDatabaseSearchReplyMsg (std::shared_ptr msg) + { + const uint8_t * buf = msg->GetPayload (); + char key[48]; + int l = i2p::data::ByteStreamToBase64 (buf, 32, key, 48); + key[l] = 0; + int num = buf[32]; // num + LogPrint ("DatabaseSearchReply for ", key, " num=", num); + IdentHash ident (buf); + auto dest = m_Requests.FindRequest (ident); + if (dest) + { + bool deleteDest = true; + if (num > 0) + { + auto pool = i2p::tunnel::tunnels.GetExploratoryPool (); + auto outbound = pool ? pool->GetNextOutboundTunnel () : nullptr; + auto inbound = pool ? pool->GetNextInboundTunnel () : nullptr; + if (!dest->IsExploratory ()) + { + // reply to our destination. Try other floodfills + if (outbound && inbound ) + { + std::vector msgs; + auto count = dest->GetExcludedPeers ().size (); + if (count < 7) + { + auto nextFloodfill = GetClosestFloodfill (dest->GetDestination (), dest->GetExcludedPeers ()); + if (nextFloodfill) + { + // tell floodfill about us + msgs.push_back (i2p::tunnel::TunnelMessageBlock + { + i2p::tunnel::eDeliveryTypeRouter, + nextFloodfill->GetIdentHash (), 0, + CreateDatabaseStoreMsg () + }); + + // request destination + LogPrint ("Try ", key, " at ", count, " floodfill ", nextFloodfill->GetIdentHash ().ToBase64 ()); + auto msg = dest->CreateRequestMessage (nextFloodfill, inbound); + msgs.push_back (i2p::tunnel::TunnelMessageBlock + { + i2p::tunnel::eDeliveryTypeRouter, + nextFloodfill->GetIdentHash (), 0, msg + }); + deleteDest = false; + } + } + else + LogPrint (key, " was not found on 7 floodfills"); - if (msgs.size () > 0) - outbound->SendTunnelDataMsg (msgs); - } - } + if (msgs.size () > 0) + outbound->SendTunnelDataMsg (msgs); + } + } - if (deleteDest) - // no more requests for the destinationation. delete it - m_Requests.RequestComplete (ident, nullptr); - } - else - // no more requests for detination possible. delete it - m_Requests.RequestComplete (ident, nullptr); - } - else - LogPrint ("Requested destination for ", key, " not found"); + if (deleteDest) + // no more requests for the destinationation. delete it + m_Requests.RequestComplete (ident, nullptr); + } + else + // no more requests for detination possible. delete it + m_Requests.RequestComplete (ident, nullptr); + } + else + LogPrint ("Requested destination for ", key, " not found"); - // try responses - for (int i = 0; i < num; i++) - { - const uint8_t * router = buf + 33 + i*32; - char peerHash[48]; - int l1 = i2p::data::ByteStreamToBase64 (router, 32, peerHash, 48); - peerHash[l1] = 0; - LogPrint (i,": ", peerHash); + // try responses + for (int i = 0; i < num; i++) + { + const uint8_t * router = buf + 33 + i*32; + char peerHash[48]; + int l1 = i2p::data::ByteStreamToBase64 (router, 32, peerHash, 48); + peerHash[l1] = 0; + LogPrint (i,": ", peerHash); - auto r = FindRouter (router); - if (!r || i2p::util::GetMillisecondsSinceEpoch () > r->GetTimestamp () + 3600*1000LL) - { - // router with ident not found or too old (1 hour) - LogPrint ("Found new/outdated router. Requesting RouterInfo ..."); - RequestDestination (router); - } - else - LogPrint ("Bayan"); - } - } - - void NetDb::HandleDatabaseLookupMsg (std::shared_ptr msg) - { - const uint8_t * buf = msg->GetPayload (); - IdentHash ident (buf); - if (ident.IsZero ()) - { - LogPrint (eLogError, "DatabaseLookup for zero ident. Ignored"); - return; - } - char key[48]; - int l = i2p::data::ByteStreamToBase64 (buf, 32, key, 48); - key[l] = 0; - uint8_t flag = buf[64]; - LogPrint ("DatabaseLookup for ", key, " recieved flags=", (int)flag); - uint8_t lookupType = flag & DATABASE_LOOKUP_TYPE_FLAGS_MASK; - const uint8_t * excluded = buf + 65; - uint32_t replyTunnelID = 0; - if (flag & DATABASE_LOOKUP_DELIVERY_FLAG) //reply to tunnel - { - replyTunnelID = bufbe32toh (buf + 64); - excluded += 4; - } - uint16_t numExcluded = bufbe16toh (excluded); - excluded += 2; - if (numExcluded > 512) - { - LogPrint ("Number of excluded peers", numExcluded, " exceeds 512"); - numExcluded = 0; // TODO: - } - - std::shared_ptr replyMsg; - if (lookupType == DATABASE_LOOKUP_TYPE_EXPLORATORY_LOOKUP) - { - LogPrint ("Exploratory close to ", key, " ", numExcluded, " excluded"); - std::set excludedRouters; - for (int i = 0; i < numExcluded; i++) - { - excludedRouters.insert (excluded); - excluded += 32; - } - std::vector routers; - for (int i = 0; i < 3; i++) - { - auto r = GetClosestNonFloodfill (ident, excludedRouters); - if (r) - { - routers.push_back (r->GetIdentHash ()); - excludedRouters.insert (r->GetIdentHash ()); - } - } - replyMsg = CreateDatabaseSearchReply (ident, routers); - } - else - { - if (lookupType == DATABASE_LOOKUP_TYPE_ROUTERINFO_LOOKUP || - lookupType == DATABASE_LOOKUP_TYPE_NORMAL_LOOKUP) - { - auto router = FindRouter (ident); - if (router) - { - LogPrint ("Requested RouterInfo ", key, " found"); - router->LoadBuffer (); - if (router->GetBuffer ()) - replyMsg = CreateDatabaseStoreMsg (router); - } - } - - if (!replyMsg && (lookupType == DATABASE_LOOKUP_TYPE_LEASESET_LOOKUP || - lookupType == DATABASE_LOOKUP_TYPE_NORMAL_LOOKUP)) - { - auto leaseSet = FindLeaseSet (ident); - if (leaseSet) // we don't send back our LeaseSets - { - LogPrint ("Requested LeaseSet ", key, " found"); - replyMsg = CreateDatabaseStoreMsg (leaseSet); - } - } - - if (!replyMsg) - { - LogPrint ("Requested ", key, " not found. ", numExcluded, " excluded"); - std::set excludedRouters; - for (int i = 0; i < numExcluded; i++) - { - excludedRouters.insert (excluded); - excluded += 32; - } - replyMsg = CreateDatabaseSearchReply (ident, GetClosestFloodfills (ident, 3, excludedRouters)); - } - } - - if (replyMsg) - { - if (replyTunnelID) - { - // encryption might be used though tunnel only - if (flag & DATABASE_LOOKUP_ENCYPTION_FLAG) // encrypted reply requested - { - const uint8_t * sessionKey = excluded; - uint8_t numTags = sessionKey[32]; - if (numTags > 0) - { - const uint8_t * sessionTag = sessionKey + 33; // take first tag - i2p::garlic::GarlicRoutingSession garlic (sessionKey, sessionTag); - replyMsg = garlic.WrapSingleMessage (replyMsg); - } - } - auto exploratoryPool = i2p::tunnel::tunnels.GetExploratoryPool (); - auto outbound = exploratoryPool ? exploratoryPool->GetNextOutboundTunnel () : nullptr; - if (outbound) - outbound->SendTunnelDataMsg (buf+32, replyTunnelID, replyMsg); - else - transports.SendMessage (buf+32, i2p::CreateTunnelGatewayMsg (replyTunnelID, replyMsg)); - } - else - transports.SendMessage (buf+32, replyMsg); - } - } + auto r = FindRouter (router); + if (!r || i2p::util::GetMillisecondsSinceEpoch () > r->GetTimestamp () + 3600*1000LL) + { + // router with ident not found or too old (1 hour) + LogPrint ("Found new/outdated router. Requesting RouterInfo ..."); + RequestDestination (router); + } + else + LogPrint ("Bayan"); + } + } + + void NetDb::HandleDatabaseLookupMsg (std::shared_ptr msg) + { + const uint8_t * buf = msg->GetPayload (); + IdentHash ident (buf); + if (ident.IsZero ()) + { + LogPrint (eLogError, "DatabaseLookup for zero ident. Ignored"); + return; + } + char key[48]; + int l = i2p::data::ByteStreamToBase64 (buf, 32, key, 48); + key[l] = 0; + uint8_t flag = buf[64]; + LogPrint ("DatabaseLookup for ", key, " recieved flags=", (int)flag); + uint8_t lookupType = flag & DATABASE_LOOKUP_TYPE_FLAGS_MASK; + const uint8_t * excluded = buf + 65; + uint32_t replyTunnelID = 0; + if (flag & DATABASE_LOOKUP_DELIVERY_FLAG) //reply to tunnel + { + replyTunnelID = bufbe32toh (buf + 64); + excluded += 4; + } + uint16_t numExcluded = bufbe16toh (excluded); + excluded += 2; + if (numExcluded > 512) + { + LogPrint ("Number of excluded peers", numExcluded, " exceeds 512"); + numExcluded = 0; // TODO: + } + + std::shared_ptr replyMsg; + if (lookupType == DATABASE_LOOKUP_TYPE_EXPLORATORY_LOOKUP) + { + LogPrint ("Exploratory close to ", key, " ", numExcluded, " excluded"); + std::set excludedRouters; + for (int i = 0; i < numExcluded; i++) + { + excludedRouters.insert (excluded); + excluded += 32; + } + std::vector routers; + for (int i = 0; i < 3; i++) + { + auto r = GetClosestNonFloodfill (ident, excludedRouters); + if (r) + { + routers.push_back (r->GetIdentHash ()); + excludedRouters.insert (r->GetIdentHash ()); + } + } + replyMsg = CreateDatabaseSearchReply (ident, routers); + } + else + { + if (lookupType == DATABASE_LOOKUP_TYPE_ROUTERINFO_LOOKUP || + lookupType == DATABASE_LOOKUP_TYPE_NORMAL_LOOKUP) + { + auto router = FindRouter (ident); + if (router) + { + LogPrint ("Requested RouterInfo ", key, " found"); + router->LoadBuffer (); + if (router->GetBuffer ()) + replyMsg = CreateDatabaseStoreMsg (router); + } + } + + if (!replyMsg && (lookupType == DATABASE_LOOKUP_TYPE_LEASESET_LOOKUP || + lookupType == DATABASE_LOOKUP_TYPE_NORMAL_LOOKUP)) + { + auto leaseSet = FindLeaseSet (ident); + if (leaseSet) // we don't send back our LeaseSets + { + LogPrint ("Requested LeaseSet ", key, " found"); + replyMsg = CreateDatabaseStoreMsg (leaseSet); + } + } + + if (!replyMsg) + { + LogPrint ("Requested ", key, " not found. ", numExcluded, " excluded"); + std::set excludedRouters; + for (int i = 0; i < numExcluded; i++) + { + excludedRouters.insert (excluded); + excluded += 32; + } + replyMsg = CreateDatabaseSearchReply (ident, GetClosestFloodfills (ident, 3, excludedRouters)); + } + } + + if (replyMsg) + { + if (replyTunnelID) + { + // encryption might be used though tunnel only + if (flag & DATABASE_LOOKUP_ENCYPTION_FLAG) // encrypted reply requested + { + const uint8_t * sessionKey = excluded; + uint8_t numTags = sessionKey[32]; + if (numTags > 0) + { + const uint8_t * sessionTag = sessionKey + 33; // take first tag + i2p::garlic::GarlicRoutingSession garlic (sessionKey, sessionTag); + replyMsg = garlic.WrapSingleMessage (replyMsg); + } + } + auto exploratoryPool = i2p::tunnel::tunnels.GetExploratoryPool (); + auto outbound = exploratoryPool ? exploratoryPool->GetNextOutboundTunnel () : nullptr; + if (outbound) + outbound->SendTunnelDataMsg (buf+32, replyTunnelID, replyMsg); + else + transports.SendMessage (buf+32, i2p::CreateTunnelGatewayMsg (replyTunnelID, replyMsg)); + } + else + transports.SendMessage (buf+32, replyMsg); + } + } - void NetDb::Explore (int numDestinations) - { - // new requests - auto exploratoryPool = i2p::tunnel::tunnels.GetExploratoryPool (); - auto outbound = exploratoryPool ? exploratoryPool->GetNextOutboundTunnel () : nullptr; - auto inbound = exploratoryPool ? exploratoryPool->GetNextInboundTunnel () : nullptr; - bool throughTunnels = outbound && inbound; - - CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator (); - uint8_t randomHash[32]; - std::vector msgs; - std::set floodfills; - LogPrint ("Exploring new ", numDestinations, " routers ..."); - for (int i = 0; i < numDestinations; i++) - { - rnd.GenerateBlock (randomHash, 32); - auto dest = m_Requests.CreateRequest (randomHash, true); // exploratory - if (!dest) - { - LogPrint (eLogWarning, "Exploratory destination is requested already"); - return; - } - auto floodfill = GetClosestFloodfill (randomHash, dest->GetExcludedPeers ()); - if (floodfill && !floodfills.count (floodfill.get ())) // request floodfill only once - { - floodfills.insert (floodfill.get ()); - if (i2p::transport::transports.IsConnected (floodfill->GetIdentHash ())) - throughTunnels = false; - if (throughTunnels) - { - msgs.push_back (i2p::tunnel::TunnelMessageBlock - { - i2p::tunnel::eDeliveryTypeRouter, - floodfill->GetIdentHash (), 0, - CreateDatabaseStoreMsg () // tell floodfill about us - }); - msgs.push_back (i2p::tunnel::TunnelMessageBlock - { - i2p::tunnel::eDeliveryTypeRouter, - floodfill->GetIdentHash (), 0, - dest->CreateRequestMessage (floodfill, inbound) // explore - }); - } - else - i2p::transport::transports.SendMessage (floodfill->GetIdentHash (), dest->CreateRequestMessage (floodfill->GetIdentHash ())); - } - else - m_Requests.RequestComplete (randomHash, nullptr); - } - if (throughTunnels && msgs.size () > 0) - outbound->SendTunnelDataMsg (msgs); - } + void NetDb::Explore (int numDestinations) + { + // new requests + auto exploratoryPool = i2p::tunnel::tunnels.GetExploratoryPool (); + auto outbound = exploratoryPool ? exploratoryPool->GetNextOutboundTunnel () : nullptr; + auto inbound = exploratoryPool ? exploratoryPool->GetNextInboundTunnel () : nullptr; + bool throughTunnels = outbound && inbound; + + CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator (); + uint8_t randomHash[32]; + std::vector msgs; + std::set floodfills; + LogPrint ("Exploring new ", numDestinations, " routers ..."); + for (int i = 0; i < numDestinations; i++) + { + rnd.GenerateBlock (randomHash, 32); + auto dest = m_Requests.CreateRequest (randomHash, true); // exploratory + if (!dest) + { + LogPrint (eLogWarning, "Exploratory destination is requested already"); + return; + } + auto floodfill = GetClosestFloodfill (randomHash, dest->GetExcludedPeers ()); + if (floodfill && !floodfills.count (floodfill.get ())) // request floodfill only once + { + floodfills.insert (floodfill.get ()); + if (i2p::transport::transports.IsConnected (floodfill->GetIdentHash ())) + throughTunnels = false; + if (throughTunnels) + { + msgs.push_back (i2p::tunnel::TunnelMessageBlock + { + i2p::tunnel::eDeliveryTypeRouter, + floodfill->GetIdentHash (), 0, + CreateDatabaseStoreMsg () // tell floodfill about us + }); + msgs.push_back (i2p::tunnel::TunnelMessageBlock + { + i2p::tunnel::eDeliveryTypeRouter, + floodfill->GetIdentHash (), 0, + dest->CreateRequestMessage (floodfill, inbound) // explore + }); + } + else + i2p::transport::transports.SendMessage (floodfill->GetIdentHash (), dest->CreateRequestMessage (floodfill->GetIdentHash ())); + } + else + m_Requests.RequestComplete (randomHash, nullptr); + } + if (throughTunnels && msgs.size () > 0) + outbound->SendTunnelDataMsg (msgs); + } - void NetDb::Publish () - { - std::set excluded; // TODO: fill up later - for (int i = 0; i < 2; i++) - { - auto floodfill = GetClosestFloodfill (i2p::context.GetRouterInfo ().GetIdentHash (), excluded); - if (floodfill) - { - uint32_t replyToken = i2p::context.GetRandomNumberGenerator ().GenerateWord32 (); - LogPrint ("Publishing our RouterInfo to ", floodfill->GetIdentHashAbbreviation (), ". reply token=", replyToken); - transports.SendMessage (floodfill->GetIdentHash (), CreateDatabaseStoreMsg (i2p::context.GetSharedRouterInfo (), replyToken)); - excluded.insert (floodfill->GetIdentHash ()); - } - } - } + void NetDb::Publish () + { + std::set excluded; // TODO: fill up later + for (int i = 0; i < 2; i++) + { + auto floodfill = GetClosestFloodfill (i2p::context.GetRouterInfo ().GetIdentHash (), excluded); + if (floodfill) + { + uint32_t replyToken = i2p::context.GetRandomNumberGenerator ().GenerateWord32 (); + LogPrint ("Publishing our RouterInfo to ", floodfill->GetIdentHashAbbreviation (), ". reply token=", replyToken); + transports.SendMessage (floodfill->GetIdentHash (), CreateDatabaseStoreMsg (i2p::context.GetSharedRouterInfo (), replyToken)); + excluded.insert (floodfill->GetIdentHash ()); + } + } + } - std::shared_ptr NetDb::GetRandomRouter () const - { - return GetRandomRouter ( - [](std::shared_ptr router)->bool - { - return !router->IsHidden (); - }); - } - - std::shared_ptr NetDb::GetRandomRouter (std::shared_ptr compatibleWith) const - { - return GetRandomRouter ( - [compatibleWith](std::shared_ptr router)->bool - { - return !router->IsHidden () && router != compatibleWith && - router->IsCompatible (*compatibleWith); - }); - } + std::shared_ptr NetDb::GetRandomRouter () const + { + return GetRandomRouter ( + [](std::shared_ptr router)->bool + { + return !router->IsHidden (); + }); + } + + std::shared_ptr NetDb::GetRandomRouter (std::shared_ptr compatibleWith) const + { + return GetRandomRouter ( + [compatibleWith](std::shared_ptr router)->bool + { + return !router->IsHidden () && router != compatibleWith && + router->IsCompatible (*compatibleWith); + }); + } - std::shared_ptr NetDb::GetRandomPeerTestRouter () const - { - return GetRandomRouter ( - [](std::shared_ptr router)->bool - { - return !router->IsHidden () && router->IsPeerTesting (); - }); - } + std::shared_ptr NetDb::GetRandomPeerTestRouter () const + { + return GetRandomRouter ( + [](std::shared_ptr router)->bool + { + return !router->IsHidden () && router->IsPeerTesting (); + }); + } - std::shared_ptr NetDb::GetRandomIntroducer () const - { - return GetRandomRouter ( - [](std::shared_ptr router)->bool - { - return !router->IsHidden () && router->IsIntroducer (); - }); - } - - std::shared_ptr NetDb::GetHighBandwidthRandomRouter (std::shared_ptr compatibleWith) const - { - return GetRandomRouter ( - [compatibleWith](std::shared_ptr router)->bool - { - return !router->IsHidden () && router != compatibleWith && - router->IsCompatible (*compatibleWith) && - (router->GetCaps () & RouterInfo::eHighBandwidth); - }); - } - - template - std::shared_ptr NetDb::GetRandomRouter (Filter filter) const - { - CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator (); - uint32_t ind = rnd.GenerateWord32 (0, m_RouterInfos.size () - 1); - for (int j = 0; j < 2; j++) - { - uint32_t i = 0; - std::unique_lock l(m_RouterInfosMutex); - for (auto it: m_RouterInfos) - { - if (i >= ind) - { - if (!it.second->IsUnreachable () && filter (it.second)) - return it.second; - } - else - i++; - } - // we couldn't find anything, try second pass - ind = 0; - } - return nullptr; // seems we have too few routers - } - - void NetDb::PostI2NPMsg (std::shared_ptr msg) - { - if (msg) m_Queue.Put (msg); - } + std::shared_ptr NetDb::GetRandomIntroducer () const + { + return GetRandomRouter ( + [](std::shared_ptr router)->bool + { + return !router->IsHidden () && router->IsIntroducer (); + }); + } + + std::shared_ptr NetDb::GetHighBandwidthRandomRouter (std::shared_ptr compatibleWith) const + { + return GetRandomRouter ( + [compatibleWith](std::shared_ptr router)->bool + { + return !router->IsHidden () && router != compatibleWith && + router->IsCompatible (*compatibleWith) && + (router->GetCaps () & RouterInfo::eHighBandwidth); + }); + } + + template + std::shared_ptr NetDb::GetRandomRouter (Filter filter) const + { + CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator (); + uint32_t ind = rnd.GenerateWord32 (0, m_RouterInfos.size () - 1); + for (int j = 0; j < 2; j++) + { + uint32_t i = 0; + std::unique_lock l(m_RouterInfosMutex); + for (auto it: m_RouterInfos) + { + if (i >= ind) + { + if (!it.second->IsUnreachable () && filter (it.second)) + return it.second; + } + else + i++; + } + // we couldn't find anything, try second pass + ind = 0; + } + return nullptr; // seems we have too few routers + } + + void NetDb::PostI2NPMsg (std::shared_ptr msg) + { + if (msg) m_Queue.Put (msg); + } - std::shared_ptr NetDb::GetClosestFloodfill (const IdentHash& destination, - const std::set& excluded) const - { - std::shared_ptr r; - XORMetric minMetric; - IdentHash destKey = CreateRoutingKey (destination); - minMetric.SetMax (); - std::unique_lock l(m_FloodfillsMutex); - for (auto it: m_Floodfills) - { - if (!it->IsUnreachable ()) - { - XORMetric m = destKey ^ it->GetIdentHash (); - if (m < minMetric && !excluded.count (it->GetIdentHash ())) - { - minMetric = m; - r = it; - } - } - } - return r; - } + std::shared_ptr NetDb::GetClosestFloodfill (const IdentHash& destination, + const std::set& excluded) const + { + std::shared_ptr r; + XORMetric minMetric; + IdentHash destKey = CreateRoutingKey (destination); + minMetric.SetMax (); + std::unique_lock l(m_FloodfillsMutex); + for (auto it: m_Floodfills) + { + if (!it->IsUnreachable ()) + { + XORMetric m = destKey ^ it->GetIdentHash (); + if (m < minMetric && !excluded.count (it->GetIdentHash ())) + { + minMetric = m; + r = it; + } + } + } + return r; + } - std::vector NetDb::GetClosestFloodfills (const IdentHash& destination, size_t num, - std::set& excluded) const - { - struct Sorted - { - std::shared_ptr r; - XORMetric metric; - bool operator< (const Sorted& other) const { return metric < other.metric; }; - }; + std::vector NetDb::GetClosestFloodfills (const IdentHash& destination, size_t num, + std::set& excluded) const + { + struct Sorted + { + std::shared_ptr r; + XORMetric metric; + bool operator< (const Sorted& other) const { return metric < other.metric; }; + }; - std::set sorted; - IdentHash destKey = CreateRoutingKey (destination); - { - std::unique_lock l(m_FloodfillsMutex); - for (auto it: m_Floodfills) - { - if (!it->IsUnreachable ()) - { - XORMetric m = destKey ^ it->GetIdentHash (); - if (sorted.size () < num) - sorted.insert ({it, m}); - else if (m < sorted.rbegin ()->metric) - { - sorted.insert ({it, m}); - sorted.erase (std::prev (sorted.end ())); - } - } - } - } + std::set sorted; + IdentHash destKey = CreateRoutingKey (destination); + { + std::unique_lock l(m_FloodfillsMutex); + for (auto it: m_Floodfills) + { + if (!it->IsUnreachable ()) + { + XORMetric m = destKey ^ it->GetIdentHash (); + if (sorted.size () < num) + sorted.insert ({it, m}); + else if (m < sorted.rbegin ()->metric) + { + sorted.insert ({it, m}); + sorted.erase (std::prev (sorted.end ())); + } + } + } + } - std::vector res; - size_t i = 0; - for (auto it: sorted) - { - if (i < num) - { - auto& ident = it.r->GetIdentHash (); - if (!excluded.count (ident)) - { - res.push_back (ident); - i++; - } - } - else - break; - } - return res; - } + std::vector res; + size_t i = 0; + for (auto it: sorted) + { + if (i < num) + { + auto& ident = it.r->GetIdentHash (); + if (!excluded.count (ident)) + { + res.push_back (ident); + i++; + } + } + else + break; + } + return res; + } - std::shared_ptr NetDb::GetClosestNonFloodfill (const IdentHash& destination, - const std::set& excluded) const - { - std::shared_ptr r; - XORMetric minMetric; - IdentHash destKey = CreateRoutingKey (destination); - minMetric.SetMax (); - // must be called from NetDb thread only - for (auto it: m_RouterInfos) - { - if (!it.second->IsFloodfill ()) - { - XORMetric m = destKey ^ it.first; - if (m < minMetric && !excluded.count (it.first)) - { - minMetric = m; - r = it.second; - } - } - } - return r; - } - - void NetDb::ManageLeaseSets () - { - for (auto it = m_LeaseSets.begin (); it != m_LeaseSets.end ();) - { - if (!it->second->HasNonExpiredLeases ()) // all leases expired - { - LogPrint ("LeaseSet ", it->second->GetIdentHash ().ToBase64 (), " expired"); - it = m_LeaseSets.erase (it); - } - else - it++; - } - } + std::shared_ptr NetDb::GetClosestNonFloodfill (const IdentHash& destination, + const std::set& excluded) const + { + std::shared_ptr r; + XORMetric minMetric; + IdentHash destKey = CreateRoutingKey (destination); + minMetric.SetMax (); + // must be called from NetDb thread only + for (auto it: m_RouterInfos) + { + if (!it.second->IsFloodfill ()) + { + XORMetric m = destKey ^ it.first; + if (m < minMetric && !excluded.count (it.first)) + { + minMetric = m; + r = it.second; + } + } + } + return r; + } + + void NetDb::ManageLeaseSets () + { + for (auto it = m_LeaseSets.begin (); it != m_LeaseSets.end ();) + { + if (!it->second->HasNonExpiredLeases ()) // all leases expired + { + LogPrint ("LeaseSet ", it->second->GetIdentHash ().ToBase64 (), " expired"); + it = m_LeaseSets.erase (it); + } + else + it++; + } + } } } diff --git a/NetDb.h b/NetDb.h index 71db2e52..d63fdc7c 100644 --- a/NetDb.h +++ b/NetDb.h @@ -21,85 +21,85 @@ namespace i2p { namespace data -{ - - class NetDb - { - public: +{ + + class NetDb + { + public: - NetDb (); - ~NetDb (); + NetDb (); + ~NetDb (); - void Start (); - void Stop (); - - void AddRouterInfo (const uint8_t * buf, int len); - void AddRouterInfo (const IdentHash& ident, const uint8_t * buf, int len); - void AddLeaseSet (const IdentHash& ident, const uint8_t * buf, int len, std::shared_ptr from); - std::shared_ptr FindRouter (const IdentHash& ident) const; - std::shared_ptr FindLeaseSet (const IdentHash& destination) const; + void Start (); + void Stop (); + + void AddRouterInfo (const uint8_t * buf, int len); + void AddRouterInfo (const IdentHash& ident, const uint8_t * buf, int len); + void AddLeaseSet (const IdentHash& ident, const uint8_t * buf, int len, std::shared_ptr from); + std::shared_ptr FindRouter (const IdentHash& ident) const; + std::shared_ptr FindLeaseSet (const IdentHash& destination) const; - void RequestDestination (const IdentHash& destination, RequestedDestination::RequestComplete requestComplete = nullptr); - - void HandleDatabaseStoreMsg (std::shared_ptr msg); - void HandleDatabaseSearchReplyMsg (std::shared_ptr msg); - void HandleDatabaseLookupMsg (std::shared_ptr msg); + void RequestDestination (const IdentHash& destination, RequestedDestination::RequestComplete requestComplete = nullptr); + + void HandleDatabaseStoreMsg (std::shared_ptr msg); + void HandleDatabaseSearchReplyMsg (std::shared_ptr msg); + void HandleDatabaseLookupMsg (std::shared_ptr msg); - std::shared_ptr GetRandomRouter () const; - std::shared_ptr GetRandomRouter (std::shared_ptr compatibleWith) const; - std::shared_ptr GetHighBandwidthRandomRouter (std::shared_ptr compatibleWith) const; - std::shared_ptr GetRandomPeerTestRouter () const; - std::shared_ptr GetRandomIntroducer () const; - std::shared_ptr GetClosestFloodfill (const IdentHash& destination, const std::set& excluded) const; - std::vector GetClosestFloodfills (const IdentHash& destination, size_t num, - std::set& excluded) const; - std::shared_ptr GetClosestNonFloodfill (const IdentHash& destination, const std::set& excluded) const; - void SetUnreachable (const IdentHash& ident, bool unreachable); + std::shared_ptr GetRandomRouter () const; + std::shared_ptr GetRandomRouter (std::shared_ptr compatibleWith) const; + std::shared_ptr GetHighBandwidthRandomRouter (std::shared_ptr compatibleWith) const; + std::shared_ptr GetRandomPeerTestRouter () const; + std::shared_ptr GetRandomIntroducer () const; + std::shared_ptr GetClosestFloodfill (const IdentHash& destination, const std::set& excluded) const; + std::vector GetClosestFloodfills (const IdentHash& destination, size_t num, + std::set& excluded) const; + std::shared_ptr GetClosestNonFloodfill (const IdentHash& destination, const std::set& excluded) const; + void SetUnreachable (const IdentHash& ident, bool unreachable); - void PostI2NPMsg (std::shared_ptr msg); + void PostI2NPMsg (std::shared_ptr msg); - void Reseed (); + void Reseed (); - // for web interface - int GetNumRouters () const { return m_RouterInfos.size (); }; - int GetNumFloodfills () const { return m_Floodfills.size (); }; - int GetNumLeaseSets () const { return m_LeaseSets.size (); }; - - private: + // for web interface + int GetNumRouters () const { return m_RouterInfos.size (); }; + int GetNumFloodfills () const { return m_Floodfills.size (); }; + int GetNumLeaseSets () const { return m_LeaseSets.size (); }; + + private: - bool CreateNetDb(boost::filesystem::path directory); - void Load (); - void SaveUpdated (); - void Run (); // exploratory thread - void Explore (int numDestinations); - void Publish (); - void ManageLeaseSets (); - void ManageRequests (); + bool CreateNetDb(boost::filesystem::path directory); + void Load (); + void SaveUpdated (); + void Run (); // exploratory thread + void Explore (int numDestinations); + void Publish (); + void ManageLeaseSets (); + void ManageRequests (); - template - std::shared_ptr GetRandomRouter (Filter filter) const; - - private: + template + std::shared_ptr GetRandomRouter (Filter filter) const; + + private: - std::map > m_LeaseSets; - mutable std::mutex m_RouterInfosMutex; - std::map > m_RouterInfos; - mutable std::mutex m_FloodfillsMutex; - std::list > m_Floodfills; - - bool m_IsRunning; - std::thread * m_Thread; - i2p::util::Queue > m_Queue; // of I2NPDatabaseStoreMsg + std::map > m_LeaseSets; + mutable std::mutex m_RouterInfosMutex; + std::map > m_RouterInfos; + mutable std::mutex m_FloodfillsMutex; + std::list > m_Floodfills; + + bool m_IsRunning; + std::thread * m_Thread; + i2p::util::Queue > m_Queue; // of I2NPDatabaseStoreMsg - Reseeder * m_Reseeder; + Reseeder * m_Reseeder; - friend class NetDbRequests; - NetDbRequests m_Requests; + friend class NetDbRequests; + NetDbRequests m_Requests; - static const char m_NetDbPath[]; - }; + static const char m_NetDbPath[]; + }; - extern NetDb netdb; + extern NetDb netdb; } } diff --git a/NetDbRequests.cpp b/NetDbRequests.cpp index a8ccf3f9..d3bff8ce 100644 --- a/NetDbRequests.cpp +++ b/NetDbRequests.cpp @@ -8,142 +8,142 @@ namespace i2p { namespace data { - std::shared_ptr RequestedDestination::CreateRequestMessage (std::shared_ptr router, - std::shared_ptr replyTunnel) - { - auto msg = i2p::CreateRouterInfoDatabaseLookupMsg (m_Destination, - replyTunnel->GetNextIdentHash (), replyTunnel->GetNextTunnelID (), m_IsExploratory, - &m_ExcludedPeers); - m_ExcludedPeers.insert (router->GetIdentHash ()); - m_CreationTime = i2p::util::GetSecondsSinceEpoch (); - return msg; - } + std::shared_ptr RequestedDestination::CreateRequestMessage (std::shared_ptr router, + std::shared_ptr replyTunnel) + { + auto msg = i2p::CreateRouterInfoDatabaseLookupMsg (m_Destination, + replyTunnel->GetNextIdentHash (), replyTunnel->GetNextTunnelID (), m_IsExploratory, + &m_ExcludedPeers); + m_ExcludedPeers.insert (router->GetIdentHash ()); + m_CreationTime = i2p::util::GetSecondsSinceEpoch (); + return msg; + } - std::shared_ptr RequestedDestination::CreateRequestMessage (const IdentHash& floodfill) - { - auto msg = i2p::CreateRouterInfoDatabaseLookupMsg (m_Destination, - i2p::context.GetRouterInfo ().GetIdentHash () , 0, false, &m_ExcludedPeers); - m_ExcludedPeers.insert (floodfill); - m_CreationTime = i2p::util::GetSecondsSinceEpoch (); - return msg; - } + std::shared_ptr RequestedDestination::CreateRequestMessage (const IdentHash& floodfill) + { + auto msg = i2p::CreateRouterInfoDatabaseLookupMsg (m_Destination, + i2p::context.GetRouterInfo ().GetIdentHash () , 0, false, &m_ExcludedPeers); + m_ExcludedPeers.insert (floodfill); + m_CreationTime = i2p::util::GetSecondsSinceEpoch (); + return msg; + } - void RequestedDestination::ClearExcludedPeers () - { - m_ExcludedPeers.clear (); - } - - void RequestedDestination::Success (std::shared_ptr r) - { - if (m_RequestComplete) - { - m_RequestComplete (r); - m_RequestComplete = nullptr; - } - } + void RequestedDestination::ClearExcludedPeers () + { + m_ExcludedPeers.clear (); + } + + void RequestedDestination::Success (std::shared_ptr r) + { + if (m_RequestComplete) + { + m_RequestComplete (r); + m_RequestComplete = nullptr; + } + } - void RequestedDestination::Fail () - { - if (m_RequestComplete) - { - m_RequestComplete (nullptr); - m_RequestComplete = nullptr; - } - } + void RequestedDestination::Fail () + { + if (m_RequestComplete) + { + m_RequestComplete (nullptr); + m_RequestComplete = nullptr; + } + } - void NetDbRequests::Start () - { - } + void NetDbRequests::Start () + { + } - void NetDbRequests::Stop () - { - m_RequestedDestinations.clear (); - } + void NetDbRequests::Stop () + { + m_RequestedDestinations.clear (); + } - std::shared_ptr NetDbRequests::CreateRequest (const IdentHash& destination, bool isExploratory, RequestedDestination::RequestComplete requestComplete) - { - // request RouterInfo directly - auto dest = std::make_shared (destination, isExploratory); - dest->SetRequestComplete (requestComplete); - { - std::unique_lock l(m_RequestedDestinationsMutex); - if (!m_RequestedDestinations.insert (std::make_pair (destination, - std::shared_ptr (dest))).second) // not inserted - return nullptr; - } - return dest; - } + std::shared_ptr NetDbRequests::CreateRequest (const IdentHash& destination, bool isExploratory, RequestedDestination::RequestComplete requestComplete) + { + // request RouterInfo directly + auto dest = std::make_shared (destination, isExploratory); + dest->SetRequestComplete (requestComplete); + { + std::unique_lock l(m_RequestedDestinationsMutex); + if (!m_RequestedDestinations.insert (std::make_pair (destination, + std::shared_ptr (dest))).second) // not inserted + return nullptr; + } + return dest; + } - void NetDbRequests::RequestComplete (const IdentHash& ident, std::shared_ptr r) - { - auto it = m_RequestedDestinations.find (ident); - if (it != m_RequestedDestinations.end ()) - { - if (r) - it->second->Success (r); - else - it->second->Fail (); - std::unique_lock l(m_RequestedDestinationsMutex); - m_RequestedDestinations.erase (it); - } - } + void NetDbRequests::RequestComplete (const IdentHash& ident, std::shared_ptr r) + { + auto it = m_RequestedDestinations.find (ident); + if (it != m_RequestedDestinations.end ()) + { + if (r) + it->second->Success (r); + else + it->second->Fail (); + std::unique_lock l(m_RequestedDestinationsMutex); + m_RequestedDestinations.erase (it); + } + } - std::shared_ptr NetDbRequests::FindRequest (const IdentHash& ident) const - { - auto it = m_RequestedDestinations.find (ident); - if (it != m_RequestedDestinations.end ()) - return it->second; - return nullptr; - } + std::shared_ptr NetDbRequests::FindRequest (const IdentHash& ident) const + { + auto it = m_RequestedDestinations.find (ident); + if (it != m_RequestedDestinations.end ()) + return it->second; + return nullptr; + } - void NetDbRequests::ManageRequests () - { - uint64_t ts = i2p::util::GetSecondsSinceEpoch (); - std::unique_lock l(m_RequestedDestinationsMutex); - for (auto it = m_RequestedDestinations.begin (); it != m_RequestedDestinations.end ();) - { - auto& dest = it->second; - bool done = false; - if (ts < dest->GetCreationTime () + 60) // request is worthless after 1 minute - { - if (ts > dest->GetCreationTime () + 5) // no response for 5 seconds - { - auto count = dest->GetExcludedPeers ().size (); - if (!dest->IsExploratory () && count < 7) - { - auto pool = i2p::tunnel::tunnels.GetExploratoryPool (); - auto outbound = pool->GetNextOutboundTunnel (); - auto inbound = pool->GetNextInboundTunnel (); - auto nextFloodfill = netdb.GetClosestFloodfill (dest->GetDestination (), dest->GetExcludedPeers ()); - if (nextFloodfill && outbound && inbound) - outbound->SendTunnelDataMsg (nextFloodfill->GetIdentHash (), 0, - dest->CreateRequestMessage (nextFloodfill, inbound)); - else - { - done = true; - if (!inbound) LogPrint (eLogWarning, "No inbound tunnels"); - if (!outbound) LogPrint (eLogWarning, "No outbound tunnels"); - if (!nextFloodfill) LogPrint (eLogWarning, "No more floodfills"); - } - } - else - { - if (!dest->IsExploratory ()) - LogPrint (eLogWarning, dest->GetDestination ().ToBase64 (), " not found after 7 attempts"); - done = true; - } - } - } - else // delete obsolete request - done = true; + void NetDbRequests::ManageRequests () + { + uint64_t ts = i2p::util::GetSecondsSinceEpoch (); + std::unique_lock l(m_RequestedDestinationsMutex); + for (auto it = m_RequestedDestinations.begin (); it != m_RequestedDestinations.end ();) + { + auto& dest = it->second; + bool done = false; + if (ts < dest->GetCreationTime () + 60) // request is worthless after 1 minute + { + if (ts > dest->GetCreationTime () + 5) // no response for 5 seconds + { + auto count = dest->GetExcludedPeers ().size (); + if (!dest->IsExploratory () && count < 7) + { + auto pool = i2p::tunnel::tunnels.GetExploratoryPool (); + auto outbound = pool->GetNextOutboundTunnel (); + auto inbound = pool->GetNextInboundTunnel (); + auto nextFloodfill = netdb.GetClosestFloodfill (dest->GetDestination (), dest->GetExcludedPeers ()); + if (nextFloodfill && outbound && inbound) + outbound->SendTunnelDataMsg (nextFloodfill->GetIdentHash (), 0, + dest->CreateRequestMessage (nextFloodfill, inbound)); + else + { + done = true; + if (!inbound) LogPrint (eLogWarning, "No inbound tunnels"); + if (!outbound) LogPrint (eLogWarning, "No outbound tunnels"); + if (!nextFloodfill) LogPrint (eLogWarning, "No more floodfills"); + } + } + else + { + if (!dest->IsExploratory ()) + LogPrint (eLogWarning, dest->GetDestination ().ToBase64 (), " not found after 7 attempts"); + done = true; + } + } + } + else // delete obsolete request + done = true; - if (done) - it = m_RequestedDestinations.erase (it); - else - it++; - } - } + if (done) + it = m_RequestedDestinations.erase (it); + else + it++; + } + } } } diff --git a/NetDbRequests.h b/NetDbRequests.h index 0bab7080..2620591e 100644 --- a/NetDbRequests.h +++ b/NetDbRequests.h @@ -11,57 +11,57 @@ namespace i2p { namespace data { - class RequestedDestination - { - public: + class RequestedDestination + { + public: - typedef std::function)> RequestComplete; + typedef std::function)> RequestComplete; - RequestedDestination (const IdentHash& destination, bool isExploratory = false): - m_Destination (destination), m_IsExploratory (isExploratory), m_CreationTime (0) {}; - ~RequestedDestination () { if (m_RequestComplete) m_RequestComplete (nullptr); }; + RequestedDestination (const IdentHash& destination, bool isExploratory = false): + m_Destination (destination), m_IsExploratory (isExploratory), m_CreationTime (0) {}; + ~RequestedDestination () { if (m_RequestComplete) m_RequestComplete (nullptr); }; - const IdentHash& GetDestination () const { return m_Destination; }; - int GetNumExcludedPeers () const { return m_ExcludedPeers.size (); }; - const std::set& GetExcludedPeers () { return m_ExcludedPeers; }; - void ClearExcludedPeers (); - bool IsExploratory () const { return m_IsExploratory; }; - bool IsExcluded (const IdentHash& ident) const { return m_ExcludedPeers.count (ident); }; - uint64_t GetCreationTime () const { return m_CreationTime; }; - std::shared_ptr CreateRequestMessage (std::shared_ptr, std::shared_ptr replyTunnel); - std::shared_ptr CreateRequestMessage (const IdentHash& floodfill); - - void SetRequestComplete (const RequestComplete& requestComplete) { m_RequestComplete = requestComplete; }; - bool IsRequestComplete () const { return m_RequestComplete != nullptr; }; - void Success (std::shared_ptr r); - void Fail (); - - private: + const IdentHash& GetDestination () const { return m_Destination; }; + int GetNumExcludedPeers () const { return m_ExcludedPeers.size (); }; + const std::set& GetExcludedPeers () { return m_ExcludedPeers; }; + void ClearExcludedPeers (); + bool IsExploratory () const { return m_IsExploratory; }; + bool IsExcluded (const IdentHash& ident) const { return m_ExcludedPeers.count (ident); }; + uint64_t GetCreationTime () const { return m_CreationTime; }; + std::shared_ptr CreateRequestMessage (std::shared_ptr, std::shared_ptr replyTunnel); + std::shared_ptr CreateRequestMessage (const IdentHash& floodfill); + + void SetRequestComplete (const RequestComplete& requestComplete) { m_RequestComplete = requestComplete; }; + bool IsRequestComplete () const { return m_RequestComplete != nullptr; }; + void Success (std::shared_ptr r); + void Fail (); + + private: - IdentHash m_Destination; - bool m_IsExploratory; - std::set m_ExcludedPeers; - uint64_t m_CreationTime; - RequestComplete m_RequestComplete; - }; + IdentHash m_Destination; + bool m_IsExploratory; + std::set m_ExcludedPeers; + uint64_t m_CreationTime; + RequestComplete m_RequestComplete; + }; - class NetDbRequests - { - public: + class NetDbRequests + { + public: - void Start (); - void Stop (); + void Start (); + void Stop (); - std::shared_ptr CreateRequest (const IdentHash& destination, bool isExploratory, RequestedDestination::RequestComplete requestComplete = nullptr); - void RequestComplete (const IdentHash& ident, std::shared_ptr r); - std::shared_ptr FindRequest (const IdentHash& ident) const; - void ManageRequests (); + std::shared_ptr CreateRequest (const IdentHash& destination, bool isExploratory, RequestedDestination::RequestComplete requestComplete = nullptr); + void RequestComplete (const IdentHash& ident, std::shared_ptr r); + std::shared_ptr FindRequest (const IdentHash& ident) const; + void ManageRequests (); - private: + private: - std::mutex m_RequestedDestinationsMutex; - std::map > m_RequestedDestinations; - }; + std::mutex m_RequestedDestinationsMutex; + std::map > m_RequestedDestinations; + }; } } diff --git a/Profiling.cpp b/Profiling.cpp index d0b8e85d..5d3ad943 100644 --- a/Profiling.cpp +++ b/Profiling.cpp @@ -9,205 +9,205 @@ namespace i2p { namespace data { - RouterProfile::RouterProfile (const IdentHash& identHash): - m_IdentHash (identHash), m_LastUpdateTime (boost::posix_time::second_clock::local_time()), - m_NumTunnelsAgreed (0), m_NumTunnelsDeclined (0), m_NumTunnelsNonReplied (0), - m_NumTimesTaken (0), m_NumTimesRejected (0) - { - } + RouterProfile::RouterProfile (const IdentHash& identHash): + m_IdentHash (identHash), m_LastUpdateTime (boost::posix_time::second_clock::local_time()), + m_NumTunnelsAgreed (0), m_NumTunnelsDeclined (0), m_NumTunnelsNonReplied (0), + m_NumTimesTaken (0), m_NumTimesRejected (0) + { + } - boost::posix_time::ptime RouterProfile::GetTime () const - { - return boost::posix_time::second_clock::local_time(); - } - - void RouterProfile::UpdateTime () - { - m_LastUpdateTime = GetTime (); - } - - void RouterProfile::Save () - { - // fill sections - boost::property_tree::ptree participation; - participation.put (PEER_PROFILE_PARTICIPATION_AGREED, m_NumTunnelsAgreed); - participation.put (PEER_PROFILE_PARTICIPATION_DECLINED, m_NumTunnelsDeclined); - participation.put (PEER_PROFILE_PARTICIPATION_NON_REPLIED, m_NumTunnelsNonReplied); - boost::property_tree::ptree usage; - usage.put (PEER_PROFILE_USAGE_TAKEN, m_NumTimesTaken); - usage.put (PEER_PROFILE_USAGE_REJECTED, m_NumTimesRejected); - // fill property tree - boost::property_tree::ptree pt; - pt.put (PEER_PROFILE_LAST_UPDATE_TIME, boost::posix_time::to_simple_string (m_LastUpdateTime)); - pt.put_child (PEER_PROFILE_SECTION_PARTICIPATION, participation); - pt.put_child (PEER_PROFILE_SECTION_USAGE, usage); + boost::posix_time::ptime RouterProfile::GetTime () const + { + return boost::posix_time::second_clock::local_time(); + } + + void RouterProfile::UpdateTime () + { + m_LastUpdateTime = GetTime (); + } + + void RouterProfile::Save () + { + // fill sections + boost::property_tree::ptree participation; + participation.put (PEER_PROFILE_PARTICIPATION_AGREED, m_NumTunnelsAgreed); + participation.put (PEER_PROFILE_PARTICIPATION_DECLINED, m_NumTunnelsDeclined); + participation.put (PEER_PROFILE_PARTICIPATION_NON_REPLIED, m_NumTunnelsNonReplied); + boost::property_tree::ptree usage; + usage.put (PEER_PROFILE_USAGE_TAKEN, m_NumTimesTaken); + usage.put (PEER_PROFILE_USAGE_REJECTED, m_NumTimesRejected); + // fill property tree + boost::property_tree::ptree pt; + pt.put (PEER_PROFILE_LAST_UPDATE_TIME, boost::posix_time::to_simple_string (m_LastUpdateTime)); + pt.put_child (PEER_PROFILE_SECTION_PARTICIPATION, participation); + pt.put_child (PEER_PROFILE_SECTION_USAGE, usage); - // save to file - auto path = i2p::util::filesystem::GetDefaultDataDir() / PEER_PROFILES_DIRECTORY; - if (!boost::filesystem::exists (path)) - { - // Create directory is necessary - if (!boost::filesystem::create_directory (path)) - { - LogPrint (eLogError, "Failed to create directory ", path); - return; - } - const char * chars = GetBase64SubstitutionTable (); // 64 bytes - for (int i = 0; i < 64; i++) - { - auto path1 = path / (std::string ("p") + chars[i]); - if (!boost::filesystem::create_directory (path1)) - { - LogPrint (eLogError, "Failed to create directory ", path1); - return; - } - } - } - std::string base64 = m_IdentHash.ToBase64 (); - path = path / (std::string ("p") + base64[0]); - auto filename = path / (std::string (PEER_PROFILE_PREFIX) + base64 + ".txt"); - try - { - boost::property_tree::write_ini (filename.string (), pt); - } - catch (std::exception& ex) - { - LogPrint (eLogError, "Can't write ", filename, ": ", ex.what ()); - } - } + // save to file + auto path = i2p::util::filesystem::GetDefaultDataDir() / PEER_PROFILES_DIRECTORY; + if (!boost::filesystem::exists (path)) + { + // Create directory is necessary + if (!boost::filesystem::create_directory (path)) + { + LogPrint (eLogError, "Failed to create directory ", path); + return; + } + const char * chars = GetBase64SubstitutionTable (); // 64 bytes + for (int i = 0; i < 64; i++) + { + auto path1 = path / (std::string ("p") + chars[i]); + if (!boost::filesystem::create_directory (path1)) + { + LogPrint (eLogError, "Failed to create directory ", path1); + return; + } + } + } + std::string base64 = m_IdentHash.ToBase64 (); + path = path / (std::string ("p") + base64[0]); + auto filename = path / (std::string (PEER_PROFILE_PREFIX) + base64 + ".txt"); + try + { + boost::property_tree::write_ini (filename.string (), pt); + } + catch (std::exception& ex) + { + LogPrint (eLogError, "Can't write ", filename, ": ", ex.what ()); + } + } - void RouterProfile::Load () - { - std::string base64 = m_IdentHash.ToBase64 (); - auto path = i2p::util::filesystem::GetDefaultDataDir() / PEER_PROFILES_DIRECTORY; - path /= std::string ("p") + base64[0]; - auto filename = path / (std::string (PEER_PROFILE_PREFIX) + base64 + ".txt"); - if (boost::filesystem::exists (filename)) - { - boost::property_tree::ptree pt; - try - { - boost::property_tree::read_ini (filename.string (), pt); - } - catch (std::exception& ex) - { - LogPrint (eLogError, "Can't read ", filename, ": ", ex.what ()); - return; - } - try - { - auto t = pt.get (PEER_PROFILE_LAST_UPDATE_TIME, ""); - if (t.length () > 0) - m_LastUpdateTime = boost::posix_time::time_from_string (t); - if ((GetTime () - m_LastUpdateTime).hours () < PEER_PROFILE_EXPIRATION_TIMEOUT) - { - try - { - // read participations - auto participations = pt.get_child (PEER_PROFILE_SECTION_PARTICIPATION); - m_NumTunnelsAgreed = participations.get (PEER_PROFILE_PARTICIPATION_AGREED, 0); - m_NumTunnelsDeclined = participations.get (PEER_PROFILE_PARTICIPATION_DECLINED, 0); - m_NumTunnelsNonReplied = participations.get (PEER_PROFILE_PARTICIPATION_NON_REPLIED, 0); - } - catch (boost::property_tree::ptree_bad_path& ex) - { - LogPrint (eLogWarning, "Missing section ", PEER_PROFILE_SECTION_PARTICIPATION); - } - try - { - // read usage - auto usage = pt.get_child (PEER_PROFILE_SECTION_USAGE); - m_NumTimesTaken = usage.get (PEER_PROFILE_USAGE_TAKEN, 0); - m_NumTimesRejected = usage.get (PEER_PROFILE_USAGE_REJECTED, 0); - } - catch (boost::property_tree::ptree_bad_path& ex) - { - LogPrint (eLogWarning, "Missing section ", PEER_PROFILE_SECTION_USAGE); - } - } - else - *this = RouterProfile (m_IdentHash); - } - catch (std::exception& ex) - { - LogPrint (eLogError, "Can't read profile ", base64, " :", ex.what ()); - } - } - } - - void RouterProfile::TunnelBuildResponse (uint8_t ret) - { - UpdateTime (); - if (ret > 0) - m_NumTunnelsDeclined++; - else - m_NumTunnelsAgreed++; - } + void RouterProfile::Load () + { + std::string base64 = m_IdentHash.ToBase64 (); + auto path = i2p::util::filesystem::GetDefaultDataDir() / PEER_PROFILES_DIRECTORY; + path /= std::string ("p") + base64[0]; + auto filename = path / (std::string (PEER_PROFILE_PREFIX) + base64 + ".txt"); + if (boost::filesystem::exists (filename)) + { + boost::property_tree::ptree pt; + try + { + boost::property_tree::read_ini (filename.string (), pt); + } + catch (std::exception& ex) + { + LogPrint (eLogError, "Can't read ", filename, ": ", ex.what ()); + return; + } + try + { + auto t = pt.get (PEER_PROFILE_LAST_UPDATE_TIME, ""); + if (t.length () > 0) + m_LastUpdateTime = boost::posix_time::time_from_string (t); + if ((GetTime () - m_LastUpdateTime).hours () < PEER_PROFILE_EXPIRATION_TIMEOUT) + { + try + { + // read participations + auto participations = pt.get_child (PEER_PROFILE_SECTION_PARTICIPATION); + m_NumTunnelsAgreed = participations.get (PEER_PROFILE_PARTICIPATION_AGREED, 0); + m_NumTunnelsDeclined = participations.get (PEER_PROFILE_PARTICIPATION_DECLINED, 0); + m_NumTunnelsNonReplied = participations.get (PEER_PROFILE_PARTICIPATION_NON_REPLIED, 0); + } + catch (boost::property_tree::ptree_bad_path& ex) + { + LogPrint (eLogWarning, "Missing section ", PEER_PROFILE_SECTION_PARTICIPATION); + } + try + { + // read usage + auto usage = pt.get_child (PEER_PROFILE_SECTION_USAGE); + m_NumTimesTaken = usage.get (PEER_PROFILE_USAGE_TAKEN, 0); + m_NumTimesRejected = usage.get (PEER_PROFILE_USAGE_REJECTED, 0); + } + catch (boost::property_tree::ptree_bad_path& ex) + { + LogPrint (eLogWarning, "Missing section ", PEER_PROFILE_SECTION_USAGE); + } + } + else + *this = RouterProfile (m_IdentHash); + } + catch (std::exception& ex) + { + LogPrint (eLogError, "Can't read profile ", base64, " :", ex.what ()); + } + } + } + + void RouterProfile::TunnelBuildResponse (uint8_t ret) + { + UpdateTime (); + if (ret > 0) + m_NumTunnelsDeclined++; + else + m_NumTunnelsAgreed++; + } - void RouterProfile::TunnelNonReplied () - { - m_NumTunnelsNonReplied++; - UpdateTime (); - } + void RouterProfile::TunnelNonReplied () + { + m_NumTunnelsNonReplied++; + UpdateTime (); + } - bool RouterProfile::IsLowPartcipationRate () const - { - return 4*m_NumTunnelsAgreed < m_NumTunnelsDeclined; // < 20% rate - } + bool RouterProfile::IsLowPartcipationRate () const + { + return 4*m_NumTunnelsAgreed < m_NumTunnelsDeclined; // < 20% rate + } - bool RouterProfile::IsLowReplyRate () const - { - auto total = m_NumTunnelsAgreed + m_NumTunnelsDeclined; - return m_NumTunnelsNonReplied > 10*(total + 1); - } - - bool RouterProfile::IsBad () - { - auto isBad = IsAlwaysDeclining () || IsLowPartcipationRate () /*|| IsLowReplyRate ()*/; - if (isBad && m_NumTimesRejected > 10*(m_NumTimesTaken + 1)) - { - // reset profile - m_NumTunnelsAgreed = 0; - m_NumTunnelsDeclined = 0; - m_NumTunnelsNonReplied = 0; - isBad = false; - } - if (isBad) m_NumTimesRejected++; else m_NumTimesTaken++; - return isBad; - } - - std::shared_ptr GetRouterProfile (const IdentHash& identHash) - { - auto profile = std::make_shared (identHash); - profile->Load (); // if possible - return profile; - } + bool RouterProfile::IsLowReplyRate () const + { + auto total = m_NumTunnelsAgreed + m_NumTunnelsDeclined; + return m_NumTunnelsNonReplied > 10*(total + 1); + } + + bool RouterProfile::IsBad () + { + auto isBad = IsAlwaysDeclining () || IsLowPartcipationRate () /*|| IsLowReplyRate ()*/; + if (isBad && m_NumTimesRejected > 10*(m_NumTimesTaken + 1)) + { + // reset profile + m_NumTunnelsAgreed = 0; + m_NumTunnelsDeclined = 0; + m_NumTunnelsNonReplied = 0; + isBad = false; + } + if (isBad) m_NumTimesRejected++; else m_NumTimesTaken++; + return isBad; + } + + std::shared_ptr GetRouterProfile (const IdentHash& identHash) + { + auto profile = std::make_shared (identHash); + profile->Load (); // if possible + return profile; + } - void DeleteObsoleteProfiles () - { - int num = 0; - auto ts = boost::posix_time::second_clock::local_time(); - boost::filesystem::path p (i2p::util::filesystem::GetDataDir()/PEER_PROFILES_DIRECTORY); - if (boost::filesystem::exists (p)) - { - boost::filesystem::directory_iterator end; - for (boost::filesystem::directory_iterator it (p); it != end; ++it) - { - if (boost::filesystem::is_directory (it->status())) - { - for (boost::filesystem::directory_iterator it1 (it->path ()); it1 != end; ++it1) - { - auto lastModified = boost::posix_time::from_time_t (boost::filesystem::last_write_time (it1->path ())); - if ((ts - lastModified).hours () >= PEER_PROFILE_EXPIRATION_TIMEOUT) - { - boost::filesystem::remove (it1->path ()); - num++; - } - } - } - } - } - LogPrint (eLogInfo, num, " obsolete profiles deleted"); - } -} -} + void DeleteObsoleteProfiles () + { + int num = 0; + auto ts = boost::posix_time::second_clock::local_time(); + boost::filesystem::path p (i2p::util::filesystem::GetDataDir()/PEER_PROFILES_DIRECTORY); + if (boost::filesystem::exists (p)) + { + boost::filesystem::directory_iterator end; + for (boost::filesystem::directory_iterator it (p); it != end; ++it) + { + if (boost::filesystem::is_directory (it->status())) + { + for (boost::filesystem::directory_iterator it1 (it->path ()); it1 != end; ++it1) + { + auto lastModified = boost::posix_time::from_time_t (boost::filesystem::last_write_time (it1->path ())); + if ((ts - lastModified).hours () >= PEER_PROFILE_EXPIRATION_TIMEOUT) + { + boost::filesystem::remove (it1->path ()); + num++; + } + } + } + } + } + LogPrint (eLogInfo, num, " obsolete profiles deleted"); + } +} +} diff --git a/Profiling.h b/Profiling.h index 0690d6cb..d4758f87 100644 --- a/Profiling.h +++ b/Profiling.h @@ -8,62 +8,62 @@ namespace i2p { namespace data -{ - const char PEER_PROFILES_DIRECTORY[] = "peerProfiles"; - const char PEER_PROFILE_PREFIX[] = "profile-"; - // sections - const char PEER_PROFILE_SECTION_PARTICIPATION[] = "participation"; - const char PEER_PROFILE_SECTION_USAGE[] = "usage"; - // params - const char PEER_PROFILE_LAST_UPDATE_TIME[] = "lastupdatetime"; - const char PEER_PROFILE_PARTICIPATION_AGREED[] = "agreed"; - const char PEER_PROFILE_PARTICIPATION_DECLINED[] = "declined"; - const char PEER_PROFILE_PARTICIPATION_NON_REPLIED[] = "nonreplied"; - const char PEER_PROFILE_USAGE_TAKEN[] = "taken"; - const char PEER_PROFILE_USAGE_REJECTED[] = "rejected"; +{ + const char PEER_PROFILES_DIRECTORY[] = "peerProfiles"; + const char PEER_PROFILE_PREFIX[] = "profile-"; + // sections + const char PEER_PROFILE_SECTION_PARTICIPATION[] = "participation"; + const char PEER_PROFILE_SECTION_USAGE[] = "usage"; + // params + const char PEER_PROFILE_LAST_UPDATE_TIME[] = "lastupdatetime"; + const char PEER_PROFILE_PARTICIPATION_AGREED[] = "agreed"; + const char PEER_PROFILE_PARTICIPATION_DECLINED[] = "declined"; + const char PEER_PROFILE_PARTICIPATION_NON_REPLIED[] = "nonreplied"; + const char PEER_PROFILE_USAGE_TAKEN[] = "taken"; + const char PEER_PROFILE_USAGE_REJECTED[] = "rejected"; - const int PEER_PROFILE_EXPIRATION_TIMEOUT = 72; // in hours (3 days) - - class RouterProfile - { - public: + const int PEER_PROFILE_EXPIRATION_TIMEOUT = 72; // in hours (3 days) + + class RouterProfile + { + public: - RouterProfile (const IdentHash& identHash); - RouterProfile& operator= (const RouterProfile& ) = default; - - void Save (); - void Load (); + RouterProfile (const IdentHash& identHash); + RouterProfile& operator= (const RouterProfile& ) = default; + + void Save (); + void Load (); - bool IsBad (); - - void TunnelBuildResponse (uint8_t ret); - void TunnelNonReplied (); + bool IsBad (); + + void TunnelBuildResponse (uint8_t ret); + void TunnelNonReplied (); - private: + private: - boost::posix_time::ptime GetTime () const; - void UpdateTime (); + boost::posix_time::ptime GetTime () const; + void UpdateTime (); - bool IsAlwaysDeclining () const { return !m_NumTunnelsAgreed && m_NumTunnelsDeclined >= 5; }; - bool IsLowPartcipationRate () const; - bool IsLowReplyRate () const; - - private: + bool IsAlwaysDeclining () const { return !m_NumTunnelsAgreed && m_NumTunnelsDeclined >= 5; }; + bool IsLowPartcipationRate () const; + bool IsLowReplyRate () const; + + private: - IdentHash m_IdentHash; - boost::posix_time::ptime m_LastUpdateTime; - // participation - uint32_t m_NumTunnelsAgreed; - uint32_t m_NumTunnelsDeclined; - uint32_t m_NumTunnelsNonReplied; - // usage - uint32_t m_NumTimesTaken; - uint32_t m_NumTimesRejected; - }; + IdentHash m_IdentHash; + boost::posix_time::ptime m_LastUpdateTime; + // participation + uint32_t m_NumTunnelsAgreed; + uint32_t m_NumTunnelsDeclined; + uint32_t m_NumTunnelsNonReplied; + // usage + uint32_t m_NumTimesTaken; + uint32_t m_NumTimesRejected; + }; - std::shared_ptr GetRouterProfile (const IdentHash& identHash); - void DeleteObsoleteProfiles (); -} -} + std::shared_ptr GetRouterProfile (const IdentHash& identHash); + void DeleteObsoleteProfiles (); +} +} #endif diff --git a/Queue.h b/Queue.h index 6f50e189..441afa25 100644 --- a/Queue.h +++ b/Queue.h @@ -12,158 +12,158 @@ namespace i2p { namespace util { - template - class Queue - { - public: + template + class Queue + { + public: - void Put (Element e) - { - std::unique_lock l(m_QueueMutex); - m_Queue.push (e); - m_NonEmpty.notify_one (); - } + void Put (Element e) + { + std::unique_lock l(m_QueueMutex); + m_Queue.push (e); + m_NonEmpty.notify_one (); + } - void Put (const std::vector& vec) - { - if (!vec.empty ()) - { - std::unique_lock l(m_QueueMutex); - for (auto it: vec) - m_Queue.push (it); - m_NonEmpty.notify_one (); - } - } - - Element GetNext () - { - std::unique_lock l(m_QueueMutex); - auto el = GetNonThreadSafe (); - if (!el) - { - m_NonEmpty.wait (l); - el = GetNonThreadSafe (); - } - return el; - } + void Put (const std::vector& vec) + { + if (!vec.empty ()) + { + std::unique_lock l(m_QueueMutex); + for (auto it: vec) + m_Queue.push (it); + m_NonEmpty.notify_one (); + } + } + + Element GetNext () + { + std::unique_lock l(m_QueueMutex); + auto el = GetNonThreadSafe (); + if (!el) + { + m_NonEmpty.wait (l); + el = GetNonThreadSafe (); + } + return el; + } - Element GetNextWithTimeout (int usec) - { - std::unique_lock l(m_QueueMutex); - auto el = GetNonThreadSafe (); - if (!el) - { - m_NonEmpty.wait_for (l, std::chrono::milliseconds (usec)); - el = GetNonThreadSafe (); - } - return el; - } + Element GetNextWithTimeout (int usec) + { + std::unique_lock l(m_QueueMutex); + auto el = GetNonThreadSafe (); + if (!el) + { + m_NonEmpty.wait_for (l, std::chrono::milliseconds (usec)); + el = GetNonThreadSafe (); + } + return el; + } - void Wait () - { - std::unique_lock l(m_QueueMutex); - m_NonEmpty.wait (l); - } + void Wait () + { + std::unique_lock l(m_QueueMutex); + m_NonEmpty.wait (l); + } - bool Wait (int sec, int usec) - { - std::unique_lock l(m_QueueMutex); - return m_NonEmpty.wait_for (l, std::chrono::seconds (sec) + std::chrono::milliseconds (usec)) != std::cv_status::timeout; - } + bool Wait (int sec, int usec) + { + std::unique_lock l(m_QueueMutex); + return m_NonEmpty.wait_for (l, std::chrono::seconds (sec) + std::chrono::milliseconds (usec)) != std::cv_status::timeout; + } - bool IsEmpty () - { - std::unique_lock l(m_QueueMutex); - return m_Queue.empty (); - } + bool IsEmpty () + { + std::unique_lock l(m_QueueMutex); + return m_Queue.empty (); + } - int GetSize () - { - std::unique_lock l(m_QueueMutex); - return m_Queue.size (); - } + int GetSize () + { + std::unique_lock l(m_QueueMutex); + return m_Queue.size (); + } - void WakeUp () { m_NonEmpty.notify_all (); }; + void WakeUp () { m_NonEmpty.notify_all (); }; - Element Get () - { - std::unique_lock l(m_QueueMutex); - return GetNonThreadSafe (); - } + Element Get () + { + std::unique_lock l(m_QueueMutex); + return GetNonThreadSafe (); + } - Element Peek () - { - std::unique_lock l(m_QueueMutex); - return GetNonThreadSafe (true); - } - - private: + Element Peek () + { + std::unique_lock l(m_QueueMutex); + return GetNonThreadSafe (true); + } + + private: - Element GetNonThreadSafe (bool peek = false) - { - if (!m_Queue.empty ()) - { - auto el = m_Queue.front (); - if (!peek) - m_Queue.pop (); - return el; - } - return nullptr; - } - - private: + Element GetNonThreadSafe (bool peek = false) + { + if (!m_Queue.empty ()) + { + auto el = m_Queue.front (); + if (!peek) + m_Queue.pop (); + return el; + } + return nullptr; + } + + private: - std::queue m_Queue; - std::mutex m_QueueMutex; - std::condition_variable m_NonEmpty; - }; + std::queue m_Queue; + std::mutex m_QueueMutex; + std::condition_variable m_NonEmpty; + }; - template - class MsgQueue: public Queue - { - public: + template + class MsgQueue: public Queue + { + public: - typedef std::function OnEmpty; + typedef std::function OnEmpty; - MsgQueue (): m_IsRunning (true), m_Thread (std::bind (&MsgQueue::Run, this)) {}; - ~MsgQueue () { Stop (); }; - void Stop() - { - if (m_IsRunning) - { - m_IsRunning = false; - Queue::WakeUp (); - m_Thread.join(); - } - } + MsgQueue (): m_IsRunning (true), m_Thread (std::bind (&MsgQueue::Run, this)) {}; + ~MsgQueue () { Stop (); }; + void Stop() + { + if (m_IsRunning) + { + m_IsRunning = false; + Queue::WakeUp (); + m_Thread.join(); + } + } - void SetOnEmpty (OnEmpty const & e) { m_OnEmpty = e; }; + void SetOnEmpty (OnEmpty const & e) { m_OnEmpty = e; }; - private: + private: - void Run () - { - while (m_IsRunning) - { - while (auto msg = Queue::Get ()) - { - msg->Process (); - delete msg; - } - if (m_OnEmpty != nullptr) - m_OnEmpty (); - if (m_IsRunning) - Queue::Wait (); - } - } - - private: - - volatile bool m_IsRunning; - OnEmpty m_OnEmpty; - std::thread m_Thread; - }; -} -} + void Run () + { + while (m_IsRunning) + { + while (auto msg = Queue::Get ()) + { + msg->Process (); + delete msg; + } + if (m_OnEmpty != nullptr) + m_OnEmpty (); + if (m_IsRunning) + Queue::Wait (); + } + } + + private: + + volatile bool m_IsRunning; + OnEmpty m_OnEmpty; + std::thread m_Thread; + }; +} +} #endif diff --git a/Reseed.cpp b/Reseed.cpp index 9f137432..89fbde7c 100644 --- a/Reseed.cpp +++ b/Reseed.cpp @@ -25,908 +25,908 @@ namespace i2p namespace data { - static std::vector httpReseedHostList = { - "http://netdb.i2p2.no/", // only SU3 (v2) support - "http://i2p-netdb.innovatio.no/", - "http://193.150.121.66/netDb/" - }; + static std::vector httpReseedHostList = { + "http://netdb.i2p2.no/", // only SU3 (v2) support + "http://i2p-netdb.innovatio.no/", + "http://193.150.121.66/netDb/" + }; - static std::vector httpsReseedHostList = { - // "https://193.150.121.66/netDb/", // unstable - // "https://i2p-netdb.innovatio.no/",// Vuln to POODLE - "https://netdb.i2p2.no/", // Only SU3 (v2) support - "https://reseed.i2p-projekt.de/", // Only HTTPS - //"https://cowpuncher.drollette.com/netdb/", // returns error - "https://netdb.rows.io:444/", - "https://uk.reseed.i2p2.no:444/" - // following hosts are fine but don't support AES256 - /*"https://i2p.mooo.com/netDb/", - "https://link.mx24.eu/", // Only HTTPS and SU3 (v2) support - "https://i2pseed.zarrenspry.info/", // Only HTTPS and SU3 (v2) support - "https://ieb9oopo.mooo.com/" // Only HTTPS and SU3 (v2) support*/ - }; - - Reseeder::Reseeder() - { - } + static std::vector httpsReseedHostList = { + // "https://193.150.121.66/netDb/", // unstable + // "https://i2p-netdb.innovatio.no/",// Vuln to POODLE + "https://netdb.i2p2.no/", // Only SU3 (v2) support + "https://reseed.i2p-projekt.de/", // Only HTTPS + //"https://cowpuncher.drollette.com/netdb/", // returns error + "https://netdb.rows.io:444/", + "https://uk.reseed.i2p2.no:444/" + // following hosts are fine but don't support AES256 + /*"https://i2p.mooo.com/netDb/", + "https://link.mx24.eu/", // Only HTTPS and SU3 (v2) support + "https://i2pseed.zarrenspry.info/", // Only HTTPS and SU3 (v2) support + "https://ieb9oopo.mooo.com/" // Only HTTPS and SU3 (v2) support*/ + }; + + Reseeder::Reseeder() + { + } - Reseeder::~Reseeder() - { - } + Reseeder::~Reseeder() + { + } - bool Reseeder::reseedNow() - { - // This method is deprecated - try - { - std::string reseedHost = httpReseedHostList[(rand() % httpReseedHostList.size())]; - LogPrint("Reseeding from ", reseedHost); - std::string content = i2p::util::http::httpRequest(reseedHost); - if (content == "") - { - LogPrint("Reseed failed"); - return false; - } - boost::regex e("<\\s*A\\s+[^>]*href\\s*=\\s*\"([^\"]*)\"", boost::regex::normal | boost::regbase::icase); - boost::sregex_token_iterator i(content.begin(), content.end(), e, 1); - boost::sregex_token_iterator j; - //TODO: Ugly code, try to clean up. - //TODO: Try to reduce N number of variables - std::string name; - std::string routerInfo; - std::string tmpUrl; - std::string filename; - std::string ignoreFileSuffix = ".su3"; - boost::filesystem::path root = i2p::util::filesystem::GetDataDir(); - while (i != j) - { - name = *i++; - if (name.find(ignoreFileSuffix)!=std::string::npos) - continue; - LogPrint("Downloading ", name); - tmpUrl = reseedHost; - tmpUrl.append(name); - routerInfo = i2p::util::http::httpRequest(tmpUrl); - if (routerInfo.size()==0) - continue; - filename = root.string(); + bool Reseeder::reseedNow() + { + // This method is deprecated + try + { + std::string reseedHost = httpReseedHostList[(rand() % httpReseedHostList.size())]; + LogPrint("Reseeding from ", reseedHost); + std::string content = i2p::util::http::httpRequest(reseedHost); + if (content == "") + { + LogPrint("Reseed failed"); + return false; + } + boost::regex e("<\\s*A\\s+[^>]*href\\s*=\\s*\"([^\"]*)\"", boost::regex::normal | boost::regbase::icase); + boost::sregex_token_iterator i(content.begin(), content.end(), e, 1); + boost::sregex_token_iterator j; + //TODO: Ugly code, try to clean up. + //TODO: Try to reduce N number of variables + std::string name; + std::string routerInfo; + std::string tmpUrl; + std::string filename; + std::string ignoreFileSuffix = ".su3"; + boost::filesystem::path root = i2p::util::filesystem::GetDataDir(); + while (i != j) + { + name = *i++; + if (name.find(ignoreFileSuffix)!=std::string::npos) + continue; + LogPrint("Downloading ", name); + tmpUrl = reseedHost; + tmpUrl.append(name); + routerInfo = i2p::util::http::httpRequest(tmpUrl); + if (routerInfo.size()==0) + continue; + filename = root.string(); #ifndef _WIN32 - filename += "/netDb/r"; + filename += "/netDb/r"; #else - filename += "\\netDb\\r"; + filename += "\\netDb\\r"; #endif - filename += name.at(11); // first char in id + filename += name.at(11); // first char in id #ifndef _WIN32 - filename.append("/"); + filename.append("/"); #else - filename.append("\\"); + filename.append("\\"); #endif - filename.append(name.c_str()); - std::ofstream outfile (filename, std::ios::binary); - outfile << routerInfo; - outfile.close(); - } - return true; - } - catch (std::exception& ex) - { - //TODO: error reporting - return false; - } - return false; - } + filename.append(name.c_str()); + std::ofstream outfile (filename, std::ios::binary); + outfile << routerInfo; + outfile.close(); + } + return true; + } + catch (std::exception& ex) + { + //TODO: error reporting + return false; + } + return false; + } - int Reseeder::ReseedNowSU3 () - { - CryptoPP::AutoSeededRandomPool rnd; - auto ind = rnd.GenerateWord32 (0, httpReseedHostList.size() - 1 + httpsReseedHostList.size () - 1); - std::string reseedHost = (ind < httpReseedHostList.size()) ? httpReseedHostList[ind] : - httpsReseedHostList[ind - httpReseedHostList.size()]; - return ReseedFromSU3 (reseedHost, ind >= httpReseedHostList.size()); - } + int Reseeder::ReseedNowSU3 () + { + CryptoPP::AutoSeededRandomPool rnd; + auto ind = rnd.GenerateWord32 (0, httpReseedHostList.size() - 1 + httpsReseedHostList.size () - 1); + std::string reseedHost = (ind < httpReseedHostList.size()) ? httpReseedHostList[ind] : + httpsReseedHostList[ind - httpReseedHostList.size()]; + return ReseedFromSU3 (reseedHost, ind >= httpReseedHostList.size()); + } - int Reseeder::ReseedFromSU3 (const std::string& host, bool https) - { - std::string url = host + "i2pseeds.su3"; - LogPrint (eLogInfo, "Dowloading SU3 from ", host); - std::string su3 = https ? HttpsRequest (url) : i2p::util::http::httpRequest (url); - if (su3.length () > 0) - { - std::stringstream s(su3); - return ProcessSU3Stream (s); - } - else - { - LogPrint (eLogWarning, "SU3 download failed"); - return 0; - } - } - - int Reseeder::ProcessSU3File (const char * filename) - { - std::ifstream s(filename, std::ifstream::binary); - if (s.is_open ()) - return ProcessSU3Stream (s); - else - { - LogPrint (eLogError, "Can't open file ", filename); - return 0; - } - } + int Reseeder::ReseedFromSU3 (const std::string& host, bool https) + { + std::string url = host + "i2pseeds.su3"; + LogPrint (eLogInfo, "Dowloading SU3 from ", host); + std::string su3 = https ? HttpsRequest (url) : i2p::util::http::httpRequest (url); + if (su3.length () > 0) + { + std::stringstream s(su3); + return ProcessSU3Stream (s); + } + else + { + LogPrint (eLogWarning, "SU3 download failed"); + return 0; + } + } + + int Reseeder::ProcessSU3File (const char * filename) + { + std::ifstream s(filename, std::ifstream::binary); + if (s.is_open ()) + return ProcessSU3Stream (s); + else + { + LogPrint (eLogError, "Can't open file ", filename); + return 0; + } + } - const char SU3_MAGIC_NUMBER[]="I2Psu3"; - const uint32_t ZIP_HEADER_SIGNATURE = 0x04034B50; - const uint32_t ZIP_CENTRAL_DIRECTORY_HEADER_SIGNATURE = 0x02014B50; - const uint16_t ZIP_BIT_FLAG_DATA_DESCRIPTOR = 0x0008; - int Reseeder::ProcessSU3Stream (std::istream& s) - { - char magicNumber[7]; - s.read (magicNumber, 7); // magic number and zero byte 6 - if (strcmp (magicNumber, SU3_MAGIC_NUMBER)) - { - LogPrint (eLogError, "Unexpected SU3 magic number"); - return 0; - } - s.seekg (1, std::ios::cur); // su3 file format version - SigningKeyType signatureType; - s.read ((char *)&signatureType, 2); // signature type - signatureType = be16toh (signatureType); - uint16_t signatureLength; - s.read ((char *)&signatureLength, 2); // signature length - signatureLength = be16toh (signatureLength); - s.seekg (1, std::ios::cur); // unused - uint8_t versionLength; - s.read ((char *)&versionLength, 1); // version length - s.seekg (1, std::ios::cur); // unused - uint8_t signerIDLength; - s.read ((char *)&signerIDLength, 1); // signer ID length - uint64_t contentLength; - s.read ((char *)&contentLength, 8); // content length - contentLength = be64toh (contentLength); - s.seekg (1, std::ios::cur); // unused - uint8_t fileType; - s.read ((char *)&fileType, 1); // file type - if (fileType != 0x00) // zip file - { - LogPrint (eLogError, "Can't handle file type ", (int)fileType); - return 0; - } - s.seekg (1, std::ios::cur); // unused - uint8_t contentType; - s.read ((char *)&contentType, 1); // content type - if (contentType != 0x03) // reseed data - { - LogPrint (eLogError, "Unexpected content type ", (int)contentType); - return 0; - } - s.seekg (12, std::ios::cur); // unused + const char SU3_MAGIC_NUMBER[]="I2Psu3"; + const uint32_t ZIP_HEADER_SIGNATURE = 0x04034B50; + const uint32_t ZIP_CENTRAL_DIRECTORY_HEADER_SIGNATURE = 0x02014B50; + const uint16_t ZIP_BIT_FLAG_DATA_DESCRIPTOR = 0x0008; + int Reseeder::ProcessSU3Stream (std::istream& s) + { + char magicNumber[7]; + s.read (magicNumber, 7); // magic number and zero byte 6 + if (strcmp (magicNumber, SU3_MAGIC_NUMBER)) + { + LogPrint (eLogError, "Unexpected SU3 magic number"); + return 0; + } + s.seekg (1, std::ios::cur); // su3 file format version + SigningKeyType signatureType; + s.read ((char *)&signatureType, 2); // signature type + signatureType = be16toh (signatureType); + uint16_t signatureLength; + s.read ((char *)&signatureLength, 2); // signature length + signatureLength = be16toh (signatureLength); + s.seekg (1, std::ios::cur); // unused + uint8_t versionLength; + s.read ((char *)&versionLength, 1); // version length + s.seekg (1, std::ios::cur); // unused + uint8_t signerIDLength; + s.read ((char *)&signerIDLength, 1); // signer ID length + uint64_t contentLength; + s.read ((char *)&contentLength, 8); // content length + contentLength = be64toh (contentLength); + s.seekg (1, std::ios::cur); // unused + uint8_t fileType; + s.read ((char *)&fileType, 1); // file type + if (fileType != 0x00) // zip file + { + LogPrint (eLogError, "Can't handle file type ", (int)fileType); + return 0; + } + s.seekg (1, std::ios::cur); // unused + uint8_t contentType; + s.read ((char *)&contentType, 1); // content type + if (contentType != 0x03) // reseed data + { + LogPrint (eLogError, "Unexpected content type ", (int)contentType); + return 0; + } + s.seekg (12, std::ios::cur); // unused - s.seekg (versionLength, std::ios::cur); // skip version - char signerID[256]; - s.read (signerID, signerIDLength); // signerID - signerID[signerIDLength] = 0; - - //try to verify signature - auto it = m_SigningKeys.find (signerID); - if (it != m_SigningKeys.end ()) - { - // TODO: implement all signature types - if (signatureType == SIGNING_KEY_TYPE_RSA_SHA512_4096) - { - size_t pos = s.tellg (); - size_t tbsLen = pos + contentLength; - uint8_t * tbs = new uint8_t[tbsLen]; - s.seekg (0, std::ios::beg); - s.read ((char *)tbs, tbsLen); - uint8_t * signature = new uint8_t[signatureLength]; - s.read ((char *)signature, signatureLength); - // RSA-raw - i2p::crypto::RSASHA5124096RawVerifier verifier(it->second); - verifier.Update (tbs, tbsLen); - if (!verifier.Verify (signature)) - LogPrint (eLogWarning, "SU3 signature verification failed"); - delete[] signature; - delete[] tbs; - s.seekg (pos, std::ios::beg); - } - else - LogPrint (eLogWarning, "Signature type ", signatureType, " is not supported"); - } - else - LogPrint (eLogWarning, "Certificate for ", signerID, " not loaded"); - - // handle content - int numFiles = 0; - size_t contentPos = s.tellg (); - while (!s.eof ()) - { - uint32_t signature; - s.read ((char *)&signature, 4); - signature = le32toh (signature); - if (signature == ZIP_HEADER_SIGNATURE) - { - // next local file - s.seekg (2, std::ios::cur); // version - uint16_t bitFlag; - s.read ((char *)&bitFlag, 2); - bitFlag = le16toh (bitFlag); - uint16_t compressionMethod; - s.read ((char *)&compressionMethod, 2); - compressionMethod = le16toh (compressionMethod); - s.seekg (4, std::ios::cur); // skip fields we don't care about - uint32_t compressedSize, uncompressedSize; - uint8_t crc32[4]; - s.read ((char *)crc32, 4); - s.read ((char *)&compressedSize, 4); - compressedSize = le32toh (compressedSize); - s.read ((char *)&uncompressedSize, 4); - uncompressedSize = le32toh (uncompressedSize); - uint16_t fileNameLength, extraFieldLength; - s.read ((char *)&fileNameLength, 2); - fileNameLength = le16toh (fileNameLength); - s.read ((char *)&extraFieldLength, 2); - extraFieldLength = le16toh (extraFieldLength); - char localFileName[255]; - s.read (localFileName, fileNameLength); - localFileName[fileNameLength] = 0; - s.seekg (extraFieldLength, std::ios::cur); - // take care about data desriptor if presented - if (bitFlag & ZIP_BIT_FLAG_DATA_DESCRIPTOR) - { - size_t pos = s.tellg (); - if (!FindZipDataDescriptor (s)) - { - LogPrint (eLogError, "SU3 archive data descriptor not found"); - return numFiles; - } - - s.read ((char *)crc32, 4); - s.read ((char *)&compressedSize, 4); - compressedSize = le32toh (compressedSize) + 4; // ??? we must consider signature as part of compressed data - s.read ((char *)&uncompressedSize, 4); - uncompressedSize = le32toh (uncompressedSize); + s.seekg (versionLength, std::ios::cur); // skip version + char signerID[256]; + s.read (signerID, signerIDLength); // signerID + signerID[signerIDLength] = 0; + + //try to verify signature + auto it = m_SigningKeys.find (signerID); + if (it != m_SigningKeys.end ()) + { + // TODO: implement all signature types + if (signatureType == SIGNING_KEY_TYPE_RSA_SHA512_4096) + { + size_t pos = s.tellg (); + size_t tbsLen = pos + contentLength; + uint8_t * tbs = new uint8_t[tbsLen]; + s.seekg (0, std::ios::beg); + s.read ((char *)tbs, tbsLen); + uint8_t * signature = new uint8_t[signatureLength]; + s.read ((char *)signature, signatureLength); + // RSA-raw + i2p::crypto::RSASHA5124096RawVerifier verifier(it->second); + verifier.Update (tbs, tbsLen); + if (!verifier.Verify (signature)) + LogPrint (eLogWarning, "SU3 signature verification failed"); + delete[] signature; + delete[] tbs; + s.seekg (pos, std::ios::beg); + } + else + LogPrint (eLogWarning, "Signature type ", signatureType, " is not supported"); + } + else + LogPrint (eLogWarning, "Certificate for ", signerID, " not loaded"); + + // handle content + int numFiles = 0; + size_t contentPos = s.tellg (); + while (!s.eof ()) + { + uint32_t signature; + s.read ((char *)&signature, 4); + signature = le32toh (signature); + if (signature == ZIP_HEADER_SIGNATURE) + { + // next local file + s.seekg (2, std::ios::cur); // version + uint16_t bitFlag; + s.read ((char *)&bitFlag, 2); + bitFlag = le16toh (bitFlag); + uint16_t compressionMethod; + s.read ((char *)&compressionMethod, 2); + compressionMethod = le16toh (compressionMethod); + s.seekg (4, std::ios::cur); // skip fields we don't care about + uint32_t compressedSize, uncompressedSize; + uint8_t crc32[4]; + s.read ((char *)crc32, 4); + s.read ((char *)&compressedSize, 4); + compressedSize = le32toh (compressedSize); + s.read ((char *)&uncompressedSize, 4); + uncompressedSize = le32toh (uncompressedSize); + uint16_t fileNameLength, extraFieldLength; + s.read ((char *)&fileNameLength, 2); + fileNameLength = le16toh (fileNameLength); + s.read ((char *)&extraFieldLength, 2); + extraFieldLength = le16toh (extraFieldLength); + char localFileName[255]; + s.read (localFileName, fileNameLength); + localFileName[fileNameLength] = 0; + s.seekg (extraFieldLength, std::ios::cur); + // take care about data desriptor if presented + if (bitFlag & ZIP_BIT_FLAG_DATA_DESCRIPTOR) + { + size_t pos = s.tellg (); + if (!FindZipDataDescriptor (s)) + { + LogPrint (eLogError, "SU3 archive data descriptor not found"); + return numFiles; + } + + s.read ((char *)crc32, 4); + s.read ((char *)&compressedSize, 4); + compressedSize = le32toh (compressedSize) + 4; // ??? we must consider signature as part of compressed data + s.read ((char *)&uncompressedSize, 4); + uncompressedSize = le32toh (uncompressedSize); - // now we know compressed and uncompressed size - s.seekg (pos, std::ios::beg); // back to compressed data - } + // now we know compressed and uncompressed size + s.seekg (pos, std::ios::beg); // back to compressed data + } - LogPrint (eLogDebug, "Proccessing file ", localFileName, " ", compressedSize, " bytes"); - if (!compressedSize) - { - LogPrint (eLogWarning, "Unexpected size 0. Skipped"); - continue; - } - - uint8_t * compressed = new uint8_t[compressedSize]; - s.read ((char *)compressed, compressedSize); - if (compressionMethod) // we assume Deflate - { - CryptoPP::Inflator decompressor; - decompressor.Put (compressed, compressedSize); - decompressor.MessageEnd(); - if (decompressor.MaxRetrievable () <= uncompressedSize) - { - uint8_t * uncompressed = new uint8_t[uncompressedSize]; - decompressor.Get (uncompressed, uncompressedSize); - if (CryptoPP::CRC32().VerifyDigest (crc32, uncompressed, uncompressedSize)) - { - i2p::data::netdb.AddRouterInfo (uncompressed, uncompressedSize); - numFiles++; - } - else - LogPrint (eLogError, "CRC32 verification failed"); - delete[] uncompressed; - } - else - LogPrint (eLogError, "Actual uncompressed size ", decompressor.MaxRetrievable (), " exceed ", uncompressedSize, " from header"); - } - else // no compression - { - i2p::data::netdb.AddRouterInfo (compressed, compressedSize); - numFiles++; - } - delete[] compressed; - if (bitFlag & ZIP_BIT_FLAG_DATA_DESCRIPTOR) - s.seekg (12, std::ios::cur); // skip data descriptor section if presented (12 = 16 - 4) - } - else - { - if (signature != ZIP_CENTRAL_DIRECTORY_HEADER_SIGNATURE) - LogPrint (eLogWarning, "Missing zip central directory header"); - break; // no more files - } - size_t end = s.tellg (); - if (end - contentPos >= contentLength) - break; // we are beyond contentLength - } - return numFiles; - } + LogPrint (eLogDebug, "Proccessing file ", localFileName, " ", compressedSize, " bytes"); + if (!compressedSize) + { + LogPrint (eLogWarning, "Unexpected size 0. Skipped"); + continue; + } + + uint8_t * compressed = new uint8_t[compressedSize]; + s.read ((char *)compressed, compressedSize); + if (compressionMethod) // we assume Deflate + { + CryptoPP::Inflator decompressor; + decompressor.Put (compressed, compressedSize); + decompressor.MessageEnd(); + if (decompressor.MaxRetrievable () <= uncompressedSize) + { + uint8_t * uncompressed = new uint8_t[uncompressedSize]; + decompressor.Get (uncompressed, uncompressedSize); + if (CryptoPP::CRC32().VerifyDigest (crc32, uncompressed, uncompressedSize)) + { + i2p::data::netdb.AddRouterInfo (uncompressed, uncompressedSize); + numFiles++; + } + else + LogPrint (eLogError, "CRC32 verification failed"); + delete[] uncompressed; + } + else + LogPrint (eLogError, "Actual uncompressed size ", decompressor.MaxRetrievable (), " exceed ", uncompressedSize, " from header"); + } + else // no compression + { + i2p::data::netdb.AddRouterInfo (compressed, compressedSize); + numFiles++; + } + delete[] compressed; + if (bitFlag & ZIP_BIT_FLAG_DATA_DESCRIPTOR) + s.seekg (12, std::ios::cur); // skip data descriptor section if presented (12 = 16 - 4) + } + else + { + if (signature != ZIP_CENTRAL_DIRECTORY_HEADER_SIGNATURE) + LogPrint (eLogWarning, "Missing zip central directory header"); + break; // no more files + } + size_t end = s.tellg (); + if (end - contentPos >= contentLength) + break; // we are beyond contentLength + } + return numFiles; + } - const uint8_t ZIP_DATA_DESCRIPTOR_SIGNATURE[] = { 0x50, 0x4B, 0x07, 0x08 }; - bool Reseeder::FindZipDataDescriptor (std::istream& s) - { - size_t nextInd = 0; - while (!s.eof ()) - { - uint8_t nextByte; - s.read ((char *)&nextByte, 1); - if (nextByte == ZIP_DATA_DESCRIPTOR_SIGNATURE[nextInd]) - { - nextInd++; - if (nextInd >= sizeof (ZIP_DATA_DESCRIPTOR_SIGNATURE)) - return true; - } - else - nextInd = 0; - } - return false; - } + const uint8_t ZIP_DATA_DESCRIPTOR_SIGNATURE[] = { 0x50, 0x4B, 0x07, 0x08 }; + bool Reseeder::FindZipDataDescriptor (std::istream& s) + { + size_t nextInd = 0; + while (!s.eof ()) + { + uint8_t nextByte; + s.read ((char *)&nextByte, 1); + if (nextByte == ZIP_DATA_DESCRIPTOR_SIGNATURE[nextInd]) + { + nextInd++; + if (nextInd >= sizeof (ZIP_DATA_DESCRIPTOR_SIGNATURE)) + return true; + } + else + nextInd = 0; + } + return false; + } - const char CERTIFICATE_HEADER[] = "-----BEGIN CERTIFICATE-----"; - const char CERTIFICATE_FOOTER[] = "-----END CERTIFICATE-----"; - void Reseeder::LoadCertificate (const std::string& filename) - { - std::ifstream s(filename, std::ifstream::binary); - if (s.is_open ()) - { - s.seekg (0, std::ios::end); - size_t len = s.tellg (); - s.seekg (0, std::ios::beg); - char buf[2048]; - s.read (buf, len); - std::string cert (buf, len); - // assume file in pem format - auto pos1 = cert.find (CERTIFICATE_HEADER); - auto pos2 = cert.find (CERTIFICATE_FOOTER); - if (pos1 == std::string::npos || pos2 == std::string::npos) - { - LogPrint (eLogError, "Malformed certificate file"); - return; - } - pos1 += strlen (CERTIFICATE_HEADER); - pos2 -= pos1; - std::string base64 = cert.substr (pos1, pos2); + const char CERTIFICATE_HEADER[] = "-----BEGIN CERTIFICATE-----"; + const char CERTIFICATE_FOOTER[] = "-----END CERTIFICATE-----"; + void Reseeder::LoadCertificate (const std::string& filename) + { + std::ifstream s(filename, std::ifstream::binary); + if (s.is_open ()) + { + s.seekg (0, std::ios::end); + size_t len = s.tellg (); + s.seekg (0, std::ios::beg); + char buf[2048]; + s.read (buf, len); + std::string cert (buf, len); + // assume file in pem format + auto pos1 = cert.find (CERTIFICATE_HEADER); + auto pos2 = cert.find (CERTIFICATE_FOOTER); + if (pos1 == std::string::npos || pos2 == std::string::npos) + { + LogPrint (eLogError, "Malformed certificate file"); + return; + } + pos1 += strlen (CERTIFICATE_HEADER); + pos2 -= pos1; + std::string base64 = cert.substr (pos1, pos2); - CryptoPP::ByteQueue queue; - CryptoPP::Base64Decoder decoder; // regular base64 rather than I2P - decoder.Attach (new CryptoPP::Redirector (queue)); - decoder.Put ((const uint8_t *)base64.data(), base64.length()); - decoder.MessageEnd (); - - LoadCertificate (queue); - } - else - LogPrint (eLogError, "Can't open certificate file ", filename); - } + CryptoPP::ByteQueue queue; + CryptoPP::Base64Decoder decoder; // regular base64 rather than I2P + decoder.Attach (new CryptoPP::Redirector (queue)); + decoder.Put ((const uint8_t *)base64.data(), base64.length()); + decoder.MessageEnd (); + + LoadCertificate (queue); + } + else + LogPrint (eLogError, "Can't open certificate file ", filename); + } - std::string Reseeder::LoadCertificate (CryptoPP::ByteQueue& queue) - { - // extract X.509 - CryptoPP::BERSequenceDecoder x509Cert (queue); - CryptoPP::BERSequenceDecoder tbsCert (x509Cert); - // version - uint32_t ver; - CryptoPP::BERGeneralDecoder context (tbsCert, CryptoPP::CONTEXT_SPECIFIC | CryptoPP::CONSTRUCTED); - CryptoPP::BERDecodeUnsigned(context, ver, CryptoPP::INTEGER); - // serial - CryptoPP::Integer serial; - serial.BERDecode(tbsCert); - // signature - CryptoPP::BERSequenceDecoder signature (tbsCert); - signature.SkipAll(); - - // issuer - std::string name; - CryptoPP::BERSequenceDecoder issuer (tbsCert); - { - CryptoPP::BERSetDecoder c (issuer); c.SkipAll(); - CryptoPP::BERSetDecoder st (issuer); st.SkipAll(); - CryptoPP::BERSetDecoder l (issuer); l.SkipAll(); - CryptoPP::BERSetDecoder o (issuer); o.SkipAll(); - CryptoPP::BERSetDecoder ou (issuer); ou.SkipAll(); - CryptoPP::BERSetDecoder cn (issuer); - { - CryptoPP::BERSequenceDecoder attributes (cn); - { - CryptoPP::BERGeneralDecoder ident(attributes, CryptoPP::OBJECT_IDENTIFIER); - ident.SkipAll (); - CryptoPP::BERDecodeTextString (attributes, name, CryptoPP::UTF8_STRING); - } - } - } - issuer.SkipAll(); - // validity - CryptoPP::BERSequenceDecoder validity (tbsCert); - validity.SkipAll(); - // subject - CryptoPP::BERSequenceDecoder subject (tbsCert); - subject.SkipAll(); - // public key - CryptoPP::BERSequenceDecoder publicKey (tbsCert); - { - CryptoPP::BERSequenceDecoder ident (publicKey); - ident.SkipAll (); - CryptoPP::BERGeneralDecoder key (publicKey, CryptoPP::BIT_STRING); - key.Skip (1); // FIXME: probably bug in crypto++ - CryptoPP::BERSequenceDecoder keyPair (key); - CryptoPP::Integer n; - n.BERDecode (keyPair); - if (name.length () > 0) - { - PublicKey value; - n.Encode (value, 512); - m_SigningKeys[name] = value; - } - else - LogPrint (eLogWarning, "Unknown issuer. Skipped"); - } - publicKey.SkipAll(); - - tbsCert.SkipAll(); - x509Cert.SkipAll(); - return name; - } - - void Reseeder::LoadCertificates () - { - boost::filesystem::path reseedDir = i2p::util::filesystem::GetCertificatesDir() / "reseed"; - - if (!boost::filesystem::exists (reseedDir)) - { - LogPrint (eLogWarning, "Reseed certificates not loaded. ", reseedDir, " doesn't exist"); - return; - } + std::string Reseeder::LoadCertificate (CryptoPP::ByteQueue& queue) + { + // extract X.509 + CryptoPP::BERSequenceDecoder x509Cert (queue); + CryptoPP::BERSequenceDecoder tbsCert (x509Cert); + // version + uint32_t ver; + CryptoPP::BERGeneralDecoder context (tbsCert, CryptoPP::CONTEXT_SPECIFIC | CryptoPP::CONSTRUCTED); + CryptoPP::BERDecodeUnsigned(context, ver, CryptoPP::INTEGER); + // serial + CryptoPP::Integer serial; + serial.BERDecode(tbsCert); + // signature + CryptoPP::BERSequenceDecoder signature (tbsCert); + signature.SkipAll(); + + // issuer + std::string name; + CryptoPP::BERSequenceDecoder issuer (tbsCert); + { + CryptoPP::BERSetDecoder c (issuer); c.SkipAll(); + CryptoPP::BERSetDecoder st (issuer); st.SkipAll(); + CryptoPP::BERSetDecoder l (issuer); l.SkipAll(); + CryptoPP::BERSetDecoder o (issuer); o.SkipAll(); + CryptoPP::BERSetDecoder ou (issuer); ou.SkipAll(); + CryptoPP::BERSetDecoder cn (issuer); + { + CryptoPP::BERSequenceDecoder attributes (cn); + { + CryptoPP::BERGeneralDecoder ident(attributes, CryptoPP::OBJECT_IDENTIFIER); + ident.SkipAll (); + CryptoPP::BERDecodeTextString (attributes, name, CryptoPP::UTF8_STRING); + } + } + } + issuer.SkipAll(); + // validity + CryptoPP::BERSequenceDecoder validity (tbsCert); + validity.SkipAll(); + // subject + CryptoPP::BERSequenceDecoder subject (tbsCert); + subject.SkipAll(); + // public key + CryptoPP::BERSequenceDecoder publicKey (tbsCert); + { + CryptoPP::BERSequenceDecoder ident (publicKey); + ident.SkipAll (); + CryptoPP::BERGeneralDecoder key (publicKey, CryptoPP::BIT_STRING); + key.Skip (1); // FIXME: probably bug in crypto++ + CryptoPP::BERSequenceDecoder keyPair (key); + CryptoPP::Integer n; + n.BERDecode (keyPair); + if (name.length () > 0) + { + PublicKey value; + n.Encode (value, 512); + m_SigningKeys[name] = value; + } + else + LogPrint (eLogWarning, "Unknown issuer. Skipped"); + } + publicKey.SkipAll(); + + tbsCert.SkipAll(); + x509Cert.SkipAll(); + return name; + } + + void Reseeder::LoadCertificates () + { + boost::filesystem::path reseedDir = i2p::util::filesystem::GetCertificatesDir() / "reseed"; + + if (!boost::filesystem::exists (reseedDir)) + { + LogPrint (eLogWarning, "Reseed certificates not loaded. ", reseedDir, " doesn't exist"); + return; + } - int numCertificates = 0; - boost::filesystem::directory_iterator end; // empty - for (boost::filesystem::directory_iterator it (reseedDir); it != end; ++it) - { - if (boost::filesystem::is_regular_file (it->status()) && it->path ().extension () == ".crt") - { - LoadCertificate (it->path ().string ()); - numCertificates++; - } - } - LogPrint (eLogInfo, numCertificates, " certificates loaded"); - } + int numCertificates = 0; + boost::filesystem::directory_iterator end; // empty + for (boost::filesystem::directory_iterator it (reseedDir); it != end; ++it) + { + if (boost::filesystem::is_regular_file (it->status()) && it->path ().extension () == ".crt") + { + LoadCertificate (it->path ().string ()); + numCertificates++; + } + } + LogPrint (eLogInfo, numCertificates, " certificates loaded"); + } - std::string Reseeder::HttpsRequest (const std::string& address) - { - i2p::util::http::url u(address); - if (u.port_ == 80) u.port_ = 443; - TlsSession session (u.host_, u.port_); - - if (session.IsEstablished ()) - { - // send request - std::stringstream ss; - ss << "GET " << u.path_ << " HTTP/1.1\r\nHost: " << u.host_ - << "\r\nAccept: */*\r\n" << "User-Agent: Wget/1.11.4\r\n" << "Connection: close\r\n\r\n"; - session.Send ((uint8_t *)ss.str ().c_str (), ss.str ().length ()); + std::string Reseeder::HttpsRequest (const std::string& address) + { + i2p::util::http::url u(address); + if (u.port_ == 80) u.port_ = 443; + TlsSession session (u.host_, u.port_); + + if (session.IsEstablished ()) + { + // send request + std::stringstream ss; + ss << "GET " << u.path_ << " HTTP/1.1\r\nHost: " << u.host_ + << "\r\nAccept: */*\r\n" << "User-Agent: Wget/1.11.4\r\n" << "Connection: close\r\n\r\n"; + session.Send ((uint8_t *)ss.str ().c_str (), ss.str ().length ()); - // read response - std::stringstream rs; - while (session.Receive (rs)) - ; - return i2p::util::http::GetHttpContent (rs); - } - else - return ""; - } + // read response + std::stringstream rs; + while (session.Receive (rs)) + ; + return i2p::util::http::GetHttpContent (rs); + } + else + return ""; + } //------------------------------------------------------------- - template - class TlsCipherMAC: public TlsCipher - { - public: + template + class TlsCipherMAC: public TlsCipher + { + public: - TlsCipherMAC (const uint8_t * keys): m_Seqn (0) - { - memcpy (m_MacKey, keys, Hash::DIGESTSIZE); - } - - void CalculateMAC (uint8_t type, const uint8_t * buf, size_t len, uint8_t * mac) - { - uint8_t header[13]; // seqn (8) + type (1) + version (2) + length (2) - htobe64buf (header, m_Seqn); - header[8] = type; header[9] = 3; header[10] = 3; // 3,3 means TLS 1.2 - htobe16buf (header + 11, len); - CryptoPP::HMAC hmac (m_MacKey, Hash::DIGESTSIZE); - hmac.Update (header, 13); - hmac.Update (buf, len); - hmac.Final (mac); - m_Seqn++; - } - - private: + TlsCipherMAC (const uint8_t * keys): m_Seqn (0) + { + memcpy (m_MacKey, keys, Hash::DIGESTSIZE); + } + + void CalculateMAC (uint8_t type, const uint8_t * buf, size_t len, uint8_t * mac) + { + uint8_t header[13]; // seqn (8) + type (1) + version (2) + length (2) + htobe64buf (header, m_Seqn); + header[8] = type; header[9] = 3; header[10] = 3; // 3,3 means TLS 1.2 + htobe16buf (header + 11, len); + CryptoPP::HMAC hmac (m_MacKey, Hash::DIGESTSIZE); + hmac.Update (header, 13); + hmac.Update (buf, len); + hmac.Final (mac); + m_Seqn++; + } + + private: - uint64_t m_Seqn; - uint8_t m_MacKey[Hash::DIGESTSIZE]; // client - }; + uint64_t m_Seqn; + uint8_t m_MacKey[Hash::DIGESTSIZE]; // client + }; - template - class TlsCipher_AES_256_CBC: public TlsCipherMAC - { - public: + template + class TlsCipher_AES_256_CBC: public TlsCipherMAC + { + public: - TlsCipher_AES_256_CBC (const uint8_t * keys): TlsCipherMAC (keys) - { - m_Encryption.SetKey (keys + 2*Hash::DIGESTSIZE); - m_Decryption.SetKey (keys + 2*Hash::DIGESTSIZE + 32); - } + TlsCipher_AES_256_CBC (const uint8_t * keys): TlsCipherMAC (keys) + { + m_Encryption.SetKey (keys + 2*Hash::DIGESTSIZE); + m_Decryption.SetKey (keys + 2*Hash::DIGESTSIZE + 32); + } - size_t Encrypt (const uint8_t * in, size_t len, const uint8_t * mac, uint8_t * out) - { - size_t size = 0; - m_Rnd.GenerateBlock (out, 16); // iv - size += 16; - m_Encryption.SetIV (out); - memcpy (out + size, in, len); - size += len; - memcpy (out + size, mac, Hash::DIGESTSIZE); - size += Hash::DIGESTSIZE; - uint8_t paddingSize = size + 1; - paddingSize &= 0x0F; // %16 - if (paddingSize > 0) paddingSize = 16 - paddingSize; - memset (out + size, paddingSize, paddingSize + 1); // paddind and last byte are equal to padding size - size += paddingSize + 1; - m_Encryption.Encrypt (out + 16, size - 16, out + 16); - return size; - } + size_t Encrypt (const uint8_t * in, size_t len, const uint8_t * mac, uint8_t * out) + { + size_t size = 0; + m_Rnd.GenerateBlock (out, 16); // iv + size += 16; + m_Encryption.SetIV (out); + memcpy (out + size, in, len); + size += len; + memcpy (out + size, mac, Hash::DIGESTSIZE); + size += Hash::DIGESTSIZE; + uint8_t paddingSize = size + 1; + paddingSize &= 0x0F; // %16 + if (paddingSize > 0) paddingSize = 16 - paddingSize; + memset (out + size, paddingSize, paddingSize + 1); // paddind and last byte are equal to padding size + size += paddingSize + 1; + m_Encryption.Encrypt (out + 16, size - 16, out + 16); + return size; + } - size_t Decrypt (uint8_t * buf, size_t len) // payload is buf + 16 - { - m_Decryption.SetIV (buf); - m_Decryption.Decrypt (buf + 16, len - 16, buf + 16); - return len - 16 - Hash::DIGESTSIZE - buf[len -1] - 1; // IV(16), mac(32 or 20) and padding - } + size_t Decrypt (uint8_t * buf, size_t len) // payload is buf + 16 + { + m_Decryption.SetIV (buf); + m_Decryption.Decrypt (buf + 16, len - 16, buf + 16); + return len - 16 - Hash::DIGESTSIZE - buf[len -1] - 1; // IV(16), mac(32 or 20) and padding + } - size_t GetIVSize () const { return 16; }; - - private: + size_t GetIVSize () const { return 16; }; + + private: - CryptoPP::AutoSeededRandomPool m_Rnd; - i2p::crypto::CBCEncryption m_Encryption; - i2p::crypto::CBCDecryption m_Decryption; - }; + CryptoPP::AutoSeededRandomPool m_Rnd; + i2p::crypto::CBCEncryption m_Encryption; + i2p::crypto::CBCDecryption m_Decryption; + }; - class TlsCipher_RC4_SHA: public TlsCipherMAC - { - public: + class TlsCipher_RC4_SHA: public TlsCipherMAC + { + public: - TlsCipher_RC4_SHA (const uint8_t * keys): TlsCipherMAC (keys) - { - m_Encryption.SetKey (keys + 40, 16); // 20 + 20 - m_Decryption.SetKey (keys + 56, 16); // 20 + 20 + 16 - } + TlsCipher_RC4_SHA (const uint8_t * keys): TlsCipherMAC (keys) + { + m_Encryption.SetKey (keys + 40, 16); // 20 + 20 + m_Decryption.SetKey (keys + 56, 16); // 20 + 20 + 16 + } - size_t Encrypt (const uint8_t * in, size_t len, const uint8_t * mac, uint8_t * out) - { - memcpy (out, in, len); - memcpy (out + len, mac, 20); - m_Encryption.ProcessData (out, out, len + 20); - return len + 20; - } - - size_t Decrypt (uint8_t * buf, size_t len) - { - m_Decryption.ProcessData (buf, buf, len); - return len - 20; - } - - private: + size_t Encrypt (const uint8_t * in, size_t len, const uint8_t * mac, uint8_t * out) + { + memcpy (out, in, len); + memcpy (out + len, mac, 20); + m_Encryption.ProcessData (out, out, len + 20); + return len + 20; + } + + size_t Decrypt (uint8_t * buf, size_t len) + { + m_Decryption.ProcessData (buf, buf, len); + return len - 20; + } + + private: - CryptoPP::Weak1::ARC4 m_Encryption, m_Decryption; - }; + CryptoPP::Weak1::ARC4 m_Encryption, m_Decryption; + }; - - TlsSession::TlsSession (const std::string& host, int port): - m_IsEstablished (false), m_Cipher (nullptr) - { - m_Site.connect(host, boost::lexical_cast(port)); - if (m_Site.good ()) - Handshake (); - else - LogPrint (eLogError, "Can't connect to ", host, ":", port); - } - - TlsSession::~TlsSession () - { - delete m_Cipher; - } + + TlsSession::TlsSession (const std::string& host, int port): + m_IsEstablished (false), m_Cipher (nullptr) + { + m_Site.connect(host, boost::lexical_cast(port)); + if (m_Site.good ()) + Handshake (); + else + LogPrint (eLogError, "Can't connect to ", host, ":", port); + } + + TlsSession::~TlsSession () + { + delete m_Cipher; + } - void TlsSession::Handshake () - { - static uint8_t clientHello[] = - { - 0x16, // handshake - 0x03, 0x03, // version (TLS 1.2) - 0x00, 0x33, // length of handshake - // handshake - 0x01, // handshake type (client hello) - 0x00, 0x00, 0x2F, // length of handshake payload - // client hello - 0x03, 0x03, // highest version supported (TLS 1.2) - 0x45, 0xFA, 0x01, 0x19, 0x74, 0x55, 0x18, 0x36, - 0x42, 0x05, 0xC1, 0xDD, 0x4A, 0x21, 0x80, 0x80, - 0xEC, 0x37, 0x11, 0x93, 0x16, 0xF4, 0x66, 0x00, - 0x12, 0x67, 0xAB, 0xBA, 0xFF, 0x29, 0x13, 0x9E, // 32 random bytes - 0x00, // session id length - 0x00, 0x06, // chiper suites length - 0x00, 0x3D, // RSA_WITH_AES_256_CBC_SHA256 - 0x00, 0x35, // RSA_WITH_AES_256_CBC_SHA - 0x00, 0x05, // RSA_WITH_RC4_128_SHA - 0x01, // compression methods length - 0x00, // no compression - 0x00, 0x00 // extensions length - }; + void TlsSession::Handshake () + { + static uint8_t clientHello[] = + { + 0x16, // handshake + 0x03, 0x03, // version (TLS 1.2) + 0x00, 0x33, // length of handshake + // handshake + 0x01, // handshake type (client hello) + 0x00, 0x00, 0x2F, // length of handshake payload + // client hello + 0x03, 0x03, // highest version supported (TLS 1.2) + 0x45, 0xFA, 0x01, 0x19, 0x74, 0x55, 0x18, 0x36, + 0x42, 0x05, 0xC1, 0xDD, 0x4A, 0x21, 0x80, 0x80, + 0xEC, 0x37, 0x11, 0x93, 0x16, 0xF4, 0x66, 0x00, + 0x12, 0x67, 0xAB, 0xBA, 0xFF, 0x29, 0x13, 0x9E, // 32 random bytes + 0x00, // session id length + 0x00, 0x06, // chiper suites length + 0x00, 0x3D, // RSA_WITH_AES_256_CBC_SHA256 + 0x00, 0x35, // RSA_WITH_AES_256_CBC_SHA + 0x00, 0x05, // RSA_WITH_RC4_128_SHA + 0x01, // compression methods length + 0x00, // no compression + 0x00, 0x00 // extensions length + }; - static uint8_t changeCipherSpecs[] = - { - 0x14, // change cipher specs - 0x03, 0x03, // version (TLS 1.2) - 0x00, 0x01, // length - 0x01 // type - }; - - // send ClientHello - m_Site.write ((char *)clientHello, sizeof (clientHello)); - m_FinishedHash.Update (clientHello + 5, sizeof (clientHello) - 5); - // read ServerHello - uint8_t type; - m_Site.read ((char *)&type, 1); - uint16_t version; - m_Site.read ((char *)&version, 2); - uint16_t length; - m_Site.read ((char *)&length, 2); - length = be16toh (length); - char * serverHello = new char[length]; - m_Site.read (serverHello, length); - m_FinishedHash.Update ((uint8_t *)serverHello, length); - uint8_t serverRandom[32]; - if (serverHello[0] == 0x02) // handshake type server hello - memcpy (serverRandom, serverHello + 6, 32); - else - LogPrint (eLogError, "Unexpected handshake type ", (int)serverHello[0]); - uint8_t sessionIDLen = serverHello[38]; // 6 + 32 - char * cipherSuite = serverHello + 39 + sessionIDLen; - if (cipherSuite[1] == 0x3D || cipherSuite[1] == 0x35 || cipherSuite[1] == 0x05) - m_IsEstablished = true; - else - LogPrint (eLogError, "Unsupported cipher ", (int)cipherSuite[0], ",", (int)cipherSuite[1]); - // read Certificate - m_Site.read ((char *)&type, 1); - m_Site.read ((char *)&version, 2); - m_Site.read ((char *)&length, 2); - length = be16toh (length); - char * certificate = new char[length]; - m_Site.read (certificate, length); - m_FinishedHash.Update ((uint8_t *)certificate, length); - CryptoPP::RSA::PublicKey publicKey; - // 0 - handshake type - // 1 - 3 - handshake payload length - // 4 - 6 - length of array of certificates - // 7 - 9 - length of certificate - if (certificate[0] == 0x0B) // handshake type certificate - publicKey = ExtractPublicKey ((uint8_t *)certificate + 10, length - 10); - else - LogPrint (eLogError, "Unexpected handshake type ", (int)certificate[0]); - // read ServerHelloDone - m_Site.read ((char *)&type, 1); - m_Site.read ((char *)&version, 2); - m_Site.read ((char *)&length, 2); - length = be16toh (length); - char * serverHelloDone = new char[length]; - m_Site.read (serverHelloDone, length); - m_FinishedHash.Update ((uint8_t *)serverHelloDone, length); - if (serverHelloDone[0] != 0x0E) // handshake type hello done - LogPrint (eLogError, "Unexpected handshake type ", (int)serverHelloDone[0]); - // our turn now - // generate secret key - uint8_t secret[48]; - secret[0] = 3; secret[1] = 3; // version - CryptoPP::AutoSeededRandomPool rnd; - rnd.GenerateBlock (secret + 2, 46); // 46 random bytes - // encrypt RSA - CryptoPP::RSAES_PKCS1v15_Encryptor encryptor(publicKey); - size_t encryptedLen = encryptor.CiphertextLength (48); // number of bytes for encrypted 48 bytes, usually 256 (2048 bits key) - uint8_t * encrypted = new uint8_t[encryptedLen + 2]; // + 2 bytes for length - htobe16buf (encrypted, encryptedLen); // first two bytes means length - encryptor.Encrypt (rnd, secret, 48, encrypted + 2); - // send ClientKeyExchange - // 0x10 - handshake type "client key exchange" - SendHandshakeMsg (0x10, encrypted, encryptedLen + 2); - delete[] encrypted; - // send ChangeCipherSpecs - m_Site.write ((char *)changeCipherSpecs, sizeof (changeCipherSpecs)); - // calculate master secret - uint8_t random[64]; - memcpy (random, clientHello + 11, 32); - memcpy (random + 32, serverRandom, 32); - PRF (secret, "master secret", random, 64, 48, m_MasterSecret); - // create keys - memcpy (random, serverRandom, 32); - memcpy (random + 32, clientHello + 11, 32); - uint8_t keys[128]; // clientMACKey(32 or 20), serverMACKey(32 or 20), clientKey(32), serverKey(32) - PRF (m_MasterSecret, "key expansion", random, 64, 128, keys); - // create cipher - if (cipherSuite[1] == 0x3D) - { - LogPrint (eLogInfo, "Chiper suite is RSA_WITH_AES_256_CBC_SHA256"); - m_Cipher = new TlsCipher_AES_256_CBC (keys); - } - else if (cipherSuite[1] == 0x35) - { - LogPrint (eLogInfo, "Chiper suite is RSA_WITH_AES_256_CBC_SHA"); - m_Cipher = new TlsCipher_AES_256_CBC (keys); - } - else - { - // TODO: - if (cipherSuite[1] == 0x05) - LogPrint (eLogInfo, "Chiper suite is RSA_WITH_RC4_128_SHA"); - m_Cipher = new TlsCipher_RC4_SHA (keys); - } - // send finished - SendFinishedMsg (); - // read ChangeCipherSpecs - uint8_t changeCipherSpecs1[6]; - m_Site.read ((char *)changeCipherSpecs1, 6); - // read finished - m_Site.read ((char *)&type, 1); - m_Site.read ((char *)&version, 2); - m_Site.read ((char *)&length, 2); - length = be16toh (length); - char * finished1 = new char[length]; - m_Site.read (finished1, length); - m_Cipher->Decrypt ((uint8_t *)finished1, length); // for streaming ciphers - delete[] finished1; + static uint8_t changeCipherSpecs[] = + { + 0x14, // change cipher specs + 0x03, 0x03, // version (TLS 1.2) + 0x00, 0x01, // length + 0x01 // type + }; + + // send ClientHello + m_Site.write ((char *)clientHello, sizeof (clientHello)); + m_FinishedHash.Update (clientHello + 5, sizeof (clientHello) - 5); + // read ServerHello + uint8_t type; + m_Site.read ((char *)&type, 1); + uint16_t version; + m_Site.read ((char *)&version, 2); + uint16_t length; + m_Site.read ((char *)&length, 2); + length = be16toh (length); + char * serverHello = new char[length]; + m_Site.read (serverHello, length); + m_FinishedHash.Update ((uint8_t *)serverHello, length); + uint8_t serverRandom[32]; + if (serverHello[0] == 0x02) // handshake type server hello + memcpy (serverRandom, serverHello + 6, 32); + else + LogPrint (eLogError, "Unexpected handshake type ", (int)serverHello[0]); + uint8_t sessionIDLen = serverHello[38]; // 6 + 32 + char * cipherSuite = serverHello + 39 + sessionIDLen; + if (cipherSuite[1] == 0x3D || cipherSuite[1] == 0x35 || cipherSuite[1] == 0x05) + m_IsEstablished = true; + else + LogPrint (eLogError, "Unsupported cipher ", (int)cipherSuite[0], ",", (int)cipherSuite[1]); + // read Certificate + m_Site.read ((char *)&type, 1); + m_Site.read ((char *)&version, 2); + m_Site.read ((char *)&length, 2); + length = be16toh (length); + char * certificate = new char[length]; + m_Site.read (certificate, length); + m_FinishedHash.Update ((uint8_t *)certificate, length); + CryptoPP::RSA::PublicKey publicKey; + // 0 - handshake type + // 1 - 3 - handshake payload length + // 4 - 6 - length of array of certificates + // 7 - 9 - length of certificate + if (certificate[0] == 0x0B) // handshake type certificate + publicKey = ExtractPublicKey ((uint8_t *)certificate + 10, length - 10); + else + LogPrint (eLogError, "Unexpected handshake type ", (int)certificate[0]); + // read ServerHelloDone + m_Site.read ((char *)&type, 1); + m_Site.read ((char *)&version, 2); + m_Site.read ((char *)&length, 2); + length = be16toh (length); + char * serverHelloDone = new char[length]; + m_Site.read (serverHelloDone, length); + m_FinishedHash.Update ((uint8_t *)serverHelloDone, length); + if (serverHelloDone[0] != 0x0E) // handshake type hello done + LogPrint (eLogError, "Unexpected handshake type ", (int)serverHelloDone[0]); + // our turn now + // generate secret key + uint8_t secret[48]; + secret[0] = 3; secret[1] = 3; // version + CryptoPP::AutoSeededRandomPool rnd; + rnd.GenerateBlock (secret + 2, 46); // 46 random bytes + // encrypt RSA + CryptoPP::RSAES_PKCS1v15_Encryptor encryptor(publicKey); + size_t encryptedLen = encryptor.CiphertextLength (48); // number of bytes for encrypted 48 bytes, usually 256 (2048 bits key) + uint8_t * encrypted = new uint8_t[encryptedLen + 2]; // + 2 bytes for length + htobe16buf (encrypted, encryptedLen); // first two bytes means length + encryptor.Encrypt (rnd, secret, 48, encrypted + 2); + // send ClientKeyExchange + // 0x10 - handshake type "client key exchange" + SendHandshakeMsg (0x10, encrypted, encryptedLen + 2); + delete[] encrypted; + // send ChangeCipherSpecs + m_Site.write ((char *)changeCipherSpecs, sizeof (changeCipherSpecs)); + // calculate master secret + uint8_t random[64]; + memcpy (random, clientHello + 11, 32); + memcpy (random + 32, serverRandom, 32); + PRF (secret, "master secret", random, 64, 48, m_MasterSecret); + // create keys + memcpy (random, serverRandom, 32); + memcpy (random + 32, clientHello + 11, 32); + uint8_t keys[128]; // clientMACKey(32 or 20), serverMACKey(32 or 20), clientKey(32), serverKey(32) + PRF (m_MasterSecret, "key expansion", random, 64, 128, keys); + // create cipher + if (cipherSuite[1] == 0x3D) + { + LogPrint (eLogInfo, "Chiper suite is RSA_WITH_AES_256_CBC_SHA256"); + m_Cipher = new TlsCipher_AES_256_CBC (keys); + } + else if (cipherSuite[1] == 0x35) + { + LogPrint (eLogInfo, "Chiper suite is RSA_WITH_AES_256_CBC_SHA"); + m_Cipher = new TlsCipher_AES_256_CBC (keys); + } + else + { + // TODO: + if (cipherSuite[1] == 0x05) + LogPrint (eLogInfo, "Chiper suite is RSA_WITH_RC4_128_SHA"); + m_Cipher = new TlsCipher_RC4_SHA (keys); + } + // send finished + SendFinishedMsg (); + // read ChangeCipherSpecs + uint8_t changeCipherSpecs1[6]; + m_Site.read ((char *)changeCipherSpecs1, 6); + // read finished + m_Site.read ((char *)&type, 1); + m_Site.read ((char *)&version, 2); + m_Site.read ((char *)&length, 2); + length = be16toh (length); + char * finished1 = new char[length]; + m_Site.read (finished1, length); + m_Cipher->Decrypt ((uint8_t *)finished1, length); // for streaming ciphers + delete[] finished1; - delete[] serverHello; - delete[] certificate; - delete[] serverHelloDone; - } + delete[] serverHello; + delete[] certificate; + delete[] serverHelloDone; + } - void TlsSession::SendHandshakeMsg (uint8_t handshakeType, uint8_t * data, size_t len) - { - uint8_t handshakeHeader[9]; - handshakeHeader[0] = 0x16; // handshake - handshakeHeader[1] = 0x03; handshakeHeader[2] = 0x03; // version is always TLS 1.2 (3,3) - htobe16buf (handshakeHeader + 3, len + 4); // length of payload - //payload starts - handshakeHeader[5] = handshakeType; // handshake type - handshakeHeader[6] = 0; // highest byte of payload length is always zero - htobe16buf (handshakeHeader + 7, len); // length of data - m_Site.write ((char *)handshakeHeader, 9); - m_FinishedHash.Update (handshakeHeader + 5, 4); // only payload counts - m_Site.write ((char *)data, len); - m_FinishedHash.Update (data, len); - } + void TlsSession::SendHandshakeMsg (uint8_t handshakeType, uint8_t * data, size_t len) + { + uint8_t handshakeHeader[9]; + handshakeHeader[0] = 0x16; // handshake + handshakeHeader[1] = 0x03; handshakeHeader[2] = 0x03; // version is always TLS 1.2 (3,3) + htobe16buf (handshakeHeader + 3, len + 4); // length of payload + //payload starts + handshakeHeader[5] = handshakeType; // handshake type + handshakeHeader[6] = 0; // highest byte of payload length is always zero + htobe16buf (handshakeHeader + 7, len); // length of data + m_Site.write ((char *)handshakeHeader, 9); + m_FinishedHash.Update (handshakeHeader + 5, 4); // only payload counts + m_Site.write ((char *)data, len); + m_FinishedHash.Update (data, len); + } - void TlsSession::SendFinishedMsg () - { - // 0x16 handshake - // 0x03, 0x03 version (TLS 1.2) - // 2 bytes length of handshake (80 or 64 bytes) - // handshake (encrypted) - // unencrypted context - // 0x14 handshake type (finished) - // 0x00, 0x00, 0x0C length of handshake payload - // 12 bytes of verified data + void TlsSession::SendFinishedMsg () + { + // 0x16 handshake + // 0x03, 0x03 version (TLS 1.2) + // 2 bytes length of handshake (80 or 64 bytes) + // handshake (encrypted) + // unencrypted context + // 0x14 handshake type (finished) + // 0x00, 0x00, 0x0C length of handshake payload + // 12 bytes of verified data - uint8_t finishedHashDigest[32], finishedPayload[40], encryptedPayload[80]; - finishedPayload[0] = 0x14; // handshake type (finished) - finishedPayload[1] = 0; finishedPayload[2] = 0; finishedPayload[3] = 0x0C; // 12 bytes - m_FinishedHash.Final (finishedHashDigest); - PRF (m_MasterSecret, "client finished", finishedHashDigest, 32, 12, finishedPayload + 4); - uint8_t mac[32]; - m_Cipher->CalculateMAC (0x16, finishedPayload, 16, mac); - size_t encryptedPayloadSize = m_Cipher->Encrypt (finishedPayload, 16, mac, encryptedPayload); - uint8_t finished[5]; - finished[0] = 0x16; // handshake - finished[1] = 0x03; finished[2] = 0x03; // version is always TLS 1.2 (3,3) - htobe16buf (finished + 3, encryptedPayloadSize); // length of payload - m_Site.write ((char *)finished, sizeof (finished)); - m_Site.write ((char *)encryptedPayload, encryptedPayloadSize); - } + uint8_t finishedHashDigest[32], finishedPayload[40], encryptedPayload[80]; + finishedPayload[0] = 0x14; // handshake type (finished) + finishedPayload[1] = 0; finishedPayload[2] = 0; finishedPayload[3] = 0x0C; // 12 bytes + m_FinishedHash.Final (finishedHashDigest); + PRF (m_MasterSecret, "client finished", finishedHashDigest, 32, 12, finishedPayload + 4); + uint8_t mac[32]; + m_Cipher->CalculateMAC (0x16, finishedPayload, 16, mac); + size_t encryptedPayloadSize = m_Cipher->Encrypt (finishedPayload, 16, mac, encryptedPayload); + uint8_t finished[5]; + finished[0] = 0x16; // handshake + finished[1] = 0x03; finished[2] = 0x03; // version is always TLS 1.2 (3,3) + htobe16buf (finished + 3, encryptedPayloadSize); // length of payload + m_Site.write ((char *)finished, sizeof (finished)); + m_Site.write ((char *)encryptedPayload, encryptedPayloadSize); + } - void TlsSession::PRF (const uint8_t * secret, const char * label, const uint8_t * random, size_t randomLen, - size_t len, uint8_t * buf) - { - // secret is assumed 48 bytes - // random is not more than 64 bytes - CryptoPP::HMAC hmac (secret, 48); - uint8_t seed[96]; size_t seedLen; - seedLen = strlen (label); - memcpy (seed, label, seedLen); - memcpy (seed + seedLen, random, randomLen); - seedLen += randomLen; + void TlsSession::PRF (const uint8_t * secret, const char * label, const uint8_t * random, size_t randomLen, + size_t len, uint8_t * buf) + { + // secret is assumed 48 bytes + // random is not more than 64 bytes + CryptoPP::HMAC hmac (secret, 48); + uint8_t seed[96]; size_t seedLen; + seedLen = strlen (label); + memcpy (seed, label, seedLen); + memcpy (seed + seedLen, random, randomLen); + seedLen += randomLen; - size_t offset = 0; - uint8_t a[128]; - hmac.CalculateDigest (a, seed, seedLen); - while (offset < len) - { - memcpy (a + 32, seed, seedLen); - hmac.CalculateDigest (buf + offset, a, seedLen + 32); - offset += 32; - hmac.CalculateDigest (a, a, 32); - } - } + size_t offset = 0; + uint8_t a[128]; + hmac.CalculateDigest (a, seed, seedLen); + while (offset < len) + { + memcpy (a + 32, seed, seedLen); + hmac.CalculateDigest (buf + offset, a, seedLen + 32); + offset += 32; + hmac.CalculateDigest (a, a, 32); + } + } - CryptoPP::RSA::PublicKey TlsSession::ExtractPublicKey (const uint8_t * certificate, size_t len) - { - CryptoPP::ByteQueue queue; - queue.Put (certificate, len); - queue.MessageEnd (); - // extract X.509 - CryptoPP::BERSequenceDecoder x509Cert (queue); - CryptoPP::BERSequenceDecoder tbsCert (x509Cert); - // version - uint32_t ver; - CryptoPP::BERGeneralDecoder context (tbsCert, CryptoPP::CONTEXT_SPECIFIC | CryptoPP::CONSTRUCTED); - CryptoPP::BERDecodeUnsigned(context, ver, CryptoPP::INTEGER); - // serial - CryptoPP::Integer serial; - serial.BERDecode(tbsCert); - // signature - CryptoPP::BERSequenceDecoder signature (tbsCert); - signature.SkipAll(); - // issuer - CryptoPP::BERSequenceDecoder issuer (tbsCert); - issuer.SkipAll(); - // validity - CryptoPP::BERSequenceDecoder validity (tbsCert); - validity.SkipAll(); - // subject - CryptoPP::BERSequenceDecoder subject (tbsCert); - subject.SkipAll(); - // public key - CryptoPP::BERSequenceDecoder publicKey (tbsCert); - CryptoPP::BERSequenceDecoder ident (publicKey); - ident.SkipAll (); - CryptoPP::BERGeneralDecoder key (publicKey, CryptoPP::BIT_STRING); - key.Skip (1); // FIXME: probably bug in crypto++ - CryptoPP::BERSequenceDecoder keyPair (key); - CryptoPP::Integer n, e; - n.BERDecode (keyPair); - e.BERDecode (keyPair); + CryptoPP::RSA::PublicKey TlsSession::ExtractPublicKey (const uint8_t * certificate, size_t len) + { + CryptoPP::ByteQueue queue; + queue.Put (certificate, len); + queue.MessageEnd (); + // extract X.509 + CryptoPP::BERSequenceDecoder x509Cert (queue); + CryptoPP::BERSequenceDecoder tbsCert (x509Cert); + // version + uint32_t ver; + CryptoPP::BERGeneralDecoder context (tbsCert, CryptoPP::CONTEXT_SPECIFIC | CryptoPP::CONSTRUCTED); + CryptoPP::BERDecodeUnsigned(context, ver, CryptoPP::INTEGER); + // serial + CryptoPP::Integer serial; + serial.BERDecode(tbsCert); + // signature + CryptoPP::BERSequenceDecoder signature (tbsCert); + signature.SkipAll(); + // issuer + CryptoPP::BERSequenceDecoder issuer (tbsCert); + issuer.SkipAll(); + // validity + CryptoPP::BERSequenceDecoder validity (tbsCert); + validity.SkipAll(); + // subject + CryptoPP::BERSequenceDecoder subject (tbsCert); + subject.SkipAll(); + // public key + CryptoPP::BERSequenceDecoder publicKey (tbsCert); + CryptoPP::BERSequenceDecoder ident (publicKey); + ident.SkipAll (); + CryptoPP::BERGeneralDecoder key (publicKey, CryptoPP::BIT_STRING); + key.Skip (1); // FIXME: probably bug in crypto++ + CryptoPP::BERSequenceDecoder keyPair (key); + CryptoPP::Integer n, e; + n.BERDecode (keyPair); + e.BERDecode (keyPair); - CryptoPP::RSA::PublicKey ret; - ret.Initialize (n, e); - return ret; - } + CryptoPP::RSA::PublicKey ret; + ret.Initialize (n, e); + return ret; + } - void TlsSession::Send (const uint8_t * buf, size_t len) - { - uint8_t * out = new uint8_t[len + 64 + 5]; // 64 = 32 mac + 16 iv + upto 16 padding, 5 = header - out[0] = 0x17; // application data - out[1] = 0x03; out[2] = 0x03; // version - uint8_t mac[32]; - m_Cipher->CalculateMAC (0x17, buf, len, mac); - size_t encryptedLen = m_Cipher->Encrypt (buf, len, mac, out + 5); - htobe16buf (out + 3, encryptedLen); - m_Site.write ((char *)out, encryptedLen + 5); - delete[] out; - } + void TlsSession::Send (const uint8_t * buf, size_t len) + { + uint8_t * out = new uint8_t[len + 64 + 5]; // 64 = 32 mac + 16 iv + upto 16 padding, 5 = header + out[0] = 0x17; // application data + out[1] = 0x03; out[2] = 0x03; // version + uint8_t mac[32]; + m_Cipher->CalculateMAC (0x17, buf, len, mac); + size_t encryptedLen = m_Cipher->Encrypt (buf, len, mac, out + 5); + htobe16buf (out + 3, encryptedLen); + m_Site.write ((char *)out, encryptedLen + 5); + delete[] out; + } - bool TlsSession::Receive (std::ostream& rs) - { - if (m_Site.eof ()) return false; - uint8_t type; uint16_t version, length; - m_Site.read ((char *)&type, 1); - m_Site.read ((char *)&version, 2); - m_Site.read ((char *)&length, 2); - length = be16toh (length); - uint8_t * buf = new uint8_t[length]; - m_Site.read ((char *)buf, length); - size_t decryptedLen = m_Cipher->Decrypt (buf, length); - rs.write ((char *)buf + m_Cipher->GetIVSize (), decryptedLen); - delete[] buf; - return true; - } + bool TlsSession::Receive (std::ostream& rs) + { + if (m_Site.eof ()) return false; + uint8_t type; uint16_t version, length; + m_Site.read ((char *)&type, 1); + m_Site.read ((char *)&version, 2); + m_Site.read ((char *)&length, 2); + length = be16toh (length); + uint8_t * buf = new uint8_t[length]; + m_Site.read ((char *)buf, length); + size_t decryptedLen = m_Cipher->Decrypt (buf, length); + rs.write ((char *)buf + m_Cipher->GetIVSize (), decryptedLen); + delete[] buf; + return true; + } } } diff --git a/Reseed.h b/Reseed.h index 3c92d066..cdd76858 100644 --- a/Reseed.h +++ b/Reseed.h @@ -16,79 +16,79 @@ namespace i2p namespace data { - class Reseeder - { - typedef Tag<512> PublicKey; - - public: - - Reseeder(); - ~Reseeder(); - bool reseedNow(); // depreacted - int ReseedNowSU3 (); + class Reseeder + { + typedef Tag<512> PublicKey; + + public: + + Reseeder(); + ~Reseeder(); + bool reseedNow(); // depreacted + int ReseedNowSU3 (); - void LoadCertificates (); - - private: + void LoadCertificates (); + + private: - void LoadCertificate (const std::string& filename); - std::string LoadCertificate (CryptoPP::ByteQueue& queue); // returns issuer's name - - int ReseedFromSU3 (const std::string& host, bool https = false); - int ProcessSU3File (const char * filename); - int ProcessSU3Stream (std::istream& s); + void LoadCertificate (const std::string& filename); + std::string LoadCertificate (CryptoPP::ByteQueue& queue); // returns issuer's name + + int ReseedFromSU3 (const std::string& host, bool https = false); + int ProcessSU3File (const char * filename); + int ProcessSU3Stream (std::istream& s); - bool FindZipDataDescriptor (std::istream& s); - - std::string HttpsRequest (const std::string& address); + bool FindZipDataDescriptor (std::istream& s); + + std::string HttpsRequest (const std::string& address); - private: + private: - std::map m_SigningKeys; - }; + std::map m_SigningKeys; + }; - class TlsCipher - { - public: + class TlsCipher + { + public: - virtual ~TlsCipher () {}; + virtual ~TlsCipher () {}; - virtual void CalculateMAC (uint8_t type, const uint8_t * buf, size_t len, uint8_t * mac) = 0; - virtual size_t Encrypt (const uint8_t * in, size_t len, const uint8_t * mac, uint8_t * out) = 0; - virtual size_t Decrypt (uint8_t * buf, size_t len) = 0; - virtual size_t GetIVSize () const { return 0; }; // override for AES - }; + virtual void CalculateMAC (uint8_t type, const uint8_t * buf, size_t len, uint8_t * mac) = 0; + virtual size_t Encrypt (const uint8_t * in, size_t len, const uint8_t * mac, uint8_t * out) = 0; + virtual size_t Decrypt (uint8_t * buf, size_t len) = 0; + virtual size_t GetIVSize () const { return 0; }; // override for AES + }; - class TlsSession - { - public: + class TlsSession + { + public: - TlsSession (const std::string& host, int port); - ~TlsSession (); - void Send (const uint8_t * buf, size_t len); - bool Receive (std::ostream& rs); - bool IsEstablished () const { return m_IsEstablished; }; - - private: + TlsSession (const std::string& host, int port); + ~TlsSession (); + void Send (const uint8_t * buf, size_t len); + bool Receive (std::ostream& rs); + bool IsEstablished () const { return m_IsEstablished; }; + + private: - void Handshake (); - void SendHandshakeMsg (uint8_t handshakeType, uint8_t * data, size_t len); - void SendFinishedMsg (); - CryptoPP::RSA::PublicKey ExtractPublicKey (const uint8_t * certificate, size_t len); + void Handshake (); + void SendHandshakeMsg (uint8_t handshakeType, uint8_t * data, size_t len); + void SendFinishedMsg (); + CryptoPP::RSA::PublicKey ExtractPublicKey (const uint8_t * certificate, size_t len); - void PRF (const uint8_t * secret, const char * label, const uint8_t * random, size_t randomLen, - size_t len, uint8_t * buf); + void PRF (const uint8_t * secret, const char * label, const uint8_t * random, size_t randomLen, + size_t len, uint8_t * buf); - private: + private: - bool m_IsEstablished; - boost::asio::ip::tcp::iostream m_Site; - CryptoPP::SHA256 m_FinishedHash; - uint8_t m_MasterSecret[64]; // actual size is 48, but must be multiple of 32 - TlsCipher * m_Cipher; - }; + bool m_IsEstablished; + boost::asio::ip::tcp::iostream m_Site; + CryptoPP::SHA256 m_FinishedHash; + uint8_t m_MasterSecret[64]; // actual size is 48, but must be multiple of 32 + TlsCipher * m_Cipher; + }; } } diff --git a/RouterContext.cpp b/RouterContext.cpp index c9328b5c..0f6d4954 100644 --- a/RouterContext.cpp +++ b/RouterContext.cpp @@ -12,307 +12,307 @@ namespace i2p { - RouterContext context; + RouterContext context; - RouterContext::RouterContext (): - m_LastUpdateTime (0), m_AcceptsTunnels (true), m_IsFloodfill (false), - m_StartupTime (0), m_Status (eRouterStatusOK ) - { - } + RouterContext::RouterContext (): + m_LastUpdateTime (0), m_AcceptsTunnels (true), m_IsFloodfill (false), + m_StartupTime (0), m_Status (eRouterStatusOK ) + { + } - void RouterContext::Init () - { - m_StartupTime = i2p::util::GetSecondsSinceEpoch (); - if (!Load ()) - CreateNewRouter (); - UpdateRouterInfo (); - } + void RouterContext::Init () + { + m_StartupTime = i2p::util::GetSecondsSinceEpoch (); + if (!Load ()) + CreateNewRouter (); + UpdateRouterInfo (); + } - void RouterContext::CreateNewRouter () - { - m_Keys = i2p::data::CreateRandomKeys (); - SaveKeys (); - NewRouterInfo (); - } + void RouterContext::CreateNewRouter () + { + m_Keys = i2p::data::CreateRandomKeys (); + SaveKeys (); + NewRouterInfo (); + } - void RouterContext::NewRouterInfo () - { - i2p::data::RouterInfo routerInfo; - routerInfo.SetRouterIdentity (GetIdentity ()); - int port = i2p::util::config::GetArg("-port", 0); - if (!port) - port = m_Rnd.GenerateWord32 (9111, 30777); // I2P network ports range - routerInfo.AddSSUAddress (i2p::util::config::GetCharArg("-host", "127.0.0.1"), port, routerInfo.GetIdentHash ()); - routerInfo.AddNTCPAddress (i2p::util::config::GetCharArg("-host", "127.0.0.1"), port); - routerInfo.SetCaps (i2p::data::RouterInfo::eReachable | - i2p::data::RouterInfo::eSSUTesting | i2p::data::RouterInfo::eSSUIntroducer); // LR, BC - routerInfo.SetProperty ("coreVersion", I2P_VERSION); - routerInfo.SetProperty ("netId", "2"); - routerInfo.SetProperty ("router.version", I2P_VERSION); - routerInfo.SetProperty ("stat_uptime", "90m"); - routerInfo.CreateBuffer (m_Keys); - m_RouterInfo.Update (routerInfo.GetBuffer (), routerInfo.GetBufferLen ()); - } + void RouterContext::NewRouterInfo () + { + i2p::data::RouterInfo routerInfo; + routerInfo.SetRouterIdentity (GetIdentity ()); + int port = i2p::util::config::GetArg("-port", 0); + if (!port) + port = m_Rnd.GenerateWord32 (9111, 30777); // I2P network ports range + routerInfo.AddSSUAddress (i2p::util::config::GetCharArg("-host", "127.0.0.1"), port, routerInfo.GetIdentHash ()); + routerInfo.AddNTCPAddress (i2p::util::config::GetCharArg("-host", "127.0.0.1"), port); + routerInfo.SetCaps (i2p::data::RouterInfo::eReachable | + i2p::data::RouterInfo::eSSUTesting | i2p::data::RouterInfo::eSSUIntroducer); // LR, BC + routerInfo.SetProperty ("coreVersion", I2P_VERSION); + routerInfo.SetProperty ("netId", "2"); + routerInfo.SetProperty ("router.version", I2P_VERSION); + routerInfo.SetProperty ("stat_uptime", "90m"); + routerInfo.CreateBuffer (m_Keys); + m_RouterInfo.Update (routerInfo.GetBuffer (), routerInfo.GetBufferLen ()); + } - void RouterContext::UpdateRouterInfo () - { - m_RouterInfo.CreateBuffer (m_Keys); - m_RouterInfo.SaveToFile (i2p::util::filesystem::GetFullPath (ROUTER_INFO)); - m_LastUpdateTime = i2p::util::GetSecondsSinceEpoch (); - } + void RouterContext::UpdateRouterInfo () + { + m_RouterInfo.CreateBuffer (m_Keys); + m_RouterInfo.SaveToFile (i2p::util::filesystem::GetFullPath (ROUTER_INFO)); + m_LastUpdateTime = i2p::util::GetSecondsSinceEpoch (); + } - void RouterContext::UpdatePort (int port) - { - bool updated = false; - for (auto& address : m_RouterInfo.GetAddresses ()) - { - if (address.port != port) - { - address.port = port; - updated = true; - } - } - if (updated) - UpdateRouterInfo (); - } + void RouterContext::UpdatePort (int port) + { + bool updated = false; + for (auto& address : m_RouterInfo.GetAddresses ()) + { + if (address.port != port) + { + address.port = port; + updated = true; + } + } + if (updated) + UpdateRouterInfo (); + } - void RouterContext::UpdateAddress (const boost::asio::ip::address& host) - { - bool updated = false; - for (auto& address : m_RouterInfo.GetAddresses ()) - { - if (address.host != host && address.IsCompatible (host)) - { - address.host = host; - updated = true; - } - } - auto ts = i2p::util::GetSecondsSinceEpoch (); - if (updated || ts > m_LastUpdateTime + ROUTER_INFO_UPDATE_INTERVAL) - UpdateRouterInfo (); - } + void RouterContext::UpdateAddress (const boost::asio::ip::address& host) + { + bool updated = false; + for (auto& address : m_RouterInfo.GetAddresses ()) + { + if (address.host != host && address.IsCompatible (host)) + { + address.host = host; + updated = true; + } + } + auto ts = i2p::util::GetSecondsSinceEpoch (); + if (updated || ts > m_LastUpdateTime + ROUTER_INFO_UPDATE_INTERVAL) + UpdateRouterInfo (); + } - bool RouterContext::AddIntroducer (const i2p::data::RouterInfo& routerInfo, uint32_t tag) - { - bool ret = false; - auto address = routerInfo.GetSSUAddress (); - if (address) - { - ret = m_RouterInfo.AddIntroducer (address, tag); - if (ret) - UpdateRouterInfo (); - } - return ret; - } + bool RouterContext::AddIntroducer (const i2p::data::RouterInfo& routerInfo, uint32_t tag) + { + bool ret = false; + auto address = routerInfo.GetSSUAddress (); + if (address) + { + ret = m_RouterInfo.AddIntroducer (address, tag); + if (ret) + UpdateRouterInfo (); + } + return ret; + } - void RouterContext::RemoveIntroducer (const boost::asio::ip::udp::endpoint& e) - { - if (m_RouterInfo.RemoveIntroducer (e)) - UpdateRouterInfo (); - } - - void RouterContext::SetFloodfill (bool floodfill) - { - m_IsFloodfill = floodfill; - if (floodfill) - m_RouterInfo.SetCaps (m_RouterInfo.GetCaps () | i2p::data::RouterInfo::eFloodfill); - else - { - m_RouterInfo.SetCaps (m_RouterInfo.GetCaps () & ~i2p::data::RouterInfo::eFloodfill); - // we don't publish number of routers and leaseset for non-floodfill - m_RouterInfo.DeleteProperty (ROUTER_INFO_PROPERTY_LEASESETS); - m_RouterInfo.DeleteProperty (ROUTER_INFO_PROPERTY_ROUTERS); - } - UpdateRouterInfo (); - } + void RouterContext::RemoveIntroducer (const boost::asio::ip::udp::endpoint& e) + { + if (m_RouterInfo.RemoveIntroducer (e)) + UpdateRouterInfo (); + } + + void RouterContext::SetFloodfill (bool floodfill) + { + m_IsFloodfill = floodfill; + if (floodfill) + m_RouterInfo.SetCaps (m_RouterInfo.GetCaps () | i2p::data::RouterInfo::eFloodfill); + else + { + m_RouterInfo.SetCaps (m_RouterInfo.GetCaps () & ~i2p::data::RouterInfo::eFloodfill); + // we don't publish number of routers and leaseset for non-floodfill + m_RouterInfo.DeleteProperty (ROUTER_INFO_PROPERTY_LEASESETS); + m_RouterInfo.DeleteProperty (ROUTER_INFO_PROPERTY_ROUTERS); + } + UpdateRouterInfo (); + } - void RouterContext::SetHighBandwidth () - { - if (!m_RouterInfo.IsHighBandwidth ()) - { - m_RouterInfo.SetCaps (m_RouterInfo.GetCaps () | i2p::data::RouterInfo::eHighBandwidth); - UpdateRouterInfo (); - } - } + void RouterContext::SetHighBandwidth () + { + if (!m_RouterInfo.IsHighBandwidth ()) + { + m_RouterInfo.SetCaps (m_RouterInfo.GetCaps () | i2p::data::RouterInfo::eHighBandwidth); + UpdateRouterInfo (); + } + } - void RouterContext::SetLowBandwidth () - { - if (m_RouterInfo.IsHighBandwidth ()) - { - m_RouterInfo.SetCaps (m_RouterInfo.GetCaps () & ~i2p::data::RouterInfo::eHighBandwidth); - UpdateRouterInfo (); - } - } + void RouterContext::SetLowBandwidth () + { + if (m_RouterInfo.IsHighBandwidth ()) + { + m_RouterInfo.SetCaps (m_RouterInfo.GetCaps () & ~i2p::data::RouterInfo::eHighBandwidth); + UpdateRouterInfo (); + } + } - bool RouterContext::IsUnreachable () const - { - return m_RouterInfo.GetCaps () & i2p::data::RouterInfo::eUnreachable; - } - - void RouterContext::SetUnreachable () - { - // set caps - m_RouterInfo.SetCaps (i2p::data::RouterInfo::eUnreachable | i2p::data::RouterInfo::eSSUTesting); // LU, B - // remove NTCP address - auto& addresses = m_RouterInfo.GetAddresses (); - for (size_t i = 0; i < addresses.size (); i++) - { - if (addresses[i].transportStyle == i2p::data::RouterInfo::eTransportNTCP) - { - addresses.erase (addresses.begin () + i); - break; - } - } - // delete previous introducers - for (auto& addr : addresses) - addr.introducers.clear (); - - // update - UpdateRouterInfo (); - } + bool RouterContext::IsUnreachable () const + { + return m_RouterInfo.GetCaps () & i2p::data::RouterInfo::eUnreachable; + } + + void RouterContext::SetUnreachable () + { + // set caps + m_RouterInfo.SetCaps (i2p::data::RouterInfo::eUnreachable | i2p::data::RouterInfo::eSSUTesting); // LU, B + // remove NTCP address + auto& addresses = m_RouterInfo.GetAddresses (); + for (size_t i = 0; i < addresses.size (); i++) + { + if (addresses[i].transportStyle == i2p::data::RouterInfo::eTransportNTCP) + { + addresses.erase (addresses.begin () + i); + break; + } + } + // delete previous introducers + for (auto& addr : addresses) + addr.introducers.clear (); + + // update + UpdateRouterInfo (); + } - void RouterContext::SetReachable () - { - // update caps - uint8_t caps = m_RouterInfo.GetCaps (); - caps &= ~i2p::data::RouterInfo::eUnreachable; - caps |= i2p::data::RouterInfo::eReachable; - caps |= i2p::data::RouterInfo::eSSUIntroducer; - if (m_IsFloodfill) - caps |= i2p::data::RouterInfo::eFloodfill; - m_RouterInfo.SetCaps (caps); - - // insert NTCP back - auto& addresses = m_RouterInfo.GetAddresses (); - for (size_t i = 0; i < addresses.size (); i++) - { - if (addresses[i].transportStyle == i2p::data::RouterInfo::eTransportSSU) - { - // insert NTCP address with host/port form SSU - m_RouterInfo.AddNTCPAddress (addresses[i].host.to_string ().c_str (), addresses[i].port); - break; - } - } - // delete previous introducers - for (auto& addr : addresses) - addr.introducers.clear (); - - // update - UpdateRouterInfo (); - } - - void RouterContext::SetSupportsV6 (bool supportsV6) - { - if (supportsV6) - m_RouterInfo.EnableV6 (); - else - m_RouterInfo.DisableV6 (); - UpdateRouterInfo (); - } + void RouterContext::SetReachable () + { + // update caps + uint8_t caps = m_RouterInfo.GetCaps (); + caps &= ~i2p::data::RouterInfo::eUnreachable; + caps |= i2p::data::RouterInfo::eReachable; + caps |= i2p::data::RouterInfo::eSSUIntroducer; + if (m_IsFloodfill) + caps |= i2p::data::RouterInfo::eFloodfill; + m_RouterInfo.SetCaps (caps); + + // insert NTCP back + auto& addresses = m_RouterInfo.GetAddresses (); + for (size_t i = 0; i < addresses.size (); i++) + { + if (addresses[i].transportStyle == i2p::data::RouterInfo::eTransportSSU) + { + // insert NTCP address with host/port form SSU + m_RouterInfo.AddNTCPAddress (addresses[i].host.to_string ().c_str (), addresses[i].port); + break; + } + } + // delete previous introducers + for (auto& addr : addresses) + addr.introducers.clear (); + + // update + UpdateRouterInfo (); + } + + void RouterContext::SetSupportsV6 (bool supportsV6) + { + if (supportsV6) + m_RouterInfo.EnableV6 (); + else + m_RouterInfo.DisableV6 (); + UpdateRouterInfo (); + } - void RouterContext::UpdateNTCPV6Address (const boost::asio::ip::address& host) - { - bool updated = false, found = false; - int port = 0; - auto& addresses = m_RouterInfo.GetAddresses (); - for (auto& addr : addresses) - { - if (addr.host.is_v6 () && addr.transportStyle == i2p::data::RouterInfo::eTransportNTCP) - { - if (addr.host != host) - { - addr.host = host; - updated = true; - } - found = true; - } - else - port = addr.port; - } - if (!found) - { - // create new address - m_RouterInfo.AddNTCPAddress (host.to_string ().c_str (), port); - auto mtu = i2p::util::net::GetMTU (host); - if (mtu) - { - LogPrint ("Our v6 MTU=", mtu); - if (mtu > 1472) mtu = 1472; - } - m_RouterInfo.AddSSUAddress (host.to_string ().c_str (), port, GetIdentHash (), mtu ? mtu : 1472); // TODO - updated = true; - } - if (updated) - UpdateRouterInfo (); - } + void RouterContext::UpdateNTCPV6Address (const boost::asio::ip::address& host) + { + bool updated = false, found = false; + int port = 0; + auto& addresses = m_RouterInfo.GetAddresses (); + for (auto& addr : addresses) + { + if (addr.host.is_v6 () && addr.transportStyle == i2p::data::RouterInfo::eTransportNTCP) + { + if (addr.host != host) + { + addr.host = host; + updated = true; + } + found = true; + } + else + port = addr.port; + } + if (!found) + { + // create new address + m_RouterInfo.AddNTCPAddress (host.to_string ().c_str (), port); + auto mtu = i2p::util::net::GetMTU (host); + if (mtu) + { + LogPrint ("Our v6 MTU=", mtu); + if (mtu > 1472) mtu = 1472; + } + m_RouterInfo.AddSSUAddress (host.to_string ().c_str (), port, GetIdentHash (), mtu ? mtu : 1472); // TODO + updated = true; + } + if (updated) + UpdateRouterInfo (); + } - void RouterContext::UpdateStats () - { - if (m_IsFloodfill) - { - // update routers and leasesets - m_RouterInfo.SetProperty (ROUTER_INFO_PROPERTY_LEASESETS, boost::lexical_cast(i2p::data::netdb.GetNumLeaseSets ())); - m_RouterInfo.SetProperty (ROUTER_INFO_PROPERTY_ROUTERS, boost::lexical_cast(i2p::data::netdb.GetNumRouters ())); - UpdateRouterInfo (); - } - } - - bool RouterContext::Load () - { - std::ifstream fk (i2p::util::filesystem::GetFullPath (ROUTER_KEYS).c_str (), std::ifstream::binary | std::ofstream::in); - if (!fk.is_open ()) return false; - - i2p::data::Keys keys; - fk.read ((char *)&keys, sizeof (keys)); - m_Keys = keys; + void RouterContext::UpdateStats () + { + if (m_IsFloodfill) + { + // update routers and leasesets + m_RouterInfo.SetProperty (ROUTER_INFO_PROPERTY_LEASESETS, boost::lexical_cast(i2p::data::netdb.GetNumLeaseSets ())); + m_RouterInfo.SetProperty (ROUTER_INFO_PROPERTY_ROUTERS, boost::lexical_cast(i2p::data::netdb.GetNumRouters ())); + UpdateRouterInfo (); + } + } + + bool RouterContext::Load () + { + std::ifstream fk (i2p::util::filesystem::GetFullPath (ROUTER_KEYS).c_str (), std::ifstream::binary | std::ofstream::in); + if (!fk.is_open ()) return false; + + i2p::data::Keys keys; + fk.read ((char *)&keys, sizeof (keys)); + m_Keys = keys; - i2p::data::RouterInfo routerInfo(i2p::util::filesystem::GetFullPath (ROUTER_INFO)); // TODO - m_RouterInfo.Update (routerInfo.GetBuffer (), routerInfo.GetBufferLen ()); - m_RouterInfo.SetProperty ("coreVersion", I2P_VERSION); - m_RouterInfo.SetProperty ("router.version", I2P_VERSION); + i2p::data::RouterInfo routerInfo(i2p::util::filesystem::GetFullPath (ROUTER_INFO)); // TODO + m_RouterInfo.Update (routerInfo.GetBuffer (), routerInfo.GetBufferLen ()); + m_RouterInfo.SetProperty ("coreVersion", I2P_VERSION); + m_RouterInfo.SetProperty ("router.version", I2P_VERSION); - if (IsUnreachable ()) - SetReachable (); // we assume reachable until we discover firewall through peer tests - - return true; - } + if (IsUnreachable ()) + SetReachable (); // we assume reachable until we discover firewall through peer tests + + return true; + } - void RouterContext::SaveKeys () - { - std::ofstream fk (i2p::util::filesystem::GetFullPath (ROUTER_KEYS).c_str (), std::ofstream::binary | std::ofstream::out); - i2p::data::Keys keys; - memcpy (keys.privateKey, m_Keys.GetPrivateKey (), sizeof (keys.privateKey)); - memcpy (keys.signingPrivateKey, m_Keys.GetSigningPrivateKey (), sizeof (keys.signingPrivateKey)); - auto& ident = GetIdentity ().GetStandardIdentity (); - memcpy (keys.publicKey, ident.publicKey, sizeof (keys.publicKey)); - memcpy (keys.signingKey, ident.signingKey, sizeof (keys.signingKey)); - fk.write ((char *)&keys, sizeof (keys)); - } + void RouterContext::SaveKeys () + { + std::ofstream fk (i2p::util::filesystem::GetFullPath (ROUTER_KEYS).c_str (), std::ofstream::binary | std::ofstream::out); + i2p::data::Keys keys; + memcpy (keys.privateKey, m_Keys.GetPrivateKey (), sizeof (keys.privateKey)); + memcpy (keys.signingPrivateKey, m_Keys.GetSigningPrivateKey (), sizeof (keys.signingPrivateKey)); + auto& ident = GetIdentity ().GetStandardIdentity (); + memcpy (keys.publicKey, ident.publicKey, sizeof (keys.publicKey)); + memcpy (keys.signingKey, ident.signingKey, sizeof (keys.signingKey)); + fk.write ((char *)&keys, sizeof (keys)); + } - std::shared_ptr RouterContext::GetTunnelPool () const - { - return i2p::tunnel::tunnels.GetExploratoryPool (); - } - - void RouterContext::HandleI2NPMessage (const uint8_t * buf, size_t len, std::shared_ptr from) - { - i2p::HandleI2NPMessage (CreateI2NPMessage (buf, GetI2NPMessageLength (buf), from)); - } + std::shared_ptr RouterContext::GetTunnelPool () const + { + return i2p::tunnel::tunnels.GetExploratoryPool (); + } + + void RouterContext::HandleI2NPMessage (const uint8_t * buf, size_t len, std::shared_ptr from) + { + i2p::HandleI2NPMessage (CreateI2NPMessage (buf, GetI2NPMessageLength (buf), from)); + } - void RouterContext::ProcessGarlicMessage (std::shared_ptr msg) - { - std::unique_lock l(m_GarlicMutex); - i2p::garlic::GarlicDestination::ProcessGarlicMessage (msg); - } - - void RouterContext::ProcessDeliveryStatusMessage (std::shared_ptr msg) - { - std::unique_lock l(m_GarlicMutex); - i2p::garlic::GarlicDestination::ProcessDeliveryStatusMessage (msg); - } - - uint32_t RouterContext::GetUptime () const - { - return i2p::util::GetSecondsSinceEpoch () - m_StartupTime; - } + void RouterContext::ProcessGarlicMessage (std::shared_ptr msg) + { + std::unique_lock l(m_GarlicMutex); + i2p::garlic::GarlicDestination::ProcessGarlicMessage (msg); + } + + void RouterContext::ProcessDeliveryStatusMessage (std::shared_ptr msg) + { + std::unique_lock l(m_GarlicMutex); + i2p::garlic::GarlicDestination::ProcessDeliveryStatusMessage (msg); + } + + uint32_t RouterContext::GetUptime () const + { + return i2p::util::GetSecondsSinceEpoch () - m_StartupTime; + } } diff --git a/RouterContext.h b/RouterContext.h index 2689d025..7c40f20e 100644 --- a/RouterContext.h +++ b/RouterContext.h @@ -14,94 +14,94 @@ namespace i2p { - const char ROUTER_INFO[] = "router.info"; - const char ROUTER_KEYS[] = "router.keys"; - const int ROUTER_INFO_UPDATE_INTERVAL = 1800; // 30 minutes - - const char ROUTER_INFO_PROPERTY_LEASESETS[] = "netdb.knownLeaseSets"; - const char ROUTER_INFO_PROPERTY_ROUTERS[] = "netdb.knownRouters"; + const char ROUTER_INFO[] = "router.info"; + const char ROUTER_KEYS[] = "router.keys"; + const int ROUTER_INFO_UPDATE_INTERVAL = 1800; // 30 minutes + + const char ROUTER_INFO_PROPERTY_LEASESETS[] = "netdb.knownLeaseSets"; + const char ROUTER_INFO_PROPERTY_ROUTERS[] = "netdb.knownRouters"; - enum RouterStatus - { - eRouterStatusOK = 0, - eRouterStatusTesting = 1, - eRouterStatusFirewalled = 2 - }; + enum RouterStatus + { + eRouterStatusOK = 0, + eRouterStatusTesting = 1, + eRouterStatusFirewalled = 2 + }; - class RouterContext: public i2p::garlic::GarlicDestination - { - public: + class RouterContext: public i2p::garlic::GarlicDestination + { + public: - RouterContext (); - void Init (); + RouterContext (); + void Init (); - i2p::data::RouterInfo& GetRouterInfo () { return m_RouterInfo; }; - std::shared_ptr GetSharedRouterInfo () const - { - return std::shared_ptr (&m_RouterInfo, - [](const i2p::data::RouterInfo *) {}); - } - CryptoPP::RandomNumberGenerator& GetRandomNumberGenerator () { return m_Rnd; }; - uint32_t GetUptime () const; - uint32_t GetStartupTime () const { return m_StartupTime; }; - uint64_t GetLastUpdateTime () const { return m_LastUpdateTime; }; - RouterStatus GetStatus () const { return m_Status; }; - void SetStatus (RouterStatus status) { m_Status = status; }; + i2p::data::RouterInfo& GetRouterInfo () { return m_RouterInfo; }; + std::shared_ptr GetSharedRouterInfo () const + { + return std::shared_ptr (&m_RouterInfo, + [](const i2p::data::RouterInfo *) {}); + } + CryptoPP::RandomNumberGenerator& GetRandomNumberGenerator () { return m_Rnd; }; + uint32_t GetUptime () const; + uint32_t GetStartupTime () const { return m_StartupTime; }; + uint64_t GetLastUpdateTime () const { return m_LastUpdateTime; }; + RouterStatus GetStatus () const { return m_Status; }; + void SetStatus (RouterStatus status) { m_Status = status; }; - void UpdatePort (int port); // called from Daemon - void UpdateAddress (const boost::asio::ip::address& host); // called from SSU or Daemon - bool AddIntroducer (const i2p::data::RouterInfo& routerInfo, uint32_t tag); - void RemoveIntroducer (const boost::asio::ip::udp::endpoint& e); - bool IsUnreachable () const; - void SetUnreachable (); - void SetReachable (); - bool IsFloodfill () const { return m_IsFloodfill; }; - void SetFloodfill (bool floodfill); - void SetHighBandwidth (); - void SetLowBandwidth (); - bool AcceptsTunnels () const { return m_AcceptsTunnels; }; - void SetAcceptsTunnels (bool acceptsTunnels) { m_AcceptsTunnels = acceptsTunnels; }; - bool SupportsV6 () const { return m_RouterInfo.IsV6 (); }; - void SetSupportsV6 (bool supportsV6); - void UpdateNTCPV6Address (const boost::asio::ip::address& host); // called from NTCP session - void UpdateStats (); + void UpdatePort (int port); // called from Daemon + void UpdateAddress (const boost::asio::ip::address& host); // called from SSU or Daemon + bool AddIntroducer (const i2p::data::RouterInfo& routerInfo, uint32_t tag); + void RemoveIntroducer (const boost::asio::ip::udp::endpoint& e); + bool IsUnreachable () const; + void SetUnreachable (); + void SetReachable (); + bool IsFloodfill () const { return m_IsFloodfill; }; + void SetFloodfill (bool floodfill); + void SetHighBandwidth (); + void SetLowBandwidth (); + bool AcceptsTunnels () const { return m_AcceptsTunnels; }; + void SetAcceptsTunnels (bool acceptsTunnels) { m_AcceptsTunnels = acceptsTunnels; }; + bool SupportsV6 () const { return m_RouterInfo.IsV6 (); }; + void SetSupportsV6 (bool supportsV6); + void UpdateNTCPV6Address (const boost::asio::ip::address& host); // called from NTCP session + void UpdateStats (); - // implements LocalDestination - const i2p::data::PrivateKeys& GetPrivateKeys () const { return m_Keys; }; - const uint8_t * GetEncryptionPrivateKey () const { return m_Keys.GetPrivateKey (); }; - const uint8_t * GetEncryptionPublicKey () const { return GetIdentity ().GetStandardIdentity ().publicKey; }; - void SetLeaseSetUpdated () {}; + // implements LocalDestination + const i2p::data::PrivateKeys& GetPrivateKeys () const { return m_Keys; }; + const uint8_t * GetEncryptionPrivateKey () const { return m_Keys.GetPrivateKey (); }; + const uint8_t * GetEncryptionPublicKey () const { return GetIdentity ().GetStandardIdentity ().publicKey; }; + void SetLeaseSetUpdated () {}; - // implements GarlicDestination - std::shared_ptr GetLeaseSet () { return nullptr; }; - std::shared_ptr GetTunnelPool () const; - void HandleI2NPMessage (const uint8_t * buf, size_t len, std::shared_ptr from); + // implements GarlicDestination + std::shared_ptr GetLeaseSet () { return nullptr; }; + std::shared_ptr GetTunnelPool () const; + void HandleI2NPMessage (const uint8_t * buf, size_t len, std::shared_ptr from); - // override GarlicDestination - void ProcessGarlicMessage (std::shared_ptr msg); - void ProcessDeliveryStatusMessage (std::shared_ptr msg); - - private: + // override GarlicDestination + void ProcessGarlicMessage (std::shared_ptr msg); + void ProcessDeliveryStatusMessage (std::shared_ptr msg); + + private: - void CreateNewRouter (); - void NewRouterInfo (); - void UpdateRouterInfo (); - bool Load (); - void SaveKeys (); - - private: + void CreateNewRouter (); + void NewRouterInfo (); + void UpdateRouterInfo (); + bool Load (); + void SaveKeys (); + + private: - i2p::data::RouterInfo m_RouterInfo; - i2p::data::PrivateKeys m_Keys; - CryptoPP::AutoSeededRandomPool m_Rnd; - uint64_t m_LastUpdateTime; - bool m_AcceptsTunnels, m_IsFloodfill; - uint64_t m_StartupTime; // in seconds since epoch - RouterStatus m_Status; - std::mutex m_GarlicMutex; - }; + i2p::data::RouterInfo m_RouterInfo; + i2p::data::PrivateKeys m_Keys; + CryptoPP::AutoSeededRandomPool m_Rnd; + uint64_t m_LastUpdateTime; + bool m_AcceptsTunnels, m_IsFloodfill; + uint64_t m_StartupTime; // in seconds since epoch + RouterStatus m_Status; + std::mutex m_GarlicMutex; + }; - extern RouterContext context; -} + extern RouterContext context; +} #endif diff --git a/RouterInfo.cpp b/RouterInfo.cpp index 20d119d9..f4dadeb7 100644 --- a/RouterInfo.cpp +++ b/RouterInfo.cpp @@ -16,651 +16,651 @@ namespace i2p { namespace data -{ - RouterInfo::RouterInfo (const std::string& fullPath): - m_FullPath (fullPath), m_IsUpdated (false), m_IsUnreachable (false), - m_SupportedTransports (0), m_Caps (0) - { - m_Buffer = new uint8_t[MAX_RI_BUFFER_SIZE]; - ReadFromFile (); - } +{ + RouterInfo::RouterInfo (const std::string& fullPath): + m_FullPath (fullPath), m_IsUpdated (false), m_IsUnreachable (false), + m_SupportedTransports (0), m_Caps (0) + { + m_Buffer = new uint8_t[MAX_RI_BUFFER_SIZE]; + ReadFromFile (); + } - RouterInfo::RouterInfo (const uint8_t * buf, int len): - m_IsUpdated (true), m_IsUnreachable (false), m_SupportedTransports (0), m_Caps (0) - { - m_Buffer = new uint8_t[MAX_RI_BUFFER_SIZE]; - memcpy (m_Buffer, buf, len); - m_BufferLen = len; - ReadFromBuffer (true); - } + RouterInfo::RouterInfo (const uint8_t * buf, int len): + m_IsUpdated (true), m_IsUnreachable (false), m_SupportedTransports (0), m_Caps (0) + { + m_Buffer = new uint8_t[MAX_RI_BUFFER_SIZE]; + memcpy (m_Buffer, buf, len); + m_BufferLen = len; + ReadFromBuffer (true); + } - RouterInfo::~RouterInfo () - { - delete[] m_Buffer; - } - - void RouterInfo::Update (const uint8_t * buf, int len) - { - if (!m_Buffer) - m_Buffer = new uint8_t[MAX_RI_BUFFER_SIZE]; - m_IsUpdated = true; - m_IsUnreachable = false; - m_SupportedTransports = 0; - m_Caps = 0; - m_Addresses.clear (); - m_Properties.clear (); - memcpy (m_Buffer, buf, len); - m_BufferLen = len; - ReadFromBuffer (true); - // don't delete buffer until save to file - } - - void RouterInfo::SetRouterIdentity (const IdentityEx& identity) - { - m_RouterIdentity = identity; - m_Timestamp = i2p::util::GetMillisecondsSinceEpoch (); - } - - bool RouterInfo::LoadFile () - { - std::ifstream s(m_FullPath.c_str (), std::ifstream::binary); - if (s.is_open ()) - { - s.seekg (0,std::ios::end); - m_BufferLen = s.tellg (); - if (m_BufferLen < 40) - { - LogPrint(eLogError, "File", m_FullPath, " is malformed"); - return false; - } - s.seekg(0, std::ios::beg); - if (!m_Buffer) - m_Buffer = new uint8_t[MAX_RI_BUFFER_SIZE]; - s.read((char *)m_Buffer, m_BufferLen); - } - else - { - LogPrint (eLogError, "Can't open file ", m_FullPath); - return false; - } - return true; - } + RouterInfo::~RouterInfo () + { + delete[] m_Buffer; + } + + void RouterInfo::Update (const uint8_t * buf, int len) + { + if (!m_Buffer) + m_Buffer = new uint8_t[MAX_RI_BUFFER_SIZE]; + m_IsUpdated = true; + m_IsUnreachable = false; + m_SupportedTransports = 0; + m_Caps = 0; + m_Addresses.clear (); + m_Properties.clear (); + memcpy (m_Buffer, buf, len); + m_BufferLen = len; + ReadFromBuffer (true); + // don't delete buffer until save to file + } + + void RouterInfo::SetRouterIdentity (const IdentityEx& identity) + { + m_RouterIdentity = identity; + m_Timestamp = i2p::util::GetMillisecondsSinceEpoch (); + } + + bool RouterInfo::LoadFile () + { + std::ifstream s(m_FullPath.c_str (), std::ifstream::binary); + if (s.is_open ()) + { + s.seekg (0,std::ios::end); + m_BufferLen = s.tellg (); + if (m_BufferLen < 40) + { + LogPrint(eLogError, "File", m_FullPath, " is malformed"); + return false; + } + s.seekg(0, std::ios::beg); + if (!m_Buffer) + m_Buffer = new uint8_t[MAX_RI_BUFFER_SIZE]; + s.read((char *)m_Buffer, m_BufferLen); + } + else + { + LogPrint (eLogError, "Can't open file ", m_FullPath); + return false; + } + return true; + } - void RouterInfo::ReadFromFile () - { - if (LoadFile ()) - ReadFromBuffer (false); - } + void RouterInfo::ReadFromFile () + { + if (LoadFile ()) + ReadFromBuffer (false); + } - void RouterInfo::ReadFromBuffer (bool verifySignature) - { - size_t identityLen = m_RouterIdentity.FromBuffer (m_Buffer, m_BufferLen); - std::stringstream str (std::string ((char *)m_Buffer + identityLen, m_BufferLen - identityLen)); - ReadFromStream (str); - if (verifySignature) - { - // verify signature - int l = m_BufferLen - m_RouterIdentity.GetSignatureLen (); - if (!m_RouterIdentity.Verify ((uint8_t *)m_Buffer, l, (uint8_t *)m_Buffer + l)) - { - LogPrint (eLogError, "signature verification failed"); - m_IsUnreachable = true; - } - m_RouterIdentity.DropVerifier (); - } - } - - void RouterInfo::ReadFromStream (std::istream& s) - { - s.read ((char *)&m_Timestamp, sizeof (m_Timestamp)); - m_Timestamp = be64toh (m_Timestamp); - // read addresses - uint8_t numAddresses; - s.read ((char *)&numAddresses, sizeof (numAddresses)); - bool introducers = false; - for (int i = 0; i < numAddresses; i++) - { - bool isValidAddress = true; - Address address; - s.read ((char *)&address.cost, sizeof (address.cost)); - s.read ((char *)&address.date, sizeof (address.date)); - char transportStyle[5]; - ReadString (transportStyle, s); - if (!strcmp (transportStyle, "NTCP")) - address.transportStyle = eTransportNTCP; - else if (!strcmp (transportStyle, "SSU")) - address.transportStyle = eTransportSSU; - else - address.transportStyle = eTransportUnknown; - address.port = 0; - address.mtu = 0; - uint16_t size, r = 0; - s.read ((char *)&size, sizeof (size)); - size = be16toh (size); - while (r < size) - { - char key[500], value[500]; - r += ReadString (key, s); - s.seekg (1, std::ios_base::cur); r++; // = - r += ReadString (value, s); - s.seekg (1, std::ios_base::cur); r++; // ; - if (!strcmp (key, "host")) - { - boost::system::error_code ecode; - address.host = boost::asio::ip::address::from_string (value, ecode); - if (ecode) - { - if (address.transportStyle == eTransportNTCP) - { - m_SupportedTransports |= eNTCPV4; // TODO: - address.addressString = value; - } - else - { - // TODO: resolve address for SSU - LogPrint (eLogWarning, "Unexpected SSU address ", value); - isValidAddress = false; - } - } - else - { - // add supported protocol - if (address.host.is_v4 ()) - m_SupportedTransports |= (address.transportStyle == eTransportNTCP) ? eNTCPV4 : eSSUV4; - else - m_SupportedTransports |= (address.transportStyle == eTransportNTCP) ? eNTCPV6 : eSSUV6; - } - } - else if (!strcmp (key, "port")) - address.port = boost::lexical_cast(value); - else if (!strcmp (key, "mtu")) - address.mtu = boost::lexical_cast(value); - else if (!strcmp (key, "key")) - Base64ToByteStream (value, strlen (value), address.key, 32); - else if (!strcmp (key, "caps")) - ExtractCaps (value); - else if (key[0] == 'i') - { - // introducers - introducers = true; - size_t l = strlen(key); - unsigned char index = key[l-1] - '0'; // TODO: - key[l-1] = 0; - if (index >= address.introducers.size ()) - address.introducers.resize (index + 1); - Introducer& introducer = address.introducers.at (index); - if (!strcmp (key, "ihost")) - { - boost::system::error_code ecode; - introducer.iHost = boost::asio::ip::address::from_string (value, ecode); - } - else if (!strcmp (key, "iport")) - introducer.iPort = boost::lexical_cast(value); - else if (!strcmp (key, "itag")) - introducer.iTag = boost::lexical_cast(value); - else if (!strcmp (key, "ikey")) - Base64ToByteStream (value, strlen (value), introducer.iKey, 32); - } - } - if (isValidAddress) - m_Addresses.push_back(address); - } - // read peers - uint8_t numPeers; - s.read ((char *)&numPeers, sizeof (numPeers)); - s.seekg (numPeers*32, std::ios_base::cur); // TODO: read peers - // read properties - uint16_t size, r = 0; - s.read ((char *)&size, sizeof (size)); - size = be16toh (size); - while (r < size) - { -#ifdef _WIN32 - char key[500], value[500]; - // TODO: investigate why properties get read as one long string under Windows - // length should not be more than 44 + void RouterInfo::ReadFromBuffer (bool verifySignature) + { + size_t identityLen = m_RouterIdentity.FromBuffer (m_Buffer, m_BufferLen); + std::stringstream str (std::string ((char *)m_Buffer + identityLen, m_BufferLen - identityLen)); + ReadFromStream (str); + if (verifySignature) + { + // verify signature + int l = m_BufferLen - m_RouterIdentity.GetSignatureLen (); + if (!m_RouterIdentity.Verify ((uint8_t *)m_Buffer, l, (uint8_t *)m_Buffer + l)) + { + LogPrint (eLogError, "signature verification failed"); + m_IsUnreachable = true; + } + m_RouterIdentity.DropVerifier (); + } + } + + void RouterInfo::ReadFromStream (std::istream& s) + { + s.read ((char *)&m_Timestamp, sizeof (m_Timestamp)); + m_Timestamp = be64toh (m_Timestamp); + // read addresses + uint8_t numAddresses; + s.read ((char *)&numAddresses, sizeof (numAddresses)); + bool introducers = false; + for (int i = 0; i < numAddresses; i++) + { + bool isValidAddress = true; + Address address; + s.read ((char *)&address.cost, sizeof (address.cost)); + s.read ((char *)&address.date, sizeof (address.date)); + char transportStyle[5]; + ReadString (transportStyle, s); + if (!strcmp (transportStyle, "NTCP")) + address.transportStyle = eTransportNTCP; + else if (!strcmp (transportStyle, "SSU")) + address.transportStyle = eTransportSSU; + else + address.transportStyle = eTransportUnknown; + address.port = 0; + address.mtu = 0; + uint16_t size, r = 0; + s.read ((char *)&size, sizeof (size)); + size = be16toh (size); + while (r < size) + { + char key[500], value[500]; + r += ReadString (key, s); + s.seekg (1, std::ios_base::cur); r++; // = + r += ReadString (value, s); + s.seekg (1, std::ios_base::cur); r++; // ; + if (!strcmp (key, "host")) + { + boost::system::error_code ecode; + address.host = boost::asio::ip::address::from_string (value, ecode); + if (ecode) + { + if (address.transportStyle == eTransportNTCP) + { + m_SupportedTransports |= eNTCPV4; // TODO: + address.addressString = value; + } + else + { + // TODO: resolve address for SSU + LogPrint (eLogWarning, "Unexpected SSU address ", value); + isValidAddress = false; + } + } + else + { + // add supported protocol + if (address.host.is_v4 ()) + m_SupportedTransports |= (address.transportStyle == eTransportNTCP) ? eNTCPV4 : eSSUV4; + else + m_SupportedTransports |= (address.transportStyle == eTransportNTCP) ? eNTCPV6 : eSSUV6; + } + } + else if (!strcmp (key, "port")) + address.port = boost::lexical_cast(value); + else if (!strcmp (key, "mtu")) + address.mtu = boost::lexical_cast(value); + else if (!strcmp (key, "key")) + Base64ToByteStream (value, strlen (value), address.key, 32); + else if (!strcmp (key, "caps")) + ExtractCaps (value); + else if (key[0] == 'i') + { + // introducers + introducers = true; + size_t l = strlen(key); + unsigned char index = key[l-1] - '0'; // TODO: + key[l-1] = 0; + if (index >= address.introducers.size ()) + address.introducers.resize (index + 1); + Introducer& introducer = address.introducers.at (index); + if (!strcmp (key, "ihost")) + { + boost::system::error_code ecode; + introducer.iHost = boost::asio::ip::address::from_string (value, ecode); + } + else if (!strcmp (key, "iport")) + introducer.iPort = boost::lexical_cast(value); + else if (!strcmp (key, "itag")) + introducer.iTag = boost::lexical_cast(value); + else if (!strcmp (key, "ikey")) + Base64ToByteStream (value, strlen (value), introducer.iKey, 32); + } + } + if (isValidAddress) + m_Addresses.push_back(address); + } + // read peers + uint8_t numPeers; + s.read ((char *)&numPeers, sizeof (numPeers)); + s.seekg (numPeers*32, std::ios_base::cur); // TODO: read peers + // read properties + uint16_t size, r = 0; + s.read ((char *)&size, sizeof (size)); + size = be16toh (size); + while (r < size) + { +#ifdef _WIN32 + char key[500], value[500]; + // TODO: investigate why properties get read as one long string under Windows + // length should not be more than 44 #else - char key[50], value[50]; -#endif - r += ReadString (key, s); - s.seekg (1, std::ios_base::cur); r++; // = - r += ReadString (value, s); - s.seekg (1, std::ios_base::cur); r++; // ; - m_Properties[key] = value; - - // extract caps - if (!strcmp (key, "caps")) - ExtractCaps (value); - } + char key[50], value[50]; +#endif + r += ReadString (key, s); + s.seekg (1, std::ios_base::cur); r++; // = + r += ReadString (value, s); + s.seekg (1, std::ios_base::cur); r++; // ; + m_Properties[key] = value; + + // extract caps + if (!strcmp (key, "caps")) + ExtractCaps (value); + } - if (!m_SupportedTransports || !m_Addresses.size() || (UsesIntroducer () && !introducers)) - SetUnreachable (true); - } + if (!m_SupportedTransports || !m_Addresses.size() || (UsesIntroducer () && !introducers)) + SetUnreachable (true); + } - void RouterInfo::ExtractCaps (const char * value) - { - const char * cap = value; - while (*cap) - { - switch (*cap) - { - case CAPS_FLAG_FLOODFILL: - m_Caps |= Caps::eFloodfill; - break; - case CAPS_FLAG_HIGH_BANDWIDTH1: - case CAPS_FLAG_HIGH_BANDWIDTH2: - case CAPS_FLAG_HIGH_BANDWIDTH3: - m_Caps |= Caps::eHighBandwidth; - break; - case CAPS_FLAG_HIDDEN: - m_Caps |= Caps::eHidden; - break; - case CAPS_FLAG_REACHABLE: - m_Caps |= Caps::eReachable; - break; - case CAPS_FLAG_UNREACHABLE: - m_Caps |= Caps::eUnreachable; - break; - case CAPS_FLAG_SSU_TESTING: - m_Caps |= Caps::eSSUTesting; - break; - case CAPS_FLAG_SSU_INTRODUCER: - m_Caps |= Caps::eSSUIntroducer; - break; - default: ; - } - cap++; - } - } + void RouterInfo::ExtractCaps (const char * value) + { + const char * cap = value; + while (*cap) + { + switch (*cap) + { + case CAPS_FLAG_FLOODFILL: + m_Caps |= Caps::eFloodfill; + break; + case CAPS_FLAG_HIGH_BANDWIDTH1: + case CAPS_FLAG_HIGH_BANDWIDTH2: + case CAPS_FLAG_HIGH_BANDWIDTH3: + m_Caps |= Caps::eHighBandwidth; + break; + case CAPS_FLAG_HIDDEN: + m_Caps |= Caps::eHidden; + break; + case CAPS_FLAG_REACHABLE: + m_Caps |= Caps::eReachable; + break; + case CAPS_FLAG_UNREACHABLE: + m_Caps |= Caps::eUnreachable; + break; + case CAPS_FLAG_SSU_TESTING: + m_Caps |= Caps::eSSUTesting; + break; + case CAPS_FLAG_SSU_INTRODUCER: + m_Caps |= Caps::eSSUIntroducer; + break; + default: ; + } + cap++; + } + } - void RouterInfo::UpdateCapsProperty () - { - std::string caps; - if (m_Caps & eFloodfill) - { - caps += CAPS_FLAG_HIGH_BANDWIDTH3; // highest bandwidth - caps += CAPS_FLAG_FLOODFILL; // floodfill - } - else - caps += (m_Caps & eHighBandwidth) ? CAPS_FLAG_HIGH_BANDWIDTH3 : CAPS_FLAG_LOW_BANDWIDTH2; // bandwidth - if (m_Caps & eHidden) caps += CAPS_FLAG_HIDDEN; // hidden - if (m_Caps & eReachable) caps += CAPS_FLAG_REACHABLE; // reachable - if (m_Caps & eUnreachable) caps += CAPS_FLAG_UNREACHABLE; // unreachable + void RouterInfo::UpdateCapsProperty () + { + std::string caps; + if (m_Caps & eFloodfill) + { + caps += CAPS_FLAG_HIGH_BANDWIDTH3; // highest bandwidth + caps += CAPS_FLAG_FLOODFILL; // floodfill + } + else + caps += (m_Caps & eHighBandwidth) ? CAPS_FLAG_HIGH_BANDWIDTH3 : CAPS_FLAG_LOW_BANDWIDTH2; // bandwidth + if (m_Caps & eHidden) caps += CAPS_FLAG_HIDDEN; // hidden + if (m_Caps & eReachable) caps += CAPS_FLAG_REACHABLE; // reachable + if (m_Caps & eUnreachable) caps += CAPS_FLAG_UNREACHABLE; // unreachable - SetProperty ("caps", caps); - } - - void RouterInfo::WriteToStream (std::ostream& s) - { - uint64_t ts = htobe64 (m_Timestamp); - s.write ((char *)&ts, sizeof (ts)); + SetProperty ("caps", caps); + } + + void RouterInfo::WriteToStream (std::ostream& s) + { + uint64_t ts = htobe64 (m_Timestamp); + s.write ((char *)&ts, sizeof (ts)); - // addresses - uint8_t numAddresses = m_Addresses.size (); - s.write ((char *)&numAddresses, sizeof (numAddresses)); - for (auto& address : m_Addresses) - { - s.write ((char *)&address.cost, sizeof (address.cost)); - s.write ((char *)&address.date, sizeof (address.date)); - std::stringstream properties; - if (address.transportStyle == eTransportNTCP) - WriteString ("NTCP", s); - else if (address.transportStyle == eTransportSSU) - { - WriteString ("SSU", s); - // caps - WriteString ("caps", properties); - properties << '='; - std::string caps; - if (IsPeerTesting ()) caps += CAPS_FLAG_SSU_TESTING; - if (IsIntroducer ()) caps += CAPS_FLAG_SSU_INTRODUCER; - WriteString (caps, properties); - properties << ';'; - } - else - WriteString ("", s); + // addresses + uint8_t numAddresses = m_Addresses.size (); + s.write ((char *)&numAddresses, sizeof (numAddresses)); + for (auto& address : m_Addresses) + { + s.write ((char *)&address.cost, sizeof (address.cost)); + s.write ((char *)&address.date, sizeof (address.date)); + std::stringstream properties; + if (address.transportStyle == eTransportNTCP) + WriteString ("NTCP", s); + else if (address.transportStyle == eTransportSSU) + { + WriteString ("SSU", s); + // caps + WriteString ("caps", properties); + properties << '='; + std::string caps; + if (IsPeerTesting ()) caps += CAPS_FLAG_SSU_TESTING; + if (IsIntroducer ()) caps += CAPS_FLAG_SSU_INTRODUCER; + WriteString (caps, properties); + properties << ';'; + } + else + WriteString ("", s); - WriteString ("host", properties); - properties << '='; - WriteString (address.host.to_string (), properties); - properties << ';'; - if (address.transportStyle == eTransportSSU) - { - // write introducers if any - if (address.introducers.size () > 0) - { - int i = 0; - for (auto introducer: address.introducers) - { - WriteString ("ihost" + boost::lexical_cast(i), properties); - properties << '='; - WriteString (introducer.iHost.to_string (), properties); - properties << ';'; - i++; - } - i = 0; - for (auto introducer: address.introducers) - { - WriteString ("ikey" + boost::lexical_cast(i), properties); - properties << '='; - char value[64]; - size_t l = ByteStreamToBase64 (introducer.iKey, 32, value, 64); - value[l] = 0; - WriteString (value, properties); - properties << ';'; - i++; - } - i = 0; - for (auto introducer: address.introducers) - { - WriteString ("iport" + boost::lexical_cast(i), properties); - properties << '='; - WriteString (boost::lexical_cast(introducer.iPort), properties); - properties << ';'; - i++; - } - i = 0; - for (auto introducer: address.introducers) - { - WriteString ("itag" + boost::lexical_cast(i), properties); - properties << '='; - WriteString (boost::lexical_cast(introducer.iTag), properties); - properties << ';'; - i++; - } - } - // write intro key - WriteString ("key", properties); - properties << '='; - char value[64]; - size_t l = ByteStreamToBase64 (address.key, 32, value, 64); - value[l] = 0; - WriteString (value, properties); - properties << ';'; - // write mtu - if (address.mtu) - { - WriteString ("mtu", properties); - properties << '='; - WriteString (boost::lexical_cast(address.mtu), properties); - properties << ';'; - } - } - WriteString ("port", properties); - properties << '='; - WriteString (boost::lexical_cast(address.port), properties); - properties << ';'; - - uint16_t size = htobe16 (properties.str ().size ()); - s.write ((char *)&size, sizeof (size)); - s.write (properties.str ().c_str (), properties.str ().size ()); - } + WriteString ("host", properties); + properties << '='; + WriteString (address.host.to_string (), properties); + properties << ';'; + if (address.transportStyle == eTransportSSU) + { + // write introducers if any + if (address.introducers.size () > 0) + { + int i = 0; + for (auto introducer: address.introducers) + { + WriteString ("ihost" + boost::lexical_cast(i), properties); + properties << '='; + WriteString (introducer.iHost.to_string (), properties); + properties << ';'; + i++; + } + i = 0; + for (auto introducer: address.introducers) + { + WriteString ("ikey" + boost::lexical_cast(i), properties); + properties << '='; + char value[64]; + size_t l = ByteStreamToBase64 (introducer.iKey, 32, value, 64); + value[l] = 0; + WriteString (value, properties); + properties << ';'; + i++; + } + i = 0; + for (auto introducer: address.introducers) + { + WriteString ("iport" + boost::lexical_cast(i), properties); + properties << '='; + WriteString (boost::lexical_cast(introducer.iPort), properties); + properties << ';'; + i++; + } + i = 0; + for (auto introducer: address.introducers) + { + WriteString ("itag" + boost::lexical_cast(i), properties); + properties << '='; + WriteString (boost::lexical_cast(introducer.iTag), properties); + properties << ';'; + i++; + } + } + // write intro key + WriteString ("key", properties); + properties << '='; + char value[64]; + size_t l = ByteStreamToBase64 (address.key, 32, value, 64); + value[l] = 0; + WriteString (value, properties); + properties << ';'; + // write mtu + if (address.mtu) + { + WriteString ("mtu", properties); + properties << '='; + WriteString (boost::lexical_cast(address.mtu), properties); + properties << ';'; + } + } + WriteString ("port", properties); + properties << '='; + WriteString (boost::lexical_cast(address.port), properties); + properties << ';'; + + uint16_t size = htobe16 (properties.str ().size ()); + s.write ((char *)&size, sizeof (size)); + s.write (properties.str ().c_str (), properties.str ().size ()); + } - // peers - uint8_t numPeers = 0; - s.write ((char *)&numPeers, sizeof (numPeers)); + // peers + uint8_t numPeers = 0; + s.write ((char *)&numPeers, sizeof (numPeers)); - // properties - std::stringstream properties; - for (auto& p : m_Properties) - { - WriteString (p.first, properties); - properties << '='; - WriteString (p.second, properties); - properties << ';'; - } - uint16_t size = htobe16 (properties.str ().size ()); - s.write ((char *)&size, sizeof (size)); - s.write (properties.str ().c_str (), properties.str ().size ()); - } + // properties + std::stringstream properties; + for (auto& p : m_Properties) + { + WriteString (p.first, properties); + properties << '='; + WriteString (p.second, properties); + properties << ';'; + } + uint16_t size = htobe16 (properties.str ().size ()); + s.write ((char *)&size, sizeof (size)); + s.write (properties.str ().c_str (), properties.str ().size ()); + } - const uint8_t * RouterInfo::LoadBuffer () - { - if (!m_Buffer) - { - if (LoadFile ()) - LogPrint ("Buffer for ", GetIdentHashAbbreviation (), " loaded from file"); - } - return m_Buffer; - } + const uint8_t * RouterInfo::LoadBuffer () + { + if (!m_Buffer) + { + if (LoadFile ()) + LogPrint ("Buffer for ", GetIdentHashAbbreviation (), " loaded from file"); + } + return m_Buffer; + } - void RouterInfo::CreateBuffer (const PrivateKeys& privateKeys) - { - m_Timestamp = i2p::util::GetMillisecondsSinceEpoch (); // refresh timstamp - std::stringstream s; - uint8_t ident[1024]; - auto identLen = privateKeys.GetPublic ().ToBuffer (ident, 1024); - s.write ((char *)ident, identLen); - WriteToStream (s); - m_BufferLen = s.str ().size (); - if (!m_Buffer) - m_Buffer = new uint8_t[MAX_RI_BUFFER_SIZE]; - memcpy (m_Buffer, s.str ().c_str (), m_BufferLen); - // signature - privateKeys.Sign ((uint8_t *)m_Buffer, m_BufferLen, (uint8_t *)m_Buffer + m_BufferLen); - m_BufferLen += privateKeys.GetPublic ().GetSignatureLen (); - } + void RouterInfo::CreateBuffer (const PrivateKeys& privateKeys) + { + m_Timestamp = i2p::util::GetMillisecondsSinceEpoch (); // refresh timstamp + std::stringstream s; + uint8_t ident[1024]; + auto identLen = privateKeys.GetPublic ().ToBuffer (ident, 1024); + s.write ((char *)ident, identLen); + WriteToStream (s); + m_BufferLen = s.str ().size (); + if (!m_Buffer) + m_Buffer = new uint8_t[MAX_RI_BUFFER_SIZE]; + memcpy (m_Buffer, s.str ().c_str (), m_BufferLen); + // signature + privateKeys.Sign ((uint8_t *)m_Buffer, m_BufferLen, (uint8_t *)m_Buffer + m_BufferLen); + m_BufferLen += privateKeys.GetPublic ().GetSignatureLen (); + } - void RouterInfo::SaveToFile (const std::string& fullPath) - { - m_FullPath = fullPath; - if (m_Buffer) - { - std::ofstream f (fullPath, std::ofstream::binary | std::ofstream::out); - if (f.is_open ()) - f.write ((char *)m_Buffer, m_BufferLen); - else - LogPrint(eLogError, "Can't save RouterInfo to ", fullPath); - } - else - LogPrint (eLogError, "Can't save RouterInfo m_Buffer==NULL"); - } - - size_t RouterInfo::ReadString (char * str, std::istream& s) - { - uint8_t len; - s.read ((char *)&len, 1); - s.read (str, len); - str[len] = 0; - return len+1; - } + void RouterInfo::SaveToFile (const std::string& fullPath) + { + m_FullPath = fullPath; + if (m_Buffer) + { + std::ofstream f (fullPath, std::ofstream::binary | std::ofstream::out); + if (f.is_open ()) + f.write ((char *)m_Buffer, m_BufferLen); + else + LogPrint(eLogError, "Can't save RouterInfo to ", fullPath); + } + else + LogPrint (eLogError, "Can't save RouterInfo m_Buffer==NULL"); + } + + size_t RouterInfo::ReadString (char * str, std::istream& s) + { + uint8_t len; + s.read ((char *)&len, 1); + s.read (str, len); + str[len] = 0; + return len+1; + } - void RouterInfo::WriteString (const std::string& str, std::ostream& s) - { - uint8_t len = str.size (); - s.write ((char *)&len, 1); - s.write (str.c_str (), len); - } + void RouterInfo::WriteString (const std::string& str, std::ostream& s) + { + uint8_t len = str.size (); + s.write ((char *)&len, 1); + s.write (str.c_str (), len); + } - void RouterInfo::AddNTCPAddress (const char * host, int port) - { - Address addr; - addr.host = boost::asio::ip::address::from_string (host); - addr.port = port; - addr.transportStyle = eTransportNTCP; - addr.cost = 2; - addr.date = 0; - addr.mtu = 0; - m_Addresses.push_back(addr); - m_SupportedTransports |= addr.host.is_v6 () ? eNTCPV6 : eNTCPV4; - } + void RouterInfo::AddNTCPAddress (const char * host, int port) + { + Address addr; + addr.host = boost::asio::ip::address::from_string (host); + addr.port = port; + addr.transportStyle = eTransportNTCP; + addr.cost = 2; + addr.date = 0; + addr.mtu = 0; + m_Addresses.push_back(addr); + m_SupportedTransports |= addr.host.is_v6 () ? eNTCPV6 : eNTCPV4; + } - void RouterInfo::AddSSUAddress (const char * host, int port, const uint8_t * key, int mtu) - { - Address addr; - addr.host = boost::asio::ip::address::from_string (host); - addr.port = port; - addr.transportStyle = eTransportSSU; - addr.cost = 10; // NTCP should have priority over SSU - addr.date = 0; - addr.mtu = mtu; - memcpy (addr.key, key, 32); - m_Addresses.push_back(addr); - m_SupportedTransports |= addr.host.is_v6 () ? eNTCPV6 : eSSUV4; - m_Caps |= eSSUTesting; - m_Caps |= eSSUIntroducer; - } + void RouterInfo::AddSSUAddress (const char * host, int port, const uint8_t * key, int mtu) + { + Address addr; + addr.host = boost::asio::ip::address::from_string (host); + addr.port = port; + addr.transportStyle = eTransportSSU; + addr.cost = 10; // NTCP should have priority over SSU + addr.date = 0; + addr.mtu = mtu; + memcpy (addr.key, key, 32); + m_Addresses.push_back(addr); + m_SupportedTransports |= addr.host.is_v6 () ? eNTCPV6 : eSSUV4; + m_Caps |= eSSUTesting; + m_Caps |= eSSUIntroducer; + } - bool RouterInfo::AddIntroducer (const Address * address, uint32_t tag) - { - for (auto& addr : m_Addresses) - { - if (addr.transportStyle == eTransportSSU && addr.host.is_v4 ()) - { - for (auto intro: addr.introducers) - if (intro.iTag == tag) return false; // already presented - Introducer x; - x.iHost = address->host; - x.iPort = address->port; - x.iTag = tag; - memcpy (x.iKey, address->key, 32); // TODO: replace to Tag<32> - addr.introducers.push_back (x); - return true; - } - } - return false; - } + bool RouterInfo::AddIntroducer (const Address * address, uint32_t tag) + { + for (auto& addr : m_Addresses) + { + if (addr.transportStyle == eTransportSSU && addr.host.is_v4 ()) + { + for (auto intro: addr.introducers) + if (intro.iTag == tag) return false; // already presented + Introducer x; + x.iHost = address->host; + x.iPort = address->port; + x.iTag = tag; + memcpy (x.iKey, address->key, 32); // TODO: replace to Tag<32> + addr.introducers.push_back (x); + return true; + } + } + return false; + } - bool RouterInfo::RemoveIntroducer (const boost::asio::ip::udp::endpoint& e) - { - for (auto& addr : m_Addresses) - { - if (addr.transportStyle == eTransportSSU && addr.host.is_v4 ()) - { - for (std::vector::iterator it = addr.introducers.begin (); it != addr.introducers.end (); it++) - if ( boost::asio::ip::udp::endpoint (it->iHost, it->iPort) == e) - { - addr.introducers.erase (it); - return true; - } - } - } - return false; - } + bool RouterInfo::RemoveIntroducer (const boost::asio::ip::udp::endpoint& e) + { + for (auto& addr : m_Addresses) + { + if (addr.transportStyle == eTransportSSU && addr.host.is_v4 ()) + { + for (std::vector::iterator it = addr.introducers.begin (); it != addr.introducers.end (); it++) + if ( boost::asio::ip::udp::endpoint (it->iHost, it->iPort) == e) + { + addr.introducers.erase (it); + return true; + } + } + } + return false; + } - void RouterInfo::SetCaps (uint8_t caps) - { - m_Caps = caps; - UpdateCapsProperty (); - } - - void RouterInfo::SetCaps (const char * caps) - { - SetProperty ("caps", caps); - m_Caps = 0; - ExtractCaps (caps); - } - - void RouterInfo::SetProperty (const std::string& key, const std::string& value) - { - m_Properties[key] = value; - } + void RouterInfo::SetCaps (uint8_t caps) + { + m_Caps = caps; + UpdateCapsProperty (); + } + + void RouterInfo::SetCaps (const char * caps) + { + SetProperty ("caps", caps); + m_Caps = 0; + ExtractCaps (caps); + } + + void RouterInfo::SetProperty (const std::string& key, const std::string& value) + { + m_Properties[key] = value; + } - void RouterInfo::DeleteProperty (const std::string& key) - { - m_Properties.erase (key); - } + void RouterInfo::DeleteProperty (const std::string& key) + { + m_Properties.erase (key); + } - bool RouterInfo::IsFloodfill () const - { - return m_Caps & Caps::eFloodfill; - } + bool RouterInfo::IsFloodfill () const + { + return m_Caps & Caps::eFloodfill; + } - bool RouterInfo::IsNTCP (bool v4only) const - { - if (v4only) - return m_SupportedTransports & eNTCPV4; - else - return m_SupportedTransports & (eNTCPV4 | eNTCPV6); - } + bool RouterInfo::IsNTCP (bool v4only) const + { + if (v4only) + return m_SupportedTransports & eNTCPV4; + else + return m_SupportedTransports & (eNTCPV4 | eNTCPV6); + } - bool RouterInfo::IsSSU (bool v4only) const - { - if (v4only) - return m_SupportedTransports & eSSUV4; - else - return m_SupportedTransports & (eSSUV4 | eSSUV6); - } + bool RouterInfo::IsSSU (bool v4only) const + { + if (v4only) + return m_SupportedTransports & eSSUV4; + else + return m_SupportedTransports & (eSSUV4 | eSSUV6); + } - bool RouterInfo::IsV6 () const - { - return m_SupportedTransports & (eNTCPV6 | eSSUV6); - } + bool RouterInfo::IsV6 () const + { + return m_SupportedTransports & (eNTCPV6 | eSSUV6); + } - void RouterInfo::EnableV6 () - { - if (!IsV6 ()) - m_SupportedTransports |= eNTCPV6 | eSSUV6; - } - - void RouterInfo::DisableV6 () - { - if (IsV6 ()) - { - // NTCP - m_SupportedTransports &= ~eNTCPV6; - for (size_t i = 0; i < m_Addresses.size (); i++) - { - if (m_Addresses[i].transportStyle == i2p::data::RouterInfo::eTransportNTCP && - m_Addresses[i].host.is_v6 ()) - { - m_Addresses.erase (m_Addresses.begin () + i); - break; - } - } - - // 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; - } - } - } - } - - bool RouterInfo::UsesIntroducer () const - { - return m_Caps & Caps::eUnreachable; // non-reachable - } - - const RouterInfo::Address * RouterInfo::GetNTCPAddress (bool v4only) const - { - return GetAddress (eTransportNTCP, v4only); - } + void RouterInfo::EnableV6 () + { + if (!IsV6 ()) + m_SupportedTransports |= eNTCPV6 | eSSUV6; + } + + void RouterInfo::DisableV6 () + { + if (IsV6 ()) + { + // NTCP + m_SupportedTransports &= ~eNTCPV6; + for (size_t i = 0; i < m_Addresses.size (); i++) + { + if (m_Addresses[i].transportStyle == i2p::data::RouterInfo::eTransportNTCP && + m_Addresses[i].host.is_v6 ()) + { + m_Addresses.erase (m_Addresses.begin () + i); + break; + } + } + + // 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; + } + } + } + } + + bool RouterInfo::UsesIntroducer () const + { + return m_Caps & Caps::eUnreachable; // non-reachable + } + + const RouterInfo::Address * RouterInfo::GetNTCPAddress (bool v4only) const + { + return GetAddress (eTransportNTCP, v4only); + } - const RouterInfo::Address * RouterInfo::GetSSUAddress (bool v4only) const - { - return GetAddress (eTransportSSU, v4only); - } + const RouterInfo::Address * RouterInfo::GetSSUAddress (bool v4only) const + { + return GetAddress (eTransportSSU, v4only); + } - const RouterInfo::Address * RouterInfo::GetSSUV6Address () const - { - return GetAddress (eTransportSSU, false, true); - } - - const RouterInfo::Address * RouterInfo::GetAddress (TransportStyle s, bool v4only, bool v6only) const - { - for (auto& address : m_Addresses) - { - if (address.transportStyle == s) - { - if ((!v4only || address.host.is_v4 ()) && (!v6only || address.host.is_v6 ())) - return &address; - } - } - return nullptr; - } + const RouterInfo::Address * RouterInfo::GetSSUV6Address () const + { + return GetAddress (eTransportSSU, false, true); + } + + const RouterInfo::Address * RouterInfo::GetAddress (TransportStyle s, bool v4only, bool v6only) const + { + for (auto& address : m_Addresses) + { + if (address.transportStyle == s) + { + if ((!v4only || address.host.is_v4 ()) && (!v6only || address.host.is_v6 ())) + return &address; + } + } + return nullptr; + } - std::shared_ptr RouterInfo::GetProfile () const - { - if (!m_Profile) - m_Profile = GetRouterProfile (GetIdentHash ()); - return m_Profile; - } + std::shared_ptr RouterInfo::GetProfile () const + { + if (!m_Profile) + m_Profile = GetRouterProfile (GetIdentHash ()); + return m_Profile; + } } } diff --git a/RouterInfo.h b/RouterInfo.h index 1c5af450..5bcb95e9 100644 --- a/RouterInfo.h +++ b/RouterInfo.h @@ -14,170 +14,170 @@ namespace i2p { namespace data { - const char CAPS_FLAG_FLOODFILL = 'f'; - const char CAPS_FLAG_HIDDEN = 'H'; - const char CAPS_FLAG_REACHABLE = 'R'; - const char CAPS_FLAG_UNREACHABLE = 'U'; - const char CAPS_FLAG_LOW_BANDWIDTH1 = 'K'; - const char CAPS_FLAG_LOW_BANDWIDTH2 = 'L'; - const char CAPS_FLAG_HIGH_BANDWIDTH1 = 'M'; - const char CAPS_FLAG_HIGH_BANDWIDTH2 = 'N'; - const char CAPS_FLAG_HIGH_BANDWIDTH3 = 'O'; + const char CAPS_FLAG_FLOODFILL = 'f'; + const char CAPS_FLAG_HIDDEN = 'H'; + const char CAPS_FLAG_REACHABLE = 'R'; + const char CAPS_FLAG_UNREACHABLE = 'U'; + const char CAPS_FLAG_LOW_BANDWIDTH1 = 'K'; + const char CAPS_FLAG_LOW_BANDWIDTH2 = 'L'; + const char CAPS_FLAG_HIGH_BANDWIDTH1 = 'M'; + const char CAPS_FLAG_HIGH_BANDWIDTH2 = 'N'; + const char CAPS_FLAG_HIGH_BANDWIDTH3 = 'O'; - const char CAPS_FLAG_SSU_TESTING = 'B'; - const char CAPS_FLAG_SSU_INTRODUCER = 'C'; + const char CAPS_FLAG_SSU_TESTING = 'B'; + const char CAPS_FLAG_SSU_INTRODUCER = 'C'; - const int MAX_RI_BUFFER_SIZE = 2048; - class RouterInfo: public RoutingDestination - { - public: + const int MAX_RI_BUFFER_SIZE = 2048; + class RouterInfo: public RoutingDestination + { + public: - enum SupportedTranports - { - eNTCPV4 = 0x01, - eNTCPV6 = 0x02, - eSSUV4 = 0x04, - eSSUV6 = 0x08 - }; - - enum Caps - { - eFloodfill = 0x01, - eHighBandwidth = 0x02, - eReachable = 0x04, - eSSUTesting = 0x08, - eSSUIntroducer = 0x10, - eHidden = 0x20, - eUnreachable = 0x40 - }; + enum SupportedTranports + { + eNTCPV4 = 0x01, + eNTCPV6 = 0x02, + eSSUV4 = 0x04, + eSSUV6 = 0x08 + }; + + enum Caps + { + eFloodfill = 0x01, + eHighBandwidth = 0x02, + eReachable = 0x04, + eSSUTesting = 0x08, + eSSUIntroducer = 0x10, + eHidden = 0x20, + eUnreachable = 0x40 + }; - enum TransportStyle - { - eTransportUnknown = 0, - eTransportNTCP, - eTransportSSU - }; + enum TransportStyle + { + eTransportUnknown = 0, + eTransportNTCP, + eTransportSSU + }; - struct Introducer - { - boost::asio::ip::address iHost; - int iPort; - Tag<32> iKey; - uint32_t iTag; - }; + struct Introducer + { + boost::asio::ip::address iHost; + int iPort; + Tag<32> iKey; + uint32_t iTag; + }; - struct Address - { - TransportStyle transportStyle; - boost::asio::ip::address host; - std::string addressString; - int port, mtu; - uint64_t date; - uint8_t cost; - // SSU only - Tag<32> key; // intro key for SSU - std::vector introducers; + struct Address + { + TransportStyle transportStyle; + boost::asio::ip::address host; + std::string addressString; + int port, mtu; + uint64_t date; + uint8_t cost; + // SSU only + Tag<32> key; // intro key for SSU + std::vector introducers; - bool IsCompatible (const boost::asio::ip::address& other) const - { - return (host.is_v4 () && other.is_v4 ()) || - (host.is_v6 () && other.is_v6 ()); - } - }; - - RouterInfo (const std::string& fullPath); - RouterInfo (): m_Buffer (nullptr) { }; + bool IsCompatible (const boost::asio::ip::address& other) const + { + return (host.is_v4 () && other.is_v4 ()) || + (host.is_v6 () && other.is_v6 ()); + } + }; + + RouterInfo (const std::string& fullPath); + RouterInfo (): m_Buffer (nullptr) { }; - RouterInfo (const RouterInfo& ) = default; - RouterInfo& operator=(const RouterInfo& ) = default; - RouterInfo (const uint8_t * buf, int len); - ~RouterInfo (); - - const IdentityEx& GetRouterIdentity () const { return m_RouterIdentity; }; - void SetRouterIdentity (const IdentityEx& identity); - std::string GetIdentHashBase64 () const { return GetIdentHash ().ToBase64 (); }; - std::string GetIdentHashAbbreviation () const { return GetIdentHash ().ToBase64 ().substr (0, 4); }; - uint64_t GetTimestamp () const { return m_Timestamp; }; - std::vector
& GetAddresses () { return m_Addresses; }; - const Address * GetNTCPAddress (bool v4only = true) const; - const Address * GetSSUAddress (bool v4only = true) const; - const Address * GetSSUV6Address () const; - - void AddNTCPAddress (const char * host, int port); - void AddSSUAddress (const char * host, int port, const uint8_t * key, int mtu = 0); - bool AddIntroducer (const Address * address, uint32_t tag); - bool RemoveIntroducer (const boost::asio::ip::udp::endpoint& e); - void SetProperty (const std::string& key, const std::string& value); // called from RouterContext only - void DeleteProperty (const std::string& key); // called from RouterContext only - void ClearProperties () { m_Properties.clear (); }; - bool IsFloodfill () const; - bool IsNTCP (bool v4only = true) const; - bool IsSSU (bool v4only = true) const; - bool IsV6 () const; - void EnableV6 (); - void DisableV6 (); - bool IsCompatible (const RouterInfo& other) const { return m_SupportedTransports & other.m_SupportedTransports; }; - bool UsesIntroducer () const; - bool IsIntroducer () const { return m_Caps & eSSUIntroducer; }; - bool IsPeerTesting () const { return m_Caps & eSSUTesting; }; - bool IsHidden () const { return m_Caps & eHidden; }; - bool IsHighBandwidth () const { return m_Caps & RouterInfo::eHighBandwidth; }; + RouterInfo (const RouterInfo& ) = default; + RouterInfo& operator=(const RouterInfo& ) = default; + RouterInfo (const uint8_t * buf, int len); + ~RouterInfo (); + + const IdentityEx& GetRouterIdentity () const { return m_RouterIdentity; }; + void SetRouterIdentity (const IdentityEx& identity); + std::string GetIdentHashBase64 () const { return GetIdentHash ().ToBase64 (); }; + std::string GetIdentHashAbbreviation () const { return GetIdentHash ().ToBase64 ().substr (0, 4); }; + uint64_t GetTimestamp () const { return m_Timestamp; }; + std::vector
& GetAddresses () { return m_Addresses; }; + const Address * GetNTCPAddress (bool v4only = true) const; + const Address * GetSSUAddress (bool v4only = true) const; + const Address * GetSSUV6Address () const; + + void AddNTCPAddress (const char * host, int port); + void AddSSUAddress (const char * host, int port, const uint8_t * key, int mtu = 0); + bool AddIntroducer (const Address * address, uint32_t tag); + bool RemoveIntroducer (const boost::asio::ip::udp::endpoint& e); + void SetProperty (const std::string& key, const std::string& value); // called from RouterContext only + void DeleteProperty (const std::string& key); // called from RouterContext only + void ClearProperties () { m_Properties.clear (); }; + bool IsFloodfill () const; + bool IsNTCP (bool v4only = true) const; + bool IsSSU (bool v4only = true) const; + bool IsV6 () const; + void EnableV6 (); + void DisableV6 (); + bool IsCompatible (const RouterInfo& other) const { return m_SupportedTransports & other.m_SupportedTransports; }; + bool UsesIntroducer () const; + bool IsIntroducer () const { return m_Caps & eSSUIntroducer; }; + bool IsPeerTesting () const { return m_Caps & eSSUTesting; }; + bool IsHidden () const { return m_Caps & eHidden; }; + bool IsHighBandwidth () const { return m_Caps & RouterInfo::eHighBandwidth; }; - uint8_t GetCaps () const { return m_Caps; }; - void SetCaps (uint8_t caps); - void SetCaps (const char * caps); + uint8_t GetCaps () const { return m_Caps; }; + void SetCaps (uint8_t caps); + void SetCaps (const char * caps); - void SetUnreachable (bool unreachable) { m_IsUnreachable = unreachable; }; - bool IsUnreachable () const { return m_IsUnreachable; }; + void SetUnreachable (bool unreachable) { m_IsUnreachable = unreachable; }; + bool IsUnreachable () const { return m_IsUnreachable; }; - const uint8_t * GetBuffer () const { return m_Buffer; }; - const uint8_t * LoadBuffer (); // load if necessary - int GetBufferLen () const { return m_BufferLen; }; - void CreateBuffer (const PrivateKeys& privateKeys); + const uint8_t * GetBuffer () const { return m_Buffer; }; + const uint8_t * LoadBuffer (); // load if necessary + int GetBufferLen () const { return m_BufferLen; }; + void CreateBuffer (const PrivateKeys& privateKeys); - bool IsUpdated () const { return m_IsUpdated; }; - void SetUpdated (bool updated) { m_IsUpdated = updated; }; - void SaveToFile (const std::string& fullPath); + bool IsUpdated () const { return m_IsUpdated; }; + void SetUpdated (bool updated) { m_IsUpdated = updated; }; + void SaveToFile (const std::string& fullPath); - std::shared_ptr GetProfile () const; - void SaveProfile () { if (m_Profile) m_Profile->Save (); }; - - void Update (const uint8_t * buf, int len); - void DeleteBuffer () { delete[] m_Buffer; m_Buffer = nullptr; }; - - // implements RoutingDestination - const IdentHash& GetIdentHash () const { return m_RouterIdentity.GetIdentHash (); }; - const uint8_t * GetEncryptionPublicKey () const { return m_RouterIdentity.GetStandardIdentity ().publicKey; }; - bool IsDestination () const { return false; }; + std::shared_ptr GetProfile () const; + void SaveProfile () { if (m_Profile) m_Profile->Save (); }; + + void Update (const uint8_t * buf, int len); + void DeleteBuffer () { delete[] m_Buffer; m_Buffer = nullptr; }; + + // implements RoutingDestination + const IdentHash& GetIdentHash () const { return m_RouterIdentity.GetIdentHash (); }; + const uint8_t * GetEncryptionPublicKey () const { return m_RouterIdentity.GetStandardIdentity ().publicKey; }; + bool IsDestination () const { return false; }; - - private: + + private: - bool LoadFile (); - void ReadFromFile (); - void ReadFromStream (std::istream& s); - void ReadFromBuffer (bool verifySignature); - void WriteToStream (std::ostream& s); - size_t ReadString (char * str, std::istream& s); - void WriteString (const std::string& str, std::ostream& s); - void ExtractCaps (const char * value); - const Address * GetAddress (TransportStyle s, bool v4only, bool v6only = false) const; - void UpdateCapsProperty (); + bool LoadFile (); + void ReadFromFile (); + void ReadFromStream (std::istream& s); + void ReadFromBuffer (bool verifySignature); + void WriteToStream (std::ostream& s); + size_t ReadString (char * str, std::istream& s); + void WriteString (const std::string& str, std::ostream& s); + void ExtractCaps (const char * value); + const Address * GetAddress (TransportStyle s, bool v4only, bool v6only = false) const; + void UpdateCapsProperty (); - private: + private: - std::string m_FullPath; - IdentityEx m_RouterIdentity; - uint8_t * m_Buffer; - int m_BufferLen; - uint64_t m_Timestamp; - std::vector
m_Addresses; - std::map m_Properties; - bool m_IsUpdated, m_IsUnreachable; - uint8_t m_SupportedTransports, m_Caps; - mutable std::shared_ptr m_Profile; - }; -} + std::string m_FullPath; + IdentityEx m_RouterIdentity; + uint8_t * m_Buffer; + int m_BufferLen; + uint64_t m_Timestamp; + std::vector
m_Addresses; + std::map m_Properties; + bool m_IsUpdated, m_IsUnreachable; + uint8_t m_SupportedTransports, m_Caps; + mutable std::shared_ptr m_Profile; + }; +} } #endif diff --git a/SAM.cpp b/SAM.cpp index 7db1e1bb..d8ed184f 100644 --- a/SAM.cpp +++ b/SAM.cpp @@ -15,857 +15,857 @@ namespace i2p { namespace client { - SAMSocket::SAMSocket (SAMBridge& owner): - m_Owner (owner), m_Socket (m_Owner.GetService ()), m_Timer (m_Owner.GetService ()), - m_BufferOffset (0), m_SocketType (eSAMSocketTypeUnknown), m_IsSilent (false), - m_Stream (nullptr), m_Session (nullptr) - { - } + SAMSocket::SAMSocket (SAMBridge& owner): + m_Owner (owner), m_Socket (m_Owner.GetService ()), m_Timer (m_Owner.GetService ()), + m_BufferOffset (0), m_SocketType (eSAMSocketTypeUnknown), m_IsSilent (false), + m_Stream (nullptr), m_Session (nullptr) + { + } - SAMSocket::~SAMSocket () - { - Terminate (); - } + SAMSocket::~SAMSocket () + { + Terminate (); + } - void SAMSocket::CloseStream () - { - if (m_Stream) - { - m_Stream->Close (); - m_Stream.reset (); - } - } - - void SAMSocket::Terminate () - { - CloseStream (); - - switch (m_SocketType) - { - case eSAMSocketTypeSession: - m_Owner.CloseSession (m_ID); - break; - case eSAMSocketTypeStream: - { - if (m_Session) - m_Session->sockets.remove (shared_from_this ()); - break; - } - case eSAMSocketTypeAcceptor: - { - if (m_Session) - { - m_Session->sockets.remove (shared_from_this ()); - m_Session->localDestination->StopAcceptingStreams (); - } - break; - } - default: - ; - } - m_SocketType = eSAMSocketTypeTerminated; - m_Socket.close (); - } - - void SAMSocket::ReceiveHandshake () - { - m_Socket.async_read_some (boost::asio::buffer(m_Buffer, SAM_SOCKET_BUFFER_SIZE), - std::bind(&SAMSocket::HandleHandshakeReceived, shared_from_this (), - std::placeholders::_1, std::placeholders::_2)); - } - - void SAMSocket::HandleHandshakeReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred) - { - if (ecode) + void SAMSocket::CloseStream () + { + if (m_Stream) + { + m_Stream->Close (); + m_Stream.reset (); + } + } + + void SAMSocket::Terminate () + { + CloseStream (); + + switch (m_SocketType) { - LogPrint ("SAM handshake read error: ", ecode.message ()); - if (ecode != boost::asio::error::operation_aborted) - Terminate (); - } - else - { - m_Buffer[bytes_transferred] = 0; - char * eol = (char *)memchr (m_Buffer, '\n', bytes_transferred); - if (eol) - *eol = 0; - LogPrint ("SAM handshake ", m_Buffer); - char * separator = strchr (m_Buffer, ' '); - if (separator) - { - separator = strchr (separator + 1, ' '); - if (separator) - *separator = 0; - } + case eSAMSocketTypeSession: + m_Owner.CloseSession (m_ID); + break; + case eSAMSocketTypeStream: + { + if (m_Session) + m_Session->sockets.remove (shared_from_this ()); + break; + } + case eSAMSocketTypeAcceptor: + { + if (m_Session) + { + m_Session->sockets.remove (shared_from_this ()); + m_Session->localDestination->StopAcceptingStreams (); + } + break; + } + default: + ; + } + m_SocketType = eSAMSocketTypeTerminated; + m_Socket.close (); + } - if (!strcmp (m_Buffer, SAM_HANDSHAKE)) - { - std::string version("3.0"); - // try to find MIN and MAX, 3.0 if not found - if (separator) - { - separator++; - std::map params; - ExtractParams (separator, params); - auto it = params.find (SAM_PARAM_MAX); - // TODO: check MIN as well - if (it != params.end ()) - version = it->second; - } - if (version[0] == '3') // we support v3 (3.0 and 3.1) only - { -#ifdef _MSC_VER - size_t l = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_HANDSHAKE_REPLY, version.c_str ()); -#else - size_t l = snprintf (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_HANDSHAKE_REPLY, version.c_str ()); -#endif - boost::asio::async_write (m_Socket, boost::asio::buffer (m_Buffer, l), boost::asio::transfer_all (), - std::bind(&SAMSocket::HandleHandshakeReplySent, shared_from_this (), - std::placeholders::_1, std::placeholders::_2)); - } - else - SendMessageReply (SAM_HANDSHAKE_I2P_ERROR, strlen (SAM_HANDSHAKE_I2P_ERROR), true); - } - else - { - LogPrint ("SAM handshake mismatch"); - Terminate (); - } - } - } + void SAMSocket::ReceiveHandshake () + { + m_Socket.async_read_some (boost::asio::buffer(m_Buffer, SAM_SOCKET_BUFFER_SIZE), + std::bind(&SAMSocket::HandleHandshakeReceived, shared_from_this (), + std::placeholders::_1, std::placeholders::_2)); + } - void SAMSocket::HandleHandshakeReplySent (const boost::system::error_code& ecode, std::size_t bytes_transferred) - { - if (ecode) + void SAMSocket::HandleHandshakeReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred) + { + if (ecode) { - LogPrint ("SAM handshake reply send error: ", ecode.message ()); - if (ecode != boost::asio::error::operation_aborted) - Terminate (); - } - else - { - m_Socket.async_read_some (boost::asio::buffer(m_Buffer, SAM_SOCKET_BUFFER_SIZE), - std::bind(&SAMSocket::HandleMessage, shared_from_this (), - std::placeholders::_1, std::placeholders::_2)); - } - } + LogPrint ("SAM handshake read error: ", ecode.message ()); + if (ecode != boost::asio::error::operation_aborted) + Terminate (); + } + else + { + m_Buffer[bytes_transferred] = 0; + char * eol = (char *)memchr (m_Buffer, '\n', bytes_transferred); + if (eol) + *eol = 0; + LogPrint ("SAM handshake ", m_Buffer); + char * separator = strchr (m_Buffer, ' '); + if (separator) + { + separator = strchr (separator + 1, ' '); + if (separator) + *separator = 0; + } - void SAMSocket::SendMessageReply (const char * msg, size_t len, bool close) - { - if (!m_IsSilent) - boost::asio::async_write (m_Socket, boost::asio::buffer (msg, len), boost::asio::transfer_all (), - std::bind(&SAMSocket::HandleMessageReplySent, shared_from_this (), - std::placeholders::_1, std::placeholders::_2, close)); - else - { - if (close) - Terminate (); - else - Receive (); - } - } + if (!strcmp (m_Buffer, SAM_HANDSHAKE)) + { + std::string version("3.0"); + // try to find MIN and MAX, 3.0 if not found + if (separator) + { + separator++; + std::map params; + ExtractParams (separator, params); + auto it = params.find (SAM_PARAM_MAX); + // TODO: check MIN as well + if (it != params.end ()) + version = it->second; + } + if (version[0] == '3') // we support v3 (3.0 and 3.1) only + { +#ifdef _MSC_VER + size_t l = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_HANDSHAKE_REPLY, version.c_str ()); +#else + size_t l = snprintf (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_HANDSHAKE_REPLY, version.c_str ()); +#endif + boost::asio::async_write (m_Socket, boost::asio::buffer (m_Buffer, l), boost::asio::transfer_all (), + std::bind(&SAMSocket::HandleHandshakeReplySent, shared_from_this (), + std::placeholders::_1, std::placeholders::_2)); + } + else + SendMessageReply (SAM_HANDSHAKE_I2P_ERROR, strlen (SAM_HANDSHAKE_I2P_ERROR), true); + } + else + { + LogPrint ("SAM handshake mismatch"); + Terminate (); + } + } + } - void SAMSocket::HandleMessageReplySent (const boost::system::error_code& ecode, std::size_t bytes_transferred, bool close) - { - if (ecode) + void SAMSocket::HandleHandshakeReplySent (const boost::system::error_code& ecode, std::size_t bytes_transferred) + { + if (ecode) { - LogPrint ("SAM reply send error: ", ecode.message ()); - if (ecode != boost::asio::error::operation_aborted) - Terminate (); - } - else - { - if (close) - Terminate (); - else - Receive (); - } - } - - void SAMSocket::HandleMessage (const boost::system::error_code& ecode, std::size_t bytes_transferred) - { - if (ecode) + LogPrint ("SAM handshake reply send error: ", ecode.message ()); + if (ecode != boost::asio::error::operation_aborted) + Terminate (); + } + else { - LogPrint ("SAM read error: ", ecode.message ()); - if (ecode != boost::asio::error::operation_aborted) - Terminate (); - } - else - { - bytes_transferred += m_BufferOffset; - m_BufferOffset = 0; - m_Buffer[bytes_transferred] = 0; - char * eol = (char *)memchr (m_Buffer, '\n', bytes_transferred); - if (eol) - { - *eol = 0; - char * separator = strchr (m_Buffer, ' '); - if (separator) - { - separator = strchr (separator + 1, ' '); - if (separator) - *separator = 0; - else - separator = eol; + m_Socket.async_read_some (boost::asio::buffer(m_Buffer, SAM_SOCKET_BUFFER_SIZE), + std::bind(&SAMSocket::HandleMessage, shared_from_this (), + std::placeholders::_1, std::placeholders::_2)); + } + } - if (!strcmp (m_Buffer, SAM_SESSION_CREATE)) - ProcessSessionCreate (separator + 1, bytes_transferred - (separator - m_Buffer) - 1); - else if (!strcmp (m_Buffer, SAM_STREAM_CONNECT)) - ProcessStreamConnect (separator + 1, bytes_transferred - (separator - m_Buffer) - 1); - else if (!strcmp (m_Buffer, SAM_STREAM_ACCEPT)) - ProcessStreamAccept (separator + 1, bytes_transferred - (separator - m_Buffer) - 1); - else if (!strcmp (m_Buffer, SAM_DEST_GENERATE)) - ProcessDestGenerate (); - else if (!strcmp (m_Buffer, SAM_NAMING_LOOKUP)) - ProcessNamingLookup (separator + 1, bytes_transferred - (separator - m_Buffer) - 1); - else if (!strcmp (m_Buffer, SAM_DATAGRAM_SEND)) - { - size_t len = bytes_transferred - (separator - m_Buffer) - 1; - size_t processed = ProcessDatagramSend (separator + 1, len, eol + 1); - if (processed < len) - { - m_BufferOffset = len - processed; - if (processed > 0) - memmove (m_Buffer, separator + 1 + processed, m_BufferOffset); - else - { - // restore string back - *separator = ' '; - *eol = '\n'; - } - } - // since it's SAM v1 reply is not expected - Receive (); - } - else - { - LogPrint (eLogError, "SAM unexpected message ", m_Buffer); - Terminate (); - } - } - else - { - LogPrint (eLogError, "SAM malformed message ", m_Buffer); - Terminate (); - } - } - else - { - LogPrint (eLogWarning, "SAM incomplete message ", bytes_transferred); - m_BufferOffset = bytes_transferred; - // try to receive remaining message - Receive (); - } - } - } - - void SAMSocket::ProcessSessionCreate (char * buf, size_t len) - { - LogPrint ("SAM session create: ", buf); - std::map params; - ExtractParams (buf, params); - std::string& style = params[SAM_PARAM_STYLE]; - std::string& id = params[SAM_PARAM_ID]; - std::string& destination = params[SAM_PARAM_DESTINATION]; - m_ID = id; - if (m_Owner.FindSession (id)) - { - // session exists - SendMessageReply (SAM_SESSION_CREATE_DUPLICATED_ID, strlen(SAM_SESSION_CREATE_DUPLICATED_ID), true); - return; - } - - // create destination - m_Session = m_Owner.CreateSession (id, destination == SAM_VALUE_TRANSIENT ? "" : destination, ¶ms); - if (m_Session) - { - m_SocketType = eSAMSocketTypeSession; - if (style == SAM_VALUE_DATAGRAM) - { - auto dest = m_Session->localDestination->CreateDatagramDestination (); - dest->SetReceiver (std::bind (&SAMSocket::HandleI2PDatagramReceive, shared_from_this (), - std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5)); - } - - if (m_Session->localDestination->IsReady ()) - SendSessionCreateReplyOk (); - else - { - m_Timer.expires_from_now (boost::posix_time::seconds(SAM_SESSION_READINESS_CHECK_INTERVAL)); - m_Timer.async_wait (std::bind (&SAMSocket::HandleSessionReadinessCheckTimer, - shared_from_this (), std::placeholders::_1)); - } - } - else - SendMessageReply (SAM_SESSION_CREATE_DUPLICATED_DEST, strlen(SAM_SESSION_CREATE_DUPLICATED_DEST), true); - } - - void SAMSocket::HandleSessionReadinessCheckTimer (const boost::system::error_code& ecode) - { - if (ecode != boost::asio::error::operation_aborted) - { - if (m_Session->localDestination->IsReady ()) - SendSessionCreateReplyOk (); - else - { - m_Timer.expires_from_now (boost::posix_time::seconds(SAM_SESSION_READINESS_CHECK_INTERVAL)); - m_Timer.async_wait (std::bind (&SAMSocket::HandleSessionReadinessCheckTimer, - shared_from_this (), std::placeholders::_1)); - } - } - } - - void SAMSocket::SendSessionCreateReplyOk () - { - uint8_t buf[1024]; - char priv[1024]; - size_t l = m_Session->localDestination->GetPrivateKeys ().ToBuffer (buf, 1024); - size_t l1 = i2p::data::ByteStreamToBase64 (buf, l, priv, 1024); - priv[l1] = 0; -#ifdef _MSC_VER - size_t l2 = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_SESSION_CREATE_REPLY_OK, priv); -#else - size_t l2 = snprintf (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_SESSION_CREATE_REPLY_OK, priv); -#endif - SendMessageReply (m_Buffer, l2, false); - } - - void SAMSocket::ProcessStreamConnect (char * buf, size_t len) - { - LogPrint (eLogDebug, "SAM stream connect: ", buf); - std::map params; - ExtractParams (buf, params); - std::string& id = params[SAM_PARAM_ID]; - std::string& destination = params[SAM_PARAM_DESTINATION]; - std::string& silent = params[SAM_PARAM_SILENT]; - if (silent == SAM_VALUE_TRUE) m_IsSilent = true; - m_ID = id; - m_Session = m_Owner.FindSession (id); - if (m_Session) - { - i2p::data::IdentityEx dest; - size_t len = dest.FromBase64(destination); - if (len > 0) - { - context.GetAddressBook().InsertAddress(dest); - auto leaseSet = m_Session->localDestination->FindLeaseSet(dest.GetIdentHash()); - if (leaseSet) - Connect(leaseSet); - else - { - m_Session->localDestination->RequestDestination(dest.GetIdentHash(), - std::bind(&SAMSocket::HandleConnectLeaseSetRequestComplete, - shared_from_this(), std::placeholders::_1)); - } - } - else - SendMessageReply(SAM_SESSION_STATUS_INVALID_KEY, strlen(SAM_SESSION_STATUS_INVALID_KEY), true); - } - else - SendMessageReply (SAM_STREAM_STATUS_INVALID_ID, strlen(SAM_STREAM_STATUS_INVALID_ID), true); - } - - void SAMSocket::Connect (std::shared_ptr remote) - { - m_SocketType = eSAMSocketTypeStream; - m_Session->sockets.push_back (shared_from_this ()); - m_Stream = m_Session->localDestination->CreateStream (remote); - m_Stream->Send ((uint8_t *)m_Buffer, 0); // connect - I2PReceive (); - SendMessageReply (SAM_STREAM_STATUS_OK, strlen(SAM_STREAM_STATUS_OK), false); - } - - void SAMSocket::HandleConnectLeaseSetRequestComplete (std::shared_ptr leaseSet) - { - if (leaseSet) - Connect (leaseSet); - else - { - LogPrint ("SAM destination to connect not found"); - SendMessageReply (SAM_STREAM_STATUS_CANT_REACH_PEER, strlen(SAM_STREAM_STATUS_CANT_REACH_PEER), true); - } - } - - void SAMSocket::ProcessStreamAccept (char * buf, size_t len) - { - LogPrint (eLogDebug, "SAM stream accept: ", buf); - std::map params; - ExtractParams (buf, params); - std::string& id = params[SAM_PARAM_ID]; - std::string& silent = params[SAM_PARAM_SILENT]; - if (silent == SAM_VALUE_TRUE) m_IsSilent = true; - m_ID = id; - m_Session = m_Owner.FindSession (id); - if (m_Session) - { - if (!m_Session->localDestination->IsAcceptingStreams ()) - { - m_SocketType = eSAMSocketTypeAcceptor; - m_Session->sockets.push_back (shared_from_this ()); - m_Session->localDestination->AcceptStreams (std::bind (&SAMSocket::HandleI2PAccept, shared_from_this (), std::placeholders::_1)); - SendMessageReply (SAM_STREAM_STATUS_OK, strlen(SAM_STREAM_STATUS_OK), false); - } - else - SendMessageReply (SAM_STREAM_STATUS_I2P_ERROR, strlen(SAM_STREAM_STATUS_I2P_ERROR), true); - } - else - SendMessageReply (SAM_STREAM_STATUS_INVALID_ID, strlen(SAM_STREAM_STATUS_INVALID_ID), true); - } - - size_t SAMSocket::ProcessDatagramSend (char * buf, size_t len, const char * data) - { - LogPrint (eLogDebug, "SAM datagram send: ", buf, " ", len); - std::map params; - ExtractParams (buf, params); - size_t size = boost::lexical_cast(params[SAM_PARAM_SIZE]), offset = data - buf; - if (offset + size <= len) - { - if (m_Session) - { - auto d = m_Session->localDestination->GetDatagramDestination (); - if (d) - { - i2p::data::IdentityEx dest; - dest.FromBase64 (params[SAM_PARAM_DESTINATION]); - d->SendDatagramTo ((const uint8_t *)data, size, dest.GetIdentHash ()); - } - else - LogPrint (eLogError, "SAM missing datagram destination"); - } - else - LogPrint (eLogError, "SAM session is not created from DATAGRAM SEND"); - } - else - { - LogPrint (eLogWarning, "SAM sent datagram size ", size, " exceeds buffer ", len - offset); - return 0; // try to receive more - } - return offset + size; - } - - void SAMSocket::ProcessDestGenerate () - { - LogPrint (eLogDebug, "SAM dest generate"); - auto keys = i2p::data::PrivateKeys::CreateRandomKeys (); -#ifdef _MSC_VER - size_t len = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_DEST_REPLY, - keys.GetPublic ().ToBase64 ().c_str (), keys.ToBase64 ().c_str ()); -#else - size_t len = snprintf (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_DEST_REPLY, - keys.GetPublic ().ToBase64 ().c_str (), keys.ToBase64 ().c_str ()); -#endif - SendMessageReply (m_Buffer, len, false); - } - - void SAMSocket::ProcessNamingLookup (char * buf, size_t len) - { - LogPrint (eLogDebug, "SAM naming lookup: ", buf); - std::map params; - ExtractParams (buf, params); - std::string& name = params[SAM_PARAM_NAME]; - i2p::data::IdentityEx identity; - i2p::data::IdentHash ident; - if (name == "ME") - SendNamingLookupReply (m_Session->localDestination->GetIdentity ()); - else if (context.GetAddressBook ().GetAddress (name, identity)) - SendNamingLookupReply (identity); - else if (m_Session && m_Session->localDestination && - context.GetAddressBook ().GetIdentHash (name, ident)) - { - auto leaseSet = m_Session->localDestination->FindLeaseSet (ident); - if (leaseSet) - SendNamingLookupReply (leaseSet->GetIdentity ()); - else - m_Session->localDestination->RequestDestination (ident, - std::bind (&SAMSocket::HandleNamingLookupLeaseSetRequestComplete, - shared_from_this (), std::placeholders::_1, ident)); - } - else - { - LogPrint ("SAM naming failed. Unknown address ", name); -#ifdef _MSC_VER - size_t len = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_NAMING_REPLY_INVALID_KEY, name.c_str()); -#else - size_t len = snprintf (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_NAMING_REPLY_INVALID_KEY, name.c_str()); -#endif - SendMessageReply (m_Buffer, len, false); - } - } - - void SAMSocket::HandleNamingLookupLeaseSetRequestComplete (std::shared_ptr leaseSet, i2p::data::IdentHash ident) - { - if (leaseSet) - { - context.GetAddressBook ().InsertAddress (leaseSet->GetIdentity ()); - SendNamingLookupReply (leaseSet->GetIdentity ()); - } - else - { - LogPrint (eLogInfo, "SAM naming lookup failed. LeaseSet for ", ident.ToBase32 (), " not found"); -#ifdef _MSC_VER - size_t len = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_NAMING_REPLY_INVALID_KEY, - context.GetAddressBook ().ToAddress (ident).c_str()); -#else - size_t len = snprintf (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_NAMING_REPLY_INVALID_KEY, - context.GetAddressBook ().ToAddress (ident).c_str()); -#endif - SendMessageReply (m_Buffer, len, false); - } - } - - void SAMSocket::SendNamingLookupReply (const i2p::data::IdentityEx& identity) - { - auto base64 = identity.ToBase64 (); -#ifdef _MSC_VER - size_t l = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_NAMING_REPLY, base64.c_str ()); -#else - size_t l = snprintf (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_NAMING_REPLY, base64.c_str ()); -#endif - SendMessageReply (m_Buffer, l, false); - } - - void SAMSocket::ExtractParams (char * buf, std::map& params) - { - char * separator; - do - { - separator = strchr (buf, ' '); - if (separator) *separator = 0; - char * value = strchr (buf, '='); - if (value) - { - *value = 0; - value++; - params[buf] = value; - } - buf = separator + 1; - } - while (separator); - } - - void SAMSocket::Receive () - { - if (m_BufferOffset >= SAM_SOCKET_BUFFER_SIZE) - { - LogPrint (eLogError, "Buffer is full. Terminate"); - Terminate (); - return; - } - m_Socket.async_read_some (boost::asio::buffer(m_Buffer + m_BufferOffset, SAM_SOCKET_BUFFER_SIZE - m_BufferOffset), - std::bind((m_SocketType == eSAMSocketTypeStream) ? &SAMSocket::HandleReceived : &SAMSocket::HandleMessage, - shared_from_this (), std::placeholders::_1, std::placeholders::_2)); - } - - void SAMSocket::HandleReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred) - { - if (ecode) + void SAMSocket::SendMessageReply (const char * msg, size_t len, bool close) + { + if (!m_IsSilent) + boost::asio::async_write (m_Socket, boost::asio::buffer (msg, len), boost::asio::transfer_all (), + std::bind(&SAMSocket::HandleMessageReplySent, shared_from_this (), + std::placeholders::_1, std::placeholders::_2, close)); + else { - LogPrint ("SAM read error: ", ecode.message ()); - if (ecode != boost::asio::error::operation_aborted) - Terminate (); - } - else - { - if (m_Stream) - { - auto s = shared_from_this (); - m_Stream->AsyncSend ((uint8_t *)m_Buffer, bytes_transferred, - [s](const boost::system::error_code& ecode) - { - if (!ecode) - s->Receive (); - else - s->Terminate (); - }); - } - } - } + if (close) + Terminate (); + else + Receive (); + } + } - void SAMSocket::I2PReceive () - { - if (m_Stream) - m_Stream->AsyncReceive (boost::asio::buffer (m_StreamBuffer, SAM_SOCKET_BUFFER_SIZE), - std::bind (&SAMSocket::HandleI2PReceive, shared_from_this (), - std::placeholders::_1, std::placeholders::_2), - SAM_SOCKET_CONNECTION_MAX_IDLE); - } + void SAMSocket::HandleMessageReplySent (const boost::system::error_code& ecode, std::size_t bytes_transferred, bool close) + { + if (ecode) + { + LogPrint ("SAM reply send error: ", ecode.message ()); + if (ecode != boost::asio::error::operation_aborted) + Terminate (); + } + else + { + if (close) + Terminate (); + else + Receive (); + } + } - void SAMSocket::HandleI2PReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred) - { - if (ecode) - { - LogPrint ("SAM stream read error: ", ecode.message ()); - if (ecode != boost::asio::error::operation_aborted) - Terminate (); - } - else - { - boost::asio::async_write (m_Socket, boost::asio::buffer (m_StreamBuffer, bytes_transferred), - std::bind (&SAMSocket::HandleWriteI2PData, shared_from_this (), std::placeholders::_1)); - } - } + void SAMSocket::HandleMessage (const boost::system::error_code& ecode, std::size_t bytes_transferred) + { + if (ecode) + { + LogPrint ("SAM read error: ", ecode.message ()); + if (ecode != boost::asio::error::operation_aborted) + Terminate (); + } + else + { + bytes_transferred += m_BufferOffset; + m_BufferOffset = 0; + m_Buffer[bytes_transferred] = 0; + char * eol = (char *)memchr (m_Buffer, '\n', bytes_transferred); + if (eol) + { + *eol = 0; + char * separator = strchr (m_Buffer, ' '); + if (separator) + { + separator = strchr (separator + 1, ' '); + if (separator) + *separator = 0; + else + separator = eol; - void SAMSocket::HandleWriteI2PData (const boost::system::error_code& ecode) - { - if (ecode) - { - LogPrint ("SAM socket write error: ", ecode.message ()); - if (ecode != boost::asio::error::operation_aborted) - Terminate (); - } - else - I2PReceive (); - } + if (!strcmp (m_Buffer, SAM_SESSION_CREATE)) + ProcessSessionCreate (separator + 1, bytes_transferred - (separator - m_Buffer) - 1); + else if (!strcmp (m_Buffer, SAM_STREAM_CONNECT)) + ProcessStreamConnect (separator + 1, bytes_transferred - (separator - m_Buffer) - 1); + else if (!strcmp (m_Buffer, SAM_STREAM_ACCEPT)) + ProcessStreamAccept (separator + 1, bytes_transferred - (separator - m_Buffer) - 1); + else if (!strcmp (m_Buffer, SAM_DEST_GENERATE)) + ProcessDestGenerate (); + else if (!strcmp (m_Buffer, SAM_NAMING_LOOKUP)) + ProcessNamingLookup (separator + 1, bytes_transferred - (separator - m_Buffer) - 1); + else if (!strcmp (m_Buffer, SAM_DATAGRAM_SEND)) + { + size_t len = bytes_transferred - (separator - m_Buffer) - 1; + size_t processed = ProcessDatagramSend (separator + 1, len, eol + 1); + if (processed < len) + { + m_BufferOffset = len - processed; + if (processed > 0) + memmove (m_Buffer, separator + 1 + processed, m_BufferOffset); + else + { + // restore string back + *separator = ' '; + *eol = '\n'; + } + } + // since it's SAM v1 reply is not expected + Receive (); + } + else + { + LogPrint (eLogError, "SAM unexpected message ", m_Buffer); + Terminate (); + } + } + else + { + LogPrint (eLogError, "SAM malformed message ", m_Buffer); + Terminate (); + } + } + else + { + LogPrint (eLogWarning, "SAM incomplete message ", bytes_transferred); + m_BufferOffset = bytes_transferred; + // try to receive remaining message + Receive (); + } + } + } - void SAMSocket::HandleI2PAccept (std::shared_ptr stream) - { - if (stream) - { - LogPrint ("SAM incoming I2P connection for session ", m_ID); - m_Stream = stream; - context.GetAddressBook ().InsertAddress (stream->GetRemoteIdentity ()); - auto session = m_Owner.FindSession (m_ID); - if (session) - session->localDestination->StopAcceptingStreams (); - m_SocketType = eSAMSocketTypeStream; - if (!m_IsSilent) - { - // send remote peer address - uint8_t ident[1024]; - size_t l = stream->GetRemoteIdentity ().ToBuffer (ident, 1024); - size_t l1 = i2p::data::ByteStreamToBase64 (ident, l, (char *)m_StreamBuffer, SAM_SOCKET_BUFFER_SIZE); - m_StreamBuffer[l1] = '\n'; - HandleI2PReceive (boost::system::error_code (), l1 +1); // we send identity like it has been received from stream - } - else - I2PReceive (); - } - else - LogPrint (eLogInfo, "SAM I2P acceptor has been reset"); - } + void SAMSocket::ProcessSessionCreate (char * buf, size_t len) + { + LogPrint ("SAM session create: ", buf); + std::map params; + ExtractParams (buf, params); + std::string& style = params[SAM_PARAM_STYLE]; + std::string& id = params[SAM_PARAM_ID]; + std::string& destination = params[SAM_PARAM_DESTINATION]; + m_ID = id; + if (m_Owner.FindSession (id)) + { + // session exists + SendMessageReply (SAM_SESSION_CREATE_DUPLICATED_ID, strlen(SAM_SESSION_CREATE_DUPLICATED_ID), true); + return; + } - void SAMSocket::HandleI2PDatagramReceive (const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len) - { - LogPrint (eLogDebug, "SAM datagram received ", len); - auto base64 = from.ToBase64 (); + // create destination + m_Session = m_Owner.CreateSession (id, destination == SAM_VALUE_TRANSIENT ? "" : destination, ¶ms); + if (m_Session) + { + m_SocketType = eSAMSocketTypeSession; + if (style == SAM_VALUE_DATAGRAM) + { + auto dest = m_Session->localDestination->CreateDatagramDestination (); + dest->SetReceiver (std::bind (&SAMSocket::HandleI2PDatagramReceive, shared_from_this (), + std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5)); + } + + if (m_Session->localDestination->IsReady ()) + SendSessionCreateReplyOk (); + else + { + m_Timer.expires_from_now (boost::posix_time::seconds(SAM_SESSION_READINESS_CHECK_INTERVAL)); + m_Timer.async_wait (std::bind (&SAMSocket::HandleSessionReadinessCheckTimer, + shared_from_this (), std::placeholders::_1)); + } + } + else + SendMessageReply (SAM_SESSION_CREATE_DUPLICATED_DEST, strlen(SAM_SESSION_CREATE_DUPLICATED_DEST), true); + } + + void SAMSocket::HandleSessionReadinessCheckTimer (const boost::system::error_code& ecode) + { + if (ecode != boost::asio::error::operation_aborted) + { + if (m_Session->localDestination->IsReady ()) + SendSessionCreateReplyOk (); + else + { + m_Timer.expires_from_now (boost::posix_time::seconds(SAM_SESSION_READINESS_CHECK_INTERVAL)); + m_Timer.async_wait (std::bind (&SAMSocket::HandleSessionReadinessCheckTimer, + shared_from_this (), std::placeholders::_1)); + } + } + } + + void SAMSocket::SendSessionCreateReplyOk () + { + uint8_t buf[1024]; + char priv[1024]; + size_t l = m_Session->localDestination->GetPrivateKeys ().ToBuffer (buf, 1024); + size_t l1 = i2p::data::ByteStreamToBase64 (buf, l, priv, 1024); + priv[l1] = 0; #ifdef _MSC_VER - size_t l = sprintf_s ((char *)m_StreamBuffer, SAM_SOCKET_BUFFER_SIZE, SAM_DATAGRAM_RECEIVED, base64.c_str (), len); -#else - size_t l = snprintf ((char *)m_StreamBuffer, SAM_SOCKET_BUFFER_SIZE, SAM_DATAGRAM_RECEIVED, base64.c_str (), len); + size_t l2 = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_SESSION_CREATE_REPLY_OK, priv); +#else + size_t l2 = snprintf (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_SESSION_CREATE_REPLY_OK, priv); #endif - if (len < SAM_SOCKET_BUFFER_SIZE - l) - { - memcpy (m_StreamBuffer + l, buf, len); - boost::asio::async_write (m_Socket, boost::asio::buffer (m_StreamBuffer, len + l), - std::bind (&SAMSocket::HandleWriteI2PData, shared_from_this (), std::placeholders::_1)); - } - else - LogPrint (eLogWarning, "SAM received datagram size ", len," exceeds buffer"); - } + SendMessageReply (m_Buffer, l2, false); + } - SAMSession::SAMSession (std::shared_ptr dest): - localDestination (dest) - { - } - - SAMSession::~SAMSession () - { - for (auto it: sockets) - it->SetSocketType (eSAMSocketTypeTerminated); - i2p::client::context.DeleteLocalDestination (localDestination); - } + void SAMSocket::ProcessStreamConnect (char * buf, size_t len) + { + LogPrint (eLogDebug, "SAM stream connect: ", buf); + std::map params; + ExtractParams (buf, params); + std::string& id = params[SAM_PARAM_ID]; + std::string& destination = params[SAM_PARAM_DESTINATION]; + std::string& silent = params[SAM_PARAM_SILENT]; + if (silent == SAM_VALUE_TRUE) m_IsSilent = true; + m_ID = id; + m_Session = m_Owner.FindSession (id); + if (m_Session) + { + i2p::data::IdentityEx dest; + size_t len = dest.FromBase64(destination); + if (len > 0) + { + context.GetAddressBook().InsertAddress(dest); + auto leaseSet = m_Session->localDestination->FindLeaseSet(dest.GetIdentHash()); + if (leaseSet) + Connect(leaseSet); + else + { + m_Session->localDestination->RequestDestination(dest.GetIdentHash(), + std::bind(&SAMSocket::HandleConnectLeaseSetRequestComplete, + shared_from_this(), std::placeholders::_1)); + } + } + else + SendMessageReply(SAM_SESSION_STATUS_INVALID_KEY, strlen(SAM_SESSION_STATUS_INVALID_KEY), true); + } + else + SendMessageReply (SAM_STREAM_STATUS_INVALID_ID, strlen(SAM_STREAM_STATUS_INVALID_ID), true); + } - void SAMSession::CloseStreams () - { - for (auto it: sockets) - { - it->CloseStream (); - it->SetSocketType (eSAMSocketTypeTerminated); - } - sockets.clear (); - } + void SAMSocket::Connect (std::shared_ptr remote) + { + m_SocketType = eSAMSocketTypeStream; + m_Session->sockets.push_back (shared_from_this ()); + m_Stream = m_Session->localDestination->CreateStream (remote); + m_Stream->Send ((uint8_t *)m_Buffer, 0); // connect + I2PReceive (); + SendMessageReply (SAM_STREAM_STATUS_OK, strlen(SAM_STREAM_STATUS_OK), false); + } - SAMBridge::SAMBridge (int port): - m_IsRunning (false), m_Thread (nullptr), - m_Acceptor (m_Service, boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), port)), - m_DatagramEndpoint (boost::asio::ip::udp::v4 (), port-1), m_DatagramSocket (m_Service, m_DatagramEndpoint) - { - } + void SAMSocket::HandleConnectLeaseSetRequestComplete (std::shared_ptr leaseSet) + { + if (leaseSet) + Connect (leaseSet); + else + { + LogPrint ("SAM destination to connect not found"); + SendMessageReply (SAM_STREAM_STATUS_CANT_REACH_PEER, strlen(SAM_STREAM_STATUS_CANT_REACH_PEER), true); + } + } - SAMBridge::~SAMBridge () - { - if (m_IsRunning) - Stop (); - } + void SAMSocket::ProcessStreamAccept (char * buf, size_t len) + { + LogPrint (eLogDebug, "SAM stream accept: ", buf); + std::map params; + ExtractParams (buf, params); + std::string& id = params[SAM_PARAM_ID]; + std::string& silent = params[SAM_PARAM_SILENT]; + if (silent == SAM_VALUE_TRUE) m_IsSilent = true; + m_ID = id; + m_Session = m_Owner.FindSession (id); + if (m_Session) + { + if (!m_Session->localDestination->IsAcceptingStreams ()) + { + m_SocketType = eSAMSocketTypeAcceptor; + m_Session->sockets.push_back (shared_from_this ()); + m_Session->localDestination->AcceptStreams (std::bind (&SAMSocket::HandleI2PAccept, shared_from_this (), std::placeholders::_1)); + SendMessageReply (SAM_STREAM_STATUS_OK, strlen(SAM_STREAM_STATUS_OK), false); + } + else + SendMessageReply (SAM_STREAM_STATUS_I2P_ERROR, strlen(SAM_STREAM_STATUS_I2P_ERROR), true); + } + else + SendMessageReply (SAM_STREAM_STATUS_INVALID_ID, strlen(SAM_STREAM_STATUS_INVALID_ID), true); + } - void SAMBridge::Start () - { - Accept (); - ReceiveDatagram (); - m_IsRunning = true; - m_Thread = new std::thread (std::bind (&SAMBridge::Run, this)); - } + size_t SAMSocket::ProcessDatagramSend (char * buf, size_t len, const char * data) + { + LogPrint (eLogDebug, "SAM datagram send: ", buf, " ", len); + std::map params; + ExtractParams (buf, params); + size_t size = boost::lexical_cast(params[SAM_PARAM_SIZE]), offset = data - buf; + if (offset + size <= len) + { + if (m_Session) + { + auto d = m_Session->localDestination->GetDatagramDestination (); + if (d) + { + i2p::data::IdentityEx dest; + dest.FromBase64 (params[SAM_PARAM_DESTINATION]); + d->SendDatagramTo ((const uint8_t *)data, size, dest.GetIdentHash ()); + } + else + LogPrint (eLogError, "SAM missing datagram destination"); + } + else + LogPrint (eLogError, "SAM session is not created from DATAGRAM SEND"); + } + else + { + LogPrint (eLogWarning, "SAM sent datagram size ", size, " exceeds buffer ", len - offset); + return 0; // try to receive more + } + return offset + size; + } + + void SAMSocket::ProcessDestGenerate () + { + LogPrint (eLogDebug, "SAM dest generate"); + auto keys = i2p::data::PrivateKeys::CreateRandomKeys (); +#ifdef _MSC_VER + size_t len = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_DEST_REPLY, + keys.GetPublic ().ToBase64 ().c_str (), keys.ToBase64 ().c_str ()); +#else + size_t len = snprintf (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_DEST_REPLY, + keys.GetPublic ().ToBase64 ().c_str (), keys.ToBase64 ().c_str ()); +#endif + SendMessageReply (m_Buffer, len, false); + } - void SAMBridge::Stop () - { - m_IsRunning = false; - m_Acceptor.cancel (); - for (auto it: m_Sessions) - delete it.second; - m_Sessions.clear (); - m_Service.stop (); - if (m_Thread) - { - m_Thread->join (); - delete m_Thread; - m_Thread = nullptr; - } - } + void SAMSocket::ProcessNamingLookup (char * buf, size_t len) + { + LogPrint (eLogDebug, "SAM naming lookup: ", buf); + std::map params; + ExtractParams (buf, params); + std::string& name = params[SAM_PARAM_NAME]; + i2p::data::IdentityEx identity; + i2p::data::IdentHash ident; + if (name == "ME") + SendNamingLookupReply (m_Session->localDestination->GetIdentity ()); + else if (context.GetAddressBook ().GetAddress (name, identity)) + SendNamingLookupReply (identity); + else if (m_Session && m_Session->localDestination && + context.GetAddressBook ().GetIdentHash (name, ident)) + { + auto leaseSet = m_Session->localDestination->FindLeaseSet (ident); + if (leaseSet) + SendNamingLookupReply (leaseSet->GetIdentity ()); + else + m_Session->localDestination->RequestDestination (ident, + std::bind (&SAMSocket::HandleNamingLookupLeaseSetRequestComplete, + shared_from_this (), std::placeholders::_1, ident)); + } + else + { + LogPrint ("SAM naming failed. Unknown address ", name); +#ifdef _MSC_VER + size_t len = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_NAMING_REPLY_INVALID_KEY, name.c_str()); +#else + size_t len = snprintf (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_NAMING_REPLY_INVALID_KEY, name.c_str()); +#endif + SendMessageReply (m_Buffer, len, false); + } + } - void SAMBridge::Run () - { - while (m_IsRunning) - { - try - { - m_Service.run (); - } - catch (std::exception& ex) - { - LogPrint ("SAM: ", ex.what ()); - } - } - } + void SAMSocket::HandleNamingLookupLeaseSetRequestComplete (std::shared_ptr leaseSet, i2p::data::IdentHash ident) + { + if (leaseSet) + { + context.GetAddressBook ().InsertAddress (leaseSet->GetIdentity ()); + SendNamingLookupReply (leaseSet->GetIdentity ()); + } + else + { + LogPrint (eLogInfo, "SAM naming lookup failed. LeaseSet for ", ident.ToBase32 (), " not found"); +#ifdef _MSC_VER + size_t len = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_NAMING_REPLY_INVALID_KEY, + context.GetAddressBook ().ToAddress (ident).c_str()); +#else + size_t len = snprintf (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_NAMING_REPLY_INVALID_KEY, + context.GetAddressBook ().ToAddress (ident).c_str()); +#endif + SendMessageReply (m_Buffer, len, false); + } + } + + void SAMSocket::SendNamingLookupReply (const i2p::data::IdentityEx& identity) + { + auto base64 = identity.ToBase64 (); +#ifdef _MSC_VER + size_t l = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_NAMING_REPLY, base64.c_str ()); +#else + size_t l = snprintf (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_NAMING_REPLY, base64.c_str ()); +#endif + SendMessageReply (m_Buffer, l, false); + } - void SAMBridge::Accept () - { - auto newSocket = std::make_shared (*this); - m_Acceptor.async_accept (newSocket->GetSocket (), std::bind (&SAMBridge::HandleAccept, this, - std::placeholders::_1, newSocket)); - } + void SAMSocket::ExtractParams (char * buf, std::map& params) + { + char * separator; + do + { + separator = strchr (buf, ' '); + if (separator) *separator = 0; + char * value = strchr (buf, '='); + if (value) + { + *value = 0; + value++; + params[buf] = value; + } + buf = separator + 1; + } + while (separator); + } - void SAMBridge::HandleAccept(const boost::system::error_code& ecode, std::shared_ptr socket) - { - if (!ecode) - { - boost::system::error_code ec; - auto ep = socket->GetSocket ().remote_endpoint (ec); - if (!ec) - { - LogPrint ("New SAM connection from ", ep); - socket->ReceiveHandshake (); - } - else - LogPrint (eLogError, "SAM connection from error ", ec.message ()); - } - else - LogPrint ("SAM accept error: ", ecode.message ()); + void SAMSocket::Receive () + { + if (m_BufferOffset >= SAM_SOCKET_BUFFER_SIZE) + { + LogPrint (eLogError, "Buffer is full. Terminate"); + Terminate (); + return; + } + m_Socket.async_read_some (boost::asio::buffer(m_Buffer + m_BufferOffset, SAM_SOCKET_BUFFER_SIZE - m_BufferOffset), + std::bind((m_SocketType == eSAMSocketTypeStream) ? &SAMSocket::HandleReceived : &SAMSocket::HandleMessage, + shared_from_this (), std::placeholders::_1, std::placeholders::_2)); + } - if (ecode != boost::asio::error::operation_aborted) - Accept (); - } + void SAMSocket::HandleReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred) + { + if (ecode) + { + LogPrint ("SAM read error: ", ecode.message ()); + if (ecode != boost::asio::error::operation_aborted) + Terminate (); + } + else + { + if (m_Stream) + { + auto s = shared_from_this (); + m_Stream->AsyncSend ((uint8_t *)m_Buffer, bytes_transferred, + [s](const boost::system::error_code& ecode) + { + if (!ecode) + s->Receive (); + else + s->Terminate (); + }); + } + } + } - SAMSession * SAMBridge::CreateSession (const std::string& id, const std::string& destination, - const std::map * params) - { - std::shared_ptr localDestination = nullptr; - if (destination != "") - { - i2p::data::PrivateKeys keys; - keys.FromBase64 (destination); - localDestination = i2p::client::context.CreateNewLocalDestination (keys, true, params); - } - else // transient - { - // extract signature type - i2p::data::SigningKeyType signatureType = i2p::data::SIGNING_KEY_TYPE_DSA_SHA1; - if (params) - { - auto it = params->find (SAM_PARAM_SIGNATURE_TYPE); - if (it != params->end ()) - // TODO: extract string values - signatureType = boost::lexical_cast (it->second); - } - localDestination = i2p::client::context.CreateNewLocalDestination (true, signatureType, params); - } - if (localDestination) - { - std::unique_lock l(m_SessionsMutex); - auto ret = m_Sessions.insert (std::pair(id, new SAMSession (localDestination))); - if (!ret.second) - LogPrint ("Session ", id, " already exists"); - return ret.first->second; - } - return nullptr; - } + void SAMSocket::I2PReceive () + { + if (m_Stream) + m_Stream->AsyncReceive (boost::asio::buffer (m_StreamBuffer, SAM_SOCKET_BUFFER_SIZE), + std::bind (&SAMSocket::HandleI2PReceive, shared_from_this (), + std::placeholders::_1, std::placeholders::_2), + SAM_SOCKET_CONNECTION_MAX_IDLE); + } - void SAMBridge::CloseSession (const std::string& id) - { - std::unique_lock l(m_SessionsMutex); - auto it = m_Sessions.find (id); - if (it != m_Sessions.end ()) - { - auto session = it->second; - session->localDestination->StopAcceptingStreams (); - session->CloseStreams (); - m_Sessions.erase (it); - delete session; - } - } + void SAMSocket::HandleI2PReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred) + { + if (ecode) + { + LogPrint ("SAM stream read error: ", ecode.message ()); + if (ecode != boost::asio::error::operation_aborted) + Terminate (); + } + else + { + boost::asio::async_write (m_Socket, boost::asio::buffer (m_StreamBuffer, bytes_transferred), + std::bind (&SAMSocket::HandleWriteI2PData, shared_from_this (), std::placeholders::_1)); + } + } - SAMSession * SAMBridge::FindSession (const std::string& id) const - { - std::unique_lock l(m_SessionsMutex); - auto it = m_Sessions.find (id); - if (it != m_Sessions.end ()) - return it->second; - return nullptr; - } + void SAMSocket::HandleWriteI2PData (const boost::system::error_code& ecode) + { + if (ecode) + { + LogPrint ("SAM socket write error: ", ecode.message ()); + if (ecode != boost::asio::error::operation_aborted) + Terminate (); + } + else + I2PReceive (); + } - void SAMBridge::ReceiveDatagram () - { - m_DatagramSocket.async_receive_from ( - boost::asio::buffer (m_DatagramReceiveBuffer, i2p::datagram::MAX_DATAGRAM_SIZE), - m_SenderEndpoint, - std::bind (&SAMBridge::HandleReceivedDatagram, this, std::placeholders::_1, std::placeholders::_2)); - } + void SAMSocket::HandleI2PAccept (std::shared_ptr stream) + { + if (stream) + { + LogPrint ("SAM incoming I2P connection for session ", m_ID); + m_Stream = stream; + context.GetAddressBook ().InsertAddress (stream->GetRemoteIdentity ()); + auto session = m_Owner.FindSession (m_ID); + if (session) + session->localDestination->StopAcceptingStreams (); + m_SocketType = eSAMSocketTypeStream; + if (!m_IsSilent) + { + // send remote peer address + uint8_t ident[1024]; + size_t l = stream->GetRemoteIdentity ().ToBuffer (ident, 1024); + size_t l1 = i2p::data::ByteStreamToBase64 (ident, l, (char *)m_StreamBuffer, SAM_SOCKET_BUFFER_SIZE); + m_StreamBuffer[l1] = '\n'; + HandleI2PReceive (boost::system::error_code (), l1 +1); // we send identity like it has been received from stream + } + else + I2PReceive (); + } + else + LogPrint (eLogInfo, "SAM I2P acceptor has been reset"); + } - void SAMBridge::HandleReceivedDatagram (const boost::system::error_code& ecode, std::size_t bytes_transferred) - { - if (!ecode) - { - m_DatagramReceiveBuffer[bytes_transferred] = 0; - char * eol = strchr ((char *)m_DatagramReceiveBuffer, '\n'); - *eol = 0; eol++; - size_t payloadLen = bytes_transferred - ((uint8_t *)eol - m_DatagramReceiveBuffer); - LogPrint ("SAM datagram received ", m_DatagramReceiveBuffer," size=", payloadLen); - char * sessionID = strchr ((char *)m_DatagramReceiveBuffer, ' '); - if (sessionID) - { - sessionID++; - char * destination = strchr (sessionID, ' '); - if (destination) - { - *destination = 0; destination++; - auto session = FindSession (sessionID); - if (session) - { - i2p::data::IdentityEx dest; - dest.FromBase64 (destination); - session->localDestination->GetDatagramDestination ()-> - SendDatagramTo ((uint8_t *)eol, payloadLen, dest.GetIdentHash ()); - } - else - LogPrint ("Session ", sessionID, " not found"); - } - else - LogPrint ("Missing destination key"); - } - else - LogPrint ("Missing sessionID"); - ReceiveDatagram (); - } - else - LogPrint ("SAM datagram receive error: ", ecode.message ()); - } + void SAMSocket::HandleI2PDatagramReceive (const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len) + { + LogPrint (eLogDebug, "SAM datagram received ", len); + auto base64 = from.ToBase64 (); +#ifdef _MSC_VER + size_t l = sprintf_s ((char *)m_StreamBuffer, SAM_SOCKET_BUFFER_SIZE, SAM_DATAGRAM_RECEIVED, base64.c_str (), len); +#else + size_t l = snprintf ((char *)m_StreamBuffer, SAM_SOCKET_BUFFER_SIZE, SAM_DATAGRAM_RECEIVED, base64.c_str (), len); +#endif + if (len < SAM_SOCKET_BUFFER_SIZE - l) + { + memcpy (m_StreamBuffer + l, buf, len); + boost::asio::async_write (m_Socket, boost::asio::buffer (m_StreamBuffer, len + l), + std::bind (&SAMSocket::HandleWriteI2PData, shared_from_this (), std::placeholders::_1)); + } + else + LogPrint (eLogWarning, "SAM received datagram size ", len," exceeds buffer"); + } + + SAMSession::SAMSession (std::shared_ptr dest): + localDestination (dest) + { + } + + SAMSession::~SAMSession () + { + for (auto it: sockets) + it->SetSocketType (eSAMSocketTypeTerminated); + i2p::client::context.DeleteLocalDestination (localDestination); + } + + void SAMSession::CloseStreams () + { + for (auto it: sockets) + { + it->CloseStream (); + it->SetSocketType (eSAMSocketTypeTerminated); + } + sockets.clear (); + } + + SAMBridge::SAMBridge (int port): + m_IsRunning (false), m_Thread (nullptr), + m_Acceptor (m_Service, boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), port)), + m_DatagramEndpoint (boost::asio::ip::udp::v4 (), port-1), m_DatagramSocket (m_Service, m_DatagramEndpoint) + { + } + + SAMBridge::~SAMBridge () + { + if (m_IsRunning) + Stop (); + } + + void SAMBridge::Start () + { + Accept (); + ReceiveDatagram (); + m_IsRunning = true; + m_Thread = new std::thread (std::bind (&SAMBridge::Run, this)); + } + + void SAMBridge::Stop () + { + m_IsRunning = false; + m_Acceptor.cancel (); + for (auto it: m_Sessions) + delete it.second; + m_Sessions.clear (); + m_Service.stop (); + if (m_Thread) + { + m_Thread->join (); + delete m_Thread; + m_Thread = nullptr; + } + } + + void SAMBridge::Run () + { + while (m_IsRunning) + { + try + { + m_Service.run (); + } + catch (std::exception& ex) + { + LogPrint ("SAM: ", ex.what ()); + } + } + } + + void SAMBridge::Accept () + { + auto newSocket = std::make_shared (*this); + m_Acceptor.async_accept (newSocket->GetSocket (), std::bind (&SAMBridge::HandleAccept, this, + std::placeholders::_1, newSocket)); + } + + void SAMBridge::HandleAccept(const boost::system::error_code& ecode, std::shared_ptr socket) + { + if (!ecode) + { + boost::system::error_code ec; + auto ep = socket->GetSocket ().remote_endpoint (ec); + if (!ec) + { + LogPrint ("New SAM connection from ", ep); + socket->ReceiveHandshake (); + } + else + LogPrint (eLogError, "SAM connection from error ", ec.message ()); + } + else + LogPrint ("SAM accept error: ", ecode.message ()); + + if (ecode != boost::asio::error::operation_aborted) + Accept (); + } + + SAMSession * SAMBridge::CreateSession (const std::string& id, const std::string& destination, + const std::map * params) + { + std::shared_ptr localDestination = nullptr; + if (destination != "") + { + i2p::data::PrivateKeys keys; + keys.FromBase64 (destination); + localDestination = i2p::client::context.CreateNewLocalDestination (keys, true, params); + } + else // transient + { + // extract signature type + i2p::data::SigningKeyType signatureType = i2p::data::SIGNING_KEY_TYPE_DSA_SHA1; + if (params) + { + auto it = params->find (SAM_PARAM_SIGNATURE_TYPE); + if (it != params->end ()) + // TODO: extract string values + signatureType = boost::lexical_cast (it->second); + } + localDestination = i2p::client::context.CreateNewLocalDestination (true, signatureType, params); + } + if (localDestination) + { + std::unique_lock l(m_SessionsMutex); + auto ret = m_Sessions.insert (std::pair(id, new SAMSession (localDestination))); + if (!ret.second) + LogPrint ("Session ", id, " already exists"); + return ret.first->second; + } + return nullptr; + } + + void SAMBridge::CloseSession (const std::string& id) + { + std::unique_lock l(m_SessionsMutex); + auto it = m_Sessions.find (id); + if (it != m_Sessions.end ()) + { + auto session = it->second; + session->localDestination->StopAcceptingStreams (); + session->CloseStreams (); + m_Sessions.erase (it); + delete session; + } + } + + SAMSession * SAMBridge::FindSession (const std::string& id) const + { + std::unique_lock l(m_SessionsMutex); + auto it = m_Sessions.find (id); + if (it != m_Sessions.end ()) + return it->second; + return nullptr; + } + + void SAMBridge::ReceiveDatagram () + { + m_DatagramSocket.async_receive_from ( + boost::asio::buffer (m_DatagramReceiveBuffer, i2p::datagram::MAX_DATAGRAM_SIZE), + m_SenderEndpoint, + std::bind (&SAMBridge::HandleReceivedDatagram, this, std::placeholders::_1, std::placeholders::_2)); + } + + void SAMBridge::HandleReceivedDatagram (const boost::system::error_code& ecode, std::size_t bytes_transferred) + { + if (!ecode) + { + m_DatagramReceiveBuffer[bytes_transferred] = 0; + char * eol = strchr ((char *)m_DatagramReceiveBuffer, '\n'); + *eol = 0; eol++; + size_t payloadLen = bytes_transferred - ((uint8_t *)eol - m_DatagramReceiveBuffer); + LogPrint ("SAM datagram received ", m_DatagramReceiveBuffer," size=", payloadLen); + char * sessionID = strchr ((char *)m_DatagramReceiveBuffer, ' '); + if (sessionID) + { + sessionID++; + char * destination = strchr (sessionID, ' '); + if (destination) + { + *destination = 0; destination++; + auto session = FindSession (sessionID); + if (session) + { + i2p::data::IdentityEx dest; + dest.FromBase64 (destination); + session->localDestination->GetDatagramDestination ()-> + SendDatagramTo ((uint8_t *)eol, payloadLen, dest.GetIdentHash ()); + } + else + LogPrint ("Session ", sessionID, " not found"); + } + else + LogPrint ("Missing destination key"); + } + else + LogPrint ("Missing sessionID"); + ReceiveDatagram (); + } + else + LogPrint ("SAM datagram receive error: ", ecode.message ()); + } } } diff --git a/SAM.h b/SAM.h index 7684d461..06063fb6 100644 --- a/SAM.h +++ b/SAM.h @@ -18,173 +18,173 @@ namespace i2p { namespace client { - const size_t SAM_SOCKET_BUFFER_SIZE = 8192; - const int SAM_SOCKET_CONNECTION_MAX_IDLE = 3600; // in seconds - const int SAM_SESSION_READINESS_CHECK_INTERVAL = 20; // in seconds - const char SAM_HANDSHAKE[] = "HELLO VERSION"; - const char SAM_HANDSHAKE_REPLY[] = "HELLO REPLY RESULT=OK VERSION=%s\n"; - const char SAM_HANDSHAKE_I2P_ERROR[] = "HELLO REPLY RESULT=I2P_ERROR\n"; - const char SAM_SESSION_CREATE[] = "SESSION CREATE"; - const char SAM_SESSION_CREATE_REPLY_OK[] = "SESSION STATUS RESULT=OK DESTINATION=%s\n"; - const char SAM_SESSION_CREATE_DUPLICATED_ID[] = "SESSION STATUS RESULT=DUPLICATED_ID\n"; - const char SAM_SESSION_CREATE_DUPLICATED_DEST[] = "SESSION STATUS RESULT=DUPLICATED_DEST\n"; - const char SAM_SESSION_STATUS_INVALID_KEY[] = "SESSION STATUS RESULT=INVALID_KEY\n"; - const char SAM_STREAM_CONNECT[] = "STREAM CONNECT"; - const char SAM_STREAM_STATUS_OK[] = "STREAM STATUS RESULT=OK\n"; - const char SAM_STREAM_STATUS_INVALID_ID[] = "STREAM STATUS RESULT=INVALID_ID\n"; - const char SAM_STREAM_STATUS_CANT_REACH_PEER[] = "STREAM STATUS RESULT=CANT_REACH_PEER\n"; - const char SAM_STREAM_STATUS_I2P_ERROR[] = "STREAM STATUS RESULT=I2P_ERROR\n"; - const char SAM_STREAM_ACCEPT[] = "STREAM ACCEPT"; - const char SAM_DATAGRAM_SEND[] = "DATAGRAM SEND"; - const char SAM_DEST_GENERATE[] = "DEST GENERATE"; - const char SAM_DEST_REPLY[] = "DEST REPLY PUB=%s PRIV=%s\n"; - const char SAM_DEST_REPLY_I2P_ERROR[] = "DEST REPLY RESULT=I2P_ERROR\n"; - const char SAM_NAMING_LOOKUP[] = "NAMING LOOKUP"; - const char SAM_NAMING_REPLY[] = "NAMING REPLY RESULT=OK NAME=ME VALUE=%s\n"; - const char SAM_DATAGRAM_RECEIVED[] = "DATAGRAM RECEIVED DESTINATION=%s SIZE=%lu\n"; - const char SAM_NAMING_REPLY_INVALID_KEY[] = "NAMING REPLY RESULT=INVALID_KEY NAME=%s\n"; - const char SAM_NAMING_REPLY_KEY_NOT_FOUND[] = "NAMING REPLY RESULT=INVALID_KEY_NOT_FOUND NAME=%s\n"; - const char SAM_PARAM_MIN[] = "MIN"; - const char SAM_PARAM_MAX[] = "MAX"; - const char SAM_PARAM_STYLE[] = "STYLE"; - const char SAM_PARAM_ID[] = "ID"; - const char SAM_PARAM_SILENT[] = "SILENT"; - const char SAM_PARAM_DESTINATION[] = "DESTINATION"; - const char SAM_PARAM_NAME[] = "NAME"; - const char SAM_PARAM_SIGNATURE_TYPE[] = "SIGNATURE_TYPE"; - const char SAM_PARAM_SIZE[] = "SIZE"; - const char SAM_VALUE_TRANSIENT[] = "TRANSIENT"; - const char SAM_VALUE_STREAM[] = "STREAM"; - const char SAM_VALUE_DATAGRAM[] = "DATAGRAM"; - const char SAM_VALUE_RAW[] = "RAW"; - const char SAM_VALUE_TRUE[] = "true"; - const char SAM_VALUE_FALSE[] = "false"; + const size_t SAM_SOCKET_BUFFER_SIZE = 8192; + const int SAM_SOCKET_CONNECTION_MAX_IDLE = 3600; // in seconds + const int SAM_SESSION_READINESS_CHECK_INTERVAL = 20; // in seconds + const char SAM_HANDSHAKE[] = "HELLO VERSION"; + const char SAM_HANDSHAKE_REPLY[] = "HELLO REPLY RESULT=OK VERSION=%s\n"; + const char SAM_HANDSHAKE_I2P_ERROR[] = "HELLO REPLY RESULT=I2P_ERROR\n"; + const char SAM_SESSION_CREATE[] = "SESSION CREATE"; + const char SAM_SESSION_CREATE_REPLY_OK[] = "SESSION STATUS RESULT=OK DESTINATION=%s\n"; + const char SAM_SESSION_CREATE_DUPLICATED_ID[] = "SESSION STATUS RESULT=DUPLICATED_ID\n"; + const char SAM_SESSION_CREATE_DUPLICATED_DEST[] = "SESSION STATUS RESULT=DUPLICATED_DEST\n"; + const char SAM_SESSION_STATUS_INVALID_KEY[] = "SESSION STATUS RESULT=INVALID_KEY\n"; + const char SAM_STREAM_CONNECT[] = "STREAM CONNECT"; + const char SAM_STREAM_STATUS_OK[] = "STREAM STATUS RESULT=OK\n"; + const char SAM_STREAM_STATUS_INVALID_ID[] = "STREAM STATUS RESULT=INVALID_ID\n"; + const char SAM_STREAM_STATUS_CANT_REACH_PEER[] = "STREAM STATUS RESULT=CANT_REACH_PEER\n"; + const char SAM_STREAM_STATUS_I2P_ERROR[] = "STREAM STATUS RESULT=I2P_ERROR\n"; + const char SAM_STREAM_ACCEPT[] = "STREAM ACCEPT"; + const char SAM_DATAGRAM_SEND[] = "DATAGRAM SEND"; + const char SAM_DEST_GENERATE[] = "DEST GENERATE"; + const char SAM_DEST_REPLY[] = "DEST REPLY PUB=%s PRIV=%s\n"; + const char SAM_DEST_REPLY_I2P_ERROR[] = "DEST REPLY RESULT=I2P_ERROR\n"; + const char SAM_NAMING_LOOKUP[] = "NAMING LOOKUP"; + const char SAM_NAMING_REPLY[] = "NAMING REPLY RESULT=OK NAME=ME VALUE=%s\n"; + const char SAM_DATAGRAM_RECEIVED[] = "DATAGRAM RECEIVED DESTINATION=%s SIZE=%lu\n"; + const char SAM_NAMING_REPLY_INVALID_KEY[] = "NAMING REPLY RESULT=INVALID_KEY NAME=%s\n"; + const char SAM_NAMING_REPLY_KEY_NOT_FOUND[] = "NAMING REPLY RESULT=INVALID_KEY_NOT_FOUND NAME=%s\n"; + const char SAM_PARAM_MIN[] = "MIN"; + const char SAM_PARAM_MAX[] = "MAX"; + const char SAM_PARAM_STYLE[] = "STYLE"; + const char SAM_PARAM_ID[] = "ID"; + const char SAM_PARAM_SILENT[] = "SILENT"; + const char SAM_PARAM_DESTINATION[] = "DESTINATION"; + const char SAM_PARAM_NAME[] = "NAME"; + const char SAM_PARAM_SIGNATURE_TYPE[] = "SIGNATURE_TYPE"; + const char SAM_PARAM_SIZE[] = "SIZE"; + const char SAM_VALUE_TRANSIENT[] = "TRANSIENT"; + const char SAM_VALUE_STREAM[] = "STREAM"; + const char SAM_VALUE_DATAGRAM[] = "DATAGRAM"; + const char SAM_VALUE_RAW[] = "RAW"; + const char SAM_VALUE_TRUE[] = "true"; + const char SAM_VALUE_FALSE[] = "false"; - enum SAMSocketType - { - eSAMSocketTypeUnknown, - eSAMSocketTypeSession, - eSAMSocketTypeStream, - eSAMSocketTypeAcceptor, - eSAMSocketTypeTerminated - }; + enum SAMSocketType + { + eSAMSocketTypeUnknown, + eSAMSocketTypeSession, + eSAMSocketTypeStream, + eSAMSocketTypeAcceptor, + eSAMSocketTypeTerminated + }; - class SAMBridge; - struct SAMSession; - class SAMSocket: public std::enable_shared_from_this - { - public: + class SAMBridge; + struct SAMSession; + class SAMSocket: public std::enable_shared_from_this + { + public: - SAMSocket (SAMBridge& owner); - ~SAMSocket (); - void CloseStream (); // TODO: implement it better + SAMSocket (SAMBridge& owner); + ~SAMSocket (); + void CloseStream (); // TODO: implement it better - boost::asio::ip::tcp::socket& GetSocket () { return m_Socket; }; - void ReceiveHandshake (); - void SetSocketType (SAMSocketType socketType) { m_SocketType = socketType; }; - SAMSocketType GetSocketType () const { return m_SocketType; }; + boost::asio::ip::tcp::socket& GetSocket () { return m_Socket; }; + void ReceiveHandshake (); + void SetSocketType (SAMSocketType socketType) { m_SocketType = socketType; }; + SAMSocketType GetSocketType () const { return m_SocketType; }; - private: + private: - void Terminate (); - void HandleHandshakeReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred); - void HandleHandshakeReplySent (const boost::system::error_code& ecode, std::size_t bytes_transferred); - void HandleMessage (const boost::system::error_code& ecode, std::size_t bytes_transferred); - void SendMessageReply (const char * msg, size_t len, bool close); - void HandleMessageReplySent (const boost::system::error_code& ecode, std::size_t bytes_transferred, bool close); - void Receive (); - void HandleReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred); + void Terminate (); + void HandleHandshakeReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred); + void HandleHandshakeReplySent (const boost::system::error_code& ecode, std::size_t bytes_transferred); + void HandleMessage (const boost::system::error_code& ecode, std::size_t bytes_transferred); + void SendMessageReply (const char * msg, size_t len, bool close); + void HandleMessageReplySent (const boost::system::error_code& ecode, std::size_t bytes_transferred, bool close); + void Receive (); + void HandleReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred); - void I2PReceive (); - void HandleI2PReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred); - void HandleI2PAccept (std::shared_ptr stream); - void HandleWriteI2PData (const boost::system::error_code& ecode); - void HandleI2PDatagramReceive (const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len); + void I2PReceive (); + void HandleI2PReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred); + void HandleI2PAccept (std::shared_ptr stream); + void HandleWriteI2PData (const boost::system::error_code& ecode); + void HandleI2PDatagramReceive (const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len); - void ProcessSessionCreate (char * buf, size_t len); - void ProcessStreamConnect (char * buf, size_t len); - void ProcessStreamAccept (char * buf, size_t len); - void ProcessDestGenerate (); - void ProcessNamingLookup (char * buf, size_t len); - size_t ProcessDatagramSend (char * buf, size_t len, const char * data); // from SAM 1.0 - void ExtractParams (char * buf, std::map& params); + void ProcessSessionCreate (char * buf, size_t len); + void ProcessStreamConnect (char * buf, size_t len); + void ProcessStreamAccept (char * buf, size_t len); + void ProcessDestGenerate (); + void ProcessNamingLookup (char * buf, size_t len); + size_t ProcessDatagramSend (char * buf, size_t len, const char * data); // from SAM 1.0 + void ExtractParams (char * buf, std::map& params); - void Connect (std::shared_ptr remote); - void HandleConnectLeaseSetRequestComplete (std::shared_ptr leaseSet); - void SendNamingLookupReply (const i2p::data::IdentityEx& identity); - void HandleNamingLookupLeaseSetRequestComplete (std::shared_ptr leaseSet, i2p::data::IdentHash ident); - void HandleSessionReadinessCheckTimer (const boost::system::error_code& ecode); - void SendSessionCreateReplyOk (); + void Connect (std::shared_ptr remote); + void HandleConnectLeaseSetRequestComplete (std::shared_ptr leaseSet); + void SendNamingLookupReply (const i2p::data::IdentityEx& identity); + void HandleNamingLookupLeaseSetRequestComplete (std::shared_ptr leaseSet, i2p::data::IdentHash ident); + void HandleSessionReadinessCheckTimer (const boost::system::error_code& ecode); + void SendSessionCreateReplyOk (); - private: + private: - SAMBridge& m_Owner; - boost::asio::ip::tcp::socket m_Socket; - boost::asio::deadline_timer m_Timer; - char m_Buffer[SAM_SOCKET_BUFFER_SIZE + 1]; - size_t m_BufferOffset; - uint8_t m_StreamBuffer[SAM_SOCKET_BUFFER_SIZE]; - SAMSocketType m_SocketType; - std::string m_ID; // nickname - bool m_IsSilent; - std::shared_ptr m_Stream; - SAMSession * m_Session; - }; + SAMBridge& m_Owner; + boost::asio::ip::tcp::socket m_Socket; + boost::asio::deadline_timer m_Timer; + char m_Buffer[SAM_SOCKET_BUFFER_SIZE + 1]; + size_t m_BufferOffset; + uint8_t m_StreamBuffer[SAM_SOCKET_BUFFER_SIZE]; + SAMSocketType m_SocketType; + std::string m_ID; // nickname + bool m_IsSilent; + std::shared_ptr m_Stream; + SAMSession * m_Session; + }; - struct SAMSession - { - std::shared_ptr localDestination; - std::list > sockets; - - SAMSession (std::shared_ptr dest); - ~SAMSession (); + struct SAMSession + { + std::shared_ptr localDestination; + std::list > sockets; + + SAMSession (std::shared_ptr dest); + ~SAMSession (); - void CloseStreams (); - }; + void CloseStreams (); + }; - class SAMBridge - { - public: + class SAMBridge + { + public: - SAMBridge (int port); - ~SAMBridge (); + SAMBridge (int port); + ~SAMBridge (); - void Start (); - void Stop (); - - boost::asio::io_service& GetService () { return m_Service; }; - SAMSession * CreateSession (const std::string& id, const std::string& destination, // empty string means transient - const std::map * params); - void CloseSession (const std::string& id); - SAMSession * FindSession (const std::string& id) const; + void Start (); + void Stop (); + + boost::asio::io_service& GetService () { return m_Service; }; + SAMSession * CreateSession (const std::string& id, const std::string& destination, // empty string means transient + const std::map * params); + void CloseSession (const std::string& id); + SAMSession * FindSession (const std::string& id) const; - private: + private: - void Run (); + void Run (); - void Accept (); - void HandleAccept(const boost::system::error_code& ecode, std::shared_ptr socket); + void Accept (); + void HandleAccept(const boost::system::error_code& ecode, std::shared_ptr socket); - void ReceiveDatagram (); - void HandleReceivedDatagram (const boost::system::error_code& ecode, std::size_t bytes_transferred); + void ReceiveDatagram (); + void HandleReceivedDatagram (const boost::system::error_code& ecode, std::size_t bytes_transferred); - private: + private: - bool m_IsRunning; - std::thread * m_Thread; - boost::asio::io_service m_Service; - boost::asio::ip::tcp::acceptor m_Acceptor; - boost::asio::ip::udp::endpoint m_DatagramEndpoint, m_SenderEndpoint; - boost::asio::ip::udp::socket m_DatagramSocket; - mutable std::mutex m_SessionsMutex; - std::map m_Sessions; - uint8_t m_DatagramReceiveBuffer[i2p::datagram::MAX_DATAGRAM_SIZE+1]; + bool m_IsRunning; + std::thread * m_Thread; + boost::asio::io_service m_Service; + boost::asio::ip::tcp::acceptor m_Acceptor; + boost::asio::ip::udp::endpoint m_DatagramEndpoint, m_SenderEndpoint; + boost::asio::ip::udp::socket m_DatagramSocket; + mutable std::mutex m_SessionsMutex; + std::map m_Sessions; + uint8_t m_DatagramReceiveBuffer[i2p::datagram::MAX_DATAGRAM_SIZE+1]; - public: + public: - // for HTTP - const decltype(m_Sessions)& GetSessions () const { return m_Sessions; }; - }; + // for HTTP + const decltype(m_Sessions)& GetSessions () const { return m_Sessions; }; + }; } } diff --git a/SOCKS.cpp b/SOCKS.cpp index 8cf85bbd..7d0d020e 100644 --- a/SOCKS.cpp +++ b/SOCKS.cpp @@ -14,561 +14,561 @@ namespace i2p { namespace proxy { - static const size_t socks_buffer_size = 8192; - static const size_t max_socks_hostname_size = 255; // Limit for socks5 and bad idea to traverse + static const size_t socks_buffer_size = 8192; + static const size_t max_socks_hostname_size = 255; // Limit for socks5 and bad idea to traverse - struct SOCKSDnsAddress - { - uint8_t size; - char value[max_socks_hostname_size]; - void FromString (std::string str) - { - size = str.length(); - if (str.length() > max_socks_hostname_size) size = max_socks_hostname_size; - memcpy(value,str.c_str(),size); - } - std::string ToString() { return std::string(value, size); } - void push_back (char c) { value[size++] = c; } - }; + struct SOCKSDnsAddress + { + uint8_t size; + char value[max_socks_hostname_size]; + void FromString (std::string str) + { + size = str.length(); + if (str.length() > max_socks_hostname_size) size = max_socks_hostname_size; + memcpy(value,str.c_str(),size); + } + std::string ToString() { return std::string(value, size); } + void push_back (char c) { value[size++] = c; } + }; - class SOCKSServer; - class SOCKSHandler: public i2p::client::I2PServiceHandler, public std::enable_shared_from_this - { - private: - enum state - { - GET_SOCKSV, - GET_COMMAND, - GET_PORT, - GET_IPV4, - GET4_IDENT, - GET4A_HOST, - GET5_AUTHNUM, - GET5_AUTH, - GET5_REQUESTV, - GET5_GETRSV, - GET5_GETADDRTYPE, - GET5_IPV6, - GET5_HOST_SIZE, - GET5_HOST, - DONE - }; - enum authMethods - { - AUTH_NONE = 0, //No authentication, skip to next step - AUTH_GSSAPI = 1, //GSSAPI authentication - AUTH_USERPASSWD = 2, //Username and password - AUTH_UNACCEPTABLE = 0xff //No acceptable method found - }; - enum addrTypes - { - ADDR_IPV4 = 1, //IPv4 address (4 octets) - ADDR_DNS = 3, // DNS name (up to 255 octets) - ADDR_IPV6 = 4 //IPV6 address (16 octets) - }; - enum errTypes - { - SOCKS5_OK = 0, // No error for SOCKS5 - SOCKS5_GEN_FAIL = 1, // General server failure - SOCKS5_RULE_DENIED = 2, // Connection disallowed by ruleset - SOCKS5_NET_UNREACH = 3, // Network unreachable - SOCKS5_HOST_UNREACH = 4, // Host unreachable - SOCKS5_CONN_REFUSED = 5, // Connection refused by the peer - SOCKS5_TTL_EXPIRED = 6, // TTL Expired - SOCKS5_CMD_UNSUP = 7, // Command unsuported - SOCKS5_ADDR_UNSUP = 8, // Address type unsuported - SOCKS4_OK = 90, // No error for SOCKS4 - SOCKS4_FAIL = 91, // Failed establishing connecting or not allowed - SOCKS4_IDENTD_MISSING = 92, // Couldn't connect to the identd server - SOCKS4_IDENTD_DIFFER = 93 // The ID reported by the application and by identd differ - }; - enum cmdTypes - { - CMD_CONNECT = 1, // TCP Connect - CMD_BIND = 2, // TCP Bind - CMD_UDP = 3 // UDP associate - }; - enum socksVersions - { - SOCKS4 = 4, // SOCKS4 - SOCKS5 = 5 // SOCKS5 - }; - union address - { - uint32_t ip; - SOCKSDnsAddress dns; - uint8_t ipv6[16]; - }; + class SOCKSServer; + class SOCKSHandler: public i2p::client::I2PServiceHandler, public std::enable_shared_from_this + { + private: + enum state + { + GET_SOCKSV, + GET_COMMAND, + GET_PORT, + GET_IPV4, + GET4_IDENT, + GET4A_HOST, + GET5_AUTHNUM, + GET5_AUTH, + GET5_REQUESTV, + GET5_GETRSV, + GET5_GETADDRTYPE, + GET5_IPV6, + GET5_HOST_SIZE, + GET5_HOST, + DONE + }; + enum authMethods + { + AUTH_NONE = 0, //No authentication, skip to next step + AUTH_GSSAPI = 1, //GSSAPI authentication + AUTH_USERPASSWD = 2, //Username and password + AUTH_UNACCEPTABLE = 0xff //No acceptable method found + }; + enum addrTypes + { + ADDR_IPV4 = 1, //IPv4 address (4 octets) + ADDR_DNS = 3, // DNS name (up to 255 octets) + ADDR_IPV6 = 4 //IPV6 address (16 octets) + }; + enum errTypes + { + SOCKS5_OK = 0, // No error for SOCKS5 + SOCKS5_GEN_FAIL = 1, // General server failure + SOCKS5_RULE_DENIED = 2, // Connection disallowed by ruleset + SOCKS5_NET_UNREACH = 3, // Network unreachable + SOCKS5_HOST_UNREACH = 4, // Host unreachable + SOCKS5_CONN_REFUSED = 5, // Connection refused by the peer + SOCKS5_TTL_EXPIRED = 6, // TTL Expired + SOCKS5_CMD_UNSUP = 7, // Command unsuported + SOCKS5_ADDR_UNSUP = 8, // Address type unsuported + SOCKS4_OK = 90, // No error for SOCKS4 + SOCKS4_FAIL = 91, // Failed establishing connecting or not allowed + SOCKS4_IDENTD_MISSING = 92, // Couldn't connect to the identd server + SOCKS4_IDENTD_DIFFER = 93 // The ID reported by the application and by identd differ + }; + enum cmdTypes + { + CMD_CONNECT = 1, // TCP Connect + CMD_BIND = 2, // TCP Bind + CMD_UDP = 3 // UDP associate + }; + enum socksVersions + { + SOCKS4 = 4, // SOCKS4 + SOCKS5 = 5 // SOCKS5 + }; + union address + { + uint32_t ip; + SOCKSDnsAddress dns; + uint8_t ipv6[16]; + }; - void EnterState(state nstate, uint8_t parseleft = 1); - bool HandleData(uint8_t *sock_buff, std::size_t len); - bool ValidateSOCKSRequest(); - void HandleSockRecv(const boost::system::error_code & ecode, std::size_t bytes_transfered); - void Terminate(); - void AsyncSockRead(); - boost::asio::const_buffers_1 GenerateSOCKS5SelectAuth(authMethods method); - boost::asio::const_buffers_1 GenerateSOCKS4Response(errTypes error, uint32_t ip, uint16_t port); - boost::asio::const_buffers_1 GenerateSOCKS5Response(errTypes error, addrTypes type, const address &addr, uint16_t port); - bool Socks5ChooseAuth(); - void SocksRequestFailed(errTypes error); - void SocksRequestSuccess(); - void SentSocksFailed(const boost::system::error_code & ecode); - void SentSocksDone(const boost::system::error_code & ecode); - void SentSocksResponse(const boost::system::error_code & ecode); - void HandleStreamRequestComplete (std::shared_ptr stream); + void EnterState(state nstate, uint8_t parseleft = 1); + bool HandleData(uint8_t *sock_buff, std::size_t len); + bool ValidateSOCKSRequest(); + void HandleSockRecv(const boost::system::error_code & ecode, std::size_t bytes_transfered); + void Terminate(); + void AsyncSockRead(); + boost::asio::const_buffers_1 GenerateSOCKS5SelectAuth(authMethods method); + boost::asio::const_buffers_1 GenerateSOCKS4Response(errTypes error, uint32_t ip, uint16_t port); + boost::asio::const_buffers_1 GenerateSOCKS5Response(errTypes error, addrTypes type, const address &addr, uint16_t port); + bool Socks5ChooseAuth(); + void SocksRequestFailed(errTypes error); + void SocksRequestSuccess(); + void SentSocksFailed(const boost::system::error_code & ecode); + void SentSocksDone(const boost::system::error_code & ecode); + void SentSocksResponse(const boost::system::error_code & ecode); + void HandleStreamRequestComplete (std::shared_ptr stream); - uint8_t m_sock_buff[socks_buffer_size]; - std::shared_ptr m_sock; - std::shared_ptr m_stream; - uint8_t *m_remaining_data; //Data left to be sent - uint8_t m_response[7+max_socks_hostname_size]; - address m_address; //Address - std::size_t m_remaining_data_len; //Size of the data left to be sent - uint32_t m_4aip; //Used in 4a requests - uint16_t m_port; - uint8_t m_command; - uint8_t m_parseleft; //Octets left to parse - authMethods m_authchosen; //Authentication chosen - addrTypes m_addrtype; //Address type chosen - socksVersions m_socksv; //Socks version - cmdTypes m_cmd; // Command requested - state m_state; + uint8_t m_sock_buff[socks_buffer_size]; + std::shared_ptr m_sock; + std::shared_ptr m_stream; + uint8_t *m_remaining_data; //Data left to be sent + uint8_t m_response[7+max_socks_hostname_size]; + address m_address; //Address + std::size_t m_remaining_data_len; //Size of the data left to be sent + uint32_t m_4aip; //Used in 4a requests + uint16_t m_port; + uint8_t m_command; + uint8_t m_parseleft; //Octets left to parse + authMethods m_authchosen; //Authentication chosen + addrTypes m_addrtype; //Address type chosen + socksVersions m_socksv; //Socks version + cmdTypes m_cmd; // Command requested + state m_state; - public: - SOCKSHandler(SOCKSServer * parent, std::shared_ptr sock) : - I2PServiceHandler(parent), m_sock(sock), m_stream(nullptr), - m_authchosen(AUTH_UNACCEPTABLE), m_addrtype(ADDR_IPV4) - { m_address.ip = 0; EnterState(GET_SOCKSV); } - ~SOCKSHandler() { Terminate(); } - void Handle() { AsyncSockRead(); } - }; + public: + SOCKSHandler(SOCKSServer * parent, std::shared_ptr sock) : + I2PServiceHandler(parent), m_sock(sock), m_stream(nullptr), + m_authchosen(AUTH_UNACCEPTABLE), m_addrtype(ADDR_IPV4) + { m_address.ip = 0; EnterState(GET_SOCKSV); } + ~SOCKSHandler() { Terminate(); } + void Handle() { AsyncSockRead(); } + }; - void SOCKSHandler::AsyncSockRead() - { - LogPrint(eLogDebug,"--- SOCKS async sock read"); - if(m_sock) - m_sock->async_receive(boost::asio::buffer(m_sock_buff, socks_buffer_size), - std::bind(&SOCKSHandler::HandleSockRecv, shared_from_this(), - std::placeholders::_1, std::placeholders::_2)); - else - LogPrint(eLogError,"--- SOCKS no socket for read"); - } + void SOCKSHandler::AsyncSockRead() + { + LogPrint(eLogDebug,"--- SOCKS async sock read"); + if(m_sock) + m_sock->async_receive(boost::asio::buffer(m_sock_buff, socks_buffer_size), + std::bind(&SOCKSHandler::HandleSockRecv, shared_from_this(), + std::placeholders::_1, std::placeholders::_2)); + else + LogPrint(eLogError,"--- SOCKS no socket for read"); + } - void SOCKSHandler::Terminate() - { - if (Kill()) return; - if (m_sock) - { - LogPrint(eLogDebug,"--- SOCKS close sock"); - m_sock->close(); - m_sock = nullptr; - } - if (m_stream) - { - LogPrint(eLogDebug,"--- SOCKS close stream"); - m_stream.reset (); - } - Done(shared_from_this()); - } + void SOCKSHandler::Terminate() + { + if (Kill()) return; + if (m_sock) + { + LogPrint(eLogDebug,"--- SOCKS close sock"); + m_sock->close(); + m_sock = nullptr; + } + if (m_stream) + { + LogPrint(eLogDebug,"--- SOCKS close stream"); + m_stream.reset (); + } + Done(shared_from_this()); + } - boost::asio::const_buffers_1 SOCKSHandler::GenerateSOCKS4Response(SOCKSHandler::errTypes error, uint32_t ip, uint16_t port) - { - assert(error >= SOCKS4_OK); - m_response[0] = '\x00'; //Version - m_response[1] = error; //Response code - htobe16buf(m_response+2,port); //Port - htobe32buf(m_response+4,ip); //IP - return boost::asio::const_buffers_1(m_response,8); - } + boost::asio::const_buffers_1 SOCKSHandler::GenerateSOCKS4Response(SOCKSHandler::errTypes error, uint32_t ip, uint16_t port) + { + assert(error >= SOCKS4_OK); + m_response[0] = '\x00'; //Version + m_response[1] = error; //Response code + htobe16buf(m_response+2,port); //Port + htobe32buf(m_response+4,ip); //IP + return boost::asio::const_buffers_1(m_response,8); + } - boost::asio::const_buffers_1 SOCKSHandler::GenerateSOCKS5Response(SOCKSHandler::errTypes error, SOCKSHandler::addrTypes type, const SOCKSHandler::address &addr, uint16_t port) - { - size_t size = 6; - assert(error <= SOCKS5_ADDR_UNSUP); - m_response[0] = '\x05'; //Version - m_response[1] = error; //Response code - m_response[2] = '\x00'; //RSV - m_response[3] = type; //Address type - switch (type) - { - case ADDR_IPV4: - size = 10; - htobe32buf(m_response+4,addr.ip); - break; - case ADDR_IPV6: - size = 22; - memcpy(m_response+4,addr.ipv6, 16); - break; - case ADDR_DNS: - size = 7+addr.dns.size; - m_response[4] = addr.dns.size; - memcpy(m_response+5,addr.dns.value, addr.dns.size); - break; - } - htobe16buf(m_response+size-2,port); //Port - return boost::asio::const_buffers_1(m_response,size); - } + boost::asio::const_buffers_1 SOCKSHandler::GenerateSOCKS5Response(SOCKSHandler::errTypes error, SOCKSHandler::addrTypes type, const SOCKSHandler::address &addr, uint16_t port) + { + size_t size = 6; + assert(error <= SOCKS5_ADDR_UNSUP); + m_response[0] = '\x05'; //Version + m_response[1] = error; //Response code + m_response[2] = '\x00'; //RSV + m_response[3] = type; //Address type + switch (type) + { + case ADDR_IPV4: + size = 10; + htobe32buf(m_response+4,addr.ip); + break; + case ADDR_IPV6: + size = 22; + memcpy(m_response+4,addr.ipv6, 16); + break; + case ADDR_DNS: + size = 7+addr.dns.size; + m_response[4] = addr.dns.size; + memcpy(m_response+5,addr.dns.value, addr.dns.size); + break; + } + htobe16buf(m_response+size-2,port); //Port + return boost::asio::const_buffers_1(m_response,size); + } - bool SOCKSHandler::Socks5ChooseAuth() - { - m_response[0] = '\x05'; //Version - m_response[1] = m_authchosen; //Response code - boost::asio::const_buffers_1 response(m_response,2); - if (m_authchosen == AUTH_UNACCEPTABLE) - { - LogPrint(eLogWarning,"--- SOCKS5 authentication negotiation failed"); - boost::asio::async_write(*m_sock, response, std::bind(&SOCKSHandler::SentSocksFailed, - shared_from_this(), std::placeholders::_1)); - return false; - } - else - { - LogPrint(eLogDebug,"--- SOCKS5 choosing authentication method: ", m_authchosen); - boost::asio::async_write(*m_sock, response, std::bind(&SOCKSHandler::SentSocksResponse, - shared_from_this(), std::placeholders::_1)); - return true; - } - } + bool SOCKSHandler::Socks5ChooseAuth() + { + m_response[0] = '\x05'; //Version + m_response[1] = m_authchosen; //Response code + boost::asio::const_buffers_1 response(m_response,2); + if (m_authchosen == AUTH_UNACCEPTABLE) + { + LogPrint(eLogWarning,"--- SOCKS5 authentication negotiation failed"); + boost::asio::async_write(*m_sock, response, std::bind(&SOCKSHandler::SentSocksFailed, + shared_from_this(), std::placeholders::_1)); + return false; + } + else + { + LogPrint(eLogDebug,"--- SOCKS5 choosing authentication method: ", m_authchosen); + boost::asio::async_write(*m_sock, response, std::bind(&SOCKSHandler::SentSocksResponse, + shared_from_this(), std::placeholders::_1)); + return true; + } + } - /* All hope is lost beyond this point */ - void SOCKSHandler::SocksRequestFailed(SOCKSHandler::errTypes error) - { - boost::asio::const_buffers_1 response(nullptr,0); - assert(error != SOCKS4_OK && error != SOCKS5_OK); - switch (m_socksv) - { - case SOCKS4: - LogPrint(eLogWarning,"--- SOCKS4 failed: ", error); - if (error < SOCKS4_OK) error = SOCKS4_FAIL; //Transparently map SOCKS5 errors - response = GenerateSOCKS4Response(error, m_4aip, m_port); - break; - case SOCKS5: - LogPrint(eLogWarning,"--- SOCKS5 failed: ", error); - response = GenerateSOCKS5Response(error, m_addrtype, m_address, m_port); - break; - } - boost::asio::async_write(*m_sock, response, std::bind(&SOCKSHandler::SentSocksFailed, - shared_from_this(), std::placeholders::_1)); - } + /* All hope is lost beyond this point */ + void SOCKSHandler::SocksRequestFailed(SOCKSHandler::errTypes error) + { + boost::asio::const_buffers_1 response(nullptr,0); + assert(error != SOCKS4_OK && error != SOCKS5_OK); + switch (m_socksv) + { + case SOCKS4: + LogPrint(eLogWarning,"--- SOCKS4 failed: ", error); + if (error < SOCKS4_OK) error = SOCKS4_FAIL; //Transparently map SOCKS5 errors + response = GenerateSOCKS4Response(error, m_4aip, m_port); + break; + case SOCKS5: + LogPrint(eLogWarning,"--- SOCKS5 failed: ", error); + response = GenerateSOCKS5Response(error, m_addrtype, m_address, m_port); + break; + } + boost::asio::async_write(*m_sock, response, std::bind(&SOCKSHandler::SentSocksFailed, + shared_from_this(), std::placeholders::_1)); + } - void SOCKSHandler::SocksRequestSuccess() - { - boost::asio::const_buffers_1 response(nullptr,0); - //TODO: this should depend on things like the command type and callbacks may change - switch (m_socksv) - { - case SOCKS4: - LogPrint(eLogInfo,"--- SOCKS4 connection success"); - response = GenerateSOCKS4Response(SOCKS4_OK, m_4aip, m_port); - break; - case SOCKS5: - LogPrint(eLogInfo,"--- SOCKS5 connection success"); - auto s = i2p::client::context.GetAddressBook().ToAddress(GetOwner()->GetLocalDestination()->GetIdentHash()); - address ad; ad.dns.FromString(s); - //HACK only 16 bits passed in port as SOCKS5 doesn't allow for more - response = GenerateSOCKS5Response(SOCKS5_OK, ADDR_DNS, ad, m_stream->GetRecvStreamID()); - break; - } - boost::asio::async_write(*m_sock, response, std::bind(&SOCKSHandler::SentSocksDone, - shared_from_this(), std::placeholders::_1)); - } + void SOCKSHandler::SocksRequestSuccess() + { + boost::asio::const_buffers_1 response(nullptr,0); + //TODO: this should depend on things like the command type and callbacks may change + switch (m_socksv) + { + case SOCKS4: + LogPrint(eLogInfo,"--- SOCKS4 connection success"); + response = GenerateSOCKS4Response(SOCKS4_OK, m_4aip, m_port); + break; + case SOCKS5: + LogPrint(eLogInfo,"--- SOCKS5 connection success"); + auto s = i2p::client::context.GetAddressBook().ToAddress(GetOwner()->GetLocalDestination()->GetIdentHash()); + address ad; ad.dns.FromString(s); + //HACK only 16 bits passed in port as SOCKS5 doesn't allow for more + response = GenerateSOCKS5Response(SOCKS5_OK, ADDR_DNS, ad, m_stream->GetRecvStreamID()); + break; + } + boost::asio::async_write(*m_sock, response, std::bind(&SOCKSHandler::SentSocksDone, + shared_from_this(), std::placeholders::_1)); + } - void SOCKSHandler::EnterState(SOCKSHandler::state nstate, uint8_t parseleft) { - switch (nstate) - { - case GET_PORT: parseleft = 2; break; - case GET_IPV4: m_addrtype = ADDR_IPV4; m_address.ip = 0; parseleft = 4; break; - case GET4_IDENT: m_4aip = m_address.ip; break; - case GET4A_HOST: - case GET5_HOST: m_addrtype = ADDR_DNS; m_address.dns.size = 0; break; - case GET5_IPV6: m_addrtype = ADDR_IPV6; parseleft = 16; break; - default:; - } - m_parseleft = parseleft; - m_state = nstate; - } + void SOCKSHandler::EnterState(SOCKSHandler::state nstate, uint8_t parseleft) { + switch (nstate) + { + case GET_PORT: parseleft = 2; break; + case GET_IPV4: m_addrtype = ADDR_IPV4; m_address.ip = 0; parseleft = 4; break; + case GET4_IDENT: m_4aip = m_address.ip; break; + case GET4A_HOST: + case GET5_HOST: m_addrtype = ADDR_DNS; m_address.dns.size = 0; break; + case GET5_IPV6: m_addrtype = ADDR_IPV6; parseleft = 16; break; + default:; + } + m_parseleft = parseleft; + m_state = nstate; + } - bool SOCKSHandler::ValidateSOCKSRequest() - { - if ( m_cmd != CMD_CONNECT ) - { - //TODO: we need to support binds and other shit! - LogPrint(eLogError,"--- SOCKS unsupported command: ", m_cmd); - SocksRequestFailed(SOCKS5_CMD_UNSUP); - return false; - } - //TODO: we may want to support other address types! - if ( m_addrtype != ADDR_DNS ) - { - switch (m_socksv) - { - case SOCKS5: - LogPrint(eLogError,"--- SOCKS5 unsupported address type: ", m_addrtype); - break; - case SOCKS4: - LogPrint(eLogError,"--- SOCKS4a rejected because it's actually SOCKS4"); - break; - } - SocksRequestFailed(SOCKS5_ADDR_UNSUP); - return false; - } - //TODO: we may want to support other domains - if(m_addrtype == ADDR_DNS && m_address.dns.ToString().find(".i2p") == std::string::npos) - { - LogPrint(eLogError,"--- SOCKS invalid hostname: ", m_address.dns.ToString()); - SocksRequestFailed(SOCKS5_ADDR_UNSUP); - return false; - } - return true; - } + bool SOCKSHandler::ValidateSOCKSRequest() + { + if ( m_cmd != CMD_CONNECT ) + { + //TODO: we need to support binds and other shit! + LogPrint(eLogError,"--- SOCKS unsupported command: ", m_cmd); + SocksRequestFailed(SOCKS5_CMD_UNSUP); + return false; + } + //TODO: we may want to support other address types! + if ( m_addrtype != ADDR_DNS ) + { + switch (m_socksv) + { + case SOCKS5: + LogPrint(eLogError,"--- SOCKS5 unsupported address type: ", m_addrtype); + break; + case SOCKS4: + LogPrint(eLogError,"--- SOCKS4a rejected because it's actually SOCKS4"); + break; + } + SocksRequestFailed(SOCKS5_ADDR_UNSUP); + return false; + } + //TODO: we may want to support other domains + if(m_addrtype == ADDR_DNS && m_address.dns.ToString().find(".i2p") == std::string::npos) + { + LogPrint(eLogError,"--- SOCKS invalid hostname: ", m_address.dns.ToString()); + SocksRequestFailed(SOCKS5_ADDR_UNSUP); + return false; + } + return true; + } - bool SOCKSHandler::HandleData(uint8_t *sock_buff, std::size_t len) - { - assert(len); // This should always be called with a least a byte left to parse - while (len > 0) - { - switch (m_state) - { - case GET_SOCKSV: - m_socksv = (SOCKSHandler::socksVersions) *sock_buff; - switch (*sock_buff) - { - case SOCKS4: - EnterState(GET_COMMAND); //Initialize the parser at the right position - break; - case SOCKS5: - EnterState(GET5_AUTHNUM); //Initialize the parser at the right position - break; - default: - LogPrint(eLogError,"--- SOCKS rejected invalid version: ", ((int)*sock_buff)); - Terminate(); - return false; - } - break; - case GET5_AUTHNUM: - EnterState(GET5_AUTH, *sock_buff); - break; - case GET5_AUTH: - m_parseleft --; - if (*sock_buff == AUTH_NONE) - m_authchosen = AUTH_NONE; - if ( m_parseleft == 0 ) - { - if (!Socks5ChooseAuth()) return false; - EnterState(GET5_REQUESTV); - } - break; - case GET_COMMAND: - switch (*sock_buff) - { - case CMD_CONNECT: - case CMD_BIND: - break; - case CMD_UDP: - if (m_socksv == SOCKS5) break; - default: - LogPrint(eLogError,"--- SOCKS invalid command: ", ((int)*sock_buff)); - SocksRequestFailed(SOCKS5_GEN_FAIL); - return false; - } - m_cmd = (SOCKSHandler::cmdTypes)*sock_buff; - switch (m_socksv) - { - case SOCKS5: EnterState(GET5_GETRSV); break; - case SOCKS4: EnterState(GET_PORT); break; - } - break; - case GET_PORT: - m_port = (m_port << 8)|((uint16_t)*sock_buff); - m_parseleft--; - if (m_parseleft == 0) - { - switch (m_socksv) - { - case SOCKS5: EnterState(DONE); break; - case SOCKS4: EnterState(GET_IPV4); break; - } - } - break; - case GET_IPV4: - m_address.ip = (m_address.ip << 8)|((uint32_t)*sock_buff); - m_parseleft--; - if (m_parseleft == 0) - { - switch (m_socksv) - { - case SOCKS5: EnterState(GET_PORT); break; - case SOCKS4: EnterState(GET4_IDENT); m_4aip = m_address.ip; break; - } - } - break; - case GET4_IDENT: - if (!*sock_buff) - { - if( m_4aip == 0 || m_4aip > 255 ) - EnterState(DONE); - else - EnterState(GET4A_HOST); - } - break; - case GET4A_HOST: - if (!*sock_buff) - { - EnterState(DONE); - break; - } - if (m_address.dns.size >= max_socks_hostname_size) - { - LogPrint(eLogError,"--- SOCKS4a destination is too large"); - SocksRequestFailed(SOCKS4_FAIL); - return false; - } - m_address.dns.push_back(*sock_buff); - break; - case GET5_REQUESTV: - if (*sock_buff != SOCKS5) - { - LogPrint(eLogError,"--- SOCKS5 rejected unknown request version: ", ((int)*sock_buff)); - SocksRequestFailed(SOCKS5_GEN_FAIL); - return false; - } - EnterState(GET_COMMAND); - break; - case GET5_GETRSV: - if ( *sock_buff != 0 ) - { - LogPrint(eLogError,"--- SOCKS5 unknown reserved field: ", ((int)*sock_buff)); - SocksRequestFailed(SOCKS5_GEN_FAIL); - return false; - } - EnterState(GET5_GETADDRTYPE); - break; - case GET5_GETADDRTYPE: - switch (*sock_buff) - { - case ADDR_IPV4: EnterState(GET_IPV4); break; - case ADDR_IPV6: EnterState(GET5_IPV6); break; - case ADDR_DNS : EnterState(GET5_HOST_SIZE); break; - default: - LogPrint(eLogError,"--- SOCKS5 unknown address type: ", ((int)*sock_buff)); - SocksRequestFailed(SOCKS5_GEN_FAIL); - return false; - } - break; - case GET5_IPV6: - m_address.ipv6[16-m_parseleft] = *sock_buff; - m_parseleft--; - if (m_parseleft == 0) EnterState(GET_PORT); - break; - case GET5_HOST_SIZE: - EnterState(GET5_HOST, *sock_buff); - break; - case GET5_HOST: - m_address.dns.push_back(*sock_buff); - m_parseleft--; - if (m_parseleft == 0) EnterState(GET_PORT); - break; - default: - LogPrint(eLogError,"--- SOCKS parse state?? ", m_state); - Terminate(); - return false; - } - sock_buff++; - len--; - if (m_state == DONE) - { - m_remaining_data_len = len; - m_remaining_data = sock_buff; - return ValidateSOCKSRequest(); - } - } - return true; - } + bool SOCKSHandler::HandleData(uint8_t *sock_buff, std::size_t len) + { + assert(len); // This should always be called with a least a byte left to parse + while (len > 0) + { + switch (m_state) + { + case GET_SOCKSV: + m_socksv = (SOCKSHandler::socksVersions) *sock_buff; + switch (*sock_buff) + { + case SOCKS4: + EnterState(GET_COMMAND); //Initialize the parser at the right position + break; + case SOCKS5: + EnterState(GET5_AUTHNUM); //Initialize the parser at the right position + break; + default: + LogPrint(eLogError,"--- SOCKS rejected invalid version: ", ((int)*sock_buff)); + Terminate(); + return false; + } + break; + case GET5_AUTHNUM: + EnterState(GET5_AUTH, *sock_buff); + break; + case GET5_AUTH: + m_parseleft --; + if (*sock_buff == AUTH_NONE) + m_authchosen = AUTH_NONE; + if ( m_parseleft == 0 ) + { + if (!Socks5ChooseAuth()) return false; + EnterState(GET5_REQUESTV); + } + break; + case GET_COMMAND: + switch (*sock_buff) + { + case CMD_CONNECT: + case CMD_BIND: + break; + case CMD_UDP: + if (m_socksv == SOCKS5) break; + default: + LogPrint(eLogError,"--- SOCKS invalid command: ", ((int)*sock_buff)); + SocksRequestFailed(SOCKS5_GEN_FAIL); + return false; + } + m_cmd = (SOCKSHandler::cmdTypes)*sock_buff; + switch (m_socksv) + { + case SOCKS5: EnterState(GET5_GETRSV); break; + case SOCKS4: EnterState(GET_PORT); break; + } + break; + case GET_PORT: + m_port = (m_port << 8)|((uint16_t)*sock_buff); + m_parseleft--; + if (m_parseleft == 0) + { + switch (m_socksv) + { + case SOCKS5: EnterState(DONE); break; + case SOCKS4: EnterState(GET_IPV4); break; + } + } + break; + case GET_IPV4: + m_address.ip = (m_address.ip << 8)|((uint32_t)*sock_buff); + m_parseleft--; + if (m_parseleft == 0) + { + switch (m_socksv) + { + case SOCKS5: EnterState(GET_PORT); break; + case SOCKS4: EnterState(GET4_IDENT); m_4aip = m_address.ip; break; + } + } + break; + case GET4_IDENT: + if (!*sock_buff) + { + if( m_4aip == 0 || m_4aip > 255 ) + EnterState(DONE); + else + EnterState(GET4A_HOST); + } + break; + case GET4A_HOST: + if (!*sock_buff) + { + EnterState(DONE); + break; + } + if (m_address.dns.size >= max_socks_hostname_size) + { + LogPrint(eLogError,"--- SOCKS4a destination is too large"); + SocksRequestFailed(SOCKS4_FAIL); + return false; + } + m_address.dns.push_back(*sock_buff); + break; + case GET5_REQUESTV: + if (*sock_buff != SOCKS5) + { + LogPrint(eLogError,"--- SOCKS5 rejected unknown request version: ", ((int)*sock_buff)); + SocksRequestFailed(SOCKS5_GEN_FAIL); + return false; + } + EnterState(GET_COMMAND); + break; + case GET5_GETRSV: + if ( *sock_buff != 0 ) + { + LogPrint(eLogError,"--- SOCKS5 unknown reserved field: ", ((int)*sock_buff)); + SocksRequestFailed(SOCKS5_GEN_FAIL); + return false; + } + EnterState(GET5_GETADDRTYPE); + break; + case GET5_GETADDRTYPE: + switch (*sock_buff) + { + case ADDR_IPV4: EnterState(GET_IPV4); break; + case ADDR_IPV6: EnterState(GET5_IPV6); break; + case ADDR_DNS : EnterState(GET5_HOST_SIZE); break; + default: + LogPrint(eLogError,"--- SOCKS5 unknown address type: ", ((int)*sock_buff)); + SocksRequestFailed(SOCKS5_GEN_FAIL); + return false; + } + break; + case GET5_IPV6: + m_address.ipv6[16-m_parseleft] = *sock_buff; + m_parseleft--; + if (m_parseleft == 0) EnterState(GET_PORT); + break; + case GET5_HOST_SIZE: + EnterState(GET5_HOST, *sock_buff); + break; + case GET5_HOST: + m_address.dns.push_back(*sock_buff); + m_parseleft--; + if (m_parseleft == 0) EnterState(GET_PORT); + break; + default: + LogPrint(eLogError,"--- SOCKS parse state?? ", m_state); + Terminate(); + return false; + } + sock_buff++; + len--; + if (m_state == DONE) + { + m_remaining_data_len = len; + m_remaining_data = sock_buff; + return ValidateSOCKSRequest(); + } + } + return true; + } - void SOCKSHandler::HandleSockRecv(const boost::system::error_code & ecode, std::size_t len) - { - LogPrint(eLogDebug,"--- SOCKS sock recv: ", len); - if(ecode) - { - LogPrint(eLogWarning," --- SOCKS sock recv got error: ", ecode); + void SOCKSHandler::HandleSockRecv(const boost::system::error_code & ecode, std::size_t len) + { + LogPrint(eLogDebug,"--- SOCKS sock recv: ", len); + if(ecode) + { + LogPrint(eLogWarning," --- SOCKS sock recv got error: ", ecode); Terminate(); - return; - } + return; + } - if (HandleData(m_sock_buff, len)) - { - if (m_state == DONE) - { - LogPrint(eLogInfo,"--- SOCKS requested ", m_address.dns.ToString(), ":" , m_port); - GetOwner()->CreateStream ( std::bind (&SOCKSHandler::HandleStreamRequestComplete, - shared_from_this(), std::placeholders::_1), m_address.dns.ToString(), m_port); - } - else - AsyncSockRead(); - } - } + if (HandleData(m_sock_buff, len)) + { + if (m_state == DONE) + { + LogPrint(eLogInfo,"--- SOCKS requested ", m_address.dns.ToString(), ":" , m_port); + GetOwner()->CreateStream ( std::bind (&SOCKSHandler::HandleStreamRequestComplete, + shared_from_this(), std::placeholders::_1), m_address.dns.ToString(), m_port); + } + else + AsyncSockRead(); + } + } - void SOCKSHandler::SentSocksFailed(const boost::system::error_code & ecode) - { - if (!ecode) - Terminate(); - else - { - LogPrint (eLogError,"--- SOCKS Closing socket after sending failure because: ", ecode.message ()); - Terminate(); - } - } + void SOCKSHandler::SentSocksFailed(const boost::system::error_code & ecode) + { + if (!ecode) + Terminate(); + else + { + LogPrint (eLogError,"--- SOCKS Closing socket after sending failure because: ", ecode.message ()); + Terminate(); + } + } - void SOCKSHandler::SentSocksDone(const boost::system::error_code & ecode) - { - if (!ecode) - { - if (Kill()) return; - LogPrint (eLogInfo,"--- SOCKS New I2PTunnel connection"); - auto connection = std::make_shared(GetOwner(), m_sock, m_stream); - GetOwner()->AddHandler (connection); - connection->I2PConnect (m_remaining_data,m_remaining_data_len); - Done(shared_from_this()); - } - else - { - LogPrint (eLogError,"--- SOCKS Closing socket after completion reply because: ", ecode.message ()); - Terminate(); - } - } + void SOCKSHandler::SentSocksDone(const boost::system::error_code & ecode) + { + if (!ecode) + { + if (Kill()) return; + LogPrint (eLogInfo,"--- SOCKS New I2PTunnel connection"); + auto connection = std::make_shared(GetOwner(), m_sock, m_stream); + GetOwner()->AddHandler (connection); + connection->I2PConnect (m_remaining_data,m_remaining_data_len); + Done(shared_from_this()); + } + else + { + LogPrint (eLogError,"--- SOCKS Closing socket after completion reply because: ", ecode.message ()); + Terminate(); + } + } - void SOCKSHandler::SentSocksResponse(const boost::system::error_code & ecode) - { - if (ecode) - { - LogPrint (eLogError,"--- SOCKS Closing socket after sending reply because: ", ecode.message ()); - Terminate(); - } - } + void SOCKSHandler::SentSocksResponse(const boost::system::error_code & ecode) + { + if (ecode) + { + LogPrint (eLogError,"--- SOCKS Closing socket after sending reply because: ", ecode.message ()); + Terminate(); + } + } - void SOCKSHandler::HandleStreamRequestComplete (std::shared_ptr stream) - { - if (stream) - { - m_stream = stream; - SocksRequestSuccess(); - } - else - { - LogPrint (eLogError,"--- SOCKS Issue when creating the stream, check the previous warnings for more info."); - SocksRequestFailed(SOCKS5_HOST_UNREACH); - } - } + void SOCKSHandler::HandleStreamRequestComplete (std::shared_ptr stream) + { + if (stream) + { + m_stream = stream; + SocksRequestSuccess(); + } + else + { + LogPrint (eLogError,"--- SOCKS Issue when creating the stream, check the previous warnings for more info."); + SocksRequestFailed(SOCKS5_HOST_UNREACH); + } + } - SOCKSServer::SOCKSServer(int port, std::shared_ptr localDestination) : - TCPIPAcceptor (port, localDestination ? localDestination : i2p::client::context.GetSharedLocalDestination ()) - { - } - - std::shared_ptr SOCKSServer::CreateHandler(std::shared_ptr socket) - { - return std::make_shared (this, socket); - } + SOCKSServer::SOCKSServer(int port, std::shared_ptr localDestination) : + TCPIPAcceptor (port, localDestination ? localDestination : i2p::client::context.GetSharedLocalDestination ()) + { + } + + std::shared_ptr SOCKSServer::CreateHandler(std::shared_ptr socket) + { + return std::make_shared (this, socket); + } } } diff --git a/SOCKS.h b/SOCKS.h index 7854ce3a..87469c79 100644 --- a/SOCKS.h +++ b/SOCKS.h @@ -11,20 +11,20 @@ namespace i2p { namespace proxy { - class SOCKSServer: public i2p::client::TCPIPAcceptor - { - public: + class SOCKSServer: public i2p::client::TCPIPAcceptor + { + public: - SOCKSServer(int port, std::shared_ptr localDestination = nullptr); - ~SOCKSServer() {}; + SOCKSServer(int port, std::shared_ptr localDestination = nullptr); + ~SOCKSServer() {}; - protected: - // Implements TCPIPAcceptor - std::shared_ptr CreateHandler(std::shared_ptr socket); - const char* GetName() { return "SOCKS"; } - }; + protected: + // Implements TCPIPAcceptor + std::shared_ptr CreateHandler(std::shared_ptr socket); + const char* GetName() { return "SOCKS"; } + }; - typedef SOCKSServer SOCKSProxy; + typedef SOCKSServer SOCKSProxy; } } diff --git a/SSU.cpp b/SSU.cpp index 9c12441a..f294db17 100644 --- a/SSU.cpp +++ b/SSU.cpp @@ -10,547 +10,547 @@ namespace i2p { namespace transport { - SSUServer::SSUServer (int port): m_Thread (nullptr), m_ThreadV6 (nullptr), m_ReceiversThread (nullptr), - m_Work (m_Service), m_WorkV6 (m_ServiceV6), m_ReceiversWork (m_ReceiversService), - m_Endpoint (boost::asio::ip::udp::v4 (), port), m_EndpointV6 (boost::asio::ip::udp::v6 (), port), - m_Socket (m_ReceiversService, m_Endpoint), m_SocketV6 (m_ReceiversService), - m_IntroducersUpdateTimer (m_Service), m_PeerTestsCleanupTimer (m_Service) - { - m_Socket.set_option (boost::asio::socket_base::receive_buffer_size (65535)); - m_Socket.set_option (boost::asio::socket_base::send_buffer_size (65535)); - if (context.SupportsV6 ()) - { - m_SocketV6.open (boost::asio::ip::udp::v6()); - m_SocketV6.set_option (boost::asio::ip::v6_only (true)); - m_SocketV6.set_option (boost::asio::socket_base::receive_buffer_size (65535)); - m_SocketV6.set_option (boost::asio::socket_base::send_buffer_size (65535)); - m_SocketV6.bind (m_EndpointV6); - } - } - - SSUServer::~SSUServer () - { - } + SSUServer::SSUServer (int port): m_Thread (nullptr), m_ThreadV6 (nullptr), m_ReceiversThread (nullptr), + m_Work (m_Service), m_WorkV6 (m_ServiceV6), m_ReceiversWork (m_ReceiversService), + m_Endpoint (boost::asio::ip::udp::v4 (), port), m_EndpointV6 (boost::asio::ip::udp::v6 (), port), + m_Socket (m_ReceiversService, m_Endpoint), m_SocketV6 (m_ReceiversService), + m_IntroducersUpdateTimer (m_Service), m_PeerTestsCleanupTimer (m_Service) + { + m_Socket.set_option (boost::asio::socket_base::receive_buffer_size (65535)); + m_Socket.set_option (boost::asio::socket_base::send_buffer_size (65535)); + if (context.SupportsV6 ()) + { + m_SocketV6.open (boost::asio::ip::udp::v6()); + m_SocketV6.set_option (boost::asio::ip::v6_only (true)); + m_SocketV6.set_option (boost::asio::socket_base::receive_buffer_size (65535)); + m_SocketV6.set_option (boost::asio::socket_base::send_buffer_size (65535)); + m_SocketV6.bind (m_EndpointV6); + } + } + + SSUServer::~SSUServer () + { + } - void SSUServer::Start () - { - m_IsRunning = true; - m_ReceiversThread = new std::thread (std::bind (&SSUServer::RunReceivers, this)); - m_Thread = new std::thread (std::bind (&SSUServer::Run, this)); - m_ReceiversService.post (std::bind (&SSUServer::Receive, this)); - if (context.SupportsV6 ()) - { - m_ThreadV6 = new std::thread (std::bind (&SSUServer::RunV6, this)); - m_ReceiversService.post (std::bind (&SSUServer::ReceiveV6, this)); - } - SchedulePeerTestsCleanupTimer (); - ScheduleIntroducersUpdateTimer (); // wait for 30 seconds and decide if we need introducers - } + void SSUServer::Start () + { + m_IsRunning = true; + m_ReceiversThread = new std::thread (std::bind (&SSUServer::RunReceivers, this)); + m_Thread = new std::thread (std::bind (&SSUServer::Run, this)); + m_ReceiversService.post (std::bind (&SSUServer::Receive, this)); + if (context.SupportsV6 ()) + { + m_ThreadV6 = new std::thread (std::bind (&SSUServer::RunV6, this)); + m_ReceiversService.post (std::bind (&SSUServer::ReceiveV6, this)); + } + SchedulePeerTestsCleanupTimer (); + ScheduleIntroducersUpdateTimer (); // wait for 30 seconds and decide if we need introducers + } - void SSUServer::Stop () - { - DeleteAllSessions (); - m_IsRunning = false; - m_Service.stop (); - m_Socket.close (); - m_ServiceV6.stop (); - m_SocketV6.close (); - m_ReceiversService.stop (); - if (m_ReceiversThread) - { - m_ReceiversThread->join (); - delete m_ReceiversThread; - m_ReceiversThread = nullptr; - } - if (m_Thread) - { - m_Thread->join (); - delete m_Thread; - m_Thread = nullptr; - } - if (m_ThreadV6) - { - m_ThreadV6->join (); - delete m_ThreadV6; - m_ThreadV6 = nullptr; - } - } + void SSUServer::Stop () + { + DeleteAllSessions (); + m_IsRunning = false; + m_Service.stop (); + m_Socket.close (); + m_ServiceV6.stop (); + m_SocketV6.close (); + m_ReceiversService.stop (); + if (m_ReceiversThread) + { + m_ReceiversThread->join (); + delete m_ReceiversThread; + m_ReceiversThread = nullptr; + } + if (m_Thread) + { + m_Thread->join (); + delete m_Thread; + m_Thread = nullptr; + } + if (m_ThreadV6) + { + m_ThreadV6->join (); + delete m_ThreadV6; + m_ThreadV6 = nullptr; + } + } - void SSUServer::Run () - { - while (m_IsRunning) - { - try - { - m_Service.run (); - } - catch (std::exception& ex) - { - LogPrint (eLogError, "SSU server: ", ex.what ()); - } - } - } + void SSUServer::Run () + { + while (m_IsRunning) + { + try + { + m_Service.run (); + } + catch (std::exception& ex) + { + LogPrint (eLogError, "SSU server: ", ex.what ()); + } + } + } - void SSUServer::RunV6 () - { - while (m_IsRunning) - { - try - { - m_ServiceV6.run (); - } - catch (std::exception& ex) - { - LogPrint (eLogError, "SSU V6 server: ", ex.what ()); - } - } - } + void SSUServer::RunV6 () + { + while (m_IsRunning) + { + try + { + m_ServiceV6.run (); + } + catch (std::exception& ex) + { + LogPrint (eLogError, "SSU V6 server: ", ex.what ()); + } + } + } - void SSUServer::RunReceivers () - { - while (m_IsRunning) - { - try - { - m_ReceiversService.run (); - } - catch (std::exception& ex) - { - LogPrint (eLogError, "SSU receivers: ", ex.what ()); - } - } - } - - void SSUServer::AddRelay (uint32_t tag, const boost::asio::ip::udp::endpoint& relay) - { - m_Relays[tag] = relay; - } + void SSUServer::RunReceivers () + { + while (m_IsRunning) + { + try + { + m_ReceiversService.run (); + } + catch (std::exception& ex) + { + LogPrint (eLogError, "SSU receivers: ", ex.what ()); + } + } + } + + void SSUServer::AddRelay (uint32_t tag, const boost::asio::ip::udp::endpoint& relay) + { + m_Relays[tag] = relay; + } - std::shared_ptr SSUServer::FindRelaySession (uint32_t tag) - { - auto it = m_Relays.find (tag); - if (it != m_Relays.end ()) - return FindSession (it->second); - return nullptr; - } + std::shared_ptr SSUServer::FindRelaySession (uint32_t tag) + { + auto it = m_Relays.find (tag); + if (it != m_Relays.end ()) + return FindSession (it->second); + return nullptr; + } - void SSUServer::Send (const uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& to) - { - if (to.protocol () == boost::asio::ip::udp::v4()) - m_Socket.send_to (boost::asio::buffer (buf, len), to); - else - m_SocketV6.send_to (boost::asio::buffer (buf, len), to); - } + void SSUServer::Send (const uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& to) + { + if (to.protocol () == boost::asio::ip::udp::v4()) + m_Socket.send_to (boost::asio::buffer (buf, len), to); + else + m_SocketV6.send_to (boost::asio::buffer (buf, len), to); + } - void SSUServer::Receive () - { - SSUPacket * packet = new SSUPacket (); - m_Socket.async_receive_from (boost::asio::buffer (packet->buf, SSU_MTU_V4), packet->from, - std::bind (&SSUServer::HandleReceivedFrom, this, std::placeholders::_1, std::placeholders::_2, packet)); - } + void SSUServer::Receive () + { + SSUPacket * packet = new SSUPacket (); + m_Socket.async_receive_from (boost::asio::buffer (packet->buf, SSU_MTU_V4), packet->from, + std::bind (&SSUServer::HandleReceivedFrom, this, std::placeholders::_1, std::placeholders::_2, packet)); + } - void SSUServer::ReceiveV6 () - { - SSUPacket * packet = new SSUPacket (); - m_SocketV6.async_receive_from (boost::asio::buffer (packet->buf, SSU_MTU_V6), packet->from, - std::bind (&SSUServer::HandleReceivedFromV6, this, std::placeholders::_1, std::placeholders::_2, packet)); - } + void SSUServer::ReceiveV6 () + { + SSUPacket * packet = new SSUPacket (); + m_SocketV6.async_receive_from (boost::asio::buffer (packet->buf, SSU_MTU_V6), packet->from, + std::bind (&SSUServer::HandleReceivedFromV6, this, std::placeholders::_1, std::placeholders::_2, packet)); + } - void SSUServer::HandleReceivedFrom (const boost::system::error_code& ecode, std::size_t bytes_transferred, SSUPacket * packet) - { - if (!ecode) - { - packet->len = bytes_transferred; - std::vector packets; - packets.push_back (packet); + void SSUServer::HandleReceivedFrom (const boost::system::error_code& ecode, std::size_t bytes_transferred, SSUPacket * packet) + { + if (!ecode) + { + packet->len = bytes_transferred; + std::vector packets; + packets.push_back (packet); - boost::system::error_code ec; - size_t moreBytes = m_Socket.available(ec); - while (moreBytes && packets.size () < 25) - { - packet = new SSUPacket (); - packet->len = m_Socket.receive_from (boost::asio::buffer (packet->buf, SSU_MTU_V4), packet->from); - packets.push_back (packet); - moreBytes = m_Socket.available(); - } + boost::system::error_code ec; + size_t moreBytes = m_Socket.available(ec); + while (moreBytes && packets.size () < 25) + { + packet = new SSUPacket (); + packet->len = m_Socket.receive_from (boost::asio::buffer (packet->buf, SSU_MTU_V4), packet->from); + packets.push_back (packet); + moreBytes = m_Socket.available(); + } - m_Service.post (std::bind (&SSUServer::HandleReceivedPackets, this, packets)); - Receive (); - } - else - { - LogPrint ("SSU receive error: ", ecode.message ()); - delete packet; - } - } + m_Service.post (std::bind (&SSUServer::HandleReceivedPackets, this, packets)); + Receive (); + } + else + { + LogPrint ("SSU receive error: ", ecode.message ()); + delete packet; + } + } - void SSUServer::HandleReceivedFromV6 (const boost::system::error_code& ecode, std::size_t bytes_transferred, SSUPacket * packet) - { - if (!ecode) - { - packet->len = bytes_transferred; - std::vector packets; - packets.push_back (packet); + void SSUServer::HandleReceivedFromV6 (const boost::system::error_code& ecode, std::size_t bytes_transferred, SSUPacket * packet) + { + if (!ecode) + { + packet->len = bytes_transferred; + std::vector packets; + packets.push_back (packet); - size_t moreBytes = m_SocketV6.available (); - while (moreBytes && packets.size () < 25) - { - packet = new SSUPacket (); - packet->len = m_SocketV6.receive_from (boost::asio::buffer (packet->buf, SSU_MTU_V6), packet->from); - packets.push_back (packet); - moreBytes = m_SocketV6.available(); - } + size_t moreBytes = m_SocketV6.available (); + while (moreBytes && packets.size () < 25) + { + packet = new SSUPacket (); + packet->len = m_SocketV6.receive_from (boost::asio::buffer (packet->buf, SSU_MTU_V6), packet->from); + packets.push_back (packet); + moreBytes = m_SocketV6.available(); + } - m_ServiceV6.post (std::bind (&SSUServer::HandleReceivedPackets, this, packets)); - ReceiveV6 (); - } - else - { - LogPrint ("SSU V6 receive error: ", ecode.message ()); - delete packet; - } - } + m_ServiceV6.post (std::bind (&SSUServer::HandleReceivedPackets, this, packets)); + ReceiveV6 (); + } + else + { + LogPrint ("SSU V6 receive error: ", ecode.message ()); + delete packet; + } + } - void SSUServer::HandleReceivedPackets (std::vector packets) - { - std::shared_ptr session; - for (auto it1: packets) - { - auto packet = it1; - try - { - if (!session || session->GetRemoteEndpoint () != packet->from) // we received packet for other session than previous - { - if (session) session->FlushData (); - auto it = m_Sessions.find (packet->from); - if (it != m_Sessions.end ()) - session = it->second; - if (!session) - { - session = std::make_shared (*this, packet->from); - session->WaitForConnect (); - { - std::unique_lock l(m_SessionsMutex); - m_Sessions[packet->from] = session; - } - LogPrint (eLogInfo, "New SSU session from ", packet->from.address ().to_string (), ":", packet->from.port (), " created"); - } - } - session->ProcessNextMessage (packet->buf, packet->len, packet->from); - } - catch (std::exception& ex) - { - LogPrint (eLogError, "SSU: HandleReceivedPackets ", ex.what ()); - if (session) session->FlushData (); - session = nullptr; - } - delete packet; - } - if (session) session->FlushData (); - } + void SSUServer::HandleReceivedPackets (std::vector packets) + { + std::shared_ptr session; + for (auto it1: packets) + { + auto packet = it1; + try + { + if (!session || session->GetRemoteEndpoint () != packet->from) // we received packet for other session than previous + { + if (session) session->FlushData (); + auto it = m_Sessions.find (packet->from); + if (it != m_Sessions.end ()) + session = it->second; + if (!session) + { + session = std::make_shared (*this, packet->from); + session->WaitForConnect (); + { + std::unique_lock l(m_SessionsMutex); + m_Sessions[packet->from] = session; + } + LogPrint (eLogInfo, "New SSU session from ", packet->from.address ().to_string (), ":", packet->from.port (), " created"); + } + } + session->ProcessNextMessage (packet->buf, packet->len, packet->from); + } + catch (std::exception& ex) + { + LogPrint (eLogError, "SSU: HandleReceivedPackets ", ex.what ()); + if (session) session->FlushData (); + session = nullptr; + } + delete packet; + } + if (session) session->FlushData (); + } - std::shared_ptr SSUServer::FindSession (std::shared_ptr router) const - { - if (!router) return nullptr; - auto address = router->GetSSUAddress (true); // v4 only - if (!address) return nullptr; - auto session = FindSession (boost::asio::ip::udp::endpoint (address->host, address->port)); - if (session || !context.SupportsV6 ()) - return session; - // try v6 - address = router->GetSSUV6Address (); - if (!address) return nullptr; - return FindSession (boost::asio::ip::udp::endpoint (address->host, address->port)); - } + std::shared_ptr SSUServer::FindSession (std::shared_ptr router) const + { + if (!router) return nullptr; + auto address = router->GetSSUAddress (true); // v4 only + if (!address) return nullptr; + auto session = FindSession (boost::asio::ip::udp::endpoint (address->host, address->port)); + if (session || !context.SupportsV6 ()) + return session; + // try v6 + address = router->GetSSUV6Address (); + if (!address) return nullptr; + return FindSession (boost::asio::ip::udp::endpoint (address->host, address->port)); + } - std::shared_ptr SSUServer::FindSession (const boost::asio::ip::udp::endpoint& e) const - { - auto it = m_Sessions.find (e); - if (it != m_Sessions.end ()) - return it->second; - else - return nullptr; - } - - std::shared_ptr SSUServer::GetSession (std::shared_ptr router, bool peerTest) - { - std::shared_ptr session; - if (router) - { - auto address = router->GetSSUAddress (!context.SupportsV6 ()); - if (address) - { - boost::asio::ip::udp::endpoint remoteEndpoint (address->host, address->port); - auto it = m_Sessions.find (remoteEndpoint); - if (it != m_Sessions.end ()) - session = it->second; - else - { - // otherwise create new session - session = std::make_shared (*this, remoteEndpoint, router, peerTest); - { - std::unique_lock l(m_SessionsMutex); - m_Sessions[remoteEndpoint] = session; - } - if (!router->UsesIntroducer ()) - { - // connect directly - LogPrint ("Creating new SSU session to [", router->GetIdentHashAbbreviation (), "] ", - remoteEndpoint.address ().to_string (), ":", remoteEndpoint.port ()); - session->Connect (); - } - else - { - // connect through introducer - int numIntroducers = address->introducers.size (); - if (numIntroducers > 0) - { - std::shared_ptr introducerSession; - const i2p::data::RouterInfo::Introducer * introducer = nullptr; - // we might have a session to introducer already - for (int i = 0; i < numIntroducers; i++) - { - introducer = &(address->introducers[i]); - it = m_Sessions.find (boost::asio::ip::udp::endpoint (introducer->iHost, introducer->iPort)); - if (it != m_Sessions.end ()) - { - introducerSession = it->second; - break; - } - } + std::shared_ptr SSUServer::FindSession (const boost::asio::ip::udp::endpoint& e) const + { + auto it = m_Sessions.find (e); + if (it != m_Sessions.end ()) + return it->second; + else + return nullptr; + } + + std::shared_ptr SSUServer::GetSession (std::shared_ptr router, bool peerTest) + { + std::shared_ptr session; + if (router) + { + auto address = router->GetSSUAddress (!context.SupportsV6 ()); + if (address) + { + boost::asio::ip::udp::endpoint remoteEndpoint (address->host, address->port); + auto it = m_Sessions.find (remoteEndpoint); + if (it != m_Sessions.end ()) + session = it->second; + else + { + // otherwise create new session + session = std::make_shared (*this, remoteEndpoint, router, peerTest); + { + std::unique_lock l(m_SessionsMutex); + m_Sessions[remoteEndpoint] = session; + } + if (!router->UsesIntroducer ()) + { + // connect directly + LogPrint ("Creating new SSU session to [", router->GetIdentHashAbbreviation (), "] ", + remoteEndpoint.address ().to_string (), ":", remoteEndpoint.port ()); + session->Connect (); + } + else + { + // connect through introducer + int numIntroducers = address->introducers.size (); + if (numIntroducers > 0) + { + std::shared_ptr introducerSession; + const i2p::data::RouterInfo::Introducer * introducer = nullptr; + // we might have a session to introducer already + for (int i = 0; i < numIntroducers; i++) + { + introducer = &(address->introducers[i]); + it = m_Sessions.find (boost::asio::ip::udp::endpoint (introducer->iHost, introducer->iPort)); + if (it != m_Sessions.end ()) + { + introducerSession = it->second; + break; + } + } - if (introducerSession) // session found - LogPrint ("Session to introducer already exists"); - else // create new - { - LogPrint ("Creating new session to introducer"); - introducer = &(address->introducers[0]); // TODO: - boost::asio::ip::udp::endpoint introducerEndpoint (introducer->iHost, introducer->iPort); - introducerSession = std::make_shared (*this, introducerEndpoint, router); - std::unique_lock l(m_SessionsMutex); - m_Sessions[introducerEndpoint] = introducerSession; - } - // introduce - LogPrint ("Introduce new SSU session to [", router->GetIdentHashAbbreviation (), - "] through introducer ", introducer->iHost, ":", introducer->iPort); - session->WaitForIntroduction (); - if (i2p::context.GetRouterInfo ().UsesIntroducer ()) // if we are unreachable - { - uint8_t buf[1]; - Send (buf, 0, remoteEndpoint); // send HolePunch - } - introducerSession->Introduce (introducer->iTag, introducer->iKey); - } - else - { - LogPrint (eLogWarning, "Can't connect to unreachable router. No introducers presented"); - std::unique_lock l(m_SessionsMutex); - m_Sessions.erase (remoteEndpoint); - session.reset (); - } - } - } - } - else - LogPrint (eLogWarning, "Router ", router->GetIdentHashAbbreviation (), " doesn't have SSU address"); - } - return session; - } + if (introducerSession) // session found + LogPrint ("Session to introducer already exists"); + else // create new + { + LogPrint ("Creating new session to introducer"); + introducer = &(address->introducers[0]); // TODO: + boost::asio::ip::udp::endpoint introducerEndpoint (introducer->iHost, introducer->iPort); + introducerSession = std::make_shared (*this, introducerEndpoint, router); + std::unique_lock l(m_SessionsMutex); + m_Sessions[introducerEndpoint] = introducerSession; + } + // introduce + LogPrint ("Introduce new SSU session to [", router->GetIdentHashAbbreviation (), + "] through introducer ", introducer->iHost, ":", introducer->iPort); + session->WaitForIntroduction (); + if (i2p::context.GetRouterInfo ().UsesIntroducer ()) // if we are unreachable + { + uint8_t buf[1]; + Send (buf, 0, remoteEndpoint); // send HolePunch + } + introducerSession->Introduce (introducer->iTag, introducer->iKey); + } + else + { + LogPrint (eLogWarning, "Can't connect to unreachable router. No introducers presented"); + std::unique_lock l(m_SessionsMutex); + m_Sessions.erase (remoteEndpoint); + session.reset (); + } + } + } + } + else + LogPrint (eLogWarning, "Router ", router->GetIdentHashAbbreviation (), " doesn't have SSU address"); + } + return session; + } - void SSUServer::DeleteSession (std::shared_ptr session) - { - if (session) - { - session->Close (); - std::unique_lock l(m_SessionsMutex); - m_Sessions.erase (session->GetRemoteEndpoint ()); - } - } + void SSUServer::DeleteSession (std::shared_ptr session) + { + if (session) + { + session->Close (); + std::unique_lock l(m_SessionsMutex); + m_Sessions.erase (session->GetRemoteEndpoint ()); + } + } - void SSUServer::DeleteAllSessions () - { - std::unique_lock l(m_SessionsMutex); - for (auto it: m_Sessions) - it.second->Close (); - m_Sessions.clear (); - } + void SSUServer::DeleteAllSessions () + { + std::unique_lock l(m_SessionsMutex); + for (auto it: m_Sessions) + it.second->Close (); + m_Sessions.clear (); + } - template - std::shared_ptr SSUServer::GetRandomSession (Filter filter) - { - std::vector > filteredSessions; - for (auto s :m_Sessions) - if (filter (s.second)) filteredSessions.push_back (s.second); - if (filteredSessions.size () > 0) - { - auto ind = i2p::context.GetRandomNumberGenerator ().GenerateWord32 (0, filteredSessions.size ()-1); - return filteredSessions[ind]; - } - return nullptr; - } + template + std::shared_ptr SSUServer::GetRandomSession (Filter filter) + { + std::vector > filteredSessions; + for (auto s :m_Sessions) + if (filter (s.second)) filteredSessions.push_back (s.second); + if (filteredSessions.size () > 0) + { + auto ind = i2p::context.GetRandomNumberGenerator ().GenerateWord32 (0, filteredSessions.size ()-1); + return filteredSessions[ind]; + } + return nullptr; + } - std::shared_ptr SSUServer::GetRandomEstablishedSession (std::shared_ptr excluded) - { - return GetRandomSession ( - [excluded](std::shared_ptr session)->bool - { - return session->GetState () == eSessionStateEstablished && !session->IsV6 () && - session != excluded; - } - ); - } + std::shared_ptr SSUServer::GetRandomEstablishedSession (std::shared_ptr excluded) + { + return GetRandomSession ( + [excluded](std::shared_ptr session)->bool + { + return session->GetState () == eSessionStateEstablished && !session->IsV6 () && + session != excluded; + } + ); + } - std::set SSUServer::FindIntroducers (int maxNumIntroducers) - { - uint32_t ts = i2p::util::GetSecondsSinceEpoch (); - std::set ret; - for (int i = 0; i < maxNumIntroducers; i++) - { - auto session = GetRandomSession ( - [&ret, ts](std::shared_ptr session)->bool - { - return session->GetRelayTag () && !ret.count (session.get ()) && - session->GetState () == eSessionStateEstablished && - ts < session->GetCreationTime () + SSU_TO_INTRODUCER_SESSION_DURATION; - } - ); - if (session) - { - ret.insert (session.get ()); - break; - } - } - return ret; - } + std::set SSUServer::FindIntroducers (int maxNumIntroducers) + { + uint32_t ts = i2p::util::GetSecondsSinceEpoch (); + std::set ret; + for (int i = 0; i < maxNumIntroducers; i++) + { + auto session = GetRandomSession ( + [&ret, ts](std::shared_ptr session)->bool + { + return session->GetRelayTag () && !ret.count (session.get ()) && + session->GetState () == eSessionStateEstablished && + ts < session->GetCreationTime () + SSU_TO_INTRODUCER_SESSION_DURATION; + } + ); + if (session) + { + ret.insert (session.get ()); + break; + } + } + return ret; + } - void SSUServer::ScheduleIntroducersUpdateTimer () - { - m_IntroducersUpdateTimer.expires_from_now (boost::posix_time::seconds(SSU_KEEP_ALIVE_INTERVAL)); - m_IntroducersUpdateTimer.async_wait (std::bind (&SSUServer::HandleIntroducersUpdateTimer, - this, std::placeholders::_1)); - } + void SSUServer::ScheduleIntroducersUpdateTimer () + { + m_IntroducersUpdateTimer.expires_from_now (boost::posix_time::seconds(SSU_KEEP_ALIVE_INTERVAL)); + m_IntroducersUpdateTimer.async_wait (std::bind (&SSUServer::HandleIntroducersUpdateTimer, + this, std::placeholders::_1)); + } - void SSUServer::HandleIntroducersUpdateTimer (const boost::system::error_code& ecode) - { - if (ecode != boost::asio::error::operation_aborted) - { - // timeout expired - if (i2p::context.GetStatus () == eRouterStatusTesting) - { - // we still don't know if we need introducers - ScheduleIntroducersUpdateTimer (); - return; - } - if (i2p::context.GetStatus () == eRouterStatusOK) return; // we don't need introducers anymore - // we are firewalled - if (!i2p::context.IsUnreachable ()) i2p::context.SetUnreachable (); - std::list newList; - size_t numIntroducers = 0; - uint32_t ts = i2p::util::GetSecondsSinceEpoch (); - for (auto it :m_Introducers) - { - auto session = FindSession (it); - if (session && ts < session->GetCreationTime () + SSU_TO_INTRODUCER_SESSION_DURATION) - { - session->SendKeepAlive (); - newList.push_back (it); - numIntroducers++; - } - else - i2p::context.RemoveIntroducer (it); - } + void SSUServer::HandleIntroducersUpdateTimer (const boost::system::error_code& ecode) + { + if (ecode != boost::asio::error::operation_aborted) + { + // timeout expired + if (i2p::context.GetStatus () == eRouterStatusTesting) + { + // we still don't know if we need introducers + ScheduleIntroducersUpdateTimer (); + return; + } + if (i2p::context.GetStatus () == eRouterStatusOK) return; // we don't need introducers anymore + // we are firewalled + if (!i2p::context.IsUnreachable ()) i2p::context.SetUnreachable (); + std::list newList; + size_t numIntroducers = 0; + uint32_t ts = i2p::util::GetSecondsSinceEpoch (); + for (auto it :m_Introducers) + { + auto session = FindSession (it); + if (session && ts < session->GetCreationTime () + SSU_TO_INTRODUCER_SESSION_DURATION) + { + session->SendKeepAlive (); + newList.push_back (it); + numIntroducers++; + } + else + i2p::context.RemoveIntroducer (it); + } - if (numIntroducers < SSU_MAX_NUM_INTRODUCERS) - { - // create new - auto introducers = FindIntroducers (SSU_MAX_NUM_INTRODUCERS); - if (introducers.size () > 0) - { - for (auto it1: introducers) - { - auto router = it1->GetRemoteRouter (); - if (router && i2p::context.AddIntroducer (*router, it1->GetRelayTag ())) - { - newList.push_back (it1->GetRemoteEndpoint ()); - if (newList.size () >= SSU_MAX_NUM_INTRODUCERS) break; - } - } - } - } - m_Introducers = newList; - if (m_Introducers.empty ()) - { - auto introducer = i2p::data::netdb.GetRandomIntroducer (); - if (introducer) - GetSession (introducer); - } - ScheduleIntroducersUpdateTimer (); - } - } + if (numIntroducers < SSU_MAX_NUM_INTRODUCERS) + { + // create new + auto introducers = FindIntroducers (SSU_MAX_NUM_INTRODUCERS); + if (introducers.size () > 0) + { + for (auto it1: introducers) + { + auto router = it1->GetRemoteRouter (); + if (router && i2p::context.AddIntroducer (*router, it1->GetRelayTag ())) + { + newList.push_back (it1->GetRemoteEndpoint ()); + if (newList.size () >= SSU_MAX_NUM_INTRODUCERS) break; + } + } + } + } + m_Introducers = newList; + if (m_Introducers.empty ()) + { + auto introducer = i2p::data::netdb.GetRandomIntroducer (); + if (introducer) + GetSession (introducer); + } + ScheduleIntroducersUpdateTimer (); + } + } - void SSUServer::NewPeerTest (uint32_t nonce, PeerTestParticipant role, std::shared_ptr session) - { - m_PeerTests[nonce] = { i2p::util::GetMillisecondsSinceEpoch (), role, session }; - } + void SSUServer::NewPeerTest (uint32_t nonce, PeerTestParticipant role, std::shared_ptr session) + { + m_PeerTests[nonce] = { i2p::util::GetMillisecondsSinceEpoch (), role, session }; + } - PeerTestParticipant SSUServer::GetPeerTestParticipant (uint32_t nonce) - { - auto it = m_PeerTests.find (nonce); - if (it != m_PeerTests.end ()) - return it->second.role; - else - return ePeerTestParticipantUnknown; - } + PeerTestParticipant SSUServer::GetPeerTestParticipant (uint32_t nonce) + { + auto it = m_PeerTests.find (nonce); + if (it != m_PeerTests.end ()) + return it->second.role; + else + return ePeerTestParticipantUnknown; + } - std::shared_ptr SSUServer::GetPeerTestSession (uint32_t nonce) - { - auto it = m_PeerTests.find (nonce); - if (it != m_PeerTests.end ()) - return it->second.session; - else - return nullptr; - } + std::shared_ptr SSUServer::GetPeerTestSession (uint32_t nonce) + { + auto it = m_PeerTests.find (nonce); + if (it != m_PeerTests.end ()) + return it->second.session; + else + return nullptr; + } - void SSUServer::UpdatePeerTest (uint32_t nonce, PeerTestParticipant role) - { - auto it = m_PeerTests.find (nonce); - if (it != m_PeerTests.end ()) - it->second.role = role; - } - - void SSUServer::RemovePeerTest (uint32_t nonce) - { - m_PeerTests.erase (nonce); - } + void SSUServer::UpdatePeerTest (uint32_t nonce, PeerTestParticipant role) + { + auto it = m_PeerTests.find (nonce); + if (it != m_PeerTests.end ()) + it->second.role = role; + } + + void SSUServer::RemovePeerTest (uint32_t nonce) + { + m_PeerTests.erase (nonce); + } - void SSUServer::SchedulePeerTestsCleanupTimer () - { - m_PeerTestsCleanupTimer.expires_from_now (boost::posix_time::seconds(SSU_PEER_TEST_TIMEOUT)); - m_PeerTestsCleanupTimer.async_wait (std::bind (&SSUServer::HandlePeerTestsCleanupTimer, - this, std::placeholders::_1)); - } + void SSUServer::SchedulePeerTestsCleanupTimer () + { + m_PeerTestsCleanupTimer.expires_from_now (boost::posix_time::seconds(SSU_PEER_TEST_TIMEOUT)); + m_PeerTestsCleanupTimer.async_wait (std::bind (&SSUServer::HandlePeerTestsCleanupTimer, + this, std::placeholders::_1)); + } - void SSUServer::HandlePeerTestsCleanupTimer (const boost::system::error_code& ecode) - { - if (ecode != boost::asio::error::operation_aborted) - { - int numDeleted = 0; - uint64_t ts = i2p::util::GetMillisecondsSinceEpoch (); - for (auto it = m_PeerTests.begin (); it != m_PeerTests.end ();) - { - if (ts > it->second.creationTime + SSU_PEER_TEST_TIMEOUT*1000LL) - { - numDeleted++; - it = m_PeerTests.erase (it); - } - else - it++; - } - if (numDeleted > 0) - LogPrint (eLogInfo, numDeleted, " peer tests have been expired"); - SchedulePeerTestsCleanupTimer (); - } - } + void SSUServer::HandlePeerTestsCleanupTimer (const boost::system::error_code& ecode) + { + if (ecode != boost::asio::error::operation_aborted) + { + int numDeleted = 0; + uint64_t ts = i2p::util::GetMillisecondsSinceEpoch (); + for (auto it = m_PeerTests.begin (); it != m_PeerTests.end ();) + { + if (ts > it->second.creationTime + SSU_PEER_TEST_TIMEOUT*1000LL) + { + numDeleted++; + it = m_PeerTests.erase (it); + } + else + it++; + } + if (numDeleted > 0) + LogPrint (eLogInfo, numDeleted, " peer tests have been expired"); + SchedulePeerTestsCleanupTimer (); + } + } } } diff --git a/SSU.h b/SSU.h index 1033a2bf..54d9326a 100644 --- a/SSU.h +++ b/SSU.h @@ -20,93 +20,93 @@ namespace i2p { namespace transport { - const int SSU_KEEP_ALIVE_INTERVAL = 30; // 30 seconds - const int SSU_PEER_TEST_TIMEOUT = 60; // 60 seconds - const int SSU_TO_INTRODUCER_SESSION_DURATION = 3600; // 1 hour - const size_t SSU_MAX_NUM_INTRODUCERS = 3; + const int SSU_KEEP_ALIVE_INTERVAL = 30; // 30 seconds + const int SSU_PEER_TEST_TIMEOUT = 60; // 60 seconds + const int SSU_TO_INTRODUCER_SESSION_DURATION = 3600; // 1 hour + const size_t SSU_MAX_NUM_INTRODUCERS = 3; - struct SSUPacket - { - i2p::crypto::AESAlignedBuffer<1500> buf; - boost::asio::ip::udp::endpoint from; - size_t len; - }; - - class SSUServer - { - public: + struct SSUPacket + { + i2p::crypto::AESAlignedBuffer<1500> buf; + boost::asio::ip::udp::endpoint from; + size_t len; + }; + + class SSUServer + { + public: - SSUServer (int port); - ~SSUServer (); - void Start (); - void Stop (); - std::shared_ptr GetSession (std::shared_ptr router, bool peerTest = false); - std::shared_ptr FindSession (std::shared_ptr router) const; - std::shared_ptr FindSession (const boost::asio::ip::udp::endpoint& e) const; - std::shared_ptr GetRandomEstablishedSession (std::shared_ptr excluded); - void DeleteSession (std::shared_ptr session); - void DeleteAllSessions (); + SSUServer (int port); + ~SSUServer (); + void Start (); + void Stop (); + std::shared_ptr GetSession (std::shared_ptr router, bool peerTest = false); + std::shared_ptr FindSession (std::shared_ptr router) const; + std::shared_ptr FindSession (const boost::asio::ip::udp::endpoint& e) const; + std::shared_ptr GetRandomEstablishedSession (std::shared_ptr excluded); + void DeleteSession (std::shared_ptr session); + void DeleteAllSessions (); - boost::asio::io_service& GetService () { return m_Service; }; - boost::asio::io_service& GetServiceV6 () { return m_ServiceV6; }; - const boost::asio::ip::udp::endpoint& GetEndpoint () const { return m_Endpoint; }; - void Send (const uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& to); - void AddRelay (uint32_t tag, const boost::asio::ip::udp::endpoint& relay); - std::shared_ptr FindRelaySession (uint32_t tag); + boost::asio::io_service& GetService () { return m_Service; }; + boost::asio::io_service& GetServiceV6 () { return m_ServiceV6; }; + const boost::asio::ip::udp::endpoint& GetEndpoint () const { return m_Endpoint; }; + void Send (const uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& to); + void AddRelay (uint32_t tag, const boost::asio::ip::udp::endpoint& relay); + std::shared_ptr FindRelaySession (uint32_t tag); - void NewPeerTest (uint32_t nonce, PeerTestParticipant role, std::shared_ptr session = nullptr); - PeerTestParticipant GetPeerTestParticipant (uint32_t nonce); - std::shared_ptr GetPeerTestSession (uint32_t nonce); - void UpdatePeerTest (uint32_t nonce, PeerTestParticipant role); - void RemovePeerTest (uint32_t nonce); + void NewPeerTest (uint32_t nonce, PeerTestParticipant role, std::shared_ptr session = nullptr); + PeerTestParticipant GetPeerTestParticipant (uint32_t nonce); + std::shared_ptr GetPeerTestSession (uint32_t nonce); + void UpdatePeerTest (uint32_t nonce, PeerTestParticipant role); + void RemovePeerTest (uint32_t nonce); - private: + private: - void Run (); - void RunV6 (); - void RunReceivers (); - void Receive (); - void ReceiveV6 (); - void HandleReceivedFrom (const boost::system::error_code& ecode, std::size_t bytes_transferred, SSUPacket * packet); - void HandleReceivedFromV6 (const boost::system::error_code& ecode, std::size_t bytes_transferred, SSUPacket * packet); - void HandleReceivedPackets (std::vector packets); + void Run (); + void RunV6 (); + void RunReceivers (); + void Receive (); + void ReceiveV6 (); + void HandleReceivedFrom (const boost::system::error_code& ecode, std::size_t bytes_transferred, SSUPacket * packet); + void HandleReceivedFromV6 (const boost::system::error_code& ecode, std::size_t bytes_transferred, SSUPacket * packet); + void HandleReceivedPackets (std::vector packets); - template - std::shared_ptr GetRandomSession (Filter filter); - - std::set FindIntroducers (int maxNumIntroducers); - void ScheduleIntroducersUpdateTimer (); - void HandleIntroducersUpdateTimer (const boost::system::error_code& ecode); + template + std::shared_ptr GetRandomSession (Filter filter); + + std::set FindIntroducers (int maxNumIntroducers); + void ScheduleIntroducersUpdateTimer (); + void HandleIntroducersUpdateTimer (const boost::system::error_code& ecode); - void SchedulePeerTestsCleanupTimer (); - void HandlePeerTestsCleanupTimer (const boost::system::error_code& ecode); + void SchedulePeerTestsCleanupTimer (); + void HandlePeerTestsCleanupTimer (const boost::system::error_code& ecode); - private: + private: - struct PeerTest - { - uint64_t creationTime; - PeerTestParticipant role; - std::shared_ptr session; // for Bob to Alice - }; - - bool m_IsRunning; - std::thread * m_Thread, * m_ThreadV6, * m_ReceiversThread; - boost::asio::io_service m_Service, m_ServiceV6, m_ReceiversService; - boost::asio::io_service::work m_Work, m_WorkV6, m_ReceiversWork; - boost::asio::ip::udp::endpoint m_Endpoint, m_EndpointV6; - boost::asio::ip::udp::socket m_Socket, m_SocketV6; - boost::asio::deadline_timer m_IntroducersUpdateTimer, m_PeerTestsCleanupTimer; - std::list m_Introducers; // introducers we are connected to - mutable std::mutex m_SessionsMutex; - std::map > m_Sessions; - std::map m_Relays; // we are introducer - std::map m_PeerTests; // nonce -> creation time in milliseconds + struct PeerTest + { + uint64_t creationTime; + PeerTestParticipant role; + std::shared_ptr session; // for Bob to Alice + }; + + bool m_IsRunning; + std::thread * m_Thread, * m_ThreadV6, * m_ReceiversThread; + boost::asio::io_service m_Service, m_ServiceV6, m_ReceiversService; + boost::asio::io_service::work m_Work, m_WorkV6, m_ReceiversWork; + boost::asio::ip::udp::endpoint m_Endpoint, m_EndpointV6; + boost::asio::ip::udp::socket m_Socket, m_SocketV6; + boost::asio::deadline_timer m_IntroducersUpdateTimer, m_PeerTestsCleanupTimer; + std::list m_Introducers; // introducers we are connected to + mutable std::mutex m_SessionsMutex; + std::map > m_Sessions; + std::map m_Relays; // we are introducer + std::map m_PeerTests; // nonce -> creation time in milliseconds - public: - // for HTTP only - const decltype(m_Sessions)& GetSessions () const { return m_Sessions; }; - }; + public: + // for HTTP only + const decltype(m_Sessions)& GetSessions () const { return m_Sessions; }; + }; } } diff --git a/SSUData.cpp b/SSUData.cpp index 5c0c03dd..02473db4 100644 --- a/SSUData.cpp +++ b/SSUData.cpp @@ -10,498 +10,498 @@ namespace i2p { namespace transport { - void IncompleteMessage::AttachNextFragment (const uint8_t * fragment, size_t fragmentSize) - { - if (msg->len + fragmentSize > msg->maxLen) - { - LogPrint (eLogInfo, "SSU I2NP message size ", msg->maxLen, " is not enough"); - auto newMsg = ToSharedI2NPMessage(NewI2NPMessage ()); - *newMsg = *msg; - msg = newMsg; - } - memcpy (msg->buf + msg->len, fragment, fragmentSize); - msg->len += fragmentSize; - nextFragmentNum++; - } + void IncompleteMessage::AttachNextFragment (const uint8_t * fragment, size_t fragmentSize) + { + if (msg->len + fragmentSize > msg->maxLen) + { + LogPrint (eLogInfo, "SSU I2NP message size ", msg->maxLen, " is not enough"); + auto newMsg = ToSharedI2NPMessage(NewI2NPMessage ()); + *newMsg = *msg; + msg = newMsg; + } + memcpy (msg->buf + msg->len, fragment, fragmentSize); + msg->len += fragmentSize; + nextFragmentNum++; + } - SSUData::SSUData (SSUSession& session): - m_Session (session), m_ResendTimer (session.GetService ()), m_DecayTimer (session.GetService ()), - m_IncompleteMessagesCleanupTimer (session.GetService ()) - { - m_MaxPacketSize = session.IsV6 () ? SSU_V6_MAX_PACKET_SIZE : SSU_V4_MAX_PACKET_SIZE; - m_PacketSize = m_MaxPacketSize; - auto remoteRouter = session.GetRemoteRouter (); - if (remoteRouter) - AdjustPacketSize (*remoteRouter); - } + SSUData::SSUData (SSUSession& session): + m_Session (session), m_ResendTimer (session.GetService ()), m_DecayTimer (session.GetService ()), + m_IncompleteMessagesCleanupTimer (session.GetService ()) + { + m_MaxPacketSize = session.IsV6 () ? SSU_V6_MAX_PACKET_SIZE : SSU_V4_MAX_PACKET_SIZE; + m_PacketSize = m_MaxPacketSize; + auto remoteRouter = session.GetRemoteRouter (); + if (remoteRouter) + AdjustPacketSize (*remoteRouter); + } - SSUData::~SSUData () - { - } + SSUData::~SSUData () + { + } - void SSUData::Start () - { - ScheduleIncompleteMessagesCleanup (); - } - - void SSUData::Stop () - { - m_ResendTimer.cancel (); - m_DecayTimer.cancel (); - m_IncompleteMessagesCleanupTimer.cancel (); - } - - void SSUData::AdjustPacketSize (const i2p::data::RouterInfo& remoteRouter) - { - auto ssuAddress = remoteRouter.GetSSUAddress (); - if (ssuAddress && ssuAddress->mtu) - { - if (m_Session.IsV6 ()) - m_PacketSize = ssuAddress->mtu - IPV6_HEADER_SIZE - UDP_HEADER_SIZE; - else - m_PacketSize = ssuAddress->mtu - IPV4_HEADER_SIZE - UDP_HEADER_SIZE; - if (m_PacketSize > 0) - { - // make sure packet size multiple of 16 - m_PacketSize >>= 4; - m_PacketSize <<= 4; - if (m_PacketSize > m_MaxPacketSize) m_PacketSize = m_MaxPacketSize; - LogPrint ("MTU=", ssuAddress->mtu, " packet size=", m_PacketSize); - } - else - { - LogPrint (eLogWarning, "Unexpected MTU ", ssuAddress->mtu); - m_PacketSize = m_MaxPacketSize; - } - } - } + void SSUData::Start () + { + ScheduleIncompleteMessagesCleanup (); + } + + void SSUData::Stop () + { + m_ResendTimer.cancel (); + m_DecayTimer.cancel (); + m_IncompleteMessagesCleanupTimer.cancel (); + } + + void SSUData::AdjustPacketSize (const i2p::data::RouterInfo& remoteRouter) + { + auto ssuAddress = remoteRouter.GetSSUAddress (); + if (ssuAddress && ssuAddress->mtu) + { + if (m_Session.IsV6 ()) + m_PacketSize = ssuAddress->mtu - IPV6_HEADER_SIZE - UDP_HEADER_SIZE; + else + m_PacketSize = ssuAddress->mtu - IPV4_HEADER_SIZE - UDP_HEADER_SIZE; + if (m_PacketSize > 0) + { + // make sure packet size multiple of 16 + m_PacketSize >>= 4; + m_PacketSize <<= 4; + if (m_PacketSize > m_MaxPacketSize) m_PacketSize = m_MaxPacketSize; + LogPrint ("MTU=", ssuAddress->mtu, " packet size=", m_PacketSize); + } + else + { + LogPrint (eLogWarning, "Unexpected MTU ", ssuAddress->mtu); + m_PacketSize = m_MaxPacketSize; + } + } + } - void SSUData::UpdatePacketSize (const i2p::data::IdentHash& remoteIdent) - { - auto routerInfo = i2p::data::netdb.FindRouter (remoteIdent); - if (routerInfo) - AdjustPacketSize (*routerInfo); - } + void SSUData::UpdatePacketSize (const i2p::data::IdentHash& remoteIdent) + { + auto routerInfo = i2p::data::netdb.FindRouter (remoteIdent); + if (routerInfo) + AdjustPacketSize (*routerInfo); + } - void SSUData::ProcessSentMessageAck (uint32_t msgID) - { - auto it = m_SentMessages.find (msgID); - if (it != m_SentMessages.end ()) - { - m_SentMessages.erase (it); - if (m_SentMessages.empty ()) - m_ResendTimer.cancel (); - } - } + void SSUData::ProcessSentMessageAck (uint32_t msgID) + { + auto it = m_SentMessages.find (msgID); + if (it != m_SentMessages.end ()) + { + m_SentMessages.erase (it); + if (m_SentMessages.empty ()) + m_ResendTimer.cancel (); + } + } - void SSUData::ProcessAcks (uint8_t *& buf, uint8_t flag) - { - if (flag & DATA_FLAG_EXPLICIT_ACKS_INCLUDED) - { - // explicit ACKs - uint8_t numAcks =*buf; - buf++; - for (int i = 0; i < numAcks; i++) - ProcessSentMessageAck (bufbe32toh (buf+i*4)); - buf += numAcks*4; - } - if (flag & DATA_FLAG_ACK_BITFIELDS_INCLUDED) - { - // explicit ACK bitfields - uint8_t numBitfields =*buf; - buf++; - for (int i = 0; i < numBitfields; i++) - { - uint32_t msgID = bufbe32toh (buf); - buf += 4; // msgID - auto it = m_SentMessages.find (msgID); - // process individual Ack bitfields - bool isNonLast = false; - int fragment = 0; - do - { - uint8_t bitfield = *buf; - isNonLast = bitfield & 0x80; - bitfield &= 0x7F; // clear MSB - if (bitfield && it != m_SentMessages.end ()) - { - int numSentFragments = it->second->fragments.size (); - // process bits - uint8_t mask = 0x01; - for (int j = 0; j < 7; j++) - { - if (bitfield & mask) - { - if (fragment < numSentFragments) - it->second->fragments[fragment].reset (nullptr); - } - fragment++; - mask <<= 1; - } - } - buf++; - } - while (isNonLast); - } - } - } + void SSUData::ProcessAcks (uint8_t *& buf, uint8_t flag) + { + if (flag & DATA_FLAG_EXPLICIT_ACKS_INCLUDED) + { + // explicit ACKs + uint8_t numAcks =*buf; + buf++; + for (int i = 0; i < numAcks; i++) + ProcessSentMessageAck (bufbe32toh (buf+i*4)); + buf += numAcks*4; + } + if (flag & DATA_FLAG_ACK_BITFIELDS_INCLUDED) + { + // explicit ACK bitfields + uint8_t numBitfields =*buf; + buf++; + for (int i = 0; i < numBitfields; i++) + { + uint32_t msgID = bufbe32toh (buf); + buf += 4; // msgID + auto it = m_SentMessages.find (msgID); + // process individual Ack bitfields + bool isNonLast = false; + int fragment = 0; + do + { + uint8_t bitfield = *buf; + isNonLast = bitfield & 0x80; + bitfield &= 0x7F; // clear MSB + if (bitfield && it != m_SentMessages.end ()) + { + int numSentFragments = it->second->fragments.size (); + // process bits + uint8_t mask = 0x01; + for (int j = 0; j < 7; j++) + { + if (bitfield & mask) + { + if (fragment < numSentFragments) + it->second->fragments[fragment].reset (nullptr); + } + fragment++; + mask <<= 1; + } + } + buf++; + } + while (isNonLast); + } + } + } - void SSUData::ProcessFragments (uint8_t * buf) - { - uint8_t numFragments = *buf; // number of fragments - buf++; - for (int i = 0; i < numFragments; i++) - { - uint32_t msgID = bufbe32toh (buf); // message ID - buf += 4; - uint8_t frag[4]; - frag[0] = 0; - memcpy (frag + 1, buf, 3); - buf += 3; - uint32_t fragmentInfo = bufbe32toh (frag); // fragment info - uint16_t fragmentSize = fragmentInfo & 0x1FFF; // bits 0 - 13 - bool isLast = fragmentInfo & 0x010000; // bit 16 - uint8_t fragmentNum = fragmentInfo >> 17; // bits 23 - 17 - if (fragmentSize >= SSU_V4_MAX_PACKET_SIZE) - { - LogPrint (eLogError, "Fragment size ", fragmentSize, "exceeds max SSU packet size"); - return; - } + void SSUData::ProcessFragments (uint8_t * buf) + { + uint8_t numFragments = *buf; // number of fragments + buf++; + for (int i = 0; i < numFragments; i++) + { + uint32_t msgID = bufbe32toh (buf); // message ID + buf += 4; + uint8_t frag[4]; + frag[0] = 0; + memcpy (frag + 1, buf, 3); + buf += 3; + uint32_t fragmentInfo = bufbe32toh (frag); // fragment info + uint16_t fragmentSize = fragmentInfo & 0x1FFF; // bits 0 - 13 + bool isLast = fragmentInfo & 0x010000; // bit 16 + uint8_t fragmentNum = fragmentInfo >> 17; // bits 23 - 17 + if (fragmentSize >= SSU_V4_MAX_PACKET_SIZE) + { + LogPrint (eLogError, "Fragment size ", fragmentSize, "exceeds max SSU packet size"); + return; + } - // find message with msgID - auto it = m_IncompleteMessages.find (msgID); - if (it == m_IncompleteMessages.end ()) - { - // create new message - auto msg = ToSharedI2NPMessage (NewI2NPShortMessage ()); - msg->len -= I2NP_SHORT_HEADER_SIZE; - it = m_IncompleteMessages.insert (std::make_pair (msgID, - std::unique_ptr(new IncompleteMessage (msg)))).first; - } - std::unique_ptr& incompleteMessage = it->second; + // find message with msgID + auto it = m_IncompleteMessages.find (msgID); + if (it == m_IncompleteMessages.end ()) + { + // create new message + auto msg = ToSharedI2NPMessage (NewI2NPShortMessage ()); + msg->len -= I2NP_SHORT_HEADER_SIZE; + it = m_IncompleteMessages.insert (std::make_pair (msgID, + std::unique_ptr(new IncompleteMessage (msg)))).first; + } + std::unique_ptr& incompleteMessage = it->second; - // handle current fragment - if (fragmentNum == incompleteMessage->nextFragmentNum) - { - // expected fragment - incompleteMessage->AttachNextFragment (buf, fragmentSize); - if (!isLast && !incompleteMessage->savedFragments.empty ()) - { - // try saved fragments - for (auto it1 = incompleteMessage->savedFragments.begin (); it1 != incompleteMessage->savedFragments.end ();) - { - auto& savedFragment = *it1; - if (savedFragment->fragmentNum == incompleteMessage->nextFragmentNum) - { - incompleteMessage->AttachNextFragment (savedFragment->buf, savedFragment->len); - isLast = savedFragment->isLast; - incompleteMessage->savedFragments.erase (it1++); - } - else - break; - } - if (isLast) - LogPrint (eLogDebug, "Message ", msgID, " complete"); - } - } - else - { - if (fragmentNum < incompleteMessage->nextFragmentNum) - // duplicate fragment - LogPrint (eLogWarning, "Duplicate fragment ", (int)fragmentNum, " of message ", msgID, ". Ignored"); - else - { - // missing fragment - LogPrint (eLogWarning, "Missing fragments from ", (int)incompleteMessage->nextFragmentNum, " to ", fragmentNum - 1, " of message ", msgID); - auto savedFragment = new Fragment (fragmentNum, buf, fragmentSize, isLast); - if (incompleteMessage->savedFragments.insert (std::unique_ptr(savedFragment)).second) - incompleteMessage->lastFragmentInsertTime = i2p::util::GetSecondsSinceEpoch (); - else - LogPrint (eLogWarning, "Fragment ", (int)fragmentNum, " of message ", msgID, " already saved"); - } - isLast = false; - } + // handle current fragment + if (fragmentNum == incompleteMessage->nextFragmentNum) + { + // expected fragment + incompleteMessage->AttachNextFragment (buf, fragmentSize); + if (!isLast && !incompleteMessage->savedFragments.empty ()) + { + // try saved fragments + for (auto it1 = incompleteMessage->savedFragments.begin (); it1 != incompleteMessage->savedFragments.end ();) + { + auto& savedFragment = *it1; + if (savedFragment->fragmentNum == incompleteMessage->nextFragmentNum) + { + incompleteMessage->AttachNextFragment (savedFragment->buf, savedFragment->len); + isLast = savedFragment->isLast; + incompleteMessage->savedFragments.erase (it1++); + } + else + break; + } + if (isLast) + LogPrint (eLogDebug, "Message ", msgID, " complete"); + } + } + else + { + if (fragmentNum < incompleteMessage->nextFragmentNum) + // duplicate fragment + LogPrint (eLogWarning, "Duplicate fragment ", (int)fragmentNum, " of message ", msgID, ". Ignored"); + else + { + // missing fragment + LogPrint (eLogWarning, "Missing fragments from ", (int)incompleteMessage->nextFragmentNum, " to ", fragmentNum - 1, " of message ", msgID); + auto savedFragment = new Fragment (fragmentNum, buf, fragmentSize, isLast); + if (incompleteMessage->savedFragments.insert (std::unique_ptr(savedFragment)).second) + incompleteMessage->lastFragmentInsertTime = i2p::util::GetSecondsSinceEpoch (); + else + LogPrint (eLogWarning, "Fragment ", (int)fragmentNum, " of message ", msgID, " already saved"); + } + isLast = false; + } - if (isLast) - { - // delete incomplete message - auto msg = incompleteMessage->msg; - incompleteMessage->msg = nullptr; - m_IncompleteMessages.erase (msgID); - // process message - SendMsgAck (msgID); - msg->FromSSU (msgID); - if (m_Session.GetState () == eSessionStateEstablished) - { - if (!m_ReceivedMessages.count (msgID)) - { - if (m_ReceivedMessages.size () > MAX_NUM_RECEIVED_MESSAGES) - m_ReceivedMessages.clear (); - else - ScheduleDecay (); - m_ReceivedMessages.insert (msgID); - m_Handler.PutNextMessage (msg); - } - else - LogPrint (eLogWarning, "SSU message ", msgID, " already received"); - } - else - { - // we expect DeliveryStatus - if (msg->GetTypeID () == eI2NPDeliveryStatus) - { - LogPrint ("SSU session established"); - m_Session.Established (); - } - else - LogPrint (eLogError, "SSU unexpected message ", (int)msg->GetTypeID ()); - } - } - else - SendFragmentAck (msgID, fragmentNum); - buf += fragmentSize; - } - } + if (isLast) + { + // delete incomplete message + auto msg = incompleteMessage->msg; + incompleteMessage->msg = nullptr; + m_IncompleteMessages.erase (msgID); + // process message + SendMsgAck (msgID); + msg->FromSSU (msgID); + if (m_Session.GetState () == eSessionStateEstablished) + { + if (!m_ReceivedMessages.count (msgID)) + { + if (m_ReceivedMessages.size () > MAX_NUM_RECEIVED_MESSAGES) + m_ReceivedMessages.clear (); + else + ScheduleDecay (); + m_ReceivedMessages.insert (msgID); + m_Handler.PutNextMessage (msg); + } + else + LogPrint (eLogWarning, "SSU message ", msgID, " already received"); + } + else + { + // we expect DeliveryStatus + if (msg->GetTypeID () == eI2NPDeliveryStatus) + { + LogPrint ("SSU session established"); + m_Session.Established (); + } + else + LogPrint (eLogError, "SSU unexpected message ", (int)msg->GetTypeID ()); + } + } + else + SendFragmentAck (msgID, fragmentNum); + buf += fragmentSize; + } + } - void SSUData::FlushReceivedMessage () - { - m_Handler.Flush (); - } - - void SSUData::ProcessMessage (uint8_t * buf, size_t len) - { - //uint8_t * start = buf; - uint8_t flag = *buf; - buf++; - LogPrint (eLogDebug, "Process SSU data flags=", (int)flag, " len=", len); - // process acks if presented - if (flag & (DATA_FLAG_ACK_BITFIELDS_INCLUDED | DATA_FLAG_EXPLICIT_ACKS_INCLUDED)) - ProcessAcks (buf, flag); - // extended data if presented - if (flag & DATA_FLAG_EXTENDED_DATA_INCLUDED) - { - uint8_t extendedDataSize = *buf; - buf++; // size - LogPrint (eLogDebug, "SSU extended data of ", extendedDataSize, " bytes presented"); - buf += extendedDataSize; - } - // process data - ProcessFragments (buf); - } + void SSUData::FlushReceivedMessage () + { + m_Handler.Flush (); + } + + void SSUData::ProcessMessage (uint8_t * buf, size_t len) + { + //uint8_t * start = buf; + uint8_t flag = *buf; + buf++; + LogPrint (eLogDebug, "Process SSU data flags=", (int)flag, " len=", len); + // process acks if presented + if (flag & (DATA_FLAG_ACK_BITFIELDS_INCLUDED | DATA_FLAG_EXPLICIT_ACKS_INCLUDED)) + ProcessAcks (buf, flag); + // extended data if presented + if (flag & DATA_FLAG_EXTENDED_DATA_INCLUDED) + { + uint8_t extendedDataSize = *buf; + buf++; // size + LogPrint (eLogDebug, "SSU extended data of ", extendedDataSize, " bytes presented"); + buf += extendedDataSize; + } + // process data + ProcessFragments (buf); + } - void SSUData::Send (std::shared_ptr msg) - { - uint32_t msgID = msg->ToSSU (); - if (m_SentMessages.count (msgID) > 0) - { - LogPrint (eLogWarning, "SSU message ", msgID, " already sent"); - return; - } - if (m_SentMessages.empty ()) // schedule resend at first message only - ScheduleResend (); - - auto ret = m_SentMessages.insert (std::make_pair (msgID, std::unique_ptr(new SentMessage))); - std::unique_ptr& sentMessage = ret.first->second; - if (ret.second) - { - sentMessage->nextResendTime = i2p::util::GetSecondsSinceEpoch () + RESEND_INTERVAL; - sentMessage->numResends = 0; - } - auto& fragments = sentMessage->fragments; - size_t payloadSize = m_PacketSize - sizeof (SSUHeader) - 9; // 9 = flag + #frg(1) + messageID(4) + frag info (3) - size_t len = msg->GetLength (); - uint8_t * msgBuf = msg->GetSSUHeader (); + void SSUData::Send (std::shared_ptr msg) + { + uint32_t msgID = msg->ToSSU (); + if (m_SentMessages.count (msgID) > 0) + { + LogPrint (eLogWarning, "SSU message ", msgID, " already sent"); + return; + } + if (m_SentMessages.empty ()) // schedule resend at first message only + ScheduleResend (); + + auto ret = m_SentMessages.insert (std::make_pair (msgID, std::unique_ptr(new SentMessage))); + std::unique_ptr& sentMessage = ret.first->second; + if (ret.second) + { + sentMessage->nextResendTime = i2p::util::GetSecondsSinceEpoch () + RESEND_INTERVAL; + sentMessage->numResends = 0; + } + auto& fragments = sentMessage->fragments; + size_t payloadSize = m_PacketSize - sizeof (SSUHeader) - 9; // 9 = flag + #frg(1) + messageID(4) + frag info (3) + size_t len = msg->GetLength (); + uint8_t * msgBuf = msg->GetSSUHeader (); - uint32_t fragmentNum = 0; - while (len > 0) - { - Fragment * fragment = new Fragment; - fragment->fragmentNum = fragmentNum; - uint8_t * buf = fragment->buf; - uint8_t * payload = buf + sizeof (SSUHeader); - *payload = DATA_FLAG_WANT_REPLY; // for compatibility - payload++; - *payload = 1; // always 1 message fragment per message - payload++; - htobe32buf (payload, msgID); - payload += 4; - bool isLast = (len <= payloadSize); - size_t size = isLast ? len : payloadSize; - uint32_t fragmentInfo = (fragmentNum << 17); - if (isLast) - fragmentInfo |= 0x010000; - - fragmentInfo |= size; - fragmentInfo = htobe32 (fragmentInfo); - memcpy (payload, (uint8_t *)(&fragmentInfo) + 1, 3); - payload += 3; - memcpy (payload, msgBuf, size); - - size += payload - buf; - if (size & 0x0F) // make sure 16 bytes boundary - size = ((size >> 4) + 1) << 4; // (/16 + 1)*16 - fragment->len = size; - fragments.push_back (std::unique_ptr (fragment)); - - // encrypt message with session key - m_Session.FillHeaderAndEncrypt (PAYLOAD_TYPE_DATA, buf, size); - try - { - m_Session.Send (buf, size); - } - catch (boost::system::system_error& ec) - { - LogPrint (eLogError, "Can't send SSU fragment ", ec.what ()); - } - if (!isLast) - { - len -= payloadSize; - msgBuf += payloadSize; - } - else - len = 0; - fragmentNum++; - } - } + uint32_t fragmentNum = 0; + while (len > 0) + { + Fragment * fragment = new Fragment; + fragment->fragmentNum = fragmentNum; + uint8_t * buf = fragment->buf; + uint8_t * payload = buf + sizeof (SSUHeader); + *payload = DATA_FLAG_WANT_REPLY; // for compatibility + payload++; + *payload = 1; // always 1 message fragment per message + payload++; + htobe32buf (payload, msgID); + payload += 4; + bool isLast = (len <= payloadSize); + size_t size = isLast ? len : payloadSize; + uint32_t fragmentInfo = (fragmentNum << 17); + if (isLast) + fragmentInfo |= 0x010000; + + fragmentInfo |= size; + fragmentInfo = htobe32 (fragmentInfo); + memcpy (payload, (uint8_t *)(&fragmentInfo) + 1, 3); + payload += 3; + memcpy (payload, msgBuf, size); + + size += payload - buf; + if (size & 0x0F) // make sure 16 bytes boundary + size = ((size >> 4) + 1) << 4; // (/16 + 1)*16 + fragment->len = size; + fragments.push_back (std::unique_ptr (fragment)); + + // encrypt message with session key + m_Session.FillHeaderAndEncrypt (PAYLOAD_TYPE_DATA, buf, size); + try + { + m_Session.Send (buf, size); + } + catch (boost::system::system_error& ec) + { + LogPrint (eLogError, "Can't send SSU fragment ", ec.what ()); + } + if (!isLast) + { + len -= payloadSize; + msgBuf += payloadSize; + } + else + len = 0; + fragmentNum++; + } + } - void SSUData::SendMsgAck (uint32_t msgID) - { - uint8_t buf[48 + 18]; // actual length is 44 = 37 + 7 but pad it to multiple of 16 - uint8_t * payload = buf + sizeof (SSUHeader); - *payload = DATA_FLAG_EXPLICIT_ACKS_INCLUDED; // flag - payload++; - *payload = 1; // number of ACKs - payload++; - *(uint32_t *)(payload) = htobe32 (msgID); // msgID - payload += 4; - *payload = 0; // number of fragments + void SSUData::SendMsgAck (uint32_t msgID) + { + uint8_t buf[48 + 18]; // actual length is 44 = 37 + 7 but pad it to multiple of 16 + uint8_t * payload = buf + sizeof (SSUHeader); + *payload = DATA_FLAG_EXPLICIT_ACKS_INCLUDED; // flag + payload++; + *payload = 1; // number of ACKs + payload++; + *(uint32_t *)(payload) = htobe32 (msgID); // msgID + payload += 4; + *payload = 0; // number of fragments - // encrypt message with session key - m_Session.FillHeaderAndEncrypt (PAYLOAD_TYPE_DATA, buf, 48); - m_Session.Send (buf, 48); - } + // encrypt message with session key + m_Session.FillHeaderAndEncrypt (PAYLOAD_TYPE_DATA, buf, 48); + m_Session.Send (buf, 48); + } - void SSUData::SendFragmentAck (uint32_t msgID, int fragmentNum) - { - if (fragmentNum > 64) - { - LogPrint (eLogWarning, "Fragment number ", fragmentNum, " exceeds 64"); - return; - } - uint8_t buf[64 + 18]; - uint8_t * payload = buf + sizeof (SSUHeader); - *payload = DATA_FLAG_ACK_BITFIELDS_INCLUDED; // flag - payload++; - *payload = 1; // number of ACK bitfields - payload++; - // one ack - *(uint32_t *)(payload) = htobe32 (msgID); // msgID - payload += 4; - div_t d = div (fragmentNum, 7); - memset (payload, 0x80, d.quot); // 0x80 means non-last - payload += d.quot; - *payload = 0x01 << d.rem; // set corresponding bit - payload++; - *payload = 0; // number of fragments + void SSUData::SendFragmentAck (uint32_t msgID, int fragmentNum) + { + if (fragmentNum > 64) + { + LogPrint (eLogWarning, "Fragment number ", fragmentNum, " exceeds 64"); + return; + } + uint8_t buf[64 + 18]; + uint8_t * payload = buf + sizeof (SSUHeader); + *payload = DATA_FLAG_ACK_BITFIELDS_INCLUDED; // flag + payload++; + *payload = 1; // number of ACK bitfields + payload++; + // one ack + *(uint32_t *)(payload) = htobe32 (msgID); // msgID + payload += 4; + div_t d = div (fragmentNum, 7); + memset (payload, 0x80, d.quot); // 0x80 means non-last + payload += d.quot; + *payload = 0x01 << d.rem; // set corresponding bit + payload++; + *payload = 0; // number of fragments - size_t len = d.quot < 4 ? 48 : 64; // 48 = 37 + 7 + 4 (3+1) - // encrypt message with session key - m_Session.FillHeaderAndEncrypt (PAYLOAD_TYPE_DATA, buf, len); - m_Session.Send (buf, len); - } + size_t len = d.quot < 4 ? 48 : 64; // 48 = 37 + 7 + 4 (3+1) + // encrypt message with session key + m_Session.FillHeaderAndEncrypt (PAYLOAD_TYPE_DATA, buf, len); + m_Session.Send (buf, len); + } - void SSUData::ScheduleResend() - { - m_ResendTimer.cancel (); - m_ResendTimer.expires_from_now (boost::posix_time::seconds(RESEND_INTERVAL)); - auto s = m_Session.shared_from_this(); - m_ResendTimer.async_wait ([s](const boost::system::error_code& ecode) - { s->m_Data.HandleResendTimer (ecode); }); - } + void SSUData::ScheduleResend() + { + m_ResendTimer.cancel (); + m_ResendTimer.expires_from_now (boost::posix_time::seconds(RESEND_INTERVAL)); + auto s = m_Session.shared_from_this(); + m_ResendTimer.async_wait ([s](const boost::system::error_code& ecode) + { s->m_Data.HandleResendTimer (ecode); }); + } - void SSUData::HandleResendTimer (const boost::system::error_code& ecode) - { - if (ecode != boost::asio::error::operation_aborted) - { - uint32_t ts = i2p::util::GetSecondsSinceEpoch (); - for (auto it = m_SentMessages.begin (); it != m_SentMessages.end ();) - { - if (ts >= it->second->nextResendTime) - { - if (it->second->numResends < MAX_NUM_RESENDS) - { - for (auto& f: it->second->fragments) - if (f) - { - try - { - m_Session.Send (f->buf, f->len); // resend - } - catch (boost::system::system_error& ec) - { - LogPrint (eLogError, "Can't resend SSU fragment ", ec.what ()); - } - } + void SSUData::HandleResendTimer (const boost::system::error_code& ecode) + { + if (ecode != boost::asio::error::operation_aborted) + { + uint32_t ts = i2p::util::GetSecondsSinceEpoch (); + for (auto it = m_SentMessages.begin (); it != m_SentMessages.end ();) + { + if (ts >= it->second->nextResendTime) + { + if (it->second->numResends < MAX_NUM_RESENDS) + { + for (auto& f: it->second->fragments) + if (f) + { + try + { + m_Session.Send (f->buf, f->len); // resend + } + catch (boost::system::system_error& ec) + { + LogPrint (eLogError, "Can't resend SSU fragment ", ec.what ()); + } + } - it->second->numResends++; - it->second->nextResendTime += it->second->numResends*RESEND_INTERVAL; - it++; - } - else - { - LogPrint (eLogError, "SSU message has not been ACKed after ", MAX_NUM_RESENDS, " attempts. Deleted"); - it = m_SentMessages.erase (it); - } - } - else - it++; - } - ScheduleResend (); - } - } + it->second->numResends++; + it->second->nextResendTime += it->second->numResends*RESEND_INTERVAL; + it++; + } + else + { + LogPrint (eLogError, "SSU message has not been ACKed after ", MAX_NUM_RESENDS, " attempts. Deleted"); + it = m_SentMessages.erase (it); + } + } + else + it++; + } + ScheduleResend (); + } + } - void SSUData::ScheduleDecay () - { - m_DecayTimer.cancel (); - m_DecayTimer.expires_from_now (boost::posix_time::seconds(DECAY_INTERVAL)); - auto s = m_Session.shared_from_this(); - m_ResendTimer.async_wait ([s](const boost::system::error_code& ecode) - { s->m_Data.HandleDecayTimer (ecode); }); - } + void SSUData::ScheduleDecay () + { + m_DecayTimer.cancel (); + m_DecayTimer.expires_from_now (boost::posix_time::seconds(DECAY_INTERVAL)); + auto s = m_Session.shared_from_this(); + m_ResendTimer.async_wait ([s](const boost::system::error_code& ecode) + { s->m_Data.HandleDecayTimer (ecode); }); + } - void SSUData::HandleDecayTimer (const boost::system::error_code& ecode) - { - if (ecode != boost::asio::error::operation_aborted) - m_ReceivedMessages.clear (); - } + void SSUData::HandleDecayTimer (const boost::system::error_code& ecode) + { + if (ecode != boost::asio::error::operation_aborted) + m_ReceivedMessages.clear (); + } - void SSUData::ScheduleIncompleteMessagesCleanup () - { - m_IncompleteMessagesCleanupTimer.cancel (); - m_IncompleteMessagesCleanupTimer.expires_from_now (boost::posix_time::seconds(INCOMPLETE_MESSAGES_CLEANUP_TIMEOUT)); - auto s = m_Session.shared_from_this(); - m_IncompleteMessagesCleanupTimer.async_wait ([s](const boost::system::error_code& ecode) - { s->m_Data.HandleIncompleteMessagesCleanupTimer (ecode); }); - } - - void SSUData::HandleIncompleteMessagesCleanupTimer (const boost::system::error_code& ecode) - { - if (ecode != boost::asio::error::operation_aborted) - { - uint32_t ts = i2p::util::GetSecondsSinceEpoch (); - for (auto it = m_IncompleteMessages.begin (); it != m_IncompleteMessages.end ();) - { - if (ts > it->second->lastFragmentInsertTime + INCOMPLETE_MESSAGES_CLEANUP_TIMEOUT) - { - LogPrint (eLogError, "SSU message ", it->first, " was not completed in ", INCOMPLETE_MESSAGES_CLEANUP_TIMEOUT, " seconds. Deleted"); - it = m_IncompleteMessages.erase (it); - } - else - it++; - } - ScheduleIncompleteMessagesCleanup (); - } - } + void SSUData::ScheduleIncompleteMessagesCleanup () + { + m_IncompleteMessagesCleanupTimer.cancel (); + m_IncompleteMessagesCleanupTimer.expires_from_now (boost::posix_time::seconds(INCOMPLETE_MESSAGES_CLEANUP_TIMEOUT)); + auto s = m_Session.shared_from_this(); + m_IncompleteMessagesCleanupTimer.async_wait ([s](const boost::system::error_code& ecode) + { s->m_Data.HandleIncompleteMessagesCleanupTimer (ecode); }); + } + + void SSUData::HandleIncompleteMessagesCleanupTimer (const boost::system::error_code& ecode) + { + if (ecode != boost::asio::error::operation_aborted) + { + uint32_t ts = i2p::util::GetSecondsSinceEpoch (); + for (auto it = m_IncompleteMessages.begin (); it != m_IncompleteMessages.end ();) + { + if (ts > it->second->lastFragmentInsertTime + INCOMPLETE_MESSAGES_CLEANUP_TIMEOUT) + { + LogPrint (eLogError, "SSU message ", it->first, " was not completed in ", INCOMPLETE_MESSAGES_CLEANUP_TIMEOUT, " seconds. Deleted"); + it = m_IncompleteMessages.erase (it); + } + else + it++; + } + ScheduleIncompleteMessagesCleanup (); + } + } } } diff --git a/SSUData.h b/SSUData.h index 60d5057e..6eb8a22c 100644 --- a/SSUData.h +++ b/SSUData.h @@ -17,110 +17,110 @@ namespace i2p namespace transport { - const size_t SSU_MTU_V4 = 1484; - const size_t SSU_MTU_V6 = 1472; - const size_t IPV4_HEADER_SIZE = 20; - const size_t IPV6_HEADER_SIZE = 40; - const size_t UDP_HEADER_SIZE = 8; - const size_t SSU_V4_MAX_PACKET_SIZE = SSU_MTU_V4 - IPV4_HEADER_SIZE - UDP_HEADER_SIZE; // 1456 - const size_t SSU_V6_MAX_PACKET_SIZE = SSU_MTU_V6 - IPV6_HEADER_SIZE - UDP_HEADER_SIZE; // 1424 - const int RESEND_INTERVAL = 3; // in seconds - const int MAX_NUM_RESENDS = 5; - const int DECAY_INTERVAL = 20; // in seconds - const int MAX_NUM_RECEIVED_MESSAGES = 1000; // how many msgID we store for duplicates check - const int INCOMPLETE_MESSAGES_CLEANUP_TIMEOUT = 30; // in seconds - // data flags - const uint8_t DATA_FLAG_EXTENDED_DATA_INCLUDED = 0x02; - const uint8_t DATA_FLAG_WANT_REPLY = 0x04; - const uint8_t DATA_FLAG_REQUEST_PREVIOUS_ACKS = 0x08; - const uint8_t DATA_FLAG_EXPLICIT_CONGESTION_NOTIFICATION = 0x10; - const uint8_t DATA_FLAG_ACK_BITFIELDS_INCLUDED = 0x40; - const uint8_t DATA_FLAG_EXPLICIT_ACKS_INCLUDED = 0x80; + const size_t SSU_MTU_V4 = 1484; + const size_t SSU_MTU_V6 = 1472; + const size_t IPV4_HEADER_SIZE = 20; + const size_t IPV6_HEADER_SIZE = 40; + const size_t UDP_HEADER_SIZE = 8; + const size_t SSU_V4_MAX_PACKET_SIZE = SSU_MTU_V4 - IPV4_HEADER_SIZE - UDP_HEADER_SIZE; // 1456 + const size_t SSU_V6_MAX_PACKET_SIZE = SSU_MTU_V6 - IPV6_HEADER_SIZE - UDP_HEADER_SIZE; // 1424 + const int RESEND_INTERVAL = 3; // in seconds + const int MAX_NUM_RESENDS = 5; + const int DECAY_INTERVAL = 20; // in seconds + const int MAX_NUM_RECEIVED_MESSAGES = 1000; // how many msgID we store for duplicates check + const int INCOMPLETE_MESSAGES_CLEANUP_TIMEOUT = 30; // in seconds + // data flags + const uint8_t DATA_FLAG_EXTENDED_DATA_INCLUDED = 0x02; + const uint8_t DATA_FLAG_WANT_REPLY = 0x04; + const uint8_t DATA_FLAG_REQUEST_PREVIOUS_ACKS = 0x08; + const uint8_t DATA_FLAG_EXPLICIT_CONGESTION_NOTIFICATION = 0x10; + const uint8_t DATA_FLAG_ACK_BITFIELDS_INCLUDED = 0x40; + const uint8_t DATA_FLAG_EXPLICIT_ACKS_INCLUDED = 0x80; - struct Fragment - { - int fragmentNum; - size_t len; - bool isLast; - uint8_t buf[SSU_V4_MAX_PACKET_SIZE + 18]; // use biggest + struct Fragment + { + int fragmentNum; + size_t len; + bool isLast; + uint8_t buf[SSU_V4_MAX_PACKET_SIZE + 18]; // use biggest - Fragment () = default; - Fragment (int n, const uint8_t * b, int l, bool last): - fragmentNum (n), len (l), isLast (last) { memcpy (buf, b, len); }; - }; + Fragment () = default; + Fragment (int n, const uint8_t * b, int l, bool last): + fragmentNum (n), len (l), isLast (last) { memcpy (buf, b, len); }; + }; - struct FragmentCmp - { - bool operator() (const std::unique_ptr& f1, const std::unique_ptr& f2) const - { - return f1->fragmentNum < f2->fragmentNum; - }; - }; - - struct IncompleteMessage - { - std::shared_ptr msg; - int nextFragmentNum; - uint32_t lastFragmentInsertTime; // in seconds - std::set, FragmentCmp> savedFragments; - - IncompleteMessage (std::shared_ptr m): msg (m), nextFragmentNum (0), lastFragmentInsertTime (0) {}; - void AttachNextFragment (const uint8_t * fragment, size_t fragmentSize); - }; + struct FragmentCmp + { + bool operator() (const std::unique_ptr& f1, const std::unique_ptr& f2) const + { + return f1->fragmentNum < f2->fragmentNum; + }; + }; + + struct IncompleteMessage + { + std::shared_ptr msg; + int nextFragmentNum; + uint32_t lastFragmentInsertTime; // in seconds + std::set, FragmentCmp> savedFragments; + + IncompleteMessage (std::shared_ptr m): msg (m), nextFragmentNum (0), lastFragmentInsertTime (0) {}; + void AttachNextFragment (const uint8_t * fragment, size_t fragmentSize); + }; - struct SentMessage - { - std::vector > fragments; - uint32_t nextResendTime; // in seconds - int numResends; - }; - - class SSUSession; - class SSUData - { - public: + struct SentMessage + { + std::vector > fragments; + uint32_t nextResendTime; // in seconds + int numResends; + }; + + class SSUSession; + class SSUData + { + public: - SSUData (SSUSession& session); - ~SSUData (); + SSUData (SSUSession& session); + ~SSUData (); - void Start (); - void Stop (); - - void ProcessMessage (uint8_t * buf, size_t len); - void FlushReceivedMessage (); - void Send (std::shared_ptr msg); + void Start (); + void Stop (); + + void ProcessMessage (uint8_t * buf, size_t len); + void FlushReceivedMessage (); + void Send (std::shared_ptr msg); - void UpdatePacketSize (const i2p::data::IdentHash& remoteIdent); + void UpdatePacketSize (const i2p::data::IdentHash& remoteIdent); - private: + private: - void SendMsgAck (uint32_t msgID); - void SendFragmentAck (uint32_t msgID, int fragmentNum); - void ProcessAcks (uint8_t *& buf, uint8_t flag); - void ProcessFragments (uint8_t * buf); - void ProcessSentMessageAck (uint32_t msgID); + void SendMsgAck (uint32_t msgID); + void SendFragmentAck (uint32_t msgID, int fragmentNum); + void ProcessAcks (uint8_t *& buf, uint8_t flag); + void ProcessFragments (uint8_t * buf); + void ProcessSentMessageAck (uint32_t msgID); - void ScheduleResend (); - void HandleResendTimer (const boost::system::error_code& ecode); + void ScheduleResend (); + void HandleResendTimer (const boost::system::error_code& ecode); - void ScheduleDecay (); - void HandleDecayTimer (const boost::system::error_code& ecode); + void ScheduleDecay (); + void HandleDecayTimer (const boost::system::error_code& ecode); - void ScheduleIncompleteMessagesCleanup (); - void HandleIncompleteMessagesCleanupTimer (const boost::system::error_code& ecode); - - void AdjustPacketSize (const i2p::data::RouterInfo& remoteRouter); - - private: + void ScheduleIncompleteMessagesCleanup (); + void HandleIncompleteMessagesCleanupTimer (const boost::system::error_code& ecode); + + void AdjustPacketSize (const i2p::data::RouterInfo& remoteRouter); + + private: - SSUSession& m_Session; - std::map > m_IncompleteMessages; - std::map > m_SentMessages; - std::set m_ReceivedMessages; - boost::asio::deadline_timer m_ResendTimer, m_DecayTimer, m_IncompleteMessagesCleanupTimer; - int m_MaxPacketSize, m_PacketSize; - i2p::I2NPMessagesHandler m_Handler; - }; + SSUSession& m_Session; + std::map > m_IncompleteMessages; + std::map > m_SentMessages; + std::set m_ReceivedMessages; + boost::asio::deadline_timer m_ResendTimer, m_DecayTimer, m_IncompleteMessagesCleanupTimer; + int m_MaxPacketSize, m_PacketSize; + i2p::I2NPMessagesHandler m_Handler; + }; } } diff --git a/SSUSession.cpp b/SSUSession.cpp index 1b89e9ba..8f2c2d66 100644 --- a/SSUSession.cpp +++ b/SSUSession.cpp @@ -13,1078 +13,1078 @@ namespace i2p { namespace transport { - SSUSession::SSUSession (SSUServer& server, boost::asio::ip::udp::endpoint& remoteEndpoint, - std::shared_ptr router, bool peerTest ): TransportSession (router), - m_Server (server), m_RemoteEndpoint (remoteEndpoint), m_Timer (GetService ()), - m_PeerTest (peerTest),m_State (eSessionStateUnknown), m_IsSessionKey (false), - m_RelayTag (0),m_Data (*this), m_IsDataReceived (false) - { - m_CreationTime = i2p::util::GetSecondsSinceEpoch (); - } + SSUSession::SSUSession (SSUServer& server, boost::asio::ip::udp::endpoint& remoteEndpoint, + std::shared_ptr router, bool peerTest ): TransportSession (router), + m_Server (server), m_RemoteEndpoint (remoteEndpoint), m_Timer (GetService ()), + m_PeerTest (peerTest),m_State (eSessionStateUnknown), m_IsSessionKey (false), + m_RelayTag (0),m_Data (*this), m_IsDataReceived (false) + { + m_CreationTime = i2p::util::GetSecondsSinceEpoch (); + } - SSUSession::~SSUSession () - { - } + SSUSession::~SSUSession () + { + } - boost::asio::io_service& SSUSession::GetService () - { - return IsV6 () ? m_Server.GetServiceV6 () : m_Server.GetService (); - } - - void SSUSession::CreateAESandMacKey (const uint8_t * pubKey) - { - CryptoPP::DH dh (i2p::crypto::elgp, i2p::crypto::elgg); - uint8_t sharedKey[256]; - if (!dh.Agree (sharedKey, m_DHKeysPair->privateKey, pubKey)) - { - LogPrint (eLogError, "Couldn't create shared key"); - return; - }; + boost::asio::io_service& SSUSession::GetService () + { + return IsV6 () ? m_Server.GetServiceV6 () : m_Server.GetService (); + } + + void SSUSession::CreateAESandMacKey (const uint8_t * pubKey) + { + CryptoPP::DH dh (i2p::crypto::elgp, i2p::crypto::elgg); + uint8_t sharedKey[256]; + if (!dh.Agree (sharedKey, m_DHKeysPair->privateKey, pubKey)) + { + LogPrint (eLogError, "Couldn't create shared key"); + return; + }; - uint8_t * sessionKey = m_SessionKey, * macKey = m_MacKey; - if (sharedKey[0] & 0x80) - { - sessionKey[0] = 0; - memcpy (sessionKey + 1, sharedKey, 31); - memcpy (macKey, sharedKey + 31, 32); - } - else if (sharedKey[0]) - { - memcpy (sessionKey, sharedKey, 32); - memcpy (macKey, sharedKey + 32, 32); - } - else - { - // find first non-zero byte - uint8_t * nonZero = sharedKey + 1; - while (!*nonZero) - { - nonZero++; - if (nonZero - sharedKey > 32) - { - LogPrint ("First 32 bytes of shared key is all zeros. Ignored"); - return; - } - } - - memcpy (sessionKey, nonZero, 32); - CryptoPP::SHA256().CalculateDigest(macKey, nonZero, 64 - (nonZero - sharedKey)); - } - m_IsSessionKey = true; - m_SessionKeyEncryption.SetKey (m_SessionKey); - m_SessionKeyDecryption.SetKey (m_SessionKey); - } + uint8_t * sessionKey = m_SessionKey, * macKey = m_MacKey; + if (sharedKey[0] & 0x80) + { + sessionKey[0] = 0; + memcpy (sessionKey + 1, sharedKey, 31); + memcpy (macKey, sharedKey + 31, 32); + } + else if (sharedKey[0]) + { + memcpy (sessionKey, sharedKey, 32); + memcpy (macKey, sharedKey + 32, 32); + } + else + { + // find first non-zero byte + uint8_t * nonZero = sharedKey + 1; + while (!*nonZero) + { + nonZero++; + if (nonZero - sharedKey > 32) + { + LogPrint ("First 32 bytes of shared key is all zeros. Ignored"); + return; + } + } + + memcpy (sessionKey, nonZero, 32); + CryptoPP::SHA256().CalculateDigest(macKey, nonZero, 64 - (nonZero - sharedKey)); + } + m_IsSessionKey = true; + m_SessionKeyEncryption.SetKey (m_SessionKey); + m_SessionKeyDecryption.SetKey (m_SessionKey); + } - void SSUSession::ProcessNextMessage (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint) - { - m_NumReceivedBytes += len; - i2p::transport::transports.UpdateReceivedBytes (len); - if (m_State == eSessionStateIntroduced) - { - // HolePunch received - LogPrint ("SSU HolePunch of ", len, " bytes received"); - m_State = eSessionStateUnknown; - Connect (); - } - else - { - if (!len) return; // ignore zero-length packets - if (m_State == eSessionStateEstablished) - ScheduleTermination (); - - if (m_IsSessionKey && Validate (buf, len, m_MacKey)) // try session key first - DecryptSessionKey (buf, len); - else - { - // try intro key depending on side - auto introKey = GetIntroKey (); - if (introKey && Validate (buf, len, introKey)) - Decrypt (buf, len, introKey); - else - { - // try own intro key - auto address = i2p::context.GetRouterInfo ().GetSSUAddress (); - if (!address) - { - LogPrint (eLogError, "SSU is not supported"); - return; - } - if (Validate (buf, len, address->key)) - Decrypt (buf, len, address->key); - else - { - LogPrint (eLogError, "MAC verification failed ", len, " bytes from ", senderEndpoint); - m_Server.DeleteSession (shared_from_this ()); - return; - } - } - } - // successfully decrypted - ProcessMessage (buf, len, senderEndpoint); - } - } + void SSUSession::ProcessNextMessage (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint) + { + m_NumReceivedBytes += len; + i2p::transport::transports.UpdateReceivedBytes (len); + if (m_State == eSessionStateIntroduced) + { + // HolePunch received + LogPrint ("SSU HolePunch of ", len, " bytes received"); + m_State = eSessionStateUnknown; + Connect (); + } + else + { + if (!len) return; // ignore zero-length packets + if (m_State == eSessionStateEstablished) + ScheduleTermination (); + + if (m_IsSessionKey && Validate (buf, len, m_MacKey)) // try session key first + DecryptSessionKey (buf, len); + else + { + // try intro key depending on side + auto introKey = GetIntroKey (); + if (introKey && Validate (buf, len, introKey)) + Decrypt (buf, len, introKey); + else + { + // try own intro key + auto address = i2p::context.GetRouterInfo ().GetSSUAddress (); + if (!address) + { + LogPrint (eLogError, "SSU is not supported"); + return; + } + if (Validate (buf, len, address->key)) + Decrypt (buf, len, address->key); + else + { + LogPrint (eLogError, "MAC verification failed ", len, " bytes from ", senderEndpoint); + m_Server.DeleteSession (shared_from_this ()); + return; + } + } + } + // successfully decrypted + ProcessMessage (buf, len, senderEndpoint); + } + } - void SSUSession::ProcessMessage (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint) - { - len -= (len & 0x0F); // %16, delete extra padding - if (len <= sizeof (SSUHeader)) return; // drop empty message - //TODO: since we are accessing a uint8_t this is unlikely to crash due to alignment but should be improved - SSUHeader * header = (SSUHeader *)buf; - switch (header->GetPayloadType ()) - { - case PAYLOAD_TYPE_DATA: - ProcessData (buf + sizeof (SSUHeader), len - sizeof (SSUHeader)); - break; - case PAYLOAD_TYPE_SESSION_REQUEST: - ProcessSessionRequest (buf, len, senderEndpoint); - break; - case PAYLOAD_TYPE_SESSION_CREATED: - ProcessSessionCreated (buf, len); - break; - case PAYLOAD_TYPE_SESSION_CONFIRMED: - ProcessSessionConfirmed (buf, len); - break; - case PAYLOAD_TYPE_PEER_TEST: - LogPrint (eLogDebug, "SSU peer test received"); - ProcessPeerTest (buf + sizeof (SSUHeader), len - sizeof (SSUHeader), senderEndpoint); - break; - case PAYLOAD_TYPE_SESSION_DESTROYED: - { - LogPrint (eLogDebug, "SSU session destroy received"); - m_Server.DeleteSession (shared_from_this ()); - break; - } - case PAYLOAD_TYPE_RELAY_RESPONSE: - ProcessRelayResponse (buf, len); - if (m_State != eSessionStateEstablished) - m_Server.DeleteSession (shared_from_this ()); - break; - case PAYLOAD_TYPE_RELAY_REQUEST: - LogPrint (eLogDebug, "SSU relay request received"); - ProcessRelayRequest (buf + sizeof (SSUHeader), len - sizeof (SSUHeader), senderEndpoint); - break; - case PAYLOAD_TYPE_RELAY_INTRO: - LogPrint (eLogDebug, "SSU relay intro received"); - ProcessRelayIntro (buf + sizeof (SSUHeader), len - sizeof (SSUHeader)); - break; - default: - LogPrint (eLogWarning, "Unexpected SSU payload type ", (int)header->GetPayloadType ()); - } - } + void SSUSession::ProcessMessage (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint) + { + len -= (len & 0x0F); // %16, delete extra padding + if (len <= sizeof (SSUHeader)) return; // drop empty message + //TODO: since we are accessing a uint8_t this is unlikely to crash due to alignment but should be improved + SSUHeader * header = (SSUHeader *)buf; + switch (header->GetPayloadType ()) + { + case PAYLOAD_TYPE_DATA: + ProcessData (buf + sizeof (SSUHeader), len - sizeof (SSUHeader)); + break; + case PAYLOAD_TYPE_SESSION_REQUEST: + ProcessSessionRequest (buf, len, senderEndpoint); + break; + case PAYLOAD_TYPE_SESSION_CREATED: + ProcessSessionCreated (buf, len); + break; + case PAYLOAD_TYPE_SESSION_CONFIRMED: + ProcessSessionConfirmed (buf, len); + break; + case PAYLOAD_TYPE_PEER_TEST: + LogPrint (eLogDebug, "SSU peer test received"); + ProcessPeerTest (buf + sizeof (SSUHeader), len - sizeof (SSUHeader), senderEndpoint); + break; + case PAYLOAD_TYPE_SESSION_DESTROYED: + { + LogPrint (eLogDebug, "SSU session destroy received"); + m_Server.DeleteSession (shared_from_this ()); + break; + } + case PAYLOAD_TYPE_RELAY_RESPONSE: + ProcessRelayResponse (buf, len); + if (m_State != eSessionStateEstablished) + m_Server.DeleteSession (shared_from_this ()); + break; + case PAYLOAD_TYPE_RELAY_REQUEST: + LogPrint (eLogDebug, "SSU relay request received"); + ProcessRelayRequest (buf + sizeof (SSUHeader), len - sizeof (SSUHeader), senderEndpoint); + break; + case PAYLOAD_TYPE_RELAY_INTRO: + LogPrint (eLogDebug, "SSU relay intro received"); + ProcessRelayIntro (buf + sizeof (SSUHeader), len - sizeof (SSUHeader)); + break; + default: + LogPrint (eLogWarning, "Unexpected SSU payload type ", (int)header->GetPayloadType ()); + } + } - void SSUSession::ProcessSessionRequest (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint) - { - LogPrint (eLogDebug, "Session request received"); - m_RemoteEndpoint = senderEndpoint; - if (!m_DHKeysPair) - m_DHKeysPair = transports.GetNextDHKeysPair (); - CreateAESandMacKey (buf + sizeof (SSUHeader)); - SendSessionCreated (buf + sizeof (SSUHeader)); - } + void SSUSession::ProcessSessionRequest (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint) + { + LogPrint (eLogDebug, "Session request received"); + m_RemoteEndpoint = senderEndpoint; + if (!m_DHKeysPair) + m_DHKeysPair = transports.GetNextDHKeysPair (); + CreateAESandMacKey (buf + sizeof (SSUHeader)); + SendSessionCreated (buf + sizeof (SSUHeader)); + } - void SSUSession::ProcessSessionCreated (uint8_t * buf, size_t len) - { - if (!m_RemoteRouter || !m_DHKeysPair) - { - LogPrint (eLogWarning, "Unsolicited session created message"); - return; - } + void SSUSession::ProcessSessionCreated (uint8_t * buf, size_t len) + { + if (!m_RemoteRouter || !m_DHKeysPair) + { + LogPrint (eLogWarning, "Unsolicited session created message"); + return; + } - LogPrint (eLogDebug, "Session created received"); - m_Timer.cancel (); // connect timer - SignedData s; // x,y, our IP, our port, remote IP, remote port, relayTag, signed on time - uint8_t * payload = buf + sizeof (SSUHeader); - uint8_t * y = payload; - CreateAESandMacKey (y); - s.Insert (m_DHKeysPair->publicKey, 256); // x - s.Insert (y, 256); // y - payload += 256; - uint8_t addressSize = *payload; - payload += 1; // size - uint8_t * ourAddress = payload; - boost::asio::ip::address ourIP; - if (addressSize == 4) // v4 - { - boost::asio::ip::address_v4::bytes_type bytes; - memcpy (bytes.data (), ourAddress, 4); - ourIP = boost::asio::ip::address_v4 (bytes); - } - else // v6 - { - boost::asio::ip::address_v6::bytes_type bytes; - memcpy (bytes.data (), ourAddress, 16); - ourIP = boost::asio::ip::address_v6 (bytes); - } - s.Insert (ourAddress, addressSize); // our IP - payload += addressSize; // address - uint16_t ourPort = bufbe16toh (payload); - s.Insert (payload, 2); // our port - payload += 2; // port - LogPrint ("Our external address is ", ourIP.to_string (), ":", ourPort); - i2p::context.UpdateAddress (ourIP); - if (m_RemoteEndpoint.address ().is_v4 ()) - s.Insert (m_RemoteEndpoint.address ().to_v4 ().to_bytes ().data (), 4); // remote IP v4 - else - s.Insert (m_RemoteEndpoint.address ().to_v6 ().to_bytes ().data (), 16); // remote IP v6 - s.Insert (htobe16 (m_RemoteEndpoint.port ())); // remote port - s.Insert (payload, 8); // relayTag and signed on time - m_RelayTag = bufbe32toh (payload); - payload += 4; // relayTag - payload += 4; // signed on time - // decrypt signature - size_t signatureLen = m_RemoteIdentity.GetSignatureLen (); - size_t paddingSize = signatureLen & 0x0F; // %16 - if (paddingSize > 0) signatureLen += (16 - paddingSize); - //TODO: since we are accessing a uint8_t this is unlikely to crash due to alignment but should be improved - m_SessionKeyDecryption.SetIV (((SSUHeader *)buf)->iv); - m_SessionKeyDecryption.Decrypt (payload, signatureLen, payload); - // verify - if (!s.Verify (m_RemoteIdentity, payload)) - LogPrint (eLogError, "SSU signature verification failed"); - m_RemoteIdentity.DropVerifier (); - - SendSessionConfirmed (y, ourAddress, addressSize + 2); - } + LogPrint (eLogDebug, "Session created received"); + m_Timer.cancel (); // connect timer + SignedData s; // x,y, our IP, our port, remote IP, remote port, relayTag, signed on time + uint8_t * payload = buf + sizeof (SSUHeader); + uint8_t * y = payload; + CreateAESandMacKey (y); + s.Insert (m_DHKeysPair->publicKey, 256); // x + s.Insert (y, 256); // y + payload += 256; + uint8_t addressSize = *payload; + payload += 1; // size + uint8_t * ourAddress = payload; + boost::asio::ip::address ourIP; + if (addressSize == 4) // v4 + { + boost::asio::ip::address_v4::bytes_type bytes; + memcpy (bytes.data (), ourAddress, 4); + ourIP = boost::asio::ip::address_v4 (bytes); + } + else // v6 + { + boost::asio::ip::address_v6::bytes_type bytes; + memcpy (bytes.data (), ourAddress, 16); + ourIP = boost::asio::ip::address_v6 (bytes); + } + s.Insert (ourAddress, addressSize); // our IP + payload += addressSize; // address + uint16_t ourPort = bufbe16toh (payload); + s.Insert (payload, 2); // our port + payload += 2; // port + LogPrint ("Our external address is ", ourIP.to_string (), ":", ourPort); + i2p::context.UpdateAddress (ourIP); + if (m_RemoteEndpoint.address ().is_v4 ()) + s.Insert (m_RemoteEndpoint.address ().to_v4 ().to_bytes ().data (), 4); // remote IP v4 + else + s.Insert (m_RemoteEndpoint.address ().to_v6 ().to_bytes ().data (), 16); // remote IP v6 + s.Insert (htobe16 (m_RemoteEndpoint.port ())); // remote port + s.Insert (payload, 8); // relayTag and signed on time + m_RelayTag = bufbe32toh (payload); + payload += 4; // relayTag + payload += 4; // signed on time + // decrypt signature + size_t signatureLen = m_RemoteIdentity.GetSignatureLen (); + size_t paddingSize = signatureLen & 0x0F; // %16 + if (paddingSize > 0) signatureLen += (16 - paddingSize); + //TODO: since we are accessing a uint8_t this is unlikely to crash due to alignment but should be improved + m_SessionKeyDecryption.SetIV (((SSUHeader *)buf)->iv); + m_SessionKeyDecryption.Decrypt (payload, signatureLen, payload); + // verify + if (!s.Verify (m_RemoteIdentity, payload)) + LogPrint (eLogError, "SSU signature verification failed"); + m_RemoteIdentity.DropVerifier (); + + SendSessionConfirmed (y, ourAddress, addressSize + 2); + } - void SSUSession::ProcessSessionConfirmed (uint8_t * buf, size_t len) - { - LogPrint (eLogDebug, "Session confirmed received"); - uint8_t * payload = buf + sizeof (SSUHeader); - payload++; // identity fragment info - uint16_t identitySize = bufbe16toh (payload); - payload += 2; // size of identity fragment - m_RemoteIdentity.FromBuffer (payload, identitySize); - m_Data.UpdatePacketSize (m_RemoteIdentity.GetIdentHash ()); - payload += identitySize; // identity - payload += 4; // signed-on time - size_t paddingSize = (payload - buf) + m_RemoteIdentity.GetSignatureLen (); - paddingSize &= 0x0F; // %16 - if (paddingSize > 0) paddingSize = 16 - paddingSize; - payload += paddingSize; - // TODO: verify signature (need data from session request), payload points to signature - m_Data.Send (CreateDeliveryStatusMsg (0)); - Established (); - } + void SSUSession::ProcessSessionConfirmed (uint8_t * buf, size_t len) + { + LogPrint (eLogDebug, "Session confirmed received"); + uint8_t * payload = buf + sizeof (SSUHeader); + payload++; // identity fragment info + uint16_t identitySize = bufbe16toh (payload); + payload += 2; // size of identity fragment + m_RemoteIdentity.FromBuffer (payload, identitySize); + m_Data.UpdatePacketSize (m_RemoteIdentity.GetIdentHash ()); + payload += identitySize; // identity + payload += 4; // signed-on time + size_t paddingSize = (payload - buf) + m_RemoteIdentity.GetSignatureLen (); + paddingSize &= 0x0F; // %16 + if (paddingSize > 0) paddingSize = 16 - paddingSize; + payload += paddingSize; + // TODO: verify signature (need data from session request), payload points to signature + m_Data.Send (CreateDeliveryStatusMsg (0)); + Established (); + } - void SSUSession::SendSessionRequest () - { - auto introKey = GetIntroKey (); - if (!introKey) - { - LogPrint (eLogError, "SSU is not supported"); - return; - } - - uint8_t buf[320 + 18] = {}; // 304 bytes for ipv4, 320 for ipv6, all set to 0 - uint8_t * payload = buf + sizeof (SSUHeader); - memcpy (payload, m_DHKeysPair->publicKey, 256); // x - bool isV4 = m_RemoteEndpoint.address ().is_v4 (); - if (isV4) - { - payload[256] = 4; - memcpy (payload + 257, m_RemoteEndpoint.address ().to_v4 ().to_bytes ().data(), 4); - } - else - { - payload[256] = 16; - memcpy (payload + 257, m_RemoteEndpoint.address ().to_v6 ().to_bytes ().data(), 16); - } - - uint8_t iv[16]; - CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator (); - rnd.GenerateBlock (iv, 16); // random iv - FillHeaderAndEncrypt (PAYLOAD_TYPE_SESSION_REQUEST, buf, isV4 ? 304 : 320, introKey, iv, introKey); - m_Server.Send (buf, isV4 ? 304 : 320, m_RemoteEndpoint); - } + void SSUSession::SendSessionRequest () + { + auto introKey = GetIntroKey (); + if (!introKey) + { + LogPrint (eLogError, "SSU is not supported"); + return; + } + + uint8_t buf[320 + 18] = {}; // 304 bytes for ipv4, 320 for ipv6, all set to 0 + uint8_t * payload = buf + sizeof (SSUHeader); + memcpy (payload, m_DHKeysPair->publicKey, 256); // x + bool isV4 = m_RemoteEndpoint.address ().is_v4 (); + if (isV4) + { + payload[256] = 4; + memcpy (payload + 257, m_RemoteEndpoint.address ().to_v4 ().to_bytes ().data(), 4); + } + else + { + payload[256] = 16; + memcpy (payload + 257, m_RemoteEndpoint.address ().to_v6 ().to_bytes ().data(), 16); + } + + uint8_t iv[16]; + CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator (); + rnd.GenerateBlock (iv, 16); // random iv + FillHeaderAndEncrypt (PAYLOAD_TYPE_SESSION_REQUEST, buf, isV4 ? 304 : 320, introKey, iv, introKey); + m_Server.Send (buf, isV4 ? 304 : 320, m_RemoteEndpoint); + } - void SSUSession::SendRelayRequest (uint32_t iTag, const uint8_t * iKey) - { - auto address = i2p::context.GetRouterInfo ().GetSSUAddress (); - if (!address) - { - LogPrint (eLogError, "SSU is not supported"); - return; - } - - uint8_t buf[96 + 18] = {}; - uint8_t * payload = buf + sizeof (SSUHeader); - htobe32buf (payload, iTag); - payload += 4; - *payload = 0; // no address - payload++; - htobuf16(payload, 0); // port = 0 - payload += 2; - *payload = 0; // challenge - payload++; - memcpy (payload, (const uint8_t *)address->key, 32); - payload += 32; - CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator (); - htobe32buf (payload, rnd.GenerateWord32 ()); // nonce + void SSUSession::SendRelayRequest (uint32_t iTag, const uint8_t * iKey) + { + auto address = i2p::context.GetRouterInfo ().GetSSUAddress (); + if (!address) + { + LogPrint (eLogError, "SSU is not supported"); + return; + } + + uint8_t buf[96 + 18] = {}; + uint8_t * payload = buf + sizeof (SSUHeader); + htobe32buf (payload, iTag); + payload += 4; + *payload = 0; // no address + payload++; + htobuf16(payload, 0); // port = 0 + payload += 2; + *payload = 0; // challenge + payload++; + memcpy (payload, (const uint8_t *)address->key, 32); + payload += 32; + CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator (); + htobe32buf (payload, rnd.GenerateWord32 ()); // nonce - uint8_t iv[16]; - rnd.GenerateBlock (iv, 16); // random iv - if (m_State == eSessionStateEstablished) - FillHeaderAndEncrypt (PAYLOAD_TYPE_RELAY_REQUEST, buf, 96, m_SessionKey, iv, m_MacKey); - else - FillHeaderAndEncrypt (PAYLOAD_TYPE_RELAY_REQUEST, buf, 96, iKey, iv, iKey); - m_Server.Send (buf, 96, m_RemoteEndpoint); - } + uint8_t iv[16]; + rnd.GenerateBlock (iv, 16); // random iv + if (m_State == eSessionStateEstablished) + FillHeaderAndEncrypt (PAYLOAD_TYPE_RELAY_REQUEST, buf, 96, m_SessionKey, iv, m_MacKey); + else + FillHeaderAndEncrypt (PAYLOAD_TYPE_RELAY_REQUEST, buf, 96, iKey, iv, iKey); + m_Server.Send (buf, 96, m_RemoteEndpoint); + } - void SSUSession::SendSessionCreated (const uint8_t * x) - { - auto introKey = GetIntroKey (); - auto address = IsV6 () ? i2p::context.GetRouterInfo ().GetSSUV6Address () : - i2p::context.GetRouterInfo ().GetSSUAddress (true); //v4 only - if (!introKey || !address) - { - LogPrint (eLogError, "SSU is not supported"); - return; - } - CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator (); - SignedData s; // x,y, remote IP, remote port, our IP, our port, relayTag, signed on time - s.Insert (x, 256); // x + void SSUSession::SendSessionCreated (const uint8_t * x) + { + auto introKey = GetIntroKey (); + auto address = IsV6 () ? i2p::context.GetRouterInfo ().GetSSUV6Address () : + i2p::context.GetRouterInfo ().GetSSUAddress (true); //v4 only + if (!introKey || !address) + { + LogPrint (eLogError, "SSU is not supported"); + return; + } + CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator (); + SignedData s; // x,y, remote IP, remote port, our IP, our port, relayTag, signed on time + s.Insert (x, 256); // x - uint8_t buf[384 + 18] = {}; - uint8_t * payload = buf + sizeof (SSUHeader); - memcpy (payload, m_DHKeysPair->publicKey, 256); - s.Insert (payload, 256); // y - payload += 256; - if (m_RemoteEndpoint.address ().is_v4 ()) - { - // ipv4 - *payload = 4; - payload++; - memcpy (payload, m_RemoteEndpoint.address ().to_v4 ().to_bytes ().data(), 4); - s.Insert (payload, 4); // remote endpoint IP V4 - payload += 4; - } - else - { - // ipv6 - *payload = 16; - payload++; - memcpy (payload, m_RemoteEndpoint.address ().to_v6 ().to_bytes ().data(), 16); - s.Insert (payload, 16); // remote endpoint IP V6 - payload += 16; - } - htobe16buf (payload, m_RemoteEndpoint.port ()); - s.Insert (payload, 2); // remote port - payload += 2; - if (address->host.is_v4 ()) - s.Insert (address->host.to_v4 ().to_bytes ().data (), 4); // our IP V4 - else - s.Insert (address->host.to_v6 ().to_bytes ().data (), 16); // our IP V6 - s.Insert (htobe16 (address->port)); // our port - uint32_t relayTag = 0; - if (i2p::context.GetRouterInfo ().IsIntroducer ()) - { - relayTag = rnd.GenerateWord32 (); - if (!relayTag) relayTag = 1; - m_Server.AddRelay (relayTag, m_RemoteEndpoint); - } - htobe32buf (payload, relayTag); - payload += 4; // relay tag - htobe32buf (payload, i2p::util::GetSecondsSinceEpoch ()); // signed on time - payload += 4; - s.Insert (payload - 8, 8); // relayTag and signed on time - s.Sign (i2p::context.GetPrivateKeys (), payload); // DSA signature - // TODO: fill padding with random data + uint8_t buf[384 + 18] = {}; + uint8_t * payload = buf + sizeof (SSUHeader); + memcpy (payload, m_DHKeysPair->publicKey, 256); + s.Insert (payload, 256); // y + payload += 256; + if (m_RemoteEndpoint.address ().is_v4 ()) + { + // ipv4 + *payload = 4; + payload++; + memcpy (payload, m_RemoteEndpoint.address ().to_v4 ().to_bytes ().data(), 4); + s.Insert (payload, 4); // remote endpoint IP V4 + payload += 4; + } + else + { + // ipv6 + *payload = 16; + payload++; + memcpy (payload, m_RemoteEndpoint.address ().to_v6 ().to_bytes ().data(), 16); + s.Insert (payload, 16); // remote endpoint IP V6 + payload += 16; + } + htobe16buf (payload, m_RemoteEndpoint.port ()); + s.Insert (payload, 2); // remote port + payload += 2; + if (address->host.is_v4 ()) + s.Insert (address->host.to_v4 ().to_bytes ().data (), 4); // our IP V4 + else + s.Insert (address->host.to_v6 ().to_bytes ().data (), 16); // our IP V6 + s.Insert (htobe16 (address->port)); // our port + uint32_t relayTag = 0; + if (i2p::context.GetRouterInfo ().IsIntroducer ()) + { + relayTag = rnd.GenerateWord32 (); + if (!relayTag) relayTag = 1; + m_Server.AddRelay (relayTag, m_RemoteEndpoint); + } + htobe32buf (payload, relayTag); + payload += 4; // relay tag + htobe32buf (payload, i2p::util::GetSecondsSinceEpoch ()); // signed on time + payload += 4; + s.Insert (payload - 8, 8); // relayTag and signed on time + s.Sign (i2p::context.GetPrivateKeys (), payload); // DSA signature + // TODO: fill padding with random data - uint8_t iv[16]; - rnd.GenerateBlock (iv, 16); // random iv - // encrypt signature and padding with newly created session key - size_t signatureLen = i2p::context.GetIdentity ().GetSignatureLen (); - size_t paddingSize = signatureLen & 0x0F; // %16 - if (paddingSize > 0) signatureLen += (16 - paddingSize); - m_SessionKeyEncryption.SetIV (iv); - m_SessionKeyEncryption.Encrypt (payload, signatureLen, payload); - payload += signatureLen; - size_t msgLen = payload - buf; - - // encrypt message with intro key - FillHeaderAndEncrypt (PAYLOAD_TYPE_SESSION_CREATED, buf, msgLen, introKey, iv, introKey); - Send (buf, msgLen); - } + uint8_t iv[16]; + rnd.GenerateBlock (iv, 16); // random iv + // encrypt signature and padding with newly created session key + size_t signatureLen = i2p::context.GetIdentity ().GetSignatureLen (); + size_t paddingSize = signatureLen & 0x0F; // %16 + if (paddingSize > 0) signatureLen += (16 - paddingSize); + m_SessionKeyEncryption.SetIV (iv); + m_SessionKeyEncryption.Encrypt (payload, signatureLen, payload); + payload += signatureLen; + size_t msgLen = payload - buf; + + // encrypt message with intro key + FillHeaderAndEncrypt (PAYLOAD_TYPE_SESSION_CREATED, buf, msgLen, introKey, iv, introKey); + Send (buf, msgLen); + } - void SSUSession::SendSessionConfirmed (const uint8_t * y, const uint8_t * ourAddress, size_t ourAddressLen) - { - uint8_t buf[512 + 18] = {}; - uint8_t * payload = buf + sizeof (SSUHeader); - *payload = 1; // 1 fragment - payload++; // info - size_t identLen = i2p::context.GetIdentity ().GetFullLen (); // 387+ bytes - htobe16buf (payload, identLen); - payload += 2; // cursize - i2p::context.GetIdentity ().ToBuffer (payload, identLen); - payload += identLen; - uint32_t signedOnTime = i2p::util::GetSecondsSinceEpoch (); - htobe32buf (payload, signedOnTime); // signed on time - payload += 4; - auto signatureLen = i2p::context.GetIdentity ().GetSignatureLen (); - size_t paddingSize = ((payload - buf) + signatureLen)%16; - if (paddingSize > 0) paddingSize = 16 - paddingSize; - // TODO: fill padding - payload += paddingSize; // padding size + void SSUSession::SendSessionConfirmed (const uint8_t * y, const uint8_t * ourAddress, size_t ourAddressLen) + { + uint8_t buf[512 + 18] = {}; + uint8_t * payload = buf + sizeof (SSUHeader); + *payload = 1; // 1 fragment + payload++; // info + size_t identLen = i2p::context.GetIdentity ().GetFullLen (); // 387+ bytes + htobe16buf (payload, identLen); + payload += 2; // cursize + i2p::context.GetIdentity ().ToBuffer (payload, identLen); + payload += identLen; + uint32_t signedOnTime = i2p::util::GetSecondsSinceEpoch (); + htobe32buf (payload, signedOnTime); // signed on time + payload += 4; + auto signatureLen = i2p::context.GetIdentity ().GetSignatureLen (); + size_t paddingSize = ((payload - buf) + signatureLen)%16; + if (paddingSize > 0) paddingSize = 16 - paddingSize; + // TODO: fill padding + payload += paddingSize; // padding size - // signature - SignedData s; // x,y, our IP, our port, remote IP, remote port, relayTag, our signed on time - s.Insert (m_DHKeysPair->publicKey, 256); // x - s.Insert (y, 256); // y - s.Insert (ourAddress, ourAddressLen); // our address/port as seem by party - if (m_RemoteEndpoint.address ().is_v4 ()) - s.Insert (m_RemoteEndpoint.address ().to_v4 ().to_bytes ().data (), 4); // remote IP V4 - else - s.Insert (m_RemoteEndpoint.address ().to_v6 ().to_bytes ().data (), 16); // remote IP V6 - s.Insert (htobe16 (m_RemoteEndpoint.port ())); // remote port - s.Insert (htobe32 (m_RelayTag)); // relay tag - s.Insert (htobe32 (signedOnTime)); // signed on time - s.Sign (i2p::context.GetPrivateKeys (), payload); // DSA signature - payload += signatureLen; - - size_t msgLen = payload - buf; - uint8_t iv[16]; - CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator (); - rnd.GenerateBlock (iv, 16); // random iv - // encrypt message with session key - FillHeaderAndEncrypt (PAYLOAD_TYPE_SESSION_CONFIRMED, buf, msgLen, m_SessionKey, iv, m_MacKey); - Send (buf, msgLen); - } + // signature + SignedData s; // x,y, our IP, our port, remote IP, remote port, relayTag, our signed on time + s.Insert (m_DHKeysPair->publicKey, 256); // x + s.Insert (y, 256); // y + s.Insert (ourAddress, ourAddressLen); // our address/port as seem by party + if (m_RemoteEndpoint.address ().is_v4 ()) + s.Insert (m_RemoteEndpoint.address ().to_v4 ().to_bytes ().data (), 4); // remote IP V4 + else + s.Insert (m_RemoteEndpoint.address ().to_v6 ().to_bytes ().data (), 16); // remote IP V6 + s.Insert (htobe16 (m_RemoteEndpoint.port ())); // remote port + s.Insert (htobe32 (m_RelayTag)); // relay tag + s.Insert (htobe32 (signedOnTime)); // signed on time + s.Sign (i2p::context.GetPrivateKeys (), payload); // DSA signature + payload += signatureLen; + + size_t msgLen = payload - buf; + uint8_t iv[16]; + CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator (); + rnd.GenerateBlock (iv, 16); // random iv + // encrypt message with session key + FillHeaderAndEncrypt (PAYLOAD_TYPE_SESSION_CONFIRMED, buf, msgLen, m_SessionKey, iv, m_MacKey); + Send (buf, msgLen); + } - void SSUSession::ProcessRelayRequest (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& from) - { - uint32_t relayTag = bufbe32toh (buf); - auto session = m_Server.FindRelaySession (relayTag); - if (session) - { - buf += 4; // relay tag - uint8_t size = *buf; - buf++; // size - buf += size; // address - buf += 2; // port - uint8_t challengeSize = *buf; - buf++; // challenge size - buf += challengeSize; - uint8_t * introKey = buf; - buf += 32; // introkey - uint32_t nonce = bufbe32toh (buf); - SendRelayResponse (nonce, from, introKey, session->m_RemoteEndpoint); - SendRelayIntro (session.get (), from); - } - } + void SSUSession::ProcessRelayRequest (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& from) + { + uint32_t relayTag = bufbe32toh (buf); + auto session = m_Server.FindRelaySession (relayTag); + if (session) + { + buf += 4; // relay tag + uint8_t size = *buf; + buf++; // size + buf += size; // address + buf += 2; // port + uint8_t challengeSize = *buf; + buf++; // challenge size + buf += challengeSize; + uint8_t * introKey = buf; + buf += 32; // introkey + uint32_t nonce = bufbe32toh (buf); + SendRelayResponse (nonce, from, introKey, session->m_RemoteEndpoint); + SendRelayIntro (session.get (), from); + } + } - void SSUSession::SendRelayResponse (uint32_t nonce, const boost::asio::ip::udp::endpoint& from, - const uint8_t * introKey, const boost::asio::ip::udp::endpoint& to) - { - uint8_t buf[80 + 18] = {}; // 64 Alice's ipv4 and 80 Alice's ipv6 - uint8_t * payload = buf + sizeof (SSUHeader); - // Charlie's address always v4 - if (!to.address ().is_v4 ()) - { - LogPrint (eLogError, "Charlie's IP must be v4"); - return; - } - *payload = 4; - payload++; // size - htobe32buf (payload, to.address ().to_v4 ().to_ulong ()); // Charlie's IP - payload += 4; // address - htobe16buf (payload, to.port ()); // Charlie's port - payload += 2; // port - // Alice - bool isV4 = from.address ().is_v4 (); // Alice's - if (isV4) - { - *payload = 4; - payload++; // size - memcpy (payload, from.address ().to_v4 ().to_bytes ().data (), 4); // Alice's IP V4 - payload += 4; // address - } - else - { - *payload = 16; - payload++; // size - memcpy (payload, from.address ().to_v6 ().to_bytes ().data (), 16); // Alice's IP V6 - payload += 16; // address - } - htobe16buf (payload, from.port ()); // Alice's port - payload += 2; // port - htobe32buf (payload, nonce); + void SSUSession::SendRelayResponse (uint32_t nonce, const boost::asio::ip::udp::endpoint& from, + const uint8_t * introKey, const boost::asio::ip::udp::endpoint& to) + { + uint8_t buf[80 + 18] = {}; // 64 Alice's ipv4 and 80 Alice's ipv6 + uint8_t * payload = buf + sizeof (SSUHeader); + // Charlie's address always v4 + if (!to.address ().is_v4 ()) + { + LogPrint (eLogError, "Charlie's IP must be v4"); + return; + } + *payload = 4; + payload++; // size + htobe32buf (payload, to.address ().to_v4 ().to_ulong ()); // Charlie's IP + payload += 4; // address + htobe16buf (payload, to.port ()); // Charlie's port + payload += 2; // port + // Alice + bool isV4 = from.address ().is_v4 (); // Alice's + if (isV4) + { + *payload = 4; + payload++; // size + memcpy (payload, from.address ().to_v4 ().to_bytes ().data (), 4); // Alice's IP V4 + payload += 4; // address + } + else + { + *payload = 16; + payload++; // size + memcpy (payload, from.address ().to_v6 ().to_bytes ().data (), 16); // Alice's IP V6 + payload += 16; // address + } + htobe16buf (payload, from.port ()); // Alice's port + payload += 2; // port + htobe32buf (payload, nonce); - if (m_State == eSessionStateEstablished) - { - // encrypt with session key - FillHeaderAndEncrypt (PAYLOAD_TYPE_RELAY_RESPONSE, buf, isV4 ? 64 : 80); - Send (buf, isV4 ? 64 : 80); - } - else - { - // ecrypt with Alice's intro key - uint8_t iv[16]; - CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator (); - rnd.GenerateBlock (iv, 16); // random iv - FillHeaderAndEncrypt (PAYLOAD_TYPE_RELAY_RESPONSE, buf, isV4 ? 64 : 80, introKey, iv, introKey); - m_Server.Send (buf, isV4 ? 64 : 80, from); - } - LogPrint (eLogDebug, "SSU relay response sent"); - } + if (m_State == eSessionStateEstablished) + { + // encrypt with session key + FillHeaderAndEncrypt (PAYLOAD_TYPE_RELAY_RESPONSE, buf, isV4 ? 64 : 80); + Send (buf, isV4 ? 64 : 80); + } + else + { + // ecrypt with Alice's intro key + uint8_t iv[16]; + CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator (); + rnd.GenerateBlock (iv, 16); // random iv + FillHeaderAndEncrypt (PAYLOAD_TYPE_RELAY_RESPONSE, buf, isV4 ? 64 : 80, introKey, iv, introKey); + m_Server.Send (buf, isV4 ? 64 : 80, from); + } + LogPrint (eLogDebug, "SSU relay response sent"); + } - void SSUSession::SendRelayIntro (SSUSession * session, const boost::asio::ip::udp::endpoint& from) - { - if (!session) return; - // Alice's address always v4 - if (!from.address ().is_v4 ()) - { - LogPrint (eLogError, "Alice's IP must be v4"); - return; - } - uint8_t buf[48 + 18] = {}; - uint8_t * payload = buf + sizeof (SSUHeader); - *payload = 4; - payload++; // size - htobe32buf (payload, from.address ().to_v4 ().to_ulong ()); // Alice's IP - payload += 4; // address - htobe16buf (payload, from.port ()); // Alice's port - payload += 2; // port - *payload = 0; // challenge size - uint8_t iv[16]; - CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator (); - rnd.GenerateBlock (iv, 16); // random iv - FillHeaderAndEncrypt (PAYLOAD_TYPE_RELAY_INTRO, buf, 48, session->m_SessionKey, iv, session->m_MacKey); - m_Server.Send (buf, 48, session->m_RemoteEndpoint); - LogPrint (eLogDebug, "SSU relay intro sent"); - } - - void SSUSession::ProcessRelayResponse (uint8_t * buf, size_t len) - { - LogPrint (eLogDebug, "Relay response received"); - uint8_t * payload = buf + sizeof (SSUHeader); - uint8_t remoteSize = *payload; - payload++; // remote size - //boost::asio::ip::address_v4 remoteIP (bufbe32toh (payload)); - payload += remoteSize; // remote address - //uint16_t remotePort = bufbe16toh (payload); - payload += 2; // remote port - uint8_t ourSize = *payload; - payload++; // our size - boost::asio::ip::address ourIP; - if (ourSize == 4) - { - boost::asio::ip::address_v4::bytes_type bytes; - memcpy (bytes.data (), payload, 4); - ourIP = boost::asio::ip::address_v4 (bytes); - } - else - { - boost::asio::ip::address_v6::bytes_type bytes; - memcpy (bytes.data (), payload, 16); - ourIP = boost::asio::ip::address_v6 (bytes); - } - payload += ourSize; // our address - uint16_t ourPort = bufbe16toh (payload); - payload += 2; // our port - LogPrint ("Our external address is ", ourIP.to_string (), ":", ourPort); - i2p::context.UpdateAddress (ourIP); - } + void SSUSession::SendRelayIntro (SSUSession * session, const boost::asio::ip::udp::endpoint& from) + { + if (!session) return; + // Alice's address always v4 + if (!from.address ().is_v4 ()) + { + LogPrint (eLogError, "Alice's IP must be v4"); + return; + } + uint8_t buf[48 + 18] = {}; + uint8_t * payload = buf + sizeof (SSUHeader); + *payload = 4; + payload++; // size + htobe32buf (payload, from.address ().to_v4 ().to_ulong ()); // Alice's IP + payload += 4; // address + htobe16buf (payload, from.port ()); // Alice's port + payload += 2; // port + *payload = 0; // challenge size + uint8_t iv[16]; + CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator (); + rnd.GenerateBlock (iv, 16); // random iv + FillHeaderAndEncrypt (PAYLOAD_TYPE_RELAY_INTRO, buf, 48, session->m_SessionKey, iv, session->m_MacKey); + m_Server.Send (buf, 48, session->m_RemoteEndpoint); + LogPrint (eLogDebug, "SSU relay intro sent"); + } + + void SSUSession::ProcessRelayResponse (uint8_t * buf, size_t len) + { + LogPrint (eLogDebug, "Relay response received"); + uint8_t * payload = buf + sizeof (SSUHeader); + uint8_t remoteSize = *payload; + payload++; // remote size + //boost::asio::ip::address_v4 remoteIP (bufbe32toh (payload)); + payload += remoteSize; // remote address + //uint16_t remotePort = bufbe16toh (payload); + payload += 2; // remote port + uint8_t ourSize = *payload; + payload++; // our size + boost::asio::ip::address ourIP; + if (ourSize == 4) + { + boost::asio::ip::address_v4::bytes_type bytes; + memcpy (bytes.data (), payload, 4); + ourIP = boost::asio::ip::address_v4 (bytes); + } + else + { + boost::asio::ip::address_v6::bytes_type bytes; + memcpy (bytes.data (), payload, 16); + ourIP = boost::asio::ip::address_v6 (bytes); + } + payload += ourSize; // our address + uint16_t ourPort = bufbe16toh (payload); + payload += 2; // our port + LogPrint ("Our external address is ", ourIP.to_string (), ":", ourPort); + i2p::context.UpdateAddress (ourIP); + } - void SSUSession::ProcessRelayIntro (uint8_t * buf, size_t len) - { - uint8_t size = *buf; - if (size == 4) - { - buf++; // size - boost::asio::ip::address_v4 address (bufbe32toh (buf)); - buf += 4; // address - uint16_t port = bufbe16toh (buf); - // send hole punch of 1 byte - m_Server.Send (buf, 0, boost::asio::ip::udp::endpoint (address, port)); - } - else - LogPrint (eLogWarning, "Address size ", size, " is not supported"); - } + void SSUSession::ProcessRelayIntro (uint8_t * buf, size_t len) + { + uint8_t size = *buf; + if (size == 4) + { + buf++; // size + boost::asio::ip::address_v4 address (bufbe32toh (buf)); + buf += 4; // address + uint16_t port = bufbe16toh (buf); + // send hole punch of 1 byte + m_Server.Send (buf, 0, boost::asio::ip::udp::endpoint (address, port)); + } + else + LogPrint (eLogWarning, "Address size ", size, " is not supported"); + } - void SSUSession::FillHeaderAndEncrypt (uint8_t payloadType, uint8_t * buf, size_t len, - const uint8_t * aesKey, const uint8_t * iv, const uint8_t * macKey) - { - if (len < sizeof (SSUHeader)) - { - LogPrint (eLogError, "Unexpected SSU packet length ", len); - return; - } - //TODO: we are using a dirty solution here but should work for now - SSUHeader * header = (SSUHeader *)buf; - memcpy (header->iv, iv, 16); - header->flag = payloadType << 4; // MSB is 0 - htobe32buf (&(header->time), i2p::util::GetSecondsSinceEpoch ()); - uint8_t * encrypted = &header->flag; - uint16_t encryptedLen = len - (encrypted - buf); - i2p::crypto::CBCEncryption encryption(aesKey, iv); - encryption.Encrypt (encrypted, encryptedLen, encrypted); - // assume actual buffer size is 18 (16 + 2) bytes more - memcpy (buf + len, iv, 16); - htobe16buf (buf + len + 16, encryptedLen); - i2p::crypto::HMACMD5Digest (encrypted, encryptedLen + 18, macKey, header->mac); - } + void SSUSession::FillHeaderAndEncrypt (uint8_t payloadType, uint8_t * buf, size_t len, + const uint8_t * aesKey, const uint8_t * iv, const uint8_t * macKey) + { + if (len < sizeof (SSUHeader)) + { + LogPrint (eLogError, "Unexpected SSU packet length ", len); + return; + } + //TODO: we are using a dirty solution here but should work for now + SSUHeader * header = (SSUHeader *)buf; + memcpy (header->iv, iv, 16); + header->flag = payloadType << 4; // MSB is 0 + htobe32buf (&(header->time), i2p::util::GetSecondsSinceEpoch ()); + uint8_t * encrypted = &header->flag; + uint16_t encryptedLen = len - (encrypted - buf); + i2p::crypto::CBCEncryption encryption(aesKey, iv); + encryption.Encrypt (encrypted, encryptedLen, encrypted); + // assume actual buffer size is 18 (16 + 2) bytes more + memcpy (buf + len, iv, 16); + htobe16buf (buf + len + 16, encryptedLen); + i2p::crypto::HMACMD5Digest (encrypted, encryptedLen + 18, macKey, header->mac); + } - void SSUSession::FillHeaderAndEncrypt (uint8_t payloadType, uint8_t * buf, size_t len) - { - if (len < sizeof (SSUHeader)) - { - LogPrint (eLogError, "Unexpected SSU packet length ", len); - return; - } - //TODO: we are using a dirty solution here but should work for now - SSUHeader * header = (SSUHeader *)buf; - i2p::context.GetRandomNumberGenerator ().GenerateBlock (header->iv, 16); // random iv - m_SessionKeyEncryption.SetIV (header->iv); - header->flag = payloadType << 4; // MSB is 0 - htobe32buf (&(header->time), i2p::util::GetSecondsSinceEpoch ()); - uint8_t * encrypted = &header->flag; - uint16_t encryptedLen = len - (encrypted - buf); - m_SessionKeyEncryption.Encrypt (encrypted, encryptedLen, encrypted); - // assume actual buffer size is 18 (16 + 2) bytes more - memcpy (buf + len, header->iv, 16); - htobe16buf (buf + len + 16, encryptedLen); - i2p::crypto::HMACMD5Digest (encrypted, encryptedLen + 18, m_MacKey, header->mac); - } - - void SSUSession::Decrypt (uint8_t * buf, size_t len, const uint8_t * aesKey) - { - if (len < sizeof (SSUHeader)) - { - LogPrint (eLogError, "Unexpected SSU packet length ", len); - return; - } - //TODO: since we are accessing a uint8_t this is unlikely to crash due to alignment but should be improved - SSUHeader * header = (SSUHeader *)buf; - uint8_t * encrypted = &header->flag; - uint16_t encryptedLen = len - (encrypted - buf); - i2p::crypto::CBCDecryption decryption; - decryption.SetKey (aesKey); - decryption.SetIV (header->iv); - decryption.Decrypt (encrypted, encryptedLen, encrypted); - } + void SSUSession::FillHeaderAndEncrypt (uint8_t payloadType, uint8_t * buf, size_t len) + { + if (len < sizeof (SSUHeader)) + { + LogPrint (eLogError, "Unexpected SSU packet length ", len); + return; + } + //TODO: we are using a dirty solution here but should work for now + SSUHeader * header = (SSUHeader *)buf; + i2p::context.GetRandomNumberGenerator ().GenerateBlock (header->iv, 16); // random iv + m_SessionKeyEncryption.SetIV (header->iv); + header->flag = payloadType << 4; // MSB is 0 + htobe32buf (&(header->time), i2p::util::GetSecondsSinceEpoch ()); + uint8_t * encrypted = &header->flag; + uint16_t encryptedLen = len - (encrypted - buf); + m_SessionKeyEncryption.Encrypt (encrypted, encryptedLen, encrypted); + // assume actual buffer size is 18 (16 + 2) bytes more + memcpy (buf + len, header->iv, 16); + htobe16buf (buf + len + 16, encryptedLen); + i2p::crypto::HMACMD5Digest (encrypted, encryptedLen + 18, m_MacKey, header->mac); + } + + void SSUSession::Decrypt (uint8_t * buf, size_t len, const uint8_t * aesKey) + { + if (len < sizeof (SSUHeader)) + { + LogPrint (eLogError, "Unexpected SSU packet length ", len); + return; + } + //TODO: since we are accessing a uint8_t this is unlikely to crash due to alignment but should be improved + SSUHeader * header = (SSUHeader *)buf; + uint8_t * encrypted = &header->flag; + uint16_t encryptedLen = len - (encrypted - buf); + i2p::crypto::CBCDecryption decryption; + decryption.SetKey (aesKey); + decryption.SetIV (header->iv); + decryption.Decrypt (encrypted, encryptedLen, encrypted); + } - void SSUSession::DecryptSessionKey (uint8_t * buf, size_t len) - { - if (len < sizeof (SSUHeader)) - { - LogPrint (eLogError, "Unexpected SSU packet length ", len); - return; - } - //TODO: since we are accessing a uint8_t this is unlikely to crash due to alignment but should be improved - SSUHeader * header = (SSUHeader *)buf; - uint8_t * encrypted = &header->flag; - uint16_t encryptedLen = len - (encrypted - buf); - if (encryptedLen > 0) - { - m_SessionKeyDecryption.SetIV (header->iv); - m_SessionKeyDecryption.Decrypt (encrypted, encryptedLen, encrypted); - } - } - - bool SSUSession::Validate (uint8_t * buf, size_t len, const uint8_t * macKey) - { - if (len < sizeof (SSUHeader)) - { - LogPrint (eLogError, "Unexpected SSU packet length ", len); - return false; - } - //TODO: since we are accessing a uint8_t this is unlikely to crash due to alignment but should be improved - SSUHeader * header = (SSUHeader *)buf; - uint8_t * encrypted = &header->flag; - uint16_t encryptedLen = len - (encrypted - buf); - // assume actual buffer size is 18 (16 + 2) bytes more - memcpy (buf + len, header->iv, 16); - htobe16buf (buf + len + 16, encryptedLen); - uint8_t digest[16]; - i2p::crypto::HMACMD5Digest (encrypted, encryptedLen + 18, macKey, digest); - return !memcmp (header->mac, digest, 16); - } + void SSUSession::DecryptSessionKey (uint8_t * buf, size_t len) + { + if (len < sizeof (SSUHeader)) + { + LogPrint (eLogError, "Unexpected SSU packet length ", len); + return; + } + //TODO: since we are accessing a uint8_t this is unlikely to crash due to alignment but should be improved + SSUHeader * header = (SSUHeader *)buf; + uint8_t * encrypted = &header->flag; + uint16_t encryptedLen = len - (encrypted - buf); + if (encryptedLen > 0) + { + m_SessionKeyDecryption.SetIV (header->iv); + m_SessionKeyDecryption.Decrypt (encrypted, encryptedLen, encrypted); + } + } + + bool SSUSession::Validate (uint8_t * buf, size_t len, const uint8_t * macKey) + { + if (len < sizeof (SSUHeader)) + { + LogPrint (eLogError, "Unexpected SSU packet length ", len); + return false; + } + //TODO: since we are accessing a uint8_t this is unlikely to crash due to alignment but should be improved + SSUHeader * header = (SSUHeader *)buf; + uint8_t * encrypted = &header->flag; + uint16_t encryptedLen = len - (encrypted - buf); + // assume actual buffer size is 18 (16 + 2) bytes more + memcpy (buf + len, header->iv, 16); + htobe16buf (buf + len + 16, encryptedLen); + uint8_t digest[16]; + i2p::crypto::HMACMD5Digest (encrypted, encryptedLen + 18, macKey, digest); + return !memcmp (header->mac, digest, 16); + } - void SSUSession::Connect () - { - if (m_State == eSessionStateUnknown) - { - // set connect timer - ScheduleConnectTimer (); - m_DHKeysPair = transports.GetNextDHKeysPair (); - SendSessionRequest (); - } - } + void SSUSession::Connect () + { + if (m_State == eSessionStateUnknown) + { + // set connect timer + ScheduleConnectTimer (); + m_DHKeysPair = transports.GetNextDHKeysPair (); + SendSessionRequest (); + } + } - void SSUSession::WaitForConnect () - { - if (!m_RemoteRouter) // incoming session - ScheduleConnectTimer (); - else - LogPrint (eLogError, "SSU wait for connect for outgoing session"); - } + void SSUSession::WaitForConnect () + { + if (!m_RemoteRouter) // incoming session + ScheduleConnectTimer (); + else + LogPrint (eLogError, "SSU wait for connect for outgoing session"); + } - void SSUSession::ScheduleConnectTimer () - { - m_Timer.cancel (); - m_Timer.expires_from_now (boost::posix_time::seconds(SSU_CONNECT_TIMEOUT)); - m_Timer.async_wait (std::bind (&SSUSession::HandleConnectTimer, - shared_from_this (), std::placeholders::_1)); + void SSUSession::ScheduleConnectTimer () + { + m_Timer.cancel (); + m_Timer.expires_from_now (boost::posix_time::seconds(SSU_CONNECT_TIMEOUT)); + m_Timer.async_wait (std::bind (&SSUSession::HandleConnectTimer, + shared_from_this (), std::placeholders::_1)); } - void SSUSession::HandleConnectTimer (const boost::system::error_code& ecode) - { - if (!ecode) - { - // timeout expired - LogPrint ("SSU session was not established after ", SSU_CONNECT_TIMEOUT, " second"); - Failed (); - } - } - - void SSUSession::Introduce (uint32_t iTag, const uint8_t * iKey) - { - if (m_State == eSessionStateUnknown) - { - // set connect timer - m_Timer.expires_from_now (boost::posix_time::seconds(SSU_CONNECT_TIMEOUT)); - m_Timer.async_wait (std::bind (&SSUSession::HandleConnectTimer, - shared_from_this (), std::placeholders::_1)); - } - SendRelayRequest (iTag, iKey); - } + void SSUSession::HandleConnectTimer (const boost::system::error_code& ecode) + { + if (!ecode) + { + // timeout expired + LogPrint ("SSU session was not established after ", SSU_CONNECT_TIMEOUT, " second"); + Failed (); + } + } + + void SSUSession::Introduce (uint32_t iTag, const uint8_t * iKey) + { + if (m_State == eSessionStateUnknown) + { + // set connect timer + m_Timer.expires_from_now (boost::posix_time::seconds(SSU_CONNECT_TIMEOUT)); + m_Timer.async_wait (std::bind (&SSUSession::HandleConnectTimer, + shared_from_this (), std::placeholders::_1)); + } + SendRelayRequest (iTag, iKey); + } - void SSUSession::WaitForIntroduction () - { - m_State = eSessionStateIntroduced; - // set connect timer - m_Timer.expires_from_now (boost::posix_time::seconds(SSU_CONNECT_TIMEOUT)); - m_Timer.async_wait (std::bind (&SSUSession::HandleConnectTimer, - shared_from_this (), std::placeholders::_1)); - } + void SSUSession::WaitForIntroduction () + { + m_State = eSessionStateIntroduced; + // set connect timer + m_Timer.expires_from_now (boost::posix_time::seconds(SSU_CONNECT_TIMEOUT)); + m_Timer.async_wait (std::bind (&SSUSession::HandleConnectTimer, + shared_from_this (), std::placeholders::_1)); + } - void SSUSession::Close () - { - m_State = eSessionStateClosed; - SendSesionDestroyed (); - transports.PeerDisconnected (shared_from_this ()); - m_Data.Stop (); - m_Timer.cancel (); - } + void SSUSession::Close () + { + m_State = eSessionStateClosed; + SendSesionDestroyed (); + transports.PeerDisconnected (shared_from_this ()); + m_Data.Stop (); + m_Timer.cancel (); + } - void SSUSession::Done () - { - GetService ().post (std::bind (&SSUSession::Failed, shared_from_this ())); - } + void SSUSession::Done () + { + GetService ().post (std::bind (&SSUSession::Failed, shared_from_this ())); + } - void SSUSession::Established () - { - m_State = eSessionStateEstablished; - if (m_DHKeysPair) - { - delete m_DHKeysPair; - m_DHKeysPair = nullptr; - } - m_Data.Start (); - m_Data.Send (CreateDatabaseStoreMsg ()); - transports.PeerConnected (shared_from_this ()); - if (m_PeerTest && (m_RemoteRouter && m_RemoteRouter->IsPeerTesting ())) - SendPeerTest (); - ScheduleTermination (); - } + void SSUSession::Established () + { + m_State = eSessionStateEstablished; + if (m_DHKeysPair) + { + delete m_DHKeysPair; + m_DHKeysPair = nullptr; + } + m_Data.Start (); + m_Data.Send (CreateDatabaseStoreMsg ()); + transports.PeerConnected (shared_from_this ()); + if (m_PeerTest && (m_RemoteRouter && m_RemoteRouter->IsPeerTesting ())) + SendPeerTest (); + ScheduleTermination (); + } - void SSUSession::Failed () - { - if (m_State != eSessionStateFailed) - { - m_State = eSessionStateFailed; - m_Server.DeleteSession (shared_from_this ()); - } - } + void SSUSession::Failed () + { + if (m_State != eSessionStateFailed) + { + m_State = eSessionStateFailed; + m_Server.DeleteSession (shared_from_this ()); + } + } - void SSUSession::ScheduleTermination () - { - m_Timer.cancel (); - m_Timer.expires_from_now (boost::posix_time::seconds(SSU_TERMINATION_TIMEOUT)); - m_Timer.async_wait (std::bind (&SSUSession::HandleTerminationTimer, - shared_from_this (), std::placeholders::_1)); - } + void SSUSession::ScheduleTermination () + { + m_Timer.cancel (); + m_Timer.expires_from_now (boost::posix_time::seconds(SSU_TERMINATION_TIMEOUT)); + m_Timer.async_wait (std::bind (&SSUSession::HandleTerminationTimer, + shared_from_this (), std::placeholders::_1)); + } - void SSUSession::HandleTerminationTimer (const boost::system::error_code& ecode) - { - if (ecode != boost::asio::error::operation_aborted) - { - LogPrint ("SSU no activity fo ", SSU_TERMINATION_TIMEOUT, " seconds"); - Failed (); - } - } - - const uint8_t * SSUSession::GetIntroKey () const - { - if (m_RemoteRouter) - { - // we are client - auto address = m_RemoteRouter->GetSSUAddress (); - return address ? (const uint8_t *)address->key : nullptr; - } - else - { - // we are server - auto address = i2p::context.GetRouterInfo ().GetSSUAddress (); - return address ? (const uint8_t *)address->key : nullptr; - } - } + void SSUSession::HandleTerminationTimer (const boost::system::error_code& ecode) + { + if (ecode != boost::asio::error::operation_aborted) + { + LogPrint ("SSU no activity fo ", SSU_TERMINATION_TIMEOUT, " seconds"); + Failed (); + } + } + + const uint8_t * SSUSession::GetIntroKey () const + { + if (m_RemoteRouter) + { + // we are client + auto address = m_RemoteRouter->GetSSUAddress (); + return address ? (const uint8_t *)address->key : nullptr; + } + else + { + // we are server + auto address = i2p::context.GetRouterInfo ().GetSSUAddress (); + return address ? (const uint8_t *)address->key : nullptr; + } + } - void SSUSession::SendI2NPMessages (const std::vector >& msgs) - { - GetService ().post (std::bind (&SSUSession::PostI2NPMessages, shared_from_this (), msgs)); - } + void SSUSession::SendI2NPMessages (const std::vector >& msgs) + { + GetService ().post (std::bind (&SSUSession::PostI2NPMessages, shared_from_this (), msgs)); + } - void SSUSession::PostI2NPMessages (std::vector > msgs) - { - if (m_State == eSessionStateEstablished) - { - for (auto it: msgs) - if (it) m_Data.Send (it); - } - } + void SSUSession::PostI2NPMessages (std::vector > msgs) + { + if (m_State == eSessionStateEstablished) + { + for (auto it: msgs) + if (it) m_Data.Send (it); + } + } - void SSUSession::ProcessData (uint8_t * buf, size_t len) - { - m_Data.ProcessMessage (buf, len); - m_IsDataReceived = true; - } + void SSUSession::ProcessData (uint8_t * buf, size_t len) + { + m_Data.ProcessMessage (buf, len); + m_IsDataReceived = true; + } - void SSUSession::FlushData () - { - if (m_IsDataReceived) - { - m_Data.FlushReceivedMessage (); - m_IsDataReceived = false; - } - } + void SSUSession::FlushData () + { + if (m_IsDataReceived) + { + m_Data.FlushReceivedMessage (); + m_IsDataReceived = false; + } + } - void SSUSession::ProcessPeerTest (const uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint) - { - uint32_t nonce = bufbe32toh (buf); // 4 bytes - uint8_t size = buf[4]; // 1 byte - uint32_t address = (size == 4) ? buf32toh(buf + 5) : 0; // big endian, size bytes - uint16_t port = buf16toh(buf + size + 5); // big endian, 2 bytes - const uint8_t * introKey = buf + size + 7; - if (port && !address) - { - LogPrint (eLogWarning, "Address of ", size, " bytes not supported"); - return; - } - switch (m_Server.GetPeerTestParticipant (nonce)) - { - // existing test - case ePeerTestParticipantAlice1: - { - if (m_State == eSessionStateEstablished) - { - LogPrint (eLogDebug, "SSU peer test from Bob. We are Alice"); - if (i2p::context.GetStatus () == eRouterStatusTesting) // still not OK - i2p::context.SetStatus (eRouterStatusFirewalled); - } - else - { - LogPrint (eLogDebug, "SSU first peer test from Charlie. We are Alice"); - i2p::context.SetStatus (eRouterStatusOK); - m_Server.UpdatePeerTest (nonce, ePeerTestParticipantAlice2); - SendPeerTest (nonce, senderEndpoint.address ().to_v4 ().to_ulong (), - senderEndpoint.port (), introKey, true, false); // to Charlie - } - break; - } - case ePeerTestParticipantAlice2: - { - if (m_State == eSessionStateEstablished) - LogPrint (eLogDebug, "SSU peer test from Bob. We are Alice"); - else - { - // peer test successive - LogPrint (eLogDebug, "SSU second peer test from Charlie. We are Alice"); - i2p::context.SetStatus (eRouterStatusOK); - m_Server.RemovePeerTest (nonce); - } - break; - } - case ePeerTestParticipantBob: - { - LogPrint (eLogDebug, "SSU peer test from Charlie. We are Bob"); - auto session = m_Server.GetPeerTestSession (nonce); // session with Alice from PeerTest - if (session && session->m_State == eSessionStateEstablished) - session->Send (PAYLOAD_TYPE_PEER_TEST, buf, len); // back to Alice - m_Server.RemovePeerTest (nonce); // nonce has been used - break; - } - case ePeerTestParticipantCharlie: - { - LogPrint (eLogDebug, "SSU peer test from Alice. We are Charlie"); - SendPeerTest (nonce, senderEndpoint.address ().to_v4 ().to_ulong (), - senderEndpoint.port (), introKey); // to Alice with her actual address - m_Server.RemovePeerTest (nonce); // nonce has been used - break; - } - // test not found - case ePeerTestParticipantUnknown: - { - if (m_State == eSessionStateEstablished) - { - // new test - if (port) - { - LogPrint (eLogDebug, "SSU peer test from Bob. We are Charlie"); - m_Server.NewPeerTest (nonce, ePeerTestParticipantCharlie); - Send (PAYLOAD_TYPE_PEER_TEST, buf, len); // back to Bob - SendPeerTest (nonce, be32toh (address), be16toh (port), introKey); // to Alice with her address received from Bob - } - else - { - LogPrint (eLogDebug, "SSU peer test from Alice. We are Bob"); - auto session = m_Server.GetRandomEstablishedSession (shared_from_this ()); // Charlie - if (session) - { - m_Server.NewPeerTest (nonce, ePeerTestParticipantBob, shared_from_this ()); - session->SendPeerTest (nonce, senderEndpoint.address ().to_v4 ().to_ulong (), - senderEndpoint.port (), introKey, false); // to Charlie with Alice's actual address - } - } - } - else - LogPrint (eLogError, "SSU unexpected peer test"); - } - } - } - - void SSUSession::SendPeerTest (uint32_t nonce, uint32_t address, uint16_t port, - const uint8_t * introKey, bool toAddress, bool sendAddress) - // toAddress is true for Alice<->Chalie communications only - // sendAddress is false if message comes from Alice - { - uint8_t buf[80 + 18] = {}; - uint8_t * payload = buf + sizeof (SSUHeader); - htobe32buf (payload, nonce); - payload += 4; // nonce - // address and port - if (sendAddress && address) - { - *payload = 4; - payload++; // size - htobe32buf (payload, address); - payload += 4; // address - } - else - { - *payload = 0; - payload++; //size - } - htobe16buf (payload, port); - payload += 2; // port - // intro key - if (toAddress) - { - // send our intro key to address instead it's own - auto addr = i2p::context.GetRouterInfo ().GetSSUAddress (); - if (addr) - memcpy (payload, addr->key, 32); // intro key - else - LogPrint (eLogError, "SSU is not supported. Can't send peer test"); - } - else - memcpy (payload, introKey, 32); // intro key + void SSUSession::ProcessPeerTest (const uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint) + { + uint32_t nonce = bufbe32toh (buf); // 4 bytes + uint8_t size = buf[4]; // 1 byte + uint32_t address = (size == 4) ? buf32toh(buf + 5) : 0; // big endian, size bytes + uint16_t port = buf16toh(buf + size + 5); // big endian, 2 bytes + const uint8_t * introKey = buf + size + 7; + if (port && !address) + { + LogPrint (eLogWarning, "Address of ", size, " bytes not supported"); + return; + } + switch (m_Server.GetPeerTestParticipant (nonce)) + { + // existing test + case ePeerTestParticipantAlice1: + { + if (m_State == eSessionStateEstablished) + { + LogPrint (eLogDebug, "SSU peer test from Bob. We are Alice"); + if (i2p::context.GetStatus () == eRouterStatusTesting) // still not OK + i2p::context.SetStatus (eRouterStatusFirewalled); + } + else + { + LogPrint (eLogDebug, "SSU first peer test from Charlie. We are Alice"); + i2p::context.SetStatus (eRouterStatusOK); + m_Server.UpdatePeerTest (nonce, ePeerTestParticipantAlice2); + SendPeerTest (nonce, senderEndpoint.address ().to_v4 ().to_ulong (), + senderEndpoint.port (), introKey, true, false); // to Charlie + } + break; + } + case ePeerTestParticipantAlice2: + { + if (m_State == eSessionStateEstablished) + LogPrint (eLogDebug, "SSU peer test from Bob. We are Alice"); + else + { + // peer test successive + LogPrint (eLogDebug, "SSU second peer test from Charlie. We are Alice"); + i2p::context.SetStatus (eRouterStatusOK); + m_Server.RemovePeerTest (nonce); + } + break; + } + case ePeerTestParticipantBob: + { + LogPrint (eLogDebug, "SSU peer test from Charlie. We are Bob"); + auto session = m_Server.GetPeerTestSession (nonce); // session with Alice from PeerTest + if (session && session->m_State == eSessionStateEstablished) + session->Send (PAYLOAD_TYPE_PEER_TEST, buf, len); // back to Alice + m_Server.RemovePeerTest (nonce); // nonce has been used + break; + } + case ePeerTestParticipantCharlie: + { + LogPrint (eLogDebug, "SSU peer test from Alice. We are Charlie"); + SendPeerTest (nonce, senderEndpoint.address ().to_v4 ().to_ulong (), + senderEndpoint.port (), introKey); // to Alice with her actual address + m_Server.RemovePeerTest (nonce); // nonce has been used + break; + } + // test not found + case ePeerTestParticipantUnknown: + { + if (m_State == eSessionStateEstablished) + { + // new test + if (port) + { + LogPrint (eLogDebug, "SSU peer test from Bob. We are Charlie"); + m_Server.NewPeerTest (nonce, ePeerTestParticipantCharlie); + Send (PAYLOAD_TYPE_PEER_TEST, buf, len); // back to Bob + SendPeerTest (nonce, be32toh (address), be16toh (port), introKey); // to Alice with her address received from Bob + } + else + { + LogPrint (eLogDebug, "SSU peer test from Alice. We are Bob"); + auto session = m_Server.GetRandomEstablishedSession (shared_from_this ()); // Charlie + if (session) + { + m_Server.NewPeerTest (nonce, ePeerTestParticipantBob, shared_from_this ()); + session->SendPeerTest (nonce, senderEndpoint.address ().to_v4 ().to_ulong (), + senderEndpoint.port (), introKey, false); // to Charlie with Alice's actual address + } + } + } + else + LogPrint (eLogError, "SSU unexpected peer test"); + } + } + } + + void SSUSession::SendPeerTest (uint32_t nonce, uint32_t address, uint16_t port, + const uint8_t * introKey, bool toAddress, bool sendAddress) + // toAddress is true for Alice<->Chalie communications only + // sendAddress is false if message comes from Alice + { + uint8_t buf[80 + 18] = {}; + uint8_t * payload = buf + sizeof (SSUHeader); + htobe32buf (payload, nonce); + payload += 4; // nonce + // address and port + if (sendAddress && address) + { + *payload = 4; + payload++; // size + htobe32buf (payload, address); + payload += 4; // address + } + else + { + *payload = 0; + payload++; //size + } + htobe16buf (payload, port); + payload += 2; // port + // intro key + if (toAddress) + { + // send our intro key to address instead it's own + auto addr = i2p::context.GetRouterInfo ().GetSSUAddress (); + if (addr) + memcpy (payload, addr->key, 32); // intro key + else + LogPrint (eLogError, "SSU is not supported. Can't send peer test"); + } + else + memcpy (payload, introKey, 32); // intro key - // send - CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator (); - uint8_t iv[16]; - rnd.GenerateBlock (iv, 16); // random iv - if (toAddress) - { - // encrypt message with specified intro key - FillHeaderAndEncrypt (PAYLOAD_TYPE_PEER_TEST, buf, 80, introKey, iv, introKey); - boost::asio::ip::udp::endpoint e (boost::asio::ip::address_v4 (address), port); - m_Server.Send (buf, 80, e); - } - else - { - // encrypt message with session key - FillHeaderAndEncrypt (PAYLOAD_TYPE_PEER_TEST, buf, 80); - Send (buf, 80); - } - } + // send + CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator (); + uint8_t iv[16]; + rnd.GenerateBlock (iv, 16); // random iv + if (toAddress) + { + // encrypt message with specified intro key + FillHeaderAndEncrypt (PAYLOAD_TYPE_PEER_TEST, buf, 80, introKey, iv, introKey); + boost::asio::ip::udp::endpoint e (boost::asio::ip::address_v4 (address), port); + m_Server.Send (buf, 80, e); + } + else + { + // encrypt message with session key + FillHeaderAndEncrypt (PAYLOAD_TYPE_PEER_TEST, buf, 80); + Send (buf, 80); + } + } - void SSUSession::SendPeerTest () - { - // we are Alice - LogPrint (eLogDebug, "SSU sending peer test"); - auto address = i2p::context.GetRouterInfo ().GetSSUAddress (); - if (!address) - { - LogPrint (eLogError, "SSU is not supported. Can't send peer test"); - return; - } - uint32_t nonce = i2p::context.GetRandomNumberGenerator ().GenerateWord32 (); - if (!nonce) nonce = 1; - m_PeerTest = false; - m_Server.NewPeerTest (nonce, ePeerTestParticipantAlice1); - SendPeerTest (nonce, 0, 0, address->key, false, false); // address and port always zero for Alice - } + void SSUSession::SendPeerTest () + { + // we are Alice + LogPrint (eLogDebug, "SSU sending peer test"); + auto address = i2p::context.GetRouterInfo ().GetSSUAddress (); + if (!address) + { + LogPrint (eLogError, "SSU is not supported. Can't send peer test"); + return; + } + uint32_t nonce = i2p::context.GetRandomNumberGenerator ().GenerateWord32 (); + if (!nonce) nonce = 1; + m_PeerTest = false; + m_Server.NewPeerTest (nonce, ePeerTestParticipantAlice1); + SendPeerTest (nonce, 0, 0, address->key, false, false); // address and port always zero for Alice + } - void SSUSession::SendKeepAlive () - { - if (m_State == eSessionStateEstablished) - { - uint8_t buf[48 + 18] = {}; - uint8_t * payload = buf + sizeof (SSUHeader); - *payload = 0; // flags - payload++; - *payload = 0; // num fragments - // encrypt message with session key - FillHeaderAndEncrypt (PAYLOAD_TYPE_DATA, buf, 48); - Send (buf, 48); - LogPrint (eLogDebug, "SSU keep-alive sent"); - ScheduleTermination (); - } - } + void SSUSession::SendKeepAlive () + { + if (m_State == eSessionStateEstablished) + { + uint8_t buf[48 + 18] = {}; + uint8_t * payload = buf + sizeof (SSUHeader); + *payload = 0; // flags + payload++; + *payload = 0; // num fragments + // encrypt message with session key + FillHeaderAndEncrypt (PAYLOAD_TYPE_DATA, buf, 48); + Send (buf, 48); + LogPrint (eLogDebug, "SSU keep-alive sent"); + ScheduleTermination (); + } + } - void SSUSession::SendSesionDestroyed () - { - if (m_IsSessionKey) - { - uint8_t buf[48 + 18] = {}; - // encrypt message with session key - FillHeaderAndEncrypt (PAYLOAD_TYPE_SESSION_DESTROYED, buf, 48); - try - { - Send (buf, 48); - } - catch (std::exception& ex) - { - LogPrint (eLogError, "SSU send session destoriyed exception ", ex.what ()); - } - LogPrint (eLogDebug, "SSU session destroyed sent"); - } - } + void SSUSession::SendSesionDestroyed () + { + if (m_IsSessionKey) + { + uint8_t buf[48 + 18] = {}; + // encrypt message with session key + FillHeaderAndEncrypt (PAYLOAD_TYPE_SESSION_DESTROYED, buf, 48); + try + { + Send (buf, 48); + } + catch (std::exception& ex) + { + LogPrint (eLogError, "SSU send session destoriyed exception ", ex.what ()); + } + LogPrint (eLogDebug, "SSU session destroyed sent"); + } + } - void SSUSession::Send (uint8_t type, const uint8_t * payload, size_t len) - { - uint8_t buf[SSU_MTU_V4 + 18] = {}; - size_t msgSize = len + sizeof (SSUHeader); - size_t paddingSize = msgSize & 0x0F; // %16 - if (paddingSize > 0) msgSize += (16 - paddingSize); - if (msgSize > SSU_MTU_V4) - { - LogPrint (eLogWarning, "SSU payload size ", msgSize, " exceeds MTU"); - return; - } - memcpy (buf + sizeof (SSUHeader), payload, len); - // encrypt message with session key - FillHeaderAndEncrypt (type, buf, msgSize); - Send (buf, msgSize); - } + void SSUSession::Send (uint8_t type, const uint8_t * payload, size_t len) + { + uint8_t buf[SSU_MTU_V4 + 18] = {}; + size_t msgSize = len + sizeof (SSUHeader); + size_t paddingSize = msgSize & 0x0F; // %16 + if (paddingSize > 0) msgSize += (16 - paddingSize); + if (msgSize > SSU_MTU_V4) + { + LogPrint (eLogWarning, "SSU payload size ", msgSize, " exceeds MTU"); + return; + } + memcpy (buf + sizeof (SSUHeader), payload, len); + // encrypt message with session key + FillHeaderAndEncrypt (type, buf, msgSize); + Send (buf, msgSize); + } - void SSUSession::Send (const uint8_t * buf, size_t size) - { - m_NumSentBytes += size; - i2p::transport::transports.UpdateSentBytes (size); - m_Server.Send (buf, size, m_RemoteEndpoint); - } + void SSUSession::Send (const uint8_t * buf, size_t size) + { + m_NumSentBytes += size; + i2p::transport::transports.UpdateSentBytes (size); + m_Server.Send (buf, size, m_RemoteEndpoint); + } } } diff --git a/SSUSession.h b/SSUSession.h index 0cbe3e52..427b81c8 100644 --- a/SSUSession.h +++ b/SSUSession.h @@ -17,139 +17,139 @@ namespace transport #pragma pack(1) // Warning: do not change the order of these variables // (or fix the unsafe casts in SSU.h) - struct SSUHeader - { - uint8_t mac[16]; - uint8_t iv[16]; - uint8_t flag; - uint32_t time; + struct SSUHeader + { + uint8_t mac[16]; + uint8_t iv[16]; + uint8_t flag; + uint32_t time; - uint8_t GetPayloadType () const { return flag >> 4; }; - }; + uint8_t GetPayloadType () const { return flag >> 4; }; + }; #pragma pack() - const int SSU_CONNECT_TIMEOUT = 5; // 5 seconds - const int SSU_TERMINATION_TIMEOUT = 330; // 5.5 minutes + const int SSU_CONNECT_TIMEOUT = 5; // 5 seconds + const int SSU_TERMINATION_TIMEOUT = 330; // 5.5 minutes - // payload types (4 bits) - const uint8_t PAYLOAD_TYPE_SESSION_REQUEST = 0; - const uint8_t PAYLOAD_TYPE_SESSION_CREATED = 1; - const uint8_t PAYLOAD_TYPE_SESSION_CONFIRMED = 2; - const uint8_t PAYLOAD_TYPE_RELAY_REQUEST = 3; - const uint8_t PAYLOAD_TYPE_RELAY_RESPONSE = 4; - const uint8_t PAYLOAD_TYPE_RELAY_INTRO = 5; - const uint8_t PAYLOAD_TYPE_DATA = 6; - const uint8_t PAYLOAD_TYPE_PEER_TEST = 7; - const uint8_t PAYLOAD_TYPE_SESSION_DESTROYED = 8; + // payload types (4 bits) + const uint8_t PAYLOAD_TYPE_SESSION_REQUEST = 0; + const uint8_t PAYLOAD_TYPE_SESSION_CREATED = 1; + const uint8_t PAYLOAD_TYPE_SESSION_CONFIRMED = 2; + const uint8_t PAYLOAD_TYPE_RELAY_REQUEST = 3; + const uint8_t PAYLOAD_TYPE_RELAY_RESPONSE = 4; + const uint8_t PAYLOAD_TYPE_RELAY_INTRO = 5; + const uint8_t PAYLOAD_TYPE_DATA = 6; + const uint8_t PAYLOAD_TYPE_PEER_TEST = 7; + const uint8_t PAYLOAD_TYPE_SESSION_DESTROYED = 8; - enum SessionState - { - eSessionStateUnknown, - eSessionStateIntroduced, - eSessionStateEstablished, - eSessionStateClosed, - eSessionStateFailed - }; + enum SessionState + { + eSessionStateUnknown, + eSessionStateIntroduced, + eSessionStateEstablished, + eSessionStateClosed, + eSessionStateFailed + }; - enum PeerTestParticipant - { - ePeerTestParticipantUnknown = 0, - ePeerTestParticipantAlice1, - ePeerTestParticipantAlice2, - ePeerTestParticipantBob, - ePeerTestParticipantCharlie - }; - - class SSUServer; - class SSUSession: public TransportSession, public std::enable_shared_from_this - { - public: + enum PeerTestParticipant + { + ePeerTestParticipantUnknown = 0, + ePeerTestParticipantAlice1, + ePeerTestParticipantAlice2, + ePeerTestParticipantBob, + ePeerTestParticipantCharlie + }; + + class SSUServer; + class SSUSession: public TransportSession, public std::enable_shared_from_this + { + public: - SSUSession (SSUServer& server, boost::asio::ip::udp::endpoint& remoteEndpoint, - std::shared_ptr router = nullptr, bool peerTest = false); - void ProcessNextMessage (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint); - ~SSUSession (); - - void Connect (); - void WaitForConnect (); - void Introduce (uint32_t iTag, const uint8_t * iKey); - void WaitForIntroduction (); - void Close (); - void Done (); - boost::asio::ip::udp::endpoint& GetRemoteEndpoint () { return m_RemoteEndpoint; }; - bool IsV6 () const { return m_RemoteEndpoint.address ().is_v6 (); }; - void SendI2NPMessages (const std::vector >& msgs); - void SendPeerTest (); // Alice + SSUSession (SSUServer& server, boost::asio::ip::udp::endpoint& remoteEndpoint, + std::shared_ptr router = nullptr, bool peerTest = false); + void ProcessNextMessage (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint); + ~SSUSession (); + + void Connect (); + void WaitForConnect (); + void Introduce (uint32_t iTag, const uint8_t * iKey); + void WaitForIntroduction (); + void Close (); + void Done (); + boost::asio::ip::udp::endpoint& GetRemoteEndpoint () { return m_RemoteEndpoint; }; + bool IsV6 () const { return m_RemoteEndpoint.address ().is_v6 (); }; + void SendI2NPMessages (const std::vector >& msgs); + void SendPeerTest (); // Alice - SessionState GetState () const { return m_State; }; - size_t GetNumSentBytes () const { return m_NumSentBytes; }; - size_t GetNumReceivedBytes () const { return m_NumReceivedBytes; }; - - void SendKeepAlive (); - uint32_t GetRelayTag () const { return m_RelayTag; }; - uint32_t GetCreationTime () const { return m_CreationTime; }; + SessionState GetState () const { return m_State; }; + size_t GetNumSentBytes () const { return m_NumSentBytes; }; + size_t GetNumReceivedBytes () const { return m_NumReceivedBytes; }; + + void SendKeepAlive (); + uint32_t GetRelayTag () const { return m_RelayTag; }; + uint32_t GetCreationTime () const { return m_CreationTime; }; - void FlushData (); - - private: + void FlushData (); + + private: - boost::asio::io_service& GetService (); - void CreateAESandMacKey (const uint8_t * pubKey); + boost::asio::io_service& GetService (); + void CreateAESandMacKey (const uint8_t * pubKey); - void PostI2NPMessages (std::vector > msgs); - void ProcessMessage (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint); // call for established session - void ProcessSessionRequest (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint); - void SendSessionRequest (); - void SendRelayRequest (uint32_t iTag, const uint8_t * iKey); - void ProcessSessionCreated (uint8_t * buf, size_t len); - void SendSessionCreated (const uint8_t * x); - void ProcessSessionConfirmed (uint8_t * buf, size_t len); - void SendSessionConfirmed (const uint8_t * y, const uint8_t * ourAddress, size_t ourAddressLen); - void ProcessRelayRequest (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& from); - void SendRelayResponse (uint32_t nonce, const boost::asio::ip::udp::endpoint& from, - const uint8_t * introKey, const boost::asio::ip::udp::endpoint& to); - void SendRelayIntro (SSUSession * session, const boost::asio::ip::udp::endpoint& from); - void ProcessRelayResponse (uint8_t * buf, size_t len); - void ProcessRelayIntro (uint8_t * buf, size_t len); - void Established (); - void Failed (); - void ScheduleConnectTimer (); - void HandleConnectTimer (const boost::system::error_code& ecode); - void ProcessPeerTest (const uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint); - void SendPeerTest (uint32_t nonce, uint32_t address, uint16_t port, const uint8_t * introKey, bool toAddress = true, bool sendAddress = true); - void ProcessData (uint8_t * buf, size_t len); - void SendSesionDestroyed (); - void Send (uint8_t type, const uint8_t * payload, size_t len); // with session key - void Send (const uint8_t * buf, size_t size); - - void FillHeaderAndEncrypt (uint8_t payloadType, uint8_t * buf, size_t len, const uint8_t * aesKey, const uint8_t * iv, const uint8_t * macKey); - void FillHeaderAndEncrypt (uint8_t payloadType, uint8_t * buf, size_t len); // with session key - void Decrypt (uint8_t * buf, size_t len, const uint8_t * aesKey); - void DecryptSessionKey (uint8_t * buf, size_t len); - bool Validate (uint8_t * buf, size_t len, const uint8_t * macKey); - const uint8_t * GetIntroKey () const; + void PostI2NPMessages (std::vector > msgs); + void ProcessMessage (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint); // call for established session + void ProcessSessionRequest (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint); + void SendSessionRequest (); + void SendRelayRequest (uint32_t iTag, const uint8_t * iKey); + void ProcessSessionCreated (uint8_t * buf, size_t len); + void SendSessionCreated (const uint8_t * x); + void ProcessSessionConfirmed (uint8_t * buf, size_t len); + void SendSessionConfirmed (const uint8_t * y, const uint8_t * ourAddress, size_t ourAddressLen); + void ProcessRelayRequest (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& from); + void SendRelayResponse (uint32_t nonce, const boost::asio::ip::udp::endpoint& from, + const uint8_t * introKey, const boost::asio::ip::udp::endpoint& to); + void SendRelayIntro (SSUSession * session, const boost::asio::ip::udp::endpoint& from); + void ProcessRelayResponse (uint8_t * buf, size_t len); + void ProcessRelayIntro (uint8_t * buf, size_t len); + void Established (); + void Failed (); + void ScheduleConnectTimer (); + void HandleConnectTimer (const boost::system::error_code& ecode); + void ProcessPeerTest (const uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint); + void SendPeerTest (uint32_t nonce, uint32_t address, uint16_t port, const uint8_t * introKey, bool toAddress = true, bool sendAddress = true); + void ProcessData (uint8_t * buf, size_t len); + void SendSesionDestroyed (); + void Send (uint8_t type, const uint8_t * payload, size_t len); // with session key + void Send (const uint8_t * buf, size_t size); + + void FillHeaderAndEncrypt (uint8_t payloadType, uint8_t * buf, size_t len, const uint8_t * aesKey, const uint8_t * iv, const uint8_t * macKey); + void FillHeaderAndEncrypt (uint8_t payloadType, uint8_t * buf, size_t len); // with session key + void Decrypt (uint8_t * buf, size_t len, const uint8_t * aesKey); + void DecryptSessionKey (uint8_t * buf, size_t len); + bool Validate (uint8_t * buf, size_t len, const uint8_t * macKey); + const uint8_t * GetIntroKey () const; - void ScheduleTermination (); - void HandleTerminationTimer (const boost::system::error_code& ecode); + void ScheduleTermination (); + void HandleTerminationTimer (const boost::system::error_code& ecode); - private: - - friend class SSUData; // TODO: change in later - SSUServer& m_Server; - boost::asio::ip::udp::endpoint m_RemoteEndpoint; - boost::asio::deadline_timer m_Timer; - bool m_PeerTest; - SessionState m_State; - bool m_IsSessionKey; - uint32_t m_RelayTag; - i2p::crypto::CBCEncryption m_SessionKeyEncryption; - i2p::crypto::CBCDecryption m_SessionKeyDecryption; - i2p::crypto::AESKey m_SessionKey; - i2p::crypto::MACKey m_MacKey; - uint32_t m_CreationTime; // seconds since epoch - SSUData m_Data; - bool m_IsDataReceived; - }; + private: + + friend class SSUData; // TODO: change in later + SSUServer& m_Server; + boost::asio::ip::udp::endpoint m_RemoteEndpoint; + boost::asio::deadline_timer m_Timer; + bool m_PeerTest; + SessionState m_State; + bool m_IsSessionKey; + uint32_t m_RelayTag; + i2p::crypto::CBCEncryption m_SessionKeyEncryption; + i2p::crypto::CBCDecryption m_SessionKeyDecryption; + i2p::crypto::AESKey m_SessionKey; + i2p::crypto::MACKey m_MacKey; + uint32_t m_CreationTime; // seconds since epoch + SSUData m_Data; + bool m_IsDataReceived; + }; } diff --git a/Signature.cpp b/Signature.cpp index 603ba6eb..d56efddd 100644 --- a/Signature.cpp +++ b/Signature.cpp @@ -8,113 +8,113 @@ namespace i2p { namespace crypto { - class Ed25519 - { - public: + class Ed25519 + { + public: - Ed25519 () - { - q = CryptoPP::Integer::Power2 (255) - CryptoPP::Integer (19); // 2^255-19 - l = CryptoPP::Integer::Power2 (252) + CryptoPP::Integer ("27742317777372353535851937790883648493"); - // 2^252 + 27742317777372353535851937790883648493 - d = CryptoPP::Integer (-121665) * CryptoPP::Integer (121666).InverseMod (q); // -121665/121666 - I = a_exp_b_mod_c (CryptoPP::Integer::Two (), (q - CryptoPP::Integer::One ()).DividedBy (4), q); - B = DecodePoint (CryptoPP::Integer (4)*CryptoPP::Integer (5).InverseMod (q)); - } + Ed25519 () + { + q = CryptoPP::Integer::Power2 (255) - CryptoPP::Integer (19); // 2^255-19 + l = CryptoPP::Integer::Power2 (252) + CryptoPP::Integer ("27742317777372353535851937790883648493"); + // 2^252 + 27742317777372353535851937790883648493 + d = CryptoPP::Integer (-121665) * CryptoPP::Integer (121666).InverseMod (q); // -121665/121666 + I = a_exp_b_mod_c (CryptoPP::Integer::Two (), (q - CryptoPP::Integer::One ()).DividedBy (4), q); + B = DecodePoint (CryptoPP::Integer (4)*CryptoPP::Integer (5).InverseMod (q)); + } - CryptoPP::ECP::Point DecodePublicKey (const uint8_t * key) const - { - return DecodePoint (CryptoPP::Integer (key, 32)); - } + CryptoPP::ECP::Point DecodePublicKey (const uint8_t * key) const + { + return DecodePoint (CryptoPP::Integer (key, 32)); + } - CryptoPP::ECP::Point GeneratePublicKey (const uint8_t * privateKey) const - { - return Mul (B, CryptoPP::Integer (privateKey, 32)); - } + CryptoPP::ECP::Point GeneratePublicKey (const uint8_t * privateKey) const + { + return Mul (B, CryptoPP::Integer (privateKey, 32)); + } - private: + private: - CryptoPP::ECP::Point Sum (const CryptoPP::ECP::Point& p1, const CryptoPP::ECP::Point& p2) const - { - CryptoPP::Integer m = d*p1.x*p2.x*p1.y*p2.y, - x = a_times_b_mod_c (p1.x*p2.y + p2.x*p1.y, (CryptoPP::Integer::One() + m).InverseMod (q), q), - y = a_times_b_mod_c (p1.y*p2.y + p1.x*p2.x, (CryptoPP::Integer::One() - m).InverseMod (q), q); - return CryptoPP::ECP::Point {x, y}; - } + CryptoPP::ECP::Point Sum (const CryptoPP::ECP::Point& p1, const CryptoPP::ECP::Point& p2) const + { + CryptoPP::Integer m = d*p1.x*p2.x*p1.y*p2.y, + x = a_times_b_mod_c (p1.x*p2.y + p2.x*p1.y, (CryptoPP::Integer::One() + m).InverseMod (q), q), + y = a_times_b_mod_c (p1.y*p2.y + p1.x*p2.x, (CryptoPP::Integer::One() - m).InverseMod (q), q); + return CryptoPP::ECP::Point {x, y}; + } - CryptoPP::ECP::Point Mul (const CryptoPP::ECP::Point& p, const CryptoPP::Integer& e) const - { - CryptoPP::ECP::Point res {0, 1}; - if (!e.IsZero ()) - { - auto bitCount = e.BitCount (); - for (int i = bitCount - 1; i >= 0; i--) - { - res = Sum (res, res); - if (e.GetBit (i)) res = Sum (res, p); - } - } - return res; - } + CryptoPP::ECP::Point Mul (const CryptoPP::ECP::Point& p, const CryptoPP::Integer& e) const + { + CryptoPP::ECP::Point res {0, 1}; + if (!e.IsZero ()) + { + auto bitCount = e.BitCount (); + for (int i = bitCount - 1; i >= 0; i--) + { + res = Sum (res, res); + if (e.GetBit (i)) res = Sum (res, p); + } + } + return res; + } - bool IsOnCurve (const CryptoPP::ECP::Point& p) const - { - auto x2 = p.x.Squared(), y2 = p.y.Squared (); - return (y2 - x2 - CryptoPP::Integer::One() - d*x2*y2).Modulo (q).IsZero (); - } + bool IsOnCurve (const CryptoPP::ECP::Point& p) const + { + auto x2 = p.x.Squared(), y2 = p.y.Squared (); + return (y2 - x2 - CryptoPP::Integer::One() - d*x2*y2).Modulo (q).IsZero (); + } - CryptoPP::Integer RecoverX (const CryptoPP::Integer& y) const - { - auto y2 = y.Squared (); - auto xx = (y2 - CryptoPP::Integer::One())*(d*y2 + CryptoPP::Integer::One()).InverseMod (q); - auto x = a_exp_b_mod_c (xx, (q + CryptoPP::Integer (3)).DividedBy (8), q); - if (!(x.Squared () - xx).Modulo (q).IsZero ()) - x = a_times_b_mod_c (x, I, q); - if (x.IsOdd ()) x = q - x; - return x; - } + CryptoPP::Integer RecoverX (const CryptoPP::Integer& y) const + { + auto y2 = y.Squared (); + auto xx = (y2 - CryptoPP::Integer::One())*(d*y2 + CryptoPP::Integer::One()).InverseMod (q); + auto x = a_exp_b_mod_c (xx, (q + CryptoPP::Integer (3)).DividedBy (8), q); + if (!(x.Squared () - xx).Modulo (q).IsZero ()) + x = a_times_b_mod_c (x, I, q); + if (x.IsOdd ()) x = q - x; + return x; + } - CryptoPP::ECP::Point DecodePoint (const CryptoPP::Integer& y) const - { - auto x = RecoverX (y); - CryptoPP::ECP::Point p {x, y}; - if (!IsOnCurve (p)) - { - LogPrint (eLogError, "Decoded point is not on 25519"); - return CryptoPP::ECP::Point {0, 1}; - } - return p; - } + CryptoPP::ECP::Point DecodePoint (const CryptoPP::Integer& y) const + { + auto x = RecoverX (y); + CryptoPP::ECP::Point p {x, y}; + if (!IsOnCurve (p)) + { + LogPrint (eLogError, "Decoded point is not on 25519"); + return CryptoPP::ECP::Point {0, 1}; + } + return p; + } - private: + private: - CryptoPP::Integer q, l, d, I; - CryptoPP::ECP::Point B; // base point - }; + CryptoPP::Integer q, l, d, I; + CryptoPP::ECP::Point B; // base point + }; - static std::unique_ptr g_Ed25519; - std::unique_ptr& GetEd25519 () - { - if (!g_Ed25519) - g_Ed25519.reset (new Ed25519 ()); - return g_Ed25519; - } - + static std::unique_ptr g_Ed25519; + std::unique_ptr& GetEd25519 () + { + if (!g_Ed25519) + g_Ed25519.reset (new Ed25519 ()); + return g_Ed25519; + } + - EDDSA25519Verifier::EDDSA25519Verifier (const uint8_t * signingKey): - m_PublicKey (GetEd25519 ()->DecodePublicKey (signingKey)) - { - } + EDDSA25519Verifier::EDDSA25519Verifier (const uint8_t * signingKey): + m_PublicKey (GetEd25519 ()->DecodePublicKey (signingKey)) + { + } - bool EDDSA25519Verifier::Verify (const uint8_t * buf, size_t len, const uint8_t * signature) const - { - return true; // TODO: - } + bool EDDSA25519Verifier::Verify (const uint8_t * buf, size_t len, const uint8_t * signature) const + { + return true; // TODO: + } - void EDDSA25519Signer::Sign (CryptoPP::RandomNumberGenerator& rnd, const uint8_t * buf, int len, uint8_t * signature) const - { - // TODO - } + void EDDSA25519Signer::Sign (CryptoPP::RandomNumberGenerator& rnd, const uint8_t * buf, int len, uint8_t * signature) const + { + // TODO + } } } diff --git a/Signature.h b/Signature.h index acfaa62f..cf6d826e 100644 --- a/Signature.h +++ b/Signature.h @@ -14,430 +14,430 @@ namespace i2p { namespace crypto { - class Verifier - { - public: - - virtual ~Verifier () {}; - virtual bool Verify (const uint8_t * buf, size_t len, const uint8_t * signature) const = 0; - virtual size_t GetPublicKeyLen () const = 0; - virtual size_t GetSignatureLen () const = 0; - virtual size_t GetPrivateKeyLen () const { return GetSignatureLen ()/2; }; - }; + class Verifier + { + public: + + virtual ~Verifier () {}; + virtual bool Verify (const uint8_t * buf, size_t len, const uint8_t * signature) const = 0; + virtual size_t GetPublicKeyLen () const = 0; + virtual size_t GetSignatureLen () const = 0; + virtual size_t GetPrivateKeyLen () const { return GetSignatureLen ()/2; }; + }; - class Signer - { - public: + class Signer + { + public: - virtual ~Signer () {}; - virtual void Sign (CryptoPP::RandomNumberGenerator& rnd, const uint8_t * buf, int len, uint8_t * signature) const = 0; - }; + virtual ~Signer () {}; + virtual void Sign (CryptoPP::RandomNumberGenerator& rnd, const uint8_t * buf, int len, uint8_t * signature) const = 0; + }; - const size_t DSA_PUBLIC_KEY_LENGTH = 128; - const size_t DSA_SIGNATURE_LENGTH = 40; - const size_t DSA_PRIVATE_KEY_LENGTH = DSA_SIGNATURE_LENGTH/2; - class DSAVerifier: public Verifier - { - public: + const size_t DSA_PUBLIC_KEY_LENGTH = 128; + const size_t DSA_SIGNATURE_LENGTH = 40; + const size_t DSA_PRIVATE_KEY_LENGTH = DSA_SIGNATURE_LENGTH/2; + class DSAVerifier: public Verifier + { + public: - DSAVerifier (const uint8_t * signingKey) - { - m_PublicKey.Initialize (dsap, dsaq, dsag, CryptoPP::Integer (signingKey, DSA_PUBLIC_KEY_LENGTH)); - } - - bool Verify (const uint8_t * buf, size_t len, const uint8_t * signature) const - { - CryptoPP::DSA::Verifier verifier (m_PublicKey); - return verifier.VerifyMessage (buf, len, signature, DSA_SIGNATURE_LENGTH); - } + DSAVerifier (const uint8_t * signingKey) + { + m_PublicKey.Initialize (dsap, dsaq, dsag, CryptoPP::Integer (signingKey, DSA_PUBLIC_KEY_LENGTH)); + } + + bool Verify (const uint8_t * buf, size_t len, const uint8_t * signature) const + { + CryptoPP::DSA::Verifier verifier (m_PublicKey); + return verifier.VerifyMessage (buf, len, signature, DSA_SIGNATURE_LENGTH); + } - size_t GetPublicKeyLen () const { return DSA_PUBLIC_KEY_LENGTH; }; - size_t GetSignatureLen () const { return DSA_SIGNATURE_LENGTH; }; - - private: + size_t GetPublicKeyLen () const { return DSA_PUBLIC_KEY_LENGTH; }; + size_t GetSignatureLen () const { return DSA_SIGNATURE_LENGTH; }; + + private: - CryptoPP::DSA::PublicKey m_PublicKey; - }; + CryptoPP::DSA::PublicKey m_PublicKey; + }; - class DSASigner: public Signer - { - public: + class DSASigner: public Signer + { + public: - DSASigner (const uint8_t * signingPrivateKey) - { - m_PrivateKey.Initialize (dsap, dsaq, dsag, CryptoPP::Integer (signingPrivateKey, DSA_PRIVATE_KEY_LENGTH)); - } + DSASigner (const uint8_t * signingPrivateKey) + { + m_PrivateKey.Initialize (dsap, dsaq, dsag, CryptoPP::Integer (signingPrivateKey, DSA_PRIVATE_KEY_LENGTH)); + } - void Sign (CryptoPP::RandomNumberGenerator& rnd, const uint8_t * buf, int len, uint8_t * signature) const - { - CryptoPP::DSA::Signer signer (m_PrivateKey); - signer.SignMessage (rnd, buf, len, signature); - } + void Sign (CryptoPP::RandomNumberGenerator& rnd, const uint8_t * buf, int len, uint8_t * signature) const + { + CryptoPP::DSA::Signer signer (m_PrivateKey); + signer.SignMessage (rnd, buf, len, signature); + } - private: + private: - CryptoPP::DSA::PrivateKey m_PrivateKey; - }; + CryptoPP::DSA::PrivateKey m_PrivateKey; + }; - inline void CreateDSARandomKeys (CryptoPP::RandomNumberGenerator& rnd, uint8_t * signingPrivateKey, uint8_t * signingPublicKey) - { - CryptoPP::DSA::PrivateKey privateKey; - CryptoPP::DSA::PublicKey publicKey; - privateKey.Initialize (rnd, dsap, dsaq, dsag); - privateKey.MakePublicKey (publicKey); - privateKey.GetPrivateExponent ().Encode (signingPrivateKey, DSA_PRIVATE_KEY_LENGTH); - publicKey.GetPublicElement ().Encode (signingPublicKey, DSA_PUBLIC_KEY_LENGTH); - } + inline void CreateDSARandomKeys (CryptoPP::RandomNumberGenerator& rnd, uint8_t * signingPrivateKey, uint8_t * signingPublicKey) + { + CryptoPP::DSA::PrivateKey privateKey; + CryptoPP::DSA::PublicKey publicKey; + privateKey.Initialize (rnd, dsap, dsaq, dsag); + privateKey.MakePublicKey (publicKey); + privateKey.GetPrivateExponent ().Encode (signingPrivateKey, DSA_PRIVATE_KEY_LENGTH); + publicKey.GetPublicElement ().Encode (signingPublicKey, DSA_PUBLIC_KEY_LENGTH); + } - template - class ECDSAVerifier: public Verifier - { - public: + template + class ECDSAVerifier: public Verifier + { + public: - template - ECDSAVerifier (Curve curve, const uint8_t * signingKey) - { - m_PublicKey.Initialize (curve, - CryptoPP::ECP::Point (CryptoPP::Integer (signingKey, keyLen/2), - CryptoPP::Integer (signingKey + keyLen/2, keyLen/2))); - } + template + ECDSAVerifier (Curve curve, const uint8_t * signingKey) + { + m_PublicKey.Initialize (curve, + CryptoPP::ECP::Point (CryptoPP::Integer (signingKey, keyLen/2), + CryptoPP::Integer (signingKey + keyLen/2, keyLen/2))); + } - bool Verify (const uint8_t * buf, size_t len, const uint8_t * signature) const - { - typename CryptoPP::ECDSA::Verifier verifier (m_PublicKey); - return verifier.VerifyMessage (buf, len, signature, keyLen); // signature length - } + bool Verify (const uint8_t * buf, size_t len, const uint8_t * signature) const + { + typename CryptoPP::ECDSA::Verifier verifier (m_PublicKey); + return verifier.VerifyMessage (buf, len, signature, keyLen); // signature length + } - size_t GetPublicKeyLen () const { return keyLen; }; - size_t GetSignatureLen () const { return keyLen; }; // signature length = key length - - private: + size_t GetPublicKeyLen () const { return keyLen; }; + size_t GetSignatureLen () const { return keyLen; }; // signature length = key length + + private: - typename CryptoPP::ECDSA::PublicKey m_PublicKey; - }; + typename CryptoPP::ECDSA::PublicKey m_PublicKey; + }; - template - class ECDSASigner: public Signer - { - public: + template + class ECDSASigner: public Signer + { + public: - template - ECDSASigner (Curve curve, const uint8_t * signingPrivateKey, size_t keyLen) - { - m_PrivateKey.Initialize (curve, CryptoPP::Integer (signingPrivateKey, keyLen/2)); // private key length - } + template + ECDSASigner (Curve curve, const uint8_t * signingPrivateKey, size_t keyLen) + { + m_PrivateKey.Initialize (curve, CryptoPP::Integer (signingPrivateKey, keyLen/2)); // private key length + } - void Sign (CryptoPP::RandomNumberGenerator& rnd, const uint8_t * buf, int len, uint8_t * signature) const - { - typename CryptoPP::ECDSA::Signer signer (m_PrivateKey); - signer.SignMessage (rnd, buf, len, signature); - } + void Sign (CryptoPP::RandomNumberGenerator& rnd, const uint8_t * buf, int len, uint8_t * signature) const + { + typename CryptoPP::ECDSA::Signer signer (m_PrivateKey); + signer.SignMessage (rnd, buf, len, signature); + } - private: + private: - typename CryptoPP::ECDSA::PrivateKey m_PrivateKey; - }; + typename CryptoPP::ECDSA::PrivateKey m_PrivateKey; + }; - template - inline void CreateECDSARandomKeys (CryptoPP::RandomNumberGenerator& rnd, Curve curve, - size_t keyLen, uint8_t * signingPrivateKey, uint8_t * signingPublicKey) - { - typename CryptoPP::ECDSA::PrivateKey privateKey; - typename CryptoPP::ECDSA::PublicKey publicKey; - privateKey.Initialize (rnd, curve); - privateKey.MakePublicKey (publicKey); - privateKey.GetPrivateExponent ().Encode (signingPrivateKey, keyLen/2); - auto q = publicKey.GetPublicElement (); - q.x.Encode (signingPublicKey, keyLen/2); - q.y.Encode (signingPublicKey + keyLen/2, keyLen/2); - } + template + inline void CreateECDSARandomKeys (CryptoPP::RandomNumberGenerator& rnd, Curve curve, + size_t keyLen, uint8_t * signingPrivateKey, uint8_t * signingPublicKey) + { + typename CryptoPP::ECDSA::PrivateKey privateKey; + typename CryptoPP::ECDSA::PublicKey publicKey; + privateKey.Initialize (rnd, curve); + privateKey.MakePublicKey (publicKey); + privateKey.GetPrivateExponent ().Encode (signingPrivateKey, keyLen/2); + auto q = publicKey.GetPublicElement (); + q.x.Encode (signingPublicKey, keyLen/2); + q.y.Encode (signingPublicKey + keyLen/2, keyLen/2); + } // ECDSA_SHA256_P256 - const size_t ECDSAP256_KEY_LENGTH = 64; - class ECDSAP256Verifier: public ECDSAVerifier - { - public: + const size_t ECDSAP256_KEY_LENGTH = 64; + class ECDSAP256Verifier: public ECDSAVerifier + { + public: - ECDSAP256Verifier (const uint8_t * signingKey): - ECDSAVerifier (CryptoPP::ASN1::secp256r1(), signingKey) - { - } - }; + ECDSAP256Verifier (const uint8_t * signingKey): + ECDSAVerifier (CryptoPP::ASN1::secp256r1(), signingKey) + { + } + }; - class ECDSAP256Signer: public ECDSASigner - { - public: + class ECDSAP256Signer: public ECDSASigner + { + public: - ECDSAP256Signer (const uint8_t * signingPrivateKey): - ECDSASigner (CryptoPP::ASN1::secp256r1(), signingPrivateKey, ECDSAP256_KEY_LENGTH) - { - } - }; + ECDSAP256Signer (const uint8_t * signingPrivateKey): + ECDSASigner (CryptoPP::ASN1::secp256r1(), signingPrivateKey, ECDSAP256_KEY_LENGTH) + { + } + }; - inline void CreateECDSAP256RandomKeys (CryptoPP::RandomNumberGenerator& rnd, uint8_t * signingPrivateKey, uint8_t * signingPublicKey) - { - CreateECDSARandomKeys (rnd, CryptoPP::ASN1::secp256r1(), ECDSAP256_KEY_LENGTH, signingPrivateKey, signingPublicKey); - } + inline void CreateECDSAP256RandomKeys (CryptoPP::RandomNumberGenerator& rnd, uint8_t * signingPrivateKey, uint8_t * signingPublicKey) + { + CreateECDSARandomKeys (rnd, CryptoPP::ASN1::secp256r1(), ECDSAP256_KEY_LENGTH, signingPrivateKey, signingPublicKey); + } // ECDSA_SHA384_P384 - const size_t ECDSAP384_KEY_LENGTH = 96; - class ECDSAP384Verifier: public ECDSAVerifier - { - public: + const size_t ECDSAP384_KEY_LENGTH = 96; + class ECDSAP384Verifier: public ECDSAVerifier + { + public: - ECDSAP384Verifier (const uint8_t * signingKey): - ECDSAVerifier (CryptoPP::ASN1::secp384r1(), signingKey) - { - } - }; + ECDSAP384Verifier (const uint8_t * signingKey): + ECDSAVerifier (CryptoPP::ASN1::secp384r1(), signingKey) + { + } + }; - class ECDSAP384Signer: public ECDSASigner - { - public: + class ECDSAP384Signer: public ECDSASigner + { + public: - ECDSAP384Signer (const uint8_t * signingPrivateKey): - ECDSASigner (CryptoPP::ASN1::secp384r1(), signingPrivateKey, ECDSAP384_KEY_LENGTH) - { - } - }; + ECDSAP384Signer (const uint8_t * signingPrivateKey): + ECDSASigner (CryptoPP::ASN1::secp384r1(), signingPrivateKey, ECDSAP384_KEY_LENGTH) + { + } + }; - inline void CreateECDSAP384RandomKeys (CryptoPP::RandomNumberGenerator& rnd, uint8_t * signingPrivateKey, uint8_t * signingPublicKey) - { - CreateECDSARandomKeys (rnd, CryptoPP::ASN1::secp384r1(), ECDSAP384_KEY_LENGTH, signingPrivateKey, signingPublicKey); - } + inline void CreateECDSAP384RandomKeys (CryptoPP::RandomNumberGenerator& rnd, uint8_t * signingPrivateKey, uint8_t * signingPublicKey) + { + CreateECDSARandomKeys (rnd, CryptoPP::ASN1::secp384r1(), ECDSAP384_KEY_LENGTH, signingPrivateKey, signingPublicKey); + } // ECDSA_SHA512_P521 - const size_t ECDSAP521_KEY_LENGTH = 132; - class ECDSAP521Verifier: public ECDSAVerifier - { - public: + const size_t ECDSAP521_KEY_LENGTH = 132; + class ECDSAP521Verifier: public ECDSAVerifier + { + public: - ECDSAP521Verifier (const uint8_t * signingKey): - ECDSAVerifier (CryptoPP::ASN1::secp521r1(), signingKey) - { - } - }; + ECDSAP521Verifier (const uint8_t * signingKey): + ECDSAVerifier (CryptoPP::ASN1::secp521r1(), signingKey) + { + } + }; - class ECDSAP521Signer: public ECDSASigner - { - public: + class ECDSAP521Signer: public ECDSASigner + { + public: - ECDSAP521Signer (const uint8_t * signingPrivateKey): - ECDSASigner (CryptoPP::ASN1::secp521r1(), signingPrivateKey, ECDSAP521_KEY_LENGTH) - { - } - }; + ECDSAP521Signer (const uint8_t * signingPrivateKey): + ECDSASigner (CryptoPP::ASN1::secp521r1(), signingPrivateKey, ECDSAP521_KEY_LENGTH) + { + } + }; - inline void CreateECDSAP521RandomKeys (CryptoPP::RandomNumberGenerator& rnd, uint8_t * signingPrivateKey, uint8_t * signingPublicKey) - { - CreateECDSARandomKeys (rnd, CryptoPP::ASN1::secp521r1(), ECDSAP521_KEY_LENGTH, signingPrivateKey, signingPublicKey); - } + inline void CreateECDSAP521RandomKeys (CryptoPP::RandomNumberGenerator& rnd, uint8_t * signingPrivateKey, uint8_t * signingPublicKey) + { + CreateECDSARandomKeys (rnd, CryptoPP::ASN1::secp521r1(), ECDSAP521_KEY_LENGTH, signingPrivateKey, signingPublicKey); + } // RSA - template - class RSAVerifier: public Verifier - { - public: + template + class RSAVerifier: public Verifier + { + public: - RSAVerifier (const uint8_t * signingKey) - { - m_PublicKey.Initialize (CryptoPP::Integer (signingKey, keyLen), CryptoPP::Integer (rsae)); - } + RSAVerifier (const uint8_t * signingKey) + { + m_PublicKey.Initialize (CryptoPP::Integer (signingKey, keyLen), CryptoPP::Integer (rsae)); + } - bool Verify (const uint8_t * buf, size_t len, const uint8_t * signature) const - { - typename CryptoPP::RSASS::Verifier verifier (m_PublicKey); - return verifier.VerifyMessage (buf, len, signature, keyLen); // signature length - } - size_t GetPublicKeyLen () const { return keyLen; } - size_t GetSignatureLen () const { return keyLen; } - size_t GetPrivateKeyLen () const { return GetSignatureLen ()*2; }; + bool Verify (const uint8_t * buf, size_t len, const uint8_t * signature) const + { + typename CryptoPP::RSASS::Verifier verifier (m_PublicKey); + return verifier.VerifyMessage (buf, len, signature, keyLen); // signature length + } + size_t GetPublicKeyLen () const { return keyLen; } + size_t GetSignatureLen () const { return keyLen; } + size_t GetPrivateKeyLen () const { return GetSignatureLen ()*2; }; - private: - - CryptoPP::RSA::PublicKey m_PublicKey; - }; + private: + + CryptoPP::RSA::PublicKey m_PublicKey; + }; - - template - class RSASigner: public Signer - { - public: + + template + class RSASigner: public Signer + { + public: - RSASigner (const uint8_t * signingPrivateKey, size_t keyLen) - { - m_PrivateKey.Initialize (CryptoPP::Integer (signingPrivateKey, keyLen/2), - rsae, - CryptoPP::Integer (signingPrivateKey + keyLen/2, keyLen/2)); - } + RSASigner (const uint8_t * signingPrivateKey, size_t keyLen) + { + m_PrivateKey.Initialize (CryptoPP::Integer (signingPrivateKey, keyLen/2), + rsae, + CryptoPP::Integer (signingPrivateKey + keyLen/2, keyLen/2)); + } - void Sign (CryptoPP::RandomNumberGenerator& rnd, const uint8_t * buf, int len, uint8_t * signature) const - { - typename CryptoPP::RSASS::Signer signer (m_PrivateKey); - signer.SignMessage (rnd, buf, len, signature); - } - - private: + void Sign (CryptoPP::RandomNumberGenerator& rnd, const uint8_t * buf, int len, uint8_t * signature) const + { + typename CryptoPP::RSASS::Signer signer (m_PrivateKey); + signer.SignMessage (rnd, buf, len, signature); + } + + private: - CryptoPP::RSA::PrivateKey m_PrivateKey; - }; + CryptoPP::RSA::PrivateKey m_PrivateKey; + }; - inline void CreateRSARandomKeys (CryptoPP::RandomNumberGenerator& rnd, - size_t publicKeyLen, uint8_t * signingPrivateKey, uint8_t * signingPublicKey) - { - CryptoPP::RSA::PrivateKey privateKey; - privateKey.Initialize (rnd, publicKeyLen*8, rsae); - privateKey.GetModulus ().Encode (signingPrivateKey, publicKeyLen); - privateKey.GetPrivateExponent ().Encode (signingPrivateKey + publicKeyLen, publicKeyLen); - privateKey.GetModulus ().Encode (signingPublicKey, publicKeyLen); - } + inline void CreateRSARandomKeys (CryptoPP::RandomNumberGenerator& rnd, + size_t publicKeyLen, uint8_t * signingPrivateKey, uint8_t * signingPublicKey) + { + CryptoPP::RSA::PrivateKey privateKey; + privateKey.Initialize (rnd, publicKeyLen*8, rsae); + privateKey.GetModulus ().Encode (signingPrivateKey, publicKeyLen); + privateKey.GetPrivateExponent ().Encode (signingPrivateKey + publicKeyLen, publicKeyLen); + privateKey.GetModulus ().Encode (signingPublicKey, publicKeyLen); + } - + // RSA_SHA256_2048 - const size_t RSASHA2562048_KEY_LENGTH = 256; - class RSASHA2562048Verifier: public RSAVerifier - { - public: + const size_t RSASHA2562048_KEY_LENGTH = 256; + class RSASHA2562048Verifier: public RSAVerifier + { + public: - RSASHA2562048Verifier (const uint8_t * signingKey): RSAVerifier (signingKey) - { - } - }; + RSASHA2562048Verifier (const uint8_t * signingKey): RSAVerifier (signingKey) + { + } + }; - class RSASHA2562048Signer: public RSASigner - { - public: + class RSASHA2562048Signer: public RSASigner + { + public: - RSASHA2562048Signer (const uint8_t * signingPrivateKey): - RSASigner (signingPrivateKey, RSASHA2562048_KEY_LENGTH*2) - { - } - }; + RSASHA2562048Signer (const uint8_t * signingPrivateKey): + RSASigner (signingPrivateKey, RSASHA2562048_KEY_LENGTH*2) + { + } + }; // RSA_SHA384_3072 - const size_t RSASHA3843072_KEY_LENGTH = 384; - class RSASHA3843072Verifier: public RSAVerifier - { - public: + const size_t RSASHA3843072_KEY_LENGTH = 384; + class RSASHA3843072Verifier: public RSAVerifier + { + public: - RSASHA3843072Verifier (const uint8_t * signingKey): RSAVerifier (signingKey) - { - } - }; + RSASHA3843072Verifier (const uint8_t * signingKey): RSAVerifier (signingKey) + { + } + }; - class RSASHA3843072Signer: public RSASigner - { - public: + class RSASHA3843072Signer: public RSASigner + { + public: - RSASHA3843072Signer (const uint8_t * signingPrivateKey): - RSASigner (signingPrivateKey, RSASHA3843072_KEY_LENGTH*2) - { - } - }; + RSASHA3843072Signer (const uint8_t * signingPrivateKey): + RSASigner (signingPrivateKey, RSASHA3843072_KEY_LENGTH*2) + { + } + }; // RSA_SHA512_4096 - const size_t RSASHA5124096_KEY_LENGTH = 512; - class RSASHA5124096Verifier: public RSAVerifier - { - public: + const size_t RSASHA5124096_KEY_LENGTH = 512; + class RSASHA5124096Verifier: public RSAVerifier + { + public: - RSASHA5124096Verifier (const uint8_t * signingKey): RSAVerifier (signingKey) - { - } - }; + RSASHA5124096Verifier (const uint8_t * signingKey): RSAVerifier (signingKey) + { + } + }; - class RSASHA5124096Signer: public RSASigner - { - public: + class RSASHA5124096Signer: public RSASigner + { + public: - RSASHA5124096Signer (const uint8_t * signingPrivateKey): - RSASigner (signingPrivateKey, RSASHA5124096_KEY_LENGTH*2) - { - } - }; + RSASHA5124096Signer (const uint8_t * signingPrivateKey): + RSASigner (signingPrivateKey, RSASHA5124096_KEY_LENGTH*2) + { + } + }; -// Raw verifiers - class RawVerifier - { - public: - - virtual ~RawVerifier () {}; - virtual void Update (const uint8_t * buf, size_t len) = 0; - virtual bool Verify (const uint8_t * signature) = 0; - }; +// Raw verifiers + class RawVerifier + { + public: + + virtual ~RawVerifier () {}; + virtual void Update (const uint8_t * buf, size_t len) = 0; + virtual bool Verify (const uint8_t * signature) = 0; + }; - template - class RSARawVerifier: public RawVerifier - { - public: + template + class RSARawVerifier: public RawVerifier + { + public: - RSARawVerifier (const uint8_t * signingKey): - n (signingKey, keyLen) - { - } + RSARawVerifier (const uint8_t * signingKey): + n (signingKey, keyLen) + { + } - void Update (const uint8_t * buf, size_t len) - { - m_Hash.Update (buf, len); - } - - bool Verify (const uint8_t * signature) - { - // RSA encryption first - CryptoPP::Integer enSig (a_exp_b_mod_c (CryptoPP::Integer (signature, keyLen), - CryptoPP::Integer (i2p::crypto::rsae), n)); // s^e mod n - uint8_t enSigBuf[keyLen]; - enSig.Encode (enSigBuf, keyLen); + void Update (const uint8_t * buf, size_t len) + { + m_Hash.Update (buf, len); + } + + bool Verify (const uint8_t * signature) + { + // RSA encryption first + CryptoPP::Integer enSig (a_exp_b_mod_c (CryptoPP::Integer (signature, keyLen), + CryptoPP::Integer (i2p::crypto::rsae), n)); // s^e mod n + uint8_t enSigBuf[keyLen]; + enSig.Encode (enSigBuf, keyLen); - uint8_t digest[Hash::DIGESTSIZE]; - m_Hash.Final (digest); - if ((int)keyLen < Hash::DIGESTSIZE) return false; // can't verify digest longer than key - // we assume digest is right aligned, at least for PKCS#1 v1.5 padding - return !memcmp (enSigBuf + (keyLen - Hash::DIGESTSIZE), digest, Hash::DIGESTSIZE); - } + uint8_t digest[Hash::DIGESTSIZE]; + m_Hash.Final (digest); + if ((int)keyLen < Hash::DIGESTSIZE) return false; // can't verify digest longer than key + // we assume digest is right aligned, at least for PKCS#1 v1.5 padding + return !memcmp (enSigBuf + (keyLen - Hash::DIGESTSIZE), digest, Hash::DIGESTSIZE); + } - private: + private: - CryptoPP::Integer n; // RSA modulus - Hash m_Hash; - }; + CryptoPP::Integer n; // RSA modulus + Hash m_Hash; + }; - class RSASHA5124096RawVerifier: public RSARawVerifier - { - public: + class RSASHA5124096RawVerifier: public RSARawVerifier + { + public: - RSASHA5124096RawVerifier (const uint8_t * signingKey): RSARawVerifier (signingKey) - { - } - }; + RSASHA5124096RawVerifier (const uint8_t * signingKey): RSARawVerifier (signingKey) + { + } + }; - // EdDSA - const size_t EDDSA25519_PUBLIC_KEY_LENGTH = 32; - const size_t EDDSA25519_SIGNATURE_LENGTH = 64; - const size_t EDDSA25519_PRIVATE_KEY_LENGTH = 32; - class EDDSA25519Verifier: public Verifier - { - public: + // EdDSA + const size_t EDDSA25519_PUBLIC_KEY_LENGTH = 32; + const size_t EDDSA25519_SIGNATURE_LENGTH = 64; + const size_t EDDSA25519_PRIVATE_KEY_LENGTH = 32; + class EDDSA25519Verifier: public Verifier + { + public: - EDDSA25519Verifier (const uint8_t * signingKey); - bool Verify (const uint8_t * buf, size_t len, const uint8_t * signature) const; + EDDSA25519Verifier (const uint8_t * signingKey); + bool Verify (const uint8_t * buf, size_t len, const uint8_t * signature) const; - size_t GetPublicKeyLen () const { return EDDSA25519_PUBLIC_KEY_LENGTH; }; - size_t GetSignatureLen () const { return EDDSA25519_SIGNATURE_LENGTH; }; + size_t GetPublicKeyLen () const { return EDDSA25519_PUBLIC_KEY_LENGTH; }; + size_t GetSignatureLen () const { return EDDSA25519_SIGNATURE_LENGTH; }; - private: - - CryptoPP::ECP::Point m_PublicKey; - }; + private: + + CryptoPP::ECP::Point m_PublicKey; + }; - class EDDSA25519Signer: public Signer - { - public: + class EDDSA25519Signer: public Signer + { + public: - EDDSA25519Signer (const uint8_t * signingPrivateKey) {}; + EDDSA25519Signer (const uint8_t * signingPrivateKey) {}; - void Sign (CryptoPP::RandomNumberGenerator& rnd, const uint8_t * buf, int len, uint8_t * signature) const; - }; + void Sign (CryptoPP::RandomNumberGenerator& rnd, const uint8_t * buf, int len, uint8_t * signature) const; + }; } } diff --git a/Streaming.cpp b/Streaming.cpp index 4543569c..7e2d3bef 100644 --- a/Streaming.cpp +++ b/Streaming.cpp @@ -11,884 +11,884 @@ namespace i2p { namespace stream { - Stream::Stream (boost::asio::io_service& service, StreamingDestination& local, - std::shared_ptr remote, int port): m_Service (service), - m_SendStreamID (0), m_SequenceNumber (0), m_LastReceivedSequenceNumber (-1), - m_Status (eStreamStatusNew), m_IsAckSendScheduled (false), m_LocalDestination (local), - m_RemoteLeaseSet (remote), m_ReceiveTimer (m_Service), m_ResendTimer (m_Service), - m_AckSendTimer (m_Service), m_NumSentBytes (0), m_NumReceivedBytes (0), m_Port (port), - m_WindowSize (MIN_WINDOW_SIZE), m_RTT (INITIAL_RTT), m_RTO (INITIAL_RTO), - m_LastWindowSizeIncreaseTime (0), m_NumResendAttempts (0) - { - m_RecvStreamID = i2p::context.GetRandomNumberGenerator ().GenerateWord32 (); - m_RemoteIdentity = remote->GetIdentity (); - m_CurrentRemoteLease.endDate = 0; - } + Stream::Stream (boost::asio::io_service& service, StreamingDestination& local, + std::shared_ptr remote, int port): m_Service (service), + m_SendStreamID (0), m_SequenceNumber (0), m_LastReceivedSequenceNumber (-1), + m_Status (eStreamStatusNew), m_IsAckSendScheduled (false), m_LocalDestination (local), + m_RemoteLeaseSet (remote), m_ReceiveTimer (m_Service), m_ResendTimer (m_Service), + m_AckSendTimer (m_Service), m_NumSentBytes (0), m_NumReceivedBytes (0), m_Port (port), + m_WindowSize (MIN_WINDOW_SIZE), m_RTT (INITIAL_RTT), m_RTO (INITIAL_RTO), + m_LastWindowSizeIncreaseTime (0), m_NumResendAttempts (0) + { + m_RecvStreamID = i2p::context.GetRandomNumberGenerator ().GenerateWord32 (); + m_RemoteIdentity = remote->GetIdentity (); + m_CurrentRemoteLease.endDate = 0; + } - Stream::Stream (boost::asio::io_service& service, StreamingDestination& local): - m_Service (service), m_SendStreamID (0), m_SequenceNumber (0), m_LastReceivedSequenceNumber (-1), - m_Status (eStreamStatusNew), m_IsAckSendScheduled (false), m_LocalDestination (local), - m_ReceiveTimer (m_Service), m_ResendTimer (m_Service), m_AckSendTimer (m_Service), - m_NumSentBytes (0), m_NumReceivedBytes (0), m_Port (0), m_WindowSize (MIN_WINDOW_SIZE), - m_RTT (INITIAL_RTT), m_RTO (INITIAL_RTO), m_LastWindowSizeIncreaseTime (0), m_NumResendAttempts (0) - { - m_RecvStreamID = i2p::context.GetRandomNumberGenerator ().GenerateWord32 (); - } + Stream::Stream (boost::asio::io_service& service, StreamingDestination& local): + m_Service (service), m_SendStreamID (0), m_SequenceNumber (0), m_LastReceivedSequenceNumber (-1), + m_Status (eStreamStatusNew), m_IsAckSendScheduled (false), m_LocalDestination (local), + m_ReceiveTimer (m_Service), m_ResendTimer (m_Service), m_AckSendTimer (m_Service), + m_NumSentBytes (0), m_NumReceivedBytes (0), m_Port (0), m_WindowSize (MIN_WINDOW_SIZE), + m_RTT (INITIAL_RTT), m_RTO (INITIAL_RTO), m_LastWindowSizeIncreaseTime (0), m_NumResendAttempts (0) + { + m_RecvStreamID = i2p::context.GetRandomNumberGenerator ().GenerateWord32 (); + } - Stream::~Stream () - { - Terminate (); - while (!m_ReceiveQueue.empty ()) - { - auto packet = m_ReceiveQueue.front (); - m_ReceiveQueue.pop (); - delete packet; - } - - for (auto it: m_SentPackets) - delete it; - m_SentPackets.clear (); - - for (auto it: m_SavedPackets) - delete it; - m_SavedPackets.clear (); - - LogPrint (eLogDebug, "Stream deleted"); - } + Stream::~Stream () + { + Terminate (); + while (!m_ReceiveQueue.empty ()) + { + auto packet = m_ReceiveQueue.front (); + m_ReceiveQueue.pop (); + delete packet; + } + + for (auto it: m_SentPackets) + delete it; + m_SentPackets.clear (); + + for (auto it: m_SavedPackets) + delete it; + m_SavedPackets.clear (); + + LogPrint (eLogDebug, "Stream deleted"); + } - void Stream::Terminate () - { - m_AckSendTimer.cancel (); - m_ReceiveTimer.cancel (); - m_ResendTimer.cancel (); - if (m_SendHandler) - { - auto handler = m_SendHandler; - m_SendHandler = nullptr; - handler (boost::asio::error::make_error_code (boost::asio::error::operation_aborted)); - } - } - - void Stream::HandleNextPacket (Packet * packet) - { - m_NumReceivedBytes += packet->GetLength (); - if (!m_SendStreamID) - m_SendStreamID = packet->GetReceiveStreamID (); + void Stream::Terminate () + { + m_AckSendTimer.cancel (); + m_ReceiveTimer.cancel (); + m_ResendTimer.cancel (); + if (m_SendHandler) + { + auto handler = m_SendHandler; + m_SendHandler = nullptr; + handler (boost::asio::error::make_error_code (boost::asio::error::operation_aborted)); + } + } + + void Stream::HandleNextPacket (Packet * packet) + { + m_NumReceivedBytes += packet->GetLength (); + if (!m_SendStreamID) + m_SendStreamID = packet->GetReceiveStreamID (); - if (!packet->IsNoAck ()) // ack received - ProcessAck (packet); - - int32_t receivedSeqn = packet->GetSeqn (); - bool isSyn = packet->IsSYN (); - if (!receivedSeqn && !isSyn) - { - // plain ack - LogPrint (eLogDebug, "Plain ACK received"); - delete packet; - return; - } + if (!packet->IsNoAck ()) // ack received + ProcessAck (packet); + + int32_t receivedSeqn = packet->GetSeqn (); + bool isSyn = packet->IsSYN (); + if (!receivedSeqn && !isSyn) + { + // plain ack + LogPrint (eLogDebug, "Plain ACK received"); + delete packet; + return; + } - LogPrint (eLogDebug, "Received seqn=", receivedSeqn); - if (isSyn || receivedSeqn == m_LastReceivedSequenceNumber + 1) - { - // we have received next in sequence message - ProcessPacket (packet); - - // we should also try stored messages if any - for (auto it = m_SavedPackets.begin (); it != m_SavedPackets.end ();) - { - if ((*it)->GetSeqn () == (uint32_t)(m_LastReceivedSequenceNumber + 1)) - { - Packet * savedPacket = *it; - m_SavedPackets.erase (it++); + LogPrint (eLogDebug, "Received seqn=", receivedSeqn); + if (isSyn || receivedSeqn == m_LastReceivedSequenceNumber + 1) + { + // we have received next in sequence message + ProcessPacket (packet); + + // we should also try stored messages if any + for (auto it = m_SavedPackets.begin (); it != m_SavedPackets.end ();) + { + if ((*it)->GetSeqn () == (uint32_t)(m_LastReceivedSequenceNumber + 1)) + { + Packet * savedPacket = *it; + m_SavedPackets.erase (it++); - ProcessPacket (savedPacket); - } - else - break; - } + ProcessPacket (savedPacket); + } + else + break; + } - // schedule ack for last message - if (m_Status == eStreamStatusOpen) - { - if (!m_IsAckSendScheduled) - { - m_IsAckSendScheduled = true; - m_AckSendTimer.expires_from_now (boost::posix_time::milliseconds(ACK_SEND_TIMEOUT)); - m_AckSendTimer.async_wait (std::bind (&Stream::HandleAckSendTimer, - shared_from_this (), std::placeholders::_1)); - } - } - else if (isSyn) - // we have to send SYN back to incoming connection - SendBuffer (); // also sets m_IsOpen - } - else - { - if (receivedSeqn <= m_LastReceivedSequenceNumber) - { - // we have received duplicate - LogPrint (eLogWarning, "Duplicate message ", receivedSeqn, " received"); - SendQuickAck (); // resend ack for previous message again - delete packet; // packet dropped - } - else - { - LogPrint (eLogWarning, "Missing messages from ", m_LastReceivedSequenceNumber + 1, " to ", receivedSeqn - 1); - // save message and wait for missing message again - SavePacket (packet); - if (m_LastReceivedSequenceNumber >= 0) - { - // send NACKs for missing messages ASAP - if (m_IsAckSendScheduled) - { - m_IsAckSendScheduled = false; - m_AckSendTimer.cancel (); - } - SendQuickAck (); - } - else - { - // wait for SYN - m_IsAckSendScheduled = true; - m_AckSendTimer.expires_from_now (boost::posix_time::milliseconds(ACK_SEND_TIMEOUT)); - m_AckSendTimer.async_wait (std::bind (&Stream::HandleAckSendTimer, - shared_from_this (), std::placeholders::_1)); - } - } - } - } + // schedule ack for last message + if (m_Status == eStreamStatusOpen) + { + if (!m_IsAckSendScheduled) + { + m_IsAckSendScheduled = true; + m_AckSendTimer.expires_from_now (boost::posix_time::milliseconds(ACK_SEND_TIMEOUT)); + m_AckSendTimer.async_wait (std::bind (&Stream::HandleAckSendTimer, + shared_from_this (), std::placeholders::_1)); + } + } + else if (isSyn) + // we have to send SYN back to incoming connection + SendBuffer (); // also sets m_IsOpen + } + else + { + if (receivedSeqn <= m_LastReceivedSequenceNumber) + { + // we have received duplicate + LogPrint (eLogWarning, "Duplicate message ", receivedSeqn, " received"); + SendQuickAck (); // resend ack for previous message again + delete packet; // packet dropped + } + else + { + LogPrint (eLogWarning, "Missing messages from ", m_LastReceivedSequenceNumber + 1, " to ", receivedSeqn - 1); + // save message and wait for missing message again + SavePacket (packet); + if (m_LastReceivedSequenceNumber >= 0) + { + // send NACKs for missing messages ASAP + if (m_IsAckSendScheduled) + { + m_IsAckSendScheduled = false; + m_AckSendTimer.cancel (); + } + SendQuickAck (); + } + else + { + // wait for SYN + m_IsAckSendScheduled = true; + m_AckSendTimer.expires_from_now (boost::posix_time::milliseconds(ACK_SEND_TIMEOUT)); + m_AckSendTimer.async_wait (std::bind (&Stream::HandleAckSendTimer, + shared_from_this (), std::placeholders::_1)); + } + } + } + } - void Stream::SavePacket (Packet * packet) - { - m_SavedPackets.insert (packet); - } + void Stream::SavePacket (Packet * packet) + { + m_SavedPackets.insert (packet); + } - void Stream::ProcessPacket (Packet * packet) - { - // process flags - uint32_t receivedSeqn = packet->GetSeqn (); - uint16_t flags = packet->GetFlags (); - LogPrint (eLogDebug, "Process seqn=", receivedSeqn, ", flags=", flags); - - const uint8_t * optionData = packet->GetOptionData (); - if (flags & PACKET_FLAG_SYNCHRONIZE) - LogPrint (eLogDebug, "Synchronize"); + void Stream::ProcessPacket (Packet * packet) + { + // process flags + uint32_t receivedSeqn = packet->GetSeqn (); + uint16_t flags = packet->GetFlags (); + LogPrint (eLogDebug, "Process seqn=", receivedSeqn, ", flags=", flags); + + const uint8_t * optionData = packet->GetOptionData (); + if (flags & PACKET_FLAG_SYNCHRONIZE) + LogPrint (eLogDebug, "Synchronize"); - if (flags & PACKET_FLAG_DELAY_REQUESTED) - { - optionData += 2; - } - - if (flags & PACKET_FLAG_FROM_INCLUDED) - { - optionData += m_RemoteIdentity.FromBuffer (optionData, packet->GetOptionSize ()); - LogPrint (eLogInfo, "From identity ", m_RemoteIdentity.GetIdentHash ().ToBase64 ()); - if (!m_RemoteLeaseSet) - LogPrint (eLogDebug, "Incoming stream from ", m_RemoteIdentity.GetIdentHash ().ToBase64 ()); - } + if (flags & PACKET_FLAG_DELAY_REQUESTED) + { + optionData += 2; + } + + if (flags & PACKET_FLAG_FROM_INCLUDED) + { + optionData += m_RemoteIdentity.FromBuffer (optionData, packet->GetOptionSize ()); + LogPrint (eLogInfo, "From identity ", m_RemoteIdentity.GetIdentHash ().ToBase64 ()); + if (!m_RemoteLeaseSet) + LogPrint (eLogDebug, "Incoming stream from ", m_RemoteIdentity.GetIdentHash ().ToBase64 ()); + } - if (flags & PACKET_FLAG_MAX_PACKET_SIZE_INCLUDED) - { - uint16_t maxPacketSize = bufbe16toh (optionData); - LogPrint (eLogDebug, "Max packet size ", maxPacketSize); - optionData += 2; - } - - if (flags & PACKET_FLAG_SIGNATURE_INCLUDED) - { - LogPrint (eLogDebug, "Signature"); - uint8_t signature[256]; - auto signatureLen = m_RemoteIdentity.GetSignatureLen (); - memcpy (signature, optionData, signatureLen); - memset (const_cast(optionData), 0, signatureLen); - if (!m_RemoteIdentity.Verify (packet->GetBuffer (), packet->GetLength (), signature)) - { - LogPrint (eLogError, "Signature verification failed"); - Close (); - flags |= PACKET_FLAG_CLOSE; - } - memcpy (const_cast(optionData), signature, signatureLen); - optionData += signatureLen; - } + if (flags & PACKET_FLAG_MAX_PACKET_SIZE_INCLUDED) + { + uint16_t maxPacketSize = bufbe16toh (optionData); + LogPrint (eLogDebug, "Max packet size ", maxPacketSize); + optionData += 2; + } + + if (flags & PACKET_FLAG_SIGNATURE_INCLUDED) + { + LogPrint (eLogDebug, "Signature"); + uint8_t signature[256]; + auto signatureLen = m_RemoteIdentity.GetSignatureLen (); + memcpy (signature, optionData, signatureLen); + memset (const_cast(optionData), 0, signatureLen); + if (!m_RemoteIdentity.Verify (packet->GetBuffer (), packet->GetLength (), signature)) + { + LogPrint (eLogError, "Signature verification failed"); + Close (); + flags |= PACKET_FLAG_CLOSE; + } + memcpy (const_cast(optionData), signature, signatureLen); + optionData += signatureLen; + } - packet->offset = packet->GetPayload () - packet->buf; - if (packet->GetLength () > 0) - { - m_ReceiveQueue.push (packet); - m_ReceiveTimer.cancel (); - } - else - delete packet; - - m_LastReceivedSequenceNumber = receivedSeqn; + packet->offset = packet->GetPayload () - packet->buf; + if (packet->GetLength () > 0) + { + m_ReceiveQueue.push (packet); + m_ReceiveTimer.cancel (); + } + else + delete packet; + + m_LastReceivedSequenceNumber = receivedSeqn; - if (flags & (PACKET_FLAG_CLOSE | PACKET_FLAG_RESET)) - { - LogPrint (eLogInfo, (flags & PACKET_FLAG_RESET) ? "Reset" : "Closed"); - m_Status = eStreamStatusReset; - Close (); - } - } + if (flags & (PACKET_FLAG_CLOSE | PACKET_FLAG_RESET)) + { + LogPrint (eLogInfo, (flags & PACKET_FLAG_RESET) ? "Reset" : "Closed"); + m_Status = eStreamStatusReset; + Close (); + } + } - void Stream::ProcessAck (Packet * packet) - { - bool acknowledged = false; - auto ts = i2p::util::GetMillisecondsSinceEpoch (); - uint32_t ackThrough = packet->GetAckThrough (); - int nackCount = packet->GetNACKCount (); - for (auto it = m_SentPackets.begin (); it != m_SentPackets.end ();) - { - auto seqn = (*it)->GetSeqn (); - if (seqn <= ackThrough) - { - if (nackCount > 0) - { - bool nacked = false; - for (int i = 0; i < nackCount; i++) - if (seqn == packet->GetNACK (i)) - { - nacked = true; - break; - } - if (nacked) - { - LogPrint (eLogDebug, "Packet ", seqn, " NACK"); - it++; - continue; - } - } - auto sentPacket = *it; - uint64_t rtt = ts - sentPacket->sendTime; - m_RTT = (m_RTT*seqn + rtt)/(seqn + 1); - m_RTO = m_RTT*1.5; // TODO: implement it better - LogPrint (eLogDebug, "Packet ", seqn, " acknowledged rtt=", rtt); - m_SentPackets.erase (it++); - delete sentPacket; - acknowledged = true; - if (m_WindowSize < WINDOW_SIZE) - m_WindowSize++; // slow start - else - { - // linear growth - if (ts > m_LastWindowSizeIncreaseTime + m_RTT) - { - m_WindowSize++; - if (m_WindowSize > MAX_WINDOW_SIZE) m_WindowSize = MAX_WINDOW_SIZE; - m_LastWindowSizeIncreaseTime = ts; - } - } - } - else - break; - } - if (m_SentPackets.empty ()) - m_ResendTimer.cancel (); - if (acknowledged) - { - m_NumResendAttempts = 0; - SendBuffer (); - } - if (m_Status == eStreamStatusClosing) - Close (); // all outgoing messages have been sent - } - - size_t Stream::Send (const uint8_t * buf, size_t len) - { - if (len > 0 && buf) - { - std::unique_lock l(m_SendBufferMutex); - m_SendBuffer.clear (); - m_SendBuffer.write ((const char *)buf, len); - } - m_Service.post (std::bind (&Stream::SendBuffer, shared_from_this ())); - return len; - } + void Stream::ProcessAck (Packet * packet) + { + bool acknowledged = false; + auto ts = i2p::util::GetMillisecondsSinceEpoch (); + uint32_t ackThrough = packet->GetAckThrough (); + int nackCount = packet->GetNACKCount (); + for (auto it = m_SentPackets.begin (); it != m_SentPackets.end ();) + { + auto seqn = (*it)->GetSeqn (); + if (seqn <= ackThrough) + { + if (nackCount > 0) + { + bool nacked = false; + for (int i = 0; i < nackCount; i++) + if (seqn == packet->GetNACK (i)) + { + nacked = true; + break; + } + if (nacked) + { + LogPrint (eLogDebug, "Packet ", seqn, " NACK"); + it++; + continue; + } + } + auto sentPacket = *it; + uint64_t rtt = ts - sentPacket->sendTime; + m_RTT = (m_RTT*seqn + rtt)/(seqn + 1); + m_RTO = m_RTT*1.5; // TODO: implement it better + LogPrint (eLogDebug, "Packet ", seqn, " acknowledged rtt=", rtt); + m_SentPackets.erase (it++); + delete sentPacket; + acknowledged = true; + if (m_WindowSize < WINDOW_SIZE) + m_WindowSize++; // slow start + else + { + // linear growth + if (ts > m_LastWindowSizeIncreaseTime + m_RTT) + { + m_WindowSize++; + if (m_WindowSize > MAX_WINDOW_SIZE) m_WindowSize = MAX_WINDOW_SIZE; + m_LastWindowSizeIncreaseTime = ts; + } + } + } + else + break; + } + if (m_SentPackets.empty ()) + m_ResendTimer.cancel (); + if (acknowledged) + { + m_NumResendAttempts = 0; + SendBuffer (); + } + if (m_Status == eStreamStatusClosing) + Close (); // all outgoing messages have been sent + } + + size_t Stream::Send (const uint8_t * buf, size_t len) + { + if (len > 0 && buf) + { + std::unique_lock l(m_SendBufferMutex); + m_SendBuffer.clear (); + m_SendBuffer.write ((const char *)buf, len); + } + m_Service.post (std::bind (&Stream::SendBuffer, shared_from_this ())); + return len; + } - void Stream::AsyncSend (const uint8_t * buf, size_t len, SendHandler handler) - { - if (m_SendHandler) - handler (boost::asio::error::make_error_code (boost::asio::error::in_progress)); - else - m_SendHandler = handler; - Send (buf, len); - } + void Stream::AsyncSend (const uint8_t * buf, size_t len, SendHandler handler) + { + if (m_SendHandler) + handler (boost::asio::error::make_error_code (boost::asio::error::in_progress)); + else + m_SendHandler = handler; + Send (buf, len); + } - void Stream::SendBuffer () - { - int numMsgs = m_WindowSize - m_SentPackets.size (); - if (numMsgs <= 0) return; // window is full - - bool isNoAck = m_LastReceivedSequenceNumber < 0; // first packet - std::vector packets; - { - std::unique_lock l(m_SendBufferMutex); - while ((m_Status == eStreamStatusNew) || (IsEstablished () && !m_SendBuffer.eof () && numMsgs > 0)) - { - Packet * p = new Packet (); - uint8_t * packet = p->GetBuffer (); - // TODO: implement setters - size_t size = 0; - htobe32buf (packet + size, m_SendStreamID); - size += 4; // sendStreamID - htobe32buf (packet + size, m_RecvStreamID); - size += 4; // receiveStreamID - htobe32buf (packet + size, m_SequenceNumber++); - size += 4; // sequenceNum - if (isNoAck) - htobe32buf (packet + size, m_LastReceivedSequenceNumber); - else - htobuf32 (packet + size, 0); - size += 4; // ack Through - packet[size] = 0; - size++; // NACK count - packet[size] = m_RTO/1000; - size++; // resend delay - if (m_Status == eStreamStatusNew) - { - // initial packet - m_Status = eStreamStatusOpen; - uint16_t flags = PACKET_FLAG_SYNCHRONIZE | PACKET_FLAG_FROM_INCLUDED | - PACKET_FLAG_SIGNATURE_INCLUDED | PACKET_FLAG_MAX_PACKET_SIZE_INCLUDED; - if (isNoAck) flags |= PACKET_FLAG_NO_ACK; - htobe16buf (packet + size, flags); - size += 2; // flags - size_t identityLen = m_LocalDestination.GetOwner ().GetIdentity ().GetFullLen (); - size_t signatureLen = m_LocalDestination.GetOwner ().GetIdentity ().GetSignatureLen (); - htobe16buf (packet + size, identityLen + signatureLen + 2); // identity + signature + packet size - size += 2; // options size - m_LocalDestination.GetOwner ().GetIdentity ().ToBuffer (packet + size, identityLen); - size += identityLen; // from - htobe16buf (packet + size, STREAMING_MTU); - size += 2; // max packet size - uint8_t * signature = packet + size; // set it later - memset (signature, 0, signatureLen); // zeroes for now - size += signatureLen; // signature - m_SendBuffer.read ((char *)(packet + size), STREAMING_MTU - size); - size += m_SendBuffer.gcount (); // payload - m_LocalDestination.GetOwner ().Sign (packet, size, signature); - } - else - { - // follow on packet - htobuf16 (packet + size, 0); - size += 2; // flags - htobuf16 (packet + size, 0); // no options - size += 2; // options size - m_SendBuffer.read((char *)(packet + size), STREAMING_MTU - size); - size += m_SendBuffer.gcount (); // payload - } - p->len = size; - packets.push_back (p); - numMsgs--; - } - if (m_SendBuffer.eof () && m_SendHandler) - { - m_SendHandler (boost::system::error_code ()); - m_SendHandler = nullptr; - } - } - if (packets.size () > 0) - { - m_IsAckSendScheduled = false; - m_AckSendTimer.cancel (); - bool isEmpty = m_SentPackets.empty (); - auto ts = i2p::util::GetMillisecondsSinceEpoch (); - for (auto it: packets) - { - it->sendTime = ts; - m_SentPackets.insert (it); - } - SendPackets (packets); - if (m_Status == eStreamStatusClosing && m_SendBuffer.eof ()) - SendClose (); - if (isEmpty) - ScheduleResend (); - } - } - - void Stream::SendQuickAck () - { - int32_t lastReceivedSeqn = m_LastReceivedSequenceNumber; - if (!m_SavedPackets.empty ()) - { - int32_t seqn = (*m_SavedPackets.rbegin ())->GetSeqn (); - if (seqn > lastReceivedSeqn) lastReceivedSeqn = seqn; - } - if (lastReceivedSeqn < 0) - { - LogPrint (eLogError, "No packets have been received yet"); - return; - } - - Packet p; - uint8_t * packet = p.GetBuffer (); - size_t size = 0; - htobe32buf (packet + size, m_SendStreamID); - size += 4; // sendStreamID - htobe32buf (packet + size, m_RecvStreamID); - size += 4; // receiveStreamID - htobuf32 (packet + size, 0); // this is plain Ack message - size += 4; // sequenceNum - htobe32buf (packet + size, lastReceivedSeqn); - size += 4; // ack Through - uint8_t numNacks = 0; - if (lastReceivedSeqn > m_LastReceivedSequenceNumber) - { - // fill NACKs - uint8_t * nacks = packet + size + 1; - auto nextSeqn = m_LastReceivedSequenceNumber + 1; - for (auto it: m_SavedPackets) - { - auto seqn = it->GetSeqn (); - if (numNacks + (seqn - nextSeqn) >= 256) - { - LogPrint (eLogError, "Number of NACKs exceeds 256. seqn=", seqn, " nextSeqn=", nextSeqn); - htobe32buf (packet + 12, nextSeqn); // change ack Through - break; - } - for (uint32_t i = nextSeqn; i < seqn; i++) - { - htobe32buf (nacks, i); - nacks += 4; - numNacks++; - } - nextSeqn = seqn + 1; - } - packet[size] = numNacks; - size++; // NACK count - size += numNacks*4; // NACKs - } - else - { - // No NACKs - packet[size] = 0; - size++; // NACK count - } - size++; // resend delay - htobuf16 (packet + size, 0); // nof flags set - size += 2; // flags - htobuf16 (packet + size, 0); // no options - size += 2; // options size - p.len = size; + void Stream::SendBuffer () + { + int numMsgs = m_WindowSize - m_SentPackets.size (); + if (numMsgs <= 0) return; // window is full + + bool isNoAck = m_LastReceivedSequenceNumber < 0; // first packet + std::vector packets; + { + std::unique_lock l(m_SendBufferMutex); + while ((m_Status == eStreamStatusNew) || (IsEstablished () && !m_SendBuffer.eof () && numMsgs > 0)) + { + Packet * p = new Packet (); + uint8_t * packet = p->GetBuffer (); + // TODO: implement setters + size_t size = 0; + htobe32buf (packet + size, m_SendStreamID); + size += 4; // sendStreamID + htobe32buf (packet + size, m_RecvStreamID); + size += 4; // receiveStreamID + htobe32buf (packet + size, m_SequenceNumber++); + size += 4; // sequenceNum + if (isNoAck) + htobe32buf (packet + size, m_LastReceivedSequenceNumber); + else + htobuf32 (packet + size, 0); + size += 4; // ack Through + packet[size] = 0; + size++; // NACK count + packet[size] = m_RTO/1000; + size++; // resend delay + if (m_Status == eStreamStatusNew) + { + // initial packet + m_Status = eStreamStatusOpen; + uint16_t flags = PACKET_FLAG_SYNCHRONIZE | PACKET_FLAG_FROM_INCLUDED | + PACKET_FLAG_SIGNATURE_INCLUDED | PACKET_FLAG_MAX_PACKET_SIZE_INCLUDED; + if (isNoAck) flags |= PACKET_FLAG_NO_ACK; + htobe16buf (packet + size, flags); + size += 2; // flags + size_t identityLen = m_LocalDestination.GetOwner ().GetIdentity ().GetFullLen (); + size_t signatureLen = m_LocalDestination.GetOwner ().GetIdentity ().GetSignatureLen (); + htobe16buf (packet + size, identityLen + signatureLen + 2); // identity + signature + packet size + size += 2; // options size + m_LocalDestination.GetOwner ().GetIdentity ().ToBuffer (packet + size, identityLen); + size += identityLen; // from + htobe16buf (packet + size, STREAMING_MTU); + size += 2; // max packet size + uint8_t * signature = packet + size; // set it later + memset (signature, 0, signatureLen); // zeroes for now + size += signatureLen; // signature + m_SendBuffer.read ((char *)(packet + size), STREAMING_MTU - size); + size += m_SendBuffer.gcount (); // payload + m_LocalDestination.GetOwner ().Sign (packet, size, signature); + } + else + { + // follow on packet + htobuf16 (packet + size, 0); + size += 2; // flags + htobuf16 (packet + size, 0); // no options + size += 2; // options size + m_SendBuffer.read((char *)(packet + size), STREAMING_MTU - size); + size += m_SendBuffer.gcount (); // payload + } + p->len = size; + packets.push_back (p); + numMsgs--; + } + if (m_SendBuffer.eof () && m_SendHandler) + { + m_SendHandler (boost::system::error_code ()); + m_SendHandler = nullptr; + } + } + if (packets.size () > 0) + { + m_IsAckSendScheduled = false; + m_AckSendTimer.cancel (); + bool isEmpty = m_SentPackets.empty (); + auto ts = i2p::util::GetMillisecondsSinceEpoch (); + for (auto it: packets) + { + it->sendTime = ts; + m_SentPackets.insert (it); + } + SendPackets (packets); + if (m_Status == eStreamStatusClosing && m_SendBuffer.eof ()) + SendClose (); + if (isEmpty) + ScheduleResend (); + } + } + + void Stream::SendQuickAck () + { + int32_t lastReceivedSeqn = m_LastReceivedSequenceNumber; + if (!m_SavedPackets.empty ()) + { + int32_t seqn = (*m_SavedPackets.rbegin ())->GetSeqn (); + if (seqn > lastReceivedSeqn) lastReceivedSeqn = seqn; + } + if (lastReceivedSeqn < 0) + { + LogPrint (eLogError, "No packets have been received yet"); + return; + } + + Packet p; + uint8_t * packet = p.GetBuffer (); + size_t size = 0; + htobe32buf (packet + size, m_SendStreamID); + size += 4; // sendStreamID + htobe32buf (packet + size, m_RecvStreamID); + size += 4; // receiveStreamID + htobuf32 (packet + size, 0); // this is plain Ack message + size += 4; // sequenceNum + htobe32buf (packet + size, lastReceivedSeqn); + size += 4; // ack Through + uint8_t numNacks = 0; + if (lastReceivedSeqn > m_LastReceivedSequenceNumber) + { + // fill NACKs + uint8_t * nacks = packet + size + 1; + auto nextSeqn = m_LastReceivedSequenceNumber + 1; + for (auto it: m_SavedPackets) + { + auto seqn = it->GetSeqn (); + if (numNacks + (seqn - nextSeqn) >= 256) + { + LogPrint (eLogError, "Number of NACKs exceeds 256. seqn=", seqn, " nextSeqn=", nextSeqn); + htobe32buf (packet + 12, nextSeqn); // change ack Through + break; + } + for (uint32_t i = nextSeqn; i < seqn; i++) + { + htobe32buf (nacks, i); + nacks += 4; + numNacks++; + } + nextSeqn = seqn + 1; + } + packet[size] = numNacks; + size++; // NACK count + size += numNacks*4; // NACKs + } + else + { + // No NACKs + packet[size] = 0; + size++; // NACK count + } + size++; // resend delay + htobuf16 (packet + size, 0); // nof flags set + size += 2; // flags + htobuf16 (packet + size, 0); // no options + size += 2; // options size + p.len = size; - SendPackets (std::vector { &p }); - LogPrint ("Quick Ack sent. ", (int)numNacks, " NACKs"); - } + SendPackets (std::vector { &p }); + LogPrint ("Quick Ack sent. ", (int)numNacks, " NACKs"); + } - void Stream::Close () - { - switch (m_Status) - { - case eStreamStatusOpen: - m_Status = eStreamStatusClosing; - Close (); // recursion - if (m_Status == eStreamStatusClosing) //still closing - LogPrint (eLogInfo, "Trying to send stream data before closing"); - break; - case eStreamStatusReset: - SendClose (); - Terminate (); - m_LocalDestination.DeleteStream (shared_from_this ()); - break; - case eStreamStatusClosing: - if (m_SentPackets.empty () && m_SendBuffer.eof ()) // nothing to send - { - m_Status = eStreamStatusClosed; - SendClose (); - Terminate (); - m_LocalDestination.DeleteStream (shared_from_this ()); - } - break; - case eStreamStatusClosed: - // already closed - Terminate (); - m_LocalDestination.DeleteStream (shared_from_this ()); - break; - default: - LogPrint (eLogWarning, "Unexpected stream status ", (int)m_Status); - }; - } + void Stream::Close () + { + switch (m_Status) + { + case eStreamStatusOpen: + m_Status = eStreamStatusClosing; + Close (); // recursion + if (m_Status == eStreamStatusClosing) //still closing + LogPrint (eLogInfo, "Trying to send stream data before closing"); + break; + case eStreamStatusReset: + SendClose (); + Terminate (); + m_LocalDestination.DeleteStream (shared_from_this ()); + break; + case eStreamStatusClosing: + if (m_SentPackets.empty () && m_SendBuffer.eof ()) // nothing to send + { + m_Status = eStreamStatusClosed; + SendClose (); + Terminate (); + m_LocalDestination.DeleteStream (shared_from_this ()); + } + break; + case eStreamStatusClosed: + // already closed + Terminate (); + m_LocalDestination.DeleteStream (shared_from_this ()); + break; + default: + LogPrint (eLogWarning, "Unexpected stream status ", (int)m_Status); + }; + } - void Stream::SendClose () - { - Packet * p = new Packet (); - uint8_t * packet = p->GetBuffer (); - size_t size = 0; - htobe32buf (packet + size, m_SendStreamID); - size += 4; // sendStreamID - htobe32buf (packet + size, m_RecvStreamID); - size += 4; // receiveStreamID - htobe32buf (packet + size, m_SequenceNumber++); - size += 4; // sequenceNum - htobe32buf (packet + size, m_LastReceivedSequenceNumber); - size += 4; // ack Through - packet[size] = 0; - size++; // NACK count - size++; // resend delay - htobe16buf (packet + size, PACKET_FLAG_CLOSE | PACKET_FLAG_SIGNATURE_INCLUDED); - size += 2; // flags - size_t signatureLen = m_LocalDestination.GetOwner ().GetIdentity ().GetSignatureLen (); - htobe16buf (packet + size, signatureLen); // signature only - size += 2; // options size - uint8_t * signature = packet + size; - memset (packet + size, 0, signatureLen); - size += signatureLen; // signature - m_LocalDestination.GetOwner ().Sign (packet, size, signature); - - p->len = size; - m_Service.post (std::bind (&Stream::SendPacket, shared_from_this (), p)); - LogPrint ("FIN sent"); - } - - size_t Stream::ConcatenatePackets (uint8_t * buf, size_t len) - { - size_t pos = 0; - while (pos < len && !m_ReceiveQueue.empty ()) - { - Packet * packet = m_ReceiveQueue.front (); - size_t l = std::min (packet->GetLength (), len - pos); - memcpy (buf + pos, packet->GetBuffer (), l); - pos += l; - packet->offset += l; - if (!packet->GetLength ()) - { - m_ReceiveQueue.pop (); - delete packet; - } - } - return pos; - } + void Stream::SendClose () + { + Packet * p = new Packet (); + uint8_t * packet = p->GetBuffer (); + size_t size = 0; + htobe32buf (packet + size, m_SendStreamID); + size += 4; // sendStreamID + htobe32buf (packet + size, m_RecvStreamID); + size += 4; // receiveStreamID + htobe32buf (packet + size, m_SequenceNumber++); + size += 4; // sequenceNum + htobe32buf (packet + size, m_LastReceivedSequenceNumber); + size += 4; // ack Through + packet[size] = 0; + size++; // NACK count + size++; // resend delay + htobe16buf (packet + size, PACKET_FLAG_CLOSE | PACKET_FLAG_SIGNATURE_INCLUDED); + size += 2; // flags + size_t signatureLen = m_LocalDestination.GetOwner ().GetIdentity ().GetSignatureLen (); + htobe16buf (packet + size, signatureLen); // signature only + size += 2; // options size + uint8_t * signature = packet + size; + memset (packet + size, 0, signatureLen); + size += signatureLen; // signature + m_LocalDestination.GetOwner ().Sign (packet, size, signature); + + p->len = size; + m_Service.post (std::bind (&Stream::SendPacket, shared_from_this (), p)); + LogPrint ("FIN sent"); + } + + size_t Stream::ConcatenatePackets (uint8_t * buf, size_t len) + { + size_t pos = 0; + while (pos < len && !m_ReceiveQueue.empty ()) + { + Packet * packet = m_ReceiveQueue.front (); + size_t l = std::min (packet->GetLength (), len - pos); + memcpy (buf + pos, packet->GetBuffer (), l); + pos += l; + packet->offset += l; + if (!packet->GetLength ()) + { + m_ReceiveQueue.pop (); + delete packet; + } + } + return pos; + } - bool Stream::SendPacket (Packet * packet) - { - if (packet) - { - if (m_IsAckSendScheduled) - { - m_IsAckSendScheduled = false; - m_AckSendTimer.cancel (); - } - SendPackets (std::vector { packet }); - if (m_Status == eStreamStatusOpen) - { - bool isEmpty = m_SentPackets.empty (); - m_SentPackets.insert (packet); - if (isEmpty) - ScheduleResend (); - } - else - delete packet; - return true; - } - else - return false; - } - - void Stream::SendPackets (const std::vector& packets) - { - if (!m_RemoteLeaseSet) - { - UpdateCurrentRemoteLease (); - if (!m_RemoteLeaseSet) - { - LogPrint (eLogError, "Can't send packets. Missing remote LeaseSet"); - return; - } - } - if (!m_CurrentOutboundTunnel || !m_CurrentOutboundTunnel->IsEstablished ()) - m_CurrentOutboundTunnel = m_LocalDestination.GetOwner ().GetTunnelPool ()->GetNewOutboundTunnel (m_CurrentOutboundTunnel); - if (!m_CurrentOutboundTunnel) - { - LogPrint (eLogError, "No outbound tunnels in the pool"); - return; - } + bool Stream::SendPacket (Packet * packet) + { + if (packet) + { + if (m_IsAckSendScheduled) + { + m_IsAckSendScheduled = false; + m_AckSendTimer.cancel (); + } + SendPackets (std::vector { packet }); + if (m_Status == eStreamStatusOpen) + { + bool isEmpty = m_SentPackets.empty (); + m_SentPackets.insert (packet); + if (isEmpty) + ScheduleResend (); + } + else + delete packet; + return true; + } + else + return false; + } + + void Stream::SendPackets (const std::vector& packets) + { + if (!m_RemoteLeaseSet) + { + UpdateCurrentRemoteLease (); + if (!m_RemoteLeaseSet) + { + LogPrint (eLogError, "Can't send packets. Missing remote LeaseSet"); + return; + } + } + if (!m_CurrentOutboundTunnel || !m_CurrentOutboundTunnel->IsEstablished ()) + m_CurrentOutboundTunnel = m_LocalDestination.GetOwner ().GetTunnelPool ()->GetNewOutboundTunnel (m_CurrentOutboundTunnel); + if (!m_CurrentOutboundTunnel) + { + LogPrint (eLogError, "No outbound tunnels in the pool"); + return; + } - auto ts = i2p::util::GetMillisecondsSinceEpoch (); - if (!m_CurrentRemoteLease.endDate || ts >= m_CurrentRemoteLease.endDate - i2p::tunnel::TUNNEL_EXPIRATION_THRESHOLD*1000) - UpdateCurrentRemoteLease (true); - if (ts < m_CurrentRemoteLease.endDate) - { - std::vector msgs; - for (auto it: packets) - { - auto msg = m_RoutingSession->WrapSingleMessage (CreateDataMessage (it->GetBuffer (), it->GetLength ())); - msgs.push_back (i2p::tunnel::TunnelMessageBlock - { - i2p::tunnel::eDeliveryTypeTunnel, - m_CurrentRemoteLease.tunnelGateway, m_CurrentRemoteLease.tunnelID, - msg - }); - m_NumSentBytes += it->GetLength (); - } - m_CurrentOutboundTunnel->SendTunnelDataMsg (msgs); - } - else - LogPrint (eLogWarning, "All leases are expired"); - } + auto ts = i2p::util::GetMillisecondsSinceEpoch (); + if (!m_CurrentRemoteLease.endDate || ts >= m_CurrentRemoteLease.endDate - i2p::tunnel::TUNNEL_EXPIRATION_THRESHOLD*1000) + UpdateCurrentRemoteLease (true); + if (ts < m_CurrentRemoteLease.endDate) + { + std::vector msgs; + for (auto it: packets) + { + auto msg = m_RoutingSession->WrapSingleMessage (CreateDataMessage (it->GetBuffer (), it->GetLength ())); + msgs.push_back (i2p::tunnel::TunnelMessageBlock + { + i2p::tunnel::eDeliveryTypeTunnel, + m_CurrentRemoteLease.tunnelGateway, m_CurrentRemoteLease.tunnelID, + msg + }); + m_NumSentBytes += it->GetLength (); + } + m_CurrentOutboundTunnel->SendTunnelDataMsg (msgs); + } + else + LogPrint (eLogWarning, "All leases are expired"); + } - - void Stream::ScheduleResend () - { - m_ResendTimer.cancel (); - m_ResendTimer.expires_from_now (boost::posix_time::milliseconds(m_RTO)); - m_ResendTimer.async_wait (std::bind (&Stream::HandleResendTimer, - shared_from_this (), std::placeholders::_1)); - } - - void Stream::HandleResendTimer (const boost::system::error_code& ecode) - { - if (ecode != boost::asio::error::operation_aborted) - { - // check for resend attempts - if (m_NumResendAttempts >= MAX_NUM_RESEND_ATTEMPTS) - { - LogPrint (eLogWarning, "Stream packet was not ACKed after ", MAX_NUM_RESEND_ATTEMPTS, " attempts. Terminate"); - m_Status = eStreamStatusReset; - Close (); - return; - } + + void Stream::ScheduleResend () + { + m_ResendTimer.cancel (); + m_ResendTimer.expires_from_now (boost::posix_time::milliseconds(m_RTO)); + m_ResendTimer.async_wait (std::bind (&Stream::HandleResendTimer, + shared_from_this (), std::placeholders::_1)); + } + + void Stream::HandleResendTimer (const boost::system::error_code& ecode) + { + if (ecode != boost::asio::error::operation_aborted) + { + // check for resend attempts + if (m_NumResendAttempts >= MAX_NUM_RESEND_ATTEMPTS) + { + LogPrint (eLogWarning, "Stream packet was not ACKed after ", MAX_NUM_RESEND_ATTEMPTS, " attempts. Terminate"); + m_Status = eStreamStatusReset; + Close (); + return; + } - // collect packets to resend - auto ts = i2p::util::GetMillisecondsSinceEpoch (); - std::vector packets; - for (auto it : m_SentPackets) - { - if (ts >= it->sendTime + m_RTO) - { - it->sendTime = ts; - packets.push_back (it); - } - } + // collect packets to resend + auto ts = i2p::util::GetMillisecondsSinceEpoch (); + std::vector packets; + for (auto it : m_SentPackets) + { + if (ts >= it->sendTime + m_RTO) + { + it->sendTime = ts; + packets.push_back (it); + } + } - // select tunnels if necessary and send - if (packets.size () > 0) - { - m_NumResendAttempts++; - m_RTO *= 2; - switch (m_NumResendAttempts) - { - case 1: // congesion avoidance - m_WindowSize /= 2; - if (m_WindowSize < MIN_WINDOW_SIZE) m_WindowSize = MIN_WINDOW_SIZE; - break; - case 2: - m_RTO = INITIAL_RTO; // drop RTO to initial upon tunnels pair change first time - // no break here - case 4: - UpdateCurrentRemoteLease (); // pick another lease - LogPrint (eLogWarning, "Another remote lease has been selected for stream"); - break; - case 3: - // pick another outbound tunnel - m_CurrentOutboundTunnel = m_LocalDestination.GetOwner ().GetTunnelPool ()->GetNextOutboundTunnel (m_CurrentOutboundTunnel); - LogPrint (eLogWarning, "Another outbound tunnel has been selected for stream"); - break; - default: ; - } - SendPackets (packets); - } - ScheduleResend (); - } - } - - void Stream::HandleAckSendTimer (const boost::system::error_code& ecode) - { - if (m_IsAckSendScheduled) - { - if (m_LastReceivedSequenceNumber < 0) - { - LogPrint (eLogWarning, "SYN has not been recived after ", ACK_SEND_TIMEOUT, " milliseconds after follow on. Terminate"); - m_Status = eStreamStatusReset; - Close (); - return; - } - if (m_Status == eStreamStatusOpen) - SendQuickAck (); - m_IsAckSendScheduled = false; - } - } + // select tunnels if necessary and send + if (packets.size () > 0) + { + m_NumResendAttempts++; + m_RTO *= 2; + switch (m_NumResendAttempts) + { + case 1: // congesion avoidance + m_WindowSize /= 2; + if (m_WindowSize < MIN_WINDOW_SIZE) m_WindowSize = MIN_WINDOW_SIZE; + break; + case 2: + m_RTO = INITIAL_RTO; // drop RTO to initial upon tunnels pair change first time + // no break here + case 4: + UpdateCurrentRemoteLease (); // pick another lease + LogPrint (eLogWarning, "Another remote lease has been selected for stream"); + break; + case 3: + // pick another outbound tunnel + m_CurrentOutboundTunnel = m_LocalDestination.GetOwner ().GetTunnelPool ()->GetNextOutboundTunnel (m_CurrentOutboundTunnel); + LogPrint (eLogWarning, "Another outbound tunnel has been selected for stream"); + break; + default: ; + } + SendPackets (packets); + } + ScheduleResend (); + } + } + + void Stream::HandleAckSendTimer (const boost::system::error_code& ecode) + { + if (m_IsAckSendScheduled) + { + if (m_LastReceivedSequenceNumber < 0) + { + LogPrint (eLogWarning, "SYN has not been recived after ", ACK_SEND_TIMEOUT, " milliseconds after follow on. Terminate"); + m_Status = eStreamStatusReset; + Close (); + return; + } + if (m_Status == eStreamStatusOpen) + SendQuickAck (); + m_IsAckSendScheduled = false; + } + } - void Stream::UpdateCurrentRemoteLease (bool expired) - { - if (!m_RemoteLeaseSet) - { - m_RemoteLeaseSet = m_LocalDestination.GetOwner ().FindLeaseSet (m_RemoteIdentity.GetIdentHash ()); - if (!m_RemoteLeaseSet) - LogPrint ("LeaseSet ", m_RemoteIdentity.GetIdentHash ().ToBase64 (), " not found"); - } - if (m_RemoteLeaseSet) - { - if (!m_RoutingSession) - m_RoutingSession = m_LocalDestination.GetOwner ().GetRoutingSession (m_RemoteLeaseSet, 32); - auto leases = m_RemoteLeaseSet->GetNonExpiredLeases (false); // try without threshold first - if (leases.empty ()) - { - expired = false; - m_LocalDestination.GetOwner ().RequestDestination (m_RemoteIdentity.GetIdentHash ()); // time to re-request - leases = m_RemoteLeaseSet->GetNonExpiredLeases (true); // then with threshold - } - if (!leases.empty ()) - { - bool updated = false; - if (expired) - { - for (auto it: leases) - if ((it.tunnelGateway == m_CurrentRemoteLease.tunnelGateway) && (it.tunnelID != m_CurrentRemoteLease.tunnelID)) - { - m_CurrentRemoteLease = it; - updated = true; - break; - } - } - if (!updated) - { - uint32_t i = i2p::context.GetRandomNumberGenerator ().GenerateWord32 (0, leases.size () - 1); - if (m_CurrentRemoteLease.endDate && leases[i].tunnelID == m_CurrentRemoteLease.tunnelID) - // make sure we don't select previous - i = (i + 1) % leases.size (); // if so, pick next - m_CurrentRemoteLease = leases[i]; - } - } - else - { - m_RemoteLeaseSet = nullptr; - m_CurrentRemoteLease.endDate = 0; - // re-request expired - } - } - else - m_CurrentRemoteLease.endDate = 0; - } + void Stream::UpdateCurrentRemoteLease (bool expired) + { + if (!m_RemoteLeaseSet) + { + m_RemoteLeaseSet = m_LocalDestination.GetOwner ().FindLeaseSet (m_RemoteIdentity.GetIdentHash ()); + if (!m_RemoteLeaseSet) + LogPrint ("LeaseSet ", m_RemoteIdentity.GetIdentHash ().ToBase64 (), " not found"); + } + if (m_RemoteLeaseSet) + { + if (!m_RoutingSession) + m_RoutingSession = m_LocalDestination.GetOwner ().GetRoutingSession (m_RemoteLeaseSet, 32); + auto leases = m_RemoteLeaseSet->GetNonExpiredLeases (false); // try without threshold first + if (leases.empty ()) + { + expired = false; + m_LocalDestination.GetOwner ().RequestDestination (m_RemoteIdentity.GetIdentHash ()); // time to re-request + leases = m_RemoteLeaseSet->GetNonExpiredLeases (true); // then with threshold + } + if (!leases.empty ()) + { + bool updated = false; + if (expired) + { + for (auto it: leases) + if ((it.tunnelGateway == m_CurrentRemoteLease.tunnelGateway) && (it.tunnelID != m_CurrentRemoteLease.tunnelID)) + { + m_CurrentRemoteLease = it; + updated = true; + break; + } + } + if (!updated) + { + uint32_t i = i2p::context.GetRandomNumberGenerator ().GenerateWord32 (0, leases.size () - 1); + if (m_CurrentRemoteLease.endDate && leases[i].tunnelID == m_CurrentRemoteLease.tunnelID) + // make sure we don't select previous + i = (i + 1) % leases.size (); // if so, pick next + m_CurrentRemoteLease = leases[i]; + } + } + else + { + m_RemoteLeaseSet = nullptr; + m_CurrentRemoteLease.endDate = 0; + // re-request expired + } + } + else + m_CurrentRemoteLease.endDate = 0; + } - std::shared_ptr Stream::CreateDataMessage (const uint8_t * payload, size_t len) - { - auto msg = ToSharedI2NPMessage (NewI2NPShortMessage ()); - CryptoPP::Gzip compressor; - if (len <= i2p::stream::COMPRESSION_THRESHOLD_SIZE) - compressor.SetDeflateLevel (CryptoPP::Gzip::MIN_DEFLATE_LEVEL); - else - compressor.SetDeflateLevel (CryptoPP::Gzip::DEFAULT_DEFLATE_LEVEL); - compressor.Put (payload, len); - compressor.MessageEnd(); - int size = compressor.MaxRetrievable (); - uint8_t * buf = msg->GetPayload (); - htobe32buf (buf, size); // length - buf += 4; - compressor.Get (buf, size); - htobe16buf (buf + 4, m_LocalDestination.GetLocalPort ()); // source port - htobe16buf (buf + 6, m_Port); // destination port - buf[9] = i2p::client::PROTOCOL_TYPE_STREAMING; // streaming protocol - msg->len += size + 4; - msg->FillI2NPMessageHeader (eI2NPData); - - return msg; - } - - void StreamingDestination::Start () - { - } - - void StreamingDestination::Stop () - { - ResetAcceptor (); - { - std::unique_lock l(m_StreamsMutex); - m_Streams.clear (); - } - } - - void StreamingDestination::HandleNextPacket (Packet * packet) - { - uint32_t sendStreamID = packet->GetSendStreamID (); - if (sendStreamID) - { - auto it = m_Streams.find (sendStreamID); - if (it != m_Streams.end ()) - it->second->HandleNextPacket (packet); - else - { - LogPrint ("Unknown stream sendStreamID=", sendStreamID); - delete packet; - } - } - else - { - if (packet->IsSYN () && !packet->GetSeqn ()) // new incoming stream - { - auto incomingStream = CreateNewIncomingStream (); - incomingStream->HandleNextPacket (packet); - if (m_Acceptor != nullptr) - m_Acceptor (incomingStream); - else - { - LogPrint ("Acceptor for incoming stream is not set"); - DeleteStream (incomingStream); - } - } - else // follow on packet without SYN - { - uint32_t receiveStreamID = packet->GetReceiveStreamID (); - for (auto it: m_Streams) - if (it.second->GetSendStreamID () == receiveStreamID) - { - // found - it.second->HandleNextPacket (packet); - return; - } - // TODO: should queue it up - LogPrint ("Unknown stream receiveStreamID=", receiveStreamID); - delete packet; - } - } - } + std::shared_ptr Stream::CreateDataMessage (const uint8_t * payload, size_t len) + { + auto msg = ToSharedI2NPMessage (NewI2NPShortMessage ()); + CryptoPP::Gzip compressor; + if (len <= i2p::stream::COMPRESSION_THRESHOLD_SIZE) + compressor.SetDeflateLevel (CryptoPP::Gzip::MIN_DEFLATE_LEVEL); + else + compressor.SetDeflateLevel (CryptoPP::Gzip::DEFAULT_DEFLATE_LEVEL); + compressor.Put (payload, len); + compressor.MessageEnd(); + int size = compressor.MaxRetrievable (); + uint8_t * buf = msg->GetPayload (); + htobe32buf (buf, size); // length + buf += 4; + compressor.Get (buf, size); + htobe16buf (buf + 4, m_LocalDestination.GetLocalPort ()); // source port + htobe16buf (buf + 6, m_Port); // destination port + buf[9] = i2p::client::PROTOCOL_TYPE_STREAMING; // streaming protocol + msg->len += size + 4; + msg->FillI2NPMessageHeader (eI2NPData); + + return msg; + } + + void StreamingDestination::Start () + { + } + + void StreamingDestination::Stop () + { + ResetAcceptor (); + { + std::unique_lock l(m_StreamsMutex); + m_Streams.clear (); + } + } + + void StreamingDestination::HandleNextPacket (Packet * packet) + { + uint32_t sendStreamID = packet->GetSendStreamID (); + if (sendStreamID) + { + auto it = m_Streams.find (sendStreamID); + if (it != m_Streams.end ()) + it->second->HandleNextPacket (packet); + else + { + LogPrint ("Unknown stream sendStreamID=", sendStreamID); + delete packet; + } + } + else + { + if (packet->IsSYN () && !packet->GetSeqn ()) // new incoming stream + { + auto incomingStream = CreateNewIncomingStream (); + incomingStream->HandleNextPacket (packet); + if (m_Acceptor != nullptr) + m_Acceptor (incomingStream); + else + { + LogPrint ("Acceptor for incoming stream is not set"); + DeleteStream (incomingStream); + } + } + else // follow on packet without SYN + { + uint32_t receiveStreamID = packet->GetReceiveStreamID (); + for (auto it: m_Streams) + if (it.second->GetSendStreamID () == receiveStreamID) + { + // found + it.second->HandleNextPacket (packet); + return; + } + // TODO: should queue it up + LogPrint ("Unknown stream receiveStreamID=", receiveStreamID); + delete packet; + } + } + } - std::shared_ptr StreamingDestination::CreateNewOutgoingStream (std::shared_ptr remote, int port) - { - auto s = std::make_shared (m_Owner.GetService (), *this, remote, port); - std::unique_lock l(m_StreamsMutex); - m_Streams[s->GetRecvStreamID ()] = s; - return s; - } + std::shared_ptr StreamingDestination::CreateNewOutgoingStream (std::shared_ptr remote, int port) + { + auto s = std::make_shared (m_Owner.GetService (), *this, remote, port); + std::unique_lock l(m_StreamsMutex); + m_Streams[s->GetRecvStreamID ()] = s; + return s; + } - std::shared_ptr StreamingDestination::CreateNewIncomingStream () - { - auto s = std::make_shared (m_Owner.GetService (), *this); - std::unique_lock l(m_StreamsMutex); - m_Streams[s->GetRecvStreamID ()] = s; - return s; - } + std::shared_ptr StreamingDestination::CreateNewIncomingStream () + { + auto s = std::make_shared (m_Owner.GetService (), *this); + std::unique_lock l(m_StreamsMutex); + m_Streams[s->GetRecvStreamID ()] = s; + return s; + } - void StreamingDestination::DeleteStream (std::shared_ptr stream) - { - if (stream) - { - std::unique_lock l(m_StreamsMutex); - auto it = m_Streams.find (stream->GetRecvStreamID ()); - if (it != m_Streams.end ()) - m_Streams.erase (it); - } - } + void StreamingDestination::DeleteStream (std::shared_ptr stream) + { + if (stream) + { + std::unique_lock l(m_StreamsMutex); + auto it = m_Streams.find (stream->GetRecvStreamID ()); + if (it != m_Streams.end ()) + m_Streams.erase (it); + } + } - void StreamingDestination::HandleDataMessagePayload (const uint8_t * buf, size_t len) - { - // unzip it - CryptoPP::Gunzip decompressor; - decompressor.Put (buf, len); - decompressor.MessageEnd(); - Packet * uncompressed = new Packet; - uncompressed->offset = 0; - uncompressed->len = decompressor.MaxRetrievable (); - if (uncompressed->len <= MAX_PACKET_SIZE) - { - decompressor.Get (uncompressed->buf, uncompressed->len); - HandleNextPacket (uncompressed); - } - else - { - LogPrint ("Received packet size ", uncompressed->len, " exceeds max packet size. Skipped"); - delete uncompressed; - } - } -} -} + void StreamingDestination::HandleDataMessagePayload (const uint8_t * buf, size_t len) + { + // unzip it + CryptoPP::Gunzip decompressor; + decompressor.Put (buf, len); + decompressor.MessageEnd(); + Packet * uncompressed = new Packet; + uncompressed->offset = 0; + uncompressed->len = decompressor.MaxRetrievable (); + if (uncompressed->len <= MAX_PACKET_SIZE) + { + decompressor.Get (uncompressed->buf, uncompressed->len); + HandleNextPacket (uncompressed); + } + else + { + LogPrint ("Received packet size ", uncompressed->len, " exceeds max packet size. Skipped"); + delete uncompressed; + } + } +} +} diff --git a/Streaming.h b/Streaming.h index 7e2aaa71..f100a69f 100644 --- a/Streaming.h +++ b/Streaming.h @@ -22,250 +22,250 @@ namespace i2p { namespace client { - class ClientDestination; + class ClientDestination; } namespace stream { - const uint16_t PACKET_FLAG_SYNCHRONIZE = 0x0001; - const uint16_t PACKET_FLAG_CLOSE = 0x0002; - const uint16_t PACKET_FLAG_RESET = 0x0004; - const uint16_t PACKET_FLAG_SIGNATURE_INCLUDED = 0x0008; - const uint16_t PACKET_FLAG_SIGNATURE_REQUESTED = 0x0010; - const uint16_t PACKET_FLAG_FROM_INCLUDED = 0x0020; - const uint16_t PACKET_FLAG_DELAY_REQUESTED = 0x0040; - const uint16_t PACKET_FLAG_MAX_PACKET_SIZE_INCLUDED = 0x0080; - const uint16_t PACKET_FLAG_PROFILE_INTERACTIVE = 0x0100; - const uint16_t PACKET_FLAG_ECHO = 0x0200; - const uint16_t PACKET_FLAG_NO_ACK = 0x0400; + const uint16_t PACKET_FLAG_SYNCHRONIZE = 0x0001; + const uint16_t PACKET_FLAG_CLOSE = 0x0002; + const uint16_t PACKET_FLAG_RESET = 0x0004; + const uint16_t PACKET_FLAG_SIGNATURE_INCLUDED = 0x0008; + const uint16_t PACKET_FLAG_SIGNATURE_REQUESTED = 0x0010; + const uint16_t PACKET_FLAG_FROM_INCLUDED = 0x0020; + const uint16_t PACKET_FLAG_DELAY_REQUESTED = 0x0040; + const uint16_t PACKET_FLAG_MAX_PACKET_SIZE_INCLUDED = 0x0080; + const uint16_t PACKET_FLAG_PROFILE_INTERACTIVE = 0x0100; + const uint16_t PACKET_FLAG_ECHO = 0x0200; + const uint16_t PACKET_FLAG_NO_ACK = 0x0400; - const size_t STREAMING_MTU = 1730; - const size_t MAX_PACKET_SIZE = 4096; - const size_t COMPRESSION_THRESHOLD_SIZE = 66; - const int ACK_SEND_TIMEOUT = 200; // in milliseconds - const int MAX_NUM_RESEND_ATTEMPTS = 6; - const int WINDOW_SIZE = 6; // in messages - const int MIN_WINDOW_SIZE = 1; - const int MAX_WINDOW_SIZE = 128; - const int INITIAL_RTT = 8000; // in milliseconds - const int INITIAL_RTO = 9000; // in milliseconds - - struct Packet - { - size_t len, offset; - uint8_t buf[MAX_PACKET_SIZE]; - uint64_t sendTime; - - Packet (): len (0), offset (0), sendTime (0) {}; - uint8_t * GetBuffer () { return buf + offset; }; - size_t GetLength () const { return len - offset; }; + const size_t STREAMING_MTU = 1730; + const size_t MAX_PACKET_SIZE = 4096; + const size_t COMPRESSION_THRESHOLD_SIZE = 66; + const int ACK_SEND_TIMEOUT = 200; // in milliseconds + const int MAX_NUM_RESEND_ATTEMPTS = 6; + const int WINDOW_SIZE = 6; // in messages + const int MIN_WINDOW_SIZE = 1; + const int MAX_WINDOW_SIZE = 128; + const int INITIAL_RTT = 8000; // in milliseconds + const int INITIAL_RTO = 9000; // in milliseconds + + struct Packet + { + size_t len, offset; + uint8_t buf[MAX_PACKET_SIZE]; + uint64_t sendTime; + + Packet (): len (0), offset (0), sendTime (0) {}; + uint8_t * GetBuffer () { return buf + offset; }; + size_t GetLength () const { return len - offset; }; - uint32_t GetSendStreamID () const { return bufbe32toh (buf); }; - uint32_t GetReceiveStreamID () const { return bufbe32toh (buf + 4); }; - uint32_t GetSeqn () const { return bufbe32toh (buf + 8); }; - uint32_t GetAckThrough () const { return bufbe32toh (buf + 12); }; - uint8_t GetNACKCount () const { return buf[16]; }; - uint32_t GetNACK (int i) const { return bufbe32toh (buf + 17 + 4 * i); }; - const uint8_t * GetOption () const { return buf + 17 + GetNACKCount ()*4 + 3; }; // 3 = resendDelay + flags - uint16_t GetFlags () const { return bufbe16toh (GetOption () - 2); }; - uint16_t GetOptionSize () const { return bufbe16toh (GetOption ()); }; - const uint8_t * GetOptionData () const { return GetOption () + 2; }; - const uint8_t * GetPayload () const { return GetOptionData () + GetOptionSize (); }; + uint32_t GetSendStreamID () const { return bufbe32toh (buf); }; + uint32_t GetReceiveStreamID () const { return bufbe32toh (buf + 4); }; + uint32_t GetSeqn () const { return bufbe32toh (buf + 8); }; + uint32_t GetAckThrough () const { return bufbe32toh (buf + 12); }; + uint8_t GetNACKCount () const { return buf[16]; }; + uint32_t GetNACK (int i) const { return bufbe32toh (buf + 17 + 4 * i); }; + const uint8_t * GetOption () const { return buf + 17 + GetNACKCount ()*4 + 3; }; // 3 = resendDelay + flags + uint16_t GetFlags () const { return bufbe16toh (GetOption () - 2); }; + uint16_t GetOptionSize () const { return bufbe16toh (GetOption ()); }; + const uint8_t * GetOptionData () const { return GetOption () + 2; }; + const uint8_t * GetPayload () const { return GetOptionData () + GetOptionSize (); }; - bool IsSYN () const { return GetFlags () & PACKET_FLAG_SYNCHRONIZE; }; - bool IsNoAck () const { return GetFlags () & PACKET_FLAG_NO_ACK; }; - }; + bool IsSYN () const { return GetFlags () & PACKET_FLAG_SYNCHRONIZE; }; + bool IsNoAck () const { return GetFlags () & PACKET_FLAG_NO_ACK; }; + }; - struct PacketCmp - { - bool operator() (const Packet * p1, const Packet * p2) const - { - return p1->GetSeqn () < p2->GetSeqn (); - }; - }; + struct PacketCmp + { + bool operator() (const Packet * p1, const Packet * p2) const + { + return p1->GetSeqn () < p2->GetSeqn (); + }; + }; - enum StreamStatus - { - eStreamStatusNew = 0, - eStreamStatusOpen, - eStreamStatusReset, - eStreamStatusClosing, - eStreamStatusClosed - }; - - class StreamingDestination; - class Stream: public std::enable_shared_from_this - { - public: + enum StreamStatus + { + eStreamStatusNew = 0, + eStreamStatusOpen, + eStreamStatusReset, + eStreamStatusClosing, + eStreamStatusClosed + }; + + class StreamingDestination; + class Stream: public std::enable_shared_from_this + { + public: - typedef std::function SendHandler; + typedef std::function SendHandler; - Stream (boost::asio::io_service& service, StreamingDestination& local, - std::shared_ptr remote, int port = 0); // outgoing - Stream (boost::asio::io_service& service, StreamingDestination& local); // incoming + Stream (boost::asio::io_service& service, StreamingDestination& local, + std::shared_ptr remote, int port = 0); // outgoing + Stream (boost::asio::io_service& service, StreamingDestination& local); // incoming - ~Stream (); - uint32_t GetSendStreamID () const { return m_SendStreamID; }; - uint32_t GetRecvStreamID () const { return m_RecvStreamID; }; - std::shared_ptr GetRemoteLeaseSet () const { return m_RemoteLeaseSet; }; - const i2p::data::IdentityEx& GetRemoteIdentity () const { return m_RemoteIdentity; }; - bool IsOpen () const { return m_Status == eStreamStatusOpen; }; - bool IsEstablished () const { return m_SendStreamID; }; - StreamStatus GetStatus () const { return m_Status; }; - StreamingDestination& GetLocalDestination () { return m_LocalDestination; }; - - void HandleNextPacket (Packet * packet); - size_t Send (const uint8_t * buf, size_t len); - void AsyncSend (const uint8_t * buf, size_t len, SendHandler handler); - - template - void AsyncReceive (const Buffer& buffer, ReceiveHandler handler, int timeout = 0); - size_t ReadSome (uint8_t * buf, size_t len) { return ConcatenatePackets (buf, len); }; - - void Close (); - void Cancel () { m_ReceiveTimer.cancel (); }; + ~Stream (); + uint32_t GetSendStreamID () const { return m_SendStreamID; }; + uint32_t GetRecvStreamID () const { return m_RecvStreamID; }; + std::shared_ptr GetRemoteLeaseSet () const { return m_RemoteLeaseSet; }; + const i2p::data::IdentityEx& GetRemoteIdentity () const { return m_RemoteIdentity; }; + bool IsOpen () const { return m_Status == eStreamStatusOpen; }; + bool IsEstablished () const { return m_SendStreamID; }; + StreamStatus GetStatus () const { return m_Status; }; + StreamingDestination& GetLocalDestination () { return m_LocalDestination; }; + + void HandleNextPacket (Packet * packet); + size_t Send (const uint8_t * buf, size_t len); + void AsyncSend (const uint8_t * buf, size_t len, SendHandler handler); + + template + void AsyncReceive (const Buffer& buffer, ReceiveHandler handler, int timeout = 0); + size_t ReadSome (uint8_t * buf, size_t len) { return ConcatenatePackets (buf, len); }; + + void Close (); + void Cancel () { m_ReceiveTimer.cancel (); }; - size_t GetNumSentBytes () const { return m_NumSentBytes; }; - size_t GetNumReceivedBytes () const { return m_NumReceivedBytes; }; - size_t GetSendQueueSize () const { return m_SentPackets.size (); }; - size_t GetReceiveQueueSize () const { return m_ReceiveQueue.size (); }; - size_t GetSendBufferSize () const { return m_SendBuffer.rdbuf ()->in_avail (); }; - int GetWindowSize () const { return m_WindowSize; }; - int GetRTT () const { return m_RTT; }; - - private: + size_t GetNumSentBytes () const { return m_NumSentBytes; }; + size_t GetNumReceivedBytes () const { return m_NumReceivedBytes; }; + size_t GetSendQueueSize () const { return m_SentPackets.size (); }; + size_t GetReceiveQueueSize () const { return m_ReceiveQueue.size (); }; + size_t GetSendBufferSize () const { return m_SendBuffer.rdbuf ()->in_avail (); }; + int GetWindowSize () const { return m_WindowSize; }; + int GetRTT () const { return m_RTT; }; + + private: - void Terminate (); + void Terminate (); - void SendBuffer (); - void SendQuickAck (); - void SendClose (); - bool SendPacket (Packet * packet); - void SendPackets (const std::vector& packets); + void SendBuffer (); + void SendQuickAck (); + void SendClose (); + bool SendPacket (Packet * packet); + void SendPackets (const std::vector& packets); - void SavePacket (Packet * packet); - void ProcessPacket (Packet * packet); - void ProcessAck (Packet * packet); - size_t ConcatenatePackets (uint8_t * buf, size_t len); + void SavePacket (Packet * packet); + void ProcessPacket (Packet * packet); + void ProcessAck (Packet * packet); + size_t ConcatenatePackets (uint8_t * buf, size_t len); - void UpdateCurrentRemoteLease (bool expired = false); - - template - void HandleReceiveTimer (const boost::system::error_code& ecode, const Buffer& buffer, ReceiveHandler handler); - - void ScheduleResend (); - void HandleResendTimer (const boost::system::error_code& ecode); - void HandleAckSendTimer (const boost::system::error_code& ecode); + void UpdateCurrentRemoteLease (bool expired = false); + + template + void HandleReceiveTimer (const boost::system::error_code& ecode, const Buffer& buffer, ReceiveHandler handler); + + void ScheduleResend (); + void HandleResendTimer (const boost::system::error_code& ecode); + void HandleAckSendTimer (const boost::system::error_code& ecode); - std::shared_ptr CreateDataMessage (const uint8_t * payload, size_t len); - - private: + std::shared_ptr CreateDataMessage (const uint8_t * payload, size_t len); + + private: - boost::asio::io_service& m_Service; - uint32_t m_SendStreamID, m_RecvStreamID, m_SequenceNumber; - int32_t m_LastReceivedSequenceNumber; - StreamStatus m_Status; - bool m_IsAckSendScheduled; - StreamingDestination& m_LocalDestination; - i2p::data::IdentityEx m_RemoteIdentity; - std::shared_ptr m_RemoteLeaseSet; - std::shared_ptr m_RoutingSession; - i2p::data::Lease m_CurrentRemoteLease; - std::shared_ptr m_CurrentOutboundTunnel; - std::queue m_ReceiveQueue; - std::set m_SavedPackets; - std::set m_SentPackets; - boost::asio::deadline_timer m_ReceiveTimer, m_ResendTimer, m_AckSendTimer; - size_t m_NumSentBytes, m_NumReceivedBytes; - uint16_t m_Port; + boost::asio::io_service& m_Service; + uint32_t m_SendStreamID, m_RecvStreamID, m_SequenceNumber; + int32_t m_LastReceivedSequenceNumber; + StreamStatus m_Status; + bool m_IsAckSendScheduled; + StreamingDestination& m_LocalDestination; + i2p::data::IdentityEx m_RemoteIdentity; + std::shared_ptr m_RemoteLeaseSet; + std::shared_ptr m_RoutingSession; + i2p::data::Lease m_CurrentRemoteLease; + std::shared_ptr m_CurrentOutboundTunnel; + std::queue m_ReceiveQueue; + std::set m_SavedPackets; + std::set m_SentPackets; + boost::asio::deadline_timer m_ReceiveTimer, m_ResendTimer, m_AckSendTimer; + size_t m_NumSentBytes, m_NumReceivedBytes; + uint16_t m_Port; - std::mutex m_SendBufferMutex; - std::stringstream m_SendBuffer; - int m_WindowSize, m_RTT, m_RTO; - uint64_t m_LastWindowSizeIncreaseTime; - int m_NumResendAttempts; - SendHandler m_SendHandler; - }; + std::mutex m_SendBufferMutex; + std::stringstream m_SendBuffer; + int m_WindowSize, m_RTT, m_RTO; + uint64_t m_LastWindowSizeIncreaseTime; + int m_NumResendAttempts; + SendHandler m_SendHandler; + }; - class StreamingDestination - { - public: + class StreamingDestination + { + public: - typedef std::function)> Acceptor; + typedef std::function)> Acceptor; - StreamingDestination (i2p::client::ClientDestination& owner, uint16_t localPort = 0): - m_Owner (owner), m_LocalPort (localPort) {}; - ~StreamingDestination () {}; + StreamingDestination (i2p::client::ClientDestination& owner, uint16_t localPort = 0): + m_Owner (owner), m_LocalPort (localPort) {}; + ~StreamingDestination () {}; - void Start (); - void Stop (); + void Start (); + void Stop (); - std::shared_ptr CreateNewOutgoingStream (std::shared_ptr remote, int port = 0); - void DeleteStream (std::shared_ptr stream); - void SetAcceptor (const Acceptor& acceptor) { m_Acceptor = acceptor; }; - void ResetAcceptor () { if (m_Acceptor) m_Acceptor (nullptr); m_Acceptor = nullptr; }; - bool IsAcceptorSet () const { return m_Acceptor != nullptr; }; - i2p::client::ClientDestination& GetOwner () { return m_Owner; }; - uint16_t GetLocalPort () const { return m_LocalPort; }; + std::shared_ptr CreateNewOutgoingStream (std::shared_ptr remote, int port = 0); + void DeleteStream (std::shared_ptr stream); + void SetAcceptor (const Acceptor& acceptor) { m_Acceptor = acceptor; }; + void ResetAcceptor () { if (m_Acceptor) m_Acceptor (nullptr); m_Acceptor = nullptr; }; + bool IsAcceptorSet () const { return m_Acceptor != nullptr; }; + i2p::client::ClientDestination& GetOwner () { return m_Owner; }; + uint16_t GetLocalPort () const { return m_LocalPort; }; - void HandleDataMessagePayload (const uint8_t * buf, size_t len); + void HandleDataMessagePayload (const uint8_t * buf, size_t len); - private: - - void HandleNextPacket (Packet * packet); - std::shared_ptr CreateNewIncomingStream (); + private: + + void HandleNextPacket (Packet * packet); + std::shared_ptr CreateNewIncomingStream (); - private: + private: - i2p::client::ClientDestination& m_Owner; - uint16_t m_LocalPort; - std::mutex m_StreamsMutex; - std::map > m_Streams; - Acceptor m_Acceptor; - - public: + i2p::client::ClientDestination& m_Owner; + uint16_t m_LocalPort; + std::mutex m_StreamsMutex; + std::map > m_Streams; + Acceptor m_Acceptor; + + public: - // for HTTP only - const decltype(m_Streams)& GetStreams () const { return m_Streams; }; - }; + // for HTTP only + const decltype(m_Streams)& GetStreams () const { return m_Streams; }; + }; //------------------------------------------------- - template - void Stream::AsyncReceive (const Buffer& buffer, ReceiveHandler handler, int timeout) - { - auto s = shared_from_this(); - m_Service.post ([=](void) - { - if (!m_ReceiveQueue.empty () || m_Status == eStreamStatusReset) - s->HandleReceiveTimer (boost::asio::error::make_error_code (boost::asio::error::operation_aborted), buffer, handler); - else - { - s->m_ReceiveTimer.expires_from_now (boost::posix_time::seconds(timeout)); - s->m_ReceiveTimer.async_wait ([=](const boost::system::error_code& ecode) - { s->HandleReceiveTimer (ecode, buffer, handler); }); - } - }); - } + template + void Stream::AsyncReceive (const Buffer& buffer, ReceiveHandler handler, int timeout) + { + auto s = shared_from_this(); + m_Service.post ([=](void) + { + if (!m_ReceiveQueue.empty () || m_Status == eStreamStatusReset) + s->HandleReceiveTimer (boost::asio::error::make_error_code (boost::asio::error::operation_aborted), buffer, handler); + else + { + s->m_ReceiveTimer.expires_from_now (boost::posix_time::seconds(timeout)); + s->m_ReceiveTimer.async_wait ([=](const boost::system::error_code& ecode) + { s->HandleReceiveTimer (ecode, buffer, handler); }); + } + }); + } - template - void Stream::HandleReceiveTimer (const boost::system::error_code& ecode, const Buffer& buffer, ReceiveHandler handler) - { - size_t received = ConcatenatePackets (boost::asio::buffer_cast(buffer), boost::asio::buffer_size(buffer)); - if (received > 0) - handler (boost::system::error_code (), received); - else if (ecode == boost::asio::error::operation_aborted) - { - // timeout not expired - if (m_Status == eStreamStatusReset) - handler (boost::asio::error::make_error_code (boost::asio::error::connection_reset), 0); - else - handler (boost::asio::error::make_error_code (boost::asio::error::operation_aborted), 0); - } - else - // timeout expired - handler (boost::asio::error::make_error_code (boost::asio::error::timed_out), received); - } -} -} + template + void Stream::HandleReceiveTimer (const boost::system::error_code& ecode, const Buffer& buffer, ReceiveHandler handler) + { + size_t received = ConcatenatePackets (boost::asio::buffer_cast(buffer), boost::asio::buffer_size(buffer)); + if (received > 0) + handler (boost::system::error_code (), received); + else if (ecode == boost::asio::error::operation_aborted) + { + // timeout not expired + if (m_Status == eStreamStatusReset) + handler (boost::asio::error::make_error_code (boost::asio::error::connection_reset), 0); + else + handler (boost::asio::error::make_error_code (boost::asio::error::operation_aborted), 0); + } + else + // timeout expired + handler (boost::asio::error::make_error_code (boost::asio::error::timed_out), received); + } +} +} #endif diff --git a/Timestamp.h b/Timestamp.h index d48cb164..4193f7ef 100644 --- a/Timestamp.h +++ b/Timestamp.h @@ -8,23 +8,23 @@ namespace i2p { namespace util { - inline uint64_t GetMillisecondsSinceEpoch () - { - return std::chrono::duration_cast( - std::chrono::system_clock::now().time_since_epoch()).count (); - } + inline uint64_t GetMillisecondsSinceEpoch () + { + return std::chrono::duration_cast( + std::chrono::system_clock::now().time_since_epoch()).count (); + } - inline uint32_t GetHoursSinceEpoch () - { - return std::chrono::duration_cast( - std::chrono::system_clock::now().time_since_epoch()).count (); - } + inline uint32_t GetHoursSinceEpoch () + { + return std::chrono::duration_cast( + std::chrono::system_clock::now().time_since_epoch()).count (); + } - inline uint64_t GetSecondsSinceEpoch () - { - return std::chrono::duration_cast( - std::chrono::system_clock::now().time_since_epoch()).count (); - } + inline uint64_t GetSecondsSinceEpoch () + { + return std::chrono::duration_cast( + std::chrono::system_clock::now().time_since_epoch()).count (); + } } } diff --git a/TransitTunnel.cpp b/TransitTunnel.cpp index ad6aa0b3..e042ee8a 100644 --- a/TransitTunnel.cpp +++ b/TransitTunnel.cpp @@ -10,102 +10,102 @@ namespace i2p { namespace tunnel -{ - TransitTunnel::TransitTunnel (uint32_t receiveTunnelID, - const uint8_t * nextIdent, uint32_t nextTunnelID, - const uint8_t * layerKey,const uint8_t * ivKey): - m_TunnelID (receiveTunnelID), m_NextTunnelID (nextTunnelID), - m_NextIdent (nextIdent) - { - m_Encryption.SetKeys (layerKey, ivKey); - } +{ + TransitTunnel::TransitTunnel (uint32_t receiveTunnelID, + const uint8_t * nextIdent, uint32_t nextTunnelID, + const uint8_t * layerKey,const uint8_t * ivKey): + m_TunnelID (receiveTunnelID), m_NextTunnelID (nextTunnelID), + m_NextIdent (nextIdent) + { + m_Encryption.SetKeys (layerKey, ivKey); + } - void TransitTunnel::EncryptTunnelMsg (std::shared_ptr in, std::shared_ptr out) - { - m_Encryption.Encrypt (in->GetPayload () + 4, out->GetPayload () + 4); - } + void TransitTunnel::EncryptTunnelMsg (std::shared_ptr in, std::shared_ptr out) + { + m_Encryption.Encrypt (in->GetPayload () + 4, out->GetPayload () + 4); + } - TransitTunnelParticipant::~TransitTunnelParticipant () - { - } - - void TransitTunnelParticipant::HandleTunnelDataMsg (std::shared_ptr tunnelMsg) - { - auto newMsg = CreateEmptyTunnelDataMsg (); - EncryptTunnelMsg (tunnelMsg, newMsg); - - m_NumTransmittedBytes += tunnelMsg->GetLength (); - htobe32buf (newMsg->GetPayload (), GetNextTunnelID ()); - newMsg->FillI2NPMessageHeader (eI2NPTunnelData); - m_TunnelDataMsgs.push_back (newMsg); - } + TransitTunnelParticipant::~TransitTunnelParticipant () + { + } + + void TransitTunnelParticipant::HandleTunnelDataMsg (std::shared_ptr tunnelMsg) + { + auto newMsg = CreateEmptyTunnelDataMsg (); + EncryptTunnelMsg (tunnelMsg, newMsg); + + m_NumTransmittedBytes += tunnelMsg->GetLength (); + htobe32buf (newMsg->GetPayload (), GetNextTunnelID ()); + newMsg->FillI2NPMessageHeader (eI2NPTunnelData); + m_TunnelDataMsgs.push_back (newMsg); + } - void TransitTunnelParticipant::FlushTunnelDataMsgs () - { - if (!m_TunnelDataMsgs.empty ()) - { - auto num = m_TunnelDataMsgs.size (); - if (num > 1) - LogPrint (eLogDebug, "TransitTunnel: ",GetTunnelID (),"->", GetNextTunnelID (), " ", num); - i2p::transport::transports.SendMessages (GetNextIdentHash (), m_TunnelDataMsgs); - m_TunnelDataMsgs.clear (); - } - } - - void TransitTunnel::SendTunnelDataMsg (std::shared_ptr msg) - { - LogPrint (eLogError, "We are not a gateway for transit tunnel ", m_TunnelID); - } + void TransitTunnelParticipant::FlushTunnelDataMsgs () + { + if (!m_TunnelDataMsgs.empty ()) + { + auto num = m_TunnelDataMsgs.size (); + if (num > 1) + LogPrint (eLogDebug, "TransitTunnel: ",GetTunnelID (),"->", GetNextTunnelID (), " ", num); + i2p::transport::transports.SendMessages (GetNextIdentHash (), m_TunnelDataMsgs); + m_TunnelDataMsgs.clear (); + } + } + + void TransitTunnel::SendTunnelDataMsg (std::shared_ptr msg) + { + LogPrint (eLogError, "We are not a gateway for transit tunnel ", m_TunnelID); + } - void TransitTunnel::HandleTunnelDataMsg (std::shared_ptr tunnelMsg) - { - LogPrint (eLogError, "Incoming tunnel message is not supported ", m_TunnelID); - } - - void TransitTunnelGateway::SendTunnelDataMsg (std::shared_ptr msg) - { - TunnelMessageBlock block; - block.deliveryType = eDeliveryTypeLocal; - block.data = msg; - std::unique_lock l(m_SendMutex); - m_Gateway.PutTunnelDataMsg (block); - } + void TransitTunnel::HandleTunnelDataMsg (std::shared_ptr tunnelMsg) + { + LogPrint (eLogError, "Incoming tunnel message is not supported ", m_TunnelID); + } + + void TransitTunnelGateway::SendTunnelDataMsg (std::shared_ptr msg) + { + TunnelMessageBlock block; + block.deliveryType = eDeliveryTypeLocal; + block.data = msg; + std::unique_lock l(m_SendMutex); + m_Gateway.PutTunnelDataMsg (block); + } - void TransitTunnelGateway::FlushTunnelDataMsgs () - { - std::unique_lock l(m_SendMutex); - m_Gateway.SendBuffer (); - } - - void TransitTunnelEndpoint::HandleTunnelDataMsg (std::shared_ptr tunnelMsg) - { - auto newMsg = CreateEmptyTunnelDataMsg (); - EncryptTunnelMsg (tunnelMsg, newMsg); - - LogPrint (eLogDebug, "TransitTunnel endpoint for ", GetTunnelID ()); - m_Endpoint.HandleDecryptedTunnelDataMsg (newMsg); - } - - TransitTunnel * CreateTransitTunnel (uint32_t receiveTunnelID, - const uint8_t * nextIdent, uint32_t nextTunnelID, - const uint8_t * layerKey,const uint8_t * ivKey, - bool isGateway, bool isEndpoint) - { - if (isEndpoint) - { - LogPrint (eLogInfo, "TransitTunnel endpoint: ", receiveTunnelID, " created"); - return new TransitTunnelEndpoint (receiveTunnelID, nextIdent, nextTunnelID, layerKey, ivKey); - } - else if (isGateway) - { - LogPrint (eLogInfo, "TransitTunnel gateway: ", receiveTunnelID, " created"); - return new TransitTunnelGateway (receiveTunnelID, nextIdent, nextTunnelID, layerKey, ivKey); - } - else - { - LogPrint (eLogInfo, "TransitTunnel: ", receiveTunnelID, "->", nextTunnelID, " created"); - return new TransitTunnelParticipant (receiveTunnelID, nextIdent, nextTunnelID, layerKey, ivKey); - } - } + void TransitTunnelGateway::FlushTunnelDataMsgs () + { + std::unique_lock l(m_SendMutex); + m_Gateway.SendBuffer (); + } + + void TransitTunnelEndpoint::HandleTunnelDataMsg (std::shared_ptr tunnelMsg) + { + auto newMsg = CreateEmptyTunnelDataMsg (); + EncryptTunnelMsg (tunnelMsg, newMsg); + + LogPrint (eLogDebug, "TransitTunnel endpoint for ", GetTunnelID ()); + m_Endpoint.HandleDecryptedTunnelDataMsg (newMsg); + } + + TransitTunnel * CreateTransitTunnel (uint32_t receiveTunnelID, + const uint8_t * nextIdent, uint32_t nextTunnelID, + const uint8_t * layerKey,const uint8_t * ivKey, + bool isGateway, bool isEndpoint) + { + if (isEndpoint) + { + LogPrint (eLogInfo, "TransitTunnel endpoint: ", receiveTunnelID, " created"); + return new TransitTunnelEndpoint (receiveTunnelID, nextIdent, nextTunnelID, layerKey, ivKey); + } + else if (isGateway) + { + LogPrint (eLogInfo, "TransitTunnel gateway: ", receiveTunnelID, " created"); + return new TransitTunnelGateway (receiveTunnelID, nextIdent, nextTunnelID, layerKey, ivKey); + } + else + { + LogPrint (eLogInfo, "TransitTunnel: ", receiveTunnelID, "->", nextTunnelID, " created"); + return new TransitTunnelParticipant (receiveTunnelID, nextIdent, nextTunnelID, layerKey, ivKey); + } + } } } diff --git a/TransitTunnel.h b/TransitTunnel.h index 79bb99bb..fea6d4eb 100644 --- a/TransitTunnel.h +++ b/TransitTunnel.h @@ -14,97 +14,97 @@ namespace i2p { namespace tunnel -{ - class TransitTunnel: public TunnelBase - { - public: +{ + class TransitTunnel: public TunnelBase + { + public: - TransitTunnel (uint32_t receiveTunnelID, - const uint8_t * nextIdent, uint32_t nextTunnelID, - const uint8_t * layerKey,const uint8_t * ivKey); - - virtual size_t GetNumTransmittedBytes () const { return 0; }; - - uint32_t GetTunnelID () const { return m_TunnelID; }; + TransitTunnel (uint32_t receiveTunnelID, + const uint8_t * nextIdent, uint32_t nextTunnelID, + const uint8_t * layerKey,const uint8_t * ivKey); + + virtual size_t GetNumTransmittedBytes () const { return 0; }; + + uint32_t GetTunnelID () const { return m_TunnelID; }; - // implements TunnelBase - void SendTunnelDataMsg (std::shared_ptr msg); - void HandleTunnelDataMsg (std::shared_ptr tunnelMsg); - void EncryptTunnelMsg (std::shared_ptr in, std::shared_ptr out); - uint32_t GetNextTunnelID () const { return m_NextTunnelID; }; - const i2p::data::IdentHash& GetNextIdentHash () const { return m_NextIdent; }; - - private: + // implements TunnelBase + void SendTunnelDataMsg (std::shared_ptr msg); + void HandleTunnelDataMsg (std::shared_ptr tunnelMsg); + void EncryptTunnelMsg (std::shared_ptr in, std::shared_ptr out); + uint32_t GetNextTunnelID () const { return m_NextTunnelID; }; + const i2p::data::IdentHash& GetNextIdentHash () const { return m_NextIdent; }; + + private: - uint32_t m_TunnelID, m_NextTunnelID; - i2p::data::IdentHash m_NextIdent; - - i2p::crypto::TunnelEncryption m_Encryption; - }; + uint32_t m_TunnelID, m_NextTunnelID; + i2p::data::IdentHash m_NextIdent; + + i2p::crypto::TunnelEncryption m_Encryption; + }; - class TransitTunnelParticipant: public TransitTunnel - { - public: + class TransitTunnelParticipant: public TransitTunnel + { + public: - TransitTunnelParticipant (uint32_t receiveTunnelID, - const uint8_t * nextIdent, uint32_t nextTunnelID, - const uint8_t * layerKey,const uint8_t * ivKey): - TransitTunnel (receiveTunnelID, nextIdent, nextTunnelID, - layerKey, ivKey), m_NumTransmittedBytes (0) {}; - ~TransitTunnelParticipant (); + TransitTunnelParticipant (uint32_t receiveTunnelID, + const uint8_t * nextIdent, uint32_t nextTunnelID, + const uint8_t * layerKey,const uint8_t * ivKey): + TransitTunnel (receiveTunnelID, nextIdent, nextTunnelID, + layerKey, ivKey), m_NumTransmittedBytes (0) {}; + ~TransitTunnelParticipant (); - size_t GetNumTransmittedBytes () const { return m_NumTransmittedBytes; }; - void HandleTunnelDataMsg (std::shared_ptr tunnelMsg); - void FlushTunnelDataMsgs (); + size_t GetNumTransmittedBytes () const { return m_NumTransmittedBytes; }; + void HandleTunnelDataMsg (std::shared_ptr tunnelMsg); + void FlushTunnelDataMsgs (); - private: + private: - size_t m_NumTransmittedBytes; - std::vector > m_TunnelDataMsgs; - }; - - class TransitTunnelGateway: public TransitTunnel - { - public: + size_t m_NumTransmittedBytes; + std::vector > m_TunnelDataMsgs; + }; + + class TransitTunnelGateway: public TransitTunnel + { + public: - TransitTunnelGateway (uint32_t receiveTunnelID, - const uint8_t * nextIdent, uint32_t nextTunnelID, - const uint8_t * layerKey,const uint8_t * ivKey): - TransitTunnel (receiveTunnelID, nextIdent, nextTunnelID, - layerKey, ivKey), m_Gateway(this) {}; + TransitTunnelGateway (uint32_t receiveTunnelID, + const uint8_t * nextIdent, uint32_t nextTunnelID, + const uint8_t * layerKey,const uint8_t * ivKey): + TransitTunnel (receiveTunnelID, nextIdent, nextTunnelID, + layerKey, ivKey), m_Gateway(this) {}; - void SendTunnelDataMsg (std::shared_ptr msg); - void FlushTunnelDataMsgs (); - size_t GetNumTransmittedBytes () const { return m_Gateway.GetNumSentBytes (); }; - - private: + void SendTunnelDataMsg (std::shared_ptr msg); + void FlushTunnelDataMsgs (); + size_t GetNumTransmittedBytes () const { return m_Gateway.GetNumSentBytes (); }; + + private: - std::mutex m_SendMutex; - TunnelGateway m_Gateway; - }; + std::mutex m_SendMutex; + TunnelGateway m_Gateway; + }; - class TransitTunnelEndpoint: public TransitTunnel - { - public: + class TransitTunnelEndpoint: public TransitTunnel + { + public: - TransitTunnelEndpoint (uint32_t receiveTunnelID, - const uint8_t * nextIdent, uint32_t nextTunnelID, - const uint8_t * layerKey,const uint8_t * ivKey): - TransitTunnel (receiveTunnelID, nextIdent, nextTunnelID, layerKey, ivKey), - m_Endpoint (false) {}; // transit endpoint is always outbound + TransitTunnelEndpoint (uint32_t receiveTunnelID, + const uint8_t * nextIdent, uint32_t nextTunnelID, + const uint8_t * layerKey,const uint8_t * ivKey): + TransitTunnel (receiveTunnelID, nextIdent, nextTunnelID, layerKey, ivKey), + m_Endpoint (false) {}; // transit endpoint is always outbound - void HandleTunnelDataMsg (std::shared_ptr tunnelMsg); - size_t GetNumTransmittedBytes () const { return m_Endpoint.GetNumReceivedBytes (); } - - private: + void HandleTunnelDataMsg (std::shared_ptr tunnelMsg); + size_t GetNumTransmittedBytes () const { return m_Endpoint.GetNumReceivedBytes (); } + + private: - TunnelEndpoint m_Endpoint; - }; - - TransitTunnel * CreateTransitTunnel (uint32_t receiveTunnelID, - const uint8_t * nextIdent, uint32_t nextTunnelID, - const uint8_t * layerKey,const uint8_t * ivKey, - bool isGateway, bool isEndpoint); + TunnelEndpoint m_Endpoint; + }; + + TransitTunnel * CreateTransitTunnel (uint32_t receiveTunnelID, + const uint8_t * nextIdent, uint32_t nextTunnelID, + const uint8_t * layerKey,const uint8_t * ivKey, + bool isGateway, bool isEndpoint); } } diff --git a/TransportSession.h b/TransportSession.h index f8da2b4d..c50f22f5 100644 --- a/TransportSession.h +++ b/TransportSession.h @@ -13,73 +13,73 @@ namespace i2p { namespace transport { - struct DHKeysPair // transient keys for transport sessions - { - uint8_t publicKey[256]; - uint8_t privateKey[256]; - }; + struct DHKeysPair // transient keys for transport sessions + { + uint8_t publicKey[256]; + uint8_t privateKey[256]; + }; - class SignedData - { - public: + class SignedData + { + public: - SignedData () {}; - void Insert (const uint8_t * buf, size_t len) - { - m_Stream.write ((char *)buf, len); - } + SignedData () {}; + void Insert (const uint8_t * buf, size_t len) + { + m_Stream.write ((char *)buf, len); + } - template - void Insert (T t) - { - m_Stream.write ((char *)&t, sizeof (T)); - } + template + void Insert (T t) + { + m_Stream.write ((char *)&t, sizeof (T)); + } - bool Verify (const i2p::data::IdentityEx& ident, const uint8_t * signature) const - { - return ident.Verify ((const uint8_t *)m_Stream.str ().c_str (), m_Stream.str ().size (), signature); - } + bool Verify (const i2p::data::IdentityEx& ident, const uint8_t * signature) const + { + return ident.Verify ((const uint8_t *)m_Stream.str ().c_str (), m_Stream.str ().size (), signature); + } - void Sign (const i2p::data::PrivateKeys& keys, uint8_t * signature) const - { - keys.Sign ((const uint8_t *)m_Stream.str ().c_str (), m_Stream.str ().size (), signature); - } + void Sign (const i2p::data::PrivateKeys& keys, uint8_t * signature) const + { + keys.Sign ((const uint8_t *)m_Stream.str ().c_str (), m_Stream.str ().size (), signature); + } - private: - - std::stringstream m_Stream; - }; + private: + + std::stringstream m_Stream; + }; - class TransportSession - { - public: + class TransportSession + { + public: - TransportSession (std::shared_ptr in_RemoteRouter): - m_RemoteRouter (in_RemoteRouter), m_DHKeysPair (nullptr), - m_NumSentBytes (0), m_NumReceivedBytes (0) - { - if (m_RemoteRouter) - m_RemoteIdentity = m_RemoteRouter->GetRouterIdentity (); - } + TransportSession (std::shared_ptr in_RemoteRouter): + m_RemoteRouter (in_RemoteRouter), m_DHKeysPair (nullptr), + m_NumSentBytes (0), m_NumReceivedBytes (0) + { + if (m_RemoteRouter) + m_RemoteIdentity = m_RemoteRouter->GetRouterIdentity (); + } - virtual ~TransportSession () { delete m_DHKeysPair; }; - virtual void Done () = 0; - - std::shared_ptr GetRemoteRouter () { return m_RemoteRouter; }; - const i2p::data::IdentityEx& GetRemoteIdentity () { return m_RemoteIdentity; }; + virtual ~TransportSession () { delete m_DHKeysPair; }; + virtual void Done () = 0; + + std::shared_ptr GetRemoteRouter () { return m_RemoteRouter; }; + const i2p::data::IdentityEx& GetRemoteIdentity () { return m_RemoteIdentity; }; - size_t GetNumSentBytes () const { return m_NumSentBytes; }; - size_t GetNumReceivedBytes () const { return m_NumReceivedBytes; }; - - virtual void SendI2NPMessages (const std::vector >& msgs) = 0; - - protected: + size_t GetNumSentBytes () const { return m_NumSentBytes; }; + size_t GetNumReceivedBytes () const { return m_NumReceivedBytes; }; + + virtual void SendI2NPMessages (const std::vector >& msgs) = 0; + + protected: - std::shared_ptr m_RemoteRouter; - i2p::data::IdentityEx m_RemoteIdentity; - DHKeysPair * m_DHKeysPair; // X - for client and Y - for server - size_t m_NumSentBytes, m_NumReceivedBytes; - }; + std::shared_ptr m_RemoteRouter; + i2p::data::IdentityEx m_RemoteIdentity; + DHKeysPair * m_DHKeysPair; // X - for client and Y - for server + size_t m_NumSentBytes, m_NumReceivedBytes; + }; } } diff --git a/Transports.cpp b/Transports.cpp index da07dc1d..5f2d2764 100644 --- a/Transports.cpp +++ b/Transports.cpp @@ -12,490 +12,490 @@ namespace i2p { namespace transport { - DHKeysPairSupplier::DHKeysPairSupplier (int size): - m_QueueSize (size), m_IsRunning (false), m_Thread (nullptr) - { - } + DHKeysPairSupplier::DHKeysPairSupplier (int size): + m_QueueSize (size), m_IsRunning (false), m_Thread (nullptr) + { + } - DHKeysPairSupplier::~DHKeysPairSupplier () - { - Stop (); - } + DHKeysPairSupplier::~DHKeysPairSupplier () + { + Stop (); + } - void DHKeysPairSupplier::Start () - { - m_IsRunning = true; - m_Thread = new std::thread (std::bind (&DHKeysPairSupplier::Run, this)); - } + void DHKeysPairSupplier::Start () + { + m_IsRunning = true; + m_Thread = new std::thread (std::bind (&DHKeysPairSupplier::Run, this)); + } - void DHKeysPairSupplier::Stop () - { - m_IsRunning = false; - m_Acquired.notify_one (); - if (m_Thread) - { - m_Thread->join (); - delete m_Thread; - m_Thread = 0; - } - } + void DHKeysPairSupplier::Stop () + { + m_IsRunning = false; + m_Acquired.notify_one (); + if (m_Thread) + { + m_Thread->join (); + delete m_Thread; + m_Thread = 0; + } + } - void DHKeysPairSupplier::Run () - { - while (m_IsRunning) - { - int num; - while ((num = m_QueueSize - m_Queue.size ()) > 0) - CreateDHKeysPairs (num); - std::unique_lock l(m_AcquiredMutex); - m_Acquired.wait (l); // wait for element gets aquired - } - } + void DHKeysPairSupplier::Run () + { + while (m_IsRunning) + { + int num; + while ((num = m_QueueSize - m_Queue.size ()) > 0) + CreateDHKeysPairs (num); + std::unique_lock l(m_AcquiredMutex); + m_Acquired.wait (l); // wait for element gets aquired + } + } - void DHKeysPairSupplier::CreateDHKeysPairs (int num) - { - if (num > 0) - { - CryptoPP::DH dh (i2p::crypto::elgp, i2p::crypto::elgg); - for (int i = 0; i < num; i++) - { - i2p::transport::DHKeysPair * pair = new i2p::transport::DHKeysPair (); - dh.GenerateKeyPair(m_Rnd, pair->privateKey, pair->publicKey); - std::unique_lock l(m_AcquiredMutex); - m_Queue.push (pair); - } - } - } + void DHKeysPairSupplier::CreateDHKeysPairs (int num) + { + if (num > 0) + { + CryptoPP::DH dh (i2p::crypto::elgp, i2p::crypto::elgg); + for (int i = 0; i < num; i++) + { + i2p::transport::DHKeysPair * pair = new i2p::transport::DHKeysPair (); + dh.GenerateKeyPair(m_Rnd, pair->privateKey, pair->publicKey); + std::unique_lock l(m_AcquiredMutex); + m_Queue.push (pair); + } + } + } - DHKeysPair * DHKeysPairSupplier::Acquire () - { - if (!m_Queue.empty ()) - { - std::unique_lock l(m_AcquiredMutex); - auto pair = m_Queue.front (); - m_Queue.pop (); - m_Acquired.notify_one (); - return pair; - } - else // queue is empty, create new - { - DHKeysPair * pair = new DHKeysPair (); - CryptoPP::DH dh (i2p::crypto::elgp, i2p::crypto::elgg); - dh.GenerateKeyPair(m_Rnd, pair->privateKey, pair->publicKey); - return pair; - } - } + DHKeysPair * DHKeysPairSupplier::Acquire () + { + if (!m_Queue.empty ()) + { + std::unique_lock l(m_AcquiredMutex); + auto pair = m_Queue.front (); + m_Queue.pop (); + m_Acquired.notify_one (); + return pair; + } + else // queue is empty, create new + { + DHKeysPair * pair = new DHKeysPair (); + CryptoPP::DH dh (i2p::crypto::elgp, i2p::crypto::elgg); + dh.GenerateKeyPair(m_Rnd, pair->privateKey, pair->publicKey); + return pair; + } + } - void DHKeysPairSupplier::Return (DHKeysPair * pair) - { - std::unique_lock l(m_AcquiredMutex); - m_Queue.push (pair); - } + void DHKeysPairSupplier::Return (DHKeysPair * pair) + { + std::unique_lock l(m_AcquiredMutex); + m_Queue.push (pair); + } - Transports transports; - - Transports::Transports (): - m_IsRunning (false), m_Thread (nullptr), m_Work (m_Service), m_PeerCleanupTimer (m_Service), - m_NTCPServer (nullptr), m_SSUServer (nullptr), m_DHKeysPairSupplier (5), // 5 pre-generated keys - m_TotalSentBytes(0), m_TotalReceivedBytes(0), m_InBandwidth (0), m_OutBandwidth (0), - m_LastInBandwidthUpdateBytes (0), m_LastOutBandwidthUpdateBytes (0), m_LastBandwidthUpdateTime (0) - { - } - - Transports::~Transports () - { - Stop (); - } + Transports transports; + + Transports::Transports (): + m_IsRunning (false), m_Thread (nullptr), m_Work (m_Service), m_PeerCleanupTimer (m_Service), + m_NTCPServer (nullptr), m_SSUServer (nullptr), m_DHKeysPairSupplier (5), // 5 pre-generated keys + m_TotalSentBytes(0), m_TotalReceivedBytes(0), m_InBandwidth (0), m_OutBandwidth (0), + m_LastInBandwidthUpdateBytes (0), m_LastOutBandwidthUpdateBytes (0), m_LastBandwidthUpdateTime (0) + { + } + + Transports::~Transports () + { + Stop (); + } - void Transports::Start () - { + void Transports::Start () + { #ifdef USE_UPNP - m_UPnP.Start (); - LogPrint(eLogInfo, "UPnP started"); + m_UPnP.Start (); + LogPrint(eLogInfo, "UPnP started"); #endif - m_DHKeysPairSupplier.Start (); - m_IsRunning = true; - m_Thread = new std::thread (std::bind (&Transports::Run, this)); - // create acceptors - auto addresses = context.GetRouterInfo ().GetAddresses (); - for (auto& address : addresses) - { - if (!m_NTCPServer) - { - m_NTCPServer = new NTCPServer (address.port); - m_NTCPServer->Start (); - } - - if (address.transportStyle == RouterInfo::eTransportSSU && address.host.is_v4 ()) - { - if (!m_SSUServer) - { - m_SSUServer = new SSUServer (address.port); - LogPrint ("Start listening UDP port ", address.port); - m_SSUServer->Start (); - DetectExternalIP (); - } - else - LogPrint ("SSU server already exists"); - } - } - m_PeerCleanupTimer.expires_from_now (boost::posix_time::seconds(5*SESSION_CREATION_TIMEOUT)); - m_PeerCleanupTimer.async_wait (std::bind (&Transports::HandlePeerCleanupTimer, this, std::placeholders::_1)); - } - - void Transports::Stop () - { + m_DHKeysPairSupplier.Start (); + m_IsRunning = true; + m_Thread = new std::thread (std::bind (&Transports::Run, this)); + // create acceptors + auto addresses = context.GetRouterInfo ().GetAddresses (); + for (auto& address : addresses) + { + if (!m_NTCPServer) + { + m_NTCPServer = new NTCPServer (address.port); + m_NTCPServer->Start (); + } + + if (address.transportStyle == RouterInfo::eTransportSSU && address.host.is_v4 ()) + { + if (!m_SSUServer) + { + m_SSUServer = new SSUServer (address.port); + LogPrint ("Start listening UDP port ", address.port); + m_SSUServer->Start (); + DetectExternalIP (); + } + else + LogPrint ("SSU server already exists"); + } + } + m_PeerCleanupTimer.expires_from_now (boost::posix_time::seconds(5*SESSION_CREATION_TIMEOUT)); + m_PeerCleanupTimer.async_wait (std::bind (&Transports::HandlePeerCleanupTimer, this, std::placeholders::_1)); + } + + void Transports::Stop () + { #ifdef USE_UPNP - m_UPnP.Stop (); - LogPrint(eLogInfo, "UPnP stopped"); + m_UPnP.Stop (); + LogPrint(eLogInfo, "UPnP stopped"); #endif - m_PeerCleanupTimer.cancel (); - m_Peers.clear (); - if (m_SSUServer) - { - m_SSUServer->Stop (); - delete m_SSUServer; - m_SSUServer = nullptr; - } - if (m_NTCPServer) - { - m_NTCPServer->Stop (); - delete m_NTCPServer; - m_NTCPServer = nullptr; - } + m_PeerCleanupTimer.cancel (); + m_Peers.clear (); + if (m_SSUServer) + { + m_SSUServer->Stop (); + delete m_SSUServer; + m_SSUServer = nullptr; + } + if (m_NTCPServer) + { + m_NTCPServer->Stop (); + delete m_NTCPServer; + m_NTCPServer = nullptr; + } - m_DHKeysPairSupplier.Stop (); - m_IsRunning = false; - m_Service.stop (); - if (m_Thread) - { - m_Thread->join (); - delete m_Thread; - m_Thread = nullptr; - } - } + m_DHKeysPairSupplier.Stop (); + m_IsRunning = false; + m_Service.stop (); + if (m_Thread) + { + m_Thread->join (); + delete m_Thread; + m_Thread = nullptr; + } + } - void Transports::Run () - { - while (m_IsRunning) - { - try - { - m_Service.run (); - } - catch (std::exception& ex) - { - LogPrint ("Transports: ", ex.what ()); - } - } - } - - void Transports::UpdateBandwidth () - { - uint64_t ts = i2p::util::GetMillisecondsSinceEpoch (); - if (m_LastBandwidthUpdateTime > 0) - { - auto delta = ts - m_LastBandwidthUpdateTime; - if (delta > 0) - { - m_InBandwidth = (m_TotalReceivedBytes - m_LastInBandwidthUpdateBytes)*1000/delta; // per second - m_OutBandwidth = (m_TotalSentBytes - m_LastOutBandwidthUpdateBytes)*1000/delta; // per second - } - } - m_LastBandwidthUpdateTime = ts; - m_LastInBandwidthUpdateBytes = m_TotalReceivedBytes; - m_LastOutBandwidthUpdateBytes = m_TotalSentBytes; - } + void Transports::Run () + { + while (m_IsRunning) + { + try + { + m_Service.run (); + } + catch (std::exception& ex) + { + LogPrint ("Transports: ", ex.what ()); + } + } + } + + void Transports::UpdateBandwidth () + { + uint64_t ts = i2p::util::GetMillisecondsSinceEpoch (); + if (m_LastBandwidthUpdateTime > 0) + { + auto delta = ts - m_LastBandwidthUpdateTime; + if (delta > 0) + { + m_InBandwidth = (m_TotalReceivedBytes - m_LastInBandwidthUpdateBytes)*1000/delta; // per second + m_OutBandwidth = (m_TotalSentBytes - m_LastOutBandwidthUpdateBytes)*1000/delta; // per second + } + } + m_LastBandwidthUpdateTime = ts; + m_LastInBandwidthUpdateBytes = m_TotalReceivedBytes; + m_LastOutBandwidthUpdateBytes = m_TotalSentBytes; + } - bool Transports::IsBandwidthExceeded () const - { - if (i2p::context.GetRouterInfo ().IsHighBandwidth ()) return false; - return std::max (m_InBandwidth, m_OutBandwidth) > LOW_BANDWIDTH_LIMIT; - } + bool Transports::IsBandwidthExceeded () const + { + if (i2p::context.GetRouterInfo ().IsHighBandwidth ()) return false; + return std::max (m_InBandwidth, m_OutBandwidth) > LOW_BANDWIDTH_LIMIT; + } - void Transports::SendMessage (const i2p::data::IdentHash& ident, std::shared_ptr msg) - { - SendMessages (ident, std::vector > {msg }); - } + void Transports::SendMessage (const i2p::data::IdentHash& ident, std::shared_ptr msg) + { + SendMessages (ident, std::vector > {msg }); + } - void Transports::SendMessages (const i2p::data::IdentHash& ident, const std::vector >& msgs) - { - m_Service.post (std::bind (&Transports::PostMessages, this, ident, msgs)); - } + void Transports::SendMessages (const i2p::data::IdentHash& ident, const std::vector >& msgs) + { + m_Service.post (std::bind (&Transports::PostMessages, this, ident, msgs)); + } - void Transports::PostMessages (i2p::data::IdentHash ident, std::vector > msgs) - { - if (ident == i2p::context.GetRouterInfo ().GetIdentHash ()) - { - // we send it to ourself - for (auto it: msgs) - i2p::HandleI2NPMessage (it); - return; - } - auto it = m_Peers.find (ident); - if (it == m_Peers.end ()) - { - bool connected = false; - try - { - auto r = netdb.FindRouter (ident); - it = m_Peers.insert (std::pair(ident, { 0, r, {}, - i2p::util::GetSecondsSinceEpoch () })).first; - connected = ConnectToPeer (ident, it->second); - } - catch (std::exception& ex) - { - LogPrint (eLogError, "Transports::PostMessages ", ex.what ()); - } - if (!connected) return; - } - if (!it->second.sessions.empty ()) - it->second.sessions.front ()->SendI2NPMessages (msgs); - else - { - for (auto it1: msgs) - it->second.delayedMessages.push_back (it1); - } - } - - bool Transports::ConnectToPeer (const i2p::data::IdentHash& ident, Peer& peer) - { - if (peer.router) // we have RI already - { - if (!peer.numAttempts) // NTCP - { - peer.numAttempts++; - auto address = peer.router->GetNTCPAddress (!context.SupportsV6 ()); - if (address) - { + void Transports::PostMessages (i2p::data::IdentHash ident, std::vector > msgs) + { + if (ident == i2p::context.GetRouterInfo ().GetIdentHash ()) + { + // we send it to ourself + for (auto it: msgs) + i2p::HandleI2NPMessage (it); + return; + } + auto it = m_Peers.find (ident); + if (it == m_Peers.end ()) + { + bool connected = false; + try + { + auto r = netdb.FindRouter (ident); + it = m_Peers.insert (std::pair(ident, { 0, r, {}, + i2p::util::GetSecondsSinceEpoch () })).first; + connected = ConnectToPeer (ident, it->second); + } + catch (std::exception& ex) + { + LogPrint (eLogError, "Transports::PostMessages ", ex.what ()); + } + if (!connected) return; + } + if (!it->second.sessions.empty ()) + it->second.sessions.front ()->SendI2NPMessages (msgs); + else + { + for (auto it1: msgs) + it->second.delayedMessages.push_back (it1); + } + } + + bool Transports::ConnectToPeer (const i2p::data::IdentHash& ident, Peer& peer) + { + if (peer.router) // we have RI already + { + if (!peer.numAttempts) // NTCP + { + peer.numAttempts++; + auto address = peer.router->GetNTCPAddress (!context.SupportsV6 ()); + if (address) + { #if BOOST_VERSION >= 104900 - if (!address->host.is_unspecified ()) // we have address now + if (!address->host.is_unspecified ()) // we have address now #else - boost::system::error_code ecode; - address->host.to_string (ecode); - if (!ecode) + boost::system::error_code ecode; + address->host.to_string (ecode); + if (!ecode) #endif - { - if (!peer.router->UsesIntroducer () && !peer.router->IsUnreachable ()) - { - auto s = std::make_shared (*m_NTCPServer, peer.router); - m_NTCPServer->Connect (address->host, address->port, s); - return true; - } - } - else // we don't have address - { - if (address->addressString.length () > 0) // trying to resolve - { - LogPrint (eLogInfo, "Resolving ", address->addressString); - NTCPResolve (address->addressString, ident); - return true; - } - } - } - } - else if (peer.numAttempts == 1)// SSU - { - peer.numAttempts++; - if (m_SSUServer) - { - if (m_SSUServer->GetSession (peer.router)) - return true; - } - } - LogPrint (eLogError, "No NTCP and SSU addresses available"); - peer.Done (); - m_Peers.erase (ident); - return false; - } - else // otherwise request RI - { - LogPrint ("Router not found. Requested"); - i2p::data::netdb.RequestDestination (ident, std::bind ( - &Transports::RequestComplete, this, std::placeholders::_1, ident)); - } - return true; - } - - void Transports::RequestComplete (std::shared_ptr r, const i2p::data::IdentHash& ident) - { - m_Service.post (std::bind (&Transports::HandleRequestComplete, this, r, ident)); - } - - void Transports::HandleRequestComplete (std::shared_ptr r, const i2p::data::IdentHash& ident) - { - auto it = m_Peers.find (ident); - if (it != m_Peers.end ()) - { - if (r) - { - LogPrint ("Router found. Trying to connect"); - it->second.router = r; - ConnectToPeer (ident, it->second); - } - else - { - LogPrint ("Router not found. Failed to send messages"); - m_Peers.erase (it); - } - } - } + { + if (!peer.router->UsesIntroducer () && !peer.router->IsUnreachable ()) + { + auto s = std::make_shared (*m_NTCPServer, peer.router); + m_NTCPServer->Connect (address->host, address->port, s); + return true; + } + } + else // we don't have address + { + if (address->addressString.length () > 0) // trying to resolve + { + LogPrint (eLogInfo, "Resolving ", address->addressString); + NTCPResolve (address->addressString, ident); + return true; + } + } + } + } + else if (peer.numAttempts == 1)// SSU + { + peer.numAttempts++; + if (m_SSUServer) + { + if (m_SSUServer->GetSession (peer.router)) + return true; + } + } + LogPrint (eLogError, "No NTCP and SSU addresses available"); + peer.Done (); + m_Peers.erase (ident); + return false; + } + else // otherwise request RI + { + LogPrint ("Router not found. Requested"); + i2p::data::netdb.RequestDestination (ident, std::bind ( + &Transports::RequestComplete, this, std::placeholders::_1, ident)); + } + return true; + } + + void Transports::RequestComplete (std::shared_ptr r, const i2p::data::IdentHash& ident) + { + m_Service.post (std::bind (&Transports::HandleRequestComplete, this, r, ident)); + } + + void Transports::HandleRequestComplete (std::shared_ptr r, const i2p::data::IdentHash& ident) + { + auto it = m_Peers.find (ident); + if (it != m_Peers.end ()) + { + if (r) + { + LogPrint ("Router found. Trying to connect"); + it->second.router = r; + ConnectToPeer (ident, it->second); + } + else + { + LogPrint ("Router not found. Failed to send messages"); + m_Peers.erase (it); + } + } + } - void Transports::NTCPResolve (const std::string& addr, const i2p::data::IdentHash& ident) - { - auto resolver = std::make_shared(m_Service); - resolver->async_resolve (boost::asio::ip::tcp::resolver::query (addr, ""), - std::bind (&Transports::HandleNTCPResolve, this, - std::placeholders::_1, std::placeholders::_2, ident, resolver)); - } + void Transports::NTCPResolve (const std::string& addr, const i2p::data::IdentHash& ident) + { + auto resolver = std::make_shared(m_Service); + resolver->async_resolve (boost::asio::ip::tcp::resolver::query (addr, ""), + std::bind (&Transports::HandleNTCPResolve, this, + std::placeholders::_1, std::placeholders::_2, ident, resolver)); + } - void Transports::HandleNTCPResolve (const boost::system::error_code& ecode, boost::asio::ip::tcp::resolver::iterator it, - i2p::data::IdentHash ident, std::shared_ptr resolver) - { - auto it1 = m_Peers.find (ident); - if (it1 != m_Peers.end ()) - { - auto& peer = it1->second; - if (!ecode && peer.router) - { - auto address = (*it).endpoint ().address (); - LogPrint (eLogInfo, (*it).host_name (), " has been resolved to ", address); - auto addr = peer.router->GetNTCPAddress (); - if (addr) - { - auto s = std::make_shared (*m_NTCPServer, peer.router); - m_NTCPServer->Connect (address, addr->port, s); - return; - } - } - LogPrint (eLogError, "Unable to resolve NTCP address: ", ecode.message ()); - m_Peers.erase (it1); - } - } + void Transports::HandleNTCPResolve (const boost::system::error_code& ecode, boost::asio::ip::tcp::resolver::iterator it, + i2p::data::IdentHash ident, std::shared_ptr resolver) + { + auto it1 = m_Peers.find (ident); + if (it1 != m_Peers.end ()) + { + auto& peer = it1->second; + if (!ecode && peer.router) + { + auto address = (*it).endpoint ().address (); + LogPrint (eLogInfo, (*it).host_name (), " has been resolved to ", address); + auto addr = peer.router->GetNTCPAddress (); + if (addr) + { + auto s = std::make_shared (*m_NTCPServer, peer.router); + m_NTCPServer->Connect (address, addr->port, s); + return; + } + } + LogPrint (eLogError, "Unable to resolve NTCP address: ", ecode.message ()); + m_Peers.erase (it1); + } + } - void Transports::CloseSession (std::shared_ptr router) - { - if (!router) return; - m_Service.post (std::bind (&Transports::PostCloseSession, this, router)); - } + void Transports::CloseSession (std::shared_ptr router) + { + if (!router) return; + m_Service.post (std::bind (&Transports::PostCloseSession, this, router)); + } - void Transports::PostCloseSession (std::shared_ptr router) - { - auto ssuSession = m_SSUServer ? m_SSUServer->FindSession (router) : nullptr; - if (ssuSession) // try SSU first - { - m_SSUServer->DeleteSession (ssuSession); - LogPrint ("SSU session closed"); - } - // TODO: delete NTCP - } - - void Transports::DetectExternalIP () - { - if (m_SSUServer) - { - i2p::context.SetStatus (eRouterStatusTesting); - for (int i = 0; i < 5; i++) - { - auto router = i2p::data::netdb.GetRandomPeerTestRouter (); - if (router && router->IsSSU ()) - m_SSUServer->GetSession (router, true); // peer test - else - { - // if not peer test capable routers found pick any - router = i2p::data::netdb.GetRandomRouter (); - if (router && router->IsSSU ()) - m_SSUServer->GetSession (router); // no peer test - } - } - } - else - LogPrint (eLogError, "Can't detect external IP. SSU is not available"); - } - - DHKeysPair * Transports::GetNextDHKeysPair () - { - return m_DHKeysPairSupplier.Acquire (); - } + void Transports::PostCloseSession (std::shared_ptr router) + { + auto ssuSession = m_SSUServer ? m_SSUServer->FindSession (router) : nullptr; + if (ssuSession) // try SSU first + { + m_SSUServer->DeleteSession (ssuSession); + LogPrint ("SSU session closed"); + } + // TODO: delete NTCP + } + + void Transports::DetectExternalIP () + { + if (m_SSUServer) + { + i2p::context.SetStatus (eRouterStatusTesting); + for (int i = 0; i < 5; i++) + { + auto router = i2p::data::netdb.GetRandomPeerTestRouter (); + if (router && router->IsSSU ()) + m_SSUServer->GetSession (router, true); // peer test + else + { + // if not peer test capable routers found pick any + router = i2p::data::netdb.GetRandomRouter (); + if (router && router->IsSSU ()) + m_SSUServer->GetSession (router); // no peer test + } + } + } + else + LogPrint (eLogError, "Can't detect external IP. SSU is not available"); + } + + DHKeysPair * Transports::GetNextDHKeysPair () + { + return m_DHKeysPairSupplier.Acquire (); + } - void Transports::ReuseDHKeysPair (DHKeysPair * pair) - { - m_DHKeysPairSupplier.Return (pair); - } + void Transports::ReuseDHKeysPair (DHKeysPair * pair) + { + m_DHKeysPairSupplier.Return (pair); + } - void Transports::PeerConnected (std::shared_ptr session) - { - m_Service.post([session, this]() - { - auto ident = session->GetRemoteIdentity ().GetIdentHash (); - auto it = m_Peers.find (ident); - if (it != m_Peers.end ()) - { - it->second.sessions.push_back (session); - session->SendI2NPMessages (it->second.delayedMessages); - it->second.delayedMessages.clear (); - } - else // incoming connection - m_Peers.insert (std::make_pair (ident, Peer{ 0, nullptr, { session }, i2p::util::GetSecondsSinceEpoch () })); - }); - } - - void Transports::PeerDisconnected (std::shared_ptr session) - { - m_Service.post([session, this]() - { - auto ident = session->GetRemoteIdentity ().GetIdentHash (); - auto it = m_Peers.find (ident); - if (it != m_Peers.end ()) - { - it->second.sessions.remove (session); - if (it->second.sessions.empty ()) // TODO: why? - { - if (it->second.delayedMessages.size () > 0) - ConnectToPeer (ident, it->second); - else - m_Peers.erase (it); - } - } - }); - } + void Transports::PeerConnected (std::shared_ptr session) + { + m_Service.post([session, this]() + { + auto ident = session->GetRemoteIdentity ().GetIdentHash (); + auto it = m_Peers.find (ident); + if (it != m_Peers.end ()) + { + it->second.sessions.push_back (session); + session->SendI2NPMessages (it->second.delayedMessages); + it->second.delayedMessages.clear (); + } + else // incoming connection + m_Peers.insert (std::make_pair (ident, Peer{ 0, nullptr, { session }, i2p::util::GetSecondsSinceEpoch () })); + }); + } + + void Transports::PeerDisconnected (std::shared_ptr session) + { + m_Service.post([session, this]() + { + auto ident = session->GetRemoteIdentity ().GetIdentHash (); + auto it = m_Peers.find (ident); + if (it != m_Peers.end ()) + { + it->second.sessions.remove (session); + if (it->second.sessions.empty ()) // TODO: why? + { + if (it->second.delayedMessages.size () > 0) + ConnectToPeer (ident, it->second); + else + m_Peers.erase (it); + } + } + }); + } - bool Transports::IsConnected (const i2p::data::IdentHash& ident) const - { - auto it = m_Peers.find (ident); - return it != m_Peers.end (); - } - - void Transports::HandlePeerCleanupTimer (const boost::system::error_code& ecode) - { - if (ecode != boost::asio::error::operation_aborted) - { - auto ts = i2p::util::GetSecondsSinceEpoch (); - for (auto it = m_Peers.begin (); it != m_Peers.end (); ) - { - if (it->second.sessions.empty () && ts > it->second.creationTime + SESSION_CREATION_TIMEOUT) - { - LogPrint (eLogError, "Session to peer ", it->first.ToBase64 (), " has not been created in ", SESSION_CREATION_TIMEOUT, " seconds"); - it = m_Peers.erase (it); - } - else - it++; - } - UpdateBandwidth (); // TODO: use separate timer(s) for it - if (i2p::context.GetStatus () == eRouterStatusTesting) // if still testing, repeat peer test - DetectExternalIP (); - m_PeerCleanupTimer.expires_from_now (boost::posix_time::seconds(5*SESSION_CREATION_TIMEOUT)); - m_PeerCleanupTimer.async_wait (std::bind (&Transports::HandlePeerCleanupTimer, this, std::placeholders::_1)); - } - } + bool Transports::IsConnected (const i2p::data::IdentHash& ident) const + { + auto it = m_Peers.find (ident); + return it != m_Peers.end (); + } + + void Transports::HandlePeerCleanupTimer (const boost::system::error_code& ecode) + { + if (ecode != boost::asio::error::operation_aborted) + { + auto ts = i2p::util::GetSecondsSinceEpoch (); + for (auto it = m_Peers.begin (); it != m_Peers.end (); ) + { + if (it->second.sessions.empty () && ts > it->second.creationTime + SESSION_CREATION_TIMEOUT) + { + LogPrint (eLogError, "Session to peer ", it->first.ToBase64 (), " has not been created in ", SESSION_CREATION_TIMEOUT, " seconds"); + it = m_Peers.erase (it); + } + else + it++; + } + UpdateBandwidth (); // TODO: use separate timer(s) for it + if (i2p::context.GetStatus () == eRouterStatusTesting) // if still testing, repeat peer test + DetectExternalIP (); + m_PeerCleanupTimer.expires_from_now (boost::posix_time::seconds(5*SESSION_CREATION_TIMEOUT)); + m_PeerCleanupTimer.async_wait (std::bind (&Transports::HandlePeerCleanupTimer, this, std::placeholders::_1)); + } + } - std::shared_ptr Transports::GetRandomPeer () const - { - CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator (); - auto it = m_Peers.begin (); - std::advance (it, rnd.GenerateWord32 (0, m_Peers.size () - 1)); - return it != m_Peers.end () ? it->second.router : nullptr; - } + std::shared_ptr Transports::GetRandomPeer () const + { + CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator (); + auto it = m_Peers.begin (); + std::advance (it, rnd.GenerateWord32 (0, m_Peers.size () - 1)); + return it != m_Peers.end () ? it->second.router : nullptr; + } } } diff --git a/Transports.h b/Transports.h index 15ad980e..0b8eef9e 100644 --- a/Transports.h +++ b/Transports.h @@ -28,133 +28,133 @@ namespace i2p { namespace transport { - class DHKeysPairSupplier - { - public: + class DHKeysPairSupplier + { + public: - DHKeysPairSupplier (int size); - ~DHKeysPairSupplier (); - void Start (); - void Stop (); - DHKeysPair * Acquire (); - void Return (DHKeysPair * pair); + DHKeysPairSupplier (int size); + ~DHKeysPairSupplier (); + void Start (); + void Stop (); + DHKeysPair * Acquire (); + void Return (DHKeysPair * pair); - private: + private: - void Run (); - void CreateDHKeysPairs (int num); + void Run (); + void CreateDHKeysPairs (int num); - private: + private: - const int m_QueueSize; - std::queue m_Queue; + const int m_QueueSize; + std::queue m_Queue; - bool m_IsRunning; - std::thread * m_Thread; - std::condition_variable m_Acquired; - std::mutex m_AcquiredMutex; - CryptoPP::AutoSeededRandomPool m_Rnd; - }; + bool m_IsRunning; + std::thread * m_Thread; + std::condition_variable m_Acquired; + std::mutex m_AcquiredMutex; + CryptoPP::AutoSeededRandomPool m_Rnd; + }; - struct Peer - { - int numAttempts; - std::shared_ptr router; - std::list > sessions; - uint64_t creationTime; - std::vector > delayedMessages; + struct Peer + { + int numAttempts; + std::shared_ptr router; + std::list > sessions; + uint64_t creationTime; + std::vector > delayedMessages; - void Done () - { - for (auto it: sessions) - it->Done (); - } - }; - - const size_t SESSION_CREATION_TIMEOUT = 10; // in seconds - const uint32_t LOW_BANDWIDTH_LIMIT = 32*1024; // 32KBs - class Transports - { - public: + void Done () + { + for (auto it: sessions) + it->Done (); + } + }; + + const size_t SESSION_CREATION_TIMEOUT = 10; // in seconds + const uint32_t LOW_BANDWIDTH_LIMIT = 32*1024; // 32KBs + class Transports + { + public: - Transports (); - ~Transports (); + Transports (); + ~Transports (); - void Start (); - void Stop (); - - boost::asio::io_service& GetService () { return m_Service; }; - i2p::transport::DHKeysPair * GetNextDHKeysPair (); - void ReuseDHKeysPair (DHKeysPair * pair); + void Start (); + void Stop (); + + boost::asio::io_service& GetService () { return m_Service; }; + i2p::transport::DHKeysPair * GetNextDHKeysPair (); + void ReuseDHKeysPair (DHKeysPair * pair); - void SendMessage (const i2p::data::IdentHash& ident, std::shared_ptr msg); - void SendMessages (const i2p::data::IdentHash& ident, const std::vector >& msgs); - void CloseSession (std::shared_ptr router); + void SendMessage (const i2p::data::IdentHash& ident, std::shared_ptr msg); + void SendMessages (const i2p::data::IdentHash& ident, const std::vector >& msgs); + void CloseSession (std::shared_ptr router); - void PeerConnected (std::shared_ptr session); - void PeerDisconnected (std::shared_ptr session); - bool IsConnected (const i2p::data::IdentHash& ident) const; - - void UpdateSentBytes (uint64_t numBytes) { m_TotalSentBytes += numBytes; }; - void UpdateReceivedBytes (uint64_t numBytes) { m_TotalReceivedBytes += numBytes; }; - uint64_t GetTotalSentBytes () const { return m_TotalSentBytes; }; - uint64_t GetTotalReceivedBytes () const { return m_TotalReceivedBytes; }; - uint32_t GetInBandwidth () const { return m_InBandwidth; }; // bytes per second - uint32_t GetOutBandwidth () const { return m_OutBandwidth; }; // bytes per second - bool IsBandwidthExceeded () const; - size_t GetNumPeers () const { return m_Peers.size (); }; - std::shared_ptr GetRandomPeer () const; + void PeerConnected (std::shared_ptr session); + void PeerDisconnected (std::shared_ptr session); + bool IsConnected (const i2p::data::IdentHash& ident) const; + + void UpdateSentBytes (uint64_t numBytes) { m_TotalSentBytes += numBytes; }; + void UpdateReceivedBytes (uint64_t numBytes) { m_TotalReceivedBytes += numBytes; }; + uint64_t GetTotalSentBytes () const { return m_TotalSentBytes; }; + uint64_t GetTotalReceivedBytes () const { return m_TotalReceivedBytes; }; + uint32_t GetInBandwidth () const { return m_InBandwidth; }; // bytes per second + uint32_t GetOutBandwidth () const { return m_OutBandwidth; }; // bytes per second + bool IsBandwidthExceeded () const; + size_t GetNumPeers () const { return m_Peers.size (); }; + std::shared_ptr GetRandomPeer () const; - private: + private: - void Run (); - void RequestComplete (std::shared_ptr r, const i2p::data::IdentHash& ident); - void HandleRequestComplete (std::shared_ptr r, const i2p::data::IdentHash& ident); - void PostMessages (i2p::data::IdentHash ident, std::vector > msgs); - void PostCloseSession (std::shared_ptr router); - bool ConnectToPeer (const i2p::data::IdentHash& ident, Peer& peer); - void HandlePeerCleanupTimer (const boost::system::error_code& ecode); + void Run (); + void RequestComplete (std::shared_ptr r, const i2p::data::IdentHash& ident); + void HandleRequestComplete (std::shared_ptr r, const i2p::data::IdentHash& ident); + void PostMessages (i2p::data::IdentHash ident, std::vector > msgs); + void PostCloseSession (std::shared_ptr router); + bool ConnectToPeer (const i2p::data::IdentHash& ident, Peer& peer); + void HandlePeerCleanupTimer (const boost::system::error_code& ecode); - void NTCPResolve (const std::string& addr, const i2p::data::IdentHash& ident); - void HandleNTCPResolve (const boost::system::error_code& ecode, boost::asio::ip::tcp::resolver::iterator it, - i2p::data::IdentHash ident, std::shared_ptr resolver); + void NTCPResolve (const std::string& addr, const i2p::data::IdentHash& ident); + void HandleNTCPResolve (const boost::system::error_code& ecode, boost::asio::ip::tcp::resolver::iterator it, + i2p::data::IdentHash ident, std::shared_ptr resolver); - void UpdateBandwidth (); - void DetectExternalIP (); - - private: + void UpdateBandwidth (); + void DetectExternalIP (); + + private: - bool m_IsRunning; - std::thread * m_Thread; - boost::asio::io_service m_Service; - boost::asio::io_service::work m_Work; - boost::asio::deadline_timer m_PeerCleanupTimer; + bool m_IsRunning; + std::thread * m_Thread; + boost::asio::io_service m_Service; + boost::asio::io_service::work m_Work; + boost::asio::deadline_timer m_PeerCleanupTimer; - NTCPServer * m_NTCPServer; - SSUServer * m_SSUServer; - std::map m_Peers; - - DHKeysPairSupplier m_DHKeysPairSupplier; + NTCPServer * m_NTCPServer; + SSUServer * m_SSUServer; + std::map m_Peers; + + DHKeysPairSupplier m_DHKeysPairSupplier; - std::atomic m_TotalSentBytes, m_TotalReceivedBytes; - uint32_t m_InBandwidth, m_OutBandwidth; - uint64_t m_LastInBandwidthUpdateBytes, m_LastOutBandwidthUpdateBytes; - uint64_t m_LastBandwidthUpdateTime; + std::atomic m_TotalSentBytes, m_TotalReceivedBytes; + uint32_t m_InBandwidth, m_OutBandwidth; + uint64_t m_LastInBandwidthUpdateBytes, m_LastOutBandwidthUpdateBytes; + uint64_t m_LastBandwidthUpdateTime; #ifdef USE_UPNP - UPnP m_UPnP; + UPnP m_UPnP; #endif - public: + public: - // for HTTP only - const NTCPServer * GetNTCPServer () const { return m_NTCPServer; }; - const SSUServer * GetSSUServer () const { return m_SSUServer; }; - const decltype(m_Peers)& GetPeers () const { return m_Peers; }; - }; + // for HTTP only + const NTCPServer * GetNTCPServer () const { return m_NTCPServer; }; + const SSUServer * GetSSUServer () const { return m_SSUServer; }; + const decltype(m_Peers)& GetPeers () const { return m_Peers; }; + }; - extern Transports transports; -} + extern Transports transports; +} } #endif diff --git a/Tunnel.cpp b/Tunnel.cpp index 0c5600e7..9e85b698 100644 --- a/Tunnel.cpp +++ b/Tunnel.cpp @@ -13,720 +13,720 @@ #include "Tunnel.h" namespace i2p -{ +{ namespace tunnel -{ - - Tunnel::Tunnel (std::shared_ptr config): - m_Config (config), m_Pool (nullptr), m_State (eTunnelStatePending), m_IsRecreated (false) - { - } +{ + + Tunnel::Tunnel (std::shared_ptr config): + m_Config (config), m_Pool (nullptr), m_State (eTunnelStatePending), m_IsRecreated (false) + { + } - Tunnel::~Tunnel () - { - } + Tunnel::~Tunnel () + { + } - void Tunnel::Build (uint32_t replyMsgID, std::shared_ptr outboundTunnel) - { - CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator (); - auto numHops = m_Config->GetNumHops (); - int numRecords = numHops <= STANDARD_NUM_RECORDS ? STANDARD_NUM_RECORDS : numHops; - auto msg = NewI2NPShortMessage (); - *msg->GetPayload () = numRecords; - msg->len += numRecords*TUNNEL_BUILD_RECORD_SIZE + 1; + void Tunnel::Build (uint32_t replyMsgID, std::shared_ptr outboundTunnel) + { + CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator (); + auto numHops = m_Config->GetNumHops (); + int numRecords = numHops <= STANDARD_NUM_RECORDS ? STANDARD_NUM_RECORDS : numHops; + auto msg = NewI2NPShortMessage (); + *msg->GetPayload () = numRecords; + msg->len += numRecords*TUNNEL_BUILD_RECORD_SIZE + 1; - // shuffle records - std::vector recordIndicies; - for (int i = 0; i < numRecords; i++) recordIndicies.push_back(i); - std::random_shuffle (recordIndicies.begin(), recordIndicies.end()); + // shuffle records + std::vector recordIndicies; + for (int i = 0; i < numRecords; i++) recordIndicies.push_back(i); + std::random_shuffle (recordIndicies.begin(), recordIndicies.end()); - // create real records - uint8_t * records = msg->GetPayload () + 1; - TunnelHopConfig * hop = m_Config->GetFirstHop (); - int i = 0; - while (hop) - { - int idx = recordIndicies[i]; - hop->CreateBuildRequestRecord (records + idx*TUNNEL_BUILD_RECORD_SIZE, - hop->next ? rnd.GenerateWord32 () : replyMsgID); // we set replyMsgID for last hop only - hop->recordIndex = idx; - i++; - hop = hop->next; - } - // fill up fake records with random data - for (int i = numHops; i < numRecords; i++) - { - int idx = recordIndicies[i]; - rnd.GenerateBlock (records + idx*TUNNEL_BUILD_RECORD_SIZE, TUNNEL_BUILD_RECORD_SIZE); - } + // create real records + uint8_t * records = msg->GetPayload () + 1; + TunnelHopConfig * hop = m_Config->GetFirstHop (); + int i = 0; + while (hop) + { + int idx = recordIndicies[i]; + hop->CreateBuildRequestRecord (records + idx*TUNNEL_BUILD_RECORD_SIZE, + hop->next ? rnd.GenerateWord32 () : replyMsgID); // we set replyMsgID for last hop only + hop->recordIndex = idx; + i++; + hop = hop->next; + } + // fill up fake records with random data + for (int i = numHops; i < numRecords; i++) + { + int idx = recordIndicies[i]; + rnd.GenerateBlock (records + idx*TUNNEL_BUILD_RECORD_SIZE, TUNNEL_BUILD_RECORD_SIZE); + } - // decrypt real records - i2p::crypto::CBCDecryption decryption; - hop = m_Config->GetLastHop ()->prev; - while (hop) - { - decryption.SetKey (hop->replyKey); - // decrypt records after current hop - TunnelHopConfig * hop1 = hop->next; - while (hop1) - { - decryption.SetIV (hop->replyIV); - uint8_t * record = records + hop1->recordIndex*TUNNEL_BUILD_RECORD_SIZE; - decryption.Decrypt(record, TUNNEL_BUILD_RECORD_SIZE, record); - hop1 = hop1->next; - } - hop = hop->prev; - } - msg->FillI2NPMessageHeader (eI2NPVariableTunnelBuild); + // decrypt real records + i2p::crypto::CBCDecryption decryption; + hop = m_Config->GetLastHop ()->prev; + while (hop) + { + decryption.SetKey (hop->replyKey); + // decrypt records after current hop + TunnelHopConfig * hop1 = hop->next; + while (hop1) + { + decryption.SetIV (hop->replyIV); + uint8_t * record = records + hop1->recordIndex*TUNNEL_BUILD_RECORD_SIZE; + decryption.Decrypt(record, TUNNEL_BUILD_RECORD_SIZE, record); + hop1 = hop1->next; + } + hop = hop->prev; + } + msg->FillI2NPMessageHeader (eI2NPVariableTunnelBuild); - // send message - if (outboundTunnel) - outboundTunnel->SendTunnelDataMsg (GetNextIdentHash (), 0, ToSharedI2NPMessage (msg)); - else - i2p::transport::transports.SendMessage (GetNextIdentHash (), ToSharedI2NPMessage (msg)); - } - - bool Tunnel::HandleTunnelBuildResponse (uint8_t * msg, size_t len) - { - LogPrint ("TunnelBuildResponse ", (int)msg[0], " records."); - - i2p::crypto::CBCDecryption decryption; - TunnelHopConfig * hop = m_Config->GetLastHop (); - while (hop) - { - decryption.SetKey (hop->replyKey); - // decrypt records before and including current hop - TunnelHopConfig * hop1 = hop; - while (hop1) - { - auto idx = hop1->recordIndex; - if (idx >= 0 && idx < msg[0]) - { - uint8_t * record = msg + 1 + idx*TUNNEL_BUILD_RECORD_SIZE; - decryption.SetIV (hop->replyIV); - decryption.Decrypt(record, TUNNEL_BUILD_RECORD_SIZE, record); - } - else - LogPrint ("Tunnel hop index ", idx, " is out of range"); - hop1 = hop1->prev; - } - hop = hop->prev; - } + // send message + if (outboundTunnel) + outboundTunnel->SendTunnelDataMsg (GetNextIdentHash (), 0, ToSharedI2NPMessage (msg)); + else + i2p::transport::transports.SendMessage (GetNextIdentHash (), ToSharedI2NPMessage (msg)); + } + + bool Tunnel::HandleTunnelBuildResponse (uint8_t * msg, size_t len) + { + LogPrint ("TunnelBuildResponse ", (int)msg[0], " records."); + + i2p::crypto::CBCDecryption decryption; + TunnelHopConfig * hop = m_Config->GetLastHop (); + while (hop) + { + decryption.SetKey (hop->replyKey); + // decrypt records before and including current hop + TunnelHopConfig * hop1 = hop; + while (hop1) + { + auto idx = hop1->recordIndex; + if (idx >= 0 && idx < msg[0]) + { + uint8_t * record = msg + 1 + idx*TUNNEL_BUILD_RECORD_SIZE; + decryption.SetIV (hop->replyIV); + decryption.Decrypt(record, TUNNEL_BUILD_RECORD_SIZE, record); + } + else + LogPrint ("Tunnel hop index ", idx, " is out of range"); + hop1 = hop1->prev; + } + hop = hop->prev; + } - bool established = true; - hop = m_Config->GetFirstHop (); - while (hop) - { - const uint8_t * record = msg + 1 + hop->recordIndex*TUNNEL_BUILD_RECORD_SIZE; - uint8_t ret = record[BUILD_RESPONSE_RECORD_RET_OFFSET]; - LogPrint ("Ret code=", (int)ret); - hop->router->GetProfile ()->TunnelBuildResponse (ret); - if (ret) - // if any of participants declined the tunnel is not established - established = false; - hop = hop->next; - } - if (established) - { - // change reply keys to layer keys - hop = m_Config->GetFirstHop (); - while (hop) - { - hop->decryption.SetKeys (hop->layerKey, hop->ivKey); - hop = hop->next; - } - } - if (established) m_State = eTunnelStateEstablished; - return established; - } + bool established = true; + hop = m_Config->GetFirstHop (); + while (hop) + { + const uint8_t * record = msg + 1 + hop->recordIndex*TUNNEL_BUILD_RECORD_SIZE; + uint8_t ret = record[BUILD_RESPONSE_RECORD_RET_OFFSET]; + LogPrint ("Ret code=", (int)ret); + hop->router->GetProfile ()->TunnelBuildResponse (ret); + if (ret) + // if any of participants declined the tunnel is not established + established = false; + hop = hop->next; + } + if (established) + { + // change reply keys to layer keys + hop = m_Config->GetFirstHop (); + while (hop) + { + hop->decryption.SetKeys (hop->layerKey, hop->ivKey); + hop = hop->next; + } + } + if (established) m_State = eTunnelStateEstablished; + return established; + } - void Tunnel::EncryptTunnelMsg (std::shared_ptr in, std::shared_ptr out) - { - const uint8_t * inPayload = in->GetPayload () + 4; - uint8_t * outPayload = out->GetPayload () + 4; - TunnelHopConfig * hop = m_Config->GetLastHop (); - while (hop) - { - hop->decryption.Decrypt (inPayload, outPayload); - hop = hop->prev; - inPayload = outPayload; - } - } + void Tunnel::EncryptTunnelMsg (std::shared_ptr in, std::shared_ptr out) + { + const uint8_t * inPayload = in->GetPayload () + 4; + uint8_t * outPayload = out->GetPayload () + 4; + TunnelHopConfig * hop = m_Config->GetLastHop (); + while (hop) + { + hop->decryption.Decrypt (inPayload, outPayload); + hop = hop->prev; + inPayload = outPayload; + } + } - void Tunnel::SendTunnelDataMsg (std::shared_ptr msg) - { - LogPrint (eLogInfo, "Can't send I2NP messages without delivery instructions"); - } + void Tunnel::SendTunnelDataMsg (std::shared_ptr msg) + { + LogPrint (eLogInfo, "Can't send I2NP messages without delivery instructions"); + } - void InboundTunnel::HandleTunnelDataMsg (std::shared_ptr msg) - { - if (IsFailed ()) SetState (eTunnelStateEstablished); // incoming messages means a tunnel is alive - auto newMsg = CreateEmptyTunnelDataMsg (); - EncryptTunnelMsg (msg, newMsg); - newMsg->from = shared_from_this (); - m_Endpoint.HandleDecryptedTunnelDataMsg (newMsg); - } + void InboundTunnel::HandleTunnelDataMsg (std::shared_ptr msg) + { + if (IsFailed ()) SetState (eTunnelStateEstablished); // incoming messages means a tunnel is alive + auto newMsg = CreateEmptyTunnelDataMsg (); + EncryptTunnelMsg (msg, newMsg); + newMsg->from = shared_from_this (); + m_Endpoint.HandleDecryptedTunnelDataMsg (newMsg); + } - void OutboundTunnel::SendTunnelDataMsg (const uint8_t * gwHash, uint32_t gwTunnel, std::shared_ptr msg) - { - TunnelMessageBlock block; - if (gwHash) - { - block.hash = gwHash; - if (gwTunnel) - { - block.deliveryType = eDeliveryTypeTunnel; - block.tunnelID = gwTunnel; - } - else - block.deliveryType = eDeliveryTypeRouter; - } - else - block.deliveryType = eDeliveryTypeLocal; - block.data = msg; - - std::unique_lock l(m_SendMutex); - m_Gateway.SendTunnelDataMsg (block); - } - - void OutboundTunnel::SendTunnelDataMsg (const std::vector& msgs) - { - std::unique_lock l(m_SendMutex); - for (auto& it : msgs) - m_Gateway.PutTunnelDataMsg (it); - m_Gateway.SendBuffer (); - } - - void OutboundTunnel::HandleTunnelDataMsg (std::shared_ptr tunnelMsg) - { - LogPrint (eLogError, "Incoming message for outbound tunnel ", GetTunnelID ()); - } + void OutboundTunnel::SendTunnelDataMsg (const uint8_t * gwHash, uint32_t gwTunnel, std::shared_ptr msg) + { + TunnelMessageBlock block; + if (gwHash) + { + block.hash = gwHash; + if (gwTunnel) + { + block.deliveryType = eDeliveryTypeTunnel; + block.tunnelID = gwTunnel; + } + else + block.deliveryType = eDeliveryTypeRouter; + } + else + block.deliveryType = eDeliveryTypeLocal; + block.data = msg; + + std::unique_lock l(m_SendMutex); + m_Gateway.SendTunnelDataMsg (block); + } + + void OutboundTunnel::SendTunnelDataMsg (const std::vector& msgs) + { + std::unique_lock l(m_SendMutex); + for (auto& it : msgs) + m_Gateway.PutTunnelDataMsg (it); + m_Gateway.SendBuffer (); + } + + void OutboundTunnel::HandleTunnelDataMsg (std::shared_ptr tunnelMsg) + { + LogPrint (eLogError, "Incoming message for outbound tunnel ", GetTunnelID ()); + } - Tunnels tunnels; - - Tunnels::Tunnels (): m_IsRunning (false), m_Thread (nullptr), - m_NumSuccesiveTunnelCreations (0), m_NumFailedTunnelCreations (0) - { - } - - Tunnels::~Tunnels () - { - for (auto& it : m_TransitTunnels) - delete it.second; - m_TransitTunnels.clear (); - } - - std::shared_ptr Tunnels::GetInboundTunnel (uint32_t tunnelID) - { - auto it = m_InboundTunnels.find(tunnelID); - if (it != m_InboundTunnels.end ()) - return it->second; - return nullptr; - } - - TransitTunnel * Tunnels::GetTransitTunnel (uint32_t tunnelID) - { - auto it = m_TransitTunnels.find(tunnelID); - if (it != m_TransitTunnels.end ()) - return it->second; - return nullptr; - } - - std::shared_ptr Tunnels::GetPendingInboundTunnel (uint32_t replyMsgID) - { - return GetPendingTunnel (replyMsgID, m_PendingInboundTunnels); - } - - std::shared_ptr Tunnels::GetPendingOutboundTunnel (uint32_t replyMsgID) - { - return GetPendingTunnel (replyMsgID, m_PendingOutboundTunnels); - } + Tunnels tunnels; + + Tunnels::Tunnels (): m_IsRunning (false), m_Thread (nullptr), + m_NumSuccesiveTunnelCreations (0), m_NumFailedTunnelCreations (0) + { + } + + Tunnels::~Tunnels () + { + for (auto& it : m_TransitTunnels) + delete it.second; + m_TransitTunnels.clear (); + } + + std::shared_ptr Tunnels::GetInboundTunnel (uint32_t tunnelID) + { + auto it = m_InboundTunnels.find(tunnelID); + if (it != m_InboundTunnels.end ()) + return it->second; + return nullptr; + } + + TransitTunnel * Tunnels::GetTransitTunnel (uint32_t tunnelID) + { + auto it = m_TransitTunnels.find(tunnelID); + if (it != m_TransitTunnels.end ()) + return it->second; + return nullptr; + } + + std::shared_ptr Tunnels::GetPendingInboundTunnel (uint32_t replyMsgID) + { + return GetPendingTunnel (replyMsgID, m_PendingInboundTunnels); + } + + std::shared_ptr Tunnels::GetPendingOutboundTunnel (uint32_t replyMsgID) + { + return GetPendingTunnel (replyMsgID, m_PendingOutboundTunnels); + } - template - std::shared_ptr Tunnels::GetPendingTunnel (uint32_t replyMsgID, const std::map >& pendingTunnels) - { - auto it = pendingTunnels.find(replyMsgID); - if (it != pendingTunnels.end () && it->second->GetState () == eTunnelStatePending) - { - it->second->SetState (eTunnelStateBuildReplyReceived); - return it->second; - } - return nullptr; - } + template + std::shared_ptr Tunnels::GetPendingTunnel (uint32_t replyMsgID, const std::map >& pendingTunnels) + { + auto it = pendingTunnels.find(replyMsgID); + if (it != pendingTunnels.end () && it->second->GetState () == eTunnelStatePending) + { + it->second->SetState (eTunnelStateBuildReplyReceived); + return it->second; + } + return nullptr; + } - std::shared_ptr Tunnels::GetNextInboundTunnel () - { - std::shared_ptr tunnel; - size_t minReceived = 0; - for (auto it : m_InboundTunnels) - { - if (!it.second->IsEstablished ()) continue; - if (!tunnel || it.second->GetNumReceivedBytes () < minReceived) - { - tunnel = it.second; - minReceived = it.second->GetNumReceivedBytes (); - } - } - return tunnel; - } - - std::shared_ptr Tunnels::GetNextOutboundTunnel () - { - CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator (); - uint32_t ind = rnd.GenerateWord32 (0, m_OutboundTunnels.size () - 1), i = 0; - std::shared_ptr tunnel; - for (auto it: m_OutboundTunnels) - { - if (it->IsEstablished ()) - { - tunnel = it; - i++; - } - if (i > ind && tunnel) break; - } - return tunnel; - } + std::shared_ptr Tunnels::GetNextInboundTunnel () + { + std::shared_ptr tunnel; + size_t minReceived = 0; + for (auto it : m_InboundTunnels) + { + if (!it.second->IsEstablished ()) continue; + if (!tunnel || it.second->GetNumReceivedBytes () < minReceived) + { + tunnel = it.second; + minReceived = it.second->GetNumReceivedBytes (); + } + } + return tunnel; + } + + std::shared_ptr Tunnels::GetNextOutboundTunnel () + { + CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator (); + uint32_t ind = rnd.GenerateWord32 (0, m_OutboundTunnels.size () - 1), i = 0; + std::shared_ptr tunnel; + for (auto it: m_OutboundTunnels) + { + if (it->IsEstablished ()) + { + tunnel = it; + i++; + } + if (i > ind && tunnel) break; + } + return tunnel; + } - std::shared_ptr Tunnels::CreateTunnelPool (i2p::garlic::GarlicDestination * localDestination, int numInboundHops, int numOutboundHops, int numInboundTunnels, int numOutboundTunnels) - { - auto pool = std::make_shared (localDestination, numInboundHops, numOutboundHops, numInboundTunnels, numOutboundTunnels); - std::unique_lock l(m_PoolsMutex); - m_Pools.push_back (pool); - return pool; - } + std::shared_ptr Tunnels::CreateTunnelPool (i2p::garlic::GarlicDestination * localDestination, int numInboundHops, int numOutboundHops, int numInboundTunnels, int numOutboundTunnels) + { + auto pool = std::make_shared (localDestination, numInboundHops, numOutboundHops, numInboundTunnels, numOutboundTunnels); + std::unique_lock l(m_PoolsMutex); + m_Pools.push_back (pool); + return pool; + } - void Tunnels::DeleteTunnelPool (std::shared_ptr pool) - { - if (pool) - { - StopTunnelPool (pool); - { - std::unique_lock l(m_PoolsMutex); - m_Pools.remove (pool); - } - } - } + void Tunnels::DeleteTunnelPool (std::shared_ptr pool) + { + if (pool) + { + StopTunnelPool (pool); + { + std::unique_lock l(m_PoolsMutex); + m_Pools.remove (pool); + } + } + } - void Tunnels::StopTunnelPool (std::shared_ptr pool) - { - if (pool) - { - pool->SetActive (false); - pool->DetachTunnels (); - } - } - - void Tunnels::AddTransitTunnel (TransitTunnel * tunnel) - { - std::unique_lock l(m_TransitTunnelsMutex); - if (!m_TransitTunnels.insert (std::make_pair (tunnel->GetTunnelID (), tunnel)).second) - { - LogPrint (eLogError, "Transit tunnel ", tunnel->GetTunnelID (), " already exists"); - delete tunnel; - } - } + void Tunnels::StopTunnelPool (std::shared_ptr pool) + { + if (pool) + { + pool->SetActive (false); + pool->DetachTunnels (); + } + } + + void Tunnels::AddTransitTunnel (TransitTunnel * tunnel) + { + std::unique_lock l(m_TransitTunnelsMutex); + if (!m_TransitTunnels.insert (std::make_pair (tunnel->GetTunnelID (), tunnel)).second) + { + LogPrint (eLogError, "Transit tunnel ", tunnel->GetTunnelID (), " already exists"); + delete tunnel; + } + } - void Tunnels::Start () - { - m_IsRunning = true; - m_Thread = new std::thread (std::bind (&Tunnels::Run, this)); - } - - void Tunnels::Stop () - { - m_IsRunning = false; - m_Queue.WakeUp (); - if (m_Thread) - { - m_Thread->join (); - delete m_Thread; - m_Thread = 0; - } - } + void Tunnels::Start () + { + m_IsRunning = true; + m_Thread = new std::thread (std::bind (&Tunnels::Run, this)); + } + + void Tunnels::Stop () + { + m_IsRunning = false; + m_Queue.WakeUp (); + if (m_Thread) + { + m_Thread->join (); + delete m_Thread; + m_Thread = 0; + } + } - void Tunnels::Run () - { - std::this_thread::sleep_for (std::chrono::seconds(1)); // wait for other parts are ready - - uint64_t lastTs = 0; - while (m_IsRunning) - { - try - { - auto msg = m_Queue.GetNextWithTimeout (1000); // 1 sec - if (msg) - { - uint32_t prevTunnelID = 0, tunnelID = 0; - TunnelBase * prevTunnel = nullptr; - do - { - TunnelBase * tunnel = nullptr; - uint8_t typeID = msg->GetTypeID (); - switch (typeID) - { - case eI2NPTunnelData: - case eI2NPTunnelGateway: - { - tunnelID = bufbe32toh (msg->GetPayload ()); - if (tunnelID == prevTunnelID) - tunnel = prevTunnel; - else if (prevTunnel) - prevTunnel->FlushTunnelDataMsgs (); - - if (!tunnel && typeID == eI2NPTunnelData) - tunnel = GetInboundTunnel (tunnelID).get (); - if (!tunnel) - tunnel = GetTransitTunnel (tunnelID); - if (tunnel) - { - if (typeID == eI2NPTunnelData) - tunnel->HandleTunnelDataMsg (msg); - else // tunnel gateway assumed - HandleTunnelGatewayMsg (tunnel, msg); - } - else - LogPrint (eLogWarning, "Tunnel ", tunnelID, " not found"); - break; - } - case eI2NPVariableTunnelBuild: - case eI2NPVariableTunnelBuildReply: - case eI2NPTunnelBuild: - case eI2NPTunnelBuildReply: - HandleI2NPMessage (msg->GetBuffer (), msg->GetLength ()); - break; - default: - LogPrint (eLogError, "Unexpected messsage type ", (int)typeID); - } - - msg = m_Queue.Get (); - if (msg) - { - prevTunnelID = tunnelID; - prevTunnel = tunnel; - } - else if (tunnel) - tunnel->FlushTunnelDataMsgs (); - } - while (msg); - } - - uint64_t ts = i2p::util::GetSecondsSinceEpoch (); - if (ts - lastTs >= 15) // manage tunnels every 15 seconds - { - ManageTunnels (); - lastTs = ts; - } - } - catch (std::exception& ex) - { - LogPrint ("Tunnels: ", ex.what ()); - } - } - } + void Tunnels::Run () + { + std::this_thread::sleep_for (std::chrono::seconds(1)); // wait for other parts are ready + + uint64_t lastTs = 0; + while (m_IsRunning) + { + try + { + auto msg = m_Queue.GetNextWithTimeout (1000); // 1 sec + if (msg) + { + uint32_t prevTunnelID = 0, tunnelID = 0; + TunnelBase * prevTunnel = nullptr; + do + { + TunnelBase * tunnel = nullptr; + uint8_t typeID = msg->GetTypeID (); + switch (typeID) + { + case eI2NPTunnelData: + case eI2NPTunnelGateway: + { + tunnelID = bufbe32toh (msg->GetPayload ()); + if (tunnelID == prevTunnelID) + tunnel = prevTunnel; + else if (prevTunnel) + prevTunnel->FlushTunnelDataMsgs (); + + if (!tunnel && typeID == eI2NPTunnelData) + tunnel = GetInboundTunnel (tunnelID).get (); + if (!tunnel) + tunnel = GetTransitTunnel (tunnelID); + if (tunnel) + { + if (typeID == eI2NPTunnelData) + tunnel->HandleTunnelDataMsg (msg); + else // tunnel gateway assumed + HandleTunnelGatewayMsg (tunnel, msg); + } + else + LogPrint (eLogWarning, "Tunnel ", tunnelID, " not found"); + break; + } + case eI2NPVariableTunnelBuild: + case eI2NPVariableTunnelBuildReply: + case eI2NPTunnelBuild: + case eI2NPTunnelBuildReply: + HandleI2NPMessage (msg->GetBuffer (), msg->GetLength ()); + break; + default: + LogPrint (eLogError, "Unexpected messsage type ", (int)typeID); + } + + msg = m_Queue.Get (); + if (msg) + { + prevTunnelID = tunnelID; + prevTunnel = tunnel; + } + else if (tunnel) + tunnel->FlushTunnelDataMsgs (); + } + while (msg); + } + + uint64_t ts = i2p::util::GetSecondsSinceEpoch (); + if (ts - lastTs >= 15) // manage tunnels every 15 seconds + { + ManageTunnels (); + lastTs = ts; + } + } + catch (std::exception& ex) + { + LogPrint ("Tunnels: ", ex.what ()); + } + } + } - void Tunnels::HandleTunnelGatewayMsg (TunnelBase * tunnel, std::shared_ptr msg) - { - if (!tunnel) - { - LogPrint (eLogError, "Missing tunnel for TunnelGateway"); - return; - } - const uint8_t * payload = msg->GetPayload (); - uint16_t len = bufbe16toh(payload + TUNNEL_GATEWAY_HEADER_LENGTH_OFFSET); - // we make payload as new I2NP message to send - msg->offset += I2NP_HEADER_SIZE + TUNNEL_GATEWAY_HEADER_SIZE; - msg->len = msg->offset + len; - auto typeID = msg->GetTypeID (); - LogPrint (eLogDebug, "TunnelGateway of ", (int)len, " bytes for tunnel ", tunnel->GetTunnelID (), ". Msg type ", (int)typeID); - - if (typeID == eI2NPDatabaseStore || typeID == eI2NPDatabaseSearchReply) - // transit DatabaseStore my contain new/updated RI - // or DatabaseSearchReply with new routers - i2p::data::netdb.PostI2NPMsg (msg); - tunnel->SendTunnelDataMsg (msg); - } + void Tunnels::HandleTunnelGatewayMsg (TunnelBase * tunnel, std::shared_ptr msg) + { + if (!tunnel) + { + LogPrint (eLogError, "Missing tunnel for TunnelGateway"); + return; + } + const uint8_t * payload = msg->GetPayload (); + uint16_t len = bufbe16toh(payload + TUNNEL_GATEWAY_HEADER_LENGTH_OFFSET); + // we make payload as new I2NP message to send + msg->offset += I2NP_HEADER_SIZE + TUNNEL_GATEWAY_HEADER_SIZE; + msg->len = msg->offset + len; + auto typeID = msg->GetTypeID (); + LogPrint (eLogDebug, "TunnelGateway of ", (int)len, " bytes for tunnel ", tunnel->GetTunnelID (), ". Msg type ", (int)typeID); + + if (typeID == eI2NPDatabaseStore || typeID == eI2NPDatabaseSearchReply) + // transit DatabaseStore my contain new/updated RI + // or DatabaseSearchReply with new routers + i2p::data::netdb.PostI2NPMsg (msg); + tunnel->SendTunnelDataMsg (msg); + } - void Tunnels::ManageTunnels () - { - ManagePendingTunnels (); - ManageInboundTunnels (); - ManageOutboundTunnels (); - ManageTransitTunnels (); - ManageTunnelPools (); - } + void Tunnels::ManageTunnels () + { + ManagePendingTunnels (); + ManageInboundTunnels (); + ManageOutboundTunnels (); + ManageTransitTunnels (); + ManageTunnelPools (); + } - void Tunnels::ManagePendingTunnels () - { - ManagePendingTunnels (m_PendingInboundTunnels); - ManagePendingTunnels (m_PendingOutboundTunnels); - } + void Tunnels::ManagePendingTunnels () + { + ManagePendingTunnels (m_PendingInboundTunnels); + ManagePendingTunnels (m_PendingOutboundTunnels); + } - template - void Tunnels::ManagePendingTunnels (PendingTunnels& pendingTunnels) - { - // check pending tunnel. delete failed or timeout - uint64_t ts = i2p::util::GetSecondsSinceEpoch (); - for (auto it = pendingTunnels.begin (); it != pendingTunnels.end ();) - { - auto tunnel = it->second; - switch (tunnel->GetState ()) - { - case eTunnelStatePending: - if (ts > tunnel->GetCreationTime () + TUNNEL_CREATION_TIMEOUT) - { - LogPrint ("Pending tunnel build request ", it->first, " timeout. Deleted"); - // update stats - auto config = tunnel->GetTunnelConfig (); - if (config) - { - auto hop = config->GetFirstHop (); - while (hop) - { - if (hop->router) - hop->router->GetProfile ()->TunnelNonReplied (); - hop = hop->next; - } - } - // delete - it = pendingTunnels.erase (it); - m_NumFailedTunnelCreations++; - } - else - it++; - break; - case eTunnelStateBuildFailed: - LogPrint ("Pending tunnel build request ", it->first, " failed. Deleted"); - it = pendingTunnels.erase (it); - m_NumFailedTunnelCreations++; - break; - case eTunnelStateBuildReplyReceived: - // intermediate state, will be either established of build failed - it++; - break; - default: - // success - it = pendingTunnels.erase (it); - m_NumSuccesiveTunnelCreations++; - } - } - } + template + void Tunnels::ManagePendingTunnels (PendingTunnels& pendingTunnels) + { + // check pending tunnel. delete failed or timeout + uint64_t ts = i2p::util::GetSecondsSinceEpoch (); + for (auto it = pendingTunnels.begin (); it != pendingTunnels.end ();) + { + auto tunnel = it->second; + switch (tunnel->GetState ()) + { + case eTunnelStatePending: + if (ts > tunnel->GetCreationTime () + TUNNEL_CREATION_TIMEOUT) + { + LogPrint ("Pending tunnel build request ", it->first, " timeout. Deleted"); + // update stats + auto config = tunnel->GetTunnelConfig (); + if (config) + { + auto hop = config->GetFirstHop (); + while (hop) + { + if (hop->router) + hop->router->GetProfile ()->TunnelNonReplied (); + hop = hop->next; + } + } + // delete + it = pendingTunnels.erase (it); + m_NumFailedTunnelCreations++; + } + else + it++; + break; + case eTunnelStateBuildFailed: + LogPrint ("Pending tunnel build request ", it->first, " failed. Deleted"); + it = pendingTunnels.erase (it); + m_NumFailedTunnelCreations++; + break; + case eTunnelStateBuildReplyReceived: + // intermediate state, will be either established of build failed + it++; + break; + default: + // success + it = pendingTunnels.erase (it); + m_NumSuccesiveTunnelCreations++; + } + } + } - void Tunnels::ManageOutboundTunnels () - { - uint64_t ts = i2p::util::GetSecondsSinceEpoch (); - { - for (auto it = m_OutboundTunnels.begin (); it != m_OutboundTunnels.end ();) - { - auto tunnel = *it; - if (ts > tunnel->GetCreationTime () + TUNNEL_EXPIRATION_TIMEOUT) - { - LogPrint ("Tunnel ", tunnel->GetTunnelID (), " expired"); - auto pool = tunnel->GetTunnelPool (); - if (pool) - pool->TunnelExpired (tunnel); - it = m_OutboundTunnels.erase (it); - } - else - { - if (tunnel->IsEstablished ()) - { - if (!tunnel->IsRecreated () && ts + TUNNEL_RECREATION_THRESHOLD > tunnel->GetCreationTime () + TUNNEL_EXPIRATION_TIMEOUT) - { - tunnel->SetIsRecreated (); - auto pool = tunnel->GetTunnelPool (); - if (pool) - pool->RecreateOutboundTunnel (tunnel); - } - if (ts + TUNNEL_EXPIRATION_THRESHOLD > tunnel->GetCreationTime () + TUNNEL_EXPIRATION_TIMEOUT) - tunnel->SetState (eTunnelStateExpiring); - } - it++; - } - } - } - - if (m_OutboundTunnels.size () < 5) - { - // trying to create one more oubound tunnel - auto inboundTunnel = GetNextInboundTunnel (); - auto router = i2p::data::netdb.GetRandomRouter (); - if (!inboundTunnel || !router) return; - LogPrint ("Creating one hop outbound tunnel..."); - CreateTunnel ( - std::make_shared (std::vector > { router }, - inboundTunnel->GetTunnelConfig ()) - ); - } - } - - void Tunnels::ManageInboundTunnels () - { - uint64_t ts = i2p::util::GetSecondsSinceEpoch (); - { - for (auto it = m_InboundTunnels.begin (); it != m_InboundTunnels.end ();) - { - auto tunnel = it->second; - if (ts > tunnel->GetCreationTime () + TUNNEL_EXPIRATION_TIMEOUT) - { - LogPrint ("Tunnel ", tunnel->GetTunnelID (), " expired"); - auto pool = tunnel->GetTunnelPool (); - if (pool) - pool->TunnelExpired (tunnel); - it = m_InboundTunnels.erase (it); - } - else - { - if (tunnel->IsEstablished ()) - { - if (!tunnel->IsRecreated () && ts + TUNNEL_RECREATION_THRESHOLD > tunnel->GetCreationTime () + TUNNEL_EXPIRATION_TIMEOUT) - { - tunnel->SetIsRecreated (); - auto pool = tunnel->GetTunnelPool (); - if (pool) - pool->RecreateInboundTunnel (tunnel); - } - - if (ts + TUNNEL_EXPIRATION_THRESHOLD > tunnel->GetCreationTime () + TUNNEL_EXPIRATION_TIMEOUT) - tunnel->SetState (eTunnelStateExpiring); - } - it++; - } - } - } + void Tunnels::ManageOutboundTunnels () + { + uint64_t ts = i2p::util::GetSecondsSinceEpoch (); + { + for (auto it = m_OutboundTunnels.begin (); it != m_OutboundTunnels.end ();) + { + auto tunnel = *it; + if (ts > tunnel->GetCreationTime () + TUNNEL_EXPIRATION_TIMEOUT) + { + LogPrint ("Tunnel ", tunnel->GetTunnelID (), " expired"); + auto pool = tunnel->GetTunnelPool (); + if (pool) + pool->TunnelExpired (tunnel); + it = m_OutboundTunnels.erase (it); + } + else + { + if (tunnel->IsEstablished ()) + { + if (!tunnel->IsRecreated () && ts + TUNNEL_RECREATION_THRESHOLD > tunnel->GetCreationTime () + TUNNEL_EXPIRATION_TIMEOUT) + { + tunnel->SetIsRecreated (); + auto pool = tunnel->GetTunnelPool (); + if (pool) + pool->RecreateOutboundTunnel (tunnel); + } + if (ts + TUNNEL_EXPIRATION_THRESHOLD > tunnel->GetCreationTime () + TUNNEL_EXPIRATION_TIMEOUT) + tunnel->SetState (eTunnelStateExpiring); + } + it++; + } + } + } + + if (m_OutboundTunnels.size () < 5) + { + // trying to create one more oubound tunnel + auto inboundTunnel = GetNextInboundTunnel (); + auto router = i2p::data::netdb.GetRandomRouter (); + if (!inboundTunnel || !router) return; + LogPrint ("Creating one hop outbound tunnel..."); + CreateTunnel ( + std::make_shared (std::vector > { router }, + inboundTunnel->GetTunnelConfig ()) + ); + } + } + + void Tunnels::ManageInboundTunnels () + { + uint64_t ts = i2p::util::GetSecondsSinceEpoch (); + { + for (auto it = m_InboundTunnels.begin (); it != m_InboundTunnels.end ();) + { + auto tunnel = it->second; + if (ts > tunnel->GetCreationTime () + TUNNEL_EXPIRATION_TIMEOUT) + { + LogPrint ("Tunnel ", tunnel->GetTunnelID (), " expired"); + auto pool = tunnel->GetTunnelPool (); + if (pool) + pool->TunnelExpired (tunnel); + it = m_InboundTunnels.erase (it); + } + else + { + if (tunnel->IsEstablished ()) + { + if (!tunnel->IsRecreated () && ts + TUNNEL_RECREATION_THRESHOLD > tunnel->GetCreationTime () + TUNNEL_EXPIRATION_TIMEOUT) + { + tunnel->SetIsRecreated (); + auto pool = tunnel->GetTunnelPool (); + if (pool) + pool->RecreateInboundTunnel (tunnel); + } + + if (ts + TUNNEL_EXPIRATION_THRESHOLD > tunnel->GetCreationTime () + TUNNEL_EXPIRATION_TIMEOUT) + tunnel->SetState (eTunnelStateExpiring); + } + it++; + } + } + } - if (m_InboundTunnels.empty ()) - { - LogPrint ("Creating zero hops inbound tunnel..."); - CreateZeroHopsInboundTunnel (); - if (!m_ExploratoryPool) - m_ExploratoryPool = CreateTunnelPool (&i2p::context, 2, 2, 5, 5); // 2-hop exploratory, 5 tunnels - return; - } - - if (m_OutboundTunnels.empty () || m_InboundTunnels.size () < 5) - { - // trying to create one more inbound tunnel - auto router = i2p::data::netdb.GetRandomRouter (); - LogPrint ("Creating one hop inbound tunnel..."); - CreateTunnel ( - std::make_shared (std::vector > { router }) - ); - } - } + if (m_InboundTunnels.empty ()) + { + LogPrint ("Creating zero hops inbound tunnel..."); + CreateZeroHopsInboundTunnel (); + if (!m_ExploratoryPool) + m_ExploratoryPool = CreateTunnelPool (&i2p::context, 2, 2, 5, 5); // 2-hop exploratory, 5 tunnels + return; + } + + if (m_OutboundTunnels.empty () || m_InboundTunnels.size () < 5) + { + // trying to create one more inbound tunnel + auto router = i2p::data::netdb.GetRandomRouter (); + LogPrint ("Creating one hop inbound tunnel..."); + CreateTunnel ( + std::make_shared (std::vector > { router }) + ); + } + } - void Tunnels::ManageTransitTunnels () - { - uint32_t ts = i2p::util::GetSecondsSinceEpoch (); - for (auto it = m_TransitTunnels.begin (); it != m_TransitTunnels.end ();) - { - if (ts > it->second->GetCreationTime () + TUNNEL_EXPIRATION_TIMEOUT) - { - auto tmp = it->second; - LogPrint ("Transit tunnel ", tmp->GetTunnelID (), " expired"); - { - std::unique_lock l(m_TransitTunnelsMutex); - it = m_TransitTunnels.erase (it); - } - delete tmp; - } - else - it++; - } - } + void Tunnels::ManageTransitTunnels () + { + uint32_t ts = i2p::util::GetSecondsSinceEpoch (); + for (auto it = m_TransitTunnels.begin (); it != m_TransitTunnels.end ();) + { + if (ts > it->second->GetCreationTime () + TUNNEL_EXPIRATION_TIMEOUT) + { + auto tmp = it->second; + LogPrint ("Transit tunnel ", tmp->GetTunnelID (), " expired"); + { + std::unique_lock l(m_TransitTunnelsMutex); + it = m_TransitTunnels.erase (it); + } + delete tmp; + } + else + it++; + } + } - void Tunnels::ManageTunnelPools () - { - std::unique_lock l(m_PoolsMutex); - for (auto it: m_Pools) - { - auto pool = it; - if (pool && pool->IsActive ()) - { - pool->CreateTunnels (); - pool->TestTunnels (); - } - } - } - - void Tunnels::PostTunnelData (std::shared_ptr msg) - { - if (msg) m_Queue.Put (msg); - } + void Tunnels::ManageTunnelPools () + { + std::unique_lock l(m_PoolsMutex); + for (auto it: m_Pools) + { + auto pool = it; + if (pool && pool->IsActive ()) + { + pool->CreateTunnels (); + pool->TestTunnels (); + } + } + } + + void Tunnels::PostTunnelData (std::shared_ptr msg) + { + if (msg) m_Queue.Put (msg); + } - void Tunnels::PostTunnelData (const std::vector >& msgs) - { - m_Queue.Put (msgs); - } - - template - std::shared_ptr Tunnels::CreateTunnel (std::shared_ptr config, std::shared_ptr outboundTunnel) - { - auto newTunnel = std::make_shared (config); - uint32_t replyMsgID = i2p::context.GetRandomNumberGenerator ().GenerateWord32 (); - AddPendingTunnel (replyMsgID, newTunnel); - newTunnel->Build (replyMsgID, outboundTunnel); - return newTunnel; - } + void Tunnels::PostTunnelData (const std::vector >& msgs) + { + m_Queue.Put (msgs); + } + + template + std::shared_ptr Tunnels::CreateTunnel (std::shared_ptr config, std::shared_ptr outboundTunnel) + { + auto newTunnel = std::make_shared (config); + uint32_t replyMsgID = i2p::context.GetRandomNumberGenerator ().GenerateWord32 (); + AddPendingTunnel (replyMsgID, newTunnel); + newTunnel->Build (replyMsgID, outboundTunnel); + return newTunnel; + } - void Tunnels::AddPendingTunnel (uint32_t replyMsgID, std::shared_ptr tunnel) - { - m_PendingInboundTunnels[replyMsgID] = tunnel; - } + void Tunnels::AddPendingTunnel (uint32_t replyMsgID, std::shared_ptr tunnel) + { + m_PendingInboundTunnels[replyMsgID] = tunnel; + } - void Tunnels::AddPendingTunnel (uint32_t replyMsgID, std::shared_ptr tunnel) - { - m_PendingOutboundTunnels[replyMsgID] = tunnel; - } + void Tunnels::AddPendingTunnel (uint32_t replyMsgID, std::shared_ptr tunnel) + { + m_PendingOutboundTunnels[replyMsgID] = tunnel; + } - void Tunnels::AddOutboundTunnel (std::shared_ptr newTunnel) - { - m_OutboundTunnels.push_back (newTunnel); - auto pool = newTunnel->GetTunnelPool (); - if (pool && pool->IsActive ()) - pool->TunnelCreated (newTunnel); - else - newTunnel->SetTunnelPool (nullptr); - } + void Tunnels::AddOutboundTunnel (std::shared_ptr newTunnel) + { + m_OutboundTunnels.push_back (newTunnel); + auto pool = newTunnel->GetTunnelPool (); + if (pool && pool->IsActive ()) + pool->TunnelCreated (newTunnel); + else + newTunnel->SetTunnelPool (nullptr); + } - void Tunnels::AddInboundTunnel (std::shared_ptr newTunnel) - { - m_InboundTunnels[newTunnel->GetTunnelID ()] = newTunnel; - auto pool = newTunnel->GetTunnelPool (); - if (!pool) - { - // build symmetric outbound tunnel - CreateTunnel (newTunnel->GetTunnelConfig ()->Invert (), GetNextOutboundTunnel ()); - } - else - { - if (pool->IsActive ()) - pool->TunnelCreated (newTunnel); - else - newTunnel->SetTunnelPool (nullptr); - } - } + void Tunnels::AddInboundTunnel (std::shared_ptr newTunnel) + { + m_InboundTunnels[newTunnel->GetTunnelID ()] = newTunnel; + auto pool = newTunnel->GetTunnelPool (); + if (!pool) + { + // build symmetric outbound tunnel + CreateTunnel (newTunnel->GetTunnelConfig ()->Invert (), GetNextOutboundTunnel ()); + } + else + { + if (pool->IsActive ()) + pool->TunnelCreated (newTunnel); + else + newTunnel->SetTunnelPool (nullptr); + } + } - - void Tunnels::CreateZeroHopsInboundTunnel () - { - CreateTunnel ( - std::make_shared (std::vector > - { - i2p::context.GetSharedRouterInfo () - })); - } + + void Tunnels::CreateZeroHopsInboundTunnel () + { + CreateTunnel ( + std::make_shared (std::vector > + { + i2p::context.GetSharedRouterInfo () + })); + } - int Tunnels::GetTransitTunnelsExpirationTimeout () - { - int timeout = 0; - uint32_t ts = i2p::util::GetSecondsSinceEpoch (); - std::unique_lock l(m_TransitTunnelsMutex); - for (auto it: m_TransitTunnels) - { - int t = it.second->GetCreationTime () + TUNNEL_EXPIRATION_TIMEOUT - ts; - if (t > timeout) timeout = t; - } - return timeout; - } + int Tunnels::GetTransitTunnelsExpirationTimeout () + { + int timeout = 0; + uint32_t ts = i2p::util::GetSecondsSinceEpoch (); + std::unique_lock l(m_TransitTunnelsMutex); + for (auto it: m_TransitTunnels) + { + int t = it.second->GetCreationTime () + TUNNEL_EXPIRATION_TIMEOUT - ts; + if (t > timeout) timeout = t; + } + return timeout; + } } } diff --git a/Tunnel.h b/Tunnel.h index e75de74b..cc029b76 100644 --- a/Tunnel.h +++ b/Tunnel.h @@ -21,183 +21,183 @@ namespace i2p { namespace tunnel -{ - const int TUNNEL_EXPIRATION_TIMEOUT = 660; // 11 minutes - const int TUNNEL_EXPIRATION_THRESHOLD = 60; // 1 minute - const int TUNNEL_RECREATION_THRESHOLD = 90; // 1.5 minutes - const int TUNNEL_CREATION_TIMEOUT = 30; // 30 seconds - const int STANDARD_NUM_RECORDS = 5; // in VariableTunnelBuild message +{ + const int TUNNEL_EXPIRATION_TIMEOUT = 660; // 11 minutes + const int TUNNEL_EXPIRATION_THRESHOLD = 60; // 1 minute + const int TUNNEL_RECREATION_THRESHOLD = 90; // 1.5 minutes + const int TUNNEL_CREATION_TIMEOUT = 30; // 30 seconds + const int STANDARD_NUM_RECORDS = 5; // in VariableTunnelBuild message - enum TunnelState - { - eTunnelStatePending, - eTunnelStateBuildReplyReceived, - eTunnelStateBuildFailed, - eTunnelStateEstablished, - eTunnelStateTestFailed, - eTunnelStateFailed, - eTunnelStateExpiring - }; - - class OutboundTunnel; - class InboundTunnel; - class Tunnel: public TunnelBase - { - public: + enum TunnelState + { + eTunnelStatePending, + eTunnelStateBuildReplyReceived, + eTunnelStateBuildFailed, + eTunnelStateEstablished, + eTunnelStateTestFailed, + eTunnelStateFailed, + eTunnelStateExpiring + }; + + class OutboundTunnel; + class InboundTunnel; + class Tunnel: public TunnelBase + { + public: - Tunnel (std::shared_ptr config); - ~Tunnel (); + Tunnel (std::shared_ptr config); + ~Tunnel (); - void Build (uint32_t replyMsgID, std::shared_ptr outboundTunnel = nullptr); - - std::shared_ptr GetTunnelConfig () const { return m_Config; } - TunnelState GetState () const { return m_State; }; - void SetState (TunnelState state) { m_State = state; }; - bool IsEstablished () const { return m_State == eTunnelStateEstablished; }; - bool IsFailed () const { return m_State == eTunnelStateFailed; }; - bool IsRecreated () const { return m_IsRecreated; }; - void SetIsRecreated () { m_IsRecreated = true; }; + void Build (uint32_t replyMsgID, std::shared_ptr outboundTunnel = nullptr); + + std::shared_ptr GetTunnelConfig () const { return m_Config; } + TunnelState GetState () const { return m_State; }; + void SetState (TunnelState state) { m_State = state; }; + bool IsEstablished () const { return m_State == eTunnelStateEstablished; }; + bool IsFailed () const { return m_State == eTunnelStateFailed; }; + bool IsRecreated () const { return m_IsRecreated; }; + void SetIsRecreated () { m_IsRecreated = true; }; - std::shared_ptr GetTunnelPool () const { return m_Pool; }; - void SetTunnelPool (std::shared_ptr pool) { m_Pool = pool; }; - - bool HandleTunnelBuildResponse (uint8_t * msg, size_t len); - - // implements TunnelBase - void SendTunnelDataMsg (std::shared_ptr msg); - void EncryptTunnelMsg (std::shared_ptr in, std::shared_ptr out); - uint32_t GetNextTunnelID () const { return m_Config->GetFirstHop ()->tunnelID; }; - const i2p::data::IdentHash& GetNextIdentHash () const { return m_Config->GetFirstHop ()->router->GetIdentHash (); }; - - private: + std::shared_ptr GetTunnelPool () const { return m_Pool; }; + void SetTunnelPool (std::shared_ptr pool) { m_Pool = pool; }; + + bool HandleTunnelBuildResponse (uint8_t * msg, size_t len); + + // implements TunnelBase + void SendTunnelDataMsg (std::shared_ptr msg); + void EncryptTunnelMsg (std::shared_ptr in, std::shared_ptr out); + uint32_t GetNextTunnelID () const { return m_Config->GetFirstHop ()->tunnelID; }; + const i2p::data::IdentHash& GetNextIdentHash () const { return m_Config->GetFirstHop ()->router->GetIdentHash (); }; + + private: - std::shared_ptr m_Config; - std::shared_ptr m_Pool; // pool, tunnel belongs to, or null - TunnelState m_State; - bool m_IsRecreated; - }; + std::shared_ptr m_Config; + std::shared_ptr m_Pool; // pool, tunnel belongs to, or null + TunnelState m_State; + bool m_IsRecreated; + }; - class OutboundTunnel: public Tunnel - { - public: + class OutboundTunnel: public Tunnel + { + public: - OutboundTunnel (std::shared_ptr config): Tunnel (config), m_Gateway (this) {}; + OutboundTunnel (std::shared_ptr config): Tunnel (config), m_Gateway (this) {}; - void SendTunnelDataMsg (const uint8_t * gwHash, uint32_t gwTunnel, std::shared_ptr msg); - void SendTunnelDataMsg (const std::vector& msgs); // multiple messages - std::shared_ptr GetEndpointRouter () const - { return GetTunnelConfig ()->GetLastHop ()->router; }; - size_t GetNumSentBytes () const { return m_Gateway.GetNumSentBytes (); }; + void SendTunnelDataMsg (const uint8_t * gwHash, uint32_t gwTunnel, std::shared_ptr msg); + void SendTunnelDataMsg (const std::vector& msgs); // multiple messages + std::shared_ptr GetEndpointRouter () const + { return GetTunnelConfig ()->GetLastHop ()->router; }; + size_t GetNumSentBytes () const { return m_Gateway.GetNumSentBytes (); }; - // implements TunnelBase - void HandleTunnelDataMsg (std::shared_ptr tunnelMsg); - uint32_t GetTunnelID () const { return GetNextTunnelID (); }; - - private: + // implements TunnelBase + void HandleTunnelDataMsg (std::shared_ptr tunnelMsg); + uint32_t GetTunnelID () const { return GetNextTunnelID (); }; + + private: - std::mutex m_SendMutex; - TunnelGateway m_Gateway; - }; - - class InboundTunnel: public Tunnel, public std::enable_shared_from_this - { - public: + std::mutex m_SendMutex; + TunnelGateway m_Gateway; + }; + + class InboundTunnel: public Tunnel, public std::enable_shared_from_this + { + public: - InboundTunnel (std::shared_ptr config): Tunnel (config), m_Endpoint (true) {}; - void HandleTunnelDataMsg (std::shared_ptr msg); - size_t GetNumReceivedBytes () const { return m_Endpoint.GetNumReceivedBytes (); }; + InboundTunnel (std::shared_ptr config): Tunnel (config), m_Endpoint (true) {}; + void HandleTunnelDataMsg (std::shared_ptr msg); + size_t GetNumReceivedBytes () const { return m_Endpoint.GetNumReceivedBytes (); }; - // implements TunnelBase - uint32_t GetTunnelID () const { return GetTunnelConfig ()->GetLastHop ()->nextTunnelID; }; - private: + // implements TunnelBase + uint32_t GetTunnelID () const { return GetTunnelConfig ()->GetLastHop ()->nextTunnelID; }; + private: - TunnelEndpoint m_Endpoint; - }; + TunnelEndpoint m_Endpoint; + }; - - class Tunnels - { - public: + + class Tunnels + { + public: - Tunnels (); - ~Tunnels (); - void Start (); - void Stop (); - - std::shared_ptr GetInboundTunnel (uint32_t tunnelID); - std::shared_ptr GetPendingInboundTunnel (uint32_t replyMsgID); - std::shared_ptr GetPendingOutboundTunnel (uint32_t replyMsgID); - std::shared_ptr GetNextInboundTunnel (); - std::shared_ptr GetNextOutboundTunnel (); - std::shared_ptr GetExploratoryPool () const { return m_ExploratoryPool; }; - TransitTunnel * GetTransitTunnel (uint32_t tunnelID); - int GetTransitTunnelsExpirationTimeout (); - void AddTransitTunnel (TransitTunnel * tunnel); - void AddOutboundTunnel (std::shared_ptr newTunnel); - void AddInboundTunnel (std::shared_ptr newTunnel); - void PostTunnelData (std::shared_ptr msg); - void PostTunnelData (const std::vector >& msgs); - template - std::shared_ptr CreateTunnel (std::shared_ptr config, std::shared_ptr outboundTunnel = nullptr); - void AddPendingTunnel (uint32_t replyMsgID, std::shared_ptr tunnel); - void AddPendingTunnel (uint32_t replyMsgID, std::shared_ptr tunnel); - std::shared_ptr CreateTunnelPool (i2p::garlic::GarlicDestination * localDestination, int numInboundHops, int numOuboundHops, int numInboundTunnels, int numOutboundTunnels); - void DeleteTunnelPool (std::shared_ptr pool); - void StopTunnelPool (std::shared_ptr pool); - - private: - - template - std::shared_ptr GetPendingTunnel (uint32_t replyMsgID, const std::map >& pendingTunnels); + Tunnels (); + ~Tunnels (); + void Start (); + void Stop (); + + std::shared_ptr GetInboundTunnel (uint32_t tunnelID); + std::shared_ptr GetPendingInboundTunnel (uint32_t replyMsgID); + std::shared_ptr GetPendingOutboundTunnel (uint32_t replyMsgID); + std::shared_ptr GetNextInboundTunnel (); + std::shared_ptr GetNextOutboundTunnel (); + std::shared_ptr GetExploratoryPool () const { return m_ExploratoryPool; }; + TransitTunnel * GetTransitTunnel (uint32_t tunnelID); + int GetTransitTunnelsExpirationTimeout (); + void AddTransitTunnel (TransitTunnel * tunnel); + void AddOutboundTunnel (std::shared_ptr newTunnel); + void AddInboundTunnel (std::shared_ptr newTunnel); + void PostTunnelData (std::shared_ptr msg); + void PostTunnelData (const std::vector >& msgs); + template + std::shared_ptr CreateTunnel (std::shared_ptr config, std::shared_ptr outboundTunnel = nullptr); + void AddPendingTunnel (uint32_t replyMsgID, std::shared_ptr tunnel); + void AddPendingTunnel (uint32_t replyMsgID, std::shared_ptr tunnel); + std::shared_ptr CreateTunnelPool (i2p::garlic::GarlicDestination * localDestination, int numInboundHops, int numOuboundHops, int numInboundTunnels, int numOutboundTunnels); + void DeleteTunnelPool (std::shared_ptr pool); + void StopTunnelPool (std::shared_ptr pool); + + private: + + template + std::shared_ptr GetPendingTunnel (uint32_t replyMsgID, const std::map >& pendingTunnels); - void HandleTunnelGatewayMsg (TunnelBase * tunnel, std::shared_ptr msg); + void HandleTunnelGatewayMsg (TunnelBase * tunnel, std::shared_ptr msg); - void Run (); - void ManageTunnels (); - void ManageOutboundTunnels (); - void ManageInboundTunnels (); - void ManageTransitTunnels (); - void ManagePendingTunnels (); - template - void ManagePendingTunnels (PendingTunnels& pendingTunnels); - void ManageTunnelPools (); - - void CreateZeroHopsInboundTunnel (); - - private: + void Run (); + void ManageTunnels (); + void ManageOutboundTunnels (); + void ManageInboundTunnels (); + void ManageTransitTunnels (); + void ManagePendingTunnels (); + template + void ManagePendingTunnels (PendingTunnels& pendingTunnels); + void ManageTunnelPools (); + + void CreateZeroHopsInboundTunnel (); + + private: - bool m_IsRunning; - std::thread * m_Thread; - std::map > m_PendingInboundTunnels; // by replyMsgID - std::map > m_PendingOutboundTunnels; // by replyMsgID - std::map > m_InboundTunnels; - std::list > m_OutboundTunnels; - std::mutex m_TransitTunnelsMutex; - std::map m_TransitTunnels; - std::mutex m_PoolsMutex; - std::list> m_Pools; - std::shared_ptr m_ExploratoryPool; - i2p::util::Queue > m_Queue; + bool m_IsRunning; + std::thread * m_Thread; + std::map > m_PendingInboundTunnels; // by replyMsgID + std::map > m_PendingOutboundTunnels; // by replyMsgID + std::map > m_InboundTunnels; + std::list > m_OutboundTunnels; + std::mutex m_TransitTunnelsMutex; + std::map m_TransitTunnels; + std::mutex m_PoolsMutex; + std::list> m_Pools; + std::shared_ptr m_ExploratoryPool; + i2p::util::Queue > m_Queue; - // some stats - int m_NumSuccesiveTunnelCreations, m_NumFailedTunnelCreations; + // some stats + int m_NumSuccesiveTunnelCreations, m_NumFailedTunnelCreations; - public: + public: - // for HTTP only - const decltype(m_OutboundTunnels)& GetOutboundTunnels () const { return m_OutboundTunnels; }; - const decltype(m_InboundTunnels)& GetInboundTunnels () const { return m_InboundTunnels; }; - const decltype(m_TransitTunnels)& GetTransitTunnels () const { return m_TransitTunnels; }; - int GetQueueSize () { return m_Queue.GetSize (); }; - int GetTunnelCreationSuccessRate () const // in percents - { - int totalNum = m_NumSuccesiveTunnelCreations + m_NumFailedTunnelCreations; - return totalNum ? m_NumSuccesiveTunnelCreations*100/totalNum : 0; - } - }; + // for HTTP only + const decltype(m_OutboundTunnels)& GetOutboundTunnels () const { return m_OutboundTunnels; }; + const decltype(m_InboundTunnels)& GetInboundTunnels () const { return m_InboundTunnels; }; + const decltype(m_TransitTunnels)& GetTransitTunnels () const { return m_TransitTunnels; }; + int GetQueueSize () { return m_Queue.GetSize (); }; + int GetTunnelCreationSuccessRate () const // in percents + { + int totalNum = m_NumSuccesiveTunnelCreations + m_NumFailedTunnelCreations; + return totalNum ? m_NumSuccesiveTunnelCreations*100/totalNum : 0; + } + }; - extern Tunnels tunnels; -} + extern Tunnels tunnels; +} } #endif diff --git a/TunnelBase.h b/TunnelBase.h index 76175d0a..c7d6b536 100644 --- a/TunnelBase.h +++ b/TunnelBase.h @@ -11,58 +11,58 @@ namespace i2p { namespace tunnel { - const size_t TUNNEL_DATA_MSG_SIZE = 1028; - const size_t TUNNEL_DATA_ENCRYPTED_SIZE = 1008; - const size_t TUNNEL_DATA_MAX_PAYLOAD_SIZE = 1003; - - enum TunnelDeliveryType - { - eDeliveryTypeLocal = 0, - eDeliveryTypeTunnel = 1, - eDeliveryTypeRouter = 2 - }; - struct TunnelMessageBlock - { - TunnelDeliveryType deliveryType; - i2p::data::IdentHash hash; - uint32_t tunnelID; - std::shared_ptr data; - }; + const size_t TUNNEL_DATA_MSG_SIZE = 1028; + const size_t TUNNEL_DATA_ENCRYPTED_SIZE = 1008; + const size_t TUNNEL_DATA_MAX_PAYLOAD_SIZE = 1003; + + enum TunnelDeliveryType + { + eDeliveryTypeLocal = 0, + eDeliveryTypeTunnel = 1, + eDeliveryTypeRouter = 2 + }; + struct TunnelMessageBlock + { + TunnelDeliveryType deliveryType; + i2p::data::IdentHash hash; + uint32_t tunnelID; + std::shared_ptr data; + }; - class TunnelBase - { - public: + class TunnelBase + { + public: - //WARNING!!! GetSecondsSinceEpoch() return uint64_t - TunnelBase (): m_CreationTime (i2p::util::GetSecondsSinceEpoch ()) {}; - virtual ~TunnelBase () {}; - - virtual void HandleTunnelDataMsg (std::shared_ptr tunnelMsg) = 0; - virtual void SendTunnelDataMsg (std::shared_ptr msg) = 0; - virtual void FlushTunnelDataMsgs () {}; - virtual void EncryptTunnelMsg (std::shared_ptr in, std::shared_ptr out) = 0; - virtual uint32_t GetNextTunnelID () const = 0; - virtual const i2p::data::IdentHash& GetNextIdentHash () const = 0; - virtual uint32_t GetTunnelID () const = 0; // as known at our side + //WARNING!!! GetSecondsSinceEpoch() return uint64_t + TunnelBase (): m_CreationTime (i2p::util::GetSecondsSinceEpoch ()) {}; + virtual ~TunnelBase () {}; + + virtual void HandleTunnelDataMsg (std::shared_ptr tunnelMsg) = 0; + virtual void SendTunnelDataMsg (std::shared_ptr msg) = 0; + virtual void FlushTunnelDataMsgs () {}; + virtual void EncryptTunnelMsg (std::shared_ptr in, std::shared_ptr out) = 0; + virtual uint32_t GetNextTunnelID () const = 0; + virtual const i2p::data::IdentHash& GetNextIdentHash () const = 0; + virtual uint32_t GetTunnelID () const = 0; // as known at our side - uint32_t GetCreationTime () const { return m_CreationTime; }; - void SetCreationTime (uint32_t t) { m_CreationTime = t; }; + uint32_t GetCreationTime () const { return m_CreationTime; }; + void SetCreationTime (uint32_t t) { m_CreationTime = t; }; - private: - - uint32_t m_CreationTime; // seconds since epoch - }; + private: + + uint32_t m_CreationTime; // seconds since epoch + }; - struct TunnelCreationTimeCmp - { - bool operator() (std::shared_ptr t1, std::shared_ptr t2) const - { - if (t1->GetCreationTime () != t2->GetCreationTime ()) - return t1->GetCreationTime () > t2->GetCreationTime (); - else - return t1 < t2; - }; - }; + struct TunnelCreationTimeCmp + { + bool operator() (std::shared_ptr t1, std::shared_ptr t2) const + { + if (t1->GetCreationTime () != t2->GetCreationTime ()) + return t1->GetCreationTime () > t2->GetCreationTime (); + else + return t1 < t2; + }; + }; } } diff --git a/TunnelConfig.h b/TunnelConfig.h index 697005fd..d3ab5ed2 100644 --- a/TunnelConfig.h +++ b/TunnelConfig.h @@ -14,220 +14,220 @@ namespace i2p { namespace tunnel { - struct TunnelHopConfig - { - std::shared_ptr router, nextRouter; - uint32_t tunnelID, nextTunnelID; - uint8_t layerKey[32]; - uint8_t ivKey[32]; - uint8_t replyKey[32]; - uint8_t replyIV[16]; - bool isGateway, isEndpoint; - - TunnelHopConfig * next, * prev; - i2p::crypto::TunnelDecryption decryption; - int recordIndex; // record # in tunnel build message - - TunnelHopConfig (std::shared_ptr r) - { - CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator (); - rnd.GenerateBlock (layerKey, 32); - rnd.GenerateBlock (ivKey, 32); - rnd.GenerateBlock (replyIV, 16); - tunnelID = rnd.GenerateWord32 (); - isGateway = true; - isEndpoint = true; - router = r; - //nextRouter = nullptr; - nextTunnelID = 0; + struct TunnelHopConfig + { + std::shared_ptr router, nextRouter; + uint32_t tunnelID, nextTunnelID; + uint8_t layerKey[32]; + uint8_t ivKey[32]; + uint8_t replyKey[32]; + uint8_t replyIV[16]; + bool isGateway, isEndpoint; + + TunnelHopConfig * next, * prev; + i2p::crypto::TunnelDecryption decryption; + int recordIndex; // record # in tunnel build message + + TunnelHopConfig (std::shared_ptr r) + { + CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator (); + rnd.GenerateBlock (layerKey, 32); + rnd.GenerateBlock (ivKey, 32); + rnd.GenerateBlock (replyIV, 16); + tunnelID = rnd.GenerateWord32 (); + isGateway = true; + isEndpoint = true; + router = r; + //nextRouter = nullptr; + nextTunnelID = 0; - next = nullptr; - prev = nullptr; - } + next = nullptr; + prev = nullptr; + } - void SetNextRouter (std::shared_ptr r) - { - nextRouter = r; - isEndpoint = false; - CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator (); - nextTunnelID = rnd.GenerateWord32 (); - } + void SetNextRouter (std::shared_ptr r) + { + nextRouter = r; + isEndpoint = false; + CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator (); + nextTunnelID = rnd.GenerateWord32 (); + } - void SetReplyHop (const TunnelHopConfig * replyFirstHop) - { - nextRouter = replyFirstHop->router; - nextTunnelID = replyFirstHop->tunnelID; - isEndpoint = true; - } - - void SetNext (TunnelHopConfig * n) - { - next = n; - if (next) - { - next->prev = this; - next->isGateway = false; - isEndpoint = false; - nextRouter = next->router; - nextTunnelID = next->tunnelID; - } - } + void SetReplyHop (const TunnelHopConfig * replyFirstHop) + { + nextRouter = replyFirstHop->router; + nextTunnelID = replyFirstHop->tunnelID; + isEndpoint = true; + } + + void SetNext (TunnelHopConfig * n) + { + next = n; + if (next) + { + next->prev = this; + next->isGateway = false; + isEndpoint = false; + nextRouter = next->router; + nextTunnelID = next->tunnelID; + } + } - void SetPrev (TunnelHopConfig * p) - { - prev = p; - if (prev) - { - prev->next = this; - prev->isEndpoint = false; - isGateway = false; - } - } + void SetPrev (TunnelHopConfig * p) + { + prev = p; + if (prev) + { + prev->next = this; + prev->isEndpoint = false; + isGateway = false; + } + } - void CreateBuildRequestRecord (uint8_t * record, uint32_t replyMsgID) const - { - uint8_t clearText[BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE] = {}; - htobe32buf (clearText + BUILD_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET, tunnelID); - memcpy (clearText + BUILD_REQUEST_RECORD_OUR_IDENT_OFFSET, router->GetIdentHash (), 32); - htobe32buf (clearText + BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET, nextTunnelID); - memcpy (clearText + BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET, nextRouter->GetIdentHash (), 32); - memcpy (clearText + BUILD_REQUEST_RECORD_LAYER_KEY_OFFSET, layerKey, 32); - memcpy (clearText + BUILD_REQUEST_RECORD_IV_KEY_OFFSET, ivKey, 32); - memcpy (clearText + BUILD_REQUEST_RECORD_REPLY_KEY_OFFSET, replyKey, 32); - memcpy (clearText + BUILD_REQUEST_RECORD_REPLY_IV_OFFSET, replyIV, 16); - uint8_t flag = 0; - if (isGateway) flag |= 0x80; - if (isEndpoint) flag |= 0x40; - clearText[BUILD_REQUEST_RECORD_FLAG_OFFSET] = flag; - htobe32buf (clearText + BUILD_REQUEST_RECORD_REQUEST_TIME_OFFSET, i2p::util::GetHoursSinceEpoch ()); - htobe32buf (clearText + BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET, replyMsgID); - // TODO: fill padding - router->GetElGamalEncryption ()->Encrypt (clearText, BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE, record + BUILD_REQUEST_RECORD_ENCRYPTED_OFFSET); - memcpy (record + BUILD_REQUEST_RECORD_TO_PEER_OFFSET, (const uint8_t *)router->GetIdentHash (), 16); - } - }; + void CreateBuildRequestRecord (uint8_t * record, uint32_t replyMsgID) const + { + uint8_t clearText[BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE] = {}; + htobe32buf (clearText + BUILD_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET, tunnelID); + memcpy (clearText + BUILD_REQUEST_RECORD_OUR_IDENT_OFFSET, router->GetIdentHash (), 32); + htobe32buf (clearText + BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET, nextTunnelID); + memcpy (clearText + BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET, nextRouter->GetIdentHash (), 32); + memcpy (clearText + BUILD_REQUEST_RECORD_LAYER_KEY_OFFSET, layerKey, 32); + memcpy (clearText + BUILD_REQUEST_RECORD_IV_KEY_OFFSET, ivKey, 32); + memcpy (clearText + BUILD_REQUEST_RECORD_REPLY_KEY_OFFSET, replyKey, 32); + memcpy (clearText + BUILD_REQUEST_RECORD_REPLY_IV_OFFSET, replyIV, 16); + uint8_t flag = 0; + if (isGateway) flag |= 0x80; + if (isEndpoint) flag |= 0x40; + clearText[BUILD_REQUEST_RECORD_FLAG_OFFSET] = flag; + htobe32buf (clearText + BUILD_REQUEST_RECORD_REQUEST_TIME_OFFSET, i2p::util::GetHoursSinceEpoch ()); + htobe32buf (clearText + BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET, replyMsgID); + // TODO: fill padding + router->GetElGamalEncryption ()->Encrypt (clearText, BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE, record + BUILD_REQUEST_RECORD_ENCRYPTED_OFFSET); + memcpy (record + BUILD_REQUEST_RECORD_TO_PEER_OFFSET, (const uint8_t *)router->GetIdentHash (), 16); + } + }; - class TunnelConfig: public std::enable_shared_from_this - { - public: - + class TunnelConfig: public std::enable_shared_from_this + { + public: + - TunnelConfig (std::vector > peers, - std::shared_ptr replyTunnelConfig = nullptr) // replyTunnelConfig=nullptr means inbound - { - TunnelHopConfig * prev = nullptr; - for (auto it: peers) - { - auto hop = new TunnelHopConfig (it); - if (prev) - prev->SetNext (hop); - else - m_FirstHop = hop; - prev = hop; - } - m_LastHop = prev; - - if (replyTunnelConfig) // outbound - { - m_FirstHop->isGateway = false; - m_LastHop->SetReplyHop (replyTunnelConfig->GetFirstHop ()); - } - else // inbound - m_LastHop->SetNextRouter (i2p::context.GetSharedRouterInfo ()); - } - - ~TunnelConfig () - { - TunnelHopConfig * hop = m_FirstHop; - - while (hop) - { - auto tmp = hop; - hop = hop->next; - delete tmp; - } - } - - TunnelHopConfig * GetFirstHop () const - { - return m_FirstHop; - } + TunnelConfig (std::vector > peers, + std::shared_ptr replyTunnelConfig = nullptr) // replyTunnelConfig=nullptr means inbound + { + TunnelHopConfig * prev = nullptr; + for (auto it: peers) + { + auto hop = new TunnelHopConfig (it); + if (prev) + prev->SetNext (hop); + else + m_FirstHop = hop; + prev = hop; + } + m_LastHop = prev; + + if (replyTunnelConfig) // outbound + { + m_FirstHop->isGateway = false; + m_LastHop->SetReplyHop (replyTunnelConfig->GetFirstHop ()); + } + else // inbound + m_LastHop->SetNextRouter (i2p::context.GetSharedRouterInfo ()); + } + + ~TunnelConfig () + { + TunnelHopConfig * hop = m_FirstHop; + + while (hop) + { + auto tmp = hop; + hop = hop->next; + delete tmp; + } + } + + TunnelHopConfig * GetFirstHop () const + { + return m_FirstHop; + } - TunnelHopConfig * GetLastHop () const - { - return m_LastHop; - } + TunnelHopConfig * GetLastHop () const + { + return m_LastHop; + } - int GetNumHops () const - { - int num = 0; - TunnelHopConfig * hop = m_FirstHop; - while (hop) - { - num++; - hop = hop->next; - } - return num; - } + int GetNumHops () const + { + int num = 0; + TunnelHopConfig * hop = m_FirstHop; + while (hop) + { + num++; + hop = hop->next; + } + return num; + } - bool IsInbound () const { return m_FirstHop->isGateway; } + bool IsInbound () const { return m_FirstHop->isGateway; } - std::vector > GetPeers () const - { - std::vector > peers; - TunnelHopConfig * hop = m_FirstHop; - while (hop) - { - peers.push_back (hop->router); - hop = hop->next; - } - return peers; - } + std::vector > GetPeers () const + { + std::vector > peers; + TunnelHopConfig * hop = m_FirstHop; + while (hop) + { + peers.push_back (hop->router); + hop = hop->next; + } + return peers; + } - void Print (std::stringstream& s) const - { - TunnelHopConfig * hop = m_FirstHop; - if (!IsInbound ()) // outbound - s << "me"; - s << "-->" << m_FirstHop->tunnelID; - while (hop) - { - s << ":" << hop->router->GetIdentHashAbbreviation () << "-->"; - if (!hop->isEndpoint) - s << hop->nextTunnelID; - else - return; - hop = hop->next; - } - // we didn't reach enpoint that mean we are last hop - s << ":me"; - } + void Print (std::stringstream& s) const + { + TunnelHopConfig * hop = m_FirstHop; + if (!IsInbound ()) // outbound + s << "me"; + s << "-->" << m_FirstHop->tunnelID; + while (hop) + { + s << ":" << hop->router->GetIdentHashAbbreviation () << "-->"; + if (!hop->isEndpoint) + s << hop->nextTunnelID; + else + return; + hop = hop->next; + } + // we didn't reach enpoint that mean we are last hop + s << ":me"; + } - std::shared_ptr Invert () const - { - auto peers = GetPeers (); - std::reverse (peers.begin (), peers.end ()); - // we use ourself as reply tunnel for outbound tunnel - return IsInbound () ? std::make_shared(peers, shared_from_this ()) : std::make_shared(peers); - } + std::shared_ptr Invert () const + { + auto peers = GetPeers (); + std::reverse (peers.begin (), peers.end ()); + // we use ourself as reply tunnel for outbound tunnel + return IsInbound () ? std::make_shared(peers, shared_from_this ()) : std::make_shared(peers); + } - std::shared_ptr Clone (std::shared_ptr replyTunnelConfig = nullptr) const - { - return std::make_shared (GetPeers (), replyTunnelConfig); - } - - private: + std::shared_ptr Clone (std::shared_ptr replyTunnelConfig = nullptr) const + { + return std::make_shared (GetPeers (), replyTunnelConfig); + } + + private: - // this constructor can't be called from outside - TunnelConfig (): m_FirstHop (nullptr), m_LastHop (nullptr) - { - } - - private: + // this constructor can't be called from outside + TunnelConfig (): m_FirstHop (nullptr), m_LastHop (nullptr) + { + } + + private: - TunnelHopConfig * m_FirstHop, * m_LastHop; - }; -} -} + TunnelHopConfig * m_FirstHop, * m_LastHop; + }; +} +} #endif diff --git a/TunnelEndpoint.cpp b/TunnelEndpoint.cpp index 9d1425e1..24dd0bb1 100644 --- a/TunnelEndpoint.cpp +++ b/TunnelEndpoint.cpp @@ -11,249 +11,249 @@ namespace i2p { namespace tunnel { - TunnelEndpoint::~TunnelEndpoint () - { - } - - void TunnelEndpoint::HandleDecryptedTunnelDataMsg (std::shared_ptr msg) - { - m_NumReceivedBytes += TUNNEL_DATA_MSG_SIZE; - - uint8_t * decrypted = msg->GetPayload () + 20; // 4 + 16 - uint8_t * zero = (uint8_t *)memchr (decrypted + 4, 0, TUNNEL_DATA_ENCRYPTED_SIZE - 4); // witout 4-byte checksum - if (zero) - { - uint8_t * fragment = zero + 1; - // verify checksum - memcpy (msg->GetPayload () + TUNNEL_DATA_MSG_SIZE, msg->GetPayload () + 4, 16); // copy iv to the end - uint8_t hash[32]; - CryptoPP::SHA256().CalculateDigest (hash, fragment, TUNNEL_DATA_MSG_SIZE -(fragment - msg->GetPayload ()) + 16); // payload + iv - if (memcmp (hash, decrypted, 4)) - { - LogPrint (eLogError, "TunnelMessage: checksum verification failed"); - return; - } - // process fragments - while (fragment < decrypted + TUNNEL_DATA_ENCRYPTED_SIZE) - { - uint8_t flag = fragment[0]; - fragment++; - - bool isFollowOnFragment = flag & 0x80, isLastFragment = true; - uint32_t msgID = 0; - int fragmentNum = 0; - TunnelMessageBlockEx m; - if (!isFollowOnFragment) - { - // first fragment - - m.deliveryType = (TunnelDeliveryType)((flag >> 5) & 0x03); - switch (m.deliveryType) - { - case eDeliveryTypeLocal: // 0 - break; - case eDeliveryTypeTunnel: // 1 - m.tunnelID = bufbe32toh (fragment); - fragment += 4; // tunnelID - m.hash = i2p::data::IdentHash (fragment); - fragment += 32; // hash - break; - case eDeliveryTypeRouter: // 2 - m.hash = i2p::data::IdentHash (fragment); - fragment += 32; // to hash - break; - default: - ; - } + TunnelEndpoint::~TunnelEndpoint () + { + } + + void TunnelEndpoint::HandleDecryptedTunnelDataMsg (std::shared_ptr msg) + { + m_NumReceivedBytes += TUNNEL_DATA_MSG_SIZE; + + uint8_t * decrypted = msg->GetPayload () + 20; // 4 + 16 + uint8_t * zero = (uint8_t *)memchr (decrypted + 4, 0, TUNNEL_DATA_ENCRYPTED_SIZE - 4); // witout 4-byte checksum + if (zero) + { + uint8_t * fragment = zero + 1; + // verify checksum + memcpy (msg->GetPayload () + TUNNEL_DATA_MSG_SIZE, msg->GetPayload () + 4, 16); // copy iv to the end + uint8_t hash[32]; + CryptoPP::SHA256().CalculateDigest (hash, fragment, TUNNEL_DATA_MSG_SIZE -(fragment - msg->GetPayload ()) + 16); // payload + iv + if (memcmp (hash, decrypted, 4)) + { + LogPrint (eLogError, "TunnelMessage: checksum verification failed"); + return; + } + // process fragments + while (fragment < decrypted + TUNNEL_DATA_ENCRYPTED_SIZE) + { + uint8_t flag = fragment[0]; + fragment++; + + bool isFollowOnFragment = flag & 0x80, isLastFragment = true; + uint32_t msgID = 0; + int fragmentNum = 0; + TunnelMessageBlockEx m; + if (!isFollowOnFragment) + { + // first fragment + + m.deliveryType = (TunnelDeliveryType)((flag >> 5) & 0x03); + switch (m.deliveryType) + { + case eDeliveryTypeLocal: // 0 + break; + case eDeliveryTypeTunnel: // 1 + m.tunnelID = bufbe32toh (fragment); + fragment += 4; // tunnelID + m.hash = i2p::data::IdentHash (fragment); + fragment += 32; // hash + break; + case eDeliveryTypeRouter: // 2 + m.hash = i2p::data::IdentHash (fragment); + fragment += 32; // to hash + break; + default: + ; + } - bool isFragmented = flag & 0x08; - if (isFragmented) - { - // Message ID - msgID = bufbe32toh (fragment); - fragment += 4; - isLastFragment = false; - } - } - else - { - // follow on - msgID = bufbe32toh (fragment); // MessageID - fragment += 4; - fragmentNum = (flag >> 1) & 0x3F; // 6 bits - isLastFragment = flag & 0x01; - } - - uint16_t size = bufbe16toh (fragment); - fragment += 2; + bool isFragmented = flag & 0x08; + if (isFragmented) + { + // Message ID + msgID = bufbe32toh (fragment); + fragment += 4; + isLastFragment = false; + } + } + else + { + // follow on + msgID = bufbe32toh (fragment); // MessageID + fragment += 4; + fragmentNum = (flag >> 1) & 0x3F; // 6 bits + isLastFragment = flag & 0x01; + } + + uint16_t size = bufbe16toh (fragment); + fragment += 2; - msg->offset = fragment - msg->buf; - msg->len = msg->offset + size; - if (fragment + size < decrypted + TUNNEL_DATA_ENCRYPTED_SIZE) - { - // this is not last message. we have to copy it - m.data = ToSharedI2NPMessage (NewI2NPShortMessage ()); - m.data->offset += TUNNEL_GATEWAY_HEADER_SIZE; // reserve room for TunnelGateway header - m.data->len += TUNNEL_GATEWAY_HEADER_SIZE; - *(m.data) = *msg; - } - else - m.data = msg; - - if (!isFollowOnFragment && isLastFragment) - HandleNextMessage (m); - else - { - if (msgID) // msgID is presented, assume message is fragmented - { - if (!isFollowOnFragment) // create new incomlete message - { - m.nextFragmentNum = 1; - auto ret = m_IncompleteMessages.insert (std::pair(msgID, m)); - if (ret.second) - HandleOutOfSequenceFragment (msgID, ret.first->second); - else - LogPrint (eLogError, "Incomplete message ", msgID, "already exists"); - } - else - { - m.nextFragmentNum = fragmentNum; - HandleFollowOnFragment (msgID, isLastFragment, m); - } - } - else - LogPrint (eLogError, "Message is fragmented, but msgID is not presented"); - } - - fragment += size; - } - } - else - LogPrint (eLogError, "TunnelMessage: zero not found"); - } + msg->offset = fragment - msg->buf; + msg->len = msg->offset + size; + if (fragment + size < decrypted + TUNNEL_DATA_ENCRYPTED_SIZE) + { + // this is not last message. we have to copy it + m.data = ToSharedI2NPMessage (NewI2NPShortMessage ()); + m.data->offset += TUNNEL_GATEWAY_HEADER_SIZE; // reserve room for TunnelGateway header + m.data->len += TUNNEL_GATEWAY_HEADER_SIZE; + *(m.data) = *msg; + } + else + m.data = msg; + + if (!isFollowOnFragment && isLastFragment) + HandleNextMessage (m); + else + { + if (msgID) // msgID is presented, assume message is fragmented + { + if (!isFollowOnFragment) // create new incomlete message + { + m.nextFragmentNum = 1; + auto ret = m_IncompleteMessages.insert (std::pair(msgID, m)); + if (ret.second) + HandleOutOfSequenceFragment (msgID, ret.first->second); + else + LogPrint (eLogError, "Incomplete message ", msgID, "already exists"); + } + else + { + m.nextFragmentNum = fragmentNum; + HandleFollowOnFragment (msgID, isLastFragment, m); + } + } + else + LogPrint (eLogError, "Message is fragmented, but msgID is not presented"); + } + + fragment += size; + } + } + else + LogPrint (eLogError, "TunnelMessage: zero not found"); + } - void TunnelEndpoint::HandleFollowOnFragment (uint32_t msgID, bool isLastFragment, const TunnelMessageBlockEx& m) - { - auto fragment = m.data->GetBuffer (); - auto size = m.data->GetLength (); - auto it = m_IncompleteMessages.find (msgID); - if (it != m_IncompleteMessages.end()) - { - auto& msg = it->second; - if (m.nextFragmentNum == msg.nextFragmentNum) - { - if (msg.data->len + size < I2NP_MAX_MESSAGE_SIZE) // check if message is not too long - { - if (msg.data->len + size > msg.data->maxLen) - { - LogPrint (eLogInfo, "Tunnel endpoint I2NP message size ", msg.data->maxLen, " is not enough"); - auto newMsg = ToSharedI2NPMessage (NewI2NPMessage ()); - *newMsg = *(msg.data); - msg.data = newMsg; - } - memcpy (msg.data->buf + msg.data->len, fragment, size); // concatenate fragment - msg.data->len += size; - if (isLastFragment) - { - // message complete - HandleNextMessage (msg); - m_IncompleteMessages.erase (it); - } - else - { - msg.nextFragmentNum++; - HandleOutOfSequenceFragment (msgID, msg); - } - } - else - { - LogPrint (eLogError, "Fragment ", m.nextFragmentNum, " of message ", msgID, "exceeds max I2NP message size. Message dropped"); - m_IncompleteMessages.erase (it); - } - } - else - { - LogPrint (eLogInfo, "Unexpected fragment ", (int)m.nextFragmentNum, " instead ", (int)msg.nextFragmentNum, " of message ", msgID, ". Saved"); - AddOutOfSequenceFragment (msgID, m.nextFragmentNum, isLastFragment, m.data); - } - } - else - { - LogPrint (eLogInfo, "First fragment of message ", msgID, " not found. Saved"); - AddOutOfSequenceFragment (msgID, m.nextFragmentNum, isLastFragment, m.data); - } - } + void TunnelEndpoint::HandleFollowOnFragment (uint32_t msgID, bool isLastFragment, const TunnelMessageBlockEx& m) + { + auto fragment = m.data->GetBuffer (); + auto size = m.data->GetLength (); + auto it = m_IncompleteMessages.find (msgID); + if (it != m_IncompleteMessages.end()) + { + auto& msg = it->second; + if (m.nextFragmentNum == msg.nextFragmentNum) + { + if (msg.data->len + size < I2NP_MAX_MESSAGE_SIZE) // check if message is not too long + { + if (msg.data->len + size > msg.data->maxLen) + { + LogPrint (eLogInfo, "Tunnel endpoint I2NP message size ", msg.data->maxLen, " is not enough"); + auto newMsg = ToSharedI2NPMessage (NewI2NPMessage ()); + *newMsg = *(msg.data); + msg.data = newMsg; + } + memcpy (msg.data->buf + msg.data->len, fragment, size); // concatenate fragment + msg.data->len += size; + if (isLastFragment) + { + // message complete + HandleNextMessage (msg); + m_IncompleteMessages.erase (it); + } + else + { + msg.nextFragmentNum++; + HandleOutOfSequenceFragment (msgID, msg); + } + } + else + { + LogPrint (eLogError, "Fragment ", m.nextFragmentNum, " of message ", msgID, "exceeds max I2NP message size. Message dropped"); + m_IncompleteMessages.erase (it); + } + } + else + { + LogPrint (eLogInfo, "Unexpected fragment ", (int)m.nextFragmentNum, " instead ", (int)msg.nextFragmentNum, " of message ", msgID, ". Saved"); + AddOutOfSequenceFragment (msgID, m.nextFragmentNum, isLastFragment, m.data); + } + } + else + { + LogPrint (eLogInfo, "First fragment of message ", msgID, " not found. Saved"); + AddOutOfSequenceFragment (msgID, m.nextFragmentNum, isLastFragment, m.data); + } + } - void TunnelEndpoint::AddOutOfSequenceFragment (uint32_t msgID, uint8_t fragmentNum, bool isLastFragment, std::shared_ptr data) - { - auto it = m_OutOfSequenceFragments.find (msgID); - if (it == m_OutOfSequenceFragments.end ()) - m_OutOfSequenceFragments.insert (std::pair (msgID, {fragmentNum, isLastFragment, data})); - } + void TunnelEndpoint::AddOutOfSequenceFragment (uint32_t msgID, uint8_t fragmentNum, bool isLastFragment, std::shared_ptr data) + { + auto it = m_OutOfSequenceFragments.find (msgID); + if (it == m_OutOfSequenceFragments.end ()) + m_OutOfSequenceFragments.insert (std::pair (msgID, {fragmentNum, isLastFragment, data})); + } - void TunnelEndpoint::HandleOutOfSequenceFragment (uint32_t msgID, TunnelMessageBlockEx& msg) - { - auto it = m_OutOfSequenceFragments.find (msgID); - if (it != m_OutOfSequenceFragments.end ()) - { - if (it->second.fragmentNum == msg.nextFragmentNum) - { - LogPrint (eLogInfo, "Out-of-sequence fragment ", (int)it->second.fragmentNum, " of message ", msgID, " found"); - auto size = it->second.data->GetLength (); - if (msg.data->len + size > msg.data->maxLen) - { - LogPrint (eLogInfo, "Tunnel endpoint I2NP message size ", msg.data->maxLen, " is not enough"); - auto newMsg = ToSharedI2NPMessage (NewI2NPMessage ()); - *newMsg = *(msg.data); - msg.data = newMsg; - } - memcpy (msg.data->buf + msg.data->len, it->second.data->GetBuffer (), size); // concatenate out-of-sync fragment - msg.data->len += size; - if (it->second.isLastFragment) - { - // message complete - HandleNextMessage (msg); - m_IncompleteMessages.erase (msgID); - } - else - msg.nextFragmentNum++; - m_OutOfSequenceFragments.erase (it); - } - } - } - - void TunnelEndpoint::HandleNextMessage (const TunnelMessageBlock& msg) - { - LogPrint (eLogInfo, "TunnelMessage: handle fragment of ", msg.data->GetLength ()," bytes. Msg type ", (int)msg.data->GetTypeID ()); - switch (msg.deliveryType) - { - case eDeliveryTypeLocal: - i2p::HandleI2NPMessage (msg.data); - break; - case eDeliveryTypeTunnel: - i2p::transport::transports.SendMessage (msg.hash, i2p::CreateTunnelGatewayMsg (msg.tunnelID, msg.data)); - break; - case eDeliveryTypeRouter: - if (msg.hash == i2p::context.GetRouterInfo ().GetIdentHash ()) // check if message is sent to us - i2p::HandleI2NPMessage (msg.data); - else - { - // to somebody else - if (!m_IsInbound) // outbound transit tunnel - { - /* auto typeID = msg.data->GetTypeID (); - if (typeID == eI2NPDatabaseStore || typeID == eI2NPDatabaseSearchReply ) - // catch RI or reply with new list of routers - i2p::data::netdb.PostI2NPMsg (msg.data);*/ - i2p::transport::transports.SendMessage (msg.hash, msg.data); - } - else // we shouldn't send this message. possible leakage - LogPrint (eLogError, "Message to another router arrived from an inbound tunnel. Dropped"); - } - break; - default: - LogPrint (eLogError, "TunnelMessage: Unknown delivery type ", (int)msg.deliveryType); - }; - } -} + void TunnelEndpoint::HandleOutOfSequenceFragment (uint32_t msgID, TunnelMessageBlockEx& msg) + { + auto it = m_OutOfSequenceFragments.find (msgID); + if (it != m_OutOfSequenceFragments.end ()) + { + if (it->second.fragmentNum == msg.nextFragmentNum) + { + LogPrint (eLogInfo, "Out-of-sequence fragment ", (int)it->second.fragmentNum, " of message ", msgID, " found"); + auto size = it->second.data->GetLength (); + if (msg.data->len + size > msg.data->maxLen) + { + LogPrint (eLogInfo, "Tunnel endpoint I2NP message size ", msg.data->maxLen, " is not enough"); + auto newMsg = ToSharedI2NPMessage (NewI2NPMessage ()); + *newMsg = *(msg.data); + msg.data = newMsg; + } + memcpy (msg.data->buf + msg.data->len, it->second.data->GetBuffer (), size); // concatenate out-of-sync fragment + msg.data->len += size; + if (it->second.isLastFragment) + { + // message complete + HandleNextMessage (msg); + m_IncompleteMessages.erase (msgID); + } + else + msg.nextFragmentNum++; + m_OutOfSequenceFragments.erase (it); + } + } + } + + void TunnelEndpoint::HandleNextMessage (const TunnelMessageBlock& msg) + { + LogPrint (eLogInfo, "TunnelMessage: handle fragment of ", msg.data->GetLength ()," bytes. Msg type ", (int)msg.data->GetTypeID ()); + switch (msg.deliveryType) + { + case eDeliveryTypeLocal: + i2p::HandleI2NPMessage (msg.data); + break; + case eDeliveryTypeTunnel: + i2p::transport::transports.SendMessage (msg.hash, i2p::CreateTunnelGatewayMsg (msg.tunnelID, msg.data)); + break; + case eDeliveryTypeRouter: + if (msg.hash == i2p::context.GetRouterInfo ().GetIdentHash ()) // check if message is sent to us + i2p::HandleI2NPMessage (msg.data); + else + { + // to somebody else + if (!m_IsInbound) // outbound transit tunnel + { + /* auto typeID = msg.data->GetTypeID (); + if (typeID == eI2NPDatabaseStore || typeID == eI2NPDatabaseSearchReply ) + // catch RI or reply with new list of routers + i2p::data::netdb.PostI2NPMsg (msg.data);*/ + i2p::transport::transports.SendMessage (msg.hash, msg.data); + } + else // we shouldn't send this message. possible leakage + LogPrint (eLogError, "Message to another router arrived from an inbound tunnel. Dropped"); + } + break; + default: + LogPrint (eLogError, "TunnelMessage: Unknown delivery type ", (int)msg.deliveryType); + }; + } +} } diff --git a/TunnelEndpoint.h b/TunnelEndpoint.h index 20b9105f..bbdb8a4e 100644 --- a/TunnelEndpoint.h +++ b/TunnelEndpoint.h @@ -11,44 +11,44 @@ namespace i2p { namespace tunnel { - class TunnelEndpoint - { - struct TunnelMessageBlockEx: public TunnelMessageBlock - { - uint8_t nextFragmentNum; - }; + class TunnelEndpoint + { + struct TunnelMessageBlockEx: public TunnelMessageBlock + { + uint8_t nextFragmentNum; + }; - struct Fragment - { - uint8_t fragmentNum; - bool isLastFragment; - std::shared_ptr data; - }; - - public: + struct Fragment + { + uint8_t fragmentNum; + bool isLastFragment; + std::shared_ptr data; + }; + + public: - TunnelEndpoint (bool isInbound): m_IsInbound (isInbound), m_NumReceivedBytes (0) {}; - ~TunnelEndpoint (); - size_t GetNumReceivedBytes () const { return m_NumReceivedBytes; }; - - void HandleDecryptedTunnelDataMsg (std::shared_ptr msg); + TunnelEndpoint (bool isInbound): m_IsInbound (isInbound), m_NumReceivedBytes (0) {}; + ~TunnelEndpoint (); + size_t GetNumReceivedBytes () const { return m_NumReceivedBytes; }; + + void HandleDecryptedTunnelDataMsg (std::shared_ptr msg); - private: + private: - void HandleFollowOnFragment (uint32_t msgID, bool isLastFragment, const TunnelMessageBlockEx& m); - void HandleNextMessage (const TunnelMessageBlock& msg); + void HandleFollowOnFragment (uint32_t msgID, bool isLastFragment, const TunnelMessageBlockEx& m); + void HandleNextMessage (const TunnelMessageBlock& msg); - void AddOutOfSequenceFragment (uint32_t msgID, uint8_t fragmentNum, bool isLastFragment, std::shared_ptr data); - void HandleOutOfSequenceFragment (uint32_t msgID, TunnelMessageBlockEx& msg); - - private: + void AddOutOfSequenceFragment (uint32_t msgID, uint8_t fragmentNum, bool isLastFragment, std::shared_ptr data); + void HandleOutOfSequenceFragment (uint32_t msgID, TunnelMessageBlockEx& msg); + + private: - std::map m_IncompleteMessages; - std::map m_OutOfSequenceFragments; - bool m_IsInbound; - size_t m_NumReceivedBytes; - }; -} + std::map m_IncompleteMessages; + std::map m_OutOfSequenceFragments; + bool m_IsInbound; + size_t m_NumReceivedBytes; + }; +} } #endif diff --git a/TunnelGateway.cpp b/TunnelGateway.cpp index b05a342c..89f69da1 100644 --- a/TunnelGateway.cpp +++ b/TunnelGateway.cpp @@ -10,203 +10,203 @@ namespace i2p { namespace tunnel { - TunnelGatewayBuffer::TunnelGatewayBuffer (uint32_t tunnelID): m_TunnelID (tunnelID), - m_CurrentTunnelDataMsg (nullptr), m_RemainingSize (0) - { - context.GetRandomNumberGenerator ().GenerateBlock (m_NonZeroRandomBuffer, TUNNEL_DATA_MAX_PAYLOAD_SIZE); - for (size_t i = 0; i < TUNNEL_DATA_MAX_PAYLOAD_SIZE; i++) - if (!m_NonZeroRandomBuffer[i]) m_NonZeroRandomBuffer[i] = 1; - } + TunnelGatewayBuffer::TunnelGatewayBuffer (uint32_t tunnelID): m_TunnelID (tunnelID), + m_CurrentTunnelDataMsg (nullptr), m_RemainingSize (0) + { + context.GetRandomNumberGenerator ().GenerateBlock (m_NonZeroRandomBuffer, TUNNEL_DATA_MAX_PAYLOAD_SIZE); + for (size_t i = 0; i < TUNNEL_DATA_MAX_PAYLOAD_SIZE; i++) + if (!m_NonZeroRandomBuffer[i]) m_NonZeroRandomBuffer[i] = 1; + } - TunnelGatewayBuffer::~TunnelGatewayBuffer () - { - } - - void TunnelGatewayBuffer::PutI2NPMsg (const TunnelMessageBlock& block) - { - bool messageCreated = false; - if (!m_CurrentTunnelDataMsg) - { - CreateCurrentTunnelDataMessage (); - messageCreated = true; - } + TunnelGatewayBuffer::~TunnelGatewayBuffer () + { + } + + void TunnelGatewayBuffer::PutI2NPMsg (const TunnelMessageBlock& block) + { + bool messageCreated = false; + if (!m_CurrentTunnelDataMsg) + { + CreateCurrentTunnelDataMessage (); + messageCreated = true; + } - // create delivery instructions - uint8_t di[43]; // max delivery instruction length is 43 for tunnel - size_t diLen = 1;// flag - if (block.deliveryType != eDeliveryTypeLocal) // tunnel or router - { - if (block.deliveryType == eDeliveryTypeTunnel) - { - htobe32buf (di + diLen, block.tunnelID); - diLen += 4; // tunnelID - } - - memcpy (di + diLen, block.hash, 32); - diLen += 32; //len - } - di[0] = block.deliveryType << 5; // set delivery type + // create delivery instructions + uint8_t di[43]; // max delivery instruction length is 43 for tunnel + size_t diLen = 1;// flag + if (block.deliveryType != eDeliveryTypeLocal) // tunnel or router + { + if (block.deliveryType == eDeliveryTypeTunnel) + { + htobe32buf (di + diLen, block.tunnelID); + diLen += 4; // tunnelID + } + + memcpy (di + diLen, block.hash, 32); + diLen += 32; //len + } + di[0] = block.deliveryType << 5; // set delivery type - // create fragments - std::shared_ptr msg = block.data; - auto fullMsgLen = diLen + msg->GetLength () + 2; // delivery instructions + payload + 2 bytes length - if (fullMsgLen <= m_RemainingSize) - { - // message fits. First and last fragment - htobe16buf (di + diLen, msg->GetLength ()); - diLen += 2; // size - memcpy (m_CurrentTunnelDataMsg->buf + m_CurrentTunnelDataMsg->len, di, diLen); - memcpy (m_CurrentTunnelDataMsg->buf + m_CurrentTunnelDataMsg->len + diLen, msg->GetBuffer (), msg->GetLength ()); - m_CurrentTunnelDataMsg->len += diLen + msg->GetLength (); - m_RemainingSize -= diLen + msg->GetLength (); - if (!m_RemainingSize) - CompleteCurrentTunnelDataMessage (); - } - else - { - if (!messageCreated) // check if we should complete previous message - { - auto numFollowOnFragments = fullMsgLen / TUNNEL_DATA_MAX_PAYLOAD_SIZE; - // length of bytes don't fit full tunnel message - // every follow-on fragment adds 7 bytes - auto nonFit = (fullMsgLen + numFollowOnFragments*7) % TUNNEL_DATA_MAX_PAYLOAD_SIZE; - if (!nonFit || nonFit > m_RemainingSize) - { - CompleteCurrentTunnelDataMessage (); - CreateCurrentTunnelDataMessage (); - } - } - if (diLen + 6 <= m_RemainingSize) - { - // delivery instructions fit - uint32_t msgID; - memcpy (&msgID, msg->GetHeader () + I2NP_HEADER_MSGID_OFFSET, 4); // in network bytes order - size_t size = m_RemainingSize - diLen - 6; // 6 = 4 (msgID) + 2 (size) + // create fragments + std::shared_ptr msg = block.data; + auto fullMsgLen = diLen + msg->GetLength () + 2; // delivery instructions + payload + 2 bytes length + if (fullMsgLen <= m_RemainingSize) + { + // message fits. First and last fragment + htobe16buf (di + diLen, msg->GetLength ()); + diLen += 2; // size + memcpy (m_CurrentTunnelDataMsg->buf + m_CurrentTunnelDataMsg->len, di, diLen); + memcpy (m_CurrentTunnelDataMsg->buf + m_CurrentTunnelDataMsg->len + diLen, msg->GetBuffer (), msg->GetLength ()); + m_CurrentTunnelDataMsg->len += diLen + msg->GetLength (); + m_RemainingSize -= diLen + msg->GetLength (); + if (!m_RemainingSize) + CompleteCurrentTunnelDataMessage (); + } + else + { + if (!messageCreated) // check if we should complete previous message + { + auto numFollowOnFragments = fullMsgLen / TUNNEL_DATA_MAX_PAYLOAD_SIZE; + // length of bytes don't fit full tunnel message + // every follow-on fragment adds 7 bytes + auto nonFit = (fullMsgLen + numFollowOnFragments*7) % TUNNEL_DATA_MAX_PAYLOAD_SIZE; + if (!nonFit || nonFit > m_RemainingSize) + { + CompleteCurrentTunnelDataMessage (); + CreateCurrentTunnelDataMessage (); + } + } + if (diLen + 6 <= m_RemainingSize) + { + // delivery instructions fit + uint32_t msgID; + memcpy (&msgID, msg->GetHeader () + I2NP_HEADER_MSGID_OFFSET, 4); // in network bytes order + size_t size = m_RemainingSize - diLen - 6; // 6 = 4 (msgID) + 2 (size) - // first fragment - di[0] |= 0x08; // fragmented - htobuf32 (di + diLen, msgID); - diLen += 4; // Message ID - htobe16buf (di + diLen, size); - diLen += 2; // size - memcpy (m_CurrentTunnelDataMsg->buf + m_CurrentTunnelDataMsg->len, di, diLen); - memcpy (m_CurrentTunnelDataMsg->buf + m_CurrentTunnelDataMsg->len + diLen, msg->GetBuffer (), size); - m_CurrentTunnelDataMsg->len += diLen + size; - CompleteCurrentTunnelDataMessage (); - // follow on fragments - int fragmentNumber = 1; - while (size < msg->GetLength ()) - { - CreateCurrentTunnelDataMessage (); - uint8_t * buf = m_CurrentTunnelDataMsg->GetBuffer (); - buf[0] = 0x80 | (fragmentNumber << 1); // frag - bool isLastFragment = false; - size_t s = msg->GetLength () - size; - if (s > TUNNEL_DATA_MAX_PAYLOAD_SIZE - 7) // 7 follow on instructions - s = TUNNEL_DATA_MAX_PAYLOAD_SIZE - 7; - else // last fragment - { - buf[0] |= 0x01; - isLastFragment = true; - } - htobuf32 (buf + 1, msgID); //Message ID - htobe16buf (buf + 5, s); // size - memcpy (buf + 7, msg->GetBuffer () + size, s); - m_CurrentTunnelDataMsg->len += s+7; - if (isLastFragment) - { - m_RemainingSize -= s+7; - if (!m_RemainingSize) - CompleteCurrentTunnelDataMessage (); - } - else - CompleteCurrentTunnelDataMessage (); - size += s; - fragmentNumber++; - } - } - else - { - // delivery instructions don't fit. Create new message - CompleteCurrentTunnelDataMessage (); - PutI2NPMsg (block); - // don't delete msg because it's taken care inside - } - } - } - - void TunnelGatewayBuffer::ClearTunnelDataMsgs () - { - m_TunnelDataMsgs.clear (); - } + // first fragment + di[0] |= 0x08; // fragmented + htobuf32 (di + diLen, msgID); + diLen += 4; // Message ID + htobe16buf (di + diLen, size); + diLen += 2; // size + memcpy (m_CurrentTunnelDataMsg->buf + m_CurrentTunnelDataMsg->len, di, diLen); + memcpy (m_CurrentTunnelDataMsg->buf + m_CurrentTunnelDataMsg->len + diLen, msg->GetBuffer (), size); + m_CurrentTunnelDataMsg->len += diLen + size; + CompleteCurrentTunnelDataMessage (); + // follow on fragments + int fragmentNumber = 1; + while (size < msg->GetLength ()) + { + CreateCurrentTunnelDataMessage (); + uint8_t * buf = m_CurrentTunnelDataMsg->GetBuffer (); + buf[0] = 0x80 | (fragmentNumber << 1); // frag + bool isLastFragment = false; + size_t s = msg->GetLength () - size; + if (s > TUNNEL_DATA_MAX_PAYLOAD_SIZE - 7) // 7 follow on instructions + s = TUNNEL_DATA_MAX_PAYLOAD_SIZE - 7; + else // last fragment + { + buf[0] |= 0x01; + isLastFragment = true; + } + htobuf32 (buf + 1, msgID); //Message ID + htobe16buf (buf + 5, s); // size + memcpy (buf + 7, msg->GetBuffer () + size, s); + m_CurrentTunnelDataMsg->len += s+7; + if (isLastFragment) + { + m_RemainingSize -= s+7; + if (!m_RemainingSize) + CompleteCurrentTunnelDataMessage (); + } + else + CompleteCurrentTunnelDataMessage (); + size += s; + fragmentNumber++; + } + } + else + { + // delivery instructions don't fit. Create new message + CompleteCurrentTunnelDataMessage (); + PutI2NPMsg (block); + // don't delete msg because it's taken care inside + } + } + } + + void TunnelGatewayBuffer::ClearTunnelDataMsgs () + { + m_TunnelDataMsgs.clear (); + } - void TunnelGatewayBuffer::CreateCurrentTunnelDataMessage () - { - m_CurrentTunnelDataMsg = ToSharedI2NPMessage (NewI2NPShortMessage ()); - m_CurrentTunnelDataMsg->Align (12); - // we reserve space for padding - m_CurrentTunnelDataMsg->offset += TUNNEL_DATA_MSG_SIZE + I2NP_HEADER_SIZE; - m_CurrentTunnelDataMsg->len = m_CurrentTunnelDataMsg->offset; - m_RemainingSize = TUNNEL_DATA_MAX_PAYLOAD_SIZE; - } - - void TunnelGatewayBuffer::CompleteCurrentTunnelDataMessage () - { - if (!m_CurrentTunnelDataMsg) return; - uint8_t * payload = m_CurrentTunnelDataMsg->GetBuffer (); - size_t size = m_CurrentTunnelDataMsg->len - m_CurrentTunnelDataMsg->offset; - - m_CurrentTunnelDataMsg->offset = m_CurrentTunnelDataMsg->len - TUNNEL_DATA_MSG_SIZE - I2NP_HEADER_SIZE; - uint8_t * buf = m_CurrentTunnelDataMsg->GetPayload (); - htobe32buf (buf, m_TunnelID); - CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator (); - rnd.GenerateBlock (buf + 4, 16); // original IV - memcpy (payload + size, buf + 4, 16); // copy IV for checksum - uint8_t hash[32]; - CryptoPP::SHA256().CalculateDigest (hash, payload, size+16); - memcpy (buf+20, hash, 4); // checksum - payload[-1] = 0; // zero - ptrdiff_t paddingSize = payload - buf - 25; // 25 = 24 + 1 - if (paddingSize > 0) - { - // non-zero padding - auto randomOffset = rnd.GenerateWord32 (0, TUNNEL_DATA_MAX_PAYLOAD_SIZE - paddingSize); - memcpy (buf + 24, m_NonZeroRandomBuffer + randomOffset, paddingSize); - } + void TunnelGatewayBuffer::CreateCurrentTunnelDataMessage () + { + m_CurrentTunnelDataMsg = ToSharedI2NPMessage (NewI2NPShortMessage ()); + m_CurrentTunnelDataMsg->Align (12); + // we reserve space for padding + m_CurrentTunnelDataMsg->offset += TUNNEL_DATA_MSG_SIZE + I2NP_HEADER_SIZE; + m_CurrentTunnelDataMsg->len = m_CurrentTunnelDataMsg->offset; + m_RemainingSize = TUNNEL_DATA_MAX_PAYLOAD_SIZE; + } + + void TunnelGatewayBuffer::CompleteCurrentTunnelDataMessage () + { + if (!m_CurrentTunnelDataMsg) return; + uint8_t * payload = m_CurrentTunnelDataMsg->GetBuffer (); + size_t size = m_CurrentTunnelDataMsg->len - m_CurrentTunnelDataMsg->offset; + + m_CurrentTunnelDataMsg->offset = m_CurrentTunnelDataMsg->len - TUNNEL_DATA_MSG_SIZE - I2NP_HEADER_SIZE; + uint8_t * buf = m_CurrentTunnelDataMsg->GetPayload (); + htobe32buf (buf, m_TunnelID); + CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator (); + rnd.GenerateBlock (buf + 4, 16); // original IV + memcpy (payload + size, buf + 4, 16); // copy IV for checksum + uint8_t hash[32]; + CryptoPP::SHA256().CalculateDigest (hash, payload, size+16); + memcpy (buf+20, hash, 4); // checksum + payload[-1] = 0; // zero + ptrdiff_t paddingSize = payload - buf - 25; // 25 = 24 + 1 + if (paddingSize > 0) + { + // non-zero padding + auto randomOffset = rnd.GenerateWord32 (0, TUNNEL_DATA_MAX_PAYLOAD_SIZE - paddingSize); + memcpy (buf + 24, m_NonZeroRandomBuffer + randomOffset, paddingSize); + } - // we can't fill message header yet because encryption is required - m_TunnelDataMsgs.push_back (m_CurrentTunnelDataMsg); - m_CurrentTunnelDataMsg = nullptr; - } + // we can't fill message header yet because encryption is required + m_TunnelDataMsgs.push_back (m_CurrentTunnelDataMsg); + m_CurrentTunnelDataMsg = nullptr; + } - void TunnelGateway::SendTunnelDataMsg (const TunnelMessageBlock& block) - { - if (block.data) - { - PutTunnelDataMsg (block); - SendBuffer (); - } - } + void TunnelGateway::SendTunnelDataMsg (const TunnelMessageBlock& block) + { + if (block.data) + { + PutTunnelDataMsg (block); + SendBuffer (); + } + } - void TunnelGateway::PutTunnelDataMsg (const TunnelMessageBlock& block) - { - if (block.data) - m_Buffer.PutI2NPMsg (block); - } + void TunnelGateway::PutTunnelDataMsg (const TunnelMessageBlock& block) + { + if (block.data) + m_Buffer.PutI2NPMsg (block); + } - void TunnelGateway::SendBuffer () - { - m_Buffer.CompleteCurrentTunnelDataMessage (); - auto tunnelMsgs = m_Buffer.GetTunnelDataMsgs (); - for (auto tunnelMsg : tunnelMsgs) - { - m_Tunnel->EncryptTunnelMsg (tunnelMsg, tunnelMsg); - tunnelMsg->FillI2NPMessageHeader (eI2NPTunnelData); - m_NumSentBytes += TUNNEL_DATA_MSG_SIZE; - } - i2p::transport::transports.SendMessages (m_Tunnel->GetNextIdentHash (), tunnelMsgs); - m_Buffer.ClearTunnelDataMsgs (); - } -} -} + void TunnelGateway::SendBuffer () + { + m_Buffer.CompleteCurrentTunnelDataMessage (); + auto tunnelMsgs = m_Buffer.GetTunnelDataMsgs (); + for (auto tunnelMsg : tunnelMsgs) + { + m_Tunnel->EncryptTunnelMsg (tunnelMsg, tunnelMsg); + tunnelMsg->FillI2NPMessageHeader (eI2NPTunnelData); + m_NumSentBytes += TUNNEL_DATA_MSG_SIZE; + } + i2p::transport::transports.SendMessages (m_Tunnel->GetNextIdentHash (), tunnelMsgs); + m_Buffer.ClearTunnelDataMsgs (); + } +} +} diff --git a/TunnelGateway.h b/TunnelGateway.h index ea88317b..6d899f66 100644 --- a/TunnelGateway.h +++ b/TunnelGateway.h @@ -11,47 +11,47 @@ namespace i2p { namespace tunnel { - class TunnelGatewayBuffer - { - public: - TunnelGatewayBuffer (uint32_t tunnelID); - ~TunnelGatewayBuffer (); - void PutI2NPMsg (const TunnelMessageBlock& block); - const std::vector >& GetTunnelDataMsgs () const { return m_TunnelDataMsgs; }; - void ClearTunnelDataMsgs (); - void CompleteCurrentTunnelDataMessage (); + class TunnelGatewayBuffer + { + public: + TunnelGatewayBuffer (uint32_t tunnelID); + ~TunnelGatewayBuffer (); + void PutI2NPMsg (const TunnelMessageBlock& block); + const std::vector >& GetTunnelDataMsgs () const { return m_TunnelDataMsgs; }; + void ClearTunnelDataMsgs (); + void CompleteCurrentTunnelDataMessage (); - private: + private: - void CreateCurrentTunnelDataMessage (); - - private: + void CreateCurrentTunnelDataMessage (); + + private: - uint32_t m_TunnelID; - std::vector > m_TunnelDataMsgs; - std::shared_ptr m_CurrentTunnelDataMsg; - size_t m_RemainingSize; - uint8_t m_NonZeroRandomBuffer[TUNNEL_DATA_MAX_PAYLOAD_SIZE]; - }; + uint32_t m_TunnelID; + std::vector > m_TunnelDataMsgs; + std::shared_ptr m_CurrentTunnelDataMsg; + size_t m_RemainingSize; + uint8_t m_NonZeroRandomBuffer[TUNNEL_DATA_MAX_PAYLOAD_SIZE]; + }; - class TunnelGateway - { - public: + class TunnelGateway + { + public: - TunnelGateway (TunnelBase * tunnel): - m_Tunnel (tunnel), m_Buffer (tunnel->GetNextTunnelID ()), m_NumSentBytes (0) {}; - void SendTunnelDataMsg (const TunnelMessageBlock& block); - void PutTunnelDataMsg (const TunnelMessageBlock& block); - void SendBuffer (); - size_t GetNumSentBytes () const { return m_NumSentBytes; }; - - private: + TunnelGateway (TunnelBase * tunnel): + m_Tunnel (tunnel), m_Buffer (tunnel->GetNextTunnelID ()), m_NumSentBytes (0) {}; + void SendTunnelDataMsg (const TunnelMessageBlock& block); + void PutTunnelDataMsg (const TunnelMessageBlock& block); + void SendBuffer (); + size_t GetNumSentBytes () const { return m_NumSentBytes; }; + + private: - TunnelBase * m_Tunnel; - TunnelGatewayBuffer m_Buffer; - size_t m_NumSentBytes; - }; -} -} + TunnelBase * m_Tunnel; + TunnelGatewayBuffer m_Buffer; + size_t m_NumSentBytes; + }; +} +} #endif diff --git a/TunnelPool.cpp b/TunnelPool.cpp index 0f0eb709..62d52075 100644 --- a/TunnelPool.cpp +++ b/TunnelPool.cpp @@ -12,424 +12,424 @@ namespace i2p { namespace tunnel { - TunnelPool::TunnelPool (i2p::garlic::GarlicDestination * localDestination, int numInboundHops, int numOutboundHops, int numInboundTunnels, int numOutboundTunnels): - m_LocalDestination (localDestination), m_NumInboundHops (numInboundHops), m_NumOutboundHops (numOutboundHops), - m_NumInboundTunnels (numInboundTunnels), m_NumOutboundTunnels (numOutboundTunnels), m_IsActive (true) - { - } + TunnelPool::TunnelPool (i2p::garlic::GarlicDestination * localDestination, int numInboundHops, int numOutboundHops, int numInboundTunnels, int numOutboundTunnels): + m_LocalDestination (localDestination), m_NumInboundHops (numInboundHops), m_NumOutboundHops (numOutboundHops), + m_NumInboundTunnels (numInboundTunnels), m_NumOutboundTunnels (numOutboundTunnels), m_IsActive (true) + { + } - TunnelPool::~TunnelPool () - { - DetachTunnels (); - } + TunnelPool::~TunnelPool () + { + DetachTunnels (); + } - void TunnelPool::SetExplicitPeers (std::shared_ptr > explicitPeers) - { - m_ExplicitPeers = explicitPeers; - if (m_ExplicitPeers) - { - int size = m_ExplicitPeers->size (); - if (m_NumInboundHops > size) - { - m_NumInboundHops = size; - LogPrint (eLogInfo, "Inbound tunnel length has beed adjusted to ", size, " for explicit peers"); - } - if (m_NumOutboundHops > size) - { - m_NumOutboundHops = size; - LogPrint (eLogInfo, "Outbound tunnel length has beed adjusted to ", size, " for explicit peers"); - } - m_NumInboundTunnels = 1; - m_NumOutboundTunnels = 1; - } - } + void TunnelPool::SetExplicitPeers (std::shared_ptr > explicitPeers) + { + m_ExplicitPeers = explicitPeers; + if (m_ExplicitPeers) + { + int size = m_ExplicitPeers->size (); + if (m_NumInboundHops > size) + { + m_NumInboundHops = size; + LogPrint (eLogInfo, "Inbound tunnel length has beed adjusted to ", size, " for explicit peers"); + } + if (m_NumOutboundHops > size) + { + m_NumOutboundHops = size; + LogPrint (eLogInfo, "Outbound tunnel length has beed adjusted to ", size, " for explicit peers"); + } + m_NumInboundTunnels = 1; + m_NumOutboundTunnels = 1; + } + } - void TunnelPool::DetachTunnels () - { - { - std::unique_lock l(m_InboundTunnelsMutex); - for (auto it: m_InboundTunnels) - it->SetTunnelPool (nullptr); - m_InboundTunnels.clear (); - } - { - std::unique_lock l(m_OutboundTunnelsMutex); - for (auto it: m_OutboundTunnels) - it->SetTunnelPool (nullptr); - m_OutboundTunnels.clear (); - } - m_Tests.clear (); - } - - void TunnelPool::TunnelCreated (std::shared_ptr createdTunnel) - { - if (!m_IsActive) return; - { - std::unique_lock l(m_InboundTunnelsMutex); - m_InboundTunnels.insert (createdTunnel); - } - if (m_LocalDestination) - m_LocalDestination->SetLeaseSetUpdated (); - } + void TunnelPool::DetachTunnels () + { + { + std::unique_lock l(m_InboundTunnelsMutex); + for (auto it: m_InboundTunnels) + it->SetTunnelPool (nullptr); + m_InboundTunnels.clear (); + } + { + std::unique_lock l(m_OutboundTunnelsMutex); + for (auto it: m_OutboundTunnels) + it->SetTunnelPool (nullptr); + m_OutboundTunnels.clear (); + } + m_Tests.clear (); + } + + void TunnelPool::TunnelCreated (std::shared_ptr createdTunnel) + { + if (!m_IsActive) return; + { + std::unique_lock l(m_InboundTunnelsMutex); + m_InboundTunnels.insert (createdTunnel); + } + if (m_LocalDestination) + m_LocalDestination->SetLeaseSetUpdated (); + } - void TunnelPool::TunnelExpired (std::shared_ptr expiredTunnel) - { - if (expiredTunnel) - { - expiredTunnel->SetTunnelPool (nullptr); - for (auto it: m_Tests) - if (it.second.second == expiredTunnel) it.second.second = nullptr; + void TunnelPool::TunnelExpired (std::shared_ptr expiredTunnel) + { + if (expiredTunnel) + { + expiredTunnel->SetTunnelPool (nullptr); + for (auto it: m_Tests) + if (it.second.second == expiredTunnel) it.second.second = nullptr; - std::unique_lock l(m_InboundTunnelsMutex); - m_InboundTunnels.erase (expiredTunnel); - } - } + std::unique_lock l(m_InboundTunnelsMutex); + m_InboundTunnels.erase (expiredTunnel); + } + } - void TunnelPool::TunnelCreated (std::shared_ptr createdTunnel) - { - if (!m_IsActive) return; - { - std::unique_lock l(m_OutboundTunnelsMutex); - m_OutboundTunnels.insert (createdTunnel); - } - //CreatePairedInboundTunnel (createdTunnel); - } + void TunnelPool::TunnelCreated (std::shared_ptr createdTunnel) + { + if (!m_IsActive) return; + { + std::unique_lock l(m_OutboundTunnelsMutex); + m_OutboundTunnels.insert (createdTunnel); + } + //CreatePairedInboundTunnel (createdTunnel); + } - void TunnelPool::TunnelExpired (std::shared_ptr expiredTunnel) - { - if (expiredTunnel) - { - expiredTunnel->SetTunnelPool (nullptr); - for (auto it: m_Tests) - if (it.second.first == expiredTunnel) it.second.first = nullptr; + void TunnelPool::TunnelExpired (std::shared_ptr expiredTunnel) + { + if (expiredTunnel) + { + expiredTunnel->SetTunnelPool (nullptr); + for (auto it: m_Tests) + if (it.second.first == expiredTunnel) it.second.first = nullptr; - std::unique_lock l(m_OutboundTunnelsMutex); - m_OutboundTunnels.erase (expiredTunnel); - } - } - - std::vector > TunnelPool::GetInboundTunnels (int num) const - { - std::vector > v; - int i = 0; - std::unique_lock l(m_InboundTunnelsMutex); - for (auto it : m_InboundTunnels) - { - if (i >= num) break; - if (it->IsEstablished ()) - { - v.push_back (it); - i++; - } - } - return v; - } + std::unique_lock l(m_OutboundTunnelsMutex); + m_OutboundTunnels.erase (expiredTunnel); + } + } + + std::vector > TunnelPool::GetInboundTunnels (int num) const + { + std::vector > v; + int i = 0; + std::unique_lock l(m_InboundTunnelsMutex); + for (auto it : m_InboundTunnels) + { + if (i >= num) break; + if (it->IsEstablished ()) + { + v.push_back (it); + i++; + } + } + return v; + } - std::shared_ptr TunnelPool::GetNextOutboundTunnel (std::shared_ptr excluded) const - { - std::unique_lock l(m_OutboundTunnelsMutex); - return GetNextTunnel (m_OutboundTunnels, excluded); - } + std::shared_ptr TunnelPool::GetNextOutboundTunnel (std::shared_ptr excluded) const + { + std::unique_lock l(m_OutboundTunnelsMutex); + return GetNextTunnel (m_OutboundTunnels, excluded); + } - std::shared_ptr TunnelPool::GetNextInboundTunnel (std::shared_ptr excluded) const - { - std::unique_lock l(m_InboundTunnelsMutex); - return GetNextTunnel (m_InboundTunnels, excluded); - } + std::shared_ptr TunnelPool::GetNextInboundTunnel (std::shared_ptr excluded) const + { + std::unique_lock l(m_InboundTunnelsMutex); + return GetNextTunnel (m_InboundTunnels, excluded); + } - template - typename TTunnels::value_type TunnelPool::GetNextTunnel (TTunnels& tunnels, typename TTunnels::value_type excluded) const - { - if (tunnels.empty ()) return nullptr; - CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator (); - uint32_t ind = rnd.GenerateWord32 (0, tunnels.size ()/2), i = 0; - typename TTunnels::value_type tunnel = nullptr; - for (auto it: tunnels) - { - if (it->IsEstablished () && it != excluded) - { - tunnel = it; - i++; - } - if (i > ind && tunnel) break; - } - if (!tunnel && excluded && excluded->IsEstablished ()) tunnel = excluded; - return tunnel; - } + template + typename TTunnels::value_type TunnelPool::GetNextTunnel (TTunnels& tunnels, typename TTunnels::value_type excluded) const + { + if (tunnels.empty ()) return nullptr; + CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator (); + uint32_t ind = rnd.GenerateWord32 (0, tunnels.size ()/2), i = 0; + typename TTunnels::value_type tunnel = nullptr; + for (auto it: tunnels) + { + if (it->IsEstablished () && it != excluded) + { + tunnel = it; + i++; + } + if (i > ind && tunnel) break; + } + if (!tunnel && excluded && excluded->IsEstablished ()) tunnel = excluded; + return tunnel; + } - std::shared_ptr TunnelPool::GetNewOutboundTunnel (std::shared_ptr old) const - { - if (old && old->IsEstablished ()) return old; - std::shared_ptr tunnel; - if (old) - { - std::unique_lock l(m_OutboundTunnelsMutex); - for (auto it: m_OutboundTunnels) - if (it->IsEstablished () && old->GetEndpointRouter ()->GetIdentHash () == it->GetEndpointRouter ()->GetIdentHash ()) - { - tunnel = it; - break; - } - } - - if (!tunnel) - tunnel = GetNextOutboundTunnel (); - return tunnel; - } + std::shared_ptr TunnelPool::GetNewOutboundTunnel (std::shared_ptr old) const + { + if (old && old->IsEstablished ()) return old; + std::shared_ptr tunnel; + if (old) + { + std::unique_lock l(m_OutboundTunnelsMutex); + for (auto it: m_OutboundTunnels) + if (it->IsEstablished () && old->GetEndpointRouter ()->GetIdentHash () == it->GetEndpointRouter ()->GetIdentHash ()) + { + tunnel = it; + break; + } + } + + if (!tunnel) + tunnel = GetNextOutboundTunnel (); + return tunnel; + } - void TunnelPool::CreateTunnels () - { - int num = 0; - { - std::unique_lock l(m_InboundTunnelsMutex); - for (auto it : m_InboundTunnels) - if (it->IsEstablished ()) num++; - } - for (int i = num; i < m_NumInboundTunnels; i++) - CreateInboundTunnel (); - - num = 0; - { - std::unique_lock l(m_OutboundTunnelsMutex); - for (auto it : m_OutboundTunnels) - if (it->IsEstablished ()) num++; - } - for (int i = num; i < m_NumOutboundTunnels; i++) - CreateOutboundTunnel (); - } + void TunnelPool::CreateTunnels () + { + int num = 0; + { + std::unique_lock l(m_InboundTunnelsMutex); + for (auto it : m_InboundTunnels) + if (it->IsEstablished ()) num++; + } + for (int i = num; i < m_NumInboundTunnels; i++) + CreateInboundTunnel (); + + num = 0; + { + std::unique_lock l(m_OutboundTunnelsMutex); + for (auto it : m_OutboundTunnels) + if (it->IsEstablished ()) num++; + } + for (int i = num; i < m_NumOutboundTunnels; i++) + CreateOutboundTunnel (); + } - void TunnelPool::TestTunnels () - { - auto& rnd = i2p::context.GetRandomNumberGenerator (); - for (auto it: m_Tests) - { - LogPrint ("Tunnel test ", (int)it.first, " failed"); - // if test failed again with another tunnel we consider it failed - if (it.second.first) - { - if (it.second.first->GetState () == eTunnelStateTestFailed) - { - it.second.first->SetState (eTunnelStateFailed); - std::unique_lock l(m_OutboundTunnelsMutex); - m_OutboundTunnels.erase (it.second.first); - } - else - it.second.first->SetState (eTunnelStateTestFailed); - } - if (it.second.second) - { - if (it.second.second->GetState () == eTunnelStateTestFailed) - { - it.second.second->SetState (eTunnelStateFailed); - { - std::unique_lock l(m_InboundTunnelsMutex); - m_InboundTunnels.erase (it.second.second); - } - if (m_LocalDestination) - m_LocalDestination->SetLeaseSetUpdated (); - } - else - it.second.second->SetState (eTunnelStateTestFailed); - } - } - m_Tests.clear (); - // new tests - auto it1 = m_OutboundTunnels.begin (); - auto it2 = m_InboundTunnels.begin (); - while (it1 != m_OutboundTunnels.end () && it2 != m_InboundTunnels.end ()) - { - bool failed = false; - if ((*it1)->IsFailed ()) - { - failed = true; - it1++; - } - if ((*it2)->IsFailed ()) - { - failed = true; - it2++; - } - if (!failed) - { - uint32_t msgID = rnd.GenerateWord32 (); - m_Tests[msgID] = std::make_pair (*it1, *it2); - (*it1)->SendTunnelDataMsg ((*it2)->GetNextIdentHash (), (*it2)->GetNextTunnelID (), - CreateDeliveryStatusMsg (msgID)); - it1++; it2++; - } - } - } + void TunnelPool::TestTunnels () + { + auto& rnd = i2p::context.GetRandomNumberGenerator (); + for (auto it: m_Tests) + { + LogPrint ("Tunnel test ", (int)it.first, " failed"); + // if test failed again with another tunnel we consider it failed + if (it.second.first) + { + if (it.second.first->GetState () == eTunnelStateTestFailed) + { + it.second.first->SetState (eTunnelStateFailed); + std::unique_lock l(m_OutboundTunnelsMutex); + m_OutboundTunnels.erase (it.second.first); + } + else + it.second.first->SetState (eTunnelStateTestFailed); + } + if (it.second.second) + { + if (it.second.second->GetState () == eTunnelStateTestFailed) + { + it.second.second->SetState (eTunnelStateFailed); + { + std::unique_lock l(m_InboundTunnelsMutex); + m_InboundTunnels.erase (it.second.second); + } + if (m_LocalDestination) + m_LocalDestination->SetLeaseSetUpdated (); + } + else + it.second.second->SetState (eTunnelStateTestFailed); + } + } + m_Tests.clear (); + // new tests + auto it1 = m_OutboundTunnels.begin (); + auto it2 = m_InboundTunnels.begin (); + while (it1 != m_OutboundTunnels.end () && it2 != m_InboundTunnels.end ()) + { + bool failed = false; + if ((*it1)->IsFailed ()) + { + failed = true; + it1++; + } + if ((*it2)->IsFailed ()) + { + failed = true; + it2++; + } + if (!failed) + { + uint32_t msgID = rnd.GenerateWord32 (); + m_Tests[msgID] = std::make_pair (*it1, *it2); + (*it1)->SendTunnelDataMsg ((*it2)->GetNextIdentHash (), (*it2)->GetNextTunnelID (), + CreateDeliveryStatusMsg (msgID)); + it1++; it2++; + } + } + } - void TunnelPool::ProcessGarlicMessage (std::shared_ptr msg) - { - if (m_LocalDestination) - m_LocalDestination->ProcessGarlicMessage (msg); - else - LogPrint (eLogWarning, "Local destination doesn't exist. Dropped"); - } - - void TunnelPool::ProcessDeliveryStatus (std::shared_ptr msg) - { - const uint8_t * buf = msg->GetPayload (); - uint32_t msgID = bufbe32toh (buf); - buf += 4; - uint64_t timestamp = bufbe64toh (buf); + void TunnelPool::ProcessGarlicMessage (std::shared_ptr msg) + { + if (m_LocalDestination) + m_LocalDestination->ProcessGarlicMessage (msg); + else + LogPrint (eLogWarning, "Local destination doesn't exist. Dropped"); + } + + void TunnelPool::ProcessDeliveryStatus (std::shared_ptr msg) + { + const uint8_t * buf = msg->GetPayload (); + uint32_t msgID = bufbe32toh (buf); + buf += 4; + uint64_t timestamp = bufbe64toh (buf); - auto it = m_Tests.find (msgID); - if (it != m_Tests.end ()) - { - // restore from test failed state if any - if (it->second.first->GetState () == eTunnelStateTestFailed) - it->second.first->SetState (eTunnelStateEstablished); - if (it->second.second->GetState () == eTunnelStateTestFailed) - it->second.second->SetState (eTunnelStateEstablished); - LogPrint ("Tunnel test ", it->first, " successive. ", i2p::util::GetMillisecondsSinceEpoch () - timestamp, " milliseconds"); - m_Tests.erase (it); - } - else - { - if (m_LocalDestination) - m_LocalDestination->ProcessDeliveryStatusMessage (msg); - else - LogPrint (eLogWarning, "Local destination doesn't exist. Dropped"); - } - } + auto it = m_Tests.find (msgID); + if (it != m_Tests.end ()) + { + // restore from test failed state if any + if (it->second.first->GetState () == eTunnelStateTestFailed) + it->second.first->SetState (eTunnelStateEstablished); + if (it->second.second->GetState () == eTunnelStateTestFailed) + it->second.second->SetState (eTunnelStateEstablished); + LogPrint ("Tunnel test ", it->first, " successive. ", i2p::util::GetMillisecondsSinceEpoch () - timestamp, " milliseconds"); + m_Tests.erase (it); + } + else + { + if (m_LocalDestination) + m_LocalDestination->ProcessDeliveryStatusMessage (msg); + else + LogPrint (eLogWarning, "Local destination doesn't exist. Dropped"); + } + } - std::shared_ptr TunnelPool::SelectNextHop (std::shared_ptr prevHop) const - { - bool isExploratory = (m_LocalDestination == &i2p::context); // TODO: implement it better - auto hop = isExploratory ? i2p::data::netdb.GetRandomRouter (prevHop): - i2p::data::netdb.GetHighBandwidthRandomRouter (prevHop); + std::shared_ptr TunnelPool::SelectNextHop (std::shared_ptr prevHop) const + { + bool isExploratory = (m_LocalDestination == &i2p::context); // TODO: implement it better + auto hop = isExploratory ? i2p::data::netdb.GetRandomRouter (prevHop): + i2p::data::netdb.GetHighBandwidthRandomRouter (prevHop); - if (!hop || hop->GetProfile ()->IsBad ()) - hop = i2p::data::netdb.GetRandomRouter (); - return hop; - } + if (!hop || hop->GetProfile ()->IsBad ()) + hop = i2p::data::netdb.GetRandomRouter (); + return hop; + } - bool TunnelPool::SelectPeers (std::vector >& hops, bool isInbound) - { - if (m_ExplicitPeers) return SelectExplicitPeers (hops, isInbound); - auto prevHop = i2p::context.GetSharedRouterInfo (); - int numHops = isInbound ? m_NumInboundHops : m_NumOutboundHops; - if (i2p::transport::transports.GetNumPeers () > 25) - { - auto r = i2p::transport::transports.GetRandomPeer (); - if (r && !r->GetProfile ()->IsBad ()) - { - prevHop = r; - hops.push_back (r); - numHops--; - } - } - - for (int i = 0; i < numHops; i++) - { - auto hop = SelectNextHop (prevHop); - if (!hop) - { - LogPrint (eLogError, "Can't select next hop"); - return false; - } - prevHop = hop; - hops.push_back (hop); - } - return true; - } - - bool TunnelPool::SelectExplicitPeers (std::vector >& hops, bool isInbound) - { - int size = m_ExplicitPeers->size (); - std::vector peerIndicies; - for (int i = 0; i < size; i++) peerIndicies.push_back(i); - std::random_shuffle (peerIndicies.begin(), peerIndicies.end()); - - int numHops = isInbound ? m_NumInboundHops : m_NumOutboundHops; - for (int i = 0; i < numHops; i++) - { - auto& ident = (*m_ExplicitPeers)[peerIndicies[i]]; - auto r = i2p::data::netdb.FindRouter (ident); - if (r) - hops.push_back (r); - else - { - LogPrint (eLogInfo, "Can't find router for ", ident.ToBase64 ()); - i2p::data::netdb.RequestDestination (ident); - return false; - } - } - return true; - } - - void TunnelPool::CreateInboundTunnel () - { - auto outboundTunnel = GetNextOutboundTunnel (); - if (!outboundTunnel) - outboundTunnel = tunnels.GetNextOutboundTunnel (); - LogPrint ("Creating destination inbound tunnel..."); - std::vector > hops; - if (SelectPeers (hops, true)) - { - std::reverse (hops.begin (), hops.end ()); - auto tunnel = tunnels.CreateTunnel (std::make_shared (hops), outboundTunnel); - tunnel->SetTunnelPool (shared_from_this ()); - } - else - LogPrint (eLogError, "Can't create inbound tunnel. No peers available"); - } + bool TunnelPool::SelectPeers (std::vector >& hops, bool isInbound) + { + if (m_ExplicitPeers) return SelectExplicitPeers (hops, isInbound); + auto prevHop = i2p::context.GetSharedRouterInfo (); + int numHops = isInbound ? m_NumInboundHops : m_NumOutboundHops; + if (i2p::transport::transports.GetNumPeers () > 25) + { + auto r = i2p::transport::transports.GetRandomPeer (); + if (r && !r->GetProfile ()->IsBad ()) + { + prevHop = r; + hops.push_back (r); + numHops--; + } + } + + for (int i = 0; i < numHops; i++) + { + auto hop = SelectNextHop (prevHop); + if (!hop) + { + LogPrint (eLogError, "Can't select next hop"); + return false; + } + prevHop = hop; + hops.push_back (hop); + } + return true; + } + + bool TunnelPool::SelectExplicitPeers (std::vector >& hops, bool isInbound) + { + int size = m_ExplicitPeers->size (); + std::vector peerIndicies; + for (int i = 0; i < size; i++) peerIndicies.push_back(i); + std::random_shuffle (peerIndicies.begin(), peerIndicies.end()); + + int numHops = isInbound ? m_NumInboundHops : m_NumOutboundHops; + for (int i = 0; i < numHops; i++) + { + auto& ident = (*m_ExplicitPeers)[peerIndicies[i]]; + auto r = i2p::data::netdb.FindRouter (ident); + if (r) + hops.push_back (r); + else + { + LogPrint (eLogInfo, "Can't find router for ", ident.ToBase64 ()); + i2p::data::netdb.RequestDestination (ident); + return false; + } + } + return true; + } + + void TunnelPool::CreateInboundTunnel () + { + auto outboundTunnel = GetNextOutboundTunnel (); + if (!outboundTunnel) + outboundTunnel = tunnels.GetNextOutboundTunnel (); + LogPrint ("Creating destination inbound tunnel..."); + std::vector > hops; + if (SelectPeers (hops, true)) + { + std::reverse (hops.begin (), hops.end ()); + auto tunnel = tunnels.CreateTunnel (std::make_shared (hops), outboundTunnel); + tunnel->SetTunnelPool (shared_from_this ()); + } + else + LogPrint (eLogError, "Can't create inbound tunnel. No peers available"); + } - void TunnelPool::RecreateInboundTunnel (std::shared_ptr tunnel) - { - auto outboundTunnel = GetNextOutboundTunnel (); - if (!outboundTunnel) - outboundTunnel = tunnels.GetNextOutboundTunnel (); - LogPrint ("Re-creating destination inbound tunnel..."); - auto newTunnel = tunnels.CreateTunnel (tunnel->GetTunnelConfig ()->Clone (), outboundTunnel); - newTunnel->SetTunnelPool (shared_from_this()); - } - - void TunnelPool::CreateOutboundTunnel () - { - auto inboundTunnel = GetNextInboundTunnel (); - if (!inboundTunnel) - inboundTunnel = tunnels.GetNextInboundTunnel (); - if (inboundTunnel) - { - LogPrint ("Creating destination outbound tunnel..."); - std::vector > hops; - if (SelectPeers (hops, false)) - { - auto tunnel = tunnels.CreateTunnel ( - std::make_shared (hops, inboundTunnel->GetTunnelConfig ())); - tunnel->SetTunnelPool (shared_from_this ()); - } - else - LogPrint (eLogError, "Can't create outbound tunnel. No peers available"); - } - else - LogPrint (eLogError, "Can't create outbound tunnel. No inbound tunnels found"); - } - - void TunnelPool::RecreateOutboundTunnel (std::shared_ptr tunnel) - { - auto inboundTunnel = GetNextInboundTunnel (); - if (!inboundTunnel) - inboundTunnel = tunnels.GetNextInboundTunnel (); - if (inboundTunnel) - { - LogPrint ("Re-creating destination outbound tunnel..."); - auto newTunnel = tunnels.CreateTunnel ( - tunnel->GetTunnelConfig ()->Clone (inboundTunnel->GetTunnelConfig ())); - newTunnel->SetTunnelPool (shared_from_this ()); - } - else - LogPrint ("Can't re-create outbound tunnel. No inbound tunnels found"); - } + void TunnelPool::RecreateInboundTunnel (std::shared_ptr tunnel) + { + auto outboundTunnel = GetNextOutboundTunnel (); + if (!outboundTunnel) + outboundTunnel = tunnels.GetNextOutboundTunnel (); + LogPrint ("Re-creating destination inbound tunnel..."); + auto newTunnel = tunnels.CreateTunnel (tunnel->GetTunnelConfig ()->Clone (), outboundTunnel); + newTunnel->SetTunnelPool (shared_from_this()); + } + + void TunnelPool::CreateOutboundTunnel () + { + auto inboundTunnel = GetNextInboundTunnel (); + if (!inboundTunnel) + inboundTunnel = tunnels.GetNextInboundTunnel (); + if (inboundTunnel) + { + LogPrint ("Creating destination outbound tunnel..."); + std::vector > hops; + if (SelectPeers (hops, false)) + { + auto tunnel = tunnels.CreateTunnel ( + std::make_shared (hops, inboundTunnel->GetTunnelConfig ())); + tunnel->SetTunnelPool (shared_from_this ()); + } + else + LogPrint (eLogError, "Can't create outbound tunnel. No peers available"); + } + else + LogPrint (eLogError, "Can't create outbound tunnel. No inbound tunnels found"); + } + + void TunnelPool::RecreateOutboundTunnel (std::shared_ptr tunnel) + { + auto inboundTunnel = GetNextInboundTunnel (); + if (!inboundTunnel) + inboundTunnel = tunnels.GetNextInboundTunnel (); + if (inboundTunnel) + { + LogPrint ("Re-creating destination outbound tunnel..."); + auto newTunnel = tunnels.CreateTunnel ( + tunnel->GetTunnelConfig ()->Clone (inboundTunnel->GetTunnelConfig ())); + newTunnel->SetTunnelPool (shared_from_this ()); + } + else + LogPrint ("Can't re-create outbound tunnel. No inbound tunnels found"); + } - void TunnelPool::CreatePairedInboundTunnel (std::shared_ptr outboundTunnel) - { - LogPrint (eLogInfo, "Creating paired inbound tunnel..."); - auto tunnel = tunnels.CreateTunnel (outboundTunnel->GetTunnelConfig ()->Invert (), outboundTunnel); - tunnel->SetTunnelPool (shared_from_this ()); - } + void TunnelPool::CreatePairedInboundTunnel (std::shared_ptr outboundTunnel) + { + LogPrint (eLogInfo, "Creating paired inbound tunnel..."); + auto tunnel = tunnels.CreateTunnel (outboundTunnel->GetTunnelConfig ()->Invert (), outboundTunnel); + tunnel->SetTunnelPool (shared_from_this ()); + } } } diff --git a/TunnelPool.h b/TunnelPool.h index 2d4203ef..e7d81c3b 100644 --- a/TunnelPool.h +++ b/TunnelPool.h @@ -19,71 +19,71 @@ namespace i2p { namespace tunnel { - class Tunnel; - class InboundTunnel; - class OutboundTunnel; + class Tunnel; + class InboundTunnel; + class OutboundTunnel; - class TunnelPool: public std::enable_shared_from_this // per local destination - { - public: + class TunnelPool: public std::enable_shared_from_this // per local destination + { + public: - TunnelPool (i2p::garlic::GarlicDestination * localDestination, int numInboundHops, int numOutboundHops, int numInboundTunnels, int numOutboundTunnels); - ~TunnelPool (); - - i2p::garlic::GarlicDestination * GetLocalDestination () const { return m_LocalDestination; }; - void SetLocalDestination (i2p::garlic::GarlicDestination * destination) { m_LocalDestination = destination; }; - void SetExplicitPeers (std::shared_ptr > explicitPeers); + TunnelPool (i2p::garlic::GarlicDestination * localDestination, int numInboundHops, int numOutboundHops, int numInboundTunnels, int numOutboundTunnels); + ~TunnelPool (); + + i2p::garlic::GarlicDestination * GetLocalDestination () const { return m_LocalDestination; }; + void SetLocalDestination (i2p::garlic::GarlicDestination * destination) { m_LocalDestination = destination; }; + void SetExplicitPeers (std::shared_ptr > explicitPeers); - void CreateTunnels (); - void TunnelCreated (std::shared_ptr createdTunnel); - void TunnelExpired (std::shared_ptr expiredTunnel); - void TunnelCreated (std::shared_ptr createdTunnel); - void TunnelExpired (std::shared_ptr expiredTunnel); - void RecreateInboundTunnel (std::shared_ptr tunnel); - void RecreateOutboundTunnel (std::shared_ptr tunnel); - std::vector > GetInboundTunnels (int num) const; - std::shared_ptr GetNextOutboundTunnel (std::shared_ptr excluded = nullptr) const; - std::shared_ptr GetNextInboundTunnel (std::shared_ptr excluded = nullptr) const; - std::shared_ptr GetNewOutboundTunnel (std::shared_ptr old) const; + void CreateTunnels (); + void TunnelCreated (std::shared_ptr createdTunnel); + void TunnelExpired (std::shared_ptr expiredTunnel); + void TunnelCreated (std::shared_ptr createdTunnel); + void TunnelExpired (std::shared_ptr expiredTunnel); + void RecreateInboundTunnel (std::shared_ptr tunnel); + void RecreateOutboundTunnel (std::shared_ptr tunnel); + std::vector > GetInboundTunnels (int num) const; + std::shared_ptr GetNextOutboundTunnel (std::shared_ptr excluded = nullptr) const; + std::shared_ptr GetNextInboundTunnel (std::shared_ptr excluded = nullptr) const; + std::shared_ptr GetNewOutboundTunnel (std::shared_ptr old) const; - void TestTunnels (); - void ProcessGarlicMessage (std::shared_ptr msg); - void ProcessDeliveryStatus (std::shared_ptr msg); + void TestTunnels (); + void ProcessGarlicMessage (std::shared_ptr msg); + void ProcessDeliveryStatus (std::shared_ptr msg); - bool IsActive () const { return m_IsActive; }; - void SetActive (bool isActive) { m_IsActive = isActive; }; - void DetachTunnels (); - - private: + bool IsActive () const { return m_IsActive; }; + void SetActive (bool isActive) { m_IsActive = isActive; }; + void DetachTunnels (); + + private: - void CreateInboundTunnel (); - void CreateOutboundTunnel (); - void CreatePairedInboundTunnel (std::shared_ptr outboundTunnel); - template - typename TTunnels::value_type GetNextTunnel (TTunnels& tunnels, typename TTunnels::value_type excluded) const; - std::shared_ptr SelectNextHop (std::shared_ptr prevHop) const; - bool SelectPeers (std::vector >& hops, bool isInbound); - bool SelectExplicitPeers (std::vector >& hops, bool isInbound); + void CreateInboundTunnel (); + void CreateOutboundTunnel (); + void CreatePairedInboundTunnel (std::shared_ptr outboundTunnel); + template + typename TTunnels::value_type GetNextTunnel (TTunnels& tunnels, typename TTunnels::value_type excluded) const; + std::shared_ptr SelectNextHop (std::shared_ptr prevHop) const; + bool SelectPeers (std::vector >& hops, bool isInbound); + bool SelectExplicitPeers (std::vector >& hops, bool isInbound); - private: + private: - i2p::garlic::GarlicDestination * m_LocalDestination; - int m_NumInboundHops, m_NumOutboundHops, m_NumInboundTunnels, m_NumOutboundTunnels; - std::shared_ptr > m_ExplicitPeers; - mutable std::mutex m_InboundTunnelsMutex; - std::set, TunnelCreationTimeCmp> m_InboundTunnels; // recent tunnel appears first - mutable std::mutex m_OutboundTunnelsMutex; - std::set, TunnelCreationTimeCmp> m_OutboundTunnels; - std::map, std::shared_ptr > > m_Tests; - bool m_IsActive; + i2p::garlic::GarlicDestination * m_LocalDestination; + int m_NumInboundHops, m_NumOutboundHops, m_NumInboundTunnels, m_NumOutboundTunnels; + std::shared_ptr > m_ExplicitPeers; + mutable std::mutex m_InboundTunnelsMutex; + std::set, TunnelCreationTimeCmp> m_InboundTunnels; // recent tunnel appears first + mutable std::mutex m_OutboundTunnelsMutex; + std::set, TunnelCreationTimeCmp> m_OutboundTunnels; + std::map, std::shared_ptr > > m_Tests; + bool m_IsActive; - public: + public: - // for HTTP only - const decltype(m_OutboundTunnels)& GetOutboundTunnels () const { return m_OutboundTunnels; }; - const decltype(m_InboundTunnels)& GetInboundTunnels () const { return m_InboundTunnels; }; + // for HTTP only + const decltype(m_OutboundTunnels)& GetOutboundTunnels () const { return m_OutboundTunnels; }; + const decltype(m_InboundTunnels)& GetInboundTunnels () const { return m_InboundTunnels; }; - }; + }; } } diff --git a/UPnP.h b/UPnP.h index 1a7b55c5..e1211a29 100644 --- a/UPnP.h +++ b/UPnP.h @@ -21,22 +21,22 @@ namespace i2p { namespace transport { - class UPnP - { - public: + class UPnP + { + public: - UPnP (); - ~UPnP (); + UPnP (); + ~UPnP (); void Close (); void Start (); void Stop (); - void Discover (); - void TryPortMapping (int type, int port); - void CloseMapping (int type, int port); - private: - void Run (); + void Discover (); + void TryPortMapping (int type, int port); + void CloseMapping (int type, int port); + private: + void Run (); std::thread * m_Thread; struct UPNPUrls m_upnpUrls; @@ -54,7 +54,7 @@ namespace transport #else HINSTANCE m_Module; #endif - }; + }; } } diff --git a/aes.cpp b/aes.cpp index 506e7ca8..fe354b9f 100644 --- a/aes.cpp +++ b/aes.cpp @@ -8,350 +8,350 @@ namespace crypto { #ifdef AESNI - - #define KeyExpansion256(round0,round1) \ - "pshufd $0xff, %%xmm2, %%xmm2 \n" \ - "movaps %%xmm1, %%xmm4 \n" \ - "pslldq $4, %%xmm4 \n" \ - "pxor %%xmm4, %%xmm1 \n" \ - "pslldq $4, %%xmm4 \n" \ - "pxor %%xmm4, %%xmm1 \n" \ - "pslldq $4, %%xmm4 \n" \ - "pxor %%xmm4, %%xmm1 \n" \ - "pxor %%xmm2, %%xmm1 \n" \ - "movaps %%xmm1, "#round0"(%[sched]) \n" \ - "aeskeygenassist $0, %%xmm1, %%xmm4 \n" \ - "pshufd $0xaa, %%xmm4, %%xmm2 \n" \ - "movaps %%xmm3, %%xmm4 \n" \ - "pslldq $4, %%xmm4 \n" \ - "pxor %%xmm4, %%xmm3 \n" \ - "pslldq $4, %%xmm4 \n" \ - "pxor %%xmm4, %%xmm3 \n" \ - "pslldq $4, %%xmm4 \n" \ - "pxor %%xmm4, %%xmm3 \n" \ - "pxor %%xmm2, %%xmm3 \n" \ - "movaps %%xmm3, "#round1"(%[sched]) \n" + + #define KeyExpansion256(round0,round1) \ + "pshufd $0xff, %%xmm2, %%xmm2 \n" \ + "movaps %%xmm1, %%xmm4 \n" \ + "pslldq $4, %%xmm4 \n" \ + "pxor %%xmm4, %%xmm1 \n" \ + "pslldq $4, %%xmm4 \n" \ + "pxor %%xmm4, %%xmm1 \n" \ + "pslldq $4, %%xmm4 \n" \ + "pxor %%xmm4, %%xmm1 \n" \ + "pxor %%xmm2, %%xmm1 \n" \ + "movaps %%xmm1, "#round0"(%[sched]) \n" \ + "aeskeygenassist $0, %%xmm1, %%xmm4 \n" \ + "pshufd $0xaa, %%xmm4, %%xmm2 \n" \ + "movaps %%xmm3, %%xmm4 \n" \ + "pslldq $4, %%xmm4 \n" \ + "pxor %%xmm4, %%xmm3 \n" \ + "pslldq $4, %%xmm4 \n" \ + "pxor %%xmm4, %%xmm3 \n" \ + "pslldq $4, %%xmm4 \n" \ + "pxor %%xmm4, %%xmm3 \n" \ + "pxor %%xmm2, %%xmm3 \n" \ + "movaps %%xmm3, "#round1"(%[sched]) \n" - void ECBCryptoAESNI::ExpandKey (const AESKey& key) - { - __asm__ - ( - "movups (%[key]), %%xmm1 \n" - "movups 16(%[key]), %%xmm3 \n" - "movaps %%xmm1, (%[sched]) \n" - "movaps %%xmm3, 16(%[sched]) \n" - "aeskeygenassist $1, %%xmm3, %%xmm2 \n" - KeyExpansion256(32,48) - "aeskeygenassist $2, %%xmm3, %%xmm2 \n" - KeyExpansion256(64,80) - "aeskeygenassist $4, %%xmm3, %%xmm2 \n" - KeyExpansion256(96,112) - "aeskeygenassist $8, %%xmm3, %%xmm2 \n" - KeyExpansion256(128,144) - "aeskeygenassist $16, %%xmm3, %%xmm2 \n" - KeyExpansion256(160,176) - "aeskeygenassist $32, %%xmm3, %%xmm2 \n" - KeyExpansion256(192,208) - "aeskeygenassist $64, %%xmm3, %%xmm2 \n" - // key expansion final - "pshufd $0xff, %%xmm2, %%xmm2 \n" - "movaps %%xmm1, %%xmm4 \n" - "pslldq $4, %%xmm4 \n" - "pxor %%xmm4, %%xmm1 \n" - "pslldq $4, %%xmm4 \n" - "pxor %%xmm4, %%xmm1 \n" - "pslldq $4, %%xmm4 \n" - "pxor %%xmm4, %%xmm1 \n" - "pxor %%xmm2, %%xmm1 \n" - "movups %%xmm1, 224(%[sched]) \n" - : // output - : [key]"r"((const uint8_t *)key), [sched]"r"(GetKeySchedule ()) // input - : "%xmm1", "%xmm2", "%xmm3", "%xmm4", "memory" // clogged - ); - } + void ECBCryptoAESNI::ExpandKey (const AESKey& key) + { + __asm__ + ( + "movups (%[key]), %%xmm1 \n" + "movups 16(%[key]), %%xmm3 \n" + "movaps %%xmm1, (%[sched]) \n" + "movaps %%xmm3, 16(%[sched]) \n" + "aeskeygenassist $1, %%xmm3, %%xmm2 \n" + KeyExpansion256(32,48) + "aeskeygenassist $2, %%xmm3, %%xmm2 \n" + KeyExpansion256(64,80) + "aeskeygenassist $4, %%xmm3, %%xmm2 \n" + KeyExpansion256(96,112) + "aeskeygenassist $8, %%xmm3, %%xmm2 \n" + KeyExpansion256(128,144) + "aeskeygenassist $16, %%xmm3, %%xmm2 \n" + KeyExpansion256(160,176) + "aeskeygenassist $32, %%xmm3, %%xmm2 \n" + KeyExpansion256(192,208) + "aeskeygenassist $64, %%xmm3, %%xmm2 \n" + // key expansion final + "pshufd $0xff, %%xmm2, %%xmm2 \n" + "movaps %%xmm1, %%xmm4 \n" + "pslldq $4, %%xmm4 \n" + "pxor %%xmm4, %%xmm1 \n" + "pslldq $4, %%xmm4 \n" + "pxor %%xmm4, %%xmm1 \n" + "pslldq $4, %%xmm4 \n" + "pxor %%xmm4, %%xmm1 \n" + "pxor %%xmm2, %%xmm1 \n" + "movups %%xmm1, 224(%[sched]) \n" + : // output + : [key]"r"((const uint8_t *)key), [sched]"r"(GetKeySchedule ()) // input + : "%xmm1", "%xmm2", "%xmm3", "%xmm4", "memory" // clogged + ); + } - #define EncryptAES256(sched) \ - "pxor (%["#sched"]), %%xmm0 \n" \ - "aesenc 16(%["#sched"]), %%xmm0 \n" \ - "aesenc 32(%["#sched"]), %%xmm0 \n" \ - "aesenc 48(%["#sched"]), %%xmm0 \n" \ - "aesenc 64(%["#sched"]), %%xmm0 \n" \ - "aesenc 80(%["#sched"]), %%xmm0 \n" \ - "aesenc 96(%["#sched"]), %%xmm0 \n" \ - "aesenc 112(%["#sched"]), %%xmm0 \n" \ - "aesenc 128(%["#sched"]), %%xmm0 \n" \ - "aesenc 144(%["#sched"]), %%xmm0 \n" \ - "aesenc 160(%["#sched"]), %%xmm0 \n" \ - "aesenc 176(%["#sched"]), %%xmm0 \n" \ - "aesenc 192(%["#sched"]), %%xmm0 \n" \ - "aesenc 208(%["#sched"]), %%xmm0 \n" \ - "aesenclast 224(%["#sched"]), %%xmm0 \n" - - void ECBEncryptionAESNI::Encrypt (const ChipherBlock * in, ChipherBlock * out) - { - __asm__ - ( - "movups (%[in]), %%xmm0 \n" - EncryptAES256(sched) - "movups %%xmm0, (%[out]) \n" - : : [sched]"r"(GetKeySchedule ()), [in]"r"(in), [out]"r"(out) : "%xmm0", "memory" - ); - } + #define EncryptAES256(sched) \ + "pxor (%["#sched"]), %%xmm0 \n" \ + "aesenc 16(%["#sched"]), %%xmm0 \n" \ + "aesenc 32(%["#sched"]), %%xmm0 \n" \ + "aesenc 48(%["#sched"]), %%xmm0 \n" \ + "aesenc 64(%["#sched"]), %%xmm0 \n" \ + "aesenc 80(%["#sched"]), %%xmm0 \n" \ + "aesenc 96(%["#sched"]), %%xmm0 \n" \ + "aesenc 112(%["#sched"]), %%xmm0 \n" \ + "aesenc 128(%["#sched"]), %%xmm0 \n" \ + "aesenc 144(%["#sched"]), %%xmm0 \n" \ + "aesenc 160(%["#sched"]), %%xmm0 \n" \ + "aesenc 176(%["#sched"]), %%xmm0 \n" \ + "aesenc 192(%["#sched"]), %%xmm0 \n" \ + "aesenc 208(%["#sched"]), %%xmm0 \n" \ + "aesenclast 224(%["#sched"]), %%xmm0 \n" + + void ECBEncryptionAESNI::Encrypt (const ChipherBlock * in, ChipherBlock * out) + { + __asm__ + ( + "movups (%[in]), %%xmm0 \n" + EncryptAES256(sched) + "movups %%xmm0, (%[out]) \n" + : : [sched]"r"(GetKeySchedule ()), [in]"r"(in), [out]"r"(out) : "%xmm0", "memory" + ); + } - #define DecryptAES256(sched) \ - "pxor 224(%["#sched"]), %%xmm0 \n" \ - "aesdec 208(%["#sched"]), %%xmm0 \n" \ - "aesdec 192(%["#sched"]), %%xmm0 \n" \ - "aesdec 176(%["#sched"]), %%xmm0 \n" \ - "aesdec 160(%["#sched"]), %%xmm0 \n" \ - "aesdec 144(%["#sched"]), %%xmm0 \n" \ - "aesdec 128(%["#sched"]), %%xmm0 \n" \ - "aesdec 112(%["#sched"]), %%xmm0 \n" \ - "aesdec 96(%["#sched"]), %%xmm0 \n" \ - "aesdec 80(%["#sched"]), %%xmm0 \n" \ - "aesdec 64(%["#sched"]), %%xmm0 \n" \ - "aesdec 48(%["#sched"]), %%xmm0 \n" \ - "aesdec 32(%["#sched"]), %%xmm0 \n" \ - "aesdec 16(%["#sched"]), %%xmm0 \n" \ - "aesdeclast (%["#sched"]), %%xmm0 \n" - - void ECBDecryptionAESNI::Decrypt (const ChipherBlock * in, ChipherBlock * out) - { - __asm__ - ( - "movups (%[in]), %%xmm0 \n" - DecryptAES256(sched) - "movups %%xmm0, (%[out]) \n" - : : [sched]"r"(GetKeySchedule ()), [in]"r"(in), [out]"r"(out) : "%xmm0", "memory" - ); - } + #define DecryptAES256(sched) \ + "pxor 224(%["#sched"]), %%xmm0 \n" \ + "aesdec 208(%["#sched"]), %%xmm0 \n" \ + "aesdec 192(%["#sched"]), %%xmm0 \n" \ + "aesdec 176(%["#sched"]), %%xmm0 \n" \ + "aesdec 160(%["#sched"]), %%xmm0 \n" \ + "aesdec 144(%["#sched"]), %%xmm0 \n" \ + "aesdec 128(%["#sched"]), %%xmm0 \n" \ + "aesdec 112(%["#sched"]), %%xmm0 \n" \ + "aesdec 96(%["#sched"]), %%xmm0 \n" \ + "aesdec 80(%["#sched"]), %%xmm0 \n" \ + "aesdec 64(%["#sched"]), %%xmm0 \n" \ + "aesdec 48(%["#sched"]), %%xmm0 \n" \ + "aesdec 32(%["#sched"]), %%xmm0 \n" \ + "aesdec 16(%["#sched"]), %%xmm0 \n" \ + "aesdeclast (%["#sched"]), %%xmm0 \n" + + void ECBDecryptionAESNI::Decrypt (const ChipherBlock * in, ChipherBlock * out) + { + __asm__ + ( + "movups (%[in]), %%xmm0 \n" + DecryptAES256(sched) + "movups %%xmm0, (%[out]) \n" + : : [sched]"r"(GetKeySchedule ()), [in]"r"(in), [out]"r"(out) : "%xmm0", "memory" + ); + } - #define CallAESIMC(offset) \ - "movaps "#offset"(%[shed]), %%xmm0 \n" \ - "aesimc %%xmm0, %%xmm0 \n" \ - "movaps %%xmm0, "#offset"(%[shed]) \n" + #define CallAESIMC(offset) \ + "movaps "#offset"(%[shed]), %%xmm0 \n" \ + "aesimc %%xmm0, %%xmm0 \n" \ + "movaps %%xmm0, "#offset"(%[shed]) \n" - void ECBDecryptionAESNI::SetKey (const AESKey& key) - { - ExpandKey (key); // expand encryption key first - // then invert it using aesimc - __asm__ - ( - CallAESIMC(16) - CallAESIMC(32) - CallAESIMC(48) - CallAESIMC(64) - CallAESIMC(80) - CallAESIMC(96) - CallAESIMC(112) - CallAESIMC(128) - CallAESIMC(144) - CallAESIMC(160) - CallAESIMC(176) - CallAESIMC(192) - CallAESIMC(208) - : : [shed]"r"(GetKeySchedule ()) : "%xmm0", "memory" - ); - } + void ECBDecryptionAESNI::SetKey (const AESKey& key) + { + ExpandKey (key); // expand encryption key first + // then invert it using aesimc + __asm__ + ( + CallAESIMC(16) + CallAESIMC(32) + CallAESIMC(48) + CallAESIMC(64) + CallAESIMC(80) + CallAESIMC(96) + CallAESIMC(112) + CallAESIMC(128) + CallAESIMC(144) + CallAESIMC(160) + CallAESIMC(176) + CallAESIMC(192) + CallAESIMC(208) + : : [shed]"r"(GetKeySchedule ()) : "%xmm0", "memory" + ); + } -#endif +#endif - void CBCEncryption::Encrypt (int numBlocks, const ChipherBlock * in, ChipherBlock * out) - { + void CBCEncryption::Encrypt (int numBlocks, const ChipherBlock * in, ChipherBlock * out) + { #ifdef AESNI - __asm__ - ( - "movups (%[iv]), %%xmm1 \n" - "1: \n" - "movups (%[in]), %%xmm0 \n" - "pxor %%xmm1, %%xmm0 \n" - EncryptAES256(sched) - "movaps %%xmm0, %%xmm1 \n" - "movups %%xmm0, (%[out]) \n" - "add $16, %[in] \n" - "add $16, %[out] \n" - "dec %[num] \n" - "jnz 1b \n" - "movups %%xmm1, (%[iv]) \n" - : - : [iv]"r"(&m_LastBlock), [sched]"r"(m_ECBEncryption.GetKeySchedule ()), - [in]"r"(in), [out]"r"(out), [num]"r"(numBlocks) - : "%xmm0", "%xmm1", "cc", "memory" - ); -#else - for (int i = 0; i < numBlocks; i++) - { - m_LastBlock ^= in[i]; - m_ECBEncryption.Encrypt (&m_LastBlock, &m_LastBlock); - out[i] = m_LastBlock; - } -#endif - } + __asm__ + ( + "movups (%[iv]), %%xmm1 \n" + "1: \n" + "movups (%[in]), %%xmm0 \n" + "pxor %%xmm1, %%xmm0 \n" + EncryptAES256(sched) + "movaps %%xmm0, %%xmm1 \n" + "movups %%xmm0, (%[out]) \n" + "add $16, %[in] \n" + "add $16, %[out] \n" + "dec %[num] \n" + "jnz 1b \n" + "movups %%xmm1, (%[iv]) \n" + : + : [iv]"r"(&m_LastBlock), [sched]"r"(m_ECBEncryption.GetKeySchedule ()), + [in]"r"(in), [out]"r"(out), [num]"r"(numBlocks) + : "%xmm0", "%xmm1", "cc", "memory" + ); +#else + for (int i = 0; i < numBlocks; i++) + { + m_LastBlock ^= in[i]; + m_ECBEncryption.Encrypt (&m_LastBlock, &m_LastBlock); + out[i] = m_LastBlock; + } +#endif + } - void CBCEncryption::Encrypt (const uint8_t * in, std::size_t len, uint8_t * out) - { - // len/16 - int numBlocks = len >> 4; - if (numBlocks > 0) - Encrypt (numBlocks, (const ChipherBlock *)in, (ChipherBlock *)out); - } + void CBCEncryption::Encrypt (const uint8_t * in, std::size_t len, uint8_t * out) + { + // len/16 + int numBlocks = len >> 4; + if (numBlocks > 0) + Encrypt (numBlocks, (const ChipherBlock *)in, (ChipherBlock *)out); + } - void CBCEncryption::Encrypt (const uint8_t * in, uint8_t * out) - { + void CBCEncryption::Encrypt (const uint8_t * in, uint8_t * out) + { #ifdef AESNI - __asm__ - ( - "movups (%[iv]), %%xmm1 \n" - "movups (%[in]), %%xmm0 \n" - "pxor %%xmm1, %%xmm0 \n" - EncryptAES256(sched) - "movups %%xmm0, (%[out]) \n" - "movups %%xmm0, (%[iv]) \n" - : - : [iv]"r"(&m_LastBlock), [sched]"r"(m_ECBEncryption.GetKeySchedule ()), - [in]"r"(in), [out]"r"(out) - : "%xmm0", "%xmm1", "memory" - ); + __asm__ + ( + "movups (%[iv]), %%xmm1 \n" + "movups (%[in]), %%xmm0 \n" + "pxor %%xmm1, %%xmm0 \n" + EncryptAES256(sched) + "movups %%xmm0, (%[out]) \n" + "movups %%xmm0, (%[iv]) \n" + : + : [iv]"r"(&m_LastBlock), [sched]"r"(m_ECBEncryption.GetKeySchedule ()), + [in]"r"(in), [out]"r"(out) + : "%xmm0", "%xmm1", "memory" + ); #else - Encrypt (1, (const ChipherBlock *)in, (ChipherBlock *)out); + Encrypt (1, (const ChipherBlock *)in, (ChipherBlock *)out); #endif - } + } - void CBCDecryption::Decrypt (int numBlocks, const ChipherBlock * in, ChipherBlock * out) - { + void CBCDecryption::Decrypt (int numBlocks, const ChipherBlock * in, ChipherBlock * out) + { #ifdef AESNI - __asm__ - ( - "movups (%[iv]), %%xmm1 \n" - "1: \n" - "movups (%[in]), %%xmm0 \n" - "movaps %%xmm0, %%xmm2 \n" - DecryptAES256(sched) - "pxor %%xmm1, %%xmm0 \n" - "movups %%xmm0, (%[out]) \n" - "movaps %%xmm2, %%xmm1 \n" - "add $16, %[in] \n" - "add $16, %[out] \n" - "dec %[num] \n" - "jnz 1b \n" - "movups %%xmm1, (%[iv]) \n" - : - : [iv]"r"(&m_IV), [sched]"r"(m_ECBDecryption.GetKeySchedule ()), - [in]"r"(in), [out]"r"(out), [num]"r"(numBlocks) - : "%xmm0", "%xmm1", "%xmm2", "cc", "memory" - ); + __asm__ + ( + "movups (%[iv]), %%xmm1 \n" + "1: \n" + "movups (%[in]), %%xmm0 \n" + "movaps %%xmm0, %%xmm2 \n" + DecryptAES256(sched) + "pxor %%xmm1, %%xmm0 \n" + "movups %%xmm0, (%[out]) \n" + "movaps %%xmm2, %%xmm1 \n" + "add $16, %[in] \n" + "add $16, %[out] \n" + "dec %[num] \n" + "jnz 1b \n" + "movups %%xmm1, (%[iv]) \n" + : + : [iv]"r"(&m_IV), [sched]"r"(m_ECBDecryption.GetKeySchedule ()), + [in]"r"(in), [out]"r"(out), [num]"r"(numBlocks) + : "%xmm0", "%xmm1", "%xmm2", "cc", "memory" + ); #else - for (int i = 0; i < numBlocks; i++) - { - ChipherBlock tmp = in[i]; - m_ECBDecryption.Decrypt (in + i, out + i); - out[i] ^= m_IV; - m_IV = tmp; - } + for (int i = 0; i < numBlocks; i++) + { + ChipherBlock tmp = in[i]; + m_ECBDecryption.Decrypt (in + i, out + i); + out[i] ^= m_IV; + m_IV = tmp; + } #endif - } + } - void CBCDecryption::Decrypt (const uint8_t * in, std::size_t len, uint8_t * out) - { - int numBlocks = len >> 4; - if (numBlocks > 0) - Decrypt (numBlocks, (const ChipherBlock *)in, (ChipherBlock *)out); - } + void CBCDecryption::Decrypt (const uint8_t * in, std::size_t len, uint8_t * out) + { + int numBlocks = len >> 4; + if (numBlocks > 0) + Decrypt (numBlocks, (const ChipherBlock *)in, (ChipherBlock *)out); + } - void CBCDecryption::Decrypt (const uint8_t * in, uint8_t * out) - { + void CBCDecryption::Decrypt (const uint8_t * in, uint8_t * out) + { #ifdef AESNI - __asm__ - ( - "movups (%[iv]), %%xmm1 \n" - "movups (%[in]), %%xmm0 \n" - "movups %%xmm0, (%[iv]) \n" - DecryptAES256(sched) - "pxor %%xmm1, %%xmm0 \n" - "movups %%xmm0, (%[out]) \n" - : - : [iv]"r"(&m_IV), [sched]"r"(m_ECBDecryption.GetKeySchedule ()), - [in]"r"(in), [out]"r"(out) - : "%xmm0", "%xmm1", "memory" - ); + __asm__ + ( + "movups (%[iv]), %%xmm1 \n" + "movups (%[in]), %%xmm0 \n" + "movups %%xmm0, (%[iv]) \n" + DecryptAES256(sched) + "pxor %%xmm1, %%xmm0 \n" + "movups %%xmm0, (%[out]) \n" + : + : [iv]"r"(&m_IV), [sched]"r"(m_ECBDecryption.GetKeySchedule ()), + [in]"r"(in), [out]"r"(out) + : "%xmm0", "%xmm1", "memory" + ); #else - Decrypt (1, (const ChipherBlock *)in, (ChipherBlock *)out); + Decrypt (1, (const ChipherBlock *)in, (ChipherBlock *)out); #endif - } + } - void TunnelEncryption::Encrypt (const uint8_t * in, uint8_t * out) - { + void TunnelEncryption::Encrypt (const uint8_t * in, uint8_t * out) + { #ifdef AESNI - __asm__ - ( + __asm__ + ( // encrypt IV - "movups (%[in]), %%xmm0 \n" - EncryptAES256(sched_iv) - "movaps %%xmm0, %%xmm1 \n" - // double IV encryption - EncryptAES256(sched_iv) - "movups %%xmm0, (%[out]) \n" - // encrypt data, IV is xmm1 - "1: \n" - "add $16, %[in] \n" - "add $16, %[out] \n" - "movups (%[in]), %%xmm0 \n" - "pxor %%xmm1, %%xmm0 \n" - EncryptAES256(sched_l) - "movaps %%xmm0, %%xmm1 \n" - "movups %%xmm0, (%[out]) \n" - "dec %[num] \n" - "jnz 1b \n" - : - : [sched_iv]"r"(m_IVEncryption.GetKeySchedule ()), [sched_l]"r"(m_LayerEncryption.GetKeySchedule ()), - [in]"r"(in), [out]"r"(out), [num]"r"(63) // 63 blocks = 1008 bytes - : "%xmm0", "%xmm1", "cc", "memory" - ); + "movups (%[in]), %%xmm0 \n" + EncryptAES256(sched_iv) + "movaps %%xmm0, %%xmm1 \n" + // double IV encryption + EncryptAES256(sched_iv) + "movups %%xmm0, (%[out]) \n" + // encrypt data, IV is xmm1 + "1: \n" + "add $16, %[in] \n" + "add $16, %[out] \n" + "movups (%[in]), %%xmm0 \n" + "pxor %%xmm1, %%xmm0 \n" + EncryptAES256(sched_l) + "movaps %%xmm0, %%xmm1 \n" + "movups %%xmm0, (%[out]) \n" + "dec %[num] \n" + "jnz 1b \n" + : + : [sched_iv]"r"(m_IVEncryption.GetKeySchedule ()), [sched_l]"r"(m_LayerEncryption.GetKeySchedule ()), + [in]"r"(in), [out]"r"(out), [num]"r"(63) // 63 blocks = 1008 bytes + : "%xmm0", "%xmm1", "cc", "memory" + ); #else - m_IVEncryption.Encrypt ((const ChipherBlock *)in, (ChipherBlock *)out); // iv - m_LayerEncryption.SetIV (out); - m_LayerEncryption.Encrypt (in + 16, i2p::tunnel::TUNNEL_DATA_ENCRYPTED_SIZE, out + 16); // data - m_IVEncryption.Encrypt ((ChipherBlock *)out, (ChipherBlock *)out); // double iv + m_IVEncryption.Encrypt ((const ChipherBlock *)in, (ChipherBlock *)out); // iv + m_LayerEncryption.SetIV (out); + m_LayerEncryption.Encrypt (in + 16, i2p::tunnel::TUNNEL_DATA_ENCRYPTED_SIZE, out + 16); // data + m_IVEncryption.Encrypt ((ChipherBlock *)out, (ChipherBlock *)out); // double iv #endif - } + } - void TunnelDecryption::Decrypt (const uint8_t * in, uint8_t * out) - { + void TunnelDecryption::Decrypt (const uint8_t * in, uint8_t * out) + { #ifdef AESNI - __asm__ - ( + __asm__ + ( // decrypt IV - "movups (%[in]), %%xmm0 \n" - DecryptAES256(sched_iv) - "movaps %%xmm0, %%xmm1 \n" - // double IV encryption - DecryptAES256(sched_iv) - "movups %%xmm0, (%[out]) \n" - // decrypt data, IV is xmm1 - "1: \n" - "add $16, %[in] \n" - "add $16, %[out] \n" - "movups (%[in]), %%xmm0 \n" - "movaps %%xmm0, %%xmm2 \n" - DecryptAES256(sched_l) - "pxor %%xmm1, %%xmm0 \n" - "movups %%xmm0, (%[out]) \n" - "movaps %%xmm2, %%xmm1 \n" - "dec %[num] \n" - "jnz 1b \n" - : - : [sched_iv]"r"(m_IVDecryption.GetKeySchedule ()), [sched_l]"r"(m_LayerDecryption.GetKeySchedule ()), - [in]"r"(in), [out]"r"(out), [num]"r"(63) // 63 blocks = 1008 bytes - : "%xmm0", "%xmm1", "%xmm2", "cc", "memory" - ); + "movups (%[in]), %%xmm0 \n" + DecryptAES256(sched_iv) + "movaps %%xmm0, %%xmm1 \n" + // double IV encryption + DecryptAES256(sched_iv) + "movups %%xmm0, (%[out]) \n" + // decrypt data, IV is xmm1 + "1: \n" + "add $16, %[in] \n" + "add $16, %[out] \n" + "movups (%[in]), %%xmm0 \n" + "movaps %%xmm0, %%xmm2 \n" + DecryptAES256(sched_l) + "pxor %%xmm1, %%xmm0 \n" + "movups %%xmm0, (%[out]) \n" + "movaps %%xmm2, %%xmm1 \n" + "dec %[num] \n" + "jnz 1b \n" + : + : [sched_iv]"r"(m_IVDecryption.GetKeySchedule ()), [sched_l]"r"(m_LayerDecryption.GetKeySchedule ()), + [in]"r"(in), [out]"r"(out), [num]"r"(63) // 63 blocks = 1008 bytes + : "%xmm0", "%xmm1", "%xmm2", "cc", "memory" + ); #else - m_IVDecryption.Decrypt ((const ChipherBlock *)in, (ChipherBlock *)out); // iv - m_LayerDecryption.SetIV (out); - m_LayerDecryption.Decrypt (in + 16, i2p::tunnel::TUNNEL_DATA_ENCRYPTED_SIZE, out + 16); // data - m_IVDecryption.Decrypt ((ChipherBlock *)out, (ChipherBlock *)out); // double iv + m_IVDecryption.Decrypt ((const ChipherBlock *)in, (ChipherBlock *)out); // iv + m_LayerDecryption.SetIV (out); + m_LayerDecryption.Decrypt (in + 16, i2p::tunnel::TUNNEL_DATA_ENCRYPTED_SIZE, out + 16); // data + m_IVDecryption.Decrypt ((ChipherBlock *)out, (ChipherBlock *)out); // double iv #endif - } + } } } diff --git a/aes.h b/aes.h index 56dd97b9..1751246f 100644 --- a/aes.h +++ b/aes.h @@ -9,221 +9,221 @@ namespace i2p { namespace crypto -{ - struct ChipherBlock - { - uint8_t buf[16]; +{ + struct ChipherBlock + { + uint8_t buf[16]; - void operator^=(const ChipherBlock& other) // XOR - { + void operator^=(const ChipherBlock& other) // XOR + { #if defined(__x86_64__) // for Intel x64 - __asm__ - ( - "movups (%[buf]), %%xmm0 \n" - "movups (%[other]), %%xmm1 \n" - "pxor %%xmm1, %%xmm0 \n" - "movups %%xmm0, (%[buf]) \n" - : - : [buf]"r"(buf), [other]"r"(other.buf) - : "%xmm0", "%xmm1", "memory" - ); + __asm__ + ( + "movups (%[buf]), %%xmm0 \n" + "movups (%[other]), %%xmm1 \n" + "pxor %%xmm1, %%xmm0 \n" + "movups %%xmm0, (%[buf]) \n" + : + : [buf]"r"(buf), [other]"r"(other.buf) + : "%xmm0", "%xmm1", "memory" + ); #else - // TODO: implement it better - for (int i = 0; i < 16; i++) - buf[i] ^= other.buf[i]; + // TODO: implement it better + for (int i = 0; i < 16; i++) + buf[i] ^= other.buf[i]; #endif - } - }; + } + }; - typedef i2p::data::Tag<32> AESKey; - - template - class AESAlignedBuffer // 16 bytes alignment - { - public: - - AESAlignedBuffer () - { - m_Buf = m_UnalignedBuffer; - uint8_t rem = ((size_t)m_Buf) & 0x0f; - if (rem) - m_Buf += (16 - rem); - } - - operator uint8_t * () { return m_Buf; }; - operator const uint8_t * () const { return m_Buf; }; + typedef i2p::data::Tag<32> AESKey; + + template + class AESAlignedBuffer // 16 bytes alignment + { + public: + + AESAlignedBuffer () + { + m_Buf = m_UnalignedBuffer; + uint8_t rem = ((size_t)m_Buf) & 0x0f; + if (rem) + m_Buf += (16 - rem); + } + + operator uint8_t * () { return m_Buf; }; + operator const uint8_t * () const { return m_Buf; }; - private: + private: - uint8_t m_UnalignedBuffer[sz + 15]; // up to 15 bytes alignment - uint8_t * m_Buf; - }; + uint8_t m_UnalignedBuffer[sz + 15]; // up to 15 bytes alignment + uint8_t * m_Buf; + }; #ifdef AESNI - class ECBCryptoAESNI - { - public: + class ECBCryptoAESNI + { + public: - uint8_t * GetKeySchedule () { return m_KeySchedule; }; + uint8_t * GetKeySchedule () { return m_KeySchedule; }; - protected: + protected: - void ExpandKey (const AESKey& key); - - private: + void ExpandKey (const AESKey& key); + + private: - AESAlignedBuffer<240> m_KeySchedule; // 14 rounds for AES-256, 240 bytes - }; + AESAlignedBuffer<240> m_KeySchedule; // 14 rounds for AES-256, 240 bytes + }; - class ECBEncryptionAESNI: public ECBCryptoAESNI - { - public: - - void SetKey (const AESKey& key) { ExpandKey (key); }; - void Encrypt (const ChipherBlock * in, ChipherBlock * out); - }; + class ECBEncryptionAESNI: public ECBCryptoAESNI + { + public: + + void SetKey (const AESKey& key) { ExpandKey (key); }; + void Encrypt (const ChipherBlock * in, ChipherBlock * out); + }; - class ECBDecryptionAESNI: public ECBCryptoAESNI - { - public: - - void SetKey (const AESKey& key); - void Decrypt (const ChipherBlock * in, ChipherBlock * out); - }; + class ECBDecryptionAESNI: public ECBCryptoAESNI + { + public: + + void SetKey (const AESKey& key); + void Decrypt (const ChipherBlock * in, ChipherBlock * out); + }; - typedef ECBEncryptionAESNI ECBEncryption; - typedef ECBDecryptionAESNI ECBDecryption; + typedef ECBEncryptionAESNI ECBEncryption; + typedef ECBDecryptionAESNI ECBDecryption; #else // use crypto++ - class ECBEncryption - { - public: - - void SetKey (const AESKey& key) - { - m_Encryption.SetKey (key, 32); - } - void Encrypt (const ChipherBlock * in, ChipherBlock * out) - { - m_Encryption.ProcessData (out->buf, in->buf, 16); - } + class ECBEncryption + { + public: + + void SetKey (const AESKey& key) + { + m_Encryption.SetKey (key, 32); + } + void Encrypt (const ChipherBlock * in, ChipherBlock * out) + { + m_Encryption.ProcessData (out->buf, in->buf, 16); + } - private: + private: - CryptoPP::ECB_Mode::Encryption m_Encryption; - }; + CryptoPP::ECB_Mode::Encryption m_Encryption; + }; - class ECBDecryption - { - public: - - void SetKey (const AESKey& key) - { - m_Decryption.SetKey (key, 32); - } - void Decrypt (const ChipherBlock * in, ChipherBlock * out) - { - m_Decryption.ProcessData (out->buf, in->buf, 16); - } + class ECBDecryption + { + public: + + void SetKey (const AESKey& key) + { + m_Decryption.SetKey (key, 32); + } + void Decrypt (const ChipherBlock * in, ChipherBlock * out) + { + m_Decryption.ProcessData (out->buf, in->buf, 16); + } - private: + private: - CryptoPP::ECB_Mode::Decryption m_Decryption; - }; + CryptoPP::ECB_Mode::Decryption m_Decryption; + }; -#endif +#endif - class CBCEncryption - { - public: - - CBCEncryption () { memset (m_LastBlock.buf, 0, 16); }; - CBCEncryption(const AESKey& key, const uint8_t* iv) + class CBCEncryption + { + public: + + CBCEncryption () { memset (m_LastBlock.buf, 0, 16); }; + CBCEncryption(const AESKey& key, const uint8_t* iv) : CBCEncryption() { SetKey(key); SetIV(iv); }; - void SetKey (const AESKey& key) { m_ECBEncryption.SetKey (key); }; // 32 bytes - void SetIV (const uint8_t * iv) { memcpy (m_LastBlock.buf, iv, 16); }; // 16 bytes + void SetKey (const AESKey& key) { m_ECBEncryption.SetKey (key); }; // 32 bytes + void SetIV (const uint8_t * iv) { memcpy (m_LastBlock.buf, iv, 16); }; // 16 bytes - void Encrypt (int numBlocks, const ChipherBlock * in, ChipherBlock * out); - void Encrypt (const uint8_t * in, std::size_t len, uint8_t * out); - void Encrypt (const uint8_t * in, uint8_t * out); // one block + void Encrypt (int numBlocks, const ChipherBlock * in, ChipherBlock * out); + void Encrypt (const uint8_t * in, std::size_t len, uint8_t * out); + void Encrypt (const uint8_t * in, uint8_t * out); // one block - private: + private: - ChipherBlock m_LastBlock; - - ECBEncryption m_ECBEncryption; - }; + ChipherBlock m_LastBlock; + + ECBEncryption m_ECBEncryption; + }; - class CBCDecryption - { - public: - - CBCDecryption () { memset (m_IV.buf, 0, 16); }; + class CBCDecryption + { + public: + + CBCDecryption () { memset (m_IV.buf, 0, 16); }; - void SetKey (const AESKey& key) { m_ECBDecryption.SetKey (key); }; // 32 bytes - void SetIV (const uint8_t * iv) { memcpy (m_IV.buf, iv, 16); }; // 16 bytes + void SetKey (const AESKey& key) { m_ECBDecryption.SetKey (key); }; // 32 bytes + void SetIV (const uint8_t * iv) { memcpy (m_IV.buf, iv, 16); }; // 16 bytes - void Decrypt (int numBlocks, const ChipherBlock * in, ChipherBlock * out); - void Decrypt (const uint8_t * in, std::size_t len, uint8_t * out); - void Decrypt (const uint8_t * in, uint8_t * out); // one block + void Decrypt (int numBlocks, const ChipherBlock * in, ChipherBlock * out); + void Decrypt (const uint8_t * in, std::size_t len, uint8_t * out); + void Decrypt (const uint8_t * in, uint8_t * out); // one block - private: + private: - ChipherBlock m_IV; - ECBDecryption m_ECBDecryption; - }; + ChipherBlock m_IV; + ECBDecryption m_ECBDecryption; + }; - class TunnelEncryption // with double IV encryption - { - public: + class TunnelEncryption // with double IV encryption + { + public: - void SetKeys (const AESKey& layerKey, const AESKey& ivKey) - { - m_LayerEncryption.SetKey (layerKey); - m_IVEncryption.SetKey (ivKey); - } + void SetKeys (const AESKey& layerKey, const AESKey& ivKey) + { + m_LayerEncryption.SetKey (layerKey); + m_IVEncryption.SetKey (ivKey); + } - void Encrypt (const uint8_t * in, uint8_t * out); // 1024 bytes (16 IV + 1008 data) + void Encrypt (const uint8_t * in, uint8_t * out); // 1024 bytes (16 IV + 1008 data) - private: + private: - ECBEncryption m_IVEncryption; + ECBEncryption m_IVEncryption; #ifdef AESNI - ECBEncryption m_LayerEncryption; + ECBEncryption m_LayerEncryption; #else - CBCEncryption m_LayerEncryption; + CBCEncryption m_LayerEncryption; #endif - }; + }; - class TunnelDecryption // with double IV encryption - { - public: + class TunnelDecryption // with double IV encryption + { + public: - void SetKeys (const AESKey& layerKey, const AESKey& ivKey) - { - m_LayerDecryption.SetKey (layerKey); - m_IVDecryption.SetKey (ivKey); - } + void SetKeys (const AESKey& layerKey, const AESKey& ivKey) + { + m_LayerDecryption.SetKey (layerKey); + m_IVDecryption.SetKey (ivKey); + } - void Decrypt (const uint8_t * in, uint8_t * out); // 1024 bytes (16 IV + 1008 data) + void Decrypt (const uint8_t * in, uint8_t * out); // 1024 bytes (16 IV + 1008 data) - private: + private: - ECBDecryption m_IVDecryption; + ECBDecryption m_IVDecryption; #ifdef AESNI - ECBDecryption m_LayerDecryption; + ECBDecryption m_LayerDecryption; #else - CBCDecryption m_LayerDecryption; + CBCDecryption m_LayerDecryption; #endif - }; + }; } } diff --git a/api.cpp b/api.cpp index 6f3c8c4c..e5258195 100644 --- a/api.cpp +++ b/api.cpp @@ -14,99 +14,99 @@ namespace i2p { namespace api { - void InitI2P (int argc, char* argv[], const char * appName) - { - i2p::util::filesystem::SetAppName (appName); - i2p::util::config::OptionParser(argc, argv); - i2p::context.Init (); - } + void InitI2P (int argc, char* argv[], const char * appName) + { + i2p::util::filesystem::SetAppName (appName); + i2p::util::config::OptionParser(argc, argv); + i2p::context.Init (); + } - void StartI2P (std::ostream * logStream) - { - if (logStream) - StartLog (logStream); - else - StartLog (i2p::util::filesystem::GetAppName () + ".log"); - i2p::data::netdb.Start(); - LogPrint("NetDB started"); - i2p::transport::transports.Start(); - LogPrint("Transports started"); - i2p::tunnel::tunnels.Start(); - LogPrint("Tunnels started"); - } + void StartI2P (std::ostream * logStream) + { + if (logStream) + StartLog (logStream); + else + StartLog (i2p::util::filesystem::GetAppName () + ".log"); + i2p::data::netdb.Start(); + LogPrint("NetDB started"); + i2p::transport::transports.Start(); + LogPrint("Transports started"); + i2p::tunnel::tunnels.Start(); + LogPrint("Tunnels started"); + } - void StopI2P () - { - LogPrint("Shutdown started."); - i2p::tunnel::tunnels.Stop(); - LogPrint("Tunnels stopped"); - i2p::transport::transports.Stop(); - LogPrint("Transports stopped"); - i2p::data::netdb.Stop(); - LogPrint("NetDB stopped"); - StopLog (); - } + void StopI2P () + { + LogPrint("Shutdown started."); + i2p::tunnel::tunnels.Stop(); + LogPrint("Tunnels stopped"); + i2p::transport::transports.Stop(); + LogPrint("Transports stopped"); + i2p::data::netdb.Stop(); + LogPrint("NetDB stopped"); + StopLog (); + } - i2p::client::ClientDestination * CreateLocalDestination (const i2p::data::PrivateKeys& keys, bool isPublic, - const std::map * params) - { - auto localDestination = new i2p::client::ClientDestination (keys, isPublic, params); - localDestination->Start (); - return localDestination; - } + i2p::client::ClientDestination * CreateLocalDestination (const i2p::data::PrivateKeys& keys, bool isPublic, + const std::map * params) + { + auto localDestination = new i2p::client::ClientDestination (keys, isPublic, params); + localDestination->Start (); + return localDestination; + } - i2p::client::ClientDestination * CreateLocalDestination (bool isPublic, i2p::data::SigningKeyType sigType, - const std::map * params) - { - i2p::data::PrivateKeys keys = i2p::data::PrivateKeys::CreateRandomKeys (sigType); - auto localDestination = new i2p::client::ClientDestination (keys, isPublic, params); - localDestination->Start (); - return localDestination; - } + i2p::client::ClientDestination * CreateLocalDestination (bool isPublic, i2p::data::SigningKeyType sigType, + const std::map * params) + { + i2p::data::PrivateKeys keys = i2p::data::PrivateKeys::CreateRandomKeys (sigType); + auto localDestination = new i2p::client::ClientDestination (keys, isPublic, params); + localDestination->Start (); + return localDestination; + } - void DestroyLocalDestination (i2p::client::ClientDestination * dest) - { - if (dest) - { - dest->Stop (); - delete dest; - } - } + void DestroyLocalDestination (i2p::client::ClientDestination * dest) + { + if (dest) + { + dest->Stop (); + delete dest; + } + } - void RequestLeaseSet (i2p::client::ClientDestination * dest, const i2p::data::IdentHash& remote) - { - if (dest) - dest->RequestDestination (remote); - } + void RequestLeaseSet (i2p::client::ClientDestination * dest, const i2p::data::IdentHash& remote) + { + if (dest) + dest->RequestDestination (remote); + } - std::shared_ptr CreateStream (i2p::client::ClientDestination * dest, const i2p::data::IdentHash& remote) - { - if (!dest) return nullptr; - auto leaseSet = dest->FindLeaseSet (remote); - if (leaseSet) - { - auto stream = dest->CreateStream (leaseSet); - stream->Send (nullptr, 0); // connect - return stream; - } - else - { - RequestLeaseSet (dest, remote); - return nullptr; - } - } + std::shared_ptr CreateStream (i2p::client::ClientDestination * dest, const i2p::data::IdentHash& remote) + { + if (!dest) return nullptr; + auto leaseSet = dest->FindLeaseSet (remote); + if (leaseSet) + { + auto stream = dest->CreateStream (leaseSet); + stream->Send (nullptr, 0); // connect + return stream; + } + else + { + RequestLeaseSet (dest, remote); + return nullptr; + } + } - void AcceptStream (i2p::client::ClientDestination * dest, const i2p::stream::StreamingDestination::Acceptor& acceptor) - { - if (dest) - dest->AcceptStreams (acceptor); - } + void AcceptStream (i2p::client::ClientDestination * dest, const i2p::stream::StreamingDestination::Acceptor& acceptor) + { + if (dest) + dest->AcceptStreams (acceptor); + } - void DestroyStream (std::shared_ptr stream) - { - if (stream) - stream->Close (); - } + void DestroyStream (std::shared_ptr stream) + { + if (stream) + stream->Close (); + } } } diff --git a/api.h b/api.h index 894aff49..3fcbcb2f 100644 --- a/api.h +++ b/api.h @@ -11,24 +11,24 @@ namespace i2p { namespace api { - // initialization start and stop - void InitI2P (int argc, char* argv[], const char * appName); - void StartI2P (std::ostream * logStream = nullptr); - // write system log to logStream, if not specified to .log in application's folder - void StopI2P (); + // initialization start and stop + void InitI2P (int argc, char* argv[], const char * appName); + void StartI2P (std::ostream * logStream = nullptr); + // write system log to logStream, if not specified to .log in application's folder + void StopI2P (); - // destinations - i2p::client::ClientDestination * CreateLocalDestination (const i2p::data::PrivateKeys& keys, bool isPublic = true, - const std::map * params = nullptr); - i2p::client::ClientDestination * CreateLocalDestination (bool isPublic = false, i2p::data::SigningKeyType sigType = i2p::data::SIGNING_KEY_TYPE_ECDSA_SHA256_P256, - const std::map * params = nullptr); // transient destinations usually not published - void DestroyLocalDestination (i2p::client::ClientDestination * dest); + // destinations + i2p::client::ClientDestination * CreateLocalDestination (const i2p::data::PrivateKeys& keys, bool isPublic = true, + const std::map * params = nullptr); + i2p::client::ClientDestination * CreateLocalDestination (bool isPublic = false, i2p::data::SigningKeyType sigType = i2p::data::SIGNING_KEY_TYPE_ECDSA_SHA256_P256, + const std::map * params = nullptr); // transient destinations usually not published + void DestroyLocalDestination (i2p::client::ClientDestination * dest); - // streams - void RequestLeaseSet (i2p::client::ClientDestination * dest, const i2p::data::IdentHash& remote); - std::shared_ptr CreateStream (i2p::client::ClientDestination * dest, const i2p::data::IdentHash& remote); - void AcceptStream (i2p::client::ClientDestination * dest, const i2p::stream::StreamingDestination::Acceptor& acceptor); - void DestroyStream (std::shared_ptr stream); + // streams + void RequestLeaseSet (i2p::client::ClientDestination * dest, const i2p::data::IdentHash& remote); + std::shared_ptr CreateStream (i2p::client::ClientDestination * dest, const i2p::data::IdentHash& remote); + void AcceptStream (i2p::client::ClientDestination * dest, const i2p::stream::StreamingDestination::Acceptor& acceptor); + void DestroyStream (std::shared_ptr stream); } } diff --git a/base64.cpp b/base64.cpp index 3d1ec07e..f5eac89b 100644 --- a/base64.cpp +++ b/base64.cpp @@ -6,264 +6,264 @@ namespace i2p namespace data { - static void iT64Build(void); + static void iT64Build(void); - /* - * - * BASE64 Substitution Table - * ------------------------- - * - * Direct Substitution Table - */ + /* + * + * BASE64 Substitution Table + * ------------------------- + * + * Direct Substitution Table + */ - static char T64[64] = { - 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', - 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', - 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', - 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', - 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', - 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', - 'w', 'x', 'y', 'z', '0', '1', '2', '3', - '4', '5', '6', '7', '8', '9', '-', '~' - }; + static char T64[64] = { + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', + 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', + 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', + 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', + 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', + 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', + 'w', 'x', 'y', 'z', '0', '1', '2', '3', + '4', '5', '6', '7', '8', '9', '-', '~' + }; - const char * GetBase64SubstitutionTable () - { - return T64; - } - - /* - * Reverse Substitution Table (built in run time) - */ + const char * GetBase64SubstitutionTable () + { + return T64; + } + + /* + * Reverse Substitution Table (built in run time) + */ - static char iT64[256]; - static int isFirstTime = 1; + static char iT64[256]; + static int isFirstTime = 1; - /* - * Padding - */ + /* + * Padding + */ - static char P64 = '='; + static char P64 = '='; - /* - * - * ByteStreamToBase64 - * ------------------ - * - * Converts binary encoded data to BASE64 format. - * - */ + /* + * + * ByteStreamToBase64 + * ------------------ + * + * Converts binary encoded data to BASE64 format. + * + */ - size_t /* Number of bytes in the encoded buffer */ - ByteStreamToBase64 ( - const uint8_t * InBuffer, /* Input buffer, binary data */ - size_t InCount, /* Number of bytes in the input buffer */ - char * OutBuffer, /* output buffer */ - size_t len /* length of output buffer */ - ) + size_t /* Number of bytes in the encoded buffer */ + ByteStreamToBase64 ( + const uint8_t * InBuffer, /* Input buffer, binary data */ + size_t InCount, /* Number of bytes in the input buffer */ + char * OutBuffer, /* output buffer */ + size_t len /* length of output buffer */ + ) - { - unsigned char * ps; - unsigned char * pd; - unsigned char acc_1; - unsigned char acc_2; - int i; - int n; - int m; - size_t outCount; + { + unsigned char * ps; + unsigned char * pd; + unsigned char acc_1; + unsigned char acc_2; + int i; + int n; + int m; + size_t outCount; - ps = (unsigned char *)InBuffer; - n = InCount/3; - m = InCount%3; - if (!m) - outCount = 4*n; - else - outCount = 4*(n+1); - if (outCount > len) return 0; - pd = (unsigned char *)OutBuffer; - for ( i = 0; i>= 2; /* base64 digit #1 */ - *pd++ = T64[acc_1]; - acc_1 = *ps++; - acc_2 |= acc_1 >> 4; /* base64 digit #2 */ - *pd++ = T64[acc_2]; - acc_1 &= 0x0f; - acc_1 <<=2; - acc_2 = *ps++; - acc_1 |= acc_2>>6; /* base64 digit #3 */ - *pd++ = T64[acc_1]; - acc_2 &= 0x3f; /* base64 digit #4 */ - *pd++ = T64[acc_2]; - } - if ( m == 1 ){ - acc_1 = *ps++; - acc_2 = (acc_1<<4)&0x3f; /* base64 digit #2 */ - acc_1 >>= 2; /* base64 digit #1 */ - *pd++ = T64[acc_1]; - *pd++ = T64[acc_2]; - *pd++ = P64; - *pd++ = P64; + ps = (unsigned char *)InBuffer; + n = InCount/3; + m = InCount%3; + if (!m) + outCount = 4*n; + else + outCount = 4*(n+1); + if (outCount > len) return 0; + pd = (unsigned char *)OutBuffer; + for ( i = 0; i>= 2; /* base64 digit #1 */ + *pd++ = T64[acc_1]; + acc_1 = *ps++; + acc_2 |= acc_1 >> 4; /* base64 digit #2 */ + *pd++ = T64[acc_2]; + acc_1 &= 0x0f; + acc_1 <<=2; + acc_2 = *ps++; + acc_1 |= acc_2>>6; /* base64 digit #3 */ + *pd++ = T64[acc_1]; + acc_2 &= 0x3f; /* base64 digit #4 */ + *pd++ = T64[acc_2]; + } + if ( m == 1 ){ + acc_1 = *ps++; + acc_2 = (acc_1<<4)&0x3f; /* base64 digit #2 */ + acc_1 >>= 2; /* base64 digit #1 */ + *pd++ = T64[acc_1]; + *pd++ = T64[acc_2]; + *pd++ = P64; + *pd++ = P64; - } - else if ( m == 2 ){ - acc_1 = *ps++; - acc_2 = (acc_1<<4)&0x3f; - acc_1 >>= 2; /* base64 digit #1 */ - *pd++ = T64[acc_1]; - acc_1 = *ps++; - acc_2 |= acc_1 >> 4; /* base64 digit #2 */ - *pd++ = T64[acc_2]; - acc_1 &= 0x0f; - acc_1 <<=2; /* base64 digit #3 */ - *pd++ = T64[acc_1]; - *pd++ = P64; - } - - return outCount; - } + } + else if ( m == 2 ){ + acc_1 = *ps++; + acc_2 = (acc_1<<4)&0x3f; + acc_1 >>= 2; /* base64 digit #1 */ + *pd++ = T64[acc_1]; + acc_1 = *ps++; + acc_2 |= acc_1 >> 4; /* base64 digit #2 */ + *pd++ = T64[acc_2]; + acc_1 &= 0x0f; + acc_1 <<=2; /* base64 digit #3 */ + *pd++ = T64[acc_1]; + *pd++ = P64; + } + + return outCount; + } - /* - * - * Base64ToByteStream - * ------------------ - * - * Converts BASE64 encoded data to binary format. If input buffer is - * not properly padded, buffer of negative length is returned - * - */ + /* + * + * Base64ToByteStream + * ------------------ + * + * Converts BASE64 encoded data to binary format. If input buffer is + * not properly padded, buffer of negative length is returned + * + */ - size_t /* Number of output bytes */ - Base64ToByteStream ( - const char * InBuffer, /* BASE64 encoded buffer */ - size_t InCount, /* Number of input bytes */ - uint8_t * OutBuffer, /* output buffer length */ - size_t len /* length of output buffer */ - ) - { - unsigned char * ps; - unsigned char * pd; - unsigned char acc_1; - unsigned char acc_2; - int i; - int n; - int m; - size_t outCount; + size_t /* Number of output bytes */ + Base64ToByteStream ( + const char * InBuffer, /* BASE64 encoded buffer */ + size_t InCount, /* Number of input bytes */ + uint8_t * OutBuffer, /* output buffer length */ + size_t len /* length of output buffer */ + ) + { + unsigned char * ps; + unsigned char * pd; + unsigned char acc_1; + unsigned char acc_2; + int i; + int n; + int m; + size_t outCount; - if (isFirstTime) iT64Build(); - n = InCount/4; - m = InCount%4; - if (!m) - outCount = 3*n; - else { - outCount = 0; - return 0; - } - - ps = (unsigned char *)(InBuffer + InCount - 1); - while ( *ps-- == P64 ) outCount--; - ps = (unsigned char *)InBuffer; - - if (outCount > len) return -1; - pd = OutBuffer; - auto endOfOutBuffer = OutBuffer + outCount; - for ( i = 0; i < n; i++ ){ - acc_1 = iT64[*ps++]; - acc_2 = iT64[*ps++]; - acc_1 <<= 2; - acc_1 |= acc_2>>4; - *pd++ = acc_1; - if (pd >= endOfOutBuffer) break; + if (isFirstTime) iT64Build(); + n = InCount/4; + m = InCount%4; + if (!m) + outCount = 3*n; + else { + outCount = 0; + return 0; + } + + ps = (unsigned char *)(InBuffer + InCount - 1); + while ( *ps-- == P64 ) outCount--; + ps = (unsigned char *)InBuffer; + + if (outCount > len) return -1; + pd = OutBuffer; + auto endOfOutBuffer = OutBuffer + outCount; + for ( i = 0; i < n; i++ ){ + acc_1 = iT64[*ps++]; + acc_2 = iT64[*ps++]; + acc_1 <<= 2; + acc_1 |= acc_2>>4; + *pd++ = acc_1; + if (pd >= endOfOutBuffer) break; - acc_2 <<= 4; - acc_1 = iT64[*ps++]; - acc_2 |= acc_1 >> 2; - *pd++ = acc_2; - if (pd >= endOfOutBuffer) break; + acc_2 <<= 4; + acc_1 = iT64[*ps++]; + acc_2 |= acc_1 >> 2; + *pd++ = acc_2; + if (pd >= endOfOutBuffer) break; - acc_2 = iT64[*ps++]; - acc_2 |= acc_1 << 6; - *pd++ = acc_2; - } + acc_2 = iT64[*ps++]; + acc_2 |= acc_1 << 6; + *pd++ = acc_2; + } - return outCount; - } + return outCount; + } - /* - * - * iT64 - * ---- - * Reverse table builder. P64 character is replaced with 0 - * - * - */ + /* + * + * iT64 + * ---- + * Reverse table builder. P64 character is replaced with 0 + * + * + */ - static void iT64Build() - { - int i; - isFirstTime = 0; - for ( i=0; i<256; i++ ) iT64[i] = -1; - for ( i=0; i<64; i++ ) iT64[(int)T64[i]] = i; - iT64[(int)P64] = 0; - } + static void iT64Build() + { + int i; + isFirstTime = 0; + for ( i=0; i<256; i++ ) iT64[i] = -1; + for ( i=0; i<64; i++ ) iT64[(int)T64[i]] = i; + iT64[(int)P64] = 0; + } - size_t Base32ToByteStream (const char * inBuf, size_t len, uint8_t * outBuf, size_t outLen) - { - int tmp = 0, bits = 0; - size_t ret = 0; - for (size_t i = 0; i < len; i++) - { - char ch = inBuf[i]; - if (ch >= '2' && ch <= '7') // digit - ch = (ch - '2') + 26; // 26 means a-z - else if (ch >= 'a' && ch <= 'z') - ch = ch - 'a'; // a = 0 - else - return 0; // unexpected character - - tmp |= ch; - bits += 5; - if (bits >= 8) - { - if (ret >= outLen) return ret; - outBuf[ret] = tmp >> (bits - 8); - bits -= 8; - ret++; - } - tmp <<= 5; - } - return ret; - } + size_t Base32ToByteStream (const char * inBuf, size_t len, uint8_t * outBuf, size_t outLen) + { + int tmp = 0, bits = 0; + size_t ret = 0; + for (size_t i = 0; i < len; i++) + { + char ch = inBuf[i]; + if (ch >= '2' && ch <= '7') // digit + ch = (ch - '2') + 26; // 26 means a-z + else if (ch >= 'a' && ch <= 'z') + ch = ch - 'a'; // a = 0 + else + return 0; // unexpected character + + tmp |= ch; + bits += 5; + if (bits >= 8) + { + if (ret >= outLen) return ret; + outBuf[ret] = tmp >> (bits - 8); + bits -= 8; + ret++; + } + tmp <<= 5; + } + return ret; + } - size_t ByteStreamToBase32 (const uint8_t * inBuf, size_t len, char * outBuf, size_t outLen) - { - size_t ret = 0, pos = 1; - int bits = 8, tmp = inBuf[0]; - while (ret < outLen && (bits > 0 || pos < len)) - { - if (bits < 5) - { - if (pos < len) - { - tmp <<= 8; - tmp |= inBuf[pos] & 0xFF; - pos++; - bits += 8; - } - else // last byte - { - tmp <<= (5 - bits); - bits = 5; - } - } - - bits -= 5; - int ind = (tmp >> bits) & 0x1F; - outBuf[ret] = (ind < 26) ? (ind + 'a') : ((ind - 26) + '2'); - ret++; - } - return ret; - } + size_t ByteStreamToBase32 (const uint8_t * inBuf, size_t len, char * outBuf, size_t outLen) + { + size_t ret = 0, pos = 1; + int bits = 8, tmp = inBuf[0]; + while (ret < outLen && (bits > 0 || pos < len)) + { + if (bits < 5) + { + if (pos < len) + { + tmp <<= 8; + tmp |= inBuf[pos] & 0xFF; + pos++; + bits += 8; + } + else // last byte + { + tmp <<= (5 - bits); + bits = 5; + } + } + + bits -= 5; + int ind = (tmp >> bits) & 0x1F; + outBuf[ret] = (ind < 26) ? (ind + 'a') : ((ind - 26) + '2'); + ret++; + } + return ret; + } } } diff --git a/base64.h b/base64.h index c0ce1495..027d62ba 100644 --- a/base64.h +++ b/base64.h @@ -9,12 +9,12 @@ namespace i2p namespace data { - size_t ByteStreamToBase64 (const uint8_t * InBuffer, size_t InCount, char * OutBuffer, size_t len); - size_t Base64ToByteStream (const char * InBuffer, size_t InCount, uint8_t * OutBuffer, size_t len ); - const char * GetBase64SubstitutionTable (); - - size_t Base32ToByteStream (const char * inBuf, size_t len, uint8_t * outBuf, size_t outLen); - size_t ByteStreamToBase32 (const uint8_t * InBuf, size_t len, char * outBuf, size_t outLen); + size_t ByteStreamToBase64 (const uint8_t * InBuffer, size_t InCount, char * OutBuffer, size_t len); + size_t Base64ToByteStream (const char * InBuffer, size_t InCount, uint8_t * OutBuffer, size_t len ); + const char * GetBase64SubstitutionTable (); + + size_t Base32ToByteStream (const char * inBuf, size_t len, uint8_t * outBuf, size_t outLen); + size_t ByteStreamToBase32 (const uint8_t * InBuf, size_t len, char * outBuf, size_t outLen); } } diff --git a/hmac.h b/hmac.h index 0b76ceee..490691b2 100644 --- a/hmac.h +++ b/hmac.h @@ -11,51 +11,50 @@ namespace i2p { namespace crypto { - const uint64_t IPAD = 0x3636363636363636; - const uint64_t OPAD = 0x5C5C5C5C5C5C5C5C; + const uint64_t IPAD = 0x3636363636363636; + const uint64_t OPAD = 0x5C5C5C5C5C5C5C5C; - typedef i2p::data::Tag<32> MACKey; - - inline void HMACMD5Digest (uint8_t * msg, size_t len, const MACKey& key, uint8_t * digest) - // key is 32 bytes - // digest is 16 bytes - // block size is 64 bytes - { - uint64_t buf[256]; - // ikeypad - buf[0] = key.GetLL ()[0] ^ IPAD; - buf[1] = key.GetLL ()[1] ^ IPAD; - buf[2] = key.GetLL ()[2] ^ IPAD; - buf[3] = key.GetLL ()[3] ^ IPAD; - buf[4] = IPAD; - buf[5] = IPAD; - buf[6] = IPAD; - buf[7] = IPAD; - // concatenate with msg - memcpy (buf + 8, msg, len); - // calculate first hash - uint8_t hash[16]; // MD5 - CryptoPP::Weak1::MD5().CalculateDigest (hash, (uint8_t *)buf, len + 64); - - // okeypad - buf[0] = key.GetLL ()[0] ^ OPAD; - buf[1] = key.GetLL ()[1] ^ OPAD; - buf[2] = key.GetLL ()[2] ^ OPAD; - buf[3] = key.GetLL ()[3] ^ OPAD; - buf[4] = OPAD; - buf[5] = OPAD; - buf[6] = OPAD; - buf[7] = OPAD; - // copy first hash after okeypad - memcpy (buf + 8, hash, 16); - // fill next 16 bytes with zeros (first hash size assumed 32 bytes in I2P) - memset (buf + 10, 0, 16); - - // calculate digest - CryptoPP::Weak1::MD5().CalculateDigest (digest, (uint8_t *)buf, 96); - } + typedef i2p::data::Tag<32> MACKey; + + inline void HMACMD5Digest (uint8_t * msg, size_t len, const MACKey& key, uint8_t * digest) + // key is 32 bytes + // digest is 16 bytes + // block size is 64 bytes + { + uint64_t buf[256]; + // ikeypad + buf[0] = key.GetLL ()[0] ^ IPAD; + buf[1] = key.GetLL ()[1] ^ IPAD; + buf[2] = key.GetLL ()[2] ^ IPAD; + buf[3] = key.GetLL ()[3] ^ IPAD; + buf[4] = IPAD; + buf[5] = IPAD; + buf[6] = IPAD; + buf[7] = IPAD; + // concatenate with msg + memcpy (buf + 8, msg, len); + // calculate first hash + uint8_t hash[16]; // MD5 + CryptoPP::Weak1::MD5().CalculateDigest (hash, (uint8_t *)buf, len + 64); + + // okeypad + buf[0] = key.GetLL ()[0] ^ OPAD; + buf[1] = key.GetLL ()[1] ^ OPAD; + buf[2] = key.GetLL ()[2] ^ OPAD; + buf[3] = key.GetLL ()[3] ^ OPAD; + buf[4] = OPAD; + buf[5] = OPAD; + buf[6] = OPAD; + buf[7] = OPAD; + // copy first hash after okeypad + memcpy (buf + 8, hash, 16); + // fill next 16 bytes with zeros (first hash size assumed 32 bytes in I2P) + memset (buf + 10, 0, 16); + + // calculate digest + CryptoPP::Weak1::MD5().CalculateDigest (digest, (uint8_t *)buf, 96); + } } } #endif - diff --git a/i2p.cpp b/i2p.cpp index 25fe29b0..fa277b38 100644 --- a/i2p.cpp +++ b/i2p.cpp @@ -5,15 +5,15 @@ int main( int argc, char* argv[] ) { - Daemon.init(argc, argv); - if (Daemon.start()) - { - while (Daemon.running) - { - //TODO Meeh: Find something better to do here. - std::this_thread::sleep_for (std::chrono::seconds(1)); - } - } - Daemon.stop(); - return EXIT_SUCCESS; + Daemon.init(argc, argv); + if (Daemon.start()) + { + while (Daemon.running) + { + //TODO Meeh: Find something better to do here. + std::this_thread::sleep_for (std::chrono::seconds(1)); + } + } + Daemon.stop(); + return EXIT_SUCCESS; } diff --git a/util.cpp b/util.cpp index 3215f090..db96d964 100644 --- a/util.cpp +++ b/util.cpp @@ -36,27 +36,27 @@ int inet_pton(int af, const char *src, void *dst) { /* This function was written by Petar Korponai?. See http://stackoverflow.com/questions/15660203/inet-pton-identifier-not-found */ - struct sockaddr_storage ss; - int size = sizeof (ss); - char src_copy[INET6_ADDRSTRLEN + 1]; + struct sockaddr_storage ss; + int size = sizeof (ss); + char src_copy[INET6_ADDRSTRLEN + 1]; - ZeroMemory (&ss, sizeof (ss)); - strncpy_s (src_copy, src, INET6_ADDRSTRLEN + 1); - src_copy[INET6_ADDRSTRLEN] = 0; + ZeroMemory (&ss, sizeof (ss)); + strncpy_s (src_copy, src, INET6_ADDRSTRLEN + 1); + src_copy[INET6_ADDRSTRLEN] = 0; - if (WSAStringToAddress (src_copy, af, NULL, (struct sockaddr *)&ss, &size) == 0) - { - switch (af) - { - case AF_INET: - *(struct in_addr *)dst = ((struct sockaddr_in *)&ss)->sin_addr; - return 1; - case AF_INET6: - *(struct in6_addr *)dst = ((struct sockaddr_in6 *)&ss)->sin6_addr; - return 1; - } - } - return 0; + if (WSAStringToAddress (src_copy, af, NULL, (struct sockaddr *)&ss, &size) == 0) + { + switch (af) + { + case AF_INET: + *(struct in_addr *)dst = ((struct sockaddr_in *)&ss)->sin_addr; + return 1; + case AF_INET6: + *(struct in6_addr *)dst = ((struct sockaddr_in6 *)&ss)->sin6_addr; + return 1; + } + } + return 0; } #endif @@ -67,631 +67,631 @@ namespace util namespace config { - std::map mapArgs; - std::map > mapMultiArgs; + std::map mapArgs; + std::map > mapMultiArgs; - void OptionParser(int argc, const char* const argv[]) - { - mapArgs.clear(); - mapMultiArgs.clear(); - for (int i = 1; i < argc; i++) - { - std::string strKey (argv[i]); - std::string strValue; - size_t has_data = strKey.find('='); - if (has_data != std::string::npos) - { - strValue = strKey.substr(has_data+1); - strKey = strKey.substr(0, has_data); - } + void OptionParser(int argc, const char* const argv[]) + { + mapArgs.clear(); + mapMultiArgs.clear(); + for (int i = 1; i < argc; i++) + { + std::string strKey (argv[i]); + std::string strValue; + size_t has_data = strKey.find('='); + if (has_data != std::string::npos) + { + strValue = strKey.substr(has_data+1); + strKey = strKey.substr(0, has_data); + } #ifdef WIN32 - boost::to_lower(strKey); - if (boost::algorithm::starts_with(strKey, "/")) - strKey = "-" + strKey.substr(1); + boost::to_lower(strKey); + if (boost::algorithm::starts_with(strKey, "/")) + strKey = "-" + strKey.substr(1); #endif - if (strKey[0] != '-') - break; + if (strKey[0] != '-') + break; - mapArgs[strKey] = strValue; - mapMultiArgs[strKey].push_back(strValue); - } + mapArgs[strKey] = strValue; + mapMultiArgs[strKey].push_back(strValue); + } - BOOST_FOREACH(PAIRTYPE(const std::string,std::string)& entry, mapArgs) - { - std::string name = entry.first; + BOOST_FOREACH(PAIRTYPE(const std::string,std::string)& entry, mapArgs) + { + std::string name = entry.first; - // interpret --foo as -foo (as long as both are not set) - if (name.find("--") == 0) - { - std::string singleDash(name.begin()+1, name.end()); - if (mapArgs.count(singleDash) == 0) - mapArgs[singleDash] = entry.second; - name = singleDash; - } - } - } + // interpret --foo as -foo (as long as both are not set) + if (name.find("--") == 0) + { + std::string singleDash(name.begin()+1, name.end()); + if (mapArgs.count(singleDash) == 0) + mapArgs[singleDash] = entry.second; + name = singleDash; + } + } + } - const char* GetCharArg(const std::string& strArg, const std::string& nDefault) - { - if (mapArgs.count(strArg)) - return mapArgs[strArg].c_str(); - return nDefault.c_str(); - } + const char* GetCharArg(const std::string& strArg, const std::string& nDefault) + { + if (mapArgs.count(strArg)) + return mapArgs[strArg].c_str(); + return nDefault.c_str(); + } - std::string GetArg(const std::string& strArg, const std::string& strDefault) - { - if (mapArgs.count(strArg)) - return mapArgs[strArg]; - return strDefault; - } + std::string GetArg(const std::string& strArg, const std::string& strDefault) + { + if (mapArgs.count(strArg)) + return mapArgs[strArg]; + return strDefault; + } - int GetArg(const std::string& strArg, int nDefault) - { - if (mapArgs.count(strArg)) - return atoi(mapArgs[strArg].c_str()); - return nDefault; - } + int GetArg(const std::string& strArg, int nDefault) + { + if (mapArgs.count(strArg)) + return atoi(mapArgs[strArg].c_str()); + return nDefault; + } } namespace filesystem { - std::string appName ("i2pd"); + std::string appName ("i2pd"); - void SetAppName (const std::string& name) - { - appName = name; - } + void SetAppName (const std::string& name) + { + appName = name; + } - std::string GetAppName () - { - return appName; - } + std::string GetAppName () + { + return appName; + } - const boost::filesystem::path &GetDataDir() - { - static boost::filesystem::path path; + const boost::filesystem::path &GetDataDir() + { + static boost::filesystem::path path; - // TODO: datadir parameter is useless because GetDataDir is called before OptionParser - // and mapArgs is not initialized yet - /*if (i2p::util::config::mapArgs.count("-datadir")) - path = boost::filesystem::system_complete(i2p::util::config::mapArgs["-datadir"]); - else */ - path = GetDefaultDataDir(); + // TODO: datadir parameter is useless because GetDataDir is called before OptionParser + // and mapArgs is not initialized yet + /*if (i2p::util::config::mapArgs.count("-datadir")) + path = boost::filesystem::system_complete(i2p::util::config::mapArgs["-datadir"]); + else */ + path = GetDefaultDataDir(); - if (!boost::filesystem::exists( path )) - { - // Create data directory - if (!boost::filesystem::create_directory( path )) - { - LogPrint("Failed to create data directory!"); - path = ""; - return path; - } - } - if (!boost::filesystem::is_directory(path)) - path = GetDefaultDataDir(); - return path; - } + if (!boost::filesystem::exists( path )) + { + // Create data directory + if (!boost::filesystem::create_directory( path )) + { + LogPrint("Failed to create data directory!"); + path = ""; + return path; + } + } + if (!boost::filesystem::is_directory(path)) + path = GetDefaultDataDir(); + return path; + } - std::string GetFullPath (const std::string& filename) - { - std::string fullPath = GetDataDir ().string (); + std::string GetFullPath (const std::string& filename) + { + std::string fullPath = GetDataDir ().string (); #ifndef _WIN32 - fullPath.append ("/"); + fullPath.append ("/"); #else - fullPath.append ("\\"); + fullPath.append ("\\"); #endif - fullPath.append (filename); - return fullPath; - } + fullPath.append (filename); + return fullPath; + } - boost::filesystem::path GetConfigFile() - { - boost::filesystem::path pathConfigFile(i2p::util::config::GetArg("-conf", "i2p.conf")); - if (!pathConfigFile.is_complete()) pathConfigFile = GetDataDir() / pathConfigFile; - return pathConfigFile; - } + boost::filesystem::path GetConfigFile() + { + boost::filesystem::path pathConfigFile(i2p::util::config::GetArg("-conf", "i2p.conf")); + if (!pathConfigFile.is_complete()) pathConfigFile = GetDataDir() / pathConfigFile; + return pathConfigFile; + } - void ReadConfigFile(std::map& mapSettingsRet, - std::map >& mapMultiSettingsRet) - { - boost::filesystem::ifstream streamConfig(GetConfigFile()); - if (!streamConfig.good()) - return; // No i2pd.conf file is OK + void ReadConfigFile(std::map& mapSettingsRet, + std::map >& mapMultiSettingsRet) + { + boost::filesystem::ifstream streamConfig(GetConfigFile()); + if (!streamConfig.good()) + return; // No i2pd.conf file is OK - std::set setOptions; - setOptions.insert("*"); + std::set setOptions; + setOptions.insert("*"); - for (boost::program_options::detail::config_file_iterator it(streamConfig, setOptions), end; it != end; ++it) - { - // Don't overwrite existing settings so command line settings override i2pd.conf - std::string strKey = std::string("-") + it->string_key; - if (mapSettingsRet.count(strKey) == 0) - { - mapSettingsRet[strKey] = it->value[0]; - } - mapMultiSettingsRet[strKey].push_back(it->value[0]); - } - } + for (boost::program_options::detail::config_file_iterator it(streamConfig, setOptions), end; it != end; ++it) + { + // Don't overwrite existing settings so command line settings override i2pd.conf + std::string strKey = std::string("-") + it->string_key; + if (mapSettingsRet.count(strKey) == 0) + { + mapSettingsRet[strKey] = it->value[0]; + } + mapMultiSettingsRet[strKey].push_back(it->value[0]); + } + } - boost::filesystem::path GetDefaultDataDir() - { - // Windows < Vista: C:\Documents and Settings\Username\Application Data\i2pd - // Windows >= Vista: C:\Users\Username\AppData\Roaming\i2pd - // Mac: ~/Library/Application Support/i2pd - // Unix: ~/.i2pd or /var/lib/i2pd is system=1 + boost::filesystem::path GetDefaultDataDir() + { + // Windows < Vista: C:\Documents and Settings\Username\Application Data\i2pd + // Windows >= Vista: C:\Users\Username\AppData\Roaming\i2pd + // Mac: ~/Library/Application Support/i2pd + // Unix: ~/.i2pd or /var/lib/i2pd is system=1 #ifdef WIN32 - // Windows - char localAppData[MAX_PATH]; - SHGetFolderPath(NULL, CSIDL_APPDATA, 0, NULL, localAppData); - return boost::filesystem::path(std::string(localAppData) + "\\" + appName); + // Windows + char localAppData[MAX_PATH]; + SHGetFolderPath(NULL, CSIDL_APPDATA, 0, NULL, localAppData); + return boost::filesystem::path(std::string(localAppData) + "\\" + appName); #else - if (i2p::util::config::GetArg("-service", 0)) // use system folder - return boost::filesystem::path(std::string ("/var/lib/") + appName); - boost::filesystem::path pathRet; - char* pszHome = getenv("HOME"); - if (pszHome == NULL || strlen(pszHome) == 0) - pathRet = boost::filesystem::path("/"); - else - pathRet = boost::filesystem::path(pszHome); + if (i2p::util::config::GetArg("-service", 0)) // use system folder + return boost::filesystem::path(std::string ("/var/lib/") + appName); + boost::filesystem::path pathRet; + char* pszHome = getenv("HOME"); + if (pszHome == NULL || strlen(pszHome) == 0) + pathRet = boost::filesystem::path("/"); + else + pathRet = boost::filesystem::path(pszHome); #ifdef MAC_OSX - // Mac - pathRet /= "Library/Application Support"; - boost::filesystem::create_directory(pathRet); - return pathRet / appName; + // Mac + pathRet /= "Library/Application Support"; + boost::filesystem::create_directory(pathRet); + return pathRet / appName; #else - // Unix - return pathRet / (std::string (".") + appName); + // Unix + return pathRet / (std::string (".") + appName); #endif #endif - } + } - boost::filesystem::path GetCertificatesDir() - { - return GetDataDir () / "certificates"; - } + boost::filesystem::path GetCertificatesDir() + { + return GetDataDir () / "certificates"; + } } namespace http { - std::string httpRequest(const std::string& address) - { - try - { - i2p::util::http::url u(address); - boost::asio::ip::tcp::iostream site; - // please don't uncomment following line because it's not compatible with boost 1.46 - // 1.46 is default boost for Ubuntu 12.04 LTS - //site.expires_from_now (boost::posix_time::seconds(30)); - if (u.port_ == 80) - site.connect(u.host_, "http"); - else - { - std::stringstream ss; ss << u.port_; - site.connect(u.host_, ss.str()); - } - if (site) - { - // User-Agent is needed to get the server list routerInfo files. - site << "GET " << u.path_ << " HTTP/1.1\r\nHost: " << u.host_ - << "\r\nAccept: */*\r\n" << "User-Agent: Wget/1.11.4\r\n" << "Connection: close\r\n\r\n"; - // read response and extract content - return GetHttpContent (site); - } - else - { - LogPrint ("Can't connect to ", address); - return ""; - } - } - catch (std::exception& ex) - { - LogPrint ("Failed to download ", address, " : ", ex.what ()); - return ""; - } - } + std::string httpRequest(const std::string& address) + { + try + { + i2p::util::http::url u(address); + boost::asio::ip::tcp::iostream site; + // please don't uncomment following line because it's not compatible with boost 1.46 + // 1.46 is default boost for Ubuntu 12.04 LTS + //site.expires_from_now (boost::posix_time::seconds(30)); + if (u.port_ == 80) + site.connect(u.host_, "http"); + else + { + std::stringstream ss; ss << u.port_; + site.connect(u.host_, ss.str()); + } + if (site) + { + // User-Agent is needed to get the server list routerInfo files. + site << "GET " << u.path_ << " HTTP/1.1\r\nHost: " << u.host_ + << "\r\nAccept: */*\r\n" << "User-Agent: Wget/1.11.4\r\n" << "Connection: close\r\n\r\n"; + // read response and extract content + return GetHttpContent (site); + } + else + { + LogPrint ("Can't connect to ", address); + return ""; + } + } + catch (std::exception& ex) + { + LogPrint ("Failed to download ", address, " : ", ex.what ()); + return ""; + } + } - std::string GetHttpContent (std::istream& response) - { - std::string version, statusMessage; - response >> version; // HTTP version - int status; - response >> status; // status - std::getline (response, statusMessage); - if (status == 200) // OK - { - bool isChunked = false; - std::string header; - while (!response.eof () && header != "\r") - { - std::getline(response, header); - auto colon = header.find (':'); - if (colon != std::string::npos) - { - std::string field = header.substr (0, colon); - if (field == i2p::util::http::TRANSFER_ENCODING) - isChunked = (header.find ("chunked", colon + 1) != std::string::npos); - } - } + std::string GetHttpContent (std::istream& response) + { + std::string version, statusMessage; + response >> version; // HTTP version + int status; + response >> status; // status + std::getline (response, statusMessage); + if (status == 200) // OK + { + bool isChunked = false; + std::string header; + while (!response.eof () && header != "\r") + { + std::getline(response, header); + auto colon = header.find (':'); + if (colon != std::string::npos) + { + std::string field = header.substr (0, colon); + if (field == i2p::util::http::TRANSFER_ENCODING) + isChunked = (header.find ("chunked", colon + 1) != std::string::npos); + } + } - std::stringstream ss; - if (isChunked) - MergeChunkedResponse (response, ss); - else - ss << response.rdbuf(); - return ss.str(); - } - else - { - LogPrint ("HTTP response ", status); - return ""; - } - } + std::stringstream ss; + if (isChunked) + MergeChunkedResponse (response, ss); + else + ss << response.rdbuf(); + return ss.str(); + } + else + { + LogPrint ("HTTP response ", status); + return ""; + } + } - void MergeChunkedResponse (std::istream& response, std::ostream& merged) - { - while (!response.eof ()) - { - std::string hexLen; - int len; - std::getline (response, hexLen); - std::istringstream iss (hexLen); - iss >> std::hex >> len; - if (!len) break; - char * buf = new char[len]; - response.read (buf, len); - merged.write (buf, len); - delete[] buf; - std::getline (response, hexLen); // read \r\n after chunk - } - } - - int httpRequestViaI2pProxy(const std::string& address, std::string &content) - { - content = ""; - try - { - boost::asio::ip::tcp::iostream site; - // please don't uncomment following line because it's not compatible with boost 1.46 - // 1.46 is default boost for Ubuntu 12.04 LTS - //site.expires_from_now (boost::posix_time::seconds(30)); - { - std::stringstream ss; ss << i2p::util::config::GetArg("-httpproxyport", 4446); - site.connect("127.0.0.1", ss.str()); - } - if (site) - { - i2p::util::http::url u(address); - std::stringstream ss; - ss << "GET " << address << " HTTP/1.0" << std::endl; - ss << "Host: " << u.host_ << std::endl; - ss << "Accept: */*" << std::endl; - ss << "User - Agent: Wget / 1.11.4" << std::endl; - ss << "Connection: close" << std::endl; - ss << std::endl; - site << ss.str(); + void MergeChunkedResponse (std::istream& response, std::ostream& merged) + { + while (!response.eof ()) + { + std::string hexLen; + int len; + std::getline (response, hexLen); + std::istringstream iss (hexLen); + iss >> std::hex >> len; + if (!len) break; + char * buf = new char[len]; + response.read (buf, len); + merged.write (buf, len); + delete[] buf; + std::getline (response, hexLen); // read \r\n after chunk + } + } + + int httpRequestViaI2pProxy(const std::string& address, std::string &content) + { + content = ""; + try + { + boost::asio::ip::tcp::iostream site; + // please don't uncomment following line because it's not compatible with boost 1.46 + // 1.46 is default boost for Ubuntu 12.04 LTS + //site.expires_from_now (boost::posix_time::seconds(30)); + { + std::stringstream ss; ss << i2p::util::config::GetArg("-httpproxyport", 4446); + site.connect("127.0.0.1", ss.str()); + } + if (site) + { + i2p::util::http::url u(address); + std::stringstream ss; + ss << "GET " << address << " HTTP/1.0" << std::endl; + ss << "Host: " << u.host_ << std::endl; + ss << "Accept: */*" << std::endl; + ss << "User - Agent: Wget / 1.11.4" << std::endl; + ss << "Connection: close" << std::endl; + ss << std::endl; + site << ss.str(); - // read response - std::string version, statusMessage; - site >> version; // HTTP version - int status; - site >> status; // status - std::getline(site, statusMessage); - if (status == 200) // OK - { - std::string header; - while (std::getline(site, header) && header != "\r"){} - std::stringstream ss; - ss << site.rdbuf(); - content = ss.str(); - return status; - } - else - { - LogPrint("HTTP response ", status); - return status; - } - } - else - { - LogPrint("Can't connect to proxy"); - return 408; - } - } - catch (std::exception& ex) - { - LogPrint("Failed to download ", address, " : ", ex.what()); - return 408; - } - } - - url::url(const std::string& url_s) - { - portstr_ = "80"; - port_ = 80; - user_ = ""; - pass_ = ""; + // read response + std::string version, statusMessage; + site >> version; // HTTP version + int status; + site >> status; // status + std::getline(site, statusMessage); + if (status == 200) // OK + { + std::string header; + while (std::getline(site, header) && header != "\r"){} + std::stringstream ss; + ss << site.rdbuf(); + content = ss.str(); + return status; + } + else + { + LogPrint("HTTP response ", status); + return status; + } + } + else + { + LogPrint("Can't connect to proxy"); + return 408; + } + } + catch (std::exception& ex) + { + LogPrint("Failed to download ", address, " : ", ex.what()); + return 408; + } + } + + url::url(const std::string& url_s) + { + portstr_ = "80"; + port_ = 80; + user_ = ""; + pass_ = ""; - parse(url_s); - } + parse(url_s); + } - // code for parser tests - //{ + // code for parser tests + //{ // i2p::util::http::url u_0("http://127.0.0.1:7070/asdasd?qqqqqqqqqqqq"); - // i2p::util::http::url u_1("http://user:password@site.com:8080/asdasd?qqqqqqqqqqqqq"); - // i2p::util::http::url u_2("http://user:password@site.com/asdasd?qqqqqqqqqqqqqq"); - // i2p::util::http::url u_3("http://user:@site.com/asdasd?qqqqqqqqqqqqq"); - // i2p::util::http::url u_4("http://user@site.com/asdasd?qqqqqqqqqqqq"); - // i2p::util::http::url u_5("http://@site.com:800/asdasd?qqqqqqqqqqqq"); - // i2p::util::http::url u_6("http://@site.com:err_port/asdasd?qqqqqqqqqqqq"); - // i2p::util::http::url u_7("http://user:password@site.com:err_port/asdasd?qqqqqqqqqqqq"); - //} - void url::parse(const std::string& url_s) - { - const std::string prot_end("://"); - std::string::const_iterator prot_i = search(url_s.begin(), url_s.end(), - prot_end.begin(), prot_end.end()); - protocol_.reserve(distance(url_s.begin(), prot_i)); - transform(url_s.begin(), prot_i, - back_inserter(protocol_), - std::ptr_fun(tolower)); // protocol is icase - if( prot_i == url_s.end() ) - return; - advance(prot_i, prot_end.length()); - std::string::const_iterator path_i = find(prot_i, url_s.end(), '/'); - host_.reserve(distance(prot_i, path_i)); - transform(prot_i, path_i, - back_inserter(host_), - std::ptr_fun(tolower)); // host is icase + // i2p::util::http::url u_1("http://user:password@site.com:8080/asdasd?qqqqqqqqqqqqq"); + // i2p::util::http::url u_2("http://user:password@site.com/asdasd?qqqqqqqqqqqqqq"); + // i2p::util::http::url u_3("http://user:@site.com/asdasd?qqqqqqqqqqqqq"); + // i2p::util::http::url u_4("http://user@site.com/asdasd?qqqqqqqqqqqq"); + // i2p::util::http::url u_5("http://@site.com:800/asdasd?qqqqqqqqqqqq"); + // i2p::util::http::url u_6("http://@site.com:err_port/asdasd?qqqqqqqqqqqq"); + // i2p::util::http::url u_7("http://user:password@site.com:err_port/asdasd?qqqqqqqqqqqq"); + //} + void url::parse(const std::string& url_s) + { + const std::string prot_end("://"); + std::string::const_iterator prot_i = search(url_s.begin(), url_s.end(), + prot_end.begin(), prot_end.end()); + protocol_.reserve(distance(url_s.begin(), prot_i)); + transform(url_s.begin(), prot_i, + back_inserter(protocol_), + std::ptr_fun(tolower)); // protocol is icase + if( prot_i == url_s.end() ) + return; + advance(prot_i, prot_end.length()); + std::string::const_iterator path_i = find(prot_i, url_s.end(), '/'); + host_.reserve(distance(prot_i, path_i)); + transform(prot_i, path_i, + back_inserter(host_), + std::ptr_fun(tolower)); // host is icase - // parse user/password - auto user_pass_i = find(host_.begin(), host_.end(), '@'); - if (user_pass_i != host_.end()) - { - std::string user_pass = std::string(host_.begin(), user_pass_i); - auto pass_i = find(user_pass.begin(), user_pass.end(), ':'); - if (pass_i != user_pass.end()) - { - user_ = std::string(user_pass.begin(), pass_i); - pass_ = std::string(pass_i + 1, user_pass.end()); - } - else - user_ = user_pass; + // parse user/password + auto user_pass_i = find(host_.begin(), host_.end(), '@'); + if (user_pass_i != host_.end()) + { + std::string user_pass = std::string(host_.begin(), user_pass_i); + auto pass_i = find(user_pass.begin(), user_pass.end(), ':'); + if (pass_i != user_pass.end()) + { + user_ = std::string(user_pass.begin(), pass_i); + pass_ = std::string(pass_i + 1, user_pass.end()); + } + else + user_ = user_pass; - host_.assign(user_pass_i + 1, host_.end()); - } + host_.assign(user_pass_i + 1, host_.end()); + } - // parse port - auto port_i = find(host_.begin(), host_.end(), ':'); - if (port_i != host_.end()) - { - portstr_ = std::string(port_i + 1, host_.end()); - host_.assign(host_.begin(), port_i); - try{ - port_ = boost::lexical_cast(portstr_); - } - catch (const std::exception& e) { - port_ = 80; - } - } + // parse port + auto port_i = find(host_.begin(), host_.end(), ':'); + if (port_i != host_.end()) + { + portstr_ = std::string(port_i + 1, host_.end()); + host_.assign(host_.begin(), port_i); + try{ + port_ = boost::lexical_cast(portstr_); + } + catch (const std::exception& e) { + port_ = 80; + } + } - std::string::const_iterator query_i = find(path_i, url_s.end(), '?'); - path_.assign(path_i, query_i); - if( query_i != url_s.end() ) - ++query_i; - query_.assign(query_i, url_s.end()); - } + std::string::const_iterator query_i = find(path_i, url_s.end(), '?'); + path_.assign(path_i, query_i); + if( query_i != url_s.end() ) + ++query_i; + query_.assign(query_i, url_s.end()); + } - std::string urlDecode(const std::string& data) - { - std::string res(data); - for (size_t pos = res.find('%'); pos != std::string::npos; pos = res.find('%',pos+1)) - { - char c = strtol(res.substr(pos+1,2).c_str(), NULL, 16); - res.replace(pos,3,1,c); - } - return res; - } + std::string urlDecode(const std::string& data) + { + std::string res(data); + for (size_t pos = res.find('%'); pos != std::string::npos; pos = res.find('%',pos+1)) + { + char c = strtol(res.substr(pos+1,2).c_str(), NULL, 16); + res.replace(pos,3,1,c); + } + return res; + } } namespace net { - int GetMTU (const boost::asio::ip::address& localAddress) - { -#if defined(__linux__) || defined(__FreeBSD_kernel__) - ifaddrs * ifaddr, * ifa = nullptr; - if (getifaddrs(&ifaddr) == -1) - { - LogPrint (eLogError, "Can't excute getifaddrs"); + int GetMTU (const boost::asio::ip::address& localAddress) + { +#if defined(__linux__) || defined(__FreeBSD_kernel__) + ifaddrs * ifaddr, * ifa = nullptr; + if (getifaddrs(&ifaddr) == -1) + { + LogPrint (eLogError, "Can't excute getifaddrs"); return 0; } - int family = 0; - // loook for interface matching local address - for (ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next) - { - if (!ifa->ifa_addr) continue; - family = ifa->ifa_addr->sa_family; - if (family == AF_INET && localAddress.is_v4 ()) - { - sockaddr_in * sa = (sockaddr_in *)ifa->ifa_addr; - if (!memcmp (&sa->sin_addr, localAddress.to_v4 ().to_bytes ().data (), 4)) - break; // address matches - } - else if (family == AF_INET6 && localAddress.is_v6 ()) - { - sockaddr_in6 * sa = (sockaddr_in6 *)ifa->ifa_addr; - if (!memcmp (&sa->sin6_addr, localAddress.to_v6 ().to_bytes ().data (), 16)) - break; // address matches - } - } - int mtu = 0; - if (ifa && family) // interface found? - { - int fd = socket (family, SOCK_DGRAM, 0); - if (fd > 0) - { - ifreq ifr; - strncpy (ifr.ifr_name, ifa->ifa_name, IFNAMSIZ); // set interface for query - if (ioctl (fd, SIOCGIFMTU, &ifr) >= 0) - mtu = ifr.ifr_mtu; // MTU - else - LogPrint (eLogError, "Failed to run ioctl"); - close (fd); - } - else - LogPrint (eLogError, "Failed to create datagram socket"); - } - else - LogPrint (eLogWarning, "Interface for local address", localAddress.to_string (), " not found"); + int family = 0; + // loook for interface matching local address + for (ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next) + { + if (!ifa->ifa_addr) continue; + family = ifa->ifa_addr->sa_family; + if (family == AF_INET && localAddress.is_v4 ()) + { + sockaddr_in * sa = (sockaddr_in *)ifa->ifa_addr; + if (!memcmp (&sa->sin_addr, localAddress.to_v4 ().to_bytes ().data (), 4)) + break; // address matches + } + else if (family == AF_INET6 && localAddress.is_v6 ()) + { + sockaddr_in6 * sa = (sockaddr_in6 *)ifa->ifa_addr; + if (!memcmp (&sa->sin6_addr, localAddress.to_v6 ().to_bytes ().data (), 16)) + break; // address matches + } + } + int mtu = 0; + if (ifa && family) // interface found? + { + int fd = socket (family, SOCK_DGRAM, 0); + if (fd > 0) + { + ifreq ifr; + strncpy (ifr.ifr_name, ifa->ifa_name, IFNAMSIZ); // set interface for query + if (ioctl (fd, SIOCGIFMTU, &ifr) >= 0) + mtu = ifr.ifr_mtu; // MTU + else + LogPrint (eLogError, "Failed to run ioctl"); + close (fd); + } + else + LogPrint (eLogError, "Failed to create datagram socket"); + } + else + LogPrint (eLogWarning, "Interface for local address", localAddress.to_string (), " not found"); - freeifaddrs (ifaddr); - return mtu; + freeifaddrs (ifaddr); + return mtu; #elif defined(WIN32) - int result = 576; // fallback MTU + int result = 576; // fallback MTU - DWORD dwRetVal = 0; - ULONG outBufLen = 0; - PIP_ADAPTER_ADDRESSES pAddresses = nullptr; - PIP_ADAPTER_ADDRESSES pCurrAddresses = nullptr; - PIP_ADAPTER_UNICAST_ADDRESS pUnicast = nullptr; + DWORD dwRetVal = 0; + ULONG outBufLen = 0; + PIP_ADAPTER_ADDRESSES pAddresses = nullptr; + PIP_ADAPTER_ADDRESSES pCurrAddresses = nullptr; + PIP_ADAPTER_UNICAST_ADDRESS pUnicast = nullptr; #ifdef UNICODE - string localAddress_temporary = localAddress.to_string(); - wstring localAddressUniversal (localAddress_temporary.begin(), localAddress_temporary.end()); + string localAddress_temporary = localAddress.to_string(); + wstring localAddressUniversal (localAddress_temporary.begin(), localAddress_temporary.end()); #else - std::string localAddressUniversal = localAddress.to_string(); + std::string localAddressUniversal = localAddress.to_string(); #endif - if (localAddress.is_v4()) - { - struct sockaddr_in inputAddress; - inet_pton(AF_INET, localAddressUniversal.c_str(), &(inputAddress.sin_addr)); + if (localAddress.is_v4()) + { + struct sockaddr_in inputAddress; + inet_pton(AF_INET, localAddressUniversal.c_str(), &(inputAddress.sin_addr)); - if (GetAdaptersAddresses (AF_INET, GAA_FLAG_INCLUDE_PREFIX, nullptr, pAddresses, &outBufLen) - == ERROR_BUFFER_OVERFLOW) - { - FREE (pAddresses); - pAddresses = (IP_ADAPTER_ADDRESSES *)MALLOC (outBufLen); - } + if (GetAdaptersAddresses (AF_INET, GAA_FLAG_INCLUDE_PREFIX, nullptr, pAddresses, &outBufLen) + == ERROR_BUFFER_OVERFLOW) + { + FREE (pAddresses); + pAddresses = (IP_ADAPTER_ADDRESSES *)MALLOC (outBufLen); + } - dwRetVal = GetAdaptersAddresses (AF_INET, GAA_FLAG_INCLUDE_PREFIX, nullptr, pAddresses, &outBufLen); - if (dwRetVal == NO_ERROR) - { - pCurrAddresses = pAddresses; - while (pCurrAddresses) - { - PIP_ADAPTER_UNICAST_ADDRESS firstUnicastAddress = pCurrAddresses->FirstUnicastAddress; + dwRetVal = GetAdaptersAddresses (AF_INET, GAA_FLAG_INCLUDE_PREFIX, nullptr, pAddresses, &outBufLen); + if (dwRetVal == NO_ERROR) + { + pCurrAddresses = pAddresses; + while (pCurrAddresses) + { + PIP_ADAPTER_UNICAST_ADDRESS firstUnicastAddress = pCurrAddresses->FirstUnicastAddress; - pUnicast = pCurrAddresses->FirstUnicastAddress; - if (pUnicast != nullptr) - { - for (int i = 0; pUnicast != nullptr; ++i) - { - LPSOCKADDR lpAddr = pUnicast->Address.lpSockaddr; - struct sockaddr_in *localInterfaceAddress = (struct sockaddr_in*) lpAddr; - if (localInterfaceAddress->sin_addr.S_un.S_addr == inputAddress.sin_addr.S_un.S_addr) - { - result = pAddresses->Mtu; - FREE (pAddresses); - pAddresses = nullptr; - return result; - } - pUnicast = pUnicast->Next; - } - } - else - { - LogPrint (eLogError, "GetMTU() has failed: not a unicast ipv4 address, this is not supported"); - } + pUnicast = pCurrAddresses->FirstUnicastAddress; + if (pUnicast != nullptr) + { + for (int i = 0; pUnicast != nullptr; ++i) + { + LPSOCKADDR lpAddr = pUnicast->Address.lpSockaddr; + struct sockaddr_in *localInterfaceAddress = (struct sockaddr_in*) lpAddr; + if (localInterfaceAddress->sin_addr.S_un.S_addr == inputAddress.sin_addr.S_un.S_addr) + { + result = pAddresses->Mtu; + FREE (pAddresses); + pAddresses = nullptr; + return result; + } + pUnicast = pUnicast->Next; + } + } + else + { + LogPrint (eLogError, "GetMTU() has failed: not a unicast ipv4 address, this is not supported"); + } - pCurrAddresses = pCurrAddresses->Next; - } + pCurrAddresses = pCurrAddresses->Next; + } - } - else - { - LogPrint (eLogError, "GetMTU() has failed: enclosed GetAdaptersAddresses() call has failed"); - } + } + else + { + LogPrint (eLogError, "GetMTU() has failed: enclosed GetAdaptersAddresses() call has failed"); + } - } - else if (localAddress.is_v6()) - { - struct sockaddr_in6 inputAddress; - inet_pton(AF_INET6, localAddressUniversal.c_str(), &(inputAddress.sin6_addr)); + } + else if (localAddress.is_v6()) + { + struct sockaddr_in6 inputAddress; + inet_pton(AF_INET6, localAddressUniversal.c_str(), &(inputAddress.sin6_addr)); - if (GetAdaptersAddresses(AF_INET6, GAA_FLAG_INCLUDE_PREFIX, nullptr, pAddresses, &outBufLen) - == ERROR_BUFFER_OVERFLOW) - { - FREE (pAddresses); - pAddresses = (IP_ADAPTER_ADDRESSES *)MALLOC (outBufLen); - } + if (GetAdaptersAddresses(AF_INET6, GAA_FLAG_INCLUDE_PREFIX, nullptr, pAddresses, &outBufLen) + == ERROR_BUFFER_OVERFLOW) + { + FREE (pAddresses); + pAddresses = (IP_ADAPTER_ADDRESSES *)MALLOC (outBufLen); + } - dwRetVal = GetAdaptersAddresses (AF_INET6, GAA_FLAG_INCLUDE_PREFIX, nullptr, pAddresses, &outBufLen); - if (dwRetVal == NO_ERROR) - { - bool found_address = false; - pCurrAddresses = pAddresses; - while (pCurrAddresses) - { - PIP_ADAPTER_UNICAST_ADDRESS firstUnicastAddress = pCurrAddresses->FirstUnicastAddress; + dwRetVal = GetAdaptersAddresses (AF_INET6, GAA_FLAG_INCLUDE_PREFIX, nullptr, pAddresses, &outBufLen); + if (dwRetVal == NO_ERROR) + { + bool found_address = false; + pCurrAddresses = pAddresses; + while (pCurrAddresses) + { + PIP_ADAPTER_UNICAST_ADDRESS firstUnicastAddress = pCurrAddresses->FirstUnicastAddress; - pUnicast = pCurrAddresses->FirstUnicastAddress; - if (pUnicast != nullptr) - { - for (int i = 0; pUnicast != nullptr; ++i) - { - LPSOCKADDR lpAddr = pUnicast->Address.lpSockaddr; - struct sockaddr_in6 *localInterfaceAddress = (struct sockaddr_in6*) lpAddr; + pUnicast = pCurrAddresses->FirstUnicastAddress; + if (pUnicast != nullptr) + { + for (int i = 0; pUnicast != nullptr; ++i) + { + LPSOCKADDR lpAddr = pUnicast->Address.lpSockaddr; + struct sockaddr_in6 *localInterfaceAddress = (struct sockaddr_in6*) lpAddr; - for (int j = 0; j != 8; ++j) - { - if (localInterfaceAddress->sin6_addr.u.Word[j] != inputAddress.sin6_addr.u.Word[j]) - { - break; - } - else - { - found_address = true; - } - } - if (found_address) - { - result = pAddresses->Mtu; - FREE (pAddresses); - pAddresses = nullptr; - return result; - } - pUnicast = pUnicast->Next; - } - } - else - { - LogPrint (eLogError, "GetMTU() has failed: not a unicast ipv6 address, this is not supported"); - } + for (int j = 0; j != 8; ++j) + { + if (localInterfaceAddress->sin6_addr.u.Word[j] != inputAddress.sin6_addr.u.Word[j]) + { + break; + } + else + { + found_address = true; + } + } + if (found_address) + { + result = pAddresses->Mtu; + FREE (pAddresses); + pAddresses = nullptr; + return result; + } + pUnicast = pUnicast->Next; + } + } + else + { + LogPrint (eLogError, "GetMTU() has failed: not a unicast ipv6 address, this is not supported"); + } - pCurrAddresses = pCurrAddresses->Next; - } + pCurrAddresses = pCurrAddresses->Next; + } - } - else - { - LogPrint (eLogError, "GetMTU() has failed: enclosed GetAdaptersAddresses() call has failed"); - } - } - else - { - LogPrint (eLogError, "GetMTU() has failed: address family is not supported"); - } + } + else + { + LogPrint (eLogError, "GetMTU() has failed: enclosed GetAdaptersAddresses() call has failed"); + } + } + else + { + LogPrint (eLogError, "GetMTU() has failed: address family is not supported"); + } - FREE (pAddresses); - pAddresses = nullptr; - LogPrint(eLogError, "GetMTU() error: control flow should never reach this line"); - return result; + FREE (pAddresses); + pAddresses = nullptr; + LogPrint(eLogError, "GetMTU() error: control flow should never reach this line"); + return result; #endif - } + } } } // util diff --git a/util.h b/util.h index 4b229380..2b70e703 100644 --- a/util.h +++ b/util.h @@ -14,61 +14,61 @@ namespace i2p { namespace util { - namespace config - { - extern std::map mapArgs; - extern std::map > mapMultiArgs; - void OptionParser(int argc, const char* const argv[]); - int GetArg(const std::string& strArg, int nDefault); - std::string GetArg(const std::string& strArg, const std::string& strDefault); - const char* GetCharArg(const std::string& strArg, const std::string& nDefault); - } + namespace config + { + extern std::map mapArgs; + extern std::map > mapMultiArgs; + void OptionParser(int argc, const char* const argv[]); + int GetArg(const std::string& strArg, int nDefault); + std::string GetArg(const std::string& strArg, const std::string& strDefault); + const char* GetCharArg(const std::string& strArg, const std::string& nDefault); + } - namespace filesystem - { - void SetAppName (const std::string& name); - std::string GetAppName (); + namespace filesystem + { + void SetAppName (const std::string& name); + std::string GetAppName (); - const boost::filesystem::path &GetDataDir(); - std::string GetFullPath (const std::string& filename); - boost::filesystem::path GetDefaultDataDir(); - boost::filesystem::path GetConfigFile(); - void ReadConfigFile(std::map& mapSettingsRet, + const boost::filesystem::path &GetDataDir(); + std::string GetFullPath (const std::string& filename); + boost::filesystem::path GetDefaultDataDir(); + boost::filesystem::path GetConfigFile(); + void ReadConfigFile(std::map& mapSettingsRet, std::map >& mapMultiSettingsRet); - boost::filesystem::path GetCertificatesDir(); - } + boost::filesystem::path GetCertificatesDir(); + } - namespace http - { - const char ETAG[] = "ETag"; - const char IF_NONE_MATCH[] = "If-None-Match"; - const char IF_MODIFIED_SINCE[] = "If-Modified-Since"; - const char LAST_MODIFIED[] = "Last-Modified"; - const char TRANSFER_ENCODING[] = "Transfer-Encoding"; + namespace http + { + const char ETAG[] = "ETag"; + const char IF_NONE_MATCH[] = "If-None-Match"; + const char IF_MODIFIED_SINCE[] = "If-Modified-Since"; + const char LAST_MODIFIED[] = "Last-Modified"; + const char TRANSFER_ENCODING[] = "Transfer-Encoding"; - std::string httpRequest(const std::string& address); - std::string GetHttpContent (std::istream& response); - void MergeChunkedResponse (std::istream& response, std::ostream& merged); - int httpRequestViaI2pProxy(const std::string& address, std::string &content); // return http code - std::string urlDecode(const std::string& data); - - struct url { - url(const std::string& url_s); // omitted copy, ==, accessors, ... - private: - void parse(const std::string& url_s); - public: - std::string protocol_, host_, path_, query_; - std::string portstr_; - unsigned int port_; - std::string user_; - std::string pass_; - }; - } + std::string httpRequest(const std::string& address); + std::string GetHttpContent (std::istream& response); + void MergeChunkedResponse (std::istream& response, std::ostream& merged); + int httpRequestViaI2pProxy(const std::string& address, std::string &content); // return http code + std::string urlDecode(const std::string& data); + + struct url { + url(const std::string& url_s); // omitted copy, ==, accessors, ... + private: + void parse(const std::string& url_s); + public: + std::string protocol_, host_, path_, query_; + std::string portstr_; + unsigned int port_; + std::string user_; + std::string pass_; + }; + } - namespace net - { - int GetMTU (const boost::asio::ip::address& localAddress); - } + namespace net + { + int GetMTU (const boost::asio::ip::address& localAddress); + } } }