mirror of
https://github.com/PurpleI2P/i2pd.git
synced 2025-03-07 06:09:42 +00:00
Compare commits
156 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f22eaa6db5 | ||
|
|
e37244fa0d | ||
|
|
c359c6e634 | ||
|
|
d299cbaabd | ||
|
|
2b22bfadbc | ||
|
|
baec22610e | ||
|
|
43b587636b | ||
|
|
c6cdb26f47 | ||
|
|
1285e30b3e | ||
|
|
a8e1cd9a13 | ||
|
|
d6f5640685 | ||
|
|
79dbf2a43e | ||
|
|
5ad4c2a65e | ||
|
|
fffa550bb0 | ||
|
|
0b9cb4e75b | ||
|
|
7f143a7f23 | ||
|
|
d8d8a68814 | ||
|
|
4018cf9d76 | ||
|
|
bd33ac202f | ||
|
|
e091eba831 | ||
|
|
4a0dbec4fb | ||
|
|
90dee900f0 | ||
|
|
94555b9c43 | ||
|
|
db93a7315f | ||
|
|
7a19533380 | ||
|
|
9d79b26506 | ||
|
|
b43a9cc80d | ||
|
|
b5618af308 | ||
|
|
9c8c3b9174 | ||
|
|
01e591b261 | ||
|
|
060e30d283 | ||
|
|
ad019da553 | ||
|
|
69afd3a1da | ||
|
|
7978adc577 | ||
|
|
ca77ca6ef0 | ||
|
|
d5b61ed544 | ||
|
|
5edb256990 | ||
|
|
74d0c04314 | ||
|
|
39d4464be0 | ||
|
|
be48dc6e87 | ||
|
|
2783337284 | ||
|
|
727743979c | ||
|
|
4543e14c57 | ||
|
|
83fc1b0b8e | ||
|
|
df858d9143 | ||
|
|
ac47c9c673 | ||
|
|
b9a2d5df02 | ||
|
|
3e873f88c9 | ||
|
|
277cef5ec4 | ||
|
|
5c9b478e46 | ||
|
|
ff89edf127 | ||
|
|
2cc9791bf2 | ||
|
|
67b32005f6 | ||
|
|
0f166973ca | ||
|
|
4f3333c841 | ||
|
|
bea384abea | ||
|
|
43033695f6 | ||
|
|
51ef7ef61c | ||
|
|
823b499a02 | ||
|
|
bb5ed0b40c | ||
|
|
94ca2514af | ||
|
|
5412352dec | ||
|
|
c94e8c7df4 | ||
|
|
094541caa6 | ||
|
|
8c59977e34 | ||
|
|
881bca6ae3 | ||
|
|
22865f8ee4 | ||
|
|
f3b728d828 | ||
|
|
bd7328345f | ||
|
|
25eae3c116 | ||
|
|
5cca5472e6 | ||
|
|
8462d382f4 | ||
|
|
2b0d18a6d7 | ||
|
|
edf3b7e2fc | ||
|
|
167d3a0e3c | ||
|
|
86415bc61f | ||
|
|
a6ea37a21e | ||
|
|
3695aa924b | ||
|
|
9e050d1a23 | ||
|
|
34eee2fc26 | ||
|
|
ac10f3055d | ||
|
|
991b74f036 | ||
|
|
589049ef0f | ||
|
|
6b0c7c2313 | ||
|
|
a9c7d0d598 | ||
|
|
ef1dfb153c | ||
|
|
ff9ee5873f | ||
|
|
a7b56bbbb7 | ||
|
|
820a365474 | ||
|
|
1d5d06f731 | ||
|
|
43d458cf72 | ||
|
|
436a3e7f54 | ||
|
|
7015bad905 | ||
|
|
cf8665748b | ||
|
|
1b8da90cbb | ||
|
|
6012585067 | ||
|
|
f162876600 | ||
|
|
6555ae5b0a | ||
|
|
f5af059ef4 | ||
|
|
cb8651ec68 | ||
|
|
7c0b0a4e3e | ||
|
|
880d1a7ccd | ||
|
|
744b25190a | ||
|
|
3792bb4928 | ||
|
|
9049902ced | ||
|
|
5f93dc72fd | ||
|
|
09dadd7e01 | ||
|
|
60b92f98db | ||
|
|
97f315d488 | ||
|
|
f3676d7f18 | ||
|
|
742dbdb68a | ||
|
|
2d59c968ca | ||
|
|
ad22247c9e | ||
|
|
f38920c338 | ||
|
|
8f90b21a5d | ||
|
|
ff0e6813c6 | ||
|
|
fa5e4d57fd | ||
|
|
876973f071 | ||
|
|
b994af9209 | ||
|
|
1f6cde652e | ||
|
|
3bf6db1c08 | ||
|
|
e70ffc9d7c | ||
|
|
065cfe3b9d | ||
|
|
def9873a70 | ||
|
|
618aa26454 | ||
|
|
924a7bc533 | ||
|
|
ef85277a1b | ||
|
|
876375f2c3 | ||
|
|
f70ee480ba | ||
|
|
6d88c3ab05 | ||
|
|
57c969b0ed | ||
|
|
ae58a7007b | ||
|
|
11c924bbe7 | ||
|
|
8bab4f60ef | ||
|
|
bef9a54f4a | ||
|
|
288b19c3f7 | ||
|
|
40f7e9d33e | ||
|
|
fab53dda66 | ||
|
|
a4e8bf9857 | ||
|
|
2cdf84cdab | ||
|
|
fbe83f729d | ||
|
|
4371a084ec | ||
|
|
d13f58088a | ||
|
|
f75bef7c03 | ||
|
|
3d7e93a688 | ||
|
|
a4dda304d2 | ||
|
|
124c3ef2d7 | ||
|
|
c3a2fca76a | ||
|
|
b60ebfe1c6 | ||
|
|
1d7639b3f4 | ||
|
|
2d972752ff | ||
|
|
616f0b2a21 | ||
|
|
94659ba890 | ||
|
|
d65bc068de | ||
|
|
1ca0354cf2 | ||
|
|
b1fcd4d27b |
4
.github/workflows/build-windows.yml
vendored
4
.github/workflows/build-windows.yml
vendored
@@ -29,3 +29,7 @@ jobs:
|
||||
run: |
|
||||
mkdir -p obj/Win32 obj/libi2pd obj/libi2pd_client obj/daemon
|
||||
make USE_UPNP=yes DEBUG=no -j3
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
path: i2pd.exe
|
||||
|
||||
0
.gitmodules
vendored
0
.gitmodules
vendored
59
ChangeLog
59
ChangeLog
@@ -1,6 +1,65 @@
|
||||
# for this file format description,
|
||||
# see https://github.com/olivierlacan/keep-a-changelog
|
||||
|
||||
## [2.38.0] - 2021-03-17
|
||||
### Added
|
||||
- Publish ipv6 introducers
|
||||
- Bind ipv6 or yggdrasil NTCP2 acceptor to specified address
|
||||
- Support .b32.i2p addresses and hostnames for SAM STREAM CREATE
|
||||
- ipv6 peer tests
|
||||
- Publish iexp param for introducers
|
||||
- Show ipv6 network status on the webconsole
|
||||
- EdDSA signing keys can also be blinded
|
||||
- Show router version on the webconsole
|
||||
### Changed
|
||||
- Rekey of all routers but floodfills to ECIES
|
||||
- Increased number of precalculated x25519 keys to 15
|
||||
- Don't publish LeaseSet without inbound tunnels
|
||||
- Reseed from compatible address(ipv4 or ipv6)
|
||||
- Recongnize v4 and v6 SSU addresses without host
|
||||
- Inbound tunnel gateway must be ipv4 compatible
|
||||
- Don't select next introducers from existing sessions
|
||||
- Set X bandwidth for floodfill by default
|
||||
### Fixed
|
||||
- Incoming ECIES-x25519 session doesn't send updated LeaseSet
|
||||
- Unique local address for server tunnels
|
||||
- Race condition for LeaseSet creation in I2CP
|
||||
- Relay tag for ipv6 introducer
|
||||
- Already expired introducers
|
||||
- Find connected router for first peer in tunnel
|
||||
- Failed outgoing ECIES-x25519 session's tagset stays forever
|
||||
- Yggdrasil address disappears if router becomes unreachable through ipv6
|
||||
- Ignore SSU address/introducers if port is not specified
|
||||
- Check identity and signature length for SSU SessionConfirmed
|
||||
|
||||
## [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
|
||||
### Added
|
||||
- Encrypted lookup and publications to ECIES-x25519 floodfiils
|
||||
|
||||
4
Makefile
4
Makefile
@@ -39,7 +39,7 @@ else ifneq (, $(findstring freebsd, $(SYS))$(findstring openbsd, $(SYS)))
|
||||
DAEMON_SRC += $(DAEMON_SRC_DIR)/UnixDaemon.cpp
|
||||
include Makefile.bsd
|
||||
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
|
||||
else # not supported
|
||||
$(error Not supported platform)
|
||||
@@ -66,6 +66,7 @@ mk_obj_dir:
|
||||
@mkdir -p obj/$(DAEMON_SRC_DIR)
|
||||
|
||||
api: mk_obj_dir $(SHLIB) $(ARLIB)
|
||||
client: mk_obj_dir $(SHLIB_CLIENT) $(ARLIB_CLIENT)
|
||||
api_client: mk_obj_dir $(SHLIB) $(ARLIB) $(SHLIB_CLIENT) $(ARLIB_CLIENT)
|
||||
|
||||
## NOTE: The NEEDED_CXXFLAGS are here so that CXXFLAGS can be specified at build time
|
||||
@@ -128,6 +129,7 @@ doxygen:
|
||||
.PHONY: last-dist
|
||||
.PHONY: api
|
||||
.PHONY: api_client
|
||||
.PHONY: client
|
||||
.PHONY: mk_obj_dir
|
||||
.PHONY: install
|
||||
.PHONY: strip
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
#include "Log.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include "Win32Service.h"
|
||||
#ifdef WIN32_APP
|
||||
#include <windows.h>
|
||||
#include "Win32App.h"
|
||||
@@ -35,45 +34,11 @@ namespace util
|
||||
i2p::log::SetThrowFunction ([](const std::string& s)
|
||||
{
|
||||
MessageBox(0, TEXT(s.c_str ()), TEXT("i2pd"), MB_ICONERROR | MB_TASKMODAL | MB_OK );
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
if (!Daemon_Singleton::init(argc, argv))
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -86,9 +51,6 @@ namespace util
|
||||
setlocale(LC_TIME, "C");
|
||||
#ifdef WIN32_APP
|
||||
if (!i2p::win32::StartWin32App ()) return false;
|
||||
|
||||
// override log
|
||||
i2p::config::SetOption("log", std::string ("file"));
|
||||
#endif
|
||||
bool ret = Daemon_Singleton::start();
|
||||
if (ret && i2p::log::Logger().GetLogType() == eLogFile)
|
||||
|
||||
@@ -142,25 +142,47 @@ namespace win32
|
||||
s << bytes << " Bytes\n";
|
||||
}
|
||||
|
||||
static void ShowNetworkStatus (std::stringstream& s, RouterStatus status)
|
||||
{
|
||||
switch (status)
|
||||
{
|
||||
case eRouterStatusOK: s << "OK"; break;
|
||||
case eRouterStatusTesting: s << "Test"; break;
|
||||
case eRouterStatusFirewalled: s << "FW"; break;
|
||||
case eRouterStatusUnknown: s << "Unk"; break;
|
||||
case eRouterStatusProxy: s << "Proxy"; break;
|
||||
case eRouterStatusMesh: s << "Mesh"; break;
|
||||
case eRouterStatusError:
|
||||
{
|
||||
s << "Err";
|
||||
switch (i2p::context.GetError ())
|
||||
{
|
||||
case eRouterErrorClockSkew:
|
||||
s << " - Clock skew";
|
||||
break;
|
||||
case eRouterErrorOffline:
|
||||
s << " - Offline";
|
||||
break;
|
||||
case eRouterErrorSymmetricNAT:
|
||||
s << " - Symmetric NAT";
|
||||
break;
|
||||
default: ;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: s << "Unk";
|
||||
}
|
||||
}
|
||||
|
||||
static void PrintMainWindowText (std::stringstream& s)
|
||||
{
|
||||
s << "\n";
|
||||
s << "Status: ";
|
||||
switch (i2p::context.GetStatus())
|
||||
ShowNetworkStatus (s, i2p::context.GetStatus ());
|
||||
if (i2p::context.SupportsV6 ())
|
||||
{
|
||||
case eRouterStatusOK: s << "OK"; break;
|
||||
case eRouterStatusTesting: s << "Testing"; break;
|
||||
case eRouterStatusFirewalled: s << "Firewalled"; break;
|
||||
case eRouterStatusError:
|
||||
{
|
||||
switch (i2p::context.GetError())
|
||||
{
|
||||
case eRouterErrorClockSkew: s << "Clock skew"; break;
|
||||
default: s << "Error";
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: s << "Unknown";
|
||||
s << " / ";
|
||||
ShowNetworkStatus (s, i2p::context.GetStatusV6 ());
|
||||
}
|
||||
s << "; ";
|
||||
s << "Success Rate: " << i2p::tunnel::tunnels.GetTunnelCreationSuccessRate() << "%\n";
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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__
|
||||
@@ -1,4 +1,4 @@
|
||||
version: 2.36.0.{build}
|
||||
version: 2.38.0.{build}
|
||||
pull_requests:
|
||||
do_not_increment_build_number: true
|
||||
branches:
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
|
||||
## Logging configuration section
|
||||
## By default logs go to stdout with level 'info' and higher
|
||||
## For Windows OS by default logs go to file with level 'warn' and higher
|
||||
##
|
||||
## Logs destination (valid values: stdout, file, syslog)
|
||||
## * stdout - print log entries to stdout
|
||||
@@ -34,14 +35,30 @@
|
||||
## Write full CLF-formatted date and time to log (default: write only time)
|
||||
# logclftime = true
|
||||
|
||||
## Daemon mode. Router will go to background after start
|
||||
## Daemon mode. Router will go to background after start. Ignored on Windows
|
||||
# daemon = true
|
||||
|
||||
## Specify a family, router belongs to (default - none)
|
||||
# family =
|
||||
|
||||
## External IP address to listen for connections
|
||||
## Network interface to bind to
|
||||
## Updates address4/6 options if they are not set
|
||||
# ifname =
|
||||
## You can specify different interfaces for IPv4 and IPv6
|
||||
# ifname4 =
|
||||
# ifname6 =
|
||||
|
||||
## Local address to bind transport sockets to
|
||||
## Overrides host option if:
|
||||
## For ipv4: if ipv4 = true and nat = false
|
||||
## For ipv6: if 'host' is not set or ipv4 = true
|
||||
# address4 =
|
||||
# address6 =
|
||||
|
||||
## External IPv4 or IPv6 address to listen for connections
|
||||
## By default i2pd sets IP automatically
|
||||
## Sets published NTCP2v4/SSUv4 address to 'host' value if nat = true
|
||||
## Sets published NTCP2v6/SSUv6 address to 'host' value if ipv4 = false
|
||||
# host = 1.2.3.4
|
||||
|
||||
## Port to listen for connections
|
||||
@@ -54,23 +71,9 @@ ipv4 = true
|
||||
## Enable communication through ipv6
|
||||
ipv6 = false
|
||||
|
||||
## Network interface to bind to
|
||||
# ifname =
|
||||
## You can specify different interfaces for IPv4 and IPv6
|
||||
# ifname4 =
|
||||
# ifname6 =
|
||||
|
||||
## Enable NTCP transport (default = true)
|
||||
# ntcp = true
|
||||
## If you run i2pd behind a proxy server, you can only use NTCP transport with ntcpproxy option
|
||||
## Should be http://address:port or socks://address:port
|
||||
# ntcpproxy = http://127.0.0.1:8118
|
||||
## Enable SSU transport (default = true)
|
||||
# ssu = true
|
||||
|
||||
## Should we assume we are behind NAT? (false only in MeshNet)
|
||||
# nat = true
|
||||
|
||||
## Bandwidth configuration
|
||||
## L limit bandwidth to 32KBs/sec, O - to 256KBs/sec, P - to 2048KBs/sec,
|
||||
## X - unlimited
|
||||
@@ -84,6 +87,7 @@ ipv6 = false
|
||||
# notransit = true
|
||||
|
||||
## Router will be floodfill
|
||||
## Note: that mode uses much more network connections and CPU!
|
||||
# floodfill = true
|
||||
|
||||
[http]
|
||||
@@ -95,7 +99,7 @@ address = 127.0.0.1
|
||||
port = 7070
|
||||
## Path to web console, default "/"
|
||||
# webroot = /
|
||||
## Uncomment following lines to enable Web Console authentication
|
||||
## Uncomment following lines to enable Web Console authentication
|
||||
# auth = true
|
||||
# user = i2pd
|
||||
# pass = changeme
|
||||
@@ -131,7 +135,7 @@ port = 4447
|
||||
## socksproxy section also accepts I2CP parameters, like "inbound.length" etc.
|
||||
|
||||
[sam]
|
||||
## Uncomment and set to 'true' to enable SAM Bridge
|
||||
## Comment or set to 'false' to disable SAM Bridge
|
||||
enabled = true
|
||||
## Address and port service will listen on
|
||||
# address = 127.0.0.1
|
||||
@@ -171,6 +175,13 @@ enabled = true
|
||||
## Name i2pd appears in UPnP forwardings list (default = I2Pd)
|
||||
# name = I2Pd
|
||||
|
||||
[meshnets]
|
||||
## Enable connectivity over the Yggdrasil network
|
||||
# yggdrasil = false
|
||||
## You can bind address from your Yggdrasil subnet 300::/64
|
||||
## The address must first be added to the network interface
|
||||
# yggaddress =
|
||||
|
||||
[reseed]
|
||||
## Options for bootstrapping into I2P network, aka reseeding
|
||||
## Enable or disable reseed data verification.
|
||||
@@ -178,6 +189,8 @@ verify = true
|
||||
## URLs to request reseed data from, separated by comma
|
||||
## Default: "mainline" I2P Network reseeds
|
||||
# urls = https://reseed.i2p-projekt.de/,https://i2p.mooo.com/netDb/,https://netdb.i2p2.no/
|
||||
## Reseed URLs through the Yggdrasil, separated by comma
|
||||
# yggurls = http://[324:9de3:fea4:f6ac::ace]:7070/
|
||||
## Path to local reseed data file (.su3) for manual reseeding
|
||||
# file = /path/to/i2pseeds.su3
|
||||
## or HTTPS URL to reseed from
|
||||
@@ -195,19 +208,15 @@ verify = true
|
||||
## Default: reg.i2p at "mainline" I2P Network
|
||||
# defaulturl = http://shx5vqsw7usdaunyzr2qmes2fq37oumybpudrd4jjj4e4vk4uusa.b32.i2p/hosts.txt
|
||||
## Optional subscriptions URLs, separated by comma
|
||||
# subscriptions = http://inr.i2p/export/alive-hosts.txt,http://stats.i2p/cgi-bin/newhosts.txt,http://rus.i2p/hosts.txt
|
||||
# subscriptions = http://reg.i2p/hosts.txt,http://identiguy.i2p/hosts.txt,http://stats.i2p/cgi-bin/newhosts.txt,http://rus.i2p/hosts.txt
|
||||
|
||||
[limits]
|
||||
## Maximum active transit sessions (default:2500)
|
||||
# transittunnels = 2500
|
||||
## Limit number of open file descriptors (0 - use system limit)
|
||||
## Limit number of open file descriptors (0 - use system limit)
|
||||
# openfiles = 0
|
||||
## Maximum size of corefile in Kb (0 - use system limit)
|
||||
## Maximum size of corefile in Kb (0 - use system limit)
|
||||
# coresize = 0
|
||||
## Threshold to start probabalistic backoff with ntcp sessions (0 - use system limit)
|
||||
# ntcpsoft = 0
|
||||
## Maximum number of ntcp sessions (0 - use system limit)
|
||||
# ntcphard = 0
|
||||
|
||||
[trust]
|
||||
## Enable explicit trust options. false by default
|
||||
@@ -215,13 +224,13 @@ verify = true
|
||||
## Make direct I2P connections only to routers in specified Family.
|
||||
# family = MyFamily
|
||||
## Make direct I2P connections only to routers specified here. Comma separated list of base64 identities.
|
||||
# routers =
|
||||
# routers =
|
||||
## Should we hide our router from other routers? false by default
|
||||
# hidden = true
|
||||
|
||||
[exploratory]
|
||||
## Exploratory tunnels settings with default values
|
||||
# inbound.length = 2
|
||||
# inbound.length = 2
|
||||
# inbound.quantity = 3
|
||||
# outbound.length = 2
|
||||
# outbound.quantity = 3
|
||||
@@ -229,6 +238,8 @@ verify = true
|
||||
[persist]
|
||||
## Save peer profiles on disk (default: true)
|
||||
# profiles = true
|
||||
## Save full addresses on disk (default: true)
|
||||
# addressbook = true
|
||||
|
||||
[cpuext]
|
||||
## Use CPU AES-NI instructions set when work with cryptography when available (default: true)
|
||||
|
||||
@@ -17,7 +17,12 @@ PIDFile=/run/i2pd/i2pd.pid
|
||||
### Uncomment, if auto restart needed
|
||||
#Restart=on-failure
|
||||
|
||||
KillSignal=SIGQUIT
|
||||
# Use SIGTERM to stop i2pd immediately.
|
||||
# Some cleanup processes can delay stopping, so we set 30 seconds timeout and then SIGKILL i2pd.
|
||||
KillSignal=SIGTERM
|
||||
TimeoutStopSec=30s
|
||||
SendSIGKILL=yes
|
||||
|
||||
# If you have the patience waiting 10 min on restarting/stopping it, uncomment this.
|
||||
# i2pd stops accepting new tunnels and waits ~10 min while old ones do not die.
|
||||
#KillSignal=SIGINT
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
%define git_hash %(git rev-parse HEAD | cut -c -7)
|
||||
|
||||
Name: i2pd-git
|
||||
Version: 2.36.0
|
||||
Version: 2.38.0
|
||||
Release: git%{git_hash}%{?dist}
|
||||
Summary: I2P router written in C++
|
||||
Conflicts: i2pd
|
||||
@@ -137,6 +137,12 @@ getent passwd i2pd >/dev/null || \
|
||||
|
||||
|
||||
%changelog
|
||||
* Mon May 17 2021 orignal <i2porignal@yandex.ru> - 2.38.0
|
||||
- update to 2.38.0
|
||||
|
||||
* 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
|
||||
- update to 2.36.0
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
Name: i2pd
|
||||
Version: 2.36.0
|
||||
Version: 2.38.0
|
||||
Release: 1%{?dist}
|
||||
Summary: I2P router written in C++
|
||||
Conflicts: i2pd-git
|
||||
@@ -135,6 +135,12 @@ getent passwd i2pd >/dev/null || \
|
||||
|
||||
|
||||
%changelog
|
||||
* Mon May 17 2021 orignal <i2porignal@yandex.ru> - 2.38.0
|
||||
- update to 2.38.0
|
||||
|
||||
* 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
|
||||
- update to 2.36.0
|
||||
|
||||
|
||||
@@ -102,8 +102,13 @@ namespace util
|
||||
if (logclftime)
|
||||
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"))
|
||||
logs = "file";
|
||||
#endif
|
||||
|
||||
i2p::log::Logger().SetLogLevel(loglevel);
|
||||
if (logstream) {
|
||||
@@ -123,7 +128,7 @@ namespace util
|
||||
// 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: data directory: ", datadir);
|
||||
|
||||
@@ -137,13 +142,32 @@ namespace util
|
||||
i2p::context.SetNetID (netID);
|
||||
i2p::context.Init ();
|
||||
|
||||
bool ipv6; i2p::config::GetOption("ipv6", ipv6);
|
||||
bool ipv4; i2p::config::GetOption("ipv4", ipv4);
|
||||
bool ipv6; i2p::config::GetOption("ipv6", ipv6);
|
||||
bool ipv4; i2p::config::GetOption("ipv4", ipv4);
|
||||
#ifdef MESHNET
|
||||
// manual override for meshnet
|
||||
ipv4 = false;
|
||||
ipv6 = true;
|
||||
#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);
|
||||
boost::asio::ip::address_v6 yggaddr;
|
||||
if (ygg)
|
||||
@@ -186,10 +210,15 @@ namespace util
|
||||
{
|
||||
bool published; i2p::config::GetOption("ntcp2.published", 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);
|
||||
if (!ntcp2port) ntcp2port = port; // use standard port
|
||||
i2p::context.PublishNTCP2Address (ntcp2port, true); // publish
|
||||
i2p::context.PublishNTCP2Address (ntcp2port, true, ipv4, ipv6, false); // publish
|
||||
if (ipv6)
|
||||
{
|
||||
std::string ipv6Addr; i2p::config::GetOption("ntcp2.addressv6", ipv6Addr);
|
||||
@@ -199,15 +228,16 @@ namespace util
|
||||
}
|
||||
}
|
||||
else
|
||||
i2p::context.PublishNTCP2Address (port, false); // unpublish
|
||||
i2p::context.PublishNTCP2Address (port, false, ipv4, ipv6, false); // unpublish
|
||||
}
|
||||
if (ygg)
|
||||
{
|
||||
if (!ntcp2)
|
||||
i2p::context.PublishNTCP2Address (port, true);
|
||||
i2p::context.PublishNTCP2Address (port, true, false, false, true);
|
||||
i2p::context.UpdateNTCP2V6Address (yggaddr);
|
||||
}
|
||||
|
||||
if (!ipv4 && !ipv6)
|
||||
i2p::context.SetStatus (eRouterStatusMesh);
|
||||
}
|
||||
|
||||
bool transit; i2p::config::GetOption("notransit", transit);
|
||||
i2p::context.SetAcceptsTunnels (!transit);
|
||||
uint16_t transitTunnels; i2p::config::GetOption("limits.transittunnels", transitTunnels);
|
||||
@@ -250,7 +280,7 @@ namespace util
|
||||
else if (isFloodfill)
|
||||
{
|
||||
LogPrint(eLogInfo, "Daemon: floodfill bandwidth set to 'extra'");
|
||||
i2p::context.SetBandwidth (i2p::data::CAPS_FLAG_EXTRA_BANDWIDTH1);
|
||||
i2p::context.SetBandwidth (i2p::data::CAPS_FLAG_EXTRA_BANDWIDTH2);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -343,7 +373,7 @@ namespace util
|
||||
if(!ntcp2) LogPrint(eLogInfo, "Daemon: ntcp2 disabled");
|
||||
|
||||
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())
|
||||
LogPrint(eLogInfo, "Daemon: Transports started");
|
||||
else
|
||||
|
||||
@@ -123,6 +123,7 @@ namespace http {
|
||||
const char HTTP_COMMAND_LOGLEVEL[] = "set_loglevel";
|
||||
const char HTTP_COMMAND_KILLSTREAM[] = "closestream";
|
||||
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_ADDRESS[] = "address";
|
||||
|
||||
@@ -241,17 +242,16 @@ namespace http {
|
||||
s << "<b>ERROR:</b> " << string << "<br>\r\n";
|
||||
}
|
||||
|
||||
void ShowStatus (std::stringstream& s, bool includeHiddenContent, i2p::http::OutputFormatEnum outputFormat)
|
||||
static void ShowNetworkStatus (std::stringstream& s, RouterStatus status)
|
||||
{
|
||||
s << "<b>Uptime:</b> ";
|
||||
ShowUptime(s, i2p::context.GetUptime ());
|
||||
s << "<br>\r\n";
|
||||
s << "<b>Network status:</b> ";
|
||||
switch (i2p::context.GetStatus ())
|
||||
switch (status)
|
||||
{
|
||||
case eRouterStatusOK: s << "OK"; break;
|
||||
case eRouterStatusTesting: s << "Testing"; break;
|
||||
case eRouterStatusFirewalled: s << "Firewalled"; break;
|
||||
case eRouterStatusUnknown: s << "Unknown"; break;
|
||||
case eRouterStatusProxy: s << "Proxy"; break;
|
||||
case eRouterStatusMesh: s << "Mesh"; break;
|
||||
case eRouterStatusError:
|
||||
{
|
||||
s << "Error";
|
||||
@@ -263,13 +263,31 @@ namespace http {
|
||||
case eRouterErrorOffline:
|
||||
s << " - Offline";
|
||||
break;
|
||||
case eRouterErrorSymmetricNAT:
|
||||
s << " - Symmetric NAT";
|
||||
break;
|
||||
default: ;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: s << "Unknown";
|
||||
}
|
||||
}
|
||||
|
||||
void ShowStatus (std::stringstream& s, bool includeHiddenContent, i2p::http::OutputFormatEnum outputFormat)
|
||||
{
|
||||
s << "<b>Uptime:</b> ";
|
||||
ShowUptime(s, i2p::context.GetUptime ());
|
||||
s << "<br>\r\n";
|
||||
s << "<b>Network status:</b> ";
|
||||
ShowNetworkStatus (s, i2p::context.GetStatus ());
|
||||
s << "<br>\r\n";
|
||||
if (i2p::context.SupportsV6 ())
|
||||
{
|
||||
s << "<b>Network status 6:</b> ";
|
||||
ShowNetworkStatus (s, i2p::context.GetStatusV6 ());
|
||||
s << "<br>\r\n";
|
||||
}
|
||||
#if ((!defined(WIN32) && !defined(QT_GUI_LIB) && !defined(ANDROID)) || defined(ANDROID_BINARY))
|
||||
if (auto remains = Daemon.gracefulShutdownInterval) {
|
||||
s << "<b>Stopping in:</b> ";
|
||||
@@ -307,6 +325,7 @@ namespace http {
|
||||
if (!i2p::context.GetRouterInfo().GetProperty("family").empty())
|
||||
s << "<b>Router Family:</b> " << i2p::context.GetRouterInfo().GetProperty("family") << "<br>\r\n";
|
||||
s << "<b>Router Caps:</b> " << i2p::context.GetRouterInfo().GetProperty("caps") << "<br>\r\n";
|
||||
s << "<b>Version:</b> " VERSION "<br>\r\n";
|
||||
s << "<b>Our external address:</b>" << "<br>\r\n<table class=\"extaddr\"><tbody>\r\n";
|
||||
for (const auto& address : i2p::context.GetRouterInfo().GetAddresses())
|
||||
{
|
||||
@@ -402,9 +421,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";
|
||||
if (dest->IsEncryptedLeaseSet ())
|
||||
{
|
||||
@@ -414,6 +433,20 @@ namespace http {
|
||||
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())
|
||||
{
|
||||
s << "<div class='slide'><label for='slide-lease'><b>LeaseSets:</b> <i>" << dest->GetNumRemoteLeaseSets ()
|
||||
@@ -489,7 +522,7 @@ namespace http {
|
||||
|
||||
if (dest)
|
||||
{
|
||||
ShowLeaseSetDestination (s, dest);
|
||||
ShowLeaseSetDestination (s, dest, token);
|
||||
// show streams
|
||||
s << "<table>\r\n<caption>Streams</caption>\r\n<thead>\r\n<tr>";
|
||||
s << "<th style=\"width:25px;\">StreamID</th>";
|
||||
@@ -532,7 +565,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 ();
|
||||
if (i2cpServer)
|
||||
@@ -540,7 +573,7 @@ namespace http {
|
||||
s << "<b>I2CP Local Destination:</b><br>\r\n<br>\r\n";
|
||||
auto it = i2cpServer->GetSessions ().find (std::stoi (id));
|
||||
if (it != i2cpServer->GetSessions ().end ())
|
||||
ShowLeaseSetDestination (s, it->second->GetDestination ());
|
||||
ShowLeaseSetDestination (s, it->second->GetDestination (), 0);
|
||||
else
|
||||
ShowError(s, "I2CP session not found");
|
||||
}
|
||||
@@ -706,23 +739,23 @@ namespace http {
|
||||
std::stringstream tmp_s, tmp_s6; uint16_t cnt = 0, cnt6 = 0;
|
||||
for (const auto& it: sessions )
|
||||
{
|
||||
if (it.second && it.second->IsEstablished () && !it.second->GetSocket ().remote_endpoint ().address ().is_v6 ())
|
||||
if (it.second && it.second->IsEstablished () && !it.second->GetRemoteEndpoint ().address ().is_v6 ())
|
||||
{
|
||||
tmp_s << "<div class=\"listitem\">\r\n";
|
||||
if (it.second->IsOutgoing ()) tmp_s << " ⇒ ";
|
||||
tmp_s << i2p::data::GetIdentHashAbbreviation (it.second->GetRemoteIdentity ()->GetIdentHash ()) << ": "
|
||||
<< it.second->GetSocket ().remote_endpoint().address ().to_string ();
|
||||
<< it.second->GetRemoteEndpoint ().address ().to_string ();
|
||||
if (!it.second->IsOutgoing ()) tmp_s << " ⇒ ";
|
||||
tmp_s << " [" << it.second->GetNumSentBytes () << ":" << it.second->GetNumReceivedBytes () << "]";
|
||||
tmp_s << "</div>\r\n" << std::endl;
|
||||
cnt++;
|
||||
}
|
||||
if (it.second && it.second->IsEstablished () && it.second->GetSocket ().remote_endpoint ().address ().is_v6 ())
|
||||
if (it.second && it.second->IsEstablished () && it.second->GetRemoteEndpoint ().address ().is_v6 ())
|
||||
{
|
||||
tmp_s6 << "<div class=\"listitem\">\r\n";
|
||||
if (it.second->IsOutgoing ()) tmp_s6 << " ⇒ ";
|
||||
tmp_s6 << i2p::data::GetIdentHashAbbreviation (it.second->GetRemoteIdentity ()->GetIdentHash ()) << ": "
|
||||
<< "[" << it.second->GetSocket ().remote_endpoint().address ().to_string () << "]";
|
||||
<< "[" << it.second->GetRemoteEndpoint ().address ().to_string () << "]";
|
||||
if (!it.second->IsOutgoing ()) tmp_s6 << " ⇒ ";
|
||||
tmp_s6 << " [" << it.second->GetNumSentBytes () << ":" << it.second->GetNumReceivedBytes () << "]";
|
||||
tmp_s6 << "</div>\r\n" << std::endl;
|
||||
@@ -810,7 +843,7 @@ namespace http {
|
||||
s << "<b>SAM Sessions:</b><br>\r\n<div class=\"list\">\r\n";
|
||||
for (auto& it: sam->GetSessions ())
|
||||
{
|
||||
auto& name = it.second->localDestination->GetNickname ();
|
||||
auto& name = it.second->GetLocalDestination ()->GetNickname ();
|
||||
s << "<div class=\"listitem\"><a href=\"" << webroot << "?page=" << HTTP_PAGE_SAM_SESSION << "&sam_id=" << it.first << "\">";
|
||||
s << name << " (" << it.first << ")</a></div>\r\n" << std::endl;
|
||||
}
|
||||
@@ -820,7 +853,7 @@ namespace http {
|
||||
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 ();
|
||||
if (!sam) {
|
||||
@@ -836,7 +869,7 @@ namespace http {
|
||||
|
||||
std::string webroot; i2p::config::GetOption("http.webroot", webroot);
|
||||
s << "<b>SAM Session:</b><br>\r\n<div class=\"list\">\r\n";
|
||||
auto& ident = session->localDestination->GetIdentHash();
|
||||
auto& ident = session->GetLocalDestination ()->GetIdentHash();
|
||||
s << "<div class=\"listitem\"><a href=\"" << webroot << "?page=" << HTTP_PAGE_LOCAL_DESTINATION << "&b32=" << ident.ToBase32 () << "\">";
|
||||
s << i2p::client::context.GetAddressBook ().ToAddress(ident) << "</a></div>\r\n";
|
||||
s << "<br>\r\n";
|
||||
@@ -1205,15 +1238,15 @@ namespace http {
|
||||
if (dest)
|
||||
{
|
||||
if(dest->DeleteStream (streamID))
|
||||
s << "<b>SUCCESS</b>: Stream closed<br><br>\r\n";
|
||||
s << "<b>SUCCESS</b>: Stream closed<br>\r\n<br>\r\n";
|
||||
else
|
||||
s << "<b>ERROR</b>: Stream not found or already was closed<br><br>\r\n";
|
||||
s << "<b>ERROR</b>: Stream not found or already was closed<br>\r\n<br>\r\n";
|
||||
}
|
||||
else
|
||||
s << "<b>ERROR</b>: Destination not found<br><br>\r\n";
|
||||
s << "<b>ERROR</b>: Destination not found<br>\r\n<br>\r\n";
|
||||
}
|
||||
else
|
||||
s << "<b>ERROR</b>: StreamID can be null<br><br>\r\n";
|
||||
s << "<b>ERROR</b>: 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 << "<p>You will be redirected back in 5 seconds</b>";
|
||||
@@ -1227,13 +1260,62 @@ namespace http {
|
||||
if (limit > 0 && limit <= 65535)
|
||||
SetMaxNumTransitTunnels (limit);
|
||||
else {
|
||||
s << "<b>ERROR</b>: Transit tunnels count must not exceed 65535<br><br>\r\n";
|
||||
s << "<a href=\"" << webroot << "?page=commands\">Back to commands list</a><br>\r\n";
|
||||
s << "<b>ERROR</b>: 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>\r\n<br>\r\n";
|
||||
s << "<p>You will be redirected back in 5 seconds</b>";
|
||||
res.add_header("Refresh", redirect.c_str());
|
||||
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>: Domain can't end with .b32.i2p\r\n<br>\r\n<br>\r\n";
|
||||
}
|
||||
else
|
||||
s << "<b>ERROR</b>: Domain must end with .i2p\r\n<br>\r\n<br>\r\n";
|
||||
}
|
||||
else
|
||||
s << "<b>ERROR</b>: 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
|
||||
{
|
||||
res.code = 400;
|
||||
|
||||
@@ -2,6 +2,10 @@
|
||||
#include <sstream>
|
||||
#include <openssl/x509.h>
|
||||
#include <openssl/pem.h>
|
||||
|
||||
// Use global placeholders from boost introduced when local_time.hpp is loaded
|
||||
#define BOOST_BIND_GLOBAL_PLACEHOLDERS
|
||||
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include <boost/date_time/local_time/local_time.hpp>
|
||||
#include <boost/date_time/posix_time/posix_time.hpp>
|
||||
@@ -714,8 +718,8 @@ namespace client
|
||||
for (auto& it: sam->GetSessions ())
|
||||
{
|
||||
boost::property_tree::ptree sam_session, sam_session_sockets;
|
||||
auto& name = it.second->localDestination->GetNickname ();
|
||||
auto& ident = it.second->localDestination->GetIdentHash();
|
||||
auto& name = it.second->GetLocalDestination ()->GetNickname ();
|
||||
auto& ident = it.second->GetLocalDestination ()->GetIdentHash();
|
||||
sam_session.put("name", name);
|
||||
sam_session.put("address", i2p::client::context.GetAddressBook ().ToAddress(ident));
|
||||
|
||||
|
||||
@@ -81,10 +81,10 @@ namespace transport
|
||||
void UPnP::Discover ()
|
||||
{
|
||||
bool isError;
|
||||
int err;
|
||||
int err;
|
||||
|
||||
#if ((MINIUPNPC_API_VERSION >= 8) || defined (UPNPDISCOVER_SUCCESS))
|
||||
err = UPNPDISCOVER_SUCCESS;
|
||||
err = UPNPDISCOVER_SUCCESS;
|
||||
|
||||
#if (MINIUPNPC_API_VERSION >= 14)
|
||||
m_Devlist = upnpDiscover (UPNP_RESPONSE_TIMEOUT, NULL, NULL, 0, 0, 2, &err);
|
||||
@@ -94,8 +94,8 @@ namespace transport
|
||||
|
||||
isError = err != UPNPDISCOVER_SUCCESS;
|
||||
#else // MINIUPNPC_API_VERSION >= 8
|
||||
err = 0;
|
||||
m_Devlist = upnpDiscover (UPNP_RESPONSE_TIMEOUT, NULL, NULL, 0);
|
||||
err = 0;
|
||||
m_Devlist = upnpDiscover (UPNP_RESPONSE_TIMEOUT, NULL, NULL, 0);
|
||||
isError = m_Devlist == NULL;
|
||||
#endif // MINIUPNPC_API_VERSION >= 8
|
||||
{
|
||||
@@ -106,15 +106,15 @@ namespace transport
|
||||
|
||||
if (isError)
|
||||
{
|
||||
LogPrint (eLogError, "UPnP: unable to discover Internet Gateway Devices: error ", err);
|
||||
LogPrint (eLogError, "UPnP: unable to discover Internet Gateway Devices: error ", err);
|
||||
return;
|
||||
}
|
||||
|
||||
err = UPNP_GetValidIGD (m_Devlist, &m_upnpUrls, &m_upnpData, m_NetworkAddr, sizeof (m_NetworkAddr));
|
||||
m_upnpUrlsInitialized=err!=0;
|
||||
m_upnpUrlsInitialized=err!=0;
|
||||
if (err == UPNP_IGD_VALID_CONNECTED)
|
||||
{
|
||||
err = UPNP_GetExternalIPAddress (m_upnpUrls.controlURL, m_upnpData.first.servicetype, m_externalIPAddress);
|
||||
err = UPNP_GetExternalIPAddress (m_upnpUrls.controlURL, m_upnpData.first.servicetype, m_externalIPAddress);
|
||||
if(err != UPNPCOMMAND_SUCCESS)
|
||||
{
|
||||
LogPrint (eLogError, "UPnP: unable to get external address: error ", err);
|
||||
@@ -125,14 +125,14 @@ namespace transport
|
||||
LogPrint (eLogError, "UPnP: found Internet Gateway Device ", m_upnpUrls.controlURL);
|
||||
if (!m_externalIPAddress[0])
|
||||
{
|
||||
LogPrint (eLogError, "UPnP: found Internet Gateway Device doesn't know our external address");
|
||||
LogPrint (eLogError, "UPnP: found Internet Gateway Device doesn't know our external address");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint (eLogError, "UPnP: unable to find valid Internet Gateway Device: error ", err);
|
||||
LogPrint (eLogError, "UPnP: unable to find valid Internet Gateway Device: error ", err);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -183,7 +183,7 @@ namespace transport
|
||||
err = CheckMapping (strPort.c_str (), strType.c_str ());
|
||||
if (err != UPNPCOMMAND_SUCCESS) // if mapping not found
|
||||
{
|
||||
LogPrint (eLogDebug, "UPnP: possibly port ", strPort, " is not forwarded: return code ", err);
|
||||
LogPrint (eLogDebug, "UPnP: possibly port ", strPort, " is not forwarded: return code ", err);
|
||||
|
||||
#if ((MINIUPNPC_API_VERSION >= 8) || defined (UPNPDISCOVER_SUCCESS))
|
||||
err = UPNP_AddPortMapping (m_upnpUrls.controlURL, m_upnpData.first.servicetype, strPort.c_str (), strPort.c_str (), m_NetworkAddr, strDesc.c_str (), strType.c_str (), NULL, NULL);
|
||||
@@ -203,7 +203,7 @@ namespace transport
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint (eLogDebug, "UPnP: external forward from ", m_NetworkAddr, ":", strPort, " exists on current Internet Gateway Device");
|
||||
LogPrint (eLogDebug, "UPnP: external forward from ", m_NetworkAddr, ":", strPort, " exists on current Internet Gateway Device");
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -220,14 +220,14 @@ namespace transport
|
||||
|
||||
void UPnP::CloseMapping (std::shared_ptr<i2p::data::RouterInfo::Address> address)
|
||||
{
|
||||
if(!m_upnpUrlsInitialized) {
|
||||
return;
|
||||
}
|
||||
if(!m_upnpUrlsInitialized) {
|
||||
return;
|
||||
}
|
||||
std::string strType (GetProto (address)), strPort (std::to_string (address->port));
|
||||
int err = UPNPCOMMAND_SUCCESS;
|
||||
|
||||
err = CheckMapping (strPort.c_str (), strType.c_str ());
|
||||
if (err == UPNPCOMMAND_SUCCESS)
|
||||
if (err == UPNPCOMMAND_SUCCESS)
|
||||
{
|
||||
err = UPNP_DeletePortMapping (m_upnpUrls.controlURL, m_upnpData.first.servicetype, strPort.c_str (), strType.c_str (), NULL);
|
||||
LogPrint (eLogError, "UPnP: DeletePortMapping() returned : ", err);
|
||||
@@ -238,11 +238,11 @@ namespace transport
|
||||
{
|
||||
freeUPNPDevlist (m_Devlist);
|
||||
m_Devlist = 0;
|
||||
if(m_upnpUrlsInitialized){
|
||||
FreeUPNPUrls (&m_upnpUrls);
|
||||
m_upnpUrlsInitialized=false;
|
||||
}
|
||||
}
|
||||
if(m_upnpUrlsInitialized){
|
||||
FreeUPNPUrls (&m_upnpUrls);
|
||||
m_upnpUrlsInitialized=false;
|
||||
}
|
||||
}
|
||||
|
||||
std::string UPnP::GetProto (std::shared_ptr<i2p::data::RouterInfo::Address> address)
|
||||
{
|
||||
|
||||
12
debian/changelog
vendored
12
debian/changelog
vendored
@@ -1,3 +1,15 @@
|
||||
i2pd (2.38.0-1) unstable; urgency=medium
|
||||
|
||||
* updated to version 2.38.0/0.9.50
|
||||
|
||||
-- orignal <orignal@i2pmail.org> Mon, 17 May 2021 16:00:00 +0000
|
||||
|
||||
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
|
||||
|
||||
* updated to version 2.36.0/0.9.49
|
||||
|
||||
@@ -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
|
||||
*
|
||||
@@ -272,11 +272,19 @@ namespace data
|
||||
case i2p::data::SIGNING_KEY_TYPE_ECDSA_SHA384_P384:
|
||||
case i2p::data::SIGNING_KEY_TYPE_ECDSA_SHA512_P521:
|
||||
publicKeyLength = BlindECDSA (m_SigType, priv, seed, BlindEncodedPrivateKeyECDSA, blindedPriv, blindedPub);
|
||||
break;
|
||||
break;
|
||||
case i2p::data::SIGNING_KEY_TYPE_REDDSA_SHA512_ED25519:
|
||||
i2p::crypto::GetEd25519 ()->BlindPrivateKey (priv, seed, blindedPriv, blindedPub);
|
||||
publicKeyLength = i2p::crypto::EDDSA25519_PUBLIC_KEY_LENGTH;
|
||||
break;
|
||||
case i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519:
|
||||
{
|
||||
uint8_t exp[64];
|
||||
i2p::crypto::Ed25519::ExpandPrivateKey (priv, exp);
|
||||
i2p::crypto::GetEd25519 ()->BlindPrivateKey (exp, seed, blindedPriv, blindedPub);
|
||||
publicKeyLength = i2p::crypto::EDDSA25519_PUBLIC_KEY_LENGTH;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
LogPrint (eLogError, "Blinding: can't blind signature type ", (int)m_SigType);
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include "Identity.h"
|
||||
#include "Config.h"
|
||||
#include "version.h"
|
||||
#include "Log.h"
|
||||
|
||||
using namespace boost::program_options;
|
||||
|
||||
@@ -50,7 +51,9 @@ namespace config {
|
||||
("nat", bool_switch()->default_value(true), "Should we assume we are behind NAT? (default: enabled)")
|
||||
("port", value<uint16_t>()->default_value(0), "Port to listen for incoming connections (default: auto)")
|
||||
("ipv4", bool_switch()->default_value(true), "Enable communication through ipv4 (default: enabled)")
|
||||
("address4", value<std::string>()->default_value(""), "Local address to bind ipv4 transport sockets to")
|
||||
("ipv6", bool_switch()->default_value(false), "Enable communication through ipv6 (default: disabled)")
|
||||
("address6", value<std::string>()->default_value(""), "Local address to bind ipv6 transport sockets to")
|
||||
("reservedrange", bool_switch()->default_value(true), "Check remote RI for being in blacklist of reserved IP ranges (default: enabled)")
|
||||
("netid", value<int>()->default_value(I2PD_NET_ID), "Specify NetID. Main I2P is 2")
|
||||
("daemon", bool_switch()->default_value(false), "Router will go to background after start (default: disabled)")
|
||||
@@ -59,11 +62,11 @@ namespace config {
|
||||
("floodfill", bool_switch()->default_value(false), "Router will be floodfill (default: disabled)")
|
||||
("bandwidth", value<std::string>()->default_value(""), "Bandwidth limit: integer in KBps or letters: L (32), O (256), P (2048), X (>9000)")
|
||||
("share", value<int>()->default_value(100), "Limit of transit traffic from max bandwidth in percents. (default: 100)")
|
||||
("ntcp", bool_switch()->default_value(false), "Ignored. Always false")
|
||||
("ntcp", bool_switch()->default_value(false), "Deprecated option. Always false")
|
||||
("ssu", bool_switch()->default_value(true), "Enable SSU transport (default: enabled)")
|
||||
("ntcpproxy", value<std::string>()->default_value(""), "Ignored")
|
||||
("ntcpproxy", value<std::string>()->default_value(""), "Deprecated option")
|
||||
#ifdef _WIN32
|
||||
("svcctl", value<std::string>()->default_value(""), "Windows service management ('install' or 'remove')")
|
||||
("svcctl", value<std::string>()->default_value(""), "Deprecated option")
|
||||
("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")
|
||||
#endif
|
||||
@@ -74,9 +77,9 @@ namespace config {
|
||||
("limits.coresize", value<uint32_t>()->default_value(0), "Maximum size of corefile in Kb (0 - use system limit)")
|
||||
("limits.openfiles", value<uint16_t>()->default_value(0), "Maximum number of open files (0 - use system default)")
|
||||
("limits.transittunnels", value<uint16_t>()->default_value(2500), "Maximum active transit sessions (default:2500)")
|
||||
("limits.ntcpsoft", value<uint16_t>()->default_value(0), "Threshold to start probabilistic backoff with ntcp sessions (default: use system limit)")
|
||||
("limits.ntcphard", value<uint16_t>()->default_value(0), "Maximum number of ntcp sessions (default: use system limit)")
|
||||
("limits.ntcpthreads", value<uint16_t>()->default_value(1), "Maximum number of threads used by NTCP DH worker (default: 1)")
|
||||
("limits.ntcpsoft", value<uint16_t>()->default_value(0), "Deprecated option")
|
||||
("limits.ntcphard", value<uint16_t>()->default_value(0), "Deprecated option")
|
||||
("limits.ntcpthreads", value<uint16_t>()->default_value(1), "Deprecated option")
|
||||
;
|
||||
|
||||
options_description httpserver("HTTP Server options");
|
||||
@@ -207,7 +210,7 @@ namespace config {
|
||||
), "Reseed URLs, separated by comma")
|
||||
("reseed.yggurls", value<std::string>()->default_value(
|
||||
"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");
|
||||
@@ -216,12 +219,12 @@ namespace config {
|
||||
"http://shx5vqsw7usdaunyzr2qmes2fq37oumybpudrd4jjj4e4vk4uusa.b32.i2p/hosts.txt"
|
||||
), "AddressBook subscription URL for initial setup")
|
||||
("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");
|
||||
trust.add_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.hidden", value<bool>()->default_value(false), "Should we hide our router from other routers?")
|
||||
;
|
||||
@@ -247,7 +250,7 @@ namespace config {
|
||||
("ntcp2.enabled", value<bool>()->default_value(true), "Enable NTCP2 (default: enabled)")
|
||||
("ntcp2.published", value<bool>()->default_value(true), "Publish NTCP2 (default: enabled)")
|
||||
("ntcp2.port", value<uint16_t>()->default_value(0), "Port to listen for incoming NTCP2 connections (default: auto)")
|
||||
("ntcp2.addressv6", value<std::string>()->default_value("::"), "Address to bind NTCP2 on")
|
||||
("ntcp2.addressv6", value<std::string>()->default_value("::"), "Address to publish NTCP2 with")
|
||||
("ntcp2.proxy", value<std::string>()->default_value(""), "Proxy URL for NTCP2 transport")
|
||||
;
|
||||
|
||||
@@ -278,10 +281,10 @@ namespace config {
|
||||
|
||||
options_description meshnets("Meshnet transports options");
|
||||
meshnets.add_options()
|
||||
("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.yggdrasil", bool_switch()->default_value(false), "Support transports through the Yggdrasil (default: false)")
|
||||
("meshnets.yggaddress", value<std::string>()->default_value(""), "Yggdrasil address to publish")
|
||||
;
|
||||
|
||||
m_OptionsDesc
|
||||
.add(general)
|
||||
.add(limits)
|
||||
@@ -312,7 +315,7 @@ namespace config {
|
||||
try
|
||||
{
|
||||
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;
|
||||
if (ignoreUnknown)
|
||||
store(command_line_parser(argc, argv).options(m_OptionsDesc).style (style).allow_unregistered().run(), m_Options);
|
||||
@@ -321,6 +324,7 @@ namespace config {
|
||||
}
|
||||
catch (boost::program_options::error& e)
|
||||
{
|
||||
ThrowFatal ("Error while parsing arguments: ", e.what());
|
||||
std::cerr << "args: " << e.what() << std::endl;
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
@@ -358,6 +362,7 @@ namespace config {
|
||||
|
||||
if (!config.is_open())
|
||||
{
|
||||
ThrowFatal ("Missing or unreadable config file: ", path);
|
||||
std::cerr << "missing/unreadable config file: " << path << std::endl;
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
@@ -368,6 +373,7 @@ namespace config {
|
||||
}
|
||||
catch (boost::program_options::error& e)
|
||||
{
|
||||
ThrowFatal ("Error while parsing config file: ", e.what());
|
||||
std::cerr << e.what() << std::endl;
|
||||
exit(EXIT_FAILURE);
|
||||
};
|
||||
|
||||
@@ -353,7 +353,7 @@ namespace crypto
|
||||
|
||||
bool X25519Keys::Agree (const uint8_t * pub, uint8_t * shared)
|
||||
{
|
||||
if (pub[31] & 0x80) return false; // not x25519 key
|
||||
if (!pub || (pub[31] & 0x80)) return false; // not x25519 key
|
||||
#if OPENSSL_X25519
|
||||
EVP_PKEY_derive_init (m_Ctx);
|
||||
auto pkey = EVP_PKEY_new_raw_public_key (EVP_PKEY_X25519, NULL, pub, 32);
|
||||
|
||||
@@ -300,7 +300,11 @@ namespace client
|
||||
{
|
||||
int numTunnels = m_Pool->GetNumInboundTunnels () + 2; // 2 backup tunnels
|
||||
if (numTunnels > i2p::data::MAX_NUM_LEASES) numTunnels = i2p::data::MAX_NUM_LEASES; // 16 tunnels maximum
|
||||
CreateNewLeaseSet (m_Pool->GetInboundTunnels (numTunnels));
|
||||
auto tunnels = m_Pool->GetInboundTunnels (numTunnels);
|
||||
if (!tunnels.empty ())
|
||||
CreateNewLeaseSet (tunnels);
|
||||
else
|
||||
LogPrint (eLogInfo, "Destination: No inbound tunnels for LeaseSet");
|
||||
}
|
||||
|
||||
bool LeaseSetDestination::SubmitSessionKey (const uint8_t * key, const uint8_t * tag)
|
||||
@@ -386,7 +390,7 @@ namespace client
|
||||
if (leaseSet->IsNewer (buf + offset, len - offset))
|
||||
{
|
||||
leaseSet->Update (buf + offset, len - offset);
|
||||
if (leaseSet->IsValid () && leaseSet->GetIdentHash () == key)
|
||||
if (leaseSet->IsValid () && leaseSet->GetIdentHash () == key && !leaseSet->IsExpired ())
|
||||
LogPrint (eLogDebug, "Destination: Remote LeaseSet updated");
|
||||
else
|
||||
{
|
||||
@@ -405,7 +409,7 @@ namespace client
|
||||
leaseSet = std::make_shared<i2p::data::LeaseSet> (buf + offset, len - offset); // LeaseSet
|
||||
else
|
||||
leaseSet = std::make_shared<i2p::data::LeaseSet2> (buf[DATABASE_STORE_TYPE_OFFSET], buf + offset, len - offset, true, GetPreferredCryptoType () ); // LeaseSet2
|
||||
if (leaseSet->IsValid () && leaseSet->GetIdentHash () == key)
|
||||
if (leaseSet->IsValid () && leaseSet->GetIdentHash () == key && !leaseSet->IsExpired ())
|
||||
{
|
||||
if (leaseSet->GetIdentHash () != GetIdentHash ())
|
||||
{
|
||||
@@ -505,7 +509,7 @@ namespace client
|
||||
// schedule verification
|
||||
m_PublishVerificationTimer.expires_from_now (boost::posix_time::seconds(PUBLISH_VERIFICATION_TIMEOUT));
|
||||
m_PublishVerificationTimer.async_wait (std::bind (&LeaseSetDestination::HandlePublishVerificationTimer,
|
||||
shared_from_this (), std::placeholders::_1));
|
||||
shared_from_this (), std::placeholders::_1));
|
||||
}
|
||||
else
|
||||
i2p::garlic::GarlicDestination::HandleDeliveryStatusMessage (msgID);
|
||||
@@ -588,8 +592,7 @@ namespace client
|
||||
// assume it successive and try to verify
|
||||
m_PublishVerificationTimer.expires_from_now (boost::posix_time::seconds(PUBLISH_VERIFICATION_TIMEOUT));
|
||||
m_PublishVerificationTimer.async_wait (std::bind (&LeaseSetDestination::HandlePublishVerificationTimer,
|
||||
shared_from_this (), std::placeholders::_1));
|
||||
|
||||
shared_from_this (), std::placeholders::_1));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1127,13 +1130,28 @@ namespace client
|
||||
return dest;
|
||||
}
|
||||
|
||||
std::shared_ptr<i2p::stream::StreamingDestination> ClientDestination::RemoveStreamingDestination (int port)
|
||||
{
|
||||
if (port)
|
||||
{
|
||||
auto it = m_StreamingDestinationsByPorts.find (port);
|
||||
if (it != m_StreamingDestinationsByPorts.end ())
|
||||
{
|
||||
auto ret = it->second;
|
||||
m_StreamingDestinationsByPorts.erase (it);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
i2p::datagram::DatagramDestination * ClientDestination::CreateDatagramDestination (bool gzip)
|
||||
{
|
||||
if (m_DatagramDestination == nullptr)
|
||||
m_DatagramDestination = new i2p::datagram::DatagramDestination (GetSharedFromThis (), gzip);
|
||||
return m_DatagramDestination;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
std::vector<std::shared_ptr<const i2p::stream::Stream> > ClientDestination::GetAllStreams () const
|
||||
{
|
||||
std::vector<std::shared_ptr<const i2p::stream::Stream> > ret;
|
||||
@@ -1176,7 +1194,7 @@ namespace client
|
||||
LogPrint(eLogError, "Destinations: Can't save keys to ", path);
|
||||
}
|
||||
|
||||
void ClientDestination::CreateNewLeaseSet (std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> > tunnels)
|
||||
void ClientDestination::CreateNewLeaseSet (const std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> >& tunnels)
|
||||
{
|
||||
std::shared_ptr<i2p::data::LocalLeaseSet> leaseSet;
|
||||
if (GetLeaseSetType () == i2p::data::NETDB_STORE_TYPE_LEASESET)
|
||||
|
||||
@@ -137,6 +137,8 @@ namespace client
|
||||
void ProcessDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg);
|
||||
void SetLeaseSetUpdated ();
|
||||
|
||||
bool IsPublic () const { return m_IsPublic; };
|
||||
|
||||
protected:
|
||||
|
||||
// implements GarlicDestination
|
||||
@@ -147,11 +149,10 @@ namespace client
|
||||
int GetLeaseSetType () const { return m_LeaseSetType; };
|
||||
void SetLeaseSetType (int leaseSetType) { m_LeaseSetType = leaseSetType; };
|
||||
int GetAuthType () const { return m_AuthType; };
|
||||
bool IsPublic () const { return m_IsPublic; };
|
||||
virtual void CleanupDestination () {}; // additional clean up in derived classes
|
||||
// I2CP
|
||||
virtual void HandleDataMessage (const uint8_t * buf, size_t len) = 0;
|
||||
virtual void CreateNewLeaseSet (std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> > tunnels) = 0;
|
||||
virtual void CreateNewLeaseSet (const std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> >& tunnels) = 0;
|
||||
|
||||
private:
|
||||
|
||||
@@ -235,6 +236,7 @@ namespace client
|
||||
// streaming
|
||||
std::shared_ptr<i2p::stream::StreamingDestination> CreateStreamingDestination (int port, bool gzip = true); // additional
|
||||
std::shared_ptr<i2p::stream::StreamingDestination> GetStreamingDestination (int port = 0) const;
|
||||
std::shared_ptr<i2p::stream::StreamingDestination> RemoveStreamingDestination (int port);
|
||||
// following methods operate with default streaming destination
|
||||
void CreateStream (StreamRequestComplete streamRequestComplete, const i2p::data::IdentHash& dest, int port = 0);
|
||||
void CreateStream (StreamRequestComplete streamRequestComplete, std::shared_ptr<const i2p::data::BlindedPublicKey> dest, int port = 0);
|
||||
@@ -261,7 +263,7 @@ namespace client
|
||||
void CleanupDestination ();
|
||||
// I2CP
|
||||
void HandleDataMessage (const uint8_t * buf, size_t len);
|
||||
void CreateNewLeaseSet (std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> > tunnels);
|
||||
void CreateNewLeaseSet (const std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> >& tunnels);
|
||||
|
||||
private:
|
||||
|
||||
|
||||
@@ -160,9 +160,10 @@ namespace garlic
|
||||
return true;
|
||||
}
|
||||
|
||||
ECIESX25519AEADRatchetSession::ECIESX25519AEADRatchetSession (GarlicDestination * owner, bool attachLeaseSet):
|
||||
GarlicRoutingSession (owner, attachLeaseSet)
|
||||
ECIESX25519AEADRatchetSession::ECIESX25519AEADRatchetSession (GarlicDestination * owner, bool attachLeaseSetNS):
|
||||
GarlicRoutingSession (owner, true)
|
||||
{
|
||||
if (!attachLeaseSetNS) SetLeaseSetUpdateStatus (eLeaseSetUpToDate);
|
||||
RAND_bytes (m_PaddingSizes, 32); m_NextPaddingSize = 0;
|
||||
}
|
||||
|
||||
@@ -195,7 +196,7 @@ namespace garlic
|
||||
i2p::transport::transports.ReuseX25519KeysPair (m_EphemeralKeys);
|
||||
}
|
||||
// we still didn't find elligator eligible pair
|
||||
for (int i = 0; i < 10; i++)
|
||||
for (int i = 0; i < 25; i++)
|
||||
{
|
||||
// create new
|
||||
m_EphemeralKeys = std::make_shared<i2p::crypto::X25519Keys>();
|
||||
@@ -227,7 +228,7 @@ namespace garlic
|
||||
if (!GetOwner ()) return false;
|
||||
// we are Bob
|
||||
// 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))
|
||||
{
|
||||
@@ -460,7 +461,7 @@ namespace garlic
|
||||
offset += 32;
|
||||
|
||||
// KDF1
|
||||
i2p::crypto::InitNoiseIKState (*this, m_RemoteStaticKey); // bpk
|
||||
i2p::crypto::InitNoiseIKState (GetNoiseState (), m_RemoteStaticKey); // bpk
|
||||
MixHash (m_EphemeralKeys->GetPublicKey (), 32); // h = SHA256(h || aepk)
|
||||
uint8_t sharedSecret[32];
|
||||
if (!m_EphemeralKeys->Agree (m_RemoteStaticKey, sharedSecret)) // x25519(aesk, bpk)
|
||||
@@ -511,6 +512,7 @@ namespace garlic
|
||||
{
|
||||
auto tagsetNsr = std::make_shared<ReceiveRatchetTagSet>(shared_from_this (), true);
|
||||
InitNewSessionTagset (tagsetNsr);
|
||||
tagsetNsr->Expire (); // let non-replied session expire
|
||||
GenerateMoreReceiveTags (tagsetNsr, ECIESX25519_NSR_NUM_GENERATED_TAGS);
|
||||
}
|
||||
}
|
||||
@@ -520,7 +522,7 @@ namespace garlic
|
||||
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
|
||||
i2p::crypto::InitNoiseNState (*this, m_RemoteStaticKey);
|
||||
i2p::crypto::InitNoiseNState (GetNoiseState (), m_RemoteStaticKey);
|
||||
size_t offset = 0;
|
||||
m_EphemeralKeys = i2p::transport::transports.GetNextX25519KeysPair ();
|
||||
memcpy (out + offset, m_EphemeralKeys->GetPublicKey (), 32);
|
||||
@@ -656,7 +658,7 @@ namespace garlic
|
||||
}
|
||||
buf += 32; len -= 32;
|
||||
// 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 (bepk, 32); // h = SHA256(h || bepk)
|
||||
uint8_t sharedSecret[32];
|
||||
@@ -813,39 +815,12 @@ namespace garlic
|
||||
case eSessionStateNew:
|
||||
return HandleNewIncomingSession (buf, len);
|
||||
case eSessionStateNewSessionSent:
|
||||
receiveTagset->Expire (); // NSR tagset
|
||||
return HandleNewOutgoingSessionReply (buf, len);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
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)
|
||||
{
|
||||
@@ -1124,6 +1099,39 @@ namespace garlic
|
||||
ts*1000 > m_LastSentTimestamp + ECIESX25519_SEND_EXPIRATION_TIMEOUT*1000; // milliseconds
|
||||
}
|
||||
|
||||
RouterIncomingRatchetSession::RouterIncomingRatchetSession (const i2p::crypto::NoiseSymmetricState& initState):
|
||||
ECIESX25519AEADRatchetSession (&i2p::context, false)
|
||||
{
|
||||
SetLeaseSetUpdateStatus (eLeaseSetDoNotSend);
|
||||
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)
|
||||
{
|
||||
auto m = NewI2NPMessage ();
|
||||
|
||||
@@ -88,7 +88,8 @@ namespace garlic
|
||||
bool IsNS () const { return m_IsNS; };
|
||||
std::shared_ptr<ECIESX25519AEADRatchetSession> GetSession () { return m_Session; };
|
||||
void SetTrimBehind (int index) { if (index > m_TrimBehindIndex) m_TrimBehindIndex = index; };
|
||||
|
||||
int GetTrimBehind () const { return m_TrimBehindIndex; };
|
||||
|
||||
void Expire ();
|
||||
bool IsExpired (uint64_t ts) const;
|
||||
|
||||
@@ -160,11 +161,10 @@ namespace garlic
|
||||
|
||||
public:
|
||||
|
||||
ECIESX25519AEADRatchetSession (GarlicDestination * owner, bool attachLeaseSet);
|
||||
ECIESX25519AEADRatchetSession (GarlicDestination * owner, bool attachLeaseSetNS);
|
||||
~ECIESX25519AEADRatchetSession ();
|
||||
|
||||
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> WrapOneTimeMessage (std::shared_ptr<const I2NPMessage> msg, bool isForRouter = false);
|
||||
|
||||
@@ -186,16 +186,21 @@ namespace garlic
|
||||
bool IsTerminated () const { return m_IsTerminated; }
|
||||
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:
|
||||
|
||||
void CreateNonce (uint64_t seqn, uint8_t * nonce);
|
||||
bool GenerateEphemeralKeysAndEncode (uint8_t * buf); // buf is 32 bytes
|
||||
void InitNewSessionTagset (std::shared_ptr<RatchetTagSet> tagsetNsr) const;
|
||||
|
||||
bool HandleNewIncomingSession (const 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);
|
||||
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);
|
||||
|
||||
bool NewOutgoingSessionMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen, bool isStatic = true);
|
||||
@@ -237,6 +242,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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -553,9 +553,10 @@ namespace garlic
|
||||
if (!session->HandleNextMessage (buf, length, nullptr, 0))
|
||||
{
|
||||
// try to gererate more tags for last tagset
|
||||
if (m_LastTagset && m_LastTagset->GetNextIndex () < 2*ECIESX25519_TAGSET_MAX_NUM_TAGS)
|
||||
if (m_LastTagset && (m_LastTagset->GetNextIndex () - m_LastTagset->GetTrimBehind () < 3*ECIESX25519_MAX_NUM_GENERATED_TAGS))
|
||||
{
|
||||
auto maxTags = std::max (m_NumRatchetInboundTags, ECIESX25519_MAX_NUM_GENERATED_TAGS);
|
||||
LogPrint (eLogWarning, "Garlic: trying to generate more ECIES-X25519-AEAD-Ratchet tags");
|
||||
for (int i = 0; i < maxTags; i++)
|
||||
{
|
||||
auto nextTag = AddECIESx25519SessionNextTag (m_LastTagset);
|
||||
@@ -879,8 +880,12 @@ namespace garlic
|
||||
{
|
||||
auto session = it->second.tagset->GetSession ();
|
||||
if (!session || session->IsTerminated())
|
||||
it->second.tagset->Expire ();
|
||||
++it;
|
||||
{
|
||||
it = m_ECIESx25519Tags.erase (it);
|
||||
numExpiredTags++;
|
||||
}
|
||||
else
|
||||
++it;
|
||||
}
|
||||
}
|
||||
if (numExpiredTags > 0)
|
||||
|
||||
@@ -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
|
||||
*
|
||||
@@ -9,12 +9,15 @@
|
||||
#include <algorithm>
|
||||
#include <utility>
|
||||
#include <stdio.h>
|
||||
#include "util.h"
|
||||
#include "HTTP.h"
|
||||
#include <ctime>
|
||||
#include "util.h"
|
||||
#include "Base.h"
|
||||
#include "HTTP.h"
|
||||
|
||||
namespace i2p {
|
||||
namespace http {
|
||||
namespace i2p
|
||||
{
|
||||
namespace http
|
||||
{
|
||||
const std::vector<std::string> HTTP_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
|
||||
@@ -471,12 +474,15 @@ namespace http {
|
||||
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);
|
||||
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);
|
||||
if (c == '\0' && !allow_null) {
|
||||
if (c == '\0' && !allow_null)
|
||||
{
|
||||
pos += 3;
|
||||
continue;
|
||||
}
|
||||
@@ -486,9 +492,11 @@ namespace http {
|
||||
return decoded;
|
||||
}
|
||||
|
||||
bool MergeChunkedResponse (std::istream& in, std::ostream& out) {
|
||||
bool MergeChunkedResponse (std::istream& in, std::ostream& out)
|
||||
{
|
||||
std::string hexLen;
|
||||
while (!in.eof ()) {
|
||||
while (!in.eof ())
|
||||
{
|
||||
std::getline (in, hexLen);
|
||||
errno = 0;
|
||||
long int len = strtoul(hexLen.c_str(), (char **) NULL, 16);
|
||||
@@ -506,5 +514,12 @@ namespace http {
|
||||
}
|
||||
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
|
||||
} // i2p
|
||||
|
||||
@@ -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
|
||||
*
|
||||
@@ -166,6 +166,9 @@ namespace http
|
||||
* @return true on success, false otherwise
|
||||
*/
|
||||
bool MergeChunkedResponse (std::istream& in, std::ostream& out);
|
||||
|
||||
std::string CreateBasicAuthorizationString (const std::string& user, const std::string& pass);
|
||||
|
||||
} // http
|
||||
} // i2p
|
||||
|
||||
|
||||
@@ -371,10 +371,7 @@ namespace i2p
|
||||
if (!memcmp (record + BUILD_REQUEST_RECORD_TO_PEER_OFFSET, (const uint8_t *)i2p::context.GetRouterInfo ().GetIdentHash (), 16))
|
||||
{
|
||||
LogPrint (eLogDebug, "I2NP: Build request record ", i, " is ours");
|
||||
BN_CTX * ctx = BN_CTX_new ();
|
||||
bool success = i2p::context.DecryptTunnelBuildRecord (record + BUILD_REQUEST_RECORD_ENCRYPTED_OFFSET, clearText, ctx);
|
||||
BN_CTX_free (ctx);
|
||||
if(!success) return false;
|
||||
if (!i2p::context.DecryptTunnelBuildRecord (record + BUILD_REQUEST_RECORD_ENCRYPTED_OFFSET, clearText)) return false;
|
||||
uint8_t retCode = 0;
|
||||
bool isECIES = i2p::context.IsECIES ();
|
||||
// replace record to reply
|
||||
|
||||
@@ -72,6 +72,12 @@ namespace data
|
||||
}
|
||||
size += 256; // encryption key
|
||||
size += m_Identity->GetSigningPublicKeyLen (); // unused signing key
|
||||
if (size + 1 > m_BufferLen)
|
||||
{
|
||||
LogPrint (eLogError, "LeaseSet: ", size, " exceeds buffer size ", m_BufferLen);
|
||||
m_IsValid = false;
|
||||
return;
|
||||
}
|
||||
uint8_t num = m_Buffer[size];
|
||||
size++; // num
|
||||
LogPrint (eLogDebug, "LeaseSet: read num=", (int)num);
|
||||
@@ -81,9 +87,14 @@ namespace data
|
||||
m_IsValid = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (size + num*LEASE_SIZE > m_BufferLen)
|
||||
{
|
||||
LogPrint (eLogError, "LeaseSet: ", size, " exceeds buffer size ", m_BufferLen);
|
||||
m_IsValid = false;
|
||||
return;
|
||||
}
|
||||
|
||||
UpdateLeasesBegin ();
|
||||
|
||||
// process leases
|
||||
m_ExpirationTime = 0;
|
||||
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
|
||||
@@ -106,14 +117,22 @@ namespace data
|
||||
return;
|
||||
}
|
||||
m_ExpirationTime += LEASE_ENDDATE_THRESHOLD;
|
||||
|
||||
UpdateLeasesEnd ();
|
||||
|
||||
// verify
|
||||
if (verifySignature && !m_Identity->Verify (m_Buffer, leases - m_Buffer, leases))
|
||||
{
|
||||
LogPrint (eLogWarning, "LeaseSet: verification failed");
|
||||
m_IsValid = false;
|
||||
if (verifySignature)
|
||||
{
|
||||
auto signedSize = leases - m_Buffer;
|
||||
if (signedSize + m_Identity->GetSignatureLen () > m_BufferLen)
|
||||
{
|
||||
LogPrint (eLogError, "LeaseSet: Signature exceeds buffer size ", m_BufferLen);
|
||||
m_IsValid = false;
|
||||
}
|
||||
else if (!m_Identity->Verify (m_Buffer, signedSize, leases))
|
||||
{
|
||||
LogPrint (eLogWarning, "LeaseSet: verification failed");
|
||||
m_IsValid = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -778,7 +797,7 @@ namespace data
|
||||
}
|
||||
|
||||
LocalLeaseSet2::LocalLeaseSet2 (uint8_t storeType, const i2p::data::PrivateKeys& keys,
|
||||
const KeySections& encryptionKeys, std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> > tunnels,
|
||||
const KeySections& encryptionKeys, const std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> >& tunnels,
|
||||
bool isPublic, bool isPublishedEncrypted):
|
||||
LocalLeaseSet (keys.GetPublic (), nullptr, 0)
|
||||
{
|
||||
@@ -843,9 +862,18 @@ namespace data
|
||||
offset += 4; // end date
|
||||
}
|
||||
// update expiration
|
||||
SetExpirationTime (expirationTime*1000LL);
|
||||
auto expires = expirationTime - timestamp;
|
||||
htobe16buf (expiresBuf, expires > 0 ? expires : 0);
|
||||
if (expirationTime)
|
||||
{
|
||||
SetExpirationTime (expirationTime*1000LL);
|
||||
auto expires = (int)expirationTime - timestamp;
|
||||
htobe16buf (expiresBuf, expires > 0 ? expires : 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
// no tunnels or withdraw
|
||||
SetExpirationTime (timestamp*1000LL);
|
||||
memset (expiresBuf, 0, 2); // expires immeditely
|
||||
}
|
||||
// sign
|
||||
keys.Sign (m_Buffer, offset, m_Buffer + offset); // LS + leading store type
|
||||
}
|
||||
|
||||
@@ -251,7 +251,7 @@ namespace data
|
||||
|
||||
LocalLeaseSet2 (uint8_t storeType, const i2p::data::PrivateKeys& keys,
|
||||
const KeySections& encryptionKeys,
|
||||
std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> > tunnels,
|
||||
const std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> >& tunnels,
|
||||
bool isPublic, bool isPublishedEncrypted = false);
|
||||
|
||||
LocalLeaseSet2 (uint8_t storeType, std::shared_ptr<const IdentityEx> identity, const uint8_t * buf, size_t len); // from I2CP
|
||||
|
||||
@@ -148,7 +148,7 @@ namespace log {
|
||||
LogLevel level; /**< message level */
|
||||
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();
|
||||
@@ -189,7 +189,7 @@ void LogPrint (LogLevel level, TArgs&&... args) noexcept
|
||||
return;
|
||||
|
||||
// fold message to single string
|
||||
std::stringstream ss("");
|
||||
std::stringstream ss;
|
||||
|
||||
#if (__cplusplus >= 201703L) // C++ 17 or higher
|
||||
(LogPrint (ss, std::forward<TArgs>(args)), ...);
|
||||
@@ -197,7 +197,7 @@ void LogPrint (LogLevel level, TArgs&&... args) noexcept
|
||||
LogPrint (ss, std::forward<TArgs>(args)...);
|
||||
#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();
|
||||
log.Append(msg);
|
||||
}
|
||||
|
||||
@@ -333,17 +333,16 @@ namespace transport
|
||||
if (in_RemoteRouter) // Alice
|
||||
{
|
||||
m_Establisher->m_RemoteIdentHash = GetRemoteIdentity ()->GetIdentHash ();
|
||||
if (!addr)
|
||||
addr = in_RemoteRouter->GetNTCP2Address (true); // we need a published address
|
||||
if (addr)
|
||||
{
|
||||
memcpy (m_Establisher->m_RemoteStaticKey, addr->ntcp2->staticKey, 32);
|
||||
memcpy (m_Establisher->m_IV, addr->ntcp2->iv, 16);
|
||||
m_RemoteEndpoint = boost::asio::ip::tcp::endpoint (addr->host, addr->port);
|
||||
}
|
||||
else
|
||||
LogPrint (eLogWarning, "NTCP2: Missing NTCP2 parameters");
|
||||
LogPrint (eLogWarning, "NTCP2: Missing NTCP2 address");
|
||||
}
|
||||
m_NextRouterInfoResendTime = i2p::util::GetSecondsSinceEpoch () + NTCP2_ROUTERINFO_RESEND_INTERVAL +
|
||||
m_NextRouterInfoResendTime = i2p::util::GetSecondsSinceEpoch () + NTCP2_ROUTERINFO_RESEND_INTERVAL +
|
||||
rand ()%NTCP2_ROUTERINFO_RESEND_INTERVAL_THRESHOLD;
|
||||
}
|
||||
|
||||
@@ -657,19 +656,13 @@ namespace transport
|
||||
SendTerminationAndTerminate (eNTCP2Message3Error);
|
||||
return;
|
||||
}
|
||||
auto addr = ri.GetNTCP2Address (false); // any NTCP2 address
|
||||
auto addr = ri.GetNTCP2AddressWithStaticKey (m_Establisher->m_RemoteStaticKey);
|
||||
if (!addr)
|
||||
{
|
||||
LogPrint (eLogError, "NTCP2: No NTCP2 address found in SessionConfirmed");
|
||||
LogPrint (eLogError, "NTCP2: No NTCP2 address wth static key found in SessionConfirmed");
|
||||
Terminate ();
|
||||
return;
|
||||
}
|
||||
if (memcmp (addr->ntcp2->staticKey, m_Establisher->m_RemoteStaticKey, 32))
|
||||
{
|
||||
LogPrint (eLogError, "NTCP2: Static key mismatch in SessionConfirmed");
|
||||
SendTerminationAndTerminate (eNTCP2IncorrectSParameter);
|
||||
return;
|
||||
}
|
||||
i2p::data::netdb.PostI2NPMsg (CreateI2NPMessage (eI2NPDummyMsg, buf.data () + 3, size)); // TODO: should insert ri and not parse it twice
|
||||
// TODO: process options
|
||||
|
||||
@@ -724,7 +717,7 @@ namespace transport
|
||||
m_Establisher->m_SessionRequestBuffer = new uint8_t[287]; // 287 bytes max for now
|
||||
boost::asio::async_read (m_Socket, boost::asio::buffer(m_Establisher->m_SessionRequestBuffer, 64), boost::asio::transfer_all (),
|
||||
std::bind(&NTCP2Session::HandleSessionRequestReceived, shared_from_this (),
|
||||
std::placeholders::_1, std::placeholders::_2));
|
||||
std::placeholders::_1, std::placeholders::_2));
|
||||
}
|
||||
|
||||
void NTCP2Session::ReceiveLength ()
|
||||
@@ -733,7 +726,7 @@ namespace transport
|
||||
#ifdef __linux__
|
||||
const int one = 1;
|
||||
setsockopt(m_Socket.native_handle(), IPPROTO_TCP, TCP_QUICKACK, &one, sizeof(one));
|
||||
#endif
|
||||
#endif
|
||||
boost::asio::async_read (m_Socket, boost::asio::buffer(&m_NextReceivedLen, 2), boost::asio::transfer_all (),
|
||||
std::bind(&NTCP2Session::HandleReceivedLength, shared_from_this (), std::placeholders::_1, std::placeholders::_2));
|
||||
}
|
||||
@@ -787,8 +780,8 @@ namespace transport
|
||||
if (IsTerminated ()) return;
|
||||
#ifdef __linux__
|
||||
const int one = 1;
|
||||
setsockopt(m_Socket.native_handle(), IPPROTO_TCP, TCP_QUICKACK, &one, sizeof(one));
|
||||
#endif
|
||||
setsockopt(m_Socket.native_handle(), IPPROTO_TCP, TCP_QUICKACK, &one, sizeof(one));
|
||||
#endif
|
||||
boost::asio::async_read (m_Socket, boost::asio::buffer(m_NextReceivedBuffer, m_NextReceivedLen), boost::asio::transfer_all (),
|
||||
std::bind(&NTCP2Session::HandleReceived, shared_from_this (), std::placeholders::_1, std::placeholders::_2));
|
||||
}
|
||||
@@ -1016,11 +1009,11 @@ namespace transport
|
||||
LogPrint (eLogDebug, "NTCP2: Next frame sent ", bytes_transferred);
|
||||
if (m_LastActivityTimestamp > m_NextRouterInfoResendTime)
|
||||
{
|
||||
m_NextRouterInfoResendTime += NTCP2_ROUTERINFO_RESEND_INTERVAL +
|
||||
m_NextRouterInfoResendTime += NTCP2_ROUTERINFO_RESEND_INTERVAL +
|
||||
rand ()%NTCP2_ROUTERINFO_RESEND_INTERVAL_THRESHOLD;
|
||||
SendRouterInfo ();
|
||||
}
|
||||
else
|
||||
SendRouterInfo ();
|
||||
}
|
||||
else
|
||||
SendQueue ();
|
||||
}
|
||||
}
|
||||
@@ -1120,7 +1113,7 @@ namespace transport
|
||||
SendQueue ();
|
||||
else if (m_SendQueue.size () > NTCP2_MAX_OUTGOING_QUEUE_SIZE)
|
||||
{
|
||||
LogPrint (eLogWarning, "NTCP2: outgoing messages queue size to ",
|
||||
LogPrint (eLogWarning, "NTCP2: outgoing messages queue size to ",
|
||||
GetIdentHashBase64(), " exceeds ", NTCP2_MAX_OUTGOING_QUEUE_SIZE);
|
||||
Terminate ();
|
||||
}
|
||||
@@ -1165,52 +1158,58 @@ namespace transport
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint(eLogInfo, "NTCP2: Proxy is not used");
|
||||
auto& addresses = context.GetRouterInfo ().GetAddresses ();
|
||||
for (const auto& address: addresses)
|
||||
// start acceptors
|
||||
auto& addresses = context.GetRouterInfo ().GetAddresses ();
|
||||
for (const auto& address: addresses)
|
||||
{
|
||||
if (!address) continue;
|
||||
if (address->IsPublishedNTCP2 () && address->port)
|
||||
{
|
||||
if (!address) continue;
|
||||
if (address->IsPublishedNTCP2 ())
|
||||
if (address->host.is_v4())
|
||||
{
|
||||
if (address->host.is_v4())
|
||||
try
|
||||
{
|
||||
try
|
||||
{
|
||||
m_NTCP2Acceptor.reset (new boost::asio::ip::tcp::acceptor (GetService (), boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), address->port)));
|
||||
}
|
||||
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));
|
||||
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);
|
||||
m_NTCP2Acceptor.reset (new boost::asio::ip::tcp::acceptor (GetService (), ep));
|
||||
}
|
||||
else if (address->host.is_v6() && (context.SupportsV6 () || context.SupportsMesh ()))
|
||||
catch ( std::exception & ex )
|
||||
{
|
||||
m_NTCP2V6Acceptor.reset (new boost::asio::ip::tcp::acceptor (GetService ()));
|
||||
try
|
||||
{
|
||||
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(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 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;
|
||||
}
|
||||
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 ()))
|
||||
{
|
||||
m_NTCP2V6Acceptor.reset (new boost::asio::ip::tcp::acceptor (GetService ()));
|
||||
try
|
||||
{
|
||||
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));
|
||||
auto ep = boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v6(), address->port);
|
||||
if (m_Address6 && !context.SupportsMesh ())
|
||||
ep = boost::asio::ip::tcp::endpoint (m_Address6->address(), address->port);
|
||||
else if (m_YggdrasilAddress && !context.SupportsV6 ())
|
||||
ep = boost::asio::ip::tcp::endpoint (m_YggdrasilAddress->address(), address->port);
|
||||
m_NTCP2V6Acceptor->bind (ep);
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1274,10 +1273,15 @@ namespace transport
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void NTCP2Server::Connect(const boost::asio::ip::address & address, uint16_t port, std::shared_ptr<NTCP2Session> conn)
|
||||
void NTCP2Server::Connect(std::shared_ptr<NTCP2Session> conn)
|
||||
{
|
||||
LogPrint (eLogDebug, "NTCP2: Connecting to ", address ,":", port);
|
||||
GetService ().post([this, address, port, conn]()
|
||||
if (!conn || conn->GetRemoteEndpoint ().address ().is_unspecified ())
|
||||
{
|
||||
LogPrint (eLogError, "NTCP2: Can't connect to unspecified address");
|
||||
return;
|
||||
}
|
||||
LogPrint (eLogDebug, "NTCP2: Connecting to ", conn->GetRemoteEndpoint ());
|
||||
GetService ().post([this, conn]()
|
||||
{
|
||||
if (this->AddNTCP2Session (conn))
|
||||
{
|
||||
@@ -1290,12 +1294,32 @@ namespace transport
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
{
|
||||
LogPrint (eLogInfo, "NTCP2: Not connected in ", timeout, " seconds");
|
||||
if (conn->GetRemoteIdentity ())
|
||||
i2p::data::netdb.SetUnreachable (conn->GetRemoteIdentity ()->GetIdentHash (), true);
|
||||
conn->Terminate ();
|
||||
}
|
||||
});
|
||||
conn->GetSocket ().async_connect (boost::asio::ip::tcp::endpoint (address, port), std::bind (&NTCP2Server::HandleConnect, this, std::placeholders::_1, conn, timer));
|
||||
// bind to local address
|
||||
std::shared_ptr<boost::asio::ip::tcp::endpoint> localAddress;
|
||||
if (conn->GetRemoteEndpoint ().address ().is_v6 ())
|
||||
{
|
||||
if (i2p::util::net::IsYggdrasilAddress (conn->GetRemoteEndpoint ().address ()))
|
||||
localAddress = m_YggdrasilAddress;
|
||||
else
|
||||
localAddress = m_Address6;
|
||||
conn->GetSocket ().open (boost::asio::ip::tcp::v6 ());
|
||||
}
|
||||
else
|
||||
{
|
||||
localAddress = m_Address4;
|
||||
conn->GetSocket ().open (boost::asio::ip::tcp::v4 ());
|
||||
}
|
||||
if (localAddress)
|
||||
{
|
||||
boost::system::error_code ec;
|
||||
conn->GetSocket ().bind (*localAddress, ec);
|
||||
if (ec)
|
||||
LogPrint (eLogError, "NTCP2: can't bind to ", localAddress->address ().to_string (), ": ", ec.message ());
|
||||
}
|
||||
conn->GetSocket ().async_connect (conn->GetRemoteEndpoint (), std::bind (&NTCP2Server::HandleConnect, this, std::placeholders::_1, conn, timer));
|
||||
}
|
||||
else
|
||||
conn->Terminate ();
|
||||
@@ -1312,7 +1336,7 @@ namespace transport
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint (eLogDebug, "NTCP2: Connected to ", conn->GetSocket ().remote_endpoint ());
|
||||
LogPrint (eLogDebug, "NTCP2: Connected to ", conn->GetRemoteEndpoint ());
|
||||
conn->ClientLogin ();
|
||||
}
|
||||
}
|
||||
@@ -1328,6 +1352,7 @@ namespace transport
|
||||
LogPrint (eLogDebug, "NTCP2: Connected from ", ep);
|
||||
if (conn)
|
||||
{
|
||||
conn->SetRemoteEndpoint (ep);
|
||||
conn->ServerLogin ();
|
||||
m_PendingIncomingSessions.push_back (conn);
|
||||
conn = nullptr;
|
||||
@@ -1361,6 +1386,7 @@ namespace transport
|
||||
LogPrint (eLogDebug, "NTCP2: Connected from ", ep);
|
||||
if (conn)
|
||||
{
|
||||
conn->SetRemoteEndpoint (ep);
|
||||
conn->ServerLogin ();
|
||||
m_PendingIncomingSessions.push_back (conn);
|
||||
}
|
||||
@@ -1415,13 +1441,18 @@ namespace transport
|
||||
}
|
||||
}
|
||||
|
||||
void NTCP2Server::ConnectWithProxy (const std::string& host, uint16_t port, RemoteAddressType addrtype, std::shared_ptr<NTCP2Session> conn)
|
||||
void NTCP2Server::ConnectWithProxy (std::shared_ptr<NTCP2Session> conn)
|
||||
{
|
||||
if(!m_ProxyEndpoint) return;
|
||||
GetService().post([this, host, port, addrtype, conn]() {
|
||||
if (!conn || conn->GetRemoteEndpoint ().address ().is_unspecified ())
|
||||
{
|
||||
LogPrint (eLogError, "NTCP2: Can't connect to unspecified address");
|
||||
return;
|
||||
}
|
||||
GetService().post([this, conn]()
|
||||
{
|
||||
if (this->AddNTCP2Session (conn))
|
||||
{
|
||||
|
||||
auto timer = std::make_shared<boost::asio::deadline_timer>(GetService());
|
||||
auto timeout = NTCP2_CONNECT_TIMEOUT * 5;
|
||||
conn->SetTerminationTimeout(timeout * 2);
|
||||
@@ -1431,23 +1462,25 @@ namespace transport
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
{
|
||||
LogPrint (eLogInfo, "NTCP2: Not connected in ", timeout, " seconds");
|
||||
i2p::data::netdb.SetUnreachable (conn->GetRemoteIdentity ()->GetIdentHash (), true);
|
||||
conn->Terminate ();
|
||||
}
|
||||
});
|
||||
conn->GetSocket ().async_connect (*m_ProxyEndpoint, std::bind (&NTCP2Server::HandleProxyConnect, this, std::placeholders::_1, conn, timer, host, port, addrtype));
|
||||
conn->GetSocket ().async_connect (*m_ProxyEndpoint, std::bind (&NTCP2Server::HandleProxyConnect, this, std::placeholders::_1, conn, timer));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
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_ProxyAddress = addr;
|
||||
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, const std::string & host, uint16_t port, RemoteAddressType addrtype)
|
||||
void NTCP2Server::HandleProxyConnect(const boost::system::error_code& ecode, std::shared_ptr<NTCP2Session> conn, std::shared_ptr<boost::asio::deadline_timer> timer)
|
||||
{
|
||||
if (ecode)
|
||||
{
|
||||
@@ -1473,7 +1506,7 @@ namespace transport
|
||||
});
|
||||
auto readbuff = std::make_shared<std::vector<uint8_t> >(2);
|
||||
boost::asio::async_read(conn->GetSocket(), boost::asio::buffer(readbuff->data (), 2),
|
||||
[this, readbuff, timer, conn, host, port, addrtype](const boost::system::error_code & ec, std::size_t transferred)
|
||||
[this, readbuff, timer, conn](const boost::system::error_code & ec, std::size_t transferred)
|
||||
{
|
||||
if(ec)
|
||||
{
|
||||
@@ -1486,7 +1519,7 @@ namespace transport
|
||||
{
|
||||
if((*readbuff)[1] == 0x00)
|
||||
{
|
||||
AfterSocksHandshake(conn, timer, host, port, addrtype);
|
||||
AfterSocksHandshake(conn, timer);
|
||||
return;
|
||||
}
|
||||
else if ((*readbuff)[1] == 0xff)
|
||||
@@ -1506,13 +1539,16 @@ namespace transport
|
||||
}
|
||||
case eHTTPProxy:
|
||||
{
|
||||
auto& ep = conn->GetRemoteEndpoint ();
|
||||
i2p::http::HTTPReq req;
|
||||
req.method = "CONNECT";
|
||||
req.version ="HTTP/1.1";
|
||||
if(addrtype == eIP6Address)
|
||||
req.uri = "[" + host + "]:" + std::to_string(port);
|
||||
if(ep.address ().is_v6 ())
|
||||
req.uri = "[" + ep.address ().to_string() + "]:" + std::to_string(ep.port ());
|
||||
else
|
||||
req.uri = host + ":" + std::to_string(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;
|
||||
std::ostream out(&writebuff);
|
||||
@@ -1566,7 +1602,7 @@ namespace transport
|
||||
}
|
||||
}
|
||||
|
||||
void NTCP2Server::AfterSocksHandshake(std::shared_ptr<NTCP2Session> conn, std::shared_ptr<boost::asio::deadline_timer> timer, const std::string & host, uint16_t port, RemoteAddressType addrtype)
|
||||
void NTCP2Server::AfterSocksHandshake(std::shared_ptr<NTCP2Session> conn, std::shared_ptr<boost::asio::deadline_timer> timer)
|
||||
{
|
||||
// build request
|
||||
size_t sz = 6; // header + port
|
||||
@@ -1576,27 +1612,28 @@ namespace transport
|
||||
(*buff)[1] = 0x01;
|
||||
(*buff)[2] = 0x00;
|
||||
|
||||
if(addrtype == eIP4Address)
|
||||
auto& ep = conn->GetRemoteEndpoint ();
|
||||
if(ep.address ().is_v4 ())
|
||||
{
|
||||
(*buff)[3] = 0x01;
|
||||
auto addrbytes = boost::asio::ip::address::from_string(host).to_v4().to_bytes();
|
||||
auto addrbytes = ep.address ().to_v4().to_bytes();
|
||||
sz += 4;
|
||||
memcpy(buff->data () + 4, addrbytes.data(), 4);
|
||||
}
|
||||
else if (addrtype == eIP6Address)
|
||||
else if (ep.address ().is_v6 ())
|
||||
{
|
||||
(*buff)[3] = 0x04;
|
||||
auto addrbytes = boost::asio::ip::address::from_string(host).to_v6().to_bytes();
|
||||
auto addrbytes = ep.address ().to_v6().to_bytes();
|
||||
sz += 16;
|
||||
memcpy(buff->data () + 4, addrbytes.data(), 16);
|
||||
}
|
||||
else if (addrtype == eHostname)
|
||||
else
|
||||
{
|
||||
// We mustn't really fall here because all connections are made to IP addresses
|
||||
LogPrint(eLogError, "NTCP2: Tried to connect to domain name via socks proxy");
|
||||
LogPrint(eLogError, "NTCP2: Tried to connect to unexpected address via proxy");
|
||||
return;
|
||||
}
|
||||
htobe16buf(buff->data () + sz - 2, port);
|
||||
htobe16buf(buff->data () + sz - 2, ep.port ());
|
||||
boost::asio::async_write(conn->GetSocket(), boost::asio::buffer(buff->data (), sz), boost::asio::transfer_all(),
|
||||
[buff](const boost::system::error_code & ec, std::size_t written)
|
||||
{
|
||||
@@ -1623,11 +1660,23 @@ namespace transport
|
||||
return;
|
||||
}
|
||||
}
|
||||
if(!e)
|
||||
i2p::data::netdb.SetUnreachable (conn->GetRemoteIdentity ()->GetIdentHash (), true);
|
||||
timer->cancel();
|
||||
conn->Terminate();
|
||||
});
|
||||
}
|
||||
|
||||
void NTCP2Server::SetLocalAddress (const boost::asio::ip::address& localAddress)
|
||||
{
|
||||
auto addr = std::make_shared<boost::asio::ip::tcp::endpoint>(boost::asio::ip::tcp::endpoint(localAddress, 0));
|
||||
if (localAddress.is_v6 ())
|
||||
{
|
||||
if (i2p::util::net::IsYggdrasilAddress (localAddress))
|
||||
m_YggdrasilAddress = addr;
|
||||
else
|
||||
m_Address6 = addr;
|
||||
}
|
||||
else
|
||||
m_Address4 = addr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -134,6 +134,8 @@ namespace transport
|
||||
void Close () { m_Socket.close (); }; // for accept
|
||||
|
||||
boost::asio::ip::tcp::socket& GetSocket () { return m_Socket; };
|
||||
const boost::asio::ip::tcp::endpoint& GetRemoteEndpoint () { return m_RemoteEndpoint; };
|
||||
void SetRemoteEndpoint (const boost::asio::ip::tcp::endpoint& ep) { m_RemoteEndpoint = ep; };
|
||||
|
||||
bool IsEstablished () const { return m_IsEstablished; };
|
||||
bool IsTerminated () const { return m_IsTerminated; };
|
||||
@@ -189,6 +191,7 @@ namespace transport
|
||||
|
||||
NTCP2Server& m_Server;
|
||||
boost::asio::ip::tcp::socket m_Socket;
|
||||
boost::asio::ip::tcp::endpoint m_RemoteEndpoint;
|
||||
bool m_IsEstablished, m_IsTerminated;
|
||||
|
||||
std::unique_ptr<NTCP2Establisher> m_Establisher;
|
||||
@@ -221,13 +224,6 @@ namespace transport
|
||||
{
|
||||
public:
|
||||
|
||||
enum RemoteAddressType
|
||||
{
|
||||
eIP4Address,
|
||||
eIP6Address,
|
||||
eHostname
|
||||
};
|
||||
|
||||
enum ProxyType
|
||||
{
|
||||
eNoProxy,
|
||||
@@ -246,23 +242,23 @@ namespace transport
|
||||
void RemoveNTCP2Session (std::shared_ptr<NTCP2Session> session);
|
||||
std::shared_ptr<NTCP2Session> FindNTCP2Session (const i2p::data::IdentHash& ident);
|
||||
|
||||
void ConnectWithProxy (const std::string& addr, uint16_t port, RemoteAddressType addrtype, std::shared_ptr<NTCP2Session> conn);
|
||||
void Connect(const boost::asio::ip::address & address, uint16_t port, std::shared_ptr<NTCP2Session> conn);
|
||||
|
||||
void AfterSocksHandshake(std::shared_ptr<NTCP2Session> conn, std::shared_ptr<boost::asio::deadline_timer> timer, const std::string & host, uint16_t port, RemoteAddressType addrtype);
|
||||
|
||||
void ConnectWithProxy (std::shared_ptr<NTCP2Session> conn);
|
||||
void Connect(std::shared_ptr<NTCP2Session> conn);
|
||||
|
||||
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);
|
||||
|
||||
private:
|
||||
|
||||
void HandleAccept (std::shared_ptr<NTCP2Session> conn, const boost::system::error_code& error);
|
||||
void HandleAcceptV6 (std::shared_ptr<NTCP2Session> conn, const boost::system::error_code& error);
|
||||
|
||||
void HandleConnect (const boost::system::error_code& ecode, std::shared_ptr<NTCP2Session> conn, std::shared_ptr<boost::asio::deadline_timer> timer);
|
||||
void HandleProxyConnect(const boost::system::error_code& ecode, std::shared_ptr<NTCP2Session> conn, std::shared_ptr<boost::asio::deadline_timer> timer, const std::string & host, uint16_t port, RemoteAddressType adddrtype);
|
||||
|
||||
void HandleProxyConnect(const boost::system::error_code& ecode, std::shared_ptr<NTCP2Session> conn, std::shared_ptr<boost::asio::deadline_timer> timer);
|
||||
void AfterSocksHandshake(std::shared_ptr<NTCP2Session> conn, std::shared_ptr<boost::asio::deadline_timer> timer);
|
||||
|
||||
// timer
|
||||
void ScheduleTermination ();
|
||||
void HandleTerminationTimer (const boost::system::error_code& ecode);
|
||||
@@ -275,11 +271,12 @@ namespace transport
|
||||
std::list<std::shared_ptr<NTCP2Session> > m_PendingIncomingSessions;
|
||||
|
||||
ProxyType m_ProxyType;
|
||||
std::string m_ProxyAddress;
|
||||
std::string m_ProxyAddress, m_ProxyAuthorization;
|
||||
uint16_t m_ProxyPort;
|
||||
boost::asio::ip::tcp::resolver m_Resolver;
|
||||
std::unique_ptr<boost::asio::ip::tcp::endpoint> m_ProxyEndpoint;
|
||||
|
||||
std::shared_ptr<boost::asio::ip::tcp::endpoint> m_Address4, m_Address6, m_YggdrasilAddress;
|
||||
|
||||
public:
|
||||
|
||||
// for HTTP/I2PControl
|
||||
|
||||
@@ -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
|
||||
*
|
||||
@@ -57,7 +57,7 @@ namespace data
|
||||
uint16_t threshold; i2p::config::GetOption("reseed.threshold", threshold);
|
||||
if (m_RouterInfos.size () < threshold) // reseed if # of router less than threshold
|
||||
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
|
||||
|
||||
i2p::config::GetOption("persist.profiles", m_PersistProfiles);
|
||||
@@ -336,7 +336,7 @@ namespace data
|
||||
if (it == m_LeaseSets.end () || it->second->GetStoreType () != storeType ||
|
||||
leaseSet->GetPublishedTimestamp () > it->second->GetPublishedTimestamp ())
|
||||
{
|
||||
if (leaseSet->IsPublic ())
|
||||
if (leaseSet->IsPublic () && !leaseSet->IsExpired ())
|
||||
{
|
||||
// TODO: implement actual update
|
||||
LogPrint (eLogInfo, "NetDb: LeaseSet2 updated: ", ident.ToBase32());
|
||||
@@ -345,7 +345,7 @@ namespace data
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint (eLogWarning, "NetDb: Unpublished LeaseSet2 received: ", ident.ToBase32());
|
||||
LogPrint (eLogWarning, "NetDb: Unpublished or expired LeaseSet2 received: ", ident.ToBase32());
|
||||
m_LeaseSets.erase (ident);
|
||||
}
|
||||
}
|
||||
@@ -454,7 +454,7 @@ namespace data
|
||||
{
|
||||
auto r = std::make_shared<RouterInfo>(path);
|
||||
if (r->GetRouterIdentity () && !r->IsUnreachable () &&
|
||||
(!r->UsesIntroducer () || m_LastLoad < r->GetTimestamp () + NETDB_INTRODUCEE_EXPIRATION_TIMEOUT*1000LL)) // 1 hour
|
||||
(r->IsReachable () || !r->IsSSU (false) || m_LastLoad < r->GetTimestamp () + NETDB_INTRODUCEE_EXPIRATION_TIMEOUT*1000LL)) // 1 hour
|
||||
{
|
||||
r->DeleteBuffer ();
|
||||
r->ClearProperties (); // properties are not used for regular routers
|
||||
@@ -584,7 +584,7 @@ namespace data
|
||||
if (it.second->IsUnreachable () && total - deletedCount < NETDB_MIN_ROUTERS)
|
||||
it.second->SetUnreachable (false);
|
||||
// find & mark expired routers
|
||||
if (it.second->UsesIntroducer ())
|
||||
if (!it.second->IsReachable () && it.second->IsSSU (false))
|
||||
{
|
||||
if (ts > it.second->GetTimestamp () + NETDB_INTRODUCEE_EXPIRATION_TIMEOUT*1000LL)
|
||||
// RouterInfo expires after 1 hour if uses introducer
|
||||
@@ -645,7 +645,9 @@ namespace data
|
||||
auto floodfill = GetClosestFloodfill (destination, dest->GetExcludedPeers ());
|
||||
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)
|
||||
transports.SendMessage (floodfill->GetIdentHash (), dest->CreateRequestMessage (floodfill->GetIdentHash ()));
|
||||
else
|
||||
@@ -1090,7 +1092,8 @@ namespace data
|
||||
LogPrint (eLogInfo, "NetDb: Publishing our RouterInfo to ", i2p::data::GetIdentHashAbbreviation(floodfill->GetIdentHash ()), ". reply token=", replyToken);
|
||||
m_PublishExcluded.insert (floodfill->GetIdentHash ());
|
||||
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
|
||||
transports.SendMessage (floodfill->GetIdentHash (), CreateDatabaseStoreMsg (i2p::context.GetSharedRouterInfo (), replyToken));
|
||||
else
|
||||
@@ -1135,22 +1138,24 @@ 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 (
|
||||
[compatibleWith](std::shared_ptr<const RouterInfo> router)->bool
|
||||
[compatibleWith, reverse](std::shared_ptr<const RouterInfo> router)->bool
|
||||
{
|
||||
return !router->IsHidden () && router != compatibleWith &&
|
||||
router->IsCompatible (*compatibleWith);
|
||||
(reverse ? compatibleWith->IsReachableFrom (*router) :
|
||||
router->IsReachableFrom (*compatibleWith));
|
||||
});
|
||||
}
|
||||
|
||||
std::shared_ptr<const RouterInfo> NetDb::GetRandomPeerTestRouter (bool v4only) const
|
||||
std::shared_ptr<const RouterInfo> NetDb::GetRandomPeerTestRouter (bool v4, const std::set<IdentHash>& excluded) const
|
||||
{
|
||||
return GetRandomRouter (
|
||||
[v4only](std::shared_ptr<const RouterInfo> router)->bool
|
||||
[v4, &excluded](std::shared_ptr<const RouterInfo> router)->bool
|
||||
{
|
||||
return !router->IsHidden () && router->IsPeerTesting () && router->IsSSU (v4only);
|
||||
return !router->IsHidden () && router->IsPeerTesting (v4) &&
|
||||
!excluded.count (router->GetIdentHash ());
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1163,22 +1168,24 @@ namespace data
|
||||
});
|
||||
}
|
||||
|
||||
std::shared_ptr<const RouterInfo> NetDb::GetRandomIntroducer () const
|
||||
std::shared_ptr<const RouterInfo> NetDb::GetRandomIntroducer (bool v4, const std::set<IdentHash>& excluded) const
|
||||
{
|
||||
return GetRandomRouter (
|
||||
[](std::shared_ptr<const RouterInfo> router)->bool
|
||||
[v4, &excluded](std::shared_ptr<const RouterInfo> router)->bool
|
||||
{
|
||||
return !router->IsHidden () && router->IsIntroducer ();
|
||||
return router->IsIntroducer (v4) && !excluded.count (router->GetIdentHash ()) &&
|
||||
!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 (
|
||||
[compatibleWith](std::shared_ptr<const RouterInfo> router)->bool
|
||||
[compatibleWith, reverse](std::shared_ptr<const RouterInfo> router)->bool
|
||||
{
|
||||
return !router->IsHidden () && router != compatibleWith &&
|
||||
router->IsCompatible (*compatibleWith) &&
|
||||
(reverse ? compatibleWith->IsReachableFrom (*router) :
|
||||
router->IsReachableFrom (*compatibleWith)) &&
|
||||
(router->GetCaps () & RouterInfo::eHighBandwidth) &&
|
||||
router->GetVersion () >= NETDB_MIN_HIGHBANDWIDTH_VERSION;
|
||||
});
|
||||
|
||||
@@ -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
|
||||
*
|
||||
@@ -83,11 +83,11 @@ namespace data
|
||||
void HandleDeliveryStatusMsg (std::shared_ptr<const I2NPMessage> msg);
|
||||
|
||||
std::shared_ptr<const RouterInfo> GetRandomRouter () const;
|
||||
std::shared_ptr<const RouterInfo> GetRandomRouter (std::shared_ptr<const RouterInfo> compatibleWith) const;
|
||||
std::shared_ptr<const RouterInfo> GetHighBandwidthRandomRouter (std::shared_ptr<const RouterInfo> compatibleWith) const;
|
||||
std::shared_ptr<const RouterInfo> GetRandomPeerTestRouter (bool v4only = true) 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, bool reverse) const;
|
||||
std::shared_ptr<const RouterInfo> GetRandomPeerTestRouter (bool v4, const std::set<IdentHash>& excluded) const;
|
||||
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 (bool v4, const std::set<IdentHash>& excluded) const;
|
||||
std::shared_ptr<const RouterInfo> GetClosestFloodfill (const IdentHash& destination, const std::set<IdentHash>& excluded, bool closeThanUsOnly = false) const;
|
||||
std::vector<IdentHash> GetClosestFloodfills (const IdentHash& destination, size_t num,
|
||||
std::set<IdentHash>& excluded, bool closeThanUsOnly = false) const;
|
||||
|
||||
@@ -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
|
||||
*
|
||||
@@ -574,9 +574,11 @@ namespace data
|
||||
proxyReq.method = "CONNECT";
|
||||
proxyReq.version = "HTTP/1.1";
|
||||
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;
|
||||
|
||||
std::ostream out(&writebuf);
|
||||
out << proxyReq.to_string();
|
||||
|
||||
@@ -676,8 +678,31 @@ namespace data
|
||||
// direct connection
|
||||
auto it = boost::asio::ip::tcp::resolver(service).resolve (
|
||||
boost::asio::ip::tcp::resolver::query (url.host, std::to_string(url.port)), ecode);
|
||||
if(!ecode)
|
||||
s.lowest_layer().connect (*it, ecode);
|
||||
if (!ecode)
|
||||
{
|
||||
bool connected = false;
|
||||
boost::asio::ip::tcp::resolver::iterator end;
|
||||
while (it != end)
|
||||
{
|
||||
boost::asio::ip::tcp::endpoint ep = *it;
|
||||
if ((ep.address ().is_v4 () && i2p::context.SupportsV4 ()) ||
|
||||
(ep.address ().is_v6 () && i2p::context.SupportsV6 ()))
|
||||
{
|
||||
s.lowest_layer().connect (ep, ecode);
|
||||
if (!ecode)
|
||||
{
|
||||
connected = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
it++;
|
||||
}
|
||||
if (!connected)
|
||||
{
|
||||
LogPrint(eLogError, "Reseed: Failed to connect to ", url.host);
|
||||
return "";
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!ecode)
|
||||
{
|
||||
|
||||
@@ -28,7 +28,7 @@ namespace i2p
|
||||
|
||||
RouterContext::RouterContext ():
|
||||
m_LastUpdateTime (0), m_AcceptsTunnels (true), m_IsFloodfill (false),
|
||||
m_ShareRatio (100), m_Status (eRouterStatusUnknown),
|
||||
m_ShareRatio (100), m_Status (eRouterStatusUnknown), m_StatusV6 (eRouterStatusUnknown),
|
||||
m_Error (eRouterErrorNone), m_NetID (I2PD_NET_ID)
|
||||
{
|
||||
}
|
||||
@@ -41,13 +41,15 @@ namespace i2p
|
||||
if (!Load ())
|
||||
CreateNewRouter ();
|
||||
m_Decryptor = m_Keys.CreateDecryptor (nullptr);
|
||||
m_TunnelDecryptor = m_Keys.CreateDecryptor (nullptr);
|
||||
UpdateRouterInfo ();
|
||||
if (IsECIES ())
|
||||
{
|
||||
{
|
||||
auto initState = new i2p::crypto::NoiseSymmetricState ();
|
||||
i2p::crypto::InitNoiseNState (*initState, GetIdentity ()->GetEncryptionPublicKey ());
|
||||
m_InitialNoiseState.reset (initState);
|
||||
}
|
||||
m_InitialNoiseState.reset (initState);
|
||||
m_ECIESSession = std::make_shared<i2p::garlic::RouterIncomingRatchetSession>(*initState);
|
||||
}
|
||||
}
|
||||
|
||||
void RouterContext::CreateNewRouter ()
|
||||
@@ -72,88 +74,100 @@ namespace i2p
|
||||
bool ipv6; i2p::config::GetOption("ipv6", ipv6);
|
||||
bool ssu; i2p::config::GetOption("ssu", ssu);
|
||||
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);
|
||||
std::string ifname; i2p::config::GetOption("ifname", ifname);
|
||||
std::string ifname4; i2p::config::GetOption("ifname4", ifname4);
|
||||
std::string ifname6; i2p::config::GetOption("ifname6", ifname6);
|
||||
uint8_t caps = 0;
|
||||
|
||||
if ((ntcp2 || ygg) && !m_NTCP2Keys)
|
||||
NewNTCP2Keys ();
|
||||
bool ntcp2Published = false;
|
||||
if (ntcp2)
|
||||
{
|
||||
i2p::config::GetOption("ntcp2.published", ntcp2Published);
|
||||
if (ntcp2Published)
|
||||
{
|
||||
std::string ntcp2proxy; i2p::config::GetOption("ntcp2.proxy", ntcp2proxy);
|
||||
if (!ntcp2proxy.empty ()) ntcp2Published = false;
|
||||
}
|
||||
}
|
||||
uint8_t caps = 0, addressCaps = 0;
|
||||
if (ipv4)
|
||||
{
|
||||
std::string host = "127.0.0.1";
|
||||
if (!i2p::config::IsDefault("host"))
|
||||
i2p::config::GetOption("host", host);
|
||||
else if (!nat && !ifname.empty())
|
||||
/* bind to interface, we have no NAT so set external address too */
|
||||
host = i2p::util::net::GetInterfaceAddress(ifname, false).to_string(); // v4
|
||||
|
||||
if(ifname4.size())
|
||||
host = i2p::util::net::GetInterfaceAddress(ifname4, false).to_string();
|
||||
else if (!nat)
|
||||
{
|
||||
// we have no NAT so set external address from local address
|
||||
std::string address4; i2p::config::GetOption("address4", address4);
|
||||
if (!address4.empty ()) host = address4;
|
||||
}
|
||||
|
||||
if (ntcp2)
|
||||
{
|
||||
if (ntcp2Published)
|
||||
routerInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv, boost::asio::ip::address_v4::from_string (host), port);
|
||||
else // add non-published NTCP2 address
|
||||
{
|
||||
addressCaps = i2p::data::RouterInfo::AddressCaps::eV4;
|
||||
routerInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv);
|
||||
}
|
||||
}
|
||||
if (ssu)
|
||||
{
|
||||
{
|
||||
routerInfo.AddSSUAddress (host.c_str(), port, nullptr);
|
||||
caps |= i2p::data::RouterInfo::eReachable | i2p::data::RouterInfo::eSSUTesting | i2p::data::RouterInfo::eSSUIntroducer; // R, BC
|
||||
}
|
||||
caps |= i2p::data::RouterInfo::eReachable; // R
|
||||
}
|
||||
}
|
||||
if (ipv6)
|
||||
{
|
||||
std::string host = "::1";
|
||||
if (!i2p::config::IsDefault("host") && !ipv4) // override if v6 only
|
||||
i2p::config::GetOption("host", host);
|
||||
else if (!ifname.empty())
|
||||
host = i2p::util::net::GetInterfaceAddress(ifname, true).to_string(); // v6
|
||||
|
||||
if(ifname6.size())
|
||||
host = i2p::util::net::GetInterfaceAddress(ifname6, true).to_string();
|
||||
|
||||
if (ssu)
|
||||
{
|
||||
routerInfo.AddSSUAddress (host.c_str(), port, nullptr);
|
||||
caps |= i2p::data::RouterInfo::eReachable; // R
|
||||
}
|
||||
}
|
||||
|
||||
routerInfo.SetCaps (caps); // caps + L
|
||||
routerInfo.SetProperty ("netId", std::to_string (m_NetID));
|
||||
routerInfo.SetProperty ("router.version", I2P_VERSION);
|
||||
routerInfo.CreateBuffer (m_Keys);
|
||||
m_RouterInfo.SetRouterIdentity (GetIdentity ());
|
||||
m_RouterInfo.Update (routerInfo.GetBuffer (), routerInfo.GetBufferLen ());
|
||||
|
||||
if (ntcp2) // we don't store iv in the address if non published so we must update it from keys
|
||||
{
|
||||
if (!m_NTCP2Keys) NewNTCP2Keys ();
|
||||
bool published; i2p::config::GetOption("ntcp2.published", published);
|
||||
if (ipv4 || !published) UpdateNTCP2Address (true); // create not published NTCP2 address
|
||||
if (published)
|
||||
else
|
||||
{
|
||||
if (ipv4)
|
||||
PublishNTCP2Address (port, true);
|
||||
if (ipv6)
|
||||
std::string address6; i2p::config::GetOption("address6", address6);
|
||||
if (!address6.empty ()) host = address6;
|
||||
}
|
||||
|
||||
if (ntcp2)
|
||||
{
|
||||
if (ntcp2Published)
|
||||
{
|
||||
// add NTCP2 ipv6 address
|
||||
std::string host = "::1";
|
||||
std::string ntcp2Host;
|
||||
if (!i2p::config::IsDefault ("ntcp2.addressv6"))
|
||||
i2p::config::GetOption ("ntcp2.addressv6", host);
|
||||
m_RouterInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv, boost::asio::ip::address_v6::from_string (host), port);
|
||||
i2p::config::GetOption ("ntcp2.addressv6", ntcp2Host);
|
||||
else
|
||||
ntcp2Host = host;
|
||||
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
|
||||
routerInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv);
|
||||
addressCaps |= i2p::data::RouterInfo::AddressCaps::eV6;
|
||||
}
|
||||
}
|
||||
// enable added NTCP2 addresses
|
||||
if (ipv4) m_RouterInfo.EnableV4 ();
|
||||
if (ipv6) m_RouterInfo.EnableV6 ();
|
||||
if (ssu)
|
||||
{
|
||||
routerInfo.AddSSUAddress (host.c_str(), port, nullptr);
|
||||
caps |= i2p::data::RouterInfo::eReachable; // R
|
||||
}
|
||||
}
|
||||
if (ygg)
|
||||
{
|
||||
auto yggaddr = i2p::util::net::GetYggdrasilAddress ();
|
||||
if (!yggaddr.is_unspecified ())
|
||||
{
|
||||
if (!m_NTCP2Keys) NewNTCP2Keys ();
|
||||
m_RouterInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv, yggaddr, port);
|
||||
m_RouterInfo.EnableMesh ();
|
||||
UpdateRouterInfo ();
|
||||
}
|
||||
}
|
||||
routerInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv, yggaddr, port);
|
||||
}
|
||||
|
||||
if (addressCaps)
|
||||
routerInfo.SetUnreachableAddressesTransportCaps (addressCaps);
|
||||
routerInfo.SetCaps (caps); // caps + L
|
||||
routerInfo.SetProperty ("netId", std::to_string (m_NetID));
|
||||
routerInfo.SetProperty ("router.version", I2P_VERSION);
|
||||
routerInfo.CreateBuffer (m_Keys);
|
||||
m_RouterInfo.SetRouterIdentity (GetIdentity ());
|
||||
m_RouterInfo.Update (routerInfo.GetBuffer (), routerInfo.GetBufferLen ());
|
||||
}
|
||||
|
||||
void RouterContext::UpdateRouterInfo ()
|
||||
@@ -185,10 +199,29 @@ namespace i2p
|
||||
switch (m_Status)
|
||||
{
|
||||
case eRouterStatusOK:
|
||||
SetReachable ();
|
||||
SetReachable (true, false); // ipv4
|
||||
break;
|
||||
case eRouterStatusFirewalled:
|
||||
SetUnreachable ();
|
||||
SetUnreachable (true, false); // ipv4
|
||||
break;
|
||||
default:
|
||||
;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RouterContext::SetStatusV6 (RouterStatus status)
|
||||
{
|
||||
if (status != m_StatusV6)
|
||||
{
|
||||
m_StatusV6 = status;
|
||||
switch (m_StatusV6)
|
||||
{
|
||||
case eRouterStatusOK:
|
||||
SetReachable (false, true); // ipv6
|
||||
break;
|
||||
case eRouterStatusFirewalled:
|
||||
SetUnreachable (false, true); // ipv6
|
||||
break;
|
||||
default:
|
||||
;
|
||||
@@ -211,25 +244,35 @@ namespace i2p
|
||||
UpdateRouterInfo ();
|
||||
}
|
||||
|
||||
void RouterContext::PublishNTCP2Address (int port, bool publish, bool v4only)
|
||||
void RouterContext::PublishNTCP2Address (int port, bool publish, bool v4, bool v6, bool ygg)
|
||||
{
|
||||
if (!m_NTCP2Keys) return;
|
||||
bool updated = false;
|
||||
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->published != publish))
|
||||
{
|
||||
if (!port && !address->port)
|
||||
bool isAddr = v4 && address->IsV4 ();
|
||||
if (!isAddr && (v6 || ygg))
|
||||
{
|
||||
// select random port only if address's port is not set
|
||||
port = rand () % (30777 - 9111) + 9111; // I2P network ports range
|
||||
if (port == 9150) port = 9151; // Tor browser
|
||||
if (i2p::util::net::IsYggdrasilAddress (address->host))
|
||||
isAddr = ygg;
|
||||
else
|
||||
isAddr = v6 && address->IsV6 ();
|
||||
}
|
||||
if (isAddr)
|
||||
{
|
||||
if (!port && !address->port)
|
||||
{
|
||||
// select random port only if address's port is not set
|
||||
port = rand () % (30777 - 9111) + 9111; // I2P network ports range
|
||||
if (port == 9150) port = 9151; // Tor browser
|
||||
}
|
||||
if (port) address->port = port;
|
||||
address->published = publish;
|
||||
address->ntcp2->iv = m_NTCP2Keys->iv;
|
||||
updated = true;
|
||||
}
|
||||
if (port) address->port = port;
|
||||
address->cost = publish ? 3 : 14;
|
||||
address->ntcp2->isPublished = publish;
|
||||
address->ntcp2->iv = m_NTCP2Keys->iv;
|
||||
updated = true;
|
||||
}
|
||||
}
|
||||
if (updated)
|
||||
@@ -267,7 +310,7 @@ namespace i2p
|
||||
bool updated = false;
|
||||
for (auto& address : m_RouterInfo.GetAddresses ())
|
||||
{
|
||||
if (address->host != host && address->IsCompatible (host) &&
|
||||
if (address->host != host && address->IsCompatible (host) &&
|
||||
!i2p::util::net::IsYggdrasilAddress (address->host))
|
||||
{
|
||||
address->host = host;
|
||||
@@ -359,7 +402,7 @@ namespace i2p
|
||||
case i2p::data::CAPS_FLAG_EXTRA_BANDWIDTH1 : limit = 2048; type = extra; break;
|
||||
case i2p::data::CAPS_FLAG_EXTRA_BANDWIDTH2 : limit = 1000000; type = unlim; break; // 1Gbyte/s
|
||||
default:
|
||||
limit = 48; type = low;
|
||||
limit = 48; type = low;
|
||||
}
|
||||
/* update caps & flags in RI */
|
||||
auto caps = m_RouterInfo.GetCaps ();
|
||||
@@ -373,8 +416,8 @@ namespace i2p
|
||||
#if (__cplusplus >= 201703L) // C++ 17 or higher
|
||||
[[fallthrough]];
|
||||
#endif
|
||||
// no break here, extra + high means 'X'
|
||||
case high : caps |= i2p::data::RouterInfo::eHighBandwidth; break;
|
||||
// no break here, extra + high means 'X'
|
||||
case high : caps |= i2p::data::RouterInfo::eHighBandwidth; break;
|
||||
}
|
||||
m_RouterInfo.SetCaps (caps);
|
||||
UpdateRouterInfo ();
|
||||
@@ -390,6 +433,7 @@ namespace i2p
|
||||
else if (limit > 48) { SetBandwidth('M'); }
|
||||
else if (limit > 12) { SetBandwidth('L'); }
|
||||
else { SetBandwidth('K'); }
|
||||
m_BandwidthLimit = limit; // set precise limit
|
||||
}
|
||||
|
||||
void RouterContext::SetShareRatio (int percents)
|
||||
@@ -420,52 +464,60 @@ namespace i2p
|
||||
}
|
||||
}
|
||||
|
||||
void RouterContext::SetUnreachable ()
|
||||
void RouterContext::SetUnreachable (bool v4, bool v6)
|
||||
{
|
||||
// set caps
|
||||
uint8_t caps = m_RouterInfo.GetCaps ();
|
||||
caps &= ~i2p::data::RouterInfo::eReachable;
|
||||
caps |= i2p::data::RouterInfo::eUnreachable;
|
||||
caps &= ~i2p::data::RouterInfo::eFloodfill; // can't be floodfill
|
||||
caps &= ~i2p::data::RouterInfo::eSSUIntroducer; // can't be introducer
|
||||
m_RouterInfo.SetCaps (caps);
|
||||
if (v4 || (v6 && !SupportsV4 ()))
|
||||
{
|
||||
// set caps
|
||||
uint8_t caps = m_RouterInfo.GetCaps ();
|
||||
caps &= ~i2p::data::RouterInfo::eReachable;
|
||||
caps |= i2p::data::RouterInfo::eUnreachable;
|
||||
caps &= ~i2p::data::RouterInfo::eFloodfill; // can't be floodfill
|
||||
m_RouterInfo.SetCaps (caps);
|
||||
}
|
||||
uint16_t port = 0;
|
||||
// delete previous introducers
|
||||
auto& addresses = m_RouterInfo.GetAddresses ();
|
||||
for (auto& addr : addresses)
|
||||
if (addr->ssu)
|
||||
if (addr->ssu && ((v4 && addr->IsV4 ()) || (v6 && addr->IsV6 ())))
|
||||
{
|
||||
addr->published = false;
|
||||
addr->caps &= ~i2p::data::RouterInfo::eSSUIntroducer; // can't be introducer
|
||||
addr->ssu->introducers.clear ();
|
||||
port = addr->port;
|
||||
}
|
||||
// remove NTCP2 v4 address
|
||||
// unpiblish NTCP2 addreeses
|
||||
bool ntcp2; i2p::config::GetOption("ntcp2.enabled", ntcp2);
|
||||
if (ntcp2)
|
||||
PublishNTCP2Address (port, false, true);
|
||||
PublishNTCP2Address (port, false, v4, v6, false);
|
||||
// update
|
||||
UpdateRouterInfo ();
|
||||
}
|
||||
|
||||
void RouterContext::SetReachable ()
|
||||
void RouterContext::SetReachable (bool v4, bool v6)
|
||||
{
|
||||
// update caps
|
||||
uint8_t caps = m_RouterInfo.GetCaps ();
|
||||
caps &= ~i2p::data::RouterInfo::eUnreachable;
|
||||
caps |= i2p::data::RouterInfo::eReachable;
|
||||
caps |= i2p::data::RouterInfo::eSSUIntroducer;
|
||||
if (m_IsFloodfill)
|
||||
caps |= i2p::data::RouterInfo::eFloodfill;
|
||||
m_RouterInfo.SetCaps (caps);
|
||||
if (v4 || (v6 && !SupportsV4 ()))
|
||||
{
|
||||
// update caps
|
||||
uint8_t caps = m_RouterInfo.GetCaps ();
|
||||
caps &= ~i2p::data::RouterInfo::eUnreachable;
|
||||
caps |= i2p::data::RouterInfo::eReachable;
|
||||
if (m_IsFloodfill)
|
||||
caps |= i2p::data::RouterInfo::eFloodfill;
|
||||
m_RouterInfo.SetCaps (caps);
|
||||
}
|
||||
uint16_t port = 0;
|
||||
// delete previous introducers
|
||||
auto& addresses = m_RouterInfo.GetAddresses ();
|
||||
for (auto& addr : addresses)
|
||||
if (addr->ssu)
|
||||
if (addr->ssu && ((v4 && addr->IsV4 ()) || (v6 && addr->IsV6 ())))
|
||||
{
|
||||
addr->published = true;
|
||||
addr->caps |= i2p::data::RouterInfo::eSSUIntroducer;
|
||||
addr->ssu->introducers.clear ();
|
||||
port = addr->port;
|
||||
}
|
||||
// insert NTCP2 back
|
||||
// publish NTCP2
|
||||
bool ntcp2; i2p::config::GetOption("ntcp2.enabled", ntcp2);
|
||||
if (ntcp2)
|
||||
{
|
||||
@@ -474,7 +526,7 @@ namespace i2p
|
||||
{
|
||||
uint16_t ntcp2Port; i2p::config::GetOption ("ntcp2.port", ntcp2Port);
|
||||
if (!ntcp2Port) ntcp2Port = port;
|
||||
PublishNTCP2Address (ntcp2Port, true, true);
|
||||
PublishNTCP2Address (ntcp2Port, true, v4, v6, false);
|
||||
}
|
||||
}
|
||||
// update
|
||||
@@ -485,18 +537,17 @@ namespace i2p
|
||||
{
|
||||
if (supportsV6)
|
||||
{
|
||||
m_RouterInfo.EnableV6 ();
|
||||
// insert v6 addresses if necessary
|
||||
bool foundSSU = false, foundNTCP2 = false;
|
||||
uint16_t port = 0;
|
||||
auto& addresses = m_RouterInfo.GetAddresses ();
|
||||
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)
|
||||
foundSSU = true;
|
||||
else if (addr->IsPublishedNTCP2 ())
|
||||
else if (addr->transportStyle == i2p::data::RouterInfo::eTransportNTCP)
|
||||
foundNTCP2 = true;
|
||||
}
|
||||
port = addr->port;
|
||||
@@ -529,6 +580,7 @@ namespace i2p
|
||||
m_RouterInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv, boost::asio::ip::address::from_string (ntcp2Host), ntcp2Port);
|
||||
}
|
||||
}
|
||||
m_RouterInfo.EnableV6 ();
|
||||
}
|
||||
else
|
||||
m_RouterInfo.DisableV6 ();
|
||||
@@ -537,17 +589,63 @@ namespace i2p
|
||||
|
||||
void RouterContext::SetSupportsV4 (bool supportsV4)
|
||||
{
|
||||
// check if updates
|
||||
if (supportsV4 && SupportsV4 ()) return;
|
||||
if (!supportsV4 && !SupportsV4 ()) return;
|
||||
// update
|
||||
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 ();
|
||||
}
|
||||
else
|
||||
m_RouterInfo.DisableV4 ();
|
||||
UpdateRouterInfo ();
|
||||
}
|
||||
|
||||
void RouterContext::SetSupportsMesh (bool supportsmesh, const boost::asio::ip::address_v6& host)
|
||||
{
|
||||
{
|
||||
if (supportsmesh)
|
||||
{
|
||||
{
|
||||
m_RouterInfo.EnableMesh ();
|
||||
uint16_t port = 0;
|
||||
i2p::config::GetOption ("ntcp2.port", port);
|
||||
@@ -561,16 +659,16 @@ namespace i2p
|
||||
{
|
||||
foundMesh = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!foundMesh)
|
||||
m_RouterInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv, host, port);
|
||||
}
|
||||
}
|
||||
else
|
||||
m_RouterInfo.DisableMesh ();
|
||||
UpdateRouterInfo ();
|
||||
}
|
||||
|
||||
|
||||
void RouterContext::UpdateNTCP2V6Address (const boost::asio::ip::address& host)
|
||||
{
|
||||
bool isYgg = i2p::util::net::IsYggdrasilAddress (host);
|
||||
@@ -638,7 +736,14 @@ namespace i2p
|
||||
}
|
||||
}
|
||||
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
|
||||
bool isFloodfill; i2p::config::GetOption("floodfill", isFloodfill);
|
||||
if (!isFloodfill) rekey = true;
|
||||
}
|
||||
if (rekey)
|
||||
{
|
||||
// update keys
|
||||
LogPrint (eLogInfo, "Router: router keys are obsolete. Creating new");
|
||||
@@ -646,7 +751,7 @@ namespace i2p
|
||||
m_Keys = i2p::data::PrivateKeys::CreateRandomKeys (i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519,
|
||||
i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD);
|
||||
SaveKeys ();
|
||||
}
|
||||
}
|
||||
// read NTCP2 keys if available
|
||||
std::ifstream n2k (i2p::fs::DataDirPath (NTCP2_KEYS), std::ifstream::in | std::ifstream::binary);
|
||||
if (n2k)
|
||||
@@ -669,8 +774,8 @@ namespace i2p
|
||||
m_RouterInfo.Update (routerInfo.GetBuffer (), routerInfo.GetBufferLen ());
|
||||
if (oldIdentity)
|
||||
m_RouterInfo.SetRouterIdentity (GetIdentity ()); // from new keys
|
||||
m_RouterInfo.SetProperty ("coreVersion", I2P_VERSION);
|
||||
m_RouterInfo.SetProperty ("router.version", I2P_VERSION);
|
||||
m_RouterInfo.DeleteProperty ("coreVersion"); // TODO: remove later
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -679,11 +784,11 @@ namespace i2p
|
||||
}
|
||||
|
||||
if (IsUnreachable ())
|
||||
SetReachable (); // we assume reachable until we discover firewall through peer tests
|
||||
SetReachable (true, true); // we assume reachable until we discover firewall through peer tests
|
||||
|
||||
// read NTCP2
|
||||
bool ntcp2; i2p::config::GetOption("ntcp2.enabled", ntcp2);
|
||||
bool ygg; i2p::config::GetOption("meshnets.yggdrasil", ygg);
|
||||
bool ntcp2; i2p::config::GetOption("ntcp2.enabled", ntcp2);
|
||||
bool ygg; i2p::config::GetOption("meshnets.yggdrasil", ygg);
|
||||
if (ntcp2 || ygg)
|
||||
{
|
||||
if (!m_NTCP2Keys) NewNTCP2Keys ();
|
||||
@@ -716,15 +821,15 @@ namespace i2p
|
||||
i2p::HandleI2NPMessage (CreateI2NPMessage (buf, GetI2NPMessageLength (buf, len)));
|
||||
}
|
||||
|
||||
bool RouterContext::HandleCloveI2NPMessage (I2NPMessageType typeID, const uint8_t * payload, size_t len)
|
||||
{
|
||||
bool RouterContext::HandleCloveI2NPMessage (I2NPMessageType typeID, const uint8_t * payload, size_t len)
|
||||
{
|
||||
auto msg = CreateI2NPMessage (typeID, payload, len);
|
||||
if (!msg) return false;
|
||||
i2p::HandleI2NPMessage (msg);
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void RouterContext::ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg)
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_GarlicMutex);
|
||||
@@ -738,10 +843,12 @@ namespace i2p
|
||||
return;
|
||||
}
|
||||
buf += 4;
|
||||
auto session = std::make_shared<i2p::garlic::ECIESX25519AEADRatchetSession>(this, false);
|
||||
session->HandleNextMessageForRouter (buf, len);
|
||||
}
|
||||
else
|
||||
if (m_ECIESSession)
|
||||
m_ECIESSession->HandleNextMessage (buf, len);
|
||||
else
|
||||
LogPrint (eLogError, "Router: Session is not set for ECIES router");
|
||||
}
|
||||
else
|
||||
i2p::garlic::GarlicDestination::ProcessGarlicMessage (msg);
|
||||
}
|
||||
|
||||
@@ -750,10 +857,10 @@ namespace i2p
|
||||
if (i2p::data::netdb.GetPublishReplyToken () == bufbe32toh (msg->GetPayload () + DELIVERY_STATUS_MSGID_OFFSET))
|
||||
i2p::data::netdb.PostI2NPMsg (msg);
|
||||
else
|
||||
{
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_GarlicMutex);
|
||||
i2p::garlic::GarlicDestination::ProcessDeliveryStatusMessage (msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RouterContext::CleanupDestination ()
|
||||
@@ -772,36 +879,41 @@ namespace i2p
|
||||
return m_Decryptor ? m_Decryptor->Decrypt (encrypted, data, ctx, true) : false;
|
||||
}
|
||||
|
||||
bool RouterContext::DecryptTunnelBuildRecord (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx)
|
||||
bool RouterContext::DecryptTunnelBuildRecord (const uint8_t * encrypted, uint8_t * data)
|
||||
{
|
||||
if (!m_Decryptor) return false;
|
||||
if (!m_TunnelDecryptor) return false;
|
||||
if (IsECIES ())
|
||||
{
|
||||
if (!m_InitialNoiseState) return false;
|
||||
// m_InitialNoiseState is h = SHA256(h || hepk)
|
||||
m_CurrentNoiseState.reset (new i2p::crypto::NoiseSymmetricState (*m_InitialNoiseState));
|
||||
m_CurrentNoiseState.reset (new i2p::crypto::NoiseSymmetricState (*m_InitialNoiseState));
|
||||
m_CurrentNoiseState->MixHash (encrypted, 32); // h = SHA256(h || sepk)
|
||||
uint8_t sharedSecret[32];
|
||||
if (!m_Decryptor->Decrypt (encrypted, sharedSecret, ctx, false))
|
||||
if (!m_TunnelDecryptor->Decrypt (encrypted, sharedSecret, nullptr, false))
|
||||
{
|
||||
LogPrint (eLogWarning, "Router: Incorrect ephemeral public key");
|
||||
return false;
|
||||
}
|
||||
m_CurrentNoiseState->MixKey (sharedSecret);
|
||||
}
|
||||
m_CurrentNoiseState->MixKey (sharedSecret);
|
||||
encrypted += 32;
|
||||
uint8_t nonce[12];
|
||||
memset (nonce, 0, 12);
|
||||
if (!i2p::crypto::AEADChaCha20Poly1305 (encrypted, ECIES_BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE,
|
||||
if (!i2p::crypto::AEADChaCha20Poly1305 (encrypted, ECIES_BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE,
|
||||
m_CurrentNoiseState->m_H, 32, m_CurrentNoiseState->m_CK + 32, nonce, data, ECIES_BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE, false)) // decrypt
|
||||
{
|
||||
LogPrint (eLogWarning, "Router: Tunnel record AEAD decryption failed");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
m_CurrentNoiseState->MixHash (encrypted, ECIES_BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE + 16); // h = SHA256(h || ciphertext)
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return m_Decryptor->Decrypt (encrypted, data, ctx, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
BN_CTX * ctx = BN_CTX_new ();
|
||||
bool success = m_TunnelDecryptor->Decrypt (encrypted, data, ctx, false);
|
||||
BN_CTX_free (ctx);
|
||||
return success;
|
||||
}
|
||||
}
|
||||
|
||||
i2p::crypto::X25519Keys& RouterContext::GetStaticKeys ()
|
||||
|
||||
@@ -21,6 +21,11 @@
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace garlic
|
||||
{
|
||||
class RouterIncomingRatchetSession;
|
||||
}
|
||||
|
||||
const char ROUTER_INFO[] = "router.info";
|
||||
const char ROUTER_KEYS[] = "router.keys";
|
||||
const char NTCP2_KEYS[] = "ntcp2.keys";
|
||||
@@ -32,16 +37,19 @@ namespace i2p
|
||||
eRouterStatusTesting = 1,
|
||||
eRouterStatusFirewalled = 2,
|
||||
eRouterStatusError = 3,
|
||||
eRouterStatusUnknown = 4
|
||||
eRouterStatusUnknown = 4,
|
||||
eRouterStatusProxy = 5,
|
||||
eRouterStatusMesh = 6
|
||||
};
|
||||
|
||||
enum RouterError
|
||||
{
|
||||
eRouterErrorNone = 0,
|
||||
eRouterErrorClockSkew = 1,
|
||||
eRouterErrorOffline = 2
|
||||
eRouterErrorOffline = 2,
|
||||
eRouterErrorSymmetricNAT = 3
|
||||
};
|
||||
|
||||
|
||||
class RouterContext: public i2p::garlic::GarlicDestination
|
||||
{
|
||||
private:
|
||||
@@ -83,20 +91,22 @@ namespace i2p
|
||||
void SetStatus (RouterStatus status);
|
||||
RouterError GetError () const { return m_Error; };
|
||||
void SetError (RouterError error) { m_Status = eRouterStatusError; m_Error = error; };
|
||||
RouterStatus GetStatusV6 () const { return m_StatusV6; };
|
||||
void SetStatusV6 (RouterStatus status);
|
||||
int GetNetID () const { return m_NetID; };
|
||||
void SetNetID (int netID) { m_NetID = netID; };
|
||||
bool DecryptTunnelBuildRecord (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx);
|
||||
bool DecryptTunnelBuildRecord (const uint8_t * encrypted, uint8_t * data);
|
||||
|
||||
void UpdatePort (int port); // called from Daemon
|
||||
void UpdateAddress (const boost::asio::ip::address& host); // called from SSU or Daemon
|
||||
void PublishNTCP2Address (int port, bool publish = true, bool v4only = false);
|
||||
void PublishNTCP2Address (int port, bool publish, bool v4, bool v6, bool ygg);
|
||||
void UpdateNTCP2Address (bool enable);
|
||||
void RemoveNTCPAddress (bool v4only = true); // delete NTCP address for older routers. TODO: remove later
|
||||
bool AddIntroducer (const i2p::data::RouterInfo::Introducer& introducer);
|
||||
void RemoveIntroducer (const boost::asio::ip::udp::endpoint& e);
|
||||
bool IsUnreachable () const;
|
||||
void SetUnreachable ();
|
||||
void SetReachable ();
|
||||
void SetUnreachable (bool v4, bool v6);
|
||||
void SetReachable (bool v4, bool v6);
|
||||
bool IsFloodfill () const { return m_IsFloodfill; };
|
||||
void SetFloodfill (bool floodfill);
|
||||
void SetFamily (const std::string& family);
|
||||
@@ -153,13 +163,14 @@ namespace i2p
|
||||
|
||||
i2p::data::RouterInfo m_RouterInfo;
|
||||
i2p::data::PrivateKeys m_Keys;
|
||||
std::shared_ptr<i2p::crypto::CryptoKeyDecryptor> m_Decryptor;
|
||||
std::shared_ptr<i2p::crypto::CryptoKeyDecryptor> m_Decryptor, m_TunnelDecryptor;
|
||||
std::shared_ptr<i2p::garlic::RouterIncomingRatchetSession> m_ECIESSession;
|
||||
uint64_t m_LastUpdateTime; // in seconds
|
||||
bool m_AcceptsTunnels, m_IsFloodfill;
|
||||
std::chrono::time_point<std::chrono::steady_clock> m_StartupTime;
|
||||
uint64_t m_BandwidthLimit; // allowed bandwidth
|
||||
int m_ShareRatio;
|
||||
RouterStatus m_Status;
|
||||
RouterStatus m_Status, m_StatusV6;
|
||||
RouterError m_Error;
|
||||
int m_NetID;
|
||||
std::mutex m_GarlicMutex;
|
||||
|
||||
@@ -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
|
||||
*
|
||||
@@ -186,18 +186,19 @@ namespace data
|
||||
|
||||
void RouterInfo::ReadFromStream (std::istream& s)
|
||||
{
|
||||
m_Caps = 0;
|
||||
s.read ((char *)&m_Timestamp, sizeof (m_Timestamp));
|
||||
m_Timestamp = be64toh (m_Timestamp);
|
||||
// read addresses
|
||||
auto addresses = boost::make_shared<Addresses>();
|
||||
uint8_t numAddresses;
|
||||
s.read ((char *)&numAddresses, sizeof (numAddresses)); if (!s) return;
|
||||
bool introducers = false;
|
||||
for (int i = 0; i < numAddresses; i++)
|
||||
{
|
||||
uint8_t supportedTransports = 0;
|
||||
auto address = std::make_shared<Address>();
|
||||
s.read ((char *)&address->cost, sizeof (address->cost));
|
||||
uint8_t cost; // ignore
|
||||
s.read ((char *)&cost, sizeof (cost));
|
||||
s.read ((char *)&address->date, sizeof (address->date));
|
||||
bool isHost = false, isIntroKey = false, isStaticKey = false;
|
||||
char transportStyle[6];
|
||||
@@ -215,6 +216,7 @@ namespace data
|
||||
}
|
||||
else
|
||||
address->transportStyle = eTransportUnknown;
|
||||
address->caps = 0;
|
||||
address->port = 0;
|
||||
uint16_t size, r = 0;
|
||||
s.read ((char *)&size, sizeof (size)); if (!s) return;
|
||||
@@ -250,7 +252,7 @@ namespace data
|
||||
LogPrint (eLogWarning, "RouterInfo: Unexpected field 'key' for NTCP");
|
||||
}
|
||||
else if (!strcmp (key, "caps"))
|
||||
ExtractCaps (value);
|
||||
address->caps = ExtractAddressCaps (value);
|
||||
else if (!strcmp (key, "s")) // ntcp2 static key
|
||||
{
|
||||
Base64ToByteStream (value, strlen (value), address->ntcp2->staticKey, 32);
|
||||
@@ -259,7 +261,7 @@ namespace data
|
||||
else if (!strcmp (key, "i")) // ntcp2 iv
|
||||
{
|
||||
Base64ToByteStream (value, strlen (value), address->ntcp2->iv, 16);
|
||||
address->ntcp2->isPublished = true; // presence if "i" means "published"
|
||||
address->published = true; // presence if "i" means "published"
|
||||
}
|
||||
else if (key[0] == 'i')
|
||||
{
|
||||
@@ -269,7 +271,6 @@ namespace data
|
||||
LogPrint (eLogError, "RouterInfo: Introducer is presented for non-SSU address. Skipped");
|
||||
continue;
|
||||
}
|
||||
introducers = true;
|
||||
size_t l = strlen(key);
|
||||
unsigned char index = key[l-1] - '0'; // TODO:
|
||||
key[l-1] = 0;
|
||||
@@ -308,8 +309,16 @@ namespace data
|
||||
else
|
||||
supportedTransports |= eNTCP2V4;
|
||||
}
|
||||
else if (!address->ntcp2->isPublished)
|
||||
supportedTransports |= eNTCP2V4; // most likely, since we don't have host
|
||||
else if (!address->published)
|
||||
{
|
||||
if (address->caps)
|
||||
{
|
||||
if (address->caps & AddressCaps::eV4) supportedTransports |= eNTCP2V4;
|
||||
if (address->caps & AddressCaps::eV6) supportedTransports |= eNTCP2V6;
|
||||
}
|
||||
else
|
||||
supportedTransports |= eNTCP2V4; // most likely, since we don't have host
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (address->transportStyle == eTransportSSU)
|
||||
@@ -318,8 +327,30 @@ namespace data
|
||||
{
|
||||
if (isHost)
|
||||
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
|
||||
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 (address->ssu && !address->ssu->introducers.empty ())
|
||||
{
|
||||
// exclude invalid introducers
|
||||
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
|
||||
int numValid = 0;
|
||||
for (auto& it: address->ssu->introducers)
|
||||
{
|
||||
if ((!it.iExp || ts <= it.iExp) && it.iPort > 0 &&
|
||||
((it.iHost.is_v4 () && address->IsV4 ()) || (it.iHost.is_v6 () && address->IsV6 ())))
|
||||
numValid++;
|
||||
else
|
||||
it.iPort = 0;
|
||||
}
|
||||
if (!numValid) address->ssu->introducers.resize (0);
|
||||
}
|
||||
else if (isHost && address->port)
|
||||
address->published = true;
|
||||
}
|
||||
}
|
||||
if (supportedTransports)
|
||||
@@ -393,7 +424,7 @@ namespace data
|
||||
if (!s) return;
|
||||
}
|
||||
|
||||
if (!m_SupportedTransports || !m_Addresses->size() || (UsesIntroducer () && !introducers))
|
||||
if (!m_SupportedTransports)
|
||||
SetUnreachable (true);
|
||||
}
|
||||
|
||||
@@ -430,18 +461,39 @@ namespace data
|
||||
case CAPS_FLAG_UNREACHABLE:
|
||||
m_Caps |= Caps::eUnreachable;
|
||||
break;
|
||||
case CAPS_FLAG_SSU_TESTING:
|
||||
m_Caps |= Caps::eSSUTesting;
|
||||
break;
|
||||
case CAPS_FLAG_SSU_INTRODUCER:
|
||||
m_Caps |= Caps::eSSUIntroducer;
|
||||
break;
|
||||
default: ;
|
||||
}
|
||||
cap++;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t RouterInfo::ExtractAddressCaps (const char * value) const
|
||||
{
|
||||
uint8_t caps = 0;
|
||||
const char * cap = value;
|
||||
while (*cap)
|
||||
{
|
||||
switch (*cap)
|
||||
{
|
||||
case CAPS_FLAG_V4:
|
||||
caps |= AddressCaps::eV4;
|
||||
break;
|
||||
case CAPS_FLAG_V6:
|
||||
caps |= AddressCaps::eV6;
|
||||
break;
|
||||
case CAPS_FLAG_SSU_TESTING:
|
||||
caps |= AddressCaps::eSSUTesting;
|
||||
break;
|
||||
case CAPS_FLAG_SSU_INTRODUCER:
|
||||
caps |= AddressCaps::eSSUIntroducer;
|
||||
break;
|
||||
default: ;
|
||||
}
|
||||
cap++;
|
||||
}
|
||||
return caps;
|
||||
}
|
||||
|
||||
void RouterInfo::UpdateCapsProperty ()
|
||||
{
|
||||
std::string caps;
|
||||
@@ -479,13 +531,35 @@ namespace data
|
||||
for (const auto& addr_ptr : *m_Addresses)
|
||||
{
|
||||
const Address& address = *addr_ptr;
|
||||
s.write ((const char *)&address.cost, sizeof (address.cost));
|
||||
// calculate cost
|
||||
uint8_t cost = 0x7f;
|
||||
if (address.transportStyle == eTransportNTCP)
|
||||
cost = address.published ? COST_NTCP2_PUBLISHED : COST_NTCP2_NON_PUBLISHED;
|
||||
else if (address.transportStyle == eTransportSSU)
|
||||
cost = address.published ? COST_SSU_DIRECT : COST_SSU_THROUGH_INTRODUCERS;
|
||||
s.write ((const char *)&cost, sizeof (cost));
|
||||
s.write ((const char *)&address.date, sizeof (address.date));
|
||||
std::stringstream properties;
|
||||
bool isPublished = false;
|
||||
if (address.transportStyle == eTransportNTCP)
|
||||
{
|
||||
if (address.IsNTCP2 ())
|
||||
{
|
||||
WriteString ("NTCP2", s);
|
||||
if (address.IsPublishedNTCP2 () && !address.host.is_unspecified ())
|
||||
isPublished = true;
|
||||
else
|
||||
{
|
||||
WriteString ("caps", properties);
|
||||
properties << '=';
|
||||
std::string caps;
|
||||
if (address.IsV4 ()) caps += CAPS_FLAG_V4;
|
||||
if (address.IsV6 ()) caps += CAPS_FLAG_V6;
|
||||
if (caps.empty ()) caps += CAPS_FLAG_V4;
|
||||
WriteString (caps, properties);
|
||||
properties << ';';
|
||||
}
|
||||
}
|
||||
else
|
||||
continue; // don't write NTCP address
|
||||
}
|
||||
@@ -495,16 +569,41 @@ namespace data
|
||||
// caps
|
||||
WriteString ("caps", properties);
|
||||
properties << '=';
|
||||
std::string caps;
|
||||
if (IsPeerTesting ()) caps += CAPS_FLAG_SSU_TESTING;
|
||||
if (IsIntroducer ()) caps += CAPS_FLAG_SSU_INTRODUCER;
|
||||
std::string caps;
|
||||
if (address.IsPeerTesting ()) caps += CAPS_FLAG_SSU_TESTING;
|
||||
if (address.host.is_v4 ())
|
||||
{
|
||||
if (address.published)
|
||||
{
|
||||
isPublished = true;
|
||||
if (address.IsIntroducer ()) caps += CAPS_FLAG_SSU_INTRODUCER;
|
||||
}
|
||||
else
|
||||
caps += CAPS_FLAG_V4;
|
||||
}
|
||||
else if (address.host.is_v6 ())
|
||||
{
|
||||
if (address.published)
|
||||
{
|
||||
isPublished = true;
|
||||
if (address.IsIntroducer ()) caps += CAPS_FLAG_SSU_INTRODUCER;
|
||||
}
|
||||
else
|
||||
caps += CAPS_FLAG_V6;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (address.IsV4 ()) caps += CAPS_FLAG_V4;
|
||||
if (address.IsV6 ()) caps += CAPS_FLAG_V6;
|
||||
if (caps.empty ()) caps += CAPS_FLAG_V4;
|
||||
}
|
||||
WriteString (caps, properties);
|
||||
properties << ';';
|
||||
}
|
||||
else
|
||||
WriteString ("", s);
|
||||
|
||||
if (!address.IsNTCP2 () || address.IsPublishedNTCP2 ())
|
||||
if (isPublished)
|
||||
{
|
||||
WriteString ("host", properties);
|
||||
properties << '=';
|
||||
@@ -514,10 +613,22 @@ namespace data
|
||||
if (address.transportStyle == eTransportSSU)
|
||||
{
|
||||
// write introducers if any
|
||||
if (address.ssu->introducers.size () > 0)
|
||||
if (!address.ssu->introducers.empty())
|
||||
{
|
||||
int i = 0;
|
||||
for (const auto& introducer: address.ssu->introducers)
|
||||
{
|
||||
if (introducer.iExp) // expiration is specified
|
||||
{
|
||||
WriteString ("iexp" + boost::lexical_cast<std::string>(i), properties);
|
||||
properties << '=';
|
||||
WriteString (boost::lexical_cast<std::string>(introducer.iExp), properties);
|
||||
properties << ';';
|
||||
}
|
||||
i++;
|
||||
}
|
||||
i = 0;
|
||||
for (const auto& introducer: address.ssu->introducers)
|
||||
{
|
||||
WriteString ("ihost" + boost::lexical_cast<std::string>(i), properties);
|
||||
properties << '=';
|
||||
@@ -555,18 +666,6 @@ namespace data
|
||||
properties << ';';
|
||||
i++;
|
||||
}
|
||||
i = 0;
|
||||
for (const auto& introducer: address.ssu->introducers)
|
||||
{
|
||||
if (introducer.iExp) // expiration is specified
|
||||
{
|
||||
WriteString ("iexp" + boost::lexical_cast<std::string>(i), properties);
|
||||
properties << '=';
|
||||
WriteString (boost::lexical_cast<std::string>(introducer.iExp), properties);
|
||||
properties << ';';
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
// write intro key
|
||||
WriteString ("key", properties);
|
||||
@@ -586,14 +685,14 @@ namespace data
|
||||
}
|
||||
}
|
||||
|
||||
if (address.IsPublishedNTCP2 ())
|
||||
if (address.IsNTCP2 () && isPublished)
|
||||
{
|
||||
// publish i for NTCP2
|
||||
WriteString ("i", properties); properties << '=';
|
||||
WriteString (address.ntcp2->iv.ToBase64 (), properties); properties << ';';
|
||||
}
|
||||
|
||||
if (!address.IsNTCP2 () || address.IsPublishedNTCP2 ())
|
||||
if (isPublished || address.ssu)
|
||||
{
|
||||
WriteString ("port", properties);
|
||||
properties << '=';
|
||||
@@ -721,7 +820,8 @@ namespace data
|
||||
addr->host = boost::asio::ip::address::from_string (host);
|
||||
addr->port = port;
|
||||
addr->transportStyle = eTransportSSU;
|
||||
addr->cost = 10; // NTCP should have priority over SSU
|
||||
addr->published = true;
|
||||
addr->caps = i2p::data::RouterInfo::eSSUTesting | i2p::data::RouterInfo::eSSUIntroducer; // BC;
|
||||
addr->date = 0;
|
||||
addr->ssu.reset (new SSUExt ());
|
||||
addr->ssu->mtu = mtu;
|
||||
@@ -733,9 +833,6 @@ namespace data
|
||||
if (*it == *addr) return;
|
||||
m_SupportedTransports |= addr->host.is_v6 () ? eSSUV6 : eSSUV4;
|
||||
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)
|
||||
@@ -743,11 +840,11 @@ namespace data
|
||||
auto addr = std::make_shared<Address>();
|
||||
addr->host = host;
|
||||
addr->port = port;
|
||||
addr->transportStyle = eTransportNTCP;
|
||||
addr->cost = port ? 3 : 14; // override from RouterContext::PublishNTCP2Address
|
||||
addr->transportStyle = eTransportNTCP;
|
||||
addr->caps = 0;
|
||||
addr->date = 0;
|
||||
addr->ntcp2.reset (new NTCP2Ext ());
|
||||
if (port) addr->ntcp2->isPublished = true;
|
||||
if (port) addr->published = true;
|
||||
memcpy (addr->ntcp2->staticKey, staticKey, 32);
|
||||
memcpy (addr->ntcp2->iv, iv, 16);
|
||||
m_Addresses->push_back(std::move(addr));
|
||||
@@ -757,7 +854,8 @@ namespace data
|
||||
{
|
||||
for (auto& addr : *m_Addresses)
|
||||
{
|
||||
if (addr->transportStyle == eTransportSSU && addr->host.is_v4 ())
|
||||
if (addr->transportStyle == eTransportSSU &&
|
||||
((addr->IsV4 () && introducer.iHost.is_v4 ()) || (addr->IsV6 () && introducer.iHost.is_v6 ())))
|
||||
{
|
||||
for (auto& intro: addr->ssu->introducers)
|
||||
if (intro.iTag == introducer.iTag) return false; // already presented
|
||||
@@ -772,10 +870,11 @@ namespace data
|
||||
{
|
||||
for (auto& addr: *m_Addresses)
|
||||
{
|
||||
if (addr->transportStyle == eTransportSSU && addr->host.is_v4 ())
|
||||
if (addr->transportStyle == eTransportSSU &&
|
||||
((addr->IsV4 () && e.address ().is_v4 ()) || (addr->IsV6 () && e.address ().is_v6 ())))
|
||||
{
|
||||
for (auto it = addr->ssu->introducers.begin (); it != addr->ssu->introducers.end (); ++it)
|
||||
if ( boost::asio::ip::udp::endpoint (it->iHost, it->iPort) == e)
|
||||
if (boost::asio::ip::udp::endpoint (it->iHost, it->iPort) == e)
|
||||
{
|
||||
addr->ssu->introducers.erase (it);
|
||||
return true;
|
||||
@@ -860,13 +959,23 @@ namespace data
|
||||
void RouterInfo::EnableV6 ()
|
||||
{
|
||||
if (!IsV6 ())
|
||||
{
|
||||
m_SupportedTransports |= eSSUV6 | eNTCP2V6;
|
||||
uint8_t addressCaps = AddressCaps::eV6;
|
||||
if (IsV4 ()) addressCaps |= AddressCaps::eV4;
|
||||
SetUnreachableAddressesTransportCaps (addressCaps);
|
||||
}
|
||||
}
|
||||
|
||||
void RouterInfo::EnableV4 ()
|
||||
{
|
||||
if (!IsV4 ())
|
||||
{
|
||||
m_SupportedTransports |= eSSUV4 | eNTCP2V4;
|
||||
uint8_t addressCaps = AddressCaps::eV4;
|
||||
if (IsV6 ()) addressCaps |= AddressCaps::eV6;
|
||||
SetUnreachableAddressesTransportCaps (addressCaps);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -878,6 +987,7 @@ namespace data
|
||||
for (auto it = m_Addresses->begin (); it != m_Addresses->end ();)
|
||||
{
|
||||
auto addr = *it;
|
||||
addr->caps &= ~AddressCaps::eV6;
|
||||
if (addr->host.is_v6 ())
|
||||
it = m_Addresses->erase (it);
|
||||
else
|
||||
@@ -894,6 +1004,7 @@ namespace data
|
||||
for (auto it = m_Addresses->begin (); it != m_Addresses->end ();)
|
||||
{
|
||||
auto addr = *it;
|
||||
addr->caps &= ~AddressCaps::eV4;
|
||||
if (addr->host.is_v4 ())
|
||||
it = m_Addresses->erase (it);
|
||||
else
|
||||
@@ -924,17 +1035,12 @@ namespace data
|
||||
}
|
||||
}
|
||||
|
||||
bool RouterInfo::UsesIntroducer () const
|
||||
{
|
||||
return m_Caps & Caps::eUnreachable; // non-reachable
|
||||
}
|
||||
|
||||
std::shared_ptr<const RouterInfo::Address> RouterInfo::GetSSUAddress (bool v4only) const
|
||||
{
|
||||
return GetAddress (
|
||||
[v4only](std::shared_ptr<const RouterInfo::Address> address)->bool
|
||||
{
|
||||
return (address->transportStyle == eTransportSSU) && (!v4only || address->host.is_v4 ());
|
||||
return (address->transportStyle == eTransportSSU) && (!v4only || address->IsV4 ());
|
||||
});
|
||||
}
|
||||
|
||||
@@ -943,7 +1049,7 @@ namespace data
|
||||
return GetAddress (
|
||||
[](std::shared_ptr<const RouterInfo::Address> address)->bool
|
||||
{
|
||||
return (address->transportStyle == eTransportSSU) && address->host.is_v6 ();
|
||||
return (address->transportStyle == eTransportSSU) && address->IsV6();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -962,12 +1068,13 @@ namespace data
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::shared_ptr<const RouterInfo::Address> RouterInfo::GetNTCP2Address (bool publishedOnly) const
|
||||
std::shared_ptr<const RouterInfo::Address> RouterInfo::GetNTCP2AddressWithStaticKey (const uint8_t * key) const
|
||||
{
|
||||
if (!key) return nullptr;
|
||||
return GetAddress (
|
||||
[publishedOnly](std::shared_ptr<const RouterInfo::Address> address)->bool
|
||||
[key](std::shared_ptr<const RouterInfo::Address> address)->bool
|
||||
{
|
||||
return address->IsNTCP2 () && (!publishedOnly || address->IsPublishedNTCP2 ());
|
||||
return address->IsNTCP2 () && !memcmp (address->ntcp2->staticKey, key, 32);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1015,9 +1122,67 @@ namespace data
|
||||
|
||||
bool RouterInfo::IsEligibleFloodfill () const
|
||||
{
|
||||
// floodfill must be reachable, >= 0.9.28 and not DSA
|
||||
return IsReachable () && m_Version >= NETDB_MIN_FLOODFILL_VERSION &&
|
||||
// floodfill must be reachable somehow, >= 0.9.28 and not DSA
|
||||
return (IsReachable () || (m_SupportedTransports & eSSUV4)) &&
|
||||
m_Version >= NETDB_MIN_FLOODFILL_VERSION &&
|
||||
GetIdentity ()->GetSigningKeyType () != SIGNING_KEY_TYPE_DSA_SHA1;
|
||||
}
|
||||
|
||||
bool RouterInfo::IsPeerTesting (bool v4) const
|
||||
{
|
||||
if (!(m_SupportedTransports & (v4 ? eSSUV4 : eSSUV6))) return false;
|
||||
return (bool)GetAddress (
|
||||
[v4](std::shared_ptr<const RouterInfo::Address> address)->bool
|
||||
{
|
||||
return (address->transportStyle == eTransportSSU) && address->IsPeerTesting () &&
|
||||
((v4 && address->IsV4 ()) || (!v4 && address->IsV6 ())) && address->IsReachableSSU ();
|
||||
});
|
||||
}
|
||||
|
||||
bool RouterInfo::IsIntroducer (bool v4) const
|
||||
{
|
||||
if (!(m_SupportedTransports & (v4 ? eSSUV4 : eSSUV6))) return false;
|
||||
return (bool)GetAddress (
|
||||
[v4](std::shared_ptr<const RouterInfo::Address> address)->bool
|
||||
{
|
||||
return (address->transportStyle == eTransportSSU) && address->IsIntroducer () &&
|
||||
((v4 && address->IsV4 ()) || (!v4 && address->IsV6 ())) && !address->host.is_unspecified ();
|
||||
});
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
*
|
||||
@@ -44,9 +44,16 @@ namespace data
|
||||
const char CAPS_FLAG_EXTRA_BANDWIDTH1 = 'P'; /* 256-2000 KBps */
|
||||
const char CAPS_FLAG_EXTRA_BANDWIDTH2 = 'X'; /* > 2000 KBps */
|
||||
|
||||
const char CAPS_FLAG_V4 = '4';
|
||||
const char CAPS_FLAG_V6 = '6';
|
||||
const char CAPS_FLAG_SSU_TESTING = 'B';
|
||||
const char CAPS_FLAG_SSU_INTRODUCER = 'C';
|
||||
|
||||
const uint8_t COST_NTCP2_PUBLISHED = 3;
|
||||
const uint8_t COST_NTCP2_NON_PUBLISHED = 14;
|
||||
const uint8_t COST_SSU_DIRECT = 9;
|
||||
const uint8_t COST_SSU_THROUGH_INTRODUCERS = 11;
|
||||
|
||||
const int MAX_RI_BUFFER_SIZE = 2048; // if RouterInfo exceeds 2048 we consider it as malformed, might be changed later
|
||||
class RouterInfo: public RoutingDestination
|
||||
{
|
||||
@@ -67,12 +74,18 @@ namespace data
|
||||
eHighBandwidth = 0x02,
|
||||
eExtraBandwidth = 0x04,
|
||||
eReachable = 0x08,
|
||||
eSSUTesting = 0x10,
|
||||
eSSUIntroducer = 0x20,
|
||||
eHidden = 0x40,
|
||||
eUnreachable = 0x80
|
||||
eHidden = 0x10,
|
||||
eUnreachable = 0x20
|
||||
};
|
||||
|
||||
enum AddressCaps
|
||||
{
|
||||
eV4 = 0x01,
|
||||
eV6 = 0x02,
|
||||
eSSUTesting = 0x04,
|
||||
eSSUIntroducer = 0x08
|
||||
};
|
||||
|
||||
enum TransportStyle
|
||||
{
|
||||
eTransportUnknown = 0,
|
||||
@@ -83,7 +96,7 @@ namespace data
|
||||
typedef Tag<32> IntroKey; // should be castable to MacKey and AESKey
|
||||
struct Introducer
|
||||
{
|
||||
Introducer (): iExp (0) {};
|
||||
Introducer (): iPort (0), iExp (0) {};
|
||||
boost::asio::ip::address iHost;
|
||||
int iPort;
|
||||
IntroKey iKey;
|
||||
@@ -102,7 +115,6 @@ namespace data
|
||||
{
|
||||
Tag<32> staticKey;
|
||||
Tag<16> iv;
|
||||
bool isPublished = false;
|
||||
};
|
||||
|
||||
struct Address
|
||||
@@ -111,14 +123,15 @@ namespace data
|
||||
boost::asio::ip::address host;
|
||||
int port;
|
||||
uint64_t date;
|
||||
uint8_t cost;
|
||||
uint8_t caps;
|
||||
bool published = false;
|
||||
std::unique_ptr<SSUExt> ssu; // not null for SSU
|
||||
std::unique_ptr<NTCP2Ext> ntcp2; // not null for NTCP2
|
||||
|
||||
bool IsCompatible (const boost::asio::ip::address& other) const
|
||||
{
|
||||
return (host.is_v4 () && other.is_v4 ()) ||
|
||||
(host.is_v6 () && other.is_v6 ());
|
||||
return (IsV4 () && other.is_v4 ()) ||
|
||||
(IsV6 () && other.is_v6 ());
|
||||
}
|
||||
|
||||
bool operator==(const Address& other) const
|
||||
@@ -133,7 +146,15 @@ namespace data
|
||||
}
|
||||
|
||||
bool IsNTCP2 () const { return (bool)ntcp2; };
|
||||
bool IsPublishedNTCP2 () const { return IsNTCP2 () && ntcp2->isPublished; };
|
||||
bool IsPublishedNTCP2 () const { return IsNTCP2 () && published; };
|
||||
bool IsReachableSSU () const { return (bool)ssu && (!host.is_unspecified () || !ssu->introducers.empty ()); };
|
||||
bool UsesIntroducer () const { return (bool)ssu && !ssu->introducers.empty (); };
|
||||
|
||||
bool IsIntroducer () const { return caps & eSSUIntroducer; };
|
||||
bool IsPeerTesting () const { return caps & eSSUTesting; };
|
||||
|
||||
bool IsV4 () const { return (caps & AddressCaps::eV4) || (host.is_v4 () && !host.is_unspecified ()); };
|
||||
bool IsV6 () const { return (caps & AddressCaps::eV6) || (host.is_v6 () && !host.is_unspecified ()); };
|
||||
};
|
||||
typedef std::list<std::shared_ptr<Address> > Addresses;
|
||||
|
||||
@@ -150,7 +171,7 @@ namespace data
|
||||
uint64_t GetTimestamp () const { return m_Timestamp; };
|
||||
int GetVersion () const { return m_Version; };
|
||||
Addresses& GetAddresses () { return *m_Addresses; }; // should be called for local RI only, otherwise must return shared_ptr
|
||||
std::shared_ptr<const Address> GetNTCP2Address (bool publishedOnly) const;
|
||||
std::shared_ptr<const Address> GetNTCP2AddressWithStaticKey (const uint8_t * key) const;
|
||||
std::shared_ptr<const Address> GetPublishedNTCP2V4Address () const;
|
||||
std::shared_ptr<const Address> GetPublishedNTCP2V6Address () const;
|
||||
std::shared_ptr<const Address> GetSSUAddress (bool v4only = true) const;
|
||||
@@ -165,6 +186,7 @@ namespace data
|
||||
void DeleteProperty (const std::string& key); // called from RouterContext only
|
||||
std::string GetProperty (const std::string& key) const; // called from RouterContext only
|
||||
void ClearProperties () { m_Properties.clear (); };
|
||||
void SetUnreachableAddressesTransportCaps (uint8_t transports); // bitmask of AddressCaps
|
||||
bool IsFloodfill () const { return m_Caps & Caps::eFloodfill; };
|
||||
bool IsReachable () const { return m_Caps & Caps::eReachable; };
|
||||
bool IsSSU (bool v4only = true) const;
|
||||
@@ -180,15 +202,15 @@ namespace data
|
||||
void DisableV4 ();
|
||||
void EnableMesh ();
|
||||
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 UsesIntroducer () const;
|
||||
bool IsIntroducer () const { return m_Caps & eSSUIntroducer; };
|
||||
bool IsPeerTesting () const { return m_Caps & eSSUTesting; };
|
||||
bool IsHidden () const { return m_Caps & eHidden; };
|
||||
bool IsHighBandwidth () const { return m_Caps & RouterInfo::eHighBandwidth; };
|
||||
bool IsExtraBandwidth () const { return m_Caps & RouterInfo::eExtraBandwidth; };
|
||||
bool IsEligibleFloodfill () const;
|
||||
bool IsPeerTesting (bool v4) const;
|
||||
bool IsIntroducer (bool v4) const;
|
||||
|
||||
uint8_t GetCaps () const { return m_Caps; };
|
||||
void SetCaps (uint8_t caps);
|
||||
@@ -232,6 +254,7 @@ namespace data
|
||||
size_t ReadString (char* str, size_t len, std::istream& s) const;
|
||||
void WriteString (const std::string& str, std::ostream& s) const;
|
||||
void ExtractCaps (const char * value);
|
||||
uint8_t ExtractAddressCaps (const char * value) const;
|
||||
template<typename Filter>
|
||||
std::shared_ptr<const Address> GetAddress (Filter filter) const;
|
||||
void UpdateCapsProperty ();
|
||||
|
||||
406
libi2pd/SSU.cpp
406
libi2pd/SSU.cpp
@@ -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
|
||||
*
|
||||
@@ -22,31 +22,15 @@ namespace i2p
|
||||
{
|
||||
namespace transport
|
||||
{
|
||||
|
||||
SSUServer::SSUServer (const boost::asio::ip::address & addr, int port):
|
||||
m_OnlyV6(true), m_IsRunning(false), m_Thread (nullptr),
|
||||
m_ReceiversThread (nullptr), m_ReceiversThreadV6 (nullptr), m_Work (m_Service),
|
||||
m_ReceiversWork (m_ReceiversService), m_ReceiversWorkV6 (m_ReceiversServiceV6),
|
||||
m_EndpointV6 (addr, port), m_Socket (m_ReceiversService, m_Endpoint),
|
||||
m_SocketV6 (m_ReceiversServiceV6), m_IntroducersUpdateTimer (m_Service),
|
||||
m_PeerTestsCleanupTimer (m_Service), m_TerminationTimer (m_Service),
|
||||
m_TerminationTimerV6 (m_Service)
|
||||
{
|
||||
OpenSocketV6 ();
|
||||
}
|
||||
|
||||
SSUServer::SSUServer (int port):
|
||||
m_OnlyV6(false), m_IsRunning(false), m_Thread (nullptr),
|
||||
m_IsRunning(false), m_Thread (nullptr),
|
||||
m_ReceiversThread (nullptr), m_ReceiversThreadV6 (nullptr), m_Work (m_Service),
|
||||
m_ReceiversWork (m_ReceiversService), m_ReceiversWorkV6 (m_ReceiversServiceV6),
|
||||
m_Endpoint (boost::asio::ip::udp::v4 (), port), m_EndpointV6 (boost::asio::ip::udp::v6 (), port),
|
||||
m_Socket (m_ReceiversService), m_SocketV6 (m_ReceiversServiceV6),
|
||||
m_IntroducersUpdateTimer (m_Service), m_PeerTestsCleanupTimer (m_Service),
|
||||
m_TerminationTimer (m_Service), m_TerminationTimerV6 (m_Service)
|
||||
m_IntroducersUpdateTimer (m_Service), m_IntroducersUpdateTimerV6 (m_Service),
|
||||
m_PeerTestsCleanupTimer (m_Service), m_TerminationTimer (m_Service), m_TerminationTimerV6 (m_Service)
|
||||
{
|
||||
OpenSocket ();
|
||||
if (context.SupportsV6 ())
|
||||
OpenSocketV6 ();
|
||||
}
|
||||
|
||||
SSUServer::~SSUServer ()
|
||||
@@ -91,23 +75,24 @@ namespace transport
|
||||
void SSUServer::Start ()
|
||||
{
|
||||
m_IsRunning = true;
|
||||
if (!m_OnlyV6)
|
||||
m_Thread = new std::thread (std::bind (&SSUServer::Run, this));
|
||||
if (context.SupportsV4 ())
|
||||
{
|
||||
OpenSocket ();
|
||||
m_ReceiversThread = new std::thread (std::bind (&SSUServer::RunReceivers, this));
|
||||
m_Thread = new std::thread (std::bind (&SSUServer::Run, this));
|
||||
m_ReceiversService.post (std::bind (&SSUServer::Receive, this));
|
||||
ScheduleTermination ();
|
||||
ScheduleIntroducersUpdateTimer (); // wait for 30 seconds and decide if we need introducers
|
||||
}
|
||||
if (context.SupportsV6 ())
|
||||
{
|
||||
OpenSocketV6 ();
|
||||
m_ReceiversThreadV6 = new std::thread (std::bind (&SSUServer::RunReceiversV6, this));
|
||||
if (!m_Thread)
|
||||
m_Thread = new std::thread (std::bind (&SSUServer::Run, this));
|
||||
m_ReceiversServiceV6.post (std::bind (&SSUServer::ReceiveV6, this));
|
||||
ScheduleTerminationV6 ();
|
||||
ScheduleIntroducersUpdateTimerV6 (); // wait for 30 seconds and decide if we need introducers
|
||||
}
|
||||
SchedulePeerTestsCleanupTimer ();
|
||||
ScheduleIntroducersUpdateTimer (); // wait for 30 seconds and decide if we need introducers
|
||||
}
|
||||
|
||||
void SSUServer::Stop ()
|
||||
@@ -116,6 +101,8 @@ namespace transport
|
||||
m_IsRunning = false;
|
||||
m_TerminationTimer.cancel ();
|
||||
m_TerminationTimerV6.cancel ();
|
||||
m_IntroducersUpdateTimer.cancel ();
|
||||
m_IntroducersUpdateTimerV6.cancel ();
|
||||
m_Service.stop ();
|
||||
m_Socket.close ();
|
||||
m_SocketV6.close ();
|
||||
@@ -138,7 +125,7 @@ namespace transport
|
||||
m_Thread->join ();
|
||||
delete m_Thread;
|
||||
m_Thread = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SSUServer::Run ()
|
||||
@@ -205,6 +192,14 @@ namespace transport
|
||||
}
|
||||
}
|
||||
|
||||
void SSUServer::SetLocalAddress (const boost::asio::ip::address& localAddress)
|
||||
{
|
||||
if (localAddress.is_v6 ())
|
||||
m_EndpointV6.address (localAddress);
|
||||
else if (localAddress.is_v4 ())
|
||||
m_Endpoint.address (localAddress);
|
||||
}
|
||||
|
||||
void SSUServer::AddRelay (uint32_t tag, std::shared_ptr<SSUSession> relay)
|
||||
{
|
||||
m_Relays[tag] = relay;
|
||||
@@ -393,7 +388,7 @@ namespace transport
|
||||
auto it = sessions->find (packet->from);
|
||||
if (it != sessions->end ())
|
||||
session = it->second;
|
||||
if (!session)
|
||||
if (!session && packet->len > 0)
|
||||
{
|
||||
session = std::make_shared<SSUSession> (*this, packet->from);
|
||||
session->WaitForConnect ();
|
||||
@@ -401,7 +396,8 @@ namespace transport
|
||||
LogPrint (eLogDebug, "SSU: new session from ", packet->from.address ().to_string (), ":", packet->from.port (), " created");
|
||||
}
|
||||
}
|
||||
session->ProcessNextMessage (packet->buf, packet->len, packet->from);
|
||||
if (session)
|
||||
session->ProcessNextMessage (packet->buf, packet->len, packet->from);
|
||||
}
|
||||
catch (std::exception& ex)
|
||||
{
|
||||
@@ -414,23 +410,9 @@ namespace transport
|
||||
if (session) session->FlushData ();
|
||||
}
|
||||
|
||||
std::shared_ptr<SSUSession> SSUServer::FindSession (std::shared_ptr<const i2p::data::RouterInfo> router) const
|
||||
{
|
||||
if (!router) return nullptr;
|
||||
auto address = router->GetSSUAddress (true); // v4 only
|
||||
if (!address) return nullptr;
|
||||
auto session = FindSession (boost::asio::ip::udp::endpoint (address->host, address->port));
|
||||
if (session || !context.SupportsV6 ())
|
||||
return session;
|
||||
// try v6
|
||||
address = router->GetSSUV6Address ();
|
||||
if (!address) return nullptr;
|
||||
return FindSession (boost::asio::ip::udp::endpoint (address->host, address->port));
|
||||
}
|
||||
|
||||
std::shared_ptr<SSUSession> SSUServer::FindSession (const boost::asio::ip::udp::endpoint& e) const
|
||||
{
|
||||
auto& sessions = e.address ().is_v6 () ? m_SessionsV6 : m_Sessions;
|
||||
auto& sessions = e.address ().is_v6 () ? m_SessionsV6 : m_Sessions;
|
||||
auto it = sessions.find (e);
|
||||
if (it != sessions.end ())
|
||||
return it->second;
|
||||
@@ -438,28 +420,33 @@ namespace transport
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void SSUServer::CreateSession (std::shared_ptr<const i2p::data::RouterInfo> router, bool peerTest, bool v4only)
|
||||
bool SSUServer::CreateSession (std::shared_ptr<const i2p::data::RouterInfo> router, bool peerTest, bool v4only)
|
||||
{
|
||||
auto address = router->GetSSUAddress (v4only || !context.SupportsV6 ());
|
||||
if (address)
|
||||
CreateSession (router, address->host, address->port, peerTest);
|
||||
return CreateSession (router, address, peerTest);
|
||||
else
|
||||
LogPrint (eLogWarning, "SSU: Router ", i2p::data::GetIdentHashAbbreviation (router->GetIdentHash ()), " doesn't have SSU address");
|
||||
return false;
|
||||
}
|
||||
|
||||
void SSUServer::CreateSession (std::shared_ptr<const i2p::data::RouterInfo> router,
|
||||
const boost::asio::ip::address& addr, int port, bool peerTest)
|
||||
bool SSUServer::CreateSession (std::shared_ptr<const i2p::data::RouterInfo> router,
|
||||
std::shared_ptr<const i2p::data::RouterInfo::Address> address, bool peerTest)
|
||||
{
|
||||
if (router)
|
||||
if (router && address)
|
||||
{
|
||||
if (router->UsesIntroducer ())
|
||||
m_Service.post (std::bind (&SSUServer::CreateSessionThroughIntroducer, this, router, peerTest)); // always V4 thread
|
||||
if (address->UsesIntroducer ())
|
||||
m_Service.post (std::bind (&SSUServer::CreateSessionThroughIntroducer, this, router, address, peerTest)); // always V4 thread
|
||||
else
|
||||
{
|
||||
boost::asio::ip::udp::endpoint remoteEndpoint (addr, port);
|
||||
if (address->host.is_unspecified () || !address->port) return false;
|
||||
boost::asio::ip::udp::endpoint remoteEndpoint (address->host, address->port);
|
||||
m_Service.post (std::bind (&SSUServer::CreateDirectSession, this, router, remoteEndpoint, peerTest));
|
||||
}
|
||||
}
|
||||
else
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
void SSUServer::CreateDirectSession (std::shared_ptr<const i2p::data::RouterInfo> router, boost::asio::ip::udp::endpoint remoteEndpoint, bool peerTest)
|
||||
@@ -485,87 +472,107 @@ namespace transport
|
||||
}
|
||||
}
|
||||
|
||||
void SSUServer::CreateSessionThroughIntroducer (std::shared_ptr<const i2p::data::RouterInfo> router, bool peerTest)
|
||||
void SSUServer::CreateSessionThroughIntroducer (std::shared_ptr<const i2p::data::RouterInfo> router,
|
||||
std::shared_ptr<const i2p::data::RouterInfo::Address> address, bool peerTest)
|
||||
{
|
||||
if (router && router->UsesIntroducer ())
|
||||
if (router && address && address->UsesIntroducer ())
|
||||
{
|
||||
auto address = router->GetSSUAddress (true); // v4 only for now
|
||||
if (address)
|
||||
if (address->IsV4 () && !i2p::context.SupportsV4 ()) return;
|
||||
if (address->IsV6 () && !i2p::context.SupportsV6 ()) return;
|
||||
if (!address->host.is_unspecified () && address->port)
|
||||
{
|
||||
// we rarely come here
|
||||
auto& sessions = address->host.is_v6 () ? m_SessionsV6 : m_Sessions;
|
||||
boost::asio::ip::udp::endpoint remoteEndpoint (address->host, address->port);
|
||||
auto it = m_Sessions.find (remoteEndpoint);
|
||||
auto it = sessions.find (remoteEndpoint);
|
||||
// check if session is presented already
|
||||
if (it != m_Sessions.end ())
|
||||
if (it != sessions.end ())
|
||||
{
|
||||
auto session = it->second;
|
||||
if (peerTest && session->GetState () == eSessionStateEstablished)
|
||||
session->SendPeerTest ();
|
||||
return;
|
||||
}
|
||||
// create new session
|
||||
int numIntroducers = address->ssu->introducers.size ();
|
||||
if (numIntroducers > 0)
|
||||
}
|
||||
// create new session
|
||||
int numIntroducers = address->ssu->introducers.size ();
|
||||
if (numIntroducers > 0)
|
||||
{
|
||||
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
|
||||
std::shared_ptr<SSUSession> introducerSession;
|
||||
const i2p::data::RouterInfo::Introducer * introducer = nullptr;
|
||||
// we might have a session to introducer already
|
||||
auto offset = rand ();
|
||||
for (int i = 0; i < numIntroducers; i++)
|
||||
{
|
||||
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
|
||||
std::shared_ptr<SSUSession> introducerSession;
|
||||
const i2p::data::RouterInfo::Introducer * introducer = nullptr;
|
||||
// we might have a session to introducer already
|
||||
for (int i = 0; i < numIntroducers; i++)
|
||||
auto intr = &(address->ssu->introducers[(offset + i)%numIntroducers]);
|
||||
if (!intr->iPort) continue; // skip invalid introducer
|
||||
if (intr->iExp > 0 && ts > intr->iExp) continue; // skip expired introducer
|
||||
boost::asio::ip::udp::endpoint ep (intr->iHost, intr->iPort);
|
||||
if (ep.address ().is_v4 () && address->IsV4 ()) // ipv4
|
||||
{
|
||||
auto intr = &(address->ssu->introducers[i]);
|
||||
if (intr->iExp > 0 && ts > intr->iExp) continue; // skip expired introducer
|
||||
boost::asio::ip::udp::endpoint ep (intr->iHost, intr->iPort);
|
||||
if (ep.address ().is_v4 ()) // ipv4 only
|
||||
if (!introducer) introducer = intr;
|
||||
auto it = m_Sessions.find (ep);
|
||||
if (it != m_Sessions.end ())
|
||||
{
|
||||
if (!introducer) introducer = intr; // we pick first one for now
|
||||
it = m_Sessions.find (ep);
|
||||
if (it != m_Sessions.end ())
|
||||
{
|
||||
introducerSession = it->second;
|
||||
break;
|
||||
}
|
||||
introducerSession = it->second;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!introducer)
|
||||
if (ep.address ().is_v6 () && address->IsV6 ()) // ipv6
|
||||
{
|
||||
LogPrint (eLogWarning, "SSU: Can't connect to unreachable router and no ipv4 non-expired introducers presented");
|
||||
return;
|
||||
}
|
||||
|
||||
if (introducerSession) // session found
|
||||
LogPrint (eLogWarning, "SSU: Session to introducer already exists");
|
||||
else // create new
|
||||
{
|
||||
LogPrint (eLogDebug, "SSU: Creating new session to introducer ", introducer->iHost);
|
||||
boost::asio::ip::udp::endpoint introducerEndpoint (introducer->iHost, introducer->iPort);
|
||||
introducerSession = std::make_shared<SSUSession> (*this, introducerEndpoint, router);
|
||||
m_Sessions[introducerEndpoint] = introducerSession;
|
||||
}
|
||||
#if BOOST_VERSION >= 104900
|
||||
if (!address->host.is_unspecified () && address->port)
|
||||
#endif
|
||||
{
|
||||
// create session
|
||||
auto session = std::make_shared<SSUSession> (*this, remoteEndpoint, router, peerTest);
|
||||
m_Sessions[remoteEndpoint] = session;
|
||||
|
||||
// introduce
|
||||
LogPrint (eLogInfo, "SSU: Introduce new session to [", i2p::data::GetIdentHashAbbreviation (router->GetIdentHash ()),
|
||||
"] through introducer ", introducer->iHost, ":", introducer->iPort);
|
||||
session->WaitForIntroduction ();
|
||||
if (i2p::context.GetRouterInfo ().UsesIntroducer ()) // if we are unreachable
|
||||
if (!introducer) introducer = intr;
|
||||
auto it = m_SessionsV6.find (ep);
|
||||
if (it != m_SessionsV6.end ())
|
||||
{
|
||||
uint8_t buf[1];
|
||||
Send (buf, 0, remoteEndpoint); // send HolePunch
|
||||
introducerSession = it->second;
|
||||
break;
|
||||
}
|
||||
}
|
||||
introducerSession->Introduce (*introducer, router);
|
||||
}
|
||||
else
|
||||
LogPrint (eLogWarning, "SSU: Can't connect to unreachable router and no introducers present");
|
||||
if (!introducer)
|
||||
{
|
||||
LogPrint (eLogWarning, "SSU: Can't connect to unreachable router and no compatibe non-expired introducers presented");
|
||||
return;
|
||||
}
|
||||
|
||||
if (introducerSession) // session found
|
||||
LogPrint (eLogWarning, "SSU: Session to introducer already exists");
|
||||
else // create new
|
||||
{
|
||||
LogPrint (eLogDebug, "SSU: Creating new session to introducer ", introducer->iHost);
|
||||
boost::asio::ip::udp::endpoint introducerEndpoint (introducer->iHost, introducer->iPort);
|
||||
introducerSession = std::make_shared<SSUSession> (*this, introducerEndpoint, router);
|
||||
if (introducerEndpoint.address ().is_v4 ())
|
||||
m_Sessions[introducerEndpoint] = introducerSession;
|
||||
else if (introducerEndpoint.address ().is_v6 ())
|
||||
m_SessionsV6[introducerEndpoint] = introducerSession;
|
||||
}
|
||||
if (!address->host.is_unspecified () && address->port)
|
||||
{
|
||||
// create session
|
||||
boost::asio::ip::udp::endpoint remoteEndpoint (address->host, address->port);
|
||||
auto session = std::make_shared<SSUSession> (*this, remoteEndpoint, router, peerTest);
|
||||
if (address->host.is_v4 ())
|
||||
m_Sessions[remoteEndpoint] = session;
|
||||
else if (address->host.is_v6 ())
|
||||
m_SessionsV6[remoteEndpoint] = session;
|
||||
|
||||
// introduce
|
||||
LogPrint (eLogInfo, "SSU: Introduce new session to [", i2p::data::GetIdentHashAbbreviation (router->GetIdentHash ()),
|
||||
"] through introducer ", introducer->iHost, ":", introducer->iPort);
|
||||
session->WaitForIntroduction ();
|
||||
if ((address->host.is_v4 () && i2p::context.GetStatus () == eRouterStatusFirewalled) ||
|
||||
(address->host.is_v6 () && i2p::context.GetStatusV6 () == eRouterStatusFirewalled))
|
||||
{
|
||||
uint8_t buf[1];
|
||||
Send (buf, 0, remoteEndpoint); // send HolePunch
|
||||
}
|
||||
}
|
||||
introducerSession->Introduce (*introducer, router);
|
||||
}
|
||||
else
|
||||
LogPrint (eLogWarning, "SSU: Router ", i2p::data::GetIdentHashAbbreviation (router->GetIdentHash ()), " doesn't have SSU address");
|
||||
LogPrint (eLogWarning, "SSU: Can't connect to unreachable router and no introducers present");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -641,71 +648,151 @@ namespace transport
|
||||
);
|
||||
}
|
||||
|
||||
std::set<SSUSession *> SSUServer::FindIntroducers (int maxNumIntroducers)
|
||||
std::list<std::shared_ptr<SSUSession> > SSUServer::FindIntroducers (int maxNumIntroducers,
|
||||
bool v4, std::set<i2p::data::IdentHash>& excluded)
|
||||
{
|
||||
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
|
||||
std::set<SSUSession *> ret;
|
||||
for (int i = 0; i < maxNumIntroducers; i++)
|
||||
std::list<std::shared_ptr<SSUSession> > ret;
|
||||
const auto& sessions = v4 ? m_Sessions : m_SessionsV6;
|
||||
for (const auto& s : sessions)
|
||||
{
|
||||
auto session = GetRandomV4Session (
|
||||
[&ret, ts](std::shared_ptr<SSUSession> session)->bool
|
||||
{
|
||||
return session->GetRelayTag () && !ret.count (session.get ()) &&
|
||||
session->GetState () == eSessionStateEstablished &&
|
||||
ts < session->GetCreationTime () + SSU_TO_INTRODUCER_SESSION_DURATION;
|
||||
}
|
||||
);
|
||||
if (session)
|
||||
if (s.second->GetRelayTag () && s.second->GetState () == eSessionStateEstablished &&
|
||||
ts < s.second->GetCreationTime () + SSU_TO_INTRODUCER_SESSION_EXPIRATION)
|
||||
ret.push_back (s.second);
|
||||
else if (s.second->GetRemoteIdentity ())
|
||||
excluded.insert (s.second->GetRemoteIdentity ()->GetIdentHash ());
|
||||
}
|
||||
if ((int)ret.size () > maxNumIntroducers)
|
||||
{
|
||||
// shink ret randomly
|
||||
int sz = ret.size () - maxNumIntroducers;
|
||||
for (int i = 0; i < sz; i++)
|
||||
{
|
||||
ret.insert (session.get ());
|
||||
break;
|
||||
auto ind = rand () % ret.size ();
|
||||
auto it = ret.begin ();
|
||||
std::advance (it, ind);
|
||||
ret.erase (it);
|
||||
}
|
||||
}
|
||||
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, true));
|
||||
}
|
||||
|
||||
void SSUServer::ScheduleIntroducersUpdateTimer ()
|
||||
{
|
||||
m_IntroducersUpdateTimer.expires_from_now (boost::posix_time::seconds(SSU_KEEP_ALIVE_INTERVAL));
|
||||
m_IntroducersUpdateTimer.async_wait (std::bind (&SSUServer::HandleIntroducersUpdateTimer,
|
||||
this, std::placeholders::_1));
|
||||
this, std::placeholders::_1, true));
|
||||
}
|
||||
|
||||
void SSUServer::HandleIntroducersUpdateTimer (const boost::system::error_code& ecode)
|
||||
void SSUServer::RescheduleIntroducersUpdateTimerV6 ()
|
||||
{
|
||||
m_IntroducersUpdateTimerV6.cancel ();
|
||||
m_IntroducersUpdateTimerV6.expires_from_now (boost::posix_time::seconds(SSU_KEEP_ALIVE_INTERVAL/2));
|
||||
m_IntroducersUpdateTimerV6.async_wait (std::bind (&SSUServer::HandleIntroducersUpdateTimer,
|
||||
this, std::placeholders::_1, false));
|
||||
}
|
||||
|
||||
void SSUServer::ScheduleIntroducersUpdateTimerV6 ()
|
||||
{
|
||||
m_IntroducersUpdateTimerV6.expires_from_now (boost::posix_time::seconds(SSU_KEEP_ALIVE_INTERVAL));
|
||||
m_IntroducersUpdateTimerV6.async_wait (std::bind (&SSUServer::HandleIntroducersUpdateTimer,
|
||||
this, std::placeholders::_1, false));
|
||||
}
|
||||
|
||||
void SSUServer::HandleIntroducersUpdateTimer (const boost::system::error_code& ecode, bool v4)
|
||||
{
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
{
|
||||
// timeout expired
|
||||
if (i2p::context.GetStatus () == eRouterStatusTesting)
|
||||
if (v4)
|
||||
{
|
||||
// we still don't know if we need introducers
|
||||
ScheduleIntroducersUpdateTimer ();
|
||||
return;
|
||||
if (i2p::context.GetStatus () == eRouterStatusTesting)
|
||||
{
|
||||
// we still don't know if we need introducers
|
||||
ScheduleIntroducersUpdateTimer ();
|
||||
return;
|
||||
}
|
||||
if (i2p::context.GetStatus () != eRouterStatusFirewalled)
|
||||
{
|
||||
// we don't need introducers
|
||||
m_Introducers.clear ();
|
||||
return;
|
||||
}
|
||||
// we are firewalled
|
||||
if (!i2p::context.IsUnreachable ()) i2p::context.SetUnreachable (true, false); // v4
|
||||
}
|
||||
if (i2p::context.GetStatus () == eRouterStatusOK) return; // we don't need introducers anymore
|
||||
// we are firewalled
|
||||
if (!i2p::context.IsUnreachable ()) i2p::context.SetUnreachable ();
|
||||
else
|
||||
{
|
||||
if (i2p::context.GetStatusV6 () == eRouterStatusTesting)
|
||||
{
|
||||
// we still don't know if we need introducers
|
||||
ScheduleIntroducersUpdateTimerV6 ();
|
||||
return;
|
||||
}
|
||||
if (i2p::context.GetStatusV6 () != eRouterStatusFirewalled)
|
||||
{
|
||||
// we don't need introducers
|
||||
m_IntroducersV6.clear ();
|
||||
return;
|
||||
}
|
||||
// we are firewalled
|
||||
auto addr = i2p::context.GetRouterInfo ().GetSSUV6Address ();
|
||||
if (addr && addr->ssu && addr->ssu->introducers.empty ())
|
||||
i2p::context.SetUnreachable (false, true); // v6
|
||||
}
|
||||
|
||||
std::list<boost::asio::ip::udp::endpoint> newList;
|
||||
size_t numIntroducers = 0;
|
||||
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
|
||||
for (const auto& it : m_Introducers)
|
||||
std::set<i2p::data::IdentHash> excluded;
|
||||
auto& introducers = v4 ? m_Introducers : m_IntroducersV6;
|
||||
for (const auto& it : introducers)
|
||||
{
|
||||
auto session = FindSession (it);
|
||||
if (session && ts < session->GetCreationTime () + SSU_TO_INTRODUCER_SESSION_DURATION)
|
||||
if (session)
|
||||
{
|
||||
session->SendKeepAlive ();
|
||||
newList.push_back (it);
|
||||
numIntroducers++;
|
||||
if (ts < session->GetCreationTime () + SSU_TO_INTRODUCER_SESSION_EXPIRATION)
|
||||
session->SendKeepAlive ();
|
||||
if (ts < session->GetCreationTime () + SSU_TO_INTRODUCER_SESSION_DURATION)
|
||||
{
|
||||
newList.push_back (it);
|
||||
numIntroducers++;
|
||||
if (session->GetRemoteIdentity ())
|
||||
excluded.insert (session->GetRemoteIdentity ()->GetIdentHash ());
|
||||
}
|
||||
else
|
||||
session = nullptr;
|
||||
}
|
||||
else
|
||||
if (!session)
|
||||
i2p::context.RemoveIntroducer (it);
|
||||
}
|
||||
|
||||
if (numIntroducers < SSU_MAX_NUM_INTRODUCERS)
|
||||
{
|
||||
// create new
|
||||
auto introducers = FindIntroducers (SSU_MAX_NUM_INTRODUCERS);
|
||||
for (const auto& it1: introducers)
|
||||
auto sessions = FindIntroducers (SSU_MAX_NUM_INTRODUCERS, v4, excluded); // try to find if duplicates
|
||||
if (sessions.empty () && !introducers.empty ())
|
||||
{
|
||||
// bump creation time for previous introducers if no new sessions found
|
||||
LogPrint (eLogDebug, "SSU: no new introducers found. Trying to reuse existing");
|
||||
for (const auto& it : introducers)
|
||||
{
|
||||
auto session = FindSession (it);
|
||||
if (session)
|
||||
session->SetCreationTime (session->GetCreationTime () + SSU_TO_INTRODUCER_SESSION_DURATION);
|
||||
}
|
||||
// try again
|
||||
excluded.clear ();
|
||||
sessions = FindIntroducers (SSU_MAX_NUM_INTRODUCERS, v4, excluded);
|
||||
}
|
||||
for (const auto& it1: sessions)
|
||||
{
|
||||
const auto& ep = it1->GetRemoteEndpoint ();
|
||||
i2p::data::RouterInfo::Introducer introducer;
|
||||
@@ -713,21 +800,46 @@ namespace transport
|
||||
introducer.iPort = ep.port ();
|
||||
introducer.iTag = it1->GetRelayTag ();
|
||||
introducer.iKey = it1->GetIntroKey ();
|
||||
introducer.iExp = it1->GetCreationTime () + SSU_TO_INTRODUCER_SESSION_EXPIRATION;
|
||||
if (i2p::context.AddIntroducer (introducer))
|
||||
{
|
||||
newList.push_back (ep);
|
||||
if (newList.size () >= SSU_MAX_NUM_INTRODUCERS) break;
|
||||
}
|
||||
if (it1->GetRemoteIdentity ())
|
||||
excluded.insert (it1->GetRemoteIdentity ()->GetIdentHash ());
|
||||
}
|
||||
}
|
||||
m_Introducers = newList;
|
||||
if (m_Introducers.size () < SSU_MAX_NUM_INTRODUCERS)
|
||||
introducers = newList;
|
||||
if (introducers.size () < SSU_MAX_NUM_INTRODUCERS)
|
||||
{
|
||||
auto introducer = i2p::data::netdb.GetRandomIntroducer ();
|
||||
if (introducer)
|
||||
CreateSession (introducer);
|
||||
for (auto i = introducers.size (); i < SSU_MAX_NUM_INTRODUCERS; i++)
|
||||
{
|
||||
auto introducer = i2p::data::netdb.GetRandomIntroducer (v4, excluded);
|
||||
if (introducer)
|
||||
{
|
||||
auto address = v4 ? introducer->GetSSUAddress (true) : introducer->GetSSUV6Address ();
|
||||
if (address && !address->host.is_unspecified () && address->port)
|
||||
{
|
||||
boost::asio::ip::udp::endpoint ep (address->host, address->port);
|
||||
if (std::find (introducers.begin (), introducers.end (), ep) == introducers.end ()) // not connected yet
|
||||
{
|
||||
CreateDirectSession (introducer, ep, false);
|
||||
excluded.insert (introducer->GetIdentHash ());
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint (eLogDebug, "SSU: can't find more introducers");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
ScheduleIntroducersUpdateTimer ();
|
||||
if (v4)
|
||||
ScheduleIntroducersUpdateTimer ();
|
||||
else
|
||||
ScheduleIntroducersUpdateTimerV6 ();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@ namespace transport
|
||||
const int SSU_KEEP_ALIVE_INTERVAL = 30; // 30 seconds
|
||||
const int SSU_PEER_TEST_TIMEOUT = 60; // 60 seconds
|
||||
const int SSU_TO_INTRODUCER_SESSION_DURATION = 3600; // 1 hour
|
||||
const int SSU_TO_INTRODUCER_SESSION_EXPIRATION = 4800; // 80 minutes
|
||||
const int SSU_TERMINATION_CHECK_TIMEOUT = 30; // 30 seconds
|
||||
const size_t SSU_MAX_NUM_INTRODUCERS = 3;
|
||||
const size_t SSU_SOCKET_RECEIVE_BUFFER_SIZE = 0x1FFFF; // 128K
|
||||
@@ -48,15 +49,13 @@ namespace transport
|
||||
public:
|
||||
|
||||
SSUServer (int port);
|
||||
SSUServer (const boost::asio::ip::address & addr, int port); // ipv6 only constructor
|
||||
~SSUServer ();
|
||||
void Start ();
|
||||
void Stop ();
|
||||
void CreateSession (std::shared_ptr<const i2p::data::RouterInfo> router, bool peerTest = false, bool v4only = false);
|
||||
void CreateSession (std::shared_ptr<const i2p::data::RouterInfo> router,
|
||||
const boost::asio::ip::address& addr, int port, bool peerTest = false);
|
||||
bool CreateSession (std::shared_ptr<const i2p::data::RouterInfo> router, bool peerTest = false, bool v4only = false);
|
||||
bool CreateSession (std::shared_ptr<const i2p::data::RouterInfo> router,
|
||||
std::shared_ptr<const i2p::data::RouterInfo::Address> address, bool peerTest = false);
|
||||
void CreateDirectSession (std::shared_ptr<const i2p::data::RouterInfo> router, boost::asio::ip::udp::endpoint remoteEndpoint, bool peerTest);
|
||||
std::shared_ptr<SSUSession> FindSession (std::shared_ptr<const i2p::data::RouterInfo> router) const;
|
||||
std::shared_ptr<SSUSession> FindSession (const boost::asio::ip::udp::endpoint& e) const;
|
||||
std::shared_ptr<SSUSession> GetRandomEstablishedV4Session (std::shared_ptr<const SSUSession> excluded);
|
||||
std::shared_ptr<SSUSession> GetRandomEstablishedV6Session (std::shared_ptr<const SSUSession> excluded);
|
||||
@@ -64,12 +63,16 @@ namespace transport
|
||||
void DeleteAllSessions ();
|
||||
|
||||
boost::asio::io_service& GetService () { return m_Service; };
|
||||
const boost::asio::ip::udp::endpoint& GetEndpoint () const { return m_Endpoint; };
|
||||
uint16_t GetPort () const { return m_Endpoint.port (); };
|
||||
void SetLocalAddress (const boost::asio::ip::address& localAddress);
|
||||
|
||||
void Send (const uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& to);
|
||||
void AddRelay (uint32_t tag, std::shared_ptr<SSUSession> relay);
|
||||
void RemoveRelay (uint32_t tag);
|
||||
std::shared_ptr<SSUSession> FindRelaySession (uint32_t tag);
|
||||
|
||||
void RescheduleIntroducersUpdateTimer ();
|
||||
void RescheduleIntroducersUpdateTimerV6 ();
|
||||
|
||||
void NewPeerTest (uint32_t nonce, PeerTestParticipant role, std::shared_ptr<SSUSession> session = nullptr);
|
||||
PeerTestParticipant GetPeerTestParticipant (uint32_t nonce);
|
||||
std::shared_ptr<SSUSession> GetPeerTestSession (uint32_t nonce);
|
||||
@@ -90,15 +93,17 @@ namespace transport
|
||||
void HandleReceivedPackets (std::vector<SSUPacket *> packets,
|
||||
std::map<boost::asio::ip::udp::endpoint, std::shared_ptr<SSUSession> >* sessions);
|
||||
|
||||
void CreateSessionThroughIntroducer (std::shared_ptr<const i2p::data::RouterInfo> router, bool peerTest = false);
|
||||
void CreateSessionThroughIntroducer (std::shared_ptr<const i2p::data::RouterInfo> router,
|
||||
std::shared_ptr<const i2p::data::RouterInfo::Address> address, bool peerTest = false);
|
||||
template<typename Filter>
|
||||
std::shared_ptr<SSUSession> GetRandomV4Session (Filter filter);
|
||||
template<typename Filter>
|
||||
std::shared_ptr<SSUSession> GetRandomV6Session (Filter filter);
|
||||
|
||||
std::set<SSUSession *> FindIntroducers (int maxNumIntroducers);
|
||||
std::list<std::shared_ptr<SSUSession> > FindIntroducers (int maxNumIntroducers, bool v4, std::set<i2p::data::IdentHash>& excluded);
|
||||
void ScheduleIntroducersUpdateTimer ();
|
||||
void HandleIntroducersUpdateTimer (const boost::system::error_code& ecode);
|
||||
void ScheduleIntroducersUpdateTimerV6 ();
|
||||
void HandleIntroducersUpdateTimer (const boost::system::error_code& ecode, bool v4);
|
||||
|
||||
void SchedulePeerTestsCleanupTimer ();
|
||||
void HandlePeerTestsCleanupTimer (const boost::system::error_code& ecode);
|
||||
@@ -118,16 +123,15 @@ namespace transport
|
||||
std::shared_ptr<SSUSession> session; // for Bob to Alice
|
||||
};
|
||||
|
||||
bool m_OnlyV6;
|
||||
volatile bool m_IsRunning;
|
||||
std::thread * m_Thread, * m_ReceiversThread, * m_ReceiversThreadV6;
|
||||
boost::asio::io_service m_Service, m_ReceiversService, m_ReceiversServiceV6;
|
||||
boost::asio::io_service::work m_Work, m_ReceiversWork, m_ReceiversWorkV6;
|
||||
boost::asio::ip::udp::endpoint m_Endpoint, m_EndpointV6;
|
||||
boost::asio::ip::udp::socket m_Socket, m_SocketV6;
|
||||
boost::asio::deadline_timer m_IntroducersUpdateTimer, m_PeerTestsCleanupTimer,
|
||||
m_TerminationTimer, m_TerminationTimerV6;
|
||||
std::list<boost::asio::ip::udp::endpoint> m_Introducers; // introducers we are connected to
|
||||
boost::asio::deadline_timer m_IntroducersUpdateTimer, m_IntroducersUpdateTimerV6,
|
||||
m_PeerTestsCleanupTimer, m_TerminationTimer, m_TerminationTimerV6;
|
||||
std::list<boost::asio::ip::udp::endpoint> m_Introducers, m_IntroducersV6; // introducers we are connected to
|
||||
std::map<boost::asio::ip::udp::endpoint, std::shared_ptr<SSUSession> > m_Sessions, m_SessionsV6;
|
||||
std::map<uint32_t, std::shared_ptr<SSUSession> > m_Relays; // we are introducer
|
||||
std::map<uint32_t, PeerTest> m_PeerTests; // nonce -> creation time in milliseconds
|
||||
|
||||
@@ -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
|
||||
*
|
||||
@@ -120,7 +120,8 @@ namespace transport
|
||||
else
|
||||
{
|
||||
// try own intro key
|
||||
auto address = i2p::context.GetRouterInfo ().GetSSUAddress (false);
|
||||
auto address = IsV6 () ? i2p::context.GetRouterInfo ().GetSSUV6Address () :
|
||||
i2p::context.GetRouterInfo ().GetSSUAddress (true);
|
||||
if (!address)
|
||||
{
|
||||
LogPrint (eLogInfo, "SSU is not supported");
|
||||
@@ -212,7 +213,7 @@ namespace transport
|
||||
{
|
||||
uint8_t extendedOptionsLen = buf[headerSize];
|
||||
headerSize++;
|
||||
if (extendedOptionsLen >= 3) // options are presented
|
||||
if (extendedOptionsLen >= 2) // options are presented
|
||||
{
|
||||
uint16_t flags = bufbe16toh (buf + headerSize);
|
||||
sendRelayTag = flags & EXTENDED_OPTIONS_FLAG_REQUEST_RELAY_TAG;
|
||||
@@ -257,27 +258,14 @@ namespace transport
|
||||
s.Insert (m_DHKeysPair->GetPublicKey (), 256); // x
|
||||
s.Insert (y, 256); // y
|
||||
payload += 256;
|
||||
uint8_t addressSize = *payload;
|
||||
payload += 1; // size
|
||||
uint8_t * ourAddress = payload;
|
||||
boost::asio::ip::address ourIP;
|
||||
if (addressSize == 4) // v4
|
||||
{
|
||||
boost::asio::ip::address_v4::bytes_type bytes;
|
||||
memcpy (bytes.data (), ourAddress, 4);
|
||||
ourIP = boost::asio::ip::address_v4 (bytes);
|
||||
}
|
||||
else // v6
|
||||
{
|
||||
boost::asio::ip::address_v6::bytes_type bytes;
|
||||
memcpy (bytes.data (), ourAddress, 16);
|
||||
ourIP = boost::asio::ip::address_v6 (bytes);
|
||||
}
|
||||
s.Insert (ourAddress, addressSize); // our IP
|
||||
payload += addressSize; // address
|
||||
uint16_t ourPort = bufbe16toh (payload);
|
||||
s.Insert (payload, 2); // our port
|
||||
payload += 2; // port
|
||||
uint16_t ourPort = 0;
|
||||
auto addressAndPortLen = ExtractIPAddressAndPort (payload, len, ourIP, ourPort);
|
||||
if (!addressAndPortLen) return;
|
||||
uint8_t * ourAddressAndPort = payload + 1;
|
||||
payload += addressAndPortLen;
|
||||
addressAndPortLen--; // -1 byte address size
|
||||
s.Insert (ourAddressAndPort, addressAndPortLen); // address + port
|
||||
if (m_RemoteEndpoint.address ().is_v4 ())
|
||||
s.Insert (m_RemoteEndpoint.address ().to_v4 ().to_bytes ().data (), 4); // remote IP v4
|
||||
else
|
||||
@@ -286,7 +274,7 @@ namespace transport
|
||||
s.Insert (payload, 8); // relayTag and signed on time
|
||||
m_RelayTag = bufbe32toh (payload);
|
||||
payload += 4; // relayTag
|
||||
if (i2p::context.GetStatus () == eRouterStatusTesting)
|
||||
if (ourIP.is_v4 () && i2p::context.GetStatus () == eRouterStatusTesting)
|
||||
{
|
||||
auto ts = i2p::util::GetSecondsSinceEpoch ();
|
||||
uint32_t signedOnTime = bufbe32toh(payload);
|
||||
@@ -308,8 +296,16 @@ namespace transport
|
||||
if (s.Verify (m_RemoteIdentity, payload))
|
||||
{
|
||||
LogPrint (eLogInfo, "SSU: Our external address is ", ourIP.to_string (), ":", ourPort);
|
||||
i2p::context.UpdateAddress (ourIP);
|
||||
SendSessionConfirmed (y, ourAddress, addressSize + 2);
|
||||
if (!i2p::util::net::IsInReservedRange (ourIP))
|
||||
{
|
||||
i2p::context.UpdateAddress (ourIP);
|
||||
SendSessionConfirmed (y, ourAddressAndPort, addressAndPortLen);
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint (eLogError, "SSU: Wrong external address ", ourIP.to_string ());
|
||||
Failed ();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -321,15 +317,21 @@ namespace transport
|
||||
void SSUSession::ProcessSessionConfirmed (const uint8_t * buf, size_t len)
|
||||
{
|
||||
LogPrint (eLogDebug, "SSU: Session confirmed received");
|
||||
m_ConnectTimer.cancel ();
|
||||
auto headerSize = GetSSUHeaderSize (buf);
|
||||
if (headerSize >= len)
|
||||
{
|
||||
LogPrint (eLogError, "SSU: Session confirmed header size ", len, " exceeds packet length ", len);
|
||||
LogPrint (eLogError, "SSU: Session confirmed header size ", headerSize, " exceeds packet length ", len);
|
||||
return;
|
||||
}
|
||||
const uint8_t * payload = buf + headerSize;
|
||||
payload++; // identity fragment info
|
||||
uint16_t identitySize = bufbe16toh (payload);
|
||||
if (identitySize + headerSize + 7 > len) // 7 = fragment info + fragment size + signed on time
|
||||
{
|
||||
LogPrint (eLogError, "SSU: Session confirmed identity size ", identitySize, " exceeds packet length ", len);
|
||||
return;
|
||||
}
|
||||
payload += 2; // size of identity fragment
|
||||
auto identity = std::make_shared<i2p::data::IdentityEx> (payload, identitySize);
|
||||
auto existing = i2p::data::netdb.FindRouter (identity->GetIdentHash ()); // check if exists already
|
||||
@@ -347,10 +349,15 @@ namespace transport
|
||||
if (m_SignedData)
|
||||
m_SignedData->Insert (payload, 4); // insert Alice's signed on time
|
||||
payload += 4; // signed-on time
|
||||
size_t paddingSize = (payload - buf) + m_RemoteIdentity->GetSignatureLen ();
|
||||
paddingSize &= 0x0F; // %16
|
||||
size_t fullSize = (payload - buf) + m_RemoteIdentity->GetSignatureLen ();
|
||||
size_t paddingSize = fullSize & 0x0F; // %16
|
||||
if (paddingSize > 0) paddingSize = 16 - paddingSize;
|
||||
payload += paddingSize;
|
||||
if (fullSize + paddingSize > len)
|
||||
{
|
||||
LogPrint (eLogError, "SSU: Session confirmed message is too short ", len);
|
||||
return;
|
||||
}
|
||||
// verify signature
|
||||
if (m_SignedData && m_SignedData->Verify (m_RemoteIdentity, payload))
|
||||
{
|
||||
@@ -370,18 +377,19 @@ namespace transport
|
||||
uint8_t * payload = buf + sizeof (SSUHeader);
|
||||
uint8_t flag = 0;
|
||||
// fill extended options, 3 bytes extended options don't change message size
|
||||
if (i2p::context.GetStatus () == eRouterStatusOK) // we don't need relays
|
||||
bool isV4 = m_RemoteEndpoint.address ().is_v4 ();
|
||||
if ((isV4 && i2p::context.GetStatus () == eRouterStatusOK) ||
|
||||
(!isV4 && i2p::context.GetStatusV6 () == eRouterStatusOK)) // we don't need relays
|
||||
{
|
||||
// tell out peer to now assign relay tag
|
||||
flag = SSU_HEADER_EXTENDED_OPTIONS_INCLUDED;
|
||||
*payload = 2; payload++; // 1 byte length
|
||||
*payload = 2; payload++; // 1 byte length
|
||||
uint16_t flags = 0; // clear EXTENDED_OPTIONS_FLAG_REQUEST_RELAY_TAG
|
||||
htobe16buf (payload, flags);
|
||||
payload += 2;
|
||||
}
|
||||
// fill payload
|
||||
memcpy (payload, m_DHKeysPair->GetPublicKey (), 256); // x
|
||||
bool isV4 = m_RemoteEndpoint.address ().is_v4 ();
|
||||
if (isV4)
|
||||
{
|
||||
payload[256] = 4;
|
||||
@@ -401,7 +409,8 @@ namespace transport
|
||||
|
||||
void SSUSession::SendRelayRequest (const i2p::data::RouterInfo::Introducer& introducer, uint32_t nonce)
|
||||
{
|
||||
auto address = i2p::context.GetRouterInfo ().GetSSUAddress (false);
|
||||
auto address = IsV6 () ? i2p::context.GetRouterInfo ().GetSSUV6Address () :
|
||||
i2p::context.GetRouterInfo ().GetSSUAddress (true);
|
||||
if (!address)
|
||||
{
|
||||
LogPrint (eLogInfo, "SSU is not supported");
|
||||
@@ -429,6 +438,7 @@ namespace transport
|
||||
else
|
||||
FillHeaderAndEncrypt (PAYLOAD_TYPE_RELAY_REQUEST, buf, 96, introducer.iKey, iv, introducer.iKey);
|
||||
m_Server.Send (buf, 96, m_RemoteEndpoint);
|
||||
LogPrint (eLogDebug, "SSU: relay request sent");
|
||||
}
|
||||
|
||||
void SSUSession::SendSessionCreated (const uint8_t * x, bool sendRelayTag)
|
||||
@@ -474,7 +484,7 @@ namespace transport
|
||||
else
|
||||
s.Insert (address->host.to_v6 ().to_bytes ().data (), 16); // our IP V6
|
||||
s.Insert<uint16_t> (htobe16 (address->port)); // our port
|
||||
if (sendRelayTag && i2p::context.GetRouterInfo ().IsIntroducer () && !IsV6 ())
|
||||
if (sendRelayTag && i2p::context.GetRouterInfo ().IsIntroducer (!IsV6 ()))
|
||||
{
|
||||
RAND_bytes((uint8_t *)&m_SentRelayTag, 4);
|
||||
if (!m_SentRelayTag) m_SentRelayTag = 1;
|
||||
@@ -578,22 +588,33 @@ namespace transport
|
||||
void SSUSession::SendRelayResponse (uint32_t nonce, const boost::asio::ip::udp::endpoint& from,
|
||||
const uint8_t * introKey, const boost::asio::ip::udp::endpoint& to)
|
||||
{
|
||||
// Charlie's address always v4
|
||||
if (!to.address ().is_v4 ())
|
||||
bool isV4 = to.address ().is_v4 (); // Charle's
|
||||
bool isV4A = from.address ().is_v4 (); // Alice's
|
||||
if ((isV4 && !isV4A) || (!isV4 && isV4A))
|
||||
{
|
||||
LogPrint (eLogWarning, "SSU: Charlie's IP must be v4");
|
||||
LogPrint (eLogWarning, "SSU: Charlie's IP and Alice's IP belong to different networks for relay response");
|
||||
return;
|
||||
}
|
||||
uint8_t buf[80 + 18] = {0}; // 64 Alice's ipv4 and 80 Alice's ipv6
|
||||
uint8_t buf[80 + 18] = {0}; // 64 for ipv4 and 80 for ipv6
|
||||
uint8_t * payload = buf + sizeof (SSUHeader);
|
||||
*payload = 4;
|
||||
payload++; // size
|
||||
htobe32buf (payload, to.address ().to_v4 ().to_ulong ()); // Charlie's IP
|
||||
payload += 4; // address
|
||||
// Charlie
|
||||
if (isV4)
|
||||
{
|
||||
*payload = 4;
|
||||
payload++; // size
|
||||
memcpy (payload, to.address ().to_v4 ().to_bytes ().data (), 4); // Charlie's IP V4
|
||||
payload += 4; // address
|
||||
}
|
||||
else
|
||||
{
|
||||
*payload = 16;
|
||||
payload++; // size
|
||||
memcpy (payload, to.address ().to_v6 ().to_bytes ().data (), 16); // Alice's IP V6
|
||||
payload += 16; // address
|
||||
}
|
||||
htobe16buf (payload, to.port ()); // Charlie's port
|
||||
payload += 2; // port
|
||||
// Alice
|
||||
bool isV4 = from.address ().is_v4 (); // Alice's
|
||||
if (isV4)
|
||||
{
|
||||
*payload = 4;
|
||||
@@ -632,57 +653,67 @@ namespace transport
|
||||
void SSUSession::SendRelayIntro (std::shared_ptr<SSUSession> session, const boost::asio::ip::udp::endpoint& from)
|
||||
{
|
||||
if (!session) return;
|
||||
// Alice's address always v4
|
||||
if (!from.address ().is_v4 ())
|
||||
bool isV4 = from.address ().is_v4 (); // Alice's
|
||||
bool isV4C = session->m_RemoteEndpoint.address ().is_v4 (); // Charlie's
|
||||
if ((isV4 && !isV4C) || (!isV4 && isV4C))
|
||||
{
|
||||
LogPrint (eLogWarning, "SSU: Alice's IP must be v4");
|
||||
LogPrint (eLogWarning, "SSU: Charlie's IP and Alice's IP belong to different networks for relay intro");
|
||||
return;
|
||||
}
|
||||
uint8_t buf[48 + 18] = {0};
|
||||
uint8_t buf[64 + 18] = {0}; // 48 for ipv4 and 64 for ipv6
|
||||
uint8_t * payload = buf + sizeof (SSUHeader);
|
||||
*payload = 4;
|
||||
payload++; // size
|
||||
htobe32buf (payload, from.address ().to_v4 ().to_ulong ()); // Alice's IP
|
||||
payload += 4; // address
|
||||
if (isV4)
|
||||
{
|
||||
*payload = 4;
|
||||
payload++; // size
|
||||
memcpy (payload, from.address ().to_v4 ().to_bytes ().data (), 4); // Alice's IP V4
|
||||
payload += 4; // address
|
||||
}
|
||||
else
|
||||
{
|
||||
*payload = 16;
|
||||
payload++; // size
|
||||
memcpy (payload, from.address ().to_v6 ().to_bytes ().data (), 16); // Alice's IP V6
|
||||
payload += 16; // address
|
||||
}
|
||||
htobe16buf (payload, from.port ()); // Alice's port
|
||||
payload += 2; // port
|
||||
*payload = 0; // challenge size
|
||||
uint8_t iv[16];
|
||||
RAND_bytes (iv, 16); // random iv
|
||||
FillHeaderAndEncrypt (PAYLOAD_TYPE_RELAY_INTRO, buf, 48, session->m_SessionKey, iv, session->m_MacKey);
|
||||
m_Server.Send (buf, 48, session->m_RemoteEndpoint);
|
||||
FillHeaderAndEncrypt (PAYLOAD_TYPE_RELAY_INTRO, buf, isV4 ? 48 : 64, session->m_SessionKey, iv, session->m_MacKey);
|
||||
m_Server.Send (buf, isV4 ? 48 : 64, session->m_RemoteEndpoint);
|
||||
LogPrint (eLogDebug, "SSU: relay intro sent");
|
||||
}
|
||||
|
||||
void SSUSession::ProcessRelayResponse (const uint8_t * buf, size_t len)
|
||||
{
|
||||
LogPrint (eLogDebug, "SSU message: Relay response received");
|
||||
uint8_t remoteSize = *buf;
|
||||
buf++; // remote size
|
||||
boost::asio::ip::address_v4 remoteIP (bufbe32toh (buf));
|
||||
buf += remoteSize; // remote address
|
||||
uint16_t remotePort = bufbe16toh (buf);
|
||||
buf += 2; // remote port
|
||||
uint8_t ourSize = *buf;
|
||||
buf++; // our size
|
||||
boost::asio::ip::address remoteIP;
|
||||
uint16_t remotePort = 0;
|
||||
auto remoteSize = ExtractIPAddressAndPort (buf, len, remoteIP, remotePort);
|
||||
if (!remoteSize) return;
|
||||
buf += remoteSize; len -= remoteSize;
|
||||
boost::asio::ip::address ourIP;
|
||||
if (ourSize == 4)
|
||||
{
|
||||
boost::asio::ip::address_v4::bytes_type bytes;
|
||||
memcpy (bytes.data (), buf, 4);
|
||||
ourIP = boost::asio::ip::address_v4 (bytes);
|
||||
}
|
||||
else
|
||||
{
|
||||
boost::asio::ip::address_v6::bytes_type bytes;
|
||||
memcpy (bytes.data (), buf, 16);
|
||||
ourIP = boost::asio::ip::address_v6 (bytes);
|
||||
}
|
||||
buf += ourSize; // our address
|
||||
uint16_t ourPort = bufbe16toh (buf);
|
||||
buf += 2; // our port
|
||||
uint16_t ourPort = 0;
|
||||
auto ourSize = ExtractIPAddressAndPort (buf, len, ourIP, ourPort);
|
||||
if (!ourSize) return;
|
||||
buf += ourSize; len -= ourSize;
|
||||
LogPrint (eLogInfo, "SSU: Our external address is ", ourIP.to_string (), ":", ourPort);
|
||||
i2p::context.UpdateAddress (ourIP);
|
||||
if (!i2p::util::net::IsInReservedRange (ourIP))
|
||||
i2p::context.UpdateAddress (ourIP);
|
||||
else
|
||||
LogPrint (eLogWarning, "SSU: Wrong external address ", ourIP.to_string ());
|
||||
if (ourIP.is_v4 ())
|
||||
{
|
||||
if (ourPort != m_Server.GetPort ())
|
||||
{
|
||||
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);
|
||||
buf += 4; // nonce
|
||||
auto it = m_RelayRequests.find (nonce);
|
||||
@@ -695,12 +726,16 @@ namespace transport
|
||||
// we didn't have correct endpoint when sent relay request
|
||||
// now we do
|
||||
LogPrint (eLogInfo, "SSU: RelayReponse connecting to endpoint ", remoteEndpoint);
|
||||
if (i2p::context.GetRouterInfo ().UsesIntroducer ()) // if we are unreachable
|
||||
if ((remoteIP.is_v4 () && i2p::context.GetStatus () == eRouterStatusFirewalled) ||
|
||||
(remoteIP.is_v6 () && i2p::context.GetStatusV6 () == eRouterStatusFirewalled))
|
||||
m_Server.Send (buf, 0, remoteEndpoint); // send HolePunch
|
||||
// we assume that HolePunch has been sent by this time and our SessionRequest will go through
|
||||
m_Server.CreateDirectSession (it->second, remoteEndpoint, false);
|
||||
}
|
||||
// delete request
|
||||
m_RelayRequests.erase (it);
|
||||
// cancel connect timer
|
||||
m_ConnectTimer.cancel ();
|
||||
}
|
||||
else
|
||||
LogPrint (eLogError, "SSU: Unsolicited RelayResponse, nonce=", nonce);
|
||||
@@ -708,18 +743,12 @@ namespace transport
|
||||
|
||||
void SSUSession::ProcessRelayIntro (const uint8_t * buf, size_t len)
|
||||
{
|
||||
uint8_t size = *buf;
|
||||
if (size == 4)
|
||||
{
|
||||
buf++; // size
|
||||
boost::asio::ip::address_v4 address (bufbe32toh (buf));
|
||||
buf += 4; // address
|
||||
uint16_t port = bufbe16toh (buf);
|
||||
boost::asio::ip::address ip;
|
||||
uint16_t port = 0;
|
||||
ExtractIPAddressAndPort (buf, len, ip, port);
|
||||
if (!ip.is_unspecified () && port)
|
||||
// send hole punch of 0 bytes
|
||||
m_Server.Send (buf, 0, boost::asio::ip::udp::endpoint (address, port));
|
||||
}
|
||||
else
|
||||
LogPrint (eLogWarning, "SSU: Address size ", size, " is not supported");
|
||||
m_Server.Send (buf, 0, boost::asio::ip::udp::endpoint (ip, port));
|
||||
}
|
||||
|
||||
void SSUSession::FillHeaderAndEncrypt (uint8_t payloadType, uint8_t * buf, size_t len,
|
||||
@@ -978,15 +1007,15 @@ namespace transport
|
||||
void SSUSession::ProcessPeerTest (const uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint)
|
||||
{
|
||||
uint32_t nonce = bufbe32toh (buf); // 4 bytes
|
||||
uint8_t size = buf[4]; // 1 byte
|
||||
const uint8_t * address = buf + 5; // big endian, size bytes
|
||||
uint16_t port = buf16toh(buf + size + 5); // big endian, 2 bytes
|
||||
const uint8_t * introKey = buf + size + 7;
|
||||
if (port && (size != 4) && (size != 16))
|
||||
boost::asio::ip::address addr; // Alice's addresss
|
||||
uint16_t port = 0; // and port
|
||||
auto size = ExtractIPAddressAndPort (buf + 4, len - 4, addr, port);
|
||||
if (port && (size != 7) && (size != 19))
|
||||
{
|
||||
LogPrint (eLogWarning, "SSU: Address of ", size, " bytes not supported");
|
||||
LogPrint (eLogWarning, "SSU: Address of ", size - 3, " bytes not supported");
|
||||
return;
|
||||
}
|
||||
const uint8_t * introKey = buf + 4 + size;
|
||||
switch (m_Server.GetPeerTestParticipant (nonce))
|
||||
{
|
||||
// existing test
|
||||
@@ -995,15 +1024,29 @@ namespace transport
|
||||
if (m_Server.GetPeerTestSession (nonce) == shared_from_this ()) // Alice-Bob
|
||||
{
|
||||
LogPrint (eLogDebug, "SSU: peer test from Bob. We are Alice");
|
||||
if (i2p::context.GetStatus () == eRouterStatusTesting) // still not OK
|
||||
if (IsV6 ())
|
||||
{
|
||||
if (i2p::context.GetStatusV6 () == eRouterStatusTesting)
|
||||
{
|
||||
i2p::context.SetStatusV6 (eRouterStatusFirewalled);
|
||||
m_Server.RescheduleIntroducersUpdateTimerV6 ();
|
||||
}
|
||||
}
|
||||
else if (i2p::context.GetStatus () == eRouterStatusTesting) // still not OK
|
||||
{
|
||||
i2p::context.SetStatus (eRouterStatusFirewalled);
|
||||
m_Server.RescheduleIntroducersUpdateTimer ();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint (eLogDebug, "SSU: first peer test from Charlie. We are Alice");
|
||||
if (m_State == eSessionStateEstablished)
|
||||
LogPrint (eLogWarning, "SSU: first peer test from Charlie through established session. We are Alice");
|
||||
i2p::context.SetStatus (eRouterStatusOK);
|
||||
if (IsV6 ())
|
||||
i2p::context.SetStatusV6 (eRouterStatusOK);
|
||||
else
|
||||
i2p::context.SetStatus (eRouterStatusOK);
|
||||
m_Server.UpdatePeerTest (nonce, ePeerTestParticipantAlice2);
|
||||
SendPeerTest (nonce, senderEndpoint.address (), senderEndpoint.port (), introKey, true, false); // to Charlie
|
||||
}
|
||||
@@ -1017,7 +1060,10 @@ namespace transport
|
||||
{
|
||||
// peer test successive
|
||||
LogPrint (eLogDebug, "SSU: second peer test from Charlie. We are Alice");
|
||||
i2p::context.SetStatus (eRouterStatusOK);
|
||||
if (IsV6 ())
|
||||
i2p::context.SetStatusV6 (eRouterStatusOK);
|
||||
else
|
||||
i2p::context.SetStatus (eRouterStatusOK);
|
||||
m_Server.RemovePeerTest (nonce);
|
||||
}
|
||||
break;
|
||||
@@ -1049,20 +1095,7 @@ namespace transport
|
||||
LogPrint (eLogDebug, "SSU: peer test from Bob. We are Charlie");
|
||||
m_Server.NewPeerTest (nonce, ePeerTestParticipantCharlie);
|
||||
Send (PAYLOAD_TYPE_PEER_TEST, buf, len); // back to Bob
|
||||
boost::asio::ip::address addr; // Alice's address
|
||||
if (size == 4) // v4
|
||||
{
|
||||
boost::asio::ip::address_v4::bytes_type bytes;
|
||||
memcpy (bytes.data (), address, 4);
|
||||
addr = boost::asio::ip::address_v4 (bytes);
|
||||
}
|
||||
else // v6
|
||||
{
|
||||
boost::asio::ip::address_v6::bytes_type bytes;
|
||||
memcpy (bytes.data (), address, 16);
|
||||
addr = boost::asio::ip::address_v6 (bytes);
|
||||
}
|
||||
SendPeerTest (nonce, addr, be16toh (port), introKey); // to Alice with her address received from Bob
|
||||
SendPeerTest (nonce, addr, port, introKey); // to Alice with her address received from Bob
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1119,7 +1152,8 @@ namespace transport
|
||||
if (toAddress)
|
||||
{
|
||||
// send our intro key to address instead of its own
|
||||
auto addr = i2p::context.GetRouterInfo ().GetSSUAddress ();
|
||||
auto addr = address.is_v4 () ? i2p::context.GetRouterInfo ().GetSSUAddress (true) : // ipv4
|
||||
i2p::context.GetRouterInfo ().GetSSUV6Address ();
|
||||
if (addr)
|
||||
memcpy (payload, addr->ssu->key, 32); // intro key
|
||||
else
|
||||
@@ -1149,7 +1183,7 @@ namespace transport
|
||||
{
|
||||
// we are Alice
|
||||
LogPrint (eLogDebug, "SSU: sending peer test");
|
||||
auto address = i2p::context.GetRouterInfo ().GetSSUAddress (i2p::context.SupportsV4 ());
|
||||
auto address = IsV6 () ? i2p::context.GetRouterInfo ().GetSSUV6Address () : i2p::context.GetRouterInfo ().GetSSUAddress (true);
|
||||
if (!address)
|
||||
{
|
||||
LogPrint (eLogInfo, "SSU is not supported. Can't send peer test");
|
||||
@@ -1222,5 +1256,36 @@ namespace transport
|
||||
i2p::transport::transports.UpdateSentBytes (size);
|
||||
m_Server.Send (buf, size, m_RemoteEndpoint);
|
||||
}
|
||||
|
||||
size_t SSUSession::ExtractIPAddressAndPort (const uint8_t * buf, size_t len, boost::asio::ip::address& ip, uint16_t& port)
|
||||
{
|
||||
if (!len) return 0;
|
||||
uint8_t size = *buf;
|
||||
size_t s = 1 + size + 2; // size + address + port
|
||||
if (len < s)
|
||||
{
|
||||
LogPrint (eLogWarning, "SSU: Address is too short ", len);
|
||||
port = 0;
|
||||
return len;
|
||||
}
|
||||
buf++; // size
|
||||
if (size == 4)
|
||||
{
|
||||
boost::asio::ip::address_v4::bytes_type bytes;
|
||||
memcpy (bytes.data (), buf, 4);
|
||||
ip = boost::asio::ip::address_v4 (bytes);
|
||||
}
|
||||
else if (size == 16)
|
||||
{
|
||||
boost::asio::ip::address_v6::bytes_type bytes;
|
||||
memcpy (bytes.data (), buf, 16);
|
||||
ip = boost::asio::ip::address_v6 (bytes);
|
||||
}
|
||||
else
|
||||
LogPrint (eLogWarning, "SSU: Address size ", size, " is not supported");
|
||||
buf += size;
|
||||
port = bufbe16toh (buf);
|
||||
return s;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,7 +102,8 @@ namespace transport
|
||||
uint32_t GetRelayTag () const { return m_RelayTag; };
|
||||
const i2p::data::RouterInfo::IntroKey& GetIntroKey () const { return m_IntroKey; };
|
||||
uint32_t GetCreationTime () const { return m_CreationTime; };
|
||||
|
||||
void SetCreationTime (uint32_t ts) { m_CreationTime = ts; }; // for introducers
|
||||
|
||||
void FlushData ();
|
||||
|
||||
private:
|
||||
@@ -145,6 +146,8 @@ namespace transport
|
||||
|
||||
void Reset ();
|
||||
|
||||
static size_t ExtractIPAddressAndPort (const uint8_t * buf, size_t len, boost::asio::ip::address& ip, uint16_t& port); // returns actual buf size
|
||||
|
||||
private:
|
||||
|
||||
friend class SSUData; // TODO: change in later
|
||||
|
||||
@@ -942,15 +942,26 @@ namespace stream
|
||||
{
|
||||
if (!m_RemoteLeaseSet || m_RemoteLeaseSet->IsExpired ())
|
||||
{
|
||||
m_RemoteLeaseSet = m_LocalDestination.GetOwner ()->FindLeaseSet (m_RemoteIdentity->GetIdentHash ());
|
||||
if (!m_RemoteLeaseSet)
|
||||
auto remoteLeaseSet = m_LocalDestination.GetOwner ()->FindLeaseSet (m_RemoteIdentity->GetIdentHash ());
|
||||
if (!remoteLeaseSet)
|
||||
{
|
||||
LogPrint (eLogWarning, "Streaming: LeaseSet ", m_RemoteIdentity->GetIdentHash ().ToBase64 (), " not found");
|
||||
m_LocalDestination.GetOwner ()->RequestDestination (m_RemoteIdentity->GetIdentHash ()); // try to request for a next attempt
|
||||
LogPrint (eLogWarning, "Streaming: LeaseSet ", m_RemoteIdentity->GetIdentHash ().ToBase64 (), m_RemoteLeaseSet ? " expired" : " not found");
|
||||
if (m_RemoteLeaseSet && m_RemoteLeaseSet->IsPublishedEncrypted ())
|
||||
{
|
||||
m_LocalDestination.GetOwner ()->RequestDestinationWithEncryptedLeaseSet (
|
||||
std::make_shared<i2p::data::BlindedPublicKey>(m_RemoteIdentity));
|
||||
return; // we keep m_RemoteLeaseSet for possible next request
|
||||
}
|
||||
else
|
||||
{
|
||||
m_RemoteLeaseSet = nullptr;
|
||||
m_LocalDestination.GetOwner ()->RequestDestination (m_RemoteIdentity->GetIdentHash ()); // try to request for a next attempt
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// LeaseSet updated
|
||||
m_RemoteLeaseSet = remoteLeaseSet;
|
||||
m_RemoteIdentity = m_RemoteLeaseSet->GetIdentity ();
|
||||
m_TransientVerifier = m_RemoteLeaseSet->GetTransientVerifier ();
|
||||
}
|
||||
|
||||
@@ -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
|
||||
*
|
||||
@@ -137,7 +137,7 @@ namespace transport
|
||||
m_IsOnline (true), m_IsRunning (false), m_IsNAT (true), m_CheckReserved(true), m_Thread (nullptr),
|
||||
m_Service (nullptr), m_Work (nullptr), m_PeerCleanupTimer (nullptr), m_PeerTestTimer (nullptr),
|
||||
m_SSUServer (nullptr), m_NTCP2Server (nullptr),
|
||||
m_X25519KeysPairSupplier (5), // 5 pre-generated keys
|
||||
m_X25519KeysPairSupplier (15), // 15 pre-generated keys
|
||||
m_TotalSentBytes(0), m_TotalReceivedBytes(0), m_TotalTransitTransmittedBytes (0),
|
||||
m_InBandwidth (0), m_OutBandwidth (0), m_TransitBandwidth(0),
|
||||
m_LastInBandwidthUpdateBytes (0), m_LastOutBandwidthUpdateBytes (0),
|
||||
@@ -174,9 +174,9 @@ namespace transport
|
||||
std::string ntcp2proxy; i2p::config::GetOption("ntcp2.proxy", ntcp2proxy);
|
||||
i2p::http::URL proxyurl;
|
||||
// create NTCP2. TODO: move to acceptor
|
||||
if (enableNTCP2)
|
||||
if (enableNTCP2 || i2p::context.SupportsMesh ())
|
||||
{
|
||||
if(!ntcp2proxy.empty())
|
||||
if(!ntcp2proxy.empty() && enableNTCP2)
|
||||
{
|
||||
if(proxyurl.parse(ntcp2proxy))
|
||||
{
|
||||
@@ -188,51 +188,101 @@ namespace transport
|
||||
if (proxyurl.schema == "http")
|
||||
proxytype = NTCP2Server::eHTTPProxy;
|
||||
|
||||
m_NTCP2Server->UseProxy(proxytype, proxyurl.host, proxyurl.port);
|
||||
m_NTCP2Server->Start();
|
||||
m_NTCP2Server->UseProxy(proxytype, proxyurl.host, proxyurl.port, proxyurl.user, proxyurl.pass);
|
||||
i2p::context.SetStatus (eRouterStatusProxy);
|
||||
}
|
||||
else
|
||||
LogPrint(eLogError, "Transports: unsupported NTCP2 proxy URL ", ntcp2proxy);
|
||||
}
|
||||
else
|
||||
LogPrint(eLogError, "Transports: invalid NTCP2 proxy url ", ntcp2proxy);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_NTCP2Server = new NTCP2Server ();
|
||||
m_NTCP2Server->Start ();
|
||||
}
|
||||
}
|
||||
|
||||
// create acceptors
|
||||
auto& addresses = context.GetRouterInfo ().GetAddresses ();
|
||||
for (const auto& address : addresses)
|
||||
{
|
||||
if (!address) continue;
|
||||
if (address->transportStyle == RouterInfo::eTransportSSU)
|
||||
// create SSU server
|
||||
int ssuPort = 0;
|
||||
if (enableSSU)
|
||||
{
|
||||
auto& addresses = context.GetRouterInfo ().GetAddresses ();
|
||||
for (const auto& address: addresses)
|
||||
{
|
||||
if (m_SSUServer == nullptr && enableSSU)
|
||||
if (!address) continue;
|
||||
if (address->transportStyle == RouterInfo::eTransportSSU)
|
||||
{
|
||||
if (address->host.is_v4())
|
||||
m_SSUServer = new SSUServer (address->port);
|
||||
else
|
||||
m_SSUServer = new SSUServer (address->host, address->port);
|
||||
LogPrint (eLogInfo, "Transports: Start listening UDP port ", address->port);
|
||||
try {
|
||||
m_SSUServer->Start ();
|
||||
} catch ( std::exception & ex ) {
|
||||
LogPrint(eLogError, "Transports: Failed to bind to UDP port", address->port);
|
||||
delete m_SSUServer;
|
||||
m_SSUServer = nullptr;
|
||||
continue;
|
||||
}
|
||||
DetectExternalIP ();
|
||||
ssuPort = address->port;
|
||||
m_SSUServer = new SSUServer (address->port);
|
||||
break;
|
||||
}
|
||||
else
|
||||
LogPrint (eLogError, "Transports: SSU server already exists");
|
||||
}
|
||||
}
|
||||
|
||||
// bind to interfaces
|
||||
bool ipv4; i2p::config::GetOption("ipv4", ipv4);
|
||||
if (ipv4)
|
||||
{
|
||||
std::string address; i2p::config::GetOption("address4", address);
|
||||
if (!address.empty ())
|
||||
{
|
||||
boost::system::error_code ec;
|
||||
auto addr = boost::asio::ip::address::from_string (address, ec);
|
||||
if (!ec)
|
||||
{
|
||||
if (m_NTCP2Server) m_NTCP2Server->SetLocalAddress (addr);
|
||||
if (m_SSUServer) m_SSUServer->SetLocalAddress (addr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool ipv6; i2p::config::GetOption("ipv6", ipv6);
|
||||
if (ipv6)
|
||||
{
|
||||
std::string address; i2p::config::GetOption("address6", address);
|
||||
if (!address.empty ())
|
||||
{
|
||||
boost::system::error_code ec;
|
||||
auto addr = boost::asio::ip::address::from_string (address, ec);
|
||||
if (!ec)
|
||||
{
|
||||
if (m_NTCP2Server) m_NTCP2Server->SetLocalAddress (addr);
|
||||
if (m_SSUServer) m_SSUServer->SetLocalAddress (addr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool ygg; i2p::config::GetOption("meshnets.yggdrasil", ygg);
|
||||
if (ygg)
|
||||
{
|
||||
std::string address; i2p::config::GetOption("meshnets.yggaddress", address);
|
||||
if (!address.empty ())
|
||||
{
|
||||
boost::system::error_code ec;
|
||||
auto addr = boost::asio::ip::address::from_string (address, ec);
|
||||
if (!ec && m_NTCP2Server && i2p::util::net::IsYggdrasilAddress (addr))
|
||||
m_NTCP2Server->SetLocalAddress (addr);
|
||||
}
|
||||
}
|
||||
|
||||
// start servers
|
||||
if (m_NTCP2Server) m_NTCP2Server->Start ();
|
||||
if (m_SSUServer)
|
||||
{
|
||||
LogPrint (eLogInfo, "Transports: Start listening UDP port ", ssuPort);
|
||||
try
|
||||
{
|
||||
m_SSUServer->Start ();
|
||||
}
|
||||
catch (std::exception& ex )
|
||||
{
|
||||
LogPrint(eLogError, "Transports: Failed to bind to UDP port", ssuPort);
|
||||
m_SSUServer->Stop ();
|
||||
delete m_SSUServer;
|
||||
m_SSUServer = nullptr;
|
||||
}
|
||||
if (m_SSUServer) DetectExternalIP ();
|
||||
}
|
||||
|
||||
m_PeerCleanupTimer->expires_from_now (boost::posix_time::seconds(5*SESSION_CREATION_TIMEOUT));
|
||||
m_PeerCleanupTimer->async_wait (std::bind (&Transports::HandlePeerCleanupTimer, this, std::placeholders::_1));
|
||||
|
||||
@@ -351,7 +401,7 @@ namespace transport
|
||||
try
|
||||
{
|
||||
auto r = netdb.FindRouter (ident);
|
||||
if (!r || !r->IsCompatible (i2p::context.GetRouterInfo ())) return;
|
||||
if (!r || r->IsUnreachable () || !r->IsCompatible (i2p::context.GetRouterInfo ())) return;
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_PeersMutex);
|
||||
it = m_Peers.insert (std::pair<i2p::data::IdentHash, Peer>(ident, { 0, r, {},
|
||||
@@ -418,19 +468,10 @@ namespace transport
|
||||
if (address)
|
||||
{
|
||||
auto s = std::make_shared<NTCP2Session> (*m_NTCP2Server, peer.router, address);
|
||||
|
||||
if(m_NTCP2Server->UsingProxy())
|
||||
{
|
||||
NTCP2Server::RemoteAddressType remote = NTCP2Server::eIP4Address;
|
||||
std::string addr = address->host.to_string();
|
||||
|
||||
if(address->host.is_v6())
|
||||
remote = NTCP2Server::eIP6Address;
|
||||
|
||||
m_NTCP2Server->ConnectWithProxy(addr, address->port, remote, s);
|
||||
}
|
||||
if( m_NTCP2Server->UsingProxy())
|
||||
m_NTCP2Server->ConnectWithProxy(s);
|
||||
else
|
||||
m_NTCP2Server->Connect (address->host, address->port, s);
|
||||
m_NTCP2Server->Connect (s);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -462,10 +503,10 @@ namespace transport
|
||||
}
|
||||
peer.numAttempts++;
|
||||
}
|
||||
if (address)
|
||||
if (address && address->IsReachableSSU ())
|
||||
{
|
||||
m_SSUServer->CreateSession (peer.router, address->host, address->port);
|
||||
return true;
|
||||
if (m_SSUServer->CreateSession (peer.router, address))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -480,7 +521,7 @@ namespace transport
|
||||
if (address)
|
||||
{
|
||||
auto s = std::make_shared<NTCP2Session> (*m_NTCP2Server, peer.router, address);
|
||||
m_NTCP2Server->Connect (address->host, address->port, s);
|
||||
m_NTCP2Server->Connect (s);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -535,68 +576,66 @@ namespace transport
|
||||
return;
|
||||
}
|
||||
if (m_SSUServer)
|
||||
{
|
||||
bool isv4 = i2p::context.SupportsV4 ();
|
||||
if (m_IsNAT && isv4)
|
||||
i2p::context.SetStatus (eRouterStatusTesting);
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
auto router = i2p::data::netdb.GetRandomPeerTestRouter (isv4); // v4 only if v4
|
||||
if (router)
|
||||
m_SSUServer->CreateSession (router, true, isv4); // peer test
|
||||
else
|
||||
{
|
||||
// if not peer test capable routers found pick any
|
||||
router = i2p::data::netdb.GetRandomRouter ();
|
||||
if (router && router->IsSSU ())
|
||||
m_SSUServer->CreateSession (router); // no peer test
|
||||
}
|
||||
}
|
||||
if (i2p::context.SupportsV6 ())
|
||||
{
|
||||
// try to connect to few v6 addresses to get our address back
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
auto router = i2p::data::netdb.GetRandomSSUV6Router ();
|
||||
if (router)
|
||||
{
|
||||
auto addr = router->GetSSUV6Address ();
|
||||
if (addr)
|
||||
m_SSUServer->GetService ().post ([this, router, addr]
|
||||
{
|
||||
m_SSUServer->CreateDirectSession (router, { addr->host, (uint16_t)addr->port }, false);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
PeerTest ();
|
||||
else
|
||||
LogPrint (eLogError, "Transports: Can't detect external IP. SSU is not available");
|
||||
}
|
||||
|
||||
void Transports::PeerTest ()
|
||||
void Transports::PeerTest (bool ipv4, bool ipv6)
|
||||
{
|
||||
if (RoutesRestricted() || !i2p::context.SupportsV4 ()) return;
|
||||
if (m_SSUServer)
|
||||
if (RoutesRestricted() || !m_SSUServer) return;
|
||||
if (ipv4 && i2p::context.SupportsV4 ())
|
||||
{
|
||||
LogPrint (eLogInfo, "Transports: Started peer test");
|
||||
LogPrint (eLogInfo, "Transports: Started peer test ipv4");
|
||||
std::set<i2p::data::IdentHash> excluded;
|
||||
bool statusChanged = false;
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
auto router = i2p::data::netdb.GetRandomPeerTestRouter (true); // v4 only
|
||||
auto router = i2p::data::netdb.GetRandomPeerTestRouter (true, excluded); // v4
|
||||
if (router)
|
||||
{
|
||||
if (!statusChanged)
|
||||
auto addr = router->GetSSUAddress (true); // ipv4
|
||||
if (addr && !i2p::util::net::IsInReservedRange(addr->host))
|
||||
{
|
||||
statusChanged = true;
|
||||
i2p::context.SetStatus (eRouterStatusTesting); // first time only
|
||||
}
|
||||
m_SSUServer->CreateSession (router, true, true); // peer test v4
|
||||
if (!statusChanged)
|
||||
{
|
||||
statusChanged = true;
|
||||
i2p::context.SetStatus (eRouterStatusTesting); // first time only
|
||||
}
|
||||
m_SSUServer->CreateSession (router, addr, true); // peer test v4
|
||||
}
|
||||
excluded.insert (router->GetIdentHash ());
|
||||
}
|
||||
}
|
||||
if (!statusChanged)
|
||||
LogPrint (eLogWarning, "Transports: Can't find routers for peer test");
|
||||
LogPrint (eLogWarning, "Transports: Can't find routers for peer test ipv4");
|
||||
}
|
||||
if (ipv6 && i2p::context.SupportsV6 ())
|
||||
{
|
||||
LogPrint (eLogInfo, "Transports: Started peer test ipv6");
|
||||
std::set<i2p::data::IdentHash> excluded;
|
||||
bool statusChanged = false;
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
auto router = i2p::data::netdb.GetRandomPeerTestRouter (false, excluded); // v6
|
||||
if (router)
|
||||
{
|
||||
auto addr = router->GetSSUV6Address ();
|
||||
if (addr && !i2p::util::net::IsInReservedRange(addr->host))
|
||||
{
|
||||
if (!statusChanged)
|
||||
{
|
||||
statusChanged = true;
|
||||
i2p::context.SetStatusV6 (eRouterStatusTesting); // first time only
|
||||
}
|
||||
m_SSUServer->CreateSession (router, addr, true); // peer test v6
|
||||
}
|
||||
excluded.insert (router->GetIdentHash ());
|
||||
}
|
||||
}
|
||||
if (!statusChanged)
|
||||
LogPrint (eLogWarning, "Transports: Can't find routers for peer test ipv6");
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<i2p::crypto::X25519Keys> Transports::GetNextX25519KeysPair ()
|
||||
@@ -711,9 +750,12 @@ namespace transport
|
||||
++it;
|
||||
}
|
||||
UpdateBandwidth (); // TODO: use separate timer(s) for it
|
||||
if (i2p::context.GetStatus () == eRouterStatusTesting) // if still testing, repeat peer test
|
||||
DetectExternalIP ();
|
||||
m_PeerCleanupTimer->expires_from_now (boost::posix_time::seconds(5*SESSION_CREATION_TIMEOUT));
|
||||
bool ipv4Testing = i2p::context.GetStatus () == eRouterStatusTesting;
|
||||
bool ipv6Testing = i2p::context.GetStatusV6 () == eRouterStatusTesting;
|
||||
// if still testing, repeat peer test
|
||||
if (ipv4Testing || ipv6Testing)
|
||||
PeerTest (ipv4Testing, ipv6Testing);
|
||||
m_PeerCleanupTimer->expires_from_now (boost::posix_time::seconds(3*SESSION_CREATION_TIMEOUT));
|
||||
m_PeerCleanupTimer->async_wait (std::bind (&Transports::HandlePeerCleanupTimer, this, std::placeholders::_1));
|
||||
}
|
||||
}
|
||||
@@ -731,10 +773,15 @@ namespace transport
|
||||
std::shared_ptr<const i2p::data::RouterInfo> Transports::GetRandomPeer () const
|
||||
{
|
||||
if (m_Peers.empty ()) return nullptr;
|
||||
std::unique_lock<std::mutex> l(m_PeersMutex);
|
||||
auto it = m_Peers.begin ();
|
||||
std::advance (it, rand () % m_Peers.size ());
|
||||
return it != m_Peers.end () ? it->second.router : nullptr;
|
||||
i2p::data::IdentHash ident;
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_PeersMutex);
|
||||
auto it = m_Peers.begin ();
|
||||
std::advance (it, rand () % m_Peers.size ());
|
||||
if (it == m_Peers.end () || it->second.router) return nullptr; // not connected
|
||||
ident = it->first;
|
||||
}
|
||||
return i2p::data::netdb.FindRouter (ident);
|
||||
}
|
||||
void Transports::RestrictRoutesToFamilies(std::set<std::string> families)
|
||||
{
|
||||
|
||||
@@ -76,9 +76,9 @@ namespace transport
|
||||
}
|
||||
};
|
||||
|
||||
const size_t SESSION_CREATION_TIMEOUT = 10; // in seconds
|
||||
const size_t SESSION_CREATION_TIMEOUT = 15; // in seconds
|
||||
const int PEER_TEST_INTERVAL = 71; // in minutes
|
||||
const int MAX_NUM_DELAYED_MESSAGES = 50;
|
||||
const int MAX_NUM_DELAYED_MESSAGES = 150;
|
||||
class Transports
|
||||
{
|
||||
public:
|
||||
@@ -131,7 +131,7 @@ namespace transport
|
||||
|
||||
bool IsRestrictedPeer(const i2p::data::IdentHash & ident) const;
|
||||
|
||||
void PeerTest ();
|
||||
void PeerTest (bool ipv4 = true, bool ipv6 = true);
|
||||
|
||||
void SetCheckReserved (bool check) { m_CheckReserved = check; };
|
||||
bool IsCheckReserved () { return m_CheckReserved; };
|
||||
|
||||
@@ -698,7 +698,7 @@ namespace tunnel
|
||||
auto inboundTunnel = GetNextInboundTunnel ();
|
||||
auto router = i2p::transport::transports.RoutesRestricted() ?
|
||||
i2p::transport::transports.GetRestrictedPeer() :
|
||||
i2p::data::netdb.GetRandomRouter ();
|
||||
i2p::data::netdb.GetRandomRouter (i2p::context.GetSharedRouterInfo (), false); // reachable by us
|
||||
if (!inboundTunnel || !router) return;
|
||||
LogPrint (eLogDebug, "Tunnel: creating one hop outbound tunnel");
|
||||
CreateTunnel<OutboundTunnel> (
|
||||
@@ -771,7 +771,8 @@ namespace tunnel
|
||||
// trying to create one more inbound tunnel
|
||||
auto router = i2p::transport::transports.RoutesRestricted() ?
|
||||
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) {
|
||||
LogPrint (eLogWarning, "Tunnel: can't find any router, skip creating tunnel");
|
||||
return;
|
||||
|
||||
@@ -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
|
||||
*
|
||||
@@ -393,19 +393,20 @@ 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 ());
|
||||
auto hop = isExploratory ? i2p::data::netdb.GetRandomRouter (prevHop):
|
||||
i2p::data::netdb.GetHighBandwidthRandomRouter (prevHop);
|
||||
auto hop = isExploratory ? i2p::data::netdb.GetRandomRouter (prevHop, reverse):
|
||||
i2p::data::netdb.GetHighBandwidthRandomRouter (prevHop, reverse);
|
||||
|
||||
if (!hop || hop->GetProfile ()->IsBad ())
|
||||
hop = i2p::data::netdb.GetRandomRouter (prevHop);
|
||||
hop = i2p::data::netdb.GetRandomRouter (prevHop, reverse);
|
||||
return hop;
|
||||
}
|
||||
|
||||
bool StandardSelectPeers(Path & peers, int numHops, bool inbound, SelectHopFunc nextHop)
|
||||
{
|
||||
int start = 0;
|
||||
auto prevHop = i2p::context.GetSharedRouterInfo ();
|
||||
if(i2p::transport::transports.RoutesRestricted())
|
||||
{
|
||||
@@ -414,31 +415,38 @@ namespace tunnel
|
||||
if(!hop) return false;
|
||||
peers.push_back(hop->GetRouterIdentity());
|
||||
prevHop = hop;
|
||||
start++;
|
||||
}
|
||||
else if (i2p::transport::transports.GetNumPeers () > 25)
|
||||
else if (i2p::transport::transports.GetNumPeers () > 100 ||
|
||||
(inbound && i2p::transport::transports.GetNumPeers () > 25))
|
||||
{
|
||||
auto r = i2p::transport::transports.GetRandomPeer ();
|
||||
if (r && !r->GetProfile ()->IsBad () &&
|
||||
(numHops > 1 || !inbound || r->IsReachable ())) // first must be reachable
|
||||
(numHops > 1 || (r->IsV4 () && (!inbound || r->IsReachable ())))) // first inbound must be reachable
|
||||
{
|
||||
prevHop = r;
|
||||
peers.push_back (r->GetRouterIdentity ());
|
||||
numHops--;
|
||||
start++;
|
||||
}
|
||||
}
|
||||
|
||||
for(int i = 0; i < numHops; i++ )
|
||||
for(int i = start; 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)
|
||||
{
|
||||
LogPrint (eLogError, "Tunnels: Can't select next hop for ", prevHop->GetIdentHashBase64 ());
|
||||
return false;
|
||||
}
|
||||
if (inbound && (i == numHops - 1) && !hop->IsReachable ())
|
||||
if ((i == numHops - 1) && (!hop->IsV4 () || // doesn't support ipv4
|
||||
(inbound && !hop->IsReachable ()))) // IBGW is not reachable
|
||||
{
|
||||
// if first is not reachable try again
|
||||
auto hop1 = nextHop (prevHop);
|
||||
auto hop1 = nextHop (prevHop, true);
|
||||
if (hop1) hop = hop1;
|
||||
}
|
||||
prevHop = hop;
|
||||
@@ -460,7 +468,7 @@ namespace tunnel
|
||||
}
|
||||
// explicit peers in use
|
||||
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)
|
||||
|
||||
@@ -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
|
||||
*
|
||||
@@ -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
|
||||
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;
|
||||
|
||||
// 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:
|
||||
|
||||
|
||||
@@ -67,8 +67,12 @@ int inet_pton_xp (int af, const char *src, void *dst)
|
||||
}
|
||||
#else /* !_WIN32 => UNIX */
|
||||
#include <sys/types.h>
|
||||
#ifdef ANDROID
|
||||
#include "ifaddrs.h"
|
||||
#else
|
||||
#include <ifaddrs.h>
|
||||
#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_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");
|
||||
#else
|
||||
int af = (ipv6 ? AF_INET6 : AF_INET);
|
||||
ifaddrs * addrs = nullptr;
|
||||
ifaddrs *addrs, *cur = nullptr;
|
||||
if(getifaddrs(&addrs) == 0)
|
||||
{
|
||||
// got ifaddrs
|
||||
ifaddrs * cur = addrs;
|
||||
cur = addrs;
|
||||
while(cur)
|
||||
{
|
||||
std::string cur_ifname(cur->ifa_name);
|
||||
@@ -431,10 +435,7 @@ namespace net
|
||||
|
||||
boost::asio::ip::address_v6 GetYggdrasilAddress ()
|
||||
{
|
||||
#if defined(ANDROID)
|
||||
// TODO: implement
|
||||
return boost::asio::ip::address_v6 ();
|
||||
#elif defined(_WIN32)
|
||||
#if defined(_WIN32)
|
||||
ULONG outBufLen = 0;
|
||||
PIP_ADAPTER_ADDRESSES pAddresses = nullptr;
|
||||
PIP_ADAPTER_ADDRESSES pCurrAddresses = nullptr;
|
||||
@@ -482,11 +483,11 @@ namespace net
|
||||
FREE(pAddresses);
|
||||
return boost::asio::ip::address_v6 ();
|
||||
#else
|
||||
ifaddrs * addrs = nullptr;
|
||||
ifaddrs *addrs, *cur = nullptr;
|
||||
auto err = getifaddrs(&addrs);
|
||||
if (!err)
|
||||
{
|
||||
ifaddrs * cur = addrs;
|
||||
cur = addrs;
|
||||
while(cur)
|
||||
{
|
||||
if (cur->ifa_addr && cur->ifa_addr->sa_family == AF_INET6)
|
||||
@@ -523,6 +524,7 @@ namespace net
|
||||
bool IsInReservedRange (const boost::asio::ip::address& host)
|
||||
{
|
||||
// https://en.wikipedia.org/wiki/Reserved_IP_addresses
|
||||
if (host.is_unspecified ()) return false;
|
||||
if(host.is_v4())
|
||||
{
|
||||
static const std::vector< std::pair<uint32_t, uint32_t> > reservedIPv4Ranges {
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
#define MAKE_VERSION_NUMBER(a,b,c) ((a*100+b)*100+c)
|
||||
|
||||
#define I2PD_VERSION_MAJOR 2
|
||||
#define I2PD_VERSION_MINOR 36
|
||||
#define I2PD_VERSION_MINOR 38
|
||||
#define I2PD_VERSION_MICRO 0
|
||||
#define I2PD_VERSION_PATCH 0
|
||||
#define I2PD_VERSION MAKE_VERSION(I2PD_VERSION_MAJOR, I2PD_VERSION_MINOR, I2PD_VERSION_MICRO)
|
||||
@@ -30,7 +30,7 @@
|
||||
|
||||
#define I2P_VERSION_MAJOR 0
|
||||
#define I2P_VERSION_MINOR 9
|
||||
#define I2P_VERSION_MICRO 49
|
||||
#define I2P_VERSION_MICRO 50
|
||||
#define I2P_VERSION_PATCH 0
|
||||
#define I2P_VERSION MAKE_VERSION(I2P_VERSION_MAJOR, I2P_VERSION_MINOR, I2P_VERSION_MICRO)
|
||||
#define I2P_VERSION_NUMBER MAKE_VERSION_NUMBER(I2P_VERSION_MAJOR, I2P_VERSION_MINOR, I2P_VERSION_MICRO)
|
||||
|
||||
@@ -645,6 +645,22 @@ namespace client
|
||||
localDestination->RequestDestinationWithEncryptedLeaseSet (addr->blindedPublicKey, requstCallback);
|
||||
}
|
||||
|
||||
void BOBCommandSession::LookupLocalCommandHandler (const char * operand, size_t len)
|
||||
{
|
||||
LogPrint (eLogDebug, "BOB: lookup local ", operand);
|
||||
auto addr = context.GetAddressBook ().GetAddress (operand);
|
||||
if (!addr)
|
||||
{
|
||||
SendReplyError ("Address Not found");
|
||||
return;
|
||||
}
|
||||
auto ls = i2p::data::netdb.FindLeaseSet (addr->identHash);
|
||||
if (ls)
|
||||
SendReplyOK (ls->GetIdentity ()->ToBase64 ().c_str ());
|
||||
else
|
||||
SendReplyError ("Local LeaseSet Not found");
|
||||
}
|
||||
|
||||
void BOBCommandSession::ClearCommandHandler (const char * operand, size_t len)
|
||||
{
|
||||
LogPrint (eLogDebug, "BOB: clear");
|
||||
@@ -770,6 +786,7 @@ namespace client
|
||||
m_CommandHandlers[BOB_COMMAND_INPORT] = &BOBCommandSession::InportCommandHandler;
|
||||
m_CommandHandlers[BOB_COMMAND_QUIET] = &BOBCommandSession::QuietCommandHandler;
|
||||
m_CommandHandlers[BOB_COMMAND_LOOKUP] = &BOBCommandSession::LookupCommandHandler;
|
||||
m_CommandHandlers[BOB_COMMAND_LOOKUP_LOCAL] = &BOBCommandSession::LookupLocalCommandHandler;
|
||||
m_CommandHandlers[BOB_COMMAND_CLEAR] = &BOBCommandSession::ClearCommandHandler;
|
||||
m_CommandHandlers[BOB_COMMAND_LIST] = &BOBCommandSession::ListCommandHandler;
|
||||
m_CommandHandlers[BOB_COMMAND_OPTION] = &BOBCommandSession::OptionCommandHandler;
|
||||
|
||||
@@ -42,6 +42,7 @@ namespace client
|
||||
const char BOB_COMMAND_INPORT[] = "inport";
|
||||
const char BOB_COMMAND_QUIET[] = "quiet";
|
||||
const char BOB_COMMAND_LOOKUP[] = "lookup";
|
||||
const char BOB_COMMAND_LOOKUP_LOCAL[] = "lookuplocal";
|
||||
const char BOB_COMMAND_CLEAR[] = "clear";
|
||||
const char BOB_COMMAND_LIST[] = "list";
|
||||
const char BOB_COMMAND_OPTION[] = "option";
|
||||
@@ -206,6 +207,7 @@ namespace client
|
||||
void InportCommandHandler (const char * operand, size_t len);
|
||||
void QuietCommandHandler (const char * operand, size_t len);
|
||||
void LookupCommandHandler (const char * operand, size_t len);
|
||||
void LookupLocalCommandHandler (const char * operand, size_t len);
|
||||
void ClearCommandHandler (const char * operand, size_t len);
|
||||
void ListCommandHandler (const char * operand, size_t len);
|
||||
void OptionCommandHandler (const char * operand, size_t len);
|
||||
|
||||
@@ -691,7 +691,7 @@ namespace client
|
||||
i2p::data::SigningKeyType sigType = section.second.get (I2P_SERVER_TUNNEL_SIGNATURE_TYPE, i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519);
|
||||
i2p::data::CryptoKeyType cryptoType = section.second.get (I2P_CLIENT_TUNNEL_CRYPTO_TYPE, i2p::data::CRYPTO_KEY_TYPE_ELGAMAL);
|
||||
|
||||
std::string address = section.second.get<std::string> (I2P_SERVER_TUNNEL_ADDRESS, "127.0.0.1");
|
||||
std::string address = section.second.get<std::string> (I2P_SERVER_TUNNEL_ADDRESS, "");
|
||||
bool isUniqueLocal = section.second.get(I2P_SERVER_TUNNEL_ENABLE_UNIQUE_LOCAL, true);
|
||||
|
||||
// I2CP
|
||||
@@ -718,6 +718,7 @@ namespace client
|
||||
{
|
||||
// udp server tunnel
|
||||
// TODO: hostnames
|
||||
if (address.empty ()) address = "127.0.0.1";
|
||||
auto localAddress = boost::asio::ip::address::from_string(address);
|
||||
boost::asio::ip::udp::endpoint endpoint(boost::asio::ip::address::from_string(host), port);
|
||||
auto serverTunnel = std::make_shared<I2PUDPServerTunnel>(name, localDestination, localAddress, endpoint, port, gzip);
|
||||
@@ -750,12 +751,13 @@ namespace client
|
||||
else // regular server tunnel by default
|
||||
serverTunnel = std::make_shared<I2PServerTunnel> (name, host, port, localDestination, inPort, gzip);
|
||||
|
||||
if (!address.empty ())
|
||||
serverTunnel->SetLocalAddress (address);
|
||||
if(!isUniqueLocal)
|
||||
{
|
||||
LogPrint(eLogInfo, "Clients: disabling loopback address mapping");
|
||||
serverTunnel->SetUniqueLocal(isUniqueLocal);
|
||||
}
|
||||
|
||||
if (accessList.length () > 0)
|
||||
{
|
||||
std::set<i2p::data::IdentHash> idents;
|
||||
|
||||
@@ -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
|
||||
*
|
||||
@@ -433,13 +433,13 @@ namespace proxy {
|
||||
if (m_ProxyURL.is_i2p())
|
||||
{
|
||||
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
|
||||
m_ClientRequest.RemoveHeader("Proxy-");
|
||||
// add own http proxy authorization
|
||||
std::string s = "Basic " + i2p::data::ToBase64Standard (m_ProxyURL.user + ":" + m_ProxyURL.pass);
|
||||
m_ClientRequest.AddHeader("Proxy-Authorization", s);
|
||||
m_ClientRequest.AddHeader("Proxy-Authorization", auth);
|
||||
}
|
||||
m_send_buf = m_ClientRequest.to_string();
|
||||
m_recv_buf.erase(0, m_req_len);
|
||||
|
||||
@@ -84,7 +84,12 @@ namespace client
|
||||
m_Owner->SendMessagePayloadMessage (buf + 4, length);
|
||||
}
|
||||
|
||||
void I2CPDestination::CreateNewLeaseSet (std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> > tunnels)
|
||||
void I2CPDestination::CreateNewLeaseSet (const std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> >& tunnels)
|
||||
{
|
||||
GetService ().post (std::bind (&I2CPDestination::PostCreateNewLeaseSet, this, tunnels));
|
||||
}
|
||||
|
||||
void I2CPDestination::PostCreateNewLeaseSet (std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> > tunnels)
|
||||
{
|
||||
if (m_IsCreatingLeaseSet)
|
||||
{
|
||||
|
||||
@@ -93,7 +93,7 @@ namespace client
|
||||
|
||||
// I2CP
|
||||
void HandleDataMessage (const uint8_t * buf, size_t len);
|
||||
void CreateNewLeaseSet (std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> > tunnels);
|
||||
void CreateNewLeaseSet (const std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> >& tunnels);
|
||||
|
||||
private:
|
||||
|
||||
@@ -101,6 +101,8 @@ namespace client
|
||||
{ return std::static_pointer_cast<I2CPDestination>(shared_from_this ()); }
|
||||
bool SendMsg (std::shared_ptr<I2NPMessage> msg, std::shared_ptr<const i2p::data::LeaseSet> remote);
|
||||
|
||||
void PostCreateNewLeaseSet (std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> > tunnels);
|
||||
|
||||
private:
|
||||
|
||||
std::shared_ptr<I2CPSession> m_Owner;
|
||||
|
||||
@@ -84,7 +84,11 @@ namespace client
|
||||
// bind to 127.x.x.x address
|
||||
// where x.x.x are first three bytes from ident
|
||||
auto ourIP = GetLoopbackAddressFor(addr);
|
||||
sock->bind (boost::asio::ip::tcp::endpoint (ourIP, 0));
|
||||
boost::system::error_code ec;
|
||||
sock->bind (boost::asio::ip::tcp::endpoint (ourIP, 0), ec);
|
||||
if (ec)
|
||||
LogPrint (eLogError, "I2PTunnel: can't bind ourIP to ", ourIP.to_string (), ": ", ec.message ());
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -107,6 +111,22 @@ namespace client
|
||||
}
|
||||
}
|
||||
|
||||
void I2PTunnelConnection::Connect (const boost::asio::ip::address& localAddress)
|
||||
{
|
||||
if (m_Socket)
|
||||
{
|
||||
if (m_RemoteEndpoint.address().is_v6 ())
|
||||
m_Socket->open (boost::asio::ip::tcp::v6 ());
|
||||
else
|
||||
m_Socket->open (boost::asio::ip::tcp::v4 ());
|
||||
boost::system::error_code ec;
|
||||
m_Socket->bind (boost::asio::ip::tcp::endpoint (localAddress, 0), ec);
|
||||
if (ec)
|
||||
LogPrint (eLogError, "I2PTunnel: can't bind to ", localAddress.to_string (), ": ", ec.message ());
|
||||
}
|
||||
Connect (false);
|
||||
}
|
||||
|
||||
void I2PTunnelConnection::Terminate ()
|
||||
{
|
||||
if (Kill()) return;
|
||||
@@ -600,6 +620,16 @@ namespace client
|
||||
m_IsAccessList = true;
|
||||
}
|
||||
|
||||
void I2PServerTunnel::SetLocalAddress (const std::string& localAddress)
|
||||
{
|
||||
boost::system::error_code ec;
|
||||
auto addr = boost::asio::ip::address::from_string(localAddress, ec);
|
||||
if (!ec)
|
||||
m_LocalAddress.reset (new boost::asio::ip::address (addr));
|
||||
else
|
||||
LogPrint (eLogError, "I2PTunnel: can't set local address ", localAddress);
|
||||
}
|
||||
|
||||
void I2PServerTunnel::Accept ()
|
||||
{
|
||||
if (m_PortDestination)
|
||||
@@ -631,7 +661,10 @@ namespace client
|
||||
// new connection
|
||||
auto conn = CreateI2PConnection (stream);
|
||||
AddHandler (conn);
|
||||
conn->Connect (m_IsUniqueLocal);
|
||||
if (m_LocalAddress)
|
||||
conn->Connect (*m_LocalAddress);
|
||||
else
|
||||
conn->Connect (m_IsUniqueLocal);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -48,7 +48,8 @@ namespace client
|
||||
~I2PTunnelConnection ();
|
||||
void I2PConnect (const uint8_t * msg = nullptr, size_t len = 0);
|
||||
void Connect (bool isUniqueLocal = true);
|
||||
|
||||
void Connect (const boost::asio::ip::address& localAddress);
|
||||
|
||||
protected:
|
||||
|
||||
void Terminate ();
|
||||
@@ -314,6 +315,8 @@ namespace client
|
||||
void SetUniqueLocal (bool isUniqueLocal) { m_IsUniqueLocal = isUniqueLocal; }
|
||||
bool IsUniqueLocal () const { return m_IsUniqueLocal; }
|
||||
|
||||
void SetLocalAddress (const std::string& localAddress);
|
||||
|
||||
const std::string& GetAddress() const { return m_Address; }
|
||||
int GetPort () const { return m_Port; };
|
||||
uint16_t GetLocalPort () const { return m_PortDestination->GetLocalPort (); };
|
||||
@@ -339,6 +342,7 @@ namespace client
|
||||
std::shared_ptr<i2p::stream::StreamingDestination> m_PortDestination;
|
||||
std::set<i2p::data::IdentHash> m_AccessList;
|
||||
bool m_IsAccessList;
|
||||
std::unique_ptr<boost::asio::ip::address> m_LocalAddress;
|
||||
};
|
||||
|
||||
class I2PServerTunnelHTTP: public I2PServerTunnel
|
||||
|
||||
@@ -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
|
||||
*
|
||||
@@ -72,7 +72,8 @@ namespace client
|
||||
bool MatchedTunnelDestination::SelectPeers(i2p::tunnel::Path & path, int hops, bool inbound)
|
||||
{
|
||||
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;
|
||||
// more here for outbound tunnels
|
||||
if(!inbound && m_RemoteLeaseSet)
|
||||
|
||||
@@ -58,8 +58,8 @@ namespace client
|
||||
{
|
||||
if (Session)
|
||||
{
|
||||
if (m_IsAccepting && Session->localDestination)
|
||||
Session->localDestination->StopAcceptingStreams ();
|
||||
if (m_IsAccepting && Session->GetLocalDestination ())
|
||||
Session->GetLocalDestination ()->StopAcceptingStreams ();
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -270,6 +270,10 @@ namespace client
|
||||
ProcessDestGenerate (separator + 1, bytes_transferred - (separator - m_Buffer) - 1);
|
||||
else if (!strcmp (m_Buffer, SAM_NAMING_LOOKUP))
|
||||
ProcessNamingLookup (separator + 1, bytes_transferred - (separator - m_Buffer) - 1);
|
||||
else if (!strcmp (m_Buffer, SAM_SESSION_ADD))
|
||||
ProcessSessionAdd (separator + 1, bytes_transferred - (separator - m_Buffer) - 1);
|
||||
else if (!strcmp (m_Buffer, SAM_SESSION_REMOVE))
|
||||
ProcessSessionRemove (separator + 1, bytes_transferred - (separator - m_Buffer) - 1);
|
||||
else if (!strcmp (m_Buffer, SAM_DATAGRAM_SEND) || !strcmp (m_Buffer, SAM_RAW_SEND))
|
||||
{
|
||||
size_t len = bytes_transferred - (separator - m_Buffer) - 1;
|
||||
@@ -352,6 +356,7 @@ namespace client
|
||||
if (style == SAM_VALUE_STREAM) type = eSAMSessionTypeStream;
|
||||
else if (style == SAM_VALUE_DATAGRAM) type = eSAMSessionTypeDatagram;
|
||||
else if (style == SAM_VALUE_RAW) type = eSAMSessionTypeRaw;
|
||||
else if (style == SAM_VALUE_MASTER) type = eSAMSessionTypeMaster;
|
||||
if (type == eSAMSessionTypeUnknown)
|
||||
{
|
||||
// unknown style
|
||||
@@ -409,7 +414,7 @@ namespace client
|
||||
if (type == eSAMSessionTypeDatagram || type == eSAMSessionTypeRaw)
|
||||
{
|
||||
session->UDPEndpoint = forward;
|
||||
auto dest = session->localDestination->CreateDatagramDestination ();
|
||||
auto dest = session->GetLocalDestination ()->CreateDatagramDestination ();
|
||||
if (type == eSAMSessionTypeDatagram)
|
||||
dest->SetReceiver (std::bind (&SAMSocket::HandleI2PDatagramReceive, shared_from_this (),
|
||||
std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5));
|
||||
@@ -418,7 +423,7 @@ namespace client
|
||||
std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4));
|
||||
}
|
||||
|
||||
if (session->localDestination->IsReady ())
|
||||
if (session->GetLocalDestination ()->IsReady ())
|
||||
SendSessionCreateReplyOk ();
|
||||
else
|
||||
{
|
||||
@@ -438,7 +443,7 @@ namespace client
|
||||
auto session = m_Owner.FindSession(m_ID);
|
||||
if(session)
|
||||
{
|
||||
if (session->localDestination->IsReady ())
|
||||
if (session->GetLocalDestination ()->IsReady ())
|
||||
SendSessionCreateReplyOk ();
|
||||
else
|
||||
{
|
||||
@@ -457,7 +462,7 @@ namespace client
|
||||
{
|
||||
uint8_t buf[1024];
|
||||
char priv[1024];
|
||||
size_t l = session->localDestination->GetPrivateKeys ().ToBuffer (buf, 1024);
|
||||
size_t l = session->GetLocalDestination ()->GetPrivateKeys ().ToBuffer (buf, 1024);
|
||||
size_t l1 = i2p::data::ByteStreamToBase64 (buf, l, priv, 1024);
|
||||
priv[l1] = 0;
|
||||
#ifdef _MSC_VER
|
||||
@@ -495,20 +500,38 @@ namespace client
|
||||
else
|
||||
m_BufferOffset = 0;
|
||||
|
||||
auto dest = std::make_shared<i2p::data::IdentityEx> ();
|
||||
size_t l = dest->FromBase64(destination);
|
||||
if (l > 0)
|
||||
std::shared_ptr<const Address> addr;
|
||||
if (destination.find(".i2p") != std::string::npos)
|
||||
addr = context.GetAddressBook().GetAddress (destination);
|
||||
else
|
||||
{
|
||||
context.GetAddressBook().InsertFullAddress(dest);
|
||||
auto leaseSet = session->localDestination->FindLeaseSet(dest->GetIdentHash());
|
||||
if (leaseSet)
|
||||
Connect(leaseSet, session);
|
||||
else
|
||||
auto dest = std::make_shared<i2p::data::IdentityEx> ();
|
||||
size_t l = dest->FromBase64(destination);
|
||||
if (l > 0)
|
||||
{
|
||||
session->localDestination->RequestDestination(dest->GetIdentHash(),
|
||||
context.GetAddressBook().InsertFullAddress(dest);
|
||||
addr = std::make_shared<Address>(dest->GetIdentHash ());
|
||||
}
|
||||
}
|
||||
|
||||
if (addr && addr->IsValid ())
|
||||
{
|
||||
if (addr->IsIdentHash ())
|
||||
{
|
||||
auto leaseSet = session->GetLocalDestination ()->FindLeaseSet(addr->identHash);
|
||||
if (leaseSet)
|
||||
Connect(leaseSet, session);
|
||||
else
|
||||
{
|
||||
session->GetLocalDestination ()->RequestDestination(addr->identHash,
|
||||
std::bind(&SAMSocket::HandleConnectLeaseSetRequestComplete,
|
||||
shared_from_this(), std::placeholders::_1));
|
||||
}
|
||||
}
|
||||
else // B33
|
||||
session->GetLocalDestination ()->RequestDestinationWithEncryptedLeaseSet (addr->blindedPublicKey,
|
||||
std::bind(&SAMSocket::HandleConnectLeaseSetRequestComplete,
|
||||
shared_from_this(), std::placeholders::_1));
|
||||
}
|
||||
}
|
||||
else
|
||||
SendMessageReply (SAM_STREAM_STATUS_INVALID_KEY, strlen(SAM_STREAM_STATUS_INVALID_KEY), true);
|
||||
@@ -523,7 +546,7 @@ namespace client
|
||||
if (session)
|
||||
{
|
||||
m_SocketType = eSAMSocketTypeStream;
|
||||
m_Stream = session->localDestination->CreateStream (remote);
|
||||
m_Stream = session->GetLocalDestination ()->CreateStream (remote);
|
||||
if (m_Stream)
|
||||
{
|
||||
m_Stream->Send ((uint8_t *)m_Buffer, m_BufferOffset); // connect and send
|
||||
@@ -567,10 +590,10 @@ namespace client
|
||||
if (session)
|
||||
{
|
||||
m_SocketType = eSAMSocketTypeAcceptor;
|
||||
if (!session->localDestination->IsAcceptingStreams ())
|
||||
if (!session->GetLocalDestination ()->IsAcceptingStreams ())
|
||||
{
|
||||
m_IsAccepting = true;
|
||||
session->localDestination->AcceptOnce (std::bind (&SAMSocket::HandleI2PAccept, shared_from_this (), std::placeholders::_1));
|
||||
session->GetLocalDestination ()->AcceptOnce (std::bind (&SAMSocket::HandleI2PAccept, shared_from_this (), std::placeholders::_1));
|
||||
}
|
||||
SendMessageReply (SAM_STREAM_STATUS_OK, strlen(SAM_STREAM_STATUS_OK), false);
|
||||
}
|
||||
@@ -590,7 +613,7 @@ namespace client
|
||||
SendMessageReply (SAM_STREAM_STATUS_INVALID_ID, strlen(SAM_STREAM_STATUS_INVALID_ID), true);
|
||||
return;
|
||||
}
|
||||
if (session->localDestination->IsAcceptingStreams ())
|
||||
if (session->GetLocalDestination ()->IsAcceptingStreams ())
|
||||
{
|
||||
SendI2PError ("Already accepting");
|
||||
return;
|
||||
@@ -620,7 +643,7 @@ namespace client
|
||||
m_IsAccepting = true;
|
||||
std::string& silent = params[SAM_PARAM_SILENT];
|
||||
if (silent == SAM_VALUE_TRUE) m_IsSilent = true;
|
||||
session->localDestination->AcceptStreams (std::bind (&SAMSocket::HandleI2PForward,
|
||||
session->GetLocalDestination ()->AcceptStreams (std::bind (&SAMSocket::HandleI2PForward,
|
||||
shared_from_this (), std::placeholders::_1, ep));
|
||||
SendMessageReply (SAM_STREAM_STATUS_OK, strlen(SAM_STREAM_STATUS_OK), false);
|
||||
}
|
||||
@@ -636,7 +659,7 @@ namespace client
|
||||
auto session = m_Owner.FindSession(m_ID);
|
||||
if (session)
|
||||
{
|
||||
auto d = session->localDestination->GetDatagramDestination ();
|
||||
auto d = session->GetLocalDestination ()->GetDatagramDestination ();
|
||||
if (d)
|
||||
{
|
||||
i2p::data::IdentityEx dest;
|
||||
@@ -706,7 +729,7 @@ namespace client
|
||||
std::shared_ptr<const i2p::data::IdentityEx> identity;
|
||||
std::shared_ptr<const Address> addr;
|
||||
auto session = m_Owner.FindSession(m_ID);
|
||||
auto dest = session == nullptr ? context.GetSharedLocalDestination() : session->localDestination;
|
||||
auto dest = session == nullptr ? context.GetSharedLocalDestination() : session->GetLocalDestination ();
|
||||
if (name == "ME")
|
||||
SendNamingLookupReply (name, dest->GetIdentity ());
|
||||
else if ((identity = context.GetAddressBook ().GetFullAddress (name)) != nullptr)
|
||||
@@ -740,6 +763,73 @@ namespace client
|
||||
}
|
||||
}
|
||||
|
||||
void SAMSocket::ProcessSessionAdd (char * buf, size_t len)
|
||||
{
|
||||
auto session = m_Owner.FindSession(m_ID);
|
||||
if (session && session->Type == eSAMSessionTypeMaster)
|
||||
{
|
||||
LogPrint (eLogDebug, "SAM: subsession add: ", buf);
|
||||
auto masterSession = std::static_pointer_cast<SAMMasterSession>(session);
|
||||
std::map<std::string, std::string> params;
|
||||
ExtractParams (buf, params);
|
||||
std::string& id = params[SAM_PARAM_ID];
|
||||
if (masterSession->subsessions.count (id) > 1)
|
||||
{
|
||||
// session exists
|
||||
SendMessageReply (SAM_SESSION_CREATE_DUPLICATED_ID, strlen(SAM_SESSION_CREATE_DUPLICATED_ID), false);
|
||||
return;
|
||||
}
|
||||
std::string& style = params[SAM_PARAM_STYLE];
|
||||
SAMSessionType type = eSAMSessionTypeUnknown;
|
||||
if (style == SAM_VALUE_STREAM) type = eSAMSessionTypeStream;
|
||||
// TODO: implement other styles
|
||||
if (type == eSAMSessionTypeUnknown)
|
||||
{
|
||||
// unknown style
|
||||
SendI2PError("Unsupported STYLE");
|
||||
return;
|
||||
}
|
||||
auto fromPort = std::stoi(params[SAM_PARAM_FROM_PORT]);
|
||||
if (fromPort == -1)
|
||||
{
|
||||
SendI2PError("Invalid from port");
|
||||
return;
|
||||
}
|
||||
auto subsession = std::make_shared<SAMSubSession>(masterSession, id, type, fromPort);
|
||||
if (m_Owner.AddSession (subsession))
|
||||
{
|
||||
masterSession->subsessions.insert (id);
|
||||
SendSessionCreateReplyOk ();
|
||||
}
|
||||
else
|
||||
SendMessageReply (SAM_SESSION_CREATE_DUPLICATED_ID, strlen(SAM_SESSION_CREATE_DUPLICATED_ID), false);
|
||||
}
|
||||
else
|
||||
SendI2PError ("Wrong session type");
|
||||
}
|
||||
|
||||
void SAMSocket::ProcessSessionRemove (char * buf, size_t len)
|
||||
{
|
||||
auto session = m_Owner.FindSession(m_ID);
|
||||
if (session && session->Type == eSAMSessionTypeMaster)
|
||||
{
|
||||
LogPrint (eLogDebug, "SAM: subsession remove: ", buf);
|
||||
auto masterSession = std::static_pointer_cast<SAMMasterSession>(session);
|
||||
std::map<std::string, std::string> params;
|
||||
ExtractParams (buf, params);
|
||||
std::string& id = params[SAM_PARAM_ID];
|
||||
if (!masterSession->subsessions.erase (id))
|
||||
{
|
||||
SendMessageReply (SAM_SESSION_STATUS_INVALID_KEY, strlen(SAM_SESSION_STATUS_INVALID_KEY), false);
|
||||
return;
|
||||
}
|
||||
m_Owner.CloseSession (id);
|
||||
SendSessionCreateReplyOk ();
|
||||
}
|
||||
else
|
||||
SendI2PError ("Wrong session type");
|
||||
}
|
||||
|
||||
void SAMSocket::SendI2PError(const std::string & msg)
|
||||
{
|
||||
LogPrint (eLogError, "SAM: i2p error ", msg);
|
||||
@@ -952,7 +1042,7 @@ namespace client
|
||||
if (it->m_SocketType == eSAMSocketTypeAcceptor)
|
||||
{
|
||||
it->m_IsAccepting = true;
|
||||
session->localDestination->AcceptOnce (std::bind (&SAMSocket::HandleI2PAccept, it, std::placeholders::_1));
|
||||
session->GetLocalDestination ()->AcceptOnce (std::bind (&SAMSocket::HandleI2PAccept, it, std::placeholders::_1));
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -1090,19 +1180,11 @@ namespace client
|
||||
m_Owner.GetService ().post (std::bind( !ec ? &SAMSocket::Receive : &SAMSocket::TerminateClose, shared_from_this()));
|
||||
}
|
||||
|
||||
SAMSession::SAMSession (SAMBridge & parent, const std::string & id, SAMSessionType type, std::shared_ptr<ClientDestination> dest):
|
||||
m_Bridge(parent),
|
||||
localDestination (dest),
|
||||
UDPEndpoint(nullptr),
|
||||
Name(id), Type (type)
|
||||
SAMSession::SAMSession (SAMBridge & parent, const std::string & id, SAMSessionType type):
|
||||
m_Bridge(parent), Name(id), Type (type), UDPEndpoint(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
SAMSession::~SAMSession ()
|
||||
{
|
||||
i2p::client::context.DeleteLocalDestination (localDestination);
|
||||
}
|
||||
|
||||
void SAMSession::CloseStreams ()
|
||||
{
|
||||
for(const auto & itr : m_Bridge.ListSockets(Name))
|
||||
@@ -1111,6 +1193,58 @@ namespace client
|
||||
}
|
||||
}
|
||||
|
||||
SAMSingleSession::SAMSingleSession (SAMBridge & parent, const std::string & name, SAMSessionType type, std::shared_ptr<ClientDestination> dest):
|
||||
SAMSession (parent, name, type),
|
||||
localDestination (dest)
|
||||
{
|
||||
}
|
||||
|
||||
SAMSingleSession::~SAMSingleSession ()
|
||||
{
|
||||
i2p::client::context.DeleteLocalDestination (localDestination);
|
||||
}
|
||||
|
||||
void SAMSingleSession::StopLocalDestination ()
|
||||
{
|
||||
localDestination->Release ();
|
||||
localDestination->StopAcceptingStreams ();
|
||||
}
|
||||
|
||||
void SAMMasterSession::Close ()
|
||||
{
|
||||
SAMSingleSession::Close ();
|
||||
for (const auto& it: subsessions)
|
||||
m_Bridge.CloseSession (it);
|
||||
subsessions.clear ();
|
||||
}
|
||||
|
||||
SAMSubSession::SAMSubSession (std::shared_ptr<SAMMasterSession> master, const std::string& name, SAMSessionType type, int port):
|
||||
SAMSession (master->m_Bridge, name, type), masterSession (master), inPort (port)
|
||||
{
|
||||
if (Type == eSAMSessionTypeStream)
|
||||
{
|
||||
auto d = masterSession->GetLocalDestination ()->CreateStreamingDestination (inPort);
|
||||
if (d) d->Start ();
|
||||
}
|
||||
// TODO: implement datagrams
|
||||
}
|
||||
|
||||
std::shared_ptr<ClientDestination> SAMSubSession::GetLocalDestination ()
|
||||
{
|
||||
return masterSession ? masterSession->GetLocalDestination () : nullptr;
|
||||
}
|
||||
|
||||
void SAMSubSession::StopLocalDestination ()
|
||||
{
|
||||
auto dest = GetLocalDestination ();
|
||||
if (dest && Type == eSAMSessionTypeStream)
|
||||
{
|
||||
auto d = dest->RemoveStreamingDestination (inPort);
|
||||
if (d) d->Stop ();
|
||||
}
|
||||
// TODO: implement datagrams
|
||||
}
|
||||
|
||||
SAMBridge::SAMBridge (const std::string& address, int port, bool singleThread):
|
||||
RunnableService ("SAM"), m_IsSingleThread (singleThread),
|
||||
m_Acceptor (GetIOService (), boost::asio::ip::tcp::endpoint(boost::asio::ip::address::from_string(address), port)),
|
||||
@@ -1156,7 +1290,7 @@ namespace client
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_SessionsMutex);
|
||||
for (auto& it: m_Sessions)
|
||||
it.second->CloseStreams ();
|
||||
it.second->Close ();
|
||||
m_Sessions.clear ();
|
||||
}
|
||||
StopIOService ();
|
||||
@@ -1248,7 +1382,8 @@ namespace client
|
||||
if (localDestination)
|
||||
{
|
||||
localDestination->Acquire ();
|
||||
auto session = std::make_shared<SAMSession>(*this, id, type, localDestination);
|
||||
auto session = (type == eSAMSessionTypeMaster) ? std::make_shared<SAMMasterSession>(*this, id, localDestination) :
|
||||
std::make_shared<SAMSingleSession>(*this, id, type, localDestination);
|
||||
std::unique_lock<std::mutex> l(m_SessionsMutex);
|
||||
auto ret = m_Sessions.insert (std::make_pair(id, session));
|
||||
if (!ret.second)
|
||||
@@ -1258,6 +1393,13 @@ namespace client
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool SAMBridge::AddSession (std::shared_ptr<SAMSession> session)
|
||||
{
|
||||
if (!session) return false;
|
||||
auto ret = m_Sessions.emplace (session->Name, session);
|
||||
return ret.second;
|
||||
}
|
||||
|
||||
void SAMBridge::CloseSession (const std::string& id)
|
||||
{
|
||||
std::shared_ptr<SAMSession> session;
|
||||
@@ -1272,9 +1414,8 @@ namespace client
|
||||
}
|
||||
if (session)
|
||||
{
|
||||
session->localDestination->Release ();
|
||||
session->localDestination->StopAcceptingStreams ();
|
||||
session->CloseStreams ();
|
||||
session->StopLocalDestination ();
|
||||
session->Close ();
|
||||
if (m_IsSingleThread)
|
||||
{
|
||||
auto timer = std::make_shared<boost::asio::deadline_timer>(GetService ());
|
||||
@@ -1349,10 +1490,10 @@ namespace client
|
||||
i2p::data::IdentityEx dest;
|
||||
dest.FromBase64 (destination);
|
||||
if (session->Type == eSAMSessionTypeDatagram)
|
||||
session->localDestination->GetDatagramDestination ()->
|
||||
session->GetLocalDestination ()->GetDatagramDestination ()->
|
||||
SendDatagramTo ((uint8_t *)eol, payloadLen, dest.GetIdentHash ());
|
||||
else // raw
|
||||
session->localDestination->GetDatagramDestination ()->
|
||||
session->GetLocalDestination ()->GetDatagramDestination ()->
|
||||
SendRawDatagramTo ((uint8_t *)eol, payloadLen, dest.GetIdentHash ());
|
||||
}
|
||||
else
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <list>
|
||||
#include <set>
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
#include <memory>
|
||||
@@ -41,6 +42,8 @@ namespace client
|
||||
const char SAM_SESSION_CREATE_INVALID_ID[] = "SESSION STATUS RESULT=INVALID_ID\n";
|
||||
const char SAM_SESSION_STATUS_INVALID_KEY[] = "SESSION STATUS RESULT=INVALID_KEY\n";
|
||||
const char SAM_SESSION_STATUS_I2P_ERROR[] = "SESSION STATUS RESULT=I2P_ERROR MESSAGE=%s\n";
|
||||
const char SAM_SESSION_ADD[] = "SESSION ADD";
|
||||
const char SAM_SESSION_REMOVE[] = "SESSION REMOVE";
|
||||
const char SAM_STREAM_CONNECT[] = "STREAM CONNECT";
|
||||
const char SAM_STREAM_STATUS_OK[] = "STREAM STATUS RESULT=OK\n";
|
||||
const char SAM_STREAM_STATUS_INVALID_ID[] = "STREAM STATUS RESULT=INVALID_ID\n";
|
||||
@@ -72,10 +75,12 @@ namespace client
|
||||
const char SAM_PARAM_SIZE[] = "SIZE";
|
||||
const char SAM_PARAM_HOST[] = "HOST";
|
||||
const char SAM_PARAM_PORT[] = "PORT";
|
||||
const char SAM_PARAM_FROM_PORT[] = "FROM_PORT";
|
||||
const char SAM_VALUE_TRANSIENT[] = "TRANSIENT";
|
||||
const char SAM_VALUE_STREAM[] = "STREAM";
|
||||
const char SAM_VALUE_DATAGRAM[] = "DATAGRAM";
|
||||
const char SAM_VALUE_RAW[] = "RAW";
|
||||
const char SAM_VALUE_MASTER[] = "MASTER";
|
||||
const char SAM_VALUE_TRUE[] = "true";
|
||||
const char SAM_VALUE_FALSE[] = "false";
|
||||
|
||||
@@ -134,6 +139,8 @@ namespace client
|
||||
void ProcessStreamForward (char * buf, size_t len);
|
||||
void ProcessDestGenerate (char * buf, size_t len);
|
||||
void ProcessNamingLookup (char * buf, size_t len);
|
||||
void ProcessSessionAdd (char * buf, size_t len);
|
||||
void ProcessSessionRemove (char * buf, size_t len);
|
||||
void SendI2PError(const std::string & msg);
|
||||
size_t ProcessDatagramSend (char * buf, size_t len, const char * data); // from SAM 1.0
|
||||
void ExtractParams (char * buf, std::map<std::string, std::string>& params);
|
||||
@@ -171,23 +178,57 @@ namespace client
|
||||
eSAMSessionTypeUnknown,
|
||||
eSAMSessionTypeStream,
|
||||
eSAMSessionTypeDatagram,
|
||||
eSAMSessionTypeRaw
|
||||
eSAMSessionTypeRaw,
|
||||
eSAMSessionTypeMaster
|
||||
};
|
||||
|
||||
struct SAMSession
|
||||
{
|
||||
SAMBridge & m_Bridge;
|
||||
std::shared_ptr<ClientDestination> localDestination;
|
||||
std::shared_ptr<boost::asio::ip::udp::endpoint> UDPEndpoint;
|
||||
std::string Name;
|
||||
SAMSessionType Type;
|
||||
|
||||
SAMSession (SAMBridge & parent, const std::string & name, SAMSessionType type, std::shared_ptr<ClientDestination> dest);
|
||||
~SAMSession ();
|
||||
|
||||
std::shared_ptr<boost::asio::ip::udp::endpoint> UDPEndpoint; // TODO: move
|
||||
|
||||
SAMSession (SAMBridge & parent, const std::string & name, SAMSessionType type);
|
||||
virtual ~SAMSession () {};
|
||||
|
||||
virtual std::shared_ptr<ClientDestination> GetLocalDestination () = 0;
|
||||
virtual void StopLocalDestination () = 0;
|
||||
virtual void Close () { CloseStreams (); };
|
||||
|
||||
void CloseStreams ();
|
||||
};
|
||||
|
||||
struct SAMSingleSession: public SAMSession
|
||||
{
|
||||
std::shared_ptr<ClientDestination> localDestination;
|
||||
|
||||
SAMSingleSession (SAMBridge & parent, const std::string & name, SAMSessionType type, std::shared_ptr<ClientDestination> dest);
|
||||
~SAMSingleSession ();
|
||||
|
||||
std::shared_ptr<ClientDestination> GetLocalDestination () { return localDestination; };
|
||||
void StopLocalDestination ();
|
||||
};
|
||||
|
||||
struct SAMMasterSession: public SAMSingleSession
|
||||
{
|
||||
std::set<std::string> subsessions;
|
||||
SAMMasterSession (SAMBridge & parent, const std::string & name, std::shared_ptr<ClientDestination> dest):
|
||||
SAMSingleSession (parent, name, eSAMSessionTypeMaster, dest) {};
|
||||
void Close ();
|
||||
};
|
||||
|
||||
struct SAMSubSession: public SAMSession
|
||||
{
|
||||
std::shared_ptr<SAMMasterSession> masterSession;
|
||||
int inPort;
|
||||
|
||||
SAMSubSession (std::shared_ptr<SAMMasterSession> master, const std::string& name, SAMSessionType type, int port);
|
||||
// implements SAMSession
|
||||
std::shared_ptr<ClientDestination> GetLocalDestination ();
|
||||
void StopLocalDestination ();
|
||||
};
|
||||
|
||||
class SAMBridge: private i2p::util::RunnableService
|
||||
{
|
||||
public:
|
||||
@@ -201,6 +242,7 @@ namespace client
|
||||
boost::asio::io_service& GetService () { return GetIOService (); };
|
||||
std::shared_ptr<SAMSession> CreateSession (const std::string& id, SAMSessionType type, const std::string& destination, // empty string means transient
|
||||
const std::map<std::string, std::string> * params);
|
||||
bool AddSession (std::shared_ptr<SAMSession> session);
|
||||
void CloseSession (const std::string& id);
|
||||
std::shared_ptr<SAMSession> FindSession (const std::string& id) const;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user