mirror of
				https://github.com/PurpleI2P/i2pd.git
				synced 2025-11-04 08:30:46 +00:00 
			
		
		
		
	Implement #243, separate core/client (PCH support dropped for now)
This commit is contained in:
		
							parent
							
								
									bdaf2c16aa
								
							
						
					
					
						commit
						8ac9520dfd
					
				
					 153 changed files with 360 additions and 20020 deletions
				
			
		
							
								
								
									
										582
									
								
								core/AddressBook.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										582
									
								
								core/AddressBook.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,582 @@
 | 
			
		|||
#include <string.h>
 | 
			
		||||
#include <inttypes.h>
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <map>
 | 
			
		||||
#include <fstream>
 | 
			
		||||
#include <chrono>
 | 
			
		||||
#include <condition_variable>
 | 
			
		||||
#include <boost/filesystem.hpp>
 | 
			
		||||
#include <boost/lexical_cast.hpp>
 | 
			
		||||
#include <cryptopp/osrng.h>
 | 
			
		||||
#include "util/base64.h"
 | 
			
		||||
#include "util/util.h"
 | 
			
		||||
#include "Identity.h"
 | 
			
		||||
#include "util/Log.h"
 | 
			
		||||
#include "NetDb.h"
 | 
			
		||||
#include "AddressBook.h"
 | 
			
		||||
#include "Destination.h"
 | 
			
		||||
 | 
			
		||||
namespace i2p
 | 
			
		||||
{
 | 
			
		||||
namespace client
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
    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);
 | 
			
		||||
 | 
			
		||||
            int Load (std::map<std::string, i2p::data::IdentHash>& addresses);
 | 
			
		||||
            int Save (const std::map<std::string, i2p::data::IdentHash>& addresses);
 | 
			
		||||
 | 
			
		||||
        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");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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::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<std::string, i2p::data::IdentHash>& 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);
 | 
			
		||||
 | 
			
		||||
                    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<std::string, i2p::data::IdentHash>& 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),
 | 
			
		||||
        m_SharedLocalDestination(nullptr)
 | 
			
		||||
    {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    AddressBook::~AddressBook ()
 | 
			
		||||
    {   
 | 
			
		||||
        Stop ();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void AddressBook::Start (ClientDestination* local_destination)
 | 
			
		||||
    {
 | 
			
		||||
        m_SharedLocalDestination = local_destination;
 | 
			
		||||
        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)
 | 
			
		||||
        {
 | 
			
		||||
            i2p::util::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; 
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ClientDestination* AddressBook::getSharedLocalDestination() const
 | 
			
		||||
    {
 | 
			
		||||
        return m_SharedLocalDestination;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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::LoadHostsFromStream (std::istream& f)
 | 
			
		||||
    {
 | 
			
		||||
        std::unique_lock<std::mutex> l(m_AddressBookMutex);
 | 
			
		||||
        int numAddresses = 0;
 | 
			
		||||
        std::string s;
 | 
			
		||||
        while (!f.eof ())
 | 
			
		||||
        {
 | 
			
		||||
            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);
 | 
			
		||||
 | 
			
		||||
                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::StartSubscriptions ()
 | 
			
		||||
    {
 | 
			
		||||
        LoadSubscriptions ();
 | 
			
		||||
        if (!m_Subscriptions.size ()) return;   
 | 
			
		||||
 | 
			
		||||
        if (m_SharedLocalDestination)
 | 
			
		||||
        {
 | 
			
		||||
            m_SubscriptionsUpdateTimer = new boost::asio::deadline_timer(
 | 
			
		||||
                m_SharedLocalDestination->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::HandleSubscriptionsUpdateTimer (const boost::system::error_code& ecode)
 | 
			
		||||
    {
 | 
			
		||||
        if (ecode != boost::asio::error::operation_aborted)
 | 
			
		||||
        {
 | 
			
		||||
            if (!m_SharedLocalDestination)
 | 
			
		||||
                return; // TODO: error handling
 | 
			
		||||
            if (m_IsLoaded && !m_IsDownloading && m_SharedLocalDestination->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)
 | 
			
		||||
    {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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 = m_Book.getSharedLocalDestination()->FindLeaseSet (ident);
 | 
			
		||||
            if (!leaseSet)
 | 
			
		||||
            {
 | 
			
		||||
                std::unique_lock<std::mutex> l(newDataReceivedMutex);
 | 
			
		||||
                m_Book.getSharedLocalDestination()->RequestDestination (ident,
 | 
			
		||||
                    [&newDataReceived, &leaseSet](std::shared_ptr<i2p::data::LeaseSet> 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 = m_Book.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<std::mutex> 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);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										110
									
								
								core/AddressBook.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										110
									
								
								core/AddressBook.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,110 @@
 | 
			
		|||
#ifndef ADDRESS_BOOK_H__
 | 
			
		||||
#define ADDRESS_BOOK_H__
 | 
			
		||||
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <map>
 | 
			
		||||
#include <vector>
 | 
			
		||||
#include <iostream>
 | 
			
		||||
#include <mutex>
 | 
			
		||||
#include <boost/asio.hpp>
 | 
			
		||||
#include "util/base64.h"
 | 
			
		||||
#include "util/util.h"
 | 
			
		||||
#include "Identity.h"
 | 
			
		||||
#include "util/Log.h"
 | 
			
		||||
 | 
			
		||||
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"); }
 | 
			
		||||
 | 
			
		||||
    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<std::string, i2p::data::IdentHash>& addresses) = 0;
 | 
			
		||||
            virtual int Save (const std::map<std::string, i2p::data::IdentHash>& addresses) = 0;
 | 
			
		||||
    };          
 | 
			
		||||
 | 
			
		||||
    class ClientDestination;
 | 
			
		||||
    class AddressBookSubscription;
 | 
			
		||||
    class AddressBook
 | 
			
		||||
    {
 | 
			
		||||
        public:
 | 
			
		||||
 | 
			
		||||
            AddressBook ();
 | 
			
		||||
            ~AddressBook ();
 | 
			
		||||
            void Start (ClientDestination* local_destination);
 | 
			
		||||
            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);
 | 
			
		||||
 | 
			
		||||
            ClientDestination* getSharedLocalDestination() const;
 | 
			
		||||
 | 
			
		||||
            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 StartSubscriptions ();
 | 
			
		||||
            void StopSubscriptions ();
 | 
			
		||||
            
 | 
			
		||||
            AddressBookStorage * CreateStorage ();  
 | 
			
		||||
            void LoadHosts ();
 | 
			
		||||
            void LoadSubscriptions ();
 | 
			
		||||
 | 
			
		||||
            void HandleSubscriptionsUpdateTimer (const boost::system::error_code& ecode);
 | 
			
		||||
 | 
			
		||||
        private:    
 | 
			
		||||
 | 
			
		||||
            std::mutex m_AddressBookMutex;
 | 
			
		||||
            std::map<std::string, i2p::data::IdentHash>  m_Addresses;
 | 
			
		||||
            AddressBookStorage * m_Storage;
 | 
			
		||||
            volatile bool m_IsLoaded, m_IsDownloading;
 | 
			
		||||
            std::vector<AddressBookSubscription *> m_Subscriptions;
 | 
			
		||||
            AddressBookSubscription * m_DefaultSubscription; // in case if we don't know any addresses yet
 | 
			
		||||
            boost::asio::deadline_timer * m_SubscriptionsUpdateTimer;
 | 
			
		||||
            ClientDestination* m_SharedLocalDestination;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    class AddressBookSubscription
 | 
			
		||||
    {
 | 
			
		||||
        public:
 | 
			
		||||
 | 
			
		||||
            AddressBookSubscription (AddressBook& book, const std::string& link);
 | 
			
		||||
            void CheckSubscription ();
 | 
			
		||||
 | 
			
		||||
        private:
 | 
			
		||||
 | 
			
		||||
            void Request ();
 | 
			
		||||
        
 | 
			
		||||
        private:
 | 
			
		||||
 | 
			
		||||
            AddressBook& m_Book;
 | 
			
		||||
            std::string m_Link, m_Etag, m_LastModified;
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										56
									
								
								core/CMakeLists.txt
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								core/CMakeLists.txt
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,56 @@
 | 
			
		|||
set(CORE_SRC
 | 
			
		||||
  "transport/NTCPSession.cpp"
 | 
			
		||||
  "transport/SSU.cpp"
 | 
			
		||||
  "transport/SSUData.cpp"
 | 
			
		||||
  "transport/SSUSession.cpp"
 | 
			
		||||
  "transport/Transports.cpp"
 | 
			
		||||
  "crypto/CryptoConst.cpp"
 | 
			
		||||
  "crypto/aes.cpp"
 | 
			
		||||
  "crypto/Signature.cpp"
 | 
			
		||||
  "util/base64.cpp"
 | 
			
		||||
  "util/util.cpp"
 | 
			
		||||
  "util/Log.cpp"
 | 
			
		||||
  "tunnel/TransitTunnel.cpp"
 | 
			
		||||
  "tunnel/Tunnel.cpp"
 | 
			
		||||
  "tunnel/TunnelGateway.cpp"
 | 
			
		||||
  "tunnel/TunnelEndpoint.cpp"
 | 
			
		||||
  "tunnel/TunnelPool.cpp"
 | 
			
		||||
  "tunnel/TunnelCrypto.cpp"
 | 
			
		||||
  "AddressBook.cpp"	
 | 
			
		||||
  "Garlic.cpp"
 | 
			
		||||
  "I2NPProtocol.cpp"
 | 
			
		||||
  "Identity.cpp"
 | 
			
		||||
  "LeaseSet.cpp"
 | 
			
		||||
  "NetDbRequests.cpp"	
 | 
			
		||||
  "NetDb.cpp"
 | 
			
		||||
  "Profiling.cpp"
 | 
			
		||||
  "RouterContext.cpp"
 | 
			
		||||
  "RouterInfo.cpp"
 | 
			
		||||
  "Streaming.cpp"
 | 
			
		||||
  "Destination.cpp"	
 | 
			
		||||
  "Datagram.cpp"
 | 
			
		||||
  "UPnP.cpp"
 | 
			
		||||
  "Reseed.cpp"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
if(CMAKE_SYSTEM_NAME STREQUAL "Windows")
 | 
			
		||||
    list(APPEND CORE_SRC "util/I2PEndian.cpp")
 | 
			
		||||
endif()
 | 
			
		||||
 | 
			
		||||
include_directories(".")
 | 
			
		||||
 | 
			
		||||
# Library building
 | 
			
		||||
if(WITH_LIBRARY)
 | 
			
		||||
    add_library(${CORE_NAME} ${CORE_SRC})
 | 
			
		||||
    target_link_libraries(
 | 
			
		||||
        ${CORE_NAME} ${Boost_LIBRARIES} ${CRYPTO++_LIBRARIES}
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    install(
 | 
			
		||||
        TARGETS ${CORE_NAME}
 | 
			
		||||
        RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
 | 
			
		||||
        LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
 | 
			
		||||
        ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
 | 
			
		||||
    )
 | 
			
		||||
endif()
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										151
									
								
								core/Datagram.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										151
									
								
								core/Datagram.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,151 @@
 | 
			
		|||
#include <string.h>
 | 
			
		||||
#include <vector>
 | 
			
		||||
#include <cryptopp/sha.h>
 | 
			
		||||
#include <cryptopp/gzip.h>
 | 
			
		||||
#include "util/Log.h"
 | 
			
		||||
#include "tunnel/TunnelBase.h"
 | 
			
		||||
#include "RouterContext.h"
 | 
			
		||||
#include "Destination.h"
 | 
			
		||||
#include "Datagram.h"
 | 
			
		||||
 | 
			
		||||
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);
 | 
			
		||||
 | 
			
		||||
        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<i2p::data::LeaseSet> remote, I2NPMessage * msg)
 | 
			
		||||
    {
 | 
			
		||||
        if (remote)
 | 
			
		||||
            SendMsg (msg, remote);
 | 
			
		||||
        else
 | 
			
		||||
            DeleteI2NPMessage (msg);
 | 
			
		||||
    }   
 | 
			
		||||
        
 | 
			
		||||
    void DatagramDestination::SendMsg (I2NPMessage * msg, std::shared_ptr<const i2p::data::LeaseSet> remote)
 | 
			
		||||
    {
 | 
			
		||||
        auto outboundTunnel = m_Owner.GetTunnelPool ()->GetNextOutboundTunnel ();
 | 
			
		||||
        auto leases = remote->GetNonExpiredLeases ();
 | 
			
		||||
        if (!leases.empty () && outboundTunnel)
 | 
			
		||||
        {
 | 
			
		||||
            std::vector<i2p::tunnel::TunnelMessageBlock> 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 ();
 | 
			
		||||
 | 
			
		||||
        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");
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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;
 | 
			
		||||
    }   
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										57
									
								
								core/Datagram.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								core/Datagram.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,57 @@
 | 
			
		|||
#ifndef DATAGRAM_H__
 | 
			
		||||
#define DATAGRAM_H__
 | 
			
		||||
 | 
			
		||||
#include <inttypes.h>
 | 
			
		||||
#include <memory>
 | 
			
		||||
#include <functional>
 | 
			
		||||
#include <map>
 | 
			
		||||
#include "Identity.h"
 | 
			
		||||
#include "LeaseSet.h"
 | 
			
		||||
#include "I2NPProtocol.h"
 | 
			
		||||
 | 
			
		||||
namespace i2p
 | 
			
		||||
{
 | 
			
		||||
namespace client
 | 
			
		||||
{
 | 
			
		||||
    class ClientDestination;
 | 
			
		||||
}
 | 
			
		||||
namespace datagram
 | 
			
		||||
{
 | 
			
		||||
    const size_t MAX_DATAGRAM_SIZE = 32768;
 | 
			
		||||
    class DatagramDestination
 | 
			
		||||
    {
 | 
			
		||||
        typedef std::function<void (const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len)> Receiver;
 | 
			
		||||
 | 
			
		||||
        public:
 | 
			
		||||
 | 
			
		||||
            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 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); };
 | 
			
		||||
 | 
			
		||||
        private:
 | 
			
		||||
 | 
			
		||||
            void HandleLeaseSetRequestComplete (std::shared_ptr<i2p::data::LeaseSet> 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<const i2p::data::LeaseSet> remote);
 | 
			
		||||
            void HandleDatagram (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len);
 | 
			
		||||
 | 
			
		||||
        private:
 | 
			
		||||
 | 
			
		||||
            i2p::client::ClientDestination& m_Owner;
 | 
			
		||||
            Receiver m_Receiver; // default
 | 
			
		||||
            std::map<uint16_t, Receiver> m_ReceiversByPorts;
 | 
			
		||||
    };      
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										667
									
								
								core/Destination.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										667
									
								
								core/Destination.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,667 @@
 | 
			
		|||
#include <algorithm>
 | 
			
		||||
#include <cassert>
 | 
			
		||||
#include <boost/lexical_cast.hpp>
 | 
			
		||||
#include "util/Log.h"
 | 
			
		||||
#include "util/util.h"
 | 
			
		||||
#include "crypto/ElGamal.h"
 | 
			
		||||
#include "util/Timestamp.h"
 | 
			
		||||
#include "NetDb.h"
 | 
			
		||||
#include "Destination.h"
 | 
			
		||||
#include "AddressBook.h"
 | 
			
		||||
 | 
			
		||||
namespace i2p
 | 
			
		||||
{
 | 
			
		||||
namespace client
 | 
			
		||||
{
 | 
			
		||||
    ClientDestination::ClientDestination (const i2p::data::PrivateKeys& keys, bool isPublic, 
 | 
			
		||||
            const std::map<std::string, std::string> * 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<std::vector<i2p::data::IdentHash> > explicitPeers;
 | 
			
		||||
        if (params)
 | 
			
		||||
        {
 | 
			
		||||
            auto it = params->find (I2CP_PARAM_INBOUND_TUNNEL_LENGTH);
 | 
			
		||||
            if (it != params->end ())
 | 
			
		||||
            {   
 | 
			
		||||
                int len = boost::lexical_cast<int>(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<int>(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<int>(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<int>(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::vector<i2p::data::IdentHash> >();
 | 
			
		||||
                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<i2p::stream::StreamingDestination> (*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;
 | 
			
		||||
    }   
 | 
			
		||||
 | 
			
		||||
    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;
 | 
			
		||||
            }   
 | 
			
		||||
        }   
 | 
			
		||||
    }   
 | 
			
		||||
 | 
			
		||||
    std::shared_ptr<const i2p::data::LeaseSet> 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<const i2p::data::LeaseSet> 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));
 | 
			
		||||
    }   
 | 
			
		||||
 | 
			
		||||
    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<I2NPMessage> msg)
 | 
			
		||||
    {
 | 
			
		||||
        m_Service.post (std::bind (&ClientDestination::HandleGarlicMessage, this, msg)); 
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void ClientDestination::ProcessDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg)
 | 
			
		||||
    {
 | 
			
		||||
        m_Service.post (std::bind (&ClientDestination::HandleDeliveryStatusMessage, this, msg)); 
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void ClientDestination::HandleI2NPMessage (const uint8_t * buf, size_t len, std::shared_ptr<i2p::tunnel::InboundTunnel> 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<i2p::data::LeaseSet> 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<i2p::data::LeaseSet> (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<I2NPMessage> 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<i2p::data::IdentHash> 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::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<i2p::data::LeaseSet> ls)
 | 
			
		||||
                {
 | 
			
		||||
                    if (ls)
 | 
			
		||||
                        streamRequestComplete(CreateStream (ls, port));
 | 
			
		||||
                    else
 | 
			
		||||
                        streamRequestComplete (nullptr);
 | 
			
		||||
                });
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    std::shared_ptr<i2p::stream::Stream> ClientDestination::CreateStream (std::shared_ptr<const i2p::data::LeaseSet> remote, int port)
 | 
			
		||||
    {
 | 
			
		||||
        if (m_StreamingDestination)
 | 
			
		||||
            return m_StreamingDestination->CreateNewOutgoingStream (remote, port);
 | 
			
		||||
        else
 | 
			
		||||
            return nullptr;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    std::shared_ptr<i2p::stream::StreamingDestination> 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;
 | 
			
		||||
    }   
 | 
			
		||||
 | 
			
		||||
    std::shared_ptr<i2p::stream::StreamingDestination> ClientDestination::CreateStreamingDestination (int port)
 | 
			
		||||
    {
 | 
			
		||||
        auto dest = std::make_shared<i2p::stream::StreamingDestination> (*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 (nullptr);
 | 
			
		||||
            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<i2p::data::IdentHash> 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<i2p::data::IdentHash, LeaseSetRequest *>(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<const i2p::data::RouterInfo>  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);
 | 
			
		||||
 | 
			
		||||
            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 (nullptr);
 | 
			
		||||
                    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::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++;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										156
									
								
								core/Destination.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										156
									
								
								core/Destination.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,156 @@
 | 
			
		|||
#ifndef DESTINATION_H__
 | 
			
		||||
#define DESTINATION_H__
 | 
			
		||||
 | 
			
		||||
#include <thread>
 | 
			
		||||
#include <mutex>
 | 
			
		||||
#include <memory>
 | 
			
		||||
#include <map>
 | 
			
		||||
#include <set>
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <functional>
 | 
			
		||||
#include <boost/asio.hpp>
 | 
			
		||||
#include "Identity.h"
 | 
			
		||||
#include "tunnel/TunnelPool.h"
 | 
			
		||||
#include "crypto/CryptoConst.h"
 | 
			
		||||
#include "LeaseSet.h"
 | 
			
		||||
#include "Garlic.h"
 | 
			
		||||
#include "NetDb.h"
 | 
			
		||||
#include "Streaming.h"
 | 
			
		||||
#include "Datagram.h"
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
 | 
			
		||||
    typedef std::function<void (std::shared_ptr<i2p::stream::Stream> stream)> StreamRequestComplete;
 | 
			
		||||
 | 
			
		||||
    class ClientDestination: public i2p::garlic::GarlicDestination
 | 
			
		||||
    {
 | 
			
		||||
        typedef std::function<void (std::shared_ptr<i2p::data::LeaseSet> leaseSet)> RequestComplete;
 | 
			
		||||
        // leaseSet = nullptr means not found
 | 
			
		||||
        struct LeaseSetRequest
 | 
			
		||||
        {
 | 
			
		||||
            LeaseSetRequest (boost::asio::io_service& service): requestTime (0), requestTimeoutTimer (service) {};
 | 
			
		||||
            std::set<i2p::data::IdentHash> excluded;
 | 
			
		||||
            uint64_t requestTime;
 | 
			
		||||
            boost::asio::deadline_timer requestTimeoutTimer;
 | 
			
		||||
            RequestComplete requestComplete;
 | 
			
		||||
        };  
 | 
			
		||||
        
 | 
			
		||||
        
 | 
			
		||||
        public:
 | 
			
		||||
 | 
			
		||||
            ClientDestination (const i2p::data::PrivateKeys& keys, bool isPublic, const std::map<std::string, std::string> * 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<i2p::tunnel::TunnelPool> GetTunnelPool () { return m_Pool; }; 
 | 
			
		||||
            bool IsReady () const { return m_LeaseSet && m_LeaseSet->HasNonExpiredLeases () && m_Pool->GetOutboundTunnels ().size () > 0; };
 | 
			
		||||
            std::shared_ptr<const i2p::data::LeaseSet> FindLeaseSet (const i2p::data::IdentHash& ident);
 | 
			
		||||
            bool RequestDestination (const i2p::data::IdentHash& dest, RequestComplete requestComplete = nullptr);
 | 
			
		||||
            
 | 
			
		||||
            // streaming
 | 
			
		||||
            std::shared_ptr<i2p::stream::StreamingDestination> CreateStreamingDestination (int port); // additional
 | 
			
		||||
            std::shared_ptr<i2p::stream::StreamingDestination> 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<i2p::stream::Stream> CreateStream (std::shared_ptr<const i2p::data::LeaseSet> 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<const i2p::data::LeaseSet> GetLeaseSet ();
 | 
			
		||||
            std::shared_ptr<i2p::tunnel::TunnelPool> GetTunnelPool () const { return m_Pool; }
 | 
			
		||||
            void HandleI2NPMessage (const uint8_t * buf, size_t len, std::shared_ptr<i2p::tunnel::InboundTunnel> from);
 | 
			
		||||
 | 
			
		||||
            // override GarlicDestination
 | 
			
		||||
            bool SubmitSessionKey (const uint8_t * key, const uint8_t * tag);
 | 
			
		||||
            void ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg);
 | 
			
		||||
            void ProcessDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg);   
 | 
			
		||||
            void SetLeaseSetUpdated ();
 | 
			
		||||
 | 
			
		||||
            // 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<I2NPMessage> msg);        
 | 
			
		||||
 | 
			
		||||
            void RequestLeaseSet (const i2p::data::IdentHash& dest, RequestComplete requestComplete);
 | 
			
		||||
            bool SendLeaseSetRequest (const i2p::data::IdentHash& dest, std::shared_ptr<const i2p::data::RouterInfo>  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<i2p::data::IdentHash, std::shared_ptr<i2p::data::LeaseSet> > m_RemoteLeaseSets;
 | 
			
		||||
            std::map<i2p::data::IdentHash, LeaseSetRequest *> m_LeaseSetRequests;
 | 
			
		||||
 | 
			
		||||
            std::shared_ptr<i2p::tunnel::TunnelPool> m_Pool;
 | 
			
		||||
            std::shared_ptr<i2p::data::LeaseSet> m_LeaseSet;
 | 
			
		||||
            bool m_IsPublic;
 | 
			
		||||
            uint32_t m_PublishReplyToken;
 | 
			
		||||
            std::set<i2p::data::IdentHash> m_ExcludedFloodfills; // for publishing
 | 
			
		||||
            
 | 
			
		||||
            std::shared_ptr<i2p::stream::StreamingDestination> m_StreamingDestination; // default
 | 
			
		||||
            std::map<uint16_t, std::shared_ptr<i2p::stream::StreamingDestination> > 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 (); };
 | 
			
		||||
    };  
 | 
			
		||||
}   
 | 
			
		||||
}   
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										617
									
								
								core/Garlic.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										617
									
								
								core/Garlic.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,617 @@
 | 
			
		|||
#include <inttypes.h>
 | 
			
		||||
#include "util/I2PEndian.h"
 | 
			
		||||
#include <map>
 | 
			
		||||
#include <string>
 | 
			
		||||
#include "RouterContext.h"
 | 
			
		||||
#include "I2NPProtocol.h"
 | 
			
		||||
#include "tunnel/Tunnel.h"
 | 
			
		||||
#include "tunnel/TunnelPool.h"
 | 
			
		||||
#include "util/Timestamp.h"
 | 
			
		||||
#include "Destination.h"
 | 
			
		||||
#include "Garlic.h"
 | 
			
		||||
 | 
			
		||||
namespace i2p
 | 
			
		||||
{
 | 
			
		||||
namespace garlic
 | 
			
		||||
{
 | 
			
		||||
    GarlicRoutingSession::GarlicRoutingSession (GarlicDestination * owner, 
 | 
			
		||||
        std::shared_ptr<const i2p::data::RoutingDestination> 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 ()
 | 
			
		||||
    {   
 | 
			
		||||
        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;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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<I2NPMessage> GarlicRoutingSession::WrapSingleMessage (std::shared_ptr<const I2NPMessage> 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;
 | 
			
		||||
    }   
 | 
			
		||||
 | 
			
		||||
    size_t GarlicRoutingSession::CreateAESBlock (uint8_t * buf, std::shared_ptr<const I2NPMessage> 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<const I2NPMessage> 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;
 | 
			
		||||
 | 
			
		||||
            // 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<const I2NPMessage> 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");
 | 
			
		||||
 | 
			
		||||
        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<i2p::crypto::CBCDecryption>();
 | 
			
		||||
            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;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void GarlicDestination::HandleGarlicMessage (std::shared_ptr<I2NPMessage> 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<i2p::crypto::CBCDecryption>();
 | 
			
		||||
                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;
 | 
			
		||||
        }   
 | 
			
		||||
    }   
 | 
			
		||||
 | 
			
		||||
    void GarlicDestination::HandleAESBlock (uint8_t * buf, size_t len, std::shared_ptr<i2p::crypto::CBCDecryption> decryption,
 | 
			
		||||
        std::shared_ptr<i2p::tunnel::InboundTunnel> 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);
 | 
			
		||||
    }   
 | 
			
		||||
 | 
			
		||||
    void GarlicDestination::HandleGarlicPayload (uint8_t * buf, size_t len, std::shared_ptr<i2p::tunnel::InboundTunnel> 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<i2p::tunnel::OutboundTunnel> 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<I2NPMessage> GarlicDestination::WrapMessage (std::shared_ptr<const i2p::data::RoutingDestination> destination, 
 | 
			
		||||
        std::shared_ptr<I2NPMessage> msg, bool attachLeaseSet)  
 | 
			
		||||
    {
 | 
			
		||||
        auto session = GetRoutingSession (destination, attachLeaseSet);  // 32 tags by default
 | 
			
		||||
        return session->WrapSingleMessage (msg);    
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    std::shared_ptr<GarlicRoutingSession> GarlicDestination::GetRoutingSession (
 | 
			
		||||
        std::shared_ptr<const i2p::data::RoutingDestination> destination, bool attachLeaseSet)
 | 
			
		||||
    {
 | 
			
		||||
        auto it = m_Sessions.find (destination->GetIdentHash ());
 | 
			
		||||
        std::shared_ptr<GarlicRoutingSession> session;
 | 
			
		||||
        if (it != m_Sessions.end ())
 | 
			
		||||
            session = it->second;
 | 
			
		||||
        if (!session)
 | 
			
		||||
        {
 | 
			
		||||
            session = std::make_shared<GarlicRoutingSession> (this, destination, 
 | 
			
		||||
                attachLeaseSet ? 40 : 4, attachLeaseSet); // 40 tags for connections and 4 for LS requests
 | 
			
		||||
            std::unique_lock<std::mutex> l(m_SessionsMutex);
 | 
			
		||||
            m_Sessions[destination->GetIdentHash ()] = session;
 | 
			
		||||
        }   
 | 
			
		||||
        return session;
 | 
			
		||||
    }   
 | 
			
		||||
    
 | 
			
		||||
    void GarlicDestination::CleanupRoutingSessions ()
 | 
			
		||||
    {
 | 
			
		||||
        std::unique_lock<std::mutex> 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<GarlicRoutingSession> session, uint32_t msgID)
 | 
			
		||||
    {
 | 
			
		||||
        m_CreatedSessions[msgID] = session;
 | 
			
		||||
    }       
 | 
			
		||||
 | 
			
		||||
    void GarlicDestination::HandleDeliveryStatusMessage (std::shared_ptr<I2NPMessage> 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<std::mutex> l(m_SessionsMutex);    
 | 
			
		||||
        for (auto it: m_Sessions)
 | 
			
		||||
            it.second->SetLeaseSetUpdated ();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void GarlicDestination::ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg)
 | 
			
		||||
    {
 | 
			
		||||
        HandleGarlicMessage (msg);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void GarlicDestination::ProcessDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg)
 | 
			
		||||
    {
 | 
			
		||||
        HandleDeliveryStatusMessage (msg);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}   
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										169
									
								
								core/Garlic.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										169
									
								
								core/Garlic.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,169 @@
 | 
			
		|||
#ifndef GARLIC_H__
 | 
			
		||||
#define GARLIC_H__
 | 
			
		||||
 | 
			
		||||
#include <inttypes.h>
 | 
			
		||||
#include <map>
 | 
			
		||||
#include <list>
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <thread>
 | 
			
		||||
#include <mutex>
 | 
			
		||||
#include <memory>
 | 
			
		||||
#include <cryptopp/osrng.h>
 | 
			
		||||
#include "crypto/aes.h"
 | 
			
		||||
#include "I2NPProtocol.h"
 | 
			
		||||
#include "LeaseSet.h"
 | 
			
		||||
#include "util/Queue.h"
 | 
			
		||||
#include "Identity.h"
 | 
			
		||||
 | 
			
		||||
namespace i2p
 | 
			
		||||
{   
 | 
			
		||||
namespace garlic
 | 
			
		||||
{
 | 
			
		||||
    
 | 
			
		||||
    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()  
 | 
			
		||||
 | 
			
		||||
    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;    
 | 
			
		||||
#endif
 | 
			
		||||
        uint32_t creationTime; // seconds since epoch   
 | 
			
		||||
    };
 | 
			
		||||
        
 | 
			
		||||
    class GarlicDestination;
 | 
			
		||||
    class GarlicRoutingSession: public std::enable_shared_from_this<GarlicRoutingSession>
 | 
			
		||||
    {
 | 
			
		||||
            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:
 | 
			
		||||
 | 
			
		||||
            GarlicRoutingSession (GarlicDestination * owner, std::shared_ptr<const i2p::data::RoutingDestination> destination, 
 | 
			
		||||
                int numTags, bool attachLeaseSet);
 | 
			
		||||
            GarlicRoutingSession (const uint8_t * sessionKey, const SessionTag& sessionTag); // one time encryption
 | 
			
		||||
            ~GarlicRoutingSession ();
 | 
			
		||||
            std::shared_ptr<I2NPMessage> WrapSingleMessage (std::shared_ptr<const I2NPMessage> msg);
 | 
			
		||||
            void MessageConfirmed (uint32_t msgID);
 | 
			
		||||
            bool CleanupExpiredTags (); // returns true if something left 
 | 
			
		||||
 | 
			
		||||
            void SetLeaseSetUpdated () 
 | 
			
		||||
            { 
 | 
			
		||||
                if (m_LeaseSetUpdateStatus != eLeaseSetDoNotSend) m_LeaseSetUpdateStatus = eLeaseSetUpdated; 
 | 
			
		||||
            };
 | 
			
		||||
            
 | 
			
		||||
        private:
 | 
			
		||||
 | 
			
		||||
            size_t CreateAESBlock (uint8_t * buf, std::shared_ptr<const I2NPMessage> msg);
 | 
			
		||||
            size_t CreateGarlicPayload (uint8_t * payload, std::shared_ptr<const I2NPMessage> msg, UnconfirmedTags * newTags);
 | 
			
		||||
            size_t CreateGarlicClove (uint8_t * buf, std::shared_ptr<const I2NPMessage> msg, bool isDestination);
 | 
			
		||||
            size_t CreateDeliveryStatusClove (uint8_t * buf, uint32_t msgID);
 | 
			
		||||
 | 
			
		||||
            void TagsConfirmed (uint32_t msgID);
 | 
			
		||||
            UnconfirmedTags * GenerateSessionTags ();
 | 
			
		||||
 | 
			
		||||
        private:
 | 
			
		||||
 | 
			
		||||
            GarlicDestination * m_Owner;
 | 
			
		||||
            std::shared_ptr<const i2p::data::RoutingDestination> m_Destination;
 | 
			
		||||
            i2p::crypto::AESKey m_SessionKey;
 | 
			
		||||
            std::list<SessionTag> m_SessionTags;
 | 
			
		||||
            int m_NumTags;
 | 
			
		||||
            std::map<uint32_t, UnconfirmedTags *> 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 ();
 | 
			
		||||
 | 
			
		||||
            std::shared_ptr<GarlicRoutingSession> GetRoutingSession (std::shared_ptr<const i2p::data::RoutingDestination> destination, bool attachLeaseSet);    
 | 
			
		||||
            void CleanupRoutingSessions ();
 | 
			
		||||
            void RemoveCreatedSession (uint32_t msgID);
 | 
			
		||||
            std::shared_ptr<I2NPMessage> WrapMessage (std::shared_ptr<const i2p::data::RoutingDestination> destination, 
 | 
			
		||||
                std::shared_ptr<I2NPMessage> 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<GarlicRoutingSession> session, uint32_t msgID);
 | 
			
		||||
            
 | 
			
		||||
            virtual void ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg);
 | 
			
		||||
            virtual void ProcessDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg);           
 | 
			
		||||
            virtual void SetLeaseSetUpdated ();
 | 
			
		||||
            
 | 
			
		||||
            virtual std::shared_ptr<const i2p::data::LeaseSet> GetLeaseSet () = 0; // TODO
 | 
			
		||||
            virtual std::shared_ptr<i2p::tunnel::TunnelPool> GetTunnelPool () const = 0;
 | 
			
		||||
            virtual void HandleI2NPMessage (const uint8_t * buf, size_t len, std::shared_ptr<i2p::tunnel::InboundTunnel> from) = 0;
 | 
			
		||||
            
 | 
			
		||||
        protected:
 | 
			
		||||
 | 
			
		||||
            void HandleGarlicMessage (std::shared_ptr<I2NPMessage> msg);
 | 
			
		||||
            void HandleDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg);            
 | 
			
		||||
    
 | 
			
		||||
        private:
 | 
			
		||||
 | 
			
		||||
            void HandleAESBlock (uint8_t * buf, size_t len, std::shared_ptr<i2p::crypto::CBCDecryption> decryption, 
 | 
			
		||||
                std::shared_ptr<i2p::tunnel::InboundTunnel> from);
 | 
			
		||||
            void HandleGarlicPayload (uint8_t * buf, size_t len, std::shared_ptr<i2p::tunnel::InboundTunnel> from);
 | 
			
		||||
 | 
			
		||||
        private:
 | 
			
		||||
            
 | 
			
		||||
            // outgoing sessions
 | 
			
		||||
            std::mutex m_SessionsMutex;
 | 
			
		||||
            std::map<i2p::data::IdentHash, std::shared_ptr<GarlicRoutingSession> > m_Sessions;
 | 
			
		||||
            // incoming
 | 
			
		||||
            std::map<SessionTag, std::shared_ptr<i2p::crypto::CBCDecryption>> m_Tags;
 | 
			
		||||
            uint32_t m_LastTagsCleanupTime;
 | 
			
		||||
            // DeliveryStatus
 | 
			
		||||
            std::map<uint32_t, std::shared_ptr<GarlicRoutingSession> > m_CreatedSessions; // msgID -> session
 | 
			
		||||
    };  
 | 
			
		||||
}   
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										624
									
								
								core/I2NPProtocol.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										624
									
								
								core/I2NPProtocol.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,624 @@
 | 
			
		|||
#include <string.h>
 | 
			
		||||
#include <atomic>
 | 
			
		||||
#include "util/I2PEndian.h"
 | 
			
		||||
#include <cryptopp/gzip.h>
 | 
			
		||||
#include "crypto/ElGamal.h"
 | 
			
		||||
#include "util/Timestamp.h"
 | 
			
		||||
#include "RouterContext.h"
 | 
			
		||||
#include "NetDb.h"
 | 
			
		||||
#include "tunnel/Tunnel.h"
 | 
			
		||||
#include "transport/Transports.h"
 | 
			
		||||
#include "Garlic.h"
 | 
			
		||||
#include "I2NPProtocol.h"
 | 
			
		||||
 | 
			
		||||
using namespace i2p::transport;
 | 
			
		||||
 | 
			
		||||
namespace i2p
 | 
			
		||||
{
 | 
			
		||||
    I2NPMessage * NewI2NPMessage ()
 | 
			
		||||
    {
 | 
			
		||||
        return new I2NPMessageBuffer<I2NP_MAX_MESSAGE_SIZE>();
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    I2NPMessage * NewI2NPShortMessage ()
 | 
			
		||||
    {
 | 
			
		||||
        return new I2NPMessageBuffer<I2NP_MAX_SHORT_MESSAGE_SIZE>();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    I2NPMessage * NewI2NPMessage (size_t len)
 | 
			
		||||
    {
 | 
			
		||||
        return (len < I2NP_MAX_SHORT_MESSAGE_SIZE/2) ? NewI2NPShortMessage () : NewI2NPMessage ();
 | 
			
		||||
    }   
 | 
			
		||||
    
 | 
			
		||||
    void DeleteI2NPMessage (I2NPMessage * msg)
 | 
			
		||||
    {
 | 
			
		||||
        delete msg;
 | 
			
		||||
    }   
 | 
			
		||||
 | 
			
		||||
    std::shared_ptr<I2NPMessage> ToSharedI2NPMessage (I2NPMessage * msg)
 | 
			
		||||
    {
 | 
			
		||||
        return std::shared_ptr<I2NPMessage>(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);         
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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<I2NPMessage> CreateI2NPMessage (const uint8_t * buf, int len, std::shared_ptr<i2p::tunnel::InboundTunnel> 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<I2NPMessage> 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<I2NPMessage> CreateRouterInfoDatabaseLookupMsg (const uint8_t * key, const uint8_t * from, 
 | 
			
		||||
        uint32_t replyTunnelID, bool exploratory, std::set<i2p::data::IdentHash> * 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<I2NPMessage> CreateLeaseSetDatabaseLookupMsg (const i2p::data::IdentHash& dest, 
 | 
			
		||||
        const std::set<i2p::data::IdentHash>& 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;                   
 | 
			
		||||
    }           
 | 
			
		||||
 | 
			
		||||
    std::shared_ptr<I2NPMessage> CreateDatabaseSearchReply (const i2p::data::IdentHash& ident, 
 | 
			
		||||
         std::vector<i2p::data::IdentHash> 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<I2NPMessage> CreateDatabaseStoreMsg (std::shared_ptr<const i2p::data::RouterInfo> router, uint32_t replyToken)
 | 
			
		||||
    {
 | 
			
		||||
        if (!router) // we send own RouterInfo
 | 
			
		||||
            router = context.GetSharedRouterInfo ();
 | 
			
		||||
 | 
			
		||||
        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;
 | 
			
		||||
        }       
 | 
			
		||||
 | 
			
		||||
        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<I2NPMessage> CreateDatabaseStoreMsg (std::shared_ptr<const i2p::data::LeaseSet> 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");
 | 
			
		||||
 | 
			
		||||
        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 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 (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<I2NPMessage> 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<I2NPMessage> CreateTunnelGatewayMsg (uint32_t tunnelID, std::shared_ptr<I2NPMessage> 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;
 | 
			
		||||
    }   
 | 
			
		||||
 | 
			
		||||
    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);
 | 
			
		||||
        }   
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void HandleI2NPMessage (std::shared_ptr<I2NPMessage> 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<I2NPMessage>  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 ();
 | 
			
		||||
        }   
 | 
			
		||||
    }   
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										252
									
								
								core/I2NPProtocol.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										252
									
								
								core/I2NPProtocol.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,252 @@
 | 
			
		|||
#ifndef I2NP_PROTOCOL_H__
 | 
			
		||||
#define I2NP_PROTOCOL_H__
 | 
			
		||||
 | 
			
		||||
#include <inttypes.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <set>
 | 
			
		||||
#include <memory>
 | 
			
		||||
#include <cryptopp/sha.h>
 | 
			
		||||
#include "util/I2PEndian.h"
 | 
			
		||||
#include "Identity.h"
 | 
			
		||||
#include "RouterInfo.h"
 | 
			
		||||
#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 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;
 | 
			
		||||
 | 
			
		||||
    // 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;
 | 
			
		||||
 | 
			
		||||
    //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  
 | 
			
		||||
    };  
 | 
			
		||||
 | 
			
		||||
    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
 | 
			
		||||
 | 
			
		||||
    const int MAX_NUM_TRANSIT_TUNNELS = 2500;
 | 
			
		||||
 | 
			
		||||
namespace tunnel
 | 
			
		||||
{       
 | 
			
		||||
    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<i2p::tunnel::InboundTunnel> 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;
 | 
			
		||||
            maxLen = other.maxLen;
 | 
			
		||||
            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);
 | 
			
		||||
        }   
 | 
			
		||||
 | 
			
		||||
        void FillI2NPMessageHeader (I2NPMessageType msgType, uint32_t replyMsgID = 0);
 | 
			
		||||
        void RenewI2NPMessageHeader ();
 | 
			
		||||
    };  
 | 
			
		||||
 | 
			
		||||
    template<int sz>
 | 
			
		||||
    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<I2NPMessage> ToSharedI2NPMessage (I2NPMessage * msg);
 | 
			
		||||
    
 | 
			
		||||
    I2NPMessage * CreateI2NPMessage (I2NPMessageType msgType, const uint8_t * buf, int len, uint32_t replyMsgID = 0);   
 | 
			
		||||
    std::shared_ptr<I2NPMessage> CreateI2NPMessage (const uint8_t * buf, int len, std::shared_ptr<i2p::tunnel::InboundTunnel> from = nullptr);
 | 
			
		||||
    
 | 
			
		||||
    std::shared_ptr<I2NPMessage> CreateDeliveryStatusMsg (uint32_t msgID);
 | 
			
		||||
    std::shared_ptr<I2NPMessage> CreateRouterInfoDatabaseLookupMsg (const uint8_t * key, const uint8_t * from, 
 | 
			
		||||
        uint32_t replyTunnelID, bool exploratory = false, std::set<i2p::data::IdentHash> * excludedPeers = nullptr);
 | 
			
		||||
    std::shared_ptr<I2NPMessage> CreateLeaseSetDatabaseLookupMsg (const i2p::data::IdentHash& dest, 
 | 
			
		||||
        const std::set<i2p::data::IdentHash>& excludedFloodfills,
 | 
			
		||||
        const i2p::tunnel::InboundTunnel * replyTunnel, const uint8_t * replyKey, const uint8_t * replyTag);
 | 
			
		||||
    std::shared_ptr<I2NPMessage> CreateDatabaseSearchReply (const i2p::data::IdentHash& ident, std::vector<i2p::data::IdentHash> routers);
 | 
			
		||||
    
 | 
			
		||||
    std::shared_ptr<I2NPMessage> CreateDatabaseStoreMsg (std::shared_ptr<const i2p::data::RouterInfo> router = nullptr, uint32_t replyToken = 0);
 | 
			
		||||
    std::shared_ptr<I2NPMessage> CreateDatabaseStoreMsg (std::shared_ptr<const i2p::data::LeaseSet> 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<I2NPMessage> 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<I2NPMessage> CreateTunnelGatewayMsg (uint32_t tunnelID, std::shared_ptr<I2NPMessage> msg);
 | 
			
		||||
 | 
			
		||||
    size_t GetI2NPMessageLength (const uint8_t * msg);
 | 
			
		||||
    void HandleI2NPMessage (uint8_t * msg, size_t len);
 | 
			
		||||
    void HandleI2NPMessage (std::shared_ptr<I2NPMessage> msg);
 | 
			
		||||
 | 
			
		||||
    class I2NPMessagesHandler
 | 
			
		||||
    {
 | 
			
		||||
        public:
 | 
			
		||||
 | 
			
		||||
            ~I2NPMessagesHandler ();
 | 
			
		||||
            void PutNextMessage (std::shared_ptr<I2NPMessage> msg);
 | 
			
		||||
            void Flush ();
 | 
			
		||||
            
 | 
			
		||||
        private:
 | 
			
		||||
 | 
			
		||||
            std::vector<std::shared_ptr<I2NPMessage> > m_TunnelMsgs, m_TunnelGatewayMsgs;
 | 
			
		||||
    };
 | 
			
		||||
}   
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										566
									
								
								core/Identity.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										566
									
								
								core/Identity.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,566 @@
 | 
			
		|||
#include <time.h>
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <cryptopp/sha.h>
 | 
			
		||||
#include <cryptopp/osrng.h>
 | 
			
		||||
#include <cryptopp/dsa.h>
 | 
			
		||||
#include "util/base64.h"
 | 
			
		||||
#include "crypto/CryptoConst.h"
 | 
			
		||||
#include "crypto/ElGamal.h"
 | 
			
		||||
#include "RouterContext.h"
 | 
			
		||||
#include "Identity.h"
 | 
			
		||||
#include "util/I2PEndian.h"
 | 
			
		||||
 | 
			
		||||
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;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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)
 | 
			
		||||
    {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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::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;
 | 
			
		||||
 | 
			
		||||
        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 ();
 | 
			
		||||
    }   
 | 
			
		||||
 | 
			
		||||
    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 = i2p::util::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::util::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;
 | 
			
		||||
    }   
 | 
			
		||||
 | 
			
		||||
    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;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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;
 | 
			
		||||
    }   
 | 
			
		||||
 | 
			
		||||
    size_t PrivateKeys::FromBase64(const std::string& s)
 | 
			
		||||
    {
 | 
			
		||||
        uint8_t * buf = new uint8_t[s.length ()];
 | 
			
		||||
        size_t l = i2p::util::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::util::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::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;
 | 
			
		||||
    }   
 | 
			
		||||
 | 
			
		||||
    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);
 | 
			
		||||
#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;
 | 
			
		||||
    }   
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										275
									
								
								core/Identity.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										275
									
								
								core/Identity.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,275 @@
 | 
			
		|||
#ifndef IDENTITY_H__
 | 
			
		||||
#define IDENTITY_H__
 | 
			
		||||
 | 
			
		||||
#include <inttypes.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <memory>
 | 
			
		||||
#include "util/base64.h"
 | 
			
		||||
#include "crypto/ElGamal.h"
 | 
			
		||||
#include "crypto/Signature.h"
 | 
			
		||||
 | 
			
		||||
namespace i2p
 | 
			
		||||
{
 | 
			
		||||
namespace data
 | 
			
		||||
{
 | 
			
		||||
    template<int sz>
 | 
			
		||||
    class Tag
 | 
			
		||||
    {
 | 
			
		||||
        public:
 | 
			
		||||
 | 
			
		||||
            Tag (const uint8_t * buf) { memcpy (m_Buf, buf, sz); };
 | 
			
		||||
            Tag (const Tag<sz>& ) = default;
 | 
			
		||||
#ifndef _WIN32 // FIXME!!! msvs 2013 can't compile it
 | 
			
		||||
            Tag (Tag<sz>&& ) = default;
 | 
			
		||||
#endif
 | 
			
		||||
            Tag () = default;
 | 
			
		||||
            
 | 
			
		||||
            Tag<sz>& operator= (const Tag<sz>& ) = default;
 | 
			
		||||
#ifndef _WIN32
 | 
			
		||||
            Tag<sz>& operator= (Tag<sz>&& ) = default;
 | 
			
		||||
#endif
 | 
			
		||||
            
 | 
			
		||||
            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; };
 | 
			
		||||
 | 
			
		||||
            bool operator== (const Tag<sz>& other) const { return !memcmp (m_Buf, other.m_Buf, sz); };
 | 
			
		||||
            bool operator< (const Tag<sz>& 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::util::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::util::ByteStreamToBase32 (m_Buf, sz, str, sz*2);
 | 
			
		||||
                str[l] = 0;
 | 
			
		||||
                return std::string (str);
 | 
			
		||||
            }   
 | 
			
		||||
 | 
			
		||||
            void FromBase32 (const std::string& s)
 | 
			
		||||
            {
 | 
			
		||||
                i2p::util::Base32ToByteStream (s.c_str (), s.length (), m_Buf, sz);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            void FromBase64 (const std::string& s)
 | 
			
		||||
            {
 | 
			
		||||
                i2p::util::Base64ToByteStream (s.c_str (), s.length (), m_Buf, sz);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        private:
 | 
			
		||||
 | 
			
		||||
            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 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;
 | 
			
		||||
    };
 | 
			
		||||
#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:
 | 
			
		||||
 | 
			
		||||
            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         
 | 
			
		||||
 | 
			
		||||
        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;
 | 
			
		||||
 | 
			
		||||
            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;
 | 
			
		||||
 | 
			
		||||
            static PrivateKeys CreateRandomKeys (SigningKeyType type = SIGNING_KEY_TYPE_DSA_SHA1);
 | 
			
		||||
    
 | 
			
		||||
        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;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    // 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; };
 | 
			
		||||
    };  
 | 
			
		||||
 | 
			
		||||
    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 
 | 
			
		||||
 | 
			
		||||
            std::unique_ptr<const i2p::crypto::ElGamalEncryption>& GetElGamalEncryption () const
 | 
			
		||||
            {
 | 
			
		||||
                if (!m_ElGamalEncryption)
 | 
			
		||||
                    m_ElGamalEncryption.reset (new i2p::crypto::ElGamalEncryption (GetEncryptionPublicKey ()));
 | 
			
		||||
                return m_ElGamalEncryption;
 | 
			
		||||
            }
 | 
			
		||||
            
 | 
			
		||||
        private:
 | 
			
		||||
 | 
			
		||||
            mutable std::unique_ptr<const i2p::crypto::ElGamalEncryption> m_ElGamalEncryption; // use lazy initialization
 | 
			
		||||
    };  
 | 
			
		||||
 | 
			
		||||
    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; 
 | 
			
		||||
 | 
			
		||||
            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); 
 | 
			
		||||
            };
 | 
			
		||||
    };  
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										157
									
								
								core/LeaseSet.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										157
									
								
								core/LeaseSet.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,157 @@
 | 
			
		|||
#include <string.h>
 | 
			
		||||
#include "util/I2PEndian.h"
 | 
			
		||||
#include <cryptopp/dsa.h>
 | 
			
		||||
#include <cryptopp/osrng.h>
 | 
			
		||||
#include "crypto/CryptoConst.h"
 | 
			
		||||
#include "util/Log.h"
 | 
			
		||||
#include "util/Timestamp.h"
 | 
			
		||||
#include "NetDb.h"
 | 
			
		||||
#include "tunnel/TunnelPool.h"
 | 
			
		||||
#include "LeaseSet.h"
 | 
			
		||||
 | 
			
		||||
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 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 ();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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);
 | 
			
		||||
 | 
			
		||||
            // 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<Lease> LeaseSet::GetNonExpiredLeases (bool withThreshold) const
 | 
			
		||||
    {
 | 
			
		||||
        auto ts = i2p::util::GetMillisecondsSinceEpoch ();
 | 
			
		||||
        std::vector<Lease> 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::HasNonExpiredLeases () const
 | 
			
		||||
    {
 | 
			
		||||
        auto ts = i2p::util::GetMillisecondsSinceEpoch ();
 | 
			
		||||
        for (auto& it: m_Leases)
 | 
			
		||||
            if (ts < it.endDate) return true;
 | 
			
		||||
        return false;
 | 
			
		||||
    }   
 | 
			
		||||
}       
 | 
			
		||||
}   
 | 
			
		||||
							
								
								
									
										74
									
								
								core/LeaseSet.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										74
									
								
								core/LeaseSet.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,74 @@
 | 
			
		|||
#ifndef LEASE_SET_H__
 | 
			
		||||
#define LEASE_SET_H__
 | 
			
		||||
 | 
			
		||||
#include <inttypes.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <vector>
 | 
			
		||||
#include "Identity.h"
 | 
			
		||||
 | 
			
		||||
namespace i2p
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
namespace tunnel
 | 
			
		||||
{
 | 
			
		||||
    class TunnelPool;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
namespace data
 | 
			
		||||
{   
 | 
			
		||||
    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; 
 | 
			
		||||
        }   
 | 
			
		||||
    };  
 | 
			
		||||
 | 
			
		||||
    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; };          
 | 
			
		||||
 | 
			
		||||
            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<Lease>& GetLeases () const { return m_Leases; };
 | 
			
		||||
            const std::vector<Lease> 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:
 | 
			
		||||
 | 
			
		||||
            void ReadFromBuffer ();
 | 
			
		||||
            
 | 
			
		||||
        private:
 | 
			
		||||
 | 
			
		||||
            bool m_IsValid;
 | 
			
		||||
            std::vector<Lease> m_Leases;
 | 
			
		||||
            IdentityEx m_Identity;
 | 
			
		||||
            uint8_t m_EncryptionKey[256];
 | 
			
		||||
            uint8_t * m_Buffer;
 | 
			
		||||
            size_t m_BufferLen;
 | 
			
		||||
    };  
 | 
			
		||||
}       
 | 
			
		||||
}   
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										1007
									
								
								core/NetDb.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										1007
									
								
								core/NetDb.cpp
									
										
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										106
									
								
								core/NetDb.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										106
									
								
								core/NetDb.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,106 @@
 | 
			
		|||
#ifndef NETDB_H__
 | 
			
		||||
#define NETDB_H__
 | 
			
		||||
 | 
			
		||||
#include <inttypes.h>
 | 
			
		||||
#include <set>
 | 
			
		||||
#include <map>
 | 
			
		||||
#include <list>
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <thread>
 | 
			
		||||
#include <mutex>
 | 
			
		||||
#include <boost/filesystem.hpp>
 | 
			
		||||
#include "util/Queue.h"
 | 
			
		||||
#include "I2NPProtocol.h"
 | 
			
		||||
#include "RouterInfo.h"
 | 
			
		||||
#include "LeaseSet.h"
 | 
			
		||||
#include "tunnel/Tunnel.h"
 | 
			
		||||
#include "tunnel/TunnelPool.h"
 | 
			
		||||
#include "Reseed.h"
 | 
			
		||||
#include "NetDbRequests.h"
 | 
			
		||||
 | 
			
		||||
namespace i2p
 | 
			
		||||
{
 | 
			
		||||
namespace data
 | 
			
		||||
{       
 | 
			
		||||
    
 | 
			
		||||
    class NetDb
 | 
			
		||||
    {
 | 
			
		||||
        public:
 | 
			
		||||
 | 
			
		||||
            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<i2p::tunnel::InboundTunnel> from);
 | 
			
		||||
            std::shared_ptr<RouterInfo> FindRouter (const IdentHash& ident) const;
 | 
			
		||||
            std::shared_ptr<LeaseSet> FindLeaseSet (const IdentHash& destination) const;
 | 
			
		||||
 | 
			
		||||
            void RequestDestination (const IdentHash& destination, RequestedDestination::RequestComplete requestComplete = nullptr);            
 | 
			
		||||
            
 | 
			
		||||
            void HandleDatabaseStoreMsg (std::shared_ptr<const I2NPMessage> msg);
 | 
			
		||||
            void HandleDatabaseSearchReplyMsg (std::shared_ptr<const I2NPMessage> msg);
 | 
			
		||||
            void HandleDatabaseLookupMsg (std::shared_ptr<const I2NPMessage> msg);          
 | 
			
		||||
 | 
			
		||||
            std::shared_ptr<const RouterInfo> GetRandomRouter () const;
 | 
			
		||||
            std::shared_ptr<const RouterInfo> GetRandomRouter (std::shared_ptr<const RouterInfo> compatibleWith) const;
 | 
			
		||||
            std::shared_ptr<const RouterInfo> GetHighBandwidthRandomRouter (std::shared_ptr<const RouterInfo> compatibleWith) const;
 | 
			
		||||
            std::shared_ptr<const RouterInfo> GetRandomPeerTestRouter () const;
 | 
			
		||||
            std::shared_ptr<const RouterInfo> GetRandomIntroducer () const;
 | 
			
		||||
            std::shared_ptr<const RouterInfo> GetClosestFloodfill (const IdentHash& destination, const std::set<IdentHash>& excluded) const;
 | 
			
		||||
            std::vector<IdentHash> GetClosestFloodfills (const IdentHash& destination, size_t num,
 | 
			
		||||
                std::set<IdentHash>& excluded) const;
 | 
			
		||||
            std::shared_ptr<const RouterInfo> GetClosestNonFloodfill (const IdentHash& destination, const std::set<IdentHash>& excluded) const;
 | 
			
		||||
            void SetUnreachable (const IdentHash& ident, bool unreachable);         
 | 
			
		||||
 | 
			
		||||
            void PostI2NPMsg (std::shared_ptr<const I2NPMessage> msg);
 | 
			
		||||
 | 
			
		||||
            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:
 | 
			
		||||
 | 
			
		||||
            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<typename Filter>
 | 
			
		||||
            std::shared_ptr<const RouterInfo> GetRandomRouter (Filter filter) const;    
 | 
			
		||||
        
 | 
			
		||||
        private:
 | 
			
		||||
 | 
			
		||||
            std::map<IdentHash, std::shared_ptr<LeaseSet> > m_LeaseSets;
 | 
			
		||||
            mutable std::mutex m_RouterInfosMutex;
 | 
			
		||||
            std::map<IdentHash, std::shared_ptr<RouterInfo> > m_RouterInfos;
 | 
			
		||||
            mutable std::mutex m_FloodfillsMutex;
 | 
			
		||||
            std::list<std::shared_ptr<RouterInfo> > m_Floodfills;
 | 
			
		||||
            
 | 
			
		||||
            bool m_IsRunning;
 | 
			
		||||
            std::thread * m_Thread; 
 | 
			
		||||
            i2p::util::Queue<std::shared_ptr<const I2NPMessage> > m_Queue; // of I2NPDatabaseStoreMsg
 | 
			
		||||
 | 
			
		||||
            Reseeder * m_Reseeder;
 | 
			
		||||
 | 
			
		||||
            friend class NetDbRequests; 
 | 
			
		||||
            NetDbRequests m_Requests;
 | 
			
		||||
 | 
			
		||||
            static const char m_NetDbPath[];
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    extern NetDb netdb;
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										149
									
								
								core/NetDbRequests.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										149
									
								
								core/NetDbRequests.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,149 @@
 | 
			
		|||
#include "util/Log.h"
 | 
			
		||||
#include "I2NPProtocol.h"
 | 
			
		||||
#include "transport/Transports.h"
 | 
			
		||||
#include "NetDb.h"
 | 
			
		||||
#include "NetDbRequests.h"
 | 
			
		||||
 | 
			
		||||
namespace i2p
 | 
			
		||||
{
 | 
			
		||||
namespace data
 | 
			
		||||
{
 | 
			
		||||
    std::shared_ptr<I2NPMessage> RequestedDestination::CreateRequestMessage (std::shared_ptr<const RouterInfo> router,
 | 
			
		||||
        std::shared_ptr<const i2p::tunnel::InboundTunnel> 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<I2NPMessage> 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<RouterInfo> r)
 | 
			
		||||
    {
 | 
			
		||||
        if (m_RequestComplete)
 | 
			
		||||
        {
 | 
			
		||||
            m_RequestComplete (r);
 | 
			
		||||
            m_RequestComplete = nullptr;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void RequestedDestination::Fail ()
 | 
			
		||||
    {
 | 
			
		||||
        if (m_RequestComplete)
 | 
			
		||||
        {
 | 
			
		||||
            m_RequestComplete (nullptr);
 | 
			
		||||
            m_RequestComplete = nullptr;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void NetDbRequests::Start ()
 | 
			
		||||
    {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void NetDbRequests::Stop ()
 | 
			
		||||
    {
 | 
			
		||||
        m_RequestedDestinations.clear ();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    std::shared_ptr<RequestedDestination> NetDbRequests::CreateRequest (const IdentHash& destination, bool isExploratory, RequestedDestination::RequestComplete requestComplete)
 | 
			
		||||
    {
 | 
			
		||||
        // request RouterInfo directly
 | 
			
		||||
        auto dest = std::make_shared<RequestedDestination> (destination, isExploratory); 
 | 
			
		||||
        dest->SetRequestComplete (requestComplete);
 | 
			
		||||
        {
 | 
			
		||||
            std::unique_lock<std::mutex> l(m_RequestedDestinationsMutex);
 | 
			
		||||
            if (!m_RequestedDestinations.insert (std::make_pair (destination, 
 | 
			
		||||
                std::shared_ptr<RequestedDestination> (dest))).second) // not inserted
 | 
			
		||||
                return nullptr; 
 | 
			
		||||
        }
 | 
			
		||||
        return dest;
 | 
			
		||||
    }   
 | 
			
		||||
 | 
			
		||||
    void NetDbRequests::RequestComplete (const IdentHash& ident, std::shared_ptr<RouterInfo> 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<std::mutex> l(m_RequestedDestinationsMutex);
 | 
			
		||||
            m_RequestedDestinations.erase (it);
 | 
			
		||||
        }   
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    std::shared_ptr<RequestedDestination> 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<std::mutex> 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++;
 | 
			
		||||
        }   
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										69
									
								
								core/NetDbRequests.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								core/NetDbRequests.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,69 @@
 | 
			
		|||
#ifndef NETDB_REQUESTS_H__
 | 
			
		||||
#define NETDB_REQUESTS_H__
 | 
			
		||||
 | 
			
		||||
#include <memory>
 | 
			
		||||
#include <set>
 | 
			
		||||
#include <map>
 | 
			
		||||
#include "Identity.h"
 | 
			
		||||
#include "RouterInfo.h"
 | 
			
		||||
 | 
			
		||||
namespace i2p
 | 
			
		||||
{
 | 
			
		||||
namespace data
 | 
			
		||||
{
 | 
			
		||||
    class RequestedDestination
 | 
			
		||||
    {   
 | 
			
		||||
        public:
 | 
			
		||||
 | 
			
		||||
            typedef std::function<void (std::shared_ptr<RouterInfo>)> RequestComplete;
 | 
			
		||||
 | 
			
		||||
            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<IdentHash>& 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<I2NPMessage> CreateRequestMessage (std::shared_ptr<const RouterInfo>, std::shared_ptr<const i2p::tunnel::InboundTunnel> replyTunnel);
 | 
			
		||||
            std::shared_ptr<I2NPMessage> CreateRequestMessage (const IdentHash& floodfill);
 | 
			
		||||
            
 | 
			
		||||
            void SetRequestComplete (const RequestComplete& requestComplete) { m_RequestComplete = requestComplete; };
 | 
			
		||||
            bool IsRequestComplete () const { return m_RequestComplete != nullptr; };
 | 
			
		||||
            void Success (std::shared_ptr<RouterInfo> r);
 | 
			
		||||
            void Fail ();
 | 
			
		||||
            
 | 
			
		||||
        private:
 | 
			
		||||
 | 
			
		||||
            IdentHash m_Destination;
 | 
			
		||||
            bool m_IsExploratory;
 | 
			
		||||
            std::set<IdentHash> m_ExcludedPeers;
 | 
			
		||||
            uint64_t m_CreationTime;
 | 
			
		||||
            RequestComplete m_RequestComplete;
 | 
			
		||||
    };  
 | 
			
		||||
 | 
			
		||||
    class NetDbRequests
 | 
			
		||||
    {
 | 
			
		||||
        public:
 | 
			
		||||
 | 
			
		||||
            void Start ();
 | 
			
		||||
            void Stop ();
 | 
			
		||||
 | 
			
		||||
             std::shared_ptr<RequestedDestination> CreateRequest (const IdentHash& destination, bool isExploratory, RequestedDestination::RequestComplete requestComplete = nullptr);
 | 
			
		||||
            void RequestComplete (const IdentHash& ident, std::shared_ptr<RouterInfo> r);
 | 
			
		||||
            std::shared_ptr<RequestedDestination> FindRequest (const IdentHash& ident) const;
 | 
			
		||||
            void ManageRequests ();
 | 
			
		||||
 | 
			
		||||
        private:
 | 
			
		||||
 | 
			
		||||
            std::mutex m_RequestedDestinationsMutex;
 | 
			
		||||
            std::map<IdentHash, std::shared_ptr<RequestedDestination> > m_RequestedDestinations;
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										213
									
								
								core/Profiling.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										213
									
								
								core/Profiling.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,213 @@
 | 
			
		|||
#include <boost/filesystem.hpp>
 | 
			
		||||
#include <boost/property_tree/ptree.hpp>
 | 
			
		||||
#include <boost/property_tree/ini_parser.hpp>
 | 
			
		||||
#include "util/base64.h"
 | 
			
		||||
#include "util/util.h"
 | 
			
		||||
#include "Profiling.h"
 | 
			
		||||
 | 
			
		||||
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) 
 | 
			
		||||
    {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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 = i2p::util::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::TunnelNonReplied ()
 | 
			
		||||
    {
 | 
			
		||||
        m_NumTunnelsNonReplied++;
 | 
			
		||||
        UpdateTime ();
 | 
			
		||||
    }   
 | 
			
		||||
 | 
			
		||||
    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<RouterProfile> GetRouterProfile (const IdentHash& identHash)
 | 
			
		||||
    {
 | 
			
		||||
        auto profile = std::make_shared<RouterProfile> (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");
 | 
			
		||||
    }   
 | 
			
		||||
}       
 | 
			
		||||
}   
 | 
			
		||||
							
								
								
									
										69
									
								
								core/Profiling.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								core/Profiling.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,69 @@
 | 
			
		|||
#ifndef PROFILING_H__
 | 
			
		||||
#define PROFILING_H__
 | 
			
		||||
 | 
			
		||||
#include <memory>
 | 
			
		||||
#include <boost/date_time/posix_time/posix_time.hpp>
 | 
			
		||||
#include "Identity.h"
 | 
			
		||||
 | 
			
		||||
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 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 ();
 | 
			
		||||
 | 
			
		||||
            bool IsBad ();
 | 
			
		||||
            
 | 
			
		||||
            void TunnelBuildResponse (uint8_t ret);
 | 
			
		||||
            void TunnelNonReplied ();
 | 
			
		||||
 | 
			
		||||
        private:
 | 
			
		||||
 | 
			
		||||
            boost::posix_time::ptime GetTime () const;
 | 
			
		||||
            void UpdateTime ();
 | 
			
		||||
 | 
			
		||||
            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;    
 | 
			
		||||
    };  
 | 
			
		||||
 | 
			
		||||
    std::shared_ptr<RouterProfile> GetRouterProfile (const IdentHash& identHash); 
 | 
			
		||||
    void DeleteObsoleteProfiles ();
 | 
			
		||||
}       
 | 
			
		||||
}   
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										932
									
								
								core/Reseed.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										932
									
								
								core/Reseed.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,932 @@
 | 
			
		|||
#include <string.h>
 | 
			
		||||
#include <fstream>
 | 
			
		||||
#include <sstream>
 | 
			
		||||
#include <boost/regex.hpp>
 | 
			
		||||
#include <boost/filesystem.hpp>
 | 
			
		||||
#include <boost/lexical_cast.hpp>
 | 
			
		||||
#include <cryptopp/asn.h>
 | 
			
		||||
#include <cryptopp/base64.h>
 | 
			
		||||
#include <cryptopp/crc.h>
 | 
			
		||||
#include <cryptopp/hmac.h>
 | 
			
		||||
#include <cryptopp/zinflate.h>
 | 
			
		||||
#define CRYPTOPP_ENABLE_NAMESPACE_WEAK 1
 | 
			
		||||
#include <cryptopp/arc4.h>
 | 
			
		||||
#include "util/I2PEndian.h"
 | 
			
		||||
#include "Reseed.h"
 | 
			
		||||
#include "util/Log.h"
 | 
			
		||||
#include "Identity.h"
 | 
			
		||||
#include "crypto/CryptoConst.h"
 | 
			
		||||
#include "NetDb.h"
 | 
			
		||||
#include "util/util.h"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
namespace i2p
 | 
			
		||||
{
 | 
			
		||||
namespace data
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
    static std::vector<std::string> httpReseedHostList = {
 | 
			
		||||
                "http://netdb.i2p2.no/",            // only SU3 (v2) support
 | 
			
		||||
                "http://i2p-netdb.innovatio.no/",
 | 
			
		||||
                "http://193.150.121.66/netDb/"
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
    static std::vector<std::string> 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()
 | 
			
		||||
    {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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";
 | 
			
		||||
#else
 | 
			
		||||
                filename += "\\netDb\\r";
 | 
			
		||||
#endif
 | 
			
		||||
                filename += name.at(11); // first char in id
 | 
			
		||||
#ifndef _WIN32
 | 
			
		||||
                filename.append("/");
 | 
			
		||||
#else
 | 
			
		||||
                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;
 | 
			
		||||
    }   
 | 
			
		||||
 | 
			
		||||
    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;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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);  
 | 
			
		||||
 | 
			
		||||
                    // 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;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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);
 | 
			
		||||
 | 
			
		||||
            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<uint32_t>(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");
 | 
			
		||||
    }   
 | 
			
		||||
 | 
			
		||||
    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 "";
 | 
			
		||||
    }   
 | 
			
		||||
 | 
			
		||||
//-------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
    template<class Hash>
 | 
			
		||||
    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<Hash> 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   
 | 
			
		||||
    };  
 | 
			
		||||
 | 
			
		||||
    template<class Hash>
 | 
			
		||||
    class TlsCipher_AES_256_CBC: public TlsCipherMAC<Hash>
 | 
			
		||||
    {
 | 
			
		||||
        public:
 | 
			
		||||
 | 
			
		||||
            TlsCipher_AES_256_CBC (const uint8_t * keys): TlsCipherMAC<Hash> (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 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:
 | 
			
		||||
 | 
			
		||||
            CryptoPP::AutoSeededRandomPool m_Rnd;
 | 
			
		||||
            i2p::crypto::CBCEncryption m_Encryption;
 | 
			
		||||
            i2p::crypto::CBCDecryption m_Decryption; 
 | 
			
		||||
    };  
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    class TlsCipher_RC4_SHA: public TlsCipherMAC<CryptoPP::SHA1>
 | 
			
		||||
    {
 | 
			
		||||
        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
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            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;
 | 
			
		||||
    };  
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
    TlsSession::TlsSession (const std::string& host, int port):
 | 
			
		||||
        m_IsEstablished (false), m_Cipher (nullptr)
 | 
			
		||||
    {
 | 
			
		||||
        m_Site.connect(host, boost::lexical_cast<std::string>(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
 | 
			
		||||
        };  
 | 
			
		||||
 | 
			
		||||
        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<CryptoPP::SHA256> (keys);
 | 
			
		||||
        }
 | 
			
		||||
        else if (cipherSuite[1] == 0x35)
 | 
			
		||||
        {
 | 
			
		||||
            LogPrint (eLogInfo, "Chiper suite is RSA_WITH_AES_256_CBC_SHA"); 
 | 
			
		||||
            m_Cipher = new TlsCipher_AES_256_CBC<CryptoPP::SHA1> (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;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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
 | 
			
		||||
 | 
			
		||||
        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<CryptoPP::SHA256> 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);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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<uint32_t>(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;
 | 
			
		||||
    }       
 | 
			
		||||
 | 
			
		||||
    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;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										95
									
								
								core/Reseed.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										95
									
								
								core/Reseed.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,95 @@
 | 
			
		|||
#ifndef RESEED_H
 | 
			
		||||
#define RESEED_H
 | 
			
		||||
 | 
			
		||||
#include <iostream>
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <vector>
 | 
			
		||||
#include <map>
 | 
			
		||||
#include <cryptopp/osrng.h>
 | 
			
		||||
#include <cryptopp/rsa.h>
 | 
			
		||||
#include <boost/asio.hpp>
 | 
			
		||||
#include "Identity.h"
 | 
			
		||||
#include "crypto/aes.h"
 | 
			
		||||
 | 
			
		||||
namespace i2p
 | 
			
		||||
{
 | 
			
		||||
namespace data
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
    class Reseeder
 | 
			
		||||
    {
 | 
			
		||||
        typedef Tag<512> PublicKey; 
 | 
			
		||||
        
 | 
			
		||||
        public:
 | 
			
		||||
        
 | 
			
		||||
            Reseeder();
 | 
			
		||||
            ~Reseeder();
 | 
			
		||||
            bool reseedNow(); // depreacted
 | 
			
		||||
            int ReseedNowSU3 ();
 | 
			
		||||
 | 
			
		||||
            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); 
 | 
			
		||||
 | 
			
		||||
            bool FindZipDataDescriptor (std::istream& s);
 | 
			
		||||
            
 | 
			
		||||
            std::string HttpsRequest (const std::string& address);
 | 
			
		||||
 | 
			
		||||
        private:    
 | 
			
		||||
 | 
			
		||||
            std::map<std::string, PublicKey> m_SigningKeys;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    class TlsCipher
 | 
			
		||||
    {
 | 
			
		||||
        public:
 | 
			
		||||
 | 
			
		||||
            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
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    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:
 | 
			
		||||
 | 
			
		||||
            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);
 | 
			
		||||
 | 
			
		||||
        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;
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										318
									
								
								core/RouterContext.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										318
									
								
								core/RouterContext.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,318 @@
 | 
			
		|||
#include <fstream>
 | 
			
		||||
#include <cryptopp/dh.h>
 | 
			
		||||
#include <cryptopp/dsa.h>
 | 
			
		||||
#include <boost/lexical_cast.hpp>
 | 
			
		||||
#include "crypto/CryptoConst.h"
 | 
			
		||||
#include "RouterContext.h"
 | 
			
		||||
#include "util/Timestamp.h"
 | 
			
		||||
#include "I2NPProtocol.h"
 | 
			
		||||
#include "NetDb.h"
 | 
			
		||||
#include "util/util.h"
 | 
			
		||||
#include "version.h"
 | 
			
		||||
 | 
			
		||||
namespace i2p
 | 
			
		||||
{
 | 
			
		||||
    RouterContext context;
 | 
			
		||||
 | 
			
		||||
    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::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::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::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;
 | 
			
		||||
    }   
 | 
			
		||||
 | 
			
		||||
    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::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 ();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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::UpdateStats ()
 | 
			
		||||
    {
 | 
			
		||||
        if (m_IsFloodfill)
 | 
			
		||||
        {
 | 
			
		||||
            // update routers and leasesets
 | 
			
		||||
            m_RouterInfo.SetProperty (ROUTER_INFO_PROPERTY_LEASESETS, boost::lexical_cast<std::string>(i2p::data::netdb.GetNumLeaseSets ()));
 | 
			
		||||
            m_RouterInfo.SetProperty (ROUTER_INFO_PROPERTY_ROUTERS, boost::lexical_cast<std::string>(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);
 | 
			
		||||
 | 
			
		||||
        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));    
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    std::shared_ptr<i2p::tunnel::TunnelPool> RouterContext::GetTunnelPool () const
 | 
			
		||||
    {
 | 
			
		||||
        return i2p::tunnel::tunnels.GetExploratoryPool (); 
 | 
			
		||||
    }   
 | 
			
		||||
        
 | 
			
		||||
    void RouterContext::HandleI2NPMessage (const uint8_t * buf, size_t len, std::shared_ptr<i2p::tunnel::InboundTunnel> from)
 | 
			
		||||
    {
 | 
			
		||||
        i2p::HandleI2NPMessage (CreateI2NPMessage (buf, GetI2NPMessageLength (buf), from));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void RouterContext::ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg)
 | 
			
		||||
    {
 | 
			
		||||
        std::unique_lock<std::mutex> l(m_GarlicMutex);
 | 
			
		||||
        i2p::garlic::GarlicDestination::ProcessGarlicMessage (msg);
 | 
			
		||||
    }   
 | 
			
		||||
            
 | 
			
		||||
    void RouterContext::ProcessDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg)
 | 
			
		||||
    {
 | 
			
		||||
        std::unique_lock<std::mutex> l(m_GarlicMutex);
 | 
			
		||||
        i2p::garlic::GarlicDestination::ProcessDeliveryStatusMessage (msg);
 | 
			
		||||
    }   
 | 
			
		||||
        
 | 
			
		||||
    uint32_t RouterContext::GetUptime () const
 | 
			
		||||
    {
 | 
			
		||||
        return i2p::util::GetSecondsSinceEpoch () - m_StartupTime;
 | 
			
		||||
    }   
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										107
									
								
								core/RouterContext.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										107
									
								
								core/RouterContext.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,107 @@
 | 
			
		|||
#ifndef ROUTER_CONTEXT_H__
 | 
			
		||||
#define ROUTER_CONTEXT_H__
 | 
			
		||||
 | 
			
		||||
#include <inttypes.h>
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <memory>
 | 
			
		||||
#include <mutex>
 | 
			
		||||
#include <boost/asio.hpp>
 | 
			
		||||
#include <cryptopp/dsa.h>
 | 
			
		||||
#include <cryptopp/osrng.h>
 | 
			
		||||
#include "Identity.h"
 | 
			
		||||
#include "RouterInfo.h"
 | 
			
		||||
#include "Garlic.h"
 | 
			
		||||
 | 
			
		||||
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";       
 | 
			
		||||
 | 
			
		||||
    enum RouterStatus
 | 
			
		||||
    {
 | 
			
		||||
        eRouterStatusOK = 0,
 | 
			
		||||
        eRouterStatusTesting = 1,
 | 
			
		||||
        eRouterStatusFirewalled = 2
 | 
			
		||||
    };  
 | 
			
		||||
 | 
			
		||||
    class RouterContext: public i2p::garlic::GarlicDestination 
 | 
			
		||||
    {
 | 
			
		||||
        public:
 | 
			
		||||
 | 
			
		||||
            RouterContext ();
 | 
			
		||||
            void Init ();
 | 
			
		||||
 | 
			
		||||
            i2p::data::RouterInfo& GetRouterInfo () { return m_RouterInfo; };
 | 
			
		||||
            std::shared_ptr<const i2p::data::RouterInfo> GetSharedRouterInfo () const 
 | 
			
		||||
            { 
 | 
			
		||||
                return std::shared_ptr<const i2p::data::RouterInfo> (&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 ();        
 | 
			
		||||
 | 
			
		||||
            // 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<const i2p::data::LeaseSet> GetLeaseSet () { return nullptr; };
 | 
			
		||||
            std::shared_ptr<i2p::tunnel::TunnelPool> GetTunnelPool () const;
 | 
			
		||||
            void HandleI2NPMessage (const uint8_t * buf, size_t len, std::shared_ptr<i2p::tunnel::InboundTunnel> from);
 | 
			
		||||
 | 
			
		||||
            // override GarlicDestination
 | 
			
		||||
            void ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg);
 | 
			
		||||
            void ProcessDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg);   
 | 
			
		||||
            
 | 
			
		||||
        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;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    extern RouterContext context;
 | 
			
		||||
}   
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										666
									
								
								core/RouterInfo.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										666
									
								
								core/RouterInfo.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,666 @@
 | 
			
		|||
#include <stdio.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include "util/I2PEndian.h"
 | 
			
		||||
#include <fstream>
 | 
			
		||||
#include <boost/lexical_cast.hpp>
 | 
			
		||||
#include <cryptopp/sha.h>
 | 
			
		||||
#include <cryptopp/dsa.h>
 | 
			
		||||
#include "crypto/CryptoConst.h"
 | 
			
		||||
#include "util/base64.h"
 | 
			
		||||
#include "util/Timestamp.h"
 | 
			
		||||
#include "util/Log.h"
 | 
			
		||||
#include "RouterInfo.h"
 | 
			
		||||
#include "RouterContext.h"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
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 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;
 | 
			
		||||
    }   
 | 
			
		||||
 | 
			
		||||
    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<int>(value);
 | 
			
		||||
                else if (!strcmp (key, "mtu"))
 | 
			
		||||
                    address.mtu = boost::lexical_cast<int>(value);
 | 
			
		||||
                else if (!strcmp (key, "key"))
 | 
			
		||||
                    i2p::util::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<int>(value);
 | 
			
		||||
                    else if (!strcmp (key, "itag"))
 | 
			
		||||
                        introducer.iTag = boost::lexical_cast<uint32_t>(value);
 | 
			
		||||
                    else if (!strcmp (key, "ikey"))
 | 
			
		||||
                        i2p::util::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);
 | 
			
		||||
        }       
 | 
			
		||||
 | 
			
		||||
        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::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));
 | 
			
		||||
 | 
			
		||||
        // 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<std::string>(i), properties);
 | 
			
		||||
                        properties << '=';
 | 
			
		||||
                        WriteString (introducer.iHost.to_string (), properties);
 | 
			
		||||
                        properties << ';';
 | 
			
		||||
                        i++;
 | 
			
		||||
                    }   
 | 
			
		||||
                    i = 0;
 | 
			
		||||
                    for (auto introducer: address.introducers)
 | 
			
		||||
                    {
 | 
			
		||||
                        WriteString ("ikey" + boost::lexical_cast<std::string>(i), properties);
 | 
			
		||||
                        properties << '=';
 | 
			
		||||
                        char value[64];
 | 
			
		||||
                        size_t l = i2p::util::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<std::string>(i), properties);
 | 
			
		||||
                        properties << '=';
 | 
			
		||||
                        WriteString (boost::lexical_cast<std::string>(introducer.iPort), properties);
 | 
			
		||||
                        properties << ';';
 | 
			
		||||
                        i++;
 | 
			
		||||
                    }   
 | 
			
		||||
                    i = 0;
 | 
			
		||||
                    for (auto introducer: address.introducers)
 | 
			
		||||
                    {
 | 
			
		||||
                        WriteString ("itag" + boost::lexical_cast<std::string>(i), properties);
 | 
			
		||||
                        properties << '=';
 | 
			
		||||
                        WriteString (boost::lexical_cast<std::string>(introducer.iTag), properties);
 | 
			
		||||
                        properties << ';';
 | 
			
		||||
                        i++;
 | 
			
		||||
                    }   
 | 
			
		||||
                }   
 | 
			
		||||
                // write intro key
 | 
			
		||||
                WriteString ("key", properties);
 | 
			
		||||
                properties << '=';
 | 
			
		||||
                char value[64];
 | 
			
		||||
                size_t l = i2p::util::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<std::string>(address.mtu), properties);
 | 
			
		||||
                    properties << ';';
 | 
			
		||||
                }   
 | 
			
		||||
            }   
 | 
			
		||||
            WriteString ("port", properties);
 | 
			
		||||
            properties << '=';
 | 
			
		||||
            WriteString (boost::lexical_cast<std::string>(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));
 | 
			
		||||
 | 
			
		||||
        // 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; 
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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::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::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::RemoveIntroducer (const boost::asio::ip::udp::endpoint& e)
 | 
			
		||||
    {       
 | 
			
		||||
        for (auto& addr : m_Addresses)
 | 
			
		||||
        {
 | 
			
		||||
            if (addr.transportStyle == eTransportSSU && addr.host.is_v4 ())
 | 
			
		||||
            {   
 | 
			
		||||
                for (std::vector<Introducer>::iterator it = addr.introducers.begin (); it != addr.introducers.end (); it++)
 | 
			
		||||
                    if ( boost::asio::ip::udp::endpoint (it->iHost, it->iPort) == e) 
 | 
			
		||||
                    {
 | 
			
		||||
                        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::DeleteProperty (const std::string& key)
 | 
			
		||||
    {
 | 
			
		||||
        m_Properties.erase (key);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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::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);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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::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<RouterProfile> RouterInfo::GetProfile () const 
 | 
			
		||||
    {
 | 
			
		||||
        if (!m_Profile)
 | 
			
		||||
            m_Profile = GetRouterProfile (GetIdentHash ());
 | 
			
		||||
        return m_Profile;
 | 
			
		||||
    }   
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										183
									
								
								core/RouterInfo.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										183
									
								
								core/RouterInfo.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,183 @@
 | 
			
		|||
#ifndef ROUTER_INFO_H__
 | 
			
		||||
#define ROUTER_INFO_H__
 | 
			
		||||
 | 
			
		||||
#include <inttypes.h>
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <map>
 | 
			
		||||
#include <vector>
 | 
			
		||||
#include <iostream>
 | 
			
		||||
#include <boost/asio.hpp>
 | 
			
		||||
#include "Identity.h"
 | 
			
		||||
#include "Profiling.h"
 | 
			
		||||
 | 
			
		||||
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_SSU_TESTING = 'B';
 | 
			
		||||
    const char CAPS_FLAG_SSU_INTRODUCER = 'C';
 | 
			
		||||
 | 
			
		||||
    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 TransportStyle
 | 
			
		||||
            {
 | 
			
		||||
                eTransportUnknown = 0,
 | 
			
		||||
                eTransportNTCP,
 | 
			
		||||
                eTransportSSU
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            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<Introducer> 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) { };
 | 
			
		||||
 | 
			
		||||
            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<Address>& 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);
 | 
			
		||||
 | 
			
		||||
            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);
 | 
			
		||||
 | 
			
		||||
            bool IsUpdated () const { return m_IsUpdated; };
 | 
			
		||||
            void SetUpdated (bool updated) { m_IsUpdated = updated; }; 
 | 
			
		||||
            void SaveToFile (const std::string& fullPath);
 | 
			
		||||
 | 
			
		||||
            std::shared_ptr<RouterProfile> 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:
 | 
			
		||||
 | 
			
		||||
            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:
 | 
			
		||||
 | 
			
		||||
            std::string m_FullPath;
 | 
			
		||||
            IdentityEx m_RouterIdentity;
 | 
			
		||||
            uint8_t * m_Buffer;
 | 
			
		||||
            int m_BufferLen;
 | 
			
		||||
            uint64_t m_Timestamp;
 | 
			
		||||
            std::vector<Address> m_Addresses;
 | 
			
		||||
            std::map<std::string, std::string> m_Properties;
 | 
			
		||||
            bool m_IsUpdated, m_IsUnreachable;
 | 
			
		||||
            uint8_t m_SupportedTransports, m_Caps;
 | 
			
		||||
            mutable std::shared_ptr<RouterProfile> m_Profile;
 | 
			
		||||
    };  
 | 
			
		||||
}   
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										894
									
								
								core/Streaming.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										894
									
								
								core/Streaming.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,894 @@
 | 
			
		|||
#include <cryptopp/gzip.h>
 | 
			
		||||
#include "util/Log.h"
 | 
			
		||||
#include "RouterInfo.h"
 | 
			
		||||
#include "RouterContext.h"
 | 
			
		||||
#include "tunnel/Tunnel.h"
 | 
			
		||||
#include "util/Timestamp.h"
 | 
			
		||||
#include "Destination.h"
 | 
			
		||||
#include "Streaming.h"
 | 
			
		||||
 | 
			
		||||
namespace i2p
 | 
			
		||||
{
 | 
			
		||||
namespace stream
 | 
			
		||||
{
 | 
			
		||||
    Stream::Stream (boost::asio::io_service& service, StreamingDestination& local, 
 | 
			
		||||
        std::shared_ptr<const i2p::data::LeaseSet> 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 ()
 | 
			
		||||
    {   
 | 
			
		||||
        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 ();     
 | 
			
		||||
 | 
			
		||||
        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++);
 | 
			
		||||
 | 
			
		||||
                    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));
 | 
			
		||||
                }           
 | 
			
		||||
            }   
 | 
			
		||||
        }   
 | 
			
		||||
    }   
 | 
			
		||||
 | 
			
		||||
    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");
 | 
			
		||||
 | 
			
		||||
        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<uint8_t *>(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<uint8_t *>(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;
 | 
			
		||||
 | 
			
		||||
        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<std::mutex> 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::SendBuffer ()
 | 
			
		||||
    {   
 | 
			
		||||
        int numMsgs = m_WindowSize - m_SentPackets.size ();
 | 
			
		||||
        if (numMsgs <= 0) return; // window is full 
 | 
			
		||||
        
 | 
			
		||||
        bool isNoAck = m_LastReceivedSequenceNumber < 0; // first packet
 | 
			
		||||
        std::vector<Packet *> packets;
 | 
			
		||||
        {
 | 
			
		||||
            std::unique_lock<std::mutex> 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<Packet *> { &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::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 *> { 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<Packet *>& 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<i2p::tunnel::TunnelMessageBlock> 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;
 | 
			
		||||
            }   
 | 
			
		||||
 | 
			
		||||
            // collect packets to resend
 | 
			
		||||
            auto ts = i2p::util::GetMillisecondsSinceEpoch ();
 | 
			
		||||
            std::vector<Packet *> 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;
 | 
			
		||||
        }   
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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<I2NPMessage> 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<std::mutex> 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> StreamingDestination::CreateNewOutgoingStream (std::shared_ptr<const i2p::data::LeaseSet> remote, int port)
 | 
			
		||||
    {
 | 
			
		||||
        auto s = std::make_shared<Stream> (m_Owner.GetService (), *this, remote, port);
 | 
			
		||||
        std::unique_lock<std::mutex> l(m_StreamsMutex);
 | 
			
		||||
        m_Streams[s->GetRecvStreamID ()] = s;
 | 
			
		||||
        return s;
 | 
			
		||||
    }   
 | 
			
		||||
 | 
			
		||||
    std::shared_ptr<Stream> StreamingDestination::CreateNewIncomingStream ()
 | 
			
		||||
    {
 | 
			
		||||
        auto s = std::make_shared<Stream> (m_Owner.GetService (), *this);
 | 
			
		||||
        std::unique_lock<std::mutex> l(m_StreamsMutex);
 | 
			
		||||
        m_Streams[s->GetRecvStreamID ()] = s;
 | 
			
		||||
        return s;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void StreamingDestination::DeleteStream (std::shared_ptr<Stream> stream)
 | 
			
		||||
    {
 | 
			
		||||
        if (stream)
 | 
			
		||||
        {   
 | 
			
		||||
            std::unique_lock<std::mutex> 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;
 | 
			
		||||
        }   
 | 
			
		||||
    }
 | 
			
		||||
}       
 | 
			
		||||
}   
 | 
			
		||||
							
								
								
									
										271
									
								
								core/Streaming.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										271
									
								
								core/Streaming.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,271 @@
 | 
			
		|||
#ifndef STREAMING_H__
 | 
			
		||||
#define STREAMING_H__
 | 
			
		||||
 | 
			
		||||
#include <inttypes.h>
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <sstream>
 | 
			
		||||
#include <map>
 | 
			
		||||
#include <set>
 | 
			
		||||
#include <queue>
 | 
			
		||||
#include <functional>
 | 
			
		||||
#include <memory>
 | 
			
		||||
#include <mutex>
 | 
			
		||||
#include <boost/asio.hpp>
 | 
			
		||||
#include "util/I2PEndian.h"
 | 
			
		||||
#include "Identity.h"
 | 
			
		||||
#include "LeaseSet.h"
 | 
			
		||||
#include "I2NPProtocol.h"
 | 
			
		||||
#include "Garlic.h"
 | 
			
		||||
#include "tunnel/Tunnel.h"
 | 
			
		||||
 | 
			
		||||
namespace i2p
 | 
			
		||||
{
 | 
			
		||||
namespace client
 | 
			
		||||
{
 | 
			
		||||
    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 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 (); };
 | 
			
		||||
 | 
			
		||||
        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 (); 
 | 
			
		||||
        };
 | 
			
		||||
    };  
 | 
			
		||||
 | 
			
		||||
    enum StreamStatus
 | 
			
		||||
    {
 | 
			
		||||
        eStreamStatusNew = 0,
 | 
			
		||||
        eStreamStatusOpen,
 | 
			
		||||
        eStreamStatusReset,
 | 
			
		||||
        eStreamStatusClosing,
 | 
			
		||||
        eStreamStatusClosed
 | 
			
		||||
    };  
 | 
			
		||||
    
 | 
			
		||||
    class StreamingDestination;
 | 
			
		||||
    class Stream: public std::enable_shared_from_this<Stream>
 | 
			
		||||
    {   
 | 
			
		||||
        public:
 | 
			
		||||
 | 
			
		||||
            typedef std::function<void (const boost::system::error_code& ecode)> SendHandler;
 | 
			
		||||
 | 
			
		||||
            Stream (boost::asio::io_service& service, StreamingDestination& local, 
 | 
			
		||||
                std::shared_ptr<const i2p::data::LeaseSet> 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<const i2p::data::LeaseSet> 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<typename Buffer, typename ReceiveHandler>
 | 
			
		||||
            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:
 | 
			
		||||
 | 
			
		||||
            void Terminate ();
 | 
			
		||||
 | 
			
		||||
            void SendBuffer ();
 | 
			
		||||
            void SendQuickAck ();
 | 
			
		||||
            void SendClose ();
 | 
			
		||||
            bool SendPacket (Packet * packet);
 | 
			
		||||
            void SendPackets (const std::vector<Packet *>& packets);
 | 
			
		||||
 | 
			
		||||
            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<typename Buffer, typename ReceiveHandler>
 | 
			
		||||
            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<I2NPMessage> 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<const i2p::data::LeaseSet> m_RemoteLeaseSet;
 | 
			
		||||
            std::shared_ptr<i2p::garlic::GarlicRoutingSession> m_RoutingSession;
 | 
			
		||||
            i2p::data::Lease m_CurrentRemoteLease;
 | 
			
		||||
            std::shared_ptr<i2p::tunnel::OutboundTunnel> m_CurrentOutboundTunnel;
 | 
			
		||||
            std::queue<Packet *> m_ReceiveQueue;
 | 
			
		||||
            std::set<Packet *, PacketCmp> m_SavedPackets;
 | 
			
		||||
            std::set<Packet *, PacketCmp> 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;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    class StreamingDestination
 | 
			
		||||
    {
 | 
			
		||||
        public:
 | 
			
		||||
 | 
			
		||||
            typedef std::function<void (std::shared_ptr<Stream>)> Acceptor;
 | 
			
		||||
 | 
			
		||||
            StreamingDestination (i2p::client::ClientDestination& owner, uint16_t localPort = 0): 
 | 
			
		||||
                m_Owner (owner), m_LocalPort (localPort) {};
 | 
			
		||||
            ~StreamingDestination () {};    
 | 
			
		||||
 | 
			
		||||
            void Start ();
 | 
			
		||||
            void Stop ();
 | 
			
		||||
 | 
			
		||||
            std::shared_ptr<Stream> CreateNewOutgoingStream (std::shared_ptr<const i2p::data::LeaseSet> remote, int port = 0);
 | 
			
		||||
            void DeleteStream (std::shared_ptr<Stream> 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);
 | 
			
		||||
 | 
			
		||||
        private:        
 | 
			
		||||
    
 | 
			
		||||
            void HandleNextPacket (Packet * packet);
 | 
			
		||||
            std::shared_ptr<Stream> CreateNewIncomingStream ();
 | 
			
		||||
 | 
			
		||||
        private:
 | 
			
		||||
 | 
			
		||||
            i2p::client::ClientDestination& m_Owner;
 | 
			
		||||
            uint16_t m_LocalPort;
 | 
			
		||||
            std::mutex m_StreamsMutex;
 | 
			
		||||
            std::map<uint32_t, std::shared_ptr<Stream> > m_Streams;
 | 
			
		||||
            Acceptor m_Acceptor;
 | 
			
		||||
            
 | 
			
		||||
        public:
 | 
			
		||||
 | 
			
		||||
            // for HTTP only
 | 
			
		||||
            const decltype(m_Streams)& GetStreams () const { return m_Streams; };
 | 
			
		||||
    };      
 | 
			
		||||
 | 
			
		||||
//-------------------------------------------------
 | 
			
		||||
 | 
			
		||||
    template<typename Buffer, typename ReceiveHandler>
 | 
			
		||||
    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<typename Buffer, typename ReceiveHandler>
 | 
			
		||||
    void Stream::HandleReceiveTimer (const boost::system::error_code& ecode, const Buffer& buffer, ReceiveHandler handler)
 | 
			
		||||
    {
 | 
			
		||||
        size_t received = ConcatenatePackets (boost::asio::buffer_cast<uint8_t *>(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
 | 
			
		||||
							
								
								
									
										247
									
								
								core/UPnP.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										247
									
								
								core/UPnP.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,247 @@
 | 
			
		|||
#ifdef USE_UPNP
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <thread>
 | 
			
		||||
 | 
			
		||||
#include <boost/thread/thread.hpp>
 | 
			
		||||
#include <boost/asio.hpp>
 | 
			
		||||
#include <boost/bind.hpp>
 | 
			
		||||
 | 
			
		||||
#ifdef _WIN32
 | 
			
		||||
#include <windows.h>
 | 
			
		||||
#define dlsym GetProcAddress
 | 
			
		||||
#else
 | 
			
		||||
#include <dlfcn.h>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#include "util/Log.h"
 | 
			
		||||
 | 
			
		||||
#include "RouterContext.h"
 | 
			
		||||
#include "UPnP.h"
 | 
			
		||||
#include "NetDb.h"
 | 
			
		||||
#include "util/util.h"
 | 
			
		||||
 | 
			
		||||
#include <miniupnpc/miniupnpc.h>
 | 
			
		||||
#include <miniupnpc/upnpcommands.h>
 | 
			
		||||
 | 
			
		||||
// These are per-process and are safe to reuse for all threads
 | 
			
		||||
#ifndef UPNPDISCOVER_SUCCESS
 | 
			
		||||
/* miniupnpc 1.5 */
 | 
			
		||||
UPNPDev* (*upnpDiscoverFunc) (int, const char *, const char *, int);
 | 
			
		||||
int (*UPNP_AddPortMappingFunc) (const char *, const char *, const char *, const char *,
 | 
			
		||||
                                             const char *, const char *, const char *, const char *);
 | 
			
		||||
#else
 | 
			
		||||
/* miniupnpc 1.6 */
 | 
			
		||||
UPNPDev* (*upnpDiscoverFunc) (int, const char *, const char *, int, int, int *);
 | 
			
		||||
int (*UPNP_AddPortMappingFunc) (const char *, const char *, const char *, const char *,
 | 
			
		||||
                                             const char *, const char *, const char *, const char *, const char *);
 | 
			
		||||
#endif
 | 
			
		||||
int (*UPNP_GetValidIGDFunc) (struct UPNPDev *, struct UPNPUrls *, struct IGDdatas *, char *, int);
 | 
			
		||||
int (*UPNP_GetExternalIPAddressFunc) (const char *, const char *, char *);
 | 
			
		||||
int (*UPNP_DeletePortMappingFunc) (const char *, const char *, const char *, const char *, const char *);
 | 
			
		||||
void (*freeUPNPDevlistFunc) (struct UPNPDev *);
 | 
			
		||||
void (*FreeUPNPUrlsFunc) (struct UPNPUrls *);
 | 
			
		||||
 | 
			
		||||
// Nice approach http://stackoverflow.com/a/21517513/673826
 | 
			
		||||
template<class M, typename F>
 | 
			
		||||
F GetKnownProcAddressImpl(M hmod, const char *name, F) {
 | 
			
		||||
    auto proc = reinterpret_cast<F>(dlsym(hmod, name));
 | 
			
		||||
    if (!proc) {
 | 
			
		||||
        LogPrint("Error resolving ", name, " from UPNP library. This often happens if there is version mismatch!");
 | 
			
		||||
    }
 | 
			
		||||
    return proc;
 | 
			
		||||
}
 | 
			
		||||
#define GetKnownProcAddress(hmod, func) GetKnownProcAddressImpl(hmod, #func, func##Func);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
namespace i2p
 | 
			
		||||
{
 | 
			
		||||
namespace transport
 | 
			
		||||
{
 | 
			
		||||
    UPnP::UPnP () : m_Thread (nullptr) , m_IsModuleLoaded (false)
 | 
			
		||||
    {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void UPnP::Stop ()
 | 
			
		||||
    {
 | 
			
		||||
        if (m_Thread)
 | 
			
		||||
        {   
 | 
			
		||||
            m_Thread->join (); 
 | 
			
		||||
            delete m_Thread;
 | 
			
		||||
            m_Thread = nullptr;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void UPnP::Start()
 | 
			
		||||
    {
 | 
			
		||||
        if (!m_IsModuleLoaded) {
 | 
			
		||||
#ifdef MAC_OSX
 | 
			
		||||
            m_Module = dlopen ("libminiupnpc.dylib", RTLD_LAZY);
 | 
			
		||||
#elif _WIN32
 | 
			
		||||
            m_Module = LoadLibrary ("miniupnpc.dll"); // official prebuilt binary, e.g., in upnpc-exe-win32-20140422.zip
 | 
			
		||||
#else
 | 
			
		||||
            m_Module = dlopen ("libminiupnpc.so", RTLD_LAZY);
 | 
			
		||||
#endif
 | 
			
		||||
            if (m_Module == NULL)
 | 
			
		||||
            {
 | 
			
		||||
                LogPrint ("Error loading UPNP library. This often happens if there is version mismatch!");
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                upnpDiscoverFunc = GetKnownProcAddress (m_Module, upnpDiscover);
 | 
			
		||||
                UPNP_GetValidIGDFunc = GetKnownProcAddress (m_Module, UPNP_GetValidIGD);
 | 
			
		||||
                UPNP_GetExternalIPAddressFunc = GetKnownProcAddress (m_Module, UPNP_GetExternalIPAddress);
 | 
			
		||||
                UPNP_AddPortMappingFunc = GetKnownProcAddress (m_Module, UPNP_AddPortMapping);
 | 
			
		||||
                UPNP_DeletePortMappingFunc = GetKnownProcAddress (m_Module, UPNP_DeletePortMapping);
 | 
			
		||||
                freeUPNPDevlistFunc = GetKnownProcAddress (m_Module, freeUPNPDevlist);
 | 
			
		||||
                FreeUPNPUrlsFunc = GetKnownProcAddress (m_Module, FreeUPNPUrls);
 | 
			
		||||
                if (upnpDiscoverFunc && UPNP_GetValidIGDFunc && UPNP_GetExternalIPAddressFunc && UPNP_AddPortMappingFunc &&
 | 
			
		||||
                    UPNP_DeletePortMappingFunc && freeUPNPDevlistFunc && FreeUPNPUrlsFunc)
 | 
			
		||||
                    m_IsModuleLoaded = true;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        m_Thread = new std::thread (std::bind (&UPnP::Run, this));
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    UPnP::~UPnP ()
 | 
			
		||||
    {
 | 
			
		||||
    } 
 | 
			
		||||
 | 
			
		||||
    void UPnP::Run ()
 | 
			
		||||
    {
 | 
			
		||||
        for (auto& address : context.GetRouterInfo ().GetAddresses ())
 | 
			
		||||
        {
 | 
			
		||||
            if (!address.host.is_v6 ())
 | 
			
		||||
            {
 | 
			
		||||
                Discover ();
 | 
			
		||||
                if (address.transportStyle == data::RouterInfo::eTransportSSU )
 | 
			
		||||
                {
 | 
			
		||||
                    TryPortMapping (I2P_UPNP_UDP, address.port);
 | 
			
		||||
                }
 | 
			
		||||
                else if (address.transportStyle == data::RouterInfo::eTransportNTCP )
 | 
			
		||||
                {
 | 
			
		||||
                    TryPortMapping (I2P_UPNP_TCP, address.port);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    } 
 | 
			
		||||
        
 | 
			
		||||
    void UPnP::Discover ()
 | 
			
		||||
    {
 | 
			
		||||
#ifndef UPNPDISCOVER_SUCCESS
 | 
			
		||||
        /* miniupnpc 1.5 */
 | 
			
		||||
        m_Devlist = upnpDiscoverFunc (2000, m_MulticastIf, m_Minissdpdpath, 0);
 | 
			
		||||
#else
 | 
			
		||||
        /* miniupnpc 1.6 */
 | 
			
		||||
        int nerror = 0;
 | 
			
		||||
        m_Devlist = upnpDiscoverFunc (2000, m_MulticastIf, m_Minissdpdpath, 0, 0, &nerror);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
        int r;
 | 
			
		||||
        r = UPNP_GetValidIGDFunc (m_Devlist, &m_upnpUrls, &m_upnpData, m_NetworkAddr, sizeof (m_NetworkAddr));
 | 
			
		||||
        if (r == 1)
 | 
			
		||||
        {
 | 
			
		||||
            r = UPNP_GetExternalIPAddressFunc (m_upnpUrls.controlURL, m_upnpData.first.servicetype, m_externalIPAddress);
 | 
			
		||||
            if(r != UPNPCOMMAND_SUCCESS)
 | 
			
		||||
            {
 | 
			
		||||
                LogPrint ("UPnP: UPNP_GetExternalIPAddress () returned ", r);
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                if (m_externalIPAddress[0])
 | 
			
		||||
                {
 | 
			
		||||
                    LogPrint ("UPnP: ExternalIPAddress = ", m_externalIPAddress);
 | 
			
		||||
                    i2p::context.UpdateAddress (boost::asio::ip::address::from_string (m_externalIPAddress));
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
                else
 | 
			
		||||
                {
 | 
			
		||||
                    LogPrint ("UPnP: GetExternalIPAddress failed.");
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void UPnP::TryPortMapping (int type, int port)
 | 
			
		||||
    {
 | 
			
		||||
        std::string strType, strPort (std::to_string (port));
 | 
			
		||||
        switch (type)
 | 
			
		||||
        {
 | 
			
		||||
            case I2P_UPNP_TCP:
 | 
			
		||||
                strType = "TCP";
 | 
			
		||||
                break;
 | 
			
		||||
            case I2P_UPNP_UDP:
 | 
			
		||||
            default:
 | 
			
		||||
                strType = "UDP";
 | 
			
		||||
        }
 | 
			
		||||
        int r;
 | 
			
		||||
        std::string strDesc = "I2Pd";
 | 
			
		||||
        try {
 | 
			
		||||
            for (;;) {
 | 
			
		||||
#ifndef UPNPDISCOVER_SUCCESS
 | 
			
		||||
                /* miniupnpc 1.5 */
 | 
			
		||||
                r = UPNP_AddPortMappingFunc (m_upnpUrls.controlURL, m_upnpData.first.servicetype, strPort.c_str (), strPort.c_str (), m_NetworkAddr, strDesc.c_str (), strType.c_str (), 0);
 | 
			
		||||
#else
 | 
			
		||||
                /* miniupnpc 1.6 */
 | 
			
		||||
                r = UPNP_AddPortMappingFunc (m_upnpUrls.controlURL, m_upnpData.first.servicetype, strPort.c_str (), strPort.c_str (), m_NetworkAddr, strDesc.c_str (), strType.c_str (), 0, "0");
 | 
			
		||||
#endif
 | 
			
		||||
                if (r!=UPNPCOMMAND_SUCCESS)
 | 
			
		||||
                {
 | 
			
		||||
                    LogPrint ("AddPortMapping (", strPort.c_str () ,", ", strPort.c_str () ,", ", m_NetworkAddr, ") failed with code ", r);
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
                else
 | 
			
		||||
                {
 | 
			
		||||
                    LogPrint ("UPnP Port Mapping successful. (", m_NetworkAddr ,":", strPort.c_str(), " type ", strType.c_str () ," -> ", m_externalIPAddress ,":", strPort.c_str() ,")");
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
                std::this_thread::sleep_for(std::chrono::minutes(20)); // c++11
 | 
			
		||||
                //boost::this_thread::sleep_for(); // pre c++11
 | 
			
		||||
                //sleep(20*60); // non-portable
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        catch (boost::thread_interrupted)
 | 
			
		||||
        {
 | 
			
		||||
            CloseMapping(type, port);
 | 
			
		||||
            Close();
 | 
			
		||||
            throw;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void UPnP::CloseMapping (int type, int port)
 | 
			
		||||
    {
 | 
			
		||||
        std::string strType, strPort (std::to_string (port));
 | 
			
		||||
        switch (type)
 | 
			
		||||
        {
 | 
			
		||||
            case I2P_UPNP_TCP:
 | 
			
		||||
                strType = "TCP";
 | 
			
		||||
                break;
 | 
			
		||||
            case I2P_UPNP_UDP:
 | 
			
		||||
            default:
 | 
			
		||||
                strType = "UDP";
 | 
			
		||||
        }
 | 
			
		||||
        int r = 0;
 | 
			
		||||
        r = UPNP_DeletePortMappingFunc (m_upnpUrls.controlURL, m_upnpData.first.servicetype, strPort.c_str (), strType.c_str (), 0);
 | 
			
		||||
        LogPrint ("UPNP_DeletePortMapping() returned : ", r, "\n");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void UPnP::Close ()
 | 
			
		||||
    {
 | 
			
		||||
        freeUPNPDevlistFunc (m_Devlist);
 | 
			
		||||
        m_Devlist = 0;
 | 
			
		||||
        FreeUPNPUrlsFunc (&m_upnpUrls);
 | 
			
		||||
#ifndef _WIN32
 | 
			
		||||
        dlclose (m_Module);
 | 
			
		||||
#else
 | 
			
		||||
        FreeLibrary (m_Module);
 | 
			
		||||
#endif
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										63
									
								
								core/UPnP.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								core/UPnP.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,63 @@
 | 
			
		|||
#ifndef __UPNP_H__
 | 
			
		||||
#define __UPNP_H__
 | 
			
		||||
 | 
			
		||||
#ifdef USE_UPNP
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <thread>
 | 
			
		||||
 | 
			
		||||
#include <miniupnpc/miniwget.h>
 | 
			
		||||
#include <miniupnpc/miniupnpc.h>
 | 
			
		||||
#include <miniupnpc/upnpcommands.h>
 | 
			
		||||
#include <miniupnpc/upnperrors.h>
 | 
			
		||||
 | 
			
		||||
#include <boost/asio.hpp>
 | 
			
		||||
 | 
			
		||||
#include "util/util.h"
 | 
			
		||||
 | 
			
		||||
#define I2P_UPNP_TCP 1
 | 
			
		||||
#define I2P_UPNP_UDP 2
 | 
			
		||||
 | 
			
		||||
namespace i2p
 | 
			
		||||
{
 | 
			
		||||
namespace transport
 | 
			
		||||
{
 | 
			
		||||
    class UPnP
 | 
			
		||||
    {
 | 
			
		||||
    public:
 | 
			
		||||
 | 
			
		||||
        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 ();
 | 
			
		||||
 | 
			
		||||
        std::thread * m_Thread;
 | 
			
		||||
        struct UPNPUrls m_upnpUrls;
 | 
			
		||||
        struct IGDdatas m_upnpData;
 | 
			
		||||
 | 
			
		||||
        // For miniupnpc
 | 
			
		||||
        char * m_MulticastIf = 0;
 | 
			
		||||
        char * m_Minissdpdpath = 0;
 | 
			
		||||
        struct UPNPDev * m_Devlist = 0;
 | 
			
		||||
        char m_NetworkAddr[64];
 | 
			
		||||
        char m_externalIPAddress[40];
 | 
			
		||||
        bool m_IsModuleLoaded;
 | 
			
		||||
#ifndef _WIN32
 | 
			
		||||
        void *m_Module;
 | 
			
		||||
#else
 | 
			
		||||
        HINSTANCE m_Module;
 | 
			
		||||
#endif
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										112
									
								
								core/api.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										112
									
								
								core/api.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,112 @@
 | 
			
		|||
#include <string>
 | 
			
		||||
#include <map>
 | 
			
		||||
#include "util/Log.h"
 | 
			
		||||
#include "NetDb.h"
 | 
			
		||||
#include "transport/Transports.h"
 | 
			
		||||
#include "tunnel/Tunnel.h"
 | 
			
		||||
#include "RouterContext.h"
 | 
			
		||||
#include "Identity.h"
 | 
			
		||||
#include "Destination.h"
 | 
			
		||||
#include "util/util.h"
 | 
			
		||||
#include "api.h"
 | 
			
		||||
 | 
			
		||||
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 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 ();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    i2p::client::ClientDestination * CreateLocalDestination (const i2p::data::PrivateKeys& keys, bool isPublic,
 | 
			
		||||
        const std::map<std::string, std::string> * 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<std::string, std::string> * 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 RequestLeaseSet (i2p::client::ClientDestination * dest, const i2p::data::IdentHash& remote)
 | 
			
		||||
    {
 | 
			
		||||
        if (dest)
 | 
			
		||||
            dest->RequestDestination (remote);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    std::shared_ptr<i2p::stream::Stream> 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 DestroyStream (std::shared_ptr<i2p::stream::Stream> stream)
 | 
			
		||||
    {
 | 
			
		||||
        if (stream)
 | 
			
		||||
            stream->Close ();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										36
									
								
								core/api.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								core/api.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,36 @@
 | 
			
		|||
#ifndef API_H__
 | 
			
		||||
#define API_H__
 | 
			
		||||
 | 
			
		||||
#include <memory>
 | 
			
		||||
#include <iostream>
 | 
			
		||||
#include "Identity.h"
 | 
			
		||||
#include "Destination.h"
 | 
			
		||||
#include "Streaming.h"
 | 
			
		||||
 | 
			
		||||
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 <appName>.log in application's folder
 | 
			
		||||
    void StopI2P ();
 | 
			
		||||
 | 
			
		||||
    // destinations
 | 
			
		||||
    i2p::client::ClientDestination * CreateLocalDestination (const i2p::data::PrivateKeys& keys, bool isPublic = true,
 | 
			
		||||
        const std::map<std::string, std::string> * params = nullptr); 
 | 
			
		||||
    i2p::client::ClientDestination * CreateLocalDestination (bool isPublic = false, i2p::data::SigningKeyType sigType = i2p::data::SIGNING_KEY_TYPE_ECDSA_SHA256_P256, 
 | 
			
		||||
        const std::map<std::string, std::string> * 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<i2p::stream::Stream> 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<i2p::stream::Stream> stream);
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										66
									
								
								core/crypto/AESNIMacros.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										66
									
								
								core/crypto/AESNIMacros.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,66 @@
 | 
			
		|||
#ifndef AESNIMACROS_H__
 | 
			
		||||
#define AESNIMACROS_H__
 | 
			
		||||
 | 
			
		||||
#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 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"
 | 
			
		||||
 | 
			
		||||
#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"
 | 
			
		||||
 | 
			
		||||
#define CallAESIMC(offset) \
 | 
			
		||||
    "movaps "#offset"(%[shed]), %%xmm0 \n"  \
 | 
			
		||||
    "aesimc %%xmm0, %%xmm0 \n" \
 | 
			
		||||
    "movaps %%xmm0, "#offset"(%[shed]) \n" 
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										73
									
								
								core/crypto/CryptoConst.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										73
									
								
								core/crypto/CryptoConst.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,73 @@
 | 
			
		|||
#include <inttypes.h>
 | 
			
		||||
#include "CryptoConst.h"
 | 
			
		||||
 | 
			
		||||
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 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 CryptoConstants& GetCryptoConstants ()
 | 
			
		||||
    {
 | 
			
		||||
        static CryptoConstants cryptoConstants = 
 | 
			
		||||
        {
 | 
			
		||||
            {elgp_, 256},   // elgp
 | 
			
		||||
            {2},            // elgg 
 | 
			
		||||
            {dsap_, 128},   // dsap
 | 
			
		||||
            {dsaq_, 20},    // dsaq
 | 
			
		||||
            {dsag_, 128}    // dsag
 | 
			
		||||
        };  
 | 
			
		||||
        return cryptoConstants;
 | 
			
		||||
    }   
 | 
			
		||||
    
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										38
									
								
								core/crypto/CryptoConst.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								core/crypto/CryptoConst.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,38 @@
 | 
			
		|||
#ifndef CRYPTO_CONST_H__
 | 
			
		||||
#define CRYPTO_CONST_H__
 | 
			
		||||
 | 
			
		||||
#include <cryptopp/integer.h>
 | 
			
		||||
 | 
			
		||||
namespace i2p
 | 
			
		||||
{
 | 
			
		||||
namespace crypto
 | 
			
		||||
{
 | 
			
		||||
    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
 | 
			
		||||
    #define dsap GetCryptoConstants ().dsap 
 | 
			
		||||
    #define dsaq GetCryptoConstants ().dsaq
 | 
			
		||||
    #define dsag GetCryptoConstants ().dsag 
 | 
			
		||||
 | 
			
		||||
    // RSA
 | 
			
		||||
    const int rsae = 65537; 
 | 
			
		||||
}       
 | 
			
		||||
}   
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										87
									
								
								core/crypto/ElGamal.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										87
									
								
								core/crypto/ElGamal.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,87 @@
 | 
			
		|||
#ifndef EL_GAMAL_H__
 | 
			
		||||
#define EL_GAMAL_H__
 | 
			
		||||
 | 
			
		||||
#include <inttypes.h>
 | 
			
		||||
#include <cryptopp/integer.h>
 | 
			
		||||
#include <cryptopp/osrng.h>
 | 
			
		||||
#include <cryptopp/dh.h>
 | 
			
		||||
#include <cryptopp/sha.h>
 | 
			
		||||
#include "CryptoConst.h"
 | 
			
		||||
#include "util/Log.h"
 | 
			
		||||
 | 
			
		||||
namespace i2p
 | 
			
		||||
{
 | 
			
		||||
namespace crypto
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
    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);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            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);
 | 
			
		||||
                }   
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        private:
 | 
			
		||||
 | 
			
		||||
            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 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      
 | 
			
		||||
    }   
 | 
			
		||||
}
 | 
			
		||||
}   
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										121
									
								
								core/crypto/Signature.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										121
									
								
								core/crypto/Signature.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,121 @@
 | 
			
		|||
#include <memory>
 | 
			
		||||
#include <cryptopp/integer.h>
 | 
			
		||||
#include <cryptopp/eccrypto.h>
 | 
			
		||||
#include "util/Log.h"
 | 
			
		||||
#include "Signature.h"
 | 
			
		||||
 | 
			
		||||
namespace i2p
 | 
			
		||||
{
 | 
			
		||||
namespace crypto
 | 
			
		||||
{
 | 
			
		||||
    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));
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            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));
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        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 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 ();
 | 
			
		||||
            }   
 | 
			
		||||
 | 
			
		||||
            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;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        private:
 | 
			
		||||
 | 
			
		||||
            CryptoPP::Integer q, l, d, I; 
 | 
			
		||||
            CryptoPP::ECP::Point B; // base point
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    static std::unique_ptr<Ed25519> g_Ed25519;
 | 
			
		||||
    std::unique_ptr<Ed25519>& GetEd25519 ()
 | 
			
		||||
    {
 | 
			
		||||
        if (!g_Ed25519)
 | 
			
		||||
            g_Ed25519.reset (new Ed25519 ());
 | 
			
		||||
        return g_Ed25519; 
 | 
			
		||||
    }       
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    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:   
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void EDDSA25519Signer::Sign (CryptoPP::RandomNumberGenerator& rnd, const uint8_t * buf, int len, uint8_t * signature) const
 | 
			
		||||
    {
 | 
			
		||||
        // TODO
 | 
			
		||||
    }   
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										445
									
								
								core/crypto/Signature.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										445
									
								
								core/crypto/Signature.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,445 @@
 | 
			
		|||
#ifndef SIGNATURE_H__
 | 
			
		||||
#define SIGNATURE_H__
 | 
			
		||||
 | 
			
		||||
#include <inttypes.h>
 | 
			
		||||
#include <cryptopp/dsa.h>
 | 
			
		||||
#include <cryptopp/rsa.h>
 | 
			
		||||
#include <cryptopp/asn.h>
 | 
			
		||||
#include <cryptopp/oids.h>
 | 
			
		||||
#include <cryptopp/osrng.h>
 | 
			
		||||
#include <cryptopp/eccrypto.h>
 | 
			
		||||
#include "CryptoConst.h"
 | 
			
		||||
 | 
			
		||||
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 Signer
 | 
			
		||||
    {
 | 
			
		||||
        public:
 | 
			
		||||
 | 
			
		||||
            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:
 | 
			
		||||
 | 
			
		||||
            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:
 | 
			
		||||
 | 
			
		||||
            CryptoPP::DSA::PublicKey m_PublicKey;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    class DSASigner: public Signer
 | 
			
		||||
    {
 | 
			
		||||
        public:
 | 
			
		||||
 | 
			
		||||
            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);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        private:
 | 
			
		||||
 | 
			
		||||
            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);
 | 
			
		||||
    }   
 | 
			
		||||
 | 
			
		||||
    template<typename Hash, size_t keyLen>
 | 
			
		||||
    class ECDSAVerifier: public Verifier
 | 
			
		||||
    {       
 | 
			
		||||
        public:
 | 
			
		||||
 | 
			
		||||
            template<typename Curve>
 | 
			
		||||
            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<CryptoPP::ECP, Hash>::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:
 | 
			
		||||
 | 
			
		||||
            typename CryptoPP::ECDSA<CryptoPP::ECP, Hash>::PublicKey m_PublicKey;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    template<typename Hash>
 | 
			
		||||
    class ECDSASigner: public Signer
 | 
			
		||||
    {
 | 
			
		||||
        public:
 | 
			
		||||
 | 
			
		||||
            template<typename Curve>
 | 
			
		||||
            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<CryptoPP::ECP, Hash>::Signer signer (m_PrivateKey);
 | 
			
		||||
                signer.SignMessage (rnd, buf, len, signature);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        private:
 | 
			
		||||
 | 
			
		||||
            typename CryptoPP::ECDSA<CryptoPP::ECP, Hash>::PrivateKey m_PrivateKey;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    template<typename Hash, typename Curve>
 | 
			
		||||
    inline void CreateECDSARandomKeys (CryptoPP::RandomNumberGenerator& rnd, Curve curve, 
 | 
			
		||||
        size_t keyLen, uint8_t * signingPrivateKey, uint8_t * signingPublicKey)
 | 
			
		||||
    {
 | 
			
		||||
        typename CryptoPP::ECDSA<CryptoPP::ECP, Hash>::PrivateKey privateKey;
 | 
			
		||||
        typename CryptoPP::ECDSA<CryptoPP::ECP, Hash>::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<CryptoPP::SHA256, ECDSAP256_KEY_LENGTH>
 | 
			
		||||
    {
 | 
			
		||||
        public:
 | 
			
		||||
 | 
			
		||||
            ECDSAP256Verifier (const uint8_t * signingKey): 
 | 
			
		||||
                ECDSAVerifier (CryptoPP::ASN1::secp256r1(), signingKey)
 | 
			
		||||
            {
 | 
			
		||||
            }           
 | 
			
		||||
    };  
 | 
			
		||||
 | 
			
		||||
    class ECDSAP256Signer: public ECDSASigner<CryptoPP::SHA256>
 | 
			
		||||
    {
 | 
			
		||||
        public:
 | 
			
		||||
 | 
			
		||||
            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<CryptoPP::SHA256> (rnd, CryptoPP::ASN1::secp256r1(), ECDSAP256_KEY_LENGTH, signingPrivateKey, signingPublicKey);
 | 
			
		||||
    }   
 | 
			
		||||
 | 
			
		||||
// ECDSA_SHA384_P384
 | 
			
		||||
    const size_t ECDSAP384_KEY_LENGTH = 96;
 | 
			
		||||
    class ECDSAP384Verifier: public ECDSAVerifier<CryptoPP::SHA384, ECDSAP384_KEY_LENGTH>
 | 
			
		||||
    {
 | 
			
		||||
        public:
 | 
			
		||||
 | 
			
		||||
            ECDSAP384Verifier (const uint8_t * signingKey): 
 | 
			
		||||
                ECDSAVerifier (CryptoPP::ASN1::secp384r1(), signingKey)
 | 
			
		||||
            {
 | 
			
		||||
            }           
 | 
			
		||||
    };  
 | 
			
		||||
 | 
			
		||||
    class ECDSAP384Signer: public ECDSASigner<CryptoPP::SHA384>
 | 
			
		||||
    {
 | 
			
		||||
        public:
 | 
			
		||||
 | 
			
		||||
            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<CryptoPP::SHA384> (rnd, CryptoPP::ASN1::secp384r1(), ECDSAP384_KEY_LENGTH, signingPrivateKey, signingPublicKey);
 | 
			
		||||
    }   
 | 
			
		||||
 | 
			
		||||
// ECDSA_SHA512_P521
 | 
			
		||||
    const size_t ECDSAP521_KEY_LENGTH = 132;
 | 
			
		||||
    class ECDSAP521Verifier: public ECDSAVerifier<CryptoPP::SHA512, ECDSAP521_KEY_LENGTH>
 | 
			
		||||
    {
 | 
			
		||||
        public:
 | 
			
		||||
 | 
			
		||||
            ECDSAP521Verifier (const uint8_t * signingKey): 
 | 
			
		||||
                ECDSAVerifier (CryptoPP::ASN1::secp521r1(), signingKey)
 | 
			
		||||
            {
 | 
			
		||||
            }           
 | 
			
		||||
    };  
 | 
			
		||||
 | 
			
		||||
    class ECDSAP521Signer: public ECDSASigner<CryptoPP::SHA512>
 | 
			
		||||
    {
 | 
			
		||||
        public:
 | 
			
		||||
 | 
			
		||||
            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<CryptoPP::SHA512> (rnd, CryptoPP::ASN1::secp521r1(), ECDSAP521_KEY_LENGTH, signingPrivateKey, signingPublicKey);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
// RSA
 | 
			
		||||
    template<typename Hash, size_t keyLen>  
 | 
			
		||||
    class RSAVerifier: public Verifier
 | 
			
		||||
    {
 | 
			
		||||
        public:
 | 
			
		||||
 | 
			
		||||
            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<CryptoPP::PKCS1v15, Hash>::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;           
 | 
			
		||||
    };  
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
    template<typename Hash>
 | 
			
		||||
    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));
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            void Sign (CryptoPP::RandomNumberGenerator& rnd, const uint8_t * buf, int len, uint8_t * signature) const
 | 
			
		||||
            {
 | 
			
		||||
                typename CryptoPP::RSASS<CryptoPP::PKCS1v15, Hash>::Signer signer (m_PrivateKey);
 | 
			
		||||
                signer.SignMessage (rnd, buf, len, signature);
 | 
			
		||||
            }
 | 
			
		||||
            
 | 
			
		||||
        private:
 | 
			
		||||
 | 
			
		||||
            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);
 | 
			
		||||
    }   
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
//  RSA_SHA256_2048
 | 
			
		||||
    const size_t RSASHA2562048_KEY_LENGTH = 256;
 | 
			
		||||
    class RSASHA2562048Verifier: public RSAVerifier<CryptoPP::SHA256, RSASHA2562048_KEY_LENGTH> 
 | 
			
		||||
    {
 | 
			
		||||
        public:
 | 
			
		||||
 | 
			
		||||
            RSASHA2562048Verifier (const uint8_t * signingKey): RSAVerifier (signingKey) 
 | 
			
		||||
            {
 | 
			
		||||
            }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    class RSASHA2562048Signer: public RSASigner<CryptoPP::SHA256> 
 | 
			
		||||
    {
 | 
			
		||||
        public:
 | 
			
		||||
 | 
			
		||||
            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<CryptoPP::SHA384, RSASHA3843072_KEY_LENGTH> 
 | 
			
		||||
    {
 | 
			
		||||
        public:
 | 
			
		||||
 | 
			
		||||
            RSASHA3843072Verifier (const uint8_t * signingKey): RSAVerifier (signingKey) 
 | 
			
		||||
            {
 | 
			
		||||
            }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    class RSASHA3843072Signer: public RSASigner<CryptoPP::SHA384> 
 | 
			
		||||
    {
 | 
			
		||||
        public:
 | 
			
		||||
 | 
			
		||||
            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<CryptoPP::SHA512, RSASHA5124096_KEY_LENGTH> 
 | 
			
		||||
    {
 | 
			
		||||
        public:
 | 
			
		||||
 | 
			
		||||
            RSASHA5124096Verifier (const uint8_t * signingKey): RSAVerifier (signingKey) 
 | 
			
		||||
            {
 | 
			
		||||
            }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    class RSASHA5124096Signer: public RSASigner<CryptoPP::SHA512> 
 | 
			
		||||
    {
 | 
			
		||||
        public:
 | 
			
		||||
 | 
			
		||||
            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;
 | 
			
		||||
    };      
 | 
			
		||||
 | 
			
		||||
    template<typename Hash, size_t keyLen>
 | 
			
		||||
    class RSARawVerifier: public RawVerifier
 | 
			
		||||
    {
 | 
			
		||||
        public:
 | 
			
		||||
 | 
			
		||||
            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);
 | 
			
		||||
 | 
			
		||||
                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:
 | 
			
		||||
 | 
			
		||||
            CryptoPP::Integer n; // RSA modulus 
 | 
			
		||||
            Hash m_Hash;
 | 
			
		||||
    };  
 | 
			
		||||
 | 
			
		||||
    class RSASHA5124096RawVerifier: public RSARawVerifier<CryptoPP::SHA512, RSASHA5124096_KEY_LENGTH> 
 | 
			
		||||
    {
 | 
			
		||||
        public:
 | 
			
		||||
 | 
			
		||||
            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:
 | 
			
		||||
 | 
			
		||||
            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; };
 | 
			
		||||
 | 
			
		||||
        private:
 | 
			
		||||
    
 | 
			
		||||
            CryptoPP::ECP::Point m_PublicKey;   
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    class EDDSA25519Signer: public Signer
 | 
			
		||||
    {
 | 
			
		||||
        public:
 | 
			
		||||
 | 
			
		||||
            EDDSA25519Signer (const uint8_t * signingPrivateKey) {}; 
 | 
			
		||||
 | 
			
		||||
            void Sign (CryptoPP::RandomNumberGenerator& rnd, const uint8_t * buf, int len, uint8_t * signature) const; 
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										222
									
								
								core/crypto/aes.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										222
									
								
								core/crypto/aes.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,222 @@
 | 
			
		|||
#include <stdlib.h>
 | 
			
		||||
#include "aes.h"
 | 
			
		||||
 | 
			
		||||
namespace i2p {
 | 
			
		||||
namespace crypto {
 | 
			
		||||
 | 
			
		||||
#ifdef AESNI
 | 
			
		||||
#include "AESNIMacros.h" 
 | 
			
		||||
 | 
			
		||||
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 ECBEncryptionAESNI::Encrypt (const CipherBlock * in, CipherBlock * out)
 | 
			
		||||
{
 | 
			
		||||
    __asm__
 | 
			
		||||
    (
 | 
			
		||||
        "movups (%[in]), %%xmm0 \n"
 | 
			
		||||
        EncryptAES256(sched)
 | 
			
		||||
        "movups %%xmm0, (%[out]) \n"    
 | 
			
		||||
        : : [sched]"r"(GetKeySchedule ()), [in]"r"(in), [out]"r"(out) : "%xmm0", "memory"
 | 
			
		||||
    );
 | 
			
		||||
}       
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void ECBDecryptionAESNI::Decrypt (const CipherBlock * in, CipherBlock * out)
 | 
			
		||||
{
 | 
			
		||||
    __asm__
 | 
			
		||||
    (
 | 
			
		||||
        "movups (%[in]), %%xmm0 \n"
 | 
			
		||||
        DecryptAES256(sched)
 | 
			
		||||
        "movups %%xmm0, (%[out]) \n"    
 | 
			
		||||
        : : [sched]"r"(GetKeySchedule ()), [in]"r"(in), [out]"r"(out) : "%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      
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void CBCEncryption::Encrypt (int numBlocks, const CipherBlock * in, CipherBlock * 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      
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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 CipherBlock *)in, (CipherBlock *)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"
 | 
			
		||||
    );      
 | 
			
		||||
#else
 | 
			
		||||
    Encrypt (1, (const CipherBlock *)in, (CipherBlock *)out); 
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void CBCDecryption::Decrypt (int numBlocks, const CipherBlock * in, CipherBlock * 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"
 | 
			
		||||
    ); 
 | 
			
		||||
#else
 | 
			
		||||
    for (int i = 0; i < numBlocks; i++)
 | 
			
		||||
    {
 | 
			
		||||
        CipherBlock 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 CipherBlock *)in, (CipherBlock *)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"
 | 
			
		||||
    );
 | 
			
		||||
#else
 | 
			
		||||
    Decrypt (1, (const CipherBlock *)in, (CipherBlock *)out); 
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // crypto
 | 
			
		||||
} // i2p
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										194
									
								
								core/crypto/aes.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										194
									
								
								core/crypto/aes.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,194 @@
 | 
			
		|||
#ifndef AES_H__
 | 
			
		||||
#define AES_H__
 | 
			
		||||
 | 
			
		||||
#include <inttypes.h>
 | 
			
		||||
#include <cryptopp/modes.h>
 | 
			
		||||
#include <cryptopp/aes.h>
 | 
			
		||||
#include "Identity.h"
 | 
			
		||||
 | 
			
		||||
namespace i2p
 | 
			
		||||
{
 | 
			
		||||
namespace crypto
 | 
			
		||||
{   
 | 
			
		||||
    struct CipherBlock 
 | 
			
		||||
    {
 | 
			
		||||
        uint8_t buf[16];
 | 
			
		||||
 | 
			
		||||
        void operator^=(const CipherBlock& 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"
 | 
			
		||||
            );          
 | 
			
		||||
#else
 | 
			
		||||
            // TODO: implement it better
 | 
			
		||||
            for (int i = 0; i < 16; i++)
 | 
			
		||||
                buf[i] ^= other.buf[i];
 | 
			
		||||
#endif
 | 
			
		||||
        }    
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    typedef i2p::data::Tag<32> AESKey;
 | 
			
		||||
    
 | 
			
		||||
    template<size_t sz>
 | 
			
		||||
    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:
 | 
			
		||||
 | 
			
		||||
            uint8_t m_UnalignedBuffer[sz + 15]; // up to 15 bytes alignment
 | 
			
		||||
            uint8_t * m_Buf;
 | 
			
		||||
    };          
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#ifdef AESNI
 | 
			
		||||
    class ECBCryptoAESNI
 | 
			
		||||
    {   
 | 
			
		||||
        public:
 | 
			
		||||
 | 
			
		||||
            uint8_t * GetKeySchedule () { return m_KeySchedule; };
 | 
			
		||||
 | 
			
		||||
        protected:
 | 
			
		||||
 | 
			
		||||
            void ExpandKey (const AESKey& key);
 | 
			
		||||
        
 | 
			
		||||
        private:
 | 
			
		||||
 | 
			
		||||
            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 CipherBlock * in, CipherBlock * out); 
 | 
			
		||||
    };  
 | 
			
		||||
 | 
			
		||||
    class ECBDecryptionAESNI: public ECBCryptoAESNI
 | 
			
		||||
    {
 | 
			
		||||
        public:
 | 
			
		||||
        
 | 
			
		||||
            void SetKey (const AESKey& key);
 | 
			
		||||
            void Decrypt (const CipherBlock * in, CipherBlock * out);     
 | 
			
		||||
    };  
 | 
			
		||||
 | 
			
		||||
    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 CipherBlock * in, CipherBlock * out)
 | 
			
		||||
            {
 | 
			
		||||
                m_Encryption.ProcessData (out->buf, in->buf, 16);
 | 
			
		||||
            }   
 | 
			
		||||
 | 
			
		||||
        private:
 | 
			
		||||
 | 
			
		||||
            CryptoPP::ECB_Mode<CryptoPP::AES>::Encryption m_Encryption;
 | 
			
		||||
    };  
 | 
			
		||||
 | 
			
		||||
    class ECBDecryption
 | 
			
		||||
    {
 | 
			
		||||
        public:
 | 
			
		||||
        
 | 
			
		||||
            void SetKey (const AESKey& key) 
 | 
			
		||||
            { 
 | 
			
		||||
                m_Decryption.SetKey (key, 32); 
 | 
			
		||||
            }
 | 
			
		||||
            void Decrypt (const CipherBlock * in, CipherBlock * out)
 | 
			
		||||
            {
 | 
			
		||||
                m_Decryption.ProcessData (out->buf, in->buf, 16);
 | 
			
		||||
            }   
 | 
			
		||||
 | 
			
		||||
        private:
 | 
			
		||||
 | 
			
		||||
            CryptoPP::ECB_Mode<CryptoPP::AES>::Decryption m_Decryption;
 | 
			
		||||
    };      
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#endif          
 | 
			
		||||
 | 
			
		||||
    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 Encrypt (int numBlocks, const CipherBlock * in, CipherBlock * 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:
 | 
			
		||||
 | 
			
		||||
            CipherBlock m_LastBlock;
 | 
			
		||||
            
 | 
			
		||||
            ECBEncryption m_ECBEncryption;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    class CBCDecryption
 | 
			
		||||
    {
 | 
			
		||||
        public:
 | 
			
		||||
    
 | 
			
		||||
            CBCDecryption () { memset (m_IV.buf, 0, 16); };
 | 
			
		||||
 | 
			
		||||
            CBCDecryption(const AESKey& key, const uint8_t* iv)
 | 
			
		||||
                : CBCDecryption()
 | 
			
		||||
            {
 | 
			
		||||
                SetKey(key);
 | 
			
		||||
                SetIV(iv);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            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 CipherBlock * in, CipherBlock * 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:
 | 
			
		||||
 | 
			
		||||
            CipherBlock m_IV;
 | 
			
		||||
            ECBDecryption m_ECBDecryption;
 | 
			
		||||
    };  
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										60
									
								
								core/crypto/hmac.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								core/crypto/hmac.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,60 @@
 | 
			
		|||
#ifndef HMAC_H__
 | 
			
		||||
#define HMAC_H__
 | 
			
		||||
 | 
			
		||||
#include <inttypes.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#define CRYPTOPP_ENABLE_NAMESPACE_WEAK 1
 | 
			
		||||
#include <cryptopp/md5.h>
 | 
			
		||||
#include "Identity.h"
 | 
			
		||||
 | 
			
		||||
namespace i2p
 | 
			
		||||
{
 | 
			
		||||
namespace crypto
 | 
			
		||||
{
 | 
			
		||||
    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);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										936
									
								
								core/transport/NTCPSession.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										936
									
								
								core/transport/NTCPSession.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,936 @@
 | 
			
		|||
#include <string.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include "util/I2PEndian.h"
 | 
			
		||||
#include <cryptopp/dh.h>
 | 
			
		||||
#include <cryptopp/adler32.h>
 | 
			
		||||
#include "util/base64.h"
 | 
			
		||||
#include "util/Log.h"
 | 
			
		||||
#include "util/Timestamp.h"
 | 
			
		||||
#include "crypto/CryptoConst.h"
 | 
			
		||||
#include "I2NPProtocol.h"
 | 
			
		||||
#include "RouterContext.h"
 | 
			
		||||
#include "Transports.h"
 | 
			
		||||
#include "NetDb.h"
 | 
			
		||||
#include "NTCPSession.h"
 | 
			
		||||
 | 
			
		||||
using namespace i2p::crypto;
 | 
			
		||||
 | 
			
		||||
namespace i2p
 | 
			
		||||
{
 | 
			
		||||
namespace transport
 | 
			
		||||
{
 | 
			
		||||
    NTCPSession::NTCPSession (NTCPServer& server, std::shared_ptr<const i2p::data::RouterInfo> 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;
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        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)
 | 
			
		||||
        {
 | 
			
		||||
            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 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::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
 | 
			
		||||
 | 
			
		||||
        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::HandlePhase2Sent (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 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::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;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        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);
 | 
			
		||||
 | 
			
		||||
        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 (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, "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);
 | 
			
		||||
        }   
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void NTCPSession::HandlePhase3ExtraReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred, uint32_t tsB, size_t paddingLen)
 | 
			
		||||
    {
 | 
			
		||||
        if (ecode)
 | 
			
		||||
        {
 | 
			
		||||
            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);
 | 
			
		||||
        }       
 | 
			
		||||
    }   
 | 
			
		||||
 | 
			
		||||
    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<i2p::I2NPMessage> 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<std::shared_ptr<I2NPMessage> >{ msg })); 
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    boost::asio::const_buffers_1 NTCPSession::CreateMsgBuffer (std::shared_ptr<I2NPMessage> 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<std::shared_ptr<I2NPMessage> >& msgs)
 | 
			
		||||
    {
 | 
			
		||||
        m_IsSending = true;
 | 
			
		||||
        std::vector<boost::asio::const_buffer> 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<std::shared_ptr<I2NPMessage> > 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::SendTimeSyncMessage ()
 | 
			
		||||
    {
 | 
			
		||||
        Send (nullptr);
 | 
			
		||||
    }   
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    void NTCPSession::SendI2NPMessages (const std::vector<std::shared_ptr<I2NPMessage> >& msgs)
 | 
			
		||||
    {
 | 
			
		||||
        m_Server.GetService ().post (std::bind (&NTCPSession::PostI2NPMessages, shared_from_this (), msgs));  
 | 
			
		||||
    }   
 | 
			
		||||
 | 
			
		||||
    void NTCPSession::PostI2NPMessages (std::vector<std::shared_ptr<I2NPMessage> > 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 ();
 | 
			
		||||
    }   
 | 
			
		||||
 | 
			
		||||
    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<NTCPSession>(*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<NTCPSession> (*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;
 | 
			
		||||
 | 
			
		||||
            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<NTCPSession> session)
 | 
			
		||||
    {
 | 
			
		||||
        if (session)
 | 
			
		||||
        {
 | 
			
		||||
            std::unique_lock<std::mutex> l(m_NTCPSessionsMutex);    
 | 
			
		||||
            m_NTCPSessions[session->GetRemoteIdentity ().GetIdentHash ()] = session;
 | 
			
		||||
        }
 | 
			
		||||
    }   
 | 
			
		||||
 | 
			
		||||
    void NTCPServer::RemoveNTCPSession (std::shared_ptr<NTCPSession> session)
 | 
			
		||||
    {
 | 
			
		||||
        if (session)
 | 
			
		||||
        {
 | 
			
		||||
            std::unique_lock<std::mutex> l(m_NTCPSessionsMutex);    
 | 
			
		||||
            m_NTCPSessions.erase (session->GetRemoteIdentity ().GetIdentHash ());
 | 
			
		||||
        }
 | 
			
		||||
    }   
 | 
			
		||||
 | 
			
		||||
    std::shared_ptr<NTCPSession> NTCPServer::FindNTCPSession (const i2p::data::IdentHash& ident)
 | 
			
		||||
    {
 | 
			
		||||
        std::unique_lock<std::mutex> 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<NTCPSession> 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<NTCPSession> (*this);
 | 
			
		||||
            m_NTCPAcceptor->async_accept(conn->GetSocket (), std::bind (&NTCPServer::HandleAccept, this, 
 | 
			
		||||
                conn, std::placeholders::_1));
 | 
			
		||||
        }   
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void NTCPServer::HandleAcceptV6 (std::shared_ptr<NTCPSession> 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<NTCPSession> (*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<NTCPSession> 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<NTCPSession> 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");
 | 
			
		||||
    }
 | 
			
		||||
}   
 | 
			
		||||
}   
 | 
			
		||||
							
								
								
									
										183
									
								
								core/transport/NTCPSession.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										183
									
								
								core/transport/NTCPSession.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,183 @@
 | 
			
		|||
#ifndef NTCP_SESSION_H__
 | 
			
		||||
#define NTCP_SESSION_H__
 | 
			
		||||
 | 
			
		||||
#include <inttypes.h>
 | 
			
		||||
#include <map>
 | 
			
		||||
#include <memory>
 | 
			
		||||
#include <thread>
 | 
			
		||||
#include <mutex>
 | 
			
		||||
#include <boost/asio.hpp>
 | 
			
		||||
#include <cryptopp/modes.h>
 | 
			
		||||
#include <cryptopp/aes.h>
 | 
			
		||||
#include "crypto/aes.h"
 | 
			
		||||
#include "Identity.h"
 | 
			
		||||
#include "RouterInfo.h"
 | 
			
		||||
#include "I2NPProtocol.h"
 | 
			
		||||
#include "TransportSession.h"
 | 
			
		||||
 | 
			
		||||
namespace i2p
 | 
			
		||||
{
 | 
			
		||||
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()  
 | 
			
		||||
 | 
			
		||||
    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<NTCPSession>
 | 
			
		||||
    {
 | 
			
		||||
        public:
 | 
			
		||||
 | 
			
		||||
            NTCPSession (NTCPServer& server, std::shared_ptr<const i2p::data::RouterInfo> 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<std::shared_ptr<I2NPMessage> >& msgs);
 | 
			
		||||
            
 | 
			
		||||
        private:
 | 
			
		||||
 | 
			
		||||
            void PostI2NPMessages (std::vector<std::shared_ptr<I2NPMessage> > 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);
 | 
			
		||||
 | 
			
		||||
            //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<i2p::I2NPMessage> msg);
 | 
			
		||||
            boost::asio::const_buffers_1 CreateMsgBuffer (std::shared_ptr<I2NPMessage> msg);
 | 
			
		||||
            void Send (const std::vector<std::shared_ptr<I2NPMessage> >& msgs);
 | 
			
		||||
            void HandleSent (const boost::system::error_code& ecode, std::size_t bytes_transferred, std::vector<std::shared_ptr<I2NPMessage> > 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;
 | 
			
		||||
 | 
			
		||||
            struct Establisher
 | 
			
		||||
            {   
 | 
			
		||||
                NTCPPhase1 phase1;
 | 
			
		||||
                NTCPPhase2 phase2;
 | 
			
		||||
            } * m_Establisher;  
 | 
			
		||||
            
 | 
			
		||||
            i2p::crypto::AESAlignedBuffer<NTCP_BUFFER_SIZE + 16> m_ReceiveBuffer;
 | 
			
		||||
            i2p::crypto::AESAlignedBuffer<16> m_TimeSyncBuffer;
 | 
			
		||||
            int m_ReceiveBufferOffset; 
 | 
			
		||||
 | 
			
		||||
            std::shared_ptr<I2NPMessage> m_NextMessage;
 | 
			
		||||
            size_t m_NextMessageOffset;
 | 
			
		||||
            i2p::I2NPMessagesHandler m_Handler;
 | 
			
		||||
 | 
			
		||||
            bool m_IsSending;
 | 
			
		||||
            std::vector<std::shared_ptr<I2NPMessage> > m_SendQueue;
 | 
			
		||||
            
 | 
			
		||||
            boost::asio::ip::address m_ConnectedFrom; // for ban
 | 
			
		||||
    };  
 | 
			
		||||
 | 
			
		||||
    // TODO: move to NTCP.h/.cpp
 | 
			
		||||
    class NTCPServer
 | 
			
		||||
    {
 | 
			
		||||
        public:
 | 
			
		||||
 | 
			
		||||
            NTCPServer (int port);
 | 
			
		||||
            ~NTCPServer ();
 | 
			
		||||
 | 
			
		||||
            void Start ();
 | 
			
		||||
            void Stop ();
 | 
			
		||||
 | 
			
		||||
            void AddNTCPSession (std::shared_ptr<NTCPSession> session);
 | 
			
		||||
            void RemoveNTCPSession (std::shared_ptr<NTCPSession> session);
 | 
			
		||||
            std::shared_ptr<NTCPSession> FindNTCPSession (const i2p::data::IdentHash& ident);
 | 
			
		||||
            void Connect (const boost::asio::ip::address& address, int port, std::shared_ptr<NTCPSession> conn);
 | 
			
		||||
            
 | 
			
		||||
            boost::asio::io_service& GetService () { return m_Service; };
 | 
			
		||||
            void Ban (const boost::asio::ip::address& addr);            
 | 
			
		||||
 | 
			
		||||
        private:
 | 
			
		||||
 | 
			
		||||
            void Run ();
 | 
			
		||||
            void HandleAccept (std::shared_ptr<NTCPSession> conn, const boost::system::error_code& error);
 | 
			
		||||
            void HandleAcceptV6 (std::shared_ptr<NTCPSession> conn, const boost::system::error_code& error);
 | 
			
		||||
 | 
			
		||||
            void HandleConnect (const boost::system::error_code& ecode, std::shared_ptr<NTCPSession> 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<i2p::data::IdentHash, std::shared_ptr<NTCPSession> > m_NTCPSessions;
 | 
			
		||||
            std::map<boost::asio::ip::address, uint32_t> m_BanList; // IP -> ban expiration time in seconds
 | 
			
		||||
 | 
			
		||||
        public:
 | 
			
		||||
 | 
			
		||||
            // for HTTP/I2PControl
 | 
			
		||||
            const decltype(m_NTCPSessions)& GetNTCPSessions () const { return m_NTCPSessions; };
 | 
			
		||||
    };  
 | 
			
		||||
}   
 | 
			
		||||
}   
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										556
									
								
								core/transport/SSU.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										556
									
								
								core/transport/SSU.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,556 @@
 | 
			
		|||
#include <string.h>
 | 
			
		||||
#include <boost/bind.hpp>
 | 
			
		||||
#include "util/Log.h"
 | 
			
		||||
#include "util/Timestamp.h"
 | 
			
		||||
#include "RouterContext.h"
 | 
			
		||||
#include "NetDb.h"
 | 
			
		||||
#include "SSU.h"
 | 
			
		||||
 | 
			
		||||
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 ()
 | 
			
		||||
    {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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::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::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<SSUSession> 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::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::HandleReceivedFrom (const boost::system::error_code& ecode, std::size_t bytes_transferred, SSUPacket * packet)
 | 
			
		||||
    {
 | 
			
		||||
        if (!ecode)
 | 
			
		||||
        {
 | 
			
		||||
            packet->len = bytes_transferred;
 | 
			
		||||
            std::vector<SSUPacket *> 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();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            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<SSUPacket *> 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();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            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<SSUPacket *> packets)
 | 
			
		||||
    {
 | 
			
		||||
        std::shared_ptr<SSUSession> 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<SSUSession> (*this, packet->from);
 | 
			
		||||
                        session->WaitForConnect ();
 | 
			
		||||
                        {
 | 
			
		||||
                            std::unique_lock<std::mutex> 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<SSUSession> SSUServer::FindSession (std::shared_ptr<const i2p::data::RouterInfo> 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<SSUSession> 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<SSUSession> SSUServer::GetSession (std::shared_ptr<const i2p::data::RouterInfo> router, bool peerTest)
 | 
			
		||||
    {
 | 
			
		||||
        std::shared_ptr<SSUSession> 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<SSUSession> (*this, remoteEndpoint, router, peerTest);
 | 
			
		||||
                    {
 | 
			
		||||
                        std::unique_lock<std::mutex> 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<SSUSession> 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<SSUSession> (*this, introducerEndpoint, router);
 | 
			
		||||
                                std::unique_lock<std::mutex> 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<std::mutex> 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<SSUSession> session)
 | 
			
		||||
    {
 | 
			
		||||
        if (session)
 | 
			
		||||
        {
 | 
			
		||||
            session->Close ();
 | 
			
		||||
            std::unique_lock<std::mutex> l(m_SessionsMutex);    
 | 
			
		||||
            m_Sessions.erase (session->GetRemoteEndpoint ());
 | 
			
		||||
        }   
 | 
			
		||||
    }   
 | 
			
		||||
 | 
			
		||||
    void SSUServer::DeleteAllSessions ()
 | 
			
		||||
    {
 | 
			
		||||
        std::unique_lock<std::mutex> l(m_SessionsMutex);
 | 
			
		||||
        for (auto it: m_Sessions)
 | 
			
		||||
            it.second->Close ();
 | 
			
		||||
        m_Sessions.clear ();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    template<typename Filter>
 | 
			
		||||
    std::shared_ptr<SSUSession> SSUServer::GetRandomSession (Filter filter)
 | 
			
		||||
    {
 | 
			
		||||
        std::vector<std::shared_ptr<SSUSession> > 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<SSUSession> SSUServer::GetRandomEstablishedSession (std::shared_ptr<const SSUSession> excluded)
 | 
			
		||||
    {
 | 
			
		||||
        return GetRandomSession (
 | 
			
		||||
            [excluded](std::shared_ptr<SSUSession> session)->bool 
 | 
			
		||||
            { 
 | 
			
		||||
                return session->GetState () == eSessionStateEstablished && !session->IsV6 () && 
 | 
			
		||||
                    session != excluded; 
 | 
			
		||||
            }
 | 
			
		||||
                                );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    std::set<SSUSession *> SSUServer::FindIntroducers (int maxNumIntroducers)
 | 
			
		||||
    {
 | 
			
		||||
        uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
 | 
			
		||||
        std::set<SSUSession *> ret;
 | 
			
		||||
        for (int i = 0; i < maxNumIntroducers; i++)
 | 
			
		||||
        {
 | 
			
		||||
            auto session = GetRandomSession (
 | 
			
		||||
                [&ret, ts](std::shared_ptr<SSUSession> 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::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<boost::asio::ip::udp::endpoint> 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 ();
 | 
			
		||||
        }   
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void SSUServer::NewPeerTest (uint32_t nonce, PeerTestParticipant role, std::shared_ptr<SSUSession> 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;
 | 
			
		||||
    }   
 | 
			
		||||
 | 
			
		||||
    std::shared_ptr<SSUSession> 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::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 ();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										114
									
								
								core/transport/SSU.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										114
									
								
								core/transport/SSU.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,114 @@
 | 
			
		|||
#ifndef SSU_H__
 | 
			
		||||
#define SSU_H__
 | 
			
		||||
 | 
			
		||||
#include <inttypes.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <map>
 | 
			
		||||
#include <list>
 | 
			
		||||
#include <set>
 | 
			
		||||
#include <thread>
 | 
			
		||||
#include <mutex>
 | 
			
		||||
#include <boost/asio.hpp>
 | 
			
		||||
#include "crypto/aes.h"
 | 
			
		||||
#include "util/I2PEndian.h"
 | 
			
		||||
#include "Identity.h"
 | 
			
		||||
#include "RouterInfo.h"
 | 
			
		||||
#include "I2NPProtocol.h"
 | 
			
		||||
#include "SSUSession.h"
 | 
			
		||||
 | 
			
		||||
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;
 | 
			
		||||
 | 
			
		||||
    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<SSUSession> GetSession (std::shared_ptr<const i2p::data::RouterInfo> router, bool peerTest = false);
 | 
			
		||||
            std::shared_ptr<SSUSession> FindSession (std::shared_ptr<const i2p::data::RouterInfo> router) const;
 | 
			
		||||
            std::shared_ptr<SSUSession> FindSession (const boost::asio::ip::udp::endpoint& e) const;
 | 
			
		||||
            std::shared_ptr<SSUSession> GetRandomEstablishedSession (std::shared_ptr<const SSUSession> excluded);
 | 
			
		||||
            void DeleteSession (std::shared_ptr<SSUSession> 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<SSUSession> FindRelaySession (uint32_t tag);
 | 
			
		||||
 | 
			
		||||
            void NewPeerTest (uint32_t nonce, PeerTestParticipant role, std::shared_ptr<SSUSession> session = nullptr);
 | 
			
		||||
            PeerTestParticipant GetPeerTestParticipant (uint32_t nonce);
 | 
			
		||||
            std::shared_ptr<SSUSession> GetPeerTestSession (uint32_t nonce);
 | 
			
		||||
            void UpdatePeerTest (uint32_t nonce, PeerTestParticipant role);
 | 
			
		||||
            void RemovePeerTest (uint32_t nonce);
 | 
			
		||||
 | 
			
		||||
        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<SSUPacket *> packets);
 | 
			
		||||
 | 
			
		||||
            template<typename Filter>
 | 
			
		||||
            std::shared_ptr<SSUSession> GetRandomSession (Filter filter);
 | 
			
		||||
            
 | 
			
		||||
            std::set<SSUSession *> FindIntroducers (int maxNumIntroducers); 
 | 
			
		||||
            void ScheduleIntroducersUpdateTimer ();
 | 
			
		||||
            void HandleIntroducersUpdateTimer (const boost::system::error_code& ecode);
 | 
			
		||||
 | 
			
		||||
            void SchedulePeerTestsCleanupTimer ();
 | 
			
		||||
            void HandlePeerTestsCleanupTimer (const boost::system::error_code& ecode);
 | 
			
		||||
 | 
			
		||||
        private:
 | 
			
		||||
 | 
			
		||||
            struct PeerTest
 | 
			
		||||
            {
 | 
			
		||||
                uint64_t creationTime;
 | 
			
		||||
                PeerTestParticipant role;
 | 
			
		||||
                std::shared_ptr<SSUSession> 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<boost::asio::ip::udp::endpoint> m_Introducers; // introducers we are connected to
 | 
			
		||||
            mutable std::mutex m_SessionsMutex;
 | 
			
		||||
            std::map<boost::asio::ip::udp::endpoint, std::shared_ptr<SSUSession> > m_Sessions;
 | 
			
		||||
            std::map<uint32_t, boost::asio::ip::udp::endpoint> m_Relays; // we are introducer
 | 
			
		||||
            std::map<uint32_t, PeerTest> m_PeerTests; // nonce -> creation time in milliseconds
 | 
			
		||||
 | 
			
		||||
        public:
 | 
			
		||||
            // for HTTP only
 | 
			
		||||
            const decltype(m_Sessions)& GetSessions () const { return m_Sessions; };
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										507
									
								
								core/transport/SSUData.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										507
									
								
								core/transport/SSUData.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,507 @@
 | 
			
		|||
#include <stdlib.h>
 | 
			
		||||
#include <boost/bind.hpp>
 | 
			
		||||
#include "util/Log.h"
 | 
			
		||||
#include "util/Timestamp.h"
 | 
			
		||||
#include "NetDb.h"
 | 
			
		||||
#include "SSU.h"
 | 
			
		||||
#include "SSUData.h"
 | 
			
		||||
 | 
			
		||||
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++;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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 ()
 | 
			
		||||
    {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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::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::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<IncompleteMessage>(new IncompleteMessage (msg)))).first;
 | 
			
		||||
            }
 | 
			
		||||
            std::unique_ptr<IncompleteMessage>& 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<Fragment>(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;
 | 
			
		||||
        }   
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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<i2p::I2NPMessage> 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<SentMessage>(new SentMessage))); 
 | 
			
		||||
        std::unique_ptr<SentMessage>& 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> (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
 | 
			
		||||
 | 
			
		||||
        // 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
 | 
			
		||||
 | 
			
		||||
        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::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 ();  
 | 
			
		||||
        }   
 | 
			
		||||
    }   
 | 
			
		||||
 | 
			
		||||
    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::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 ();
 | 
			
		||||
        }   
 | 
			
		||||
    }   
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										128
									
								
								core/transport/SSUData.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										128
									
								
								core/transport/SSUData.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,128 @@
 | 
			
		|||
#ifndef SSU_DATA_H__
 | 
			
		||||
#define SSU_DATA_H__
 | 
			
		||||
 | 
			
		||||
#include <inttypes.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <map>
 | 
			
		||||
#include <vector>
 | 
			
		||||
#include <set>
 | 
			
		||||
#include <memory>
 | 
			
		||||
#include <boost/asio.hpp>
 | 
			
		||||
#include "I2NPProtocol.h"
 | 
			
		||||
#include "Identity.h"
 | 
			
		||||
#include "RouterInfo.h"
 | 
			
		||||
 | 
			
		||||
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;  
 | 
			
		||||
 | 
			
		||||
    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); };      
 | 
			
		||||
    };  
 | 
			
		||||
 | 
			
		||||
    struct FragmentCmp
 | 
			
		||||
    {
 | 
			
		||||
        bool operator() (const std::unique_ptr<Fragment>& f1, const std::unique_ptr<Fragment>& f2) const
 | 
			
		||||
        {   
 | 
			
		||||
            return f1->fragmentNum < f2->fragmentNum; 
 | 
			
		||||
        };
 | 
			
		||||
    };  
 | 
			
		||||
    
 | 
			
		||||
    struct IncompleteMessage
 | 
			
		||||
    {
 | 
			
		||||
        std::shared_ptr<I2NPMessage> msg;
 | 
			
		||||
        int nextFragmentNum;    
 | 
			
		||||
        uint32_t lastFragmentInsertTime; // in seconds
 | 
			
		||||
        std::set<std::unique_ptr<Fragment>, FragmentCmp> savedFragments;
 | 
			
		||||
        
 | 
			
		||||
        IncompleteMessage (std::shared_ptr<I2NPMessage> m): msg (m), nextFragmentNum (0), lastFragmentInsertTime (0) {};
 | 
			
		||||
        void AttachNextFragment (const uint8_t * fragment, size_t fragmentSize);    
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    struct SentMessage
 | 
			
		||||
    {
 | 
			
		||||
        std::vector<std::unique_ptr<Fragment> > fragments;
 | 
			
		||||
        uint32_t nextResendTime; // in seconds
 | 
			
		||||
        int numResends;
 | 
			
		||||
    };  
 | 
			
		||||
    
 | 
			
		||||
    class SSUSession;
 | 
			
		||||
    class SSUData
 | 
			
		||||
    {
 | 
			
		||||
        public:
 | 
			
		||||
 | 
			
		||||
            SSUData (SSUSession& session);
 | 
			
		||||
            ~SSUData ();
 | 
			
		||||
 | 
			
		||||
            void Start ();
 | 
			
		||||
            void Stop ();   
 | 
			
		||||
            
 | 
			
		||||
            void ProcessMessage (uint8_t * buf, size_t len);
 | 
			
		||||
            void FlushReceivedMessage ();
 | 
			
		||||
            void Send (std::shared_ptr<i2p::I2NPMessage> msg);
 | 
			
		||||
 | 
			
		||||
            void UpdatePacketSize (const i2p::data::IdentHash& remoteIdent);
 | 
			
		||||
 | 
			
		||||
        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 ScheduleResend ();
 | 
			
		||||
            void HandleResendTimer (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:    
 | 
			
		||||
 | 
			
		||||
            SSUSession& m_Session;
 | 
			
		||||
            std::map<uint32_t, std::unique_ptr<IncompleteMessage> > m_IncompleteMessages;
 | 
			
		||||
            std::map<uint32_t, std::unique_ptr<SentMessage> > m_SentMessages;
 | 
			
		||||
            std::set<uint32_t> m_ReceivedMessages;
 | 
			
		||||
            boost::asio::deadline_timer m_ResendTimer, m_DecayTimer, m_IncompleteMessagesCleanupTimer;
 | 
			
		||||
            int m_MaxPacketSize, m_PacketSize;
 | 
			
		||||
            i2p::I2NPMessagesHandler m_Handler;
 | 
			
		||||
    };  
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										1090
									
								
								core/transport/SSUSession.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										1090
									
								
								core/transport/SSUSession.cpp
									
										
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										159
									
								
								core/transport/SSUSession.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										159
									
								
								core/transport/SSUSession.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,159 @@
 | 
			
		|||
#ifndef SSU_SESSION_H__
 | 
			
		||||
#define SSU_SESSION_H__
 | 
			
		||||
 | 
			
		||||
#include <inttypes.h>
 | 
			
		||||
#include <set>
 | 
			
		||||
#include <memory>
 | 
			
		||||
#include "crypto/aes.h"
 | 
			
		||||
#include "crypto/hmac.h"
 | 
			
		||||
#include "I2NPProtocol.h"
 | 
			
		||||
#include "TransportSession.h"
 | 
			
		||||
#include "SSUData.h"
 | 
			
		||||
 | 
			
		||||
namespace i2p
 | 
			
		||||
{
 | 
			
		||||
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;  
 | 
			
		||||
 | 
			
		||||
        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
 | 
			
		||||
 | 
			
		||||
    // 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 PeerTestParticipant
 | 
			
		||||
    {
 | 
			
		||||
        ePeerTestParticipantUnknown = 0,
 | 
			
		||||
        ePeerTestParticipantAlice1,
 | 
			
		||||
        ePeerTestParticipantAlice2,
 | 
			
		||||
        ePeerTestParticipantBob,
 | 
			
		||||
        ePeerTestParticipantCharlie
 | 
			
		||||
    };
 | 
			
		||||
    
 | 
			
		||||
    class SSUServer;
 | 
			
		||||
    class SSUSession: public TransportSession, public std::enable_shared_from_this<SSUSession>
 | 
			
		||||
    {
 | 
			
		||||
        public:
 | 
			
		||||
 | 
			
		||||
            SSUSession (SSUServer& server, boost::asio::ip::udp::endpoint& remoteEndpoint,
 | 
			
		||||
                std::shared_ptr<const i2p::data::RouterInfo> 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<std::shared_ptr<I2NPMessage> >& 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; };
 | 
			
		||||
 | 
			
		||||
            void FlushData ();
 | 
			
		||||
            
 | 
			
		||||
        private:
 | 
			
		||||
 | 
			
		||||
            boost::asio::io_service& GetService ();
 | 
			
		||||
            void CreateAESandMacKey (const uint8_t * pubKey); 
 | 
			
		||||
 | 
			
		||||
            void PostI2NPMessages (std::vector<std::shared_ptr<I2NPMessage> > 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);
 | 
			
		||||
 | 
			
		||||
        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;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										86
									
								
								core/transport/TransportSession.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										86
									
								
								core/transport/TransportSession.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,86 @@
 | 
			
		|||
#ifndef TRANSPORT_SESSION_H__
 | 
			
		||||
#define TRANSPORT_SESSION_H__
 | 
			
		||||
 | 
			
		||||
#include <inttypes.h>
 | 
			
		||||
#include <iostream>
 | 
			
		||||
#include <memory>
 | 
			
		||||
#include <vector>
 | 
			
		||||
#include "Identity.h"
 | 
			
		||||
#include "RouterInfo.h"
 | 
			
		||||
#include "I2NPProtocol.h"
 | 
			
		||||
 | 
			
		||||
namespace i2p
 | 
			
		||||
{
 | 
			
		||||
namespace transport
 | 
			
		||||
{
 | 
			
		||||
    struct DHKeysPair // transient keys for transport sessions
 | 
			
		||||
    {
 | 
			
		||||
        uint8_t publicKey[256];
 | 
			
		||||
        uint8_t privateKey[256];
 | 
			
		||||
    };  
 | 
			
		||||
 | 
			
		||||
    class SignedData
 | 
			
		||||
    {
 | 
			
		||||
        public:
 | 
			
		||||
 | 
			
		||||
            SignedData () {};
 | 
			
		||||
            void Insert (const uint8_t * buf, size_t len) 
 | 
			
		||||
            { 
 | 
			
		||||
                m_Stream.write ((char *)buf, len); 
 | 
			
		||||
            }           
 | 
			
		||||
 | 
			
		||||
            template<typename T>
 | 
			
		||||
            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); 
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            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;
 | 
			
		||||
    };      
 | 
			
		||||
 | 
			
		||||
    class TransportSession
 | 
			
		||||
    {
 | 
			
		||||
        public:
 | 
			
		||||
 | 
			
		||||
            TransportSession (std::shared_ptr<const i2p::data::RouterInfo> 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<const i2p::data::RouterInfo> 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<std::shared_ptr<I2NPMessage> >& msgs) = 0;
 | 
			
		||||
            
 | 
			
		||||
        protected:
 | 
			
		||||
 | 
			
		||||
            std::shared_ptr<const i2p::data::RouterInfo> m_RemoteRouter;
 | 
			
		||||
            i2p::data::IdentityEx m_RemoteIdentity; 
 | 
			
		||||
            DHKeysPair * m_DHKeysPair; // X - for client and Y - for server
 | 
			
		||||
            size_t m_NumSentBytes, m_NumReceivedBytes;
 | 
			
		||||
    };  
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										505
									
								
								core/transport/Transports.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										505
									
								
								core/transport/Transports.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,505 @@
 | 
			
		|||
#include <cryptopp/dh.h>
 | 
			
		||||
#include "util/Log.h"
 | 
			
		||||
#include "crypto/CryptoConst.h"
 | 
			
		||||
#include "RouterContext.h"
 | 
			
		||||
#include "I2NPProtocol.h"
 | 
			
		||||
#include "NetDb.h"
 | 
			
		||||
#include "Transports.h"
 | 
			
		||||
 | 
			
		||||
using namespace i2p::data;
 | 
			
		||||
 | 
			
		||||
namespace i2p
 | 
			
		||||
{
 | 
			
		||||
namespace transport
 | 
			
		||||
{
 | 
			
		||||
    DHKeysPairSupplier::DHKeysPairSupplier (int size):
 | 
			
		||||
        m_QueueSize (size), m_IsRunning (false), m_Thread (nullptr)
 | 
			
		||||
    {
 | 
			
		||||
    }   
 | 
			
		||||
 | 
			
		||||
    DHKeysPairSupplier::~DHKeysPairSupplier ()
 | 
			
		||||
    {
 | 
			
		||||
        Stop ();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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::Run ()
 | 
			
		||||
    {
 | 
			
		||||
        while (m_IsRunning)
 | 
			
		||||
        {
 | 
			
		||||
            int num;
 | 
			
		||||
            while ((num = m_QueueSize - m_Queue.size ()) > 0)
 | 
			
		||||
                CreateDHKeysPairs (num);
 | 
			
		||||
            std::unique_lock<std::mutex>  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<std::mutex>  l(m_AcquiredMutex);
 | 
			
		||||
                m_Queue.push (pair);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    DHKeysPair * DHKeysPairSupplier::Acquire ()
 | 
			
		||||
    {
 | 
			
		||||
        if (!m_Queue.empty ())
 | 
			
		||||
        {
 | 
			
		||||
            std::unique_lock<std::mutex>  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<std::mutex>  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 ();
 | 
			
		||||
    }   
 | 
			
		||||
 | 
			
		||||
    void Transports::Start ()
 | 
			
		||||
    {
 | 
			
		||||
#ifdef USE_UPNP
 | 
			
		||||
        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 ()
 | 
			
		||||
    {   
 | 
			
		||||
#ifdef USE_UPNP
 | 
			
		||||
        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_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;       
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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<i2p::I2NPMessage> msg)
 | 
			
		||||
    {
 | 
			
		||||
        SendMessages (ident, std::vector<std::shared_ptr<i2p::I2NPMessage> > {msg });                             
 | 
			
		||||
    }   
 | 
			
		||||
 | 
			
		||||
    void Transports::SendMessages (const i2p::data::IdentHash& ident, const std::vector<std::shared_ptr<i2p::I2NPMessage> >& msgs)
 | 
			
		||||
    {
 | 
			
		||||
        m_Service.post (std::bind (&Transports::PostMessages, this, ident, msgs));
 | 
			
		||||
    }   
 | 
			
		||||
 | 
			
		||||
    void Transports::PostMessages (i2p::data::IdentHash ident, std::vector<std::shared_ptr<i2p::I2NPMessage> > 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<i2p::data::IdentHash, Peer>(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
 | 
			
		||||
#else
 | 
			
		||||
                    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<NTCPSession> (*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<const i2p::data::RouterInfo> r, const i2p::data::IdentHash& ident)
 | 
			
		||||
    {
 | 
			
		||||
        m_Service.post (std::bind (&Transports::HandleRequestComplete, this, r, ident));
 | 
			
		||||
    }       
 | 
			
		||||
    
 | 
			
		||||
    void Transports::HandleRequestComplete (std::shared_ptr<const i2p::data::RouterInfo> 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<boost::asio::ip::tcp::resolver>(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<boost::asio::ip::tcp::resolver> 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<NTCPSession> (*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<const i2p::data::RouterInfo> router)
 | 
			
		||||
    {
 | 
			
		||||
        if (!router) return;
 | 
			
		||||
        m_Service.post (std::bind (&Transports::PostCloseSession, this, router));    
 | 
			
		||||
    }   
 | 
			
		||||
 | 
			
		||||
    void Transports::PostCloseSession (std::shared_ptr<const i2p::data::RouterInfo> 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::PeerConnected (std::shared_ptr<TransportSession> 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<TransportSession> 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));
 | 
			
		||||
        }   
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    std::shared_ptr<const i2p::data::RouterInfo> Transports::GetRandomPeer () const
 | 
			
		||||
    {
 | 
			
		||||
        if(m_Peers.empty()) // ensure m.Peers.size() >= 1
 | 
			
		||||
            return nullptr;
 | 
			
		||||
 | 
			
		||||
        CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator();
 | 
			
		||||
        auto it = m_Peers.begin();
 | 
			
		||||
        std::advance(it, rnd.GenerateWord32(0, m_Peers.size () - 1));
 | 
			
		||||
 | 
			
		||||
        return it->second.router;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										160
									
								
								core/transport/Transports.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										160
									
								
								core/transport/Transports.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,160 @@
 | 
			
		|||
#ifndef TRANSPORTS_H__
 | 
			
		||||
#define TRANSPORTS_H__
 | 
			
		||||
 | 
			
		||||
#include <thread>
 | 
			
		||||
#include <mutex>
 | 
			
		||||
#include <condition_variable>
 | 
			
		||||
#include <functional>
 | 
			
		||||
#include <map>
 | 
			
		||||
#include <vector>
 | 
			
		||||
#include <queue>
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <memory>
 | 
			
		||||
#include <atomic>
 | 
			
		||||
#include <cryptopp/osrng.h>
 | 
			
		||||
#include <boost/asio.hpp>
 | 
			
		||||
#include "TransportSession.h"
 | 
			
		||||
#include "NTCPSession.h"
 | 
			
		||||
#include "SSU.h"
 | 
			
		||||
#include "RouterInfo.h"
 | 
			
		||||
#include "I2NPProtocol.h"
 | 
			
		||||
#include "Identity.h"
 | 
			
		||||
 | 
			
		||||
#ifdef USE_UPNP
 | 
			
		||||
#include "UPnP.h"
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
namespace i2p
 | 
			
		||||
{
 | 
			
		||||
namespace transport
 | 
			
		||||
{
 | 
			
		||||
    class DHKeysPairSupplier
 | 
			
		||||
    {
 | 
			
		||||
        public:
 | 
			
		||||
 | 
			
		||||
            DHKeysPairSupplier (int size);
 | 
			
		||||
            ~DHKeysPairSupplier ();
 | 
			
		||||
            void Start ();
 | 
			
		||||
            void Stop ();
 | 
			
		||||
            DHKeysPair * Acquire ();
 | 
			
		||||
            void Return (DHKeysPair * pair);
 | 
			
		||||
 | 
			
		||||
        private:
 | 
			
		||||
 | 
			
		||||
            void Run ();
 | 
			
		||||
            void CreateDHKeysPairs (int num);
 | 
			
		||||
 | 
			
		||||
        private:
 | 
			
		||||
 | 
			
		||||
            const int m_QueueSize;
 | 
			
		||||
            std::queue<DHKeysPair *> m_Queue;
 | 
			
		||||
 | 
			
		||||
            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<const i2p::data::RouterInfo> router;
 | 
			
		||||
        std::list<std::shared_ptr<TransportSession> > sessions;
 | 
			
		||||
        uint64_t creationTime;
 | 
			
		||||
        std::vector<std::shared_ptr<i2p::I2NPMessage> > 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:
 | 
			
		||||
 | 
			
		||||
            Transports ();
 | 
			
		||||
            ~Transports ();
 | 
			
		||||
 | 
			
		||||
            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<i2p::I2NPMessage> msg);
 | 
			
		||||
            void SendMessages (const i2p::data::IdentHash& ident, const std::vector<std::shared_ptr<i2p::I2NPMessage> >& msgs);
 | 
			
		||||
            void CloseSession (std::shared_ptr<const i2p::data::RouterInfo> router);
 | 
			
		||||
 | 
			
		||||
            void PeerConnected (std::shared_ptr<TransportSession> session);
 | 
			
		||||
            void PeerDisconnected (std::shared_ptr<TransportSession> 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<const i2p::data::RouterInfo> GetRandomPeer () const;
 | 
			
		||||
 | 
			
		||||
        private:
 | 
			
		||||
 | 
			
		||||
            void Run ();
 | 
			
		||||
            void RequestComplete (std::shared_ptr<const i2p::data::RouterInfo> r, const i2p::data::IdentHash& ident);
 | 
			
		||||
            void HandleRequestComplete (std::shared_ptr<const i2p::data::RouterInfo> r, const i2p::data::IdentHash& ident);
 | 
			
		||||
            void PostMessages (i2p::data::IdentHash ident, std::vector<std::shared_ptr<i2p::I2NPMessage> > msgs);
 | 
			
		||||
            void PostCloseSession (std::shared_ptr<const i2p::data::RouterInfo> 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<boost::asio::ip::tcp::resolver> resolver);
 | 
			
		||||
 | 
			
		||||
            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;
 | 
			
		||||
 | 
			
		||||
            NTCPServer * m_NTCPServer;
 | 
			
		||||
            SSUServer * m_SSUServer;
 | 
			
		||||
            std::map<i2p::data::IdentHash, Peer> m_Peers;
 | 
			
		||||
            
 | 
			
		||||
            DHKeysPairSupplier m_DHKeysPairSupplier;
 | 
			
		||||
 | 
			
		||||
            std::atomic<uint64_t> 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;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
        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; };
 | 
			
		||||
    };  
 | 
			
		||||
 | 
			
		||||
    extern Transports transports;
 | 
			
		||||
}   
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										111
									
								
								core/tunnel/TransitTunnel.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										111
									
								
								core/tunnel/TransitTunnel.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,111 @@
 | 
			
		|||
#include <string.h>
 | 
			
		||||
#include "util/I2PEndian.h"
 | 
			
		||||
#include "util/Log.h"
 | 
			
		||||
#include "RouterContext.h"
 | 
			
		||||
#include "I2NPProtocol.h"
 | 
			
		||||
#include "Tunnel.h"
 | 
			
		||||
#include "transport/Transports.h"
 | 
			
		||||
#include "TransitTunnel.h"
 | 
			
		||||
 | 
			
		||||
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);
 | 
			
		||||
    }   
 | 
			
		||||
 | 
			
		||||
    void TransitTunnel::EncryptTunnelMsg (std::shared_ptr<const I2NPMessage> in, std::shared_ptr<I2NPMessage> out)
 | 
			
		||||
    {       
 | 
			
		||||
        m_Encryption.Encrypt (in->GetPayload () + 4, out->GetPayload () + 4); 
 | 
			
		||||
    }   
 | 
			
		||||
 | 
			
		||||
    TransitTunnelParticipant::~TransitTunnelParticipant ()
 | 
			
		||||
    {
 | 
			
		||||
    }   
 | 
			
		||||
        
 | 
			
		||||
    void TransitTunnelParticipant::HandleTunnelDataMsg (std::shared_ptr<const i2p::I2NPMessage> 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<i2p::I2NPMessage> msg)
 | 
			
		||||
    {   
 | 
			
		||||
        LogPrint (eLogError, "We are not a gateway for transit tunnel ", m_TunnelID);
 | 
			
		||||
    }       
 | 
			
		||||
 | 
			
		||||
    void TransitTunnel::HandleTunnelDataMsg (std::shared_ptr<const i2p::I2NPMessage> tunnelMsg)
 | 
			
		||||
    {
 | 
			
		||||
        LogPrint (eLogError, "Incoming tunnel message is not supported  ", m_TunnelID);
 | 
			
		||||
    }   
 | 
			
		||||
        
 | 
			
		||||
    void TransitTunnelGateway::SendTunnelDataMsg (std::shared_ptr<i2p::I2NPMessage> msg)
 | 
			
		||||
    {
 | 
			
		||||
        TunnelMessageBlock block;
 | 
			
		||||
        block.deliveryType = eDeliveryTypeLocal;
 | 
			
		||||
        block.data = msg;
 | 
			
		||||
        std::unique_lock<std::mutex> l(m_SendMutex);
 | 
			
		||||
        m_Gateway.PutTunnelDataMsg (block);
 | 
			
		||||
    }       
 | 
			
		||||
 | 
			
		||||
    void TransitTunnelGateway::FlushTunnelDataMsgs ()
 | 
			
		||||
    {
 | 
			
		||||
        std::unique_lock<std::mutex> l(m_SendMutex);
 | 
			
		||||
        m_Gateway.SendBuffer ();
 | 
			
		||||
    }   
 | 
			
		||||
        
 | 
			
		||||
    void TransitTunnelEndpoint::HandleTunnelDataMsg (std::shared_ptr<const i2p::I2NPMessage> 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);
 | 
			
		||||
        }   
 | 
			
		||||
    }       
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										111
									
								
								core/tunnel/TransitTunnel.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										111
									
								
								core/tunnel/TransitTunnel.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,111 @@
 | 
			
		|||
#ifndef TRANSIT_TUNNEL_H__
 | 
			
		||||
#define TRANSIT_TUNNEL_H__
 | 
			
		||||
 | 
			
		||||
#include <inttypes.h>
 | 
			
		||||
#include <vector>
 | 
			
		||||
#include <mutex>
 | 
			
		||||
#include <memory>
 | 
			
		||||
#include "TunnelCrypto.h"
 | 
			
		||||
#include "I2NPProtocol.h"
 | 
			
		||||
#include "TunnelEndpoint.h"
 | 
			
		||||
#include "TunnelGateway.h"
 | 
			
		||||
#include "TunnelBase.h"
 | 
			
		||||
 | 
			
		||||
namespace i2p
 | 
			
		||||
{
 | 
			
		||||
namespace tunnel
 | 
			
		||||
{   
 | 
			
		||||
    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; };
 | 
			
		||||
 | 
			
		||||
            // implements TunnelBase
 | 
			
		||||
            void SendTunnelDataMsg (std::shared_ptr<i2p::I2NPMessage> msg);
 | 
			
		||||
            void HandleTunnelDataMsg (std::shared_ptr<const i2p::I2NPMessage> tunnelMsg);
 | 
			
		||||
            void EncryptTunnelMsg (std::shared_ptr<const I2NPMessage> in, std::shared_ptr<I2NPMessage> 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;
 | 
			
		||||
    };  
 | 
			
		||||
 | 
			
		||||
    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 ();
 | 
			
		||||
 | 
			
		||||
            size_t GetNumTransmittedBytes () const { return m_NumTransmittedBytes; };
 | 
			
		||||
            void HandleTunnelDataMsg (std::shared_ptr<const i2p::I2NPMessage> tunnelMsg);
 | 
			
		||||
            void FlushTunnelDataMsgs ();
 | 
			
		||||
 | 
			
		||||
        private:
 | 
			
		||||
 | 
			
		||||
            size_t m_NumTransmittedBytes;
 | 
			
		||||
            std::vector<std::shared_ptr<i2p::I2NPMessage> > 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) {};
 | 
			
		||||
 | 
			
		||||
            void SendTunnelDataMsg (std::shared_ptr<i2p::I2NPMessage> msg);
 | 
			
		||||
            void FlushTunnelDataMsgs ();
 | 
			
		||||
            size_t GetNumTransmittedBytes () const { return m_Gateway.GetNumSentBytes (); };
 | 
			
		||||
            
 | 
			
		||||
        private:
 | 
			
		||||
 | 
			
		||||
            std::mutex m_SendMutex;
 | 
			
		||||
            TunnelGateway m_Gateway;
 | 
			
		||||
    };  
 | 
			
		||||
 | 
			
		||||
    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
 | 
			
		||||
 | 
			
		||||
            void HandleTunnelDataMsg (std::shared_ptr<const i2p::I2NPMessage> 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);
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										732
									
								
								core/tunnel/Tunnel.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										732
									
								
								core/tunnel/Tunnel.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,732 @@
 | 
			
		|||
#include <string.h>
 | 
			
		||||
#include "util/I2PEndian.h"
 | 
			
		||||
#include <thread>
 | 
			
		||||
#include <algorithm>
 | 
			
		||||
#include <vector> 
 | 
			
		||||
#include <cryptopp/sha.h>
 | 
			
		||||
#include "RouterContext.h"
 | 
			
		||||
#include "util/Log.h"
 | 
			
		||||
#include "util/Timestamp.h"
 | 
			
		||||
#include "I2NPProtocol.h"
 | 
			
		||||
#include "transport/Transports.h"
 | 
			
		||||
#include "NetDb.h"
 | 
			
		||||
#include "Tunnel.h"
 | 
			
		||||
 | 
			
		||||
namespace i2p
 | 
			
		||||
{   
 | 
			
		||||
namespace tunnel
 | 
			
		||||
{       
 | 
			
		||||
    
 | 
			
		||||
    Tunnel::Tunnel (std::shared_ptr<const TunnelConfig> config): 
 | 
			
		||||
        m_Config (config), m_Pool (nullptr), m_State (eTunnelStatePending), m_IsRecreated (false)
 | 
			
		||||
    {
 | 
			
		||||
    }   
 | 
			
		||||
 | 
			
		||||
    Tunnel::~Tunnel ()
 | 
			
		||||
    {
 | 
			
		||||
    }   
 | 
			
		||||
 | 
			
		||||
    void Tunnel::Build (uint32_t replyMsgID, std::shared_ptr<OutboundTunnel> 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<int> 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); 
 | 
			
		||||
        }   
 | 
			
		||||
 | 
			
		||||
        // 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;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        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<const I2NPMessage> in, std::shared_ptr<I2NPMessage> 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<i2p::I2NPMessage> msg)
 | 
			
		||||
    {
 | 
			
		||||
        LogPrint (eLogInfo, "Can't send I2NP messages without delivery instructions");  
 | 
			
		||||
    }   
 | 
			
		||||
 | 
			
		||||
    void InboundTunnel::HandleTunnelDataMsg (std::shared_ptr<const I2NPMessage> 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<i2p::I2NPMessage> 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<std::mutex> l(m_SendMutex);
 | 
			
		||||
        m_Gateway.SendTunnelDataMsg (block);
 | 
			
		||||
    }
 | 
			
		||||
        
 | 
			
		||||
    void OutboundTunnel::SendTunnelDataMsg (const std::vector<TunnelMessageBlock>& msgs)
 | 
			
		||||
    {
 | 
			
		||||
        std::unique_lock<std::mutex> l(m_SendMutex);
 | 
			
		||||
        for (auto& it : msgs)
 | 
			
		||||
            m_Gateway.PutTunnelDataMsg (it);
 | 
			
		||||
        m_Gateway.SendBuffer ();
 | 
			
		||||
    }   
 | 
			
		||||
    
 | 
			
		||||
    void OutboundTunnel::HandleTunnelDataMsg (std::shared_ptr<const i2p::I2NPMessage> 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<InboundTunnel> 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<InboundTunnel> Tunnels::GetPendingInboundTunnel (uint32_t replyMsgID)
 | 
			
		||||
    {
 | 
			
		||||
        return GetPendingTunnel (replyMsgID, m_PendingInboundTunnels);  
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    std::shared_ptr<OutboundTunnel> Tunnels::GetPendingOutboundTunnel (uint32_t replyMsgID)
 | 
			
		||||
    {
 | 
			
		||||
        return GetPendingTunnel (replyMsgID, m_PendingOutboundTunnels); 
 | 
			
		||||
    }       
 | 
			
		||||
 | 
			
		||||
    template<class TTunnel>     
 | 
			
		||||
    std::shared_ptr<TTunnel> Tunnels::GetPendingTunnel (uint32_t replyMsgID, const std::map<uint32_t, std::shared_ptr<TTunnel> >& 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<InboundTunnel> Tunnels::GetNextInboundTunnel ()
 | 
			
		||||
    {
 | 
			
		||||
        std::shared_ptr<InboundTunnel> 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<OutboundTunnel> Tunnels::GetNextOutboundTunnel ()
 | 
			
		||||
    {
 | 
			
		||||
        CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator ();
 | 
			
		||||
        uint32_t ind = rnd.GenerateWord32 (0, m_OutboundTunnels.size () - 1), i = 0;
 | 
			
		||||
        std::shared_ptr<OutboundTunnel> tunnel;
 | 
			
		||||
        for (auto it: m_OutboundTunnels)
 | 
			
		||||
        {   
 | 
			
		||||
            if (it->IsEstablished ())
 | 
			
		||||
            {
 | 
			
		||||
                tunnel = it;
 | 
			
		||||
                i++;
 | 
			
		||||
            }
 | 
			
		||||
            if (i > ind && tunnel) break;
 | 
			
		||||
        }   
 | 
			
		||||
        return tunnel;
 | 
			
		||||
    }   
 | 
			
		||||
 | 
			
		||||
    std::shared_ptr<TunnelPool> Tunnels::CreateTunnelPool (i2p::garlic::GarlicDestination * localDestination, int numInboundHops, int numOutboundHops, int numInboundTunnels, int numOutboundTunnels)
 | 
			
		||||
    {
 | 
			
		||||
        auto pool = std::make_shared<TunnelPool> (localDestination, numInboundHops, numOutboundHops, numInboundTunnels, numOutboundTunnels);
 | 
			
		||||
        std::unique_lock<std::mutex> l(m_PoolsMutex);
 | 
			
		||||
        m_Pools.push_back (pool);
 | 
			
		||||
        return pool;
 | 
			
		||||
    }   
 | 
			
		||||
 | 
			
		||||
    void Tunnels::DeleteTunnelPool (std::shared_ptr<TunnelPool> pool)
 | 
			
		||||
    {
 | 
			
		||||
        if (pool)
 | 
			
		||||
        {   
 | 
			
		||||
            StopTunnelPool (pool);
 | 
			
		||||
            {
 | 
			
		||||
                std::unique_lock<std::mutex> l(m_PoolsMutex);
 | 
			
		||||
                m_Pools.remove (pool);
 | 
			
		||||
            }   
 | 
			
		||||
        }   
 | 
			
		||||
    }   
 | 
			
		||||
 | 
			
		||||
    void Tunnels::StopTunnelPool (std::shared_ptr<TunnelPool> pool)
 | 
			
		||||
    {
 | 
			
		||||
        if (pool)
 | 
			
		||||
        {
 | 
			
		||||
            pool->SetActive (false);
 | 
			
		||||
            pool->DetachTunnels ();
 | 
			
		||||
        }   
 | 
			
		||||
    }   
 | 
			
		||||
        
 | 
			
		||||
    void Tunnels::AddTransitTunnel (TransitTunnel * tunnel)
 | 
			
		||||
    {
 | 
			
		||||
        std::unique_lock<std::mutex> 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::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<I2NPMessage> 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::ManagePendingTunnels ()
 | 
			
		||||
    {
 | 
			
		||||
        ManagePendingTunnels (m_PendingInboundTunnels);
 | 
			
		||||
        ManagePendingTunnels (m_PendingOutboundTunnels);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    template<class PendingTunnels>
 | 
			
		||||
    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<OutboundTunnel> (
 | 
			
		||||
                std::make_shared<TunnelConfig> (std::vector<std::shared_ptr<const i2p::data::RouterInfo> > { 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<InboundTunnel> (
 | 
			
		||||
                std::make_shared<TunnelConfig> (std::vector<std::shared_ptr<const i2p::data::RouterInfo> > { 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<std::mutex> l(m_TransitTunnelsMutex);
 | 
			
		||||
                    it = m_TransitTunnels.erase (it);
 | 
			
		||||
                }   
 | 
			
		||||
                delete tmp;
 | 
			
		||||
            }   
 | 
			
		||||
            else 
 | 
			
		||||
                it++;
 | 
			
		||||
        }
 | 
			
		||||
    }   
 | 
			
		||||
 | 
			
		||||
    void Tunnels::ManageTunnelPools ()
 | 
			
		||||
    {
 | 
			
		||||
        std::unique_lock<std::mutex> 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<I2NPMessage> msg)
 | 
			
		||||
    {
 | 
			
		||||
        if (msg) m_Queue.Put (msg);     
 | 
			
		||||
    }   
 | 
			
		||||
 | 
			
		||||
    void Tunnels::PostTunnelData (const std::vector<std::shared_ptr<I2NPMessage> >& msgs)
 | 
			
		||||
    {
 | 
			
		||||
        m_Queue.Put (msgs);
 | 
			
		||||
    }   
 | 
			
		||||
        
 | 
			
		||||
    template<class TTunnel>
 | 
			
		||||
    std::shared_ptr<TTunnel> Tunnels::CreateTunnel (std::shared_ptr<TunnelConfig> config, std::shared_ptr<OutboundTunnel> outboundTunnel)
 | 
			
		||||
    {
 | 
			
		||||
        auto newTunnel = std::make_shared<TTunnel> (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<InboundTunnel> tunnel)
 | 
			
		||||
    {
 | 
			
		||||
        m_PendingInboundTunnels[replyMsgID] = tunnel; 
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void Tunnels::AddPendingTunnel (uint32_t replyMsgID, std::shared_ptr<OutboundTunnel> tunnel)
 | 
			
		||||
    {
 | 
			
		||||
        m_PendingOutboundTunnels[replyMsgID] = tunnel; 
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void Tunnels::AddOutboundTunnel (std::shared_ptr<OutboundTunnel> 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<InboundTunnel> newTunnel)
 | 
			
		||||
    {
 | 
			
		||||
        m_InboundTunnels[newTunnel->GetTunnelID ()] = newTunnel;
 | 
			
		||||
        auto pool = newTunnel->GetTunnelPool ();
 | 
			
		||||
        if (!pool)
 | 
			
		||||
        {       
 | 
			
		||||
            // build symmetric outbound tunnel
 | 
			
		||||
            CreateTunnel<OutboundTunnel> (newTunnel->GetTunnelConfig ()->Invert (), GetNextOutboundTunnel ());      
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            if (pool->IsActive ())
 | 
			
		||||
                pool->TunnelCreated (newTunnel);
 | 
			
		||||
            else
 | 
			
		||||
                newTunnel->SetTunnelPool (nullptr);
 | 
			
		||||
        }   
 | 
			
		||||
    }   
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
    void Tunnels::CreateZeroHopsInboundTunnel ()
 | 
			
		||||
    {
 | 
			
		||||
        CreateTunnel<InboundTunnel> (
 | 
			
		||||
            std::make_shared<TunnelConfig> (std::vector<std::shared_ptr<const i2p::data::RouterInfo> >
 | 
			
		||||
                { 
 | 
			
		||||
                    i2p::context.GetSharedRouterInfo ()
 | 
			
		||||
                }));
 | 
			
		||||
    }   
 | 
			
		||||
 | 
			
		||||
    int Tunnels::GetTransitTunnelsExpirationTimeout ()
 | 
			
		||||
    {
 | 
			
		||||
        int timeout = 0;
 | 
			
		||||
        uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
 | 
			
		||||
        std::unique_lock<std::mutex> l(m_TransitTunnelsMutex);
 | 
			
		||||
        for (auto it: m_TransitTunnels)
 | 
			
		||||
        {
 | 
			
		||||
            int t = it.second->GetCreationTime () + TUNNEL_EXPIRATION_TIMEOUT - ts;
 | 
			
		||||
            if (t > timeout) timeout = t;
 | 
			
		||||
        }   
 | 
			
		||||
        return timeout;
 | 
			
		||||
    }   
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										203
									
								
								core/tunnel/Tunnel.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										203
									
								
								core/tunnel/Tunnel.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,203 @@
 | 
			
		|||
#ifndef TUNNEL_H__
 | 
			
		||||
#define TUNNEL_H__
 | 
			
		||||
 | 
			
		||||
#include <inttypes.h>
 | 
			
		||||
#include <map>
 | 
			
		||||
#include <list>
 | 
			
		||||
#include <vector>
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <thread>
 | 
			
		||||
#include <mutex>
 | 
			
		||||
#include <memory>
 | 
			
		||||
#include "util/Queue.h"
 | 
			
		||||
#include "TunnelConfig.h"
 | 
			
		||||
#include "TunnelPool.h"
 | 
			
		||||
#include "TransitTunnel.h"
 | 
			
		||||
#include "TunnelEndpoint.h"
 | 
			
		||||
#include "TunnelGateway.h"
 | 
			
		||||
#include "TunnelBase.h"
 | 
			
		||||
#include "I2NPProtocol.h"
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
 | 
			
		||||
    enum TunnelState
 | 
			
		||||
    {
 | 
			
		||||
        eTunnelStatePending,
 | 
			
		||||
        eTunnelStateBuildReplyReceived,
 | 
			
		||||
        eTunnelStateBuildFailed,
 | 
			
		||||
        eTunnelStateEstablished,
 | 
			
		||||
        eTunnelStateTestFailed,
 | 
			
		||||
        eTunnelStateFailed,
 | 
			
		||||
        eTunnelStateExpiring
 | 
			
		||||
    };  
 | 
			
		||||
    
 | 
			
		||||
    class OutboundTunnel;
 | 
			
		||||
    class InboundTunnel;
 | 
			
		||||
    class Tunnel: public TunnelBase
 | 
			
		||||
    {
 | 
			
		||||
        public:
 | 
			
		||||
 | 
			
		||||
            Tunnel (std::shared_ptr<const TunnelConfig> config);
 | 
			
		||||
            ~Tunnel ();
 | 
			
		||||
 | 
			
		||||
            void Build (uint32_t replyMsgID, std::shared_ptr<OutboundTunnel> outboundTunnel = nullptr);
 | 
			
		||||
            
 | 
			
		||||
            std::shared_ptr<const TunnelConfig> 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<TunnelPool> GetTunnelPool () const { return m_Pool; };
 | 
			
		||||
            void SetTunnelPool (std::shared_ptr<TunnelPool> pool) { m_Pool = pool; };           
 | 
			
		||||
            
 | 
			
		||||
            bool HandleTunnelBuildResponse (uint8_t * msg, size_t len);
 | 
			
		||||
            
 | 
			
		||||
            // implements TunnelBase
 | 
			
		||||
            void SendTunnelDataMsg (std::shared_ptr<i2p::I2NPMessage> msg);
 | 
			
		||||
            void EncryptTunnelMsg (std::shared_ptr<const I2NPMessage> in, std::shared_ptr<I2NPMessage> 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<const TunnelConfig> m_Config;
 | 
			
		||||
            std::shared_ptr<TunnelPool> m_Pool; // pool, tunnel belongs to, or null
 | 
			
		||||
            TunnelState m_State;
 | 
			
		||||
            bool m_IsRecreated;
 | 
			
		||||
    };  
 | 
			
		||||
 | 
			
		||||
    class OutboundTunnel: public Tunnel 
 | 
			
		||||
    {
 | 
			
		||||
        public:
 | 
			
		||||
 | 
			
		||||
            OutboundTunnel (std::shared_ptr<const TunnelConfig> config): Tunnel (config), m_Gateway (this) {};
 | 
			
		||||
 | 
			
		||||
            void SendTunnelDataMsg (const uint8_t * gwHash, uint32_t gwTunnel, std::shared_ptr<i2p::I2NPMessage> msg);
 | 
			
		||||
            void SendTunnelDataMsg (const std::vector<TunnelMessageBlock>& msgs); // multiple messages
 | 
			
		||||
            std::shared_ptr<const i2p::data::RouterInfo> GetEndpointRouter () const 
 | 
			
		||||
                { return GetTunnelConfig ()->GetLastHop ()->router; }; 
 | 
			
		||||
            size_t GetNumSentBytes () const { return m_Gateway.GetNumSentBytes (); };
 | 
			
		||||
 | 
			
		||||
            // implements TunnelBase
 | 
			
		||||
            void HandleTunnelDataMsg (std::shared_ptr<const i2p::I2NPMessage> 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<InboundTunnel>
 | 
			
		||||
    {
 | 
			
		||||
        public:
 | 
			
		||||
 | 
			
		||||
            InboundTunnel (std::shared_ptr<const TunnelConfig> config): Tunnel (config), m_Endpoint (true) {};
 | 
			
		||||
            void HandleTunnelDataMsg (std::shared_ptr<const I2NPMessage> msg);
 | 
			
		||||
            size_t GetNumReceivedBytes () const { return m_Endpoint.GetNumReceivedBytes (); };
 | 
			
		||||
 | 
			
		||||
            // implements TunnelBase
 | 
			
		||||
            uint32_t GetTunnelID () const { return GetTunnelConfig ()->GetLastHop ()->nextTunnelID; };
 | 
			
		||||
        private:
 | 
			
		||||
 | 
			
		||||
            TunnelEndpoint m_Endpoint; 
 | 
			
		||||
    };  
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
    class Tunnels
 | 
			
		||||
    {   
 | 
			
		||||
        public:
 | 
			
		||||
 | 
			
		||||
            Tunnels ();
 | 
			
		||||
            ~Tunnels ();
 | 
			
		||||
            void Start ();
 | 
			
		||||
            void Stop ();       
 | 
			
		||||
            
 | 
			
		||||
            std::shared_ptr<InboundTunnel> GetInboundTunnel (uint32_t tunnelID);
 | 
			
		||||
            std::shared_ptr<InboundTunnel> GetPendingInboundTunnel (uint32_t replyMsgID);   
 | 
			
		||||
            std::shared_ptr<OutboundTunnel> GetPendingOutboundTunnel (uint32_t replyMsgID);         
 | 
			
		||||
            std::shared_ptr<InboundTunnel> GetNextInboundTunnel ();
 | 
			
		||||
            std::shared_ptr<OutboundTunnel> GetNextOutboundTunnel ();
 | 
			
		||||
            std::shared_ptr<TunnelPool> GetExploratoryPool () const { return m_ExploratoryPool; };
 | 
			
		||||
            TransitTunnel * GetTransitTunnel (uint32_t tunnelID);
 | 
			
		||||
            int GetTransitTunnelsExpirationTimeout ();
 | 
			
		||||
            void AddTransitTunnel (TransitTunnel * tunnel);
 | 
			
		||||
            void AddOutboundTunnel (std::shared_ptr<OutboundTunnel> newTunnel);
 | 
			
		||||
            void AddInboundTunnel (std::shared_ptr<InboundTunnel> newTunnel);
 | 
			
		||||
            void PostTunnelData (std::shared_ptr<I2NPMessage> msg);
 | 
			
		||||
            void PostTunnelData (const std::vector<std::shared_ptr<I2NPMessage> >& msgs);
 | 
			
		||||
            template<class TTunnel>
 | 
			
		||||
            std::shared_ptr<TTunnel> CreateTunnel (std::shared_ptr<TunnelConfig> config, std::shared_ptr<OutboundTunnel> outboundTunnel = nullptr);
 | 
			
		||||
            void AddPendingTunnel (uint32_t replyMsgID, std::shared_ptr<InboundTunnel> tunnel);
 | 
			
		||||
            void AddPendingTunnel (uint32_t replyMsgID, std::shared_ptr<OutboundTunnel> tunnel);
 | 
			
		||||
            std::shared_ptr<TunnelPool> CreateTunnelPool (i2p::garlic::GarlicDestination * localDestination, int numInboundHops, int numOuboundHops, int numInboundTunnels, int numOutboundTunnels);
 | 
			
		||||
            void DeleteTunnelPool (std::shared_ptr<TunnelPool> pool);
 | 
			
		||||
            void StopTunnelPool (std::shared_ptr<TunnelPool> pool);
 | 
			
		||||
            
 | 
			
		||||
        private:
 | 
			
		||||
        
 | 
			
		||||
            template<class TTunnel>
 | 
			
		||||
            std::shared_ptr<TTunnel> GetPendingTunnel (uint32_t replyMsgID, const std::map<uint32_t, std::shared_ptr<TTunnel> >& pendingTunnels);           
 | 
			
		||||
 | 
			
		||||
            void HandleTunnelGatewayMsg (TunnelBase * tunnel, std::shared_ptr<I2NPMessage> msg);
 | 
			
		||||
 | 
			
		||||
            void Run ();    
 | 
			
		||||
            void ManageTunnels ();
 | 
			
		||||
            void ManageOutboundTunnels ();
 | 
			
		||||
            void ManageInboundTunnels ();
 | 
			
		||||
            void ManageTransitTunnels ();
 | 
			
		||||
            void ManagePendingTunnels ();
 | 
			
		||||
            template<class PendingTunnels>
 | 
			
		||||
            void ManagePendingTunnels (PendingTunnels& pendingTunnels);
 | 
			
		||||
            void ManageTunnelPools ();
 | 
			
		||||
            
 | 
			
		||||
            void CreateZeroHopsInboundTunnel ();
 | 
			
		||||
            
 | 
			
		||||
        private:
 | 
			
		||||
 | 
			
		||||
            bool m_IsRunning;
 | 
			
		||||
            std::thread * m_Thread; 
 | 
			
		||||
            std::map<uint32_t, std::shared_ptr<InboundTunnel> > m_PendingInboundTunnels; // by replyMsgID
 | 
			
		||||
            std::map<uint32_t, std::shared_ptr<OutboundTunnel> > m_PendingOutboundTunnels; // by replyMsgID
 | 
			
		||||
            std::map<uint32_t, std::shared_ptr<InboundTunnel> > m_InboundTunnels;
 | 
			
		||||
            std::list<std::shared_ptr<OutboundTunnel> > m_OutboundTunnels;
 | 
			
		||||
            std::mutex m_TransitTunnelsMutex;
 | 
			
		||||
            std::map<uint32_t, TransitTunnel *> m_TransitTunnels;
 | 
			
		||||
            std::mutex m_PoolsMutex;
 | 
			
		||||
            std::list<std::shared_ptr<TunnelPool>> m_Pools;
 | 
			
		||||
            std::shared_ptr<TunnelPool> m_ExploratoryPool;
 | 
			
		||||
            i2p::util::Queue<std::shared_ptr<I2NPMessage> > m_Queue;
 | 
			
		||||
 | 
			
		||||
            // some stats
 | 
			
		||||
            int m_NumSuccesiveTunnelCreations, m_NumFailedTunnelCreations;
 | 
			
		||||
 | 
			
		||||
        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;
 | 
			
		||||
            }       
 | 
			
		||||
    };  
 | 
			
		||||
 | 
			
		||||
    extern Tunnels tunnels;
 | 
			
		||||
}   
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										69
									
								
								core/tunnel/TunnelBase.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								core/tunnel/TunnelBase.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,69 @@
 | 
			
		|||
#ifndef TUNNEL_BASE_H__
 | 
			
		||||
#define TUNNEL_BASE_H__
 | 
			
		||||
 | 
			
		||||
#include <inttypes.h>
 | 
			
		||||
#include <memory>
 | 
			
		||||
#include "util/Timestamp.h"
 | 
			
		||||
#include "I2NPProtocol.h"
 | 
			
		||||
#include "Identity.h"
 | 
			
		||||
 | 
			
		||||
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<I2NPMessage> data;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    class TunnelBase
 | 
			
		||||
    {
 | 
			
		||||
        public:
 | 
			
		||||
 | 
			
		||||
            //WARNING!!! GetSecondsSinceEpoch() return uint64_t
 | 
			
		||||
            TunnelBase (): m_CreationTime (i2p::util::GetSecondsSinceEpoch ()) {};
 | 
			
		||||
            virtual ~TunnelBase () {};
 | 
			
		||||
            
 | 
			
		||||
            virtual void HandleTunnelDataMsg (std::shared_ptr<const i2p::I2NPMessage> tunnelMsg) = 0;
 | 
			
		||||
            virtual void SendTunnelDataMsg (std::shared_ptr<i2p::I2NPMessage> msg) = 0;
 | 
			
		||||
            virtual void FlushTunnelDataMsgs () {};
 | 
			
		||||
            virtual void EncryptTunnelMsg (std::shared_ptr<const I2NPMessage> in, std::shared_ptr<I2NPMessage> 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; };
 | 
			
		||||
 | 
			
		||||
        private:
 | 
			
		||||
            
 | 
			
		||||
            uint32_t m_CreationTime; // seconds since epoch
 | 
			
		||||
    };  
 | 
			
		||||
 | 
			
		||||
    struct TunnelCreationTimeCmp
 | 
			
		||||
    {
 | 
			
		||||
        bool operator() (std::shared_ptr<const TunnelBase> t1, std::shared_ptr<const TunnelBase> t2) const
 | 
			
		||||
        {   
 | 
			
		||||
            if (t1->GetCreationTime () != t2->GetCreationTime ())
 | 
			
		||||
                return t1->GetCreationTime () > t2->GetCreationTime (); 
 | 
			
		||||
            else
 | 
			
		||||
                return t1 < t2;
 | 
			
		||||
        };
 | 
			
		||||
    };  
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										233
									
								
								core/tunnel/TunnelConfig.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										233
									
								
								core/tunnel/TunnelConfig.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,233 @@
 | 
			
		|||
#ifndef TUNNEL_CONFIG_H__
 | 
			
		||||
#define TUNNEL_CONFIG_H__
 | 
			
		||||
 | 
			
		||||
#include <inttypes.h>
 | 
			
		||||
#include <sstream>
 | 
			
		||||
#include <vector>
 | 
			
		||||
#include <memory>
 | 
			
		||||
#include "TunnelCrypto.h"
 | 
			
		||||
#include "RouterInfo.h"
 | 
			
		||||
#include "RouterContext.h"
 | 
			
		||||
#include "util/Timestamp.h"
 | 
			
		||||
 | 
			
		||||
namespace i2p
 | 
			
		||||
{
 | 
			
		||||
namespace tunnel
 | 
			
		||||
{
 | 
			
		||||
    struct TunnelHopConfig
 | 
			
		||||
    {
 | 
			
		||||
        std::shared_ptr<const i2p::data::RouterInfo> 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<const i2p::data::RouterInfo> 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;
 | 
			
		||||
        }   
 | 
			
		||||
 | 
			
		||||
        void SetNextRouter (std::shared_ptr<const i2p::data::RouterInfo> 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 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);
 | 
			
		||||
        }   
 | 
			
		||||
    };  
 | 
			
		||||
 | 
			
		||||
    class TunnelConfig: public std::enable_shared_from_this<TunnelConfig>
 | 
			
		||||
    {
 | 
			
		||||
        public:         
 | 
			
		||||
            
 | 
			
		||||
 | 
			
		||||
            TunnelConfig (std::vector<std::shared_ptr<const i2p::data::RouterInfo> > peers, 
 | 
			
		||||
                std::shared_ptr<const TunnelConfig> 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;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            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; }
 | 
			
		||||
 | 
			
		||||
            std::vector<std::shared_ptr<const i2p::data::RouterInfo> > GetPeers () const
 | 
			
		||||
            {
 | 
			
		||||
                std::vector<std::shared_ptr<const i2p::data::RouterInfo> > 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"; 
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            std::shared_ptr<TunnelConfig> 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<TunnelConfig>(peers, shared_from_this ()) : std::make_shared<TunnelConfig>(peers);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            std::shared_ptr<TunnelConfig> Clone (std::shared_ptr<const TunnelConfig> replyTunnelConfig = nullptr) const
 | 
			
		||||
            {
 | 
			
		||||
                return std::make_shared<TunnelConfig> (GetPeers (), replyTunnelConfig);
 | 
			
		||||
            }   
 | 
			
		||||
            
 | 
			
		||||
        private:
 | 
			
		||||
 | 
			
		||||
            // this constructor can't be called from outside
 | 
			
		||||
            TunnelConfig (): m_FirstHop (nullptr), m_LastHop (nullptr)
 | 
			
		||||
            {
 | 
			
		||||
            }
 | 
			
		||||
            
 | 
			
		||||
        private:
 | 
			
		||||
 | 
			
		||||
            TunnelHopConfig * m_FirstHop, * m_LastHop;
 | 
			
		||||
    };  
 | 
			
		||||
}       
 | 
			
		||||
}   
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										88
									
								
								core/tunnel/TunnelCrypto.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										88
									
								
								core/tunnel/TunnelCrypto.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,88 @@
 | 
			
		|||
#include "TunnelCrypto.h"
 | 
			
		||||
#include "TunnelBase.h"
 | 
			
		||||
#include "crypto/AESNIMacros.h" 
 | 
			
		||||
 | 
			
		||||
namespace i2p {
 | 
			
		||||
namespace crypto {
 | 
			
		||||
 | 
			
		||||
void TunnelEncryption::SetKeys (const AESKey& layerKey, const AESKey& ivKey)
 | 
			
		||||
{
 | 
			
		||||
    m_LayerEncryption.SetKey (layerKey);
 | 
			
		||||
    m_IVEncryption.SetKey (ivKey);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void TunnelEncryption::Encrypt (const uint8_t * in, uint8_t * out)
 | 
			
		||||
{
 | 
			
		||||
#ifdef AESNI
 | 
			
		||||
    __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"
 | 
			
		||||
    );
 | 
			
		||||
#else
 | 
			
		||||
    m_IVEncryption.Encrypt ((const CipherBlock *)in, (CipherBlock *)out); // iv
 | 
			
		||||
    m_LayerEncryption.SetIV (out);
 | 
			
		||||
    m_LayerEncryption.Encrypt (in + 16, i2p::tunnel::TUNNEL_DATA_ENCRYPTED_SIZE, out + 16); // data
 | 
			
		||||
    m_IVEncryption.Encrypt ((CipherBlock *)out, (CipherBlock *)out); // double iv
 | 
			
		||||
#endif
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
void TunnelDecryption::Decrypt (const uint8_t * in, uint8_t * out)
 | 
			
		||||
{
 | 
			
		||||
#ifdef AESNI
 | 
			
		||||
    __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"
 | 
			
		||||
    );
 | 
			
		||||
#else
 | 
			
		||||
    m_IVDecryption.Decrypt ((const CipherBlock *)in, (CipherBlock *)out); // iv
 | 
			
		||||
    m_LayerDecryption.SetIV (out);  
 | 
			
		||||
    m_LayerDecryption.Decrypt (in + 16, i2p::tunnel::TUNNEL_DATA_ENCRYPTED_SIZE, out + 16); // data
 | 
			
		||||
    m_IVDecryption.Decrypt ((CipherBlock *)out, (CipherBlock *)out); // double iv
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // crypto
 | 
			
		||||
} // i2p
 | 
			
		||||
							
								
								
									
										49
									
								
								core/tunnel/TunnelCrypto.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								core/tunnel/TunnelCrypto.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,49 @@
 | 
			
		|||
#ifndef TUNNEL_CRYPTO_H__
 | 
			
		||||
#define TUNNEL_CRYPTO_H__
 | 
			
		||||
 | 
			
		||||
#include "crypto/aes.h"
 | 
			
		||||
 | 
			
		||||
namespace i2p {
 | 
			
		||||
namespace crypto {
 | 
			
		||||
 | 
			
		||||
class TunnelEncryption { // with double IV encryption
 | 
			
		||||
public:
 | 
			
		||||
    void SetKeys (const AESKey& layerKey, const AESKey& ivKey);
 | 
			
		||||
 | 
			
		||||
    void Encrypt (const uint8_t * in, uint8_t * out); // 1024 bytes (16 IV + 1008 data)     
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
 | 
			
		||||
    ECBEncryption m_IVEncryption;
 | 
			
		||||
#ifdef AESNI
 | 
			
		||||
    ECBEncryption m_LayerEncryption;
 | 
			
		||||
#else
 | 
			
		||||
    CBCEncryption m_LayerEncryption;
 | 
			
		||||
#endif
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class TunnelDecryption { // with double IV encryption
 | 
			
		||||
public:
 | 
			
		||||
 | 
			
		||||
    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) 
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
 | 
			
		||||
    ECBDecryption m_IVDecryption;
 | 
			
		||||
#ifdef AESNI
 | 
			
		||||
    ECBDecryption m_LayerDecryption;
 | 
			
		||||
#else
 | 
			
		||||
    CBCDecryption m_LayerDecryption;
 | 
			
		||||
#endif
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // crypto
 | 
			
		||||
} // i2p
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										259
									
								
								core/tunnel/TunnelEndpoint.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										259
									
								
								core/tunnel/TunnelEndpoint.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,259 @@
 | 
			
		|||
#include "util/I2PEndian.h"
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include "util/Log.h"
 | 
			
		||||
#include "NetDb.h"
 | 
			
		||||
#include "I2NPProtocol.h"
 | 
			
		||||
#include "transport/Transports.h"
 | 
			
		||||
#include "RouterContext.h"
 | 
			
		||||
#include "TunnelEndpoint.h"
 | 
			
		||||
 | 
			
		||||
namespace i2p
 | 
			
		||||
{
 | 
			
		||||
namespace tunnel
 | 
			
		||||
{
 | 
			
		||||
    TunnelEndpoint::~TunnelEndpoint ()
 | 
			
		||||
    {
 | 
			
		||||
    }   
 | 
			
		||||
    
 | 
			
		||||
    void TunnelEndpoint::HandleDecryptedTunnelDataMsg (std::shared_ptr<I2NPMessage> 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;
 | 
			
		||||
 | 
			
		||||
                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<uint32_t, TunnelMessageBlockEx>(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::AddOutOfSequenceFragment (uint32_t msgID, uint8_t fragmentNum, bool isLastFragment, std::shared_ptr<I2NPMessage> data)
 | 
			
		||||
    {
 | 
			
		||||
        auto it = m_OutOfSequenceFragments.find (msgID);
 | 
			
		||||
        if (it == m_OutOfSequenceFragments.end ())
 | 
			
		||||
            m_OutOfSequenceFragments.insert (std::pair<uint32_t, Fragment> (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);
 | 
			
		||||
        };  
 | 
			
		||||
    }   
 | 
			
		||||
}       
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										54
									
								
								core/tunnel/TunnelEndpoint.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								core/tunnel/TunnelEndpoint.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,54 @@
 | 
			
		|||
#ifndef TUNNEL_ENDPOINT_H__
 | 
			
		||||
#define TUNNEL_ENDPOINT_H__
 | 
			
		||||
 | 
			
		||||
#include <inttypes.h>
 | 
			
		||||
#include <map>
 | 
			
		||||
#include <string>
 | 
			
		||||
#include "I2NPProtocol.h"
 | 
			
		||||
#include "TunnelBase.h"
 | 
			
		||||
 | 
			
		||||
namespace i2p
 | 
			
		||||
{
 | 
			
		||||
namespace tunnel
 | 
			
		||||
{
 | 
			
		||||
    class TunnelEndpoint
 | 
			
		||||
    {   
 | 
			
		||||
        struct TunnelMessageBlockEx: public TunnelMessageBlock
 | 
			
		||||
        {
 | 
			
		||||
            uint8_t nextFragmentNum;
 | 
			
		||||
        };  
 | 
			
		||||
 | 
			
		||||
        struct Fragment
 | 
			
		||||
        {
 | 
			
		||||
            uint8_t fragmentNum;
 | 
			
		||||
            bool isLastFragment;
 | 
			
		||||
            std::shared_ptr<I2NPMessage> data;
 | 
			
		||||
        };  
 | 
			
		||||
        
 | 
			
		||||
        public:
 | 
			
		||||
 | 
			
		||||
            TunnelEndpoint (bool isInbound): m_IsInbound (isInbound), m_NumReceivedBytes (0) {};
 | 
			
		||||
            ~TunnelEndpoint ();
 | 
			
		||||
            size_t GetNumReceivedBytes () const { return m_NumReceivedBytes; };
 | 
			
		||||
            
 | 
			
		||||
            void HandleDecryptedTunnelDataMsg (std::shared_ptr<I2NPMessage> msg);
 | 
			
		||||
 | 
			
		||||
        private:
 | 
			
		||||
 | 
			
		||||
            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<I2NPMessage> data);
 | 
			
		||||
            void HandleOutOfSequenceFragment (uint32_t msgID, TunnelMessageBlockEx& msg);
 | 
			
		||||
            
 | 
			
		||||
        private:            
 | 
			
		||||
 | 
			
		||||
            std::map<uint32_t, TunnelMessageBlockEx> m_IncompleteMessages;
 | 
			
		||||
            std::map<uint32_t, Fragment> m_OutOfSequenceFragments;
 | 
			
		||||
            bool m_IsInbound;
 | 
			
		||||
            size_t m_NumReceivedBytes;
 | 
			
		||||
    };  
 | 
			
		||||
}       
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										212
									
								
								core/tunnel/TunnelGateway.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										212
									
								
								core/tunnel/TunnelGateway.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,212 @@
 | 
			
		|||
#include <string.h>
 | 
			
		||||
#include "util/I2PEndian.h"
 | 
			
		||||
#include <cryptopp/sha.h>
 | 
			
		||||
#include "util/Log.h"
 | 
			
		||||
#include "RouterContext.h"
 | 
			
		||||
#include "transport/Transports.h"
 | 
			
		||||
#include "TunnelGateway.h"
 | 
			
		||||
 | 
			
		||||
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 ()
 | 
			
		||||
    {
 | 
			
		||||
    }   
 | 
			
		||||
    
 | 
			
		||||
    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 fragments
 | 
			
		||||
        std::shared_ptr<I2NPMessage> 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 ();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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;
 | 
			
		||||
    }   
 | 
			
		||||
 | 
			
		||||
    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::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 ();
 | 
			
		||||
    }   
 | 
			
		||||
}       
 | 
			
		||||
}   
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										57
									
								
								core/tunnel/TunnelGateway.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								core/tunnel/TunnelGateway.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,57 @@
 | 
			
		|||
#ifndef TUNNEL_GATEWAY_H__
 | 
			
		||||
#define TUNNEL_GATEWAY_H__
 | 
			
		||||
 | 
			
		||||
#include <inttypes.h>
 | 
			
		||||
#include <vector>
 | 
			
		||||
#include <memory>
 | 
			
		||||
#include "I2NPProtocol.h"
 | 
			
		||||
#include "TunnelBase.h"
 | 
			
		||||
 | 
			
		||||
namespace i2p
 | 
			
		||||
{
 | 
			
		||||
namespace tunnel
 | 
			
		||||
{
 | 
			
		||||
    class TunnelGatewayBuffer
 | 
			
		||||
    {
 | 
			
		||||
        public:
 | 
			
		||||
            TunnelGatewayBuffer (uint32_t tunnelID);
 | 
			
		||||
            ~TunnelGatewayBuffer ();
 | 
			
		||||
            void PutI2NPMsg (const TunnelMessageBlock& block);  
 | 
			
		||||
            const std::vector<std::shared_ptr<I2NPMessage> >& GetTunnelDataMsgs () const { return m_TunnelDataMsgs; };
 | 
			
		||||
            void ClearTunnelDataMsgs ();
 | 
			
		||||
            void CompleteCurrentTunnelDataMessage ();
 | 
			
		||||
 | 
			
		||||
        private:
 | 
			
		||||
 | 
			
		||||
            void CreateCurrentTunnelDataMessage ();
 | 
			
		||||
            
 | 
			
		||||
        private:
 | 
			
		||||
 | 
			
		||||
            uint32_t m_TunnelID;
 | 
			
		||||
            std::vector<std::shared_ptr<I2NPMessage> > m_TunnelDataMsgs;
 | 
			
		||||
            std::shared_ptr<I2NPMessage> m_CurrentTunnelDataMsg;
 | 
			
		||||
            size_t m_RemainingSize;
 | 
			
		||||
            uint8_t m_NonZeroRandomBuffer[TUNNEL_DATA_MAX_PAYLOAD_SIZE];
 | 
			
		||||
    };  
 | 
			
		||||
 | 
			
		||||
    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:
 | 
			
		||||
 | 
			
		||||
            TunnelBase * m_Tunnel;
 | 
			
		||||
            TunnelGatewayBuffer m_Buffer;
 | 
			
		||||
            size_t m_NumSentBytes;
 | 
			
		||||
    };  
 | 
			
		||||
}       
 | 
			
		||||
}   
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										435
									
								
								core/tunnel/TunnelPool.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										435
									
								
								core/tunnel/TunnelPool.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,435 @@
 | 
			
		|||
#include <algorithm>
 | 
			
		||||
#include "util/I2PEndian.h"
 | 
			
		||||
#include "crypto/CryptoConst.h"
 | 
			
		||||
#include "Tunnel.h"
 | 
			
		||||
#include "NetDb.h"
 | 
			
		||||
#include "util/Timestamp.h"
 | 
			
		||||
#include "Garlic.h"
 | 
			
		||||
#include "transport/Transports.h"
 | 
			
		||||
#include "TunnelPool.h"
 | 
			
		||||
 | 
			
		||||
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 ()
 | 
			
		||||
    {
 | 
			
		||||
        DetachTunnels ();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void TunnelPool::SetExplicitPeers (std::shared_ptr<std::vector<i2p::data::IdentHash> > 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<std::mutex> l(m_InboundTunnelsMutex);  
 | 
			
		||||
            for (auto it: m_InboundTunnels)
 | 
			
		||||
                it->SetTunnelPool (nullptr);
 | 
			
		||||
            m_InboundTunnels.clear ();
 | 
			
		||||
        }
 | 
			
		||||
        {
 | 
			
		||||
            std::unique_lock<std::mutex> l(m_OutboundTunnelsMutex);
 | 
			
		||||
            for (auto it: m_OutboundTunnels)
 | 
			
		||||
                it->SetTunnelPool (nullptr);
 | 
			
		||||
            m_OutboundTunnels.clear ();
 | 
			
		||||
        }
 | 
			
		||||
        m_Tests.clear ();
 | 
			
		||||
    }   
 | 
			
		||||
        
 | 
			
		||||
    void TunnelPool::TunnelCreated (std::shared_ptr<InboundTunnel> createdTunnel)
 | 
			
		||||
    {
 | 
			
		||||
        if (!m_IsActive) return;
 | 
			
		||||
        {
 | 
			
		||||
            std::unique_lock<std::mutex> l(m_InboundTunnelsMutex);
 | 
			
		||||
            m_InboundTunnels.insert (createdTunnel);
 | 
			
		||||
        }
 | 
			
		||||
        if (m_LocalDestination)
 | 
			
		||||
            m_LocalDestination->SetLeaseSetUpdated ();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void TunnelPool::TunnelExpired (std::shared_ptr<InboundTunnel> expiredTunnel)
 | 
			
		||||
    {
 | 
			
		||||
        if (expiredTunnel)
 | 
			
		||||
        {   
 | 
			
		||||
            expiredTunnel->SetTunnelPool (nullptr);
 | 
			
		||||
            for (auto it: m_Tests)
 | 
			
		||||
                if (it.second.second == expiredTunnel) it.second.second = nullptr;
 | 
			
		||||
 | 
			
		||||
            std::unique_lock<std::mutex> l(m_InboundTunnelsMutex);
 | 
			
		||||
            m_InboundTunnels.erase (expiredTunnel);
 | 
			
		||||
        }   
 | 
			
		||||
    }   
 | 
			
		||||
 | 
			
		||||
    void TunnelPool::TunnelCreated (std::shared_ptr<OutboundTunnel> createdTunnel)
 | 
			
		||||
    {
 | 
			
		||||
        if (!m_IsActive) return;
 | 
			
		||||
        {
 | 
			
		||||
            std::unique_lock<std::mutex> l(m_OutboundTunnelsMutex);
 | 
			
		||||
            m_OutboundTunnels.insert (createdTunnel);
 | 
			
		||||
        }
 | 
			
		||||
        //CreatePairedInboundTunnel (createdTunnel);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void TunnelPool::TunnelExpired (std::shared_ptr<OutboundTunnel> expiredTunnel)
 | 
			
		||||
    {
 | 
			
		||||
        if (expiredTunnel)
 | 
			
		||||
        {
 | 
			
		||||
            expiredTunnel->SetTunnelPool (nullptr);
 | 
			
		||||
            for (auto it: m_Tests)
 | 
			
		||||
                if (it.second.first == expiredTunnel) it.second.first = nullptr;
 | 
			
		||||
 | 
			
		||||
            std::unique_lock<std::mutex> l(m_OutboundTunnelsMutex);
 | 
			
		||||
            m_OutboundTunnels.erase (expiredTunnel);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
        
 | 
			
		||||
    std::vector<std::shared_ptr<InboundTunnel> > TunnelPool::GetInboundTunnels (int num) const
 | 
			
		||||
    {
 | 
			
		||||
        std::vector<std::shared_ptr<InboundTunnel> > v;
 | 
			
		||||
        int i = 0;
 | 
			
		||||
        std::unique_lock<std::mutex> 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<OutboundTunnel> TunnelPool::GetNextOutboundTunnel (std::shared_ptr<OutboundTunnel> excluded) const
 | 
			
		||||
    {
 | 
			
		||||
        std::unique_lock<std::mutex> l(m_OutboundTunnelsMutex); 
 | 
			
		||||
        return GetNextTunnel (m_OutboundTunnels, excluded);
 | 
			
		||||
    }   
 | 
			
		||||
 | 
			
		||||
    std::shared_ptr<InboundTunnel> TunnelPool::GetNextInboundTunnel (std::shared_ptr<InboundTunnel> excluded) const
 | 
			
		||||
    {
 | 
			
		||||
        std::unique_lock<std::mutex> l(m_InboundTunnelsMutex);  
 | 
			
		||||
        return GetNextTunnel (m_InboundTunnels, excluded);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    template<class TTunnels>
 | 
			
		||||
    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<OutboundTunnel> TunnelPool::GetNewOutboundTunnel (std::shared_ptr<OutboundTunnel> old) const
 | 
			
		||||
    {
 | 
			
		||||
        if (old && old->IsEstablished ()) return old;
 | 
			
		||||
        std::shared_ptr<OutboundTunnel> tunnel; 
 | 
			
		||||
        if (old)
 | 
			
		||||
        {
 | 
			
		||||
            std::unique_lock<std::mutex> 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<std::mutex> 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<std::mutex> 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<std::mutex> 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<std::mutex> 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<I2NPMessage> msg)
 | 
			
		||||
    {
 | 
			
		||||
        if (m_LocalDestination)
 | 
			
		||||
            m_LocalDestination->ProcessGarlicMessage (msg);
 | 
			
		||||
        else
 | 
			
		||||
            LogPrint (eLogWarning, "Local destination doesn't exist. Dropped");
 | 
			
		||||
    }   
 | 
			
		||||
        
 | 
			
		||||
    void TunnelPool::ProcessDeliveryStatus (std::shared_ptr<I2NPMessage> 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");
 | 
			
		||||
        }   
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    std::shared_ptr<const i2p::data::RouterInfo> TunnelPool::SelectNextHop (std::shared_ptr<const i2p::data::RouterInfo> 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; 
 | 
			
		||||
    }   
 | 
			
		||||
 | 
			
		||||
    bool TunnelPool::SelectPeers (std::vector<std::shared_ptr<const i2p::data::RouterInfo> >& 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<std::shared_ptr<const i2p::data::RouterInfo> >& hops, bool isInbound)
 | 
			
		||||
    {
 | 
			
		||||
        int size = m_ExplicitPeers->size ();
 | 
			
		||||
        std::vector<int> 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<std::shared_ptr<const i2p::data::RouterInfo> > hops;
 | 
			
		||||
        if (SelectPeers (hops, true))
 | 
			
		||||
        {
 | 
			
		||||
            std::reverse (hops.begin (), hops.end ());  
 | 
			
		||||
            auto tunnel = tunnels.CreateTunnel<InboundTunnel> (std::make_shared<TunnelConfig> (hops), outboundTunnel);
 | 
			
		||||
            tunnel->SetTunnelPool (shared_from_this ());
 | 
			
		||||
        }   
 | 
			
		||||
        else
 | 
			
		||||
            LogPrint (eLogError, "Can't create inbound tunnel. No peers available");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void TunnelPool::RecreateInboundTunnel (std::shared_ptr<InboundTunnel> tunnel)
 | 
			
		||||
    {
 | 
			
		||||
        auto outboundTunnel = GetNextOutboundTunnel ();
 | 
			
		||||
        if (!outboundTunnel)
 | 
			
		||||
            outboundTunnel = tunnels.GetNextOutboundTunnel ();
 | 
			
		||||
        LogPrint ("Re-creating destination inbound tunnel...");
 | 
			
		||||
        auto newTunnel = tunnels.CreateTunnel<InboundTunnel> (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<std::shared_ptr<const i2p::data::RouterInfo> > hops;
 | 
			
		||||
            if (SelectPeers (hops, false))
 | 
			
		||||
            {   
 | 
			
		||||
                auto tunnel = tunnels.CreateTunnel<OutboundTunnel> (
 | 
			
		||||
                    std::make_shared<TunnelConfig> (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<OutboundTunnel> tunnel)
 | 
			
		||||
    {
 | 
			
		||||
        auto inboundTunnel = GetNextInboundTunnel ();
 | 
			
		||||
        if (!inboundTunnel)
 | 
			
		||||
            inboundTunnel = tunnels.GetNextInboundTunnel ();
 | 
			
		||||
        if (inboundTunnel)
 | 
			
		||||
        {   
 | 
			
		||||
            LogPrint ("Re-creating destination outbound tunnel...");
 | 
			
		||||
            auto newTunnel = tunnels.CreateTunnel<OutboundTunnel> (
 | 
			
		||||
                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> outboundTunnel)
 | 
			
		||||
    {
 | 
			
		||||
        LogPrint (eLogInfo, "Creating paired inbound tunnel...");
 | 
			
		||||
        auto tunnel = tunnels.CreateTunnel<InboundTunnel> (outboundTunnel->GetTunnelConfig ()->Invert (), outboundTunnel);
 | 
			
		||||
        tunnel->SetTunnelPool (shared_from_this ());
 | 
			
		||||
    }   
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										91
									
								
								core/tunnel/TunnelPool.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										91
									
								
								core/tunnel/TunnelPool.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,91 @@
 | 
			
		|||
#ifndef TUNNEL_POOL__
 | 
			
		||||
#define TUNNEL_POOL__
 | 
			
		||||
 | 
			
		||||
#include <inttypes.h>
 | 
			
		||||
#include <set>
 | 
			
		||||
#include <vector>
 | 
			
		||||
#include <utility>
 | 
			
		||||
#include <mutex>
 | 
			
		||||
#include <memory>
 | 
			
		||||
#include "Identity.h"
 | 
			
		||||
#include "LeaseSet.h"
 | 
			
		||||
#include "RouterInfo.h"
 | 
			
		||||
#include "I2NPProtocol.h"
 | 
			
		||||
#include "TunnelBase.h"
 | 
			
		||||
#include "RouterContext.h"
 | 
			
		||||
#include "Garlic.h"
 | 
			
		||||
 | 
			
		||||
namespace i2p
 | 
			
		||||
{
 | 
			
		||||
namespace tunnel
 | 
			
		||||
{
 | 
			
		||||
    class Tunnel;
 | 
			
		||||
    class InboundTunnel;
 | 
			
		||||
    class OutboundTunnel;
 | 
			
		||||
 | 
			
		||||
    class TunnelPool: public std::enable_shared_from_this<TunnelPool> // 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<std::vector<i2p::data::IdentHash> > explicitPeers);
 | 
			
		||||
 | 
			
		||||
            void CreateTunnels ();
 | 
			
		||||
            void TunnelCreated (std::shared_ptr<InboundTunnel> createdTunnel);
 | 
			
		||||
            void TunnelExpired (std::shared_ptr<InboundTunnel> expiredTunnel);
 | 
			
		||||
            void TunnelCreated (std::shared_ptr<OutboundTunnel> createdTunnel);
 | 
			
		||||
            void TunnelExpired (std::shared_ptr<OutboundTunnel> expiredTunnel);
 | 
			
		||||
            void RecreateInboundTunnel (std::shared_ptr<InboundTunnel> tunnel);
 | 
			
		||||
            void RecreateOutboundTunnel (std::shared_ptr<OutboundTunnel> tunnel);
 | 
			
		||||
            std::vector<std::shared_ptr<InboundTunnel> > GetInboundTunnels (int num) const;
 | 
			
		||||
            std::shared_ptr<OutboundTunnel> GetNextOutboundTunnel (std::shared_ptr<OutboundTunnel> excluded = nullptr) const;
 | 
			
		||||
            std::shared_ptr<InboundTunnel> GetNextInboundTunnel (std::shared_ptr<InboundTunnel> excluded = nullptr) const;      
 | 
			
		||||
            std::shared_ptr<OutboundTunnel> GetNewOutboundTunnel (std::shared_ptr<OutboundTunnel> old) const;
 | 
			
		||||
 | 
			
		||||
            void TestTunnels ();
 | 
			
		||||
            void ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg);
 | 
			
		||||
            void ProcessDeliveryStatus (std::shared_ptr<I2NPMessage> msg);
 | 
			
		||||
 | 
			
		||||
            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> outboundTunnel);
 | 
			
		||||
            template<class TTunnels>
 | 
			
		||||
            typename TTunnels::value_type GetNextTunnel (TTunnels& tunnels, typename TTunnels::value_type excluded) const;
 | 
			
		||||
            std::shared_ptr<const i2p::data::RouterInfo> SelectNextHop (std::shared_ptr<const i2p::data::RouterInfo> prevHop) const;
 | 
			
		||||
            bool SelectPeers (std::vector<std::shared_ptr<const i2p::data::RouterInfo> >& hops, bool isInbound);
 | 
			
		||||
            bool SelectExplicitPeers (std::vector<std::shared_ptr<const i2p::data::RouterInfo> >& hops, bool isInbound);            
 | 
			
		||||
 | 
			
		||||
        private:
 | 
			
		||||
 | 
			
		||||
            i2p::garlic::GarlicDestination * m_LocalDestination;
 | 
			
		||||
            int m_NumInboundHops, m_NumOutboundHops, m_NumInboundTunnels, m_NumOutboundTunnels;
 | 
			
		||||
            std::shared_ptr<std::vector<i2p::data::IdentHash> > m_ExplicitPeers;    
 | 
			
		||||
            mutable std::mutex m_InboundTunnelsMutex;
 | 
			
		||||
            std::set<std::shared_ptr<InboundTunnel>, TunnelCreationTimeCmp> m_InboundTunnels; // recent tunnel appears first
 | 
			
		||||
            mutable std::mutex m_OutboundTunnelsMutex;
 | 
			
		||||
            std::set<std::shared_ptr<OutboundTunnel>, TunnelCreationTimeCmp> m_OutboundTunnels;
 | 
			
		||||
            std::map<uint32_t, std::pair<std::shared_ptr<OutboundTunnel>, std::shared_ptr<InboundTunnel> > > m_Tests;
 | 
			
		||||
            bool m_IsActive;
 | 
			
		||||
 | 
			
		||||
        public:
 | 
			
		||||
 | 
			
		||||
            // for HTTP only
 | 
			
		||||
            const decltype(m_OutboundTunnels)& GetOutboundTunnels () const { return m_OutboundTunnels; };
 | 
			
		||||
            const decltype(m_InboundTunnels)& GetInboundTunnels () const { return m_InboundTunnels; };
 | 
			
		||||
 | 
			
		||||
    };  
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										83
									
								
								core/util/I2PEndian.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										83
									
								
								core/util/I2PEndian.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,83 @@
 | 
			
		|||
#include "I2PEndian.h"
 | 
			
		||||
 | 
			
		||||
// http://habrahabr.ru/post/121811/
 | 
			
		||||
// http://codepad.org/2ycmkz2y
 | 
			
		||||
 | 
			
		||||
#include "LittleBigEndian.h"
 | 
			
		||||
 | 
			
		||||
#ifdef NEEDS_LOCAL_ENDIAN
 | 
			
		||||
uint16_t htobe16(uint16_t int16)
 | 
			
		||||
{
 | 
			
		||||
    BigEndian<uint16_t> u16(int16);
 | 
			
		||||
    return u16.raw_value;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint32_t htobe32(uint32_t int32)
 | 
			
		||||
{
 | 
			
		||||
    BigEndian<uint32_t> u32(int32);
 | 
			
		||||
    return u32.raw_value;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint64_t htobe64(uint64_t int64)
 | 
			
		||||
{
 | 
			
		||||
    BigEndian<uint64_t> u64(int64);
 | 
			
		||||
    return u64.raw_value;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint16_t be16toh(uint16_t big16)
 | 
			
		||||
{
 | 
			
		||||
    LittleEndian<uint16_t> u16(big16);
 | 
			
		||||
    return u16.raw_value;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint32_t be32toh(uint32_t big32)
 | 
			
		||||
{
 | 
			
		||||
    LittleEndian<uint32_t> u32(big32);
 | 
			
		||||
    return u32.raw_value;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint64_t be64toh(uint64_t big64)
 | 
			
		||||
{
 | 
			
		||||
    LittleEndian<uint64_t> u64(big64);
 | 
			
		||||
    return u64.raw_value;
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* it can be used in Windows 8
 | 
			
		||||
#include <Winsock2.h>
 | 
			
		||||
 | 
			
		||||
uint16_t htobe16(uint16_t int16)
 | 
			
		||||
{
 | 
			
		||||
    return htons(int16);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint32_t htobe32(uint32_t 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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
uint16_t be16toh(uint16_t big16)
 | 
			
		||||
{
 | 
			
		||||
    return ntohs(big16);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint32_t be32toh(uint32_t 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;
 | 
			
		||||
}
 | 
			
		||||
*/
 | 
			
		||||
							
								
								
									
										119
									
								
								core/util/I2PEndian.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										119
									
								
								core/util/I2PEndian.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,119 @@
 | 
			
		|||
#ifndef I2PENDIAN_H__
 | 
			
		||||
#define I2PENDIAN_H__
 | 
			
		||||
#include <inttypes.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
 | 
			
		||||
#if defined(__linux__) || defined(__FreeBSD_kernel__)
 | 
			
		||||
#include <endian.h>
 | 
			
		||||
#elif __FreeBSD__
 | 
			
		||||
#include <sys/endian.h>
 | 
			
		||||
#elif defined(__APPLE__) && defined(__MACH__)
 | 
			
		||||
 | 
			
		||||
#include <libkern/OSByteOrder.h>
 | 
			
		||||
 | 
			
		||||
#define htobe16(x) OSSwapHostToBigInt16(x)
 | 
			
		||||
#define htole16(x) OSSwapHostToLittleInt16(x)
 | 
			
		||||
#define be16toh(x) OSSwapBigToHostInt16(x)
 | 
			
		||||
#define le16toh(x) OSSwapLittleToHostInt16(x)
 | 
			
		||||
 | 
			
		||||
#define htobe32(x) OSSwapHostToBigInt32(x)
 | 
			
		||||
#define htole32(x) OSSwapHostToLittleInt32(x)
 | 
			
		||||
#define be32toh(x) OSSwapBigToHostInt32(x)
 | 
			
		||||
#define le32toh(x) OSSwapLittleToHostInt32(x)
 | 
			
		||||
 | 
			
		||||
#define htobe64(x) OSSwapHostToBigInt64(x)
 | 
			
		||||
#define htole64(x) OSSwapHostToLittleInt64(x)
 | 
			
		||||
#define be64toh(x) OSSwapBigToHostInt64(x)
 | 
			
		||||
#define le64toh(x) OSSwapLittleToHostInt64(x)
 | 
			
		||||
 | 
			
		||||
#else
 | 
			
		||||
#define NEEDS_LOCAL_ENDIAN
 | 
			
		||||
#include <cstdint>
 | 
			
		||||
uint16_t htobe16(uint16_t int16);
 | 
			
		||||
uint32_t htobe32(uint32_t int32);
 | 
			
		||||
uint64_t htobe64(uint64_t int64);
 | 
			
		||||
 | 
			
		||||
uint16_t be16toh(uint16_t big16);
 | 
			
		||||
uint32_t be32toh(uint32_t big32);
 | 
			
		||||
uint64_t be64toh(uint64_t big64);
 | 
			
		||||
 | 
			
		||||
// assume LittleEndine
 | 
			
		||||
#define htole16
 | 
			
		||||
#define htole32
 | 
			
		||||
#define htole64
 | 
			
		||||
#define le16toh
 | 
			
		||||
#define le32toh
 | 
			
		||||
#define le64toh
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
inline uint16_t buf16toh(const void *buf)
 | 
			
		||||
{
 | 
			
		||||
    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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline uint64_t buf64toh(const void *buf)
 | 
			
		||||
{
 | 
			
		||||
    uint64_t b64;
 | 
			
		||||
    memcpy(&b64, buf, sizeof(uint64_t));
 | 
			
		||||
    return b64;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline uint16_t bufbe16toh(const void *buf)
 | 
			
		||||
{
 | 
			
		||||
    return be16toh(buf16toh(buf));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline uint32_t bufbe32toh(const void *buf)
 | 
			
		||||
{
 | 
			
		||||
    return be32toh(buf32toh(buf));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline uint64_t bufbe64toh(const void *buf)
 | 
			
		||||
{
 | 
			
		||||
    return be64toh(buf64toh(buf));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline void htobuf16(void *buf, uint16_t b16)
 | 
			
		||||
{
 | 
			
		||||
    memcpy(buf, &b16, sizeof(uint16_t));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline void htobuf32(void *buf, uint32_t b32)
 | 
			
		||||
{
 | 
			
		||||
    memcpy(buf, &b32, sizeof(uint32_t));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline void htobuf64(void *buf, uint64_t b64)
 | 
			
		||||
{
 | 
			
		||||
    memcpy(buf, &b64, sizeof(uint64_t));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline void htobe16buf(void *buf, uint16_t big16)
 | 
			
		||||
{
 | 
			
		||||
    htobuf16(buf, htobe16(big16));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline void htobe32buf(void *buf, uint32_t big32)
 | 
			
		||||
{
 | 
			
		||||
    htobuf32(buf, htobe32(big32));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline void htobe64buf(void *buf, uint64_t big64)
 | 
			
		||||
{
 | 
			
		||||
    htobuf64(buf, htobe64(big64));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#endif // I2PENDIAN_H__
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										242
									
								
								core/util/LittleBigEndian.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										242
									
								
								core/util/LittleBigEndian.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,242 @@
 | 
			
		|||
// LittleBigEndian.h fixed for 64-bits added union
 | 
			
		||||
//
 | 
			
		||||
 | 
			
		||||
#ifndef LITTLEBIGENDIAN_H
 | 
			
		||||
#define LITTLEBIGENDIAN_H
 | 
			
		||||
 | 
			
		||||
// Determine Little-Endian or Big-Endian
 | 
			
		||||
 | 
			
		||||
#define CURRENT_BYTE_ORDER       (*(int *)"\x01\x02\x03\x04")
 | 
			
		||||
#define LITTLE_ENDIAN_BYTE_ORDER 0x04030201
 | 
			
		||||
#define BIG_ENDIAN_BYTE_ORDER    0x01020304
 | 
			
		||||
#define PDP_ENDIAN_BYTE_ORDER    0x02010403
 | 
			
		||||
 | 
			
		||||
#define IS_LITTLE_ENDIAN (CURRENT_BYTE_ORDER == LITTLE_ENDIAN_BYTE_ORDER)
 | 
			
		||||
#define IS_BIG_ENDIAN    (CURRENT_BYTE_ORDER == BIG_ENDIAN_BYTE_ORDER)
 | 
			
		||||
#define IS_PDP_ENDIAN    (CURRENT_BYTE_ORDER == PDP_ENDIAN_BYTE_ORDER)
 | 
			
		||||
 | 
			
		||||
// Forward declaration
 | 
			
		||||
 | 
			
		||||
template<typename T>
 | 
			
		||||
struct LittleEndian;
 | 
			
		||||
 | 
			
		||||
template<typename T>
 | 
			
		||||
struct BigEndian;
 | 
			
		||||
 | 
			
		||||
// Little-Endian template
 | 
			
		||||
 | 
			
		||||
#pragma pack(push,1)
 | 
			
		||||
template<typename T>
 | 
			
		||||
struct LittleEndian
 | 
			
		||||
{
 | 
			
		||||
    union
 | 
			
		||||
    {
 | 
			
		||||
        unsigned char bytes[sizeof(T)];
 | 
			
		||||
        T raw_value;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    LittleEndian(T t = T())
 | 
			
		||||
    {
 | 
			
		||||
        operator =(t);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    LittleEndian(const LittleEndian<T> & t)
 | 
			
		||||
    {
 | 
			
		||||
        raw_value = t.raw_value;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    LittleEndian(const BigEndian<T> & t)
 | 
			
		||||
    {
 | 
			
		||||
        for (unsigned i = 0; i < sizeof(T); i++)
 | 
			
		||||
            bytes[i] = t.bytes[sizeof(T)-1-i];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    operator const T() const
 | 
			
		||||
    {
 | 
			
		||||
        T t = T();
 | 
			
		||||
        for (unsigned i = 0; i < sizeof(T); i++)
 | 
			
		||||
            t |= T(bytes[i]) << (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<unsigned char>(t >> (i << 3));
 | 
			
		||||
        return t;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // operators
 | 
			
		||||
 | 
			
		||||
    const T operator += (const T t)
 | 
			
		||||
    {
 | 
			
		||||
        return (*this = *this + t);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const T operator -= (const T t)
 | 
			
		||||
    {
 | 
			
		||||
        return (*this = *this - t);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const T operator *= (const T t)
 | 
			
		||||
    {
 | 
			
		||||
        return (*this = *this * t);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const T operator /= (const T t)
 | 
			
		||||
    {
 | 
			
		||||
        return (*this = *this / t);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const T operator %= (const T t)
 | 
			
		||||
    {
 | 
			
		||||
        return (*this = *this % t);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    LittleEndian<T> operator ++ (int)
 | 
			
		||||
    {
 | 
			
		||||
        LittleEndian<T> tmp(*this);
 | 
			
		||||
        operator ++ ();
 | 
			
		||||
        return tmp;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    LittleEndian<T> & operator ++ ()
 | 
			
		||||
    {
 | 
			
		||||
        for (unsigned i = 0; i < sizeof(T); i++)
 | 
			
		||||
        {
 | 
			
		||||
            ++bytes[i];
 | 
			
		||||
            if (bytes[i] != 0)
 | 
			
		||||
                break;
 | 
			
		||||
        }
 | 
			
		||||
        return (*this);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    LittleEndian<T> operator -- (int)
 | 
			
		||||
    {
 | 
			
		||||
        LittleEndian<T> tmp(*this);
 | 
			
		||||
        operator -- ();
 | 
			
		||||
        return tmp;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    LittleEndian<T> & operator -- ()
 | 
			
		||||
    {
 | 
			
		||||
        for (unsigned i = 0; i < sizeof(T); i++)
 | 
			
		||||
        {
 | 
			
		||||
            --bytes[i];
 | 
			
		||||
            if (bytes[i] != (T)(-1))
 | 
			
		||||
                break;
 | 
			
		||||
        }
 | 
			
		||||
        return (*this);
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
#pragma pack(pop)
 | 
			
		||||
 | 
			
		||||
// Big-Endian template
 | 
			
		||||
 | 
			
		||||
#pragma pack(push,1)
 | 
			
		||||
template<typename T>
 | 
			
		||||
struct BigEndian
 | 
			
		||||
{
 | 
			
		||||
    union
 | 
			
		||||
    {
 | 
			
		||||
        unsigned char bytes[sizeof(T)];
 | 
			
		||||
        T raw_value;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    BigEndian(T t = T())
 | 
			
		||||
    {
 | 
			
		||||
        operator =(t);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    BigEndian(const BigEndian<T> & t)
 | 
			
		||||
    {
 | 
			
		||||
        raw_value = t.raw_value;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    BigEndian(const LittleEndian<T> & t)
 | 
			
		||||
    {
 | 
			
		||||
        for (unsigned i = 0; i < sizeof(T); i++)
 | 
			
		||||
            bytes[i] = t.bytes[sizeof(T)-1-i];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    operator const T() const
 | 
			
		||||
    {
 | 
			
		||||
        T t = T();
 | 
			
		||||
        for (unsigned i = 0; i < sizeof(T); i++)
 | 
			
		||||
            t |= T(bytes[sizeof(T) - 1 - i]) << (i << 3);
 | 
			
		||||
        return t;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const T operator = (const T t)
 | 
			
		||||
    {
 | 
			
		||||
        for (unsigned i = 0; i < sizeof(T); i++)
 | 
			
		||||
            bytes[sizeof(T) - 1 - i] = t >> (i << 3);
 | 
			
		||||
        return t;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // operators
 | 
			
		||||
 | 
			
		||||
    const T operator += (const T t)
 | 
			
		||||
    {
 | 
			
		||||
        return (*this = *this + t);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const T operator -= (const T t)
 | 
			
		||||
    {
 | 
			
		||||
        return (*this = *this - t);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const T operator *= (const T t)
 | 
			
		||||
    {
 | 
			
		||||
        return (*this = *this * t);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const T operator /= (const T t)
 | 
			
		||||
    {
 | 
			
		||||
        return (*this = *this / t);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const T operator %= (const T t)
 | 
			
		||||
    {
 | 
			
		||||
        return (*this = *this % t);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    BigEndian<T> operator ++ (int)
 | 
			
		||||
    {
 | 
			
		||||
        BigEndian<T> tmp(*this);
 | 
			
		||||
        operator ++ ();
 | 
			
		||||
        return tmp;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    BigEndian<T> & operator ++ ()
 | 
			
		||||
    {
 | 
			
		||||
        for (unsigned i = 0; i < sizeof(T); i++)
 | 
			
		||||
        {
 | 
			
		||||
            ++bytes[sizeof(T) - 1 - i];
 | 
			
		||||
            if (bytes[sizeof(T) - 1 - i] != 0)
 | 
			
		||||
                break;
 | 
			
		||||
        }
 | 
			
		||||
        return (*this);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    BigEndian<T> operator -- (int)
 | 
			
		||||
    {
 | 
			
		||||
        BigEndian<T> tmp(*this);
 | 
			
		||||
        operator -- ();
 | 
			
		||||
        return tmp;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    BigEndian<T> & operator -- ()
 | 
			
		||||
    {
 | 
			
		||||
        for (unsigned i = 0; i < sizeof(T); i++)
 | 
			
		||||
        {
 | 
			
		||||
            --bytes[sizeof(T) - 1 - i];
 | 
			
		||||
            if (bytes[sizeof(T) - 1 - i] != (T)(-1))
 | 
			
		||||
                break;
 | 
			
		||||
        }
 | 
			
		||||
        return (*this);
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
#pragma pack(pop)
 | 
			
		||||
 | 
			
		||||
#endif // LITTLEBIGENDIAN_H
 | 
			
		||||
							
								
								
									
										62
									
								
								core/util/Log.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										62
									
								
								core/util/Log.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,62 @@
 | 
			
		|||
#include <boost/date_time/posix_time/posix_time.hpp>
 | 
			
		||||
#include "Log.h"
 | 
			
		||||
 | 
			
		||||
Log * g_Log = nullptr;
 | 
			
		||||
 | 
			
		||||
static const char * g_LogLevelStr[eNumLogLevels] =
 | 
			
		||||
{
 | 
			
		||||
	"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();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const std::string& Log::GetTimestamp ()
 | 
			
		||||
{
 | 
			
		||||
#if (__GNUC__ == 4) && (__GNUC_MINOR__ <= 6) && !defined(__clang__)
 | 
			
		||||
	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();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Log::SetLogStream (std::ostream * logStream)
 | 
			
		||||
{
 | 
			
		||||
	if (m_LogStream) delete m_LogStream;
 | 
			
		||||
	m_LogStream = logStream;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										129
									
								
								core/util/Log.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										129
									
								
								core/util/Log.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,129 @@
 | 
			
		|||
#ifndef LOG_H__
 | 
			
		||||
#define LOG_H__
 | 
			
		||||
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <iostream>
 | 
			
		||||
#include <sstream>
 | 
			
		||||
#include <fstream>
 | 
			
		||||
#include <functional>
 | 
			
		||||
#include <chrono>
 | 
			
		||||
#include "Queue.h"
 | 
			
		||||
 | 
			
		||||
enum LogLevel
 | 
			
		||||
{
 | 
			
		||||
	eLogError = 0,
 | 
			
		||||
	eLogWarning,
 | 
			
		||||
	eLogInfo,
 | 
			
		||||
	eLogDebug,
 | 
			
		||||
	eNumLogLevels
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class Log;
 | 
			
		||||
struct LogMsg
 | 
			
		||||
{
 | 
			
		||||
	std::stringstream s;
 | 
			
		||||
	Log * log;
 | 
			
		||||
	LogLevel level;
 | 
			
		||||
 | 
			
		||||
	LogMsg (Log * l = nullptr, LogLevel lv = eLogInfo): log (l), level (lv) {};
 | 
			
		||||
 | 
			
		||||
	void Process();
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class Log: public i2p::util::MsgQueue<LogMsg>
 | 
			
		||||
{
 | 
			
		||||
    public:
 | 
			
		||||
 | 
			
		||||
        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 ();
 | 
			
		||||
 | 
			
		||||
    private:
 | 
			
		||||
 | 
			
		||||
        void Flush ();
 | 
			
		||||
 | 
			
		||||
	private:
 | 
			
		||||
 | 
			
		||||
		std::ostream * m_LogStream;
 | 
			
		||||
		std::string m_Timestamp;
 | 
			
		||||
#if (__GNUC__ == 4) && (__GNUC_MINOR__ <= 6) && !defined(__clang__)
 | 
			
		||||
		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;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline void StartLog (std::ostream * s)
 | 
			
		||||
{
 | 
			
		||||
	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;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template<typename TValue>
 | 
			
		||||
void LogPrint (std::stringstream& s, TValue arg)
 | 
			
		||||
{
 | 
			
		||||
    s << arg;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template<typename TValue, typename... TArgs>
 | 
			
		||||
void LogPrint (std::stringstream& s, TValue arg, TArgs... args)
 | 
			
		||||
{
 | 
			
		||||
    LogPrint (s, arg);
 | 
			
		||||
    LogPrint (s, args...);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template<typename... TArgs>
 | 
			
		||||
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;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template<typename... TArgs>
 | 
			
		||||
void LogPrint (TArgs... args)
 | 
			
		||||
{
 | 
			
		||||
	LogPrint (eLogInfo, args...);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										169
									
								
								core/util/Queue.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										169
									
								
								core/util/Queue.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,169 @@
 | 
			
		|||
#ifndef QUEUE_H__
 | 
			
		||||
#define QUEUE_H__
 | 
			
		||||
 | 
			
		||||
#include <queue>
 | 
			
		||||
#include <vector>
 | 
			
		||||
#include <mutex>
 | 
			
		||||
#include <thread>
 | 
			
		||||
#include <condition_variable>
 | 
			
		||||
#include <functional>
 | 
			
		||||
 | 
			
		||||
namespace i2p
 | 
			
		||||
{
 | 
			
		||||
namespace util
 | 
			
		||||
{
 | 
			
		||||
    template<typename Element>
 | 
			
		||||
    class Queue
 | 
			
		||||
    {   
 | 
			
		||||
        public:
 | 
			
		||||
 | 
			
		||||
            void Put (Element e)
 | 
			
		||||
            {
 | 
			
		||||
                std::unique_lock<std::mutex>  l(m_QueueMutex);
 | 
			
		||||
                m_Queue.push (e);   
 | 
			
		||||
                m_NonEmpty.notify_one ();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            void Put (const std::vector<Element>& vec)
 | 
			
		||||
            {
 | 
			
		||||
                if (!vec.empty ())
 | 
			
		||||
                {   
 | 
			
		||||
                    std::unique_lock<std::mutex>  l(m_QueueMutex);
 | 
			
		||||
                    for (auto it: vec)
 | 
			
		||||
                        m_Queue.push (it);  
 | 
			
		||||
                    m_NonEmpty.notify_one ();
 | 
			
		||||
                }   
 | 
			
		||||
            }
 | 
			
		||||
            
 | 
			
		||||
            Element GetNext ()
 | 
			
		||||
            {
 | 
			
		||||
                std::unique_lock<std::mutex> l(m_QueueMutex);
 | 
			
		||||
                auto el = GetNonThreadSafe ();
 | 
			
		||||
                if (!el)
 | 
			
		||||
                {
 | 
			
		||||
                    m_NonEmpty.wait (l);
 | 
			
		||||
                    el = GetNonThreadSafe ();
 | 
			
		||||
                }   
 | 
			
		||||
                return el;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            Element GetNextWithTimeout (int usec)
 | 
			
		||||
            {
 | 
			
		||||
                std::unique_lock<std::mutex> 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<std::mutex> l(m_QueueMutex);
 | 
			
		||||
                m_NonEmpty.wait (l);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            bool Wait (int sec, int usec)
 | 
			
		||||
            {
 | 
			
		||||
                std::unique_lock<std::mutex> 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<std::mutex> l(m_QueueMutex);
 | 
			
		||||
                return m_Queue.empty ();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            int GetSize () 
 | 
			
		||||
            {
 | 
			
		||||
                std::unique_lock<std::mutex> l(m_QueueMutex);
 | 
			
		||||
                return m_Queue.size ();
 | 
			
		||||
            }           
 | 
			
		||||
 | 
			
		||||
            void WakeUp () { m_NonEmpty.notify_all (); };
 | 
			
		||||
 | 
			
		||||
            Element Get ()
 | 
			
		||||
            {
 | 
			
		||||
                std::unique_lock<std::mutex> l(m_QueueMutex);
 | 
			
		||||
                return GetNonThreadSafe ();
 | 
			
		||||
            }   
 | 
			
		||||
 | 
			
		||||
            Element Peek ()
 | 
			
		||||
            {
 | 
			
		||||
                std::unique_lock<std::mutex> 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:
 | 
			
		||||
 | 
			
		||||
            std::queue<Element> m_Queue;
 | 
			
		||||
            std::mutex m_QueueMutex;
 | 
			
		||||
            std::condition_variable m_NonEmpty;
 | 
			
		||||
    };  
 | 
			
		||||
 | 
			
		||||
    template<class Msg>
 | 
			
		||||
    class MsgQueue: public Queue<Msg *>
 | 
			
		||||
    {
 | 
			
		||||
        public:
 | 
			
		||||
 | 
			
		||||
            typedef std::function<void()> OnEmpty;
 | 
			
		||||
 | 
			
		||||
            MsgQueue (): m_IsRunning (true), m_Thread (std::bind (&MsgQueue<Msg>::Run, this))  {};
 | 
			
		||||
            ~MsgQueue () { Stop (); };
 | 
			
		||||
            void Stop()
 | 
			
		||||
            {
 | 
			
		||||
                if (m_IsRunning)
 | 
			
		||||
                {
 | 
			
		||||
                    m_IsRunning = false;
 | 
			
		||||
                    Queue<Msg *>::WakeUp ();                    
 | 
			
		||||
                    m_Thread.join();
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            void SetOnEmpty (OnEmpty const & e) { m_OnEmpty = e; };
 | 
			
		||||
 | 
			
		||||
        private:
 | 
			
		||||
 | 
			
		||||
            void Run ()
 | 
			
		||||
            {
 | 
			
		||||
                while (m_IsRunning)
 | 
			
		||||
                {
 | 
			
		||||
                    while (auto msg = Queue<Msg *>::Get ())
 | 
			
		||||
                    {
 | 
			
		||||
                        msg->Process ();
 | 
			
		||||
                        delete msg;
 | 
			
		||||
                    }
 | 
			
		||||
                    if (m_OnEmpty != nullptr)
 | 
			
		||||
                        m_OnEmpty ();
 | 
			
		||||
                    if (m_IsRunning)
 | 
			
		||||
                        Queue<Msg *>::Wait ();
 | 
			
		||||
                }   
 | 
			
		||||
            }   
 | 
			
		||||
            
 | 
			
		||||
        private:
 | 
			
		||||
            
 | 
			
		||||
            volatile bool m_IsRunning;
 | 
			
		||||
            OnEmpty m_OnEmpty;
 | 
			
		||||
            std::thread m_Thread;   
 | 
			
		||||
    };  
 | 
			
		||||
}       
 | 
			
		||||
}   
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										32
									
								
								core/util/Timestamp.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								core/util/Timestamp.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,32 @@
 | 
			
		|||
#ifndef TIMESTAMP_H__
 | 
			
		||||
#define TIMESTAMP_H__
 | 
			
		||||
 | 
			
		||||
#include <inttypes.h>
 | 
			
		||||
#include <chrono>
 | 
			
		||||
 | 
			
		||||
namespace i2p
 | 
			
		||||
{
 | 
			
		||||
namespace util
 | 
			
		||||
{
 | 
			
		||||
    inline uint64_t GetMillisecondsSinceEpoch ()
 | 
			
		||||
    {
 | 
			
		||||
        return std::chrono::duration_cast<std::chrono::milliseconds>(
 | 
			
		||||
                 std::chrono::system_clock::now().time_since_epoch()).count ();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    inline uint32_t GetHoursSinceEpoch ()
 | 
			
		||||
    {
 | 
			
		||||
        return std::chrono::duration_cast<std::chrono::hours>(
 | 
			
		||||
                 std::chrono::system_clock::now().time_since_epoch()).count ();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    inline uint64_t GetSecondsSinceEpoch ()
 | 
			
		||||
    {
 | 
			
		||||
        return std::chrono::duration_cast<std::chrono::seconds>(
 | 
			
		||||
                 std::chrono::system_clock::now().time_since_epoch()).count ();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										239
									
								
								core/util/base64.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										239
									
								
								core/util/base64.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,239 @@
 | 
			
		|||
#include <stdlib.h>
 | 
			
		||||
#include "base64.h"
 | 
			
		||||
 | 
			
		||||
namespace i2p
 | 
			
		||||
{
 | 
			
		||||
namespace util
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
    static void iT64Build(void);
 | 
			
		||||
 | 
			
		||||
    /*
 | 
			
		||||
    *
 | 
			
		||||
    * 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', '-', '~'
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    const char * GetBase64SubstitutionTable ()
 | 
			
		||||
    {
 | 
			
		||||
        return T64;
 | 
			
		||||
    }   
 | 
			
		||||
    
 | 
			
		||||
    /*
 | 
			
		||||
     * Reverse Substitution Table (built in run time)
 | 
			
		||||
     */
 | 
			
		||||
    static char iT64[256];
 | 
			
		||||
    static int isFirstTime = 1;
 | 
			
		||||
 | 
			
		||||
    /*
 | 
			
		||||
    * Padding 
 | 
			
		||||
    */
 | 
			
		||||
    static char P64 = '='; 
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    size_t ByteStreamToBase64(const uint8_t* InBuffer, size_t InCount, char* OutBuffer, size_t len)
 | 
			
		||||
    {
 | 
			
		||||
        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<n; i++ ){
 | 
			
		||||
             acc_1 = *ps++;
 | 
			
		||||
             acc_2 = (acc_1<<4)&0x30; 
 | 
			
		||||
             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;
 | 
			
		||||
             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;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    size_t Base64ToByteStream(const char * InBuffer, size_t InCount, uint8_t* OutBuffer, size_t len)
 | 
			
		||||
    {
 | 
			
		||||
        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(InCount && !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 0;
 | 
			
		||||
        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 = iT64[*ps++];
 | 
			
		||||
             acc_2 |= acc_1 << 6;
 | 
			
		||||
             *pd++ = acc_2;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return outCount;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /*
 | 
			
		||||
    *
 | 
			
		||||
    * 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;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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)
 | 
			
		||||
    {
 | 
			
		||||
        if(!len)
 | 
			
		||||
            return 0; // No data given
 | 
			
		||||
 | 
			
		||||
        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;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										63
									
								
								core/util/base64.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								core/util/base64.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,63 @@
 | 
			
		|||
#ifndef BASE64_H
 | 
			
		||||
#define BASE64_H
 | 
			
		||||
 | 
			
		||||
#include <inttypes.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
 | 
			
		||||
namespace i2p
 | 
			
		||||
{
 | 
			
		||||
namespace util
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
    /*
 | 
			
		||||
     * Base64 encodes an array of bytes.
 | 
			
		||||
     * @return the number of characters written to the output buffer
 | 
			
		||||
     * @param InBuffer array of input bytes to be encoded 
 | 
			
		||||
     * @param InCount length of the input array
 | 
			
		||||
     * @param OutBuffer array to store output characters 
 | 
			
		||||
     * @param len length of the output buffer
 | 
			
		||||
     * @note zero is returned when the output buffer is too small 
 | 
			
		||||
     */
 | 
			
		||||
    size_t ByteStreamToBase64 (const uint8_t * InBuffer, size_t InCount, char * OutBuffer, size_t len);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Decodes base 64 encoded data to an array of bytes.
 | 
			
		||||
     * @return the number of bytes written to the output buffer
 | 
			
		||||
     * @param InBuffer array of input characters to be decoded
 | 
			
		||||
     * @param InCount length of the input array
 | 
			
		||||
     * @param OutBuffer array to store output bytes
 | 
			
		||||
     * @param len length of the output buffer
 | 
			
		||||
     * @todo Do not return a negative value on failure, size_t could be unsigned.
 | 
			
		||||
     * @note zero is returned when the output buffer is too small 
 | 
			
		||||
     */
 | 
			
		||||
    size_t Base64ToByteStream (const char * InBuffer, size_t InCount, uint8_t * OutBuffer, size_t len );
 | 
			
		||||
 | 
			
		||||
    const char * GetBase64SubstitutionTable (); 
 | 
			
		||||
    
 | 
			
		||||
    /**
 | 
			
		||||
     * Decodes base 32 encoded data to an array of bytes.
 | 
			
		||||
     * @return the number of bytes written to the output buffer
 | 
			
		||||
     * @param inBuf array of input characters to be decoded
 | 
			
		||||
     * @param len length of the input buffer
 | 
			
		||||
     * @param outBuf array to store output bytes
 | 
			
		||||
     * @param outLen length of the output array 
 | 
			
		||||
     * @note zero is returned when the output buffer is too small 
 | 
			
		||||
     */
 | 
			
		||||
    size_t Base32ToByteStream (const char * inBuf, size_t len, uint8_t * outBuf, size_t outLen);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Base 32 encodes an array of bytes.
 | 
			
		||||
     * @return the number of bytes written to the output buffer
 | 
			
		||||
     * @param inBuf array of input bytes to be encoded
 | 
			
		||||
     * @param len length of the input buffer
 | 
			
		||||
     * @param outBuf array to store output characters
 | 
			
		||||
     * @param outLen length of the output array 
 | 
			
		||||
     * @note zero is returned when the output buffer is too small 
 | 
			
		||||
     */
 | 
			
		||||
    size_t ByteStreamToBase32 (const uint8_t * inBuf, size_t len, char * outBuf, size_t outLen);
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										676
									
								
								core/util/util.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										676
									
								
								core/util/util.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,676 @@
 | 
			
		|||
#include <cstdlib>
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <algorithm>
 | 
			
		||||
#include <cctype>
 | 
			
		||||
#include <functional>
 | 
			
		||||
#include <fstream>
 | 
			
		||||
#include <set>
 | 
			
		||||
#include <boost/asio.hpp>
 | 
			
		||||
#include <boost/filesystem.hpp>
 | 
			
		||||
#include <boost/filesystem/fstream.hpp>
 | 
			
		||||
#include <boost/lexical_cast.hpp>
 | 
			
		||||
#include <boost/program_options/detail/config_file.hpp>
 | 
			
		||||
#include <boost/program_options/parsers.hpp>
 | 
			
		||||
#include <boost/algorithm/string.hpp>
 | 
			
		||||
#include "util.h"
 | 
			
		||||
#include "Log.h"
 | 
			
		||||
 | 
			
		||||
#if defined(__linux__) || defined(__FreeBSD_kernel__) || defined(__APPLE__)
 | 
			
		||||
#include <sys/types.h>
 | 
			
		||||
#include <ifaddrs.h>
 | 
			
		||||
#elif defined(WIN32)
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <stdio.h>    
 | 
			
		||||
#include <winsock2.h>
 | 
			
		||||
#include <ws2tcpip.h>
 | 
			
		||||
#include <iphlpapi.h>
 | 
			
		||||
#include <shlobj.h>
 | 
			
		||||
 | 
			
		||||
#pragma comment(lib, "IPHLPAPI.lib")
 | 
			
		||||
 | 
			
		||||
#define MALLOC(x) HeapAlloc(GetProcessHeap(), 0, (x))
 | 
			
		||||
#define FREE(x) HeapFree(GetProcessHeap(), 0, (x))
 | 
			
		||||
 | 
			
		||||
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];
 | 
			
		||||
 | 
			
		||||
    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;
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
namespace i2p {
 | 
			
		||||
namespace util {
 | 
			
		||||
 | 
			
		||||
namespace config {
 | 
			
		||||
    std::map<std::string, std::string> mapArgs;
 | 
			
		||||
    std::map<std::string, std::vector<std::string> > 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);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
#ifdef WIN32
 | 
			
		||||
            boost::to_lower(strKey);
 | 
			
		||||
            if(boost::algorithm::starts_with(strKey, "/"))
 | 
			
		||||
                strKey = "-" + strKey.substr(1);
 | 
			
		||||
#endif
 | 
			
		||||
            if(strKey[0] != '-')
 | 
			
		||||
                break;
 | 
			
		||||
 | 
			
		||||
            mapArgs[strKey] = strValue;
 | 
			
		||||
            mapMultiArgs[strKey].push_back(strValue);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        for(auto& 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;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    int GetArg(const std::string& strArg, int nDefault)
 | 
			
		||||
    {
 | 
			
		||||
        if(mapArgs.count(strArg))
 | 
			
		||||
            return stoi(mapArgs[strArg]);
 | 
			
		||||
        return nDefault;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
namespace filesystem
 | 
			
		||||
{
 | 
			
		||||
    std::string appName("i2pd");
 | 
			
		||||
 | 
			
		||||
    void SetAppName(const std::string& name)
 | 
			
		||||
    {
 | 
			
		||||
        appName = name;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    std::string GetAppName()
 | 
			
		||||
    {
 | 
			
		||||
        return appName;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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();
 | 
			
		||||
 | 
			
		||||
        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();
 | 
			
		||||
#ifndef _WIN32
 | 
			
		||||
        fullPath.append("/");
 | 
			
		||||
#else
 | 
			
		||||
        fullPath.append("\\");
 | 
			
		||||
#endif
 | 
			
		||||
        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 GetTunnelsConfigFile()
 | 
			
		||||
    {
 | 
			
		||||
        boost::filesystem::path pathTunnelsConfigFile(i2p::util::config::GetArg("-tunnelscfg", "tunnels.cfg"));
 | 
			
		||||
        if(!pathTunnelsConfigFile.is_complete())
 | 
			
		||||
            pathTunnelsConfigFile = GetDataDir() / pathTunnelsConfigFile;
 | 
			
		||||
        return pathTunnelsConfigFile;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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);
 | 
			
		||||
#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);
 | 
			
		||||
#ifdef MAC_OSX
 | 
			
		||||
        // Mac
 | 
			
		||||
        pathRet /= "Library/Application Support";
 | 
			
		||||
        boost::filesystem::create_directory(pathRet);
 | 
			
		||||
        return pathRet / appName;
 | 
			
		||||
#else
 | 
			
		||||
        // Unix
 | 
			
		||||
        return pathRet / (std::string (".") + appName);
 | 
			
		||||
#endif
 | 
			
		||||
#endif
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void ReadConfigFile(std::map<std::string, std::string>& mapSettingsRet,
 | 
			
		||||
                        std::map<std::string, std::vector<std::string> >& mapMultiSettingsRet)
 | 
			
		||||
    {
 | 
			
		||||
        boost::filesystem::ifstream streamConfig(GetConfigFile());
 | 
			
		||||
        if(!streamConfig.good())
 | 
			
		||||
            return; // No i2pd.conf file is OK
 | 
			
		||||
 | 
			
		||||
        std::set<std::string> 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]);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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(const 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::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();
 | 
			
		||||
 | 
			
		||||
                // 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);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    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));
 | 
			
		||||
        // Make portocol lowercase
 | 
			
		||||
        transform(
 | 
			
		||||
            url_s.begin(), prot_i, back_inserter(protocol_), std::ptr_fun<int, int>(std::tolower)
 | 
			
		||||
        ); 
 | 
			
		||||
        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));
 | 
			
		||||
        // Make host lowerase
 | 
			
		||||
        transform(prot_i, path_i, back_inserter(host_), std::ptr_fun<int, int>(std::tolower));
 | 
			
		||||
 | 
			
		||||
        // 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());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // 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<decltype(port_)>(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 urlDecode(const std::string& data)
 | 
			
		||||
    {
 | 
			
		||||
        std::string res(data);
 | 
			
		||||
        for(size_t pos = res.find('%'); pos != std::string::npos; pos = res.find('%', pos + 1)) {
 | 
			
		||||
            const char c = strtol(res.substr(pos + 1, 2).c_str(), NULL, 16);
 | 
			
		||||
            res.replace(pos, 3, 1, c);
 | 
			
		||||
        }
 | 
			
		||||
        return res;
 | 
			
		||||
    }
 | 
			
		||||
} 
 | 
			
		||||
 | 
			
		||||
namespace net {
 | 
			
		||||
 | 
			
		||||
#if defined(__linux__) || defined(__FreeBSD_kernel__) || defined(__APPLE__)
 | 
			
		||||
    
 | 
			
		||||
    int GetMTUUnix(const boost::asio::ip::address& localAddress, int fallback)
 | 
			
		||||
    {
 | 
			
		||||
        ifaddrs* ifaddr, *ifa = nullptr;
 | 
			
		||||
        if(getifaddrs(&ifaddr) == -1) {
 | 
			
		||||
            LogPrint(eLogError, "Can't excute getifaddrs");
 | 
			
		||||
            return fallback;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        int family = 0;
 | 
			
		||||
        // look 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 = fallback;
 | 
			
		||||
        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;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
#elif defined(WIN32)
 | 
			
		||||
    int GetMTUWindowsIpv4(sockaddr_in inputAddress, int fallback)
 | 
			
		||||
    {
 | 
			
		||||
        ULONG outBufLen = 0;
 | 
			
		||||
        PIP_ADAPTER_ADDRESSES pAddresses = nullptr;
 | 
			
		||||
        PIP_ADAPTER_ADDRESSES pCurrAddresses = nullptr;
 | 
			
		||||
        PIP_ADAPTER_UNICAST_ADDRESS pUnicast = nullptr;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        if(GetAdaptersAddresses(AF_INET, GAA_FLAG_INCLUDE_PREFIX, nullptr, pAddresses, &outBufLen)
 | 
			
		||||
          == ERROR_BUFFER_OVERFLOW) {
 | 
			
		||||
            FREE(pAddresses);
 | 
			
		||||
            pAddresses = (IP_ADAPTER_ADDRESSES*) MALLOC(outBufLen);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        DWORD dwRetVal = GetAdaptersAddresses(
 | 
			
		||||
            AF_INET, GAA_FLAG_INCLUDE_PREFIX, nullptr, pAddresses, &outBufLen
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        if(dwRetVal != NO_ERROR) {
 | 
			
		||||
            LogPrint(
 | 
			
		||||
                eLogError, "GetMTU() has failed: enclosed GetAdaptersAddresses() call has failed"
 | 
			
		||||
            );
 | 
			
		||||
            FREE(pAddresses);
 | 
			
		||||
            return fallback;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        pCurrAddresses = pAddresses;
 | 
			
		||||
        while(pCurrAddresses) {
 | 
			
		||||
            PIP_ADAPTER_UNICAST_ADDRESS firstUnicastAddress = pCurrAddresses->FirstUnicastAddress;
 | 
			
		||||
 | 
			
		||||
            pUnicast = pCurrAddresses->FirstUnicastAddress;
 | 
			
		||||
            if(pUnicast == nullptr) {
 | 
			
		||||
                LogPrint(
 | 
			
		||||
                    eLogError, "GetMTU() has failed: not a unicast ipv4 address, this is not supported"
 | 
			
		||||
                );
 | 
			
		||||
            }
 | 
			
		||||
            for(int i = 0; pUnicast != nullptr; ++i) {
 | 
			
		||||
                LPSOCKADDR lpAddr = pUnicast->Address.lpSockaddr;
 | 
			
		||||
                sockaddr_in* localInterfaceAddress = (sockaddr_in*) lpAddr;
 | 
			
		||||
                if(localInterfaceAddress->sin_addr.S_un.S_addr == inputAddress.sin_addr.S_un.S_addr) {
 | 
			
		||||
                    result = pAddresses->Mtu;
 | 
			
		||||
                    FREE(pAddresses);
 | 
			
		||||
                    return result;
 | 
			
		||||
                }
 | 
			
		||||
                pUnicast = pUnicast->Next;
 | 
			
		||||
            }
 | 
			
		||||
            pCurrAddresses = pCurrAddresses->Next;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        LogPrint(eLogError, "GetMTU() error: no usable unicast ipv4 addresses found");
 | 
			
		||||
        FREE(pAddresses);
 | 
			
		||||
        return fallback;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    int GetMTUWindowsIpv6(sockaddr_in6 inputAddress, int fallback)
 | 
			
		||||
    {
 | 
			
		||||
        ULONG outBufLen = 0;
 | 
			
		||||
        PIP_ADAPTER_ADDRESSES pAddresses = nullptr;
 | 
			
		||||
        PIP_ADAPTER_ADDRESSES pCurrAddresses = nullptr;
 | 
			
		||||
        PIP_ADAPTER_UNICAST_ADDRESS pUnicast = nullptr;
 | 
			
		||||
 | 
			
		||||
        if(GetAdaptersAddresses(AF_INET6, GAA_FLAG_INCLUDE_PREFIX, nullptr, pAddresses, &outBufLen)
 | 
			
		||||
          == ERROR_BUFFER_OVERFLOW) {
 | 
			
		||||
            FREE(pAddresses);
 | 
			
		||||
            pAddresses = (IP_ADAPTER_ADDRESSES*) MALLOC(outBufLen);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        DWORD dwRetVal = GetAdaptersAddresses(
 | 
			
		||||
            AF_INET6, GAA_FLAG_INCLUDE_PREFIX, nullptr, pAddresses, &outBufLen
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        if(dwRetVal != NO_ERROR) {
 | 
			
		||||
            LogPrint(
 | 
			
		||||
                eLogError,
 | 
			
		||||
                "GetMTU() has failed: enclosed GetAdaptersAddresses() call has failed"
 | 
			
		||||
            );
 | 
			
		||||
            FREE(pAddresses);
 | 
			
		||||
            return fallback;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        bool found_address = false;
 | 
			
		||||
        pCurrAddresses = pAddresses;
 | 
			
		||||
        while(pCurrAddresses) {
 | 
			
		||||
            PIP_ADAPTER_UNICAST_ADDRESS firstUnicastAddress = pCurrAddresses->FirstUnicastAddress;
 | 
			
		||||
            pUnicast = pCurrAddresses->FirstUnicastAddress;
 | 
			
		||||
            if(pUnicast == nullptr) {
 | 
			
		||||
                LogPrint(
 | 
			
		||||
                    eLogError,
 | 
			
		||||
                    "GetMTU() has failed: not a unicast ipv6 address, this is not supported"
 | 
			
		||||
                );
 | 
			
		||||
            }
 | 
			
		||||
            for(int i = 0; pUnicast != nullptr; ++i) {
 | 
			
		||||
                LPSOCKADDR lpAddr = pUnicast->Address.lpSockaddr;
 | 
			
		||||
                sockaddr_in6 *localInterfaceAddress = (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;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            pCurrAddresses = pCurrAddresses->Next;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        LogPrint(eLogError, "GetMTU() error: no usable unicast ipv6 addresses found");
 | 
			
		||||
        FREE(pAddresses);
 | 
			
		||||
        return fallback;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    int GetMTUWindows(const boost::asio::ip::address& localAddress, int fallback)
 | 
			
		||||
    { 
 | 
			
		||||
#ifdef UNICODE
 | 
			
		||||
        string localAddress_temporary = localAddress.to_string();
 | 
			
		||||
        wstring localAddressUniversal(localAddress_temporary.begin(), localAddress_temporary.end());
 | 
			
		||||
#else
 | 
			
		||||
        std::string localAddressUniversal = localAddress.to_string();
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
        if(localAddress.is_v4()) {
 | 
			
		||||
            sockaddr_in inputAddress;
 | 
			
		||||
            inet_pton(AF_INET, localAddressUniversal.c_str(), &(inputAddress.sin_addr));
 | 
			
		||||
            return GetMTUWindowsIpv4(inputAddress, fallback);
 | 
			
		||||
        } else if(localAddress.is_v6()) {
 | 
			
		||||
            sockaddr_in6 inputAddress;
 | 
			
		||||
            inet_pton(AF_INET6, localAddressUniversal.c_str(), &(inputAddress.sin6_addr)); 
 | 
			
		||||
            return GetMTUWindowsIpv6(inputAddress, fallback);
 | 
			
		||||
        } else {
 | 
			
		||||
            LogPrint(eLogError, "GetMTU() has failed: address family is not supported");
 | 
			
		||||
            return fallback;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
#endif // WIN32
 | 
			
		||||
 | 
			
		||||
    int GetMTU(const boost::asio::ip::address& localAddress)
 | 
			
		||||
    {
 | 
			
		||||
        const int fallback = 576; // fallback MTU
 | 
			
		||||
 | 
			
		||||
#if defined(__linux__) || defined(__FreeBSD_kernel__) || defined(__APPLE__)
 | 
			
		||||
        return GetMTUUnix(localAddress, fallback);
 | 
			
		||||
#elif defined(WIN32)
 | 
			
		||||
        return GetMTUWindows(localAddress, fallback);
 | 
			
		||||
#endif
 | 
			
		||||
        return fallback;
 | 
			
		||||
    }
 | 
			
		||||
} 
 | 
			
		||||
 | 
			
		||||
} // util
 | 
			
		||||
} // i2p
 | 
			
		||||
							
								
								
									
										162
									
								
								core/util/util.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										162
									
								
								core/util/util.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,162 @@
 | 
			
		|||
#ifndef UTIL_H
 | 
			
		||||
#define UTIL_H
 | 
			
		||||
 | 
			
		||||
#include <map>
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <iostream>
 | 
			
		||||
#include <boost/asio.hpp>
 | 
			
		||||
#include <boost/filesystem.hpp>
 | 
			
		||||
#include <boost/filesystem/fstream.hpp>
 | 
			
		||||
 | 
			
		||||
#define PAIRTYPE(t1, t2)    std::pair<t1, t2>
 | 
			
		||||
 | 
			
		||||
namespace i2p
 | 
			
		||||
{
 | 
			
		||||
namespace util
 | 
			
		||||
{
 | 
			
		||||
    namespace config
 | 
			
		||||
    {
 | 
			
		||||
        extern std::map<std::string, std::string> mapArgs;
 | 
			
		||||
        extern std::map<std::string, std::vector<std::string> > mapMultiArgs;
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Parses command line arguments, i.e. stores them in config::mapArgs.
 | 
			
		||||
         */
 | 
			
		||||
        void OptionParser(int argc, const char* const argv[]);
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * @return a command line argument from config::mapArgs as an int
 | 
			
		||||
         * @param nDefault the default value to be returned
 | 
			
		||||
         */
 | 
			
		||||
        int GetArg(const std::string& strArg, int nDefault);
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * @return a command line argument from config::mapArgs as a std::string
 | 
			
		||||
         * @param strDefault the default value to be returned
 | 
			
		||||
         */
 | 
			
		||||
        std::string GetArg(const std::string& strArg, const std::string& strDefault);
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * @return a command line argument from config::mapArgs as a C-style string
 | 
			
		||||
         * @param nDefault the default value to be returned
 | 
			
		||||
         */
 | 
			
		||||
        const char* GetCharArg(const std::string& strArg, const std::string& nDefault);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    namespace filesystem
 | 
			
		||||
    {
 | 
			
		||||
        /**
 | 
			
		||||
         * Change the application name.
 | 
			
		||||
         */
 | 
			
		||||
        void SetAppName(const std::string& name);
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * @return the application name.
 | 
			
		||||
         */
 | 
			
		||||
        std::string GetAppName();
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * @return the path of the i2pd directory
 | 
			
		||||
         */
 | 
			
		||||
        const boost::filesystem::path& GetDataDir();
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * @return the full path of a file within the i2pd directory
 | 
			
		||||
         */
 | 
			
		||||
        std::string GetFullPath(const std::string& filename);
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * @return the path of the configuration file
 | 
			
		||||
         */
 | 
			
		||||
        boost::filesystem::path GetConfigFile();
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * @return the path of the tunnels configuration file
 | 
			
		||||
         */
 | 
			
		||||
        boost::filesystem::path GetTunnelsConfigFile();
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * @return the default directory for i2pd data
 | 
			
		||||
         */
 | 
			
		||||
        boost::filesystem::path GetDefaultDataDir();
 | 
			
		||||
 | 
			
		||||
        
 | 
			
		||||
        /**
 | 
			
		||||
         * Read a configuration file and store its contents in the given maps.
 | 
			
		||||
         */
 | 
			
		||||
        void ReadConfigFile(std::map<std::string, std::string>& mapSettingsRet,
 | 
			
		||||
                std::map<std::string, std::vector<std::string> >& mapMultiSettingsRet);
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * @return the path of the certificates directory
 | 
			
		||||
         */
 | 
			
		||||
        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";
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Perform an HTTP request.
 | 
			
		||||
         * @return the result of the request, or an empty string if it fails
 | 
			
		||||
         */
 | 
			
		||||
        std::string httpRequest(const std::string& address);
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * @return the content of the given HTTP stream without headers
 | 
			
		||||
         */
 | 
			
		||||
        std::string GetHttpContent(std::istream& response);
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Merge chunks of a HTTP response into the gien std:ostream object.
 | 
			
		||||
         */
 | 
			
		||||
        void MergeChunkedResponse(std::istream& response, std::ostream& merged);
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Send an HTTP request through the i2p proxy.
 | 
			
		||||
         * @return the HTTP status code 
 | 
			
		||||
         */
 | 
			
		||||
        int httpRequestViaI2pProxy(const std::string& address, std::string &content);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * @return the decoded url
 | 
			
		||||
         */
 | 
			
		||||
        std::string urlDecode(const std::string& data);
 | 
			
		||||
        
 | 
			
		||||
        /**
 | 
			
		||||
         * Provides functionality for parsing URLs.
 | 
			
		||||
         */
 | 
			
		||||
        struct url {
 | 
			
		||||
            /**
 | 
			
		||||
             * Parse a url given as a string.
 | 
			
		||||
             */
 | 
			
		||||
            url(const std::string& url_s);
 | 
			
		||||
        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
 | 
			
		||||
    {
 | 
			
		||||
        /**
 | 
			
		||||
         * @return the maximum transmission unit, or 576 on failure
 | 
			
		||||
         */
 | 
			
		||||
        int GetMTU(const boost::asio::ip::address& localAddress);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										8
									
								
								core/version.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								core/version.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,8 @@
 | 
			
		|||
#ifndef _VERSION_H_
 | 
			
		||||
#define _VERSION_H_
 | 
			
		||||
 | 
			
		||||
#define CODENAME "Purple"
 | 
			
		||||
#define VERSION "0.10.0"
 | 
			
		||||
#define I2P_VERSION "0.9.20"
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue