Browse Source

Merge pull request #1301 from PurpleI2P/openssl

recent changes
pull/1348/head
orignal 6 years ago committed by GitHub
parent
commit
cc48436794
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      Win32/DaemonWin32.cpp
  2. 80
      Win32/Win32App.cpp
  3. 3
      Win32/Win32Service.cpp
  4. 7
      libi2pd/Destination.cpp
  5. 1
      libi2pd/Destination.h
  6. 351
      libi2pd/FS.cpp
  7. 177
      libi2pd/Identity.cpp
  8. 20
      libi2pd/Identity.h
  9. 122
      libi2pd/LeaseSet.cpp
  10. 37
      libi2pd/LeaseSet.h
  11. 129
      libi2pd/Streaming.cpp
  12. 3
      libi2pd/Streaming.h
  13. 25
      libi2pd/TunnelGateway.cpp
  14. 393
      libi2pd/util.cpp
  15. 12
      libi2pd_client/HTTPProxy.cpp
  16. 40
      libi2pd_client/I2CP.cpp
  17. 5
      libi2pd_client/I2CP.h
  18. 8
      libi2pd_client/I2PTunnel.cpp
  19. 11
      libi2pd_client/SAM.cpp

2
Win32/DaemonWin32.cpp

@ -50,7 +50,7 @@ namespace util @@ -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());

80
Win32/Win32App.cpp

@ -24,17 +24,22 @@ @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -290,14 +331,27 @@ namespace win32
}
case WM_TIMER:
{
if (wParam == IDT_GRACEFUL_SHUTDOWN_TIMER)
{
PostMessage (hWnd, WM_CLOSE, 0, 0); // exit
return 0;
}
if (wParam == FRAME_UPDATE_TIMER)
switch(wParam)
{
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 @@ -318,6 +372,12 @@ namespace win32
EndPaint(hWnd, &ps);
break;
}
default:
{
if (uMsg == s_uTaskbarRestart)
AddTrayIcon (hWnd);
break;
}
}
return DefWindowProc( hWnd, uMsg, wParam, lParam);
}

3
Win32/Win32Service.cpp

@ -297,7 +297,8 @@ void InstallService(PCSTR pszServiceName, PCSTR pszDisplayName, DWORD dwStartTyp @@ -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);

7
libi2pd/Destination.cpp

@ -777,6 +777,9 @@ namespace client @@ -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 @@ -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);
}

1
libi2pd/Destination.h

@ -126,6 +126,7 @@ namespace client @@ -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;

351
libi2pd/FS.cpp

@ -11,6 +11,7 @@ @@ -11,6 +11,7 @@
#ifdef _WIN32
#include <shlobj.h>
#include <windows.h>
#endif
#include "Base.h"
@ -20,187 +21,211 @@ @@ -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;
return;
}
// otherwise use /data/files
const char * ext = getenv("EXTERNAL_STORAGE");
if (!ext) ext = "/sdcard";
if (boost::filesystem::exists(ext))
{
dataDir = std::string (ext) + "/" + appName;
return;
}
#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;
// 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
}
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 ();
return true;
}
bool ReadDir(const std::string & path, std::vector<std::string> & files) {
if (!boost::filesystem::exists(path))
return false;
boost::filesystem::directory_iterator it(path);
boost::filesystem::directory_iterator end;
for ( ; it != end; it++) {
if (!boost::filesystem::is_regular_file(it->status()))
continue;
files.push_back(it->path().string());
}
return true;
}
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 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 ();
return true;
}
bool ReadDir(const std::string & path, std::vector<std::string> & files) {
if (!boost::filesystem::exists(path))
return false;
boost::filesystem::directory_iterator it(path);
boost::filesystem::directory_iterator end;
for ( ; it != end; it++) {
if (!boost::filesystem::is_regular_file(it->status()))
continue;
files.push_back(it->path().string());
}
return true;
}
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;
}
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;
}
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;
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::Traverse(std::vector<std::string> & 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;
for ( ; it != end; it++) {
if (!boost::filesystem::is_regular_file( it->status() ))
continue;
const std::string & t = it->path().string();
v(t);
}
}
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);
}
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::stringstream t("");
t << this->root << i2p::fs::dirSep;
t << prefix1 << safe_ident[0] << i2p::fs::dirSep;
t << prefix2 << safe_ident << "." << suffix;
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::Traverse(std::vector<std::string> & 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;
for ( ; it != end; it++) {
if (!boost::filesystem::is_regular_file( it->status() ))
continue;
const std::string & t = it->path().string();
v(t);
}
}
} // fs
} // i2p

177
libi2pd/Identity.cpp

@ -432,6 +432,9 @@ namespace data @@ -432,6 +432,9 @@ namespace data
m_Public = std::make_shared<IdentityEx>(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 @@ -441,12 +444,23 @@ namespace data
{
m_Public = std::make_shared<IdentityEx>(*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<IdentityEx>();
@ -455,11 +469,50 @@ namespace data @@ -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<i2p::crypto::Verifier> 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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -642,6 +728,27 @@ namespace data
}
}
PrivateKeys PrivateKeys::CreateOfflineKeys (SigningKeyType type, uint32_t expires) const
{
PrivateKeys keys (*this);
std::unique_ptr<i2p::crypto::Verifier> 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;

20
libi2pd/Identity.h

@ -6,6 +6,7 @@ @@ -6,6 +6,7 @@
#include <string>
#include <memory>
#include <atomic>
#include <vector>
#include "Base.h"
#include "Signature.h"
#include "CryptoKey.h"
@ -142,11 +143,13 @@ namespace data @@ -142,11 +143,13 @@ namespace data
std::shared_ptr<const IdentityEx> 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 @@ -157,18 +160,27 @@ namespace data
static std::shared_ptr<i2p::crypto::CryptoKeyDecryptor> 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<uint8_t>& GetOfflineSignature () const { return m_OfflineSignature; };
private:
void CreateSigner () const;
void CreateSigner (SigningKeyType keyType) const;
private:
std::shared_ptr<IdentityEx> 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<i2p::crypto::Signer> m_Signer;
std::vector<uint8_t> m_OfflineSignature; // non zero length, if applicable
size_t m_TransientSignatureLen = 0;
size_t m_TransientSigningPrivateKeyLen = 0;
};
// kademlia

122
libi2pd/LeaseSet.cpp

@ -262,43 +262,38 @@ namespace data @@ -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<IdentityEx>(buf, len);
SetIdentity (identity);
std::shared_ptr<const IdentityEx> identity;
if (readIdentity)
{
identity = std::make_shared<IdentityEx>(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<i2p::crypto::Verifier> 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 @@ -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<typename Verifier>
@ -344,6 +342,7 @@ namespace data @@ -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 @@ -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 @@ -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<i2p::crypto::Verifier> 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 @@ -586,16 +579,24 @@ namespace data
return ident.Verify(ptr, leases - ptr, leases);
}
LocalLeaseSet2::LocalLeaseSet2 (uint8_t storeType, std::shared_ptr<const IdentityEx> identity,
LocalLeaseSet2::LocalLeaseSet2 (uint8_t storeType, const i2p::data::PrivateKeys& keys,
uint16_t keyType, uint16_t keyLen, const uint8_t * encryptionPublicKey,
std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> > 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 @@ -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 @@ -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<const IdentityEx> 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;
}
}
}

37
libi2pd/LeaseSet.h

@ -8,6 +8,7 @@ @@ -8,6 +8,7 @@
#include <memory>
#include "Identity.h"
#include "Timestamp.h"
#include "I2PEndian.h"
namespace i2p
{
@ -77,7 +78,8 @@ namespace data @@ -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<const i2p::crypto::Verifier> GetTransientVerifier () const { return nullptr; };
// implements RoutingDestination
std::shared_ptr<const IdentityEx> GetIdentity () const { return m_Identity; };
void Encrypt (const uint8_t * data, uint8_t * encrypted, BN_CTX * ctx) const;
@ -122,20 +124,24 @@ namespace data @@ -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<const i2p::crypto::Verifier> 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 @@ -148,9 +154,31 @@ namespace data
private:
uint8_t m_StoreType;
std::shared_ptr<i2p::crypto::Verifier> m_TransientVerifier;
std::shared_ptr<i2p::crypto::CryptoKeyEncryptor> m_Encryptor; // for standardLS2
};
// also called from Streaming.cpp
template<typename Verifier>
std::shared_ptr<i2p::crypto::Verifier> 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<i2p::crypto::Verifier> 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 @@ -186,9 +214,10 @@ namespace data
{
public:
LocalLeaseSet2 (uint8_t storeType, std::shared_ptr<const IdentityEx> identity,
LocalLeaseSet2 (uint8_t storeType, const i2p::data::PrivateKeys& keys,
uint16_t keyType, uint16_t keyLen, const uint8_t * encryptionPublicKey,
std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> > tunnels);
LocalLeaseSet2 (uint8_t storeType, std::shared_ptr<const IdentityEx> identity, const uint8_t * buf, size_t len);
virtual ~LocalLeaseSet2 () { delete[] m_Buffer; };
uint8_t * GetBuffer () const { return m_Buffer + 1; };

129
libi2pd/Streaming.cpp

@ -217,25 +217,59 @@ namespace stream @@ -217,25 +217,59 @@ 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 (!ProcessOptions (flags, packet))
{
m_LocalDestination.DeletePacket (packet);
Terminate ();
return;
}
packet->offset = packet->GetPayload () - packet->buf;
if (packet->GetLength () > 0)
{
m_ReceiveQueue.push (packet);
m_ReceiveTimer.cancel ();
}
else
m_LocalDestination.DeletePacket (packet);
m_LastReceivedSequenceNumber = receivedSeqn;
if (flags & PACKET_FLAG_RESET)
{
LogPrint (eLogDebug, "Streaming: closing stream sSID=", m_SendStreamID, ", rSID=", m_RecvStreamID, ": reset flag received in packet #", receivedSeqn);
m_Status = eStreamStatusReset;
Close ();
}
else if (flags & PACKET_FLAG_CLOSE)
{
if (m_Status != eStreamStatusClosed)
SendClose ();
m_Status = eStreamStatusClosed;
Terminate ();
}
}
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)
{
m_RemoteIdentity = std::make_shared<i2p::data::IdentityEx>(optionData, packet->GetOptionSize ());
if (m_RemoteLeaseSet) m_RemoteIdentity = m_RemoteLeaseSet->GetIdentity ();
if (!m_RemoteIdentity)
m_RemoteIdentity = std::make_shared<i2p::data::IdentityEx>(optionData, optionSize);
if (m_RemoteIdentity->IsRSA ())
{
LogPrint (eLogInfo, "Streaming: Incoming stream from RSA destination ", m_RemoteIdentity->GetIdentHash ().ToBase64 (), " Discarded");
m_LocalDestination.DeletePacket (packet);
Terminate ();
return;
return false;
}
optionData += m_RemoteIdentity->GetFullLen ();
if (!m_RemoteLeaseSet)
@ -249,6 +283,36 @@ namespace stream @@ -249,6 +283,36 @@ namespace stream
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];
@ -257,7 +321,10 @@ namespace stream @@ -257,7 +321,10 @@ namespace stream
{
memcpy (signature, optionData, signatureLen);
memset (const_cast<uint8_t *>(optionData), 0, signatureLen);
if (!m_RemoteIdentity->Verify (packet->GetBuffer (), packet->GetLength (), signature))
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 ();
@ -268,34 +335,11 @@ namespace stream @@ -268,34 +335,11 @@ namespace stream
}
else
{
LogPrint(eLogError, "Streaming: Signature too big, ", signatureLen, " bytes");
LogPrint (eLogError, "Streaming: Signature too big, ", signatureLen, " bytes");
return false;
}
}
packet->offset = packet->GetPayload () - packet->buf;
if (packet->GetLength () > 0)
{
m_ReceiveQueue.push (packet);
m_ReceiveTimer.cancel ();
}
else
m_LocalDestination.DeletePacket (packet);
m_LastReceivedSequenceNumber = receivedSeqn;
if (flags & PACKET_FLAG_RESET)
{
LogPrint (eLogDebug, "Streaming: closing stream sSID=", m_SendStreamID, ", rSID=", m_RecvStreamID, ": reset flag received in packet #", receivedSeqn);
m_Status = eStreamStatusReset;
Close ();
}
else if (flags & PACKET_FLAG_CLOSE)
{
if (m_Status != eStreamStatusClosed)
SendClose ();
m_Status = eStreamStatusClosed;
Terminate ();
}
return true;
}
void Stream::ProcessAck (Packet * packet)
@ -438,19 +482,28 @@ namespace stream @@ -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 @@ -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)
{

3
libi2pd/Streaming.h

@ -38,6 +38,7 @@ namespace stream @@ -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 @@ -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 @@ -216,6 +218,7 @@ namespace stream
bool m_IsAckSendScheduled;
StreamingDestination& m_LocalDestination;
std::shared_ptr<const i2p::data::IdentityEx> m_RemoteIdentity;
std::shared_ptr<const i2p::crypto::Verifier> m_TransientVerifier; // in case of offline key
std::shared_ptr<const i2p::data::LeaseSet> m_RemoteLeaseSet;
std::shared_ptr<i2p::garlic::GarlicRoutingSession> m_RoutingSession;
std::shared_ptr<const i2p::data::Lease> m_CurrentRemoteLease;

25
libi2pd/TunnelGateway.cpp

@ -51,6 +51,19 @@ namespace tunnel @@ -51,6 +51,19 @@ namespace tunnel
// create fragments
const std::shared_ptr<I2NPMessage> & 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 @@ -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

393
libi2pd/util.cpp

@ -9,6 +9,7 @@ @@ -9,6 +9,7 @@
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <sysinfoapi.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <iphlpapi.h>
@ -21,9 +22,10 @@ @@ -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) @@ -45,7 +47,7 @@ int inet_pton(int af, const char *src, void *dst)
}
}
return 0;
}*/
}
#else /* !WIN32 => UNIX */
#include <sys/types.h>
#include <ifaddrs.h>
@ -58,204 +60,236 @@ namespace util @@ -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;
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, "NetIface: GetMTU(): 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, "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;
}
LogPrint(eLogError, "NetIface: GetMTU(): 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, "NetIface: GetMTU(): 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, "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;
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)
{
bool IsWindowsXPorLater()
{
OSVERSIONINFO osvi;
ZeroMemory(&osvi, sizeof(OSVERSIONINFO));
osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
GetVersionEx(&osvi);
if (osvi.dwMajorVersion <= 5)
return true;
else
return false;
}
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, "NetIface: GetMTU(): 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, "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;
}
LogPrint(eLogError, "NetIface: GetMTU(): 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, "NetIface: GetMTU(): 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, "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;
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 @@ -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 {

12
libi2pd_client/HTTPProxy.cpp

@ -156,7 +156,7 @@ namespace proxy { @@ -156,7 +156,7 @@ namespace proxy {
std::stringstream ss;
ss << "<h1>Proxy error: Host not found</h1>\r\n"
<< "<p>Remote host not found in router's addressbook</p>\r\n"
<< "<p>You may try to find this host on jumpservices below:</p>\r\n"
<< "<p>You may try to find this host on jump services below:</p>\r\n"
<< "<ul>\r\n";
for (const auto& js : jumpservices) {
ss << " <li><a href=\"" << js.second << host << "\">" << js.first << "</a></li>\r\n";
@ -219,7 +219,11 @@ namespace proxy { @@ -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 { @@ -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 { @@ -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);

40
libi2pd_client/I2CP.cpp

@ -1,5 +1,5 @@ @@ -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 @@ -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 @@ -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 @@ -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;

5
libi2pd_client/I2CP.h

@ -1,5 +1,5 @@ @@ -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 @@ -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 @@ -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 @@ -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);

8
libi2pd_client/I2PTunnel.cpp

@ -256,7 +256,13 @@ namespace client @@ -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"))

11
libi2pd_client/SAM.cpp

@ -963,7 +963,16 @@ namespace client @@ -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 ();

Loading…
Cancel
Save