Browse Source

Merge pull request #2105 from PurpleI2P/openssl

2.54.0
master
orignal 2 months ago committed by GitHub
parent
commit
ecc635e9bc
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 19
      .github/workflows/build-windows.yml
  2. 38
      ChangeLog
  3. 20
      Makefile.bsd
  4. 4
      Makefile.haiku
  5. 2
      Makefile.homebrew
  6. 27
      Makefile.linux
  7. 11
      Makefile.mingw
  8. 2
      Makefile.osx
  9. 4
      Win32/Win32App.cpp
  10. 2
      Win32/Win32NetState.cpp
  11. 2
      Win32/Win32NetState.h
  12. 41
      build/CMakeLists.txt
  13. 4
      build/cmake_modules/CheckAtomic.cmake
  14. 7
      contrib/rpm/i2pd-git.spec
  15. 7
      contrib/rpm/i2pd.spec
  16. 2
      daemon/HTTPServer.cpp
  17. 6
      debian/changelog
  18. 8
      libi2pd/Config.cpp
  19. 4
      libi2pd/Datagram.cpp
  20. 22
      libi2pd/Destination.cpp
  21. 6
      libi2pd/Destination.h
  22. 2
      libi2pd/ECIESX25519AEADRatchetSession.cpp
  23. 2
      libi2pd/ECIESX25519AEADRatchetSession.h
  24. 120
      libi2pd/FS.cpp
  25. 12
      libi2pd/FS.h
  26. 37
      libi2pd/Garlic.cpp
  27. 1
      libi2pd/Garlic.h
  28. 95
      libi2pd/HTTP.cpp
  29. 13
      libi2pd/HTTP.h
  30. 1
      libi2pd/I2NPProtocol.h
  31. 20
      libi2pd/Log.h
  32. 53
      libi2pd/NTCP2.cpp
  33. 38
      libi2pd/NetDb.cpp
  34. 8
      libi2pd/NetDb.hpp
  35. 16
      libi2pd/RouterContext.cpp
  36. 2
      libi2pd/RouterContext.h
  37. 153
      libi2pd/RouterInfo.cpp
  38. 26
      libi2pd/RouterInfo.h
  39. 404
      libi2pd/SSU2.cpp
  40. 45
      libi2pd/SSU2.h
  41. 648
      libi2pd/SSU2Session.cpp
  42. 129
      libi2pd/SSU2Session.h
  43. 360
      libi2pd/Streaming.cpp
  44. 31
      libi2pd/Streaming.h
  45. 2
      libi2pd/Transports.cpp
  46. 14
      libi2pd/Tunnel.cpp
  47. 5
      libi2pd/Tunnel.h
  48. 20
      libi2pd/TunnelPool.cpp
  49. 4
      libi2pd/TunnelPool.h
  50. 12
      libi2pd/util.h
  51. 6
      libi2pd/version.h
  52. 35
      libi2pd_client/AddressBook.cpp
  53. 5
      libi2pd_client/AddressBook.h
  54. 13
      libi2pd_client/ClientContext.cpp
  55. 46
      libi2pd_client/HTTPProxy.cpp
  56. 12
      libi2pd_client/HTTPProxy.h
  57. 3
      libi2pd_client/I2CP.cpp
  58. 2
      libi2pd_client/SOCKS.cpp
  59. 4
      tests/Makefile

19
.github/workflows/build-windows.yml

@ -128,8 +128,11 @@ jobs:
cache: true cache: true
update: true update: true
- name: Clone MinGW packages repository - name: Clone MinGW packages repository and revert boost to 1.85.0
run: git clone https://github.com/msys2/MINGW-packages run: |
git clone https://github.com/msys2/MINGW-packages
cd MINGW-packages
git checkout 4cbb366edf2f268ac3146174b40ce38604646fc5 mingw-w64-boost
# headers # headers
- name: Get headers package version - name: Get headers package version
@ -205,21 +208,23 @@ jobs:
run: | run: |
cd MINGW-packages/mingw-w64-openssl cd MINGW-packages/mingw-w64-openssl
gpg --recv-keys D894E2CE8B3D79F5 gpg --recv-keys D894E2CE8B3D79F5
gpg --recv-keys 216094DFD0CB81EF
MINGW_ARCH=mingw32 makepkg-mingw -sCLf --noconfirm --nocheck MINGW_ARCH=mingw32 makepkg-mingw -sCLf --noconfirm --nocheck
- name: Install openssl package - name: Install openssl package
run: pacman --noconfirm -U MINGW-packages/mingw-w64-openssl/mingw-w64-i686-*-any.pkg.tar.zst run: pacman --noconfirm -U MINGW-packages/mingw-w64-openssl/mingw-w64-i686-*-any.pkg.tar.zst
# Boost # Boost
- name: Get boost package version #- name: Get boost package version
id: version-boost # id: version-boost
run: | # run: |
echo "version=$(pacman -Si mingw-w64-i686-boost | grep -Po '^Version\s*: \K.+')" >> $GITHUB_OUTPUT # echo "version=$(pacman -Si mingw-w64-i686-boost | grep -Po '^Version\s*: \K.+')" >> $GITHUB_OUTPUT
- name: Cache boost package - name: Cache boost package
uses: actions/cache@v4 uses: actions/cache@v4
id: cache-boost id: cache-boost
with: with:
path: MINGW-packages/mingw-w64-boost/*.zst path: MINGW-packages/mingw-w64-boost/*.zst
key: winxp-winpthreads-${{ steps.version-boost.outputs.version }} key: winxp-boost-1.85.0+crt-${{ steps.version-headers.outputs.version }}+ossl-${{ steps.version-openssl.outputs.version }}
# Rebuild package if packages above has changed
- name: Build WinXP-capable boost package - name: Build WinXP-capable boost package
if: steps.cache-boost.outputs.cache-hit != 'true' if: steps.cache-boost.outputs.cache-hit != 'true'
run: | run: |

38
ChangeLog

@ -1,6 +1,44 @@
# for this file format description, # for this file format description,
# see https://github.com/olivierlacan/keep-a-changelog # see https://github.com/olivierlacan/keep-a-changelog
## [2.54.0] - 2024-10-06
### Added
- Maintain recently connected routers list to avoid false-positive peer test
- Limited connectivity mode(through proxy)
- "i2p.streaming.profile" tunnel's param to let tunnel select also low-bandwidth routers
- Limit stream's inbound speed
- Periodic ack requests in ratchets session
- Set congestion cap G immediately if through proxy
- Show tunnel's routers bandwidth caps in web console
- Handle immediate ack requested flag in SSU2 data packets
- Resend and ack peer test and relay messages
- "senduseragent" HTTP proxy's param to pass through user's User-Agent
### Changed
- Exclude 'N' routers from high-bandwidth routers for client tunnels
- C++11 support has been dropped, the minimal requirement is C++17 now, C++20 for some compilers
- Removed dependency from boost::date_time and boost::filesystem
- Set default i2cp.leaseSetEncType to 0,4 and to 4 for server tunnels
- Handle i2cp.inboundlimit and i2cp.outboundlimit params in I2CP
- Publish LeaseSet with new timestamp update if tunnel was replaced in the same second
- Increase max number of generated tags to 800 per tagset
- Routing path expiration by time instead num attempts
- Save timestamp from epoch instead local time to profiles
- Update introducer's iTag if session to introducer was replaced to new one
- RTT, window size and number of NACKs calculation for streaming
- Don't select same peer for tunnel too often
- Use WinApi for data path UTF-8 conversion for Windows
### Fixed
- Jump link crash if address book is disabled
- Race condition if connect through an introducer
- "Date" header in I2PControl response
- Incomplete response from web console
- AEAD verification with LibreSSL
- Number of generated tags and new keys for follow-on tagsets
- Expired leases in LeaseSet
- Attempts to send HolePunch to 0.0.0.0
- Incorrect options size in quick ack streaming packet
- Low bandwidth router appeared as first peer in high-bandwidth client tunnel
## [2.53.1] - 2024-07-29 ## [2.53.1] - 2024-07-29
### Changed ### Changed
- I2CP performance improvement - I2CP performance improvement

20
Makefile.bsd

@ -1,18 +1,22 @@
CXX = clang++ CXX = clang++
CXXFLAGS ?= ${CXX_DEBUG} -Wall -Wextra -Wno-unused-parameter -pedantic -Wno-misleading-indentation CXXFLAGS ?= ${CXX_DEBUG} -Wall -Wextra -Wno-unused-parameter -pedantic -Wno-misleading-indentation
DEFINES = -D_GLIBCXX_USE_NANOSLEEP=1
INCFLAGS = -I/usr/include/ -I/usr/local/include/
LDFLAGS = ${LD_DEBUG} -Wl,-rpath,/usr/local/lib -L/usr/local/lib
LDLIBS = -lcrypto -lssl -lz -lpthread -lboost_system -lboost_program_options
## NOTE: NEEDED_CXXFLAGS is here so that custom CXXFLAGS can be specified at build time ## NOTE: NEEDED_CXXFLAGS is here so that custom CXXFLAGS can be specified at build time
## **without** overwriting the CXXFLAGS which we need in order to build. ## **without** overwriting the CXXFLAGS which we need in order to build.
## For example, when adding 'hardening flags' to the build ## For example, when adding 'hardening flags' to the build
## (e.g. -fstack-protector-strong -Wformat -Werror=format-security), we do not want to remove ## (e.g. -fstack-protector-strong -Wformat -Werror=format-security), we do not want to remove
## -std=c++11. If you want to remove this variable please do so in a way that allows setting ## -std=c++11. If you want to remove this variable please do so in a way that allows setting
## custom FLAGS to work at build-time. ## custom FLAGS to work at build-time.
CXXVER := $(shell $(CXX) -dumpversion) CXXVER := $(shell $(CXX) -dumpversion|cut -c 1-2)
ifeq (${CXXVER}, "4.2.1") # older clang always returned 4.2.1 ifeq (${CXXVER}, "4.") # older clang always returned 4.2.1
NEEDED_CXXFLAGS = -std=c++11 $(error Compiler too old)
else # newer versions support C++17 else ifeq (${CXXVER}, ${filter ${CXXVER},16 17 18 19}) # clang 16 - 19
NEEDED_CXXFLAGS = -std=c++20
else
NEEDED_CXXFLAGS = -std=c++17 NEEDED_CXXFLAGS = -std=c++17
endif endif
DEFINES = -D_GLIBCXX_USE_NANOSLEEP=1
INCFLAGS = -I/usr/include/ -I/usr/local/include/
LDFLAGS = ${LD_DEBUG} -Wl,-rpath,/usr/local/lib -L/usr/local/lib
LDLIBS = -lcrypto -lssl -lz -lboost_system -lboost_filesystem -lboost_program_options -lpthread

4
Makefile.haiku

@ -1,8 +1,8 @@
CXX = g++ CXX = g++
CXXFLAGS := -Wall -std=c++11 CXXFLAGS := -Wall -std=c++17
INCFLAGS = -I/system/develop/headers INCFLAGS = -I/system/develop/headers
DEFINES = -D_DEFAULT_SOURCE -D_GNU_SOURCE DEFINES = -D_DEFAULT_SOURCE -D_GNU_SOURCE
LDLIBS = -lbe -lbsd -lnetwork -lz -lcrypto -lssl -lboost_system -lboost_filesystem -lboost_program_options -lpthread LDLIBS = -lbe -lbsd -lnetwork -lz -lcrypto -lssl -lboost_system -lboost_program_options -lpthread
ifeq ($(USE_UPNP),yes) ifeq ($(USE_UPNP),yes)
DEFINES += -DUSE_UPNP DEFINES += -DUSE_UPNP

2
Makefile.homebrew

@ -5,7 +5,7 @@ SSLROOT = ${BREWROOT}/opt/openssl@1.1
UPNPROOT = ${BREWROOT}/opt/miniupnpc UPNPROOT = ${BREWROOT}/opt/miniupnpc
CXXFLAGS ?= ${CXX_DEBUG} -Wall -Wno-overloaded-virtual CXXFLAGS ?= ${CXX_DEBUG} -Wall -Wno-overloaded-virtual
NEEDED_CXXFLAGS ?= -std=c++11 NEEDED_CXXFLAGS ?= -std=c++17
INCFLAGS ?= -I${SSLROOT}/include -I${BOOSTROOT}/include INCFLAGS ?= -I${SSLROOT}/include -I${BOOSTROOT}/include
LDFLAGS ?= ${LD_DEBUG} LDFLAGS ?= ${LD_DEBUG}
DEFINES += -DMAC_OSX DEFINES += -DMAC_OSX

27
Makefile.linux

@ -9,24 +9,20 @@ LDFLAGS ?= ${LD_DEBUG}
## -std=c++11. If you want to remove this variable please do so in a way that allows setting ## -std=c++11. If you want to remove this variable please do so in a way that allows setting
## custom FDLAGS to work at build-time. ## custom FDLAGS to work at build-time.
# detect proper flag for c++11 support by compilers # detect proper flag for c++17 support by compilers
CXXVER := $(shell $(CXX) -dumpversion) CXXVER := $(shell $(CXX) -dumpversion)
ifeq ($(shell expr match $(CXX) 'clang'),5) ifeq ($(shell expr match $(CXX) 'clang'),5)
NEEDED_CXXFLAGS += -std=c++11
else ifeq ($(shell expr match ${CXXVER} "4\.[0-9][0-9]"),4) # gcc >= 4.10
NEEDED_CXXFLAGS += -std=c++11
else ifeq ($(shell expr match ${CXXVER} "4\.[8-9]"),3) # gcc 4.8 - 4.9
NEEDED_CXXFLAGS += -std=c++11 -D_GLIBCXX_USE_NANOSLEEP=1
else ifeq ($(shell expr match ${CXXVER} "[5-6]"),1) # gcc 5 - 6
NEEDED_CXXFLAGS += -std=c++11
LDLIBS = -latomic
else ifeq ($(shell expr match ${CXXVER} "[7-9]"),1) # gcc 7 - 9
NEEDED_CXXFLAGS += -std=c++17 NEEDED_CXXFLAGS += -std=c++17
LDLIBS = -latomic else ifeq ($(shell expr match ${CXXVER} "7"),1) # gcc 7
else ifeq ($(shell expr match ${CXXVER} "1[0-9]"),2) # gcc 10+
# NEEDED_CXXFLAGS += -std=c++20
NEEDED_CXXFLAGS += -std=c++17 NEEDED_CXXFLAGS += -std=c++17
LDLIBS = -latomic LDLIBS = -lboost_filesystem
else ifeq ($(shell expr match ${CXXVER} "[8-9]"),1) # gcc 8 - 9
NEEDED_CXXFLAGS += -std=c++17
LDLIBS = -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+
NEEDED_CXXFLAGS += -std=c++20
else # not supported else # not supported
$(error Compiler too old) $(error Compiler too old)
endif endif
@ -39,7 +35,6 @@ ifeq ($(USE_STATIC),yes)
# the shared libraries from the glibc version used for linking # the shared libraries from the glibc version used for linking
LIBDIR := /usr/lib/$(SYS) LIBDIR := /usr/lib/$(SYS)
LDLIBS += $(LIBDIR)/libboost_system.a LDLIBS += $(LIBDIR)/libboost_system.a
LDLIBS += $(LIBDIR)/libboost_filesystem.a
LDLIBS += $(LIBDIR)/libboost_program_options.a LDLIBS += $(LIBDIR)/libboost_program_options.a
LDLIBS += $(LIBDIR)/libssl.a LDLIBS += $(LIBDIR)/libssl.a
LDLIBS += $(LIBDIR)/libcrypto.a LDLIBS += $(LIBDIR)/libcrypto.a
@ -49,7 +44,7 @@ ifeq ($(USE_UPNP),yes)
endif endif
LDLIBS += -lpthread -ldl LDLIBS += -lpthread -ldl
else else
LDLIBS += -lcrypto -lssl -lz -lboost_system -lboost_filesystem -lboost_program_options -lpthread LDLIBS += -lcrypto -lssl -lz -lboost_system -lboost_program_options -lpthread -latomic
ifeq ($(USE_UPNP),yes) ifeq ($(USE_UPNP),yes)
LDLIBS += -lminiupnpc LDLIBS += -lminiupnpc
endif endif

11
Makefile.mingw

@ -7,7 +7,7 @@ CXXFLAGS := $(CXX_DEBUG) -fPIC -msse
INCFLAGS := -I$(DAEMON_SRC_DIR) -IWin32 INCFLAGS := -I$(DAEMON_SRC_DIR) -IWin32
LDFLAGS := ${LD_DEBUG} -static -fPIC -msse LDFLAGS := ${LD_DEBUG} -static -fPIC -msse
NEEDED_CXXFLAGS += -std=c++17 NEEDED_CXXFLAGS += -std=c++20
DEFINES += -DWIN32_LEAN_AND_MEAN DEFINES += -DWIN32_LEAN_AND_MEAN
# UPNP Support # UPNP Support
@ -16,8 +16,11 @@ ifeq ($(USE_UPNP),yes)
LDLIBS = -lminiupnpc LDLIBS = -lminiupnpc
endif endif
ifeq ($(USE_WINXP_FLAGS), yes)
DEFINES += -DWINVER=0x0501 -D_WIN32_WINNT=0x0501
endif
LDLIBS += \ LDLIBS += \
$(MINGW_PREFIX)/lib/libboost_system-mt.a \
$(MINGW_PREFIX)/lib/libboost_filesystem-mt.a \ $(MINGW_PREFIX)/lib/libboost_filesystem-mt.a \
$(MINGW_PREFIX)/lib/libboost_program_options-mt.a \ $(MINGW_PREFIX)/lib/libboost_program_options-mt.a \
$(MINGW_PREFIX)/lib/libssl.a \ $(MINGW_PREFIX)/lib/libssl.a \
@ -39,10 +42,6 @@ ifeq ($(USE_WIN32_APP), yes)
DAEMON_OBJS += $(patsubst %.rc,obj/%.o,$(DAEMON_RC)) DAEMON_OBJS += $(patsubst %.rc,obj/%.o,$(DAEMON_RC))
endif endif
ifeq ($(USE_WINXP_FLAGS), yes)
DEFINES += -DWINVER=0x0501 -D_WIN32_WINNT=0x0501
endif
ifeq ($(USE_AESNI),yes) ifeq ($(USE_AESNI),yes)
NEEDED_CXXFLAGS += -maes NEEDED_CXXFLAGS += -maes
LDFLAGS += -maes LDFLAGS += -maes

2
Makefile.osx vendored

@ -1,5 +1,5 @@
CXX = clang++ CXX = clang++
CXXFLAGS := ${CXX_DEBUG} -Wall -std=c++11 CXXFLAGS := ${CXX_DEBUG} -Wall -std=c++17
INCFLAGS = -I/usr/local/include INCFLAGS = -I/usr/local/include
DEFINES := -DMAC_OSX DEFINES := -DMAC_OSX
LDFLAGS := -Wl,-rpath,/usr/local/lib -L/usr/local/lib LDFLAGS := -Wl,-rpath,/usr/local/lib -L/usr/local/lib

4
Win32/Win32App.cpp

@ -313,7 +313,7 @@ namespace win32
} }
case ID_DATADIR: case ID_DATADIR:
{ {
std::string datadir(i2p::fs::GetUTF8DataDir()); std::string datadir(i2p::fs::GetDataDir());
ShellExecute(NULL, "explore", datadir.c_str(), NULL, NULL, SW_SHOWNORMAL); ShellExecute(NULL, "explore", datadir.c_str(), NULL, NULL, SW_SHOWNORMAL);
return 0; return 0;
} }
@ -355,9 +355,7 @@ namespace win32
} }
} }
} }
#if (__cplusplus >= 201703L) // C++ 17 or higher
[[fallthrough]]; [[fallthrough]];
#endif
} }
case WM_TRAYICON: case WM_TRAYICON:
{ {

2
Win32/Win32NetState.cpp

@ -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 * This file is part of Purple i2pd project and licensed under BSD3
* *

2
Win32/Win32NetState.h

@ -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 * This file is part of Purple i2pd project and licensed under BSD3
* *

41
build/CMakeLists.txt

@ -156,20 +156,6 @@ else()
endif() endif()
set(CMAKE_CXX_FLAGS_MINSIZEREL "${CMAKE_CXX_FLAGS_MINSIZEREL} -ffunction-sections -fdata-sections") set(CMAKE_CXX_FLAGS_MINSIZEREL "${CMAKE_CXX_FLAGS_MINSIZEREL} -ffunction-sections -fdata-sections")
set(CMAKE_EXE_LINKER_FLAGS_MINSIZEREL "-Wl,--gc-sections") # -flto is added from above set(CMAKE_EXE_LINKER_FLAGS_MINSIZEREL "-Wl,--gc-sections") # -flto is added from above
# check for c++17 & c++11 support
include(CheckCXXCompilerFlag)
CHECK_CXX_COMPILER_FLAG("-std=c++17" CXX17_SUPPORTED)
CHECK_CXX_COMPILER_FLAG("-std=c++11" CXX11_SUPPORTED)
if(CXX17_SUPPORTED)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++17")
elseif(CXX11_SUPPORTED)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
else()
message(SEND_ERROR "C++17 nor C++11 standard not seems to be supported by compiler. Too old version?")
endif()
endif() endif()
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
@ -223,6 +209,10 @@ if(WITH_THREADSANITIZER)
endif() endif()
endif() endif()
if (CMAKE_COMPILER_IS_GNUCC AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 10.0 AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 8.0) # gcc 8-9
list(APPEND CMAKE_REQUIRED_LIBRARIES "stdc++fs")
endif()
# Use std::atomic instead of GCC builtins on macOS PowerPC: # Use std::atomic instead of GCC builtins on macOS PowerPC:
# For more information refer to: https://github.com/PurpleI2P/i2pd/issues/1726#issuecomment-1306335111 # For more information refer to: https://github.com/PurpleI2P/i2pd/issues/1726#issuecomment-1306335111
# This has been fixed in Boost 1.81, nevertheless we retain the setting for the sake of compatibility. # This has been fixed in Boost 1.81, nevertheless we retain the setting for the sake of compatibility.
@ -284,7 +274,7 @@ else()
endif() endif()
endif() endif()
find_package(Boost REQUIRED COMPONENTS system filesystem program_options OPTIONAL_COMPONENTS atomic) find_package(Boost REQUIRED COMPONENTS system filesystem program_options)
if(NOT DEFINED Boost_FOUND) if(NOT DEFINED Boost_FOUND)
message(SEND_ERROR "Boost is not found, or your boost version was below 1.46. Please download Boost!") message(SEND_ERROR "Boost is not found, or your boost version was below 1.46. Please download Boost!")
endif() endif()
@ -312,6 +302,26 @@ if(ZLIB_FOUND)
link_directories(${ZLIB_ROOT}/lib) link_directories(${ZLIB_ROOT}/lib)
endif() endif()
# C++ standard to use, based on compiler and version of boost
if(NOT MSVC)
# check for c++20 & c++17 support
include(CheckCXXCompilerFlag)
if(Boost_VERSION VERSION_GREATER_EQUAL "1.83") # min boost version for c++20
CHECK_CXX_COMPILER_FLAG("-std=c++20" CXX20_SUPPORTED)
endif()
CHECK_CXX_COMPILER_FLAG("-std=c++17" CXX17_SUPPORTED)
if(CXX20_SUPPORTED)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++20")
elseif(CXX17_SUPPORTED)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++17")
else()
message(SEND_ERROR "C++20 nor C++17 standard not seems to be supported by compiler. Too old version?")
endif()
endif()
# load includes # load includes
include_directories(SYSTEM ${Boost_INCLUDE_DIRS} ${OPENSSL_INCLUDE_DIR} ${ZLIB_INCLUDE_DIR}) include_directories(SYSTEM ${Boost_INCLUDE_DIRS} ${OPENSSL_INCLUDE_DIR} ${ZLIB_INCLUDE_DIR})
@ -322,6 +332,7 @@ message(STATUS "Compiler vendor : ${CMAKE_CXX_COMPILER_ID}")
message(STATUS "Compiler version : ${CMAKE_CXX_COMPILER_VERSION}") message(STATUS "Compiler version : ${CMAKE_CXX_COMPILER_VERSION}")
message(STATUS "Compiler path : ${CMAKE_CXX_COMPILER}") message(STATUS "Compiler path : ${CMAKE_CXX_COMPILER}")
message(STATUS "Architecture : ${ARCHITECTURE}") message(STATUS "Architecture : ${ARCHITECTURE}")
message(STATUS "Compiler flags : ${CMAKE_CXX_FLAGS}")
message(STATUS "Install prefix: : ${CMAKE_INSTALL_PREFIX}") message(STATUS "Install prefix: : ${CMAKE_INSTALL_PREFIX}")
message(STATUS "Options:") message(STATUS "Options:")
message(STATUS " AESNI : ${WITH_AESNI}") message(STATUS " AESNI : ${WITH_AESNI}")

4
build/cmake_modules/CheckAtomic.cmake

@ -8,7 +8,7 @@ INCLUDE(CheckLibraryExists)
function(check_working_cxx_atomics varname) function(check_working_cxx_atomics varname)
set(OLD_CMAKE_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS}) set(OLD_CMAKE_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS})
set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -std=c++11") set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -std=c++17")
CHECK_CXX_SOURCE_COMPILES(" CHECK_CXX_SOURCE_COMPILES("
#include <atomic> #include <atomic>
std::atomic<int> x; std::atomic<int> x;
@ -25,7 +25,7 @@ endfunction(check_working_cxx_atomics)
function(check_working_cxx_atomics64 varname) function(check_working_cxx_atomics64 varname)
set(OLD_CMAKE_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS}) set(OLD_CMAKE_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS})
set(CMAKE_REQUIRED_FLAGS "-std=c++11 ${CMAKE_REQUIRED_FLAGS}") set(CMAKE_REQUIRED_FLAGS "-std=c++17 ${CMAKE_REQUIRED_FLAGS}")
CHECK_CXX_SOURCE_COMPILES(" CHECK_CXX_SOURCE_COMPILES("
#include <atomic> #include <atomic>
#include <cstdint> #include <cstdint>

7
contrib/rpm/i2pd-git.spec

@ -1,7 +1,7 @@
%define git_hash %(git rev-parse HEAD | cut -c -7) %define git_hash %(git rev-parse HEAD | cut -c -7)
Name: i2pd-git Name: i2pd-git
Version: 2.53.1 Version: 2.54.0
Release: git%{git_hash}%{?dist} Release: git%{git_hash}%{?dist}
Summary: I2P router written in C++ Summary: I2P router written in C++
Conflicts: i2pd Conflicts: i2pd
@ -24,7 +24,7 @@ BuildRequires: openssl-devel
BuildRequires: miniupnpc-devel BuildRequires: miniupnpc-devel
BuildRequires: systemd-units BuildRequires: systemd-units
%if 0%{?fedora} > 40 || 0%{?eln} %if 0%{?fedora} == 41
BuildRequires: openssl-devel-engine BuildRequires: openssl-devel-engine
%endif %endif
@ -148,6 +148,9 @@ getent passwd i2pd >/dev/null || \
%changelog %changelog
* Sun Oct 6 2024 orignal <orignal@i2pmail.org> - 2.54.0
- update to 2.54.0
* Tue Jul 30 2024 orignal <orignal@i2pmail.org> - 2.53.1 * Tue Jul 30 2024 orignal <orignal@i2pmail.org> - 2.53.1
- update to 2.53.1 - update to 2.53.1

7
contrib/rpm/i2pd.spec

@ -1,5 +1,5 @@
Name: i2pd Name: i2pd
Version: 2.53.1 Version: 2.54.0
Release: 1%{?dist} Release: 1%{?dist}
Summary: I2P router written in C++ Summary: I2P router written in C++
Conflicts: i2pd-git Conflicts: i2pd-git
@ -22,7 +22,7 @@ BuildRequires: openssl-devel
BuildRequires: miniupnpc-devel BuildRequires: miniupnpc-devel
BuildRequires: systemd-units BuildRequires: systemd-units
%if 0%{?fedora} > 40 || 0%{?eln} %if 0%{?fedora} == 41
BuildRequires: openssl-devel-engine BuildRequires: openssl-devel-engine
%endif %endif
@ -146,6 +146,9 @@ getent passwd i2pd >/dev/null || \
%changelog %changelog
* Sun Oct 6 2024 orignal <orignal@i2pmail.org> - 2.54.0
- update to 2.54.0
* Tue Jul 30 2024 orignal <orignal@i2pmail.org> - 2.53.1 * Tue Jul 30 2024 orignal <orignal@i2pmail.org> - 2.53.1
- update to 2.53.1 - update to 2.53.1

2
daemon/HTTPServer.cpp

@ -1480,7 +1480,7 @@ namespace http {
reply.body = content; reply.body = content;
m_SendBuffer = reply.to_string(); m_SendBuffer = reply.to_string();
boost::asio::async_write (*m_Socket, boost::asio::buffer(m_SendBuffer), boost::asio::async_write (*m_Socket, boost::asio::buffer(m_SendBuffer), boost::asio::transfer_all (),
std::bind (&HTTPConnection::Terminate, shared_from_this (), std::placeholders::_1)); std::bind (&HTTPConnection::Terminate, shared_from_this (), std::placeholders::_1));
} }

6
debian/changelog vendored

@ -1,3 +1,9 @@
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
i2pd (2.53.1-1) unstable; urgency=medium i2pd (2.53.1-1) unstable; urgency=medium
* updated to version 2.53.1 * updated to version 2.53.1

8
libi2pd/Config.cpp

@ -117,9 +117,14 @@ namespace config {
("httpproxy.latency.max", value<std::string>()->default_value("0"), "HTTP proxy max latency for tunnels") ("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.outproxy", value<std::string>()->default_value(""), "HTTP proxy upstream out proxy url")
("httpproxy.addresshelper", value<bool>()->default_value(true), "Enable or disable addresshelper") ("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.i2cp.leaseSetType", value<std::string>()->default_value("3"), "Local destination's LeaseSet type") ("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.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") ("httpproxy.i2cp.leaseSetPrivKey", value<std::string>()->default_value(""), "LeaseSet private key")
("httpproxy.i2p.streaming.maxOutboundSpeed", value<std::string>()->default_value("1730000000"), "Max outbound speed of HTTP proxy stream in bytes/sec")
("httpproxy.i2p.streaming.maxInboundSpeed", value<std::string>()->default_value("1730000000"), "Max inbound speed of HTTP proxy stream in bytes/sec")
("httpproxy.i2p.streaming.profile", value<std::string>()->default_value("1"), "HTTP Proxy bandwidth usage profile. 1 - bulk(high), 2- interactive(low)")
; ;
options_description socksproxy("SOCKS Proxy options"); options_description socksproxy("SOCKS Proxy options");
@ -144,6 +149,9 @@ namespace config {
("socksproxy.i2cp.leaseSetType", value<std::string>()->default_value("3"), "Local destination's LeaseSet type") ("socksproxy.i2cp.leaseSetType", value<std::string>()->default_value("3"), "Local destination's LeaseSet type")
("socksproxy.i2cp.leaseSetEncType", value<std::string>()->default_value("0,4"), "Local destination's LeaseSet encryption type") ("socksproxy.i2cp.leaseSetEncType", value<std::string>()->default_value("0,4"), "Local destination's LeaseSet encryption type")
("socksproxy.i2cp.leaseSetPrivKey", value<std::string>()->default_value(""), "LeaseSet private key") ("socksproxy.i2cp.leaseSetPrivKey", value<std::string>()->default_value(""), "LeaseSet private key")
("socksproxy.i2p.streaming.maxOutboundSpeed", value<std::string>()->default_value("1730000000"), "Max outbound speed of SOCKS proxy stream in bytes/sec")
("socksproxy.i2p.streaming.maxInboundSpeed", value<std::string>()->default_value("1730000000"), "Max inbound speed of SOCKS proxy stream in bytes/sec")
("socksproxy.i2p.streaming.profile", value<std::string>()->default_value("1"), "SOCKS Proxy bandwidth usage profile. 1 - bulk(high), 2- interactive(low)")
; ;
options_description sam("SAM bridge options"); options_description sam("SAM bridge options");

4
libi2pd/Datagram.cpp

@ -422,7 +422,7 @@ namespace datagram
{ {
auto pool = m_LocalDestination->GetTunnelPool (); auto pool = m_LocalDestination->GetTunnelPool ();
if (pool) if (pool)
idx = m_LocalDestination->GetTunnelPool ()->GetRng ()() % sz; idx = pool->GetRng ()() % sz;
} }
if (idx < 0) idx = rand () % sz; if (idx < 0) idx = rand () % sz;
path->remoteLease = ls[idx]; path->remoteLease = ls[idx];
@ -455,7 +455,7 @@ namespace datagram
{ {
auto pool = m_LocalDestination->GetTunnelPool (); auto pool = m_LocalDestination->GetTunnelPool ();
if (pool) if (pool)
idx = m_LocalDestination->GetTunnelPool ()->GetRng ()() % sz; idx = pool->GetRng ()() % sz;
} }
if (idx < 0) idx = rand () % sz; if (idx < 0) idx = rand () % sz;
path->remoteLease = ls[idx]; path->remoteLease = ls[idx];

22
libi2pd/Destination.cpp

@ -37,6 +37,7 @@ namespace client
int inVar = DEFAULT_INBOUND_TUNNELS_LENGTH_VARIANCE; int inVar = DEFAULT_INBOUND_TUNNELS_LENGTH_VARIANCE;
int outVar = DEFAULT_OUTBOUND_TUNNELS_LENGTH_VARIANCE; int outVar = DEFAULT_OUTBOUND_TUNNELS_LENGTH_VARIANCE;
int numTags = DEFAULT_TAGS_TO_SEND; int numTags = DEFAULT_TAGS_TO_SEND;
bool isHighBandwidth = true;
std::shared_ptr<std::vector<i2p::data::IdentHash> > explicitPeers; std::shared_ptr<std::vector<i2p::data::IdentHash> > explicitPeers;
try try
{ {
@ -92,7 +93,7 @@ namespace client
it = params->find (I2CP_PARAM_DONT_PUBLISH_LEASESET); it = params->find (I2CP_PARAM_DONT_PUBLISH_LEASESET);
if (it != params->end ()) if (it != params->end ())
{ {
// oveeride isPublic // override isPublic
m_IsPublic = (it->second != "true"); m_IsPublic = (it->second != "true");
} }
it = params->find (I2CP_PARAM_LEASESET_TYPE); it = params->find (I2CP_PARAM_LEASESET_TYPE);
@ -121,6 +122,9 @@ namespace client
m_LeaseSetPrivKey.reset (nullptr); m_LeaseSetPrivKey.reset (nullptr);
} }
} }
it = params->find (I2CP_PARAM_STREAMING_PROFILE);
if (it != params->end ())
isHighBandwidth = std::stoi (it->second) != STREAMING_PROFILE_INTERACTIVE;
} }
} }
catch (std::exception & ex) catch (std::exception & ex)
@ -128,7 +132,7 @@ namespace client
LogPrint(eLogError, "Destination: Unable to parse parameters for destination: ", ex.what()); LogPrint(eLogError, "Destination: Unable to parse parameters for destination: ", ex.what());
} }
SetNumTags (numTags); SetNumTags (numTags);
m_Pool = i2p::tunnel::tunnels.CreateTunnelPool (inLen, outLen, inQty, outQty, inVar, outVar); m_Pool = i2p::tunnel::tunnels.CreateTunnelPool (inLen, outLen, inQty, outQty, inVar, outVar, isHighBandwidth);
if (explicitPeers) if (explicitPeers)
m_Pool->SetExplicitPeers (explicitPeers); m_Pool->SetExplicitPeers (explicitPeers);
if(params) if(params)
@ -1013,18 +1017,15 @@ namespace client
} }
} }
// if no param or valid crypto type use from identity // if no param or valid crypto type use from identity
bool isSingleKey = false;
if (encryptionKeyTypes.empty ()) if (encryptionKeyTypes.empty ())
{ encryptionKeyTypes.insert ( { GetIdentity ()->GetCryptoKeyType (),
isSingleKey = true; i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD }); // usually 0,4
encryptionKeyTypes.insert (GetIdentity ()->GetCryptoKeyType ());
}
for (auto& it: encryptionKeyTypes) for (auto& it: encryptionKeyTypes)
{ {
auto encryptionKey = new EncryptionKey (it); auto encryptionKey = new EncryptionKey (it);
if (IsPublic ()) if (IsPublic ())
PersistTemporaryKeys (encryptionKey, isSingleKey); PersistTemporaryKeys (encryptionKey);
else else
encryptionKey->GenerateKeys (); encryptionKey->GenerateKeys ();
encryptionKey->CreateDecryptor (); encryptionKey->CreateDecryptor ();
@ -1383,12 +1384,11 @@ namespace client
return ret; return ret;
} }
void ClientDestination::PersistTemporaryKeys (EncryptionKey * keys, bool isSingleKey) void ClientDestination::PersistTemporaryKeys (EncryptionKey * keys)
{ {
if (!keys) return; if (!keys) return;
std::string ident = GetIdentHash().ToBase32(); std::string ident = GetIdentHash().ToBase32();
std::string path = i2p::fs::DataDirPath("destinations", std::string path = i2p::fs::DataDirPath("destinations", ident + "." + std::to_string (keys->keyType) + ".dat");
isSingleKey ? (ident + ".dat") : (ident + "." + std::to_string (keys->keyType) + ".dat"));
std::ifstream f(path, std::ifstream::binary); std::ifstream f(path, std::ifstream::binary);
if (f) { if (f) {

6
libi2pd/Destination.h

@ -90,6 +90,10 @@ namespace client
const int DEFAULT_MAX_INBOUND_SPEED = 1730000000; // no more than 1.73 Gbytes/s const int DEFAULT_MAX_INBOUND_SPEED = 1730000000; // no more than 1.73 Gbytes/s
const char I2CP_PARAM_STREAMING_ANSWER_PINGS[] = "i2p.streaming.answerPings"; const char I2CP_PARAM_STREAMING_ANSWER_PINGS[] = "i2p.streaming.answerPings";
const int DEFAULT_ANSWER_PINGS = true; const int DEFAULT_ANSWER_PINGS = true;
const char I2CP_PARAM_STREAMING_PROFILE[] = "i2p.streaming.profile";
const int STREAMING_PROFILE_BULK = 1; // high bandwidth
const int STREAMING_PROFILE_INTERACTIVE = 2; // low bandwidth
const int DEFAULT_STREAMING_PROFILE = STREAMING_PROFILE_BULK;
typedef std::function<void (std::shared_ptr<i2p::stream::Stream> stream)> StreamRequestComplete; typedef std::function<void (std::shared_ptr<i2p::stream::Stream> stream)> StreamRequestComplete;
@ -289,7 +293,7 @@ namespace client
std::shared_ptr<ClientDestination> GetSharedFromThis () { std::shared_ptr<ClientDestination> GetSharedFromThis () {
return std::static_pointer_cast<ClientDestination>(shared_from_this ()); return std::static_pointer_cast<ClientDestination>(shared_from_this ());
} }
void PersistTemporaryKeys (EncryptionKey * keys, bool isSingleKey); void PersistTemporaryKeys (EncryptionKey * keys);
void ReadAuthKey (const std::string& group, const std::map<std::string, std::string> * params); void ReadAuthKey (const std::string& group, const std::map<std::string, std::string> * params);
template<typename Dest> template<typename Dest>

2
libi2pd/ECIESX25519AEADRatchetSession.cpp

@ -801,9 +801,7 @@ namespace garlic
m_State = eSessionStateEstablished; m_State = eSessionStateEstablished;
m_NSRSendTagset = nullptr; m_NSRSendTagset = nullptr;
m_EphemeralKeys = nullptr; m_EphemeralKeys = nullptr;
#if (__cplusplus >= 201703L) // C++ 17 or higher
[[fallthrough]]; [[fallthrough]];
#endif
case eSessionStateEstablished: case eSessionStateEstablished:
if (m_SendReverseKey && receiveTagset->GetTagSetID () == m_NextReceiveRatchet->GetReceiveTagSetID ()) if (m_SendReverseKey && receiveTagset->GetTagSetID () == m_NextReceiveRatchet->GetReceiveTagSetID ())
m_SendReverseKey = false; // tag received on new tagset m_SendReverseKey = false; // tag received on new tagset

2
libi2pd/ECIESX25519AEADRatchetSession.h

@ -35,7 +35,7 @@ namespace garlic
const int ECIESX25519_ACK_REQUEST_MAX_NUM_ATTEMPTS = 3; const int ECIESX25519_ACK_REQUEST_MAX_NUM_ATTEMPTS = 3;
const int ECIESX25519_TAGSET_MAX_NUM_TAGS = 8192; // number of tags we request new tagset after const int ECIESX25519_TAGSET_MAX_NUM_TAGS = 8192; // number of tags we request new tagset after
const int ECIESX25519_MIN_NUM_GENERATED_TAGS = 24; const int ECIESX25519_MIN_NUM_GENERATED_TAGS = 24;
const int ECIESX25519_MAX_NUM_GENERATED_TAGS = 320; const int ECIESX25519_MAX_NUM_GENERATED_TAGS = 800;
const int ECIESX25519_NSR_NUM_GENERATED_TAGS = 12; const int ECIESX25519_NSR_NUM_GENERATED_TAGS = 12;
const size_t ECIESX25519_OPTIMAL_PAYLOAD_SIZE = 1912; // 1912 = 1956 /* to fit 2 tunnel messages */ const size_t ECIESX25519_OPTIMAL_PAYLOAD_SIZE = 1912; // 1912 = 1956 /* to fit 2 tunnel messages */

120
libi2pd/FS.cpp

@ -7,10 +7,11 @@
*/ */
#include <algorithm> #include <algorithm>
#include <boost/filesystem.hpp>
#if defined(MAC_OSX) #if defined(MAC_OSX)
#if !STD_FILESYSTEM
#include <boost/system/system_error.hpp> #include <boost/system/system_error.hpp>
#endif
#include <TargetConditionals.h> #include <TargetConditionals.h>
#endif #endif
@ -25,6 +26,14 @@
#include "Log.h" #include "Log.h"
#include "Garlic.h" #include "Garlic.h"
#if STD_FILESYSTEM
#include <filesystem>
namespace fs_lib = std::filesystem;
#else
#include <boost/filesystem.hpp>
namespace fs_lib = boost::filesystem;
#endif
namespace i2p { namespace i2p {
namespace fs { namespace fs {
std::string appName = "i2pd"; std::string appName = "i2pd";
@ -54,15 +63,17 @@ namespace fs {
const std::string GetUTF8DataDir () { const std::string GetUTF8DataDir () {
#ifdef _WIN32 #ifdef _WIN32
#if (BOOST_VERSION >= 108500) int size = MultiByteToWideChar(CP_ACP, 0,
boost::filesystem::path path (dataDir); dataDir.c_str(), dataDir.size(), nullptr, 0);
#else std::wstring utf16Str(size, L'\0');
boost::filesystem::wpath path (dataDir); MultiByteToWideChar(CP_ACP, 0,
#endif dataDir.c_str(), dataDir.size(), &utf16Str[0], size);
auto loc = boost::filesystem::path::imbue(std::locale( std::locale(), new std::codecvt_utf8_utf16<wchar_t>() ) ); // convert path to UTF-8 int utf8Size = WideCharToMultiByte(CP_UTF8, 0,
auto dataDirUTF8 = path.string(); utf16Str.c_str(), utf16Str.size(), nullptr, 0, nullptr, nullptr);
boost::filesystem::path::imbue(loc); // Return locale settings back std::string utf8Str(utf8Size, '\0');
return dataDirUTF8; WideCharToMultiByte(CP_UTF8, 0,
utf16Str.c_str(), utf16Str.size(), &utf8Str[0], utf8Size, nullptr, nullptr);
return utf8Str;
#else #else
return dataDir; // linux, osx, android uses UTF-8 by default return dataDir; // linux, osx, android uses UTF-8 by default
#endif #endif
@ -91,10 +102,10 @@ namespace fs {
} }
else else
{ {
#if (BOOST_VERSION >= 108500) #if ((BOOST_VERSION >= 108500) || STD_FILESYSTEM)
dataDir = boost::filesystem::path(commonAppData).string() + "\\" + appName; dataDir = fs_lib::path(commonAppData).string() + "\\" + appName;
#else #else
dataDir = boost::filesystem::wpath(commonAppData).string() + "\\" + appName; dataDir = fs_lib::wpath(commonAppData).string() + "\\" + appName;
#endif #endif
} }
#else #else
@ -120,14 +131,14 @@ namespace fs {
} }
else else
{ {
#if (BOOST_VERSION >= 108500) #if ((BOOST_VERSION >= 108500) || STD_FILESYSTEM)
auto execPath = boost::filesystem::path(localAppData).parent_path(); auto execPath = fs_lib::path(localAppData).parent_path();
#else #else
auto execPath = boost::filesystem::wpath(localAppData).parent_path(); auto execPath = fs_lib::wpath(localAppData).parent_path();
#endif #endif
// if config file exists in .exe's folder use it // if config file exists in .exe's folder use it
if(boost::filesystem::exists(execPath/"i2pd.conf")) // TODO: magic string if(fs_lib::exists(execPath/"i2pd.conf")) // TODO: magic string
{ {
dataDir = execPath.string (); dataDir = execPath.string ();
} else // otherwise %appdata% } else // otherwise %appdata%
@ -143,10 +154,10 @@ namespace fs {
} }
else else
{ {
#if (BOOST_VERSION >= 108500) #if ((BOOST_VERSION >= 108500) || STD_FILESYSTEM)
dataDir = boost::filesystem::path(localAppData).string() + "\\" + appName; dataDir = fs_lib::path(localAppData).string() + "\\" + appName;
#else #else
dataDir = boost::filesystem::wpath(localAppData).string() + "\\" + appName; dataDir = fs_lib::wpath(localAppData).string() + "\\" + appName;
#endif #endif
} }
} }
@ -169,7 +180,7 @@ namespace fs {
#if defined(ANDROID) #if defined(ANDROID)
const char * ext = getenv("EXTERNAL_STORAGE"); const char * ext = getenv("EXTERNAL_STORAGE");
if (!ext) ext = "/sdcard"; if (!ext) ext = "/sdcard";
if (boost::filesystem::exists(ext)) if (fs_lib::exists(ext))
{ {
dataDir = std::string (ext) + "/" + appName; dataDir = std::string (ext) + "/" + appName;
return; return;
@ -202,16 +213,16 @@ namespace fs {
} }
bool Init() { bool Init() {
if (!boost::filesystem::exists(dataDir)) if (!fs_lib::exists(dataDir))
boost::filesystem::create_directory(dataDir); fs_lib::create_directory(dataDir);
std::string destinations = DataDirPath("destinations"); std::string destinations = DataDirPath("destinations");
if (!boost::filesystem::exists(destinations)) if (!fs_lib::exists(destinations))
boost::filesystem::create_directory(destinations); fs_lib::create_directory(destinations);
std::string tags = DataDirPath("tags"); std::string tags = DataDirPath("tags");
if (!boost::filesystem::exists(tags)) if (!fs_lib::exists(tags))
boost::filesystem::create_directory(tags); fs_lib::create_directory(tags);
else else
i2p::garlic::CleanUpTagsFiles (); i2p::garlic::CleanUpTagsFiles ();
@ -219,13 +230,13 @@ namespace fs {
} }
bool ReadDir(const std::string & path, std::vector<std::string> & files) { bool ReadDir(const std::string & path, std::vector<std::string> & files) {
if (!boost::filesystem::exists(path)) if (!fs_lib::exists(path))
return false; return false;
boost::filesystem::directory_iterator it(path); fs_lib::directory_iterator it(path);
boost::filesystem::directory_iterator end; fs_lib::directory_iterator end;
for ( ; it != end; it++) { for ( ; it != end; it++) {
if (!boost::filesystem::is_regular_file(it->status())) if (!fs_lib::is_regular_file(it->status()))
continue; continue;
files.push_back(it->path().string()); files.push_back(it->path().string());
} }
@ -234,29 +245,42 @@ namespace fs {
} }
bool Exists(const std::string & path) { bool Exists(const std::string & path) {
return boost::filesystem::exists(path); return fs_lib::exists(path);
} }
uint32_t GetLastUpdateTime (const std::string & path) uint32_t GetLastUpdateTime (const std::string & path)
{ {
if (!boost::filesystem::exists(path)) if (!fs_lib::exists(path))
return 0; return 0;
#if STD_FILESYSTEM
std::error_code ec;
auto t = std::filesystem::last_write_time (path, ec);
if (ec) return 0;
/*#if __cplusplus >= 202002L // C++ 20 or higher
const auto sctp = std::chrono::clock_cast<std::chrono::system_clock>(t);
#else */ // TODO: wait until implemented
const auto sctp = std::chrono::time_point_cast<std::chrono::system_clock::duration>(
t - decltype(t)::clock::now() + std::chrono::system_clock::now());
/*#endif */
return std::chrono::system_clock::to_time_t(sctp);
#else
boost::system::error_code ec; boost::system::error_code ec;
auto t = boost::filesystem::last_write_time (path, ec); auto t = boost::filesystem::last_write_time (path, ec);
return ec ? 0 : t; return ec ? 0 : t;
#endif
} }
bool Remove(const std::string & path) { bool Remove(const std::string & path) {
if (!boost::filesystem::exists(path)) if (!fs_lib::exists(path))
return false; return false;
return boost::filesystem::remove(path); return fs_lib::remove(path);
} }
bool CreateDirectory (const std::string& path) bool CreateDirectory (const std::string& path)
{ {
if (boost::filesystem::exists(path) && boost::filesystem::is_directory (boost::filesystem::status (path))) if (fs_lib::exists(path) && fs_lib::is_directory (fs_lib::status (path)))
return true; return true;
return boost::filesystem::create_directory(path); return fs_lib::create_directory(path);
} }
void HashedStorage::SetPlace(const std::string &path) { void HashedStorage::SetPlace(const std::string &path) {
@ -264,18 +288,18 @@ namespace fs {
} }
bool HashedStorage::Init(const char * chars, size_t count) { bool HashedStorage::Init(const char * chars, size_t count) {
if (!boost::filesystem::exists(root)) { if (!fs_lib::exists(root)) {
boost::filesystem::create_directories(root); fs_lib::create_directories(root);
} }
for (size_t i = 0; i < count; i++) { for (size_t i = 0; i < count; i++) {
auto p = root + i2p::fs::dirSep + prefix1 + chars[i]; auto p = root + i2p::fs::dirSep + prefix1 + chars[i];
if (boost::filesystem::exists(p)) if (fs_lib::exists(p))
continue; continue;
#if TARGET_OS_SIMULATOR #if TARGET_OS_SIMULATOR
// ios simulator fs says it is case sensitive, but it is not // ios simulator fs says it is case sensitive, but it is not
boost::system::error_code ec; boost::system::error_code ec;
if (boost::filesystem::create_directory(p, ec)) if (fs_lib::create_directory(p, ec))
continue; continue;
switch (ec.value()) { switch (ec.value()) {
case boost::system::errc::file_exists: case boost::system::errc::file_exists:
@ -285,7 +309,7 @@ namespace fs {
throw boost::system::system_error( ec, __func__ ); throw boost::system::system_error( ec, __func__ );
} }
#else #else
if (boost::filesystem::create_directory(p)) if (fs_lib::create_directory(p))
continue; /* ^ throws exception on failure */ continue; /* ^ throws exception on failure */
#endif #endif
return false; return false;
@ -308,9 +332,9 @@ namespace fs {
void HashedStorage::Remove(const std::string & ident) { void HashedStorage::Remove(const std::string & ident) {
std::string path = Path(ident); std::string path = Path(ident);
if (!boost::filesystem::exists(path)) if (!fs_lib::exists(path))
return; return;
boost::filesystem::remove(path); fs_lib::remove(path);
} }
void HashedStorage::Traverse(std::vector<std::string> & files) { void HashedStorage::Traverse(std::vector<std::string> & files) {
@ -321,12 +345,12 @@ namespace fs {
void HashedStorage::Iterate(FilenameVisitor v) void HashedStorage::Iterate(FilenameVisitor v)
{ {
boost::filesystem::path p(root); fs_lib::path p(root);
boost::filesystem::recursive_directory_iterator it(p); fs_lib::recursive_directory_iterator it(p);
boost::filesystem::recursive_directory_iterator end; fs_lib::recursive_directory_iterator end;
for ( ; it != end; it++) { for ( ; it != end; it++) {
if (!boost::filesystem::is_regular_file( it->status() )) if (!fs_lib::is_regular_file( it->status() ))
continue; continue;
const std::string & t = it->path().string(); const std::string & t = it->path().string();
v(t); v(t);

12
libi2pd/FS.h

@ -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 * This file is part of Purple i2pd project and licensed under BSD3
* *
@ -15,6 +15,16 @@
#include <sstream> #include <sstream>
#include <functional> #include <functional>
#ifndef STD_FILESYSTEM
# if (_WIN32 && __GNUG__) // MinGW GCC somehow incorrectly converts paths
# define STD_FILESYSTEM 0
# elif (!TARGET_OS_SIMULATOR && __has_include(<filesystem>)) // supports std::filesystem
# define STD_FILESYSTEM 1
# else
# define STD_FILESYSTEM 0
# endif
#endif
namespace i2p { namespace i2p {
namespace fs { namespace fs {
extern std::string dirSep; extern std::string dirSep;

37
libi2pd/Garlic.cpp

@ -541,34 +541,7 @@ namespace garlic
// otherwise ECIESx25519 // otherwise ECIESx25519
auto session = std::make_shared<ECIESX25519AEADRatchetSession> (this, false); // incoming auto session = std::make_shared<ECIESX25519AEADRatchetSession> (this, false); // incoming
if (!session->HandleNextMessage (buf, length, nullptr, 0)) if (!session->HandleNextMessage (buf, length, nullptr, 0))
{ LogPrint (eLogError, "Garlic: Can't handle ECIES-X25519-AEAD-Ratchet message");
// try to generate more tags for last tagset
if (m_LastTagset && (m_LastTagset->GetNextIndex () - m_LastTagset->GetTrimBehind () < 3*ECIESX25519_MAX_NUM_GENERATED_TAGS))
{
uint64_t missingTag; memcpy (&missingTag, buf, 8);
auto maxTags = std::max (m_NumRatchetInboundTags, ECIESX25519_MAX_NUM_GENERATED_TAGS);
LogPrint (eLogWarning, "Garlic: Trying to generate more ECIES-X25519-AEAD-Ratchet tags");
for (int i = 0; i < maxTags; i++)
{
auto nextTag = AddECIESx25519SessionNextTag (m_LastTagset);
if (!nextTag)
{
LogPrint (eLogError, "Garlic: Can't create new ECIES-X25519-AEAD-Ratchet tag for last tagset");
break;
}
if (nextTag == missingTag)
{
LogPrint (eLogDebug, "Garlic: Missing ECIES-X25519-AEAD-Ratchet tag was generated");
if (m_LastTagset->HandleNextMessage (buf, length, m_ECIESx25519Tags[nextTag].index))
found = true;
break;
}
}
if (!found) m_LastTagset = nullptr;
}
if (!found)
LogPrint (eLogError, "Garlic: Can't handle ECIES-X25519-AEAD-Ratchet message");
}
} }
else else
LogPrint (eLogError, "Garlic: Failed to decrypt message"); LogPrint (eLogError, "Garlic: Failed to decrypt message");
@ -583,9 +556,7 @@ namespace garlic
auto it = m_ECIESx25519Tags.find (tag); auto it = m_ECIESx25519Tags.find (tag);
if (it != m_ECIESx25519Tags.end ()) if (it != m_ECIESx25519Tags.end ())
{ {
if (it->second.tagset && it->second.tagset->HandleNextMessage (buf, len, it->second.index)) if (!it->second.tagset || !it->second.tagset->HandleNextMessage (buf, len, it->second.index))
m_LastTagset = it->second.tagset;
else
LogPrint (eLogError, "Garlic: Can't handle ECIES-X25519-AEAD-Ratchet message"); LogPrint (eLogError, "Garlic: Can't handle ECIES-X25519-AEAD-Ratchet message");
m_ECIESx25519Tags.erase (it); m_ECIESx25519Tags.erase (it);
return true; return true;
@ -893,8 +864,6 @@ namespace garlic
} }
if (numExpiredTags > 0) if (numExpiredTags > 0)
LogPrint (eLogDebug, "Garlic: ", numExpiredTags, " ECIESx25519 tags expired for ", GetIdentHash().ToBase64 ()); LogPrint (eLogDebug, "Garlic: ", numExpiredTags, " ECIESx25519 tags expired for ", GetIdentHash().ToBase64 ());
if (m_LastTagset && m_LastTagset->IsExpired (ts))
m_LastTagset = nullptr;
} }
void GarlicDestination::RemoveDeliveryStatusSession (uint32_t msgID) void GarlicDestination::RemoveDeliveryStatusSession (uint32_t msgID)
@ -1031,9 +1000,7 @@ namespace garlic
case eGarlicDeliveryTypeDestination: case eGarlicDeliveryTypeDestination:
LogPrint (eLogDebug, "Garlic: Type destination"); LogPrint (eLogDebug, "Garlic: Type destination");
buf += 32; // TODO: check destination buf += 32; // TODO: check destination
#if (__cplusplus >= 201703L) // C++ 17 or higher
[[fallthrough]]; [[fallthrough]];
#endif
// no break here // no break here
case eGarlicDeliveryTypeLocal: case eGarlicDeliveryTypeLocal:
{ {

1
libi2pd/Garlic.h

@ -288,7 +288,6 @@ namespace garlic
int m_NumRatchetInboundTags; int m_NumRatchetInboundTags;
std::unordered_map<SessionTag, std::shared_ptr<AESDecryption>, std::hash<i2p::data::Tag<32> > > m_Tags; std::unordered_map<SessionTag, std::shared_ptr<AESDecryption>, std::hash<i2p::data::Tag<32> > > m_Tags;
std::unordered_map<uint64_t, ECIESX25519AEADRatchetIndexTagset> m_ECIESx25519Tags; // session tag -> session std::unordered_map<uint64_t, ECIESX25519AEADRatchetIndexTagset> m_ECIESx25519Tags; // session tag -> session
ReceiveRatchetTagSetPtr m_LastTagset; // tagset last message came for
// DeliveryStatus // DeliveryStatus
std::mutex m_DeliveryStatusSessionsMutex; std::mutex m_DeliveryStatusSessionsMutex;
std::unordered_map<uint32_t, GarlicRoutingSessionPtr> m_DeliveryStatusSessions; // msgID -> session std::unordered_map<uint32_t, GarlicRoutingSessionPtr> m_DeliveryStatusSessions; // msgID -> session

95
libi2pd/HTTP.cpp

@ -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 * This file is part of Purple i2pd project and licensed under BSD3
* *
@ -40,12 +40,13 @@ namespace http
inline bool is_http_method(const std::string & str) { inline bool is_http_method(const std::string & str) {
return std::find(HTTP_METHODS.begin(), HTTP_METHODS.end(), str) != std::end(HTTP_METHODS); return std::find(HTTP_METHODS.begin(), HTTP_METHODS.end(), str) != std::end(HTTP_METHODS);
} }
void strsplit(const std::string & line, std::vector<std::string> &tokens, char delim, std::size_t limit = 0) { static void strsplit(std::stringstream& ss, std::vector<std::string> &tokens, char delim, std::size_t limit = 0)
{
std::size_t count = 0; std::size_t count = 0;
std::stringstream ss(line);
std::string token; std::string token;
while (1) { while (1)
{
count++; count++;
if (limit > 0 && count >= limit) if (limit > 0 && count >= limit)
delim = '\n'; /* reset delimiter */ delim = '\n'; /* reset delimiter */
@ -55,21 +56,33 @@ namespace http
} }
} }
static std::pair<std::string, std::string> parse_header_line(const std::string& line) static void strsplit(const std::string & line, std::vector<std::string> &tokens, char delim, std::size_t limit = 0)
{
std::stringstream ss{line};
strsplit (ss, tokens, delim, limit);
}
static void strsplit(std::string_view line, std::vector<std::string> &tokens, char delim, std::size_t limit = 0)
{
std::stringstream ss{std::string(line)};
strsplit (ss, tokens, delim, limit);
}
static std::pair<std::string, std::string> parse_header_line(std::string_view line)
{ {
std::size_t pos = 0; std::size_t pos = 0;
std::size_t len = 1; /*: */ std::size_t len = 1; /*: */
std::size_t max = line.length(); std::size_t max = line.length();
if ((pos = line.find(':', pos)) == std::string::npos) if ((pos = line.find(':', pos)) == std::string::npos)
return std::make_pair("", ""); // no ':' found return std::pair{"", ""}; // no ':' found
if (pos + 1 < max) // ':' at the end of header is valid if (pos + 1 < max) // ':' at the end of header is valid
{ {
while ((pos + len) < max && isspace(line.at(pos + len))) while ((pos + len) < max && isspace(line.at(pos + len)))
len++; len++;
if (len == 1) if (len == 1)
return std::make_pair("", ""); // no following space, but something else return std::pair{"", ""}; // no following space, but something else
} }
return std::make_pair(line.substr(0, pos), line.substr(pos + len)); return std::pair{std::string (line.substr(0, pos)), std::string (line.substr(pos + len))};
} }
void gen_rfc7231_date(std::string & out) { void gen_rfc7231_date(std::string & out) {
@ -83,15 +96,17 @@ namespace http
out = buf; out = buf;
} }
bool URL::parse(const char *str, std::size_t len) { bool URL::parse(const char *str, std::size_t len)
std::string url(str, len ? len : strlen(str)); {
return parse(url); return parse({str, len ? len : strlen(str)});
} }
bool URL::parse(const std::string& url) { bool URL::parse(std::string_view url)
{
std::size_t pos_p = 0; /* < current parse position */ std::size_t pos_p = 0; /* < current parse position */
std::size_t pos_c = 0; /* < work position */ std::size_t pos_c = 0; /* < work position */
if(url.at(0) != '/' || pos_p > 0) { if(url.at(0) != '/' || pos_p > 0)
{
std::size_t pos_s = 0; std::size_t pos_s = 0;
/* schema */ /* schema */
@ -141,7 +156,7 @@ namespace http
/* port[/path] */ /* port[/path] */
pos_p = pos_c + 1; pos_p = pos_c + 1;
pos_c = url.find('/', pos_p); pos_c = url.find('/', pos_p);
std::string port_str = (pos_c == std::string::npos) std::string_view port_str = (pos_c == std::string::npos)
? url.substr(pos_p, std::string::npos) ? url.substr(pos_p, std::string::npos)
: url.substr(pos_p, pos_c - pos_p); : url.substr(pos_p, pos_c - pos_p);
/* stoi throws exception on failure, we don't need it */ /* stoi throws exception on failure, we don't need it */
@ -253,7 +268,7 @@ namespace http
return host.rfind(".i2p") == ( host.size() - 4 ); return host.rfind(".i2p") == ( host.size() - 4 );
} }
void HTTPMsg::add_header(const char *name, std::string & value, bool replace) { void HTTPMsg::add_header(const char *name, const std::string & value, bool replace) {
add_header(name, value.c_str(), replace); add_header(name, value.c_str(), replace);
} }
@ -272,12 +287,13 @@ namespace http
headers.erase(name); headers.erase(name);
} }
int HTTPReq::parse(const char *buf, size_t len) { int HTTPReq::parse(const char *buf, size_t len)
std::string str(buf, len); {
return parse(str); return parse({buf, len});
} }
int HTTPReq::parse(const std::string& str) { int HTTPReq::parse(std::string_view str)
{
enum { REQ_LINE, HEADER_LINE } expect = REQ_LINE; enum { REQ_LINE, HEADER_LINE } expect = REQ_LINE;
std::size_t eoh = str.find(HTTP_EOH); /* request head size */ std::size_t eoh = str.find(HTTP_EOH); /* request head size */
std::size_t eol = 0, pos = 0; std::size_t eol = 0, pos = 0;
@ -286,9 +302,11 @@ namespace http
if (eoh == std::string::npos) if (eoh == std::string::npos)
return 0; /* str not contains complete request */ return 0; /* str not contains complete request */
while ((eol = str.find(CRLF, pos)) != std::string::npos) { while ((eol = str.find(CRLF, pos)) != std::string::npos)
if (expect == REQ_LINE) { {
std::string line = str.substr(pos, eol - pos); if (expect == REQ_LINE)
{
std::string_view line = str.substr(pos, eol - pos);
std::vector<std::string> tokens; std::vector<std::string> tokens;
strsplit(line, tokens, ' '); strsplit(line, tokens, ' ');
if (tokens.size() != 3) if (tokens.size() != 3)
@ -307,7 +325,7 @@ namespace http
} }
else else
{ {
std::string line = str.substr(pos, eol - pos); std::string_view line = str.substr(pos, eol - pos);
auto p = parse_header_line(line); auto p = parse_header_line(line);
if (p.first.length () > 0) if (p.first.length () > 0)
headers.push_back (p); headers.push_back (p);
@ -413,12 +431,13 @@ namespace http
return length; return length;
} }
int HTTPRes::parse(const char *buf, size_t len) { int HTTPRes::parse(const char *buf, size_t len)
std::string str(buf, len); {
return parse(str); return parse({buf,len});
} }
int HTTPRes::parse(const std::string& str) { int HTTPRes::parse(std::string_view str)
{
enum { RES_LINE, HEADER_LINE } expect = RES_LINE; enum { RES_LINE, HEADER_LINE } expect = RES_LINE;
std::size_t eoh = str.find(HTTP_EOH); /* request head size */ std::size_t eoh = str.find(HTTP_EOH); /* request head size */
std::size_t eol = 0, pos = 0; std::size_t eol = 0, pos = 0;
@ -426,9 +445,11 @@ namespace http
if (eoh == std::string::npos) if (eoh == std::string::npos)
return 0; /* str not contains complete request */ return 0; /* str not contains complete request */
while ((eol = str.find(CRLF, pos)) != std::string::npos) { while ((eol = str.find(CRLF, pos)) != std::string::npos)
if (expect == RES_LINE) { {
std::string line = str.substr(pos, eol - pos); if (expect == RES_LINE)
{
std::string_view line = str.substr(pos, eol - pos);
std::vector<std::string> tokens; std::vector<std::string> tokens;
strsplit(line, tokens, ' ', 3); strsplit(line, tokens, ' ', 3);
if (tokens.size() != 3) if (tokens.size() != 3)
@ -442,8 +463,10 @@ namespace http
version = tokens[0]; version = tokens[0];
status = tokens[2]; status = tokens[2];
expect = HEADER_LINE; expect = HEADER_LINE;
} else { }
std::string line = str.substr(pos, eol - pos); else
{
std::string_view line = str.substr(pos, eol - pos);
auto p = parse_header_line(line); auto p = parse_header_line(line);
if (p.first.length () > 0) if (p.first.length () > 0)
headers.insert (p); headers.insert (p);
@ -508,14 +531,14 @@ namespace http
return ptr; return ptr;
} }
std::string UrlDecode(const std::string& data, bool allow_null) std::string UrlDecode(std::string_view data, bool allow_null)
{ {
std::string decoded(data); std::string decoded(data);
size_t pos = 0; size_t pos = 0;
while ((pos = decoded.find('%', pos)) != std::string::npos) while ((pos = decoded.find('%', pos)) != std::string::npos)
{ {
char c = strtol(decoded.substr(pos + 1, 2).c_str(), NULL, 16); char c = std::stol(decoded.substr(pos + 1, 2), nullptr, 16);
if (c == '\0' && !allow_null) if (!c && !allow_null)
{ {
pos += 3; pos += 3;
continue; continue;

13
libi2pd/HTTP.h

@ -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 * This file is part of Purple i2pd project and licensed under BSD3
* *
@ -14,6 +14,7 @@
#include <list> #include <list>
#include <sstream> #include <sstream>
#include <string> #include <string>
#include <string_view>
#include <vector> #include <vector>
namespace i2p namespace i2p
@ -45,7 +46,7 @@ namespace http
* @return true on success, false on invalid url * @return true on success, false on invalid url
*/ */
bool parse (const char *str, std::size_t len = 0); bool parse (const char *str, std::size_t len = 0);
bool parse (const std::string& url); bool parse (std::string_view url);
/** /**
* @brief Parse query part of url to key/value map * @brief Parse query part of url to key/value map
@ -69,7 +70,7 @@ namespace http
{ {
std::map<std::string, std::string> headers; std::map<std::string, std::string> headers;
void add_header(const char *name, std::string & value, bool replace = false); void add_header(const char *name, const std::string & value, bool replace = false);
void add_header(const char *name, const char *value, bool replace = false); void add_header(const char *name, const char *value, bool replace = false);
void del_header(const char *name); void del_header(const char *name);
@ -92,7 +93,7 @@ namespace http
* @note Positive return value is a size of header * @note Positive return value is a size of header
*/ */
int parse(const char *buf, size_t len); int parse(const char *buf, size_t len);
int parse(const std::string& buf); int parse(std::string_view buf);
/** @brief Serialize HTTP request to string */ /** @brief Serialize HTTP request to string */
std::string to_string(); std::string to_string();
@ -128,7 +129,7 @@ namespace http
* @note Positive return value is a size of header * @note Positive return value is a size of header
*/ */
int parse(const char *buf, size_t len); int parse(const char *buf, size_t len);
int parse(const std::string& buf); int parse(const std::string_view buf);
/** /**
* @brief Serialize HTTP response to string * @brief Serialize HTTP response to string
@ -161,7 +162,7 @@ namespace http
* @param null If set to true - decode also %00 sequence, otherwise - skip * @param null If set to true - decode also %00 sequence, otherwise - skip
* @return Decoded string * @return Decoded string
*/ */
std::string UrlDecode(const std::string& data, bool null = false); std::string UrlDecode(std::string_view data, bool null = false);
/** /**
* @brief Merge HTTP response content with Transfer-Encoding: chunked * @brief Merge HTTP response content with Transfer-Encoding: chunked

1
libi2pd/I2NPProtocol.h

@ -107,7 +107,6 @@ namespace i2p
enum I2NPMessageType enum I2NPMessageType
{ {
eI2NPDummyMsg = 0,
eI2NPDatabaseStore = 1, eI2NPDatabaseStore = 1,
eI2NPDatabaseLookup = 2, eI2NPDatabaseLookup = 2,
eI2NPDatabaseSearchReply = 3, eI2NPDatabaseSearchReply = 3,

20
libi2pd/Log.h

@ -172,16 +172,6 @@ void LogPrint (std::stringstream& s, TValue&& arg) noexcept
s << std::forward<TValue>(arg); s << std::forward<TValue>(arg);
} }
#if (__cplusplus < 201703L) // below C++ 17
/** internal usage only -- folding args array to single string */
template<typename TValue, typename... TArgs>
void LogPrint (std::stringstream& s, TValue&& arg, TArgs&&... args) noexcept
{
LogPrint (s, std::forward<TValue>(arg));
LogPrint (s, std::forward<TArgs>(args)...);
}
#endif
/** /**
* @brief Create log message and send it to queue * @brief Create log message and send it to queue
* @param level Message level (eLogError, eLogInfo, ...) * @param level Message level (eLogError, eLogInfo, ...)
@ -194,13 +184,7 @@ void LogPrint (LogLevel level, TArgs&&... args) noexcept
// fold message to single string // fold message to single string
std::stringstream ss; std::stringstream ss;
#if (__cplusplus >= 201703L) // C++ 17 or higher
(LogPrint (ss, std::forward<TArgs>(args)), ...); (LogPrint (ss, std::forward<TArgs>(args)), ...);
#else
LogPrint (ss, std::forward<TArgs>(args)...);
#endif
auto msg = std::make_shared<i2p::log::LogMsg>(level, std::time(nullptr), std::move(ss).str()); auto msg = std::make_shared<i2p::log::LogMsg>(level, std::time(nullptr), std::move(ss).str());
msg->tid = std::this_thread::get_id(); msg->tid = std::this_thread::get_id();
i2p::log::Logger().Append(msg); i2p::log::Logger().Append(msg);
@ -217,11 +201,7 @@ void ThrowFatal (TArgs&&... args) noexcept
if (!f) return; if (!f) return;
// fold message to single string // fold message to single string
std::stringstream ss(""); std::stringstream ss("");
#if (__cplusplus >= 201703L) // C++ 17 or higher
(LogPrint (ss, std::forward<TArgs>(args)), ...); (LogPrint (ss, std::forward<TArgs>(args)), ...);
#else
LogPrint (ss, std::forward<TArgs>(args)...);
#endif
f (ss.str ()); f (ss.str ());
} }

53
libi2pd/NTCP2.cpp

@ -695,7 +695,7 @@ namespace transport
return; return;
} }
auto size = bufbe16toh (buf.data () + 1); auto size = bufbe16toh (buf.data () + 1);
if (size > buf.size () - 3) if (size > buf.size () - 3 || size > i2p::data::MAX_RI_BUFFER_SIZE + 1)
{ {
LogPrint (eLogError, "NTCP2: Unexpected RouterInfo size ", size, " in SessionConfirmed"); LogPrint (eLogError, "NTCP2: Unexpected RouterInfo size ", size, " in SessionConfirmed");
Terminate (); Terminate ();
@ -724,8 +724,28 @@ namespace transport
SendTerminationAndTerminate (eNTCP2Message3Error); SendTerminationAndTerminate (eNTCP2Message3Error);
return; return;
} }
auto addr = m_RemoteEndpoint.address ().is_v4 () ? ri.GetNTCP2V4Address () : // update RouterInfo in netdb
(i2p::util::net::IsYggdrasilAddress (m_RemoteEndpoint.address ()) ? ri.GetYggdrasilAddress () : ri.GetNTCP2V6Address ()); 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)) if (!addr || memcmp (m_Establisher->m_RemoteStaticKey, addr->s, 32))
{ {
LogPrint (eLogError, "NTCP2: Wrong static key in SessionConfirmed"); LogPrint (eLogError, "NTCP2: Wrong static key in SessionConfirmed");
@ -737,16 +757,17 @@ namespace transport
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 () + 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 memcmp (m_RemoteEndpoint.address ().to_v6 ().to_bytes ().data (), addr->host.to_v6 ().to_bytes ().data (), 8)))) // temporary address
{ {
LogPrint (eLogError, "NTCP2: Host mismatch between published address ", addr->host, " and actual endpoint ", m_RemoteEndpoint.address ()); if (profile) // older router?
Terminate (); 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; return;
} }
i2p::data::netdb.PostI2NPMsg (CreateI2NPMessage (eI2NPDummyMsg, buf.data () + 3, size)); // TODO: should insert ri and not parse it twice
// TODO: process options // TODO: process options
// ready to communicate // ready to communicate
auto existing = i2p::data::netdb.FindRouter (ri.GetRouterIdentity ()->GetIdentHash ()); // check if exists already SetRemoteIdentity (ri1->GetRouterIdentity ());
SetRemoteIdentity (existing ? existing->GetRouterIdentity () : ri.GetRouterIdentity ());
if (m_Server.AddNTCP2Session (shared_from_this (), true)) if (m_Server.AddNTCP2Session (shared_from_this (), true))
{ {
Established (); Established ();
@ -938,8 +959,20 @@ namespace transport
break; break;
case eNTCP2BlkRouterInfo: case eNTCP2BlkRouterInfo:
{ {
LogPrint (eLogDebug, "NTCP2: RouterInfo flag=", (int)frame[offset]); LogPrint (eLogDebug, "NTCP2: RouterInfo flag=", (int)frame[offset]);
i2p::data::netdb.PostI2NPMsg (CreateI2NPMessage (eI2NPDummyMsg, frame + offset, size)); if (size <= i2p::data::MAX_RI_BUFFER_SIZE + 1)
{
auto newRi = i2p::data::netdb.AddRouterInfo (frame + offset + 1, size - 1);
if (newRi)
{
auto remoteIdentity = GetRemoteIdentity ();
if (remoteIdentity && remoteIdentity->GetIdentHash () == newRi->GetIdentHash ())
// peer's RouterInfo update
SetRemoteIdentity (newRi->GetIdentity ());
}
}
else
LogPrint (eLogInfo, "NTCP2: RouterInfo block is too long ", size);
break; break;
} }
case eNTCP2BlkI2NPMessage: case eNTCP2BlkI2NPMessage:

38
libi2pd/NetDb.cpp

@ -69,7 +69,7 @@ namespace data
{ {
Reseed (); Reseed ();
} }
else if (!GetRandomRouter (i2p::context.GetSharedRouterInfo (), false, false)) else if (!GetRandomRouter (i2p::context.GetSharedRouterInfo (), false, false, false))
Reseed (); // we don't have a router we can connect to. Trying to reseed Reseed (); // we don't have a router we can connect to. Trying to reseed
auto it = m_RouterInfos.find (i2p::context.GetIdentHash ()); auto it = m_RouterInfos.find (i2p::context.GetIdentHash ());
@ -141,10 +141,6 @@ namespace data
case eI2NPDatabaseLookup: case eI2NPDatabaseLookup:
HandleDatabaseLookupMsg (msg); HandleDatabaseLookupMsg (msg);
break; break;
case eI2NPDummyMsg:
// plain RouterInfo from NTCP2 with flags for now
HandleNTCP2RouterInfoMsg (msg);
break;
default: // WTF? default: // WTF?
LogPrint (eLogError, "NetDb: Unexpected message type ", (int) msg->GetTypeID ()); LogPrint (eLogError, "NetDb: Unexpected message type ", (int) msg->GetTypeID ());
//i2p::HandleI2NPMessage (msg); //i2p::HandleI2NPMessage (msg);
@ -299,7 +295,8 @@ namespace data
{ {
auto mts = i2p::util::GetMillisecondsSinceEpoch (); auto mts = i2p::util::GetMillisecondsSinceEpoch ();
isValid = mts + NETDB_EXPIRATION_TIMEOUT_THRESHOLD*1000LL > r->GetTimestamp () && // from future isValid = mts + NETDB_EXPIRATION_TIMEOUT_THRESHOLD*1000LL > r->GetTimestamp () && // from future
mts < r->GetTimestamp () + NETDB_MAX_EXPIRATION_TIMEOUT*1000LL; // too old (mts < r->GetTimestamp () + NETDB_MAX_EXPIRATION_TIMEOUT*1000LL || // too old
context.GetUptime () < NETDB_CHECK_FOR_EXPIRATION_UPTIME/10); // enough uptime
} }
if (isValid) if (isValid)
{ {
@ -763,7 +760,8 @@ namespace data
void NetDb::RequestDestination (const IdentHash& destination, RequestedDestination::RequestComplete requestComplete, bool direct) void NetDb::RequestDestination (const IdentHash& destination, RequestedDestination::RequestComplete requestComplete, bool direct)
{ {
if (direct && i2p::transport::transports.RoutesRestricted ()) direct = false; // always use tunnels for restricted routes if (direct && (i2p::transport::transports.RoutesRestricted () || i2p::context.IsLimitedConnectivity ()))
direct = false; // always use tunnels for restricted routes or limited connectivity
if (m_Requests) if (m_Requests)
m_Requests->PostRequestDestination (destination, requestComplete, direct); m_Requests->PostRequestDestination (destination, requestComplete, direct);
else else
@ -1133,15 +1131,18 @@ namespace data
} }
std::shared_ptr<const RouterInfo> NetDb::GetRandomRouter (std::shared_ptr<const RouterInfo> compatibleWith, std::shared_ptr<const RouterInfo> NetDb::GetRandomRouter (std::shared_ptr<const RouterInfo> compatibleWith,
bool reverse, bool endpoint) const bool reverse, bool endpoint, bool clientTunnel) const
{ {
bool checkIsReal = clientTunnel && i2p::tunnel::tunnels.GetPreciseTunnelCreationSuccessRate () < NETDB_TUNNEL_CREATION_RATE_THRESHOLD && // too low rate
context.GetUptime () > NETDB_CHECK_FOR_EXPIRATION_UPTIME; // after 10 minutes uptime
return GetRandomRouter ( return GetRandomRouter (
[compatibleWith, reverse, endpoint](std::shared_ptr<const RouterInfo> router)->bool [compatibleWith, reverse, endpoint, clientTunnel, checkIsReal](std::shared_ptr<const RouterInfo> router)->bool
{ {
return !router->IsHidden () && router != compatibleWith && return !router->IsHidden () && router != compatibleWith &&
(reverse ? (compatibleWith->IsReachableFrom (*router) && router->GetCompatibleTransports (true)): (reverse ? (compatibleWith->IsReachableFrom (*router) && router->GetCompatibleTransports (true)):
router->IsReachableFrom (*compatibleWith)) && !router->IsNAT2NATOnly (*compatibleWith) && router->IsReachableFrom (*compatibleWith)) && !router->IsNAT2NATOnly (*compatibleWith) &&
router->IsECIES () && !router->IsHighCongestion (false) && router->IsECIES () && !router->IsHighCongestion (clientTunnel) &&
(!checkIsReal || router->GetProfile ()->IsReal ()) &&
(!endpoint || (router->IsV4 () && (!reverse || router->IsPublished (true)))); // endpoint must be ipv4 and published if inbound(reverse) (!endpoint || (router->IsV4 () && (!reverse || router->IsPublished (true)))); // endpoint must be ipv4 and published if inbound(reverse)
}); });
} }
@ -1314,12 +1315,8 @@ namespace data
{ {
// update selection // update selection
m_ExploratorySelection.clear (); m_ExploratorySelection.clear ();
#if (__cplusplus >= 201703L) // C++ 17 or higher
std::vector<std::shared_ptr<const RouterInfo> > eligible; std::vector<std::shared_ptr<const RouterInfo> > eligible;
eligible.reserve (m_RouterInfos.size ()); eligible.reserve (m_RouterInfos.size ());
#else
auto& eligible = m_ExploratorySelection;
#endif
{ {
// collect eligible from current netdb // collect eligible from current netdb
bool checkIsReal = i2p::tunnel::tunnels.GetPreciseTunnelCreationSuccessRate () < NETDB_TUNNEL_CREATION_RATE_THRESHOLD; // too low rate bool checkIsReal = i2p::tunnel::tunnels.GetPreciseTunnelCreationSuccessRate () < NETDB_TUNNEL_CREATION_RATE_THRESHOLD; // too low rate
@ -1329,22 +1326,13 @@ namespace data
(!checkIsReal || (it.second->HasProfile () && it.second->GetProfile ()->IsReal ()))) (!checkIsReal || (it.second->HasProfile () && it.second->GetProfile ()->IsReal ())))
eligible.push_back (it.second); eligible.push_back (it.second);
} }
#if (__cplusplus >= 201703L) // C++ 17 or higher
if (eligible.size () > NETDB_MAX_EXPLORATORY_SELECTION_SIZE) if (eligible.size () > NETDB_MAX_EXPLORATORY_SELECTION_SIZE)
{ {
std::sample (eligible.begin(), eligible.end(), std::back_inserter(m_ExploratorySelection), std::sample (eligible.begin(), eligible.end(), std::back_inserter(m_ExploratorySelection),
NETDB_MAX_EXPLORATORY_SELECTION_SIZE, std::mt19937(ts)); NETDB_MAX_EXPLORATORY_SELECTION_SIZE, std::mt19937(ts));
} }
else else
std::swap (m_ExploratorySelection, eligible); std::swap (m_ExploratorySelection, eligible);
#else
if (m_ExploratorySelection.size () > NETDB_MAX_EXPLORATORY_SELECTION_SIZE)
{
// reduce number of eligible to max selection size
std::shuffle (m_ExploratorySelection.begin(), m_ExploratorySelection.end(), std::mt19937(ts));
m_ExploratorySelection.resize (NETDB_MAX_EXPLORATORY_SELECTION_SIZE);
}
#endif
m_LastExploratorySelectionUpdateTime = ts; m_LastExploratorySelectionUpdateTime = ts;
} }

8
libi2pd/NetDb.hpp

@ -87,7 +87,7 @@ namespace data
void RequestDestination (const IdentHash& destination, RequestedDestination::RequestComplete requestComplete = nullptr, bool direct = true); void RequestDestination (const IdentHash& destination, RequestedDestination::RequestComplete requestComplete = nullptr, bool direct = true);
std::shared_ptr<const RouterInfo> GetRandomRouter () const; std::shared_ptr<const RouterInfo> GetRandomRouter () const;
std::shared_ptr<const RouterInfo> GetRandomRouter (std::shared_ptr<const RouterInfo> compatibleWith, bool reverse, bool endpoint) const; std::shared_ptr<const RouterInfo> GetRandomRouter (std::shared_ptr<const RouterInfo> compatibleWith, bool reverse, bool endpoint, bool clientTunnel) const;
std::shared_ptr<const RouterInfo> GetHighBandwidthRandomRouter (std::shared_ptr<const RouterInfo> compatibleWith, bool reverse, bool endpoint) const; std::shared_ptr<const RouterInfo> GetHighBandwidthRandomRouter (std::shared_ptr<const RouterInfo> compatibleWith, bool reverse, bool endpoint) const;
std::shared_ptr<const RouterInfo> GetRandomSSU2PeerTestRouter (bool v4, const std::unordered_set<IdentHash>& excluded) const; std::shared_ptr<const RouterInfo> GetRandomSSU2PeerTestRouter (bool v4, const std::unordered_set<IdentHash>& excluded) const;
std::shared_ptr<const RouterInfo> GetRandomSSU2Introducer (bool v4, const std::unordered_set<IdentHash>& excluded) const; std::shared_ptr<const RouterInfo> GetRandomSSU2Introducer (bool v4, const std::unordered_set<IdentHash>& excluded) const;
@ -127,12 +127,12 @@ namespace data
} }
bool PopulateRouterInfoBuffer (std::shared_ptr<RouterInfo> r); bool PopulateRouterInfoBuffer (std::shared_ptr<RouterInfo> r);
std::shared_ptr<RouterInfo::Address> NewRouterInfoAddress () { return m_RouterInfoAddressesPool.AcquireSharedMt (); }; std::shared_ptr<RouterInfo::Address> NewRouterInfoAddress () { return m_RouterInfoAddressesPool.AcquireSharedMt (); };
boost::shared_ptr<RouterInfo::Addresses> NewRouterInfoAddresses () RouterInfo::AddressesPtr NewRouterInfoAddresses ()
{ {
return boost::shared_ptr<RouterInfo::Addresses>(m_RouterInfoAddressVectorsPool.AcquireMt (), return RouterInfo::AddressesPtr{m_RouterInfoAddressVectorsPool.AcquireMt (),
std::bind <void (i2p::util::MemoryPoolMt<RouterInfo::Addresses>::*)(RouterInfo::Addresses *)> std::bind <void (i2p::util::MemoryPoolMt<RouterInfo::Addresses>::*)(RouterInfo::Addresses *)>
(&i2p::util::MemoryPoolMt<RouterInfo::Addresses>::ReleaseMt, (&i2p::util::MemoryPoolMt<RouterInfo::Addresses>::ReleaseMt,
&m_RouterInfoAddressVectorsPool, std::placeholders::_1)); &m_RouterInfoAddressVectorsPool, std::placeholders::_1)};
}; };
std::shared_ptr<Lease> NewLease (const Lease& lease) { return m_LeasesPool.AcquireSharedMt (lease); }; std::shared_ptr<Lease> NewLease (const Lease& lease) { return m_LeasesPool.AcquireSharedMt (lease); };
std::shared_ptr<IdentityEx> NewIdentity (const uint8_t * buf, size_t len) { return m_IdentitiesPool.AcquireSharedMt (buf, len); }; std::shared_ptr<IdentityEx> NewIdentity (const uint8_t * buf, size_t len) { return m_IdentitiesPool.AcquireSharedMt (buf, len); };

16
libi2pd/RouterContext.cpp

@ -323,6 +323,10 @@ namespace i2p
case eRouterStatusFirewalled: case eRouterStatusFirewalled:
SetUnreachable (true, false); // ipv4 SetUnreachable (true, false); // ipv4
break; break;
case eRouterStatusProxy:
m_AcceptsTunnels = false;
UpdateCongestion ();
break;
default: default:
; ;
} }
@ -553,6 +557,12 @@ namespace i2p
UpdateRouterInfo (); UpdateRouterInfo ();
} }
void RouterContext::UpdateSSU2Introducer (const i2p::data::IdentHash& h, bool v4, uint32_t iTag, uint32_t iExp)
{
if (m_RouterInfo.UpdateSSU2Introducer (h, v4, iTag, iExp))
UpdateRouterInfo ();
}
void RouterContext::ClearSSU2Introducers (bool v4) void RouterContext::ClearSSU2Introducers (bool v4)
{ {
auto addr = m_RouterInfo.GetSSU2Address (v4); auto addr = m_RouterInfo.GetSSU2Address (v4);
@ -610,8 +620,8 @@ namespace i2p
case i2p::data::CAPS_FLAG_LOW_BANDWIDTH1 : limit = 12; type = low; break; case i2p::data::CAPS_FLAG_LOW_BANDWIDTH1 : limit = 12; type = low; break;
case i2p::data::CAPS_FLAG_LOW_BANDWIDTH2 : limit = i2p::data::LOW_BANDWIDTH_LIMIT; type = low; break; // 48 case i2p::data::CAPS_FLAG_LOW_BANDWIDTH2 : limit = i2p::data::LOW_BANDWIDTH_LIMIT; type = low; break; // 48
case i2p::data::CAPS_FLAG_LOW_BANDWIDTH3 : limit = 64; type = low; break; case i2p::data::CAPS_FLAG_LOW_BANDWIDTH3 : limit = 64; type = low; break;
case i2p::data::CAPS_FLAG_HIGH_BANDWIDTH1 : limit = 128; type = high; break; case i2p::data::CAPS_FLAG_LOW_BANDWIDTH4 : limit = 128; type = low; break;
case i2p::data::CAPS_FLAG_HIGH_BANDWIDTH2 : limit = i2p::data::HIGH_BANDWIDTH_LIMIT; type = high; break; // 256 case i2p::data::CAPS_FLAG_HIGH_BANDWIDTH : limit = i2p::data::HIGH_BANDWIDTH_LIMIT; type = high; break; // 256
case i2p::data::CAPS_FLAG_EXTRA_BANDWIDTH1 : limit = i2p::data::EXTRA_BANDWIDTH_LIMIT; type = extra; break; // 2048 case i2p::data::CAPS_FLAG_EXTRA_BANDWIDTH1 : limit = i2p::data::EXTRA_BANDWIDTH_LIMIT; type = extra; break; // 2048
case i2p::data::CAPS_FLAG_EXTRA_BANDWIDTH2 : limit = 1000000; type = unlim; break; // 1Gbyte/s case i2p::data::CAPS_FLAG_EXTRA_BANDWIDTH2 : limit = 1000000; type = unlim; break; // 1Gbyte/s
default: default:
@ -626,9 +636,7 @@ namespace i2p
case low : /* not set */; break; case low : /* not set */; break;
case extra : caps |= i2p::data::RouterInfo::eExtraBandwidth; break; // 'P' case extra : caps |= i2p::data::RouterInfo::eExtraBandwidth; break; // 'P'
case unlim : caps |= i2p::data::RouterInfo::eExtraBandwidth; case unlim : caps |= i2p::data::RouterInfo::eExtraBandwidth;
#if (__cplusplus >= 201703L) // C++ 17 or higher
[[fallthrough]]; [[fallthrough]];
#endif
// no break here, extra + high means 'X' // no break here, extra + high means 'X'
case high : caps |= i2p::data::RouterInfo::eHighBandwidth; break; case high : caps |= i2p::data::RouterInfo::eHighBandwidth; break;
} }

2
libi2pd/RouterContext.h

@ -154,6 +154,7 @@ namespace garlic
void PublishSSU2Address (int port, bool publish, bool v4, bool v6); void PublishSSU2Address (int port, bool publish, bool v4, bool v6);
bool AddSSU2Introducer (const i2p::data::RouterInfo::Introducer& introducer, bool v4); bool AddSSU2Introducer (const i2p::data::RouterInfo::Introducer& introducer, bool v4);
void RemoveSSU2Introducer (const i2p::data::IdentHash& h, bool v4); void RemoveSSU2Introducer (const i2p::data::IdentHash& h, bool v4);
void UpdateSSU2Introducer (const i2p::data::IdentHash& h, bool v4, uint32_t iTag, uint32_t iExp);
void ClearSSU2Introducers (bool v4); void ClearSSU2Introducers (bool v4);
bool IsUnreachable () const; bool IsUnreachable () const;
void SetUnreachable (bool v4, bool v6); void SetUnreachable (bool v4, bool v6);
@ -177,6 +178,7 @@ namespace garlic
void SetMTU (int mtu, bool v4); void SetMTU (int mtu, bool v4);
void SetHidden(bool hide) { m_IsHiddenMode = hide; }; void SetHidden(bool hide) { m_IsHiddenMode = hide; };
bool IsHidden() const { return m_IsHiddenMode; }; bool IsHidden() const { return m_IsHiddenMode; };
bool IsLimitedConnectivity () const { return m_Status == eRouterStatusProxy; }; // TODO: implement other cases
i2p::crypto::NoiseSymmetricState& GetCurrentNoiseState () { return m_CurrentNoiseState; }; i2p::crypto::NoiseSymmetricState& GetCurrentNoiseState () { return m_CurrentNoiseState; };
void UpdateNTCP2V6Address (const boost::asio::ip::address& host); // called from Daemon. TODO: remove void UpdateNTCP2V6Address (const boost::asio::ip::address& host); // called from Daemon. TODO: remove

153
libi2pd/RouterInfo.cpp

@ -10,10 +10,10 @@
#include <string.h> #include <string.h>
#include "I2PEndian.h" #include "I2PEndian.h"
#include <fstream> #include <fstream>
#include <memory>
#include <boost/lexical_cast.hpp> #include <boost/lexical_cast.hpp>
#include <boost/make_shared.hpp>
#include <boost/algorithm/string.hpp> // for boost::to_lower #include <boost/algorithm/string.hpp> // for boost::to_lower
#if (BOOST_VERSION >= 105300) #ifndef __cpp_lib_atomic_shared_ptr
#include <boost/atomic.hpp> #include <boost/atomic.hpp>
#endif #endif
#include "version.h" #include "version.h"
@ -40,7 +40,7 @@ namespace data
RouterInfo::RouterInfo (): m_Buffer (nullptr) RouterInfo::RouterInfo (): m_Buffer (nullptr)
{ {
m_Addresses = boost::make_shared<Addresses>(); // create empty list m_Addresses = AddressesPtr(new Addresses ()); // create empty list
} }
RouterInfo::RouterInfo (const std::string& fullPath): RouterInfo::RouterInfo (const std::string& fullPath):
@ -48,8 +48,8 @@ namespace data
m_SupportedTransports (0),m_ReachableTransports (0), m_PublishedTransports (0), m_SupportedTransports (0),m_ReachableTransports (0), m_PublishedTransports (0),
m_Caps (0), m_Version (0), m_Congestion (eLowCongestion) m_Caps (0), m_Version (0), m_Congestion (eLowCongestion)
{ {
m_Addresses = boost::make_shared<Addresses>(); // create empty list m_Addresses = AddressesPtr(new Addresses ()); // create empty list
m_Buffer = NewBuffer (); // always RouterInfo's m_Buffer = RouterInfo::NewBuffer (); // always RouterInfo's
ReadFromFile (fullPath); ReadFromFile (fullPath);
} }
@ -60,7 +60,7 @@ namespace data
{ {
if (len <= MAX_RI_BUFFER_SIZE) if (len <= MAX_RI_BUFFER_SIZE)
{ {
m_Addresses = boost::make_shared<Addresses>(); // create empty list m_Addresses = AddressesPtr(new Addresses ()); // create empty list
m_Buffer = buf; m_Buffer = buf;
if (m_Buffer) m_Buffer->SetBufferLen (len); if (m_Buffer) m_Buffer->SetBufferLen (len);
ReadFromBuffer (true); ReadFromBuffer (true);
@ -74,7 +74,7 @@ namespace data
} }
RouterInfo::RouterInfo (const uint8_t * buf, size_t len): RouterInfo::RouterInfo (const uint8_t * buf, size_t len):
RouterInfo (std::make_shared<Buffer> (buf, len), len) RouterInfo (netdb.NewRouterInfoBuffer (buf, len), len)
{ {
} }
@ -439,10 +439,10 @@ namespace data
} }
m_ReachableTransports |= m_PublishedTransports; m_ReachableTransports |= m_PublishedTransports;
// update addresses // update addresses
#if (BOOST_VERSION >= 105300) #ifdef __cpp_lib_atomic_shared_ptr
m_Addresses = addresses;
#else
boost::atomic_store (&m_Addresses, addresses); boost::atomic_store (&m_Addresses, addresses);
#else
m_Addresses = addresses; // race condition
#endif #endif
// read peers // read peers
uint8_t numPeers; uint8_t numPeers;
@ -538,10 +538,10 @@ namespace data
case CAPS_FLAG_LOW_BANDWIDTH1: case CAPS_FLAG_LOW_BANDWIDTH1:
case CAPS_FLAG_LOW_BANDWIDTH2: case CAPS_FLAG_LOW_BANDWIDTH2:
case CAPS_FLAG_LOW_BANDWIDTH3: case CAPS_FLAG_LOW_BANDWIDTH3:
case CAPS_FLAG_LOW_BANDWIDTH4:
m_BandwidthCap = *cap; m_BandwidthCap = *cap;
break; break;
case CAPS_FLAG_HIGH_BANDWIDTH1: case CAPS_FLAG_HIGH_BANDWIDTH:
case CAPS_FLAG_HIGH_BANDWIDTH2:
m_Caps |= Caps::eHighBandwidth; m_Caps |= Caps::eHighBandwidth;
m_BandwidthCap = *cap; m_BandwidthCap = *cap;
break; break;
@ -692,12 +692,12 @@ namespace data
if (addr->IsV4 ()) if (addr->IsV4 ())
{ {
m_SupportedTransports |= eNTCP2V4; m_SupportedTransports |= eNTCP2V4;
(*m_Addresses)[eNTCP2V4Idx] = addr; (*GetAddresses ())[eNTCP2V4Idx] = addr;
} }
if (addr->IsV6 ()) if (addr->IsV6 ())
{ {
m_SupportedTransports |= eNTCP2V6; m_SupportedTransports |= eNTCP2V6;
(*m_Addresses)[eNTCP2V6Idx] = addr; (*GetAddresses ())[eNTCP2V6Idx] = addr;
} }
} }
@ -718,11 +718,12 @@ namespace data
if (host.is_v4 ()) addr->caps |= eV4; if (host.is_v4 ()) addr->caps |= eV4;
if (host.is_v6 ()) addr->caps |= eV6; if (host.is_v6 ()) addr->caps |= eV6;
} }
auto addresses = GetAddresses ();
if (addr->IsV4 ()) if (addr->IsV4 ())
{ {
m_SupportedTransports |= eNTCP2V4; m_SupportedTransports |= eNTCP2V4;
m_ReachableTransports |= eNTCP2V4; m_ReachableTransports |= eNTCP2V4;
(*m_Addresses)[eNTCP2V4Idx] = addr; (*addresses)[eNTCP2V4Idx] = addr;
} }
if (addr->IsV6 ()) if (addr->IsV6 ())
{ {
@ -730,30 +731,31 @@ namespace data
{ {
m_SupportedTransports |= eNTCP2V6Mesh; m_SupportedTransports |= eNTCP2V6Mesh;
m_ReachableTransports |= eNTCP2V6Mesh; m_ReachableTransports |= eNTCP2V6Mesh;
(*m_Addresses)[eNTCP2V6MeshIdx] = addr; (*addresses)[eNTCP2V6MeshIdx] = addr;
} }
else else
{ {
m_SupportedTransports |= eNTCP2V6; m_SupportedTransports |= eNTCP2V6;
m_ReachableTransports |= eNTCP2V6; m_ReachableTransports |= eNTCP2V6;
(*m_Addresses)[eNTCP2V6Idx] = addr; (*addresses)[eNTCP2V6Idx] = addr;
} }
} }
} }
void RouterInfo::RemoveNTCP2Address (bool v4) void RouterInfo::RemoveNTCP2Address (bool v4)
{ {
auto addresses = GetAddresses ();
if (v4) if (v4)
{ {
if ((*m_Addresses)[eNTCP2V6Idx]) if ((*addresses)[eNTCP2V6Idx])
(*m_Addresses)[eNTCP2V6Idx]->caps &= ~AddressCaps::eV4; (*addresses)[eNTCP2V6Idx]->caps &= ~AddressCaps::eV4;
(*m_Addresses)[eNTCP2V4Idx].reset (); (*addresses)[eNTCP2V4Idx].reset ();
} }
else else
{ {
if ((*m_Addresses)[eNTCP2V4Idx]) if ((*addresses)[eNTCP2V4Idx])
(*m_Addresses)[eNTCP2V4Idx]->caps &= ~AddressCaps::eV6; (*addresses)[eNTCP2V4Idx]->caps &= ~AddressCaps::eV6;
(*m_Addresses)[eNTCP2V6Idx].reset (); (*addresses)[eNTCP2V6Idx].reset ();
} }
UpdateSupportedTransports (); UpdateSupportedTransports ();
} }
@ -769,15 +771,16 @@ namespace data
addr->ssu->mtu = 0; addr->ssu->mtu = 0;
memcpy (addr->s, staticKey, 32); memcpy (addr->s, staticKey, 32);
memcpy (addr->i, introKey, 32); memcpy (addr->i, introKey, 32);
auto addresses = GetAddresses ();
if (addr->IsV4 ()) if (addr->IsV4 ())
{ {
m_SupportedTransports |= eSSU2V4; m_SupportedTransports |= eSSU2V4;
(*m_Addresses)[eSSU2V4Idx] = addr; (*addresses)[eSSU2V4Idx] = addr;
} }
if (addr->IsV6 ()) if (addr->IsV6 ())
{ {
m_SupportedTransports |= eSSU2V6; m_SupportedTransports |= eSSU2V6;
(*m_Addresses)[eSSU2V6Idx] = addr; (*addresses)[eSSU2V6Idx] = addr;
} }
} }
@ -802,33 +805,35 @@ namespace data
if (host.is_v4 ()) addr->caps |= eV4; if (host.is_v4 ()) addr->caps |= eV4;
if (host.is_v6 ()) addr->caps |= eV6; if (host.is_v6 ()) addr->caps |= eV6;
} }
auto addresses = GetAddresses ();
if (addr->IsV4 ()) if (addr->IsV4 ())
{ {
m_SupportedTransports |= eSSU2V4; m_SupportedTransports |= eSSU2V4;
m_ReachableTransports |= eSSU2V4; m_ReachableTransports |= eSSU2V4;
(*m_Addresses)[eSSU2V4Idx] = addr; (*addresses)[eSSU2V4Idx] = addr;
} }
if (addr->IsV6 ()) if (addr->IsV6 ())
{ {
m_SupportedTransports |= eSSU2V6; m_SupportedTransports |= eSSU2V6;
m_ReachableTransports |= eSSU2V6; m_ReachableTransports |= eSSU2V6;
(*m_Addresses)[eSSU2V6Idx] = addr; (*addresses)[eSSU2V6Idx] = addr;
} }
} }
void RouterInfo::RemoveSSU2Address (bool v4) void RouterInfo::RemoveSSU2Address (bool v4)
{ {
auto addresses = GetAddresses ();
if (v4) if (v4)
{ {
if ((*m_Addresses)[eSSU2V6Idx]) if ((*addresses)[eSSU2V6Idx])
(*m_Addresses)[eSSU2V6Idx]->caps &= ~AddressCaps::eV4; (*addresses)[eSSU2V6Idx]->caps &= ~AddressCaps::eV4;
(*m_Addresses)[eSSU2V4Idx].reset (); (*addresses)[eSSU2V4Idx].reset ();
} }
else else
{ {
if ((*m_Addresses)[eSSU2V4Idx]) if ((*addresses)[eSSU2V4Idx])
(*m_Addresses)[eSSU2V4Idx]->caps &= ~AddressCaps::eV6; (*addresses)[eSSU2V4Idx]->caps &= ~AddressCaps::eV6;
(*m_Addresses)[eSSU2V6Idx].reset (); (*addresses)[eSSU2V6Idx].reset ();
} }
UpdateSupportedTransports (); UpdateSupportedTransports ();
} }
@ -869,17 +874,18 @@ namespace data
{ {
if (IsV6 ()) if (IsV6 ())
{ {
if ((*m_Addresses)[eNTCP2V6Idx]) auto addresses = GetAddresses ();
if ((*addresses)[eNTCP2V6Idx])
{ {
if ((*m_Addresses)[eNTCP2V6Idx]->IsV4 () && (*m_Addresses)[eNTCP2V4Idx]) if ((*addresses)[eNTCP2V6Idx]->IsV4 () && (*addresses)[eNTCP2V4Idx])
(*m_Addresses)[eNTCP2V4Idx]->caps &= ~AddressCaps::eV6; (*addresses)[eNTCP2V4Idx]->caps &= ~AddressCaps::eV6;
(*m_Addresses)[eNTCP2V6Idx].reset (); (*addresses)[eNTCP2V6Idx].reset ();
} }
if ((*m_Addresses)[eSSU2V6Idx]) if ((*addresses)[eSSU2V6Idx])
{ {
if ((*m_Addresses)[eSSU2V6Idx]->IsV4 () && (*m_Addresses)[eSSU2V4Idx]) if ((*addresses)[eSSU2V6Idx]->IsV4 () && (*addresses)[eSSU2V4Idx])
(*m_Addresses)[eSSU2V4Idx]->caps &= ~AddressCaps::eV6; (*addresses)[eSSU2V4Idx]->caps &= ~AddressCaps::eV6;
(*m_Addresses)[eSSU2V6Idx].reset (); (*addresses)[eSSU2V6Idx].reset ();
} }
UpdateSupportedTransports (); UpdateSupportedTransports ();
} }
@ -889,17 +895,18 @@ namespace data
{ {
if (IsV4 ()) if (IsV4 ())
{ {
if ((*m_Addresses)[eNTCP2V4Idx]) auto addresses = GetAddresses ();
if ((*addresses)[eNTCP2V4Idx])
{ {
if ((*m_Addresses)[eNTCP2V4Idx]->IsV6 () && (*m_Addresses)[eNTCP2V6Idx]) if ((*addresses)[eNTCP2V4Idx]->IsV6 () && (*addresses)[eNTCP2V6Idx])
(*m_Addresses)[eNTCP2V6Idx]->caps &= ~AddressCaps::eV4; (*addresses)[eNTCP2V6Idx]->caps &= ~AddressCaps::eV4;
(*m_Addresses)[eNTCP2V4Idx].reset (); (*addresses)[eNTCP2V4Idx].reset ();
} }
if ((*m_Addresses)[eSSU2V4Idx]) if ((*addresses)[eSSU2V4Idx])
{ {
if ((*m_Addresses)[eSSU2V4Idx]->IsV6 () && (*m_Addresses)[eSSU2V6Idx]) if ((*addresses)[eSSU2V4Idx]->IsV6 () && (*addresses)[eSSU2V6Idx])
(*m_Addresses)[eSSU2V6Idx]->caps &= ~AddressCaps::eV4; (*addresses)[eSSU2V6Idx]->caps &= ~AddressCaps::eV4;
(*m_Addresses)[eSSU2V4Idx].reset (); (*addresses)[eSSU2V4Idx].reset ();
} }
UpdateSupportedTransports (); UpdateSupportedTransports ();
} }
@ -920,7 +927,7 @@ namespace data
{ {
m_SupportedTransports &= ~eNTCP2V6Mesh; m_SupportedTransports &= ~eNTCP2V6Mesh;
m_ReachableTransports &= ~eNTCP2V6Mesh; m_ReachableTransports &= ~eNTCP2V6Mesh;
(*m_Addresses)[eNTCP2V6MeshIdx].reset (); (*GetAddresses ())[eNTCP2V6MeshIdx].reset ();
} }
} }
@ -949,12 +956,12 @@ namespace data
return nullptr; return nullptr;
} }
boost::shared_ptr<RouterInfo::Addresses> RouterInfo::GetAddresses () const RouterInfo::AddressesPtr RouterInfo::GetAddresses () const
{ {
#if (BOOST_VERSION >= 105300) #ifdef __cpp_lib_atomic_shared_ptr
return boost::atomic_load (&m_Addresses);
#else
return m_Addresses; return m_Addresses;
#else
return boost::atomic_load (&m_Addresses);
#endif #endif
} }
@ -962,10 +969,10 @@ namespace data
std::shared_ptr<const RouterInfo::Address> RouterInfo::GetAddress (Filter filter) const std::shared_ptr<const RouterInfo::Address> RouterInfo::GetAddress (Filter filter) const
{ {
// TODO: make it more generic using comparator // TODO: make it more generic using comparator
#if (BOOST_VERSION >= 105300) #ifdef __cpp_lib_atomic_shared_ptr
AddressesPtr addresses = m_Addresses;
#else
auto addresses = boost::atomic_load (&m_Addresses); auto addresses = boost::atomic_load (&m_Addresses);
#else
auto addresses = m_Addresses;
#endif #endif
for (const auto& address : *addresses) for (const auto& address : *addresses)
if (address && filter (address)) return address; if (address && filter (address)) return address;
@ -1062,7 +1069,7 @@ namespace data
void RouterInfo::SetUnreachableAddressesTransportCaps (uint8_t transports) void RouterInfo::SetUnreachableAddressesTransportCaps (uint8_t transports)
{ {
for (auto& addr: *m_Addresses) for (auto& addr: *GetAddresses ())
{ {
if (addr && !addr->published) if (addr && !addr->published)
{ {
@ -1076,7 +1083,7 @@ namespace data
{ {
m_SupportedTransports = 0; m_SupportedTransports = 0;
m_ReachableTransports = 0; m_ReachableTransports = 0;
for (const auto& addr: *m_Addresses) for (const auto& addr: *GetAddresses ())
{ {
if (!addr) continue; if (!addr) continue;
uint8_t transports = 0; uint8_t transports = 0;
@ -1151,7 +1158,7 @@ namespace data
return netdb.NewRouterInfoAddress (); return netdb.NewRouterInfoAddress ();
} }
boost::shared_ptr<RouterInfo::Addresses> RouterInfo::NewAddresses () const RouterInfo::AddressesPtr RouterInfo::NewAddresses () const
{ {
return netdb.NewRouterInfoAddresses (); return netdb.NewRouterInfoAddresses ();
} }
@ -1224,7 +1231,7 @@ namespace data
CAPS_FLAG_EXTRA_BANDWIDTH2 : // 'X' CAPS_FLAG_EXTRA_BANDWIDTH2 : // 'X'
CAPS_FLAG_EXTRA_BANDWIDTH1; // 'P' CAPS_FLAG_EXTRA_BANDWIDTH1; // 'P'
else else
caps += CAPS_FLAG_HIGH_BANDWIDTH2; // 'O' caps += CAPS_FLAG_HIGH_BANDWIDTH; // 'O'
caps += CAPS_FLAG_FLOODFILL; // floodfill caps += CAPS_FLAG_FLOODFILL; // floodfill
} }
else else
@ -1232,7 +1239,7 @@ namespace data
if (c & eExtraBandwidth) if (c & eExtraBandwidth)
caps += (c & eHighBandwidth) ? CAPS_FLAG_EXTRA_BANDWIDTH2 /* 'X' */ : CAPS_FLAG_EXTRA_BANDWIDTH1; /*'P' */ caps += (c & eHighBandwidth) ? CAPS_FLAG_EXTRA_BANDWIDTH2 /* 'X' */ : CAPS_FLAG_EXTRA_BANDWIDTH1; /*'P' */
else else
caps += (c & eHighBandwidth) ? CAPS_FLAG_HIGH_BANDWIDTH2 /* 'O' */: CAPS_FLAG_LOW_BANDWIDTH2 /* 'L' */; // bandwidth caps += (c & eHighBandwidth) ? CAPS_FLAG_HIGH_BANDWIDTH /* 'O' */: CAPS_FLAG_LOW_BANDWIDTH2 /* 'L' */; // bandwidth
} }
if (c & eHidden) caps += CAPS_FLAG_HIDDEN; // hidden if (c & eHidden) caps += CAPS_FLAG_HIDDEN; // hidden
if (c & eReachable) caps += CAPS_FLAG_REACHABLE; // reachable if (c & eReachable) caps += CAPS_FLAG_REACHABLE; // reachable
@ -1503,9 +1510,9 @@ namespace data
return std::make_shared<Address> (); return std::make_shared<Address> ();
} }
boost::shared_ptr<RouterInfo::Addresses> LocalRouterInfo::NewAddresses () const RouterInfo::AddressesPtr LocalRouterInfo::NewAddresses () const
{ {
return boost::make_shared<Addresses> (); return RouterInfo::AddressesPtr(new RouterInfo::Addresses ());
} }
std::shared_ptr<IdentityEx> LocalRouterInfo::NewIdentity (const uint8_t * buf, size_t len) const std::shared_ptr<IdentityEx> LocalRouterInfo::NewIdentity (const uint8_t * buf, size_t len) const
@ -1547,5 +1554,23 @@ namespace data
} }
return false; return false;
} }
bool LocalRouterInfo::UpdateSSU2Introducer (const IdentHash& h, bool v4, uint32_t iTag, uint32_t iExp)
{
auto addresses = GetAddresses ();
if (!addresses) return false;
auto addr = (*addresses)[v4 ? eSSU2V4Idx : eSSU2V6Idx];
if (addr)
{
for (auto& it: addr->ssu->introducers)
if (h == it.iH)
{
it.iTag = iTag;
it.iExp = iExp;
return true;
}
}
return false;
}
} }
} }

26
libi2pd/RouterInfo.h

@ -15,8 +15,11 @@
#include <vector> #include <vector>
#include <array> #include <array>
#include <iostream> #include <iostream>
#include <memory>
#include <boost/asio.hpp> #include <boost/asio.hpp>
#ifndef __cpp_lib_atomic_shared_ptr
#include <boost/shared_ptr.hpp> #include <boost/shared_ptr.hpp>
#endif
#include "Identity.h" #include "Identity.h"
#include "Profiling.h" #include "Profiling.h"
#include "Family.h" #include "Family.h"
@ -40,8 +43,8 @@ namespace data
const char CAPS_FLAG_LOW_BANDWIDTH1 = 'K'; /* < 12 KBps */ const char CAPS_FLAG_LOW_BANDWIDTH1 = 'K'; /* < 12 KBps */
const char CAPS_FLAG_LOW_BANDWIDTH2 = 'L'; /* 12-48 KBps */ const char CAPS_FLAG_LOW_BANDWIDTH2 = 'L'; /* 12-48 KBps */
const char CAPS_FLAG_LOW_BANDWIDTH3 = 'M'; /* 48-64 KBps */ const char CAPS_FLAG_LOW_BANDWIDTH3 = 'M'; /* 48-64 KBps */
const char CAPS_FLAG_HIGH_BANDWIDTH1 = 'N'; /* 64-128 KBps */ const char CAPS_FLAG_LOW_BANDWIDTH4 = 'N'; /* 64-128 KBps */
const char CAPS_FLAG_HIGH_BANDWIDTH2 = 'O'; /* 128-256 KBps */ const char CAPS_FLAG_HIGH_BANDWIDTH = 'O'; /* 128-256 KBps */
const char CAPS_FLAG_EXTRA_BANDWIDTH1 = 'P'; /* 256-2048 KBps */ const char CAPS_FLAG_EXTRA_BANDWIDTH1 = 'P'; /* 256-2048 KBps */
const char CAPS_FLAG_EXTRA_BANDWIDTH2 = 'X'; /* > 2048 KBps */ const char CAPS_FLAG_EXTRA_BANDWIDTH2 = 'X'; /* > 2048 KBps */
// bandwidth limits in kBps // bandwidth limits in kBps
@ -199,7 +202,11 @@ namespace data
}; };
typedef std::array<std::shared_ptr<Address>, eNumTransports> Addresses; typedef std::array<std::shared_ptr<Address>, eNumTransports> Addresses;
#ifdef __cpp_lib_atomic_shared_ptr
typedef std::shared_ptr<Addresses> AddressesPtr;
#else
typedef boost::shared_ptr<Addresses> AddressesPtr;
#endif
RouterInfo (const std::string& fullPath); RouterInfo (const std::string& fullPath);
RouterInfo (const RouterInfo& ) = default; RouterInfo (const RouterInfo& ) = default;
RouterInfo& operator=(const RouterInfo& ) = default; RouterInfo& operator=(const RouterInfo& ) = default;
@ -214,7 +221,7 @@ namespace data
int GetVersion () const { return m_Version; }; int GetVersion () const { return m_Version; };
virtual void SetProperty (const std::string& key, const std::string& value) {}; virtual void SetProperty (const std::string& key, const std::string& value) {};
virtual void ClearProperties () {}; virtual void ClearProperties () {};
boost::shared_ptr<Addresses> GetAddresses () const; // should be called for local RI only, otherwise must return shared_ptr AddressesPtr GetAddresses () const; // should be called for local RI only, otherwise must return shared_ptr
std::shared_ptr<const Address> GetNTCP2V4Address () const; std::shared_ptr<const Address> GetNTCP2V4Address () const;
std::shared_ptr<const Address> GetNTCP2V6Address () const; std::shared_ptr<const Address> GetNTCP2V6Address () const;
std::shared_ptr<const Address> GetPublishedNTCP2V4Address () const; std::shared_ptr<const Address> GetPublishedNTCP2V4Address () const;
@ -333,7 +340,7 @@ namespace data
std::shared_ptr<const Address> GetAddress (Filter filter) const; std::shared_ptr<const Address> GetAddress (Filter filter) const;
virtual std::shared_ptr<Buffer> NewBuffer () const; virtual std::shared_ptr<Buffer> NewBuffer () const;
virtual std::shared_ptr<Address> NewAddress () const; virtual std::shared_ptr<Address> NewAddress () const;
virtual boost::shared_ptr<Addresses> NewAddresses () const; virtual AddressesPtr NewAddresses () const;
virtual std::shared_ptr<IdentityEx> NewIdentity (const uint8_t * buf, size_t len) const; virtual std::shared_ptr<IdentityEx> NewIdentity (const uint8_t * buf, size_t len) const;
private: private:
@ -342,7 +349,11 @@ namespace data
std::shared_ptr<const IdentityEx> m_RouterIdentity; std::shared_ptr<const IdentityEx> m_RouterIdentity;
std::shared_ptr<Buffer> m_Buffer; std::shared_ptr<Buffer> m_Buffer;
uint64_t m_Timestamp; // in milliseconds uint64_t m_Timestamp; // in milliseconds
boost::shared_ptr<Addresses> m_Addresses; // TODO: use std::shared_ptr and std::atomic_store for gcc >= 4.9 #ifdef __cpp_lib_atomic_shared_ptr
std::atomic<AddressesPtr> m_Addresses;
#else
AddressesPtr m_Addresses;
#endif
bool m_IsUpdated, m_IsUnreachable, m_IsFloodfill; bool m_IsUpdated, m_IsUnreachable, m_IsFloodfill;
CompatibleTransports m_SupportedTransports, m_ReachableTransports, m_PublishedTransports; CompatibleTransports m_SupportedTransports, m_ReachableTransports, m_PublishedTransports;
uint8_t m_Caps; uint8_t m_Caps;
@ -369,6 +380,7 @@ namespace data
bool AddSSU2Introducer (const Introducer& introducer, bool v4); bool AddSSU2Introducer (const Introducer& introducer, bool v4);
bool RemoveSSU2Introducer (const IdentHash& h, bool v4); bool RemoveSSU2Introducer (const IdentHash& h, bool v4);
bool UpdateSSU2Introducer (const IdentHash& h, bool v4, uint32_t iTag, uint32_t iExp);
private: private:
@ -377,7 +389,7 @@ namespace data
void WriteString (const std::string& str, std::ostream& s) const; void WriteString (const std::string& str, std::ostream& s) const;
std::shared_ptr<Buffer> NewBuffer () const override; std::shared_ptr<Buffer> NewBuffer () const override;
std::shared_ptr<Address> NewAddress () const override; std::shared_ptr<Address> NewAddress () const override;
boost::shared_ptr<Addresses> NewAddresses () const override; RouterInfo::AddressesPtr NewAddresses () const override;
std::shared_ptr<IdentityEx> NewIdentity (const uint8_t * buf, size_t len) const override; std::shared_ptr<IdentityEx> NewIdentity (const uint8_t * buf, size_t len) const override;
private: private:

404
libi2pd/SSU2.cpp

@ -152,8 +152,11 @@ namespace transport
m_SessionsByRouterHash.clear (); m_SessionsByRouterHash.clear ();
m_PendingOutgoingSessions.clear (); m_PendingOutgoingSessions.clear ();
m_Relays.clear (); m_Relays.clear ();
m_PeerTests.clear ();
m_Introducers.clear (); m_Introducers.clear ();
m_IntroducersV6.clear (); m_IntroducersV6.clear ();
m_ConnectedRecently.clear ();
m_RequestedPeerTests.clear ();
} }
void SSU2Server::SetLocalAddress (const boost::asio::ip::address& localAddress) void SSU2Server::SetLocalAddress (const boost::asio::ip::address& localAddress)
@ -210,6 +213,29 @@ namespace transport
return ep.port (); return ep.port ();
} }
bool SSU2Server::IsConnectedRecently (const boost::asio::ip::udp::endpoint& ep)
{
if (!ep.port () || ep.address ().is_unspecified ()) return false;
auto it = m_ConnectedRecently.find (ep);
if (it != m_ConnectedRecently.end ())
{
if (i2p::util::GetSecondsSinceEpoch () <= it->second + SSU2_HOLE_PUNCH_EXPIRATION)
return true;
else
m_ConnectedRecently.erase (it);
}
return false;
}
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;
auto [it, added] = m_ConnectedRecently.try_emplace (ep, ts);
if (!added && ts > it->second)
it->second = ts; // renew timestamp of existing endpoint
}
void SSU2Server::AdjustTimeOffset (int64_t offset, std::shared_ptr<const i2p::data::IdentityEx> from) void SSU2Server::AdjustTimeOffset (int64_t offset, std::shared_ptr<const i2p::data::IdentityEx> from)
{ {
if (offset) if (offset)
@ -343,16 +369,25 @@ namespace transport
size_t moreBytes = socket.available (ec); size_t moreBytes = socket.available (ec);
if (!ec && moreBytes) if (!ec && moreBytes)
{ {
std::vector<Packet *> packets; auto packets = m_PacketsArrayPool.AcquireMt ();
packets.push_back (packet); packets->AddPacket (packet);
while (moreBytes && packets.size () < 32) while (moreBytes && packets->numPackets < SSU2_MAX_NUM_PACKETS_PER_BATCH)
{ {
packet = m_PacketsPool.AcquireMt (); packet = m_PacketsPool.AcquireMt ();
packet->len = socket.receive_from (boost::asio::buffer (packet->buf, SSU2_MAX_PACKET_SIZE), packet->from, 0, ec); packet->len = socket.receive_from (boost::asio::buffer (packet->buf, SSU2_MAX_PACKET_SIZE), packet->from, 0, ec);
if (!ec) if (!ec)
{ {
i2p::transport::transports.UpdateReceivedBytes (packet->len); i2p::transport::transports.UpdateReceivedBytes (packet->len);
packets.push_back (packet); if (packet->len >= SSU2_MIN_RECEIVED_PACKET_SIZE)
{
if (!packets->AddPacket (packet))
{
LogPrint (eLogError, "SSU2: Received packets array is full");
m_PacketsPool.ReleaseMt (packet);
}
}
else // drop too short packets
m_PacketsPool.ReleaseMt (packet);
moreBytes = socket.available(ec); moreBytes = socket.available(ec);
if (ec) break; if (ec) break;
} }
@ -407,15 +442,23 @@ namespace transport
} }
} }
void SSU2Server::HandleReceivedPackets (std::vector<Packet *> packets) void SSU2Server::HandleReceivedPackets (Packets * packets)
{ {
if (!packets) return;
if (m_IsThroughProxy) if (m_IsThroughProxy)
for (auto& packet: packets) for (size_t i = 0; i < packets->numPackets; i++)
{
auto& packet = (*packets)[i];
ProcessNextPacketFromProxy (packet->buf, packet->len); ProcessNextPacketFromProxy (packet->buf, packet->len);
}
else else
for (auto& packet: packets) for (size_t i = 0; i < packets->numPackets; i++)
{
auto& packet = (*packets)[i];
ProcessNextPacket (packet->buf, packet->len, packet->from); ProcessNextPacket (packet->buf, packet->len, packet->from);
m_PacketsPool.ReleaseMt (packets); }
m_PacketsPool.ReleaseMt (packets->data (), packets->numPackets);
m_PacketsArrayPool.ReleaseMt (packets);
if (m_LastSession && m_LastSession->GetState () != eSSU2SessionStateTerminated) if (m_LastSession && m_LastSession->GetState () != eSSU2SessionStateTerminated)
m_LastSession->FlushData (); m_LastSession->FlushData ();
} }
@ -425,7 +468,8 @@ namespace transport
if (session) if (session)
{ {
m_Sessions.emplace (session->GetConnID (), session); m_Sessions.emplace (session->GetConnID (), session);
AddSessionByRouterHash (session); if (session->GetState () != eSSU2SessionStatePeerTest)
AddSessionByRouterHash (session);
} }
} }
@ -434,19 +478,28 @@ namespace transport
auto it = m_Sessions.find (connID); auto it = m_Sessions.find (connID);
if (it != m_Sessions.end ()) if (it != m_Sessions.end ())
{ {
auto ident = it->second->GetRemoteIdentity (); if (it->second->GetState () != eSSU2SessionStatePeerTest)
if (ident) {
{ auto ident = it->second->GetRemoteIdentity ();
auto it1 = m_SessionsByRouterHash.find (ident->GetIdentHash ()); if (ident)
if (it1 != m_SessionsByRouterHash.end () && it->second == it1->second) {
m_SessionsByRouterHash.erase (it1); std::lock_guard<std::mutex> l(m_SessionsByRouterHashMutex);
} auto it1 = m_SessionsByRouterHash.find (ident->GetIdentHash ());
if (it1 != m_SessionsByRouterHash.end () && it->second == it1->second.lock ())
m_SessionsByRouterHash.erase (it1);
}
}
if (m_LastSession == it->second) if (m_LastSession == it->second)
m_LastSession = nullptr; m_LastSession = nullptr;
m_Sessions.erase (it); m_Sessions.erase (it);
} }
} }
void SSU2Server::RequestRemoveSession (uint64_t connID)
{
GetService ().post ([connID, this]() { RemoveSession (connID); });
}
void SSU2Server::AddSessionByRouterHash (std::shared_ptr<SSU2Session> session) void SSU2Server::AddSessionByRouterHash (std::shared_ptr<SSU2Session> session)
{ {
if (session) if (session)
@ -454,18 +507,26 @@ namespace transport
auto ident = session->GetRemoteIdentity (); auto ident = session->GetRemoteIdentity ();
if (ident) if (ident)
{ {
auto ret = m_SessionsByRouterHash.emplace (ident->GetIdentHash (), session); std::shared_ptr<SSU2Session> oldSession;
if (!ret.second && ret.first->second != session)
{ {
std::lock_guard<std::mutex> l(m_SessionsByRouterHashMutex);
auto ret = m_SessionsByRouterHash.emplace (ident->GetIdentHash (), session);
if (!ret.second)
{
oldSession = ret.first->second.lock ();
// update session
ret.first->second = session;
}
}
if (oldSession && oldSession != session)
{
// session already exists // session already exists
LogPrint (eLogWarning, "SSU2: Session to ", ident->GetIdentHash ().ToBase64 (), " already exists"); LogPrint (eLogWarning, "SSU2: Session to ", ident->GetIdentHash ().ToBase64 (), " already exists");
// move unsent msgs to new session // move unsent msgs to new session
ret.first->second->MoveSendQueue (session); oldSession->MoveSendQueue (session);
// terminate existing // terminate existing
GetService ().post (std::bind (&SSU2Session::RequestTermination, ret.first->second, eSSU2TerminationReasonReplacedByNewSession)); GetService ().post (std::bind (&SSU2Session::RequestTermination, oldSession, eSSU2TerminationReasonReplacedByNewSession));
// update session }
ret.first->second = session;
}
} }
} }
} }
@ -473,21 +534,30 @@ namespace transport
bool SSU2Server::AddPendingOutgoingSession (std::shared_ptr<SSU2Session> session) bool SSU2Server::AddPendingOutgoingSession (std::shared_ptr<SSU2Session> session)
{ {
if (!session) return false; if (!session) return false;
std::unique_lock<std::mutex> l(m_PendingOutgoingSessionsMutex); std::lock_guard<std::mutex> l(m_PendingOutgoingSessionsMutex);
return m_PendingOutgoingSessions.emplace (session->GetRemoteEndpoint (), session).second; return m_PendingOutgoingSessions.emplace (session->GetRemoteEndpoint (), session).second;
} }
std::shared_ptr<SSU2Session> SSU2Server::FindSession (const i2p::data::IdentHash& ident) const std::shared_ptr<SSU2Session> SSU2Server::FindSession (const i2p::data::IdentHash& ident)
{ {
std::lock_guard<std::mutex> l(m_SessionsByRouterHashMutex);
auto it = m_SessionsByRouterHash.find (ident); auto it = m_SessionsByRouterHash.find (ident);
if (it != m_SessionsByRouterHash.end ()) if (it != m_SessionsByRouterHash.end ())
return it->second; {
if (!it->second.expired ())
{
auto s = it->second.lock ();
if (s && s->GetState () != eSSU2SessionStateTerminated)
return s;
}
m_SessionsByRouterHash.erase (it);
}
return nullptr; return nullptr;
} }
std::shared_ptr<SSU2Session> SSU2Server::FindPendingOutgoingSession (const boost::asio::ip::udp::endpoint& ep) const std::shared_ptr<SSU2Session> SSU2Server::FindPendingOutgoingSession (const boost::asio::ip::udp::endpoint& ep) const
{ {
std::unique_lock<std::mutex> l(m_PendingOutgoingSessionsMutex); std::lock_guard<std::mutex> l(m_PendingOutgoingSessionsMutex);
auto it = m_PendingOutgoingSessions.find (ep); auto it = m_PendingOutgoingSessions.find (ep);
if (it != m_PendingOutgoingSessions.end ()) if (it != m_PendingOutgoingSessions.end ())
return it->second; return it->second;
@ -496,7 +566,7 @@ namespace transport
void SSU2Server::RemovePendingOutgoingSession (const boost::asio::ip::udp::endpoint& ep) void SSU2Server::RemovePendingOutgoingSession (const boost::asio::ip::udp::endpoint& ep)
{ {
std::unique_lock<std::mutex> l(m_PendingOutgoingSessionsMutex); std::lock_guard<std::mutex> l(m_PendingOutgoingSessionsMutex);
m_PendingOutgoingSessions.erase (ep); m_PendingOutgoingSessions.erase (ep);
} }
@ -541,14 +611,51 @@ namespace transport
auto it = m_Relays.find (tag); auto it = m_Relays.find (tag);
if (it != m_Relays.end ()) if (it != m_Relays.end ())
{ {
if (it->second->IsEstablished ()) if (!it->second.expired ())
return it->second; {
else auto s = it->second.lock ();
m_Relays.erase (it); if (s && s->IsEstablished ())
return s;
}
m_Relays.erase (it);
} }
return nullptr; return nullptr;
} }
bool SSU2Server::AddPeerTest (uint32_t nonce, std::shared_ptr<SSU2Session> aliceSession, uint64_t ts)
{
return m_PeerTests.emplace (nonce, std::pair{ aliceSession, ts }).second;
}
std::shared_ptr<SSU2Session> SSU2Server::GetPeerTest (uint32_t nonce)
{
auto it = m_PeerTests.find (nonce);
if (it != m_PeerTests.end ())
{
auto s = it->second.first.lock ();
m_PeerTests.erase (it);
return s;
}
return nullptr;
}
bool SSU2Server::AddRequestedPeerTest (uint32_t nonce, std::shared_ptr<SSU2PeerTestSession> session, uint64_t ts)
{
return m_RequestedPeerTests.emplace (nonce, std::pair{ session, ts }).second;
}
std::shared_ptr<SSU2PeerTestSession> SSU2Server::GetRequestedPeerTest (uint32_t nonce)
{
auto it = m_RequestedPeerTests.find (nonce);
if (it != m_RequestedPeerTests.end ())
{
auto s = it->second.first.lock ();
m_RequestedPeerTests.erase (it);
return s;
}
return nullptr;
}
void SSU2Server::ProcessNextPacket (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint) void SSU2Server::ProcessNextPacket (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint)
{ {
if (len < 24) return; if (len < 24) return;
@ -618,7 +725,7 @@ namespace transport
if (it1->second->GetState () == eSSU2SessionStateSessionRequestSent && if (it1->second->GetState () == eSSU2SessionStateSessionRequestSent &&
it1->second->ProcessSessionCreated (buf, len)) it1->second->ProcessSessionCreated (buf, len))
{ {
std::unique_lock<std::mutex> l(m_PendingOutgoingSessionsMutex); std::lock_guard<std::mutex> l(m_PendingOutgoingSessionsMutex);
m_PendingOutgoingSessions.erase (it1); // we are done with that endpoint m_PendingOutgoingSessions.erase (it1); // we are done with that endpoint
} }
else else
@ -715,15 +822,12 @@ namespace transport
if (router && address) if (router && address)
{ {
// check if no session // check if no session
auto it = m_SessionsByRouterHash.find (router->GetIdentHash ()); auto existingSession = FindSession (router->GetIdentHash ());
if (it != m_SessionsByRouterHash.end ()) if (existingSession)
{ {
// session with router found, trying to send peer test if requested // session with router found, trying to send peer test if requested
if (peerTest && it->second->IsEstablished ()) if (peerTest && existingSession->IsEstablished ())
{ GetService ().post ([existingSession]() { existingSession->SendPeerTest (); });
auto session = it->second;
GetService ().post ([session]() { session->SendPeerTest (); });
}
return false; return false;
} }
// check is no pending session // check is no pending session
@ -780,15 +884,15 @@ namespace transport
{ {
if (it.iTag && ts < it.iExp) if (it.iTag && ts < it.iExp)
{ {
auto it1 = m_SessionsByRouterHash.find (it.iH); auto s = FindSession (it.iH);
if (it1 != m_SessionsByRouterHash.end ()) if (s)
{ {
auto addr = it1->second->GetAddress (); auto addr = s->GetAddress ();
if (addr && addr->IsIntroducer ()) if (addr && addr->IsIntroducer ())
{ {
it1->second->Introduce (session, it.iTag); s->Introduce (session, it.iTag);
return; return;
} }
} }
else else
indices.push_back(i); indices.push_back(i);
@ -894,17 +998,16 @@ namespace transport
if (!router) return false; if (!router) return false;
auto addr = v4 ? router->GetSSU2V4Address () : router->GetSSU2V6Address (); auto addr = v4 ? router->GetSSU2V4Address () : router->GetSSU2V6Address ();
if (!addr) return false; if (!addr) return false;
auto it = m_SessionsByRouterHash.find (router->GetIdentHash ()); auto session = FindSession (router->GetIdentHash ());
if (it != m_SessionsByRouterHash.end ()) if (session)
{ {
auto remoteAddr = it->second->GetAddress (); auto remoteAddr = session->GetAddress ();
if (!remoteAddr || !remoteAddr->IsPeerTesting () || if (!remoteAddr || !remoteAddr->IsPeerTesting () ||
(v4 && !remoteAddr->IsV4 ()) || (!v4 && !remoteAddr->IsV6 ())) return false; (v4 && !remoteAddr->IsV4 ()) || (!v4 && !remoteAddr->IsV6 ())) return false;
auto s = it->second; if (session->IsEstablished ())
if (s->IsEstablished ()) GetService ().post ([session]() { session->SendPeerTest (); });
GetService ().post ([s]() { s->SendPeerTest (); });
else else
s->SetOnEstablished ([s]() { s->SendPeerTest (); }); session->SetOnEstablished ([session]() { session->SendPeerTest (); });
return true; return true;
} }
else else
@ -925,17 +1028,20 @@ namespace transport
if (ecode != boost::asio::error::operation_aborted) if (ecode != boost::asio::error::operation_aborted)
{ {
auto ts = i2p::util::GetSecondsSinceEpoch (); auto ts = i2p::util::GetSecondsSinceEpoch ();
for (auto it = m_PendingOutgoingSessions.begin (); it != m_PendingOutgoingSessions.end ();)
{ {
if (it->second->IsTerminationTimeoutExpired (ts)) std::lock_guard<std::mutex> l(m_PendingOutgoingSessionsMutex);
for (auto it = m_PendingOutgoingSessions.begin (); it != m_PendingOutgoingSessions.end ();)
{ {
//it->second->Terminate (); if (it->second->IsTerminationTimeoutExpired (ts))
std::unique_lock<std::mutex> l(m_PendingOutgoingSessionsMutex); {
it = m_PendingOutgoingSessions.erase (it); //it->second->Terminate ();
it = m_PendingOutgoingSessions.erase (it);
}
else
it++;
} }
else }
it++;
}
for (auto it: m_Sessions) for (auto it: m_Sessions)
{ {
@ -953,14 +1059,6 @@ namespace transport
it.second->CleanUp (ts); it.second->CleanUp (ts);
} }
for (auto it = m_SessionsByRouterHash.begin (); it != m_SessionsByRouterHash.begin ();)
{
if (it->second && it->second->GetState () == eSSU2SessionStateTerminated)
it = m_SessionsByRouterHash.erase (it);
else
it++;
}
ScheduleTermination (); ScheduleTermination ();
} }
} }
@ -979,12 +1077,23 @@ namespace transport
auto ts = i2p::util::GetSecondsSinceEpoch (); auto ts = i2p::util::GetSecondsSinceEpoch ();
for (auto it = m_Relays.begin (); it != m_Relays.begin ();) for (auto it = m_Relays.begin (); it != m_Relays.begin ();)
{ {
if (it->second && it->second->GetState () == eSSU2SessionStateTerminated) if (it->second.expired ())
it = m_Relays.erase (it); it = m_Relays.erase (it);
else else
it++; it++;
} }
for (auto it = m_PeerTests.begin (); it != m_PeerTests.end ();)
{
if (ts > it->second.second + SSU2_PEER_TEST_EXPIRATION_TIMEOUT || it->second.first.expired ())
{
LogPrint (eLogInfo, "SSU2: Peer test nonce ", it->first, " was not responded in ", SSU2_PEER_TEST_EXPIRATION_TIMEOUT, " seconds or session invalid. Deleted");
it = m_PeerTests.erase (it);
}
else
it++;
}
for (auto it = m_IncomingTokens.begin (); it != m_IncomingTokens.end (); ) for (auto it = m_IncomingTokens.begin (); it != m_IncomingTokens.end (); )
{ {
if (ts > it->second.second) if (ts > it->second.second)
@ -1001,7 +1110,35 @@ namespace transport
it++; it++;
} }
for (auto it = m_ConnectedRecently.begin (); it != m_ConnectedRecently.end (); )
{
if (ts > it->second + SSU2_HOLE_PUNCH_EXPIRATION)
it = m_ConnectedRecently.erase (it);
else
it++;
}
for (auto it = m_RequestedPeerTests.begin (); it != m_RequestedPeerTests.end ();)
{
if (ts > it->second.second + SSU2_PEER_TEST_EXPIRATION_TIMEOUT)
it = m_RequestedPeerTests.erase (it);
else
it++;
}
{
std::lock_guard<std::mutex> l(m_SessionsByRouterHashMutex);
for (auto it = m_SessionsByRouterHash.begin (); it != m_SessionsByRouterHash.begin ();)
{
if (it->second.expired ())
it = m_SessionsByRouterHash.erase (it);
else
it++;
}
}
m_PacketsPool.CleanUpMt (); m_PacketsPool.CleanUpMt ();
m_PacketsArrayPool.CleanUpMt ();
m_SentPacketsPool.CleanUp (); m_SentPacketsPool.CleanUp ();
m_IncompleteMessagesPool.CleanUp (); m_IncompleteMessagesPool.CleanUp ();
m_FragmentsPool.CleanUp (); m_FragmentsPool.CleanUp ();
@ -1085,97 +1222,100 @@ namespace transport
} }
std::vector<std::shared_ptr<SSU2Session> > SSU2Server::FindIntroducers (int maxNumIntroducers, std::vector<std::shared_ptr<SSU2Session> > SSU2Server::FindIntroducers (int maxNumIntroducers,
bool v4, const std::unordered_set<i2p::data::IdentHash>& excluded) const bool v4, const std::unordered_set<i2p::data::IdentHash>& excluded)
{ {
std::vector<std::shared_ptr<SSU2Session> > ret; std::vector<std::shared_ptr<SSU2Session> > ret;
if (maxNumIntroducers <= 0) return ret; if (maxNumIntroducers <= 0 || m_Sessions.empty ()) return ret;
auto newer = [](const std::shared_ptr<SSU2Session>& s1, const std::shared_ptr<SSU2Session>& s2) -> bool
{ std::vector<std::shared_ptr<SSU2Session> > eligible;
auto t1 = s1->GetCreationTime (), t2 = s2->GetCreationTime (); eligible.reserve (m_Sessions.size ()/2);
return (t1 != t2) ? (t1 > t2) : (s1->GetConnID () > s2->GetConnID ()); auto ts = i2p::util::GetSecondsSinceEpoch ();
};
std::set<std::shared_ptr<SSU2Session>, decltype (newer)> introducers(newer);
for (const auto& s : m_Sessions) for (const auto& s : m_Sessions)
{ {
if (s.second->IsEstablished () && (s.second->GetRelayTag () && s.second->IsOutgoing ()) && if (s.second->IsEstablished () && (s.second->GetRelayTag () && s.second->IsOutgoing ()) &&
ts < s.second->GetCreationTime () + SSU2_TO_INTRODUCER_SESSION_DURATION/2 &&
!excluded.count (s.second->GetRemoteIdentity ()->GetIdentHash ()) && !excluded.count (s.second->GetRemoteIdentity ()->GetIdentHash ()) &&
((v4 && (s.second->GetRemoteTransports () & i2p::data::RouterInfo::eSSU2V4)) || ((v4 && (s.second->GetRemoteTransports () & i2p::data::RouterInfo::eSSU2V4)) ||
(!v4 && (s.second->GetRemoteTransports () & i2p::data::RouterInfo::eSSU2V6)))) (!v4 && (s.second->GetRemoteTransports () & i2p::data::RouterInfo::eSSU2V6))))
introducers.insert (s.second); eligible.push_back (s.second);
} }
int i = 0;
for (auto it: introducers) if (eligible.size () <= (size_t)maxNumIntroducers)
{ return eligible;
ret.push_back (it); else
i++; std::sample (eligible.begin(), eligible.end(), std::back_inserter(ret), maxNumIntroducers, m_Rng);
if (i >= maxNumIntroducers) break;
}
return ret; return ret;
} }
void SSU2Server::UpdateIntroducers (bool v4) void SSU2Server::UpdateIntroducers (bool v4)
{ {
uint32_t ts = i2p::util::GetSecondsSinceEpoch (); uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
std::list<i2p::data::IdentHash> newList, impliedList; std::list<std::pair<i2p::data::IdentHash, uint32_t> > newList, impliedList;
auto& introducers = v4 ? m_Introducers : m_IntroducersV6; auto& introducers = v4 ? m_Introducers : m_IntroducersV6;
std::unordered_set<i2p::data::IdentHash> excluded; std::unordered_set<i2p::data::IdentHash> excluded;
for (const auto& it : introducers) for (const auto& [ident, tag] : introducers)
{ {
std::shared_ptr<SSU2Session> session; std::shared_ptr<SSU2Session> session = FindSession (ident);
auto it1 = m_SessionsByRouterHash.find (it); if (session)
if (it1 != m_SessionsByRouterHash.end ()) excluded.insert (ident);
{ if (session)
session = it1->second;
excluded.insert (it);
}
if (session && session->IsEstablished () && session->GetRelayTag () && session->IsOutgoing () && // still session with introducer?
ts < session->GetCreationTime () + SSU2_TO_INTRODUCER_SESSION_EXPIRATION)
{ {
session->SendKeepAlive (); if (session->IsEstablished () && session->GetRelayTag () && session->IsOutgoing () && // still session with introducer?
if (ts < session->GetCreationTime () + SSU2_TO_INTRODUCER_SESSION_DURATION) ts < session->GetCreationTime () + SSU2_TO_INTRODUCER_SESSION_EXPIRATION)
newList.push_back (it);
else
{ {
impliedList.push_back (it); // keep in introducers list, but not publish session->SendKeepAlive ();
session = nullptr; if (ts < session->GetCreationTime () + SSU2_TO_INTRODUCER_SESSION_DURATION)
} {
newList.push_back ({ident, session->GetRelayTag ()});
if (tag != session->GetRelayTag ())
{
LogPrint (eLogDebug, "SSU2: Introducer session to ", session->GetIdentHashBase64() , " was replaced. iTag ", tag, "->", session->GetRelayTag ());
i2p::context.UpdateSSU2Introducer (ident, v4, session->GetRelayTag (),
session->GetCreationTime () + SSU2_TO_INTRODUCER_SESSION_EXPIRATION);
}
}
else
{
impliedList.push_back ({ident, session->GetRelayTag ()}); // keep in introducers list, but not publish
session = nullptr;
}
}
else
session = nullptr;
} }
else
session = nullptr;
if (!session) if (!session)
i2p::context.RemoveSSU2Introducer (it, v4); i2p::context.RemoveSSU2Introducer (ident, v4);
} }
int numOldSessions = 0;
if (newList.size () < SSU2_MAX_NUM_INTRODUCERS) if (newList.size () < SSU2_MAX_NUM_INTRODUCERS)
{ {
auto sessions = FindIntroducers (SSU2_MAX_NUM_INTRODUCERS - newList.size (), v4, excluded); auto sessions = FindIntroducers (SSU2_MAX_NUM_INTRODUCERS - newList.size (), v4, excluded);
if (sessions.empty () && !introducers.empty ()) if (sessions.empty () && !impliedList.empty ())
{ {
// bump creation time for previous introducers if no new sessions found
LogPrint (eLogDebug, "SSU2: No new introducers found. Trying to reuse existing"); LogPrint (eLogDebug, "SSU2: No new introducers found. Trying to reuse existing");
impliedList.clear (); for (const auto& it : impliedList)
for (auto& it : introducers)
{ {
auto it1 = m_SessionsByRouterHash.find (it); auto session = FindSession (it.first);
if (it1 != m_SessionsByRouterHash.end ()) if (session)
{ {
auto session = it1->second; if (std::find_if (newList.begin (), newList.end (),
if (session->IsEstablished () && session->GetRelayTag () && session->IsOutgoing ()) [&ident = it.first](const auto& s){ return ident == s.first; }) == newList.end ())
{ {
session->SetCreationTime (session->GetCreationTime () + SSU2_TO_INTRODUCER_SESSION_DURATION); sessions.push_back (session);
if (std::find (newList.begin (), newList.end (), it) == newList.end ()) numOldSessions++;
sessions.push_back (session); }
}
} }
} }
impliedList.clear ();
} }
for (const auto& it : sessions) for (const auto& it : sessions)
{ {
uint32_t tag = it->GetRelayTag (); uint32_t tag = it->GetRelayTag ();
uint32_t exp = it->GetCreationTime () + SSU2_TO_INTRODUCER_SESSION_EXPIRATION; uint32_t exp = it->GetCreationTime () + SSU2_TO_INTRODUCER_SESSION_EXPIRATION;
if (!tag || ts + SSU2_TO_INTRODUCER_SESSION_DURATION/2 > exp) if (!tag && ts >= exp)
continue; // don't pick too old session for introducer continue; // don't publish expired introducer
i2p::data::RouterInfo::Introducer introducer; i2p::data::RouterInfo::Introducer introducer;
introducer.iTag = tag; introducer.iTag = tag;
introducer.iH = it->GetRemoteIdentity ()->GetIdentHash (); introducer.iH = it->GetRemoteIdentity ()->GetIdentHash ();
@ -1185,16 +1325,28 @@ namespace transport
{ {
LogPrint (eLogDebug, "SSU2: Introducer added ", it->GetRelayTag (), " at ", LogPrint (eLogDebug, "SSU2: Introducer added ", it->GetRelayTag (), " at ",
i2p::data::GetIdentHashAbbreviation (it->GetRemoteIdentity ()->GetIdentHash ())); i2p::data::GetIdentHashAbbreviation (it->GetRemoteIdentity ()->GetIdentHash ()));
newList.push_back (it->GetRemoteIdentity ()->GetIdentHash ()); newList.push_back ({ it->GetRemoteIdentity ()->GetIdentHash (), tag });
it->SendKeepAlive ();
if (newList.size () >= SSU2_MAX_NUM_INTRODUCERS) break; if (newList.size () >= SSU2_MAX_NUM_INTRODUCERS) break;
} }
} }
} }
introducers = newList; introducers = newList;
if (introducers.size () < SSU2_MAX_NUM_INTRODUCERS) if (introducers.size () < SSU2_MAX_NUM_INTRODUCERS || numOldSessions)
{ {
for (auto i = introducers.size (); i < SSU2_MAX_NUM_INTRODUCERS; i++) // we need to create more sessions with relay tag
// exclude all existing sessions
excluded.clear ();
{
std::lock_guard<std::mutex> l(m_SessionsByRouterHashMutex);
for (const auto& [ident, s] : m_SessionsByRouterHash)
excluded.insert (ident);
}
// sesssion 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); auto introducer = i2p::data::netdb.GetRandomSSU2Introducer (v4, excluded);
if (introducer) if (introducer)

45
libi2pd/SSU2.h

@ -12,6 +12,7 @@
#include <unordered_map> #include <unordered_map>
#include <unordered_set> #include <unordered_set>
#include <vector> #include <vector>
#include <array>
#include <mutex> #include <mutex>
#include <random> #include <random>
#include "util.h" #include "util.h"
@ -39,6 +40,8 @@ namespace transport
const int SSU2_KEEP_ALIVE_INTERVAL = 15; // in seconds const int SSU2_KEEP_ALIVE_INTERVAL = 15; // in seconds
const int SSU2_KEEP_ALIVE_INTERVAL_VARIANCE = 4; // 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_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;
class SSU2Server: private i2p::util::RunnableServiceWithWork class SSU2Server: private i2p::util::RunnableServiceWithWork
{ {
@ -49,6 +52,20 @@ namespace transport
boost::asio::ip::udp::endpoint from; 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 class ReceiveService: public i2p::util::RunnableService
{ {
public: public:
@ -72,6 +89,8 @@ namespace transport
bool UsesProxy () const { return m_IsThroughProxy; }; bool UsesProxy () const { return m_IsThroughProxy; };
bool IsSupported (const boost::asio::ip::address& addr) const; bool IsSupported (const boost::asio::ip::address& addr) const;
uint16_t GetPort (bool v4) const; uint16_t GetPort (bool v4) const;
bool IsConnectedRecently (const boost::asio::ip::udp::endpoint& ep);
void AddConnectedRecently (const boost::asio::ip::udp::endpoint& ep, uint64_t ts);
std::mt19937& GetRng () { return m_Rng; } std::mt19937& GetRng () { return m_Rng; }
bool IsMaxNumIntroducers (bool v4) const { return (v4 ? m_Introducers.size () : m_IntroducersV6.size ()) >= SSU2_MAX_NUM_INTRODUCERS; } bool IsMaxNumIntroducers (bool v4) const { return (v4 ? m_Introducers.size () : m_IntroducersV6.size ()) >= SSU2_MAX_NUM_INTRODUCERS; }
bool IsSyncClockFromPeers () const { return m_IsSyncClockFromPeers; }; bool IsSyncClockFromPeers () const { return m_IsSyncClockFromPeers; };
@ -79,10 +98,11 @@ namespace transport
void AddSession (std::shared_ptr<SSU2Session> session); void AddSession (std::shared_ptr<SSU2Session> session);
void RemoveSession (uint64_t connID); void RemoveSession (uint64_t connID);
void RequestRemoveSession (uint64_t connID);
void AddSessionByRouterHash (std::shared_ptr<SSU2Session> session); void AddSessionByRouterHash (std::shared_ptr<SSU2Session> session);
bool AddPendingOutgoingSession (std::shared_ptr<SSU2Session> session); bool AddPendingOutgoingSession (std::shared_ptr<SSU2Session> session);
void RemovePendingOutgoingSession (const boost::asio::ip::udp::endpoint& ep); void RemovePendingOutgoingSession (const boost::asio::ip::udp::endpoint& ep);
std::shared_ptr<SSU2Session> FindSession (const i2p::data::IdentHash& ident) const; std::shared_ptr<SSU2Session> FindSession (const i2p::data::IdentHash& ident);
std::shared_ptr<SSU2Session> FindPendingOutgoingSession (const boost::asio::ip::udp::endpoint& ep) const; std::shared_ptr<SSU2Session> FindPendingOutgoingSession (const boost::asio::ip::udp::endpoint& ep) const;
std::shared_ptr<SSU2Session> GetRandomPeerTestSession (i2p::data::RouterInfo::CompatibleTransports remoteTransports, std::shared_ptr<SSU2Session> GetRandomPeerTestSession (i2p::data::RouterInfo::CompatibleTransports remoteTransports,
const i2p::data::IdentHash& excluded); const i2p::data::IdentHash& excluded);
@ -91,6 +111,12 @@ namespace transport
void RemoveRelay (uint32_t tag); void RemoveRelay (uint32_t tag);
std::shared_ptr<SSU2Session> FindRelaySession (uint32_t tag); std::shared_ptr<SSU2Session> FindRelaySession (uint32_t tag);
bool AddPeerTest (uint32_t nonce, std::shared_ptr<SSU2Session> aliceSession, uint64_t ts);
std::shared_ptr<SSU2Session> GetPeerTest (uint32_t nonce);
bool AddRequestedPeerTest (uint32_t nonce, std::shared_ptr<SSU2PeerTestSession> session, uint64_t ts);
std::shared_ptr<SSU2PeerTestSession> GetRequestedPeerTest (uint32_t nonce);
void Send (const uint8_t * header, size_t headerLen, const uint8_t * payload, size_t payloadLen, void Send (const uint8_t * header, size_t headerLen, const uint8_t * payload, size_t payloadLen,
const boost::asio::ip::udp::endpoint& to); const boost::asio::ip::udp::endpoint& to);
void Send (const uint8_t * header, size_t headerLen, const uint8_t * headerX, size_t headerXLen, void Send (const uint8_t * header, size_t headerLen, const uint8_t * headerX, size_t headerXLen,
@ -119,7 +145,7 @@ namespace transport
void HandleReceivedFrom (const boost::system::error_code& ecode, size_t bytes_transferred, void HandleReceivedFrom (const boost::system::error_code& ecode, size_t bytes_transferred,
Packet * packet, boost::asio::ip::udp::socket& socket); Packet * packet, boost::asio::ip::udp::socket& socket);
void HandleReceivedPacket (Packet * packet); void HandleReceivedPacket (Packet * packet);
void HandleReceivedPackets (std::vector<Packet *> packets); void HandleReceivedPackets (Packets * packets);
void ProcessNextPacket (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint); void ProcessNextPacket (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint);
void ScheduleTermination (); void ScheduleTermination ();
@ -133,7 +159,7 @@ namespace transport
void ConnectThroughIntroducer (std::shared_ptr<SSU2Session> session); void ConnectThroughIntroducer (std::shared_ptr<SSU2Session> session);
std::vector<std::shared_ptr<SSU2Session> > FindIntroducers (int maxNumIntroducers, std::vector<std::shared_ptr<SSU2Session> > FindIntroducers (int maxNumIntroducers,
bool v4, const std::unordered_set<i2p::data::IdentHash>& excluded) const; bool v4, const std::unordered_set<i2p::data::IdentHash>& excluded);
void UpdateIntroducers (bool v4); void UpdateIntroducers (bool v4);
void ScheduleIntroducersUpdateTimer (); void ScheduleIntroducersUpdateTimer ();
void HandleIntroducersUpdateTimer (const boost::system::error_code& ecode, bool v4); void HandleIntroducersUpdateTimer (const boost::system::error_code& ecode, bool v4);
@ -156,13 +182,16 @@ namespace transport
boost::asio::ip::udp::socket m_SocketV4, m_SocketV6; boost::asio::ip::udp::socket m_SocketV4, m_SocketV6;
boost::asio::ip::address m_AddressV4, m_AddressV6; boost::asio::ip::address m_AddressV4, m_AddressV6;
std::unordered_map<uint64_t, std::shared_ptr<SSU2Session> > m_Sessions; std::unordered_map<uint64_t, std::shared_ptr<SSU2Session> > m_Sessions;
std::unordered_map<i2p::data::IdentHash, std::shared_ptr<SSU2Session> > m_SessionsByRouterHash; std::unordered_map<i2p::data::IdentHash, std::weak_ptr<SSU2Session> > m_SessionsByRouterHash;
mutable std::mutex m_SessionsByRouterHashMutex;
std::map<boost::asio::ip::udp::endpoint, std::shared_ptr<SSU2Session> > m_PendingOutgoingSessions; std::map<boost::asio::ip::udp::endpoint, std::shared_ptr<SSU2Session> > m_PendingOutgoingSessions;
mutable std::mutex m_PendingOutgoingSessionsMutex; mutable std::mutex m_PendingOutgoingSessionsMutex;
std::map<boost::asio::ip::udp::endpoint, std::pair<uint64_t, uint32_t> > m_IncomingTokens, m_OutgoingTokens; // remote endpoint -> (token, expires in seconds) std::map<boost::asio::ip::udp::endpoint, std::pair<uint64_t, uint32_t> > m_IncomingTokens, m_OutgoingTokens; // remote endpoint -> (token, expires in seconds)
std::map<uint32_t, std::shared_ptr<SSU2Session> > m_Relays; // we are introducer, relay tag -> session std::unordered_map<uint32_t, std::weak_ptr<SSU2Session> > m_Relays; // we are introducer, relay tag -> session
std::list<i2p::data::IdentHash> m_Introducers, m_IntroducersV6; // introducers we are connected to 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<Packet> m_PacketsPool;
i2p::util::MemoryPoolMt<Packets> m_PacketsArrayPool;
i2p::util::MemoryPool<SSU2SentPacket> m_SentPacketsPool; i2p::util::MemoryPool<SSU2SentPacket> m_SentPacketsPool;
i2p::util::MemoryPool<SSU2IncompleteMessage> m_IncompleteMessagesPool; i2p::util::MemoryPool<SSU2IncompleteMessage> m_IncompleteMessagesPool;
i2p::util::MemoryPool<SSU2IncompleteMessage::Fragment> m_FragmentsPool; i2p::util::MemoryPool<SSU2IncompleteMessage::Fragment> m_FragmentsPool;
@ -174,7 +203,9 @@ namespace transport
int64_t m_PendingTimeOffset; // during peer test int64_t m_PendingTimeOffset; // during peer test
std::shared_ptr<const i2p::data::IdentityEx> m_PendingTimeOffsetFrom; std::shared_ptr<const i2p::data::IdentityEx> m_PendingTimeOffsetFrom;
std::mt19937 m_Rng; std::mt19937 m_Rng;
std::map<boost::asio::ip::udp::endpoint, uint64_t> m_ConnectedRecently; // endpoint -> last activity time in seconds
std::unordered_map<uint32_t, std::pair <std::weak_ptr<SSU2PeerTestSession>, uint64_t > > m_RequestedPeerTests; // nonce->(Alice, timestamp)
// proxy // proxy
bool m_IsThroughProxy; bool m_IsThroughProxy;
uint8_t m_UDPRequestHeader[SOCKS5_UDP_IPV6_REQUEST_HEADER_SIZE]; uint8_t m_UDPRequestHeader[SOCKS5_UDP_IPV6_REQUEST_HEADER_SIZE];

648
libi2pd/SSU2Session.cpp

@ -18,6 +18,12 @@ namespace i2p
{ {
namespace transport 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) void SSU2IncompleteMessage::AttachNextFragment (const uint8_t * fragment, size_t fragmentSize)
{ {
if (msg->len + fragmentSize > msg->maxLen) if (msg->len + fragmentSize > msg->maxLen)
@ -79,7 +85,7 @@ namespace transport
} }
SSU2Session::SSU2Session (SSU2Server& server, std::shared_ptr<const i2p::data::RouterInfo> in_RemoteRouter, SSU2Session::SSU2Session (SSU2Server& server, std::shared_ptr<const i2p::data::RouterInfo> in_RemoteRouter,
std::shared_ptr<const i2p::data::RouterInfo::Address> addr): std::shared_ptr<const i2p::data::RouterInfo::Address> addr, bool noise):
TransportSession (in_RemoteRouter, SSU2_CONNECT_TIMEOUT), TransportSession (in_RemoteRouter, SSU2_CONNECT_TIMEOUT),
m_Server (server), m_Address (addr), m_RemoteTransports (0), m_RemotePeerTestTransports (0), m_Server (server), m_Address (addr), m_RemoteTransports (0), m_RemotePeerTestTransports (0),
m_DestConnID (0), m_SourceConnID (0), m_State (eSSU2SessionStateUnknown), m_DestConnID (0), m_SourceConnID (0), m_State (eSSU2SessionStateUnknown),
@ -93,11 +99,13 @@ namespace transport
m_MaxPayloadSize (SSU2_MIN_PACKET_SIZE - IPV6_HEADER_SIZE - UDP_HEADER_SIZE - 32), // min size m_MaxPayloadSize (SSU2_MIN_PACKET_SIZE - IPV6_HEADER_SIZE - UDP_HEADER_SIZE - 32), // min size
m_LastResendTime (0), m_LastResendAttemptTime (0) m_LastResendTime (0), m_LastResendAttemptTime (0)
{ {
m_NoiseState.reset (new i2p::crypto::NoiseSymmetricState); if (noise)
m_NoiseState.reset (new i2p::crypto::NoiseSymmetricState);
if (in_RemoteRouter && m_Address) if (in_RemoteRouter && m_Address)
{ {
// outgoing // outgoing
InitNoiseXKState1 (*m_NoiseState, m_Address->s); if (noise)
InitNoiseXKState1 (*m_NoiseState, m_Address->s);
m_RemoteEndpoint = boost::asio::ip::udp::endpoint (m_Address->host, m_Address->port); m_RemoteEndpoint = boost::asio::ip::udp::endpoint (m_Address->host, m_Address->port);
m_RemoteTransports = in_RemoteRouter->GetCompatibleTransports (false); m_RemoteTransports = in_RemoteRouter->GetCompatibleTransports (false);
if (in_RemoteRouter->IsSSU2PeerTesting (true)) m_RemotePeerTestTransports |= i2p::data::RouterInfo::eSSU2V4; if (in_RemoteRouter->IsSSU2PeerTesting (true)) m_RemotePeerTestTransports |= i2p::data::RouterInfo::eSSU2V4;
@ -108,7 +116,8 @@ namespace transport
else else
{ {
// incoming // incoming
InitNoiseXKState1 (*m_NoiseState, i2p::context.GetSSU2StaticPublicKey ()); if (noise)
InitNoiseXKState1 (*m_NoiseState, i2p::context.GetSSU2StaticPublicKey ());
} }
} }
@ -161,40 +170,47 @@ namespace transport
if (!session || !relayTag) return false; if (!session || !relayTag) return false;
// find local address to introduce // find local address to introduce
auto localAddress = session->FindLocalAddress (); auto localAddress = session->FindLocalAddress ();
if (!localAddress) return false; if (!localAddress || localAddress->host.is_unspecified () || !localAddress->port)
{
// can't introduce invalid endpoint
LogPrint (eLogWarning, "SSU2: Can't find local address to introduce");
return false;
}
// create nonce // create nonce
uint32_t nonce; uint32_t nonce;
RAND_bytes ((uint8_t *)&nonce, 4); RAND_bytes ((uint8_t *)&nonce, 4);
auto ts = i2p::util::GetSecondsSinceEpoch (); auto ts = i2p::util::GetMillisecondsSinceEpoch ();
// payload // payload
uint8_t payload[SSU2_MAX_PACKET_SIZE]; auto packet = m_Server.GetSentPacketsPool ().AcquireShared ();
size_t payloadSize = 0; uint8_t * payload = packet->payload;
payload[0] = eSSU2BlkRelayRequest; payload[0] = eSSU2BlkRelayRequest;
payload[3] = 0; // flag payload[3] = 0; // flag
htobe32buf (payload + 4, nonce); htobe32buf (payload + 4, nonce);
htobe32buf (payload + 8, relayTag); htobe32buf (payload + 8, relayTag);
htobe32buf (payload + 12, ts); htobe32buf (payload + 12, ts/1000);
payload[16] = 2; // ver payload[16] = 2; // ver
size_t asz = CreateEndpoint (payload + 18, m_MaxPayloadSize - 18, boost::asio::ip::udp::endpoint (localAddress->host, localAddress->port)); size_t asz = CreateEndpoint (payload + 18, m_MaxPayloadSize - 18, boost::asio::ip::udp::endpoint (localAddress->host, localAddress->port));
if (!asz) return false; if (!asz) return false;
payload[17] = asz; payload[17] = asz;
payloadSize += asz + 18; packet->payloadSize = asz + 18;
SignedData s; SignedData s;
s.Insert ((const uint8_t *)"RelayRequestData", 16); // prologue s.Insert ((const uint8_t *)"RelayRequestData", 16); // prologue
s.Insert (GetRemoteIdentity ()->GetIdentHash (), 32); // bhash s.Insert (GetRemoteIdentity ()->GetIdentHash (), 32); // bhash
s.Insert (session->GetRemoteIdentity ()->GetIdentHash (), 32); // chash s.Insert (session->GetRemoteIdentity ()->GetIdentHash (), 32); // chash
s.Insert (payload + 4, 14 + asz); // nonce, relay tag, timestamp, ver, asz and Alice's endpoint s.Insert (payload + 4, 14 + asz); // nonce, relay tag, timestamp, ver, asz and Alice's endpoint
s.Sign (i2p::context.GetPrivateKeys (), payload + payloadSize); s.Sign (i2p::context.GetPrivateKeys (), payload + packet->payloadSize);
payloadSize += i2p::context.GetIdentity ()->GetSignatureLen (); packet->payloadSize += i2p::context.GetIdentity ()->GetSignatureLen ();
htobe16buf (payload + 1, payloadSize - 3); // size htobe16buf (payload + 1, packet->payloadSize - 3); // size
payloadSize += CreatePaddingBlock (payload + payloadSize, m_MaxPayloadSize - payloadSize); packet->payloadSize += CreatePaddingBlock (payload + packet->payloadSize, m_MaxPayloadSize - packet->payloadSize);
// send // send
m_RelaySessions.emplace (nonce, std::make_pair (session, ts)); m_RelaySessions.emplace (nonce, std::make_pair (session, ts/1000));
session->m_SourceConnID = htobe64 (((uint64_t)nonce << 32) | nonce); session->m_SourceConnID = htobe64 (((uint64_t)nonce << 32) | nonce);
session->m_DestConnID = ~session->m_SourceConnID; session->m_DestConnID = ~session->m_SourceConnID;
m_Server.AddSession (session); m_Server.AddSession (session);
SendData (payload, payloadSize); int32_t packetNum = SendData (packet->payload, packet->payloadSize);
packet->sendTime = ts;
m_SentPackets.emplace (packetNum, packet);
return true; return true;
} }
@ -208,15 +224,22 @@ namespace transport
{ {
if (m_State == eSSU2SessionStateIntroduced) if (m_State == eSSU2SessionStateIntroduced)
{ {
// create new connID // we are Alice
uint64_t oldConnID = GetConnID (); // keep ConnIDs used for introduction, because Charlie waits for SessionRequest from us
RAND_bytes ((uint8_t *)&m_DestConnID, 8);
RAND_bytes ((uint8_t *)&m_SourceConnID, 8);
// connect
m_State = eSSU2SessionStateTokenReceived; m_State = eSSU2SessionStateTokenReceived;
m_Server.AddPendingOutgoingSession (shared_from_this ()); // move session to pending outgoing
m_Server.RemoveSession (oldConnID); if (m_Server.AddPendingOutgoingSession (shared_from_this ()))
Connect (); {
m_Server.RemoveSession (GetConnID ());
// connect
LogPrint (eLogDebug, "SSU2: Connecting after introduction to ", GetIdentHashBase64());
Connect ();
}
else
{
LogPrint (eLogError, "SSU2: Session ", GetConnID (), " is already pending");
m_Server.RequestRemoveSession (GetConnID ());
}
} }
} }
@ -227,11 +250,9 @@ namespace transport
RAND_bytes ((uint8_t *)&nonce, 4); RAND_bytes ((uint8_t *)&nonce, 4);
auto ts = i2p::util::GetMillisecondsSinceEpoch (); auto ts = i2p::util::GetMillisecondsSinceEpoch ();
// session for message 5 // session for message 5
auto session = std::make_shared<SSU2Session> (m_Server); auto session = std::make_shared<SSU2PeerTestSession> (m_Server,
session->SetState (eSSU2SessionStatePeerTest); htobe64 (((uint64_t)nonce << 32) | nonce), 0);
m_PeerTests.emplace (nonce, std::make_pair (session, ts/1000)); m_Server.AddRequestedPeerTest (nonce, session, ts/1000);
session->m_SourceConnID = htobe64 (((uint64_t)nonce << 32) | nonce);
session->m_DestConnID = ~session->m_SourceConnID;
m_Server.AddSession (session); m_Server.AddSession (session);
// peer test block // peer test block
auto packet = m_Server.GetSentPacketsPool ().AcquireShared (); auto packet = m_Server.GetSentPacketsPool ().AcquireShared ();
@ -252,7 +273,7 @@ namespace transport
{ {
uint8_t payload[20]; uint8_t payload[20];
size_t payloadSize = CreatePaddingBlock (payload, 20, 8); size_t payloadSize = CreatePaddingBlock (payload, 20, 8);
SendData (payload, payloadSize); SendData (payload, payloadSize, SSU2_FLAG_IMMEDIATE_ACK_REQUESTED);
} }
} }
@ -265,6 +286,7 @@ namespace transport
m_OnEstablished = nullptr; m_OnEstablished = nullptr;
if (m_RelayTag) if (m_RelayTag)
m_Server.RemoveRelay (m_RelayTag); m_Server.RemoveRelay (m_RelayTag);
m_Server.AddConnectedRecently (m_RemoteEndpoint, GetLastActivityTimestamp ());
m_SentHandshakePacket.reset (nullptr); m_SentHandshakePacket.reset (nullptr);
m_SessionConfirmedFragment.reset (nullptr); m_SessionConfirmedFragment.reset (nullptr);
m_PathChallenge.reset (nullptr); m_PathChallenge.reset (nullptr);
@ -275,20 +297,15 @@ namespace transport
m_SentPackets.clear (); m_SentPackets.clear ();
m_IncompleteMessages.clear (); m_IncompleteMessages.clear ();
m_RelaySessions.clear (); m_RelaySessions.clear ();
m_PeerTests.clear ();
m_ReceivedI2NPMsgIDs.clear (); m_ReceivedI2NPMsgIDs.clear ();
m_Server.RemoveSession (m_SourceConnID); m_Server.RemoveSession (m_SourceConnID);
transports.PeerDisconnected (shared_from_this ()); transports.PeerDisconnected (shared_from_this ());
auto remoteIdentity = GetRemoteIdentity (); auto remoteIdentity = GetRemoteIdentity ();
if (remoteIdentity) if (remoteIdentity)
{
LogPrint (eLogDebug, "SSU2: Session with ", GetRemoteEndpoint (), LogPrint (eLogDebug, "SSU2: Session with ", GetRemoteEndpoint (),
" (", i2p::data::GetIdentHashAbbreviation (remoteIdentity->GetIdentHash ()), ") terminated"); " (", i2p::data::GetIdentHashAbbreviation (remoteIdentity->GetIdentHash ()), ") terminated");
}
else else
{
LogPrint (eLogDebug, "SSU2: Session with ", GetRemoteEndpoint (), " terminated"); LogPrint (eLogDebug, "SSU2: Session with ", GetRemoteEndpoint (), " terminated");
}
} }
} }
@ -587,11 +604,7 @@ namespace transport
if (!resentPackets.empty ()) if (!resentPackets.empty ())
{ {
m_LastResendTime = ts; m_LastResendTime = ts;
#if (__cplusplus >= 201703L) // C++ 17 or higher
m_SentPackets.merge (resentPackets); m_SentPackets.merge (resentPackets);
#else
m_SentPackets.insert (resentPackets.begin (), resentPackets.end ());
#endif
m_WindowSize >>= 1; // /2 m_WindowSize >>= 1; // /2
if (m_WindowSize < SSU2_MIN_WINDOW_SIZE) m_WindowSize = SSU2_MIN_WINDOW_SIZE; if (m_WindowSize < SSU2_MIN_WINDOW_SIZE) m_WindowSize = SSU2_MIN_WINDOW_SIZE;
return resentPackets.size (); return resentPackets.size ();
@ -1157,7 +1170,7 @@ namespace transport
if (profile) // older router? if (profile) // older router?
profile->Duplicated (); // mark router as duplicated in profile profile->Duplicated (); // mark router as duplicated in profile
else else
LogPrint (eLogError, "SSU2: Host mismatch between published address ", m_Address->host, LogPrint (eLogInfo, "SSU2: Host mismatch between published address ", m_Address->host,
" and actual endpoint ", m_RemoteEndpoint.address (), " from ", i2p::data::GetIdentHashAbbreviation (ri->GetIdentHash ())); " and actual endpoint ", m_RemoteEndpoint.address (), " from ", i2p::data::GetIdentHashAbbreviation (ri->GetIdentHash ()));
return false; return false;
} }
@ -1419,83 +1432,17 @@ namespace transport
return false; return false;
} }
HandlePayload (payload, len - 48); HandlePayload (payload, len - 48);
m_IsDataReceived = false;
// connect to Charlie // connect to Charlie
ConnectAfterIntroduction (); ConnectAfterIntroduction ();
return true; return true;
} }
void SSU2Session::SendPeerTest (uint8_t msg, const uint8_t * signedData, size_t signedDataLen, const uint8_t * introKey)
{
Header header;
uint8_t h[32], payload[SSU2_MAX_PACKET_SIZE];
// fill packet
header.h.connID = m_DestConnID; // 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);
memcpy (h + 16, &m_SourceConnID, 8); // 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, m_MaxPayloadSize - payloadSize, m_RemoteEndpoint);
payloadSize += CreatePeerTestBlock (payload + payloadSize, m_MaxPayloadSize - payloadSize,
msg, eSSU2PeerTestCodeAccept, nullptr, signedData, signedDataLen);
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, m_RemoteEndpoint);
}
bool SSU2Session::ProcessPeerTest (uint8_t * buf, size_t len) bool SSU2Session::ProcessPeerTest (uint8_t * buf, size_t len)
{ {
// we are Alice or Charlie LogPrint (eLogWarning, "SSU2: Unexpected peer test message for this session type");
Header header; return false;
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);
m_DestConnID = 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);
return true;
} }
uint32_t SSU2Session::SendData (const uint8_t * buf, size_t len, uint8_t flags) uint32_t SSU2Session::SendData (const uint8_t * buf, size_t len, uint8_t flags)
@ -1561,6 +1508,7 @@ namespace transport
return; return;
} }
UpdateNumReceivedBytes (len); UpdateNumReceivedBytes (len);
if (header.h.flags[0] & SSU2_FLAG_IMMEDIATE_ACK_REQUESTED) m_IsDataReceived = true;
if (!packetNum || UpdateReceivePacketNum (packetNum)) if (!packetNum || UpdateReceivePacketNum (packetNum))
HandlePayload (payload, payloadSize); HandlePayload (payload, payloadSize);
} }
@ -1636,14 +1584,17 @@ namespace transport
case eSSU2BlkRelayRequest: case eSSU2BlkRelayRequest:
LogPrint (eLogDebug, "SSU2: RelayRequest"); LogPrint (eLogDebug, "SSU2: RelayRequest");
HandleRelayRequest (buf + offset, size); HandleRelayRequest (buf + offset, size);
m_IsDataReceived = true;
break; break;
case eSSU2BlkRelayResponse: case eSSU2BlkRelayResponse:
LogPrint (eLogDebug, "SSU2: RelayResponse"); LogPrint (eLogDebug, "SSU2: RelayResponse");
HandleRelayResponse (buf + offset, size); HandleRelayResponse (buf + offset, size);
m_IsDataReceived = true;
break; break;
case eSSU2BlkRelayIntro: case eSSU2BlkRelayIntro:
LogPrint (eLogDebug, "SSU2: RelayIntro"); LogPrint (eLogDebug, "SSU2: RelayIntro");
HandleRelayIntro (buf + offset, size); HandleRelayIntro (buf + offset, size);
m_IsDataReceived = true;
break; break;
case eSSU2BlkPeerTest: case eSSU2BlkPeerTest:
LogPrint (eLogDebug, "SSU2: PeerTest msg=", (int)buf[offset], " code=", (int)buf[offset+1]); LogPrint (eLogDebug, "SSU2: PeerTest msg=", (int)buf[offset], " code=", (int)buf[offset+1]);
@ -1754,28 +1705,34 @@ namespace transport
void SSU2Session::HandleRouterInfo (const uint8_t * buf, size_t len) void SSU2Session::HandleRouterInfo (const uint8_t * buf, size_t len)
{ {
auto ri = ExtractRouterInfo (buf, len); if (len < 2) return;
if (ri) // not from SessionConfirmed, we must add it instantly to use in next block
std::shared_ptr<const i2p::data::RouterInfo> newRi;
if (buf[0] & SSU2_ROUTER_INFO_FLAG_GZIP) // compressed?
{ {
// not from SessionConfirmed, we must add it instantly to use in next block auto ri = ExtractRouterInfo (buf, len);
auto newRi = i2p::data::netdb.AddRouterInfo (ri->GetBuffer (), ri->GetBufferLen ()); // TODO: add ri if (ri)
if (newRi) newRi = i2p::data::netdb.AddRouterInfo (ri->GetBuffer (), ri->GetBufferLen ());
}
else // use buffer directly. TODO: handle frag
newRi = i2p::data::netdb.AddRouterInfo (buf + 2, len - 2);
if (newRi)
{
auto remoteIdentity = GetRemoteIdentity ();
if (remoteIdentity && remoteIdentity->GetIdentHash () == newRi->GetIdentHash ())
{ {
auto remoteIdentity = GetRemoteIdentity (); // peer's RouterInfo update
if (remoteIdentity && remoteIdentity->GetIdentHash () == newRi->GetIdentHash ()) SetRemoteIdentity (newRi->GetIdentity ());
auto address = m_RemoteEndpoint.address ().is_v6 () ? newRi->GetSSU2V6Address () : newRi->GetSSU2V4Address ();
if (address)
{ {
// peer's RouterInfo update m_Address = address;
SetRemoteIdentity (newRi->GetIdentity ()); if (IsOutgoing () && m_RelayTag && !address->IsIntroducer ())
auto address = m_RemoteEndpoint.address ().is_v6 () ? newRi->GetSSU2V6Address () : newRi->GetSSU2V4Address (); m_RelayTag = 0; // not longer introducer
if (address)
{
m_Address = address;
if (IsOutgoing () && m_RelayTag && !address->IsIntroducer ())
m_RelayTag = 0; // not longer introducer
}
} }
} }
} }
} }
void SSU2Session::HandleAck (const uint8_t * buf, size_t len) void SSU2Session::HandleAck (const uint8_t * buf, size_t len)
@ -2007,27 +1964,32 @@ namespace transport
SendData (payload, payloadSize); SendData (payload, payloadSize);
return; return;
} }
auto mts = i2p::util::GetMillisecondsSinceEpoch ();
session->m_RelaySessions.emplace (bufbe32toh (buf + 1), // nonce session->m_RelaySessions.emplace (bufbe32toh (buf + 1), // nonce
std::make_pair (shared_from_this (), i2p::util::GetSecondsSinceEpoch ()) ); std::make_pair (shared_from_this (), mts/1000) );
// send relay intro to Charlie // send relay intro to Charlie
auto r = i2p::data::netdb.FindRouter (GetRemoteIdentity ()->GetIdentHash ()); // Alice's RI auto r = i2p::data::netdb.FindRouter (GetRemoteIdentity ()->GetIdentHash ()); // Alice's RI
if (r && (r->IsUnreachable () || !i2p::data::netdb.PopulateRouterInfoBuffer (r))) r = nullptr; if (r && (r->IsUnreachable () || !i2p::data::netdb.PopulateRouterInfoBuffer (r))) r = nullptr;
if (!r) LogPrint (eLogWarning, "SSU2: RelayRequest Alice's router info not found"); if (!r) LogPrint (eLogWarning, "SSU2: RelayRequest Alice's router info not found");
uint8_t payload[SSU2_MAX_PACKET_SIZE]; auto packet = m_Server.GetSentPacketsPool ().AcquireShared ();
size_t payloadSize = r ? CreateRouterInfoBlock (payload, m_MaxPayloadSize - len - 32, r) : 0; packet->payloadSize = r ? CreateRouterInfoBlock (packet->payload, m_MaxPayloadSize - len - 32, r) : 0;
if (!payloadSize && r) if (!packet->payloadSize && r)
session->SendFragmentedMessage (CreateDatabaseStoreMsg (r)); session->SendFragmentedMessage (CreateDatabaseStoreMsg (r));
payloadSize += CreateRelayIntroBlock (payload + payloadSize, m_MaxPayloadSize - payloadSize, buf + 1, len -1); packet->payloadSize += CreateRelayIntroBlock (packet->payload + packet->payloadSize, m_MaxPayloadSize - packet->payloadSize, buf + 1, len -1);
if (payloadSize < m_MaxPayloadSize) if (packet->payloadSize < m_MaxPayloadSize)
payloadSize += CreatePaddingBlock (payload + payloadSize, m_MaxPayloadSize - payloadSize); packet->payloadSize += CreatePaddingBlock (packet->payload + packet->payloadSize, m_MaxPayloadSize - packet->payloadSize);
session->SendData (payload, payloadSize); uint32_t packetNum = session->SendData (packet->payload, packet->payloadSize);
packet->sendTime = mts;
// Charlie always responds with RelayResponse
session->m_SentPackets.emplace (packetNum, packet);
} }
void SSU2Session::HandleRelayIntro (const uint8_t * buf, size_t len, int attempts) void SSU2Session::HandleRelayIntro (const uint8_t * buf, size_t len, int attempts)
{ {
// we are Charlie // we are Charlie
auto mts = i2p::util::GetMillisecondsSinceEpoch ();
SSU2RelayResponseCode code = eSSU2RelayResponseCodeAccept; SSU2RelayResponseCode code = eSSU2RelayResponseCodeAccept;
uint64_t token = 0; uint64_t token = 0;
bool isV4 = false; bool isV4 = false;
@ -2047,7 +2009,9 @@ namespace transport
boost::asio::ip::udp::endpoint ep; boost::asio::ip::udp::endpoint ep;
if (ExtractEndpoint (buf + 47, asz, ep)) if (ExtractEndpoint (buf + 47, asz, ep))
{ {
auto addr = ep.address ().is_v6 () ? r->GetSSU2V6Address () : r->GetSSU2V4Address (); 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 (addr)
{ {
if (m_Server.IsSupported (ep.address ())) if (m_Server.IsSupported (ep.address ()))
@ -2055,6 +2019,7 @@ namespace transport
token = m_Server.GetIncomingToken (ep); token = m_Server.GetIncomingToken (ep);
isV4 = ep.address ().is_v4 (); isV4 = ep.address ().is_v4 ();
SendHolePunch (bufbe32toh (buf + 33), ep, addr->i, token); SendHolePunch (bufbe32toh (buf + 33), ep, addr->i, token);
m_Server.AddConnectedRecently (ep, mts/1000);
} }
else else
{ {
@ -2099,11 +2064,15 @@ namespace transport
code = eSSU2RelayResponseCodeCharlieAliceIsUnknown; code = eSSU2RelayResponseCodeCharlieAliceIsUnknown;
} }
// send relay response to Bob // send relay response to Bob
uint8_t payload[SSU2_MAX_PACKET_SIZE]; auto packet = m_Server.GetSentPacketsPool ().AcquireShared ();
size_t payloadSize = CreateRelayResponseBlock (payload, m_MaxPayloadSize, packet->payloadSize = CreateRelayResponseBlock (packet->payload, m_MaxPayloadSize,
code, bufbe32toh (buf + 33), token, isV4); code, bufbe32toh (buf + 33), token, isV4);
payloadSize += CreatePaddingBlock (payload + payloadSize, m_MaxPayloadSize - payloadSize); packet->payloadSize += CreatePaddingBlock (packet->payload + packet->payloadSize, m_MaxPayloadSize - packet->payloadSize);
SendData (payload, 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);
} }
void SSU2Session::HandleRelayResponse (const uint8_t * buf, size_t len) void SSU2Session::HandleRelayResponse (const uint8_t * buf, size_t len)
@ -2131,13 +2100,18 @@ namespace transport
if (it->second.first && it->second.first->IsEstablished ()) if (it->second.first && it->second.first->IsEstablished ())
{ {
// we are Bob, message from Charlie // we are Bob, message from Charlie
uint8_t payload[SSU2_MAX_PACKET_SIZE]; auto packet = m_Server.GetSentPacketsPool ().AcquireShared ();
uint8_t * payload = packet->payload;
payload[0] = eSSU2BlkRelayResponse; payload[0] = eSSU2BlkRelayResponse;
htobe16buf (payload + 1, len); htobe16buf (payload + 1, len);
memcpy (payload + 3, buf, len); // forward to Alice as is memcpy (payload + 3, buf, len); // forward to Alice as is
size_t payloadSize = len + 3; packet->payloadSize = len + 3;
payloadSize += CreatePaddingBlock (payload + payloadSize, m_MaxPayloadSize - payloadSize); packet->payloadSize += CreatePaddingBlock (payload + packet->payloadSize, m_MaxPayloadSize - packet->payloadSize);
it->second.first->SendData (payload, 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);
} }
else else
{ {
@ -2177,18 +2151,19 @@ namespace transport
} }
else else
{ {
LogPrint (eLogInfo, "SSU2: RelayResponse status code=", (int)buf[1]); LogPrint (eLogInfo, "SSU2: RelayResponse status code=", (int)buf[1], " nonce=", bufbe32toh (buf + 2));
it->second.first->Done (); it->second.first->Done ();
} }
} }
m_RelaySessions.erase (it); m_RelaySessions.erase (it);
} }
else else
LogPrint (eLogWarning, "SSU2: RelayResponse unknown nonce ", bufbe32toh (buf + 2)); LogPrint (eLogDebug, "SSU2: RelayResponse unknown nonce ", bufbe32toh (buf + 2));
} }
void SSU2Session::HandlePeerTest (const uint8_t * buf, size_t len) void SSU2Session::HandlePeerTest (const uint8_t * buf, size_t len)
{ {
// msgs 1-4
if (len < 3) return; if (len < 3) return;
uint8_t msg = buf[0]; uint8_t msg = buf[0];
size_t offset = 3; // points to signed data size_t offset = 3; // points to signed data
@ -2204,7 +2179,7 @@ namespace transport
GetRemoteIdentity ()->GetIdentHash ()); GetRemoteIdentity ()->GetIdentHash ());
if (session) // session with Charlie if (session) // session with Charlie
{ {
session->m_PeerTests.emplace (nonce, std::make_pair (shared_from_this (), i2p::util::GetSecondsSinceEpoch ())); m_Server.AddPeerTest (nonce, shared_from_this (), ts/1000);
auto packet = m_Server.GetSentPacketsPool ().AcquireShared (); auto packet = m_Server.GetSentPacketsPool ().AcquireShared ();
// Alice's RouterInfo // Alice's RouterInfo
auto r = i2p::data::netdb.FindRouter (GetRemoteIdentity ()->GetIdentHash ()); auto r = i2p::data::netdb.FindRouter (GetRemoteIdentity ()->GetIdentHash ());
@ -2231,11 +2206,14 @@ namespace transport
else else
{ {
// Charlie not found, send error back to Alice // Charlie not found, send error back to Alice
uint8_t payload[SSU2_MAX_PACKET_SIZE], zeroHash[32] = {0}; auto packet = m_Server.GetSentPacketsPool ().AcquireShared ();
size_t payloadSize = CreatePeerTestBlock (payload, m_MaxPayloadSize, 4, uint8_t zeroHash[32] = {0};
packet->payloadSize = CreatePeerTestBlock (packet->payload, m_MaxPayloadSize, 4,
eSSU2PeerTestCodeBobNoCharlieAvailable, zeroHash, buf + offset, len - offset); eSSU2PeerTestCodeBobNoCharlieAvailable, zeroHash, buf + offset, len - offset);
payloadSize += CreatePaddingBlock (payload + payloadSize, m_MaxPayloadSize - payloadSize); packet->payloadSize += CreatePaddingBlock (packet->payload + packet->payloadSize, m_MaxPayloadSize - packet->payloadSize);
SendData (payload, payloadSize); uint32_t packetNum = SendData (packet->payload, packet->payloadSize);
packet->sendTime = ts;
m_SentPackets.emplace (packetNum, packet);
} }
break; break;
} }
@ -2269,19 +2247,22 @@ namespace transport
{ {
boost::asio::ip::udp::endpoint ep; boost::asio::ip::udp::endpoint ep;
std::shared_ptr<const i2p::data::RouterInfo::Address> addr; std::shared_ptr<const i2p::data::RouterInfo::Address> addr;
if (ExtractEndpoint (buf + offset + 10, asz, ep)) if (ExtractEndpoint (buf + offset + 10, asz, ep) && !ep.address ().is_unspecified () && ep.port ())
addr = r->GetSSU2Address (ep.address ().is_v4 ()); addr = r->GetSSU2Address (ep.address ().is_v4 ());
if (addr && m_Server.IsSupported (ep.address ()) && if (addr && m_Server.IsSupported (ep.address ()) &&
i2p::context.GetRouterInfo ().IsSSU2PeerTesting (ep.address ().is_v4 ())) i2p::context.GetRouterInfo ().IsSSU2PeerTesting (ep.address ().is_v4 ()))
{ {
// send msg 5 to Alice if (!m_Server.IsConnectedRecently (ep)) // no alive hole punch
auto session = std::make_shared<SSU2Session> (m_Server, r, addr); {
session->SetState (eSSU2SessionStatePeerTest); // send msg 5 to Alice
session->m_RemoteEndpoint = ep; // might be different auto session = std::make_shared<SSU2PeerTestSession> (m_Server,
session->m_DestConnID = htobe64 (((uint64_t)nonce << 32) | nonce); 0, htobe64 (((uint64_t)nonce << 32) | nonce));
session->m_SourceConnID = ~session->m_DestConnID; session->m_RemoteEndpoint = ep; // might be different
m_Server.AddSession (session); m_Server.AddSession (session);
session->SendPeerTest (5, newSignedData.data (), newSignedData.size (), addr->i); session->SendPeerTest (5, newSignedData.data (), newSignedData.size (), addr);
}
else
code = eSSU2PeerTestCodeCharlieAliceIsAlreadyConnected;
} }
else else
code = eSSU2PeerTestCodeCharlieUnsupportedAddress; code = eSSU2PeerTestCodeCharlieUnsupportedAddress;
@ -2298,54 +2279,59 @@ namespace transport
else else
code = eSSU2PeerTestCodeCharlieAliceIsUnknown; code = eSSU2PeerTestCodeCharlieAliceIsUnknown;
// send msg 3 back to Bob // send msg 3 back to Bob
uint8_t payload[SSU2_MAX_PACKET_SIZE]; auto packet = m_Server.GetSentPacketsPool ().AcquireShared ();
size_t payloadSize = CreatePeerTestBlock (payload, m_MaxPayloadSize, 3, packet->payloadSize = CreatePeerTestBlock (packet->payload, m_MaxPayloadSize, 3,
code, nullptr, newSignedData.data (), newSignedData.size ()); code, nullptr, newSignedData.data (), newSignedData.size ());
payloadSize += CreatePaddingBlock (payload + payloadSize, m_MaxPayloadSize - payloadSize); packet->payloadSize += CreatePaddingBlock (packet->payload + packet->payloadSize, m_MaxPayloadSize - packet->payloadSize);
SendData (payload, payloadSize); uint32_t packetNum = SendData (packet->payload, packet->payloadSize);
packet->sendTime = ts;
m_SentPackets.emplace (packetNum, packet);
break; break;
} }
case 3: // Bob from Charlie case 3: // Bob from Charlie
{ {
auto it = m_PeerTests.find (nonce); auto aliceSession = m_Server.GetPeerTest (nonce);
if (it != m_PeerTests.end () && it->second.first) if (aliceSession && aliceSession->IsEstablished ())
{ {
uint8_t payload[SSU2_MAX_PACKET_SIZE]; auto packet = m_Server.GetSentPacketsPool ().AcquireShared ();
// Charlie's RouterInfo // Charlie's RouterInfo
auto r = i2p::data::netdb.FindRouter (GetRemoteIdentity ()->GetIdentHash ()); auto r = i2p::data::netdb.FindRouter (GetRemoteIdentity ()->GetIdentHash ());
if (r && (r->IsUnreachable () || !i2p::data::netdb.PopulateRouterInfoBuffer (r))) r = nullptr; if (r && (r->IsUnreachable () || !i2p::data::netdb.PopulateRouterInfoBuffer (r))) r = nullptr;
size_t payloadSize = r ? CreateRouterInfoBlock (payload, m_MaxPayloadSize - len - 32, r) : 0; packet->payloadSize = r ? CreateRouterInfoBlock (packet->payload, m_MaxPayloadSize - len - 32, r) : 0;
if (!payloadSize && r) if (!packet->payloadSize && r)
it->second.first->SendFragmentedMessage (CreateDatabaseStoreMsg (r)); aliceSession->SendFragmentedMessage (CreateDatabaseStoreMsg (r));
if (payloadSize + len + 16 > m_MaxPayloadSize) if (packet->payloadSize + len + 16 > m_MaxPayloadSize)
{ {
// doesn't fit one message, send RouterInfo in separate message // doesn't fit one message, send RouterInfo in separate message
it->second.first->SendData (payload, payloadSize); uint32_t packetNum = aliceSession->SendData (packet->payload, packet->payloadSize);
payloadSize = 0; packet->sendTime = ts;
aliceSession->m_SentPackets.emplace (packetNum, packet);
packet = m_Server.GetSentPacketsPool ().AcquireShared ();
} }
// PeerTest to Alice // PeerTest to Alice
payloadSize += CreatePeerTestBlock (payload + payloadSize, m_MaxPayloadSize, 4, packet->payloadSize += CreatePeerTestBlock (packet->payload + packet->payloadSize, m_MaxPayloadSize, 4,
(SSU2PeerTestCode)buf[1], GetRemoteIdentity ()->GetIdentHash (), buf + offset, len - offset); (SSU2PeerTestCode)buf[1], GetRemoteIdentity ()->GetIdentHash (), buf + offset, len - offset);
if (payloadSize < m_MaxPayloadSize) if (packet->payloadSize < m_MaxPayloadSize)
payloadSize += CreatePaddingBlock (payload + payloadSize, m_MaxPayloadSize - payloadSize); packet->payloadSize += CreatePaddingBlock (packet->payload + packet->payloadSize, m_MaxPayloadSize - packet->payloadSize);
it->second.first->SendData (payload, payloadSize); uint32_t packetNum = aliceSession->SendData (packet->payload, packet->payloadSize);
m_PeerTests.erase (it); packet->sendTime = ts;
} aliceSession->m_SentPackets.emplace (packetNum, packet);
}
else else
LogPrint (eLogWarning, "SSU2: Unknown peer test 3 nonce ", nonce); LogPrint (eLogDebug, "SSU2: Unknown peer test 3 nonce ", nonce);
break; break;
} }
case 4: // Alice from Bob case 4: // Alice from Bob
{ {
auto it = m_PeerTests.find (nonce); auto session = m_Server.GetRequestedPeerTest (nonce);
if (it != m_PeerTests.end ()) if (session)
{ {
if (buf[1] == eSSU2PeerTestCodeAccept) if (buf[1] == eSSU2PeerTestCodeAccept)
{ {
if (GetRouterStatus () == eRouterStatusUnknown) if (GetRouterStatus () == eRouterStatusUnknown)
SetTestingState (true); SetTestingState (true);
auto r = i2p::data::netdb.FindRouter (buf + 3); // find Charlie auto r = i2p::data::netdb.FindRouter (buf + 3); // find Charlie
if (r && it->second.first) if (r)
{ {
uint8_t asz = buf[offset + 9]; uint8_t asz = buf[offset + 9];
SignedData s; SignedData s;
@ -2355,26 +2341,33 @@ namespace transport
s.Insert (buf + offset, asz + 10); // ver, nonce, ts, asz, Alice's endpoint s.Insert (buf + offset, asz + 10); // ver, nonce, ts, asz, Alice's endpoint
if (s.Verify (r->GetIdentity (), buf + offset + asz + 10)) if (s.Verify (r->GetIdentity (), buf + offset + asz + 10))
{ {
it->second.first->SetRemoteIdentity (r->GetIdentity ()); session->SetRemoteIdentity (r->GetIdentity ());
auto addr = r->GetSSU2Address (m_Address->IsV4 ()); auto addr = r->GetSSU2Address (m_Address->IsV4 ());
if (addr) if (addr)
{ {
it->second.first->m_Address = addr; if (session->GetMsgNumReceived () >= 5)
if (it->second.first->m_State == eSSU2SessionStatePeerTestReceived)
{ {
// msg 5 already received. send msg 6 // msg 5 already received
SetRouterStatus (eRouterStatusOK); if (session->GetMsgNumReceived () == 5)
it->second.first->m_State = eSSU2SessionStatePeerTest; {
it->second.first->SendPeerTest (6, buf + offset, len - offset, addr->i); if (!session->IsConnectedRecently ())
SetRouterStatus (eRouterStatusOK);
// send msg 6
session->SendPeerTest (6, buf + offset, len - offset, addr);
}
else
LogPrint (eLogWarning, "SSU2: PeerTest 4 received, but msg ", session->GetMsgNumReceived (), " already received");
} }
else else
{ {
session->m_Address = addr;
if (GetTestingState ()) if (GetTestingState ())
{ {
SetTestingState (false); SetTestingState (false);
if (GetRouterStatus () != eRouterStatusFirewalled && addr->IsPeerTesting ()) if (GetRouterStatus () != eRouterStatusFirewalled && addr->IsPeerTesting ())
{ {
SetRouterStatus (eRouterStatusFirewalled); SetRouterStatus (eRouterStatusFirewalled);
session->SetStatusChanged ();
if (m_Address->IsV4 ()) if (m_Address->IsV4 ())
m_Server.RescheduleIntroducersUpdateTimer (); m_Server.RescheduleIntroducersUpdateTimer ();
else else
@ -2388,63 +2381,34 @@ namespace transport
else else
{ {
LogPrint (eLogWarning, "SSU2: Peer test 4 address not found"); LogPrint (eLogWarning, "SSU2: Peer test 4 address not found");
it->second.first->Done (); session->Done ();
} }
} }
else else
{ {
LogPrint (eLogWarning, "SSU2: Peer test 4 signature verification failed"); LogPrint (eLogWarning, "SSU2: Peer test 4 signature verification failed");
it->second.first->Done (); session->Done ();
} }
} }
else else
{ {
LogPrint (eLogWarning, "SSU2: Peer test 4 router not found"); LogPrint (eLogWarning, "SSU2: Peer test 4 router not found");
if (it->second.first) session->Done ();
it->second.first->Done ();
} }
} }
else else
{ {
LogPrint (eLogInfo, "SSU2: Peer test 4 error code ", (int)buf[1], " from ", LogPrint (eLogInfo, "SSU2: Peer test 4 error code ", (int)buf[1], " from ",
i2p::data::GetIdentHashAbbreviation (buf[1] < 64 ? GetRemoteIdentity ()->GetIdentHash () : i2p::data::IdentHash (buf + 3))); i2p::data::GetIdentHashAbbreviation (buf[1] < 64 ? GetRemoteIdentity ()->GetIdentHash () : i2p::data::IdentHash (buf + 3)));
if (GetTestingState ()) if (GetTestingState () && GetRouterStatus () != eRouterStatusFirewalled)
SetRouterStatus (eRouterStatusUnknown); SetRouterStatus (eRouterStatusUnknown);
it->second.first->Done (); session->Done ();
} }
m_PeerTests.erase (it);
} }
else else
LogPrint (eLogWarning, "SSU2: Unknown peer test 4 nonce ", nonce); LogPrint (eLogDebug, "SSU2: Unknown peer test 4 nonce ", nonce);
break; break;
} }
case 5: // Alice from Charlie 1
if (htobe64 (((uint64_t)nonce << 32) | nonce) == m_SourceConnID)
{
if (m_Address)
{
SetRouterStatus (eRouterStatusOK);
SendPeerTest (6, buf + offset, len - offset, m_Address->i);
}
else
// we received msg 5 before msg 4
m_State = eSSU2SessionStatePeerTestReceived;
}
else
LogPrint (eLogWarning, "SSU2: Peer test 5 nonce mismatch ", nonce, " connID=", m_SourceConnID);
break;
case 6: // Charlie from Alice
if (m_Address)
SendPeerTest (7, buf + offset, len - offset, m_Address->i);
else
LogPrint (eLogWarning, "SSU2: Unknown address for peer test 6");
m_Server.RemoveSession (~htobe64 (((uint64_t)nonce << 32) | nonce));
break;
case 7: // Alice from Charlie 2
if (m_Address->IsV6 ())
i2p::context.SetStatusV6 (eRouterStatusOK); // set status OK for ipv6 even if from SSU2
m_Server.RemoveSession (htobe64 (((uint64_t)nonce << 32) | nonce));
break;
default: default:
LogPrint (eLogWarning, "SSU2: PeerTest unexpected msg num ", buf[0]); LogPrint (eLogWarning, "SSU2: PeerTest unexpected msg num ", buf[0]);
} }
@ -2933,22 +2897,18 @@ namespace transport
i2p::data::GzipInflator inflator; i2p::data::GzipInflator inflator;
uint8_t uncompressed[i2p::data::MAX_RI_BUFFER_SIZE]; uint8_t uncompressed[i2p::data::MAX_RI_BUFFER_SIZE];
size_t uncompressedSize = inflator.Inflate (buf + 2, size - 2, uncompressed, i2p::data::MAX_RI_BUFFER_SIZE); size_t uncompressedSize = inflator.Inflate (buf + 2, size - 2, uncompressed, i2p::data::MAX_RI_BUFFER_SIZE);
if (uncompressedSize && uncompressedSize < i2p::data::MAX_RI_BUFFER_SIZE) if (uncompressedSize && uncompressedSize <= i2p::data::MAX_RI_BUFFER_SIZE)
ri = std::make_shared<i2p::data::RouterInfo>(uncompressed, uncompressedSize); ri = std::make_shared<i2p::data::RouterInfo>(uncompressed, uncompressedSize);
else else
LogPrint (eLogInfo, "SSU2: RouterInfo decompression failed ", uncompressedSize); LogPrint (eLogInfo, "SSU2: RouterInfo decompression failed ", uncompressedSize);
} }
else else if (size <= i2p::data::MAX_RI_BUFFER_SIZE + 2)
ri = std::make_shared<i2p::data::RouterInfo>(buf + 2, size - 2); ri = std::make_shared<i2p::data::RouterInfo>(buf + 2, size - 2);
else
LogPrint (eLogInfo, "SSU2: RouterInfo is too long ", size);
return ri; return ri;
} }
void SSU2Session::CreateNonce (uint64_t seqn, uint8_t * nonce)
{
memset (nonce, 0, 4);
htole64buf (nonce + 4, seqn);
}
bool SSU2Session::UpdateReceivePacketNum (uint32_t packetNum) bool SSU2Session::UpdateReceivePacketNum (uint32_t packetNum)
{ {
if (packetNum <= m_ReceivePacketNum) return false; // duplicate if (packetNum <= m_ReceivePacketNum) return false; // duplicate
@ -3108,16 +3068,6 @@ namespace transport
else else
++it; ++it;
} }
for (auto it = m_PeerTests.begin (); it != m_PeerTests.end ();)
{
if (ts > it->second.second + SSU2_PEER_TEST_EXPIRATION_TIMEOUT)
{
LogPrint (eLogWarning, "SSU2: Peer test nonce ", it->first, " was not responded in ", SSU2_PEER_TEST_EXPIRATION_TIMEOUT, " seconds, deleted");
it = m_PeerTests.erase (it);
}
else
++it;
}
if (m_PathChallenge) if (m_PathChallenge)
RequestTermination (eSSU2TerminationReasonNormalClose); RequestTermination (eSSU2TerminationReasonNormalClose);
} }
@ -3137,5 +3087,215 @@ namespace transport
Resend (i2p::util::GetMillisecondsSinceEpoch ()); // than right time 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++;
}
}
} }
} }

129
libi2pd/SSU2Session.h

@ -113,7 +113,6 @@ namespace transport
eSSU2SessionStateFailed, eSSU2SessionStateFailed,
eSSU2SessionStateIntroduced, eSSU2SessionStateIntroduced,
eSSU2SessionStatePeerTest, eSSU2SessionStatePeerTest,
eSSU2SessionStatePeerTestReceived, // 5 before 4
eSSU2SessionStateTokenRequestReceived eSSU2SessionStateTokenRequestReceived
}; };
@ -206,36 +205,40 @@ namespace transport
class SSU2Server; class SSU2Server;
class SSU2Session: public TransportSession, public std::enable_shared_from_this<SSU2Session> class SSU2Session: public TransportSession, public std::enable_shared_from_this<SSU2Session>
{ {
union Header protected:
{
uint64_t ll[2]; union Header
uint8_t buf[16];
struct
{ {
uint64_t connID; uint64_t ll[2];
uint32_t packetNum; uint8_t buf[16];
uint8_t type; struct
uint8_t flags[3]; {
} h; uint64_t connID;
}; uint32_t packetNum;
uint8_t type;
uint8_t flags[3];
} h;
};
struct HandshakePacket private:
{
Header header; struct HandshakePacket
uint8_t headerX[48]; // part1 for SessionConfirmed {
uint8_t payload[SSU2_MAX_PACKET_SIZE*2]; Header header;
size_t payloadSize = 0; uint8_t headerX[48]; // part1 for SessionConfirmed
uint64_t sendTime = 0; // in milliseconds uint8_t payload[SSU2_MAX_PACKET_SIZE*2];
bool isSecondFragment = false; // for SessionConfirmed size_t payloadSize = 0;
}; uint64_t sendTime = 0; // in milliseconds
bool isSecondFragment = false; // for SessionConfirmed
};
typedef std::function<void ()> OnEstablished; typedef std::function<void ()> OnEstablished;
public: public:
SSU2Session (SSU2Server& server, std::shared_ptr<const i2p::data::RouterInfo> in_RemoteRouter = nullptr, SSU2Session (SSU2Server& server, std::shared_ptr<const i2p::data::RouterInfo> in_RemoteRouter = nullptr,
std::shared_ptr<const i2p::data::RouterInfo::Address> addr = nullptr); std::shared_ptr<const i2p::data::RouterInfo::Address> addr = nullptr, bool noise = true);
~SSU2Session (); virtual ~SSU2Session ();
void SetRemoteEndpoint (const boost::asio::ip::udp::endpoint& ep) { m_RemoteEndpoint = ep; }; void SetRemoteEndpoint (const boost::asio::ip::udp::endpoint& ep) { m_RemoteEndpoint = ep; };
const boost::asio::ip::udp::endpoint& GetRemoteEndpoint () const { return m_RemoteEndpoint; }; const boost::asio::ip::udp::endpoint& GetRemoteEndpoint () const { return m_RemoteEndpoint; };
@ -245,7 +248,7 @@ namespace transport
void SetOnEstablished (OnEstablished e) { m_OnEstablished = e; }; void SetOnEstablished (OnEstablished e) { m_OnEstablished = e; };
OnEstablished GetOnEstablished () const { return m_OnEstablished; }; OnEstablished GetOnEstablished () const { return m_OnEstablished; };
void Connect (); virtual void Connect ();
bool Introduce (std::shared_ptr<SSU2Session> session, uint32_t relayTag); bool Introduce (std::shared_ptr<SSU2Session> session, uint32_t relayTag);
void WaitForIntroduction (); void WaitForIntroduction ();
void SendPeerTest (); // Alice, Data message void SendPeerTest (); // Alice, Data message
@ -265,14 +268,34 @@ namespace transport
SSU2SessionState GetState () const { return m_State; }; SSU2SessionState GetState () const { return m_State; };
void SetState (SSU2SessionState state) { m_State = state; }; void SetState (SSU2SessionState state) { m_State = state; };
bool ProcessFirstIncomingMessage (uint64_t connID, uint8_t * buf, size_t len); virtual bool ProcessFirstIncomingMessage (uint64_t connID, uint8_t * buf, size_t len);
bool ProcessSessionCreated (uint8_t * buf, size_t len); bool ProcessSessionCreated (uint8_t * buf, size_t len);
bool ProcessSessionConfirmed (uint8_t * buf, size_t len); bool ProcessSessionConfirmed (uint8_t * buf, size_t len);
bool ProcessRetry (uint8_t * buf, size_t len); bool ProcessRetry (uint8_t * buf, size_t len);
bool ProcessHolePunch (uint8_t * buf, size_t len); bool ProcessHolePunch (uint8_t * buf, size_t len);
bool ProcessPeerTest (uint8_t * buf, size_t len); virtual bool ProcessPeerTest (uint8_t * buf, size_t len);
void ProcessData (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& from); void ProcessData (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& from);
protected:
SSU2Server& GetServer () { return m_Server; }
RouterStatus GetRouterStatus () const;
void SetRouterStatus (RouterStatus status) const;
size_t GetMaxPayloadSize () const { return m_MaxPayloadSize; }
void SetIsDataReceived (bool dataReceived) { m_IsDataReceived = dataReceived; };
uint64_t GetSourceConnID () const { return m_SourceConnID; }
void SetSourceConnID (uint64_t sourceConnID) { m_SourceConnID = sourceConnID; }
uint64_t GetDestConnID () const { return m_DestConnID; }
void SetDestConnID (uint64_t destConnID) { m_DestConnID = destConnID; }
void SetAddress (std::shared_ptr<const i2p::data::RouterInfo::Address> addr) { m_Address = addr; }
void HandlePayload (const uint8_t * buf, size_t len);
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);
private: private:
void Terminate (); void Terminate ();
@ -298,11 +321,9 @@ namespace transport
void SendQuickAck (); void SendQuickAck ();
void SendTermination (); void SendTermination ();
void SendHolePunch (uint32_t nonce, const boost::asio::ip::udp::endpoint& ep, const uint8_t * introKey, uint64_t token); void SendHolePunch (uint32_t nonce, const boost::asio::ip::udp::endpoint& ep, const uint8_t * introKey, uint64_t token);
void SendPeerTest (uint8_t msg, const uint8_t * signedData, size_t signedDataLen, const uint8_t * introKey); // PeerTest message
void SendPathResponse (const uint8_t * data, size_t len); void SendPathResponse (const uint8_t * data, size_t len);
void SendPathChallenge (); void SendPathChallenge ();
void HandlePayload (const uint8_t * buf, size_t len);
void HandleDateTime (const uint8_t * buf, size_t len); void HandleDateTime (const uint8_t * buf, size_t len);
void HandleRouterInfo (const uint8_t * buf, size_t len); void HandleRouterInfo (const uint8_t * buf, size_t len);
void HandleAck (const uint8_t * buf, size_t len); void HandleAck (const uint8_t * buf, size_t len);
@ -312,35 +333,29 @@ namespace transport
size_t CreateEndpoint (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& ep); 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; std::shared_ptr<const i2p::data::RouterInfo::Address> FindLocalAddress () const;
void AdjustMaxPayloadSize (); void AdjustMaxPayloadSize ();
RouterStatus GetRouterStatus () const;
void SetRouterStatus (RouterStatus status) const;
bool GetTestingState () const; bool GetTestingState () const;
void SetTestingState(bool testing) const; void SetTestingState(bool testing) const;
std::shared_ptr<const i2p::data::RouterInfo> ExtractRouterInfo (const uint8_t * buf, size_t size); std::shared_ptr<const i2p::data::RouterInfo> ExtractRouterInfo (const uint8_t * buf, size_t size);
void CreateNonce (uint64_t seqn, uint8_t * nonce);
bool UpdateReceivePacketNum (uint32_t packetNum); // for Ack, returns false if duplicate bool UpdateReceivePacketNum (uint32_t packetNum); // for Ack, returns false if duplicate
void HandleFirstFragment (const uint8_t * buf, size_t len); void HandleFirstFragment (const uint8_t * buf, size_t len);
void HandleFollowOnFragment (const uint8_t * buf, size_t len); void HandleFollowOnFragment (const uint8_t * buf, size_t len);
void HandleRelayRequest (const uint8_t * buf, size_t len); void HandleRelayRequest (const uint8_t * buf, size_t len);
void HandleRelayIntro (const uint8_t * buf, size_t len, int attempts = 0); void HandleRelayIntro (const uint8_t * buf, size_t len, int attempts = 0);
void HandleRelayResponse (const uint8_t * buf, size_t len); void HandleRelayResponse (const uint8_t * buf, size_t len);
void HandlePeerTest (const uint8_t * buf, size_t len); virtual void HandlePeerTest (const uint8_t * buf, size_t len);
void HandleI2NPMsg (std::shared_ptr<I2NPMessage>&& msg); void HandleI2NPMsg (std::shared_ptr<I2NPMessage>&& msg);
size_t CreateAddressBlock (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& ep);
size_t CreateRouterInfoBlock (uint8_t * buf, size_t len, std::shared_ptr<const i2p::data::RouterInfo> r); size_t CreateRouterInfoBlock (uint8_t * buf, size_t len, std::shared_ptr<const i2p::data::RouterInfo> r);
size_t CreateRouterInfoBlock (uint8_t * buf, size_t len, std::shared_ptr<const i2p::data::RouterInfo::Buffer> riBuffer); size_t CreateRouterInfoBlock (uint8_t * buf, size_t len, std::shared_ptr<const i2p::data::RouterInfo::Buffer> riBuffer);
size_t CreateAckBlock (uint8_t * buf, size_t len); size_t CreateAckBlock (uint8_t * buf, size_t len);
size_t CreatePaddingBlock (uint8_t * buf, size_t len, size_t minSize = 0);
size_t CreateI2NPBlock (uint8_t * buf, size_t len, std::shared_ptr<I2NPMessage>&& msg); size_t CreateI2NPBlock (uint8_t * buf, size_t len, std::shared_ptr<I2NPMessage>&& msg);
size_t CreateFirstFragmentBlock (uint8_t * buf, size_t len, std::shared_ptr<I2NPMessage> msg); size_t CreateFirstFragmentBlock (uint8_t * buf, size_t len, std::shared_ptr<I2NPMessage> msg);
size_t CreateFollowOnFragmentBlock (uint8_t * buf, size_t len, std::shared_ptr<I2NPMessage> msg, uint8_t& fragmentNum, uint32_t msgID); 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 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 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, uint8_t msg, SSU2PeerTestCode code, const uint8_t * routerHash, const uint8_t * signedData, size_t signedDataLen);
size_t CreatePeerTestBlock (uint8_t * buf, size_t len, uint32_t nonce); // Alice size_t CreatePeerTestBlock (uint8_t * buf, size_t len, uint32_t nonce); // Alice
size_t CreateTerminationBlock (uint8_t * buf, size_t len); size_t CreateTerminationBlock (uint8_t * buf, size_t len);
private: private:
SSU2Server& m_Server; SSU2Server& m_Server;
@ -358,8 +373,7 @@ namespace transport
std::set<uint32_t> m_OutOfSequencePackets; // packet nums > receive packet num std::set<uint32_t> m_OutOfSequencePackets; // packet nums > receive packet num
std::map<uint32_t, std::shared_ptr<SSU2SentPacket> > m_SentPackets; // packetNum -> packet std::map<uint32_t, std::shared_ptr<SSU2SentPacket> > m_SentPackets; // packetNum -> packet
std::unordered_map<uint32_t, std::shared_ptr<SSU2IncompleteMessage> > m_IncompleteMessages; // msgID -> I2NP std::unordered_map<uint32_t, std::shared_ptr<SSU2IncompleteMessage> > m_IncompleteMessages; // msgID -> I2NP
std::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::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::map<uint32_t, std::pair <std::shared_ptr<SSU2Session>, uint64_t > > m_PeerTests; // same as for relay sessions
std::list<std::shared_ptr<I2NPMessage> > m_SendQueue; std::list<std::shared_ptr<I2NPMessage> > m_SendQueue;
i2p::I2NPMessagesHandler m_Handler; i2p::I2NPMessagesHandler m_Handler;
bool m_IsDataReceived; bool m_IsDataReceived;
@ -377,6 +391,43 @@ namespace transport
uint64_t m_LastResendTime, m_LastResendAttemptTime; // in milliseconds 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) inline uint64_t CreateHeaderMask (const uint8_t * kh, const uint8_t * nonce)
{ {
uint64_t data = 0; uint64_t data = 0;

360
libi2pd/Streaming.cpp

@ -68,18 +68,19 @@ namespace stream
Stream::Stream (boost::asio::io_service& service, StreamingDestination& local, Stream::Stream (boost::asio::io_service& service, StreamingDestination& local,
std::shared_ptr<const i2p::data::LeaseSet> remote, int port): m_Service (service), std::shared_ptr<const i2p::data::LeaseSet> remote, int port): m_Service (service),
m_SendStreamID (0), m_SequenceNumber (0), m_SendStreamID (0), m_SequenceNumber (0), m_DropWindowDelaySequenceNumber (0),
m_TunnelsChangeSequenceNumber (0), m_LastReceivedSequenceNumber (-1), m_PreviousReceivedSequenceNumber (-1), m_TunnelsChangeSequenceNumber (0), m_LastReceivedSequenceNumber (-1), m_PreviousReceivedSequenceNumber (-1),
m_LastConfirmedReceivedSequenceNumber (0), // for limit inbound speed m_LastConfirmedReceivedSequenceNumber (0), // for limit inbound speed
m_Status (eStreamStatusNew), m_IsAckSendScheduled (false), m_IsNAcked (false), m_IsFirstACK (false), m_Status (eStreamStatusNew), m_IsAckSendScheduled (false), m_IsNAcked (false), m_IsFirstACK (false),
m_IsResendNeeded (false), m_IsFirstRttSample (false), m_IsSendTime (true), m_IsWinDropped (true), m_IsResendNeeded (false), m_IsFirstRttSample (false), m_IsSendTime (true), m_IsWinDropped (false),
m_IsTimeOutResend (false), m_LocalDestination (local), m_IsTimeOutResend (false), m_IsImmediateAckRequested (false), m_LocalDestination (local),
m_RemoteLeaseSet (remote), m_ReceiveTimer (m_Service), m_SendTimer (m_Service), m_ResendTimer (m_Service), 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_AckSendTimer (m_Service), m_NumSentBytes (0), m_NumReceivedBytes (0), m_Port (port),
m_RTT (INITIAL_RTT), m_SlowRTT (INITIAL_RTT), m_WindowSize (INITIAL_WINDOW_SIZE), m_LastWindowDropSize (0), m_WindowIncCounter (0), m_RTO (INITIAL_RTO), 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_PrevRTT (INITIAL_RTT), m_Jitter (0), m_MinPacingTime (0), m_Jitter (0), m_MinPacingTime (0),
m_PacingTime (INITIAL_PACING_TIME), m_PacingTimeRem (0), m_DropWindowDelayTime (0), m_LastSendTime (0), m_PacingTime (INITIAL_PACING_TIME), m_PacingTimeRem (0), m_LastSendTime (0),
m_LastACKSendTime (0), m_PacketACKInterval (1), m_PacketACKIntervalRem (0), // for limit inbound speed m_LastACKSendTime (0), m_PacketACKInterval (1), m_PacketACKIntervalRem (0), // for limit inbound speed
m_NumResendAttempts (0), m_NumPacketsToSend (0), m_MTU (STREAMING_MTU) m_NumResendAttempts (0), m_NumPacketsToSend (0), m_MTU (STREAMING_MTU)
{ {
@ -95,18 +96,18 @@ namespace stream
} }
Stream::Stream (boost::asio::io_service& service, StreamingDestination& local): Stream::Stream (boost::asio::io_service& service, StreamingDestination& local):
m_Service (service), m_SendStreamID (0), m_SequenceNumber (0), m_Service (service), m_SendStreamID (0), m_SequenceNumber (0), m_DropWindowDelaySequenceNumber (0),
m_TunnelsChangeSequenceNumber (0), m_LastReceivedSequenceNumber (-1), m_PreviousReceivedSequenceNumber (-1), m_TunnelsChangeSequenceNumber (0), m_LastReceivedSequenceNumber (-1), m_PreviousReceivedSequenceNumber (-1),
m_LastConfirmedReceivedSequenceNumber (0), // for limit inbound speed m_LastConfirmedReceivedSequenceNumber (0), // for limit inbound speed
m_Status (eStreamStatusNew), m_IsAckSendScheduled (false), m_IsNAcked (false), m_IsFirstACK (false), m_Status (eStreamStatusNew), m_IsAckSendScheduled (false), m_IsNAcked (false), m_IsFirstACK (false),
m_IsResendNeeded (false), m_IsFirstRttSample (false), m_IsSendTime (true), m_IsWinDropped (true), m_IsResendNeeded (false), m_IsFirstRttSample (false), m_IsSendTime (true), m_IsWinDropped (false),
m_IsTimeOutResend (false), m_LocalDestination (local), m_IsTimeOutResend (false), m_IsImmediateAckRequested (false), m_LocalDestination (local),
m_ReceiveTimer (m_Service), m_SendTimer (m_Service), m_ResendTimer (m_Service), m_AckSendTimer (m_Service), 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_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_WindowIncCounter (0), m_WindowSize (INITIAL_WINDOW_SIZE), m_LastWindowDropSize (0), m_WindowDropTargetSize (0), m_WindowIncCounter (0),
m_RTO (INITIAL_RTO), m_AckDelay (local.GetOwner ()->GetStreamingAckDelay ()), m_RTO (INITIAL_RTO), m_AckDelay (local.GetOwner ()->GetStreamingAckDelay ()),
m_PrevRTTSample (INITIAL_RTT), m_PrevRTT (INITIAL_RTT), m_Jitter (0), m_MinPacingTime (0), m_PrevRTTSample (INITIAL_RTT), m_Jitter (0), m_MinPacingTime (0),
m_PacingTime (INITIAL_PACING_TIME), m_PacingTimeRem (0), m_DropWindowDelayTime (0), m_LastSendTime (0), m_PacingTime (INITIAL_PACING_TIME), m_PacingTimeRem (0), m_LastSendTime (0),
m_LastACKSendTime (0), m_PacketACKInterval (1), m_PacketACKIntervalRem (0), // for limit inbound speed m_LastACKSendTime (0), m_PacketACKInterval (1), m_PacketACKIntervalRem (0), // for limit inbound speed
m_NumResendAttempts (0), m_NumPacketsToSend (0), m_MTU (STREAMING_MTU) m_NumResendAttempts (0), m_NumPacketsToSend (0), m_MTU (STREAMING_MTU)
{ {
@ -183,10 +184,29 @@ namespace stream
ProcessAck (packet); ProcessAck (packet);
int32_t receivedSeqn = packet->GetSeqn (); int32_t receivedSeqn = packet->GetSeqn ();
if (!receivedSeqn && !packet->GetFlags ()) if (!receivedSeqn && m_LastReceivedSequenceNumber >= 0)
{ {
// plain ack uint16_t flags = packet->GetFlags ();
LogPrint (eLogDebug, "Streaming: Plain ACK received"); if (flags)
// plain ack with options
ProcessOptions (flags, packet);
else
// plain ack
{
LogPrint (eLogDebug, "Streaming: Plain ACK received");
if (m_IsImmediateAckRequested)
{
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
if (m_IsFirstRttSample)
{
m_RTT = ts - m_LastSendTime;
m_IsFirstRttSample = false;
}
else
m_RTT = (m_RTT + (ts - m_LastSendTime)) / 2;
m_IsImmediateAckRequested = false;
}
}
m_LocalDestination.DeletePacket (packet); m_LocalDestination.DeletePacket (packet);
return; return;
} }
@ -325,12 +345,16 @@ namespace stream
LogPrint (eLogInfo, "Streaming: Invalid option size ", optionSize, " Discarded"); LogPrint (eLogInfo, "Streaming: Invalid option size ", optionSize, " Discarded");
return false; return false;
} }
if (!flags) return true;
bool immediateAckRequested = false;
if (flags & PACKET_FLAG_DELAY_REQUESTED) if (flags & PACKET_FLAG_DELAY_REQUESTED)
{ {
if (!m_IsAckSendScheduled) uint16_t delayRequested = bufbe16toh (optionData);
if (!delayRequested) // 0 requests an immediate ack
immediateAckRequested = true;
else if (!m_IsAckSendScheduled)
{ {
uint16_t delayRequested = bufbe16toh (optionData); if (delayRequested < m_RTT)
if (delayRequested > 0 && delayRequested < m_RTT)
{ {
m_IsAckSendScheduled = true; m_IsAckSendScheduled = true;
m_AckSendTimer.expires_from_now (boost::posix_time::milliseconds(delayRequested)); m_AckSendTimer.expires_from_now (boost::posix_time::milliseconds(delayRequested));
@ -339,8 +363,15 @@ namespace stream
} }
if (delayRequested >= DELAY_CHOKING) if (delayRequested >= DELAY_CHOKING)
{ {
m_WindowSize = 1; if (!m_IsWinDropped)
m_WindowIncCounter = 0; {
m_WindowDropTargetSize = MIN_WINDOW_SIZE;
m_LastWindowDropSize = 0;
m_WindowIncCounter = 0;
m_IsWinDropped = true; // don't drop window twice
m_DropWindowDelaySequenceNumber = m_SequenceNumber;
UpdatePacingTime ();
}
} }
} }
optionData += 2; optionData += 2;
@ -424,6 +455,8 @@ namespace stream
return false; return false;
} }
} }
if (immediateAckRequested)
SendQuickAck ();
return true; return true;
} }
@ -503,58 +536,47 @@ namespace stream
m_LocalDestination.DeletePacket (sentPacket); m_LocalDestination.DeletePacket (sentPacket);
acknowledged = true; acknowledged = true;
if (m_WindowSize < MAX_WINDOW_SIZE && !m_IsFirstACK) if (m_WindowSize < MAX_WINDOW_SIZE && !m_IsFirstACK)
m_WindowIncCounter++; if (m_RTT < m_LocalDestination.GetRandom () % INITIAL_RTT) // dirty
m_WindowIncCounter++;
} }
else else
break; break;
} }
if (rttSample != INT_MAX) if (rttSample != INT_MAX)
{ {
if (m_IsFirstRttSample) if (m_IsFirstRttSample && !m_IsFirstACK)
{ {
m_RTT = rttSample; m_RTT = rttSample;
m_SlowRTT = rttSample; m_SlowRTT = rttSample;
m_SlowRTT2 = rttSample;
m_PrevRTTSample = rttSample; m_PrevRTTSample = rttSample;
if (m_RoutingSession) m_Jitter = rttSample / 10; // 10%
m_RoutingSession->SetSharedRoutingPath ( m_Jitter += 5; // for low-latency connections
std::make_shared<i2p::garlic::GarlicRoutingPath> (
i2p::garlic::GarlicRoutingPath{m_CurrentOutboundTunnel, m_CurrentRemoteLease, (int)m_RTT, 0}));
m_IsFirstRttSample = false; m_IsFirstRttSample = false;
} }
else else
m_RTT = RTT_EWMA_ALPHA * m_RTT + (1.0 - RTT_EWMA_ALPHA) * rttSample; m_RTT = (m_PrevRTTSample + rttSample) / 2;
// calculate jitter if (!m_IsWinDropped)
int jitter = 0;
if (rttSample > m_PrevRTTSample)
jitter = rttSample - m_PrevRTTSample;
else if (rttSample < m_PrevRTTSample)
jitter = m_PrevRTTSample - rttSample;
else
jitter = std::round (rttSample / 10); // 10%
jitter += 5; // for low-latency connections
m_Jitter = std::round (RTT_EWMA_ALPHA * jitter + (1.0 - RTT_EWMA_ALPHA) * m_Jitter);
m_PrevRTTSample = rttSample;
//
// delay-based CC
if ((m_PrevRTT > m_SlowRTT + m_Jitter) && (m_RTT > m_SlowRTT + m_Jitter) && !m_IsWinDropped) // Drop window if RTT grows too fast, late detection
{ {
if (m_LastWindowDropSize) m_SlowRTT = SLOWRTT_EWMA_ALPHA * m_RTT + (1.0 - SLOWRTT_EWMA_ALPHA) * m_SlowRTT;
m_LastWindowDropSize = (m_LastWindowDropSize + m_WindowSize) / 2; m_SlowRTT2 = RTT_EWMA_ALPHA * m_RTT + (1.0 - RTT_EWMA_ALPHA) * m_SlowRTT2;
// calculate jitter
double jitter = 0;
if (rttSample > m_PrevRTTSample)
jitter = rttSample - m_PrevRTTSample;
else if (rttSample < m_PrevRTTSample)
jitter = m_PrevRTTSample - rttSample;
else else
m_LastWindowDropSize = m_WindowSize; jitter = rttSample / 10; // 10%
m_WindowSize = m_WindowSize / 2; // /2 jitter += 5; // for low-latency connections
if (m_WindowSize < MIN_WINDOW_SIZE) m_WindowSize = MIN_WINDOW_SIZE; m_Jitter = (0.05 * jitter) + (1.0 - 0.05) * m_Jitter;
m_WindowIncCounter = 0;
m_DropWindowDelayTime = ts + m_SlowRTT;
m_IsFirstACK = true;
m_IsWinDropped = true; // don't drop window twice
} }
//
// 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
ProcessWindowDrop ();
UpdatePacingTime (); UpdatePacingTime ();
if (rttSample < m_RTT) // need for delay-based CC m_PrevRTTSample = rttSample;
m_SlowRTT = RTT_EWMA_ALPHA * rttSample + (1.0 - RTT_EWMA_ALPHA) * m_SlowRTT;
else
m_SlowRTT = RTT_EWMA_ALPHA * m_RTT + (1.0 - RTT_EWMA_ALPHA) * m_SlowRTT;
m_PrevRTT = m_RTT;
bool wasInitial = m_RTO == INITIAL_RTO; 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)); // TODO: implement it better
@ -562,26 +584,52 @@ namespace stream
if (wasInitial) if (wasInitial)
ScheduleResend (); ScheduleResend ();
} }
if ( ts > m_DropWindowDelayTime) if (m_IsWinDropped && ackThrough > m_DropWindowDelaySequenceNumber)
{
m_IsFirstRttSample = true;
m_IsWinDropped = false; m_IsWinDropped = false;
}
if (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) if (acknowledged || m_IsNAcked)
{ {
ScheduleResend (); ScheduleResend ();
} }
if ((m_SendBuffer.IsEmpty () && m_SentPackets.size () > 0) // tail loss if (m_SendBuffer.IsEmpty () && m_SentPackets.size () > 0) // tail loss
|| int(m_SentPackets.size ()) > m_WindowSize) // or we drop window
{ {
m_IsResendNeeded = true; m_IsResendNeeded = true;
m_RTO = std::max (MIN_RTO, (int)(m_RTT * 1.5 + m_Jitter)); // to prevent spurious retransmit
} }
if (m_SentPackets.empty () && m_SendBuffer.IsEmpty ()) if (m_SentPackets.empty () && m_SendBuffer.IsEmpty ())
{ {
m_ResendTimer.cancel (); m_ResendTimer.cancel ();
m_SendTimer.cancel (); m_SendTimer.cancel ();
} }
if (acknowledged && m_IsFirstACK)
{
if (m_RoutingSession)
m_RoutingSession->SetSharedRoutingPath (
std::make_shared<i2p::garlic::GarlicRoutingPath> (
i2p::garlic::GarlicRoutingPath{m_CurrentOutboundTunnel, m_CurrentRemoteLease, (int)m_RTT, 0}));
m_IsFirstACK = false;
}
if (acknowledged) if (acknowledged)
{ {
m_NumResendAttempts = 0; m_NumResendAttempts = 0;
m_IsFirstACK = false; m_IsTimeOutResend = false;
SendBuffer (); SendBuffer ();
} }
if (m_Status == eStreamStatusClosed) if (m_Status == eStreamStatusClosed)
@ -781,13 +829,22 @@ namespace stream
// for limit inbound speed // for limit inbound speed
auto ts = i2p::util::GetMillisecondsSinceEpoch (); auto ts = i2p::util::GetMillisecondsSinceEpoch ();
int numPackets = 0; int numPackets = 0;
bool lostPackets = false;
int64_t passedTime = m_PacketACKInterval * INITIAL_WINDOW_SIZE; // in microseconds // while m_LastACKSendTime == 0 int64_t passedTime = m_PacketACKInterval * INITIAL_WINDOW_SIZE; // in microseconds // while m_LastACKSendTime == 0
if (m_LastACKSendTime) if (m_LastACKSendTime)
passedTime = (ts - m_LastACKSendTime)*1000; // in microseconds passedTime = (ts - m_LastACKSendTime)*1000; // in microseconds
numPackets = (passedTime + m_PacketACKIntervalRem) / m_PacketACKInterval; numPackets = (passedTime + m_PacketACKIntervalRem) / m_PacketACKInterval;
m_PacketACKIntervalRem = (passedTime + m_PacketACKIntervalRem) - (numPackets * m_PacketACKInterval); m_PacketACKIntervalRem = (passedTime + m_PacketACKIntervalRem) - (numPackets * m_PacketACKInterval);
if (m_LastConfirmedReceivedSequenceNumber + numPackets < m_LastReceivedSequenceNumber) if (m_LastConfirmedReceivedSequenceNumber + numPackets < m_LastReceivedSequenceNumber)
{
lastReceivedSeqn = m_LastConfirmedReceivedSequenceNumber + numPackets; lastReceivedSeqn = m_LastConfirmedReceivedSequenceNumber + numPackets;
if (!m_IsAckSendScheduled)
{
auto ackTimeout = m_RTT/10;
if (ackTimeout > m_AckDelay) ackTimeout = m_AckDelay;
ScheduleAck (ackTimeout);
}
}
if (numPackets == 0) return; if (numPackets == 0) return;
// for limit inbound speed // for limit inbound speed
if (!m_SavedPackets.empty ()) if (!m_SavedPackets.empty ())
@ -795,8 +852,26 @@ namespace stream
for (auto it: m_SavedPackets) for (auto it: m_SavedPackets)
{ {
auto seqn = it->GetSeqn (); auto seqn = it->GetSeqn ();
if (m_LastConfirmedReceivedSequenceNumber + numPackets < int(seqn)) break; // for limit inbound speed // for limit inbound speed
if ((int)seqn > lastReceivedSeqn) lastReceivedSeqn = seqn; if (m_LastConfirmedReceivedSequenceNumber + numPackets < int(seqn))
{
if (!m_IsAckSendScheduled)
{
auto ackTimeout = m_RTT/10;
if (ackTimeout > m_AckDelay) ackTimeout = m_AckDelay;
ScheduleAck (ackTimeout);
}
if (lostPackets)
break;
else
return;
}
// for limit inbound speed
if ((int)seqn > lastReceivedSeqn)
{
lastReceivedSeqn = seqn;
lostPackets = true; // for limit inbound speed
}
} }
} }
if (lastReceivedSeqn < 0) if (lastReceivedSeqn < 0)
@ -858,13 +933,22 @@ namespace stream
} }
packet[size] = 0; packet[size] = 0;
size++; // resend delay size++; // resend delay
htobuf16 (packet + size, choking ? PACKET_FLAG_DELAY_REQUESTED : 0); // no flags set or delay bool requestImmediateAck = false;
if (!choking)
requestImmediateAck = m_LastSendTime && ts > m_LastSendTime + REQUEST_IMMEDIATE_ACK_INTERVAL &&
ts > m_LastSendTime + REQUEST_IMMEDIATE_ACK_INTERVAL + m_LocalDestination.GetRandom () % REQUEST_IMMEDIATE_ACK_INTERVAL_VARIANCE;
htobe16buf (packet + size, (choking || requestImmediateAck) ? PACKET_FLAG_DELAY_REQUESTED : 0); // no flags set or delay requested
size += 2; // flags size += 2; // flags
if (choking) if (choking || requestImmediateAck)
{ {
htobuf16 (packet + size, 2); // 2 bytes delay interval htobe16buf (packet + size, 2); // 2 bytes delay interval
htobuf16 (packet + size + 2, DELAY_CHOKING); // set choking interval htobe16buf (packet + size + 2, choking ? DELAY_CHOKING : 0); // set choking or immediated ack interval
size += 2; size += 2;
if (requestImmediateAck) // ack request sent
{
m_LastSendTime = ts;
m_IsImmediateAckRequested = true;
}
} }
else else
htobuf16 (packet + size, 0); // no options htobuf16 (packet + size, 0); // no options
@ -1135,45 +1219,37 @@ namespace stream
{ {
if (ecode != boost::asio::error::operation_aborted) if (ecode != boost::asio::error::operation_aborted)
{ {
if (m_WindowIncCounter && m_WindowSize < MAX_WINDOW_SIZE)
{
if (m_LastWindowDropSize && (m_LastWindowDropSize > m_WindowSize))
{
m_WindowSize += 2.001-(2/((m_LastWindowDropSize+(1/m_WindowSize))/m_WindowSize)); // some magic here
m_WindowIncCounter --;
}
else
{
m_WindowSize += 1;
m_WindowIncCounter --;
}
if (m_WindowSize > MAX_WINDOW_SIZE) m_WindowSize = MAX_WINDOW_SIZE;
UpdatePacingTime ();
}
auto ts = i2p::util::GetMillisecondsSinceEpoch (); auto ts = i2p::util::GetMillisecondsSinceEpoch ();
if (m_LastSendTime && ts*1000 > m_LastSendTime*1000 + m_PacingTime) if (m_LastSendTime && ts*1000 > m_LastSendTime*1000 + m_PacingTime)
{ {
m_NumPacketsToSend = ((ts*1000 - m_LastSendTime*1000) + m_PacingTimeRem) / 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); m_PacingTimeRem = ((ts*1000 - m_LastSendTime*1000) + m_PacingTimeRem) - (m_NumPacketsToSend * m_PacingTime);
m_IsSendTime = true; m_IsSendTime = true;
if (m_IsNAcked || m_IsResendNeeded) // resend packets if (m_WindowIncCounter && m_WindowSize < MAX_WINDOW_SIZE && !m_SendBuffer.IsEmpty ())
{
for (int i = 0; i < m_NumPacketsToSend; i++)
{
if (m_WindowIncCounter)
{
if (m_LastWindowDropSize && (m_LastWindowDropSize >= m_WindowSize))
m_WindowSize += 1 - (1 / ((m_LastWindowDropSize + PREV_SPEED_KEEP_TIME_COEFF) / m_WindowSize)); // some magic here
else if (m_LastWindowDropSize && (m_LastWindowDropSize < m_WindowSize))
m_WindowSize += (m_WindowSize - (m_LastWindowDropSize - PREV_SPEED_KEEP_TIME_COEFF)) / m_WindowSize; // some magic here
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 ();
}
}
}
if (m_IsNAcked)
ResendPacket ();
else if (m_IsResendNeeded) // resend packets
ResendPacket (); ResendPacket ();
// delay-based CC // delay-based CC
else if (!m_IsWinDropped && int(m_SentPackets.size ()) == m_WindowSize) // we sending packets too fast, early detection else if (!m_IsWinDropped && int(m_SentPackets.size ()) == m_WindowSize) // we sending packets too fast, early detection
{ ProcessWindowDrop ();
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
if (m_LastWindowDropSize)
m_LastWindowDropSize = (m_LastWindowDropSize + m_WindowSize) / 2;
else
m_LastWindowDropSize = m_WindowSize;
m_WindowSize = m_WindowSize / 2; // /2
if (m_WindowSize < MIN_WINDOW_SIZE) m_WindowSize = MIN_WINDOW_SIZE;
m_WindowIncCounter = 0;
m_DropWindowDelayTime = ts + m_SlowRTT;
m_IsFirstACK = true;
m_IsWinDropped = true; // don't drop window twice
UpdatePacingTime ();
}
else if (m_WindowSize > int(m_SentPackets.size ())) // send packets else if (m_WindowSize > int(m_SentPackets.size ())) // send packets
SendBuffer (); SendBuffer ();
} }
@ -1265,31 +1341,19 @@ namespace stream
if (m_NumResendAttempts == 1 && m_RTO != INITIAL_RTO) if (m_NumResendAttempts == 1 && m_RTO != INITIAL_RTO)
{ {
// loss-based CC // loss-based CC
if (!m_IsWinDropped) if (!m_IsWinDropped && LOSS_BASED_CONTROL_ENABLED)
{ ProcessWindowDrop ();
if (m_LastWindowDropSize)
m_LastWindowDropSize = (m_LastWindowDropSize + m_WindowSize) / 2;
else
m_LastWindowDropSize = m_WindowSize;
m_WindowSize = m_WindowSize / 2; // /2
if (m_WindowSize < MIN_WINDOW_SIZE) m_WindowSize = MIN_WINDOW_SIZE;
m_WindowIncCounter = 0;
m_IsWinDropped = true; // don't drop window twice
m_DropWindowDelayTime = ts + m_SlowRTT;
m_IsFirstACK = true;
UpdatePacingTime ();
}
} }
else if (m_IsTimeOutResend) else if (m_IsTimeOutResend)
{ {
m_IsTimeOutResend = false; m_IsTimeOutResend = false;
m_RTO = INITIAL_RTO; // drop RTO to initial upon tunnels pair change m_RTO = INITIAL_RTO; // drop RTO to initial upon tunnels pair change
m_WindowSize = INITIAL_WINDOW_SIZE; m_WindowDropTargetSize = INITIAL_WINDOW_SIZE;
m_LastWindowDropSize = 0; m_LastWindowDropSize = 0;
m_WindowIncCounter = 0; m_WindowIncCounter = 0;
m_IsWinDropped = true; m_IsWinDropped = true;
m_IsFirstRttSample = true; m_IsFirstRttSample = true;
m_DropWindowDelayTime = 0; m_DropWindowDelaySequenceNumber = 0;
m_IsFirstACK = true; m_IsFirstACK = true;
UpdatePacingTime (); UpdatePacingTime ();
if (m_RoutingSession) m_RoutingSession->SetSharedRoutingPath (nullptr); if (m_RoutingSession) m_RoutingSession->SetSharedRoutingPath (nullptr);
@ -1359,6 +1423,7 @@ namespace stream
void Stream::UpdateCurrentRemoteLease (bool expired) void Stream::UpdateCurrentRemoteLease (bool expired)
{ {
bool isLeaseChanged = true;
if (!m_RemoteLeaseSet || m_RemoteLeaseSet->IsExpired ()) if (!m_RemoteLeaseSet || m_RemoteLeaseSet->IsExpired ())
{ {
auto remoteLeaseSet = m_LocalDestination.GetOwner ()->FindLeaseSet (m_RemoteIdentity->GetIdentHash ()); auto remoteLeaseSet = m_LocalDestination.GetOwner ()->FindLeaseSet (m_RemoteIdentity->GetIdentHash ());
@ -1416,10 +1481,15 @@ namespace stream
} }
if (!updated) if (!updated)
{ {
uint32_t i = rand () % leases.size (); uint32_t i = m_LocalDestination.GetRandom () % leases.size ();
if (m_CurrentRemoteLease && leases[i]->tunnelID == m_CurrentRemoteLease->tunnelID) if (m_CurrentRemoteLease && leases[i]->tunnelID == m_CurrentRemoteLease->tunnelID)
{
// make sure we don't select previous // make sure we don't select previous
i = (i + 1) % leases.size (); // if so, pick next if (leases.size () > 1)
i = (i + 1) % leases.size (); // if so, pick next
else
isLeaseChanged = false;
}
m_CurrentRemoteLease = leases[i]; m_CurrentRemoteLease = leases[i];
} }
} }
@ -1436,16 +1506,23 @@ namespace stream
LogPrint (eLogWarning, "Streaming: Remote LeaseSet not found"); LogPrint (eLogWarning, "Streaming: Remote LeaseSet not found");
m_CurrentRemoteLease = nullptr; m_CurrentRemoteLease = nullptr;
} }
// drop window to initial upon RemoteLease change if (isLeaseChanged)
m_RTO = INITIAL_RTO; {
m_WindowSize = INITIAL_WINDOW_SIZE; // drop window to initial upon RemoteLease change
m_LastWindowDropSize = 0; m_RTO = INITIAL_RTO;
m_WindowIncCounter = 0; if (m_WindowSize > INITIAL_WINDOW_SIZE)
m_IsWinDropped = true; {
m_IsFirstRttSample = true; m_WindowDropTargetSize = std::max (m_WindowSize/2, (float)INITIAL_WINDOW_SIZE);
m_DropWindowDelayTime = 0; m_IsWinDropped = true;
m_IsFirstACK = true; }
UpdatePacingTime (); else
m_WindowSize = INITIAL_WINDOW_SIZE;
m_LastWindowDropSize = 0;
m_WindowIncCounter = 0;
m_IsFirstRttSample = true;
m_IsFirstACK = true;
UpdatePacingTime ();
}
} }
void Stream::ResetRoutingPath () void Stream::ResetRoutingPath ()
@ -1464,10 +1541,29 @@ namespace stream
if (m_MinPacingTime && m_PacingTime < m_MinPacingTime) if (m_MinPacingTime && m_PacingTime < m_MinPacingTime)
m_PacingTime = m_MinPacingTime; m_PacingTime = m_MinPacingTime;
} }
void Stream::ProcessWindowDrop ()
{
if (m_WindowSize > m_LastWindowDropSize)
m_LastWindowDropSize = (m_LastWindowDropSize + m_WindowSize) / 2;
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;
m_WindowIncCounter = 0; // disable window growth
m_DropWindowDelaySequenceNumber = m_SequenceNumber;
m_IsFirstACK = true; // ignore first RTT sample
m_IsWinDropped = true; // don't drop window twice
UpdatePacingTime ();
}
StreamingDestination::StreamingDestination (std::shared_ptr<i2p::client::ClientDestination> owner, uint16_t localPort, bool gzip): StreamingDestination::StreamingDestination (std::shared_ptr<i2p::client::ClientDestination> owner, uint16_t localPort, bool gzip):
m_Owner (owner), m_LocalPort (localPort), m_Gzip (gzip), m_Owner (owner), m_LocalPort (localPort), m_Gzip (gzip),
m_PendingIncomingTimer (m_Owner->GetService ()) m_PendingIncomingTimer (m_Owner->GetService ()),
m_LastCleanupTime (i2p::util::GetSecondsSinceEpoch ())
{ {
} }
@ -1656,10 +1752,12 @@ namespace stream
m_IncomingStreams.erase (stream->GetSendStreamID ()); m_IncomingStreams.erase (stream->GetSendStreamID ());
if (m_LastStream == stream) m_LastStream = nullptr; if (m_LastStream == stream) m_LastStream = nullptr;
} }
if (m_Streams.empty ()) auto ts = i2p::util::GetSecondsSinceEpoch ();
if (m_Streams.empty () || ts > m_LastCleanupTime + STREAMING_DESTINATION_POOLS_CLEANUP_INTERVAL)
{ {
m_PacketsPool.CleanUp (); m_PacketsPool.CleanUp ();
m_I2NPMsgsPool.CleanUp (); m_I2NPMsgsPool.CleanUp ();
m_LastCleanupTime = ts;
} }
} }
@ -1795,5 +1893,15 @@ namespace stream
return msg; return msg;
} }
uint32_t StreamingDestination::GetRandom ()
{
if (m_Owner)
{
auto pool = m_Owner->GetTunnelPool ();
if (pool)
return pool->GetRng ()();
}
return rand ();
}
} }
} }

31
libi2pd/Streaming.h

@ -54,9 +54,11 @@ namespace stream
const size_t COMPRESSION_THRESHOLD_SIZE = 66; const size_t COMPRESSION_THRESHOLD_SIZE = 66;
const int MAX_NUM_RESEND_ATTEMPTS = 10; const int MAX_NUM_RESEND_ATTEMPTS = 10;
const int INITIAL_WINDOW_SIZE = 10; const int INITIAL_WINDOW_SIZE = 10;
const int MIN_WINDOW_SIZE = 1; const int MIN_WINDOW_SIZE = 2;
const int MAX_WINDOW_SIZE = 1024; const int MAX_WINDOW_SIZE = 512;
const double RTT_EWMA_ALPHA = 0.125; 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 MIN_RTO = 20; // in milliseconds
const int INITIAL_RTT = 8000; // in milliseconds const int INITIAL_RTT = 8000; // in milliseconds
const int INITIAL_RTO = 9000; // in milliseconds const int INITIAL_RTO = 9000; // in milliseconds
@ -68,7 +70,11 @@ namespace stream
const int MAX_RECEIVE_TIMEOUT = 20; // in seconds const int MAX_RECEIVE_TIMEOUT = 20; // in seconds
const uint16_t DELAY_CHOKING = 60000; // in milliseconds const uint16_t DELAY_CHOKING = 60000; // in milliseconds
const uint64_t SEND_INTERVAL = 1000; // in microseconds const uint64_t SEND_INTERVAL = 1000; // 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
const uint64_t STREAMING_DESTINATION_POOLS_CLEANUP_INTERVAL = 646; // in seconds
struct Packet struct Packet
{ {
size_t len, offset; size_t len, offset;
@ -241,11 +247,13 @@ namespace stream
void HandleAckSendTimer (const boost::system::error_code& ecode); void HandleAckSendTimer (const boost::system::error_code& ecode);
void UpdatePacingTime (); void UpdatePacingTime ();
void ProcessWindowDrop ();
private: private:
boost::asio::io_service& m_Service; boost::asio::io_service& m_Service;
uint32_t m_SendStreamID, m_RecvStreamID, m_SequenceNumber; uint32_t m_SendStreamID, m_RecvStreamID, m_SequenceNumber;
uint32_t m_DropWindowDelaySequenceNumber;
uint32_t m_TunnelsChangeSequenceNumber; uint32_t m_TunnelsChangeSequenceNumber;
int32_t m_LastReceivedSequenceNumber; int32_t m_LastReceivedSequenceNumber;
int32_t m_PreviousReceivedSequenceNumber; int32_t m_PreviousReceivedSequenceNumber;
@ -259,6 +267,7 @@ namespace stream
bool m_IsSendTime; bool m_IsSendTime;
bool m_IsWinDropped; bool m_IsWinDropped;
bool m_IsTimeOutResend; bool m_IsTimeOutResend;
bool m_IsImmediateAckRequested;
StreamingDestination& m_LocalDestination; StreamingDestination& m_LocalDestination;
std::shared_ptr<const i2p::data::IdentityEx> m_RemoteIdentity; 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::crypto::Verifier> m_TransientVerifier; // in case of offline key
@ -275,10 +284,12 @@ namespace stream
uint16_t m_Port; uint16_t m_Port;
SendBufferQueue m_SendBuffer; SendBufferQueue m_SendBuffer;
double m_RTT, m_SlowRTT; double m_RTT, m_SlowRTT, m_SlowRTT2;
float m_WindowSize, m_LastWindowDropSize; float m_WindowSize, m_LastWindowDropSize, m_WindowDropTargetSize;
int m_WindowIncCounter, m_RTO, m_AckDelay, m_PrevRTTSample, m_PrevRTT, m_Jitter; int m_WindowIncCounter, m_RTO, m_AckDelay, m_PrevRTTSample;
uint64_t m_MinPacingTime, m_PacingTime, m_PacingTimeRem, m_DropWindowDelayTime, m_LastSendTime; // microseconds double m_Jitter;
uint64_t m_MinPacingTime, m_PacingTime, m_PacingTimeRem, // microseconds
m_LastSendTime; // miliseconds
uint64_t m_LastACKSendTime, m_PacketACKInterval, m_PacketACKIntervalRem; // for limit inbound speed uint64_t m_LastACKSendTime, m_PacketACKInterval, m_PacketACKIntervalRem; // for limit inbound speed
int m_NumResendAttempts, m_NumPacketsToSend; int m_NumResendAttempts, m_NumPacketsToSend;
size_t m_MTU; size_t m_MTU;
@ -316,6 +327,7 @@ namespace stream
Packet * NewPacket () { return m_PacketsPool.Acquire(); } Packet * NewPacket () { return m_PacketsPool.Acquire(); }
void DeletePacket (Packet * p) { return m_PacketsPool.Release(p); } void DeletePacket (Packet * p) { return m_PacketsPool.Release(p); }
uint32_t GetRandom ();
private: private:
@ -339,7 +351,8 @@ namespace stream
i2p::util::MemoryPool<Packet> m_PacketsPool; i2p::util::MemoryPool<Packet> m_PacketsPool;
i2p::util::MemoryPool<I2NPMessageBuffer<I2NP_MAX_SHORT_MESSAGE_SIZE> > m_I2NPMsgsPool; i2p::util::MemoryPool<I2NPMessageBuffer<I2NP_MAX_SHORT_MESSAGE_SIZE> > m_I2NPMsgsPool;
uint64_t m_LastCleanupTime; // in seconds
public: public:
i2p::data::GzipInflator m_Inflator; i2p::data::GzipInflator m_Inflator;

2
libi2pd/Transports.cpp

@ -599,7 +599,7 @@ namespace transport
} }
LogPrint (eLogInfo, "Transports: No compatible addresses available"); LogPrint (eLogInfo, "Transports: No compatible addresses available");
if (peer->router->IsReachableFrom (i2p::context.GetRouterInfo ())) 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 i2p::data::netdb.SetUnreachable (ident, true); // we are here because all connection attempts failed but router claimed them
peer->Done (); peer->Done ();
std::unique_lock<std::mutex> l(m_PeersMutex); std::unique_lock<std::mutex> l(m_PeersMutex);

14
libi2pd/Tunnel.cpp

@ -411,10 +411,12 @@ namespace tunnel
return tunnel; return tunnel;
} }
std::shared_ptr<TunnelPool> Tunnels::CreateTunnelPool (int numInboundHops, int numOutboundHops, std::shared_ptr<TunnelPool> Tunnels::CreateTunnelPool (int numInboundHops,
int numInboundTunnels, int numOutboundTunnels, int inboundVariance, int outboundVariance) int numOutboundHops, int numInboundTunnels, int numOutboundTunnels,
int inboundVariance, int outboundVariance, bool isHighBandwidth)
{ {
auto pool = std::make_shared<TunnelPool> (numInboundHops, numOutboundHops, numInboundTunnels, numOutboundTunnels, inboundVariance, outboundVariance); auto pool = std::make_shared<TunnelPool> (numInboundHops, numOutboundHops,
numInboundTunnels, numOutboundTunnels, inboundVariance, outboundVariance, isHighBandwidth);
std::unique_lock<std::mutex> l(m_PoolsMutex); std::unique_lock<std::mutex> l(m_PoolsMutex);
m_Pools.push_back (pool); m_Pools.push_back (pool);
return pool; return pool;
@ -705,7 +707,7 @@ namespace tunnel
auto inboundTunnel = GetNextInboundTunnel (); auto inboundTunnel = GetNextInboundTunnel ();
auto router = i2p::transport::transports.RoutesRestricted() ? auto router = i2p::transport::transports.RoutesRestricted() ?
i2p::transport::transports.GetRestrictedPeer() : i2p::transport::transports.GetRestrictedPeer() :
i2p::data::netdb.GetRandomRouter (i2p::context.GetSharedRouterInfo (), false, true); // reachable by us i2p::data::netdb.GetRandomRouter (i2p::context.GetSharedRouterInfo (), false, true, false); // reachable by us
if (!inboundTunnel || !router) return; if (!inboundTunnel || !router) return;
LogPrint (eLogDebug, "Tunnel: Creating one hop outbound tunnel"); LogPrint (eLogDebug, "Tunnel: Creating one hop outbound tunnel");
CreateTunnel<OutboundTunnel> ( CreateTunnel<OutboundTunnel> (
@ -765,7 +767,7 @@ namespace tunnel
int obLen; i2p::config::GetOption("exploratory.outbound.length", obLen); int obLen; i2p::config::GetOption("exploratory.outbound.length", obLen);
int ibNum; i2p::config::GetOption("exploratory.inbound.quantity", ibNum); int ibNum; i2p::config::GetOption("exploratory.inbound.quantity", ibNum);
int obNum; i2p::config::GetOption("exploratory.outbound.quantity", obNum); int obNum; i2p::config::GetOption("exploratory.outbound.quantity", obNum);
m_ExploratoryPool = CreateTunnelPool (ibLen, obLen, ibNum, obNum, 0, 0); m_ExploratoryPool = CreateTunnelPool (ibLen, obLen, ibNum, obNum, 0, 0, false);
m_ExploratoryPool->SetLocalDestination (i2p::context.GetSharedDestination ()); m_ExploratoryPool->SetLocalDestination (i2p::context.GetSharedDestination ());
} }
return; return;
@ -777,7 +779,7 @@ namespace tunnel
auto router = i2p::transport::transports.RoutesRestricted() ? auto router = i2p::transport::transports.RoutesRestricted() ?
i2p::transport::transports.GetRestrictedPeer() : i2p::transport::transports.GetRestrictedPeer() :
// should be reachable by us because we send build request directly // should be reachable by us because we send build request directly
i2p::data::netdb.GetRandomRouter (i2p::context.GetSharedRouterInfo (), false, true); i2p::data::netdb.GetRandomRouter (i2p::context.GetSharedRouterInfo (), false, true, false);
if (!router) { if (!router) {
LogPrint (eLogWarning, "Tunnel: Can't find any router, skip creating tunnel"); LogPrint (eLogWarning, "Tunnel: Can't find any router, skip creating tunnel");
return; return;

5
libi2pd/Tunnel.h

@ -232,8 +232,9 @@ namespace tunnel
void PostTunnelData (const std::vector<std::shared_ptr<I2NPMessage> >& msgs); void PostTunnelData (const std::vector<std::shared_ptr<I2NPMessage> >& msgs);
void AddPendingTunnel (uint32_t replyMsgID, std::shared_ptr<InboundTunnel> tunnel); void AddPendingTunnel (uint32_t replyMsgID, std::shared_ptr<InboundTunnel> tunnel);
void AddPendingTunnel (uint32_t replyMsgID, std::shared_ptr<OutboundTunnel> tunnel); void AddPendingTunnel (uint32_t replyMsgID, std::shared_ptr<OutboundTunnel> tunnel);
std::shared_ptr<TunnelPool> CreateTunnelPool (int numInboundHops, int numOuboundHops, std::shared_ptr<TunnelPool> CreateTunnelPool (int numInboundHops,
int numInboundTunnels, int numOutboundTunnels, int inboundVariance, int outboundVariance); int numOuboundHops, int numInboundTunnels, int numOutboundTunnels,
int inboundVariance, int outboundVariance, bool isHighBandwidth);
void DeleteTunnelPool (std::shared_ptr<TunnelPool> pool); void DeleteTunnelPool (std::shared_ptr<TunnelPool> pool);
void StopTunnelPool (std::shared_ptr<TunnelPool> pool); void StopTunnelPool (std::shared_ptr<TunnelPool> pool);

20
libi2pd/TunnelPool.cpp

@ -41,11 +41,11 @@ namespace tunnel
} }
TunnelPool::TunnelPool (int numInboundHops, int numOutboundHops, int numInboundTunnels, TunnelPool::TunnelPool (int numInboundHops, int numOutboundHops, int numInboundTunnels,
int numOutboundTunnels, int inboundVariance, int outboundVariance): int numOutboundTunnels, int inboundVariance, int outboundVariance, bool isHighBandwidth):
m_NumInboundHops (numInboundHops), m_NumOutboundHops (numOutboundHops), m_NumInboundHops (numInboundHops), m_NumOutboundHops (numOutboundHops),
m_NumInboundTunnels (numInboundTunnels), m_NumOutboundTunnels (numOutboundTunnels), m_NumInboundTunnels (numInboundTunnels), m_NumOutboundTunnels (numOutboundTunnels),
m_InboundVariance (inboundVariance), m_OutboundVariance (outboundVariance), m_InboundVariance (inboundVariance), m_OutboundVariance (outboundVariance),
m_IsActive (true), m_CustomPeerSelector(nullptr), m_IsActive (true), m_IsHighBandwidth (isHighBandwidth), m_CustomPeerSelector(nullptr),
m_Rng(i2p::util::GetMonotonicMicroseconds ()%1000000LL) m_Rng(i2p::util::GetMonotonicMicroseconds ()%1000000LL)
{ {
if (m_NumInboundTunnels > TUNNEL_POOL_MAX_INBOUND_TUNNELS_QUANTITY) if (m_NumInboundTunnels > TUNNEL_POOL_MAX_INBOUND_TUNNELS_QUANTITY)
@ -549,20 +549,22 @@ namespace tunnel
std::shared_ptr<const i2p::data::RouterInfo> TunnelPool::SelectNextHop (std::shared_ptr<const i2p::data::RouterInfo> prevHop, std::shared_ptr<const i2p::data::RouterInfo> TunnelPool::SelectNextHop (std::shared_ptr<const i2p::data::RouterInfo> prevHop,
bool reverse, bool endpoint) const bool reverse, bool endpoint) const
{ {
bool tryHighBandwidth = !IsExploratory (); bool tryClient = !IsExploratory () && !i2p::context.IsLimitedConnectivity ();
std::shared_ptr<const i2p::data::RouterInfo> hop; std::shared_ptr<const i2p::data::RouterInfo> hop;
for (int i = 0; i < TUNNEL_POOL_MAX_HOP_SELECTION_ATTEMPTS; i++) for (int i = 0; i < TUNNEL_POOL_MAX_HOP_SELECTION_ATTEMPTS; i++)
{ {
hop = tryHighBandwidth ? hop = tryClient ?
i2p::data::netdb.GetHighBandwidthRandomRouter (prevHop, reverse, endpoint) : (m_IsHighBandwidth ?
i2p::data::netdb.GetRandomRouter (prevHop, reverse, endpoint); i2p::data::netdb.GetHighBandwidthRandomRouter (prevHop, reverse, endpoint) :
i2p::data::netdb.GetRandomRouter (prevHop, reverse, endpoint, true)):
i2p::data::netdb.GetRandomRouter (prevHop, reverse, endpoint, false);
if (hop) if (hop)
{ {
if (!hop->GetProfile ()->IsBad ()) if (!hop->GetProfile ()->IsBad ())
break; break;
} }
else if (tryHighBandwidth) else if (tryClient)
tryHighBandwidth = false; tryClient = false;
else else
return nullptr; return nullptr;
} }
@ -585,7 +587,7 @@ namespace tunnel
else if (i2p::transport::transports.GetNumPeers () > 100 || else if (i2p::transport::transports.GetNumPeers () > 100 ||
(inbound && i2p::transport::transports.GetNumPeers () > 25)) (inbound && i2p::transport::transports.GetNumPeers () > 25))
{ {
auto r = i2p::transport::transports.GetRandomPeer (!IsExploratory ()); auto r = i2p::transport::transports.GetRandomPeer (m_IsHighBandwidth && !i2p::context.IsLimitedConnectivity ());
if (r && r->IsECIES () && !r->GetProfile ()->IsBad () && if (r && r->IsECIES () && !r->GetProfile ()->IsBad () &&
(numHops > 1 || (r->IsV4 () && (!inbound || r->IsPublished (true))))) // first inbound must be published ipv4 (numHops > 1 || (r->IsV4 () && (!inbound || r->IsPublished (true))))) // first inbound must be published ipv4
{ {

4
libi2pd/TunnelPool.h

@ -62,7 +62,7 @@ namespace tunnel
public: public:
TunnelPool (int numInboundHops, int numOutboundHops, int numInboundTunnels, TunnelPool (int numInboundHops, int numOutboundHops, int numInboundTunnels,
int numOutboundTunnels, int inboundVariance, int outboundVariance); int numOutboundTunnels, int inboundVariance, int outboundVariance, bool isHighBandwidth);
~TunnelPool (); ~TunnelPool ();
std::shared_ptr<i2p::garlic::GarlicDestination> GetLocalDestination () const { return m_LocalDestination; }; std::shared_ptr<i2p::garlic::GarlicDestination> GetLocalDestination () const { return m_LocalDestination; };
@ -146,7 +146,7 @@ namespace tunnel
std::set<std::shared_ptr<OutboundTunnel>, TunnelCreationTimeCmp> m_OutboundTunnels; std::set<std::shared_ptr<OutboundTunnel>, TunnelCreationTimeCmp> m_OutboundTunnels;
mutable std::mutex m_TestsMutex; mutable std::mutex m_TestsMutex;
std::map<uint32_t, std::pair<std::shared_ptr<OutboundTunnel>, std::shared_ptr<InboundTunnel> > > m_Tests; std::map<uint32_t, std::pair<std::shared_ptr<OutboundTunnel>, std::shared_ptr<InboundTunnel> > > m_Tests;
bool m_IsActive; bool m_IsActive, m_IsHighBandwidth;
uint64_t m_NextManageTime; // in seconds uint64_t m_NextManageTime; // in seconds
std::mutex m_CustomPeerSelectorMutex; std::mutex m_CustomPeerSelectorMutex;
ITunnelPeerSelector * m_CustomPeerSelector; ITunnelPeerSelector * m_CustomPeerSelector;

12
libi2pd/util.h

@ -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 * This file is part of Purple i2pd project and licensed under BSD3
* *
@ -131,6 +131,14 @@ namespace util
this->Release (t); this->Release (t);
} }
void ReleaseMt (T * * arr, size_t num)
{
if (!arr || !num) return;
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> template<template<typename, typename...>class C, typename... R>
void ReleaseMt(const C<T *, R...>& c) void ReleaseMt(const C<T *, R...>& c)
{ {
@ -138,7 +146,7 @@ namespace util
for (auto& it: c) for (auto& it: c)
this->Release (it); this->Release (it);
} }
template<typename... TArgs> template<typename... TArgs>
std::shared_ptr<T> AcquireSharedMt (TArgs&&... args) std::shared_ptr<T> AcquireSharedMt (TArgs&&... args)
{ {

6
libi2pd/version.h

@ -18,8 +18,8 @@
#define MAKE_VERSION_NUMBER(a,b,c) ((a*100+b)*100+c) #define MAKE_VERSION_NUMBER(a,b,c) ((a*100+b)*100+c)
#define I2PD_VERSION_MAJOR 2 #define I2PD_VERSION_MAJOR 2
#define I2PD_VERSION_MINOR 53 #define I2PD_VERSION_MINOR 54
#define I2PD_VERSION_MICRO 1 #define I2PD_VERSION_MICRO 0
#define I2PD_VERSION_PATCH 0 #define I2PD_VERSION_PATCH 0
#ifdef GITVER #ifdef GITVER
#define I2PD_VERSION XSTRINGIZE(GITVER) #define I2PD_VERSION XSTRINGIZE(GITVER)
@ -33,7 +33,7 @@
#define I2P_VERSION_MAJOR 0 #define I2P_VERSION_MAJOR 0
#define I2P_VERSION_MINOR 9 #define I2P_VERSION_MINOR 9
#define I2P_VERSION_MICRO 63 #define I2P_VERSION_MICRO 64
#define I2P_VERSION_PATCH 0 #define I2P_VERSION_PATCH 0
#define I2P_VERSION MAKE_VERSION(I2P_VERSION_MAJOR, I2P_VERSION_MINOR, I2P_VERSION_MICRO) #define I2P_VERSION MAKE_VERSION(I2P_VERSION_MAJOR, I2P_VERSION_MINOR, I2P_VERSION_MICRO)
#define I2P_VERSION_NUMBER MAKE_VERSION_NUMBER(I2P_VERSION_MAJOR, I2P_VERSION_MINOR, I2P_VERSION_MICRO) #define I2P_VERSION_NUMBER MAKE_VERSION_NUMBER(I2P_VERSION_MAJOR, I2P_VERSION_MINOR, I2P_VERSION_MICRO)

35
libi2pd_client/AddressBook.cpp

@ -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 * This file is part of Purple i2pd project and licensed under BSD3
* *
@ -15,7 +15,6 @@
#include <condition_variable> #include <condition_variable>
#include <openssl/rand.h> #include <openssl/rand.h>
#include <boost/algorithm/string.hpp> #include <boost/algorithm/string.hpp>
#include <boost/filesystem.hpp>
#include "Base.h" #include "Base.h"
#include "util.h" #include "util.h"
#include "Identity.h" #include "Identity.h"
@ -27,6 +26,14 @@
#include "AddressBook.h" #include "AddressBook.h"
#include "Config.h" #include "Config.h"
#if STD_FILESYSTEM
#include <filesystem>
namespace fs_lib = std::filesystem;
#else
#include <boost/filesystem.hpp>
namespace fs_lib = boost::filesystem;
#endif
namespace i2p namespace i2p
{ {
namespace client namespace client
@ -266,11 +273,11 @@ namespace client
void AddressBookFilesystemStorage::ResetEtags () void AddressBookFilesystemStorage::ResetEtags ()
{ {
LogPrint (eLogError, "Addressbook: Resetting eTags"); LogPrint (eLogError, "Addressbook: Resetting eTags");
for (boost::filesystem::directory_iterator it (etagsPath); it != boost::filesystem::directory_iterator (); ++it) for (fs_lib::directory_iterator it (etagsPath); it != fs_lib::directory_iterator (); ++it)
{ {
if (!boost::filesystem::is_regular_file (it->status ())) if (!fs_lib::is_regular_file (it->status ()))
continue; continue;
boost::filesystem::remove (it->path ()); fs_lib::remove (it->path ());
} }
} }
@ -434,7 +441,7 @@ namespace client
auto ident = std::make_shared<i2p::data::IdentityEx>(); auto ident = std::make_shared<i2p::data::IdentityEx>();
if (ident->FromBase64 (jump)) if (ident->FromBase64 (jump))
{ {
m_Storage->AddAddress (ident); if (m_Storage) m_Storage->AddAddress (ident);
m_Addresses[address] = std::make_shared<Address>(ident->GetIdentHash ()); m_Addresses[address] = std::make_shared<Address>(ident->GetIdentHash ());
LogPrint (eLogInfo, "Addressbook: Added ", address," -> ", ToAddress(ident->GetIdentHash ())); LogPrint (eLogInfo, "Addressbook: Added ", address," -> ", ToAddress(ident->GetIdentHash ()));
} }
@ -445,18 +452,19 @@ namespace client
void AddressBook::InsertFullAddress (std::shared_ptr<const i2p::data::IdentityEx> address) void AddressBook::InsertFullAddress (std::shared_ptr<const i2p::data::IdentityEx> address)
{ {
m_Storage->AddAddress (address); if (m_Storage) m_Storage->AddAddress (address);
} }
std::shared_ptr<const i2p::data::IdentityEx> AddressBook::GetFullAddress (const std::string& address) std::shared_ptr<const i2p::data::IdentityEx> AddressBook::GetFullAddress (const std::string& address)
{ {
auto addr = GetAddress (address); auto addr = GetAddress (address);
if (!addr || !addr->IsIdentHash ()) return nullptr; if (!addr || !addr->IsIdentHash ()) return nullptr;
return m_Storage->GetAddress (addr->identHash); return m_Storage ? m_Storage->GetAddress (addr->identHash) : nullptr;
} }
void AddressBook::LoadHosts () void AddressBook::LoadHosts ()
{ {
if (!m_Storage) return;
if (m_Storage->Load (m_Addresses) > 0) if (m_Storage->Load (m_Addresses) > 0)
{ {
m_IsLoaded = true; m_IsLoaded = true;
@ -527,15 +535,18 @@ namespace client
ident->GetSigningKeyType () != i2p::data::SIGNING_KEY_TYPE_DSA_SHA1) // don't replace by DSA ident->GetSigningKeyType () != i2p::data::SIGNING_KEY_TYPE_DSA_SHA1) // don't replace by DSA
{ {
it->second->identHash = ident->GetIdentHash (); it->second->identHash = ident->GetIdentHash ();
m_Storage->AddAddress (ident); if (m_Storage)
m_Storage->RemoveAddress (it->second->identHash); {
m_Storage->AddAddress (ident);
m_Storage->RemoveAddress (it->second->identHash);
}
LogPrint (eLogInfo, "Addressbook: Updated host: ", name); LogPrint (eLogInfo, "Addressbook: Updated host: ", name);
} }
} }
else else
{ {
m_Addresses.emplace (name, std::make_shared<Address>(ident->GetIdentHash ())); m_Addresses.emplace (name, std::make_shared<Address>(ident->GetIdentHash ()));
m_Storage->AddAddress (ident); if (m_Storage) m_Storage->AddAddress (ident);
if (is_update) if (is_update)
LogPrint (eLogInfo, "Addressbook: Added new host: ", name); LogPrint (eLogInfo, "Addressbook: Added new host: ", name);
} }
@ -547,7 +558,7 @@ namespace client
if (numAddresses > 0) if (numAddresses > 0)
{ {
if (!incomplete) m_IsLoaded = true; if (!incomplete) m_IsLoaded = true;
m_Storage->Save (m_Addresses); if (m_Storage) m_Storage->Save (m_Addresses);
} }
return !incomplete; return !incomplete;
} }

5
libi2pd_client/AddressBook.h

@ -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 * This file is part of Purple i2pd project and licensed under BSD3
* *
@ -99,7 +99,8 @@ namespace client
std::string ToAddress(std::shared_ptr<const i2p::data::IdentityEx> ident) { return ToAddress(ident->GetIdentHash ()); } std::string ToAddress(std::shared_ptr<const i2p::data::IdentityEx> ident) { return ToAddress(ident->GetIdentHash ()); }
bool GetEtag (const i2p::data::IdentHash& subscription, std::string& etag, std::string& lastModified); bool GetEtag (const i2p::data::IdentHash& subscription, std::string& etag, std::string& lastModified);
bool IsEnabled () const { return m_IsEnabled; }
private: private:
void StartSubscriptions (); void StartSubscriptions ();

13
libi2pd_client/ClientContext.cpp

@ -474,8 +474,9 @@ namespace client
options[I2CP_PARAM_STREAMING_MAX_OUTBOUND_SPEED] = GetI2CPOption(section, I2CP_PARAM_STREAMING_MAX_OUTBOUND_SPEED, DEFAULT_MAX_OUTBOUND_SPEED); 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_INBOUND_SPEED] = GetI2CPOption(section, I2CP_PARAM_STREAMING_MAX_INBOUND_SPEED, DEFAULT_MAX_INBOUND_SPEED);
options[I2CP_PARAM_STREAMING_ANSWER_PINGS] = GetI2CPOption(section, I2CP_PARAM_STREAMING_ANSWER_PINGS, isServer ? DEFAULT_ANSWER_PINGS : false); 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_LEASESET_TYPE] = GetI2CPOption(section, I2CP_PARAM_LEASESET_TYPE, DEFAULT_LEASESET_TYPE); options[I2CP_PARAM_LEASESET_TYPE] = GetI2CPOption(section, I2CP_PARAM_LEASESET_TYPE, DEFAULT_LEASESET_TYPE);
std::string encType = GetI2CPStringOption(section, I2CP_PARAM_LEASESET_ENCRYPTION_TYPE, "0,4"); 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; if (encType.length () > 0) options[I2CP_PARAM_LEASESET_ENCRYPTION_TYPE] = encType;
std::string privKey = GetI2CPStringOption(section, I2CP_PARAM_LEASESET_PRIV_KEY, ""); std::string privKey = GetI2CPStringOption(section, I2CP_PARAM_LEASESET_PRIV_KEY, "");
if (privKey.length () > 0) options[I2CP_PARAM_LEASESET_PRIV_KEY] = privKey; if (privKey.length () > 0) options[I2CP_PARAM_LEASESET_PRIV_KEY] = privKey;
@ -519,6 +520,8 @@ namespace client
options[I2CP_PARAM_LEASESET_ENCRYPTION_TYPE] = value; options[I2CP_PARAM_LEASESET_ENCRYPTION_TYPE] = value;
if (i2p::config::GetOption(prefix + I2CP_PARAM_LEASESET_PRIV_KEY, value) && !value.empty ()) if (i2p::config::GetOption(prefix + I2CP_PARAM_LEASESET_PRIV_KEY, value) && !value.empty ())
options[I2CP_PARAM_LEASESET_PRIV_KEY] = value; options[I2CP_PARAM_LEASESET_PRIV_KEY] = value;
if (i2p::config::GetOption(prefix + I2CP_PARAM_STREAMING_PROFILE, value))
options[I2CP_PARAM_STREAMING_PROFILE] = value;
} }
void ClientContext::ReadTunnels () void ClientContext::ReadTunnels ()
@ -663,7 +666,9 @@ namespace client
// http proxy // http proxy
std::string outproxy = section.second.get("outproxy", ""); std::string outproxy = section.second.get("outproxy", "");
bool addresshelper = section.second.get("addresshelper", true); bool addresshelper = section.second.get("addresshelper", true);
auto tun = std::make_shared<i2p::proxy::HTTPProxy>(name, address, port, outproxy, addresshelper, localDestination); bool senduseragent = section.second.get("senduseragent", false);
auto tun = std::make_shared<i2p::proxy::HTTPProxy>(name, address, port,
outproxy, addresshelper, senduseragent, localDestination);
clientTunnel = tun; clientTunnel = tun;
clientEndpoint = tun->GetLocalEndpoint (); clientEndpoint = tun->GetLocalEndpoint ();
} }
@ -879,6 +884,7 @@ namespace client
uint16_t httpProxyPort; i2p::config::GetOption("httpproxy.port", httpProxyPort); uint16_t httpProxyPort; i2p::config::GetOption("httpproxy.port", httpProxyPort);
std::string httpOutProxyURL; i2p::config::GetOption("httpproxy.outproxy", httpOutProxyURL); std::string httpOutProxyURL; i2p::config::GetOption("httpproxy.outproxy", httpOutProxyURL);
bool httpAddresshelper; i2p::config::GetOption("httpproxy.addresshelper", httpAddresshelper); bool httpAddresshelper; i2p::config::GetOption("httpproxy.addresshelper", httpAddresshelper);
bool httpSendUserAgent; i2p::config::GetOption("httpproxy.senduseragent", httpSendUserAgent);
if (httpAddresshelper) if (httpAddresshelper)
i2p::config::GetOption("addressbook.enabled", httpAddresshelper); // addresshelper is not supported without address book i2p::config::GetOption("addressbook.enabled", httpAddresshelper); // addresshelper is not supported without address book
i2p::data::SigningKeyType sigType; i2p::config::GetOption("httpproxy.signaturetype", sigType); i2p::data::SigningKeyType sigType; i2p::config::GetOption("httpproxy.signaturetype", sigType);
@ -898,7 +904,8 @@ namespace client
} }
try try
{ {
m_HttpProxy = new i2p::proxy::HTTPProxy("HTTP Proxy", httpProxyAddr, httpProxyPort, httpOutProxyURL, httpAddresshelper, localDestination); m_HttpProxy = new i2p::proxy::HTTPProxy("HTTP Proxy", httpProxyAddr, httpProxyPort,
httpOutProxyURL, httpAddresshelper, httpSendUserAgent, localDestination);
m_HttpProxy->Start(); m_HttpProxy->Start();
} }
catch (std::exception& e) catch (std::exception& e)

46
libi2pd_client/HTTPProxy.cpp

@ -9,6 +9,7 @@
#include <cstring> #include <cstring>
#include <cassert> #include <cassert>
#include <string> #include <string>
#include <string_view>
#include <atomic> #include <atomic>
#include <memory> #include <memory>
#include <set> #include <set>
@ -59,7 +60,8 @@ namespace proxy {
"</head>\r\n" "</head>\r\n"
; ;
bool str_rmatch(std::string & str, const char *suffix) { static bool str_rmatch(std::string & str, const char *suffix)
{
auto pos = str.rfind (suffix); auto pos = str.rfind (suffix);
if (pos == std::string::npos) if (pos == std::string::npos)
return false; /* not found */ return false; /* not found */
@ -77,16 +79,16 @@ namespace proxy {
void Terminate(); void Terminate();
void AsyncSockRead(); void AsyncSockRead();
static bool ExtractAddressHelper(i2p::http::URL& url, std::string& jump, bool& confirm); static bool ExtractAddressHelper(i2p::http::URL& url, std::string& jump, bool& confirm);
static bool VerifyAddressHelper (const std::string& jump); static bool VerifyAddressHelper (std::string_view jump);
static void SanitizeHTTPRequest(i2p::http::HTTPReq& req); void SanitizeHTTPRequest(i2p::http::HTTPReq& req);
void SentHTTPFailed(const boost::system::error_code & ecode); void SentHTTPFailed(const boost::system::error_code & ecode);
void HandleStreamRequestComplete (std::shared_ptr<i2p::stream::Stream> stream); void HandleStreamRequestComplete (std::shared_ptr<i2p::stream::Stream> stream);
/* error helpers */ /* error helpers */
void GenericProxyError(const std::string& title, const std::string& description); void GenericProxyError(const std::string& title, const std::string& description);
void GenericProxyInfo(const std::string& title, const std::string& description); void GenericProxyInfo(const std::string& title, const std::string& description);
void HostNotFound(std::string& host); void HostNotFound(const std::string& host);
void SendProxyError(std::string& content); void SendProxyError(const std::string& content);
void SendRedirect(std::string& address); void SendRedirect(const std::string& address);
void ForwardToUpstreamProxy(); void ForwardToUpstreamProxy();
void HandleUpstreamHTTPProxyConnect(const boost::system::error_code & ec); void HandleUpstreamHTTPProxyConnect(const boost::system::error_code & ec);
@ -108,7 +110,7 @@ namespace proxy {
std::shared_ptr<boost::asio::ip::tcp::socket> m_proxysock; std::shared_ptr<boost::asio::ip::tcp::socket> m_proxysock;
boost::asio::ip::tcp::resolver m_proxy_resolver; boost::asio::ip::tcp::resolver m_proxy_resolver;
std::string m_OutproxyUrl, m_Response; std::string m_OutproxyUrl, m_Response;
bool m_Addresshelper; bool m_Addresshelper, m_SendUserAgent;
i2p::http::URL m_ProxyURL; i2p::http::URL m_ProxyURL;
i2p::http::URL m_RequestURL; i2p::http::URL m_RequestURL;
int m_req_len; int m_req_len;
@ -124,7 +126,8 @@ namespace proxy {
m_proxysock(std::make_shared<boost::asio::ip::tcp::socket>(parent->GetService())), m_proxysock(std::make_shared<boost::asio::ip::tcp::socket>(parent->GetService())),
m_proxy_resolver(parent->GetService()), m_proxy_resolver(parent->GetService()),
m_OutproxyUrl(parent->GetOutproxyURL()), m_OutproxyUrl(parent->GetOutproxyURL()),
m_Addresshelper(parent->GetHelperSupport()) {} m_Addresshelper(parent->GetHelperSupport()),
m_SendUserAgent (parent->GetSendUserAgent ()) {}
~HTTPReqHandler() { Terminate(); } ~HTTPReqHandler() { Terminate(); }
void Handle () { AsyncSockRead(); } /* overload */ void Handle () { AsyncSockRead(); } /* overload */
}; };
@ -175,7 +178,8 @@ namespace proxy {
SendProxyError(content); SendProxyError(content);
} }
void HTTPReqHandler::HostNotFound(std::string& host) { void HTTPReqHandler::HostNotFound(const std::string& host)
{
std::stringstream ss; std::stringstream ss;
ss << "<h1>" << tr("Proxy error: Host not found") << "</h1>\r\n" ss << "<h1>" << tr("Proxy error: Host not found") << "</h1>\r\n"
<< "<p>" << tr("Remote host not found in router's addressbook") << "</p>\r\n" << "<p>" << tr("Remote host not found in router's addressbook") << "</p>\r\n"
@ -192,7 +196,7 @@ namespace proxy {
SendProxyError(content); SendProxyError(content);
} }
void HTTPReqHandler::SendProxyError(std::string& content) void HTTPReqHandler::SendProxyError(const std::string& content)
{ {
i2p::http::HTTPRes res; i2p::http::HTTPRes res;
res.code = 500; res.code = 500;
@ -208,7 +212,7 @@ namespace proxy {
std::bind(&HTTPReqHandler::SentHTTPFailed, shared_from_this(), std::placeholders::_1)); std::bind(&HTTPReqHandler::SentHTTPFailed, shared_from_this(), std::placeholders::_1));
} }
void HTTPReqHandler::SendRedirect(std::string& address) void HTTPReqHandler::SendRedirect(const std::string& address)
{ {
i2p::http::HTTPRes res; i2p::http::HTTPRes res;
res.code = 302; res.code = 302;
@ -272,7 +276,7 @@ namespace proxy {
return true; return true;
} }
bool HTTPReqHandler::VerifyAddressHelper (const std::string& jump) bool HTTPReqHandler::VerifyAddressHelper (std::string_view jump)
{ {
auto pos = jump.find(".b32.i2p"); auto pos = jump.find(".b32.i2p");
if (pos != std::string::npos) if (pos != std::string::npos)
@ -312,7 +316,8 @@ namespace proxy {
req.RemoveHeader("X-Forwarded"); req.RemoveHeader("X-Forwarded");
req.RemoveHeader("Proxy-"); // Proxy-* req.RemoveHeader("Proxy-"); // Proxy-*
/* replace headers */ /* replace headers */
req.UpdateHeader("User-Agent", "MYOB/6.66 (AN/ON)"); if (!m_SendUserAgent)
req.UpdateHeader("User-Agent", "MYOB/6.66 (AN/ON)");
/** /**
* i2pd PR #1816: * i2pd PR #1816:
@ -373,7 +378,7 @@ namespace proxy {
std::string jump; std::string jump;
if (ExtractAddressHelper(m_RequestURL, jump, m_Confirm)) if (ExtractAddressHelper(m_RequestURL, jump, m_Confirm))
{ {
if (!m_Addresshelper) if (!m_Addresshelper || !i2p::client::context.GetAddressBook ().IsEnabled ())
{ {
LogPrint(eLogWarning, "HTTPProxy: Addresshelper request rejected"); LogPrint(eLogWarning, "HTTPProxy: Addresshelper request rejected");
GenericProxyError(tr("Invalid request"), tr("Addresshelper is not supported")); GenericProxyError(tr("Invalid request"), tr("Addresshelper is not supported"));
@ -441,7 +446,7 @@ namespace proxy {
bool useConnect = false; bool useConnect = false;
if(m_ClientRequest.method == "CONNECT") if(m_ClientRequest.method == "CONNECT")
{ {
std::string uri(m_ClientRequest.uri); const std::string& uri = m_ClientRequest.uri;
auto pos = uri.find(":"); auto pos = uri.find(":");
if(pos == std::string::npos || pos == uri.size() - 1) if(pos == std::string::npos || pos == uri.size() - 1)
{ {
@ -548,9 +553,9 @@ namespace proxy {
std::string origURI = m_ClientRequest.uri; // TODO: what do we need to change uri for? std::string origURI = m_ClientRequest.uri; // TODO: what do we need to change uri for?
m_ClientRequest.uri = m_ClientRequestURL.to_string(); m_ClientRequest.uri = m_ClientRequestURL.to_string();
/* update User-Agent to ESR version of Firefox, same as Tor Browser below version 8, for non-HTTPS connections */ /* update User-Agent to ESR version of Firefox, same as Tor Browser below version 13, for non-HTTPS connections */
if(m_ClientRequest.method != "CONNECT") if(m_ClientRequest.method != "CONNECT" && !m_SendUserAgent)
m_ClientRequest.UpdateHeader("User-Agent", "Mozilla/5.0 (Windows NT 6.1; rv:60.0) Gecko/20100101 Firefox/60.0"); m_ClientRequest.UpdateHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; rv:109.0) Gecko/20100101 Firefox/115.0");
m_ClientRequest.write(m_ClientRequestBuffer); m_ClientRequest.write(m_ClientRequestBuffer);
m_ClientRequestBuffer << m_recv_buf.substr(m_req_len); m_ClientRequestBuffer << m_recv_buf.substr(m_req_len);
@ -748,9 +753,10 @@ namespace proxy {
Done (shared_from_this()); Done (shared_from_this());
} }
HTTPProxy::HTTPProxy(const std::string& name, const std::string& address, uint16_t port, const std::string & outproxy, bool addresshelper, std::shared_ptr<i2p::client::ClientDestination> localDestination): HTTPProxy::HTTPProxy(const std::string& name, const std::string& address, uint16_t port,
const std::string & outproxy, bool addresshelper, bool senduseragent, std::shared_ptr<i2p::client::ClientDestination> localDestination):
TCPIPAcceptor (address, port, localDestination ? localDestination : i2p::client::context.GetSharedLocalDestination ()), TCPIPAcceptor (address, port, localDestination ? localDestination : i2p::client::context.GetSharedLocalDestination ()),
m_Name (name), m_OutproxyUrl (outproxy), m_Addresshelper (addresshelper) m_Name (name), m_OutproxyUrl (outproxy), m_Addresshelper (addresshelper), m_SendUserAgent (senduseragent)
{ {
} }

12
libi2pd_client/HTTPProxy.h

@ -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 * This file is part of Purple i2pd project and licensed under BSD3
* *
@ -15,13 +15,15 @@ namespace proxy {
{ {
public: public:
HTTPProxy(const std::string& name, const std::string& address, uint16_t port, const std::string & outproxy, bool addresshelper, std::shared_ptr<i2p::client::ClientDestination> localDestination); HTTPProxy(const std::string& name, const std::string& address, uint16_t port, const std::string & outproxy,
bool addresshelper, bool senduseragent, std::shared_ptr<i2p::client::ClientDestination> localDestination);
HTTPProxy(const std::string& name, const std::string& address, uint16_t port, std::shared_ptr<i2p::client::ClientDestination> localDestination = nullptr) : HTTPProxy(const std::string& name, const std::string& address, uint16_t port, std::shared_ptr<i2p::client::ClientDestination> localDestination = nullptr) :
HTTPProxy(name, address, port, "", true, localDestination) {} ; HTTPProxy(name, address, port, "", true, false, localDestination) {} ;
~HTTPProxy() {}; ~HTTPProxy() {};
std::string GetOutproxyURL() const { return m_OutproxyUrl; } std::string GetOutproxyURL() const { return m_OutproxyUrl; }
bool GetHelperSupport() { return m_Addresshelper; } bool GetHelperSupport() const { return m_Addresshelper; }
bool GetSendUserAgent () const { return m_SendUserAgent; }
protected: protected:
@ -33,7 +35,7 @@ namespace proxy {
std::string m_Name; std::string m_Name;
std::string m_OutproxyUrl; std::string m_OutproxyUrl;
bool m_Addresshelper; bool m_Addresshelper, m_SendUserAgent;
}; };
} // http } // http
} // i2p } // i2p

3
libi2pd_client/I2CP.cpp

@ -226,7 +226,8 @@ namespace client
leases = remote->GetNonExpiredLeases (true); // with threshold leases = remote->GetNonExpiredLeases (true); // with threshold
if (!leases.empty ()) if (!leases.empty ())
{ {
remoteLease = leases[rand () % leases.size ()]; auto pool = GetTunnelPool ();
remoteLease = leases[(pool ? pool->GetRng ()() : rand ()) % leases.size ()];
auto leaseRouter = i2p::data::netdb.FindRouter (remoteLease->tunnelGateway); auto leaseRouter = i2p::data::netdb.FindRouter (remoteLease->tunnelGateway);
outboundTunnel = GetTunnelPool ()->GetNextOutboundTunnel (nullptr, outboundTunnel = GetTunnelPool ()->GetNextOutboundTunnel (nullptr,
leaseRouter ? leaseRouter->GetCompatibleTransports (false) : (i2p::data::RouterInfo::CompatibleTransports)i2p::data::RouterInfo::eAllTransports); leaseRouter ? leaseRouter->GetCompatibleTransports (false) : (i2p::data::RouterInfo::CompatibleTransports)i2p::data::RouterInfo::eAllTransports);

2
libi2pd_client/SOCKS.cpp

@ -444,9 +444,7 @@ namespace proxy
break; break;
case CMD_UDP: case CMD_UDP:
if (m_socksv == SOCKS5) break; if (m_socksv == SOCKS5) break;
#if (__cplusplus >= 201703L) // C++ 17 or higher
[[fallthrough]]; [[fallthrough]];
#endif
default: default:
LogPrint(eLogError, "SOCKS: Invalid command: ", ((int)*sock_buff)); LogPrint(eLogError, "SOCKS: Invalid command: ", ((int)*sock_buff));
SocksRequestFailed(SOCKS5_GEN_FAIL); SocksRequestFailed(SOCKS5_GEN_FAIL);

4
tests/Makefile

@ -1,6 +1,6 @@
SYS := $(shell $(CXX) -dumpmachine) SYS := $(shell $(CXX) -dumpmachine)
CXXFLAGS += -Wall -Wno-unused-parameter -Wextra -pedantic -O0 -g -std=c++11 -D_GLIBCXX_USE_NANOSLEEP=1 -DOPENSSL_SUPPRESS_DEPRECATED -pthread -Wl,--unresolved-symbols=ignore-in-object-files CXXFLAGS += -Wall -Wno-unused-parameter -Wextra -pedantic -O0 -g -std=c++17 -D_GLIBCXX_USE_NANOSLEEP=1 -DOPENSSL_SUPPRESS_DEPRECATED -pthread -Wl,--unresolved-symbols=ignore-in-object-files
INCFLAGS += -I../libi2pd INCFLAGS += -I../libi2pd
LIBI2PD = ../libi2pd.a LIBI2PD = ../libi2pd.a
@ -18,7 +18,7 @@ ifneq (, $(findstring mingw, $(SYS))$(findstring windows-gnu, $(SYS))$(findstrin
endif endif
LDLIBS = \ LDLIBS = \
-lboost_filesystem$(BOOST_SUFFIX) \ -lboost_system$(BOOST_SUFFIX) \
-lboost_program_options$(BOOST_SUFFIX) \ -lboost_program_options$(BOOST_SUFFIX) \
-lssl \ -lssl \
-lcrypto \ -lcrypto \

Loading…
Cancel
Save