diff --git a/.editorconfig b/.editorconfig index e1f1243a..0f57a84a 100644 --- a/.editorconfig +++ b/.editorconfig @@ -34,3 +34,6 @@ trim_trailing_whitespace = false [*.yml] indent_style = space indent_size = 2 + +[*.patch] +trim_trailing_whitespace = false diff --git a/.github/workflows/build-deb.yml b/.github/workflows/build-deb.yml index ebc3df4d..124dd35e 100644 --- a/.github/workflows/build-deb.yml +++ b/.github/workflows/build-deb.yml @@ -1,6 +1,24 @@ name: Build Debian packages -on: [push, pull_request] +on: + push: + branches: + - '*' + paths: + - .github/workflows/build-deb.yml + - contrib/** + - daemon/** + - debian/** + - i18n/** + - libi2pd/** + - libi2pd_client/** + - Makefile + - Makefile.linux + tags: + - '*' + pull_request: + branches: + - '*' jobs: build: diff --git a/.github/workflows/build-freebsd.yml b/.github/workflows/build-freebsd.yml index 76b496ad..f0296d7e 100644 --- a/.github/workflows/build-freebsd.yml +++ b/.github/workflows/build-freebsd.yml @@ -1,10 +1,28 @@ name: Build on FreeBSD -on: [push, pull_request] +on: + push: + branches: + - '*' + paths: + - .github/workflows/build-freebsd.yml + - build/CMakeLists.txt + - build/cmake_modules/** + - daemon/** + - i18n/** + - libi2pd/** + - libi2pd_client/** + - Makefile + - Makefile.homebrew + tags: + - '*' + pull_request: + branches: + - '*' jobs: build: - runs-on: macos-12 + runs-on: ubuntu-latest name: with UPnP steps: @@ -13,7 +31,7 @@ jobs: - name: Test in FreeBSD id: test - uses: vmactions/freebsd-vm@v0.3.0 + uses: vmactions/freebsd-vm@v1 with: usesh: true mem: 2048 diff --git a/.github/workflows/build-osx.yml b/.github/workflows/build-osx.yml index 266f2c54..1ae1042a 100644 --- a/.github/workflows/build-osx.yml +++ b/.github/workflows/build-osx.yml @@ -1,6 +1,22 @@ name: Build on OSX -on: [push, pull_request] +on: + push: + branches: + - '*' + paths: + - .github/workflows/build-osx.yml + - daemon/** + - i18n/** + - libi2pd/** + - libi2pd_client/** + - Makefile + - Makefile.homebrew + tags: + - '*' + pull_request: + branches: + - '*' jobs: build: diff --git a/.github/workflows/build-windows-msvc.yml b/.github/workflows/build-windows-msvc.yml deleted file mode 100644 index 356bb466..00000000 --- a/.github/workflows/build-windows-msvc.yml +++ /dev/null @@ -1,52 +0,0 @@ -name: Build on Windows with MSVC - -on: [push, pull_request] - -jobs: - build: - name: Build - runs-on: windows-latest - - strategy: - fail-fast: false - - steps: - - name: Checkout - uses: actions/checkout@v3 - with: - fetch-depth: 0 - - - name: Build and install zlib - run: | - powershell -Command "(Invoke-WebRequest -Uri https://raw.githubusercontent.com/r4sas/zlib.install/master/install.bat -OutFile install_zlib.bat)" - powershell -Command "(Get-Content install_zlib.bat) | Set-Content install_zlib.bat" # fixing line endings - set BUILD_TYPE=Debug - ./install_zlib.bat - set BUILD_TYPE=Release - ./install_zlib.bat - del install_zlib.bat - - - name: Install Boost - uses: crazy-max/ghaction-chocolatey@v2 - with: - args: install boost-msvc-14.3 - - - name: Install OpenSSL - uses: crazy-max/ghaction-chocolatey@v2 - with: - args: install openssl - - - name: Configure - working-directory: build - run: cmake -DWITH_STATIC=ON . - - - name: Build - working-directory: build - run: cmake --build . --config Debug -- -m - - - name: Upload artifacts - uses: actions/upload-artifact@v3 - with: - name: i2pd-msvc - path: build/Debug/i2pd.* - diff --git a/.github/workflows/build-windows-msvc.yml-disabled b/.github/workflows/build-windows-msvc.yml-disabled new file mode 100644 index 00000000..0afb9d6b --- /dev/null +++ b/.github/workflows/build-windows-msvc.yml-disabled @@ -0,0 +1,80 @@ +name: Build on Windows with MSVC + +on: + push: + branches: + - '*' + paths: + - .github/workflows/build-windows-msvc.yml + - build/CMakeLists.txt + - build/cmake_modules/** + - daemon/** + - i18n/** + - libi2pd/** + - libi2pd_client/** + - Win32/** + tags: + - '*' + pull_request: + branches: + - '*' + +jobs: + build: + name: Build + runs-on: windows-latest + env: + boost_path: ${{ github.workspace }}\boost_1_83_0 + openssl_path: ${{ github.workspace }}\openssl_3_2_1 + + strategy: + fail-fast: false + + steps: + - name: Checkout + uses: actions/checkout@v3 + with: + fetch-depth: 0 + + - name: Build and install zlib + run: | + powershell -Command "(Invoke-WebRequest -Uri https://raw.githubusercontent.com/r4sas/zlib.install/master/install.bat -OutFile install_zlib.bat)" + powershell -Command "(Get-Content install_zlib.bat) | Set-Content install_zlib.bat" # fixing line endings + set BUILD_TYPE=Debug + ./install_zlib.bat + set BUILD_TYPE=Release + ./install_zlib.bat + del install_zlib.bat + + - name: Install Boost + run: | + powershell -Command "(Start-BitsTransfer -Source https://sourceforge.net/projects/boost/files/boost-binaries/1.83.0/boost_1_83_0-msvc-14.3-64.exe/download -Destination boost_1_83_0-msvc-14.3-64.exe)" + ./boost_1_83_0-msvc-14.3-64.exe /DIR="${{env.boost_path}}" /VERYSILENT /SUPPRESSMSGBOXES /SP- + + - name: Install OpenSSL + run: | + powershell -Command "(Start-BitsTransfer -Source https://slproweb.com/download/Win64OpenSSL-3_2_1.exe -Destination Win64OpenSSL-3_2_1.exe)" + ./Win64OpenSSL-3_2_1.exe /DIR="${{env.openssl_path}}" /TASKS="copytobin" /VERYSILENT /SUPPRESSMSGBOXES /NORESTART /SP- + + - name: Make copy of the OpenSSL libraries for CMake + run: | + dir ${{ github.workspace }} + dir ${{env.openssl_path}}\lib\VC + dir ${{env.openssl_path}}\lib\VC\x64\ + dir ${{env.openssl_path}}\lib\VC\x64\MTd\ + xcopy /s /y "${{env.openssl_path}}\lib\VC\x64\MTd" "${{env.openssl_path}}\lib" + + - name: Configure + working-directory: build + run: cmake -DBoost_ROOT="${{env.boost_path}}" -DOPENSSL_ROOT_DIR="${{env.openssl_path}}" -DWITH_STATIC=ON . + + - name: Build + working-directory: build + run: cmake --build . --config Debug -- -m + + - name: Upload artifacts + uses: actions/upload-artifact@v3 + with: + name: i2pd-msvc + path: build/Debug/i2pd.* + diff --git a/.github/workflows/build-windows.yml b/.github/workflows/build-windows.yml index 66c846c1..ed75dd69 100644 --- a/.github/workflows/build-windows.yml +++ b/.github/workflows/build-windows.yml @@ -1,6 +1,25 @@ name: Build on Windows -on: [push, pull_request] +on: + push: + branches: + - '*' + paths: + - .github/workflows/build-windows.yml + - build/CMakeLists.txt + - build/cmake_modules/** + - daemon/** + - i18n/** + - libi2pd/** + - libi2pd_client/** + - Win32/** + - Makefile + - Makefile.mingw + tags: + - '*' + pull_request: + branches: + - '*' defaults: run: diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 935c2f93..3f35cfe5 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,6 +1,24 @@ name: Build on Ubuntu -on: [push, pull_request] +on: + push: + branches: + - '*' + paths: + - .github/workflows/build.yml + - build/CMakeLists.txt + - build/cmake_modules/** + - daemon/** + - i18n/** + - libi2pd/** + - libi2pd_client/** + - Makefile + - Makefile.linux + tags: + - '*' + pull_request: + branches: + - '*' jobs: build-make: diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 28e4e8b0..41fe859e 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -5,6 +5,16 @@ on: branches: - openssl - docker + paths: + - .github/workflows/docker.yml + - contrib/docker/** + - contrib/certificates/** + - daemon/** + - i18n/** + - libi2pd/** + - libi2pd_client/** + - Makefile + - Makefile.linux tags: - '*' diff --git a/ChangeLog b/ChangeLog index b6005d4f..6534a9b5 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,6 +1,86 @@ # for this file format description, # see https://github.com/olivierlacan/keep-a-changelog +## [2.50.2] - 2024-01-06 +###Fixed +- Crash with OpenSSL 3.2.0 +- False positive clock skew detection + +## [2.50.1] - 2023-12-23 +###Fixed +- Support for new EdDSA usage behavior in OpenSSL 3.2.0 + +## [2.50.0] - 2023-12-18 +### Added +- Support of concurrent ACCEPTs on SAM 3.1 +- Haiku OS support +- Low bandwidth and far routers can expire before 1 hour +### Changed +- Don't pick too active peer for first hop +- Try peer test again if status is Unknown +- Send peer tests with random delay +- Reseeds list +### Fixed +- XSS vulnerability in addresshelper +- Publishing NAT64 ipv6 addresses +- Deadlock in AsyncSend callback + +## [2.49.0] - 2023-09-18 +### Added +- Handle SOCK5 authorization with empty user/password +- Drop incoming transport sessions from too old or from future routers +- Memory pool for router profiles +- Allow 0 hops in explicitPeers +### Changed +- Separate network and testing status +- Remove AVX code +- Improve NTCP2 transport session logging +- Select router with ipv4 for tunnel endpoint +- Consider all addresses non-published for U and H routers even if they have host/port +- Don't pick completely unreachable routers for tunnels +- Exclude SSU1 introducers from SSU2 addresses +- Don't create paired inbound tunnel if length is different +- Remove introducer from RouterInfo after 60 minutes +- Reduce SSU2 keep alive interval and add keep alive interval variance +- Don't pick too old sessions for introducer +### Fixed +- Version of the subnegotiation in user/password SOCKS5 response +- Send keepalive for existing session with introducer +- Buffer offset for EVP_EncryptFinal_ex() to include outlen +- Termination block size processing for transport sessions +- Crash if deleted BOB destination was shared between few BOB sessions +- Introducers with zero tag +- Padding for SSU2 path response + +## [2.48.0] - 2023-06-12 +### Added +- Allow user/password authentication method for SOCK5 proxy +- Publish reject all congestion cap 'G' if transit is not accepted +- 'critical' log level +- Print b32 on webconsole destination page +- Webconsole button to drop a remote LeaseSet +- limits.zombies param - minimum percentage of successfully created tunnels for routers cleanup +- Recognize real routers if successfully connected or responded to tunnel build request +### Changed +- Bypass slow transport sessions for first hop selection +- Limit AESNI inline asm to x86/x64 +- Create smaller I2NP packets if possible +- Make router unreachable if AEAD tag verification fails in SessionCreated +- Don't include a router to floodfills list until it's confirmed as real +- Drop LeaseSet store request if not floodfill +- Bypass medium congestion('D') routers for client tunnels +- Publish encrypted RouterInfo through tunnels +- Check if s is valid x25519 public key +- Check if socket is open before sending data in SSU2 +### Fixed +- Webconsole empty page if destination is not found +- i2p.streaming.answerPings param +- Reload tunnels +- Address caps for unspecified ipv6 address +- Incomplete HTTP headers in I2P tunnels +- SSU2 socket network exceptions on Windows +- Use of 'server' type tunnel port as inport (#1936) + ## [2.47.0] - 2023-03-11 ### Added - Congestion caps diff --git a/LICENSE b/LICENSE index 9a1e4527..93280084 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2013-2020, The PurpleI2P Project +Copyright (c) 2013-2023, The PurpleI2P Project All rights reserved. diff --git a/Makefile b/Makefile index 0861ec0b..3998beb0 100644 --- a/Makefile +++ b/Makefile @@ -67,6 +67,9 @@ else ifneq (, $(findstring linux, $(SYS))$(findstring gnu, $(SYS))) else ifneq (, $(findstring freebsd, $(SYS))$(findstring openbsd, $(SYS))) DAEMON_SRC += $(DAEMON_SRC_DIR)/UnixDaemon.cpp include Makefile.bsd +else ifneq (, $(findstring haiku, $(SYS))) + DAEMON_SRC += $(DAEMON_SRC_DIR)/UnixDaemon.cpp + include Makefile.haiku else # not supported $(error Not supported platform) endif @@ -118,7 +121,7 @@ obj/%.o: %.cpp | mk_obj_dir -include $(DEPS) $(I2PD): $(DAEMON_OBJS) $(ARLIB) $(ARLIB_CLIENT) $(ARLIB_LANG) - $(CXX) -o $@ $(DEFINES) $(LDFLAGS) $^ $(LDLIBS) + $(CXX) $(DEFINES) $(LDFLAGS) -o $@ $^ $(LDLIBS) $(SHLIB): $(LIB_OBJS) ifneq ($(USE_STATIC),yes) diff --git a/Makefile.haiku b/Makefile.haiku new file mode 100644 index 00000000..85c2835d --- /dev/null +++ b/Makefile.haiku @@ -0,0 +1,10 @@ +CXX = g++ +CXXFLAGS := -Wall -std=c++11 +INCFLAGS = -I/system/develop/headers +DEFINES = -D_DEFAULT_SOURCE -D_GNU_SOURCE +LDLIBS = -lbe -lbsd -lnetwork -lz -lcrypto -lssl -lboost_system -lboost_date_time -lboost_filesystem -lboost_program_options -lpthread + +ifeq ($(USE_UPNP),yes) + DEFINES += -DUSE_UPNP + LDLIBS += -lminiupnpc +endif diff --git a/Makefile.mingw b/Makefile.mingw index 6cd19080..38da225a 100644 --- a/Makefile.mingw +++ b/Makefile.mingw @@ -5,14 +5,11 @@ WINDRES = windres CXXFLAGS := $(CXX_DEBUG) -fPIC -msse INCFLAGS := -I$(DAEMON_SRC_DIR) -IWin32 -LDFLAGS := ${LD_DEBUG} -static +LDFLAGS := ${LD_DEBUG} -static -fPIC -msse NEEDED_CXXFLAGS += -std=c++17 DEFINES += -DWIN32_LEAN_AND_MEAN -# Boost libraries suffix -BOOST_SUFFIX = -mt - # UPNP Support ifeq ($(USE_UPNP),yes) DEFINES += -DUSE_UPNP -DMINIUPNP_STATICLIB @@ -20,17 +17,18 @@ ifeq ($(USE_UPNP),yes) endif LDLIBS += \ - -lboost_system$(BOOST_SUFFIX) \ - -lboost_date_time$(BOOST_SUFFIX) \ - -lboost_filesystem$(BOOST_SUFFIX) \ - -lboost_program_options$(BOOST_SUFFIX) \ - -lssl \ - -lcrypto \ - -lz \ + $(MINGW_PREFIX)/lib/libboost_system-mt.a \ + $(MINGW_PREFIX)/lib/libboost_date_time-mt.a \ + $(MINGW_PREFIX)/lib/libboost_filesystem-mt.a \ + $(MINGW_PREFIX)/lib/libboost_program_options-mt.a \ + $(MINGW_PREFIX)/lib/libssl.a \ + $(MINGW_PREFIX)/lib/libcrypto.a \ + $(MINGW_PREFIX)/lib/libz.a \ -lwsock32 \ -lws2_32 \ - -lgdi32 \ -liphlpapi \ + -lcrypt32 \ + -lgdi32 \ -lole32 \ -luuid \ -lpthread @@ -48,6 +46,7 @@ endif ifeq ($(USE_AESNI),yes) NEEDED_CXXFLAGS += -maes + LDFLAGS += -maes DEFINES += -D__AES__ endif diff --git a/Makefile.osx b/Makefile.osx index e069aaff..467c9fdd 100644 --- a/Makefile.osx +++ b/Makefile.osx @@ -5,7 +5,6 @@ DEFINES := -DMAC_OSX LDFLAGS := -Wl,-rpath,/usr/local/lib -L/usr/local/lib LDFLAGS += -Wl,-dead_strip LDFLAGS += -Wl,-dead_strip_dylibs -LDFLAGS += -Wl,-bind_at_load ifeq ($(USE_STATIC),yes) LDLIBS = -lz /usr/local/lib/libcrypto.a /usr/local/lib/libssl.a /usr/local/lib/libboost_system.a /usr/local/lib/libboost_date_time.a /usr/local/lib/libboost_filesystem.a /usr/local/lib/libboost_program_options.a -lpthread diff --git a/README.md b/README.md index d0481ed9..ce25c8f2 100644 --- a/README.md +++ b/README.md @@ -99,13 +99,23 @@ Current status: [![Crowdin](https://badges.crowdin.net/i2pd/localized.svg)](http Donations --------- -BTC: 3MDoGJW9TLMTCDGrR9bLgWXfm6sjmgy86f -LTC: LKQirrYrDeTuAPnpYq5y7LVKtywfkkHi59 -ETH: 0x9e5bac70d20d1079ceaa111127f4fb3bccce379d -DASH: Xw8YUrQpYzP9tZBmbjqxS3M97Q7v3vJKUF -ZEC: t1cTckLuXsr1dwVrK4NDzfhehss4NvMadAJ -GST: GbD2JSQHBHCKLa9WTHmigJRpyFgmBj4woG -XMR: 497pJc7X4xqKvcLBLpSUtRgWqMMyo24u4btCos3cak6gbMkpobgSU6492ztUcUBghyeHpYeczB55s38NpuHoH5WGNSPDRMH +**E-Mail**: ```i2porignal at yandex.com``` + +**BTC**: ```3MDoGJW9TLMTCDGrR9bLgWXfm6sjmgy86f``` + +**LTC**: ```LKQirrYrDeTuAPnpYq5y7LVKtywfkkHi59``` + +**ETH**: ```0x9e5bac70d20d1079ceaa111127f4fb3bccce379d``` + +**GST**: ```GbD2JSQHBHCKLa9WTHmigJRpyFgmBj4woG``` + +**DASH**: ```Xw8YUrQpYzP9tZBmbjqxS3M97Q7v3vJKUF``` + +**ZEC**: ```t1cTckLuXsr1dwVrK4NDzfhehss4NvMadAJ``` + +**ANC**: ```AQJYweYYUqM1nVfLqfoSMpUMfzxvS4Xd7z``` + +**XMR**: ```497pJc7X4xqKvcLBLpSUtRgWqMMyo24u4btCos3cak6gbMkpobgSU6492ztUcUBghyeHpYeczB55s38NpuHoH5WGNSPDRMH``` License ------- diff --git a/Win32/Win32App.cpp b/Win32/Win32App.cpp index 9eae7a95..9f750c4c 100644 --- a/Win32/Win32App.cpp +++ b/Win32/Win32App.cpp @@ -145,18 +145,19 @@ namespace win32 s << bytes << " Bytes\n"; } - static void ShowNetworkStatus (std::stringstream& s, RouterStatus status) + static void ShowNetworkStatus (std::stringstream& s, RouterStatus status, bool testing) { switch (status) { case eRouterStatusOK: s << "OK"; break; - case eRouterStatusTesting: s << "Test"; break; case eRouterStatusFirewalled: s << "FW"; break; case eRouterStatusUnknown: s << "Unk"; break; case eRouterStatusProxy: s << "Proxy"; break; case eRouterStatusMesh: s << "Mesh"; break; default: s << "Unk"; }; + if (testing) + s << " (Test)"; if (i2p::context.GetError () != eRouterErrorNone) { switch (i2p::context.GetError ()) @@ -179,11 +180,11 @@ namespace win32 { s << "\n"; s << "Status: "; - ShowNetworkStatus (s, i2p::context.GetStatus ()); + ShowNetworkStatus (s, i2p::context.GetStatus (), i2p::context.GetTesting ()); if (i2p::context.SupportsV6 ()) { s << " / "; - ShowNetworkStatus (s, i2p::context.GetStatusV6 ()); + ShowNetworkStatus (s, i2p::context.GetStatusV6 (), i2p::context.GetTestingV6 ()); } s << "; "; s << "Success Rate: " << i2p::tunnel::tunnels.GetTunnelCreationSuccessRate() << "%\n"; @@ -348,6 +349,9 @@ namespace win32 } } } +#if (__cplusplus >= 201703L) // C++ 17 or higher + [[fallthrough]]; +#endif } case WM_TRAYICON: { diff --git a/Win32/mask.bmp b/Win32/mask.bmp deleted file mode 100644 index cc2aeda7..00000000 Binary files a/Win32/mask.bmp and /dev/null differ diff --git a/build/CMakeLists.txt b/build/CMakeLists.txt index 05401b1f..232e427f 100644 --- a/build/CMakeLists.txt +++ b/build/CMakeLists.txt @@ -25,7 +25,7 @@ project( i2pd VERSION ${PROJECT_VERSION} HOMEPAGE_URL "https://i2pd.website/" - LANGUAGES CXX + LANGUAGES C CXX ) # configurable options @@ -121,7 +121,7 @@ if(WIN32) ) file(GLOB WIN32_RC ${WIN32_SRC_DIR}/*.rc) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DWIN32_APP -DWIN32_LEAN_AND_MEAN") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DWIN32_APP -DWIN32_LEAN_AND_MEAN -DNOMINMAX") endif() @@ -139,6 +139,10 @@ if(APPLE) add_definitions(-DMAC_OSX) endif() +if(HAIKU) + add_definitions(-D_DEFAULT_SOURCE -D_GNU_SOURCE) +endif() + if(MSVC) add_definitions(-DWINVER=0x0600) add_definitions(-D_WIN32_WINNT=0x0600) @@ -197,14 +201,11 @@ endif() # Note: AES-NI and AVX is available on x86-based CPU's. # Here also ARM64 implementation, but currently we don't support it. -# MSVC is not supported. -if(MSVC) - message(STATUS "AES-NI is not supported on MSVC, option was disabled") - set(WITH_AESNI OFF) -endif() - +# MSVC is not supported due to different ASM processing, so we hope OpenSSL has its own checks to run optimized code. if(WITH_AESNI AND (ARCHITECTURE MATCHES "x86_64" OR ARCHITECTURE MATCHES "i386")) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -maes") + if(NOT MSVC) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -maes") + endif() add_definitions(-D__AES__) endif() @@ -329,7 +330,11 @@ message(STATUS " LIBRARY : ${WITH_LIBRARY}") message(STATUS " BINARY : ${WITH_BINARY}") message(STATUS " STATIC BUILD : ${WITH_STATIC}") message(STATUS " UPnP : ${WITH_UPNP}") +if(WITH_GIT_VERSION) +message(STATUS " GIT VERSION : ${WITH_GIT_VERSION} (${GIT_VERSION})") +else() message(STATUS " GIT VERSION : ${WITH_GIT_VERSION}") +endif() message(STATUS " ADDRSANITIZER : ${WITH_ADDRSANITIZER}") message(STATUS " THREADSANITIZER : ${WITH_THREADSANITIZER}") message(STATUS "---------------------------------------") diff --git a/contrib/apparmor/docker-i2pd b/contrib/apparmor/docker-i2pd new file mode 100644 index 00000000..8e34298a --- /dev/null +++ b/contrib/apparmor/docker-i2pd @@ -0,0 +1,42 @@ +# _________________________________________ +# / Copy this file to the right location \ +# | then load with: | +# | | +# | apparmor_parser -r -W | +# | /etc/apparmor.d/docker-i2pd | +# | | +# | docker run --security-opt | +# | "apparmor=docker-i2pd" ... | +# | purplei2p/i2pd | +# | | +# \ And "aa-status" to verify it's loaded. / +# ----------------------------------------- +# \ ^__^ +# \ (oo)\_______ +# (__)\ )\/\ +# ||----w | +# || || + +#include + +profile docker-i2pd flags=(attach_disconnected,mediate_deleted) { + #include + #include + #include + + /bin/busybox ix, + /usr/local/bin/i2pd ix, + /entrypoint.sh ixr, + + /i2pd_certificates/** r, + + /home/i2pd/data/** rw, + + /home/i2pd/data/i2pd.pid k, + + deny /home/i2pd/data/i2pd.conf w, + deny /home/i2pd/data/tunnels.conf w, + deny /home/i2pd/data/tunnels.d/** w, + deny /home/i2pd/data/certificates/** w, + deny /home/i2pd/data/i2pd.log r, +} diff --git a/contrib/certificates/reseed/admin_at_stormycloud.org.crt b/contrib/certificates/reseed/admin_at_stormycloud.org.crt new file mode 100644 index 00000000..ae44521b --- /dev/null +++ b/contrib/certificates/reseed/admin_at_stormycloud.org.crt @@ -0,0 +1,34 @@ +-----BEGIN CERTIFICATE----- +MIIF1zCCA7+gAwIBAgIRAMDqFR09Xuj8ZUu+oetSvAEwDQYJKoZIhvcNAQELBQAw +dTELMAkGA1UEBhMCWFgxCzAJBgNVBAcTAlhYMQswCQYDVQQJEwJYWDEeMBwGA1UE +ChMVSTJQIEFub255bW91cyBOZXR3b3JrMQwwCgYDVQQLEwNJMlAxHjAcBgNVBAMM +FWFkbWluQHN0b3JteWNsb3VkLm9yZzAeFw0yNDAxMjUxNDE1MzBaFw0zNDAxMjUx +NDE1MzBaMHUxCzAJBgNVBAYTAlhYMQswCQYDVQQHEwJYWDELMAkGA1UECRMCWFgx +HjAcBgNVBAoTFUkyUCBBbm9ueW1vdXMgTmV0d29yazEMMAoGA1UECxMDSTJQMR4w +HAYDVQQDDBVhZG1pbkBzdG9ybXljbG91ZC5vcmcwggIiMA0GCSqGSIb3DQEBAQUA +A4ICDwAwggIKAoICAQDbGX+GikPzQXr9zvkrhfO9g0l49KHLNQhUKYqd6T+PfnGo +Fm0d3ZZVVQZ045vWgroOXDGGZZWxUIlb2inRaR2DF1TxN3pPYt59RgY9ZQ9+TL7o +isY91krCRygY8EcAmHIjlfZQ9dBVcL7CfyT0MYZA5Efee9+NDHSewTfQP9T2faIE +83Fcyd93a2mIHYjKUbJnojng/wgsy8srbsEuuTok4MIQmDj+B5nz+za2FgI0/ydh +srlMt4aGJF4/DIem9z9d0zBCOkwrmtFIzjNF1mOSA8ES4m5YnKA/y9rZlRidLPGu +prbXhPVnqHeOnHMz2QCw1wbVo504kl0bMqyEz2tVWsO9ep7iZoQs2xkFAEaegYNT +QLUpwVGlyuq3wXXwopFRffOSimGSazICwWI6j+K0pOtgefNJaWrqKYvtkj1SbK2L +LBNUIENz6VnB7KPRckuX6zxC8PpOiBK9BcftfO+xAz/wC6qq3riBPw30KKSym0nC +Zp5KciDn4Phtw9PGq8Bkl8SyWl0jtFnfTB1tzJkisf2qKcNHaFTEe2JW763YLbh/ +AU+8X8evFu40qLgvOgKoyy5DLy6i8zetX+3t9K0Fxt9+Vzzq6lm5V/RS8iIPPn+M +q1/3Z5kD0KQBG9h/Gl8BH+lB71ZxPAOZ3SMu8DJZcxBLVmDWqQPCr5CKnoz0swID +AQABo2IwYDAOBgNVHQ8BAf8EBAMCAoQwHQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsG +AQUFBwMBMA8GA1UdEwEB/wQFMAMBAf8wHgYDVR0OBBcEFWFkbWluQHN0b3JteWNs +b3VkLm9yZzANBgkqhkiG9w0BAQsFAAOCAgEARWOJ69vTHMneSXYscha+4Ytjg0RM +faewJNEGj8qy/Qvh9si2bWYNPRK6BlbHFS7pRYBLAnhaeLBGVv1CCR6GUMMe74zQ +UuMeAoWU6qMDmB3GfYoZJh8sIxpwHqyJeTdeccRbZ4sX4F6u3IHPXYiU/AgbYqH7 +pYXQg2lCjXZYaDFAlEf5SlYUDOhhXe5kR8Edhlrsu32/JzA1DQK0JjxKCBp+DQmA +ltdOpQtAg03fHP4ssdj7VvjIDl28iIlATwBvHrdNm7T0tYWn6TWhvxbRqvfTxfaH +MvxnPdIJwNP4/9TyQkwjwHb1h+ucho3CnxI/AxspdOvT1ElMhP6Ce6rcS9pk11Rl +x0ChsqpWwDg7KYpg0qZFSKCTBp4zBq9xoMJ6BQcgMfyl736WbsCzFTEyfifp8beg +NxUa/Qk7w7cuSPGyMIKNOmOR7FLlFbtocy8sXVsUQdqnp/edelufdNe39U9uNtY6 +yoXI9//Tc6NgOwy2Oyia0slZ5qHRkB7e4USXMRzJ3p4q9eCVKjAJs81Utp7O2U+9 +vhbhwWP8CAnNTT1E5WS6EKtfrdqF7wjkV+noPGLDGmrXi01J1fSMAjMfVO+7/LOL +UN+G4ybKWnEhhOO27yidN8Xx6UrCS23DBlPPQAeA74dTsTExiOxf1o1EXzcQiMyO +LAj3/Ojbi1xkWhI= +-----END CERTIFICATE----- diff --git a/contrib/certificates/reseed/ls_at_mail.i2p.crt b/contrib/certificates/reseed/ls_at_mail.i2p.crt new file mode 100644 index 00000000..8e0902d7 --- /dev/null +++ b/contrib/certificates/reseed/ls_at_mail.i2p.crt @@ -0,0 +1,32 @@ +-----BEGIN CERTIFICATE----- +MIIFdTCCA12gAwIBAgIEQ5vCxzANBgkqhkiG9w0BAQ0FADBrMQswCQYDVQQGEwJY +WDELMAkGA1UECAwCWFgxCzAJBgNVBAcMAlhYMR4wHAYDVQQKDBVJMlAgQW5vbnlt +b3VzIE5ldHdvcmsxDDAKBgNVBAsMA0kyUDEUMBIGA1UEAwwLbHNAbWFpbC5pMnAw +HhcNMjMxMDE2MjAwNTA5WhcNMzMxMDEzMjAwNTA5WjBrMQswCQYDVQQGEwJYWDEL +MAkGA1UECAwCWFgxCzAJBgNVBAcMAlhYMR4wHAYDVQQKDBVJMlAgQW5vbnltb3Vz +IE5ldHdvcmsxDDAKBgNVBAsMA0kyUDEUMBIGA1UEAwwLbHNAbWFpbC5pMnAwggIi +MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDPcbKRtf4PzrDa0iRit0XrwnmA +2c1fJhkBipdPor7gMOAlkR82H1lkZSizR7kTZnr7vYqjDrOQr7bl5Dy3qo8/YCbZ +jsnUCTIIgIJQUxUlR40RjaSXphqzUEiXKHR6b0RahhFisQ3hlbbgzSch5YgSLKws +hOLi+eDSXw+HlwHlWFlT1XOKxSTJ/F3Bv40gxqZVC2pbxiPOeRZHQ6Ojw75lxTSF +gww2WzgztiWt4X9BO1yepnVqhAVRPmTfGUMfKzq9jkMzZKeQFV4uZSP9nCqzEpYd +WNDUfpTWiAQ9F+BwFXGusXXA3tGVwS7s6IEoiJFM5fsoJYfRoWGh3/1eirhBXW7U +M6oubMSTADyrvfjLfJBMmMnc2hNblRlKr0ZKUjMfv8cnyT4kQxlXLAHHXY2P89TM +TEVODkU48gnv6tC4t1JCb1/Da+3yVMjNX6rCzQfUwnLFrWthrwiI0NivAKFtiZjq +w1/ZQcYke2YyeqcfXMn+NTUA22Sm2mJoMo7jUf+rbM9Pi27/DncJgRGj5qwY0D3S +gc7829EjuZNPttGBmae1EmO7WQMB32cqdmItnV2FXpMhnn9h0u5H52kYqwn+mdtc +dTJRcbfKG1RTr3UjFISaTwL8qigMIkVXIzcpnr/R/sSeEs8xCqfsJ6rb4dCyFx+M +hqQcOCL5tumyd4W/LQIDAQABoyEwHzAdBgNVHQ4EFgQUgfaOG5HCnlW82wZ5BahL +GRO06igwDQYJKoZIhvcNAQENBQADggIBAKdVpqS9qF7gGotgXaVA1iP5YNsWlTvG +daGqeA/87//U21W6gpq82FhzsmsvUtXZfIeVIlDPI7WNDzS+A3K/KKrwM7dLgSie +r9eMl3D8WYPU95QF4mAlRyl7PCCsYoVjyvfro0iq3/iudIA5476rjfLdTXRi5hAT +qemPj0S+6sRjKEldRtGXrQATFlvLIWVYpgHijdDDx5M2hAz2y0mFxlDZTlA4BhL4 +DwtGlVKmbc2x5MvIQM4UhbQqkxYS4gXnzf5Qx9QIytHfTr/hmbrkhKR1GCO31BSk +x9LhZxdI8LlwKSo6YgwXEB9E0M/tplaK9iZJFv4HPYLZrVJpb4IklMumyLMrgW5P +fR0dgKn+R9lk0emJ1Cu+qyyzf1vsLycYBwaEztINn4VK+/HfDFpnVCvJOyNuDmj5 +KBLIoGdGoVfylmnc+e8zAXe+DY41fgniHMISOO78P8Bx9vTB+rhqnOUr9MzlUxPB +sKGjbXy2YynEqiGb+9g344v/+ukTSDenqTPHVzJ5uOi0iedy+3ASzUNN6GJocovP +167VOhwaETM0FwiKe0VdZRLLbbZ79CtJC0tmgcgPQPRa9Ldr6KN7u1J3D6lUp6zl +byPom10ueKONRb36t7ai79l2SEUZRSMkx6AXIU0JJ1SMtQtav7b5LkpYJfdL7+vO +dDx2/Za0VmdD +-----END CERTIFICATE----- diff --git a/contrib/debian/README b/contrib/debian/README index cccbc4de..7dc3a61f 100644 --- a/contrib/debian/README +++ b/contrib/debian/README @@ -1,2 +1,5 @@ -This forder contain systemd unit files. -To use systemd daemon control, place files from this directory to debian folder before building package. +This forder contain files required for building debian packages. + +The trunk repository is contains the packaging files for the latest stable version of Debian (if we not forgot to update them). + +Files in subdirectories contains fixes to make possible to build package on specific versions of Debian/Ubuntu. They are used when building the release package. diff --git a/contrib/debian/bionic/compat b/contrib/debian/bionic/compat new file mode 100644 index 00000000..b4de3947 --- /dev/null +++ b/contrib/debian/bionic/compat @@ -0,0 +1 @@ +11 diff --git a/contrib/debian/bionic/control b/contrib/debian/bionic/control new file mode 100644 index 00000000..d872881c --- /dev/null +++ b/contrib/debian/bionic/control @@ -0,0 +1,18 @@ +Source: i2pd +Section: net +Priority: optional +Maintainer: r4sas +Build-Depends: debhelper (>= 11~), libboost-system-dev (>= 1.46), libboost-date-time-dev (>= 1.46), libboost-filesystem-dev (>= 1.46), libboost-program-options-dev (>= 1.46), libminiupnpc-dev, libssl-dev, zlib1g-dev +Standards-Version: 4.2.0 +Homepage: http://i2pd.website/ +Vcs-Git: git://github.com/PurpleI2P/i2pd.git +Vcs-Browser: https://github.com/PurpleI2P/i2pd + +Package: i2pd +Architecture: any +Pre-Depends: ${misc:Pre-Depends}, adduser +Depends: ${shlibs:Depends}, ${misc:Depends}, lsb-base, +Description: Full-featured C++ implementation of I2P client. + I2P (Invisible Internet Protocol) is a universal anonymous network layer. All + communications over I2P are anonymous and end-to-end encrypted, participants + don't reveal their real IP addresses. diff --git a/contrib/debian/trusty/compat b/contrib/debian/trusty/compat new file mode 100644 index 00000000..ec635144 --- /dev/null +++ b/contrib/debian/trusty/compat @@ -0,0 +1 @@ +9 diff --git a/contrib/debian/trusty/control b/contrib/debian/trusty/control new file mode 100644 index 00000000..fc618ddc --- /dev/null +++ b/contrib/debian/trusty/control @@ -0,0 +1,18 @@ +Source: i2pd +Section: net +Priority: optional +Maintainer: r4sas +Build-Depends: debhelper (>= 9), libboost-system-dev (>= 1.46), libboost-date-time-dev (>= 1.46), libboost-filesystem-dev (>= 1.46), libboost-program-options-dev (>= 1.46), libminiupnpc-dev, libssl-dev, zlib1g-dev +Standards-Version: 3.9.8 +Homepage: http://i2pd.website/ +Vcs-Git: git://github.com/PurpleI2P/i2pd.git +Vcs-Browser: https://github.com/PurpleI2P/i2pd + +Package: i2pd +Architecture: any +Pre-Depends: ${misc:Pre-Depends}, adduser +Depends: ${shlibs:Depends}, ${misc:Depends}, lsb-base, +Description: Full-featured C++ implementation of I2P client. + I2P (Invisible Internet Protocol) is a universal anonymous network layer. All + communications over I2P are anonymous and end-to-end encrypted, participants + don't reveal their real IP addresses. diff --git a/contrib/debian/trusty/patches/01-upnp.patch b/contrib/debian/trusty/patches/01-upnp.patch new file mode 100644 index 00000000..bec8f2b0 --- /dev/null +++ b/contrib/debian/trusty/patches/01-upnp.patch @@ -0,0 +1,17 @@ +Description: Enable UPnP usage in package +Author: r4sas + +Reviewed-By: r4sas +Last-Update: 2022-03-23 + +--- i2pd.orig/Makefile ++++ i2pd/Makefile +@@ -31,7 +31,7 @@ include filelist.mk + + USE_AESNI := $(or $(USE_AESNI),yes) + USE_STATIC := $(or $(USE_STATIC),no) +-USE_UPNP := $(or $(USE_UPNP),no) ++USE_UPNP := $(or $(USE_UPNP),yes) + DEBUG := $(or $(DEBUG),yes) + + # for debugging purposes only, when commit hash needed in trunk builds in i2pd version string diff --git a/contrib/debian/trusty/patches/02-service.patch b/contrib/debian/trusty/patches/02-service.patch new file mode 100644 index 00000000..12b35525 --- /dev/null +++ b/contrib/debian/trusty/patches/02-service.patch @@ -0,0 +1,19 @@ +Description: Disable LogsDirectory and LogsDirectoryMode options in service +Author: r4sas + +Reviewed-By: r4sas +Last-Update: 2023-05-17 + +--- a/contrib/i2pd.service ++++ b/contrib/i2pd.service +@@ -8,8 +8,8 @@ User=i2pd + Group=i2pd + RuntimeDirectory=i2pd + RuntimeDirectoryMode=0700 +-LogsDirectory=i2pd +-LogsDirectoryMode=0700 ++#LogsDirectory=i2pd ++#LogsDirectoryMode=0700 + Type=forking + ExecStart=/usr/sbin/i2pd --conf=/etc/i2pd/i2pd.conf --tunconf=/etc/i2pd/tunnels.conf --tunnelsdir=/etc/i2pd/tunnels.conf.d --pidfile=/run/i2pd/i2pd.pid --logfile=/var/log/i2pd/i2pd.log --daemon --service + ExecReload=/bin/sh -c "kill -HUP $MAINPID" diff --git a/contrib/debian/trusty/patches/series b/contrib/debian/trusty/patches/series new file mode 100644 index 00000000..d8caec41 --- /dev/null +++ b/contrib/debian/trusty/patches/series @@ -0,0 +1,2 @@ +01-upnp.patch +02-service.patch diff --git a/contrib/debian/trusty/rules b/contrib/debian/trusty/rules new file mode 100755 index 00000000..97a4e008 --- /dev/null +++ b/contrib/debian/trusty/rules @@ -0,0 +1,18 @@ +#!/usr/bin/make -f +#export DH_VERBOSE=1 + +export DEB_BUILD_MAINT_OPTIONS=hardening=+all + +include /usr/share/dpkg/architecture.mk + +ifeq ($(DEB_HOST_ARCH),i386) + export DEB_BUILD_OPTIONS=parallel=1 +endif + +export DEB_CXXFLAGS_MAINT_APPEND=-Wall -pedantic +export DEB_LDFLAGS_MAINT_APPEND= + +%: + dh $@ --parallel + +override_dh_auto_install: diff --git a/contrib/debian/xenial/compat b/contrib/debian/xenial/compat new file mode 100644 index 00000000..ec635144 --- /dev/null +++ b/contrib/debian/xenial/compat @@ -0,0 +1 @@ +9 diff --git a/contrib/debian/xenial/control b/contrib/debian/xenial/control new file mode 100644 index 00000000..fc618ddc --- /dev/null +++ b/contrib/debian/xenial/control @@ -0,0 +1,18 @@ +Source: i2pd +Section: net +Priority: optional +Maintainer: r4sas +Build-Depends: debhelper (>= 9), libboost-system-dev (>= 1.46), libboost-date-time-dev (>= 1.46), libboost-filesystem-dev (>= 1.46), libboost-program-options-dev (>= 1.46), libminiupnpc-dev, libssl-dev, zlib1g-dev +Standards-Version: 3.9.8 +Homepage: http://i2pd.website/ +Vcs-Git: git://github.com/PurpleI2P/i2pd.git +Vcs-Browser: https://github.com/PurpleI2P/i2pd + +Package: i2pd +Architecture: any +Pre-Depends: ${misc:Pre-Depends}, adduser +Depends: ${shlibs:Depends}, ${misc:Depends}, lsb-base, +Description: Full-featured C++ implementation of I2P client. + I2P (Invisible Internet Protocol) is a universal anonymous network layer. All + communications over I2P are anonymous and end-to-end encrypted, participants + don't reveal their real IP addresses. diff --git a/contrib/debian/xenial/patches/01-upnp.patch b/contrib/debian/xenial/patches/01-upnp.patch new file mode 100644 index 00000000..bec8f2b0 --- /dev/null +++ b/contrib/debian/xenial/patches/01-upnp.patch @@ -0,0 +1,17 @@ +Description: Enable UPnP usage in package +Author: r4sas + +Reviewed-By: r4sas +Last-Update: 2022-03-23 + +--- i2pd.orig/Makefile ++++ i2pd/Makefile +@@ -31,7 +31,7 @@ include filelist.mk + + USE_AESNI := $(or $(USE_AESNI),yes) + USE_STATIC := $(or $(USE_STATIC),no) +-USE_UPNP := $(or $(USE_UPNP),no) ++USE_UPNP := $(or $(USE_UPNP),yes) + DEBUG := $(or $(DEBUG),yes) + + # for debugging purposes only, when commit hash needed in trunk builds in i2pd version string diff --git a/contrib/debian/xenial/patches/02-service.patch b/contrib/debian/xenial/patches/02-service.patch new file mode 100644 index 00000000..12b35525 --- /dev/null +++ b/contrib/debian/xenial/patches/02-service.patch @@ -0,0 +1,19 @@ +Description: Disable LogsDirectory and LogsDirectoryMode options in service +Author: r4sas + +Reviewed-By: r4sas +Last-Update: 2023-05-17 + +--- a/contrib/i2pd.service ++++ b/contrib/i2pd.service +@@ -8,8 +8,8 @@ User=i2pd + Group=i2pd + RuntimeDirectory=i2pd + RuntimeDirectoryMode=0700 +-LogsDirectory=i2pd +-LogsDirectoryMode=0700 ++#LogsDirectory=i2pd ++#LogsDirectoryMode=0700 + Type=forking + ExecStart=/usr/sbin/i2pd --conf=/etc/i2pd/i2pd.conf --tunconf=/etc/i2pd/tunnels.conf --tunnelsdir=/etc/i2pd/tunnels.conf.d --pidfile=/run/i2pd/i2pd.pid --logfile=/var/log/i2pd/i2pd.log --daemon --service + ExecReload=/bin/sh -c "kill -HUP $MAINPID" diff --git a/contrib/debian/xenial/patches/series b/contrib/debian/xenial/patches/series new file mode 100644 index 00000000..d8caec41 --- /dev/null +++ b/contrib/debian/xenial/patches/series @@ -0,0 +1,2 @@ +01-upnp.patch +02-service.patch diff --git a/contrib/debian/xenial/rules b/contrib/debian/xenial/rules new file mode 100755 index 00000000..11791d9b --- /dev/null +++ b/contrib/debian/xenial/rules @@ -0,0 +1,13 @@ +#!/usr/bin/make -f +#export DH_VERBOSE=1 +export DEB_BUILD_MAINT_OPTIONS = hardening=+all + +include /usr/share/dpkg/architecture.mk + +export DEB_CXXFLAGS_MAINT_APPEND = -Wall -pedantic +export DEB_LDFLAGS_MAINT_APPEND = + +%: + dh $@ --parallel + +override_dh_auto_install: diff --git a/contrib/i18n/English.po b/contrib/i18n/English.po index 22010680..7782187f 100644 --- a/contrib/i18n/English.po +++ b/contrib/i18n/English.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: i2pd\n" "Report-Msgid-Bugs-To: https://github.com/PurpleI2P/i2pd/issues\n" -"POT-Creation-Date: 2023-01-19 04:18\n" +"POT-Creation-Date: 2023-06-10 01:25\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" @@ -18,28 +18,28 @@ msgstr "" "X-Poedit-SearchPath-0: daemon/HTTPServer.cpp\n" "X-Poedit-SearchPath-1: libi2pd_client/HTTPProxy.cpp\n" -#: daemon/HTTPServer.cpp:106 +#: daemon/HTTPServer.cpp:107 #, c-format msgid "%d day" msgid_plural "%d days" msgstr[0] "" msgstr[1] "" -#: daemon/HTTPServer.cpp:110 +#: daemon/HTTPServer.cpp:111 #, c-format msgid "%d hour" msgid_plural "%d hours" msgstr[0] "" msgstr[1] "" -#: daemon/HTTPServer.cpp:114 +#: daemon/HTTPServer.cpp:115 #, c-format msgid "%d minute" msgid_plural "%d minutes" msgstr[0] "" msgstr[1] "" -#: daemon/HTTPServer.cpp:117 +#: daemon/HTTPServer.cpp:118 #, c-format msgid "%d second" msgid_plural "%d seconds" @@ -47,560 +47,578 @@ msgstr[0] "" msgstr[1] "" #. tr: Kibibyte -#: daemon/HTTPServer.cpp:125 daemon/HTTPServer.cpp:153 +#: daemon/HTTPServer.cpp:126 #, c-format msgid "%.2f KiB" msgstr "" #. tr: Mebibyte -#: daemon/HTTPServer.cpp:127 +#: daemon/HTTPServer.cpp:128 #, c-format msgid "%.2f MiB" msgstr "" #. tr: Gibibyte -#: daemon/HTTPServer.cpp:129 +#: daemon/HTTPServer.cpp:130 #, c-format msgid "%.2f GiB" msgstr "" -#: daemon/HTTPServer.cpp:146 +#: daemon/HTTPServer.cpp:147 msgid "building" msgstr "" -#: daemon/HTTPServer.cpp:147 +#: daemon/HTTPServer.cpp:148 msgid "failed" msgstr "" -#: daemon/HTTPServer.cpp:148 +#: daemon/HTTPServer.cpp:149 msgid "expiring" msgstr "" -#: daemon/HTTPServer.cpp:149 +#: daemon/HTTPServer.cpp:150 msgid "established" msgstr "" -#: daemon/HTTPServer.cpp:150 +#: daemon/HTTPServer.cpp:151 msgid "unknown" msgstr "" -#: daemon/HTTPServer.cpp:152 +#: daemon/HTTPServer.cpp:153 msgid "exploratory" msgstr "" #. tr: Webconsole page title -#: daemon/HTTPServer.cpp:183 +#: daemon/HTTPServer.cpp:185 msgid "Purple I2P Webconsole" msgstr "" -#: daemon/HTTPServer.cpp:188 +#: daemon/HTTPServer.cpp:190 msgid "i2pd webconsole" msgstr "" -#: daemon/HTTPServer.cpp:191 +#: daemon/HTTPServer.cpp:193 msgid "Main page" msgstr "" -#: daemon/HTTPServer.cpp:192 daemon/HTTPServer.cpp:712 +#: daemon/HTTPServer.cpp:194 daemon/HTTPServer.cpp:742 msgid "Router commands" msgstr "" -#: daemon/HTTPServer.cpp:193 daemon/HTTPServer.cpp:387 -#: daemon/HTTPServer.cpp:399 +#: daemon/HTTPServer.cpp:195 daemon/HTTPServer.cpp:395 +#: daemon/HTTPServer.cpp:407 msgid "Local Destinations" msgstr "" -#: daemon/HTTPServer.cpp:195 daemon/HTTPServer.cpp:357 -#: daemon/HTTPServer.cpp:443 daemon/HTTPServer.cpp:449 -#: daemon/HTTPServer.cpp:609 daemon/HTTPServer.cpp:652 -#: daemon/HTTPServer.cpp:656 +#: daemon/HTTPServer.cpp:197 daemon/HTTPServer.cpp:365 +#: daemon/HTTPServer.cpp:454 daemon/HTTPServer.cpp:474 +#: daemon/HTTPServer.cpp:636 daemon/HTTPServer.cpp:682 +#: daemon/HTTPServer.cpp:686 msgid "LeaseSets" msgstr "" -#: daemon/HTTPServer.cpp:197 daemon/HTTPServer.cpp:662 +#: daemon/HTTPServer.cpp:199 daemon/HTTPServer.cpp:692 msgid "Tunnels" msgstr "" -#: daemon/HTTPServer.cpp:199 daemon/HTTPServer.cpp:364 -#: daemon/HTTPServer.cpp:781 daemon/HTTPServer.cpp:797 +#: daemon/HTTPServer.cpp:201 daemon/HTTPServer.cpp:372 +#: daemon/HTTPServer.cpp:813 daemon/HTTPServer.cpp:830 msgid "Transit Tunnels" msgstr "" -#: daemon/HTTPServer.cpp:201 daemon/HTTPServer.cpp:855 +#: daemon/HTTPServer.cpp:203 daemon/HTTPServer.cpp:898 msgid "Transports" msgstr "" -#: daemon/HTTPServer.cpp:202 +#: daemon/HTTPServer.cpp:204 msgid "I2P tunnels" msgstr "" -#: daemon/HTTPServer.cpp:204 daemon/HTTPServer.cpp:884 -#: daemon/HTTPServer.cpp:894 +#: daemon/HTTPServer.cpp:206 daemon/HTTPServer.cpp:927 +#: daemon/HTTPServer.cpp:937 msgid "SAM sessions" msgstr "" -#: daemon/HTTPServer.cpp:220 daemon/HTTPServer.cpp:1278 -#: daemon/HTTPServer.cpp:1281 daemon/HTTPServer.cpp:1284 -#: daemon/HTTPServer.cpp:1298 daemon/HTTPServer.cpp:1343 -#: daemon/HTTPServer.cpp:1346 daemon/HTTPServer.cpp:1349 +#: daemon/HTTPServer.cpp:222 daemon/HTTPServer.cpp:1329 +#: daemon/HTTPServer.cpp:1332 daemon/HTTPServer.cpp:1335 +#: daemon/HTTPServer.cpp:1362 daemon/HTTPServer.cpp:1365 +#: daemon/HTTPServer.cpp:1379 daemon/HTTPServer.cpp:1424 +#: daemon/HTTPServer.cpp:1427 daemon/HTTPServer.cpp:1430 msgid "ERROR" msgstr "" -#: daemon/HTTPServer.cpp:227 +#: daemon/HTTPServer.cpp:229 msgid "OK" msgstr "" -#: daemon/HTTPServer.cpp:228 +#: daemon/HTTPServer.cpp:230 msgid "Testing" msgstr "" -#: daemon/HTTPServer.cpp:229 +#: daemon/HTTPServer.cpp:231 msgid "Firewalled" msgstr "" -#: daemon/HTTPServer.cpp:230 daemon/HTTPServer.cpp:233 -#: daemon/HTTPServer.cpp:329 +#: daemon/HTTPServer.cpp:232 daemon/HTTPServer.cpp:235 +#: daemon/HTTPServer.cpp:336 msgid "Unknown" msgstr "" -#: daemon/HTTPServer.cpp:231 daemon/HTTPServer.cpp:374 -#: daemon/HTTPServer.cpp:375 daemon/HTTPServer.cpp:952 -#: daemon/HTTPServer.cpp:961 +#: daemon/HTTPServer.cpp:233 daemon/HTTPServer.cpp:382 +#: daemon/HTTPServer.cpp:383 daemon/HTTPServer.cpp:1003 +#: daemon/HTTPServer.cpp:1011 msgid "Proxy" msgstr "" -#: daemon/HTTPServer.cpp:232 +#: daemon/HTTPServer.cpp:234 msgid "Mesh" msgstr "" -#: daemon/HTTPServer.cpp:240 +#: daemon/HTTPServer.cpp:242 msgid "Clock skew" msgstr "" -#: daemon/HTTPServer.cpp:243 +#: daemon/HTTPServer.cpp:245 msgid "Offline" msgstr "" -#: daemon/HTTPServer.cpp:246 +#: daemon/HTTPServer.cpp:248 msgid "Symmetric NAT" msgstr "" -#: daemon/HTTPServer.cpp:249 +#: daemon/HTTPServer.cpp:251 msgid "Full cone NAT" msgstr "" -#: daemon/HTTPServer.cpp:252 +#: daemon/HTTPServer.cpp:254 msgid "No Descriptors" msgstr "" -#: daemon/HTTPServer.cpp:261 +#: daemon/HTTPServer.cpp:263 msgid "Uptime" msgstr "" -#: daemon/HTTPServer.cpp:264 +#: daemon/HTTPServer.cpp:266 msgid "Network status" msgstr "" -#: daemon/HTTPServer.cpp:269 +#: daemon/HTTPServer.cpp:271 msgid "Network status v6" msgstr "" -#: daemon/HTTPServer.cpp:275 daemon/HTTPServer.cpp:282 +#: daemon/HTTPServer.cpp:277 daemon/HTTPServer.cpp:284 msgid "Stopping in" msgstr "" -#: daemon/HTTPServer.cpp:289 +#: daemon/HTTPServer.cpp:291 msgid "Family" msgstr "" -#: daemon/HTTPServer.cpp:290 +#: daemon/HTTPServer.cpp:292 msgid "Tunnel creation success rate" msgstr "" -#: daemon/HTTPServer.cpp:291 +#: daemon/HTTPServer.cpp:296 +msgid "Total tunnel creation success rate" +msgstr "" + +#: daemon/HTTPServer.cpp:298 msgid "Received" msgstr "" #. tr: Kibibyte/s -#: daemon/HTTPServer.cpp:293 daemon/HTTPServer.cpp:296 -#: daemon/HTTPServer.cpp:299 +#: daemon/HTTPServer.cpp:300 daemon/HTTPServer.cpp:303 +#: daemon/HTTPServer.cpp:306 #, c-format msgid "%.2f KiB/s" msgstr "" -#: daemon/HTTPServer.cpp:294 +#: daemon/HTTPServer.cpp:301 msgid "Sent" msgstr "" -#: daemon/HTTPServer.cpp:297 +#: daemon/HTTPServer.cpp:304 msgid "Transit" msgstr "" -#: daemon/HTTPServer.cpp:300 +#: daemon/HTTPServer.cpp:307 msgid "Data path" msgstr "" -#: daemon/HTTPServer.cpp:303 +#: daemon/HTTPServer.cpp:310 msgid "Hidden content. Press on text to see." msgstr "" -#: daemon/HTTPServer.cpp:307 +#: daemon/HTTPServer.cpp:314 msgid "Router Ident" msgstr "" -#: daemon/HTTPServer.cpp:309 +#: daemon/HTTPServer.cpp:316 msgid "Router Family" msgstr "" -#: daemon/HTTPServer.cpp:310 +#: daemon/HTTPServer.cpp:317 msgid "Router Caps" msgstr "" -#: daemon/HTTPServer.cpp:311 +#: daemon/HTTPServer.cpp:318 msgid "Version" msgstr "" -#: daemon/HTTPServer.cpp:312 +#: daemon/HTTPServer.cpp:319 msgid "Our external address" msgstr "" #. tr: Shown when router doesn't publish itself and have "Firewalled" state -#: daemon/HTTPServer.cpp:341 +#: daemon/HTTPServer.cpp:349 msgid "supported" msgstr "" -#: daemon/HTTPServer.cpp:355 +#: daemon/HTTPServer.cpp:363 msgid "Routers" msgstr "" -#: daemon/HTTPServer.cpp:356 +#: daemon/HTTPServer.cpp:364 msgid "Floodfills" msgstr "" -#: daemon/HTTPServer.cpp:363 daemon/HTTPServer.cpp:938 +#: daemon/HTTPServer.cpp:371 daemon/HTTPServer.cpp:987 msgid "Client Tunnels" msgstr "" -#: daemon/HTTPServer.cpp:373 +#: daemon/HTTPServer.cpp:381 msgid "Services" msgstr "" -#: daemon/HTTPServer.cpp:374 daemon/HTTPServer.cpp:375 -#: daemon/HTTPServer.cpp:376 daemon/HTTPServer.cpp:377 -#: daemon/HTTPServer.cpp:378 daemon/HTTPServer.cpp:379 +#: daemon/HTTPServer.cpp:382 daemon/HTTPServer.cpp:383 +#: daemon/HTTPServer.cpp:384 daemon/HTTPServer.cpp:385 +#: daemon/HTTPServer.cpp:386 daemon/HTTPServer.cpp:387 msgid "Enabled" msgstr "" -#: daemon/HTTPServer.cpp:374 daemon/HTTPServer.cpp:375 -#: daemon/HTTPServer.cpp:376 daemon/HTTPServer.cpp:377 -#: daemon/HTTPServer.cpp:378 daemon/HTTPServer.cpp:379 +#: daemon/HTTPServer.cpp:382 daemon/HTTPServer.cpp:383 +#: daemon/HTTPServer.cpp:384 daemon/HTTPServer.cpp:385 +#: daemon/HTTPServer.cpp:386 daemon/HTTPServer.cpp:387 msgid "Disabled" msgstr "" -#: daemon/HTTPServer.cpp:422 +#: daemon/HTTPServer.cpp:434 msgid "Encrypted B33 address" msgstr "" -#: daemon/HTTPServer.cpp:431 +#: daemon/HTTPServer.cpp:442 msgid "Address registration line" msgstr "" -#: daemon/HTTPServer.cpp:436 +#: daemon/HTTPServer.cpp:447 msgid "Domain" msgstr "" -#: daemon/HTTPServer.cpp:437 +#: daemon/HTTPServer.cpp:448 msgid "Generate" msgstr "" -#: daemon/HTTPServer.cpp:438 +#: daemon/HTTPServer.cpp:449 msgid "" "Note: result string can be used only for registering 2LD domains " "(example.i2p). For registering subdomains please use i2pd-tools." msgstr "" -#: daemon/HTTPServer.cpp:444 +#: daemon/HTTPServer.cpp:457 msgid "Address" msgstr "" -#: daemon/HTTPServer.cpp:444 +#: daemon/HTTPServer.cpp:459 msgid "Type" msgstr "" -#: daemon/HTTPServer.cpp:444 +#: daemon/HTTPServer.cpp:460 msgid "EncType" msgstr "" -#: daemon/HTTPServer.cpp:454 daemon/HTTPServer.cpp:667 +#: daemon/HTTPServer.cpp:467 +msgid "Expire LeaseSet" +msgstr "" + +#: daemon/HTTPServer.cpp:479 daemon/HTTPServer.cpp:697 msgid "Inbound tunnels" msgstr "" #. tr: Milliseconds -#: daemon/HTTPServer.cpp:469 daemon/HTTPServer.cpp:489 -#: daemon/HTTPServer.cpp:681 daemon/HTTPServer.cpp:701 +#: daemon/HTTPServer.cpp:494 daemon/HTTPServer.cpp:514 +#: daemon/HTTPServer.cpp:711 daemon/HTTPServer.cpp:731 #, c-format msgid "%dms" msgstr "" -#: daemon/HTTPServer.cpp:474 daemon/HTTPServer.cpp:686 +#: daemon/HTTPServer.cpp:499 daemon/HTTPServer.cpp:716 msgid "Outbound tunnels" msgstr "" -#: daemon/HTTPServer.cpp:496 +#: daemon/HTTPServer.cpp:521 msgid "Tags" msgstr "" -#: daemon/HTTPServer.cpp:497 +#: daemon/HTTPServer.cpp:522 msgid "Incoming" msgstr "" -#: daemon/HTTPServer.cpp:504 daemon/HTTPServer.cpp:510 +#: daemon/HTTPServer.cpp:529 daemon/HTTPServer.cpp:535 msgid "Outgoing" msgstr "" -#: daemon/HTTPServer.cpp:507 daemon/HTTPServer.cpp:526 +#: daemon/HTTPServer.cpp:532 daemon/HTTPServer.cpp:551 msgid "Destination" msgstr "" -#: daemon/HTTPServer.cpp:507 +#: daemon/HTTPServer.cpp:532 daemon/HTTPServer.cpp:814 msgid "Amount" msgstr "" -#: daemon/HTTPServer.cpp:515 +#: daemon/HTTPServer.cpp:540 msgid "Incoming Tags" msgstr "" -#: daemon/HTTPServer.cpp:523 daemon/HTTPServer.cpp:529 +#: daemon/HTTPServer.cpp:548 daemon/HTTPServer.cpp:554 msgid "Tags sessions" msgstr "" -#: daemon/HTTPServer.cpp:526 +#: daemon/HTTPServer.cpp:551 msgid "Status" msgstr "" -#: daemon/HTTPServer.cpp:536 daemon/HTTPServer.cpp:594 +#: daemon/HTTPServer.cpp:561 daemon/HTTPServer.cpp:621 msgid "Local Destination" msgstr "" -#: daemon/HTTPServer.cpp:547 daemon/HTTPServer.cpp:917 +#: daemon/HTTPServer.cpp:572 daemon/HTTPServer.cpp:960 msgid "Streams" msgstr "" -#: daemon/HTTPServer.cpp:570 +#: daemon/HTTPServer.cpp:595 msgid "Close stream" msgstr "" -#: daemon/HTTPServer.cpp:599 +#: daemon/HTTPServer.cpp:613 daemon/HTTPServer.cpp:1430 +msgid "Such destination is not found" +msgstr "" + +#: daemon/HTTPServer.cpp:626 msgid "I2CP session not found" msgstr "" -#: daemon/HTTPServer.cpp:602 +#: daemon/HTTPServer.cpp:629 msgid "I2CP is not enabled" msgstr "" -#: daemon/HTTPServer.cpp:628 +#: daemon/HTTPServer.cpp:658 msgid "Invalid" msgstr "" -#: daemon/HTTPServer.cpp:631 +#: daemon/HTTPServer.cpp:661 msgid "Store type" msgstr "" -#: daemon/HTTPServer.cpp:632 +#: daemon/HTTPServer.cpp:662 msgid "Expires" msgstr "" -#: daemon/HTTPServer.cpp:637 +#: daemon/HTTPServer.cpp:667 msgid "Non Expired Leases" msgstr "" -#: daemon/HTTPServer.cpp:640 +#: daemon/HTTPServer.cpp:670 msgid "Gateway" msgstr "" -#: daemon/HTTPServer.cpp:641 +#: daemon/HTTPServer.cpp:671 msgid "TunnelID" msgstr "" -#: daemon/HTTPServer.cpp:642 +#: daemon/HTTPServer.cpp:672 msgid "EndDate" msgstr "" -#: daemon/HTTPServer.cpp:652 +#: daemon/HTTPServer.cpp:682 msgid "floodfill mode is disabled" msgstr "" -#: daemon/HTTPServer.cpp:663 +#: daemon/HTTPServer.cpp:693 msgid "Queue size" msgstr "" -#: daemon/HTTPServer.cpp:713 +#: daemon/HTTPServer.cpp:743 msgid "Run peer test" msgstr "" -#: daemon/HTTPServer.cpp:714 +#: daemon/HTTPServer.cpp:744 msgid "Reload tunnels configuration" msgstr "" -#: daemon/HTTPServer.cpp:717 +#: daemon/HTTPServer.cpp:747 msgid "Decline transit tunnels" msgstr "" -#: daemon/HTTPServer.cpp:719 +#: daemon/HTTPServer.cpp:749 msgid "Accept transit tunnels" msgstr "" -#: daemon/HTTPServer.cpp:723 daemon/HTTPServer.cpp:728 +#: daemon/HTTPServer.cpp:753 daemon/HTTPServer.cpp:758 msgid "Cancel graceful shutdown" msgstr "" -#: daemon/HTTPServer.cpp:725 daemon/HTTPServer.cpp:730 +#: daemon/HTTPServer.cpp:755 daemon/HTTPServer.cpp:760 msgid "Start graceful shutdown" msgstr "" -#: daemon/HTTPServer.cpp:733 +#: daemon/HTTPServer.cpp:763 msgid "Force shutdown" msgstr "" -#: daemon/HTTPServer.cpp:734 +#: daemon/HTTPServer.cpp:764 msgid "Reload external CSS styles" msgstr "" -#: daemon/HTTPServer.cpp:737 +#: daemon/HTTPServer.cpp:767 msgid "" "Note: any action done here are not persistent and not changes your " "config files." msgstr "" -#: daemon/HTTPServer.cpp:739 +#: daemon/HTTPServer.cpp:770 msgid "Logging level" msgstr "" -#: daemon/HTTPServer.cpp:747 +#: daemon/HTTPServer.cpp:779 msgid "Transit tunnels limit" msgstr "" -#: daemon/HTTPServer.cpp:752 daemon/HTTPServer.cpp:771 +#: daemon/HTTPServer.cpp:784 daemon/HTTPServer.cpp:803 msgid "Change" msgstr "" -#: daemon/HTTPServer.cpp:759 +#: daemon/HTTPServer.cpp:791 msgid "Change language" msgstr "" -#: daemon/HTTPServer.cpp:797 +#: daemon/HTTPServer.cpp:830 msgid "no transit tunnels currently built" msgstr "" -#: daemon/HTTPServer.cpp:878 daemon/HTTPServer.cpp:901 +#: daemon/HTTPServer.cpp:921 daemon/HTTPServer.cpp:944 msgid "SAM disabled" msgstr "" -#: daemon/HTTPServer.cpp:894 +#: daemon/HTTPServer.cpp:937 msgid "no sessions currently running" msgstr "" -#: daemon/HTTPServer.cpp:907 +#: daemon/HTTPServer.cpp:950 msgid "SAM session not found" msgstr "" -#: daemon/HTTPServer.cpp:912 +#: daemon/HTTPServer.cpp:955 msgid "SAM Session" msgstr "" -#: daemon/HTTPServer.cpp:969 +#: daemon/HTTPServer.cpp:1020 msgid "Server Tunnels" msgstr "" -#: daemon/HTTPServer.cpp:985 +#: daemon/HTTPServer.cpp:1036 msgid "Client Forwards" msgstr "" -#: daemon/HTTPServer.cpp:999 +#: daemon/HTTPServer.cpp:1050 msgid "Server Forwards" msgstr "" -#: daemon/HTTPServer.cpp:1199 +#: daemon/HTTPServer.cpp:1250 msgid "Unknown page" msgstr "" -#: daemon/HTTPServer.cpp:1218 +#: daemon/HTTPServer.cpp:1269 msgid "Invalid token" msgstr "" -#: daemon/HTTPServer.cpp:1276 daemon/HTTPServer.cpp:1333 -#: daemon/HTTPServer.cpp:1373 +#: daemon/HTTPServer.cpp:1327 daemon/HTTPServer.cpp:1359 +#: daemon/HTTPServer.cpp:1414 daemon/HTTPServer.cpp:1454 msgid "SUCCESS" msgstr "" -#: daemon/HTTPServer.cpp:1276 +#: daemon/HTTPServer.cpp:1327 msgid "Stream closed" msgstr "" -#: daemon/HTTPServer.cpp:1278 +#: daemon/HTTPServer.cpp:1329 msgid "Stream not found or already was closed" msgstr "" -#: daemon/HTTPServer.cpp:1281 +#: daemon/HTTPServer.cpp:1332 daemon/HTTPServer.cpp:1365 msgid "Destination not found" msgstr "" -#: daemon/HTTPServer.cpp:1284 +#: daemon/HTTPServer.cpp:1335 msgid "StreamID can't be null" msgstr "" -#: daemon/HTTPServer.cpp:1286 daemon/HTTPServer.cpp:1351 +#: daemon/HTTPServer.cpp:1337 daemon/HTTPServer.cpp:1367 +#: daemon/HTTPServer.cpp:1432 msgid "Return to destination page" msgstr "" -#: daemon/HTTPServer.cpp:1287 daemon/HTTPServer.cpp:1300 -#: daemon/HTTPServer.cpp:1375 +#: daemon/HTTPServer.cpp:1338 daemon/HTTPServer.cpp:1368 +#: daemon/HTTPServer.cpp:1381 daemon/HTTPServer.cpp:1456 #, c-format msgid "You will be redirected in %d seconds" msgstr "" -#: daemon/HTTPServer.cpp:1298 +#: daemon/HTTPServer.cpp:1359 +msgid "LeaseSet expiration time updated" +msgstr "" + +#: daemon/HTTPServer.cpp:1362 +msgid "LeaseSet is not found or already expired" +msgstr "" + +#: daemon/HTTPServer.cpp:1379 #, c-format msgid "Transit tunnels count must not exceed %d" msgstr "" -#: daemon/HTTPServer.cpp:1299 daemon/HTTPServer.cpp:1374 +#: daemon/HTTPServer.cpp:1380 daemon/HTTPServer.cpp:1455 msgid "Back to commands list" msgstr "" -#: daemon/HTTPServer.cpp:1335 +#: daemon/HTTPServer.cpp:1416 msgid "Register at reg.i2p" msgstr "" -#: daemon/HTTPServer.cpp:1336 +#: daemon/HTTPServer.cpp:1417 msgid "Description" msgstr "" -#: daemon/HTTPServer.cpp:1336 +#: daemon/HTTPServer.cpp:1417 msgid "A bit information about service on domain" msgstr "" -#: daemon/HTTPServer.cpp:1337 +#: daemon/HTTPServer.cpp:1418 msgid "Submit" msgstr "" -#: daemon/HTTPServer.cpp:1343 +#: daemon/HTTPServer.cpp:1424 msgid "Domain can't end with .b32.i2p" msgstr "" -#: daemon/HTTPServer.cpp:1346 +#: daemon/HTTPServer.cpp:1427 msgid "Domain must end with .i2p" msgstr "" -#: daemon/HTTPServer.cpp:1349 -msgid "Such destination is not found" -msgstr "" - -#: daemon/HTTPServer.cpp:1369 +#: daemon/HTTPServer.cpp:1450 msgid "Unknown command" msgstr "" -#: daemon/HTTPServer.cpp:1373 +#: daemon/HTTPServer.cpp:1454 msgid "Command accepted" msgstr "" @@ -624,20 +642,20 @@ msgstr "" msgid "You may try to find this host on jump services below" msgstr "" -#: libi2pd_client/HTTPProxy.cpp:309 libi2pd_client/HTTPProxy.cpp:324 -#: libi2pd_client/HTTPProxy.cpp:392 libi2pd_client/HTTPProxy.cpp:435 +#: libi2pd_client/HTTPProxy.cpp:333 libi2pd_client/HTTPProxy.cpp:348 +#: libi2pd_client/HTTPProxy.cpp:417 libi2pd_client/HTTPProxy.cpp:460 msgid "Invalid request" msgstr "" -#: libi2pd_client/HTTPProxy.cpp:309 +#: libi2pd_client/HTTPProxy.cpp:333 msgid "Proxy unable to parse your request" msgstr "" -#: libi2pd_client/HTTPProxy.cpp:324 +#: libi2pd_client/HTTPProxy.cpp:348 msgid "Addresshelper is not supported" msgstr "" -#: libi2pd_client/HTTPProxy.cpp:349 +#: libi2pd_client/HTTPProxy.cpp:373 #, c-format msgid "" "Host %s is already in router's addressbook. Be " @@ -645,121 +663,121 @@ msgid "" "Continue." msgstr "" -#: libi2pd_client/HTTPProxy.cpp:351 +#: libi2pd_client/HTTPProxy.cpp:375 msgid "Addresshelper forced update rejected" msgstr "" -#: libi2pd_client/HTTPProxy.cpp:358 +#: libi2pd_client/HTTPProxy.cpp:382 #, c-format msgid "" -"To add host %s in router's addressbook, click here: Continue." +"To add host %s in router's addressbook, click here: Continue." msgstr "" -#: libi2pd_client/HTTPProxy.cpp:360 +#: libi2pd_client/HTTPProxy.cpp:384 msgid "Addresshelper request" msgstr "" -#: libi2pd_client/HTTPProxy.cpp:369 +#: libi2pd_client/HTTPProxy.cpp:393 #, c-format msgid "" "Host %s added to router's addressbook from helper. Click here to proceed: Continue." msgstr "" -#: libi2pd_client/HTTPProxy.cpp:370 +#: libi2pd_client/HTTPProxy.cpp:395 msgid "Addresshelper adding" msgstr "" -#: libi2pd_client/HTTPProxy.cpp:377 +#: libi2pd_client/HTTPProxy.cpp:402 #, c-format msgid "" "Host %s is already in router's addressbook. Click " "here to update record: Continue." msgstr "" -#: libi2pd_client/HTTPProxy.cpp:379 +#: libi2pd_client/HTTPProxy.cpp:404 msgid "Addresshelper update" msgstr "" -#: libi2pd_client/HTTPProxy.cpp:392 +#: libi2pd_client/HTTPProxy.cpp:417 msgid "Invalid request URI" msgstr "" -#: libi2pd_client/HTTPProxy.cpp:435 +#: libi2pd_client/HTTPProxy.cpp:460 msgid "Can't detect destination host from request" msgstr "" -#: libi2pd_client/HTTPProxy.cpp:452 libi2pd_client/HTTPProxy.cpp:456 +#: libi2pd_client/HTTPProxy.cpp:477 libi2pd_client/HTTPProxy.cpp:481 msgid "Outproxy failure" msgstr "" -#: libi2pd_client/HTTPProxy.cpp:452 +#: libi2pd_client/HTTPProxy.cpp:477 msgid "Bad outproxy settings" msgstr "" -#: libi2pd_client/HTTPProxy.cpp:455 +#: libi2pd_client/HTTPProxy.cpp:480 #, c-format msgid "Host %s is not inside I2P network, but outproxy is not enabled" msgstr "" -#: libi2pd_client/HTTPProxy.cpp:544 +#: libi2pd_client/HTTPProxy.cpp:569 msgid "Unknown outproxy URL" msgstr "" -#: libi2pd_client/HTTPProxy.cpp:550 +#: libi2pd_client/HTTPProxy.cpp:575 msgid "Cannot resolve upstream proxy" msgstr "" -#: libi2pd_client/HTTPProxy.cpp:558 +#: libi2pd_client/HTTPProxy.cpp:583 msgid "Hostname is too long" msgstr "" -#: libi2pd_client/HTTPProxy.cpp:585 +#: libi2pd_client/HTTPProxy.cpp:610 msgid "Cannot connect to upstream SOCKS proxy" msgstr "" -#: libi2pd_client/HTTPProxy.cpp:591 +#: libi2pd_client/HTTPProxy.cpp:616 msgid "Cannot negotiate with SOCKS proxy" msgstr "" -#: libi2pd_client/HTTPProxy.cpp:633 +#: libi2pd_client/HTTPProxy.cpp:658 msgid "CONNECT error" msgstr "" -#: libi2pd_client/HTTPProxy.cpp:633 +#: libi2pd_client/HTTPProxy.cpp:658 msgid "Failed to connect" msgstr "" -#: libi2pd_client/HTTPProxy.cpp:644 libi2pd_client/HTTPProxy.cpp:670 +#: libi2pd_client/HTTPProxy.cpp:669 libi2pd_client/HTTPProxy.cpp:695 msgid "SOCKS proxy error" msgstr "" -#: libi2pd_client/HTTPProxy.cpp:652 +#: libi2pd_client/HTTPProxy.cpp:677 msgid "Failed to send request to upstream" msgstr "" -#: libi2pd_client/HTTPProxy.cpp:673 +#: libi2pd_client/HTTPProxy.cpp:698 msgid "No reply from SOCKS proxy" msgstr "" -#: libi2pd_client/HTTPProxy.cpp:680 +#: libi2pd_client/HTTPProxy.cpp:705 msgid "Cannot connect" msgstr "" -#: libi2pd_client/HTTPProxy.cpp:680 +#: libi2pd_client/HTTPProxy.cpp:705 msgid "HTTP out proxy not implemented" msgstr "" -#: libi2pd_client/HTTPProxy.cpp:681 +#: libi2pd_client/HTTPProxy.cpp:706 msgid "Cannot connect to upstream HTTP proxy" msgstr "" -#: libi2pd_client/HTTPProxy.cpp:714 +#: libi2pd_client/HTTPProxy.cpp:739 msgid "Host is down" msgstr "" -#: libi2pd_client/HTTPProxy.cpp:714 +#: libi2pd_client/HTTPProxy.cpp:739 msgid "" "Can't create connection to requested host, it may be down. Please try again " "later." diff --git a/contrib/i18n/README.md b/contrib/i18n/README.md index 56d76c5a..ce775ecb 100644 --- a/contrib/i18n/README.md +++ b/contrib/i18n/README.md @@ -2,12 +2,17 @@ --- ``` -xgettext --omit-header -ctr: -ktr -ktr:1,2 daemon/HTTPServer.cpp libi2pd_client/HTTPProxy.cpp +xgettext --omit-header -ctr: -ktr -kntr:1,2 daemon/HTTPServer.cpp libi2pd_client/HTTPProxy.cpp ``` Regex for transforming gettext translations to our format: --- +``` +in: ^(\"|#[:.,]|msgctxt)(.*)$\n +out: +``` + ``` in: msgid\ \"(.*)\"\nmsgid_plural\ \"(.*)\"\nmsgstr\[0\]\ \"(.*)\"\n(msgstr\[1\]\ \"(.*)\"\n)?(msgstr\[2\]\ \"(.*)\"\n)?(msgstr\[3\]\ \"(.*)\"\n)?(msgstr\[4\]\ \"(.*)\"\n)?(msgstr\[5\]\ \"(.*)\"\n)? out: #{"$2", {"$3", "$5", "$7", "$9", "$11"}},\n @@ -18,10 +23,6 @@ in: msgid\ \"(.*)\"\nmsgstr\ \"(.*)\"\n out: {"$1", "$2"},\n ``` -``` -in: ^#[:.,](.*)$\n -out: -``` ``` in: \n\n diff --git a/contrib/i2pd.conf b/contrib/i2pd.conf index 5d09c90a..be4a6719 100644 --- a/contrib/i2pd.conf +++ b/contrib/i2pd.conf @@ -19,7 +19,7 @@ ## Default: ~/.i2pd/certificates or /var/lib/i2pd/certificates # certsdir = /var/lib/i2pd/certificates -## Where to write pidfile (default: i2pd.pid, not used in Windows) +## Where to write pidfile (default: /run/i2pd.pid, not used in Windows) # pidfile = /run/i2pd.pid ## Logging configuration section @@ -31,7 +31,7 @@ ## * file - log entries to a file ## * syslog - use syslog, see man 3 syslog # log = file -## Path to logfile (default - autodetect) +## Path to logfile (default: autodetect) # logfile = /var/log/i2pd/i2pd.log ## Log messages above this level (debug, info, *warn, error, critical, none) ## If you set it to none, logging will be disabled @@ -40,6 +40,7 @@ # logclftime = true ## Daemon mode. Router will go to background after start. Ignored on Windows +## (default: true) # daemon = true ## Specify a family, router belongs to (default - none) @@ -70,58 +71,60 @@ ## don't just uncomment this # port = 4567 -## Enable communication through ipv4 +## Enable communication through ipv4 (default: true) ipv4 = true -## Enable communication through ipv6 +## Enable communication through ipv6 (default: false) ipv6 = false -## Enable SSU transport -ssu = false - ## Bandwidth configuration -## L limit bandwidth to 32KBs/sec, O - to 256KBs/sec, P - to 2048KBs/sec, +## L limit bandwidth to 32 KB/sec, O - to 256 KB/sec, P - to 2048 KB/sec, ## X - unlimited -## Default is L (regular node) and X if floodfill mode enabled. If you want to -## share more bandwidth without floodfill mode, uncomment that line and adjust -## value to your possibilities +## Default is L (regular node) and X if floodfill mode enabled. +## If you want to share more bandwidth without floodfill mode, uncomment +## that line and adjust value to your possibilities. Value can be set to +## integer in kilobytes, it will apply that limit and flag will be used +## from next upper limit (example: if you set 4096 flag will be X, but real +## limit will be 4096 KB/s). Same can be done when floodfill mode is used, +## but keep in mind that low values may be negatively evaluated by Java +## router algorithms. # bandwidth = L -## Max % of bandwidth limit for transit. 0-100. 100 by default +## Max % of bandwidth limit for transit. 0-100 (default: 100) # share = 100 ## Router will not accept transit tunnels, disabling transit traffic completely -## (default = false) +## (default: false) # notransit = true -## Router will be floodfill +## Router will be floodfill (default: false) ## Note: that mode uses much more network connections and CPU! # floodfill = true [ntcp2] -## Enable NTCP2 transport (default = true) +## Enable NTCP2 transport (default: true) # enabled = true -## Publish address in RouterInfo (default = true) +## Publish address in RouterInfo (default: true) # published = true ## Port for incoming connections (default is global port option value) # port = 4567 [ssu2] -## Enable SSU2 transport +## Enable SSU2 transport (default: true) # enabled = true -## Publish address in RouterInfo +## Publish address in RouterInfo (default: true) # published = true -## Port for incoming connections (default is global port option value or port + 1 if SSU is enabled) +## Port for incoming connections (default is global port option value) # port = 4567 [http] ## Web Console settings -## Uncomment and set to 'false' to disable Web Console +## Enable the Web Console (default: true) # enabled = true -## Address and port service will listen on -address = 127.0.0.1 -port = 7070 -## Path to web console, default "/" +## Address and port service will listen on (default: 127.0.0.1:7070) +# address = 127.0.0.1 +# port = 7070 +## Path to web console (default: /) # webroot = / -## Uncomment following lines to enable Web Console authentication +## Enable Web Console authentication (default: false) ## You should not use Web Console via public networks without additional encryption. ## HTTP authentication is not encryption layer! # auth = true @@ -134,12 +137,12 @@ port = 7070 # lang = english [httpproxy] -## Uncomment and set to 'false' to disable HTTP Proxy +## Enable the HTTP proxy (default: true) # enabled = true -## Address and port service will listen on -address = 127.0.0.1 -port = 4444 -## Optional keys file for proxy local destination +## Address and port service will listen on (default: 127.0.0.1:4444) +# address = 127.0.0.1 +# port = 4444 +## Optional keys file for proxy local destination (default: http-proxy-keys.dat) # keys = http-proxy-keys.dat ## Enable address helper for adding .i2p domains with "jump URLs" (default: true) ## You should disable this feature if your i2pd HTTP Proxy is public, @@ -150,15 +153,15 @@ port = 4444 ## httpproxy section also accepts I2CP parameters, like "inbound.length" etc. [socksproxy] -## Uncomment and set to 'false' to disable SOCKS Proxy +## Enable the SOCKS proxy (default: true) # enabled = true -## Address and port service will listen on -address = 127.0.0.1 -port = 4447 -## Optional keys file for proxy local destination +## Address and port service will listen on (default: 127.0.0.1:4447) +# address = 127.0.0.1 +# port = 4447 +## Optional keys file for proxy local destination (default: socks-proxy-keys.dat) # keys = socks-proxy-keys.dat ## Socks outproxy. Example below is set to use Tor for all connections except i2p -## Uncomment and set to 'true' to enable using of SOCKS outproxy +## Enable using of SOCKS outproxy (works only with SOCKS4, default: false) # outproxy.enabled = false ## Address and port of outproxy # outproxy = 127.0.0.1 @@ -166,34 +169,34 @@ port = 4447 ## socksproxy section also accepts I2CP parameters, like "inbound.length" etc. [sam] -## Comment or set to 'false' to disable SAM Bridge -enabled = true -## Address and ports service will listen on +## Enable the SAM bridge (default: true) +# enabled = false +## Address and ports service will listen on (default: 127.0.0.1:7656, udp: 7655) # address = 127.0.0.1 # port = 7656 # portudp = 7655 [bob] -## Uncomment and set to 'true' to enable BOB command channel +## Enable the BOB command channel (default: false) # enabled = false -## Address and port service will listen on +## Address and port service will listen on (default: 127.0.0.1:2827) # address = 127.0.0.1 # port = 2827 [i2cp] -## Uncomment and set to 'true' to enable I2CP protocol +## Enable the I2CP protocol (default: false) # enabled = false -## Address and port service will listen on +## Address and port service will listen on (default: 127.0.0.1:7654) # address = 127.0.0.1 # port = 7654 [i2pcontrol] -## Uncomment and set to 'true' to enable I2PControl protocol +## Enable the I2PControl protocol (default: false) # enabled = false -## Address and port service will listen on +## Address and port service will listen on (default: 127.0.0.1:7650) # address = 127.0.0.1 # port = 7650 -## Authentication password. "itoopie" by default +## Authentication password (default: itoopie) # password = itoopie [precomputation] @@ -204,11 +207,11 @@ enabled = true [upnp] ## Enable or disable UPnP: automatic port forwarding (enabled by default in WINDOWS, ANDROID) # enabled = false -## Name i2pd appears in UPnP forwardings list (default = I2Pd) +## Name i2pd appears in UPnP forwardings list (default: I2Pd) # name = I2Pd [meshnets] -## Enable connectivity over the Yggdrasil network +## Enable connectivity over the Yggdrasil network (default: false) # yggdrasil = false ## You can bind address from your Yggdrasil subnet 300::/64 ## The address must first be added to the network interface @@ -216,13 +219,13 @@ enabled = true [reseed] ## Options for bootstrapping into I2P network, aka reseeding -## Enable or disable reseed data verification. +## Enable reseed data verification (default: true) verify = true ## URLs to request reseed data from, separated by comma ## Default: "mainline" I2P Network reseeds # urls = https://reseed.i2p-projekt.de/,https://i2p.mooo.com/netDb/,https://netdb.i2p2.no/ ## Reseed URLs through the Yggdrasil, separated by comma -# yggurls = http://[324:9de3:fea4:f6ac::ace]:7070/ +# yggurls = http://[324:71e:281a:9ed3::ace]:7070/ ## Path to local reseed data file (.su3) for manual reseeding # file = /path/to/i2pseeds.su3 ## or HTTPS URL to reseed from @@ -232,7 +235,7 @@ verify = true ## If you run i2pd behind a proxy server, set proxy server for reseeding here ## Should be http://address:port or socks://address:port # proxy = http://127.0.0.1:8118 -## Minimum number of known routers, below which i2pd triggers reseeding. 25 by default +## Minimum number of known routers, below which i2pd triggers reseeding (default: 25) # threshold = 25 [addressbook] @@ -252,13 +255,13 @@ verify = true # coresize = 0 [trust] -## Enable explicit trust options. false by default +## Enable explicit trust options. (default: false) # enabled = true ## Make direct I2P connections only to routers in specified Family. # family = MyFamily ## Make direct I2P connections only to routers specified here. Comma separated list of base64 identities. # routers = -## Should we hide our router from other routers? false by default +## Should we hide our router from other routers? (default: false) # hidden = true [exploratory] @@ -277,8 +280,6 @@ verify = true [cpuext] ## Use CPU AES-NI instructions set when work with cryptography when available (default: true) # aesni = true -## Use CPU AVX instructions set when work with cryptography when available (default: true) -# avx = true -## Force usage of CPU instructions set, even if they not found +## Force usage of CPU instructions set, even if they not found (default: false) ## DO NOT TOUCH that option if you really don't know what are you doing! # force = false diff --git a/contrib/rpm/i2pd-git.spec b/contrib/rpm/i2pd-git.spec index 4ee7a8df..0bc46eab 100644 --- a/contrib/rpm/i2pd-git.spec +++ b/contrib/rpm/i2pd-git.spec @@ -1,7 +1,7 @@ %define git_hash %(git rev-parse HEAD | cut -c -7) Name: i2pd-git -Version: 2.47.0 +Version: 2.50.2 Release: git%{git_hash}%{?dist} Summary: I2P router written in C++ Conflicts: i2pd @@ -28,9 +28,11 @@ Requires: logrotate Requires: systemd Requires(pre): %{_sbindir}/useradd %{_sbindir}/groupadd + %description C++ implementation of I2P. + %prep %setup -q -n i2pd-openssl @@ -38,72 +40,56 @@ C++ implementation of I2P. %build cd build %if 0%{?rhel} == 7 -%cmake3 \ + %cmake3 \ -DWITH_LIBRARY=OFF \ -DWITH_UPNP=ON \ -DWITH_HARDENING=ON \ -DBUILD_SHARED_LIBS:BOOL=OFF %else -%cmake \ + %cmake \ -DWITH_LIBRARY=OFF \ -DWITH_UPNP=ON \ -DWITH_HARDENING=ON \ -%if 0%{?fedora} > 29 + %if 0%{?fedora} > 29 -DBUILD_SHARED_LIBS:BOOL=OFF \ . -%else + %else -DBUILD_SHARED_LIBS:BOOL=OFF -%endif + %endif %endif - -%if 0%{?rhel} == 9 -pushd redhat-linux-build -%endif - -%if 0%{?fedora} >= 35 -pushd redhat-linux-build +%if 0%{?rhel} == 9 || 0%{?fedora} >= 35 || 0%{?eln} + pushd redhat-linux-build %else -%if 0%{?fedora} >= 33 -pushd %{_target_platform} -%endif -%endif + %if 0%{?fedora} >= 33 + pushd %{_target_platform} + %endif -%if 0%{?mageia} > 7 -pushd build + %if 0%{?mageia} > 7 + pushd build + %endif %endif make %{?_smp_mflags} -%if 0%{?rhel} == 9 -popd +%if 0%{?rhel} == 9 || 0%{?fedora} >= 33 || 0%{?mageia} > 7 + popd %endif -%if 0%{?fedora} >= 33 -popd -%endif - -%if 0%{?mageia} > 7 -popd -%endif %install pushd build -%if 0%{?rhel} == 9 -pushd redhat-linux-build -%endif - -%if 0%{?fedora} >= 35 -pushd redhat-linux-build +%if 0%{?rhel} == 9 || 0%{?fedora} >= 35 || 0%{?eln} + pushd redhat-linux-build %else -%if 0%{?fedora} >= 33 -pushd %{_target_platform} -%endif -%endif + %if 0%{?fedora} >= 33 + pushd %{_target_platform} + %endif -%if 0%{?mageia} -pushd build + %if 0%{?mageia} + pushd build + %endif %endif chrpath -d i2pd @@ -158,6 +144,21 @@ getent passwd i2pd >/dev/null || \ %changelog +* Sat Jan 06 2024 orignal - 2.50.2 +- update to 2.50.2 + +* Sat Dec 23 2023 r4sas - 2.50.1 +- update to 2.50.1 + +* Mon Dec 18 2023 orignal - 2.50.0 +- update to 2.50.0 + +* Mon Sep 18 2023 orignal - 2.49.0 +- update to 2.49.0 + +* Mon Jun 12 2023 orignal - 2.48.0 +- update to 2.48.0 + * Sat Mar 11 2023 orignal - 2.47.0 - update to 2.47.0 diff --git a/contrib/rpm/i2pd.spec b/contrib/rpm/i2pd.spec index 407e2d43..1de4076e 100644 --- a/contrib/rpm/i2pd.spec +++ b/contrib/rpm/i2pd.spec @@ -1,5 +1,5 @@ Name: i2pd -Version: 2.47.0 +Version: 2.50.2 Release: 1%{?dist} Summary: I2P router written in C++ Conflicts: i2pd-git @@ -26,9 +26,11 @@ Requires: logrotate Requires: systemd Requires(pre): %{_sbindir}/useradd %{_sbindir}/groupadd + %description C++ implementation of I2P. + %prep %setup -q @@ -36,71 +38,56 @@ C++ implementation of I2P. %build cd build %if 0%{?rhel} == 7 -%cmake3 \ + %cmake3 \ -DWITH_LIBRARY=OFF \ -DWITH_UPNP=ON \ -DWITH_HARDENING=ON \ -DBUILD_SHARED_LIBS:BOOL=OFF %else -%cmake \ + %cmake \ -DWITH_LIBRARY=OFF \ -DWITH_UPNP=ON \ -DWITH_HARDENING=ON \ -%if 0%{?fedora} > 29 + %if 0%{?fedora} > 29 -DBUILD_SHARED_LIBS:BOOL=OFF \ . -%else + %else -DBUILD_SHARED_LIBS:BOOL=OFF -%endif + %endif %endif -%if 0%{?rhel} == 9 -pushd redhat-linux-build -%endif - -%if 0%{?fedora} >= 35 -pushd redhat-linux-build +%if 0%{?rhel} == 9 || 0%{?fedora} >= 35 || 0%{?eln} + pushd redhat-linux-build %else -%if 0%{?fedora} >= 33 -pushd %{_target_platform} -%endif -%endif + %if 0%{?fedora} >= 33 + pushd %{_target_platform} + %endif -%if 0%{?mageia} > 7 -pushd build + %if 0%{?mageia} > 7 + pushd build + %endif %endif make %{?_smp_mflags} -%if 0%{?rhel} == 9 -popd +%if 0%{?rhel} == 9 || 0%{?fedora} >= 33 || 0%{?mageia} > 7 + popd %endif -%if 0%{?fedora} >= 33 -popd -%endif - -%if 0%{?mageia} > 7 -popd -%endif %install pushd build -%if 0%{?rhel} == 9 -pushd redhat-linux-build -%endif - -%if 0%{?fedora} >= 35 -pushd redhat-linux-build +%if 0%{?rhel} == 9 || 0%{?fedora} >= 35 || 0%{?eln} + pushd redhat-linux-build %else -%if 0%{?fedora} >= 33 -pushd %{_target_platform} -%endif -%endif + %if 0%{?fedora} >= 33 + pushd %{_target_platform} + %endif -%if 0%{?mageia} -pushd build + %if 0%{?mageia} + pushd build + %endif %endif chrpath -d i2pd @@ -155,6 +142,21 @@ getent passwd i2pd >/dev/null || \ %changelog +* Sat Jan 06 2024 orignal - 2.50.2 +- update to 2.50.2 + +* Sat Dec 23 2023 r4sas - 2.50.1 +- update to 2.50.1 + +* Mon Dec 18 2023 orignal - 2.50.0 +- update to 2.50.0 + +* Mon Sep 18 2023 orignal - 2.49.0 +- update to 2.49.0 + +* Mon Jun 12 2023 orignal - 2.48.0 +- update to 2.48.0 + * Sat Mar 11 2023 orignal - 2.47.0 - update to 2.47.0 diff --git a/daemon/Daemon.cpp b/daemon/Daemon.cpp index 8e9721db..090792bf 100644 --- a/daemon/Daemon.cpp +++ b/daemon/Daemon.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2023, The PurpleI2P Project +* Copyright (c) 2013-2024, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -150,17 +150,20 @@ namespace util bool precomputation; i2p::config::GetOption("precomputation.elgamal", precomputation); bool aesni; i2p::config::GetOption("cpuext.aesni", aesni); - bool avx; i2p::config::GetOption("cpuext.avx", avx); bool forceCpuExt; i2p::config::GetOption("cpuext.force", forceCpuExt); bool ssu; i2p::config::GetOption("ssu", ssu); if (!ssu && i2p::config::IsDefault ("precomputation.elgamal")) precomputation = false; // we don't elgamal table if no ssu, unless it's specified explicitly - i2p::crypto::InitCrypto (precomputation, aesni, avx, forceCpuExt); + i2p::crypto::InitCrypto (precomputation, aesni, forceCpuExt); i2p::transport::InitAddressFromIface (); // get address4/6 from interfaces int netID; i2p::config::GetOption("netid", netID); i2p::context.SetNetID (netID); + + bool checkReserved; i2p::config::GetOption("reservedrange", checkReserved); + i2p::transport::transports.SetCheckReserved(checkReserved); + i2p::context.Init (); i2p::transport::InitTransports (); @@ -176,7 +179,7 @@ namespace util bool transit; i2p::config::GetOption("notransit", transit); i2p::context.SetAcceptsTunnels (!transit); - uint16_t transitTunnels; i2p::config::GetOption("limits.transittunnels", transitTunnels); + uint32_t transitTunnels; i2p::config::GetOption("limits.transittunnels", transitTunnels); if (isFloodfill && i2p::config::IsDefault ("limits.transittunnels")) transitTunnels *= 2; // double default number of transit tunnels for floodfill i2p::tunnel::tunnels.SetMaxNumTransitTunnels (transitTunnels); @@ -299,12 +302,10 @@ namespace util bool ntcp2; i2p::config::GetOption("ntcp2.enabled", ntcp2); bool ssu2; i2p::config::GetOption("ssu2.enabled", ssu2); - bool checkInReserved; i2p::config::GetOption("reservedrange", checkInReserved); LogPrint(eLogInfo, "Daemon: Starting Transports"); if(!ssu2) LogPrint(eLogInfo, "Daemon: SSU2 disabled"); if(!ntcp2) LogPrint(eLogInfo, "Daemon: NTCP2 disabled"); - i2p::transport::transports.SetCheckReserved(checkInReserved); i2p::transport::transports.Start(ntcp2, ssu2); if (i2p::transport::transports.IsBoundSSU2() || i2p::transport::transports.IsBoundNTCP2()) LogPrint(eLogInfo, "Daemon: Transports started"); diff --git a/daemon/HTTPServer.cpp b/daemon/HTTPServer.cpp index dceee06a..494a4e8e 100644 --- a/daemon/HTTPServer.cpp +++ b/daemon/HTTPServer.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2023, The PurpleI2P Project +* Copyright (c) 2013-2024, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -87,6 +87,7 @@ namespace http { const char HTTP_COMMAND_GET_REG_STRING[] = "get_reg_string"; const char HTTP_COMMAND_SETLANGUAGE[] = "setlanguage"; const char HTTP_COMMAND_RELOAD_CSS[] = "reload_css"; + const char HTTP_COMMAND_EXPIRELEASE[] = "expirelease"; static std::string ConvertTime (uint64_t time) { @@ -132,23 +133,19 @@ namespace http { static void ShowTunnelDetails (std::stringstream& s, enum i2p::tunnel::TunnelState eState, bool explr, int bytes) { std::string state, stateText; - switch (eState) { + switch (eState) + { case i2p::tunnel::eTunnelStateBuildReplyReceived : case i2p::tunnel::eTunnelStatePending : state = "building"; break; - case i2p::tunnel::eTunnelStateBuildFailed : - case i2p::tunnel::eTunnelStateTestFailed : + case i2p::tunnel::eTunnelStateBuildFailed : state = "failed"; stateText = "declined"; break; + case i2p::tunnel::eTunnelStateTestFailed : state = "failed"; stateText = "test failed"; break; case i2p::tunnel::eTunnelStateFailed : state = "failed"; break; case i2p::tunnel::eTunnelStateExpiring : state = "expiring"; break; case i2p::tunnel::eTunnelStateEstablished : state = "established"; break; default: state = "unknown"; break; } - - if (state == "building") stateText = tr("building"); - else if (state == "failed") stateText = tr("failed"); - else if (state == "expiring") stateText = tr("expiring"); - else if (state == "established") stateText = tr("established"); - else stateText = tr("unknown"); - + if (stateText.empty ()) stateText = tr(state); + s << " " << stateText << ((explr) ? " (" + tr("exploratory") + ")" : "") << ", "; ShowTraffic(s, bytes); s << "\r\n"; @@ -221,18 +218,19 @@ namespace http { s << "" << tr("ERROR") << ": " << string << "
\r\n"; } - static void ShowNetworkStatus (std::stringstream& s, RouterStatus status, RouterError error) + static void ShowNetworkStatus (std::stringstream& s, RouterStatus status, bool testing, RouterError error) { switch (status) { case eRouterStatusOK: s << tr("OK"); break; - case eRouterStatusTesting: s << tr("Testing"); break; case eRouterStatusFirewalled: s << tr("Firewalled"); break; case eRouterStatusUnknown: s << tr("Unknown"); break; case eRouterStatusProxy: s << tr("Proxy"); break; case eRouterStatusMesh: s << tr("Mesh"); break; default: s << tr("Unknown"); } + if (testing) + s << " (" << tr("Testing") << ")"; if (error != eRouterErrorNone) { switch (error) @@ -263,12 +261,12 @@ namespace http { ShowUptime(s, i2p::context.GetUptime ()); s << "
\r\n"; s << "" << tr("Network status") << ": "; - ShowNetworkStatus (s, i2p::context.GetStatus (), i2p::context.GetError ()); + ShowNetworkStatus (s, i2p::context.GetStatus (), i2p::context.GetTesting(), i2p::context.GetError ()); s << "
\r\n"; if (i2p::context.SupportsV6 ()) { s << "" << tr("Network status v6") << ": "; - ShowNetworkStatus (s, i2p::context.GetStatusV6 (), i2p::context.GetErrorV6 ()); + ShowNetworkStatus (s, i2p::context.GetStatusV6 (), i2p::context.GetTestingV6(), i2p::context.GetErrorV6 ()); s << "
\r\n"; } #if ((!defined(WIN32) && !defined(QT_GUI_LIB) && !defined(ANDROID)) || defined(ANDROID_BINARY)) @@ -421,8 +419,12 @@ namespace http { static void ShowLeaseSetDestination (std::stringstream& s, std::shared_ptr dest, uint32_t token) { + s << "Base32:
\r\n
\r\n
\r\n"; + s << "Base64:
\r\n
\r\n
\r\n"; + if (dest->IsEncryptedLeaseSet ()) { i2p::data::BlindedPublicKey blinded (dest->GetIdentity (), dest->IsPerClientAuth ()); @@ -434,12 +436,11 @@ namespace http { if (dest->IsPublic() && token && !dest->IsEncryptedLeaseSet ()) { std::string webroot; i2p::config::GetOption("http.webroot", webroot); - auto base32 = dest->GetIdentHash ().ToBase32 (); s << "
\r\n\r\n
\r\n" "
\r\n" " \r\n" " \r\n" - " \r\n" + " GetIdentHash ().ToBase32 () << "\">\r\n" " " << tr("Domain") << ":\r\n\r\n" " \r\n" "
\r\n" << tr("Note: result string can be used only for registering 2LD domains (example.i2p). For registering subdomains please use i2pd-tools.") << "\r\n
\r\n
\r\n
\r\n"; @@ -448,9 +449,23 @@ namespace http { if (dest->GetNumRemoteLeaseSets()) { s << "
\r\n\r\n
\r\n"; + << "\r\n\r\n
\r\n" + << "
"<< tr("Address") << "" << tr("Type") << "" << tr("EncType") << "
" + << "" + << "" // LeaseSet expiration button column + << "" + << "" + << ""; for(auto& it: dest->GetLeaseSets ()) - s << "\r\n"; + { + s << "" + << "" + << "" + << "" + << "" + << "\r\n"; + } s << "
" << tr("Address") << " " << tr("Type") << "" << tr("EncType") << "
" << it.first.ToBase32 () << "" << (int)it.second->GetStoreType () << "" << (int)it.second->GetEncryptionType () <<"
" << it.first.ToBase32 () << "" << (int)it.second->GetStoreType () << "" << (int)it.second->GetEncryptionType () <<"
\r\n
\r\n
\r\n
\r\n"; } else s << "" << tr("LeaseSets") << ": 0
\r\n
\r\n"; @@ -591,6 +606,8 @@ namespace http { } s << "\r\n"; } + else + ShowError(s, tr("Such destination is not found")); } void ShowI2CPLocalDestination (std::stringstream& s, const std::string& id) @@ -755,7 +772,7 @@ namespace http { s << " info \r\n"; s << " debug
\r\n
\r\n"; - uint16_t maxTunnels = i2p::tunnel::tunnels.GetMaxNumTransitTunnels (); + uint32_t maxTunnels = i2p::tunnel::tunnels.GetMaxNumTransitTunnels (); s << "" << tr("Transit tunnels limit") << "
\r\n"; s << "
\r\n"; s << " \r\n"; @@ -958,34 +975,42 @@ namespace http { void ShowI2PTunnels (std::stringstream& s) { std::string webroot; i2p::config::GetOption("http.webroot", webroot); - s << "" << tr("Client Tunnels") << ":
\r\n
\r\n"; - for (auto& it: i2p::client::context.GetClientTunnels ()) - { - auto& ident = it.second->GetLocalDestination ()->GetIdentHash(); - s << "
"; - s << it.second->GetName () << " ⇐ "; - s << i2p::client::context.GetAddressBook ().ToAddress(ident); - s << "
\r\n"<< std::endl; - } + + auto& clientTunnels = i2p::client::context.GetClientTunnels (); auto httpProxy = i2p::client::context.GetHttpProxy (); - if (httpProxy) - { - auto& ident = httpProxy->GetLocalDestination ()->GetIdentHash(); - s << "
"; - s << "HTTP " << tr("Proxy") << " ⇐ "; - s << i2p::client::context.GetAddressBook ().ToAddress(ident); - s << "
\r\n"<< std::endl; - } auto socksProxy = i2p::client::context.GetSocksProxy (); - if (socksProxy) + if (!clientTunnels.empty () || httpProxy || socksProxy) { - auto& ident = socksProxy->GetLocalDestination ()->GetIdentHash(); - s << "
"; - s << "SOCKS " << tr("Proxy") << " ⇐ "; - s << i2p::client::context.GetAddressBook ().ToAddress(ident); - s << "
\r\n"<< std::endl; + s << "" << tr("Client Tunnels") << ":
\r\n
\r\n"; + if (!clientTunnels.empty ()) + { + for (auto& it: clientTunnels) + { + auto& ident = it.second->GetLocalDestination ()->GetIdentHash(); + s << "
"; + s << it.second->GetName () << " ⇐ "; + s << i2p::client::context.GetAddressBook ().ToAddress(ident); + s << "
\r\n"<< std::endl; + } + } + if (httpProxy) + { + auto& ident = httpProxy->GetLocalDestination ()->GetIdentHash(); + s << "
"; + s << "HTTP " << tr("Proxy") << " ⇐ "; + s << i2p::client::context.GetAddressBook ().ToAddress(ident); + s << "
\r\n"<< std::endl; + } + if (socksProxy) + { + auto& ident = socksProxy->GetLocalDestination ()->GetIdentHash(); + s << "
"; + s << "SOCKS " << tr("Proxy") << " ⇐ "; + s << i2p::client::context.GetAddressBook ().ToAddress(ident); + s << "
\r\n"<< std::endl; + } + s << "
\r\n"; } - s << "
\r\n"; auto& serverTunnels = i2p::client::context.GetServerTunnels (); if (!serverTunnels.empty ()) { @@ -1312,6 +1337,36 @@ namespace http { res.add_header("Refresh", redirect.c_str()); return; } + else if (cmd == HTTP_COMMAND_EXPIRELEASE) + { + std::string b32 = params["b32"]; + std::string lease = params["lease"]; + + i2p::data::IdentHash ident, leaseident; + ident.FromBase32 (b32); + leaseident.FromBase32 (lease); + auto dest = i2p::client::context.FindLocalDestination (ident); + + if (dest) + { + auto leaseset = dest->FindLeaseSet (leaseident); + if (leaseset) + { + leaseset->ExpireLease (); + s << "" << tr("SUCCESS") << ": " << tr("LeaseSet expiration time updated") << "
\r\n
\r\n"; + } + else + s << "" << tr("ERROR") << ": " << tr("LeaseSet is not found or already expired") << "
\r\n
\r\n"; + } + else + s << "" << tr("ERROR") << ": " << tr("Destination not found") << "
\r\n
\r\n"; + + s << "" << tr("Return to destination page") << "
\r\n"; + s << "

" << tr("You will be redirected in %d seconds", COMMAND_REDIRECT_TIMEOUT) << ""; + redirect = std::to_string(COMMAND_REDIRECT_TIMEOUT) + "; url=" + webroot + "?page=local_destination&b32=" + b32; + res.add_header("Refresh", redirect.c_str()); + return; + } else if (cmd == HTTP_COMMAND_LIMITTRANSIT) { uint32_t limit = std::stoul(params["limit"], nullptr); diff --git a/daemon/HTTPServer.h b/daemon/HTTPServer.h index f41d925d..f751c5a8 100644 --- a/daemon/HTTPServer.h +++ b/daemon/HTTPServer.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2023, The PurpleI2P Project +* Copyright (c) 2013-2024, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -25,7 +25,7 @@ namespace http const size_t HTTP_CONNECTION_BUFFER_SIZE = 8192; const int TOKEN_EXPIRATION_TIMEOUT = 30; // in seconds const int COMMAND_REDIRECT_TIMEOUT = 5; // in seconds - const int TRANSIT_TUNNELS_LIMIT = 65535; + const int TRANSIT_TUNNELS_LIMIT = 1000000; class HTTPConnection: public std::enable_shared_from_this { diff --git a/debian/changelog b/debian/changelog index 44b0b8b5..56ab7c16 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,33 @@ +i2pd (2.50.2) unstable; urgency=medium + + * updated to version 2.50.2/0.9.61 + +-- orignal Sat, 06 Jan 2024 16:00:00 +0000 + +i2pd (2.50.1-1) unstable; urgency=medium + + * updated to version 2.50.1/0.9.61 + + -- r4sas Sat, 23 Dec 2023 18:30:00 +0000 + +i2pd (2.50.0-1) unstable; urgency=medium + + * updated to version 2.50.0/0.9.61 + + -- orignal Mon, 18 Dec 2023 16:00:00 +0000 + +i2pd (2.49.0-1) unstable; urgency=medium + + * updated to version 2.49.0/0.9.60 + + -- orignal Mon, 18 Sep 2023 16:00:00 +0000 + +i2pd (2.48.0-1) unstable; urgency=high + + * updated to version 2.48.0/0.9.59 + + -- orignal Mon, 12 Jun 2023 16:00:00 +0000 + i2pd (2.47.0-1) unstable; urgency=high * updated to version 2.47.0/0.9.58 diff --git a/debian/i2pd.1 b/debian/i2pd.1 index 5145321f..42c282d9 100644 --- a/debian/i2pd.1 +++ b/debian/i2pd.1 @@ -64,7 +64,7 @@ The network interface to bind to for IPv4 connections The network interface to bind to for IPv6 connections .TP \fB\-\-ipv4=\fR -Enable communication through ipv6 (\fIenabled\fR by default) +Enable communication through ipv4 (\fIenabled\fR by default) .TP \fB\-\-ipv6\fR Enable communication through ipv6 (\fIdisabled\fR by default) diff --git a/i18n/Chinese.cpp b/i18n/Chinese.cpp index cd38fa0f..5ecfe067 100644 --- a/i18n/Chinese.cpp +++ b/i18n/Chinese.cpp @@ -109,6 +109,7 @@ namespace chinese // language namespace {"Local Destination", "本地目标"}, {"Streams", "流"}, {"Close stream", "断开流"}, + {"Such destination is not found", "找不到此目标"}, {"I2CP session not found", "未找到 I2CP 会话"}, {"I2CP is not enabled", "I2CP 未启用"}, {"Invalid", "无效"}, @@ -158,7 +159,6 @@ namespace chinese // language namespace {"Submit", "提交"}, {"Domain can't end with .b32.i2p", "域名不能以 .b32.i2p 结尾"}, {"Domain must end with .i2p", "域名必须以 .i2p 结尾"}, - {"Such destination is not found", "找不到此目标"}, {"Unknown command", "未知指令"}, {"Command accepted", "已接受指令"}, {"Proxy error", "代理错误"}, diff --git a/i18n/French.cpp b/i18n/French.cpp index 0ce044e1..0a5b147e 100644 --- a/i18n/French.cpp +++ b/i18n/French.cpp @@ -69,6 +69,7 @@ namespace french // language namespace {"Stopping in", "Arrêt dans"}, {"Family", "Famille"}, {"Tunnel creation success rate", "Taux de succès de création de tunnels"}, + {"Total tunnel creation success rate", "Taux de réussite de création de tunnel"}, {"Received", "Reçu"}, {"%.2f KiB/s", "%.2f Kio/s"}, {"Sent", "Envoyé"}, @@ -91,10 +92,11 @@ namespace french // language namespace {"Address registration line", "Ligne d'inscription de l'adresse"}, {"Domain", "Domaine"}, {"Generate", "Générer"}, - {"Note: result string can be used only for registering 2LD domains (example.i2p). For registering subdomains please use i2pd-tools.", "Note: La chaîne résultante peut seulement être utilisée pour enregistrer les domaines 2LD (exemple.i2p). Pour enregistrer des sous-domaines, veuillez utiliser i2pd-tools."}, + {"Note: result string can be used only for registering 2LD domains (example.i2p). For registering subdomains please use i2pd-tools.", "Note : La chaîne résultante peut seulement être utilisée pour enregistrer les domaines 2LD (exemple.i2p). Pour enregistrer des sous-domaines, veuillez utiliser i2pd-tools."}, {"Address", "Adresse"}, {"Type", "Type"}, {"EncType", "EncType"}, + {"Expire LeaseSet", "Expirer le jeu de baux"}, {"Inbound tunnels", "Tunnels entrants"}, {"%dms", "%dms"}, {"Outbound tunnels", "Tunnels sortants"}, @@ -109,6 +111,7 @@ namespace french // language namespace {"Local Destination", "Destination locale"}, {"Streams", "Flux"}, {"Close stream", "Fermer le flux"}, + {"Such destination is not found", "Cette destination est introuvable"}, {"I2CP session not found", "Session I2CP introuvable"}, {"I2CP is not enabled", "I2CP est désactivé"}, {"Invalid", "Invalide"}, @@ -128,7 +131,7 @@ namespace french // language namespace {"Start graceful shutdown", "Démarrer l'arrêt gracieux"}, {"Force shutdown", "Forcer l'arrêt"}, {"Reload external CSS styles", "Rafraîchir les styles CSS externes"}, - {"Note: any action done here are not persistent and not changes your config files.", "Note: Toute action effectuée ici n'est pas permanente et ne modifie pas vos fichiers de configuration."}, + {"Note: any action done here are not persistent and not changes your config files.", "Note : Toute action effectuée ici n'est pas permanente et ne modifie pas vos fichiers de configuration."}, {"Logging level", "Niveau de journalisation"}, {"Transit tunnels limit", "Limite sur les tunnels transitoires"}, {"Change", "Changer"}, @@ -150,6 +153,8 @@ namespace french // language namespace {"StreamID can't be null", "StreamID ne peut pas être vide"}, {"Return to destination page", "Retourner à la page de destination"}, {"You will be redirected in %d seconds", "Vous serez redirigé dans %d secondes"}, + {"LeaseSet expiration time updated", "Temps d'expiration du jeu de baux mis à jour"}, + {"LeaseSet is not found or already expired", "Le jeu de baux est introuvable ou a déjà expiré"}, {"Transit tunnels count must not exceed %d", "Le nombre de tunnels de transit ne doit pas excéder %d"}, {"Back to commands list", "Retour à la liste des commandes"}, {"Register at reg.i2p", "Inscription à reg.i2p"}, @@ -158,24 +163,23 @@ namespace french // language namespace {"Submit", "Soumettre"}, {"Domain can't end with .b32.i2p", "Le domaine ne peut pas terminer par .b32.i2p"}, {"Domain must end with .i2p", "Le domaine doit terminer par .i2p"}, - {"Such destination is not found", "Cette destination est introuvable"}, {"Unknown command", "Commande inconnue"}, {"Command accepted", "Commande acceptée"}, {"Proxy error", "Erreur de proxy"}, {"Proxy info", "Information sur le proxy"}, - {"Proxy error: Host not found", "Erreur de proxy: Hôte introuvable"}, + {"Proxy error: Host not found", "Erreur de proxy : Hôte introuvable"}, {"Remote host not found in router's addressbook", "Hôte distant introuvable dans le carnet d'adresse du routeur"}, {"You may try to find this host on jump services below", "Vous pouvez essayer de trouver cet hôte sur des services de redirection ci-dessous"}, {"Invalid request", "Requête invalide"}, {"Proxy unable to parse your request", "Proxy incapable de comprendre votre requête"}, {"Addresshelper is not supported", "Assistant d'adresse non supporté"}, - {"Host %s is already in router's addressbook. Be careful: source of this URL may be harmful! Click here to update record: Continue.", "L'hôte %s est déjà dans le carnet d'adresses du routeur. Attention : la source de cette URL peut être nuisible ! Cliquez ici pour mettre à jour l'enregistrement : Continuer."}, + {"Host %s is already in router's addressbook. Be careful: source of this URL may be harmful! Click here to update record: Continue.", "L'hôte %s est déjà dans le carnet d'adresses du routeur. Attention : la source de cette URL peut être nuisible ! Cliquez ici pour mettre à jour l'enregistrement : Continuer."}, {"Addresshelper forced update rejected", "Mise à jour forcée des assistants d'adresses rejetée"}, - {"To add host %s in router's addressbook, click here: Continue.", "Pour ajouter l'hôte %s au carnet d'adresses du routeur, cliquez ici : Continuer."}, + {"To add host %s in router's addressbook, click here: Continue.", "Pour ajouter l'hôte %s au carnet d'adresses du routeur, cliquez ici : Continuer."}, {"Addresshelper request", "Demande à l'assistant d'adresse"}, {"Host %s added to router's addressbook from helper. Click here to proceed: Continue.", "L'hôte %s a été ajouté au carnet d'adresses du routeur depuis l'assistant. Cliquez ici pour continuer : Continuer."}, {"Addresshelper adding", "Ajout de l'assistant d'adresse"}, - {"Host %s is already in router's addressbook. Click here to update record: Continue.", "L'hôte %s est déjà dans le carnet d'adresses du routeur. Cliquez ici pour mettre à jour le dossier : Continuer."}, + {"Host %s is already in router's addressbook. Click here to update record: Continue.", "L'hôte %s est déjà dans le carnet d'adresses du routeur. Cliquez ici pour mettre à jour le dossier : Continuer."}, {"Addresshelper update", "Mise à jour de l'assistant d'adresse"}, {"Invalid request URI", "URI de la requête invalide"}, {"Can't detect destination host from request", "Impossible de détecter l'hôte de destination à partir de la requête"}, diff --git a/i18n/Italian.cpp b/i18n/Italian.cpp index 386372d5..2dcaab5e 100644 --- a/i18n/Italian.cpp +++ b/i18n/Italian.cpp @@ -69,6 +69,7 @@ namespace italian // language namespace {"Stopping in", "Arresto in"}, {"Family", "Famiglia"}, {"Tunnel creation success rate", "Percentuale di tunnel creati con successo"}, + {"Total tunnel creation success rate", "Percentuale di successo totale nella creazione del tunnel"}, {"Received", "Ricevuti"}, {"%.2f KiB/s", "%.2f KiB/s"}, {"Sent", "Inviati"}, @@ -95,6 +96,7 @@ namespace italian // language namespace {"Address", "Indirizzo"}, {"Type", "Tipologia"}, {"EncType", "Tipo di crittografia"}, + {"Expire LeaseSet", "Scadenza LeaseSet"}, {"Inbound tunnels", "Tunnel in entrata"}, {"%dms", "%dms"}, {"Outbound tunnels", "Tunnel in uscita"}, @@ -109,6 +111,7 @@ namespace italian // language namespace {"Local Destination", "Destinazione locale"}, {"Streams", "Flussi"}, {"Close stream", "Interrompi il flusso"}, + {"Such destination is not found", "Questa destinazione non è stata trovata"}, {"I2CP session not found", "Sessione I2CP non trovata"}, {"I2CP is not enabled", "I2CP non è abilitato"}, {"Invalid", "Invalido"}, @@ -150,6 +153,8 @@ namespace italian // language namespace {"StreamID can't be null", "Lo StreamID non può essere null"}, {"Return to destination page", "Ritorna alla pagina di destinazione"}, {"You will be redirected in %d seconds", "Sarai reindirizzato tra %d secondi"}, + {"LeaseSet expiration time updated", "Tempo di scadenza LeaseSet aggiornato"}, + {"LeaseSet is not found or already expired", "LeaseSet non trovato o già scaduto"}, {"Transit tunnels count must not exceed %d", "Il conteggio dei tunnel di transito non deve superare %d"}, {"Back to commands list", "Ritorna alla lista dei comandi"}, {"Register at reg.i2p", "Registra a reg.i2p"}, @@ -158,7 +163,6 @@ namespace italian // language namespace {"Submit", "Invia"}, {"Domain can't end with .b32.i2p", "I domini non possono terminare con .b32.i2p"}, {"Domain must end with .i2p", "I domini devono terminare con .i2p"}, - {"Such destination is not found", "Questa destinazione non è stata trovata"}, {"Unknown command", "Comando sconosciuto"}, {"Command accepted", "Comando accettato"}, {"Proxy error", "Errore del proxy"}, diff --git a/i18n/Portuguese.cpp b/i18n/Portuguese.cpp index 8ba7f0cd..4c6d749f 100644 --- a/i18n/Portuguese.cpp +++ b/i18n/Portuguese.cpp @@ -69,6 +69,7 @@ namespace portuguese // language namespace {"Stopping in", "Parando em"}, {"Family", "Família"}, {"Tunnel creation success rate", "Taxa de sucesso na criação de túneis"}, + {"Total tunnel creation success rate", "Taxa total de sucesso na criação de túneis"}, {"Received", "Recebido"}, {"%.2f KiB/s", "%.2f KiB/s"}, {"Sent", "Enviado"}, @@ -95,6 +96,7 @@ namespace portuguese // language namespace {"Address", "Endereço"}, {"Type", "Tipo"}, {"EncType", "Tipo de Criptografia"}, + {"Expire LeaseSet", "Expirar LeaseSet"}, {"Inbound tunnels", "Túneis de Entrada"}, {"%dms", "%dms"}, {"Outbound tunnels", "Túneis de Saída"}, @@ -109,6 +111,7 @@ namespace portuguese // language namespace {"Local Destination", "Destinos Locais"}, {"Streams", "Fluxos"}, {"Close stream", "Fechar fluxo"}, + {"Such destination is not found", "Tal destino não foi encontrado"}, {"I2CP session not found", "Sessão do I2CP não encontrada"}, {"I2CP is not enabled", "I2CP não está ativado"}, {"Invalid", "Inválido"}, @@ -150,6 +153,8 @@ namespace portuguese // language namespace {"StreamID can't be null", "StreamID não pode ser nulo"}, {"Return to destination page", "Retornar para à página de destino"}, {"You will be redirected in %d seconds", "Você será redirecionado em %d segundos"}, + {"LeaseSet expiration time updated", "Tempo de validade do LeaseSet atualizado"}, + {"LeaseSet is not found or already expired", "LeaseSet não foi encontrado ou já expirou"}, {"Transit tunnels count must not exceed %d", "A contagem de túneis de trânsito não deve exceder %d"}, {"Back to commands list", "Voltar para a lista de comandos"}, {"Register at reg.i2p", "Registrar na reg.i2p"}, @@ -158,7 +163,6 @@ namespace portuguese // language namespace {"Submit", "Enviar"}, {"Domain can't end with .b32.i2p", "O domínio não pode terminar com .b32.i2p"}, {"Domain must end with .i2p", "O domínio não pode terminar com .i2p"}, - {"Such destination is not found", "Tal destino não foi encontrado"}, {"Unknown command", "Comando desconhecido"}, {"Command accepted", "Comando aceito"}, {"Proxy error", "Erro no proxy"}, diff --git a/i18n/Russian.cpp b/i18n/Russian.cpp index dbfb13f2..15952710 100644 --- a/i18n/Russian.cpp +++ b/i18n/Russian.cpp @@ -69,6 +69,7 @@ namespace russian // language namespace {"Stopping in", "Остановка через"}, {"Family", "Семейство"}, {"Tunnel creation success rate", "Успешно построенных туннелей"}, + {"Total tunnel creation success rate", "Общий процент успешно построенных туннелей"}, {"Received", "Получено"}, {"%.2f KiB/s", "%.2f КиБ/с"}, {"Sent", "Отправлено"}, @@ -95,6 +96,7 @@ namespace russian // language namespace {"Address", "Адрес"}, {"Type", "Тип"}, {"EncType", "ТипШифр"}, + {"Expire LeaseSet", "Просрочить Лизсет"}, {"Inbound tunnels", "Входящие туннели"}, {"%dms", "%dмс"}, {"Outbound tunnels", "Исходящие туннели"}, @@ -109,6 +111,7 @@ namespace russian // language namespace {"Local Destination", "Локальное назначение"}, {"Streams", "Стримы"}, {"Close stream", "Закрыть стрим"}, + {"Such destination is not found", "Такая точка назначения не найдена"}, {"I2CP session not found", "I2CP сессия не найдена"}, {"I2CP is not enabled", "I2CP не включен"}, {"Invalid", "Некорректный"}, @@ -150,6 +153,8 @@ namespace russian // language namespace {"StreamID can't be null", "StreamID не может быть пустым"}, {"Return to destination page", "Вернуться на страницу точки назначения"}, {"You will be redirected in %d seconds", "Вы будете переадресованы через %d секунд"}, + {"LeaseSet expiration time updated", "Время действия LeaseSet обновлено"}, + {"LeaseSet is not found or already expired", "Лизсет не найден или время действия уже истекло"}, {"Transit tunnels count must not exceed %d", "Число транзитных туннелей не должно превышать %d"}, {"Back to commands list", "Вернуться к списку команд"}, {"Register at reg.i2p", "Зарегистрировать на reg.i2p"}, @@ -158,7 +163,6 @@ namespace russian // language namespace {"Submit", "Отправить"}, {"Domain can't end with .b32.i2p", "Домен не может заканчиваться на .b32.i2p"}, {"Domain must end with .i2p", "Домен должен заканчиваться на .i2p"}, - {"Such destination is not found", "Такая точка назначения не найдена"}, {"Unknown command", "Неизвестная команда"}, {"Command accepted", "Команда принята"}, {"Proxy error", "Ошибка прокси"}, diff --git a/i18n/Swedish.cpp b/i18n/Swedish.cpp index e7f84e69..05ed1e18 100644 --- a/i18n/Swedish.cpp +++ b/i18n/Swedish.cpp @@ -61,6 +61,8 @@ namespace swedish // language namespace {"Clock skew", "Tidsförskjutning"}, {"Offline", "Nedkopplad"}, {"Symmetric NAT", "Symmetrisk NAT"}, + {"Full cone NAT", "Full kon NAT"}, + {"No Descriptors", "Inga Beskrivningar"}, {"Uptime", "Upptid"}, {"Network status", "Nätverkstillstånd"}, {"Network status v6", "Nätverkstillstånd v6"}, @@ -107,6 +109,7 @@ namespace swedish // language namespace {"Local Destination", "Lokal Plats"}, {"Streams", "Strömmar"}, {"Close stream", "Stäng strömmen"}, + {"Such destination is not found", "En sådan plats hittas ej"}, {"I2CP session not found", "I2CP-period hittades inte"}, {"I2CP is not enabled", "I2CP är inte påslaget"}, {"Invalid", "Ogiltig"}, @@ -116,8 +119,10 @@ namespace swedish // language namespace {"Gateway", "Gateway"}, {"TunnelID", "TunnelID"}, {"EndDate", "EndDate"}, + {"floodfill mode is disabled", "Floodfill läget är inaktiverat"}, {"Queue size", "Köstorlek"}, {"Run peer test", "Utför utsiktstest"}, + {"Reload tunnels configuration", "Ladda om tunnelkonfiguration"}, {"Decline transit tunnels", "Avvisa förmedlande tunnlar"}, {"Accept transit tunnels", "Tillåt förmedlande tunnlar"}, {"Cancel graceful shutdown", "Avbryt välvillig avstängning"}, @@ -154,7 +159,6 @@ namespace swedish // language namespace {"Submit", "Skicka"}, {"Domain can't end with .b32.i2p", "Domänen får inte sluta med .b32.i2p"}, {"Domain must end with .i2p", "Domänen måste sluta med .i2p"}, - {"Such destination is not found", "En sådan plats hittas ej"}, {"Unknown command", "Okänt kommando"}, {"Command accepted", "Kommando accepterades"}, {"Proxy error", "Proxyfel"}, @@ -165,6 +169,14 @@ namespace swedish // language namespace {"Invalid request", "Ogiltig förfrågan"}, {"Proxy unable to parse your request", "Proxyt kan inte behandla din förfrågan"}, {"Addresshelper is not supported", "Adresshjälparen stöds ej"}, + {"Host %s is already in router's addressbook. Be careful: source of this URL may be harmful! Click here to update record: Continue.", "Värd %s är redan i routerns adressbok. Var försiktig: källan till denna URL kan vara skadlig! Klicka här för att uppdatera registreringen: Fortsätt."}, + {"Addresshelper forced update rejected", "Tvingad uppdatering av adresshjälparen nekad"}, + {"To add host %s in router's addressbook, click here: Continue.", "För att lägga till värd %s i routerns adressbok, klicka här: Fortsätt."}, + {"Addresshelper request", "Adresshjälpare förfrågan"}, + {"Host %s added to router's addressbook from helper. Click here to proceed: Continue.", "Värd %s tillagd i routerns adressbok från hjälparen. Klicka här för att fortsätta: Fortsätt."}, + {"Addresshelper adding", "Adresshjälpare tilläggning"}, + {"Host %s is already in router's addressbook. Click here to update record: Continue.", "Värd %s är redan i routerns adressbok. Klicka här för att uppdatera registreringen: Fortsätt."}, + {"Addresshelper update", "Adresshjälpare uppdatering"}, {"Invalid request URI", "Ogiltig förfrågnings-URI"}, {"Can't detect destination host from request", "Kan inte upptäcka platsvärden från förfrågan"}, {"Outproxy failure", "Utproxyfel"}, diff --git a/i18n/Ukrainian.cpp b/i18n/Ukrainian.cpp index c1e222ea..d089c142 100644 --- a/i18n/Ukrainian.cpp +++ b/i18n/Ukrainian.cpp @@ -69,6 +69,7 @@ namespace ukrainian // language namespace {"Stopping in", "Зупинка через"}, {"Family", "Сімейство"}, {"Tunnel creation success rate", "Успішно побудованих тунелів"}, + {"Total tunnel creation success rate", "Загальна кількість створених тунелів"}, {"Received", "Отримано"}, {"%.2f KiB/s", "%.2f КіБ/с"}, {"Sent", "Відправлено"}, @@ -95,6 +96,7 @@ namespace ukrainian // language namespace {"Address", "Адреса"}, {"Type", "Тип"}, {"EncType", "ТипШифр"}, + {"Expire LeaseSet", "Завершити LeaseSet"}, {"Inbound tunnels", "Вхідні тунелі"}, {"%dms", "%dмс"}, {"Outbound tunnels", "Вихідні тунелі"}, @@ -109,6 +111,7 @@ namespace ukrainian // language namespace {"Local Destination", "Локальні Призначення"}, {"Streams", "Потоки"}, {"Close stream", "Закрити потік"}, + {"Such destination is not found", "Така точка призначення не знайдена"}, {"I2CP session not found", "I2CP сесія не знайдена"}, {"I2CP is not enabled", "I2CP не увікнуто"}, {"Invalid", "Некоректний"}, @@ -150,6 +153,8 @@ namespace ukrainian // language namespace {"StreamID can't be null", "Ідентифікатор потоку не може бути порожнім"}, {"Return to destination page", "Повернутися на сторінку точки призначення"}, {"You will be redirected in %d seconds", "Ви будете переадресовані через %d секунд"}, + {"LeaseSet expiration time updated", "Час закінчення LeaseSet оновлено"}, + {"LeaseSet is not found or already expired", "LeaseSet не знайдено або вже закінчився"}, {"Transit tunnels count must not exceed %d", "Кількість транзитних тунелів не повинна перевищувати %d"}, {"Back to commands list", "Повернутися до списку команд"}, {"Register at reg.i2p", "Зареєструвати на reg.i2p"}, @@ -158,7 +163,6 @@ namespace ukrainian // language namespace {"Submit", "Надіслати"}, {"Domain can't end with .b32.i2p", "Домен не може закінчуватися на .b32.i2p"}, {"Domain must end with .i2p", "Домен повинен закінчуватися на .i2p"}, - {"Such destination is not found", "Така точка призначення не знайдена"}, {"Unknown command", "Невідома команда"}, {"Command accepted", "Команда прийнята"}, {"Proxy error", "Помилка проксі"}, diff --git a/i18n/Uzbek.cpp b/i18n/Uzbek.cpp index 9d798be4..cf94a489 100644 --- a/i18n/Uzbek.cpp +++ b/i18n/Uzbek.cpp @@ -69,6 +69,7 @@ namespace uzbek // language namespace {"Stopping in", "Ichida to'xtatish"}, {"Family", "Oila"}, {"Tunnel creation success rate", "Tunnel yaratish muvaffaqiyat darajasi"}, + {"Total tunnel creation success rate", "Tunnel yaratishning umumiy muvaffaqiyat darajasi"}, {"Received", "Qabul qilindi"}, {"%.2f KiB/s", "%.2f KiB/s"}, {"Sent", "Yuborilgan"}, @@ -95,6 +96,7 @@ namespace uzbek // language namespace {"Address", "Manzil"}, {"Type", "Turi"}, {"EncType", "ShifrlashTuri"}, + {"Expire LeaseSet", "LeaseSet muddati tugaydi"}, {"Inbound tunnels", "Kirish tunnellari"}, {"%dms", "%dms"}, {"Outbound tunnels", "Chiquvchi tunnellar"}, @@ -109,6 +111,7 @@ namespace uzbek // language namespace {"Local Destination", "Mahalliy joylanish"}, {"Streams", "Strim"}, {"Close stream", "Strimni o'chirish"}, + {"Such destination is not found", "Bunday yo'nalish topilmadi"}, {"I2CP session not found", "I2CP sessiyasi topilmadi"}, {"I2CP is not enabled", "I2CP yoqilmagan"}, {"Invalid", "Noto'g'ri"}, @@ -150,6 +153,8 @@ namespace uzbek // language namespace {"StreamID can't be null", "StreamID bo'sh bo'lishi mumkin emas"}, {"Return to destination page", "Manzilgoh sahifasiga qaytish"}, {"You will be redirected in %d seconds", "Siz %d soniyadan so‘ng boshqa yo‘nalishga yo‘naltirilasiz"}, + {"LeaseSet expiration time updated", "LeaseSet amal qilish muddati yangilandi"}, + {"LeaseSet is not found or already expired", "LeaseSet topilmadi yoki muddati tugagan"}, {"Transit tunnels count must not exceed %d", "Tranzit tunnellar soni %d dan oshmasligi kerak"}, {"Back to commands list", "Buyruqlar ro'yxatiga qaytish"}, {"Register at reg.i2p", "Reg.i2p-da ro'yxatdan o'ting"}, @@ -158,7 +163,6 @@ namespace uzbek // language namespace {"Submit", "Yuborish"}, {"Domain can't end with .b32.i2p", "Domen .b32.i2p bilan tugashi mumkin emas"}, {"Domain must end with .i2p", "Domen .i2p bilan tugashi kerak"}, - {"Such destination is not found", "Bunday yo'nalish topilmadi"}, {"Unknown command", "Noma'lum buyruq"}, {"Command accepted", "Buyruq qabul qilindi"}, {"Proxy error", "Proksi xatosi"}, diff --git a/libi2pd/Base.cpp b/libi2pd/Base.cpp index 94446e86..b8de571b 100644 --- a/libi2pd/Base.cpp +++ b/libi2pd/Base.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2022, The PurpleI2P Project +* Copyright (c) 2013-2023, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -28,6 +28,11 @@ namespace data return T32; } + bool IsBase32 (char ch) + { + return (ch >= 'a' && ch <= 'z') || (ch >= '2' && ch <= '7'); + } + static void iT64Build(void); /* @@ -55,6 +60,11 @@ namespace data return T64; } + bool IsBase64 (char ch) + { + return (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') || (ch >= '0' && ch <= '9') || ch == '-' || ch == '~'; + } + /* * Reverse Substitution Table (built in run time) */ @@ -187,6 +197,9 @@ namespace data else return 0; + if(*InBuffer == P64) + return 0; + ps = (unsigned char *)(InBuffer + InCount - 1); while ( *ps-- == P64 ) outCount--; @@ -269,7 +282,7 @@ namespace data size_t Base32ToByteStream (const char * inBuf, size_t len, uint8_t * outBuf, size_t outLen) { - int tmp = 0, bits = 0; + unsigned int tmp = 0, bits = 0; size_t ret = 0; for (size_t i = 0; i < len; i++) { @@ -298,7 +311,7 @@ namespace data size_t ByteStreamToBase32 (const uint8_t * inBuf, size_t len, char * outBuf, size_t outLen) { size_t ret = 0, pos = 1; - int bits = 8, tmp = inBuf[0]; + unsigned int bits = 8, tmp = inBuf[0]; while (ret < outLen && (bits > 0 || pos < len)) { if (bits < 5) diff --git a/libi2pd/Base.h b/libi2pd/Base.h index 79152e02..a163435c 100644 --- a/libi2pd/Base.h +++ b/libi2pd/Base.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2020, The PurpleI2P Project +* Copyright (c) 2013-2023, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -19,9 +19,11 @@ namespace data { size_t Base64ToByteStream (const char * InBuffer, size_t InCount, uint8_t * OutBuffer, size_t len ); const char * GetBase32SubstitutionTable (); const char * GetBase64SubstitutionTable (); + bool IsBase64 (char ch); size_t Base32ToByteStream (const char * inBuf, size_t len, uint8_t * outBuf, size_t outLen); size_t ByteStreamToBase32 (const uint8_t * InBuf, size_t len, char * outBuf, size_t outLen); + bool IsBase32 (char ch); /** * Compute the size for a buffer to contain encoded base64 given that the size of the input is input_size bytes diff --git a/libi2pd/CPU.cpp b/libi2pd/CPU.cpp index 0804e2ac..77820c88 100644 --- a/libi2pd/CPU.cpp +++ b/libi2pd/CPU.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2020, The PurpleI2P Project +* Copyright (c) 2013-2023, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -7,52 +7,62 @@ */ #include "CPU.h" -#if defined(__x86_64__) || defined(__i386__) -#include -#endif #include "Log.h" #ifndef bit_AES -#define bit_AES (1 << 25) -#endif -#ifndef bit_AVX -#define bit_AVX (1 << 28) + #define bit_AES (1 << 25) #endif +#if defined(__GNUC__) && __GNUC__ < 6 && IS_X86 + #include +#endif + +#ifdef _MSC_VER + #include +#endif namespace i2p { namespace cpu { bool aesni = false; - bool avx = false; - void Detect(bool AesSwitch, bool AvxSwitch, bool force) + inline bool cpu_support_aes() { -#if defined(__x86_64__) || defined(__i386__) - int info[4]; - __cpuid(0, info[0], info[1], info[2], info[3]); - if (info[0] >= 0x00000001) { - __cpuid(0x00000001, info[0], info[1], info[2], info[3]); -#if defined (_WIN32) && (WINVER == 0x0501) // WinXP - if (AesSwitch && force) { // only if forced -#else - if ((info[2] & bit_AES && AesSwitch) || (AesSwitch && force)) { -#endif - aesni = true; - } -#if defined (_WIN32) && (WINVER == 0x0501) // WinXP - if (AvxSwitch && force) { // only if forced -#else - if ((info[2] & bit_AVX && AvxSwitch) || (AvxSwitch && force)) { -#endif - avx = true; - } +#if IS_X86 +#if defined(__clang__) +# if (__clang_major__ >= 6) + __builtin_cpu_init(); +# endif + return __builtin_cpu_supports("aes"); +#elif (defined(__GNUC__) && __GNUC__ >= 6) + __builtin_cpu_init(); + return __builtin_cpu_supports("aes"); +#elif (defined(__GNUC__) && __GNUC__ < 6) + int cpu_info[4]; + bool flag = false; + __cpuid(0, cpu_info[0], cpu_info[1], cpu_info[2], cpu_info[3]); + if (cpu_info[0] >= 0x00000001) { + __cpuid(0x00000001, cpu_info[0], cpu_info[1], cpu_info[2], cpu_info[3]); + flag = ((cpu_info[2] & bit_AES) != 0); + } + return flag; +#elif defined(_MSC_VER) + int cpu_info[4]; + __cpuid(cpu_info, 1); + return ((cpu_info[2] & bit_AES) != 0); +#endif +#endif + return false; + } + + void Detect(bool AesSwitch, bool force) + { + if ((cpu_support_aes() && AesSwitch) || (AesSwitch && force)) { + aesni = true; } -#endif // defined(__x86_64__) || defined(__i386__) LogPrint(eLogInfo, "AESNI ", (aesni ? "enabled" : "disabled")); - LogPrint(eLogInfo, "AVX ", (avx ? "enabled" : "disabled")); } } } diff --git a/libi2pd/CPU.h b/libi2pd/CPU.h index f021bccb..1c30db48 100644 --- a/libi2pd/CPU.h +++ b/libi2pd/CPU.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2020, The PurpleI2P Project +* Copyright (c) 2013-2023, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -9,14 +9,31 @@ #ifndef LIBI2PD_CPU_H #define LIBI2PD_CPU_H +#if defined(_M_AMD64) || defined(__x86_64__) || defined(_M_IX86) || defined(__i386__) +# define IS_X86 1 +# if defined(_M_AMD64) || defined(__x86_64__) +# define IS_X86_64 1 +# else +# define IS_X86_64 0 +# endif +#else +# define IS_X86 0 +# define IS_X86_64 0 +#endif + +#if defined(__AES__) && !defined(_MSC_VER) && IS_X86 +# define SUPPORTS_AES 1 +#else +# define SUPPORTS_AES 0 +#endif + namespace i2p { namespace cpu { extern bool aesni; - extern bool avx; - void Detect(bool AesSwitch, bool AvxSwitch, bool force); + void Detect(bool AesSwitch, bool force); } } diff --git a/libi2pd/Config.cpp b/libi2pd/Config.cpp index 39af997b..a6885178 100644 --- a/libi2pd/Config.cpp +++ b/libi2pd/Config.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2023, The PurpleI2P Project +* Copyright (c) 2013-2024, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -77,7 +77,8 @@ namespace config { limits.add_options() ("limits.coresize", value()->default_value(0), "Maximum size of corefile in Kb (0 - use system limit)") ("limits.openfiles", value()->default_value(0), "Maximum number of open files (0 - use system default)") - ("limits.transittunnels", value()->default_value(5000), "Maximum active transit tunnels (default:5000)") + ("limits.transittunnels", value()->default_value(10000), "Maximum active transit tunnels (default:10000)") + ("limits.zombies", value()->default_value(0), "Minimum percentage of successfully created tunnels under which tunnel cleanup is paused (default [%]: 0.00)") ("limits.ntcpsoft", value()->default_value(0), "Ignored") ("limits.ntcphard", value()->default_value(0), "Ignored") ("limits.ntcpthreads", value()->default_value(1), "Ignored") @@ -192,7 +193,7 @@ namespace config { options_description precomputation("Precomputation options"); precomputation.add_options() ("precomputation.elgamal", -#if defined(__x86_64__) +#if (defined(_M_AMD64) || defined(__x86_64__)) value()->default_value(false), #else value()->default_value(true), @@ -220,14 +221,16 @@ namespace config { "https://reseed-pl.i2pd.xyz/," "https://www2.mk16.de/," "https://i2p.ghativega.in/," - "https://i2p.novg.net/" + "https://i2p.novg.net/," + "https://reseed.stormycloud.org/" ), "Reseed URLs, separated by comma") ("reseed.yggurls", value()->default_value( "http://[324:71e:281a:9ed3::ace]:7070/," "http://[301:65b9:c7cd:9a36::1]:18801/," "http://[320:8936:ec1a:31f1::216]/," "http://[306:3834:97b9:a00a::1]/," - "http://[316:f9e0:f22e:a74f::216]/" + "http://[316:f9e0:f22e:a74f::216]/," + "http://[300:eaff:7fab:181b::e621]:7170" ), "Reseed URLs through the Yggdrasil, separated by comma") ; @@ -307,7 +310,7 @@ namespace config { options_description cpuext("CPU encryption extensions options"); cpuext.add_options() ("cpuext.aesni", bool_switch()->default_value(true), "Use auto detection for AESNI CPU extensions. If false, AESNI will be not used") - ("cpuext.avx", bool_switch()->default_value(true), "Use auto detection for AVX CPU extensions. If false, AVX will be not used") + ("cpuext.avx", bool_switch()->default_value(false), "Deprecated option") ("cpuext.force", bool_switch()->default_value(false), "Force usage of CPU extensions. Useful when cpuinfo is not available on virtual machines") ; diff --git a/libi2pd/Crypto.cpp b/libi2pd/Crypto.cpp index c9670f6c..3e5bdd35 100644 --- a/libi2pd/Crypto.cpp +++ b/libi2pd/Crypto.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2022, The PurpleI2P Project +* Copyright (c) 2013-2023, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -28,6 +28,7 @@ #include "I2PEndian.h" #include "Log.h" + namespace i2p { namespace crypto @@ -159,7 +160,7 @@ namespace crypto // DH/ElGamal -#if !defined(__x86_64__) +#if !IS_X86_64 const int ELGAMAL_SHORT_EXPONENT_NUM_BITS = 226; const int ELGAMAL_SHORT_EXPONENT_NUM_BYTES = ELGAMAL_SHORT_EXPONENT_NUM_BITS/8+1; #endif @@ -361,7 +362,7 @@ namespace crypto BIGNUM * b1 = BN_CTX_get (ctx); BIGNUM * b = BN_CTX_get (ctx); // select random k -#if defined(__x86_64__) +#if IS_X86_64 BN_rand (k, ELGAMAL_FULL_EXPONENT_NUM_BITS, -1, 1); // full exponent for x64 #else BN_rand (k, ELGAMAL_SHORT_EXPONENT_NUM_BITS, -1, 1); // short exponent of 226 bits @@ -428,7 +429,7 @@ namespace crypto void GenerateElGamalKeyPair (uint8_t * priv, uint8_t * pub) { -#if defined(__x86_64__) || defined(__i386__) || defined(_MSC_VER) +#if IS_X86 || defined(_MSC_VER) RAND_bytes (priv, 256); #else // lower 226 bits (28 bytes and 2 bits) only. short exponent @@ -555,7 +556,7 @@ namespace crypto } // AES -#ifdef __AES__ +#if SUPPORTS_AES #define KeyExpansion256(round0,round1) \ "pshufd $0xff, %%xmm2, %%xmm2 \n" \ "movaps %%xmm1, %%xmm4 \n" \ @@ -580,7 +581,7 @@ namespace crypto "movaps %%xmm3, "#round1"(%[sched]) \n" #endif -#ifdef __AES__ +#if SUPPORTS_AES void ECBCryptoAESNI::ExpandKey (const AESKey& key) { __asm__ @@ -621,7 +622,7 @@ namespace crypto #endif -#ifdef __AES__ +#if SUPPORTS_AES #define EncryptAES256(sched) \ "pxor (%["#sched"]), %%xmm0 \n" \ "aesenc 16(%["#sched"]), %%xmm0 \n" \ @@ -642,16 +643,18 @@ namespace crypto void ECBEncryption::Encrypt (const ChipherBlock * in, ChipherBlock * out) { -#ifdef __AES__ +#if SUPPORTS_AES if(i2p::cpu::aesni) { __asm__ - ( - "movups (%[in]), %%xmm0 \n" - EncryptAES256(sched) - "movups %%xmm0, (%[out]) \n" - : : [sched]"r"(GetKeySchedule ()), [in]"r"(in), [out]"r"(out) : "%xmm0", "memory" - ); + ( + "movups (%[in]), %%xmm0 \n" + EncryptAES256(sched) + "movups %%xmm0, (%[out]) \n" + : + : [sched]"r"(GetKeySchedule ()), [in]"r"(in), [out]"r"(out) + : "%xmm0", "memory" + ); } else #endif @@ -660,7 +663,7 @@ namespace crypto } } -#ifdef __AES__ +#if SUPPORTS_AES #define DecryptAES256(sched) \ "pxor 224(%["#sched"]), %%xmm0 \n" \ "aesdec 208(%["#sched"]), %%xmm0 \n" \ @@ -681,16 +684,18 @@ namespace crypto void ECBDecryption::Decrypt (const ChipherBlock * in, ChipherBlock * out) { -#ifdef __AES__ +#if SUPPORTS_AES if(i2p::cpu::aesni) { __asm__ - ( - "movups (%[in]), %%xmm0 \n" - DecryptAES256(sched) - "movups %%xmm0, (%[out]) \n" - : : [sched]"r"(GetKeySchedule ()), [in]"r"(in), [out]"r"(out) : "%xmm0", "memory" - ); + ( + "movups (%[in]), %%xmm0 \n" + DecryptAES256(sched) + "movups %%xmm0, (%[out]) \n" + : + : [sched]"r"(GetKeySchedule ()), [in]"r"(in), [out]"r"(out) + : "%xmm0", "memory" + ); } else #endif @@ -699,7 +704,7 @@ namespace crypto } } -#ifdef __AES__ +#if SUPPORTS_AES #define CallAESIMC(offset) \ "movaps "#offset"(%[shed]), %%xmm0 \n" \ "aesimc %%xmm0, %%xmm0 \n" \ @@ -708,7 +713,7 @@ namespace crypto void ECBEncryption::SetKey (const AESKey& key) { -#ifdef __AES__ +#if SUPPORTS_AES if(i2p::cpu::aesni) { ExpandKey (key); @@ -722,28 +727,30 @@ namespace crypto void ECBDecryption::SetKey (const AESKey& key) { -#ifdef __AES__ +#if SUPPORTS_AES if(i2p::cpu::aesni) { ExpandKey (key); // expand encryption key first // then invert it using aesimc __asm__ - ( - CallAESIMC(16) - CallAESIMC(32) - CallAESIMC(48) - CallAESIMC(64) - CallAESIMC(80) - CallAESIMC(96) - CallAESIMC(112) - CallAESIMC(128) - CallAESIMC(144) - CallAESIMC(160) - CallAESIMC(176) - CallAESIMC(192) - CallAESIMC(208) - : : [shed]"r"(GetKeySchedule ()) : "%xmm0", "memory" - ); + ( + CallAESIMC(16) + CallAESIMC(32) + CallAESIMC(48) + CallAESIMC(64) + CallAESIMC(80) + CallAESIMC(96) + CallAESIMC(112) + CallAESIMC(128) + CallAESIMC(144) + CallAESIMC(160) + CallAESIMC(176) + CallAESIMC(192) + CallAESIMC(208) + : + : [shed]"r"(GetKeySchedule ()) + : "%xmm0", "memory" + ); } else #endif @@ -754,28 +761,28 @@ namespace crypto void CBCEncryption::Encrypt (int numBlocks, const ChipherBlock * in, ChipherBlock * out) { -#ifdef __AES__ +#if SUPPORTS_AES if(i2p::cpu::aesni) { __asm__ - ( - "movups (%[iv]), %%xmm1 \n" - "1: \n" - "movups (%[in]), %%xmm0 \n" - "pxor %%xmm1, %%xmm0 \n" - EncryptAES256(sched) - "movaps %%xmm0, %%xmm1 \n" - "movups %%xmm0, (%[out]) \n" - "add $16, %[in] \n" - "add $16, %[out] \n" - "dec %[num] \n" - "jnz 1b \n" - "movups %%xmm1, (%[iv]) \n" - : - : [iv]"r"((uint8_t *)m_LastBlock), [sched]"r"(m_ECBEncryption.GetKeySchedule ()), - [in]"r"(in), [out]"r"(out), [num]"r"(numBlocks) - : "%xmm0", "%xmm1", "cc", "memory" - ); + ( + "movups (%[iv]), %%xmm1 \n" + "1: \n" + "movups (%[in]), %%xmm0 \n" + "pxor %%xmm1, %%xmm0 \n" + EncryptAES256(sched) + "movaps %%xmm0, %%xmm1 \n" + "movups %%xmm0, (%[out]) \n" + "add $16, %[in] \n" + "add $16, %[out] \n" + "dec %[num] \n" + "jnz 1b \n" + "movups %%xmm1, (%[iv]) \n" + : + : [iv]"r"((uint8_t *)m_LastBlock), [sched]"r"(m_ECBEncryption.GetKeySchedule ()), + [in]"r"(in), [out]"r"(out), [num]"r"(numBlocks) + : "%xmm0", "%xmm1", "cc", "memory" + ); } else #endif @@ -799,22 +806,22 @@ namespace crypto void CBCEncryption::Encrypt (const uint8_t * in, uint8_t * out) { -#ifdef __AES__ +#if SUPPORTS_AES if(i2p::cpu::aesni) { __asm__ - ( - "movups (%[iv]), %%xmm1 \n" - "movups (%[in]), %%xmm0 \n" - "pxor %%xmm1, %%xmm0 \n" - EncryptAES256(sched) - "movups %%xmm0, (%[out]) \n" - "movups %%xmm0, (%[iv]) \n" - : - : [iv]"r"((uint8_t *)m_LastBlock), [sched]"r"(m_ECBEncryption.GetKeySchedule ()), - [in]"r"(in), [out]"r"(out) - : "%xmm0", "%xmm1", "memory" - ); + ( + "movups (%[iv]), %%xmm1 \n" + "movups (%[in]), %%xmm0 \n" + "pxor %%xmm1, %%xmm0 \n" + EncryptAES256(sched) + "movups %%xmm0, (%[out]) \n" + "movups %%xmm0, (%[iv]) \n" + : + : [iv]"r"((uint8_t *)m_LastBlock), [sched]"r"(m_ECBEncryption.GetKeySchedule ()), + [in]"r"(in), [out]"r"(out) + : "%xmm0", "%xmm1", "memory" + ); } else #endif @@ -823,29 +830,29 @@ namespace crypto void CBCDecryption::Decrypt (int numBlocks, const ChipherBlock * in, ChipherBlock * out) { -#ifdef __AES__ +#if SUPPORTS_AES if(i2p::cpu::aesni) { __asm__ - ( - "movups (%[iv]), %%xmm1 \n" - "1: \n" - "movups (%[in]), %%xmm0 \n" - "movaps %%xmm0, %%xmm2 \n" - DecryptAES256(sched) - "pxor %%xmm1, %%xmm0 \n" - "movups %%xmm0, (%[out]) \n" - "movaps %%xmm2, %%xmm1 \n" - "add $16, %[in] \n" - "add $16, %[out] \n" - "dec %[num] \n" - "jnz 1b \n" - "movups %%xmm1, (%[iv]) \n" - : - : [iv]"r"((uint8_t *)m_IV), [sched]"r"(m_ECBDecryption.GetKeySchedule ()), - [in]"r"(in), [out]"r"(out), [num]"r"(numBlocks) - : "%xmm0", "%xmm1", "%xmm2", "cc", "memory" - ); + ( + "movups (%[iv]), %%xmm1 \n" + "1: \n" + "movups (%[in]), %%xmm0 \n" + "movaps %%xmm0, %%xmm2 \n" + DecryptAES256(sched) + "pxor %%xmm1, %%xmm0 \n" + "movups %%xmm0, (%[out]) \n" + "movaps %%xmm2, %%xmm1 \n" + "add $16, %[in] \n" + "add $16, %[out] \n" + "dec %[num] \n" + "jnz 1b \n" + "movups %%xmm1, (%[iv]) \n" + : + : [iv]"r"((uint8_t *)m_IV), [sched]"r"(m_ECBDecryption.GetKeySchedule ()), + [in]"r"(in), [out]"r"(out), [num]"r"(numBlocks) + : "%xmm0", "%xmm1", "%xmm2", "cc", "memory" + ); } else #endif @@ -869,22 +876,22 @@ namespace crypto void CBCDecryption::Decrypt (const uint8_t * in, uint8_t * out) { -#ifdef __AES__ +#if SUPPORTS_AES if(i2p::cpu::aesni) { __asm__ - ( - "movups (%[iv]), %%xmm1 \n" - "movups (%[in]), %%xmm0 \n" - "movups %%xmm0, (%[iv]) \n" - DecryptAES256(sched) - "pxor %%xmm1, %%xmm0 \n" - "movups %%xmm0, (%[out]) \n" - : - : [iv]"r"((uint8_t *)m_IV), [sched]"r"(m_ECBDecryption.GetKeySchedule ()), - [in]"r"(in), [out]"r"(out) - : "%xmm0", "%xmm1", "memory" - ); + ( + "movups (%[iv]), %%xmm1 \n" + "movups (%[in]), %%xmm0 \n" + "movups %%xmm0, (%[iv]) \n" + DecryptAES256(sched) + "pxor %%xmm1, %%xmm0 \n" + "movups %%xmm0, (%[out]) \n" + : + : [iv]"r"((uint8_t *)m_IV), [sched]"r"(m_ECBDecryption.GetKeySchedule ()), + [in]"r"(in), [out]"r"(out) + : "%xmm0", "%xmm1", "memory" + ); } else #endif @@ -893,34 +900,34 @@ namespace crypto void TunnelEncryption::Encrypt (const uint8_t * in, uint8_t * out) { -#ifdef __AES__ +#if SUPPORTS_AES if(i2p::cpu::aesni) { __asm__ - ( - // encrypt IV - "movups (%[in]), %%xmm0 \n" - EncryptAES256(sched_iv) - "movaps %%xmm0, %%xmm1 \n" - // double IV encryption - EncryptAES256(sched_iv) - "movups %%xmm0, (%[out]) \n" - // encrypt data, IV is xmm1 - "1: \n" - "add $16, %[in] \n" - "add $16, %[out] \n" - "movups (%[in]), %%xmm0 \n" - "pxor %%xmm1, %%xmm0 \n" - EncryptAES256(sched_l) - "movaps %%xmm0, %%xmm1 \n" - "movups %%xmm0, (%[out]) \n" - "dec %[num] \n" - "jnz 1b \n" - : - : [sched_iv]"r"(m_IVEncryption.GetKeySchedule ()), [sched_l]"r"(m_LayerEncryption.ECB().GetKeySchedule ()), - [in]"r"(in), [out]"r"(out), [num]"r"(63) // 63 blocks = 1008 bytes - : "%xmm0", "%xmm1", "cc", "memory" - ); + ( + // encrypt IV + "movups (%[in]), %%xmm0 \n" + EncryptAES256(sched_iv) + "movaps %%xmm0, %%xmm1 \n" + // double IV encryption + EncryptAES256(sched_iv) + "movups %%xmm0, (%[out]) \n" + // encrypt data, IV is xmm1 + "1: \n" + "add $16, %[in] \n" + "add $16, %[out] \n" + "movups (%[in]), %%xmm0 \n" + "pxor %%xmm1, %%xmm0 \n" + EncryptAES256(sched_l) + "movaps %%xmm0, %%xmm1 \n" + "movups %%xmm0, (%[out]) \n" + "dec %[num] \n" + "jnz 1b \n" + : + : [sched_iv]"r"(m_IVEncryption.GetKeySchedule ()), [sched_l]"r"(m_LayerEncryption.ECB().GetKeySchedule ()), + [in]"r"(in), [out]"r"(out), [num]"r"(63) // 63 blocks = 1008 bytes + : "%xmm0", "%xmm1", "cc", "memory" + ); } else #endif @@ -934,35 +941,35 @@ namespace crypto void TunnelDecryption::Decrypt (const uint8_t * in, uint8_t * out) { -#ifdef __AES__ +#if SUPPORTS_AES if(i2p::cpu::aesni) { __asm__ - ( - // decrypt IV - "movups (%[in]), %%xmm0 \n" - DecryptAES256(sched_iv) - "movaps %%xmm0, %%xmm1 \n" - // double IV encryption - DecryptAES256(sched_iv) - "movups %%xmm0, (%[out]) \n" - // decrypt data, IV is xmm1 - "1: \n" - "add $16, %[in] \n" - "add $16, %[out] \n" - "movups (%[in]), %%xmm0 \n" - "movaps %%xmm0, %%xmm2 \n" - DecryptAES256(sched_l) - "pxor %%xmm1, %%xmm0 \n" - "movups %%xmm0, (%[out]) \n" - "movaps %%xmm2, %%xmm1 \n" - "dec %[num] \n" - "jnz 1b \n" - : - : [sched_iv]"r"(m_IVDecryption.GetKeySchedule ()), [sched_l]"r"(m_LayerDecryption.ECB().GetKeySchedule ()), - [in]"r"(in), [out]"r"(out), [num]"r"(63) // 63 blocks = 1008 bytes - : "%xmm0", "%xmm1", "%xmm2", "cc", "memory" - ); + ( + // decrypt IV + "movups (%[in]), %%xmm0 \n" + DecryptAES256(sched_iv) + "movaps %%xmm0, %%xmm1 \n" + // double IV encryption + DecryptAES256(sched_iv) + "movups %%xmm0, (%[out]) \n" + // decrypt data, IV is xmm1 + "1: \n" + "add $16, %[in] \n" + "add $16, %[out] \n" + "movups (%[in]), %%xmm0 \n" + "movaps %%xmm0, %%xmm2 \n" + DecryptAES256(sched_l) + "pxor %%xmm1, %%xmm0 \n" + "movups %%xmm0, (%[out]) \n" + "movaps %%xmm2, %%xmm1 \n" + "dec %[num] \n" + "jnz 1b \n" + : + : [sched_iv]"r"(m_IVDecryption.GetKeySchedule ()), [sched_l]"r"(m_LayerDecryption.ECB().GetKeySchedule ()), + [in]"r"(in), [out]"r"(out), [num]"r"(63) // 63 blocks = 1008 bytes + : "%xmm0", "%xmm1", "%xmm2", "cc", "memory" + ); } else #endif @@ -991,7 +998,7 @@ namespace crypto EVP_EncryptInit_ex(ctx, NULL, NULL, key, nonce); EVP_EncryptUpdate(ctx, NULL, &outlen, ad, adLen); EVP_EncryptUpdate(ctx, buf, &outlen, msg, msgLen); - EVP_EncryptFinal_ex(ctx, buf, &outlen); + EVP_EncryptFinal_ex(ctx, buf + outlen, &outlen); EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_GET_TAG, 16, buf + msgLen); } else @@ -1285,9 +1292,9 @@ namespace crypto } }*/ - void InitCrypto (bool precomputation, bool aesni, bool avx, bool force) + void InitCrypto (bool precomputation, bool aesni, bool force) { - i2p::cpu::Detect (aesni, avx, force); + i2p::cpu::Detect (aesni, force); #if LEGACY_OPENSSL SSL_library_init (); #endif @@ -1297,7 +1304,7 @@ namespace crypto CRYPTO_set_locking_callback (OpensslLockingCallback);*/ if (precomputation) { -#if defined(__x86_64__) +#if IS_X86_64 g_ElggTable = new BIGNUM * [ELGAMAL_FULL_EXPONENT_NUM_BYTES][255]; PrecalculateElggTable (g_ElggTable, ELGAMAL_FULL_EXPONENT_NUM_BYTES); #else @@ -1312,7 +1319,7 @@ namespace crypto if (g_ElggTable) { DestroyElggTable (g_ElggTable, -#if defined(__x86_64__) +#if IS_X86_64 ELGAMAL_FULL_EXPONENT_NUM_BYTES #else ELGAMAL_SHORT_EXPONENT_NUM_BYTES diff --git a/libi2pd/Crypto.h b/libi2pd/Crypto.h index d4cbda97..816d79fd 100644 --- a/libi2pd/Crypto.h +++ b/libi2pd/Crypto.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2022, The PurpleI2P Project +* Copyright (c) 2013-2023, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -150,7 +150,7 @@ namespace crypto }; -#ifdef __AES__ +#if SUPPORTS_AES class ECBCryptoAESNI { public: @@ -167,7 +167,7 @@ namespace crypto }; #endif -#ifdef __AES__ +#if SUPPORTS_AES class ECBEncryption: public ECBCryptoAESNI #else class ECBEncryption @@ -183,7 +183,7 @@ namespace crypto AES_KEY m_Key; }; -#ifdef __AES__ +#if SUPPORTS_AES class ECBDecryption: public ECBCryptoAESNI #else class ECBDecryption @@ -307,7 +307,7 @@ namespace crypto void InitNoiseIKState (NoiseSymmetricState& state, const uint8_t * pub); // Noise_IK (ratchets) // init and terminate - void InitCrypto (bool precomputation, bool aesni, bool avx, bool force); + void InitCrypto (bool precomputation, bool aesni, bool force); void TerminateCrypto (); } } diff --git a/libi2pd/Datagram.cpp b/libi2pd/Datagram.cpp index 64738ebe..f87f9653 100644 --- a/libi2pd/Datagram.cpp +++ b/libi2pd/Datagram.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2023, The PurpleI2P Project +* Copyright (c) 2013-2024, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -19,7 +19,7 @@ namespace i2p namespace datagram { DatagramDestination::DatagramDestination (std::shared_ptr owner, bool gzip): - m_Owner (owner), m_Receiver (nullptr), m_RawReceiver (nullptr), m_Gzip (gzip) + m_Owner (owner), m_DefaultReceiver (nullptr), m_DefaultRawReceiver (nullptr), m_Gzip (gzip) { if (m_Gzip) m_Deflator.reset (new i2p::data::GzipDeflator); @@ -119,19 +119,79 @@ namespace datagram void DatagramDestination::HandleRawDatagram (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len) { - if (m_RawReceiver) - m_RawReceiver (fromPort, toPort, buf, len); + auto r = FindRawReceiver(toPort); + + if (r) + r (fromPort, toPort, buf, len); else LogPrint (eLogWarning, "DatagramDestination: no receiver for raw datagram"); } + void DatagramDestination::SetReceiver (const Receiver& receiver, uint16_t port) + { + std::lock_guard lock(m_ReceiversMutex); + m_ReceiversByPorts[port] = receiver; + if (!m_DefaultReceiver) { + m_DefaultReceiver = receiver; + m_DefaultReceiverPort = port; + } + } + + void DatagramDestination::ResetReceiver (uint16_t port) + { + std::lock_guard lock(m_ReceiversMutex); + m_ReceiversByPorts.erase (port); + if (m_DefaultReceiverPort == port) { + m_DefaultReceiver = nullptr; + m_DefaultReceiverPort = 0; + } + } + + + void DatagramDestination::SetRawReceiver (const RawReceiver& receiver, uint16_t port) + { + std::lock_guard lock(m_RawReceiversMutex); + m_RawReceiversByPorts[port] = receiver; + if (!m_DefaultRawReceiver) { + m_DefaultRawReceiver = receiver; + m_DefaultRawReceiverPort = port; + } + } + + void DatagramDestination::ResetRawReceiver (uint16_t port) + { + std::lock_guard lock(m_RawReceiversMutex); + m_RawReceiversByPorts.erase (port); + if (m_DefaultRawReceiverPort == port) { + m_DefaultRawReceiver = nullptr; + m_DefaultRawReceiverPort = 0; + } + } + + DatagramDestination::Receiver DatagramDestination::FindReceiver(uint16_t port) { std::lock_guard lock(m_ReceiversMutex); - Receiver r = m_Receiver; + Receiver r = nullptr; auto itr = m_ReceiversByPorts.find(port); if (itr != m_ReceiversByPorts.end()) r = itr->second; + else { + r = m_DefaultReceiver; + } + return r; + } + + DatagramDestination::RawReceiver DatagramDestination::FindRawReceiver(uint16_t port) + { + std::lock_guard lock(m_RawReceiversMutex); + RawReceiver r = nullptr; + auto itr = m_RawReceiversByPorts.find(port); + if (itr != m_RawReceiversByPorts.end()) + r = itr->second; + else { + r = m_DefaultRawReceiver; + } return r; } diff --git a/libi2pd/Datagram.h b/libi2pd/Datagram.h index 8f3d5ceb..45edecab 100644 --- a/libi2pd/Datagram.h +++ b/libi2pd/Datagram.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2022, The PurpleI2P Project +* Copyright (c) 2013-2024, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -126,14 +126,12 @@ namespace datagram void HandleDataMessagePayload (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len, bool isRaw = false); - void SetReceiver (const Receiver& receiver) { m_Receiver = receiver; }; - void ResetReceiver () { m_Receiver = nullptr; }; - void SetReceiver (const Receiver& receiver, uint16_t port) { std::lock_guard lock(m_ReceiversMutex); m_ReceiversByPorts[port] = receiver; }; - void ResetReceiver (uint16_t port) { std::lock_guard lock(m_ReceiversMutex); m_ReceiversByPorts.erase (port); }; + void SetReceiver (const Receiver& receiver, uint16_t port); + void ResetReceiver (uint16_t port); - void SetRawReceiver (const RawReceiver& receiver) { m_RawReceiver = receiver; }; - void ResetRawReceiver () { m_RawReceiver = nullptr; }; + void SetRawReceiver (const RawReceiver& receiver, uint16_t port); + void ResetRawReceiver (uint16_t port); std::shared_ptr GetInfoForRemote(const i2p::data::IdentHash & remote); @@ -150,20 +148,26 @@ namespace datagram void HandleDatagram (uint16_t fromPort, uint16_t toPort, uint8_t *const& buf, size_t len); void HandleRawDatagram (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len); - /** find a receiver by port, if none by port is found try default receiever, otherwise returns nullptr */ Receiver FindReceiver(uint16_t port); + RawReceiver FindRawReceiver(uint16_t port); private: std::shared_ptr m_Owner; - Receiver m_Receiver; // default - RawReceiver m_RawReceiver; // default - bool m_Gzip; // gzip compression of data messages + std::mutex m_SessionsMutex; std::map m_Sessions; - std::mutex m_ReceiversMutex; - std::map m_ReceiversByPorts; + Receiver m_DefaultReceiver; + RawReceiver m_DefaultRawReceiver; + uint16_t m_DefaultReceiverPort; + uint16_t m_DefaultRawReceiverPort; + std::mutex m_ReceiversMutex; + std::mutex m_RawReceiversMutex; + std::unordered_map m_ReceiversByPorts; + std::unordered_map m_RawReceiversByPorts; + + bool m_Gzip; // gzip compression of data messages i2p::data::GzipInflator m_Inflator; std::unique_ptr m_Deflator; std::vector m_From, m_Signature; diff --git a/libi2pd/Destination.cpp b/libi2pd/Destination.cpp index 9e5fbe20..64e5d085 100644 --- a/libi2pd/Destination.cpp +++ b/libi2pd/Destination.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2023, The PurpleI2P Project +* Copyright (c) 2013-2024, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -262,17 +262,6 @@ namespace client return nullptr; } } - else - { - auto ls = i2p::data::netdb.FindLeaseSet (ident); - if (ls && !ls->IsExpired ()) - { - ls->PopulateLeases (); // since we don't store them in netdb - std::lock_guard _lock(m_RemoteLeaseSetsMutex); - m_RemoteLeaseSets[ident] = ls; - return ls; - } - } return nullptr; } @@ -378,8 +367,9 @@ namespace client HandleDataMessage (payload, len); break; case eI2NPDeliveryStatus: - // we assume tunnel tests non-encrypted - HandleDeliveryStatusMessage (bufbe32toh (payload + DELIVERY_STATUS_MSGID_OFFSET)); + // try tunnel test first + if (!m_Pool || !m_Pool->ProcessDeliveryStatus (bufbe32toh (payload + DELIVERY_STATUS_MSGID_OFFSET), bufbe64toh (payload + DELIVERY_STATUS_TIMESTAMP_OFFSET))) + HandleDeliveryStatusMessage (bufbe32toh (payload + DELIVERY_STATUS_MSGID_OFFSET)); break; case eI2NPDatabaseStore: HandleDatabaseStoreMessage (payload, len); @@ -513,38 +503,43 @@ namespace client if (it != m_LeaseSetRequests.end ()) { auto request = it->second; - bool found = false; - if (request->excluded.size () < MAX_NUM_FLOODFILLS_PER_REQUEST) + for (int i = 0; i < num; i++) { - for (int i = 0; i < num; i++) + i2p::data::IdentHash peerHash (buf + 33 + i*32); + if (!request->excluded.count (peerHash) && !i2p::data::netdb.FindRouter (peerHash)) { - i2p::data::IdentHash peerHash (buf + 33 + i*32); - if (!request->excluded.count (peerHash) && !i2p::data::netdb.FindRouter (peerHash)) - { - LogPrint (eLogInfo, "Destination: Found new floodfill, request it"); - i2p::data::netdb.RequestDestination (peerHash, nullptr, false); // through exploratory - } - } - - auto floodfill = i2p::data::netdb.GetClosestFloodfill (key, request->excluded); - if (floodfill) - { - LogPrint (eLogInfo, "Destination: Requesting ", key.ToBase64 (), " at ", floodfill->GetIdentHash ().ToBase64 ()); - if (SendLeaseSetRequest (key, floodfill, request)) - found = true; + LogPrint (eLogInfo, "Destination: Found new floodfill, request it"); + i2p::data::netdb.RequestDestination (peerHash, nullptr, false); // through exploratory } } - if (!found) - { - LogPrint (eLogInfo, "Destination: ", key.ToBase64 (), " was not found on ", MAX_NUM_FLOODFILLS_PER_REQUEST, " floodfills"); - request->Complete (nullptr); - m_LeaseSetRequests.erase (key); - } + SendNextLeaseSetRequest (key, request); } else LogPrint (eLogWarning, "Destination: Request for ", key.ToBase64 (), " not found"); } + void LeaseSetDestination::SendNextLeaseSetRequest (const i2p::data::IdentHash& key, + std::shared_ptr request) + { + bool found = false; + if (request->excluded.size () < MAX_NUM_FLOODFILLS_PER_REQUEST) + { + auto floodfill = i2p::data::netdb.GetClosestFloodfill (key, request->excluded); + if (floodfill) + { + LogPrint (eLogInfo, "Destination: Requesting ", key.ToBase64 (), " at ", floodfill->GetIdentHash ().ToBase64 ()); + if (SendLeaseSetRequest (key, floodfill, request)) + found = true; + } + } + if (!found) + { + LogPrint (eLogInfo, "Destination: ", key.ToBase64 (), " was not found on ", MAX_NUM_FLOODFILLS_PER_REQUEST, " floodfills"); + request->Complete (nullptr); + m_LeaseSetRequests.erase (key); + } + } + void LeaseSetDestination::HandleDeliveryStatusMessage (uint32_t msgID) { if (msgID == m_PublishReplyToken) @@ -632,6 +627,15 @@ namespace client LogPrint (eLogDebug, "Destination: Publish LeaseSet of ", GetIdentHash ().ToBase32 ()); RAND_bytes ((uint8_t *)&m_PublishReplyToken, 4); auto msg = WrapMessageForRouter (floodfill, i2p::CreateDatabaseStoreMsg (leaseSet, m_PublishReplyToken, inbound)); + auto s = shared_from_this (); + msg->onDrop = [s]() + { + s->GetService ().post([s]() + { + s->m_PublishConfirmationTimer.cancel (); + s->HandlePublishConfirmationTimer (boost::system::error_code()); + }); + }; m_PublishConfirmationTimer.expires_from_now (boost::posix_time::seconds(PUBLISH_CONFIRMATION_TIMEOUT)); m_PublishConfirmationTimer.async_wait (std::bind (&LeaseSetDestination::HandlePublishConfirmationTimer, shared_from_this (), std::placeholders::_1)); @@ -648,7 +652,7 @@ namespace client m_PublishReplyToken = 0; if (GetIdentity ()->GetCryptoKeyType () == i2p::data::CRYPTO_KEY_TYPE_ELGAMAL) { - LogPrint (eLogWarning, "Destination: Publish confirmation was not received in ", PUBLISH_CONFIRMATION_TIMEOUT, " seconds, will try again"); + LogPrint (eLogWarning, "Destination: Publish confirmation was not received in ", PUBLISH_CONFIRMATION_TIMEOUT, " seconds or failed. will try again"); Publish (); } else @@ -833,8 +837,17 @@ namespace client AddECIESx25519Key (replyKey, replyTag); else AddSessionKey (replyKey, replyTag); - auto msg = WrapMessageForRouter (nextFloodfill, CreateLeaseSetDatabaseLookupMsg (dest, - request->excluded, request->replyTunnel, replyKey, replyTag, isECIES)); + + auto msg = WrapMessageForRouter (nextFloodfill, + CreateLeaseSetDatabaseLookupMsg (dest, request->excluded, request->replyTunnel, replyKey, replyTag, isECIES)); + auto s = shared_from_this (); + msg->onDrop = [s, dest, request]() + { + s->GetService ().post([s, dest, request]() + { + s->SendNextLeaseSetRequest (dest, request); + }); + }; request->outboundTunnel->SendTunnelDataMsgs ( { i2p::tunnel::TunnelMessageBlock @@ -930,7 +943,7 @@ namespace client bool isPublic, const std::map * params): LeaseSetDestination (service, isPublic, params), m_Keys (keys), m_StreamingAckDelay (DEFAULT_INITIAL_ACK_DELAY), - m_IsStreamingAnswerPings (DEFAULT_ANSWER_PINGS), + m_IsStreamingAnswerPings (DEFAULT_ANSWER_PINGS), m_LastPort (0), m_DatagramDestination (nullptr), m_RefCounter (0), m_ReadyChecker(service) { @@ -1047,22 +1060,30 @@ namespace client void ClientDestination::Stop () { + LogPrint(eLogDebug, "Destination: Stopping destination ", GetIdentHash().ToBase32(), ".b32.i2p"); LeaseSetDestination::Stop (); m_ReadyChecker.cancel(); + LogPrint(eLogDebug, "Destination: -> Stopping Streaming Destination"); m_StreamingDestination->Stop (); //m_StreamingDestination->SetOwner (nullptr); m_StreamingDestination = nullptr; + + LogPrint(eLogDebug, "Destination: -> Stopping Streaming Destination by ports"); for (auto& it: m_StreamingDestinationsByPorts) { it.second->Stop (); //it.second->SetOwner (nullptr); } m_StreamingDestinationsByPorts.clear (); + m_LastStreamingDestination = nullptr; + if (m_DatagramDestination) { + LogPrint(eLogDebug, "Destination: -> Stopping Datagram Destination"); delete m_DatagramDestination; m_DatagramDestination = nullptr; } + LogPrint(eLogDebug, "Destination: -> Stopping done"); } void ClientDestination::HandleDataMessage (const uint8_t * buf, size_t len) @@ -1082,9 +1103,15 @@ namespace client case PROTOCOL_TYPE_STREAMING: { // streaming protocol - auto dest = GetStreamingDestination (toPort); - if (dest) - dest->HandleDataMessagePayload (buf, length); + if (toPort != m_LastPort || !m_LastStreamingDestination) + { + m_LastStreamingDestination = GetStreamingDestination (toPort); + if (!m_LastStreamingDestination) + m_LastStreamingDestination = m_StreamingDestination; // if no destination on port use default + m_LastPort = toPort; + } + if (m_LastStreamingDestination) + m_LastStreamingDestination->HandleDataMessagePayload (buf, length); else LogPrint (eLogError, "Destination: Missing streaming destination"); } @@ -1108,7 +1135,7 @@ namespace client } } - void ClientDestination::CreateStream (StreamRequestComplete streamRequestComplete, const i2p::data::IdentHash& dest, int port) + void ClientDestination::CreateStream (StreamRequestComplete streamRequestComplete, const i2p::data::IdentHash& dest, uint16_t port) { if (!streamRequestComplete) { @@ -1138,7 +1165,7 @@ namespace client } } - void ClientDestination::CreateStream (StreamRequestComplete streamRequestComplete, std::shared_ptr dest, int port) + void ClientDestination::CreateStream (StreamRequestComplete streamRequestComplete, std::shared_ptr dest, uint16_t port) { if (!streamRequestComplete) { @@ -1157,7 +1184,7 @@ namespace client } template - std::shared_ptr ClientDestination::CreateStreamSync (const Dest& dest, int port) + std::shared_ptr ClientDestination::CreateStreamSync (const Dest& dest, uint16_t port) { volatile bool done = false; std::shared_ptr stream; @@ -1181,17 +1208,17 @@ namespace client return stream; } - std::shared_ptr ClientDestination::CreateStream (const i2p::data::IdentHash& dest, int port) + std::shared_ptr ClientDestination::CreateStream (const i2p::data::IdentHash& dest, uint16_t port) { return CreateStreamSync (dest, port); } - std::shared_ptr ClientDestination::CreateStream (std::shared_ptr dest, int port) + std::shared_ptr ClientDestination::CreateStream (std::shared_ptr dest, uint16_t port) { return CreateStreamSync (dest, port); } - std::shared_ptr ClientDestination::CreateStream (std::shared_ptr remote, int port) + std::shared_ptr ClientDestination::CreateStream (std::shared_ptr remote, uint16_t port) { if (m_StreamingDestination) return m_StreamingDestination->CreateNewOutgoingStream (remote, port); @@ -1228,7 +1255,7 @@ namespace client }); } - std::shared_ptr ClientDestination::GetStreamingDestination (int port) const + std::shared_ptr ClientDestination::GetStreamingDestination (uint16_t port) const { if (port) { @@ -1236,8 +1263,9 @@ namespace client if (it != m_StreamingDestinationsByPorts.end ()) return it->second; } - // if port is zero or not found, use default destination - return m_StreamingDestination; + else // if port is zero, use default destination + return m_StreamingDestination; + return nullptr; } void ClientDestination::AcceptStreams (const i2p::stream::StreamingDestination::Acceptor& acceptor) @@ -1265,7 +1293,7 @@ namespace client m_StreamingDestination->AcceptOnce (acceptor); } - std::shared_ptr ClientDestination::CreateStreamingDestination (int port, bool gzip) + std::shared_ptr ClientDestination::CreateStreamingDestination (uint16_t port, bool gzip) { auto dest = std::make_shared (GetSharedFromThis (), port, gzip); if (port) @@ -1275,7 +1303,7 @@ namespace client return dest; } - std::shared_ptr ClientDestination::RemoveStreamingDestination (int port) + std::shared_ptr ClientDestination::RemoveStreamingDestination (uint16_t port) { if (port) { diff --git a/libi2pd/Destination.h b/libi2pd/Destination.h index 9e2d171f..a066c64c 100644 --- a/libi2pd/Destination.h +++ b/libi2pd/Destination.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2022, The PurpleI2P Project +* Copyright (c) 2013-2024, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -175,6 +176,7 @@ namespace client void RequestLeaseSet (const i2p::data::IdentHash& dest, RequestComplete requestComplete, std::shared_ptr requestedBlindedKey = nullptr); bool SendLeaseSetRequest (const i2p::data::IdentHash& dest, std::shared_ptr nextFloodfill, std::shared_ptr request); + void SendNextLeaseSetRequest (const i2p::data::IdentHash& key, std::shared_ptr request); void HandleRequestTimoutTimer (const boost::system::error_code& ecode, const i2p::data::IdentHash& dest); void HandleCleanupTimer (const boost::system::error_code& ecode); void CleanupRemoteLeaseSets (); @@ -184,8 +186,8 @@ namespace client boost::asio::io_service& m_Service; mutable std::mutex m_RemoteLeaseSetsMutex; - std::map > m_RemoteLeaseSets; - std::map > m_LeaseSetRequests; + std::unordered_map > m_RemoteLeaseSets; + std::unordered_map > m_LeaseSetRequests; std::shared_ptr m_Pool; std::mutex m_LeaseSetMutex; @@ -241,15 +243,15 @@ namespace client int GetRefCounter () const { return m_RefCounter; }; // streaming - std::shared_ptr CreateStreamingDestination (int port, bool gzip = true); // additional - std::shared_ptr GetStreamingDestination (int port = 0) const; - std::shared_ptr RemoveStreamingDestination (int port); + std::shared_ptr CreateStreamingDestination (uint16_t port, bool gzip = true); // additional + std::shared_ptr GetStreamingDestination (uint16_t port = 0) const; + std::shared_ptr RemoveStreamingDestination (uint16_t port); // following methods operate with default streaming destination - void CreateStream (StreamRequestComplete streamRequestComplete, const i2p::data::IdentHash& dest, int port = 0); - void CreateStream (StreamRequestComplete streamRequestComplete, std::shared_ptr dest, int port = 0); - std::shared_ptr CreateStream (const i2p::data::IdentHash& dest, int port = 0); // sync - std::shared_ptr CreateStream (std::shared_ptr dest, int port = 0); // sync - std::shared_ptr CreateStream (std::shared_ptr remote, int port = 0); + void CreateStream (StreamRequestComplete streamRequestComplete, const i2p::data::IdentHash& dest, uint16_t port = 0); + void CreateStream (StreamRequestComplete streamRequestComplete, std::shared_ptr dest, uint16_t port = 0); + std::shared_ptr CreateStream (const i2p::data::IdentHash& dest, uint16_t port = 0); // sync + std::shared_ptr CreateStream (std::shared_ptr dest, uint16_t port = 0); // sync + std::shared_ptr CreateStream (std::shared_ptr remote, uint16_t port = 0); void SendPing (const i2p::data::IdentHash& to); void SendPing (std::shared_ptr to); void AcceptStreams (const i2p::stream::StreamingDestination::Acceptor& acceptor); @@ -285,7 +287,7 @@ namespace client void ReadAuthKey (const std::string& group, const std::map * params); template - std::shared_ptr CreateStreamSync (const Dest& dest, int port); + std::shared_ptr CreateStreamSync (const Dest& dest, uint16_t port); private: @@ -297,6 +299,7 @@ namespace client bool m_IsStreamingAnswerPings; std::shared_ptr m_StreamingDestination; // default std::map > m_StreamingDestinationsByPorts; + std::shared_ptr m_LastStreamingDestination; uint16_t m_LastPort; // for server tunnels i2p::datagram::DatagramDestination * m_DatagramDestination; int m_RefCounter; // how many clients(tunnels) use this destination diff --git a/libi2pd/ECIESX25519AEADRatchetSession.cpp b/libi2pd/ECIESX25519AEADRatchetSession.cpp index 3b3ed485..b4730d0e 100644 --- a/libi2pd/ECIESX25519AEADRatchetSession.cpp +++ b/libi2pd/ECIESX25519AEADRatchetSession.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2023, The PurpleI2P Project +* Copyright (c) 2013-2024, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -117,6 +117,12 @@ namespace garlic return session->HandleNextMessage (buf, len, shared_from_this (), index); } + bool ReceiveRatchetTagSet::IsSessionTerminated () const + { + return !m_Session || m_Session->IsTerminated (); + } + + SymmetricKeyTagSet::SymmetricKeyTagSet (GarlicDestination * destination, const uint8_t * key): ReceiveRatchetTagSet (nullptr), m_Destination (destination) { @@ -1148,7 +1154,7 @@ namespace garlic return len; } - std::shared_ptr WrapECIESX25519Message (std::shared_ptr msg, const uint8_t * key, uint64_t tag) + std::shared_ptr WrapECIESX25519Message (std::shared_ptr msg, const uint8_t * key, uint64_t tag) { auto m = NewI2NPMessage ((msg ? msg->GetPayloadLength () : 0) + 128); m->Align (12); // in order to get buf aligned to 16 (12 + 4) @@ -1168,10 +1174,16 @@ namespace garlic htobe32buf (m->GetPayload (), offset); m->len += offset + 4; m->FillI2NPMessageHeader (eI2NPGarlic); + if (msg->onDrop) + { + // move onDrop to the wrapping I2NP messages + m->onDrop = msg->onDrop; + msg->onDrop = nullptr; + } return m; } - std::shared_ptr WrapECIESX25519MessageForRouter (std::shared_ptr msg, const uint8_t * routerPublicKey) + std::shared_ptr WrapECIESX25519MessageForRouter (std::shared_ptr msg, const uint8_t * routerPublicKey) { // Noise_N, we are Alice, routerPublicKey is Bob's i2p::crypto::NoiseSymmetricState noiseState; @@ -1205,6 +1217,12 @@ namespace garlic htobe32buf (m->GetPayload (), offset); m->len += offset + 4; m->FillI2NPMessageHeader (eI2NPGarlic); + if (msg->onDrop) + { + // move onDrop to the wrapping I2NP messages + m->onDrop = msg->onDrop; + msg->onDrop = nullptr; + } return m; } } diff --git a/libi2pd/ECIESX25519AEADRatchetSession.h b/libi2pd/ECIESX25519AEADRatchetSession.h index 301f597a..e6deb2bc 100644 --- a/libi2pd/ECIESX25519AEADRatchetSession.h +++ b/libi2pd/ECIESX25519AEADRatchetSession.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2021, The PurpleI2P Project +* Copyright (c) 2013-2024, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -86,7 +86,8 @@ namespace garlic virtual bool IsIndexExpired (int index) const; virtual bool HandleNextMessage (uint8_t * buf, size_t len, int index); - + virtual bool IsSessionTerminated () const; + private: int m_TrimBehindIndex = 0; @@ -101,9 +102,10 @@ namespace garlic SymmetricKeyTagSet (GarlicDestination * destination, const uint8_t * key); - bool IsIndexExpired (int index) const { return false; }; - bool HandleNextMessage (uint8_t * buf, size_t len, int index); - + bool IsIndexExpired (int index) const override { return false; }; + bool HandleNextMessage (uint8_t * buf, size_t len, int index) override; + bool IsSessionTerminated () const override { return false; } + private: GarlicDestination * m_Destination; @@ -245,8 +247,8 @@ namespace garlic i2p::crypto::NoiseSymmetricState m_CurrentNoiseState; }; - std::shared_ptr WrapECIESX25519Message (std::shared_ptr msg, const uint8_t * key, uint64_t tag); - std::shared_ptr WrapECIESX25519MessageForRouter (std::shared_ptr msg, const uint8_t * routerPublicKey); + std::shared_ptr WrapECIESX25519Message (std::shared_ptr msg, const uint8_t * key, uint64_t tag); + std::shared_ptr WrapECIESX25519MessageForRouter (std::shared_ptr msg, const uint8_t * routerPublicKey); } } diff --git a/libi2pd/FS.cpp b/libi2pd/FS.cpp index 7334550f..d38bcc2f 100644 --- a/libi2pd/FS.cpp +++ b/libi2pd/FS.cpp @@ -136,6 +136,14 @@ namespace fs { dataDir = (home != NULL && strlen(home) > 0) ? home : ""; dataDir += "/Library/Application Support/" + appName; return; +#elif defined(__HAIKU__) + char *home = getenv("HOME"); + if (home != NULL && strlen(home) > 0) { + dataDir = std::string(home) + "/config/settings/" + appName; + } else { + dataDir = "/tmp/" + appName; + } + return; #else /* other unix */ #if defined(ANDROID) const char * ext = getenv("EXTERNAL_STORAGE"); diff --git a/libi2pd/Garlic.cpp b/libi2pd/Garlic.cpp index c351f6d6..ba35995f 100644 --- a/libi2pd/Garlic.cpp +++ b/libi2pd/Garlic.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2023, The PurpleI2P Project +* Copyright (c) 2013-2024, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -431,7 +431,8 @@ namespace garlic } GarlicDestination::GarlicDestination (): m_NumTags (32), // 32 tags by default - m_PayloadBuffer (nullptr), m_NumRatchetInboundTags (0) // 0 means standard + m_PayloadBuffer (nullptr), m_NumRatchetInboundTags (0), // 0 means standard + m_NumUsedECIESx25519Tags (0) { } @@ -588,11 +589,18 @@ namespace garlic auto it = m_ECIESx25519Tags.find (tag); if (it != m_ECIESx25519Tags.end ()) { + if (!it->second.tagset) return true; // duplicate if (it->second.tagset->HandleNextMessage (buf, len, it->second.index)) + { m_LastTagset = it->second.tagset; + it->second.tagset = nullptr; // mark as used + } else + { LogPrint (eLogError, "Garlic: Can't handle ECIES-X25519-AEAD-Ratchet message"); - m_ECIESx25519Tags.erase (it); + m_ECIESx25519Tags.erase (it); + } + m_NumUsedECIESx25519Tags++; return true; } return false; @@ -877,18 +885,41 @@ namespace garlic } numExpiredTags = 0; - for (auto it = m_ECIESx25519Tags.begin (); it != m_ECIESx25519Tags.end ();) + if (m_NumUsedECIESx25519Tags > ECIESX25519_TAGSET_MAX_NUM_TAGS) // too many used tags { - if (it->second.tagset->IsExpired (ts) || it->second.tagset->IsIndexExpired (it->second.index)) + std::unordered_map oldTags; + std::swap (m_ECIESx25519Tags, oldTags); // re-create + for (auto& it: oldTags) + if (it.second.tagset) + { + if (it.second.tagset->IsExpired (ts) || it.second.tagset->IsIndexExpired (it.second.index)) + { + it.second.tagset->DeleteSymmKey (it.second.index); + numExpiredTags++; + } + else if (it.second.tagset->IsSessionTerminated()) + numExpiredTags++; + else + m_ECIESx25519Tags.emplace (it); + } + } + else + { + for (auto it = m_ECIESx25519Tags.begin (); it != m_ECIESx25519Tags.end ();) { - it->second.tagset->DeleteSymmKey (it->second.index); - it = m_ECIESx25519Tags.erase (it); - numExpiredTags++; - } - else - { - auto session = it->second.tagset->GetSession (); - if (!session || session->IsTerminated()) + if (!it->second.tagset) + { + // delete used tag + it = m_ECIESx25519Tags.erase (it); + continue; + } + if (it->second.tagset->IsExpired (ts) || it->second.tagset->IsIndexExpired (it->second.index)) + { + it->second.tagset->DeleteSymmKey (it->second.index); + it = m_ECIESx25519Tags.erase (it); + numExpiredTags++; + } + else if (it->second.tagset->IsSessionTerminated()) { it = m_ECIESx25519Tags.erase (it); numExpiredTags++; @@ -896,7 +927,8 @@ namespace garlic else ++it; } - } + } + m_NumUsedECIESx25519Tags = 0; if (numExpiredTags > 0) LogPrint (eLogDebug, "Garlic: ", numExpiredTags, " ECIESx25519 tags expired for ", GetIdentHash().ToBase64 ()); if (m_LastTagset && m_LastTagset->IsExpired (ts)) diff --git a/libi2pd/Garlic.h b/libi2pd/Garlic.h index b926abda..0386752b 100644 --- a/libi2pd/Garlic.h +++ b/libi2pd/Garlic.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2022, The PurpleI2P Project +* Copyright (c) 2013-2024, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -221,7 +221,7 @@ namespace garlic struct ECIESX25519AEADRatchetIndexTagset { int index; - ReceiveRatchetTagSetPtr tagset; + ReceiveRatchetTagSetPtr tagset; // null if used }; class GarlicDestination: public i2p::data::LocalDestination @@ -291,6 +291,7 @@ namespace garlic std::unordered_map, std::hash > > m_Tags; std::unordered_map m_ECIESx25519Tags; // session tag -> session ReceiveRatchetTagSetPtr m_LastTagset; // tagset last message came for + int m_NumUsedECIESx25519Tags; // DeliveryStatus std::mutex m_DeliveryStatusSessionsMutex; std::unordered_map m_DeliveryStatusSessions; // msgID -> session @@ -299,7 +300,7 @@ namespace garlic // for HTTP only size_t GetNumIncomingTags () const { return m_Tags.size (); } - size_t GetNumIncomingECIESx25519Tags () const { return m_ECIESx25519Tags.size (); } + size_t GetNumIncomingECIESx25519Tags () const { return m_ECIESx25519Tags.size () - m_NumUsedECIESx25519Tags; } const decltype(m_Sessions)& GetSessions () const { return m_Sessions; }; const decltype(m_ECIESx25519Sessions)& GetECIESx25519Sessions () const { return m_ECIESx25519Sessions; } }; diff --git a/libi2pd/I2NPProtocol.cpp b/libi2pd/I2NPProtocol.cpp index 5303edbf..da6da638 100644 --- a/libi2pd/I2NPProtocol.cpp +++ b/libi2pd/I2NPProtocol.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2023, The PurpleI2P Project +* Copyright (c) 2013-2024, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -40,7 +40,7 @@ namespace i2p { return std::make_shared >(); } - + std::shared_ptr NewI2NPTunnelMessage (bool endpoint) { return i2p::tunnel::tunnels.NewI2NPTunnelMessage (endpoint); @@ -51,7 +51,7 @@ namespace i2p len += I2NP_HEADER_SIZE + 2; if (len <= I2NP_MAX_SHORT_MESSAGE_SIZE) return NewI2NPShortMessage (); if (len <= I2NP_MAX_MEDIUM_MESSAGE_SIZE) return NewI2NPMediumMessage (); - return NewI2NPMessage (); + return NewI2NPMessage (); } void I2NPMessage::FillI2NPMessageHeader (I2NPMessageType msgType, uint32_t replyMsgID, bool checksum) @@ -72,11 +72,15 @@ namespace i2p SetExpiration (i2p::util::GetMillisecondsSinceEpoch () + I2NP_MESSAGE_EXPIRATION_TIMEOUT); } - bool I2NPMessage::IsExpired () const + bool I2NPMessage::IsExpired (uint64_t ts) const { - auto ts = i2p::util::GetMillisecondsSinceEpoch (); auto exp = GetExpiration (); return (ts > exp + I2NP_MESSAGE_CLOCK_SKEW) || (ts < exp - 3*I2NP_MESSAGE_CLOCK_SKEW); // check if expired or too far in future + } + + bool I2NPMessage::IsExpired () const + { + return IsExpired (i2p::util::GetMillisecondsSinceEpoch ()); } std::shared_ptr CreateI2NPMessage (I2NPMessageType msgType, const uint8_t * buf, size_t len, uint32_t replyMsgID) @@ -369,10 +373,20 @@ namespace i2p if (!memcmp (record + BUILD_REQUEST_RECORD_TO_PEER_OFFSET, (const uint8_t *)i2p::context.GetRouterInfo ().GetIdentHash (), 16)) { LogPrint (eLogDebug, "I2NP: Build request record ", i, " is ours"); - if (!i2p::context.DecryptTunnelBuildRecord (record + BUILD_REQUEST_RECORD_ENCRYPTED_OFFSET, clearText)) return false; + if (!i2p::context.DecryptTunnelBuildRecord (record + BUILD_REQUEST_RECORD_ENCRYPTED_OFFSET, clearText)) + { + LogPrint (eLogWarning, "I2NP: Failed to decrypt tunnel build record"); + return false; + } + if (!memcmp ((const uint8_t *)i2p::context.GetIdentHash (), clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET, 32) && // if next ident is now ours + !(clearText[ECIES_BUILD_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_ENDPOINT_FLAG)) // and not endpoint + { + LogPrint (eLogWarning, "I2NP: Next ident is ours in tunnel build record"); + return false; + } uint8_t retCode = 0; // replace record to reply - if (i2p::context.AcceptsTunnels () && !i2p::context.IsHighCongestion ()) + if (i2p::context.AcceptsTunnels () && i2p::context.GetCongestionLevel (false) < CONGESTION_LEVEL_FULL) { auto transitTunnel = i2p::tunnel::CreateTransitTunnel ( bufbe32toh (clearText + ECIES_BUILD_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET), @@ -424,6 +438,11 @@ namespace i2p { int num = buf[0]; LogPrint (eLogDebug, "I2NP: VariableTunnelBuild ", num, " records"); + if (num > i2p::tunnel::MAX_NUM_RECORDS) + { + LogPrint (eLogError, "I2NP: Too many records in VaribleTunnelBuild message ", num); + return; + } if (len < num*TUNNEL_BUILD_RECORD_SIZE + 1) { LogPrint (eLogError, "I2NP: VaribleTunnelBuild message of ", num, " records is too short ", len); @@ -477,6 +496,11 @@ namespace i2p { int num = buf[0]; LogPrint (eLogDebug, "I2NP: TunnelBuildReplyMsg of ", num, " records replyMsgID=", replyMsgID); + if (num > i2p::tunnel::MAX_NUM_RECORDS) + { + LogPrint (eLogError, "I2NP: Too many records in TunnelBuildReply message ", num); + return; + } size_t recordSize = isShort ? SHORT_TUNNEL_BUILD_RECORD_SIZE : TUNNEL_BUILD_RECORD_SIZE; if (len < num*recordSize + 1) { @@ -508,6 +532,11 @@ namespace i2p { int num = buf[0]; LogPrint (eLogDebug, "I2NP: ShortTunnelBuild ", num, " records"); + if (num > i2p::tunnel::MAX_NUM_RECORDS) + { + LogPrint (eLogError, "I2NP: Too many records in ShortTunnelBuild message ", num); + return; + } if (len < num*SHORT_TUNNEL_BUILD_RECORD_SIZE + 1) { LogPrint (eLogError, "I2NP: ShortTunnelBuild message of ", num, " records is too short ", len); @@ -562,16 +591,24 @@ namespace i2p memcpy (ivKey, noiseState.m_CK + 32, 32); } else + { + if (!memcmp ((const uint8_t *)i2p::context.GetIdentHash (), clearText + SHORT_REQUEST_RECORD_NEXT_IDENT_OFFSET, 32)) // if next ident is now ours + { + LogPrint (eLogWarning, "I2NP: Next ident is ours in short request record"); + return; + } memcpy (ivKey, noiseState.m_CK , 32); + } // check if we accept this tunnel + std::shared_ptr transitTunnel; uint8_t retCode = 0; - if (!i2p::context.AcceptsTunnels () || i2p::context.IsHighCongestion ()) + if (!i2p::context.AcceptsTunnels () || i2p::context.GetCongestionLevel (false) >= CONGESTION_LEVEL_FULL) retCode = 30; if (!retCode) { // create new transit tunnel - auto transitTunnel = i2p::tunnel::CreateTransitTunnel ( + transitTunnel = i2p::tunnel::CreateTransitTunnel ( bufbe32toh (clearText + SHORT_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET), clearText + SHORT_REQUEST_RECORD_NEXT_IDENT_OFFSET, bufbe32toh (clearText + SHORT_REQUEST_RECORD_NEXT_TUNNEL_OFFSET), @@ -605,11 +642,22 @@ namespace i2p reply += SHORT_TUNNEL_BUILD_RECORD_SIZE; } // send reply + auto onDrop = [transitTunnel]() + { + if (transitTunnel) + { + auto t = transitTunnel->GetCreationTime (); + if (t > i2p::tunnel::TUNNEL_EXPIRATION_TIMEOUT) + // make transit tunnel expired + transitTunnel->SetCreationTime (t - i2p::tunnel::TUNNEL_EXPIRATION_TIMEOUT); + } + }; if (isEndpoint) { auto replyMsg = NewI2NPShortMessage (); replyMsg->Concat (buf, len); replyMsg->FillI2NPMessageHeader (eI2NPShortTunnelBuildReply, bufbe32toh (clearText + SHORT_REQUEST_RECORD_SEND_MSG_ID_OFFSET)); + if (transitTunnel) replyMsg->onDrop = onDrop; if (memcmp ((const uint8_t *)i2p::context.GetIdentHash (), clearText + SHORT_REQUEST_RECORD_NEXT_IDENT_OFFSET, 32)) // reply IBGW is not local? { @@ -627,15 +675,21 @@ namespace i2p uint32_t tunnelID = bufbe32toh (clearText + SHORT_REQUEST_RECORD_NEXT_TUNNEL_OFFSET); auto tunnel = i2p::tunnel::tunnels.GetTunnel (tunnelID); if (tunnel) + { tunnel->SendTunnelDataMsg (replyMsg); + tunnel->FlushTunnelDataMsgs (); + } else LogPrint (eLogWarning, "I2NP: Tunnel ", tunnelID, " not found for short tunnel build reply"); } } else - transports.SendMessage (clearText + SHORT_REQUEST_RECORD_NEXT_IDENT_OFFSET, - CreateI2NPMessage (eI2NPShortTunnelBuild, buf, len, - bufbe32toh (clearText + SHORT_REQUEST_RECORD_SEND_MSG_ID_OFFSET))); + { + auto msg = CreateI2NPMessage (eI2NPShortTunnelBuild, buf, len, + bufbe32toh (clearText + SHORT_REQUEST_RECORD_SEND_MSG_ID_OFFSET)); + if (transitTunnel) msg->onDrop = onDrop; + transports.SendMessage (clearText + SHORT_REQUEST_RECORD_NEXT_IDENT_OFFSET, msg); + } return; } record += SHORT_TUNNEL_BUILD_RECORD_SIZE; @@ -695,7 +749,11 @@ namespace i2p return msg; } else - return CreateTunnelGatewayMsg (tunnelID, msg->GetBuffer (), msg->GetLength ()); + { + auto newMsg = CreateTunnelGatewayMsg (tunnelID, msg->GetBuffer (), msg->GetLength ()); + if (msg->onDrop) newMsg->onDrop = msg->onDrop; + return newMsg; + } } std::shared_ptr CreateTunnelGatewayMsg (uint32_t tunnelID, I2NPMessageType msgType, @@ -733,46 +791,38 @@ namespace i2p return l; } - void HandleI2NPMessage (uint8_t * msg, size_t len) + void HandleTunnelBuildI2NPMessage (std::shared_ptr msg) { - if (len < I2NP_HEADER_SIZE) + if (msg) { - LogPrint (eLogError, "I2NP: Message length ", len, " is smaller than header"); - return; - } - uint8_t typeID = msg[I2NP_HEADER_TYPEID_OFFSET]; - uint32_t msgID = bufbe32toh (msg + I2NP_HEADER_MSGID_OFFSET); - LogPrint (eLogDebug, "I2NP: Msg received len=", len,", type=", (int)typeID, ", msgID=", (unsigned int)msgID); - uint8_t * buf = msg + I2NP_HEADER_SIZE; - auto size = bufbe16toh (msg + I2NP_HEADER_SIZE_OFFSET); - len -= I2NP_HEADER_SIZE; - if (size > len) - { - LogPrint (eLogError, "I2NP: Payload size ", size, " exceeds buffer length ", len); - size = len; - } - switch (typeID) - { - case eI2NPVariableTunnelBuild: - HandleVariableTunnelBuildMsg (msgID, buf, size); - break; - case eI2NPShortTunnelBuild: - HandleShortTunnelBuildMsg (msgID, buf, size); - break; - case eI2NPVariableTunnelBuildReply: - HandleTunnelBuildReplyMsg (msgID, buf, size, false); - break; - case eI2NPShortTunnelBuildReply: - HandleTunnelBuildReplyMsg (msgID, buf, size, true); - break; - case eI2NPTunnelBuild: - HandleTunnelBuildMsg (buf, size); - break; - case eI2NPTunnelBuildReply: - // TODO: - break; - default: - LogPrint (eLogWarning, "I2NP: Unexpected message ", (int)typeID); + uint8_t typeID = msg->GetTypeID(); + uint32_t msgID = msg->GetMsgID(); + LogPrint (eLogDebug, "I2NP: Handling tunnel build message with len=", msg->GetLength(),", type=", (int)typeID, ", msgID=", (unsigned int)msgID); + uint8_t * payload = msg->GetPayload(); + auto size = msg->GetPayloadLength(); + switch (typeID) + { + case eI2NPVariableTunnelBuild: + HandleVariableTunnelBuildMsg (msgID, payload, size); + break; + case eI2NPShortTunnelBuild: + HandleShortTunnelBuildMsg (msgID, payload, size); + break; + case eI2NPVariableTunnelBuildReply: + HandleTunnelBuildReplyMsg (msgID, payload, size, false); + break; + case eI2NPShortTunnelBuildReply: + HandleTunnelBuildReplyMsg (msgID, payload, size, true); + break; + case eI2NPTunnelBuild: + HandleTunnelBuildMsg (payload, size); + break; + case eI2NPTunnelBuildReply: + // TODO: + break; + default: + LogPrint (eLogError, "I2NP: Unexpected message with type", (int)typeID, " during handling TBM; skipping"); + } } } @@ -785,10 +835,12 @@ namespace i2p switch (typeID) { case eI2NPTunnelData: - i2p::tunnel::tunnels.PostTunnelData (msg); + if (!msg->from) + i2p::tunnel::tunnels.PostTunnelData (msg); break; case eI2NPTunnelGateway: - i2p::tunnel::tunnels.PostTunnelData (msg); + if (!msg->from) + i2p::tunnel::tunnels.PostTunnelData (msg); break; case eI2NPGarlic: { @@ -799,10 +851,16 @@ namespace i2p break; } case eI2NPDatabaseStore: - case eI2NPDatabaseSearchReply: + case eI2NPDatabaseSearchReply: + // forward to netDb if came directly or through exploratory tunnel as response to our request + if (!msg->from || !msg->from->GetTunnelPool () || msg->from->GetTunnelPool ()->IsExploratory ()) + i2p::data::netdb.PostI2NPMsg (msg); + break; + case eI2NPDatabaseLookup: - // forward to netDb - i2p::data::netdb.PostI2NPMsg (msg); + // forward to netDb if floodfill and came directly + if (!msg->from && i2p::context.IsFloodfill ()) + i2p::data::netdb.PostI2NPMsg (msg); break; case eI2NPDeliveryStatus: { @@ -813,16 +871,20 @@ namespace i2p break; } case eI2NPVariableTunnelBuild: - case eI2NPVariableTunnelBuildReply: case eI2NPTunnelBuild: - case eI2NPTunnelBuildReply: case eI2NPShortTunnelBuild: + // forward to tunnel thread + if (!msg->from) + i2p::tunnel::tunnels.PostTunnelData (msg); + break; + case eI2NPVariableTunnelBuildReply: + case eI2NPTunnelBuildReply: case eI2NPShortTunnelBuildReply: // forward to tunnel thread i2p::tunnel::tunnels.PostTunnelData (msg); break; default: - HandleI2NPMessage (msg->GetBuffer (), msg->GetLength ()); + LogPrint(eLogError, "I2NP: Unexpected I2NP message with type ", int(typeID), " during handling; skipping"); } } } diff --git a/libi2pd/I2NPProtocol.h b/libi2pd/I2NPProtocol.h index c0685190..36facbe3 100644 --- a/libi2pd/I2NPProtocol.h +++ b/libi2pd/I2NPProtocol.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2023, The PurpleI2P Project +* Copyright (c) 2013-2024, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -13,6 +13,7 @@ #include #include #include +#include #include "Crypto.h" #include "I2PEndian.h" #include "Identity.h" @@ -138,6 +139,10 @@ namespace tunnel class TunnelPool; } + const int CONGESTION_LEVEL_MEDIUM = 70; + const int CONGESTION_LEVEL_HIGH = 90; + const int CONGESTION_LEVEL_FULL = 100; + const size_t I2NP_MAX_MESSAGE_SIZE = 62708; const size_t I2NP_MAX_SHORT_MESSAGE_SIZE = 4096; const size_t I2NP_MAX_MEDIUM_MESSAGE_SIZE = 16384; @@ -149,7 +154,8 @@ namespace tunnel uint8_t * buf; size_t len, offset, maxLen; std::shared_ptr from; - + std::function onDrop; + I2NPMessage (): buf (nullptr),len (I2NP_HEADER_SIZE + 2), offset(2), maxLen (0), from (nullptr) {}; // reserve 2 bytes for NTCP header @@ -241,7 +247,6 @@ namespace tunnel SetSize (len - offset - I2NP_HEADER_SIZE); SetChks (0); } - void ToNTCP2 () { uint8_t * ntcp2 = GetNTCP2Header (); @@ -252,6 +257,9 @@ namespace tunnel void FillI2NPMessageHeader (I2NPMessageType msgType, uint32_t replyMsgID = 0, bool checksum = true); void RenewI2NPMessageHeader (); bool IsExpired () const; + bool IsExpired (uint64_t ts) const; // in milliseconds + + void Drop () { if (onDrop) { onDrop (); onDrop = nullptr; }; } }; template @@ -295,7 +303,7 @@ namespace tunnel std::shared_ptr CreateTunnelGatewayMsg (uint32_t tunnelID, std::shared_ptr msg); size_t GetI2NPMessageLength (const uint8_t * msg, size_t len); - void HandleI2NPMessage (uint8_t * msg, size_t len); + void HandleTunnelBuildI2NPMessage (std::shared_ptr msg); void HandleI2NPMessage (std::shared_ptr msg); class I2NPMessagesHandler diff --git a/libi2pd/I2PEndian.h b/libi2pd/I2PEndian.h index 06abf29a..681a4999 100644 --- a/libi2pd/I2PEndian.h +++ b/libi2pd/I2PEndian.h @@ -14,7 +14,7 @@ #if defined(__FreeBSD__) || defined(__NetBSD__) #include -#elif defined(__linux__) || defined(__FreeBSD_kernel__) || defined(__OpenBSD__) || defined(__GLIBC__) +#elif defined(__linux__) || defined(__FreeBSD_kernel__) || defined(__OpenBSD__) || defined(__GLIBC__) || defined(__HAIKU__) #include #elif defined(__APPLE__) && defined(__MACH__) diff --git a/libi2pd/Identity.cpp b/libi2pd/Identity.cpp index ca47e797..3a659c11 100644 --- a/libi2pd/Identity.cpp +++ b/libi2pd/Identity.cpp @@ -581,7 +581,7 @@ namespace data if (keyType == SIGNING_KEY_TYPE_DSA_SHA1) m_Signer.reset (new i2p::crypto::DSASigner (m_SigningPrivateKey, m_Public->GetStandardIdentity ().signingKey)); else if (keyType == SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519 && !IsOfflineSignature ()) - m_Signer.reset (new i2p::crypto::EDDSA25519Signer (m_SigningPrivateKey, m_Public->GetStandardIdentity ().certificate - i2p::crypto::EDDSA25519_PUBLIC_KEY_LENGTH)); // TODO: remove public key check + m_Signer.reset (new i2p::crypto::EDDSA25519Signer (m_SigningPrivateKey, m_Public->GetStandardIdentity ().signingKey + (sizeof(Identity::signingKey) - i2p::crypto::EDDSA25519_PUBLIC_KEY_LENGTH))); // TODO: remove public key check else { // public key is not required @@ -803,29 +803,12 @@ namespace data XORMetric operator^(const IdentHash& key1, const IdentHash& key2) { XORMetric m; -#if (defined(__x86_64__) || defined(__i386__)) && defined(__AVX__) // not all X86 targets supports AVX (like old Pentium, see #1600) - if(i2p::cpu::avx) - { - __asm__ - ( - "vmovups %1, %%ymm0 \n" - "vmovups %2, %%ymm1 \n" - "vxorps %%ymm0, %%ymm1, %%ymm1 \n" - "vmovups %%ymm1, %0 \n" - : "=m"(*m.metric) - : "m"(*key1), "m"(*key2) - : "memory", "%xmm0", "%xmm1" // should be replaced by %ymm0/1 once supported by compiler - ); - } - else -#endif - { - const uint64_t * hash1 = key1.GetLL (), * hash2 = key2.GetLL (); - m.metric_ll[0] = hash1[0] ^ hash2[0]; - m.metric_ll[1] = hash1[1] ^ hash2[1]; - m.metric_ll[2] = hash1[2] ^ hash2[2]; - m.metric_ll[3] = hash1[3] ^ hash2[3]; - } + + const uint64_t * hash1 = key1.GetLL (), * hash2 = key2.GetLL (); + m.metric_ll[0] = hash1[0] ^ hash2[0]; + m.metric_ll[1] = hash1[1] ^ hash2[1]; + m.metric_ll[2] = hash1[2] ^ hash2[2]; + m.metric_ll[3] = hash1[3] ^ hash2[3]; return m; } diff --git a/libi2pd/LeaseSet.h b/libi2pd/LeaseSet.h index 566c4655..4b1311a5 100644 --- a/libi2pd/LeaseSet.h +++ b/libi2pd/LeaseSet.h @@ -96,6 +96,9 @@ namespace data void Encrypt (const uint8_t * data, uint8_t * encrypted) const; bool IsDestination () const { return true; }; + // used in webconsole + void ExpireLease () { m_ExpirationTime = i2p::util::GetSecondsSinceEpoch (); }; + protected: void UpdateLeasesBegin (); diff --git a/libi2pd/Log.h b/libi2pd/Log.h index 1ec0c5fe..0164ea4f 100644 --- a/libi2pd/Log.h +++ b/libi2pd/Log.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 * @@ -87,8 +87,8 @@ namespace log { Log (); ~Log (); - LogType GetLogType () { return m_Destination; }; - LogLevel GetLogLevel () { return m_MinLevel; }; + LogType GetLogType () const { return m_Destination; }; + LogLevel GetLogLevel () const { return m_MinLevel; }; void Start (); void Stop (); @@ -160,6 +160,11 @@ namespace log { } // log } // i2p +inline bool CheckLogLevel (LogLevel level) noexcept +{ + return level <= i2p::log::Logger().GetLogLevel (); +} + /** internal usage only -- folding args array to single string */ template void LogPrint (std::stringstream& s, TValue&& arg) noexcept @@ -185,9 +190,7 @@ void LogPrint (std::stringstream& s, TValue&& arg, TArgs&&... args) noexcept template void LogPrint (LogLevel level, TArgs&&... args) noexcept { - i2p::log::Log &log = i2p::log::Logger(); - if (level > log.GetLogLevel ()) - return; + if (!CheckLogLevel (level)) return; // fold message to single string std::stringstream ss; @@ -200,7 +203,7 @@ void LogPrint (LogLevel level, TArgs&&... args) noexcept auto msg = std::make_shared(level, std::time(nullptr), std::move(ss).str()); msg->tid = std::this_thread::get_id(); - log.Append(msg); + i2p::log::Logger().Append(msg); } /** diff --git a/libi2pd/NTCP2.cpp b/libi2pd/NTCP2.cpp index 91bc9b3d..58a1c539 100644 --- a/libi2pd/NTCP2.cpp +++ b/libi2pd/NTCP2.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2023, The PurpleI2P Project +* Copyright (c) 2013-2024, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -19,9 +19,10 @@ #include "RouterContext.h" #include "Transports.h" #include "NetDb.hpp" -#include "NTCP2.h" #include "HTTP.h" #include "util.h" +#include "Socks5.h" +#include "NTCP2.h" #if defined(__linux__) && !defined(_NETINET_IN_H) #include @@ -373,9 +374,20 @@ namespace transport m_Socket.close (); transports.PeerDisconnected (shared_from_this ()); m_Server.RemoveNTCP2Session (shared_from_this ()); + for (auto& it: m_SendQueue) + it->Drop (); m_SendQueue.clear (); - m_SendQueueSize = 0; - LogPrint (eLogDebug, "NTCP2: Session terminated"); + SetSendQueueSize (0); + auto remoteIdentity = GetRemoteIdentity (); + if (remoteIdentity) + { + LogPrint (eLogDebug, "NTCP2: Session with ", GetRemoteEndpoint (), + " (", i2p::data::GetIdentHashAbbreviation (remoteIdentity->GetIdentHash ()), ") terminated"); + } + else + { + LogPrint (eLogDebug, "NTCP2: Session with ", GetRemoteEndpoint (), " terminated"); + } } } @@ -424,7 +436,7 @@ namespace transport void NTCP2Session::DeleteNextReceiveBuffer (uint64_t ts) { if (m_NextReceivedBuffer && !m_IsReceiving && - ts > m_LastActivityTimestamp + NTCP2_RECEIVE_BUFFER_DELETION_TIMEOUT) + ts > GetLastActivityTimestamp () + NTCP2_RECEIVE_BUFFER_DELETION_TIMEOUT) { delete[] m_NextReceivedBuffer; m_NextReceivedBuffer = nullptr; @@ -566,7 +578,11 @@ namespace transport SendSessionConfirmed (); } else + { + if (GetRemoteIdentity ()) + i2p::data::netdb.SetUnreachable (GetRemoteIdentity ()->GetIdentHash (), true); // assume wrong s key Terminate (); + } } } @@ -687,16 +703,25 @@ namespace transport i2p::data::RouterInfo ri (buf.data () + 4, size - 1); // 1 byte block type + 2 bytes size + 1 byte flag if (ri.IsUnreachable ()) { - LogPrint (eLogError, "NTCP2: Signature verification failed in SessionConfirmed"); + LogPrint (eLogError, "NTCP2: RouterInfo verification failed in SessionConfirmed from ", GetRemoteEndpoint ()); SendTerminationAndTerminate (eNTCP2RouterInfoSignatureVerificationFail); return; } - if (i2p::util::GetMillisecondsSinceEpoch () > ri.GetTimestamp () + i2p::data::NETDB_MIN_EXPIRATION_TIMEOUT*1000LL) // 90 minutes + LogPrint(eLogDebug, "NTCP2: SessionConfirmed from ", GetRemoteEndpoint (), + " (", i2p::data::GetIdentHashAbbreviation (ri.GetIdentHash ()), ")"); + auto ts = i2p::util::GetMillisecondsSinceEpoch (); + if (ts > ri.GetTimestamp () + i2p::data::NETDB_MIN_EXPIRATION_TIMEOUT*1000LL) // 90 minutes { - LogPrint (eLogError, "NTCP2: RouterInfo is too old in SessionConfirmed"); + LogPrint (eLogError, "NTCP2: RouterInfo is too old in SessionConfirmed for ", (ts - ri.GetTimestamp ())/1000LL, " seconds"); SendTerminationAndTerminate (eNTCP2Message3Error); return; } + if (ts + i2p::data::NETDB_EXPIRATION_TIMEOUT_THRESHOLD*1000LL < ri.GetTimestamp ()) // 2 minutes + { + LogPrint (eLogError, "NTCP2: RouterInfo is from future for ", (ri.GetTimestamp () - ts)/1000LL, " seconds"); + SendTerminationAndTerminate (eNTCP2Message3Error); + return; + } auto addr = m_RemoteEndpoint.address ().is_v4 () ? ri.GetNTCP2V4Address () : (i2p::util::net::IsYggdrasilAddress (m_RemoteEndpoint.address ()) ? ri.GetYggdrasilAddress () : ri.GetNTCP2V6Address ()); if (!addr || memcmp (m_Establisher->m_RemoteStaticKey, addr->s, 32)) @@ -767,7 +792,7 @@ namespace transport void NTCP2Session::ServerLogin () { SetTerminationTimeout (NTCP2_ESTABLISH_TIMEOUT); - m_LastActivityTimestamp = i2p::util::GetSecondsSinceEpoch (); + SetLastActivityTimestamp (i2p::util::GetSecondsSinceEpoch ()); m_Establisher->CreateEphemeralKey (); boost::asio::async_read (m_Socket, boost::asio::buffer(m_Establisher->m_SessionRequestBuffer, 64), boost::asio::transfer_all (), std::bind(&NTCP2Session::HandleSessionRequestReceived, shared_from_this (), @@ -811,14 +836,19 @@ namespace transport CreateNextReceivedBuffer (m_NextReceivedLen); boost::system::error_code ec; size_t moreBytes = m_Socket.available(ec); - if (!ec && moreBytes >= m_NextReceivedLen) - { - // read and process message immediately if available - moreBytes = boost::asio::read (m_Socket, boost::asio::buffer(m_NextReceivedBuffer, m_NextReceivedLen), boost::asio::transfer_all (), ec); - HandleReceived (ec, moreBytes); - } + if (!ec) + { + if (moreBytes >= m_NextReceivedLen) + { + // read and process message immediately if available + moreBytes = boost::asio::read (m_Socket, boost::asio::buffer(m_NextReceivedBuffer, m_NextReceivedLen), boost::asio::transfer_all (), ec); + HandleReceived (ec, moreBytes); + } + else + Receive (); + } else - Receive (); + LogPrint (eLogWarning, "NTCP2: Socket error: ", ec.message ()); } else { @@ -844,15 +874,14 @@ namespace transport { if (ecode) { - if (ecode != boost::asio::error::operation_aborted) - LogPrint (eLogWarning, "NTCP2: Receive read error: ", ecode.message ()); + if (ecode != boost::asio::error::operation_aborted) + LogPrint (eLogWarning, "NTCP2: Receive read error: ", ecode.message ()); Terminate (); } else { - m_LastActivityTimestamp = i2p::util::GetSecondsSinceEpoch (); - m_NumReceivedBytes += bytes_transferred + 2; // + length - i2p::transport::transports.UpdateReceivedBytes (bytes_transferred); + UpdateNumReceivedBytes (bytes_transferred + 2); + i2p::transport::transports.UpdateReceivedBytes (bytes_transferred + 2); uint8_t nonce[12]; CreateNonce (m_ReceiveSequenceNumber, nonce); m_ReceiveSequenceNumber++; if (i2p::crypto::AEADChaCha20Poly1305 (m_NextReceivedBuffer, m_NextReceivedLen-16, nullptr, 0, m_ReceiveKey, nonce, m_NextReceivedBuffer, m_NextReceivedLen, false)) @@ -880,7 +909,7 @@ namespace transport auto size = bufbe16toh (frame + offset); offset += 2; LogPrint (eLogDebug, "NTCP2: Block type ", (int)blk, " of size ", size); - if (size > len) + if (offset + size > len) { LogPrint (eLogError, "NTCP2: Unexpected block length ", size); break; @@ -1025,6 +1054,11 @@ namespace transport macBuf = m_NextSendBuffer + paddingLen; totalLen += paddingLen; } + if (totalLen > NTCP2_UNENCRYPTED_FRAME_MAX_SIZE) + { + LogPrint (eLogError, "NTCP2: Frame to send is too long ", totalLen); + return; + } uint8_t nonce[12]; CreateNonce (m_SendSequenceNumber, nonce); m_SendSequenceNumber++; i2p::crypto::AEADChaCha20Poly1305Encrypt (encryptBufs, m_SendKey, nonce, macBuf); // encrypt buffers @@ -1049,6 +1083,12 @@ namespace transport delete[] m_NextSendBuffer; m_NextSendBuffer = nullptr; return; } + if (payloadLen > NTCP2_UNENCRYPTED_FRAME_MAX_SIZE) + { + LogPrint (eLogError, "NTCP2: Buffer to send is too long ", payloadLen); + delete[] m_NextSendBuffer; m_NextSendBuffer = nullptr; + return; + } // encrypt uint8_t nonce[12]; CreateNonce (m_SendSequenceNumber, nonce); m_SendSequenceNumber++; @@ -1073,11 +1113,10 @@ namespace transport } else { - m_LastActivityTimestamp = i2p::util::GetSecondsSinceEpoch (); - m_NumSentBytes += bytes_transferred; + UpdateNumSentBytes (bytes_transferred); i2p::transport::transports.UpdateSentBytes (bytes_transferred); LogPrint (eLogDebug, "NTCP2: Next frame sent ", bytes_transferred); - if (m_LastActivityTimestamp > m_NextRouterInfoResendTime) + if (GetLastActivityTimestamp () > m_NextRouterInfoResendTime) { m_NextRouterInfoResendTime += NTCP2_ROUTERINFO_RESEND_INTERVAL + rand ()%NTCP2_ROUTERINFO_RESEND_INTERVAL_THRESHOLD; @@ -1086,7 +1125,7 @@ namespace transport else { SendQueue (); - m_SendQueueSize = m_SendQueue.size (); + SetSendQueueSize (m_SendQueue.size ()); } } } @@ -1096,20 +1135,31 @@ namespace transport if (!m_SendQueue.empty ()) { std::vector > msgs; + auto ts = i2p::util::GetMillisecondsSinceEpoch (); size_t s = 0; while (!m_SendQueue.empty ()) { auto msg = m_SendQueue.front (); + if (!msg || msg->IsExpired (ts)) + { + // drop null or expired message + if (msg) msg->Drop (); + m_SendQueue.pop_front (); + continue; + } size_t len = msg->GetNTCP2Length (); if (s + len + 3 <= NTCP2_UNENCRYPTED_FRAME_MAX_SIZE) // 3 bytes block header { msgs.push_back (msg); s += (len + 3); m_SendQueue.pop_front (); + if (s >= NTCP2_SEND_AFTER_FRAME_SIZE) + break; // send frame right a way } else if (len + 3 > NTCP2_UNENCRYPTED_FRAME_MAX_SIZE) { LogPrint (eLogError, "NTCP2: I2NP message of size ", len, " can't be sent. Dropped"); + msg->Drop (); m_SendQueue.pop_front (); } else @@ -1125,7 +1175,12 @@ namespace transport len -= 3; if (msgLen < 256) msgLen = 256; // for short message padding should not be always zero size_t paddingSize = (msgLen*NTCP2_MAX_PADDING_RATIO)/100; - if (msgLen + paddingSize + 3 > NTCP2_UNENCRYPTED_FRAME_MAX_SIZE) paddingSize = NTCP2_UNENCRYPTED_FRAME_MAX_SIZE - msgLen -3; + if (msgLen + paddingSize + 3 > NTCP2_UNENCRYPTED_FRAME_MAX_SIZE) + { + int l = (int)NTCP2_UNENCRYPTED_FRAME_MAX_SIZE - msgLen -3; + if (l <= 0) return 0; + paddingSize = l; + } if (paddingSize > len) paddingSize = len; if (paddingSize) { @@ -1134,7 +1189,7 @@ namespace transport RAND_bytes ((uint8_t *)m_PaddingSizes, sizeof (m_PaddingSizes)); m_NextPaddingSize = 0; } - paddingSize = m_PaddingSizes[m_NextPaddingSize++] % paddingSize; + paddingSize = m_PaddingSizes[m_NextPaddingSize++] % (paddingSize + 1); } buf[0] = eNTCP2BlkPadding; // blk htobe16buf (buf + 1, paddingSize); // size @@ -1199,8 +1254,13 @@ namespace transport void NTCP2Session::PostI2NPMessages (std::vector > msgs) { if (m_IsTerminated) return; + bool isSemiFull = m_SendQueue.size () > NTCP2_MAX_OUTGOING_QUEUE_SIZE/2; for (auto it: msgs) - m_SendQueue.push_back (std::move (it)); + if (isSemiFull && it->onDrop) + it->Drop (); // drop earlier because we can handle it + else + m_SendQueue.push_back (std::move (it)); + if (!m_IsSending) SendQueue (); else if (m_SendQueue.size () > NTCP2_MAX_OUTGOING_QUEUE_SIZE) @@ -1209,12 +1269,12 @@ namespace transport GetIdentHashBase64(), " exceeds ", NTCP2_MAX_OUTGOING_QUEUE_SIZE); Terminate (); } - m_SendQueueSize = m_SendQueue.size (); + SetSendQueueSize (m_SendQueue.size ()); } void NTCP2Session::SendLocalRouterInfo (bool update) { - if (update || !IsOutgoing ()) // we send it in SessionConfirmed for ougoing session + if (update || !IsOutgoing ()) // we send it in SessionConfirmed for outgoing session m_Server.GetService ().post (std::bind (&NTCP2Session::SendRouterInfo, shared_from_this ())); } @@ -1380,7 +1440,11 @@ namespace transport void NTCP2Server::RemoveNTCP2Session (std::shared_ptr session) { if (session && session->GetRemoteIdentity ()) - m_NTCP2Sessions.erase (session->GetRemoteIdentity ()->GetIdentHash ()); + { + auto it = m_NTCP2Sessions.find (session->GetRemoteIdentity ()->GetIdentHash ()); + if (it != m_NTCP2Sessions.end () && it->second == session) + m_NTCP2Sessions.erase (it); + } } std::shared_ptr NTCP2Server::FindNTCP2Session (const i2p::data::IdentHash& ident) @@ -1398,7 +1462,8 @@ namespace transport LogPrint (eLogError, "NTCP2: Can't connect to unspecified address"); return; } - LogPrint (eLogDebug, "NTCP2: Connecting to ", conn->GetRemoteEndpoint ()); + LogPrint (eLogDebug, "NTCP2: Connecting to ", conn->GetRemoteEndpoint (), + " (", i2p::data::GetIdentHashAbbreviation (conn->GetRemoteIdentity ()->GetIdentHash ()), ")"); GetService ().post([this, conn]() { if (this->AddNTCP2Session (conn)) @@ -1454,7 +1519,8 @@ namespace transport } else { - LogPrint (eLogDebug, "NTCP2: Connected to ", conn->GetRemoteEndpoint ()); + LogPrint (eLogDebug, "NTCP2: Connected to ", conn->GetRemoteEndpoint (), + " (", i2p::data::GetIdentHashAbbreviation (conn->GetRemoteIdentity ()->GetIdentHash ()), ")"); conn->ClientLogin (); } } @@ -1468,7 +1534,7 @@ namespace transport if (!ec) { LogPrint (eLogDebug, "NTCP2: Connected from ", ep); - if (!i2p::util::net::IsInReservedRange(ep.address ())) + if (!i2p::transport::transports.IsInReservedRange(ep.address ())) { if (m_PendingIncomingSessions.emplace (ep.address (), conn).second) { @@ -1515,7 +1581,7 @@ namespace transport if (!ec) { LogPrint (eLogDebug, "NTCP2: Connected from ", ep); - if (!i2p::util::net::IsInReservedRange(ep.address ()) || + if (!i2p::transport::transports.IsInReservedRange(ep.address ()) || i2p::util::net::IsYggdrasilAddress (ep.address ())) { if (m_PendingIncomingSessions.emplace (ep.address (), conn).second) @@ -1663,47 +1729,18 @@ namespace transport case eSocksProxy: { // TODO: support username/password auth etc - static const uint8_t buff[3] = {SOCKS5_VER, 0x01, 0x00}; - boost::asio::async_write(conn->GetSocket(), boost::asio::buffer(buff, 3), boost::asio::transfer_all(), - [] (const boost::system::error_code & ec, std::size_t transferred) - { - (void) transferred; - if(ec) - { - LogPrint(eLogWarning, "NTCP2: SOCKS5 write error ", ec.message()); - } - }); - auto readbuff = std::make_shared >(2); - boost::asio::async_read(conn->GetSocket(), boost::asio::buffer(readbuff->data (), 2), - [this, readbuff, timer, conn](const boost::system::error_code & ec, std::size_t transferred) - { - if(ec) - { - LogPrint(eLogError, "NTCP2: SOCKS5 read error ", ec.message()); - timer->cancel(); - conn->Terminate(); - return; - } - else if(transferred == 2) - { - if((*readbuff)[1] == 0x00) - { - AfterSocksHandshake(conn, timer); - return; - } - else if ((*readbuff)[1] == 0xff) - { - LogPrint(eLogError, "NTCP2: SOCKS5 proxy rejected authentication"); - timer->cancel(); - conn->Terminate(); - return; - } - LogPrint(eLogError, "NTCP2:", (int)(*readbuff)[1]); - } - LogPrint(eLogError, "NTCP2: SOCKS5 server gave invalid response"); + Socks5Handshake (conn->GetSocket(), conn->GetRemoteEndpoint (), + [conn, timer](const boost::system::error_code& ec) + { timer->cancel(); - conn->Terminate(); - }); + if (!ec) + conn->ClientLogin(); + else + { + LogPrint(eLogError, "NTCP2: SOCKS proxy handshake error ", ec.message()); + conn->Terminate(); + } + }); break; } case eHTTPProxy: @@ -1771,71 +1808,6 @@ namespace transport } } - void NTCP2Server::AfterSocksHandshake(std::shared_ptr conn, std::shared_ptr timer) - { - // build request - size_t sz = 6; // header + port - auto buff = std::make_shared >(256); - auto readbuff = std::make_shared >(256); - (*buff)[0] = SOCKS5_VER; - (*buff)[1] = SOCKS5_CMD_CONNECT; - (*buff)[2] = 0x00; - - auto& ep = conn->GetRemoteEndpoint (); - if(ep.address ().is_v4 ()) - { - (*buff)[3] = SOCKS5_ATYP_IPV4; - auto addrbytes = ep.address ().to_v4().to_bytes(); - sz += 4; - memcpy(buff->data () + 4, addrbytes.data(), 4); - } - else if (ep.address ().is_v6 ()) - { - (*buff)[3] = SOCKS5_ATYP_IPV6; - auto addrbytes = ep.address ().to_v6().to_bytes(); - sz += 16; - memcpy(buff->data () + 4, addrbytes.data(), 16); - } - else - { - // We mustn't really fall here because all connections are made to IP addresses - LogPrint(eLogError, "NTCP2: Tried to connect to unexpected address via proxy"); - return; - } - htobe16buf(buff->data () + sz - 2, ep.port ()); - boost::asio::async_write(conn->GetSocket(), boost::asio::buffer(buff->data (), sz), boost::asio::transfer_all(), - [buff](const boost::system::error_code & ec, std::size_t written) - { - if(ec) - { - LogPrint(eLogError, "NTCP2: Failed to write handshake to socks proxy ", ec.message()); - return; - } - }); - - boost::asio::async_read(conn->GetSocket(), boost::asio::buffer(readbuff->data (), SOCKS5_UDP_IPV4_REQUEST_HEADER_SIZE), // read min reply size - boost::asio::transfer_all(), - [timer, conn, readbuff](const boost::system::error_code & e, std::size_t transferred) - { - if (e) - LogPrint(eLogError, "NTCP2: SOCKS proxy read error ", e.message()); - else if (!(*readbuff)[1]) // succeeded - { - boost::system::error_code ec; - size_t moreBytes = conn->GetSocket ().available(ec); - if (moreBytes) // read remaining portion of reply if ipv6 received - boost::asio::read (conn->GetSocket (), boost::asio::buffer(readbuff->data (), moreBytes), boost::asio::transfer_all (), ec); - timer->cancel(); - conn->ClientLogin(); - return; - } - else - LogPrint(eLogError, "NTCP2: Proxy reply error ", (int)(*readbuff)[1]); - timer->cancel(); - conn->Terminate(); - }); - } - void NTCP2Server::SetLocalAddress (const boost::asio::ip::address& localAddress) { auto addr = std::make_shared(boost::asio::ip::tcp::endpoint(localAddress, 0)); diff --git a/libi2pd/NTCP2.h b/libi2pd/NTCP2.h index ba1380c3..a566b38d 100644 --- a/libi2pd/NTCP2.h +++ b/libi2pd/NTCP2.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2022, The PurpleI2P Project +* Copyright (c) 2013-2024, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -28,6 +28,7 @@ namespace transport { const size_t NTCP2_UNENCRYPTED_FRAME_MAX_SIZE = 65519; + const size_t NTCP2_SEND_AFTER_FRAME_SIZE = 16386; // send frame when exceeds this size const size_t NTCP2_SESSION_REQUEST_MAX_SIZE = 287; const size_t NTCP2_SESSION_CREATED_MAX_SIZE = 287; const int NTCP2_MAX_PADDING_RATIO = 6; // in % @@ -266,8 +267,7 @@ namespace transport void HandleConnect (const boost::system::error_code& ecode, std::shared_ptr conn, std::shared_ptr timer); void HandleProxyConnect(const boost::system::error_code& ecode, std::shared_ptr conn, std::shared_ptr timer); - void AfterSocksHandshake(std::shared_ptr conn, std::shared_ptr timer); - + // timer void ScheduleTermination (); void HandleTerminationTimer (const boost::system::error_code& ecode); diff --git a/libi2pd/NetDb.cpp b/libi2pd/NetDb.cpp index 447eb873..bfd07df3 100644 --- a/libi2pd/NetDb.cpp +++ b/libi2pd/NetDb.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2023, The PurpleI2P Project +* Copyright (c) 2013-2024, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -59,7 +59,7 @@ namespace data { Reseed (); } - else if (!GetRandomRouter (i2p::context.GetSharedRouterInfo (), false)) + else if (!GetRandomRouter (i2p::context.GetSharedRouterInfo (), false, false)) Reseed (); // we don't have a router we can connect to. Trying to reseed auto it = m_RouterInfos.find (i2p::context.GetIdentHash ()); @@ -106,7 +106,7 @@ namespace data { i2p::util::SetThreadName("NetDB"); - uint64_t lastSave = 0, lastExploratory = 0, lastManageRequest = 0, lastDestinationCleanup = 0; + uint64_t lastManage = 0, lastExploratory = 0, lastManageRequest = 0; uint64_t lastProfilesCleanup = i2p::util::GetSecondsSinceEpoch (); int16_t profilesCleanupVariance = 0; @@ -149,32 +149,26 @@ namespace data if (!i2p::transport::transports.IsOnline ()) continue; // don't manage netdb when offline uint64_t ts = i2p::util::GetSecondsSinceEpoch (); - if (ts - lastManageRequest >= 15 || ts + 15 < lastManageRequest) // manage requests every 15 seconds + if (ts - lastManageRequest >= MANAGE_REQUESTS_INTERVAL || ts + MANAGE_REQUESTS_INTERVAL < lastManageRequest) // manage requests every 15 seconds { m_Requests.ManageRequests (); lastManageRequest = ts; } - if (ts - lastSave >= 60 || ts + 60 < lastSave) // save routers, manage leasesets and validate subscriptions every minute + if (ts - lastManage >= 60 || ts + 60 < lastManage) // manage routers and leasesets every minute { - if (lastSave) + if (lastManage) { - SaveUpdated (); + ManageRouterInfos (); ManageLeaseSets (); } - lastSave = ts; - } - - if (ts - lastDestinationCleanup >= i2p::garlic::INCOMING_TAGS_EXPIRATION_TIMEOUT || - ts + i2p::garlic::INCOMING_TAGS_EXPIRATION_TIMEOUT < lastDestinationCleanup) - { - i2p::context.CleanupDestination (); - lastDestinationCleanup = ts; + lastManage = ts; } if (ts - lastProfilesCleanup >= (uint64_t)(i2p::data::PEER_PROFILE_AUTOCLEAN_TIMEOUT + profilesCleanupVariance) || ts + i2p::data::PEER_PROFILE_AUTOCLEAN_TIMEOUT < lastProfilesCleanup) { + m_RouterProfilesPool.CleanUpMt (); if (m_PersistProfiles) PersistProfiles (); DeleteObsoleteProfiles (); lastProfilesCleanup = ts; @@ -225,7 +219,8 @@ namespace data bool NetDb::AddRouterInfo (const IdentHash& ident, const uint8_t * buf, int len) { bool updated; - AddRouterInfo (ident, buf, len, updated); + if (!AddRouterInfo (ident, buf, len, updated)) + updated = false; return updated; } @@ -239,40 +234,49 @@ namespace data { bool wasFloodfill = r->IsFloodfill (); { - std::unique_lock l(m_RouterInfosMutex); + std::lock_guard l(m_RouterInfosMutex); if (!r->Update (buf, len)) { updated = false; m_Requests.RequestComplete (ident, r); return r; - } - if (r->IsUnreachable ()) + } + if (r->IsUnreachable () || + i2p::util::GetMillisecondsSinceEpoch () + NETDB_EXPIRATION_TIMEOUT_THRESHOLD*1000LL < r->GetTimestamp ()) { - // delete router as invalid after update + // delete router as invalid or from future after update m_RouterInfos.erase (ident); if (wasFloodfill) - { - std::unique_lock l(m_FloodfillsMutex); + { + std::lock_guard l(m_FloodfillsMutex); m_Floodfills.Remove (r->GetIdentHash ()); - } + } m_Requests.RequestComplete (ident, nullptr); - return nullptr; - } + return nullptr; + } } - LogPrint (eLogInfo, "NetDb: RouterInfo updated: ", ident.ToBase64()); + if (CheckLogLevel (eLogInfo)) + LogPrint (eLogInfo, "NetDb: RouterInfo updated: ", ident.ToBase64()); if (wasFloodfill != r->IsFloodfill ()) // if floodfill status updated { - LogPrint (eLogDebug, "NetDb: RouterInfo floodfill status updated: ", ident.ToBase64()); - std::unique_lock l(m_FloodfillsMutex); + if (CheckLogLevel (eLogDebug)) + LogPrint (eLogDebug, "NetDb: RouterInfo floodfill status updated: ", ident.ToBase64()); + std::lock_guard l(m_FloodfillsMutex); if (wasFloodfill) m_Floodfills.Remove (r->GetIdentHash ()); else if (r->IsEligibleFloodfill ()) - m_Floodfills.Insert (r); + { + if (m_Floodfills.GetSize () < NETDB_NUM_FLOODFILLS_THRESHOLD || r->GetProfile ()->IsReal ()) + m_Floodfills.Insert (r); + else + r->ResetFlooldFill (); + } } } else { - LogPrint (eLogDebug, "NetDb: RouterInfo is older: ", ident.ToBase64()); + if (CheckLogLevel (eLogDebug)) + LogPrint (eLogDebug, "NetDb: RouterInfo is older: ", ident.ToBase64()); updated = false; } } @@ -284,16 +288,23 @@ namespace data { bool inserted = false; { - std::unique_lock l(m_RouterInfosMutex); + std::lock_guard l(m_RouterInfosMutex); inserted = m_RouterInfos.insert ({r->GetIdentHash (), r}).second; } if (inserted) { - LogPrint (eLogInfo, "NetDb: RouterInfo added: ", ident.ToBase64()); + if (CheckLogLevel (eLogInfo)) + LogPrint (eLogInfo, "NetDb: RouterInfo added: ", ident.ToBase64()); if (r->IsFloodfill () && r->IsEligibleFloodfill ()) { - std::unique_lock l(m_FloodfillsMutex); - m_Floodfills.Insert (r); + if (m_Floodfills.GetSize () < NETDB_NUM_FLOODFILLS_THRESHOLD || + r->GetProfile ()->IsReal ()) // don't insert floodfill until it's known real if we have enough + { + std::lock_guard l(m_FloodfillsMutex); + m_Floodfills.Insert (r); + } + else + r->ResetFlooldFill (); } } else @@ -312,7 +323,7 @@ namespace data bool NetDb::AddLeaseSet (const IdentHash& ident, const uint8_t * buf, int len) { - std::unique_lock lock(m_LeaseSetsMutex); + std::lock_guard lock(m_LeaseSetsMutex); bool updated = false; auto it = m_LeaseSets.find(ident); if (it != m_LeaseSets.end () && it->second->GetStoreType () == i2p::data::NETDB_STORE_TYPE_LEASESET) @@ -324,10 +335,11 @@ namespace data if(it->second->GetExpirationTime() < expires) { it->second->Update (buf, len, false); // signature is verified already - LogPrint (eLogInfo, "NetDb: LeaseSet updated: ", ident.ToBase32()); + if (CheckLogLevel (eLogInfo)) + LogPrint (eLogInfo, "NetDb: LeaseSet updated: ", ident.ToBase32()); updated = true; } - else + else if (CheckLogLevel (eLogDebug)) LogPrint(eLogDebug, "NetDb: LeaseSet is older: ", ident.ToBase32()); } else @@ -338,7 +350,8 @@ namespace data auto leaseSet = std::make_shared (buf, len, false); // we don't need leases in netdb if (leaseSet->IsValid ()) { - LogPrint (eLogInfo, "NetDb: LeaseSet added: ", ident.ToBase32()); + if (CheckLogLevel (eLogInfo)) + LogPrint (eLogInfo, "NetDb: LeaseSet added: ", ident.ToBase32()); m_LeaseSets[ident] = leaseSet; updated = true; } @@ -353,7 +366,7 @@ namespace data auto leaseSet = std::make_shared (storeType, buf, len, false); // we don't need leases in netdb if (leaseSet->IsValid ()) { - std::unique_lock lock(m_LeaseSetsMutex); + std::lock_guard lock(m_LeaseSetsMutex); auto it = m_LeaseSets.find(ident); if (it == m_LeaseSets.end () || it->second->GetStoreType () != storeType || leaseSet->GetPublishedTimestamp () > it->second->GetPublishedTimestamp ()) @@ -362,7 +375,8 @@ namespace data i2p::util::GetSecondsSinceEpoch () + NETDB_EXPIRATION_TIMEOUT_THRESHOLD > leaseSet->GetPublishedTimestamp ()) { // TODO: implement actual update - LogPrint (eLogInfo, "NetDb: LeaseSet2 updated: ", ident.ToBase32()); + if (CheckLogLevel (eLogInfo)) + LogPrint (eLogInfo, "NetDb: LeaseSet2 updated: ", ident.ToBase32()); m_LeaseSets[ident] = leaseSet; return true; } @@ -380,7 +394,7 @@ namespace data std::shared_ptr NetDb::FindRouter (const IdentHash& ident) const { - std::unique_lock l(m_RouterInfosMutex); + std::lock_guard l(m_RouterInfosMutex); auto it = m_RouterInfos.find (ident); if (it != m_RouterInfos.end ()) return it->second; @@ -390,7 +404,7 @@ namespace data std::shared_ptr NetDb::FindLeaseSet (const IdentHash& destination) const { - std::unique_lock lock(m_LeaseSetsMutex); + std::lock_guard lock(m_LeaseSetsMutex); auto it = m_LeaseSets.find (destination); if (it != m_LeaseSets.end ()) return it->second; @@ -409,16 +423,23 @@ namespace data void NetDb::SetUnreachable (const IdentHash& ident, bool unreachable) { - auto it = m_RouterInfos.find (ident); - if (it != m_RouterInfos.end ()) + auto r = FindRouter (ident); + if (r) { - it->second->SetUnreachable (unreachable); - if (unreachable) - { - auto profile = it->second->GetProfile (); - if (profile) - profile->Unreachable (); - } + r->SetUnreachable (unreachable); + auto profile = r->GetProfile (); + if (profile) + profile->Unreachable (unreachable); + } + } + + void NetDb::ExcludeReachableTransports (const IdentHash& ident, RouterInfo::CompatibleTransports transports) + { + auto r = FindRouter (ident); + if (r) + { + std::lock_guard l(m_RouterInfosMutex); + r->ExcludeReachableTransports (transports); } } @@ -507,7 +528,7 @@ namespace data void NetDb::VisitLeaseSets(LeaseSetVisitor v) { - std::unique_lock lock(m_LeaseSetsMutex); + std::lock_guard lock(m_LeaseSetsMutex); for ( auto & entry : m_LeaseSets) v(entry.first, entry.second); } @@ -523,7 +544,7 @@ namespace data void NetDb::VisitRouterInfos(RouterInfoVisitor v) { - std::unique_lock lock(m_RouterInfosMutex); + std::lock_guard lock(m_RouterInfosMutex); for ( const auto & item : m_RouterInfos ) v(item.second); } @@ -535,7 +556,7 @@ namespace data size_t iters = max_iters_per_cyle; while(n > 0) { - std::unique_lock lock(m_RouterInfosMutex); + std::lock_guard lock(m_RouterInfosMutex); uint32_t idx = rand () % m_RouterInfos.size (); uint32_t i = 0; for (const auto & it : m_RouterInfos) { @@ -597,7 +618,9 @@ namespace data uint64_t expirationTimeout = NETDB_MAX_EXPIRATION_TIMEOUT*1000LL; uint64_t ts = i2p::util::GetMillisecondsSinceEpoch(); auto uptime = i2p::context.GetUptime (); - bool isLowRate = i2p::tunnel::tunnels.GetTunnelCreationSuccessRate () < NETDB_MIN_TUNNEL_CREATION_SUCCESS_RATE; + double minTunnelCreationSuccessRate; + i2p::config::GetOption("limits.zombies", minTunnelCreationSuccessRate); + bool isLowRate = i2p::tunnel::tunnels.GetPreciseTunnelCreationSuccessRate () < minTunnelCreationSuccessRate; // routers don't expire if less than 90 or uptime is less than 1 hour bool checkForExpiration = total > NETDB_MIN_ROUTERS && uptime > 600; // 10 minutes if (checkForExpiration && uptime > 3600) // 1 hour @@ -607,17 +630,18 @@ namespace data auto own = i2p::context.GetSharedRouterInfo (); for (auto& it: m_RouterInfos) { - if (it.second == own) continue; // skip own + if (!it.second || it.second == own) continue; // skip own std::string ident = it.second->GetIdentHashBase64(); if (it.second->IsUpdated ()) { if (it.second->GetBuffer ()) - { + { // we have something to save it.second->SaveToFile (m_Storage.Path(ident)); it.second->SetUnreachable (false); + std::lock_guard l(m_RouterInfosMutex); // possible collision between DeleteBuffer and Update it.second->DeleteBuffer (); - } + } it.second->SetUpdated (false); updatedCount++; continue; @@ -629,25 +653,28 @@ namespace data (it.second->IsFloodfill () && totalFloodfills - deletedFloodfillsCount < NETDB_MIN_FLOODFILLS))) it.second->SetUnreachable (false); if (!it.second->IsUnreachable ()) - { + { // find & mark expired routers - if (!it.second->IsReachable () && (it.second->GetCompatibleTransports (true) & RouterInfo::eSSU2V4)) - // non-reachable router, but reachable by ipv4 SSU2 means introducers - { - if (ts > it.second->GetTimestamp () + NETDB_INTRODUCEE_EXPIRATION_TIMEOUT*1000LL) - // RouterInfo expires after 1 hour if uses introducer - it.second->SetUnreachable (true); - } - else if (checkForExpiration && ts > it.second->GetTimestamp () + expirationTimeout) + if (!it.second->GetCompatibleTransports (true)) // non reachable by any transport it.second->SetUnreachable (true); else if (ts + NETDB_EXPIRATION_TIMEOUT_THRESHOLD*1000LL < it.second->GetTimestamp ()) { LogPrint (eLogWarning, "NetDb: RouterInfo is from future for ", (it.second->GetTimestamp () - ts)/1000LL, " seconds"); it.second->SetUnreachable (true); } + else if (checkForExpiration) + { + if (ts > it.second->GetTimestamp () + expirationTimeout) + it.second->SetUnreachable (true); + else if ((ts > it.second->GetTimestamp () + expirationTimeout/2) && // more than half of expiration + total > NETDB_NUM_ROUTERS_THRESHOLD && !it.second->IsHighBandwidth() && // low bandwidth + !it.second->IsFloodfill() && (!i2p::context.IsFloodfill () || // non floodfill + (CreateRoutingKey (it.second->GetIdentHash ()) ^ i2p::context.GetIdentHash ()).metric[0] >= 0x02)) // different first 7 bits + it.second->SetUnreachable (true); + } if (it.second->IsUnreachable () && i2p::transport::transports.IsConnected (it.second->GetIdentHash ())) it.second->SetUnreachable (false); // don't expire connected router - } + } if (it.second->IsUnreachable ()) { @@ -662,7 +689,7 @@ namespace data m_RouterInfoBuffersPool.CleanUpMt (); m_RouterInfoAddressesPool.CleanUpMt (); m_RouterInfoAddressVectorsPool.CleanUpMt (); - m_IdentitiesPool.CleanUpMt (); + m_IdentitiesPool.CleanUpMt (); if (updatedCount > 0) LogPrint (eLogInfo, "NetDb: Saved ", updatedCount, " new/updated routers"); @@ -671,21 +698,21 @@ namespace data LogPrint (eLogInfo, "NetDb: Deleting ", deletedCount, " unreachable routers"); // clean up RouterInfos table { - std::unique_lock l(m_RouterInfosMutex); + std::lock_guard l(m_RouterInfosMutex); for (auto it = m_RouterInfos.begin (); it != m_RouterInfos.end ();) { - if (it->second->IsUnreachable ()) + if (!it->second || it->second->IsUnreachable ()) it = m_RouterInfos.erase (it); else - { + { it->second->DropProfile (); it++; - } + } } } // clean up expired floodfills or not floodfills anymore { - std::unique_lock l(m_FloodfillsMutex); + std::lock_guard l(m_FloodfillsMutex); m_Floodfills.Cleanup ([](const std::shared_ptr& r)->bool { return r && r->IsFloodfill () && !r->IsUnreachable (); @@ -696,7 +723,8 @@ namespace data void NetDb::RequestDestination (const IdentHash& destination, RequestedDestination::RequestComplete requestComplete, bool direct) { - auto dest = m_Requests.CreateRequest (destination, false, requestComplete); // non-exploratory + if (direct && i2p::transport::transports.RoutesRestricted ()) direct = false; // always use tunnels for restricted routes + auto dest = m_Requests.CreateRequest (destination, false, direct, requestComplete); // non-exploratory if (!dest) { LogPrint (eLogWarning, "NetDb: Destination ", destination.ToBase64(), " is requested already"); @@ -710,14 +738,23 @@ namespace data !i2p::transport::transports.IsConnected (floodfill->GetIdentHash ())) direct = false; // floodfill can't be reached directly if (direct) - transports.SendMessage (floodfill->GetIdentHash (), dest->CreateRequestMessage (floodfill->GetIdentHash ())); + { + auto msg = dest->CreateRequestMessage (floodfill->GetIdentHash ()); + msg->onDrop = [this, dest]() { this->m_Requests.SendNextRequest (dest); }; + transports.SendMessage (floodfill->GetIdentHash (), msg); + } else { auto pool = i2p::tunnel::tunnels.GetExploratoryPool (); auto outbound = pool ? pool->GetNextOutboundTunnel (nullptr, floodfill->GetCompatibleTransports (false)) : nullptr; auto inbound = pool ? pool->GetNextInboundTunnel (nullptr, floodfill->GetCompatibleTransports (true)) : nullptr; if (outbound && inbound) - outbound->SendTunnelDataMsgTo (floodfill->GetIdentHash (), 0, dest->CreateRequestMessage (floodfill, inbound)); + { + auto msg = dest->CreateRequestMessage (floodfill, inbound); + msg->onDrop = [this, dest]() { this->m_Requests.SendNextRequest (dest); }; + outbound->SendTunnelDataMsgTo (floodfill->GetIdentHash (), 0, + i2p::garlic::WrapECIESX25519MessageForRouter (msg, floodfill->GetIdentity ()->GetEncryptionPublicKey ())); + } else { LogPrint (eLogError, "NetDb: ", destination.ToBase64(), " destination requested, but no tunnels found"); @@ -732,10 +769,10 @@ namespace data } } - void NetDb::RequestDestinationFrom (const IdentHash& destination, const IdentHash & from, bool exploritory, RequestedDestination::RequestComplete requestComplete) + void NetDb::RequestDestinationFrom (const IdentHash& destination, const IdentHash & from, bool exploratory, RequestedDestination::RequestComplete requestComplete) { - auto dest = m_Requests.CreateRequest (destination, exploritory, requestComplete); // non-exploratory + auto dest = m_Requests.CreateRequest (destination, exploratory, true, requestComplete); // non-exploratory if (!dest) { LogPrint (eLogWarning, "NetDb: Destination ", destination.ToBase64(), " is requested already"); @@ -782,19 +819,22 @@ namespace data LogPrint (eLogError, "NetDb: Database store msg with reply token is too short ", len, ". Dropped"); return; } - auto deliveryStatus = CreateDeliveryStatusMsg (replyToken); uint32_t tunnelID = bufbe32toh (buf + offset); offset += 4; - if (!tunnelID) // send response directly - transports.SendMessage (buf + offset, deliveryStatus); - else + if (replyToken != 0xFFFFFFFFU) // if not caught on OBEP or IBGW { - auto pool = i2p::tunnel::tunnels.GetExploratoryPool (); - auto outbound = pool ? pool->GetNextOutboundTunnel () : nullptr; - if (outbound) - outbound->SendTunnelDataMsgTo (buf + offset, tunnelID, deliveryStatus); + auto deliveryStatus = CreateDeliveryStatusMsg (replyToken); + if (!tunnelID) // send response directly + transports.SendMessage (buf + offset, deliveryStatus); else - LogPrint (eLogWarning, "NetDb: No outbound tunnels for DatabaseStore reply found"); + { + auto pool = i2p::tunnel::tunnels.GetExploratoryPool (); + auto outbound = pool ? pool->GetNextOutboundTunnel () : nullptr; + if (outbound) + outbound->SendTunnelDataMsgTo (buf + offset, tunnelID, deliveryStatus); + else + LogPrint (eLogWarning, "NetDb: No outbound tunnels for DatabaseStore reply found"); + } } offset += 32; } @@ -815,23 +855,31 @@ namespace data LogPrint (eLogError, "NetDb: Database store message is too long ", len); return; } - if (!m->from) // unsolicited LS must be received directly + if (!context.IsFloodfill ()) + { + LogPrint (eLogInfo, "NetDb: Not Floodfill, LeaseSet store request ignored for ", ident.ToBase32()); + return; + } + else if (!m->from) // unsolicited LS must be received directly { if (storeType == NETDB_STORE_TYPE_LEASESET) // 1 { - LogPrint (eLogDebug, "NetDb: Store request: LeaseSet for ", ident.ToBase32()); + if (CheckLogLevel (eLogDebug)) + LogPrint (eLogDebug, "NetDb: Store request: LeaseSet for ", ident.ToBase32()); updated = AddLeaseSet (ident, buf + offset, len - offset); } else // all others are considered as LeaseSet2 { - LogPrint (eLogDebug, "NetDb: Store request: LeaseSet2 of type ", int(storeType), " for ", ident.ToBase32()); + if (CheckLogLevel (eLogDebug)) + LogPrint (eLogDebug, "NetDb: Store request: LeaseSet2 of type ", int(storeType), " for ", ident.ToBase32()); updated = AddLeaseSet2 (ident, buf + offset, len - offset, storeType); } } } else // RouterInfo { - LogPrint (eLogDebug, "NetDb: Store request: RouterInfo"); + if (CheckLogLevel (eLogDebug)) + LogPrint (eLogDebug, "NetDb: Store request: RouterInfo ", ident.ToBase64()); size_t size = bufbe16toh (buf + offset); offset += 2; if (size > MAX_RI_BUFFER_SIZE || size > len - offset) @@ -882,39 +930,9 @@ namespace data auto dest = m_Requests.FindRequest (ident); if (dest) { - bool deleteDest = true; if (num > 0) - { - auto pool = i2p::tunnel::tunnels.GetExploratoryPool (); - auto outbound = pool ? pool->GetNextOutboundTunnel () : nullptr; - auto inbound = pool ? pool->GetNextInboundTunnel () : nullptr; - if (!dest->IsExploratory ()) - { - // reply to our destination. Try other floodfills - if (outbound && inbound) - { - auto count = dest->GetExcludedPeers ().size (); - if (count < 7) - { - auto nextFloodfill = GetClosestFloodfill (dest->GetDestination (), dest->GetExcludedPeers ()); - if (nextFloodfill) - { - // request destination - LogPrint (eLogDebug, "NetDb: Try ", key, " at ", count, " floodfill ", nextFloodfill->GetIdentHash ().ToBase64 ()); - outbound->SendTunnelDataMsgTo (nextFloodfill->GetIdentHash (), 0, - dest->CreateRequestMessage (nextFloodfill, inbound)); - deleteDest = false; - } - } - else - LogPrint (eLogWarning, "NetDb: ", key, " was not found on ", count, " floodfills"); - } - } - - if (deleteDest) - // no more requests for the destinationation. delete it - m_Requests.RequestComplete (ident, nullptr); - } + // try to send next requests + m_Requests.SendNextRequest (dest); else // no more requests for destination possible. delete it m_Requests.RequestComplete (ident, nullptr); @@ -983,6 +1001,11 @@ namespace data std::shared_ptr replyMsg; if (lookupType == DATABASE_LOOKUP_TYPE_EXPLORATORY_LOOKUP) { + if (!context.IsFloodfill ()) + { + LogPrint (eLogWarning, "NetDb: Exploratory lookup to non-floodfill dropped"); + return; + } LogPrint (eLogInfo, "NetDb: Exploratory close to ", key, " ", numExcluded, " excluded"); std::set excludedRouters; const uint8_t * excluded_ident = excluded; @@ -1008,6 +1031,7 @@ namespace data if (lookupType == DATABASE_LOOKUP_TYPE_ROUTERINFO_LOOKUP || lookupType == DATABASE_LOOKUP_TYPE_NORMAL_LOOKUP) { + // try to find router auto router = FindRouter (ident); if (router && !router->IsUnreachable ()) { @@ -1020,17 +1044,26 @@ namespace data if (!replyMsg && (lookupType == DATABASE_LOOKUP_TYPE_LEASESET_LOOKUP || lookupType == DATABASE_LOOKUP_TYPE_NORMAL_LOOKUP)) { - auto leaseSet = FindLeaseSet (ident); - if (!leaseSet) + // try to find leaseset + if (context.IsFloodfill ()) + { + auto leaseSet = FindLeaseSet (ident); + if (!leaseSet) + { + // no leaseset found + LogPrint(eLogDebug, "NetDb: Requested LeaseSet not found for ", ident.ToBase32()); + } + else if (!leaseSet->IsExpired ()) // we don't send back expired leasesets + { + LogPrint (eLogDebug, "NetDb: Requested LeaseSet ", key, " found"); + replyMsg = CreateDatabaseStoreMsg (ident, leaseSet); + } + } + else if (lookupType == DATABASE_LOOKUP_TYPE_LEASESET_LOOKUP) { - // no lease set found - LogPrint(eLogDebug, "NetDb: Requested LeaseSet not found for ", ident.ToBase32()); - } - else if (!leaseSet->IsExpired ()) // we don't send back our LeaseSets - { - LogPrint (eLogDebug, "NetDb: Requested LeaseSet ", key, " found"); - replyMsg = CreateDatabaseStoreMsg (ident, leaseSet); - } + LogPrint (eLogWarning, "NetDb: Explicit LeaseSet lookup to non-floodfill dropped"); + return; + } } if (!replyMsg) @@ -1104,7 +1137,7 @@ namespace data for (int i = 0; i < numDestinations; i++) { RAND_bytes (randomHash, 32); - auto dest = m_Requests.CreateRequest (randomHash, true); // exploratory + auto dest = m_Requests.CreateRequest (randomHash, true, !throughTunnels); // exploratory if (!dest) { LogPrint (eLogWarning, "NetDb: Exploratory destination is requested already"); @@ -1169,15 +1202,17 @@ namespace data }); } - std::shared_ptr NetDb::GetRandomRouter (std::shared_ptr compatibleWith, bool reverse) const + std::shared_ptr NetDb::GetRandomRouter (std::shared_ptr compatibleWith, + bool reverse, bool endpoint) const { return GetRandomRouter ( - [compatibleWith, reverse](std::shared_ptr router)->bool + [compatibleWith, reverse, endpoint](std::shared_ptr router)->bool { return !router->IsHidden () && router != compatibleWith && - (reverse ? compatibleWith->IsReachableFrom (*router) : + (reverse ? (compatibleWith->IsReachableFrom (*router) && router->GetCompatibleTransports (true)): router->IsReachableFrom (*compatibleWith)) && - router->IsECIES (); + router->IsECIES () && !router->IsHighCongestion (false) && + (!endpoint || (router->IsV4 () && (!reverse || router->IsPublished (true)))); // endpoint must be ipv4 and published if inbound(reverse) }); } @@ -1201,17 +1236,20 @@ namespace data }); } - std::shared_ptr NetDb::GetHighBandwidthRandomRouter (std::shared_ptr compatibleWith, bool reverse) const + std::shared_ptr NetDb::GetHighBandwidthRandomRouter (std::shared_ptr compatibleWith, + bool reverse, bool endpoint) const { return GetRandomRouter ( - [compatibleWith, reverse](std::shared_ptr router)->bool + [compatibleWith, reverse, endpoint](std::shared_ptr router)->bool { return !router->IsHidden () && router != compatibleWith && - (reverse ? compatibleWith->IsReachableFrom (*router) : + (reverse ? (compatibleWith->IsReachableFrom (*router) && router->GetCompatibleTransports (true)) : router->IsReachableFrom (*compatibleWith)) && (router->GetCaps () & RouterInfo::eHighBandwidth) && router->GetVersion () >= NETDB_MIN_HIGHBANDWIDTH_VERSION && - router->IsECIES () && !router->IsHighCongestion (); + router->IsECIES () && !router->IsHighCongestion (true) && + (!endpoint || (router->IsV4 () && (!reverse || router->IsPublished (true)))); // endpoint must be ipv4 and published if inbound(reverse) + }); } @@ -1219,11 +1257,13 @@ namespace data std::shared_ptr NetDb::GetRandomRouter (Filter filter) const { if (m_RouterInfos.empty()) - return 0; + return nullptr; uint16_t inds[3]; RAND_bytes ((uint8_t *)inds, sizeof (inds)); - std::unique_lock l(m_RouterInfosMutex); - inds[0] %= m_RouterInfos.size (); + std::lock_guard l(m_RouterInfosMutex); + auto count = m_RouterInfos.size (); + if(count == 0) return nullptr; + inds[0] %= count; auto it = m_RouterInfos.begin (); std::advance (it, inds[0]); // try random router @@ -1282,7 +1322,7 @@ namespace data const std::set& excluded) const { IdentHash destKey = CreateRoutingKey (destination); - std::unique_lock l(m_FloodfillsMutex); + std::lock_guard l(m_FloodfillsMutex); return m_Floodfills.FindClosest (destKey, [&excluded](const std::shared_ptr& r)->bool { return r && !r->IsUnreachable () && !r->GetProfile ()->IsUnreachable () && @@ -1297,22 +1337,22 @@ namespace data IdentHash destKey = CreateRoutingKey (destination); std::vector > v; { - std::unique_lock l(m_FloodfillsMutex); + std::lock_guard l(m_FloodfillsMutex); v = m_Floodfills.FindClosest (destKey, num, [&excluded](const std::shared_ptr& r)->bool { return r && !r->IsUnreachable () && !r->GetProfile ()->IsUnreachable () && !excluded.count (r->GetIdentHash ()); }); - } + } if (v.empty ()) return res; - + XORMetric ourMetric; if (closeThanUsOnly) ourMetric = destKey ^ i2p::context.GetIdentHash (); for (auto& it: v) { if (closeThanUsOnly && ourMetric < (destKey ^ it->GetIdentHash ())) break; res.push_back (it->GetIdentHash ()); - } + } return res; } @@ -1348,6 +1388,17 @@ namespace data return r; } + void NetDb::ManageRouterInfos () + { + auto ts = i2p::util::GetSecondsSinceEpoch (); + { + std::lock_guard l(m_RouterInfosMutex); + for (auto& it: m_RouterInfos) + it.second->UpdateIntroducers (ts); + } + SaveUpdated (); + } + void NetDb::ManageLeaseSets () { auto ts = i2p::util::GetMillisecondsSinceEpoch (); diff --git a/libi2pd/NetDb.hpp b/libi2pd/NetDb.hpp index 71694d8d..8edc94d6 100644 --- a/libi2pd/NetDb.hpp +++ b/libi2pd/NetDb.hpp @@ -38,9 +38,9 @@ namespace data { const int NETDB_MIN_ROUTERS = 90; const int NETDB_MIN_FLOODFILLS = 5; - const int NETDB_MIN_TUNNEL_CREATION_SUCCESS_RATE = 8; // in percents + const int NETDB_NUM_FLOODFILLS_THRESHOLD = 1500; + const int NETDB_NUM_ROUTERS_THRESHOLD = 4*NETDB_NUM_FLOODFILLS_THRESHOLD; const int NETDB_FLOODFILL_EXPIRATION_TIMEOUT = 60 * 60; // 1 hour, in seconds - const int NETDB_INTRODUCEE_EXPIRATION_TIMEOUT = 65 * 60; const int NETDB_MIN_EXPIRATION_TIMEOUT = 90 * 60; // 1.5 hours const int NETDB_MAX_EXPIRATION_TIMEOUT = 27 * 60 * 60; // 27 hours const int NETDB_MAX_OFFLINE_EXPIRATION_TIMEOUT = 180; // in days @@ -85,8 +85,8 @@ namespace data void HandleNTCP2RouterInfoMsg (std::shared_ptr m); std::shared_ptr GetRandomRouter () const; - std::shared_ptr GetRandomRouter (std::shared_ptr compatibleWith, bool reverse) const; - std::shared_ptr GetHighBandwidthRandomRouter (std::shared_ptr compatibleWith, bool reverse) const; + std::shared_ptr GetRandomRouter (std::shared_ptr compatibleWith, bool reverse, bool endpoint) const; + std::shared_ptr GetHighBandwidthRandomRouter (std::shared_ptr compatibleWith, bool reverse, bool endpoint) const; std::shared_ptr GetRandomSSU2PeerTestRouter (bool v4, const std::set& excluded) const; std::shared_ptr GetRandomSSU2Introducer (bool v4, const std::set& excluded) const; std::shared_ptr GetClosestFloodfill (const IdentHash& destination, const std::set& excluded) const; @@ -95,6 +95,7 @@ namespace data std::shared_ptr GetClosestNonFloodfill (const IdentHash& destination, const std::set& excluded) const; std::shared_ptr GetRandomRouterInFamily (FamilyID fam) const; void SetUnreachable (const IdentHash& ident, bool unreachable); + void ExcludeReachableTransports (const IdentHash& ident, RouterInfo::CompatibleTransports transports); void PostI2NPMsg (std::shared_ptr msg); @@ -128,6 +129,7 @@ namespace data }; std::shared_ptr NewLease (const Lease& lease) { return m_LeasesPool.AcquireSharedMt (lease); }; std::shared_ptr NewIdentity (const uint8_t * buf, size_t len) { return m_IdentitiesPool.AcquireSharedMt (buf, len); }; + std::shared_ptr NewRouterProfile () { return m_RouterProfilesPool.AcquireSharedMt (); }; uint32_t GetPublishReplyToken () const { return m_PublishReplyToken; }; @@ -139,6 +141,7 @@ namespace data void Run (); // exploratory thread void Explore (int numDestinations); void Flood (const IdentHash& ident, std::shared_ptr floodMsg); + void ManageRouterInfos (); void ManageLeaseSets (); void ManageRequests (); @@ -184,6 +187,7 @@ namespace data i2p::util::MemoryPoolMt m_RouterInfoAddressVectorsPool; i2p::util::MemoryPoolMt m_LeasesPool; i2p::util::MemoryPoolMt m_IdentitiesPool; + i2p::util::MemoryPoolMt m_RouterProfilesPool; }; extern NetDb netdb; diff --git a/libi2pd/NetDbRequests.cpp b/libi2pd/NetDbRequests.cpp index 4011b8aa..4893fa1b 100644 --- a/libi2pd/NetDbRequests.cpp +++ b/libi2pd/NetDbRequests.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2023, The PurpleI2P Project +* Copyright (c) 2013-2024, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -11,11 +11,23 @@ #include "Transports.h" #include "NetDb.hpp" #include "NetDbRequests.h" +#include "ECIESX25519AEADRatchetSession.h" namespace i2p { namespace data { + RequestedDestination::RequestedDestination (const IdentHash& destination, bool isExploratory, bool direct): + m_Destination (destination), m_IsExploratory (isExploratory), m_IsDirect (direct), + m_CreationTime (i2p::util::GetSecondsSinceEpoch ()), m_LastRequestTime (0) + { + } + + RequestedDestination::~RequestedDestination () + { + if (m_RequestComplete) m_RequestComplete (nullptr); + } + std::shared_ptr RequestedDestination::CreateRequestMessage (std::shared_ptr router, std::shared_ptr replyTunnel) { @@ -28,7 +40,7 @@ namespace data msg = i2p::CreateRouterInfoDatabaseLookupMsg(m_Destination, i2p::context.GetIdentHash(), 0, m_IsExploratory, &m_ExcludedPeers); if(router) m_ExcludedPeers.insert (router->GetIdentHash ()); - m_CreationTime = i2p::util::GetSecondsSinceEpoch (); + m_LastRequestTime = i2p::util::GetSecondsSinceEpoch (); return msg; } @@ -37,7 +49,7 @@ namespace data auto msg = i2p::CreateRouterInfoDatabaseLookupMsg (m_Destination, i2p::context.GetRouterInfo ().GetIdentHash () , 0, false, &m_ExcludedPeers); m_ExcludedPeers.insert (floodfill); - m_CreationTime = i2p::util::GetSecondsSinceEpoch (); + m_LastRequestTime = i2p::util::GetSecondsSinceEpoch (); return msg; } @@ -74,15 +86,36 @@ namespace data } - std::shared_ptr NetDbRequests::CreateRequest (const IdentHash& destination, bool isExploratory, RequestedDestination::RequestComplete requestComplete) + std::shared_ptr NetDbRequests::CreateRequest (const IdentHash& destination, + bool isExploratory, bool direct, RequestedDestination::RequestComplete requestComplete) { // request RouterInfo directly - auto dest = std::make_shared (destination, isExploratory); + auto dest = std::make_shared (destination, isExploratory, direct); dest->SetRequestComplete (requestComplete); { - std::unique_lock l(m_RequestedDestinationsMutex); - if (!m_RequestedDestinations.insert (std::make_pair (destination, dest)).second) // not inserted + std::unique_lock l(m_RequestedDestinationsMutex); + auto ret = m_RequestedDestinations.emplace (destination, dest); + if (!ret.second) // not inserted + { + dest->SetRequestComplete (nullptr); // don't call requestComplete in destructor + if (requestComplete) + { + auto prev = ret.first->second->GetRequestComplete (); + if (prev) // if already set + ret.first->second->SetRequestComplete ( + [requestComplete, prev](std::shared_ptr r) + { + prev (r); // call previous + requestComplete (r); // then new + }); + else + ret.first->second->SetRequestComplete (requestComplete); + } + if (i2p::util::GetSecondsSinceEpoch () > ret.first->second->GetLastRequestTime () + MIN_REQUEST_TIME) + if (!SendNextRequest (ret.first->second)) // try next floodfill + m_RequestedDestinations.erase (ret.first); // delete request if failed return nullptr; + } } return dest; } @@ -125,35 +158,10 @@ namespace data { auto& dest = it->second; bool done = false; - if (ts < dest->GetCreationTime () + 60) // request is worthless after 1 minute + if (ts < dest->GetCreationTime () + MAX_REQUEST_TIME) // request becomes worthless { - if (ts > dest->GetCreationTime () + 5) // no response for 5 seconds - { - auto count = dest->GetExcludedPeers ().size (); - if (!dest->IsExploratory () && count < 7) - { - auto pool = i2p::tunnel::tunnels.GetExploratoryPool (); - auto outbound = pool->GetNextOutboundTunnel (); - auto inbound = pool->GetNextInboundTunnel (); - auto nextFloodfill = netdb.GetClosestFloodfill (dest->GetDestination (), dest->GetExcludedPeers ()); - if (nextFloodfill && outbound && inbound) - outbound->SendTunnelDataMsgTo (nextFloodfill->GetIdentHash (), 0, - dest->CreateRequestMessage (nextFloodfill, inbound)); - else - { - done = true; - if (!inbound) LogPrint (eLogWarning, "NetDbReq: No inbound tunnels"); - if (!outbound) LogPrint (eLogWarning, "NetDbReq: No outbound tunnels"); - if (!nextFloodfill) LogPrint (eLogWarning, "NetDbReq: No more floodfills"); - } - } - else - { - if (!dest->IsExploratory ()) - LogPrint (eLogWarning, "NetDbReq: ", dest->GetDestination ().ToBase64 (), " not found after 7 attempts"); - done = true; - } - } + if (ts > dest->GetLastRequestTime () + MIN_REQUEST_TIME) // try next floodfill if no response after min interval + done = !SendNextRequest (dest); } else // delete obsolete request done = true; @@ -164,5 +172,62 @@ namespace data ++it; } } + + bool NetDbRequests::SendNextRequest (std::shared_ptr dest) + { + if (!dest) return false; + bool ret = true; + auto count = dest->GetExcludedPeers ().size (); + if (!dest->IsExploratory () && count < MAX_NUM_REQUEST_ATTEMPTS) + { + auto nextFloodfill = netdb.GetClosestFloodfill (dest->GetDestination (), dest->GetExcludedPeers ()); + if (nextFloodfill) + { + bool direct = dest->IsDirect (); + if (direct && !nextFloodfill->IsReachableFrom (i2p::context.GetRouterInfo ()) && + !i2p::transport::transports.IsConnected (nextFloodfill->GetIdentHash ())) + direct = false; // floodfill can't be reached directly + if (direct) + { + LogPrint (eLogDebug, "NetDbReq: Try ", dest->GetDestination ().ToBase64 (), " at ", count, " floodfill ", nextFloodfill->GetIdentHash ().ToBase64 (), " directly"); + auto msg = dest->CreateRequestMessage (nextFloodfill->GetIdentHash ()); + msg->onDrop = [this, dest]() { this->SendNextRequest (dest); }; + i2p::transport::transports.SendMessage (nextFloodfill->GetIdentHash (), msg); + } + else + { + auto pool = i2p::tunnel::tunnels.GetExploratoryPool (); + auto outbound = pool->GetNextOutboundTunnel (); + auto inbound = pool->GetNextInboundTunnel (); + if (nextFloodfill && outbound && inbound) + { + LogPrint (eLogDebug, "NetDbReq: Try ", dest->GetDestination ().ToBase64 (), " at ", count, " floodfill ", nextFloodfill->GetIdentHash ().ToBase64 (), " through tunnels"); + auto msg = dest->CreateRequestMessage (nextFloodfill, inbound); + msg->onDrop = [this, dest]() { this->SendNextRequest (dest); }; + outbound->SendTunnelDataMsgTo (nextFloodfill->GetIdentHash (), 0, + i2p::garlic::WrapECIESX25519MessageForRouter (msg, nextFloodfill->GetIdentity ()->GetEncryptionPublicKey ())); + } + else + { + ret = false; + if (!inbound) LogPrint (eLogWarning, "NetDbReq: No inbound tunnels"); + if (!outbound) LogPrint (eLogWarning, "NetDbReq: No outbound tunnels"); + } + } + } + else + { + ret = false; + if (!nextFloodfill) LogPrint (eLogWarning, "NetDbReq: No more floodfills"); + } + } + else + { + if (!dest->IsExploratory ()) + LogPrint (eLogWarning, "NetDbReq: ", dest->GetDestination ().ToBase64 (), " not found after 7 attempts"); + ret = false; + } + return ret; + } } } diff --git a/libi2pd/NetDbRequests.h b/libi2pd/NetDbRequests.h index cf2f0915..25c102de 100644 --- a/libi2pd/NetDbRequests.h +++ b/libi2pd/NetDbRequests.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 * @@ -9,9 +9,10 @@ #ifndef NETDB_REQUESTS_H__ #define NETDB_REQUESTS_H__ +#include #include #include -#include +#include #include "Identity.h" #include "RouterInfo.h" @@ -19,27 +20,34 @@ namespace i2p { namespace data { + const size_t MAX_NUM_REQUEST_ATTEMPTS = 7; + const uint64_t MANAGE_REQUESTS_INTERVAL = 15; // in seconds + const uint64_t MIN_REQUEST_TIME = 5; // in seconds + const uint64_t MAX_REQUEST_TIME = MAX_NUM_REQUEST_ATTEMPTS*MANAGE_REQUESTS_INTERVAL; + class RequestedDestination { public: typedef std::function)> RequestComplete; - RequestedDestination (const IdentHash& destination, bool isExploratory = false): - m_Destination (destination), m_IsExploratory (isExploratory), m_CreationTime (0) {}; - ~RequestedDestination () { if (m_RequestComplete) m_RequestComplete (nullptr); }; + RequestedDestination (const IdentHash& destination, bool isExploratory = false, bool direct = true); + ~RequestedDestination (); const IdentHash& GetDestination () const { return m_Destination; }; int GetNumExcludedPeers () const { return m_ExcludedPeers.size (); }; const std::set& GetExcludedPeers () { return m_ExcludedPeers; }; void ClearExcludedPeers (); bool IsExploratory () const { return m_IsExploratory; }; + bool IsDirect () const { return m_IsDirect; }; bool IsExcluded (const IdentHash& ident) const { return m_ExcludedPeers.count (ident); }; uint64_t GetCreationTime () const { return m_CreationTime; }; + uint64_t GetLastRequestTime () const { return m_LastRequestTime; }; std::shared_ptr CreateRequestMessage (std::shared_ptr, std::shared_ptr replyTunnel); std::shared_ptr CreateRequestMessage (const IdentHash& floodfill); void SetRequestComplete (const RequestComplete& requestComplete) { m_RequestComplete = requestComplete; }; + RequestComplete GetRequestComplete () const { return m_RequestComplete; }; bool IsRequestComplete () const { return m_RequestComplete != nullptr; }; void Success (std::shared_ptr r); void Fail (); @@ -47,9 +55,9 @@ namespace data private: IdentHash m_Destination; - bool m_IsExploratory; + bool m_IsExploratory, m_IsDirect; std::set m_ExcludedPeers; - uint64_t m_CreationTime; + uint64_t m_CreationTime, m_LastRequestTime; // in seconds RequestComplete m_RequestComplete; }; @@ -60,15 +68,17 @@ namespace data void Start (); void Stop (); - std::shared_ptr CreateRequest (const IdentHash& destination, bool isExploratory, RequestedDestination::RequestComplete requestComplete = nullptr); + std::shared_ptr CreateRequest (const IdentHash& destination, bool isExploratory, + bool direct = false, RequestedDestination::RequestComplete requestComplete = nullptr); void RequestComplete (const IdentHash& ident, std::shared_ptr r); std::shared_ptr FindRequest (const IdentHash& ident) const; void ManageRequests (); - + bool SendNextRequest (std::shared_ptr dest); + private: mutable std::mutex m_RequestedDestinationsMutex; - std::map > m_RequestedDestinations; + std::unordered_map > m_RequestedDestinations; }; } } diff --git a/libi2pd/Profiling.cpp b/libi2pd/Profiling.cpp index 311d1c86..2031fa39 100644 --- a/libi2pd/Profiling.cpp +++ b/libi2pd/Profiling.cpp @@ -16,6 +16,7 @@ #include "FS.h" #include "Log.h" #include "Timestamp.h" +#include "NetDb.hpp" #include "Profiling.h" namespace i2p @@ -30,12 +31,12 @@ namespace data { return boost::posix_time::second_clock::local_time(); } - + RouterProfile::RouterProfile (): m_LastUpdateTime (GetTime ()), m_IsUpdated (false), m_LastDeclineTime (0), m_LastUnreachableTime (0), m_NumTunnelsAgreed (0), m_NumTunnelsDeclined (0), m_NumTunnelsNonReplied (0), - m_NumTimesTaken (0), m_NumTimesRejected (0) + m_NumTimesTaken (0), m_NumTimesRejected (0), m_HasConnected (false) { } @@ -55,6 +56,7 @@ namespace data boost::property_tree::ptree usage; usage.put (PEER_PROFILE_USAGE_TAKEN, m_NumTimesTaken); usage.put (PEER_PROFILE_USAGE_REJECTED, m_NumTimesRejected); + usage.put (PEER_PROFILE_USAGE_CONNECTED, m_HasConnected); // fill property tree boost::property_tree::ptree pt; pt.put (PEER_PROFILE_LAST_UPDATE_TIME, boost::posix_time::to_simple_string (m_LastUpdateTime)); @@ -123,6 +125,7 @@ namespace data auto usage = pt.get_child (PEER_PROFILE_SECTION_USAGE); m_NumTimesTaken = usage.get (PEER_PROFILE_USAGE_TAKEN, 0); m_NumTimesRejected = usage.get (PEER_PROFILE_USAGE_REJECTED, 0); + m_HasConnected = usage.get (PEER_PROFILE_USAGE_CONNECTED, false); } catch (boost::property_tree::ptree_bad_path& ex) { @@ -148,22 +151,30 @@ namespace data } else { - m_NumTunnelsAgreed++; + m_NumTunnelsAgreed++; m_LastDeclineTime = 0; } } void RouterProfile::TunnelNonReplied () { - m_NumTunnelsNonReplied++; + m_NumTunnelsNonReplied++; UpdateTime (); if (m_NumTunnelsNonReplied > 2*m_NumTunnelsAgreed && m_NumTunnelsNonReplied > 3) + { m_LastDeclineTime = i2p::util::GetSecondsSinceEpoch (); + } } - void RouterProfile::Unreachable () + void RouterProfile::Unreachable (bool unreachable) { - m_LastUnreachableTime = i2p::util::GetSecondsSinceEpoch (); + m_LastUnreachableTime = unreachable ? i2p::util::GetSecondsSinceEpoch () : 0; + UpdateTime (); + } + + void RouterProfile::Connected () + { + m_HasConnected = true; UpdateTime (); } @@ -214,6 +225,11 @@ namespace data return (bool)m_LastUnreachableTime; } + bool RouterProfile::IsUseful() const + { + return IsReal () || m_NumTunnelsNonReplied >= PEER_PROFILE_USEFUL_THRESHOLD; + } + std::shared_ptr GetRouterProfile (const IdentHash& identHash) { { @@ -221,8 +237,8 @@ namespace data auto it = g_Profiles.find (identHash); if (it != g_Profiles.end ()) return it->second; - } - auto profile = std::make_shared (); + } + auto profile = netdb.NewRouterProfile (); profile->Load (identHash); // if possible std::unique_lock l(g_ProfilesMutex); g_Profiles.emplace (identHash, profile); @@ -242,7 +258,7 @@ namespace data { std::unique_lock l(g_ProfilesMutex); for (auto it = g_Profiles.begin (); it != g_Profiles.end ();) - { + { if ((ts - it->second->GetLastUpdateTime ()).total_seconds () > PEER_PROFILE_PERSIST_INTERVAL) { if (it->second->IsUpdated ()) @@ -251,11 +267,11 @@ namespace data } else it++; - } + } } for (auto& it: tmp) if (it.second) it.second->Save (it.first); - } + } void SaveProfiles () { @@ -267,24 +283,24 @@ namespace data } auto ts = GetTime (); for (auto& it: tmp) - if (it.second->IsUpdated () && (ts - it.second->GetLastUpdateTime ()).total_seconds () < PEER_PROFILE_EXPIRATION_TIMEOUT*3600) + if (it.second->IsUseful() && (it.second->IsUpdated () || (ts - it.second->GetLastUpdateTime ()).total_seconds () < PEER_PROFILE_EXPIRATION_TIMEOUT*3600)) it.second->Save (it.first); - } - + } + void DeleteObsoleteProfiles () { { auto ts = GetTime (); std::unique_lock l(g_ProfilesMutex); for (auto it = g_Profiles.begin (); it != g_Profiles.end ();) - { + { if ((ts - it->second->GetLastUpdateTime ()).total_seconds () >= PEER_PROFILE_EXPIRATION_TIMEOUT*3600) it = g_Profiles.erase (it); else it++; } } - + struct stat st; std::time_t now = std::time(nullptr); diff --git a/libi2pd/Profiling.h b/libi2pd/Profiling.h index 752d6190..ed23fb12 100644 --- a/libi2pd/Profiling.h +++ b/libi2pd/Profiling.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2023, The PurpleI2P Project +* Copyright (c) 2013-2024, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -28,35 +28,40 @@ namespace data const char PEER_PROFILE_PARTICIPATION_NON_REPLIED[] = "nonreplied"; const char PEER_PROFILE_USAGE_TAKEN[] = "taken"; const char PEER_PROFILE_USAGE_REJECTED[] = "rejected"; - + const char PEER_PROFILE_USAGE_CONNECTED[] = "connected"; + const int PEER_PROFILE_EXPIRATION_TIMEOUT = 36; // in hours (1.5 days) - const int PEER_PROFILE_AUTOCLEAN_TIMEOUT = 6 * 3600; // in seconds (6 hours) + const int PEER_PROFILE_AUTOCLEAN_TIMEOUT = 3 * 3600; // in seconds (3 hours) const int PEER_PROFILE_AUTOCLEAN_VARIANCE = 3600; // in seconds (1 hour) const int PEER_PROFILE_DECLINED_RECENTLY_INTERVAL = 150; // in seconds (2.5 minutes) const int PEER_PROFILE_PERSIST_INTERVAL = 3300; // in seconds (55 minutes) - const int PEER_PROFILE_UNREACHABLE_INTERVAL = 2*3600; // on seconds (2 hours) + const int PEER_PROFILE_UNREACHABLE_INTERVAL = 480; // in seconds (8 minutes) + const int PEER_PROFILE_USEFUL_THRESHOLD = 3; class RouterProfile { public: RouterProfile (); - RouterProfile& operator= (const RouterProfile& ) = default; void Save (const IdentHash& identHash); void Load (const IdentHash& identHash); bool IsBad (); bool IsUnreachable (); + bool IsReal () const { return m_HasConnected || m_NumTunnelsAgreed > 0 || m_NumTunnelsDeclined > 0; } void TunnelBuildResponse (uint8_t ret); void TunnelNonReplied (); - void Unreachable (); + void Unreachable (bool unreachable); + void Connected (); boost::posix_time::ptime GetLastUpdateTime () const { return m_LastUpdateTime; }; bool IsUpdated () const { return m_IsUpdated; }; + bool IsUseful() const; + private: void UpdateTime (); @@ -78,6 +83,7 @@ namespace data // usage uint32_t m_NumTimesTaken; uint32_t m_NumTimesRejected; + bool m_HasConnected; // successful trusted(incoming or NTCP2) connection }; std::shared_ptr GetRouterProfile (const IdentHash& identHash); diff --git a/libi2pd/Reseed.cpp b/libi2pd/Reseed.cpp index 28e4db24..f8307a56 100644 --- a/libi2pd/Reseed.cpp +++ b/libi2pd/Reseed.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2023, The PurpleI2P Project +* Copyright (c) 2013-2024, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -26,6 +26,7 @@ #include "HTTP.h" #include "util.h" #include "Config.h" +#include "Socks5.h" namespace i2p { @@ -615,62 +616,21 @@ namespace data { // assume socks if not http, is checked before this for other types // TODO: support username/password auth etc - uint8_t hs_writebuf[3] = {0x05, 0x01, 0x00}; - uint8_t hs_readbuf[2]; - boost::asio::write(sock, boost::asio::buffer(hs_writebuf, 3), boost::asio::transfer_all(), ecode); - if(ecode) + bool success = false; + i2p::transport::Socks5Handshake (sock, std::make_pair(url.host, url.port), + [&success](const boost::system::error_code& ec) + { + if (!ec) + success = true; + else + LogPrint (eLogError, "Reseed: SOCKS handshake failed: ", ec.message()); + }); + service.run (); // execute all async operations + if (!success) { sock.close(); - LogPrint(eLogError, "Reseed: SOCKS handshake write failed: ", ecode.message()); return ""; - } - boost::asio::read(sock, boost::asio::buffer(hs_readbuf, 2), ecode); - if(ecode) - { - sock.close(); - LogPrint(eLogError, "Reseed: SOCKS handshake read failed: ", ecode.message()); - return ""; - } - size_t sz = 0; - uint8_t buf[256]; - - buf[0] = 0x05; - buf[1] = 0x01; - buf[2] = 0x00; - buf[3] = 0x03; - sz += 4; - size_t hostsz = url.host.size(); - if(1 + 2 + hostsz + sz > sizeof(buf)) - { - sock.close(); - LogPrint(eLogError, "Reseed: SOCKS handshake failed, hostname too big: ", url.host); - return ""; - } - buf[4] = (uint8_t) hostsz; - memcpy(buf+5, url.host.c_str(), hostsz); - sz += hostsz + 1; - htobe16buf(buf+sz, url.port); - sz += 2; - boost::asio::write(sock, boost::asio::buffer(buf, sz), boost::asio::transfer_all(), ecode); - if(ecode) - { - sock.close(); - LogPrint(eLogError, "Reseed: SOCKS handshake failed writing: ", ecode.message()); - return ""; - } - boost::asio::read(sock, boost::asio::buffer(buf, 10), ecode); - if(ecode) - { - sock.close(); - LogPrint(eLogError, "Reseed: SOCKS handshake failed reading: ", ecode.message()); - return ""; - } - if(buf[1] != 0x00) - { - sock.close(); - LogPrint(eLogError, "Reseed: SOCKS handshake bad reply code: ", std::to_string(buf[1])); - return ""; - } + } } } } @@ -687,18 +647,16 @@ namespace data while (it != end) { boost::asio::ip::tcp::endpoint ep = *it; - if ( - ( - !i2p::util::net::IsInReservedRange(ep.address ()) && ( - (ep.address ().is_v4 () && i2p::context.SupportsV4 ()) || - (ep.address ().is_v6 () && i2p::context.SupportsV6 ()) - ) - ) || - ( - i2p::util::net::IsYggdrasilAddress (ep.address ()) && - i2p::context.SupportsMesh () - ) - ) + bool supported = false; + if (!ep.address ().is_unspecified ()) + { + if (ep.address ().is_v4 ()) + supported = i2p::context.SupportsV4 (); + else if (ep.address ().is_v6 ()) + supported = i2p::util::net::IsYggdrasilAddress (ep.address ()) ? + i2p::context.SupportsMesh () : i2p::context.SupportsV6 (); + } + if (supported) { s.lowest_layer().connect (ep, ecode); if (!ecode) diff --git a/libi2pd/RouterContext.cpp b/libi2pd/RouterContext.cpp index d6c3c57b..2c1dc979 100644 --- a/libi2pd/RouterContext.cpp +++ b/libi2pd/RouterContext.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2023, The PurpleI2P Project +* Copyright (c) 2013-2024, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -31,7 +31,8 @@ namespace i2p RouterContext::RouterContext (): m_LastUpdateTime (0), m_AcceptsTunnels (true), m_IsFloodfill (false), m_ShareRatio (100), m_Status (eRouterStatusUnknown), m_StatusV6 (eRouterStatusUnknown), - m_Error (eRouterErrorNone), m_ErrorV6 (eRouterErrorNone), m_NetID (I2PD_NET_ID), + m_Error (eRouterErrorNone), m_ErrorV6 (eRouterErrorNone), + m_Testing (false), m_TestingV6 (false), m_NetID (I2PD_NET_ID), m_PublishReplyToken (0), m_IsHiddenMode (false) { } @@ -60,9 +61,11 @@ namespace i2p { m_PublishTimer.reset (new boost::asio::deadline_timer (m_Service->GetService ())); ScheduleInitialPublish (); - m_CongestionUpdateTimer.reset (new boost::asio::deadline_timer (m_Service->GetService ())); - ScheduleCongestionUpdate (); } + m_CongestionUpdateTimer.reset (new boost::asio::deadline_timer (m_Service->GetService ())); + ScheduleCongestionUpdate (); + m_CleanupTimer.reset (new boost::asio::deadline_timer (m_Service->GetService ())); + ScheduleCleanupTimer (); } } @@ -277,10 +280,33 @@ namespace i2p fk.write ((char *)m_SSU2Keys.get (), sizeof (SSU2PrivateKeys)); } + void RouterContext::SetTesting (bool testing) + { + if (testing != m_Testing) + { + m_Testing = testing; + if (m_Testing) + m_Error = eRouterErrorNone; + } + } + + void RouterContext::SetTestingV6 (bool testing) + { + if (testing != m_TestingV6) + { + m_TestingV6 = testing; + if (m_TestingV6) + m_ErrorV6 = eRouterErrorNone; + } + } + void RouterContext::SetStatus (RouterStatus status) { + SetTesting (false); if (status != m_Status) { + LogPrint(eLogInfo, "Router: network status v4 changed ", + ROUTER_STATUS_NAMES[m_Status], " -> ", ROUTER_STATUS_NAMES[status]); m_Status = status; switch (m_Status) { @@ -290,9 +316,6 @@ namespace i2p case eRouterStatusFirewalled: SetUnreachable (true, false); // ipv4 break; - case eRouterStatusTesting: - m_Error = eRouterErrorNone; - break; default: ; } @@ -301,8 +324,11 @@ namespace i2p void RouterContext::SetStatusV6 (RouterStatus status) { + SetTestingV6 (false); if (status != m_StatusV6) { + LogPrint(eLogInfo, "Router: network status v6 changed ", + ROUTER_STATUS_NAMES[m_StatusV6], " -> ", ROUTER_STATUS_NAMES[status]); m_StatusV6 = status; switch (m_StatusV6) { @@ -312,9 +338,6 @@ namespace i2p case eRouterStatusFirewalled: SetUnreachable (false, true); // ipv6 break; - case eRouterStatusTesting: - m_ErrorV6 = eRouterErrorNone; - break; default: ; } @@ -577,15 +600,15 @@ namespace i2p /* detect parameters */ switch (L) { - case i2p::data::CAPS_FLAG_LOW_BANDWIDTH1 : limit = 12; type = low; break; - case i2p::data::CAPS_FLAG_LOW_BANDWIDTH2 : limit = 48; type = low; break; - case i2p::data::CAPS_FLAG_HIGH_BANDWIDTH1 : limit = 64; type = high; break; - case i2p::data::CAPS_FLAG_HIGH_BANDWIDTH2 : limit = 128; type = high; break; - case i2p::data::CAPS_FLAG_HIGH_BANDWIDTH3 : limit = 256; type = high; break; - case i2p::data::CAPS_FLAG_EXTRA_BANDWIDTH1 : limit = 2048; type = extra; break; + case i2p::data::CAPS_FLAG_LOW_BANDWIDTH1 : limit = 12; type = low; break; + case i2p::data::CAPS_FLAG_LOW_BANDWIDTH2 : limit = i2p::data::LOW_BANDWIDTH_LIMIT; type = low; break; // 48 + case i2p::data::CAPS_FLAG_LOW_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_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: - limit = 48; type = low; + limit = i2p::data::LOW_BANDWIDTH_LIMIT; type = low; // 48 } /* update caps & flags in RI */ auto caps = m_RouterInfo.GetCaps (); @@ -1114,13 +1137,14 @@ namespace i2p return i2p::tunnel::tunnels.GetExploratoryPool (); } - bool RouterContext::IsHighCongestion () const + int RouterContext::GetCongestionLevel (bool longTerm) const { - return i2p::tunnel::tunnels.IsTooManyTransitTunnels () || - i2p::transport::transports.IsBandwidthExceeded () || - i2p::transport::transports.IsTransitBandwidthExceeded (); - } - + return std::max ( + i2p::tunnel::tunnels.GetCongestionLevel (), + i2p::transport::transports.GetCongestionLevel (longTerm) + ); + } + void RouterContext::HandleI2NPMessage (const uint8_t * buf, size_t len) { i2p::HandleI2NPMessage (CreateI2NPMessage (buf, GetI2NPMessageLength (buf, len))); @@ -1128,6 +1152,13 @@ namespace i2p bool RouterContext::HandleCloveI2NPMessage (I2NPMessageType typeID, const uint8_t * payload, size_t len, uint32_t msgID) { + if (typeID == eI2NPDeliveryStatus) + { + // try tunnel test + auto pool = GetTunnelPool (); + if (pool && pool->ProcessDeliveryStatus (bufbe32toh (payload + DELIVERY_STATUS_MSGID_OFFSET), bufbe64toh (payload + DELIVERY_STATUS_TIMESTAMP_OFFSET))) + return true; + } auto msg = CreateI2NPMessage (typeID, payload, len, msgID); if (!msg) return false; i2p::HandleI2NPMessage (msg); @@ -1182,17 +1213,26 @@ namespace i2p else i2p::garlic::GarlicDestination::ProcessDeliveryStatusMessage (msg); } - - void RouterContext::CleanupDestination () + + void RouterContext::SubmitECIESx25519Key (const uint8_t * key, uint64_t tag) { if (m_Service) - m_Service->GetService ().post ([this]() - { - this->i2p::garlic::GarlicDestination::CleanupExpiredTags (); - }); + { + struct + { + uint8_t k[32]; + uint64_t t; + } data; + memcpy (data.k, key, 32); + data.t = tag; + m_Service->GetService ().post ([this,data](void) + { + AddECIESx25519Key (data.k, data.t); + }); + } else LogPrint (eLogError, "Router: service is NULL"); - } + } uint32_t RouterContext::GetUptime () const { @@ -1337,10 +1377,19 @@ namespace i2p uint32_t replyToken; RAND_bytes ((uint8_t *)&replyToken, 4); LogPrint (eLogInfo, "Router: Publishing our RouterInfo to ", i2p::data::GetIdentHashAbbreviation(floodfill->GetIdentHash ()), ". reply token=", replyToken); + auto onDrop = [this]() + { + if (m_Service) + m_Service->GetService ().post ([this]() { HandlePublishResendTimer (boost::system::error_code ()); }); + }; if (floodfill->IsReachableFrom (i2p::context.GetRouterInfo ()) || // are we able to connect? i2p::transport::transports.IsConnected (floodfill->GetIdentHash ())) // already connected ? + { // send directly - i2p::transport::transports.SendMessage (floodfill->GetIdentHash (), CreateDatabaseStoreMsg (i2p::context.GetSharedRouterInfo (), replyToken)); + auto msg = CreateDatabaseStoreMsg (i2p::context.GetSharedRouterInfo (), replyToken); + msg->onDrop = onDrop; + i2p::transport::transports.SendMessage (floodfill->GetIdentHash (), msg); + } else { // otherwise through exploratory @@ -1348,8 +1397,13 @@ namespace i2p auto outbound = exploratoryPool ? exploratoryPool->GetNextOutboundTunnel (nullptr, floodfill->GetCompatibleTransports (false)) : nullptr; auto inbound = exploratoryPool ? exploratoryPool->GetNextInboundTunnel (nullptr, floodfill->GetCompatibleTransports (true)) : nullptr; if (inbound && outbound) - outbound->SendTunnelDataMsgTo (floodfill->GetIdentHash (), 0, - CreateDatabaseStoreMsg (i2p::context.GetSharedRouterInfo (), replyToken, inbound)); + { + // encrypt for floodfill + auto msg = CreateDatabaseStoreMsg (i2p::context.GetSharedRouterInfo (), replyToken, inbound); + msg->onDrop = onDrop; + outbound->SendTunnelDataMsgTo (floodfill->GetIdentHash (), 0, + i2p::garlic::WrapECIESX25519MessageForRouter (msg, floodfill->GetIdentity ()->GetEncryptionPublicKey ())); + } else LogPrint (eLogInfo, "Router: Can't publish our RouterInfo. No tunnles. Try again in ", ROUTER_INFO_CONFIRMATION_TIMEOUT, " seconds"); } @@ -1401,13 +1455,41 @@ namespace i2p if (ecode != boost::asio::error::operation_aborted) { auto c = i2p::data::RouterInfo::eLowCongestion; - if (!AcceptsTunnels ()) + if (!AcceptsTunnels () || !m_ShareRatio) c = i2p::data::RouterInfo::eRejectAll; - else if (IsHighCongestion ()) - c = i2p::data::RouterInfo::eHighCongestion; + else + { + int congestionLevel = GetCongestionLevel (true); + if (congestionLevel > CONGESTION_LEVEL_HIGH) + c = i2p::data::RouterInfo::eHighCongestion; + else if (congestionLevel > CONGESTION_LEVEL_MEDIUM) + c = i2p::data::RouterInfo::eMediumCongestion; + } if (m_RouterInfo.UpdateCongestion (c)) UpdateRouterInfo (); ScheduleCongestionUpdate (); } } + + void RouterContext::ScheduleCleanupTimer () + { + if (m_CleanupTimer) + { + m_CleanupTimer->cancel (); + m_CleanupTimer->expires_from_now (boost::posix_time::minutes(ROUTER_INFO_CLEANUP_INTERVAL)); + m_CleanupTimer->async_wait (std::bind (&RouterContext::HandleCleanupTimer, + this, std::placeholders::_1)); + } + else + LogPrint (eLogError, "Router: Cleanup timer is NULL"); + } + + void RouterContext::HandleCleanupTimer (const boost::system::error_code& ecode) + { + if (ecode != boost::asio::error::operation_aborted) + { + CleanupExpiredTags (); + ScheduleCleanupTimer (); + } + } } diff --git a/libi2pd/RouterContext.h b/libi2pd/RouterContext.h index b8183339..062c187d 100644 --- a/libi2pd/RouterContext.h +++ b/libi2pd/RouterContext.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2023, The PurpleI2P Project +* Copyright (c) 2013-2024, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -38,15 +38,24 @@ namespace garlic const int ROUTER_INFO_CONFIRMATION_TIMEOUT = 5; // in seconds const int ROUTER_INFO_MAX_PUBLISH_EXCLUDED_FLOODFILLS = 15; const int ROUTER_INFO_CONGESTION_UPDATE_INTERVAL = 12*60; // in seconds + const int ROUTER_INFO_CLEANUP_INTERVAL = 5; // in minutes enum RouterStatus { eRouterStatusOK = 0, - eRouterStatusTesting = 1, - eRouterStatusFirewalled = 2, - eRouterStatusUnknown = 3, - eRouterStatusProxy = 4, - eRouterStatusMesh = 5 + eRouterStatusFirewalled = 1, + eRouterStatusUnknown = 2, + eRouterStatusProxy = 3, + eRouterStatusMesh = 4 + }; + + const char* const ROUTER_STATUS_NAMES[] = + { + "OK", // 0 + "Firewalled", // 1 + "Unknown", // 2 + "Proxy", // 3 + "Mesh" // 4 }; enum RouterError @@ -121,10 +130,14 @@ namespace garlic uint64_t GetLastUpdateTime () const { return m_LastUpdateTime; }; uint64_t GetBandwidthLimit () const { return m_BandwidthLimit; }; uint64_t GetTransitBandwidthLimit () const { return (m_BandwidthLimit*m_ShareRatio)/100LL; }; + bool GetTesting () const { return m_Testing; }; + void SetTesting (bool testing); RouterStatus GetStatus () const { return m_Status; }; void SetStatus (RouterStatus status); RouterError GetError () const { return m_Error; }; void SetError (RouterError error) { m_Error = error; }; + bool GetTestingV6 () const { return m_TestingV6; }; + void SetTestingV6 (bool testing); RouterStatus GetStatusV6 () const { return m_StatusV6; }; void SetStatusV6 (RouterStatus status); RouterError GetErrorV6 () const { return m_ErrorV6; }; @@ -133,6 +146,7 @@ namespace garlic void SetNetID (int netID) { m_NetID = netID; }; bool DecryptTunnelBuildRecord (const uint8_t * encrypted, uint8_t * data); bool DecryptTunnelShortRequestRecord (const uint8_t * encrypted, uint8_t * data); + void SubmitECIESx25519Key (const uint8_t * key, uint64_t tag); void UpdatePort (int port); // called from Daemon void UpdateAddress (const boost::asio::ip::address& host); // called from SSU2 or Daemon @@ -153,7 +167,7 @@ namespace garlic void SetShareRatio (int percents); // 0 - 100 bool AcceptsTunnels () const { return m_AcceptsTunnels; }; void SetAcceptsTunnels (bool acceptsTunnels) { m_AcceptsTunnels = acceptsTunnels; }; - bool IsHighCongestion () const; + int GetCongestionLevel (bool longTerm) const; bool SupportsV6 () const { return m_RouterInfo.IsV6 (); }; bool SupportsV4 () const { return m_RouterInfo.IsV4 (); }; bool SupportsMesh () const { return m_RouterInfo.IsMesh (); }; @@ -168,7 +182,6 @@ namespace garlic void UpdateNTCP2V6Address (const boost::asio::ip::address& host); // called from Daemon. TODO: remove void UpdateStats (); void UpdateTimestamp (uint64_t ts); // in seconds, called from NetDb before publishing - void CleanupDestination (); // garlic destination // implements LocalDestination std::shared_ptr GetIdentity () const { return m_Keys.GetPublic (); }; @@ -217,6 +230,8 @@ namespace garlic void HandlePublishResendTimer (const boost::system::error_code& ecode); void ScheduleCongestionUpdate (); void HandleCongestionUpdateTimer (const boost::system::error_code& ecode); + void ScheduleCleanupTimer (); + void HandleCleanupTimer (const boost::system::error_code& ecode); private: @@ -231,6 +246,7 @@ namespace garlic int m_ShareRatio; RouterStatus m_Status, m_StatusV6; RouterError m_Error, m_ErrorV6; + bool m_Testing, m_TestingV6; int m_NetID; std::unique_ptr m_NTCP2Keys; std::unique_ptr m_SSU2Keys; @@ -239,7 +255,7 @@ namespace garlic i2p::crypto::NoiseSymmetricState m_InitialNoiseState, m_CurrentNoiseState; // publish std::unique_ptr m_Service; - std::unique_ptr m_PublishTimer, m_CongestionUpdateTimer; + std::unique_ptr m_PublishTimer, m_CongestionUpdateTimer, m_CleanupTimer; std::set m_PublishExcluded; uint32_t m_PublishReplyToken; bool m_IsHiddenMode; // not publish diff --git a/libi2pd/RouterInfo.cpp b/libi2pd/RouterInfo.cpp index 3ae5abd9..a9c43b2e 100644 --- a/libi2pd/RouterInfo.cpp +++ b/libi2pd/RouterInfo.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2023, The PurpleI2P Project +* Copyright (c) 2013-2024, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -21,6 +21,7 @@ #include "Base.h" #include "Timestamp.h" #include "Log.h" +#include "Transports.h" #include "NetDb.hpp" #include "RouterContext.h" #include "RouterInfo.h" @@ -130,7 +131,7 @@ namespace data m_BufferLen = s.tellg (); if (m_BufferLen < 40 || m_BufferLen > MAX_RI_BUFFER_SIZE) { - LogPrint(eLogError, "RouterInfo: File", fullPath, " is malformed"); + LogPrint(eLogError, "RouterInfo: File ", fullPath, " is malformed"); return false; } s.seekg(0, std::ios::beg); @@ -253,7 +254,7 @@ namespace data address->host = boost::asio::ip::address::from_string (value, ecode); if (!ecode && !address->host.is_unspecified ()) { - if (!i2p::util::net::IsInReservedRange (address->host) || + if (!i2p::transport::transports.IsInReservedRange (address->host) || i2p::util::net::IsYggdrasilAddress (address->host)) isHost = true; else @@ -293,7 +294,8 @@ namespace data else if (!strcmp (key, "s")) // ntcp2 or ssu2 static key { Base64ToByteStream (value, strlen (value), address->s, 32); - isStaticKey = true; + if (!(address->s[31] & 0x80)) // check if x25519 public key + isStaticKey = true; } else if (!strcmp (key, "i")) // ntcp2 iv or ssu2 intro { @@ -362,11 +364,12 @@ namespace data } if (!s) return; } + if (address->transportStyle == eTransportNTCP2) { if (isStaticKey) { - if (isHost) + if (isHost && address->port) { if (address->host.is_v6 ()) supportedTransports |= (i2p::util::net::IsYggdrasilAddress (address->host) ? eNTCP2V6Mesh : eNTCP2V6); @@ -374,8 +377,9 @@ namespace data supportedTransports |= eNTCP2V4; m_ReachableTransports |= supportedTransports; } - else if (!address->published) + else { + address->published = false; if (address->caps) { if (address->caps & AddressCaps::eV4) supportedTransports |= eNTCP2V4; @@ -386,7 +390,7 @@ namespace data } } } - else if (address->transportStyle == eTransportSSU2 && isV2) + else if (address->transportStyle == eTransportSSU2 && isV2 && isStaticKey) { if (address->IsV4 ()) supportedTransports |= eSSU2V4; if (address->IsV6 ()) supportedTransports |= eSSU2V6; @@ -400,18 +404,9 @@ namespace data { // exclude invalid introducers uint32_t ts = i2p::util::GetSecondsSinceEpoch (); - int numValid = 0; - for (auto& it: address->ssu->introducers) - { - if (it.iTag && ts < it.iExp) - numValid++; - else - it.iTag = 0; - } - if (numValid) + UpdateIntroducers (address, ts); + if (!address->ssu->introducers.empty ()) // still has something m_ReachableTransports |= supportedTransports; - else - address->ssu->introducers.resize (0); } } if (supportedTransports) @@ -491,7 +486,10 @@ namespace data if (netdb.GetFamilies ().VerifyFamily (family, GetIdentHash (), value)) m_FamilyID = netdb.GetFamilies ().GetFamilyID (family); else + { LogPrint (eLogWarning, "RouterInfo: Family ", family, " signature verification failed"); + SetUnreachable (true); + } } if (!s) return; @@ -518,7 +516,6 @@ namespace data break; case CAPS_FLAG_HIGH_BANDWIDTH1: case CAPS_FLAG_HIGH_BANDWIDTH2: - case CAPS_FLAG_HIGH_BANDWIDTH3: m_Caps |= Caps::eHighBandwidth; break; case CAPS_FLAG_EXTRA_BANDWIDTH1: @@ -576,6 +573,21 @@ namespace data return caps; } + void RouterInfo::UpdateIntroducers (std::shared_ptr

address, uint64_t ts) + { + if (!address || !address->ssu) return; + int numValid = 0; + for (auto& it: address->ssu->introducers) + { + if (it.iTag && ts < it.iExp && !it.iH.IsZero ()) + numValid++; + else + it.iTag = 0; + } + if (!numValid) + address->ssu->introducers.resize (0); + } + bool RouterInfo::IsNewer (const uint8_t * buf, size_t len) const { if (!m_RouterIdentity) return false; @@ -976,11 +988,23 @@ namespace data bool RouterInfo::IsEligibleFloodfill () const { - // floodfill must be reachable by ipv4, >= 0.9.38 and not DSA - return IsReachableBy (eNTCP2V4 | eSSU2V4) && m_Version >= NETDB_MIN_FLOODFILL_VERSION && + // floodfill must have published ipv4, >= 0.9.38 and not DSA + return m_Version >= NETDB_MIN_FLOODFILL_VERSION && IsPublished (true) && GetIdentity ()->GetSigningKeyType () != SIGNING_KEY_TYPE_DSA_SHA1; } + bool RouterInfo::IsPublished (bool v4) const + { + if (m_Caps & (eUnreachable | eHidden)) return false; // if router sets U or H we assume that all addresses are not published + auto addr = GetAddresses (); + if (v4) + return ((*addr)[eNTCP2V4Idx] && ((*addr)[eNTCP2V4Idx])->published) || + ((*addr)[eSSU2V4Idx] && ((*addr)[eSSU2V4Idx])->published); + else + return ((*addr)[eNTCP2V6Idx] && ((*addr)[eNTCP2V6Idx])->published) || + ((*addr)[eSSU2V6Idx] && ((*addr)[eSSU2V6Idx])->published); + } + bool RouterInfo::IsSSU2PeerTesting (bool v4) const { if (!(m_SupportedTransports & (v4 ? eSSU2V4 : eSSU2V6))) return false; @@ -1036,6 +1060,31 @@ namespace data } } + void RouterInfo::UpdateIntroducers (uint64_t ts) + { + if (ts*1000 < m_Timestamp + INTRODUCER_UPDATE_INTERVAL) return; + if (m_ReachableTransports & eSSU2V4) + { + auto addr = (*GetAddresses ())[eSSU2V4Idx]; + if (addr && addr->UsesIntroducer ()) + { + UpdateIntroducers (addr, ts); + if (!addr->UsesIntroducer ()) // no more valid introducers + m_ReachableTransports &= ~eSSU2V4; + } + } + if (m_ReachableTransports & eSSU2V6) + { + auto addr = (*GetAddresses ())[eSSU2V6Idx]; + if (addr && addr->UsesIntroducer ()) + { + UpdateIntroducers (addr, ts); + if (!addr->UsesIntroducer ()) // no more valid introducers + m_ReachableTransports &= ~eSSU2V6; + } + } + } + void RouterInfo::UpdateBuffer (const uint8_t * buf, size_t len) { if (!m_Buffer) @@ -1070,13 +1119,25 @@ namespace data m_Timestamp = i2p::util::GetMillisecondsSinceEpoch (); } - bool RouterInfo::IsHighCongestion () const + bool RouterInfo::IsHighCongestion (bool highBandwidth) const { - if (m_Congestion == eLowCongestion || m_Congestion == eMediumCongestion) return false; - if (m_Congestion == eRejectAll) return true; - if (m_Congestion == eHighCongestion) - return (i2p::util::GetMillisecondsSinceEpoch () < m_Timestamp + HIGH_CONGESTION_INTERVAL*1000LL) ? true : false; - return false; + switch (m_Congestion) + { + case eLowCongestion: + return false; + break; + case eMediumCongestion: + return highBandwidth; + break; + case eHighCongestion: + return i2p::util::GetMillisecondsSinceEpoch () < m_Timestamp + HIGH_CONGESTION_INTERVAL*1000LL; + break; + case eRejectAll: + return true; + break; + default: + return false; + } } void LocalRouterInfo::CreateBuffer (const PrivateKeys& privateKeys) @@ -1116,7 +1177,7 @@ namespace data CAPS_FLAG_EXTRA_BANDWIDTH2 : // 'X' CAPS_FLAG_EXTRA_BANDWIDTH1; // 'P' else - caps += CAPS_FLAG_HIGH_BANDWIDTH3; // 'O' + caps += CAPS_FLAG_HIGH_BANDWIDTH2; // 'O' caps += CAPS_FLAG_FLOODFILL; // floodfill } else @@ -1124,7 +1185,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_BANDWIDTH3 /* 'O' */: CAPS_FLAG_LOW_BANDWIDTH2 /* 'L' */; // bandwidth + caps += (c & eHighBandwidth) ? CAPS_FLAG_HIGH_BANDWIDTH2 /* 'O' */: CAPS_FLAG_LOW_BANDWIDTH2 /* 'L' */; // bandwidth } if (c & eHidden) caps += CAPS_FLAG_HIDDEN; // hidden if (c & eReachable) caps += CAPS_FLAG_REACHABLE; // reachable @@ -1260,6 +1321,7 @@ namespace data int i = 0; for (const auto& introducer: address.ssu->introducers) { + if (!introducer.iTag) continue; if (introducer.iExp) // expiration is specified { WriteString ("iexp" + boost::lexical_cast(i), properties); @@ -1272,6 +1334,7 @@ namespace data i = 0; for (const auto& introducer: address.ssu->introducers) { + if (!introducer.iTag) continue; WriteString ("ih" + boost::lexical_cast(i), properties); properties << '='; char value[64]; @@ -1284,6 +1347,7 @@ namespace data i = 0; for (const auto& introducer: address.ssu->introducers) { + if (!introducer.iTag) continue; WriteString ("itag" + boost::lexical_cast(i), properties); properties << '='; WriteString (boost::lexical_cast(introducer.iTag), properties); diff --git a/libi2pd/RouterInfo.h b/libi2pd/RouterInfo.h index f258b018..9e074b9a 100644 --- a/libi2pd/RouterInfo.h +++ b/libi2pd/RouterInfo.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2023, The PurpleI2P Project +* Copyright (c) 2013-2024, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -39,11 +39,15 @@ namespace data /* bandwidth flags */ const char CAPS_FLAG_LOW_BANDWIDTH1 = 'K'; /* < 12 KBps */ const char CAPS_FLAG_LOW_BANDWIDTH2 = 'L'; /* 12-48 KBps */ - const char CAPS_FLAG_HIGH_BANDWIDTH1 = 'M'; /* 48-64 KBps */ - const char CAPS_FLAG_HIGH_BANDWIDTH2 = 'N'; /* 64-128 KBps */ - const char CAPS_FLAG_HIGH_BANDWIDTH3 = 'O'; /* 128-256 KBps */ - const char CAPS_FLAG_EXTRA_BANDWIDTH1 = 'P'; /* 256-2000 KBps */ - const char CAPS_FLAG_EXTRA_BANDWIDTH2 = 'X'; /* > 2000 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_EXTRA_BANDWIDTH1 = 'P'; /* 256-2048 KBps */ + const char CAPS_FLAG_EXTRA_BANDWIDTH2 = 'X'; /* > 2048 KBps */ + // bandwidth limits in kBps + const uint32_t LOW_BANDWIDTH_LIMIT = 48; + const uint32_t HIGH_BANDWIDTH_LIMIT = 256; + const uint32_t EXTRA_BANDWIDTH_LIMIT = 2048; // congesion flags const char CAPS_FLAG_MEDIUM_CONGESTION = 'D'; const char CAPS_FLAG_HIGH_CONGESTION = 'E'; @@ -61,6 +65,7 @@ namespace data const size_t MAX_RI_BUFFER_SIZE = 3072; // if RouterInfo exceeds 3K we consider it as malformed, might extend later const int HIGH_CONGESTION_INTERVAL = 15*60; // in seconds, 15 minutes + const int INTRODUCER_UPDATE_INTERVAL = 20*60*1000; // in milliseconds, 20 minutes class RouterInfo: public RoutingDestination { @@ -124,7 +129,7 @@ namespace data struct Introducer { - Introducer (): iTag (0), iExp (0) {}; + Introducer (): iTag (0), iExp (0) { iH.Fill(0); }; IdentHash iH; uint32_t iTag; uint32_t iExp; @@ -221,8 +226,9 @@ namespace data void RemoveSSU2Address (bool v4); void SetUnreachableAddressesTransportCaps (uint8_t transports); // bitmask of AddressCaps void UpdateSupportedTransports (); + void UpdateIntroducers (uint64_t ts); // ts in seconds bool IsFloodfill () const { return m_Caps & Caps::eFloodfill; }; - bool IsReachable () const { return m_Caps & Caps::eReachable; }; + void ResetFlooldFill () { m_Caps &= ~Caps::eFloodfill; }; bool IsECIES () const { return m_RouterIdentity->GetCryptoKeyType () == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD; }; bool IsNTCP2 (bool v4only = true) const; bool IsNTCP2V6 () const { return m_SupportedTransports & eNTCP2V6; }; @@ -246,9 +252,10 @@ namespace data bool IsHighBandwidth () const { return m_Caps & RouterInfo::eHighBandwidth; }; bool IsExtraBandwidth () const { return m_Caps & RouterInfo::eExtraBandwidth; }; bool IsEligibleFloodfill () const; + bool IsPublished (bool v4) const; bool IsSSU2PeerTesting (bool v4) const; bool IsSSU2Introducer (bool v4) const; - bool IsHighCongestion () const; + bool IsHighCongestion (bool highBandwidth) const; uint8_t GetCaps () const { return m_Caps; }; void SetCaps (uint8_t caps) { m_Caps = caps; }; @@ -257,6 +264,7 @@ namespace data void SetUnreachable (bool unreachable) { m_IsUnreachable = unreachable; }; bool IsUnreachable () const { return m_IsUnreachable; }; + void ExcludeReachableTransports (CompatibleTransports transports) { m_ReachableTransports &= ~transports; }; const uint8_t * GetBuffer () const { return m_Buffer ? m_Buffer->data () : nullptr; }; const uint8_t * LoadBuffer (const std::string& fullPath); // load if necessary @@ -302,6 +310,7 @@ namespace data size_t ReadString (char* str, size_t len, std::istream& s) const; void ExtractCaps (const char * value); uint8_t ExtractAddressCaps (const char * value) const; + void UpdateIntroducers (std::shared_ptr
address, uint64_t ts); template std::shared_ptr GetAddress (Filter filter) const; virtual std::shared_ptr NewBuffer () const; @@ -315,7 +324,7 @@ namespace data std::shared_ptr m_RouterIdentity; std::shared_ptr m_Buffer; size_t m_BufferLen; - uint64_t m_Timestamp; + uint64_t m_Timestamp; // in milliseconds boost::shared_ptr m_Addresses; // TODO: use std::shared_ptr and std::atomic_store for gcc >= 4.9 bool m_IsUpdated, m_IsUnreachable; CompatibleTransports m_SupportedTransports, m_ReachableTransports; diff --git a/libi2pd/SSU2.cpp b/libi2pd/SSU2.cpp index 5df4d33a..3e0583ea 100644 --- a/libi2pd/SSU2.cpp +++ b/libi2pd/SSU2.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2022-2023, The PurpleI2P Project +* Copyright (c) 2022-2024, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -24,7 +24,8 @@ namespace transport m_AddressV4 (boost::asio::ip::address_v4()), m_AddressV6 (boost::asio::ip::address_v6()), m_TerminationTimer (GetService ()), m_CleanupTimer (GetService ()), m_ResendTimer (GetService ()), m_IntroducersUpdateTimer (GetService ()), m_IntroducersUpdateTimerV6 (GetService ()), - m_IsPublished (true), m_IsSyncClockFromPeers (true), m_IsThroughProxy (false) + m_IsPublished (true), m_IsSyncClockFromPeers (true), m_PendingTimeOffset (0), + m_IsThroughProxy (false) { } @@ -78,6 +79,7 @@ namespace transport if (address->IsV4 ()) { found = true; + LogPrint (eLogDebug, "SSU2: Opening IPv4 socket at Start"); OpenSocket (boost::asio::ip::udp::endpoint (m_AddressV4, port)); m_ReceiveService.GetService ().post( [this]() @@ -89,6 +91,7 @@ namespace transport if (address->IsV6 ()) { found = true; + LogPrint (eLogDebug, "SSU2: Opening IPv6 socket at Start"); OpenSocket (boost::asio::ip::udp::endpoint (m_AddressV6, port)); m_ReceiveService.GetService ().post( [this]() @@ -207,6 +210,42 @@ namespace transport return ep.port (); } + void SSU2Server::AdjustTimeOffset (int64_t offset, std::shared_ptr from) + { + if (offset) + { + if (m_PendingTimeOffset) // one more + { + if (m_PendingTimeOffsetFrom && from && + m_PendingTimeOffsetFrom->GetIdentHash ().GetLL()[0] != from->GetIdentHash ().GetLL()[0]) // from different routers + { + if (std::abs (m_PendingTimeOffset - offset) < SSU2_CLOCK_SKEW) + { + offset = (m_PendingTimeOffset + offset)/2; // average + LogPrint (eLogWarning, "SSU2: Clock adjusted by ", offset, " seconds"); + i2p::util::AdjustTimeOffset (offset); + } + else + LogPrint (eLogWarning, "SSU2: Time offsets are too different. Clock not adjusted"); + m_PendingTimeOffset = 0; + m_PendingTimeOffsetFrom = nullptr; + } + else + LogPrint (eLogWarning, "SSU2: Time offsets from same router. Clock not adjusted"); + } + else + { + m_PendingTimeOffset = offset; // first + m_PendingTimeOffsetFrom = from; + } + } + else + { + m_PendingTimeOffset = 0; // reset + m_PendingTimeOffsetFrom = nullptr; + } + } + boost::asio::ip::udp::socket& SSU2Server::OpenSocket (const boost::asio::ip::udp::endpoint& localEndpoint) { boost::asio::ip::udp::socket& socket = localEndpoint.address ().is_v6 () ? m_SocketV6 : m_SocketV4; @@ -219,13 +258,22 @@ namespace transport socket.set_option (boost::asio::ip::v6_only (true)); socket.set_option (boost::asio::socket_base::receive_buffer_size (SSU2_SOCKET_RECEIVE_BUFFER_SIZE)); socket.set_option (boost::asio::socket_base::send_buffer_size (SSU2_SOCKET_SEND_BUFFER_SIZE)); + } + catch (std::exception& ex ) + { + LogPrint (eLogCritical, "SSU2: Failed to open socket on ", localEndpoint.address (), ": ", ex.what()); + ThrowFatal ("Unable to start SSU2 transport on ", localEndpoint.address (), ": ", ex.what ()); + return socket; + } + try + { socket.bind (localEndpoint); LogPrint (eLogInfo, "SSU2: Start listening on ", localEndpoint); } catch (std::exception& ex ) { - LogPrint (eLogCritical, "SSU2: Failed to bind to ", localEndpoint, ": ", ex.what()); - ThrowFatal ("Unable to start SSU2 transport on ", localEndpoint, ": ", ex.what ()); + LogPrint (eLogWarning, "SSU2: Failed to bind to ", localEndpoint, ": ", ex.what(), ". Actual endpoint is ", socket.local_endpoint ()); + // we can continue without binding being firewalled } return socket; } @@ -243,10 +291,12 @@ namespace transport if (!ecode || ecode == boost::asio::error::connection_refused || ecode == boost::asio::error::connection_reset + || ecode == boost::asio::error::network_reset || ecode == boost::asio::error::network_unreachable || ecode == boost::asio::error::host_unreachable #ifdef _WIN32 // windows can throw WinAPI error, which is not handled by ASIO || ecode.value() == boost::winapi::ERROR_CONNECTION_REFUSED_ + || ecode.value() == boost::winapi::WSAENETRESET_ // 10052 || ecode.value() == boost::winapi::ERROR_NETWORK_UNREACHABLE_ || ecode.value() == boost::winapi::ERROR_HOST_UNREACHABLE_ #endif @@ -255,8 +305,15 @@ namespace transport // but better to find out which host were sent it and mark that router as unreachable { i2p::transport::transports.UpdateReceivedBytes (bytes_transferred); + if (bytes_transferred < SSU2_MIN_RECEIVED_PACKET_SIZE) + { + // drop too short packets + m_PacketsPool.ReleaseMt (packet); + Receive (socket); + return; + } packet->len = bytes_transferred; - + boost::system::error_code ec; size_t moreBytes = socket.available (ec); if (!ec && moreBytes) @@ -303,7 +360,7 @@ namespace transport else { auto ep = socket.local_endpoint (); - socket.close (); + LogPrint (eLogCritical, "SSU2: Reopening socket in HandleReceivedFrom: code ", ecode.value(), ": ", ecode.message ()); OpenSocket (ep); Receive (socket); } @@ -538,7 +595,7 @@ namespace transport else it1->second->ProcessRetry (buf, len); } - else if (!i2p::util::net::IsInReservedRange(senderEndpoint.address ()) && senderEndpoint.port ()) + else if (!i2p::transport::transports.IsInReservedRange(senderEndpoint.address ()) && senderEndpoint.port ()) { // assume new incoming session auto session = std::make_shared (*this); @@ -558,16 +615,25 @@ namespace transport SendThroughProxy (header, headerLen, nullptr, 0, payload, payloadLen, to); return; } + std::vector bufs { boost::asio::buffer (header, headerLen), boost::asio::buffer (payload, payloadLen) }; + boost::system::error_code ec; if (to.address ().is_v6 ()) + { + if (!m_SocketV6.is_open ()) return; m_SocketV6.send_to (bufs, to, 0, ec); + } else + { + if (!m_SocketV4.is_open ()) return; m_SocketV4.send_to (bufs, to, 0, ec); + } + if (!ec) i2p::transport::transports.UpdateSentBytes (headerLen + payloadLen); else @@ -582,17 +648,25 @@ namespace transport SendThroughProxy (header, headerLen, headerX, headerXLen, payload, payloadLen, to); return; } + std::vector bufs { boost::asio::buffer (header, headerLen), boost::asio::buffer (headerX, headerXLen), boost::asio::buffer (payload, payloadLen) }; + boost::system::error_code ec; if (to.address ().is_v6 ()) + { + if (!m_SocketV6.is_open ()) return; m_SocketV6.send_to (bufs, to, 0, ec); + } else + { + if (!m_SocketV4.is_open ()) return; m_SocketV4.send_to (bufs, to, 0, ec); + } if (!ec) i2p::transport::transports.UpdateSentBytes (headerLen + headerXLen + payloadLen); @@ -621,7 +695,7 @@ namespace transport bool isValidEndpoint = !address->host.is_unspecified () && address->port; if (isValidEndpoint) { - if (i2p::util::net::IsInReservedRange(address->host)) return false; + if (i2p::transport::transports.IsInReservedRange(address->host)) return false; auto s = FindPendingOutgoingSession (boost::asio::ip::udp::endpoint (address->host, address->port)); if (s) { @@ -708,7 +782,7 @@ namespace transport if (addr) { bool isValidEndpoint = !addr->host.is_unspecified () && addr->port && - !i2p::util::net::IsInReservedRange(addr->host); + !i2p::transport::transports.IsInReservedRange(addr->host); if (isValidEndpoint) { auto s = FindPendingOutgoingSession (boost::asio::ip::udp::endpoint (addr->host, addr->port)); @@ -962,7 +1036,7 @@ namespace transport void SSU2Server::UpdateIntroducers (bool v4) { uint32_t ts = i2p::util::GetSecondsSinceEpoch (); - std::list newList; + std::list newList, impliedList; auto& introducers = v4 ? m_Introducers : m_IntroducersV6; std::set excluded; for (const auto& it : introducers) @@ -974,12 +1048,19 @@ namespace transport session = it1->second; excluded.insert (it); } - if (session && session->IsEstablished ()) + if (session && session->IsEstablished () && session->GetRelayTag () && session->IsOutgoing ()) // still session with introducer? { if (ts < session->GetCreationTime () + SSU2_TO_INTRODUCER_SESSION_EXPIRATION) + { session->SendKeepAlive (); - if (ts < session->GetCreationTime () + SSU2_TO_INTRODUCER_SESSION_DURATION) - newList.push_back (it); + if (ts < session->GetCreationTime () + SSU2_TO_INTRODUCER_SESSION_DURATION) + newList.push_back (it); + else + { + impliedList.push_back (it); // keep in introducers list, but not publish + session = nullptr; + } + } else session = nullptr; } @@ -993,20 +1074,18 @@ namespace transport { // 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) { auto it1 = m_SessionsByRouterHash.find (it); if (it1 != m_SessionsByRouterHash.end ()) { auto session = it1->second; - if (session->IsEstablished ()) + 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 ()) - { - newList.push_back (it); sessions.push_back (session); - } } } } @@ -1014,10 +1093,14 @@ namespace transport for (const auto& it : sessions) { + 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 i2p::data::RouterInfo::Introducer introducer; - introducer.iTag = it->GetRelayTag (); + introducer.iTag = tag; introducer.iH = it->GetRemoteIdentity ()->GetIdentHash (); - introducer.iExp = it->GetCreationTime () + SSU2_TO_INTRODUCER_SESSION_EXPIRATION; + introducer.iExp = exp; excluded.insert (it->GetRemoteIdentity ()->GetIdentHash ()); if (i2p::context.AddSSU2Introducer (introducer, v4)) { @@ -1051,13 +1134,15 @@ namespace transport } } } + introducers.splice (introducers.end (), impliedList); // insert non-published, but non-expired introducers back } void SSU2Server::ScheduleIntroducersUpdateTimer () { if (m_IsPublished) { - m_IntroducersUpdateTimer.expires_from_now (boost::posix_time::seconds(SSU2_KEEP_ALIVE_INTERVAL)); + m_IntroducersUpdateTimer.expires_from_now (boost::posix_time::seconds( + SSU2_KEEP_ALIVE_INTERVAL + rand () % SSU2_KEEP_ALIVE_INTERVAL_VARIANCE)); m_IntroducersUpdateTimer.async_wait (std::bind (&SSU2Server::HandleIntroducersUpdateTimer, this, std::placeholders::_1, true)); } @@ -1070,7 +1155,8 @@ namespace transport m_IntroducersUpdateTimer.cancel (); i2p::context.ClearSSU2Introducers (true); m_Introducers.clear (); - m_IntroducersUpdateTimer.expires_from_now (boost::posix_time::seconds(SSU2_KEEP_ALIVE_INTERVAL/2)); + m_IntroducersUpdateTimer.expires_from_now (boost::posix_time::seconds( + (SSU2_KEEP_ALIVE_INTERVAL + rand () % SSU2_KEEP_ALIVE_INTERVAL_VARIANCE)/2)); m_IntroducersUpdateTimer.async_wait (std::bind (&SSU2Server::HandleIntroducersUpdateTimer, this, std::placeholders::_1, true)); } @@ -1080,7 +1166,8 @@ namespace transport { if (m_IsPublished) { - m_IntroducersUpdateTimerV6.expires_from_now (boost::posix_time::seconds(SSU2_KEEP_ALIVE_INTERVAL)); + m_IntroducersUpdateTimerV6.expires_from_now (boost::posix_time::seconds( + SSU2_KEEP_ALIVE_INTERVAL + rand () % SSU2_KEEP_ALIVE_INTERVAL_VARIANCE)); m_IntroducersUpdateTimerV6.async_wait (std::bind (&SSU2Server::HandleIntroducersUpdateTimer, this, std::placeholders::_1, false)); } @@ -1093,7 +1180,8 @@ namespace transport m_IntroducersUpdateTimerV6.cancel (); i2p::context.ClearSSU2Introducers (false); m_IntroducersV6.clear (); - m_IntroducersUpdateTimerV6.expires_from_now (boost::posix_time::seconds(SSU2_KEEP_ALIVE_INTERVAL/2)); + m_IntroducersUpdateTimerV6.expires_from_now (boost::posix_time::seconds( + (SSU2_KEEP_ALIVE_INTERVAL + rand () % SSU2_KEEP_ALIVE_INTERVAL_VARIANCE)/2)); m_IntroducersUpdateTimerV6.async_wait (std::bind (&SSU2Server::HandleIntroducersUpdateTimer, this, std::placeholders::_1, false)); } @@ -1106,7 +1194,7 @@ namespace transport // timeout expired if (v4) { - if (i2p::context.GetStatus () == eRouterStatusTesting) + if (i2p::context.GetTesting ()) { // we still don't know if we need introducers ScheduleIntroducersUpdateTimer (); @@ -1129,7 +1217,7 @@ namespace transport } else { - if (i2p::context.GetStatusV6 () == eRouterStatusTesting) + if (i2p::context.GetTestingV6 ()) { // we still don't know if we need introducers ScheduleIntroducersUpdateTimerV6 (); diff --git a/libi2pd/SSU2.h b/libi2pd/SSU2.h index 2e652786..da5ca317 100644 --- a/libi2pd/SSU2.h +++ b/libi2pd/SSU2.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2022-2023, The PurpleI2P Project +* Copyright (c) 2022-2024, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -13,6 +13,7 @@ #include #include "util.h" #include "SSU2Session.h" +#include "Socks5.h" namespace i2p { @@ -27,9 +28,11 @@ namespace transport const size_t SSU2_SOCKET_RECEIVE_BUFFER_SIZE = 0x1FFFF; // 128K const size_t SSU2_SOCKET_SEND_BUFFER_SIZE = 0x1FFFF; // 128K const size_t SSU2_MAX_NUM_INTRODUCERS = 3; + const size_t SSU2_MIN_RECEIVED_PACKET_SIZE = 40; // 16 byte short header + 8 byte minimum payload + 16 byte MAC const int SSU2_TO_INTRODUCER_SESSION_DURATION = 3600; // 1 hour const int SSU2_TO_INTRODUCER_SESSION_EXPIRATION = 4800; // 80 minutes - const int SSU2_KEEP_ALIVE_INTERVAL = 30; // in seconds + 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 class SSU2Server: private i2p::util::RunnableServiceWithWork @@ -65,6 +68,7 @@ namespace transport bool IsSupported (const boost::asio::ip::address& addr) const; uint16_t GetPort (bool v4) const; bool IsSyncClockFromPeers () const { return m_IsSyncClockFromPeers; }; + void AdjustTimeOffset (int64_t offset, std::shared_ptr from); void AddSession (std::shared_ptr session); void RemoveSession (uint64_t connID); @@ -160,6 +164,8 @@ namespace transport std::shared_ptr m_LastSession; bool m_IsPublished; // if we maintain introducers bool m_IsSyncClockFromPeers; + int64_t m_PendingTimeOffset; // during peer test + std::shared_ptr m_PendingTimeOffsetFrom; // proxy bool m_IsThroughProxy; diff --git a/libi2pd/SSU2Session.cpp b/libi2pd/SSU2Session.cpp index 47d59ea4..4015dcac 100644 --- a/libi2pd/SSU2Session.cpp +++ b/libi2pd/SSU2Session.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2022-2023, The PurpleI2P Project +* Copyright (c) 2022-2024, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -114,6 +114,8 @@ namespace transport { if (m_State == eSSU2SessionStateUnknown || m_State == eSSU2SessionStateTokenReceived) { + LogPrint(eLogDebug, "SSU2: Connecting to ", GetRemoteEndpoint (), + " (", i2p::data::GetIdentHashAbbreviation (GetRemoteIdentity ()->GetIdentHash ()), ")"); ScheduleConnectTimer (); auto token = m_Server.FindOutgoingToken (m_RemoteEndpoint); if (token) @@ -243,7 +245,7 @@ namespace transport if (IsEstablished ()) { uint8_t payload[20]; - size_t payloadSize = CreatePaddingBlock (payload, 20, 5); + size_t payloadSize = CreatePaddingBlock (payload, 20, 8); SendData (payload, payloadSize); } } @@ -260,8 +262,10 @@ namespace transport m_SentHandshakePacket.reset (nullptr); m_SessionConfirmedFragment.reset (nullptr); m_PathChallenge.reset (nullptr); + for (auto& it: m_SendQueue) + it->Drop (); m_SendQueue.clear (); - m_SendQueueSize = 0; + SetSendQueueSize (0); m_SentPackets.clear (); m_IncompleteMessages.clear (); m_RelaySessions.clear (); @@ -269,7 +273,16 @@ namespace transport m_ReceivedI2NPMsgIDs.clear (); m_Server.RemoveSession (m_SourceConnID); transports.PeerDisconnected (shared_from_this ()); - LogPrint (eLogDebug, "SSU2: Session terminated"); + 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"); + } } } @@ -298,6 +311,8 @@ namespace transport m_OnEstablished (); m_OnEstablished = nullptr; } + LogPrint(eLogDebug, "SSU2: Session with ", GetRemoteEndpoint (), + " (", i2p::data::GetIdentHashAbbreviation (GetRemoteIdentity ()->GetIdentHash ()), ") established"); } void SSU2Session::Done () @@ -336,8 +351,12 @@ namespace transport void SSU2Session::PostI2NPMessages (std::vector > msgs) { if (m_State == eSSU2SessionStateTerminated) return; + bool isSemiFull = m_SendQueue.size () > SSU2_MAX_OUTGOING_QUEUE_SIZE/2; for (auto it: msgs) - m_SendQueue.push_back (std::move (it)); + if (isSemiFull && it->onDrop) + it->Drop (); // drop earlier because we can handle it + else + m_SendQueue.push_back (std::move (it)); SendQueue (); if (m_SendQueue.size () > 0) // windows is full @@ -351,7 +370,7 @@ namespace transport RequestTermination (eSSU2TerminationReasonTimeout); } } - m_SendQueueSize = m_SendQueue.size (); + SetSendQueueSize (m_SendQueue.size ()); } bool SSU2Session::SendQueue () @@ -366,8 +385,10 @@ namespace transport while (!m_SendQueue.empty () && m_SentPackets.size () <= m_WindowSize) { auto msg = m_SendQueue.front (); - if (!msg) + if (!msg || msg->IsExpired (ts)) { + // drop null or expired message + if (msg) msg->Drop (); m_SendQueue.pop_front (); continue; } @@ -511,7 +532,7 @@ namespace transport LogPrint (eLogInfo, "SSU2: Packet was not Acked after ", it->second->numResends, " attempts. Terminate session"); m_SentPackets.clear (); m_SendQueue.clear (); - m_SendQueueSize = 0; + SetSendQueueSize (0); RequestTermination (eSSU2TerminationReasonTimeout); return resentPackets.size (); } @@ -809,6 +830,8 @@ namespace transport m_NoiseState->m_CK + 32, nonce, decryptedPayload.data (), decryptedPayload.size (), false)) { LogPrint (eLogWarning, "SSU2: SessionCreated AEAD verification failed "); + if (GetRemoteIdentity ()) + i2p::data::netdb.SetUnreachable (GetRemoteIdentity ()->GetIdentHash (), true); // assume wrong s key return false; } m_NoiseState->MixHash (payload, len - 64); // h = SHA256(h || encrypted payload from SessionCreated) for SessionConfirmed @@ -1051,6 +1074,17 @@ namespace transport LogPrint (eLogError, "SSU2: SessionConfirmed malformed RouterInfo block"); return false; } + auto ts = i2p::util::GetMillisecondsSinceEpoch(); + if (ts > ri->GetTimestamp () + i2p::data::NETDB_MIN_EXPIRATION_TIMEOUT*1000LL) // 90 minutes + { + LogPrint (eLogError, "SSU2: RouterInfo in SessionConfirmed is too old for ", (ts - ri->GetTimestamp ())/1000LL, " seconds"); + return false; + } + if (ts + i2p::data::NETDB_EXPIRATION_TIMEOUT_THRESHOLD*1000LL < ri->GetTimestamp ()) // 2 minutes + { + LogPrint (eLogError, "SSU2: RouterInfo in SessionConfirmed is from future for ", (ri->GetTimestamp () - ts)/1000LL, " seconds"); + return false; + } m_Address = m_RemoteEndpoint.address ().is_v6 () ? ri->GetSSU2V6Address () : ri->GetSSU2V4Address (); if (!m_Address || memcmp (S, m_Address->s, 32)) { @@ -1426,8 +1460,7 @@ namespace transport header.ll[1] ^= CreateHeaderMask (m_KeyDataSend + 32, payload + (len + 4)); m_Server.Send (header.buf, 16, payload, len + 16, m_RemoteEndpoint); m_SendPacketNum++; - m_LastActivityTimestamp = i2p::util::GetSecondsSinceEpoch (); - m_NumSentBytes += len + 32; + UpdateNumSentBytes (len + 32); return m_SendPacketNum - 1; } @@ -1446,7 +1479,7 @@ namespace transport ResendHandshakePacket (); // assume we receive return; } - if (from != m_RemoteEndpoint && !i2p::util::net::IsInReservedRange (from.address ())) + if (from != m_RemoteEndpoint && !i2p::transport::transports.IsInReservedRange (from.address ())) { LogPrint (eLogInfo, "SSU2: Remote endpoint update ", m_RemoteEndpoint, "->", from); m_RemoteEndpoint = from; @@ -1468,8 +1501,7 @@ namespace transport LogPrint (eLogWarning, "SSU2: Data AEAD verification failed "); return; } - m_LastActivityTimestamp = i2p::util::GetSecondsSinceEpoch (); - m_NumReceivedBytes += len; + UpdateNumReceivedBytes (len); if (!packetNum || UpdateReceivePacketNum (packetNum)) HandlePayload (payload, payloadSize); } @@ -1484,7 +1516,7 @@ namespace transport auto size = bufbe16toh (buf + offset); offset += 2; LogPrint (eLogDebug, "SSU2: Block type ", (int)blk, " of size ", size); - if (size > len) + if (offset + size > len) { LogPrint (eLogError, "SSU2: Unexpected block length ", size); break; @@ -1530,16 +1562,21 @@ namespace transport break; case eSSU2BlkTermination: { - uint8_t rsn = buf[11]; // reason - LogPrint (eLogDebug, "SSU2: Termination reason=", (int)rsn); - if (IsEstablished () && rsn != eSSU2TerminationReasonTerminationReceived) - RequestTermination (eSSU2TerminationReasonTerminationReceived); - else if (m_State != eSSU2SessionStateTerminated) + if (size >= 9) { - if (m_State == eSSU2SessionStateClosing && rsn == eSSU2TerminationReasonTerminationReceived) - m_State = eSSU2SessionStateClosingConfirmed; - Done (); + uint8_t rsn = buf[offset + 8]; // reason + LogPrint (eLogDebug, "SSU2: Termination reason=", (int)rsn); + if (IsEstablished () && rsn != eSSU2TerminationReasonTerminationReceived) + RequestTermination (eSSU2TerminationReasonTerminationReceived); + else if (m_State != eSSU2SessionStateTerminated) + { + if (m_State == eSSU2SessionStateClosing && rsn == eSSU2TerminationReasonTerminationReceived) + m_State = eSSU2SessionStateClosingConfirmed; + Done (); + } } + else + LogPrint(eLogWarning, "SSU2: Unexpected termination block size ", size); break; } case eSSU2BlkRelayRequest: @@ -1633,16 +1670,18 @@ namespace transport break; case eSSU2SessionStateSessionCreatedReceived: case eSSU2SessionStateTokenReceived: - if ((m_RemoteEndpoint.address ().is_v4 () && i2p::context.GetStatus () == eRouterStatusTesting) || - (m_RemoteEndpoint.address ().is_v6 () && i2p::context.GetStatusV6 () == eRouterStatusTesting)) + if ((m_RemoteEndpoint.address ().is_v4 () && i2p::context.GetTesting ()) || + (m_RemoteEndpoint.address ().is_v6 () && i2p::context.GetTestingV6 ())) { if (m_Server.IsSyncClockFromPeers ()) { if (std::abs (offset) > SSU2_CLOCK_THRESHOLD) - { - LogPrint (eLogWarning, "SSU2: Clock adjusted by ", -offset, " seconds"); - i2p::util::AdjustTimeOffset (-offset); - } + { + LogPrint (eLogWarning, "SSU2: Time offset ", offset, " from ", m_RemoteEndpoint); + m_Server.AdjustTimeOffset (-offset, GetRemoteIdentity ()); + } + else + m_Server.AdjustTimeOffset (0, nullptr); } else if (std::abs (offset) > SSU2_CLOCK_SKEW) { @@ -1722,7 +1761,7 @@ namespace transport if (ExtractEndpoint (buf, len, ep)) { LogPrint (eLogInfo, "SSU2: Our external address is ", ep); - if (!i2p::util::net::IsInReservedRange (ep.address ())) + if (!i2p::transport::transports.IsInReservedRange (ep.address ())) { i2p::context.UpdateAddress (ep.address ()); // check our port @@ -1732,14 +1771,14 @@ namespace transport LogPrint (eLogInfo, "SSU2: Our port ", ep.port (), " received from ", m_RemoteEndpoint, " is different from ", m_Server.GetPort (isV4)); if (isV4) { - if (i2p::context.GetStatus () == eRouterStatusTesting) + if (i2p::context.GetTesting ()) i2p::context.SetError (eRouterErrorSymmetricNAT); else if (m_State == eSSU2SessionStatePeerTest) i2p::context.SetError (eRouterErrorFullConeNAT); } else { - if (i2p::context.GetStatusV6 () == eRouterStatusTesting) + if (i2p::context.GetTestingV6 ()) i2p::context.SetErrorV6 (eRouterErrorSymmetricNAT); else if (m_State == eSSU2SessionStatePeerTest) i2p::context.SetErrorV6 (eRouterErrorFullConeNAT); @@ -2212,7 +2251,7 @@ namespace transport if (buf[1] == eSSU2PeerTestCodeAccept) { if (GetRouterStatus () == eRouterStatusUnknown) - SetRouterStatus (eRouterStatusTesting); + SetTestingState (true); auto r = i2p::data::netdb.FindRouter (buf + 3); // find Charlie if (r && it->second.first) { @@ -2238,13 +2277,17 @@ namespace transport } else { - if (GetRouterStatus () == eRouterStatusTesting) + if (GetTestingState ()) { - SetRouterStatus (eRouterStatusFirewalled); - if (m_Address->IsV4 ()) - m_Server.RescheduleIntroducersUpdateTimer (); - else - m_Server.RescheduleIntroducersUpdateTimerV6 (); + SetTestingState (false); + if (GetRouterStatus () != eRouterStatusFirewalled) + { + SetRouterStatus (eRouterStatusFirewalled); + if (m_Address->IsV4 ()) + m_Server.RescheduleIntroducersUpdateTimer (); + else + m_Server.RescheduleIntroducersUpdateTimerV6 (); + } } } LogPrint (eLogDebug, "SSU2: Peer test 4 received from ", i2p::data::GetIdentHashAbbreviation (GetRemoteIdentity ()->GetIdentHash ()), @@ -2273,7 +2316,7 @@ namespace transport { 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 (GetRouterStatus () == eRouterStatusTesting) + if (GetTestingState ()) SetRouterStatus (eRouterStatusUnknown); it->second.first->Done (); } @@ -2322,7 +2365,7 @@ namespace transport if (!msg->IsExpired ()) { // m_LastActivityTimestamp is updated in ProcessData before - if (m_ReceivedI2NPMsgIDs.emplace (msgID, (uint32_t)m_LastActivityTimestamp).second) + if (m_ReceivedI2NPMsgIDs.emplace (msgID, (uint32_t)GetLastActivityTimestamp ()).second) m_Handler.PutNextMessage (std::move (msg)); else LogPrint (eLogDebug, "SSU2: Message ", msgID, " already received"); @@ -2427,6 +2470,31 @@ namespace transport } } + bool SSU2Session::GetTestingState () const + { + if (m_Address) + { + if (m_Address->IsV4 ()) + return i2p::context.GetTesting (); + if (m_Address->IsV6 ()) + return i2p::context.GetTestingV6 (); + } + return false; + } + + void SSU2Session::SetTestingState (bool testing) const + { + if (m_Address) + { + if (m_Address->IsV4 ()) + i2p::context.SetTesting (testing); + else if (m_Address->IsV6 ()) + i2p::context.SetTestingV6 (testing); + } + if (!testing) + m_Server.AdjustTimeOffset (0, nullptr); // reset time offset when testing is over + } + size_t SSU2Session::CreateAddressBlock (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& ep) { if (len < 9) return 0; @@ -2839,7 +2907,7 @@ namespace transport void SSU2Session::SendPathResponse (const uint8_t * data, size_t len) { - if (len < 8 || len > m_MaxPayloadSize - 3) + if (len > m_MaxPayloadSize - 3) { LogPrint (eLogWarning, "SSU2: Incorrect data size for path response ", len); return; @@ -2848,7 +2916,10 @@ namespace transport payload[0] = eSSU2BlkPathResponse; htobe16buf (payload + 1, len); memcpy (payload + 3, data, len); - SendData (payload, len + 3); + size_t payloadSize = len + 3; + if (payloadSize < m_MaxPayloadSize) + payloadSize += CreatePaddingBlock (payload + payloadSize, m_MaxPayloadSize - payloadSize, payloadSize < 8 ? 8 : 0); + SendData (payload, payloadSize); } void SSU2Session::SendPathChallenge () @@ -2866,7 +2937,7 @@ namespace transport } len += 3; if (len < m_MaxPayloadSize) - len += CreatePaddingBlock (payload + len, m_MaxPayloadSize - len); + len += CreatePaddingBlock (payload + len, m_MaxPayloadSize - len, len < 8 ? 8 : 0); SendData (payload, len); } @@ -2882,7 +2953,7 @@ namespace transport else ++it; } - if (m_ReceivedI2NPMsgIDs.size () > SSU2_MAX_NUM_RECEIVED_I2NP_MSGIDS || ts > m_LastActivityTimestamp + SSU2_DECAY_INTERVAL) + if (m_ReceivedI2NPMsgIDs.size () > SSU2_MAX_NUM_RECEIVED_I2NP_MSGIDS || ts > GetLastActivityTimestamp () + SSU2_DECAY_INTERVAL) // decay m_ReceivedI2NPMsgIDs.clear (); else @@ -2954,7 +3025,7 @@ namespace transport { bool sent = SendQueue (); // if we have something to send if (sent) - m_SendQueueSize = m_SendQueue.size (); + SetSendQueueSize (m_SendQueue.size ()); if (m_IsDataReceived) { if (!sent) SendQuickAck (); diff --git a/libi2pd/SSU2Session.h b/libi2pd/SSU2Session.h index 304efdab..5dd45fd4 100644 --- a/libi2pd/SSU2Session.h +++ b/libi2pd/SSU2Session.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2022-2023, The PurpleI2P Project +* Copyright (c) 2022-2024, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -25,7 +25,7 @@ namespace i2p namespace transport { const int SSU2_CONNECT_TIMEOUT = 5; // 5 seconds - const int SSU2_TERMINATION_TIMEOUT = 330; // 5.5 minutes + const int SSU2_TERMINATION_TIMEOUT = 165; // in seconds const int SSU2_CLOCK_SKEW = 60; // in seconds const int SSU2_CLOCK_THRESHOLD = 15; // in seconds, if more we should adjust const int SSU2_TOKEN_EXPIRATION_TIMEOUT = 9; // for Retry message, in seconds @@ -308,6 +308,8 @@ namespace transport void AdjustMaxPayloadSize (); RouterStatus GetRouterStatus () const; void SetRouterStatus (RouterStatus status) const; + bool GetTestingState () const; + void SetTestingState(bool testing) const; std::shared_ptr 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 diff --git a/libi2pd/Signature.cpp b/libi2pd/Signature.cpp index ebc188a9..342b6d03 100644 --- a/libi2pd/Signature.cpp +++ b/libi2pd/Signature.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2021, The PurpleI2P Project +* Copyright (c) 2013-2023, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -15,26 +15,35 @@ namespace i2p namespace crypto { #if OPENSSL_EDDSA - EDDSA25519Verifier::EDDSA25519Verifier () + EDDSA25519Verifier::EDDSA25519Verifier (): + m_Pkey (nullptr) { - m_MDCtx = EVP_MD_CTX_create (); } EDDSA25519Verifier::~EDDSA25519Verifier () { - EVP_MD_CTX_destroy (m_MDCtx); + EVP_PKEY_free (m_Pkey); } void EDDSA25519Verifier::SetPublicKey (const uint8_t * signingKey) { - EVP_PKEY * pkey = EVP_PKEY_new_raw_public_key (EVP_PKEY_ED25519, NULL, signingKey, 32); - EVP_DigestVerifyInit (m_MDCtx, NULL, NULL, NULL, pkey); - EVP_PKEY_free (pkey); + if (m_Pkey) EVP_PKEY_free (m_Pkey); + m_Pkey = EVP_PKEY_new_raw_public_key (EVP_PKEY_ED25519, NULL, signingKey, 32); } bool EDDSA25519Verifier::Verify (const uint8_t * buf, size_t len, const uint8_t * signature) const { - return EVP_DigestVerify (m_MDCtx, signature, 64, buf, len); + if (m_Pkey) + { + EVP_MD_CTX * ctx = EVP_MD_CTX_create (); + EVP_DigestVerifyInit (ctx, NULL, NULL, NULL, m_Pkey); + auto ret = EVP_DigestVerify (ctx, signature, 64, buf, len); + EVP_MD_CTX_destroy (ctx); + return ret; + } + else + LogPrint (eLogError, "EdDSA verification key is not set"); + return false; } #else @@ -99,41 +108,45 @@ namespace crypto #if OPENSSL_EDDSA EDDSA25519Signer::EDDSA25519Signer (const uint8_t * signingPrivateKey, const uint8_t * signingPublicKey): - m_MDCtx (nullptr), m_Fallback (nullptr) + m_Pkey (nullptr), m_Fallback (nullptr) { - EVP_PKEY * pkey = EVP_PKEY_new_raw_private_key (EVP_PKEY_ED25519, NULL, signingPrivateKey, 32); + m_Pkey = EVP_PKEY_new_raw_private_key (EVP_PKEY_ED25519, NULL, signingPrivateKey, 32); uint8_t publicKey[EDDSA25519_PUBLIC_KEY_LENGTH]; size_t len = EDDSA25519_PUBLIC_KEY_LENGTH; - EVP_PKEY_get_raw_public_key (pkey, publicKey, &len); + EVP_PKEY_get_raw_public_key (m_Pkey, publicKey, &len); if (signingPublicKey && memcmp (publicKey, signingPublicKey, EDDSA25519_PUBLIC_KEY_LENGTH)) { LogPrint (eLogWarning, "EdDSA public key mismatch. Fallback"); m_Fallback = new EDDSA25519SignerCompat (signingPrivateKey, signingPublicKey); + EVP_PKEY_free (m_Pkey); + m_Pkey = nullptr; } - else - { - m_MDCtx = EVP_MD_CTX_create (); - EVP_DigestSignInit (m_MDCtx, NULL, NULL, NULL, pkey); - } - EVP_PKEY_free (pkey); } EDDSA25519Signer::~EDDSA25519Signer () { if (m_Fallback) delete m_Fallback; - EVP_MD_CTX_destroy (m_MDCtx); + if (m_Pkey) EVP_PKEY_free (m_Pkey); } void EDDSA25519Signer::Sign (const uint8_t * buf, int len, uint8_t * signature) const { - if (m_Fallback) return m_Fallback->Sign (buf, len, signature); - else + if (m_Fallback) + return m_Fallback->Sign (buf, len, signature); + else if (m_Pkey) { + + EVP_MD_CTX * ctx = EVP_MD_CTX_create (); size_t l = 64; uint8_t sig[64]; // temporary buffer for signature. openssl issue #7232 - EVP_DigestSign (m_MDCtx, sig, &l, buf, len); + EVP_DigestSignInit (ctx, NULL, NULL, NULL, m_Pkey); + if (!EVP_DigestSign (ctx, sig, &l, buf, len)) + LogPrint (eLogError, "EdDSA signing failed"); memcpy (signature, sig, 64); + EVP_MD_CTX_destroy (ctx); } + else + LogPrint (eLogError, "EdDSA signing key is not set"); } #endif } diff --git a/libi2pd/Signature.h b/libi2pd/Signature.h index e153e66d..8bd94357 100644 --- a/libi2pd/Signature.h +++ b/libi2pd/Signature.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2021, The PurpleI2P Project +* Copyright (c) 2013-2023, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -304,7 +304,7 @@ namespace crypto private: #if OPENSSL_EDDSA - EVP_MD_CTX * m_MDCtx; + EVP_PKEY * m_Pkey; #else EDDSAPoint m_PublicKey; uint8_t m_PublicKeyEncoded[EDDSA25519_PUBLIC_KEY_LENGTH]; @@ -341,7 +341,7 @@ namespace crypto private: - EVP_MD_CTX * m_MDCtx; + EVP_PKEY * m_Pkey; EDDSA25519SignerCompat * m_Fallback; }; #else diff --git a/libi2pd/Socks5.h b/libi2pd/Socks5.h new file mode 100644 index 00000000..8db8939b --- /dev/null +++ b/libi2pd/Socks5.h @@ -0,0 +1,210 @@ +/* +* Copyright (c) 2024, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +* +*/ + +#ifndef SOCKS5_H__ +#define SOCKS5_H__ + +#include +#include +#include +#include "I2PEndian.h" + +namespace i2p +{ +namespace transport +{ + // SOCKS5 constants + const uint8_t SOCKS5_VER = 0x05; + const uint8_t SOCKS5_CMD_CONNECT = 0x01; + const uint8_t SOCKS5_CMD_UDP_ASSOCIATE = 0x03; + const uint8_t SOCKS5_ATYP_IPV4 = 0x01; + const uint8_t SOCKS5_ATYP_IPV6 = 0x04; + const uint8_t SOCKS5_ATYP_NAME = 0x03; + const size_t SOCKS5_UDP_IPV4_REQUEST_HEADER_SIZE = 10; + const size_t SOCKS5_UDP_IPV6_REQUEST_HEADER_SIZE = 22; + + const uint8_t SOCKS5_REPLY_SUCCESS = 0x00; + const uint8_t SOCKS5_REPLY_SERVER_FAILURE = 0x01; + const uint8_t SOCKS5_REPLY_CONNECTION_NOT_ALLOWED = 0x02; + const uint8_t SOCKS5_REPLY_NETWORK_UNREACHABLE = 0x03; + const uint8_t SOCKS5_REPLY_HOST_UNREACHABLE = 0x04; + const uint8_t SOCKS5_REPLY_CONNECTION_REFUSED = 0x05; + const uint8_t SOCKS5_REPLY_TTL_EXPIRED = 0x06; + const uint8_t SOCKS5_REPLY_COMMAND_NOT_SUPPORTED = 0x07; + const uint8_t SOCKS5_REPLY_ADDRESS_TYPE_NOT_SUPPORTED = 0x08; + + // SOCKS5 handshake + template + void Socks5ReadReply (Socket& s, Handler handler) + { + auto readbuff = std::make_shared >(258); // max possible + boost::asio::async_read(s, boost::asio::buffer(readbuff->data (), 5), boost::asio::transfer_all(), // read 4 bytes of header + first byte of address + [readbuff, &s, handler](const boost::system::error_code& ec, std::size_t transferred) + { + if (!ec) + { + if ((*readbuff)[1] == SOCKS5_REPLY_SUCCESS) + { + size_t len = 0; + switch ((*readbuff)[3]) // ATYP + { + case SOCKS5_ATYP_IPV4: len = 3; break; // address length 4 bytes + case SOCKS5_ATYP_IPV6: len = 15; break; // address length 16 bytes + case SOCKS5_ATYP_NAME: len += (*readbuff)[4]; break; // first byte of address is length + default: ; + } + if (len) + { + len += 2; // port + boost::asio::async_read(s, boost::asio::buffer(readbuff->data (), len), boost::asio::transfer_all(), + [readbuff, handler](const boost::system::error_code& ec, std::size_t transferred) + { + if (!ec) + handler (boost::system::error_code ()); // success + else + handler (boost::asio::error::make_error_code (boost::asio::error::connection_aborted)); + }); + } + else + handler (boost::asio::error::make_error_code (boost::asio::error::fault)); // unknown address type + } + else + switch ((*readbuff)[1]) // REP + { + case SOCKS5_REPLY_SERVER_FAILURE: + handler (boost::asio::error::make_error_code (boost::asio::error::access_denied )); + break; + case SOCKS5_REPLY_CONNECTION_NOT_ALLOWED: + handler (boost::asio::error::make_error_code (boost::asio::error::no_permission)); + break; + case SOCKS5_REPLY_HOST_UNREACHABLE: + handler (boost::asio::error::make_error_code (boost::asio::error::host_unreachable)); + break; + case SOCKS5_REPLY_NETWORK_UNREACHABLE: + handler (boost::asio::error::make_error_code (boost::asio::error::network_unreachable)); + break; + case SOCKS5_REPLY_CONNECTION_REFUSED: + handler (boost::asio::error::make_error_code (boost::asio::error::connection_refused)); + break; + case SOCKS5_REPLY_TTL_EXPIRED: + handler (boost::asio::error::make_error_code (boost::asio::error::timed_out)); + break; + case SOCKS5_REPLY_COMMAND_NOT_SUPPORTED: + handler (boost::asio::error::make_error_code (boost::asio::error::operation_not_supported)); + break; + case SOCKS5_REPLY_ADDRESS_TYPE_NOT_SUPPORTED: + handler (boost::asio::error::make_error_code (boost::asio::error::no_protocol_option)); + break; + default: + handler (boost::asio::error::make_error_code (boost::asio::error::connection_aborted)); + } + } + else + handler (ec); + }); + } + + template + void Socks5Connect (Socket& s, Handler handler, std::shared_ptr > buff, uint16_t port) + { + if (buff && buff->size () >= 6) + { + (*buff)[0] = SOCKS5_VER; + (*buff)[1] = SOCKS5_CMD_CONNECT; + (*buff)[2] = 0x00; + htobe16buf(buff->data () + buff->size () - 2, port); + boost::asio::async_write(s, boost::asio::buffer(*buff), boost::asio::transfer_all(), + [buff, &s, handler](const boost::system::error_code& ec, std::size_t transferred) + { + (void) transferred; + if (!ec) + Socks5ReadReply (s, handler); + else + handler (ec); + }); + } + else + handler (boost::asio::error::make_error_code (boost::asio::error::no_buffer_space)); + } + + template + void Socks5Connect (Socket& s, const boost::asio::ip::tcp::endpoint& ep, Handler handler) + { + std::shared_ptr > buff; + if(ep.address ().is_v4 ()) + { + buff = std::make_shared >(10); + (*buff)[3] = SOCKS5_ATYP_IPV4; + auto addrbytes = ep.address ().to_v4().to_bytes(); + memcpy(buff->data () + 4, addrbytes.data(), 4); + } + else if (ep.address ().is_v6 ()) + { + buff = std::make_shared >(22); + (*buff)[3] = SOCKS5_ATYP_IPV6; + auto addrbytes = ep.address ().to_v6().to_bytes(); + memcpy(buff->data () + 4, addrbytes.data(), 16); + } + if (buff) + Socks5Connect (s, handler, buff, ep.port ()); + else + handler (boost::asio::error::make_error_code (boost::asio::error::fault)); + } + + template + void Socks5Connect (Socket& s, const std::pair& ep, Handler handler) + { + auto& addr = ep.first; + if (addr.length () <= 255) + { + auto buff = std::make_shared >(addr.length () + 7); + (*buff)[3] = SOCKS5_ATYP_NAME; + (*buff)[4] = addr.length (); + memcpy (buff->data () + 5, addr.c_str (), addr.length ()); + Socks5Connect (s, handler, buff, ep.second); + } + else + handler (boost::asio::error::make_error_code (boost::asio::error::name_too_long)); + } + + + template + void Socks5Handshake (Socket& s, Endpoint ep, Handler handler) + { + static const uint8_t methodSelection[3] = { SOCKS5_VER, 0x01, 0x00 }; // 1 method, no auth + boost::asio::async_write(s, boost::asio::buffer(methodSelection, 3), boost::asio::transfer_all(), + [&s, ep, handler] (const boost::system::error_code& ec, std::size_t transferred) + { + (void) transferred; + if (!ec) + { + auto readbuff = std::make_shared >(2); + boost::asio::async_read(s, boost::asio::buffer(*readbuff), boost::asio::transfer_all(), + [&s, ep, handler, readbuff] (const boost::system::error_code& ec, std::size_t transferred) + { + if (!ec) + { + if (transferred == 2 && (*readbuff)[1] == 0x00) // no auth + Socks5Connect (s, ep, handler); + else + handler (boost::asio::error::make_error_code (boost::asio::error::invalid_argument)); + } + else + handler (ec); + }); + } + else + handler (ec); + }); + } + +} +} + +#endif diff --git a/libi2pd/Streaming.cpp b/libi2pd/Streaming.cpp index c2cf3a0a..fa809c19 100644 --- a/libi2pd/Streaming.cpp +++ b/libi2pd/Streaming.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2023, The PurpleI2P Project +* Copyright (c) 2013-2024, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -19,11 +19,6 @@ namespace i2p { namespace stream { - void SendBufferQueue::Add (const uint8_t * buf, size_t len, SendHandler handler) - { - Add (std::make_shared(buf, len, handler)); - } - void SendBufferQueue::Add (std::shared_ptr buf) { if (buf) @@ -115,10 +110,7 @@ namespace stream void Stream::CleanUp () { - { - std::unique_lock l(m_SendBufferMutex); - m_SendBuffer.CleanUp (); - } + m_SendBuffer.CleanUp (); while (!m_ReceiveQueue.empty ()) { auto packet = m_ReceiveQueue.front (); @@ -137,6 +129,11 @@ namespace stream void Stream::HandleNextPacket (Packet * packet) { + if (m_Status == eStreamStatusTerminated) + { + m_LocalDestination.DeletePacket (packet); + return; + } m_NumReceivedBytes += packet->GetLength (); if (!m_SendStreamID) { @@ -167,7 +164,8 @@ namespace stream { // we have received next in sequence message ProcessPacket (packet); - + if (m_Status == eStreamStatusTerminated) return; + // we should also try stored messages if any for (auto it = m_SavedPackets.begin (); it != m_SavedPackets.end ();) { @@ -177,6 +175,7 @@ namespace stream m_SavedPackets.erase (it++); ProcessPacket (savedPacket); + if (m_Status == eStreamStatusTerminated) return; } else break; @@ -187,13 +186,9 @@ namespace stream { if (!m_IsAckSendScheduled) { - m_IsAckSendScheduled = true; auto ackTimeout = m_RTT/10; if (ackTimeout > m_AckDelay) ackTimeout = m_AckDelay; - else if (ackTimeout < MIN_SEND_ACK_TIMEOUT) ackTimeout = MIN_SEND_ACK_TIMEOUT; - m_AckSendTimer.expires_from_now (boost::posix_time::milliseconds(ackTimeout)); - m_AckSendTimer.async_wait (std::bind (&Stream::HandleAckSendTimer, - shared_from_this (), std::placeholders::_1)); + ScheduleAck (ackTimeout); } } else if (packet->IsSYN ()) @@ -216,22 +211,17 @@ namespace stream SavePacket (packet); if (m_LastReceivedSequenceNumber >= 0) { - // send NACKs for missing messages ASAP - if (m_IsAckSendScheduled) - { - m_IsAckSendScheduled = false; - m_AckSendTimer.cancel (); - } - SendQuickAck (); - } + if (!m_IsAckSendScheduled) + { + // send NACKs for missing messages + int ackTimeout = MIN_SEND_ACK_TIMEOUT*m_SavedPackets.size (); + if (ackTimeout > m_AckDelay) ackTimeout = m_AckDelay; + ScheduleAck (ackTimeout); + } + } else - { // wait for SYN - m_IsAckSendScheduled = true; - m_AckSendTimer.expires_from_now (boost::posix_time::milliseconds(SYN_TIMEOUT)); - m_AckSendTimer.async_wait (std::bind (&Stream::HandleAckSendTimer, - shared_from_this (), std::placeholders::_1)); - } + ScheduleAck (SYN_TIMEOUT); } } } @@ -532,14 +522,18 @@ namespace stream void Stream::AsyncSend (const uint8_t * buf, size_t len, SendHandler handler) { + std::shared_ptr buffer; if (len > 0 && buf) - { - std::unique_lock l(m_SendBufferMutex); - m_SendBuffer.Add (buf, len, handler); - } + buffer = std::make_shared(buf, len, handler); else if (handler) handler(boost::system::error_code ()); - m_Service.post (std::bind (&Stream::SendBuffer, shared_from_this ())); + auto s = shared_from_this (); + m_Service.post ([s, buffer]() + { + if (buffer) + s->m_SendBuffer.Add (buffer); + s->SendBuffer (); + }); } void Stream::SendBuffer () @@ -549,91 +543,88 @@ namespace stream bool isNoAck = m_LastReceivedSequenceNumber < 0; // first packet std::vector packets; + while ((m_Status == eStreamStatusNew) || (IsEstablished () && !m_SendBuffer.IsEmpty () && numMsgs > 0)) { - std::unique_lock l(m_SendBufferMutex); - while ((m_Status == eStreamStatusNew) || (IsEstablished () && !m_SendBuffer.IsEmpty () && numMsgs > 0)) + Packet * p = m_LocalDestination.NewPacket (); + uint8_t * packet = p->GetBuffer (); + // TODO: implement setters + size_t size = 0; + htobe32buf (packet + size, m_SendStreamID); + size += 4; // sendStreamID + htobe32buf (packet + size, m_RecvStreamID); + size += 4; // receiveStreamID + htobe32buf (packet + size, m_SequenceNumber++); + size += 4; // sequenceNum + if (isNoAck) + htobuf32 (packet + size, 0); + else + htobe32buf (packet + size, m_LastReceivedSequenceNumber); + size += 4; // ack Through + if (m_Status == eStreamStatusNew && !m_SendStreamID && m_RemoteIdentity) { - Packet * p = m_LocalDestination.NewPacket (); - uint8_t * packet = p->GetBuffer (); - // TODO: implement setters - size_t size = 0; - htobe32buf (packet + size, m_SendStreamID); - size += 4; // sendStreamID - htobe32buf (packet + size, m_RecvStreamID); - size += 4; // receiveStreamID - htobe32buf (packet + size, m_SequenceNumber++); - size += 4; // sequenceNum - if (isNoAck) - htobuf32 (packet + size, 0); - else - htobe32buf (packet + size, m_LastReceivedSequenceNumber); - size += 4; // ack Through - if (m_Status == eStreamStatusNew && !m_SendStreamID && m_RemoteIdentity) - { - // first SYN packet - packet[size] = 8; - size++; // NACK count - memcpy (packet + size, m_RemoteIdentity->GetIdentHash (), 32); - size += 32; - } - else - { - packet[size] = 0; - size++; // NACK count - } - packet[size] = m_RTO/1000; - size++; // resend delay - if (m_Status == eStreamStatusNew) - { - // initial packet - m_Status = eStreamStatusOpen; - if (!m_RemoteLeaseSet) m_RemoteLeaseSet = m_LocalDestination.GetOwner ()->FindLeaseSet (m_RemoteIdentity->GetIdentHash ());; - if (m_RemoteLeaseSet) - { - m_RoutingSession = m_LocalDestination.GetOwner ()->GetRoutingSession (m_RemoteLeaseSet, true); - m_MTU = m_RoutingSession->IsRatchets () ? STREAMING_MTU_RATCHETS : STREAMING_MTU; - } - uint16_t flags = PACKET_FLAG_SYNCHRONIZE | PACKET_FLAG_FROM_INCLUDED | - PACKET_FLAG_SIGNATURE_INCLUDED | PACKET_FLAG_MAX_PACKET_SIZE_INCLUDED; - if (isNoAck) flags |= PACKET_FLAG_NO_ACK; - bool isOfflineSignature = m_LocalDestination.GetOwner ()->GetPrivateKeys ().IsOfflineSignature (); - if (isOfflineSignature) flags |= PACKET_FLAG_OFFLINE_SIGNATURE; - htobe16buf (packet + size, flags); - size += 2; // flags - size_t identityLen = m_LocalDestination.GetOwner ()->GetIdentity ()->GetFullLen (); - size_t signatureLen = m_LocalDestination.GetOwner ()->GetPrivateKeys ().GetSignatureLen (); - uint8_t * optionsSize = packet + size; // set options size later - size += 2; // options size - m_LocalDestination.GetOwner ()->GetIdentity ()->ToBuffer (packet + size, identityLen); - size += identityLen; // from - htobe16buf (packet + size, m_MTU); - size += 2; // max packet size - if (isOfflineSignature) - { - const auto& offlineSignature = m_LocalDestination.GetOwner ()->GetPrivateKeys ().GetOfflineSignature (); - memcpy (packet + size, offlineSignature.data (), offlineSignature.size ()); - size += offlineSignature.size (); // offline signature - } - uint8_t * signature = packet + size; // set it later - memset (signature, 0, signatureLen); // zeroes for now - size += signatureLen; // signature - htobe16buf (optionsSize, packet + size - 2 - optionsSize); // actual options size - size += m_SendBuffer.Get (packet + size, m_MTU); // payload - m_LocalDestination.GetOwner ()->Sign (packet, size, signature); - } - else - { - // follow on packet - htobuf16 (packet + size, 0); - size += 2; // flags - htobuf16 (packet + size, 0); // no options - size += 2; // options size - size += m_SendBuffer.Get(packet + size, m_MTU); // payload - } - p->len = size; - packets.push_back (p); - numMsgs--; + // first SYN packet + packet[size] = 8; + size++; // NACK count + memcpy (packet + size, m_RemoteIdentity->GetIdentHash (), 32); + size += 32; } + else + { + packet[size] = 0; + size++; // NACK count + } + packet[size] = m_RTO/1000; + size++; // resend delay + if (m_Status == eStreamStatusNew) + { + // initial packet + m_Status = eStreamStatusOpen; + if (!m_RemoteLeaseSet) m_RemoteLeaseSet = m_LocalDestination.GetOwner ()->FindLeaseSet (m_RemoteIdentity->GetIdentHash ());; + if (m_RemoteLeaseSet) + { + m_RoutingSession = m_LocalDestination.GetOwner ()->GetRoutingSession (m_RemoteLeaseSet, true); + m_MTU = m_RoutingSession->IsRatchets () ? STREAMING_MTU_RATCHETS : STREAMING_MTU; + } + uint16_t flags = PACKET_FLAG_SYNCHRONIZE | PACKET_FLAG_FROM_INCLUDED | + PACKET_FLAG_SIGNATURE_INCLUDED | PACKET_FLAG_MAX_PACKET_SIZE_INCLUDED; + if (isNoAck) flags |= PACKET_FLAG_NO_ACK; + bool isOfflineSignature = m_LocalDestination.GetOwner ()->GetPrivateKeys ().IsOfflineSignature (); + if (isOfflineSignature) flags |= PACKET_FLAG_OFFLINE_SIGNATURE; + htobe16buf (packet + size, flags); + size += 2; // flags + size_t identityLen = m_LocalDestination.GetOwner ()->GetIdentity ()->GetFullLen (); + size_t signatureLen = m_LocalDestination.GetOwner ()->GetPrivateKeys ().GetSignatureLen (); + uint8_t * optionsSize = packet + size; // set options size later + size += 2; // options size + m_LocalDestination.GetOwner ()->GetIdentity ()->ToBuffer (packet + size, identityLen); + size += identityLen; // from + htobe16buf (packet + size, m_MTU); + size += 2; // max packet size + if (isOfflineSignature) + { + const auto& offlineSignature = m_LocalDestination.GetOwner ()->GetPrivateKeys ().GetOfflineSignature (); + memcpy (packet + size, offlineSignature.data (), offlineSignature.size ()); + size += offlineSignature.size (); // offline signature + } + uint8_t * signature = packet + size; // set it later + memset (signature, 0, signatureLen); // zeroes for now + size += signatureLen; // signature + htobe16buf (optionsSize, packet + size - 2 - optionsSize); // actual options size + size += m_SendBuffer.Get (packet + size, m_MTU); // payload + m_LocalDestination.GetOwner ()->Sign (packet, size, signature); + } + else + { + // follow on packet + htobuf16 (packet + size, 0); + size += 2; // flags + htobuf16 (packet + size, 0); // no options + size += 2; // options size + size += m_SendBuffer.Get(packet + size, m_MTU); // payload + } + p->len = size; + packets.push_back (p); + numMsgs--; } if (packets.size () > 0) { @@ -1036,6 +1027,17 @@ namespace stream } } + void Stream::ScheduleAck (int timeout) + { + if (m_IsAckSendScheduled) + m_AckSendTimer.cancel (); + m_IsAckSendScheduled = true; + if (timeout < MIN_SEND_ACK_TIMEOUT) timeout = MIN_SEND_ACK_TIMEOUT; + m_AckSendTimer.expires_from_now (boost::posix_time::milliseconds(timeout)); + m_AckSendTimer.async_wait (std::bind (&Stream::HandleAckSendTimer, + shared_from_this (), std::placeholders::_1)); + } + void Stream::HandleAckSendTimer (const boost::system::error_code& ecode) { if (m_IsAckSendScheduled) diff --git a/libi2pd/Streaming.h b/libi2pd/Streaming.h index 3609df92..ed79edc9 100644 --- a/libi2pd/Streaming.h +++ b/libi2pd/Streaming.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2023, The PurpleI2P Project +* Copyright (c) 2013-2024, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -135,7 +135,6 @@ namespace stream SendBufferQueue (): m_Size (0) {}; ~SendBufferQueue () { CleanUp (); }; - void Add (const uint8_t * buf, size_t len, SendHandler handler); void Add (std::shared_ptr buf); size_t Get (uint8_t * buf, size_t len); size_t GetSize () const { return m_Size; }; @@ -228,6 +227,7 @@ namespace stream void ScheduleResend (); void HandleResendTimer (const boost::system::error_code& ecode); + void ScheduleAck (int timeout); void HandleAckSendTimer (const boost::system::error_code& ecode); private: @@ -251,7 +251,6 @@ namespace stream size_t m_NumSentBytes, m_NumReceivedBytes; uint16_t m_Port; - std::mutex m_SendBufferMutex; SendBufferQueue m_SendBuffer; int m_WindowSize, m_RTT, m_RTO, m_AckDelay; uint64_t m_LastWindowSizeIncreaseTime; diff --git a/libi2pd/TransportSession.h b/libi2pd/TransportSession.h index 7d2f2653..c6bf0de3 100644 --- a/libi2pd/TransportSession.h +++ b/libi2pd/TransportSession.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2023, The PurpleI2P Project +* Copyright (c) 2013-2024, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -71,15 +71,17 @@ namespace transport const int64_t TRANSPORT_SESSION_SLOWNESS_THRESHOLD = 500; // in milliseconds const int64_t TRANSPORT_SESSION_MAX_HANDSHAKE_INTERVAL = 10000; // in milliseconds + const uint64_t TRANSPORT_SESSION_BANDWIDTH_UPDATE_MIN_INTERVAL = 5; // in seconds class TransportSession { public: TransportSession (std::shared_ptr router, int terminationTimeout): - m_NumSentBytes (0), m_NumReceivedBytes (0), m_SendQueueSize (0), - m_IsOutgoing (router), m_TerminationTimeout (terminationTimeout), - m_LastActivityTimestamp (i2p::util::GetSecondsSinceEpoch ()), - m_HandshakeInterval (0) + m_IsOutgoing (router), m_TerminationTimeout (terminationTimeout), m_HandshakeInterval (0), + m_SendQueueSize (0), m_NumSentBytes (0), m_NumReceivedBytes (0), + m_LastBandWidthUpdateNumSentBytes (0), m_LastBandWidthUpdateNumReceivedBytes (0), + m_LastActivityTimestamp (i2p::util::GetSecondsSinceEpoch ()), + m_LastBandwidthUpdateTimestamp (m_LastActivityTimestamp), m_InBandwidth (0), m_OutBandwidth (0) { if (router) m_RemoteIdentity = router->GetRouterIdentity (); @@ -103,11 +105,29 @@ namespace transport } size_t GetNumSentBytes () const { return m_NumSentBytes; }; + void UpdateNumSentBytes (size_t len) + { + m_LastActivityTimestamp = i2p::util::GetSecondsSinceEpoch (); + m_NumSentBytes += len; + UpdateBandwidth (); + } size_t GetNumReceivedBytes () const { return m_NumReceivedBytes; }; + void UpdateNumReceivedBytes (size_t len) + { + m_LastActivityTimestamp = i2p::util::GetSecondsSinceEpoch (); + m_NumReceivedBytes += len; + UpdateBandwidth (); + } size_t GetSendQueueSize () const { return m_SendQueueSize; }; + void SetSendQueueSize (size_t s) { m_SendQueueSize = s; }; bool IsOutgoing () const { return m_IsOutgoing; }; bool IsSlow () const { return m_HandshakeInterval > TRANSPORT_SESSION_SLOWNESS_THRESHOLD && m_HandshakeInterval < TRANSPORT_SESSION_MAX_HANDSHAKE_INTERVAL; }; + bool IsBandwidthExceeded (bool isHighBandwidth) const + { + auto limit = isHighBandwidth ? i2p::data::HIGH_BANDWIDTH_LIMIT*1024 : i2p::data::LOW_BANDWIDTH_LIMIT*1024; // convert to bytes + return std::max (m_InBandwidth, m_OutBandwidth) > limit; + } int GetTerminationTimeout () const { return m_TerminationTimeout; }; void SetTerminationTimeout (int terminationTimeout) { m_TerminationTimeout = terminationTimeout; }; @@ -120,31 +140,53 @@ namespace transport uint32_t GetCreationTime () const { return m_CreationTime; }; void SetCreationTime (uint32_t ts) { m_CreationTime = ts; }; // for introducers + uint64_t GetLastActivityTimestamp () const { return m_LastActivityTimestamp; }; + void SetLastActivityTimestamp (uint64_t ts) { m_LastActivityTimestamp = ts; }; + virtual uint32_t GetRelayTag () const { return 0; }; virtual void SendLocalRouterInfo (bool update = false) { SendI2NPMessages ({ CreateDatabaseStoreMsg () }); }; virtual void SendI2NPMessages (const std::vector >& msgs) = 0; virtual bool IsEstablished () const = 0; + private: + + void UpdateBandwidth () + { + int64_t interval = m_LastActivityTimestamp - m_LastBandwidthUpdateTimestamp; + if (interval < 0 || interval > 60*10) // 10 minutes + { + // clock was adjusted, copy new values + m_LastBandWidthUpdateNumSentBytes = m_NumSentBytes; + m_LastBandWidthUpdateNumReceivedBytes = m_NumReceivedBytes; + m_LastBandwidthUpdateTimestamp = m_LastActivityTimestamp; + return; + } + if ((uint64_t)interval > TRANSPORT_SESSION_BANDWIDTH_UPDATE_MIN_INTERVAL) + { + m_OutBandwidth = (m_NumSentBytes - m_LastBandWidthUpdateNumSentBytes)/interval; + m_LastBandWidthUpdateNumSentBytes = m_NumSentBytes; + m_InBandwidth = (m_NumReceivedBytes - m_LastBandWidthUpdateNumReceivedBytes)/interval; + m_LastBandWidthUpdateNumReceivedBytes = m_NumReceivedBytes; + m_LastBandwidthUpdateTimestamp = m_LastActivityTimestamp; + } + } + protected: std::shared_ptr m_RemoteIdentity; mutable std::mutex m_RemoteIdentityMutex; - size_t m_NumSentBytes, m_NumReceivedBytes, m_SendQueueSize; bool m_IsOutgoing; int m_TerminationTimeout; - uint64_t m_LastActivityTimestamp; uint32_t m_CreationTime; // seconds since epoch int64_t m_HandshakeInterval; // in milliseconds between SessionRequest->SessionCreated or SessionCreated->SessionConfirmed - }; - // SOCKS5 proxy - const uint8_t SOCKS5_VER = 0x05; - const uint8_t SOCKS5_CMD_CONNECT = 0x01; - const uint8_t SOCKS5_CMD_UDP_ASSOCIATE = 0x03; - const uint8_t SOCKS5_ATYP_IPV4 = 0x01; - const uint8_t SOCKS5_ATYP_IPV6 = 0x04; - const size_t SOCKS5_UDP_IPV4_REQUEST_HEADER_SIZE = 10; - const size_t SOCKS5_UDP_IPV6_REQUEST_HEADER_SIZE = 22; + private: + + size_t m_SendQueueSize, m_NumSentBytes, m_NumReceivedBytes, + m_LastBandWidthUpdateNumSentBytes, m_LastBandWidthUpdateNumReceivedBytes; + uint64_t m_LastActivityTimestamp, m_LastBandwidthUpdateTimestamp; + uint32_t m_InBandwidth, m_OutBandwidth; + }; } } diff --git a/libi2pd/Transports.cpp b/libi2pd/Transports.cpp index c2aeb964..04c58cf3 100644 --- a/libi2pd/Transports.cpp +++ b/libi2pd/Transports.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2023, The PurpleI2P Project +* Copyright (c) 2013-2024, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -140,10 +140,8 @@ namespace transport m_X25519KeysPairSupplier (15), // 15 pre-generated keys m_TotalSentBytes (0), m_TotalReceivedBytes (0), m_TotalTransitTransmittedBytes (0), m_InBandwidth (0), m_OutBandwidth (0), m_TransitBandwidth (0), - m_LastInBandwidthUpdateBytes (0), m_LastOutBandwidthUpdateBytes (0), m_LastTransitBandwidthUpdateBytes (0), m_InBandwidth15s (0), m_OutBandwidth15s (0), m_TransitBandwidth15s (0), - m_LastInBandwidth15sUpdateBytes (0), m_LastOutBandwidth15sUpdateBytes (0), m_LastTransitBandwidth15sUpdateBytes (0), - m_LastBandwidth15sUpdateTime (0) + m_InBandwidth5m (0), m_OutBandwidth5m (0), m_TransitBandwidth5m (0) { } @@ -306,6 +304,16 @@ namespace transport m_PeerCleanupTimer->expires_from_now (boost::posix_time::seconds(5 * SESSION_CREATION_TIMEOUT)); m_PeerCleanupTimer->async_wait (std::bind (&Transports::HandlePeerCleanupTimer, this, std::placeholders::_1)); + uint64_t ts = i2p::util::GetMillisecondsSinceEpoch(); + for (int i = 0; i < TRAFFIC_SAMPLE_COUNT; i++) + { + m_TrafficSamples[i].Timestamp = ts - (TRAFFIC_SAMPLE_COUNT - i - 1) * 1000; + m_TrafficSamples[i].TotalReceivedBytes = 0; + m_TrafficSamples[i].TotalSentBytes = 0; + m_TrafficSamples[i].TotalTransitTransmittedBytes = 0; + } + m_TrafficSamplePtr = TRAFFIC_SAMPLE_COUNT - 1; + m_UpdateBandwidthTimer->expires_from_now (boost::posix_time::seconds(1)); m_UpdateBandwidthTimer->async_wait (std::bind (&Transports::HandleUpdateBandwidthTimer, this, std::placeholders::_1)); @@ -364,51 +372,67 @@ namespace transport } } + void Transports::UpdateBandwidthValues(int interval, uint32_t& in, uint32_t& out, uint32_t& transit) + { + TrafficSample& sample1 = m_TrafficSamples[m_TrafficSamplePtr]; + TrafficSample& sample2 = m_TrafficSamples[(TRAFFIC_SAMPLE_COUNT + m_TrafficSamplePtr - interval) % TRAFFIC_SAMPLE_COUNT]; + auto delta = (int64_t)sample1.Timestamp - (int64_t)sample2.Timestamp; + if (delta <= 0) + { + LogPrint (eLogError, "Transports: Backward clock jump detected, got ", delta, " instead of ", interval * 1000); + return; + } + in = (sample1.TotalReceivedBytes - sample2.TotalReceivedBytes) * 1000 / delta; + out = (sample1.TotalSentBytes - sample2.TotalSentBytes) * 1000 / delta; + transit = (sample1.TotalTransitTransmittedBytes - sample2.TotalTransitTransmittedBytes) * 1000 / delta; + } + void Transports::HandleUpdateBandwidthTimer (const boost::system::error_code& ecode) { if (ecode != boost::asio::error::operation_aborted) { - uint64_t ts = i2p::util::GetMillisecondsSinceEpoch (); + m_TrafficSamplePtr++; + if (m_TrafficSamplePtr == TRAFFIC_SAMPLE_COUNT) + m_TrafficSamplePtr = 0; - // updated every second - m_InBandwidth = m_TotalReceivedBytes - m_LastInBandwidthUpdateBytes; - m_OutBandwidth = m_TotalSentBytes - m_LastOutBandwidthUpdateBytes; - m_TransitBandwidth = m_TotalTransitTransmittedBytes - m_LastTransitBandwidthUpdateBytes; + TrafficSample& sample = m_TrafficSamples[m_TrafficSamplePtr]; + sample.Timestamp = i2p::util::GetMillisecondsSinceEpoch(); + sample.TotalReceivedBytes = m_TotalReceivedBytes; + sample.TotalSentBytes = m_TotalSentBytes; + sample.TotalTransitTransmittedBytes = m_TotalTransitTransmittedBytes; - m_LastInBandwidthUpdateBytes = m_TotalReceivedBytes; - m_LastOutBandwidthUpdateBytes = m_TotalSentBytes; - m_LastTransitBandwidthUpdateBytes = m_TotalTransitTransmittedBytes; - - // updated every 15 seconds - auto delta = ts - m_LastBandwidth15sUpdateTime; - if (delta > 15 * 1000) - { - m_InBandwidth15s = (m_TotalReceivedBytes - m_LastInBandwidth15sUpdateBytes) * 1000 / delta; - m_OutBandwidth15s = (m_TotalSentBytes - m_LastOutBandwidth15sUpdateBytes) * 1000 / delta; - m_TransitBandwidth15s = (m_TotalTransitTransmittedBytes - m_LastTransitBandwidth15sUpdateBytes) * 1000 / delta; - - m_LastBandwidth15sUpdateTime = ts; - m_LastInBandwidth15sUpdateBytes = m_TotalReceivedBytes; - m_LastOutBandwidth15sUpdateBytes = m_TotalSentBytes; - m_LastTransitBandwidth15sUpdateBytes = m_TotalTransitTransmittedBytes; - } + UpdateBandwidthValues (1, m_InBandwidth, m_OutBandwidth, m_TransitBandwidth); + UpdateBandwidthValues (15, m_InBandwidth15s, m_OutBandwidth15s, m_TransitBandwidth15s); + UpdateBandwidthValues (300, m_InBandwidth5m, m_OutBandwidth5m, m_TransitBandwidth5m); m_UpdateBandwidthTimer->expires_from_now (boost::posix_time::seconds(1)); m_UpdateBandwidthTimer->async_wait (std::bind (&Transports::HandleUpdateBandwidthTimer, this, std::placeholders::_1)); } } - bool Transports::IsBandwidthExceeded () const + int Transports::GetCongestionLevel (bool longTerm) const { - auto limit = i2p::context.GetBandwidthLimit() * 1024; // convert to bytes - auto bw = std::max (m_InBandwidth15s, m_OutBandwidth15s); - return bw > limit; - } + auto bwLimit = i2p::context.GetBandwidthLimit () * 1024; // convert to bytes + auto tbwLimit = i2p::context.GetTransitBandwidthLimit () * 1024; // convert to bytes - bool Transports::IsTransitBandwidthExceeded () const - { - auto limit = i2p::context.GetTransitBandwidthLimit() * 1024; // convert to bytes - return m_TransitBandwidth > limit; + if (tbwLimit == 0 || bwLimit == 0) + return CONGESTION_LEVEL_FULL; + + uint32_t bw; + uint32_t tbw; + if (longTerm) + { + bw = std::max (m_InBandwidth5m, m_OutBandwidth5m); + tbw = m_TransitBandwidth5m; + } + else + { + bw = std::max (m_InBandwidth15s, m_OutBandwidth15s); + tbw = m_TransitBandwidth; + } + auto bwCongestionLevel = CONGESTION_LEVEL_FULL * bw / bwLimit; + auto tbwCongestionLevel = CONGESTION_LEVEL_FULL * tbw / tbwLimit; + return std::max (bwCongestionLevel, tbwCongestionLevel); } void Transports::SendMessage (const i2p::data::IdentHash& ident, std::shared_ptr msg) @@ -436,6 +460,10 @@ namespace transport auto it = m_Peers.find (ident); if (it == m_Peers.end ()) { + // check if not known as unreachable + auto profile = i2p::data::GetRouterProfile (ident); + if (profile && profile->IsUnreachable ()) return; // don't create peer to unreachable router + // try to connect bool connected = false; try { @@ -454,14 +482,29 @@ namespace transport } if (!connected) return; } - if (!it->second.sessions.empty ()) + if (it->second.IsConnected ()) it->second.sessions.front ()->SendI2NPMessages (msgs); else { - if (it->second.delayedMessages.size () < MAX_NUM_DELAYED_MESSAGES) + auto sz = it->second.delayedMessages.size (); + if (sz < MAX_NUM_DELAYED_MESSAGES) { + if (sz < CHECK_PROFILE_NUM_DELAYED_MESSAGES && sz + msgs.size () >= CHECK_PROFILE_NUM_DELAYED_MESSAGES) + { + auto profile = i2p::data::GetRouterProfile (ident); + if (profile && profile->IsUnreachable ()) + { + LogPrint (eLogWarning, "Transports: Peer profile for ", ident.ToBase64 (), " reports unreachable. Dropped"); + std::unique_lock l(m_PeersMutex); + m_Peers.erase (it); + return; + } + } for (auto& it1: msgs) - it->second.delayedMessages.push_back (it1); + if (sz > MAX_NUM_DELAYED_MESSAGES/2 && it1->onDrop) + it1->Drop (); // drop earlier because we can handle it + else + it->second.delayedMessages.push_back (it1); } else { @@ -493,7 +536,7 @@ namespace transport if (!m_NTCP2Server) continue; std::shared_ptr address = (tr == i2p::data::RouterInfo::eNTCP2V6) ? peer.router->GetPublishedNTCP2V6Address () : peer.router->GetPublishedNTCP2V4Address (); - if (address && m_CheckReserved && i2p::util::net::IsInReservedRange(address->host)) + if (address && IsInReservedRange(address->host)) address = nullptr; if (address) { @@ -512,7 +555,7 @@ namespace transport if (!m_SSU2Server) continue; std::shared_ptr address = (tr == i2p::data::RouterInfo::eSSU2V6) ? peer.router->GetSSU2V6Address () : peer.router->GetSSU2V4Address (); - if (address && m_CheckReserved && i2p::util::net::IsInReservedRange(address->host)) + if (address && IsInReservedRange(address->host)) address = nullptr; if (address && address->IsReachableSSU ()) { @@ -579,7 +622,7 @@ namespace transport peer.router->GetCompatibleTransports (true); peer.numAttempts = 0; peer.priority.clear (); - bool ssu2 = rand () & 1; + bool ssu2 = peer.router->GetProfile ()->IsReal () ? (rand () & 1) : false; // try NTCP2 if router is not confirmed real const auto& priority = ssu2 ? ssu2Priority : ntcp2Priority; for (auto transport: priority) if (transport & compatibleTransports) @@ -600,7 +643,8 @@ namespace transport { LogPrint (eLogDebug, "Transports: RouterInfo for ", ident.ToBase64 (), " found, trying to connect"); it->second.SetRouter (r); - ConnectToPeer (ident, it->second); + if (!it->second.IsConnected ()) + ConnectToPeer (ident, it->second); } else { @@ -633,14 +677,33 @@ namespace transport LogPrint (eLogInfo, "Transports: Started peer test IPv4"); std::set excluded; excluded.insert (i2p::context.GetIdentHash ()); // don't pick own router + int testDelay = 0; for (int i = 0; i < 5; i++) { auto router = i2p::data::netdb.GetRandomSSU2PeerTestRouter (true, excluded); // v4 if (router) { - if (i2p::context.GetStatus () != eRouterStatusTesting) - i2p::context.SetStatus (eRouterStatusTesting); - m_SSU2Server->StartPeerTest (router, true); + if (!i2p::context.GetTesting ()) + { + i2p::context.SetTesting (true); + // send first peer test immediately + m_SSU2Server->StartPeerTest (router, true); + } + else + { + testDelay += PEER_TEST_DELAY_INTERVAL + rand() % PEER_TEST_DELAY_INTERVAL_VARIANCE; + if (m_Service) + { + auto delayTimer = std::make_shared(*m_Service); + delayTimer->expires_from_now (boost::posix_time::milliseconds (testDelay)); + delayTimer->async_wait ( + [this, router, delayTimer](const boost::system::error_code& ecode) + { + if (ecode != boost::asio::error::operation_aborted) + m_SSU2Server->StartPeerTest (router, true); + }); + } + } excluded.insert (router->GetIdentHash ()); } } @@ -652,14 +715,33 @@ namespace transport LogPrint (eLogInfo, "Transports: Started peer test IPv6"); std::set excluded; excluded.insert (i2p::context.GetIdentHash ()); // don't pick own router + int testDelay = 0; for (int i = 0; i < 5; i++) { auto router = i2p::data::netdb.GetRandomSSU2PeerTestRouter (false, excluded); // v6 if (router) { - if (i2p::context.GetStatusV6 () != eRouterStatusTesting) - i2p::context.SetStatusV6 (eRouterStatusTesting); - m_SSU2Server->StartPeerTest (router, false); + if (!i2p::context.GetTestingV6 ()) + { + i2p::context.SetTestingV6 (true); + // send first peer test immediately + m_SSU2Server->StartPeerTest (router, false); + } + else + { + testDelay += PEER_TEST_DELAY_INTERVAL + rand() % PEER_TEST_DELAY_INTERVAL_VARIANCE; + if (m_Service) + { + auto delayTimer = std::make_shared(*m_Service); + delayTimer->expires_from_now (boost::posix_time::milliseconds (testDelay)); + delayTimer->async_wait ( + [this, router, delayTimer](const boost::system::error_code& ecode) + { + if (ecode != boost::asio::error::operation_aborted) + m_SSU2Server->StartPeerTest (router, false); + }); + } + } excluded.insert (router->GetIdentHash ()); } } @@ -688,6 +770,25 @@ namespace transport auto it = m_Peers.find (ident); if (it != m_Peers.end ()) { + if (it->second.numAttempts > 1) + { + // exclude failed transports + i2p::data::RouterInfo::CompatibleTransports transports = 0; + int numExcluded = it->second.numAttempts - 1; + if (numExcluded > (int)it->second.priority.size ()) numExcluded = it->second.priority.size (); + for (int i = 0; i < numExcluded; i++) + transports |= it->second.priority[i]; + i2p::data::netdb.ExcludeReachableTransports (ident, transports); + } + if (it->second.router && it->second.numAttempts) + { + auto transport = it->second.priority[it->second.numAttempts-1]; + if (transport == i2p::data::RouterInfo::eNTCP2V4 || + transport == i2p::data::RouterInfo::eNTCP2V6 || transport == i2p::data::RouterInfo::eNTCP2V6Mesh) + it->second.router->GetProfile ()->Connected (); // outgoing NTCP2 connection if always real + i2p::data::netdb.SetUnreachable (ident, false); // clear unreachable + } + it->second.numAttempts = 0; it->second.router = nullptr; // we don't need RouterInfo after successive connect bool sendDatabaseStore = true; if (it->second.delayedMessages.size () > 0) @@ -706,7 +807,7 @@ namespace transport session->SendI2NPMessages (it->second.delayedMessages); it->second.delayedMessages.clear (); } - else // incoming connection + else // incoming connection or peer test { if(RoutesRestricted() && ! IsRestrictedPeer(ident)) { // not trusted @@ -714,11 +815,15 @@ namespace transport session->Done(); return; } - session->SendI2NPMessages ({ CreateDatabaseStoreMsg () }); // send DatabaseStore + if (!session->IsOutgoing ()) // incoming + session->SendI2NPMessages ({ CreateDatabaseStoreMsg () }); // send DatabaseStore + auto r = i2p::data::netdb.FindRouter (ident); // router should be in netdb after SessionConfirmed + if (r) r->GetProfile ()->Connected (); auto ts = i2p::util::GetSecondsSinceEpoch (); std::unique_lock l(m_PeersMutex); - auto it = m_Peers.insert (std::make_pair (ident, Peer{ nullptr, ts })).first; + auto it = m_Peers.insert (std::make_pair (ident, Peer{ r, ts })).first; it->second.sessions.push_back (session); + it->second.router = nullptr; } }); } @@ -733,13 +838,13 @@ namespace transport auto it = m_Peers.find (ident); if (it != m_Peers.end ()) { - auto before = it->second.sessions.size (); + bool wasConnected = it->second.IsConnected (); it->second.sessions.remove (session); - if (it->second.sessions.empty ()) + if (!it->second.IsConnected ()) { if (it->second.delayedMessages.size () > 0) { - if (before > 0) // we had an active session before + if (wasConnected) // we had an active session before it->second.numAttempts = 0; // start over ConnectToPeer (ident, it->second); } @@ -772,14 +877,15 @@ namespace transport { return !session || !session->IsEstablished (); }); - if (it->second.sessions.empty () && ts > it->second.creationTime + SESSION_CREATION_TIMEOUT) + if (!it->second.IsConnected () && ts > it->second.creationTime + SESSION_CREATION_TIMEOUT) { LogPrint (eLogWarning, "Transports: Session to peer ", it->first.ToBase64 (), " has not been created in ", SESSION_CREATION_TIMEOUT, " seconds"); - auto profile = i2p::data::GetRouterProfile(it->first); - if (profile) - { - profile->TunnelNonReplied(); - } + /* if (!it->second.router) + { + // if router for ident not found mark it unreachable + auto profile = i2p::data::GetRouterProfile (it->first); + if (profile) profile->Unreachable (); + } */ std::unique_lock l(m_PeersMutex); it = m_Peers.erase (it); } @@ -796,9 +902,13 @@ namespace transport ++it; } } - bool ipv4Testing = i2p::context.GetStatus () == eRouterStatusTesting; - bool ipv6Testing = i2p::context.GetStatusV6 () == eRouterStatusTesting; - // if still testing, repeat peer test + bool ipv4Testing = i2p::context.GetTesting (); + if (!ipv4Testing) + ipv4Testing = i2p::context.GetRouterInfo ().IsSSU2V4 () && (i2p::context.GetStatus() == eRouterStatusUnknown); + bool ipv6Testing = i2p::context.GetTestingV6 (); + if (!ipv6Testing) + ipv6Testing = i2p::context.GetRouterInfo ().IsSSU2V6 () && (i2p::context.GetStatusV6() == eRouterStatusUnknown); + // if still testing or unknown, repeat peer test if (ipv4Testing || ipv6Testing) PeerTest (ipv4Testing, ipv6Testing); m_PeerCleanupTimer->expires_from_now (boost::posix_time::seconds(3 * SESSION_CREATION_TIMEOUT)); @@ -826,7 +936,9 @@ namespace transport uint16_t inds[3]; RAND_bytes ((uint8_t *)inds, sizeof (inds)); std::unique_lock l(m_PeersMutex); - inds[0] %= m_Peers.size (); + auto count = m_Peers.size (); + if(count == 0) return nullptr; + inds[0] %= count; auto it = m_Peers.begin (); std::advance (it, inds[0]); // try random peer @@ -907,9 +1019,9 @@ namespace transport [isHighBandwidth](const Peer& peer)->bool { // connected, not overloaded and not slow - return !peer.router && !peer.sessions.empty () && peer.isReachable && + return !peer.router && peer.IsConnected () && peer.isReachable && peer.sessions.front ()->GetSendQueueSize () <= PEER_ROUTER_INFO_OVERLOAD_QUEUE_SIZE && - !peer.sessions.front ()->IsSlow () && + !peer.sessions.front ()->IsSlow () && !peer.sessions.front ()->IsBandwidthExceeded (peer.isHighBandwidth) && (!isHighBandwidth || peer.isHighBandwidth); }); } @@ -927,18 +1039,25 @@ namespace transport } } - void Transports::RestrictRoutesToRouters(std::set routers) + void Transports::RestrictRoutesToRouters(const std::set& routers) { - std::unique_lock lock(m_TrustedRoutersMutex); + std::lock_guard lock(m_TrustedRoutersMutex); m_TrustedRouters.clear(); for (const auto & ri : routers ) m_TrustedRouters.push_back(ri); } - bool Transports::RoutesRestricted() const { - std::unique_lock famlock(m_FamilyMutex); - std::unique_lock routerslock(m_TrustedRoutersMutex); - return m_TrustedFamilies.size() > 0 || m_TrustedRouters.size() > 0; + bool Transports::RoutesRestricted() const + { + { + std::lock_guard routerslock(m_TrustedRoutersMutex); + if (!m_TrustedRouters.empty ()) return true; + } + { + std::lock_guard famlock(m_FamilyMutex); + if (!m_TrustedFamilies.empty ()) return true; + } + return false; } /** XXX: if routes are not restricted this dies */ @@ -962,7 +1081,7 @@ namespace transport return i2p::data::netdb.GetRandomRouterInFamily(fam); } { - std::unique_lock l(m_TrustedRoutersMutex); + std::lock_guard l(m_TrustedRoutersMutex); auto sz = m_TrustedRouters.size(); if (sz) { @@ -979,12 +1098,12 @@ namespace transport bool Transports::IsRestrictedPeer(const i2p::data::IdentHash & ih) const { { - std::unique_lock l(m_TrustedRoutersMutex); + std::lock_guard l(m_TrustedRoutersMutex); for (const auto & r : m_TrustedRouters ) if ( r == ih ) return true; } { - std::unique_lock l(m_FamilyMutex); + std::lock_guard l(m_FamilyMutex); auto ri = i2p::data::netdb.FindRouter(ih); for (const auto & fam : m_TrustedFamilies) if(ri->IsFamily(fam)) return true; @@ -1004,6 +1123,11 @@ namespace transport } } + bool Transports::IsInReservedRange (const boost::asio::ip::address& host) const + { + return IsCheckReserved () && i2p::util::net::IsInReservedRange (host); + } + void InitAddressFromIface () { bool ipv6; i2p::config::GetOption("ipv6", ipv6); @@ -1115,7 +1239,6 @@ namespace transport else i2p::context.PublishSSU2Address (ssu2port, false, ipv4, ipv6); // unpublish } - } } } diff --git a/libi2pd/Transports.h b/libi2pd/Transports.h index 1058d31b..92d75efd 100644 --- a/libi2pd/Transports.h +++ b/libi2pd/Transports.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2023, The PurpleI2P Project +* Copyright (c) 2013-2024, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -85,11 +85,14 @@ namespace transport isReachable = (bool)router->GetCompatibleTransports (true); } } - + void Done () { for (auto& it: sessions) it->Done (); + // drop not sent delayed messages + for (auto& it: delayedMessages) + it->Drop (); } void SetRouter (std::shared_ptr r) @@ -101,11 +104,27 @@ namespace transport isReachable = (bool)router->GetCompatibleTransports (true); } } + + bool IsConnected () const { return !sessions.empty (); } }; const uint64_t SESSION_CREATION_TIMEOUT = 15; // in seconds const int PEER_TEST_INTERVAL = 71; // in minutes + const int PEER_TEST_DELAY_INTERVAL = 20; // in milliseconds + const int PEER_TEST_DELAY_INTERVAL_VARIANCE = 30; // in milliseconds const int MAX_NUM_DELAYED_MESSAGES = 150; + const int CHECK_PROFILE_NUM_DELAYED_MESSAGES = 15; // check profile after + + const int TRAFFIC_SAMPLE_COUNT = 301; // seconds + + struct TrafficSample + { + uint64_t Timestamp; + uint64_t TotalReceivedBytes; + uint64_t TotalSentBytes; + uint64_t TotalTransitTransmittedBytes; + }; + class Transports { public: @@ -145,8 +164,7 @@ namespace transport uint32_t GetInBandwidth15s () const { return m_InBandwidth15s; }; uint32_t GetOutBandwidth15s () const { return m_OutBandwidth15s; }; uint32_t GetTransitBandwidth15s () const { return m_TransitBandwidth15s; }; - bool IsBandwidthExceeded () const; - bool IsTransitBandwidthExceeded () const; + int GetCongestionLevel (bool longTerm) const; size_t GetNumPeers () const { return m_Peers.size (); }; std::shared_ptr GetRandomPeer (bool isHighBandwidth) const; @@ -157,14 +175,15 @@ namespace transport /** restrict routes to use only these router families for first hops */ void RestrictRoutesToFamilies(const std::set& families); /** restrict routes to use only these routers for first hops */ - void RestrictRoutesToRouters(std::set routers); + void RestrictRoutesToRouters(const std::set& routers); bool IsRestrictedPeer(const i2p::data::IdentHash & ident) const; void PeerTest (bool ipv4 = true, bool ipv6 = true); void SetCheckReserved (bool check) { m_CheckReserved = check; }; - bool IsCheckReserved () { return m_CheckReserved; }; + bool IsCheckReserved () const { return m_CheckReserved; }; + bool IsInReservedRange (const boost::asio::ip::address& host) const; private: @@ -177,6 +196,7 @@ namespace transport void HandlePeerCleanupTimer (const boost::system::error_code& ecode); void HandlePeerTestTimer (const boost::system::error_code& ecode); void HandleUpdateBandwidthTimer (const boost::system::error_code& ecode); + void UpdateBandwidthValues (int interval, uint32_t& in, uint32_t& out, uint32_t& transit); void DetectExternalIP (); @@ -201,14 +221,15 @@ namespace transport std::atomic m_TotalSentBytes, m_TotalReceivedBytes, m_TotalTransitTransmittedBytes; + TrafficSample m_TrafficSamples[TRAFFIC_SAMPLE_COUNT]; + int m_TrafficSamplePtr; + // Bandwidth per second uint32_t m_InBandwidth, m_OutBandwidth, m_TransitBandwidth; - uint64_t m_LastInBandwidthUpdateBytes, m_LastOutBandwidthUpdateBytes, m_LastTransitBandwidthUpdateBytes; - - // Bandwidth every 15 seconds + // Bandwidth during last 15 seconds uint32_t m_InBandwidth15s, m_OutBandwidth15s, m_TransitBandwidth15s; - uint64_t m_LastInBandwidth15sUpdateBytes, m_LastOutBandwidth15sUpdateBytes, m_LastTransitBandwidth15sUpdateBytes; - uint64_t m_LastBandwidth15sUpdateTime; + // Bandwidth during last 5 minutes + uint32_t m_InBandwidth5m, m_OutBandwidth5m, m_TransitBandwidth5m; /** which router families to trust for first hops */ std::vector m_TrustedFamilies; diff --git a/libi2pd/Tunnel.cpp b/libi2pd/Tunnel.cpp index 05359390..246f68e4 100644 --- a/libi2pd/Tunnel.cpp +++ b/libi2pd/Tunnel.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2023, The PurpleI2P Project +* Copyright (c) 2013-2024, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -52,7 +52,7 @@ namespace tunnel // shuffle records std::vector recordIndicies; for (int i = 0; i < numRecords; i++) recordIndicies.push_back(i); - std::shuffle (recordIndicies.begin(), recordIndicies.end(), std::mt19937(std::random_device()())); + std::shuffle (recordIndicies.begin(), recordIndicies.end(), m_Pool ? m_Pool->GetRng () : std::mt19937(std::random_device()())); // create real records uint8_t * records = msg->GetPayload () + 1; @@ -90,7 +90,13 @@ namespace tunnel hop = hop->prev; } msg->FillI2NPMessageHeader (m_Config->IsShort () ? eI2NPShortTunnelBuild : eI2NPVariableTunnelBuild); - + auto s = shared_from_this (); + msg->onDrop = [s]() + { + LogPrint (eLogInfo, "I2NP: Tunnel ", s->GetTunnelID (), " request was not sent"); + s->SetState (i2p::tunnel::eTunnelStateBuildFailed); + }; + // send message if (outboundTunnel) { @@ -116,7 +122,7 @@ namespace tunnel if (m_Pool && m_Pool->GetLocalDestination ()) m_Pool->GetLocalDestination ()->SubmitECIESx25519Key (key, tag); else - i2p::context.AddECIESx25519Key (key, tag); + i2p::context.SubmitECIESx25519Key (key, tag); } i2p::transport::transports.SendMessage (GetNextIdentHash (), msg); } @@ -244,9 +250,9 @@ namespace tunnel void InboundTunnel::HandleTunnelDataMsg (std::shared_ptr&& msg) { - if (IsFailed ()) SetState (eTunnelStateEstablished); // incoming messages means a tunnel is alive + if (GetState () != eTunnelStateExpiring) SetState (eTunnelStateEstablished); // incoming messages means a tunnel is alive EncryptTunnelMsg (msg, msg); - msg->from = shared_from_this (); + msg->from = GetSharedFromThis (); m_Endpoint.HandleDecryptedTunnelDataMsg (msg); } @@ -261,7 +267,7 @@ namespace tunnel if (msg) { m_NumReceivedBytes += msg->GetLength (); - msg->from = shared_from_this (); + msg->from = GetSharedFromThis (); HandleI2NPMessage (msg); } } @@ -516,7 +522,7 @@ namespace tunnel case eI2NPShortTunnelBuildReply: case eI2NPTunnelBuild: case eI2NPTunnelBuildReply: - HandleI2NPMessage (msg->GetBuffer (), msg->GetLength ()); + HandleTunnelBuildI2NPMessage (msg); break; default: LogPrint (eLogWarning, "Tunnel: Unexpected message type ", (int) typeID); @@ -586,10 +592,17 @@ namespace tunnel auto typeID = msg->GetTypeID (); LogPrint (eLogDebug, "Tunnel: Gateway of ", (int) len, " bytes for tunnel ", tunnel->GetTunnelID (), ", msg type ", (int)typeID); - if (IsRouterInfoMsg (msg) || typeID == eI2NPDatabaseSearchReply) - // transit DatabaseStore my contain new/updated RI - // or DatabaseSearchReply with new routers + if (typeID == eI2NPDatabaseSearchReply) + // DatabaseSearchReply with new routers i2p::data::netdb.PostI2NPMsg (CopyI2NPMessage (msg)); + else if (IsRouterInfoMsg (msg)) + { + // transit DatabaseStore might contain new/updated RI + auto m = CopyI2NPMessage (msg); + if (bufbe32toh (m->GetPayload () + DATABASE_STORE_REPLY_TOKEN_OFFSET)) + memset (m->GetPayload () + DATABASE_STORE_REPLY_TOKEN_OFFSET, 0xFF, 4); // fake replyToken meaning no reply + i2p::data::netdb.PostI2NPMsg (m); + } tunnel->SendTunnelDataMsg (msg); } @@ -666,9 +679,10 @@ namespace tunnel for (auto it = m_OutboundTunnels.begin (); it != m_OutboundTunnels.end ();) { auto tunnel = *it; - if (ts > tunnel->GetCreationTime () + TUNNEL_EXPIRATION_TIMEOUT) + if (tunnel->IsFailed () || ts > tunnel->GetCreationTime () + TUNNEL_EXPIRATION_TIMEOUT || + ts + TUNNEL_EXPIRATION_TIMEOUT < tunnel->GetCreationTime ()) { - LogPrint (eLogDebug, "Tunnel: Tunnel with id ", tunnel->GetTunnelID (), " expired"); + LogPrint (eLogDebug, "Tunnel: Tunnel with id ", tunnel->GetTunnelID (), " expired or failed"); auto pool = tunnel->GetTunnelPool (); if (pool) pool->TunnelExpired (tunnel); @@ -702,7 +716,7 @@ namespace tunnel auto inboundTunnel = GetNextInboundTunnel (); auto router = i2p::transport::transports.RoutesRestricted() ? i2p::transport::transports.GetRestrictedPeer() : - i2p::data::netdb.GetRandomRouter (i2p::context.GetSharedRouterInfo (), false); // reachable by us + i2p::data::netdb.GetRandomRouter (i2p::context.GetSharedRouterInfo (), false, true); // reachable by us if (!inboundTunnel || !router) return; LogPrint (eLogDebug, "Tunnel: Creating one hop outbound tunnel"); CreateTunnel ( @@ -717,10 +731,10 @@ namespace tunnel for (auto it = m_InboundTunnels.begin (); it != m_InboundTunnels.end ();) { auto tunnel = *it; - if (ts > tunnel->GetCreationTime () + TUNNEL_EXPIRATION_TIMEOUT || + if (tunnel->IsFailed () || ts > tunnel->GetCreationTime () + TUNNEL_EXPIRATION_TIMEOUT || ts + TUNNEL_EXPIRATION_TIMEOUT < tunnel->GetCreationTime ()) { - LogPrint (eLogDebug, "Tunnel: Tunnel with id ", tunnel->GetTunnelID (), " expired"); + LogPrint (eLogDebug, "Tunnel: Tunnel with id ", tunnel->GetTunnelID (), " expired or failed"); auto pool = tunnel->GetTunnelPool (); if (pool) pool->TunnelExpired (tunnel); @@ -774,7 +788,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); + i2p::data::netdb.GetRandomRouter (i2p::context.GetSharedRouterInfo (), false, true); if (!router) { LogPrint (eLogWarning, "Tunnel: Can't find any router, skip creating tunnel"); return; @@ -972,13 +986,13 @@ namespace tunnel return m_OutboundTunnels.size(); } - void Tunnels::SetMaxNumTransitTunnels (uint16_t maxNumTransitTunnels) + void Tunnels::SetMaxNumTransitTunnels (uint32_t maxNumTransitTunnels) { if (maxNumTransitTunnels > 0 && m_MaxNumTransitTunnels != maxNumTransitTunnels) { LogPrint (eLogDebug, "Tunnel: Max number of transit tunnels set to ", maxNumTransitTunnels); m_MaxNumTransitTunnels = maxNumTransitTunnels; } - } + } } } diff --git a/libi2pd/Tunnel.h b/libi2pd/Tunnel.h index 5810a7a8..4d9ea830 100644 --- a/libi2pd/Tunnel.h +++ b/libi2pd/Tunnel.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2023, The PurpleI2P Project +* Copyright (c) 2013-2024, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -45,7 +45,7 @@ namespace tunnel const int TUNNEL_MANAGE_INTERVAL = 15; // in seconds const int TUNNEL_POOLS_MANAGE_INTERVAL = 5; // in seconds const int TUNNEL_MEMORY_POOL_MANAGE_INTERVAL = 120; // in seconds - + const size_t I2NP_TUNNEL_MESSAGE_SIZE = TUNNEL_DATA_MSG_SIZE + I2NP_HEADER_SIZE + 34; // reserved for alignment and NTCP 16 + 6 + 12 const size_t I2NP_TUNNEL_ENPOINT_MESSAGE_SIZE = 2*TUNNEL_DATA_MSG_SIZE + I2NP_HEADER_SIZE + TUNNEL_GATEWAY_HEADER_SIZE + 28; // reserved for alignment and NTCP 16 + 6 + 6 @@ -65,7 +65,8 @@ namespace tunnel class OutboundTunnel; class InboundTunnel; - class Tunnel: public TunnelBase + class Tunnel: public TunnelBase, + public std::enable_shared_from_this { struct TunnelHop { @@ -90,7 +91,7 @@ namespace tunnel i2p::data::RouterInfo::CompatibleTransports GetFarEndTransports () const { return m_FarEndTransports; }; TunnelState GetState () const { return m_State; }; void SetState (TunnelState state); - bool IsEstablished () const { return m_State == eTunnelStateEstablished; }; + bool IsEstablished () const { return m_State == eTunnelStateEstablished || m_State == eTunnelStateTestFailed; }; bool IsFailed () const { return m_State == eTunnelStateFailed; }; bool IsRecreated () const { return m_IsRecreated; }; void SetRecreated (bool recreated) { m_IsRecreated = recreated; }; @@ -155,7 +156,7 @@ namespace tunnel i2p::data::IdentHash m_EndpointIdentHash; }; - class InboundTunnel: public Tunnel, public std::enable_shared_from_this + class InboundTunnel: public Tunnel { public: @@ -167,6 +168,13 @@ namespace tunnel // override TunnelBase void Cleanup () override { m_Endpoint.Cleanup (); }; + protected: + + std::shared_ptr GetSharedFromThis () + { + return std::static_pointer_cast(shared_from_this ()); + } + private: TunnelEndpoint m_Endpoint; @@ -230,10 +238,10 @@ namespace tunnel std::shared_ptr NewI2NPTunnelMessage (bool endpoint); - void SetMaxNumTransitTunnels (uint16_t maxNumTransitTunnels); - uint16_t GetMaxNumTransitTunnels () const { return m_MaxNumTransitTunnels; }; - bool IsTooManyTransitTunnels () const { return m_TransitTunnels.size () >= m_MaxNumTransitTunnels; }; - + void SetMaxNumTransitTunnels (uint32_t maxNumTransitTunnels); + uint32_t GetMaxNumTransitTunnels () const { return m_MaxNumTransitTunnels; }; + int GetCongestionLevel() const { return m_MaxNumTransitTunnels ? CONGESTION_LEVEL_FULL * m_TransitTunnels.size() / m_MaxNumTransitTunnels : CONGESTION_LEVEL_FULL; } + private: template @@ -292,7 +300,7 @@ namespace tunnel i2p::util::Queue > m_Queue; i2p::util::MemoryPoolMt > m_I2NPTunnelEndpointMessagesMemoryPool; i2p::util::MemoryPoolMt > m_I2NPTunnelMessagesMemoryPool; - uint16_t m_MaxNumTransitTunnels; + uint32_t m_MaxNumTransitTunnels; // count of tunnels for total TCSR algorithm int m_TotalNumSuccesiveTunnelCreations, m_TotalNumFailedTunnelCreations; double m_TunnelCreationSuccessRate; @@ -311,6 +319,7 @@ namespace tunnel int GetQueueSize () { return m_Queue.GetSize (); }; int GetTunnelCreationSuccessRate () const { return std::round(m_TunnelCreationSuccessRate * 100); } // in percents + double GetPreciseTunnelCreationSuccessRate () const { return m_TunnelCreationSuccessRate * 100; } // in percents int GetTotalTunnelCreationSuccessRate () const // in percents { int totalNum = m_TotalNumSuccesiveTunnelCreations + m_TotalNumFailedTunnelCreations; diff --git a/libi2pd/TunnelEndpoint.cpp b/libi2pd/TunnelEndpoint.cpp index 7d3b3cbe..3dc0dc07 100644 --- a/libi2pd/TunnelEndpoint.cpp +++ b/libi2pd/TunnelEndpoint.cpp @@ -323,11 +323,7 @@ namespace tunnel } uint8_t typeID = msg.data->GetTypeID (); LogPrint (eLogDebug, "TunnelMessage: Handle fragment of ", msg.data->GetLength (), " bytes, msg type ", (int)typeID); - // catch RI or reply with new list of routers - if ((IsRouterInfoMsg (msg.data) || typeID == eI2NPDatabaseSearchReply) && - !m_IsInbound && msg.deliveryType != eDeliveryTypeLocal) - i2p::data::netdb.PostI2NPMsg (CopyI2NPMessage (msg.data)); - + switch (msg.deliveryType) { case eDeliveryTypeLocal: diff --git a/libi2pd/TunnelGateway.cpp b/libi2pd/TunnelGateway.cpp index 12e7652f..85ff224e 100644 --- a/libi2pd/TunnelGateway.cpp +++ b/libi2pd/TunnelGateway.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2021, The PurpleI2P Project +* Copyright (c) 2013-2024, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -35,6 +35,13 @@ namespace tunnel if (!m_CurrentTunnelDataMsg) { CreateCurrentTunnelDataMessage (); + if (block.data && block.data->onDrop) + { + // onDrop is called for the first fragment in tunnel message + // that's usually true for short TBMs or lookups + m_CurrentTunnelDataMsg->onDrop = block.data->onDrop; + block.data->onDrop = nullptr; + } messageCreated = true; } @@ -155,7 +162,6 @@ namespace tunnel void TunnelGatewayBuffer::CreateCurrentTunnelDataMessage () { - m_CurrentTunnelDataMsg = nullptr; m_CurrentTunnelDataMsg = NewI2NPTunnelMessage (true); // tunnel endpoint is at least of two tunnel messages size // we reserve space for padding m_CurrentTunnelDataMsg->offset += TUNNEL_DATA_MSG_SIZE + I2NP_HEADER_SIZE; @@ -223,6 +229,7 @@ namespace tunnel m_Tunnel->EncryptTunnelMsg (tunnelMsg, newMsg); htobe32buf (newMsg->GetPayload (), m_Tunnel->GetNextTunnelID ()); newMsg->FillI2NPMessageHeader (eI2NPTunnelData); + if (tunnelMsg->onDrop) newMsg->onDrop = tunnelMsg->onDrop; newTunnelMsgs.push_back (newMsg); m_NumSentBytes += TUNNEL_DATA_MSG_SIZE; } diff --git a/libi2pd/TunnelPool.cpp b/libi2pd/TunnelPool.cpp index cf146218..2a5e9dd1 100644 --- a/libi2pd/TunnelPool.cpp +++ b/libi2pd/TunnelPool.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2023, The PurpleI2P Project +* Copyright (c) 2013-2024, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -7,13 +7,13 @@ */ #include -#include #include "I2PEndian.h" #include "Crypto.h" #include "Tunnel.h" #include "NetDb.hpp" #include "Timestamp.h" #include "Garlic.h" +#include "ECIESX25519AEADRatchetSession.h" #include "Transports.h" #include "Log.h" #include "Tunnel.h" @@ -45,7 +45,7 @@ namespace tunnel 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_CustomPeerSelector(nullptr), m_Rng(m_Rd()) { if (m_NumInboundTunnels > TUNNEL_POOL_MAX_INBOUND_TUNNELS_QUANTITY) m_NumInboundTunnels = TUNNEL_POOL_MAX_INBOUND_TUNNELS_QUANTITY; @@ -296,10 +296,12 @@ namespace tunnel for (const auto& it : m_InboundTunnels) if (it->IsEstablished ()) num++; } - if (!num && !m_OutboundTunnels.empty () && m_NumOutboundHops > 0) + if (!num && !m_OutboundTunnels.empty () && m_NumOutboundHops > 0 && + m_NumInboundHops == m_NumOutboundHops) { for (auto it: m_OutboundTunnels) { + // try to create inbound tunnel through the same path as successive outbound CreatePairedInboundTunnel (it); num++; if (num >= m_NumInboundTunnels) break; @@ -335,7 +337,10 @@ namespace tunnel { it.second.first->SetState (eTunnelStateFailed); std::unique_lock l(m_OutboundTunnelsMutex); - m_OutboundTunnels.erase (it.second.first); + if (m_OutboundTunnels.size () > 1 || m_NumOutboundTunnels <= 1) // don't fail last tunnel + m_OutboundTunnels.erase (it.second.first); + else + it.second.first->SetState (eTunnelStateTestFailed); } else it.second.first->SetState (eTunnelStateTestFailed); @@ -347,7 +352,10 @@ namespace tunnel it.second.second->SetState (eTunnelStateFailed); { std::unique_lock l(m_InboundTunnelsMutex); - m_InboundTunnels.erase (it.second.second); + if (m_InboundTunnels.size () > 1 || m_NumInboundTunnels <= 1) // don't fail last tunnel + m_InboundTunnels.erase (it.second.second); + else + it.second.second->SetState (eTunnelStateTestFailed); } if (m_LocalDestination) m_LocalDestination->SetLeaseSetUpdated (); @@ -358,36 +366,65 @@ namespace tunnel } // new tests - std::unique_lock l1(m_OutboundTunnelsMutex); - auto it1 = m_OutboundTunnels.begin (); - std::unique_lock l2(m_InboundTunnelsMutex); - auto it2 = m_InboundTunnels.begin (); - while (it1 != m_OutboundTunnels.end () && it2 != m_InboundTunnels.end ()) + std::vector, std::shared_ptr > > newTests; + std::vector > outboundTunnels; { - bool failed = false; - if ((*it1)->IsFailed ()) - { - failed = true; - ++it1; - } - if ((*it2)->IsFailed ()) - { - failed = true; - ++it2; - } - if (!failed) - { - uint32_t msgID; - RAND_bytes ((uint8_t *)&msgID, 4); - { - std::unique_lock l(m_TestsMutex); - m_Tests[msgID] = std::make_pair (*it1, *it2); - } - (*it1)->SendTunnelDataMsgTo ((*it2)->GetNextIdentHash (), (*it2)->GetNextTunnelID (), - CreateDeliveryStatusMsg (msgID)); - ++it1; ++it2; - } + std::unique_lock l(m_OutboundTunnelsMutex); + for (auto& it: m_OutboundTunnels) + if (it->IsEstablished () || it->GetState () == eTunnelStateTestFailed) + outboundTunnels.push_back (it); } + std::shuffle (outboundTunnels.begin(), outboundTunnels.end(), m_Rng); + std::vector > inboundTunnels; + { + std::unique_lock l(m_InboundTunnelsMutex); + for (auto& it: m_InboundTunnels) + if (it->IsEstablished () || it->GetState () == eTunnelStateTestFailed) + inboundTunnels.push_back (it); + } + std::shuffle (inboundTunnels.begin(), inboundTunnels.end(), m_Rng); + auto it1 = outboundTunnels.begin (); + auto it2 = inboundTunnels.begin (); + while (it1 != outboundTunnels.end () && it2 != inboundTunnels.end ()) + { + newTests.push_back(std::make_pair (*it1, *it2)); + ++it1; ++it2; + } + bool encrypt = m_LocalDestination ? m_LocalDestination->SupportsEncryptionType (i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD) : false; + for (auto& it: newTests) + { + uint32_t msgID; + RAND_bytes ((uint8_t *)&msgID, 4); + { + std::unique_lock l(m_TestsMutex); + m_Tests[msgID] = it; + } + auto msg = CreateDeliveryStatusMsg (msgID); + auto outbound = it.first; + auto s = shared_from_this (); + msg->onDrop = [msgID, outbound, s]() + { + // if test msg dropped locally it's outbound tunnel to blame + outbound->SetState (eTunnelStateFailed); + { + std::unique_lock l(s->m_TestsMutex); + s->m_Tests.erase (msgID); + } + { + std::unique_lock l(s->m_OutboundTunnelsMutex); + s->m_OutboundTunnels.erase (outbound); + } + }; + if (encrypt) + { + // encrypt + uint8_t key[32]; RAND_bytes (key, 32); + uint64_t tag; RAND_bytes ((uint8_t *)&tag, 8); + m_LocalDestination->SubmitECIESx25519Key (key, tag); + msg = i2p::garlic::WrapECIESX25519Message (msg, key, tag); + } + outbound->SendTunnelDataMsgTo (it.second->GetNextIdentHash (), it.second->GetNextTunnelID (), msg); + } } void TunnelPool::ManageTunnels (uint64_t ts) @@ -415,6 +452,17 @@ namespace tunnel buf += 4; uint64_t timestamp = bufbe64toh (buf); + if (!ProcessDeliveryStatus (msgID, timestamp)) + { + if (m_LocalDestination) + m_LocalDestination->ProcessDeliveryStatusMessage (msg); + else + LogPrint (eLogWarning, "Tunnels: Local destination doesn't exist, dropped"); + } + } + + bool TunnelPool::ProcessDeliveryStatus (uint32_t msgID, uint64_t timestamp) + { decltype(m_Tests)::mapped_type test; bool found = false; { @@ -437,7 +485,7 @@ namespace tunnel // restore from test failed state if any if (test.first) { - if (test.first->GetState () == eTunnelStateTestFailed) + if (test.first->GetState () != eTunnelStateExpiring) test.first->SetState (eTunnelStateEstablished); // update latency uint64_t latency = 0; @@ -447,7 +495,7 @@ namespace tunnel } if (test.second) { - if (test.second->GetState () == eTunnelStateTestFailed) + if (test.second->GetState () != eTunnelStateExpiring) test.second->SetState (eTunnelStateEstablished); // update latency uint64_t latency = 0; @@ -456,27 +504,34 @@ namespace tunnel test.second->AddLatencySample(latency); } } - else - { - if (m_LocalDestination) - m_LocalDestination->ProcessDeliveryStatusMessage (msg); - else - LogPrint (eLogWarning, "Tunnels: Local destination doesn't exist, dropped"); - } - } - + return found; + } + bool TunnelPool::IsExploratory () const { return i2p::tunnel::tunnels.GetExploratoryPool () == shared_from_this (); } - std::shared_ptr TunnelPool::SelectNextHop (std::shared_ptr prevHop, bool reverse) const + std::shared_ptr TunnelPool::SelectNextHop (std::shared_ptr prevHop, + bool reverse, bool endpoint) const { - auto hop = IsExploratory () ? i2p::data::netdb.GetRandomRouter (prevHop, reverse): - i2p::data::netdb.GetHighBandwidthRandomRouter (prevHop, reverse); - - if (!hop || hop->GetProfile ()->IsBad ()) - hop = i2p::data::netdb.GetRandomRouter (prevHop, reverse); + bool tryHighBandwidth = !IsExploratory (); + std::shared_ptr 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); + if (hop) + { + if (!hop->GetProfile ()->IsBad ()) + break; + } + else if (tryHighBandwidth) + tryHighBandwidth = false; + else + return nullptr; + } return hop; } @@ -498,7 +553,7 @@ namespace tunnel { auto r = i2p::transport::transports.GetRandomPeer (!IsExploratory ()); if (r && r->IsECIES () && !r->GetProfile ()->IsBad () && - (numHops > 1 || (r->IsV4 () && (!inbound || r->IsReachable ())))) // first inbound must be reachable + (numHops > 1 || (r->IsV4 () && (!inbound || r->IsPublished (true))))) // first inbound must be published ipv4 { prevHop = r; path.Add (r); @@ -508,7 +563,7 @@ namespace tunnel for(int i = start; i < numHops; i++ ) { - auto hop = nextHop (prevHop, inbound); + auto hop = nextHop (prevHop, inbound, i == numHops - 1); if (!hop && !i) // if no suitable peer found for first hop, try already connected { LogPrint (eLogInfo, "Tunnels: Can't select first hop for a tunnel. Trying already connected"); @@ -520,12 +575,6 @@ namespace tunnel LogPrint (eLogError, "Tunnels: Can't select next hop for ", prevHop->GetIdentHashBase64 ()); return false; } - if ((i == numHops - 1) && (!hop->IsV4 () || // doesn't support ipv4 - (inbound && !hop->IsReachable ()))) // IBGW is not reachable - { - auto hop1 = nextHop (prevHop, true); - if (hop1) hop = hop1; - } prevHop = hop; path.Add (hop); } @@ -567,14 +616,15 @@ namespace tunnel if (m_CustomPeerSelector) return m_CustomPeerSelector->SelectPeers(path, numHops, isInbound); } - return StandardSelectPeers(path, numHops, isInbound, std::bind(&TunnelPool::SelectNextHop, this, std::placeholders::_1, std::placeholders::_2)); + return StandardSelectPeers(path, numHops, isInbound, std::bind(&TunnelPool::SelectNextHop, this, + std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); } bool TunnelPool::SelectExplicitPeers (Path& path, bool isInbound) { + if (!m_ExplicitPeers->size ()) return false; int numHops = isInbound ? m_NumInboundHops : m_NumOutboundHops; if (numHops > (int)m_ExplicitPeers->size ()) numHops = m_ExplicitPeers->size (); - if (!numHops) return false; for (int i = 0; i < numHops; i++) { auto& ident = (*m_ExplicitPeers)[i]; @@ -638,8 +688,13 @@ namespace tunnel outboundTunnel = tunnels.GetNextOutboundTunnel (); LogPrint (eLogDebug, "Tunnels: Re-creating destination inbound tunnel..."); std::shared_ptr config; - if (m_NumInboundHops > 0 && tunnel->GetPeers().size()) - config = std::make_shared(tunnel->GetPeers (), tunnel->IsShortBuildMessage (), tunnel->GetFarEndTransports ()); + if (m_NumInboundHops > 0) + { + auto peers = tunnel->GetPeers(); + if (peers.size ()&& ValidatePeers (peers)) + config = std::make_shared(tunnel->GetPeers (), + tunnel->IsShortBuildMessage (), tunnel->GetFarEndTransports ()); + } if (!m_NumInboundHops || config) { auto newTunnel = tunnels.CreateInboundTunnel (config, shared_from_this(), outboundTunnel); @@ -703,10 +758,12 @@ namespace tunnel { LogPrint (eLogDebug, "Tunnels: Re-creating destination outbound tunnel..."); std::shared_ptr config; - if (m_NumOutboundHops > 0 && tunnel->GetPeers().size()) + if (m_NumOutboundHops > 0) { - config = std::make_shared(tunnel->GetPeers (), inboundTunnel->GetNextTunnelID (), - inboundTunnel->GetNextIdentHash (), inboundTunnel->IsShortBuildMessage (), tunnel->GetFarEndTransports ()); + auto peers = tunnel->GetPeers(); + if (peers.size () && ValidatePeers (peers)) + config = std::make_shared(peers, inboundTunnel->GetNextTunnelID (), + inboundTunnel->GetNextIdentHash (), inboundTunnel->IsShortBuildMessage (), tunnel->GetFarEndTransports ()); } if (!m_NumOutboundHops || config) { @@ -747,6 +804,21 @@ namespace tunnel return m_CustomPeerSelector != nullptr; } + bool TunnelPool::ValidatePeers (std::vector >& peers) const + { + bool highBandwidth = !IsExploratory (); + for (auto it: peers) + { + auto r = i2p::data::netdb.FindRouter (it->GetIdentHash ()); + if (r) + { + if (r->IsHighCongestion (highBandwidth)) return false; + it = r->GetIdentity (); // use identity from updated RouterInfo + } + } + return true; + } + std::shared_ptr TunnelPool::GetLowestLatencyInboundTunnel(std::shared_ptr exclude) const { std::shared_ptr tun = nullptr; diff --git a/libi2pd/TunnelPool.h b/libi2pd/TunnelPool.h index 7d952559..3e845c0e 100644 --- a/libi2pd/TunnelPool.h +++ b/libi2pd/TunnelPool.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2023, The PurpleI2P Project +* Copyright (c) 2013-2024, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -15,6 +15,7 @@ #include #include #include +#include #include "Identity.h" #include "LeaseSet.h" #include "RouterInfo.h" @@ -30,7 +31,8 @@ namespace tunnel const int TUNNEL_POOL_MANAGE_INTERVAL = 10; // in seconds const int TUNNEL_POOL_MAX_INBOUND_TUNNELS_QUANTITY = 16; const int TUNNEL_POOL_MAX_OUTBOUND_TUNNELS_QUANTITY = 16; - const int TUNNEL_POOL_MAX_NUM_BUILD_REQUESTS = 2; + const int TUNNEL_POOL_MAX_NUM_BUILD_REQUESTS = 3; + const int TUNNEL_POOL_MAX_HOP_SELECTION_ATTEMPTS = 3; class Tunnel; class InboundTunnel; @@ -56,7 +58,7 @@ namespace tunnel class TunnelPool: public std::enable_shared_from_this // per local destination { - typedef std::function(std::shared_ptr, bool)> SelectHopFunc; + typedef std::function(std::shared_ptr, bool, bool)> SelectHopFunc; public: TunnelPool (int numInboundHops, int numOutboundHops, int numInboundTunnels, @@ -83,6 +85,7 @@ namespace tunnel void ManageTunnels (uint64_t ts); void ProcessGarlicMessage (std::shared_ptr msg); void ProcessDeliveryStatus (std::shared_ptr msg); + bool ProcessDeliveryStatus (uint32_t msgID, uint64_t timestamp); bool IsExploratory () const; bool IsActive () const { return m_IsActive; }; @@ -112,9 +115,11 @@ namespace tunnel std::shared_ptr GetLowestLatencyOutboundTunnel(std::shared_ptr exclude = nullptr) const; // for overriding tunnel peer selection - std::shared_ptr SelectNextHop (std::shared_ptr prevHop, bool reverse) const; + std::shared_ptr SelectNextHop (std::shared_ptr prevHop, bool reverse, bool endpoint) const; bool StandardSelectPeers(Path & path, int numHops, bool inbound, SelectHopFunc nextHop); + std::mt19937& GetRng () { return m_Rng; } + private: void TestTunnels (); @@ -126,6 +131,7 @@ namespace tunnel typename TTunnels::value_type excluded, i2p::data::RouterInfo::CompatibleTransports compatible) const; bool SelectPeers (Path& path, bool isInbound); bool SelectExplicitPeers (Path& path, bool isInbound); + bool ValidatePeers (std::vector >& peers) const; private: @@ -147,6 +153,9 @@ namespace tunnel uint64_t m_MinLatency = 0; // if > 0 this tunnel pool will try building tunnels with minimum latency by ms uint64_t m_MaxLatency = 0; // if > 0 this tunnel pool will try building tunnels with maximum latency by ms + std::random_device m_Rd; + std::mt19937 m_Rng; + public: // for HTTP only diff --git a/libi2pd/api.cpp b/libi2pd/api.cpp index 3bfb801c..05f962f3 100644 --- a/libi2pd/api.cpp +++ b/libi2pd/api.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2023, The PurpleI2P Project +* Copyright (c) 2013-2024, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -38,13 +38,15 @@ namespace api bool precomputation; i2p::config::GetOption("precomputation.elgamal", precomputation); bool aesni; i2p::config::GetOption("cpuext.aesni", aesni); - bool avx; i2p::config::GetOption("cpuext.avx", avx); bool forceCpuExt; i2p::config::GetOption("cpuext.force", forceCpuExt); - i2p::crypto::InitCrypto (precomputation, aesni, avx, forceCpuExt); + i2p::crypto::InitCrypto (precomputation, aesni, forceCpuExt); int netID; i2p::config::GetOption("netid", netID); i2p::context.SetNetID (netID); + bool checkReserved; i2p::config::GetOption("reservedrange", checkReserved); + i2p::transport::transports.SetCheckReserved(checkReserved); + i2p::context.Init (); } diff --git a/libi2pd/util.cpp b/libi2pd/util.cpp index 2426f13a..d1ed9992 100644 --- a/libi2pd/util.cpp +++ b/libi2pd/util.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2022, The PurpleI2P Project +* Copyright (c) 2013-2023, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -27,6 +27,17 @@ # include #endif +#if defined(__HAIKU__) +#include +#include +#include +#include +#ifndef _DEFAULT_SOURCE +#define _DEFAULT_SOURCE +#include +#endif +#endif + #ifdef _WIN32 #include #include @@ -201,7 +212,7 @@ namespace net int GetMTUWindowsIpv4 (sockaddr_in inputAddress, int fallback) { typedef const char *(* IPN)(int af, const void *src, char *dst, socklen_t size); - IPN inetntop = (IPN)GetProcAddress (GetModuleHandle ("ws2_32.dll"), "InetNtop"); + IPN inetntop = (IPN)(void*)GetProcAddress (GetModuleHandle ("ws2_32.dll"), "InetNtop"); if (!inetntop) inetntop = inet_ntop_xp; // use own implementation if not found ULONG outBufLen = 0; @@ -262,7 +273,7 @@ namespace net int GetMTUWindowsIpv6 (sockaddr_in6 inputAddress, int fallback) { typedef const char *(* IPN)(int af, const void *src, char *dst, socklen_t size); - IPN inetntop = (IPN)GetProcAddress (GetModuleHandle ("ws2_32.dll"), "InetNtop"); + IPN inetntop = (IPN)(void*)GetProcAddress (GetModuleHandle ("ws2_32.dll"), "InetNtop"); if (!inetntop) inetntop = inet_ntop_xp; // use own implementation if not found ULONG outBufLen = 0; @@ -341,7 +352,7 @@ namespace net #endif typedef int (* IPN)(int af, const char *src, void *dst); - IPN inetpton = (IPN)GetProcAddress (GetModuleHandle ("ws2_32.dll"), "InetPton"); + IPN inetpton = (IPN)(void*)GetProcAddress (GetModuleHandle ("ws2_32.dll"), "InetPton"); if (!inetpton) inetpton = inet_pton_xp; // use own implementation if not found if (localAddress.is_v4()) @@ -654,6 +665,7 @@ namespace net if (host.is_v6()) { static const std::vector< std::pair > reservedIPv6Ranges { + address_pair_v6("64:ff9b::", "64:ff9b:ffff:ffff:ffff:ffff:ffff:ffff"), // NAT64 address_pair_v6("2001:db8::", "2001:db8:ffff:ffff:ffff:ffff:ffff:ffff"), address_pair_v6("fc00::", "fdff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"), address_pair_v6("fe80::", "febf:ffff:ffff:ffff:ffff:ffff:ffff:ffff"), diff --git a/libi2pd/version.h b/libi2pd/version.h index 7d5e79fb..7b9e20c8 100644 --- a/libi2pd/version.h +++ b/libi2pd/version.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2023, The PurpleI2P Project +* Copyright (c) 2013-2024, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -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 47 -#define I2PD_VERSION_MICRO 0 +#define I2PD_VERSION_MINOR 50 +#define I2PD_VERSION_MICRO 2 #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 58 +#define I2P_VERSION_MICRO 61 #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) diff --git a/libi2pd_client/AddressBook.cpp b/libi2pd_client/AddressBook.cpp index 055311a9..8f2117a7 100644 --- a/libi2pd_client/AddressBook.cpp +++ b/libi2pd_client/AddressBook.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2022, The PurpleI2P Project +* Copyright (c) 2013-2023, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -403,10 +403,20 @@ namespace client if (!addr) return false; - i2p::data::IdentityEx ident; - if (ident.FromBase64 (jump) && ident.GetIdentHash () == addr->identHash) - return true; - + auto pos = jump.find(".b32.i2p"); + if (pos != std::string::npos) + { + i2p::data::IdentHash identHash; + if (identHash.FromBase32(jump.substr (0, pos)) && identHash == addr->identHash) + return true; + } + else + { + i2p::data::IdentityEx ident; + if (ident.FromBase64 (jump) && ident.GetIdentHash () == addr->identHash) + return true; + } + return false; } diff --git a/libi2pd_client/BOB.cpp b/libi2pd_client/BOB.cpp index 5f738380..23c3b72f 100644 --- a/libi2pd_client/BOB.cpp +++ b/libi2pd_client/BOB.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2022, The PurpleI2P Project +* Copyright (c) 2013-2023, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -127,7 +127,7 @@ namespace client connection->I2PConnect (receiver->data, receiver->dataLen); } - BOBI2POutboundTunnel::BOBI2POutboundTunnel (const std::string& outhost, int port, + BOBI2POutboundTunnel::BOBI2POutboundTunnel (const std::string& outhost, uint16_t port, std::shared_ptr localDestination, bool quiet): BOBI2PTunnel (localDestination), m_Endpoint (boost::asio::ip::address::from_string (outhost), port), m_IsQuiet (quiet) { @@ -164,7 +164,7 @@ namespace client BOBDestination::BOBDestination (std::shared_ptr localDestination, const std::string &nickname, const std::string &inhost, const std::string &outhost, - const int inport, const int outport, const bool quiet): + const uint16_t inport, const uint16_t outport, const bool quiet): m_LocalDestination (localDestination), m_OutboundTunnel (nullptr), m_InboundTunnel (nullptr), m_Nickname(nickname), m_InHost(inhost), m_OutHost(outhost), @@ -209,7 +209,7 @@ namespace client } } - void BOBDestination::CreateInboundTunnel (int port, const std::string& inhost) + void BOBDestination::CreateInboundTunnel (uint16_t port, const std::string& inhost) { if (!m_InboundTunnel) { @@ -230,7 +230,7 @@ namespace client } } - void BOBDestination::CreateOutboundTunnel (const std::string& outhost, int port, bool quiet) + void BOBDestination::CreateOutboundTunnel (const std::string& outhost, uint16_t port, bool quiet) { if (!m_OutboundTunnel) { @@ -357,13 +357,13 @@ namespace client os << data << std::endl; } - void BOBCommandSession::BuildStatusLine(bool currentTunnel, BOBDestination *dest, std::string &out) + void BOBCommandSession::BuildStatusLine(bool currentTunnel, std::shared_ptr dest, std::string &out) { // helper lambdas const auto issetStr = [](const std::string &str) { return str.empty() ? "not_set" : str; }; // for inhost, outhost const auto issetNum = [&issetStr](const int p) { return issetStr(p == 0 ? "" : std::to_string(p)); }; // for inport, outport const auto destExists = [](const BOBDestination * const dest) { return dest != nullptr; }; - const auto destReady = [](const BOBDestination * const dest) { return dest->IsRunning(); }; + const auto destReady = [](const BOBDestination * const dest) { return dest && dest->IsRunning(); }; const auto bool_str = [](const bool v) { return v ? "true" : "false"; }; // bool -> str // tunnel info @@ -373,9 +373,9 @@ namespace client const std::string outhost = issetStr(currentTunnel ? m_OutHost : dest->GetOutHost()); const std::string inport = issetNum(currentTunnel ? m_InPort : dest->GetInPort()); const std::string outport = issetNum(currentTunnel ? m_OutPort : dest->GetOutPort()); - const bool keys = destExists(dest); // key must exist when destination is created - const bool starting = destExists(dest) && !destReady(dest); - const bool running = destExists(dest) && destReady(dest); + const bool keys = destExists(dest.get ()); // key must exist when destination is created + const bool starting = destExists(dest.get ()) && !destReady(dest.get ()); + const bool running = destExists(dest.get ()) && destReady(dest.get ()); const bool stopping = false; // build line @@ -446,7 +446,7 @@ namespace client if (!m_CurrentDestination) { - m_CurrentDestination = new BOBDestination (i2p::client::context.CreateNewLocalDestination (m_Keys, true, &m_Options), // deleted in clear command + m_CurrentDestination = std::make_shared (i2p::client::context.CreateNewLocalDestination (m_Keys, true, &m_Options), // deleted in clear command m_Nickname, m_InHost, m_OutHost, m_InPort, m_OutPort, m_IsQuiet); m_Owner.AddDestination (m_Nickname, m_CurrentDestination); } @@ -595,9 +595,12 @@ namespace client LogPrint (eLogDebug, "BOB: outport ", operand); if (*operand) { - m_OutPort = std::stoi(operand); - if (m_OutPort >= 0) + int port = std::stoi(operand); + if (port >= 0 && port < 65536) + { + m_OutPort = port; SendReplyOK ("outbound port set"); + } else SendReplyError ("port out of range"); } @@ -622,9 +625,12 @@ namespace client LogPrint (eLogDebug, "BOB: inport ", operand); if (*operand) { - m_InPort = std::stoi(operand); - if (m_InPort >= 0) + int port = std::stoi(operand); + if (port >= 0 && port < 65536) + { + m_InPort = port; SendReplyOK ("inbound port set"); + } else SendReplyError ("port out of range"); } @@ -660,7 +666,13 @@ namespace client SendReplyError ("Address Not found"); return; } - auto localDestination = m_CurrentDestination ? m_CurrentDestination->GetLocalDestination () : i2p::client::context.GetSharedLocalDestination (); + auto localDestination = (m_CurrentDestination && m_CurrentDestination->IsRunning ()) ? + m_CurrentDestination->GetLocalDestination () : i2p::client::context.GetSharedLocalDestination (); + if (!localDestination) + { + SendReplyError ("No local destination"); + return; + } if (addr->IsIdentHash ()) { // we might have leaseset already @@ -814,7 +826,7 @@ namespace client } } - BOBCommandChannel::BOBCommandChannel (const std::string& address, int port): + BOBCommandChannel::BOBCommandChannel (const std::string& address, uint16_t port): RunnableService ("BOB"), m_Acceptor (GetIOService (), boost::asio::ip::tcp::endpoint(boost::asio::ip::address::from_string(address), port)) { @@ -869,8 +881,6 @@ namespace client { if (IsRunning ()) Stop (); - for (const auto& it: m_Destinations) - delete it.second; } void BOBCommandChannel::Start () @@ -887,9 +897,9 @@ namespace client StopIOService (); } - void BOBCommandChannel::AddDestination (const std::string& name, BOBDestination * dest) + void BOBCommandChannel::AddDestination (const std::string& name, std::shared_ptr dest) { - m_Destinations[name] = dest; + m_Destinations.emplace (name, dest); } void BOBCommandChannel::DeleteDestination (const std::string& name) @@ -898,12 +908,11 @@ namespace client if (it != m_Destinations.end ()) { it->second->Stop (); - delete it->second; m_Destinations.erase (it); } } - BOBDestination * BOBCommandChannel::FindDestination (const std::string& name) + std::shared_ptr BOBCommandChannel::FindDestination (const std::string& name) { auto it = m_Destinations.find (name); if (it != m_Destinations.end ()) diff --git a/libi2pd_client/BOB.h b/libi2pd_client/BOB.h index 2e6314e2..1f5fda5f 100644 --- a/libi2pd_client/BOB.h +++ b/libi2pd_client/BOB.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2020, The PurpleI2P Project +* Copyright (c) 2013-2023, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -124,7 +124,7 @@ namespace client { public: - BOBI2POutboundTunnel (const std::string& outhost, int port, std::shared_ptr localDestination, bool quiet); + BOBI2POutboundTunnel (const std::string& outhost, uint16_t port, std::shared_ptr localDestination, bool quiet); void Start (); void Stop (); @@ -149,19 +149,19 @@ namespace client BOBDestination (std::shared_ptr localDestination, const std::string &nickname, const std::string &inhost, const std::string &outhost, - const int inport, const int outport, const bool quiet); + const uint16_t inport, const uint16_t outport, const bool quiet); ~BOBDestination (); void Start (); void Stop (); void StopTunnels (); - void CreateInboundTunnel (int port, const std::string& inhost); - void CreateOutboundTunnel (const std::string& outhost, int port, bool quiet); + void CreateInboundTunnel (uint16_t port, const std::string& inhost); + void CreateOutboundTunnel (const std::string& outhost, uint16_t port, bool quiet); const std::string& GetNickname() const { return m_Nickname; } const std::string& GetInHost() const { return m_InHost; } const std::string& GetOutHost() const { return m_OutHost; } - int GetInPort() const { return m_InPort; } - int GetOutPort() const { return m_OutPort; } + uint16_t GetInPort() const { return m_InPort; } + uint16_t GetOutPort() const { return m_OutPort; } bool GetQuiet() const { return m_Quiet; } bool IsRunning() const { return m_IsRunning; } const i2p::data::PrivateKeys& GetKeys () const { return m_LocalDestination->GetPrivateKeys (); }; @@ -175,7 +175,7 @@ namespace client std::string m_Nickname; std::string m_InHost, m_OutHost; - int m_InPort, m_OutPort; + uint16_t m_InPort, m_OutPort; bool m_Quiet; bool m_IsRunning; }; @@ -228,7 +228,7 @@ namespace client void SendReplyError (const char * msg); void SendRaw (const char * data); - void BuildStatusLine(bool currentTunnel, BOBDestination *destination, std::string &out); + void BuildStatusLine(bool currentTunnel, std::shared_ptr destination, std::string &out); private: @@ -237,10 +237,10 @@ namespace client boost::asio::streambuf m_ReceiveBuffer, m_SendBuffer; bool m_IsOpen, m_IsQuiet, m_IsActive; std::string m_Nickname, m_InHost, m_OutHost; - int m_InPort, m_OutPort; + uint16_t m_InPort, m_OutPort; i2p::data::PrivateKeys m_Keys; std::map m_Options; - BOBDestination * m_CurrentDestination; + std::shared_ptr m_CurrentDestination; }; typedef void (BOBCommandSession::*BOBCommandHandler)(const char * operand, size_t len); @@ -248,16 +248,16 @@ namespace client { public: - BOBCommandChannel (const std::string& address, int port); + BOBCommandChannel (const std::string& address, uint16_t port); ~BOBCommandChannel (); void Start (); void Stop (); boost::asio::io_service& GetService () { return GetIOService (); }; - void AddDestination (const std::string& name, BOBDestination * dest); + void AddDestination (const std::string& name, std::shared_ptr dest); void DeleteDestination (const std::string& name); - BOBDestination * FindDestination (const std::string& name); + std::shared_ptr FindDestination (const std::string& name); private: @@ -267,7 +267,7 @@ namespace client private: boost::asio::ip::tcp::acceptor m_Acceptor; - std::map m_Destinations; + std::map > m_Destinations; std::map m_CommandHandlers; std::map m_HelpStrings; diff --git a/libi2pd_client/ClientContext.cpp b/libi2pd_client/ClientContext.cpp index dadb9ae5..62295842 100644 --- a/libi2pd_client/ClientContext.cpp +++ b/libi2pd_client/ClientContext.cpp @@ -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 * @@ -16,6 +16,7 @@ #include "Identity.h" #include "util.h" #include "ClientContext.h" +#include "HTTPProxy.h" #include "SOCKS.h" #include "MatchedDestination.h" @@ -186,22 +187,30 @@ namespace client LogPrint(eLogInfo, "Clients: Stopping AddressBook"); m_AddressBook.Stop (); + LogPrint(eLogInfo, "Clients: Stopping UDP Tunnels"); { std::lock_guard lock(m_ForwardsMutex); m_ServerForwards.clear(); m_ClientForwards.clear(); } + LogPrint(eLogInfo, "Clients: Stopping UDP Tunnels timers"); if (m_CleanupUDPTimer) { m_CleanupUDPTimer->cancel (); m_CleanupUDPTimer = nullptr; } - for (auto& it: m_Destinations) - it.second->Stop (); - m_Destinations.clear (); + { + LogPrint(eLogInfo, "Clients: Stopping Destinations"); + std::lock_guard lock(m_DestinationsMutex); + for (auto& it: m_Destinations) + it.second->Stop (); + LogPrint(eLogInfo, "Clients: Stopping Destinations - Clear"); + m_Destinations.clear (); + } + LogPrint(eLogInfo, "Clients: Stopping SharedLocalDestination"); m_SharedLocalDestination->Release (); m_SharedLocalDestination = nullptr; } @@ -569,12 +578,12 @@ namespace client std::string dest; if (type == I2P_TUNNELS_SECTION_TYPE_CLIENT || type == I2P_TUNNELS_SECTION_TYPE_UDPCLIENT) dest = section.second.get (I2P_CLIENT_TUNNEL_DESTINATION); - int port = section.second.get (I2P_CLIENT_TUNNEL_PORT); + uint16_t port = section.second.get (I2P_CLIENT_TUNNEL_PORT); // optional params - bool matchTunnels = section.second.get(I2P_CLIENT_TUNNEL_MATCH_TUNNELS, false); - std::string keys = section.second.get (I2P_CLIENT_TUNNEL_KEYS, "transient"); - std::string address = section.second.get (I2P_CLIENT_TUNNEL_ADDRESS, "127.0.0.1"); - int destinationPort = section.second.get (I2P_CLIENT_TUNNEL_DESTINATION_PORT, 0); + bool matchTunnels = section.second.get (I2P_CLIENT_TUNNEL_MATCH_TUNNELS, false); + std::string keys = section.second.get (I2P_CLIENT_TUNNEL_KEYS, "transient"); + std::string address = section.second.get (I2P_CLIENT_TUNNEL_ADDRESS, "127.0.0.1"); + uint16_t destinationPort = section.second.get (I2P_CLIENT_TUNNEL_DESTINATION_PORT, 0); i2p::data::SigningKeyType sigType = section.second.get (I2P_CLIENT_TUNNEL_SIGNATURE_TYPE, i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519); i2p::data::CryptoKeyType cryptoType = section.second.get (I2P_CLIENT_TUNNEL_CRYPTO_TYPE, i2p::data::CRYPTO_KEY_TYPE_ELGAMAL); // I2CP @@ -712,22 +721,22 @@ namespace client { // mandatory params std::string host = section.second.get (I2P_SERVER_TUNNEL_HOST); - int port = section.second.get (I2P_SERVER_TUNNEL_PORT); + uint16_t port = section.second.get (I2P_SERVER_TUNNEL_PORT); std::string keys = section.second.get (I2P_SERVER_TUNNEL_KEYS); // optional params - int inPort = section.second.get (I2P_SERVER_TUNNEL_INPORT, 0); - std::string accessList = section.second.get (I2P_SERVER_TUNNEL_ACCESS_LIST, ""); + uint16_t inPort = section.second.get (I2P_SERVER_TUNNEL_INPORT, port); + std::string accessList = section.second.get (I2P_SERVER_TUNNEL_ACCESS_LIST, ""); if(accessList == "") - accessList=section.second.get (I2P_SERVER_TUNNEL_WHITE_LIST, ""); - std::string hostOverride = section.second.get (I2P_SERVER_TUNNEL_HOST_OVERRIDE, ""); + accessList = section.second.get (I2P_SERVER_TUNNEL_WHITE_LIST, ""); + std::string hostOverride = section.second.get (I2P_SERVER_TUNNEL_HOST_OVERRIDE, ""); std::string webircpass = section.second.get (I2P_SERVER_TUNNEL_WEBIRC_PASSWORD, ""); bool gzip = section.second.get (I2P_SERVER_TUNNEL_GZIP, false); i2p::data::SigningKeyType sigType = section.second.get (I2P_SERVER_TUNNEL_SIGNATURE_TYPE, i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519); i2p::data::CryptoKeyType cryptoType = section.second.get (I2P_CLIENT_TUNNEL_CRYPTO_TYPE, i2p::data::CRYPTO_KEY_TYPE_ELGAMAL); std::string address = section.second.get (I2P_SERVER_TUNNEL_ADDRESS, ""); - bool isUniqueLocal = section.second.get(I2P_SERVER_TUNNEL_ENABLE_UNIQUE_LOCAL, true); - bool ssl = section.second.get(I2P_SERVER_TUNNEL_SSL, false); + bool isUniqueLocal = section.second.get (I2P_SERVER_TUNNEL_ENABLE_UNIQUE_LOCAL, true); + bool ssl = section.second.get (I2P_SERVER_TUNNEL_SSL, false); // I2CP std::map options; @@ -767,7 +776,7 @@ namespace client address = "127.0.0.1"; } auto localAddress = boost::asio::ip::address::from_string(address); - auto serverTunnel = std::make_shared(name, localDestination, localAddress, endpoint, port, gzip); + auto serverTunnel = std::make_shared(name, localDestination, localAddress, endpoint, inPort, gzip); if(!isUniqueLocal) { LogPrint(eLogInfo, "Clients: Disabling loopback address mapping"); diff --git a/libi2pd_client/ClientContext.h b/libi2pd_client/ClientContext.h index 4e969a6b..adec607a 100644 --- a/libi2pd_client/ClientContext.h +++ b/libi2pd_client/ClientContext.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2022, The PurpleI2P Project +* Copyright (c) 2013-2024, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -15,8 +15,6 @@ #include #include "Destination.h" #include "I2PService.h" -#include "HTTPProxy.h" -#include "SOCKS.h" #include "I2PTunnel.h" #include "UDPTunnel.h" #include "SAM.h" @@ -141,8 +139,7 @@ namespace client AddressBook m_AddressBook; - i2p::proxy::HTTPProxy * m_HttpProxy; - i2p::proxy::SOCKSProxy * m_SocksProxy; + I2PService * m_HttpProxy, * m_SocksProxy; std::map > m_ClientTunnels; // local endpoint -> tunnel std::map, std::shared_ptr > m_ServerTunnels; // -> tunnel @@ -167,8 +164,8 @@ namespace client const decltype(m_ServerTunnels)& GetServerTunnels () const { return m_ServerTunnels; }; const decltype(m_ClientForwards)& GetClientForwards () const { return m_ClientForwards; } const decltype(m_ServerForwards)& GetServerForwards () const { return m_ServerForwards; } - const i2p::proxy::HTTPProxy * GetHttpProxy () const { return m_HttpProxy; } - const i2p::proxy::SOCKSProxy * GetSocksProxy () const { return m_SocksProxy; } + const I2PService * GetHttpProxy () const { return m_HttpProxy; } + const I2PService * GetSocksProxy () const { return m_SocksProxy; } }; extern ClientContext context; diff --git a/libi2pd_client/HTTPProxy.cpp b/libi2pd_client/HTTPProxy.cpp index 61930876..01678f4a 100644 --- a/libi2pd_client/HTTPProxy.cpp +++ b/libi2pd_client/HTTPProxy.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2023, The PurpleI2P Project +* Copyright (c) 2013-2024, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -29,6 +29,7 @@ #include "Config.h" #include "HTTP.h" #include "I18N.h" +#include "Socks5.h" namespace i2p { namespace proxy { @@ -75,8 +76,9 @@ namespace proxy { void HandleSockRecv(const boost::system::error_code & ecode, std::size_t bytes_transfered); void Terminate(); void AsyncSockRead(); - bool ExtractAddressHelper(i2p::http::URL & url, std::string & b64, bool & confirm); - void SanitizeHTTPRequest(i2p::http::HTTPReq & req); + 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); void SentHTTPFailed(const boost::system::error_code & ecode); void HandleStreamRequestComplete (std::shared_ptr stream); /* error helpers */ @@ -92,9 +94,6 @@ namespace proxy { void HTTPConnect(const std::string & host, uint16_t port); void HandleHTTPConnectStreamRequestComplete(std::shared_ptr stream); - void HandleSocksProxySendHandshake(const boost::system::error_code & ec, std::size_t bytes_transfered); - void HandleSocksProxyReply(const boost::system::error_code & ec, std::size_t bytes_transfered); - typedef std::function ProxyResolvedHandler; void HandleUpstreamProxyResolved(const boost::system::error_code & ecode, boost::asio::ip::tcp::resolver::iterator itr, ProxyResolvedHandler handler); @@ -108,11 +107,10 @@ namespace proxy { std::shared_ptr m_sock; std::shared_ptr m_proxysock; boost::asio::ip::tcp::resolver m_proxy_resolver; - std::string m_OutproxyUrl; + std::string m_OutproxyUrl, m_Response; bool m_Addresshelper; i2p::http::URL m_ProxyURL; i2p::http::URL m_RequestURL; - uint8_t m_socks_buf[255+8]; // for socks request/response int m_req_len; i2p::http::URL m_ClientRequestURL; i2p::http::HTTPReq m_ClientRequest; @@ -205,8 +203,8 @@ namespace proxy { << "" << content << "\r\n" << "\r\n"; res.body = ss.str(); - std::string response = res.to_string(); - boost::asio::async_write(*m_sock, boost::asio::buffer(response), boost::asio::transfer_all(), + m_Response = res.to_string(); + boost::asio::async_write(*m_sock, boost::asio::buffer(m_Response), boost::asio::transfer_all(), std::bind(&HTTPReqHandler::SentHTTPFailed, shared_from_this(), std::placeholders::_1)); } @@ -216,12 +214,12 @@ namespace proxy { res.code = 302; res.add_header("Location", address); res.add_header("Connection", "close"); - std::string response = res.to_string(); - boost::asio::async_write(*m_sock, boost::asio::buffer(response), boost::asio::transfer_all(), + m_Response = res.to_string(); + boost::asio::async_write(*m_sock, boost::asio::buffer(m_Response), boost::asio::transfer_all(), std::bind(&HTTPReqHandler::SentHTTPFailed, shared_from_this(), std::placeholders::_1)); } - bool HTTPReqHandler::ExtractAddressHelper(i2p::http::URL & url, std::string & b64, bool & confirm) + bool HTTPReqHandler::ExtractAddressHelper(i2p::http::URL& url, std::string& jump, bool& confirm) { confirm = false; const char *param = "i2paddresshelper="; @@ -237,8 +235,13 @@ namespace proxy { std::string value = params["i2paddresshelper"]; len += value.length(); - b64 = i2p::http::UrlDecode(value); - + jump = i2p::http::UrlDecode(value); + if (!VerifyAddressHelper (jump)) + { + LogPrint (eLogError, "HTTPProxy: Malformed jump link ", jump); + return false; + } + // if we need update exists, request formed with update param if (params["update"] == "true") { @@ -269,7 +272,35 @@ namespace proxy { return true; } - void HTTPReqHandler::SanitizeHTTPRequest(i2p::http::HTTPReq & req) + bool HTTPReqHandler::VerifyAddressHelper (const std::string& jump) + { + auto pos = jump.find(".b32.i2p"); + if (pos != std::string::npos) + { + auto b32 = jump.substr (0, pos); + for (auto& ch: b32) + if (!i2p::data::IsBase32(ch)) return false; + return true; + } + else + { + bool padding = false; + for (auto& ch: jump) + { + if (ch == '=') + padding = true; + else + { + if (padding) return false; // other chars after padding + if (!i2p::data::IsBase64(ch)) return false; + } + } + return true; + } + return false; + } + + void HTTPReqHandler::SanitizeHTTPRequest(i2p::http::HTTPReq& req) { /* drop common headers */ req.RemoveHeader("Via"); @@ -578,49 +609,36 @@ namespace proxy { void HTTPReqHandler::HandleUpstreamSocksProxyConnect(const boost::system::error_code & ec) { - if(!ec) { - if(m_RequestURL.host.size() > 255) { + if(!ec) + { + if(m_RequestURL.host.size() > 255) + { GenericProxyError(tr("Hostname is too long"), m_RequestURL.host); return; } uint16_t port = m_RequestURL.port; if(!port) port = 80; LogPrint(eLogDebug, "HTTPProxy: Connected to SOCKS upstream"); - std::string host = m_RequestURL.host; - std::size_t reqsize = 0; - m_socks_buf[0] = '\x04'; - m_socks_buf[1] = 1; - htobe16buf(m_socks_buf+2, port); - m_socks_buf[4] = 0; - m_socks_buf[5] = 0; - m_socks_buf[6] = 0; - m_socks_buf[7] = 1; - // user id - m_socks_buf[8] = 'i'; - m_socks_buf[9] = '2'; - m_socks_buf[10] = 'p'; - m_socks_buf[11] = 'd'; - m_socks_buf[12] = 0; - reqsize += 13; - memcpy(m_socks_buf+ reqsize, host.c_str(), host.size()); - reqsize += host.size(); - m_socks_buf[++reqsize] = 0; - boost::asio::async_write(*m_proxysock, boost::asio::buffer(m_socks_buf, reqsize), boost::asio::transfer_all(), std::bind(&HTTPReqHandler::HandleSocksProxySendHandshake, this, std::placeholders::_1, std::placeholders::_2)); - } else GenericProxyError(tr("Cannot connect to upstream SOCKS proxy"), ec.message()); - } - - void HTTPReqHandler::HandleSocksProxySendHandshake(const boost::system::error_code & ec, std::size_t bytes_transferred) - { - LogPrint(eLogDebug, "HTTPProxy: Upstream SOCKS handshake sent"); - if(ec) GenericProxyError(tr("Cannot negotiate with SOCKS proxy"), ec.message()); - else m_proxysock->async_read_some(boost::asio::buffer(m_socks_buf, 8), std::bind(&HTTPReqHandler::HandleSocksProxyReply, this, std::placeholders::_1, std::placeholders::_2)); + auto s = shared_from_this (); + i2p::transport::Socks5Handshake (*m_proxysock, std::make_pair(host, port), + [s](const boost::system::error_code& ec) + { + if (!ec) + s->SocksProxySuccess(); + else + s->GenericProxyError(tr("SOCKS proxy error"), ec.message ()); + }); + + } + else + GenericProxyError(tr("Cannot connect to upstream SOCKS proxy"), ec.message()); } void HTTPReqHandler::HandoverToUpstreamProxy() { LogPrint(eLogDebug, "HTTPProxy: Handover to SOCKS proxy"); - auto connection = std::make_shared(GetOwner(), m_proxysock, m_sock); + auto connection = CreateSocketsPipe (GetOwner(), m_proxysock, m_sock); m_sock = nullptr; m_proxysock = nullptr; GetOwner()->AddHandler(connection); @@ -680,24 +698,6 @@ namespace proxy { } } - void HTTPReqHandler::HandleSocksProxyReply(const boost::system::error_code & ec, std::size_t bytes_transferred) - { - if(!ec) - { - if(m_socks_buf[1] == 90) { - // success - SocksProxySuccess(); - } else { - std::stringstream ss; - ss << "error code: "; - ss << (int) m_socks_buf[1]; - std::string msg = ss.str(); - GenericProxyError(tr("SOCKS proxy error"), msg); - } - } - else GenericProxyError(tr("No reply from SOCKS proxy"), ec.message()); - } - void HTTPReqHandler::HandleUpstreamHTTPProxyConnect(const boost::system::error_code & ec) { if(!ec) { @@ -748,7 +748,7 @@ namespace proxy { Done (shared_from_this()); } - HTTPProxy::HTTPProxy(const std::string& name, const std::string& address, int port, const std::string & outproxy, bool addresshelper, std::shared_ptr localDestination): + HTTPProxy::HTTPProxy(const std::string& name, const std::string& address, uint16_t port, const std::string & outproxy, bool addresshelper, std::shared_ptr localDestination): TCPIPAcceptor (address, port, localDestination ? localDestination : i2p::client::context.GetSharedLocalDestination ()), m_Name (name), m_OutproxyUrl (outproxy), m_Addresshelper (addresshelper) { diff --git a/libi2pd_client/HTTPProxy.h b/libi2pd_client/HTTPProxy.h index 69ed4cef..d819a53c 100644 --- a/libi2pd_client/HTTPProxy.h +++ b/libi2pd_client/HTTPProxy.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2020, The PurpleI2P Project +* Copyright (c) 2013-2023, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -15,8 +15,8 @@ namespace proxy { { public: - HTTPProxy(const std::string& name, const std::string& address, int port, const std::string & outproxy, bool addresshelper, std::shared_ptr localDestination); - HTTPProxy(const std::string& name, const std::string& address, int port, std::shared_ptr localDestination = nullptr) : + HTTPProxy(const std::string& name, const std::string& address, uint16_t port, const std::string & outproxy, bool addresshelper, std::shared_ptr localDestination); + HTTPProxy(const std::string& name, const std::string& address, uint16_t port, std::shared_ptr localDestination = nullptr) : HTTPProxy(name, address, port, "", true, localDestination) {} ; ~HTTPProxy() {}; diff --git a/libi2pd_client/I2CP.cpp b/libi2pd_client/I2CP.cpp index 87f37e5e..2c53e766 100644 --- a/libi2pd_client/I2CP.cpp +++ b/libi2pd_client/I2CP.cpp @@ -936,7 +936,7 @@ namespace client } } - I2CPServer::I2CPServer (const std::string& interface, int port, bool isSingleThread): + I2CPServer::I2CPServer (const std::string& interface, uint16_t port, bool isSingleThread): RunnableService ("I2CP"), m_IsSingleThread (isSingleThread), m_Acceptor (GetIOService (), boost::asio::ip::tcp::endpoint(boost::asio::ip::address::from_string(interface), port)) diff --git a/libi2pd_client/I2CP.h b/libi2pd_client/I2CP.h index e38da0ac..f0081ef6 100644 --- a/libi2pd_client/I2CP.h +++ b/libi2pd_client/I2CP.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2022, The PurpleI2P Project +* Copyright (c) 2013-2023, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -210,7 +210,7 @@ namespace client { public: - I2CPServer (const std::string& interface, int port, bool isSingleThread); + I2CPServer (const std::string& interface, uint16_t port, bool isSingleThread); ~I2CPServer (); void Start (); diff --git a/libi2pd_client/I2PService.cpp b/libi2pd_client/I2PService.cpp index 2a6fe44e..e9513e48 100644 --- a/libi2pd_client/I2PService.cpp +++ b/libi2pd_client/I2PService.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2020, The PurpleI2P Project +* Copyright (c) 2013-2024, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -107,7 +107,7 @@ namespace client m_ReadyTimerTriggered = false; } - void I2PService::CreateStream (StreamRequestComplete streamRequestComplete, const std::string& dest, int port) { + void I2PService::CreateStream (StreamRequestComplete streamRequestComplete, const std::string& dest, uint16_t port) { assert(streamRequestComplete); auto address = i2p::client::context.GetAddressBook ().GetAddress (dest); if (address) @@ -119,7 +119,7 @@ namespace client } } - void I2PService::CreateStream(StreamRequestComplete streamRequestComplete, std::shared_ptr address, int port) + void I2PService::CreateStream(StreamRequestComplete streamRequestComplete, std::shared_ptr address, uint16_t port) { if(m_ConnectTimeout && !m_LocalDestination->IsReady()) { @@ -147,196 +147,5 @@ namespace client m_LocalDestination->CreateStream (streamRequestComplete, address->blindedPublicKey, port); } } - - TCPIPPipe::TCPIPPipe(I2PService * owner, std::shared_ptr upstream, std::shared_ptr downstream) : I2PServiceHandler(owner), m_up(upstream), m_down(downstream) - { - boost::asio::socket_base::receive_buffer_size option(TCP_IP_PIPE_BUFFER_SIZE); - upstream->set_option(option); - downstream->set_option(option); - } - - TCPIPPipe::~TCPIPPipe() - { - Terminate(); - } - - void TCPIPPipe::Start() - { - AsyncReceiveUpstream(); - AsyncReceiveDownstream(); - } - - void TCPIPPipe::Terminate() - { - if(Kill()) return; - if (m_up) - { - if (m_up->is_open()) - m_up->close(); - m_up = nullptr; - } - if (m_down) - { - if (m_down->is_open()) - m_down->close(); - m_down = nullptr; - } - Done(shared_from_this()); - } - - void TCPIPPipe::AsyncReceiveUpstream() - { - if (m_up) - { - m_up->async_read_some(boost::asio::buffer(m_upstream_to_down_buf, TCP_IP_PIPE_BUFFER_SIZE), - std::bind(&TCPIPPipe::HandleUpstreamReceived, shared_from_this(), - std::placeholders::_1, std::placeholders::_2)); - } - else - LogPrint(eLogError, "TCPIPPipe: Upstream receive: No socket"); - } - - void TCPIPPipe::AsyncReceiveDownstream() - { - if (m_down) { - m_down->async_read_some(boost::asio::buffer(m_downstream_to_up_buf, TCP_IP_PIPE_BUFFER_SIZE), - std::bind(&TCPIPPipe::HandleDownstreamReceived, shared_from_this(), - std::placeholders::_1, std::placeholders::_2)); - } - else - LogPrint(eLogError, "TCPIPPipe: Downstream receive: No socket"); - } - - void TCPIPPipe::UpstreamWrite(size_t len) - { - if (m_up) - { - LogPrint(eLogDebug, "TCPIPPipe: Upstream: ", (int) len, " bytes written"); - boost::asio::async_write(*m_up, boost::asio::buffer(m_upstream_buf, len), - boost::asio::transfer_all(), - std::bind(&TCPIPPipe::HandleUpstreamWrite, - shared_from_this(), - std::placeholders::_1)); - } - else - LogPrint(eLogError, "TCPIPPipe: Upstream write: no socket"); - } - - void TCPIPPipe::DownstreamWrite(size_t len) - { - if (m_down) - { - LogPrint(eLogDebug, "TCPIPPipe: Downstream: ", (int) len, " bytes written"); - boost::asio::async_write(*m_down, boost::asio::buffer(m_downstream_buf, len), - boost::asio::transfer_all(), - std::bind(&TCPIPPipe::HandleDownstreamWrite, - shared_from_this(), - std::placeholders::_1)); - } - else - LogPrint(eLogError, "TCPIPPipe: Downstream write: No socket"); - } - - - void TCPIPPipe::HandleDownstreamReceived(const boost::system::error_code & ecode, std::size_t bytes_transfered) - { - LogPrint(eLogDebug, "TCPIPPipe: Downstream: ", (int) bytes_transfered, " bytes received"); - if (ecode) - { - LogPrint(eLogError, "TCPIPPipe: Downstream read error:" , ecode.message()); - if (ecode != boost::asio::error::operation_aborted) - Terminate(); - } else { - if (bytes_transfered > 0 ) - memcpy(m_upstream_buf, m_downstream_to_up_buf, bytes_transfered); - UpstreamWrite(bytes_transfered); - } - } - - void TCPIPPipe::HandleDownstreamWrite(const boost::system::error_code & ecode) { - if (ecode) - { - LogPrint(eLogError, "TCPIPPipe: Downstream write error:" , ecode.message()); - if (ecode != boost::asio::error::operation_aborted) - Terminate(); - } - else - AsyncReceiveUpstream(); - } - - void TCPIPPipe::HandleUpstreamWrite(const boost::system::error_code & ecode) { - if (ecode) - { - LogPrint(eLogError, "TCPIPPipe: Upstream write error:" , ecode.message()); - if (ecode != boost::asio::error::operation_aborted) - Terminate(); - } - else - AsyncReceiveDownstream(); - } - - void TCPIPPipe::HandleUpstreamReceived(const boost::system::error_code & ecode, std::size_t bytes_transfered) - { - LogPrint(eLogDebug, "TCPIPPipe: Upstream ", (int)bytes_transfered, " bytes received"); - if (ecode) - { - LogPrint(eLogError, "TCPIPPipe: Upstream read error:" , ecode.message()); - if (ecode != boost::asio::error::operation_aborted) - Terminate(); - } else { - if (bytes_transfered > 0 ) - memcpy(m_downstream_buf, m_upstream_to_down_buf, bytes_transfered); - DownstreamWrite(bytes_transfered); - } - } - - void TCPIPAcceptor::Start () - { - m_Acceptor.reset (new boost::asio::ip::tcp::acceptor (GetService (), m_LocalEndpoint)); - // update the local end point in case port has been set zero and got updated now - m_LocalEndpoint = m_Acceptor->local_endpoint(); - m_Acceptor->listen (); - Accept (); - } - - void TCPIPAcceptor::Stop () - { - if (m_Acceptor) - { - m_Acceptor->close(); - m_Acceptor.reset (nullptr); - } - m_Timer.cancel (); - ClearHandlers(); - } - - void TCPIPAcceptor::Accept () - { - auto newSocket = std::make_shared (GetService ()); - m_Acceptor->async_accept (*newSocket, std::bind (&TCPIPAcceptor::HandleAccept, this, - std::placeholders::_1, newSocket)); - } - - void TCPIPAcceptor::HandleAccept (const boost::system::error_code& ecode, std::shared_ptr socket) - { - if (!ecode) - { - LogPrint(eLogDebug, "I2PService: ", GetName(), " accepted"); - auto handler = CreateHandler(socket); - if (handler) - { - AddHandler(handler); - handler->Handle(); - } - else - socket->close(); - Accept(); - } - else - { - if (ecode != boost::asio::error::operation_aborted) - LogPrint (eLogError, "I2PService: ", GetName(), " closing socket on accept because: ", ecode.message ()); - } - } } } diff --git a/libi2pd_client/I2PService.h b/libi2pd_client/I2PService.h index e14f85c1..d35c954d 100644 --- a/libi2pd_client/I2PService.h +++ b/libi2pd_client/I2PService.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 * @@ -59,8 +59,8 @@ namespace client if (dest) dest->Acquire (); m_LocalDestination = dest; } - void CreateStream (StreamRequestComplete streamRequestComplete, const std::string& dest, int port = 0); - void CreateStream(StreamRequestComplete complete, std::shared_ptr address, int port); + void CreateStream (StreamRequestComplete streamRequestComplete, const std::string& dest, uint16_t port = 0); + void CreateStream(StreamRequestComplete complete, std::shared_ptr address, uint16_t port); inline boost::asio::io_service& GetService () { return m_LocalDestination->GetService (); } virtual void Start () = 0; @@ -99,6 +99,7 @@ namespace client virtual ~I2PServiceHandler() { } //If you override this make sure you call it from the children virtual void Handle() {}; //Start handling the socket + virtual void Start () {}; void Terminate () { Kill (); }; @@ -119,72 +120,171 @@ namespace client std::atomic m_Dead; //To avoid cleaning up multiple times }; - const size_t TCP_IP_PIPE_BUFFER_SIZE = 8192 * 8; + const size_t SOCKETS_PIPE_BUFFER_SIZE = 8192 * 8; - // bidirectional pipe for 2 tcp/ip sockets - class TCPIPPipe: public I2PServiceHandler, public std::enable_shared_from_this + // bidirectional pipe for 2 stream sockets + template + class SocketsPipe: public I2PServiceHandler, + public std::enable_shared_from_this > { public: - TCPIPPipe(I2PService * owner, std::shared_ptr upstream, std::shared_ptr downstream); - ~TCPIPPipe(); - void Start(); - - protected: - - void Terminate(); - void AsyncReceiveUpstream(); - void AsyncReceiveDownstream(); - void HandleUpstreamReceived(const boost::system::error_code & ecode, std::size_t bytes_transferred); - void HandleDownstreamReceived(const boost::system::error_code & ecode, std::size_t bytes_transferred); - void HandleUpstreamWrite(const boost::system::error_code & ecode); - void HandleDownstreamWrite(const boost::system::error_code & ecode); - void UpstreamWrite(size_t len); - void DownstreamWrite(size_t len); + SocketsPipe(I2PService * owner, std::shared_ptr upstream, std::shared_ptr downstream): + I2PServiceHandler(owner), m_up(upstream), m_down(downstream) + { + boost::asio::socket_base::receive_buffer_size option(SOCKETS_PIPE_BUFFER_SIZE); + upstream->set_option(option); + downstream->set_option(option); + } + ~SocketsPipe() { Terminate(); } + + void Start() override + { + Transfer (m_up, m_down, m_upstream_to_down_buf, SOCKETS_PIPE_BUFFER_SIZE); // receive from upstream + Transfer (m_down, m_up, m_downstream_to_up_buf, SOCKETS_PIPE_BUFFER_SIZE); // receive from upstream + } private: - uint8_t m_upstream_to_down_buf[TCP_IP_PIPE_BUFFER_SIZE], m_downstream_to_up_buf[TCP_IP_PIPE_BUFFER_SIZE]; - uint8_t m_upstream_buf[TCP_IP_PIPE_BUFFER_SIZE], m_downstream_buf[TCP_IP_PIPE_BUFFER_SIZE]; - std::shared_ptr m_up, m_down; + void Terminate() + { + if(Kill()) return; + if (m_up) + { + if (m_up->is_open()) + m_up->close(); + m_up = nullptr; + } + if (m_down) + { + if (m_down->is_open()) + m_down->close(); + m_down = nullptr; + } + Done(SocketsPipe::shared_from_this()); + } + + template + void Transfer (std::shared_ptr from, std::shared_ptr to, uint8_t * buf, size_t len) + { + if (!from || !to || !buf) return; + auto s = SocketsPipe::shared_from_this (); + from->async_read_some(boost::asio::buffer(buf, len), + [from, to, s, buf, len](const boost::system::error_code& ecode, std::size_t transferred) + { + if (ecode == boost::asio::error::operation_aborted) return; + if (!ecode) + { + boost::asio::async_write(*to, boost::asio::buffer(buf, transferred), boost::asio::transfer_all(), + [from, to, s, buf, len](const boost::system::error_code& ecode, std::size_t transferred) + { + (void) transferred; + if (ecode == boost::asio::error::operation_aborted) return; + if (!ecode) + s->Transfer (from, to, buf, len); + else + { + LogPrint(eLogWarning, "SocketsPipe: Write error:" , ecode.message()); + s->Terminate(); + } + }); + } + else + { + LogPrint(eLogWarning, "SocketsPipe: Read error:" , ecode.message()); + s->Terminate(); + } + }); + } + + private: + + uint8_t m_upstream_to_down_buf[SOCKETS_PIPE_BUFFER_SIZE], m_downstream_to_up_buf[SOCKETS_PIPE_BUFFER_SIZE]; + std::shared_ptr m_up; + std::shared_ptr m_down; }; - /* TODO: support IPv6 too */ - //This is a service that listens for connections on the IP network and interacts with I2P - class TCPIPAcceptor: public I2PService + template + std::shared_ptr CreateSocketsPipe (I2PService * owner, std::shared_ptr upstream, std::shared_ptr downstream) + { + return std::make_shared >(owner, upstream, downstream); + } + + //This is a service that listens for connections on the IP network or local socket and interacts with I2P + template + class ServiceAcceptor: public I2PService { public: - TCPIPAcceptor (const std::string& address, int port, std::shared_ptr localDestination = nullptr) : - I2PService(localDestination), - m_LocalEndpoint (boost::asio::ip::address::from_string(address), port), - m_Timer (GetService ()) {} - TCPIPAcceptor (const std::string& address, int port, i2p::data::SigningKeyType kt) : - I2PService(kt), - m_LocalEndpoint (boost::asio::ip::address::from_string(address), port), - m_Timer (GetService ()) {} - virtual ~TCPIPAcceptor () { TCPIPAcceptor::Stop(); } - //If you override this make sure you call it from the children - void Start (); - //If you override this make sure you call it from the children - void Stop (); + ServiceAcceptor (const typename Protocol::endpoint& localEndpoint, std::shared_ptr localDestination = nullptr) : + I2PService(localDestination), m_LocalEndpoint (localEndpoint) {} + + virtual ~ServiceAcceptor () { Stop(); } + void Start () override + { + m_Acceptor.reset (new typename Protocol::acceptor (GetService (), m_LocalEndpoint)); + // update the local end point in case port has been set zero and got updated now + m_LocalEndpoint = m_Acceptor->local_endpoint(); + m_Acceptor->listen (); + Accept (); + } + void Stop () override + { + if (m_Acceptor) + { + m_Acceptor->close(); + m_Acceptor.reset (nullptr); + } + ClearHandlers(); + } + const typename Protocol::endpoint& GetLocalEndpoint () const { return m_LocalEndpoint; }; - const boost::asio::ip::tcp::endpoint& GetLocalEndpoint () const { return m_LocalEndpoint; }; - - virtual const char* GetName() { return "Generic TCP/IP accepting daemon"; } + const char* GetName() override { return "Generic TCP/IP accepting daemon"; } protected: - virtual std::shared_ptr CreateHandler(std::shared_ptr socket) = 0; + virtual std::shared_ptr CreateHandler(std::shared_ptr socket) = 0; private: - void Accept(); - void HandleAccept(const boost::system::error_code& ecode, std::shared_ptr socket); - boost::asio::ip::tcp::endpoint m_LocalEndpoint; - std::unique_ptr m_Acceptor; - boost::asio::deadline_timer m_Timer; + void Accept() + { + auto newSocket = std::make_shared (GetService ()); + m_Acceptor->async_accept (*newSocket, + [newSocket, this](const boost::system::error_code& ecode) + { + if (ecode == boost::asio::error::operation_aborted) return; + if (!ecode) + { + LogPrint(eLogDebug, "ServiceAcceptor: ", GetName(), " accepted"); + auto handler = CreateHandler(newSocket); + if (handler) + { + AddHandler(handler); + handler->Handle(); + } + else + newSocket->close(); + Accept(); + } + else + LogPrint (eLogError, "ServiceAcceptor: ", GetName(), " closing socket on accept because: ", ecode.message ()); + }); + } + + private: + + typename Protocol::endpoint m_LocalEndpoint; + std::unique_ptr m_Acceptor; }; + + class TCPIPAcceptor: public ServiceAcceptor + { + public: + + TCPIPAcceptor (const std::string& address, uint16_t port, std::shared_ptr localDestination = nullptr) : + ServiceAcceptor (boost::asio::ip::tcp::endpoint (boost::asio::ip::address::from_string(address), port), localDestination) {} + }; } } diff --git a/libi2pd_client/I2PTunnel.cpp b/libi2pd_client/I2PTunnel.cpp index db0d0c04..ad4e14b8 100644 --- a/libi2pd_client/I2PTunnel.cpp +++ b/libi2pd_client/I2PTunnel.cpp @@ -31,7 +31,7 @@ namespace client } I2PTunnelConnection::I2PTunnelConnection (I2PService * owner, std::shared_ptr socket, - std::shared_ptr leaseSet, int port): + std::shared_ptr leaseSet, uint16_t port): I2PServiceHandler(owner), m_Socket (socket), m_RemoteEndpoint (socket->remote_endpoint ()), m_IsQuiet (true) { @@ -346,7 +346,12 @@ namespace client } } else + { + // insert incomplete line back + m_InHeader.clear (); + m_InHeader << line; break; + } } if (endOfHeader) @@ -392,7 +397,8 @@ namespace client while (!endOfHeader) { std::getline(m_InHeader, line); - if (!m_InHeader.fail ()) + if (m_InHeader.fail ()) break; + if (!m_InHeader.eof ()) { if (line == "\r") endOfHeader = true; else @@ -409,7 +415,7 @@ namespace client matched = true; break; } - if (matched) break; + if (matched) continue; // replace some headers if (!m_Host.empty () && boost::iequals (line.substr (0, 5), "Host:")) @@ -428,7 +434,12 @@ namespace client } } else + { + // insert incomplete line back + m_InHeader.clear (); + m_InHeader << line; break; + } } if (endOfHeader) @@ -475,7 +486,8 @@ namespace client while (!endOfHeader) { std::getline(m_InHeader, line); - if (!m_InHeader.fail ()) + if (m_InHeader.fail ()) break; + if (!m_InHeader.eof ()) { if (line == "\r") endOfHeader = true; else @@ -496,7 +508,12 @@ namespace client } } else + { + // insert incomplete line back + m_InHeader.clear (); + m_InHeader << line; break; + } } if (endOfHeader) @@ -564,7 +581,7 @@ namespace client { public: I2PClientTunnelHandler (I2PClientTunnel * parent, std::shared_ptr address, - int destinationPort, std::shared_ptr socket): + uint16_t destinationPort, std::shared_ptr socket): I2PServiceHandler(parent), m_Address(address), m_DestinationPort (destinationPort), m_Socket(socket) {}; void Handle(); @@ -572,7 +589,7 @@ namespace client private: void HandleStreamRequestComplete (std::shared_ptr stream); std::shared_ptr m_Address; - int m_DestinationPort; + uint16_t m_DestinationPort; std::shared_ptr m_Socket; }; @@ -613,7 +630,7 @@ namespace client } I2PClientTunnel::I2PClientTunnel (const std::string& name, const std::string& destination, - const std::string& address, int port, std::shared_ptr localDestination, int destinationPort): + const std::string& address, uint16_t port, std::shared_ptr localDestination, uint16_t destinationPort): TCPIPAcceptor (address, port, localDestination), m_Name (name), m_Destination (destination), m_DestinationPort (destinationPort), m_KeepAliveInterval (0) { @@ -688,12 +705,11 @@ namespace client } I2PServerTunnel::I2PServerTunnel (const std::string& name, const std::string& address, - int port, std::shared_ptr localDestination, int inport, bool gzip): + uint16_t port, std::shared_ptr localDestination, uint16_t inport, bool gzip): I2PService (localDestination), m_IsUniqueLocal(true), m_Name (name), m_Address (address), m_Port (port), m_IsAccessList (false) { - if (!inport) inport = port; - m_PortDestination = localDestination->GetStreamingDestination (inport); - if (!m_PortDestination) + m_PortDestination = localDestination->GetStreamingDestination (inport); + if (!m_PortDestination) // default destination m_PortDestination = localDestination->CreateStreamingDestination (inport, gzip); } @@ -853,8 +869,8 @@ namespace client } I2PServerTunnelHTTP::I2PServerTunnelHTTP (const std::string& name, const std::string& address, - int port, std::shared_ptr localDestination, - const std::string& host, int inport, bool gzip): + uint16_t port, std::shared_ptr localDestination, + const std::string& host, uint16_t inport, bool gzip): I2PServerTunnel (name, address, port, localDestination, inport, gzip), m_Host (host) { @@ -866,8 +882,8 @@ namespace client } I2PServerTunnelIRC::I2PServerTunnelIRC (const std::string& name, const std::string& address, - int port, std::shared_ptr localDestination, - const std::string& webircpass, int inport, bool gzip): + uint16_t port, std::shared_ptr localDestination, + const std::string& webircpass, uint16_t inport, bool gzip): I2PServerTunnel (name, address, port, localDestination, inport, gzip), m_WebircPass (webircpass) { diff --git a/libi2pd_client/I2PTunnel.h b/libi2pd_client/I2PTunnel.h index 4c7b2002..b94eb9e4 100644 --- a/libi2pd_client/I2PTunnel.h +++ b/libi2pd_client/I2PTunnel.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2022, The PurpleI2P Project +* Copyright (c) 2013-2023, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -41,7 +41,7 @@ namespace client public: I2PTunnelConnection (I2PService * owner, std::shared_ptr socket, - std::shared_ptr leaseSet, int port = 0); // to I2P + std::shared_ptr leaseSet, uint16_t port = 0); // to I2P I2PTunnelConnection (I2PService * owner, std::shared_ptr socket, std::shared_ptr stream); // to I2P using simplified API I2PTunnelConnection (I2PService * owner, std::shared_ptr stream, @@ -154,7 +154,7 @@ namespace client public: I2PClientTunnel (const std::string& name, const std::string& destination, - const std::string& address, int port, std::shared_ptr localDestination, int destinationPort = 0); + const std::string& address, uint16_t port, std::shared_ptr localDestination, uint16_t destinationPort = 0); ~I2PClientTunnel () {} void Start (); @@ -174,7 +174,7 @@ namespace client std::string m_Name, m_Destination; std::shared_ptr m_Address; - int m_DestinationPort; + uint16_t m_DestinationPort; uint32_t m_KeepAliveInterval; std::unique_ptr m_KeepAliveTimer; }; @@ -183,8 +183,8 @@ namespace client { public: - I2PServerTunnel (const std::string& name, const std::string& address, int port, - std::shared_ptr localDestination, int inport = 0, bool gzip = true); + I2PServerTunnel (const std::string& name, const std::string& address, uint16_t port, + std::shared_ptr localDestination, uint16_t inport = 0, bool gzip = true); void Start (); void Stop (); @@ -200,7 +200,7 @@ namespace client void SetLocalAddress (const std::string& localAddress); const std::string& GetAddress() const { return m_Address; } - int GetPort () const { return m_Port; }; + uint16_t GetPort () const { return m_Port; }; uint16_t GetLocalPort () const { return m_PortDestination->GetLocalPort (); }; const boost::asio::ip::tcp::endpoint& GetEndpoint () const { return m_Endpoint; } @@ -219,7 +219,7 @@ namespace client bool m_IsUniqueLocal; std::string m_Name, m_Address; - int m_Port; + uint16_t m_Port; boost::asio::ip::tcp::endpoint m_Endpoint; std::shared_ptr m_PortDestination; std::set m_AccessList; @@ -232,9 +232,9 @@ namespace client { public: - I2PServerTunnelHTTP (const std::string& name, const std::string& address, int port, + I2PServerTunnelHTTP (const std::string& name, const std::string& address, uint16_t port, std::shared_ptr localDestination, const std::string& host, - int inport = 0, bool gzip = true); + uint16_t inport = 0, bool gzip = true); private: @@ -249,9 +249,9 @@ namespace client { public: - I2PServerTunnelIRC (const std::string& name, const std::string& address, int port, + I2PServerTunnelIRC (const std::string& name, const std::string& address, uint16_t port, std::shared_ptr localDestination, const std::string& webircpass, - int inport = 0, bool gzip = true); + uint16_t inport = 0, bool gzip = true); private: diff --git a/libi2pd_client/MatchedDestination.cpp b/libi2pd_client/MatchedDestination.cpp index 1e2e8275..40752f5b 100644 --- a/libi2pd_client/MatchedDestination.cpp +++ b/libi2pd_client/MatchedDestination.cpp @@ -73,7 +73,8 @@ namespace client { auto pool = GetTunnelPool(); if(!pool || !pool->StandardSelectPeers(path, hops, inbound, - std::bind(&i2p::tunnel::TunnelPool::SelectNextHop, pool, std::placeholders::_1, std::placeholders::_2))) + std::bind(&i2p::tunnel::TunnelPool::SelectNextHop, pool, std::placeholders::_1, + std::placeholders::_2, std::placeholders::_3))) return false; // more here for outbound tunnels if(!inbound && m_RemoteLeaseSet) diff --git a/libi2pd_client/SAM.cpp b/libi2pd_client/SAM.cpp index 4e19990a..e4cbae8a 100644 --- a/libi2pd_client/SAM.cpp +++ b/libi2pd_client/SAM.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2023, The PurpleI2P Project +* Copyright (c) 2013-2024, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -360,7 +360,7 @@ namespace client if (type == eSAMSessionTypeUnknown) { // unknown style - SendI2PError("Unknown STYLE"); + SendSessionI2PError("Unknown STYLE"); return; } @@ -375,14 +375,14 @@ namespace client if (e) { // not an ip address - SendI2PError("Invalid IP Address in HOST"); + SendSessionI2PError("Invalid IP Address in HOST"); return; } auto port = std::stoi(params[SAM_PARAM_PORT]); if (port == -1) { - SendI2PError("Invalid port"); + SendSessionI2PError("Invalid port"); return; } forward = std::make_shared(addr, port); @@ -415,12 +415,17 @@ namespace client { session->UDPEndpoint = forward; auto dest = session->GetLocalDestination ()->CreateDatagramDestination (); + auto port = std::stoi(params[SAM_PARAM_PORT]); if (type == eSAMSessionTypeDatagram) dest->SetReceiver (std::bind (&SAMSocket::HandleI2PDatagramReceive, shared_from_this (), - std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5)); + std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5), + port + ); else // raw dest->SetRawReceiver (std::bind (&SAMSocket::HandleI2PRawDatagramReceive, shared_from_this (), - std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4)); + std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4), + port + ); } if (session->GetLocalDestination ()->IsReady ()) @@ -484,7 +489,7 @@ namespace client LogPrint (eLogDebug, "SAM: Stream connect: ", buf); if ( m_SocketType != eSAMSocketTypeUnknown) { - SendI2PError ("Socket already in use"); + SendSessionI2PError ("Socket already in use"); return; } std::map params; @@ -523,15 +528,20 @@ namespace client { if (addr->IsIdentHash ()) { - auto leaseSet = session->GetLocalDestination ()->FindLeaseSet(addr->identHash); - if (leaseSet) - Connect(leaseSet, session); - else + if (session->GetLocalDestination ()->GetIdentHash () != addr->identHash) { - session->GetLocalDestination ()->RequestDestination(addr->identHash, - std::bind(&SAMSocket::HandleConnectLeaseSetRequestComplete, - shared_from_this(), std::placeholders::_1)); + auto leaseSet = session->GetLocalDestination ()->FindLeaseSet(addr->identHash); + if (leaseSet) + Connect(leaseSet, session); + else + { + session->GetLocalDestination ()->RequestDestination(addr->identHash, + std::bind(&SAMSocket::HandleConnectLeaseSetRequestComplete, + shared_from_this(), std::placeholders::_1)); + } } + else + SendStreamCantReachPeer ("Can't connect to myself"); } else // B33 session->GetLocalDestination ()->RequestDestinationWithEncryptedLeaseSet (addr->blindedPublicKey, @@ -550,17 +560,22 @@ namespace client if (!session) session = m_Owner.FindSession(m_ID); if (session) { - m_SocketType = eSAMSocketTypeStream; - m_Stream = session->GetLocalDestination ()->CreateStream (remote); - if (m_Stream) + if (session->GetLocalDestination ()->SupportsEncryptionType (remote->GetEncryptionType ())) { - m_Stream->Send ((uint8_t *)m_Buffer, m_BufferOffset); // connect and send - m_BufferOffset = 0; - I2PReceive (); - SendMessageReply (SAM_STREAM_STATUS_OK, strlen(SAM_STREAM_STATUS_OK), false); + m_SocketType = eSAMSocketTypeStream; + m_Stream = session->GetLocalDestination ()->CreateStream (remote); + if (m_Stream) + { + m_Stream->Send ((uint8_t *)m_Buffer, m_BufferOffset); // connect and send + m_BufferOffset = 0; + I2PReceive (); + SendMessageReply (SAM_STREAM_STATUS_OK, strlen(SAM_STREAM_STATUS_OK), false); + } + else + SendMessageReply (SAM_STREAM_STATUS_INVALID_ID, strlen(SAM_STREAM_STATUS_INVALID_ID), true); } else - SendMessageReply (SAM_STREAM_STATUS_INVALID_ID, strlen(SAM_STREAM_STATUS_INVALID_ID), true); + SendStreamCantReachPeer ("Incompatible crypto"); } else SendMessageReply (SAM_STREAM_STATUS_INVALID_ID, strlen(SAM_STREAM_STATUS_INVALID_ID), true); @@ -573,7 +588,7 @@ namespace client else { LogPrint (eLogError, "SAM: Destination to connect not found"); - SendMessageReply (SAM_STREAM_STATUS_CANT_REACH_PEER, strlen(SAM_STREAM_STATUS_CANT_REACH_PEER), true); + SendStreamCantReachPeer ("LeaseSet not found"); } } @@ -582,7 +597,7 @@ namespace client LogPrint (eLogDebug, "SAM: Stream accept: ", buf); if ( m_SocketType != eSAMSocketTypeUnknown) { - SendI2PError ("Socket already in use"); + SendSessionI2PError ("Socket already in use"); return; } std::map params; @@ -598,9 +613,31 @@ namespace client if (!session->GetLocalDestination ()->IsAcceptingStreams ()) { m_IsAccepting = true; + SendMessageReply (SAM_STREAM_STATUS_OK, strlen(SAM_STREAM_STATUS_OK), false); session->GetLocalDestination ()->AcceptOnce (std::bind (&SAMSocket::HandleI2PAccept, shared_from_this (), std::placeholders::_1)); } - SendMessageReply (SAM_STREAM_STATUS_OK, strlen(SAM_STREAM_STATUS_OK), false); + else + { + auto ts = i2p::util::GetSecondsSinceEpoch (); + while (!session->acceptQueue.empty () && session->acceptQueue.front ().second + SAM_SESSION_MAX_ACCEPT_INTERVAL > ts) + { + auto socket = session->acceptQueue.front ().first; + session->acceptQueue.pop_front (); + if (socket) + m_Owner.GetService ().post (std::bind(&SAMSocket::TerminateClose, socket)); + } + if (session->acceptQueue.size () < SAM_SESSION_MAX_ACCEPT_QUEUE_SIZE) + { + // already accepting, queue up + SendMessageReply (SAM_STREAM_STATUS_OK, strlen(SAM_STREAM_STATUS_OK), false); + session->acceptQueue.push_back (std::make_pair(shared_from_this(), ts)); + } + else + { + LogPrint (eLogInfo, "SAM: Session ", m_ID, " accept queue is full ", session->acceptQueue.size ()); + SendStreamI2PError ("Already accepting"); + } + } } else SendMessageReply (SAM_STREAM_STATUS_INVALID_ID, strlen(SAM_STREAM_STATUS_INVALID_ID), true); @@ -620,26 +657,26 @@ namespace client } if (session->GetLocalDestination ()->IsAcceptingStreams ()) { - SendI2PError ("Already accepting"); + SendSessionI2PError ("Already accepting"); return; } auto it = params.find (SAM_PARAM_PORT); if (it == params.end ()) { - SendI2PError ("PORT is missing"); + SendSessionI2PError ("PORT is missing"); return; } auto port = std::stoi (it->second); if (port <= 0 || port >= 0xFFFF) { - SendI2PError ("Invalid PORT"); + SendSessionI2PError ("Invalid PORT"); return; } boost::system::error_code ec; auto ep = m_Socket.remote_endpoint (ec); if (ec) { - SendI2PError ("Socket error"); + SendSessionI2PError ("Socket error"); return; } ep.port (port); @@ -791,13 +828,13 @@ namespace client if (type == eSAMSessionTypeUnknown) { // unknown style - SendI2PError("Unsupported STYLE"); + SendSessionI2PError("Unsupported STYLE"); return; } auto fromPort = std::stoi(params[SAM_PARAM_FROM_PORT]); if (fromPort == -1) { - SendI2PError("Invalid from port"); + SendSessionI2PError("Invalid from port"); return; } auto subsession = std::make_shared(masterSession, id, type, fromPort); @@ -810,7 +847,7 @@ namespace client SendMessageReply (SAM_SESSION_CREATE_DUPLICATED_ID, strlen(SAM_SESSION_CREATE_DUPLICATED_ID), false); } else - SendI2PError ("Wrong session type"); + SendSessionI2PError ("Wrong session type"); } void SAMSocket::ProcessSessionRemove (char * buf, size_t len) @@ -832,20 +869,36 @@ namespace client SendSessionCreateReplyOk (); } else - SendI2PError ("Wrong session type"); + SendSessionI2PError ("Wrong session type"); } - void SAMSocket::SendI2PError(const std::string & msg) + void SAMSocket::SendReplyWithMessage (const char * reply, const std::string & msg) { - LogPrint (eLogError, "SAM: I2P error: ", msg); #ifdef _MSC_VER - size_t len = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_SESSION_STATUS_I2P_ERROR, msg.c_str()); + size_t len = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, reply, msg.c_str()); #else - size_t len = snprintf (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_SESSION_STATUS_I2P_ERROR, msg.c_str()); + size_t len = snprintf (m_Buffer, SAM_SOCKET_BUFFER_SIZE, reply, msg.c_str()); #endif SendMessageReply (m_Buffer, len, true); } + void SAMSocket::SendSessionI2PError(const std::string & msg) + { + LogPrint (eLogError, "SAM: Session I2P error: ", msg); + SendReplyWithMessage (SAM_SESSION_STATUS_I2P_ERROR, msg); + } + + void SAMSocket::SendStreamI2PError(const std::string & msg) + { + LogPrint (eLogError, "SAM: Stream I2P error: ", msg); + SendReplyWithMessage (SAM_STREAM_STATUS_I2P_ERROR, msg); + } + + void SAMSocket::SendStreamCantReachPeer(const std::string & msg) + { + SendReplyWithMessage (SAM_STREAM_STATUS_CANT_REACH_PEER, msg); + } + void SAMSocket::HandleNamingLookupLeaseSetRequestComplete (std::shared_ptr leaseSet, std::string name) { if (leaseSet) @@ -1040,16 +1093,27 @@ namespace client m_Stream = stream; context.GetAddressBook ().InsertFullAddress (stream->GetRemoteIdentity ()); auto session = m_Owner.FindSession (m_ID); - if (session) + if (session && !session->acceptQueue.empty ()) { - // find more pending acceptors - for (auto & it: m_Owner.ListSockets (m_ID)) - if (it->m_SocketType == eSAMSocketTypeAcceptor) + // pending acceptors + auto ts = i2p::util::GetSecondsSinceEpoch (); + while (!session->acceptQueue.empty () && session->acceptQueue.front ().second + SAM_SESSION_MAX_ACCEPT_INTERVAL > ts) + { + auto socket = session->acceptQueue.front ().first; + session->acceptQueue.pop_front (); + if (socket) + m_Owner.GetService ().post (std::bind(&SAMSocket::TerminateClose, socket)); + } + if (!session->acceptQueue.empty ()) + { + auto socket = session->acceptQueue.front ().first; + session->acceptQueue.pop_front (); + if (socket && socket->GetSocketType () == eSAMSocketTypeAcceptor) { - it->m_IsAccepting = true; - session->GetLocalDestination ()->AcceptOnce (std::bind (&SAMSocket::HandleI2PAccept, it, std::placeholders::_1)); - break; + socket->m_IsAccepting = true; + session->GetLocalDestination ()->AcceptOnce (std::bind (&SAMSocket::HandleI2PAccept, socket, std::placeholders::_1)); } + } } if (!m_IsSilent) { @@ -1217,7 +1281,7 @@ namespace client subsessions.clear (); } - SAMSubSession::SAMSubSession (std::shared_ptr master, const std::string& name, SAMSessionType type, int port): + SAMSubSession::SAMSubSession (std::shared_ptr master, const std::string& name, SAMSessionType type, uint16_t port): SAMSession (master->m_Bridge, name, type), masterSession (master), inPort (port) { if (Type == eSAMSessionTypeStream) @@ -1244,7 +1308,7 @@ namespace client // TODO: implement datagrams } - SAMBridge::SAMBridge (const std::string& address, int portTCP, int portUDP, bool singleThread): + SAMBridge::SAMBridge (const std::string& address, uint16_t portTCP, uint16_t portUDP, bool singleThread): RunnableService ("SAM"), m_IsSingleThread (singleThread), m_Acceptor (GetIOService (), boost::asio::ip::tcp::endpoint(boost::asio::ip::address::from_string(address), portTCP)), m_DatagramEndpoint (boost::asio::ip::address::from_string(address), (!portUDP) ? portTCP-1 : portUDP), m_DatagramSocket (GetIOService (), m_DatagramEndpoint), diff --git a/libi2pd_client/SAM.h b/libi2pd_client/SAM.h index 97ac0b8e..3ed8f00c 100644 --- a/libi2pd_client/SAM.h +++ b/libi2pd_client/SAM.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2023, The PurpleI2P Project +* Copyright (c) 2013-2024, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -31,6 +31,9 @@ namespace client const size_t SAM_SOCKET_BUFFER_SIZE = 8192; const int SAM_SOCKET_CONNECTION_MAX_IDLE = 3600; // in seconds const int SAM_SESSION_READINESS_CHECK_INTERVAL = 3; // in seconds + const size_t SAM_SESSION_MAX_ACCEPT_QUEUE_SIZE = 50; + const size_t SAM_SESSION_MAX_ACCEPT_INTERVAL = 3; // in seconds + const char SAM_HANDSHAKE[] = "HELLO VERSION"; const char SAM_HANDSHAKE_REPLY[] = "HELLO REPLY RESULT=OK VERSION=%s\n"; const char SAM_HANDSHAKE_NOVERSION[] = "HELLO REPLY RESULT=NOVERSION\n"; @@ -48,8 +51,8 @@ namespace client const char SAM_STREAM_STATUS_OK[] = "STREAM STATUS RESULT=OK\n"; const char SAM_STREAM_STATUS_INVALID_ID[] = "STREAM STATUS RESULT=INVALID_ID\n"; const char SAM_STREAM_STATUS_INVALID_KEY[] = "STREAM STATUS RESULT=INVALID_KEY\n"; - const char SAM_STREAM_STATUS_CANT_REACH_PEER[] = "STREAM STATUS RESULT=CANT_REACH_PEER\n"; - const char SAM_STREAM_STATUS_I2P_ERROR[] = "STREAM STATUS RESULT=I2P_ERROR\n"; + const char SAM_STREAM_STATUS_CANT_REACH_PEER[] = "STREAM STATUS RESULT=CANT_REACH_PEER MESSAGE=\"%s\"\n"; + const char SAM_STREAM_STATUS_I2P_ERROR[] = "STREAM STATUS RESULT=I2P_ERROR MESSAGE=\"%s\"\n"; const char SAM_STREAM_ACCEPT[] = "STREAM ACCEPT"; const char SAM_STREAM_FORWARD[] = "STREAM FORWARD"; const char SAM_DATAGRAM_SEND[] = "DATAGRAM SEND"; @@ -141,7 +144,10 @@ namespace client void ProcessNamingLookup (char * buf, size_t len); void ProcessSessionAdd (char * buf, size_t len); void ProcessSessionRemove (char * buf, size_t len); - void SendI2PError(const std::string & msg); + void SendReplyWithMessage (const char * reply, const std::string & msg); + void SendSessionI2PError(const std::string & msg); + void SendStreamI2PError(const std::string & msg); + void SendStreamCantReachPeer(const std::string & msg); size_t ProcessDatagramSend (char * buf, size_t len, const char * data); // from SAM 1.0 void ExtractParams (char * buf, std::map& params); @@ -188,7 +194,8 @@ namespace client std::string Name; SAMSessionType Type; std::shared_ptr UDPEndpoint; // TODO: move - + std::list, uint64_t> > acceptQueue; // socket, receive time in seconds + SAMSession (SAMBridge & parent, const std::string & name, SAMSessionType type); virtual ~SAMSession () {}; @@ -221,9 +228,9 @@ namespace client struct SAMSubSession: public SAMSession { std::shared_ptr masterSession; - int inPort; + uint16_t inPort; - SAMSubSession (std::shared_ptr master, const std::string& name, SAMSessionType type, int port); + SAMSubSession (std::shared_ptr master, const std::string& name, SAMSessionType type, uint16_t port); // implements SAMSession std::shared_ptr GetLocalDestination (); void StopLocalDestination (); @@ -233,7 +240,7 @@ namespace client { public: - SAMBridge (const std::string& address, int portTCP, int portUDP, bool singleThread); + SAMBridge (const std::string& address, uint16_t portTCP, uint16_t portUDP, bool singleThread); ~SAMBridge (); void Start (); diff --git a/libi2pd_client/SOCKS.cpp b/libi2pd_client/SOCKS.cpp index ca87e22a..3174bdb3 100644 --- a/libi2pd_client/SOCKS.cpp +++ b/libi2pd_client/SOCKS.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2020, The PurpleI2P Project +* Copyright (c) 2013-2024, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -19,6 +19,7 @@ #include "I2PTunnel.h" #include "I2PService.h" #include "util.h" +#include "Socks5.h" namespace i2p { @@ -27,10 +28,6 @@ namespace proxy static const size_t socks_buffer_size = 8192; static const size_t max_socks_hostname_size = 255; // Limit for socks5 and bad idea to traverse - //static const size_t SOCKS_FORWARDER_BUFFER_SIZE = 8192; - - static const size_t SOCKS_UPSTREAM_SOCKS4A_REPLY_SIZE = 8; - struct SOCKSDnsAddress { uint8_t size; @@ -66,6 +63,11 @@ namespace proxy GET5_IPV6, GET5_HOST_SIZE, GET5_HOST, + GET5_USERPASSWD, + GET5_USER_SIZE, + GET5_USER, + GET5_PASSWD_SIZE, + GET5_PASSWD, READY, UPSTREAM_RESOLVE, UPSTREAM_CONNECT, @@ -127,8 +129,8 @@ namespace proxy boost::asio::const_buffers_1 GenerateSOCKS5SelectAuth(authMethods method); boost::asio::const_buffers_1 GenerateSOCKS4Response(errTypes error, uint32_t ip, uint16_t port); boost::asio::const_buffers_1 GenerateSOCKS5Response(errTypes error, addrTypes type, const address &addr, uint16_t port); - boost::asio::const_buffers_1 GenerateUpstreamRequest(); bool Socks5ChooseAuth(); + void Socks5UserPasswdResponse (); void SocksRequestFailed(errTypes error); void SocksRequestSuccess(); void SentSocksFailed(const boost::system::error_code & ecode); @@ -137,12 +139,11 @@ namespace proxy void HandleStreamRequestComplete (std::shared_ptr stream); void ForwardSOCKS(); - void SocksUpstreamSuccess(); + template + void SocksUpstreamSuccess(std::shared_ptr& upstreamSock); void AsyncUpstreamSockRead(); - void SendUpstreamRequest(); - void HandleUpstreamData(uint8_t * buff, std::size_t len); - void HandleUpstreamSockSend(const boost::system::error_code & ecode, std::size_t bytes_transfered); - void HandleUpstreamSockRecv(const boost::system::error_code & ecode, std::size_t bytes_transfered); + template + void SendUpstreamRequest(std::shared_ptr& upstreamSock); void HandleUpstreamConnected(const boost::system::error_code & ecode, boost::asio::ip::tcp::resolver::iterator itr); void HandleUpstreamResolved(const boost::system::error_code & ecode, @@ -151,13 +152,13 @@ namespace proxy boost::asio::ip::tcp::resolver m_proxy_resolver; uint8_t m_sock_buff[socks_buffer_size]; std::shared_ptr m_sock, m_upstreamSock; +#if defined(BOOST_ASIO_HAS_LOCAL_SOCKETS) + std::shared_ptr m_upstreamLocalSock; +#endif std::shared_ptr m_stream; uint8_t *m_remaining_data; //Data left to be sent uint8_t *m_remaining_upstream_data; //upstream data left to be forwarded uint8_t m_response[7+max_socks_hostname_size]; - uint8_t m_upstream_response[SOCKS_UPSTREAM_SOCKS4A_REPLY_SIZE]; - uint8_t m_upstream_request[14+max_socks_hostname_size]; - std::size_t m_upstream_response_len; address m_address; //Address std::size_t m_remaining_data_len; //Size of the data left to be sent uint32_t m_4aip; //Used in 4a requests @@ -216,6 +217,14 @@ namespace proxy m_upstreamSock->close(); m_upstreamSock = nullptr; } +#if defined(BOOST_ASIO_HAS_LOCAL_SOCKETS) + if (m_upstreamLocalSock) + { + LogPrint(eLogDebug, "SOCKS: Closing upstream local socket"); + m_upstreamLocalSock->close(); + m_upstreamLocalSock = nullptr; + } +#endif if (m_stream) { LogPrint(eLogDebug, "SOCKS: Closing stream"); @@ -274,37 +283,6 @@ namespace proxy return boost::asio::const_buffers_1(m_response, size); } - boost::asio::const_buffers_1 SOCKSHandler::GenerateUpstreamRequest() - { - size_t upstreamRequestSize = 0; - // TODO: negotiate with upstream - // SOCKS 4a - m_upstream_request[0] = '\x04'; //version - m_upstream_request[1] = m_cmd; - htobe16buf(m_upstream_request + 2, m_port); - m_upstream_request[4] = 0; - m_upstream_request[5] = 0; - m_upstream_request[6] = 0; - m_upstream_request[7] = 1; - // user id - m_upstream_request[8] = 'i'; - m_upstream_request[9] = '2'; - m_upstream_request[10] = 'p'; - m_upstream_request[11] = 'd'; - m_upstream_request[12] = 0; - upstreamRequestSize += 13; - if (m_address.dns.size <= max_socks_hostname_size - ( upstreamRequestSize + 1) ) { - // bounds check okay - memcpy(m_upstream_request + upstreamRequestSize, m_address.dns.value, m_address.dns.size); - upstreamRequestSize += m_address.dns.size; - // null terminate - m_upstream_request[++upstreamRequestSize] = 0; - } else { - LogPrint(eLogError, "SOCKS: BUG!!! m_addr.dns.sizs > max_socks_hostname - ( upstreamRequestSize + 1 ) )"); - } - return boost::asio::const_buffers_1(m_upstream_request, upstreamRequestSize); - } - bool SOCKSHandler::Socks5ChooseAuth() { m_response[0] = '\x05'; // Version @@ -324,6 +302,15 @@ namespace proxy } } + void SOCKSHandler::Socks5UserPasswdResponse () + { + m_response[0] = 1; // Version of the subnegotiation + m_response[1] = 0; // Response code + LogPrint(eLogDebug, "SOCKS: v5 user/password response"); + boost::asio::async_write(*m_sock, boost::asio::const_buffers_1(m_response, 2), + std::bind(&SOCKSHandler::SentSocksResponse, shared_from_this(), std::placeholders::_1)); + } + /* All hope is lost beyond this point */ void SOCKSHandler::SocksRequestFailed(SOCKSHandler::errTypes error) { @@ -438,10 +425,15 @@ namespace proxy m_parseleft --; if (*sock_buff == AUTH_NONE) m_authchosen = AUTH_NONE; + else if (*sock_buff == AUTH_USERPASSWD) + m_authchosen = AUTH_USERPASSWD; if ( m_parseleft == 0 ) { if (!Socks5ChooseAuth()) return false; - EnterState(GET5_REQUESTV); + if (m_authchosen == AUTH_USERPASSWD) + EnterState(GET5_USERPASSWD); + else + EnterState(GET5_REQUESTV); } break; case GET_COMMAND: @@ -557,6 +549,44 @@ namespace proxy m_parseleft--; if (m_parseleft == 0) EnterState(GET_PORT); break; + case GET5_USERPASSWD: + if (*sock_buff != 1) + { + LogPrint(eLogError,"SOCKS: v5 rejected invalid username/password subnegotiation: ", ((int)*sock_buff)); + SocksRequestFailed(SOCKS5_GEN_FAIL); + return false; + } + EnterState(GET5_USER_SIZE); + break; + case GET5_USER_SIZE: + if (*sock_buff) + EnterState(GET5_USER, *sock_buff); + else // empty user + EnterState(GET5_PASSWD_SIZE); + break; + case GET5_USER: + // skip user for now + m_parseleft--; + if (m_parseleft == 0) EnterState(GET5_PASSWD_SIZE); + break; + case GET5_PASSWD_SIZE: + if (*sock_buff) + EnterState(GET5_PASSWD, *sock_buff); + else // empty password + { + Socks5UserPasswdResponse (); + EnterState(GET5_REQUESTV); + } + break; + case GET5_PASSWD: + // skip passwd for now + m_parseleft--; + if (m_parseleft == 0) + { + Socks5UserPasswdResponse (); + EnterState(GET5_REQUESTV); + } + break; default: LogPrint(eLogError, "SOCKS: Parse state?? ", m_state); Terminate(); @@ -660,39 +690,45 @@ namespace proxy void SOCKSHandler::ForwardSOCKS() { LogPrint(eLogInfo, "SOCKS: Forwarding to upstream"); - EnterState(UPSTREAM_RESOLVE); - boost::asio::ip::tcp::resolver::query q(m_UpstreamProxyAddress, std::to_string(m_UpstreamProxyPort)); - m_proxy_resolver.async_resolve(q, std::bind(&SOCKSHandler::HandleUpstreamResolved, shared_from_this(), - std::placeholders::_1, std::placeholders::_2)); - } - - void SOCKSHandler::AsyncUpstreamSockRead() - { - LogPrint(eLogDebug, "SOCKS: Async upstream sock read"); - if (m_upstreamSock) { - m_upstreamSock->async_read_some(boost::asio::buffer(m_upstream_response, SOCKS_UPSTREAM_SOCKS4A_REPLY_SIZE), - std::bind(&SOCKSHandler::HandleUpstreamSockRecv, shared_from_this(), std::placeholders::_1, std::placeholders::_2)); - } else { - LogPrint(eLogError, "SOCKS: No upstream socket for read"); - SocksRequestFailed(SOCKS5_GEN_FAIL); + if (m_UpstreamProxyPort) // TCP + { + EnterState(UPSTREAM_RESOLVE); + boost::asio::ip::tcp::resolver::query q(m_UpstreamProxyAddress, std::to_string(m_UpstreamProxyPort)); + m_proxy_resolver.async_resolve(q, std::bind(&SOCKSHandler::HandleUpstreamResolved, shared_from_this(), + std::placeholders::_1, std::placeholders::_2)); + } + else if (!m_UpstreamProxyAddress.empty ())// local + { +#if defined(BOOST_ASIO_HAS_LOCAL_SOCKETS) + EnterState(UPSTREAM_CONNECT); + m_upstreamLocalSock = std::make_shared(GetOwner()->GetService()); + auto s = shared_from_this (); + m_upstreamLocalSock->async_connect(m_UpstreamProxyAddress, + [s](const boost::system::error_code& ecode) + { + if (ecode) + { + LogPrint(eLogWarning, "SOCKS: Could not connect to local upstream proxy: ", ecode.message()); + s->SocksRequestFailed(SOCKS5_NET_UNREACH); + return; + } + LogPrint(eLogInfo, "SOCKS: Connected to local upstream proxy"); + s->SendUpstreamRequest(s->m_upstreamLocalSock); + }); +#else + LogPrint(eLogError, "SOCKS: Local sockets for upstream proxy not supported"); + SocksRequestFailed(SOCKS5_ADDR_UNSUP); +#endif } + else + { + LogPrint(eLogError, "SOCKS: Incorrect upstream proxy address"); + SocksRequestFailed(SOCKS5_ADDR_UNSUP); + } } - void SOCKSHandler::HandleUpstreamSockRecv(const boost::system::error_code & ecode, std::size_t bytes_transfered) - { - if (ecode) { - if (m_state == UPSTREAM_HANDSHAKE ) { - // we are trying to handshake but it failed - SocksRequestFailed(SOCKS5_NET_UNREACH); - } else { - LogPrint(eLogError, "SOCKS: Bad state when reading from upstream: ", (int) m_state); - } - return; - } - HandleUpstreamData(m_upstream_response, bytes_transfered); - } - - void SOCKSHandler::SocksUpstreamSuccess() + template + void SOCKSHandler::SocksUpstreamSuccess(std::shared_ptr& upstreamSock) { LogPrint(eLogInfo, "SOCKS: Upstream success"); boost::asio::const_buffers_1 response(nullptr, 0); @@ -709,55 +745,36 @@ namespace proxy break; } m_sock->send(response); - auto forwarder = std::make_shared(GetOwner(), m_sock, m_upstreamSock); - m_upstreamSock = nullptr; + auto forwarder = CreateSocketsPipe (GetOwner(), m_sock, upstreamSock); + upstreamSock = nullptr; m_sock = nullptr; GetOwner()->AddHandler(forwarder); forwarder->Start(); Terminate(); - } - void SOCKSHandler::HandleUpstreamData(uint8_t * dataptr, std::size_t len) - { - if (m_state == UPSTREAM_HANDSHAKE) { - m_upstream_response_len += len; - // handle handshake data - if (m_upstream_response_len < SOCKS_UPSTREAM_SOCKS4A_REPLY_SIZE) { - // too small, continue reading - AsyncUpstreamSockRead(); - } else if (len == SOCKS_UPSTREAM_SOCKS4A_REPLY_SIZE) { - // just right - uint8_t resp = m_upstream_response[1]; - if (resp == SOCKS4_OK) { - // we have connected ! - SocksUpstreamSuccess(); - } else { - // upstream failure - LogPrint(eLogError, "SOCKS: Upstream proxy failure: ", (int) resp); - // TODO: runtime error? - SocksRequestFailed(SOCKS5_GEN_FAIL); - } - } else { - // too big - SocksRequestFailed(SOCKS5_GEN_FAIL); - } - } else { - // invalid state - LogPrint(eLogError, "SOCKS: Invalid state reading from upstream: ", (int) m_state); - } - } - - void SOCKSHandler::SendUpstreamRequest() + template + void SOCKSHandler::SendUpstreamRequest(std::shared_ptr& upstreamSock) { LogPrint(eLogInfo, "SOCKS: Negotiating with upstream proxy"); EnterState(UPSTREAM_HANDSHAKE); - if (m_upstreamSock) { - boost::asio::write(*m_upstreamSock, GenerateUpstreamRequest()); - AsyncUpstreamSockRead(); - } else { + if (upstreamSock) + { + auto s = shared_from_this (); + i2p::transport::Socks5Handshake (*upstreamSock, std::make_pair(m_address.dns.ToString (), m_port), + [s, &upstreamSock](const boost::system::error_code& ec) + { + if (!ec) + s->SocksUpstreamSuccess(upstreamSock); + else + { + s->SocksRequestFailed(SOCKS5_NET_UNREACH); + LogPrint(eLogError, "SOCKS: Upstream proxy failure: ", ec.message ()); + } + }); + } + else LogPrint(eLogError, "SOCKS: No upstream socket to send handshake to"); - } } void SOCKSHandler::HandleUpstreamConnected(const boost::system::error_code & ecode, boost::asio::ip::tcp::resolver::iterator itr) @@ -768,7 +785,7 @@ namespace proxy return; } LogPrint(eLogInfo, "SOCKS: Connected to upstream proxy"); - SendUpstreamRequest(); + SendUpstreamRequest(m_upstreamSock); } void SOCKSHandler::HandleUpstreamResolved(const boost::system::error_code & ecode, boost::asio::ip::tcp::resolver::iterator itr) @@ -788,7 +805,7 @@ namespace proxy shared_from_this(), std::placeholders::_1, std::placeholders::_2)); } - SOCKSServer::SOCKSServer(const std::string& name, const std::string& address, int port, + SOCKSServer::SOCKSServer(const std::string& name, const std::string& address, uint16_t port, bool outEnable, const std::string& outAddress, uint16_t outPort, std::shared_ptr localDestination) : TCPIPAcceptor (address, port, localDestination ? localDestination : i2p::client::context.GetSharedLocalDestination ()), m_Name (name) diff --git a/libi2pd_client/SOCKS.h b/libi2pd_client/SOCKS.h index f41cfd72..bd88d6e6 100644 --- a/libi2pd_client/SOCKS.h +++ b/libi2pd_client/SOCKS.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2020, The PurpleI2P Project +* Copyright (c) 2013-2023, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -23,7 +23,7 @@ namespace proxy { public: - SOCKSServer(const std::string& name, const std::string& address, int port, bool outEnable, const std::string& outAddress, uint16_t outPort, + SOCKSServer(const std::string& name, const std::string& address, uint16_t port, bool outEnable, const std::string& outAddress, uint16_t outPort, std::shared_ptr localDestination = nullptr); ~SOCKSServer() {}; diff --git a/libi2pd_client/UDPTunnel.cpp b/libi2pd_client/UDPTunnel.cpp index 1e4b3d7c..cd17bbf0 100644 --- a/libi2pd_client/UDPTunnel.cpp +++ b/libi2pd_client/UDPTunnel.cpp @@ -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 * @@ -19,16 +19,22 @@ namespace client void I2PUDPServerTunnel::HandleRecvFromI2P(const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len) { if (!m_LastSession || m_LastSession->Identity.GetLL()[0] != from.GetIdentHash ().GetLL()[0] || fromPort != m_LastSession->RemotePort) - { - std::lock_guard lock(m_SessionsMutex); m_LastSession = ObtainUDPSession(from, toPort, fromPort); - } m_LastSession->IPSocket.send_to(boost::asio::buffer(buf, len), m_RemoteEndpoint); m_LastSession->LastActivity = i2p::util::GetMillisecondsSinceEpoch(); } - void I2PUDPServerTunnel::HandleRecvFromI2PRaw (uint16_t, uint16_t, const uint8_t * buf, size_t len) + void I2PUDPServerTunnel::HandleRecvFromI2PRaw (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len) { + if (m_LastSession && (fromPort != m_LastSession->RemotePort || toPort != m_LastSession->LocalPort)) + { + std::lock_guard lock(m_SessionsMutex); + auto it = m_Sessions.find (GetSessionIndex (fromPort, toPort)); + if (it != m_Sessions.end ()) + m_LastSession = it->second; + else + m_LastSession = nullptr; + } if (m_LastSession) { m_LastSession->IPSocket.send_to(boost::asio::buffer(buf, len), m_RemoteEndpoint); @@ -41,11 +47,12 @@ namespace client std::lock_guard lock(m_SessionsMutex); uint64_t now = i2p::util::GetMillisecondsSinceEpoch(); auto itr = m_Sessions.begin(); - while(itr != m_Sessions.end()) { - if(now - (*itr)->LastActivity >= delta ) + while(itr != m_Sessions.end()) + { + if(now - itr->second->LastActivity >= delta ) itr = m_Sessions.erase(itr); else - ++itr; + itr++; } } @@ -66,15 +73,25 @@ namespace client UDPSessionPtr I2PUDPServerTunnel::ObtainUDPSession(const i2p::data::IdentityEx& from, uint16_t localPort, uint16_t remotePort) { auto ih = from.GetIdentHash(); - for (auto & s : m_Sessions ) + auto idx = GetSessionIndex (remotePort, localPort); { - if (s->Identity.GetLL()[0] == ih.GetLL()[0] && remotePort == s->RemotePort) + std::lock_guard lock(m_SessionsMutex); + auto it = m_Sessions.find (idx); + if (it != m_Sessions.end ()) { - /** found existing session */ - LogPrint(eLogDebug, "UDPServer: Found session ", s->IPSocket.local_endpoint(), " ", ih.ToBase32()); - return s; + if (it->second->Identity.GetLL()[0] == ih.GetLL()[0]) + { + LogPrint(eLogDebug, "UDPServer: Found session ", it->second->IPSocket.local_endpoint(), " ", ih.ToBase32()); + return it->second; + } + else + { + LogPrint(eLogWarning, "UDPServer: Session with from ", remotePort, " and to ", localPort, " ports already exists. But from differend address. Removed"); + m_Sessions.erase (it); + } } } + boost::asio::ip::address addr; /** create new udp session */ if(m_IsUniqueLocal && m_LocalAddress.is_loopback()) @@ -84,10 +101,12 @@ namespace client } else addr = m_LocalAddress; - boost::asio::ip::udp::endpoint ep(addr, 0); - m_Sessions.push_back(std::make_shared(ep, m_LocalDest, m_RemoteEndpoint, ih, localPort, remotePort)); - auto & back = m_Sessions.back(); - return back; + + auto s = std::make_shared(boost::asio::ip::udp::endpoint(addr, 0), + m_LocalDest, m_RemoteEndpoint, ih, localPort, remotePort); + std::lock_guard lock(m_SessionsMutex); + m_Sessions.emplace (idx, s); + return s; } UDPSession::UDPSession(boost::asio::ip::udp::endpoint localEndpoint, @@ -144,9 +163,9 @@ namespace client } I2PUDPServerTunnel::I2PUDPServerTunnel (const std::string & name, std::shared_ptr localDestination, - const boost::asio::ip::address& localAddress, const boost::asio::ip::udp::endpoint& forwardTo, uint16_t port, bool gzip) : + const boost::asio::ip::address& localAddress, const boost::asio::ip::udp::endpoint& forwardTo, uint16_t inPort, bool gzip) : m_IsUniqueLocal (true), m_Name (name), m_LocalAddress (localAddress), - m_RemoteEndpoint (forwardTo), m_LocalDest (localDestination), m_Gzip (gzip) + m_RemoteEndpoint (forwardTo), m_LocalDest (localDestination), m_inPort(inPort), m_Gzip (gzip) { } @@ -160,14 +179,23 @@ namespace client m_LocalDest->Start (); auto dgram = m_LocalDest->CreateDatagramDestination (m_Gzip); - dgram->SetReceiver (std::bind (&I2PUDPServerTunnel::HandleRecvFromI2P, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5)); - dgram->SetRawReceiver (std::bind (&I2PUDPServerTunnel::HandleRecvFromI2PRaw, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4)); + dgram->SetReceiver ( + std::bind (&I2PUDPServerTunnel::HandleRecvFromI2P, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5), + m_inPort + ); + dgram->SetRawReceiver ( + std::bind (&I2PUDPServerTunnel::HandleRecvFromI2PRaw, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4), + m_inPort + ); } void I2PUDPServerTunnel::Stop () { auto dgram = m_LocalDest->GetDatagramDestination (); - if (dgram) dgram->ResetReceiver (); + if (dgram) { + dgram->ResetReceiver (m_inPort); + dgram->ResetRawReceiver (m_inPort); + } } std::vector > I2PUDPServerTunnel::GetSessions () @@ -175,8 +203,9 @@ namespace client std::vector > sessions; std::lock_guard lock (m_SessionsMutex); - for (UDPSessionPtr s: m_Sessions) + for (auto it: m_Sessions) { + auto s = it.second; if (!s->m_Destination) continue; auto info = s->m_Destination->GetInfoForRemote (s->Identity); if (!info) continue; @@ -220,9 +249,13 @@ namespace client dgram->SetReceiver (std::bind (&I2PUDPClientTunnel::HandleRecvFromI2P, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, - std::placeholders::_5)); + std::placeholders::_5), + RemotePort + ); dgram->SetRawReceiver (std::bind (&I2PUDPClientTunnel::HandleRecvFromI2PRaw, this, - std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4)); + std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4), + RemotePort + ); m_LocalDest->Start (); if (m_ResolveThread == nullptr) @@ -233,7 +266,10 @@ namespace client void I2PUDPClientTunnel::Stop () { auto dgram = m_LocalDest->GetDatagramDestination (); - if (dgram) dgram->ResetReceiver (); + if (dgram) { + dgram->ResetReceiver (RemotePort); + dgram->ResetRawReceiver (RemotePort); + } m_cancel_resolve = true; m_Sessions.clear(); diff --git a/libi2pd_client/UDPTunnel.h b/libi2pd_client/UDPTunnel.h index 862ce216..5650124c 100644 --- a/libi2pd_client/UDPTunnel.h +++ b/libi2pd_client/UDPTunnel.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2022, The PurpleI2P Project +* Copyright (c) 2013-2024, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -72,7 +72,7 @@ namespace client boost::asio::ip::udp::endpoint LocalEndpoint; /** client's udp endpoint */ boost::asio::ip::udp::endpoint RemoteEndpoint; - /** how long has this converstation been idle in ms */ + /** how long has this conversation been idle in ms */ uint64_t idle; }; @@ -104,6 +104,7 @@ namespace client void HandleRecvFromI2P (const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len); void HandleRecvFromI2PRaw (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len); UDPSessionPtr ObtainUDPSession (const i2p::data::IdentityEx& from, uint16_t localPort, uint16_t remotePort); + uint32_t GetSessionIndex (uint16_t fromPort, uint16_t toPort) const { return ((uint32_t)fromPort << 16) + toPort; } private: @@ -112,9 +113,10 @@ namespace client boost::asio::ip::address m_LocalAddress; boost::asio::ip::udp::endpoint m_RemoteEndpoint; std::mutex m_SessionsMutex; - std::vector m_Sessions; + std::unordered_map m_Sessions; // (from port, to port)->session std::shared_ptr m_LocalDest; UDPSessionPtr m_LastSession; + uint16_t m_inPort; bool m_Gzip; public: diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 21daadd9..de319f5d 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -65,6 +65,10 @@ SET(test-elligator_SRCS test-elligator.cpp ) +set(test-eddsa_SRCS + test-eddsa.cpp +) + add_executable(test-http-merge_chunked ${test-http-merge_chunked_SRCS}) add_executable(test-http-req ${test-http-req_SRCS}) add_executable(test-http-res ${test-http-res_SRCS}) @@ -77,6 +81,7 @@ add_executable(test-x25519 ${test-x25519_SRCS}) add_executable(test-aeadchacha20poly1305 ${test-aeadchacha20poly1305_SRCS}) add_executable(test-blinding ${test-blinding_SRCS}) add_executable(test-elligator ${test-elligator_SRCS}) +add_executable(test-eddsa ${test-eddsa_SRCS}) set(LIBS libi2pd @@ -101,6 +106,7 @@ target_link_libraries(test-x25519 ${LIBS}) target_link_libraries(test-aeadchacha20poly1305 ${LIBS}) target_link_libraries(test-blinding ${LIBS}) target_link_libraries(test-elligator ${LIBS}) +target_link_libraries(test-eddsa ${LIBS}) add_test(test-http-merge_chunked ${TEST_PATH}/test-http-merge_chunked) add_test(test-http-req ${TEST_PATH}/test-http-req) @@ -114,3 +120,4 @@ add_test(test-x25519 ${TEST_PATH}/test-x25519) add_test(test-aeadchacha20poly1305 ${TEST_PATH}/test-aeadchacha20poly1305) add_test(test-blinding ${TEST_PATH}/test-blinding) add_test(test-elligator ${TEST_PATH}/test-elligator) +add_test(test-eddsa ${TEST_PATH}/test-eddsa) diff --git a/tests/Makefile b/tests/Makefile index 9c5711e2..7c44e467 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -7,7 +7,8 @@ LIBI2PD = ../libi2pd.a TESTS = \ test-http-merge_chunked test-http-req test-http-res test-http-url test-http-url_decode \ - test-gost test-gost-sig test-base-64 test-x25519 test-aeadchacha20poly1305 test-blinding test-elligator + test-gost test-gost-sig test-base-64 test-x25519 test-aeadchacha20poly1305 test-blinding \ + test-elligator test-eddsa ifneq (, $(findstring mingw, $(SYS))$(findstring windows-gnu, $(SYS))$(findstring cygwin, $(SYS))) CXXFLAGS += -DWIN32_LEAN_AND_MEAN @@ -55,6 +56,9 @@ test-blinding: test-blinding.cpp $(LIBI2PD) test-elligator: test-elligator.cpp $(LIBI2PD) $(CXX) $(CXXFLAGS) $(NEEDED_CXXFLAGS) $(INCFLAGS) $(LDFLAGS) -o $@ $^ $(LDLIBS) +test-eddsa: test-eddsa.cpp $(LIBI2PD) + $(CXX) $(CXXFLAGS) $(NEEDED_CXXFLAGS) $(INCFLAGS) $(LDFLAGS) -o $@ $^ $(LDLIBS) + run: $(TESTS) @for TEST in $(TESTS); do echo Running $$TEST; ./$$TEST ; done diff --git a/tests/test-eddsa.cpp b/tests/test-eddsa.cpp new file mode 100644 index 00000000..b3895e2b --- /dev/null +++ b/tests/test-eddsa.cpp @@ -0,0 +1,68 @@ +#include +#include +#include + +#include "Signature.h" + +// TEST 1024 from RFC-8032 + +int main () +{ + uint8_t key[32], pub[32], msg[1024], sig[64]; + BIGNUM * input = BN_new(); + BN_hex2bn(&input, "f5e5767cf153319517630f226876b86c8160cc583bc013744c6bf255f5cc0ee5"); + BN_bn2bin(input, key); + BN_hex2bn(&input, + "08b8b2b733424243760fe426a4b54908632110a66c2f6591eabd3345e3e4eb98" + "fa6e264bf09efe12ee50f8f54e9f77b1e355f6c50544e23fb1433ddf73be84d8" + "79de7c0046dc4996d9e773f4bc9efe5738829adb26c81b37c93a1b270b20329d" + "658675fc6ea534e0810a4432826bf58c941efb65d57a338bbd2e26640f89ffbc" + "1a858efcb8550ee3a5e1998bd177e93a7363c344fe6b199ee5d02e82d522c4fe" + "ba15452f80288a821a579116ec6dad2b3b310da903401aa62100ab5d1a36553e" + "06203b33890cc9b832f79ef80560ccb9a39ce767967ed628c6ad573cb116dbef" + "efd75499da96bd68a8a97b928a8bbc103b6621fcde2beca1231d206be6cd9ec7" + "aff6f6c94fcd7204ed3455c68c83f4a41da4af2b74ef5c53f1d8ac70bdcb7ed1" + "85ce81bd84359d44254d95629e9855a94a7c1958d1f8ada5d0532ed8a5aa3fb2" + "d17ba70eb6248e594e1a2297acbbb39d502f1a8c6eb6f1ce22b3de1a1f40cc24" + "554119a831a9aad6079cad88425de6bde1a9187ebb6092cf67bf2b13fd65f270" + "88d78b7e883c8759d2c4f5c65adb7553878ad575f9fad878e80a0c9ba63bcbcc" + "2732e69485bbc9c90bfbd62481d9089beccf80cfe2df16a2cf65bd92dd597b07" + "07e0917af48bbb75fed413d238f5555a7a569d80c3414a8d0859dc65a46128ba" + "b27af87a71314f318c782b23ebfe808b82b0ce26401d2e22f04d83d1255dc51a" + "ddd3b75a2b1ae0784504df543af8969be3ea7082ff7fc9888c144da2af58429e" + "c96031dbcad3dad9af0dcbaaaf268cb8fcffead94f3c7ca495e056a9b47acdb7" + "51fb73e666c6c655ade8297297d07ad1ba5e43f1bca32301651339e22904cc8c" + "42f58c30c04aafdb038dda0847dd988dcda6f3bfd15c4b4c4525004aa06eeff8" + "ca61783aacec57fb3d1f92b0fe2fd1a85f6724517b65e614ad6808d6f6ee34df" + "f7310fdc82aebfd904b01e1dc54b2927094b2db68d6f903b68401adebf5a7e08" + "d78ff4ef5d63653a65040cf9bfd4aca7984a74d37145986780fc0b16ac451649" + "de6188a7dbdf191f64b5fc5e2ab47b57f7f7276cd419c17a3ca8e1b939ae49e4" + "88acba6b965610b5480109c8b17b80e1b7b750dfc7598d5d5011fd2dcc5600a3" + "2ef5b52a1ecc820e308aa342721aac0943bf6686b64b2579376504ccc493d97e" + "6aed3fb0f9cd71a43dd497f01f17c0e2cb3797aa2a2f256656168e6c496afc5f" + "b93246f6b1116398a346f1a641f3b041e989f7914f90cc2c7fff357876e506b5" + "0d334ba77c225bc307ba537152f3f1610e4eafe595f6d9d90d11faa933a15ef1" + "369546868a7f3a45a96768d40fd9d03412c091c6315cf4fde7cb68606937380d" + "b2eaaa707b4c4185c32eddcdd306705e4dc1ffc872eeee475a64dfac86aba41c" + "0618983f8741c5ef68d3a101e8a3b8cac60c905c15fc910840b94c00a0b9d0" + ); + BN_bn2bin(input, msg); + BN_hex2bn(&input, + "0aab4c900501b3e24d7cdf4663326a3a87df5e4843b2cbdb67cbf6e460fec350" + "aa5371b1508f9f4528ecea23c436d94b5e8fcd4f681e30a6ac00a9704a188a03"); + BN_bn2bin(input, sig); + BN_hex2bn(&input, + "278117fc144c72340f67d0f2316e8386ceffbf2b2428c9c51fef7c597f1d426e"); + BN_bn2bin(input, pub); + + uint8_t s[64]; + i2p::crypto::EDDSA25519Signer signer (key); + signer.Sign (msg, 1023, s); +#if OPENSSL_EDDSA + assert(memcmp (s, sig, 64) == 0); +#endif + + i2p::crypto::EDDSA25519Verifier verifier; + verifier.SetPublicKey (pub); + assert(verifier.Verify (msg, 1023, s)); +}