mirror of
https://github.com/PurpleI2P/i2pd.git
synced 2025-03-07 06:09:42 +00:00
Compare commits
131 Commits
2.54.0
...
dcbe6cfaf2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dcbe6cfaf2 | ||
|
|
3534b9c499 | ||
|
|
a7021a8283 | ||
|
|
946e523554 | ||
|
|
cdd528c51f | ||
|
|
1a748aebf1 | ||
|
|
f23a7f569b | ||
|
|
48b62340cc | ||
|
|
65da550d19 | ||
|
|
786da057f2 | ||
|
|
097813a6ca | ||
|
|
226257aa71 | ||
|
|
13604ccbb6 | ||
|
|
e996db03c0 | ||
|
|
f79a2e81ff | ||
|
|
4b1ac7420c | ||
|
|
e518b92a89 | ||
|
|
1a32ed9088 | ||
|
|
b17bbd754a | ||
|
|
7b0ff2850c | ||
|
|
31ff0ff1cb | ||
|
|
fcc70025fd | ||
|
|
56145d0f3c | ||
|
|
8b9f427aa4 | ||
|
|
cc768de8ea | ||
|
|
ffd18baf30 | ||
|
|
3474538697 | ||
|
|
5f1b31213f | ||
|
|
6fb3c7c3ba | ||
|
|
a248a2a732 | ||
|
|
09ae278306 | ||
|
|
d241e5d5cb | ||
|
|
b80278421d | ||
|
|
f2596e0187 | ||
|
|
5265dc71e9 | ||
|
|
a05bb93792 | ||
|
|
5d5970bed4 | ||
|
|
86080b26ae | ||
|
|
5b2d0c579b | ||
|
|
3c4926f377 | ||
|
|
391e3b7814 | ||
|
|
72a39609ed | ||
|
|
0c5f39ad81 | ||
|
|
3c608ec07c | ||
|
|
ce0461bf86 | ||
|
|
ce96f93c80 | ||
|
|
d88ba768d7 | ||
|
|
574d12298b | ||
|
|
7285caa4f1 | ||
|
|
2778b092e3 | ||
|
|
dbef3fe9d2 | ||
|
|
09b7d44dad | ||
|
|
a411fff1d9 | ||
|
|
e574354896 | ||
|
|
0a08383471 | ||
|
|
c5e464a8b5 | ||
|
|
002d8c7773 | ||
|
|
32921ead80 | ||
|
|
be24a3e336 | ||
|
|
d99a7d9b20 | ||
|
|
2f6bdd1c84 | ||
|
|
5a4ce66d42 | ||
|
|
76190ea365 | ||
|
|
f90386803f | ||
|
|
29d77113cc | ||
|
|
0d09a8be00 | ||
|
|
b8d61e04f0 | ||
|
|
4432c5a2c4 | ||
|
|
2419f52af4 | ||
|
|
b2a10ac82b | ||
|
|
0086f8e27a | ||
|
|
8a8277edda | ||
|
|
3f10f6651d | ||
|
|
9bc595a9a2 | ||
|
|
f04048717d | ||
|
|
361f364966 | ||
|
|
4c90a88b85 | ||
|
|
23e66671c2 | ||
|
|
ec67f48d85 | ||
|
|
3a229ea65c | ||
|
|
0e8d624d86 | ||
|
|
8f9874570a | ||
|
|
43939cedf4 | ||
|
|
4c66608caf | ||
|
|
ec4fe9a1e6 | ||
|
|
79e8ccbb5b | ||
|
|
608056dcd2 | ||
|
|
7461b640e3 | ||
|
|
743126b2ad | ||
|
|
f611136ea7 | ||
|
|
87ae9c4b74 | ||
|
|
d3630fb2b2 | ||
|
|
500afe745f | ||
|
|
26901e2945 | ||
|
|
64bde69967 | ||
|
|
ddf30784ec | ||
|
|
ea14b00d63 | ||
|
|
a24e0eb2dc | ||
|
|
0cb677a2c0 | ||
|
|
e6cbc842bf | ||
|
|
f087654f25 | ||
|
|
10335b90c5 | ||
|
|
8a234f70e6 | ||
|
|
f98a310235 | ||
|
|
1419745a5d | ||
|
|
890fe77b10 | ||
|
|
bc9d25ec3b | ||
|
|
fe71776b6f | ||
|
|
0213f058d1 | ||
|
|
0ccf0a6339 | ||
|
|
e26682f4cb | ||
|
|
8981e406f5 | ||
|
|
50d9252ba9 | ||
|
|
4f73f60e51 | ||
|
|
d69e957213 | ||
|
|
97fdedfbe3 | ||
|
|
ec1f41b13c | ||
|
|
7104d334fd | ||
|
|
4e581af3ba | ||
|
|
48f7131a7d | ||
|
|
fbd07a5276 | ||
|
|
8210911bc5 | ||
|
|
4a5406b803 | ||
|
|
ab02f722af | ||
|
|
c86e0ec371 | ||
|
|
ac4c58bbe9 | ||
|
|
23bac4a403 | ||
|
|
2321a897f5 | ||
|
|
88a5f8b125 | ||
|
|
78847306e9 | ||
|
|
1a6109109a |
2
.github/workflows/build-windows.yml
vendored
2
.github/workflows/build-windows.yml
vendored
@@ -230,6 +230,8 @@ jobs:
|
||||
run: |
|
||||
cd MINGW-packages/mingw-w64-boost
|
||||
MINGW_ARCH=mingw32 makepkg-mingw -sCLf --noconfirm --nocheck
|
||||
- name: Remove boost packages
|
||||
run: pacman --noconfirm -R mingw-w64-i686-boost mingw-w64-i686-boost-libs
|
||||
- name: Install boost package
|
||||
run: pacman --noconfirm -U MINGW-packages/mingw-w64-boost/mingw-w64-i686-*-any.pkg.tar.zst
|
||||
|
||||
|
||||
1
Makefile
1
Makefile
@@ -29,7 +29,6 @@ DAEMON_SRC_DIR := daemon
|
||||
# import source files lists
|
||||
include filelist.mk
|
||||
|
||||
USE_AESNI := $(or $(USE_AESNI),yes)
|
||||
USE_STATIC := $(or $(USE_STATIC),no)
|
||||
USE_UPNP := $(or $(USE_UPNP),no)
|
||||
DEBUG := $(or $(DEBUG),yes)
|
||||
|
||||
@@ -30,13 +30,6 @@ ifeq ($(USE_UPNP),yes)
|
||||
INCFLAGS += -I${UPNPROOT}/include
|
||||
endif
|
||||
|
||||
ifeq ($(USE_AESNI),yes)
|
||||
ifneq (, $(findstring i386, $(SYS))$(findstring i686, $(SYS))$(findstring x86_64, $(SYS))) # only x86-based CPU supports that
|
||||
NEEDED_CXXFLAGS += -maes
|
||||
DEFINES += -D__AES__
|
||||
endif
|
||||
endif
|
||||
|
||||
install: all
|
||||
install -d ${PREFIX}/bin
|
||||
install -m 755 ${I2PD} ${PREFIX}/bin
|
||||
|
||||
@@ -13,12 +13,9 @@ LDFLAGS ?= ${LD_DEBUG}
|
||||
CXXVER := $(shell $(CXX) -dumpversion)
|
||||
ifeq ($(shell expr match $(CXX) 'clang'),5)
|
||||
NEEDED_CXXFLAGS += -std=c++17
|
||||
else ifeq ($(shell expr match ${CXXVER} "7"),1) # gcc 7
|
||||
NEEDED_CXXFLAGS += -std=c++17
|
||||
LDLIBS = -lboost_filesystem
|
||||
else ifeq ($(shell expr match ${CXXVER} "[8-9]"),1) # gcc 8 - 9
|
||||
NEEDED_CXXFLAGS += -std=c++17
|
||||
LDLIBS = -lstdc++fs
|
||||
LDLIBS = -lboost_system -lstdc++fs
|
||||
else ifeq ($(shell expr match ${CXXVER} "1[0-2]"),2) # gcc 10 - 12
|
||||
NEEDED_CXXFLAGS += -std=c++17
|
||||
else ifeq ($(shell expr match ${CXXVER} "1[3-9]"),2) # gcc 13+
|
||||
@@ -34,7 +31,6 @@ ifeq ($(USE_STATIC),yes)
|
||||
# Using 'getaddrinfo' in statically linked applications requires at runtime
|
||||
# the shared libraries from the glibc version used for linking
|
||||
LIBDIR := /usr/lib/$(SYS)
|
||||
LDLIBS += $(LIBDIR)/libboost_system.a
|
||||
LDLIBS += $(LIBDIR)/libboost_program_options.a
|
||||
LDLIBS += $(LIBDIR)/libssl.a
|
||||
LDLIBS += $(LIBDIR)/libcrypto.a
|
||||
@@ -44,7 +40,7 @@ ifeq ($(USE_UPNP),yes)
|
||||
endif
|
||||
LDLIBS += -lpthread -ldl
|
||||
else
|
||||
LDLIBS += -lcrypto -lssl -lz -lboost_system -lboost_program_options -lpthread -latomic
|
||||
LDLIBS += -lcrypto -lssl -lz -lboost_program_options -lpthread -latomic
|
||||
ifeq ($(USE_UPNP),yes)
|
||||
LDLIBS += -lminiupnpc
|
||||
endif
|
||||
@@ -55,13 +51,6 @@ ifeq ($(USE_UPNP),yes)
|
||||
DEFINES += -DUSE_UPNP
|
||||
endif
|
||||
|
||||
ifeq ($(USE_AESNI),yes)
|
||||
ifneq (, $(findstring i386, $(SYS))$(findstring i686, $(SYS))$(findstring x86_64, $(SYS))) # only x86-based CPU supports that
|
||||
NEEDED_CXXFLAGS += -maes
|
||||
DEFINES += -D__AES__
|
||||
endif
|
||||
endif
|
||||
|
||||
install: all
|
||||
install -d ${PREFIX}/bin
|
||||
install -m 755 ${I2PD} ${PREFIX}/bin
|
||||
|
||||
@@ -42,12 +42,6 @@ ifeq ($(USE_WIN32_APP), yes)
|
||||
DAEMON_OBJS += $(patsubst %.rc,obj/%.o,$(DAEMON_RC))
|
||||
endif
|
||||
|
||||
ifeq ($(USE_AESNI),yes)
|
||||
NEEDED_CXXFLAGS += -maes
|
||||
LDFLAGS += -maes
|
||||
DEFINES += -D__AES__
|
||||
endif
|
||||
|
||||
ifeq ($(USE_ASLR),yes)
|
||||
LDFLAGS += -Wl,--nxcompat -Wl,--high-entropy-va -Wl,--dynamicbase,--export-all-symbols
|
||||
endif
|
||||
|
||||
@@ -25,9 +25,5 @@ endif
|
||||
OSARCH = $(shell uname -p)
|
||||
|
||||
ifneq ($(OSARCH),powerpc)
|
||||
ifeq ($(USE_AESNI),yes)
|
||||
CXXFLAGS += -D__AES__ -maes
|
||||
else
|
||||
CXXFLAGS += -msse
|
||||
endif
|
||||
CXXFLAGS += -msse
|
||||
endif
|
||||
|
||||
@@ -29,7 +29,6 @@ project(
|
||||
)
|
||||
|
||||
# configurable options
|
||||
option(WITH_AESNI "Use AES-NI instructions set" ON)
|
||||
option(WITH_HARDENING "Use hardening compiler flags" OFF)
|
||||
option(WITH_LIBRARY "Build library" ON)
|
||||
option(WITH_BINARY "Build binary" ON)
|
||||
@@ -185,16 +184,6 @@ if(UNIX)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Note: AES-NI and AVX is available on x86-based CPU's.
|
||||
# Here also ARM64 implementation, but currently we don't support it.
|
||||
# MSVC is not supported due to different ASM processing, so we hope OpenSSL has its own checks to run optimized code.
|
||||
if(WITH_AESNI AND (ARCHITECTURE MATCHES "x86_64" OR ARCHITECTURE MATCHES "i386"))
|
||||
if(NOT MSVC)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -maes")
|
||||
endif()
|
||||
add_definitions(-D__AES__)
|
||||
endif()
|
||||
|
||||
if(WITH_ADDRSANITIZER)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -fno-omit-frame-pointer")
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=address")
|
||||
@@ -335,7 +324,6 @@ message(STATUS "Architecture : ${ARCHITECTURE}")
|
||||
message(STATUS "Compiler flags : ${CMAKE_CXX_FLAGS}")
|
||||
message(STATUS "Install prefix: : ${CMAKE_INSTALL_PREFIX}")
|
||||
message(STATUS "Options:")
|
||||
message(STATUS " AESNI : ${WITH_AESNI}")
|
||||
message(STATUS " HARDENING : ${WITH_HARDENING}")
|
||||
message(STATUS " LIBRARY : ${WITH_LIBRARY}")
|
||||
message(STATUS " BINARY : ${WITH_BINARY}")
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIFgTCCA2mgAwIBAgIETWAY1DANBgkqhkiG9w0BAQ0FADBxMQswCQYDVQQGEwJY
|
||||
WDELMAkGA1UECAwCWFgxCzAJBgNVBAcMAlhYMR4wHAYDVQQKDBVJMlAgQW5vbnlt
|
||||
b3VzIE5ldHdvcmsxDDAKBgNVBAsMA0kyUDEaMBgGA1UEAwwRaGlkdXNlcjBAbWFp
|
||||
bC5pMnAwHhcNMjExMjEzMTU0MDI3WhcNMzExMjExMTU0MDI3WjBxMQswCQYDVQQG
|
||||
EwJYWDELMAkGA1UECAwCWFgxCzAJBgNVBAcMAlhYMR4wHAYDVQQKDBVJMlAgQW5v
|
||||
bnltb3VzIE5ldHdvcmsxDDAKBgNVBAsMA0kyUDEaMBgGA1UEAwwRaGlkdXNlcjBA
|
||||
bWFpbC5pMnAwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDXnjJ8UQ0f
|
||||
lHHpfPMiHofBPSuL4sbOJY6fOXwPhSg/h6THh9DS/ZWmJXQ3qRD0glDVtv4/Dr/9
|
||||
ldGQ5eltF9iCFXCQlMEy2HjQrBKq0nsl7RpYK12cyMaod0kkzCUk9ITLi9CmHM3Z
|
||||
gQZcmG8TWjFEpDR+idx/QkQt2pcO4vzWlDit3Vh4ivnbX5jGQHbsVjQEMQWxr+pX
|
||||
dsS+YQpjZ6RBmrooGTPO8QDOOeYLAn0lCjmffc/kzIH9E/p4/O0rOpyhVYbdxUD1
|
||||
5wkqN9l4yrtxmORG/PudnRQQ0r4TUq8vsxfGY0Euo9IbhgXF2Parel1ZhDxB1WZV
|
||||
VwWtgLIh9jGA1UMa8SYKnEfp8LWNZ3b3mUUnZb3kMrLk6jGYRWNsHmamhd4mC7AZ
|
||||
qf/8lOkEIw3bPd3YguCDRVcLui5BwIEZmqXg8uoESxfO/sW3pBrN/8M7MkTex9kN
|
||||
vjitGDDXvenK27qmNgZxbBlX72yTSfys7XTYTLnxZC8AwdAo2Wz9Z6HhGiPonf2h
|
||||
vZkc9ZxuE0jFIrsbJra4X7iyjXgi4vV4ARNg/9Ft6F4/OIbECgeDcBQqq4TlT2bZ
|
||||
EfWVrBbqXoj5vNsLigIkd+AyUNwPYEcB5IFSiiOh98pC7BH3pg0m8U5YBjxe1i+9
|
||||
EQOOG0Qtx+JigXZHu6bGE0Twy9zy+UzoKQIDAQABoyEwHzAdBgNVHQ4EFgQUGK1b
|
||||
0DkL6aLalcfBc/Uj/SF08C0wDQYJKoZIhvcNAQENBQADggIBAMpXM82bJDpH1TlH
|
||||
TvhU3Z7nfZdvEhOQfujaFUYiuNripuEKcFGn948+DvAG0FUN+uNlJoqOVs8D7InD
|
||||
gWlA9zpqw5Cl5Hij/Wns9QbXuAHJeA23fVUoaM2A6v9ifcIQ1A+rDuRQAo6/64KW
|
||||
ChTg2e99RBpfGOyqgeh7tLLe0lPPekVpKHFuXabokaKRDuBcVHcUL4tWXe3dcyqa
|
||||
Ej/PJrrS+nWL0EGZ4q80CEd2LPuDzPxNGCJt/R7ZfadENWajcgcXGceh1QBzozrB
|
||||
SL/Ya6wF9SrsB7V/r5wX0LM4ZdDaLWbtmUe5Op0h/ZMH25Sa8xAXVz+O9L6sWSoO
|
||||
FaiYTOvAiyyPz+nsxKa3xYryDHno7eKSt+hGOcaurhxbdZaEFY/CegEc73tCt9xK
|
||||
e9qF8O/WkDLmixuErw3f5en4IfzGR7p3lJAwW/8WD8C6HS39h/eE7dVZNaWgtQnZ
|
||||
SgGjgZMTJqTcQ3aZmfuCZefxGFok8w6AIkdbnd1pdMBRjYu8aXgl2hQSB9ZADDE9
|
||||
R5d3rXi0PkSFLIvsNjVa5KXrZk/tB0Hpfmepq7CufBqjP/LG9TieRoXzLYUKFF74
|
||||
QRwjP+y7AJ+VDUTpY1NV1P+k+2raubU2bOnLF3zL5DtyoyieGPhyeMMvp0fRIxdg
|
||||
bSl5VHgPXHNM8mcnndMAuzvl7jEK
|
||||
-----END CERTIFICATE-----
|
||||
@@ -277,9 +277,3 @@ verify = 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)
|
||||
# aesni = true
|
||||
## Force usage of CPU instructions set, even if they not found (default: false)
|
||||
## DO NOT TOUCH that option if you really don't know what are you doing!
|
||||
# force = false
|
||||
|
||||
@@ -149,12 +149,10 @@ namespace util
|
||||
LogPrint(eLogDebug, "FS: Certificates directory: ", certsdir);
|
||||
|
||||
bool precomputation; i2p::config::GetOption("precomputation.elgamal", precomputation);
|
||||
bool aesni; i2p::config::GetOption("cpuext.aesni", aesni);
|
||||
bool forceCpuExt; i2p::config::GetOption("cpuext.force", forceCpuExt);
|
||||
bool ssu; i2p::config::GetOption("ssu", ssu);
|
||||
if (!ssu && i2p::config::IsDefault ("precomputation.elgamal"))
|
||||
precomputation = false; // we don't elgamal table if no ssu, unless it's specified explicitly
|
||||
i2p::crypto::InitCrypto (precomputation, aesni, forceCpuExt);
|
||||
i2p::crypto::InitCrypto (precomputation);
|
||||
|
||||
i2p::transport::InitAddressFromIface (); // get address4/6 from interfaces
|
||||
|
||||
|
||||
@@ -701,6 +701,7 @@ namespace http {
|
||||
{
|
||||
s << "<b>" << tr("Tunnels") << ":</b><br>\r\n";
|
||||
s << "<b>" << tr("Queue size") << ":</b> " << i2p::tunnel::tunnels.GetQueueSize () << "<br>\r\n<br>\r\n";
|
||||
s << "<b>" << tr("TBM Queue size") << ":</b> " << i2p::tunnel::tunnels.GetTBMQueueSize () << "<br>\r\n<br>\r\n";
|
||||
|
||||
auto ExplPool = i2p::tunnel::tunnels.GetExploratoryPool ();
|
||||
|
||||
@@ -1485,8 +1486,8 @@ namespace http {
|
||||
}
|
||||
|
||||
HTTPServer::HTTPServer (const std::string& address, int port):
|
||||
m_IsRunning (false), m_Thread (nullptr), m_Work (m_Service),
|
||||
m_Acceptor (m_Service, boost::asio::ip::tcp::endpoint (boost::asio::ip::address::from_string(address), port)),
|
||||
m_IsRunning (false), m_Thread (nullptr), m_Work (m_Service.get_executor ()),
|
||||
m_Acceptor (m_Service, boost::asio::ip::tcp::endpoint (boost::asio::ip::make_address(address), port)),
|
||||
m_Hostname(address)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -83,8 +83,8 @@ namespace http
|
||||
|
||||
bool m_IsRunning;
|
||||
std::unique_ptr<std::thread> m_Thread;
|
||||
boost::asio::io_service m_Service;
|
||||
boost::asio::io_service::work m_Work;
|
||||
boost::asio::io_context m_Service;
|
||||
boost::asio::executor_work_guard<boost::asio::io_context::executor_type> m_Work;
|
||||
boost::asio::ip::tcp::acceptor m_Acceptor;
|
||||
std::string m_Hostname;
|
||||
};
|
||||
|
||||
@@ -31,7 +31,7 @@ namespace client
|
||||
{
|
||||
I2PControlService::I2PControlService (const std::string& address, int port):
|
||||
m_IsRunning (false), m_Thread (nullptr),
|
||||
m_Acceptor (m_Service, boost::asio::ip::tcp::endpoint(boost::asio::ip::address::from_string(address), port)),
|
||||
m_Acceptor (m_Service, boost::asio::ip::tcp::endpoint(boost::asio::ip::make_address(address), port)),
|
||||
m_SSLContext (boost::asio::ssl::context::sslv23),
|
||||
m_ShutdownTimer (m_Service)
|
||||
{
|
||||
@@ -45,15 +45,29 @@ namespace client
|
||||
i2pcp_crt = i2p::fs::DataDirPath(i2pcp_crt);
|
||||
if (i2pcp_key.at(0) != '/')
|
||||
i2pcp_key = i2p::fs::DataDirPath(i2pcp_key);
|
||||
if (!i2p::fs::Exists (i2pcp_crt) || !i2p::fs::Exists (i2pcp_key)) {
|
||||
if (!i2p::fs::Exists (i2pcp_crt) || !i2p::fs::Exists (i2pcp_key))
|
||||
{
|
||||
LogPrint (eLogInfo, "I2PControl: Creating new certificate for control connection");
|
||||
CreateCertificate (i2pcp_crt.c_str(), i2pcp_key.c_str());
|
||||
} else {
|
||||
}
|
||||
else
|
||||
LogPrint(eLogDebug, "I2PControl: Using cert from ", i2pcp_crt);
|
||||
}
|
||||
m_SSLContext.set_options (boost::asio::ssl::context::default_workarounds | boost::asio::ssl::context::no_sslv2 | boost::asio::ssl::context::single_dh_use);
|
||||
m_SSLContext.use_certificate_file (i2pcp_crt, boost::asio::ssl::context::pem);
|
||||
m_SSLContext.use_private_key_file (i2pcp_key, boost::asio::ssl::context::pem);
|
||||
boost::system::error_code ec;
|
||||
m_SSLContext.use_certificate_file (i2pcp_crt, boost::asio::ssl::context::pem, ec);
|
||||
if (!ec)
|
||||
m_SSLContext.use_private_key_file (i2pcp_key, boost::asio::ssl::context::pem, ec);
|
||||
if (ec)
|
||||
{
|
||||
LogPrint (eLogInfo, "I2PControl: Failed to load ceritifcate: ", ec.message (), ". Recreating");
|
||||
CreateCertificate (i2pcp_crt.c_str(), i2pcp_key.c_str());
|
||||
m_SSLContext.use_certificate_file (i2pcp_crt, boost::asio::ssl::context::pem, ec);
|
||||
if (!ec)
|
||||
m_SSLContext.use_private_key_file (i2pcp_key, boost::asio::ssl::context::pem, ec);
|
||||
if (ec)
|
||||
// give up
|
||||
LogPrint (eLogError, "I2PControl: Can't load certificates");
|
||||
}
|
||||
|
||||
// handlers
|
||||
m_MethodHandlers["Authenticate"] = &I2PControlService::AuthenticateHandler;
|
||||
@@ -403,7 +417,7 @@ namespace client
|
||||
X509_NAME_add_entry_by_txt (name, "O", MBSTRING_ASC, (unsigned char *)I2P_CONTROL_CERTIFICATE_ORGANIZATION, -1, -1, 0); // organization
|
||||
X509_NAME_add_entry_by_txt (name, "CN", MBSTRING_ASC, (unsigned char *)I2P_CONTROL_CERTIFICATE_COMMON_NAME, -1, -1, 0); // common name
|
||||
X509_set_issuer_name (x509, name); // set issuer to ourselves
|
||||
X509_sign (x509, pkey, EVP_sha1 ()); // sign
|
||||
X509_sign (x509, pkey, EVP_sha1 ()); // sign, last param must be NULL for EdDSA
|
||||
|
||||
// save cert
|
||||
if ((f = fopen (crt_path, "wb")) != NULL) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2022, The PurpleI2P Project
|
||||
* Copyright (c) 2013-2024, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
@@ -88,7 +88,7 @@ namespace client
|
||||
bool m_IsRunning;
|
||||
std::thread * m_Thread;
|
||||
|
||||
boost::asio::io_service m_Service;
|
||||
boost::asio::io_context m_Service;
|
||||
boost::asio::ip::tcp::acceptor m_Acceptor;
|
||||
boost::asio::ssl::context m_SSLContext;
|
||||
boost::asio::deadline_timer m_ShutdownTimer;
|
||||
|
||||
@@ -67,7 +67,7 @@ namespace transport
|
||||
std::unique_ptr<std::thread> m_Thread;
|
||||
std::condition_variable m_Started;
|
||||
std::mutex m_StartedMutex;
|
||||
boost::asio::io_service m_Service;
|
||||
boost::asio::io_context m_Service;
|
||||
boost::asio::deadline_timer m_Timer;
|
||||
bool m_upnpUrlsInitialized = false;
|
||||
struct UPNPUrls m_upnpUrls;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2020, The PurpleI2P Project
|
||||
* Copyright (c) 2013-2024, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
@@ -25,6 +25,7 @@
|
||||
#include "RouterContext.h"
|
||||
#include "ClientContext.h"
|
||||
#include "Transports.h"
|
||||
#include "util.h"
|
||||
|
||||
void handle_signal(int sig)
|
||||
{
|
||||
@@ -220,6 +221,7 @@ namespace i2p
|
||||
|
||||
void DaemonLinux::run ()
|
||||
{
|
||||
i2p::util::SetThreadName ("i2pd-daemon");
|
||||
while (running)
|
||||
{
|
||||
std::this_thread::sleep_for (std::chrono::seconds(1));
|
||||
|
||||
2
debian/changelog
vendored
2
debian/changelog
vendored
@@ -2,7 +2,7 @@ i2pd (2.54.0-1) unstable; urgency=medium
|
||||
|
||||
* updated to version 2.54.0/0.9.64
|
||||
|
||||
-- orignal <orignal@i2pmail.org> Sun, 6 Oct 2024 16:00:00 +0000
|
||||
-- orignal <orignal@i2pmail.org> Sun, 6 Oct 2024 16:00:00 +0000
|
||||
|
||||
i2pd (2.53.1-1) unstable; urgency=medium
|
||||
|
||||
|
||||
@@ -1,68 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2023, 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
|
||||
*/
|
||||
|
||||
#include "CPU.h"
|
||||
#include "Log.h"
|
||||
|
||||
#ifndef bit_AES
|
||||
#define bit_AES (1 << 25)
|
||||
#endif
|
||||
|
||||
#if defined(__GNUC__) && __GNUC__ < 6 && IS_X86
|
||||
#include <cpuid.h>
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#include <intrin.h>
|
||||
#endif
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace cpu
|
||||
{
|
||||
bool aesni = false;
|
||||
|
||||
inline bool cpu_support_aes()
|
||||
{
|
||||
#if IS_X86
|
||||
#if defined(__clang__)
|
||||
# if (__clang_major__ >= 6)
|
||||
__builtin_cpu_init();
|
||||
# endif
|
||||
return __builtin_cpu_supports("aes");
|
||||
#elif (defined(__GNUC__) && __GNUC__ >= 6)
|
||||
__builtin_cpu_init();
|
||||
return __builtin_cpu_supports("aes");
|
||||
#elif (defined(__GNUC__) && __GNUC__ < 6)
|
||||
int cpu_info[4];
|
||||
bool flag = false;
|
||||
__cpuid(0, cpu_info[0], cpu_info[1], cpu_info[2], cpu_info[3]);
|
||||
if (cpu_info[0] >= 0x00000001) {
|
||||
__cpuid(0x00000001, cpu_info[0], cpu_info[1], cpu_info[2], cpu_info[3]);
|
||||
flag = ((cpu_info[2] & bit_AES) != 0);
|
||||
}
|
||||
return flag;
|
||||
#elif defined(_MSC_VER)
|
||||
int cpu_info[4];
|
||||
__cpuid(cpu_info, 1);
|
||||
return ((cpu_info[2] & bit_AES) != 0);
|
||||
#endif
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
void Detect(bool AesSwitch, bool force)
|
||||
{
|
||||
if ((cpu_support_aes() && AesSwitch) || (AesSwitch && force)) {
|
||||
aesni = true;
|
||||
}
|
||||
|
||||
LogPrint(eLogInfo, "AESNI ", (aesni ? "enabled" : "disabled"));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2023, The PurpleI2P Project
|
||||
* Copyright (c) 2013-2024, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
@@ -21,20 +21,4 @@
|
||||
# define IS_X86_64 0
|
||||
#endif
|
||||
|
||||
#if defined(__AES__) && !defined(_MSC_VER) && IS_X86
|
||||
# define SUPPORTS_AES 1
|
||||
#else
|
||||
# define SUPPORTS_AES 0
|
||||
#endif
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace cpu
|
||||
{
|
||||
extern bool aesni;
|
||||
|
||||
void Detect(bool AesSwitch, bool force);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -117,7 +117,7 @@ namespace config {
|
||||
("httpproxy.latency.max", value<std::string>()->default_value("0"), "HTTP proxy max latency for tunnels")
|
||||
("httpproxy.outproxy", value<std::string>()->default_value(""), "HTTP proxy upstream out proxy url")
|
||||
("httpproxy.addresshelper", value<bool>()->default_value(true), "Enable or disable addresshelper")
|
||||
("httpproxy.senduseragent", value<bool>()->default_value(false), "Pass through user's User-Agent if enabled. Disabled by deafult")
|
||||
("httpproxy.senduseragent", value<bool>()->default_value(false), "Pass through user's User-Agent if enabled. Disabled by default")
|
||||
("httpproxy.i2cp.leaseSetType", value<std::string>()->default_value("3"), "Local destination's LeaseSet type")
|
||||
("httpproxy.i2cp.leaseSetEncType", value<std::string>()->default_value("0,4"), "Local destination's LeaseSet encryption type")
|
||||
("httpproxy.i2cp.leaseSetPrivKey", value<std::string>()->default_value(""), "LeaseSet private key")
|
||||
@@ -238,7 +238,6 @@ namespace config {
|
||||
"http://[324:71e:281a:9ed3::ace]:7070/,"
|
||||
"http://[301:65b9:c7cd:9a36::1]:18801/,"
|
||||
"http://[320:8936:ec1a:31f1::216]/,"
|
||||
"http://[306:3834:97b9:a00a::1]/,"
|
||||
"http://[316:f9e0:f22e:a74f::216]/"
|
||||
), "Reseed URLs through the Yggdrasil, separated by comma")
|
||||
;
|
||||
@@ -316,11 +315,11 @@ namespace config {
|
||||
("persist.addressbook", value<bool>()->default_value(true), "Persist full addresses (default: true)")
|
||||
;
|
||||
|
||||
options_description cpuext("CPU encryption extensions options");
|
||||
options_description cpuext("CPU encryption extensions options. Deprecated");
|
||||
cpuext.add_options()
|
||||
("cpuext.aesni", bool_switch()->default_value(true), "Use auto detection for AESNI CPU extensions. If false, AESNI will be not used")
|
||||
("cpuext.aesni", bool_switch()->default_value(true), "Deprecated option")
|
||||
("cpuext.avx", bool_switch()->default_value(false), "Deprecated option")
|
||||
("cpuext.force", bool_switch()->default_value(false), "Force usage of CPU extensions. Useful when cpuinfo is not available on virtual machines")
|
||||
("cpuext.force", bool_switch()->default_value(false), "Deprecated option")
|
||||
;
|
||||
|
||||
options_description meshnets("Meshnet transports options");
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#if OPENSSL_HKDF
|
||||
#include <openssl/kdf.h>
|
||||
#endif
|
||||
#include "CPU.h"
|
||||
#include "Crypto.h"
|
||||
#include "Ed25519.h"
|
||||
#include "I2PEndian.h"
|
||||
@@ -240,17 +241,12 @@ namespace crypto
|
||||
// x25519
|
||||
X25519Keys::X25519Keys ()
|
||||
{
|
||||
#if OPENSSL_X25519
|
||||
m_Ctx = EVP_PKEY_CTX_new_id (NID_X25519, NULL);
|
||||
m_Pkey = nullptr;
|
||||
#else
|
||||
m_Ctx = BN_CTX_new ();
|
||||
#endif
|
||||
}
|
||||
|
||||
X25519Keys::X25519Keys (const uint8_t * priv, const uint8_t * pub)
|
||||
{
|
||||
#if OPENSSL_X25519
|
||||
m_Pkey = EVP_PKEY_new_raw_private_key (EVP_PKEY_X25519, NULL, priv, 32);
|
||||
m_Ctx = EVP_PKEY_CTX_new (m_Pkey, NULL);
|
||||
if (pub)
|
||||
@@ -260,29 +256,16 @@ namespace crypto
|
||||
size_t len = 32;
|
||||
EVP_PKEY_get_raw_public_key (m_Pkey, m_PublicKey, &len);
|
||||
}
|
||||
#else
|
||||
m_Ctx = BN_CTX_new ();
|
||||
memcpy (m_PrivateKey, priv, 32);
|
||||
if (pub)
|
||||
memcpy (m_PublicKey, pub, 32);
|
||||
else
|
||||
GetEd25519 ()->ScalarMulB (m_PrivateKey, m_PublicKey, m_Ctx);
|
||||
#endif
|
||||
}
|
||||
|
||||
X25519Keys::~X25519Keys ()
|
||||
{
|
||||
#if OPENSSL_X25519
|
||||
EVP_PKEY_CTX_free (m_Ctx);
|
||||
if (m_Pkey) EVP_PKEY_free (m_Pkey);
|
||||
#else
|
||||
BN_CTX_free (m_Ctx);
|
||||
#endif
|
||||
}
|
||||
|
||||
void X25519Keys::GenerateKeys ()
|
||||
{
|
||||
#if OPENSSL_X25519
|
||||
if (m_Pkey)
|
||||
{
|
||||
EVP_PKEY_free (m_Pkey);
|
||||
@@ -294,16 +277,11 @@ namespace crypto
|
||||
m_Ctx = EVP_PKEY_CTX_new (m_Pkey, NULL); // TODO: do we really need to re-create m_Ctx?
|
||||
size_t len = 32;
|
||||
EVP_PKEY_get_raw_public_key (m_Pkey, m_PublicKey, &len);
|
||||
#else
|
||||
RAND_bytes (m_PrivateKey, 32);
|
||||
GetEd25519 ()->ScalarMulB (m_PrivateKey, m_PublicKey, m_Ctx);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool X25519Keys::Agree (const uint8_t * pub, uint8_t * shared)
|
||||
{
|
||||
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);
|
||||
if (!pkey) return false;
|
||||
@@ -311,25 +289,17 @@ namespace crypto
|
||||
size_t len = 32;
|
||||
EVP_PKEY_derive (m_Ctx, shared, &len);
|
||||
EVP_PKEY_free (pkey);
|
||||
#else
|
||||
GetEd25519 ()->ScalarMul (pub, m_PrivateKey, shared, m_Ctx);
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
void X25519Keys::GetPrivateKey (uint8_t * priv) const
|
||||
{
|
||||
#if OPENSSL_X25519
|
||||
size_t len = 32;
|
||||
EVP_PKEY_get_raw_private_key (m_Pkey, priv, &len);
|
||||
#else
|
||||
memcpy (priv, m_PrivateKey, 32);
|
||||
#endif
|
||||
}
|
||||
|
||||
void X25519Keys::SetPrivateKey (const uint8_t * priv, bool calculatePublic)
|
||||
{
|
||||
#if OPENSSL_X25519
|
||||
if (m_Ctx) EVP_PKEY_CTX_free (m_Ctx);
|
||||
if (m_Pkey) EVP_PKEY_free (m_Pkey);
|
||||
m_Pkey = EVP_PKEY_new_raw_private_key (EVP_PKEY_X25519, NULL, priv, 32);
|
||||
@@ -339,11 +309,6 @@ namespace crypto
|
||||
size_t len = 32;
|
||||
EVP_PKEY_get_raw_public_key (m_Pkey, m_PublicKey, &len);
|
||||
}
|
||||
#else
|
||||
memcpy (m_PrivateKey, priv, 32);
|
||||
if (calculatePublic)
|
||||
GetEd25519 ()->ScalarMulB (m_PrivateKey, m_PublicKey, m_Ctx);
|
||||
#endif
|
||||
}
|
||||
|
||||
// ElGamal
|
||||
@@ -477,9 +442,8 @@ namespace crypto
|
||||
// encrypt
|
||||
CBCEncryption encryption;
|
||||
encryption.SetKey (shared);
|
||||
encryption.SetIV (iv);
|
||||
encrypted[257] = 0;
|
||||
encryption.Encrypt (m, 256, encrypted + 258);
|
||||
encryption.Encrypt (m, 256, iv, encrypted + 258);
|
||||
EC_POINT_free (p);
|
||||
BN_CTX_end (ctx);
|
||||
BN_CTX_free (ctx);
|
||||
@@ -512,8 +476,7 @@ namespace crypto
|
||||
uint8_t m[256];
|
||||
CBCDecryption decryption;
|
||||
decryption.SetKey (shared);
|
||||
decryption.SetIV (iv);
|
||||
decryption.Decrypt (encrypted + 258, 256, m);
|
||||
decryption.Decrypt (encrypted + 258, 256, iv, m);
|
||||
// verify and copy
|
||||
uint8_t hash[32];
|
||||
SHA256 (m + 33, 222, hash);
|
||||
@@ -551,440 +514,114 @@ namespace crypto
|
||||
}
|
||||
|
||||
// AES
|
||||
#if SUPPORTS_AES
|
||||
#define KeyExpansion256(round0,round1) \
|
||||
"pshufd $0xff, %%xmm2, %%xmm2 \n" \
|
||||
"movaps %%xmm1, %%xmm4 \n" \
|
||||
"pslldq $4, %%xmm4 \n" \
|
||||
"pxor %%xmm4, %%xmm1 \n" \
|
||||
"pslldq $4, %%xmm4 \n" \
|
||||
"pxor %%xmm4, %%xmm1 \n" \
|
||||
"pslldq $4, %%xmm4 \n" \
|
||||
"pxor %%xmm4, %%xmm1 \n" \
|
||||
"pxor %%xmm2, %%xmm1 \n" \
|
||||
"movaps %%xmm1, "#round0"(%[sched]) \n" \
|
||||
"aeskeygenassist $0, %%xmm1, %%xmm4 \n" \
|
||||
"pshufd $0xaa, %%xmm4, %%xmm2 \n" \
|
||||
"movaps %%xmm3, %%xmm4 \n" \
|
||||
"pslldq $4, %%xmm4 \n" \
|
||||
"pxor %%xmm4, %%xmm3 \n" \
|
||||
"pslldq $4, %%xmm4 \n" \
|
||||
"pxor %%xmm4, %%xmm3 \n" \
|
||||
"pslldq $4, %%xmm4 \n" \
|
||||
"pxor %%xmm4, %%xmm3 \n" \
|
||||
"pxor %%xmm2, %%xmm3 \n" \
|
||||
"movaps %%xmm3, "#round1"(%[sched]) \n"
|
||||
#endif
|
||||
|
||||
#if SUPPORTS_AES
|
||||
void ECBCryptoAESNI::ExpandKey (const AESKey& key)
|
||||
ECBEncryption::ECBEncryption ()
|
||||
{
|
||||
__asm__
|
||||
(
|
||||
"movups (%[key]), %%xmm1 \n"
|
||||
"movups 16(%[key]), %%xmm3 \n"
|
||||
"movaps %%xmm1, (%[sched]) \n"
|
||||
"movaps %%xmm3, 16(%[sched]) \n"
|
||||
"aeskeygenassist $1, %%xmm3, %%xmm2 \n"
|
||||
KeyExpansion256(32,48)
|
||||
"aeskeygenassist $2, %%xmm3, %%xmm2 \n"
|
||||
KeyExpansion256(64,80)
|
||||
"aeskeygenassist $4, %%xmm3, %%xmm2 \n"
|
||||
KeyExpansion256(96,112)
|
||||
"aeskeygenassist $8, %%xmm3, %%xmm2 \n"
|
||||
KeyExpansion256(128,144)
|
||||
"aeskeygenassist $16, %%xmm3, %%xmm2 \n"
|
||||
KeyExpansion256(160,176)
|
||||
"aeskeygenassist $32, %%xmm3, %%xmm2 \n"
|
||||
KeyExpansion256(192,208)
|
||||
"aeskeygenassist $64, %%xmm3, %%xmm2 \n"
|
||||
// key expansion final
|
||||
"pshufd $0xff, %%xmm2, %%xmm2 \n"
|
||||
"movaps %%xmm1, %%xmm4 \n"
|
||||
"pslldq $4, %%xmm4 \n"
|
||||
"pxor %%xmm4, %%xmm1 \n"
|
||||
"pslldq $4, %%xmm4 \n"
|
||||
"pxor %%xmm4, %%xmm1 \n"
|
||||
"pslldq $4, %%xmm4 \n"
|
||||
"pxor %%xmm4, %%xmm1 \n"
|
||||
"pxor %%xmm2, %%xmm1 \n"
|
||||
"movups %%xmm1, 224(%[sched]) \n"
|
||||
: // output
|
||||
: [key]"r"((const uint8_t *)key), [sched]"r"(GetKeySchedule ()) // input
|
||||
: "%xmm1", "%xmm2", "%xmm3", "%xmm4", "memory" // clogged
|
||||
);
|
||||
m_Ctx = EVP_CIPHER_CTX_new ();
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#if SUPPORTS_AES
|
||||
#define EncryptAES256(sched) \
|
||||
"pxor (%["#sched"]), %%xmm0 \n" \
|
||||
"aesenc 16(%["#sched"]), %%xmm0 \n" \
|
||||
"aesenc 32(%["#sched"]), %%xmm0 \n" \
|
||||
"aesenc 48(%["#sched"]), %%xmm0 \n" \
|
||||
"aesenc 64(%["#sched"]), %%xmm0 \n" \
|
||||
"aesenc 80(%["#sched"]), %%xmm0 \n" \
|
||||
"aesenc 96(%["#sched"]), %%xmm0 \n" \
|
||||
"aesenc 112(%["#sched"]), %%xmm0 \n" \
|
||||
"aesenc 128(%["#sched"]), %%xmm0 \n" \
|
||||
"aesenc 144(%["#sched"]), %%xmm0 \n" \
|
||||
"aesenc 160(%["#sched"]), %%xmm0 \n" \
|
||||
"aesenc 176(%["#sched"]), %%xmm0 \n" \
|
||||
"aesenc 192(%["#sched"]), %%xmm0 \n" \
|
||||
"aesenc 208(%["#sched"]), %%xmm0 \n" \
|
||||
"aesenclast 224(%["#sched"]), %%xmm0 \n"
|
||||
#endif
|
||||
|
||||
void ECBEncryption::Encrypt (const ChipherBlock * in, ChipherBlock * out)
|
||||
|
||||
ECBEncryption::~ECBEncryption ()
|
||||
{
|
||||
#if SUPPORTS_AES
|
||||
if(i2p::cpu::aesni)
|
||||
{
|
||||
__asm__
|
||||
(
|
||||
"movups (%[in]), %%xmm0 \n"
|
||||
EncryptAES256(sched)
|
||||
"movups %%xmm0, (%[out]) \n"
|
||||
:
|
||||
: [sched]"r"(GetKeySchedule ()), [in]"r"(in), [out]"r"(out)
|
||||
: "%xmm0", "memory"
|
||||
);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
AES_encrypt (in->buf, out->buf, &m_Key);
|
||||
}
|
||||
if (m_Ctx)
|
||||
EVP_CIPHER_CTX_free (m_Ctx);
|
||||
}
|
||||
|
||||
void ECBEncryption::Encrypt (const uint8_t * in, uint8_t * out)
|
||||
{
|
||||
EVP_EncryptInit_ex (m_Ctx, EVP_aes_256_ecb(), NULL, m_Key, NULL);
|
||||
EVP_CIPHER_CTX_set_padding (m_Ctx, 0);
|
||||
int len;
|
||||
EVP_EncryptUpdate (m_Ctx, out, &len, in, 16);
|
||||
EVP_EncryptFinal_ex (m_Ctx, out + len, &len);
|
||||
}
|
||||
|
||||
#if SUPPORTS_AES
|
||||
#define DecryptAES256(sched) \
|
||||
"pxor 224(%["#sched"]), %%xmm0 \n" \
|
||||
"aesdec 208(%["#sched"]), %%xmm0 \n" \
|
||||
"aesdec 192(%["#sched"]), %%xmm0 \n" \
|
||||
"aesdec 176(%["#sched"]), %%xmm0 \n" \
|
||||
"aesdec 160(%["#sched"]), %%xmm0 \n" \
|
||||
"aesdec 144(%["#sched"]), %%xmm0 \n" \
|
||||
"aesdec 128(%["#sched"]), %%xmm0 \n" \
|
||||
"aesdec 112(%["#sched"]), %%xmm0 \n" \
|
||||
"aesdec 96(%["#sched"]), %%xmm0 \n" \
|
||||
"aesdec 80(%["#sched"]), %%xmm0 \n" \
|
||||
"aesdec 64(%["#sched"]), %%xmm0 \n" \
|
||||
"aesdec 48(%["#sched"]), %%xmm0 \n" \
|
||||
"aesdec 32(%["#sched"]), %%xmm0 \n" \
|
||||
"aesdec 16(%["#sched"]), %%xmm0 \n" \
|
||||
"aesdeclast (%["#sched"]), %%xmm0 \n"
|
||||
#endif
|
||||
|
||||
void ECBDecryption::Decrypt (const ChipherBlock * in, ChipherBlock * out)
|
||||
ECBDecryption::ECBDecryption ()
|
||||
{
|
||||
#if SUPPORTS_AES
|
||||
if(i2p::cpu::aesni)
|
||||
{
|
||||
__asm__
|
||||
(
|
||||
"movups (%[in]), %%xmm0 \n"
|
||||
DecryptAES256(sched)
|
||||
"movups %%xmm0, (%[out]) \n"
|
||||
:
|
||||
: [sched]"r"(GetKeySchedule ()), [in]"r"(in), [out]"r"(out)
|
||||
: "%xmm0", "memory"
|
||||
);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
AES_decrypt (in->buf, out->buf, &m_Key);
|
||||
}
|
||||
m_Ctx = EVP_CIPHER_CTX_new ();
|
||||
}
|
||||
|
||||
ECBDecryption::~ECBDecryption ()
|
||||
{
|
||||
if (m_Ctx)
|
||||
EVP_CIPHER_CTX_free (m_Ctx);
|
||||
}
|
||||
|
||||
void ECBDecryption::Decrypt (const uint8_t * in, uint8_t * out)
|
||||
{
|
||||
EVP_DecryptInit_ex (m_Ctx, EVP_aes_256_ecb(), NULL, m_Key, NULL);
|
||||
EVP_CIPHER_CTX_set_padding (m_Ctx, 0);
|
||||
int len;
|
||||
EVP_DecryptUpdate (m_Ctx, out, &len, in, 16);
|
||||
EVP_DecryptFinal_ex (m_Ctx, out + len, &len);
|
||||
}
|
||||
|
||||
#if SUPPORTS_AES
|
||||
#define CallAESIMC(offset) \
|
||||
"movaps "#offset"(%[shed]), %%xmm0 \n" \
|
||||
"aesimc %%xmm0, %%xmm0 \n" \
|
||||
"movaps %%xmm0, "#offset"(%[shed]) \n"
|
||||
#endif
|
||||
|
||||
void ECBEncryption::SetKey (const AESKey& key)
|
||||
{
|
||||
#if SUPPORTS_AES
|
||||
if(i2p::cpu::aesni)
|
||||
{
|
||||
ExpandKey (key);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
AES_set_encrypt_key (key, 256, &m_Key);
|
||||
}
|
||||
CBCEncryption::CBCEncryption ()
|
||||
{
|
||||
m_Ctx = EVP_CIPHER_CTX_new ();
|
||||
}
|
||||
|
||||
void ECBDecryption::SetKey (const AESKey& key)
|
||||
|
||||
CBCEncryption::~CBCEncryption ()
|
||||
{
|
||||
#if SUPPORTS_AES
|
||||
if(i2p::cpu::aesni)
|
||||
{
|
||||
ExpandKey (key); // expand encryption key first
|
||||
// then invert it using aesimc
|
||||
__asm__
|
||||
(
|
||||
CallAESIMC(16)
|
||||
CallAESIMC(32)
|
||||
CallAESIMC(48)
|
||||
CallAESIMC(64)
|
||||
CallAESIMC(80)
|
||||
CallAESIMC(96)
|
||||
CallAESIMC(112)
|
||||
CallAESIMC(128)
|
||||
CallAESIMC(144)
|
||||
CallAESIMC(160)
|
||||
CallAESIMC(176)
|
||||
CallAESIMC(192)
|
||||
CallAESIMC(208)
|
||||
:
|
||||
: [shed]"r"(GetKeySchedule ())
|
||||
: "%xmm0", "memory"
|
||||
);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
AES_set_decrypt_key (key, 256, &m_Key);
|
||||
}
|
||||
}
|
||||
|
||||
void CBCEncryption::Encrypt (int numBlocks, const ChipherBlock * in, ChipherBlock * out)
|
||||
{
|
||||
#if SUPPORTS_AES
|
||||
if(i2p::cpu::aesni)
|
||||
{
|
||||
__asm__
|
||||
(
|
||||
"movups (%[iv]), %%xmm1 \n"
|
||||
"1: \n"
|
||||
"movups (%[in]), %%xmm0 \n"
|
||||
"pxor %%xmm1, %%xmm0 \n"
|
||||
EncryptAES256(sched)
|
||||
"movaps %%xmm0, %%xmm1 \n"
|
||||
"movups %%xmm0, (%[out]) \n"
|
||||
"add $16, %[in] \n"
|
||||
"add $16, %[out] \n"
|
||||
"dec %[num] \n"
|
||||
"jnz 1b \n"
|
||||
"movups %%xmm1, (%[iv]) \n"
|
||||
:
|
||||
: [iv]"r"((uint8_t *)m_LastBlock), [sched]"r"(m_ECBEncryption.GetKeySchedule ()),
|
||||
[in]"r"(in), [out]"r"(out), [num]"r"(numBlocks)
|
||||
: "%xmm0", "%xmm1", "cc", "memory"
|
||||
);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
for (int i = 0; i < numBlocks; i++)
|
||||
{
|
||||
*m_LastBlock.GetChipherBlock () ^= in[i];
|
||||
m_ECBEncryption.Encrypt (m_LastBlock.GetChipherBlock (), m_LastBlock.GetChipherBlock ());
|
||||
out[i] = *m_LastBlock.GetChipherBlock ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CBCEncryption::Encrypt (const uint8_t * in, std::size_t len, uint8_t * out)
|
||||
if (m_Ctx)
|
||||
EVP_CIPHER_CTX_free (m_Ctx);
|
||||
}
|
||||
|
||||
void CBCEncryption::Encrypt (const uint8_t * in, size_t len, const uint8_t * iv, uint8_t * out)
|
||||
{
|
||||
// len/16
|
||||
int numBlocks = len >> 4;
|
||||
if (numBlocks > 0)
|
||||
Encrypt (numBlocks, (const ChipherBlock *)in, (ChipherBlock *)out);
|
||||
EVP_EncryptInit_ex (m_Ctx, EVP_aes_256_cbc(), NULL, m_Key, iv);
|
||||
EVP_CIPHER_CTX_set_padding (m_Ctx, 0);
|
||||
int l;
|
||||
EVP_EncryptUpdate (m_Ctx, out, &l, in, len);
|
||||
EVP_EncryptFinal_ex (m_Ctx, out + l, &l);
|
||||
}
|
||||
|
||||
void CBCEncryption::Encrypt (const uint8_t * in, uint8_t * out)
|
||||
{
|
||||
#if SUPPORTS_AES
|
||||
if(i2p::cpu::aesni)
|
||||
{
|
||||
__asm__
|
||||
(
|
||||
"movups (%[iv]), %%xmm1 \n"
|
||||
"movups (%[in]), %%xmm0 \n"
|
||||
"pxor %%xmm1, %%xmm0 \n"
|
||||
EncryptAES256(sched)
|
||||
"movups %%xmm0, (%[out]) \n"
|
||||
"movups %%xmm0, (%[iv]) \n"
|
||||
:
|
||||
: [iv]"r"((uint8_t *)m_LastBlock), [sched]"r"(m_ECBEncryption.GetKeySchedule ()),
|
||||
[in]"r"(in), [out]"r"(out)
|
||||
: "%xmm0", "%xmm1", "memory"
|
||||
);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
Encrypt (1, (const ChipherBlock *)in, (ChipherBlock *)out);
|
||||
CBCDecryption::CBCDecryption ()
|
||||
{
|
||||
m_Ctx = EVP_CIPHER_CTX_new ();
|
||||
}
|
||||
|
||||
void CBCDecryption::Decrypt (int numBlocks, const ChipherBlock * in, ChipherBlock * out)
|
||||
|
||||
CBCDecryption::~CBCDecryption ()
|
||||
{
|
||||
#if SUPPORTS_AES
|
||||
if(i2p::cpu::aesni)
|
||||
{
|
||||
__asm__
|
||||
(
|
||||
"movups (%[iv]), %%xmm1 \n"
|
||||
"1: \n"
|
||||
"movups (%[in]), %%xmm0 \n"
|
||||
"movaps %%xmm0, %%xmm2 \n"
|
||||
DecryptAES256(sched)
|
||||
"pxor %%xmm1, %%xmm0 \n"
|
||||
"movups %%xmm0, (%[out]) \n"
|
||||
"movaps %%xmm2, %%xmm1 \n"
|
||||
"add $16, %[in] \n"
|
||||
"add $16, %[out] \n"
|
||||
"dec %[num] \n"
|
||||
"jnz 1b \n"
|
||||
"movups %%xmm1, (%[iv]) \n"
|
||||
:
|
||||
: [iv]"r"((uint8_t *)m_IV), [sched]"r"(m_ECBDecryption.GetKeySchedule ()),
|
||||
[in]"r"(in), [out]"r"(out), [num]"r"(numBlocks)
|
||||
: "%xmm0", "%xmm1", "%xmm2", "cc", "memory"
|
||||
);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
for (int i = 0; i < numBlocks; i++)
|
||||
{
|
||||
ChipherBlock tmp = in[i];
|
||||
m_ECBDecryption.Decrypt (in + i, out + i);
|
||||
out[i] ^= *m_IV.GetChipherBlock ();
|
||||
*m_IV.GetChipherBlock () = tmp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CBCDecryption::Decrypt (const uint8_t * in, std::size_t len, uint8_t * out)
|
||||
if (m_Ctx)
|
||||
EVP_CIPHER_CTX_free (m_Ctx);
|
||||
}
|
||||
|
||||
void CBCDecryption::Decrypt (const uint8_t * in, size_t len, const uint8_t * iv, uint8_t * out)
|
||||
{
|
||||
int numBlocks = len >> 4;
|
||||
if (numBlocks > 0)
|
||||
Decrypt (numBlocks, (const ChipherBlock *)in, (ChipherBlock *)out);
|
||||
}
|
||||
|
||||
void CBCDecryption::Decrypt (const uint8_t * in, uint8_t * out)
|
||||
{
|
||||
#if SUPPORTS_AES
|
||||
if(i2p::cpu::aesni)
|
||||
{
|
||||
__asm__
|
||||
(
|
||||
"movups (%[iv]), %%xmm1 \n"
|
||||
"movups (%[in]), %%xmm0 \n"
|
||||
"movups %%xmm0, (%[iv]) \n"
|
||||
DecryptAES256(sched)
|
||||
"pxor %%xmm1, %%xmm0 \n"
|
||||
"movups %%xmm0, (%[out]) \n"
|
||||
:
|
||||
: [iv]"r"((uint8_t *)m_IV), [sched]"r"(m_ECBDecryption.GetKeySchedule ()),
|
||||
[in]"r"(in), [out]"r"(out)
|
||||
: "%xmm0", "%xmm1", "memory"
|
||||
);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
Decrypt (1, (const ChipherBlock *)in, (ChipherBlock *)out);
|
||||
// len/16
|
||||
EVP_DecryptInit_ex (m_Ctx, EVP_aes_256_cbc(), NULL, m_Key, iv);
|
||||
EVP_CIPHER_CTX_set_padding (m_Ctx, 0);
|
||||
int l;
|
||||
EVP_DecryptUpdate (m_Ctx, out, &l, in, len);
|
||||
EVP_DecryptFinal_ex (m_Ctx, out + l, &l);
|
||||
}
|
||||
|
||||
void TunnelEncryption::Encrypt (const uint8_t * in, uint8_t * out)
|
||||
{
|
||||
#if SUPPORTS_AES
|
||||
if(i2p::cpu::aesni)
|
||||
{
|
||||
__asm__
|
||||
(
|
||||
// encrypt IV
|
||||
"movups (%[in]), %%xmm0 \n"
|
||||
EncryptAES256(sched_iv)
|
||||
"movaps %%xmm0, %%xmm1 \n"
|
||||
// double IV encryption
|
||||
EncryptAES256(sched_iv)
|
||||
"movups %%xmm0, (%[out]) \n"
|
||||
// encrypt data, IV is xmm1
|
||||
"1: \n"
|
||||
"add $16, %[in] \n"
|
||||
"add $16, %[out] \n"
|
||||
"movups (%[in]), %%xmm0 \n"
|
||||
"pxor %%xmm1, %%xmm0 \n"
|
||||
EncryptAES256(sched_l)
|
||||
"movaps %%xmm0, %%xmm1 \n"
|
||||
"movups %%xmm0, (%[out]) \n"
|
||||
"dec %[num] \n"
|
||||
"jnz 1b \n"
|
||||
:
|
||||
: [sched_iv]"r"(m_IVEncryption.GetKeySchedule ()), [sched_l]"r"(m_LayerEncryption.ECB().GetKeySchedule ()),
|
||||
[in]"r"(in), [out]"r"(out), [num]"r"(63) // 63 blocks = 1008 bytes
|
||||
: "%xmm0", "%xmm1", "cc", "memory"
|
||||
);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
m_IVEncryption.Encrypt ((const ChipherBlock *)in, (ChipherBlock *)out); // iv
|
||||
m_LayerEncryption.SetIV (out);
|
||||
m_LayerEncryption.Encrypt (in + 16, i2p::tunnel::TUNNEL_DATA_ENCRYPTED_SIZE, out + 16); // data
|
||||
m_IVEncryption.Encrypt ((ChipherBlock *)out, (ChipherBlock *)out); // double iv
|
||||
}
|
||||
uint8_t iv[16];
|
||||
m_IVEncryption.Encrypt (in, iv); // iv
|
||||
m_LayerEncryption.Encrypt (in + 16, i2p::tunnel::TUNNEL_DATA_ENCRYPTED_SIZE, iv, out + 16); // data
|
||||
m_IVEncryption.Encrypt (iv, out); // double iv
|
||||
}
|
||||
|
||||
void TunnelDecryption::Decrypt (const uint8_t * in, uint8_t * out)
|
||||
{
|
||||
#if SUPPORTS_AES
|
||||
if(i2p::cpu::aesni)
|
||||
{
|
||||
__asm__
|
||||
(
|
||||
// decrypt IV
|
||||
"movups (%[in]), %%xmm0 \n"
|
||||
DecryptAES256(sched_iv)
|
||||
"movaps %%xmm0, %%xmm1 \n"
|
||||
// double IV encryption
|
||||
DecryptAES256(sched_iv)
|
||||
"movups %%xmm0, (%[out]) \n"
|
||||
// decrypt data, IV is xmm1
|
||||
"1: \n"
|
||||
"add $16, %[in] \n"
|
||||
"add $16, %[out] \n"
|
||||
"movups (%[in]), %%xmm0 \n"
|
||||
"movaps %%xmm0, %%xmm2 \n"
|
||||
DecryptAES256(sched_l)
|
||||
"pxor %%xmm1, %%xmm0 \n"
|
||||
"movups %%xmm0, (%[out]) \n"
|
||||
"movaps %%xmm2, %%xmm1 \n"
|
||||
"dec %[num] \n"
|
||||
"jnz 1b \n"
|
||||
:
|
||||
: [sched_iv]"r"(m_IVDecryption.GetKeySchedule ()), [sched_l]"r"(m_LayerDecryption.ECB().GetKeySchedule ()),
|
||||
[in]"r"(in), [out]"r"(out), [num]"r"(63) // 63 blocks = 1008 bytes
|
||||
: "%xmm0", "%xmm1", "%xmm2", "cc", "memory"
|
||||
);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
m_IVDecryption.Decrypt ((const ChipherBlock *)in, (ChipherBlock *)out); // iv
|
||||
m_LayerDecryption.SetIV (out);
|
||||
m_LayerDecryption.Decrypt (in + 16, i2p::tunnel::TUNNEL_DATA_ENCRYPTED_SIZE, out + 16); // data
|
||||
m_IVDecryption.Decrypt ((ChipherBlock *)out, (ChipherBlock *)out); // double iv
|
||||
}
|
||||
uint8_t iv[16];
|
||||
m_IVDecryption.Decrypt (in, iv); // iv
|
||||
m_LayerDecryption.Decrypt (in + 16, i2p::tunnel::TUNNEL_DATA_ENCRYPTED_SIZE, iv, out + 16); // data
|
||||
m_IVDecryption.Decrypt (iv, out); // double iv
|
||||
}
|
||||
|
||||
// AEAD/ChaCha20/Poly1305
|
||||
|
||||
bool AEADChaCha20Poly1305 (const uint8_t * msg, size_t msgLen, const uint8_t * ad, size_t adLen, const uint8_t * key, const uint8_t * nonce, uint8_t * buf, size_t len, bool encrypt)
|
||||
static bool AEADChaCha20Poly1305 (EVP_CIPHER_CTX * ctx, const uint8_t * msg, size_t msgLen,
|
||||
const uint8_t * ad, size_t adLen, const uint8_t * key, const uint8_t * nonce, uint8_t * buf, size_t len, bool encrypt)
|
||||
{
|
||||
if (len < msgLen) return false;
|
||||
if (!ctx || len < msgLen) return false;
|
||||
if (encrypt && len < msgLen + 16) return false;
|
||||
bool ret = true;
|
||||
int outlen = 0;
|
||||
EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new ();
|
||||
if (encrypt)
|
||||
{
|
||||
EVP_EncryptInit_ex(ctx, EVP_chacha20_poly1305(), 0, 0, 0);
|
||||
@@ -997,7 +634,7 @@ namespace crypto
|
||||
}
|
||||
else
|
||||
{
|
||||
#if defined(LIBRESSL_VERSION_NUMBER)
|
||||
#if defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x4000000fL
|
||||
std::vector<uint8_t> m(msgLen + 16);
|
||||
if (msg == buf)
|
||||
{
|
||||
@@ -1014,26 +651,66 @@ namespace crypto
|
||||
EVP_DecryptUpdate(ctx, buf, &outlen, msg, msgLen);
|
||||
ret = EVP_DecryptFinal_ex(ctx, buf + outlen, &outlen) > 0;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool AEADChaCha20Poly1305 (const uint8_t * msg, size_t msgLen, const uint8_t * ad, size_t adLen,
|
||||
const uint8_t * key, const uint8_t * nonce, uint8_t * buf, size_t len, bool encrypt)
|
||||
{
|
||||
EVP_CIPHER_CTX * ctx = EVP_CIPHER_CTX_new ();
|
||||
auto ret = AEADChaCha20Poly1305 (ctx, msg, msgLen, ad, adLen, key, nonce, buf, len, encrypt);
|
||||
EVP_CIPHER_CTX_free (ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void AEADChaCha20Poly1305Encrypt (const std::vector<std::pair<uint8_t *, size_t> >& bufs, const uint8_t * key, const uint8_t * nonce, uint8_t * mac)
|
||||
AEADChaCha20Poly1305Encryptor::AEADChaCha20Poly1305Encryptor ()
|
||||
{
|
||||
m_Ctx = EVP_CIPHER_CTX_new ();
|
||||
}
|
||||
|
||||
AEADChaCha20Poly1305Encryptor::~AEADChaCha20Poly1305Encryptor ()
|
||||
{
|
||||
if (m_Ctx)
|
||||
EVP_CIPHER_CTX_free (m_Ctx);
|
||||
}
|
||||
|
||||
bool AEADChaCha20Poly1305Encryptor::Encrypt (const uint8_t * msg, size_t msgLen, const uint8_t * ad, size_t adLen,
|
||||
const uint8_t * key, const uint8_t * nonce, uint8_t * buf, size_t len)
|
||||
{
|
||||
return AEADChaCha20Poly1305 (m_Ctx, msg, msgLen, ad, adLen, key, nonce, buf, len, true);
|
||||
}
|
||||
|
||||
void AEADChaCha20Poly1305Encryptor::Encrypt (const std::vector<std::pair<uint8_t *, size_t> >& bufs,
|
||||
const uint8_t * key, const uint8_t * nonce, uint8_t * mac)
|
||||
{
|
||||
if (bufs.empty ()) return;
|
||||
int outlen = 0;
|
||||
EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new ();
|
||||
EVP_EncryptInit_ex(ctx, EVP_chacha20_poly1305(), 0, 0, 0);
|
||||
EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_IVLEN, 12, 0);
|
||||
EVP_EncryptInit_ex(ctx, NULL, NULL, key, nonce);
|
||||
EVP_EncryptInit_ex(m_Ctx, EVP_chacha20_poly1305(), 0, 0, 0);
|
||||
EVP_CIPHER_CTX_ctrl(m_Ctx, EVP_CTRL_AEAD_SET_IVLEN, 12, 0);
|
||||
EVP_EncryptInit_ex(m_Ctx, NULL, NULL, key, nonce);
|
||||
for (const auto& it: bufs)
|
||||
EVP_EncryptUpdate(ctx, it.first, &outlen, it.first, it.second);
|
||||
EVP_EncryptFinal_ex(ctx, NULL, &outlen);
|
||||
EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_GET_TAG, 16, mac);
|
||||
EVP_CIPHER_CTX_free (ctx);
|
||||
EVP_EncryptUpdate(m_Ctx, it.first, &outlen, it.first, it.second);
|
||||
EVP_EncryptFinal_ex(m_Ctx, NULL, &outlen);
|
||||
EVP_CIPHER_CTX_ctrl(m_Ctx, EVP_CTRL_AEAD_GET_TAG, 16, mac);
|
||||
}
|
||||
|
||||
AEADChaCha20Poly1305Decryptor::AEADChaCha20Poly1305Decryptor ()
|
||||
{
|
||||
m_Ctx = EVP_CIPHER_CTX_new ();
|
||||
}
|
||||
|
||||
AEADChaCha20Poly1305Decryptor::~AEADChaCha20Poly1305Decryptor ()
|
||||
{
|
||||
if (m_Ctx)
|
||||
EVP_CIPHER_CTX_free (m_Ctx);
|
||||
}
|
||||
|
||||
bool AEADChaCha20Poly1305Decryptor::Decrypt (const uint8_t * msg, size_t msgLen, const uint8_t * ad, size_t adLen,
|
||||
const uint8_t * key, const uint8_t * nonce, uint8_t * buf, size_t len)
|
||||
{
|
||||
return AEADChaCha20Poly1305 (m_Ctx, msg, msgLen, ad, adLen, key, nonce, buf, len, false);
|
||||
}
|
||||
|
||||
void ChaCha20 (const uint8_t * msg, size_t msgLen, const uint8_t * key, const uint8_t * nonce, uint8_t * out)
|
||||
{
|
||||
EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new ();
|
||||
@@ -1193,9 +870,8 @@ namespace crypto
|
||||
}
|
||||
}*/
|
||||
|
||||
void InitCrypto (bool precomputation, bool aesni, bool force)
|
||||
void InitCrypto (bool precomputation)
|
||||
{
|
||||
i2p::cpu::Detect (aesni, force);
|
||||
/* auto numLocks = CRYPTO_num_locks();
|
||||
for (int i = 0; i < numLocks; i++)
|
||||
m_OpenSSLMutexes.emplace_back (new std::mutex);
|
||||
|
||||
181
libi2pd/Crypto.h
181
libi2pd/Crypto.h
@@ -25,13 +25,11 @@
|
||||
|
||||
#include "Base.h"
|
||||
#include "Tag.h"
|
||||
#include "CPU.h"
|
||||
|
||||
// recognize openssl version and features
|
||||
#if (OPENSSL_VERSION_NUMBER >= 0x010101000) // 1.1.1
|
||||
# define OPENSSL_HKDF 1
|
||||
# define OPENSSL_EDDSA 1
|
||||
# define OPENSSL_X25519 1
|
||||
# if (!defined(LIBRESSL_VERSION_NUMBER) && (OPENSSL_VERSION_NUMBER != 0x030000000)) // 3.0.0, regression in SipHash, not implemented in LibreSSL
|
||||
# define OPENSSL_SIPHASH 1
|
||||
# endif
|
||||
@@ -70,13 +68,8 @@ namespace crypto
|
||||
private:
|
||||
|
||||
uint8_t m_PublicKey[32];
|
||||
#if OPENSSL_X25519
|
||||
EVP_PKEY_CTX * m_Ctx;
|
||||
EVP_PKEY * m_Pkey;
|
||||
#else
|
||||
BN_CTX * m_Ctx;
|
||||
uint8_t m_PrivateKey[32];
|
||||
#endif
|
||||
bool m_IsElligatorIneligible = false; // true if definitely ineligible
|
||||
};
|
||||
|
||||
@@ -91,142 +84,70 @@ namespace crypto
|
||||
void GenerateECIESKeyPair (const EC_GROUP * curve, BIGNUM *& priv, EC_POINT *& pub);
|
||||
|
||||
// AES
|
||||
struct ChipherBlock
|
||||
{
|
||||
uint8_t buf[16];
|
||||
|
||||
void operator^=(const ChipherBlock& other) // XOR
|
||||
{
|
||||
if (!(((size_t)buf | (size_t)other.buf) & 0x03)) // multiple of 4 ?
|
||||
{
|
||||
for (int i = 0; i < 4; i++)
|
||||
reinterpret_cast<uint32_t *>(buf)[i] ^= reinterpret_cast<const uint32_t *>(other.buf)[i];
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < 16; i++)
|
||||
buf[i] ^= other.buf[i];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
typedef i2p::data::Tag<32> AESKey;
|
||||
|
||||
template<size_t sz>
|
||||
class AESAlignedBuffer // 16 bytes alignment
|
||||
{
|
||||
public:
|
||||
|
||||
AESAlignedBuffer ()
|
||||
{
|
||||
m_Buf = m_UnalignedBuffer;
|
||||
uint8_t rem = ((size_t)m_Buf) & 0x0f;
|
||||
if (rem)
|
||||
m_Buf += (16 - rem);
|
||||
}
|
||||
|
||||
operator uint8_t * () { return m_Buf; };
|
||||
operator const uint8_t * () const { return m_Buf; };
|
||||
ChipherBlock * GetChipherBlock () { return (ChipherBlock *)m_Buf; };
|
||||
const ChipherBlock * GetChipherBlock () const { return (const ChipherBlock *)m_Buf; };
|
||||
|
||||
private:
|
||||
|
||||
uint8_t m_UnalignedBuffer[sz + 15]; // up to 15 bytes alignment
|
||||
uint8_t * m_Buf;
|
||||
};
|
||||
|
||||
|
||||
#if SUPPORTS_AES
|
||||
class ECBCryptoAESNI
|
||||
{
|
||||
public:
|
||||
|
||||
uint8_t * GetKeySchedule () { return m_KeySchedule; };
|
||||
|
||||
protected:
|
||||
|
||||
void ExpandKey (const AESKey& key);
|
||||
|
||||
private:
|
||||
|
||||
AESAlignedBuffer<240> m_KeySchedule; // 14 rounds for AES-256, 240 bytes
|
||||
};
|
||||
#endif
|
||||
|
||||
#if SUPPORTS_AES
|
||||
class ECBEncryption: public ECBCryptoAESNI
|
||||
#else
|
||||
|
||||
class ECBEncryption
|
||||
#endif
|
||||
{
|
||||
public:
|
||||
|
||||
void SetKey (const AESKey& key);
|
||||
ECBEncryption ();
|
||||
~ECBEncryption ();
|
||||
|
||||
void SetKey (const uint8_t * key) { m_Key = key; };
|
||||
void Encrypt(const uint8_t * in, uint8_t * out);
|
||||
|
||||
void Encrypt(const ChipherBlock * in, ChipherBlock * out);
|
||||
private:
|
||||
|
||||
private:
|
||||
AES_KEY m_Key;
|
||||
AESKey m_Key;
|
||||
EVP_CIPHER_CTX * m_Ctx;
|
||||
};
|
||||
|
||||
#if SUPPORTS_AES
|
||||
class ECBDecryption: public ECBCryptoAESNI
|
||||
#else
|
||||
class ECBDecryption
|
||||
#endif
|
||||
{
|
||||
public:
|
||||
|
||||
void SetKey (const AESKey& key);
|
||||
void Decrypt (const ChipherBlock * in, ChipherBlock * out);
|
||||
ECBDecryption ();
|
||||
~ECBDecryption ();
|
||||
|
||||
void SetKey (const uint8_t * key) { m_Key = key; };
|
||||
void Decrypt (const uint8_t * in, uint8_t * out);
|
||||
|
||||
private:
|
||||
AES_KEY m_Key;
|
||||
|
||||
AESKey m_Key;
|
||||
EVP_CIPHER_CTX * m_Ctx;
|
||||
};
|
||||
|
||||
class CBCEncryption
|
||||
{
|
||||
public:
|
||||
|
||||
CBCEncryption () { memset ((uint8_t *)m_LastBlock, 0, 16); };
|
||||
|
||||
void SetKey (const AESKey& key) { m_ECBEncryption.SetKey (key); }; // 32 bytes
|
||||
void SetIV (const uint8_t * iv) { memcpy ((uint8_t *)m_LastBlock, iv, 16); }; // 16 bytes
|
||||
void GetIV (uint8_t * iv) const { memcpy (iv, (const uint8_t *)m_LastBlock, 16); };
|
||||
|
||||
void Encrypt (int numBlocks, const ChipherBlock * in, ChipherBlock * out);
|
||||
void Encrypt (const uint8_t * in, std::size_t len, uint8_t * out);
|
||||
void Encrypt (const uint8_t * in, uint8_t * out); // one block
|
||||
|
||||
ECBEncryption & ECB() { return m_ECBEncryption; }
|
||||
CBCEncryption ();
|
||||
~CBCEncryption ();
|
||||
|
||||
void SetKey (const uint8_t * key) { m_Key = key; }; // 32 bytes
|
||||
void Encrypt (const uint8_t * in, size_t len, const uint8_t * iv, uint8_t * out);
|
||||
|
||||
private:
|
||||
|
||||
AESAlignedBuffer<16> m_LastBlock;
|
||||
|
||||
ECBEncryption m_ECBEncryption;
|
||||
AESKey m_Key;
|
||||
EVP_CIPHER_CTX * m_Ctx;
|
||||
};
|
||||
|
||||
class CBCDecryption
|
||||
{
|
||||
public:
|
||||
|
||||
CBCDecryption () { memset ((uint8_t *)m_IV, 0, 16); };
|
||||
|
||||
void SetKey (const AESKey& key) { m_ECBDecryption.SetKey (key); }; // 32 bytes
|
||||
void SetIV (const uint8_t * iv) { memcpy ((uint8_t *)m_IV, iv, 16); }; // 16 bytes
|
||||
void GetIV (uint8_t * iv) const { memcpy (iv, (const uint8_t *)m_IV, 16); };
|
||||
|
||||
void Decrypt (int numBlocks, const ChipherBlock * in, ChipherBlock * out);
|
||||
void Decrypt (const uint8_t * in, std::size_t len, uint8_t * out);
|
||||
void Decrypt (const uint8_t * in, uint8_t * out); // one block
|
||||
|
||||
ECBDecryption & ECB() { return m_ECBDecryption; }
|
||||
CBCDecryption ();
|
||||
~CBCDecryption ();
|
||||
|
||||
void SetKey (const uint8_t * key) { m_Key = key; }; // 32 bytes
|
||||
void Decrypt (const uint8_t * in, size_t len, const uint8_t * iv, uint8_t * out);
|
||||
|
||||
private:
|
||||
|
||||
AESAlignedBuffer<16> m_IV;
|
||||
ECBDecryption m_ECBDecryption;
|
||||
AESKey m_Key;
|
||||
EVP_CIPHER_CTX * m_Ctx;
|
||||
};
|
||||
|
||||
class TunnelEncryption // with double IV encryption
|
||||
@@ -266,10 +187,42 @@ namespace crypto
|
||||
};
|
||||
|
||||
// AEAD/ChaCha20/Poly1305
|
||||
bool AEADChaCha20Poly1305 (const uint8_t * msg, size_t msgLen, const uint8_t * ad, size_t adLen, const uint8_t * key, const uint8_t * nonce, uint8_t * buf, size_t len, bool encrypt); // msgLen is len without tag
|
||||
|
||||
void AEADChaCha20Poly1305Encrypt (const std::vector<std::pair<uint8_t *, size_t> >& bufs, const uint8_t * key, const uint8_t * nonce, uint8_t * mac); // encrypt multiple buffers with zero ad
|
||||
class AEADChaCha20Poly1305Encryptor
|
||||
{
|
||||
public:
|
||||
|
||||
AEADChaCha20Poly1305Encryptor ();
|
||||
~AEADChaCha20Poly1305Encryptor ();
|
||||
|
||||
bool Encrypt (const uint8_t * msg, size_t msgLen, const uint8_t * ad, size_t adLen,
|
||||
const uint8_t * key, const uint8_t * nonce, uint8_t * buf, size_t len); // msgLen is len without tag
|
||||
|
||||
void Encrypt (const std::vector<std::pair<uint8_t *, size_t> >& bufs, const uint8_t * key, const uint8_t * nonce, uint8_t * mac); // encrypt multiple buffers with zero ad
|
||||
|
||||
private:
|
||||
|
||||
EVP_CIPHER_CTX * m_Ctx;
|
||||
};
|
||||
|
||||
class AEADChaCha20Poly1305Decryptor
|
||||
{
|
||||
public:
|
||||
|
||||
AEADChaCha20Poly1305Decryptor ();
|
||||
~AEADChaCha20Poly1305Decryptor ();
|
||||
|
||||
bool Decrypt (const uint8_t * msg, size_t msgLen, const uint8_t * ad, size_t adLen,
|
||||
const uint8_t * key, const uint8_t * nonce, uint8_t * buf, size_t len); // msgLen is len without tag
|
||||
|
||||
private:
|
||||
|
||||
EVP_CIPHER_CTX * m_Ctx;
|
||||
};
|
||||
|
||||
bool AEADChaCha20Poly1305 (const uint8_t * msg, size_t msgLen, const uint8_t * ad, size_t adLen,
|
||||
const uint8_t * key, const uint8_t * nonce, uint8_t * buf, size_t len, bool encrypt); // msgLen is len without tag
|
||||
|
||||
// ChaCha20
|
||||
void ChaCha20 (const uint8_t * msg, size_t msgLen, const uint8_t * key, const uint8_t * nonce, uint8_t * out);
|
||||
|
||||
@@ -294,7 +247,7 @@ namespace crypto
|
||||
void InitNoiseIKState (NoiseSymmetricState& state, const uint8_t * pub); // Noise_IK (ratchets)
|
||||
|
||||
// init and terminate
|
||||
void InitCrypto (bool precomputation, bool aesni, bool force);
|
||||
void InitCrypto (bool precomputation);
|
||||
void TerminateCrypto ();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ namespace i2p
|
||||
{
|
||||
namespace client
|
||||
{
|
||||
LeaseSetDestination::LeaseSetDestination (boost::asio::io_service& service,
|
||||
LeaseSetDestination::LeaseSetDestination (boost::asio::io_context& service,
|
||||
bool isPublic, const std::map<std::string, std::string> * params):
|
||||
m_Service (service), m_IsPublic (isPublic), m_PublishReplyToken (0),
|
||||
m_LastSubmissionTime (0), m_PublishConfirmationTimer (m_Service),
|
||||
@@ -37,7 +37,7 @@ namespace client
|
||||
int inVar = DEFAULT_INBOUND_TUNNELS_LENGTH_VARIANCE;
|
||||
int outVar = DEFAULT_OUTBOUND_TUNNELS_LENGTH_VARIANCE;
|
||||
int numTags = DEFAULT_TAGS_TO_SEND;
|
||||
bool isHighBandwidth = true;
|
||||
bool isHighBandwidth = true;
|
||||
std::shared_ptr<std::vector<i2p::data::IdentHash> > explicitPeers;
|
||||
try
|
||||
{
|
||||
@@ -168,7 +168,7 @@ namespace client
|
||||
LoadTags ();
|
||||
m_Pool->SetLocalDestination (shared_from_this ());
|
||||
m_Pool->SetActive (true);
|
||||
m_CleanupTimer.expires_from_now (boost::posix_time::minutes (DESTINATION_CLEANUP_TIMEOUT));
|
||||
m_CleanupTimer.expires_from_now (boost::posix_time::seconds (DESTINATION_CLEANUP_TIMEOUT));
|
||||
m_CleanupTimer.async_wait (std::bind (&LeaseSetDestination::HandleCleanupTimer,
|
||||
shared_from_this (), std::placeholders::_1));
|
||||
}
|
||||
@@ -294,7 +294,7 @@ namespace client
|
||||
if (m_IsPublic)
|
||||
{
|
||||
auto s = shared_from_this ();
|
||||
m_Service.post ([s](void)
|
||||
boost::asio::post (m_Service, [s](void)
|
||||
{
|
||||
s->m_PublishVerificationTimer.cancel ();
|
||||
s->Publish ();
|
||||
@@ -322,7 +322,7 @@ namespace client
|
||||
memcpy (data.k, key, 32);
|
||||
memcpy (data.t, tag, 32);
|
||||
auto s = shared_from_this ();
|
||||
m_Service.post ([s,data](void)
|
||||
boost::asio::post (m_Service, [s,data](void)
|
||||
{
|
||||
s->AddSessionKey (data.k, data.t);
|
||||
});
|
||||
@@ -339,7 +339,7 @@ namespace client
|
||||
memcpy (data.k, key, 32);
|
||||
data.t = tag;
|
||||
auto s = shared_from_this ();
|
||||
m_Service.post ([s,data](void)
|
||||
boost::asio::post (m_Service, [s,data](void)
|
||||
{
|
||||
s->AddECIESx25519Key (data.k, data.t);
|
||||
});
|
||||
@@ -347,13 +347,30 @@ namespace client
|
||||
|
||||
void LeaseSetDestination::ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg)
|
||||
{
|
||||
m_Service.post (std::bind (&LeaseSetDestination::HandleGarlicMessage, shared_from_this (), msg));
|
||||
if (!msg) return;
|
||||
bool empty = false;
|
||||
{
|
||||
std::lock_guard<std::mutex> l(m_IncomingMsgsQueueMutex);
|
||||
empty = m_IncomingMsgsQueue.empty ();
|
||||
m_IncomingMsgsQueue.push_back (msg);
|
||||
}
|
||||
if (empty)
|
||||
boost::asio::post (m_Service, [s = shared_from_this ()]()
|
||||
{
|
||||
std::list<std::shared_ptr<I2NPMessage> > receivedMsgs;
|
||||
{
|
||||
std::lock_guard<std::mutex> l(s->m_IncomingMsgsQueueMutex);
|
||||
s->m_IncomingMsgsQueue.swap (receivedMsgs);
|
||||
}
|
||||
for (auto& it: receivedMsgs)
|
||||
s->HandleGarlicMessage (it);
|
||||
});
|
||||
}
|
||||
|
||||
void LeaseSetDestination::ProcessDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg)
|
||||
{
|
||||
uint32_t msgID = bufbe32toh (msg->GetPayload () + DELIVERY_STATUS_MSGID_OFFSET);
|
||||
m_Service.post (std::bind (&LeaseSetDestination::HandleDeliveryStatusMessage, shared_from_this (), msgID));
|
||||
boost::asio::post (m_Service, std::bind (&LeaseSetDestination::HandleDeliveryStatusMessage, shared_from_this (), msgID));
|
||||
}
|
||||
|
||||
void LeaseSetDestination::HandleI2NPMessage (const uint8_t * buf, size_t len)
|
||||
@@ -471,7 +488,7 @@ namespace client
|
||||
{
|
||||
auto it2 = m_LeaseSetRequests.find (key);
|
||||
if (it2 != m_LeaseSetRequests.end ())
|
||||
{
|
||||
{
|
||||
request = it2->second;
|
||||
m_LeaseSetRequests.erase (it2);
|
||||
if (request->requestedBlindedKey)
|
||||
@@ -493,14 +510,14 @@ namespace client
|
||||
// publishing verification doesn't have requestedBlindedKey
|
||||
auto localLeaseSet = GetLeaseSetMt ();
|
||||
if (localLeaseSet->GetStoreHash () == key)
|
||||
{
|
||||
auto ls = std::make_shared<i2p::data::LeaseSet2> (i2p::data::NETDB_STORE_TYPE_ENCRYPTED_LEASESET2,
|
||||
{
|
||||
auto ls = std::make_shared<i2p::data::LeaseSet2> (i2p::data::NETDB_STORE_TYPE_ENCRYPTED_LEASESET2,
|
||||
localLeaseSet->GetBuffer (), localLeaseSet->GetBufferLen (), false);
|
||||
leaseSet = ls;
|
||||
}
|
||||
leaseSet = ls;
|
||||
}
|
||||
else
|
||||
LogPrint (eLogWarning, "Destination: Encrypted LeaseSet2 received for request without blinded key");
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
LogPrint (eLogWarning, "Destination: Couldn't find request for encrypted LeaseSet2");
|
||||
@@ -511,14 +528,14 @@ namespace client
|
||||
}
|
||||
|
||||
if (!request)
|
||||
{
|
||||
{
|
||||
auto it1 = m_LeaseSetRequests.find (key);
|
||||
if (it1 != m_LeaseSetRequests.end ())
|
||||
{
|
||||
{
|
||||
request = it1->second;
|
||||
m_LeaseSetRequests.erase (it1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (request)
|
||||
{
|
||||
request->requestTimeoutTimer.cancel ();
|
||||
@@ -550,7 +567,7 @@ namespace client
|
||||
LogPrint (eLogWarning, "Destination: Request for ", key.ToBase64 (), " not found");
|
||||
}
|
||||
|
||||
void LeaseSetDestination::SendNextLeaseSetRequest (const i2p::data::IdentHash& key,
|
||||
void LeaseSetDestination::SendNextLeaseSetRequest (const i2p::data::IdentHash& key,
|
||||
std::shared_ptr<LeaseSetRequest> request)
|
||||
{
|
||||
bool found = false;
|
||||
@@ -570,8 +587,8 @@ namespace client
|
||||
request->Complete (nullptr);
|
||||
m_LeaseSetRequests.erase (key);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void LeaseSetDestination::HandleDeliveryStatusMessage (uint32_t msgID)
|
||||
{
|
||||
if (msgID == m_PublishReplyToken)
|
||||
@@ -588,9 +605,12 @@ namespace client
|
||||
i2p::garlic::GarlicDestination::HandleDeliveryStatusMessage (msgID);
|
||||
}
|
||||
|
||||
void LeaseSetDestination::SetLeaseSetUpdated ()
|
||||
void LeaseSetDestination::SetLeaseSetUpdated (bool post)
|
||||
{
|
||||
UpdateLeaseSet ();
|
||||
if (post)
|
||||
boost::asio::post (m_Service, [s = shared_from_this ()]() { s->UpdateLeaseSet (); });
|
||||
else
|
||||
UpdateLeaseSet ();
|
||||
}
|
||||
|
||||
void LeaseSetDestination::Publish ()
|
||||
@@ -628,7 +648,7 @@ namespace client
|
||||
if (!outbound || !inbound)
|
||||
{
|
||||
if (!m_Pool->GetInboundTunnels ().empty () && !m_Pool->GetOutboundTunnels ().empty ())
|
||||
{
|
||||
{
|
||||
LogPrint (eLogInfo, "Destination: No compatible tunnels with ", floodfill->GetIdentHash ().ToBase64 (), ". Trying another floodfill");
|
||||
m_ExcludedFloodfills.insert (floodfill->GetIdentHash ());
|
||||
floodfill = i2p::data::netdb.GetClosestFloodfill (leaseSet->GetStoreHash (), m_ExcludedFloodfills);
|
||||
@@ -646,10 +666,10 @@ namespace client
|
||||
}
|
||||
else
|
||||
LogPrint (eLogError, "Destination: Can't publish LeaseSet, no more floodfills found");
|
||||
}
|
||||
}
|
||||
else
|
||||
LogPrint (eLogDebug, "Destination: No tunnels in pool");
|
||||
|
||||
|
||||
if (!floodfill || !outbound || !inbound)
|
||||
{
|
||||
// we can't publish now
|
||||
@@ -670,7 +690,7 @@ namespace client
|
||||
auto s = shared_from_this ();
|
||||
msg->onDrop = [s]()
|
||||
{
|
||||
s->GetService ().post([s]()
|
||||
boost::asio::post (s->GetService (), [s]()
|
||||
{
|
||||
s->m_PublishConfirmationTimer.cancel ();
|
||||
s->HandlePublishConfirmationTimer (boost::system::error_code());
|
||||
@@ -755,10 +775,10 @@ namespace client
|
||||
if (!m_Pool || !IsReady ())
|
||||
{
|
||||
if (requestComplete)
|
||||
m_Service.post ([requestComplete](void){requestComplete (nullptr);});
|
||||
boost::asio::post (m_Service, [requestComplete](void){requestComplete (nullptr);});
|
||||
return false;
|
||||
}
|
||||
m_Service.post (std::bind (&LeaseSetDestination::RequestLeaseSet, shared_from_this (), dest, requestComplete, nullptr));
|
||||
boost::asio::post (m_Service, std::bind (&LeaseSetDestination::RequestLeaseSet, shared_from_this (), dest, requestComplete, nullptr));
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -767,7 +787,7 @@ namespace client
|
||||
if (!dest || !m_Pool || !IsReady ())
|
||||
{
|
||||
if (requestComplete)
|
||||
m_Service.post ([requestComplete](void){requestComplete (nullptr);});
|
||||
boost::asio::post (m_Service, [requestComplete](void){requestComplete (nullptr);});
|
||||
return false;
|
||||
}
|
||||
auto storeHash = dest->GetStoreHash ();
|
||||
@@ -775,17 +795,17 @@ namespace client
|
||||
if (leaseSet)
|
||||
{
|
||||
if (requestComplete)
|
||||
m_Service.post ([requestComplete, leaseSet](void){requestComplete (leaseSet);});
|
||||
boost::asio::post (m_Service, [requestComplete, leaseSet](void){requestComplete (leaseSet);});
|
||||
return true;
|
||||
}
|
||||
m_Service.post (std::bind (&LeaseSetDestination::RequestLeaseSet, shared_from_this (), storeHash, requestComplete, dest));
|
||||
boost::asio::post (m_Service, std::bind (&LeaseSetDestination::RequestLeaseSet, shared_from_this (), storeHash, requestComplete, dest));
|
||||
return true;
|
||||
}
|
||||
|
||||
void LeaseSetDestination::CancelDestinationRequest (const i2p::data::IdentHash& dest, bool notify)
|
||||
{
|
||||
auto s = shared_from_this ();
|
||||
m_Service.post ([dest, notify, s](void)
|
||||
boost::asio::post (m_Service, [dest, notify, s](void)
|
||||
{
|
||||
auto it = s->m_LeaseSetRequests.find (dest);
|
||||
if (it != s->m_LeaseSetRequests.end ())
|
||||
@@ -877,17 +897,17 @@ namespace client
|
||||
AddECIESx25519Key (replyKey, replyTag);
|
||||
else
|
||||
AddSessionKey (replyKey, replyTag);
|
||||
|
||||
auto msg = WrapMessageForRouter (nextFloodfill,
|
||||
|
||||
auto msg = WrapMessageForRouter (nextFloodfill,
|
||||
CreateLeaseSetDatabaseLookupMsg (dest, request->excluded, request->replyTunnel, replyKey, replyTag, isECIES));
|
||||
auto s = shared_from_this ();
|
||||
msg->onDrop = [s, dest, request]()
|
||||
{
|
||||
s->GetService ().post([s, dest, request]()
|
||||
boost::asio::post (s->GetService (), [s, dest, request]()
|
||||
{
|
||||
s->SendNextLeaseSetRequest (dest, request);
|
||||
});
|
||||
};
|
||||
};
|
||||
request->outboundTunnel->SendTunnelDataMsgs (
|
||||
{
|
||||
i2p::tunnel::TunnelMessageBlock
|
||||
@@ -950,7 +970,8 @@ namespace client
|
||||
CleanupExpiredTags ();
|
||||
CleanupRemoteLeaseSets ();
|
||||
CleanupDestination ();
|
||||
m_CleanupTimer.expires_from_now (boost::posix_time::minutes (DESTINATION_CLEANUP_TIMEOUT));
|
||||
m_CleanupTimer.expires_from_now (boost::posix_time::seconds (DESTINATION_CLEANUP_TIMEOUT +
|
||||
(m_Pool ? m_Pool->GetRng ()() % DESTINATION_CLEANUP_TIMEOUT_VARIANCE : 0)));
|
||||
m_CleanupTimer.async_wait (std::bind (&LeaseSetDestination::HandleCleanupTimer,
|
||||
shared_from_this (), std::placeholders::_1));
|
||||
}
|
||||
@@ -964,7 +985,7 @@ namespace client
|
||||
{
|
||||
if (it->second->IsEmpty () || ts > it->second->GetExpirationTime ()) // leaseset expired
|
||||
{
|
||||
LogPrint (eLogWarning, "Destination: Remote LeaseSet ", it->second->GetIdentHash ().ToBase64 (), " expired");
|
||||
LogPrint (eLogDebug, "Destination: Remote LeaseSet ", it->second->GetIdentHash ().ToBase64 (), " expired");
|
||||
it = m_RemoteLeaseSets.erase (it);
|
||||
}
|
||||
else
|
||||
@@ -979,12 +1000,13 @@ namespace client
|
||||
return i2p::data::CRYPTO_KEY_TYPE_ELGAMAL;
|
||||
}
|
||||
|
||||
ClientDestination::ClientDestination (boost::asio::io_service& service, const i2p::data::PrivateKeys& keys,
|
||||
ClientDestination::ClientDestination (boost::asio::io_context& service, const i2p::data::PrivateKeys& keys,
|
||||
bool isPublic, const std::map<std::string, std::string> * params):
|
||||
LeaseSetDestination (service, isPublic, params),
|
||||
m_Keys (keys), m_StreamingAckDelay (DEFAULT_INITIAL_ACK_DELAY),
|
||||
m_StreamingOutboundSpeed (DEFAULT_MAX_OUTBOUND_SPEED),
|
||||
m_StreamingInboundSpeed (DEFAULT_MAX_INBOUND_SPEED),
|
||||
m_StreamingMaxConcurrentStreams (DEFAULT_MAX_CONCURRENT_STREAMS),
|
||||
m_IsStreamingAnswerPings (DEFAULT_ANSWER_PINGS), m_LastPort (0),
|
||||
m_DatagramDestination (nullptr), m_RefCounter (0), m_LastPublishedTimestamp (0),
|
||||
m_ReadyChecker(service)
|
||||
@@ -1056,6 +1078,8 @@ namespace client
|
||||
it = params->find (I2CP_PARAM_STREAMING_MAX_INBOUND_SPEED);
|
||||
if (it != params->end ())
|
||||
m_StreamingInboundSpeed = std::stoi(it->second);
|
||||
if (it != params->end ())
|
||||
m_StreamingMaxConcurrentStreams = std::stoi(it->second);
|
||||
it = params->find (I2CP_PARAM_STREAMING_ANSWER_PINGS);
|
||||
if (it != params->end ())
|
||||
m_IsStreamingAnswerPings = std::stoi (it->second); // 1 for true
|
||||
@@ -1191,7 +1215,7 @@ namespace client
|
||||
if (leaseSet)
|
||||
{
|
||||
auto stream = CreateStream (leaseSet, port);
|
||||
GetService ().post ([streamRequestComplete, stream]()
|
||||
boost::asio::post (GetService (), [streamRequestComplete, stream]()
|
||||
{
|
||||
streamRequestComplete(stream);
|
||||
});
|
||||
@@ -1435,11 +1459,11 @@ namespace client
|
||||
keySections.push_back ({m_StandardEncryptionKey->keyType, (uint16_t)m_StandardEncryptionKey->decryptor->GetPublicKeyLen (), m_StandardEncryptionKey->pub} );
|
||||
|
||||
auto publishedTimestamp = i2p::util::GetSecondsSinceEpoch ();
|
||||
if (publishedTimestamp <= m_LastPublishedTimestamp)
|
||||
if (publishedTimestamp <= m_LastPublishedTimestamp)
|
||||
{
|
||||
LogPrint (eLogDebug, "Destination: LeaseSet update at the same second");
|
||||
publishedTimestamp++; // force newer timestamp
|
||||
}
|
||||
}
|
||||
bool isPublishedEncrypted = GetLeaseSetType () == i2p::data::NETDB_STORE_TYPE_ENCRYPTED_LEASESET2;
|
||||
auto ls2 = std::make_shared<i2p::data::LocalLeaseSet2> (i2p::data::NETDB_STORE_TYPE_STANDARD_LEASESET2,
|
||||
m_Keys, keySections, tunnels, IsPublic (), publishedTimestamp, isPublishedEncrypted);
|
||||
@@ -1511,6 +1535,8 @@ namespace client
|
||||
RunnableService ("Destination"),
|
||||
ClientDestination (GetIOService (), keys, isPublic, params)
|
||||
{
|
||||
if (!GetNickname ().empty ())
|
||||
RunnableService::SetName (GetNickname ());
|
||||
}
|
||||
|
||||
RunnableClientDestination::~RunnableClientDestination ()
|
||||
|
||||
@@ -42,7 +42,8 @@ namespace client
|
||||
const int PUBLISH_REGULAR_VERIFICATION_INTERNAL = 100; // in seconds periodically
|
||||
const int LEASESET_REQUEST_TIMEOUT = 5; // in seconds
|
||||
const int MAX_LEASESET_REQUEST_TIMEOUT = 40; // in seconds
|
||||
const int DESTINATION_CLEANUP_TIMEOUT = 3; // in minutes
|
||||
const int DESTINATION_CLEANUP_TIMEOUT = 44; // in seconds
|
||||
const int DESTINATION_CLEANUP_TIMEOUT_VARIANCE = 30; // in seconds
|
||||
const unsigned int MAX_NUM_FLOODFILLS_PER_REQUEST = 7;
|
||||
|
||||
// I2CP
|
||||
@@ -94,7 +95,9 @@ namespace client
|
||||
const int STREAMING_PROFILE_BULK = 1; // high bandwidth
|
||||
const int STREAMING_PROFILE_INTERACTIVE = 2; // low bandwidth
|
||||
const int DEFAULT_STREAMING_PROFILE = STREAMING_PROFILE_BULK;
|
||||
|
||||
const char I2CP_PARAM_STREAMING_MAX_CONCURRENT_STREAMS[] = "i2p.streaming.maxConcurrentStreams";
|
||||
const int DEFAULT_MAX_CONCURRENT_STREAMS = 2048;
|
||||
|
||||
typedef std::function<void (std::shared_ptr<i2p::stream::Stream> stream)> StreamRequestComplete;
|
||||
|
||||
class LeaseSetDestination: public i2p::garlic::GarlicDestination,
|
||||
@@ -104,7 +107,7 @@ namespace client
|
||||
// leaseSet = nullptr means not found
|
||||
struct LeaseSetRequest
|
||||
{
|
||||
LeaseSetRequest (boost::asio::io_service& service): requestTime (0), requestTimeoutTimer (service) {};
|
||||
LeaseSetRequest (boost::asio::io_context& service): requestTime (0), requestTimeoutTimer (service) {};
|
||||
std::unordered_set<i2p::data::IdentHash> excluded;
|
||||
uint64_t requestTime;
|
||||
boost::asio::deadline_timer requestTimeoutTimer;
|
||||
@@ -122,10 +125,10 @@ namespace client
|
||||
|
||||
public:
|
||||
|
||||
LeaseSetDestination (boost::asio::io_service& service, bool isPublic, const std::map<std::string, std::string> * params = nullptr);
|
||||
LeaseSetDestination (boost::asio::io_context& service, bool isPublic, const std::map<std::string, std::string> * params = nullptr);
|
||||
~LeaseSetDestination ();
|
||||
const std::string& GetNickname () const { return m_Nickname; };
|
||||
boost::asio::io_service& GetService () { return m_Service; };
|
||||
auto& GetService () { return m_Service; };
|
||||
|
||||
virtual void Start ();
|
||||
virtual void Stop ();
|
||||
@@ -142,15 +145,15 @@ namespace client
|
||||
void CancelDestinationRequestWithEncryptedLeaseSet (std::shared_ptr<const i2p::data::BlindedPublicKey> dest, bool notify = true);
|
||||
|
||||
// implements GarlicDestination
|
||||
std::shared_ptr<const i2p::data::LocalLeaseSet> GetLeaseSet ();
|
||||
std::shared_ptr<i2p::tunnel::TunnelPool> GetTunnelPool () const { return m_Pool; }
|
||||
std::shared_ptr<const i2p::data::LocalLeaseSet> GetLeaseSet () override;
|
||||
std::shared_ptr<i2p::tunnel::TunnelPool> GetTunnelPool () const override { return m_Pool; }
|
||||
|
||||
// override GarlicDestination
|
||||
bool SubmitSessionKey (const uint8_t * key, const uint8_t * tag);
|
||||
void SubmitECIESx25519Key (const uint8_t * key, uint64_t tag);
|
||||
void ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg);
|
||||
void ProcessDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg);
|
||||
void SetLeaseSetUpdated ();
|
||||
bool SubmitSessionKey (const uint8_t * key, const uint8_t * tag) override;
|
||||
void SubmitECIESx25519Key (const uint8_t * key, uint64_t tag) override;
|
||||
void ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg) override;
|
||||
void ProcessDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg) override;
|
||||
void SetLeaseSetUpdated (bool post) override;
|
||||
|
||||
bool IsPublic () const { return m_IsPublic; };
|
||||
void SetPublic (bool pub) { m_IsPublic = pub; };
|
||||
@@ -158,8 +161,8 @@ namespace client
|
||||
protected:
|
||||
|
||||
// implements GarlicDestination
|
||||
void HandleI2NPMessage (const uint8_t * buf, size_t len);
|
||||
bool HandleCloveI2NPMessage (I2NPMessageType typeID, const uint8_t * payload, size_t len, uint32_t msgID);
|
||||
void HandleI2NPMessage (const uint8_t * buf, size_t len) override;
|
||||
bool HandleCloveI2NPMessage (I2NPMessageType typeID, const uint8_t * payload, size_t len, uint32_t msgID) override;
|
||||
|
||||
void SetLeaseSet (std::shared_ptr<const i2p::data::LocalLeaseSet> newLeaseSet);
|
||||
int GetLeaseSetType () const { return m_LeaseSetType; };
|
||||
@@ -192,11 +195,14 @@ namespace client
|
||||
|
||||
private:
|
||||
|
||||
boost::asio::io_service& m_Service;
|
||||
boost::asio::io_context& m_Service;
|
||||
mutable std::mutex m_RemoteLeaseSetsMutex;
|
||||
std::unordered_map<i2p::data::IdentHash, std::shared_ptr<i2p::data::LeaseSet> > m_RemoteLeaseSets;
|
||||
std::unordered_map<i2p::data::IdentHash, std::shared_ptr<LeaseSetRequest> > m_LeaseSetRequests;
|
||||
|
||||
std::list<std::shared_ptr<I2NPMessage> > m_IncomingMsgsQueue;
|
||||
mutable std::mutex m_IncomingMsgsQueueMutex;
|
||||
|
||||
std::shared_ptr<i2p::tunnel::TunnelPool> m_Pool;
|
||||
std::mutex m_LeaseSetMutex;
|
||||
std::shared_ptr<const i2p::data::LocalLeaseSet> m_LeaseSet;
|
||||
@@ -235,7 +241,7 @@ namespace client
|
||||
|
||||
public:
|
||||
|
||||
ClientDestination (boost::asio::io_service& service, const i2p::data::PrivateKeys& keys,
|
||||
ClientDestination (boost::asio::io_context& service, const i2p::data::PrivateKeys& keys,
|
||||
bool isPublic, const std::map<std::string, std::string> * params = nullptr);
|
||||
~ClientDestination ();
|
||||
|
||||
@@ -269,6 +275,7 @@ namespace client
|
||||
int GetStreamingAckDelay () const { return m_StreamingAckDelay; }
|
||||
int GetStreamingOutboundSpeed () const { return m_StreamingOutboundSpeed; }
|
||||
int GetStreamingInboundSpeed () const { return m_StreamingInboundSpeed; }
|
||||
int GetStreamingMaxConcurrentStreams () const { return m_StreamingMaxConcurrentStreams; }
|
||||
bool IsStreamingAnswerPings () const { return m_IsStreamingAnswerPings; }
|
||||
|
||||
// datagram
|
||||
@@ -305,9 +312,7 @@ namespace client
|
||||
std::unique_ptr<EncryptionKey> m_StandardEncryptionKey;
|
||||
std::unique_ptr<EncryptionKey> m_ECIESx25519EncryptionKey;
|
||||
|
||||
int m_StreamingAckDelay;
|
||||
int m_StreamingOutboundSpeed;
|
||||
int m_StreamingInboundSpeed;
|
||||
int m_StreamingAckDelay,m_StreamingOutboundSpeed, m_StreamingInboundSpeed, m_StreamingMaxConcurrentStreams;
|
||||
bool m_IsStreamingAnswerPings;
|
||||
std::shared_ptr<i2p::stream::StreamingDestination> m_StreamingDestination; // default
|
||||
std::map<uint16_t, std::shared_ptr<i2p::stream::StreamingDestination> > m_StreamingDestinationsByPorts;
|
||||
|
||||
@@ -1112,6 +1112,8 @@ namespace garlic
|
||||
bool ECIESX25519AEADRatchetSession::CheckExpired (uint64_t ts)
|
||||
{
|
||||
CleanupUnconfirmedLeaseSet (ts);
|
||||
if (!m_Destination && ts > m_LastActivityTimestamp + ECIESX25519_SESSION_CREATE_TIMEOUT) return true; // m_LastActivityTimestamp is NS receive time
|
||||
if (m_State != eSessionStateEstablished && m_SessionCreatedTimestamp && ts > m_SessionCreatedTimestamp + ECIESX25519_SESSION_ESTABLISH_TIMEOUT) return true;
|
||||
return ts > m_LastActivityTimestamp + ECIESX25519_RECEIVE_EXPIRATION_TIMEOUT && // seconds
|
||||
ts*1000 > m_LastSentTimestamp + ECIESX25519_SEND_EXPIRATION_TIMEOUT*1000; // milliseconds
|
||||
}
|
||||
|
||||
@@ -30,6 +30,8 @@ namespace garlic
|
||||
const int ECIESX25519_SEND_INACTIVITY_TIMEOUT = 5000; // number of milliseconds we can send empty(pyaload only) packet after
|
||||
const int ECIESX25519_SEND_EXPIRATION_TIMEOUT = 480; // in seconds
|
||||
const int ECIESX25519_RECEIVE_EXPIRATION_TIMEOUT = 600; // in seconds
|
||||
const int ECIESX25519_SESSION_CREATE_TIMEOUT = 3; // in seconds, NSR must be send after NS received
|
||||
const int ECIESX25519_SESSION_ESTABLISH_TIMEOUT = 15; // in seconds
|
||||
const int ECIESX25519_PREVIOUS_TAGSET_EXPIRATION_TIMEOUT = 180; // in seconds
|
||||
const int ECIESX25519_ACK_REQUEST_INTERVAL = 33000; // in milliseconds
|
||||
const int ECIESX25519_ACK_REQUEST_MAX_NUM_ATTEMPTS = 3;
|
||||
@@ -169,7 +171,7 @@ namespace garlic
|
||||
void SetRemoteStaticKey (const uint8_t * key) { memcpy (m_RemoteStaticKey, key, 32); }
|
||||
|
||||
void Terminate () { m_IsTerminated = true; }
|
||||
void SetDestination (const i2p::data::IdentHash& dest) // TODO:
|
||||
void SetDestination (const i2p::data::IdentHash& dest)
|
||||
{
|
||||
if (!m_Destination) m_Destination.reset (new i2p::data::IdentHash (dest));
|
||||
}
|
||||
@@ -224,7 +226,7 @@ namespace garlic
|
||||
uint64_t m_SessionCreatedTimestamp = 0, m_LastActivityTimestamp = 0, // incoming (in seconds)
|
||||
m_LastSentTimestamp = 0; // in milliseconds
|
||||
std::shared_ptr<RatchetTagSet> m_SendTagset, m_NSRSendTagset;
|
||||
std::unique_ptr<i2p::data::IdentHash> m_Destination;// TODO: might not need it
|
||||
std::unique_ptr<i2p::data::IdentHash> m_Destination;// must be set for NS if outgoing and NSR if incoming
|
||||
std::list<std::pair<uint16_t, int> > m_AckRequests; // incoming (tagsetid, index)
|
||||
bool m_SendReverseKey = false, m_SendForwardKey = false, m_IsTerminated = false;
|
||||
std::unique_ptr<DHRatchet> m_NextReceiveRatchet, m_NextSendRatchet;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2023, The PurpleI2P Project
|
||||
* Copyright (c) 2013-2024, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
@@ -457,86 +457,6 @@ namespace crypto
|
||||
}
|
||||
}
|
||||
|
||||
#if !OPENSSL_X25519
|
||||
BIGNUM * Ed25519::ScalarMul (const BIGNUM * u, const BIGNUM * k, BN_CTX * ctx) const
|
||||
{
|
||||
BN_CTX_start (ctx);
|
||||
auto x1 = BN_CTX_get (ctx); BN_copy (x1, u);
|
||||
auto x2 = BN_CTX_get (ctx); BN_one (x2);
|
||||
auto z2 = BN_CTX_get (ctx); BN_zero (z2);
|
||||
auto x3 = BN_CTX_get (ctx); BN_copy (x3, u);
|
||||
auto z3 = BN_CTX_get (ctx); BN_one (z3);
|
||||
auto c121666 = BN_CTX_get (ctx); BN_set_word (c121666, 121666);
|
||||
auto tmp0 = BN_CTX_get (ctx); auto tmp1 = BN_CTX_get (ctx);
|
||||
unsigned int swap = 0;
|
||||
auto bits = BN_num_bits (k);
|
||||
while(bits)
|
||||
{
|
||||
--bits;
|
||||
auto k_t = BN_is_bit_set(k, bits) ? 1 : 0;
|
||||
swap ^= k_t;
|
||||
if (swap)
|
||||
{
|
||||
std::swap (x2, x3);
|
||||
std::swap (z2, z3);
|
||||
}
|
||||
swap = k_t;
|
||||
BN_mod_sub(tmp0, x3, z3, q, ctx);
|
||||
BN_mod_sub(tmp1, x2, z2, q, ctx);
|
||||
BN_mod_add(x2, x2, z2, q, ctx);
|
||||
BN_mod_add(z2, x3, z3, q, ctx);
|
||||
BN_mod_mul(z3, tmp0, x2, q, ctx);
|
||||
BN_mod_mul(z2, z2, tmp1, q, ctx);
|
||||
BN_mod_sqr(tmp0, tmp1, q, ctx);
|
||||
BN_mod_sqr(tmp1, x2, q, ctx);
|
||||
BN_mod_add(x3, z3, z2, q, ctx);
|
||||
BN_mod_sub(z2, z3, z2, q, ctx);
|
||||
BN_mod_mul(x2, tmp1, tmp0, q, ctx);
|
||||
BN_mod_sub(tmp1, tmp1, tmp0, q, ctx);
|
||||
BN_mod_sqr(z2, z2, q, ctx);
|
||||
BN_mod_mul(z3, tmp1, c121666, q, ctx);
|
||||
BN_mod_sqr(x3, x3, q, ctx);
|
||||
BN_mod_add(tmp0, tmp0, z3, q, ctx);
|
||||
BN_mod_mul(z3, x1, z2, q, ctx);
|
||||
BN_mod_mul(z2, tmp1, tmp0, q, ctx);
|
||||
}
|
||||
if (swap)
|
||||
{
|
||||
std::swap (x2, x3);
|
||||
std::swap (z2, z3);
|
||||
}
|
||||
BN_mod_inverse (z2, z2, q, ctx);
|
||||
BIGNUM * res = BN_new (); // not from ctx
|
||||
BN_mod_mul(res, x2, z2, q, ctx);
|
||||
BN_CTX_end (ctx);
|
||||
return res;
|
||||
}
|
||||
|
||||
void Ed25519::ScalarMul (const uint8_t * p, const uint8_t * e, uint8_t * buf, BN_CTX * ctx) const
|
||||
{
|
||||
BIGNUM * p1 = DecodeBN<32> (p);
|
||||
uint8_t k[32];
|
||||
memcpy (k, e, 32);
|
||||
k[0] &= 248; k[31] &= 127; k[31] |= 64;
|
||||
BIGNUM * n = DecodeBN<32> (k);
|
||||
BIGNUM * q1 = ScalarMul (p1, n, ctx);
|
||||
EncodeBN (q1, buf, 32);
|
||||
BN_free (p1); BN_free (n); BN_free (q1);
|
||||
}
|
||||
|
||||
void Ed25519::ScalarMulB (const uint8_t * e, uint8_t * buf, BN_CTX * ctx) const
|
||||
{
|
||||
BIGNUM *p1 = BN_new (); BN_set_word (p1, 9);
|
||||
uint8_t k[32];
|
||||
memcpy (k, e, 32);
|
||||
k[0] &= 248; k[31] &= 127; k[31] |= 64;
|
||||
BIGNUM * n = DecodeBN<32> (k);
|
||||
BIGNUM * q1 = ScalarMul (p1, n, ctx);
|
||||
EncodeBN (q1, buf, 32);
|
||||
BN_free (p1); BN_free (n); BN_free (q1);
|
||||
}
|
||||
#endif
|
||||
|
||||
void Ed25519::BlindPublicKey (const uint8_t * pub, const uint8_t * seed, uint8_t * blinded)
|
||||
{
|
||||
BN_CTX * ctx = BN_CTX_new ();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2020, The PurpleI2P Project
|
||||
* Copyright (c) 2013-2024, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
@@ -84,10 +84,7 @@ namespace crypto
|
||||
EDDSAPoint GeneratePublicKey (const uint8_t * expandedPrivateKey, BN_CTX * ctx) const;
|
||||
EDDSAPoint DecodePublicKey (const uint8_t * buf, BN_CTX * ctx) const;
|
||||
void EncodePublicKey (const EDDSAPoint& publicKey, uint8_t * buf, BN_CTX * ctx) const;
|
||||
#if !OPENSSL_X25519
|
||||
void ScalarMul (const uint8_t * p, const uint8_t * e, uint8_t * buf, BN_CTX * ctx) const; // p is point, e is number for x25519
|
||||
void ScalarMulB (const uint8_t * e, uint8_t * buf, BN_CTX * ctx) const;
|
||||
#endif
|
||||
|
||||
void BlindPublicKey (const uint8_t * pub, const uint8_t * seed, uint8_t * blinded); // for encrypted LeaseSet2, pub - 32, seed - 64, blinded - 32
|
||||
void BlindPrivateKey (const uint8_t * priv, const uint8_t * seed, uint8_t * blindedPriv, uint8_t * blindedPub); // for encrypted LeaseSet2, pub - 32, seed - 64, blinded - 32
|
||||
|
||||
@@ -115,11 +112,6 @@ namespace crypto
|
||||
BIGNUM * DecodeBN (const uint8_t * buf) const;
|
||||
void EncodeBN (const BIGNUM * bn, uint8_t * buf, size_t len) const;
|
||||
|
||||
#if !OPENSSL_X25519
|
||||
// for x25519
|
||||
BIGNUM * ScalarMul (const BIGNUM * p, const BIGNUM * e, BN_CTX * ctx) const;
|
||||
#endif
|
||||
|
||||
private:
|
||||
|
||||
BIGNUM * q, * l, * d, * I;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2023, The PurpleI2P Project
|
||||
* Copyright (c) 2013-2024, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
@@ -7,7 +7,6 @@
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <openssl/evp.h>
|
||||
#include <openssl/ssl.h>
|
||||
#include "Crypto.h"
|
||||
#include "FS.h"
|
||||
@@ -25,6 +24,8 @@ namespace data
|
||||
|
||||
Families::~Families ()
|
||||
{
|
||||
for (auto it : m_SigningKeys)
|
||||
if (it.second.first) EVP_PKEY_free (it.second.first);
|
||||
}
|
||||
|
||||
void Families::LoadCertificate (const std::string& filename)
|
||||
@@ -47,48 +48,16 @@ namespace data
|
||||
cn += 3;
|
||||
char * family = strstr (cn, ".family");
|
||||
if (family) family[0] = 0;
|
||||
}
|
||||
auto pkey = X509_get_pubkey (cert);
|
||||
int keyType = EVP_PKEY_base_id (pkey);
|
||||
switch (keyType)
|
||||
{
|
||||
case EVP_PKEY_DSA:
|
||||
// TODO:
|
||||
break;
|
||||
case EVP_PKEY_EC:
|
||||
{
|
||||
EC_KEY * ecKey = EVP_PKEY_get1_EC_KEY (pkey);
|
||||
if (ecKey)
|
||||
auto pkey = X509_get_pubkey (cert);
|
||||
if (pkey)
|
||||
{
|
||||
if (!m_SigningKeys.emplace (cn, std::make_pair(pkey, (int)m_SigningKeys.size () + 1)).second)
|
||||
{
|
||||
auto group = EC_KEY_get0_group (ecKey);
|
||||
if (group)
|
||||
{
|
||||
int curve = EC_GROUP_get_curve_name (group);
|
||||
if (curve == NID_X9_62_prime256v1)
|
||||
{
|
||||
uint8_t signingKey[64];
|
||||
BIGNUM * x = BN_new(), * y = BN_new();
|
||||
EC_POINT_get_affine_coordinates_GFp (group,
|
||||
EC_KEY_get0_public_key (ecKey), x, y, NULL);
|
||||
i2p::crypto::bn2buf (x, signingKey, 32);
|
||||
i2p::crypto::bn2buf (y, signingKey + 32, 32);
|
||||
BN_free (x); BN_free (y);
|
||||
verifier = std::make_shared<i2p::crypto::ECDSAP256Verifier>();
|
||||
verifier->SetPublicKey (signingKey);
|
||||
}
|
||||
else
|
||||
LogPrint (eLogWarning, "Family: elliptic curve ", curve, " is not supported");
|
||||
}
|
||||
EC_KEY_free (ecKey);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
LogPrint (eLogWarning, "Family: Certificate key type ", keyType, " is not supported");
|
||||
EVP_PKEY_free (pkey);
|
||||
LogPrint (eLogError, "Family: Duplicated family name ", cn);
|
||||
}
|
||||
}
|
||||
}
|
||||
EVP_PKEY_free (pkey);
|
||||
if (verifier && cn)
|
||||
m_SigningKeys.emplace (cn, std::make_pair(verifier, (int)m_SigningKeys.size () + 1));
|
||||
}
|
||||
SSL_free (ssl);
|
||||
}
|
||||
@@ -130,14 +99,22 @@ namespace data
|
||||
LogPrint (eLogError, "Family: ", family, " is too long");
|
||||
return false;
|
||||
}
|
||||
|
||||
memcpy (buf, family.c_str (), len);
|
||||
memcpy (buf + len, (const uint8_t *)ident, 32);
|
||||
len += 32;
|
||||
Base64ToByteStream (signature, signatureLen, signatureBuf, 64);
|
||||
auto it = m_SigningKeys.find (family);
|
||||
if (it != m_SigningKeys.end ())
|
||||
return it->second.first->Verify (buf, len, signatureBuf);
|
||||
if (it != m_SigningKeys.end () && it->second.first)
|
||||
{
|
||||
memcpy (buf, family.c_str (), len);
|
||||
memcpy (buf + len, (const uint8_t *)ident, 32);
|
||||
len += 32;
|
||||
auto signatureBufLen = Base64ToByteStream (signature, signatureLen, signatureBuf, 64);
|
||||
if (signatureBufLen)
|
||||
{
|
||||
EVP_MD_CTX * ctx = EVP_MD_CTX_create ();
|
||||
EVP_DigestVerifyInit (ctx, NULL, NULL, NULL, it->second.first);
|
||||
auto ret = EVP_DigestVerify (ctx, signatureBuf, signatureBufLen, buf, len);
|
||||
EVP_MD_CTX_destroy (ctx);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
// TODO: process key
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2022, The PurpleI2P Project
|
||||
* Copyright (c) 2013-2024, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
@@ -12,7 +12,7 @@
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include "Signature.h"
|
||||
#include <openssl/evp.h>
|
||||
#include "Identity.h"
|
||||
|
||||
namespace i2p
|
||||
@@ -37,7 +37,7 @@ namespace data
|
||||
|
||||
private:
|
||||
|
||||
std::map<std::string, std::pair<std::shared_ptr<i2p::crypto::Verifier>, FamilyID> > m_SigningKeys; // family -> (verifier, id)
|
||||
std::map<std::string, std::pair<EVP_PKEY *, FamilyID> > m_SigningKeys; // family -> (verification pkey, id)
|
||||
};
|
||||
|
||||
std::string CreateFamilySignature (const std::string& family, const IdentHash& ident);
|
||||
|
||||
@@ -160,7 +160,7 @@ namespace garlic
|
||||
uint8_t iv[32]; // IV is first 16 bytes
|
||||
SHA256(elGamal.preIV, 32, iv);
|
||||
m_Destination->Encrypt ((uint8_t *)&elGamal, buf);
|
||||
m_Encryption.SetIV (iv);
|
||||
m_IV = iv;
|
||||
buf += 514;
|
||||
len += 514;
|
||||
}
|
||||
@@ -170,7 +170,7 @@ namespace garlic
|
||||
memcpy (buf, tag, 32);
|
||||
uint8_t iv[32]; // IV is first 16 bytes
|
||||
SHA256(tag, 32, iv);
|
||||
m_Encryption.SetIV (iv);
|
||||
m_IV = iv;
|
||||
buf += 32;
|
||||
len += 32;
|
||||
}
|
||||
@@ -210,7 +210,7 @@ namespace garlic
|
||||
size_t rem = blockSize % 16;
|
||||
if (rem)
|
||||
blockSize += (16-rem); //padding
|
||||
m_Encryption.Encrypt(buf, blockSize, buf);
|
||||
m_Encryption.Encrypt(buf, blockSize, m_IV, buf);
|
||||
return blockSize;
|
||||
}
|
||||
|
||||
@@ -426,7 +426,8 @@ namespace garlic
|
||||
}
|
||||
|
||||
GarlicDestination::GarlicDestination (): m_NumTags (32), // 32 tags by default
|
||||
m_PayloadBuffer (nullptr), m_NumRatchetInboundTags (0) // 0 means standard
|
||||
m_PayloadBuffer (nullptr), m_LastIncomingSessionTimestamp (0),
|
||||
m_NumRatchetInboundTags (0) // 0 means standard
|
||||
{
|
||||
}
|
||||
|
||||
@@ -513,8 +514,7 @@ namespace garlic
|
||||
{
|
||||
uint8_t iv[32]; // IV is first 16 bytes
|
||||
SHA256(buf, 32, iv);
|
||||
decryption->SetIV (iv);
|
||||
decryption->Decrypt (buf + 32, length - 32, buf + 32);
|
||||
decryption->Decrypt (buf + 32, length - 32, iv, buf + 32);
|
||||
HandleAESBlock (buf + 32, length - 32, decryption, msg->from);
|
||||
found = true;
|
||||
}
|
||||
@@ -532,16 +532,23 @@ namespace garlic
|
||||
auto decryption = std::make_shared<AESDecryption>(elGamal.sessionKey);
|
||||
uint8_t iv[32]; // IV is first 16 bytes
|
||||
SHA256(elGamal.preIV, 32, iv);
|
||||
decryption->SetIV (iv);
|
||||
decryption->Decrypt(buf + 514, length - 514, buf + 514);
|
||||
decryption->Decrypt(buf + 514, length - 514, iv, buf + 514);
|
||||
HandleAESBlock (buf + 514, length - 514, decryption, msg->from);
|
||||
}
|
||||
else if (SupportsEncryptionType (i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD))
|
||||
{
|
||||
// otherwise ECIESx25519
|
||||
auto session = std::make_shared<ECIESX25519AEADRatchetSession> (this, false); // incoming
|
||||
if (!session->HandleNextMessage (buf, length, nullptr, 0))
|
||||
LogPrint (eLogError, "Garlic: Can't handle ECIES-X25519-AEAD-Ratchet message");
|
||||
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
|
||||
if (ts > m_LastIncomingSessionTimestamp + INCOMING_SESSIONS_MINIMAL_INTERVAL)
|
||||
{
|
||||
auto session = std::make_shared<ECIESX25519AEADRatchetSession> (this, false); // incoming
|
||||
if (session->HandleNextMessage (buf, length, nullptr, 0))
|
||||
m_LastIncomingSessionTimestamp = ts;
|
||||
else
|
||||
LogPrint (eLogError, "Garlic: Can't handle ECIES-X25519-AEAD-Ratchet message");
|
||||
}
|
||||
else
|
||||
LogPrint (eLogWarning, "Garlic: Incoming sessions come too often");
|
||||
}
|
||||
else
|
||||
LogPrint (eLogError, "Garlic: Failed to decrypt message");
|
||||
@@ -737,7 +744,8 @@ namespace garlic
|
||||
}
|
||||
|
||||
std::shared_ptr<GarlicRoutingSession> GarlicDestination::GetRoutingSession (
|
||||
std::shared_ptr<const i2p::data::RoutingDestination> destination, bool attachLeaseSet)
|
||||
std::shared_ptr<const i2p::data::RoutingDestination> destination, bool attachLeaseSet,
|
||||
bool requestNewIfNotFound)
|
||||
{
|
||||
if (destination->GetEncryptionType () == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD &&
|
||||
SupportsEncryptionType (i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD))
|
||||
@@ -752,16 +760,17 @@ namespace garlic
|
||||
if (session->IsInactive (i2p::util::GetSecondsSinceEpoch ()))
|
||||
{
|
||||
LogPrint (eLogDebug, "Garlic: Session restarted");
|
||||
requestNewIfNotFound = true; // it's not a new session
|
||||
session = nullptr;
|
||||
}
|
||||
}
|
||||
if (!session)
|
||||
if (!session && requestNewIfNotFound)
|
||||
{
|
||||
session = std::make_shared<ECIESX25519AEADRatchetSession> (this, true);
|
||||
session->SetRemoteStaticKey (staticKey);
|
||||
}
|
||||
if (destination->IsDestination ())
|
||||
session->SetDestination (destination->GetIdentHash ()); // TODO: remove
|
||||
if (session && destination->IsDestination ())
|
||||
session->SetDestination (destination->GetIdentHash ()); // NS or NSR
|
||||
return session;
|
||||
}
|
||||
else
|
||||
@@ -897,7 +906,7 @@ namespace garlic
|
||||
}
|
||||
}
|
||||
|
||||
void GarlicDestination::SetLeaseSetUpdated ()
|
||||
void GarlicDestination::SetLeaseSetUpdated (bool post)
|
||||
{
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_SessionsMutex);
|
||||
|
||||
@@ -52,6 +52,7 @@ namespace garlic
|
||||
const int OUTGOING_TAGS_CONFIRMATION_TIMEOUT = 10; // 10 seconds
|
||||
const int LEASESET_CONFIRMATION_TIMEOUT = 4000; // in milliseconds
|
||||
const int ROUTING_PATH_EXPIRATION_TIMEOUT = 120; // in seconds
|
||||
const int INCOMING_SESSIONS_MINIMAL_INTERVAL = 200; // in milliseconds
|
||||
|
||||
struct SessionTag: public i2p::data::Tag<32>
|
||||
{
|
||||
@@ -204,6 +205,7 @@ namespace garlic
|
||||
std::map<uint32_t, std::unique_ptr<UnconfirmedTags> > m_UnconfirmedTagsMsgs; // msgID->tags
|
||||
|
||||
i2p::crypto::CBCEncryption m_Encryption;
|
||||
i2p::data::Tag<16> m_IV;
|
||||
|
||||
public:
|
||||
|
||||
@@ -234,7 +236,8 @@ namespace garlic
|
||||
int GetNumTags () const { return m_NumTags; };
|
||||
void SetNumRatchetInboundTags (int numTags) { m_NumRatchetInboundTags = numTags; };
|
||||
int GetNumRatchetInboundTags () const { return m_NumRatchetInboundTags; };
|
||||
std::shared_ptr<GarlicRoutingSession> GetRoutingSession (std::shared_ptr<const i2p::data::RoutingDestination> destination, bool attachLeaseSet);
|
||||
std::shared_ptr<GarlicRoutingSession> GetRoutingSession (std::shared_ptr<const i2p::data::RoutingDestination> destination,
|
||||
bool attachLeaseSet, bool requestNewIfNotFound = true);
|
||||
void CleanupExpiredTags ();
|
||||
void RemoveDeliveryStatusSession (uint32_t msgID);
|
||||
std::shared_ptr<I2NPMessage> WrapMessageForRouter (std::shared_ptr<const i2p::data::RouterInfo> router,
|
||||
@@ -253,7 +256,7 @@ namespace garlic
|
||||
|
||||
virtual void ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg);
|
||||
virtual void ProcessDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg);
|
||||
virtual void SetLeaseSetUpdated ();
|
||||
virtual void SetLeaseSetUpdated (bool post = false);
|
||||
|
||||
virtual std::shared_ptr<const i2p::data::LocalLeaseSet> GetLeaseSet () = 0; // TODO
|
||||
virtual std::shared_ptr<i2p::tunnel::TunnelPool> GetTunnelPool () const = 0;
|
||||
@@ -284,6 +287,7 @@ namespace garlic
|
||||
std::unordered_map<i2p::data::IdentHash, ElGamalAESSessionPtr> m_Sessions;
|
||||
std::unordered_map<i2p::data::Tag<32>, ECIESX25519AEADRatchetSessionPtr> m_ECIESx25519Sessions; // static key -> session
|
||||
uint8_t * m_PayloadBuffer; // for ECIESX25519AEADRatchet
|
||||
uint64_t m_LastIncomingSessionTimestamp; // in milliseconds
|
||||
// incoming
|
||||
int m_NumRatchetInboundTags;
|
||||
std::unordered_map<SessionTag, std::shared_ptr<AESDecryption>, std::hash<i2p::data::Tag<32> > > m_Tags;
|
||||
|
||||
@@ -103,6 +103,7 @@ namespace http
|
||||
|
||||
bool URL::parse(std::string_view url)
|
||||
{
|
||||
if (url.empty ()) return false;
|
||||
std::size_t pos_p = 0; /* < current parse position */
|
||||
std::size_t pos_c = 0; /* < work position */
|
||||
if(url.at(0) != '/' || pos_p > 0)
|
||||
|
||||
@@ -10,20 +10,15 @@
|
||||
#include <atomic>
|
||||
#include "Base.h"
|
||||
#include "Log.h"
|
||||
#include "Crypto.h"
|
||||
#include "I2PEndian.h"
|
||||
#include "Timestamp.h"
|
||||
#include "RouterContext.h"
|
||||
#include "NetDb.hpp"
|
||||
#include "Tunnel.h"
|
||||
#include "Transports.h"
|
||||
#include "Garlic.h"
|
||||
#include "ECIESX25519AEADRatchetSession.h"
|
||||
#include "TransitTunnel.h"
|
||||
#include "I2NPProtocol.h"
|
||||
#include "version.h"
|
||||
|
||||
using namespace i2p::transport;
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
std::shared_ptr<I2NPMessage> NewI2NPMessage ()
|
||||
@@ -376,337 +371,6 @@ namespace i2p
|
||||
return !msg->GetPayload ()[DATABASE_STORE_TYPE_OFFSET]; // 0- RouterInfo
|
||||
}
|
||||
|
||||
static bool HandleBuildRequestRecords (int num, uint8_t * records, uint8_t * clearText)
|
||||
{
|
||||
for (int i = 0; i < num; i++)
|
||||
{
|
||||
uint8_t * record = records + i*TUNNEL_BUILD_RECORD_SIZE;
|
||||
if (!memcmp (record + BUILD_REQUEST_RECORD_TO_PEER_OFFSET, (const uint8_t *)i2p::context.GetRouterInfo ().GetIdentHash (), 16))
|
||||
{
|
||||
LogPrint (eLogDebug, "I2NP: Build request record ", i, " is ours");
|
||||
if (!i2p::context.DecryptTunnelBuildRecord (record + BUILD_REQUEST_RECORD_ENCRYPTED_OFFSET, clearText))
|
||||
{
|
||||
LogPrint (eLogWarning, "I2NP: Failed to decrypt tunnel build record");
|
||||
return false;
|
||||
}
|
||||
if (!memcmp ((const uint8_t *)i2p::context.GetIdentHash (), clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET, 32) && // if next ident is now ours
|
||||
!(clearText[ECIES_BUILD_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_ENDPOINT_FLAG)) // and not endpoint
|
||||
{
|
||||
LogPrint (eLogWarning, "I2NP: Next ident is ours in tunnel build record");
|
||||
return false;
|
||||
}
|
||||
uint8_t retCode = 0;
|
||||
// replace record to reply
|
||||
if (i2p::context.AcceptsTunnels () && i2p::context.GetCongestionLevel (false) < CONGESTION_LEVEL_FULL)
|
||||
{
|
||||
auto transitTunnel = i2p::tunnel::CreateTransitTunnel (
|
||||
bufbe32toh (clearText + ECIES_BUILD_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET),
|
||||
clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET,
|
||||
bufbe32toh (clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET),
|
||||
clearText + ECIES_BUILD_REQUEST_RECORD_LAYER_KEY_OFFSET,
|
||||
clearText + ECIES_BUILD_REQUEST_RECORD_IV_KEY_OFFSET,
|
||||
clearText[ECIES_BUILD_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_GATEWAY_FLAG,
|
||||
clearText[ECIES_BUILD_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_ENDPOINT_FLAG);
|
||||
if (!i2p::tunnel::tunnels.AddTransitTunnel (transitTunnel))
|
||||
retCode = 30;
|
||||
}
|
||||
else
|
||||
retCode = 30; // always reject with bandwidth reason (30)
|
||||
|
||||
memset (record + ECIES_BUILD_RESPONSE_RECORD_OPTIONS_OFFSET, 0, 2); // no options
|
||||
record[ECIES_BUILD_RESPONSE_RECORD_RET_OFFSET] = retCode;
|
||||
// encrypt reply
|
||||
i2p::crypto::CBCEncryption encryption;
|
||||
for (int j = 0; j < num; j++)
|
||||
{
|
||||
uint8_t * reply = records + j*TUNNEL_BUILD_RECORD_SIZE;
|
||||
if (j == i)
|
||||
{
|
||||
uint8_t nonce[12];
|
||||
memset (nonce, 0, 12);
|
||||
auto& noiseState = i2p::context.GetCurrentNoiseState ();
|
||||
if (!i2p::crypto::AEADChaCha20Poly1305 (reply, TUNNEL_BUILD_RECORD_SIZE - 16,
|
||||
noiseState.m_H, 32, noiseState.m_CK, nonce, reply, TUNNEL_BUILD_RECORD_SIZE, true)) // encrypt
|
||||
{
|
||||
LogPrint (eLogWarning, "I2NP: Reply AEAD encryption failed");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
encryption.SetKey (clearText + ECIES_BUILD_REQUEST_RECORD_REPLY_KEY_OFFSET);
|
||||
encryption.SetIV (clearText + ECIES_BUILD_REQUEST_RECORD_REPLY_IV_OFFSET);
|
||||
encryption.Encrypt(reply, TUNNEL_BUILD_RECORD_SIZE, reply);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void HandleVariableTunnelBuildMsg (uint32_t replyMsgID, uint8_t * buf, size_t len)
|
||||
{
|
||||
int num = buf[0];
|
||||
LogPrint (eLogDebug, "I2NP: VariableTunnelBuild ", num, " records");
|
||||
if (num > i2p::tunnel::MAX_NUM_RECORDS)
|
||||
{
|
||||
LogPrint (eLogError, "I2NP: Too many records in VaribleTunnelBuild message ", num);
|
||||
return;
|
||||
}
|
||||
if (len < num*TUNNEL_BUILD_RECORD_SIZE + 1)
|
||||
{
|
||||
LogPrint (eLogError, "I2NP: VaribleTunnelBuild message of ", num, " records is too short ", len);
|
||||
return;
|
||||
}
|
||||
|
||||
auto tunnel = i2p::tunnel::tunnels.GetPendingInboundTunnel (replyMsgID);
|
||||
if (tunnel)
|
||||
{
|
||||
// endpoint of inbound tunnel
|
||||
LogPrint (eLogDebug, "I2NP: VariableTunnelBuild reply for tunnel ", tunnel->GetTunnelID ());
|
||||
if (tunnel->HandleTunnelBuildResponse (buf, len))
|
||||
{
|
||||
LogPrint (eLogInfo, "I2NP: Inbound tunnel ", tunnel->GetTunnelID (), " has been created");
|
||||
tunnel->SetState (i2p::tunnel::eTunnelStateEstablished);
|
||||
i2p::tunnel::tunnels.AddInboundTunnel (tunnel);
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint (eLogInfo, "I2NP: Inbound tunnel ", tunnel->GetTunnelID (), " has been declined");
|
||||
tunnel->SetState (i2p::tunnel::eTunnelStateBuildFailed);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
uint8_t clearText[ECIES_BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE];
|
||||
if (HandleBuildRequestRecords (num, buf + 1, clearText))
|
||||
{
|
||||
if (clearText[ECIES_BUILD_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_ENDPOINT_FLAG) // we are endpoint of outboud tunnel
|
||||
{
|
||||
// so we send it to reply tunnel
|
||||
transports.SendMessage (clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET,
|
||||
CreateTunnelGatewayMsg (bufbe32toh (clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET),
|
||||
eI2NPVariableTunnelBuildReply, buf, len,
|
||||
bufbe32toh (clearText + ECIES_BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET)));
|
||||
}
|
||||
else
|
||||
transports.SendMessage (clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET,
|
||||
CreateI2NPMessage (eI2NPVariableTunnelBuild, buf, len,
|
||||
bufbe32toh (clearText + ECIES_BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void HandleTunnelBuildMsg (uint8_t * buf, size_t len)
|
||||
{
|
||||
LogPrint (eLogWarning, "I2NP: TunnelBuild is too old for ECIES router");
|
||||
}
|
||||
|
||||
static void HandleTunnelBuildReplyMsg (uint32_t replyMsgID, uint8_t * buf, size_t len, bool isShort)
|
||||
{
|
||||
int num = buf[0];
|
||||
LogPrint (eLogDebug, "I2NP: TunnelBuildReplyMsg of ", num, " records replyMsgID=", replyMsgID);
|
||||
if (num > i2p::tunnel::MAX_NUM_RECORDS)
|
||||
{
|
||||
LogPrint (eLogError, "I2NP: Too many records in TunnelBuildReply message ", num);
|
||||
return;
|
||||
}
|
||||
size_t recordSize = isShort ? SHORT_TUNNEL_BUILD_RECORD_SIZE : TUNNEL_BUILD_RECORD_SIZE;
|
||||
if (len < num*recordSize + 1)
|
||||
{
|
||||
LogPrint (eLogError, "I2NP: TunnelBuildReply message of ", num, " records is too short ", len);
|
||||
return;
|
||||
}
|
||||
|
||||
auto tunnel = i2p::tunnel::tunnels.GetPendingOutboundTunnel (replyMsgID);
|
||||
if (tunnel)
|
||||
{
|
||||
// reply for outbound tunnel
|
||||
if (tunnel->HandleTunnelBuildResponse (buf, len))
|
||||
{
|
||||
LogPrint (eLogInfo, "I2NP: Outbound tunnel ", tunnel->GetTunnelID (), " has been created");
|
||||
tunnel->SetState (i2p::tunnel::eTunnelStateEstablished);
|
||||
i2p::tunnel::tunnels.AddOutboundTunnel (tunnel);
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint (eLogInfo, "I2NP: Outbound tunnel ", tunnel->GetTunnelID (), " has been declined");
|
||||
tunnel->SetState (i2p::tunnel::eTunnelStateBuildFailed);
|
||||
}
|
||||
}
|
||||
else
|
||||
LogPrint (eLogWarning, "I2NP: Pending tunnel for message ", replyMsgID, " not found");
|
||||
}
|
||||
|
||||
static void HandleShortTunnelBuildMsg (uint32_t replyMsgID, uint8_t * buf, size_t len)
|
||||
{
|
||||
int num = buf[0];
|
||||
LogPrint (eLogDebug, "I2NP: ShortTunnelBuild ", num, " records");
|
||||
if (num > i2p::tunnel::MAX_NUM_RECORDS)
|
||||
{
|
||||
LogPrint (eLogError, "I2NP: Too many records in ShortTunnelBuild message ", num);
|
||||
return;
|
||||
}
|
||||
if (len < num*SHORT_TUNNEL_BUILD_RECORD_SIZE + 1)
|
||||
{
|
||||
LogPrint (eLogError, "I2NP: ShortTunnelBuild message of ", num, " records is too short ", len);
|
||||
return;
|
||||
}
|
||||
auto tunnel = i2p::tunnel::tunnels.GetPendingInboundTunnel (replyMsgID);
|
||||
if (tunnel)
|
||||
{
|
||||
// endpoint of inbound tunnel
|
||||
LogPrint (eLogDebug, "I2NP: ShortTunnelBuild reply for tunnel ", tunnel->GetTunnelID ());
|
||||
if (tunnel->HandleTunnelBuildResponse (buf, len))
|
||||
{
|
||||
LogPrint (eLogInfo, "I2NP: Inbound tunnel ", tunnel->GetTunnelID (), " has been created");
|
||||
tunnel->SetState (i2p::tunnel::eTunnelStateEstablished);
|
||||
i2p::tunnel::tunnels.AddInboundTunnel (tunnel);
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint (eLogInfo, "I2NP: Inbound tunnel ", tunnel->GetTunnelID (), " has been declined");
|
||||
tunnel->SetState (i2p::tunnel::eTunnelStateBuildFailed);
|
||||
}
|
||||
return;
|
||||
}
|
||||
const uint8_t * record = buf + 1;
|
||||
for (int i = 0; i < num; i++)
|
||||
{
|
||||
if (!memcmp (record, (const uint8_t *)i2p::context.GetRouterInfo ().GetIdentHash (), 16))
|
||||
{
|
||||
LogPrint (eLogDebug, "I2NP: Short request record ", i, " is ours");
|
||||
uint8_t clearText[SHORT_REQUEST_RECORD_CLEAR_TEXT_SIZE];
|
||||
if (!i2p::context.DecryptTunnelShortRequestRecord (record + SHORT_REQUEST_RECORD_ENCRYPTED_OFFSET, clearText))
|
||||
{
|
||||
LogPrint (eLogWarning, "I2NP: Can't decrypt short request record ", i);
|
||||
return;
|
||||
}
|
||||
if (clearText[SHORT_REQUEST_RECORD_LAYER_ENCRYPTION_TYPE]) // not AES
|
||||
{
|
||||
LogPrint (eLogWarning, "I2NP: Unknown layer encryption type ", clearText[SHORT_REQUEST_RECORD_LAYER_ENCRYPTION_TYPE], " in short request record");
|
||||
return;
|
||||
}
|
||||
auto& noiseState = i2p::context.GetCurrentNoiseState ();
|
||||
uint8_t replyKey[32]; // AEAD/Chacha20/Poly1305
|
||||
i2p::crypto::AESKey layerKey, ivKey; // AES
|
||||
i2p::crypto::HKDF (noiseState.m_CK, nullptr, 0, "SMTunnelReplyKey", noiseState.m_CK);
|
||||
memcpy (replyKey, noiseState.m_CK + 32, 32);
|
||||
i2p::crypto::HKDF (noiseState.m_CK, nullptr, 0, "SMTunnelLayerKey", noiseState.m_CK);
|
||||
memcpy (layerKey, noiseState.m_CK + 32, 32);
|
||||
bool isEndpoint = clearText[SHORT_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_ENDPOINT_FLAG;
|
||||
if (isEndpoint)
|
||||
{
|
||||
i2p::crypto::HKDF (noiseState.m_CK, nullptr, 0, "TunnelLayerIVKey", noiseState.m_CK);
|
||||
memcpy (ivKey, noiseState.m_CK + 32, 32);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!memcmp ((const uint8_t *)i2p::context.GetIdentHash (), clearText + SHORT_REQUEST_RECORD_NEXT_IDENT_OFFSET, 32)) // if next ident is now ours
|
||||
{
|
||||
LogPrint (eLogWarning, "I2NP: Next ident is ours in short request record");
|
||||
return;
|
||||
}
|
||||
memcpy (ivKey, noiseState.m_CK , 32);
|
||||
}
|
||||
|
||||
// check if we accept this tunnel
|
||||
std::shared_ptr<i2p::tunnel::TransitTunnel> transitTunnel;
|
||||
uint8_t retCode = 0;
|
||||
if (!i2p::context.AcceptsTunnels () || i2p::context.GetCongestionLevel (false) >= CONGESTION_LEVEL_FULL)
|
||||
retCode = 30;
|
||||
if (!retCode)
|
||||
{
|
||||
// create new transit tunnel
|
||||
transitTunnel = i2p::tunnel::CreateTransitTunnel (
|
||||
bufbe32toh (clearText + SHORT_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET),
|
||||
clearText + SHORT_REQUEST_RECORD_NEXT_IDENT_OFFSET,
|
||||
bufbe32toh (clearText + SHORT_REQUEST_RECORD_NEXT_TUNNEL_OFFSET),
|
||||
layerKey, ivKey,
|
||||
clearText[SHORT_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_GATEWAY_FLAG,
|
||||
clearText[SHORT_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_ENDPOINT_FLAG);
|
||||
if (!i2p::tunnel::tunnels.AddTransitTunnel (transitTunnel))
|
||||
retCode = 30;
|
||||
}
|
||||
|
||||
// encrypt reply
|
||||
uint8_t nonce[12];
|
||||
memset (nonce, 0, 12);
|
||||
uint8_t * reply = buf + 1;
|
||||
for (int j = 0; j < num; j++)
|
||||
{
|
||||
nonce[4] = j; // nonce is record #
|
||||
if (j == i)
|
||||
{
|
||||
memset (reply + SHORT_RESPONSE_RECORD_OPTIONS_OFFSET, 0, 2); // no options
|
||||
reply[SHORT_RESPONSE_RECORD_RET_OFFSET] = retCode;
|
||||
if (!i2p::crypto::AEADChaCha20Poly1305 (reply, SHORT_TUNNEL_BUILD_RECORD_SIZE - 16,
|
||||
noiseState.m_H, 32, replyKey, nonce, reply, SHORT_TUNNEL_BUILD_RECORD_SIZE, true)) // encrypt
|
||||
{
|
||||
LogPrint (eLogWarning, "I2NP: Short reply AEAD encryption failed");
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
i2p::crypto::ChaCha20 (reply, SHORT_TUNNEL_BUILD_RECORD_SIZE, replyKey, nonce, reply);
|
||||
reply += SHORT_TUNNEL_BUILD_RECORD_SIZE;
|
||||
}
|
||||
// send reply
|
||||
auto onDrop = [transitTunnel]()
|
||||
{
|
||||
if (transitTunnel)
|
||||
{
|
||||
auto t = transitTunnel->GetCreationTime ();
|
||||
if (t > i2p::tunnel::TUNNEL_EXPIRATION_TIMEOUT)
|
||||
// make transit tunnel expired
|
||||
transitTunnel->SetCreationTime (t - i2p::tunnel::TUNNEL_EXPIRATION_TIMEOUT);
|
||||
}
|
||||
};
|
||||
if (isEndpoint)
|
||||
{
|
||||
auto replyMsg = NewI2NPShortMessage ();
|
||||
replyMsg->Concat (buf, len);
|
||||
replyMsg->FillI2NPMessageHeader (eI2NPShortTunnelBuildReply, bufbe32toh (clearText + SHORT_REQUEST_RECORD_SEND_MSG_ID_OFFSET));
|
||||
if (transitTunnel) replyMsg->onDrop = onDrop;
|
||||
if (memcmp ((const uint8_t *)i2p::context.GetIdentHash (),
|
||||
clearText + SHORT_REQUEST_RECORD_NEXT_IDENT_OFFSET, 32)) // reply IBGW is not local?
|
||||
{
|
||||
i2p::crypto::HKDF (noiseState.m_CK, nullptr, 0, "RGarlicKeyAndTag", noiseState.m_CK);
|
||||
uint64_t tag;
|
||||
memcpy (&tag, noiseState.m_CK, 8);
|
||||
// we send it to reply tunnel
|
||||
transports.SendMessage (clearText + SHORT_REQUEST_RECORD_NEXT_IDENT_OFFSET,
|
||||
CreateTunnelGatewayMsg (bufbe32toh (clearText + SHORT_REQUEST_RECORD_NEXT_TUNNEL_OFFSET),
|
||||
i2p::garlic::WrapECIESX25519Message (replyMsg, noiseState.m_CK + 32, tag)));
|
||||
}
|
||||
else
|
||||
{
|
||||
// IBGW is local
|
||||
uint32_t tunnelID = bufbe32toh (clearText + SHORT_REQUEST_RECORD_NEXT_TUNNEL_OFFSET);
|
||||
auto tunnel = i2p::tunnel::tunnels.GetTunnel (tunnelID);
|
||||
if (tunnel)
|
||||
{
|
||||
tunnel->SendTunnelDataMsg (replyMsg);
|
||||
tunnel->FlushTunnelDataMsgs ();
|
||||
}
|
||||
else
|
||||
LogPrint (eLogWarning, "I2NP: Tunnel ", tunnelID, " not found for short tunnel build reply");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
auto msg = CreateI2NPMessage (eI2NPShortTunnelBuild, buf, len,
|
||||
bufbe32toh (clearText + SHORT_REQUEST_RECORD_SEND_MSG_ID_OFFSET));
|
||||
if (transitTunnel) msg->onDrop = onDrop;
|
||||
transports.SendMessage (clearText + SHORT_REQUEST_RECORD_NEXT_IDENT_OFFSET, msg);
|
||||
}
|
||||
return;
|
||||
}
|
||||
record += SHORT_TUNNEL_BUILD_RECORD_SIZE;
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<I2NPMessage> CreateTunnelDataMsg (const uint8_t * buf)
|
||||
{
|
||||
auto msg = NewI2NPTunnelMessage (false);
|
||||
@@ -802,41 +466,6 @@ namespace i2p
|
||||
return l;
|
||||
}
|
||||
|
||||
void HandleTunnelBuildI2NPMessage (std::shared_ptr<I2NPMessage> msg)
|
||||
{
|
||||
if (msg)
|
||||
{
|
||||
uint8_t typeID = msg->GetTypeID();
|
||||
uint32_t msgID = msg->GetMsgID();
|
||||
LogPrint (eLogDebug, "I2NP: Handling tunnel build message with len=", msg->GetLength(),", type=", (int)typeID, ", msgID=", (unsigned int)msgID);
|
||||
uint8_t * payload = msg->GetPayload();
|
||||
auto size = msg->GetPayloadLength();
|
||||
switch (typeID)
|
||||
{
|
||||
case eI2NPVariableTunnelBuild:
|
||||
HandleVariableTunnelBuildMsg (msgID, payload, size);
|
||||
break;
|
||||
case eI2NPShortTunnelBuild:
|
||||
HandleShortTunnelBuildMsg (msgID, payload, size);
|
||||
break;
|
||||
case eI2NPVariableTunnelBuildReply:
|
||||
HandleTunnelBuildReplyMsg (msgID, payload, size, false);
|
||||
break;
|
||||
case eI2NPShortTunnelBuildReply:
|
||||
HandleTunnelBuildReplyMsg (msgID, payload, size, true);
|
||||
break;
|
||||
case eI2NPTunnelBuild:
|
||||
HandleTunnelBuildMsg (payload, size);
|
||||
break;
|
||||
case eI2NPTunnelBuildReply:
|
||||
// TODO:
|
||||
break;
|
||||
default:
|
||||
LogPrint (eLogError, "I2NP: Unexpected message with type", (int)typeID, " during handling TBM; skipping");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void HandleI2NPMessage (std::shared_ptr<I2NPMessage> msg)
|
||||
{
|
||||
if (msg)
|
||||
@@ -932,14 +561,8 @@ namespace i2p
|
||||
void I2NPMessagesHandler::Flush ()
|
||||
{
|
||||
if (!m_TunnelMsgs.empty ())
|
||||
{
|
||||
i2p::tunnel::tunnels.PostTunnelData (m_TunnelMsgs);
|
||||
m_TunnelMsgs.clear ();
|
||||
}
|
||||
if (!m_TunnelGatewayMsgs.empty ())
|
||||
{
|
||||
i2p::tunnel::tunnels.PostTunnelData (m_TunnelGatewayMsgs);
|
||||
m_TunnelGatewayMsgs.clear ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include <string.h>
|
||||
#include <unordered_set>
|
||||
#include <memory>
|
||||
#include <list>
|
||||
#include <functional>
|
||||
#include "Crypto.h"
|
||||
#include "I2PEndian.h"
|
||||
@@ -315,7 +316,6 @@ namespace tunnel
|
||||
std::shared_ptr<I2NPMessage> CreateTunnelGatewayMsg (uint32_t tunnelID, std::shared_ptr<I2NPMessage> msg);
|
||||
|
||||
size_t GetI2NPMessageLength (const uint8_t * msg, size_t len);
|
||||
void HandleTunnelBuildI2NPMessage (std::shared_ptr<I2NPMessage> msg);
|
||||
void HandleI2NPMessage (std::shared_ptr<I2NPMessage> msg);
|
||||
|
||||
class I2NPMessagesHandler
|
||||
@@ -328,7 +328,7 @@ namespace tunnel
|
||||
|
||||
private:
|
||||
|
||||
std::vector<std::shared_ptr<I2NPMessage> > m_TunnelMsgs, m_TunnelGatewayMsgs;
|
||||
std::list<std::shared_ptr<I2NPMessage> > m_TunnelMsgs, m_TunnelGatewayMsgs;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -425,6 +425,16 @@ namespace data
|
||||
if (offset + 1 > len) return 0;
|
||||
int numLeases = buf[offset]; offset++;
|
||||
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
|
||||
if (GetExpirationTime () > ts + LEASESET_EXPIRATION_TIME_THRESHOLD)
|
||||
{
|
||||
LogPrint (eLogWarning, "LeaseSet2: Expiration time is from future ", GetExpirationTime ()/1000LL);
|
||||
return 0;
|
||||
}
|
||||
if (ts > m_PublishedTimestamp*1000LL + LEASESET_EXPIRATION_TIME_THRESHOLD)
|
||||
{
|
||||
LogPrint (eLogWarning, "LeaseSet2: Published time is too old ", m_PublishedTimestamp);
|
||||
return 0;
|
||||
}
|
||||
if (IsStoreLeases ())
|
||||
{
|
||||
UpdateLeasesBegin ();
|
||||
@@ -435,6 +445,11 @@ namespace data
|
||||
lease.tunnelGateway = buf + offset; offset += 32; // gateway
|
||||
lease.tunnelID = bufbe32toh (buf + offset); offset += 4; // tunnel ID
|
||||
lease.endDate = bufbe32toh (buf + offset)*1000LL; offset += 4; // end date
|
||||
if (lease.endDate > ts + LEASESET_EXPIRATION_TIME_THRESHOLD)
|
||||
{
|
||||
LogPrint (eLogWarning, "LeaseSet2: Lease end date is from future ", lease.endDate);
|
||||
return 0;
|
||||
}
|
||||
UpdateLease (lease, ts);
|
||||
}
|
||||
UpdateLeasesEnd ();
|
||||
|
||||
@@ -62,7 +62,8 @@ namespace data
|
||||
const size_t LEASE_SIZE = 44; // 32 + 4 + 8
|
||||
const size_t LEASE2_SIZE = 40; // 32 + 4 + 4
|
||||
const uint8_t MAX_NUM_LEASES = 16;
|
||||
|
||||
const uint64_t LEASESET_EXPIRATION_TIME_THRESHOLD = 12*60*1000; // in milliseconds
|
||||
|
||||
const uint8_t NETDB_STORE_TYPE_LEASESET = 1;
|
||||
class LeaseSet: public RoutingDestination
|
||||
{
|
||||
@@ -180,7 +181,7 @@ namespace data
|
||||
private:
|
||||
|
||||
uint8_t m_StoreType;
|
||||
uint32_t m_PublishedTimestamp = 0;
|
||||
uint32_t m_PublishedTimestamp = 0; // seconds
|
||||
bool m_IsPublic = true, m_IsPublishedEncrypted = false;
|
||||
std::shared_ptr<i2p::crypto::Verifier> m_TransientVerifier;
|
||||
CryptoKeyType m_EncryptionType;
|
||||
|
||||
@@ -42,28 +42,29 @@ namespace transport
|
||||
delete[] m_SessionConfirmedBuffer;
|
||||
}
|
||||
|
||||
void NTCP2Establisher::KeyDerivationFunction1 (const uint8_t * pub, i2p::crypto::X25519Keys& priv, const uint8_t * rs, const uint8_t * epub)
|
||||
bool NTCP2Establisher::KeyDerivationFunction1 (const uint8_t * pub, i2p::crypto::X25519Keys& priv, const uint8_t * rs, const uint8_t * epub)
|
||||
{
|
||||
i2p::crypto::InitNoiseXKState (*this, rs);
|
||||
// h = SHA256(h || epub)
|
||||
MixHash (epub, 32);
|
||||
// x25519 between pub and priv
|
||||
uint8_t inputKeyMaterial[32];
|
||||
priv.Agree (pub, inputKeyMaterial);
|
||||
if (!priv.Agree (pub, inputKeyMaterial)) return false;
|
||||
MixKey (inputKeyMaterial);
|
||||
return true;
|
||||
}
|
||||
|
||||
void NTCP2Establisher::KDF1Alice ()
|
||||
bool NTCP2Establisher::KDF1Alice ()
|
||||
{
|
||||
KeyDerivationFunction1 (m_RemoteStaticKey, *m_EphemeralKeys, m_RemoteStaticKey, GetPub ());
|
||||
return KeyDerivationFunction1 (m_RemoteStaticKey, *m_EphemeralKeys, m_RemoteStaticKey, GetPub ());
|
||||
}
|
||||
|
||||
void NTCP2Establisher::KDF1Bob ()
|
||||
bool NTCP2Establisher::KDF1Bob ()
|
||||
{
|
||||
KeyDerivationFunction1 (GetRemotePub (), i2p::context.GetNTCP2StaticKeys (), i2p::context.GetNTCP2StaticPublicKey (), GetRemotePub ());
|
||||
return KeyDerivationFunction1 (GetRemotePub (), i2p::context.GetNTCP2StaticKeys (), i2p::context.GetNTCP2StaticPublicKey (), GetRemotePub ());
|
||||
}
|
||||
|
||||
void NTCP2Establisher::KeyDerivationFunction2 (const uint8_t * sessionRequest, size_t sessionRequestLen, const uint8_t * epub)
|
||||
bool NTCP2Establisher::KeyDerivationFunction2 (const uint8_t * sessionRequest, size_t sessionRequestLen, const uint8_t * epub)
|
||||
{
|
||||
MixHash (sessionRequest + 32, 32); // encrypted payload
|
||||
|
||||
@@ -74,33 +75,35 @@ namespace transport
|
||||
|
||||
// x25519 between remote pub and ephemaral priv
|
||||
uint8_t inputKeyMaterial[32];
|
||||
m_EphemeralKeys->Agree (GetRemotePub (), inputKeyMaterial);
|
||||
|
||||
if (!m_EphemeralKeys->Agree (GetRemotePub (), inputKeyMaterial)) return false;
|
||||
MixKey (inputKeyMaterial);
|
||||
return true;
|
||||
}
|
||||
|
||||
void NTCP2Establisher::KDF2Alice ()
|
||||
bool NTCP2Establisher::KDF2Alice ()
|
||||
{
|
||||
KeyDerivationFunction2 (m_SessionRequestBuffer, m_SessionRequestBufferLen, GetRemotePub ());
|
||||
return KeyDerivationFunction2 (m_SessionRequestBuffer, m_SessionRequestBufferLen, GetRemotePub ());
|
||||
}
|
||||
|
||||
void NTCP2Establisher::KDF2Bob ()
|
||||
bool NTCP2Establisher::KDF2Bob ()
|
||||
{
|
||||
KeyDerivationFunction2 (m_SessionRequestBuffer, m_SessionRequestBufferLen, GetPub ());
|
||||
return KeyDerivationFunction2 (m_SessionRequestBuffer, m_SessionRequestBufferLen, GetPub ());
|
||||
}
|
||||
|
||||
void NTCP2Establisher::KDF3Alice ()
|
||||
bool NTCP2Establisher::KDF3Alice ()
|
||||
{
|
||||
uint8_t inputKeyMaterial[32];
|
||||
i2p::context.GetNTCP2StaticKeys ().Agree (GetRemotePub (), inputKeyMaterial);
|
||||
if (!i2p::context.GetNTCP2StaticKeys ().Agree (GetRemotePub (), inputKeyMaterial)) return false;
|
||||
MixKey (inputKeyMaterial);
|
||||
return true;
|
||||
}
|
||||
|
||||
void NTCP2Establisher::KDF3Bob ()
|
||||
bool NTCP2Establisher::KDF3Bob ()
|
||||
{
|
||||
uint8_t inputKeyMaterial[32];
|
||||
m_EphemeralKeys->Agree (m_RemoteStaticKey, inputKeyMaterial);
|
||||
if (!m_EphemeralKeys->Agree (m_RemoteStaticKey, inputKeyMaterial)) return false;
|
||||
MixKey (inputKeyMaterial);
|
||||
return true;
|
||||
}
|
||||
|
||||
void NTCP2Establisher::CreateEphemeralKey ()
|
||||
@@ -108,7 +111,7 @@ namespace transport
|
||||
m_EphemeralKeys = i2p::transport::transports.GetNextX25519KeysPair ();
|
||||
}
|
||||
|
||||
void NTCP2Establisher::CreateSessionRequestMessage (std::mt19937& rng)
|
||||
bool NTCP2Establisher::CreateSessionRequestMessage (std::mt19937& rng)
|
||||
{
|
||||
// create buffer and fill padding
|
||||
auto paddingLength = rng () % (NTCP2_SESSION_REQUEST_MAX_SIZE - 64); // message length doesn't exceed 287 bytes
|
||||
@@ -117,11 +120,10 @@ namespace transport
|
||||
// encrypt X
|
||||
i2p::crypto::CBCEncryption encryption;
|
||||
encryption.SetKey (m_RemoteIdentHash);
|
||||
encryption.SetIV (m_IV);
|
||||
encryption.Encrypt (GetPub (), 32, m_SessionRequestBuffer); // X
|
||||
encryption.GetIV (m_IV); // save IV for SessionCreated
|
||||
encryption.Encrypt (GetPub (), 32, m_IV, m_SessionRequestBuffer); // X
|
||||
memcpy (m_IV, m_SessionRequestBuffer + 16, 16); // save last block as IV for SessionCreated
|
||||
// encryption key for next block
|
||||
KDF1Alice ();
|
||||
if (!KDF1Alice ()) return false;
|
||||
// fill options
|
||||
uint8_t options[32]; // actual options size is 16 bytes
|
||||
memset (options, 0, 16);
|
||||
@@ -147,9 +149,10 @@ namespace transport
|
||||
uint8_t nonce[12];
|
||||
memset (nonce, 0, 12); // set nonce to zero
|
||||
i2p::crypto::AEADChaCha20Poly1305 (options, 16, GetH (), 32, GetK (), nonce, m_SessionRequestBuffer + 32, 32, true); // encrypt
|
||||
return true;
|
||||
}
|
||||
|
||||
void NTCP2Establisher::CreateSessionCreatedMessage (std::mt19937& rng)
|
||||
bool NTCP2Establisher::CreateSessionCreatedMessage (std::mt19937& rng)
|
||||
{
|
||||
auto paddingLen = rng () % (NTCP2_SESSION_CREATED_MAX_SIZE - 64);
|
||||
m_SessionCreatedBufferLen = paddingLen + 64;
|
||||
@@ -157,10 +160,9 @@ namespace transport
|
||||
// encrypt Y
|
||||
i2p::crypto::CBCEncryption encryption;
|
||||
encryption.SetKey (i2p::context.GetIdentHash ());
|
||||
encryption.SetIV (m_IV);
|
||||
encryption.Encrypt (GetPub (), 32, m_SessionCreatedBuffer); // Y
|
||||
encryption.Encrypt (GetPub (), 32, m_IV, m_SessionCreatedBuffer); // Y
|
||||
// encryption key for next block (m_K)
|
||||
KDF2Bob ();
|
||||
if (!KDF2Bob ()) return false;
|
||||
uint8_t options[16];
|
||||
memset (options, 0, 16);
|
||||
htobe16buf (options + 2, paddingLen); // padLen
|
||||
@@ -169,7 +171,7 @@ namespace transport
|
||||
uint8_t nonce[12];
|
||||
memset (nonce, 0, 12); // set nonce to zero
|
||||
i2p::crypto::AEADChaCha20Poly1305 (options, 16, GetH (), 32, GetK (), nonce, m_SessionCreatedBuffer + 32, 32, true); // encrypt
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void NTCP2Establisher::CreateSessionConfirmedMessagePart1 (const uint8_t * nonce)
|
||||
@@ -184,17 +186,18 @@ namespace transport
|
||||
i2p::crypto::AEADChaCha20Poly1305 (i2p::context.GetNTCP2StaticPublicKey (), 32, GetH (), 32, GetK (), nonce, m_SessionConfirmedBuffer, 48, true); // encrypt
|
||||
}
|
||||
|
||||
void NTCP2Establisher::CreateSessionConfirmedMessagePart2 (const uint8_t * nonce)
|
||||
bool NTCP2Establisher::CreateSessionConfirmedMessagePart2 (const uint8_t * nonce)
|
||||
{
|
||||
// part 2
|
||||
// update AD again
|
||||
MixHash (m_SessionConfirmedBuffer, 48);
|
||||
// encrypt m3p2, it must be filled in SessionRequest
|
||||
KDF3Alice ();
|
||||
if (!KDF3Alice ()) return false;
|
||||
uint8_t * m3p2 = m_SessionConfirmedBuffer + 48;
|
||||
i2p::crypto::AEADChaCha20Poly1305 (m3p2, m3p2Len - 16, GetH (), 32, GetK (), nonce, m3p2, m3p2Len, true); // encrypt
|
||||
// update h again
|
||||
MixHash (m3p2, m3p2Len); //h = SHA256(h || ciphertext)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NTCP2Establisher::ProcessSessionRequestMessage (uint16_t& paddingLen, bool& clockSkew)
|
||||
@@ -203,11 +206,14 @@ namespace transport
|
||||
// decrypt X
|
||||
i2p::crypto::CBCDecryption decryption;
|
||||
decryption.SetKey (i2p::context.GetIdentHash ());
|
||||
decryption.SetIV (i2p::context.GetNTCP2IV ());
|
||||
decryption.Decrypt (m_SessionRequestBuffer, 32, GetRemotePub ());
|
||||
decryption.GetIV (m_IV); // save IV for SessionCreated
|
||||
decryption.Decrypt (m_SessionRequestBuffer, 32, i2p::context.GetNTCP2IV (), GetRemotePub ());
|
||||
memcpy (m_IV, m_SessionRequestBuffer + 16, 16); // save last block as IV for SessionCreated
|
||||
// decryption key for next block
|
||||
KDF1Bob ();
|
||||
if (!KDF1Bob ())
|
||||
{
|
||||
LogPrint (eLogWarning, "NTCP2: SessionRequest KDF failed");
|
||||
return false;
|
||||
}
|
||||
// verify MAC and decrypt options block (32 bytes), use m_H as AD
|
||||
uint8_t nonce[12], options[16];
|
||||
memset (nonce, 0, 12); // set nonce to zero
|
||||
@@ -259,10 +265,13 @@ namespace transport
|
||||
// decrypt Y
|
||||
i2p::crypto::CBCDecryption decryption;
|
||||
decryption.SetKey (m_RemoteIdentHash);
|
||||
decryption.SetIV (m_IV);
|
||||
decryption.Decrypt (m_SessionCreatedBuffer, 32, GetRemotePub ());
|
||||
decryption.Decrypt (m_SessionCreatedBuffer, 32, m_IV, GetRemotePub ());
|
||||
// decryption key for next block (m_K)
|
||||
KDF2Alice ();
|
||||
if (!KDF2Alice ())
|
||||
{
|
||||
LogPrint (eLogWarning, "NTCP2: SessionCreated KDF failed");
|
||||
return false;
|
||||
}
|
||||
// decrypt and verify MAC
|
||||
uint8_t payload[16];
|
||||
uint8_t nonce[12];
|
||||
@@ -309,7 +318,11 @@ namespace transport
|
||||
// update AD again
|
||||
MixHash (m_SessionConfirmedBuffer, 48);
|
||||
|
||||
KDF3Bob ();
|
||||
if (!KDF3Bob ())
|
||||
{
|
||||
LogPrint (eLogWarning, "NTCP2: SessionConfirmed Part2 KDF failed");
|
||||
return false;
|
||||
}
|
||||
if (i2p::crypto::AEADChaCha20Poly1305 (m_SessionConfirmedBuffer + 48, m3p2Len - 16, GetH (), 32, GetK (), nonce, m3p2Buf, m3p2Len - 16, false)) // decrypt
|
||||
// calculate new h again for KDF data
|
||||
MixHash (m_SessionConfirmedBuffer + 48, m3p2Len); // h = SHA256(h || ciphertext)
|
||||
@@ -327,6 +340,7 @@ namespace transport
|
||||
m_Server (server), m_Socket (m_Server.GetService ()),
|
||||
m_IsEstablished (false), m_IsTerminated (false),
|
||||
m_Establisher (new NTCP2Establisher),
|
||||
m_SendKey (nullptr), m_ReceiveKey (nullptr),
|
||||
#if OPENSSL_SIPHASH
|
||||
m_SendMDCtx(nullptr), m_ReceiveMDCtx (nullptr),
|
||||
#else
|
||||
@@ -375,6 +389,8 @@ namespace transport
|
||||
m_Socket.close ();
|
||||
transports.PeerDisconnected (shared_from_this ());
|
||||
m_Server.RemoveNTCP2Session (shared_from_this ());
|
||||
if (!m_IntermediateQueue.empty ())
|
||||
m_SendQueue.splice (m_SendQueue.end (), m_IntermediateQueue);
|
||||
for (auto& it: m_SendQueue)
|
||||
it->Drop ();
|
||||
m_SendQueue.clear ();
|
||||
@@ -404,7 +420,7 @@ namespace transport
|
||||
|
||||
void NTCP2Session::Done ()
|
||||
{
|
||||
m_Server.GetService ().post (std::bind (&NTCP2Session::Terminate, shared_from_this ()));
|
||||
boost::asio::post (m_Server.GetService (), std::bind (&NTCP2Session::Terminate, shared_from_this ()));
|
||||
}
|
||||
|
||||
void NTCP2Session::Established ()
|
||||
@@ -464,7 +480,12 @@ namespace transport
|
||||
|
||||
void NTCP2Session::SendSessionRequest ()
|
||||
{
|
||||
m_Establisher->CreateSessionRequestMessage (m_Server.GetRng ());
|
||||
if (!m_Establisher->CreateSessionRequestMessage (m_Server.GetRng ()))
|
||||
{
|
||||
LogPrint (eLogWarning, "NTCP2: Send SessionRequest KDF failed");
|
||||
boost::asio::post (m_Server.GetService (), std::bind (&NTCP2Session::Terminate, shared_from_this ()));
|
||||
return;
|
||||
}
|
||||
// send message
|
||||
m_HandshakeInterval = i2p::util::GetMillisecondsSinceEpoch ();
|
||||
boost::asio::async_write (m_Socket, boost::asio::buffer (m_Establisher->m_SessionRequestBuffer, m_Establisher->m_SessionRequestBufferLen), boost::asio::transfer_all (),
|
||||
@@ -489,7 +510,6 @@ namespace transport
|
||||
|
||||
void NTCP2Session::HandleSessionRequestReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred)
|
||||
{
|
||||
(void) bytes_transferred;
|
||||
if (ecode)
|
||||
{
|
||||
LogPrint (eLogWarning, "NTCP2: SessionRequest read error: ", ecode.message ());
|
||||
@@ -497,38 +517,47 @@ namespace transport
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint (eLogDebug, "NTCP2: SessionRequest received ", bytes_transferred);
|
||||
uint16_t paddingLen = 0;
|
||||
bool clockSkew = false;
|
||||
if (m_Establisher->ProcessSessionRequestMessage (paddingLen, clockSkew))
|
||||
{
|
||||
if (clockSkew)
|
||||
boost::asio::post (m_Server.GetEstablisherService (),
|
||||
[s = shared_from_this (), bytes_transferred] ()
|
||||
{
|
||||
// we don't care about padding, send SessionCreated and close session
|
||||
SendSessionCreated ();
|
||||
m_Server.GetService ().post (std::bind (&NTCP2Session::Terminate, shared_from_this ()));
|
||||
}
|
||||
else if (paddingLen > 0)
|
||||
{
|
||||
if (paddingLen <= NTCP2_SESSION_REQUEST_MAX_SIZE - 64) // session request is 287 bytes max
|
||||
{
|
||||
boost::asio::async_read (m_Socket, boost::asio::buffer(m_Establisher->m_SessionRequestBuffer + 64, paddingLen), boost::asio::transfer_all (),
|
||||
std::bind(&NTCP2Session::HandleSessionRequestPaddingReceived, shared_from_this (), std::placeholders::_1, std::placeholders::_2));
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint (eLogWarning, "NTCP2: SessionRequest padding length ", (int)paddingLen, " is too long");
|
||||
Terminate ();
|
||||
}
|
||||
}
|
||||
else
|
||||
SendSessionCreated ();
|
||||
}
|
||||
else
|
||||
Terminate ();
|
||||
s->ProcessSessionRequest (bytes_transferred);;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void NTCP2Session::ProcessSessionRequest (size_t len)
|
||||
{
|
||||
LogPrint (eLogDebug, "NTCP2: SessionRequest received ", len);
|
||||
uint16_t paddingLen = 0;
|
||||
bool clockSkew = false;
|
||||
if (m_Establisher->ProcessSessionRequestMessage (paddingLen, clockSkew))
|
||||
{
|
||||
if (clockSkew)
|
||||
{
|
||||
// we don't care about padding, send SessionCreated and close session
|
||||
SendSessionCreated ();
|
||||
boost::asio::post (m_Server.GetService (), std::bind (&NTCP2Session::Terminate, shared_from_this ()));
|
||||
}
|
||||
else if (paddingLen > 0)
|
||||
{
|
||||
if (paddingLen <= NTCP2_SESSION_REQUEST_MAX_SIZE - 64) // session request is 287 bytes max
|
||||
{
|
||||
boost::asio::async_read (m_Socket, boost::asio::buffer(m_Establisher->m_SessionRequestBuffer + 64, paddingLen), boost::asio::transfer_all (),
|
||||
std::bind(&NTCP2Session::HandleSessionRequestPaddingReceived, shared_from_this (), std::placeholders::_1, std::placeholders::_2));
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint (eLogWarning, "NTCP2: SessionRequest padding length ", (int)paddingLen, " is too long");
|
||||
boost::asio::post (m_Server.GetService (), std::bind (&NTCP2Session::Terminate, shared_from_this ()));
|
||||
}
|
||||
}
|
||||
else
|
||||
SendSessionCreated ();
|
||||
}
|
||||
else
|
||||
boost::asio::post (m_Server.GetService (), std::bind (&NTCP2Session::Terminate, shared_from_this ()));
|
||||
}
|
||||
|
||||
void NTCP2Session::HandleSessionRequestPaddingReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred)
|
||||
{
|
||||
if (ecode)
|
||||
@@ -537,12 +566,23 @@ namespace transport
|
||||
Terminate ();
|
||||
}
|
||||
else
|
||||
SendSessionCreated ();
|
||||
{
|
||||
boost::asio::post (m_Server.GetEstablisherService (),
|
||||
[s = shared_from_this ()] ()
|
||||
{
|
||||
s->SendSessionCreated ();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void NTCP2Session::SendSessionCreated ()
|
||||
{
|
||||
m_Establisher->CreateSessionCreatedMessage (m_Server.GetRng ());
|
||||
if (!m_Establisher->CreateSessionCreatedMessage (m_Server.GetRng ()))
|
||||
{
|
||||
LogPrint (eLogWarning, "NTCP2: Send SessionCreated KDF failed");
|
||||
boost::asio::post (m_Server.GetService (), std::bind (&NTCP2Session::Terminate, shared_from_this ()));
|
||||
return;
|
||||
}
|
||||
// send message
|
||||
m_HandshakeInterval = i2p::util::GetMillisecondsSinceEpoch ();
|
||||
boost::asio::async_write (m_Socket, boost::asio::buffer (m_Establisher->m_SessionCreatedBuffer, m_Establisher->m_SessionCreatedBufferLen), boost::asio::transfer_all (),
|
||||
@@ -559,35 +599,44 @@ namespace transport
|
||||
else
|
||||
{
|
||||
m_HandshakeInterval = i2p::util::GetMillisecondsSinceEpoch () - m_HandshakeInterval;
|
||||
LogPrint (eLogDebug, "NTCP2: SessionCreated received ", bytes_transferred);
|
||||
uint16_t paddingLen = 0;
|
||||
if (m_Establisher->ProcessSessionCreatedMessage (paddingLen))
|
||||
{
|
||||
if (paddingLen > 0)
|
||||
boost::asio::post (m_Server.GetEstablisherService (),
|
||||
[s = shared_from_this (), bytes_transferred] ()
|
||||
{
|
||||
if (paddingLen <= NTCP2_SESSION_CREATED_MAX_SIZE - 64) // session created is 287 bytes max
|
||||
{
|
||||
boost::asio::async_read (m_Socket, boost::asio::buffer(m_Establisher->m_SessionCreatedBuffer + 64, paddingLen), boost::asio::transfer_all (),
|
||||
std::bind(&NTCP2Session::HandleSessionCreatedPaddingReceived, shared_from_this (), std::placeholders::_1, std::placeholders::_2));
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint (eLogWarning, "NTCP2: SessionCreated padding length ", (int)paddingLen, " is too long");
|
||||
Terminate ();
|
||||
}
|
||||
}
|
||||
else
|
||||
SendSessionConfirmed ();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (GetRemoteIdentity ())
|
||||
i2p::data::netdb.SetUnreachable (GetRemoteIdentity ()->GetIdentHash (), true); // assume wrong s key
|
||||
Terminate ();
|
||||
}
|
||||
s->ProcessSessionCreated (bytes_transferred);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void NTCP2Session::ProcessSessionCreated (size_t len)
|
||||
{
|
||||
LogPrint (eLogDebug, "NTCP2: SessionCreated received ", len);
|
||||
uint16_t paddingLen = 0;
|
||||
if (m_Establisher->ProcessSessionCreatedMessage (paddingLen))
|
||||
{
|
||||
if (paddingLen > 0)
|
||||
{
|
||||
if (paddingLen <= NTCP2_SESSION_CREATED_MAX_SIZE - 64) // session created is 287 bytes max
|
||||
{
|
||||
boost::asio::async_read (m_Socket, boost::asio::buffer(m_Establisher->m_SessionCreatedBuffer + 64, paddingLen), boost::asio::transfer_all (),
|
||||
std::bind(&NTCP2Session::HandleSessionCreatedPaddingReceived, shared_from_this (), std::placeholders::_1, std::placeholders::_2));
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint (eLogWarning, "NTCP2: SessionCreated padding length ", (int)paddingLen, " is too long");
|
||||
boost::asio::post (m_Server.GetService (), std::bind (&NTCP2Session::Terminate, shared_from_this ()));
|
||||
}
|
||||
}
|
||||
else
|
||||
SendSessionConfirmed ();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (GetRemoteIdentity ())
|
||||
i2p::data::netdb.SetUnreachable (GetRemoteIdentity ()->GetIdentHash (), true); // assume wrong s key
|
||||
boost::asio::post (m_Server.GetService (), std::bind (&NTCP2Session::Terminate, shared_from_this ()));
|
||||
}
|
||||
}
|
||||
|
||||
void NTCP2Session::HandleSessionCreatedPaddingReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred)
|
||||
{
|
||||
if (ecode)
|
||||
@@ -598,7 +647,11 @@ namespace transport
|
||||
else
|
||||
{
|
||||
m_Establisher->m_SessionCreatedBufferLen += bytes_transferred;
|
||||
SendSessionConfirmed ();
|
||||
boost::asio::post (m_Server.GetEstablisherService (),
|
||||
[s = shared_from_this ()] ()
|
||||
{
|
||||
s->SendSessionConfirmed ();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -608,7 +661,12 @@ namespace transport
|
||||
CreateNonce (1, nonce); // set nonce to 1
|
||||
m_Establisher->CreateSessionConfirmedMessagePart1 (nonce);
|
||||
memset (nonce, 0, 12); // set nonce back to 0
|
||||
m_Establisher->CreateSessionConfirmedMessagePart2 (nonce);
|
||||
if (!m_Establisher->CreateSessionConfirmedMessagePart2 (nonce))
|
||||
{
|
||||
LogPrint (eLogWarning, "NTCP2: Send SessionConfirmed Part2 KDF failed");
|
||||
boost::asio::post (m_Server.GetService (), std::bind (&NTCP2Session::Terminate, shared_from_this ()));
|
||||
return;
|
||||
}
|
||||
// send message
|
||||
boost::asio::async_write (m_Socket, boost::asio::buffer (m_Establisher->m_SessionConfirmedBuffer, m_Establisher->m3p2Len + 48), boost::asio::transfer_all (),
|
||||
std::bind(&NTCP2Session::HandleSessionConfirmedSent, shared_from_this (), std::placeholders::_1, std::placeholders::_2));
|
||||
@@ -660,6 +718,7 @@ namespace transport
|
||||
|
||||
void NTCP2Session::HandleSessionConfirmedReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred)
|
||||
{
|
||||
(void) bytes_transferred;
|
||||
if (ecode)
|
||||
{
|
||||
LogPrint (eLogWarning, "NTCP2: SessionConfirmed read error: ", ecode.message ());
|
||||
@@ -668,122 +727,143 @@ namespace transport
|
||||
else
|
||||
{
|
||||
m_HandshakeInterval = i2p::util::GetMillisecondsSinceEpoch () - m_HandshakeInterval;
|
||||
LogPrint (eLogDebug, "NTCP2: SessionConfirmed received");
|
||||
// part 1
|
||||
uint8_t nonce[12];
|
||||
CreateNonce (1, nonce);
|
||||
if (m_Establisher->ProcessSessionConfirmedMessagePart1 (nonce))
|
||||
{
|
||||
// part 2
|
||||
std::vector<uint8_t> buf(m_Establisher->m3p2Len - 16); // -MAC
|
||||
memset (nonce, 0, 12); // set nonce to 0 again
|
||||
if (m_Establisher->ProcessSessionConfirmedMessagePart2 (nonce, buf.data ()))
|
||||
boost::asio::post (m_Server.GetEstablisherService (),
|
||||
[s = shared_from_this ()] ()
|
||||
{
|
||||
KeyDerivationFunctionDataPhase ();
|
||||
// Bob data phase keys
|
||||
m_SendKey = m_Kba;
|
||||
m_ReceiveKey = m_Kab;
|
||||
SetSipKeys (m_Sipkeysba, m_Sipkeysab);
|
||||
memcpy (m_ReceiveIV.buf, m_Sipkeysab + 16, 8);
|
||||
memcpy (m_SendIV.buf, m_Sipkeysba + 16, 8);
|
||||
// payload
|
||||
// process RI
|
||||
if (buf[0] != eNTCP2BlkRouterInfo)
|
||||
{
|
||||
LogPrint (eLogWarning, "NTCP2: Unexpected block ", (int)buf[0], " in SessionConfirmed");
|
||||
Terminate ();
|
||||
return;
|
||||
}
|
||||
auto size = bufbe16toh (buf.data () + 1);
|
||||
if (size > buf.size () - 3 || size > i2p::data::MAX_RI_BUFFER_SIZE + 1)
|
||||
{
|
||||
LogPrint (eLogError, "NTCP2: Unexpected RouterInfo size ", size, " in SessionConfirmed");
|
||||
Terminate ();
|
||||
return;
|
||||
}
|
||||
// TODO: check flag
|
||||
i2p::data::RouterInfo ri (buf.data () + 4, size - 1); // 1 byte block type + 2 bytes size + 1 byte flag
|
||||
if (ri.IsUnreachable ())
|
||||
{
|
||||
LogPrint (eLogError, "NTCP2: RouterInfo verification failed in SessionConfirmed from ", GetRemoteEndpoint ());
|
||||
SendTerminationAndTerminate (eNTCP2RouterInfoSignatureVerificationFail);
|
||||
return;
|
||||
}
|
||||
LogPrint(eLogDebug, "NTCP2: SessionConfirmed from ", GetRemoteEndpoint (),
|
||||
" (", i2p::data::GetIdentHashAbbreviation (ri.GetIdentHash ()), ")");
|
||||
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
|
||||
if (ts > ri.GetTimestamp () + i2p::data::NETDB_MIN_EXPIRATION_TIMEOUT*1000LL) // 90 minutes
|
||||
{
|
||||
LogPrint (eLogError, "NTCP2: RouterInfo is too old in SessionConfirmed for ", (ts - ri.GetTimestamp ())/1000LL, " seconds");
|
||||
SendTerminationAndTerminate (eNTCP2Message3Error);
|
||||
return;
|
||||
}
|
||||
if (ts + i2p::data::NETDB_EXPIRATION_TIMEOUT_THRESHOLD*1000LL < ri.GetTimestamp ()) // 2 minutes
|
||||
{
|
||||
LogPrint (eLogError, "NTCP2: RouterInfo is from future for ", (ri.GetTimestamp () - ts)/1000LL, " seconds");
|
||||
SendTerminationAndTerminate (eNTCP2Message3Error);
|
||||
return;
|
||||
}
|
||||
// update RouterInfo in netdb
|
||||
auto ri1 = i2p::data::netdb.AddRouterInfo (ri.GetBuffer (), ri.GetBufferLen ()); // ri1 points to one from netdb now
|
||||
if (!ri1)
|
||||
{
|
||||
LogPrint (eLogError, "NTCP2: Couldn't update RouterInfo from SessionConfirmed in netdb");
|
||||
Terminate ();
|
||||
return;
|
||||
}
|
||||
std::shared_ptr<i2p::data::RouterProfile> profile; // not null if older
|
||||
if (ri.GetTimestamp () + i2p::data::NETDB_EXPIRATION_TIMEOUT_THRESHOLD*1000LL < ri1->GetTimestamp ())
|
||||
{
|
||||
// received RouterInfo is older than one in netdb
|
||||
profile = i2p::data::GetRouterProfile (ri1->GetIdentHash ()); // retrieve profile
|
||||
if (profile && profile->IsDuplicated ())
|
||||
{
|
||||
SendTerminationAndTerminate (eNTCP2Banned);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
auto addr = m_RemoteEndpoint.address ().is_v4 () ? ri1->GetNTCP2V4Address () :
|
||||
(i2p::util::net::IsYggdrasilAddress (m_RemoteEndpoint.address ()) ? ri1->GetYggdrasilAddress () : ri1->GetNTCP2V6Address ());
|
||||
if (!addr || memcmp (m_Establisher->m_RemoteStaticKey, addr->s, 32))
|
||||
{
|
||||
LogPrint (eLogError, "NTCP2: Wrong static key in SessionConfirmed");
|
||||
Terminate ();
|
||||
return;
|
||||
}
|
||||
if (addr->IsPublishedNTCP2 () && m_RemoteEndpoint.address () != addr->host &&
|
||||
(!m_RemoteEndpoint.address ().is_v6 () || (i2p::util::net::IsYggdrasilAddress (m_RemoteEndpoint.address ()) ?
|
||||
memcmp (m_RemoteEndpoint.address ().to_v6 ().to_bytes ().data () + 1, addr->host.to_v6 ().to_bytes ().data () + 1, 7) : // from the same yggdrasil subnet
|
||||
memcmp (m_RemoteEndpoint.address ().to_v6 ().to_bytes ().data (), addr->host.to_v6 ().to_bytes ().data (), 8)))) // temporary address
|
||||
{
|
||||
if (profile) // older router?
|
||||
profile->Duplicated (); // mark router as duplicated in profile
|
||||
else
|
||||
LogPrint (eLogInfo, "NTCP2: Host mismatch between published address ", addr->host, " and actual endpoint ", m_RemoteEndpoint.address ());
|
||||
SendTerminationAndTerminate (eNTCP2Banned);
|
||||
return;
|
||||
}
|
||||
// TODO: process options
|
||||
|
||||
// ready to communicate
|
||||
SetRemoteIdentity (ri1->GetRouterIdentity ());
|
||||
if (m_Server.AddNTCP2Session (shared_from_this (), true))
|
||||
{
|
||||
Established ();
|
||||
ReceiveLength ();
|
||||
}
|
||||
else
|
||||
Terminate ();
|
||||
}
|
||||
else
|
||||
Terminate ();
|
||||
}
|
||||
else
|
||||
Terminate ();
|
||||
s->ProcessSessionConfirmed ();;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void NTCP2Session::ProcessSessionConfirmed ()
|
||||
{
|
||||
// run on establisher thread
|
||||
LogPrint (eLogDebug, "NTCP2: SessionConfirmed received");
|
||||
// part 1
|
||||
uint8_t nonce[12];
|
||||
CreateNonce (1, nonce);
|
||||
if (m_Establisher->ProcessSessionConfirmedMessagePart1 (nonce))
|
||||
{
|
||||
// part 2
|
||||
auto buf = std::make_shared<std::vector<uint8_t> > (m_Establisher->m3p2Len - 16); // -MAC
|
||||
memset (nonce, 0, 12); // set nonce to 0 again
|
||||
if (m_Establisher->ProcessSessionConfirmedMessagePart2 (nonce, buf->data ())) // TODO:handle in establisher thread
|
||||
{
|
||||
// payload
|
||||
// RI block must be first
|
||||
if ((*buf)[0] != eNTCP2BlkRouterInfo)
|
||||
{
|
||||
LogPrint (eLogWarning, "NTCP2: Unexpected block ", (int)(*buf)[0], " in SessionConfirmed");
|
||||
boost::asio::post (m_Server.GetService (), std::bind (&NTCP2Session::Terminate, shared_from_this ()));
|
||||
return;
|
||||
}
|
||||
auto size = bufbe16toh (buf->data () + 1);
|
||||
if (size > buf->size () - 3 || size > i2p::data::MAX_RI_BUFFER_SIZE + 1)
|
||||
{
|
||||
LogPrint (eLogError, "NTCP2: Unexpected RouterInfo size ", size, " in SessionConfirmed");
|
||||
boost::asio::post (m_Server.GetService (), std::bind (&NTCP2Session::Terminate, shared_from_this ()));
|
||||
return;
|
||||
}
|
||||
boost::asio::post (m_Server.GetService (),
|
||||
[s = shared_from_this (), buf, size] ()
|
||||
{
|
||||
s->EstablishSessionAfterSessionConfirmed (buf, size);
|
||||
});
|
||||
}
|
||||
else
|
||||
boost::asio::post (m_Server.GetService (), std::bind (&NTCP2Session::Terminate, shared_from_this ()));
|
||||
}
|
||||
else
|
||||
boost::asio::post (m_Server.GetService (), std::bind (&NTCP2Session::Terminate, shared_from_this ()));
|
||||
}
|
||||
|
||||
void NTCP2Session::EstablishSessionAfterSessionConfirmed (std::shared_ptr<std::vector<uint8_t> > buf, size_t size)
|
||||
{
|
||||
// run on main NTCP2 thread
|
||||
KeyDerivationFunctionDataPhase ();
|
||||
// Bob data phase keys
|
||||
m_SendKey = m_Kba;
|
||||
m_ReceiveKey = m_Kab;
|
||||
SetSipKeys (m_Sipkeysba, m_Sipkeysab);
|
||||
memcpy (m_ReceiveIV.buf, m_Sipkeysab + 16, 8);
|
||||
memcpy (m_SendIV.buf, m_Sipkeysba + 16, 8);
|
||||
// we need to set keys for SendTerminationAndTerminate
|
||||
// TODO: check flag
|
||||
i2p::data::RouterInfo ri (buf->data () + 4, size - 1); // 1 byte block type + 2 bytes size + 1 byte flag
|
||||
if (ri.IsUnreachable ())
|
||||
{
|
||||
LogPrint (eLogError, "NTCP2: RouterInfo verification failed in SessionConfirmed from ", GetRemoteEndpoint ());
|
||||
SendTerminationAndTerminate (eNTCP2RouterInfoSignatureVerificationFail);
|
||||
return;
|
||||
}
|
||||
LogPrint(eLogDebug, "NTCP2: SessionConfirmed from ", GetRemoteEndpoint (),
|
||||
" (", i2p::data::GetIdentHashAbbreviation (ri.GetIdentHash ()), ")");
|
||||
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
|
||||
if (ts > ri.GetTimestamp () + i2p::data::NETDB_MIN_EXPIRATION_TIMEOUT*1000LL) // 90 minutes
|
||||
{
|
||||
LogPrint (eLogError, "NTCP2: RouterInfo is too old in SessionConfirmed for ", (ts - ri.GetTimestamp ())/1000LL, " seconds");
|
||||
SendTerminationAndTerminate (eNTCP2Message3Error);
|
||||
return;
|
||||
}
|
||||
if (ts + i2p::data::NETDB_EXPIRATION_TIMEOUT_THRESHOLD*1000LL < ri.GetTimestamp ()) // 2 minutes
|
||||
{
|
||||
LogPrint (eLogError, "NTCP2: RouterInfo is from future for ", (ri.GetTimestamp () - ts)/1000LL, " seconds");
|
||||
SendTerminationAndTerminate (eNTCP2Message3Error);
|
||||
return;
|
||||
}
|
||||
// update RouterInfo in netdb
|
||||
auto ri1 = i2p::data::netdb.AddRouterInfo (ri.GetBuffer (), ri.GetBufferLen ()); // ri1 points to one from netdb now
|
||||
if (!ri1)
|
||||
{
|
||||
LogPrint (eLogError, "NTCP2: Couldn't update RouterInfo from SessionConfirmed in netdb");
|
||||
Terminate ();
|
||||
return;
|
||||
}
|
||||
std::shared_ptr<i2p::data::RouterProfile> profile; // not null if older
|
||||
if (ri.GetTimestamp () + i2p::data::NETDB_EXPIRATION_TIMEOUT_THRESHOLD*1000LL < ri1->GetTimestamp ())
|
||||
{
|
||||
// received RouterInfo is older than one in netdb
|
||||
profile = i2p::data::GetRouterProfile (ri1->GetIdentHash ()); // retrieve profile
|
||||
if (profile && profile->IsDuplicated ())
|
||||
{
|
||||
SendTerminationAndTerminate (eNTCP2Banned);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
auto addr = m_RemoteEndpoint.address ().is_v4 () ? ri1->GetNTCP2V4Address () :
|
||||
(i2p::util::net::IsYggdrasilAddress (m_RemoteEndpoint.address ()) ? ri1->GetYggdrasilAddress () : ri1->GetNTCP2V6Address ());
|
||||
if (!addr || memcmp (m_Establisher->m_RemoteStaticKey, addr->s, 32))
|
||||
{
|
||||
LogPrint (eLogError, "NTCP2: Wrong static key in SessionConfirmed");
|
||||
Terminate ();
|
||||
return;
|
||||
}
|
||||
if (addr->IsPublishedNTCP2 () && m_RemoteEndpoint.address () != addr->host &&
|
||||
(!m_RemoteEndpoint.address ().is_v6 () || (i2p::util::net::IsYggdrasilAddress (m_RemoteEndpoint.address ()) ?
|
||||
memcmp (m_RemoteEndpoint.address ().to_v6 ().to_bytes ().data () + 1, addr->host.to_v6 ().to_bytes ().data () + 1, 7) : // from the same yggdrasil subnet
|
||||
memcmp (m_RemoteEndpoint.address ().to_v6 ().to_bytes ().data (), addr->host.to_v6 ().to_bytes ().data (), 8)))) // temporary address
|
||||
{
|
||||
if (profile) // older router?
|
||||
profile->Duplicated (); // mark router as duplicated in profile
|
||||
else
|
||||
LogPrint (eLogInfo, "NTCP2: Host mismatch between published address ", addr->host, " and actual endpoint ", m_RemoteEndpoint.address ());
|
||||
SendTerminationAndTerminate (eNTCP2Banned);
|
||||
return;
|
||||
}
|
||||
// TODO: process options block
|
||||
|
||||
// ready to communicate
|
||||
SetRemoteIdentity (ri1->GetRouterIdentity ());
|
||||
if (m_Server.AddNTCP2Session (shared_from_this (), true))
|
||||
{
|
||||
Established ();
|
||||
ReceiveLength ();
|
||||
}
|
||||
else
|
||||
Terminate ();
|
||||
}
|
||||
|
||||
void NTCP2Session::SetSipKeys (const uint8_t * sendSipKey, const uint8_t * receiveSipKey)
|
||||
{
|
||||
#if OPENSSL_SIPHASH
|
||||
@@ -809,7 +889,11 @@ namespace transport
|
||||
void NTCP2Session::ClientLogin ()
|
||||
{
|
||||
m_Establisher->CreateEphemeralKey ();
|
||||
SendSessionRequest ();
|
||||
boost::asio::post (m_Server.GetEstablisherService (),
|
||||
[s = shared_from_this ()] ()
|
||||
{
|
||||
s->SendSessionRequest ();
|
||||
});
|
||||
}
|
||||
|
||||
void NTCP2Session::ServerLogin ()
|
||||
@@ -907,7 +991,7 @@ namespace transport
|
||||
i2p::transport::transports.UpdateReceivedBytes (bytes_transferred + 2);
|
||||
uint8_t nonce[12];
|
||||
CreateNonce (m_ReceiveSequenceNumber, nonce); m_ReceiveSequenceNumber++;
|
||||
if (i2p::crypto::AEADChaCha20Poly1305 (m_NextReceivedBuffer, m_NextReceivedLen-16, nullptr, 0, m_ReceiveKey, nonce, m_NextReceivedBuffer, m_NextReceivedLen, false))
|
||||
if (m_Server.AEADChaCha20Poly1305Decrypt (m_NextReceivedBuffer, m_NextReceivedLen-16, nullptr, 0, m_ReceiveKey, nonce, m_NextReceivedBuffer, m_NextReceivedLen))
|
||||
{
|
||||
LogPrint (eLogDebug, "NTCP2: Received message decrypted");
|
||||
ProcessNextFrame (m_NextReceivedBuffer, m_NextReceivedLen-16);
|
||||
@@ -1096,7 +1180,7 @@ namespace transport
|
||||
}
|
||||
uint8_t nonce[12];
|
||||
CreateNonce (m_SendSequenceNumber, nonce); m_SendSequenceNumber++;
|
||||
i2p::crypto::AEADChaCha20Poly1305Encrypt (encryptBufs, m_SendKey, nonce, macBuf); // encrypt buffers
|
||||
m_Server.AEADChaCha20Poly1305Encrypt (encryptBufs, m_SendKey, nonce, macBuf); // encrypt buffers
|
||||
SetNextSentFrameLength (totalLen + 16, first->GetNTCP2Header () - 5); // frame length right before first block
|
||||
|
||||
// send buffers
|
||||
@@ -1127,7 +1211,7 @@ namespace transport
|
||||
// encrypt
|
||||
uint8_t nonce[12];
|
||||
CreateNonce (m_SendSequenceNumber, nonce); m_SendSequenceNumber++;
|
||||
i2p::crypto::AEADChaCha20Poly1305Encrypt ({ {m_NextSendBuffer + 2, payloadLen} }, m_SendKey, nonce, m_NextSendBuffer + payloadLen + 2);
|
||||
m_Server.AEADChaCha20Poly1305Encrypt ({ {m_NextSendBuffer + 2, payloadLen} }, m_SendKey, nonce, m_NextSendBuffer + payloadLen + 2);
|
||||
SetNextSentFrameLength (payloadLen + 16, m_NextSendBuffer);
|
||||
// send
|
||||
m_IsSending = true;
|
||||
@@ -1207,7 +1291,7 @@ namespace transport
|
||||
void NTCP2Session::MoveSendQueue (std::shared_ptr<NTCP2Session> other)
|
||||
{
|
||||
if (!other || m_SendQueue.empty ()) return;
|
||||
std::vector<std::shared_ptr<I2NPMessage> > msgs;
|
||||
std::list<std::shared_ptr<I2NPMessage> > msgs;
|
||||
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
|
||||
for (auto it: m_SendQueue)
|
||||
if (!it->IsExpired (ts))
|
||||
@@ -1216,7 +1300,7 @@ namespace transport
|
||||
it->Drop ();
|
||||
m_SendQueue.clear ();
|
||||
if (!msgs.empty ())
|
||||
other->PostI2NPMessages (msgs);
|
||||
other->SendI2NPMessages (msgs);
|
||||
}
|
||||
|
||||
size_t NTCP2Session::CreatePaddingBlock (size_t msgLen, uint8_t * buf, size_t len)
|
||||
@@ -1294,23 +1378,45 @@ namespace transport
|
||||
void NTCP2Session::SendTerminationAndTerminate (NTCP2TerminationReason reason)
|
||||
{
|
||||
SendTermination (reason);
|
||||
m_Server.GetService ().post (std::bind (&NTCP2Session::Terminate, shared_from_this ())); // let termination message go
|
||||
boost::asio::post (m_Server.GetService (), std::bind (&NTCP2Session::Terminate, shared_from_this ())); // let termination message go
|
||||
}
|
||||
|
||||
void NTCP2Session::SendI2NPMessages (const std::vector<std::shared_ptr<I2NPMessage> >& msgs)
|
||||
void NTCP2Session::SendI2NPMessages (std::list<std::shared_ptr<I2NPMessage> >& msgs)
|
||||
{
|
||||
m_Server.GetService ().post (std::bind (&NTCP2Session::PostI2NPMessages, shared_from_this (), msgs));
|
||||
if (m_IsTerminated || msgs.empty ())
|
||||
{
|
||||
msgs.clear ();
|
||||
return;
|
||||
}
|
||||
bool empty = false;
|
||||
{
|
||||
std::lock_guard<std::mutex> l(m_IntermediateQueueMutex);
|
||||
empty = m_IntermediateQueue.empty ();
|
||||
m_IntermediateQueue.splice (m_IntermediateQueue.end (), msgs);
|
||||
}
|
||||
if (empty)
|
||||
boost::asio::post (m_Server.GetService (), std::bind (&NTCP2Session::PostI2NPMessages, shared_from_this ()));
|
||||
}
|
||||
|
||||
void NTCP2Session::PostI2NPMessages (std::vector<std::shared_ptr<I2NPMessage> > msgs)
|
||||
void NTCP2Session::PostI2NPMessages ()
|
||||
{
|
||||
if (m_IsTerminated) return;
|
||||
std::list<std::shared_ptr<I2NPMessage> > msgs;
|
||||
{
|
||||
std::lock_guard<std::mutex> l(m_IntermediateQueueMutex);
|
||||
m_IntermediateQueue.swap (msgs);
|
||||
}
|
||||
bool isSemiFull = m_SendQueue.size () > NTCP2_MAX_OUTGOING_QUEUE_SIZE/2;
|
||||
for (auto it: msgs)
|
||||
if (isSemiFull && it->onDrop)
|
||||
it->Drop (); // drop earlier because we can handle it
|
||||
else
|
||||
m_SendQueue.push_back (std::move (it));
|
||||
if (isSemiFull)
|
||||
{
|
||||
for (auto it: msgs)
|
||||
if (it->onDrop)
|
||||
it->Drop (); // drop earlier because we can handle it
|
||||
else
|
||||
m_SendQueue.push_back (std::move (it));
|
||||
}
|
||||
else
|
||||
m_SendQueue.splice (m_SendQueue.end (), msgs);
|
||||
|
||||
if (!m_IsSending && m_IsEstablished)
|
||||
SendQueue ();
|
||||
@@ -1326,7 +1432,7 @@ namespace transport
|
||||
void NTCP2Session::SendLocalRouterInfo (bool update)
|
||||
{
|
||||
if (update || !IsOutgoing ()) // we send it in SessionConfirmed for outgoing session
|
||||
m_Server.GetService ().post (std::bind (&NTCP2Session::SendRouterInfo, shared_from_this ()));
|
||||
boost::asio::post (m_Server.GetService (), std::bind (&NTCP2Session::SendRouterInfo, shared_from_this ()));
|
||||
}
|
||||
|
||||
NTCP2Server::NTCP2Server ():
|
||||
@@ -1343,6 +1449,7 @@ namespace transport
|
||||
|
||||
void NTCP2Server::Start ()
|
||||
{
|
||||
m_EstablisherService.Start ();
|
||||
if (!IsRunning ())
|
||||
{
|
||||
StartIOService ();
|
||||
@@ -1350,14 +1457,13 @@ namespace transport
|
||||
{
|
||||
LogPrint(eLogInfo, "NTCP2: Using proxy to connect to peers");
|
||||
// TODO: resolve proxy until it is resolved
|
||||
boost::asio::ip::tcp::resolver::query q(m_ProxyAddress, std::to_string(m_ProxyPort));
|
||||
boost::system::error_code e;
|
||||
auto itr = m_Resolver.resolve(q, e);
|
||||
auto itr = m_Resolver.resolve(m_ProxyAddress, std::to_string(m_ProxyPort), e);
|
||||
if(e)
|
||||
LogPrint(eLogCritical, "NTCP2: Failed to resolve proxy ", e.message());
|
||||
else
|
||||
{
|
||||
m_ProxyEndpoint.reset (new boost::asio::ip::tcp::endpoint(*itr));
|
||||
m_ProxyEndpoint.reset (new boost::asio::ip::tcp::endpoint(*itr.begin ()));
|
||||
if (m_ProxyEndpoint)
|
||||
LogPrint(eLogDebug, "NTCP2: m_ProxyEndpoint ", *m_ProxyEndpoint);
|
||||
}
|
||||
@@ -1453,6 +1559,7 @@ namespace transport
|
||||
m_TerminationTimer.cancel ();
|
||||
m_ProxyEndpoint = nullptr;
|
||||
}
|
||||
m_EstablisherService.Stop ();
|
||||
StopIOService ();
|
||||
}
|
||||
|
||||
@@ -1517,7 +1624,7 @@ namespace transport
|
||||
}
|
||||
LogPrint (eLogDebug, "NTCP2: Connecting to ", conn->GetRemoteEndpoint (),
|
||||
" (", i2p::data::GetIdentHashAbbreviation (conn->GetRemoteIdentity ()->GetIdentHash ()), ")");
|
||||
GetService ().post([this, conn]()
|
||||
boost::asio::post (GetService (), [this, conn]()
|
||||
{
|
||||
if (this->AddNTCP2Session (conn))
|
||||
{
|
||||
@@ -1738,7 +1845,7 @@ namespace transport
|
||||
LogPrint (eLogError, "NTCP2: Can't connect to unspecified address");
|
||||
return;
|
||||
}
|
||||
GetService().post([this, conn]()
|
||||
boost::asio::post (GetService(), [this, conn]()
|
||||
{
|
||||
if (this->AddNTCP2Session (conn))
|
||||
{
|
||||
@@ -1822,7 +1929,7 @@ namespace transport
|
||||
LogPrint(eLogError, "NTCP2: HTTP proxy write error ", ec.message());
|
||||
});
|
||||
|
||||
boost::asio::streambuf * readbuff = new boost::asio::streambuf;
|
||||
auto readbuff = std::make_shared<boost::asio::streambuf>();
|
||||
boost::asio::async_read_until(conn->GetSocket(), *readbuff, "\r\n\r\n",
|
||||
[readbuff, timer, conn] (const boost::system::error_code & ec, std::size_t transferred)
|
||||
{
|
||||
@@ -1836,13 +1943,12 @@ namespace transport
|
||||
{
|
||||
readbuff->commit(transferred);
|
||||
i2p::http::HTTPRes res;
|
||||
if(res.parse(boost::asio::buffer_cast<const char*>(readbuff->data()), readbuff->size()) > 0)
|
||||
if(res.parse(std::string {boost::asio::buffers_begin(readbuff->data ()), boost::asio::buffers_begin(readbuff->data ()) + readbuff->size ()}) > 0)
|
||||
{
|
||||
if(res.code == 200)
|
||||
{
|
||||
timer->cancel();
|
||||
conn->ClientLogin();
|
||||
delete readbuff;
|
||||
return;
|
||||
}
|
||||
else
|
||||
@@ -1852,7 +1958,6 @@ namespace transport
|
||||
LogPrint(eLogError, "NTCP2: HTTP proxy gave malformed response");
|
||||
timer->cancel();
|
||||
conn->Terminate();
|
||||
delete readbuff;
|
||||
}
|
||||
});
|
||||
break;
|
||||
@@ -1875,5 +1980,17 @@ namespace transport
|
||||
else
|
||||
m_Address4 = addr;
|
||||
}
|
||||
|
||||
void NTCP2Server::AEADChaCha20Poly1305Encrypt (const std::vector<std::pair<uint8_t *, size_t> >& bufs,
|
||||
const uint8_t * key, const uint8_t * nonce, uint8_t * mac)
|
||||
{
|
||||
return m_Encryptor.Encrypt (bufs, key, nonce, mac);
|
||||
}
|
||||
|
||||
bool NTCP2Server::AEADChaCha20Poly1305Decrypt (const uint8_t * msg, size_t msgLen,
|
||||
const uint8_t * ad, size_t adLen, const uint8_t * key, const uint8_t * nonce, uint8_t * buf, size_t len)
|
||||
{
|
||||
return m_Decryptor.Decrypt (msg, msgLen, ad, adLen, key, nonce, buf, len);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,21 +95,21 @@ namespace transport
|
||||
const uint8_t * GetCK () const { return m_CK; };
|
||||
const uint8_t * GetH () const { return m_H; };
|
||||
|
||||
void KDF1Alice ();
|
||||
void KDF1Bob ();
|
||||
void KDF2Alice ();
|
||||
void KDF2Bob ();
|
||||
void KDF3Alice (); // for SessionConfirmed part 2
|
||||
void KDF3Bob ();
|
||||
bool KDF1Alice ();
|
||||
bool KDF1Bob ();
|
||||
bool KDF2Alice ();
|
||||
bool KDF2Bob ();
|
||||
bool KDF3Alice (); // for SessionConfirmed part 2
|
||||
bool KDF3Bob ();
|
||||
|
||||
void KeyDerivationFunction1 (const uint8_t * pub, i2p::crypto::X25519Keys& priv, const uint8_t * rs, const uint8_t * epub); // for SessionRequest, (pub, priv) for DH
|
||||
void KeyDerivationFunction2 (const uint8_t * sessionRequest, size_t sessionRequestLen, const uint8_t * epub); // for SessionCreate
|
||||
bool KeyDerivationFunction1 (const uint8_t * pub, i2p::crypto::X25519Keys& priv, const uint8_t * rs, const uint8_t * epub); // for SessionRequest, (pub, priv) for DH
|
||||
bool KeyDerivationFunction2 (const uint8_t * sessionRequest, size_t sessionRequestLen, const uint8_t * epub); // for SessionCreate
|
||||
void CreateEphemeralKey ();
|
||||
|
||||
void CreateSessionRequestMessage (std::mt19937& rng);
|
||||
void CreateSessionCreatedMessage (std::mt19937& rng);
|
||||
bool CreateSessionRequestMessage (std::mt19937& rng);
|
||||
bool CreateSessionCreatedMessage (std::mt19937& rng);
|
||||
void CreateSessionConfirmedMessagePart1 (const uint8_t * nonce);
|
||||
void CreateSessionConfirmedMessagePart2 (const uint8_t * nonce);
|
||||
bool CreateSessionConfirmedMessagePart2 (const uint8_t * nonce);
|
||||
|
||||
bool ProcessSessionRequestMessage (uint16_t& paddingLen, bool& clockSkew);
|
||||
bool ProcessSessionCreatedMessage (uint16_t& paddingLen);
|
||||
@@ -153,7 +153,7 @@ namespace transport
|
||||
void ServerLogin (); // Bob
|
||||
|
||||
void SendLocalRouterInfo (bool update) override; // after handshake or by update
|
||||
void SendI2NPMessages (const std::vector<std::shared_ptr<I2NPMessage> >& msgs) override;
|
||||
void SendI2NPMessages (std::list<std::shared_ptr<I2NPMessage> >& msgs) override;
|
||||
void MoveSendQueue (std::shared_ptr<NTCP2Session> other);
|
||||
|
||||
private:
|
||||
@@ -172,13 +172,17 @@ namespace transport
|
||||
|
||||
void HandleSessionRequestSent (const boost::system::error_code& ecode, std::size_t bytes_transferred);
|
||||
void HandleSessionRequestReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred);
|
||||
void ProcessSessionRequest (size_t len);
|
||||
void HandleSessionRequestPaddingReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred);
|
||||
void HandleSessionCreatedSent (const boost::system::error_code& ecode, std::size_t bytes_transferred);
|
||||
void HandleSessionCreatedReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred);
|
||||
void ProcessSessionCreated (size_t len);
|
||||
void HandleSessionCreatedPaddingReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred);
|
||||
void HandleSessionConfirmedSent (const boost::system::error_code& ecode, std::size_t bytes_transferred);
|
||||
void HandleSessionConfirmedReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred);
|
||||
|
||||
void ProcessSessionConfirmed ();
|
||||
void EstablishSessionAfterSessionConfirmed (std::shared_ptr<std::vector<uint8_t> > buf, size_t size);
|
||||
|
||||
// data
|
||||
void ReceiveLength ();
|
||||
void HandleReceivedLength (const boost::system::error_code& ecode, std::size_t bytes_transferred);
|
||||
@@ -196,7 +200,7 @@ namespace transport
|
||||
void SendRouterInfo ();
|
||||
void SendTermination (NTCP2TerminationReason reason);
|
||||
void SendTerminationAndTerminate (NTCP2TerminationReason reason);
|
||||
void PostI2NPMessages (std::vector<std::shared_ptr<I2NPMessage> > msgs);
|
||||
void PostI2NPMessages ();
|
||||
|
||||
private:
|
||||
|
||||
@@ -229,13 +233,28 @@ namespace transport
|
||||
bool m_IsSending, m_IsReceiving;
|
||||
std::list<std::shared_ptr<I2NPMessage> > m_SendQueue;
|
||||
uint64_t m_NextRouterInfoResendTime; // seconds since epoch
|
||||
|
||||
|
||||
std::list<std::shared_ptr<I2NPMessage> > m_IntermediateQueue; // from transports
|
||||
mutable std::mutex m_IntermediateQueueMutex;
|
||||
|
||||
uint16_t m_PaddingSizes[16];
|
||||
int m_NextPaddingSize;
|
||||
};
|
||||
|
||||
class NTCP2Server: private i2p::util::RunnableServiceWithWork
|
||||
{
|
||||
private:
|
||||
|
||||
class EstablisherService: public i2p::util::RunnableServiceWithWork
|
||||
{
|
||||
public:
|
||||
|
||||
EstablisherService (): RunnableServiceWithWork ("NTCP2e") {};
|
||||
auto& GetService () { return GetIOService (); };
|
||||
void Start () { StartIOService (); };
|
||||
void Stop () { StopIOService (); };
|
||||
};
|
||||
|
||||
public:
|
||||
|
||||
enum ProxyType
|
||||
@@ -244,14 +263,20 @@ namespace transport
|
||||
eSocksProxy,
|
||||
eHTTPProxy
|
||||
};
|
||||
|
||||
|
||||
NTCP2Server ();
|
||||
~NTCP2Server ();
|
||||
|
||||
void Start ();
|
||||
void Stop ();
|
||||
boost::asio::io_service& GetService () { return GetIOService (); };
|
||||
auto& GetService () { return GetIOService (); };
|
||||
auto& GetEstablisherService () { return m_EstablisherService.GetService (); };
|
||||
std::mt19937& GetRng () { return m_Rng; };
|
||||
void AEADChaCha20Poly1305Encrypt (const std::vector<std::pair<uint8_t *, size_t> >& bufs,
|
||||
const uint8_t * key, const uint8_t * nonce, uint8_t * mac);
|
||||
bool AEADChaCha20Poly1305Decrypt (const uint8_t * msg, size_t msgLen, const uint8_t * ad, size_t adLen,
|
||||
const uint8_t * key, const uint8_t * nonce, uint8_t * buf, size_t len);
|
||||
|
||||
|
||||
bool AddNTCP2Session (std::shared_ptr<NTCP2Session> session, bool incoming = false);
|
||||
void RemoveNTCP2Session (std::shared_ptr<NTCP2Session> session);
|
||||
@@ -289,9 +314,13 @@ namespace transport
|
||||
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;
|
||||
std::mt19937 m_Rng;
|
||||
|
||||
EstablisherService m_EstablisherService;
|
||||
i2p::crypto::AEADChaCha20Poly1305Encryptor m_Encryptor;
|
||||
i2p::crypto::AEADChaCha20Poly1305Decryptor m_Decryptor;
|
||||
|
||||
public:
|
||||
|
||||
// for HTTP/I2PControl
|
||||
|
||||
@@ -122,16 +122,18 @@ namespace data
|
||||
uint64_t lastProfilesCleanup = i2p::util::GetMonotonicMilliseconds (), lastObsoleteProfilesCleanup = lastProfilesCleanup;
|
||||
int16_t profilesCleanupVariance = 0, obsoleteProfilesCleanVariance = 0;
|
||||
|
||||
std::list<std::shared_ptr<const I2NPMessage> > msgs;
|
||||
while (m_IsRunning)
|
||||
{
|
||||
try
|
||||
{
|
||||
auto msg = m_Queue.GetNextWithTimeout (1000); // 1 sec
|
||||
if (msg)
|
||||
if (m_Queue.Wait (1,0)) // 1 sec
|
||||
{
|
||||
int numMsgs = 0;
|
||||
while (msg)
|
||||
m_Queue.GetWholeQueue (msgs);
|
||||
while (!msgs.empty ())
|
||||
{
|
||||
auto msg = msgs.front (); msgs.pop_front ();
|
||||
if (!msg) continue;
|
||||
LogPrint(eLogDebug, "NetDb: Got request with type ", (int) msg->GetTypeID ());
|
||||
switch (msg->GetTypeID ())
|
||||
{
|
||||
@@ -145,9 +147,6 @@ namespace data
|
||||
LogPrint (eLogError, "NetDb: Unexpected message type ", (int) msg->GetTypeID ());
|
||||
//i2p::HandleI2NPMessage (msg);
|
||||
}
|
||||
if (numMsgs > 100) break;
|
||||
msg = m_Queue.Get ();
|
||||
numMsgs++;
|
||||
}
|
||||
}
|
||||
if (!m_IsRunning) break;
|
||||
@@ -385,8 +384,7 @@ namespace data
|
||||
if (it == m_LeaseSets.end () || it->second->GetStoreType () != storeType ||
|
||||
leaseSet->GetPublishedTimestamp () > it->second->GetPublishedTimestamp ())
|
||||
{
|
||||
if (leaseSet->IsPublic () && !leaseSet->IsExpired () &&
|
||||
i2p::util::GetSecondsSinceEpoch () + NETDB_EXPIRATION_TIMEOUT_THRESHOLD > leaseSet->GetPublishedTimestamp ())
|
||||
if (leaseSet->IsPublic () && !leaseSet->IsExpired ())
|
||||
{
|
||||
// TODO: implement actual update
|
||||
if (CheckLogLevel (eLogInfo))
|
||||
@@ -481,7 +479,7 @@ namespace data
|
||||
void NetDb::ReseedFromFloodfill(const RouterInfo & ri, int numRouters, int numFloodfills)
|
||||
{
|
||||
LogPrint(eLogInfo, "NetDB: Reseeding from floodfill ", ri.GetIdentHashBase64());
|
||||
std::vector<std::shared_ptr<i2p::I2NPMessage> > requests;
|
||||
std::list<std::shared_ptr<i2p::I2NPMessage> > requests;
|
||||
|
||||
i2p::data::IdentHash ourIdent = i2p::context.GetIdentHash();
|
||||
i2p::data::IdentHash ih = ri.GetIdentHash();
|
||||
@@ -639,70 +637,74 @@ namespace data
|
||||
if (checkForExpiration && uptime > i2p::transport::SSU2_TO_INTRODUCER_SESSION_DURATION) // 1 hour
|
||||
expirationTimeout = i2p::context.IsFloodfill () ? NETDB_FLOODFILL_EXPIRATION_TIMEOUT*1000LL :
|
||||
NETDB_MIN_EXPIRATION_TIMEOUT*1000LL + (NETDB_MAX_EXPIRATION_TIMEOUT - NETDB_MIN_EXPIRATION_TIMEOUT)*1000LL*NETDB_MIN_ROUTERS/total;
|
||||
|
||||
bool isOffline = checkForExpiration && i2p::transport::transports.GetNumPeers () < NETDB_MIN_TRANSPORTS; // enough routers and uptime, but no transports
|
||||
|
||||
std::list<std::pair<std::string, std::shared_ptr<RouterInfo::Buffer> > > saveToDisk;
|
||||
std::list<std::string> removeFromDisk;
|
||||
|
||||
auto own = i2p::context.GetSharedRouterInfo ();
|
||||
for (auto& it: m_RouterInfos)
|
||||
for (auto [ident, r]: m_RouterInfos)
|
||||
{
|
||||
if (!it.second || it.second == own) continue; // skip own
|
||||
std::string ident = it.second->GetIdentHashBase64();
|
||||
if (it.second->IsUpdated ())
|
||||
if (!r || r == own) continue; // skip own
|
||||
if (r->IsBufferScheduledToDelete ()) // from previous SaveUpdated, we assume m_PersistingRouters complete
|
||||
{
|
||||
if (it.second->GetBuffer ())
|
||||
std::lock_guard<std::mutex> l(m_RouterInfosMutex); // possible collision between DeleteBuffer and Update
|
||||
r->DeleteBuffer ();
|
||||
}
|
||||
if (r->IsUpdated ())
|
||||
{
|
||||
if (r->GetBuffer () && !r->IsUnreachable ())
|
||||
{
|
||||
// we have something to save
|
||||
std::shared_ptr<RouterInfo::Buffer> buffer;
|
||||
{
|
||||
std::lock_guard<std::mutex> l(m_RouterInfosMutex); // possible collision between DeleteBuffer and Update
|
||||
buffer = it.second->GetSharedBuffer ();
|
||||
it.second->DeleteBuffer ();
|
||||
buffer = r->CopyBuffer ();
|
||||
r->ScheduleBufferToDelete ();
|
||||
}
|
||||
if (buffer && !it.second->IsUnreachable ()) // don't save bad router
|
||||
saveToDisk.push_back(std::make_pair(ident, buffer));
|
||||
it.second->SetUnreachable (false);
|
||||
if (buffer)
|
||||
saveToDisk.push_back(std::make_pair(ident.ToBase64 (), buffer));
|
||||
}
|
||||
it.second->SetUpdated (false);
|
||||
r->SetUpdated (false);
|
||||
updatedCount++;
|
||||
continue;
|
||||
}
|
||||
if (it.second->GetProfile ()->IsUnreachable ())
|
||||
it.second->SetUnreachable (true);
|
||||
if (r->GetProfile ()->IsUnreachable ())
|
||||
r->SetUnreachable (true);
|
||||
// make router reachable back if too few routers or floodfills
|
||||
if (it.second->IsUnreachable () && (total - deletedCount < NETDB_MIN_ROUTERS || isLowRate ||
|
||||
(it.second->IsFloodfill () && totalFloodfills - deletedFloodfillsCount < NETDB_MIN_FLOODFILLS)))
|
||||
it.second->SetUnreachable (false);
|
||||
if (!it.second->IsUnreachable ())
|
||||
if (r->IsUnreachable () && (total - deletedCount < NETDB_MIN_ROUTERS || isLowRate || isOffline ||
|
||||
(r->IsFloodfill () && totalFloodfills - deletedFloodfillsCount < NETDB_MIN_FLOODFILLS)))
|
||||
r->SetUnreachable (false);
|
||||
if (!r->IsUnreachable ())
|
||||
{
|
||||
// find & mark expired routers
|
||||
if (!it.second->GetCompatibleTransports (true)) // non reachable by any transport
|
||||
it.second->SetUnreachable (true);
|
||||
else if (ts + NETDB_EXPIRATION_TIMEOUT_THRESHOLD*1000LL < it.second->GetTimestamp ())
|
||||
if (!r->GetCompatibleTransports (true)) // non reachable by any transport
|
||||
r->SetUnreachable (true);
|
||||
else if (ts + NETDB_EXPIRATION_TIMEOUT_THRESHOLD*1000LL < r->GetTimestamp ())
|
||||
{
|
||||
LogPrint (eLogWarning, "NetDb: RouterInfo is from future for ", (it.second->GetTimestamp () - ts)/1000LL, " seconds");
|
||||
it.second->SetUnreachable (true);
|
||||
LogPrint (eLogWarning, "NetDb: RouterInfo is from future for ", (r->GetTimestamp () - ts)/1000LL, " seconds");
|
||||
r->SetUnreachable (true);
|
||||
}
|
||||
else if (checkForExpiration)
|
||||
{
|
||||
if (ts > it.second->GetTimestamp () + expirationTimeout)
|
||||
it.second->SetUnreachable (true);
|
||||
else if ((ts > it.second->GetTimestamp () + expirationTimeout/2) && // more than half of expiration
|
||||
total > NETDB_NUM_ROUTERS_THRESHOLD && !it.second->IsHighBandwidth() && // low bandwidth
|
||||
!it.second->IsFloodfill() && (!i2p::context.IsFloodfill () || // non floodfill
|
||||
(CreateRoutingKey (it.second->GetIdentHash ()) ^ i2p::context.GetIdentHash ()).metric[0] >= 0x02)) // different first 7 bits
|
||||
it.second->SetUnreachable (true);
|
||||
if (ts > r->GetTimestamp () + expirationTimeout)
|
||||
r->SetUnreachable (true);
|
||||
else if ((ts > r->GetTimestamp () + expirationTimeout/2) && // more than half of expiration
|
||||
total > NETDB_NUM_ROUTERS_THRESHOLD && !r->IsHighBandwidth() && // low bandwidth
|
||||
!r->IsFloodfill() && (!i2p::context.IsFloodfill () || // non floodfill
|
||||
(CreateRoutingKey (ident) ^ i2p::context.GetIdentHash ()).metric[0] >= 0x02)) // different first 7 bits
|
||||
r->SetUnreachable (true);
|
||||
}
|
||||
}
|
||||
// make router reachable back if connected now
|
||||
if (it.second->IsUnreachable () && i2p::transport::transports.IsConnected (it.second->GetIdentHash ()))
|
||||
it.second->SetUnreachable (false);
|
||||
if (r->IsUnreachable () && i2p::transport::transports.IsConnected (ident))
|
||||
r->SetUnreachable (false);
|
||||
|
||||
if (it.second->IsUnreachable ())
|
||||
if (r->IsUnreachable ())
|
||||
{
|
||||
if (it.second->IsFloodfill ()) deletedFloodfillsCount++;
|
||||
if (r->IsFloodfill ()) deletedFloodfillsCount++;
|
||||
// delete RI file
|
||||
removeFromDisk.push_back (ident);
|
||||
removeFromDisk.push_back (ident.ToBase64());
|
||||
deletedCount++;
|
||||
if (total - deletedCount < NETDB_MIN_ROUTERS) checkForExpiration = false;
|
||||
}
|
||||
|
||||
@@ -39,6 +39,7 @@ namespace data
|
||||
{
|
||||
const int NETDB_MIN_ROUTERS = 90;
|
||||
const int NETDB_MIN_FLOODFILLS = 5;
|
||||
const int NETDB_MIN_TRANSPORTS = 10 ; // otherwise assume offline
|
||||
const int NETDB_NUM_FLOODFILLS_THRESHOLD = 1200;
|
||||
const int NETDB_NUM_ROUTERS_THRESHOLD = 4*NETDB_NUM_FLOODFILLS_THRESHOLD;
|
||||
const int NETDB_TUNNEL_CREATION_RATE_THRESHOLD = 10; // in %
|
||||
|
||||
@@ -179,7 +179,7 @@ namespace data
|
||||
|
||||
void NetDbRequests::RequestComplete (const IdentHash& ident, std::shared_ptr<RouterInfo> r)
|
||||
{
|
||||
GetIOService ().post ([this, ident, r]()
|
||||
boost::asio::post (GetIOService (), [this, ident, r]()
|
||||
{
|
||||
std::shared_ptr<RequestedDestination> request;
|
||||
auto it = m_RequestedDestinations.find (ident);
|
||||
@@ -267,7 +267,7 @@ namespace data
|
||||
{
|
||||
if (dest->IsActive ())
|
||||
{
|
||||
s->GetIOService ().post ([s, dest]()
|
||||
boost::asio::post (s->GetIOService (), [s, dest]()
|
||||
{
|
||||
if (dest->IsActive ()) s->SendNextRequest (dest);
|
||||
});
|
||||
@@ -345,7 +345,7 @@ namespace data
|
||||
|
||||
void NetDbRequests::PostDatabaseSearchReplyMsg (std::shared_ptr<const I2NPMessage> msg)
|
||||
{
|
||||
GetIOService ().post ([this, msg]()
|
||||
boost::asio::post (GetIOService (), [this, msg]()
|
||||
{
|
||||
HandleDatabaseSearchReplyMsg (msg);
|
||||
});
|
||||
@@ -431,7 +431,7 @@ namespace data
|
||||
void NetDbRequests::PostRequestDestination (const IdentHash& destination,
|
||||
const RequestedDestination::RequestComplete& requestComplete, bool direct)
|
||||
{
|
||||
GetIOService ().post ([this, destination, requestComplete, direct]()
|
||||
boost::asio::post (GetIOService (), [this, destination, requestComplete, direct]()
|
||||
{
|
||||
RequestDestination (destination, requestComplete, direct);
|
||||
});
|
||||
|
||||
@@ -206,10 +206,9 @@ namespace data
|
||||
return m_NumTunnelsNonReplied > 10*(total + 1);
|
||||
}
|
||||
|
||||
bool RouterProfile::IsDeclinedRecently ()
|
||||
bool RouterProfile::IsDeclinedRecently (uint64_t ts)
|
||||
{
|
||||
if (!m_LastDeclineTime) return false;
|
||||
auto ts = i2p::util::GetSecondsSinceEpoch ();
|
||||
if (ts > m_LastDeclineTime + PEER_PROFILE_DECLINED_RECENTLY_INTERVAL ||
|
||||
ts + PEER_PROFILE_DECLINED_RECENTLY_INTERVAL < m_LastDeclineTime)
|
||||
m_LastDeclineTime = 0;
|
||||
@@ -218,7 +217,10 @@ namespace data
|
||||
|
||||
bool RouterProfile::IsBad ()
|
||||
{
|
||||
if (IsDeclinedRecently () || IsUnreachable () || m_IsDuplicated) return true;
|
||||
if (IsUnreachable () || m_IsDuplicated) return true;
|
||||
auto ts = i2p::util::GetSecondsSinceEpoch ();
|
||||
if (ts > PEER_PROFILE_MAX_DECLINED_INTERVAL + m_LastDeclineTime) return false;
|
||||
if (IsDeclinedRecently (ts)) return true;
|
||||
auto isBad = IsAlwaysDeclining () || IsLowPartcipationRate () /*|| IsLowReplyRate ()*/;
|
||||
if (isBad && m_NumTimesRejected > 10*(m_NumTimesTaken + 1))
|
||||
{
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
|
||||
#include <memory>
|
||||
#include <future>
|
||||
#include <boost/asio.hpp>
|
||||
#include "Identity.h"
|
||||
|
||||
namespace i2p
|
||||
@@ -37,11 +38,13 @@ namespace data
|
||||
const int PEER_PROFILE_AUTOCLEAN_VARIANCE = 900; // in seconds (15 minutes)
|
||||
const int PEER_PROFILE_OBSOLETE_PROFILES_CLEAN_TIMEOUT = 5400; // in seconds (1.5 hours)
|
||||
const int PEER_PROFILE_OBSOLETE_PROFILES_CLEAN_VARIANCE = 2400; // in seconds (40 minutes)
|
||||
const int PEER_PROFILE_DECLINED_RECENTLY_INTERVAL = 150; // in seconds (2.5 minutes)
|
||||
const int PEER_PROFILE_DECLINED_RECENTLY_INTERVAL = 330; // in seconds (5.5 minutes)
|
||||
const int PEER_PROFILE_MAX_DECLINED_INTERVAL = 4400; // in second (1.5 hours)
|
||||
const int PEER_PROFILE_PERSIST_INTERVAL = 3300; // in seconds (55 minutes)
|
||||
const int PEER_PROFILE_UNREACHABLE_INTERVAL = 480; // in seconds (8 minutes)
|
||||
const int PEER_PROFILE_USEFUL_THRESHOLD = 3;
|
||||
|
||||
const int PEER_PROFILE_ALWAYS_DECLINING_NUM = 5; // num declines in row to consider always declined
|
||||
|
||||
class RouterProfile
|
||||
{
|
||||
public:
|
||||
@@ -67,6 +70,11 @@ namespace data
|
||||
|
||||
bool IsUseful() const;
|
||||
bool IsDuplicated () const { return m_IsDuplicated; };
|
||||
|
||||
const boost::asio::ip::udp::endpoint& GetLastEndpoint () const { return m_LastEndpoint; }
|
||||
void SetLastEndpoint (const boost::asio::ip::udp::endpoint& ep) { m_LastEndpoint = ep; }
|
||||
bool HasLastEndpoint (bool v4) const { return !m_LastEndpoint.address ().is_unspecified () && m_LastEndpoint.port () &&
|
||||
((v4 && m_LastEndpoint.address ().is_v4 ()) || (!v4 && m_LastEndpoint.address ().is_v6 ())); }
|
||||
|
||||
private:
|
||||
|
||||
@@ -75,7 +83,7 @@ namespace data
|
||||
bool IsAlwaysDeclining () const { return !m_NumTunnelsAgreed && m_NumTunnelsDeclined >= 5; };
|
||||
bool IsLowPartcipationRate () const;
|
||||
bool IsLowReplyRate () const;
|
||||
bool IsDeclinedRecently ();
|
||||
bool IsDeclinedRecently (uint64_t ts);
|
||||
|
||||
private:
|
||||
|
||||
@@ -90,6 +98,8 @@ namespace data
|
||||
uint32_t m_NumTimesRejected;
|
||||
bool m_HasConnected; // successful trusted(incoming or NTCP2) connection
|
||||
bool m_IsDuplicated;
|
||||
// connectivity
|
||||
boost::asio::ip::udp::endpoint m_LastEndpoint; // SSU2 for non-published addresses
|
||||
};
|
||||
|
||||
std::shared_ptr<RouterProfile> GetRouterProfile (const IdentHash& identHash);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2020, The PurpleI2P Project
|
||||
* Copyright (c) 2013-2024, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
@@ -9,8 +9,7 @@
|
||||
#ifndef QUEUE_H__
|
||||
#define QUEUE_H__
|
||||
|
||||
#include <queue>
|
||||
#include <vector>
|
||||
#include <list>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
#include <condition_variable>
|
||||
@@ -29,22 +28,20 @@ namespace util
|
||||
void Put (Element e)
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_QueueMutex);
|
||||
m_Queue.push (std::move(e));
|
||||
m_Queue.push_back (std::move(e));
|
||||
m_NonEmpty.notify_one ();
|
||||
}
|
||||
|
||||
template<template<typename, typename...>class Container, typename... R>
|
||||
void Put (const Container<Element, R...>& vec)
|
||||
void Put (std::list<Element>& list)
|
||||
{
|
||||
if (!vec.empty ())
|
||||
if (!list.empty ())
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_QueueMutex);
|
||||
for (const auto& it: vec)
|
||||
m_Queue.push (std::move(it));
|
||||
m_Queue.splice (m_Queue.end (), list);
|
||||
m_NonEmpty.notify_one ();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Element GetNext ()
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_QueueMutex);
|
||||
@@ -87,7 +84,7 @@ namespace util
|
||||
return m_Queue.empty ();
|
||||
}
|
||||
|
||||
int GetSize ()
|
||||
int GetSize () const
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_QueueMutex);
|
||||
return m_Queue.size ();
|
||||
@@ -107,15 +104,28 @@ namespace util
|
||||
return GetNonThreadSafe (true);
|
||||
}
|
||||
|
||||
private:
|
||||
void GetWholeQueue (std::list<Element>& queue)
|
||||
{
|
||||
if (!queue.empty ())
|
||||
{
|
||||
std::list<Element> newQueue;
|
||||
queue.swap (newQueue);
|
||||
}
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_QueueMutex);
|
||||
m_Queue.swap (queue);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
Element GetNonThreadSafe (bool peek = false)
|
||||
{
|
||||
if (!m_Queue.empty ())
|
||||
{
|
||||
auto el = m_Queue.front ();
|
||||
if (!peek)
|
||||
m_Queue.pop ();
|
||||
m_Queue.pop_front ();
|
||||
return el;
|
||||
}
|
||||
return nullptr;
|
||||
@@ -123,8 +133,8 @@ namespace util
|
||||
|
||||
private:
|
||||
|
||||
std::queue<Element> m_Queue;
|
||||
std::mutex m_QueueMutex;
|
||||
std::list<Element> m_Queue;
|
||||
mutable std::mutex m_QueueMutex;
|
||||
std::condition_variable m_NonEmpty;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -552,7 +552,7 @@ namespace data
|
||||
if (!url.port)
|
||||
url.port = 443;
|
||||
|
||||
boost::asio::io_service service;
|
||||
boost::asio::io_context service;
|
||||
boost::system::error_code ecode;
|
||||
|
||||
boost::asio::ssl::context ctx(boost::asio::ssl::context::sslv23);
|
||||
@@ -562,11 +562,10 @@ namespace data
|
||||
if(proxyUrl.schema.size())
|
||||
{
|
||||
// proxy connection
|
||||
auto it = boost::asio::ip::tcp::resolver(service).resolve (
|
||||
boost::asio::ip::tcp::resolver::query (proxyUrl.host, std::to_string(proxyUrl.port)), ecode);
|
||||
auto it = boost::asio::ip::tcp::resolver(service).resolve (proxyUrl.host, std::to_string(proxyUrl.port), ecode);
|
||||
if(!ecode)
|
||||
{
|
||||
s.lowest_layer().connect(*it, ecode);
|
||||
s.lowest_layer().connect(*it.begin (), ecode);
|
||||
if(!ecode)
|
||||
{
|
||||
auto & sock = s.next_layer();
|
||||
@@ -599,7 +598,7 @@ namespace data
|
||||
LogPrint(eLogError, "Reseed: HTTP CONNECT read error: ", ecode.message());
|
||||
return "";
|
||||
}
|
||||
if(proxyRes.parse(boost::asio::buffer_cast<const char *>(readbuf.data()), readbuf.size()) <= 0)
|
||||
if(proxyRes.parse(std::string {boost::asio::buffers_begin(readbuf.data ()), boost::asio::buffers_begin(readbuf.data ()) + readbuf.size ()}) <= 0)
|
||||
{
|
||||
sock.close();
|
||||
LogPrint(eLogError, "Reseed: HTTP CONNECT malformed reply");
|
||||
@@ -638,15 +637,13 @@ namespace data
|
||||
else
|
||||
{
|
||||
// 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);
|
||||
auto endpoints = boost::asio::ip::tcp::resolver(service).resolve (url.host, std::to_string(url.port), ecode);
|
||||
if (!ecode)
|
||||
{
|
||||
bool connected = false;
|
||||
boost::asio::ip::tcp::resolver::iterator end;
|
||||
while (it != end)
|
||||
for (const auto& it: endpoints)
|
||||
{
|
||||
boost::asio::ip::tcp::endpoint ep = *it;
|
||||
boost::asio::ip::tcp::endpoint ep = it;
|
||||
bool supported = false;
|
||||
if (!ep.address ().is_unspecified ())
|
||||
{
|
||||
@@ -666,7 +663,6 @@ namespace data
|
||||
break;
|
||||
}
|
||||
}
|
||||
it++;
|
||||
}
|
||||
if (!connected)
|
||||
{
|
||||
@@ -746,19 +742,16 @@ namespace data
|
||||
if (!url.port) url.port = 80;
|
||||
|
||||
boost::system::error_code ecode;
|
||||
boost::asio::io_service service;
|
||||
boost::asio::io_context service;
|
||||
boost::asio::ip::tcp::socket s(service, boost::asio::ip::tcp::v6());
|
||||
|
||||
auto it = boost::asio::ip::tcp::resolver(service).resolve (
|
||||
boost::asio::ip::tcp::resolver::query (url.host, std::to_string(url.port)), ecode);
|
||||
|
||||
auto endpoints = boost::asio::ip::tcp::resolver(service).resolve (url.host, std::to_string(url.port), ecode);
|
||||
if (!ecode)
|
||||
{
|
||||
bool connected = false;
|
||||
boost::asio::ip::tcp::resolver::iterator end;
|
||||
while (it != end)
|
||||
for (const auto& it: endpoints)
|
||||
{
|
||||
boost::asio::ip::tcp::endpoint ep = *it;
|
||||
boost::asio::ip::tcp::endpoint ep = it;
|
||||
if (
|
||||
i2p::util::net::IsYggdrasilAddress (ep.address ()) &&
|
||||
i2p::context.SupportsMesh ()
|
||||
@@ -772,7 +765,6 @@ namespace data
|
||||
break;
|
||||
}
|
||||
}
|
||||
it++;
|
||||
}
|
||||
if (!connected)
|
||||
{
|
||||
|
||||
@@ -140,7 +140,7 @@ namespace i2p
|
||||
{
|
||||
boost::asio::ip::address addr;
|
||||
if (!host.empty ())
|
||||
addr = boost::asio::ip::address::from_string (host);
|
||||
addr = boost::asio::ip::make_address (host);
|
||||
if (!addr.is_v4())
|
||||
addr = boost::asio::ip::address_v4 ();
|
||||
routerInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv, addr, ntcp2Port);
|
||||
@@ -161,7 +161,7 @@ namespace i2p
|
||||
{
|
||||
boost::asio::ip::address addr;
|
||||
if (!host.empty ())
|
||||
addr = boost::asio::ip::address::from_string (host);
|
||||
addr = boost::asio::ip::make_address (host);
|
||||
if (!addr.is_v4())
|
||||
addr = boost::asio::ip::address_v4 ();
|
||||
routerInfo.AddSSU2Address (m_SSU2Keys->staticPublicKey, m_SSU2Keys->intro, addr, ssu2Port);
|
||||
@@ -192,7 +192,7 @@ namespace i2p
|
||||
ntcp2Host = host;
|
||||
boost::asio::ip::address addr;
|
||||
if (!ntcp2Host.empty ())
|
||||
addr = boost::asio::ip::address::from_string (ntcp2Host);
|
||||
addr = boost::asio::ip::make_address (ntcp2Host);
|
||||
if (!addr.is_v6())
|
||||
addr = boost::asio::ip::address_v6 ();
|
||||
routerInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv, addr, ntcp2Port);
|
||||
@@ -211,7 +211,7 @@ namespace i2p
|
||||
{
|
||||
boost::asio::ip::address addr;
|
||||
if (!host.empty ())
|
||||
addr = boost::asio::ip::address::from_string (host);
|
||||
addr = boost::asio::ip::make_address (host);
|
||||
if (!addr.is_v6())
|
||||
addr = boost::asio::ip::address_v6 ();
|
||||
routerInfo.AddSSU2Address (m_SSU2Keys->staticPublicKey, m_SSU2Keys->intro, addr, ssu2Port);
|
||||
@@ -323,9 +323,11 @@ namespace i2p
|
||||
case eRouterStatusFirewalled:
|
||||
SetUnreachable (true, false); // ipv4
|
||||
break;
|
||||
case eRouterStatusMesh:
|
||||
m_RouterInfo.UpdateCaps (m_RouterInfo.GetCaps () | i2p::data::RouterInfo::eReachable);
|
||||
break;
|
||||
case eRouterStatusProxy:
|
||||
m_AcceptsTunnels = false;
|
||||
UpdateCongestion ();
|
||||
m_RouterInfo.UpdateCaps (m_RouterInfo.GetCaps () | i2p::data::RouterInfo::eUnreachable);
|
||||
break;
|
||||
default:
|
||||
;
|
||||
@@ -800,7 +802,7 @@ namespace i2p
|
||||
i2p::config::GetOption("host", ntcp2Host);
|
||||
if (!ntcp2Host.empty () && ntcp2Port)
|
||||
{
|
||||
auto addr = boost::asio::ip::address::from_string (ntcp2Host);
|
||||
auto addr = boost::asio::ip::make_address (ntcp2Host);
|
||||
if (addr.is_v6 ())
|
||||
{
|
||||
m_RouterInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv, addr, ntcp2Port);
|
||||
@@ -829,7 +831,7 @@ namespace i2p
|
||||
std::string host; i2p::config::GetOption("host", host);
|
||||
if (!host.empty ())
|
||||
{
|
||||
auto addr = boost::asio::ip::address::from_string (host);
|
||||
auto addr = boost::asio::ip::make_address (host);
|
||||
if (addr.is_v6 ())
|
||||
{
|
||||
m_RouterInfo.AddSSU2Address (m_SSU2Keys->staticPublicKey, m_SSU2Keys->intro, addr, ssu2Port);
|
||||
@@ -898,7 +900,7 @@ namespace i2p
|
||||
std::string host; i2p::config::GetOption("host", host);
|
||||
if (!host.empty ())
|
||||
{
|
||||
auto addr = boost::asio::ip::address::from_string (host);
|
||||
auto addr = boost::asio::ip::make_address (host);
|
||||
if (addr.is_v4 ())
|
||||
{
|
||||
m_RouterInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv, addr, ntcp2Port);
|
||||
@@ -928,7 +930,7 @@ namespace i2p
|
||||
std::string host; i2p::config::GetOption("host", host);
|
||||
if (!host.empty ())
|
||||
{
|
||||
auto addr = boost::asio::ip::address::from_string (host);
|
||||
auto addr = boost::asio::ip::make_address (host);
|
||||
if (addr.is_v4 ())
|
||||
{
|
||||
m_RouterInfo.AddSSU2Address (m_SSU2Keys->staticPublicKey, m_SSU2Keys->intro, addr, ssu2Port);
|
||||
@@ -1183,7 +1185,7 @@ namespace i2p
|
||||
void RouterContext::ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg)
|
||||
{
|
||||
if (m_Service)
|
||||
m_Service->GetService ().post (std::bind (&RouterContext::PostGarlicMessage, this, msg));
|
||||
boost::asio::post (m_Service->GetService (), std::bind (&RouterContext::PostGarlicMessage, this, msg));
|
||||
else
|
||||
LogPrint (eLogError, "Router: service is NULL");
|
||||
}
|
||||
@@ -1211,7 +1213,7 @@ namespace i2p
|
||||
void RouterContext::ProcessDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg)
|
||||
{
|
||||
if (m_Service)
|
||||
m_Service->GetService ().post (std::bind (&RouterContext::PostDeliveryStatusMessage, this, msg));
|
||||
boost::asio::post (m_Service->GetService (), std::bind (&RouterContext::PostDeliveryStatusMessage, this, msg));
|
||||
else
|
||||
LogPrint (eLogError, "Router: service is NULL");
|
||||
}
|
||||
@@ -1240,7 +1242,7 @@ namespace i2p
|
||||
} data;
|
||||
memcpy (data.k, key, 32);
|
||||
data.t = tag;
|
||||
m_Service->GetService ().post ([this,data](void)
|
||||
boost::asio::post (m_Service->GetService (), [this,data](void)
|
||||
{
|
||||
AddECIESx25519Key (data.k, data.t);
|
||||
});
|
||||
@@ -1406,7 +1408,7 @@ namespace i2p
|
||||
auto onDrop = [this]()
|
||||
{
|
||||
if (m_Service)
|
||||
m_Service->GetService ().post ([this]() { HandlePublishResendTimer (boost::system::error_code ()); });
|
||||
boost::asio::post (m_Service->GetService (), [this]() { HandlePublishResendTimer (boost::system::error_code ()); });
|
||||
};
|
||||
if (i2p::transport::transports.IsConnected (floodfill->GetIdentHash ()) || // already connected
|
||||
(floodfill->IsReachableFrom (i2p::context.GetRouterInfo ()) && // are we able to connect
|
||||
@@ -1489,7 +1491,7 @@ namespace i2p
|
||||
void RouterContext::UpdateCongestion ()
|
||||
{
|
||||
auto c = i2p::data::RouterInfo::eLowCongestion;
|
||||
if (!AcceptsTunnels () || !m_ShareRatio)
|
||||
if (!AcceptsTunnels () || !m_ShareRatio)
|
||||
c = i2p::data::RouterInfo::eRejectAll;
|
||||
else
|
||||
{
|
||||
@@ -1508,7 +1510,7 @@ namespace i2p
|
||||
if (m_CleanupTimer)
|
||||
{
|
||||
m_CleanupTimer->cancel ();
|
||||
m_CleanupTimer->expires_from_now (boost::posix_time::minutes(ROUTER_INFO_CLEANUP_INTERVAL));
|
||||
m_CleanupTimer->expires_from_now (boost::posix_time::seconds(ROUTER_INFO_CLEANUP_INTERVAL));
|
||||
m_CleanupTimer->async_wait (std::bind (&RouterContext::HandleCleanupTimer,
|
||||
this, std::placeholders::_1));
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ namespace garlic
|
||||
const int ROUTER_INFO_CONFIRMATION_TIMEOUT = 5; // in seconds
|
||||
const int ROUTER_INFO_MAX_PUBLISH_EXCLUDED_FLOODFILLS = 15;
|
||||
const int ROUTER_INFO_CONGESTION_UPDATE_INTERVAL = 12*60; // in seconds
|
||||
const int ROUTER_INFO_CLEANUP_INTERVAL = 5; // in minutes
|
||||
const int ROUTER_INFO_CLEANUP_INTERVAL = 102; // in seconds
|
||||
|
||||
enum RouterStatus
|
||||
{
|
||||
@@ -90,7 +90,7 @@ namespace garlic
|
||||
public:
|
||||
|
||||
RouterService (): RunnableServiceWithWork ("Router") {};
|
||||
boost::asio::io_service& GetService () { return GetIOService (); };
|
||||
auto& GetService () { return GetIOService (); };
|
||||
void Start () { StartIOService (); };
|
||||
void Stop () { StopIOService (); };
|
||||
};
|
||||
@@ -146,7 +146,6 @@ namespace garlic
|
||||
void SetNetID (int netID) { m_NetID = netID; };
|
||||
bool DecryptTunnelBuildRecord (const uint8_t * encrypted, uint8_t * data);
|
||||
bool DecryptTunnelShortRequestRecord (const uint8_t * encrypted, uint8_t * data);
|
||||
void SubmitECIESx25519Key (const uint8_t * key, uint64_t tag);
|
||||
|
||||
void UpdatePort (int port); // called from Daemon
|
||||
void UpdateAddress (const boost::asio::ip::address& host); // called from SSU2 or Daemon
|
||||
@@ -186,24 +185,24 @@ namespace garlic
|
||||
void UpdateTimestamp (uint64_t ts); // in seconds, called from NetDb before publishing
|
||||
|
||||
// implements LocalDestination
|
||||
std::shared_ptr<const i2p::data::IdentityEx> GetIdentity () const { return m_Keys.GetPublic (); };
|
||||
bool Decrypt (const uint8_t * encrypted, uint8_t * data, i2p::data::CryptoKeyType preferredCrypto) const;
|
||||
void Sign (const uint8_t * buf, int len, uint8_t * signature) const { m_Keys.Sign (buf, len, signature); };
|
||||
void SetLeaseSetUpdated () {};
|
||||
std::shared_ptr<const i2p::data::IdentityEx> GetIdentity () const override{ return m_Keys.GetPublic (); };
|
||||
bool Decrypt (const uint8_t * encrypted, uint8_t * data, i2p::data::CryptoKeyType preferredCrypto) const override;
|
||||
void SetLeaseSetUpdated (bool post) override {};
|
||||
|
||||
// implements GarlicDestination
|
||||
std::shared_ptr<const i2p::data::LocalLeaseSet> GetLeaseSet () { return nullptr; };
|
||||
std::shared_ptr<i2p::tunnel::TunnelPool> GetTunnelPool () const;
|
||||
std::shared_ptr<const i2p::data::LocalLeaseSet> GetLeaseSet () override { return nullptr; };
|
||||
std::shared_ptr<i2p::tunnel::TunnelPool> GetTunnelPool () const override;
|
||||
|
||||
// override GarlicDestination
|
||||
void ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg);
|
||||
void ProcessDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg);
|
||||
void ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg) override;
|
||||
void ProcessDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg) override;
|
||||
void SubmitECIESx25519Key (const uint8_t * key, uint64_t tag) override;
|
||||
|
||||
protected:
|
||||
|
||||
// implements GarlicDestination
|
||||
void HandleI2NPMessage (const uint8_t * buf, size_t len);
|
||||
bool HandleCloveI2NPMessage (I2NPMessageType typeID, const uint8_t * payload, size_t len, uint32_t msgID);
|
||||
void HandleI2NPMessage (const uint8_t * buf, size_t len) override;
|
||||
bool HandleCloveI2NPMessage (I2NPMessageType typeID, const uint8_t * payload, size_t len, uint32_t msgID) override;
|
||||
|
||||
private:
|
||||
|
||||
@@ -216,6 +215,7 @@ namespace garlic
|
||||
void UpdateSSU2Keys ();
|
||||
bool Load ();
|
||||
void SaveKeys ();
|
||||
void Sign (const uint8_t * buf, int len, uint8_t * signature) const { m_Keys.Sign (buf, len, signature); };
|
||||
uint16_t SelectRandomPort () const;
|
||||
void PublishNTCP2Address (std::shared_ptr<i2p::data::RouterInfo::Address> address, int port, bool publish) const;
|
||||
|
||||
|
||||
@@ -45,8 +45,9 @@ namespace data
|
||||
|
||||
RouterInfo::RouterInfo (const std::string& fullPath):
|
||||
m_FamilyID (0), m_IsUpdated (false), m_IsUnreachable (false), m_IsFloodfill (false),
|
||||
m_SupportedTransports (0),m_ReachableTransports (0), m_PublishedTransports (0),
|
||||
m_Caps (0), m_Version (0), m_Congestion (eLowCongestion)
|
||||
m_IsBufferScheduledToDelete (false), m_SupportedTransports (0),
|
||||
m_ReachableTransports (0), m_PublishedTransports (0), m_Caps (0), m_Version (0),
|
||||
m_Congestion (eLowCongestion)
|
||||
{
|
||||
m_Addresses = AddressesPtr(new Addresses ()); // create empty list
|
||||
m_Buffer = RouterInfo::NewBuffer (); // always RouterInfo's
|
||||
@@ -55,7 +56,7 @@ namespace data
|
||||
|
||||
RouterInfo::RouterInfo (std::shared_ptr<Buffer>&& buf, size_t len):
|
||||
m_FamilyID (0), m_IsUpdated (true), m_IsUnreachable (false), m_IsFloodfill (false),
|
||||
m_SupportedTransports (0), m_ReachableTransports (0), m_PublishedTransports (0),
|
||||
m_IsBufferScheduledToDelete (false), m_SupportedTransports (0), m_ReachableTransports (0), m_PublishedTransports (0),
|
||||
m_Caps (0), m_Version (0), m_Congestion (eLowCongestion)
|
||||
{
|
||||
if (len <= MAX_RI_BUFFER_SIZE)
|
||||
@@ -256,7 +257,7 @@ namespace data
|
||||
if (!strcmp (key, "host"))
|
||||
{
|
||||
boost::system::error_code ecode;
|
||||
address->host = boost::asio::ip::address::from_string (value, ecode);
|
||||
address->host = boost::asio::ip::make_address (value, ecode);
|
||||
if (!ecode && !address->host.is_unspecified ())
|
||||
{
|
||||
if (!i2p::transport::transports.IsInReservedRange (address->host) ||
|
||||
@@ -1140,6 +1141,7 @@ namespace data
|
||||
if (len > m_Buffer->size ()) len = m_Buffer->size ();
|
||||
memcpy (m_Buffer->data (), buf, len);
|
||||
m_Buffer->SetBufferLen (len);
|
||||
m_IsBufferScheduledToDelete = false;
|
||||
}
|
||||
|
||||
std::shared_ptr<RouterInfo::Buffer> RouterInfo::CopyBuffer () const
|
||||
|
||||
@@ -290,9 +290,11 @@ namespace data
|
||||
const uint8_t * GetBuffer () const { return m_Buffer ? m_Buffer->data () : nullptr; };
|
||||
const uint8_t * LoadBuffer (const std::string& fullPath); // load if necessary
|
||||
size_t GetBufferLen () const { return m_Buffer ? m_Buffer->GetBufferLen () : 0; };
|
||||
void DeleteBuffer () { m_Buffer = nullptr; };
|
||||
void DeleteBuffer () { m_Buffer = nullptr; m_IsBufferScheduledToDelete = false; };
|
||||
std::shared_ptr<Buffer> GetSharedBuffer () const { return m_Buffer; };
|
||||
std::shared_ptr<Buffer> CopyBuffer () const;
|
||||
void ScheduleBufferToDelete () { m_IsBufferScheduledToDelete = true; };
|
||||
bool IsBufferScheduledToDelete () const { return m_IsBufferScheduledToDelete; };
|
||||
|
||||
bool IsUpdated () const { return m_IsUpdated; };
|
||||
void SetUpdated (bool updated) { m_IsUpdated = updated; };
|
||||
@@ -354,7 +356,7 @@ namespace data
|
||||
#else
|
||||
AddressesPtr m_Addresses;
|
||||
#endif
|
||||
bool m_IsUpdated, m_IsUnreachable, m_IsFloodfill;
|
||||
bool m_IsUpdated, m_IsUnreachable, m_IsFloodfill, m_IsBufferScheduledToDelete;
|
||||
CompatibleTransports m_SupportedTransports, m_ReachableTransports, m_PublishedTransports;
|
||||
uint8_t m_Caps;
|
||||
char m_BandwidthCap;
|
||||
|
||||
229
libi2pd/SSU2.cpp
229
libi2pd/SSU2.cpp
@@ -81,7 +81,7 @@ namespace transport
|
||||
found = true;
|
||||
LogPrint (eLogDebug, "SSU2: Opening IPv4 socket at Start");
|
||||
OpenSocket (boost::asio::ip::udp::endpoint (m_AddressV4, port));
|
||||
m_ReceiveService.GetService ().post(
|
||||
boost::asio::post (m_ReceiveService.GetService (),
|
||||
[this]()
|
||||
{
|
||||
Receive (m_SocketV4);
|
||||
@@ -93,8 +93,8 @@ namespace transport
|
||||
found = true;
|
||||
LogPrint (eLogDebug, "SSU2: Opening IPv6 socket at Start");
|
||||
OpenSocket (boost::asio::ip::udp::endpoint (m_AddressV6, port));
|
||||
m_ReceiveService.GetService ().post(
|
||||
[this]()
|
||||
boost::asio::post (m_ReceiveService.GetService (),
|
||||
[this]()
|
||||
{
|
||||
Receive (m_SocketV6);
|
||||
});
|
||||
@@ -157,6 +157,9 @@ namespace transport
|
||||
m_IntroducersV6.clear ();
|
||||
m_ConnectedRecently.clear ();
|
||||
m_RequestedPeerTests.clear ();
|
||||
|
||||
m_PacketsPool.ReleaseMt (m_ReceivedPacketsQueue);
|
||||
m_ReceivedPacketsQueue.clear ();
|
||||
}
|
||||
|
||||
void SSU2Server::SetLocalAddress (const boost::asio::ip::address& localAddress)
|
||||
@@ -213,15 +216,16 @@ namespace transport
|
||||
return ep.port ();
|
||||
}
|
||||
|
||||
bool SSU2Server::IsConnectedRecently (const boost::asio::ip::udp::endpoint& ep)
|
||||
bool SSU2Server::IsConnectedRecently (const boost::asio::ip::udp::endpoint& ep, bool max)
|
||||
{
|
||||
if (!ep.port () || ep.address ().is_unspecified ()) return false;
|
||||
std::lock_guard<std::mutex> l(m_ConnectedRecentlyMutex);
|
||||
auto it = m_ConnectedRecently.find (ep);
|
||||
if (it != m_ConnectedRecently.end ())
|
||||
{
|
||||
if (i2p::util::GetSecondsSinceEpoch () <= it->second + SSU2_HOLE_PUNCH_EXPIRATION)
|
||||
if (i2p::util::GetSecondsSinceEpoch () <= it->second + (max ? SSU2_MAX_HOLE_PUNCH_EXPIRATION : SSU2_MIN_HOLE_PUNCH_EXPIRATION))
|
||||
return true;
|
||||
else
|
||||
else if (max)
|
||||
m_ConnectedRecently.erase (it);
|
||||
}
|
||||
return false;
|
||||
@@ -230,7 +234,8 @@ namespace transport
|
||||
void SSU2Server::AddConnectedRecently (const boost::asio::ip::udp::endpoint& ep, uint64_t ts)
|
||||
{
|
||||
if (!ep.port () || ep.address ().is_unspecified () ||
|
||||
i2p::util::GetSecondsSinceEpoch () > ts + SSU2_HOLE_PUNCH_EXPIRATION) return;
|
||||
i2p::util::GetSecondsSinceEpoch () > ts + SSU2_MAX_HOLE_PUNCH_EXPIRATION) return;
|
||||
std::lock_guard<std::mutex> l(m_ConnectedRecentlyMutex);
|
||||
auto [it, added] = m_ConnectedRecently.try_emplace (ep, ts);
|
||||
if (!added && ts > it->second)
|
||||
it->second = ts; // renew timestamp of existing endpoint
|
||||
@@ -364,28 +369,22 @@ namespace transport
|
||||
return;
|
||||
}
|
||||
packet->len = bytes_transferred;
|
||||
|
||||
|
||||
boost::system::error_code ec;
|
||||
size_t moreBytes = socket.available (ec);
|
||||
if (!ec && moreBytes)
|
||||
{
|
||||
auto packets = m_PacketsArrayPool.AcquireMt ();
|
||||
packets->AddPacket (packet);
|
||||
while (moreBytes && packets->numPackets < SSU2_MAX_NUM_PACKETS_PER_BATCH)
|
||||
{
|
||||
std::list<Packet *> packets;
|
||||
packets.push_back (packet);
|
||||
while (moreBytes && packets.size () < SSU2_MAX_NUM_PACKETS_PER_BATCH)
|
||||
{
|
||||
packet = m_PacketsPool.AcquireMt ();
|
||||
packet->len = socket.receive_from (boost::asio::buffer (packet->buf, SSU2_MAX_PACKET_SIZE), packet->from, 0, ec);
|
||||
if (!ec)
|
||||
{
|
||||
i2p::transport::transports.UpdateReceivedBytes (packet->len);
|
||||
if (packet->len >= SSU2_MIN_RECEIVED_PACKET_SIZE)
|
||||
{
|
||||
if (!packets->AddPacket (packet))
|
||||
{
|
||||
LogPrint (eLogError, "SSU2: Received packets array is full");
|
||||
m_PacketsPool.ReleaseMt (packet);
|
||||
}
|
||||
}
|
||||
packets.push_back (packet);
|
||||
else // drop too short packets
|
||||
m_PacketsPool.ReleaseMt (packet);
|
||||
moreBytes = socket.available(ec);
|
||||
@@ -398,10 +397,10 @@ namespace transport
|
||||
break;
|
||||
}
|
||||
}
|
||||
GetService ().post (std::bind (&SSU2Server::HandleReceivedPackets, this, packets));
|
||||
InsertToReceivedPacketsQueue (packets);
|
||||
}
|
||||
else
|
||||
GetService ().post (std::bind (&SSU2Server::HandleReceivedPacket, this, packet));
|
||||
InsertToReceivedPacketsQueue (packet);
|
||||
Receive (socket);
|
||||
}
|
||||
else
|
||||
@@ -428,49 +427,75 @@ namespace transport
|
||||
}
|
||||
}
|
||||
|
||||
void SSU2Server::HandleReceivedPacket (Packet * packet)
|
||||
void SSU2Server::HandleReceivedPackets (std::list<Packet *>&& packets)
|
||||
{
|
||||
if (packet)
|
||||
{
|
||||
if (m_IsThroughProxy)
|
||||
ProcessNextPacketFromProxy (packet->buf, packet->len);
|
||||
else
|
||||
ProcessNextPacket (packet->buf, packet->len, packet->from);
|
||||
m_PacketsPool.ReleaseMt (packet);
|
||||
if (m_LastSession && m_LastSession->GetState () != eSSU2SessionStateTerminated)
|
||||
m_LastSession->FlushData ();
|
||||
}
|
||||
}
|
||||
|
||||
void SSU2Server::HandleReceivedPackets (Packets * packets)
|
||||
{
|
||||
if (!packets) return;
|
||||
if (packets.empty ()) return;
|
||||
if (m_IsThroughProxy)
|
||||
for (size_t i = 0; i < packets->numPackets; i++)
|
||||
{
|
||||
auto& packet = (*packets)[i];
|
||||
ProcessNextPacketFromProxy (packet->buf, packet->len);
|
||||
}
|
||||
for (auto it: packets)
|
||||
ProcessNextPacketFromProxy (it->buf, it->len);
|
||||
else
|
||||
for (size_t i = 0; i < packets->numPackets; i++)
|
||||
{
|
||||
auto& packet = (*packets)[i];
|
||||
ProcessNextPacket (packet->buf, packet->len, packet->from);
|
||||
}
|
||||
m_PacketsPool.ReleaseMt (packets->data (), packets->numPackets);
|
||||
m_PacketsArrayPool.ReleaseMt (packets);
|
||||
for (auto it: packets)
|
||||
ProcessNextPacket (it->buf, it->len, it->from);
|
||||
m_PacketsPool.ReleaseMt (packets);
|
||||
if (m_LastSession && m_LastSession->GetState () != eSSU2SessionStateTerminated)
|
||||
m_LastSession->FlushData ();
|
||||
}
|
||||
|
||||
void SSU2Server::AddSession (std::shared_ptr<SSU2Session> session)
|
||||
void SSU2Server::InsertToReceivedPacketsQueue (Packet * packet)
|
||||
{
|
||||
if (!packet) return;
|
||||
bool empty = false;
|
||||
{
|
||||
std::lock_guard<std::mutex> l(m_ReceivedPacketsQueueMutex);
|
||||
empty = m_ReceivedPacketsQueue.empty ();
|
||||
m_ReceivedPacketsQueue.push_back (packet);
|
||||
}
|
||||
if (empty)
|
||||
boost::asio::post (GetService (), [this]() { HandleReceivedPacketsQueue (); });
|
||||
}
|
||||
|
||||
void SSU2Server::InsertToReceivedPacketsQueue (std::list<Packet *>& packets)
|
||||
{
|
||||
if (packets.empty ()) return;
|
||||
size_t queueSize = 0;
|
||||
{
|
||||
std::lock_guard<std::mutex> l(m_ReceivedPacketsQueueMutex);
|
||||
queueSize = m_ReceivedPacketsQueue.size ();
|
||||
if (queueSize < SSU2_MAX_RECEIVED_QUEUE_SIZE)
|
||||
m_ReceivedPacketsQueue.splice (m_ReceivedPacketsQueue.end (), packets);
|
||||
else
|
||||
{
|
||||
LogPrint (eLogError, "SSU2: Received queue size ", queueSize, " exceeds max size", SSU2_MAX_RECEIVED_QUEUE_SIZE);
|
||||
m_PacketsPool.ReleaseMt (packets);
|
||||
queueSize = 0; // invoke processing just in case
|
||||
}
|
||||
}
|
||||
if (!queueSize)
|
||||
boost::asio::post (GetService (), [this]() { HandleReceivedPacketsQueue (); });
|
||||
}
|
||||
|
||||
void SSU2Server::HandleReceivedPacketsQueue ()
|
||||
{
|
||||
std::list<Packet *> receivedPackets;
|
||||
{
|
||||
std::lock_guard<std::mutex> l(m_ReceivedPacketsQueueMutex);
|
||||
m_ReceivedPacketsQueue.swap (receivedPackets);
|
||||
}
|
||||
HandleReceivedPackets (std::move (receivedPackets));
|
||||
}
|
||||
|
||||
bool SSU2Server::AddSession (std::shared_ptr<SSU2Session> session)
|
||||
{
|
||||
if (session)
|
||||
{
|
||||
m_Sessions.emplace (session->GetConnID (), session);
|
||||
if (session->GetState () != eSSU2SessionStatePeerTest)
|
||||
AddSessionByRouterHash (session);
|
||||
if (m_Sessions.emplace (session->GetConnID (), session).second)
|
||||
{
|
||||
if (session->GetState () != eSSU2SessionStatePeerTest)
|
||||
AddSessionByRouterHash (session);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void SSU2Server::RemoveSession (uint64_t connID)
|
||||
@@ -497,7 +522,7 @@ namespace transport
|
||||
|
||||
void SSU2Server::RequestRemoveSession (uint64_t connID)
|
||||
{
|
||||
GetService ().post ([connID, this]() { RemoveSession (connID); });
|
||||
boost::asio::post (GetService (), [connID, this]() { RemoveSession (connID); });
|
||||
}
|
||||
|
||||
void SSU2Server::AddSessionByRouterHash (std::shared_ptr<SSU2Session> session)
|
||||
@@ -525,7 +550,7 @@ namespace transport
|
||||
// move unsent msgs to new session
|
||||
oldSession->MoveSendQueue (session);
|
||||
// terminate existing
|
||||
GetService ().post (std::bind (&SSU2Session::RequestTermination, oldSession, eSSU2TerminationReasonReplacedByNewSession));
|
||||
boost::asio::post (GetService (), std::bind (&SSU2Session::RequestTermination, oldSession, eSSU2TerminationReasonReplacedByNewSession));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -703,6 +728,9 @@ namespace transport
|
||||
m_LastSession->SetRemoteEndpoint (senderEndpoint);
|
||||
m_LastSession->ProcessPeerTest (buf, len);
|
||||
break;
|
||||
case eSSU2SessionStateHolePunch:
|
||||
m_LastSession->ProcessFirstIncomingMessage (connID, buf, len); // SessionRequest
|
||||
break;
|
||||
case eSSU2SessionStateClosing:
|
||||
m_LastSession->ProcessData (buf, len, senderEndpoint); // we might receive termintaion block
|
||||
if (m_LastSession && m_LastSession->GetState () == eSSU2SessionStateClosing)
|
||||
@@ -816,6 +844,29 @@ namespace transport
|
||||
}
|
||||
}
|
||||
|
||||
bool SSU2Server::CheckPendingOutgoingSession (const boost::asio::ip::udp::endpoint& ep, bool peerTest)
|
||||
{
|
||||
auto s = FindPendingOutgoingSession (ep);
|
||||
if (s)
|
||||
{
|
||||
if (peerTest)
|
||||
{
|
||||
// if peer test requested add it to the list for pending session
|
||||
auto onEstablished = s->GetOnEstablished ();
|
||||
if (onEstablished)
|
||||
s->SetOnEstablished ([s, onEstablished]()
|
||||
{
|
||||
onEstablished ();
|
||||
s->SendPeerTest ();
|
||||
});
|
||||
else
|
||||
s->SetOnEstablished ([s]() { s->SendPeerTest (); });
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SSU2Server::CreateSession (std::shared_ptr<const i2p::data::RouterInfo> router,
|
||||
std::shared_ptr<const i2p::data::RouterInfo::Address> address, bool peerTest)
|
||||
{
|
||||
@@ -827,7 +878,7 @@ namespace transport
|
||||
{
|
||||
// session with router found, trying to send peer test if requested
|
||||
if (peerTest && existingSession->IsEstablished ())
|
||||
GetService ().post ([existingSession]() { existingSession->SendPeerTest (); });
|
||||
boost::asio::post (GetService (), [existingSession]() { existingSession->SendPeerTest (); });
|
||||
return false;
|
||||
}
|
||||
// check is no pending session
|
||||
@@ -835,34 +886,28 @@ namespace transport
|
||||
if (isValidEndpoint)
|
||||
{
|
||||
if (i2p::transport::transports.IsInReservedRange(address->host)) return false;
|
||||
auto s = FindPendingOutgoingSession (boost::asio::ip::udp::endpoint (address->host, address->port));
|
||||
if (s)
|
||||
{
|
||||
if (peerTest)
|
||||
{
|
||||
// if peer test requested add it to the list for pending session
|
||||
auto onEstablished = s->GetOnEstablished ();
|
||||
if (onEstablished)
|
||||
s->SetOnEstablished ([s, onEstablished]()
|
||||
{
|
||||
onEstablished ();
|
||||
s->SendPeerTest ();
|
||||
});
|
||||
else
|
||||
s->SetOnEstablished ([s]() { s->SendPeerTest (); });
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (CheckPendingOutgoingSession (boost::asio::ip::udp::endpoint (address->host, address->port), peerTest)) return false;
|
||||
}
|
||||
|
||||
auto session = std::make_shared<SSU2Session> (*this, router, address);
|
||||
if (!isValidEndpoint && router->GetProfile ()->HasLastEndpoint (address->IsV4 ()))
|
||||
{
|
||||
// router doesn't publish endpoint, but we connected before and hole punch might be alive
|
||||
auto ep = router->GetProfile ()->GetLastEndpoint ();
|
||||
if (IsConnectedRecently (ep, false))
|
||||
{
|
||||
if (CheckPendingOutgoingSession (ep, peerTest)) return false;
|
||||
session->SetRemoteEndpoint (ep);
|
||||
isValidEndpoint = true;
|
||||
}
|
||||
}
|
||||
if (peerTest)
|
||||
session->SetOnEstablished ([session]() {session->SendPeerTest (); });
|
||||
|
||||
if (address->UsesIntroducer ())
|
||||
GetService ().post (std::bind (&SSU2Server::ConnectThroughIntroducer, this, session));
|
||||
else if (isValidEndpoint) // we can't connect without endpoint
|
||||
GetService ().post ([session]() { session->Connect (); });
|
||||
if (isValidEndpoint) // we know endpoint
|
||||
boost::asio::post (GetService (), [session]() { session->Connect (); });
|
||||
else if (address->UsesIntroducer ()) // we don't know endpoint yet
|
||||
boost::asio::post (GetService (), std::bind (&SSU2Server::ConnectThroughIntroducer, this, session));
|
||||
else
|
||||
return false;
|
||||
}
|
||||
@@ -1005,7 +1050,7 @@ namespace transport
|
||||
if (!remoteAddr || !remoteAddr->IsPeerTesting () ||
|
||||
(v4 && !remoteAddr->IsV4 ()) || (!v4 && !remoteAddr->IsV6 ())) return false;
|
||||
if (session->IsEstablished ())
|
||||
GetService ().post ([session]() { session->SendPeerTest (); });
|
||||
boost::asio::post (GetService (), [session]() { session->SendPeerTest (); });
|
||||
else
|
||||
session->SetOnEstablished ([session]() { session->SendPeerTest (); });
|
||||
return true;
|
||||
@@ -1112,7 +1157,7 @@ namespace transport
|
||||
|
||||
for (auto it = m_ConnectedRecently.begin (); it != m_ConnectedRecently.end (); )
|
||||
{
|
||||
if (ts > it->second + SSU2_HOLE_PUNCH_EXPIRATION)
|
||||
if (ts > it->second + SSU2_MAX_HOLE_PUNCH_EXPIRATION)
|
||||
it = m_ConnectedRecently.erase (it);
|
||||
else
|
||||
it++;
|
||||
@@ -1138,7 +1183,6 @@ namespace transport
|
||||
}
|
||||
|
||||
m_PacketsPool.CleanUpMt ();
|
||||
m_PacketsArrayPool.CleanUpMt ();
|
||||
m_SentPacketsPool.CleanUp ();
|
||||
m_IncompleteMessagesPool.CleanUp ();
|
||||
m_FragmentsPool.CleanUp ();
|
||||
@@ -1239,8 +1283,11 @@ namespace transport
|
||||
(!v4 && (s.second->GetRemoteTransports () & i2p::data::RouterInfo::eSSU2V6))))
|
||||
eligible.push_back (s.second);
|
||||
}
|
||||
|
||||
std::sample (eligible.begin(), eligible.end(), std::back_inserter(ret), maxNumIntroducers, m_Rng);
|
||||
|
||||
if (eligible.size () <= (size_t)maxNumIntroducers)
|
||||
return eligible;
|
||||
else
|
||||
std::sample (eligible.begin(), eligible.end(), std::back_inserter(ret), maxNumIntroducers, m_Rng);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -1342,7 +1389,7 @@ namespace transport
|
||||
excluded.insert (ident);
|
||||
}
|
||||
|
||||
// sesssion about to expire are not counted
|
||||
// session about to expire are not counted
|
||||
for (auto i = introducers.size (); i < SSU2_MAX_NUM_INTRODUCERS + numOldSessions; i++)
|
||||
{
|
||||
auto introducer = i2p::data::netdb.GetRandomSSU2Introducer (v4, excluded);
|
||||
@@ -1469,6 +1516,18 @@ namespace transport
|
||||
}
|
||||
}
|
||||
|
||||
bool SSU2Server::AEADChaCha20Poly1305Encrypt (const uint8_t * msg, size_t msgLen,
|
||||
const uint8_t * ad, size_t adLen, const uint8_t * key, const uint8_t * nonce, uint8_t * buf, size_t len)
|
||||
{
|
||||
return m_Encryptor.Encrypt (msg, msgLen, ad, adLen, key, nonce, buf, len);
|
||||
}
|
||||
|
||||
bool SSU2Server::AEADChaCha20Poly1305Decrypt (const uint8_t * msg, size_t msgLen,
|
||||
const uint8_t * ad, size_t adLen, const uint8_t * key, const uint8_t * nonce, uint8_t * buf, size_t len)
|
||||
{
|
||||
return m_Decryptor.Decrypt (msg, msgLen, ad, adLen, key, nonce, buf, len);
|
||||
}
|
||||
|
||||
void SSU2Server::SendThroughProxy (const uint8_t * header, size_t headerLen, const uint8_t * headerX, size_t headerXLen,
|
||||
const uint8_t * payload, size_t payloadLen, const boost::asio::ip::udp::endpoint& to)
|
||||
{
|
||||
@@ -1717,7 +1776,7 @@ namespace transport
|
||||
bool SSU2Server::SetProxy (const std::string& address, uint16_t port)
|
||||
{
|
||||
boost::system::error_code ecode;
|
||||
auto addr = boost::asio::ip::address::from_string (address, ecode);
|
||||
auto addr = boost::asio::ip::make_address (address, ecode);
|
||||
if (!ecode && !addr.is_unspecified () && port)
|
||||
{
|
||||
m_IsThroughProxy = true;
|
||||
|
||||
@@ -12,11 +12,13 @@
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
#include <list>
|
||||
#include <array>
|
||||
#include <mutex>
|
||||
#include <random>
|
||||
#include "util.h"
|
||||
#include "SSU2Session.h"
|
||||
#include "SSU2OutOfSession.h"
|
||||
#include "Socks5.h"
|
||||
|
||||
namespace i2p
|
||||
@@ -35,13 +37,15 @@ namespace transport
|
||||
const uint64_t SSU2_SOCKET_MAX_BUFFER_SIZE = 4 * 1024 * 1024;
|
||||
const size_t SSU2_MAX_NUM_INTRODUCERS = 3;
|
||||
const size_t SSU2_MIN_RECEIVED_PACKET_SIZE = 40; // 16 byte short header + 8 byte minimum payload + 16 byte MAC
|
||||
const size_t SSU2_MAX_RECEIVED_QUEUE_SIZE = 2500; // in packets
|
||||
const int SSU2_TO_INTRODUCER_SESSION_DURATION = 3600; // 1 hour
|
||||
const int SSU2_TO_INTRODUCER_SESSION_EXPIRATION = 4800; // 80 minutes
|
||||
const int SSU2_KEEP_ALIVE_INTERVAL = 15; // in seconds
|
||||
const int SSU2_KEEP_ALIVE_INTERVAL_VARIANCE = 4; // in seconds
|
||||
const int SSU2_PROXY_CONNECT_RETRY_TIMEOUT = 30; // in seconds
|
||||
const int SSU2_HOLE_PUNCH_EXPIRATION = 150; // in seconds
|
||||
const size_t SSU2_MAX_NUM_PACKETS_PER_BATCH = 32;
|
||||
const int SSU2_MIN_HOLE_PUNCH_EXPIRATION = 30; // in seconds
|
||||
const int SSU2_MAX_HOLE_PUNCH_EXPIRATION = 160; // in seconds
|
||||
const size_t SSU2_MAX_NUM_PACKETS_PER_BATCH = 64;
|
||||
|
||||
class SSU2Server: private i2p::util::RunnableServiceWithWork
|
||||
{
|
||||
@@ -51,27 +55,13 @@ namespace transport
|
||||
size_t len;
|
||||
boost::asio::ip::udp::endpoint from;
|
||||
};
|
||||
|
||||
struct Packets: public std::array<Packet *, SSU2_MAX_NUM_PACKETS_PER_BATCH>
|
||||
{
|
||||
size_t numPackets = 0;
|
||||
bool AddPacket (Packet *p)
|
||||
{
|
||||
if (p && numPackets < size ())
|
||||
{
|
||||
data()[numPackets] = p; numPackets++;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
class ReceiveService: public i2p::util::RunnableService
|
||||
{
|
||||
public:
|
||||
|
||||
ReceiveService (const std::string& name): RunnableService (name) {};
|
||||
boost::asio::io_service& GetService () { return GetIOService (); };
|
||||
auto& GetService () { return GetIOService (); };
|
||||
void Start () { StartIOService (); };
|
||||
void Stop () { StopIOService (); };
|
||||
};
|
||||
@@ -83,20 +73,24 @@ namespace transport
|
||||
|
||||
void Start ();
|
||||
void Stop ();
|
||||
boost::asio::io_service& GetService () { return GetIOService (); };
|
||||
auto& GetService () { return GetIOService (); };
|
||||
void SetLocalAddress (const boost::asio::ip::address& localAddress);
|
||||
bool SetProxy (const std::string& address, uint16_t port);
|
||||
bool UsesProxy () const { return m_IsThroughProxy; };
|
||||
bool IsSupported (const boost::asio::ip::address& addr) const;
|
||||
uint16_t GetPort (bool v4) const;
|
||||
bool IsConnectedRecently (const boost::asio::ip::udp::endpoint& ep);
|
||||
bool IsConnectedRecently (const boost::asio::ip::udp::endpoint& ep, bool max = true);
|
||||
void AddConnectedRecently (const boost::asio::ip::udp::endpoint& ep, uint64_t ts);
|
||||
std::mt19937& GetRng () { return m_Rng; }
|
||||
bool AEADChaCha20Poly1305Encrypt (const uint8_t * msg, size_t msgLen, const uint8_t * ad, size_t adLen,
|
||||
const uint8_t * key, const uint8_t * nonce, uint8_t * buf, size_t len);
|
||||
bool AEADChaCha20Poly1305Decrypt (const uint8_t * msg, size_t msgLen, const uint8_t * ad, size_t adLen,
|
||||
const uint8_t * key, const uint8_t * nonce, uint8_t * buf, size_t len);
|
||||
bool IsMaxNumIntroducers (bool v4) const { return (v4 ? m_Introducers.size () : m_IntroducersV6.size ()) >= SSU2_MAX_NUM_INTRODUCERS; }
|
||||
bool IsSyncClockFromPeers () const { return m_IsSyncClockFromPeers; };
|
||||
void AdjustTimeOffset (int64_t offset, std::shared_ptr<const i2p::data::IdentityEx> from);
|
||||
|
||||
void AddSession (std::shared_ptr<SSU2Session> session);
|
||||
bool AddSession (std::shared_ptr<SSU2Session> session);
|
||||
void RemoveSession (uint64_t connID);
|
||||
void RequestRemoveSession (uint64_t connID);
|
||||
void AddSessionByRouterHash (std::shared_ptr<SSU2Session> session);
|
||||
@@ -144,10 +138,12 @@ namespace transport
|
||||
void Receive (boost::asio::ip::udp::socket& socket);
|
||||
void HandleReceivedFrom (const boost::system::error_code& ecode, size_t bytes_transferred,
|
||||
Packet * packet, boost::asio::ip::udp::socket& socket);
|
||||
void HandleReceivedPacket (Packet * packet);
|
||||
void HandleReceivedPackets (Packets * packets);
|
||||
void HandleReceivedPackets (std::list<Packet *>&& packets);
|
||||
void ProcessNextPacket (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint);
|
||||
|
||||
void InsertToReceivedPacketsQueue (Packet * packet);
|
||||
void InsertToReceivedPacketsQueue (std::list<Packet *>& packets);
|
||||
void HandleReceivedPacketsQueue ();
|
||||
|
||||
void ScheduleTermination ();
|
||||
void HandleTerminationTimer (const boost::system::error_code& ecode);
|
||||
|
||||
@@ -157,6 +153,7 @@ namespace transport
|
||||
void ScheduleResend (bool more);
|
||||
void HandleResendTimer (const boost::system::error_code& ecode);
|
||||
|
||||
bool CheckPendingOutgoingSession (const boost::asio::ip::udp::endpoint& ep, bool peerTest);
|
||||
void ConnectThroughIntroducer (std::shared_ptr<SSU2Session> session);
|
||||
std::vector<std::shared_ptr<SSU2Session> > FindIntroducers (int maxNumIntroducers,
|
||||
bool v4, const std::unordered_set<i2p::data::IdentHash>& excluded);
|
||||
@@ -191,7 +188,6 @@ namespace transport
|
||||
std::unordered_map<uint32_t, std::pair <std::weak_ptr<SSU2Session>, uint64_t > > m_PeerTests; // nonce->(Alice, timestamp). We are Bob
|
||||
std::list<std::pair<i2p::data::IdentHash, uint32_t> > m_Introducers, m_IntroducersV6; // introducers we are connected to
|
||||
i2p::util::MemoryPoolMt<Packet> m_PacketsPool;
|
||||
i2p::util::MemoryPoolMt<Packets> m_PacketsArrayPool;
|
||||
i2p::util::MemoryPool<SSU2SentPacket> m_SentPacketsPool;
|
||||
i2p::util::MemoryPool<SSU2IncompleteMessage> m_IncompleteMessagesPool;
|
||||
i2p::util::MemoryPool<SSU2IncompleteMessage::Fragment> m_FragmentsPool;
|
||||
@@ -204,7 +200,12 @@ namespace transport
|
||||
std::shared_ptr<const i2p::data::IdentityEx> m_PendingTimeOffsetFrom;
|
||||
std::mt19937 m_Rng;
|
||||
std::map<boost::asio::ip::udp::endpoint, uint64_t> m_ConnectedRecently; // endpoint -> last activity time in seconds
|
||||
mutable std::mutex m_ConnectedRecentlyMutex;
|
||||
std::unordered_map<uint32_t, std::pair <std::weak_ptr<SSU2PeerTestSession>, uint64_t > > m_RequestedPeerTests; // nonce->(Alice, timestamp)
|
||||
std::list<Packet *> m_ReceivedPacketsQueue;
|
||||
mutable std::mutex m_ReceivedPacketsQueueMutex;
|
||||
i2p::crypto::AEADChaCha20Poly1305Encryptor m_Encryptor;
|
||||
i2p::crypto::AEADChaCha20Poly1305Decryptor m_Decryptor;
|
||||
|
||||
// proxy
|
||||
bool m_IsThroughProxy;
|
||||
|
||||
355
libi2pd/SSU2OutOfSession.cpp
Normal file
355
libi2pd/SSU2OutOfSession.cpp
Normal file
@@ -0,0 +1,355 @@
|
||||
/*
|
||||
* Copyright (c) 2024, 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
|
||||
*/
|
||||
|
||||
#include "Log.h"
|
||||
#include "SSU2.h"
|
||||
#include "SSU2OutOfSession.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace transport
|
||||
{
|
||||
SSU2PeerTestSession::SSU2PeerTestSession (SSU2Server& server, uint64_t sourceConnID, uint64_t destConnID):
|
||||
SSU2Session (server, nullptr, nullptr, false),
|
||||
m_MsgNumReceived (0), m_NumResends (0),m_IsConnectedRecently (false), m_IsStatusChanged (false),
|
||||
m_PeerTestResendTimer (server.GetService ())
|
||||
{
|
||||
if (!sourceConnID) sourceConnID = ~destConnID;
|
||||
if (!destConnID) destConnID = ~sourceConnID;
|
||||
SetSourceConnID (sourceConnID);
|
||||
SetDestConnID (destConnID);
|
||||
SetState (eSSU2SessionStatePeerTest);
|
||||
SetTerminationTimeout (SSU2_PEER_TEST_EXPIRATION_TIMEOUT);
|
||||
}
|
||||
|
||||
bool SSU2PeerTestSession::ProcessPeerTest (uint8_t * buf, size_t len)
|
||||
{
|
||||
// we are Alice or Charlie, msgs 5,6,7
|
||||
Header header;
|
||||
memcpy (header.buf, buf, 16);
|
||||
header.ll[0] ^= CreateHeaderMask (i2p::context.GetSSU2IntroKey (), buf + (len - 24));
|
||||
header.ll[1] ^= CreateHeaderMask (i2p::context.GetSSU2IntroKey (), buf + (len - 12));
|
||||
if (header.h.type != eSSU2PeerTest)
|
||||
{
|
||||
LogPrint (eLogWarning, "SSU2: Unexpected message type ", (int)header.h.type, " instead ", (int)eSSU2PeerTest);
|
||||
return false;
|
||||
}
|
||||
if (len < 48)
|
||||
{
|
||||
LogPrint (eLogWarning, "SSU2: PeerTest message too short ", len);
|
||||
return false;
|
||||
}
|
||||
uint8_t nonce[12] = {0};
|
||||
uint64_t headerX[2]; // sourceConnID, token
|
||||
i2p::crypto::ChaCha20 (buf + 16, 16, i2p::context.GetSSU2IntroKey (), nonce, (uint8_t *)headerX);
|
||||
SetDestConnID (headerX[0]);
|
||||
// decrypt and handle payload
|
||||
uint8_t * payload = buf + 32;
|
||||
CreateNonce (be32toh (header.h.packetNum), nonce);
|
||||
uint8_t h[32];
|
||||
memcpy (h, header.buf, 16);
|
||||
memcpy (h + 16, &headerX, 16);
|
||||
if (!i2p::crypto::AEADChaCha20Poly1305 (payload, len - 48, h, 32,
|
||||
i2p::context.GetSSU2IntroKey (), nonce, payload, len - 48, false))
|
||||
{
|
||||
LogPrint (eLogWarning, "SSU2: PeerTest AEAD verification failed ");
|
||||
return false;
|
||||
}
|
||||
HandlePayload (payload, len - 48);
|
||||
SetIsDataReceived (false);
|
||||
return true;
|
||||
}
|
||||
|
||||
void SSU2PeerTestSession::HandleAddress (const uint8_t * buf, size_t len)
|
||||
{
|
||||
if (!ExtractEndpoint (buf, len, m_OurEndpoint))
|
||||
LogPrint (eLogWarning, "SSU2: Can't handle address block from peer test message");
|
||||
}
|
||||
|
||||
void SSU2PeerTestSession::HandlePeerTest (const uint8_t * buf, size_t len)
|
||||
{
|
||||
// msgs 5-7
|
||||
if (len < 8) return;
|
||||
uint8_t msg = buf[0];
|
||||
if (msg <= m_MsgNumReceived)
|
||||
{
|
||||
LogPrint (eLogDebug, "SSU2: PeerTest msg num ", msg, " received after ", m_MsgNumReceived, ". Ignored");
|
||||
return;
|
||||
}
|
||||
size_t offset = 3; // points to signed data after msg + code + flag
|
||||
uint32_t nonce = bufbe32toh (buf + offset + 1); // 1 - ver
|
||||
switch (msg) // msg
|
||||
{
|
||||
case 5: // Alice from Charlie 1
|
||||
{
|
||||
if (htobe64 (((uint64_t)nonce << 32) | nonce) == GetSourceConnID ())
|
||||
{
|
||||
m_PeerTestResendTimer.cancel (); // cancel delayed msg 6 if any
|
||||
m_IsConnectedRecently = GetServer ().IsConnectedRecently (GetRemoteEndpoint ());
|
||||
if (GetAddress ())
|
||||
{
|
||||
if (!m_IsConnectedRecently)
|
||||
SetRouterStatus (eRouterStatusOK);
|
||||
else if (m_IsStatusChanged && GetRouterStatus () == eRouterStatusFirewalled)
|
||||
SetRouterStatus (eRouterStatusUnknown);
|
||||
SendPeerTest (6, buf + offset, len - offset);
|
||||
}
|
||||
}
|
||||
else
|
||||
LogPrint (eLogWarning, "SSU2: Peer test 5 nonce mismatch ", nonce, " connID=", GetSourceConnID ());
|
||||
break;
|
||||
}
|
||||
case 6: // Charlie from Alice
|
||||
{
|
||||
m_PeerTestResendTimer.cancel (); // no more msg 5 resends
|
||||
if (GetAddress ())
|
||||
SendPeerTest (7, buf + offset, len - offset);
|
||||
else
|
||||
LogPrint (eLogWarning, "SSU2: Unknown address for peer test 6");
|
||||
GetServer ().RequestRemoveSession (GetConnID ());
|
||||
break;
|
||||
}
|
||||
case 7: // Alice from Charlie 2
|
||||
{
|
||||
m_PeerTestResendTimer.cancel (); // no more msg 6 resends
|
||||
if (m_MsgNumReceived < 5 && m_OurEndpoint.port ()) // msg 5 was not received
|
||||
{
|
||||
if (m_OurEndpoint.address ().is_v4 ()) // ipv4
|
||||
{
|
||||
if (i2p::context.GetStatus () == eRouterStatusFirewalled)
|
||||
{
|
||||
if (m_OurEndpoint.port () != GetServer ().GetPort (true))
|
||||
i2p::context.SetError (eRouterErrorSymmetricNAT);
|
||||
else if (i2p::context.GetError () == eRouterErrorSymmetricNAT)
|
||||
i2p::context.SetError (eRouterErrorNone);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (i2p::context.GetStatusV6 () == eRouterStatusFirewalled)
|
||||
{
|
||||
if (m_OurEndpoint.port () != GetServer ().GetPort (false))
|
||||
i2p::context.SetErrorV6 (eRouterErrorSymmetricNAT);
|
||||
else if (i2p::context.GetErrorV6 () == eRouterErrorSymmetricNAT)
|
||||
i2p::context.SetErrorV6 (eRouterErrorNone);
|
||||
}
|
||||
}
|
||||
}
|
||||
GetServer ().RequestRemoveSession (GetConnID ());
|
||||
break;
|
||||
}
|
||||
default:
|
||||
LogPrint (eLogWarning, "SSU2: PeerTest unexpected msg num ", msg);
|
||||
return;
|
||||
}
|
||||
m_MsgNumReceived = msg;
|
||||
}
|
||||
|
||||
void SSU2PeerTestSession::SendPeerTest (uint8_t msg)
|
||||
{
|
||||
auto addr = GetAddress ();
|
||||
if (!addr) return;
|
||||
Header header;
|
||||
uint8_t h[32], payload[SSU2_MAX_PACKET_SIZE];
|
||||
// fill packet
|
||||
header.h.connID = GetDestConnID (); // dest id
|
||||
RAND_bytes (header.buf + 8, 4); // random packet num
|
||||
header.h.type = eSSU2PeerTest;
|
||||
header.h.flags[0] = 2; // ver
|
||||
header.h.flags[1] = (uint8_t)i2p::context.GetNetID (); // netID
|
||||
header.h.flags[2] = 0; // flag
|
||||
memcpy (h, header.buf, 16);
|
||||
htobuf64 (h + 16, GetSourceConnID ()); // source id
|
||||
// payload
|
||||
payload[0] = eSSU2BlkDateTime;
|
||||
htobe16buf (payload + 1, 4);
|
||||
htobe32buf (payload + 3, (i2p::util::GetMillisecondsSinceEpoch () + 500)/1000);
|
||||
size_t payloadSize = 7;
|
||||
if (msg == 6 || msg == 7)
|
||||
payloadSize += CreateAddressBlock (payload + payloadSize, GetMaxPayloadSize () - payloadSize, GetRemoteEndpoint ());
|
||||
payloadSize += CreatePeerTestBlock (payload + payloadSize, GetMaxPayloadSize () - payloadSize,
|
||||
msg, eSSU2PeerTestCodeAccept, nullptr, m_SignedData.data (), m_SignedData.size ());
|
||||
payloadSize += CreatePaddingBlock (payload + payloadSize, GetMaxPayloadSize () - payloadSize);
|
||||
// encrypt
|
||||
uint8_t n[12];
|
||||
CreateNonce (be32toh (header.h.packetNum), n);
|
||||
i2p::crypto::AEADChaCha20Poly1305 (payload, payloadSize, h, 32, addr->i, n, payload, payloadSize + 16, true);
|
||||
payloadSize += 16;
|
||||
header.ll[0] ^= CreateHeaderMask (addr->i, payload + (payloadSize - 24));
|
||||
header.ll[1] ^= CreateHeaderMask (addr->i, payload + (payloadSize - 12));
|
||||
memset (n, 0, 12);
|
||||
i2p::crypto::ChaCha20 (h + 16, 16, addr->i, n, h + 16);
|
||||
// send
|
||||
GetServer ().Send (header.buf, 16, h + 16, 16, payload, payloadSize, GetRemoteEndpoint ());
|
||||
UpdateNumSentBytes (payloadSize + 32);
|
||||
}
|
||||
|
||||
void SSU2PeerTestSession::SendPeerTest (uint8_t msg, const uint8_t * signedData, size_t signedDataLen, bool delayed)
|
||||
{
|
||||
#if __cplusplus >= 202002L // C++20
|
||||
m_SignedData.assign (signedData, signedData + signedDataLen);
|
||||
#else
|
||||
m_SignedData.resize (signedDataLen);
|
||||
memcpy (m_SignedData.data (), signedData, signedDataLen);
|
||||
#endif
|
||||
if (!delayed)
|
||||
SendPeerTest (msg);
|
||||
// schedule resend for msgs 5 or 6
|
||||
if (msg == 5 || msg == 6)
|
||||
ScheduleResend (msg);
|
||||
}
|
||||
|
||||
void SSU2PeerTestSession::SendPeerTest (uint8_t msg, const uint8_t * signedData, size_t signedDataLen,
|
||||
std::shared_ptr<const i2p::data::RouterInfo::Address> addr, bool delayed)
|
||||
{
|
||||
if (!addr) return;
|
||||
SetAddress (addr);
|
||||
SendPeerTest (msg, signedData, signedDataLen, delayed);
|
||||
}
|
||||
|
||||
void SSU2PeerTestSession::Connect ()
|
||||
{
|
||||
LogPrint (eLogError, "SSU2: Can't connect peer test session");
|
||||
}
|
||||
|
||||
bool SSU2PeerTestSession::ProcessFirstIncomingMessage (uint64_t connID, uint8_t * buf, size_t len)
|
||||
{
|
||||
LogPrint (eLogError, "SSU2: Can't handle incoming message in peer test session");
|
||||
return false;
|
||||
}
|
||||
|
||||
void SSU2PeerTestSession::ScheduleResend (uint8_t msg)
|
||||
{
|
||||
if (m_NumResends < SSU2_PEER_TEST_MAX_NUM_RESENDS)
|
||||
{
|
||||
m_PeerTestResendTimer.expires_from_now (boost::posix_time::milliseconds(
|
||||
SSU2_PEER_TEST_RESEND_INTERVAL + GetServer ().GetRng ()() % SSU2_PEER_TEST_RESEND_INTERVAL_VARIANCE));
|
||||
std::weak_ptr<SSU2PeerTestSession> s(std::static_pointer_cast<SSU2PeerTestSession>(shared_from_this ()));
|
||||
m_PeerTestResendTimer.async_wait ([s, msg](const boost::system::error_code& ecode)
|
||||
{
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
{
|
||||
auto s1 = s.lock ();
|
||||
if (s1)
|
||||
{
|
||||
if (msg > s1->m_MsgNumReceived)
|
||||
{
|
||||
s1->SendPeerTest (msg);
|
||||
s1->m_NumResends++;
|
||||
s1->ScheduleResend (msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
SSU2HolePunchSession::SSU2HolePunchSession (SSU2Server& server, uint32_t nonce,
|
||||
const boost::asio::ip::udp::endpoint& remoteEndpoint,
|
||||
std::shared_ptr<const i2p::data::RouterInfo::Address> addr):
|
||||
SSU2Session (server), // we create full incoming session
|
||||
m_NumResends (0), m_HolePunchResendTimer (server.GetService ())
|
||||
{
|
||||
// we are Charlie
|
||||
uint64_t destConnID = htobe64 (((uint64_t)nonce << 32) | nonce); // dest id
|
||||
uint32_t sourceConnID = ~destConnID;
|
||||
SetSourceConnID (sourceConnID);
|
||||
SetDestConnID (destConnID);
|
||||
SetState (eSSU2SessionStateHolePunch);
|
||||
SetRemoteEndpoint (remoteEndpoint);
|
||||
SetAddress (addr);
|
||||
SetTerminationTimeout (SSU2_RELAY_NONCE_EXPIRATION_TIMEOUT);
|
||||
}
|
||||
|
||||
void SSU2HolePunchSession::SendHolePunch ()
|
||||
{
|
||||
auto addr = GetAddress ();
|
||||
if (!addr) return;
|
||||
auto& ep = GetRemoteEndpoint ();
|
||||
LogPrint (eLogDebug, "SSU2: Sending HolePunch to ", ep);
|
||||
Header header;
|
||||
uint8_t h[32], payload[SSU2_MAX_PACKET_SIZE];
|
||||
// fill packet
|
||||
header.h.connID = GetDestConnID (); // dest id
|
||||
RAND_bytes (header.buf + 8, 4); // random packet num
|
||||
header.h.type = eSSU2HolePunch;
|
||||
header.h.flags[0] = 2; // ver
|
||||
header.h.flags[1] = (uint8_t)i2p::context.GetNetID (); // netID
|
||||
header.h.flags[2] = 0; // flag
|
||||
memcpy (h, header.buf, 16);
|
||||
htobuf64 (h + 16, GetSourceConnID ()); // source id
|
||||
RAND_bytes (h + 24, 8); // header token, to be ignored by Alice
|
||||
// payload
|
||||
payload[0] = eSSU2BlkDateTime;
|
||||
htobe16buf (payload + 1, 4);
|
||||
htobe32buf (payload + 3, (i2p::util::GetMillisecondsSinceEpoch () + 500)/1000);
|
||||
size_t payloadSize = 7;
|
||||
payloadSize += CreateAddressBlock (payload + payloadSize, GetMaxPayloadSize () - payloadSize, ep);
|
||||
// relay response block
|
||||
if (payloadSize + m_RelayResponseBlock.size () < GetMaxPayloadSize ())
|
||||
{
|
||||
memcpy (payload + payloadSize, m_RelayResponseBlock.data (), m_RelayResponseBlock.size ());
|
||||
payloadSize += m_RelayResponseBlock.size ();
|
||||
}
|
||||
payloadSize += CreatePaddingBlock (payload + payloadSize, GetMaxPayloadSize () - payloadSize);
|
||||
// encrypt
|
||||
uint8_t n[12];
|
||||
CreateNonce (be32toh (header.h.packetNum), n);
|
||||
i2p::crypto::AEADChaCha20Poly1305 (payload, payloadSize, h, 32, addr->i, n, payload, payloadSize + 16, true);
|
||||
payloadSize += 16;
|
||||
header.ll[0] ^= CreateHeaderMask (addr->i, payload + (payloadSize - 24));
|
||||
header.ll[1] ^= CreateHeaderMask (addr->i, payload + (payloadSize - 12));
|
||||
memset (n, 0, 12);
|
||||
i2p::crypto::ChaCha20 (h + 16, 16, addr->i, n, h + 16);
|
||||
// send
|
||||
GetServer ().Send (header.buf, 16, h + 16, 16, payload, payloadSize, ep);
|
||||
UpdateNumSentBytes (payloadSize + 32);
|
||||
}
|
||||
|
||||
void SSU2HolePunchSession::SendHolePunch (const uint8_t * relayResponseBlock, size_t relayResponseBlockLen)
|
||||
{
|
||||
#if __cplusplus >= 202002L // C++20
|
||||
m_RelayResponseBlock.assign (relayResponseBlock, relayResponseBlock + relayResponseBlockLen);
|
||||
#else
|
||||
m_RelayResponseBlock.resize (relayResponseBlockLen);
|
||||
memcpy (m_RelayResponseBlock.data (), relayResponseBlock, relayResponseBlockLen);
|
||||
#endif
|
||||
SendHolePunch ();
|
||||
ScheduleResend ();
|
||||
}
|
||||
|
||||
void SSU2HolePunchSession::ScheduleResend ()
|
||||
{
|
||||
if (m_NumResends < SSU2_HOLE_PUNCH_MAX_NUM_RESENDS)
|
||||
{
|
||||
m_HolePunchResendTimer.expires_from_now (boost::posix_time::milliseconds(
|
||||
SSU2_HOLE_PUNCH_RESEND_INTERVAL + GetServer ().GetRng ()() % SSU2_HOLE_PUNCH_RESEND_INTERVAL_VARIANCE));
|
||||
std::weak_ptr<SSU2HolePunchSession> s(std::static_pointer_cast<SSU2HolePunchSession>(shared_from_this ()));
|
||||
m_HolePunchResendTimer.async_wait ([s](const boost::system::error_code& ecode)
|
||||
{
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
{
|
||||
auto s1 = s.lock ();
|
||||
if (s1 && s1->GetState () == eSSU2SessionStateHolePunch)
|
||||
{
|
||||
s1->SendHolePunch ();
|
||||
s1->m_NumResends++;
|
||||
s1->ScheduleResend ();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
bool SSU2HolePunchSession::ProcessFirstIncomingMessage (uint64_t connID, uint8_t * buf, size_t len)
|
||||
{
|
||||
m_HolePunchResendTimer.cancel ();
|
||||
return SSU2Session::ProcessFirstIncomingMessage (connID, buf, len);
|
||||
}
|
||||
}
|
||||
}
|
||||
86
libi2pd/SSU2OutOfSession.h
Normal file
86
libi2pd/SSU2OutOfSession.h
Normal file
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Copyright (c) 2024, 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 SSU2_OUT_OF_SESSION_H__
|
||||
#define SSU2_OUT_OF_SESSION_H__
|
||||
|
||||
#include <vector>
|
||||
#include "SSU2Session.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace transport
|
||||
{
|
||||
const int SSU2_PEER_TEST_RESEND_INTERVAL = 3000; // in milliseconds
|
||||
const int SSU2_PEER_TEST_RESEND_INTERVAL_VARIANCE = 2000; // in milliseconds
|
||||
const int SSU2_PEER_TEST_MAX_NUM_RESENDS = 3;
|
||||
|
||||
class SSU2PeerTestSession: public SSU2Session // for PeerTest msgs 5,6,7
|
||||
{
|
||||
public:
|
||||
|
||||
SSU2PeerTestSession (SSU2Server& server, uint64_t sourceConnID, uint64_t destConnID);
|
||||
|
||||
uint8_t GetMsgNumReceived () const { return m_MsgNumReceived; }
|
||||
bool IsConnectedRecently () const { return m_IsConnectedRecently; }
|
||||
void SetStatusChanged () { m_IsStatusChanged = true; }
|
||||
|
||||
void SendPeerTest (uint8_t msg, const uint8_t * signedData, size_t signedDataLen,
|
||||
std::shared_ptr<const i2p::data::RouterInfo::Address> addr, bool delayed = false);
|
||||
bool ProcessPeerTest (uint8_t * buf, size_t len) override;
|
||||
void Connect () override; // outgoing
|
||||
bool ProcessFirstIncomingMessage (uint64_t connID, uint8_t * buf, size_t len) override; // incoming
|
||||
|
||||
private:
|
||||
|
||||
void SendPeerTest (uint8_t msg, const uint8_t * signedData, size_t signedDataLen, bool delayed = false); // PeerTest message
|
||||
void SendPeerTest (uint8_t msg); // send or resend m_SignedData
|
||||
void HandlePeerTest (const uint8_t * buf, size_t len) override;
|
||||
void HandleAddress (const uint8_t * buf, size_t len) override;
|
||||
|
||||
void ScheduleResend (uint8_t msg);
|
||||
|
||||
private:
|
||||
|
||||
uint8_t m_MsgNumReceived, m_NumResends;
|
||||
bool m_IsConnectedRecently, m_IsStatusChanged;
|
||||
std::vector<uint8_t> m_SignedData; // for resends
|
||||
boost::asio::deadline_timer m_PeerTestResendTimer;
|
||||
boost::asio::ip::udp::endpoint m_OurEndpoint; // as seen by peer
|
||||
};
|
||||
|
||||
const int SSU2_HOLE_PUNCH_RESEND_INTERVAL = 1000; // in milliseconds
|
||||
const int SSU2_HOLE_PUNCH_RESEND_INTERVAL_VARIANCE = 500; // in milliseconds
|
||||
const int SSU2_HOLE_PUNCH_MAX_NUM_RESENDS = 3;
|
||||
|
||||
class SSU2HolePunchSession: public SSU2Session // Charlie
|
||||
{
|
||||
public:
|
||||
|
||||
SSU2HolePunchSession (SSU2Server& server, uint32_t nonce, const boost::asio::ip::udp::endpoint& remoteEndpoint,
|
||||
std::shared_ptr<const i2p::data::RouterInfo::Address> addr);
|
||||
|
||||
void SendHolePunch (const uint8_t * relayResponseBlock, size_t relayResponseBlockLen);
|
||||
|
||||
bool ProcessFirstIncomingMessage (uint64_t connID, uint8_t * buf, size_t len) override; // SessionRequest
|
||||
|
||||
private:
|
||||
|
||||
void SendHolePunch ();
|
||||
void ScheduleResend ();
|
||||
|
||||
private:
|
||||
|
||||
int m_NumResends;
|
||||
std::vector<uint8_t> m_RelayResponseBlock;
|
||||
boost::asio::deadline_timer m_HolePunchResendTimer;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -13,17 +13,12 @@
|
||||
#include "Gzip.h"
|
||||
#include "NetDb.hpp"
|
||||
#include "SSU2.h"
|
||||
#include "SSU2Session.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace transport
|
||||
{
|
||||
static inline void CreateNonce (uint64_t seqn, uint8_t * nonce)
|
||||
{
|
||||
memset (nonce, 0, 4);
|
||||
htole64buf (nonce + 4, seqn);
|
||||
}
|
||||
|
||||
void SSU2IncompleteMessage::AttachNextFragment (const uint8_t * fragment, size_t fragmentSize)
|
||||
{
|
||||
if (msg->len + fragmentSize > msg->maxLen)
|
||||
@@ -88,7 +83,7 @@ namespace transport
|
||||
std::shared_ptr<const i2p::data::RouterInfo::Address> addr, bool noise):
|
||||
TransportSession (in_RemoteRouter, SSU2_CONNECT_TIMEOUT),
|
||||
m_Server (server), m_Address (addr), m_RemoteTransports (0), m_RemotePeerTestTransports (0),
|
||||
m_DestConnID (0), m_SourceConnID (0), m_State (eSSU2SessionStateUnknown),
|
||||
m_RemoteVersion (0), m_DestConnID (0), m_SourceConnID (0), m_State (eSSU2SessionStateUnknown),
|
||||
m_SendPacketNum (0), m_ReceivePacketNum (0), m_LastDatetimeSentPacketNum (0),
|
||||
m_IsDataReceived (false), m_RTT (SSU2_UNKNOWN_RTT),
|
||||
m_MsgLocalExpirationTimeout (I2NP_MESSAGE_LOCAL_EXPIRATION_TIMEOUT_MAX),
|
||||
@@ -108,6 +103,7 @@ namespace transport
|
||||
InitNoiseXKState1 (*m_NoiseState, m_Address->s);
|
||||
m_RemoteEndpoint = boost::asio::ip::udp::endpoint (m_Address->host, m_Address->port);
|
||||
m_RemoteTransports = in_RemoteRouter->GetCompatibleTransports (false);
|
||||
m_RemoteVersion = in_RemoteRouter->GetVersion ();
|
||||
if (in_RemoteRouter->IsSSU2PeerTesting (true)) m_RemotePeerTestTransports |= i2p::data::RouterInfo::eSSU2V4;
|
||||
if (in_RemoteRouter->IsSSU2PeerTesting (false)) m_RemotePeerTestTransports |= i2p::data::RouterInfo::eSSU2V6;
|
||||
RAND_bytes ((uint8_t *)&m_DestConnID, 8);
|
||||
@@ -231,6 +227,13 @@ namespace transport
|
||||
if (m_Server.AddPendingOutgoingSession (shared_from_this ()))
|
||||
{
|
||||
m_Server.RemoveSession (GetConnID ());
|
||||
// update endpoint in profile because we know it now
|
||||
auto identity = GetRemoteIdentity ();
|
||||
if (identity)
|
||||
{
|
||||
auto profile = i2p::data::GetRouterProfile (identity->GetIdentHash ());
|
||||
if (profile) profile->SetLastEndpoint (m_RemoteEndpoint);
|
||||
}
|
||||
// connect
|
||||
LogPrint (eLogDebug, "SSU2: Connecting after introduction to ", GetIdentHashBase64());
|
||||
Connect ();
|
||||
@@ -290,6 +293,8 @@ namespace transport
|
||||
m_SentHandshakePacket.reset (nullptr);
|
||||
m_SessionConfirmedFragment.reset (nullptr);
|
||||
m_PathChallenge.reset (nullptr);
|
||||
if (!m_IntermediateQueue.empty ())
|
||||
m_SendQueue.splice (m_SendQueue.end (), m_IntermediateQueue);
|
||||
for (auto& it: m_SendQueue)
|
||||
it->Drop ();
|
||||
m_SendQueue.clear ();
|
||||
@@ -332,18 +337,19 @@ namespace transport
|
||||
SetTerminationTimeout (SSU2_TERMINATION_TIMEOUT);
|
||||
SendQueue ();
|
||||
transports.PeerConnected (shared_from_this ());
|
||||
|
||||
LogPrint(eLogDebug, "SSU2: Session with ", GetRemoteEndpoint (),
|
||||
" (", i2p::data::GetIdentHashAbbreviation (GetRemoteIdentity ()->GetIdentHash ()), ") established");
|
||||
if (m_OnEstablished)
|
||||
{
|
||||
m_OnEstablished ();
|
||||
m_OnEstablished = nullptr;
|
||||
}
|
||||
LogPrint(eLogDebug, "SSU2: Session with ", GetRemoteEndpoint (),
|
||||
" (", i2p::data::GetIdentHashAbbreviation (GetRemoteIdentity ()->GetIdentHash ()), ") established");
|
||||
}
|
||||
|
||||
void SSU2Session::Done ()
|
||||
{
|
||||
m_Server.GetService ().post (std::bind (&SSU2Session::Terminate, shared_from_this ()));
|
||||
boost::asio::post (m_Server.GetService (), std::bind (&SSU2Session::Terminate, shared_from_this ()));
|
||||
}
|
||||
|
||||
void SSU2Session::SendLocalRouterInfo (bool update)
|
||||
@@ -351,7 +357,7 @@ namespace transport
|
||||
if (update || !IsOutgoing ())
|
||||
{
|
||||
auto s = shared_from_this ();
|
||||
m_Server.GetService ().post ([s]()
|
||||
boost::asio::post (m_Server.GetService (), [s]()
|
||||
{
|
||||
if (!s->IsEstablished ()) return;
|
||||
uint8_t payload[SSU2_MAX_PACKET_SIZE];
|
||||
@@ -369,14 +375,31 @@ namespace transport
|
||||
|
||||
}
|
||||
|
||||
void SSU2Session::SendI2NPMessages (const std::vector<std::shared_ptr<I2NPMessage> >& msgs)
|
||||
void SSU2Session::SendI2NPMessages (std::list<std::shared_ptr<I2NPMessage> >& msgs)
|
||||
{
|
||||
m_Server.GetService ().post (std::bind (&SSU2Session::PostI2NPMessages, shared_from_this (), msgs));
|
||||
if (m_State == eSSU2SessionStateTerminated || msgs.empty ())
|
||||
{
|
||||
msgs.clear ();
|
||||
return;
|
||||
}
|
||||
bool empty = false;
|
||||
{
|
||||
std::lock_guard<std::mutex> l(m_IntermediateQueueMutex);
|
||||
empty = m_IntermediateQueue.empty ();
|
||||
m_IntermediateQueue.splice (m_IntermediateQueue.end (), msgs);
|
||||
}
|
||||
if (empty)
|
||||
boost::asio::post (m_Server.GetService (), std::bind (&SSU2Session::PostI2NPMessages, shared_from_this ()));
|
||||
}
|
||||
|
||||
void SSU2Session::PostI2NPMessages (std::vector<std::shared_ptr<I2NPMessage> > msgs)
|
||||
void SSU2Session::PostI2NPMessages ()
|
||||
{
|
||||
if (m_State == eSSU2SessionStateTerminated) return;
|
||||
std::list<std::shared_ptr<I2NPMessage> > msgs;
|
||||
{
|
||||
std::lock_guard<std::mutex> l(m_IntermediateQueueMutex);
|
||||
m_IntermediateQueue.swap (msgs);
|
||||
}
|
||||
uint64_t mts = i2p::util::GetMonotonicMicroseconds ();
|
||||
bool isSemiFull = false;
|
||||
if (m_SendQueue.size ())
|
||||
@@ -390,16 +413,24 @@ namespace transport
|
||||
" is semi-full (size = ", m_SendQueue.size (), ", lag = ", queueLag / 1000, ", rtt = ", (int)m_RTT, ")");
|
||||
}
|
||||
}
|
||||
for (auto it: msgs)
|
||||
{
|
||||
if (isSemiFull && it->onDrop)
|
||||
it->Drop (); // drop earlier because we can handle it
|
||||
else
|
||||
if (isSemiFull)
|
||||
{
|
||||
for (auto it: msgs)
|
||||
{
|
||||
it->SetEnqueueTime (mts);
|
||||
m_SendQueue.push_back (std::move (it));
|
||||
if (it->onDrop)
|
||||
it->Drop (); // drop earlier because we can handle it
|
||||
else
|
||||
{
|
||||
it->SetEnqueueTime (mts);
|
||||
m_SendQueue.push_back (std::move (it));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (auto& it: msgs) it->SetEnqueueTime (mts);
|
||||
m_SendQueue.splice (m_SendQueue.end (), msgs);
|
||||
}
|
||||
if (IsEstablished ())
|
||||
{
|
||||
SendQueue ();
|
||||
@@ -412,7 +443,7 @@ namespace transport
|
||||
void SSU2Session::MoveSendQueue (std::shared_ptr<SSU2Session> other)
|
||||
{
|
||||
if (!other || m_SendQueue.empty ()) return;
|
||||
std::vector<std::shared_ptr<I2NPMessage> > msgs;
|
||||
std::list<std::shared_ptr<I2NPMessage> > msgs;
|
||||
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
|
||||
for (auto it: m_SendQueue)
|
||||
if (!it->IsExpired (ts))
|
||||
@@ -421,7 +452,7 @@ namespace transport
|
||||
it->Drop ();
|
||||
m_SendQueue.clear ();
|
||||
if (!msgs.empty ())
|
||||
other->PostI2NPMessages (msgs);
|
||||
other->SendI2NPMessages (msgs);
|
||||
}
|
||||
|
||||
bool SSU2Session::SendQueue ()
|
||||
@@ -1174,6 +1205,8 @@ namespace transport
|
||||
" and actual endpoint ", m_RemoteEndpoint.address (), " from ", i2p::data::GetIdentHashAbbreviation (ri->GetIdentHash ()));
|
||||
return false;
|
||||
}
|
||||
if (!m_Address->published)
|
||||
ri->GetProfile ()->SetLastEndpoint (m_RemoteEndpoint);
|
||||
SetRemoteIdentity (ri->GetRouterIdentity ());
|
||||
AdjustMaxPayloadSize ();
|
||||
m_Server.AddSessionByRouterHash (shared_from_this ()); // we know remote router now
|
||||
@@ -1181,7 +1214,8 @@ namespace transport
|
||||
m_RemotePeerTestTransports = 0;
|
||||
if (ri->IsSSU2PeerTesting (true)) m_RemotePeerTestTransports |= i2p::data::RouterInfo::eSSU2V4;
|
||||
if (ri->IsSSU2PeerTesting (false)) m_RemotePeerTestTransports |= i2p::data::RouterInfo::eSSU2V6;
|
||||
|
||||
m_RemoteVersion = ri->GetVersion ();
|
||||
|
||||
// handle other blocks
|
||||
HandlePayload (decryptedPayload.data () + riSize + 3, decryptedPayload.size () - riSize - 3);
|
||||
Established ();
|
||||
@@ -1356,47 +1390,7 @@ namespace transport
|
||||
SendSessionRequest (token);
|
||||
return true;
|
||||
}
|
||||
|
||||
void SSU2Session::SendHolePunch (uint32_t nonce, const boost::asio::ip::udp::endpoint& ep,
|
||||
const uint8_t * introKey, uint64_t token)
|
||||
{
|
||||
// we are Charlie
|
||||
LogPrint (eLogDebug, "SSU2: Sending HolePunch to ", ep);
|
||||
Header header;
|
||||
uint8_t h[32], payload[SSU2_MAX_PACKET_SIZE];
|
||||
// fill packet
|
||||
header.h.connID = htobe64 (((uint64_t)nonce << 32) | nonce); // dest id
|
||||
RAND_bytes (header.buf + 8, 4); // random packet num
|
||||
header.h.type = eSSU2HolePunch;
|
||||
header.h.flags[0] = 2; // ver
|
||||
header.h.flags[1] = (uint8_t)i2p::context.GetNetID (); // netID
|
||||
header.h.flags[2] = 0; // flag
|
||||
memcpy (h, header.buf, 16);
|
||||
uint64_t c = ~header.h.connID;
|
||||
memcpy (h + 16, &c, 8); // source id
|
||||
RAND_bytes (h + 24, 8); // token
|
||||
// payload
|
||||
payload[0] = eSSU2BlkDateTime;
|
||||
htobe16buf (payload + 1, 4);
|
||||
htobe32buf (payload + 3, (i2p::util::GetMillisecondsSinceEpoch () + 500)/1000);
|
||||
size_t payloadSize = 7;
|
||||
payloadSize += CreateAddressBlock (payload + payloadSize, m_MaxPayloadSize - payloadSize, ep);
|
||||
payloadSize += CreateRelayResponseBlock (payload + payloadSize, m_MaxPayloadSize - payloadSize,
|
||||
eSSU2RelayResponseCodeAccept, nonce, token, ep.address ().is_v4 ());
|
||||
payloadSize += CreatePaddingBlock (payload + payloadSize, m_MaxPayloadSize - payloadSize);
|
||||
// encrypt
|
||||
uint8_t n[12];
|
||||
CreateNonce (be32toh (header.h.packetNum), n);
|
||||
i2p::crypto::AEADChaCha20Poly1305 (payload, payloadSize, h, 32, introKey, n, payload, payloadSize + 16, true);
|
||||
payloadSize += 16;
|
||||
header.ll[0] ^= CreateHeaderMask (introKey, payload + (payloadSize - 24));
|
||||
header.ll[1] ^= CreateHeaderMask (introKey, payload + (payloadSize - 12));
|
||||
memset (n, 0, 12);
|
||||
i2p::crypto::ChaCha20 (h + 16, 16, introKey, n, h + 16);
|
||||
// send
|
||||
m_Server.Send (header.buf, 16, h + 16, 16, payload, payloadSize, ep);
|
||||
}
|
||||
|
||||
|
||||
bool SSU2Session::ProcessHolePunch (uint8_t * buf, size_t len)
|
||||
{
|
||||
// we are Alice
|
||||
@@ -1461,7 +1455,7 @@ namespace transport
|
||||
uint8_t nonce[12];
|
||||
CreateNonce (m_SendPacketNum, nonce);
|
||||
uint8_t payload[SSU2_MAX_PACKET_SIZE];
|
||||
i2p::crypto::AEADChaCha20Poly1305 (buf, len, header.buf, 16, m_KeyDataSend, nonce, payload, SSU2_MAX_PACKET_SIZE, true);
|
||||
m_Server.AEADChaCha20Poly1305Encrypt (buf, len, header.buf, 16, m_KeyDataSend, nonce, payload, SSU2_MAX_PACKET_SIZE);
|
||||
header.ll[0] ^= CreateHeaderMask (m_Address->i, payload + (len - 8));
|
||||
header.ll[1] ^= CreateHeaderMask (m_KeyDataSend + 32, payload + (len + 4));
|
||||
m_Server.Send (header.buf, 16, payload, len + 16, m_RemoteEndpoint);
|
||||
@@ -1501,8 +1495,8 @@ namespace transport
|
||||
uint32_t packetNum = be32toh (header.h.packetNum);
|
||||
uint8_t nonce[12];
|
||||
CreateNonce (packetNum, nonce);
|
||||
if (!i2p::crypto::AEADChaCha20Poly1305 (buf + 16, payloadSize, header.buf, 16,
|
||||
m_KeyDataReceive, nonce, payload, payloadSize, false))
|
||||
if (!m_Server.AEADChaCha20Poly1305Decrypt (buf + 16, payloadSize, header.buf, 16,
|
||||
m_KeyDataReceive, nonce, payload, payloadSize))
|
||||
{
|
||||
LogPrint (eLogWarning, "SSU2: Data AEAD verification failed ");
|
||||
return;
|
||||
@@ -1951,48 +1945,57 @@ namespace transport
|
||||
void SSU2Session::HandleRelayRequest (const uint8_t * buf, size_t len)
|
||||
{
|
||||
// we are Bob
|
||||
auto mts = i2p::util::GetMillisecondsSinceEpoch ();
|
||||
uint32_t nonce = bufbe32toh (buf + 1); // nonce
|
||||
uint32_t relayTag = bufbe32toh (buf + 5); // relay tag
|
||||
auto session = m_Server.FindRelaySession (relayTag);
|
||||
if (!session)
|
||||
{
|
||||
LogPrint (eLogWarning, "SSU2: RelayRequest session with relay tag ", relayTag, " not found");
|
||||
// send relay response back to Alice
|
||||
uint8_t payload[SSU2_MAX_PACKET_SIZE];
|
||||
size_t payloadSize = CreateRelayResponseBlock (payload, m_MaxPayloadSize,
|
||||
eSSU2RelayResponseCodeBobRelayTagNotFound, bufbe32toh (buf + 1), 0, false);
|
||||
payloadSize += CreatePaddingBlock (payload + payloadSize, m_MaxPayloadSize - payloadSize);
|
||||
SendData (payload, payloadSize);
|
||||
auto packet = m_Server.GetSentPacketsPool ().AcquireShared ();
|
||||
packet->payloadSize = CreateAckBlock (packet->payload, m_MaxPayloadSize);
|
||||
packet->payloadSize += CreateRelayResponseBlock (packet->payload + packet->payloadSize, m_MaxPayloadSize - packet->payloadSize,
|
||||
eSSU2RelayResponseCodeBobRelayTagNotFound, nonce, 0, false);
|
||||
packet->payloadSize += CreatePaddingBlock (packet->payload + packet->payloadSize, m_MaxPayloadSize - packet->payloadSize);
|
||||
uint32_t packetNum = SendData (packet->payload, packet->payloadSize);
|
||||
if (m_RemoteVersion >= SSU2_MIN_RELAY_RESPONSE_RESEND_VERSION)
|
||||
{
|
||||
// sometimes Alice doesn't ack this RelayResponse in older versions
|
||||
packet->sendTime = mts;
|
||||
m_SentPackets.emplace (packetNum, packet);
|
||||
}
|
||||
return;
|
||||
}
|
||||
auto mts = i2p::util::GetMillisecondsSinceEpoch ();
|
||||
session->m_RelaySessions.emplace (bufbe32toh (buf + 1), // nonce
|
||||
std::make_pair (shared_from_this (), mts/1000) );
|
||||
if (session->m_RelaySessions.emplace (nonce, std::make_pair (shared_from_this (), mts/1000)).second)
|
||||
{
|
||||
// send relay intro to Charlie
|
||||
auto r = i2p::data::netdb.FindRouter (GetRemoteIdentity ()->GetIdentHash ()); // Alice's RI
|
||||
if (r && (r->IsUnreachable () || !i2p::data::netdb.PopulateRouterInfoBuffer (r))) r = nullptr;
|
||||
if (!r) LogPrint (eLogWarning, "SSU2: RelayRequest Alice's router info not found");
|
||||
|
||||
// send relay intro to Charlie
|
||||
auto r = i2p::data::netdb.FindRouter (GetRemoteIdentity ()->GetIdentHash ()); // Alice's RI
|
||||
if (r && (r->IsUnreachable () || !i2p::data::netdb.PopulateRouterInfoBuffer (r))) r = nullptr;
|
||||
if (!r) LogPrint (eLogWarning, "SSU2: RelayRequest Alice's router info not found");
|
||||
|
||||
auto packet = m_Server.GetSentPacketsPool ().AcquireShared ();
|
||||
packet->payloadSize = r ? CreateRouterInfoBlock (packet->payload, m_MaxPayloadSize - len - 32, r) : 0;
|
||||
if (!packet->payloadSize && r)
|
||||
session->SendFragmentedMessage (CreateDatabaseStoreMsg (r));
|
||||
packet->payloadSize += CreateRelayIntroBlock (packet->payload + packet->payloadSize, m_MaxPayloadSize - packet->payloadSize, buf + 1, len -1);
|
||||
if (packet->payloadSize < m_MaxPayloadSize)
|
||||
packet->payloadSize += CreatePaddingBlock (packet->payload + packet->payloadSize, m_MaxPayloadSize - packet->payloadSize);
|
||||
uint32_t packetNum = session->SendData (packet->payload, packet->payloadSize);
|
||||
packet->sendTime = mts;
|
||||
// Charlie always responds with RelayResponse
|
||||
session->m_SentPackets.emplace (packetNum, packet);
|
||||
auto packet = m_Server.GetSentPacketsPool ().AcquireShared ();
|
||||
packet->payloadSize = r ? CreateRouterInfoBlock (packet->payload, m_MaxPayloadSize - len - 32, r) : 0;
|
||||
if (!packet->payloadSize && r)
|
||||
session->SendFragmentedMessage (CreateDatabaseStoreMsg (r));
|
||||
packet->payloadSize += CreateRelayIntroBlock (packet->payload + packet->payloadSize, m_MaxPayloadSize - packet->payloadSize, buf + 1, len -1);
|
||||
if (packet->payloadSize < m_MaxPayloadSize)
|
||||
packet->payloadSize += CreatePaddingBlock (packet->payload + packet->payloadSize, m_MaxPayloadSize - packet->payloadSize);
|
||||
uint32_t packetNum = session->SendData (packet->payload, packet->payloadSize);
|
||||
packet->sendTime = mts;
|
||||
// Charlie always responds with RelayResponse
|
||||
session->m_SentPackets.emplace (packetNum, packet);
|
||||
}
|
||||
else
|
||||
LogPrint (eLogInfo, "SSU2: Relay request nonce ", nonce, " already exists. Ignore");
|
||||
}
|
||||
|
||||
void SSU2Session::HandleRelayIntro (const uint8_t * buf, size_t len, int attempts)
|
||||
{
|
||||
// we are Charlie
|
||||
auto mts = i2p::util::GetMillisecondsSinceEpoch ();
|
||||
SSU2RelayResponseCode code = eSSU2RelayResponseCodeAccept;
|
||||
uint64_t token = 0;
|
||||
bool isV4 = false;
|
||||
boost::asio::ip::udp::endpoint ep;
|
||||
std::shared_ptr<const i2p::data::RouterInfo::Address> addr;
|
||||
auto r = i2p::data::netdb.FindRouter (buf + 1); // Alice
|
||||
if (r)
|
||||
{
|
||||
@@ -2005,31 +2008,29 @@ namespace transport
|
||||
s.Insert (buf + 47, asz); // Alice Port, Alice IP
|
||||
if (s.Verify (r->GetIdentity (), buf + 47 + asz))
|
||||
{
|
||||
// send HolePunch
|
||||
boost::asio::ip::udp::endpoint ep;
|
||||
// obtain and check endpoint and address for HolePunch
|
||||
if (ExtractEndpoint (buf + 47, asz, ep))
|
||||
{
|
||||
std::shared_ptr<const i2p::data::RouterInfo::Address> addr;
|
||||
if (!ep.address ().is_unspecified () && ep.port ())
|
||||
addr = ep.address ().is_v6 () ? r->GetSSU2V6Address () : r->GetSSU2V4Address ();
|
||||
if (addr)
|
||||
{
|
||||
if (m_Server.IsSupported (ep.address ()))
|
||||
{
|
||||
token = m_Server.GetIncomingToken (ep);
|
||||
isV4 = ep.address ().is_v4 ();
|
||||
SendHolePunch (bufbe32toh (buf + 33), ep, addr->i, token);
|
||||
m_Server.AddConnectedRecently (ep, mts/1000);
|
||||
addr = ep.address ().is_v6 () ? r->GetSSU2V6Address () : r->GetSSU2V4Address ();
|
||||
if (!addr)
|
||||
{
|
||||
LogPrint (eLogWarning, "SSU2: RelayIntro address for endpoint not found");
|
||||
code = eSSU2RelayResponseCodeCharlieAliceIsUnknown;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint (eLogWarning, "SSU2: RelayIntro unsupported address");
|
||||
code = eSSU2RelayResponseCodeCharlieUnsupportedAddress;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint (eLogWarning, "SSU2: RelayIntro unknown address");
|
||||
LogPrint (eLogWarning, "SSU2: RelayIntro invalid endpoint");
|
||||
code = eSSU2RelayResponseCodeCharlieAliceIsUnknown;
|
||||
}
|
||||
}
|
||||
@@ -2051,7 +2052,7 @@ namespace transport
|
||||
auto vec = std::make_shared<std::vector<uint8_t> >(len);
|
||||
memcpy (vec->data (), buf, len);
|
||||
auto s = shared_from_this ();
|
||||
m_Server.GetService ().post ([s, vec, attempts]()
|
||||
boost::asio::post (m_Server.GetService (), [s, vec, attempts]()
|
||||
{
|
||||
LogPrint (eLogDebug, "SSU2: RelayIntro attempt ", attempts + 1);
|
||||
s->HandleRelayIntro (vec->data (), vec->size (), attempts + 1);
|
||||
@@ -2065,14 +2066,29 @@ namespace transport
|
||||
}
|
||||
// send relay response to Bob
|
||||
auto packet = m_Server.GetSentPacketsPool ().AcquireShared ();
|
||||
uint32_t nonce = bufbe32toh (buf + 33);
|
||||
packet->payloadSize = CreateRelayResponseBlock (packet->payload, m_MaxPayloadSize,
|
||||
code, bufbe32toh (buf + 33), token, isV4);
|
||||
code, nonce, m_Server.GetIncomingToken (ep), ep.address ().is_v4 ());
|
||||
if (code == eSSU2RelayResponseCodeAccept && addr)
|
||||
{
|
||||
// send HolePunch
|
||||
auto holePunchSession = std::make_shared<SSU2HolePunchSession>(m_Server, nonce, ep, addr);
|
||||
if (m_Server.AddSession (holePunchSession))
|
||||
holePunchSession->SendHolePunch (packet->payload, packet->payloadSize); // relay response block
|
||||
else
|
||||
{
|
||||
LogPrint (eLogInfo, "SSU2: Relay intro nonce ", nonce, " already exists. Ignore");
|
||||
return;
|
||||
}
|
||||
}
|
||||
packet->payloadSize += CreatePaddingBlock (packet->payload + packet->payloadSize, m_MaxPayloadSize - packet->payloadSize);
|
||||
/*uint32_t packetNum = */SendData (packet->payload, packet->payloadSize);
|
||||
// sometimes Bob doesn't ack this RelayResponse
|
||||
// TODO: uncomment line below once the problem is resolved
|
||||
//packet->sendTime = mts;
|
||||
//m_SentPackets.emplace (packetNum, packet);
|
||||
uint32_t packetNum = SendData (packet->payload, packet->payloadSize);
|
||||
if (m_RemoteVersion >= SSU2_MIN_RELAY_RESPONSE_RESEND_VERSION)
|
||||
{
|
||||
// sometimes Bob doesn't ack this RelayResponse in older versions
|
||||
packet->sendTime = i2p::util::GetMillisecondsSinceEpoch ();
|
||||
m_SentPackets.emplace (packetNum, packet);
|
||||
}
|
||||
}
|
||||
|
||||
void SSU2Session::HandleRelayResponse (const uint8_t * buf, size_t len)
|
||||
@@ -2107,11 +2123,13 @@ namespace transport
|
||||
memcpy (payload + 3, buf, len); // forward to Alice as is
|
||||
packet->payloadSize = len + 3;
|
||||
packet->payloadSize += CreatePaddingBlock (payload + packet->payloadSize, m_MaxPayloadSize - packet->payloadSize);
|
||||
/*uint32_t packetNum = */it->second.first->SendData (packet->payload, packet->payloadSize);
|
||||
// sometimes Alice doesn't ack this RelayResponse
|
||||
// TODO: uncomment line below once the problem is resolved
|
||||
//packet->sendTime = i2p::util::GetMillisecondsSinceEpoch ();
|
||||
//it->second.first->m_SentPackets.emplace (packetNum, packet);
|
||||
uint32_t packetNum = it->second.first->SendData (packet->payload, packet->payloadSize);
|
||||
if (m_RemoteVersion >= SSU2_MIN_RELAY_RESPONSE_RESEND_VERSION)
|
||||
{
|
||||
// sometimes Alice doesn't ack this RelayResponse in older versions
|
||||
packet->sendTime = i2p::util::GetMillisecondsSinceEpoch ();
|
||||
it->second.first->m_SentPackets.emplace (packetNum, packet);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -2179,29 +2197,33 @@ namespace transport
|
||||
GetRemoteIdentity ()->GetIdentHash ());
|
||||
if (session) // session with Charlie
|
||||
{
|
||||
m_Server.AddPeerTest (nonce, shared_from_this (), ts/1000);
|
||||
auto packet = m_Server.GetSentPacketsPool ().AcquireShared ();
|
||||
// Alice's RouterInfo
|
||||
auto r = i2p::data::netdb.FindRouter (GetRemoteIdentity ()->GetIdentHash ());
|
||||
if (r && (r->IsUnreachable () || !i2p::data::netdb.PopulateRouterInfoBuffer (r))) r = nullptr;
|
||||
packet->payloadSize = r ? CreateRouterInfoBlock (packet->payload, m_MaxPayloadSize - len - 32, r) : 0;
|
||||
if (!packet->payloadSize && r)
|
||||
session->SendFragmentedMessage (CreateDatabaseStoreMsg (r));
|
||||
if (packet->payloadSize + len + 48 > m_MaxPayloadSize)
|
||||
{
|
||||
// doesn't fit one message, send RouterInfo in separate message
|
||||
if (m_Server.AddPeerTest (nonce, shared_from_this (), ts/1000))
|
||||
{
|
||||
auto packet = m_Server.GetSentPacketsPool ().AcquireShared ();
|
||||
// Alice's RouterInfo
|
||||
auto r = i2p::data::netdb.FindRouter (GetRemoteIdentity ()->GetIdentHash ());
|
||||
if (r && (r->IsUnreachable () || !i2p::data::netdb.PopulateRouterInfoBuffer (r))) r = nullptr;
|
||||
packet->payloadSize = r ? CreateRouterInfoBlock (packet->payload, m_MaxPayloadSize - len - 32, r) : 0;
|
||||
if (!packet->payloadSize && r)
|
||||
session->SendFragmentedMessage (CreateDatabaseStoreMsg (r));
|
||||
if (packet->payloadSize + len + 48 > m_MaxPayloadSize)
|
||||
{
|
||||
// doesn't fit one message, send RouterInfo in separate message
|
||||
uint32_t packetNum = session->SendData (packet->payload, packet->payloadSize, SSU2_FLAG_IMMEDIATE_ACK_REQUESTED);
|
||||
packet->sendTime = ts;
|
||||
session->m_SentPackets.emplace (packetNum, packet);
|
||||
packet = m_Server.GetSentPacketsPool ().AcquireShared (); // new packet
|
||||
}
|
||||
// PeerTest to Charlie
|
||||
packet->payloadSize += CreatePeerTestBlock (packet->payload + packet->payloadSize, m_MaxPayloadSize - packet->payloadSize, 2,
|
||||
eSSU2PeerTestCodeAccept, GetRemoteIdentity ()->GetIdentHash (), buf + offset, len - offset);
|
||||
packet->payloadSize += CreatePaddingBlock (packet->payload + packet->payloadSize, m_MaxPayloadSize - packet->payloadSize);
|
||||
uint32_t packetNum = session->SendData (packet->payload, packet->payloadSize, SSU2_FLAG_IMMEDIATE_ACK_REQUESTED);
|
||||
packet->sendTime = ts;
|
||||
session->m_SentPackets.emplace (packetNum, packet);
|
||||
packet = m_Server.GetSentPacketsPool ().AcquireShared (); // new packet
|
||||
}
|
||||
// PeerTest to Charlie
|
||||
packet->payloadSize += CreatePeerTestBlock (packet->payload + packet->payloadSize, m_MaxPayloadSize - packet->payloadSize, 2,
|
||||
eSSU2PeerTestCodeAccept, GetRemoteIdentity ()->GetIdentHash (), buf + offset, len - offset);
|
||||
packet->payloadSize += CreatePaddingBlock (packet->payload + packet->payloadSize, m_MaxPayloadSize - packet->payloadSize);
|
||||
uint32_t packetNum = session->SendData (packet->payload, packet->payloadSize, SSU2_FLAG_IMMEDIATE_ACK_REQUESTED);
|
||||
packet->sendTime = ts;
|
||||
session->m_SentPackets.emplace (packetNum, packet);
|
||||
else
|
||||
LogPrint (eLogInfo, "SSU2: Peer test 1 nonce ", nonce, " already exists. Ignored");
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -2343,16 +2365,16 @@ namespace transport
|
||||
{
|
||||
session->SetRemoteIdentity (r->GetIdentity ());
|
||||
auto addr = r->GetSSU2Address (m_Address->IsV4 ());
|
||||
if (addr)
|
||||
if (addr && addr->IsPeerTesting ())
|
||||
{
|
||||
if (session->GetMsgNumReceived () >= 5)
|
||||
{
|
||||
// msg 5 already received
|
||||
// msg 5 already received and we know remote endpoint
|
||||
if (session->GetMsgNumReceived () == 5)
|
||||
{
|
||||
if (!session->IsConnectedRecently ())
|
||||
SetRouterStatus (eRouterStatusOK);
|
||||
// send msg 6
|
||||
// send msg 6 immeditely
|
||||
session->SendPeerTest (6, buf + offset, len - offset, addr);
|
||||
}
|
||||
else
|
||||
@@ -2363,6 +2385,12 @@ namespace transport
|
||||
session->m_Address = addr;
|
||||
if (GetTestingState ())
|
||||
{
|
||||
// schedule msg 6 with delay
|
||||
if (!addr->host.is_unspecified () && addr->port)
|
||||
{
|
||||
session->SetRemoteEndpoint (boost::asio::ip::udp::endpoint (addr->host, addr->port));
|
||||
session->SendPeerTest (6, buf + offset, len - offset, addr, true);
|
||||
}
|
||||
SetTestingState (false);
|
||||
if (GetRouterStatus () != eRouterStatusFirewalled && addr->IsPeerTesting ())
|
||||
{
|
||||
@@ -2380,7 +2408,7 @@ namespace transport
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint (eLogWarning, "SSU2: Peer test 4 address not found");
|
||||
LogPrint (eLogWarning, "SSU2: Peer test 4 address not found or not supported");
|
||||
session->Done ();
|
||||
}
|
||||
}
|
||||
@@ -3062,7 +3090,7 @@ namespace transport
|
||||
{
|
||||
if (ts > it->second.second + SSU2_RELAY_NONCE_EXPIRATION_TIMEOUT)
|
||||
{
|
||||
LogPrint (eLogWarning, "SSU2: Relay nonce ", it->first, " was not responded in ", SSU2_RELAY_NONCE_EXPIRATION_TIMEOUT, " seconds, deleted");
|
||||
LogPrint (eLogInfo, "SSU2: Relay nonce ", it->first, " was not responded in ", SSU2_RELAY_NONCE_EXPIRATION_TIMEOUT, " seconds, deleted");
|
||||
it = m_RelaySessions.erase (it);
|
||||
}
|
||||
else
|
||||
@@ -3086,216 +3114,5 @@ namespace transport
|
||||
else if (!sent && !m_SentPackets.empty ()) // if only acks received, nothing sent and we still have something to resend
|
||||
Resend (i2p::util::GetMillisecondsSinceEpoch ()); // than right time to resend
|
||||
}
|
||||
|
||||
SSU2PeerTestSession::SSU2PeerTestSession (SSU2Server& server, uint64_t sourceConnID, uint64_t destConnID):
|
||||
SSU2Session (server, nullptr, nullptr, false),
|
||||
m_MsgNumReceived (0), m_NumResends (0),m_IsConnectedRecently (false), m_IsStatusChanged (false),
|
||||
m_PeerTestResendTimer (server.GetService ())
|
||||
{
|
||||
if (!sourceConnID) sourceConnID = ~destConnID;
|
||||
if (!destConnID) destConnID = ~sourceConnID;
|
||||
SetSourceConnID (sourceConnID);
|
||||
SetDestConnID (destConnID);
|
||||
SetState (eSSU2SessionStatePeerTest);
|
||||
SetTerminationTimeout (SSU2_PEER_TEST_EXPIRATION_TIMEOUT);
|
||||
}
|
||||
|
||||
bool SSU2PeerTestSession::ProcessPeerTest (uint8_t * buf, size_t len)
|
||||
{
|
||||
// we are Alice or Charlie, msgs 5,6,7
|
||||
Header header;
|
||||
memcpy (header.buf, buf, 16);
|
||||
header.ll[0] ^= CreateHeaderMask (i2p::context.GetSSU2IntroKey (), buf + (len - 24));
|
||||
header.ll[1] ^= CreateHeaderMask (i2p::context.GetSSU2IntroKey (), buf + (len - 12));
|
||||
if (header.h.type != eSSU2PeerTest)
|
||||
{
|
||||
LogPrint (eLogWarning, "SSU2: Unexpected message type ", (int)header.h.type, " instead ", (int)eSSU2PeerTest);
|
||||
return false;
|
||||
}
|
||||
if (len < 48)
|
||||
{
|
||||
LogPrint (eLogWarning, "SSU2: PeerTest message too short ", len);
|
||||
return false;
|
||||
}
|
||||
uint8_t nonce[12] = {0};
|
||||
uint64_t headerX[2]; // sourceConnID, token
|
||||
i2p::crypto::ChaCha20 (buf + 16, 16, i2p::context.GetSSU2IntroKey (), nonce, (uint8_t *)headerX);
|
||||
SetDestConnID (headerX[0]);
|
||||
// decrypt and handle payload
|
||||
uint8_t * payload = buf + 32;
|
||||
CreateNonce (be32toh (header.h.packetNum), nonce);
|
||||
uint8_t h[32];
|
||||
memcpy (h, header.buf, 16);
|
||||
memcpy (h + 16, &headerX, 16);
|
||||
if (!i2p::crypto::AEADChaCha20Poly1305 (payload, len - 48, h, 32,
|
||||
i2p::context.GetSSU2IntroKey (), nonce, payload, len - 48, false))
|
||||
{
|
||||
LogPrint (eLogWarning, "SSU2: PeerTest AEAD verification failed ");
|
||||
return false;
|
||||
}
|
||||
HandlePayload (payload, len - 48);
|
||||
SetIsDataReceived (false);
|
||||
return true;
|
||||
}
|
||||
|
||||
void SSU2PeerTestSession::HandlePeerTest (const uint8_t * buf, size_t len)
|
||||
{
|
||||
// msgs 5-7
|
||||
if (len < 8) return;
|
||||
uint8_t msg = buf[0];
|
||||
if (msg <= m_MsgNumReceived)
|
||||
{
|
||||
LogPrint (eLogDebug, "SSU2: PeerTest msg num ", msg, " received after ", m_MsgNumReceived, ". Ignored");
|
||||
return;
|
||||
}
|
||||
size_t offset = 3; // points to signed data after msg + code + flag
|
||||
uint32_t nonce = bufbe32toh (buf + offset + 1); // 1 - ver
|
||||
switch (msg) // msg
|
||||
{
|
||||
case 5: // Alice from Charlie 1
|
||||
{
|
||||
if (htobe64 (((uint64_t)nonce << 32) | nonce) == GetSourceConnID ())
|
||||
{
|
||||
m_IsConnectedRecently = GetServer ().IsConnectedRecently (GetRemoteEndpoint ());
|
||||
if (GetAddress ())
|
||||
{
|
||||
if (!m_IsConnectedRecently)
|
||||
SetRouterStatus (eRouterStatusOK);
|
||||
else if (m_IsStatusChanged && GetRouterStatus () == eRouterStatusFirewalled)
|
||||
SetRouterStatus (eRouterStatusUnknown);
|
||||
SendPeerTest (6, buf + offset, len - offset);
|
||||
}
|
||||
}
|
||||
else
|
||||
LogPrint (eLogWarning, "SSU2: Peer test 5 nonce mismatch ", nonce, " connID=", GetSourceConnID ());
|
||||
break;
|
||||
}
|
||||
case 6: // Charlie from Alice
|
||||
{
|
||||
m_PeerTestResendTimer.cancel (); // no more msg 5 resends
|
||||
if (GetAddress ())
|
||||
SendPeerTest (7, buf + offset, len - offset);
|
||||
else
|
||||
LogPrint (eLogWarning, "SSU2: Unknown address for peer test 6");
|
||||
GetServer ().AddConnectedRecently (GetRemoteEndpoint (), i2p::util::GetSecondsSinceEpoch ());
|
||||
GetServer ().RequestRemoveSession (GetConnID ());
|
||||
break;
|
||||
}
|
||||
case 7: // Alice from Charlie 2
|
||||
{
|
||||
m_PeerTestResendTimer.cancel (); // no more msg 6 resends
|
||||
auto addr = GetAddress ();
|
||||
if (addr && addr->IsV6 ())
|
||||
i2p::context.SetStatusV6 (eRouterStatusOK); // set status OK for ipv6 even if from SSU2
|
||||
GetServer ().AddConnectedRecently (GetRemoteEndpoint (), i2p::util::GetSecondsSinceEpoch ());
|
||||
GetServer ().RequestRemoveSession (GetConnID ());
|
||||
break;
|
||||
}
|
||||
default:
|
||||
LogPrint (eLogWarning, "SSU2: PeerTest unexpected msg num ", msg);
|
||||
return;
|
||||
}
|
||||
m_MsgNumReceived = msg;
|
||||
}
|
||||
|
||||
void SSU2PeerTestSession::SendPeerTest (uint8_t msg)
|
||||
{
|
||||
auto addr = GetAddress ();
|
||||
if (!addr) return;
|
||||
Header header;
|
||||
uint8_t h[32], payload[SSU2_MAX_PACKET_SIZE];
|
||||
// fill packet
|
||||
header.h.connID = GetDestConnID (); // dest id
|
||||
RAND_bytes (header.buf + 8, 4); // random packet num
|
||||
header.h.type = eSSU2PeerTest;
|
||||
header.h.flags[0] = 2; // ver
|
||||
header.h.flags[1] = (uint8_t)i2p::context.GetNetID (); // netID
|
||||
header.h.flags[2] = 0; // flag
|
||||
memcpy (h, header.buf, 16);
|
||||
htobuf64 (h + 16, GetSourceConnID ()); // source id
|
||||
// payload
|
||||
payload[0] = eSSU2BlkDateTime;
|
||||
htobe16buf (payload + 1, 4);
|
||||
htobe32buf (payload + 3, (i2p::util::GetMillisecondsSinceEpoch () + 500)/1000);
|
||||
size_t payloadSize = 7;
|
||||
if (msg == 6 || msg == 7)
|
||||
payloadSize += CreateAddressBlock (payload + payloadSize, GetMaxPayloadSize () - payloadSize, GetRemoteEndpoint ());
|
||||
payloadSize += CreatePeerTestBlock (payload + payloadSize, GetMaxPayloadSize () - payloadSize,
|
||||
msg, eSSU2PeerTestCodeAccept, nullptr, m_SignedData.data (), m_SignedData.size ());
|
||||
payloadSize += CreatePaddingBlock (payload + payloadSize, GetMaxPayloadSize () - payloadSize);
|
||||
// encrypt
|
||||
uint8_t n[12];
|
||||
CreateNonce (be32toh (header.h.packetNum), n);
|
||||
i2p::crypto::AEADChaCha20Poly1305 (payload, payloadSize, h, 32, addr->i, n, payload, payloadSize + 16, true);
|
||||
payloadSize += 16;
|
||||
header.ll[0] ^= CreateHeaderMask (addr->i, payload + (payloadSize - 24));
|
||||
header.ll[1] ^= CreateHeaderMask (addr->i, payload + (payloadSize - 12));
|
||||
memset (n, 0, 12);
|
||||
i2p::crypto::ChaCha20 (h + 16, 16, addr->i, n, h + 16);
|
||||
// send
|
||||
GetServer ().Send (header.buf, 16, h + 16, 16, payload, payloadSize, GetRemoteEndpoint ());
|
||||
}
|
||||
|
||||
void SSU2PeerTestSession::SendPeerTest (uint8_t msg, const uint8_t * signedData, size_t signedDataLen)
|
||||
{
|
||||
#if __cplusplus >= 202002L // C++20
|
||||
m_SignedData.assign (signedData, signedData + signedDataLen);
|
||||
#else
|
||||
m_SignedData.resize (signedDataLen);
|
||||
memcpy (m_SignedData.data (), signedData, signedDataLen);
|
||||
#endif
|
||||
SendPeerTest (msg);
|
||||
// schedule resend for msgs 5 or 6
|
||||
if (msg == 5 || msg == 6)
|
||||
ScheduleResend ();
|
||||
}
|
||||
|
||||
void SSU2PeerTestSession::SendPeerTest (uint8_t msg, const uint8_t * signedData, size_t signedDataLen,
|
||||
std::shared_ptr<const i2p::data::RouterInfo::Address> addr)
|
||||
{
|
||||
if (!addr) return;
|
||||
SetAddress (addr);
|
||||
SendPeerTest (msg, signedData, signedDataLen);
|
||||
}
|
||||
|
||||
void SSU2PeerTestSession::Connect ()
|
||||
{
|
||||
LogPrint (eLogError, "SSU2: Can't connect peer test session");
|
||||
}
|
||||
|
||||
bool SSU2PeerTestSession::ProcessFirstIncomingMessage (uint64_t connID, uint8_t * buf, size_t len)
|
||||
{
|
||||
LogPrint (eLogError, "SSU2: Can't handle incoming message in peer test session");
|
||||
return false;
|
||||
}
|
||||
|
||||
void SSU2PeerTestSession::ScheduleResend ()
|
||||
{
|
||||
if (m_NumResends < SSU2_PEER_TEST_MAX_NUM_RESENDS)
|
||||
{
|
||||
m_PeerTestResendTimer.expires_from_now (boost::posix_time::milliseconds(
|
||||
SSU2_PEER_TEST_RESEND_INTERVAL + GetServer ().GetRng ()() % SSU2_PEER_TEST_RESEND_INTERVAL_VARIANCE));
|
||||
std::weak_ptr<SSU2PeerTestSession> s(std::static_pointer_cast<SSU2PeerTestSession>(shared_from_this ()));
|
||||
m_PeerTestResendTimer.async_wait ([s](const boost::system::error_code& ecode)
|
||||
{
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
{
|
||||
auto s1 = s.lock ();
|
||||
if (s1)
|
||||
{
|
||||
int msg = 0;
|
||||
if (s1->m_MsgNumReceived < 6)
|
||||
msg = (s1->m_MsgNumReceived == 5) ? 6 : 5;
|
||||
if (msg) // 5 or 6
|
||||
{
|
||||
s1->SendPeerTest (msg);
|
||||
s1->ScheduleResend ();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
m_NumResends++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include <set>
|
||||
#include <list>
|
||||
#include <boost/asio.hpp>
|
||||
#include "version.h"
|
||||
#include "Crypto.h"
|
||||
#include "RouterInfo.h"
|
||||
#include "RouterContext.h"
|
||||
@@ -55,6 +56,7 @@ namespace transport
|
||||
const int SSU2_MAX_NUM_ACK_RANGES = 32; // to send
|
||||
const uint8_t SSU2_MAX_NUM_FRAGMENTS = 64;
|
||||
const int SSU2_SEND_DATETIME_NUM_PACKETS = 256;
|
||||
const int SSU2_MIN_RELAY_RESPONSE_RESEND_VERSION = MAKE_VERSION_NUMBER(0, 9, 64); // 0.9.64
|
||||
|
||||
// flags
|
||||
const uint8_t SSU2_FLAG_IMMEDIATE_ACK_REQUESTED = 0x01;
|
||||
@@ -112,6 +114,7 @@ namespace transport
|
||||
eSSU2SessionStateTerminated,
|
||||
eSSU2SessionStateFailed,
|
||||
eSSU2SessionStateIntroduced,
|
||||
eSSU2SessionStateHolePunch,
|
||||
eSSU2SessionStatePeerTest,
|
||||
eSSU2SessionStateTokenRequestReceived
|
||||
};
|
||||
@@ -258,7 +261,7 @@ namespace transport
|
||||
void FlushData ();
|
||||
void Done () override;
|
||||
void SendLocalRouterInfo (bool update) override;
|
||||
void SendI2NPMessages (const std::vector<std::shared_ptr<I2NPMessage> >& msgs) override;
|
||||
void SendI2NPMessages (std::list<std::shared_ptr<I2NPMessage> >& msgs) override;
|
||||
void MoveSendQueue (std::shared_ptr<SSU2Session> other);
|
||||
uint32_t GetRelayTag () const override { return m_RelayTag; };
|
||||
size_t Resend (uint64_t ts); // return number of resent packets
|
||||
@@ -295,6 +298,8 @@ namespace transport
|
||||
size_t CreateAddressBlock (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& ep);
|
||||
size_t CreatePaddingBlock (uint8_t * buf, size_t len, size_t minSize = 0);
|
||||
size_t CreatePeerTestBlock (uint8_t * buf, size_t len, uint8_t msg, SSU2PeerTestCode code, const uint8_t * routerHash, const uint8_t * signedData, size_t signedDataLen);
|
||||
|
||||
bool ExtractEndpoint (const uint8_t * buf, size_t size, boost::asio::ip::udp::endpoint& ep);
|
||||
|
||||
private:
|
||||
|
||||
@@ -302,7 +307,7 @@ namespace transport
|
||||
void Established ();
|
||||
void ScheduleConnectTimer ();
|
||||
void HandleConnectTimer (const boost::system::error_code& ecode);
|
||||
void PostI2NPMessages (std::vector<std::shared_ptr<I2NPMessage> > msgs);
|
||||
void PostI2NPMessages ();
|
||||
bool SendQueue (); // returns true if ack block was sent
|
||||
bool SendFragmentedMessage (std::shared_ptr<I2NPMessage> msg);
|
||||
void ResendHandshakePacket ();
|
||||
@@ -320,7 +325,6 @@ namespace transport
|
||||
uint32_t SendData (const uint8_t * buf, size_t len, uint8_t flags = 0); // returns packet num
|
||||
void SendQuickAck ();
|
||||
void SendTermination ();
|
||||
void SendHolePunch (uint32_t nonce, const boost::asio::ip::udp::endpoint& ep, const uint8_t * introKey, uint64_t token);
|
||||
void SendPathResponse (const uint8_t * data, size_t len);
|
||||
void SendPathChallenge ();
|
||||
|
||||
@@ -328,8 +332,7 @@ namespace transport
|
||||
void HandleRouterInfo (const uint8_t * buf, size_t len);
|
||||
void HandleAck (const uint8_t * buf, size_t len);
|
||||
void HandleAckRange (uint32_t firstPacketNum, uint32_t lastPacketNum, uint64_t ts);
|
||||
void HandleAddress (const uint8_t * buf, size_t len);
|
||||
bool ExtractEndpoint (const uint8_t * buf, size_t size, boost::asio::ip::udp::endpoint& ep);
|
||||
virtual void HandleAddress (const uint8_t * buf, size_t len);
|
||||
size_t CreateEndpoint (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& ep);
|
||||
std::shared_ptr<const i2p::data::RouterInfo::Address> FindLocalAddress () const;
|
||||
void AdjustMaxPayloadSize ();
|
||||
@@ -353,6 +356,7 @@ namespace transport
|
||||
size_t CreateFollowOnFragmentBlock (uint8_t * buf, size_t len, std::shared_ptr<I2NPMessage> msg, uint8_t& fragmentNum, uint32_t msgID);
|
||||
size_t CreateRelayIntroBlock (uint8_t * buf, size_t len, const uint8_t * introData, size_t introDataLen);
|
||||
size_t CreateRelayResponseBlock (uint8_t * buf, size_t len, SSU2RelayResponseCode code, uint32_t nonce, uint64_t token, bool v4);
|
||||
|
||||
size_t CreatePeerTestBlock (uint8_t * buf, size_t len, uint32_t nonce); // Alice
|
||||
size_t CreateTerminationBlock (uint8_t * buf, size_t len);
|
||||
|
||||
@@ -366,6 +370,7 @@ namespace transport
|
||||
std::shared_ptr<const i2p::data::RouterInfo::Address> m_Address;
|
||||
boost::asio::ip::udp::endpoint m_RemoteEndpoint;
|
||||
i2p::data::RouterInfo::CompatibleTransports m_RemoteTransports, m_RemotePeerTestTransports;
|
||||
int m_RemoteVersion;
|
||||
uint64_t m_DestConnID, m_SourceConnID;
|
||||
SSU2SessionState m_State;
|
||||
uint8_t m_KeyDataSend[64], m_KeyDataReceive[64];
|
||||
@@ -376,6 +381,8 @@ namespace transport
|
||||
std::unordered_map<uint32_t, std::pair <std::shared_ptr<SSU2Session>, uint64_t > > m_RelaySessions; // nonce->(Alice, timestamp) for Bob or nonce->(Charlie, timestamp) for Alice
|
||||
std::list<std::shared_ptr<I2NPMessage> > m_SendQueue;
|
||||
i2p::I2NPMessagesHandler m_Handler;
|
||||
std::list<std::shared_ptr<I2NPMessage> > m_IntermediateQueue; // from transports
|
||||
mutable std::mutex m_IntermediateQueueMutex;
|
||||
bool m_IsDataReceived;
|
||||
double m_RTT;
|
||||
int m_MsgLocalExpirationTimeout;
|
||||
@@ -390,43 +397,6 @@ namespace transport
|
||||
std::unordered_map<uint32_t, uint32_t> m_ReceivedI2NPMsgIDs; // msgID -> timestamp in seconds
|
||||
uint64_t m_LastResendTime, m_LastResendAttemptTime; // in milliseconds
|
||||
};
|
||||
|
||||
|
||||
const int SSU2_PEER_TEST_RESEND_INTERVAL = 3000; // in milliseconds
|
||||
const int SSU2_PEER_TEST_RESEND_INTERVAL_VARIANCE = 2000; // in milliseconds
|
||||
const int SSU2_PEER_TEST_MAX_NUM_RESENDS = 3;
|
||||
|
||||
class SSU2PeerTestSession: public SSU2Session // for PeerTest msgs 5,6,7
|
||||
{
|
||||
public:
|
||||
|
||||
SSU2PeerTestSession (SSU2Server& server, uint64_t sourceConnID, uint64_t destConnID);
|
||||
|
||||
uint8_t GetMsgNumReceived () const { return m_MsgNumReceived; }
|
||||
bool IsConnectedRecently () const { return m_IsConnectedRecently; }
|
||||
void SetStatusChanged () { m_IsStatusChanged = true; }
|
||||
|
||||
void SendPeerTest (uint8_t msg, const uint8_t * signedData, size_t signedDataLen,
|
||||
std::shared_ptr<const i2p::data::RouterInfo::Address> addr);
|
||||
bool ProcessPeerTest (uint8_t * buf, size_t len) override;
|
||||
void Connect () override; // outgoing
|
||||
bool ProcessFirstIncomingMessage (uint64_t connID, uint8_t * buf, size_t len) override; // incoming
|
||||
|
||||
private:
|
||||
|
||||
void SendPeerTest (uint8_t msg, const uint8_t * signedData, size_t signedDataLen); // PeerTest message
|
||||
void SendPeerTest (uint8_t msg); // send or resend m_SignedData
|
||||
void HandlePeerTest (const uint8_t * buf, size_t len) override;
|
||||
|
||||
void ScheduleResend ();
|
||||
|
||||
private:
|
||||
|
||||
uint8_t m_MsgNumReceived, m_NumResends;
|
||||
bool m_IsConnectedRecently, m_IsStatusChanged;
|
||||
std::vector<uint8_t> m_SignedData; // for resends
|
||||
boost::asio::deadline_timer m_PeerTestResendTimer;
|
||||
};
|
||||
|
||||
inline uint64_t CreateHeaderMask (const uint8_t * kh, const uint8_t * nonce)
|
||||
{
|
||||
@@ -434,6 +404,12 @@ namespace transport
|
||||
i2p::crypto::ChaCha20 ((uint8_t *)&data, 8, kh, nonce, (uint8_t *)&data);
|
||||
return data;
|
||||
}
|
||||
|
||||
inline void CreateNonce (uint64_t seqn, uint8_t * nonce)
|
||||
{
|
||||
memset (nonce, 0, 4);
|
||||
htole64buf (nonce + 4, seqn);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -66,21 +66,21 @@ namespace stream
|
||||
}
|
||||
}
|
||||
|
||||
Stream::Stream (boost::asio::io_service& service, StreamingDestination& local,
|
||||
Stream::Stream (boost::asio::io_context& service, StreamingDestination& local,
|
||||
std::shared_ptr<const i2p::data::LeaseSet> remote, int port): m_Service (service),
|
||||
m_SendStreamID (0), m_SequenceNumber (0), m_DropWindowDelaySequenceNumber (0),
|
||||
m_TunnelsChangeSequenceNumber (0), m_LastReceivedSequenceNumber (-1), m_PreviousReceivedSequenceNumber (-1),
|
||||
m_LastConfirmedReceivedSequenceNumber (0), // for limit inbound speed
|
||||
m_Status (eStreamStatusNew), m_IsAckSendScheduled (false), m_IsNAcked (false), m_IsFirstACK (false),
|
||||
m_Status (eStreamStatusNew), m_IsIncoming (false), m_IsAckSendScheduled (false), m_IsNAcked (false), m_IsFirstACK (false),
|
||||
m_IsResendNeeded (false), m_IsFirstRttSample (false), m_IsSendTime (true), m_IsWinDropped (false),
|
||||
m_IsTimeOutResend (false), m_IsImmediateAckRequested (false), m_LocalDestination (local),
|
||||
m_IsTimeOutResend (false), m_IsImmediateAckRequested (false), m_IsRemoteLeaseChangeInProgress (false), m_DoubleWinIncCounter (false), m_LocalDestination (local),
|
||||
m_RemoteLeaseSet (remote), m_ReceiveTimer (m_Service), m_SendTimer (m_Service), m_ResendTimer (m_Service),
|
||||
m_AckSendTimer (m_Service), m_NumSentBytes (0), m_NumReceivedBytes (0), m_Port (port),
|
||||
m_RTT (INITIAL_RTT), m_SlowRTT (INITIAL_RTT), m_SlowRTT2 (INITIAL_RTT), m_WindowSize (INITIAL_WINDOW_SIZE), m_LastWindowDropSize (0),
|
||||
m_WindowDropTargetSize (0), m_WindowIncCounter (0), m_RTO (INITIAL_RTO),
|
||||
m_AckDelay (local.GetOwner ()->GetStreamingAckDelay ()), m_PrevRTTSample (INITIAL_RTT),
|
||||
m_AckDelay (local.GetOwner ()->GetStreamingAckDelay ()), m_PrevRTTSample (INITIAL_RTT), m_WindowSizeTail (0),
|
||||
m_Jitter (0), m_MinPacingTime (0),
|
||||
m_PacingTime (INITIAL_PACING_TIME), m_PacingTimeRem (0), m_LastSendTime (0),
|
||||
m_PacingTime (INITIAL_PACING_TIME), m_PacingTimeRem (0), m_LastSendTime (0), m_LastACKRecieveTime (0), m_ACKRecieveInterval (local.GetOwner ()->GetStreamingAckDelay ()), m_RemoteLeaseChangeTime (0),
|
||||
m_LastACKSendTime (0), m_PacketACKInterval (1), m_PacketACKIntervalRem (0), // for limit inbound speed
|
||||
m_NumResendAttempts (0), m_NumPacketsToSend (0), m_MTU (STREAMING_MTU)
|
||||
{
|
||||
@@ -95,19 +95,19 @@ namespace stream
|
||||
m_PacketACKInterval = (1000000LL*STREAMING_MTU)/inboundSpeed;
|
||||
}
|
||||
|
||||
Stream::Stream (boost::asio::io_service& service, StreamingDestination& local):
|
||||
Stream::Stream (boost::asio::io_context& service, StreamingDestination& local):
|
||||
m_Service (service), m_SendStreamID (0), m_SequenceNumber (0), m_DropWindowDelaySequenceNumber (0),
|
||||
m_TunnelsChangeSequenceNumber (0), m_LastReceivedSequenceNumber (-1), m_PreviousReceivedSequenceNumber (-1),
|
||||
m_LastConfirmedReceivedSequenceNumber (0), // for limit inbound speed
|
||||
m_Status (eStreamStatusNew), m_IsAckSendScheduled (false), m_IsNAcked (false), m_IsFirstACK (false),
|
||||
m_Status (eStreamStatusNew), m_IsIncoming (true), m_IsAckSendScheduled (false), m_IsNAcked (false), m_IsFirstACK (false),
|
||||
m_IsResendNeeded (false), m_IsFirstRttSample (false), m_IsSendTime (true), m_IsWinDropped (false),
|
||||
m_IsTimeOutResend (false), m_IsImmediateAckRequested (false), m_LocalDestination (local),
|
||||
m_IsTimeOutResend (false), m_IsImmediateAckRequested (false), m_IsRemoteLeaseChangeInProgress (false), m_DoubleWinIncCounter (false), m_LocalDestination (local),
|
||||
m_ReceiveTimer (m_Service), m_SendTimer (m_Service), m_ResendTimer (m_Service), m_AckSendTimer (m_Service),
|
||||
m_NumSentBytes (0), m_NumReceivedBytes (0), m_Port (0), m_RTT (INITIAL_RTT), m_SlowRTT (INITIAL_RTT), m_SlowRTT2 (INITIAL_RTT),
|
||||
m_WindowSize (INITIAL_WINDOW_SIZE), m_LastWindowDropSize (0), m_WindowDropTargetSize (0), m_WindowIncCounter (0),
|
||||
m_RTO (INITIAL_RTO), m_AckDelay (local.GetOwner ()->GetStreamingAckDelay ()),
|
||||
m_PrevRTTSample (INITIAL_RTT), m_Jitter (0), m_MinPacingTime (0),
|
||||
m_PacingTime (INITIAL_PACING_TIME), m_PacingTimeRem (0), m_LastSendTime (0),
|
||||
m_PrevRTTSample (INITIAL_RTT), m_WindowSizeTail (0), m_Jitter (0), m_MinPacingTime (0),
|
||||
m_PacingTime (INITIAL_PACING_TIME), m_PacingTimeRem (0), m_LastSendTime (0), m_LastACKRecieveTime (0), m_ACKRecieveInterval (local.GetOwner ()->GetStreamingAckDelay ()), m_RemoteLeaseChangeTime (0),
|
||||
m_LastACKSendTime (0), m_PacketACKInterval (1), m_PacketACKIntervalRem (0), // for limit inbound speed
|
||||
m_NumResendAttempts (0), m_NumPacketsToSend (0), m_MTU (STREAMING_MTU)
|
||||
{
|
||||
@@ -256,6 +256,7 @@ namespace stream
|
||||
if (receivedSeqn <= m_PreviousReceivedSequenceNumber || receivedSeqn == m_LastReceivedSequenceNumber)
|
||||
{
|
||||
m_CurrentOutboundTunnel = m_LocalDestination.GetOwner ()->GetTunnelPool ()->GetNextOutboundTunnel (m_CurrentOutboundTunnel);
|
||||
CancelRemoteLeaseChange ();
|
||||
UpdateCurrentRemoteLease ();
|
||||
}
|
||||
m_PreviousReceivedSequenceNumber = receivedSeqn;
|
||||
@@ -365,6 +366,7 @@ namespace stream
|
||||
{
|
||||
if (!m_IsWinDropped)
|
||||
{
|
||||
LogPrint (eLogDebug, "Streaming: Client choked, set min. window size");
|
||||
m_WindowDropTargetSize = MIN_WINDOW_SIZE;
|
||||
m_LastWindowDropSize = 0;
|
||||
m_WindowIncCounter = 0;
|
||||
@@ -494,6 +496,7 @@ namespace stream
|
||||
return;
|
||||
}
|
||||
int rttSample = INT_MAX;
|
||||
int incCounter = 0;
|
||||
m_IsNAcked = false;
|
||||
m_IsResendNeeded = false;
|
||||
int nackCount = packet->GetNACKCount ();
|
||||
@@ -535,13 +538,21 @@ namespace stream
|
||||
m_SentPackets.erase (it++);
|
||||
m_LocalDestination.DeletePacket (sentPacket);
|
||||
acknowledged = true;
|
||||
if (m_WindowSize < MAX_WINDOW_SIZE && !m_IsFirstACK)
|
||||
if (m_RTT < m_LocalDestination.GetRandom () % INITIAL_RTT) // dirty
|
||||
m_WindowIncCounter++;
|
||||
if (m_WindowIncCounter < MAX_WINDOW_SIZE && !m_IsFirstACK && !m_IsWinDropped)
|
||||
incCounter++;
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
if (m_LastACKRecieveTime)
|
||||
{
|
||||
uint64_t interval = ts - m_LastACKRecieveTime;
|
||||
if (m_ACKRecieveInterval)
|
||||
m_ACKRecieveInterval = (m_ACKRecieveInterval + interval) / 2;
|
||||
else
|
||||
m_ACKRecieveInterval = interval;
|
||||
}
|
||||
m_LastACKRecieveTime = ts;
|
||||
if (rttSample != INT_MAX)
|
||||
{
|
||||
if (m_IsFirstRttSample && !m_IsFirstACK)
|
||||
@@ -551,7 +562,7 @@ namespace stream
|
||||
m_SlowRTT2 = rttSample;
|
||||
m_PrevRTTSample = rttSample;
|
||||
m_Jitter = rttSample / 10; // 10%
|
||||
m_Jitter += 5; // for low-latency connections
|
||||
m_Jitter += 15; // for low-latency connections
|
||||
m_IsFirstRttSample = false;
|
||||
}
|
||||
else
|
||||
@@ -568,18 +579,35 @@ namespace stream
|
||||
jitter = m_PrevRTTSample - rttSample;
|
||||
else
|
||||
jitter = rttSample / 10; // 10%
|
||||
jitter += 5; // for low-latency connections
|
||||
jitter += 15; // for low-latency connections
|
||||
m_Jitter = (0.05 * jitter) + (1.0 - 0.05) * m_Jitter;
|
||||
}
|
||||
if (rttSample > m_SlowRTT)
|
||||
{
|
||||
incCounter = 0;
|
||||
m_DoubleWinIncCounter = 1;
|
||||
}
|
||||
else if (rttSample < m_SlowRTT)
|
||||
{
|
||||
if (m_DoubleWinIncCounter)
|
||||
{
|
||||
incCounter = incCounter * 2;
|
||||
m_DoubleWinIncCounter = 0;
|
||||
}
|
||||
}
|
||||
m_WindowIncCounter = m_WindowIncCounter + incCounter;
|
||||
//
|
||||
// delay-based CC
|
||||
if ((m_SlowRTT2 > m_SlowRTT + m_Jitter && rttSample > m_SlowRTT2 && rttSample > m_PrevRTTSample) && !m_IsWinDropped) // Drop window if RTT grows too fast, late detection
|
||||
{
|
||||
LogPrint (eLogDebug, "Streaming: Congestion detected, reduce window size");
|
||||
ProcessWindowDrop ();
|
||||
}
|
||||
UpdatePacingTime ();
|
||||
m_PrevRTTSample = rttSample;
|
||||
|
||||
bool wasInitial = m_RTO == INITIAL_RTO;
|
||||
m_RTO = std::max (MIN_RTO, (int)(m_RTT * 1.3 + m_Jitter)); // TODO: implement it better
|
||||
m_RTO = std::max (MIN_RTO, (int)(m_RTT * 1.3 + m_Jitter + m_ACKRecieveInterval)); // TODO: implement it better
|
||||
|
||||
if (wasInitial)
|
||||
ScheduleResend ();
|
||||
@@ -589,20 +617,10 @@ namespace stream
|
||||
m_IsFirstRttSample = true;
|
||||
m_IsWinDropped = false;
|
||||
}
|
||||
if (m_WindowDropTargetSize && m_WindowSize <= m_WindowDropTargetSize)
|
||||
if (m_WindowDropTargetSize && int(m_SentPackets.size ()) <= m_WindowDropTargetSize)
|
||||
{
|
||||
m_WindowSize = m_WindowDropTargetSize;
|
||||
m_WindowDropTargetSize = 0;
|
||||
m_DropWindowDelaySequenceNumber = m_SequenceNumber;
|
||||
}
|
||||
if (acknowledged && m_WindowDropTargetSize && m_WindowSize > m_WindowDropTargetSize)
|
||||
{
|
||||
m_RTO = std::max (MIN_RTO, (int)(m_RTT * 1.5 + m_Jitter)); // we assume that the next rtt sample may be much larger than the current
|
||||
m_IsResendNeeded = true;
|
||||
m_WindowSize = m_SentPackets.size () + 1; // if there are no packets to resend, just send one regular packet
|
||||
if (m_WindowSize < MIN_WINDOW_SIZE) m_WindowSize = MIN_WINDOW_SIZE;
|
||||
if (m_WindowSize > MAX_WINDOW_SIZE) m_WindowSize = MAX_WINDOW_SIZE;
|
||||
m_WindowIncCounter = 0;
|
||||
UpdatePacingTime ();
|
||||
}
|
||||
if (acknowledged || m_IsNAcked)
|
||||
{
|
||||
@@ -611,12 +629,14 @@ namespace stream
|
||||
if (m_SendBuffer.IsEmpty () && m_SentPackets.size () > 0) // tail loss
|
||||
{
|
||||
m_IsResendNeeded = true;
|
||||
m_RTO = std::max (MIN_RTO, (int)(m_RTT * 1.5 + m_Jitter)); // to prevent spurious retransmit
|
||||
m_RTO = std::max (MIN_RTO, (int)(m_RTT * 1.5 + m_Jitter + m_ACKRecieveInterval)); // to prevent spurious retransmit
|
||||
}
|
||||
if (m_SentPackets.empty () && m_SendBuffer.IsEmpty ())
|
||||
{
|
||||
m_ResendTimer.cancel ();
|
||||
m_SendTimer.cancel ();
|
||||
m_LastACKRecieveTime = 0;
|
||||
m_ACKRecieveInterval = m_AckDelay;
|
||||
}
|
||||
if (acknowledged && m_IsFirstACK)
|
||||
{
|
||||
@@ -666,7 +686,7 @@ namespace stream
|
||||
{
|
||||
// make sure that AsycReceive complete
|
||||
auto s = shared_from_this();
|
||||
m_Service.post ([s]()
|
||||
boost::asio::post (m_Service, [s]()
|
||||
{
|
||||
s->m_ReceiveTimer.cancel ();
|
||||
});
|
||||
@@ -694,7 +714,7 @@ namespace stream
|
||||
else if (handler)
|
||||
handler(boost::system::error_code ());
|
||||
auto s = shared_from_this ();
|
||||
m_Service.post ([s, buffer]()
|
||||
boost::asio::post (m_Service, [s, buffer]()
|
||||
{
|
||||
if (buffer)
|
||||
s->m_SendBuffer.Add (buffer);
|
||||
@@ -704,7 +724,8 @@ namespace stream
|
||||
|
||||
void Stream::SendBuffer ()
|
||||
{
|
||||
ScheduleSend ();
|
||||
if (m_RemoteLeaseSet) // don't scheudle send for first SYN for incoming stream
|
||||
ScheduleSend ();
|
||||
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
|
||||
int numMsgs = m_WindowSize - m_SentPackets.size ();
|
||||
if (numMsgs <= 0 || !m_IsSendTime) // window is full
|
||||
@@ -755,8 +776,8 @@ namespace stream
|
||||
if (!m_RemoteLeaseSet) m_RemoteLeaseSet = m_LocalDestination.GetOwner ()->FindLeaseSet (m_RemoteIdentity->GetIdentHash ());;
|
||||
if (m_RemoteLeaseSet)
|
||||
{
|
||||
m_RoutingSession = m_LocalDestination.GetOwner ()->GetRoutingSession (m_RemoteLeaseSet, true);
|
||||
m_MTU = m_RoutingSession->IsRatchets () ? STREAMING_MTU_RATCHETS : STREAMING_MTU;
|
||||
m_RoutingSession = m_LocalDestination.GetOwner ()->GetRoutingSession (m_RemoteLeaseSet, true, !m_IsIncoming);
|
||||
m_MTU = (m_RoutingSession && m_RoutingSession->IsRatchets ()) ? STREAMING_MTU_RATCHETS : STREAMING_MTU;
|
||||
}
|
||||
uint16_t flags = PACKET_FLAG_SYNCHRONIZE | PACKET_FLAG_FROM_INCLUDED |
|
||||
PACKET_FLAG_SIGNATURE_INCLUDED | PACKET_FLAG_MAX_PACKET_SIZE_INCLUDED;
|
||||
@@ -942,7 +963,7 @@ namespace stream
|
||||
if (choking || requestImmediateAck)
|
||||
{
|
||||
htobe16buf (packet + size, 2); // 2 bytes delay interval
|
||||
htobe16buf (packet + size + 2, choking ? DELAY_CHOKING : 0); // set choking or immediated ack interval
|
||||
htobe16buf (packet + size + 2, choking ? DELAY_CHOKING : 0); // set choking or immediate ack interval
|
||||
size += 2;
|
||||
if (requestImmediateAck) // ack request sent
|
||||
{
|
||||
@@ -1056,7 +1077,7 @@ namespace stream
|
||||
m_LocalDestination.GetOwner ()->Sign (packet, size, signature);
|
||||
|
||||
p->len = size;
|
||||
m_Service.post (std::bind (&Stream::SendPacket, shared_from_this (), p));
|
||||
boost::asio::post (m_Service, std::bind (&Stream::SendPacket, shared_from_this (), p));
|
||||
LogPrint (eLogDebug, "Streaming: FIN sent, sSID=", m_SendStreamID);
|
||||
}
|
||||
|
||||
@@ -1104,6 +1125,7 @@ namespace stream
|
||||
{
|
||||
if (!m_RemoteLeaseSet)
|
||||
{
|
||||
CancelRemoteLeaseChange ();
|
||||
UpdateCurrentRemoteLease ();
|
||||
if (!m_RemoteLeaseSet)
|
||||
{
|
||||
@@ -1112,7 +1134,15 @@ namespace stream
|
||||
}
|
||||
}
|
||||
if (!m_RoutingSession || m_RoutingSession->IsTerminated () || !m_RoutingSession->IsReadyToSend ()) // expired and detached or new session sent
|
||||
m_RoutingSession = m_LocalDestination.GetOwner ()->GetRoutingSession (m_RemoteLeaseSet, true);
|
||||
{
|
||||
m_RoutingSession = m_LocalDestination.GetOwner ()->GetRoutingSession (m_RemoteLeaseSet, true, !m_IsIncoming || m_SequenceNumber > 1);
|
||||
if (!m_RoutingSession)
|
||||
{
|
||||
LogPrint (eLogError, "Streaming: Can't obtain routing session, sSID=", m_SendStreamID);
|
||||
Terminate ();
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (!m_CurrentOutboundTunnel && m_RoutingSession) // first message to send
|
||||
{
|
||||
// try to get shared path first
|
||||
@@ -1122,14 +1152,35 @@ namespace stream
|
||||
m_CurrentOutboundTunnel = routingPath->outboundTunnel;
|
||||
m_CurrentRemoteLease = routingPath->remoteLease;
|
||||
m_RTT = routingPath->rtt;
|
||||
m_RTO = std::max (MIN_RTO, (int)(m_RTT * 1.3 + m_Jitter)); // TODO: implement it better
|
||||
}
|
||||
}
|
||||
|
||||
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
|
||||
if (!m_CurrentRemoteLease || !m_CurrentRemoteLease->endDate || // excluded from LeaseSet
|
||||
ts >= m_CurrentRemoteLease->endDate - i2p::data::LEASE_ENDDATE_THRESHOLD)
|
||||
if (!m_CurrentRemoteLease || !m_CurrentRemoteLease->endDate) // excluded from LeaseSet
|
||||
{
|
||||
CancelRemoteLeaseChange ();
|
||||
UpdateCurrentRemoteLease (true);
|
||||
}
|
||||
if (m_RemoteLeaseChangeTime && m_IsRemoteLeaseChangeInProgress && ts > m_RemoteLeaseChangeTime + INITIAL_RTO)
|
||||
{
|
||||
LogPrint (eLogDebug, "Streaming: RemoteLease changed, set initial window size");
|
||||
CancelRemoteLeaseChange ();
|
||||
m_CurrentRemoteLease = m_NextRemoteLease;
|
||||
HalveWindowSize ();
|
||||
}
|
||||
auto currentRemoteLease = m_CurrentRemoteLease;
|
||||
if (!m_IsRemoteLeaseChangeInProgress && m_RemoteLeaseSet && m_CurrentRemoteLease && ts >= m_CurrentRemoteLease->endDate - i2p::data::LEASE_ENDDATE_THRESHOLD)
|
||||
{
|
||||
auto leases = m_RemoteLeaseSet->GetNonExpiredLeases (false);
|
||||
if (leases.size ())
|
||||
{
|
||||
m_IsRemoteLeaseChangeInProgress = true;
|
||||
UpdateCurrentRemoteLease (true);
|
||||
m_NextRemoteLease = m_CurrentRemoteLease;
|
||||
}
|
||||
else
|
||||
UpdateCurrentRemoteLease (true);
|
||||
}
|
||||
if (m_CurrentRemoteLease && ts < m_CurrentRemoteLease->endDate + i2p::data::LEASE_ENDDATE_THRESHOLD)
|
||||
{
|
||||
bool freshTunnel = false;
|
||||
@@ -1166,6 +1217,11 @@ namespace stream
|
||||
msg
|
||||
});
|
||||
m_NumSentBytes += it->GetLength ();
|
||||
if (m_IsRemoteLeaseChangeInProgress && !m_RemoteLeaseChangeTime)
|
||||
{
|
||||
m_RemoteLeaseChangeTime = ts;
|
||||
m_CurrentRemoteLease = currentRemoteLease; // change it back before new lease is confirmed
|
||||
}
|
||||
}
|
||||
m_CurrentOutboundTunnel->SendTunnelDataMsgs (msgs);
|
||||
}
|
||||
@@ -1209,7 +1265,8 @@ namespace stream
|
||||
if (m_Status != eStreamStatusTerminated)
|
||||
{
|
||||
m_SendTimer.cancel ();
|
||||
m_SendTimer.expires_from_now (boost::posix_time::microseconds(SEND_INTERVAL));
|
||||
m_SendTimer.expires_from_now (boost::posix_time::microseconds(
|
||||
SEND_INTERVAL + m_LocalDestination.GetRandom () % SEND_INTERVAL_VARIANCE));
|
||||
m_SendTimer.async_wait (std::bind (&Stream::HandleSendTimer,
|
||||
shared_from_this (), std::placeholders::_1));
|
||||
}
|
||||
@@ -1222,10 +1279,19 @@ namespace stream
|
||||
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
|
||||
if (m_LastSendTime && ts*1000 > m_LastSendTime*1000 + m_PacingTime)
|
||||
{
|
||||
m_NumPacketsToSend = ((ts*1000 - m_LastSendTime*1000) + m_PacingTimeRem) / m_PacingTime;
|
||||
m_PacingTimeRem = ((ts*1000 - m_LastSendTime*1000) + m_PacingTimeRem) - (m_NumPacketsToSend * m_PacingTime);
|
||||
if (m_PacingTime)
|
||||
{
|
||||
auto numPackets = std::lldiv (m_PacingTimeRem + ts*1000 - m_LastSendTime*1000, m_PacingTime);
|
||||
m_NumPacketsToSend = numPackets.quot;
|
||||
m_PacingTimeRem = numPackets.rem;
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint (eLogError, "Streaming: pacing time is zero");
|
||||
m_NumPacketsToSend = 1; m_PacingTimeRem = 0;
|
||||
}
|
||||
m_IsSendTime = true;
|
||||
if (m_WindowIncCounter && m_WindowSize < MAX_WINDOW_SIZE && !m_SendBuffer.IsEmpty ())
|
||||
if (m_WindowIncCounter && m_WindowSize < MAX_WINDOW_SIZE && !m_SendBuffer.IsEmpty () && m_PacingTime > m_MinPacingTime)
|
||||
{
|
||||
for (int i = 0; i < m_NumPacketsToSend; i++)
|
||||
{
|
||||
@@ -1238,18 +1304,23 @@ namespace stream
|
||||
else
|
||||
m_WindowSize += (m_WindowSize - (1 - PREV_SPEED_KEEP_TIME_COEFF)) / m_WindowSize;
|
||||
if (m_WindowSize > MAX_WINDOW_SIZE) m_WindowSize = MAX_WINDOW_SIZE;
|
||||
m_WindowIncCounter --;
|
||||
UpdatePacingTime ();
|
||||
m_WindowIncCounter--;
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
UpdatePacingTime ();
|
||||
}
|
||||
else if (m_WindowIncCounter && m_WindowSize == MAX_WINDOW_SIZE && !m_SendBuffer.IsEmpty () && m_PacingTime > m_MinPacingTime)
|
||||
{
|
||||
m_WindowSizeTail = m_WindowSizeTail + m_WindowIncCounter;
|
||||
if (m_WindowSizeTail > MAX_WINDOW_SIZE) m_WindowSizeTail = MAX_WINDOW_SIZE;
|
||||
}
|
||||
if (m_IsNAcked)
|
||||
ResendPacket ();
|
||||
else if (m_IsResendNeeded) // resend packets
|
||||
ResendPacket ();
|
||||
// delay-based CC
|
||||
else if (!m_IsWinDropped && int(m_SentPackets.size ()) == m_WindowSize) // we sending packets too fast, early detection
|
||||
ProcessWindowDrop ();
|
||||
else if (m_WindowSize > int(m_SentPackets.size ())) // send packets
|
||||
SendBuffer ();
|
||||
}
|
||||
@@ -1288,97 +1359,110 @@ namespace stream
|
||||
|
||||
void Stream::ResendPacket ()
|
||||
{
|
||||
// check for resend attempts
|
||||
if (m_NumResendAttempts >= MAX_NUM_RESEND_ATTEMPTS)
|
||||
{
|
||||
LogPrint (eLogWarning, "Streaming: packet was not ACKed after ", MAX_NUM_RESEND_ATTEMPTS, " attempts, terminate, rSID=", m_RecvStreamID, ", sSID=", m_SendStreamID);
|
||||
m_Status = eStreamStatusReset;
|
||||
Close ();
|
||||
return;
|
||||
}
|
||||
// check for resend attempts
|
||||
if (m_IsIncoming && m_SequenceNumber == 1 && m_NumResendAttempts > 0)
|
||||
{
|
||||
LogPrint (eLogWarning, "Streaming: SYNACK packet was not ACKed after ", m_NumResendAttempts, " attempts, terminate, rSID=", m_RecvStreamID, ", sSID=", m_SendStreamID);
|
||||
m_Status = eStreamStatusReset;
|
||||
Close ();
|
||||
return;
|
||||
}
|
||||
if (m_NumResendAttempts >= MAX_NUM_RESEND_ATTEMPTS)
|
||||
{
|
||||
LogPrint (eLogWarning, "Streaming: packet was not ACKed after ", MAX_NUM_RESEND_ATTEMPTS, " attempts, terminate, rSID=", m_RecvStreamID, ", sSID=", m_SendStreamID);
|
||||
m_Status = eStreamStatusReset;
|
||||
Close ();
|
||||
return;
|
||||
}
|
||||
|
||||
// collect packets to resend
|
||||
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
|
||||
std::vector<Packet *> packets;
|
||||
if (m_IsNAcked)
|
||||
// collect packets to resend
|
||||
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
|
||||
std::vector<Packet *> packets;
|
||||
if (m_IsNAcked)
|
||||
{
|
||||
for (auto it : m_NACKedPackets)
|
||||
{
|
||||
for (auto it : m_NACKedPackets)
|
||||
if (ts >= it->sendTime + m_RTO)
|
||||
{
|
||||
if (ts >= it->sendTime + m_RTO)
|
||||
{
|
||||
if (ts < it->sendTime + m_RTO*2)
|
||||
it->resent = true;
|
||||
else
|
||||
it->resent = false;
|
||||
it->sendTime = ts;
|
||||
packets.push_back (it);
|
||||
if ((int)packets.size () >= m_NumPacketsToSend) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (auto it : m_SentPackets)
|
||||
{
|
||||
if (ts >= it->sendTime + m_RTO)
|
||||
{
|
||||
if (ts < it->sendTime + m_RTO*2)
|
||||
it->resent = true;
|
||||
else
|
||||
it->resent = false;
|
||||
it->sendTime = ts;
|
||||
packets.push_back (it);
|
||||
if ((int)packets.size () >= m_NumPacketsToSend) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// select tunnels if necessary and send
|
||||
if (packets.size () > 0 && m_IsSendTime)
|
||||
{
|
||||
if (m_IsNAcked) m_NumResendAttempts = 1;
|
||||
else if (m_IsTimeOutResend) m_NumResendAttempts++;
|
||||
if (m_NumResendAttempts == 1 && m_RTO != INITIAL_RTO)
|
||||
{
|
||||
// loss-based CC
|
||||
if (!m_IsWinDropped && LOSS_BASED_CONTROL_ENABLED)
|
||||
ProcessWindowDrop ();
|
||||
}
|
||||
else if (m_IsTimeOutResend)
|
||||
{
|
||||
m_IsTimeOutResend = false;
|
||||
m_RTO = INITIAL_RTO; // drop RTO to initial upon tunnels pair change
|
||||
m_WindowDropTargetSize = INITIAL_WINDOW_SIZE;
|
||||
m_LastWindowDropSize = 0;
|
||||
m_WindowIncCounter = 0;
|
||||
m_IsWinDropped = true;
|
||||
m_IsFirstRttSample = true;
|
||||
m_DropWindowDelaySequenceNumber = 0;
|
||||
m_IsFirstACK = true;
|
||||
UpdatePacingTime ();
|
||||
if (m_RoutingSession) m_RoutingSession->SetSharedRoutingPath (nullptr);
|
||||
if (m_NumResendAttempts & 1)
|
||||
{
|
||||
// pick another outbound tunnel
|
||||
m_CurrentOutboundTunnel = m_LocalDestination.GetOwner ()->GetTunnelPool ()->GetNextOutboundTunnel (m_CurrentOutboundTunnel);
|
||||
LogPrint (eLogWarning, "Streaming: Resend #", m_NumResendAttempts,
|
||||
", another outbound tunnel has been selected for stream with sSID=", m_SendStreamID);
|
||||
}
|
||||
if (ts < it->sendTime + m_RTO*2)
|
||||
it->resent = true;
|
||||
else
|
||||
{
|
||||
UpdateCurrentRemoteLease (); // pick another lease
|
||||
LogPrint (eLogWarning, "Streaming: Resend #", m_NumResendAttempts,
|
||||
", another remote lease has been selected for stream with rSID=", m_RecvStreamID, ", sSID=", m_SendStreamID);
|
||||
}
|
||||
it->resent = false;
|
||||
it->sendTime = ts;
|
||||
packets.push_back (it);
|
||||
if ((int)packets.size () >= m_NumPacketsToSend) break;
|
||||
}
|
||||
SendPackets (packets);
|
||||
m_LastSendTime = ts;
|
||||
m_IsSendTime = false;
|
||||
if (m_IsNAcked || m_IsResendNeeded) ScheduleSend ();
|
||||
}
|
||||
else
|
||||
SendBuffer ();
|
||||
if (!m_IsNAcked && !m_IsResendNeeded) ScheduleResend ();
|
||||
}
|
||||
else
|
||||
{
|
||||
for (auto it : m_SentPackets)
|
||||
{
|
||||
if (ts >= it->sendTime + m_RTO)
|
||||
{
|
||||
if (ts < it->sendTime + m_RTO*2)
|
||||
it->resent = true;
|
||||
else
|
||||
it->resent = false;
|
||||
it->sendTime = ts;
|
||||
packets.push_back (it);
|
||||
if ((int)packets.size () >= m_NumPacketsToSend) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// select tunnels if necessary and send
|
||||
if (packets.size () > 0 && m_IsSendTime)
|
||||
{
|
||||
if (m_IsNAcked) m_NumResendAttempts = 1;
|
||||
else if (m_IsTimeOutResend) m_NumResendAttempts++;
|
||||
if (m_NumResendAttempts == 1 && m_RTO != INITIAL_RTO)
|
||||
{
|
||||
// loss-based CC
|
||||
if (!m_IsWinDropped && LOSS_BASED_CONTROL_ENABLED)
|
||||
{
|
||||
LogPrint (eLogDebug, "Streaming: Packet loss, reduce window size");
|
||||
ProcessWindowDrop ();
|
||||
}
|
||||
}
|
||||
else if (m_IsTimeOutResend)
|
||||
{
|
||||
m_IsTimeOutResend = false;
|
||||
m_RTO = INITIAL_RTO; // drop RTO to initial upon tunnels pair change
|
||||
m_WindowDropTargetSize = INITIAL_WINDOW_SIZE;
|
||||
m_LastWindowDropSize = 0;
|
||||
m_WindowIncCounter = 0;
|
||||
m_IsWinDropped = true;
|
||||
m_IsFirstRttSample = true;
|
||||
m_DropWindowDelaySequenceNumber = 0;
|
||||
m_IsFirstACK = true;
|
||||
m_LastACKRecieveTime = 0;
|
||||
m_ACKRecieveInterval = m_AckDelay;
|
||||
UpdatePacingTime ();
|
||||
if (m_RoutingSession) m_RoutingSession->SetSharedRoutingPath (nullptr);
|
||||
if (m_NumResendAttempts & 1)
|
||||
{
|
||||
// pick another outbound tunnel
|
||||
m_CurrentOutboundTunnel = m_LocalDestination.GetOwner ()->GetTunnelPool ()->GetNextOutboundTunnel (m_CurrentOutboundTunnel);
|
||||
LogPrint (eLogWarning, "Streaming: Resend #", m_NumResendAttempts,
|
||||
", another outbound tunnel has been selected for stream with sSID=", m_SendStreamID);
|
||||
}
|
||||
else
|
||||
{
|
||||
CancelRemoteLeaseChange ();
|
||||
UpdateCurrentRemoteLease (); // pick another lease
|
||||
LogPrint (eLogWarning, "Streaming: Resend #", m_NumResendAttempts,
|
||||
", another remote lease has been selected for stream with rSID=", m_RecvStreamID, ", sSID=", m_SendStreamID);
|
||||
}
|
||||
}
|
||||
SendPackets (packets);
|
||||
m_LastSendTime = ts;
|
||||
m_IsSendTime = false;
|
||||
if (m_IsNAcked || m_IsResendNeeded) ScheduleSend ();
|
||||
}
|
||||
else
|
||||
SendBuffer ();
|
||||
if (!m_IsNAcked && !m_IsResendNeeded) ScheduleResend ();
|
||||
}
|
||||
|
||||
void Stream::ScheduleAck (int timeout)
|
||||
@@ -1430,17 +1514,26 @@ namespace stream
|
||||
if (!remoteLeaseSet)
|
||||
{
|
||||
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
|
||||
if (!m_IsIncoming) // outgoing
|
||||
{
|
||||
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
|
||||
else // incoming
|
||||
{
|
||||
m_RemoteLeaseSet = nullptr;
|
||||
m_LocalDestination.GetOwner ()->RequestDestination (m_RemoteIdentity->GetIdentHash ()); // try to request for a next attempt
|
||||
}
|
||||
// just close the socket without sending FIN or RST
|
||||
m_Status = eStreamStatusClosed;
|
||||
AsyncClose ();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1506,22 +1599,10 @@ namespace stream
|
||||
LogPrint (eLogWarning, "Streaming: Remote LeaseSet not found");
|
||||
m_CurrentRemoteLease = nullptr;
|
||||
}
|
||||
if (isLeaseChanged)
|
||||
if (isLeaseChanged && !m_IsRemoteLeaseChangeInProgress)
|
||||
{
|
||||
// drop window to initial upon RemoteLease change
|
||||
m_RTO = INITIAL_RTO;
|
||||
if (m_WindowSize > INITIAL_WINDOW_SIZE)
|
||||
{
|
||||
m_WindowDropTargetSize = std::max (m_WindowSize/2, (float)INITIAL_WINDOW_SIZE);
|
||||
m_IsWinDropped = true;
|
||||
}
|
||||
else
|
||||
m_WindowSize = INITIAL_WINDOW_SIZE;
|
||||
m_LastWindowDropSize = 0;
|
||||
m_WindowIncCounter = 0;
|
||||
m_IsFirstRttSample = true;
|
||||
m_IsFirstACK = true;
|
||||
UpdatePacingTime ();
|
||||
LogPrint (eLogDebug, "Streaming: RemoteLease changed, set initial window size");
|
||||
HalveWindowSize ();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1537,7 +1618,10 @@ namespace stream
|
||||
|
||||
void Stream::UpdatePacingTime ()
|
||||
{
|
||||
m_PacingTime = std::round (m_RTT*1000/m_WindowSize);
|
||||
if (m_WindowDropTargetSize)
|
||||
m_PacingTime = std::round (m_RTT*1000/m_WindowDropTargetSize);
|
||||
else
|
||||
m_PacingTime = std::round (m_RTT*1000/m_WindowSize);
|
||||
if (m_MinPacingTime && m_PacingTime < m_MinPacingTime)
|
||||
m_PacingTime = m_MinPacingTime;
|
||||
}
|
||||
@@ -1545,21 +1629,47 @@ namespace stream
|
||||
void Stream::ProcessWindowDrop ()
|
||||
{
|
||||
if (m_WindowSize > m_LastWindowDropSize)
|
||||
m_LastWindowDropSize = (m_LastWindowDropSize + m_WindowSize) / 2;
|
||||
{
|
||||
m_LastWindowDropSize = (m_LastWindowDropSize + m_WindowSize + m_WindowSizeTail) / 2;
|
||||
if (m_LastWindowDropSize > MAX_WINDOW_SIZE) m_LastWindowDropSize = MAX_WINDOW_SIZE;
|
||||
}
|
||||
else
|
||||
m_LastWindowDropSize = m_WindowSize;
|
||||
m_WindowDropTargetSize = m_LastWindowDropSize - (m_LastWindowDropSize / 4); // -25%;
|
||||
if (m_WindowDropTargetSize < MIN_WINDOW_SIZE + 1)
|
||||
m_WindowDropTargetSize = MIN_WINDOW_SIZE + 1;
|
||||
m_WindowSize = m_SentPackets.size (); // stop sending now
|
||||
if (m_WindowSize < MIN_WINDOW_SIZE) m_WindowSize = MIN_WINDOW_SIZE;
|
||||
if (m_WindowDropTargetSize < MIN_WINDOW_SIZE)
|
||||
m_WindowDropTargetSize = MIN_WINDOW_SIZE;
|
||||
m_WindowIncCounter = 0; // disable window growth
|
||||
m_DropWindowDelaySequenceNumber = m_SequenceNumber;
|
||||
m_DropWindowDelaySequenceNumber = m_SequenceNumber + int(m_WindowDropTargetSize);
|
||||
m_IsFirstACK = true; // ignore first RTT sample
|
||||
m_IsWinDropped = true; // don't drop window twice
|
||||
m_WindowSizeTail = 0;
|
||||
UpdatePacingTime ();
|
||||
}
|
||||
|
||||
|
||||
void Stream::HalveWindowSize ()
|
||||
{
|
||||
m_RTO = INITIAL_RTO;
|
||||
if (m_WindowSize > INITIAL_WINDOW_SIZE)
|
||||
{
|
||||
m_WindowDropTargetSize = std::max (m_WindowSize/2, (float)INITIAL_WINDOW_SIZE);
|
||||
m_IsWinDropped = true;
|
||||
}
|
||||
else
|
||||
m_WindowSize = INITIAL_WINDOW_SIZE;
|
||||
m_LastWindowDropSize = 0;
|
||||
m_WindowIncCounter = 0;
|
||||
m_IsFirstRttSample = true;
|
||||
m_IsFirstACK = true;
|
||||
m_WindowSizeTail = 0;
|
||||
UpdatePacingTime ();
|
||||
}
|
||||
|
||||
void Stream::CancelRemoteLeaseChange ()
|
||||
{
|
||||
m_RemoteLeaseChangeTime = 0;
|
||||
m_IsRemoteLeaseChangeInProgress = false;
|
||||
}
|
||||
|
||||
StreamingDestination::StreamingDestination (std::shared_ptr<i2p::client::ClientDestination> owner, uint16_t localPort, bool gzip):
|
||||
m_Owner (owner), m_LocalPort (localPort), m_Gzip (gzip),
|
||||
m_PendingIncomingTimer (m_Owner->GetService ()),
|
||||
@@ -1645,9 +1755,20 @@ namespace stream
|
||||
DeletePacket (packet); // drop it, because previous should be connected
|
||||
return;
|
||||
}
|
||||
if (m_Owner->GetStreamingMaxConcurrentStreams () > 0 && (int)m_Streams.size () > m_Owner->GetStreamingMaxConcurrentStreams ())
|
||||
{
|
||||
LogPrint(eLogWarning, "Streaming: Number of streams exceeds ", m_Owner->GetStreamingMaxConcurrentStreams ());
|
||||
DeletePacket (packet);
|
||||
return;
|
||||
}
|
||||
auto incomingStream = CreateNewIncomingStream (receiveStreamID);
|
||||
incomingStream->HandleNextPacket (packet); // SYN
|
||||
auto ident = incomingStream->GetRemoteIdentity();
|
||||
if (!incomingStream->GetRemoteLeaseSet ())
|
||||
{
|
||||
LogPrint (eLogWarning, "Streaming: No remote LeaseSet for incoming stream. Terminated");
|
||||
incomingStream->Terminate (); // can't send FIN anyway
|
||||
return;
|
||||
}
|
||||
|
||||
// handle saved packets if any
|
||||
{
|
||||
@@ -1749,7 +1870,8 @@ namespace stream
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_StreamsMutex);
|
||||
m_Streams.erase (stream->GetRecvStreamID ());
|
||||
m_IncomingStreams.erase (stream->GetSendStreamID ());
|
||||
if (stream->IsIncoming ())
|
||||
m_IncomingStreams.erase (stream->GetSendStreamID ());
|
||||
if (m_LastStream == stream) m_LastStream = nullptr;
|
||||
}
|
||||
auto ts = i2p::util::GetSecondsSinceEpoch ();
|
||||
@@ -1767,7 +1889,7 @@ namespace stream
|
||||
if (it == m_Streams.end ())
|
||||
return false;
|
||||
auto s = it->second;
|
||||
m_Owner->GetService ().post ([this, s] ()
|
||||
boost::asio::post (m_Owner->GetService (), [this, s] ()
|
||||
{
|
||||
s->Close (); // try to send FIN
|
||||
s->Terminate (false);
|
||||
@@ -1780,7 +1902,7 @@ namespace stream
|
||||
{
|
||||
m_Acceptor = acceptor; // we must set it immediately for IsAcceptorSet
|
||||
auto s = shared_from_this ();
|
||||
m_Owner->GetService ().post([s](void)
|
||||
boost::asio::post (m_Owner->GetService (), [s](void)
|
||||
{
|
||||
// take care about incoming queue
|
||||
for (auto& it: s->m_PendingIncomingStreams)
|
||||
@@ -1799,7 +1921,7 @@ namespace stream
|
||||
|
||||
void StreamingDestination::AcceptOnce (const Acceptor& acceptor)
|
||||
{
|
||||
m_Owner->GetService ().post([acceptor, this](void)
|
||||
boost::asio::post (m_Owner->GetService (), [acceptor, this](void)
|
||||
{
|
||||
if (!m_PendingIncomingStreams.empty ())
|
||||
{
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include <mutex>
|
||||
#include <boost/asio.hpp>
|
||||
#include "Base.h"
|
||||
#include "Gzip.h"
|
||||
#include "I2PEndian.h"
|
||||
#include "Identity.h"
|
||||
#include "LeaseSet.h"
|
||||
@@ -54,13 +55,13 @@ namespace stream
|
||||
const size_t COMPRESSION_THRESHOLD_SIZE = 66;
|
||||
const int MAX_NUM_RESEND_ATTEMPTS = 10;
|
||||
const int INITIAL_WINDOW_SIZE = 10;
|
||||
const int MIN_WINDOW_SIZE = 2;
|
||||
const int MIN_WINDOW_SIZE = 3;
|
||||
const int MAX_WINDOW_SIZE = 512;
|
||||
const double RTT_EWMA_ALPHA = 0.25;
|
||||
const double SLOWRTT_EWMA_ALPHA = 0.05;
|
||||
const double PREV_SPEED_KEEP_TIME_COEFF = 0.35; // 0.1 - 1 // how long will the window size stay around the previous drop level, less is longer
|
||||
const int MIN_RTO = 20; // in milliseconds
|
||||
const int INITIAL_RTT = 8000; // in milliseconds
|
||||
const int INITIAL_RTT = 1500; // in milliseconds
|
||||
const int INITIAL_RTO = 9000; // in milliseconds
|
||||
const int INITIAL_PACING_TIME = 1000 * INITIAL_RTT / INITIAL_WINDOW_SIZE; // in microseconds
|
||||
const int MIN_SEND_ACK_TIMEOUT = 2; // in milliseconds
|
||||
@@ -69,7 +70,8 @@ namespace stream
|
||||
const int PENDING_INCOMING_TIMEOUT = 10; // in seconds
|
||||
const int MAX_RECEIVE_TIMEOUT = 20; // in seconds
|
||||
const uint16_t DELAY_CHOKING = 60000; // in milliseconds
|
||||
const uint64_t SEND_INTERVAL = 1000; // in microseconds
|
||||
const uint64_t SEND_INTERVAL = 10000; // in microseconds
|
||||
const uint64_t SEND_INTERVAL_VARIANCE = 2000; // in microseconds
|
||||
const uint64_t REQUEST_IMMEDIATE_ACK_INTERVAL = 7500; // in milliseconds
|
||||
const uint64_t REQUEST_IMMEDIATE_ACK_INTERVAL_VARIANCE = 3200; // in milliseconds
|
||||
const bool LOSS_BASED_CONTROL_ENABLED = 1; // 0/1
|
||||
@@ -174,9 +176,9 @@ namespace stream
|
||||
{
|
||||
public:
|
||||
|
||||
Stream (boost::asio::io_service& service, StreamingDestination& local,
|
||||
Stream (boost::asio::io_context& service, StreamingDestination& local,
|
||||
std::shared_ptr<const i2p::data::LeaseSet> remote, int port = 0); // outgoing
|
||||
Stream (boost::asio::io_service& service, StreamingDestination& local); // incoming
|
||||
Stream (boost::asio::io_context& service, StreamingDestination& local); // incoming
|
||||
|
||||
~Stream ();
|
||||
uint32_t GetSendStreamID () const { return m_SendStreamID; };
|
||||
@@ -185,6 +187,7 @@ namespace stream
|
||||
std::shared_ptr<const i2p::data::IdentityEx> GetRemoteIdentity () const { return m_RemoteIdentity; };
|
||||
bool IsOpen () const { return m_Status == eStreamStatusOpen; };
|
||||
bool IsEstablished () const { return m_SendStreamID; };
|
||||
bool IsIncoming () const { return m_IsIncoming; };
|
||||
StreamStatus GetStatus () const { return m_Status; };
|
||||
StreamingDestination& GetLocalDestination () { return m_LocalDestination; };
|
||||
void ResetRoutingPath ();
|
||||
@@ -200,7 +203,7 @@ namespace stream
|
||||
size_t ReadSome (uint8_t * buf, size_t len) { return ConcatenatePackets (buf, len); };
|
||||
size_t Receive (uint8_t * buf, size_t len, int timeout);
|
||||
|
||||
void AsyncClose() { m_Service.post(std::bind(&Stream::Close, shared_from_this())); };
|
||||
void AsyncClose() { boost::asio::post(m_Service, std::bind(&Stream::Close, shared_from_this())); };
|
||||
|
||||
/** only call close from destination thread, use Stream::AsyncClose for other threads */
|
||||
void Close ();
|
||||
@@ -236,7 +239,7 @@ namespace stream
|
||||
void UpdateCurrentRemoteLease (bool expired = false);
|
||||
|
||||
template<typename Buffer, typename ReceiveHandler>
|
||||
void HandleReceiveTimer (const boost::system::error_code& ecode, const Buffer& buffer, ReceiveHandler handler, int remainingTimeout);
|
||||
void HandleReceiveTimer (const boost::system::error_code& ecode, Buffer& buffer, ReceiveHandler handler, int remainingTimeout);
|
||||
|
||||
void ScheduleSend ();
|
||||
void HandleSendTimer (const boost::system::error_code& ecode);
|
||||
@@ -248,10 +251,12 @@ namespace stream
|
||||
|
||||
void UpdatePacingTime ();
|
||||
void ProcessWindowDrop ();
|
||||
void HalveWindowSize ();
|
||||
void CancelRemoteLeaseChange ();
|
||||
|
||||
private:
|
||||
|
||||
boost::asio::io_service& m_Service;
|
||||
boost::asio::io_context& m_Service;
|
||||
uint32_t m_SendStreamID, m_RecvStreamID, m_SequenceNumber;
|
||||
uint32_t m_DropWindowDelaySequenceNumber;
|
||||
uint32_t m_TunnelsChangeSequenceNumber;
|
||||
@@ -259,6 +264,7 @@ namespace stream
|
||||
int32_t m_PreviousReceivedSequenceNumber;
|
||||
int32_t m_LastConfirmedReceivedSequenceNumber; // for limit inbound speed
|
||||
StreamStatus m_Status;
|
||||
bool m_IsIncoming;
|
||||
bool m_IsAckSendScheduled;
|
||||
bool m_IsNAcked;
|
||||
bool m_IsFirstACK;
|
||||
@@ -268,12 +274,15 @@ namespace stream
|
||||
bool m_IsWinDropped;
|
||||
bool m_IsTimeOutResend;
|
||||
bool m_IsImmediateAckRequested;
|
||||
bool m_IsRemoteLeaseChangeInProgress;
|
||||
bool m_DoubleWinIncCounter;
|
||||
StreamingDestination& m_LocalDestination;
|
||||
std::shared_ptr<const i2p::data::IdentityEx> m_RemoteIdentity;
|
||||
std::shared_ptr<const i2p::crypto::Verifier> m_TransientVerifier; // in case of offline key
|
||||
std::shared_ptr<const i2p::data::LeaseSet> m_RemoteLeaseSet;
|
||||
std::shared_ptr<i2p::garlic::GarlicRoutingSession> m_RoutingSession;
|
||||
std::shared_ptr<const i2p::data::Lease> m_CurrentRemoteLease;
|
||||
std::shared_ptr<const i2p::data::Lease> m_NextRemoteLease;
|
||||
std::shared_ptr<i2p::tunnel::OutboundTunnel> m_CurrentOutboundTunnel;
|
||||
std::queue<Packet *> m_ReceiveQueue;
|
||||
std::set<Packet *, PacketCmp> m_SavedPackets;
|
||||
@@ -286,10 +295,10 @@ namespace stream
|
||||
SendBufferQueue m_SendBuffer;
|
||||
double m_RTT, m_SlowRTT, m_SlowRTT2;
|
||||
float m_WindowSize, m_LastWindowDropSize, m_WindowDropTargetSize;
|
||||
int m_WindowIncCounter, m_RTO, m_AckDelay, m_PrevRTTSample;
|
||||
int m_WindowIncCounter, m_RTO, m_AckDelay, m_PrevRTTSample, m_WindowSizeTail;
|
||||
double m_Jitter;
|
||||
uint64_t m_MinPacingTime, m_PacingTime, m_PacingTimeRem, // microseconds
|
||||
m_LastSendTime; // miliseconds
|
||||
m_LastSendTime, m_LastACKRecieveTime, m_ACKRecieveInterval, m_RemoteLeaseChangeTime; // milliseconds
|
||||
uint64_t m_LastACKSendTime, m_PacketACKInterval, m_PacketACKIntervalRem; // for limit inbound speed
|
||||
int m_NumResendAttempts, m_NumPacketsToSend;
|
||||
size_t m_MTU;
|
||||
@@ -368,7 +377,7 @@ namespace stream
|
||||
void Stream::AsyncReceive (const Buffer& buffer, ReceiveHandler handler, int timeout)
|
||||
{
|
||||
auto s = shared_from_this();
|
||||
m_Service.post ([s, buffer, handler, timeout](void)
|
||||
boost::asio::post (m_Service, [s, buffer, handler, timeout](void)
|
||||
{
|
||||
if (!s->m_ReceiveQueue.empty () || s->m_Status == eStreamStatusReset)
|
||||
s->HandleReceiveTimer (boost::asio::error::make_error_code (boost::asio::error::operation_aborted), buffer, handler, 0);
|
||||
@@ -387,9 +396,9 @@ namespace stream
|
||||
}
|
||||
|
||||
template<typename Buffer, typename ReceiveHandler>
|
||||
void Stream::HandleReceiveTimer (const boost::system::error_code& ecode, const Buffer& buffer, ReceiveHandler handler, int remainingTimeout)
|
||||
void Stream::HandleReceiveTimer (const boost::system::error_code& ecode, Buffer& buffer, ReceiveHandler handler, int remainingTimeout)
|
||||
{
|
||||
size_t received = ConcatenatePackets (boost::asio::buffer_cast<uint8_t *>(buffer), boost::asio::buffer_size(buffer));
|
||||
size_t received = ConcatenatePackets ((uint8_t *)buffer.data (), buffer.size ());
|
||||
if (received > 0)
|
||||
handler (boost::system::error_code (), received);
|
||||
else if (ecode == boost::asio::error::operation_aborted)
|
||||
|
||||
@@ -60,18 +60,16 @@ namespace util
|
||||
static void SyncTimeWithNTP (const std::string& address)
|
||||
{
|
||||
LogPrint (eLogInfo, "Timestamp: NTP request to ", address);
|
||||
boost::asio::io_service service;
|
||||
boost::asio::io_context service;
|
||||
boost::system::error_code ec;
|
||||
auto it = boost::asio::ip::udp::resolver (service).resolve (
|
||||
boost::asio::ip::udp::resolver::query (address, "ntp"), ec);
|
||||
auto endpoints = boost::asio::ip::udp::resolver (service).resolve (address, "ntp", ec);
|
||||
if (!ec)
|
||||
{
|
||||
bool found = false;
|
||||
boost::asio::ip::udp::resolver::iterator end;
|
||||
boost::asio::ip::udp::endpoint ep;
|
||||
while (it != end)
|
||||
for (const auto& it: endpoints)
|
||||
{
|
||||
ep = *it;
|
||||
ep = it;
|
||||
if (!ep.address ().is_unspecified ())
|
||||
{
|
||||
if (ep.address ().is_v4 ())
|
||||
@@ -88,7 +86,6 @@ namespace util
|
||||
}
|
||||
}
|
||||
if (found) break;
|
||||
it++;
|
||||
}
|
||||
if (!found)
|
||||
{
|
||||
@@ -154,7 +151,7 @@ namespace util
|
||||
{
|
||||
m_IsRunning = true;
|
||||
LogPrint(eLogInfo, "Timestamp: NTP time sync starting");
|
||||
m_Service.post (std::bind (&NTPTimeSync::Sync, this));
|
||||
boost::asio::post (m_Service, std::bind (&NTPTimeSync::Sync, this));
|
||||
m_Thread.reset (new std::thread (std::bind (&NTPTimeSync::Run, this)));
|
||||
}
|
||||
else
|
||||
|
||||
@@ -52,7 +52,7 @@ namespace util
|
||||
|
||||
bool m_IsRunning;
|
||||
std::unique_ptr<std::thread> m_Thread;
|
||||
boost::asio::io_service m_Service;
|
||||
boost::asio::io_context m_Service;
|
||||
boost::asio::deadline_timer m_Timer;
|
||||
int m_SyncInterval;
|
||||
std::vector<std::string> m_NTPServersList;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2022, The PurpleI2P Project
|
||||
* Copyright (c) 2013-2024, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
@@ -8,9 +8,12 @@
|
||||
|
||||
#include <string.h>
|
||||
#include "I2PEndian.h"
|
||||
#include "Crypto.h"
|
||||
#include "Log.h"
|
||||
#include "RouterContext.h"
|
||||
#include "I2NPProtocol.h"
|
||||
#include "Garlic.h"
|
||||
#include "ECIESX25519AEADRatchetSession.h"
|
||||
#include "Tunnel.h"
|
||||
#include "Transports.h"
|
||||
#include "TransitTunnel.h"
|
||||
@@ -59,8 +62,7 @@ namespace tunnel
|
||||
auto num = m_TunnelDataMsgs.size ();
|
||||
if (num > 1)
|
||||
LogPrint (eLogDebug, "TransitTunnel: ", GetTunnelID (), "->", GetNextTunnelID (), " ", num);
|
||||
i2p::transport::transports.SendMessages (GetNextIdentHash (), m_TunnelDataMsgs);
|
||||
m_TunnelDataMsgs.clear ();
|
||||
i2p::transport::transports.SendMessages (GetNextIdentHash (), m_TunnelDataMsgs); // send and clear
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,5 +121,406 @@ namespace tunnel
|
||||
return std::make_shared<TransitTunnelParticipant> (receiveTunnelID, nextIdent, nextTunnelID, layerKey, ivKey);
|
||||
}
|
||||
}
|
||||
|
||||
TransitTunnels::TransitTunnels ():
|
||||
m_IsRunning (false)
|
||||
{
|
||||
}
|
||||
|
||||
TransitTunnels::~TransitTunnels ()
|
||||
{
|
||||
Stop ();
|
||||
}
|
||||
|
||||
void TransitTunnels::Start ()
|
||||
{
|
||||
m_IsRunning = true;
|
||||
m_Thread.reset (new std::thread (std::bind (&TransitTunnels::Run, this)));
|
||||
}
|
||||
|
||||
void TransitTunnels::Stop ()
|
||||
{
|
||||
m_IsRunning = false;
|
||||
m_TunnelBuildMsgQueue.WakeUp ();
|
||||
if (m_Thread)
|
||||
{
|
||||
m_Thread->join ();
|
||||
m_Thread = nullptr;
|
||||
}
|
||||
m_TransitTunnels.clear ();
|
||||
}
|
||||
|
||||
void TransitTunnels::Run ()
|
||||
{
|
||||
i2p::util::SetThreadName("TBM");
|
||||
uint64_t lastTs = 0;
|
||||
std::list<std::shared_ptr<I2NPMessage> > msgs;
|
||||
while (m_IsRunning)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (m_TunnelBuildMsgQueue.Wait (TRANSIT_TUNNELS_QUEUE_WAIT_INTERVAL, 0))
|
||||
{
|
||||
m_TunnelBuildMsgQueue.GetWholeQueue (msgs);
|
||||
while (!msgs.empty ())
|
||||
{
|
||||
auto msg = msgs.front (); msgs.pop_front ();
|
||||
if (!msg) continue;
|
||||
uint8_t typeID = msg->GetTypeID ();
|
||||
switch (typeID)
|
||||
{
|
||||
case eI2NPShortTunnelBuild:
|
||||
HandleShortTransitTunnelBuildMsg (std::move (msg));
|
||||
break;
|
||||
case eI2NPVariableTunnelBuild:
|
||||
HandleVariableTransitTunnelBuildMsg (std::move (msg));
|
||||
break;
|
||||
default:
|
||||
LogPrint (eLogWarning, "TransitTunnel: Unexpected message type ", (int) typeID);
|
||||
}
|
||||
if (!m_IsRunning) break;
|
||||
}
|
||||
}
|
||||
if (m_IsRunning)
|
||||
{
|
||||
uint64_t ts = i2p::util::GetSecondsSinceEpoch ();
|
||||
if (ts >= lastTs + TUNNEL_MANAGE_INTERVAL || ts + TUNNEL_MANAGE_INTERVAL < lastTs)
|
||||
{
|
||||
ManageTransitTunnels (ts);
|
||||
lastTs = ts;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (std::exception& ex)
|
||||
{
|
||||
LogPrint (eLogError, "TransitTunnel: Runtime exception: ", ex.what ());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TransitTunnels::PostTransitTunnelBuildMsg (std::shared_ptr<I2NPMessage>&& msg)
|
||||
{
|
||||
if (msg) m_TunnelBuildMsgQueue.Put (msg);
|
||||
}
|
||||
|
||||
void TransitTunnels::HandleShortTransitTunnelBuildMsg (std::shared_ptr<I2NPMessage>&& msg)
|
||||
{
|
||||
if (!msg) return;
|
||||
uint8_t * buf = msg->GetPayload();
|
||||
size_t len = msg->GetPayloadLength();
|
||||
int num = buf[0];
|
||||
LogPrint (eLogDebug, "TransitTunnel: ShortTunnelBuild ", num, " records");
|
||||
if (num > i2p::tunnel::MAX_NUM_RECORDS)
|
||||
{
|
||||
LogPrint (eLogError, "TransitTunnel: Too many records in ShortTunnelBuild message ", num);
|
||||
return;
|
||||
}
|
||||
if (len < num*SHORT_TUNNEL_BUILD_RECORD_SIZE + 1)
|
||||
{
|
||||
LogPrint (eLogError, "TransitTunnel: ShortTunnelBuild message of ", num, " records is too short ", len);
|
||||
return;
|
||||
}
|
||||
const uint8_t * record = buf + 1;
|
||||
for (int i = 0; i < num; i++)
|
||||
{
|
||||
if (!memcmp (record, (const uint8_t *)i2p::context.GetRouterInfo ().GetIdentHash (), 16))
|
||||
{
|
||||
LogPrint (eLogDebug, "TransitTunnel: Short request record ", i, " is ours");
|
||||
uint8_t clearText[SHORT_REQUEST_RECORD_CLEAR_TEXT_SIZE];
|
||||
if (!i2p::context.DecryptTunnelShortRequestRecord (record + SHORT_REQUEST_RECORD_ENCRYPTED_OFFSET, clearText))
|
||||
{
|
||||
LogPrint (eLogWarning, "TransitTunnel: Can't decrypt short request record ", i);
|
||||
return;
|
||||
}
|
||||
if (clearText[SHORT_REQUEST_RECORD_LAYER_ENCRYPTION_TYPE]) // not AES
|
||||
{
|
||||
LogPrint (eLogWarning, "TransitTunnel: Unknown layer encryption type ", clearText[SHORT_REQUEST_RECORD_LAYER_ENCRYPTION_TYPE], " in short request record");
|
||||
return;
|
||||
}
|
||||
auto& noiseState = i2p::context.GetCurrentNoiseState ();
|
||||
uint8_t replyKey[32]; // AEAD/Chacha20/Poly1305
|
||||
i2p::crypto::AESKey layerKey, ivKey; // AES
|
||||
i2p::crypto::HKDF (noiseState.m_CK, nullptr, 0, "SMTunnelReplyKey", noiseState.m_CK);
|
||||
memcpy (replyKey, noiseState.m_CK + 32, 32);
|
||||
i2p::crypto::HKDF (noiseState.m_CK, nullptr, 0, "SMTunnelLayerKey", noiseState.m_CK);
|
||||
memcpy (layerKey, noiseState.m_CK + 32, 32);
|
||||
bool isEndpoint = clearText[SHORT_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_ENDPOINT_FLAG;
|
||||
if (isEndpoint)
|
||||
{
|
||||
i2p::crypto::HKDF (noiseState.m_CK, nullptr, 0, "TunnelLayerIVKey", noiseState.m_CK);
|
||||
memcpy (ivKey, noiseState.m_CK + 32, 32);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!memcmp ((const uint8_t *)i2p::context.GetIdentHash (), clearText + SHORT_REQUEST_RECORD_NEXT_IDENT_OFFSET, 32)) // if next ident is now ours
|
||||
{
|
||||
LogPrint (eLogWarning, "TransitTunnel: Next ident is ours in short request record");
|
||||
return;
|
||||
}
|
||||
memcpy (ivKey, noiseState.m_CK , 32);
|
||||
}
|
||||
|
||||
// check if we accept this tunnel
|
||||
std::shared_ptr<i2p::tunnel::TransitTunnel> transitTunnel;
|
||||
uint8_t retCode = 0;
|
||||
if (!i2p::context.AcceptsTunnels () || i2p::context.GetCongestionLevel (false) >= CONGESTION_LEVEL_FULL)
|
||||
retCode = 30;
|
||||
if (!retCode)
|
||||
{
|
||||
// create new transit tunnel
|
||||
transitTunnel = i2p::tunnel::CreateTransitTunnel (
|
||||
bufbe32toh (clearText + SHORT_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET),
|
||||
clearText + SHORT_REQUEST_RECORD_NEXT_IDENT_OFFSET,
|
||||
bufbe32toh (clearText + SHORT_REQUEST_RECORD_NEXT_TUNNEL_OFFSET),
|
||||
layerKey, ivKey,
|
||||
clearText[SHORT_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_GATEWAY_FLAG,
|
||||
clearText[SHORT_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_ENDPOINT_FLAG);
|
||||
if (!AddTransitTunnel (transitTunnel))
|
||||
retCode = 30;
|
||||
}
|
||||
|
||||
// encrypt reply
|
||||
uint8_t nonce[12];
|
||||
memset (nonce, 0, 12);
|
||||
uint8_t * reply = buf + 1;
|
||||
for (int j = 0; j < num; j++)
|
||||
{
|
||||
nonce[4] = j; // nonce is record #
|
||||
if (j == i)
|
||||
{
|
||||
memset (reply + SHORT_RESPONSE_RECORD_OPTIONS_OFFSET, 0, 2); // no options
|
||||
reply[SHORT_RESPONSE_RECORD_RET_OFFSET] = retCode;
|
||||
if (!i2p::crypto::AEADChaCha20Poly1305 (reply, SHORT_TUNNEL_BUILD_RECORD_SIZE - 16,
|
||||
noiseState.m_H, 32, replyKey, nonce, reply, SHORT_TUNNEL_BUILD_RECORD_SIZE, true)) // encrypt
|
||||
{
|
||||
LogPrint (eLogWarning, "TransitTunnel: Short reply AEAD encryption failed");
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
i2p::crypto::ChaCha20 (reply, SHORT_TUNNEL_BUILD_RECORD_SIZE, replyKey, nonce, reply);
|
||||
reply += SHORT_TUNNEL_BUILD_RECORD_SIZE;
|
||||
}
|
||||
// send reply
|
||||
auto onDrop = [transitTunnel]()
|
||||
{
|
||||
if (transitTunnel)
|
||||
{
|
||||
LogPrint (eLogDebug, "TransitTunnel: Failed to send reply for transit tunnel ", transitTunnel->GetTunnelID ());
|
||||
auto t = transitTunnel->GetCreationTime ();
|
||||
if (t > i2p::tunnel::TUNNEL_EXPIRATION_TIMEOUT)
|
||||
// make transit tunnel expired
|
||||
transitTunnel->SetCreationTime (t - i2p::tunnel::TUNNEL_EXPIRATION_TIMEOUT);
|
||||
}
|
||||
};
|
||||
if (isEndpoint)
|
||||
{
|
||||
auto replyMsg = NewI2NPShortMessage ();
|
||||
replyMsg->Concat (buf, len);
|
||||
replyMsg->FillI2NPMessageHeader (eI2NPShortTunnelBuildReply, bufbe32toh (clearText + SHORT_REQUEST_RECORD_SEND_MSG_ID_OFFSET));
|
||||
if (transitTunnel) replyMsg->onDrop = onDrop;
|
||||
if (memcmp ((const uint8_t *)i2p::context.GetIdentHash (),
|
||||
clearText + SHORT_REQUEST_RECORD_NEXT_IDENT_OFFSET, 32)) // reply IBGW is not local?
|
||||
{
|
||||
i2p::crypto::HKDF (noiseState.m_CK, nullptr, 0, "RGarlicKeyAndTag", noiseState.m_CK);
|
||||
uint64_t tag;
|
||||
memcpy (&tag, noiseState.m_CK, 8);
|
||||
// we send it to reply tunnel
|
||||
i2p::transport::transports.SendMessage (clearText + SHORT_REQUEST_RECORD_NEXT_IDENT_OFFSET,
|
||||
CreateTunnelGatewayMsg (bufbe32toh (clearText + SHORT_REQUEST_RECORD_NEXT_TUNNEL_OFFSET),
|
||||
i2p::garlic::WrapECIESX25519Message (replyMsg, noiseState.m_CK + 32, tag)));
|
||||
}
|
||||
else
|
||||
{
|
||||
// IBGW is local
|
||||
uint32_t tunnelID = bufbe32toh (clearText + SHORT_REQUEST_RECORD_NEXT_TUNNEL_OFFSET);
|
||||
auto tunnel = i2p::tunnel::tunnels.GetTunnel (tunnelID);
|
||||
if (tunnel)
|
||||
{
|
||||
tunnel->SendTunnelDataMsg (replyMsg);
|
||||
tunnel->FlushTunnelDataMsgs ();
|
||||
}
|
||||
else
|
||||
LogPrint (eLogWarning, "I2NP: Tunnel ", tunnelID, " not found for short tunnel build reply");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
auto msg = CreateI2NPMessage (eI2NPShortTunnelBuild, buf, len,
|
||||
bufbe32toh (clearText + SHORT_REQUEST_RECORD_SEND_MSG_ID_OFFSET));
|
||||
if (transitTunnel) msg->onDrop = onDrop;
|
||||
i2p::transport::transports.SendMessage (clearText + SHORT_REQUEST_RECORD_NEXT_IDENT_OFFSET, msg);
|
||||
}
|
||||
return;
|
||||
}
|
||||
record += SHORT_TUNNEL_BUILD_RECORD_SIZE;
|
||||
}
|
||||
}
|
||||
|
||||
bool TransitTunnels::HandleBuildRequestRecords (int num, uint8_t * records, uint8_t * clearText)
|
||||
{
|
||||
for (int i = 0; i < num; i++)
|
||||
{
|
||||
uint8_t * record = records + i*TUNNEL_BUILD_RECORD_SIZE;
|
||||
if (!memcmp (record + BUILD_REQUEST_RECORD_TO_PEER_OFFSET, (const uint8_t *)i2p::context.GetRouterInfo ().GetIdentHash (), 16))
|
||||
{
|
||||
LogPrint (eLogDebug, "TransitTunnel: Build request record ", i, " is ours");
|
||||
if (!i2p::context.DecryptTunnelBuildRecord (record + BUILD_REQUEST_RECORD_ENCRYPTED_OFFSET, clearText))
|
||||
{
|
||||
LogPrint (eLogWarning, "TransitTunnel: Failed to decrypt tunnel build record");
|
||||
return false;
|
||||
}
|
||||
if (!memcmp ((const uint8_t *)i2p::context.GetIdentHash (), clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET, 32) && // if next ident is now ours
|
||||
!(clearText[ECIES_BUILD_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_ENDPOINT_FLAG)) // and not endpoint
|
||||
{
|
||||
LogPrint (eLogWarning, "TransitTunnel: Next ident is ours in tunnel build record");
|
||||
return false;
|
||||
}
|
||||
uint8_t retCode = 0;
|
||||
// decide if we should accept tunnel
|
||||
bool accept = i2p::context.AcceptsTunnels ();
|
||||
if (accept)
|
||||
{
|
||||
auto congestionLevel = i2p::context.GetCongestionLevel (false);
|
||||
if (congestionLevel >= CONGESTION_LEVEL_MEDIUM)
|
||||
{
|
||||
if (congestionLevel < CONGESTION_LEVEL_FULL)
|
||||
{
|
||||
// random reject depending on congestion level
|
||||
int level = i2p::tunnel::tunnels.GetRng ()() % (CONGESTION_LEVEL_FULL - CONGESTION_LEVEL_MEDIUM) + CONGESTION_LEVEL_MEDIUM;
|
||||
if (congestionLevel > level)
|
||||
accept = false;
|
||||
}
|
||||
else
|
||||
accept = false;
|
||||
}
|
||||
}
|
||||
// replace record to reply
|
||||
if (accept)
|
||||
{
|
||||
auto transitTunnel = i2p::tunnel::CreateTransitTunnel (
|
||||
bufbe32toh (clearText + ECIES_BUILD_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET),
|
||||
clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET,
|
||||
bufbe32toh (clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET),
|
||||
clearText + ECIES_BUILD_REQUEST_RECORD_LAYER_KEY_OFFSET,
|
||||
clearText + ECIES_BUILD_REQUEST_RECORD_IV_KEY_OFFSET,
|
||||
clearText[ECIES_BUILD_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_GATEWAY_FLAG,
|
||||
clearText[ECIES_BUILD_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_ENDPOINT_FLAG);
|
||||
if (!AddTransitTunnel (transitTunnel))
|
||||
retCode = 30;
|
||||
}
|
||||
else
|
||||
retCode = 30; // always reject with bandwidth reason (30)
|
||||
|
||||
memset (record + ECIES_BUILD_RESPONSE_RECORD_OPTIONS_OFFSET, 0, 2); // no options
|
||||
record[ECIES_BUILD_RESPONSE_RECORD_RET_OFFSET] = retCode;
|
||||
// encrypt reply
|
||||
i2p::crypto::CBCEncryption encryption;
|
||||
for (int j = 0; j < num; j++)
|
||||
{
|
||||
uint8_t * reply = records + j*TUNNEL_BUILD_RECORD_SIZE;
|
||||
if (j == i)
|
||||
{
|
||||
uint8_t nonce[12];
|
||||
memset (nonce, 0, 12);
|
||||
auto& noiseState = i2p::context.GetCurrentNoiseState ();
|
||||
if (!i2p::crypto::AEADChaCha20Poly1305 (reply, TUNNEL_BUILD_RECORD_SIZE - 16,
|
||||
noiseState.m_H, 32, noiseState.m_CK, nonce, reply, TUNNEL_BUILD_RECORD_SIZE, true)) // encrypt
|
||||
{
|
||||
LogPrint (eLogWarning, "TransitTunnel: Reply AEAD encryption failed");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
encryption.SetKey (clearText + ECIES_BUILD_REQUEST_RECORD_REPLY_KEY_OFFSET);
|
||||
encryption.Encrypt(reply, TUNNEL_BUILD_RECORD_SIZE, clearText + ECIES_BUILD_REQUEST_RECORD_REPLY_IV_OFFSET, reply);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void TransitTunnels::HandleVariableTransitTunnelBuildMsg (std::shared_ptr<I2NPMessage>&& msg)
|
||||
{
|
||||
if (!msg) return;
|
||||
uint8_t * buf = msg->GetPayload();
|
||||
size_t len = msg->GetPayloadLength();
|
||||
int num = buf[0];
|
||||
LogPrint (eLogDebug, "TransitTunnel: VariableTunnelBuild ", num, " records");
|
||||
if (num > i2p::tunnel::MAX_NUM_RECORDS)
|
||||
{
|
||||
LogPrint (eLogError, "TransitTunnle: Too many records in VaribleTunnelBuild message ", num);
|
||||
return;
|
||||
}
|
||||
if (len < num*TUNNEL_BUILD_RECORD_SIZE + 1)
|
||||
{
|
||||
LogPrint (eLogError, "TransitTunnel: VaribleTunnelBuild message of ", num, " records is too short ", len);
|
||||
return;
|
||||
}
|
||||
uint8_t clearText[ECIES_BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE];
|
||||
if (HandleBuildRequestRecords (num, buf + 1, clearText))
|
||||
{
|
||||
if (clearText[ECIES_BUILD_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_ENDPOINT_FLAG) // we are endpoint of outboud tunnel
|
||||
{
|
||||
// so we send it to reply tunnel
|
||||
i2p::transport::transports.SendMessage (clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET,
|
||||
CreateTunnelGatewayMsg (bufbe32toh (clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET),
|
||||
eI2NPVariableTunnelBuildReply, buf, len,
|
||||
bufbe32toh (clearText + ECIES_BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET)));
|
||||
}
|
||||
else
|
||||
i2p::transport::transports.SendMessage (clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET,
|
||||
CreateI2NPMessage (eI2NPVariableTunnelBuild, buf, len,
|
||||
bufbe32toh (clearText + ECIES_BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET)));
|
||||
}
|
||||
}
|
||||
|
||||
bool TransitTunnels::AddTransitTunnel (std::shared_ptr<TransitTunnel> tunnel)
|
||||
{
|
||||
if (tunnels.AddTunnel (tunnel))
|
||||
m_TransitTunnels.push_back (tunnel);
|
||||
else
|
||||
{
|
||||
LogPrint (eLogError, "TransitTunnel: Tunnel with id ", tunnel->GetTunnelID (), " already exists");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void TransitTunnels::ManageTransitTunnels (uint64_t ts)
|
||||
{
|
||||
for (auto it = m_TransitTunnels.begin (); it != m_TransitTunnels.end ();)
|
||||
{
|
||||
auto tunnel = *it;
|
||||
if (ts > tunnel->GetCreationTime () + TUNNEL_EXPIRATION_TIMEOUT ||
|
||||
ts + TUNNEL_EXPIRATION_TIMEOUT < tunnel->GetCreationTime ())
|
||||
{
|
||||
LogPrint (eLogDebug, "TransitTunnel: Transit tunnel with id ", tunnel->GetTunnelID (), " expired");
|
||||
tunnels.RemoveTunnel (tunnel->GetTunnelID ());
|
||||
it = m_TransitTunnels.erase (it);
|
||||
}
|
||||
else
|
||||
{
|
||||
tunnel->Cleanup ();
|
||||
it++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int TransitTunnels::GetTransitTunnelsExpirationTimeout ()
|
||||
{
|
||||
int timeout = 0;
|
||||
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
|
||||
// TODO: possible race condition with I2PControl
|
||||
for (const auto& it : m_TransitTunnels)
|
||||
{
|
||||
int t = it->GetCreationTime () + TUNNEL_EXPIRATION_TIMEOUT - ts;
|
||||
if (t > timeout) timeout = t;
|
||||
}
|
||||
return timeout;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2023, The PurpleI2P Project
|
||||
* Copyright (c) 2013-2024, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
@@ -10,10 +10,11 @@
|
||||
#define TRANSIT_TUNNEL_H__
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <vector>
|
||||
#include <list>
|
||||
#include <mutex>
|
||||
#include <memory>
|
||||
#include "Crypto.h"
|
||||
#include "Queue.h"
|
||||
#include "I2NPProtocol.h"
|
||||
#include "TunnelEndpoint.h"
|
||||
#include "TunnelGateway.h"
|
||||
@@ -61,7 +62,7 @@ namespace tunnel
|
||||
private:
|
||||
|
||||
size_t m_NumTransmittedBytes;
|
||||
std::vector<std::shared_ptr<i2p::I2NPMessage> > m_TunnelDataMsgs;
|
||||
std::list<std::shared_ptr<i2p::I2NPMessage> > m_TunnelDataMsgs;
|
||||
};
|
||||
|
||||
class TransitTunnelGateway: public TransitTunnel
|
||||
@@ -72,7 +73,7 @@ namespace tunnel
|
||||
const i2p::data::IdentHash& nextIdent, uint32_t nextTunnelID,
|
||||
const i2p::crypto::AESKey& layerKey, const i2p::crypto::AESKey& ivKey):
|
||||
TransitTunnel (receiveTunnelID, nextIdent, nextTunnelID,
|
||||
layerKey, ivKey), m_Gateway(this) {};
|
||||
layerKey, ivKey), m_Gateway(*this) {};
|
||||
|
||||
void SendTunnelDataMsg (std::shared_ptr<i2p::I2NPMessage> msg) override;
|
||||
void FlushTunnelDataMsgs () override;
|
||||
@@ -108,6 +109,48 @@ namespace tunnel
|
||||
const i2p::data::IdentHash& nextIdent, uint32_t nextTunnelID,
|
||||
const i2p::crypto::AESKey& layerKey, const i2p::crypto::AESKey& ivKey,
|
||||
bool isGateway, bool isEndpoint);
|
||||
|
||||
|
||||
const int TRANSIT_TUNNELS_QUEUE_WAIT_INTERVAL = 10; // in seconds
|
||||
|
||||
class TransitTunnels
|
||||
{
|
||||
public:
|
||||
|
||||
TransitTunnels ();
|
||||
~TransitTunnels ();
|
||||
|
||||
void Start ();
|
||||
void Stop ();
|
||||
void PostTransitTunnelBuildMsg (std::shared_ptr<I2NPMessage>&& msg);
|
||||
|
||||
size_t GetNumTransitTunnels () const { return m_TransitTunnels.size (); }
|
||||
int GetTransitTunnelsExpirationTimeout ();
|
||||
|
||||
private:
|
||||
|
||||
bool AddTransitTunnel (std::shared_ptr<TransitTunnel> tunnel);
|
||||
void ManageTransitTunnels (uint64_t ts);
|
||||
|
||||
void HandleShortTransitTunnelBuildMsg (std::shared_ptr<I2NPMessage>&& msg);
|
||||
void HandleVariableTransitTunnelBuildMsg (std::shared_ptr<I2NPMessage>&& msg);
|
||||
bool HandleBuildRequestRecords (int num, uint8_t * records, uint8_t * clearText);
|
||||
|
||||
void Run ();
|
||||
|
||||
private:
|
||||
|
||||
volatile bool m_IsRunning;
|
||||
std::unique_ptr<std::thread> m_Thread;
|
||||
std::list<std::shared_ptr<TransitTunnel> > m_TransitTunnels;
|
||||
i2p::util::Queue<std::shared_ptr<I2NPMessage> > m_TunnelBuildMsgQueue;
|
||||
|
||||
public:
|
||||
|
||||
// for HTTP only
|
||||
const auto& GetTransitTunnels () const { return m_TransitTunnels; };
|
||||
size_t GetTunnelBuildMsgQueueSize () const { return m_TunnelBuildMsgQueue.GetSize (); };
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -144,8 +144,12 @@ namespace transport
|
||||
void SetLastActivityTimestamp (uint64_t ts) { m_LastActivityTimestamp = ts; };
|
||||
|
||||
virtual uint32_t GetRelayTag () const { return 0; };
|
||||
virtual void SendLocalRouterInfo (bool update = false) { SendI2NPMessages ({ CreateDatabaseStoreMsg () }); };
|
||||
virtual void SendI2NPMessages (const std::vector<std::shared_ptr<I2NPMessage> >& msgs) = 0;
|
||||
virtual void SendLocalRouterInfo (bool update = false)
|
||||
{
|
||||
std::list<std::shared_ptr<I2NPMessage> > msgs{ CreateDatabaseStoreMsg () };
|
||||
SendI2NPMessages (msgs);
|
||||
};
|
||||
virtual void SendI2NPMessages (std::list<std::shared_ptr<I2NPMessage> >& msgs) = 0;
|
||||
virtual bool IsEstablished () const = 0;
|
||||
|
||||
private:
|
||||
|
||||
@@ -25,7 +25,7 @@ namespace transport
|
||||
{
|
||||
template<typename Keys>
|
||||
EphemeralKeysSupplier<Keys>::EphemeralKeysSupplier (int size):
|
||||
m_QueueSize (size), m_IsRunning (false), m_Thread (nullptr)
|
||||
m_QueueSize (size), m_IsRunning (false)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ namespace transport
|
||||
void EphemeralKeysSupplier<Keys>::Start ()
|
||||
{
|
||||
m_IsRunning = true;
|
||||
m_Thread = new std::thread (std::bind (&EphemeralKeysSupplier<Keys>::Run, this));
|
||||
m_Thread.reset (new std::thread (std::bind (&EphemeralKeysSupplier<Keys>::Run, this)));
|
||||
}
|
||||
|
||||
template<typename Keys>
|
||||
@@ -53,8 +53,7 @@ namespace transport
|
||||
if (m_Thread)
|
||||
{
|
||||
m_Thread->join ();
|
||||
delete m_Thread;
|
||||
m_Thread = 0;
|
||||
m_Thread = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,18 +65,19 @@ namespace transport
|
||||
while (m_IsRunning)
|
||||
{
|
||||
int num, total = 0;
|
||||
while ((num = m_QueueSize - (int)m_Queue.size ()) > 0 && total < 10)
|
||||
while ((num = m_QueueSize - (int)m_Queue.size ()) > 0 && total < m_QueueSize)
|
||||
{
|
||||
CreateEphemeralKeys (num);
|
||||
total += num;
|
||||
}
|
||||
if (total >= 10)
|
||||
if (total > m_QueueSize)
|
||||
{
|
||||
LogPrint (eLogWarning, "Transports: ", total, " ephemeral keys generated at the time");
|
||||
std::this_thread::sleep_for (std::chrono::seconds(1)); // take a break
|
||||
}
|
||||
else
|
||||
{
|
||||
m_KeysPool.CleanUpMt ();
|
||||
std::unique_lock<std::mutex> l(m_AcquiredMutex);
|
||||
if (!m_IsRunning) break;
|
||||
m_Acquired.wait (l); // wait for element gets acquired
|
||||
@@ -92,7 +92,7 @@ namespace transport
|
||||
{
|
||||
for (int i = 0; i < num; i++)
|
||||
{
|
||||
auto pair = std::make_shared<Keys> ();
|
||||
auto pair = m_KeysPool.AcquireSharedMt ();
|
||||
pair->GenerateKeys ();
|
||||
std::unique_lock<std::mutex> l(m_AcquiredMutex);
|
||||
m_Queue.push (pair);
|
||||
@@ -114,7 +114,7 @@ namespace transport
|
||||
}
|
||||
}
|
||||
// queue is empty, create new
|
||||
auto pair = std::make_shared<Keys> ();
|
||||
auto pair = m_KeysPool.AcquireSharedMt ();
|
||||
pair->GenerateKeys ();
|
||||
return pair;
|
||||
}
|
||||
@@ -124,12 +124,12 @@ namespace transport
|
||||
{
|
||||
if (pair)
|
||||
{
|
||||
std::unique_lock<std::mutex>l(m_AcquiredMutex);
|
||||
std::unique_lock<std::mutex> l(m_AcquiredMutex);
|
||||
if ((int)m_Queue.size () < 2*m_QueueSize)
|
||||
m_Queue.push (pair);
|
||||
}
|
||||
else
|
||||
LogPrint(eLogError, "Transports: Return null DHKeys");
|
||||
LogPrint(eLogError, "Transports: Return null keys");
|
||||
}
|
||||
|
||||
void Peer::UpdateParams (std::shared_ptr<const i2p::data::RouterInfo> router)
|
||||
@@ -149,7 +149,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_UpdateBandwidthTimer (nullptr), m_SSU2Server (nullptr), m_NTCP2Server (nullptr),
|
||||
m_X25519KeysPairSupplier (15), // 15 pre-generated keys
|
||||
m_X25519KeysPairSupplier (NUM_X25519_PRE_GENERATED_KEYS),
|
||||
m_TotalSentBytes (0), m_TotalReceivedBytes (0), m_TotalTransitTransmittedBytes (0),
|
||||
m_InBandwidth (0), m_OutBandwidth (0), m_TransitBandwidth (0),
|
||||
m_InBandwidth15s (0), m_OutBandwidth15s (0), m_TransitBandwidth15s (0),
|
||||
@@ -174,8 +174,8 @@ namespace transport
|
||||
{
|
||||
if (!m_Service)
|
||||
{
|
||||
m_Service = new boost::asio::io_service ();
|
||||
m_Work = new boost::asio::io_service::work (*m_Service);
|
||||
m_Service = new boost::asio::io_context ();
|
||||
m_Work = new boost::asio::executor_work_guard<boost::asio::io_context::executor_type> (m_Service->get_executor ());
|
||||
m_PeerCleanupTimer = new boost::asio::deadline_timer (*m_Service);
|
||||
m_PeerTestTimer = new boost::asio::deadline_timer (*m_Service);
|
||||
m_UpdateBandwidthTimer = new boost::asio::deadline_timer (*m_Service);
|
||||
@@ -249,7 +249,7 @@ namespace transport
|
||||
if (!address.empty ())
|
||||
{
|
||||
boost::system::error_code ec;
|
||||
auto addr = boost::asio::ip::address::from_string (address, ec);
|
||||
auto addr = boost::asio::ip::make_address (address, ec);
|
||||
if (!ec)
|
||||
{
|
||||
if (m_NTCP2Server) m_NTCP2Server->SetLocalAddress (addr);
|
||||
@@ -275,7 +275,7 @@ namespace transport
|
||||
if (!address.empty ())
|
||||
{
|
||||
boost::system::error_code ec;
|
||||
auto addr = boost::asio::ip::address::from_string (address, ec);
|
||||
auto addr = boost::asio::ip::make_address (address, ec);
|
||||
if (!ec)
|
||||
{
|
||||
if (m_NTCP2Server) m_NTCP2Server->SetLocalAddress (addr);
|
||||
@@ -302,7 +302,7 @@ namespace transport
|
||||
if (!address.empty ())
|
||||
{
|
||||
boost::system::error_code ec;
|
||||
auto addr = boost::asio::ip::address::from_string (address, ec);
|
||||
auto addr = boost::asio::ip::make_address (address, ec);
|
||||
if (!ec && m_NTCP2Server && i2p::util::net::IsYggdrasilAddress (addr))
|
||||
m_NTCP2Server->SetLocalAddress (addr);
|
||||
}
|
||||
@@ -447,18 +447,29 @@ namespace transport
|
||||
return std::max (bwCongestionLevel, tbwCongestionLevel);
|
||||
}
|
||||
|
||||
void Transports::SendMessage (const i2p::data::IdentHash& ident, std::shared_ptr<i2p::I2NPMessage> msg)
|
||||
std::future<std::shared_ptr<TransportSession> > Transports::SendMessage (const i2p::data::IdentHash& ident, std::shared_ptr<i2p::I2NPMessage> msg)
|
||||
{
|
||||
if (m_IsOnline)
|
||||
SendMessages (ident, std::vector<std::shared_ptr<i2p::I2NPMessage> > {msg });
|
||||
return SendMessages (ident, { msg });
|
||||
return {}; // invalid future
|
||||
}
|
||||
|
||||
void Transports::SendMessages (const i2p::data::IdentHash& ident, const std::vector<std::shared_ptr<i2p::I2NPMessage> >& msgs)
|
||||
std::future<std::shared_ptr<TransportSession> > Transports::SendMessages (const i2p::data::IdentHash& ident, std::list<std::shared_ptr<i2p::I2NPMessage> >& msgs)
|
||||
{
|
||||
m_Service->post (std::bind (&Transports::PostMessages, this, ident, msgs));
|
||||
std::list<std::shared_ptr<i2p::I2NPMessage> > msgs1;
|
||||
msgs.swap (msgs1);
|
||||
return SendMessages (ident, std::move (msgs1));
|
||||
}
|
||||
|
||||
void Transports::PostMessages (i2p::data::IdentHash ident, std::vector<std::shared_ptr<i2p::I2NPMessage> > msgs)
|
||||
std::future<std::shared_ptr<TransportSession> > Transports::SendMessages (const i2p::data::IdentHash& ident, std::list<std::shared_ptr<i2p::I2NPMessage> >&& msgs)
|
||||
{
|
||||
return boost::asio::post (*m_Service, boost::asio::use_future ([this, ident, msgs = std::move(msgs)] () mutable
|
||||
{
|
||||
return PostMessages (ident, msgs);
|
||||
}));
|
||||
}
|
||||
|
||||
std::shared_ptr<TransportSession> Transports::PostMessages (const i2p::data::IdentHash& ident, std::list<std::shared_ptr<i2p::I2NPMessage> >& msgs)
|
||||
{
|
||||
if (ident == i2p::context.GetRouterInfo ().GetIdentHash ())
|
||||
{
|
||||
@@ -466,25 +477,30 @@ namespace transport
|
||||
for (auto& it: msgs)
|
||||
m_LoopbackHandler.PutNextMessage (std::move (it));
|
||||
m_LoopbackHandler.Flush ();
|
||||
return;
|
||||
return nullptr;
|
||||
}
|
||||
if(RoutesRestricted() && !IsRestrictedPeer(ident)) return;
|
||||
if(RoutesRestricted() && !IsRestrictedPeer(ident)) return nullptr;
|
||||
std::shared_ptr<Peer> peer;
|
||||
auto it = m_Peers.find (ident);
|
||||
if (it == m_Peers.end ())
|
||||
{
|
||||
std::lock_guard<std::mutex> l(m_PeersMutex);
|
||||
auto it = m_Peers.find (ident);
|
||||
if (it != m_Peers.end ())
|
||||
peer = it->second;
|
||||
}
|
||||
if (!peer)
|
||||
{
|
||||
// check if not banned
|
||||
if (i2p::data::IsRouterBanned (ident)) return; // don't create peer to unreachable router
|
||||
if (i2p::data::IsRouterBanned (ident)) return nullptr; // don't create peer to unreachable router
|
||||
// try to connect
|
||||
bool connected = false;
|
||||
try
|
||||
{
|
||||
auto r = netdb.FindRouter (ident);
|
||||
if (r && (r->IsUnreachable () || !r->IsReachableFrom (i2p::context.GetRouterInfo ()))) return; // router found but non-reachable
|
||||
{
|
||||
auto ts = i2p::util::GetSecondsSinceEpoch ();
|
||||
peer = std::make_shared<Peer>(r, ts);
|
||||
std::unique_lock<std::mutex> l(m_PeersMutex);
|
||||
if (r && (r->IsUnreachable () || !r->IsReachableFrom (i2p::context.GetRouterInfo ()))) return nullptr; // router found but non-reachable
|
||||
|
||||
peer = std::make_shared<Peer>(r, i2p::util::GetSecondsSinceEpoch ());
|
||||
{
|
||||
std::lock_guard<std::mutex> l(m_PeersMutex);
|
||||
peer = m_Peers.emplace (ident, peer).first->second;
|
||||
}
|
||||
if (peer)
|
||||
@@ -494,14 +510,16 @@ namespace transport
|
||||
{
|
||||
LogPrint (eLogError, "Transports: PostMessages exception:", ex.what ());
|
||||
}
|
||||
if (!connected) return;
|
||||
if (!connected) return nullptr;
|
||||
}
|
||||
else
|
||||
peer = it->second;
|
||||
|
||||
if (!peer) return;
|
||||
if (!peer) return nullptr;
|
||||
if (peer->IsConnected ())
|
||||
peer->sessions.front ()->SendI2NPMessages (msgs);
|
||||
{
|
||||
auto session = peer->sessions.front ();
|
||||
if (session) session->SendI2NPMessages (msgs);
|
||||
return session;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto sz = peer->delayedMessages.size ();
|
||||
@@ -512,25 +530,31 @@ namespace transport
|
||||
if (i2p::data::IsRouterBanned (ident))
|
||||
{
|
||||
LogPrint (eLogWarning, "Transports: Router ", ident.ToBase64 (), " is banned. Peer dropped");
|
||||
std::unique_lock<std::mutex> l(m_PeersMutex);
|
||||
std::lock_guard<std::mutex> l(m_PeersMutex);
|
||||
m_Peers.erase (ident);
|
||||
return;
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
for (auto& it1: msgs)
|
||||
if (sz > MAX_NUM_DELAYED_MESSAGES/2 && it1->onDrop)
|
||||
it1->Drop (); // drop earlier because we can handle it
|
||||
else
|
||||
peer->delayedMessages.push_back (it1);
|
||||
if (sz > MAX_NUM_DELAYED_MESSAGES/2)
|
||||
{
|
||||
for (auto& it1: msgs)
|
||||
if (it1->onDrop)
|
||||
it1->Drop (); // drop earlier because we can handle it
|
||||
else
|
||||
peer->delayedMessages.push_back (it1);
|
||||
}
|
||||
else
|
||||
peer->delayedMessages.splice (peer->delayedMessages.end (), msgs);
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint (eLogWarning, "Transports: Delayed messages queue size to ",
|
||||
ident.ToBase64 (), " exceeds ", MAX_NUM_DELAYED_MESSAGES);
|
||||
std::unique_lock<std::mutex> l(m_PeersMutex);
|
||||
std::lock_guard<std::mutex> l(m_PeersMutex);
|
||||
m_Peers.erase (ident);
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool Transports::ConnectToPeer (const i2p::data::IdentHash& ident, std::shared_ptr<Peer> peer)
|
||||
@@ -602,7 +626,7 @@ namespace transport
|
||||
if (!i2p::context.IsLimitedConnectivity () && peer->router->IsReachableFrom (i2p::context.GetRouterInfo ()))
|
||||
i2p::data::netdb.SetUnreachable (ident, true); // we are here because all connection attempts failed but router claimed them
|
||||
peer->Done ();
|
||||
std::unique_lock<std::mutex> l(m_PeersMutex);
|
||||
std::lock_guard<std::mutex> l(m_PeersMutex);
|
||||
m_Peers.erase (ident);
|
||||
return false;
|
||||
}
|
||||
@@ -610,7 +634,7 @@ namespace transport
|
||||
{
|
||||
LogPrint (eLogWarning, "Transports: Router ", ident.ToBase64 (), " is banned. Peer dropped");
|
||||
peer->Done ();
|
||||
std::unique_lock<std::mutex> l(m_PeersMutex);
|
||||
std::lock_guard<std::mutex> l(m_PeersMutex);
|
||||
m_Peers.erase (ident);
|
||||
return false;
|
||||
}
|
||||
@@ -672,32 +696,63 @@ namespace transport
|
||||
if (transport & compatibleTransports)
|
||||
peer->priority.push_back (transport);
|
||||
}
|
||||
if (peer->priority.empty ())
|
||||
{
|
||||
// try recently connected SSU2 if any
|
||||
auto supportedTransports = context.GetRouterInfo ().GetCompatibleTransports (false) &
|
||||
peer->router->GetCompatibleTransports (false);
|
||||
if (supportedTransports & (i2p::data::RouterInfo::eSSU2V4 | i2p::data::RouterInfo::eSSU2V6))
|
||||
{
|
||||
auto ep = peer->router->GetProfile ()->GetLastEndpoint ();
|
||||
if (!ep.address ().is_unspecified () && ep.port ())
|
||||
{
|
||||
if (ep.address ().is_v4 ())
|
||||
{
|
||||
if ((supportedTransports & i2p::data::RouterInfo::eSSU2V4) &&
|
||||
m_SSU2Server->IsConnectedRecently (ep, false))
|
||||
peer->priority.push_back (i2p::data::RouterInfo::eSSU2V4);
|
||||
}
|
||||
else if (ep.address ().is_v6 ())
|
||||
{
|
||||
if ((supportedTransports & i2p::data::RouterInfo::eSSU2V6) &&
|
||||
m_SSU2Server->IsConnectedRecently (ep))
|
||||
peer->priority.push_back (i2p::data::RouterInfo::eSSU2V6);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Transports::RequestComplete (std::shared_ptr<const i2p::data::RouterInfo> r, const i2p::data::IdentHash& ident)
|
||||
{
|
||||
m_Service->post (std::bind (&Transports::HandleRequestComplete, this, r, ident));
|
||||
boost::asio::post (*m_Service, std::bind (&Transports::HandleRequestComplete, this, r, ident));
|
||||
}
|
||||
|
||||
void Transports::HandleRequestComplete (std::shared_ptr<const i2p::data::RouterInfo> r, i2p::data::IdentHash ident)
|
||||
{
|
||||
auto it = m_Peers.find (ident);
|
||||
if (it != m_Peers.end ())
|
||||
std::shared_ptr<Peer> peer;
|
||||
{
|
||||
if (r)
|
||||
std::lock_guard<std::mutex> l(m_PeersMutex);
|
||||
auto it = m_Peers.find (ident);
|
||||
if (it != m_Peers.end ())
|
||||
{
|
||||
LogPrint (eLogDebug, "Transports: RouterInfo for ", ident.ToBase64 (), " found, trying to connect");
|
||||
it->second->SetRouter (r);
|
||||
if (!it->second->IsConnected ())
|
||||
ConnectToPeer (ident, it->second);
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint (eLogWarning, "Transports: RouterInfo not found, failed to send messages");
|
||||
std::unique_lock<std::mutex> l(m_PeersMutex);
|
||||
m_Peers.erase (it);
|
||||
}
|
||||
if (r)
|
||||
peer = it->second;
|
||||
else
|
||||
m_Peers.erase (it);
|
||||
}
|
||||
}
|
||||
|
||||
if (peer && !peer->router && r)
|
||||
{
|
||||
LogPrint (eLogDebug, "Transports: RouterInfo for ", ident.ToBase64 (), " found, trying to connect");
|
||||
peer->SetRouter (r);
|
||||
if (!peer->IsConnected ())
|
||||
ConnectToPeer (ident, peer);
|
||||
}
|
||||
else if (!r)
|
||||
LogPrint (eLogInfo, "Transports: RouterInfo not found, failed to send messages");
|
||||
|
||||
}
|
||||
|
||||
void Transports::DetectExternalIP ()
|
||||
@@ -807,7 +862,7 @@ namespace transport
|
||||
|
||||
void Transports::PeerConnected (std::shared_ptr<TransportSession> session)
|
||||
{
|
||||
m_Service->post([session, this]()
|
||||
boost::asio::post (*m_Service, [session, this]()
|
||||
{
|
||||
auto remoteIdentity = session->GetRemoteIdentity ();
|
||||
if (!remoteIdentity) return;
|
||||
@@ -840,7 +895,7 @@ namespace transport
|
||||
if (it->second->delayedMessages.size () > 0)
|
||||
{
|
||||
// check if first message is our DatabaseStore (publishing)
|
||||
auto firstMsg = peer->delayedMessages[0];
|
||||
auto firstMsg = peer->delayedMessages.front ();
|
||||
if (firstMsg && firstMsg->GetTypeID () == eI2NPDatabaseStore &&
|
||||
i2p::data::IdentHash(firstMsg->GetPayload () + DATABASE_STORE_KEY_OFFSET) == i2p::context.GetIdentHash ())
|
||||
sendDatabaseStore = false; // we have it in the list already
|
||||
@@ -850,8 +905,7 @@ namespace transport
|
||||
else
|
||||
session->SetTerminationTimeout (10); // most likely it's publishing, no follow-up messages expected, set timeout to 10 seconds
|
||||
peer->sessions.push_back (session);
|
||||
session->SendI2NPMessages (peer->delayedMessages);
|
||||
peer->delayedMessages.clear ();
|
||||
session->SendI2NPMessages (peer->delayedMessages); // send and clear
|
||||
}
|
||||
else // incoming connection or peer test
|
||||
{
|
||||
@@ -862,14 +916,17 @@ namespace transport
|
||||
return;
|
||||
}
|
||||
if (!session->IsOutgoing ()) // incoming
|
||||
session->SendI2NPMessages ({ CreateDatabaseStoreMsg () }); // send DatabaseStore
|
||||
{
|
||||
std::list<std::shared_ptr<I2NPMessage> > msgs{ CreateDatabaseStoreMsg () };
|
||||
session->SendI2NPMessages (msgs); // send DatabaseStore
|
||||
}
|
||||
auto r = i2p::data::netdb.FindRouter (ident); // router should be in netdb after SessionConfirmed
|
||||
if (r) r->GetProfile ()->Connected ();
|
||||
auto ts = i2p::util::GetSecondsSinceEpoch ();
|
||||
auto peer = std::make_shared<Peer>(r, ts);
|
||||
peer->sessions.push_back (session);
|
||||
peer->router = nullptr;
|
||||
std::unique_lock<std::mutex> l(m_PeersMutex);
|
||||
std::lock_guard<std::mutex> l(m_PeersMutex);
|
||||
m_Peers.emplace (ident, peer);
|
||||
}
|
||||
});
|
||||
@@ -877,7 +934,7 @@ namespace transport
|
||||
|
||||
void Transports::PeerDisconnected (std::shared_ptr<TransportSession> session)
|
||||
{
|
||||
m_Service->post([session, this]()
|
||||
boost::asio::post (*m_Service, [session, this]()
|
||||
{
|
||||
auto remoteIdentity = session->GetRemoteIdentity ();
|
||||
if (!remoteIdentity) return;
|
||||
@@ -898,7 +955,7 @@ namespace transport
|
||||
}
|
||||
else
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_PeersMutex);
|
||||
std::lock_guard<std::mutex> l(m_PeersMutex);
|
||||
m_Peers.erase (it);
|
||||
}
|
||||
}
|
||||
@@ -908,9 +965,13 @@ namespace transport
|
||||
|
||||
bool Transports::IsConnected (const i2p::data::IdentHash& ident) const
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_PeersMutex);
|
||||
std::lock_guard<std::mutex> l(m_PeersMutex);
|
||||
#if __cplusplus >= 202002L // C++20
|
||||
return m_Peers.contains (ident);
|
||||
#else
|
||||
auto it = m_Peers.find (ident);
|
||||
return it != m_Peers.end ();
|
||||
#endif
|
||||
}
|
||||
|
||||
void Transports::HandlePeerCleanupTimer (const boost::system::error_code& ecode)
|
||||
@@ -934,7 +995,7 @@ namespace transport
|
||||
auto profile = i2p::data::GetRouterProfile (it->first);
|
||||
if (profile) profile->Unreachable ();
|
||||
} */
|
||||
std::unique_lock<std::mutex> l(m_PeersMutex);
|
||||
std::lock_guard<std::mutex> l(m_PeersMutex);
|
||||
it = m_Peers.erase (it);
|
||||
}
|
||||
else
|
||||
@@ -984,7 +1045,7 @@ namespace transport
|
||||
{
|
||||
uint16_t inds[3];
|
||||
RAND_bytes ((uint8_t *)inds, sizeof (inds));
|
||||
std::unique_lock<std::mutex> l(m_PeersMutex);
|
||||
std::lock_guard<std::mutex> l(m_PeersMutex);
|
||||
auto count = m_Peers.size ();
|
||||
if(count == 0) return nullptr;
|
||||
inds[0] %= count;
|
||||
@@ -1221,7 +1282,7 @@ namespace transport
|
||||
std::string yggaddress; i2p::config::GetOption ("meshnets.yggaddress", yggaddress);
|
||||
if (!yggaddress.empty ())
|
||||
{
|
||||
yggaddr = boost::asio::ip::address_v6::from_string (yggaddress);
|
||||
yggaddr = boost::asio::ip::make_address (yggaddress).to_v6 ();
|
||||
if (yggaddr.is_unspecified () || !i2p::util::net::IsYggdrasilAddress (yggaddr) ||
|
||||
!i2p::util::net::IsLocalAddress (yggaddr))
|
||||
{
|
||||
@@ -1266,7 +1327,7 @@ namespace transport
|
||||
if (ipv6)
|
||||
{
|
||||
std::string ipv6Addr; i2p::config::GetOption("ntcp2.addressv6", ipv6Addr);
|
||||
auto addr = boost::asio::ip::address_v6::from_string (ipv6Addr);
|
||||
auto addr = boost::asio::ip::make_address (ipv6Addr).to_v6 ();
|
||||
if (!addr.is_unspecified () && addr != boost::asio::ip::address_v6::any ())
|
||||
i2p::context.UpdateNTCP2V6Address (addr); // set ipv6 address if configured
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
#include <future>
|
||||
#include <condition_variable>
|
||||
#include <functional>
|
||||
#include <unordered_map>
|
||||
@@ -26,6 +27,7 @@
|
||||
#include "RouterInfo.h"
|
||||
#include "I2NPProtocol.h"
|
||||
#include "Identity.h"
|
||||
#include "util.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
@@ -53,9 +55,10 @@ namespace transport
|
||||
|
||||
const int m_QueueSize;
|
||||
std::queue<std::shared_ptr<Keys> > m_Queue;
|
||||
i2p::util::MemoryPoolMt<Keys> m_KeysPool;
|
||||
|
||||
bool m_IsRunning;
|
||||
std::thread * m_Thread;
|
||||
std::unique_ptr<std::thread> m_Thread;
|
||||
std::condition_variable m_Acquired;
|
||||
std::mutex m_AcquiredMutex;
|
||||
};
|
||||
@@ -71,7 +74,7 @@ namespace transport
|
||||
std::shared_ptr<const i2p::data::RouterInfo> router;
|
||||
std::list<std::shared_ptr<TransportSession> > sessions;
|
||||
uint64_t creationTime, nextRouterInfoUpdateTime, lastSelectionTime;
|
||||
std::vector<std::shared_ptr<i2p::I2NPMessage> > delayedMessages;
|
||||
std::list<std::shared_ptr<i2p::I2NPMessage> > delayedMessages;
|
||||
std::vector<i2p::data::RouterInfo::SupportedTransports> priority;
|
||||
bool isHighBandwidth, isEligible;
|
||||
|
||||
@@ -108,7 +111,8 @@ namespace transport
|
||||
const int PEER_TEST_DELAY_INTERVAL_VARIANCE = 30; // in milliseconds
|
||||
const int MAX_NUM_DELAYED_MESSAGES = 150;
|
||||
const int CHECK_PROFILE_NUM_DELAYED_MESSAGES = 15; // check profile after
|
||||
|
||||
const int NUM_X25519_PRE_GENERATED_KEYS = 25; // pre-generated x25519 keys pairs
|
||||
|
||||
const int TRAFFIC_SAMPLE_COUNT = 301; // seconds
|
||||
|
||||
struct TrafficSample
|
||||
@@ -136,12 +140,13 @@ namespace transport
|
||||
bool IsOnline() const { return m_IsOnline; };
|
||||
void SetOnline (bool online);
|
||||
|
||||
boost::asio::io_service& GetService () { return *m_Service; };
|
||||
auto& GetService () { return *m_Service; };
|
||||
std::shared_ptr<i2p::crypto::X25519Keys> GetNextX25519KeysPair ();
|
||||
void ReuseX25519KeysPair (std::shared_ptr<i2p::crypto::X25519Keys> pair);
|
||||
|
||||
void SendMessage (const i2p::data::IdentHash& ident, std::shared_ptr<i2p::I2NPMessage> msg);
|
||||
void SendMessages (const i2p::data::IdentHash& ident, const std::vector<std::shared_ptr<i2p::I2NPMessage> >& msgs);
|
||||
std::future<std::shared_ptr<TransportSession> > SendMessage (const i2p::data::IdentHash& ident, std::shared_ptr<i2p::I2NPMessage> msg);
|
||||
std::future<std::shared_ptr<TransportSession> > SendMessages (const i2p::data::IdentHash& ident, std::list<std::shared_ptr<i2p::I2NPMessage> >& msgs);
|
||||
std::future<std::shared_ptr<TransportSession> > SendMessages (const i2p::data::IdentHash& ident, std::list<std::shared_ptr<i2p::I2NPMessage> >&& msgs);
|
||||
|
||||
void PeerConnected (std::shared_ptr<TransportSession> session);
|
||||
void PeerDisconnected (std::shared_ptr<TransportSession> session);
|
||||
@@ -185,7 +190,7 @@ namespace transport
|
||||
void Run ();
|
||||
void RequestComplete (std::shared_ptr<const i2p::data::RouterInfo> r, const i2p::data::IdentHash& ident);
|
||||
void HandleRequestComplete (std::shared_ptr<const i2p::data::RouterInfo> r, i2p::data::IdentHash ident);
|
||||
void PostMessages (i2p::data::IdentHash ident, std::vector<std::shared_ptr<i2p::I2NPMessage> > msgs);
|
||||
std::shared_ptr<TransportSession> PostMessages (const i2p::data::IdentHash& ident, std::list<std::shared_ptr<i2p::I2NPMessage> >& msgs);
|
||||
bool ConnectToPeer (const i2p::data::IdentHash& ident, std::shared_ptr<Peer> peer);
|
||||
void SetPriority (std::shared_ptr<Peer> peer) const;
|
||||
void HandlePeerCleanupTimer (const boost::system::error_code& ecode);
|
||||
@@ -203,8 +208,8 @@ namespace transport
|
||||
volatile bool m_IsOnline;
|
||||
bool m_IsRunning, m_IsNAT, m_CheckReserved;
|
||||
std::thread * m_Thread;
|
||||
boost::asio::io_service * m_Service;
|
||||
boost::asio::io_service::work * m_Work;
|
||||
boost::asio::io_context * m_Service;
|
||||
boost::asio::executor_work_guard<boost::asio::io_context::executor_type> * m_Work;
|
||||
boost::asio::deadline_timer * m_PeerCleanupTimer, * m_PeerTestTimer, * m_UpdateBandwidthTimer;
|
||||
|
||||
SSU2Server * m_SSU2Server;
|
||||
|
||||
@@ -130,8 +130,19 @@ namespace tunnel
|
||||
|
||||
bool Tunnel::HandleTunnelBuildResponse (uint8_t * msg, size_t len)
|
||||
{
|
||||
LogPrint (eLogDebug, "Tunnel: TunnelBuildResponse ", (int)msg[0], " records.");
|
||||
|
||||
int num = msg[0];
|
||||
LogPrint (eLogDebug, "Tunnel: TunnelBuildResponse ", num, " records.");
|
||||
if (num > MAX_NUM_RECORDS)
|
||||
{
|
||||
LogPrint (eLogError, "Tunnel: Too many records in TunnelBuildResponse", num);
|
||||
return false;
|
||||
}
|
||||
if (len < num*m_Config->GetRecordSize () + 1)
|
||||
{
|
||||
LogPrint (eLogError, "Tunnel: TunnelBuildResponse of ", num, " records is too short ", len);
|
||||
return false;
|
||||
}
|
||||
|
||||
TunnelHopConfig * hop = m_Config->GetLastHop ();
|
||||
while (hop)
|
||||
{
|
||||
@@ -152,7 +163,7 @@ namespace tunnel
|
||||
while (hop1)
|
||||
{
|
||||
auto idx = hop1->recordIndex;
|
||||
if (idx >= 0 && idx < msg[0])
|
||||
if (idx >= 0 && idx < num)
|
||||
hop->DecryptRecord (msg + 1, idx);
|
||||
else
|
||||
LogPrint (eLogWarning, "Tunnel: Hop index ", idx, " is out of range");
|
||||
@@ -250,7 +261,18 @@ namespace tunnel
|
||||
|
||||
void InboundTunnel::HandleTunnelDataMsg (std::shared_ptr<I2NPMessage>&& msg)
|
||||
{
|
||||
if (GetState () != eTunnelStateExpiring) SetState (eTunnelStateEstablished); // incoming messages means a tunnel is alive
|
||||
if (!IsEstablished () && GetState () != eTunnelStateExpiring)
|
||||
{
|
||||
// incoming messages means a tunnel is alive
|
||||
SetState (eTunnelStateEstablished);
|
||||
auto pool = GetTunnelPool ();
|
||||
if (pool)
|
||||
{
|
||||
// update LeaseSet
|
||||
auto dest = pool->GetLocalDestination ();
|
||||
if (dest) dest->SetLeaseSetUpdated (true);
|
||||
}
|
||||
}
|
||||
EncryptTunnelMsg (msg, msg);
|
||||
msg->from = GetSharedFromThis ();
|
||||
m_Endpoint.HandleDecryptedTunnelDataMsg (msg);
|
||||
@@ -339,7 +361,8 @@ namespace tunnel
|
||||
|
||||
Tunnels::Tunnels (): m_IsRunning (false), m_Thread (nullptr), m_MaxNumTransitTunnels (DEFAULT_MAX_NUM_TRANSIT_TUNNELS),
|
||||
m_TotalNumSuccesiveTunnelCreations (0), m_TotalNumFailedTunnelCreations (0), // for normal average
|
||||
m_TunnelCreationSuccessRate (TCSR_START_VALUE), m_TunnelCreationAttemptsNum(0)
|
||||
m_TunnelCreationSuccessRate (TCSR_START_VALUE), m_TunnelCreationAttemptsNum(0),
|
||||
m_Rng(i2p::util::GetMonotonicMicroseconds ()%1000000LL)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -350,12 +373,26 @@ namespace tunnel
|
||||
|
||||
std::shared_ptr<TunnelBase> Tunnels::GetTunnel (uint32_t tunnelID)
|
||||
{
|
||||
std::lock_guard<std::mutex> l(m_TunnelsMutex);
|
||||
auto it = m_Tunnels.find(tunnelID);
|
||||
if (it != m_Tunnels.end ())
|
||||
return it->second;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool Tunnels::AddTunnel (std::shared_ptr<TunnelBase> tunnel)
|
||||
{
|
||||
if (!tunnel) return false;
|
||||
std::lock_guard<std::mutex> l(m_TunnelsMutex);
|
||||
return m_Tunnels.emplace (tunnel->GetTunnelID (), tunnel).second;
|
||||
}
|
||||
|
||||
void Tunnels::RemoveTunnel (uint32_t tunnelID)
|
||||
{
|
||||
std::lock_guard<std::mutex> l(m_TunnelsMutex);
|
||||
m_Tunnels.erase (tunnelID);
|
||||
}
|
||||
|
||||
std::shared_ptr<InboundTunnel> Tunnels::GetPendingInboundTunnel (uint32_t replyMsgID)
|
||||
{
|
||||
return GetPendingTunnel (replyMsgID, m_PendingInboundTunnels);
|
||||
@@ -443,26 +480,16 @@ namespace tunnel
|
||||
}
|
||||
}
|
||||
|
||||
bool Tunnels::AddTransitTunnel (std::shared_ptr<TransitTunnel> tunnel)
|
||||
{
|
||||
if (m_Tunnels.emplace (tunnel->GetTunnelID (), tunnel).second)
|
||||
m_TransitTunnels.push_back (tunnel);
|
||||
else
|
||||
{
|
||||
LogPrint (eLogError, "Tunnel: Tunnel with id ", tunnel->GetTunnelID (), " already exists");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void Tunnels::Start ()
|
||||
{
|
||||
m_IsRunning = true;
|
||||
m_Thread = new std::thread (std::bind (&Tunnels::Run, this));
|
||||
m_TransitTunnels.Start ();
|
||||
}
|
||||
|
||||
void Tunnels::Stop ()
|
||||
{
|
||||
m_TransitTunnels.Stop ();
|
||||
m_IsRunning = false;
|
||||
m_Queue.WakeUp ();
|
||||
if (m_Thread)
|
||||
@@ -479,18 +506,21 @@ namespace tunnel
|
||||
std::this_thread::sleep_for (std::chrono::seconds(1)); // wait for other parts are ready
|
||||
|
||||
uint64_t lastTs = 0, lastPoolsTs = 0, lastMemoryPoolTs = 0;
|
||||
std::list<std::shared_ptr<I2NPMessage> > msgs;
|
||||
while (m_IsRunning)
|
||||
{
|
||||
try
|
||||
{
|
||||
auto msg = m_Queue.GetNextWithTimeout (1000); // 1 sec
|
||||
if (msg)
|
||||
if (m_Queue.Wait (1,0)) // 1 sec
|
||||
{
|
||||
m_Queue.GetWholeQueue (msgs);
|
||||
int numMsgs = 0;
|
||||
uint32_t prevTunnelID = 0, tunnelID = 0;
|
||||
std::shared_ptr<TunnelBase> prevTunnel;
|
||||
do
|
||||
while (!msgs.empty ())
|
||||
{
|
||||
auto msg = msgs.front (); msgs.pop_front ();
|
||||
if (!msg) continue;
|
||||
std::shared_ptr<TunnelBase> tunnel;
|
||||
uint8_t typeID = msg->GetTypeID ();
|
||||
switch (typeID)
|
||||
@@ -518,29 +548,38 @@ namespace tunnel
|
||||
|
||||
break;
|
||||
}
|
||||
case eI2NPVariableTunnelBuild:
|
||||
case eI2NPVariableTunnelBuildReply:
|
||||
case eI2NPShortTunnelBuild:
|
||||
HandleShortTunnelBuildMsg (msg);
|
||||
break;
|
||||
case eI2NPVariableTunnelBuild:
|
||||
HandleVariableTunnelBuildMsg (msg);
|
||||
break;
|
||||
case eI2NPShortTunnelBuildReply:
|
||||
HandleTunnelBuildReplyMsg (msg, true);
|
||||
break;
|
||||
case eI2NPVariableTunnelBuildReply:
|
||||
HandleTunnelBuildReplyMsg (msg, false);
|
||||
break;
|
||||
case eI2NPTunnelBuild:
|
||||
case eI2NPTunnelBuildReply:
|
||||
HandleTunnelBuildI2NPMessage (msg);
|
||||
break;
|
||||
LogPrint (eLogWarning, "Tunnel: TunnelBuild is too old for ECIES router");
|
||||
break;
|
||||
default:
|
||||
LogPrint (eLogWarning, "Tunnel: Unexpected message type ", (int) typeID);
|
||||
}
|
||||
|
||||
msg = (numMsgs <= MAX_TUNNEL_MSGS_BATCH_SIZE) ? m_Queue.Get () : nullptr;
|
||||
if (msg)
|
||||
{
|
||||
prevTunnelID = tunnelID;
|
||||
prevTunnel = tunnel;
|
||||
numMsgs++;
|
||||
}
|
||||
else if (tunnel)
|
||||
tunnel->FlushTunnelDataMsgs ();
|
||||
prevTunnelID = tunnelID;
|
||||
prevTunnel = tunnel;
|
||||
numMsgs++;
|
||||
|
||||
if (msgs.empty ())
|
||||
{
|
||||
if (numMsgs < MAX_TUNNEL_MSGS_BATCH_SIZE && !m_Queue.IsEmpty ())
|
||||
m_Queue.GetWholeQueue (msgs); // try more
|
||||
else if (tunnel)
|
||||
tunnel->FlushTunnelDataMsgs (); // otherwise flush last
|
||||
}
|
||||
}
|
||||
while (msg);
|
||||
}
|
||||
|
||||
if (i2p::transport::transports.IsOnline())
|
||||
@@ -597,12 +636,83 @@ namespace tunnel
|
||||
tunnel->SendTunnelDataMsg (msg);
|
||||
}
|
||||
|
||||
void Tunnels::HandleShortTunnelBuildMsg (std::shared_ptr<I2NPMessage> msg)
|
||||
{
|
||||
if (!msg) return;
|
||||
auto tunnel = GetPendingInboundTunnel (msg->GetMsgID()); // replyMsgID
|
||||
if (tunnel)
|
||||
{
|
||||
// endpoint of inbound tunnel
|
||||
LogPrint (eLogDebug, "Tunnel: ShortTunnelBuild reply for tunnel ", tunnel->GetTunnelID ());
|
||||
if (tunnel->HandleTunnelBuildResponse (msg->GetPayload(), msg->GetPayloadLength()))
|
||||
{
|
||||
LogPrint (eLogInfo, "Tunnel: Inbound tunnel ", tunnel->GetTunnelID (), " has been created");
|
||||
tunnel->SetState (eTunnelStateEstablished);
|
||||
AddInboundTunnel (tunnel);
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint (eLogInfo, "Tunnel: Inbound tunnel ", tunnel->GetTunnelID (), " has been declined");
|
||||
tunnel->SetState (eTunnelStateBuildFailed);
|
||||
}
|
||||
return;
|
||||
}
|
||||
else
|
||||
m_TransitTunnels.PostTransitTunnelBuildMsg (std::move (msg));
|
||||
}
|
||||
|
||||
void Tunnels::HandleVariableTunnelBuildMsg (std::shared_ptr<I2NPMessage> msg)
|
||||
{
|
||||
auto tunnel = GetPendingInboundTunnel (msg->GetMsgID()); // replyMsgID
|
||||
if (tunnel)
|
||||
{
|
||||
// endpoint of inbound tunnel
|
||||
LogPrint (eLogDebug, "Tunnel: VariableTunnelBuild reply for tunnel ", tunnel->GetTunnelID ());
|
||||
if (tunnel->HandleTunnelBuildResponse (msg->GetPayload(), msg->GetPayloadLength()))
|
||||
{
|
||||
LogPrint (eLogInfo, "Tunnel: Inbound tunnel ", tunnel->GetTunnelID (), " has been created");
|
||||
tunnel->SetState (eTunnelStateEstablished);
|
||||
AddInboundTunnel (tunnel);
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint (eLogInfo, "Tunnel: Inbound tunnel ", tunnel->GetTunnelID (), " has been declined");
|
||||
tunnel->SetState (eTunnelStateBuildFailed);
|
||||
}
|
||||
}
|
||||
else
|
||||
m_TransitTunnels.PostTransitTunnelBuildMsg (std::move (msg));
|
||||
}
|
||||
|
||||
void Tunnels::HandleTunnelBuildReplyMsg (std::shared_ptr<I2NPMessage> msg, bool isShort)
|
||||
{
|
||||
auto tunnel = GetPendingOutboundTunnel (msg->GetMsgID()); // replyMsgID
|
||||
if (tunnel)
|
||||
{
|
||||
// reply for outbound tunnel
|
||||
LogPrint (eLogDebug, "Tunnel: TunnelBuildReply for tunnel ", tunnel->GetTunnelID ());
|
||||
if (tunnel->HandleTunnelBuildResponse (msg->GetPayload(), msg->GetPayloadLength()))
|
||||
{
|
||||
LogPrint (eLogInfo, "Tunnel: Outbound tunnel ", tunnel->GetTunnelID (), " has been created");
|
||||
tunnel->SetState (eTunnelStateEstablished);
|
||||
AddOutboundTunnel (tunnel);
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint (eLogInfo, "Tunnel: Outbound tunnel ", tunnel->GetTunnelID (), " has been declined");
|
||||
tunnel->SetState (eTunnelStateBuildFailed);
|
||||
}
|
||||
}
|
||||
else
|
||||
LogPrint (eLogWarning, "Tunnel: Pending tunnel for message ", msg->GetMsgID(), " not found");
|
||||
|
||||
}
|
||||
|
||||
void Tunnels::ManageTunnels (uint64_t ts)
|
||||
{
|
||||
ManagePendingTunnels (ts);
|
||||
ManageInboundTunnels (ts);
|
||||
ManageOutboundTunnels (ts);
|
||||
ManageTransitTunnels (ts);
|
||||
}
|
||||
|
||||
void Tunnels::ManagePendingTunnels (uint64_t ts)
|
||||
@@ -729,7 +839,7 @@ namespace tunnel
|
||||
auto pool = tunnel->GetTunnelPool ();
|
||||
if (pool)
|
||||
pool->TunnelExpired (tunnel);
|
||||
m_Tunnels.erase (tunnel->GetTunnelID ());
|
||||
RemoveTunnel (tunnel->GetTunnelID ());
|
||||
it = m_InboundTunnels.erase (it);
|
||||
}
|
||||
else
|
||||
@@ -791,26 +901,6 @@ namespace tunnel
|
||||
}
|
||||
}
|
||||
|
||||
void Tunnels::ManageTransitTunnels (uint64_t ts)
|
||||
{
|
||||
for (auto it = m_TransitTunnels.begin (); it != m_TransitTunnels.end ();)
|
||||
{
|
||||
auto tunnel = *it;
|
||||
if (ts > tunnel->GetCreationTime () + TUNNEL_EXPIRATION_TIMEOUT ||
|
||||
ts + TUNNEL_EXPIRATION_TIMEOUT < tunnel->GetCreationTime ())
|
||||
{
|
||||
LogPrint (eLogDebug, "Tunnel: Transit tunnel with id ", tunnel->GetTunnelID (), " expired");
|
||||
m_Tunnels.erase (tunnel->GetTunnelID ());
|
||||
it = m_TransitTunnels.erase (it);
|
||||
}
|
||||
else
|
||||
{
|
||||
tunnel->Cleanup ();
|
||||
it++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Tunnels::ManageTunnelPools (uint64_t ts)
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_PoolsMutex);
|
||||
@@ -826,7 +916,7 @@ namespace tunnel
|
||||
if (msg) m_Queue.Put (msg);
|
||||
}
|
||||
|
||||
void Tunnels::PostTunnelData (const std::vector<std::shared_ptr<I2NPMessage> >& msgs)
|
||||
void Tunnels::PostTunnelData (std::list<std::shared_ptr<I2NPMessage> >& msgs)
|
||||
{
|
||||
m_Queue.Put (msgs);
|
||||
}
|
||||
@@ -884,7 +974,7 @@ namespace tunnel
|
||||
|
||||
void Tunnels::AddInboundTunnel (std::shared_ptr<InboundTunnel> newTunnel)
|
||||
{
|
||||
if (m_Tunnels.emplace (newTunnel->GetTunnelID (), newTunnel).second)
|
||||
if (AddTunnel (newTunnel))
|
||||
{
|
||||
m_InboundTunnels.push_back (newTunnel);
|
||||
auto pool = newTunnel->GetTunnelPool ();
|
||||
@@ -914,7 +1004,7 @@ namespace tunnel
|
||||
inboundTunnel->SetTunnelPool (pool);
|
||||
inboundTunnel->SetState (eTunnelStateEstablished);
|
||||
m_InboundTunnels.push_back (inboundTunnel);
|
||||
m_Tunnels[inboundTunnel->GetTunnelID ()] = inboundTunnel;
|
||||
AddTunnel (inboundTunnel);
|
||||
return inboundTunnel;
|
||||
}
|
||||
|
||||
@@ -948,21 +1038,12 @@ namespace tunnel
|
||||
|
||||
int Tunnels::GetTransitTunnelsExpirationTimeout ()
|
||||
{
|
||||
int timeout = 0;
|
||||
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
|
||||
// TODO: possible race condition with I2PControl
|
||||
for (const auto& it : m_TransitTunnels)
|
||||
{
|
||||
int t = it->GetCreationTime () + TUNNEL_EXPIRATION_TIMEOUT - ts;
|
||||
if (t > timeout) timeout = t;
|
||||
}
|
||||
return timeout;
|
||||
return m_TransitTunnels.GetTransitTunnelsExpirationTimeout ();
|
||||
}
|
||||
|
||||
size_t Tunnels::CountTransitTunnels() const
|
||||
{
|
||||
// TODO: locking
|
||||
return m_TransitTunnels.size();
|
||||
return m_TransitTunnels.GetNumTransitTunnels ();
|
||||
}
|
||||
|
||||
size_t Tunnels::CountInboundTunnels() const
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
#include <memory>
|
||||
#include <random>
|
||||
#include "util.h"
|
||||
#include "Queue.h"
|
||||
#include "Crypto.h"
|
||||
@@ -138,7 +139,7 @@ namespace tunnel
|
||||
public:
|
||||
|
||||
OutboundTunnel (std::shared_ptr<const TunnelConfig> config):
|
||||
Tunnel (config), m_Gateway (this), m_EndpointIdentHash (config->GetLastIdentHash ()) {};
|
||||
Tunnel (config), m_Gateway (*this), m_EndpointIdentHash (config->GetLastIdentHash ()) {};
|
||||
|
||||
void SendTunnelDataMsgTo (const uint8_t * gwHash, uint32_t gwTunnel, std::shared_ptr<i2p::I2NPMessage> msg);
|
||||
virtual void SendTunnelDataMsgs (const std::vector<TunnelMessageBlock>& msgs); // multiple messages
|
||||
@@ -222,14 +223,15 @@ namespace tunnel
|
||||
std::shared_ptr<OutboundTunnel> GetNextOutboundTunnel ();
|
||||
std::shared_ptr<TunnelPool> GetExploratoryPool () const { return m_ExploratoryPool; };
|
||||
std::shared_ptr<TunnelBase> GetTunnel (uint32_t tunnelID);
|
||||
bool AddTunnel (std::shared_ptr<TunnelBase> tunnel);
|
||||
void RemoveTunnel (uint32_t tunnelID);
|
||||
int GetTransitTunnelsExpirationTimeout ();
|
||||
bool AddTransitTunnel (std::shared_ptr<TransitTunnel> tunnel);
|
||||
void AddOutboundTunnel (std::shared_ptr<OutboundTunnel> newTunnel);
|
||||
void AddInboundTunnel (std::shared_ptr<InboundTunnel> newTunnel);
|
||||
std::shared_ptr<InboundTunnel> CreateInboundTunnel (std::shared_ptr<TunnelConfig> config, std::shared_ptr<TunnelPool> pool, std::shared_ptr<OutboundTunnel> outboundTunnel);
|
||||
std::shared_ptr<OutboundTunnel> CreateOutboundTunnel (std::shared_ptr<TunnelConfig> config, std::shared_ptr<TunnelPool> pool);
|
||||
void PostTunnelData (std::shared_ptr<I2NPMessage> msg);
|
||||
void PostTunnelData (const std::vector<std::shared_ptr<I2NPMessage> >& msgs);
|
||||
void PostTunnelData (std::list<std::shared_ptr<I2NPMessage> >& msgs); // and cleanup msgs
|
||||
void AddPendingTunnel (uint32_t replyMsgID, std::shared_ptr<InboundTunnel> tunnel);
|
||||
void AddPendingTunnel (uint32_t replyMsgID, std::shared_ptr<OutboundTunnel> tunnel);
|
||||
std::shared_ptr<TunnelPool> CreateTunnelPool (int numInboundHops,
|
||||
@@ -242,8 +244,10 @@ namespace tunnel
|
||||
|
||||
void SetMaxNumTransitTunnels (uint32_t maxNumTransitTunnels);
|
||||
uint32_t GetMaxNumTransitTunnels () const { return m_MaxNumTransitTunnels; };
|
||||
int GetCongestionLevel() const { return m_MaxNumTransitTunnels ? CONGESTION_LEVEL_FULL * m_TransitTunnels.size() / m_MaxNumTransitTunnels : CONGESTION_LEVEL_FULL; }
|
||||
int GetCongestionLevel() const { return m_MaxNumTransitTunnels ? CONGESTION_LEVEL_FULL * m_TransitTunnels.GetNumTransitTunnels () / m_MaxNumTransitTunnels : CONGESTION_LEVEL_FULL; }
|
||||
|
||||
std::mt19937& GetRng () { return m_Rng; };
|
||||
|
||||
private:
|
||||
|
||||
template<class TTunnel>
|
||||
@@ -254,12 +258,14 @@ namespace tunnel
|
||||
std::shared_ptr<TTunnel> GetPendingTunnel (uint32_t replyMsgID, const std::map<uint32_t, std::shared_ptr<TTunnel> >& pendingTunnels);
|
||||
|
||||
void HandleTunnelGatewayMsg (std::shared_ptr<TunnelBase> tunnel, std::shared_ptr<I2NPMessage> msg);
|
||||
|
||||
void HandleShortTunnelBuildMsg (std::shared_ptr<I2NPMessage> msg);
|
||||
void HandleVariableTunnelBuildMsg (std::shared_ptr<I2NPMessage> msg);
|
||||
void HandleTunnelBuildReplyMsg (std::shared_ptr<I2NPMessage> msg, bool isShort);
|
||||
|
||||
void Run ();
|
||||
void ManageTunnels (uint64_t ts);
|
||||
void ManageOutboundTunnels (uint64_t ts);
|
||||
void ManageInboundTunnels (uint64_t ts);
|
||||
void ManageTransitTunnels (uint64_t ts);
|
||||
void ManagePendingTunnels (uint64_t ts);
|
||||
template<class PendingTunnels>
|
||||
void ManagePendingTunnels (PendingTunnels& pendingTunnels, uint64_t ts);
|
||||
@@ -294,9 +300,9 @@ namespace tunnel
|
||||
std::map<uint32_t, std::shared_ptr<OutboundTunnel> > m_PendingOutboundTunnels; // by replyMsgID
|
||||
std::list<std::shared_ptr<InboundTunnel> > m_InboundTunnels;
|
||||
std::list<std::shared_ptr<OutboundTunnel> > m_OutboundTunnels;
|
||||
std::list<std::shared_ptr<TransitTunnel> > m_TransitTunnels;
|
||||
mutable std::mutex m_TunnelsMutex;
|
||||
std::unordered_map<uint32_t, std::shared_ptr<TunnelBase> > m_Tunnels; // tunnelID->tunnel known by this id
|
||||
std::mutex m_PoolsMutex;
|
||||
mutable std::mutex m_PoolsMutex;
|
||||
std::list<std::shared_ptr<TunnelPool>> m_Pools;
|
||||
std::shared_ptr<TunnelPool> m_ExploratoryPool;
|
||||
i2p::util::Queue<std::shared_ptr<I2NPMessage> > m_Queue;
|
||||
@@ -307,19 +313,22 @@ namespace tunnel
|
||||
int m_TotalNumSuccesiveTunnelCreations, m_TotalNumFailedTunnelCreations;
|
||||
double m_TunnelCreationSuccessRate;
|
||||
int m_TunnelCreationAttemptsNum;
|
||||
|
||||
std::mt19937 m_Rng;
|
||||
TransitTunnels m_TransitTunnels;
|
||||
|
||||
public:
|
||||
|
||||
// for HTTP only
|
||||
const decltype(m_OutboundTunnels)& GetOutboundTunnels () const { return m_OutboundTunnels; };
|
||||
const decltype(m_InboundTunnels)& GetInboundTunnels () const { return m_InboundTunnels; };
|
||||
const decltype(m_TransitTunnels)& GetTransitTunnels () const { return m_TransitTunnels; };
|
||||
const auto& GetTransitTunnels () const { return m_TransitTunnels.GetTransitTunnels (); };
|
||||
|
||||
size_t CountTransitTunnels() const;
|
||||
size_t CountInboundTunnels() const;
|
||||
size_t CountOutboundTunnels() const;
|
||||
|
||||
int GetQueueSize () { return m_Queue.GetSize (); };
|
||||
size_t GetQueueSize () const { return m_Queue.GetSize (); };
|
||||
size_t GetTBMQueueSize () const { return m_TransitTunnels.GetTunnelBuildMsgQueueSize (); };
|
||||
int GetTunnelCreationSuccessRate () const { return std::round(m_TunnelCreationSuccessRate * 100); } // in percents
|
||||
double GetPreciseTunnelCreationSuccessRate () const { return m_TunnelCreationSuccessRate * 100; } // in percents
|
||||
int GetTotalTunnelCreationSuccessRate () const // in percents
|
||||
|
||||
@@ -79,8 +79,7 @@ namespace tunnel
|
||||
uint8_t * record = records + index*TUNNEL_BUILD_RECORD_SIZE;
|
||||
i2p::crypto::CBCDecryption decryption;
|
||||
decryption.SetKey (replyKey);
|
||||
decryption.SetIV (replyIV);
|
||||
decryption.Decrypt(record, TUNNEL_BUILD_RECORD_SIZE, record);
|
||||
decryption.Decrypt(record, TUNNEL_BUILD_RECORD_SIZE, replyIV, record);
|
||||
}
|
||||
|
||||
void ECIESTunnelHopConfig::EncryptECIES (const uint8_t * plainText, size_t len, uint8_t * encrypted)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2021, The PurpleI2P Project
|
||||
* Copyright (c) 2013-2024, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
@@ -181,6 +181,8 @@ namespace tunnel
|
||||
return peers;
|
||||
}
|
||||
|
||||
size_t GetRecordSize () const { return m_IsShort ? SHORT_TUNNEL_BUILD_RECORD_SIZE : TUNNEL_BUILD_RECORD_SIZE; };
|
||||
|
||||
protected:
|
||||
|
||||
// this constructor can't be called from outside
|
||||
|
||||
@@ -220,21 +220,55 @@ namespace tunnel
|
||||
|
||||
void TunnelGateway::SendBuffer ()
|
||||
{
|
||||
// create list or tunnel messages
|
||||
m_Buffer.CompleteCurrentTunnelDataMessage ();
|
||||
std::vector<std::shared_ptr<I2NPMessage> > newTunnelMsgs;
|
||||
std::list<std::shared_ptr<I2NPMessage> > newTunnelMsgs;
|
||||
const auto& tunnelDataMsgs = m_Buffer.GetTunnelDataMsgs ();
|
||||
for (auto& tunnelMsg : tunnelDataMsgs)
|
||||
{
|
||||
auto newMsg = CreateEmptyTunnelDataMsg (false);
|
||||
m_Tunnel->EncryptTunnelMsg (tunnelMsg, newMsg);
|
||||
htobe32buf (newMsg->GetPayload (), m_Tunnel->GetNextTunnelID ());
|
||||
m_Tunnel.EncryptTunnelMsg (tunnelMsg, newMsg);
|
||||
htobe32buf (newMsg->GetPayload (), m_Tunnel.GetNextTunnelID ());
|
||||
newMsg->FillI2NPMessageHeader (eI2NPTunnelData);
|
||||
if (tunnelMsg->onDrop) newMsg->onDrop = tunnelMsg->onDrop;
|
||||
newTunnelMsgs.push_back (newMsg);
|
||||
m_NumSentBytes += TUNNEL_DATA_MSG_SIZE;
|
||||
}
|
||||
m_Buffer.ClearTunnelDataMsgs ();
|
||||
i2p::transport::transports.SendMessages (m_Tunnel->GetNextIdentHash (), newTunnelMsgs);
|
||||
|
||||
// send
|
||||
auto currentTransport = m_CurrentTransport.lock ();
|
||||
if (!currentTransport)
|
||||
{
|
||||
// try to obtain transport from pending request or send thought transport is not complete
|
||||
if (m_PendingTransport.valid ()) // pending request?
|
||||
{
|
||||
if (m_PendingTransport.wait_for(std::chrono::seconds(0)) == std::future_status::ready)
|
||||
{
|
||||
// pending request complete
|
||||
currentTransport = m_PendingTransport.get (); // take transports used in pending request
|
||||
if (currentTransport)
|
||||
{
|
||||
if (currentTransport->IsEstablished ())
|
||||
m_CurrentTransport = currentTransport;
|
||||
else
|
||||
currentTransport = nullptr;
|
||||
}
|
||||
}
|
||||
else // still pending
|
||||
{
|
||||
// send through transports, but don't update pending transport
|
||||
i2p::transport::transports.SendMessages (m_Tunnel.GetNextIdentHash (), std::move (newTunnelMsgs));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (currentTransport) // session is good
|
||||
// send to session directly
|
||||
currentTransport->SendI2NPMessages (newTunnelMsgs);
|
||||
else // no session yet
|
||||
// send through transports
|
||||
m_PendingTransport = i2p::transport::transports.SendMessages (m_Tunnel.GetNextIdentHash (), std::move (newTunnelMsgs));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2021, The PurpleI2P Project
|
||||
* Copyright (c) 2013-2024, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
@@ -12,7 +12,9 @@
|
||||
#include <inttypes.h>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <future>
|
||||
#include "I2NPProtocol.h"
|
||||
#include "TransportSession.h"
|
||||
#include "TunnelBase.h"
|
||||
|
||||
namespace i2p
|
||||
@@ -45,7 +47,7 @@ namespace tunnel
|
||||
{
|
||||
public:
|
||||
|
||||
TunnelGateway (TunnelBase * tunnel):
|
||||
TunnelGateway (TunnelBase& tunnel):
|
||||
m_Tunnel (tunnel), m_NumSentBytes (0) {};
|
||||
void SendTunnelDataMsg (const TunnelMessageBlock& block);
|
||||
void PutTunnelDataMsg (const TunnelMessageBlock& block);
|
||||
@@ -54,9 +56,11 @@ namespace tunnel
|
||||
|
||||
private:
|
||||
|
||||
TunnelBase * m_Tunnel;
|
||||
TunnelBase& m_Tunnel;
|
||||
TunnelGatewayBuffer m_Buffer;
|
||||
size_t m_NumSentBytes;
|
||||
std::weak_ptr<i2p::transport::TransportSession> m_CurrentTransport;
|
||||
std::future<std::shared_ptr<i2p::transport::TransportSession> > m_PendingTransport;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -141,7 +141,7 @@ namespace tunnel
|
||||
m_InboundTunnels.insert (createdTunnel);
|
||||
}
|
||||
if (m_LocalDestination)
|
||||
m_LocalDestination->SetLeaseSetUpdated ();
|
||||
m_LocalDestination->SetLeaseSetUpdated (true);
|
||||
}
|
||||
|
||||
void TunnelPool::TunnelExpired (std::shared_ptr<InboundTunnel> expiredTunnel)
|
||||
@@ -330,7 +330,7 @@ namespace tunnel
|
||||
}
|
||||
|
||||
if (num < m_NumInboundTunnels && m_NumInboundHops <= 0 && m_LocalDestination) // zero hops IB
|
||||
m_LocalDestination->SetLeaseSetUpdated (); // update LeaseSet immediately
|
||||
m_LocalDestination->SetLeaseSetUpdated (true); // update LeaseSet immediately
|
||||
}
|
||||
|
||||
void TunnelPool::TestTunnels ()
|
||||
@@ -377,10 +377,10 @@ namespace tunnel
|
||||
it.second.second->SetState (eTunnelStateTestFailed);
|
||||
}
|
||||
if (failed && m_LocalDestination)
|
||||
m_LocalDestination->SetLeaseSetUpdated ();
|
||||
m_LocalDestination->SetLeaseSetUpdated (true);
|
||||
}
|
||||
if (m_LocalDestination)
|
||||
m_LocalDestination->SetLeaseSetUpdated ();
|
||||
m_LocalDestination->SetLeaseSetUpdated (true);
|
||||
}
|
||||
else if (it.second.second->GetState () != eTunnelStateExpiring)
|
||||
it.second.second->SetState (eTunnelStateTestFailed);
|
||||
|
||||
@@ -37,9 +37,7 @@ namespace api
|
||||
i2p::fs::Init();
|
||||
|
||||
bool precomputation; i2p::config::GetOption("precomputation.elgamal", precomputation);
|
||||
bool aesni; i2p::config::GetOption("cpuext.aesni", aesni);
|
||||
bool forceCpuExt; i2p::config::GetOption("cpuext.force", forceCpuExt);
|
||||
i2p::crypto::InitCrypto (precomputation, aesni, forceCpuExt);
|
||||
i2p::crypto::InitCrypto (precomputation);
|
||||
|
||||
int netID; i2p::config::GetOption("netid", netID);
|
||||
i2p::context.SetNetID (netID);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2023, The PurpleI2P Project
|
||||
* Copyright (c) 2013-2024, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
@@ -123,8 +123,8 @@ const char *inet_ntop_xp(int af, const void *src, char *dst, socklen_t size)
|
||||
#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 () }
|
||||
#define address_pair_v4(a,b) { boost::asio::ip::make_address (a).to_v4 ().to_uint (), boost::asio::ip::make_address(b).to_v4 ().to_uint () }
|
||||
#define address_pair_v6(a,b) { boost::asio::ip::make_address (a).to_v6 ().to_bytes (), boost::asio::ip::make_address(b).to_v6 ().to_bytes () }
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
@@ -171,6 +171,14 @@ namespace util
|
||||
}
|
||||
}
|
||||
|
||||
void RunnableService::SetName (std::string_view name)
|
||||
{
|
||||
if (name.length() < 16)
|
||||
m_Name = name;
|
||||
else
|
||||
m_Name = name.substr(0,15);
|
||||
}
|
||||
|
||||
void SetThreadName (const char *name) {
|
||||
#if defined(__APPLE__)
|
||||
# if (!defined(MAC_OS_X_VERSION_10_6) || \
|
||||
@@ -470,7 +478,7 @@ namespace net
|
||||
inet_ntop(af, &((sockaddr_in6 *)cur->ifa_addr)->sin6_addr, addr, INET6_ADDRSTRLEN);
|
||||
freeifaddrs(addrs);
|
||||
std::string cur_ifaddr(addr);
|
||||
return boost::asio::ip::address::from_string(cur_ifaddr);
|
||||
return boost::asio::ip::make_address(cur_ifaddr);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -490,7 +498,7 @@ namespace net
|
||||
fallback = "127.0.0.1";
|
||||
LogPrint(eLogWarning, "NetIface: Cannot find IPv4 address for interface ", ifname);
|
||||
}
|
||||
return boost::asio::ip::address::from_string(fallback);
|
||||
return boost::asio::ip::make_address(fallback);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -656,7 +664,7 @@ namespace net
|
||||
address_pair_v4("224.0.0.0", "255.255.255.255")
|
||||
};
|
||||
|
||||
uint32_t ipv4_address = host.to_v4 ().to_ulong ();
|
||||
uint32_t ipv4_address = host.to_v4 ().to_uint ();
|
||||
for (const auto& it : reservedIPv4Ranges) {
|
||||
if (ipv4_address >= it.first && ipv4_address <= it.second)
|
||||
return true;
|
||||
|
||||
@@ -137,8 +137,8 @@ namespace util
|
||||
std::lock_guard<std::mutex> l(m_Mutex);
|
||||
for (size_t i = 0; i < num; i++)
|
||||
this->Release (arr[i]);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
template<template<typename, typename...>class C, typename... R>
|
||||
void ReleaseMt(const C<T *, R...>& c)
|
||||
{
|
||||
@@ -146,7 +146,7 @@ namespace util
|
||||
for (auto& it: c)
|
||||
this->Release (it);
|
||||
}
|
||||
|
||||
|
||||
template<typename... TArgs>
|
||||
std::shared_ptr<T> AcquireSharedMt (TArgs&&... args)
|
||||
{
|
||||
@@ -177,12 +177,14 @@ namespace util
|
||||
RunnableService (const std::string& name): m_Name (name), m_IsRunning (false) {}
|
||||
virtual ~RunnableService () {}
|
||||
|
||||
boost::asio::io_service& GetIOService () { return m_Service; }
|
||||
auto& GetIOService () { return m_Service; }
|
||||
bool IsRunning () const { return m_IsRunning; };
|
||||
|
||||
void StartIOService ();
|
||||
void StopIOService ();
|
||||
|
||||
void SetName (std::string_view name);
|
||||
|
||||
private:
|
||||
|
||||
void Run ();
|
||||
@@ -192,7 +194,7 @@ namespace util
|
||||
std::string m_Name;
|
||||
volatile bool m_IsRunning;
|
||||
std::unique_ptr<std::thread> m_Thread;
|
||||
boost::asio::io_service m_Service;
|
||||
boost::asio::io_context m_Service;
|
||||
};
|
||||
|
||||
class RunnableServiceWithWork: public RunnableService
|
||||
@@ -200,11 +202,11 @@ namespace util
|
||||
protected:
|
||||
|
||||
RunnableServiceWithWork (const std::string& name):
|
||||
RunnableService (name), m_Work (GetIOService ()) {}
|
||||
RunnableService (name), m_Work (GetIOService ().get_executor ()) {}
|
||||
|
||||
private:
|
||||
|
||||
boost::asio::io_service::work m_Work;
|
||||
boost::asio::executor_work_guard<boost::asio::io_context::executor_type> m_Work;
|
||||
};
|
||||
|
||||
void SetThreadName (const char *name);
|
||||
|
||||
@@ -305,7 +305,7 @@ namespace client
|
||||
identHash = hash;
|
||||
}
|
||||
|
||||
AddressBook::AddressBook (): m_Storage(nullptr), m_IsLoaded (false), m_IsDownloading (false),
|
||||
AddressBook::AddressBook (): m_Storage(nullptr), m_IsLoaded (false),
|
||||
m_NumRetries (0), m_DefaultSubscription (nullptr), m_SubscriptionsUpdateTimer (nullptr),
|
||||
m_IsEnabled (true)
|
||||
{
|
||||
@@ -344,20 +344,28 @@ namespace client
|
||||
delete m_SubscriptionsUpdateTimer;
|
||||
m_SubscriptionsUpdateTimer = nullptr;
|
||||
}
|
||||
if (m_IsDownloading)
|
||||
bool isDownloading = m_Downloading.valid ();
|
||||
if (isDownloading)
|
||||
{
|
||||
LogPrint (eLogInfo, "Addressbook: Subscriptions are downloading, abort");
|
||||
for (int i = 0; i < 30; i++)
|
||||
{
|
||||
if (!m_IsDownloading)
|
||||
if (m_Downloading.wait_for(std::chrono::seconds(0)) == std::future_status::ready)
|
||||
isDownloading = false;
|
||||
else
|
||||
{
|
||||
LogPrint (eLogInfo, "Addressbook: Subscriptions are downloading, abort");
|
||||
for (int i = 0; i < 30; i++)
|
||||
{
|
||||
LogPrint (eLogInfo, "Addressbook: Subscriptions download complete");
|
||||
break;
|
||||
if (m_Downloading.wait_for(std::chrono::seconds(1)) == std::future_status::ready) // wait for 1 seconds
|
||||
{
|
||||
isDownloading = false;
|
||||
LogPrint (eLogInfo, "Addressbook: Subscriptions download complete");
|
||||
break;
|
||||
}
|
||||
}
|
||||
std::this_thread::sleep_for (std::chrono::seconds (1)); // wait for 1 seconds
|
||||
}
|
||||
LogPrint (eLogError, "Addressbook: Subscription download timeout");
|
||||
m_IsDownloading = false;
|
||||
}
|
||||
if (!isDownloading)
|
||||
m_Downloading.get ();
|
||||
else
|
||||
LogPrint (eLogError, "Addressbook: Subscription download timeout");
|
||||
}
|
||||
if (m_Storage)
|
||||
{
|
||||
@@ -582,16 +590,15 @@ namespace client
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint (eLogInfo, "Addressbook: Loading subscriptions from config file");
|
||||
LogPrint (eLogInfo, "Addressbook: Loading subscriptions from config");
|
||||
// using config file items
|
||||
std::string subscriptionURLs; i2p::config::GetOption("addressbook.subscriptions", subscriptionURLs);
|
||||
std::vector<std::string> subsList;
|
||||
boost::split(subsList, subscriptionURLs, boost::is_any_of(","), boost::token_compress_on);
|
||||
|
||||
for (const auto& s: subsList)
|
||||
{
|
||||
m_Subscriptions.push_back (std::make_shared<AddressBookSubscription> (*this, s));
|
||||
}
|
||||
if (!s.empty ())
|
||||
m_Subscriptions.push_back (std::make_shared<AddressBookSubscription> (*this, s));
|
||||
LogPrint (eLogInfo, "Addressbook: ", m_Subscriptions.size (), " subscriptions urls loaded");
|
||||
}
|
||||
}
|
||||
@@ -645,7 +652,6 @@ namespace client
|
||||
|
||||
void AddressBook::DownloadComplete (bool success, const i2p::data::IdentHash& subscription, const std::string& etag, const std::string& lastModified)
|
||||
{
|
||||
m_IsDownloading = false;
|
||||
m_NumRetries++;
|
||||
int nextUpdateTimeout = m_NumRetries*CONTINIOUS_SUBSCRIPTION_RETRY_TIMEOUT;
|
||||
if (m_NumRetries > CONTINIOUS_SUBSCRIPTION_MAX_NUM_RETRIES || nextUpdateTimeout > CONTINIOUS_SUBSCRIPTION_UPDATE_TIMEOUT)
|
||||
@@ -700,7 +706,13 @@ namespace client
|
||||
LogPrint(eLogWarning, "Addressbook: Missing local destination, skip subscription update");
|
||||
return;
|
||||
}
|
||||
if (!m_IsDownloading && dest->IsReady ())
|
||||
bool isDownloading = m_Downloading.valid ();
|
||||
if (isDownloading && m_Downloading.wait_for(std::chrono::seconds(0)) == std::future_status::ready) // still active?
|
||||
{
|
||||
m_Downloading.get ();
|
||||
isDownloading = false;
|
||||
}
|
||||
if (!isDownloading && dest->IsReady ())
|
||||
{
|
||||
if (!m_IsLoaded)
|
||||
{
|
||||
@@ -709,17 +721,15 @@ namespace client
|
||||
std::string defaultSubURL; i2p::config::GetOption("addressbook.defaulturl", defaultSubURL);
|
||||
if (!m_DefaultSubscription)
|
||||
m_DefaultSubscription = std::make_shared<AddressBookSubscription>(*this, defaultSubURL);
|
||||
m_IsDownloading = true;
|
||||
std::thread load_hosts(std::bind (&AddressBookSubscription::CheckUpdates, m_DefaultSubscription));
|
||||
load_hosts.detach(); // TODO: use join
|
||||
m_Downloading = std::async (std::launch::async,
|
||||
std::bind (&AddressBookSubscription::CheckUpdates, m_DefaultSubscription));
|
||||
}
|
||||
else if (!m_Subscriptions.empty ())
|
||||
{
|
||||
// pick random subscription
|
||||
auto ind = rand () % m_Subscriptions.size();
|
||||
m_IsDownloading = true;
|
||||
std::thread load_hosts(std::bind (&AddressBookSubscription::CheckUpdates, m_Subscriptions[ind]));
|
||||
load_hosts.detach(); // TODO: use join
|
||||
m_Downloading = std::async (std::launch::async,
|
||||
std::bind (&AddressBookSubscription::CheckUpdates, m_Subscriptions[ind]));
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -823,7 +833,7 @@ namespace client
|
||||
}
|
||||
}
|
||||
|
||||
AddressBookSubscription::AddressBookSubscription (AddressBook& book, const std::string& link):
|
||||
AddressBookSubscription::AddressBookSubscription (AddressBook& book, std::string_view link):
|
||||
m_Book (book), m_Link (link)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -11,10 +11,12 @@
|
||||
|
||||
#include <string.h>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
#include <mutex>
|
||||
#include <future>
|
||||
#include <memory>
|
||||
#include <boost/asio.hpp>
|
||||
#include "Base.h"
|
||||
@@ -124,7 +126,8 @@ namespace client
|
||||
std::mutex m_LookupsMutex;
|
||||
std::map<uint32_t, std::string> m_Lookups; // nonce -> address
|
||||
AddressBookStorage * m_Storage;
|
||||
volatile bool m_IsLoaded, m_IsDownloading;
|
||||
volatile bool m_IsLoaded;
|
||||
std::future<void> m_Downloading;
|
||||
int m_NumRetries;
|
||||
std::vector<std::shared_ptr<AddressBookSubscription> > m_Subscriptions;
|
||||
std::shared_ptr<AddressBookSubscription> m_DefaultSubscription; // in case if we don't know any addresses yet
|
||||
@@ -136,7 +139,7 @@ namespace client
|
||||
{
|
||||
public:
|
||||
|
||||
AddressBookSubscription (AddressBook& book, const std::string& link);
|
||||
AddressBookSubscription (AddressBook& book, std::string_view link);
|
||||
void CheckUpdates ();
|
||||
|
||||
private:
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2023, The PurpleI2P Project
|
||||
* Copyright (c) 2013-2024, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
@@ -129,7 +129,7 @@ namespace client
|
||||
|
||||
BOBI2POutboundTunnel::BOBI2POutboundTunnel (const std::string& outhost, uint16_t port,
|
||||
std::shared_ptr<ClientDestination> localDestination, bool quiet): BOBI2PTunnel (localDestination),
|
||||
m_Endpoint (boost::asio::ip::address::from_string (outhost), port), m_IsQuiet (quiet)
|
||||
m_Endpoint (boost::asio::ip::make_address (outhost), port), m_IsQuiet (quiet)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -220,7 +220,7 @@ namespace client
|
||||
if (!inhost.empty ())
|
||||
{
|
||||
boost::system::error_code ec;
|
||||
auto addr = boost::asio::ip::address::from_string (inhost, ec);
|
||||
auto addr = boost::asio::ip::make_address (inhost, ec);
|
||||
if (!ec)
|
||||
ep.address (addr);
|
||||
else
|
||||
@@ -425,7 +425,7 @@ namespace client
|
||||
{
|
||||
// TODO: FIXME: temporary validation, until hostname support is added
|
||||
boost::system::error_code ec;
|
||||
boost::asio::ip::address::from_string(m_InHost, ec);
|
||||
boost::asio::ip::make_address(m_InHost, ec);
|
||||
if (ec)
|
||||
{
|
||||
SendReplyError("inhost must be a valid IPv4 address.");
|
||||
@@ -436,7 +436,7 @@ namespace client
|
||||
{
|
||||
// TODO: FIXME: temporary validation, until hostname support is added
|
||||
boost::system::error_code ec;
|
||||
boost::asio::ip::address::from_string(m_OutHost, ec);
|
||||
boost::asio::ip::make_address(m_OutHost, ec);
|
||||
if (ec)
|
||||
{
|
||||
SendReplyError("outhost must be a IPv4 address.");
|
||||
@@ -828,7 +828,7 @@ namespace client
|
||||
|
||||
BOBCommandChannel::BOBCommandChannel (const std::string& address, uint16_t port):
|
||||
RunnableService ("BOB"),
|
||||
m_Acceptor (GetIOService (), boost::asio::ip::tcp::endpoint(boost::asio::ip::address::from_string(address), port))
|
||||
m_Acceptor (GetIOService (), boost::asio::ip::tcp::endpoint(boost::asio::ip::make_address(address), port))
|
||||
{
|
||||
// command -> handler
|
||||
m_CommandHandlers[BOB_COMMAND_ZAP] = &BOBCommandSession::ZapCommandHandler;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2023, The PurpleI2P Project
|
||||
* Copyright (c) 2013-2024, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
@@ -254,7 +254,7 @@ namespace client
|
||||
void Start ();
|
||||
void Stop ();
|
||||
|
||||
boost::asio::io_service& GetService () { return GetIOService (); };
|
||||
auto& GetService () { return GetIOService (); };
|
||||
void AddDestination (const std::string& name, std::shared_ptr<BOBDestination> dest);
|
||||
void DeleteDestination (const std::string& name);
|
||||
std::shared_ptr<BOBDestination> FindDestination (const std::string& name);
|
||||
|
||||
@@ -345,7 +345,7 @@ namespace client
|
||||
}
|
||||
|
||||
std::shared_ptr<ClientDestination> ClientContext::CreateNewLocalDestination (
|
||||
boost::asio::io_service& service, bool isPublic,
|
||||
boost::asio::io_context& service, bool isPublic,
|
||||
i2p::data::SigningKeyType sigType, i2p::data::CryptoKeyType cryptoType,
|
||||
const std::map<std::string, std::string> * params)
|
||||
{
|
||||
@@ -399,7 +399,7 @@ namespace client
|
||||
return localDestination;
|
||||
}
|
||||
|
||||
std::shared_ptr<ClientDestination> ClientContext::CreateNewLocalDestination (boost::asio::io_service& service,
|
||||
std::shared_ptr<ClientDestination> ClientContext::CreateNewLocalDestination (boost::asio::io_context& service,
|
||||
const i2p::data::PrivateKeys& keys, bool isPublic, const std::map<std::string, std::string> * params)
|
||||
{
|
||||
auto it = m_Destinations.find (keys.GetPublic ()->GetIdentHash ());
|
||||
@@ -421,7 +421,9 @@ namespace client
|
||||
{ I2CP_PARAM_INBOUND_TUNNELS_QUANTITY, "3" },
|
||||
{ I2CP_PARAM_OUTBOUND_TUNNELS_QUANTITY, "3" },
|
||||
{ I2CP_PARAM_LEASESET_TYPE, "3" },
|
||||
{ I2CP_PARAM_LEASESET_ENCRYPTION_TYPE, "0,4" }
|
||||
{ I2CP_PARAM_LEASESET_ENCRYPTION_TYPE, "0,4" },
|
||||
{ I2CP_PARAM_OUTBOUND_NICKNAME, "SharedDest" },
|
||||
{ I2CP_PARAM_STREAMING_PROFILE, "2" }
|
||||
};
|
||||
m_SharedLocalDestination = CreateNewLocalDestination (false, i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519,
|
||||
i2p::data::CRYPTO_KEY_TYPE_ELGAMAL, ¶ms); // non-public, EDDSA
|
||||
@@ -473,8 +475,9 @@ namespace client
|
||||
options[I2CP_PARAM_STREAMING_INITIAL_ACK_DELAY] = GetI2CPOption(section, I2CP_PARAM_STREAMING_INITIAL_ACK_DELAY, DEFAULT_INITIAL_ACK_DELAY);
|
||||
options[I2CP_PARAM_STREAMING_MAX_OUTBOUND_SPEED] = GetI2CPOption(section, I2CP_PARAM_STREAMING_MAX_OUTBOUND_SPEED, DEFAULT_MAX_OUTBOUND_SPEED);
|
||||
options[I2CP_PARAM_STREAMING_MAX_INBOUND_SPEED] = GetI2CPOption(section, I2CP_PARAM_STREAMING_MAX_INBOUND_SPEED, DEFAULT_MAX_INBOUND_SPEED);
|
||||
options[I2CP_PARAM_STREAMING_MAX_CONCURRENT_STREAMS] = GetI2CPOption(section, I2CP_PARAM_STREAMING_MAX_CONCURRENT_STREAMS, DEFAULT_MAX_CONCURRENT_STREAMS);
|
||||
options[I2CP_PARAM_STREAMING_ANSWER_PINGS] = GetI2CPOption(section, I2CP_PARAM_STREAMING_ANSWER_PINGS, isServer ? DEFAULT_ANSWER_PINGS : false);
|
||||
options[I2CP_PARAM_STREAMING_PROFILE] = GetI2CPOption(section, I2CP_PARAM_STREAMING_PROFILE, DEFAULT_STREAMING_PROFILE);
|
||||
options[I2CP_PARAM_STREAMING_PROFILE] = GetI2CPOption(section, I2CP_PARAM_STREAMING_PROFILE, DEFAULT_STREAMING_PROFILE);
|
||||
options[I2CP_PARAM_LEASESET_TYPE] = GetI2CPOption(section, I2CP_PARAM_LEASESET_TYPE, DEFAULT_LEASESET_TYPE);
|
||||
std::string encType = GetI2CPStringOption(section, I2CP_PARAM_LEASESET_ENCRYPTION_TYPE, isServer ? "4" : "0,4");
|
||||
if (encType.length () > 0) options[I2CP_PARAM_LEASESET_ENCRYPTION_TYPE] = encType;
|
||||
@@ -595,6 +598,11 @@ namespace client
|
||||
std::map<std::string, std::string> options;
|
||||
ReadI2CPOptions (section, false, options);
|
||||
|
||||
// Set I2CP name if not set
|
||||
auto itopt = options.find (I2CP_PARAM_OUTBOUND_NICKNAME);
|
||||
if (itopt == options.end ())
|
||||
options[I2CP_PARAM_OUTBOUND_NICKNAME] = name;
|
||||
|
||||
std::shared_ptr<ClientDestination> localDestination = nullptr;
|
||||
if (keys.length () > 0)
|
||||
{
|
||||
@@ -623,7 +631,7 @@ namespace client
|
||||
if (type == I2P_TUNNELS_SECTION_TYPE_UDPCLIENT) {
|
||||
// udp client
|
||||
// TODO: hostnames
|
||||
boost::asio::ip::udp::endpoint end (boost::asio::ip::address::from_string(address), port);
|
||||
boost::asio::ip::udp::endpoint end (boost::asio::ip::make_address(address), port);
|
||||
if (!localDestination)
|
||||
localDestination = m_SharedLocalDestination;
|
||||
|
||||
@@ -667,7 +675,7 @@ namespace client
|
||||
std::string outproxy = section.second.get("outproxy", "");
|
||||
bool addresshelper = section.second.get("addresshelper", true);
|
||||
bool senduseragent = section.second.get("senduseragent", false);
|
||||
auto tun = std::make_shared<i2p::proxy::HTTPProxy>(name, address, port,
|
||||
auto tun = std::make_shared<i2p::proxy::HTTPProxy>(name, address, port,
|
||||
outproxy, addresshelper, senduseragent, localDestination);
|
||||
clientTunnel = tun;
|
||||
clientEndpoint = tun->GetLocalEndpoint ();
|
||||
@@ -749,6 +757,11 @@ namespace client
|
||||
std::map<std::string, std::string> options;
|
||||
ReadI2CPOptions (section, true, options);
|
||||
|
||||
// Set I2CP name if not set
|
||||
auto itopt = options.find (I2CP_PARAM_INBOUND_NICKNAME);
|
||||
if (itopt == options.end ())
|
||||
options[I2CP_PARAM_INBOUND_NICKNAME] = name;
|
||||
|
||||
std::shared_ptr<ClientDestination> localDestination = nullptr;
|
||||
auto it = destinations.find (keys);
|
||||
if (it != destinations.end ())
|
||||
@@ -774,7 +787,7 @@ namespace client
|
||||
{
|
||||
// udp server tunnel
|
||||
// TODO: hostnames
|
||||
boost::asio::ip::udp::endpoint endpoint(boost::asio::ip::address::from_string(host), port);
|
||||
boost::asio::ip::udp::endpoint endpoint(boost::asio::ip::make_address(host), port);
|
||||
if (address.empty ())
|
||||
{
|
||||
if (!endpoint.address ().is_unspecified () && endpoint.address ().is_v6 ())
|
||||
@@ -782,7 +795,7 @@ namespace client
|
||||
else
|
||||
address = "127.0.0.1";
|
||||
}
|
||||
auto localAddress = boost::asio::ip::address::from_string(address);
|
||||
auto localAddress = boost::asio::ip::make_address(address);
|
||||
auto serverTunnel = std::make_shared<I2PUDPServerTunnel>(name, localDestination, localAddress, endpoint, inPort, gzip);
|
||||
if(!isUniqueLocal)
|
||||
{
|
||||
@@ -896,6 +909,7 @@ namespace client
|
||||
{
|
||||
std::map<std::string, std::string> params;
|
||||
ReadI2CPOptionsFromConfig ("httpproxy.", params);
|
||||
params[I2CP_PARAM_OUTBOUND_NICKNAME] = "HTTPProxy";
|
||||
localDestination = CreateNewLocalDestination (keys, false, ¶ms);
|
||||
if (localDestination) localDestination->Acquire ();
|
||||
}
|
||||
@@ -904,7 +918,7 @@ namespace client
|
||||
}
|
||||
try
|
||||
{
|
||||
m_HttpProxy = new i2p::proxy::HTTPProxy("HTTP Proxy", httpProxyAddr, httpProxyPort,
|
||||
m_HttpProxy = new i2p::proxy::HTTPProxy("HTTP Proxy", httpProxyAddr, httpProxyPort,
|
||||
httpOutProxyURL, httpAddresshelper, httpSendUserAgent, localDestination);
|
||||
m_HttpProxy->Start();
|
||||
}
|
||||
@@ -944,6 +958,7 @@ namespace client
|
||||
{
|
||||
std::map<std::string, std::string> params;
|
||||
ReadI2CPOptionsFromConfig ("socksproxy.", params);
|
||||
params[I2CP_PARAM_OUTBOUND_NICKNAME] = "SOCKSProxy";
|
||||
localDestination = CreateNewLocalDestination (keys, false, ¶ms);
|
||||
if (localDestination) localDestination->Acquire ();
|
||||
}
|
||||
|
||||
@@ -79,13 +79,13 @@ namespace client
|
||||
i2p::data::SigningKeyType sigType = i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519,
|
||||
i2p::data::CryptoKeyType cryptoType = i2p::data::CRYPTO_KEY_TYPE_ELGAMAL,
|
||||
const std::map<std::string, std::string> * params = nullptr); // used by SAM only
|
||||
std::shared_ptr<ClientDestination> CreateNewLocalDestination (boost::asio::io_service& service,
|
||||
std::shared_ptr<ClientDestination> CreateNewLocalDestination (boost::asio::io_context& service,
|
||||
bool isPublic = false, i2p::data::SigningKeyType sigType = i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519,
|
||||
i2p::data::CryptoKeyType cryptoType = i2p::data::CRYPTO_KEY_TYPE_ELGAMAL,
|
||||
const std::map<std::string, std::string> * params = nullptr); // same as previous but on external io_service
|
||||
std::shared_ptr<ClientDestination> CreateNewLocalDestination (const i2p::data::PrivateKeys& keys, bool isPublic = true,
|
||||
const std::map<std::string, std::string> * params = nullptr);
|
||||
std::shared_ptr<ClientDestination> CreateNewLocalDestination (boost::asio::io_service& service,
|
||||
std::shared_ptr<ClientDestination> CreateNewLocalDestination (boost::asio::io_context& service,
|
||||
const i2p::data::PrivateKeys& keys, bool isPublic = true,
|
||||
const std::map<std::string, std::string> * params = nullptr); // same as previous but on external io_service
|
||||
std::shared_ptr<ClientDestination> CreateNewMatchedTunnelDestination(const i2p::data::PrivateKeys &keys,
|
||||
|
||||
@@ -98,7 +98,7 @@ namespace proxy {
|
||||
|
||||
typedef std::function<void(boost::asio::ip::tcp::endpoint)> ProxyResolvedHandler;
|
||||
|
||||
void HandleUpstreamProxyResolved(const boost::system::error_code & ecode, boost::asio::ip::tcp::resolver::iterator itr, ProxyResolvedHandler handler);
|
||||
void HandleUpstreamProxyResolved(const boost::system::error_code & ecode, boost::asio::ip::tcp::resolver::results_type endpoints, ProxyResolvedHandler handler);
|
||||
|
||||
void SocksProxySuccess();
|
||||
void HandoverToUpstreamProxy();
|
||||
@@ -584,20 +584,22 @@ namespace proxy {
|
||||
}
|
||||
else
|
||||
{
|
||||
boost::asio::ip::tcp::resolver::query q(m_ProxyURL.host, std::to_string(m_ProxyURL.port));
|
||||
m_proxy_resolver.async_resolve(q, std::bind(&HTTPReqHandler::HandleUpstreamProxyResolved, this, std::placeholders::_1, std::placeholders::_2, [&](boost::asio::ip::tcp::endpoint ep) {
|
||||
m_proxysock->async_connect(ep, std::bind(&HTTPReqHandler::HandleUpstreamHTTPProxyConnect, this, std::placeholders::_1));
|
||||
}));
|
||||
m_proxy_resolver.async_resolve(m_ProxyURL.host, std::to_string(m_ProxyURL.port), std::bind(&HTTPReqHandler::HandleUpstreamProxyResolved, this,
|
||||
std::placeholders::_1, std::placeholders::_2, [&](boost::asio::ip::tcp::endpoint ep)
|
||||
{
|
||||
m_proxysock->async_connect(ep, std::bind(&HTTPReqHandler::HandleUpstreamHTTPProxyConnect, this, std::placeholders::_1));
|
||||
}));
|
||||
}
|
||||
}
|
||||
else if (m_ProxyURL.schema == "socks")
|
||||
{
|
||||
/* handle upstream socks proxy */
|
||||
if (!m_ProxyURL.port) m_ProxyURL.port = 9050; // default to tor default if not specified
|
||||
boost::asio::ip::tcp::resolver::query q(m_ProxyURL.host, std::to_string(m_ProxyURL.port));
|
||||
m_proxy_resolver.async_resolve(q, std::bind(&HTTPReqHandler::HandleUpstreamProxyResolved, this, std::placeholders::_1, std::placeholders::_2, [&](boost::asio::ip::tcp::endpoint ep) {
|
||||
m_proxysock->async_connect(ep, std::bind(&HTTPReqHandler::HandleUpstreamSocksProxyConnect, this, std::placeholders::_1));
|
||||
}));
|
||||
m_proxy_resolver.async_resolve(m_ProxyURL.host, std::to_string(m_ProxyURL.port), std::bind(&HTTPReqHandler::HandleUpstreamProxyResolved, this,
|
||||
std::placeholders::_1, std::placeholders::_2, [&](boost::asio::ip::tcp::endpoint ep)
|
||||
{
|
||||
m_proxysock->async_connect(ep, std::bind(&HTTPReqHandler::HandleUpstreamSocksProxyConnect, this, std::placeholders::_1));
|
||||
}));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -606,10 +608,10 @@ namespace proxy {
|
||||
}
|
||||
}
|
||||
|
||||
void HTTPReqHandler::HandleUpstreamProxyResolved(const boost::system::error_code & ec, boost::asio::ip::tcp::resolver::iterator it, ProxyResolvedHandler handler)
|
||||
void HTTPReqHandler::HandleUpstreamProxyResolved(const boost::system::error_code & ec, boost::asio::ip::tcp::resolver::results_type endpoints, ProxyResolvedHandler handler)
|
||||
{
|
||||
if(ec) GenericProxyError(tr("Cannot resolve upstream proxy"), ec.message());
|
||||
else handler(*it);
|
||||
else handler(*endpoints.begin ());
|
||||
}
|
||||
|
||||
void HTTPReqHandler::HandleUpstreamSocksProxyConnect(const boost::system::error_code & ec)
|
||||
|
||||
@@ -24,7 +24,7 @@ namespace i2p
|
||||
namespace client
|
||||
{
|
||||
|
||||
I2CPDestination::I2CPDestination (boost::asio::io_service& service, std::shared_ptr<I2CPSession> owner,
|
||||
I2CPDestination::I2CPDestination (boost::asio::io_context& service, std::shared_ptr<I2CPSession> owner,
|
||||
std::shared_ptr<const i2p::data::IdentityEx> identity, bool isPublic, bool isSameThread,
|
||||
const std::map<std::string, std::string>& params):
|
||||
LeaseSetDestination (service, isPublic, ¶ms),
|
||||
@@ -88,7 +88,7 @@ namespace client
|
||||
|
||||
void I2CPDestination::CreateNewLeaseSet (const std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> >& tunnels)
|
||||
{
|
||||
GetService ().post (std::bind (&I2CPDestination::PostCreateNewLeaseSet, this, tunnels));
|
||||
boost::asio::post (GetService (), std::bind (&I2CPDestination::PostCreateNewLeaseSet, this, tunnels));
|
||||
}
|
||||
|
||||
void I2CPDestination::PostCreateNewLeaseSet (std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> > tunnels)
|
||||
@@ -170,7 +170,7 @@ namespace client
|
||||
{
|
||||
// send in destination's thread
|
||||
auto s = GetSharedFromThis ();
|
||||
GetService ().post (
|
||||
boost::asio::post (GetService (),
|
||||
[s, msg, remote, nonce]()
|
||||
{
|
||||
bool sent = s->SendMsg (msg, remote);
|
||||
@@ -879,7 +879,7 @@ namespace client
|
||||
if (!remoteSession || !m_Destination->SendMsg (buf + offset, payloadLen, remoteSession, nonce))
|
||||
{
|
||||
i2p::data::IdentHash identHash;
|
||||
SHA256(ident, identSize, identHash); // caclulate ident hash, because we don't need full identity
|
||||
SHA256(ident, identSize, identHash); // calculate ident hash, because we don't need full identity
|
||||
m_Destination->SendMsgTo (buf + offset, payloadLen, identHash, nonce);
|
||||
}
|
||||
}
|
||||
@@ -1079,7 +1079,7 @@ namespace client
|
||||
I2CPServer::I2CPServer (const std::string& interface, uint16_t port, bool isSingleThread):
|
||||
RunnableService ("I2CP"), m_IsSingleThread (isSingleThread),
|
||||
m_Acceptor (GetIOService (),
|
||||
boost::asio::ip::tcp::endpoint(boost::asio::ip::address::from_string(interface), port))
|
||||
boost::asio::ip::tcp::endpoint(boost::asio::ip::make_address(interface), port))
|
||||
{
|
||||
memset (m_MessagesHandlers, 0, sizeof (m_MessagesHandlers));
|
||||
m_MessagesHandlers[I2CP_GET_DATE_MESSAGE] = &I2CPSession::GetDateMessageHandler;
|
||||
|
||||
@@ -81,7 +81,7 @@ namespace client
|
||||
{
|
||||
public:
|
||||
|
||||
I2CPDestination (boost::asio::io_service& service, std::shared_ptr<I2CPSession> owner,
|
||||
I2CPDestination (boost::asio::io_context& service, std::shared_ptr<I2CPSession> owner,
|
||||
std::shared_ptr<const i2p::data::IdentityEx> identity, bool isPublic, bool isSameThread,
|
||||
const std::map<std::string, std::string>& params);
|
||||
~I2CPDestination () {};
|
||||
@@ -227,7 +227,7 @@ namespace client
|
||||
|
||||
void Start ();
|
||||
void Stop ();
|
||||
boost::asio::io_service& GetService () { return GetIOService (); };
|
||||
auto& GetService () { return GetIOService (); };
|
||||
bool IsSingleThread () const { return m_IsSingleThread; };
|
||||
|
||||
bool InsertSession (std::shared_ptr<I2CPSession> session);
|
||||
|
||||
@@ -61,7 +61,7 @@ namespace client
|
||||
}
|
||||
void CreateStream (StreamRequestComplete streamRequestComplete, const std::string& dest, uint16_t port = 0);
|
||||
void CreateStream(StreamRequestComplete complete, std::shared_ptr<const Address> address, uint16_t port);
|
||||
inline boost::asio::io_service& GetService () { return m_LocalDestination->GetService (); }
|
||||
auto& GetService () { return m_LocalDestination->GetService (); }
|
||||
|
||||
virtual void Start () = 0;
|
||||
virtual void Stop () = 0;
|
||||
@@ -283,7 +283,7 @@ namespace client
|
||||
public:
|
||||
|
||||
TCPIPAcceptor (const std::string& address, uint16_t port, std::shared_ptr<ClientDestination> localDestination = nullptr) :
|
||||
ServiceAcceptor (boost::asio::ip::tcp::endpoint (boost::asio::ip::address::from_string(address), port), localDestination) {}
|
||||
ServiceAcceptor (boost::asio::ip::tcp::endpoint (boost::asio::ip::make_address(address), port), localDestination) {}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2023, The PurpleI2P Project
|
||||
* Copyright (c) 2013-2024, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
@@ -375,10 +375,10 @@ namespace client
|
||||
}
|
||||
|
||||
I2PServerTunnelConnectionHTTP::I2PServerTunnelConnectionHTTP (I2PService * owner, std::shared_ptr<i2p::stream::Stream> stream,
|
||||
const boost::asio::ip::tcp::endpoint& target, const std::string& host,
|
||||
const boost::asio::ip::tcp::endpoint& target, const std::string& host, const std::string& XI2P,
|
||||
std::shared_ptr<boost::asio::ssl::context> sslCtx):
|
||||
I2PTunnelConnection (owner, stream, target, true, sslCtx), m_Host (host),
|
||||
m_HeaderSent (false), m_ResponseHeaderSent (false), m_From (stream->GetRemoteIdentity ())
|
||||
I2PTunnelConnection (owner, stream, target, true, sslCtx), m_Host (host), m_XI2P (XI2P),
|
||||
m_HeaderSent (false), m_ResponseHeaderSent (false)
|
||||
{
|
||||
if (sslCtx)
|
||||
SSL_set_tlsext_host_name(GetSSL ()->native_handle(), host.c_str ());
|
||||
@@ -448,17 +448,12 @@ namespace client
|
||||
if (!connection)
|
||||
m_OutHeader << "Connection: close\r\n";
|
||||
// add X-I2P fields
|
||||
if (m_From)
|
||||
{
|
||||
m_OutHeader << X_I2P_DEST_B32 << ": " << context.GetAddressBook ().ToAddress(m_From->GetIdentHash ()) << "\r\n";
|
||||
m_OutHeader << X_I2P_DEST_HASH << ": " << m_From->GetIdentHash ().ToBase64 () << "\r\n";
|
||||
m_OutHeader << X_I2P_DEST_B64 << ": " << m_From->ToBase64 () << "\r\n";
|
||||
}
|
||||
|
||||
m_OutHeader << "\r\n"; // end of header
|
||||
m_OutHeader << m_XI2P;
|
||||
// end of header
|
||||
m_OutHeader << "\r\n";
|
||||
|
||||
m_OutHeader << m_InHeader.str ().substr (m_InHeader.tellg ()); // data right after header
|
||||
m_InHeader.str ("");
|
||||
m_From = nullptr;
|
||||
m_HeaderSent = true;
|
||||
I2PTunnelConnection::Write ((uint8_t *)m_OutHeader.str ().c_str (), m_OutHeader.str ().length ());
|
||||
}
|
||||
@@ -717,7 +712,7 @@ namespace client
|
||||
{
|
||||
m_Endpoint.port (m_Port);
|
||||
boost::system::error_code ec;
|
||||
auto addr = boost::asio::ip::address::from_string (m_Address, ec);
|
||||
auto addr = boost::asio::ip::make_address (m_Address, ec);
|
||||
if (!ec)
|
||||
{
|
||||
m_Endpoint.address (addr);
|
||||
@@ -726,7 +721,7 @@ namespace client
|
||||
else
|
||||
{
|
||||
auto resolver = std::make_shared<boost::asio::ip::tcp::resolver>(GetService ());
|
||||
resolver->async_resolve (boost::asio::ip::tcp::resolver::query (m_Address, ""),
|
||||
resolver->async_resolve (m_Address, "",
|
||||
std::bind (&I2PServerTunnel::HandleResolve, this,
|
||||
std::placeholders::_1, std::placeholders::_2, resolver));
|
||||
}
|
||||
@@ -743,7 +738,7 @@ namespace client
|
||||
ClearHandlers ();
|
||||
}
|
||||
|
||||
void I2PServerTunnel::HandleResolve (const boost::system::error_code& ecode, boost::asio::ip::tcp::resolver::iterator it,
|
||||
void I2PServerTunnel::HandleResolve (const boost::system::error_code& ecode, boost::asio::ip::tcp::resolver::results_type endpoints,
|
||||
std::shared_ptr<boost::asio::ip::tcp::resolver> resolver)
|
||||
{
|
||||
if (!ecode)
|
||||
@@ -752,10 +747,9 @@ namespace client
|
||||
boost::asio::ip::tcp::endpoint ep;
|
||||
if (m_LocalAddress)
|
||||
{
|
||||
boost::asio::ip::tcp::resolver::iterator end;
|
||||
while (it != end)
|
||||
for (const auto& it: endpoints)
|
||||
{
|
||||
ep = *it;
|
||||
ep = it;
|
||||
if (!ep.address ().is_unspecified ())
|
||||
{
|
||||
if (ep.address ().is_v4 ())
|
||||
@@ -774,27 +768,26 @@ namespace client
|
||||
}
|
||||
}
|
||||
if (found) break;
|
||||
it++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
found = true;
|
||||
ep = *it; // first available
|
||||
ep = *endpoints.begin (); // first available
|
||||
}
|
||||
if (!found)
|
||||
{
|
||||
LogPrint (eLogError, "I2PTunnel: Unable to resolve to compatible address");
|
||||
LogPrint (eLogError, "I2PTunnel: Unable to resolve ", m_Address, " to compatible address");
|
||||
return;
|
||||
}
|
||||
|
||||
auto addr = ep.address ();
|
||||
LogPrint (eLogInfo, "I2PTunnel: Server tunnel ", (*it).host_name (), " has been resolved to ", addr);
|
||||
LogPrint (eLogInfo, "I2PTunnel: Server tunnel ", (*endpoints.begin ()).host_name (), " has been resolved to ", addr);
|
||||
m_Endpoint.address (addr);
|
||||
Accept ();
|
||||
}
|
||||
else
|
||||
LogPrint (eLogError, "I2PTunnel: Unable to resolve server tunnel address: ", ecode.message ());
|
||||
LogPrint (eLogError, "I2PTunnel: Unable to resolve server tunnel address ", m_Address, ": ", ecode.message ());
|
||||
}
|
||||
|
||||
void I2PServerTunnel::SetAccessList (const std::set<i2p::data::IdentHash>& accessList)
|
||||
@@ -806,7 +799,7 @@ namespace client
|
||||
void I2PServerTunnel::SetLocalAddress (const std::string& localAddress)
|
||||
{
|
||||
boost::system::error_code ec;
|
||||
auto addr = boost::asio::ip::address::from_string(localAddress, ec);
|
||||
auto addr = boost::asio::ip::make_address(localAddress, ec);
|
||||
if (!ec)
|
||||
m_LocalAddress.reset (new boost::asio::ip::address (addr));
|
||||
else
|
||||
@@ -878,7 +871,17 @@ namespace client
|
||||
|
||||
std::shared_ptr<I2PTunnelConnection> I2PServerTunnelHTTP::CreateI2PConnection (std::shared_ptr<i2p::stream::Stream> stream)
|
||||
{
|
||||
return std::make_shared<I2PServerTunnelConnectionHTTP> (this, stream, GetEndpoint (), m_Host, GetSSLCtx ());
|
||||
if (m_XI2P.empty () || stream->GetRemoteIdentity () != m_From.lock ())
|
||||
{
|
||||
auto from = stream->GetRemoteIdentity ();
|
||||
m_From = from;
|
||||
std::stringstream ss;
|
||||
ss << X_I2P_DEST_B32 << ": " << context.GetAddressBook ().ToAddress(from->GetIdentHash ()) << "\r\n";
|
||||
ss << X_I2P_DEST_HASH << ": " << from->GetIdentHash ().ToBase64 () << "\r\n";
|
||||
ss << X_I2P_DEST_B64 << ": " << from->ToBase64 () << "\r\n";
|
||||
m_XI2P = ss.str ();
|
||||
}
|
||||
return std::make_shared<I2PServerTunnelConnectionHTTP> (this, stream, GetEndpoint (), m_Host, m_XI2P, GetSSLCtx ());
|
||||
}
|
||||
|
||||
I2PServerTunnelIRC::I2PServerTunnelIRC (const std::string& name, const std::string& address,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2023, The PurpleI2P Project
|
||||
* Copyright (c) 2013-2024, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
@@ -31,9 +31,9 @@ namespace client
|
||||
const int I2P_TUNNEL_CONNECTION_MAX_IDLE = 3600; // in seconds
|
||||
const int I2P_TUNNEL_DESTINATION_REQUEST_TIMEOUT = 10; // in seconds
|
||||
// for HTTP tunnels
|
||||
const char X_I2P_DEST_HASH[] = "X-I2P-DestHash"; // hash in base64
|
||||
const char X_I2P_DEST_B64[] = "X-I2P-DestB64"; // full address in base64
|
||||
const char X_I2P_DEST_B32[] = "X-I2P-DestB32"; // .b32.i2p address
|
||||
constexpr char X_I2P_DEST_HASH[] = "X-I2P-DestHash"; // hash in base64
|
||||
constexpr char X_I2P_DEST_B64[] = "X-I2P-DestB64"; // full address in base64
|
||||
constexpr char X_I2P_DEST_B32[] = "X-I2P-DestB32"; // .b32.i2p address
|
||||
const int I2P_TUNNEL_HTTP_MAX_HEADER_SIZE = 8192;
|
||||
|
||||
class I2PTunnelConnection: public I2PServiceHandler, public std::enable_shared_from_this<I2PTunnelConnection>
|
||||
@@ -107,7 +107,7 @@ namespace client
|
||||
public:
|
||||
|
||||
I2PServerTunnelConnectionHTTP (I2PService * owner, std::shared_ptr<i2p::stream::Stream> stream,
|
||||
const boost::asio::ip::tcp::endpoint& target, const std::string& host,
|
||||
const boost::asio::ip::tcp::endpoint& target, const std::string& host, const std::string& XI2P,
|
||||
std::shared_ptr<boost::asio::ssl::context> sslCtx = nullptr);
|
||||
|
||||
protected:
|
||||
@@ -117,10 +117,9 @@ namespace client
|
||||
|
||||
private:
|
||||
|
||||
std::string m_Host;
|
||||
std::string m_Host, m_XI2P;
|
||||
std::stringstream m_InHeader, m_OutHeader;
|
||||
bool m_HeaderSent, m_ResponseHeaderSent;
|
||||
std::shared_ptr<const i2p::data::IdentityEx> m_From;
|
||||
};
|
||||
|
||||
class I2PTunnelConnectionIRC: public I2PTunnelConnection
|
||||
@@ -208,7 +207,7 @@ namespace client
|
||||
|
||||
private:
|
||||
|
||||
void HandleResolve (const boost::system::error_code& ecode, boost::asio::ip::tcp::resolver::iterator it,
|
||||
void HandleResolve (const boost::system::error_code& ecode, boost::asio::ip::tcp::resolver::results_type endpoints,
|
||||
std::shared_ptr<boost::asio::ip::tcp::resolver> resolver);
|
||||
|
||||
void Accept ();
|
||||
@@ -242,7 +241,8 @@ namespace client
|
||||
|
||||
private:
|
||||
|
||||
std::string m_Host;
|
||||
std::string m_Host, m_XI2P;
|
||||
std::weak_ptr<const i2p::data::IdentityEx> m_From;
|
||||
};
|
||||
|
||||
class I2PServerTunnelIRC: public I2PServerTunnel
|
||||
|
||||
@@ -371,7 +371,7 @@ namespace client
|
||||
// udp forward selected
|
||||
boost::system::error_code e;
|
||||
// TODO: support hostnames in udp forward
|
||||
auto addr = boost::asio::ip::address::from_string(params[SAM_PARAM_HOST], e);
|
||||
auto addr = boost::asio::ip::make_address(params[SAM_PARAM_HOST], e);
|
||||
if (e)
|
||||
{
|
||||
// not an ip address
|
||||
@@ -624,7 +624,7 @@ namespace client
|
||||
auto socket = session->acceptQueue.front ().first;
|
||||
session->acceptQueue.pop_front ();
|
||||
if (socket)
|
||||
m_Owner.GetService ().post (std::bind(&SAMSocket::TerminateClose, socket));
|
||||
boost::asio::post (m_Owner.GetService (), std::bind(&SAMSocket::TerminateClose, socket));
|
||||
}
|
||||
if (session->acceptQueue.size () < SAM_SESSION_MAX_ACCEPT_QUEUE_SIZE)
|
||||
{
|
||||
@@ -1046,13 +1046,13 @@ namespace client
|
||||
else
|
||||
{
|
||||
auto s = shared_from_this ();
|
||||
m_Owner.GetService ().post ([s] { s->Terminate ("stream read error"); });
|
||||
boost::asio::post (m_Owner.GetService (), [s] { s->Terminate ("stream read error"); });
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
auto s = shared_from_this ();
|
||||
m_Owner.GetService ().post ([s] { s->Terminate ("stream read error (op aborted)"); });
|
||||
boost::asio::post (m_Owner.GetService (), [s] { s->Terminate ("stream read error (op aborted)"); });
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -1102,7 +1102,7 @@ namespace client
|
||||
auto socket = session->acceptQueue.front ().first;
|
||||
session->acceptQueue.pop_front ();
|
||||
if (socket)
|
||||
m_Owner.GetService ().post (std::bind(&SAMSocket::TerminateClose, socket));
|
||||
boost::asio::post (m_Owner.GetService (), std::bind(&SAMSocket::TerminateClose, socket));
|
||||
}
|
||||
if (!session->acceptQueue.empty ())
|
||||
{
|
||||
@@ -1236,7 +1236,7 @@ namespace client
|
||||
|
||||
void SAMSocket::HandleStreamSend(const boost::system::error_code & ec)
|
||||
{
|
||||
m_Owner.GetService ().post (std::bind( !ec ? &SAMSocket::Receive : &SAMSocket::TerminateClose, shared_from_this()));
|
||||
boost::asio::post (m_Owner.GetService (), std::bind( !ec ? &SAMSocket::Receive : &SAMSocket::TerminateClose, shared_from_this()));
|
||||
}
|
||||
|
||||
SAMSession::SAMSession (SAMBridge & parent, const std::string & id, SAMSessionType type):
|
||||
@@ -1310,8 +1310,8 @@ namespace client
|
||||
|
||||
SAMBridge::SAMBridge (const std::string& address, uint16_t portTCP, uint16_t portUDP, bool singleThread):
|
||||
RunnableService ("SAM"), m_IsSingleThread (singleThread),
|
||||
m_Acceptor (GetIOService (), boost::asio::ip::tcp::endpoint(boost::asio::ip::address::from_string(address), portTCP)),
|
||||
m_DatagramEndpoint (boost::asio::ip::address::from_string(address), (!portUDP) ? portTCP-1 : portUDP), m_DatagramSocket (GetIOService (), m_DatagramEndpoint),
|
||||
m_Acceptor (GetIOService (), boost::asio::ip::tcp::endpoint(boost::asio::ip::make_address(address), portTCP)),
|
||||
m_DatagramEndpoint (boost::asio::ip::make_address(address), (!portUDP) ? portTCP-1 : portUDP), m_DatagramSocket (GetIOService (), m_DatagramEndpoint),
|
||||
m_SignatureTypes
|
||||
{
|
||||
{"DSA_SHA1", i2p::data::SIGNING_KEY_TYPE_DSA_SHA1},
|
||||
|
||||
@@ -246,7 +246,7 @@ namespace client
|
||||
void Start ();
|
||||
void Stop ();
|
||||
|
||||
boost::asio::io_service& GetService () { return GetIOService (); };
|
||||
auto& 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);
|
||||
|
||||
@@ -126,9 +126,8 @@ namespace proxy
|
||||
void HandleSockRecv(const boost::system::error_code & ecode, std::size_t bytes_transfered);
|
||||
void Terminate();
|
||||
void AsyncSockRead();
|
||||
boost::asio::const_buffers_1 GenerateSOCKS5SelectAuth(authMethods method);
|
||||
boost::asio::const_buffers_1 GenerateSOCKS4Response(errTypes error, uint32_t ip, uint16_t port);
|
||||
boost::asio::const_buffers_1 GenerateSOCKS5Response(errTypes error, addrTypes type, const address &addr, uint16_t port);
|
||||
boost::asio::const_buffer GenerateSOCKS4Response(errTypes error, uint32_t ip, uint16_t port);
|
||||
boost::asio::const_buffer GenerateSOCKS5Response(errTypes error, addrTypes type, const address &addr, uint16_t port);
|
||||
bool Socks5ChooseAuth();
|
||||
void Socks5UserPasswdResponse ();
|
||||
void SocksRequestFailed(errTypes error);
|
||||
@@ -145,9 +144,9 @@ namespace proxy
|
||||
template<typename Socket>
|
||||
void SendUpstreamRequest(std::shared_ptr<Socket>& upstreamSock);
|
||||
void HandleUpstreamConnected(const boost::system::error_code & ecode,
|
||||
boost::asio::ip::tcp::resolver::iterator itr);
|
||||
const boost::asio::ip::tcp::endpoint& ep);
|
||||
void HandleUpstreamResolved(const boost::system::error_code & ecode,
|
||||
boost::asio::ip::tcp::resolver::iterator itr);
|
||||
boost::asio::ip::tcp::resolver::results_type endpoints);
|
||||
|
||||
boost::asio::ip::tcp::resolver m_proxy_resolver;
|
||||
uint8_t m_sock_buff[socks_buffer_size];
|
||||
@@ -233,17 +232,17 @@ namespace proxy
|
||||
Done(shared_from_this());
|
||||
}
|
||||
|
||||
boost::asio::const_buffers_1 SOCKSHandler::GenerateSOCKS4Response(SOCKSHandler::errTypes error, uint32_t ip, uint16_t port)
|
||||
boost::asio::const_buffer SOCKSHandler::GenerateSOCKS4Response(SOCKSHandler::errTypes error, uint32_t ip, uint16_t port)
|
||||
{
|
||||
assert(error >= SOCKS4_OK);
|
||||
m_response[0] = '\x00'; // version
|
||||
m_response[1] = error; // response code
|
||||
htobe16buf(m_response + 2, port); // port
|
||||
htobe32buf(m_response + 4, ip); // IP
|
||||
return boost::asio::const_buffers_1(m_response,8);
|
||||
return boost::asio::const_buffer (m_response,8);
|
||||
}
|
||||
|
||||
boost::asio::const_buffers_1 SOCKSHandler::GenerateSOCKS5Response(SOCKSHandler::errTypes error, SOCKSHandler::addrTypes type, const SOCKSHandler::address &addr, uint16_t port)
|
||||
boost::asio::const_buffer SOCKSHandler::GenerateSOCKS5Response(SOCKSHandler::errTypes error, SOCKSHandler::addrTypes type, const SOCKSHandler::address &addr, uint16_t port)
|
||||
{
|
||||
size_t size = 6; // header + port
|
||||
assert(error <= SOCKS5_ADDR_UNSUP);
|
||||
@@ -280,14 +279,14 @@ namespace proxy
|
||||
}
|
||||
break;
|
||||
}
|
||||
return boost::asio::const_buffers_1(m_response, size);
|
||||
return boost::asio::const_buffer (m_response, size);
|
||||
}
|
||||
|
||||
bool SOCKSHandler::Socks5ChooseAuth()
|
||||
{
|
||||
m_response[0] = '\x05'; // Version
|
||||
m_response[1] = m_authchosen; // Response code
|
||||
boost::asio::const_buffers_1 response(m_response, 2);
|
||||
boost::asio::const_buffer response(m_response, 2);
|
||||
if (m_authchosen == AUTH_UNACCEPTABLE)
|
||||
{
|
||||
LogPrint(eLogWarning, "SOCKS: v5 authentication negotiation failed");
|
||||
@@ -307,14 +306,14 @@ namespace proxy
|
||||
m_response[0] = 1; // Version of the subnegotiation
|
||||
m_response[1] = 0; // Response code
|
||||
LogPrint(eLogDebug, "SOCKS: v5 user/password response");
|
||||
boost::asio::async_write(*m_sock, boost::asio::const_buffers_1(m_response, 2),
|
||||
boost::asio::async_write(*m_sock, boost::asio::const_buffer(m_response, 2),
|
||||
std::bind(&SOCKSHandler::SentSocksResponse, shared_from_this(), std::placeholders::_1));
|
||||
}
|
||||
|
||||
/* All hope is lost beyond this point */
|
||||
void SOCKSHandler::SocksRequestFailed(SOCKSHandler::errTypes error)
|
||||
{
|
||||
boost::asio::const_buffers_1 response(nullptr,0);
|
||||
boost::asio::const_buffer response(nullptr,0);
|
||||
assert(error != SOCKS4_OK && error != SOCKS5_OK);
|
||||
switch (m_socksv)
|
||||
{
|
||||
@@ -334,7 +333,7 @@ namespace proxy
|
||||
|
||||
void SOCKSHandler::SocksRequestSuccess()
|
||||
{
|
||||
boost::asio::const_buffers_1 response(nullptr,0);
|
||||
boost::asio::const_buffer response(nullptr,0);
|
||||
// TODO: this should depend on things like the command type and callbacks may change
|
||||
switch (m_socksv)
|
||||
{
|
||||
@@ -691,9 +690,8 @@ namespace proxy
|
||||
if (m_UpstreamProxyPort) // TCP
|
||||
{
|
||||
EnterState(UPSTREAM_RESOLVE);
|
||||
boost::asio::ip::tcp::resolver::query q(m_UpstreamProxyAddress, std::to_string(m_UpstreamProxyPort));
|
||||
m_proxy_resolver.async_resolve(q, std::bind(&SOCKSHandler::HandleUpstreamResolved, shared_from_this(),
|
||||
std::placeholders::_1, std::placeholders::_2));
|
||||
m_proxy_resolver.async_resolve(m_UpstreamProxyAddress, std::to_string(m_UpstreamProxyPort),
|
||||
std::bind(&SOCKSHandler::HandleUpstreamResolved, shared_from_this(), std::placeholders::_1, std::placeholders::_2));
|
||||
}
|
||||
else if (!m_UpstreamProxyAddress.empty ())// local
|
||||
{
|
||||
@@ -729,7 +727,7 @@ namespace proxy
|
||||
void SOCKSHandler::SocksUpstreamSuccess(std::shared_ptr<Socket>& upstreamSock)
|
||||
{
|
||||
LogPrint(eLogInfo, "SOCKS: Upstream success");
|
||||
boost::asio::const_buffers_1 response(nullptr, 0);
|
||||
boost::asio::const_buffer response(nullptr, 0);
|
||||
switch (m_socksv)
|
||||
{
|
||||
case SOCKS4:
|
||||
@@ -775,7 +773,8 @@ namespace proxy
|
||||
LogPrint(eLogError, "SOCKS: No upstream socket to send handshake to");
|
||||
}
|
||||
|
||||
void SOCKSHandler::HandleUpstreamConnected(const boost::system::error_code & ecode, boost::asio::ip::tcp::resolver::iterator itr)
|
||||
void SOCKSHandler::HandleUpstreamConnected(const boost::system::error_code & ecode,
|
||||
const boost::asio::ip::tcp::endpoint& ep)
|
||||
{
|
||||
if (ecode) {
|
||||
LogPrint(eLogWarning, "SOCKS: Could not connect to upstream proxy: ", ecode.message());
|
||||
@@ -786,7 +785,8 @@ namespace proxy
|
||||
SendUpstreamRequest(m_upstreamSock);
|
||||
}
|
||||
|
||||
void SOCKSHandler::HandleUpstreamResolved(const boost::system::error_code & ecode, boost::asio::ip::tcp::resolver::iterator itr)
|
||||
void SOCKSHandler::HandleUpstreamResolved(const boost::system::error_code & ecode,
|
||||
boost::asio::ip::tcp::resolver::results_type endpoints)
|
||||
{
|
||||
if (ecode) {
|
||||
// error resolving
|
||||
@@ -798,7 +798,7 @@ namespace proxy
|
||||
EnterState(UPSTREAM_CONNECT);
|
||||
auto & service = GetOwner()->GetService();
|
||||
m_upstreamSock = std::make_shared<boost::asio::ip::tcp::socket>(service);
|
||||
boost::asio::async_connect(*m_upstreamSock, itr,
|
||||
boost::asio::async_connect(*m_upstreamSock, endpoints,
|
||||
std::bind(&SOCKSHandler::HandleUpstreamConnected,
|
||||
shared_from_this(), std::placeholders::_1, std::placeholders::_2));
|
||||
}
|
||||
|
||||
@@ -86,7 +86,7 @@ namespace client
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint(eLogWarning, "UDPServer: Session with from ", remotePort, " and to ", localPort, " ports already exists. But from differend address. Removed");
|
||||
LogPrint(eLogWarning, "UDPServer: Session with from ", remotePort, " and to ", localPort, " ports already exists. But from different address. Removed");
|
||||
m_Sessions.erase (it);
|
||||
}
|
||||
}
|
||||
@@ -203,7 +203,7 @@ namespace client
|
||||
std::vector<std::shared_ptr<DatagramSessionInfo> > sessions;
|
||||
std::lock_guard<std::mutex> lock (m_SessionsMutex);
|
||||
|
||||
for (auto it: m_Sessions)
|
||||
for (const auto &it: m_Sessions)
|
||||
{
|
||||
auto s = it.second;
|
||||
if (!s->m_Destination) continue;
|
||||
|
||||
@@ -49,10 +49,6 @@ set(test-gost-sig_SRCS
|
||||
test-gost-sig.cpp
|
||||
)
|
||||
|
||||
set(test-x25519_SRCS
|
||||
test-x25519.cpp
|
||||
)
|
||||
|
||||
set(test-aeadchacha20poly1305_SRCS
|
||||
test-aeadchacha20poly1305.cpp
|
||||
)
|
||||
@@ -69,6 +65,10 @@ set(test-eddsa_SRCS
|
||||
test-eddsa.cpp
|
||||
)
|
||||
|
||||
set(test-aes_SRCS
|
||||
test-aes.cpp
|
||||
)
|
||||
|
||||
add_executable(test-http-merge_chunked ${test-http-merge_chunked_SRCS})
|
||||
add_executable(test-http-req ${test-http-req_SRCS})
|
||||
add_executable(test-http-res ${test-http-res_SRCS})
|
||||
@@ -77,11 +77,11 @@ add_executable(test-http-url ${test-http-url_SRCS})
|
||||
add_executable(test-base-64 ${test-base-64_SRCS})
|
||||
add_executable(test-gost ${test-gost_SRCS})
|
||||
add_executable(test-gost-sig ${test-gost-sig_SRCS})
|
||||
add_executable(test-x25519 ${test-x25519_SRCS})
|
||||
add_executable(test-aeadchacha20poly1305 ${test-aeadchacha20poly1305_SRCS})
|
||||
add_executable(test-blinding ${test-blinding_SRCS})
|
||||
add_executable(test-elligator ${test-elligator_SRCS})
|
||||
add_executable(test-eddsa ${test-eddsa_SRCS})
|
||||
add_executable(test-aes ${test-aes_SRCS})
|
||||
|
||||
set(LIBS
|
||||
libi2pd
|
||||
@@ -102,11 +102,11 @@ target_link_libraries(test-http-url ${LIBS})
|
||||
target_link_libraries(test-base-64 ${LIBS})
|
||||
target_link_libraries(test-gost ${LIBS})
|
||||
target_link_libraries(test-gost-sig ${LIBS})
|
||||
target_link_libraries(test-x25519 ${LIBS})
|
||||
target_link_libraries(test-aeadchacha20poly1305 ${LIBS})
|
||||
target_link_libraries(test-blinding ${LIBS})
|
||||
target_link_libraries(test-elligator ${LIBS})
|
||||
target_link_libraries(test-eddsa ${LIBS})
|
||||
target_link_libraries(test-aes ${LIBS})
|
||||
|
||||
add_test(test-http-merge_chunked ${TEST_PATH}/test-http-merge_chunked)
|
||||
add_test(test-http-req ${TEST_PATH}/test-http-req)
|
||||
@@ -116,8 +116,8 @@ add_test(test-http-url ${TEST_PATH}/test-http-url)
|
||||
add_test(test-base-64 ${TEST_PATH}/test-base-64)
|
||||
add_test(test-gost ${TEST_PATH}/test-gost)
|
||||
add_test(test-gost-sig ${TEST_PATH}/test-gost-sig)
|
||||
add_test(test-x25519 ${TEST_PATH}/test-x25519)
|
||||
add_test(test-aeadchacha20poly1305 ${TEST_PATH}/test-aeadchacha20poly1305)
|
||||
add_test(test-blinding ${TEST_PATH}/test-blinding)
|
||||
add_test(test-elligator ${TEST_PATH}/test-elligator)
|
||||
add_test(test-eddsa ${TEST_PATH}/test-eddsa)
|
||||
add_test(test-aes ${TEST_PATH}/test-aes)
|
||||
|
||||
@@ -7,8 +7,8 @@ LIBI2PD = ../libi2pd.a
|
||||
|
||||
TESTS = \
|
||||
test-http-merge_chunked test-http-req test-http-res test-http-url test-http-url_decode \
|
||||
test-gost test-gost-sig test-base-64 test-x25519 test-aeadchacha20poly1305 test-blinding \
|
||||
test-elligator test-eddsa
|
||||
test-gost test-gost-sig test-base-64 test-aeadchacha20poly1305 test-blinding \
|
||||
test-elligator test-eddsa test-aes
|
||||
|
||||
ifneq (, $(findstring mingw, $(SYS))$(findstring windows-gnu, $(SYS))$(findstring cygwin, $(SYS)))
|
||||
CXXFLAGS += -DWIN32_LEAN_AND_MEAN
|
||||
@@ -44,9 +44,6 @@ test-gost: test-gost.cpp $(LIBI2PD)
|
||||
test-gost-sig: test-gost-sig.cpp $(LIBI2PD)
|
||||
$(CXX) $(CXXFLAGS) $(NEEDED_CXXFLAGS) $(INCFLAGS) $(LDFLAGS) -o $@ $^ $(LDLIBS)
|
||||
|
||||
test-x25519: test-x25519.cpp $(LIBI2PD)
|
||||
$(CXX) $(CXXFLAGS) $(NEEDED_CXXFLAGS) $(INCFLAGS) $(LDFLAGS) -o $@ $^ $(LDLIBS)
|
||||
|
||||
test-aeadchacha20poly1305: test-aeadchacha20poly1305.cpp $(LIBI2PD)
|
||||
$(CXX) $(CXXFLAGS) $(NEEDED_CXXFLAGS) $(INCFLAGS) $(LDFLAGS) -o $@ $^ $(LDLIBS)
|
||||
|
||||
@@ -59,6 +56,9 @@ test-elligator: test-elligator.cpp $(LIBI2PD)
|
||||
test-eddsa: test-eddsa.cpp $(LIBI2PD)
|
||||
$(CXX) $(CXXFLAGS) $(NEEDED_CXXFLAGS) $(INCFLAGS) $(LDFLAGS) -o $@ $^ $(LDLIBS)
|
||||
|
||||
test-aes: test-aes.cpp $(LIBI2PD)
|
||||
$(CXX) $(CXXFLAGS) $(NEEDED_CXXFLAGS) $(INCFLAGS) $(LDFLAGS) -o $@ $^ $(LDLIBS)
|
||||
|
||||
run: $(TESTS)
|
||||
@for TEST in $(TESTS); do echo Running $$TEST; ./$$TEST ; done
|
||||
|
||||
|
||||
69
tests/test-aes.cpp
Normal file
69
tests/test-aes.cpp
Normal file
@@ -0,0 +1,69 @@
|
||||
#include <cassert>
|
||||
#include <inttypes.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "Crypto.h"
|
||||
|
||||
uint8_t ecb_key1[32] =
|
||||
{
|
||||
0x60, 0x3d, 0xeb, 0x10, 0x15, 0xca, 0x71, 0xbe, 0x2b, 0x73, 0xae, 0xf0, 0x85, 0x7d, 0x77, 0x81,
|
||||
0x1f, 0x35, 0x2c, 0x07, 0x3b, 0x61, 0x08, 0xd7, 0x2d, 0x98, 0x10, 0xa3, 0x09, 0x14, 0xdf, 0xf4
|
||||
};
|
||||
|
||||
uint8_t ecb_plain1[16] =
|
||||
{
|
||||
0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a
|
||||
};
|
||||
|
||||
uint8_t ecb_cipher1[16] =
|
||||
{
|
||||
0xf3, 0xee, 0xd1, 0xbd, 0xb5, 0xd2, 0xa0, 0x3c, 0x06, 0x4b, 0x5a, 0x7e, 0x3d, 0xb1, 0x81, 0xf8
|
||||
};
|
||||
|
||||
uint8_t cbc_key1[32] =
|
||||
{
|
||||
0x60, 0x3d, 0xeb, 0x10, 0x15, 0xca, 0x71, 0xbe, 0x2b, 0x73, 0xae, 0xf0, 0x85, 0x7d, 0x77, 0x81,
|
||||
0x1f, 0x35, 0x2c, 0x07, 0x3b, 0x61, 0x08, 0xd7, 0x2d, 0x98, 0x10, 0xa3, 0x09, 0x14, 0xdf, 0xf4
|
||||
};
|
||||
|
||||
uint8_t cbc_iv1[16] =
|
||||
{
|
||||
0xF5, 0x8C, 0x4C, 0x04, 0xD6, 0xE5, 0xF1, 0xBA, 0x77, 0x9E, 0xAB, 0xFB, 0x5F, 0x7B, 0xFB, 0xD6
|
||||
};
|
||||
|
||||
uint8_t cbc_plain1[16] =
|
||||
{
|
||||
0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51
|
||||
};
|
||||
|
||||
uint8_t cbc_cipher1[16] =
|
||||
{
|
||||
0x9c, 0xfc, 0x4e, 0x96, 0x7e, 0xdb, 0x80, 0x8d, 0x67, 0x9f, 0x77, 0x7b, 0xc6, 0x70, 0x2c, 0x7d
|
||||
};
|
||||
|
||||
int main ()
|
||||
{
|
||||
// ECB encrypt test1
|
||||
i2p::crypto::ECBEncryption ecbencryption;
|
||||
ecbencryption.SetKey (ecb_key1);
|
||||
uint8_t out[16];
|
||||
ecbencryption.Encrypt (ecb_plain1, out);
|
||||
assert (memcmp (ecb_cipher1, out, 16) == 0);
|
||||
|
||||
// ECB decrypt test1
|
||||
i2p::crypto::ECBDecryption ecbdecryption;
|
||||
ecbdecryption.SetKey (ecb_key1);
|
||||
ecbdecryption.Decrypt (ecb_cipher1, out);
|
||||
assert (memcmp (ecb_plain1, out, 16) == 0);
|
||||
// CBC encrypt test
|
||||
i2p::crypto::CBCEncryption cbcencryption;
|
||||
cbcencryption.SetKey (cbc_key1);
|
||||
cbcencryption.Encrypt (cbc_plain1, 16, cbc_iv1, out);
|
||||
assert (memcmp (cbc_cipher1, out, 16) == 0);
|
||||
// CBC decrypt test
|
||||
i2p::crypto::CBCDecryption cbcdecryption;
|
||||
cbcdecryption.SetKey (cbc_key1);
|
||||
cbcdecryption.Decrypt (cbc_cipher1, 16, cbc_iv1, out);
|
||||
assert (memcmp (cbc_plain1, out, 16) == 0);
|
||||
}
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
#include <cassert>
|
||||
#include <inttypes.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "Ed25519.h"
|
||||
|
||||
const uint8_t k[32] =
|
||||
{
|
||||
0xa5, 0x46, 0xe3, 0x6b, 0xf0, 0x52, 0x7c, 0x9d, 0x3b, 0x16, 0x15,
|
||||
0x4b, 0x82, 0x46, 0x5e, 0xdd, 0x62, 0x14, 0x4c, 0x0a, 0xc1, 0xfc,
|
||||
0x5a, 0x18, 0x50, 0x6a, 0x22, 0x44, 0xba, 0x44, 0x9a, 0xc4
|
||||
};
|
||||
|
||||
const uint8_t u[32] =
|
||||
{
|
||||
0xe6, 0xdb, 0x68, 0x67, 0x58, 0x30, 0x30, 0xdb, 0x35, 0x94, 0xc1,
|
||||
0xa4, 0x24, 0xb1, 0x5f, 0x7c, 0x72, 0x66, 0x24, 0xec, 0x26, 0xb3,
|
||||
0x35, 0x3b, 0x10, 0xa9, 0x03, 0xa6, 0xd0, 0xab, 0x1c, 0x4c
|
||||
};
|
||||
|
||||
uint8_t p[32] =
|
||||
{
|
||||
0xc3, 0xda, 0x55, 0x37, 0x9d, 0xe9, 0xc6, 0x90, 0x8e, 0x94, 0xea,
|
||||
0x4d, 0xf2, 0x8d, 0x08, 0x4f, 0x32, 0xec, 0xcf, 0x03, 0x49, 0x1c,
|
||||
0x71, 0xf7, 0x54, 0xb4, 0x07, 0x55, 0x77, 0xa2, 0x85, 0x52
|
||||
};
|
||||
|
||||
int main ()
|
||||
{
|
||||
#if !OPENSSL_X25519
|
||||
// we test it for openssl < 1.1.0
|
||||
uint8_t buf[32];
|
||||
BN_CTX * ctx = BN_CTX_new ();
|
||||
i2p::crypto::GetEd25519 ()->ScalarMul (u, k, buf, ctx);
|
||||
BN_CTX_free (ctx);
|
||||
assert(memcmp (buf, p, 32) == 0);
|
||||
#endif
|
||||
}
|
||||
Reference in New Issue
Block a user