Merge pull request #1641 from PurpleI2P/openssl

2.37.0
This commit is contained in:
orignal 2021-03-15 12:36:35 -04:00 committed by GitHub
commit fde79eecc6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
39 changed files with 618 additions and 778 deletions

0
.gitmodules vendored
View File

View File

@ -1,6 +1,34 @@
# for this file format description, # for this file format description,
# see https://github.com/olivierlacan/keep-a-changelog # see https://github.com/olivierlacan/keep-a-changelog
## [2.37.0] - 2021-03-15
### Added
- Address registration line for reg.i2p and stats.i2p through the web console
- "4" and "6" caps for addresses without published IP address
- Mesh and Proxy network statuses
- Symmetric NAT network status error
- Bind server tunnel connection to specified address
- lookuplocal BOB extended command
- address4 and address6 parameters to bind outgoing connections to
- Rekey of low-bandwidth routers to ECIES
- Popup notification windows when unable to parse config for Windows
### Changed
- Floodfills with "U" cap are not ignored anymore
- Check transports reachability between tunnel peers and between router and floodfill
- NTCP2 and reseed HTTP proxy support authorization now
- Show actual IP addresses for proxy connections
- Publish and handle SSU addreses without host
- Outbound tunnel endpoint must be ipv4 compatible
- Logging optimization
- Removed Windows service
### Fixed
- Incoming SSU session terminates after 5 seconds
- Outgoing NTCP2 ipv4 session even if ipv4 is disabled
- No incoming Yggdrasil connection if connected through NTCP2 proxy
- Race condition between tunnel build and floodfill requests decryption for ECIES routers
- Numeric bandwidth limitation
- Yggdrasil for Android
## [2.36.0] - 2021-02-15 ## [2.36.0] - 2021-02-15
### Added ### Added
- Encrypted lookup and publications to ECIES-x25519 floodfiils - Encrypted lookup and publications to ECIES-x25519 floodfiils

View File

@ -39,7 +39,7 @@ else ifneq (, $(findstring freebsd, $(SYS))$(findstring openbsd, $(SYS)))
DAEMON_SRC += $(DAEMON_SRC_DIR)/UnixDaemon.cpp DAEMON_SRC += $(DAEMON_SRC_DIR)/UnixDaemon.cpp
include Makefile.bsd include Makefile.bsd
else ifneq (, $(findstring mingw, $(SYS))$(findstring cygwin, $(SYS))) else ifneq (, $(findstring mingw, $(SYS))$(findstring cygwin, $(SYS)))
DAEMON_SRC += Win32/DaemonWin32.cpp Win32/Win32Service.cpp Win32/Win32App.cpp Win32/Win32NetState.cpp DAEMON_SRC += Win32/DaemonWin32.cpp Win32/Win32App.cpp Win32/Win32NetState.cpp
include Makefile.mingw include Makefile.mingw
else # not supported else # not supported
$(error Not supported platform) $(error Not supported platform)

View File

@ -14,7 +14,6 @@
#include "Log.h" #include "Log.h"
#ifdef _WIN32 #ifdef _WIN32
#include "Win32Service.h"
#ifdef WIN32_APP #ifdef WIN32_APP
#include <windows.h> #include <windows.h>
#include "Win32App.h" #include "Win32App.h"
@ -35,45 +34,11 @@ namespace util
i2p::log::SetThrowFunction ([](const std::string& s) i2p::log::SetThrowFunction ([](const std::string& s)
{ {
MessageBox(0, TEXT(s.c_str ()), TEXT("i2pd"), MB_ICONERROR | MB_TASKMODAL | MB_OK ); MessageBox(0, TEXT(s.c_str ()), TEXT("i2pd"), MB_ICONERROR | MB_TASKMODAL | MB_OK );
}); }
);
if (!Daemon_Singleton::init(argc, argv)) if (!Daemon_Singleton::init(argc, argv))
return false; return false;
std::string serviceControl; i2p::config::GetOption("svcctl", serviceControl);
if (serviceControl == "install")
{
LogPrint(eLogInfo, "WinSVC: installing ", SERVICE_NAME, " as service");
InstallService(
SERVICE_NAME, // Name of service
SERVICE_DISPLAY_NAME, // Name to display
SERVICE_START_TYPE, // Service start type
SERVICE_DEPENDENCIES, // Dependencies
SERVICE_ACCOUNT, // Service running account
SERVICE_PASSWORD // Password of the account
);
return false;
}
else if (serviceControl == "remove")
{
LogPrint(eLogInfo, "WinSVC: uninstalling ", SERVICE_NAME, " service");
UninstallService(SERVICE_NAME);
return false;
}
if (isDaemon)
{
LogPrint(eLogDebug, "Daemon: running as service");
I2PService service((PSTR)SERVICE_NAME);
if (!I2PService::Run(service))
{
LogPrint(eLogError, "Daemon: Service failed to run w/err 0x%08lx\n", GetLastError());
return false;
}
return false;
}
else
LogPrint(eLogDebug, "Daemon: running as user");
return true; return true;
} }
@ -86,9 +51,6 @@ namespace util
setlocale(LC_TIME, "C"); setlocale(LC_TIME, "C");
#ifdef WIN32_APP #ifdef WIN32_APP
if (!i2p::win32::StartWin32App ()) return false; if (!i2p::win32::StartWin32App ()) return false;
// override log
i2p::config::SetOption("log", std::string ("file"));
#endif #endif
bool ret = Daemon_Singleton::start(); bool ret = Daemon_Singleton::start();
if (ret && i2p::log::Logger().GetLogType() == eLogFile) if (ret && i2p::log::Logger().GetLogType() == eLogFile)

View File

@ -1,414 +0,0 @@
/*
* Copyright (c) 2013-2020, The PurpleI2P Project
*
* This file is part of Purple i2pd project and licensed under BSD3
*
* See full license text in LICENSE file at top of project tree
*/
#ifdef _WIN32
#define _CRT_SECURE_NO_WARNINGS // to use freopen
#endif
#include "Win32Service.h"
#include <assert.h>
//#include <strsafe.h>
#include <windows.h>
#include "Daemon.h"
#include "Log.h"
I2PService *I2PService::s_service = NULL;
BOOL I2PService::isService()
{
BOOL bIsService = FALSE;
HWINSTA hWinStation = GetProcessWindowStation();
if (hWinStation != NULL)
{
USEROBJECTFLAGS uof = { 0 };
if (GetUserObjectInformation(hWinStation, UOI_FLAGS, &uof, sizeof(USEROBJECTFLAGS), NULL) && ((uof.dwFlags & WSF_VISIBLE) == 0))
{
bIsService = TRUE;
}
}
return bIsService;
}
BOOL I2PService::Run(I2PService &service)
{
s_service = &service;
SERVICE_TABLE_ENTRY serviceTable[] =
{
{ service.m_name, ServiceMain },
{ NULL, NULL }
};
return StartServiceCtrlDispatcher(serviceTable);
}
void WINAPI I2PService::ServiceMain(DWORD dwArgc, PSTR *pszArgv)
{
assert(s_service != NULL);
s_service->m_statusHandle = RegisterServiceCtrlHandler(
s_service->m_name, ServiceCtrlHandler);
if (s_service->m_statusHandle == NULL)
{
throw GetLastError();
}
s_service->Start(dwArgc, pszArgv);
}
void WINAPI I2PService::ServiceCtrlHandler(DWORD dwCtrl)
{
switch (dwCtrl)
{
case SERVICE_CONTROL_STOP: s_service->Stop(); break;
case SERVICE_CONTROL_PAUSE: s_service->Pause(); break;
case SERVICE_CONTROL_CONTINUE: s_service->Continue(); break;
case SERVICE_CONTROL_SHUTDOWN: s_service->Shutdown(); break;
case SERVICE_CONTROL_INTERROGATE: break;
default: break;
}
}
I2PService::I2PService(PSTR pszServiceName,
BOOL fCanStop,
BOOL fCanShutdown,
BOOL fCanPauseContinue)
{
m_name = (pszServiceName == NULL) ? (PSTR)"" : pszServiceName;
m_statusHandle = NULL;
m_status.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
m_status.dwCurrentState = SERVICE_START_PENDING;
DWORD dwControlsAccepted = 0;
if (fCanStop)
dwControlsAccepted |= SERVICE_ACCEPT_STOP;
if (fCanShutdown)
dwControlsAccepted |= SERVICE_ACCEPT_SHUTDOWN;
if (fCanPauseContinue)
dwControlsAccepted |= SERVICE_ACCEPT_PAUSE_CONTINUE;
m_status.dwControlsAccepted = dwControlsAccepted;
m_status.dwWin32ExitCode = NO_ERROR;
m_status.dwServiceSpecificExitCode = 0;
m_status.dwCheckPoint = 0;
m_status.dwWaitHint = 0;
m_fStopping = FALSE;
// Create a manual-reset event that is not signaled at first to indicate
// the stopped signal of the service.
m_hStoppedEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if (m_hStoppedEvent == NULL)
{
throw GetLastError();
}
}
I2PService::~I2PService(void)
{
if (m_hStoppedEvent)
{
CloseHandle(m_hStoppedEvent);
m_hStoppedEvent = NULL;
}
}
void I2PService::Start(DWORD dwArgc, PSTR *pszArgv)
{
try
{
SetServiceStatus(SERVICE_START_PENDING);
OnStart(dwArgc, pszArgv);
SetServiceStatus(SERVICE_RUNNING);
}
catch (DWORD dwError)
{
LogPrint(eLogError, "Win32Service Start", dwError);
SetServiceStatus(SERVICE_STOPPED, dwError);
}
catch (...)
{
LogPrint(eLogError, "Win32Service failed to start.", EVENTLOG_ERROR_TYPE);
SetServiceStatus(SERVICE_STOPPED);
}
}
void I2PService::OnStart(DWORD dwArgc, PSTR *pszArgv)
{
LogPrint(eLogInfo, "Win32Service in OnStart", EVENTLOG_INFORMATION_TYPE);
Daemon.start();
//i2p::util::config::OptionParser(dwArgc, pszArgv);
//i2p::util::filesystem::ReadConfigFile(i2p::util::config::mapArgs, i2p::util::config::mapMultiArgs);
//i2p::context.OverrideNTCPAddress(i2p::util::config::GetCharArg("-host", "127.0.0.1"),
// i2p::util::config::GetArg("-port", 17070));
_worker = new std::thread(std::bind(&I2PService::WorkerThread, this));
}
void I2PService::WorkerThread()
{
while (!m_fStopping)
{
::Sleep(1000); // Simulate some lengthy operations.
}
// Signal the stopped event.
SetEvent(m_hStoppedEvent);
}
void I2PService::Stop()
{
DWORD dwOriginalState = m_status.dwCurrentState;
try
{
SetServiceStatus(SERVICE_STOP_PENDING);
OnStop();
SetServiceStatus(SERVICE_STOPPED);
}
catch (DWORD dwError)
{
LogPrint(eLogInfo, "Win32Service Stop", dwError);
SetServiceStatus(dwOriginalState);
}
catch (...)
{
LogPrint(eLogError, "Win32Service failed to stop.", EVENTLOG_ERROR_TYPE);
SetServiceStatus(dwOriginalState);
}
}
void I2PService::OnStop()
{
// Log a service stop message to the Application log.
LogPrint(eLogInfo, "Win32Service in OnStop", EVENTLOG_INFORMATION_TYPE);
Daemon.stop();
m_fStopping = TRUE;
if (WaitForSingleObject(m_hStoppedEvent, INFINITE) != WAIT_OBJECT_0)
{
throw GetLastError();
}
_worker->join();
delete _worker;
}
void I2PService::Pause()
{
try
{
SetServiceStatus(SERVICE_PAUSE_PENDING);
OnPause();
SetServiceStatus(SERVICE_PAUSED);
}
catch (DWORD dwError)
{
LogPrint(eLogError, "Win32Service Pause", dwError);
SetServiceStatus(SERVICE_RUNNING);
}
catch (...)
{
LogPrint(eLogError, "Win32Service failed to pause.", EVENTLOG_ERROR_TYPE);
SetServiceStatus(SERVICE_RUNNING);
}
}
void I2PService::OnPause()
{
}
void I2PService::Continue()
{
try
{
SetServiceStatus(SERVICE_CONTINUE_PENDING);
OnContinue();
SetServiceStatus(SERVICE_RUNNING);
}
catch (DWORD dwError)
{
LogPrint(eLogError, "Win32Service Continue", dwError);
SetServiceStatus(SERVICE_PAUSED);
}
catch (...)
{
LogPrint(eLogError, "Win32Service failed to resume.", EVENTLOG_ERROR_TYPE);
SetServiceStatus(SERVICE_PAUSED);
}
}
void I2PService::OnContinue()
{
}
void I2PService::Shutdown()
{
try
{
OnShutdown();
SetServiceStatus(SERVICE_STOPPED);
}
catch (DWORD dwError)
{
LogPrint(eLogError, "Win32Service Shutdown", dwError);
}
catch (...)
{
LogPrint(eLogError, "Win32Service failed to shut down.", EVENTLOG_ERROR_TYPE);
}
}
void I2PService::OnShutdown()
{
}
void I2PService::SetServiceStatus(DWORD dwCurrentState,
DWORD dwWin32ExitCode,
DWORD dwWaitHint)
{
static DWORD dwCheckPoint = 1;
m_status.dwCurrentState = dwCurrentState;
m_status.dwWin32ExitCode = dwWin32ExitCode;
m_status.dwWaitHint = dwWaitHint;
m_status.dwCheckPoint =
((dwCurrentState == SERVICE_RUNNING) ||
(dwCurrentState == SERVICE_STOPPED)) ?
0 : dwCheckPoint++;
::SetServiceStatus(m_statusHandle, &m_status);
}
//*****************************************************************************
void FreeHandles(SC_HANDLE schSCManager, SC_HANDLE schService)
{
if (schSCManager)
{
CloseServiceHandle(schSCManager);
schSCManager = NULL;
}
if (schService)
{
CloseServiceHandle(schService);
schService = NULL;
}
}
void InstallService(PCSTR pszServiceName, PCSTR pszDisplayName, DWORD dwStartType, PCSTR pszDependencies, PCSTR pszAccount, PCSTR pszPassword)
{
printf("Try to install Win32Service (%s).\n", pszServiceName);
char szPath[MAX_PATH];
SC_HANDLE schSCManager = NULL;
SC_HANDLE schService = NULL;
if (GetModuleFileName(NULL, szPath, ARRAYSIZE(szPath)) == 0)
{
printf("GetModuleFileName failed w/err 0x%08lx\n", GetLastError());
FreeHandles(schSCManager, schService);
return;
}
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);
if (schSCManager == NULL)
{
printf("OpenSCManager failed w/err 0x%08lx\n", GetLastError());
FreeHandles(schSCManager, schService);
return;
}
// Install the service into SCM by calling CreateService
schService = CreateService(
schSCManager, // SCManager database
pszServiceName, // Name of service
pszDisplayName, // Name to display
SERVICE_QUERY_STATUS, // Desired access
SERVICE_WIN32_OWN_PROCESS, // Service type
dwStartType, // Service start type
SERVICE_ERROR_NORMAL, // Error control type
szPath, // Service's binary
NULL, // No load ordering group
NULL, // No tag identifier
pszDependencies, // Dependencies
pszAccount, // Service running account
pszPassword // Password of the account
);
if (schService == NULL)
{
printf("CreateService failed w/err 0x%08lx\n", GetLastError());
FreeHandles(schSCManager, schService);
return;
}
printf("Win32Service is installed as %s.\n", pszServiceName);
// Centralized cleanup for all allocated resources.
FreeHandles(schSCManager, schService);
}
void UninstallService(PCSTR pszServiceName)
{
printf("Try to uninstall Win32Service (%s).\n", pszServiceName);
SC_HANDLE schSCManager = NULL;
SC_HANDLE schService = NULL;
SERVICE_STATUS ssSvcStatus = {};
// Open the local default service control manager database
schSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT);
if (schSCManager == NULL)
{
printf("OpenSCManager failed w/err 0x%08lx\n", GetLastError());
FreeHandles(schSCManager, schService);
return;
}
// Open the service with delete, stop, and query status permissions
schService = OpenService(schSCManager, pszServiceName, SERVICE_STOP | SERVICE_QUERY_STATUS | DELETE);
if (schService == NULL)
{
printf("OpenService failed w/err 0x%08lx\n", GetLastError());
FreeHandles(schSCManager, schService);
return;
}
// Try to stop the service
if (ControlService(schService, SERVICE_CONTROL_STOP, &ssSvcStatus))
{
printf("Stopping %s.\n", pszServiceName);
Sleep(1000);
while (QueryServiceStatus(schService, &ssSvcStatus))
{
if (ssSvcStatus.dwCurrentState == SERVICE_STOP_PENDING)
{
printf(".");
Sleep(1000);
}
else break;
}
if (ssSvcStatus.dwCurrentState == SERVICE_STOPPED)
{
printf("\n%s is stopped.\n", pszServiceName);
}
else
{
printf("\n%s failed to stop.\n", pszServiceName);
}
}
// Now remove the service by calling DeleteService.
if (!DeleteService(schService))
{
printf("DeleteService failed w/err 0x%08lx\n", GetLastError());
FreeHandles(schSCManager, schService);
return;
}
printf("%s is removed.\n", pszServiceName);
// Centralized cleanup for all allocated resources.
FreeHandles(schSCManager, schService);
}

View File

@ -1,92 +0,0 @@
/*
* Copyright (c) 2013-2020, The PurpleI2P Project
*
* This file is part of Purple i2pd project and licensed under BSD3
*
* See full license text in LICENSE file at top of project tree
*/
#ifndef WIN_32_SERVICE_H__
#define WIN_32_SERVICE_H__
#include <thread>
#include <windows.h>
#ifdef _WIN32
// Internal name of the service
#define SERVICE_NAME "i2pdService"
// Displayed name of the service
#define SERVICE_DISPLAY_NAME "i2pd router service"
// Service start options.
#define SERVICE_START_TYPE SERVICE_DEMAND_START
// List of service dependencies - "dep1\0dep2\0\0"
#define SERVICE_DEPENDENCIES ""
// The name of the account under which the service should run
#define SERVICE_ACCOUNT "NT AUTHORITY\\LocalService"
// The password to the service account name
#define SERVICE_PASSWORD NULL
#endif
class I2PService
{
public:
I2PService(PSTR pszServiceName,
BOOL fCanStop = TRUE,
BOOL fCanShutdown = TRUE,
BOOL fCanPauseContinue = FALSE);
virtual ~I2PService(void);
static BOOL isService();
static BOOL Run(I2PService &service);
void Stop();
protected:
virtual void OnStart(DWORD dwArgc, PSTR *pszArgv);
virtual void OnStop();
virtual void OnPause();
virtual void OnContinue();
virtual void OnShutdown();
void SetServiceStatus(DWORD dwCurrentState,
DWORD dwWin32ExitCode = NO_ERROR,
DWORD dwWaitHint = 0);
private:
static void WINAPI ServiceMain(DWORD dwArgc, LPSTR *lpszArgv);
static void WINAPI ServiceCtrlHandler(DWORD dwCtrl);
void WorkerThread();
void Start(DWORD dwArgc, PSTR *pszArgv);
void Pause();
void Continue();
void Shutdown();
static I2PService* s_service;
PSTR m_name;
SERVICE_STATUS m_status;
SERVICE_STATUS_HANDLE m_statusHandle;
BOOL m_fStopping;
HANDLE m_hStoppedEvent;
std::thread* _worker;
};
void InstallService(
PCSTR pszServiceName,
PCSTR pszDisplayName,
DWORD dwStartType,
PCSTR pszDependencies,
PCSTR pszAccount,
PCSTR pszPassword
);
void UninstallService(PCSTR pszServiceName);
#endif // WIN_32_SERVICE_H__

View File

@ -1,4 +1,4 @@
version: 2.36.0.{build} version: 2.37.0.{build}
pull_requests: pull_requests:
do_not_increment_build_number: true do_not_increment_build_number: true
branches: branches:

View File

@ -1,7 +1,7 @@
%define git_hash %(git rev-parse HEAD | cut -c -7) %define git_hash %(git rev-parse HEAD | cut -c -7)
Name: i2pd-git Name: i2pd-git
Version: 2.36.0 Version: 2.37.0
Release: git%{git_hash}%{?dist} Release: git%{git_hash}%{?dist}
Summary: I2P router written in C++ Summary: I2P router written in C++
Conflicts: i2pd Conflicts: i2pd
@ -137,6 +137,9 @@ getent passwd i2pd >/dev/null || \
%changelog %changelog
* Mon Mar 15 2021 orignal <i2porignal@yandex.ru> - 2.37.0
- update to 2.37.0
* Mon Feb 15 2021 orignal <i2porignal@yandex.ru> - 2.36.0 * Mon Feb 15 2021 orignal <i2porignal@yandex.ru> - 2.36.0
- update to 2.36.0 - update to 2.36.0

View File

@ -1,5 +1,5 @@
Name: i2pd Name: i2pd
Version: 2.36.0 Version: 2.37.0
Release: 1%{?dist} Release: 1%{?dist}
Summary: I2P router written in C++ Summary: I2P router written in C++
Conflicts: i2pd-git Conflicts: i2pd-git
@ -135,6 +135,9 @@ getent passwd i2pd >/dev/null || \
%changelog %changelog
* Mon Mar 15 2021 orignal <i2porignal@yandex.ru> - 2.37.0
- update to 2.37.0
* Mon Feb 15 2021 orignal <i2porignal@yandex.ru> - 2.36.0 * Mon Feb 15 2021 orignal <i2porignal@yandex.ru> - 2.36.0
- update to 2.36.0 - update to 2.36.0

View File

@ -102,8 +102,13 @@ namespace util
if (logclftime) if (logclftime)
i2p::log::Logger().SetTimeFormat ("[%d/%b/%Y:%H:%M:%S %z]"); i2p::log::Logger().SetTimeFormat ("[%d/%b/%Y:%H:%M:%S %z]");
#ifdef WIN32_APP
// Win32 app with GUI supports only logging to file
logs = "file";
#else
if (isDaemon && (logs == "" || logs == "stdout")) if (isDaemon && (logs == "" || logs == "stdout"))
logs = "file"; logs = "file";
#endif
i2p::log::Logger().SetLogLevel(loglevel); i2p::log::Logger().SetLogLevel(loglevel);
if (logstream) { if (logstream) {
@ -123,7 +128,7 @@ namespace util
// use stdout -- default // use stdout -- default
} }
LogPrint(eLogInfo, "i2pd v", VERSION, " starting"); LogPrint(eLogNone, "i2pd v", VERSION, " starting");
LogPrint(eLogDebug, "FS: main config file: ", config); LogPrint(eLogDebug, "FS: main config file: ", config);
LogPrint(eLogDebug, "FS: data directory: ", datadir); LogPrint(eLogDebug, "FS: data directory: ", datadir);
@ -144,6 +149,25 @@ namespace util
ipv4 = false; ipv4 = false;
ipv6 = true; ipv6 = true;
#endif #endif
// ifname -> address
std::string ifname; i2p::config::GetOption("ifname", ifname);
if (ipv4 && i2p::config::IsDefault ("address4"))
{
std::string ifname4; i2p::config::GetOption("ifname4", ifname4);
if (!ifname4.empty ())
i2p::config::SetOption ("address4", i2p::util::net::GetInterfaceAddress(ifname4, false).to_string ()); // v4
else if (!ifname.empty ())
i2p::config::SetOption ("address4", i2p::util::net::GetInterfaceAddress(ifname, false).to_string ()); // v4
}
if (ipv6 && i2p::config::IsDefault ("address6"))
{
std::string ifname6; i2p::config::GetOption("ifname6", ifname6);
if (!ifname6.empty ())
i2p::config::SetOption ("address6", i2p::util::net::GetInterfaceAddress(ifname6, true).to_string ()); // v6
else if (!ifname.empty ())
i2p::config::SetOption ("address6", i2p::util::net::GetInterfaceAddress(ifname, true).to_string ()); // v6
}
bool ygg; i2p::config::GetOption("meshnets.yggdrasil", ygg); bool ygg; i2p::config::GetOption("meshnets.yggdrasil", ygg);
boost::asio::ip::address_v6 yggaddr; boost::asio::ip::address_v6 yggaddr;
if (ygg) if (ygg)
@ -186,6 +210,11 @@ namespace util
{ {
bool published; i2p::config::GetOption("ntcp2.published", published); bool published; i2p::config::GetOption("ntcp2.published", published);
if (published) if (published)
{
std::string ntcp2proxy; i2p::config::GetOption("ntcp2.proxy", ntcp2proxy);
if (!ntcp2proxy.empty ()) published = false;
}
if (published)
{ {
uint16_t ntcp2port; i2p::config::GetOption("ntcp2.port", ntcp2port); uint16_t ntcp2port; i2p::config::GetOption("ntcp2.port", ntcp2port);
if (!ntcp2port) ntcp2port = port; // use standard port if (!ntcp2port) ntcp2port = port; // use standard port
@ -206,6 +235,8 @@ namespace util
if (!ntcp2) if (!ntcp2)
i2p::context.PublishNTCP2Address (port, true); i2p::context.PublishNTCP2Address (port, true);
i2p::context.UpdateNTCP2V6Address (yggaddr); i2p::context.UpdateNTCP2V6Address (yggaddr);
if (!ipv4 && !ipv6)
i2p::context.SetStatus (eRouterStatusMesh);
} }
bool transit; i2p::config::GetOption("notransit", transit); bool transit; i2p::config::GetOption("notransit", transit);
@ -343,7 +374,7 @@ namespace util
if(!ntcp2) LogPrint(eLogInfo, "Daemon: ntcp2 disabled"); if(!ntcp2) LogPrint(eLogInfo, "Daemon: ntcp2 disabled");
i2p::transport::transports.SetCheckReserved(checkInReserved); i2p::transport::transports.SetCheckReserved(checkInReserved);
i2p::transport::transports.Start(ntcp2 || i2p::context.SupportsMesh (), ssu); i2p::transport::transports.Start(ntcp2, ssu);
if (i2p::transport::transports.IsBoundSSU() || i2p::transport::transports.IsBoundNTCP2()) if (i2p::transport::transports.IsBoundSSU() || i2p::transport::transports.IsBoundNTCP2())
LogPrint(eLogInfo, "Daemon: Transports started"); LogPrint(eLogInfo, "Daemon: Transports started");
else else

View File

@ -123,6 +123,7 @@ namespace http {
const char HTTP_COMMAND_LOGLEVEL[] = "set_loglevel"; const char HTTP_COMMAND_LOGLEVEL[] = "set_loglevel";
const char HTTP_COMMAND_KILLSTREAM[] = "closestream"; const char HTTP_COMMAND_KILLSTREAM[] = "closestream";
const char HTTP_COMMAND_LIMITTRANSIT[] = "limittransit"; const char HTTP_COMMAND_LIMITTRANSIT[] = "limittransit";
const char HTTP_COMMAND_GET_REG_STRING[] = "get_reg_string";
const char HTTP_PARAM_SAM_SESSION_ID[] = "id"; const char HTTP_PARAM_SAM_SESSION_ID[] = "id";
const char HTTP_PARAM_ADDRESS[] = "address"; const char HTTP_PARAM_ADDRESS[] = "address";
@ -252,6 +253,9 @@ namespace http {
case eRouterStatusOK: s << "OK"; break; case eRouterStatusOK: s << "OK"; break;
case eRouterStatusTesting: s << "Testing"; break; case eRouterStatusTesting: s << "Testing"; break;
case eRouterStatusFirewalled: s << "Firewalled"; break; case eRouterStatusFirewalled: s << "Firewalled"; break;
case eRouterStatusUnknown: s << "Unknown"; break;
case eRouterStatusProxy: s << "Proxy"; break;
case eRouterStatusMesh: s << "Mesh"; break;
case eRouterStatusError: case eRouterStatusError:
{ {
s << "Error"; s << "Error";
@ -405,9 +409,9 @@ namespace http {
} }
} }
static void ShowLeaseSetDestination (std::stringstream& s, std::shared_ptr<const i2p::client::LeaseSetDestination> dest) static void ShowLeaseSetDestination (std::stringstream& s, std::shared_ptr<const i2p::client::LeaseSetDestination> dest, uint32_t token)
{ {
s << "<b>Base64:</b><br>\r\n<textarea readonly cols=\"80\" rows=\"8\" wrap=\"on\">"; s << "<b>Base64:</b><br>\r\n<textarea readonly cols=\"80\" rows=\"8\">";
s << dest->GetIdentity ()->ToBase64 () << "</textarea><br>\r\n<br>\r\n"; s << dest->GetIdentity ()->ToBase64 () << "</textarea><br>\r\n<br>\r\n";
if (dest->IsEncryptedLeaseSet ()) if (dest->IsEncryptedLeaseSet ())
{ {
@ -417,6 +421,20 @@ namespace http {
s << "</div>\r\n</div>\r\n"; s << "</div>\r\n</div>\r\n";
} }
if(dest->IsPublic())
{
std::string webroot; i2p::config::GetOption("http.webroot", webroot);
auto base32 = dest->GetIdentHash ().ToBase32 ();
s << "<div class='slide'><label for='slide-regaddr'><b>Address registration line</b></label>\r\n<input type=\"checkbox\" id=\"slide-regaddr\" />\r\n<div class=\"slidecontent\">\r\n"
"<form method=\"get\" action=\"" << webroot << "\">\r\n"
" <input type=\"hidden\" name=\"cmd\" value=\"" << HTTP_COMMAND_GET_REG_STRING << "\">\r\n"
" <input type=\"hidden\" name=\"token\" value=\"" << token << "\">\r\n"
" <input type=\"hidden\" name=\"b32\" value=\"" << base32 << "\">\r\n"
" <b>Domain:</b>\r\n<input type=\"text\" maxlength=\"67\" name=\"name\" placeholder=\"domain.i2p\" required>\r\n"
" <button type=\"submit\">Generate</button>\r\n"
"</form>\r\n<small><b>Note:</b> result string can be used only for registering 2LD domains (example.i2p). For registering subdomains please use i2pd-tools.</small>\r\n</div>\r\n</div>\r\n<br>\r\n";
}
if(dest->GetNumRemoteLeaseSets()) if(dest->GetNumRemoteLeaseSets())
{ {
s << "<div class='slide'><label for='slide-lease'><b>LeaseSets:</b> <i>" << dest->GetNumRemoteLeaseSets () s << "<div class='slide'><label for='slide-lease'><b>LeaseSets:</b> <i>" << dest->GetNumRemoteLeaseSets ()
@ -492,7 +510,7 @@ namespace http {
if (dest) if (dest)
{ {
ShowLeaseSetDestination (s, dest); ShowLeaseSetDestination (s, dest, token);
// show streams // show streams
s << "<table>\r\n<caption>Streams</caption>\r\n<thead>\r\n<tr>"; s << "<table>\r\n<caption>Streams</caption>\r\n<thead>\r\n<tr>";
s << "<th style=\"width:25px;\">StreamID</th>"; s << "<th style=\"width:25px;\">StreamID</th>";
@ -535,7 +553,7 @@ namespace http {
} }
} }
void ShowI2CPLocalDestination (std::stringstream& s, const std::string& id) void ShowI2CPLocalDestination (std::stringstream& s, const std::string& id)
{ {
auto i2cpServer = i2p::client::context.GetI2CPServer (); auto i2cpServer = i2p::client::context.GetI2CPServer ();
if (i2cpServer) if (i2cpServer)
@ -543,7 +561,7 @@ namespace http {
s << "<b>I2CP Local Destination:</b><br>\r\n<br>\r\n"; s << "<b>I2CP Local Destination:</b><br>\r\n<br>\r\n";
auto it = i2cpServer->GetSessions ().find (std::stoi (id)); auto it = i2cpServer->GetSessions ().find (std::stoi (id));
if (it != i2cpServer->GetSessions ().end ()) if (it != i2cpServer->GetSessions ().end ())
ShowLeaseSetDestination (s, it->second->GetDestination ()); ShowLeaseSetDestination (s, it->second->GetDestination (), 0);
else else
ShowError(s, "I2CP session not found"); ShowError(s, "I2CP session not found");
} }
@ -823,7 +841,7 @@ namespace http {
s << "<b>SAM Sessions:</b> no sessions currently running.<br>\r\n"; s << "<b>SAM Sessions:</b> no sessions currently running.<br>\r\n";
} }
void ShowSAMSession (std::stringstream& s, const std::string& id) void ShowSAMSession (std::stringstream& s, const std::string& id)
{ {
auto sam = i2p::client::context.GetSAMBridge (); auto sam = i2p::client::context.GetSAMBridge ();
if (!sam) { if (!sam) {
@ -1208,15 +1226,15 @@ namespace http {
if (dest) if (dest)
{ {
if(dest->DeleteStream (streamID)) if(dest->DeleteStream (streamID))
s << "<b>SUCCESS</b>:&nbsp;Stream closed<br><br>\r\n"; s << "<b>SUCCESS</b>:&nbsp;Stream closed<br>\r\n<br>\r\n";
else else
s << "<b>ERROR</b>:&nbsp;Stream not found or already was closed<br><br>\r\n"; s << "<b>ERROR</b>:&nbsp;Stream not found or already was closed<br>\r\n<br>\r\n";
} }
else else
s << "<b>ERROR</b>:&nbsp;Destination not found<br><br>\r\n"; s << "<b>ERROR</b>:&nbsp;Destination not found<br>\r\n<br>\r\n";
} }
else else
s << "<b>ERROR</b>:&nbsp;StreamID can be null<br><br>\r\n"; s << "<b>ERROR</b>:&nbsp;StreamID can be null<br>\r\n<br>\r\n";
s << "<a href=\"" << webroot << "?page=local_destination&b32=" << b32 << "\">Return to destination page</a><br>\r\n"; s << "<a href=\"" << webroot << "?page=local_destination&b32=" << b32 << "\">Return to destination page</a><br>\r\n";
s << "<p>You will be redirected back in 5 seconds</b>"; s << "<p>You will be redirected back in 5 seconds</b>";
@ -1230,13 +1248,62 @@ namespace http {
if (limit > 0 && limit <= 65535) if (limit > 0 && limit <= 65535)
SetMaxNumTransitTunnels (limit); SetMaxNumTransitTunnels (limit);
else { else {
s << "<b>ERROR</b>:&nbsp;Transit tunnels count must not exceed 65535<br><br>\r\n"; s << "<b>ERROR</b>:&nbsp;Transit tunnels count must not exceed 65535\r\n<br>\r\n<br>\r\n";
s << "<a href=\"" << webroot << "?page=commands\">Back to commands list</a><br>\r\n"; s << "<a href=\"" << webroot << "?page=commands\">Back to commands list</a>\r\n<br>\r\n";
s << "<p>You will be redirected back in 5 seconds</b>"; s << "<p>You will be redirected back in 5 seconds</b>";
res.add_header("Refresh", redirect.c_str()); res.add_header("Refresh", redirect.c_str());
return; return;
} }
} }
else if (cmd == HTTP_COMMAND_GET_REG_STRING)
{
std::string b32 = params["b32"];
std::string name = params["name"];
i2p::data::IdentHash ident;
ident.FromBase32 (b32);
auto dest = i2p::client::context.FindLocalDestination (ident);
if (dest)
{
std::size_t pos;
pos = name.find (".i2p");
if (pos == (name.length () - 4))
{
pos = name.find (".b32.i2p");
if (pos == std::string::npos)
{
auto signatureLen = dest->GetIdentity ()->GetSignatureLen ();
uint8_t * signature = new uint8_t[signatureLen];
char * sig = new char[signatureLen*2];
std::stringstream out;
out << name << "=" << dest->GetIdentity ()->ToBase64 ();
dest->Sign ((uint8_t *)out.str ().c_str (), out.str ().length (), signature);
auto len = i2p::data::ByteStreamToBase64 (signature, signatureLen, sig, signatureLen*2);
sig[len] = 0;
out << "#!sig=" << sig;
s << "<b>SUCCESS</b>:<br>\r\n<form action=\"http://shx5vqsw7usdaunyzr2qmes2fq37oumybpudrd4jjj4e4vk4uusa.b32.i2p/add\" method=\"post\" rel=\"noreferrer\" target=\"_blank\">\r\n"
"<textarea readonly name=\"record\" cols=\"80\" rows=\"10\">" << out.str () << "</textarea>\r\n<br>\r\n<br>\r\n"
"<b>Register at reg.i2p:</b>\r\n<br>\r\n"
"<b>Description:</b>\r\n<input type=\"text\" maxlength=\"64\" name=\"desc\" placeholder=\"A bit information about service on domain\">\r\n"
"<input type=\"submit\" value=\"Submit\">\r\n"
"</form>\r\n<br>\r\n";
delete[] signature;
delete[] sig;
}
else
s << "<b>ERROR</b>:&nbsp;Domain can't end with .b32.i2p\r\n<br>\r\n<br>\r\n";
}
else
s << "<b>ERROR</b>:&nbsp;Domain must end with .i2p\r\n<br>\r\n<br>\r\n";
}
else
s << "<b>ERROR</b>:&nbsp;Such destination is not found\r\n<br>\r\n<br>\r\n";
s << "<a href=\"" << webroot << "?page=local_destination&b32=" << b32 << "\">Return to destination page</a>\r\n";
return;
}
else else
{ {
res.code = 400; res.code = 400;

6
debian/changelog vendored
View File

@ -1,3 +1,9 @@
i2pd (2.37.0-1) unstable; urgency=medium
* updated to version 2.37.0
-- orignal <orignal@i2pmail.org> Mon, 15 Mar 2021 16:00:00 +0000
i2pd (2.36.0-1) unstable; urgency=high i2pd (2.36.0-1) unstable; urgency=high
* updated to version 2.36.0/0.9.49 * updated to version 2.36.0/0.9.49

View File

@ -19,6 +19,7 @@
#include "Identity.h" #include "Identity.h"
#include "Config.h" #include "Config.h"
#include "version.h" #include "version.h"
#include "Log.h"
using namespace boost::program_options; using namespace boost::program_options;
@ -65,7 +66,7 @@ namespace config {
("ssu", bool_switch()->default_value(true), "Enable SSU transport (default: enabled)") ("ssu", bool_switch()->default_value(true), "Enable SSU transport (default: enabled)")
("ntcpproxy", value<std::string>()->default_value(""), "Ignored") ("ntcpproxy", value<std::string>()->default_value(""), "Ignored")
#ifdef _WIN32 #ifdef _WIN32
("svcctl", value<std::string>()->default_value(""), "Windows service management ('install' or 'remove')") ("svcctl", value<std::string>()->default_value(""), "Ignored")
("insomnia", bool_switch()->default_value(false), "Prevent system from sleeping (default: disabled)") ("insomnia", bool_switch()->default_value(false), "Prevent system from sleeping (default: disabled)")
("close", value<std::string>()->default_value("ask"), "Action on close: minimize, exit, ask") ("close", value<std::string>()->default_value("ask"), "Action on close: minimize, exit, ask")
#endif #endif
@ -209,7 +210,7 @@ namespace config {
), "Reseed URLs, separated by comma") ), "Reseed URLs, separated by comma")
("reseed.yggurls", value<std::string>()->default_value( ("reseed.yggurls", value<std::string>()->default_value(
"http://[324:9de3:fea4:f6ac::ace]:7070/" "http://[324:9de3:fea4:f6ac::ace]:7070/"
), "Reseed URLs through the Yggdrasil, separated by comma") ), "Reseed URLs through the Yggdrasil, separated by comma")
; ;
options_description addressbook("AddressBook options"); options_description addressbook("AddressBook options");
@ -218,12 +219,12 @@ namespace config {
"http://shx5vqsw7usdaunyzr2qmes2fq37oumybpudrd4jjj4e4vk4uusa.b32.i2p/hosts.txt" "http://shx5vqsw7usdaunyzr2qmes2fq37oumybpudrd4jjj4e4vk4uusa.b32.i2p/hosts.txt"
), "AddressBook subscription URL for initial setup") ), "AddressBook subscription URL for initial setup")
("addressbook.subscriptions", value<std::string>()->default_value(""), "AddressBook subscriptions URLs, separated by comma") ("addressbook.subscriptions", value<std::string>()->default_value(""), "AddressBook subscriptions URLs, separated by comma")
("addressbook.hostsfile", value<std::string>()->default_value(""), "File to dump addresses in hosts.txt format"); ("addressbook.hostsfile", value<std::string>()->default_value(""), "File to dump addresses in hosts.txt format");
options_description trust("Trust options"); options_description trust("Trust options");
trust.add_options() trust.add_options()
("trust.enabled", value<bool>()->default_value(false), "Enable explicit trust options") ("trust.enabled", value<bool>()->default_value(false), "Enable explicit trust options")
("trust.family", value<std::string>()->default_value(""), "Router Familiy to trust for first hops") ("trust.family", value<std::string>()->default_value(""), "Router Family to trust for first hops")
("trust.routers", value<std::string>()->default_value(""), "Only Connect to these routers") ("trust.routers", value<std::string>()->default_value(""), "Only Connect to these routers")
("trust.hidden", value<bool>()->default_value(false), "Should we hide our router from other routers?") ("trust.hidden", value<bool>()->default_value(false), "Should we hide our router from other routers?")
; ;
@ -281,9 +282,9 @@ namespace config {
options_description meshnets("Meshnet transports options"); options_description meshnets("Meshnet transports options");
meshnets.add_options() meshnets.add_options()
("meshnets.yggdrasil", bool_switch()->default_value(false), "Support transports through the Yggdrasil (deafult: false)") ("meshnets.yggdrasil", bool_switch()->default_value(false), "Support transports through the Yggdrasil (deafult: false)")
("meshnets.yggaddress", value<std::string>()->default_value(""), "Yggdrasil address to publish") ("meshnets.yggaddress", value<std::string>()->default_value(""), "Yggdrasil address to publish")
; ;
m_OptionsDesc m_OptionsDesc
.add(general) .add(general)
.add(limits) .add(limits)
@ -314,7 +315,7 @@ namespace config {
try try
{ {
auto style = boost::program_options::command_line_style::unix_style auto style = boost::program_options::command_line_style::unix_style
| boost::program_options::command_line_style::allow_long_disguise; | boost::program_options::command_line_style::allow_long_disguise;
style &= ~ boost::program_options::command_line_style::allow_guessing; style &= ~ boost::program_options::command_line_style::allow_guessing;
if (ignoreUnknown) if (ignoreUnknown)
store(command_line_parser(argc, argv).options(m_OptionsDesc).style (style).allow_unregistered().run(), m_Options); store(command_line_parser(argc, argv).options(m_OptionsDesc).style (style).allow_unregistered().run(), m_Options);
@ -323,6 +324,7 @@ namespace config {
} }
catch (boost::program_options::error& e) catch (boost::program_options::error& e)
{ {
ThrowFatal ("Error while parsing arguments: ", e.what());
std::cerr << "args: " << e.what() << std::endl; std::cerr << "args: " << e.what() << std::endl;
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
@ -360,6 +362,7 @@ namespace config {
if (!config.is_open()) if (!config.is_open())
{ {
ThrowFatal ("Missing or unreadable config file: ", path);
std::cerr << "missing/unreadable config file: " << path << std::endl; std::cerr << "missing/unreadable config file: " << path << std::endl;
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
@ -370,6 +373,7 @@ namespace config {
} }
catch (boost::program_options::error& e) catch (boost::program_options::error& e)
{ {
ThrowFatal ("Error while parsing config file: ", e.what());
std::cerr << e.what() << std::endl; std::cerr << e.what() << std::endl;
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
}; };

View File

@ -137,6 +137,8 @@ namespace client
void ProcessDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg); void ProcessDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg);
void SetLeaseSetUpdated (); void SetLeaseSetUpdated ();
bool IsPublic () const { return m_IsPublic; };
protected: protected:
// implements GarlicDestination // implements GarlicDestination
@ -147,7 +149,6 @@ namespace client
int GetLeaseSetType () const { return m_LeaseSetType; }; int GetLeaseSetType () const { return m_LeaseSetType; };
void SetLeaseSetType (int leaseSetType) { m_LeaseSetType = leaseSetType; }; void SetLeaseSetType (int leaseSetType) { m_LeaseSetType = leaseSetType; };
int GetAuthType () const { return m_AuthType; }; int GetAuthType () const { return m_AuthType; };
bool IsPublic () const { return m_IsPublic; };
virtual void CleanupDestination () {}; // additional clean up in derived classes virtual void CleanupDestination () {}; // additional clean up in derived classes
// I2CP // I2CP
virtual void HandleDataMessage (const uint8_t * buf, size_t len) = 0; virtual void HandleDataMessage (const uint8_t * buf, size_t len) = 0;

View File

@ -227,7 +227,7 @@ namespace garlic
if (!GetOwner ()) return false; if (!GetOwner ()) return false;
// we are Bob // we are Bob
// KDF1 // KDF1
i2p::crypto::InitNoiseIKState (*this, GetOwner ()->GetEncryptionPublicKey (i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD)); // bpk i2p::crypto::InitNoiseIKState (GetNoiseState (), GetOwner ()->GetEncryptionPublicKey (i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD)); // bpk
if (!i2p::crypto::GetElligator ()->Decode (buf, m_Aepk)) if (!i2p::crypto::GetElligator ()->Decode (buf, m_Aepk))
{ {
@ -460,7 +460,7 @@ namespace garlic
offset += 32; offset += 32;
// KDF1 // KDF1
i2p::crypto::InitNoiseIKState (*this, m_RemoteStaticKey); // bpk i2p::crypto::InitNoiseIKState (GetNoiseState (), m_RemoteStaticKey); // bpk
MixHash (m_EphemeralKeys->GetPublicKey (), 32); // h = SHA256(h || aepk) MixHash (m_EphemeralKeys->GetPublicKey (), 32); // h = SHA256(h || aepk)
uint8_t sharedSecret[32]; uint8_t sharedSecret[32];
if (!m_EphemeralKeys->Agree (m_RemoteStaticKey, sharedSecret)) // x25519(aesk, bpk) if (!m_EphemeralKeys->Agree (m_RemoteStaticKey, sharedSecret)) // x25519(aesk, bpk)
@ -520,7 +520,7 @@ namespace garlic
bool ECIESX25519AEADRatchetSession::NewOutgoingMessageForRouter (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen) bool ECIESX25519AEADRatchetSession::NewOutgoingMessageForRouter (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen)
{ {
// we are Alice, router's bpk is m_RemoteStaticKey // we are Alice, router's bpk is m_RemoteStaticKey
i2p::crypto::InitNoiseNState (*this, m_RemoteStaticKey); i2p::crypto::InitNoiseNState (GetNoiseState (), m_RemoteStaticKey);
size_t offset = 0; size_t offset = 0;
m_EphemeralKeys = i2p::transport::transports.GetNextX25519KeysPair (); m_EphemeralKeys = i2p::transport::transports.GetNextX25519KeysPair ();
memcpy (out + offset, m_EphemeralKeys->GetPublicKey (), 32); memcpy (out + offset, m_EphemeralKeys->GetPublicKey (), 32);
@ -656,7 +656,7 @@ namespace garlic
} }
buf += 32; len -= 32; buf += 32; len -= 32;
// KDF for Reply Key Section // KDF for Reply Key Section
i2p::util::SaveStateHelper<i2p::crypto::NoiseSymmetricState> s(*this); // restore noise state on exit i2p::util::SaveStateHelper<i2p::crypto::NoiseSymmetricState> s(GetNoiseState ()); // restore noise state on exit
MixHash (tag, 8); // h = SHA256(h || tag) MixHash (tag, 8); // h = SHA256(h || tag)
MixHash (bepk, 32); // h = SHA256(h || bepk) MixHash (bepk, 32); // h = SHA256(h || bepk)
uint8_t sharedSecret[32]; uint8_t sharedSecret[32];
@ -820,32 +820,6 @@ namespace garlic
} }
return true; return true;
} }
bool ECIESX25519AEADRatchetSession::HandleNextMessageForRouter (const uint8_t * buf, size_t len)
{
if (!GetOwner ()) return false;
// we are Bob
i2p::crypto::InitNoiseNState (*this, GetOwner ()->GetEncryptionPublicKey (i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD)); // bpk
MixHash (buf, 32);
uint8_t sharedSecret[32];
if (!GetOwner ()->Decrypt (buf, sharedSecret, nullptr, i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD)) // x25519(bsk, aepk)
{
LogPrint (eLogWarning, "Garlic: Incorrect N ephemeral public key");
return false;
}
MixKey (sharedSecret);
buf += 32; len -= 32;
uint8_t nonce[12];
CreateNonce (0, nonce);
std::vector<uint8_t> payload (len - 16);
if (!i2p::crypto::AEADChaCha20Poly1305 (buf, len - 16, m_H, 32, m_CK + 32, nonce, payload.data (), len - 16, false)) // decrypt
{
LogPrint (eLogWarning, "Garlic: Payload for router AEAD verification failed");
return false;
}
HandlePayload (payload.data (), len - 16, nullptr, 0);
return true;
}
std::shared_ptr<I2NPMessage> ECIESX25519AEADRatchetSession::WrapSingleMessage (std::shared_ptr<const I2NPMessage> msg) std::shared_ptr<I2NPMessage> ECIESX25519AEADRatchetSession::WrapSingleMessage (std::shared_ptr<const I2NPMessage> msg)
{ {
@ -1124,6 +1098,38 @@ namespace garlic
ts*1000 > m_LastSentTimestamp + ECIESX25519_SEND_EXPIRATION_TIMEOUT*1000; // milliseconds ts*1000 > m_LastSentTimestamp + ECIESX25519_SEND_EXPIRATION_TIMEOUT*1000; // milliseconds
} }
RouterIncomingRatchetSession::RouterIncomingRatchetSession (const i2p::crypto::NoiseSymmetricState& initState):
ECIESX25519AEADRatchetSession (&i2p::context, false)
{
SetNoiseState (initState);
}
bool RouterIncomingRatchetSession::HandleNextMessage (const uint8_t * buf, size_t len)
{
if (!GetOwner ()) return false;
i2p::crypto::NoiseSymmetricState state (GetNoiseState ());
// we are Bob
state.MixHash (buf, 32);
uint8_t sharedSecret[32];
if (!GetOwner ()->Decrypt (buf, sharedSecret, nullptr, i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD)) // x25519(bsk, aepk)
{
LogPrint (eLogWarning, "Garlic: Incorrect N ephemeral public key");
return false;
}
state.MixKey (sharedSecret);
buf += 32; len -= 32;
uint8_t nonce[12];
CreateNonce (0, nonce);
std::vector<uint8_t> payload (len - 16);
if (!i2p::crypto::AEADChaCha20Poly1305 (buf, len - 16, state.m_H, 32, state.m_CK + 32, nonce, payload.data (), len - 16, false)) // decrypt
{
LogPrint (eLogWarning, "Garlic: Payload for router AEAD verification failed");
return false;
}
HandlePayload (payload.data (), len - 16, nullptr, 0);
return true;
}
std::shared_ptr<I2NPMessage> WrapECIESX25519AEADRatchetMessage (std::shared_ptr<const I2NPMessage> msg, const uint8_t * key, uint64_t tag) std::shared_ptr<I2NPMessage> WrapECIESX25519AEADRatchetMessage (std::shared_ptr<const I2NPMessage> msg, const uint8_t * key, uint64_t tag)
{ {
auto m = NewI2NPMessage (); auto m = NewI2NPMessage ();

View File

@ -164,7 +164,6 @@ namespace garlic
~ECIESX25519AEADRatchetSession (); ~ECIESX25519AEADRatchetSession ();
bool HandleNextMessage (uint8_t * buf, size_t len, std::shared_ptr<ReceiveRatchetTagSet> receiveTagset, int index = 0); bool HandleNextMessage (uint8_t * buf, size_t len, std::shared_ptr<ReceiveRatchetTagSet> receiveTagset, int index = 0);
bool HandleNextMessageForRouter (const uint8_t * buf, size_t len);
std::shared_ptr<I2NPMessage> WrapSingleMessage (std::shared_ptr<const I2NPMessage> msg); std::shared_ptr<I2NPMessage> WrapSingleMessage (std::shared_ptr<const I2NPMessage> msg);
std::shared_ptr<I2NPMessage> WrapOneTimeMessage (std::shared_ptr<const I2NPMessage> msg, bool isForRouter = false); std::shared_ptr<I2NPMessage> WrapOneTimeMessage (std::shared_ptr<const I2NPMessage> msg, bool isForRouter = false);
@ -186,16 +185,21 @@ namespace garlic
bool IsTerminated () const { return m_IsTerminated; } bool IsTerminated () const { return m_IsTerminated; }
uint64_t GetLastActivityTimestamp () const { return m_LastActivityTimestamp; }; uint64_t GetLastActivityTimestamp () const { return m_LastActivityTimestamp; };
protected:
i2p::crypto::NoiseSymmetricState& GetNoiseState () { return *this; };
void SetNoiseState (const i2p::crypto::NoiseSymmetricState& state) { GetNoiseState () = state; };
void CreateNonce (uint64_t seqn, uint8_t * nonce);
void HandlePayload (const uint8_t * buf, size_t len, const std::shared_ptr<ReceiveRatchetTagSet>& receiveTagset, int index);
private: private:
void CreateNonce (uint64_t seqn, uint8_t * nonce);
bool GenerateEphemeralKeysAndEncode (uint8_t * buf); // buf is 32 bytes bool GenerateEphemeralKeysAndEncode (uint8_t * buf); // buf is 32 bytes
void InitNewSessionTagset (std::shared_ptr<RatchetTagSet> tagsetNsr) const; void InitNewSessionTagset (std::shared_ptr<RatchetTagSet> tagsetNsr) const;
bool HandleNewIncomingSession (const uint8_t * buf, size_t len); bool HandleNewIncomingSession (const uint8_t * buf, size_t len);
bool HandleNewOutgoingSessionReply (uint8_t * buf, size_t len); bool HandleNewOutgoingSessionReply (uint8_t * buf, size_t len);
bool HandleExistingSessionMessage (uint8_t * buf, size_t len, std::shared_ptr<ReceiveRatchetTagSet> receiveTagset, int index); bool HandleExistingSessionMessage (uint8_t * buf, size_t len, std::shared_ptr<ReceiveRatchetTagSet> receiveTagset, int index);
void HandlePayload (const uint8_t * buf, size_t len, const std::shared_ptr<ReceiveRatchetTagSet>& receiveTagset, int index);
void HandleNextKey (const uint8_t * buf, size_t len, const std::shared_ptr<ReceiveRatchetTagSet>& receiveTagset); void HandleNextKey (const uint8_t * buf, size_t len, const std::shared_ptr<ReceiveRatchetTagSet>& receiveTagset);
bool NewOutgoingSessionMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen, bool isStatic = true); bool NewOutgoingSessionMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen, bool isStatic = true);
@ -237,6 +241,15 @@ namespace garlic
} }
}; };
// single session for all incoming messages
class RouterIncomingRatchetSession: public ECIESX25519AEADRatchetSession
{
public:
RouterIncomingRatchetSession (const i2p::crypto::NoiseSymmetricState& initState);
bool HandleNextMessage (const uint8_t * buf, size_t len);
};
std::shared_ptr<I2NPMessage> WrapECIESX25519AEADRatchetMessage (std::shared_ptr<const I2NPMessage> msg, const uint8_t * key, uint64_t tag); std::shared_ptr<I2NPMessage> WrapECIESX25519AEADRatchetMessage (std::shared_ptr<const I2NPMessage> msg, const uint8_t * key, uint64_t tag);
} }
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2020, The PurpleI2P Project * Copyright (c) 2013-2021, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@ -9,12 +9,15 @@
#include <algorithm> #include <algorithm>
#include <utility> #include <utility>
#include <stdio.h> #include <stdio.h>
#include "util.h"
#include "HTTP.h"
#include <ctime> #include <ctime>
#include "util.h"
#include "Base.h"
#include "HTTP.h"
namespace i2p { namespace i2p
namespace http { {
namespace http
{
const std::vector<std::string> HTTP_METHODS = { const std::vector<std::string> HTTP_METHODS = {
"GET", "HEAD", "POST", "PUT", "PATCH", "DELETE", "OPTIONS", "CONNECT", // HTTP basic methods "GET", "HEAD", "POST", "PUT", "PATCH", "DELETE", "OPTIONS", "CONNECT", // HTTP basic methods
"COPY", "LOCK", "MKCOL", "MOVE", "PROPFIND", "PROPPATCH", "UNLOCK", "SEARCH" // WebDAV methods, for SEARCH see rfc5323 "COPY", "LOCK", "MKCOL", "MOVE", "PROPFIND", "PROPPATCH", "UNLOCK", "SEARCH" // WebDAV methods, for SEARCH see rfc5323
@ -471,12 +474,15 @@ namespace http {
return ptr; return ptr;
} }
std::string UrlDecode(const std::string& data, bool allow_null) { std::string UrlDecode(const std::string& data, bool allow_null)
{
std::string decoded(data); std::string decoded(data);
size_t pos = 0; size_t pos = 0;
while ((pos = decoded.find('%', pos)) != std::string::npos) { while ((pos = decoded.find('%', pos)) != std::string::npos)
{
char c = strtol(decoded.substr(pos + 1, 2).c_str(), NULL, 16); char c = strtol(decoded.substr(pos + 1, 2).c_str(), NULL, 16);
if (c == '\0' && !allow_null) { if (c == '\0' && !allow_null)
{
pos += 3; pos += 3;
continue; continue;
} }
@ -486,9 +492,11 @@ namespace http {
return decoded; return decoded;
} }
bool MergeChunkedResponse (std::istream& in, std::ostream& out) { bool MergeChunkedResponse (std::istream& in, std::ostream& out)
{
std::string hexLen; std::string hexLen;
while (!in.eof ()) { while (!in.eof ())
{
std::getline (in, hexLen); std::getline (in, hexLen);
errno = 0; errno = 0;
long int len = strtoul(hexLen.c_str(), (char **) NULL, 16); long int len = strtoul(hexLen.c_str(), (char **) NULL, 16);
@ -506,5 +514,12 @@ namespace http {
} }
return true; return true;
} }
std::string CreateBasicAuthorizationString (const std::string& user, const std::string& pass)
{
if (user.empty () && pass.empty ()) return "";
return "Basic " + i2p::data::ToBase64Standard (user + ":" + pass);
}
} // http } // http
} // i2p } // i2p

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2020, The PurpleI2P Project * Copyright (c) 2013-2021, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@ -166,6 +166,9 @@ namespace http
* @return true on success, false otherwise * @return true on success, false otherwise
*/ */
bool MergeChunkedResponse (std::istream& in, std::ostream& out); bool MergeChunkedResponse (std::istream& in, std::ostream& out);
std::string CreateBasicAuthorizationString (const std::string& user, const std::string& pass);
} // http } // http
} // i2p } // i2p

View File

@ -148,7 +148,7 @@ namespace log {
LogLevel level; /**< message level */ LogLevel level; /**< message level */
std::thread::id tid; /**< id of thread that generated message */ std::thread::id tid; /**< id of thread that generated message */
LogMsg (LogLevel lvl, std::time_t ts, const std::string & txt): timestamp(ts), text(txt), level(lvl) {}; LogMsg (LogLevel lvl, std::time_t ts, std::string&& txt): timestamp(ts), text(std::move(txt)), level(lvl) {}
}; };
Log & Logger(); Log & Logger();
@ -189,7 +189,7 @@ void LogPrint (LogLevel level, TArgs&&... args) noexcept
return; return;
// fold message to single string // fold message to single string
std::stringstream ss(""); std::stringstream ss;
#if (__cplusplus >= 201703L) // C++ 17 or higher #if (__cplusplus >= 201703L) // C++ 17 or higher
(LogPrint (ss, std::forward<TArgs>(args)), ...); (LogPrint (ss, std::forward<TArgs>(args)), ...);
@ -197,7 +197,7 @@ void LogPrint (LogLevel level, TArgs&&... args) noexcept
LogPrint (ss, std::forward<TArgs>(args)...); LogPrint (ss, std::forward<TArgs>(args)...);
#endif #endif
auto msg = std::make_shared<i2p::log::LogMsg>(level, std::time(nullptr), ss.str()); auto msg = std::make_shared<i2p::log::LogMsg>(level, std::time(nullptr), std::move(ss).str());
msg->tid = std::this_thread::get_id(); msg->tid = std::this_thread::get_id();
log.Append(msg); log.Append(msg);
} }

View File

@ -1158,54 +1158,53 @@ namespace transport
} }
} }
else else
{
LogPrint(eLogInfo, "NTCP2: Proxy is not used"); LogPrint(eLogInfo, "NTCP2: Proxy is not used");
auto& addresses = context.GetRouterInfo ().GetAddresses (); // start acceptors
for (const auto& address: addresses) auto& addresses = context.GetRouterInfo ().GetAddresses ();
for (const auto& address: addresses)
{
if (!address) continue;
if (address->IsPublishedNTCP2 () && address->port)
{ {
if (!address) continue; if (address->host.is_v4())
if (address->IsPublishedNTCP2 ())
{ {
if (address->host.is_v4()) try
{ {
try auto ep = m_Address4 ? boost::asio::ip::tcp::endpoint (m_Address4->address(), address->port):
{ boost::asio::ip::tcp::endpoint (boost::asio::ip::tcp::v4(), address->port);
auto ep = m_Address4 ? boost::asio::ip::tcp::endpoint (m_Address4->address(), address->port): m_NTCP2Acceptor.reset (new boost::asio::ip::tcp::acceptor (GetService (), ep));
boost::asio::ip::tcp::endpoint (boost::asio::ip::tcp::v4(), address->port);
m_NTCP2Acceptor.reset (new boost::asio::ip::tcp::acceptor (GetService (), ep));
}
catch ( std::exception & ex )
{
LogPrint(eLogError, "NTCP2: Failed to bind to v4 port ", address->port, ex.what());
ThrowFatal ("Unable to start IPv4 NTCP2 transport at port ", address->port, ": ", ex.what ());
continue;
}
LogPrint (eLogInfo, "NTCP2: Start listening v4 TCP port ", address->port);
auto conn = std::make_shared<NTCP2Session>(*this);
m_NTCP2Acceptor->async_accept(conn->GetSocket (), std::bind (&NTCP2Server::HandleAccept, this, conn, std::placeholders::_1));
} }
else if (address->host.is_v6() && (context.SupportsV6 () || context.SupportsMesh ())) catch ( std::exception & ex )
{ {
m_NTCP2V6Acceptor.reset (new boost::asio::ip::tcp::acceptor (GetService ())); LogPrint(eLogError, "NTCP2: Failed to bind to v4 port ", address->port, ex.what());
try ThrowFatal ("Unable to start IPv4 NTCP2 transport at port ", address->port, ": ", ex.what ());
{ continue;
m_NTCP2V6Acceptor->open (boost::asio::ip::tcp::v6()); }
m_NTCP2V6Acceptor->set_option (boost::asio::ip::v6_only (true));
m_NTCP2V6Acceptor->set_option (boost::asio::socket_base::reuse_address (true));
m_NTCP2V6Acceptor->bind (boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v6(), address->port));
m_NTCP2V6Acceptor->listen ();
LogPrint (eLogInfo, "NTCP2: Start listening v6 TCP port ", address->port); LogPrint (eLogInfo, "NTCP2: Start listening v4 TCP port ", address->port);
auto conn = std::make_shared<NTCP2Session> (*this); auto conn = std::make_shared<NTCP2Session>(*this);
m_NTCP2V6Acceptor->async_accept(conn->GetSocket (), std::bind (&NTCP2Server::HandleAcceptV6, this, conn, std::placeholders::_1)); m_NTCP2Acceptor->async_accept(conn->GetSocket (), std::bind (&NTCP2Server::HandleAccept, this, conn, std::placeholders::_1));
} }
catch ( std::exception & ex ) else if (address->host.is_v6() && (context.SupportsV6 () || context.SupportsMesh ()))
{ {
LogPrint(eLogError, "NTCP2: failed to bind to v6 port ", address->port, ": ", ex.what()); m_NTCP2V6Acceptor.reset (new boost::asio::ip::tcp::acceptor (GetService ()));
ThrowFatal ("Unable to start IPv6 NTCP2 transport at port ", address->port, ": ", ex.what ()); try
continue; {
} m_NTCP2V6Acceptor->open (boost::asio::ip::tcp::v6());
m_NTCP2V6Acceptor->set_option (boost::asio::ip::v6_only (true));
m_NTCP2V6Acceptor->set_option (boost::asio::socket_base::reuse_address (true));
m_NTCP2V6Acceptor->bind (boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v6(), address->port));
m_NTCP2V6Acceptor->listen ();
LogPrint (eLogInfo, "NTCP2: Start listening v6 TCP port ", address->port);
auto conn = std::make_shared<NTCP2Session> (*this);
m_NTCP2V6Acceptor->async_accept(conn->GetSocket (), std::bind (&NTCP2Server::HandleAcceptV6, this, conn, std::placeholders::_1));
}
catch ( std::exception & ex )
{
LogPrint(eLogError, "NTCP2: failed to bind to v6 port ", address->port, ": ", ex.what());
ThrowFatal ("Unable to start IPv6 NTCP2 transport at port ", address->port, ": ", ex.what ());
continue;
} }
} }
} }
@ -1466,11 +1465,14 @@ namespace transport
}); });
} }
void NTCP2Server::UseProxy(ProxyType proxytype, const std::string & addr, uint16_t port) void NTCP2Server::UseProxy(ProxyType proxytype, const std::string& addr, uint16_t port,
const std::string& user, const std::string& pass)
{ {
m_ProxyType = proxytype; m_ProxyType = proxytype;
m_ProxyAddress = addr; m_ProxyAddress = addr;
m_ProxyPort = port; m_ProxyPort = port;
if (m_ProxyType == eHTTPProxy )
m_ProxyAuthorization = i2p::http::CreateBasicAuthorizationString (user, pass);
} }
void NTCP2Server::HandleProxyConnect(const boost::system::error_code& ecode, std::shared_ptr<NTCP2Session> conn, std::shared_ptr<boost::asio::deadline_timer> timer) void NTCP2Server::HandleProxyConnect(const boost::system::error_code& ecode, std::shared_ptr<NTCP2Session> conn, std::shared_ptr<boost::asio::deadline_timer> timer)
@ -1539,8 +1541,10 @@ namespace transport
if(ep.address ().is_v6 ()) if(ep.address ().is_v6 ())
req.uri = "[" + ep.address ().to_string() + "]:" + std::to_string(ep.port ()); req.uri = "[" + ep.address ().to_string() + "]:" + std::to_string(ep.port ());
else else
req.uri = ep.address ().to_string() + ":" + std::to_string(ep.port ()); req.uri = ep.address ().to_string() + ":" + std::to_string(ep.port ());
if (!m_ProxyAuthorization.empty ())
req.AddHeader("Proxy-Authorization", m_ProxyAuthorization);
boost::asio::streambuf writebuff; boost::asio::streambuf writebuff;
std::ostream out(&writebuff); std::ostream out(&writebuff);
out << req.to_string(); out << req.to_string();

View File

@ -246,7 +246,7 @@ namespace transport
void Connect(std::shared_ptr<NTCP2Session> conn); void Connect(std::shared_ptr<NTCP2Session> conn);
bool UsingProxy() const { return m_ProxyType != eNoProxy; }; bool UsingProxy() const { return m_ProxyType != eNoProxy; };
void UseProxy(ProxyType proxy, const std::string & address, uint16_t port); void UseProxy(ProxyType proxy, const std::string& address, uint16_t port, const std::string& user, const std::string& pass);
void SetLocalAddress (const boost::asio::ip::address& localAddress); void SetLocalAddress (const boost::asio::ip::address& localAddress);
@ -271,7 +271,7 @@ namespace transport
std::list<std::shared_ptr<NTCP2Session> > m_PendingIncomingSessions; std::list<std::shared_ptr<NTCP2Session> > m_PendingIncomingSessions;
ProxyType m_ProxyType; ProxyType m_ProxyType;
std::string m_ProxyAddress; std::string m_ProxyAddress, m_ProxyAuthorization;
uint16_t m_ProxyPort; uint16_t m_ProxyPort;
boost::asio::ip::tcp::resolver m_Resolver; boost::asio::ip::tcp::resolver m_Resolver;
std::unique_ptr<boost::asio::ip::tcp::endpoint> m_ProxyEndpoint; std::unique_ptr<boost::asio::ip::tcp::endpoint> m_ProxyEndpoint;

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2020, The PurpleI2P Project * Copyright (c) 2013-2021, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@ -57,7 +57,7 @@ namespace data
uint16_t threshold; i2p::config::GetOption("reseed.threshold", threshold); uint16_t threshold; i2p::config::GetOption("reseed.threshold", threshold);
if (m_RouterInfos.size () < threshold) // reseed if # of router less than threshold if (m_RouterInfos.size () < threshold) // reseed if # of router less than threshold
Reseed (); Reseed ();
else if (!GetRandomRouter (i2p::context.GetSharedRouterInfo ())) else if (!GetRandomRouter (i2p::context.GetSharedRouterInfo (), false))
Reseed (); // we don't have a router we can connect to. Trying to reseed Reseed (); // we don't have a router we can connect to. Trying to reseed
i2p::config::GetOption("persist.profiles", m_PersistProfiles); i2p::config::GetOption("persist.profiles", m_PersistProfiles);
@ -645,7 +645,9 @@ namespace data
auto floodfill = GetClosestFloodfill (destination, dest->GetExcludedPeers ()); auto floodfill = GetClosestFloodfill (destination, dest->GetExcludedPeers ());
if (floodfill) if (floodfill)
{ {
if (direct && !floodfill->IsCompatible (i2p::context.GetRouterInfo ())) direct = false; // check if fllodfill is reachable if (direct && !floodfill->IsReachableFrom (i2p::context.GetRouterInfo ()) &&
!i2p::transport::transports.IsConnected (floodfill->GetIdentHash ()))
direct = false; // floodfill can't be reached directly
if (direct) if (direct)
transports.SendMessage (floodfill->GetIdentHash (), dest->CreateRequestMessage (floodfill->GetIdentHash ())); transports.SendMessage (floodfill->GetIdentHash (), dest->CreateRequestMessage (floodfill->GetIdentHash ()));
else else
@ -1090,7 +1092,8 @@ namespace data
LogPrint (eLogInfo, "NetDb: Publishing our RouterInfo to ", i2p::data::GetIdentHashAbbreviation(floodfill->GetIdentHash ()), ". reply token=", replyToken); LogPrint (eLogInfo, "NetDb: Publishing our RouterInfo to ", i2p::data::GetIdentHashAbbreviation(floodfill->GetIdentHash ()), ". reply token=", replyToken);
m_PublishExcluded.insert (floodfill->GetIdentHash ()); m_PublishExcluded.insert (floodfill->GetIdentHash ());
m_PublishReplyToken = replyToken; m_PublishReplyToken = replyToken;
if (floodfill->IsCompatible (i2p::context.GetRouterInfo ())) // able to connect? if (floodfill->IsReachableFrom (i2p::context.GetRouterInfo ()) || // are we able to connect?
i2p::transport::transports.IsConnected (floodfill->GetIdentHash ())) // already connected ?
// send directly // send directly
transports.SendMessage (floodfill->GetIdentHash (), CreateDatabaseStoreMsg (i2p::context.GetSharedRouterInfo (), replyToken)); transports.SendMessage (floodfill->GetIdentHash (), CreateDatabaseStoreMsg (i2p::context.GetSharedRouterInfo (), replyToken));
else else
@ -1135,13 +1138,14 @@ namespace data
}); });
} }
std::shared_ptr<const RouterInfo> NetDb::GetRandomRouter (std::shared_ptr<const RouterInfo> compatibleWith) const std::shared_ptr<const RouterInfo> NetDb::GetRandomRouter (std::shared_ptr<const RouterInfo> compatibleWith, bool reverse) const
{ {
return GetRandomRouter ( return GetRandomRouter (
[compatibleWith](std::shared_ptr<const RouterInfo> router)->bool [compatibleWith, reverse](std::shared_ptr<const RouterInfo> router)->bool
{ {
return !router->IsHidden () && router != compatibleWith && return !router->IsHidden () && router != compatibleWith &&
router->IsCompatible (*compatibleWith); (reverse ? compatibleWith->IsReachableFrom (*router) :
router->IsReachableFrom (*compatibleWith));
}); });
} }
@ -1168,17 +1172,18 @@ namespace data
return GetRandomRouter ( return GetRandomRouter (
[](std::shared_ptr<const RouterInfo> router)->bool [](std::shared_ptr<const RouterInfo> router)->bool
{ {
return !router->IsHidden () && router->IsIntroducer (); return router->IsIntroducer () && !router->IsHidden () && !router->IsFloodfill (); // floodfills don't send relay tag
}); });
} }
std::shared_ptr<const RouterInfo> NetDb::GetHighBandwidthRandomRouter (std::shared_ptr<const RouterInfo> compatibleWith) const std::shared_ptr<const RouterInfo> NetDb::GetHighBandwidthRandomRouter (std::shared_ptr<const RouterInfo> compatibleWith, bool reverse) const
{ {
return GetRandomRouter ( return GetRandomRouter (
[compatibleWith](std::shared_ptr<const RouterInfo> router)->bool [compatibleWith, reverse](std::shared_ptr<const RouterInfo> router)->bool
{ {
return !router->IsHidden () && router != compatibleWith && return !router->IsHidden () && router != compatibleWith &&
router->IsCompatible (*compatibleWith) && (reverse ? compatibleWith->IsReachableFrom (*router) :
router->IsReachableFrom (*compatibleWith)) &&
(router->GetCaps () & RouterInfo::eHighBandwidth) && (router->GetCaps () & RouterInfo::eHighBandwidth) &&
router->GetVersion () >= NETDB_MIN_HIGHBANDWIDTH_VERSION; router->GetVersion () >= NETDB_MIN_HIGHBANDWIDTH_VERSION;
}); });

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2020, The PurpleI2P Project * Copyright (c) 2013-2021, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@ -83,8 +83,8 @@ namespace data
void HandleDeliveryStatusMsg (std::shared_ptr<const I2NPMessage> msg); void HandleDeliveryStatusMsg (std::shared_ptr<const I2NPMessage> msg);
std::shared_ptr<const RouterInfo> GetRandomRouter () const; std::shared_ptr<const RouterInfo> GetRandomRouter () const;
std::shared_ptr<const RouterInfo> GetRandomRouter (std::shared_ptr<const RouterInfo> compatibleWith) const; std::shared_ptr<const RouterInfo> GetRandomRouter (std::shared_ptr<const RouterInfo> compatibleWith, bool reverse) const;
std::shared_ptr<const RouterInfo> GetHighBandwidthRandomRouter (std::shared_ptr<const RouterInfo> compatibleWith) const; std::shared_ptr<const RouterInfo> GetHighBandwidthRandomRouter (std::shared_ptr<const RouterInfo> compatibleWith, bool reverse) const;
std::shared_ptr<const RouterInfo> GetRandomPeerTestRouter (bool v4only = true) const; std::shared_ptr<const RouterInfo> GetRandomPeerTestRouter (bool v4only = true) const;
std::shared_ptr<const RouterInfo> GetRandomSSUV6Router () const; // TODO: change to v6 peer test later std::shared_ptr<const RouterInfo> GetRandomSSUV6Router () const; // TODO: change to v6 peer test later
std::shared_ptr<const RouterInfo> GetRandomIntroducer () const; std::shared_ptr<const RouterInfo> GetRandomIntroducer () const;

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2020, The PurpleI2P Project * Copyright (c) 2013-2021, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@ -574,9 +574,11 @@ namespace data
proxyReq.method = "CONNECT"; proxyReq.method = "CONNECT";
proxyReq.version = "HTTP/1.1"; proxyReq.version = "HTTP/1.1";
proxyReq.uri = url.host + ":" + std::to_string(url.port); proxyReq.uri = url.host + ":" + std::to_string(url.port);
auto auth = i2p::http::CreateBasicAuthorizationString (proxyUrl.user, proxyUrl.pass);
if (!auth.empty ())
proxyReq.AddHeader("Proxy-Authorization", auth);
boost::asio::streambuf writebuf, readbuf; boost::asio::streambuf writebuf, readbuf;
std::ostream out(&writebuf); std::ostream out(&writebuf);
out << proxyReq.to_string(); out << proxyReq.to_string();

View File

@ -48,6 +48,7 @@ namespace i2p
auto initState = new i2p::crypto::NoiseSymmetricState (); auto initState = new i2p::crypto::NoiseSymmetricState ();
i2p::crypto::InitNoiseNState (*initState, GetIdentity ()->GetEncryptionPublicKey ()); i2p::crypto::InitNoiseNState (*initState, GetIdentity ()->GetEncryptionPublicKey ());
m_InitialNoiseState.reset (initState); m_InitialNoiseState.reset (initState);
m_ECIESSession = std::make_shared<i2p::garlic::RouterIncomingRatchetSession>(*initState);
} }
} }
@ -75,33 +76,41 @@ namespace i2p
bool ntcp2; i2p::config::GetOption("ntcp2.enabled", ntcp2); bool ntcp2; i2p::config::GetOption("ntcp2.enabled", ntcp2);
bool ygg; i2p::config::GetOption("meshnets.yggdrasil", ygg); bool ygg; i2p::config::GetOption("meshnets.yggdrasil", ygg);
bool nat; i2p::config::GetOption("nat", nat); bool nat; i2p::config::GetOption("nat", nat);
std::string ifname; i2p::config::GetOption("ifname", ifname);
std::string ifname4; i2p::config::GetOption("ifname4", ifname4);
std::string ifname6; i2p::config::GetOption("ifname6", ifname6);
if ((ntcp2 || ygg) && !m_NTCP2Keys) if ((ntcp2 || ygg) && !m_NTCP2Keys)
NewNTCP2Keys (); NewNTCP2Keys ();
bool ntcp2Published = false; bool ntcp2Published = false;
if (ntcp2) if (ntcp2)
{
i2p::config::GetOption("ntcp2.published", ntcp2Published); i2p::config::GetOption("ntcp2.published", ntcp2Published);
uint8_t caps = 0; if (ntcp2Published)
{
std::string ntcp2proxy; i2p::config::GetOption("ntcp2.proxy", ntcp2proxy);
if (!ntcp2proxy.empty ()) ntcp2Published = false;
}
}
uint8_t caps = 0, addressCaps = 0;
if (ipv4) if (ipv4)
{ {
std::string host = "127.0.0.1"; std::string host = "127.0.0.1";
if (!i2p::config::IsDefault("host")) if (!i2p::config::IsDefault("host"))
i2p::config::GetOption("host", host); i2p::config::GetOption("host", host);
else if (!nat && !ifname.empty()) else if (!nat)
/* bind to interface, we have no NAT so set external address too */ {
host = i2p::util::net::GetInterfaceAddress(ifname, false).to_string(); // v4 // we have no NAT so set external address from local address
if(ifname4.size()) std::string address4; i2p::config::GetOption("address4", address4);
host = i2p::util::net::GetInterfaceAddress(ifname4, false).to_string(); if (!address4.empty ()) host = address4;
}
if (ntcp2) if (ntcp2)
{ {
if (ntcp2Published) if (ntcp2Published)
routerInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv, boost::asio::ip::address_v4::from_string (host), port); routerInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv, boost::asio::ip::address_v4::from_string (host), port);
else // add non-published NTCP2 address else // add non-published NTCP2 address
{
addressCaps = i2p::data::RouterInfo::AddressCaps::eV4;
routerInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv); routerInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv);
}
} }
if (ssu) if (ssu)
{ {
@ -114,10 +123,11 @@ namespace i2p
std::string host = "::1"; std::string host = "::1";
if (!i2p::config::IsDefault("host") && !ipv4) // override if v6 only if (!i2p::config::IsDefault("host") && !ipv4) // override if v6 only
i2p::config::GetOption("host", host); i2p::config::GetOption("host", host);
else if (!ifname.empty()) else
host = i2p::util::net::GetInterfaceAddress(ifname, true).to_string(); // v6 {
if(ifname6.size()) std::string address6; i2p::config::GetOption("address6", address6);
host = i2p::util::net::GetInterfaceAddress(ifname6, true).to_string(); if (!address6.empty ()) host = address6;
}
if (ntcp2) if (ntcp2)
{ {
@ -130,8 +140,12 @@ namespace i2p
ntcp2Host = host; ntcp2Host = host;
routerInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv, boost::asio::ip::address_v6::from_string (ntcp2Host), port); routerInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv, boost::asio::ip::address_v6::from_string (ntcp2Host), port);
} }
else if (!ipv4) // no other ntcp2 addresses yet else
routerInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv); {
if (!ipv4) // no other ntcp2 addresses yet
routerInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv);
addressCaps |= i2p::data::RouterInfo::AddressCaps::eV6;
}
} }
if (ssu) if (ssu)
{ {
@ -145,7 +159,9 @@ namespace i2p
if (!yggaddr.is_unspecified ()) if (!yggaddr.is_unspecified ())
routerInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv, yggaddr, port); routerInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv, yggaddr, port);
} }
if (addressCaps)
routerInfo.SetUnreachableAddressesTransportCaps (addressCaps);
routerInfo.SetCaps (caps); // caps + L routerInfo.SetCaps (caps); // caps + L
routerInfo.SetProperty ("netId", std::to_string (m_NetID)); routerInfo.SetProperty ("netId", std::to_string (m_NetID));
routerInfo.SetProperty ("router.version", I2P_VERSION); routerInfo.SetProperty ("router.version", I2P_VERSION);
@ -215,7 +231,7 @@ namespace i2p
bool updated = false; bool updated = false;
for (auto& address : m_RouterInfo.GetAddresses ()) for (auto& address : m_RouterInfo.GetAddresses ())
{ {
if (address->IsNTCP2 () && (address->port != port || address->ntcp2->isPublished != publish) && (!v4only || address->host.is_v4 ())) if (address->IsNTCP2 () && (address->port != port || address->ntcp2->isPublished != publish) && (!v4only || address->IsV4 ()))
{ {
if (!port && !address->port) if (!port && !address->port)
{ {
@ -388,6 +404,7 @@ namespace i2p
else if (limit > 48) { SetBandwidth('M'); } else if (limit > 48) { SetBandwidth('M'); }
else if (limit > 12) { SetBandwidth('L'); } else if (limit > 12) { SetBandwidth('L'); }
else { SetBandwidth('K'); } else { SetBandwidth('K'); }
m_BandwidthLimit = limit; // set precise limit
} }
void RouterContext::SetShareRatio (int percents) void RouterContext::SetShareRatio (int percents)
@ -485,14 +502,13 @@ namespace i2p
{ {
if (supportsV6) if (supportsV6)
{ {
m_RouterInfo.EnableV6 ();
// insert v6 addresses if necessary // insert v6 addresses if necessary
bool foundSSU = false, foundNTCP2 = false; bool foundSSU = false, foundNTCP2 = false;
uint16_t port = 0; uint16_t port = 0;
auto& addresses = m_RouterInfo.GetAddresses (); auto& addresses = m_RouterInfo.GetAddresses ();
for (auto& addr: addresses) for (auto& addr: addresses)
{ {
if (addr->host.is_v6 ()) if (addr->IsV6 () && !i2p::util::net::IsYggdrasilAddress (addr->host))
{ {
if (addr->transportStyle == i2p::data::RouterInfo::eTransportSSU) if (addr->transportStyle == i2p::data::RouterInfo::eTransportSSU)
foundSSU = true; foundSSU = true;
@ -529,6 +545,7 @@ namespace i2p
m_RouterInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv, boost::asio::ip::address::from_string (ntcp2Host), ntcp2Port); m_RouterInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv, boost::asio::ip::address::from_string (ntcp2Host), ntcp2Port);
} }
} }
m_RouterInfo.EnableV6 ();
} }
else else
m_RouterInfo.DisableV6 (); m_RouterInfo.DisableV6 ();
@ -537,8 +554,54 @@ namespace i2p
void RouterContext::SetSupportsV4 (bool supportsV4) void RouterContext::SetSupportsV4 (bool supportsV4)
{ {
// check if updates
if (supportsV4 && SupportsV4 ()) return;
if (!supportsV4 && !SupportsV4 ()) return;
// update
if (supportsV4) if (supportsV4)
{
bool foundSSU = false, foundNTCP2 = false;
std::string host = "127.0.0.1";
uint16_t port = 0;
auto& addresses = m_RouterInfo.GetAddresses ();
for (auto& addr: addresses)
{
if (addr->IsV4 ())
{
if (addr->transportStyle == i2p::data::RouterInfo::eTransportSSU)
foundSSU = true;
else if (addr->transportStyle == i2p::data::RouterInfo::eTransportNTCP)
foundNTCP2 = true;
}
if (addr->port) port = addr->port;
}
if (!port) i2p::config::GetOption("port", port);
// SSU
if (!foundSSU)
{
bool ssu; i2p::config::GetOption("ssu", ssu);
if (ssu)
m_RouterInfo.AddSSUAddress (host.c_str (), port, nullptr);
}
// NTCP2
if (!foundNTCP2)
{
bool ntcp2; i2p::config::GetOption("ntcp2.enabled", ntcp2);
if (ntcp2)
{
bool ntcp2Published; i2p::config::GetOption("ntcp2.published", ntcp2Published);
if (ntcp2Published)
{
uint16_t ntcp2Port; i2p::config::GetOption ("ntcp2.port", ntcp2Port);
if (!ntcp2Port) ntcp2Port = port;
m_RouterInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv, boost::asio::ip::address::from_string (host), ntcp2Port);
}
else
m_RouterInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv);
}
}
m_RouterInfo.EnableV4 (); m_RouterInfo.EnableV4 ();
}
else else
m_RouterInfo.DisableV4 (); m_RouterInfo.DisableV4 ();
UpdateRouterInfo (); UpdateRouterInfo ();
@ -638,7 +701,14 @@ namespace i2p
} }
} }
std::shared_ptr<const i2p::data::IdentityEx> oldIdentity; std::shared_ptr<const i2p::data::IdentityEx> oldIdentity;
if (m_Keys.GetPublic ()->GetSigningKeyType () == i2p::data::SIGNING_KEY_TYPE_DSA_SHA1) bool rekey = m_Keys.GetPublic ()->GetSigningKeyType () == i2p::data::SIGNING_KEY_TYPE_DSA_SHA1;
if (!rekey && m_Keys.GetPublic ()->GetCryptoKeyType () == i2p::data::CRYPTO_KEY_TYPE_ELGAMAL)
{
// rekey routers with bandwidth = L (or default) this time
std::string bandwidth; i2p::config::GetOption("bandwidth", bandwidth);
if (bandwidth.empty () || bandwidth[0] == 'L') rekey = true;
}
if (rekey)
{ {
// update keys // update keys
LogPrint (eLogInfo, "Router: router keys are obsolete. Creating new"); LogPrint (eLogInfo, "Router: router keys are obsolete. Creating new");
@ -669,8 +739,8 @@ namespace i2p
m_RouterInfo.Update (routerInfo.GetBuffer (), routerInfo.GetBufferLen ()); m_RouterInfo.Update (routerInfo.GetBuffer (), routerInfo.GetBufferLen ());
if (oldIdentity) if (oldIdentity)
m_RouterInfo.SetRouterIdentity (GetIdentity ()); // from new keys m_RouterInfo.SetRouterIdentity (GetIdentity ()); // from new keys
m_RouterInfo.SetProperty ("coreVersion", I2P_VERSION);
m_RouterInfo.SetProperty ("router.version", I2P_VERSION); m_RouterInfo.SetProperty ("router.version", I2P_VERSION);
m_RouterInfo.DeleteProperty ("coreVersion"); // TODO: remove later
} }
else else
{ {
@ -738,8 +808,10 @@ namespace i2p
return; return;
} }
buf += 4; buf += 4;
auto session = std::make_shared<i2p::garlic::ECIESX25519AEADRatchetSession>(this, false); if (m_ECIESSession)
session->HandleNextMessageForRouter (buf, len); m_ECIESSession->HandleNextMessage (buf, len);
else
LogPrint (eLogError, "Router: Session is not set for ECIES router");
} }
else else
i2p::garlic::GarlicDestination::ProcessGarlicMessage (msg); i2p::garlic::GarlicDestination::ProcessGarlicMessage (msg);

View File

@ -21,6 +21,11 @@
namespace i2p namespace i2p
{ {
namespace garlic
{
class RouterIncomingRatchetSession;
}
const char ROUTER_INFO[] = "router.info"; const char ROUTER_INFO[] = "router.info";
const char ROUTER_KEYS[] = "router.keys"; const char ROUTER_KEYS[] = "router.keys";
const char NTCP2_KEYS[] = "ntcp2.keys"; const char NTCP2_KEYS[] = "ntcp2.keys";
@ -32,7 +37,9 @@ namespace i2p
eRouterStatusTesting = 1, eRouterStatusTesting = 1,
eRouterStatusFirewalled = 2, eRouterStatusFirewalled = 2,
eRouterStatusError = 3, eRouterStatusError = 3,
eRouterStatusUnknown = 4 eRouterStatusUnknown = 4,
eRouterStatusProxy = 5,
eRouterStatusMesh = 6
}; };
enum RouterError enum RouterError
@ -42,7 +49,7 @@ namespace i2p
eRouterErrorOffline = 2, eRouterErrorOffline = 2,
eRouterErrorSymmetricNAT = 3 eRouterErrorSymmetricNAT = 3
}; };
class RouterContext: public i2p::garlic::GarlicDestination class RouterContext: public i2p::garlic::GarlicDestination
{ {
private: private:
@ -155,6 +162,7 @@ namespace i2p
i2p::data::RouterInfo m_RouterInfo; i2p::data::RouterInfo m_RouterInfo;
i2p::data::PrivateKeys m_Keys; i2p::data::PrivateKeys m_Keys;
std::shared_ptr<i2p::crypto::CryptoKeyDecryptor> m_Decryptor, m_TunnelDecryptor; std::shared_ptr<i2p::crypto::CryptoKeyDecryptor> m_Decryptor, m_TunnelDecryptor;
std::shared_ptr<i2p::garlic::RouterIncomingRatchetSession> m_ECIESSession;
uint64_t m_LastUpdateTime; // in seconds uint64_t m_LastUpdateTime; // in seconds
bool m_AcceptsTunnels, m_IsFloodfill; bool m_AcceptsTunnels, m_IsFloodfill;
std::chrono::time_point<std::chrono::steady_clock> m_StartupTime; std::chrono::time_point<std::chrono::steady_clock> m_StartupTime;

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2020, The PurpleI2P Project * Copyright (c) 2013-2021, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@ -193,7 +193,6 @@ namespace data
auto addresses = boost::make_shared<Addresses>(); auto addresses = boost::make_shared<Addresses>();
uint8_t numAddresses; uint8_t numAddresses;
s.read ((char *)&numAddresses, sizeof (numAddresses)); if (!s) return; s.read ((char *)&numAddresses, sizeof (numAddresses)); if (!s) return;
bool introducers = false;
for (int i = 0; i < numAddresses; i++) for (int i = 0; i < numAddresses; i++)
{ {
uint8_t supportedTransports = 0; uint8_t supportedTransports = 0;
@ -271,7 +270,6 @@ namespace data
LogPrint (eLogError, "RouterInfo: Introducer is presented for non-SSU address. Skipped"); LogPrint (eLogError, "RouterInfo: Introducer is presented for non-SSU address. Skipped");
continue; continue;
} }
introducers = true;
size_t l = strlen(key); size_t l = strlen(key);
unsigned char index = key[l-1] - '0'; // TODO: unsigned char index = key[l-1] - '0'; // TODO:
key[l-1] = 0; key[l-1] = 0;
@ -314,8 +312,8 @@ namespace data
{ {
if (address->caps) if (address->caps)
{ {
if (address->caps | AddressCaps::eV4) supportedTransports |= eNTCP2V4; if (address->caps & AddressCaps::eV4) supportedTransports |= eNTCP2V4;
if (address->caps | AddressCaps::eV6) supportedTransports |= eNTCP2V6; if (address->caps & AddressCaps::eV6) supportedTransports |= eNTCP2V6;
} }
else else
supportedTransports |= eNTCP2V4; // most likely, since we don't have host supportedTransports |= eNTCP2V4; // most likely, since we don't have host
@ -328,8 +326,13 @@ namespace data
{ {
if (isHost) if (isHost)
supportedTransports |= address->host.is_v4 () ? eSSUV4 : eSSUV6; supportedTransports |= address->host.is_v4 () ? eSSUV4 : eSSUV6;
else if (address->caps & AddressCaps::eV6)
{
supportedTransports |= eSSUV6;
if (address->caps & AddressCaps::eV4) supportedTransports |= eSSUV4; // in additional to v6
}
else else
if (introducers) supportedTransports |= eSSUV4; // in case if host is not presented supportedTransports |= eSSUV4; // in case if host or 6 caps is not preasented, we assume 4
} }
} }
if (supportedTransports) if (supportedTransports)
@ -403,7 +406,7 @@ namespace data
if (!s) return; if (!s) return;
} }
if (!m_SupportedTransports || !m_Addresses->size() || (UsesIntroducer () && !introducers)) if (!m_SupportedTransports)
SetUnreachable (true); SetUnreachable (true);
} }
@ -519,7 +522,7 @@ namespace data
if (address.IsNTCP2 ()) if (address.IsNTCP2 ())
{ {
WriteString ("NTCP2", s); WriteString ("NTCP2", s);
if (address.IsPublishedNTCP2 ()) if (address.IsPublishedNTCP2 () && !address.host.is_unspecified ())
isPublished = true; isPublished = true;
else else
{ {
@ -542,13 +545,26 @@ namespace data
// caps // caps
WriteString ("caps", properties); WriteString ("caps", properties);
properties << '='; properties << '=';
std::string caps; std::string caps;
if (address.IsPeerTesting ()) caps += CAPS_FLAG_SSU_TESTING; if (address.IsPeerTesting ()) caps += CAPS_FLAG_SSU_TESTING;
if (address.IsIntroducer ()) caps += CAPS_FLAG_SSU_INTRODUCER; if (address.host.is_v4 ())
if (IsReachable ()) {
if (IsReachable ())
{
isPublished = true;
if (address.IsIntroducer ()) caps += CAPS_FLAG_SSU_INTRODUCER;
}
else
caps += CAPS_FLAG_V4;
}
else if (address.host.is_v6 ())
isPublished = true; isPublished = true;
else else
caps += CAPS_FLAG_V4; {
if (address.caps & AddressCaps::eV4) caps += CAPS_FLAG_V4;
if (address.caps & AddressCaps::eV6) caps += CAPS_FLAG_V6;
if (caps.empty ()) caps += CAPS_FLAG_V4;
}
WriteString (caps, properties); WriteString (caps, properties);
properties << ';'; properties << ';';
} }
@ -637,7 +653,7 @@ namespace data
} }
} }
if (address.IsPublishedNTCP2 ()) if (address.IsNTCP2 () && isPublished)
{ {
// publish i for NTCP2 // publish i for NTCP2
WriteString ("i", properties); properties << '='; WriteString ("i", properties); properties << '=';
@ -785,9 +801,6 @@ namespace data
if (*it == *addr) return; if (*it == *addr) return;
m_SupportedTransports |= addr->host.is_v6 () ? eSSUV6 : eSSUV4; m_SupportedTransports |= addr->host.is_v6 () ? eSSUV6 : eSSUV4;
m_Addresses->push_back(std::move(addr)); m_Addresses->push_back(std::move(addr));
m_Caps |= eSSUTesting;
m_Caps |= eSSUIntroducer;
} }
void RouterInfo::AddNTCP2Address (const uint8_t * staticKey, const uint8_t * iv, const boost::asio::ip::address& host, int port) void RouterInfo::AddNTCP2Address (const uint8_t * staticKey, const uint8_t * iv, const boost::asio::ip::address& host, int port)
@ -913,13 +926,23 @@ namespace data
void RouterInfo::EnableV6 () void RouterInfo::EnableV6 ()
{ {
if (!IsV6 ()) if (!IsV6 ())
{
m_SupportedTransports |= eSSUV6 | eNTCP2V6; m_SupportedTransports |= eSSUV6 | eNTCP2V6;
uint8_t addressCaps = AddressCaps::eV6;
if (IsV4 ()) addressCaps |= AddressCaps::eV4;
SetUnreachableAddressesTransportCaps (addressCaps);
}
} }
void RouterInfo::EnableV4 () void RouterInfo::EnableV4 ()
{ {
if (!IsV4 ()) if (!IsV4 ())
{
m_SupportedTransports |= eSSUV4 | eNTCP2V4; m_SupportedTransports |= eSSUV4 | eNTCP2V4;
uint8_t addressCaps = AddressCaps::eV4;
if (IsV6 ()) addressCaps |= AddressCaps::eV6;
SetUnreachableAddressesTransportCaps (addressCaps);
}
} }
@ -931,6 +954,7 @@ namespace data
for (auto it = m_Addresses->begin (); it != m_Addresses->end ();) for (auto it = m_Addresses->begin (); it != m_Addresses->end ();)
{ {
auto addr = *it; auto addr = *it;
addr->caps &= ~AddressCaps::eV6;
if (addr->host.is_v6 ()) if (addr->host.is_v6 ())
it = m_Addresses->erase (it); it = m_Addresses->erase (it);
else else
@ -947,6 +971,7 @@ namespace data
for (auto it = m_Addresses->begin (); it != m_Addresses->end ();) for (auto it = m_Addresses->begin (); it != m_Addresses->end ();)
{ {
auto addr = *it; auto addr = *it;
addr->caps &= ~AddressCaps::eV4;
if (addr->host.is_v4 ()) if (addr->host.is_v4 ())
it = m_Addresses->erase (it); it = m_Addresses->erase (it);
else else
@ -1069,8 +1094,9 @@ namespace data
bool RouterInfo::IsEligibleFloodfill () const bool RouterInfo::IsEligibleFloodfill () const
{ {
// floodfill must be reachable, >= 0.9.28 and not DSA // floodfill must be reachable somehow, >= 0.9.28 and not DSA
return IsReachable () && m_Version >= NETDB_MIN_FLOODFILL_VERSION && return (IsReachable () || (m_SupportedTransports & eSSUV4)) &&
m_Version >= NETDB_MIN_FLOODFILL_VERSION &&
GetIdentity ()->GetSigningKeyType () != SIGNING_KEY_TYPE_DSA_SHA1; GetIdentity ()->GetSigningKeyType () != SIGNING_KEY_TYPE_DSA_SHA1;
} }
@ -1079,22 +1105,57 @@ namespace data
auto supportedTransports = m_SupportedTransports & (eSSUV4 | eSSUV6); auto supportedTransports = m_SupportedTransports & (eSSUV4 | eSSUV6);
if (!supportedTransports) return false; // no SSU if (!supportedTransports) return false; // no SSU
if (v4only && !(supportedTransports & eSSUV4)) return false; // no SSU v4 if (v4only && !(supportedTransports & eSSUV4)) return false; // no SSU v4
return GetAddress ( return (bool)GetAddress (
[](std::shared_ptr<const RouterInfo::Address> address)->bool [](std::shared_ptr<const RouterInfo::Address> address)->bool
{ {
return (address->transportStyle == eTransportSSU) && address->IsPeerTesting (); return (address->transportStyle == eTransportSSU) && address->IsPeerTesting ();
}) != nullptr; });
} }
bool RouterInfo::IsIntroducer () const bool RouterInfo::IsIntroducer () const
{ {
// TODO: support ipv6 // TODO: support ipv6
if (!(m_SupportedTransports & eSSUV4)) return false; if (!(m_SupportedTransports & eSSUV4)) return false;
return GetAddress ( return (bool)GetAddress (
[](std::shared_ptr<const RouterInfo::Address> address)->bool [](std::shared_ptr<const RouterInfo::Address> address)->bool
{ {
return (address->transportStyle == eTransportSSU) && address->IsIntroducer (); return (address->transportStyle == eTransportSSU) && address->IsIntroducer ();
}) != nullptr; });
} }
bool RouterInfo::IsReachableFrom (const RouterInfo& other) const
{
auto commonTransports = m_SupportedTransports & other.m_SupportedTransports;
if (!commonTransports) return false;
if (commonTransports & eNTCP2V6Mesh) return true;
return (bool)GetAddress (
[commonTransports](std::shared_ptr<const RouterInfo::Address> address)->bool
{
if (address->IsPublishedNTCP2 ())
{
if ((commonTransports & eNTCP2V4) && address->IsV4 ()) return true;
if ((commonTransports & eNTCP2V6) && address->IsV6 ()) return true;
}
else if (address->IsReachableSSU ())
{
if ((commonTransports & eSSUV4) && address->IsV4 ()) return true;
if ((commonTransports & eSSUV6) && address->IsV6 ()) return true;
}
return false;
});
}
void RouterInfo::SetUnreachableAddressesTransportCaps (uint8_t transports)
{
for (auto& addr: *m_Addresses)
{
// TODO: implement SSU
if (addr->transportStyle == eTransportNTCP && (!addr->IsPublishedNTCP2 () || addr->port))
{
addr->caps &= ~(eV4 | eV6);
addr->caps |= transports;
}
}
}
} }
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2020, The PurpleI2P Project * Copyright (c) 2013-2021, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@ -130,8 +130,8 @@ namespace data
bool IsCompatible (const boost::asio::ip::address& other) const bool IsCompatible (const boost::asio::ip::address& other) const
{ {
return (host.is_v4 () && other.is_v4 ()) || return (IsV4 () && other.is_v4 ()) ||
(host.is_v6 () && other.is_v6 ()); (IsV6 () && other.is_v6 ());
} }
bool operator==(const Address& other) const bool operator==(const Address& other) const
@ -147,9 +147,13 @@ namespace data
bool IsNTCP2 () const { return (bool)ntcp2; }; bool IsNTCP2 () const { return (bool)ntcp2; };
bool IsPublishedNTCP2 () const { return IsNTCP2 () && ntcp2->isPublished; }; bool IsPublishedNTCP2 () const { return IsNTCP2 () && ntcp2->isPublished; };
bool IsReachableSSU () const { return (bool)ssu && (!host.is_unspecified () || !ssu->introducers.empty ()); };
bool IsIntroducer () const { return caps & eSSUIntroducer; }; bool IsIntroducer () const { return caps & eSSUIntroducer; };
bool IsPeerTesting () const { return caps & eSSUTesting; }; bool IsPeerTesting () const { return caps & eSSUTesting; };
bool IsV4 () const { return (caps & AddressCaps::eV4) || host.is_v4 (); };
bool IsV6 () const { return (caps & AddressCaps::eV6) || host.is_v6 (); };
}; };
typedef std::list<std::shared_ptr<Address> > Addresses; typedef std::list<std::shared_ptr<Address> > Addresses;
@ -181,6 +185,7 @@ namespace data
void DeleteProperty (const std::string& key); // called from RouterContext only void DeleteProperty (const std::string& key); // called from RouterContext only
std::string GetProperty (const std::string& key) const; // called from RouterContext only std::string GetProperty (const std::string& key) const; // called from RouterContext only
void ClearProperties () { m_Properties.clear (); }; void ClearProperties () { m_Properties.clear (); };
void SetUnreachableAddressesTransportCaps (uint8_t transports); // bitmask of AddressCaps
bool IsFloodfill () const { return m_Caps & Caps::eFloodfill; }; bool IsFloodfill () const { return m_Caps & Caps::eFloodfill; };
bool IsReachable () const { return m_Caps & Caps::eReachable; }; bool IsReachable () const { return m_Caps & Caps::eReachable; };
bool IsSSU (bool v4only = true) const; bool IsSSU (bool v4only = true) const;
@ -196,7 +201,8 @@ namespace data
void DisableV4 (); void DisableV4 ();
void EnableMesh (); void EnableMesh ();
void DisableMesh (); void DisableMesh ();
bool IsCompatible (const RouterInfo& other) const { return m_SupportedTransports & other.m_SupportedTransports; }; bool IsCompatible (const RouterInfo& other) const { return m_SupportedTransports & other.m_SupportedTransports; };
bool IsReachableFrom (const RouterInfo& other) const;
bool HasValidAddresses () const { return m_SupportedTransports; }; bool HasValidAddresses () const { return m_SupportedTransports; };
bool UsesIntroducer () const; bool UsesIntroducer () const;
bool IsHidden () const { return m_Caps & eHidden; }; bool IsHidden () const { return m_Caps & eHidden; };

View File

@ -653,6 +653,14 @@ namespace transport
return ret; return ret;
} }
void SSUServer::RescheduleIntroducersUpdateTimer ()
{
m_IntroducersUpdateTimer.cancel ();
m_IntroducersUpdateTimer.expires_from_now (boost::posix_time::seconds(SSU_KEEP_ALIVE_INTERVAL/2));
m_IntroducersUpdateTimer.async_wait (std::bind (&SSUServer::HandleIntroducersUpdateTimer,
this, std::placeholders::_1));
}
void SSUServer::ScheduleIntroducersUpdateTimer () void SSUServer::ScheduleIntroducersUpdateTimer ()
{ {
m_IntroducersUpdateTimer.expires_from_now (boost::posix_time::seconds(SSU_KEEP_ALIVE_INTERVAL)); m_IntroducersUpdateTimer.expires_from_now (boost::posix_time::seconds(SSU_KEEP_ALIVE_INTERVAL));
@ -671,7 +679,12 @@ namespace transport
ScheduleIntroducersUpdateTimer (); ScheduleIntroducersUpdateTimer ();
return; return;
} }
if (i2p::context.GetStatus () == eRouterStatusOK) return; // we don't need introducers anymore if (i2p::context.GetStatus () != eRouterStatusFirewalled)
{
// we don't need introducers
m_Introducers.clear ();
return;
}
// we are firewalled // we are firewalled
if (!i2p::context.IsUnreachable ()) i2p::context.SetUnreachable (); if (!i2p::context.IsUnreachable ()) i2p::context.SetUnreachable ();
std::list<boost::asio::ip::udp::endpoint> newList; std::list<boost::asio::ip::udp::endpoint> newList;
@ -712,9 +725,24 @@ namespace transport
m_Introducers = newList; m_Introducers = newList;
if (m_Introducers.size () < SSU_MAX_NUM_INTRODUCERS) if (m_Introducers.size () < SSU_MAX_NUM_INTRODUCERS)
{ {
auto introducer = i2p::data::netdb.GetRandomIntroducer (); std::set<std::shared_ptr<const i2p::data::RouterInfo> > requested;
if (introducer) for (auto i = m_Introducers.size (); i < SSU_MAX_NUM_INTRODUCERS; i++)
CreateSession (introducer); {
auto introducer = i2p::data::netdb.GetRandomIntroducer ();
if (introducer && !requested.count (introducer)) // not requested already
{
auto address = introducer->GetSSUAddress (true); // v4
if (address && !address->host.is_unspecified ())
{
boost::asio::ip::udp::endpoint ep (address->host, address->port);
if (std::find (m_Introducers.begin (), m_Introducers.end (), ep) == m_Introducers.end ()) // not connected yet
{
CreateDirectSession (introducer, ep, false);
requested.insert (introducer);
}
}
}
}
} }
ScheduleIntroducersUpdateTimer (); ScheduleIntroducersUpdateTimer ();
} }

View File

@ -70,7 +70,8 @@ namespace transport
void AddRelay (uint32_t tag, std::shared_ptr<SSUSession> relay); void AddRelay (uint32_t tag, std::shared_ptr<SSUSession> relay);
void RemoveRelay (uint32_t tag); void RemoveRelay (uint32_t tag);
std::shared_ptr<SSUSession> FindRelaySession (uint32_t tag); std::shared_ptr<SSUSession> FindRelaySession (uint32_t tag);
void RescheduleIntroducersUpdateTimer ();
void NewPeerTest (uint32_t nonce, PeerTestParticipant role, std::shared_ptr<SSUSession> session = nullptr); void NewPeerTest (uint32_t nonce, PeerTestParticipant role, std::shared_ptr<SSUSession> session = nullptr);
PeerTestParticipant GetPeerTestParticipant (uint32_t nonce); PeerTestParticipant GetPeerTestParticipant (uint32_t nonce);
std::shared_ptr<SSUSession> GetPeerTestSession (uint32_t nonce); std::shared_ptr<SSUSession> GetPeerTestSession (uint32_t nonce);

View File

@ -685,7 +685,12 @@ namespace transport
LogPrint (eLogInfo, "SSU: Our external address is ", ourIP.to_string (), ":", ourPort); LogPrint (eLogInfo, "SSU: Our external address is ", ourIP.to_string (), ":", ourPort);
i2p::context.UpdateAddress (ourIP); i2p::context.UpdateAddress (ourIP);
if (ourPort != m_Server.GetPort ()) if (ourPort != m_Server.GetPort ())
i2p::context.SetError (eRouterErrorSymmetricNAT); {
if (i2p::context.GetStatus () == eRouterStatusTesting)
i2p::context.SetError (eRouterErrorSymmetricNAT);
}
else if (i2p::context.GetStatus () == eRouterStatusError && i2p::context.GetError () == eRouterErrorSymmetricNAT)
i2p::context.SetStatus (eRouterStatusTesting);
uint32_t nonce = bufbe32toh (buf); uint32_t nonce = bufbe32toh (buf);
buf += 4; // nonce buf += 4; // nonce
auto it = m_RelayRequests.find (nonce); auto it = m_RelayRequests.find (nonce);
@ -999,7 +1004,10 @@ namespace transport
{ {
LogPrint (eLogDebug, "SSU: peer test from Bob. We are Alice"); LogPrint (eLogDebug, "SSU: peer test from Bob. We are Alice");
if (i2p::context.GetStatus () == eRouterStatusTesting) // still not OK if (i2p::context.GetStatus () == eRouterStatusTesting) // still not OK
{
i2p::context.SetStatus (eRouterStatusFirewalled); i2p::context.SetStatus (eRouterStatusFirewalled);
m_Server.RescheduleIntroducersUpdateTimer ();
}
} }
else else
{ {

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2020, The PurpleI2P Project * Copyright (c) 2013-2021, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@ -174,9 +174,9 @@ namespace transport
std::string ntcp2proxy; i2p::config::GetOption("ntcp2.proxy", ntcp2proxy); std::string ntcp2proxy; i2p::config::GetOption("ntcp2.proxy", ntcp2proxy);
i2p::http::URL proxyurl; i2p::http::URL proxyurl;
// create NTCP2. TODO: move to acceptor // create NTCP2. TODO: move to acceptor
if (enableNTCP2) if (enableNTCP2 || i2p::context.SupportsMesh ())
{ {
if(!ntcp2proxy.empty()) if(!ntcp2proxy.empty() && enableNTCP2)
{ {
if(proxyurl.parse(ntcp2proxy)) if(proxyurl.parse(ntcp2proxy))
{ {
@ -188,14 +188,14 @@ namespace transport
if (proxyurl.schema == "http") if (proxyurl.schema == "http")
proxytype = NTCP2Server::eHTTPProxy; proxytype = NTCP2Server::eHTTPProxy;
m_NTCP2Server->UseProxy(proxytype, proxyurl.host, proxyurl.port); m_NTCP2Server->UseProxy(proxytype, proxyurl.host, proxyurl.port, proxyurl.user, proxyurl.pass);
i2p::context.SetStatus (eRouterStatusProxy);
} }
else else
LogPrint(eLogError, "Transports: unsupported NTCP2 proxy URL ", ntcp2proxy); LogPrint(eLogError, "Transports: unsupported NTCP2 proxy URL ", ntcp2proxy);
} }
else else
LogPrint(eLogError, "Transports: invalid NTCP2 proxy url ", ntcp2proxy); LogPrint(eLogError, "Transports: invalid NTCP2 proxy url ", ntcp2proxy);
return;
} }
else else
m_NTCP2Server = new NTCP2Server (); m_NTCP2Server = new NTCP2Server ();
@ -503,7 +503,7 @@ namespace transport
} }
peer.numAttempts++; peer.numAttempts++;
} }
if (address) if (address && address->IsReachableSSU ())
{ {
m_SSUServer->CreateSession (peer.router, address); m_SSUServer->CreateSession (peer.router, address);
return true; return true;

View File

@ -698,7 +698,7 @@ namespace tunnel
auto inboundTunnel = GetNextInboundTunnel (); auto inboundTunnel = GetNextInboundTunnel ();
auto router = i2p::transport::transports.RoutesRestricted() ? auto router = i2p::transport::transports.RoutesRestricted() ?
i2p::transport::transports.GetRestrictedPeer() : i2p::transport::transports.GetRestrictedPeer() :
i2p::data::netdb.GetRandomRouter (); i2p::data::netdb.GetRandomRouter (i2p::context.GetSharedRouterInfo (), false); // reachable by us
if (!inboundTunnel || !router) return; if (!inboundTunnel || !router) return;
LogPrint (eLogDebug, "Tunnel: creating one hop outbound tunnel"); LogPrint (eLogDebug, "Tunnel: creating one hop outbound tunnel");
CreateTunnel<OutboundTunnel> ( CreateTunnel<OutboundTunnel> (
@ -771,7 +771,8 @@ namespace tunnel
// trying to create one more inbound tunnel // trying to create one more inbound tunnel
auto router = i2p::transport::transports.RoutesRestricted() ? auto router = i2p::transport::transports.RoutesRestricted() ?
i2p::transport::transports.GetRestrictedPeer() : i2p::transport::transports.GetRestrictedPeer() :
i2p::data::netdb.GetRandomRouter (); // should be reachable by us because we send build request directly
i2p::data::netdb.GetRandomRouter (i2p::context.GetSharedRouterInfo (), false);
if (!router) { if (!router) {
LogPrint (eLogWarning, "Tunnel: can't find any router, skip creating tunnel"); LogPrint (eLogWarning, "Tunnel: can't find any router, skip creating tunnel");
return; return;

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2020, The PurpleI2P Project * Copyright (c) 2013-2021, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@ -393,14 +393,14 @@ namespace tunnel
} }
} }
std::shared_ptr<const i2p::data::RouterInfo> TunnelPool::SelectNextHop (std::shared_ptr<const i2p::data::RouterInfo> prevHop) const std::shared_ptr<const i2p::data::RouterInfo> TunnelPool::SelectNextHop (std::shared_ptr<const i2p::data::RouterInfo> prevHop, bool reverse) const
{ {
bool isExploratory = (i2p::tunnel::tunnels.GetExploratoryPool () == shared_from_this ()); bool isExploratory = (i2p::tunnel::tunnels.GetExploratoryPool () == shared_from_this ());
auto hop = isExploratory ? i2p::data::netdb.GetRandomRouter (prevHop): auto hop = isExploratory ? i2p::data::netdb.GetRandomRouter (prevHop, reverse):
i2p::data::netdb.GetHighBandwidthRandomRouter (prevHop); i2p::data::netdb.GetHighBandwidthRandomRouter (prevHop, reverse);
if (!hop || hop->GetProfile ()->IsBad ()) if (!hop || hop->GetProfile ()->IsBad ())
hop = i2p::data::netdb.GetRandomRouter (prevHop); hop = i2p::data::netdb.GetRandomRouter (prevHop, reverse);
return hop; return hop;
} }
@ -419,7 +419,7 @@ namespace tunnel
{ {
auto r = i2p::transport::transports.GetRandomPeer (); auto r = i2p::transport::transports.GetRandomPeer ();
if (r && !r->GetProfile ()->IsBad () && if (r && !r->GetProfile ()->IsBad () &&
(numHops > 1 || !inbound || r->IsReachable ())) // first must be reachable (numHops > 1 || (!inbound && r->IsV4 ()) || r->IsReachable ())) // first inbound must be reachable
{ {
prevHop = r; prevHop = r;
peers.push_back (r->GetRouterIdentity ()); peers.push_back (r->GetRouterIdentity ());
@ -429,16 +429,22 @@ namespace tunnel
for(int i = 0; i < numHops; i++ ) for(int i = 0; i < numHops; i++ )
{ {
auto hop = nextHop (prevHop); auto hop = nextHop (prevHop, inbound);
if (!hop && !i) // if no suitable peer found for first hop, try already connected
{
LogPrint (eLogInfo, "Tunnels: Can't select first hop for a tunnel. Trying already connected");
hop = i2p::transport::transports.GetRandomPeer ();
}
if (!hop) if (!hop)
{ {
LogPrint (eLogError, "Tunnels: Can't select next hop for ", prevHop->GetIdentHashBase64 ()); LogPrint (eLogError, "Tunnels: Can't select next hop for ", prevHop->GetIdentHashBase64 ());
return false; return false;
} }
if (inbound && (i == numHops - 1) && !hop->IsReachable ()) if ((i == numHops - 1) &&
((inbound && !hop->IsReachable ()) || // IBGW is not reachable
(!inbound && !hop->IsV4 ()))) // OBEP is not ipv4
{ {
// if first is not reachable try again auto hop1 = nextHop (prevHop, true);
auto hop1 = nextHop (prevHop);
if (hop1) hop = hop1; if (hop1) hop = hop1;
} }
prevHop = hop; prevHop = hop;
@ -460,7 +466,7 @@ namespace tunnel
} }
// explicit peers in use // explicit peers in use
if (m_ExplicitPeers) return SelectExplicitPeers (peers, isInbound); if (m_ExplicitPeers) return SelectExplicitPeers (peers, isInbound);
return StandardSelectPeers(peers, numHops, isInbound, std::bind(&TunnelPool::SelectNextHop, this, std::placeholders::_1)); return StandardSelectPeers(peers, numHops, isInbound, std::bind(&TunnelPool::SelectNextHop, this, std::placeholders::_1, std::placeholders::_2));
} }
bool TunnelPool::SelectExplicitPeers (std::vector<std::shared_ptr<const i2p::data::IdentityEx> >& peers, bool isInbound) bool TunnelPool::SelectExplicitPeers (std::vector<std::shared_ptr<const i2p::data::IdentityEx> >& peers, bool isInbound)

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2020, The PurpleI2P Project * Copyright (c) 2013-2021, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@ -46,7 +46,7 @@ namespace tunnel
}; };
typedef std::function<std::shared_ptr<const i2p::data::RouterInfo>(std::shared_ptr<const i2p::data::RouterInfo>)> SelectHopFunc; typedef std::function<std::shared_ptr<const i2p::data::RouterInfo>(std::shared_ptr<const i2p::data::RouterInfo>, bool)> SelectHopFunc;
// standard peer selection algorithm // standard peer selection algorithm
bool StandardSelectPeers(Path & path, int hops, bool inbound, SelectHopFunc nextHop); bool StandardSelectPeers(Path & path, int hops, bool inbound, SelectHopFunc nextHop);
@ -104,7 +104,7 @@ namespace tunnel
std::shared_ptr<OutboundTunnel> GetLowestLatencyOutboundTunnel(std::shared_ptr<OutboundTunnel> exclude = nullptr) const; std::shared_ptr<OutboundTunnel> GetLowestLatencyOutboundTunnel(std::shared_ptr<OutboundTunnel> exclude = nullptr) const;
// for overriding tunnel peer selection // for overriding tunnel peer selection
std::shared_ptr<const i2p::data::RouterInfo> SelectNextHop (std::shared_ptr<const i2p::data::RouterInfo> prevHop) const; std::shared_ptr<const i2p::data::RouterInfo> SelectNextHop (std::shared_ptr<const i2p::data::RouterInfo> prevHop, bool reverse) const;
private: private:

View File

@ -67,8 +67,12 @@ int inet_pton_xp (int af, const char *src, void *dst)
} }
#else /* !_WIN32 => UNIX */ #else /* !_WIN32 => UNIX */
#include <sys/types.h> #include <sys/types.h>
#ifdef ANDROID
#include "ifaddrs.h"
#else
#include <ifaddrs.h> #include <ifaddrs.h>
#endif #endif
#endif
#define address_pair_v4(a,b) { boost::asio::ip::address_v4::from_string (a).to_ulong (), boost::asio::ip::address_v4::from_string (b).to_ulong () } #define address_pair_v4(a,b) { boost::asio::ip::address_v4::from_string (a).to_ulong (), boost::asio::ip::address_v4::from_string (b).to_ulong () }
#define address_pair_v6(a,b) { boost::asio::ip::address_v6::from_string (a).to_bytes (), boost::asio::ip::address_v6::from_string (b).to_bytes () } #define address_pair_v6(a,b) { boost::asio::ip::address_v6::from_string (a).to_bytes (), boost::asio::ip::address_v6::from_string (b).to_bytes () }
@ -380,11 +384,11 @@ namespace net
return boost::asio::ip::address::from_string("127.0.0.1"); return boost::asio::ip::address::from_string("127.0.0.1");
#else #else
int af = (ipv6 ? AF_INET6 : AF_INET); int af = (ipv6 ? AF_INET6 : AF_INET);
ifaddrs * addrs = nullptr; ifaddrs *addrs, *cur = nullptr;
if(getifaddrs(&addrs) == 0) if(getifaddrs(&addrs) == 0)
{ {
// got ifaddrs // got ifaddrs
ifaddrs * cur = addrs; cur = addrs;
while(cur) while(cur)
{ {
std::string cur_ifname(cur->ifa_name); std::string cur_ifname(cur->ifa_name);
@ -431,10 +435,7 @@ namespace net
boost::asio::ip::address_v6 GetYggdrasilAddress () boost::asio::ip::address_v6 GetYggdrasilAddress ()
{ {
#if defined(ANDROID) #if defined(_WIN32)
// TODO: implement
return boost::asio::ip::address_v6 ();
#elif defined(_WIN32)
ULONG outBufLen = 0; ULONG outBufLen = 0;
PIP_ADAPTER_ADDRESSES pAddresses = nullptr; PIP_ADAPTER_ADDRESSES pAddresses = nullptr;
PIP_ADAPTER_ADDRESSES pCurrAddresses = nullptr; PIP_ADAPTER_ADDRESSES pCurrAddresses = nullptr;
@ -482,11 +483,11 @@ namespace net
FREE(pAddresses); FREE(pAddresses);
return boost::asio::ip::address_v6 (); return boost::asio::ip::address_v6 ();
#else #else
ifaddrs * addrs = nullptr; ifaddrs *addrs, *cur = nullptr;
auto err = getifaddrs(&addrs); auto err = getifaddrs(&addrs);
if (!err) if (!err)
{ {
ifaddrs * cur = addrs; cur = addrs;
while(cur) while(cur)
{ {
if (cur->ifa_addr && cur->ifa_addr->sa_family == AF_INET6) if (cur->ifa_addr && cur->ifa_addr->sa_family == AF_INET6)

View File

@ -16,7 +16,7 @@
#define MAKE_VERSION_NUMBER(a,b,c) ((a*100+b)*100+c) #define MAKE_VERSION_NUMBER(a,b,c) ((a*100+b)*100+c)
#define I2PD_VERSION_MAJOR 2 #define I2PD_VERSION_MAJOR 2
#define I2PD_VERSION_MINOR 36 #define I2PD_VERSION_MINOR 37
#define I2PD_VERSION_MICRO 0 #define I2PD_VERSION_MICRO 0
#define I2PD_VERSION_PATCH 0 #define I2PD_VERSION_PATCH 0
#define I2PD_VERSION MAKE_VERSION(I2PD_VERSION_MAJOR, I2PD_VERSION_MINOR, I2PD_VERSION_MICRO) #define I2PD_VERSION MAKE_VERSION(I2PD_VERSION_MAJOR, I2PD_VERSION_MINOR, I2PD_VERSION_MICRO)

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2020, The PurpleI2P Project * Copyright (c) 2013-2021, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@ -433,13 +433,13 @@ namespace proxy {
if (m_ProxyURL.is_i2p()) if (m_ProxyURL.is_i2p())
{ {
m_ClientRequest.uri = origURI; m_ClientRequest.uri = origURI;
if (!m_ProxyURL.user.empty () || !m_ProxyURL.pass.empty ()) auto auth = i2p::http::CreateBasicAuthorizationString (m_ProxyURL.user, m_ProxyURL.pass);
if (!auth.empty ())
{ {
// remove existing authorization if any // remove existing authorization if any
m_ClientRequest.RemoveHeader("Proxy-"); m_ClientRequest.RemoveHeader("Proxy-");
// add own http proxy authorization // add own http proxy authorization
std::string s = "Basic " + i2p::data::ToBase64Standard (m_ProxyURL.user + ":" + m_ProxyURL.pass); m_ClientRequest.AddHeader("Proxy-Authorization", auth);
m_ClientRequest.AddHeader("Proxy-Authorization", s);
} }
m_send_buf = m_ClientRequest.to_string(); m_send_buf = m_ClientRequest.to_string();
m_recv_buf.erase(0, m_req_len); m_recv_buf.erase(0, m_req_len);

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2020, The PurpleI2P Project * Copyright (c) 2013-2021, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@ -72,7 +72,8 @@ namespace client
bool MatchedTunnelDestination::SelectPeers(i2p::tunnel::Path & path, int hops, bool inbound) bool MatchedTunnelDestination::SelectPeers(i2p::tunnel::Path & path, int hops, bool inbound)
{ {
auto pool = GetTunnelPool(); auto pool = GetTunnelPool();
if(!i2p::tunnel::StandardSelectPeers(path, hops, inbound, std::bind(&i2p::tunnel::TunnelPool::SelectNextHop, pool, std::placeholders::_1))) if(!i2p::tunnel::StandardSelectPeers(path, hops, inbound,
std::bind(&i2p::tunnel::TunnelPool::SelectNextHop, pool, std::placeholders::_1, std::placeholders::_2)))
return false; return false;
// more here for outbound tunnels // more here for outbound tunnels
if(!inbound && m_RemoteLeaseSet) if(!inbound && m_RemoteLeaseSet)