diff --git a/Win32/DaemonWin32.cpp b/Win32/DaemonWin32.cpp index 822af30c..9fffe7fb 100644 --- a/Win32/DaemonWin32.cpp +++ b/Win32/DaemonWin32.cpp @@ -50,7 +50,7 @@ namespace util if (isDaemon) { LogPrint(eLogDebug, "Daemon: running as service"); - I2PService service(SERVICE_NAME); + I2PService service((PSTR)SERVICE_NAME); if (!I2PService::Run(service)) { LogPrint(eLogError, "Daemon: Service failed to run w/err 0x%08lx\n", GetLastError()); diff --git a/Win32/Win32App.cpp b/Win32/Win32App.cpp index f58b4615..4bf4ab56 100644 --- a/Win32/Win32App.cpp +++ b/Win32/Win32App.cpp @@ -24,17 +24,22 @@ #define ID_GRACEFUL_SHUTDOWN 2004 #define ID_STOP_GRACEFUL_SHUTDOWN 2005 #define ID_RELOAD 2006 +#define ID_ACCEPT_TRANSIT 2007 +#define ID_DECLINE_TRANSIT 2008 #define ID_TRAY_ICON 2050 #define WM_TRAYICON (WM_USER + 1) #define IDT_GRACEFUL_SHUTDOWN_TIMER 2100 #define FRAME_UPDATE_TIMER 2101 +#define IDT_GRACEFUL_TUNNELCHECK_TIMER 2102 namespace i2p { namespace win32 { + static DWORD GracefulShutdownEndtime = 0; + static void ShowPopupMenu (HWND hWnd, POINT *curpos, int wDefaultItem) { HMENU hPopup = CreatePopupMenu(); @@ -42,11 +47,17 @@ namespace win32 InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_APP, "Show app"); InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_ABOUT, "&About..."); InsertMenu (hPopup, -1, MF_BYPOSITION | MF_SEPARATOR, 0, NULL); + if(!i2p::context.AcceptsTunnels()) + InsertMenu (hPopup, -1, + i2p::util::DaemonWin32::Instance ().isGraceful ? MF_BYPOSITION | MF_STRING | MF_GRAYED : MF_BYPOSITION | MF_STRING, + ID_ACCEPT_TRANSIT, "Accept &transit"); + else + InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_DECLINE_TRANSIT, "Decline &transit"); InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_RELOAD, "&Reload configs"); if (!i2p::util::DaemonWin32::Instance ().isGraceful) InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_GRACEFUL_SHUTDOWN, "&Graceful shutdown"); else - InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_STOP_GRACEFUL_SHUTDOWN, "&Stop graceful shutdown"); + InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_STOP_GRACEFUL_SHUTDOWN, "Stop &graceful shutdown"); InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_EXIT, "E&xit"); SetMenuDefaultItem (hPopup, ID_CONSOLE, FALSE); SendMessage (hWnd, WM_INITMENUPOPUP, (WPARAM)hPopup, 0); @@ -148,7 +159,13 @@ namespace win32 s << "; "; s << "Success Rate: " << i2p::tunnel::tunnels.GetTunnelCreationSuccessRate() << "%\n"; s << "Uptime: "; ShowUptime(s, i2p::context.GetUptime ()); - s << "\n"; + if (GracefulShutdownEndtime != 0) + { + DWORD GracefulTimeLeft = (GracefulShutdownEndtime - GetTickCount()) / 1000; + s << "Graceful shutdown, time left: "; ShowUptime(s, GracefulTimeLeft); + } + else + s << "\n"; s << "Inbound: " << i2p::transport::transports.GetInBandwidth() / 1024 << " KiB/s; "; s << "Outbound: " << i2p::transport::transports.GetOutBandwidth() / 1024 << " KiB/s\n"; s << "Received: "; ShowTransfered (s, i2p::transport::transports.GetTotalReceivedBytes()); @@ -166,10 +183,13 @@ namespace win32 static LRESULT CALLBACK WndProc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { + static UINT s_uTaskbarRestart; + switch (uMsg) { case WM_CREATE: { + s_uTaskbarRestart = RegisterWindowMessage(TEXT("TaskbarCreated")); AddTrayIcon (hWnd); break; } @@ -178,6 +198,7 @@ namespace win32 RemoveTrayIcon (hWnd); KillTimer (hWnd, FRAME_UPDATE_TIMER); KillTimer (hWnd, IDT_GRACEFUL_SHUTDOWN_TIMER); + KillTimer (hWnd, IDT_GRACEFUL_TUNNELCHECK_TIMER); PostQuitMessage (0); break; } @@ -197,10 +218,28 @@ namespace win32 PostMessage (hWnd, WM_CLOSE, 0, 0); return 0; } + case ID_ACCEPT_TRANSIT: + { + i2p::context.SetAcceptsTunnels (true); + std::stringstream text; + text << "I2Pd now accept transit tunnels"; + MessageBox( hWnd, TEXT(text.str ().c_str ()), TEXT("i2pd"), MB_ICONINFORMATION | MB_OK ); + return 0; + } + case ID_DECLINE_TRANSIT: + { + i2p::context.SetAcceptsTunnels (false); + std::stringstream text; + text << "I2Pd now decline new transit tunnels"; + MessageBox( hWnd, TEXT(text.str ().c_str ()), TEXT("i2pd"), MB_ICONINFORMATION | MB_OK ); + return 0; + } case ID_GRACEFUL_SHUTDOWN: { i2p::context.SetAcceptsTunnels (false); SetTimer (hWnd, IDT_GRACEFUL_SHUTDOWN_TIMER, 10*60*1000, nullptr); // 10 minutes + SetTimer (hWnd, IDT_GRACEFUL_TUNNELCHECK_TIMER, 1000, nullptr); // check tunnels every second + GracefulShutdownEndtime = GetTickCount() + 10*60*1000; i2p::util::DaemonWin32::Instance ().isGraceful = true; return 0; } @@ -208,6 +247,8 @@ namespace win32 { i2p::context.SetAcceptsTunnels (true); KillTimer (hWnd, IDT_GRACEFUL_SHUTDOWN_TIMER); + KillTimer (hWnd, IDT_GRACEFUL_TUNNELCHECK_TIMER); + GracefulShutdownEndtime = 0; i2p::util::DaemonWin32::Instance ().isGraceful = false; return 0; } @@ -223,7 +264,7 @@ namespace win32 { char buf[30]; std::string httpAddr; i2p::config::GetOption("http.address", httpAddr); - uint16_t httpPort; i2p::config::GetOption("http.port", httpPort); + uint16_t httpPort; i2p::config::GetOption("http.port", httpPort); snprintf(buf, 30, "http://%s:%d", httpAddr.c_str(), httpPort); ShellExecute(NULL, "open", buf, NULL, NULL, SW_SHOWNORMAL); return 0; @@ -290,14 +331,27 @@ namespace win32 } case WM_TIMER: { - if (wParam == IDT_GRACEFUL_SHUTDOWN_TIMER) + switch(wParam) { - PostMessage (hWnd, WM_CLOSE, 0, 0); // exit - return 0; - } - if (wParam == FRAME_UPDATE_TIMER) - { - InvalidateRect(hWnd, NULL, TRUE); + case IDT_GRACEFUL_SHUTDOWN_TIMER: + { + GracefulShutdownEndtime = 0; + PostMessage (hWnd, WM_CLOSE, 0, 0); // exit + return 0; + } + case FRAME_UPDATE_TIMER: + { + InvalidateRect(hWnd, NULL, TRUE); + return 0; + } + case IDT_GRACEFUL_TUNNELCHECK_TIMER: + { + if (i2p::tunnel::tunnels.CountTransitTunnels() == 0) + PostMessage (hWnd, WM_CLOSE, 0, 0); + else + SetTimer (hWnd, IDT_GRACEFUL_TUNNELCHECK_TIMER, 1000, nullptr); + return 0; + } } break; } @@ -318,6 +372,12 @@ namespace win32 EndPaint(hWnd, &ps); break; } + default: + { + if (uMsg == s_uTaskbarRestart) + AddTrayIcon (hWnd); + break; + } } return DefWindowProc( hWnd, uMsg, wParam, lParam); } diff --git a/Win32/Win32Service.cpp b/Win32/Win32Service.cpp index 4a0058ac..717bc887 100644 --- a/Win32/Win32Service.cpp +++ b/Win32/Win32Service.cpp @@ -297,7 +297,8 @@ void InstallService(PCSTR pszServiceName, PCSTR pszDisplayName, DWORD dwStartTyp FreeHandles(schSCManager, schService); return; } - strncat(szPath, " --daemon", MAX_PATH); + char SvcOpt[] = " --daemon"; + strncat(szPath, SvcOpt, strlen(SvcOpt)); // Open the local default service control manager database schSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT | SC_MANAGER_CREATE_SERVICE); diff --git a/libi2pd/Destination.cpp b/libi2pd/Destination.cpp index 6f7ef8f0..d9c90f51 100644 --- a/libi2pd/Destination.cpp +++ b/libi2pd/Destination.cpp @@ -777,6 +777,9 @@ namespace client m_DatagramDestination (nullptr), m_RefCounter (0), m_ReadyChecker(GetService()) { + if (keys.IsOfflineSignature () && GetLeaseSetType () == i2p::data::NETDB_STORE_TYPE_LEASESET) + SetLeaseSetType (i2p::data::NETDB_STORE_TYPE_STANDARD_LEASESET2); // offline keys can be published with LS2 only + m_EncryptionKeyType = GetIdentity ()->GetCryptoKeyType (); // extract encryption type params for LS2 if (GetLeaseSetType () == i2p::data::NETDB_STORE_TYPE_STANDARD_LEASESET2 && params) @@ -1048,9 +1051,7 @@ namespace client // standard LS2 (type 3) assumed for now. TODO: implement others auto keyLen = m_Decryptor ? m_Decryptor->GetPublicKeyLen () : 256; leaseSet = new i2p::data::LocalLeaseSet2 (i2p::data::NETDB_STORE_TYPE_STANDARD_LEASESET2, - GetIdentity (), m_EncryptionKeyType, keyLen, m_EncryptionPublicKey, tunnels); - // sign - Sign (leaseSet->GetBuffer () - 1, leaseSet->GetBufferLen () - leaseSet->GetSignatureLen () + 1, leaseSet->GetSignature ()); // + leading store type + m_Keys, m_EncryptionKeyType, keyLen, m_EncryptionPublicKey, tunnels); } SetLeaseSet (leaseSet); } diff --git a/libi2pd/Destination.h b/libi2pd/Destination.h index 847ecb1a..5c141236 100644 --- a/libi2pd/Destination.h +++ b/libi2pd/Destination.h @@ -126,6 +126,7 @@ namespace client void SetLeaseSet (i2p::data::LocalLeaseSet * newLeaseSet); int GetLeaseSetType () const { return m_LeaseSetType; }; + void SetLeaseSetType (int leaseSetType) { m_LeaseSetType = leaseSetType; }; virtual void CleanupDestination () {}; // additional clean up in derived classes // I2CP virtual void HandleDataMessage (const uint8_t * buf, size_t len) = 0; diff --git a/libi2pd/FS.cpp b/libi2pd/FS.cpp index 21fb103b..f8b3ed7e 100644 --- a/libi2pd/FS.cpp +++ b/libi2pd/FS.cpp @@ -11,6 +11,7 @@ #ifdef _WIN32 #include +#include #endif #include "Base.h" @@ -20,187 +21,211 @@ namespace i2p { namespace fs { - std::string appName = "i2pd"; - std::string dataDir = ""; + std::string appName = "i2pd"; + std::string dataDir = ""; #ifdef _WIN32 - std::string dirSep = "\\"; + std::string dirSep = "\\"; #else - std::string dirSep = "/"; + std::string dirSep = "/"; #endif - const std::string & GetAppName () { - return appName; - } + const std::string & GetAppName () { + return appName; + } - void SetAppName (const std::string& name) { - appName = name; - } + void SetAppName (const std::string& name) { + appName = name; + } - const std::string & GetDataDir () { - return dataDir; - } + const std::string & GetDataDir () { + return dataDir; + } - void DetectDataDir(const std::string & cmdline_param, bool isService) { - if (cmdline_param != "") { - dataDir = cmdline_param; - return; - } + void DetectDataDir(const std::string & cmdline_param, bool isService) { + if (cmdline_param != "") { + dataDir = cmdline_param; + return; + } #if defined(WIN32) || defined(_WIN32) - char localAppData[MAX_PATH]; - // check executable directory first - GetModuleFileName (NULL, localAppData, MAX_PATH); - auto execPath = boost::filesystem::path(localAppData).parent_path(); - // if config file exists in .exe's folder use it - if(boost::filesystem::exists(execPath/"i2pd.conf")) // TODO: magic string - dataDir = execPath.string (); - else - { - // otherwise %appdata% - SHGetFolderPath(NULL, CSIDL_APPDATA, NULL, 0, localAppData); - dataDir = std::string(localAppData) + "\\" + appName; - } - return; + char localAppData[MAX_PATH]; + + // check executable directory first + if(!GetModuleFileName(NULL, localAppData, MAX_PATH)) + { +#if defined(WIN32_APP) + MessageBox(NULL, TEXT("Unable to get application path!"), TEXT("I2Pd: error"), MB_ICONERROR | MB_OK); +#else + fprintf(stderr, "Error: Unable to get application path!"); +#endif + exit(1); + } + else + { + auto execPath = boost::filesystem::path(localAppData).parent_path(); + + // if config file exists in .exe's folder use it + if(boost::filesystem::exists(execPath/"i2pd.conf")) // TODO: magic string + dataDir = execPath.string (); + else // otherwise %appdata% + { + if(SHGetFolderPath(NULL, CSIDL_APPDATA, NULL, 0, localAppData) != S_OK) + { +#if defined(WIN32_APP) + MessageBox(NULL, TEXT("Unable to get AppData path!"), TEXT("I2Pd: error"), MB_ICONERROR | MB_OK); +#else + fprintf(stderr, "Error: Unable to get AppData path!"); +#endif + exit(1); + } + else + dataDir = std::string(localAppData) + "\\" + appName; + } + } + return; #elif defined(MAC_OSX) - char *home = getenv("HOME"); - dataDir = (home != NULL && strlen(home) > 0) ? home : ""; - dataDir += "/Library/Application Support/" + appName; - return; + char *home = getenv("HOME"); + dataDir = (home != NULL && strlen(home) > 0) ? home : ""; + dataDir += "/Library/Application Support/" + appName; + return; #else /* other unix */ #if defined(ANDROID) - const char * ext = getenv("EXTERNAL_STORAGE"); - if (!ext) ext = "/sdcard"; - if (boost::filesystem::exists(ext)) - { - dataDir = std::string (ext) + "/" + appName; + const char * ext = getenv("EXTERNAL_STORAGE"); + if (!ext) ext = "/sdcard"; + if (boost::filesystem::exists(ext)) + { + dataDir = std::string (ext) + "/" + appName; + return; + } +#endif + // otherwise use /data/files + char *home = getenv("HOME"); + if (isService) { + dataDir = "/var/lib/" + appName; + } else if (home != NULL && strlen(home) > 0) { + dataDir = std::string(home) + "/." + appName; + } else { + dataDir = "/tmp/" + appName; + } return; +#endif } - // otherwise use /data/files -#endif - char *home = getenv("HOME"); - if (isService) { - dataDir = "/var/lib/" + appName; - } else if (home != NULL && strlen(home) > 0) { - dataDir = std::string(home) + "/." + appName; - } else { - dataDir = "/tmp/" + appName; - } - return; -#endif - } - bool Init() { - if (!boost::filesystem::exists(dataDir)) - boost::filesystem::create_directory(dataDir); - std::string destinations = DataDirPath("destinations"); - if (!boost::filesystem::exists(destinations)) - boost::filesystem::create_directory(destinations); - std::string tags = DataDirPath("tags"); - if (!boost::filesystem::exists(tags)) - boost::filesystem::create_directory(tags); - else - i2p::garlic::CleanUpTagsFiles (); + bool Init() { + if (!boost::filesystem::exists(dataDir)) + boost::filesystem::create_directory(dataDir); - return true; - } + std::string destinations = DataDirPath("destinations"); + if (!boost::filesystem::exists(destinations)) + boost::filesystem::create_directory(destinations); - bool ReadDir(const std::string & path, std::vector & files) { - if (!boost::filesystem::exists(path)) - return false; - boost::filesystem::directory_iterator it(path); - boost::filesystem::directory_iterator end; + std::string tags = DataDirPath("tags"); + if (!boost::filesystem::exists(tags)) + boost::filesystem::create_directory(tags); + else + i2p::garlic::CleanUpTagsFiles (); - for ( ; it != end; it++) { - if (!boost::filesystem::is_regular_file(it->status())) - continue; - files.push_back(it->path().string()); - } + return true; + } - return true; - } + bool ReadDir(const std::string & path, std::vector & files) { + if (!boost::filesystem::exists(path)) + return false; + boost::filesystem::directory_iterator it(path); + boost::filesystem::directory_iterator end; - bool Exists(const std::string & path) { - return boost::filesystem::exists(path); - } + for ( ; it != end; it++) { + if (!boost::filesystem::is_regular_file(it->status())) + continue; + files.push_back(it->path().string()); + } - uint32_t GetLastUpdateTime (const std::string & path) - { - if (!boost::filesystem::exists(path)) return 0; - boost::system::error_code ec; - auto t = boost::filesystem::last_write_time (path, ec); - return ec ? 0 : t; - } + return true; + } - bool Remove(const std::string & path) { - if (!boost::filesystem::exists(path)) - return false; - return boost::filesystem::remove(path); - } + bool Exists(const std::string & path) { + return boost::filesystem::exists(path); + } + + uint32_t GetLastUpdateTime (const std::string & path) + { + if (!boost::filesystem::exists(path)) + return 0; + boost::system::error_code ec; + auto t = boost::filesystem::last_write_time (path, ec); + return ec ? 0 : t; + } + + bool Remove(const std::string & path) { + if (!boost::filesystem::exists(path)) + return false; + return boost::filesystem::remove(path); + } bool CreateDirectory (const std::string& path) { - if (boost::filesystem::exists(path) && - boost::filesystem::is_directory (boost::filesystem::status (path))) return true; + if (boost::filesystem::exists(path) && boost::filesystem::is_directory (boost::filesystem::status (path))) + return true; return boost::filesystem::create_directory(path); } - void HashedStorage::SetPlace(const std::string &path) { - root = path + i2p::fs::dirSep + name; - } + void HashedStorage::SetPlace(const std::string &path) { + root = path + i2p::fs::dirSep + name; + } - bool HashedStorage::Init(const char * chars, size_t count) { - if (!boost::filesystem::exists(root)) { - boost::filesystem::create_directories(root); - } + bool HashedStorage::Init(const char * chars, size_t count) { + if (!boost::filesystem::exists(root)) { + boost::filesystem::create_directories(root); + } - for (size_t i = 0; i < count; i++) { - auto p = root + i2p::fs::dirSep + prefix1 + chars[i]; - if (boost::filesystem::exists(p)) - continue; - if (boost::filesystem::create_directory(p)) - continue; /* ^ throws exception on failure */ - return false; - } - return true; - } + for (size_t i = 0; i < count; i++) { + auto p = root + i2p::fs::dirSep + prefix1 + chars[i]; + if (boost::filesystem::exists(p)) + continue; + if (boost::filesystem::create_directory(p)) + continue; /* ^ throws exception on failure */ + return false; + } + return true; + } - std::string HashedStorage::Path(const std::string & ident) const { - std::string safe_ident = ident; - std::replace(safe_ident.begin(), safe_ident.end(), '/', '-'); - std::replace(safe_ident.begin(), safe_ident.end(), '\\', '-'); + std::string HashedStorage::Path(const std::string & ident) const { + std::string safe_ident = ident; + std::replace(safe_ident.begin(), safe_ident.end(), '/', '-'); + std::replace(safe_ident.begin(), safe_ident.end(), '\\', '-'); - std::stringstream t(""); - t << this->root << i2p::fs::dirSep; - t << prefix1 << safe_ident[0] << i2p::fs::dirSep; - t << prefix2 << safe_ident << "." << suffix; + std::stringstream t(""); + t << this->root << i2p::fs::dirSep; + t << prefix1 << safe_ident[0] << i2p::fs::dirSep; + t << prefix2 << safe_ident << "." << suffix; - return t.str(); - } + return t.str(); + } - void HashedStorage::Remove(const std::string & ident) { - std::string path = Path(ident); - if (!boost::filesystem::exists(path)) - return; - boost::filesystem::remove(path); - } + void HashedStorage::Remove(const std::string & ident) { + std::string path = Path(ident); + if (!boost::filesystem::exists(path)) + return; + boost::filesystem::remove(path); + } - void HashedStorage::Traverse(std::vector & files) { - Iterate([&files] (const std::string & fname) { - files.push_back(fname); - }); - } + void HashedStorage::Traverse(std::vector & files) { + Iterate([&files] (const std::string & fname) { + files.push_back(fname); + }); + } - void HashedStorage::Iterate(FilenameVisitor v) - { - boost::filesystem::path p(root); - boost::filesystem::recursive_directory_iterator it(p); - boost::filesystem::recursive_directory_iterator end; + void HashedStorage::Iterate(FilenameVisitor v) + { + boost::filesystem::path p(root); + boost::filesystem::recursive_directory_iterator it(p); + boost::filesystem::recursive_directory_iterator end; - for ( ; it != end; it++) { - if (!boost::filesystem::is_regular_file( it->status() )) - continue; - const std::string & t = it->path().string(); - v(t); - } - } + for ( ; it != end; it++) { + if (!boost::filesystem::is_regular_file( it->status() )) + continue; + const std::string & t = it->path().string(); + v(t); + } + } } // fs } // i2p diff --git a/libi2pd/Identity.cpp b/libi2pd/Identity.cpp index 02658ecc..121f2f46 100644 --- a/libi2pd/Identity.cpp +++ b/libi2pd/Identity.cpp @@ -432,6 +432,9 @@ namespace data m_Public = std::make_shared(Identity (keys)); memcpy (m_PrivateKey, keys.privateKey, 256); // 256 memcpy (m_SigningPrivateKey, keys.signingPrivateKey, m_Public->GetSigningPrivateKeyLen ()); + m_OfflineSignature.resize (0); + m_TransientSignatureLen = 0; + m_TransientSigningPrivateKeyLen = 0; m_Signer = nullptr; CreateSigner (); return *this; @@ -441,12 +444,23 @@ namespace data { m_Public = std::make_shared(*other.m_Public); memcpy (m_PrivateKey, other.m_PrivateKey, 256); // 256 - memcpy (m_SigningPrivateKey, other.m_SigningPrivateKey, m_Public->GetSigningPrivateKeyLen ()); + m_OfflineSignature = other.m_OfflineSignature; + m_TransientSignatureLen = other.m_TransientSignatureLen; + m_TransientSigningPrivateKeyLen = other.m_TransientSigningPrivateKeyLen; + memcpy (m_SigningPrivateKey, other.m_SigningPrivateKey, m_TransientSigningPrivateKeyLen > 0 ? m_TransientSigningPrivateKeyLen : m_Public->GetSigningPrivateKeyLen ()); m_Signer = nullptr; CreateSigner (); return *this; } + size_t PrivateKeys::GetFullLen () const + { + size_t ret = m_Public->GetFullLen () + 256 + m_Public->GetSigningPrivateKeyLen (); + if (IsOfflineSignature ()) + ret += m_OfflineSignature.size () + m_TransientSigningPrivateKeyLen; + return ret; + } + size_t PrivateKeys::FromBuffer (const uint8_t * buf, size_t len) { m_Public = std::make_shared(); @@ -455,11 +469,50 @@ namespace data memcpy (m_PrivateKey, buf + ret, 256); // private key always 256 ret += 256; size_t signingPrivateKeySize = m_Public->GetSigningPrivateKeyLen (); - if(signingPrivateKeySize + ret > len) return 0; // overflow + if(signingPrivateKeySize + ret > len || signingPrivateKeySize > 128) return 0; // overflow memcpy (m_SigningPrivateKey, buf + ret, signingPrivateKeySize); ret += signingPrivateKeySize; m_Signer = nullptr; - CreateSigner (); + // check if signing private key is all zeros + bool allzeros = true; + for (size_t i = 0; i < signingPrivateKeySize; i++) + if (m_SigningPrivateKey[i]) + { + allzeros = false; + break; + } + if (allzeros) + { + // offline information + const uint8_t * offlineInfo = buf + ret; + ret += 4; // expires timestamp + SigningKeyType keyType = bufbe16toh (buf + ret); ret += 2; // key type + std::unique_ptr transientVerifier (IdentityEx::CreateVerifier (keyType)); + if (!transientVerifier) return 0; + auto keyLen = transientVerifier->GetPublicKeyLen (); + if (keyLen + ret > len) return 0; + transientVerifier->SetPublicKey (buf + ret); ret += keyLen; + if (m_Public->GetSignatureLen () + ret > len) return 0; + if (!m_Public->Verify (offlineInfo, keyLen + 6, buf + ret)) + { + LogPrint (eLogError, "Identity: offline signature verification failed"); + return 0; + } + ret += m_Public->GetSignatureLen (); + m_TransientSignatureLen = transientVerifier->GetSignatureLen (); + // copy offline signature + size_t offlineInfoLen = buf + ret - offlineInfo; + m_OfflineSignature.resize (offlineInfoLen); + memcpy (m_OfflineSignature.data (), offlineInfo, offlineInfoLen); + // override signing private key + m_TransientSigningPrivateKeyLen = transientVerifier->GetPrivateKeyLen (); + if (m_TransientSigningPrivateKeyLen + ret > len || m_TransientSigningPrivateKeyLen > 128) return 0; + memcpy (m_SigningPrivateKey, buf + ret, m_TransientSigningPrivateKeyLen); + ret += m_TransientSigningPrivateKeyLen; + CreateSigner (keyType); + } + else + CreateSigner (m_Public->GetSigningKeyType ()); return ret; } @@ -470,8 +523,23 @@ namespace data ret += 256; size_t signingPrivateKeySize = m_Public->GetSigningPrivateKeyLen (); if(ret + signingPrivateKeySize > len) return 0; // overflow - memcpy (buf + ret, m_SigningPrivateKey, signingPrivateKeySize); + if (IsOfflineSignature ()) + memset (buf + ret, 0, signingPrivateKeySize); + else + memcpy (buf + ret, m_SigningPrivateKey, signingPrivateKeySize); ret += signingPrivateKeySize; + if (IsOfflineSignature ()) + { + // offline signature + auto offlineSignatureLen = m_OfflineSignature.size (); + if (ret + offlineSignatureLen > len) return 0; + memcpy (buf + ret, m_OfflineSignature.data (), offlineSignatureLen); + ret += offlineSignatureLen; + // transient private key + if (ret + m_TransientSigningPrivateKeyLen > len) return 0; + memcpy (buf + ret, m_SigningPrivateKey, m_TransientSigningPrivateKeyLen); + ret += m_TransientSigningPrivateKeyLen; + } return ret; } @@ -505,9 +573,17 @@ namespace data } void PrivateKeys::CreateSigner () const + { + if (IsOfflineSignature ()) + CreateSigner (bufbe16toh (m_OfflineSignature.data () + 4)); // key type + else + CreateSigner (m_Public->GetSigningKeyType ()); + } + + void PrivateKeys::CreateSigner (SigningKeyType keyType) const { if (m_Signer) return; - switch (m_Public->GetSigningKeyType ()) + switch (keyType) { case SIGNING_KEY_TYPE_DSA_SHA1: m_Signer.reset (new i2p::crypto::DSASigner (m_SigningPrivateKey, m_Public->GetStandardIdentity ().signingKey)); @@ -527,7 +603,7 @@ namespace data LogPrint (eLogError, "Identity: RSA signing key type ", (int)m_Public->GetSigningKeyType (), " is not supported"); break; case SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519: - m_Signer.reset (new i2p::crypto::EDDSA25519Signer (m_SigningPrivateKey, m_Public->GetStandardIdentity ().certificate - i2p::crypto::EDDSA25519_PUBLIC_KEY_LENGTH)); + m_Signer.reset (new i2p::crypto::EDDSA25519Signer (m_SigningPrivateKey, IsOfflineSignature () ? nullptr: m_Public->GetStandardIdentity ().certificate - i2p::crypto::EDDSA25519_PUBLIC_KEY_LENGTH)); // TODO: remove public key check break; case SIGNING_KEY_TYPE_GOSTR3410_CRYPTO_PRO_A_GOSTR3411_256: m_Signer.reset (new i2p::crypto::GOSTR3410_256_Signer (i2p::crypto::eGOSTR3410CryptoProA, m_SigningPrivateKey)); @@ -540,6 +616,11 @@ namespace data } } + size_t PrivateKeys::GetSignatureLen () const + { + return IsOfflineSignature () ? m_TransientSignatureLen : m_Public->GetSignatureLen (); + } + uint8_t * PrivateKeys::GetPadding() { if(m_Public->GetSigningKeyType () == SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519) @@ -582,35 +663,7 @@ namespace data PrivateKeys keys; // signature uint8_t signingPublicKey[512]; // signing public key is 512 bytes max - switch (type) - { - case SIGNING_KEY_TYPE_ECDSA_SHA256_P256: - i2p::crypto::CreateECDSAP256RandomKeys (keys.m_SigningPrivateKey, signingPublicKey); - break; - case SIGNING_KEY_TYPE_ECDSA_SHA384_P384: - i2p::crypto::CreateECDSAP384RandomKeys (keys.m_SigningPrivateKey, signingPublicKey); - break; - case SIGNING_KEY_TYPE_ECDSA_SHA512_P521: - i2p::crypto::CreateECDSAP521RandomKeys (keys.m_SigningPrivateKey, signingPublicKey); - break; - case SIGNING_KEY_TYPE_RSA_SHA256_2048: - case SIGNING_KEY_TYPE_RSA_SHA384_3072: - case SIGNING_KEY_TYPE_RSA_SHA512_4096: - LogPrint (eLogWarning, "Identity: RSA signature type is not supported. Creating EdDSA"); - // no break here - case SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519: - i2p::crypto::CreateEDDSA25519RandomKeys (keys.m_SigningPrivateKey, signingPublicKey); - break; - case SIGNING_KEY_TYPE_GOSTR3410_CRYPTO_PRO_A_GOSTR3411_256: - i2p::crypto::CreateGOSTR3410RandomKeys (i2p::crypto::eGOSTR3410CryptoProA, keys.m_SigningPrivateKey, signingPublicKey); - break; - case SIGNING_KEY_TYPE_GOSTR3410_TC26_A_512_GOSTR3411_512: - i2p::crypto::CreateGOSTR3410RandomKeys (i2p::crypto::eGOSTR3410TC26A512, keys.m_SigningPrivateKey, signingPublicKey); - break; - default: - LogPrint (eLogWarning, "Identity: Signing key type ", (int)type, " is not supported. Create DSA-SHA1"); - return PrivateKeys (i2p::data::CreateRandomKeys ()); // DSA-SHA1 - } + GenerateSigningKeyPair (type, keys.m_SigningPrivateKey, signingPublicKey); // encryption uint8_t publicKey[256]; GenerateCryptoKeyPair (cryptoType, keys.m_PrivateKey, publicKey); @@ -623,6 +676,39 @@ namespace data return PrivateKeys (i2p::data::CreateRandomKeys ()); // DSA-SHA1 } + void PrivateKeys::GenerateSigningKeyPair (SigningKeyType type, uint8_t * priv, uint8_t * pub) + { + switch (type) + { + case SIGNING_KEY_TYPE_ECDSA_SHA256_P256: + i2p::crypto::CreateECDSAP256RandomKeys (priv, pub); + break; + case SIGNING_KEY_TYPE_ECDSA_SHA384_P384: + i2p::crypto::CreateECDSAP384RandomKeys (priv, pub); + break; + case SIGNING_KEY_TYPE_ECDSA_SHA512_P521: + i2p::crypto::CreateECDSAP521RandomKeys (priv, pub); + break; + case SIGNING_KEY_TYPE_RSA_SHA256_2048: + case SIGNING_KEY_TYPE_RSA_SHA384_3072: + case SIGNING_KEY_TYPE_RSA_SHA512_4096: + LogPrint (eLogWarning, "Identity: RSA signature type is not supported. Creating EdDSA"); + // no break here + case SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519: + i2p::crypto::CreateEDDSA25519RandomKeys (priv, pub); + break; + case SIGNING_KEY_TYPE_GOSTR3410_CRYPTO_PRO_A_GOSTR3411_256: + i2p::crypto::CreateGOSTR3410RandomKeys (i2p::crypto::eGOSTR3410CryptoProA, priv, pub); + break; + case SIGNING_KEY_TYPE_GOSTR3410_TC26_A_512_GOSTR3411_512: + i2p::crypto::CreateGOSTR3410RandomKeys (i2p::crypto::eGOSTR3410TC26A512, priv, pub); + break; + default: + LogPrint (eLogWarning, "Identity: Signing key type ", (int)type, " is not supported. Create DSA-SHA1"); + i2p::crypto::CreateDSARandomKeys (priv, pub); // DSA-SHA1 + } + } + void PrivateKeys::GenerateCryptoKeyPair (CryptoKeyType type, uint8_t * priv, uint8_t * pub) { switch (type) @@ -642,6 +728,27 @@ namespace data } } + PrivateKeys PrivateKeys::CreateOfflineKeys (SigningKeyType type, uint32_t expires) const + { + PrivateKeys keys (*this); + std::unique_ptr verifier (IdentityEx::CreateVerifier (type)); + if (verifier) + { + size_t pubKeyLen = verifier->GetPublicKeyLen (); + keys.m_TransientSigningPrivateKeyLen = verifier->GetPrivateKeyLen (); + keys.m_TransientSignatureLen = verifier->GetSignatureLen (); + keys.m_OfflineSignature.resize (pubKeyLen + m_Public->GetSignatureLen () + 6); + htobe32buf (keys.m_OfflineSignature.data (), expires); // expires + htobe16buf (keys.m_OfflineSignature.data () + 4, type); // type + GenerateSigningKeyPair (type, keys.m_SigningPrivateKey, keys.m_OfflineSignature.data () + 6); // public key + Sign (keys.m_OfflineSignature.data (), pubKeyLen + 6, keys.m_OfflineSignature.data () + 6 + pubKeyLen); // signature + // recreate signer + keys.m_Signer = nullptr; + keys.CreateSigner (type); + } + return keys; + } + Keys CreateRandomKeys () { Keys keys; diff --git a/libi2pd/Identity.h b/libi2pd/Identity.h index 2a91ef48..13b84f78 100644 --- a/libi2pd/Identity.h +++ b/libi2pd/Identity.h @@ -6,6 +6,7 @@ #include #include #include +#include #include "Base.h" #include "Signature.h" #include "CryptoKey.h" @@ -142,11 +143,13 @@ namespace data std::shared_ptr GetPublic () const { return m_Public; }; const uint8_t * GetPrivateKey () const { return m_PrivateKey; }; const uint8_t * GetSigningPrivateKey () const { return m_SigningPrivateKey; }; - uint8_t * GetPadding(); - void RecalculateIdentHash(uint8_t * buf=nullptr) { m_Public->RecalculateIdentHash(buf); } + size_t GetSignatureLen () const; // might not match identity + bool IsOfflineSignature () const { return m_TransientSignatureLen > 0; }; + uint8_t * GetPadding(); + void RecalculateIdentHash(uint8_t * buf=nullptr) { m_Public->RecalculateIdentHash(buf); } 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 GetFullLen () const; size_t FromBuffer (const uint8_t * buf, size_t len); size_t ToBuffer (uint8_t * buf, size_t len) const; @@ -157,18 +160,27 @@ namespace data static std::shared_ptr CreateDecryptor (CryptoKeyType cryptoType, const uint8_t * key); static PrivateKeys CreateRandomKeys (SigningKeyType type = SIGNING_KEY_TYPE_DSA_SHA1, CryptoKeyType cryptoType = CRYPTO_KEY_TYPE_ELGAMAL); + static void GenerateSigningKeyPair (SigningKeyType type, uint8_t * priv, uint8_t * pub); static void GenerateCryptoKeyPair (CryptoKeyType type, uint8_t * priv, uint8_t * pub); // priv and pub are 256 bytes long + // offline keys + PrivateKeys CreateOfflineKeys (SigningKeyType type, uint32_t expires) const; + const std::vector& GetOfflineSignature () const { return m_OfflineSignature; }; + private: void CreateSigner () const; + void CreateSigner (SigningKeyType keyType) const; private: std::shared_ptr m_Public; uint8_t m_PrivateKey[256]; - uint8_t m_SigningPrivateKey[1024]; // assume private key doesn't exceed 1024 bytes + uint8_t m_SigningPrivateKey[128]; // assume private key doesn't exceed 128 bytes mutable std::unique_ptr m_Signer; + std::vector m_OfflineSignature; // non zero length, if applicable + size_t m_TransientSignatureLen = 0; + size_t m_TransientSigningPrivateKeyLen = 0; }; // kademlia diff --git a/libi2pd/LeaseSet.cpp b/libi2pd/LeaseSet.cpp index 3d72401d..de38d568 100644 --- a/libi2pd/LeaseSet.cpp +++ b/libi2pd/LeaseSet.cpp @@ -262,43 +262,38 @@ namespace data void LeaseSet2::Update (const uint8_t * buf, size_t len, bool verifySignature) { - // shouldn't be called for now. Must be called from NetDb::AddLeaseSet later SetBuffer (buf, len); - // TODO:verify signature if requested + if (GetStoreType () != NETDB_STORE_TYPE_ENCRYPTED_LEASESET2) + ReadFromBuffer (buf, len, false, verifySignature); + // TODO: implement encrypted } - void LeaseSet2::ReadFromBuffer (const uint8_t * buf, size_t len) + void LeaseSet2::ReadFromBuffer (const uint8_t * buf, size_t len, bool readIdentity, bool verifySignature) { // standard LS2 header - auto identity = std::make_shared(buf, len); - SetIdentity (identity); + std::shared_ptr identity; + if (readIdentity) + { + identity = std::make_shared(buf, len); + SetIdentity (identity); + } + else + identity = GetIdentity (); size_t offset = identity->GetFullLen (); if (offset + 8 >= len) return; uint32_t timestamp = bufbe32toh (buf + offset); offset += 4; // published timestamp (seconds) uint16_t expires = bufbe16toh (buf + offset); offset += 2; // expires (seconds) SetExpirationTime ((timestamp + expires)*1000LL); // in milliseconds uint16_t flags = bufbe16toh (buf + offset); offset += 2; // flags - std::unique_ptr transientVerifier; - if (flags & 0x0001) + if (flags & LEASESET2_FLAG_OFFLINE_KEYS) { // transient key - if (offset + 6 >= len) return; - const uint8_t * signedData = buf + offset; - uint32_t expiresTimestamp = bufbe32toh (buf + offset); offset += 4; // expires timestamp - if (expiresTimestamp < i2p::util::GetSecondsSinceEpoch ()) - { - LogPrint (eLogWarning, "LeaseSet2: transient key expired"); + m_TransientVerifier = ProcessOfflineSignature (identity, buf, len, offset); + if (!m_TransientVerifier) + { + LogPrint (eLogError, "LeaseSet2: offline signature failed"); return; - } - uint16_t keyType = bufbe16toh (buf + offset); offset += 2; - transientVerifier.reset (i2p::data::IdentityEx::CreateVerifier (keyType)); - if (!transientVerifier) return; - auto keyLen = transientVerifier->GetPublicKeyLen (); - if (offset + keyLen >= len) return; - transientVerifier->SetPublicKey (buf + offset); offset += keyLen; - if (offset + identity->GetSignatureLen () >= len) return; - if (!identity->Verify (signedData, keyLen + 6, buf + offset)) return; - offset += identity->GetSignatureLen (); + } } // type specific part size_t s = 0; @@ -315,10 +310,13 @@ namespace data } if (!s) return; offset += s; - // verify signature - bool verified = transientVerifier ? VerifySignature (transientVerifier, buf, len, offset) : - VerifySignature (identity, buf, len, offset); - SetIsValid (verified); + if (verifySignature || m_TransientVerifier) + { + // verify signature + bool verified = m_TransientVerifier ? VerifySignature (m_TransientVerifier, buf, len, offset) : + VerifySignature (identity, buf, len, offset); + SetIsValid (verified); + } } template @@ -344,6 +342,7 @@ namespace data offset += propertiesLen; // skip for now. TODO: implement properties if (offset + 1 >= len) return 0; // key sections + uint16_t currentKeyType = 0; int numKeySections = buf[offset]; offset++; for (int i = 0; i < numKeySections; i++) { @@ -351,10 +350,16 @@ namespace data if (offset + 2 >= len) return 0; uint16_t encryptionKeyLen = bufbe16toh (buf + offset); offset += 2; if (offset + encryptionKeyLen >= len) return 0; - if (!m_Encryptor && IsStoreLeases ()) // create encryptor with leases only, first key + if (IsStoreLeases ()) // create encryptor with leases only { + // we pick first valid key, higher key type has higher priority 4-1-0 + // if two keys with of the same type, pick first auto encryptor = i2p::data::IdentityEx::CreateEncryptor (keyType, buf + offset); - m_Encryptor = encryptor; // TODO: atomic + if (encryptor && (!m_Encryptor || keyType > currentKeyType)) + { + m_Encryptor = encryptor; // TODO: atomic + currentKeyType = keyType; + } } offset += encryptionKeyLen; } @@ -426,33 +431,21 @@ namespace data uint16_t expires = bufbe16toh (buf + offset); offset += 2; // expires (seconds) SetExpirationTime ((timestamp + expires)*1000LL); // in milliseconds uint16_t flags = bufbe16toh (buf + offset); offset += 2; // flags - std::unique_ptr transientVerifier; - if (flags & 0x0001) + if (flags & LEASESET2_FLAG_OFFLINE_KEYS) { // transient key - if (offset + 6 >= len) return; - const uint8_t * signedData = buf + offset; - uint32_t expiresTimestamp = bufbe32toh (buf + offset); offset += 4; // expires timestamp - if (expiresTimestamp < i2p::util::GetSecondsSinceEpoch ()) + m_TransientVerifier = ProcessOfflineSignature (blindedVerifier, buf, len, offset); + if (!m_TransientVerifier) { - LogPrint (eLogWarning, "LeaseSet2: transient key expired"); + LogPrint (eLogError, "LeaseSet2: offline signature failed"); return; - } - uint16_t keyType = bufbe16toh (buf + offset); offset += 2; - transientVerifier.reset (i2p::data::IdentityEx::CreateVerifier (keyType)); - if (!transientVerifier) return; - auto keyLen = transientVerifier->GetPublicKeyLen (); - if (offset + keyLen >= len) return; - transientVerifier->SetPublicKey (buf + offset); offset += keyLen; - if (offset + blindedVerifier->GetSignatureLen () >= len) return; - if (!blindedVerifier->Verify (signedData, keyLen + 6, buf + offset)) return; - offset += blindedVerifier->GetSignatureLen (); + } } // outer ciphertext if (offset + 2 > len) return; uint16_t lenOuterCiphertext = bufbe16toh (buf + offset); offset += 2 + lenOuterCiphertext; // verify signature - bool verified = transientVerifier ? VerifySignature (transientVerifier, buf, len, offset) : + bool verified = m_TransientVerifier ? VerifySignature (m_TransientVerifier, buf, len, offset) : VerifySignature (blindedVerifier, buf, len, offset); SetIsValid (verified); } @@ -586,16 +579,24 @@ namespace data return ident.Verify(ptr, leases - ptr, leases); } - LocalLeaseSet2::LocalLeaseSet2 (uint8_t storeType, std::shared_ptr identity, + LocalLeaseSet2::LocalLeaseSet2 (uint8_t storeType, const i2p::data::PrivateKeys& keys, uint16_t keyType, uint16_t keyLen, const uint8_t * encryptionPublicKey, std::vector > tunnels): - LocalLeaseSet (identity, nullptr, 0) + LocalLeaseSet (keys.GetPublic (), nullptr, 0) { + auto identity = keys.GetPublic (); // assume standard LS2 int num = tunnels.size (); if (num > MAX_NUM_LEASES) num = MAX_NUM_LEASES; m_BufferLen = identity->GetFullLen () + 4/*published*/ + 2/*expires*/ + 2/*flag*/ + 2/*properties len*/ + - 1/*num keys*/ + 2/*key type*/ + 2/*key len*/ + keyLen/*key*/ + 1/*num leases*/ + num*LEASE2_SIZE + identity->GetSignatureLen (); + 1/*num keys*/ + 2/*key type*/ + 2/*key len*/ + keyLen/*key*/ + 1/*num leases*/ + num*LEASE2_SIZE + keys.GetSignatureLen (); + uint16_t flags = 0; + if (keys.IsOfflineSignature ()) + { + flags |= LEASESET2_FLAG_OFFLINE_KEYS; + m_BufferLen += keys.GetOfflineSignature ().size (); + } + m_Buffer = new uint8_t[m_BufferLen + 1]; m_Buffer[0] = storeType; // LS2 header @@ -603,7 +604,14 @@ namespace data auto timestamp = i2p::util::GetSecondsSinceEpoch (); htobe32buf (m_Buffer + offset, timestamp); offset += 4; // published timestamp (seconds) uint8_t * expiresBuf = m_Buffer + offset; offset += 2; // expires, fill later - htobe16buf (m_Buffer + offset, 0); offset += 2; // flags + htobe16buf (m_Buffer + offset, flags); offset += 2; // flags + if (keys.IsOfflineSignature ()) + { + // offline signature + const auto& offlineSignature = keys.GetOfflineSignature (); + memcpy (m_Buffer + offset, offlineSignature.data (), offlineSignature.size ()); + offset += offlineSignature.size (); + } htobe16buf (m_Buffer + offset, 0); offset += 2; // properties len // keys m_Buffer[offset] = 1; offset++; // 1 key @@ -628,7 +636,17 @@ namespace data SetExpirationTime (expirationTime*1000LL); auto expires = expirationTime - timestamp; htobe16buf (expiresBuf, expires > 0 ? expires : 0); - // we don't sign it yet. must be signed later on + // sign + keys.Sign (m_Buffer, offset, m_Buffer + offset); // LS + leading store type + } + + LocalLeaseSet2::LocalLeaseSet2 (uint8_t storeType, std::shared_ptr identity, const uint8_t * buf, size_t len): + LocalLeaseSet (identity, nullptr, 0) + { + m_BufferLen = len; + m_Buffer = new uint8_t[m_BufferLen + 1]; + memcpy (m_Buffer + 1, buf, len); + m_Buffer[0] = storeType; } } } diff --git a/libi2pd/LeaseSet.h b/libi2pd/LeaseSet.h index 10e60c4a..44ee873c 100644 --- a/libi2pd/LeaseSet.h +++ b/libi2pd/LeaseSet.h @@ -8,6 +8,7 @@ #include #include "Identity.h" #include "Timestamp.h" +#include "I2PEndian.h" namespace i2p { @@ -77,7 +78,8 @@ namespace data bool operator== (const LeaseSet& other) const { return m_BufferLen == other.m_BufferLen && !memcmp (m_Buffer, other.m_Buffer, m_BufferLen); }; virtual uint8_t GetStoreType () const { return NETDB_STORE_TYPE_LEASESET; }; - + virtual std::shared_ptr GetTransientVerifier () const { return nullptr; }; + // implements RoutingDestination std::shared_ptr GetIdentity () const { return m_Identity; }; void Encrypt (const uint8_t * data, uint8_t * encrypted, BN_CTX * ctx) const; @@ -122,20 +124,24 @@ namespace data const uint8_t NETDB_STORE_TYPE_STANDARD_LEASESET2 = 3; const uint8_t NETDB_STORE_TYPE_ENCRYPTED_LEASESET2 = 5; const uint8_t NETDB_STORE_TYPE_META_LEASESET2 = 7; + + const uint16_t LEASESET2_FLAG_OFFLINE_KEYS = 0x0001; + class LeaseSet2: public LeaseSet { public: LeaseSet2 (uint8_t storeType, const uint8_t * buf, size_t len, bool storeLeases = true); uint8_t GetStoreType () const { return m_StoreType; }; + std::shared_ptr GetTransientVerifier () const { return m_TransientVerifier; }; void Update (const uint8_t * buf, size_t len, bool verifySignature); - + // implements RoutingDestination void Encrypt (const uint8_t * data, uint8_t * encrypted, BN_CTX * ctx) const; private: - void ReadFromBuffer (const uint8_t * buf, size_t len); + void ReadFromBuffer (const uint8_t * buf, size_t len, bool readIdentity = true, bool verifySignature = true); void ReadFromBufferEncrypted (const uint8_t * buf, size_t len); size_t ReadStandardLS2TypeSpecificPart (const uint8_t * buf, size_t len); size_t ReadMetaLS2TypeSpecificPart (const uint8_t * buf, size_t len); @@ -148,9 +154,31 @@ namespace data private: uint8_t m_StoreType; + std::shared_ptr m_TransientVerifier; std::shared_ptr m_Encryptor; // for standardLS2 }; + // also called from Streaming.cpp + template + std::shared_ptr ProcessOfflineSignature (const Verifier& verifier, const uint8_t * buf, size_t len, size_t& offset) + { + if (offset + 6 >= len) return nullptr; + const uint8_t * signedData = buf + offset; + uint32_t expiresTimestamp = bufbe32toh (buf + offset); offset += 4; // expires timestamp + if (expiresTimestamp < i2p::util::GetSecondsSinceEpoch ()) return nullptr; + uint16_t keyType = bufbe16toh (buf + offset); offset += 2; + std::shared_ptr transientVerifier (i2p::data::IdentityEx::CreateVerifier (keyType)); + if (!transientVerifier) return nullptr; + auto keyLen = transientVerifier->GetPublicKeyLen (); + if (offset + keyLen >= len) return nullptr; + transientVerifier->SetPublicKey (buf + offset); offset += keyLen; + if (offset + verifier->GetSignatureLen () >= len) return nullptr; + if (!verifier->Verify (signedData, keyLen + 6, buf + offset)) return nullptr; + offset += verifier->GetSignatureLen (); + return transientVerifier; + } + +//------------------------------------------------------------------------------------ class LocalLeaseSet { public: @@ -186,9 +214,10 @@ namespace data { public: - LocalLeaseSet2 (uint8_t storeType, std::shared_ptr identity, + LocalLeaseSet2 (uint8_t storeType, const i2p::data::PrivateKeys& keys, uint16_t keyType, uint16_t keyLen, const uint8_t * encryptionPublicKey, std::vector > tunnels); + LocalLeaseSet2 (uint8_t storeType, std::shared_ptr identity, const uint8_t * buf, size_t len); virtual ~LocalLeaseSet2 () { delete[] m_Buffer; }; uint8_t * GetBuffer () const { return m_Buffer + 1; }; diff --git a/libi2pd/Streaming.cpp b/libi2pd/Streaming.cpp index e8386b61..4cd1b1bf 100644 --- a/libi2pd/Streaming.cpp +++ b/libi2pd/Streaming.cpp @@ -217,61 +217,17 @@ namespace stream void Stream::ProcessPacket (Packet * packet) { - // process flags uint32_t receivedSeqn = packet->GetSeqn (); uint16_t flags = packet->GetFlags (); LogPrint (eLogDebug, "Streaming: Process seqn=", receivedSeqn, ", flags=", flags); - const uint8_t * optionData = packet->GetOptionData (); - - if (flags & PACKET_FLAG_DELAY_REQUESTED) - optionData += 2; - - if (flags & PACKET_FLAG_FROM_INCLUDED) + if (!ProcessOptions (flags, packet)) { - m_RemoteIdentity = std::make_shared(optionData, packet->GetOptionSize ()); - if (m_RemoteIdentity->IsRSA ()) - { - LogPrint (eLogInfo, "Streaming: Incoming stream from RSA destination ", m_RemoteIdentity->GetIdentHash ().ToBase64 (), " Discarded"); - m_LocalDestination.DeletePacket (packet); - Terminate (); - return; - } - optionData += m_RemoteIdentity->GetFullLen (); - if (!m_RemoteLeaseSet) - LogPrint (eLogDebug, "Streaming: Incoming stream from ", m_RemoteIdentity->GetIdentHash ().ToBase64 (), ", sSID=", m_SendStreamID, ", rSID=", m_RecvStreamID); + m_LocalDestination.DeletePacket (packet); + Terminate (); + return; } - - if (flags & PACKET_FLAG_MAX_PACKET_SIZE_INCLUDED) - { - uint16_t maxPacketSize = bufbe16toh (optionData); - LogPrint (eLogDebug, "Streaming: Max packet size ", maxPacketSize); - optionData += 2; - } - - if (flags & PACKET_FLAG_SIGNATURE_INCLUDED) - { - uint8_t signature[256]; - auto signatureLen = m_RemoteIdentity->GetSignatureLen (); - if(signatureLen <= sizeof(signature)) - { - memcpy (signature, optionData, signatureLen); - memset (const_cast(optionData), 0, signatureLen); - if (!m_RemoteIdentity->Verify (packet->GetBuffer (), packet->GetLength (), signature)) - { - LogPrint (eLogError, "Streaming: Signature verification failed, sSID=", m_SendStreamID, ", rSID=", m_RecvStreamID); - Close (); - flags |= PACKET_FLAG_CLOSE; - } - memcpy (const_cast(optionData), signature, signatureLen); - optionData += signatureLen; - } - else - { - LogPrint(eLogError, "Streaming: Signature too big, ", signatureLen, " bytes"); - } - } - + packet->offset = packet->GetPayload () - packet->buf; if (packet->GetLength () > 0) { @@ -298,6 +254,94 @@ namespace stream } } + bool Stream::ProcessOptions (uint16_t flags, Packet * packet) + { + const uint8_t * optionData = packet->GetOptionData (); + size_t optionSize = packet->GetOptionSize (); + if (flags & PACKET_FLAG_DELAY_REQUESTED) + optionData += 2; + + if (flags & PACKET_FLAG_FROM_INCLUDED) + { + if (m_RemoteLeaseSet) m_RemoteIdentity = m_RemoteLeaseSet->GetIdentity (); + if (!m_RemoteIdentity) + m_RemoteIdentity = std::make_shared(optionData, optionSize); + if (m_RemoteIdentity->IsRSA ()) + { + LogPrint (eLogInfo, "Streaming: Incoming stream from RSA destination ", m_RemoteIdentity->GetIdentHash ().ToBase64 (), " Discarded"); + return false; + } + optionData += m_RemoteIdentity->GetFullLen (); + if (!m_RemoteLeaseSet) + LogPrint (eLogDebug, "Streaming: Incoming stream from ", m_RemoteIdentity->GetIdentHash ().ToBase64 (), ", sSID=", m_SendStreamID, ", rSID=", m_RecvStreamID); + } + + if (flags & PACKET_FLAG_MAX_PACKET_SIZE_INCLUDED) + { + uint16_t maxPacketSize = bufbe16toh (optionData); + LogPrint (eLogDebug, "Streaming: Max packet size ", maxPacketSize); + optionData += 2; + } + + if (flags & PACKET_FLAG_OFFLINE_SIGNATURE) + { + if (!m_RemoteIdentity) + { + LogPrint (eLogInfo, "Streaming: offline signature without identity"); + return false; + } + // if we have it in LeaseSet already we don't neet parse it again + if (m_RemoteLeaseSet) m_TransientVerifier = m_RemoteLeaseSet->GetTransientVerifier (); + if (m_TransientVerifier) + { + // skip option data + optionData += 6; // timestamp and key type + optionData += m_TransientVerifier->GetPublicKeyLen (); // public key + optionData += m_RemoteIdentity->GetSignatureLen (); // signature + } + else + { + // transient key + size_t offset = 0; + m_TransientVerifier = i2p::data::ProcessOfflineSignature (m_RemoteIdentity, optionData, optionSize - (optionData - packet->GetOptionData ()), offset); + optionData += offset; + if (!m_TransientVerifier) + { + LogPrint (eLogError, "Streaming: offline signature failed"); + return false; + } + } + } + + if (flags & PACKET_FLAG_SIGNATURE_INCLUDED) + { + uint8_t signature[256]; + auto signatureLen = m_RemoteIdentity->GetSignatureLen (); + if(signatureLen <= sizeof(signature)) + { + memcpy (signature, optionData, signatureLen); + memset (const_cast(optionData), 0, signatureLen); + bool verified = m_TransientVerifier ? + m_TransientVerifier->Verify (packet->GetBuffer (), packet->GetLength (), signature) : + m_RemoteIdentity->Verify (packet->GetBuffer (), packet->GetLength (), signature); + if (!verified) + { + LogPrint (eLogError, "Streaming: Signature verification failed, sSID=", m_SendStreamID, ", rSID=", m_RecvStreamID); + Close (); + flags |= PACKET_FLAG_CLOSE; + } + memcpy (const_cast(optionData), signature, signatureLen); + optionData += signatureLen; + } + else + { + LogPrint (eLogError, "Streaming: Signature too big, ", signatureLen, " bytes"); + return false; + } + } + return true; + } + void Stream::ProcessAck (Packet * packet) { bool acknowledged = false; @@ -438,19 +482,28 @@ namespace stream 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; + bool isOfflineSignature = m_LocalDestination.GetOwner ()->GetPrivateKeys ().IsOfflineSignature (); + if (isOfflineSignature) flags |= PACKET_FLAG_OFFLINE_SIGNATURE; 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_t signatureLen = m_LocalDestination.GetOwner ()->GetPrivateKeys ().GetSignatureLen (); + uint8_t * optionsSize = packet + size; // set options size later 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 + if (isOfflineSignature) + { + const auto& offlineSignature = m_LocalDestination.GetOwner ()->GetPrivateKeys ().GetOfflineSignature (); + memcpy (packet + size, offlineSignature.data (), offlineSignature.size ()); + size += offlineSignature.size (); // offline signature + } uint8_t * signature = packet + size; // set it later memset (signature, 0, signatureLen); // zeroes for now size += signatureLen; // signature + htobe16buf (optionsSize, packet + size - 2 - optionsSize); // actual options size size += m_SendBuffer.Get (packet + size, STREAMING_MTU - size); // payload m_LocalDestination.GetOwner ()->Sign (packet, size, signature); } @@ -849,6 +902,12 @@ namespace stream LogPrint (eLogWarning, "Streaming: LeaseSet ", m_RemoteIdentity->GetIdentHash ().ToBase64 (), " not found"); m_LocalDestination.GetOwner ()->RequestDestination (m_RemoteIdentity->GetIdentHash ()); // try to request for a next attempt } + else + { + // LeaseSet updated + m_RemoteIdentity = m_RemoteLeaseSet->GetIdentity (); + m_TransientVerifier = m_RemoteLeaseSet->GetTransientVerifier (); + } } if (m_RemoteLeaseSet) { diff --git a/libi2pd/Streaming.h b/libi2pd/Streaming.h index 3db8d760..ba52464f 100644 --- a/libi2pd/Streaming.h +++ b/libi2pd/Streaming.h @@ -38,6 +38,7 @@ namespace stream const uint16_t PACKET_FLAG_PROFILE_INTERACTIVE = 0x0100; const uint16_t PACKET_FLAG_ECHO = 0x0200; const uint16_t PACKET_FLAG_NO_ACK = 0x0400; + const uint16_t PACKET_FLAG_OFFLINE_SIGNATURE = 0x0800; const size_t STREAMING_MTU = 1730; const size_t MAX_PACKET_SIZE = 4096; @@ -195,6 +196,7 @@ namespace stream void SavePacket (Packet * packet); void ProcessPacket (Packet * packet); + bool ProcessOptions (uint16_t flags, Packet * packet); void ProcessAck (Packet * packet); size_t ConcatenatePackets (uint8_t * buf, size_t len); @@ -216,6 +218,7 @@ namespace stream bool m_IsAckSendScheduled; StreamingDestination& m_LocalDestination; std::shared_ptr m_RemoteIdentity; + std::shared_ptr m_TransientVerifier; // in case of offline key std::shared_ptr m_RemoteLeaseSet; std::shared_ptr m_RoutingSession; std::shared_ptr m_CurrentRemoteLease; diff --git a/libi2pd/TunnelGateway.cpp b/libi2pd/TunnelGateway.cpp index e6bfdd84..7d0069a9 100644 --- a/libi2pd/TunnelGateway.cpp +++ b/libi2pd/TunnelGateway.cpp @@ -51,6 +51,19 @@ namespace tunnel // create fragments const std::shared_ptr & msg = block.data; size_t fullMsgLen = diLen + msg->GetLength () + 2; // delivery instructions + payload + 2 bytes length + + if (!messageCreated && fullMsgLen > m_RemainingSize) // check if we should complete previous message + { + size_t numFollowOnFragments = fullMsgLen / TUNNEL_DATA_MAX_PAYLOAD_SIZE; + // length of bytes doesn't fit full tunnel message + // every follow-on fragment adds 7 bytes + size_t nonFit = (fullMsgLen + numFollowOnFragments*7) % TUNNEL_DATA_MAX_PAYLOAD_SIZE; + if (!nonFit || nonFit > m_RemainingSize) + { + CompleteCurrentTunnelDataMessage (); + CreateCurrentTunnelDataMessage (); + } + } if (fullMsgLen <= m_RemainingSize) { // message fits. First and last fragment @@ -65,18 +78,6 @@ namespace tunnel } else { - if (!messageCreated) // check if we should complete previous message - { - size_t numFollowOnFragments = fullMsgLen / TUNNEL_DATA_MAX_PAYLOAD_SIZE; - // length of bytes don't fit full tunnel message - // every follow-on fragment adds 7 bytes - size_t nonFit = (fullMsgLen + numFollowOnFragments*7) % TUNNEL_DATA_MAX_PAYLOAD_SIZE; - if (!nonFit || nonFit > m_RemainingSize) - { - CompleteCurrentTunnelDataMessage (); - CreateCurrentTunnelDataMessage (); - } - } if (diLen + 6 <= m_RemainingSize) { // delivery instructions fit diff --git a/libi2pd/util.cpp b/libi2pd/util.cpp index b558fca4..67366671 100644 --- a/libi2pd/util.cpp +++ b/libi2pd/util.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -21,9 +22,10 @@ #define MALLOC(x) HeapAlloc(GetProcessHeap(), 0, (x)) #define FREE(x) HeapFree(GetProcessHeap(), 0, (x)) -/* // No more needed. Exists in MinGW. -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 +// inet_pton exists Windows since Vista, but XP haven't that function! +// This function was written by Petar Korponai?. See http://stackoverflow.com/questions/15660203/inet-pton-identifier-not-found +int inet_pton_xp(int af, const char *src, void *dst) +{ struct sockaddr_storage ss; int size = sizeof (ss); char src_copy[INET6_ADDRSTRLEN + 1]; @@ -45,7 +47,7 @@ int inet_pton(int af, const char *src, void *dst) } } return 0; -}*/ +} #else /* !WIN32 => UNIX */ #include #include @@ -58,204 +60,236 @@ namespace util namespace net { #ifdef 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; + bool IsWindowsXPorLater() + { + OSVERSIONINFO osvi; + ZeroMemory(&osvi, sizeof(OSVERSIONINFO)); + osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); + GetVersionEx(&osvi); - if(GetAdaptersAddresses(AF_INET, GAA_FLAG_INCLUDE_PREFIX, nullptr, pAddresses, &outBufLen) - == ERROR_BUFFER_OVERFLOW) { - FREE(pAddresses); - pAddresses = (IP_ADAPTER_ADDRESSES*) MALLOC(outBufLen); - } + if (osvi.dwMajorVersion <= 5) + return true; + else + return false; + } - DWORD dwRetVal = GetAdaptersAddresses( - AF_INET, GAA_FLAG_INCLUDE_PREFIX, nullptr, pAddresses, &outBufLen - ); + 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(dwRetVal != NO_ERROR) { - LogPrint(eLogError, "NetIface: GetMTU(): enclosed GetAdaptersAddresses() call has failed"); - FREE(pAddresses); - return fallback; - } + if(GetAdaptersAddresses(AF_INET, GAA_FLAG_INCLUDE_PREFIX, nullptr, pAddresses, &outBufLen) + == ERROR_BUFFER_OVERFLOW) + { + FREE(pAddresses); + pAddresses = (IP_ADAPTER_ADDRESSES*) MALLOC(outBufLen); + } - pCurrAddresses = pAddresses; - while(pCurrAddresses) { - PIP_ADAPTER_UNICAST_ADDRESS firstUnicastAddress = pCurrAddresses->FirstUnicastAddress; + DWORD dwRetVal = GetAdaptersAddresses( + AF_INET, GAA_FLAG_INCLUDE_PREFIX, nullptr, pAddresses, &outBufLen + ); - pUnicast = pCurrAddresses->FirstUnicastAddress; - if(pUnicast == nullptr) { - LogPrint(eLogError, "NetIface: GetMTU(): 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) { - auto result = pAddresses->Mtu; - FREE(pAddresses); - return result; - } - pUnicast = pUnicast->Next; - } - pCurrAddresses = pCurrAddresses->Next; - } + if(dwRetVal != NO_ERROR) + { + LogPrint(eLogError, "NetIface: GetMTU(): enclosed GetAdaptersAddresses() call has failed"); + FREE(pAddresses); + return fallback; + } - LogPrint(eLogError, "NetIface: GetMTU(): no usable unicast ipv4 addresses found"); - FREE(pAddresses); - return fallback; - } + pCurrAddresses = pAddresses; + while(pCurrAddresses) + { + PIP_ADAPTER_UNICAST_ADDRESS firstUnicastAddress = pCurrAddresses->FirstUnicastAddress; - 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; + pUnicast = pCurrAddresses->FirstUnicastAddress; + if(pUnicast == nullptr) + LogPrint(eLogError, "NetIface: GetMTU(): not a unicast ipv4 address, this is not supported"); - if(GetAdaptersAddresses(AF_INET6, GAA_FLAG_INCLUDE_PREFIX, nullptr, pAddresses, &outBufLen) - == ERROR_BUFFER_OVERFLOW) { - FREE(pAddresses); - pAddresses = (IP_ADAPTER_ADDRESSES*) MALLOC(outBufLen); - } + 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) + { + auto result = pAddresses->Mtu; + FREE(pAddresses); + return result; + } + pUnicast = pUnicast->Next; + } + pCurrAddresses = pCurrAddresses->Next; + } - DWORD dwRetVal = GetAdaptersAddresses( - AF_INET6, GAA_FLAG_INCLUDE_PREFIX, nullptr, pAddresses, &outBufLen - ); + LogPrint(eLogError, "NetIface: GetMTU(): no usable unicast ipv4 addresses found"); + FREE(pAddresses); + return fallback; + } - if(dwRetVal != NO_ERROR) { - LogPrint(eLogError, "NetIface: GetMTU(): enclosed GetAdaptersAddresses() call has failed"); - 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; - bool found_address = false; - pCurrAddresses = pAddresses; - while(pCurrAddresses) { - PIP_ADAPTER_UNICAST_ADDRESS firstUnicastAddress = pCurrAddresses->FirstUnicastAddress; - pUnicast = pCurrAddresses->FirstUnicastAddress; - if(pUnicast == nullptr) { - LogPrint(eLogError, "NetIface: GetMTU(): 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; + if(GetAdaptersAddresses(AF_INET6, GAA_FLAG_INCLUDE_PREFIX, nullptr, pAddresses, &outBufLen) + == ERROR_BUFFER_OVERFLOW) + { + FREE(pAddresses); + pAddresses = (IP_ADAPTER_ADDRESSES*) MALLOC(outBufLen); + } - 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) { - auto result = pAddresses->Mtu; - FREE(pAddresses); - pAddresses = nullptr; - return result; - } - pUnicast = pUnicast->Next; - } + DWORD dwRetVal = GetAdaptersAddresses( + AF_INET6, GAA_FLAG_INCLUDE_PREFIX, nullptr, pAddresses, &outBufLen + ); - pCurrAddresses = pCurrAddresses->Next; - } + if(dwRetVal != NO_ERROR) + { + LogPrint(eLogError, "NetIface: GetMTU(): enclosed GetAdaptersAddresses() call has failed"); + FREE(pAddresses); + return fallback; + } - LogPrint(eLogError, "NetIface: GetMTU(): no usable unicast ipv6 addresses found"); - 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, "NetIface: GetMTU(): not a unicast ipv6 address, this is not supported"); - int GetMTUWindows(const boost::asio::ip::address& localAddress, int fallback) - { + 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) + { + auto result = pAddresses->Mtu; + FREE(pAddresses); + pAddresses = nullptr; + return result; + } + pUnicast = pUnicast->Next; + } + + pCurrAddresses = pCurrAddresses->Next; + } + + LogPrint(eLogError, "NetIface: GetMTU(): 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()); + string localAddress_temporary = localAddress.to_string(); + wstring localAddressUniversal(localAddress_temporary.begin(), localAddress_temporary.end()); #else - std::string localAddressUniversal = localAddress.to_string(); + std::string localAddressUniversal = localAddress.to_string(); #endif - if(localAddress.is_v4()) { - 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, "NetIface: GetMTU(): address family is not supported"); - return fallback; - } + if (IsWindowsXPorLater()) + { + #define inet_pton inet_pton_xp + } - } + 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, "NetIface: GetMTU(): address family is not supported"); + return fallback; + } + } #else // assume unix int GetMTUUnix(const boost::asio::ip::address& localAddress, int fallback) - { - ifaddrs* ifaddr, *ifa = nullptr; - if(getifaddrs(&ifaddr) == -1) + { + ifaddrs* ifaddr, *ifa = nullptr; + if(getifaddrs(&ifaddr) == -1) { - LogPrint(eLogError, "NetIface: Can't call getifaddrs(): ", strerror(errno)); - return fallback; - } + LogPrint(eLogError, "NetIface: Can't call getifaddrs(): ", strerror(errno)); + return fallback; + } - int family = 0; - // look for interface matching local address - for(ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next) + int family = 0; + // look for interface matching local address + for(ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next) { - if(!ifa->ifa_addr) - continue; + if(!ifa->ifa_addr) + continue; - family = ifa->ifa_addr->sa_family; - if(family == AF_INET && localAddress.is_v4()) + 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 - } + 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) + 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) + 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, "NetIface: Failed to run ioctl: ", strerror(errno)); - close(fd); - } + 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, "NetIface: Failed to run ioctl: ", strerror(errno)); + close(fd); + } else - LogPrint(eLogError, "NetIface: Failed to create datagram socket"); - } + LogPrint(eLogError, "NetIface: Failed to create datagram socket"); + } else - LogPrint(eLogWarning, "NetIface: interface for local address", localAddress.to_string(), " not found"); - freeifaddrs(ifaddr); + LogPrint(eLogWarning, "NetIface: interface for local address", localAddress.to_string(), " not found"); + freeifaddrs(ifaddr); - return mtu; - } + return mtu; + } #endif // WIN32 - int GetMTU(const boost::asio::ip::address& localAddress) - { - const int fallback = 576; // fallback MTU + int GetMTU(const boost::asio::ip::address& localAddress) + { + const int fallback = 576; // fallback MTU #ifdef WIN32 - return GetMTUWindows(localAddress, fallback); + return GetMTUWindows(localAddress, fallback); #else - return GetMTUUnix(localAddress, fallback); + return GetMTUUnix(localAddress, fallback); #endif - return fallback; - } + return fallback; + } const boost::asio::ip::address GetInterfaceAddress(const std::string & ifname, bool ipv6) { @@ -291,7 +325,8 @@ namespace net } if(addrs) freeifaddrs(addrs); std::string fallback; - if(ipv6) { + if(ipv6) + { fallback = "::"; LogPrint(eLogWarning, "NetIface: cannot find ipv6 address for interface ", ifname); } else { diff --git a/libi2pd_client/HTTPProxy.cpp b/libi2pd_client/HTTPProxy.cpp index e54ccda5..5fbefece 100644 --- a/libi2pd_client/HTTPProxy.cpp +++ b/libi2pd_client/HTTPProxy.cpp @@ -156,7 +156,7 @@ namespace proxy { std::stringstream ss; ss << "

Proxy error: Host not found

\r\n" << "

Remote host not found in router's addressbook

\r\n" - << "

You may try to find this host on jumpservices below:

\r\n" + << "

You may try to find this host on jump services below:

\r\n" << "
    \r\n"; for (const auto& js : jumpservices) { ss << "
  • " << js.first << "
  • \r\n"; @@ -219,7 +219,11 @@ namespace proxy { /* replace headers */ req.UpdateHeader("User-Agent", "MYOB/6.66 (AN/ON)"); /* add headers */ - req.UpdateHeader("Connection", "close"); /* keep-alive conns not supported yet */ + /* close connection, if not Connection: (U|u)pgrade (for websocket) */ + auto h = req.GetHeader ("Connection"); + auto x = h.find("pgrade"); + if (!(x != std::string::npos && std::tolower(h[x - 1]) == 'u')) + req.UpdateHeader("Connection", "close"); } /** @@ -349,7 +353,7 @@ namespace proxy { else GenericProxyError("Outproxy failure", "bad outproxy settings"); } else { - LogPrint (eLogWarning, "HTTPProxy: outproxy failure for ", dest_host, ": no outprxy enabled"); + LogPrint (eLogWarning, "HTTPProxy: outproxy failure for ", dest_host, ": no outproxy enabled"); std::string message = "Host " + dest_host + " not inside I2P network, but outproxy is not enabled"; GenericProxyError("Outproxy failure", message.c_str()); } @@ -392,7 +396,7 @@ namespace proxy { // update User-Agent to ESR version of Firefox, same as Tor Browser below version 8, for non-HTTPS connections if(m_ClientRequest.method != "CONNECT") - m_ClientRequest.UpdateHeader("User-Agent", "Mozilla/5.0 (Windows NT 6.1; rv:52.0) Gecko/20100101 Firefox/52.0"); + m_ClientRequest.UpdateHeader("User-Agent", "Mozilla/5.0 (Windows NT 6.1; rv:60.0) Gecko/20100101 Firefox/60.0"); m_ClientRequest.write(m_ClientRequestBuffer); m_ClientRequestBuffer << m_recv_buf.substr(m_req_len); diff --git a/libi2pd_client/I2CP.cpp b/libi2pd_client/I2CP.cpp index 7d7ce558..252332cd 100644 --- a/libi2pd_client/I2CP.cpp +++ b/libi2pd_client/I2CP.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2016, The PurpleI2P Project +* Copyright (c) 2013-2019, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -68,6 +68,13 @@ namespace client SetLeaseSet (ls); } + void I2CPDestination::LeaseSet2Created (uint8_t storeType, const uint8_t * buf, size_t len) + { + auto ls = new i2p::data::LocalLeaseSet2 (storeType, m_Identity, buf, len); + ls->SetExpirationTime (m_LeaseSetExpirationTime); + SetLeaseSet (ls); + } + void I2CPDestination::SendMsgTo (const uint8_t * payload, size_t len, const i2p::data::IdentHash& ident, uint32_t nonce) { auto msg = NewI2NPMessage (); @@ -512,6 +519,36 @@ namespace client LogPrint (eLogError, "I2CP: unexpected sessionID ", sessionID); } + void I2CPSession::CreateLeaseSet2MessageHandler (const uint8_t * buf, size_t len) + { + uint16_t sessionID = bufbe16toh (buf); + if (sessionID == m_SessionID) + { + size_t offset = 2; + if (m_Destination) + { + uint8_t storeType = buf[offset]; offset++; // store type + // TODO: parse LS2 and obtain correct private keys lengths + size_t signingPrivateKeyLength = 0, encryptionPrivateKeyLength = 0; + if (storeType != i2p::data::NETDB_STORE_TYPE_META_LEASESET2) // no private keys for meta + { + signingPrivateKeyLength = m_Destination->GetIdentity ()->GetSigningPrivateKeyLen (); // no offline keys + encryptionPrivateKeyLength = 256; // ElGamal only + if (len < offset + signingPrivateKeyLength + encryptionPrivateKeyLength) + { + LogPrint (eLogError, "I2CP: CreateLeaseSet2 message is too short ", len); + return; + } + m_Destination->SetEncryptionPrivateKey (buf + len - encryptionPrivateKeyLength); + // ignore signing private key + } + m_Destination->LeaseSet2Created (storeType, buf + offset, len - offset - signingPrivateKeyLength - encryptionPrivateKeyLength); + } + } + else + LogPrint (eLogError, "I2CP: unexpected sessionID ", sessionID); + } + void I2CPSession::SendMessageMessageHandler (const uint8_t * buf, size_t len) { uint16_t sessionID = bufbe16toh (buf); @@ -704,6 +741,7 @@ namespace client m_MessagesHandlers[I2CP_DESTROY_SESSION_MESSAGE] = &I2CPSession::DestroySessionMessageHandler; m_MessagesHandlers[I2CP_RECONFIGURE_SESSION_MESSAGE] = &I2CPSession::ReconfigureSessionMessageHandler; m_MessagesHandlers[I2CP_CREATE_LEASESET_MESSAGE] = &I2CPSession::CreateLeaseSetMessageHandler; + m_MessagesHandlers[I2CP_CREATE_LEASESET2_MESSAGE] = &I2CPSession::CreateLeaseSet2MessageHandler; m_MessagesHandlers[I2CP_SEND_MESSAGE_MESSAGE] = &I2CPSession::SendMessageMessageHandler; m_MessagesHandlers[I2CP_SEND_MESSAGE_EXPIRES_MESSAGE] = &I2CPSession::SendMessageExpiresMessageHandler; m_MessagesHandlers[I2CP_HOST_LOOKUP_MESSAGE] = &I2CPSession::HostLookupMessageHandler; diff --git a/libi2pd_client/I2CP.h b/libi2pd_client/I2CP.h index 657c72c1..7a89a24a 100644 --- a/libi2pd_client/I2CP.h +++ b/libi2pd_client/I2CP.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2016, The PurpleI2P Project +* Copyright (c) 2013-2019, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -36,6 +36,7 @@ namespace client const uint8_t I2CP_DESTROY_SESSION_MESSAGE = 3; const uint8_t I2CP_REQUEST_VARIABLE_LEASESET_MESSAGE = 37; const uint8_t I2CP_CREATE_LEASESET_MESSAGE = 4; + const uint8_t I2CP_CREATE_LEASESET2_MESSAGE = 40; const uint8_t I2CP_SEND_MESSAGE_MESSAGE = 5; const uint8_t I2CP_SEND_MESSAGE_EXPIRES_MESSAGE = 36; const uint8_t I2CP_MESSAGE_PAYLOAD_MESSAGE = 31; @@ -68,6 +69,7 @@ namespace client void SetEncryptionPrivateKey (const uint8_t * key); void LeaseSetCreated (const uint8_t * buf, size_t len); // called from I2CPSession + void LeaseSet2Created (uint8_t storeType, const uint8_t * buf, size_t len); // called from I2CPSession void SendMsgTo (const uint8_t * payload, size_t len, const i2p::data::IdentHash& ident, uint32_t nonce); // called from I2CPSession // implements LocalDestination @@ -126,6 +128,7 @@ namespace client void DestroySessionMessageHandler (const uint8_t * buf, size_t len); void ReconfigureSessionMessageHandler (const uint8_t * buf, size_t len); void CreateLeaseSetMessageHandler (const uint8_t * buf, size_t len); + void CreateLeaseSet2MessageHandler (const uint8_t * buf, size_t len); void SendMessageMessageHandler (const uint8_t * buf, size_t len); void SendMessageExpiresMessageHandler (const uint8_t * buf, size_t len); void HostLookupMessageHandler (const uint8_t * buf, size_t len); diff --git a/libi2pd_client/I2PTunnel.cpp b/libi2pd_client/I2PTunnel.cpp index 975cc4ce..65600cf8 100644 --- a/libi2pd_client/I2PTunnel.cpp +++ b/libi2pd_client/I2PTunnel.cpp @@ -256,7 +256,13 @@ namespace client { if (!m_ConnectionSent && !line.compare(0, 10, "Connection")) { - m_OutHeader << "Connection: close\r\n"; + /* close connection, if not Connection: (U|u)pgrade (for websocket) */ + auto x = line.find("pgrade"); + if (x != std::string::npos && std::tolower(line[x - 1]) == 'u') + m_OutHeader << line << "\r\n"; + else + m_OutHeader << "Connection: close\r\n"; + m_ConnectionSent = true; } else if (!m_ProxyConnectionSent && !line.compare(0, 16, "Proxy-Connection")) diff --git a/libi2pd_client/SAM.cpp b/libi2pd_client/SAM.cpp index 9fdf08aa..0dc73d72 100644 --- a/libi2pd_client/SAM.cpp +++ b/libi2pd_client/SAM.cpp @@ -963,7 +963,16 @@ namespace client void SAMBridge::Stop () { m_IsRunning = false; - m_Acceptor.cancel (); + + try + { + m_Acceptor.cancel (); + } + catch (const std::exception& ex) + { + LogPrint (eLogError, "SAM: runtime exception: ", ex.what ()); + } + for (auto& it: m_Sessions) it.second->CloseStreams (); m_Sessions.clear ();