mirror of
https://github.com/PurpleI2P/i2pd.git
synced 2025-01-22 13:27:17 +01:00
commit
ecc635e9bc
19
.github/workflows/build-windows.yml
vendored
19
.github/workflows/build-windows.yml
vendored
|
@ -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:
|
|||
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
38
ChangeLog
|
@ -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
20
Makefile.bsd
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
|||
# 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)
|
|||
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
|
||||
|
|
|
@ -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)
|
|||
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)
|
|||
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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|||
}
|
||||
}
|
||||
}
|
||||
#if (__cplusplus >= 201703L) // C++ 17 or higher
|
||||
[[fallthrough]];
|
||||
#endif
|
||||
}
|
||||
case WM_TRAYICON:
|
||||
{
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
|
|
@ -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)
|
|||
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()
|
|||
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)
|
|||
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}")
|
|||
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}")
|
||||
|
|
|
@ -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)
|
|||
|
||||
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>
|
||||
|
|
|
@ -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
|
|||
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 || \
|
|||
|
||||
|
||||
%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
|
||||
|
||||
|
|
|
@ -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
|
|||
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 || \
|
|||
|
||||
|
||||
%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
|
||||
|
||||
|
|
|
@ -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
6
debian/changelog
vendored
|
@ -1,3 +1,9 @@
|
|||
i2pd (2.54.0-1) unstable; urgency=medium
|
||||
|
||||
* updated to version 2.54.0/0.9.64
|
||||
|
||||
-- orignal <orignal@i2pmail.org> Sun, 6 Oct 2024 16:00:00 +0000
|
||||
|
||||
i2pd (2.53.1-1) unstable; urgency=medium
|
||||
|
||||
* updated to version 2.53.1
|
||||
|
|
|
@ -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 {
|
|||
("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");
|
||||
|
|
|
@ -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
|
|||
{
|
||||
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];
|
||||
|
|
|
@ -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
|
|||
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
|
|||
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
|
|||
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
|
|||
}
|
||||
}
|
||||
// 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
|
|||
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) {
|
||||
|
|
|
@ -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
|
|||
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>
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
120
libi2pd/FS.cpp
|
@ -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 @@
|
|||
#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 {
|
|||
|
||||
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 {
|
|||
}
|
||||
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 {
|
|||
}
|
||||
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 {
|
|||
}
|
||||
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 {
|
|||
#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 {
|
|||
}
|
||||
|
||||
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 {
|
|||
}
|
||||
|
||||
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 {
|
|||
}
|
||||
|
||||
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 {
|
|||
}
|
||||
|
||||
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 {
|
|||
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 {
|
|||
|
||||
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 {
|
|||
|
||||
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
12
libi2pd/FS.h
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2013-2020, The PurpleI2P Project
|
||||
* Copyright (c) 2013-2024, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
|
@ -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;
|
||||
|
|
|
@ -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
|
|||
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
|
|||
}
|
||||
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
|
|||
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:
|
||||
{
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|||
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
|
|||
}
|
||||
}
|
||||
|
||||
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
|
|||
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
|
|||
/* 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
|
|||
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
|
|||
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
|
|||
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
|
|||
}
|
||||
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
|
|||
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
|
|||
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
|
|||
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
|
|||
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;
|
||||
|
|
|
@ -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 @@
|
|||
#include <list>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
namespace i2p
|
||||
|
@ -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
|
|||
{
|
||||
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
|
|||
* @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
|
|||
* @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
|
|||
* @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
|
||||
|
|
|
@ -107,7 +107,6 @@ namespace i2p
|
|||
|
||||
enum I2NPMessageType
|
||||
{
|
||||
eI2NPDummyMsg = 0,
|
||||
eI2NPDatabaseStore = 1,
|
||||
eI2NPDatabaseLookup = 2,
|
||||
eI2NPDatabaseSearchReply = 3,
|
||||
|
|
|
@ -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
|
|||
|
||||
// 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
|
|||
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 ());
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
|||
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
|
|||
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
|
|||
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:
|
||||
|
|
|
@ -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
|
|||
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
|
|||
{
|
||||
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
|
|||
|
||||
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
|
|||
}
|
||||
|
||||
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
|
|||
{
|
||||
// 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
|
|||
(!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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
|||
}
|
||||
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); };
|
||||
|
|
|
@ -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
|
|||
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
|
|||
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
|
|||
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;
|
||||
}
|
||||
|
|
|
@ -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
|
|||
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
|
||||
|
|
|
@ -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
|
|||
|
||||
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
|
|||
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
|
|||
{
|
||||
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
|
|||
}
|
||||
|
||||
RouterInfo::RouterInfo (const uint8_t * buf, size_t len):
|
||||
RouterInfo (std::make_shared<Buffer> (buf, len), len)
|
||||
RouterInfo (netdb.NewRouterInfoBuffer (buf, len), len)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -439,10 +439,10 @@ namespace data
|
|||
}
|
||||
m_ReachableTransports |= m_PublishedTransports;
|
||||
// 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
|
|||
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
|
|||
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
|
|||
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
|
|||
{
|
||||
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
|
|||
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
|
|||
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
|
|||
{
|
||||
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
|
|||
{
|
||||
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
|
|||
{
|
||||
m_SupportedTransports &= ~eNTCP2V6Mesh;
|
||||
m_ReachableTransports &= ~eNTCP2V6Mesh;
|
||||
(*m_Addresses)[eNTCP2V6MeshIdx].reset ();
|
||||
(*GetAddresses ())[eNTCP2V6MeshIdx].reset ();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
|||
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
|
|||
|
||||
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
|
|||
{
|
||||
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
|
|||
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
|
|||
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
|
|||
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
|
|||
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
|
|||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
|||
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
|
|||
};
|
||||
|
||||
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
|
|||
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
|
|||
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
|
|||
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
|
|||
|
||||
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
|
|||
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
404
libi2pd/SSU2.cpp
|
@ -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
|
|||
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
|
|||
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
|
|||
}
|
||||
}
|
||||
|
||||
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
|
|||
if (session)
|
||||
{
|
||||
m_Sessions.emplace (session->GetConnID (), session);
|
||||
AddSessionByRouterHash (session);
|
||||
if (session->GetState () != eSSU2SessionStatePeerTest)
|
||||
AddSessionByRouterHash (session);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -434,19 +478,28 @@ namespace transport
|
|||
auto it = m_Sessions.find (connID);
|
||||
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
|
|||
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
|
|||
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
|
|||
|
||||
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
|
|||
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
|
|||
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
|
|||
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
|
|||
{
|
||||
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
|
|||
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
|
|||
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
|
|||
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
|
|||
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
|
|||
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
|
|||
}
|
||||
|
||||
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
|
|||
{
|
||||
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)
|
||||
|
|
|
@ -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
|
|||
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
|
|||
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
|
|||
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
|
|||
|
||||
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
|
|||
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
|
|||
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
|
|||
|
||||
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
|
|||
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
|
|||
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];
|
||||
|
|
|
@ -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
|
|||
}
|
||||
|
||||
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
|
|||
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
|
|||
else
|
||||
{
|
||||
// incoming
|
||||
InitNoiseXKState1 (*m_NoiseState, i2p::context.GetSSU2StaticPublicKey ());
|
||||
if (noise)
|
||||
InitNoiseXKState1 (*m_NoiseState, i2p::context.GetSSU2StaticPublicKey ());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -161,40 +170,47 @@ namespace transport
|
|||
if (!session || !relayTag) return false;
|
||||
// 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
|
|||
{
|
||||
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
|
|||
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
|
|||
{
|
||||
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
|
|||
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
|
|||
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
|
|||
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
|
|||
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
|
|||
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
|
|||
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
|
|||
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
|
|||
|
||||
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
|
|||
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
|
|||
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
|
|||
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
|
|||
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
|
|||
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
|
|||
}
|
||||
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
|
|||
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
|
|||
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
|
|||
{
|
||||
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
|
|||
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
|
|||
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
|
|||
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
|
|||
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
|
|||
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
|
|||
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++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -113,7 +113,6 @@ namespace transport
|
|||
eSSU2SessionStateFailed,
|
||||
eSSU2SessionStateIntroduced,
|
||||
eSSU2SessionStatePeerTest,
|
||||
eSSU2SessionStatePeerTestReceived, // 5 before 4
|
||||
eSSU2SessionStateTokenRequestReceived
|
||||
};
|
||||
|
||||
|
@ -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
|
|||
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
|
|||
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
|
|||
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
|
|||
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
|
|||
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
|
|||
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;
|
||||
|
|
|
@ -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
|
|||
}
|
||||
|
||||
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
|
|||
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
|
|||
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
|
|||
}
|
||||
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
|
|||
return false;
|
||||
}
|
||||
}
|
||||
if (immediateAckRequested)
|
||||
SendQuickAck ();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
m_RTT = (m_PrevRTTSample + rttSample) / 2;
|
||||
if (!m_IsWinDropped)
|
||||
{
|
||||
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
|
||||
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_PrevRTT > m_SlowRTT + m_Jitter) && (m_RTT > m_SlowRTT + m_Jitter) && !m_IsWinDropped) // Drop window if RTT grows too fast, late detection
|
||||
{
|
||||
if (m_LastWindowDropSize)
|
||||
m_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
|
||||
}
|
||||
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
|
|||
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
|
|||
// 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
|
|||
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
|
|||
}
|
||||
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
|
|||
{
|
||||
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
|
|||
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
|
|||
|
||||
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
|
|||
}
|
||||
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
|
|||
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
|
|||
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
|
|||
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
|
|||
return msg;
|
||||
}
|
||||
|
||||
uint32_t StreamingDestination::GetRandom ()
|
||||
{
|
||||
if (m_Owner)
|
||||
{
|
||||
auto pool = m_Owner->GetTunnelPool ();
|
||||
if (pool)
|
||||
return pool->GetRng ()();
|
||||
}
|
||||
return rand ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
|||
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
|
|||
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
|
|||
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
|
|||
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
|
|||
|
||||
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
|
|||
|
||||
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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
|||
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
|
|||
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
|
|||
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;
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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
|
|||
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
|
|||
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
|
||||
{
|
||||
|
|
|
@ -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
|
|||
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;
|
||||
|
|
|
@ -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
|
|||
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
|
|||
for (auto& it: c)
|
||||
this->Release (it);
|
||||
}
|
||||
|
||||
|
||||
template<typename... TArgs>
|
||||
std::shared_ptr<T> AcquireSharedMt (TArgs&&... args)
|
||||
{
|
||||
|
|
|
@ -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 @@
|
|||
|
||||
#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)
|
||||
|
|
|
@ -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 @@
|
|||
#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 @@
|
|||
#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
|
|||
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
|
|||
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
|
|||
|
||||
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
|
|||
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
|
|||
if (numAddresses > 0)
|
||||
{
|
||||
if (!incomplete) m_IsLoaded = true;
|
||||
m_Storage->Save (m_Addresses);
|
||||
if (m_Storage) m_Storage->Save (m_Addresses);
|
||||
}
|
||||
return !incomplete;
|
||||
}
|
||||
|
|
|
@ -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
|
|||
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 ();
|
||||
|
|
|
@ -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
|
|||
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
|
|||
// 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
|
|||
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
|
|||
}
|
||||
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)
|
||||
|
|
|
@ -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 {
|
|||
"</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 {
|
|||
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 {
|
|||
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 {
|
|||
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 {
|
|||
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 {
|
|||
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 {
|
|||
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 {
|
|||
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 {
|
|||
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 {
|
|||
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 {
|
|||
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 {
|
|||
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 {
|
|||
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)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
|||
{
|
||||
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 {
|
|||
|
||||
std::string m_Name;
|
||||
std::string m_OutproxyUrl;
|
||||
bool m_Addresshelper;
|
||||
bool m_Addresshelper, m_SendUserAgent;
|
||||
};
|
||||
} // http
|
||||
} // i2p
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
|||
endif
|
||||
|
||||
LDLIBS = \
|
||||
-lboost_filesystem$(BOOST_SUFFIX) \
|
||||
-lboost_system$(BOOST_SUFFIX) \
|
||||
-lboost_program_options$(BOOST_SUFFIX) \
|
||||
-lssl \
|
||||
-lcrypto \
|
||||
|
|
Loading…
Reference in a new issue