Browse Source

Merge pull request #2105 from PurpleI2P/openssl

2.54.0
master
orignal 1 month 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: @@ -128,8 +128,11 @@ jobs:
cache: true
update: true
- name: Clone MinGW packages repository
run: git clone https://github.com/msys2/MINGW-packages
- name: Clone MinGW packages repository and revert boost to 1.85.0
run: |
git clone https://github.com/msys2/MINGW-packages
cd MINGW-packages
git checkout 4cbb366edf2f268ac3146174b40ce38604646fc5 mingw-w64-boost
# headers
- name: Get headers package version
@ -205,21 +208,23 @@ jobs: @@ -205,21 +208,23 @@ jobs:
run: |
cd MINGW-packages/mingw-w64-openssl
gpg --recv-keys D894E2CE8B3D79F5
gpg --recv-keys 216094DFD0CB81EF
MINGW_ARCH=mingw32 makepkg-mingw -sCLf --noconfirm --nocheck
- name: Install openssl package
run: pacman --noconfirm -U MINGW-packages/mingw-w64-openssl/mingw-w64-i686-*-any.pkg.tar.zst
# Boost
- name: Get boost package version
id: version-boost
run: |
echo "version=$(pacman -Si mingw-w64-i686-boost | grep -Po '^Version\s*: \K.+')" >> $GITHUB_OUTPUT
#- name: Get boost package version
# id: version-boost
# run: |
# echo "version=$(pacman -Si mingw-w64-i686-boost | grep -Po '^Version\s*: \K.+')" >> $GITHUB_OUTPUT
- name: Cache boost package
uses: actions/cache@v4
id: cache-boost
with:
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
if: steps.cache-boost.outputs.cache-hit != 'true'
run: |

38
ChangeLog

@ -1,6 +1,44 @@ @@ -1,6 +1,44 @@
# for this file format description,
# 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
### Changed
- I2CP performance improvement

20
Makefile.bsd

@ -1,18 +1,22 @@ @@ -1,18 +1,22 @@
CXX = clang++
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
## **without** overwriting the CXXFLAGS which we need in order to 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
## -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.
CXXVER := $(shell $(CXX) -dumpversion)
ifeq (${CXXVER}, "4.2.1") # older clang always returned 4.2.1
NEEDED_CXXFLAGS = -std=c++11
else # newer versions support C++17
CXXVER := $(shell $(CXX) -dumpversion|cut -c 1-2)
ifeq (${CXXVER}, "4.") # older clang always returned 4.2.1
$(error Compiler too old)
else ifeq (${CXXVER}, ${filter ${CXXVER},16 17 18 19}) # clang 16 - 19
NEEDED_CXXFLAGS = -std=c++20
else
NEEDED_CXXFLAGS = -std=c++17
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 @@ @@ -1,8 +1,8 @@
CXX = g++
CXXFLAGS := -Wall -std=c++11
CXXFLAGS := -Wall -std=c++17
INCFLAGS = -I/system/develop/headers
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)
DEFINES += -DUSE_UPNP

2
Makefile.homebrew

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

27
Makefile.linux

@ -9,24 +9,20 @@ LDFLAGS ?= ${LD_DEBUG} @@ -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
## 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)
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
LDLIBS = -latomic
else ifeq ($(shell expr match ${CXXVER} "1[0-9]"),2) # gcc 10+
# NEEDED_CXXFLAGS += -std=c++20
else ifeq ($(shell expr match ${CXXVER} "7"),1) # gcc 7
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
$(error Compiler too old)
endif
@ -39,7 +35,6 @@ ifeq ($(USE_STATIC),yes) @@ -39,7 +35,6 @@ ifeq ($(USE_STATIC),yes)
# the shared libraries from the glibc version used for linking
LIBDIR := /usr/lib/$(SYS)
LDLIBS += $(LIBDIR)/libboost_system.a
LDLIBS += $(LIBDIR)/libboost_filesystem.a
LDLIBS += $(LIBDIR)/libboost_program_options.a
LDLIBS += $(LIBDIR)/libssl.a
LDLIBS += $(LIBDIR)/libcrypto.a
@ -49,7 +44,7 @@ ifeq ($(USE_UPNP),yes) @@ -49,7 +44,7 @@ ifeq ($(USE_UPNP),yes)
endif
LDLIBS += -lpthread -ldl
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)
LDLIBS += -lminiupnpc
endif

11
Makefile.mingw

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

2
Makefile.osx vendored

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

4
Win32/Win32App.cpp

@ -313,7 +313,7 @@ namespace win32 @@ -313,7 +313,7 @@ namespace win32
}
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);
return 0;
}
@ -355,9 +355,7 @@ namespace win32 @@ -355,9 +355,7 @@ namespace win32
}
}
}
#if (__cplusplus >= 201703L) // C++ 17 or higher
[[fallthrough]];
#endif
}
case WM_TRAYICON:
{

2
Win32/Win32NetState.cpp

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

2
Win32/Win32NetState.h

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

41
build/CMakeLists.txt

@ -156,20 +156,6 @@ else() @@ -156,20 +156,6 @@ else()
endif()
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
# 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()
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
@ -223,6 +209,10 @@ if(WITH_THREADSANITIZER) @@ -223,6 +209,10 @@ if(WITH_THREADSANITIZER)
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:
# 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.
@ -284,7 +274,7 @@ else() @@ -284,7 +274,7 @@ else()
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)
message(SEND_ERROR "Boost is not found, or your boost version was below 1.46. Please download Boost!")
endif()
@ -312,6 +302,26 @@ if(ZLIB_FOUND) @@ -312,6 +302,26 @@ if(ZLIB_FOUND)
link_directories(${ZLIB_ROOT}/lib)
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
include_directories(SYSTEM ${Boost_INCLUDE_DIRS} ${OPENSSL_INCLUDE_DIR} ${ZLIB_INCLUDE_DIR})
@ -322,6 +332,7 @@ message(STATUS "Compiler vendor : ${CMAKE_CXX_COMPILER_ID}") @@ -322,6 +332,7 @@ message(STATUS "Compiler vendor : ${CMAKE_CXX_COMPILER_ID}")
message(STATUS "Compiler version : ${CMAKE_CXX_COMPILER_VERSION}")
message(STATUS "Compiler path : ${CMAKE_CXX_COMPILER}")
message(STATUS "Architecture : ${ARCHITECTURE}")
message(STATUS "Compiler flags : ${CMAKE_CXX_FLAGS}")
message(STATUS "Install prefix: : ${CMAKE_INSTALL_PREFIX}")
message(STATUS "Options:")
message(STATUS " AESNI : ${WITH_AESNI}")

4
build/cmake_modules/CheckAtomic.cmake

@ -8,7 +8,7 @@ INCLUDE(CheckLibraryExists) @@ -8,7 +8,7 @@ INCLUDE(CheckLibraryExists)
function(check_working_cxx_atomics varname)
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("
#include <atomic>
std::atomic<int> x;
@ -25,7 +25,7 @@ endfunction(check_working_cxx_atomics) @@ -25,7 +25,7 @@ endfunction(check_working_cxx_atomics)
function(check_working_cxx_atomics64 varname)
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("
#include <atomic>
#include <cstdint>

7
contrib/rpm/i2pd-git.spec

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

7
contrib/rpm/i2pd.spec

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
Name: i2pd
Version: 2.53.1
Version: 2.54.0
Release: 1%{?dist}
Summary: I2P router written in C++
Conflicts: i2pd-git
@ -22,7 +22,7 @@ BuildRequires: openssl-devel @@ -22,7 +22,7 @@ BuildRequires: openssl-devel
BuildRequires: miniupnpc-devel
BuildRequires: systemd-units
%if 0%{?fedora} > 40 || 0%{?eln}
%if 0%{?fedora} == 41
BuildRequires: openssl-devel-engine
%endif
@ -146,6 +146,9 @@ getent passwd i2pd >/dev/null || \ @@ -146,6 +146,9 @@ getent passwd i2pd >/dev/null || \
%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
- update to 2.53.1

2
daemon/HTTPServer.cpp

@ -1480,7 +1480,7 @@ namespace http { @@ -1480,7 +1480,7 @@ namespace http {
reply.body = content;
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));
}

6
debian/changelog vendored

@ -1,3 +1,9 @@ @@ -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
* updated to version 2.53.1

8
libi2pd/Config.cpp

@ -117,9 +117,14 @@ namespace config { @@ -117,9 +117,14 @@ namespace config {
("httpproxy.latency.max", value<std::string>()->default_value("0"), "HTTP proxy max latency for tunnels")
("httpproxy.outproxy", value<std::string>()->default_value(""), "HTTP proxy upstream out proxy url")
("httpproxy.addresshelper", value<bool>()->default_value(true), "Enable or disable addresshelper")
("httpproxy.senduseragent", value<bool>()->default_value(false), "Pass through user's User-Agent if enabled. Disabled by deafult")
("httpproxy.i2cp.leaseSetType", value<std::string>()->default_value("3"), "Local destination's LeaseSet type")
("httpproxy.i2cp.leaseSetEncType", value<std::string>()->default_value("0,4"), "Local destination's LeaseSet encryption type")
("httpproxy.i2cp.leaseSetPrivKey", value<std::string>()->default_value(""), "LeaseSet private key")
("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");
@ -144,6 +149,9 @@ namespace config { @@ -144,6 +149,9 @@ namespace config {
("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.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");

4
libi2pd/Datagram.cpp

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

22
libi2pd/Destination.cpp

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

6
libi2pd/Destination.h

@ -90,6 +90,10 @@ namespace client @@ -90,6 +90,10 @@ namespace client
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 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;
@ -289,7 +293,7 @@ namespace client @@ -289,7 +293,7 @@ namespace client
std::shared_ptr<ClientDestination> GetSharedFromThis () {
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);
template<typename Dest>

2
libi2pd/ECIESX25519AEADRatchetSession.cpp

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

2
libi2pd/ECIESX25519AEADRatchetSession.h

@ -35,7 +35,7 @@ namespace garlic @@ -35,7 +35,7 @@ namespace garlic
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_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 size_t ECIESX25519_OPTIMAL_PAYLOAD_SIZE = 1912; // 1912 = 1956 /* to fit 2 tunnel messages */

120
libi2pd/FS.cpp

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

12
libi2pd/FS.h

@ -1,5 +1,5 @@ @@ -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
*
@ -15,6 +15,16 @@ @@ -15,6 +15,16 @@
#include <sstream>
#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 fs {
extern std::string dirSep;

37
libi2pd/Garlic.cpp

@ -541,34 +541,7 @@ namespace garlic @@ -541,34 +541,7 @@ namespace garlic
// otherwise ECIESx25519
auto session = std::make_shared<ECIESX25519AEADRatchetSession> (this, false); // incoming
if (!session->HandleNextMessage (buf, length, nullptr, 0))
{
// 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");
}
LogPrint (eLogError, "Garlic: Can't handle ECIES-X25519-AEAD-Ratchet message");
}
else
LogPrint (eLogError, "Garlic: Failed to decrypt message");
@ -583,9 +556,7 @@ namespace garlic @@ -583,9 +556,7 @@ namespace garlic
auto it = m_ECIESx25519Tags.find (tag);
if (it != m_ECIESx25519Tags.end ())
{
if (it->second.tagset && it->second.tagset->HandleNextMessage (buf, len, it->second.index))
m_LastTagset = it->second.tagset;
else
if (!it->second.tagset || !it->second.tagset->HandleNextMessage (buf, len, it->second.index))
LogPrint (eLogError, "Garlic: Can't handle ECIES-X25519-AEAD-Ratchet message");
m_ECIESx25519Tags.erase (it);
return true;
@ -893,8 +864,6 @@ namespace garlic @@ -893,8 +864,6 @@ namespace garlic
}
if (numExpiredTags > 0)
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)
@ -1031,9 +1000,7 @@ namespace garlic @@ -1031,9 +1000,7 @@ namespace garlic
case eGarlicDeliveryTypeDestination:
LogPrint (eLogDebug, "Garlic: Type destination");
buf += 32; // TODO: check destination
#if (__cplusplus >= 201703L) // C++ 17 or higher
[[fallthrough]];
#endif
// no break here
case eGarlicDeliveryTypeLocal:
{

1
libi2pd/Garlic.h

@ -288,7 +288,6 @@ namespace garlic @@ -288,7 +288,6 @@ namespace garlic
int m_NumRatchetInboundTags;
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
ReceiveRatchetTagSetPtr m_LastTagset; // tagset last message came for
// DeliveryStatus
std::mutex m_DeliveryStatusSessionsMutex;
std::unordered_map<uint32_t, GarlicRoutingSessionPtr> m_DeliveryStatusSessions; // msgID -> session

95
libi2pd/HTTP.cpp

@ -1,5 +1,5 @@ @@ -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
*
@ -40,12 +40,13 @@ namespace http @@ -40,12 +40,13 @@ namespace http
inline bool is_http_method(const std::string & str) {
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::stringstream ss(line);
std::string token;
while (1) {
while (1)
{
count++;
if (limit > 0 && count >= limit)
delim = '\n'; /* reset delimiter */
@ -55,21 +56,33 @@ namespace http @@ -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 len = 1; /*: */
std::size_t max = line.length();
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
{
while ((pos + len) < max && isspace(line.at(pos + len)))
len++;
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) {
@ -83,15 +96,17 @@ namespace http @@ -83,15 +96,17 @@ namespace http
out = buf;
}
bool URL::parse(const char *str, std::size_t len) {
std::string url(str, len ? len : strlen(str));
return parse(url);
bool URL::parse(const char *str, std::size_t len)
{
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_c = 0; /* < work position */
if(url.at(0) != '/' || pos_p > 0) {
if(url.at(0) != '/' || pos_p > 0)
{
std::size_t pos_s = 0;
/* schema */
@ -141,7 +156,7 @@ namespace http @@ -141,7 +156,7 @@ namespace http
/* port[/path] */
pos_p = pos_c + 1;
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, pos_c - pos_p);
/* stoi throws exception on failure, we don't need it */
@ -253,7 +268,7 @@ namespace http @@ -253,7 +268,7 @@ namespace http
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);
}
@ -272,12 +287,13 @@ namespace http @@ -272,12 +287,13 @@ namespace http
headers.erase(name);
}
int HTTPReq::parse(const char *buf, size_t len) {
std::string str(buf, len);
return parse(str);
int HTTPReq::parse(const char *buf, size_t len)
{
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;
std::size_t eoh = str.find(HTTP_EOH); /* request head size */
std::size_t eol = 0, pos = 0;
@ -286,9 +302,11 @@ namespace http @@ -286,9 +302,11 @@ namespace http
if (eoh == std::string::npos)
return 0; /* str not contains complete request */
while ((eol = str.find(CRLF, pos)) != std::string::npos) {
if (expect == REQ_LINE) {
std::string line = str.substr(pos, eol - pos);
while ((eol = str.find(CRLF, pos)) != std::string::npos)
{
if (expect == REQ_LINE)
{
std::string_view line = str.substr(pos, eol - pos);
std::vector<std::string> tokens;
strsplit(line, tokens, ' ');
if (tokens.size() != 3)
@ -307,7 +325,7 @@ namespace http @@ -307,7 +325,7 @@ namespace http
}
else
{
std::string line = str.substr(pos, eol - pos);
std::string_view line = str.substr(pos, eol - pos);
auto p = parse_header_line(line);
if (p.first.length () > 0)
headers.push_back (p);
@ -413,12 +431,13 @@ namespace http @@ -413,12 +431,13 @@ namespace http
return length;
}
int HTTPRes::parse(const char *buf, size_t len) {
std::string str(buf, len);
return parse(str);
int HTTPRes::parse(const char *buf, size_t len)
{
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;
std::size_t eoh = str.find(HTTP_EOH); /* request head size */
std::size_t eol = 0, pos = 0;
@ -426,9 +445,11 @@ namespace http @@ -426,9 +445,11 @@ namespace http
if (eoh == std::string::npos)
return 0; /* str not contains complete request */
while ((eol = str.find(CRLF, pos)) != std::string::npos) {
if (expect == RES_LINE) {
std::string line = str.substr(pos, eol - pos);
while ((eol = str.find(CRLF, pos)) != std::string::npos)
{
if (expect == RES_LINE)
{
std::string_view line = str.substr(pos, eol - pos);
std::vector<std::string> tokens;
strsplit(line, tokens, ' ', 3);
if (tokens.size() != 3)
@ -442,8 +463,10 @@ namespace http @@ -442,8 +463,10 @@ namespace http
version = tokens[0];
status = tokens[2];
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);
if (p.first.length () > 0)
headers.insert (p);
@ -508,14 +531,14 @@ namespace http @@ -508,14 +531,14 @@ namespace http
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);
size_t pos = 0;
while ((pos = decoded.find('%', pos)) != std::string::npos)
{
char c = strtol(decoded.substr(pos + 1, 2).c_str(), NULL, 16);
if (c == '\0' && !allow_null)
char c = std::stol(decoded.substr(pos + 1, 2), nullptr, 16);
if (!c && !allow_null)
{
pos += 3;
continue;

13
libi2pd/HTTP.h

@ -1,5 +1,5 @@ @@ -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
*
@ -14,6 +14,7 @@ @@ -14,6 +14,7 @@
#include <list>
#include <sstream>
#include <string>
#include <string_view>
#include <vector>
namespace i2p
@ -45,7 +46,7 @@ namespace http @@ -45,7 +46,7 @@ namespace http
* @return true on success, false on invalid url
*/
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
@ -69,7 +70,7 @@ namespace http @@ -69,7 +70,7 @@ namespace http
{
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 del_header(const char *name);
@ -92,7 +93,7 @@ namespace http @@ -92,7 +93,7 @@ namespace http
* @note Positive return value is a size of header
*/
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 */
std::string to_string();
@ -128,7 +129,7 @@ namespace http @@ -128,7 +129,7 @@ namespace http
* @note Positive return value is a size of header
*/
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
@ -161,7 +162,7 @@ namespace http @@ -161,7 +162,7 @@ namespace http
* @param null If set to true - decode also %00 sequence, otherwise - skip
* @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

1
libi2pd/I2NPProtocol.h

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

20
libi2pd/Log.h

@ -172,16 +172,6 @@ void LogPrint (std::stringstream& s, TValue&& arg) noexcept @@ -172,16 +172,6 @@ void LogPrint (std::stringstream& s, TValue&& arg) noexcept
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
* @param level Message level (eLogError, eLogInfo, ...)
@ -194,13 +184,7 @@ void LogPrint (LogLevel level, TArgs&&... args) noexcept @@ -194,13 +184,7 @@ void LogPrint (LogLevel level, TArgs&&... args) noexcept
// fold message to single string
std::stringstream ss;
#if (__cplusplus >= 201703L) // C++ 17 or higher
(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());
msg->tid = std::this_thread::get_id();
i2p::log::Logger().Append(msg);
@ -217,11 +201,7 @@ void ThrowFatal (TArgs&&... args) noexcept @@ -217,11 +201,7 @@ void ThrowFatal (TArgs&&... args) noexcept
if (!f) return;
// fold message to single string
std::stringstream ss("");
#if (__cplusplus >= 201703L) // C++ 17 or higher
(LogPrint (ss, std::forward<TArgs>(args)), ...);
#else
LogPrint (ss, std::forward<TArgs>(args)...);
#endif
f (ss.str ());
}

53
libi2pd/NTCP2.cpp

@ -695,7 +695,7 @@ namespace transport @@ -695,7 +695,7 @@ namespace transport
return;
}
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");
Terminate ();
@ -724,8 +724,28 @@ namespace transport @@ -724,8 +724,28 @@ namespace transport
SendTerminationAndTerminate (eNTCP2Message3Error);
return;
}
auto addr = m_RemoteEndpoint.address ().is_v4 () ? ri.GetNTCP2V4Address () :
(i2p::util::net::IsYggdrasilAddress (m_RemoteEndpoint.address ()) ? ri.GetYggdrasilAddress () : ri.GetNTCP2V6Address ());
// update RouterInfo in netdb
auto ri1 = i2p::data::netdb.AddRouterInfo (ri.GetBuffer (), ri.GetBufferLen ()); // ri1 points to one from netdb now
if (!ri1)
{
LogPrint (eLogError, "NTCP2: Couldn't update RouterInfo from SessionConfirmed in netdb");
Terminate ();
return;
}
std::shared_ptr<i2p::data::RouterProfile> profile; // not null if older
if (ri.GetTimestamp () + i2p::data::NETDB_EXPIRATION_TIMEOUT_THRESHOLD*1000LL < ri1->GetTimestamp ())
{
// received RouterInfo is older than one in netdb
profile = i2p::data::GetRouterProfile (ri1->GetIdentHash ()); // retrieve profile
if (profile && profile->IsDuplicated ())
{
SendTerminationAndTerminate (eNTCP2Banned);
return;
}
}
auto addr = m_RemoteEndpoint.address ().is_v4 () ? ri1->GetNTCP2V4Address () :
(i2p::util::net::IsYggdrasilAddress (m_RemoteEndpoint.address ()) ? ri1->GetYggdrasilAddress () : ri1->GetNTCP2V6Address ());
if (!addr || memcmp (m_Establisher->m_RemoteStaticKey, addr->s, 32))
{
LogPrint (eLogError, "NTCP2: Wrong static key in SessionConfirmed");
@ -737,16 +757,17 @@ namespace transport @@ -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 (), 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 ());
Terminate ();
if (profile) // older router?
profile->Duplicated (); // mark router as duplicated in profile
else
LogPrint (eLogInfo, "NTCP2: Host mismatch between published address ", addr->host, " and actual endpoint ", m_RemoteEndpoint.address ());
SendTerminationAndTerminate (eNTCP2Banned);
return;
}
i2p::data::netdb.PostI2NPMsg (CreateI2NPMessage (eI2NPDummyMsg, buf.data () + 3, size)); // TODO: should insert ri and not parse it twice
// TODO: process options
// ready to communicate
auto existing = i2p::data::netdb.FindRouter (ri.GetRouterIdentity ()->GetIdentHash ()); // check if exists already
SetRemoteIdentity (existing ? existing->GetRouterIdentity () : ri.GetRouterIdentity ());
SetRemoteIdentity (ri1->GetRouterIdentity ());
if (m_Server.AddNTCP2Session (shared_from_this (), true))
{
Established ();
@ -938,8 +959,20 @@ namespace transport @@ -938,8 +959,20 @@ namespace transport
break;
case eNTCP2BlkRouterInfo:
{
LogPrint (eLogDebug, "NTCP2: RouterInfo flag=", (int)frame[offset]);
i2p::data::netdb.PostI2NPMsg (CreateI2NPMessage (eI2NPDummyMsg, frame + offset, size));
LogPrint (eLogDebug, "NTCP2: RouterInfo flag=", (int)frame[offset]);
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;
}
case eNTCP2BlkI2NPMessage:

38
libi2pd/NetDb.cpp

@ -69,7 +69,7 @@ namespace data @@ -69,7 +69,7 @@ namespace data
{
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
auto it = m_RouterInfos.find (i2p::context.GetIdentHash ());
@ -141,10 +141,6 @@ namespace data @@ -141,10 +141,6 @@ namespace data
case eI2NPDatabaseLookup:
HandleDatabaseLookupMsg (msg);
break;
case eI2NPDummyMsg:
// plain RouterInfo from NTCP2 with flags for now
HandleNTCP2RouterInfoMsg (msg);
break;
default: // WTF?
LogPrint (eLogError, "NetDb: Unexpected message type ", (int) msg->GetTypeID ());
//i2p::HandleI2NPMessage (msg);
@ -299,7 +295,8 @@ namespace data @@ -299,7 +295,8 @@ namespace data
{
auto mts = i2p::util::GetMillisecondsSinceEpoch ();
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)
{
@ -763,7 +760,8 @@ namespace data @@ -763,7 +760,8 @@ namespace data
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)
m_Requests->PostRequestDestination (destination, requestComplete, direct);
else
@ -1133,15 +1131,18 @@ namespace data @@ -1133,15 +1131,18 @@ namespace data
}
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 (
[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 &&
(reverse ? (compatibleWith->IsReachableFrom (*router) && router->GetCompatibleTransports (true)):
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)
});
}
@ -1314,12 +1315,8 @@ namespace data @@ -1314,12 +1315,8 @@ namespace data
{
// update selection
m_ExploratorySelection.clear ();
#if (__cplusplus >= 201703L) // C++ 17 or higher
std::vector<std::shared_ptr<const RouterInfo> > eligible;
eligible.reserve (m_RouterInfos.size ());
#else
auto& eligible = m_ExploratorySelection;
#endif
eligible.reserve (m_RouterInfos.size ());
{
// collect eligible from current netdb
bool checkIsReal = i2p::tunnel::tunnels.GetPreciseTunnelCreationSuccessRate () < NETDB_TUNNEL_CREATION_RATE_THRESHOLD; // too low rate
@ -1329,22 +1326,13 @@ namespace data @@ -1329,22 +1326,13 @@ namespace data
(!checkIsReal || (it.second->HasProfile () && it.second->GetProfile ()->IsReal ())))
eligible.push_back (it.second);
}
#if (__cplusplus >= 201703L) // C++ 17 or higher
if (eligible.size () > NETDB_MAX_EXPLORATORY_SELECTION_SIZE)
{
std::sample (eligible.begin(), eligible.end(), std::back_inserter(m_ExploratorySelection),
NETDB_MAX_EXPLORATORY_SELECTION_SIZE, std::mt19937(ts));
}
else
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
std::swap (m_ExploratorySelection, eligible);
m_LastExploratorySelectionUpdateTime = ts;
}

8
libi2pd/NetDb.hpp

@ -87,7 +87,7 @@ namespace data @@ -87,7 +87,7 @@ namespace data
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 (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> 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;
@ -127,12 +127,12 @@ namespace data @@ -127,12 +127,12 @@ namespace data
}
bool PopulateRouterInfoBuffer (std::shared_ptr<RouterInfo> r);
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 *)>
(&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<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 @@ -323,6 +323,10 @@ namespace i2p
case eRouterStatusFirewalled:
SetUnreachable (true, false); // ipv4
break;
case eRouterStatusProxy:
m_AcceptsTunnels = false;
UpdateCongestion ();
break;
default:
;
}
@ -553,6 +557,12 @@ namespace i2p @@ -553,6 +557,12 @@ namespace i2p
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)
{
auto addr = m_RouterInfo.GetSSU2Address (v4);
@ -610,8 +620,8 @@ namespace i2p @@ -610,8 +620,8 @@ namespace i2p
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_BANDWIDTH3 : limit = 64; type = low; break;
case i2p::data::CAPS_FLAG_HIGH_BANDWIDTH1 : limit = 128; type = high; break;
case i2p::data::CAPS_FLAG_HIGH_BANDWIDTH2 : limit = i2p::data::HIGH_BANDWIDTH_LIMIT; type = high; break; // 256
case i2p::data::CAPS_FLAG_LOW_BANDWIDTH4 : limit = 128; type = low; break;
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_BANDWIDTH2 : limit = 1000000; type = unlim; break; // 1Gbyte/s
default:
@ -626,9 +636,7 @@ namespace i2p @@ -626,9 +636,7 @@ namespace i2p
case low : /* not set */; break;
case extra : caps |= i2p::data::RouterInfo::eExtraBandwidth; break; // 'P'
case unlim : caps |= i2p::data::RouterInfo::eExtraBandwidth;
#if (__cplusplus >= 201703L) // C++ 17 or higher
[[fallthrough]];
#endif
// no break here, extra + high means 'X'
case high : caps |= i2p::data::RouterInfo::eHighBandwidth; break;
}

2
libi2pd/RouterContext.h

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

153
libi2pd/RouterInfo.cpp

@ -10,10 +10,10 @@ @@ -10,10 +10,10 @@
#include <string.h>
#include "I2PEndian.h"
#include <fstream>
#include <memory>
#include <boost/lexical_cast.hpp>
#include <boost/make_shared.hpp>
#include <boost/algorithm/string.hpp> // for boost::to_lower
#if (BOOST_VERSION >= 105300)
#ifndef __cpp_lib_atomic_shared_ptr
#include <boost/atomic.hpp>
#endif
#include "version.h"
@ -40,7 +40,7 @@ namespace data @@ -40,7 +40,7 @@ namespace data
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):
@ -48,8 +48,8 @@ namespace data @@ -48,8 +48,8 @@ namespace data
m_SupportedTransports (0),m_ReachableTransports (0), m_PublishedTransports (0),
m_Caps (0), m_Version (0), m_Congestion (eLowCongestion)
{
m_Addresses = boost::make_shared<Addresses>(); // create empty list
m_Buffer = NewBuffer (); // always RouterInfo's
m_Addresses = AddressesPtr(new Addresses ()); // create empty list
m_Buffer = RouterInfo::NewBuffer (); // always RouterInfo's
ReadFromFile (fullPath);
}
@ -60,7 +60,7 @@ namespace data @@ -60,7 +60,7 @@ namespace data
{
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;
if (m_Buffer) m_Buffer->SetBufferLen (len);
ReadFromBuffer (true);
@ -74,7 +74,7 @@ namespace data @@ -74,7 +74,7 @@ namespace data
}
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 @@ -439,10 +439,10 @@ namespace data
}
m_ReachableTransports |= m_PublishedTransports;
// update addresses
#if (BOOST_VERSION >= 105300)
#ifdef __cpp_lib_atomic_shared_ptr
m_Addresses = addresses;
#else
boost::atomic_store (&m_Addresses, addresses);
#else
m_Addresses = addresses; // race condition
#endif
// read peers
uint8_t numPeers;
@ -538,10 +538,10 @@ namespace data @@ -538,10 +538,10 @@ namespace data
case CAPS_FLAG_LOW_BANDWIDTH1:
case CAPS_FLAG_LOW_BANDWIDTH2:
case CAPS_FLAG_LOW_BANDWIDTH3:
case CAPS_FLAG_LOW_BANDWIDTH4:
m_BandwidthCap = *cap;
break;
case CAPS_FLAG_HIGH_BANDWIDTH1:
case CAPS_FLAG_HIGH_BANDWIDTH2:
case CAPS_FLAG_HIGH_BANDWIDTH:
m_Caps |= Caps::eHighBandwidth;
m_BandwidthCap = *cap;
break;
@ -692,12 +692,12 @@ namespace data @@ -692,12 +692,12 @@ namespace data
if (addr->IsV4 ())
{
m_SupportedTransports |= eNTCP2V4;
(*m_Addresses)[eNTCP2V4Idx] = addr;
(*GetAddresses ())[eNTCP2V4Idx] = addr;
}
if (addr->IsV6 ())
{
m_SupportedTransports |= eNTCP2V6;
(*m_Addresses)[eNTCP2V6Idx] = addr;
(*GetAddresses ())[eNTCP2V6Idx] = addr;
}
}
@ -718,11 +718,12 @@ namespace data @@ -718,11 +718,12 @@ namespace data
if (host.is_v4 ()) addr->caps |= eV4;
if (host.is_v6 ()) addr->caps |= eV6;
}
auto addresses = GetAddresses ();
if (addr->IsV4 ())
{
m_SupportedTransports |= eNTCP2V4;
m_ReachableTransports |= eNTCP2V4;
(*m_Addresses)[eNTCP2V4Idx] = addr;
(*addresses)[eNTCP2V4Idx] = addr;
}
if (addr->IsV6 ())
{
@ -730,30 +731,31 @@ namespace data @@ -730,30 +731,31 @@ namespace data
{
m_SupportedTransports |= eNTCP2V6Mesh;
m_ReachableTransports |= eNTCP2V6Mesh;
(*m_Addresses)[eNTCP2V6MeshIdx] = addr;
(*addresses)[eNTCP2V6MeshIdx] = addr;
}
else
{
m_SupportedTransports |= eNTCP2V6;
m_ReachableTransports |= eNTCP2V6;
(*m_Addresses)[eNTCP2V6Idx] = addr;
(*addresses)[eNTCP2V6Idx] = addr;
}
}
}
void RouterInfo::RemoveNTCP2Address (bool v4)
{
auto addresses = GetAddresses ();
if (v4)
{
if ((*m_Addresses)[eNTCP2V6Idx])
(*m_Addresses)[eNTCP2V6Idx]->caps &= ~AddressCaps::eV4;
(*m_Addresses)[eNTCP2V4Idx].reset ();
if ((*addresses)[eNTCP2V6Idx])
(*addresses)[eNTCP2V6Idx]->caps &= ~AddressCaps::eV4;
(*addresses)[eNTCP2V4Idx].reset ();
}
else
{
if ((*m_Addresses)[eNTCP2V4Idx])
(*m_Addresses)[eNTCP2V4Idx]->caps &= ~AddressCaps::eV6;
(*m_Addresses)[eNTCP2V6Idx].reset ();
if ((*addresses)[eNTCP2V4Idx])
(*addresses)[eNTCP2V4Idx]->caps &= ~AddressCaps::eV6;
(*addresses)[eNTCP2V6Idx].reset ();
}
UpdateSupportedTransports ();
}
@ -769,15 +771,16 @@ namespace data @@ -769,15 +771,16 @@ namespace data
addr->ssu->mtu = 0;
memcpy (addr->s, staticKey, 32);
memcpy (addr->i, introKey, 32);
auto addresses = GetAddresses ();
if (addr->IsV4 ())
{
m_SupportedTransports |= eSSU2V4;
(*m_Addresses)[eSSU2V4Idx] = addr;
(*addresses)[eSSU2V4Idx] = addr;
}
if (addr->IsV6 ())
{
m_SupportedTransports |= eSSU2V6;
(*m_Addresses)[eSSU2V6Idx] = addr;
(*addresses)[eSSU2V6Idx] = addr;
}
}
@ -802,33 +805,35 @@ namespace data @@ -802,33 +805,35 @@ namespace data
if (host.is_v4 ()) addr->caps |= eV4;
if (host.is_v6 ()) addr->caps |= eV6;
}
auto addresses = GetAddresses ();
if (addr->IsV4 ())
{
m_SupportedTransports |= eSSU2V4;
m_ReachableTransports |= eSSU2V4;
(*m_Addresses)[eSSU2V4Idx] = addr;
(*addresses)[eSSU2V4Idx] = addr;
}
if (addr->IsV6 ())
{
m_SupportedTransports |= eSSU2V6;
m_ReachableTransports |= eSSU2V6;
(*m_Addresses)[eSSU2V6Idx] = addr;
(*addresses)[eSSU2V6Idx] = addr;
}
}
void RouterInfo::RemoveSSU2Address (bool v4)
{
auto addresses = GetAddresses ();
if (v4)
{
if ((*m_Addresses)[eSSU2V6Idx])
(*m_Addresses)[eSSU2V6Idx]->caps &= ~AddressCaps::eV4;
(*m_Addresses)[eSSU2V4Idx].reset ();
if ((*addresses)[eSSU2V6Idx])
(*addresses)[eSSU2V6Idx]->caps &= ~AddressCaps::eV4;
(*addresses)[eSSU2V4Idx].reset ();
}
else
{
if ((*m_Addresses)[eSSU2V4Idx])
(*m_Addresses)[eSSU2V4Idx]->caps &= ~AddressCaps::eV6;
(*m_Addresses)[eSSU2V6Idx].reset ();
if ((*addresses)[eSSU2V4Idx])
(*addresses)[eSSU2V4Idx]->caps &= ~AddressCaps::eV6;
(*addresses)[eSSU2V6Idx].reset ();
}
UpdateSupportedTransports ();
}
@ -869,17 +874,18 @@ namespace data @@ -869,17 +874,18 @@ namespace data
{
if (IsV6 ())
{
if ((*m_Addresses)[eNTCP2V6Idx])
auto addresses = GetAddresses ();
if ((*addresses)[eNTCP2V6Idx])
{
if ((*m_Addresses)[eNTCP2V6Idx]->IsV4 () && (*m_Addresses)[eNTCP2V4Idx])
(*m_Addresses)[eNTCP2V4Idx]->caps &= ~AddressCaps::eV6;
(*m_Addresses)[eNTCP2V6Idx].reset ();
if ((*addresses)[eNTCP2V6Idx]->IsV4 () && (*addresses)[eNTCP2V4Idx])
(*addresses)[eNTCP2V4Idx]->caps &= ~AddressCaps::eV6;
(*addresses)[eNTCP2V6Idx].reset ();
}
if ((*m_Addresses)[eSSU2V6Idx])
if ((*addresses)[eSSU2V6Idx])
{
if ((*m_Addresses)[eSSU2V6Idx]->IsV4 () && (*m_Addresses)[eSSU2V4Idx])
(*m_Addresses)[eSSU2V4Idx]->caps &= ~AddressCaps::eV6;
(*m_Addresses)[eSSU2V6Idx].reset ();
if ((*addresses)[eSSU2V6Idx]->IsV4 () && (*addresses)[eSSU2V4Idx])
(*addresses)[eSSU2V4Idx]->caps &= ~AddressCaps::eV6;
(*addresses)[eSSU2V6Idx].reset ();
}
UpdateSupportedTransports ();
}
@ -889,17 +895,18 @@ namespace data @@ -889,17 +895,18 @@ namespace data
{
if (IsV4 ())
{
if ((*m_Addresses)[eNTCP2V4Idx])
auto addresses = GetAddresses ();
if ((*addresses)[eNTCP2V4Idx])
{
if ((*m_Addresses)[eNTCP2V4Idx]->IsV6 () && (*m_Addresses)[eNTCP2V6Idx])
(*m_Addresses)[eNTCP2V6Idx]->caps &= ~AddressCaps::eV4;
(*m_Addresses)[eNTCP2V4Idx].reset ();
if ((*addresses)[eNTCP2V4Idx]->IsV6 () && (*addresses)[eNTCP2V6Idx])
(*addresses)[eNTCP2V6Idx]->caps &= ~AddressCaps::eV4;
(*addresses)[eNTCP2V4Idx].reset ();
}
if ((*m_Addresses)[eSSU2V4Idx])
if ((*addresses)[eSSU2V4Idx])
{
if ((*m_Addresses)[eSSU2V4Idx]->IsV6 () && (*m_Addresses)[eSSU2V6Idx])
(*m_Addresses)[eSSU2V6Idx]->caps &= ~AddressCaps::eV4;
(*m_Addresses)[eSSU2V4Idx].reset ();
if ((*addresses)[eSSU2V4Idx]->IsV6 () && (*addresses)[eSSU2V6Idx])
(*addresses)[eSSU2V6Idx]->caps &= ~AddressCaps::eV4;
(*addresses)[eSSU2V4Idx].reset ();
}
UpdateSupportedTransports ();
}
@ -920,7 +927,7 @@ namespace data @@ -920,7 +927,7 @@ namespace data
{
m_SupportedTransports &= ~eNTCP2V6Mesh;
m_ReachableTransports &= ~eNTCP2V6Mesh;
(*m_Addresses)[eNTCP2V6MeshIdx].reset ();
(*GetAddresses ())[eNTCP2V6MeshIdx].reset ();
}
}
@ -949,12 +956,12 @@ namespace data @@ -949,12 +956,12 @@ namespace data
return nullptr;
}
boost::shared_ptr<RouterInfo::Addresses> RouterInfo::GetAddresses () const
RouterInfo::AddressesPtr RouterInfo::GetAddresses () const
{
#if (BOOST_VERSION >= 105300)
return boost::atomic_load (&m_Addresses);
#else
#ifdef __cpp_lib_atomic_shared_ptr
return m_Addresses;
#else
return boost::atomic_load (&m_Addresses);
#endif
}
@ -962,10 +969,10 @@ namespace data @@ -962,10 +969,10 @@ namespace data
std::shared_ptr<const RouterInfo::Address> RouterInfo::GetAddress (Filter filter) const
{
// 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);
#else
auto addresses = m_Addresses;
#endif
for (const auto& address : *addresses)
if (address && filter (address)) return address;
@ -1062,7 +1069,7 @@ namespace data @@ -1062,7 +1069,7 @@ namespace data
void RouterInfo::SetUnreachableAddressesTransportCaps (uint8_t transports)
{
for (auto& addr: *m_Addresses)
for (auto& addr: *GetAddresses ())
{
if (addr && !addr->published)
{
@ -1076,7 +1083,7 @@ namespace data @@ -1076,7 +1083,7 @@ namespace data
{
m_SupportedTransports = 0;
m_ReachableTransports = 0;
for (const auto& addr: *m_Addresses)
for (const auto& addr: *GetAddresses ())
{
if (!addr) continue;
uint8_t transports = 0;
@ -1151,7 +1158,7 @@ namespace data @@ -1151,7 +1158,7 @@ namespace data
return netdb.NewRouterInfoAddress ();
}
boost::shared_ptr<RouterInfo::Addresses> RouterInfo::NewAddresses () const
RouterInfo::AddressesPtr RouterInfo::NewAddresses () const
{
return netdb.NewRouterInfoAddresses ();
}
@ -1224,7 +1231,7 @@ namespace data @@ -1224,7 +1231,7 @@ namespace data
CAPS_FLAG_EXTRA_BANDWIDTH2 : // 'X'
CAPS_FLAG_EXTRA_BANDWIDTH1; // 'P'
else
caps += CAPS_FLAG_HIGH_BANDWIDTH2; // 'O'
caps += CAPS_FLAG_HIGH_BANDWIDTH; // 'O'
caps += CAPS_FLAG_FLOODFILL; // floodfill
}
else
@ -1232,7 +1239,7 @@ namespace data @@ -1232,7 +1239,7 @@ namespace data
if (c & eExtraBandwidth)
caps += (c & eHighBandwidth) ? CAPS_FLAG_EXTRA_BANDWIDTH2 /* 'X' */ : CAPS_FLAG_EXTRA_BANDWIDTH1; /*'P' */
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 & eReachable) caps += CAPS_FLAG_REACHABLE; // reachable
@ -1503,9 +1510,9 @@ namespace data @@ -1503,9 +1510,9 @@ namespace data
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
@ -1547,5 +1554,23 @@ namespace data @@ -1547,5 +1554,23 @@ namespace data
}
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 @@ @@ -15,8 +15,11 @@
#include <vector>
#include <array>
#include <iostream>
#include <memory>
#include <boost/asio.hpp>
#ifndef __cpp_lib_atomic_shared_ptr
#include <boost/shared_ptr.hpp>
#endif
#include "Identity.h"
#include "Profiling.h"
#include "Family.h"
@ -40,8 +43,8 @@ namespace data @@ -40,8 +43,8 @@ namespace data
const char CAPS_FLAG_LOW_BANDWIDTH1 = 'K'; /* < 12 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_HIGH_BANDWIDTH1 = 'N'; /* 64-128 KBps */
const char CAPS_FLAG_HIGH_BANDWIDTH2 = 'O'; /* 128-256 KBps */
const char CAPS_FLAG_LOW_BANDWIDTH4 = 'N'; /* 64-128 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_BANDWIDTH2 = 'X'; /* > 2048 KBps */
// bandwidth limits in kBps
@ -199,7 +202,11 @@ namespace data @@ -199,7 +202,11 @@ namespace data
};
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 RouterInfo& ) = default;
RouterInfo& operator=(const RouterInfo& ) = default;
@ -214,7 +221,7 @@ namespace data @@ -214,7 +221,7 @@ namespace data
int GetVersion () const { return m_Version; };
virtual void SetProperty (const std::string& key, const std::string& value) {};
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> GetNTCP2V6Address () const;
std::shared_ptr<const Address> GetPublishedNTCP2V4Address () const;
@ -333,7 +340,7 @@ namespace data @@ -333,7 +340,7 @@ namespace data
std::shared_ptr<const Address> GetAddress (Filter filter) const;
virtual std::shared_ptr<Buffer> NewBuffer () 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;
private:
@ -342,7 +349,11 @@ namespace data @@ -342,7 +349,11 @@ namespace data
std::shared_ptr<const IdentityEx> m_RouterIdentity;
std::shared_ptr<Buffer> m_Buffer;
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;
CompatibleTransports m_SupportedTransports, m_ReachableTransports, m_PublishedTransports;
uint8_t m_Caps;
@ -369,6 +380,7 @@ namespace data @@ -369,6 +380,7 @@ namespace data
bool AddSSU2Introducer (const Introducer& introducer, bool v4);
bool RemoveSSU2Introducer (const IdentHash& h, bool v4);
bool UpdateSSU2Introducer (const IdentHash& h, bool v4, uint32_t iTag, uint32_t iExp);
private:
@ -377,7 +389,7 @@ namespace data @@ -377,7 +389,7 @@ namespace data
void WriteString (const std::string& str, std::ostream& s) const;
std::shared_ptr<Buffer> NewBuffer () 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;
private:

404
libi2pd/SSU2.cpp

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

45
libi2pd/SSU2.h

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

648
libi2pd/SSU2Session.cpp

@ -18,6 +18,12 @@ namespace i2p @@ -18,6 +18,12 @@ namespace i2p
{
namespace transport
{
static inline void CreateNonce (uint64_t seqn, uint8_t * nonce)
{
memset (nonce, 0, 4);
htole64buf (nonce + 4, seqn);
}
void SSU2IncompleteMessage::AttachNextFragment (const uint8_t * fragment, size_t fragmentSize)
{
if (msg->len + fragmentSize > msg->maxLen)
@ -79,7 +85,7 @@ namespace transport @@ -79,7 +85,7 @@ namespace transport
}
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),
m_Server (server), m_Address (addr), m_RemoteTransports (0), m_RemotePeerTestTransports (0),
m_DestConnID (0), m_SourceConnID (0), m_State (eSSU2SessionStateUnknown),
@ -93,11 +99,13 @@ namespace transport @@ -93,11 +99,13 @@ namespace transport
m_MaxPayloadSize (SSU2_MIN_PACKET_SIZE - IPV6_HEADER_SIZE - UDP_HEADER_SIZE - 32), // min size
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)
{
// 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_RemoteTransports = in_RemoteRouter->GetCompatibleTransports (false);
if (in_RemoteRouter->IsSSU2PeerTesting (true)) m_RemotePeerTestTransports |= i2p::data::RouterInfo::eSSU2V4;
@ -108,7 +116,8 @@ namespace transport @@ -108,7 +116,8 @@ namespace transport
else
{
// incoming
InitNoiseXKState1 (*m_NoiseState, i2p::context.GetSSU2StaticPublicKey ());
if (noise)
InitNoiseXKState1 (*m_NoiseState, i2p::context.GetSSU2StaticPublicKey ());
}
}
@ -161,40 +170,47 @@ namespace transport @@ -161,40 +170,47 @@ namespace transport
if (!session || !relayTag) return false;
// find local address to introduce
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
uint32_t nonce;
RAND_bytes ((uint8_t *)&nonce, 4);
auto ts = i2p::util::GetSecondsSinceEpoch ();
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
// payload
uint8_t payload[SSU2_MAX_PACKET_SIZE];
size_t payloadSize = 0;
auto packet = m_Server.GetSentPacketsPool ().AcquireShared ();
uint8_t * payload = packet->payload;
payload[0] = eSSU2BlkRelayRequest;
payload[3] = 0; // flag
htobe32buf (payload + 4, nonce);
htobe32buf (payload + 8, relayTag);
htobe32buf (payload + 12, ts);
htobe32buf (payload + 12, ts/1000);
payload[16] = 2; // ver
size_t asz = CreateEndpoint (payload + 18, m_MaxPayloadSize - 18, boost::asio::ip::udp::endpoint (localAddress->host, localAddress->port));
if (!asz) return false;
payload[17] = asz;
payloadSize += asz + 18;
packet->payloadSize = asz + 18;
SignedData s;
s.Insert ((const uint8_t *)"RelayRequestData", 16); // prologue
s.Insert (GetRemoteIdentity ()->GetIdentHash (), 32); // bhash
s.Insert (session->GetRemoteIdentity ()->GetIdentHash (), 32); // chash
s.Insert (payload + 4, 14 + asz); // nonce, relay tag, timestamp, ver, asz and Alice's endpoint
s.Sign (i2p::context.GetPrivateKeys (), payload + payloadSize);
payloadSize += i2p::context.GetIdentity ()->GetSignatureLen ();
htobe16buf (payload + 1, payloadSize - 3); // size
payloadSize += CreatePaddingBlock (payload + payloadSize, m_MaxPayloadSize - payloadSize);
s.Sign (i2p::context.GetPrivateKeys (), payload + packet->payloadSize);
packet->payloadSize += i2p::context.GetIdentity ()->GetSignatureLen ();
htobe16buf (payload + 1, packet->payloadSize - 3); // size
packet->payloadSize += CreatePaddingBlock (payload + packet->payloadSize, m_MaxPayloadSize - packet->payloadSize);
// 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_DestConnID = ~session->m_SourceConnID;
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;
}
@ -208,15 +224,22 @@ namespace transport @@ -208,15 +224,22 @@ namespace transport
{
if (m_State == eSSU2SessionStateIntroduced)
{
// create new connID
uint64_t oldConnID = GetConnID ();
RAND_bytes ((uint8_t *)&m_DestConnID, 8);
RAND_bytes ((uint8_t *)&m_SourceConnID, 8);
// connect
// we are Alice
// keep ConnIDs used for introduction, because Charlie waits for SessionRequest from us
m_State = eSSU2SessionStateTokenReceived;
m_Server.AddPendingOutgoingSession (shared_from_this ());
m_Server.RemoveSession (oldConnID);
Connect ();
// move session to pending outgoing
if (m_Server.AddPendingOutgoingSession (shared_from_this ()))
{
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 @@ -227,11 +250,9 @@ namespace transport
RAND_bytes ((uint8_t *)&nonce, 4);
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
// session for message 5
auto session = std::make_shared<SSU2Session> (m_Server);
session->SetState (eSSU2SessionStatePeerTest);
m_PeerTests.emplace (nonce, std::make_pair (session, ts/1000));
session->m_SourceConnID = htobe64 (((uint64_t)nonce << 32) | nonce);
session->m_DestConnID = ~session->m_SourceConnID;
auto session = std::make_shared<SSU2PeerTestSession> (m_Server,
htobe64 (((uint64_t)nonce << 32) | nonce), 0);
m_Server.AddRequestedPeerTest (nonce, session, ts/1000);
m_Server.AddSession (session);
// peer test block
auto packet = m_Server.GetSentPacketsPool ().AcquireShared ();
@ -252,7 +273,7 @@ namespace transport @@ -252,7 +273,7 @@ namespace transport
{
uint8_t payload[20];
size_t payloadSize = CreatePaddingBlock (payload, 20, 8);
SendData (payload, payloadSize);
SendData (payload, payloadSize, SSU2_FLAG_IMMEDIATE_ACK_REQUESTED);
}
}
@ -265,6 +286,7 @@ namespace transport @@ -265,6 +286,7 @@ namespace transport
m_OnEstablished = nullptr;
if (m_RelayTag)
m_Server.RemoveRelay (m_RelayTag);
m_Server.AddConnectedRecently (m_RemoteEndpoint, GetLastActivityTimestamp ());
m_SentHandshakePacket.reset (nullptr);
m_SessionConfirmedFragment.reset (nullptr);
m_PathChallenge.reset (nullptr);
@ -275,20 +297,15 @@ namespace transport @@ -275,20 +297,15 @@ namespace transport
m_SentPackets.clear ();
m_IncompleteMessages.clear ();
m_RelaySessions.clear ();
m_PeerTests.clear ();
m_ReceivedI2NPMsgIDs.clear ();
m_Server.RemoveSession (m_SourceConnID);
transports.PeerDisconnected (shared_from_this ());
auto remoteIdentity = GetRemoteIdentity ();
if (remoteIdentity)
{
LogPrint (eLogDebug, "SSU2: Session with ", GetRemoteEndpoint (),
" (", i2p::data::GetIdentHashAbbreviation (remoteIdentity->GetIdentHash ()), ") terminated");
}
else
{
LogPrint (eLogDebug, "SSU2: Session with ", GetRemoteEndpoint (), " terminated");
}
}
}
@ -587,11 +604,7 @@ namespace transport @@ -587,11 +604,7 @@ namespace transport
if (!resentPackets.empty ())
{
m_LastResendTime = ts;
#if (__cplusplus >= 201703L) // C++ 17 or higher
m_SentPackets.merge (resentPackets);
#else
m_SentPackets.insert (resentPackets.begin (), resentPackets.end ());
#endif
m_WindowSize >>= 1; // /2
if (m_WindowSize < SSU2_MIN_WINDOW_SIZE) m_WindowSize = SSU2_MIN_WINDOW_SIZE;
return resentPackets.size ();
@ -1157,7 +1170,7 @@ namespace transport @@ -1157,7 +1170,7 @@ namespace transport
if (profile) // older router?
profile->Duplicated (); // mark router as duplicated in profile
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 ()));
return false;
}
@ -1419,83 +1432,17 @@ namespace transport @@ -1419,83 +1432,17 @@ namespace transport
return false;
}
HandlePayload (payload, len - 48);
m_IsDataReceived = false;
// connect to Charlie
ConnectAfterIntroduction ();
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)
{
// we are Alice or Charlie
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);
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;
LogPrint (eLogWarning, "SSU2: Unexpected peer test message for this session type");
return false;
}
uint32_t SSU2Session::SendData (const uint8_t * buf, size_t len, uint8_t flags)
@ -1561,6 +1508,7 @@ namespace transport @@ -1561,6 +1508,7 @@ namespace transport
return;
}
UpdateNumReceivedBytes (len);
if (header.h.flags[0] & SSU2_FLAG_IMMEDIATE_ACK_REQUESTED) m_IsDataReceived = true;
if (!packetNum || UpdateReceivePacketNum (packetNum))
HandlePayload (payload, payloadSize);
}
@ -1636,14 +1584,17 @@ namespace transport @@ -1636,14 +1584,17 @@ namespace transport
case eSSU2BlkRelayRequest:
LogPrint (eLogDebug, "SSU2: RelayRequest");
HandleRelayRequest (buf + offset, size);
m_IsDataReceived = true;
break;
case eSSU2BlkRelayResponse:
LogPrint (eLogDebug, "SSU2: RelayResponse");
HandleRelayResponse (buf + offset, size);
m_IsDataReceived = true;
break;
case eSSU2BlkRelayIntro:
LogPrint (eLogDebug, "SSU2: RelayIntro");
HandleRelayIntro (buf + offset, size);
m_IsDataReceived = true;
break;
case eSSU2BlkPeerTest:
LogPrint (eLogDebug, "SSU2: PeerTest msg=", (int)buf[offset], " code=", (int)buf[offset+1]);
@ -1754,28 +1705,34 @@ namespace transport @@ -1754,28 +1705,34 @@ namespace transport
void SSU2Session::HandleRouterInfo (const uint8_t * buf, size_t len)
{
auto ri = ExtractRouterInfo (buf, len);
if (ri)
if (len < 2) return;
// 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 newRi = i2p::data::netdb.AddRouterInfo (ri->GetBuffer (), ri->GetBufferLen ()); // TODO: add ri
if (newRi)
auto ri = ExtractRouterInfo (buf, len);
if (ri)
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 ();
if (remoteIdentity && remoteIdentity->GetIdentHash () == newRi->GetIdentHash ())
// peer's RouterInfo update
SetRemoteIdentity (newRi->GetIdentity ());
auto address = m_RemoteEndpoint.address ().is_v6 () ? newRi->GetSSU2V6Address () : newRi->GetSSU2V4Address ();
if (address)
{
// peer's RouterInfo update
SetRemoteIdentity (newRi->GetIdentity ());
auto address = m_RemoteEndpoint.address ().is_v6 () ? newRi->GetSSU2V6Address () : newRi->GetSSU2V4Address ();
if (address)
{
m_Address = address;
if (IsOutgoing () && m_RelayTag && !address->IsIntroducer ())
m_RelayTag = 0; // not longer introducer
}
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)
@ -2007,27 +1964,32 @@ namespace transport @@ -2007,27 +1964,32 @@ namespace transport
SendData (payload, payloadSize);
return;
}
auto mts = i2p::util::GetMillisecondsSinceEpoch ();
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
auto r = i2p::data::netdb.FindRouter (GetRemoteIdentity ()->GetIdentHash ()); // Alice's RI
if (r && (r->IsUnreachable () || !i2p::data::netdb.PopulateRouterInfoBuffer (r))) r = nullptr;
if (!r) LogPrint (eLogWarning, "SSU2: RelayRequest Alice's router info not found");
uint8_t payload[SSU2_MAX_PACKET_SIZE];
size_t payloadSize = r ? CreateRouterInfoBlock (payload, m_MaxPayloadSize - len - 32, r) : 0;
if (!payloadSize && r)
auto packet = m_Server.GetSentPacketsPool ().AcquireShared ();
packet->payloadSize = r ? CreateRouterInfoBlock (packet->payload, m_MaxPayloadSize - len - 32, r) : 0;
if (!packet->payloadSize && r)
session->SendFragmentedMessage (CreateDatabaseStoreMsg (r));
payloadSize += CreateRelayIntroBlock (payload + payloadSize, m_MaxPayloadSize - payloadSize, buf + 1, len -1);
if (payloadSize < m_MaxPayloadSize)
payloadSize += CreatePaddingBlock (payload + payloadSize, m_MaxPayloadSize - payloadSize);
session->SendData (payload, payloadSize);
packet->payloadSize += CreateRelayIntroBlock (packet->payload + packet->payloadSize, m_MaxPayloadSize - packet->payloadSize, buf + 1, len -1);
if (packet->payloadSize < m_MaxPayloadSize)
packet->payloadSize += CreatePaddingBlock (packet->payload + packet->payloadSize, m_MaxPayloadSize - packet->payloadSize);
uint32_t packetNum = session->SendData (packet->payload, packet->payloadSize);
packet->sendTime = mts;
// Charlie always responds with RelayResponse
session->m_SentPackets.emplace (packetNum, packet);
}
void SSU2Session::HandleRelayIntro (const uint8_t * buf, size_t len, int attempts)
{
// we are Charlie
auto mts = i2p::util::GetMillisecondsSinceEpoch ();
SSU2RelayResponseCode code = eSSU2RelayResponseCodeAccept;
uint64_t token = 0;
bool isV4 = false;
@ -2047,7 +2009,9 @@ namespace transport @@ -2047,7 +2009,9 @@ namespace transport
boost::asio::ip::udp::endpoint 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 (m_Server.IsSupported (ep.address ()))
@ -2055,6 +2019,7 @@ namespace transport @@ -2055,6 +2019,7 @@ namespace transport
token = m_Server.GetIncomingToken (ep);
isV4 = ep.address ().is_v4 ();
SendHolePunch (bufbe32toh (buf + 33), ep, addr->i, token);
m_Server.AddConnectedRecently (ep, mts/1000);
}
else
{
@ -2099,11 +2064,15 @@ namespace transport @@ -2099,11 +2064,15 @@ namespace transport
code = eSSU2RelayResponseCodeCharlieAliceIsUnknown;
}
// send relay response to Bob
uint8_t payload[SSU2_MAX_PACKET_SIZE];
size_t payloadSize = CreateRelayResponseBlock (payload, m_MaxPayloadSize,
auto packet = m_Server.GetSentPacketsPool ().AcquireShared ();
packet->payloadSize = CreateRelayResponseBlock (packet->payload, m_MaxPayloadSize,
code, bufbe32toh (buf + 33), token, isV4);
payloadSize += CreatePaddingBlock (payload + payloadSize, m_MaxPayloadSize - payloadSize);
SendData (payload, payloadSize);
packet->payloadSize += CreatePaddingBlock (packet->payload + packet->payloadSize, m_MaxPayloadSize - packet->payloadSize);
/*uint32_t packetNum = */SendData (packet->payload, packet->payloadSize);
// sometimes Bob doesn't ack this RelayResponse
// TODO: uncomment line below once the problem is resolved
//packet->sendTime = mts;
//m_SentPackets.emplace (packetNum, packet);
}
void SSU2Session::HandleRelayResponse (const uint8_t * buf, size_t len)
@ -2131,13 +2100,18 @@ namespace transport @@ -2131,13 +2100,18 @@ namespace transport
if (it->second.first && it->second.first->IsEstablished ())
{
// 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;
htobe16buf (payload + 1, len);
memcpy (payload + 3, buf, len); // forward to Alice as is
size_t payloadSize = len + 3;
payloadSize += CreatePaddingBlock (payload + payloadSize, m_MaxPayloadSize - payloadSize);
it->second.first->SendData (payload, payloadSize);
packet->payloadSize = len + 3;
packet->payloadSize += CreatePaddingBlock (payload + packet->payloadSize, m_MaxPayloadSize - packet->payloadSize);
/*uint32_t packetNum = */it->second.first->SendData (packet->payload, packet->payloadSize);
// sometimes Alice doesn't ack this RelayResponse
// TODO: uncomment line below once the problem is resolved
//packet->sendTime = i2p::util::GetMillisecondsSinceEpoch ();
//it->second.first->m_SentPackets.emplace (packetNum, packet);
}
else
{
@ -2177,18 +2151,19 @@ namespace transport @@ -2177,18 +2151,19 @@ namespace transport
}
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 ();
}
}
m_RelaySessions.erase (it);
}
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)
{
// msgs 1-4
if (len < 3) return;
uint8_t msg = buf[0];
size_t offset = 3; // points to signed data
@ -2204,7 +2179,7 @@ namespace transport @@ -2204,7 +2179,7 @@ namespace transport
GetRemoteIdentity ()->GetIdentHash ());
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 ();
// Alice's RouterInfo
auto r = i2p::data::netdb.FindRouter (GetRemoteIdentity ()->GetIdentHash ());
@ -2231,11 +2206,14 @@ namespace transport @@ -2231,11 +2206,14 @@ namespace transport
else
{
// Charlie not found, send error back to Alice
uint8_t payload[SSU2_MAX_PACKET_SIZE], zeroHash[32] = {0};
size_t payloadSize = CreatePeerTestBlock (payload, m_MaxPayloadSize, 4,
auto packet = m_Server.GetSentPacketsPool ().AcquireShared ();
uint8_t zeroHash[32] = {0};
packet->payloadSize = CreatePeerTestBlock (packet->payload, m_MaxPayloadSize, 4,
eSSU2PeerTestCodeBobNoCharlieAvailable, zeroHash, buf + offset, len - offset);
payloadSize += CreatePaddingBlock (payload + payloadSize, m_MaxPayloadSize - payloadSize);
SendData (payload, payloadSize);
packet->payloadSize += CreatePaddingBlock (packet->payload + packet->payloadSize, m_MaxPayloadSize - packet->payloadSize);
uint32_t packetNum = SendData (packet->payload, packet->payloadSize);
packet->sendTime = ts;
m_SentPackets.emplace (packetNum, packet);
}
break;
}
@ -2269,19 +2247,22 @@ namespace transport @@ -2269,19 +2247,22 @@ namespace transport
{
boost::asio::ip::udp::endpoint ep;
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 ());
if (addr && m_Server.IsSupported (ep.address ()) &&
i2p::context.GetRouterInfo ().IsSSU2PeerTesting (ep.address ().is_v4 ()))
{
// send msg 5 to Alice
auto session = std::make_shared<SSU2Session> (m_Server, r, addr);
session->SetState (eSSU2SessionStatePeerTest);
session->m_RemoteEndpoint = ep; // might be different
session->m_DestConnID = htobe64 (((uint64_t)nonce << 32) | nonce);
session->m_SourceConnID = ~session->m_DestConnID;
m_Server.AddSession (session);
session->SendPeerTest (5, newSignedData.data (), newSignedData.size (), addr->i);
if (!m_Server.IsConnectedRecently (ep)) // no alive hole punch
{
// send msg 5 to Alice
auto session = std::make_shared<SSU2PeerTestSession> (m_Server,
0, htobe64 (((uint64_t)nonce << 32) | nonce));
session->m_RemoteEndpoint = ep; // might be different
m_Server.AddSession (session);
session->SendPeerTest (5, newSignedData.data (), newSignedData.size (), addr);
}
else
code = eSSU2PeerTestCodeCharlieAliceIsAlreadyConnected;
}
else
code = eSSU2PeerTestCodeCharlieUnsupportedAddress;
@ -2298,54 +2279,59 @@ namespace transport @@ -2298,54 +2279,59 @@ namespace transport
else
code = eSSU2PeerTestCodeCharlieAliceIsUnknown;
// send msg 3 back to Bob
uint8_t payload[SSU2_MAX_PACKET_SIZE];
size_t payloadSize = CreatePeerTestBlock (payload, m_MaxPayloadSize, 3,
auto packet = m_Server.GetSentPacketsPool ().AcquireShared ();
packet->payloadSize = CreatePeerTestBlock (packet->payload, m_MaxPayloadSize, 3,
code, nullptr, newSignedData.data (), newSignedData.size ());
payloadSize += CreatePaddingBlock (payload + payloadSize, m_MaxPayloadSize - payloadSize);
SendData (payload, payloadSize);
packet->payloadSize += CreatePaddingBlock (packet->payload + packet->payloadSize, m_MaxPayloadSize - packet->payloadSize);
uint32_t packetNum = SendData (packet->payload, packet->payloadSize);
packet->sendTime = ts;
m_SentPackets.emplace (packetNum, packet);
break;
}
case 3: // Bob from Charlie
{
auto it = m_PeerTests.find (nonce);
if (it != m_PeerTests.end () && it->second.first)
{
uint8_t payload[SSU2_MAX_PACKET_SIZE];
auto aliceSession = m_Server.GetPeerTest (nonce);
if (aliceSession && aliceSession->IsEstablished ())
{
auto packet = m_Server.GetSentPacketsPool ().AcquireShared ();
// Charlie's RouterInfo
auto r = i2p::data::netdb.FindRouter (GetRemoteIdentity ()->GetIdentHash ());
if (r && (r->IsUnreachable () || !i2p::data::netdb.PopulateRouterInfoBuffer (r))) r = nullptr;
size_t payloadSize = r ? CreateRouterInfoBlock (payload, m_MaxPayloadSize - len - 32, r) : 0;
if (!payloadSize && r)
it->second.first->SendFragmentedMessage (CreateDatabaseStoreMsg (r));
if (payloadSize + len + 16 > m_MaxPayloadSize)
packet->payloadSize = r ? CreateRouterInfoBlock (packet->payload, m_MaxPayloadSize - len - 32, r) : 0;
if (!packet->payloadSize && r)
aliceSession->SendFragmentedMessage (CreateDatabaseStoreMsg (r));
if (packet->payloadSize + len + 16 > m_MaxPayloadSize)
{
// doesn't fit one message, send RouterInfo in separate message
it->second.first->SendData (payload, payloadSize);
payloadSize = 0;
uint32_t packetNum = aliceSession->SendData (packet->payload, packet->payloadSize);
packet->sendTime = ts;
aliceSession->m_SentPackets.emplace (packetNum, packet);
packet = m_Server.GetSentPacketsPool ().AcquireShared ();
}
// 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);
if (payloadSize < m_MaxPayloadSize)
payloadSize += CreatePaddingBlock (payload + payloadSize, m_MaxPayloadSize - payloadSize);
it->second.first->SendData (payload, payloadSize);
m_PeerTests.erase (it);
}
if (packet->payloadSize < m_MaxPayloadSize)
packet->payloadSize += CreatePaddingBlock (packet->payload + packet->payloadSize, m_MaxPayloadSize - packet->payloadSize);
uint32_t packetNum = aliceSession->SendData (packet->payload, packet->payloadSize);
packet->sendTime = ts;
aliceSession->m_SentPackets.emplace (packetNum, packet);
}
else
LogPrint (eLogWarning, "SSU2: Unknown peer test 3 nonce ", nonce);
LogPrint (eLogDebug, "SSU2: Unknown peer test 3 nonce ", nonce);
break;
}
case 4: // Alice from Bob
{
auto it = m_PeerTests.find (nonce);
if (it != m_PeerTests.end ())
auto session = m_Server.GetRequestedPeerTest (nonce);
if (session)
{
if (buf[1] == eSSU2PeerTestCodeAccept)
{
if (GetRouterStatus () == eRouterStatusUnknown)
SetTestingState (true);
auto r = i2p::data::netdb.FindRouter (buf + 3); // find Charlie
if (r && it->second.first)
if (r)
{
uint8_t asz = buf[offset + 9];
SignedData s;
@ -2355,26 +2341,33 @@ namespace transport @@ -2355,26 +2341,33 @@ namespace transport
s.Insert (buf + offset, asz + 10); // ver, nonce, ts, asz, Alice's endpoint
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 ());
if (addr)
{
it->second.first->m_Address = addr;
if (it->second.first->m_State == eSSU2SessionStatePeerTestReceived)
if (session->GetMsgNumReceived () >= 5)
{
// msg 5 already received. send msg 6
SetRouterStatus (eRouterStatusOK);
it->second.first->m_State = eSSU2SessionStatePeerTest;
it->second.first->SendPeerTest (6, buf + offset, len - offset, addr->i);
// msg 5 already received
if (session->GetMsgNumReceived () == 5)
{
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
{
session->m_Address = addr;
if (GetTestingState ())
{
SetTestingState (false);
if (GetRouterStatus () != eRouterStatusFirewalled && addr->IsPeerTesting ())
{
SetRouterStatus (eRouterStatusFirewalled);
session->SetStatusChanged ();
if (m_Address->IsV4 ())
m_Server.RescheduleIntroducersUpdateTimer ();
else
@ -2388,63 +2381,34 @@ namespace transport @@ -2388,63 +2381,34 @@ namespace transport
else
{
LogPrint (eLogWarning, "SSU2: Peer test 4 address not found");
it->second.first->Done ();
session->Done ();
}
}
else
{
LogPrint (eLogWarning, "SSU2: Peer test 4 signature verification failed");
it->second.first->Done ();
session->Done ();
}
}
else
{
LogPrint (eLogWarning, "SSU2: Peer test 4 router not found");
if (it->second.first)
it->second.first->Done ();
session->Done ();
}
}
else
{
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)));
if (GetTestingState ())
if (GetTestingState () && GetRouterStatus () != eRouterStatusFirewalled)
SetRouterStatus (eRouterStatusUnknown);
it->second.first->Done ();
session->Done ();
}
m_PeerTests.erase (it);
}
else
LogPrint (eLogWarning, "SSU2: Unknown peer test 4 nonce ", nonce);
LogPrint (eLogDebug, "SSU2: Unknown peer test 4 nonce ", nonce);
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:
LogPrint (eLogWarning, "SSU2: PeerTest unexpected msg num ", buf[0]);
}
@ -2933,22 +2897,18 @@ namespace transport @@ -2933,22 +2897,18 @@ namespace transport
i2p::data::GzipInflator inflator;
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);
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);
else
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);
else
LogPrint (eLogInfo, "SSU2: RouterInfo is too long ", size);
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)
{
if (packetNum <= m_ReceivePacketNum) return false; // duplicate
@ -3108,16 +3068,6 @@ namespace transport @@ -3108,16 +3068,6 @@ namespace transport
else
++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)
RequestTermination (eSSU2TerminationReasonNormalClose);
}
@ -3137,5 +3087,215 @@ namespace transport @@ -3137,5 +3087,215 @@ namespace transport
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 @@ -113,7 +113,6 @@ namespace transport
eSSU2SessionStateFailed,
eSSU2SessionStateIntroduced,
eSSU2SessionStatePeerTest,
eSSU2SessionStatePeerTestReceived, // 5 before 4
eSSU2SessionStateTokenRequestReceived
};
@ -206,36 +205,40 @@ namespace transport @@ -206,36 +205,40 @@ namespace transport
class SSU2Server;
class SSU2Session: public TransportSession, public std::enable_shared_from_this<SSU2Session>
{
union Header
{
uint64_t ll[2];
uint8_t buf[16];
struct
protected:
union Header
{
uint64_t connID;
uint32_t packetNum;
uint8_t type;
uint8_t flags[3];
} h;
};
uint64_t ll[2];
uint8_t buf[16];
struct
{
uint64_t connID;
uint32_t packetNum;
uint8_t type;
uint8_t flags[3];
} h;
};
struct HandshakePacket
{
Header header;
uint8_t headerX[48]; // part1 for SessionConfirmed
uint8_t payload[SSU2_MAX_PACKET_SIZE*2];
size_t payloadSize = 0;
uint64_t sendTime = 0; // in milliseconds
bool isSecondFragment = false; // for SessionConfirmed
};
private:
struct HandshakePacket
{
Header header;
uint8_t headerX[48]; // part1 for SessionConfirmed
uint8_t payload[SSU2_MAX_PACKET_SIZE*2];
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:
SSU2Session (SSU2Server& server, std::shared_ptr<const i2p::data::RouterInfo> in_RemoteRouter = nullptr,
std::shared_ptr<const i2p::data::RouterInfo::Address> addr = nullptr);
~SSU2Session ();
std::shared_ptr<const i2p::data::RouterInfo::Address> addr = nullptr, bool noise = true);
virtual ~SSU2Session ();
void SetRemoteEndpoint (const boost::asio::ip::udp::endpoint& ep) { m_RemoteEndpoint = ep; };
const boost::asio::ip::udp::endpoint& GetRemoteEndpoint () const { return m_RemoteEndpoint; };
@ -245,7 +248,7 @@ namespace transport @@ -245,7 +248,7 @@ namespace transport
void SetOnEstablished (OnEstablished e) { m_OnEstablished = e; };
OnEstablished GetOnEstablished () const { return m_OnEstablished; };
void Connect ();
virtual void Connect ();
bool Introduce (std::shared_ptr<SSU2Session> session, uint32_t relayTag);
void WaitForIntroduction ();
void SendPeerTest (); // Alice, Data message
@ -265,14 +268,34 @@ namespace transport @@ -265,14 +268,34 @@ namespace transport
SSU2SessionState GetState () const { return m_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 ProcessSessionConfirmed (uint8_t * buf, size_t len);
bool ProcessRetry (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);
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:
void Terminate ();
@ -298,11 +321,9 @@ namespace transport @@ -298,11 +321,9 @@ namespace transport
void SendQuickAck ();
void SendTermination ();
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 SendPathChallenge ();
void HandlePayload (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 HandleAck (const uint8_t * buf, size_t len);
@ -312,35 +333,29 @@ namespace transport @@ -312,35 +333,29 @@ namespace transport
size_t CreateEndpoint (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& ep);
std::shared_ptr<const i2p::data::RouterInfo::Address> FindLocalAddress () const;
void AdjustMaxPayloadSize ();
RouterStatus GetRouterStatus () const;
void SetRouterStatus (RouterStatus status) const;
bool GetTestingState () const;
void SetTestingState(bool testing) const;
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
void HandleFirstFragment (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 HandleRelayIntro (const uint8_t * buf, size_t len, int attempts = 0);
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);
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::Buffer> riBuffer);
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 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 CreateRelayIntroBlock (uint8_t * buf, size_t len, const uint8_t * introData, size_t introDataLen);
size_t CreateRelayResponseBlock (uint8_t * buf, size_t len, SSU2RelayResponseCode code, uint32_t nonce, uint64_t token, bool v4);
size_t CreatePeerTestBlock (uint8_t * buf, size_t len, 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 CreateTerminationBlock (uint8_t * buf, size_t len);
private:
SSU2Server& m_Server;
@ -358,8 +373,7 @@ namespace transport @@ -358,8 +373,7 @@ namespace transport
std::set<uint32_t> m_OutOfSequencePackets; // packet nums > receive packet num
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::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::unordered_map<uint32_t, std::pair <std::shared_ptr<SSU2Session>, uint64_t > > m_RelaySessions; // nonce->(Alice, timestamp) for Bob or nonce->(Charlie, timestamp) for Alice
std::list<std::shared_ptr<I2NPMessage> > m_SendQueue;
i2p::I2NPMessagesHandler m_Handler;
bool m_IsDataReceived;
@ -377,6 +391,43 @@ namespace transport @@ -377,6 +391,43 @@ namespace transport
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)
{
uint64_t data = 0;

360
libi2pd/Streaming.cpp

@ -68,18 +68,19 @@ namespace stream @@ -68,18 +68,19 @@ namespace stream
Stream::Stream (boost::asio::io_service& service, StreamingDestination& local,
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_LastConfirmedReceivedSequenceNumber (0), // for limit inbound speed
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_IsTimeOutResend (false), m_LocalDestination (local),
m_IsResendNeeded (false), m_IsFirstRttSample (false), m_IsSendTime (true), m_IsWinDropped (false),
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_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_PrevRTT (INITIAL_RTT), m_Jitter (0), m_MinPacingTime (0),
m_PacingTime (INITIAL_PACING_TIME), m_PacingTimeRem (0), m_DropWindowDelayTime (0), m_LastSendTime (0),
m_Jitter (0), m_MinPacingTime (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_NumResendAttempts (0), m_NumPacketsToSend (0), m_MTU (STREAMING_MTU)
{
@ -95,18 +96,18 @@ namespace stream @@ -95,18 +96,18 @@ namespace stream
}
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_LastConfirmedReceivedSequenceNumber (0), // for limit inbound speed
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_IsTimeOutResend (false), m_LocalDestination (local),
m_IsResendNeeded (false), m_IsFirstRttSample (false), m_IsSendTime (true), m_IsWinDropped (false),
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_NumSentBytes (0), m_NumReceivedBytes (0), m_Port (0), m_RTT (INITIAL_RTT), m_SlowRTT (INITIAL_RTT),
m_WindowSize (INITIAL_WINDOW_SIZE), m_LastWindowDropSize (0), m_WindowIncCounter (0),
m_NumSentBytes (0), m_NumReceivedBytes (0), m_Port (0), m_RTT (INITIAL_RTT), m_SlowRTT (INITIAL_RTT), m_SlowRTT2 (INITIAL_RTT),
m_WindowSize (INITIAL_WINDOW_SIZE), m_LastWindowDropSize (0), m_WindowDropTargetSize (0), m_WindowIncCounter (0),
m_RTO (INITIAL_RTO), m_AckDelay (local.GetOwner ()->GetStreamingAckDelay ()),
m_PrevRTTSample (INITIAL_RTT), m_PrevRTT (INITIAL_RTT), m_Jitter (0), m_MinPacingTime (0),
m_PacingTime (INITIAL_PACING_TIME), m_PacingTimeRem (0), m_DropWindowDelayTime (0), m_LastSendTime (0),
m_PrevRTTSample (INITIAL_RTT), m_Jitter (0), m_MinPacingTime (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_NumResendAttempts (0), m_NumPacketsToSend (0), m_MTU (STREAMING_MTU)
{
@ -183,10 +184,29 @@ namespace stream @@ -183,10 +184,29 @@ namespace stream
ProcessAck (packet);
int32_t receivedSeqn = packet->GetSeqn ();
if (!receivedSeqn && !packet->GetFlags ())
if (!receivedSeqn && m_LastReceivedSequenceNumber >= 0)
{
// plain ack
LogPrint (eLogDebug, "Streaming: Plain ACK received");
uint16_t flags = packet->GetFlags ();
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);
return;
}
@ -325,12 +345,16 @@ namespace stream @@ -325,12 +345,16 @@ namespace stream
LogPrint (eLogInfo, "Streaming: Invalid option size ", optionSize, " Discarded");
return false;
}
if (!flags) return true;
bool immediateAckRequested = false;
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 > 0 && delayRequested < m_RTT)
if (delayRequested < m_RTT)
{
m_IsAckSendScheduled = true;
m_AckSendTimer.expires_from_now (boost::posix_time::milliseconds(delayRequested));
@ -339,8 +363,15 @@ namespace stream @@ -339,8 +363,15 @@ namespace stream
}
if (delayRequested >= DELAY_CHOKING)
{
m_WindowSize = 1;
m_WindowIncCounter = 0;
if (!m_IsWinDropped)
{
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;
@ -424,6 +455,8 @@ namespace stream @@ -424,6 +455,8 @@ namespace stream
return false;
}
}
if (immediateAckRequested)
SendQuickAck ();
return true;
}
@ -503,58 +536,47 @@ namespace stream @@ -503,58 +536,47 @@ namespace stream
m_LocalDestination.DeletePacket (sentPacket);
acknowledged = true;
if (m_WindowSize < MAX_WINDOW_SIZE && !m_IsFirstACK)
m_WindowIncCounter++;
if (m_RTT < m_LocalDestination.GetRandom () % INITIAL_RTT) // dirty
m_WindowIncCounter++;
}
else
break;
}
if (rttSample != INT_MAX)
{
if (m_IsFirstRttSample)
if (m_IsFirstRttSample && !m_IsFirstACK)
{
m_RTT = rttSample;
m_SlowRTT = rttSample;
m_SlowRTT2 = rttSample;
m_PrevRTTSample = rttSample;
if (m_RoutingSession)
m_RoutingSession->SetSharedRoutingPath (
std::make_shared<i2p::garlic::GarlicRoutingPath> (
i2p::garlic::GarlicRoutingPath{m_CurrentOutboundTunnel, m_CurrentRemoteLease, (int)m_RTT, 0}));
m_Jitter = rttSample / 10; // 10%
m_Jitter += 5; // for low-latency connections
m_IsFirstRttSample = false;
}
else
m_RTT = RTT_EWMA_ALPHA * m_RTT + (1.0 - RTT_EWMA_ALPHA) * rttSample;
// calculate jitter
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
m_RTT = (m_PrevRTTSample + rttSample) / 2;
if (!m_IsWinDropped)
{
if (m_LastWindowDropSize)
m_LastWindowDropSize = (m_LastWindowDropSize + m_WindowSize) / 2;
m_SlowRTT = SLOWRTT_EWMA_ALPHA * m_RTT + (1.0 - SLOWRTT_EWMA_ALPHA) * m_SlowRTT;
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
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
jitter = rttSample / 10; // 10%
jitter += 5; // for low-latency connections
m_Jitter = (0.05 * jitter) + (1.0 - 0.05) * m_Jitter;
}
//
// 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 ();
if (rttSample < m_RTT) // need for delay-based CC
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;
m_PrevRTTSample = rttSample;
bool wasInitial = m_RTO == INITIAL_RTO;
m_RTO = std::max (MIN_RTO, (int)(m_RTT * 1.3 + m_Jitter)); // TODO: implement it better
@ -562,26 +584,52 @@ namespace stream @@ -562,26 +584,52 @@ namespace stream
if (wasInitial)
ScheduleResend ();
}
if ( ts > m_DropWindowDelayTime)
if (m_IsWinDropped && ackThrough > m_DropWindowDelaySequenceNumber)
{
m_IsFirstRttSample = true;
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)
{
ScheduleResend ();
}
if ((m_SendBuffer.IsEmpty () && m_SentPackets.size () > 0) // tail loss
|| int(m_SentPackets.size ()) > m_WindowSize) // or we drop window
if (m_SendBuffer.IsEmpty () && m_SentPackets.size () > 0) // tail loss
{
m_IsResendNeeded = true;
m_RTO = std::max (MIN_RTO, (int)(m_RTT * 1.5 + m_Jitter)); // to prevent spurious retransmit
}
if (m_SentPackets.empty () && m_SendBuffer.IsEmpty ())
{
m_ResendTimer.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)
{
m_NumResendAttempts = 0;
m_IsFirstACK = false;
m_IsTimeOutResend = false;
SendBuffer ();
}
if (m_Status == eStreamStatusClosed)
@ -781,13 +829,22 @@ namespace stream @@ -781,13 +829,22 @@ namespace stream
// for limit inbound speed
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
int numPackets = 0;
bool lostPackets = false;
int64_t passedTime = m_PacketACKInterval * INITIAL_WINDOW_SIZE; // in microseconds // while m_LastACKSendTime == 0
if (m_LastACKSendTime)
passedTime = (ts - m_LastACKSendTime)*1000; // in microseconds
numPackets = (passedTime + m_PacketACKIntervalRem) / m_PacketACKInterval;
m_PacketACKIntervalRem = (passedTime + m_PacketACKIntervalRem) - (numPackets * m_PacketACKInterval);
if (m_LastConfirmedReceivedSequenceNumber + numPackets < m_LastReceivedSequenceNumber)
{
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;
// for limit inbound speed
if (!m_SavedPackets.empty ())
@ -795,8 +852,26 @@ namespace stream @@ -795,8 +852,26 @@ namespace stream
for (auto it: m_SavedPackets)
{
auto seqn = it->GetSeqn ();
if (m_LastConfirmedReceivedSequenceNumber + numPackets < int(seqn)) break; // for limit inbound speed
if ((int)seqn > lastReceivedSeqn) lastReceivedSeqn = seqn;
// for limit inbound speed
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)
@ -858,13 +933,22 @@ namespace stream @@ -858,13 +933,22 @@ namespace stream
}
packet[size] = 0;
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
if (choking)
if (choking || requestImmediateAck)
{
htobuf16 (packet + size, 2); // 2 bytes delay interval
htobuf16 (packet + size + 2, DELAY_CHOKING); // set choking interval
htobe16buf (packet + size, 2); // 2 bytes delay interval
htobe16buf (packet + size + 2, choking ? DELAY_CHOKING : 0); // set choking or immediated ack interval
size += 2;
if (requestImmediateAck) // ack request sent
{
m_LastSendTime = ts;
m_IsImmediateAckRequested = true;
}
}
else
htobuf16 (packet + size, 0); // no options
@ -1135,45 +1219,37 @@ namespace stream @@ -1135,45 +1219,37 @@ namespace stream
{
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 ();
if (m_LastSendTime && ts*1000 > m_LastSendTime*1000 + m_PacingTime)
{
m_NumPacketsToSend = ((ts*1000 - m_LastSendTime*1000) + m_PacingTimeRem) / m_PacingTime;
m_PacingTimeRem = ((ts*1000 - m_LastSendTime*1000) + m_PacingTimeRem) - (m_NumPacketsToSend * m_PacingTime);
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 ();
// delay-based CC
else if (!m_IsWinDropped && int(m_SentPackets.size ()) == m_WindowSize) // we sending packets too fast, early detection
{
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 ();
}
ProcessWindowDrop ();
else if (m_WindowSize > int(m_SentPackets.size ())) // send packets
SendBuffer ();
}
@ -1265,31 +1341,19 @@ namespace stream @@ -1265,31 +1341,19 @@ namespace stream
if (m_NumResendAttempts == 1 && m_RTO != INITIAL_RTO)
{
// loss-based CC
if (!m_IsWinDropped)
{
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 ();
}
if (!m_IsWinDropped && LOSS_BASED_CONTROL_ENABLED)
ProcessWindowDrop ();
}
else if (m_IsTimeOutResend)
{
m_IsTimeOutResend = false;
m_RTO = INITIAL_RTO; // drop RTO to initial upon tunnels pair change
m_WindowSize = INITIAL_WINDOW_SIZE;
m_WindowDropTargetSize = INITIAL_WINDOW_SIZE;
m_LastWindowDropSize = 0;
m_WindowIncCounter = 0;
m_IsWinDropped = true;
m_IsFirstRttSample = true;
m_DropWindowDelayTime = 0;
m_DropWindowDelaySequenceNumber = 0;
m_IsFirstACK = true;
UpdatePacingTime ();
if (m_RoutingSession) m_RoutingSession->SetSharedRoutingPath (nullptr);
@ -1359,6 +1423,7 @@ namespace stream @@ -1359,6 +1423,7 @@ namespace stream
void Stream::UpdateCurrentRemoteLease (bool expired)
{
bool isLeaseChanged = true;
if (!m_RemoteLeaseSet || m_RemoteLeaseSet->IsExpired ())
{
auto remoteLeaseSet = m_LocalDestination.GetOwner ()->FindLeaseSet (m_RemoteIdentity->GetIdentHash ());
@ -1416,10 +1481,15 @@ namespace stream @@ -1416,10 +1481,15 @@ namespace stream
}
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)
{
// 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];
}
}
@ -1436,16 +1506,23 @@ namespace stream @@ -1436,16 +1506,23 @@ namespace stream
LogPrint (eLogWarning, "Streaming: Remote LeaseSet not found");
m_CurrentRemoteLease = nullptr;
}
// drop window to initial upon RemoteLease change
m_RTO = INITIAL_RTO;
m_WindowSize = INITIAL_WINDOW_SIZE;
m_LastWindowDropSize = 0;
m_WindowIncCounter = 0;
m_IsWinDropped = true;
m_IsFirstRttSample = true;
m_DropWindowDelayTime = 0;
m_IsFirstACK = true;
UpdatePacingTime ();
if (isLeaseChanged)
{
// drop window to initial upon RemoteLease change
m_RTO = INITIAL_RTO;
if (m_WindowSize > INITIAL_WINDOW_SIZE)
{
m_WindowDropTargetSize = std::max (m_WindowSize/2, (float)INITIAL_WINDOW_SIZE);
m_IsWinDropped = true;
}
else
m_WindowSize = INITIAL_WINDOW_SIZE;
m_LastWindowDropSize = 0;
m_WindowIncCounter = 0;
m_IsFirstRttSample = true;
m_IsFirstACK = true;
UpdatePacingTime ();
}
}
void Stream::ResetRoutingPath ()
@ -1464,10 +1541,29 @@ namespace stream @@ -1464,10 +1541,29 @@ namespace stream
if (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):
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 @@ -1656,10 +1752,12 @@ namespace stream
m_IncomingStreams.erase (stream->GetSendStreamID ());
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_I2NPMsgsPool.CleanUp ();
m_LastCleanupTime = ts;
}
}
@ -1795,5 +1893,15 @@ namespace stream @@ -1795,5 +1893,15 @@ namespace stream
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 @@ -54,9 +54,11 @@ namespace stream
const size_t COMPRESSION_THRESHOLD_SIZE = 66;
const int MAX_NUM_RESEND_ATTEMPTS = 10;
const int INITIAL_WINDOW_SIZE = 10;
const int MIN_WINDOW_SIZE = 1;
const int MAX_WINDOW_SIZE = 1024;
const double RTT_EWMA_ALPHA = 0.125;
const int MIN_WINDOW_SIZE = 2;
const int MAX_WINDOW_SIZE = 512;
const double RTT_EWMA_ALPHA = 0.25;
const double SLOWRTT_EWMA_ALPHA = 0.05;
const double PREV_SPEED_KEEP_TIME_COEFF = 0.35; // 0.1 - 1 // how long will the window size stay around the previous drop level, less is longer
const int MIN_RTO = 20; // in milliseconds
const int INITIAL_RTT = 8000; // in milliseconds
const int INITIAL_RTO = 9000; // in milliseconds
@ -68,7 +70,11 @@ namespace stream @@ -68,7 +70,11 @@ namespace stream
const int MAX_RECEIVE_TIMEOUT = 20; // in seconds
const uint16_t DELAY_CHOKING = 60000; // in milliseconds
const uint64_t SEND_INTERVAL = 1000; // in microseconds
const uint64_t 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
{
size_t len, offset;
@ -241,11 +247,13 @@ namespace stream @@ -241,11 +247,13 @@ namespace stream
void HandleAckSendTimer (const boost::system::error_code& ecode);
void UpdatePacingTime ();
void ProcessWindowDrop ();
private:
boost::asio::io_service& m_Service;
uint32_t m_SendStreamID, m_RecvStreamID, m_SequenceNumber;
uint32_t m_DropWindowDelaySequenceNumber;
uint32_t m_TunnelsChangeSequenceNumber;
int32_t m_LastReceivedSequenceNumber;
int32_t m_PreviousReceivedSequenceNumber;
@ -259,6 +267,7 @@ namespace stream @@ -259,6 +267,7 @@ namespace stream
bool m_IsSendTime;
bool m_IsWinDropped;
bool m_IsTimeOutResend;
bool m_IsImmediateAckRequested;
StreamingDestination& m_LocalDestination;
std::shared_ptr<const i2p::data::IdentityEx> m_RemoteIdentity;
std::shared_ptr<const i2p::crypto::Verifier> m_TransientVerifier; // in case of offline key
@ -275,10 +284,12 @@ namespace stream @@ -275,10 +284,12 @@ namespace stream
uint16_t m_Port;
SendBufferQueue m_SendBuffer;
double m_RTT, m_SlowRTT;
float m_WindowSize, m_LastWindowDropSize;
int m_WindowIncCounter, m_RTO, m_AckDelay, m_PrevRTTSample, m_PrevRTT, m_Jitter;
uint64_t m_MinPacingTime, m_PacingTime, m_PacingTimeRem, m_DropWindowDelayTime, m_LastSendTime; // microseconds
double m_RTT, m_SlowRTT, m_SlowRTT2;
float m_WindowSize, m_LastWindowDropSize, m_WindowDropTargetSize;
int m_WindowIncCounter, m_RTO, m_AckDelay, m_PrevRTTSample;
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
int m_NumResendAttempts, m_NumPacketsToSend;
size_t m_MTU;
@ -316,6 +327,7 @@ namespace stream @@ -316,6 +327,7 @@ namespace stream
Packet * NewPacket () { return m_PacketsPool.Acquire(); }
void DeletePacket (Packet * p) { return m_PacketsPool.Release(p); }
uint32_t GetRandom ();
private:
@ -339,7 +351,8 @@ namespace stream @@ -339,7 +351,8 @@ namespace stream
i2p::util::MemoryPool<Packet> m_PacketsPool;
i2p::util::MemoryPool<I2NPMessageBuffer<I2NP_MAX_SHORT_MESSAGE_SIZE> > m_I2NPMsgsPool;
uint64_t m_LastCleanupTime; // in seconds
public:
i2p::data::GzipInflator m_Inflator;

2
libi2pd/Transports.cpp

@ -599,7 +599,7 @@ namespace transport @@ -599,7 +599,7 @@ namespace transport
}
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
peer->Done ();
std::unique_lock<std::mutex> l(m_PeersMutex);

14
libi2pd/Tunnel.cpp

@ -411,10 +411,12 @@ namespace tunnel @@ -411,10 +411,12 @@ namespace tunnel
return tunnel;
}
std::shared_ptr<TunnelPool> Tunnels::CreateTunnelPool (int numInboundHops, int numOutboundHops,
int numInboundTunnels, int numOutboundTunnels, int inboundVariance, int outboundVariance)
std::shared_ptr<TunnelPool> Tunnels::CreateTunnelPool (int numInboundHops,
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);
m_Pools.push_back (pool);
return pool;
@ -705,7 +707,7 @@ namespace tunnel @@ -705,7 +707,7 @@ namespace tunnel
auto inboundTunnel = GetNextInboundTunnel ();
auto router = i2p::transport::transports.RoutesRestricted() ?
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;
LogPrint (eLogDebug, "Tunnel: Creating one hop outbound tunnel");
CreateTunnel<OutboundTunnel> (
@ -765,7 +767,7 @@ namespace tunnel @@ -765,7 +767,7 @@ namespace tunnel
int obLen; i2p::config::GetOption("exploratory.outbound.length", obLen);
int ibNum; i2p::config::GetOption("exploratory.inbound.quantity", ibNum);
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 ());
}
return;
@ -777,7 +779,7 @@ namespace tunnel @@ -777,7 +779,7 @@ namespace tunnel
auto router = i2p::transport::transports.RoutesRestricted() ?
i2p::transport::transports.GetRestrictedPeer() :
// 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) {
LogPrint (eLogWarning, "Tunnel: Can't find any router, skip creating tunnel");
return;

5
libi2pd/Tunnel.h

@ -232,8 +232,9 @@ namespace tunnel @@ -232,8 +232,9 @@ namespace tunnel
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<OutboundTunnel> tunnel);
std::shared_ptr<TunnelPool> CreateTunnelPool (int numInboundHops, int numOuboundHops,
int numInboundTunnels, int numOutboundTunnels, int inboundVariance, int outboundVariance);
std::shared_ptr<TunnelPool> CreateTunnelPool (int numInboundHops,
int numOuboundHops, int numInboundTunnels, int numOutboundTunnels,
int inboundVariance, int outboundVariance, bool isHighBandwidth);
void DeleteTunnelPool (std::shared_ptr<TunnelPool> pool);
void StopTunnelPool (std::shared_ptr<TunnelPool> pool);

20
libi2pd/TunnelPool.cpp

@ -41,11 +41,11 @@ namespace tunnel @@ -41,11 +41,11 @@ namespace tunnel
}
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_NumInboundTunnels (numInboundTunnels), m_NumOutboundTunnels (numOutboundTunnels),
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)
{
if (m_NumInboundTunnels > TUNNEL_POOL_MAX_INBOUND_TUNNELS_QUANTITY)
@ -549,20 +549,22 @@ namespace tunnel @@ -549,20 +549,22 @@ namespace tunnel
std::shared_ptr<const i2p::data::RouterInfo> TunnelPool::SelectNextHop (std::shared_ptr<const i2p::data::RouterInfo> prevHop,
bool reverse, bool endpoint) const
{
bool tryHighBandwidth = !IsExploratory ();
bool tryClient = !IsExploratory () && !i2p::context.IsLimitedConnectivity ();
std::shared_ptr<const i2p::data::RouterInfo> hop;
for (int i = 0; i < TUNNEL_POOL_MAX_HOP_SELECTION_ATTEMPTS; i++)
{
hop = tryHighBandwidth ?
i2p::data::netdb.GetHighBandwidthRandomRouter (prevHop, reverse, endpoint) :
i2p::data::netdb.GetRandomRouter (prevHop, reverse, endpoint);
hop = tryClient ?
(m_IsHighBandwidth ?
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->GetProfile ()->IsBad ())
break;
}
else if (tryHighBandwidth)
tryHighBandwidth = false;
else if (tryClient)
tryClient = false;
else
return nullptr;
}
@ -585,7 +587,7 @@ namespace tunnel @@ -585,7 +587,7 @@ namespace tunnel
else if (i2p::transport::transports.GetNumPeers () > 100 ||
(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 () &&
(numHops > 1 || (r->IsV4 () && (!inbound || r->IsPublished (true))))) // first inbound must be published ipv4
{

4
libi2pd/TunnelPool.h

@ -62,7 +62,7 @@ namespace tunnel @@ -62,7 +62,7 @@ namespace tunnel
public:
TunnelPool (int numInboundHops, int numOutboundHops, int numInboundTunnels,
int numOutboundTunnels, int inboundVariance, int outboundVariance);
int numOutboundTunnels, int inboundVariance, int outboundVariance, bool isHighBandwidth);
~TunnelPool ();
std::shared_ptr<i2p::garlic::GarlicDestination> GetLocalDestination () const { return m_LocalDestination; };
@ -146,7 +146,7 @@ namespace tunnel @@ -146,7 +146,7 @@ namespace tunnel
std::set<std::shared_ptr<OutboundTunnel>, TunnelCreationTimeCmp> m_OutboundTunnels;
mutable std::mutex m_TestsMutex;
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
std::mutex m_CustomPeerSelectorMutex;
ITunnelPeerSelector * m_CustomPeerSelector;

12
libi2pd/util.h

@ -1,5 +1,5 @@ @@ -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
*
@ -131,6 +131,14 @@ namespace util @@ -131,6 +131,14 @@ namespace util
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>
void ReleaseMt(const C<T *, R...>& c)
{
@ -138,7 +146,7 @@ namespace util @@ -138,7 +146,7 @@ namespace util
for (auto& it: c)
this->Release (it);
}
template<typename... TArgs>
std::shared_ptr<T> AcquireSharedMt (TArgs&&... args)
{

6
libi2pd/version.h

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

35
libi2pd_client/AddressBook.cpp

@ -1,5 +1,5 @@ @@ -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
*
@ -15,7 +15,6 @@ @@ -15,7 +15,6 @@
#include <condition_variable>
#include <openssl/rand.h>
#include <boost/algorithm/string.hpp>
#include <boost/filesystem.hpp>
#include "Base.h"
#include "util.h"
#include "Identity.h"
@ -27,6 +26,14 @@ @@ -27,6 +26,14 @@
#include "AddressBook.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 client
@ -266,11 +273,11 @@ namespace client @@ -266,11 +273,11 @@ namespace client
void AddressBookFilesystemStorage::ResetEtags ()
{
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;
boost::filesystem::remove (it->path ());
fs_lib::remove (it->path ());
}
}
@ -434,7 +441,7 @@ namespace client @@ -434,7 +441,7 @@ namespace client
auto ident = std::make_shared<i2p::data::IdentityEx>();
if (ident->FromBase64 (jump))
{
m_Storage->AddAddress (ident);
if (m_Storage) m_Storage->AddAddress (ident);
m_Addresses[address] = std::make_shared<Address>(ident->GetIdentHash ());
LogPrint (eLogInfo, "Addressbook: Added ", address," -> ", ToAddress(ident->GetIdentHash ()));
}
@ -445,18 +452,19 @@ namespace client @@ -445,18 +452,19 @@ namespace client
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)
{
auto addr = GetAddress (address);
if (!addr || !addr->IsIdentHash ()) return nullptr;
return m_Storage->GetAddress (addr->identHash);
return m_Storage ? m_Storage->GetAddress (addr->identHash) : nullptr;
}
void AddressBook::LoadHosts ()
{
if (!m_Storage) return;
if (m_Storage->Load (m_Addresses) > 0)
{
m_IsLoaded = true;
@ -527,15 +535,18 @@ namespace client @@ -527,15 +535,18 @@ namespace client
ident->GetSigningKeyType () != i2p::data::SIGNING_KEY_TYPE_DSA_SHA1) // don't replace by DSA
{
it->second->identHash = ident->GetIdentHash ();
m_Storage->AddAddress (ident);
m_Storage->RemoveAddress (it->second->identHash);
if (m_Storage)
{
m_Storage->AddAddress (ident);
m_Storage->RemoveAddress (it->second->identHash);
}
LogPrint (eLogInfo, "Addressbook: Updated host: ", name);
}
}
else
{
m_Addresses.emplace (name, std::make_shared<Address>(ident->GetIdentHash ()));
m_Storage->AddAddress (ident);
if (m_Storage) m_Storage->AddAddress (ident);
if (is_update)
LogPrint (eLogInfo, "Addressbook: Added new host: ", name);
}
@ -547,7 +558,7 @@ namespace client @@ -547,7 +558,7 @@ namespace client
if (numAddresses > 0)
{
if (!incomplete) m_IsLoaded = true;
m_Storage->Save (m_Addresses);
if (m_Storage) m_Storage->Save (m_Addresses);
}
return !incomplete;
}

5
libi2pd_client/AddressBook.h

@ -1,5 +1,5 @@ @@ -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
*
@ -99,7 +99,8 @@ namespace client @@ -99,7 +99,8 @@ namespace client
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 IsEnabled () const { return m_IsEnabled; }
private:
void StartSubscriptions ();

13
libi2pd_client/ClientContext.cpp

@ -474,8 +474,9 @@ namespace client @@ -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_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_PROFILE] = GetI2CPOption(section, I2CP_PARAM_STREAMING_PROFILE, DEFAULT_STREAMING_PROFILE);
options[I2CP_PARAM_LEASESET_TYPE] = GetI2CPOption(section, I2CP_PARAM_LEASESET_TYPE, DEFAULT_LEASESET_TYPE);
std::string encType = GetI2CPStringOption(section, I2CP_PARAM_LEASESET_ENCRYPTION_TYPE, "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;
std::string privKey = GetI2CPStringOption(section, I2CP_PARAM_LEASESET_PRIV_KEY, "");
if (privKey.length () > 0) options[I2CP_PARAM_LEASESET_PRIV_KEY] = privKey;
@ -519,6 +520,8 @@ namespace client @@ -519,6 +520,8 @@ namespace client
options[I2CP_PARAM_LEASESET_ENCRYPTION_TYPE] = value;
if (i2p::config::GetOption(prefix + I2CP_PARAM_LEASESET_PRIV_KEY, value) && !value.empty ())
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 ()
@ -663,7 +666,9 @@ namespace client @@ -663,7 +666,9 @@ namespace client
// http proxy
std::string outproxy = section.second.get("outproxy", "");
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;
clientEndpoint = tun->GetLocalEndpoint ();
}
@ -879,6 +884,7 @@ namespace client @@ -879,6 +884,7 @@ namespace client
uint16_t httpProxyPort; i2p::config::GetOption("httpproxy.port", httpProxyPort);
std::string httpOutProxyURL; i2p::config::GetOption("httpproxy.outproxy", httpOutProxyURL);
bool httpAddresshelper; i2p::config::GetOption("httpproxy.addresshelper", httpAddresshelper);
bool httpSendUserAgent; i2p::config::GetOption("httpproxy.senduseragent", httpSendUserAgent);
if (httpAddresshelper)
i2p::config::GetOption("addressbook.enabled", httpAddresshelper); // addresshelper is not supported without address book
i2p::data::SigningKeyType sigType; i2p::config::GetOption("httpproxy.signaturetype", sigType);
@ -898,7 +904,8 @@ namespace client @@ -898,7 +904,8 @@ namespace client
}
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();
}
catch (std::exception& e)

46
libi2pd_client/HTTPProxy.cpp

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

3
libi2pd_client/I2CP.cpp

@ -226,7 +226,8 @@ namespace client @@ -226,7 +226,8 @@ namespace client
leases = remote->GetNonExpiredLeases (true); // with threshold
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);
outboundTunnel = GetTunnelPool ()->GetNextOutboundTunnel (nullptr,
leaseRouter ? leaseRouter->GetCompatibleTransports (false) : (i2p::data::RouterInfo::CompatibleTransports)i2p::data::RouterInfo::eAllTransports);

2
libi2pd_client/SOCKS.cpp

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

4
tests/Makefile

@ -1,6 +1,6 @@ @@ -1,6 +1,6 @@
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
LIBI2PD = ../libi2pd.a
@ -18,7 +18,7 @@ ifneq (, $(findstring mingw, $(SYS))$(findstring windows-gnu, $(SYS))$(findstrin @@ -18,7 +18,7 @@ ifneq (, $(findstring mingw, $(SYS))$(findstring windows-gnu, $(SYS))$(findstrin
endif
LDLIBS = \
-lboost_filesystem$(BOOST_SUFFIX) \
-lboost_system$(BOOST_SUFFIX) \
-lboost_program_options$(BOOST_SUFFIX) \
-lssl \
-lcrypto \

Loading…
Cancel
Save