diff --git a/.github/workflows/build-windows.yml b/.github/workflows/build-windows.yml index 6f10e62b..d587ba05 100644 --- a/.github/workflows/build-windows.yml +++ b/.github/workflows/build-windows.yml @@ -133,8 +133,6 @@ jobs: git clone https://github.com/msys2/MINGW-packages cd MINGW-packages git checkout 4cbb366edf2f268ac3146174b40ce38604646fc5 mingw-w64-boost - cd mingw-w64-boost - sed -i 's/boostorg.jfrog.io\/artifactory\/main/archives.boost.io/' PKGBUILD # headers - name: Get headers package version @@ -232,8 +230,6 @@ jobs: run: | cd MINGW-packages/mingw-w64-boost MINGW_ARCH=mingw32 makepkg-mingw -sCLf --noconfirm --nocheck - - name: Remove boost packages - run: pacman --noconfirm -R mingw-w64-i686-boost mingw-w64-i686-boost-libs - name: Install boost package run: pacman --noconfirm -U MINGW-packages/mingw-w64-boost/mingw-w64-i686-*-any.pkg.tar.zst diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index c6d55664..34923f31 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -108,7 +108,7 @@ jobs: uses: Noelware/docker-manifest-action@master with: inputs: purplei2p/i2pd:latest - tags: purplei2p/i2pd:latest-amd64,purplei2p/i2pd:latest-i386,purplei2p/i2pd:latest-arm64,purplei2p/i2pd:latest-armv7 + images: purplei2p/i2pd:latest-amd64,purplei2p/i2pd:latest-i386,purplei2p/i2pd:latest-arm64,purplei2p/i2pd:latest-armv7 push: true - name: Create and push latest manifest image to GHCR @@ -116,7 +116,7 @@ jobs: uses: Noelware/docker-manifest-action@master with: inputs: ghcr.io/purplei2p/i2pd:latest - tags: ghcr.io/purplei2p/i2pd:latest-amd64,ghcr.io/purplei2p/i2pd:latest-i386,ghcr.io/purplei2p/i2pd:latest-arm64,ghcr.io/purplei2p/i2pd:latest-armv7 + images: ghcr.io/purplei2p/i2pd:latest-amd64,ghcr.io/purplei2p/i2pd:latest-i386,ghcr.io/purplei2p/i2pd:latest-arm64,ghcr.io/purplei2p/i2pd:latest-armv7 push: true - name: Store release version to env @@ -128,7 +128,7 @@ jobs: uses: Noelware/docker-manifest-action@master with: inputs: purplei2p/i2pd:latest,purplei2p/i2pd:latest-release,purplei2p/i2pd:release-${{ env.RELEASE_VERSION }} - tags: purplei2p/i2pd:latest-amd64,purplei2p/i2pd:latest-i386,purplei2p/i2pd:latest-arm64,purplei2p/i2pd:latest-armv7 + images: purplei2p/i2pd:latest-amd64,purplei2p/i2pd:latest-i386,purplei2p/i2pd:latest-arm64,purplei2p/i2pd:latest-armv7 push: true - name: Create and push release manifest to GHCR @@ -136,5 +136,5 @@ jobs: uses: Noelware/docker-manifest-action@master with: inputs: ghcr.io/purplei2p/i2pd:latest,ghcr.io/purplei2p/i2pd:latest-release,ghcr.io/purplei2p/i2pd:release-${{ env.RELEASE_VERSION }} - tags: ghcr.io/purplei2p/i2pd:latest-amd64,ghcr.io/purplei2p/i2pd:latest-i386,ghcr.io/purplei2p/i2pd:latest-arm64,ghcr.io/purplei2p/i2pd:latest-armv7 + images: ghcr.io/purplei2p/i2pd:latest-amd64,ghcr.io/purplei2p/i2pd:latest-i386,ghcr.io/purplei2p/i2pd:latest-arm64,ghcr.io/purplei2p/i2pd:latest-armv7 push: true diff --git a/ChangeLog b/ChangeLog index 23864c0e..b3559fb7 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,70 +1,6 @@ # for this file format description, # see https://github.com/olivierlacan/keep-a-changelog -## [2.56.0] - 2025-02-11 -### Added -- Config params for shared local destination -- AddressBook full addresses cache -- Decline transit tunnel to duplicated router -- Recreate tunnels in random order -### Changed -- Exclude disk operations from SSU2 and NTCP2 threads -- Set minimal version for peer test to 0.9.62 -- Send ack requested flag after second SSU2 resend attempt -- Shorter ECIESx25519 ack request interval for datagram and I2CP sessions -- Don't change datagram routing path too often if unidirectional data stream -- Reduce LeaseSet and local RouterInfo publishing confirmation intervals -- Don't delete buffer of connected routers or if an update received -- Smaller RouterInfo request timeout if sent directly -- Persist local RouterInfo in separate thread -- Don't recalculate and process ranges for every SSU2 Ack block -- Reseeds list -### Fixed -- Termination deadlock if SAM session is active -- Race condition at tunnel endpoint -- Inbound tunnel build encryption - -## [2.55.0] - 2024-12-30 -### Added -- Support boost 1.87 -- "i2p.streaming.maxConcurrentStreams" tunnel's param to limit number of simultaneous streams -- Separate thread for tunnel build requests -- Show next peer and connectivity on "Transit tunnels" page -- Tunnel name for local destination thread -- Throttle incoming ECIESx25519 sessions -- Send tunnel data to transport session directly if possible -- Publish 'R' cap for yggdrasil-only routers, and 'U' cap for routers through proxy -- Random tunnel rejection when medium congestion -- Save unreachable router's endpoint to use it next time without introducers -- Recognize symmetric NAT from peer test message 7 -- Resend HolePunch and RelayResponse messages -### Changed -- Removed own implementation of AESNI and always use one from openssl -- Renamed main thread to i2pd-daemon -- Set i2p.streaming.profile=2 for shared local destination -- Reduced LeaseSet and RouterInfo lookup timeouts -- Cleanup ECIES sessions and tags more often -- Check LeaseSet expiration time -- Handle NTCP2 session handshakes in separate thread -- Limit last decline time by 1.5 hours in router's profile -- Don't handle RelayRequest and RelayIntro with same nonce twice -- Increased hole punch expiration interval -- Send peer test message 6 with delay if message 4 was received before message 5 -- Pre-calculate more x25519 keys for transports in runtime -- Don't request LeaseSet for incoming stream -- Terminate incoming stream right away if no remote LeaseSet -- Handle choked, new RTO and window size calculation and resetting algorithm for streams -### Fixed -- Empty string in addressbook subscriptions -- ECIESx25519 sessions without destination -- Missing RouterInfo buffer in NetDb -- Invalid I2PControl certificate -- Routers disappear from NetDb when offline -- Peer test message 6 sent to unknown endpoint -- Race condition with LeaseSet update -- Excessive CPU usage by streams -- Crash on shutdown - ## [2.54.0] - 2024-10-06 ### Added - Maintain recently connected routers list to avoid false-positive peer test @@ -85,12 +21,12 @@ - Handle i2cp.inboundlimit and i2cp.outboundlimit params in I2CP - Publish LeaseSet with new timestamp update if tunnel was replaced in the same second - Increase max number of generated tags to 800 per tagset -- Routing path expiration by time instead num attempts +- Routing path expiration by time instead num attempts - Save timestamp from epoch instead local time to profiles - Update introducer's iTag if session to introducer was replaced to new one - RTT, window size and number of NACKs calculation for streaming - Don't select same peer for tunnel too often -- Use WinApi for data path UTF-8 conversion for Windows +- Use WinApi for data path UTF-8 conversion for Windows ### Fixed - Jump link crash if address book is disabled - Race condition if connect through an introducer diff --git a/LICENSE b/LICENSE index f59491f5..93280084 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2013-2025, The PurpleI2P Project +Copyright (c) 2013-2023, The PurpleI2P Project All rights reserved. diff --git a/Makefile b/Makefile index 0d4ca48c..3998beb0 100644 --- a/Makefile +++ b/Makefile @@ -29,6 +29,7 @@ DAEMON_SRC_DIR := daemon # import source files lists include filelist.mk +USE_AESNI := $(or $(USE_AESNI),yes) USE_STATIC := $(or $(USE_STATIC),no) USE_UPNP := $(or $(USE_UPNP),no) DEBUG := $(or $(DEBUG),yes) @@ -69,9 +70,6 @@ else ifneq (, $(findstring freebsd, $(SYS))$(findstring openbsd, $(SYS))) else ifneq (, $(findstring haiku, $(SYS))) DAEMON_SRC += $(DAEMON_SRC_DIR)/UnixDaemon.cpp include Makefile.haiku -else ifneq (, $(findstring solaris, $(SYS))) - DAEMON_SRC += $(DAEMON_SRC_DIR)/UnixDaemon.cpp - include Makefile.solaris else # not supported $(error Not supported platform) endif diff --git a/Makefile.bsd b/Makefile.bsd index 1c911802..4cf8f80a 100644 --- a/Makefile.bsd +++ b/Makefile.bsd @@ -3,7 +3,7 @@ CXXFLAGS ?= ${CXX_DEBUG} -Wall -Wextra -Wno-unused-parameter -pedantic -Wno-misl DEFINES = -D_GLIBCXX_USE_NANOSLEEP=1 INCFLAGS = -I/usr/include/ -I/usr/local/include/ LDFLAGS = ${LD_DEBUG} -Wl,-rpath,/usr/local/lib -L/usr/local/lib -LDLIBS = -lssl -lcrypto -lz -lpthread -lboost_system -lboost_program_options +LDLIBS = -lcrypto -lssl -lz -lpthread -lboost_system -lboost_program_options ## NOTE: NEEDED_CXXFLAGS is here so that custom CXXFLAGS can be specified at build time ## **without** overwriting the CXXFLAGS which we need in order to build. diff --git a/Makefile.haiku b/Makefile.haiku index eb56a207..d0824d73 100644 --- a/Makefile.haiku +++ b/Makefile.haiku @@ -1,12 +1,8 @@ -ifeq ($(shell $(CXX) -dumpmachine | cut -c 1-4), i586) -CXX = g++-x86 -else CXX = g++ -endif -CXXFLAGS := -Wall -std=c++20 +CXXFLAGS := -Wall -std=c++17 INCFLAGS = -I/system/develop/headers DEFINES = -D_DEFAULT_SOURCE -D_GNU_SOURCE -LDLIBS = -lbe -lbsd -lnetwork -lz -lssl -lcrypto -lboost_program_options -lpthread +LDLIBS = -lbe -lbsd -lnetwork -lz -lcrypto -lssl -lboost_system -lboost_program_options -lpthread ifeq ($(USE_UPNP),yes) DEFINES += -DUSE_UPNP diff --git a/Makefile.homebrew b/Makefile.homebrew index 706f9811..e14ea955 100644 --- a/Makefile.homebrew +++ b/Makefile.homebrew @@ -18,7 +18,7 @@ endif LDLIBS += -lpthread -ldl else LDFLAGS += -L${SSLROOT}/lib -L${BOOSTROOT}/lib - LDLIBS = -lz -lssl -lcrypto -lboost_system -lboost_filesystem -lboost_program_options -lpthread + LDLIBS = -lz -lcrypto -lssl -lboost_system -lboost_filesystem -lboost_program_options -lpthread ifeq ($(USE_UPNP),yes) LDFLAGS += -L${UPNPROOT}/lib LDLIBS += -lminiupnpc @@ -30,6 +30,13 @@ ifeq ($(USE_UPNP),yes) INCFLAGS += -I${UPNPROOT}/include endif +ifeq ($(USE_AESNI),yes) +ifneq (, $(findstring i386, $(SYS))$(findstring i686, $(SYS))$(findstring x86_64, $(SYS))) # only x86-based CPU supports that + NEEDED_CXXFLAGS += -maes + DEFINES += -D__AES__ +endif +endif + install: all install -d ${PREFIX}/bin install -m 755 ${I2PD} ${PREFIX}/bin diff --git a/Makefile.linux b/Makefile.linux index 4ea39e22..aa67626a 100644 --- a/Makefile.linux +++ b/Makefile.linux @@ -13,9 +13,12 @@ LDFLAGS ?= ${LD_DEBUG} CXXVER := $(shell $(CXX) -dumpversion) ifeq ($(shell expr match $(CXX) 'clang'),5) NEEDED_CXXFLAGS += -std=c++17 +else ifeq ($(shell expr match ${CXXVER} "7"),1) # gcc 7 + NEEDED_CXXFLAGS += -std=c++17 + LDLIBS = -lboost_filesystem else ifeq ($(shell expr match ${CXXVER} "[8-9]"),1) # gcc 8 - 9 NEEDED_CXXFLAGS += -std=c++17 - LDLIBS = -lboost_system -lstdc++fs + LDLIBS = -lstdc++fs else ifeq ($(shell expr match ${CXXVER} "1[0-2]"),2) # gcc 10 - 12 NEEDED_CXXFLAGS += -std=c++17 else ifeq ($(shell expr match ${CXXVER} "1[3-9]"),2) # gcc 13+ @@ -31,6 +34,7 @@ ifeq ($(USE_STATIC),yes) # Using 'getaddrinfo' in statically linked applications requires at runtime # the shared libraries from the glibc version used for linking LIBDIR := /usr/lib/$(SYS) + LDLIBS += $(LIBDIR)/libboost_system.a LDLIBS += $(LIBDIR)/libboost_program_options.a LDLIBS += $(LIBDIR)/libssl.a LDLIBS += $(LIBDIR)/libcrypto.a @@ -40,7 +44,7 @@ ifeq ($(USE_UPNP),yes) endif LDLIBS += -lpthread -ldl else - LDLIBS += -lssl -lcrypto -lz -lboost_program_options -lpthread -latomic + LDLIBS += -lcrypto -lssl -lz -lboost_system -lboost_program_options -lpthread -latomic ifeq ($(USE_UPNP),yes) LDLIBS += -lminiupnpc endif @@ -51,6 +55,13 @@ ifeq ($(USE_UPNP),yes) DEFINES += -DUSE_UPNP endif +ifeq ($(USE_AESNI),yes) +ifneq (, $(findstring i386, $(SYS))$(findstring i686, $(SYS))$(findstring x86_64, $(SYS))) # only x86-based CPU supports that + NEEDED_CXXFLAGS += -maes + DEFINES += -D__AES__ +endif +endif + install: all install -d ${PREFIX}/bin install -m 755 ${I2PD} ${PREFIX}/bin diff --git a/Makefile.mingw b/Makefile.mingw index 32d60764..fc92e9b0 100644 --- a/Makefile.mingw +++ b/Makefile.mingw @@ -42,6 +42,12 @@ ifeq ($(USE_WIN32_APP), yes) DAEMON_OBJS += $(patsubst %.rc,obj/%.o,$(DAEMON_RC)) endif +ifeq ($(USE_AESNI),yes) + NEEDED_CXXFLAGS += -maes + LDFLAGS += -maes + DEFINES += -D__AES__ +endif + ifeq ($(USE_ASLR),yes) LDFLAGS += -Wl,--nxcompat -Wl,--high-entropy-va -Wl,--dynamicbase,--export-all-symbols endif diff --git a/Makefile.osx b/Makefile.osx index 52282307..48eb1a51 100644 --- a/Makefile.osx +++ b/Makefile.osx @@ -7,9 +7,9 @@ LDFLAGS += -Wl,-dead_strip LDFLAGS += -Wl,-dead_strip_dylibs ifeq ($(USE_STATIC),yes) - LDLIBS = -lz /usr/local/lib/libssl.a /usr/local/lib/libcrypto.a /usr/local/lib/libboost_system.a /usr/local/lib/libboost_filesystem.a /usr/local/lib/libboost_program_options.a -lpthread + LDLIBS = -lz /usr/local/lib/libcrypto.a /usr/local/lib/libssl.a /usr/local/lib/libboost_system.a /usr/local/lib/libboost_filesystem.a /usr/local/lib/libboost_program_options.a -lpthread else - LDLIBS = -lz -lssl -lcrypto -lboost_system -lboost_filesystem -lboost_program_options -lpthread + LDLIBS = -lz -lcrypto -lssl -lboost_system -lboost_filesystem -lboost_program_options -lpthread endif ifeq ($(USE_UPNP),yes) @@ -25,5 +25,9 @@ endif OSARCH = $(shell uname -p) ifneq ($(OSARCH),powerpc) - CXXFLAGS += -msse + ifeq ($(USE_AESNI),yes) + CXXFLAGS += -D__AES__ -maes + else + CXXFLAGS += -msse + endif endif diff --git a/Makefile.solaris b/Makefile.solaris deleted file mode 100644 index 77d34114..00000000 --- a/Makefile.solaris +++ /dev/null @@ -1,9 +0,0 @@ -CXX = g++ -INCFLAGS = -I/usr/openssl/3/include -CXXFLAGS := -Wall -std=c++20 -LDLIBS = -L/usr/openssl/3/lib/64 -lssl -lcrypto -lboost_program_options -lz -lpthread -lsocket - -ifeq ($(USE_UPNP),yes) - DEFINES += -DUSE_UPNP - LDLIBS += -lminiupnpc -endif \ No newline at end of file diff --git a/build/CMakeLists.txt b/build/CMakeLists.txt index bc936e18..954f5de9 100644 --- a/build/CMakeLists.txt +++ b/build/CMakeLists.txt @@ -29,6 +29,7 @@ project( ) # configurable options +option(WITH_AESNI "Use AES-NI instructions set" ON) option(WITH_HARDENING "Use hardening compiler flags" OFF) option(WITH_LIBRARY "Build library" ON) option(WITH_BINARY "Build binary" ON) @@ -184,6 +185,16 @@ if(UNIX) endif() endif() +# Note: AES-NI and AVX is available on x86-based CPU's. +# Here also ARM64 implementation, but currently we don't support it. +# MSVC is not supported due to different ASM processing, so we hope OpenSSL has its own checks to run optimized code. +if(WITH_AESNI AND (ARCHITECTURE MATCHES "x86_64" OR ARCHITECTURE MATCHES "i386")) + if(NOT MSVC) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -maes") + endif() + add_definitions(-D__AES__) +endif() + if(WITH_ADDRSANITIZER) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -fno-omit-frame-pointer") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=address") @@ -324,6 +335,7 @@ message(STATUS "Architecture : ${ARCHITECTURE}") message(STATUS "Compiler flags : ${CMAKE_CXX_FLAGS}") message(STATUS "Install prefix: : ${CMAKE_INSTALL_PREFIX}") message(STATUS "Options:") +message(STATUS " AESNI : ${WITH_AESNI}") message(STATUS " HARDENING : ${WITH_HARDENING}") message(STATUS " LIBRARY : ${WITH_LIBRARY}") message(STATUS " BINARY : ${WITH_BINARY}") diff --git a/build/cmake_modules/GetGitRevisionDescription.cmake b/build/cmake_modules/GetGitRevisionDescription.cmake index a08895c6..4fbd90db 100644 --- a/build/cmake_modules/GetGitRevisionDescription.cmake +++ b/build/cmake_modules/GetGitRevisionDescription.cmake @@ -59,7 +59,7 @@ get_filename_component(_gitdescmoddir ${CMAKE_CURRENT_LIST_FILE} PATH) # function returns an empty string via _git_dir_var. # # Example: Given a path C:/bla/foo/bar and assuming C:/bla/.git exists and -# neither foo nor bar contain a file/directory .git. This will return +# neither foo nor bar contain a file/directory .git. This wil return # C:/bla/.git # function(_git_find_closest_git_dir _start_dir _git_dir_var) diff --git a/contrib/certificates/reseed/hiduser0_at_mail.i2p.crt b/contrib/certificates/reseed/hiduser0_at_mail.i2p.crt new file mode 100644 index 00000000..a332805a --- /dev/null +++ b/contrib/certificates/reseed/hiduser0_at_mail.i2p.crt @@ -0,0 +1,32 @@ +-----BEGIN CERTIFICATE----- +MIIFgTCCA2mgAwIBAgIETWAY1DANBgkqhkiG9w0BAQ0FADBxMQswCQYDVQQGEwJY +WDELMAkGA1UECAwCWFgxCzAJBgNVBAcMAlhYMR4wHAYDVQQKDBVJMlAgQW5vbnlt +b3VzIE5ldHdvcmsxDDAKBgNVBAsMA0kyUDEaMBgGA1UEAwwRaGlkdXNlcjBAbWFp +bC5pMnAwHhcNMjExMjEzMTU0MDI3WhcNMzExMjExMTU0MDI3WjBxMQswCQYDVQQG +EwJYWDELMAkGA1UECAwCWFgxCzAJBgNVBAcMAlhYMR4wHAYDVQQKDBVJMlAgQW5v +bnltb3VzIE5ldHdvcmsxDDAKBgNVBAsMA0kyUDEaMBgGA1UEAwwRaGlkdXNlcjBA +bWFpbC5pMnAwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDXnjJ8UQ0f +lHHpfPMiHofBPSuL4sbOJY6fOXwPhSg/h6THh9DS/ZWmJXQ3qRD0glDVtv4/Dr/9 +ldGQ5eltF9iCFXCQlMEy2HjQrBKq0nsl7RpYK12cyMaod0kkzCUk9ITLi9CmHM3Z +gQZcmG8TWjFEpDR+idx/QkQt2pcO4vzWlDit3Vh4ivnbX5jGQHbsVjQEMQWxr+pX +dsS+YQpjZ6RBmrooGTPO8QDOOeYLAn0lCjmffc/kzIH9E/p4/O0rOpyhVYbdxUD1 +5wkqN9l4yrtxmORG/PudnRQQ0r4TUq8vsxfGY0Euo9IbhgXF2Parel1ZhDxB1WZV +VwWtgLIh9jGA1UMa8SYKnEfp8LWNZ3b3mUUnZb3kMrLk6jGYRWNsHmamhd4mC7AZ +qf/8lOkEIw3bPd3YguCDRVcLui5BwIEZmqXg8uoESxfO/sW3pBrN/8M7MkTex9kN +vjitGDDXvenK27qmNgZxbBlX72yTSfys7XTYTLnxZC8AwdAo2Wz9Z6HhGiPonf2h +vZkc9ZxuE0jFIrsbJra4X7iyjXgi4vV4ARNg/9Ft6F4/OIbECgeDcBQqq4TlT2bZ +EfWVrBbqXoj5vNsLigIkd+AyUNwPYEcB5IFSiiOh98pC7BH3pg0m8U5YBjxe1i+9 +EQOOG0Qtx+JigXZHu6bGE0Twy9zy+UzoKQIDAQABoyEwHzAdBgNVHQ4EFgQUGK1b +0DkL6aLalcfBc/Uj/SF08C0wDQYJKoZIhvcNAQENBQADggIBAMpXM82bJDpH1TlH +TvhU3Z7nfZdvEhOQfujaFUYiuNripuEKcFGn948+DvAG0FUN+uNlJoqOVs8D7InD +gWlA9zpqw5Cl5Hij/Wns9QbXuAHJeA23fVUoaM2A6v9ifcIQ1A+rDuRQAo6/64KW +ChTg2e99RBpfGOyqgeh7tLLe0lPPekVpKHFuXabokaKRDuBcVHcUL4tWXe3dcyqa +Ej/PJrrS+nWL0EGZ4q80CEd2LPuDzPxNGCJt/R7ZfadENWajcgcXGceh1QBzozrB +SL/Ya6wF9SrsB7V/r5wX0LM4ZdDaLWbtmUe5Op0h/ZMH25Sa8xAXVz+O9L6sWSoO +FaiYTOvAiyyPz+nsxKa3xYryDHno7eKSt+hGOcaurhxbdZaEFY/CegEc73tCt9xK +e9qF8O/WkDLmixuErw3f5en4IfzGR7p3lJAwW/8WD8C6HS39h/eE7dVZNaWgtQnZ +SgGjgZMTJqTcQ3aZmfuCZefxGFok8w6AIkdbnd1pdMBRjYu8aXgl2hQSB9ZADDE9 +R5d3rXi0PkSFLIvsNjVa5KXrZk/tB0Hpfmepq7CufBqjP/LG9TieRoXzLYUKFF74 +QRwjP+y7AJ+VDUTpY1NV1P+k+2raubU2bOnLF3zL5DtyoyieGPhyeMMvp0fRIxdg +bSl5VHgPXHNM8mcnndMAuzvl7jEK +-----END CERTIFICATE----- diff --git a/contrib/certificates/reseed/hottuna_at_mail.i2p.crt b/contrib/certificates/reseed/hottuna_at_mail.i2p.crt new file mode 100644 index 00000000..d0ff7c33 --- /dev/null +++ b/contrib/certificates/reseed/hottuna_at_mail.i2p.crt @@ -0,0 +1,33 @@ +-----BEGIN CERTIFICATE----- +MIIFxzCCA6+gAwIBAgIQZfqn0yiJL3dGgCjeOeWS6DANBgkqhkiG9w0BAQsFADBw +MQswCQYDVQQGEwJYWDELMAkGA1UEBxMCWFgxCzAJBgNVBAkTAlhYMR4wHAYDVQQK +ExVJMlAgQW5vbnltb3VzIE5ldHdvcmsxDDAKBgNVBAsTA0kyUDEZMBcGA1UEAwwQ +aG90dHVuYUBtYWlsLmkycDAeFw0xNjExMDkwMzE1MzJaFw0yNjExMDkwMzE1MzJa +MHAxCzAJBgNVBAYTAlhYMQswCQYDVQQHEwJYWDELMAkGA1UECRMCWFgxHjAcBgNV +BAoTFUkyUCBBbm9ueW1vdXMgTmV0d29yazEMMAoGA1UECxMDSTJQMRkwFwYDVQQD +DBBob3R0dW5hQG1haWwuaTJwMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKC +AgEA21Bfgcc9VVH4l2u1YvYlTw2OPUyQb16X2IOW0PzdsUO5W78Loueu974BkiKi +84lQZanLr0OwEopdfutGc6gegSLmwaWx5YCG5uwpLOPkDiObfX+nptH6As/B1cn+ +mzejYdVKRnWd7EtHW0iseSsILBK1YbGw4AGpXJ8k18DJSzUt2+spOkpBW6XqectN +8y2JDSTns8yiNxietVeRN/clolDXT9ZwWHkd+QMHTKhgl3Uz1knOffU0L9l4ij4E +oFgPfQo8NL63kLM24hF1hM/At7XvE4iOlObFwPXE+H5EGZpT5+A7Oezepvd/VMzM +tCJ49hM0OlR393tKFONye5GCYeSDJGdPEB6+rBptpRrlch63tG9ktpCRrg2wQWgC +e3aOE1xVRrmwiTZ+jpfsOCbZrrSA/C4Bmp6AfGchyHuDGGkRU/FJwa1YLJe0dkWG +ITLWeh4zeVuAS5mctdv9NQ5wflSGz9S8HjsPBS5+CDOFHh4cexXRG3ITfk6aLhuY +KTMlkIO4SHKmnwAvy1sFlsqj6PbfVjpHPLg625fdNxBpe57TLxtIdBB3C7ccQSRW ++UG6Cmbcmh80PbsSR132NLMlzLhbaOjxeCWWJRo6cLuHBptAFMNwqsXt8xVf9M0N +NdJoKUmblyvjnq0N8aMEqtQ1uGMTaCB39cutHQq+reD/uzsCAwEAAaNdMFswDgYD +VR0PAQH/BAQDAgKEMB0GA1UdJQQWMBQGCCsGAQUFBwMCBggrBgEFBQcDATAPBgNV +HRMBAf8EBTADAQH/MBkGA1UdDgQSBBBob3R0dW5hQG1haWwuaTJwMA0GCSqGSIb3 +DQEBCwUAA4ICAQCibFV8t4pajP176u3jx31x1kgqX6Nd+0YFARPZQjq99kUyoZer +GyHGsMWgM281RxiZkveHxR7Hm7pEd1nkhG3rm+d7GdJ2p2hujr9xUvl0zEqAAqtm +lkYI6uJ13WBjFc9/QuRIdeIeSUN+eazSXNg2nJhoV4pF9n2Q2xDc9dH4GWO93cMX +JPKVGujT3s0b7LWsEguZBPdaPW7wwZd902Cg/M5fE1hZQ8/SIAGUtylb/ZilVeTS +spxWP1gX3NT1SSvv0s6oL7eADCgtggWaMxEjZhi6WMnPUeeFY8X+6trkTlnF9+r/ +HiVvvzQKrPPtB3j1xfQCAF6gUKN4iY+2AOExv4rl/l+JJbPhpd/FuvD8AVkLMZ8X +uPe0Ew2xv30cc8JjGDzQvoSpBmVTra4f+xqH+w8UEmxnx97Ye2aUCtnPykACnFte +oT97K5052B1zq+4fu4xaHZnEzPYVK5POzOufNLPgciJsWrR5GDWtHd+ht/ZD37+b ++j1BXpeBWUBQgluFv+lNMVNPJxc2OMELR1EtEwXD7mTuuUEtF5Pi63IerQ5LzD3G +KBvXhMB0XhpE6WG6pBwAvkGf5zVv/CxClJH4BQbdZwj9HYddfEQlPl0z/XFR2M0+ +9/8nBfGSPYIt6KeHBCeyQWTdE9gqSzMwTMFsennXmaT8gyc7eKqKF6adqw== +-----END CERTIFICATE----- diff --git a/contrib/certificates/reseed/unixeno_at_cubicchaos.net.crt b/contrib/certificates/reseed/unixeno_at_cubicchaos.net.crt deleted file mode 100644 index c94d319e..00000000 --- a/contrib/certificates/reseed/unixeno_at_cubicchaos.net.crt +++ /dev/null @@ -1,34 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIF2TCCA8GgAwIBAgIQVpTNnJZlUTDqmZiHRU4wCjANBgkqhkiG9w0BAQsFADB2 -MQswCQYDVQQGEwJYWDELMAkGA1UEBxMCWFgxCzAJBgNVBAkTAlhYMR4wHAYDVQQK -ExVJMlAgQW5vbnltb3VzIE5ldHdvcmsxDDAKBgNVBAsTA0kyUDEfMB0GA1UEAwwW -dW5peGVub0BjdWJpY2NoYW9zLm5ldDAeFw0yNTAzMDQxODU5NDZaFw0zNTAzMDQx -ODU5NDZaMHYxCzAJBgNVBAYTAlhYMQswCQYDVQQHEwJYWDELMAkGA1UECRMCWFgx -HjAcBgNVBAoTFUkyUCBBbm9ueW1vdXMgTmV0d29yazEMMAoGA1UECxMDSTJQMR8w -HQYDVQQDDBZ1bml4ZW5vQGN1YmljY2hhb3MubmV0MIICIjANBgkqhkiG9w0BAQEF -AAOCAg8AMIICCgKCAgEAr/JoAzLDtHXoAc7QcP4IxO+xNTeiYs78Wlg/Sl/sa6qz -gJoGaKH/X++z4Xe9lBSZalXCamnO4QMTgsWOIeoMy6XVbGzNTXPl8JUcblTIXwkP -pv848b1nxLfgLHzPRz1mJMpMikBugJ3Iz1sQzDVlUdye2fgbGChWliz9P4ClEODv -A/4+7i6uvJgEZ7A+jx3vBCXhiJppE3wTuz5D9BQqG8NuEwwjwBTBByoCC4oxOe0h -Qu1k7kEr+n4qpSEg/1eJ/RYSm+I8BftK1RUfykTwxlfmyEmKsfLBQWczE8Ca9nUB -5V34UH2bRy1cvavJYcNW3EPsGNf4naRs+Gy8XIFrb315GgWC1Z6+tzk+QFli9YeF -0DgtYEZciqu/407o8ZEURTnPjB7GhLDDp1LAQ7CQRhzaraXjHj0hyO+6rFpFdD0D -mXhvI/Eph3QIldsgnQc7nPhU2csN8Vi6bNDgm0HZ8cdmIBpI2Uxn/acZX/9G40oj -czrhsCBEecu/BluLJsfaWCYg90rvONP8Fc4edHAMonzYZR4r0q4hbv7AM8GmDRDN -J9/DZFi+Qs9NAe06jJC3jSsj7IdIs8TMhw8FX3xWOlXwjmVETAgY/dta/MpLJ6tJ -i+E+TH/Ndntj/D6WUwdQq+LyCR6gqHUWR6rl6EDQz+08DWb7j/72JSLb/DaXrDUC -AwEAAaNjMGEwDgYDVR0PAQH/BAQDAgKEMB0GA1UdJQQWMBQGCCsGAQUFBwMCBggr -BgEFBQcDATAPBgNVHRMBAf8EBTADAQH/MB8GA1UdDgQYBBZ1bml4ZW5vQGN1Ymlj -Y2hhb3MubmV0MA0GCSqGSIb3DQEBCwUAA4ICAQBBVoPeuOkmJDUdzIrzmxTmjMyz -gpfrZnjirTQKWhbv53sAWNeJ3kZ9l9m+0YpqEtFDrZPL5LTBXcSci5gGuxPkp+i/ -f/axsdcFMKbI9B/M53fyXLLJY0EM4mdhNAWtph1kTowFPhhReefCdqxYIy9uk2pL -gfb6NYJf+w9//fKYFZXb9SsiRchfv81+lbN+PIprnCpV3cTZWmpLRi2hN86pMW20 -3rh7rqJ4dPnA/NoyM+JstL10IU/4StqInweEvoo4W44+cC0zYGvfkkrKL4LB8w5S -6DKebtk7NQDtzuw2QnA9Ms4bmqWQpbL6/7uGaauS0+nmF+2gkqi9hcv0W5ZoBb/D -IVRauySnCtp9PbYM7pIJP9a1U6naLj7L1VixqsJGfPQ8V9TCOOi5bDc3RTetI/DX -bXHhAqHYzboakptylCp+Ao5h2hu0+w4rqnG63HwsHDJWcETbdVFQfzlzUmbx53yV -GnBsUxDgMOiHTZdKLkEnH4Q/XI76uc0ntTRlK9ktKWZPSISUlHrFnFl6I5UdeBMy -6vpB9sJO5L5RPRi4945K5Xdennywdi508mNXtMMmNCqrk1SMYbwaY6ZtIvXEGam9 -uHQTiTEX9LED/VXzFGqzdyDbG43HgS0PksgzedelHWfVAEnc06U3JX2lqUyihYHa -N4jAXWQ7s5p4GYaf4Q== ------END CERTIFICATE----- diff --git a/contrib/debian/trusty/patches/01-upnp.patch b/contrib/debian/trusty/patches/01-upnp.patch index 74d36c06..bec8f2b0 100644 --- a/contrib/debian/trusty/patches/01-upnp.patch +++ b/contrib/debian/trusty/patches/01-upnp.patch @@ -2,13 +2,13 @@ Description: Enable UPnP usage in package Author: r4sas Reviewed-By: r4sas -Last-Update: 2024-12-30 +Last-Update: 2022-03-23 --- i2pd.orig/Makefile +++ i2pd/Makefile -@@ -31,7 +31,7 @@ # import source files lists - include filelist.mk +@@ -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) diff --git a/contrib/debian/xenial/patches/01-upnp.patch b/contrib/debian/xenial/patches/01-upnp.patch index 74d36c06..bec8f2b0 100644 --- a/contrib/debian/xenial/patches/01-upnp.patch +++ b/contrib/debian/xenial/patches/01-upnp.patch @@ -2,13 +2,13 @@ Description: Enable UPnP usage in package Author: r4sas Reviewed-By: r4sas -Last-Update: 2024-12-30 +Last-Update: 2022-03-23 --- i2pd.orig/Makefile +++ i2pd/Makefile -@@ -31,7 +31,7 @@ # import source files lists - include filelist.mk +@@ -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) diff --git a/contrib/i2pd.conf b/contrib/i2pd.conf index c26f8af0..be4a6719 100644 --- a/contrib/i2pd.conf +++ b/contrib/i2pd.conf @@ -243,7 +243,7 @@ verify = true ## Default: reg.i2p at "mainline" I2P Network # defaulturl = http://shx5vqsw7usdaunyzr2qmes2fq37oumybpudrd4jjj4e4vk4uusa.b32.i2p/hosts.txt ## Optional subscriptions URLs, separated by comma -# subscriptions = http://reg.i2p/hosts.txt,http://identiguy.i2p/hosts.txt,http://stats.i2p/cgi-bin/newhosts.txt +# subscriptions = http://reg.i2p/hosts.txt,http://identiguy.i2p/hosts.txt,http://stats.i2p/cgi-bin/newhosts.txt,http://rus.i2p/hosts.txt [limits] ## Maximum active transit sessions (default: 5000) @@ -277,3 +277,9 @@ verify = true ## Save full addresses on disk (default: true) # addressbook = true +[cpuext] +## Use CPU AES-NI instructions set when work with cryptography when available (default: true) +# aesni = true +## Force usage of CPU instructions set, even if they not found (default: false) +## DO NOT TOUCH that option if you really don't know what are you doing! +# force = false diff --git a/contrib/i2pd.service b/contrib/i2pd.service index 1ab46979..381ae483 100644 --- a/contrib/i2pd.service +++ b/contrib/i2pd.service @@ -1,8 +1,7 @@ [Unit] Description=I2P Router written in C++ Documentation=man:i2pd(1) https://i2pd.readthedocs.io/en/latest/ -Wants=network.target -After=network.target network-online.target +After=network.target [Service] User=i2pd diff --git a/contrib/rpm/i2pd-git.spec b/contrib/rpm/i2pd-git.spec index 2083ba18..05158bc9 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.56.0 +Version: 2.54.0 Release: git%{git_hash}%{?dist} Summary: I2P router written in C++ Conflicts: i2pd @@ -148,12 +148,6 @@ getent passwd i2pd >/dev/null || \ %changelog -* Tue Feb 11 2025 orignal - 2.56.0 -- update to 2.56.0 - -* Mon Dec 30 2024 orignal - 2.55.0 -- update to 2.55.0 - * Sun Oct 6 2024 orignal - 2.54.0 - update to 2.54.0 diff --git a/contrib/rpm/i2pd.spec b/contrib/rpm/i2pd.spec index 4eb558ba..4b1e573b 100644 --- a/contrib/rpm/i2pd.spec +++ b/contrib/rpm/i2pd.spec @@ -1,5 +1,5 @@ Name: i2pd -Version: 2.56.0 +Version: 2.54.0 Release: 1%{?dist} Summary: I2P router written in C++ Conflicts: i2pd-git @@ -146,12 +146,6 @@ getent passwd i2pd >/dev/null || \ %changelog -* Tue Feb 11 2025 orignal - 2.56.0 -- update to 2.56.0 - -* Mon Dec 30 2024 orignal - 2.55.0 -- update to 2.55.0 - * Sun Oct 6 2024 orignal - 2.54.0 - update to 2.54.0 diff --git a/contrib/tunnels.conf b/contrib/tunnels.conf index fc455e79..55723c43 100644 --- a/contrib/tunnels.conf +++ b/contrib/tunnels.conf @@ -5,7 +5,6 @@ port = 6668 destination = irc.ilita.i2p destinationport = 6667 keys = irc-keys.dat -i2p.streaming.profile=2 #[IRC-IRC2P] #type = client diff --git a/daemon/Daemon.cpp b/daemon/Daemon.cpp index e2fdf2d4..b572944f 100644 --- a/daemon/Daemon.cpp +++ b/daemon/Daemon.cpp @@ -149,10 +149,12 @@ namespace util LogPrint(eLogDebug, "FS: Certificates directory: ", certsdir); bool precomputation; i2p::config::GetOption("precomputation.elgamal", precomputation); + bool aesni; i2p::config::GetOption("cpuext.aesni", aesni); + bool forceCpuExt; i2p::config::GetOption("cpuext.force", forceCpuExt); bool ssu; i2p::config::GetOption("ssu", ssu); if (!ssu && i2p::config::IsDefault ("precomputation.elgamal")) precomputation = false; // we don't elgamal table if no ssu, unless it's specified explicitly - i2p::crypto::InitCrypto (precomputation); + i2p::crypto::InitCrypto (precomputation, aesni, forceCpuExt); i2p::transport::InitAddressFromIface (); // get address4/6 from interfaces diff --git a/daemon/HTTPServer.cpp b/daemon/HTTPServer.cpp index dca545fe..21c7b6c6 100644 --- a/daemon/HTTPServer.cpp +++ b/daemon/HTTPServer.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2025, The PurpleI2P Project +* Copyright (c) 2013-2024, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -132,8 +132,7 @@ namespace http { static void ShowTunnelDetails (std::stringstream& s, enum i2p::tunnel::TunnelState eState, bool explr, int bytes) { - std::string state; - std::string_view stateText; + std::string state, stateText; switch (eState) { case i2p::tunnel::eTunnelStateBuildReplyReceived : @@ -147,7 +146,7 @@ namespace http { } if (stateText.empty ()) stateText = tr(state); - s << " " << stateText << ((explr) ? " (" + std::string(tr("exploratory")) + ")" : "") << ", "; // TODO: + s << " " << stateText << ((explr) ? " (" + tr("exploratory") + ")" : "") << ", "; ShowTraffic(s, bytes); s << "\r\n"; } @@ -214,7 +213,7 @@ namespace http { "\r\n"; } - static void ShowError(std::stringstream& s, std::string_view string) + static void ShowError(std::stringstream& s, const std::string& string) { s << "" << tr("ERROR") << ": " << string << "
\r\n"; } @@ -702,7 +701,6 @@ namespace http { { s << "" << tr("Tunnels") << ":
\r\n"; s << "" << tr("Queue size") << ": " << i2p::tunnel::tunnels.GetQueueSize () << "
\r\n
\r\n"; - s << "" << tr("TBM Queue size") << ": " << i2p::tunnel::tunnels.GetTBMQueueSize () << "
\r\n
\r\n"; auto ExplPool = i2p::tunnel::tunnels.GetExploratoryPool (); @@ -827,7 +825,7 @@ namespace http { if (i2p::tunnel::tunnels.CountTransitTunnels()) { s << "" << tr("Transit Tunnels") << ":
\r\n"; - s << ""; + s << "
ID" << tr("Amount") << "" << tr("Next") << "
"; for (const auto& it: i2p::tunnel::tunnels.GetTransitTunnels ()) { if (std::dynamic_pointer_cast(it)) @@ -837,7 +835,7 @@ namespace http { else s << "\r\n"; + s << "\r\n"; } s << "
ID" << tr("Amount") << "
" << it->GetTunnelID () << ""; ShowTraffic(s, it->GetNumTransmittedBytes ()); - s << "" << it->GetNextPeerName () << "
\r\n"; } @@ -1263,7 +1261,7 @@ namespace http { ShowLeasesSets(s); else { res.code = 400; - ShowError(s, std::string (tr("Unknown page")) + ": " + page); // TODO + ShowError(s, tr("Unknown page") + ": " + page); return; } } @@ -1419,11 +1417,13 @@ namespace http { { auto signatureLen = dest->GetIdentity ()->GetSignatureLen (); uint8_t * signature = new uint8_t[signatureLen]; + char * sig = new char[signatureLen*2]; std::stringstream out; out << name << "=" << dest->GetIdentity ()->ToBase64 (); dest->Sign ((uint8_t *)out.str ().c_str (), out.str ().length (), signature); - auto sig = i2p::data::ByteStreamToBase64 (signature, signatureLen); + auto len = i2p::data::ByteStreamToBase64 (signature, signatureLen, sig, signatureLen*2); + sig[len] = 0; out << "#!sig=" << sig; s << "" << tr("SUCCESS") << ":
\r\n
\r\n" "\r\n
\r\n
\r\n" @@ -1432,6 +1432,7 @@ namespace http { "\r\n" "
\r\n
\r\n"; delete[] signature; + delete[] sig; } else s << "" << tr("ERROR") << ": " << tr("Domain can't end with .b32.i2p") << "\r\n
\r\n
\r\n"; @@ -1460,7 +1461,7 @@ namespace http { else { res.code = 400; - ShowError(s, std::string (tr("Unknown command")) + ": " + cmd); // TODO + ShowError(s, tr("Unknown command") + ": " + cmd); return; } @@ -1484,8 +1485,8 @@ namespace http { } HTTPServer::HTTPServer (const std::string& address, int port): - m_IsRunning (false), m_Thread (nullptr), m_Work (m_Service.get_executor ()), - m_Acceptor (m_Service, boost::asio::ip::tcp::endpoint (boost::asio::ip::make_address(address), port)), + m_IsRunning (false), m_Thread (nullptr), m_Work (m_Service), + m_Acceptor (m_Service, boost::asio::ip::tcp::endpoint (boost::asio::ip::address::from_string(address), port)), m_Hostname(address) { } diff --git a/daemon/HTTPServer.h b/daemon/HTTPServer.h index 38b790d4..f751c5a8 100644 --- a/daemon/HTTPServer.h +++ b/daemon/HTTPServer.h @@ -83,8 +83,8 @@ namespace http bool m_IsRunning; std::unique_ptr m_Thread; - boost::asio::io_context m_Service; - boost::asio::executor_work_guard m_Work; + boost::asio::io_service m_Service; + boost::asio::io_service::work m_Work; boost::asio::ip::tcp::acceptor m_Acceptor; std::string m_Hostname; }; diff --git a/daemon/I2PControl.cpp b/daemon/I2PControl.cpp index 6261a14c..fc7f2257 100644 --- a/daemon/I2PControl.cpp +++ b/daemon/I2PControl.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2025, 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 @@ // Use global placeholders from boost introduced when local_time.hpp is loaded #define BOOST_BIND_GLOBAL_PLACEHOLDERS #include +#include #include "FS.h" #include "Log.h" @@ -29,24 +30,11 @@ namespace i2p namespace client { I2PControlService::I2PControlService (const std::string& address, int port): - m_IsRunning (false), + m_IsRunning (false), m_Thread (nullptr), + m_Acceptor (m_Service, boost::asio::ip::tcp::endpoint(boost::asio::ip::address::from_string(address), port)), m_SSLContext (boost::asio::ssl::context::sslv23), m_ShutdownTimer (m_Service) { - if (port) - m_Acceptor = std::make_unique(m_Service, - boost::asio::ip::tcp::endpoint(boost::asio::ip::make_address(address), port)); - else -#if defined(BOOST_ASIO_HAS_LOCAL_SOCKETS) - { - std::remove (address.c_str ()); // just in case - m_LocalAcceptor = std::make_unique(m_Service, - boost::asio::local::stream_protocol::endpoint(address)); - } -#else - LogPrint(eLogError, "I2PControl: Local sockets are not supported"); -#endif - i2p::config::GetOption("i2pcontrol.password", m_Password); // certificate / keys @@ -57,29 +45,15 @@ namespace client i2pcp_crt = i2p::fs::DataDirPath(i2pcp_crt); if (i2pcp_key.at(0) != '/') i2pcp_key = i2p::fs::DataDirPath(i2pcp_key); - if (!i2p::fs::Exists (i2pcp_crt) || !i2p::fs::Exists (i2pcp_key)) - { + if (!i2p::fs::Exists (i2pcp_crt) || !i2p::fs::Exists (i2pcp_key)) { LogPrint (eLogInfo, "I2PControl: Creating new certificate for control connection"); CreateCertificate (i2pcp_crt.c_str(), i2pcp_key.c_str()); - } - else + } else { LogPrint(eLogDebug, "I2PControl: Using cert from ", i2pcp_crt); + } m_SSLContext.set_options (boost::asio::ssl::context::default_workarounds | boost::asio::ssl::context::no_sslv2 | boost::asio::ssl::context::single_dh_use); - boost::system::error_code ec; - m_SSLContext.use_certificate_file (i2pcp_crt, boost::asio::ssl::context::pem, ec); - if (!ec) - m_SSLContext.use_private_key_file (i2pcp_key, boost::asio::ssl::context::pem, ec); - if (ec) - { - LogPrint (eLogInfo, "I2PControl: Failed to load ceritifcate: ", ec.message (), ". Recreating"); - CreateCertificate (i2pcp_crt.c_str(), i2pcp_key.c_str()); - m_SSLContext.use_certificate_file (i2pcp_crt, boost::asio::ssl::context::pem, ec); - if (!ec) - m_SSLContext.use_private_key_file (i2pcp_key, boost::asio::ssl::context::pem, ec); - if (ec) - // give up - LogPrint (eLogError, "I2PControl: Can't load certificates"); - } + m_SSLContext.use_certificate_file (i2pcp_crt, boost::asio::ssl::context::pem); + m_SSLContext.use_private_key_file (i2pcp_key, boost::asio::ssl::context::pem); // handlers m_MethodHandlers["Authenticate"] = &I2PControlService::AuthenticateHandler; @@ -110,7 +84,7 @@ namespace client { Accept (); m_IsRunning = true; - m_Thread = std::make_unique(std::bind (&I2PControlService::Run, this)); + m_Thread = new std::thread (std::bind (&I2PControlService::Run, this)); } } @@ -119,19 +93,12 @@ namespace client if (m_IsRunning) { m_IsRunning = false; - if (m_Acceptor) m_Acceptor->cancel (); -#if defined(BOOST_ASIO_HAS_LOCAL_SOCKETS) - if (m_LocalAcceptor) - { - auto path = m_LocalAcceptor->local_endpoint().path(); - m_LocalAcceptor->cancel (); - std::remove (path.c_str ()); - } -#endif + m_Acceptor.cancel (); m_Service.stop (); if (m_Thread) { m_Thread->join (); + delete m_Thread; m_Thread = nullptr; } } @@ -153,60 +120,40 @@ namespace client void I2PControlService::Accept () { - if (m_Acceptor) - { - auto newSocket = std::make_shared > (m_Service, m_SSLContext); - m_Acceptor->async_accept (newSocket->lowest_layer(), - [this, newSocket](const boost::system::error_code& ecode) - { - HandleAccepted (ecode, newSocket); - }); - } -#if defined(BOOST_ASIO_HAS_LOCAL_SOCKETS) - else if (m_LocalAcceptor) - { - auto newSocket = std::make_shared > (m_Service, m_SSLContext); - m_LocalAcceptor->async_accept (newSocket->lowest_layer(), - [this, newSocket](const boost::system::error_code& ecode) - { - HandleAccepted (ecode, newSocket); - }); - } -#endif + auto newSocket = std::make_shared (m_Service, m_SSLContext); + m_Acceptor.async_accept (newSocket->lowest_layer(), std::bind (&I2PControlService::HandleAccept, this, + std::placeholders::_1, newSocket)); } - template - void I2PControlService::HandleAccepted (const boost::system::error_code& ecode, - std::shared_ptr newSocket) + void I2PControlService::HandleAccept(const boost::system::error_code& ecode, std::shared_ptr socket) { if (ecode != boost::asio::error::operation_aborted) Accept (); - if (ecode) - { + if (ecode) { LogPrint (eLogError, "I2PControl: Accept error: ", ecode.message ()); return; } - LogPrint (eLogDebug, "I2PControl: New request from ", newSocket->lowest_layer ().remote_endpoint ()); - Handshake (newSocket); - } - - template + LogPrint (eLogDebug, "I2PControl: New request from ", socket->lowest_layer ().remote_endpoint ()); + Handshake (socket); + } + void I2PControlService::Handshake (std::shared_ptr socket) { socket->async_handshake(boost::asio::ssl::stream_base::server, - [this, socket](const boost::system::error_code& ecode) - { - if (ecode) - { - LogPrint (eLogError, "I2PControl: Handshake error: ", ecode.message ()); - return; - } - ReadRequest (socket); - }); + std::bind( &I2PControlService::HandleHandshake, this, std::placeholders::_1, socket)); + } + + void I2PControlService::HandleHandshake (const boost::system::error_code& ecode, std::shared_ptr socket) + { + if (ecode) { + LogPrint (eLogError, "I2PControl: Handshake error: ", ecode.message ()); + return; + } + //std::this_thread::sleep_for (std::chrono::milliseconds(5)); + ReadRequest (socket); } - template void I2PControlService::ReadRequest (std::shared_ptr socket) { auto request = std::make_shared(); @@ -216,13 +163,10 @@ namespace client #else boost::asio::buffer (request->data (), request->size ()), #endif - [this, socket, request](const boost::system::error_code& ecode, size_t bytes_transferred) - { - HandleRequestReceived (ecode, bytes_transferred, socket, request); - }); + std::bind(&I2PControlService::HandleRequestReceived, this, + std::placeholders::_1, std::placeholders::_2, socket, request)); } - template void I2PControlService::HandleRequestReceived (const boost::system::error_code& ecode, size_t bytes_transferred, std::shared_ptr socket, std::shared_ptr buf) @@ -300,7 +244,6 @@ namespace client } } - template void I2PControlService::SendResponse (std::shared_ptr socket, std::shared_ptr buf, std::ostringstream& response, bool isHtml) { @@ -310,7 +253,7 @@ namespace client std::ostringstream header; header << "HTTP/1.1 200 OK\r\n"; header << "Connection: close\r\n"; - header << "Content-Length: " << std::to_string(len) << "\r\n"; + header << "Content-Length: " << boost::lexical_cast(len) << "\r\n"; header << "Content-Type: application/json\r\n"; header << "Date: "; std::time_t t = std::time (nullptr); @@ -323,11 +266,16 @@ namespace client memcpy (buf->data () + offset, response.str ().c_str (), len); boost::asio::async_write (*socket, boost::asio::buffer (buf->data (), offset + len), boost::asio::transfer_all (), - [socket, buf](const boost::system::error_code& ecode, std::size_t bytes_transferred) - { - if (ecode) - LogPrint (eLogError, "I2PControl: Write error: ", ecode.message ()); - }); + std::bind(&I2PControlService::HandleResponseSent, this, + std::placeholders::_1, std::placeholders::_2, socket, buf)); + } + + void I2PControlService::HandleResponseSent (const boost::system::error_code& ecode, std::size_t bytes_transferred, + std::shared_ptr socket, std::shared_ptr buf) + { + if (ecode) { + LogPrint (eLogError, "I2PControl: Write error: ", ecode.message ()); + } } // handlers @@ -455,7 +403,7 @@ namespace client X509_NAME_add_entry_by_txt (name, "O", MBSTRING_ASC, (unsigned char *)I2P_CONTROL_CERTIFICATE_ORGANIZATION, -1, -1, 0); // organization X509_NAME_add_entry_by_txt (name, "CN", MBSTRING_ASC, (unsigned char *)I2P_CONTROL_CERTIFICATE_COMMON_NAME, -1, -1, 0); // common name X509_set_issuer_name (x509, name); // set issuer to ourselves - X509_sign (x509, pkey, EVP_sha1 ()); // sign, last param must be NULL for EdDSA + X509_sign (x509, pkey, EVP_sha1 ()); // sign // save cert if ((f = fopen (crt_path, "wb")) != NULL) { diff --git a/daemon/I2PControl.h b/daemon/I2PControl.h index 83dd6549..af152631 100644 --- a/daemon/I2PControl.h +++ b/daemon/I2PControl.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2025, The PurpleI2P Project +* Copyright (c) 2013-2022, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -35,6 +35,8 @@ namespace client class I2PControlService: public I2PControlHandlers { + typedef boost::asio::ssl::stream ssl_socket; + public: I2PControlService (const std::string& address, int port); @@ -47,18 +49,16 @@ namespace client void Run (); void Accept (); - template - void HandleAccepted (const boost::system::error_code& ecode, std::shared_ptr newSocket); - template + void HandleAccept(const boost::system::error_code& ecode, std::shared_ptr socket); void Handshake (std::shared_ptr socket); - template + void HandleHandshake (const boost::system::error_code& ecode, std::shared_ptr socket); void ReadRequest (std::shared_ptr socket); - template void HandleRequestReceived (const boost::system::error_code& ecode, size_t bytes_transferred, std::shared_ptr socket, std::shared_ptr buf); - template void SendResponse (std::shared_ptr socket, std::shared_ptr buf, std::ostringstream& response, bool isHtml); + void HandleResponseSent (const boost::system::error_code& ecode, std::size_t bytes_transferred, + std::shared_ptr socket, std::shared_ptr buf); void CreateCertificate (const char *crt_path, const char *key_path); @@ -86,13 +86,10 @@ namespace client std::string m_Password; bool m_IsRunning; - std::unique_ptr m_Thread; + std::thread * m_Thread; - boost::asio::io_context m_Service; - std::unique_ptr m_Acceptor; -#if defined(BOOST_ASIO_HAS_LOCAL_SOCKETS) - std::unique_ptr m_LocalAcceptor; -#endif + boost::asio::io_service m_Service; + boost::asio::ip::tcp::acceptor m_Acceptor; boost::asio::ssl::context m_SSLContext; boost::asio::deadline_timer m_ShutdownTimer; std::set m_Tokens; diff --git a/daemon/UPnP.cpp b/daemon/UPnP.cpp index 8e6dbcf6..7885578e 100644 --- a/daemon/UPnP.cpp +++ b/daemon/UPnP.cpp @@ -52,7 +52,7 @@ namespace transport { m_IsRunning = true; LogPrint(eLogInfo, "UPnP: Starting"); - boost::asio::post (m_Service, std::bind (&UPnP::Discover, this)); + m_Service.post (std::bind (&UPnP::Discover, this)); std::unique_lock l(m_StartedMutex); m_Thread.reset (new std::thread (std::bind (&UPnP::Run, this))); m_Started.wait_for (l, std::chrono::seconds (5)); // 5 seconds maximum @@ -150,7 +150,7 @@ namespace transport // UPnP discovered LogPrint (eLogDebug, "UPnP: ExternalIPAddress is ", m_externalIPAddress); - i2p::context.UpdateAddress (boost::asio::ip::make_address (m_externalIPAddress)); + i2p::context.UpdateAddress (boost::asio::ip::address::from_string (m_externalIPAddress)); // port mapping PortMapping (); } diff --git a/daemon/UPnP.h b/daemon/UPnP.h index 2a5fe9f3..d865df40 100644 --- a/daemon/UPnP.h +++ b/daemon/UPnP.h @@ -67,7 +67,7 @@ namespace transport std::unique_ptr m_Thread; std::condition_variable m_Started; std::mutex m_StartedMutex; - boost::asio::io_context m_Service; + boost::asio::io_service m_Service; boost::asio::deadline_timer m_Timer; bool m_upnpUrlsInitialized = false; struct UPNPUrls m_upnpUrls; diff --git a/daemon/UnixDaemon.cpp b/daemon/UnixDaemon.cpp index 66661e0f..d1eb1c39 100644 --- a/daemon/UnixDaemon.cpp +++ b/daemon/UnixDaemon.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2024, The PurpleI2P Project +* Copyright (c) 2013-2020, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -25,7 +25,6 @@ #include "RouterContext.h" #include "ClientContext.h" #include "Transports.h" -#include "util.h" void handle_signal(int sig) { @@ -221,7 +220,6 @@ namespace i2p void DaemonLinux::run () { - i2p::util::SetThreadName ("i2pd-daemon"); while (running) { std::this_thread::sleep_for (std::chrono::seconds(1)); diff --git a/debian/changelog b/debian/changelog index d170f534..0fe779da 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,20 +1,8 @@ -i2pd (2.56.0-1) unstable; urgency=medium - - * updated to version 2.56.0/0.9.65 - - -- orignal Tue, 11 Feb 2025 16:00:00 +0000 - -i2pd (2.55.0-1) unstable; urgency=medium - - * updated to version 2.55.0 - - -- orignal Mon, 30 Dec 2024 16:00:00 +0000 - i2pd (2.54.0-1) unstable; urgency=medium * updated to version 2.54.0/0.9.64 - -- orignal Sun, 6 Oct 2024 16:00:00 +0000 +-- orignal Sun, 6 Oct 2024 16:00:00 +0000 i2pd (2.53.1-1) unstable; urgency=medium diff --git a/debian/patches/01-upnp.patch b/debian/patches/01-upnp.patch index 74d36c06..bec8f2b0 100644 --- a/debian/patches/01-upnp.patch +++ b/debian/patches/01-upnp.patch @@ -2,13 +2,13 @@ Description: Enable UPnP usage in package Author: r4sas Reviewed-By: r4sas -Last-Update: 2024-12-30 +Last-Update: 2022-03-23 --- i2pd.orig/Makefile +++ i2pd/Makefile -@@ -31,7 +31,7 @@ # import source files lists - include filelist.mk +@@ -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) diff --git a/i18n/Afrikaans.cpp b/i18n/Afrikaans.cpp index b69c42ef..b582a06a 100644 --- a/i18n/Afrikaans.cpp +++ b/i18n/Afrikaans.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2021-2025, The PurpleI2P Project +* Copyright (c) 2021, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -29,7 +29,7 @@ namespace afrikaans // language namespace return n != 1 ? 1 : 0; } - static const LocaleStrings strings + static std::map strings { {"failed", "Het misluk"}, {"unknown", "onbekend"}, diff --git a/i18n/Armenian.cpp b/i18n/Armenian.cpp index 67955d8a..b99e5032 100644 --- a/i18n/Armenian.cpp +++ b/i18n/Armenian.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2021-2025, The PurpleI2P Project +* Copyright (c) 2021-2023, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -29,7 +29,7 @@ namespace armenian // language namespace return n != 1 ? 1 : 0; } - static const LocaleStrings strings + static std::map strings { {"%.2f KiB", "%.2f ԿիԲ"}, {"%.2f MiB", "%.2f ՄիԲ"}, diff --git a/i18n/Chinese.cpp b/i18n/Chinese.cpp index e3b63ebd..ad46178c 100644 --- a/i18n/Chinese.cpp +++ b/i18n/Chinese.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2022-2025, The PurpleI2P Project +* Copyright (c) 2022-2024, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -29,7 +29,7 @@ namespace chinese // language namespace return 0; } - static const LocaleStrings strings + static std::map strings { {"%.2f KiB", "%.2f KiB"}, {"%.2f MiB", "%.2f MiB"}, diff --git a/i18n/Czech.cpp b/i18n/Czech.cpp index 94803354..3b865474 100644 --- a/i18n/Czech.cpp +++ b/i18n/Czech.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2022-2025, The PurpleI2P Project +* Copyright (c) 2022-2024, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -29,7 +29,7 @@ namespace czech // language namespace return (n == 1) ? 0 : (n >= 2 && n <= 4) ? 1 : 2; } - static const LocaleStrings strings + static std::map strings { {"%.2f KiB", "%.2f KiB"}, {"%.2f MiB", "%.2f MiB"}, diff --git a/i18n/English.cpp b/i18n/English.cpp index fb774527..2670e984 100644 --- a/i18n/English.cpp +++ b/i18n/English.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2021-2025, The PurpleI2P Project +* Copyright (c) 2021, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -30,7 +30,7 @@ namespace english // language namespace return n != 1 ? 1 : 0; } - static const LocaleStrings strings + static std::map strings { {"", ""}, }; diff --git a/i18n/French.cpp b/i18n/French.cpp index 985296a3..999f82b9 100644 --- a/i18n/French.cpp +++ b/i18n/French.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2022-2025, The PurpleI2P Project +* Copyright (c) 2022-2024, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -29,7 +29,7 @@ namespace french // language namespace return n != 1 ? 1 : 0; } - static const LocaleStrings strings + static std::map strings { {"%.2f KiB", "%.2f Kio"}, {"%.2f MiB", "%.2f Mio"}, diff --git a/i18n/German.cpp b/i18n/German.cpp index 90ce82f4..02662e8e 100644 --- a/i18n/German.cpp +++ b/i18n/German.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2022-2025, The PurpleI2P Project +* Copyright (c) 2022-2023, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -29,7 +29,7 @@ namespace german // language namespace return n != 1 ? 1 : 0; } - static const LocaleStrings strings + static std::map strings { {"%.2f KiB", "%.2f KiB"}, {"%.2f MiB", "%.2f MiB"}, diff --git a/i18n/I18N.cpp b/i18n/I18N.cpp index 48a02357..cf4873eb 100644 --- a/i18n/I18N.cpp +++ b/i18n/I18N.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2021-2025, The PurpleI2P Project +* Copyright (c) 2021-2023, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -30,12 +30,12 @@ namespace i18n } } - std::string_view translate (std::string_view arg) + std::string translate (const std::string& arg) { return i2p::client::context.GetLanguage ()->GetString (arg); } - std::string translate (const std::string& arg, const std::string& arg2, const int n) + std::string translate (const std::string& arg, const std::string& arg2, const int& n) { return i2p::client::context.GetLanguage ()->GetPlural (arg, arg2, n); } diff --git a/i18n/I18N.h b/i18n/I18N.h index 8ed77a6b..6ec5b16e 100644 --- a/i18n/I18N.h +++ b/i18n/I18N.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2021-2025, The PurpleI2P Project +* Copyright (c) 2021-2023, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -10,7 +10,6 @@ #define __I18N_H__ #include -#include #include #include #include @@ -19,13 +18,12 @@ namespace i2p { namespace i18n { - typedef std::map LocaleStrings; class Locale { public: Locale ( const std::string& language, - const LocaleStrings& strings, + const std::map& strings, const std::map>& plurals, std::function formula ): m_Language (language), m_Strings (strings), m_Plurals (plurals), m_Formula (formula) { }; @@ -36,7 +34,7 @@ namespace i18n return m_Language; } - std::string_view GetString (std::string_view arg) const + std::string GetString (const std::string& arg) const { const auto it = m_Strings.find(arg); if (it == m_Strings.end()) @@ -49,7 +47,7 @@ namespace i18n } } - std::string GetPlural (const std::string& arg, const std::string& arg2, int n) const + std::string GetPlural (const std::string& arg, const std::string& arg2, const int& n) const { const auto it = m_Plurals.find(arg2); if (it == m_Plurals.end()) // not found, fallback to english @@ -65,14 +63,14 @@ namespace i18n private: const std::string m_Language; - const LocaleStrings m_Strings; + const std::map m_Strings; const std::map> m_Plurals; std::function m_Formula; }; void SetLanguage(const std::string &lang); - std::string_view translate (std::string_view arg); - std::string translate (const std::string& arg, const std::string& arg2, int n); + std::string translate (const std::string& arg); + std::string translate (const std::string& arg, const std::string& arg2, const int& n); } // i18n } // i2p @@ -81,7 +79,7 @@ namespace i18n * @param arg String with message */ template -std::string_view tr (TValue&& arg) +std::string tr (TValue&& arg) { return i2p::i18n::translate(std::forward(arg)); } @@ -94,7 +92,7 @@ std::string_view tr (TValue&& arg) template std::string tr (TValue&& arg, TArgs&&... args) { - std::string tr_str = std::string (i2p::i18n::translate(std::forward(arg))); // TODO: + std::string tr_str = i2p::i18n::translate(std::forward(arg)); size_t size = std::snprintf(NULL, 0, tr_str.c_str(), std::forward(args)...); std::string str(size, 0); @@ -110,7 +108,7 @@ std::string tr (TValue&& arg, TArgs&&... args) * @param n Integer, used for selection of form */ template -std::string ntr (TValue&& arg, TValue2&& arg2, int n) +std::string ntr (TValue&& arg, TValue2&& arg2, int& n) { return i2p::i18n::translate(std::forward(arg), std::forward(arg2), std::forward(n)); } @@ -123,7 +121,7 @@ std::string ntr (TValue&& arg, TValue2&& arg2, int n) * @param args Array of arguments for string formatting */ template -std::string ntr (TValue&& arg, TValue2&& arg2, int n, TArgs&&... args) +std::string ntr (TValue&& arg, TValue2&& arg2, int& n, TArgs&&... args) { std::string tr_str = i2p::i18n::translate(std::forward(arg), std::forward(arg2), std::forward(n)); diff --git a/i18n/Italian.cpp b/i18n/Italian.cpp index 0ae26f21..2dcaab5e 100644 --- a/i18n/Italian.cpp +++ b/i18n/Italian.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2022-2025, The PurpleI2P Project +* Copyright (c) 2022-2023, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -29,7 +29,7 @@ namespace italian // language namespace return n != 1 ? 1 : 0; } - static const LocaleStrings strings + static std::map strings { {"%.2f KiB", "%.2f KiB"}, {"%.2f MiB", "%.2f MiB"}, diff --git a/i18n/Polish.cpp b/i18n/Polish.cpp index 0e8df096..b2abda11 100644 --- a/i18n/Polish.cpp +++ b/i18n/Polish.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2023-2025, The PurpleI2P Project +* Copyright (c) 2023-2024, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -29,7 +29,7 @@ namespace polish // language namespace return (n == 1 ? 0 : n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 10 || n % 100 >= 20) ? 1 : 2); } - static const LocaleStrings strings + static std::map strings { {"%.2f KiB", "%.2f KiB"}, {"%.2f MiB", "%.2f MiB"}, diff --git a/i18n/Portuguese.cpp b/i18n/Portuguese.cpp index 26204dc3..0c490ba3 100644 --- a/i18n/Portuguese.cpp +++ b/i18n/Portuguese.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2023-2025, The PurpleI2P Project +* Copyright (c) 2023-2024, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -29,7 +29,7 @@ namespace portuguese // language namespace return n != 1 ? 1 : 0; } - static const LocaleStrings strings + static std::map strings { {"%.2f KiB", "%.2f KiB"}, {"%.2f MiB", "%.2f MiB"}, diff --git a/i18n/Russian.cpp b/i18n/Russian.cpp index 235cc0ae..15952710 100644 --- a/i18n/Russian.cpp +++ b/i18n/Russian.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2021-2025, The PurpleI2P Project +* Copyright (c) 2021-2023, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -29,7 +29,7 @@ namespace russian // language namespace return n % 10 == 1 && n % 100 != 11 ? 0 : n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 10 || n % 100 >= 20) ? 1 : 2; } - static const LocaleStrings strings + static std::map strings { {"%.2f KiB", "%.2f КиБ"}, {"%.2f MiB", "%.2f МиБ"}, diff --git a/i18n/Spanish.cpp b/i18n/Spanish.cpp index 0e657fb4..a5ecc30a 100644 --- a/i18n/Spanish.cpp +++ b/i18n/Spanish.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2022-2025, The PurpleI2P Project +* Copyright (c) 2022-2023, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -29,7 +29,7 @@ namespace spanish // language namespace return n != 1 ? 1 : 0; } - static const LocaleStrings strings + static std::map strings { {"%.2f KiB", "%.2f KiB"}, {"%.2f MiB", "%.2f MiB"}, diff --git a/i18n/Swedish.cpp b/i18n/Swedish.cpp index df13d22f..05ed1e18 100644 --- a/i18n/Swedish.cpp +++ b/i18n/Swedish.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2023-2025, The PurpleI2P Project +* Copyright (c) 2023, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -29,7 +29,7 @@ namespace swedish // language namespace return n != 1 ? 1 : 0; } - static const LocaleStrings strings + static std::map strings { {"%.2f KiB", "%.2f KiB"}, {"%.2f MiB", "%.2f MiB"}, diff --git a/i18n/Turkish.cpp b/i18n/Turkish.cpp index 9946b336..d4398ebe 100644 --- a/i18n/Turkish.cpp +++ b/i18n/Turkish.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2023-2025, The PurpleI2P Project +* Copyright (c) 2023, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -29,7 +29,7 @@ namespace turkish // language namespace return n != 1 ? 1 : 0; } - static const LocaleStrings strings + static std::map strings { {"%.2f KiB", "%.2f KiB"}, {"%.2f MiB", "%.2f MiB"}, diff --git a/i18n/Turkmen.cpp b/i18n/Turkmen.cpp index 7efb8891..35ee0f89 100644 --- a/i18n/Turkmen.cpp +++ b/i18n/Turkmen.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2021-2025, The PurpleI2P Project +* Copyright (c) 2021-2023, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -29,7 +29,7 @@ namespace turkmen // language namespace return n != 1 ? 1 : 0; } - static const LocaleStrings strings + static std::map strings { {"%.2f KiB", "%.2f KiB"}, {"%.2f MiB", "%.2f MiB"}, diff --git a/i18n/Ukrainian.cpp b/i18n/Ukrainian.cpp index c1b6c772..d089c142 100644 --- a/i18n/Ukrainian.cpp +++ b/i18n/Ukrainian.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2021-2025, The PurpleI2P Project +* Copyright (c) 2021-2023, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -29,7 +29,7 @@ namespace ukrainian // language namespace return n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2; } - static const LocaleStrings strings + static std::map strings { {"%.2f KiB", "%.2f КіБ"}, {"%.2f MiB", "%.2f МіБ"}, diff --git a/i18n/Uzbek.cpp b/i18n/Uzbek.cpp index 8e870772..cf94a489 100644 --- a/i18n/Uzbek.cpp +++ b/i18n/Uzbek.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2021-2025, The PurpleI2P Project +* Copyright (c) 2021-2023, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -29,7 +29,7 @@ namespace uzbek // language namespace return n > 1 ? 1 : 0; } - static const LocaleStrings strings + static std::map strings { {"%.2f KiB", "%.2f KiB"}, {"%.2f MiB", "%.2f MiB"}, diff --git a/libi2pd/Base.cpp b/libi2pd/Base.cpp index bc9da4fb..b8de571b 100644 --- a/libi2pd/Base.cpp +++ b/libi2pd/Base.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2025, The PurpleI2P Project +* Copyright (c) 2013-2023, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -15,7 +15,7 @@ namespace i2p { namespace data { - static constexpr char T32[32] = + static const char T32[32] = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', @@ -27,6 +27,11 @@ namespace data { return T32; } + + bool IsBase32 (char ch) + { + return (ch >= 'a' && ch <= 'z') || (ch >= '2' && ch <= '7'); + } static void iT64Build(void); @@ -38,7 +43,7 @@ namespace data * Direct Substitution Table */ - static constexpr char T64[64] = + static const char T64[64] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', @@ -54,17 +59,24 @@ 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) */ + static char iT64[256]; static int isFirstTime = 1; /* * Padding */ - static constexpr char P64 = '='; + + static char P64 = '='; /* * @@ -74,112 +86,134 @@ namespace data * Converts binary encoded data to BASE64 format. * */ - std::string ByteStreamToBase64 (// base64 encoded string - const uint8_t * InBuffer, // Input buffer, binary data - size_t InCount // Number of bytes in the input buffer + + size_t ByteStreamToBase64 ( /* Number of bytes in the encoded buffer */ + const uint8_t * InBuffer, /* Input buffer, binary data */ + size_t InCount, /* Number of bytes in the input buffer */ + char * OutBuffer, /* output buffer */ + size_t len /* length of output buffer */ ) { unsigned char * ps; + unsigned char * pd; unsigned char acc_1; unsigned char acc_2; int i; int n; int m; + size_t outCount; ps = (unsigned char *)InBuffer; n = InCount / 3; m = InCount % 3; - size_t outCount = m ? (4 * (n + 1)) : (4 * n); + if (!m) + outCount = 4 * n; + else + outCount = 4 * (n + 1); - std::string out; - out.reserve (outCount); + if (outCount > len) return 0; + + pd = (unsigned char *)OutBuffer; for ( i = 0; i < n; i++ ) { acc_1 = *ps++; acc_2 = (acc_1 << 4) & 0x30; - acc_1 >>= 2; // base64 digit #1 - out.push_back (T64[acc_1]); + acc_1 >>= 2; /* base64 digit #1 */ + *pd++ = T64[acc_1]; acc_1 = *ps++; - acc_2 |= acc_1 >> 4; // base64 digit #2 - out.push_back (T64[acc_2]); + acc_2 |= acc_1 >> 4; /* base64 digit #2 */ + *pd++ = T64[acc_2]; acc_1 &= 0x0f; acc_1 <<= 2; acc_2 = *ps++; - acc_1 |= acc_2 >> 6; // base64 digit #3 - out.push_back (T64[acc_1]); - acc_2 &= 0x3f; // base64 digit #4 - out.push_back (T64[acc_2]); + acc_1 |= acc_2 >> 6; /* base64 digit #3 */ + *pd++ = T64[acc_1]; + acc_2 &= 0x3f; /* base64 digit #4 */ + *pd++ = T64[acc_2]; } if ( m == 1 ) { acc_1 = *ps++; - acc_2 = (acc_1 << 4) & 0x3f; // base64 digit #2 - acc_1 >>= 2; // base64 digit #1 - out.push_back (T64[acc_1]); - out.push_back (T64[acc_2]); - out.push_back (P64); - out.push_back (P64); + acc_2 = (acc_1 << 4) & 0x3f; /* base64 digit #2 */ + acc_1 >>= 2; /* base64 digit #1 */ + *pd++ = T64[acc_1]; + *pd++ = T64[acc_2]; + *pd++ = P64; + *pd++ = P64; } else if ( m == 2 ) { acc_1 = *ps++; acc_2 = (acc_1 << 4) & 0x3f; - acc_1 >>= 2; // base64 digit #1 - out.push_back (T64[acc_1]); + acc_1 >>= 2; /* base64 digit #1 */ + *pd++ = T64[acc_1]; acc_1 = *ps++; - acc_2 |= acc_1 >> 4; // base64 digit #2 - out.push_back (T64[acc_2]); + acc_2 |= acc_1 >> 4; /* base64 digit #2 */ + *pd++ = T64[acc_2]; acc_1 &= 0x0f; - acc_1 <<= 2; // base64 digit #3 - out.push_back (T64[acc_1]); - out.push_back (P64); + acc_1 <<= 2; /* base64 digit #3 */ + *pd++ = T64[acc_1]; + *pd++ = P64; } - return out; - } - + return outCount; + } + /* * * Base64ToByteStream * ------------------ * - * Converts BASE64 encoded string to binary format. If input buffer is + * Converts BASE64 encoded data to binary format. If input buffer is * not properly padded, buffer of negative length is returned * */ - size_t Base64ToByteStream ( // Number of output bytes - std::string_view base64Str, // BASE64 encoded string - uint8_t * OutBuffer, // output buffer length - size_t len // length of output buffer + + size_t Base64ToByteStream ( /* Number of output bytes */ + const char * InBuffer, /* BASE64 encoded buffer */ + size_t InCount, /* Number of input bytes */ + uint8_t * OutBuffer, /* output buffer length */ + size_t len /* length of output buffer */ ) { + unsigned char * ps; unsigned char * pd; unsigned char acc_1; unsigned char acc_2; + int i; + int n; + int m; size_t outCount; - if (base64Str.empty () || base64Str[0] == P64) return 0; - auto d = std::div (base64Str.length (), 4); - if (!d.rem) - outCount = 3 * d.quot; + if (isFirstTime) + iT64Build(); + + n = InCount / 4; + m = InCount % 4; + + if (InCount && !m) + outCount = 3 * n; else return 0; - if (isFirstTime) iT64Build(); + if(*InBuffer == P64) + return 0; + + ps = (unsigned char *)(InBuffer + InCount - 1); + while ( *ps-- == P64 ) + outCount--; + ps = (unsigned char *)InBuffer; + + if (outCount > len) + return 0; - auto pos = base64Str.find_last_not_of (P64); - if (pos == base64Str.npos) return 0; - outCount -= (base64Str.length () - pos - 1); - if (outCount > len) return 0; - - auto ps = base64Str.begin (); pd = OutBuffer; auto endOfOutBuffer = OutBuffer + outCount; - for (int i = 0; i < d.quot; i++) + for ( i = 0; i < n; i++ ) { - acc_1 = iT64[int(*ps++)]; - acc_2 = iT64[int(*ps++)]; + acc_1 = iT64[*ps++]; + acc_2 = iT64[*ps++]; acc_1 <<= 2; acc_1 |= acc_2 >> 4; *pd++ = acc_1; @@ -187,30 +221,45 @@ namespace data break; acc_2 <<= 4; - acc_1 = iT64[int(*ps++)]; + acc_1 = iT64[*ps++]; acc_2 |= acc_1 >> 2; *pd++ = acc_2; if (pd >= endOfOutBuffer) break; - acc_2 = iT64[int(*ps++)]; + acc_2 = iT64[*ps++]; acc_2 |= acc_1 << 6; *pd++ = acc_2; } return outCount; - } - - std::string ToBase64Standard (std::string_view in) + } + + size_t Base64EncodingBufferSize (const size_t input_size) { - auto str = ByteStreamToBase64 ((const uint8_t *)in.data (), in.length ()); + auto d = div (input_size, 3); + if (d.rem) + d.quot++; + + return 4 * d.quot; + } + + std::string ToBase64Standard (const std::string& in) + { + auto len = Base64EncodingBufferSize (in.length ()); + char * str = new char[len + 1]; + auto l = ByteStreamToBase64 ((const uint8_t *)in.c_str (), in.length (), str, len); + str[l] = 0; // replace '-' by '+' and '~' by '/' - for (auto& ch: str) - if (ch == '-') - ch = '+'; - else if (ch == '~') - ch = '/'; - return str; + for (size_t i = 0; i < l; i++) + if (str[i] == '-') + str[i] = '+'; + else if (str[i] == '~') + str[i] = '/'; + + std::string s(str); + delete[] str; + return s; } /* @@ -231,12 +280,13 @@ namespace data iT64[(int)P64] = 0; } - size_t Base32ToByteStream (std::string_view base32Str, uint8_t * outBuf, size_t outLen) + size_t Base32ToByteStream (const char * inBuf, size_t len, uint8_t * outBuf, size_t outLen) { unsigned int tmp = 0, bits = 0; size_t ret = 0; - for (auto ch: base32Str) + for (size_t i = 0; i < len; i++) { + char ch = inBuf[i]; if (ch >= '2' && ch <= '7') // digit ch = (ch - '2') + 26; // 26 means a-z else if (ch >= 'a' && ch <= 'z') @@ -256,15 +306,13 @@ namespace data tmp <<= 5; } return ret; - } - - std::string ByteStreamToBase32 (const uint8_t * inBuf, size_t len) + } + + size_t ByteStreamToBase32 (const uint8_t * inBuf, size_t len, char * outBuf, size_t outLen) { - std::string out; - out.reserve ((len * 8 + 4) / 5); - size_t pos = 1; + size_t ret = 0, pos = 1; unsigned int bits = 8, tmp = inBuf[0]; - while (bits > 0 || pos < len) + while (ret < outLen && (bits > 0 || pos < len)) { if (bits < 5) { @@ -284,9 +332,10 @@ namespace data bits -= 5; int ind = (tmp >> bits) & 0x1F; - out.push_back ((ind < 26) ? (ind + 'a') : ((ind - 26) + '2')); + outBuf[ret] = (ind < 26) ? (ind + 'a') : ((ind - 26) + '2'); + ret++; } - return out; - } + return ret; + } } } diff --git a/libi2pd/Base.h b/libi2pd/Base.h index 945dc8b3..a163435c 100644 --- a/libi2pd/Base.h +++ b/libi2pd/Base.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2025, The PurpleI2P Project +* Copyright (c) 2013-2023, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -11,42 +11,27 @@ #include #include -#include -#include - -namespace i2p -{ -namespace data -{ - std::string ByteStreamToBase64 (const uint8_t * InBuffer, size_t InCount); - size_t Base64ToByteStream (std::string_view base64Str, uint8_t * OutBuffer, size_t len); +#include +namespace i2p { +namespace data { + size_t ByteStreamToBase64 (const uint8_t * InBuffer, size_t InCount, char * OutBuffer, size_t len); + size_t Base64ToByteStream (const char * InBuffer, size_t InCount, uint8_t * OutBuffer, size_t len ); const char * GetBase32SubstitutionTable (); const char * GetBase64SubstitutionTable (); - constexpr bool IsBase64 (char ch) - { - return (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') || (ch >= '0' && ch <= '9') || ch == '-' || ch == '~'; - } + bool IsBase64 (char ch); - size_t Base32ToByteStream (std::string_view base32Str, uint8_t * outBuf, size_t outLen); - std::string ByteStreamToBase32 (const uint8_t * inBuf, size_t len); - constexpr bool IsBase32 (char ch) - { - return (ch >= 'a' && ch <= 'z') || (ch >= '2' && ch <= '7'); - } + 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 */ - inline size_t Base64EncodingBufferSize(size_t input_size) - { - auto d = std::div (input_size, 3); - if (d.rem) d.quot++; - return 4 * d.quot; - } + size_t Base64EncodingBufferSize(const size_t input_size); + + std::string ToBase64Standard (const std::string& in); // using standard table, for Proxy-Authorization - std::string ToBase64Standard (std::string_view in); // using standard table, for Proxy-Authorization - } // data } // i2p diff --git a/libi2pd/Blinding.cpp b/libi2pd/Blinding.cpp index a661b428..ced086e1 100644 --- a/libi2pd/Blinding.cpp +++ b/libi2pd/Blinding.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2025, The PurpleI2P Project +* Copyright (c) 2013-2022, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -152,11 +152,11 @@ namespace data m_BlindedSigType = m_SigType; } - BlindedPublicKey::BlindedPublicKey (std::string_view b33): + BlindedPublicKey::BlindedPublicKey (const std::string& b33): m_SigType (0) // 0 means invalid, we can't blind DSA, set it later { uint8_t addr[40]; // TODO: define length from b33 - size_t l = i2p::data::Base32ToByteStream (b33, addr, 40); + size_t l = i2p::data::Base32ToByteStream (b33.c_str (), b33.length (), addr, 40); if (l < 32) { LogPrint (eLogError, "Blinding: Malformed b33 ", b33); @@ -198,7 +198,7 @@ namespace data std::string BlindedPublicKey::ToB33 () const { if (m_PublicKey.size () > 32) return ""; // assume 25519 - uint8_t addr[35]; + uint8_t addr[35]; char str[60]; // TODO: define actual length uint8_t flags = 0; if (m_IsClientAuth) flags |= B33_PER_CLIENT_AUTH_FLAG; addr[0] = flags; // flags @@ -208,7 +208,8 @@ namespace data uint32_t checksum = crc32 (0, addr + 3, m_PublicKey.size ()); // checksum is Little Endian addr[0] ^= checksum; addr[1] ^= (checksum >> 8); addr[2] ^= (checksum >> 16); - return ByteStreamToBase32 (addr, m_PublicKey.size () + 3); + auto l = ByteStreamToBase32 (addr, m_PublicKey.size () + 3, str, 60); + return std::string (str, str + l); } void BlindedPublicKey::GetCredential (uint8_t * credential) const diff --git a/libi2pd/Blinding.h b/libi2pd/Blinding.h index fc11f613..c78db003 100644 --- a/libi2pd/Blinding.h +++ b/libi2pd/Blinding.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2025, The PurpleI2P Project +* Copyright (c) 2013-2020, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -11,7 +11,6 @@ #include #include -#include #include #include "Identity.h" @@ -24,7 +23,7 @@ namespace data public: BlindedPublicKey (std::shared_ptr identity, bool clientAuth = false); - BlindedPublicKey (std::string_view b33); // from b33 without .b32.i2p + BlindedPublicKey (const std::string& b33); // from b33 without .b32.i2p std::string ToB33 () const; const uint8_t * GetPublicKey () const { return m_PublicKey.data (); }; diff --git a/libi2pd/CPU.cpp b/libi2pd/CPU.cpp new file mode 100644 index 00000000..77820c88 --- /dev/null +++ b/libi2pd/CPU.cpp @@ -0,0 +1,68 @@ +/* +* Copyright (c) 2013-2023, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + +#include "CPU.h" +#include "Log.h" + +#ifndef bit_AES + #define bit_AES (1 << 25) +#endif + +#if defined(__GNUC__) && __GNUC__ < 6 && IS_X86 + #include +#endif + +#ifdef _MSC_VER + #include +#endif + +namespace i2p +{ +namespace cpu +{ + bool aesni = false; + + inline bool cpu_support_aes() + { +#if IS_X86 +#if defined(__clang__) +# if (__clang_major__ >= 6) + __builtin_cpu_init(); +# endif + return __builtin_cpu_supports("aes"); +#elif (defined(__GNUC__) && __GNUC__ >= 6) + __builtin_cpu_init(); + return __builtin_cpu_supports("aes"); +#elif (defined(__GNUC__) && __GNUC__ < 6) + int cpu_info[4]; + bool flag = false; + __cpuid(0, cpu_info[0], cpu_info[1], cpu_info[2], cpu_info[3]); + if (cpu_info[0] >= 0x00000001) { + __cpuid(0x00000001, cpu_info[0], cpu_info[1], cpu_info[2], cpu_info[3]); + flag = ((cpu_info[2] & bit_AES) != 0); + } + return flag; +#elif defined(_MSC_VER) + int cpu_info[4]; + __cpuid(cpu_info, 1); + return ((cpu_info[2] & bit_AES) != 0); +#endif +#endif + return false; + } + + void Detect(bool AesSwitch, bool force) + { + if ((cpu_support_aes() && AesSwitch) || (AesSwitch && force)) { + aesni = true; + } + + LogPrint(eLogInfo, "AESNI ", (aesni ? "enabled" : "disabled")); + } +} +} diff --git a/libi2pd/CPU.h b/libi2pd/CPU.h index 3fc38d47..1c30db48 100644 --- a/libi2pd/CPU.h +++ b/libi2pd/CPU.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2024, The PurpleI2P Project +* Copyright (c) 2013-2023, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -21,4 +21,20 @@ # define IS_X86_64 0 #endif +#if defined(__AES__) && !defined(_MSC_VER) && IS_X86 +# define SUPPORTS_AES 1 +#else +# define SUPPORTS_AES 0 +#endif + +namespace i2p +{ +namespace cpu +{ + extern bool aesni; + + void Detect(bool AesSwitch, bool force); +} +} + #endif diff --git a/libi2pd/Config.cpp b/libi2pd/Config.cpp index 939cd9ff..ab237613 100644 --- a/libi2pd/Config.cpp +++ b/libi2pd/Config.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2025, The PurpleI2P Project +* Copyright (c) 2013-2024, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -117,7 +117,7 @@ namespace config { ("httpproxy.latency.max", value()->default_value("0"), "HTTP proxy max latency for tunnels") ("httpproxy.outproxy", value()->default_value(""), "HTTP proxy upstream out proxy url") ("httpproxy.addresshelper", value()->default_value(true), "Enable or disable addresshelper") - ("httpproxy.senduseragent", value()->default_value(false), "Pass through user's User-Agent if enabled. Disabled by default") + ("httpproxy.senduseragent", value()->default_value(false), "Pass through user's User-Agent if enabled. Disabled by deafult") ("httpproxy.i2cp.leaseSetType", value()->default_value("3"), "Local destination's LeaseSet type") ("httpproxy.i2cp.leaseSetEncType", value()->default_value("0,4"), "Local destination's LeaseSet encryption type") ("httpproxy.i2cp.leaseSetPrivKey", value()->default_value(""), "LeaseSet private key") @@ -154,17 +154,6 @@ namespace config { ("socksproxy.i2p.streaming.profile", value()->default_value("1"), "SOCKS Proxy bandwidth usage profile. 1 - bulk(high), 2- interactive(low)") ; - options_description shareddest("Shared local destination options"); - shareddest.add_options() - ("shareddest.inbound.length", value()->default_value("3"), "Shared local destination inbound tunnel length") - ("shareddest.outbound.length", value()->default_value("3"), "Shared local destination outbound tunnel length") - ("shareddest.inbound.quantity", value()->default_value("3"), "Shared local destination inbound tunnels quantity") - ("shareddest.outbound.quantity", value()->default_value("3"), "Shared local destination outbound tunnels quantity") - ("shareddest.i2cp.leaseSetType", value()->default_value("3"), "Shared local destination's LeaseSet type") - ("shareddest.i2cp.leaseSetEncType", value()->default_value("0,4"), "Shared local destination's LeaseSet encryption type") - ("shareddest.i2p.streaming.profile", value()->default_value("2"), "Shared local destination bandwidth usage profile. 1 - bulk(high), 2- interactive(low)") - ; - options_description sam("SAM bridge options"); sam.add_options() ("sam.enabled", value()->default_value(true), "Enable or disable SAM Application bridge") @@ -234,21 +223,22 @@ namespace config { "https://reseed2.i2p.net/," "https://reseed.diva.exchange/," "https://reseed-fr.i2pd.xyz/," + "https://reseed.memcpy.io/," "https://reseed.onion.im/," "https://i2pseed.creativecowpat.net:8443/," "https://reseed.i2pgit.org/," - "https://coconut.incognet.io/," + "https://banana.incognet.io/," "https://reseed-pl.i2pd.xyz/," "https://www2.mk16.de/," "https://i2p.ghativega.in/," "https://i2p.novg.net/," - "https://reseed.stormycloud.org/," - "https://cubicchaos.net:8443/" + "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]/" ), "Reseed URLs through the Yggdrasil, separated by comma") ; @@ -326,11 +316,11 @@ namespace config { ("persist.addressbook", value()->default_value(true), "Persist full addresses (default: true)") ; - options_description cpuext("CPU encryption extensions options. Deprecated"); + options_description cpuext("CPU encryption extensions options"); cpuext.add_options() - ("cpuext.aesni", bool_switch()->default_value(true), "Deprecated option") + ("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(false), "Deprecated option") - ("cpuext.force", 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") ; options_description meshnets("Meshnet transports options"); @@ -352,7 +342,6 @@ namespace config { .add(httpserver) .add(httpproxy) .add(socksproxy) - .add(shareddest) .add(sam) .add(bob) .add(i2cp) diff --git a/libi2pd/Crypto.cpp b/libi2pd/Crypto.cpp index c41b4c10..2f9677c1 100644 --- a/libi2pd/Crypto.cpp +++ b/libi2pd/Crypto.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2025, 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 @@ #if OPENSSL_HKDF #include #endif -#if (OPENSSL_VERSION_NUMBER >= 0x030000000) // since 3.0.0 -#include -#include -#endif -#include "CPU.h" #include "Crypto.h" #include "Ed25519.h" #include "I2PEndian.h" @@ -33,7 +28,7 @@ namespace i2p { namespace crypto { - constexpr uint8_t elgp_[256]= + const uint8_t elgp_[256]= { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34, 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1, 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, @@ -53,9 +48,9 @@ namespace crypto 0x15, 0x72, 0x8E, 0x5A, 0x8A, 0xAC, 0xAA, 0x68, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; - constexpr int elgg_ = 2; + const int elgg_ = 2; - constexpr uint8_t dsap_[128]= + const uint8_t dsap_[128]= { 0x9c, 0x05, 0xb2, 0xaa, 0x96, 0x0d, 0x9b, 0x97, 0xb8, 0x93, 0x19, 0x63, 0xc9, 0xcc, 0x9e, 0x8c, 0x30, 0x26, 0xe9, 0xb8, 0xed, 0x92, 0xfa, 0xd0, 0xa6, 0x9c, 0xc8, 0x86, 0xd5, 0xbf, 0x80, 0x15, @@ -67,13 +62,13 @@ namespace crypto 0x28, 0x5d, 0x4c, 0xf2, 0x95, 0x38, 0xd9, 0xe3, 0xb6, 0x05, 0x1f, 0x5b, 0x22, 0xcc, 0x1c, 0x93 }; - constexpr uint8_t dsaq_[20]= + const uint8_t dsaq_[20]= { 0xa5, 0xdf, 0xc2, 0x8f, 0xef, 0x4c, 0xa1, 0xe2, 0x86, 0x74, 0x4c, 0xd8, 0xee, 0xd9, 0xd2, 0x9d, 0x68, 0x40, 0x46, 0xb7 }; - constexpr uint8_t dsag_[128]= + const uint8_t dsag_[128]= { 0x0c, 0x1f, 0x4d, 0x27, 0xd4, 0x00, 0x93, 0xb4, 0x29, 0xe9, 0x62, 0xd7, 0x22, 0x38, 0x24, 0xe0, 0xbb, 0xc4, 0x7e, 0x7c, 0x83, 0x2a, 0x39, 0x23, 0x6f, 0xc6, 0x83, 0xaf, 0x84, 0x88, 0x95, 0x81, @@ -85,7 +80,7 @@ namespace crypto 0xb3, 0xdb, 0xb1, 0x4a, 0x90, 0x5e, 0x7b, 0x2b, 0x3e, 0x93, 0xbe, 0x47, 0x08, 0xcb, 0xcc, 0x82 }; - constexpr int rsae_ = 65537; + const int rsae_ = 65537; struct CryptoConstants { @@ -150,37 +145,6 @@ namespace crypto #define dsap GetCryptoConstants ().dsap #define dsaq GetCryptoConstants ().dsaq #define dsag GetCryptoConstants ().dsag -#if (OPENSSL_VERSION_NUMBER >= 0x030000000) // since 3.0.0 - EVP_PKEY * CreateDSA (BIGNUM * pubKey, BIGNUM * privKey) - { - EVP_PKEY * pkey = nullptr; - int selection = EVP_PKEY_KEY_PARAMETERS; - auto bld = OSSL_PARAM_BLD_new(); - OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_FFC_P, dsap); - OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_FFC_Q, dsaq); - OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_FFC_G, dsag); - if (pubKey) - { - OSSL_PARAM_BLD_push_BN (bld, OSSL_PKEY_PARAM_PUB_KEY, pubKey); - selection = EVP_PKEY_PUBLIC_KEY; - } - if (privKey) - { - OSSL_PARAM_BLD_push_BN (bld, OSSL_PKEY_PARAM_PRIV_KEY, privKey); - selection = EVP_PKEY_KEYPAIR; - } - auto params = OSSL_PARAM_BLD_to_param(bld); - - EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new_from_name (NULL, "DSA", NULL); - EVP_PKEY_fromdata_init(ctx); - EVP_PKEY_fromdata(ctx, &pkey, selection, params); - - EVP_PKEY_CTX_free(ctx); - OSSL_PARAM_free(params); - OSSL_PARAM_BLD_free(bld); - return pkey; - } -#else DSA * CreateDSA () { DSA * dsa = DSA_new (); @@ -188,8 +152,7 @@ namespace crypto DSA_set0_key (dsa, NULL, NULL); return dsa; } -#endif - + // DH/ElGamal #if !IS_X86_64 @@ -277,12 +240,17 @@ namespace crypto // x25519 X25519Keys::X25519Keys () { +#if OPENSSL_X25519 m_Ctx = EVP_PKEY_CTX_new_id (NID_X25519, NULL); m_Pkey = nullptr; +#else + m_Ctx = BN_CTX_new (); +#endif } X25519Keys::X25519Keys (const uint8_t * priv, const uint8_t * pub) { +#if OPENSSL_X25519 m_Pkey = EVP_PKEY_new_raw_private_key (EVP_PKEY_X25519, NULL, priv, 32); m_Ctx = EVP_PKEY_CTX_new (m_Pkey, NULL); if (pub) @@ -292,16 +260,29 @@ namespace crypto size_t len = 32; EVP_PKEY_get_raw_public_key (m_Pkey, m_PublicKey, &len); } +#else + m_Ctx = BN_CTX_new (); + memcpy (m_PrivateKey, priv, 32); + if (pub) + memcpy (m_PublicKey, pub, 32); + else + GetEd25519 ()->ScalarMulB (m_PrivateKey, m_PublicKey, m_Ctx); +#endif } X25519Keys::~X25519Keys () { +#if OPENSSL_X25519 EVP_PKEY_CTX_free (m_Ctx); if (m_Pkey) EVP_PKEY_free (m_Pkey); +#else + BN_CTX_free (m_Ctx); +#endif } void X25519Keys::GenerateKeys () { +#if OPENSSL_X25519 if (m_Pkey) { EVP_PKEY_free (m_Pkey); @@ -313,11 +294,16 @@ namespace crypto m_Ctx = EVP_PKEY_CTX_new (m_Pkey, NULL); // TODO: do we really need to re-create m_Ctx? size_t len = 32; EVP_PKEY_get_raw_public_key (m_Pkey, m_PublicKey, &len); +#else + RAND_bytes (m_PrivateKey, 32); + GetEd25519 ()->ScalarMulB (m_PrivateKey, m_PublicKey, m_Ctx); +#endif } bool X25519Keys::Agree (const uint8_t * pub, uint8_t * shared) { if (!pub || (pub[31] & 0x80)) return false; // not x25519 key +#if OPENSSL_X25519 EVP_PKEY_derive_init (m_Ctx); auto pkey = EVP_PKEY_new_raw_public_key (EVP_PKEY_X25519, NULL, pub, 32); if (!pkey) return false; @@ -325,17 +311,25 @@ namespace crypto size_t len = 32; EVP_PKEY_derive (m_Ctx, shared, &len); EVP_PKEY_free (pkey); +#else + GetEd25519 ()->ScalarMul (pub, m_PrivateKey, shared, m_Ctx); +#endif return true; } void X25519Keys::GetPrivateKey (uint8_t * priv) const { +#if OPENSSL_X25519 size_t len = 32; EVP_PKEY_get_raw_private_key (m_Pkey, priv, &len); +#else + memcpy (priv, m_PrivateKey, 32); +#endif } void X25519Keys::SetPrivateKey (const uint8_t * priv, bool calculatePublic) { +#if OPENSSL_X25519 if (m_Ctx) EVP_PKEY_CTX_free (m_Ctx); if (m_Pkey) EVP_PKEY_free (m_Pkey); m_Pkey = EVP_PKEY_new_raw_private_key (EVP_PKEY_X25519, NULL, priv, 32); @@ -345,6 +339,11 @@ namespace crypto size_t len = 32; EVP_PKEY_get_raw_public_key (m_Pkey, m_PublicKey, &len); } +#else + memcpy (m_PrivateKey, priv, 32); + if (calculatePublic) + GetEd25519 ()->ScalarMulB (m_PrivateKey, m_PublicKey, m_Ctx); +#endif } // ElGamal @@ -478,8 +477,9 @@ namespace crypto // encrypt CBCEncryption encryption; encryption.SetKey (shared); + encryption.SetIV (iv); encrypted[257] = 0; - encryption.Encrypt (m, 256, iv, encrypted + 258); + encryption.Encrypt (m, 256, encrypted + 258); EC_POINT_free (p); BN_CTX_end (ctx); BN_CTX_free (ctx); @@ -512,7 +512,8 @@ namespace crypto uint8_t m[256]; CBCDecryption decryption; decryption.SetKey (shared); - decryption.Decrypt (encrypted + 258, 256, iv, m); + decryption.SetIV (iv); + decryption.Decrypt (encrypted + 258, 256, m); // verify and copy uint8_t hash[32]; SHA256 (m + 33, 222, hash); @@ -550,114 +551,440 @@ namespace crypto } // AES - ECBEncryption::ECBEncryption () - { - m_Ctx = EVP_CIPHER_CTX_new (); - } - - ECBEncryption::~ECBEncryption () - { - if (m_Ctx) - EVP_CIPHER_CTX_free (m_Ctx); - } - - void ECBEncryption::Encrypt (const uint8_t * in, uint8_t * out) - { - EVP_EncryptInit_ex (m_Ctx, EVP_aes_256_ecb(), NULL, m_Key, NULL); - EVP_CIPHER_CTX_set_padding (m_Ctx, 0); - int len; - EVP_EncryptUpdate (m_Ctx, out, &len, in, 16); - EVP_EncryptFinal_ex (m_Ctx, out + len, &len); - } +#if SUPPORTS_AES + #define KeyExpansion256(round0,round1) \ + "pshufd $0xff, %%xmm2, %%xmm2 \n" \ + "movaps %%xmm1, %%xmm4 \n" \ + "pslldq $4, %%xmm4 \n" \ + "pxor %%xmm4, %%xmm1 \n" \ + "pslldq $4, %%xmm4 \n" \ + "pxor %%xmm4, %%xmm1 \n" \ + "pslldq $4, %%xmm4 \n" \ + "pxor %%xmm4, %%xmm1 \n" \ + "pxor %%xmm2, %%xmm1 \n" \ + "movaps %%xmm1, "#round0"(%[sched]) \n" \ + "aeskeygenassist $0, %%xmm1, %%xmm4 \n" \ + "pshufd $0xaa, %%xmm4, %%xmm2 \n" \ + "movaps %%xmm3, %%xmm4 \n" \ + "pslldq $4, %%xmm4 \n" \ + "pxor %%xmm4, %%xmm3 \n" \ + "pslldq $4, %%xmm4 \n" \ + "pxor %%xmm4, %%xmm3 \n" \ + "pslldq $4, %%xmm4 \n" \ + "pxor %%xmm4, %%xmm3 \n" \ + "pxor %%xmm2, %%xmm3 \n" \ + "movaps %%xmm3, "#round1"(%[sched]) \n" +#endif - ECBDecryption::ECBDecryption () +#if SUPPORTS_AES + void ECBCryptoAESNI::ExpandKey (const AESKey& key) { - m_Ctx = EVP_CIPHER_CTX_new (); - } - - ECBDecryption::~ECBDecryption () - { - if (m_Ctx) - EVP_CIPHER_CTX_free (m_Ctx); - } - - void ECBDecryption::Decrypt (const uint8_t * in, uint8_t * out) - { - EVP_DecryptInit_ex (m_Ctx, EVP_aes_256_ecb(), NULL, m_Key, NULL); - EVP_CIPHER_CTX_set_padding (m_Ctx, 0); - int len; - EVP_DecryptUpdate (m_Ctx, out, &len, in, 16); - EVP_DecryptFinal_ex (m_Ctx, out + len, &len); + __asm__ + ( + "movups (%[key]), %%xmm1 \n" + "movups 16(%[key]), %%xmm3 \n" + "movaps %%xmm1, (%[sched]) \n" + "movaps %%xmm3, 16(%[sched]) \n" + "aeskeygenassist $1, %%xmm3, %%xmm2 \n" + KeyExpansion256(32,48) + "aeskeygenassist $2, %%xmm3, %%xmm2 \n" + KeyExpansion256(64,80) + "aeskeygenassist $4, %%xmm3, %%xmm2 \n" + KeyExpansion256(96,112) + "aeskeygenassist $8, %%xmm3, %%xmm2 \n" + KeyExpansion256(128,144) + "aeskeygenassist $16, %%xmm3, %%xmm2 \n" + KeyExpansion256(160,176) + "aeskeygenassist $32, %%xmm3, %%xmm2 \n" + KeyExpansion256(192,208) + "aeskeygenassist $64, %%xmm3, %%xmm2 \n" + // key expansion final + "pshufd $0xff, %%xmm2, %%xmm2 \n" + "movaps %%xmm1, %%xmm4 \n" + "pslldq $4, %%xmm4 \n" + "pxor %%xmm4, %%xmm1 \n" + "pslldq $4, %%xmm4 \n" + "pxor %%xmm4, %%xmm1 \n" + "pslldq $4, %%xmm4 \n" + "pxor %%xmm4, %%xmm1 \n" + "pxor %%xmm2, %%xmm1 \n" + "movups %%xmm1, 224(%[sched]) \n" + : // output + : [key]"r"((const uint8_t *)key), [sched]"r"(GetKeySchedule ()) // input + : "%xmm1", "%xmm2", "%xmm3", "%xmm4", "memory" // clogged + ); } +#endif - CBCEncryption::CBCEncryption () - { - m_Ctx = EVP_CIPHER_CTX_new (); - } - - CBCEncryption::~CBCEncryption () +#if SUPPORTS_AES + #define EncryptAES256(sched) \ + "pxor (%["#sched"]), %%xmm0 \n" \ + "aesenc 16(%["#sched"]), %%xmm0 \n" \ + "aesenc 32(%["#sched"]), %%xmm0 \n" \ + "aesenc 48(%["#sched"]), %%xmm0 \n" \ + "aesenc 64(%["#sched"]), %%xmm0 \n" \ + "aesenc 80(%["#sched"]), %%xmm0 \n" \ + "aesenc 96(%["#sched"]), %%xmm0 \n" \ + "aesenc 112(%["#sched"]), %%xmm0 \n" \ + "aesenc 128(%["#sched"]), %%xmm0 \n" \ + "aesenc 144(%["#sched"]), %%xmm0 \n" \ + "aesenc 160(%["#sched"]), %%xmm0 \n" \ + "aesenc 176(%["#sched"]), %%xmm0 \n" \ + "aesenc 192(%["#sched"]), %%xmm0 \n" \ + "aesenc 208(%["#sched"]), %%xmm0 \n" \ + "aesenclast 224(%["#sched"]), %%xmm0 \n" +#endif + + void ECBEncryption::Encrypt (const ChipherBlock * in, ChipherBlock * out) { - if (m_Ctx) - EVP_CIPHER_CTX_free (m_Ctx); - } - - void CBCEncryption::Encrypt (const uint8_t * in, size_t len, const uint8_t * iv, uint8_t * out) +#if SUPPORTS_AES + if(i2p::cpu::aesni) + { + __asm__ + ( + "movups (%[in]), %%xmm0 \n" + EncryptAES256(sched) + "movups %%xmm0, (%[out]) \n" + : + : [sched]"r"(GetKeySchedule ()), [in]"r"(in), [out]"r"(out) + : "%xmm0", "memory" + ); + } + else +#endif + { + AES_encrypt (in->buf, out->buf, &m_Key); + } + } + +#if SUPPORTS_AES + #define DecryptAES256(sched) \ + "pxor 224(%["#sched"]), %%xmm0 \n" \ + "aesdec 208(%["#sched"]), %%xmm0 \n" \ + "aesdec 192(%["#sched"]), %%xmm0 \n" \ + "aesdec 176(%["#sched"]), %%xmm0 \n" \ + "aesdec 160(%["#sched"]), %%xmm0 \n" \ + "aesdec 144(%["#sched"]), %%xmm0 \n" \ + "aesdec 128(%["#sched"]), %%xmm0 \n" \ + "aesdec 112(%["#sched"]), %%xmm0 \n" \ + "aesdec 96(%["#sched"]), %%xmm0 \n" \ + "aesdec 80(%["#sched"]), %%xmm0 \n" \ + "aesdec 64(%["#sched"]), %%xmm0 \n" \ + "aesdec 48(%["#sched"]), %%xmm0 \n" \ + "aesdec 32(%["#sched"]), %%xmm0 \n" \ + "aesdec 16(%["#sched"]), %%xmm0 \n" \ + "aesdeclast (%["#sched"]), %%xmm0 \n" +#endif + + void ECBDecryption::Decrypt (const ChipherBlock * in, ChipherBlock * out) + { +#if SUPPORTS_AES + if(i2p::cpu::aesni) + { + __asm__ + ( + "movups (%[in]), %%xmm0 \n" + DecryptAES256(sched) + "movups %%xmm0, (%[out]) \n" + : + : [sched]"r"(GetKeySchedule ()), [in]"r"(in), [out]"r"(out) + : "%xmm0", "memory" + ); + } + else +#endif + { + AES_decrypt (in->buf, out->buf, &m_Key); + } + } + +#if SUPPORTS_AES + #define CallAESIMC(offset) \ + "movaps "#offset"(%[shed]), %%xmm0 \n" \ + "aesimc %%xmm0, %%xmm0 \n" \ + "movaps %%xmm0, "#offset"(%[shed]) \n" +#endif + + void ECBEncryption::SetKey (const AESKey& key) + { +#if SUPPORTS_AES + if(i2p::cpu::aesni) + { + ExpandKey (key); + } + else +#endif + { + AES_set_encrypt_key (key, 256, &m_Key); + } + } + + void ECBDecryption::SetKey (const AESKey& key) + { +#if SUPPORTS_AES + if(i2p::cpu::aesni) + { + ExpandKey (key); // expand encryption key first + // then invert it using aesimc + __asm__ + ( + CallAESIMC(16) + CallAESIMC(32) + CallAESIMC(48) + CallAESIMC(64) + CallAESIMC(80) + CallAESIMC(96) + CallAESIMC(112) + CallAESIMC(128) + CallAESIMC(144) + CallAESIMC(160) + CallAESIMC(176) + CallAESIMC(192) + CallAESIMC(208) + : + : [shed]"r"(GetKeySchedule ()) + : "%xmm0", "memory" + ); + } + else +#endif + { + AES_set_decrypt_key (key, 256, &m_Key); + } + } + + void CBCEncryption::Encrypt (int numBlocks, const ChipherBlock * in, ChipherBlock * out) + { +#if SUPPORTS_AES + if(i2p::cpu::aesni) + { + __asm__ + ( + "movups (%[iv]), %%xmm1 \n" + "1: \n" + "movups (%[in]), %%xmm0 \n" + "pxor %%xmm1, %%xmm0 \n" + EncryptAES256(sched) + "movaps %%xmm0, %%xmm1 \n" + "movups %%xmm0, (%[out]) \n" + "add $16, %[in] \n" + "add $16, %[out] \n" + "dec %[num] \n" + "jnz 1b \n" + "movups %%xmm1, (%[iv]) \n" + : + : [iv]"r"((uint8_t *)m_LastBlock), [sched]"r"(m_ECBEncryption.GetKeySchedule ()), + [in]"r"(in), [out]"r"(out), [num]"r"(numBlocks) + : "%xmm0", "%xmm1", "cc", "memory" + ); + } + else +#endif + { + for (int i = 0; i < numBlocks; i++) + { + *m_LastBlock.GetChipherBlock () ^= in[i]; + m_ECBEncryption.Encrypt (m_LastBlock.GetChipherBlock (), m_LastBlock.GetChipherBlock ()); + out[i] = *m_LastBlock.GetChipherBlock (); + } + } + } + + void CBCEncryption::Encrypt (const uint8_t * in, std::size_t len, uint8_t * out) { // len/16 - EVP_EncryptInit_ex (m_Ctx, EVP_aes_256_cbc(), NULL, m_Key, iv); - EVP_CIPHER_CTX_set_padding (m_Ctx, 0); - int l; - EVP_EncryptUpdate (m_Ctx, out, &l, in, len); - EVP_EncryptFinal_ex (m_Ctx, out + l, &l); + int numBlocks = len >> 4; + if (numBlocks > 0) + Encrypt (numBlocks, (const ChipherBlock *)in, (ChipherBlock *)out); } - CBCDecryption::CBCDecryption () - { - m_Ctx = EVP_CIPHER_CTX_new (); + void CBCEncryption::Encrypt (const uint8_t * in, uint8_t * out) + { +#if SUPPORTS_AES + if(i2p::cpu::aesni) + { + __asm__ + ( + "movups (%[iv]), %%xmm1 \n" + "movups (%[in]), %%xmm0 \n" + "pxor %%xmm1, %%xmm0 \n" + EncryptAES256(sched) + "movups %%xmm0, (%[out]) \n" + "movups %%xmm0, (%[iv]) \n" + : + : [iv]"r"((uint8_t *)m_LastBlock), [sched]"r"(m_ECBEncryption.GetKeySchedule ()), + [in]"r"(in), [out]"r"(out) + : "%xmm0", "%xmm1", "memory" + ); + } + else +#endif + Encrypt (1, (const ChipherBlock *)in, (ChipherBlock *)out); } - - CBCDecryption::~CBCDecryption () + + void CBCDecryption::Decrypt (int numBlocks, const ChipherBlock * in, ChipherBlock * out) { - if (m_Ctx) - EVP_CIPHER_CTX_free (m_Ctx); - } - - void CBCDecryption::Decrypt (const uint8_t * in, size_t len, const uint8_t * iv, uint8_t * out) +#if SUPPORTS_AES + if(i2p::cpu::aesni) + { + __asm__ + ( + "movups (%[iv]), %%xmm1 \n" + "1: \n" + "movups (%[in]), %%xmm0 \n" + "movaps %%xmm0, %%xmm2 \n" + DecryptAES256(sched) + "pxor %%xmm1, %%xmm0 \n" + "movups %%xmm0, (%[out]) \n" + "movaps %%xmm2, %%xmm1 \n" + "add $16, %[in] \n" + "add $16, %[out] \n" + "dec %[num] \n" + "jnz 1b \n" + "movups %%xmm1, (%[iv]) \n" + : + : [iv]"r"((uint8_t *)m_IV), [sched]"r"(m_ECBDecryption.GetKeySchedule ()), + [in]"r"(in), [out]"r"(out), [num]"r"(numBlocks) + : "%xmm0", "%xmm1", "%xmm2", "cc", "memory" + ); + } + else +#endif + { + for (int i = 0; i < numBlocks; i++) + { + ChipherBlock tmp = in[i]; + m_ECBDecryption.Decrypt (in + i, out + i); + out[i] ^= *m_IV.GetChipherBlock (); + *m_IV.GetChipherBlock () = tmp; + } + } + } + + void CBCDecryption::Decrypt (const uint8_t * in, std::size_t len, uint8_t * out) { - // len/16 - EVP_DecryptInit_ex (m_Ctx, EVP_aes_256_cbc(), NULL, m_Key, iv); - EVP_CIPHER_CTX_set_padding (m_Ctx, 0); - int l; - EVP_DecryptUpdate (m_Ctx, out, &l, in, len); - EVP_DecryptFinal_ex (m_Ctx, out + l, &l); + int numBlocks = len >> 4; + if (numBlocks > 0) + Decrypt (numBlocks, (const ChipherBlock *)in, (ChipherBlock *)out); + } + + void CBCDecryption::Decrypt (const uint8_t * in, uint8_t * out) + { +#if SUPPORTS_AES + if(i2p::cpu::aesni) + { + __asm__ + ( + "movups (%[iv]), %%xmm1 \n" + "movups (%[in]), %%xmm0 \n" + "movups %%xmm0, (%[iv]) \n" + DecryptAES256(sched) + "pxor %%xmm1, %%xmm0 \n" + "movups %%xmm0, (%[out]) \n" + : + : [iv]"r"((uint8_t *)m_IV), [sched]"r"(m_ECBDecryption.GetKeySchedule ()), + [in]"r"(in), [out]"r"(out) + : "%xmm0", "%xmm1", "memory" + ); + } + else +#endif + Decrypt (1, (const ChipherBlock *)in, (ChipherBlock *)out); } void TunnelEncryption::Encrypt (const uint8_t * in, uint8_t * out) { - uint8_t iv[16]; - m_IVEncryption.Encrypt (in, iv); // iv - m_LayerEncryption.Encrypt (in + 16, i2p::tunnel::TUNNEL_DATA_ENCRYPTED_SIZE, iv, out + 16); // data - m_IVEncryption.Encrypt (iv, out); // double iv +#if SUPPORTS_AES + if(i2p::cpu::aesni) + { + __asm__ + ( + // encrypt IV + "movups (%[in]), %%xmm0 \n" + EncryptAES256(sched_iv) + "movaps %%xmm0, %%xmm1 \n" + // double IV encryption + EncryptAES256(sched_iv) + "movups %%xmm0, (%[out]) \n" + // encrypt data, IV is xmm1 + "1: \n" + "add $16, %[in] \n" + "add $16, %[out] \n" + "movups (%[in]), %%xmm0 \n" + "pxor %%xmm1, %%xmm0 \n" + EncryptAES256(sched_l) + "movaps %%xmm0, %%xmm1 \n" + "movups %%xmm0, (%[out]) \n" + "dec %[num] \n" + "jnz 1b \n" + : + : [sched_iv]"r"(m_IVEncryption.GetKeySchedule ()), [sched_l]"r"(m_LayerEncryption.ECB().GetKeySchedule ()), + [in]"r"(in), [out]"r"(out), [num]"r"(63) // 63 blocks = 1008 bytes + : "%xmm0", "%xmm1", "cc", "memory" + ); + } + else +#endif + { + m_IVEncryption.Encrypt ((const ChipherBlock *)in, (ChipherBlock *)out); // iv + m_LayerEncryption.SetIV (out); + m_LayerEncryption.Encrypt (in + 16, i2p::tunnel::TUNNEL_DATA_ENCRYPTED_SIZE, out + 16); // data + m_IVEncryption.Encrypt ((ChipherBlock *)out, (ChipherBlock *)out); // double iv + } } void TunnelDecryption::Decrypt (const uint8_t * in, uint8_t * out) { - uint8_t iv[16]; - m_IVDecryption.Decrypt (in, iv); // iv - m_LayerDecryption.Decrypt (in + 16, i2p::tunnel::TUNNEL_DATA_ENCRYPTED_SIZE, iv, out + 16); // data - m_IVDecryption.Decrypt (iv, out); // double iv +#if SUPPORTS_AES + if(i2p::cpu::aesni) + { + __asm__ + ( + // decrypt IV + "movups (%[in]), %%xmm0 \n" + DecryptAES256(sched_iv) + "movaps %%xmm0, %%xmm1 \n" + // double IV encryption + DecryptAES256(sched_iv) + "movups %%xmm0, (%[out]) \n" + // decrypt data, IV is xmm1 + "1: \n" + "add $16, %[in] \n" + "add $16, %[out] \n" + "movups (%[in]), %%xmm0 \n" + "movaps %%xmm0, %%xmm2 \n" + DecryptAES256(sched_l) + "pxor %%xmm1, %%xmm0 \n" + "movups %%xmm0, (%[out]) \n" + "movaps %%xmm2, %%xmm1 \n" + "dec %[num] \n" + "jnz 1b \n" + : + : [sched_iv]"r"(m_IVDecryption.GetKeySchedule ()), [sched_l]"r"(m_LayerDecryption.ECB().GetKeySchedule ()), + [in]"r"(in), [out]"r"(out), [num]"r"(63) // 63 blocks = 1008 bytes + : "%xmm0", "%xmm1", "%xmm2", "cc", "memory" + ); + } + else +#endif + { + m_IVDecryption.Decrypt ((const ChipherBlock *)in, (ChipherBlock *)out); // iv + m_LayerDecryption.SetIV (out); + m_LayerDecryption.Decrypt (in + 16, i2p::tunnel::TUNNEL_DATA_ENCRYPTED_SIZE, out + 16); // data + m_IVDecryption.Decrypt ((ChipherBlock *)out, (ChipherBlock *)out); // double iv + } } // AEAD/ChaCha20/Poly1305 - static bool AEADChaCha20Poly1305 (EVP_CIPHER_CTX * ctx, const uint8_t * msg, size_t msgLen, - const uint8_t * ad, size_t adLen, const uint8_t * key, const uint8_t * nonce, uint8_t * buf, size_t len, bool encrypt) + bool AEADChaCha20Poly1305 (const uint8_t * msg, size_t msgLen, const uint8_t * ad, size_t adLen, const uint8_t * key, const uint8_t * nonce, uint8_t * buf, size_t len, bool encrypt) { - if (!ctx || len < msgLen) return false; + if (len < msgLen) return false; if (encrypt && len < msgLen + 16) return false; bool ret = true; int outlen = 0; + EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new (); if (encrypt) { EVP_EncryptInit_ex(ctx, EVP_chacha20_poly1305(), 0, 0, 0); @@ -670,7 +997,7 @@ namespace crypto } else { -#if defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x4000000fL +#if defined(LIBRESSL_VERSION_NUMBER) std::vector m(msgLen + 16); if (msg == buf) { @@ -687,100 +1014,38 @@ namespace crypto EVP_DecryptUpdate(ctx, buf, &outlen, msg, msgLen); ret = EVP_DecryptFinal_ex(ctx, buf + outlen, &outlen) > 0; } - return ret; - } - bool AEADChaCha20Poly1305 (const uint8_t * msg, size_t msgLen, const uint8_t * ad, size_t adLen, - const uint8_t * key, const uint8_t * nonce, uint8_t * buf, size_t len, bool encrypt) - { - EVP_CIPHER_CTX * ctx = EVP_CIPHER_CTX_new (); - auto ret = AEADChaCha20Poly1305 (ctx, msg, msgLen, ad, adLen, key, nonce, buf, len, encrypt); EVP_CIPHER_CTX_free (ctx); return ret; } - AEADChaCha20Poly1305Encryptor::AEADChaCha20Poly1305Encryptor () - { - m_Ctx = EVP_CIPHER_CTX_new (); - } - - AEADChaCha20Poly1305Encryptor::~AEADChaCha20Poly1305Encryptor () - { - if (m_Ctx) - EVP_CIPHER_CTX_free (m_Ctx); - } - - bool AEADChaCha20Poly1305Encryptor::Encrypt (const uint8_t * msg, size_t msgLen, const uint8_t * ad, size_t adLen, - const uint8_t * key, const uint8_t * nonce, uint8_t * buf, size_t len) - { - return AEADChaCha20Poly1305 (m_Ctx, msg, msgLen, ad, adLen, key, nonce, buf, len, true); - } - - void AEADChaCha20Poly1305Encryptor::Encrypt (const std::vector >& bufs, - const uint8_t * key, const uint8_t * nonce, uint8_t * mac) + void AEADChaCha20Poly1305Encrypt (const std::vector >& bufs, const uint8_t * key, const uint8_t * nonce, uint8_t * mac) { if (bufs.empty ()) return; int outlen = 0; - EVP_EncryptInit_ex(m_Ctx, EVP_chacha20_poly1305(), 0, 0, 0); - EVP_CIPHER_CTX_ctrl(m_Ctx, EVP_CTRL_AEAD_SET_IVLEN, 12, 0); - EVP_EncryptInit_ex(m_Ctx, NULL, NULL, key, nonce); + EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new (); + EVP_EncryptInit_ex(ctx, EVP_chacha20_poly1305(), 0, 0, 0); + EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_IVLEN, 12, 0); + EVP_EncryptInit_ex(ctx, NULL, NULL, key, nonce); for (const auto& it: bufs) - EVP_EncryptUpdate(m_Ctx, it.first, &outlen, it.first, it.second); - EVP_EncryptFinal_ex(m_Ctx, NULL, &outlen); - EVP_CIPHER_CTX_ctrl(m_Ctx, EVP_CTRL_AEAD_GET_TAG, 16, mac); - } - - AEADChaCha20Poly1305Decryptor::AEADChaCha20Poly1305Decryptor () - { - m_Ctx = EVP_CIPHER_CTX_new (); - } - - AEADChaCha20Poly1305Decryptor::~AEADChaCha20Poly1305Decryptor () - { - if (m_Ctx) - EVP_CIPHER_CTX_free (m_Ctx); - } - - bool AEADChaCha20Poly1305Decryptor::Decrypt (const uint8_t * msg, size_t msgLen, const uint8_t * ad, size_t adLen, - const uint8_t * key, const uint8_t * nonce, uint8_t * buf, size_t len) - { - return AEADChaCha20Poly1305 (m_Ctx, msg, msgLen, ad, adLen, key, nonce, buf, len, false); + EVP_EncryptUpdate(ctx, it.first, &outlen, it.first, it.second); + EVP_EncryptFinal_ex(ctx, NULL, &outlen); + EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_GET_TAG, 16, mac); + EVP_CIPHER_CTX_free (ctx); } - static void ChaCha20 (EVP_CIPHER_CTX *ctx, const uint8_t * msg, size_t msgLen, const uint8_t * key, const uint8_t * nonce, uint8_t * out) + void ChaCha20 (const uint8_t * msg, size_t msgLen, const uint8_t * key, const uint8_t * nonce, uint8_t * out) { + EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new (); uint32_t iv[4]; iv[0] = htole32 (1); memcpy (iv + 1, nonce, 12); // counter | nonce EVP_EncryptInit_ex(ctx, EVP_chacha20 (), NULL, key, (const uint8_t *)iv); int outlen = 0; EVP_EncryptUpdate(ctx, out, &outlen, msg, msgLen); EVP_EncryptFinal_ex(ctx, NULL, &outlen); - } - - void ChaCha20 (const uint8_t * msg, size_t msgLen, const uint8_t * key, const uint8_t * nonce, uint8_t * out) - { - EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new (); - ChaCha20 (ctx, msg, msgLen, key, nonce, out); EVP_CIPHER_CTX_free (ctx); } - - ChaCha20Context::ChaCha20Context () - { - m_Ctx = EVP_CIPHER_CTX_new (); - } - - ChaCha20Context::~ChaCha20Context () - { - if (m_Ctx) - EVP_CIPHER_CTX_free (m_Ctx); - } - - void ChaCha20Context::operator ()(const uint8_t * msg, size_t msgLen, const uint8_t * key, const uint8_t * nonce, uint8_t * out) - { - ChaCha20 (m_Ctx, msg, msgLen, key, nonce, out); - } - void HKDF (const uint8_t * salt, const uint8_t * key, size_t keyLen, const std::string& info, uint8_t * out, size_t outLen) { @@ -821,18 +1086,6 @@ namespace crypto // Noise - void NoiseSymmetricState::Init (const uint8_t * ck, const uint8_t * hh, const uint8_t * pub) - { - // pub is Bob's public static key, hh = SHA256(h) - memcpy (m_CK, ck, 32); - SHA256_CTX ctx; - SHA256_Init (&ctx); - SHA256_Update (&ctx, hh, 32); - SHA256_Update (&ctx, pub, 32); - SHA256_Final (m_H, &ctx); // h = MixHash(pub) = SHA256(hh || pub) - m_N = 0; - } - void NoiseSymmetricState::MixHash (const uint8_t * buf, size_t len) { SHA256_CTX ctx; @@ -856,95 +1109,76 @@ namespace crypto { HKDF (m_CK, sharedSecret, 32, "", m_CK); // new ck is m_CK[0:31], key is m_CK[32:63] - m_N = 0; } - bool NoiseSymmetricState::Encrypt (const uint8_t * in, uint8_t * out, size_t len) + static void InitNoiseState (NoiseSymmetricState& state, const uint8_t * ck, + const uint8_t * hh, const uint8_t * pub) { - uint8_t nonce[12]; - if (m_N) - { - memset (nonce, 0, 4); - htole64buf (nonce + 4, m_N); - } - else - memset (nonce, 0, 12); - auto ret = AEADChaCha20Poly1305 (in, len, m_H, 32, m_CK + 32, nonce, out, len + 16, true); - if (ret) m_N++; - return ret; + // pub is Bob's public static key, hh = SHA256(h) + memcpy (state.m_CK, ck, 32); + SHA256_CTX ctx; + SHA256_Init (&ctx); + SHA256_Update (&ctx, hh, 32); + SHA256_Update (&ctx, pub, 32); + SHA256_Final (state.m_H, &ctx); // h = MixHash(pub) = SHA256(hh || pub) } - bool NoiseSymmetricState::Decrypt (const uint8_t * in, uint8_t * out, size_t len) - { - uint8_t nonce[12]; - if (m_N) - { - memset (nonce, 0, 4); - htole64buf (nonce + 4, m_N); - } - else - memset (nonce, 0, 12); - auto ret = AEADChaCha20Poly1305 (in, len, m_H, 32, m_CK + 32, nonce, out, len, false); - if (ret) m_N++; - return ret; - } - void InitNoiseNState (NoiseSymmetricState& state, const uint8_t * pub) { - static constexpr char protocolName[] = "Noise_N_25519_ChaChaPoly_SHA256"; // 31 chars - static constexpr uint8_t hh[32] = + static const char protocolName[] = "Noise_N_25519_ChaChaPoly_SHA256"; // 31 chars + static const uint8_t hh[32] = { 0x69, 0x4d, 0x52, 0x44, 0x5a, 0x27, 0xd9, 0xad, 0xfa, 0xd2, 0x9c, 0x76, 0x32, 0x39, 0x5d, 0xc1, 0xe4, 0x35, 0x4c, 0x69, 0xb4, 0xf9, 0x2e, 0xac, 0x8a, 0x1e, 0xe4, 0x6a, 0x9e, 0xd2, 0x15, 0x54 }; // hh = SHA256(protocol_name || 0) - state.Init ((const uint8_t *)protocolName, hh, pub); // ck = protocol_name || 0 + InitNoiseState (state, (const uint8_t *)protocolName, hh, pub); // ck = protocol_name || 0 } void InitNoiseXKState (NoiseSymmetricState& state, const uint8_t * pub) { - static constexpr uint8_t protocolNameHash[32] = + static const uint8_t protocolNameHash[32] = { 0x72, 0xe8, 0x42, 0xc5, 0x45, 0xe1, 0x80, 0x80, 0xd3, 0x9c, 0x44, 0x93, 0xbb, 0x91, 0xd7, 0xed, 0xf2, 0x28, 0x98, 0x17, 0x71, 0x21, 0x8c, 0x1f, 0x62, 0x4e, 0x20, 0x6f, 0x28, 0xd3, 0x2f, 0x71 }; // SHA256 ("Noise_XKaesobfse+hs2+hs3_25519_ChaChaPoly_SHA256") - static constexpr uint8_t hh[32] = + static const uint8_t hh[32] = { 0x49, 0xff, 0x48, 0x3f, 0xc4, 0x04, 0xb9, 0xb2, 0x6b, 0x11, 0x94, 0x36, 0x72, 0xff, 0x05, 0xb5, 0x61, 0x27, 0x03, 0x31, 0xba, 0x89, 0xb8, 0xfc, 0x33, 0x15, 0x93, 0x87, 0x57, 0xdd, 0x3d, 0x1e }; // SHA256 (protocolNameHash) - state.Init (protocolNameHash, hh, pub); + InitNoiseState (state, protocolNameHash, hh, pub); } void InitNoiseXKState1 (NoiseSymmetricState& state, const uint8_t * pub) { - static constexpr uint8_t protocolNameHash[32] = + static const uint8_t protocolNameHash[32] = { 0xb1, 0x37, 0x22, 0x81, 0x74, 0x23, 0xa8, 0xfd, 0xf4, 0x2d, 0xf2, 0xe6, 0x0e, 0xd1, 0xed, 0xf4, 0x1b, 0x93, 0x07, 0x1d, 0xb1, 0xec, 0x24, 0xa3, 0x67, 0xf7, 0x84, 0xec, 0x27, 0x0d, 0x81, 0x32 }; // SHA256 ("Noise_XKchaobfse+hs1+hs2+hs3_25519_ChaChaPoly_SHA256") - static constexpr uint8_t hh[32] = + static const uint8_t hh[32] = { 0xdc, 0x85, 0xe6, 0xaf, 0x7b, 0x02, 0x65, 0x0c, 0xf1, 0xf9, 0x0d, 0x71, 0xfb, 0xc6, 0xd4, 0x53, 0xa7, 0xcf, 0x6d, 0xbf, 0xbd, 0x52, 0x5e, 0xa5, 0xb5, 0x79, 0x1c, 0x47, 0xb3, 0x5e, 0xbc, 0x33 }; // SHA256 (protocolNameHash) - state.Init (protocolNameHash, hh, pub); + InitNoiseState (state, protocolNameHash, hh, pub); } void InitNoiseIKState (NoiseSymmetricState& state, const uint8_t * pub) { - static constexpr uint8_t protocolNameHash[32] = + static const uint8_t protocolNameHash[32] = { 0x4c, 0xaf, 0x11, 0xef, 0x2c, 0x8e, 0x36, 0x56, 0x4c, 0x53, 0xe8, 0x88, 0x85, 0x06, 0x4d, 0xba, 0xac, 0xbe, 0x00, 0x54, 0xad, 0x17, 0x8f, 0x80, 0x79, 0xa6, 0x46, 0x82, 0x7e, 0x6e, 0xe4, 0x0c }; // SHA256("Noise_IKelg2+hs2_25519_ChaChaPoly_SHA256"), 40 bytes - static constexpr uint8_t hh[32] = + static const uint8_t hh[32] = { 0x9c, 0xcf, 0x85, 0x2c, 0xc9, 0x3b, 0xb9, 0x50, 0x44, 0x41, 0xe9, 0x50, 0xe0, 0x1d, 0x52, 0x32, 0x2e, 0x0d, 0x47, 0xad, 0xd1, 0xe9, 0xa5, 0x55, 0xf7, 0x55, 0xb5, 0x69, 0xae, 0x18, 0x3b, 0x5c }; // SHA256 (protocolNameHash) - state.Init (protocolNameHash, hh, pub); + InitNoiseState (state, protocolNameHash, hh, pub); } - + // init and terminate /* std::vector > m_OpenSSLMutexes; @@ -959,8 +1193,9 @@ namespace crypto } }*/ - void InitCrypto (bool precomputation) + void InitCrypto (bool precomputation, bool aesni, bool force) { + i2p::cpu::Detect (aesni, force); /* auto numLocks = CRYPTO_num_locks(); for (int i = 0; i < numLocks; i++) m_OpenSSLMutexes.emplace_back (new std::mutex); diff --git a/libi2pd/Crypto.h b/libi2pd/Crypto.h index 125a217c..13d331c8 100644 --- a/libi2pd/Crypto.h +++ b/libi2pd/Crypto.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2025, The PurpleI2P Project +* Copyright (c) 2013-2024, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -25,17 +25,16 @@ #include "Base.h" #include "Tag.h" +#include "CPU.h" // recognize openssl version and features #if (OPENSSL_VERSION_NUMBER >= 0x010101000) // 1.1.1 # define OPENSSL_HKDF 1 # define OPENSSL_EDDSA 1 +# define OPENSSL_X25519 1 # if (!defined(LIBRESSL_VERSION_NUMBER) && (OPENSSL_VERSION_NUMBER != 0x030000000)) // 3.0.0, regression in SipHash, not implemented in LibreSSL # define OPENSSL_SIPHASH 1 # endif -# if (OPENSSL_VERSION_NUMBER >= 0x030500000) // 3.5.0 -# define OPENSSL_PQ 1 -# endif #endif namespace i2p @@ -45,11 +44,7 @@ namespace crypto bool bn2buf (const BIGNUM * bn, uint8_t * buf, size_t len); // DSA -#if (OPENSSL_VERSION_NUMBER >= 0x030000000) // since 3.0.0 - EVP_PKEY * CreateDSA (BIGNUM * pubKey = nullptr, BIGNUM * privKey = nullptr); -#else DSA * CreateDSA (); -#endif // RSA const BIGNUM * GetRSAE (); @@ -75,8 +70,13 @@ namespace crypto private: uint8_t m_PublicKey[32]; +#if OPENSSL_X25519 EVP_PKEY_CTX * m_Ctx; EVP_PKEY * m_Pkey; +#else + BN_CTX * m_Ctx; + uint8_t m_PrivateKey[32]; +#endif bool m_IsElligatorIneligible = false; // true if definitely ineligible }; @@ -91,70 +91,142 @@ namespace crypto void GenerateECIESKeyPair (const EC_GROUP * curve, BIGNUM *& priv, EC_POINT *& pub); // AES - typedef i2p::data::Tag<32> AESKey; - - class ECBEncryption + struct ChipherBlock { - public: + uint8_t buf[16]; - ECBEncryption (); - ~ECBEncryption (); - - void SetKey (const uint8_t * key) { m_Key = key; }; - void Encrypt(const uint8_t * in, uint8_t * out); - - private: - - AESKey m_Key; - EVP_CIPHER_CTX * m_Ctx; + void operator^=(const ChipherBlock& other) // XOR + { + if (!(((size_t)buf | (size_t)other.buf) & 0x03)) // multiple of 4 ? + { + for (int i = 0; i < 4; i++) + reinterpret_cast(buf)[i] ^= reinterpret_cast(other.buf)[i]; + } + else + { + for (int i = 0; i < 16; i++) + buf[i] ^= other.buf[i]; + } + } }; - class ECBDecryption + typedef i2p::data::Tag<32> AESKey; + + template + class AESAlignedBuffer // 16 bytes alignment { public: - ECBDecryption (); - ~ECBDecryption (); - - void SetKey (const uint8_t * key) { m_Key = key; }; - void Decrypt (const uint8_t * in, uint8_t * out); - + AESAlignedBuffer () + { + m_Buf = m_UnalignedBuffer; + uint8_t rem = ((size_t)m_Buf) & 0x0f; + if (rem) + m_Buf += (16 - rem); + } + + operator uint8_t * () { return m_Buf; }; + operator const uint8_t * () const { return m_Buf; }; + ChipherBlock * GetChipherBlock () { return (ChipherBlock *)m_Buf; }; + const ChipherBlock * GetChipherBlock () const { return (const ChipherBlock *)m_Buf; }; + private: - - AESKey m_Key; - EVP_CIPHER_CTX * m_Ctx; + + uint8_t m_UnalignedBuffer[sz + 15]; // up to 15 bytes alignment + uint8_t * m_Buf; + }; + + +#if SUPPORTS_AES + class ECBCryptoAESNI + { + public: + + uint8_t * GetKeySchedule () { return m_KeySchedule; }; + + protected: + + void ExpandKey (const AESKey& key); + + private: + + AESAlignedBuffer<240> m_KeySchedule; // 14 rounds for AES-256, 240 bytes + }; +#endif + +#if SUPPORTS_AES + class ECBEncryption: public ECBCryptoAESNI +#else + class ECBEncryption +#endif + { + public: + + void SetKey (const AESKey& key); + + void Encrypt(const ChipherBlock * in, ChipherBlock * out); + + private: + AES_KEY m_Key; + }; + +#if SUPPORTS_AES + class ECBDecryption: public ECBCryptoAESNI +#else + class ECBDecryption +#endif + { + public: + + void SetKey (const AESKey& key); + void Decrypt (const ChipherBlock * in, ChipherBlock * out); + private: + AES_KEY m_Key; }; class CBCEncryption { public: - CBCEncryption (); - ~CBCEncryption (); + CBCEncryption () { memset ((uint8_t *)m_LastBlock, 0, 16); }; + + void SetKey (const AESKey& key) { m_ECBEncryption.SetKey (key); }; // 32 bytes + void SetIV (const uint8_t * iv) { memcpy ((uint8_t *)m_LastBlock, iv, 16); }; // 16 bytes + void GetIV (uint8_t * iv) const { memcpy (iv, (const uint8_t *)m_LastBlock, 16); }; + + void Encrypt (int numBlocks, const ChipherBlock * in, ChipherBlock * out); + void Encrypt (const uint8_t * in, std::size_t len, uint8_t * out); + void Encrypt (const uint8_t * in, uint8_t * out); // one block + + ECBEncryption & ECB() { return m_ECBEncryption; } - void SetKey (const uint8_t * key) { m_Key = key; }; // 32 bytes - void Encrypt (const uint8_t * in, size_t len, const uint8_t * iv, uint8_t * out); - private: - AESKey m_Key; - EVP_CIPHER_CTX * m_Ctx; + AESAlignedBuffer<16> m_LastBlock; + + ECBEncryption m_ECBEncryption; }; class CBCDecryption { public: - CBCDecryption (); - ~CBCDecryption (); - - void SetKey (const uint8_t * key) { m_Key = key; }; // 32 bytes - void Decrypt (const uint8_t * in, size_t len, const uint8_t * iv, uint8_t * out); + CBCDecryption () { memset ((uint8_t *)m_IV, 0, 16); }; + + void SetKey (const AESKey& key) { m_ECBDecryption.SetKey (key); }; // 32 bytes + void SetIV (const uint8_t * iv) { memcpy ((uint8_t *)m_IV, iv, 16); }; // 16 bytes + void GetIV (uint8_t * iv) const { memcpy (iv, (const uint8_t *)m_IV, 16); }; + + void Decrypt (int numBlocks, const ChipherBlock * in, ChipherBlock * out); + void Decrypt (const uint8_t * in, std::size_t len, uint8_t * out); + void Decrypt (const uint8_t * in, uint8_t * out); // one block + + ECBDecryption & ECB() { return m_ECBDecryption; } private: - AESKey m_Key; - EVP_CIPHER_CTX * m_Ctx; + AESAlignedBuffer<16> m_IV; + ECBDecryption m_ECBDecryption; }; class TunnelEncryption // with double IV encryption @@ -194,58 +266,13 @@ namespace crypto }; // AEAD/ChaCha20/Poly1305 + bool AEADChaCha20Poly1305 (const uint8_t * msg, size_t msgLen, const uint8_t * ad, size_t adLen, const uint8_t * key, const uint8_t * nonce, uint8_t * buf, size_t len, bool encrypt); // msgLen is len without tag - class AEADChaCha20Poly1305Encryptor - { - public: + void AEADChaCha20Poly1305Encrypt (const std::vector >& bufs, const uint8_t * key, const uint8_t * nonce, uint8_t * mac); // encrypt multiple buffers with zero ad - AEADChaCha20Poly1305Encryptor (); - ~AEADChaCha20Poly1305Encryptor (); - - bool Encrypt (const uint8_t * msg, size_t msgLen, const uint8_t * ad, size_t adLen, - const uint8_t * key, const uint8_t * nonce, uint8_t * buf, size_t len); // msgLen is len without tag - - void Encrypt (const std::vector >& bufs, const uint8_t * key, const uint8_t * nonce, uint8_t * mac); // encrypt multiple buffers with zero ad - - private: - - EVP_CIPHER_CTX * m_Ctx; - }; - - class AEADChaCha20Poly1305Decryptor - { - public: - - AEADChaCha20Poly1305Decryptor (); - ~AEADChaCha20Poly1305Decryptor (); - - bool Decrypt (const uint8_t * msg, size_t msgLen, const uint8_t * ad, size_t adLen, - const uint8_t * key, const uint8_t * nonce, uint8_t * buf, size_t len); // msgLen is len without tag - - private: - - EVP_CIPHER_CTX * m_Ctx; - }; - - bool AEADChaCha20Poly1305 (const uint8_t * msg, size_t msgLen, const uint8_t * ad, size_t adLen, - const uint8_t * key, const uint8_t * nonce, uint8_t * buf, size_t len, bool encrypt); // msgLen is len without tag - // ChaCha20 void ChaCha20 (const uint8_t * msg, size_t msgLen, const uint8_t * key, const uint8_t * nonce, uint8_t * out); - class ChaCha20Context - { - public: - - ChaCha20Context (); - ~ChaCha20Context (); - void operator ()(const uint8_t * msg, size_t msgLen, const uint8_t * key, const uint8_t * nonce, uint8_t * out); - - private: - - EVP_CIPHER_CTX * m_Ctx; - }; - // HKDF void HKDF (const uint8_t * salt, const uint8_t * key, size_t keyLen, const std::string& info, uint8_t * out, size_t outLen = 64); // salt - 32, out - 32 or 64, info <= 32 @@ -255,25 +282,19 @@ namespace crypto struct NoiseSymmetricState { uint8_t m_H[32] /*h*/, m_CK[64] /*[ck, k]*/; - uint64_t m_N; - void Init (const uint8_t * ck, const uint8_t * hh, const uint8_t * pub); - void MixHash (const uint8_t * buf, size_t len); void MixHash (const std::vector >& bufs); void MixKey (const uint8_t * sharedSecret); - - bool Encrypt (const uint8_t * in, uint8_t * out, size_t len); // out length = len + 16 - bool Decrypt (const uint8_t * in, uint8_t * out, size_t len); // len without 16 bytes tag }; void InitNoiseNState (NoiseSymmetricState& state, const uint8_t * pub); // Noise_N (tunnels, router) void InitNoiseXKState (NoiseSymmetricState& state, const uint8_t * pub); // Noise_XK (NTCP2) void InitNoiseXKState1 (NoiseSymmetricState& state, const uint8_t * pub); // Noise_XK (SSU2) void InitNoiseIKState (NoiseSymmetricState& state, const uint8_t * pub); // Noise_IK (ratchets) - + // init and terminate - void InitCrypto (bool precomputation); + void InitCrypto (bool precomputation, bool aesni, bool force); void TerminateCrypto (); } } diff --git a/libi2pd/CryptoKey.cpp b/libi2pd/CryptoKey.cpp index e37d4039..ad986129 100644 --- a/libi2pd/CryptoKey.cpp +++ b/libi2pd/CryptoKey.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2025, The PurpleI2P Project +* Copyright (c) 2013-2021, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -181,21 +181,5 @@ namespace crypto k.GetPrivateKey (priv); memcpy (pub, k.GetPublicKey (), 32); } - - LocalEncryptionKey::LocalEncryptionKey (i2p::data::CryptoKeyType t): keyType(t) - { - pub.resize (GetCryptoPublicKeyLen (keyType)); - priv.resize (GetCryptoPrivateKeyLen (keyType)); - } - - void LocalEncryptionKey::GenerateKeys () - { - i2p::data::PrivateKeys::GenerateCryptoKeyPair (keyType, priv.data (), pub.data ()); - } - - void LocalEncryptionKey::CreateDecryptor () - { - decryptor = i2p::data::PrivateKeys::CreateDecryptor (keyType, priv.data ()); - } } } diff --git a/libi2pd/CryptoKey.h b/libi2pd/CryptoKey.h index b6c37ddf..a7d86d09 100644 --- a/libi2pd/CryptoKey.h +++ b/libi2pd/CryptoKey.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2025, The PurpleI2P Project +* Copyright (c) 2013-2021, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -11,7 +11,6 @@ #include #include "Crypto.h" -#include "Identity.h" namespace i2p { @@ -158,50 +157,7 @@ namespace crypto X25519Keys m_StaticKeys; }; - void CreateECIESX25519AEADRatchetRandomKeys (uint8_t * priv, uint8_t * pub); // including hybrid - - constexpr size_t GetCryptoPrivateKeyLen (i2p::data::CryptoKeyType type) - { - switch (type) - { - case i2p::data::CRYPTO_KEY_TYPE_ELGAMAL: return 256; - case i2p::data::CRYPTO_KEY_TYPE_ECIES_P256_SHA256_AES256CBC: return 32; - case i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD: return 32; - // ML-KEM hybrid - case i2p::data::CRYPTO_KEY_TYPE_ECIES_MLKEM512_X25519_AEAD: - case i2p::data::CRYPTO_KEY_TYPE_ECIES_MLKEM768_X25519_AEAD: - case i2p::data::CRYPTO_KEY_TYPE_ECIES_MLKEM1024_X25519_AEAD: - return 32; - }; - return 0; - } - - constexpr size_t GetCryptoPublicKeyLen (i2p::data::CryptoKeyType type) - { - switch (type) - { - case i2p::data::CRYPTO_KEY_TYPE_ELGAMAL: return 256; - case i2p::data::CRYPTO_KEY_TYPE_ECIES_P256_SHA256_AES256CBC: return 32; - case i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD: return 32; - // ML-KEM hybrid - case i2p::data::CRYPTO_KEY_TYPE_ECIES_MLKEM512_X25519_AEAD: - case i2p::data::CRYPTO_KEY_TYPE_ECIES_MLKEM768_X25519_AEAD: - case i2p::data::CRYPTO_KEY_TYPE_ECIES_MLKEM1024_X25519_AEAD: - return 32; - }; - return 0; - } - - struct LocalEncryptionKey - { - std::vector pub, priv; - i2p::data::CryptoKeyType keyType; - std::shared_ptr decryptor; - - LocalEncryptionKey (i2p::data::CryptoKeyType t); - void GenerateKeys (); - void CreateDecryptor (); - }; + void CreateECIESX25519AEADRatchetRandomKeys (uint8_t * priv, uint8_t * pub); } } diff --git a/libi2pd/Datagram.cpp b/libi2pd/Datagram.cpp index 732efca7..1e0c06cc 100644 --- a/libi2pd/Datagram.cpp +++ b/libi2pd/Datagram.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2025, The PurpleI2P Project +* Copyright (c) 2013-2024, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -104,7 +104,8 @@ namespace datagram if (verified) { - auto session = ObtainSession (identity.GetIdentHash()); + auto h = identity.GetIdentHash(); + auto session = ObtainSession(h); session->Ack(); auto r = FindReceiver(toPort); if(r) @@ -380,19 +381,15 @@ namespace datagram if (!found) { m_RoutingSession = m_LocalDestination->GetRoutingSession(m_RemoteLeaseSet, true); - if (m_RoutingSession) - { - m_RoutingSession->SetAckRequestInterval (DATAGRAM_SESSION_ACK_REQUEST_INTERVAL); - if (!m_RoutingSession->GetOwner () || !m_RoutingSession->IsReadyToSend ()) - m_PendingRoutingSessions.push_back (m_RoutingSession); - } + if (!m_RoutingSession->GetOwner () || !m_RoutingSession->IsReadyToSend ()) + m_PendingRoutingSessions.push_back (m_RoutingSession); } } auto path = m_RoutingSession->GetSharedRoutingPath(); - if (path && m_RoutingSession->IsRatchets () && m_RoutingSession->CleanupUnconfirmedTags ()) + if (path && m_RoutingSession->IsRatchets () && (m_RoutingSession->CleanupUnconfirmedTags () || + m_LastUse > m_RoutingSession->GetLastActivityTimestamp ()*1000 + DATAGRAM_SESSION_PATH_TIMEOUT)) { - LogPrint (eLogDebug, "Datagram: path reset"); m_RoutingSession->SetSharedRoutingPath (nullptr); path = nullptr; } diff --git a/libi2pd/Datagram.h b/libi2pd/Datagram.h index dd358434..5a0bfc93 100644 --- a/libi2pd/Datagram.h +++ b/libi2pd/Datagram.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2025, 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,8 @@ namespace datagram { // milliseconds for max session idle time const uint64_t DATAGRAM_SESSION_MAX_IDLE = 10 * 60 * 1000; + // milliseconds for how long we try sticking to a dead routing path before trying to switch + const uint64_t DATAGRAM_SESSION_PATH_TIMEOUT = 10 * 1000; // milliseconds interval a routing path is used before switching const uint64_t DATAGRAM_SESSION_PATH_SWITCH_INTERVAL = 20 * 60 * 1000; // milliseconds before lease expire should we try switching leases @@ -42,7 +44,6 @@ namespace datagram // max 64 messages buffered in send queue for each datagram session const size_t DATAGRAM_SEND_QUEUE_MAX_SIZE = 64; const uint64_t DATAGRAM_MAX_FLUSH_INTERVAL = 5; // in milliseconds - const int DATAGRAM_SESSION_ACK_REQUEST_INTERVAL = 5500; // in milliseconds class DatagramSession : public std::enable_shared_from_this { diff --git a/libi2pd/Destination.cpp b/libi2pd/Destination.cpp index fd23e228..28b23950 100644 --- a/libi2pd/Destination.cpp +++ b/libi2pd/Destination.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2025, The PurpleI2P Project +* Copyright (c) 2013-2024, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -13,7 +13,6 @@ #include #include #include "Crypto.h" -#include "ECIESX25519AEADRatchetSession.h" #include "Log.h" #include "FS.h" #include "Timestamp.h" @@ -24,7 +23,7 @@ namespace i2p { namespace client { - LeaseSetDestination::LeaseSetDestination (boost::asio::io_context& service, + LeaseSetDestination::LeaseSetDestination (boost::asio::io_service& service, bool isPublic, const std::map * params): m_Service (service), m_IsPublic (isPublic), m_PublishReplyToken (0), m_LastSubmissionTime (0), m_PublishConfirmationTimer (m_Service), @@ -38,7 +37,7 @@ namespace client int inVar = DEFAULT_INBOUND_TUNNELS_LENGTH_VARIANCE; int outVar = DEFAULT_OUTBOUND_TUNNELS_LENGTH_VARIANCE; int numTags = DEFAULT_TAGS_TO_SEND; - bool isHighBandwidth = true; + bool isHighBandwidth = true; std::shared_ptr > explicitPeers; try { @@ -169,7 +168,7 @@ namespace client LoadTags (); m_Pool->SetLocalDestination (shared_from_this ()); m_Pool->SetActive (true); - m_CleanupTimer.expires_from_now (boost::posix_time::seconds (DESTINATION_CLEANUP_TIMEOUT)); + m_CleanupTimer.expires_from_now (boost::posix_time::minutes (DESTINATION_CLEANUP_TIMEOUT)); m_CleanupTimer.async_wait (std::bind (&LeaseSetDestination::HandleCleanupTimer, shared_from_this (), std::placeholders::_1)); } @@ -196,7 +195,7 @@ namespace client m_IsPublic = itr->second != "true"; } - int inLen = 0, outLen = 0, inQuant = 0, outQuant = 0, numTags = 0, minLatency = 0, maxLatency = 0; + int inLen, outLen, inQuant, outQuant, numTags, minLatency, maxLatency; std::map intOpts = { {I2CP_PARAM_INBOUND_TUNNEL_LENGTH, inLen}, {I2CP_PARAM_OUTBOUND_TUNNEL_LENGTH, outLen}, @@ -295,7 +294,7 @@ namespace client if (m_IsPublic) { auto s = shared_from_this (); - boost::asio::post (m_Service, [s](void) + m_Service.post ([s](void) { s->m_PublishVerificationTimer.cancel (); s->Publish (); @@ -323,7 +322,7 @@ namespace client memcpy (data.k, key, 32); memcpy (data.t, tag, 32); auto s = shared_from_this (); - boost::asio::post (m_Service, [s,data](void) + m_Service.post ([s,data](void) { s->AddSessionKey (data.k, data.t); }); @@ -340,7 +339,7 @@ namespace client memcpy (data.k, key, 32); data.t = tag; auto s = shared_from_this (); - boost::asio::post (m_Service, [s,data](void) + m_Service.post ([s,data](void) { s->AddECIESx25519Key (data.k, data.t); }); @@ -348,42 +347,23 @@ namespace client void LeaseSetDestination::ProcessGarlicMessage (std::shared_ptr msg) { - if (!msg) return; - bool empty = false; - { - std::lock_guard l(m_IncomingMsgsQueueMutex); - empty = m_IncomingMsgsQueue.empty (); - m_IncomingMsgsQueue.push_back (msg); - } - if (empty) - boost::asio::post (m_Service, [s = shared_from_this ()]() - { - std::list > receivedMsgs; - { - std::lock_guard l(s->m_IncomingMsgsQueueMutex); - s->m_IncomingMsgsQueue.swap (receivedMsgs); - } - for (auto& it: receivedMsgs) - s->HandleGarlicMessage (it); - }); + m_Service.post (std::bind (&LeaseSetDestination::HandleGarlicMessage, shared_from_this (), msg)); } void LeaseSetDestination::ProcessDeliveryStatusMessage (std::shared_ptr msg) { uint32_t msgID = bufbe32toh (msg->GetPayload () + DELIVERY_STATUS_MSGID_OFFSET); - boost::asio::post (m_Service, std::bind (&LeaseSetDestination::HandleDeliveryStatusMessage, shared_from_this (), msgID)); + m_Service.post (std::bind (&LeaseSetDestination::HandleDeliveryStatusMessage, shared_from_this (), msgID)); } void LeaseSetDestination::HandleI2NPMessage (const uint8_t * buf, size_t len) { I2NPMessageType typeID = (I2NPMessageType)(buf[I2NP_HEADER_TYPEID_OFFSET]); uint32_t msgID = bufbe32toh (buf + I2NP_HEADER_MSGID_OFFSET); - LeaseSetDestination::HandleCloveI2NPMessage (typeID, buf + I2NP_HEADER_SIZE, - GetI2NPMessageLength(buf, len) - I2NP_HEADER_SIZE, msgID, nullptr); + LeaseSetDestination::HandleCloveI2NPMessage (typeID, buf + I2NP_HEADER_SIZE, GetI2NPMessageLength(buf, len) - I2NP_HEADER_SIZE, msgID); } - bool LeaseSetDestination::HandleCloveI2NPMessage (I2NPMessageType typeID, const uint8_t * payload, - size_t len, uint32_t msgID, i2p::garlic::ECIESX25519AEADRatchetSession * from) + bool LeaseSetDestination::HandleCloveI2NPMessage (I2NPMessageType typeID, const uint8_t * payload, size_t len, uint32_t msgID) { switch (typeID) { @@ -398,7 +378,7 @@ namespace client m_Pool->ProcessTunnelTest (bufbe32toh (payload + TUNNEL_TEST_MSGID_OFFSET), bufbe64toh (payload + TUNNEL_TEST_TIMESTAMP_OFFSET)); break; case eI2NPDatabaseStore: - HandleDatabaseStoreMessage (payload, len, from); + HandleDatabaseStoreMessage (payload, len); break; case eI2NPDatabaseSearchReply: HandleDatabaseSearchReplyMessage (payload, len); @@ -413,8 +393,7 @@ namespace client return true; } - void LeaseSetDestination::HandleDatabaseStoreMessage (const uint8_t * buf, size_t len, - i2p::garlic::ECIESX25519AEADRatchetSession * from) + void LeaseSetDestination::HandleDatabaseStoreMessage (const uint8_t * buf, size_t len) { if (len < DATABASE_STORE_HEADER_SIZE) { @@ -469,21 +448,8 @@ namespace client if (buf[DATABASE_STORE_TYPE_OFFSET] == i2p::data::NETDB_STORE_TYPE_LEASESET) leaseSet = std::make_shared (buf + offset, len - offset); // LeaseSet else - { - leaseSet = std::make_shared (buf[DATABASE_STORE_TYPE_OFFSET], - buf + offset, len - offset, true, from ? from->GetRemoteStaticKeyType () : GetPreferredCryptoType () ); // LeaseSet2 - if (from) - { - uint8_t pub[32]; - leaseSet->Encrypt (nullptr, pub); - if (memcmp (from->GetRemoteStaticKey (), pub, 32)) - { - LogPrint (eLogError, "Destination: Remote LeaseSet static key mismatch"); - leaseSet = nullptr; - } - } - } - if (leaseSet && leaseSet->IsValid () && leaseSet->GetIdentHash () == key && !leaseSet->IsExpired ()) + leaseSet = std::make_shared (buf[DATABASE_STORE_TYPE_OFFSET], buf + offset, len - offset, true, GetPreferredCryptoType () ); // LeaseSet2 + if (leaseSet->IsValid () && leaseSet->GetIdentHash () == key && !leaseSet->IsExpired ()) { if (leaseSet->GetIdentHash () != GetIdentHash ()) { @@ -505,14 +471,13 @@ namespace client { auto it2 = m_LeaseSetRequests.find (key); if (it2 != m_LeaseSetRequests.end ()) - { + { request = it2->second; m_LeaseSetRequests.erase (it2); if (request->requestedBlindedKey) { auto ls2 = std::make_shared (buf + offset, len - offset, - request->requestedBlindedKey, m_LeaseSetPrivKey ? ((const uint8_t *)*m_LeaseSetPrivKey) : nullptr, - GetPreferredCryptoType ()); + request->requestedBlindedKey, m_LeaseSetPrivKey ? ((const uint8_t *)*m_LeaseSetPrivKey) : nullptr , GetPreferredCryptoType ()); if (ls2->IsValid () && !ls2->IsExpired ()) { leaseSet = ls2; @@ -528,14 +493,14 @@ namespace client // publishing verification doesn't have requestedBlindedKey auto localLeaseSet = GetLeaseSetMt (); if (localLeaseSet->GetStoreHash () == key) - { - auto ls = std::make_shared (i2p::data::NETDB_STORE_TYPE_ENCRYPTED_LEASESET2, + { + auto ls = std::make_shared (i2p::data::NETDB_STORE_TYPE_ENCRYPTED_LEASESET2, localLeaseSet->GetBuffer (), localLeaseSet->GetBufferLen (), false); - leaseSet = ls; - } + leaseSet = ls; + } else LogPrint (eLogWarning, "Destination: Encrypted LeaseSet2 received for request without blinded key"); - } + } } else LogPrint (eLogWarning, "Destination: Couldn't find request for encrypted LeaseSet2"); @@ -546,14 +511,14 @@ namespace client } if (!request) - { + { auto it1 = m_LeaseSetRequests.find (key); if (it1 != m_LeaseSetRequests.end ()) - { + { request = it1->second; m_LeaseSetRequests.erase (it1); - } - } + } + } if (request) { request->requestTimeoutTimer.cancel (); @@ -585,7 +550,7 @@ namespace client LogPrint (eLogWarning, "Destination: Request for ", key.ToBase64 (), " not found"); } - void LeaseSetDestination::SendNextLeaseSetRequest (const i2p::data::IdentHash& key, + void LeaseSetDestination::SendNextLeaseSetRequest (const i2p::data::IdentHash& key, std::shared_ptr request) { bool found = false; @@ -605,8 +570,8 @@ namespace client request->Complete (nullptr); m_LeaseSetRequests.erase (key); } - } - + } + void LeaseSetDestination::HandleDeliveryStatusMessage (uint32_t msgID) { if (msgID == m_PublishReplyToken) @@ -615,8 +580,7 @@ namespace client m_ExcludedFloodfills.clear (); m_PublishReplyToken = 0; // schedule verification - m_PublishVerificationTimer.expires_from_now (boost::posix_time::seconds(PUBLISH_VERIFICATION_TIMEOUT + - (m_Pool ? m_Pool->GetRng ()() % PUBLISH_VERIFICATION_TIMEOUT_VARIANCE : 0))); + m_PublishVerificationTimer.expires_from_now (boost::posix_time::seconds(PUBLISH_VERIFICATION_TIMEOUT)); m_PublishVerificationTimer.async_wait (std::bind (&LeaseSetDestination::HandlePublishVerificationTimer, shared_from_this (), std::placeholders::_1)); } @@ -624,12 +588,9 @@ namespace client i2p::garlic::GarlicDestination::HandleDeliveryStatusMessage (msgID); } - void LeaseSetDestination::SetLeaseSetUpdated (bool post) + void LeaseSetDestination::SetLeaseSetUpdated () { - if (post) - boost::asio::post (m_Service, [s = shared_from_this ()]() { s->UpdateLeaseSet (); }); - else - UpdateLeaseSet (); + UpdateLeaseSet (); } void LeaseSetDestination::Publish () @@ -667,7 +628,7 @@ namespace client if (!outbound || !inbound) { if (!m_Pool->GetInboundTunnels ().empty () && !m_Pool->GetOutboundTunnels ().empty ()) - { + { LogPrint (eLogInfo, "Destination: No compatible tunnels with ", floodfill->GetIdentHash ().ToBase64 (), ". Trying another floodfill"); m_ExcludedFloodfills.insert (floodfill->GetIdentHash ()); floodfill = i2p::data::netdb.GetClosestFloodfill (leaseSet->GetStoreHash (), m_ExcludedFloodfills); @@ -685,18 +646,18 @@ namespace client } else LogPrint (eLogError, "Destination: Can't publish LeaseSet, no more floodfills found"); - } + } else LogPrint (eLogDebug, "Destination: No tunnels in pool"); - + if (!floodfill || !outbound || !inbound) { // we can't publish now m_ExcludedFloodfills.clear (); m_PublishReplyToken = 1; // dummy non-zero value // try again after a while - LogPrint (eLogInfo, "Destination: Can't publish LeasetSet because destination is not ready. Try publishing again after ", PUBLISH_CONFIRMATION_TIMEOUT, " milliseconds"); - m_PublishConfirmationTimer.expires_from_now (boost::posix_time::milliseconds(PUBLISH_CONFIRMATION_TIMEOUT)); + LogPrint (eLogInfo, "Destination: Can't publish LeasetSet because destination is not ready. Try publishing again after ", PUBLISH_CONFIRMATION_TIMEOUT, " seconds"); + 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)); return; @@ -709,13 +670,13 @@ namespace client auto s = shared_from_this (); msg->onDrop = [s]() { - boost::asio::post (s->GetService (), [s]() + s->GetService ().post([s]() { s->m_PublishConfirmationTimer.cancel (); s->HandlePublishConfirmationTimer (boost::system::error_code()); }); }; - m_PublishConfirmationTimer.expires_from_now (boost::posix_time::milliseconds(PUBLISH_CONFIRMATION_TIMEOUT)); + 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)); outbound->SendTunnelDataMsgTo (floodfill->GetIdentHash (), 0, msg); @@ -731,15 +692,15 @@ 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, " milliseconds or failed. will try again"); + LogPrint (eLogWarning, "Destination: Publish confirmation was not received in ", PUBLISH_CONFIRMATION_TIMEOUT, " seconds or failed. will try again"); Publish (); } else { - LogPrint (eLogWarning, "Destination: Publish confirmation was not received in ", PUBLISH_CONFIRMATION_TIMEOUT, " milliseconds from Java floodfill for crypto type ", (int)GetIdentity ()->GetCryptoKeyType ()); + LogPrint (eLogWarning, "Destination: Publish confirmation was not received in ", PUBLISH_CONFIRMATION_TIMEOUT, " seconds from Java floodfill for crypto type ", (int)GetIdentity ()->GetCryptoKeyType ()); // Java floodfill never sends confirmation back for unknown crypto type // assume it successive and try to verify - m_PublishVerificationTimer.expires_from_now (boost::posix_time::seconds(PUBLISH_VERIFICATION_TIMEOUT + PUBLISH_VERIFICATION_TIMEOUT_VARIANCE)); // always max + m_PublishVerificationTimer.expires_from_now (boost::posix_time::seconds(PUBLISH_VERIFICATION_TIMEOUT)); m_PublishVerificationTimer.async_wait (std::bind (&LeaseSetDestination::HandlePublishVerificationTimer, shared_from_this (), std::placeholders::_1)); @@ -794,10 +755,10 @@ namespace client if (!m_Pool || !IsReady ()) { if (requestComplete) - boost::asio::post (m_Service, [requestComplete](void){requestComplete (nullptr);}); + m_Service.post ([requestComplete](void){requestComplete (nullptr);}); return false; } - boost::asio::post (m_Service, std::bind (&LeaseSetDestination::RequestLeaseSet, shared_from_this (), dest, requestComplete, nullptr)); + m_Service.post (std::bind (&LeaseSetDestination::RequestLeaseSet, shared_from_this (), dest, requestComplete, nullptr)); return true; } @@ -806,7 +767,7 @@ namespace client if (!dest || !m_Pool || !IsReady ()) { if (requestComplete) - boost::asio::post (m_Service, [requestComplete](void){requestComplete (nullptr);}); + m_Service.post ([requestComplete](void){requestComplete (nullptr);}); return false; } auto storeHash = dest->GetStoreHash (); @@ -814,17 +775,17 @@ namespace client if (leaseSet) { if (requestComplete) - boost::asio::post (m_Service, [requestComplete, leaseSet](void){requestComplete (leaseSet);}); + m_Service.post ([requestComplete, leaseSet](void){requestComplete (leaseSet);}); return true; } - boost::asio::post (m_Service, std::bind (&LeaseSetDestination::RequestLeaseSet, shared_from_this (), storeHash, requestComplete, dest)); + m_Service.post (std::bind (&LeaseSetDestination::RequestLeaseSet, shared_from_this (), storeHash, requestComplete, dest)); return true; } void LeaseSetDestination::CancelDestinationRequest (const i2p::data::IdentHash& dest, bool notify) { auto s = shared_from_this (); - boost::asio::post (m_Service, [dest, notify, s](void) + m_Service.post ([dest, notify, s](void) { auto it = s->m_LeaseSetRequests.find (dest); if (it != s->m_LeaseSetRequests.end ()) @@ -852,7 +813,7 @@ namespace client request->requestedBlindedKey = requestedBlindedKey; // for encrypted LeaseSet2 if (requestComplete) request->requestComplete.push_back (requestComplete); - auto ts = i2p::util::GetMillisecondsSinceEpoch (); + auto ts = i2p::util::GetSecondsSinceEpoch (); auto ret = m_LeaseSetRequests.insert (std::pair >(dest,request)); if (ret.second) // inserted { @@ -916,17 +877,17 @@ namespace client AddECIESx25519Key (replyKey, replyTag); else AddSessionKey (replyKey, replyTag); - - auto msg = WrapMessageForRouter (nextFloodfill, + + auto msg = WrapMessageForRouter (nextFloodfill, CreateLeaseSetDatabaseLookupMsg (dest, request->excluded, request->replyTunnel, replyKey, replyTag, isECIES)); auto s = shared_from_this (); msg->onDrop = [s, dest, request]() { - boost::asio::post (s->GetService (), [s, dest, request]() + s->GetService ().post([s, dest, request]() { s->SendNextLeaseSetRequest (dest, request); }); - }; + }; request->outboundTunnel->SendTunnelDataMsgs ( { i2p::tunnel::TunnelMessageBlock @@ -935,7 +896,7 @@ namespace client nextFloodfill->GetIdentHash (), 0, msg } }); - request->requestTimeoutTimer.expires_from_now (boost::posix_time::milliseconds(LEASESET_REQUEST_TIMEOUT)); + request->requestTimeoutTimer.expires_from_now (boost::posix_time::seconds(LEASESET_REQUEST_TIMEOUT)); request->requestTimeoutTimer.async_wait (std::bind (&LeaseSetDestination::HandleRequestTimoutTimer, shared_from_this (), std::placeholders::_1, dest)); } @@ -952,7 +913,7 @@ namespace client if (it != m_LeaseSetRequests.end ()) { bool done = false; - uint64_t ts = i2p::util::GetMillisecondsSinceEpoch (); + uint64_t ts = i2p::util::GetSecondsSinceEpoch (); if (ts < it->second->requestTime + MAX_LEASESET_REQUEST_TIMEOUT) { auto floodfill = i2p::data::netdb.GetClosestFloodfill (dest, it->second->excluded); @@ -989,8 +950,7 @@ namespace client CleanupExpiredTags (); CleanupRemoteLeaseSets (); CleanupDestination (); - m_CleanupTimer.expires_from_now (boost::posix_time::seconds (DESTINATION_CLEANUP_TIMEOUT + - (m_Pool ? m_Pool->GetRng ()() % DESTINATION_CLEANUP_TIMEOUT_VARIANCE : 0))); + m_CleanupTimer.expires_from_now (boost::posix_time::minutes (DESTINATION_CLEANUP_TIMEOUT)); m_CleanupTimer.async_wait (std::bind (&LeaseSetDestination::HandleCleanupTimer, shared_from_this (), std::placeholders::_1)); } @@ -1004,7 +964,7 @@ namespace client { if (it->second->IsEmpty () || ts > it->second->GetExpirationTime ()) // leaseset expired { - LogPrint (eLogDebug, "Destination: Remote LeaseSet ", it->second->GetIdentHash ().ToBase64 (), " expired"); + LogPrint (eLogWarning, "Destination: Remote LeaseSet ", it->second->GetIdentHash ().ToBase64 (), " expired"); it = m_RemoteLeaseSets.erase (it); } else @@ -1012,13 +972,19 @@ namespace client } } - ClientDestination::ClientDestination (boost::asio::io_context& service, const i2p::data::PrivateKeys& keys, + i2p::data::CryptoKeyType LeaseSetDestination::GetPreferredCryptoType () const + { + if (SupportsEncryptionType (i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD)) + return i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD; + return i2p::data::CRYPTO_KEY_TYPE_ELGAMAL; + } + + ClientDestination::ClientDestination (boost::asio::io_service& service, const i2p::data::PrivateKeys& keys, bool isPublic, const std::map * params): LeaseSetDestination (service, isPublic, params), - m_Keys (keys), m_PreferredCryptoType (0), m_StreamingAckDelay (DEFAULT_INITIAL_ACK_DELAY), + m_Keys (keys), m_StreamingAckDelay (DEFAULT_INITIAL_ACK_DELAY), m_StreamingOutboundSpeed (DEFAULT_MAX_OUTBOUND_SPEED), m_StreamingInboundSpeed (DEFAULT_MAX_INBOUND_SPEED), - m_StreamingMaxConcurrentStreams (DEFAULT_MAX_CONCURRENT_STREAMS), m_IsStreamingAnswerPings (DEFAULT_ANSWER_PINGS), m_LastPort (0), m_DatagramDestination (nullptr), m_RefCounter (0), m_LastPublishedTimestamp (0), m_ReadyChecker(service) @@ -1040,15 +1006,7 @@ namespace client { try { - i2p::data::CryptoKeyType cryptoType = std::stoi(it1); -#if !OPENSSL_PQ - if (cryptoType <= i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD) // skip PQ keys if not supported -#endif - { - if (!m_PreferredCryptoType && cryptoType) - m_PreferredCryptoType = cryptoType; // first non-zero in the list - encryptionKeyTypes.insert (cryptoType); - } + encryptionKeyTypes.insert (std::stoi(it1)); } catch (std::exception& ex) { @@ -1065,15 +1023,20 @@ namespace client for (auto& it: encryptionKeyTypes) { - auto encryptionKey = std::make_shared (it); + auto encryptionKey = new EncryptionKey (it); if (IsPublic ()) PersistTemporaryKeys (encryptionKey); else encryptionKey->GenerateKeys (); encryptionKey->CreateDecryptor (); - if (it > i2p::data::CRYPTO_KEY_TYPE_ELGAMAL && GetLeaseSetType () == i2p::data::NETDB_STORE_TYPE_LEASESET) - SetLeaseSetType (i2p::data::NETDB_STORE_TYPE_STANDARD_LEASESET2); // Only DSA can use LeaseSet1 - m_EncryptionKeys.emplace (it, encryptionKey); + if (it == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD) + { + m_ECIESx25519EncryptionKey.reset (encryptionKey); + if (GetLeaseSetType () == i2p::data::NETDB_STORE_TYPE_LEASESET) + SetLeaseSetType (i2p::data::NETDB_STORE_TYPE_STANDARD_LEASESET2); // Rathets must use LeaseSet2 + } + else + m_StandardEncryptionKey.reset (encryptionKey); } if (IsPublic ()) @@ -1093,8 +1056,6 @@ namespace client it = params->find (I2CP_PARAM_STREAMING_MAX_INBOUND_SPEED); if (it != params->end ()) m_StreamingInboundSpeed = std::stoi(it->second); - if (it != params->end ()) - m_StreamingMaxConcurrentStreams = std::stoi(it->second); it = params->find (I2CP_PARAM_STREAMING_ANSWER_PINGS); if (it != params->end ()) m_IsStreamingAnswerPings = std::stoi (it->second); // 1 for true @@ -1230,7 +1191,7 @@ namespace client if (leaseSet) { auto stream = CreateStream (leaseSet, port); - boost::asio::post (GetService (), [streamRequestComplete, stream]() + GetService ().post ([streamRequestComplete, stream]() { streamRequestComplete(stream); }); @@ -1423,56 +1384,31 @@ namespace client return ret; } - void ClientDestination::PersistTemporaryKeys (std::shared_ptr keys) + void ClientDestination::PersistTemporaryKeys (EncryptionKey * keys) { if (!keys) return; std::string ident = GetIdentHash().ToBase32(); std::string path = i2p::fs::DataDirPath("destinations", ident + "." + std::to_string (keys->keyType) + ".dat"); std::ifstream f(path, std::ifstream::binary); - if (f) - { - size_t len = 0; - if (keys->keyType == i2p::data::CRYPTO_KEY_TYPE_ELGAMAL) - len = 512; - else if (keys->keyType == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD) - { - f.seekg (0, std::ios::end); - len = f.tellg(); - f.seekg (0, std::ios::beg); - } - - if (len == 512) - { - char pub[256], priv[256]; - f.read (pub, 256); - memcpy (keys->pub.data(), pub, keys->pub.size()); - f.read (priv, 256); - memcpy (keys->priv.data (), priv, keys->priv.size ()); - } - else - { - f.read ((char *)keys->pub.data(), keys->pub.size()); - f.read ((char *)keys->priv.data(), keys->priv.size()); - } - if (f) - return; - else - LogPrint(eLogWarning, "Destination: Can't read keys from ", path); + + if (f) { + f.read ((char *)keys->pub, 256); + f.read ((char *)keys->priv, 256); + return; } - LogPrint (eLogInfo, "Destination: Creating new temporary keys of type ", keys->keyType, " for address ", ident, ".b32.i2p"); - memset (keys->priv.data (), 0, keys->priv.size ()); - memset (keys->pub.data (), 0, keys->pub.size ()); + LogPrint (eLogInfo, "Destination: Creating new temporary keys of type for address ", ident, ".b32.i2p"); + memset (keys->priv, 0, 256); + memset (keys->pub, 0, 256); keys->GenerateKeys (); - + // TODO:: persist crypto key type std::ofstream f1 (path, std::ofstream::binary | std::ofstream::out); - if (f1) - { - f1.write ((char *)keys->pub.data (), keys->pub.size ()); - f1.write ((char *)keys->priv.data (), keys->priv.size ()); + if (f1) { + f1.write ((char *)keys->pub, 256); + f1.write ((char *)keys->priv, 256); + return; } - if (!f1) - LogPrint(eLogError, "Destination: Can't save keys to ", path); + LogPrint(eLogCritical, "Destinations: Can't save keys to ", path); } void ClientDestination::CreateNewLeaseSet (const std::vector >& tunnels) @@ -1480,10 +1416,9 @@ namespace client std::shared_ptr leaseSet; if (GetLeaseSetType () == i2p::data::NETDB_STORE_TYPE_LEASESET) { - auto it = m_EncryptionKeys.find (i2p::data::CRYPTO_KEY_TYPE_ELGAMAL); - if (it != m_EncryptionKeys.end ()) + if (m_StandardEncryptionKey) { - leaseSet = std::make_shared (GetIdentity (), it->second->pub.data (), tunnels); + leaseSet = std::make_shared (GetIdentity (), m_StandardEncryptionKey->pub, tunnels); // sign Sign (leaseSet->GetBuffer (), leaseSet->GetBufferLen () - leaseSet->GetSignatureLen (), leaseSet->GetSignature ()); } @@ -1493,33 +1428,18 @@ namespace client else { // standard LS2 (type 3) first - if (m_EncryptionKeys.empty ()) - { - LogPrint (eLogError, "Destinations: No encryption keys"); - return; - } - - i2p::data::LocalLeaseSet2::EncryptionKeys keySections; - std::shared_ptr preferredSection; - if (m_EncryptionKeys.size () == 1) - preferredSection = m_EncryptionKeys.begin ()->second; // only key - else - { - for (const auto& it: m_EncryptionKeys) - if (it.first == m_PreferredCryptoType) - preferredSection = it.second; - else - keySections.push_back (it.second); - } - if (preferredSection) - keySections.push_front (preferredSection); // make preferred first - + i2p::data::LocalLeaseSet2::KeySections keySections; + if (m_ECIESx25519EncryptionKey) + keySections.push_back ({m_ECIESx25519EncryptionKey->keyType, 32, m_ECIESx25519EncryptionKey->pub} ); + if (m_StandardEncryptionKey) + keySections.push_back ({m_StandardEncryptionKey->keyType, (uint16_t)m_StandardEncryptionKey->decryptor->GetPublicKeyLen (), m_StandardEncryptionKey->pub} ); + auto publishedTimestamp = i2p::util::GetSecondsSinceEpoch (); - if (publishedTimestamp <= m_LastPublishedTimestamp) + if (publishedTimestamp <= m_LastPublishedTimestamp) { LogPrint (eLogDebug, "Destination: LeaseSet update at the same second"); publishedTimestamp++; // force newer timestamp - } + } bool isPublishedEncrypted = GetLeaseSetType () == i2p::data::NETDB_STORE_TYPE_ENCRYPTED_LEASESET2; auto ls2 = std::make_shared (i2p::data::NETDB_STORE_TYPE_STANDARD_LEASESET2, m_Keys, keySections, tunnels, IsPublic (), publishedTimestamp, isPublishedEncrypted); @@ -1538,22 +1458,11 @@ namespace client bool ClientDestination::Decrypt (const uint8_t * encrypted, uint8_t * data, i2p::data::CryptoKeyType preferredCrypto) const { - std::shared_ptr encryptionKey; - if (!m_EncryptionKeys.empty ()) - { - if (m_EncryptionKeys.rbegin ()->first == preferredCrypto) - encryptionKey = m_EncryptionKeys.rbegin ()->second; - else - { - auto it = m_EncryptionKeys.find (preferredCrypto); - if (it != m_EncryptionKeys.end ()) - encryptionKey = it->second; - } - if (!encryptionKey) - encryptionKey = m_EncryptionKeys.rbegin ()->second; - } - if (encryptionKey) - return encryptionKey->decryptor->Decrypt (encrypted, data); + if (preferredCrypto == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD) + if (m_ECIESx25519EncryptionKey && m_ECIESx25519EncryptionKey->decryptor) + return m_ECIESx25519EncryptionKey->decryptor->Decrypt (encrypted, data); + if (m_StandardEncryptionKey && m_StandardEncryptionKey->decryptor) + return m_StandardEncryptionKey->decryptor->Decrypt (encrypted, data); else LogPrint (eLogError, "Destinations: Decryptor is not set"); return false; @@ -1561,26 +1470,14 @@ namespace client bool ClientDestination::SupportsEncryptionType (i2p::data::CryptoKeyType keyType) const { -#if __cplusplus >= 202002L // C++20 - return m_EncryptionKeys.contains (keyType); -#else - return m_EncryptionKeys.count (keyType) > 0; -#endif + return keyType == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD ? (bool)m_ECIESx25519EncryptionKey : (bool)m_StandardEncryptionKey; } - i2p::data::CryptoKeyType ClientDestination::GetRatchetsHighestCryptoType () const - { - if (m_EncryptionKeys.empty ()) return 0; - auto cryptoType = m_EncryptionKeys.rbegin ()->first; - return cryptoType >= i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD ? cryptoType : 0; - } - const uint8_t * ClientDestination::GetEncryptionPublicKey (i2p::data::CryptoKeyType keyType) const { - auto it = m_EncryptionKeys.find (keyType); - if (it != m_EncryptionKeys.end ()) - return it->second->pub.data (); - return nullptr; + if (keyType == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD) + return m_ECIESx25519EncryptionKey ? m_ECIESx25519EncryptionKey->pub : nullptr; + return m_StandardEncryptionKey ? m_StandardEncryptionKey->pub : nullptr; } void ClientDestination::ReadAuthKey (const std::string& group, const std::map * params) @@ -1614,8 +1511,6 @@ namespace client RunnableService ("Destination"), ClientDestination (GetIOService (), keys, isPublic, params) { - if (!GetNickname ().empty ()) - RunnableService::SetName (GetNickname ()); } RunnableClientDestination::~RunnableClientDestination () diff --git a/libi2pd/Destination.h b/libi2pd/Destination.h index 35557859..4a51a257 100644 --- a/libi2pd/Destination.h +++ b/libi2pd/Destination.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2025, The PurpleI2P Project +* Copyright (c) 2013-2024, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -22,7 +22,6 @@ #include "Identity.h" #include "TunnelPool.h" #include "Crypto.h" -#include "CryptoKey.h" #include "LeaseSet.h" #include "Garlic.h" #include "NetDb.hpp" @@ -37,15 +36,13 @@ namespace client const uint8_t PROTOCOL_TYPE_STREAMING = 6; const uint8_t PROTOCOL_TYPE_DATAGRAM = 17; const uint8_t PROTOCOL_TYPE_RAW = 18; - const int PUBLISH_CONFIRMATION_TIMEOUT = 1800; // in milliseconds - const int PUBLISH_VERIFICATION_TIMEOUT = 5; // in seconds after successful publish - const int PUBLISH_VERIFICATION_TIMEOUT_VARIANCE = 3; // in seconds + const int PUBLISH_CONFIRMATION_TIMEOUT = 5; // in seconds + const int PUBLISH_VERIFICATION_TIMEOUT = 10; // in seconds after successful publish const int PUBLISH_MIN_INTERVAL = 20; // in seconds const int PUBLISH_REGULAR_VERIFICATION_INTERNAL = 100; // in seconds periodically - const int LEASESET_REQUEST_TIMEOUT = 1600; // in milliseconds - const int MAX_LEASESET_REQUEST_TIMEOUT = 12000; // in milliseconds - const int DESTINATION_CLEANUP_TIMEOUT = 44; // in seconds - const int DESTINATION_CLEANUP_TIMEOUT_VARIANCE = 30; // in seconds + const int LEASESET_REQUEST_TIMEOUT = 5; // in seconds + const int MAX_LEASESET_REQUEST_TIMEOUT = 40; // in seconds + const int DESTINATION_CLEANUP_TIMEOUT = 3; // in minutes const unsigned int MAX_NUM_FLOODFILLS_PER_REQUEST = 7; // I2CP @@ -97,9 +94,7 @@ namespace client const int STREAMING_PROFILE_BULK = 1; // high bandwidth const int STREAMING_PROFILE_INTERACTIVE = 2; // low bandwidth const int DEFAULT_STREAMING_PROFILE = STREAMING_PROFILE_BULK; - const char I2CP_PARAM_STREAMING_MAX_CONCURRENT_STREAMS[] = "i2p.streaming.maxConcurrentStreams"; - const int DEFAULT_MAX_CONCURRENT_STREAMS = 2048; - + typedef std::function stream)> StreamRequestComplete; class LeaseSetDestination: public i2p::garlic::GarlicDestination, @@ -109,7 +104,7 @@ namespace client // leaseSet = nullptr means not found struct LeaseSetRequest { - LeaseSetRequest (boost::asio::io_context& service): requestTime (0), requestTimeoutTimer (service) {}; + LeaseSetRequest (boost::asio::io_service& service): requestTime (0), requestTimeoutTimer (service) {}; std::unordered_set excluded; uint64_t requestTime; boost::asio::deadline_timer requestTimeoutTimer; @@ -127,10 +122,10 @@ namespace client public: - LeaseSetDestination (boost::asio::io_context& service, bool isPublic, const std::map * params = nullptr); + LeaseSetDestination (boost::asio::io_service& service, bool isPublic, const std::map * params = nullptr); ~LeaseSetDestination (); const std::string& GetNickname () const { return m_Nickname; }; - auto& GetService () { return m_Service; }; + boost::asio::io_service& GetService () { return m_Service; }; virtual void Start (); virtual void Stop (); @@ -147,15 +142,15 @@ namespace client void CancelDestinationRequestWithEncryptedLeaseSet (std::shared_ptr dest, bool notify = true); // implements GarlicDestination - std::shared_ptr GetLeaseSet () override; - std::shared_ptr GetTunnelPool () const override { return m_Pool; } + std::shared_ptr GetLeaseSet (); + std::shared_ptr GetTunnelPool () const { return m_Pool; } // override GarlicDestination - bool SubmitSessionKey (const uint8_t * key, const uint8_t * tag) override; - void SubmitECIESx25519Key (const uint8_t * key, uint64_t tag) override; - void ProcessGarlicMessage (std::shared_ptr msg) override; - void ProcessDeliveryStatusMessage (std::shared_ptr msg) override; - void SetLeaseSetUpdated (bool post) override; + bool SubmitSessionKey (const uint8_t * key, const uint8_t * tag); + void SubmitECIESx25519Key (const uint8_t * key, uint64_t tag); + void ProcessGarlicMessage (std::shared_ptr msg); + void ProcessDeliveryStatusMessage (std::shared_ptr msg); + void SetLeaseSetUpdated (); bool IsPublic () const { return m_IsPublic; }; void SetPublic (bool pub) { m_IsPublic = pub; }; @@ -163,20 +158,18 @@ namespace client protected: // implements GarlicDestination - void HandleI2NPMessage (const uint8_t * buf, size_t len) override; - bool HandleCloveI2NPMessage (I2NPMessageType typeID, const uint8_t * payload, - size_t len, uint32_t msgID, i2p::garlic::ECIESX25519AEADRatchetSession * from) override; + void HandleI2NPMessage (const uint8_t * buf, size_t len); + bool HandleCloveI2NPMessage (I2NPMessageType typeID, const uint8_t * payload, size_t len, uint32_t msgID); void SetLeaseSet (std::shared_ptr newLeaseSet); int GetLeaseSetType () const { return m_LeaseSetType; }; void SetLeaseSetType (int leaseSetType) { m_LeaseSetType = leaseSetType; }; int GetAuthType () const { return m_AuthType; }; virtual void CleanupDestination () {}; // additional clean up in derived classes - virtual i2p::data::CryptoKeyType GetPreferredCryptoType () const = 0; // I2CP virtual void HandleDataMessage (const uint8_t * buf, size_t len) = 0; virtual void CreateNewLeaseSet (const std::vector >& tunnels) = 0; - + private: void UpdateLeaseSet (); @@ -185,7 +178,7 @@ namespace client void HandlePublishConfirmationTimer (const boost::system::error_code& ecode); void HandlePublishVerificationTimer (const boost::system::error_code& ecode); void HandlePublishDelayTimer (const boost::system::error_code& ecode); - void HandleDatabaseStoreMessage (const uint8_t * buf, size_t len, i2p::garlic::ECIESX25519AEADRatchetSession * from); + void HandleDatabaseStoreMessage (const uint8_t * buf, size_t len); void HandleDatabaseSearchReplyMessage (const uint8_t * buf, size_t len); void HandleDeliveryStatusMessage (uint32_t msgID); @@ -195,17 +188,15 @@ namespace client void HandleRequestTimoutTimer (const boost::system::error_code& ecode, const i2p::data::IdentHash& dest); void HandleCleanupTimer (const boost::system::error_code& ecode); void CleanupRemoteLeaseSets (); + i2p::data::CryptoKeyType GetPreferredCryptoType () const; private: - boost::asio::io_context& m_Service; + boost::asio::io_service& m_Service; mutable std::mutex m_RemoteLeaseSetsMutex; std::unordered_map > m_RemoteLeaseSets; std::unordered_map > m_LeaseSetRequests; - std::list > m_IncomingMsgsQueue; - mutable std::mutex m_IncomingMsgsQueueMutex; - std::shared_ptr m_Pool; std::mutex m_LeaseSetMutex; std::shared_ptr m_LeaseSet; @@ -231,14 +222,25 @@ namespace client class ClientDestination: public LeaseSetDestination { + struct EncryptionKey + { + uint8_t pub[256], priv[256]; + i2p::data::CryptoKeyType keyType; + std::shared_ptr decryptor; + + EncryptionKey (i2p::data::CryptoKeyType t):keyType(t) { memset (pub, 0, 256); memset (priv, 0, 256); }; + void GenerateKeys () { i2p::data::PrivateKeys::GenerateCryptoKeyPair (keyType, priv, pub); }; + void CreateDecryptor () { decryptor = i2p::data::PrivateKeys::CreateDecryptor (keyType, priv); }; + }; + public: - ClientDestination (boost::asio::io_context& service, const i2p::data::PrivateKeys& keys, + ClientDestination (boost::asio::io_service& service, const i2p::data::PrivateKeys& keys, bool isPublic, const std::map * params = nullptr); ~ClientDestination (); - void Start () override; - void Stop () override; + void Start (); + void Stop (); const i2p::data::PrivateKeys& GetPrivateKeys () const { return m_Keys; }; void Sign (const uint8_t * buf, int len, uint8_t * signature) const { m_Keys.Sign (buf, len, signature); }; @@ -267,7 +269,6 @@ namespace client int GetStreamingAckDelay () const { return m_StreamingAckDelay; } int GetStreamingOutboundSpeed () const { return m_StreamingOutboundSpeed; } int GetStreamingInboundSpeed () const { return m_StreamingInboundSpeed; } - int GetStreamingMaxConcurrentStreams () const { return m_StreamingMaxConcurrentStreams; } bool IsStreamingAnswerPings () const { return m_IsStreamingAnswerPings; } // datagram @@ -275,28 +276,24 @@ namespace client i2p::datagram::DatagramDestination * CreateDatagramDestination (bool gzip = true); // implements LocalDestination - bool Decrypt (const uint8_t * encrypted, uint8_t * data, i2p::data::CryptoKeyType preferredCrypto) const override; - std::shared_ptr GetIdentity () const override { return m_Keys.GetPublic (); }; - bool SupportsEncryptionType (i2p::data::CryptoKeyType keyType) const override; - const uint8_t * GetEncryptionPublicKey (i2p::data::CryptoKeyType keyType) const override; + bool Decrypt (const uint8_t * encrypted, uint8_t * data, i2p::data::CryptoKeyType preferredCrypto) const; + std::shared_ptr GetIdentity () const { return m_Keys.GetPublic (); }; + bool SupportsEncryptionType (i2p::data::CryptoKeyType keyType) const; + const uint8_t * GetEncryptionPublicKey (i2p::data::CryptoKeyType keyType) const; protected: - // GarlicDestionation - i2p::data::CryptoKeyType GetRatchetsHighestCryptoType () const override; - // LeaseSetDestination - void CleanupDestination () override; - i2p::data::CryptoKeyType GetPreferredCryptoType () const override { return m_PreferredCryptoType; } + void CleanupDestination (); // I2CP - void HandleDataMessage (const uint8_t * buf, size_t len) override; - void CreateNewLeaseSet (const std::vector >& tunnels) override; - + void HandleDataMessage (const uint8_t * buf, size_t len); + void CreateNewLeaseSet (const std::vector >& tunnels); + private: std::shared_ptr GetSharedFromThis () { return std::static_pointer_cast(shared_from_this ()); } - void PersistTemporaryKeys (std::shared_ptr keys); + void PersistTemporaryKeys (EncryptionKey * keys); void ReadAuthKey (const std::string& group, const std::map * params); template @@ -305,10 +302,12 @@ namespace client private: i2p::data::PrivateKeys m_Keys; - std::map > m_EncryptionKeys; // last is most preferable - i2p::data::CryptoKeyType m_PreferredCryptoType; - - int m_StreamingAckDelay,m_StreamingOutboundSpeed, m_StreamingInboundSpeed, m_StreamingMaxConcurrentStreams; + std::unique_ptr m_StandardEncryptionKey; + std::unique_ptr m_ECIESx25519EncryptionKey; + + int m_StreamingAckDelay; + int m_StreamingOutboundSpeed; + int m_StreamingInboundSpeed; bool m_IsStreamingAnswerPings; std::shared_ptr m_StreamingDestination; // default std::map > m_StreamingDestinationsByPorts; diff --git a/libi2pd/ECIESX25519AEADRatchetSession.cpp b/libi2pd/ECIESX25519AEADRatchetSession.cpp index 08af4be3..138d21e9 100644 --- a/libi2pd/ECIESX25519AEADRatchetSession.cpp +++ b/libi2pd/ECIESX25519AEADRatchetSession.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2025, The PurpleI2P Project +* Copyright (c) 2013-2024, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -11,7 +11,6 @@ #include "Log.h" #include "util.h" #include "Crypto.h" -#include "PostQuantum.h" #include "Elligator.h" #include "Tag.h" #include "I2PEndian.h" @@ -95,17 +94,6 @@ namespace garlic m_ItermediateSymmKeys.erase (index); } - ReceiveRatchetTagSet::ReceiveRatchetTagSet (std::shared_ptr session, bool isNS): - m_Session (session), m_IsNS (isNS) - { - } - - ReceiveRatchetTagSet::~ReceiveRatchetTagSet () - { - if (m_IsNS && m_Session) - m_Session->CleanupReceiveNSRKeys (); - } - void ReceiveRatchetTagSet::Expire () { if (!m_ExpirationTimestamp) @@ -174,12 +162,12 @@ namespace garlic return false; } if (m_Destination) - m_Destination->HandleECIESx25519GarlicClove (buf + offset, size, nullptr); + m_Destination->HandleECIESx25519GarlicClove (buf + offset, size); return true; } ECIESX25519AEADRatchetSession::ECIESX25519AEADRatchetSession (GarlicDestination * owner, bool attachLeaseSetNS): - GarlicRoutingSession (owner, true), m_RemoteStaticKeyType (0) + GarlicRoutingSession (owner, true) { if (!attachLeaseSetNS) SetLeaseSetUpdateStatus (eLeaseSetUpToDate); RAND_bytes (m_PaddingSizes, 32); m_NextPaddingSize = 0; @@ -263,82 +251,34 @@ namespace garlic } return false; } - - void ECIESX25519AEADRatchetSession::CleanupReceiveNSRKeys () - { - m_EphemeralKeys = nullptr; -#if OPENSSL_PQ - m_PQKeys = nullptr; -#endif - } bool ECIESX25519AEADRatchetSession::HandleNewIncomingSession (const uint8_t * buf, size_t len) { if (!GetOwner ()) return false; // we are Bob // KDF1 - + i2p::crypto::InitNoiseIKState (GetNoiseState (), GetOwner ()->GetEncryptionPublicKey (i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD)); // bpk + if (!i2p::crypto::GetElligator ()->Decode (buf, m_Aepk)) { LogPrint (eLogError, "Garlic: Can't decode elligator"); return false; } buf += 32; len -= 32; + MixHash (m_Aepk, 32); // h = SHA256(h || aepk) uint8_t sharedSecret[32]; - bool decrypted = false; - auto cryptoType = GetOwner ()->GetRatchetsHighestCryptoType (); -#if OPENSSL_PQ - if (cryptoType > i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD) // we support post quantum + if (!GetOwner ()->Decrypt (m_Aepk, sharedSecret, i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD)) // x25519(bsk, aepk) { - i2p::crypto::InitNoiseIKStateMLKEM (GetNoiseState (), cryptoType, GetOwner ()->GetEncryptionPublicKey (cryptoType)); // bpk - MixHash (m_Aepk, 32); // h = SHA256(h || aepk) - - if (GetOwner ()->Decrypt (m_Aepk, sharedSecret, cryptoType)) // x25519(bsk, aepk) - { - MixKey (sharedSecret); - - auto keyLen = i2p::crypto::GetMLKEMPublicKeyLen (cryptoType); - std::vector encapsKey(keyLen); - if (Decrypt (buf, encapsKey.data (), keyLen)) - { - decrypted = true; // encaps section has right hash - MixHash (buf, keyLen + 16); - buf += keyLen + 16; - len -= keyLen + 16; - - m_PQKeys = i2p::crypto::CreateMLKEMKeys (cryptoType); - m_PQKeys->SetPublicKey (encapsKey.data ()); - } - } - } -#endif - if (!decrypted) - { - if (cryptoType == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD || - GetOwner ()->SupportsEncryptionType (i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD)) - { - cryptoType = i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD; - i2p::crypto::InitNoiseIKState (GetNoiseState (), GetOwner ()->GetEncryptionPublicKey (i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD)); // bpk - MixHash (m_Aepk, 32); // h = SHA256(h || aepk) - - if (!GetOwner ()->Decrypt (m_Aepk, sharedSecret, i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD)) // x25519(bsk, aepk) - { - LogPrint (eLogWarning, "Garlic: Incorrect Alice ephemeral key"); - return false; - } - MixKey (sharedSecret); - } - else - { - LogPrint (eLogWarning, "Garlic: No supported encryption type"); - return false; - } - } + LogPrint (eLogWarning, "Garlic: Incorrect Alice ephemeral key"); + return false; + } + MixKey (sharedSecret); // decrypt flags/static - uint8_t fs[32]; - if (!Decrypt (buf, fs, 32)) + uint8_t nonce[12], fs[32]; + CreateNonce (0, nonce); + if (!i2p::crypto::AEADChaCha20Poly1305 (buf, 32, m_H, 32, m_CK + 32, nonce, fs, 32, false)) // decrypt { LogPrint (eLogWarning, "Garlic: Flags/static section AEAD verification failed "); return false; @@ -350,19 +290,21 @@ namespace garlic bool isStatic = !i2p::data::Tag<32> (fs).IsZero (); if (isStatic) { - // static key, fs is apk - SetRemoteStaticKey (cryptoType, fs); - if (!GetOwner ()->Decrypt (fs, sharedSecret, m_RemoteStaticKeyType)) // x25519(bsk, apk) + // static key, fs is apk + memcpy (m_RemoteStaticKey, fs, 32); + if (!GetOwner ()->Decrypt (fs, sharedSecret, i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD)) // x25519(bsk, apk) { LogPrint (eLogWarning, "Garlic: Incorrect Alice static key"); return false; } MixKey (sharedSecret); } + else // all zeros flags + CreateNonce (1, nonce); // decrypt payload std::vector payload (len - 16); // we must save original ciphertext - if (!Decrypt (buf, payload.data (), len - 16)) + if (!i2p::crypto::AEADChaCha20Poly1305 (buf, len - 16, m_H, 32, m_CK + 32, nonce, payload.data (), len - 16, false)) // decrypt { LogPrint (eLogWarning, "Garlic: Payload section AEAD verification failed"); return false; @@ -398,7 +340,7 @@ namespace garlic { case eECIESx25519BlkGalicClove: if (GetOwner ()) - GetOwner ()->HandleECIESx25519GarlicClove (buf + offset, size, this); + GetOwner ()->HandleECIESx25519GarlicClove (buf + offset, size); break; case eECIESx25519BlkNextKey: LogPrint (eLogDebug, "Garlic: Next key"); @@ -550,16 +492,7 @@ namespace garlic offset += 32; // KDF1 -#if OPENSSL_PQ - if (m_RemoteStaticKeyType >= i2p::data::CRYPTO_KEY_TYPE_ECIES_MLKEM512_X25519_AEAD) - { - i2p::crypto::InitNoiseIKStateMLKEM (GetNoiseState (), m_RemoteStaticKeyType, m_RemoteStaticKey); // bpk - m_PQKeys = i2p::crypto::CreateMLKEMKeys (m_RemoteStaticKeyType); - m_PQKeys->GenerateKeys (); - } - else -#endif - i2p::crypto::InitNoiseIKState (GetNoiseState (), m_RemoteStaticKey); // bpk + i2p::crypto::InitNoiseIKState (GetNoiseState (), m_RemoteStaticKey); // bpk MixHash (m_EphemeralKeys->GetPublicKey (), 32); // h = SHA256(h || aepk) uint8_t sharedSecret[32]; if (!m_EphemeralKeys->Agree (m_RemoteStaticKey, sharedSecret)) // x25519(aesk, bpk) @@ -568,32 +501,18 @@ namespace garlic return false; } MixKey (sharedSecret); -#if OPENSSL_PQ - if (m_RemoteStaticKeyType >= i2p::data::CRYPTO_KEY_TYPE_ECIES_MLKEM512_X25519_AEAD) - { - auto keyLen = i2p::crypto::GetMLKEMPublicKeyLen (m_RemoteStaticKeyType); - std::vector encapsKey(keyLen); - m_PQKeys->GetPublicKey (encapsKey.data ()); - // encrypt encapsKey - if (!Encrypt (encapsKey.data (), out + offset, keyLen)) - { - LogPrint (eLogWarning, "Garlic: ML-KEM encap_key section AEAD encryption failed "); - return false; - } - MixHash (out + offset, keyLen + 16); // h = SHA256(h || ciphertext) - offset += keyLen + 16; - } -#endif // encrypt flags/static key section + uint8_t nonce[12]; + CreateNonce (0, nonce); const uint8_t * fs; if (isStatic) - fs = GetOwner ()->GetEncryptionPublicKey (m_RemoteStaticKeyType); + fs = GetOwner ()->GetEncryptionPublicKey (i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD); else { memset (out + offset, 0, 32); // all zeros flags section fs = out + offset; } - if (!Encrypt (fs, out + offset, 32)) + if (!i2p::crypto::AEADChaCha20Poly1305 (fs, 32, m_H, 32, m_CK + 32, nonce, out + offset, 48, true)) // encrypt { LogPrint (eLogWarning, "Garlic: Flags/static section AEAD encryption failed "); return false; @@ -604,11 +523,13 @@ namespace garlic // KDF2 if (isStatic) { - GetOwner ()->Decrypt (m_RemoteStaticKey, sharedSecret, m_RemoteStaticKeyType); // x25519 (ask, bpk) - MixKey (sharedSecret); + GetOwner ()->Decrypt (m_RemoteStaticKey, sharedSecret, i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD); // x25519 (ask, bpk) + MixKey (sharedSecret); } + else + CreateNonce (1, nonce); // encrypt payload - if (!Encrypt (payload, out + offset, len)) + if (!i2p::crypto::AEADChaCha20Poly1305 (payload, len, m_H, 32, m_CK + 32, nonce, out + offset, len + 16, true)) // encrypt { LogPrint (eLogWarning, "Garlic: Payload section AEAD encryption failed"); return false; @@ -646,7 +567,7 @@ namespace garlic } memcpy (m_NSREncodedKey, out + offset, 32); // for possible next NSR memcpy (m_NSRH, m_H, 32); - offset += 32; + offset += 32; // KDF for Reply Key Section MixHash ((const uint8_t *)&tag, 8); // h = SHA256(h || tag) MixHash (m_EphemeralKeys->GetPublicKey (), 32); // h = SHA256(h || bepk) @@ -657,33 +578,16 @@ namespace garlic return false; } MixKey (sharedSecret); -#if OPENSSL_PQ - if (m_PQKeys) - { - size_t cipherTextLen = i2p::crypto::GetMLKEMCipherTextLen (m_RemoteStaticKeyType); - std::vector kemCiphertext(cipherTextLen); - m_PQKeys->Encaps (kemCiphertext.data (), sharedSecret); - - if (!Encrypt (kemCiphertext.data (), out + offset, cipherTextLen)) - { - LogPrint (eLogWarning, "Garlic: NSR ML-KEM ciphertext section AEAD encryption failed"); - return false; - } - m_NSREncodedPQKey = std::make_unique > (cipherTextLen + 16); - memcpy (m_NSREncodedPQKey->data (), out + offset, cipherTextLen + 16); - MixHash (out + offset, cipherTextLen + 16); - MixKey (sharedSecret); - offset += cipherTextLen + 16; - } -#endif if (!m_EphemeralKeys->Agree (m_RemoteStaticKey, sharedSecret)) // sharedSecret = x25519(besk, apk) { LogPrint (eLogWarning, "Garlic: Incorrect Alice static key"); return false; } MixKey (sharedSecret); + uint8_t nonce[12]; + CreateNonce (0, nonce); // calculate hash for zero length - if (!Encrypt (sharedSecret /* can be anything */, out + offset, 0)) // encrypt, ciphertext = ENCRYPT(k, n, ZEROLEN, ad) + if (!i2p::crypto::AEADChaCha20Poly1305 (nonce /* can be anything */, 0, m_H, 32, m_CK + 32, nonce, out + offset, 16, true)) // encrypt, ciphertext = ENCRYPT(k, n, ZEROLEN, ad) { LogPrint (eLogWarning, "Garlic: Reply key section AEAD encryption failed"); return false; @@ -704,7 +608,6 @@ namespace garlic GetOwner ()->GetNumRatchetInboundTags () : ECIESX25519_MIN_NUM_GENERATED_TAGS); i2p::crypto::HKDF (keydata + 32, nullptr, 0, "AttachPayloadKDF", m_NSRKey, 32); // k = HKDF(k_ba, ZEROLEN, "AttachPayloadKDF", 32) // encrypt payload - uint8_t nonce[12]; memset (nonce, 0, 12); // seqn = 0 if (!i2p::crypto::AEADChaCha20Poly1305 (payload, len, m_H, 32, m_NSRKey, nonce, out + offset, len + 16, true)) // encrypt { LogPrint (eLogWarning, "Garlic: NSR payload section AEAD encryption failed"); @@ -726,34 +629,16 @@ namespace garlic memcpy (m_H, m_NSRH, 32); MixHash ((const uint8_t *)&tag, 8); // h = SHA256(h || tag) MixHash (m_EphemeralKeys->GetPublicKey (), 32); // h = SHA256(h || bepk) - m_N = 0; - size_t offset = 40; -#if OPENSSL_PQ - if (m_PQKeys) - { - if (m_NSREncodedPQKey) - { - size_t cipherTextLen = i2p::crypto::GetMLKEMCipherTextLen (m_RemoteStaticKeyType); - memcpy (out + offset, m_NSREncodedPQKey->data (), cipherTextLen + 16); - MixHash (out + offset, cipherTextLen + 16); - offset += cipherTextLen + 16; - } - else - { - LogPrint (eLogWarning, "Garlic: No stored ML-KEM keys"); - return false; - } - } -#endif - if (!Encrypt (m_NSRH /* can be anything */, out + offset, 0)) // encrypt, ciphertext = ENCRYPT(k, n, ZEROLEN, ad) + uint8_t nonce[12]; + CreateNonce (0, nonce); + if (!i2p::crypto::AEADChaCha20Poly1305 (nonce /* can be anything */, 0, m_H, 32, m_CK + 32, nonce, out + 40, 16, true)) // encrypt, ciphertext = ENCRYPT(k, n, ZEROLEN, ad) { LogPrint (eLogWarning, "Garlic: Reply key section AEAD encryption failed"); return false; } - MixHash (out + offset, 16); // h = SHA256(h || ciphertext) + MixHash (out + 40, 16); // h = SHA256(h || ciphertext) // encrypt payload - uint8_t nonce[12]; memset (nonce, 0, 12); - if (!i2p::crypto::AEADChaCha20Poly1305 (payload, len, m_H, 32, m_NSRKey, nonce, out + offset + 16, len + 16, true)) // encrypt + if (!i2p::crypto::AEADChaCha20Poly1305 (payload, len, m_H, 32, m_NSRKey, nonce, out + 56, len + 16, true)) // encrypt { LogPrint (eLogWarning, "Garlic: Next NSR payload section AEAD encryption failed"); return false; @@ -785,30 +670,13 @@ namespace garlic return false; } MixKey (sharedSecret); -#if OPENSSL_PQ - if (m_RemoteStaticKeyType >= i2p::data::CRYPTO_KEY_TYPE_ECIES_MLKEM512_X25519_AEAD) - { - // decrypt kem_ciphertext section - size_t cipherTextLen = i2p::crypto::GetMLKEMCipherTextLen (m_RemoteStaticKeyType); - std::vector kemCiphertext(cipherTextLen); - if (!Decrypt (buf, kemCiphertext.data (), cipherTextLen)) - { - LogPrint (eLogWarning, "Garlic: Reply ML-KEM ciphertext section AEAD decryption failed"); - return false; - } - MixHash (buf, cipherTextLen + 16); - buf += cipherTextLen + 16; - len -= cipherTextLen + 16; - // decaps - m_PQKeys->Decaps (kemCiphertext.data (), sharedSecret); - MixKey (sharedSecret); - } -#endif - GetOwner ()->Decrypt (bepk, sharedSecret, m_RemoteStaticKeyType); // x25519 (ask, bepk) + GetOwner ()->Decrypt (bepk, sharedSecret, i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD); // x25519 (ask, bepk) MixKey (sharedSecret); - + + uint8_t nonce[12]; + CreateNonce (0, nonce); // calculate hash for zero length - if (!Decrypt (buf, sharedSecret/* can be anything */, 0)) // decrypt, DECRYPT(k, n, ZEROLEN, ad) verification only + if (!i2p::crypto::AEADChaCha20Poly1305 (buf, 0, m_H, 32, m_CK + 32, nonce, sharedSecret/* can be anything */, 0, false)) // decrypt, DECRYPT(k, n, ZEROLEN, ad) verification only { LogPrint (eLogWarning, "Garlic: Reply key section AEAD decryption failed"); return false; @@ -833,7 +701,6 @@ namespace garlic } i2p::crypto::HKDF (keydata + 32, nullptr, 0, "AttachPayloadKDF", keydata, 32); // k = HKDF(k_ba, ZEROLEN, "AttachPayloadKDF", 32) // decrypt payload - uint8_t nonce[12]; memset (nonce, 0, 12); // seqn = 0 if (!i2p::crypto::AEADChaCha20Poly1305 (buf, len - 16, m_H, 32, keydata, nonce, buf, len - 16, false)) // decrypt { LogPrint (eLogWarning, "Garlic: Payload section AEAD decryption failed"); @@ -843,8 +710,7 @@ namespace garlic if (m_State == eSessionStateNewSessionSent) { m_State = eSessionStateEstablished; - // don't delete m_EpehemralKey and m_PQKeys because delayd NSR's migth come - // done in CleanupReceiveNSRKeys called from NSR tagset destructor + //m_EphemeralKeys = nullptr; // TODO: delete after a while m_SessionCreatedTimestamp = i2p::util::GetSecondsSinceEpoch (); GetOwner ()->AddECIESx25519Session (m_RemoteStaticKey, shared_from_this ()); } @@ -859,8 +725,6 @@ namespace garlic bool ECIESX25519AEADRatchetSession::NewExistingSessionMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen) { - auto owner = GetOwner (); - if (!owner) return false; uint8_t nonce[12]; auto index = m_SendTagset->GetNextIndex (); CreateNonce (index, nonce); // tag's index @@ -868,7 +732,8 @@ namespace garlic if (!tag) { LogPrint (eLogError, "Garlic: Can't create new ECIES-X25519-AEAD-Ratchet tag for send tagset"); - owner->RemoveECIESx25519Session (m_RemoteStaticKey); + if (GetOwner ()) + GetOwner ()->RemoveECIESx25519Session (m_RemoteStaticKey); return false; } memcpy (out, &tag, 8); @@ -876,7 +741,7 @@ namespace garlic // ciphertext = ENCRYPT(k, n, payload, ad) uint8_t key[32]; m_SendTagset->GetSymmKey (index, key); - if (!owner->AEADChaCha20Poly1305Encrypt (payload, len, out, 8, key, nonce, out + 8, outLen - 8)) + if (!i2p::crypto::AEADChaCha20Poly1305 (payload, len, out, 8, key, nonce, out + 8, outLen - 8, true)) // encrypt { LogPrint (eLogWarning, "Garlic: Payload section AEAD encryption failed"); return false; @@ -895,35 +760,34 @@ namespace garlic uint8_t * payload = buf + 8; uint8_t key[32]; receiveTagset->GetSymmKey (index, key); - auto owner = GetOwner (); - if (!owner) return true; // drop message - - if (!owner->AEADChaCha20Poly1305Decrypt (payload, len - 16, buf, 8, key, nonce, payload, len - 16)) + if (!i2p::crypto::AEADChaCha20Poly1305 (payload, len - 16, buf, 8, key, nonce, payload, len - 16, false)) // decrypt { LogPrint (eLogWarning, "Garlic: Payload section AEAD decryption failed"); return false; } HandlePayload (payload, len - 16, receiveTagset, index); - - int moreTags = 0; - if (owner->GetNumRatchetInboundTags () > 0) // override in settings? + if (GetOwner ()) { - if (receiveTagset->GetNextIndex () - index < owner->GetNumRatchetInboundTags ()/2) - moreTags = owner->GetNumRatchetInboundTags (); - index -= owner->GetNumRatchetInboundTags (); // trim behind + int moreTags = 0; + if (GetOwner ()->GetNumRatchetInboundTags () > 0) // override in settings? + { + if (receiveTagset->GetNextIndex () - index < GetOwner ()->GetNumRatchetInboundTags ()/2) + moreTags = GetOwner ()->GetNumRatchetInboundTags (); + index -= GetOwner ()->GetNumRatchetInboundTags (); // trim behind + } + else + { + moreTags = (receiveTagset->GetTagSetID () > 0) ? ECIESX25519_MAX_NUM_GENERATED_TAGS : // for non first tagset + (ECIESX25519_MIN_NUM_GENERATED_TAGS + (index >> 1)); // N/2 + if (moreTags > ECIESX25519_MAX_NUM_GENERATED_TAGS) moreTags = ECIESX25519_MAX_NUM_GENERATED_TAGS; + moreTags -= (receiveTagset->GetNextIndex () - index); + index -= ECIESX25519_MAX_NUM_GENERATED_TAGS; // trim behind + } + if (moreTags > 0) + GenerateMoreReceiveTags (receiveTagset, moreTags); + if (index > 0) + receiveTagset->SetTrimBehind (index); } - else - { - moreTags = (receiveTagset->GetTagSetID () > 0) ? ECIESX25519_MAX_NUM_GENERATED_TAGS : // for non first tagset - (ECIESX25519_MIN_NUM_GENERATED_TAGS + (index >> 1)); // N/2 - if (moreTags > ECIESX25519_MAX_NUM_GENERATED_TAGS) moreTags = ECIESX25519_MAX_NUM_GENERATED_TAGS; - moreTags -= (receiveTagset->GetNextIndex () - index); - index -= ECIESX25519_MAX_NUM_GENERATED_TAGS; // trim behind - } - if (moreTags > 0) - GenerateMoreReceiveTags (receiveTagset, moreTags); - if (index > 0) - receiveTagset->SetTrimBehind (index); return true; } @@ -937,10 +801,6 @@ namespace garlic m_State = eSessionStateEstablished; m_NSRSendTagset = nullptr; m_EphemeralKeys = nullptr; -#if OPENSSL_PQ - m_PQKeys = nullptr; - m_NSREncodedPQKey = nullptr; -#endif [[fallthrough]]; case eSessionStateEstablished: if (m_SendReverseKey && receiveTagset->GetTagSetID () == m_NextReceiveRatchet->GetReceiveTagSetID ()) @@ -971,12 +831,7 @@ namespace garlic if (!payload) return nullptr; size_t len = CreatePayload (msg, m_State != eSessionStateEstablished, payload); if (!len) return nullptr; -#if OPENSSL_PQ - auto m = NewI2NPMessage (len + (m_State == eSessionStateEstablished ? 28 : - i2p::crypto::GetMLKEMPublicKeyLen (m_RemoteStaticKeyType) + 116)); -#else auto m = NewI2NPMessage (len + 100); // 96 + 4 -#endif m->Align (12); // in order to get buf aligned to 16 (12 + 4) uint8_t * buf = m->GetPayload () + 4; // 4 bytes for length @@ -991,28 +846,16 @@ namespace garlic if (!NewOutgoingSessionMessage (payload, len, buf, m->maxLen)) return nullptr; len += 96; -#if OPENSSL_PQ - if (m_RemoteStaticKeyType >= i2p::data::CRYPTO_KEY_TYPE_ECIES_MLKEM512_X25519_AEAD) - len += i2p::crypto::GetMLKEMPublicKeyLen (m_RemoteStaticKeyType) + 16; -#endif break; case eSessionStateNewSessionReceived: if (!NewSessionReplyMessage (payload, len, buf, m->maxLen)) return nullptr; len += 72; -#if OPENSSL_PQ - if (m_RemoteStaticKeyType >= i2p::data::CRYPTO_KEY_TYPE_ECIES_MLKEM512_X25519_AEAD) - len += i2p::crypto::GetMLKEMCipherTextLen (m_RemoteStaticKeyType) + 16; -#endif break; case eSessionStateNewSessionReplySent: if (!NextNewSessionReplyMessage (payload, len, buf, m->maxLen)) return nullptr; len += 72; -#if OPENSSL_PQ - if (m_RemoteStaticKeyType >= i2p::data::CRYPTO_KEY_TYPE_ECIES_MLKEM512_X25519_AEAD) - len += i2p::crypto::GetMLKEMCipherTextLen (m_RemoteStaticKeyType) + 16; -#endif break; case eSessionStateOneTime: if (!NewOutgoingSessionMessage (payload, len, buf, m->maxLen, false)) @@ -1068,7 +911,7 @@ namespace garlic } } if (!sendAckRequest && !first && - ((!m_AckRequestMsgID && ts > m_LastAckRequestSendTime + m_AckRequestInterval) || // regular request + ((!m_AckRequestMsgID && ts > m_LastAckRequestSendTime + ECIESX25519_ACK_REQUEST_INTERVAL) || // regular request (m_AckRequestMsgID && ts > m_LastAckRequestSendTime + LEASESET_CONFIRMATION_TIMEOUT))) // previous request failed. try again { // not LeaseSet @@ -1269,8 +1112,6 @@ namespace garlic bool ECIESX25519AEADRatchetSession::CheckExpired (uint64_t ts) { CleanupUnconfirmedLeaseSet (ts); - if (!m_Destination && ts > m_LastActivityTimestamp + ECIESX25519_SESSION_CREATE_TIMEOUT) return true; // m_LastActivityTimestamp is NS receive time - if (m_State != eSessionStateEstablished && m_SessionCreatedTimestamp && ts > m_SessionCreatedTimestamp + ECIESX25519_SESSION_ESTABLISH_TIMEOUT) return true; return ts > m_LastActivityTimestamp + ECIESX25519_RECEIVE_EXPIRATION_TIMEOUT && // seconds ts*1000 > m_LastSentTimestamp + ECIESX25519_SEND_EXPIRATION_TIMEOUT*1000; // milliseconds } diff --git a/libi2pd/ECIESX25519AEADRatchetSession.h b/libi2pd/ECIESX25519AEADRatchetSession.h index fd9cc45d..bcc07b30 100644 --- a/libi2pd/ECIESX25519AEADRatchetSession.h +++ b/libi2pd/ECIESX25519AEADRatchetSession.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2025, The PurpleI2P Project +* Copyright (c) 2013-2024, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -14,12 +14,10 @@ #include #include #include -#include #include #include #include "Identity.h" #include "Crypto.h" -#include "PostQuantum.h" #include "Garlic.h" #include "Tag.h" @@ -32,10 +30,8 @@ namespace garlic const int ECIESX25519_SEND_INACTIVITY_TIMEOUT = 5000; // number of milliseconds we can send empty(pyaload only) packet after const int ECIESX25519_SEND_EXPIRATION_TIMEOUT = 480; // in seconds const int ECIESX25519_RECEIVE_EXPIRATION_TIMEOUT = 600; // in seconds - const int ECIESX25519_SESSION_CREATE_TIMEOUT = 3; // in seconds, NSR must be send after NS received - const int ECIESX25519_SESSION_ESTABLISH_TIMEOUT = 15; // in seconds const int ECIESX25519_PREVIOUS_TAGSET_EXPIRATION_TIMEOUT = 180; // in seconds - const int ECIESX25519_DEFAULT_ACK_REQUEST_INTERVAL = 33000; // in milliseconds + const int ECIESX25519_ACK_REQUEST_INTERVAL = 33000; // in milliseconds const int ECIESX25519_ACK_REQUEST_MAX_NUM_ATTEMPTS = 3; const int ECIESX25519_TAGSET_MAX_NUM_TAGS = 8192; // number of tags we request new tagset after const int ECIESX25519_MIN_NUM_GENERATED_TAGS = 24; @@ -81,8 +77,8 @@ namespace garlic { public: - ReceiveRatchetTagSet (std::shared_ptr session, bool isNS = false); - ~ReceiveRatchetTagSet () override; + ReceiveRatchetTagSet (std::shared_ptr session, bool isNS = false): + m_Session (session), m_IsNS (isNS) {}; bool IsNS () const { return m_IsNS; }; std::shared_ptr GetSession () { return m_Session; }; @@ -166,32 +162,27 @@ namespace garlic ~ECIESX25519AEADRatchetSession (); bool HandleNextMessage (uint8_t * buf, size_t len, std::shared_ptr receiveTagset, int index = 0); - std::shared_ptr WrapSingleMessage (std::shared_ptr msg) override; + std::shared_ptr WrapSingleMessage (std::shared_ptr msg); std::shared_ptr WrapOneTimeMessage (std::shared_ptr msg); const uint8_t * GetRemoteStaticKey () const { return m_RemoteStaticKey; } - i2p::data::CryptoKeyType GetRemoteStaticKeyType () const { return m_RemoteStaticKeyType; } - void SetRemoteStaticKey (i2p::data::CryptoKeyType keyType, const uint8_t * key) - { - m_RemoteStaticKeyType = keyType; - memcpy (m_RemoteStaticKey, key, 32); - } + void SetRemoteStaticKey (const uint8_t * key) { memcpy (m_RemoteStaticKey, key, 32); } + void Terminate () { m_IsTerminated = true; } - void SetDestination (const i2p::data::IdentHash& dest) + void SetDestination (const i2p::data::IdentHash& dest) // TODO: { if (!m_Destination) m_Destination.reset (new i2p::data::IdentHash (dest)); } + bool CheckExpired (uint64_t ts); // true is expired bool CanBeRestarted (uint64_t ts) const { return ts > m_SessionCreatedTimestamp + ECIESX25519_RESTART_TIMEOUT; } bool IsInactive (uint64_t ts) const { return ts > m_LastActivityTimestamp + ECIESX25519_INACTIVITY_TIMEOUT && CanBeRestarted (ts); } - void CleanupReceiveNSRKeys (); // called from ReceiveRatchetTagSet at Alice's side - - bool IsRatchets () const override { return true; }; - bool IsReadyToSend () const override { return m_State != eSessionStateNewSessionSent; }; - bool IsTerminated () const override { return m_IsTerminated; } - uint64_t GetLastActivityTimestamp () const override { return m_LastActivityTimestamp; }; - void SetAckRequestInterval (int interval) override { m_AckRequestInterval = interval; }; - bool CleanupUnconfirmedTags () override; // return true if unaswered Ack requests, called from I2CP + + bool IsRatchets () const { return true; }; + bool IsReadyToSend () const { return m_State != eSessionStateNewSessionSent; }; + bool IsTerminated () const { return m_IsTerminated; } + uint64_t GetLastActivityTimestamp () const { return m_LastActivityTimestamp; }; + bool CleanupUnconfirmedTags (); // return true if unaswered Ack requests, called from I2CP protected: @@ -199,7 +190,7 @@ namespace garlic void SetNoiseState (const i2p::crypto::NoiseSymmetricState& state) { GetNoiseState () = state; }; void CreateNonce (uint64_t seqn, uint8_t * nonce); void HandlePayload (const uint8_t * buf, size_t len, const std::shared_ptr& receiveTagset, int index); - bool MessageConfirmed (uint32_t msgID) override; + bool MessageConfirmed (uint32_t msgID); private: @@ -225,20 +216,15 @@ namespace garlic private: - i2p::data::CryptoKeyType m_RemoteStaticKeyType; uint8_t m_RemoteStaticKey[32]; uint8_t m_Aepk[32]; // Alice's ephemeral keys, for incoming only uint8_t m_NSREncodedKey[32], m_NSRH[32], m_NSRKey[32]; // new session reply, for incoming only std::shared_ptr m_EphemeralKeys; -#if OPENSSL_PQ - std::unique_ptr m_PQKeys; - std::unique_ptr > m_NSREncodedPQKey; -#endif SessionState m_State = eSessionStateNew; uint64_t m_SessionCreatedTimestamp = 0, m_LastActivityTimestamp = 0, // incoming (in seconds) m_LastSentTimestamp = 0; // in milliseconds std::shared_ptr m_SendTagset, m_NSRSendTagset; - std::unique_ptr m_Destination;// must be set for NS if outgoing and NSR if incoming + std::unique_ptr m_Destination;// TODO: might not need it std::list > m_AckRequests; // incoming (tagsetid, index) bool m_SendReverseKey = false, m_SendForwardKey = false, m_IsTerminated = false; std::unique_ptr m_NextReceiveRatchet, m_NextSendRatchet; @@ -247,7 +233,6 @@ namespace garlic uint64_t m_LastAckRequestSendTime = 0; // milliseconds uint32_t m_AckRequestMsgID = 0; int m_AckRequestNumAttempts = 0; - int m_AckRequestInterval = ECIESX25519_DEFAULT_ACK_REQUEST_INTERVAL; // milliseconds public: diff --git a/libi2pd/Ed25519.cpp b/libi2pd/Ed25519.cpp index 47edb755..3e0795d5 100644 --- a/libi2pd/Ed25519.cpp +++ b/libi2pd/Ed25519.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2024, The PurpleI2P Project +* Copyright (c) 2013-2023, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -457,6 +457,86 @@ namespace crypto } } +#if !OPENSSL_X25519 + BIGNUM * Ed25519::ScalarMul (const BIGNUM * u, const BIGNUM * k, BN_CTX * ctx) const + { + BN_CTX_start (ctx); + auto x1 = BN_CTX_get (ctx); BN_copy (x1, u); + auto x2 = BN_CTX_get (ctx); BN_one (x2); + auto z2 = BN_CTX_get (ctx); BN_zero (z2); + auto x3 = BN_CTX_get (ctx); BN_copy (x3, u); + auto z3 = BN_CTX_get (ctx); BN_one (z3); + auto c121666 = BN_CTX_get (ctx); BN_set_word (c121666, 121666); + auto tmp0 = BN_CTX_get (ctx); auto tmp1 = BN_CTX_get (ctx); + unsigned int swap = 0; + auto bits = BN_num_bits (k); + while(bits) + { + --bits; + auto k_t = BN_is_bit_set(k, bits) ? 1 : 0; + swap ^= k_t; + if (swap) + { + std::swap (x2, x3); + std::swap (z2, z3); + } + swap = k_t; + BN_mod_sub(tmp0, x3, z3, q, ctx); + BN_mod_sub(tmp1, x2, z2, q, ctx); + BN_mod_add(x2, x2, z2, q, ctx); + BN_mod_add(z2, x3, z3, q, ctx); + BN_mod_mul(z3, tmp0, x2, q, ctx); + BN_mod_mul(z2, z2, tmp1, q, ctx); + BN_mod_sqr(tmp0, tmp1, q, ctx); + BN_mod_sqr(tmp1, x2, q, ctx); + BN_mod_add(x3, z3, z2, q, ctx); + BN_mod_sub(z2, z3, z2, q, ctx); + BN_mod_mul(x2, tmp1, tmp0, q, ctx); + BN_mod_sub(tmp1, tmp1, tmp0, q, ctx); + BN_mod_sqr(z2, z2, q, ctx); + BN_mod_mul(z3, tmp1, c121666, q, ctx); + BN_mod_sqr(x3, x3, q, ctx); + BN_mod_add(tmp0, tmp0, z3, q, ctx); + BN_mod_mul(z3, x1, z2, q, ctx); + BN_mod_mul(z2, tmp1, tmp0, q, ctx); + } + if (swap) + { + std::swap (x2, x3); + std::swap (z2, z3); + } + BN_mod_inverse (z2, z2, q, ctx); + BIGNUM * res = BN_new (); // not from ctx + BN_mod_mul(res, x2, z2, q, ctx); + BN_CTX_end (ctx); + return res; + } + + void Ed25519::ScalarMul (const uint8_t * p, const uint8_t * e, uint8_t * buf, BN_CTX * ctx) const + { + BIGNUM * p1 = DecodeBN<32> (p); + uint8_t k[32]; + memcpy (k, e, 32); + k[0] &= 248; k[31] &= 127; k[31] |= 64; + BIGNUM * n = DecodeBN<32> (k); + BIGNUM * q1 = ScalarMul (p1, n, ctx); + EncodeBN (q1, buf, 32); + BN_free (p1); BN_free (n); BN_free (q1); + } + + void Ed25519::ScalarMulB (const uint8_t * e, uint8_t * buf, BN_CTX * ctx) const + { + BIGNUM *p1 = BN_new (); BN_set_word (p1, 9); + uint8_t k[32]; + memcpy (k, e, 32); + k[0] &= 248; k[31] &= 127; k[31] |= 64; + BIGNUM * n = DecodeBN<32> (k); + BIGNUM * q1 = ScalarMul (p1, n, ctx); + EncodeBN (q1, buf, 32); + BN_free (p1); BN_free (n); BN_free (q1); + } +#endif + void Ed25519::BlindPublicKey (const uint8_t * pub, const uint8_t * seed, uint8_t * blinded) { BN_CTX * ctx = BN_CTX_new (); diff --git a/libi2pd/Ed25519.h b/libi2pd/Ed25519.h index 9c0ad801..470d802f 100644 --- a/libi2pd/Ed25519.h +++ b/libi2pd/Ed25519.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2024, The PurpleI2P Project +* Copyright (c) 2013-2020, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -84,7 +84,10 @@ namespace crypto EDDSAPoint GeneratePublicKey (const uint8_t * expandedPrivateKey, BN_CTX * ctx) const; EDDSAPoint DecodePublicKey (const uint8_t * buf, BN_CTX * ctx) const; void EncodePublicKey (const EDDSAPoint& publicKey, uint8_t * buf, BN_CTX * ctx) const; - +#if !OPENSSL_X25519 + void ScalarMul (const uint8_t * p, const uint8_t * e, uint8_t * buf, BN_CTX * ctx) const; // p is point, e is number for x25519 + void ScalarMulB (const uint8_t * e, uint8_t * buf, BN_CTX * ctx) const; +#endif void BlindPublicKey (const uint8_t * pub, const uint8_t * seed, uint8_t * blinded); // for encrypted LeaseSet2, pub - 32, seed - 64, blinded - 32 void BlindPrivateKey (const uint8_t * priv, const uint8_t * seed, uint8_t * blindedPriv, uint8_t * blindedPub); // for encrypted LeaseSet2, pub - 32, seed - 64, blinded - 32 @@ -112,6 +115,11 @@ namespace crypto BIGNUM * DecodeBN (const uint8_t * buf) const; void EncodeBN (const BIGNUM * bn, uint8_t * buf, size_t len) const; +#if !OPENSSL_X25519 + // for x25519 + BIGNUM * ScalarMul (const BIGNUM * p, const BIGNUM * e, BN_CTX * ctx) const; +#endif + private: BIGNUM * q, * l, * d, * I; diff --git a/libi2pd/FS.cpp b/libi2pd/FS.cpp index 3f5fc6b9..a623a4eb 100644 --- a/libi2pd/FS.cpp +++ b/libi2pd/FS.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2025, The PurpleI2P Project +* Copyright (c) 2013-2024, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -15,10 +15,6 @@ #include #endif -#if defined(__HAIKU__) -#include -#endif - #ifdef _WIN32 #include #include @@ -173,11 +169,12 @@ namespace fs { dataDir += "/Library/Application Support/" + appName; return; #elif defined(__HAIKU__) - char home[PATH_MAX]; // /boot/home/config/settings - if (find_directory(B_USER_SETTINGS_DIRECTORY, -1, false, home, PATH_MAX) == B_OK) - dataDir = std::string(home) + "/" + appName; - else + 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) diff --git a/libi2pd/Family.cpp b/libi2pd/Family.cpp index 300a50ab..8c6d3ba4 100644 --- a/libi2pd/Family.cpp +++ b/libi2pd/Family.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2025, The PurpleI2P Project +* Copyright (c) 2013-2023, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -7,6 +7,7 @@ */ #include +#include #include #include "Crypto.h" #include "FS.h" @@ -24,8 +25,6 @@ namespace data Families::~Families () { - for (auto it : m_SigningKeys) - if (it.second.first) EVP_PKEY_free (it.second.first); } void Families::LoadCertificate (const std::string& filename) @@ -48,16 +47,48 @@ namespace data cn += 3; char * family = strstr (cn, ".family"); if (family) family[0] = 0; - auto pkey = X509_get_pubkey (cert); - if (pkey) - { - if (!m_SigningKeys.emplace (cn, std::make_pair(pkey, (int)m_SigningKeys.size () + 1)).second) - { - EVP_PKEY_free (pkey); - LogPrint (eLogError, "Family: Duplicated family name ", cn); - } - } } + auto pkey = X509_get_pubkey (cert); + int keyType = EVP_PKEY_base_id (pkey); + switch (keyType) + { + case EVP_PKEY_DSA: + // TODO: + break; + case EVP_PKEY_EC: + { + EC_KEY * ecKey = EVP_PKEY_get1_EC_KEY (pkey); + if (ecKey) + { + auto group = EC_KEY_get0_group (ecKey); + if (group) + { + int curve = EC_GROUP_get_curve_name (group); + if (curve == NID_X9_62_prime256v1) + { + uint8_t signingKey[64]; + BIGNUM * x = BN_new(), * y = BN_new(); + EC_POINT_get_affine_coordinates_GFp (group, + EC_KEY_get0_public_key (ecKey), x, y, NULL); + i2p::crypto::bn2buf (x, signingKey, 32); + i2p::crypto::bn2buf (y, signingKey + 32, 32); + BN_free (x); BN_free (y); + verifier = std::make_shared(); + verifier->SetPublicKey (signingKey); + } + else + LogPrint (eLogWarning, "Family: elliptic curve ", curve, " is not supported"); + } + EC_KEY_free (ecKey); + } + break; + } + default: + LogPrint (eLogWarning, "Family: Certificate key type ", keyType, " is not supported"); + } + EVP_PKEY_free (pkey); + if (verifier && cn) + m_SigningKeys.emplace (cn, std::make_pair(verifier, (int)m_SigningKeys.size () + 1)); } SSL_free (ssl); } @@ -90,31 +121,23 @@ namespace data } bool Families::VerifyFamily (const std::string& family, const IdentHash& ident, - std::string_view signature, const char * key) const + const char * signature, const char * key) const { uint8_t buf[100], signatureBuf[64]; - size_t len = family.length (); + size_t len = family.length (), signatureLen = strlen (signature); if (len + 32 > 100) { LogPrint (eLogError, "Family: ", family, " is too long"); return false; } + + memcpy (buf, family.c_str (), len); + memcpy (buf + len, (const uint8_t *)ident, 32); + len += 32; + Base64ToByteStream (signature, signatureLen, signatureBuf, 64); auto it = m_SigningKeys.find (family); - if (it != m_SigningKeys.end () && it->second.first) - { - memcpy (buf, family.c_str (), len); - memcpy (buf + len, (const uint8_t *)ident, 32); - len += 32; - auto signatureBufLen = Base64ToByteStream (signature, signatureBuf, 64); - if (signatureBufLen) - { - EVP_MD_CTX * ctx = EVP_MD_CTX_create (); - EVP_DigestVerifyInit (ctx, NULL, NULL, NULL, it->second.first); - auto ret = EVP_DigestVerify (ctx, signatureBuf, signatureBufLen, buf, len); - EVP_MD_CTX_destroy (ctx); - return ret; - } - } + if (it != m_SigningKeys.end ()) + return it->second.first->Verify (buf, len, signatureBuf); // TODO: process key return true; } @@ -154,7 +177,12 @@ namespace data memcpy (buf + len, (const uint8_t *)ident, 32); len += 32; signer.Sign (buf, len, signature); - sig = ByteStreamToBase64 (signature, 64); + len = Base64EncodingBufferSize (64); + char * b64 = new char[len+1]; + len = ByteStreamToBase64 (signature, 64, b64, len); + b64[len] = 0; + sig = b64; + delete[] b64; } else LogPrint (eLogWarning, "Family: elliptic curve ", curve, " is not supported"); diff --git a/libi2pd/Family.h b/libi2pd/Family.h index fcf61082..b19ea142 100644 --- a/libi2pd/Family.h +++ b/libi2pd/Family.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2025, The PurpleI2P Project +* Copyright (c) 2013-2022, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -11,9 +11,8 @@ #include #include -#include #include -#include +#include "Signature.h" #include "Identity.h" namespace i2p @@ -29,7 +28,7 @@ namespace data ~Families (); void LoadCertificates (); bool VerifyFamily (const std::string& family, const IdentHash& ident, - std::string_view signature, const char * key = nullptr) const; + const char * signature, const char * key = nullptr) const; FamilyID GetFamilyID (const std::string& family) const; private: @@ -38,7 +37,7 @@ namespace data private: - std::map > m_SigningKeys; // family -> (verification pkey, id) + std::map, FamilyID> > m_SigningKeys; // family -> (verifier, id) }; std::string CreateFamilySignature (const std::string& family, const IdentHash& ident); diff --git a/libi2pd/Garlic.cpp b/libi2pd/Garlic.cpp index 8c8602e8..1705b03a 100644 --- a/libi2pd/Garlic.cpp +++ b/libi2pd/Garlic.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2025, The PurpleI2P Project +* Copyright (c) 2013-2024, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -160,7 +160,7 @@ namespace garlic uint8_t iv[32]; // IV is first 16 bytes SHA256(elGamal.preIV, 32, iv); m_Destination->Encrypt ((uint8_t *)&elGamal, buf); - m_IV = iv; + m_Encryption.SetIV (iv); buf += 514; len += 514; } @@ -170,7 +170,7 @@ namespace garlic memcpy (buf, tag, 32); uint8_t iv[32]; // IV is first 16 bytes SHA256(tag, 32, iv); - m_IV = iv; + m_Encryption.SetIV (iv); buf += 32; len += 32; } @@ -210,7 +210,7 @@ namespace garlic size_t rem = blockSize % 16; if (rem) blockSize += (16-rem); //padding - m_Encryption.Encrypt(buf, blockSize, m_IV, buf); + m_Encryption.Encrypt(buf, blockSize, buf); return blockSize; } @@ -426,8 +426,7 @@ namespace garlic } GarlicDestination::GarlicDestination (): m_NumTags (32), // 32 tags by default - m_PayloadBuffer (nullptr), m_LastIncomingSessionTimestamp (0), - m_NumRatchetInboundTags (0) // 0 means standard + m_PayloadBuffer (nullptr), m_NumRatchetInboundTags (0) // 0 means standard { } @@ -498,8 +497,7 @@ namespace garlic buf += 4; // length bool found = false; - bool supportsRatchets = SupportsRatchets (); - if (supportsRatchets) + if (SupportsEncryptionType (i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD)) // try ECIESx25519 tag found = HandleECIESx25519TagMessage (buf, length); if (!found) @@ -515,7 +513,8 @@ namespace garlic { uint8_t iv[32]; // IV is first 16 bytes SHA256(buf, 32, iv); - decryption->Decrypt (buf + 32, length - 32, iv, buf + 32); + decryption->SetIV (iv); + decryption->Decrypt (buf + 32, length - 32, buf + 32); HandleAESBlock (buf + 32, length - 32, decryption, msg->from); found = true; } @@ -533,23 +532,16 @@ namespace garlic auto decryption = std::make_shared(elGamal.sessionKey); uint8_t iv[32]; // IV is first 16 bytes SHA256(elGamal.preIV, 32, iv); - decryption->Decrypt(buf + 514, length - 514, iv, buf + 514); + decryption->SetIV (iv); + decryption->Decrypt(buf + 514, length - 514, buf + 514); HandleAESBlock (buf + 514, length - 514, decryption, msg->from); } - else if (supportsRatchets) + else if (SupportsEncryptionType (i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD)) { // otherwise ECIESx25519 - auto ts = i2p::util::GetMillisecondsSinceEpoch (); - if (ts > m_LastIncomingSessionTimestamp + INCOMING_SESSIONS_MINIMAL_INTERVAL) - { - auto session = std::make_shared (this, false); // incoming - if (session->HandleNextMessage (buf, length, nullptr, 0)) - m_LastIncomingSessionTimestamp = ts; - else - LogPrint (eLogError, "Garlic: Can't handle ECIES-X25519-AEAD-Ratchet message"); - } - else - LogPrint (eLogWarning, "Garlic: Incoming sessions come too often"); + auto session = std::make_shared (this, false); // incoming + if (!session->HandleNextMessage (buf, length, nullptr, 0)) + LogPrint (eLogError, "Garlic: Can't handle ECIES-X25519-AEAD-Ratchet message"); } else LogPrint (eLogError, "Garlic: Failed to decrypt message"); @@ -745,38 +737,32 @@ namespace garlic } std::shared_ptr GarlicDestination::GetRoutingSession ( - std::shared_ptr destination, bool attachLeaseSet, - bool requestNewIfNotFound) + std::shared_ptr destination, bool attachLeaseSet) { - if (destination->GetEncryptionType () >= i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD) + if (destination->GetEncryptionType () == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD && + SupportsEncryptionType (i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD)) { - if (SupportsEncryptionType (destination->GetEncryptionType ())) - { - ECIESX25519AEADRatchetSessionPtr session; - uint8_t staticKey[32]; - destination->Encrypt (nullptr, staticKey); // we are supposed to get static key - auto it = m_ECIESx25519Sessions.find (staticKey); - if (it != m_ECIESx25519Sessions.end ()) + ECIESX25519AEADRatchetSessionPtr session; + uint8_t staticKey[32]; + destination->Encrypt (nullptr, staticKey); // we are supposed to get static key + auto it = m_ECIESx25519Sessions.find (staticKey); + if (it != m_ECIESx25519Sessions.end ()) + { + session = it->second; + if (session->IsInactive (i2p::util::GetSecondsSinceEpoch ())) { - session = it->second; - if (session->IsInactive (i2p::util::GetSecondsSinceEpoch ())) - { - LogPrint (eLogDebug, "Garlic: Session restarted"); - requestNewIfNotFound = true; // it's not a new session - session = nullptr; - } + LogPrint (eLogDebug, "Garlic: Session restarted"); + session = nullptr; } - if (!session && requestNewIfNotFound) - { - session = std::make_shared (this, true); - session->SetRemoteStaticKey (destination->GetEncryptionType (), staticKey); - } - if (session && destination->IsDestination ()) - session->SetDestination (destination->GetIdentHash ()); // NS or NSR - return session; } - else - LogPrint (eLogError, "Garlic: Non-supported encryption type ", destination->GetEncryptionType ()); + if (!session) + { + session = std::make_shared (this, true); + session->SetRemoteStaticKey (staticKey); + } + if (destination->IsDestination ()) + session->SetDestination (destination->GetIdentHash ()); // TODO: remove + return session; } else { @@ -911,7 +897,7 @@ namespace garlic } } - void GarlicDestination::SetLeaseSetUpdated (bool post) + void GarlicDestination::SetLeaseSetUpdated () { { std::unique_lock l(m_SessionsMutex); @@ -1004,8 +990,7 @@ namespace garlic i2p::fs::Remove (it); } - void GarlicDestination::HandleECIESx25519GarlicClove (const uint8_t * buf, size_t len, - ECIESX25519AEADRatchetSession * from) + void GarlicDestination::HandleECIESx25519GarlicClove (const uint8_t * buf, size_t len) { const uint8_t * buf1 = buf; uint8_t flag = buf[0]; buf++; // flag @@ -1025,7 +1010,7 @@ namespace garlic buf += 4; // expiration ptrdiff_t offset = buf - buf1; if (offset <= (int)len) - HandleCloveI2NPMessage (typeID, buf, len - offset, msgID, from); + HandleCloveI2NPMessage (typeID, buf, len - offset, msgID); else LogPrint (eLogError, "Garlic: Clove is too long"); break; @@ -1109,17 +1094,5 @@ namespace garlic m_PayloadBuffer = new uint8_t[I2NP_MAX_MESSAGE_SIZE]; return m_PayloadBuffer; } - - bool GarlicDestination::AEADChaCha20Poly1305Encrypt (const uint8_t * msg, size_t msgLen, const uint8_t * ad, size_t adLen, - const uint8_t * key, const uint8_t * nonce, uint8_t * buf, size_t len) - { - return m_Encryptor.Encrypt (msg, msgLen, ad, adLen, key, nonce, buf, len); - } - - bool GarlicDestination::AEADChaCha20Poly1305Decrypt (const uint8_t * msg, size_t msgLen, const uint8_t * ad, size_t adLen, - const uint8_t * key, const uint8_t * nonce, uint8_t * buf, size_t len) - { - return m_Decryptor.Decrypt (msg, msgLen, ad, adLen, key, nonce, buf, len); - } } } diff --git a/libi2pd/Garlic.h b/libi2pd/Garlic.h index 25106c45..a4475dc7 100644 --- a/libi2pd/Garlic.h +++ b/libi2pd/Garlic.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2025, 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,6 @@ namespace garlic const int OUTGOING_TAGS_CONFIRMATION_TIMEOUT = 10; // 10 seconds const int LEASESET_CONFIRMATION_TIMEOUT = 4000; // in milliseconds const int ROUTING_PATH_EXPIRATION_TIMEOUT = 120; // in seconds - const int INCOMING_SESSIONS_MINIMAL_INTERVAL = 200; // in milliseconds struct SessionTag: public i2p::data::Tag<32> { @@ -116,8 +115,7 @@ namespace garlic virtual bool IsReadyToSend () const { return true; }; virtual bool IsTerminated () const { return !GetOwner (); }; virtual uint64_t GetLastActivityTimestamp () const { return 0; }; // non-zero for rathets only - virtual void SetAckRequestInterval (int interval) {}; // in milliseconds, override in ECIESX25519AEADRatchetSession - + void SetLeaseSetUpdated () { if (m_LeaseSetUpdateStatus != eLeaseSetDoNotSend) m_LeaseSetUpdateStatus = eLeaseSetUpdated; @@ -206,7 +204,6 @@ namespace garlic std::map > m_UnconfirmedTagsMsgs; // msgID->tags i2p::crypto::CBCEncryption m_Encryption; - i2p::data::Tag<16> m_IV; public: @@ -237,17 +234,11 @@ namespace garlic int GetNumTags () const { return m_NumTags; }; void SetNumRatchetInboundTags (int numTags) { m_NumRatchetInboundTags = numTags; }; int GetNumRatchetInboundTags () const { return m_NumRatchetInboundTags; }; - std::shared_ptr GetRoutingSession (std::shared_ptr destination, - bool attachLeaseSet, bool requestNewIfNotFound = true); + std::shared_ptr GetRoutingSession (std::shared_ptr destination, bool attachLeaseSet); void CleanupExpiredTags (); void RemoveDeliveryStatusSession (uint32_t msgID); std::shared_ptr WrapMessageForRouter (std::shared_ptr router, std::shared_ptr msg); - - bool AEADChaCha20Poly1305Encrypt (const uint8_t * msg, size_t msgLen, const uint8_t * ad, size_t adLen, - const uint8_t * key, const uint8_t * nonce, uint8_t * buf, size_t len); - bool AEADChaCha20Poly1305Decrypt (const uint8_t * msg, size_t msgLen, const uint8_t * ad, size_t adLen, - const uint8_t * key, const uint8_t * nonce, uint8_t * buf, size_t len); void AddSessionKey (const uint8_t * key, const uint8_t * tag); // one tag void AddECIESx25519Key (const uint8_t * key, uint64_t tag); // one tag @@ -257,36 +248,30 @@ namespace garlic uint64_t AddECIESx25519SessionNextTag (ReceiveRatchetTagSetPtr tagset); void AddECIESx25519Session (const uint8_t * staticKey, ECIESX25519AEADRatchetSessionPtr session); void RemoveECIESx25519Session (const uint8_t * staticKey); - void HandleECIESx25519GarlicClove (const uint8_t * buf, size_t len, ECIESX25519AEADRatchetSession * from); + void HandleECIESx25519GarlicClove (const uint8_t * buf, size_t len); uint8_t * GetPayloadBuffer (); virtual void ProcessGarlicMessage (std::shared_ptr msg); virtual void ProcessDeliveryStatusMessage (std::shared_ptr msg); - virtual void SetLeaseSetUpdated (bool post = false); + virtual void SetLeaseSetUpdated (); virtual std::shared_ptr GetLeaseSet () = 0; // TODO virtual std::shared_ptr GetTunnelPool () const = 0; - virtual i2p::data::CryptoKeyType GetRatchetsHighestCryptoType () const - { - return GetIdentity ()->GetCryptoKeyType () >= i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD ? GetIdentity ()->GetCryptoKeyType () : 0; - } protected: void AddECIESx25519Key (const uint8_t * key, const uint8_t * tag); // one tag bool HandleECIESx25519TagMessage (uint8_t * buf, size_t len); // return true if found virtual void HandleI2NPMessage (const uint8_t * buf, size_t len) = 0; // called from clove only - virtual bool HandleCloveI2NPMessage (I2NPMessageType typeID, const uint8_t * payload, - size_t len, uint32_t msgID, ECIESX25519AEADRatchetSession * from) = 0; + virtual bool HandleCloveI2NPMessage (I2NPMessageType typeID, const uint8_t * payload, size_t len, uint32_t msgID) = 0; void HandleGarlicMessage (std::shared_ptr msg); void HandleDeliveryStatusMessage (uint32_t msgID); void SaveTags (); void LoadTags (); - + private: - bool SupportsRatchets () const { return GetRatchetsHighestCryptoType () > 0; } void HandleAESBlock (uint8_t * buf, size_t len, std::shared_ptr decryption, std::shared_ptr from); void HandleGarlicPayload (uint8_t * buf, size_t len, std::shared_ptr from); @@ -299,7 +284,6 @@ namespace garlic std::unordered_map m_Sessions; std::unordered_map, ECIESX25519AEADRatchetSessionPtr> m_ECIESx25519Sessions; // static key -> session uint8_t * m_PayloadBuffer; // for ECIESX25519AEADRatchet - uint64_t m_LastIncomingSessionTimestamp; // in milliseconds // incoming int m_NumRatchetInboundTags; std::unordered_map, std::hash > > m_Tags; @@ -307,10 +291,7 @@ namespace garlic // DeliveryStatus std::mutex m_DeliveryStatusSessionsMutex; std::unordered_map m_DeliveryStatusSessions; // msgID -> session - // encryption - i2p::crypto::AEADChaCha20Poly1305Encryptor m_Encryptor; - i2p::crypto::AEADChaCha20Poly1305Decryptor m_Decryptor; - + public: // for HTTP only diff --git a/libi2pd/HTTP.cpp b/libi2pd/HTTP.cpp index 3cd5c193..990781bc 100644 --- a/libi2pd/HTTP.cpp +++ b/libi2pd/HTTP.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2025, The PurpleI2P Project +* Copyright (c) 2013-2024, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -10,7 +10,6 @@ #include #include #include -#include #include "util.h" #include "Base.h" #include "HTTP.h" @@ -19,51 +18,54 @@ namespace i2p { namespace http { - // list of valid HTTP methods - static constexpr std::array HTTP_METHODS = - { + const std::vector HTTP_METHODS = { "GET", "HEAD", "POST", "PUT", "PATCH", "DELETE", "OPTIONS", "CONNECT", // HTTP basic methods "COPY", "LOCK", "MKCOL", "MOVE", "PROPFIND", "PROPPATCH", "UNLOCK", "SEARCH" // WebDAV methods, for SEARCH see rfc5323 }; - - // list of valid HTTP versions - static constexpr std::array HTTP_VERSIONS = - { + const std::vector HTTP_VERSIONS = { "HTTP/1.0", "HTTP/1.1" }; - - static constexpr std::array weekdays = - { + const std::vector weekdays = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }; - - static constexpr std::array months = - { + const std::vector months = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; - static inline bool is_http_version(std::string_view str) - { + inline bool is_http_version(const std::string & str) { return std::find(HTTP_VERSIONS.begin(), HTTP_VERSIONS.end(), str) != std::end(HTTP_VERSIONS); } - static inline bool is_http_method(std::string_view str) - { + inline bool is_http_method(const std::string & str) { return std::find(HTTP_METHODS.begin(), HTTP_METHODS.end(), str) != std::end(HTTP_METHODS); } - static void strsplit(std::string_view line, std::vector &tokens, char delim, std::size_t limit = 0) - { - size_t count = 0, pos; - while ((pos = line.find (delim)) != line.npos) + static void strsplit(std::stringstream& ss, std::vector &tokens, char delim, std::size_t limit = 0) + { + std::size_t count = 0; + std::string token; + while (1) { count++; - if (limit > 0 && count >= limit) delim = '\n'; // reset delimiter - tokens.push_back (line.substr (0, pos)); - line = line.substr (pos + 1); + if (limit > 0 && count >= limit) + delim = '\n'; /* reset delimiter */ + if (!std::getline(ss, token, delim)) + break; + tokens.push_back(token); } - if (!line.empty ()) tokens.push_back (line); + } + + static void strsplit(const std::string & line, std::vector &tokens, char delim, std::size_t limit = 0) + { + std::stringstream ss{line}; + strsplit (ss, tokens, delim, limit); + } + + static void strsplit(std::string_view line, std::vector &tokens, char delim, std::size_t limit = 0) + { + std::stringstream ss{std::string(line)}; + strsplit (ss, tokens, delim, limit); } static std::pair parse_header_line(std::string_view line) @@ -101,7 +103,6 @@ namespace http bool URL::parse(std::string_view url) { - if (url.empty ()) return false; std::size_t pos_p = 0; /* < current parse position */ std::size_t pos_c = 0; /* < work position */ if(url.at(0) != '/' || pos_p > 0) @@ -209,9 +210,8 @@ namespace http return true; } - bool URL::parse_query(std::map & params) - { - std::vector tokens; + bool URL::parse_query(std::map & params) { + std::vector tokens; strsplit(query, tokens, '&'); params.clear(); @@ -307,9 +307,8 @@ namespace http if (expect == REQ_LINE) { std::string_view line = str.substr(pos, eol - pos); - std::vector tokens; + std::vector tokens; strsplit(line, tokens, ' '); - if (tokens.size() != 3) return -1; if (!is_http_method(tokens[0])) @@ -333,11 +332,11 @@ namespace http else return -1; } - pos = eol + CRLF.length(); + pos = eol + strlen(CRLF); if (pos >= eoh) break; } - return eoh + HTTP_EOH.length(); + return eoh + strlen(HTTP_EOH); } void HTTPReq::write(std::ostream & o) @@ -381,7 +380,7 @@ namespace http } } - std::string HTTPReq::GetHeader (std::string_view name) const + std::string HTTPReq::GetHeader (const std::string& name) const { for (auto& it : headers) if (it.first == name) @@ -389,7 +388,7 @@ namespace http return ""; } - size_t HTTPReq::GetNumHeaders (std::string_view name) const + size_t HTTPReq::GetNumHeaders (const std::string& name) const { size_t num = 0; for (auto& it : headers) @@ -451,15 +450,13 @@ namespace http if (expect == RES_LINE) { std::string_view line = str.substr(pos, eol - pos); - std::vector tokens; + std::vector tokens; strsplit(line, tokens, ' ', 3); if (tokens.size() != 3) return -1; if (!is_http_version(tokens[0])) return -1; - auto res = std::from_chars(tokens[1].data (), tokens[1].data() + tokens[1].size(), code); - if (res.ec != std::errc()) - return -1; + code = atoi(tokens[1].c_str()); if (code < 100 || code >= 600) return -1; /* all ok */ @@ -476,11 +473,11 @@ namespace http else return -1; } - pos = eol + CRLF.length(); + pos = eol + strlen(CRLF); if (pos >= eoh) break; } - return eoh + HTTP_EOH.length(); + return eoh + strlen(HTTP_EOH); } std::string HTTPRes::to_string() { @@ -505,11 +502,9 @@ namespace http return ss.str(); } - std::string_view HTTPCodeToStatus(int code) - { - std::string_view ptr; - switch (code) - { + const char * HTTPCodeToStatus(int code) { + const char *ptr; + switch (code) { case 105: ptr = "Name Not Resolved"; break; /* success */ case 200: ptr = "OK"; break; diff --git a/libi2pd/HTTP.h b/libi2pd/HTTP.h index c65c1ce4..438ef953 100644 --- a/libi2pd/HTTP.h +++ b/libi2pd/HTTP.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2025, The PurpleI2P Project +* Copyright (c) 2013-2024, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -21,8 +21,10 @@ namespace i2p { namespace http { - constexpr std::string_view CRLF = "\r\n"; /**< HTTP line terminator */ - constexpr std::string_view HTTP_EOH = "\r\n\r\n"; /**< HTTP end-of-headers mark */ + const char CRLF[] = "\r\n"; /**< HTTP line terminator */ + const char HTTP_EOH[] = "\r\n\r\n"; /**< HTTP end-of-headers mark */ + extern const std::vector HTTP_METHODS; /**< list of valid HTTP methods */ + extern const std::vector HTTP_VERSIONS; /**< list of valid HTTP versions */ struct URL { @@ -101,8 +103,8 @@ namespace http void UpdateHeader (const std::string& name, const std::string& value); void RemoveHeader (const std::string& name, const std::string& exempt); // remove all headers starting with name, but exempt void RemoveHeader (const std::string& name) { RemoveHeader (name, ""); }; - std::string GetHeader (std::string_view name) const; - size_t GetNumHeaders (std::string_view name) const; + std::string GetHeader (const std::string& name) const; + size_t GetNumHeaders (const std::string& name) const; size_t GetNumHeaders () const { return headers.size (); }; }; @@ -152,7 +154,7 @@ namespace http * @param code HTTP code [100, 599] * @return Immutable string with status */ - std::string_view HTTPCodeToStatus(int code); + const char * HTTPCodeToStatus(int code); /** * @brief Replaces %-encoded characters in string with their values diff --git a/libi2pd/I2NPProtocol.cpp b/libi2pd/I2NPProtocol.cpp index e97a3596..4cceda8f 100644 --- a/libi2pd/I2NPProtocol.cpp +++ b/libi2pd/I2NPProtocol.cpp @@ -10,15 +10,20 @@ #include #include "Base.h" #include "Log.h" +#include "Crypto.h" #include "I2PEndian.h" #include "Timestamp.h" #include "RouterContext.h" #include "NetDb.hpp" #include "Tunnel.h" -#include "TransitTunnel.h" +#include "Transports.h" +#include "Garlic.h" +#include "ECIESX25519AEADRatchetSession.h" #include "I2NPProtocol.h" #include "version.h" +using namespace i2p::transport; + namespace i2p { std::shared_ptr NewI2NPMessage () @@ -371,6 +376,337 @@ namespace i2p return !msg->GetPayload ()[DATABASE_STORE_TYPE_OFFSET]; // 0- RouterInfo } + static bool HandleBuildRequestRecords (int num, uint8_t * records, uint8_t * clearText) + { + for (int i = 0; i < num; i++) + { + uint8_t * record = records + i*TUNNEL_BUILD_RECORD_SIZE; + if (!memcmp (record + BUILD_REQUEST_RECORD_TO_PEER_OFFSET, (const uint8_t *)i2p::context.GetRouterInfo ().GetIdentHash (), 16)) + { + LogPrint (eLogDebug, "I2NP: Build request record ", i, " is ours"); + if (!i2p::context.DecryptTunnelBuildRecord (record + BUILD_REQUEST_RECORD_ENCRYPTED_OFFSET, clearText)) + { + LogPrint (eLogWarning, "I2NP: Failed to decrypt tunnel build record"); + return false; + } + if (!memcmp ((const uint8_t *)i2p::context.GetIdentHash (), clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET, 32) && // if next ident is now ours + !(clearText[ECIES_BUILD_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_ENDPOINT_FLAG)) // and not endpoint + { + LogPrint (eLogWarning, "I2NP: Next ident is ours in tunnel build record"); + return false; + } + uint8_t retCode = 0; + // replace record to reply + if (i2p::context.AcceptsTunnels () && i2p::context.GetCongestionLevel (false) < CONGESTION_LEVEL_FULL) + { + auto transitTunnel = i2p::tunnel::CreateTransitTunnel ( + bufbe32toh (clearText + ECIES_BUILD_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET), + clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET, + bufbe32toh (clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET), + clearText + ECIES_BUILD_REQUEST_RECORD_LAYER_KEY_OFFSET, + clearText + ECIES_BUILD_REQUEST_RECORD_IV_KEY_OFFSET, + clearText[ECIES_BUILD_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_GATEWAY_FLAG, + clearText[ECIES_BUILD_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_ENDPOINT_FLAG); + if (!i2p::tunnel::tunnels.AddTransitTunnel (transitTunnel)) + retCode = 30; + } + else + retCode = 30; // always reject with bandwidth reason (30) + + memset (record + ECIES_BUILD_RESPONSE_RECORD_OPTIONS_OFFSET, 0, 2); // no options + record[ECIES_BUILD_RESPONSE_RECORD_RET_OFFSET] = retCode; + // encrypt reply + i2p::crypto::CBCEncryption encryption; + for (int j = 0; j < num; j++) + { + uint8_t * reply = records + j*TUNNEL_BUILD_RECORD_SIZE; + if (j == i) + { + uint8_t nonce[12]; + memset (nonce, 0, 12); + auto& noiseState = i2p::context.GetCurrentNoiseState (); + if (!i2p::crypto::AEADChaCha20Poly1305 (reply, TUNNEL_BUILD_RECORD_SIZE - 16, + noiseState.m_H, 32, noiseState.m_CK, nonce, reply, TUNNEL_BUILD_RECORD_SIZE, true)) // encrypt + { + LogPrint (eLogWarning, "I2NP: Reply AEAD encryption failed"); + return false; + } + } + else + { + encryption.SetKey (clearText + ECIES_BUILD_REQUEST_RECORD_REPLY_KEY_OFFSET); + encryption.SetIV (clearText + ECIES_BUILD_REQUEST_RECORD_REPLY_IV_OFFSET); + encryption.Encrypt(reply, TUNNEL_BUILD_RECORD_SIZE, reply); + } + } + return true; + } + } + return false; + } + + static void HandleVariableTunnelBuildMsg (uint32_t replyMsgID, uint8_t * buf, size_t len) + { + int num = buf[0]; + LogPrint (eLogDebug, "I2NP: VariableTunnelBuild ", num, " records"); + if (num > i2p::tunnel::MAX_NUM_RECORDS) + { + LogPrint (eLogError, "I2NP: Too many records in VaribleTunnelBuild message ", num); + return; + } + if (len < num*TUNNEL_BUILD_RECORD_SIZE + 1) + { + LogPrint (eLogError, "I2NP: VaribleTunnelBuild message of ", num, " records is too short ", len); + return; + } + + auto tunnel = i2p::tunnel::tunnels.GetPendingInboundTunnel (replyMsgID); + if (tunnel) + { + // endpoint of inbound tunnel + LogPrint (eLogDebug, "I2NP: VariableTunnelBuild reply for tunnel ", tunnel->GetTunnelID ()); + if (tunnel->HandleTunnelBuildResponse (buf, len)) + { + LogPrint (eLogInfo, "I2NP: Inbound tunnel ", tunnel->GetTunnelID (), " has been created"); + tunnel->SetState (i2p::tunnel::eTunnelStateEstablished); + i2p::tunnel::tunnels.AddInboundTunnel (tunnel); + } + else + { + LogPrint (eLogInfo, "I2NP: Inbound tunnel ", tunnel->GetTunnelID (), " has been declined"); + tunnel->SetState (i2p::tunnel::eTunnelStateBuildFailed); + } + } + else + { + uint8_t clearText[ECIES_BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE]; + if (HandleBuildRequestRecords (num, buf + 1, clearText)) + { + if (clearText[ECIES_BUILD_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_ENDPOINT_FLAG) // we are endpoint of outboud tunnel + { + // so we send it to reply tunnel + transports.SendMessage (clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET, + CreateTunnelGatewayMsg (bufbe32toh (clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET), + eI2NPVariableTunnelBuildReply, buf, len, + bufbe32toh (clearText + ECIES_BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET))); + } + else + transports.SendMessage (clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET, + CreateI2NPMessage (eI2NPVariableTunnelBuild, buf, len, + bufbe32toh (clearText + ECIES_BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET))); + } + } + } + + static void HandleTunnelBuildMsg (uint8_t * buf, size_t len) + { + LogPrint (eLogWarning, "I2NP: TunnelBuild is too old for ECIES router"); + } + + static void HandleTunnelBuildReplyMsg (uint32_t replyMsgID, uint8_t * buf, size_t len, bool isShort) + { + int num = buf[0]; + LogPrint (eLogDebug, "I2NP: TunnelBuildReplyMsg of ", num, " records replyMsgID=", replyMsgID); + if (num > i2p::tunnel::MAX_NUM_RECORDS) + { + LogPrint (eLogError, "I2NP: Too many records in TunnelBuildReply message ", num); + return; + } + size_t recordSize = isShort ? SHORT_TUNNEL_BUILD_RECORD_SIZE : TUNNEL_BUILD_RECORD_SIZE; + if (len < num*recordSize + 1) + { + LogPrint (eLogError, "I2NP: TunnelBuildReply message of ", num, " records is too short ", len); + return; + } + + auto tunnel = i2p::tunnel::tunnels.GetPendingOutboundTunnel (replyMsgID); + if (tunnel) + { + // reply for outbound tunnel + if (tunnel->HandleTunnelBuildResponse (buf, len)) + { + LogPrint (eLogInfo, "I2NP: Outbound tunnel ", tunnel->GetTunnelID (), " has been created"); + tunnel->SetState (i2p::tunnel::eTunnelStateEstablished); + i2p::tunnel::tunnels.AddOutboundTunnel (tunnel); + } + else + { + LogPrint (eLogInfo, "I2NP: Outbound tunnel ", tunnel->GetTunnelID (), " has been declined"); + tunnel->SetState (i2p::tunnel::eTunnelStateBuildFailed); + } + } + else + LogPrint (eLogWarning, "I2NP: Pending tunnel for message ", replyMsgID, " not found"); + } + + static void HandleShortTunnelBuildMsg (uint32_t replyMsgID, uint8_t * buf, size_t len) + { + int num = buf[0]; + LogPrint (eLogDebug, "I2NP: ShortTunnelBuild ", num, " records"); + if (num > i2p::tunnel::MAX_NUM_RECORDS) + { + LogPrint (eLogError, "I2NP: Too many records in ShortTunnelBuild message ", num); + return; + } + if (len < num*SHORT_TUNNEL_BUILD_RECORD_SIZE + 1) + { + LogPrint (eLogError, "I2NP: ShortTunnelBuild message of ", num, " records is too short ", len); + return; + } + auto tunnel = i2p::tunnel::tunnels.GetPendingInboundTunnel (replyMsgID); + if (tunnel) + { + // endpoint of inbound tunnel + LogPrint (eLogDebug, "I2NP: ShortTunnelBuild reply for tunnel ", tunnel->GetTunnelID ()); + if (tunnel->HandleTunnelBuildResponse (buf, len)) + { + LogPrint (eLogInfo, "I2NP: Inbound tunnel ", tunnel->GetTunnelID (), " has been created"); + tunnel->SetState (i2p::tunnel::eTunnelStateEstablished); + i2p::tunnel::tunnels.AddInboundTunnel (tunnel); + } + else + { + LogPrint (eLogInfo, "I2NP: Inbound tunnel ", tunnel->GetTunnelID (), " has been declined"); + tunnel->SetState (i2p::tunnel::eTunnelStateBuildFailed); + } + return; + } + const uint8_t * record = buf + 1; + for (int i = 0; i < num; i++) + { + if (!memcmp (record, (const uint8_t *)i2p::context.GetRouterInfo ().GetIdentHash (), 16)) + { + LogPrint (eLogDebug, "I2NP: Short request record ", i, " is ours"); + uint8_t clearText[SHORT_REQUEST_RECORD_CLEAR_TEXT_SIZE]; + if (!i2p::context.DecryptTunnelShortRequestRecord (record + SHORT_REQUEST_RECORD_ENCRYPTED_OFFSET, clearText)) + { + LogPrint (eLogWarning, "I2NP: Can't decrypt short request record ", i); + return; + } + if (clearText[SHORT_REQUEST_RECORD_LAYER_ENCRYPTION_TYPE]) // not AES + { + LogPrint (eLogWarning, "I2NP: Unknown layer encryption type ", clearText[SHORT_REQUEST_RECORD_LAYER_ENCRYPTION_TYPE], " in short request record"); + return; + } + auto& noiseState = i2p::context.GetCurrentNoiseState (); + uint8_t replyKey[32]; // AEAD/Chacha20/Poly1305 + i2p::crypto::AESKey layerKey, ivKey; // AES + i2p::crypto::HKDF (noiseState.m_CK, nullptr, 0, "SMTunnelReplyKey", noiseState.m_CK); + memcpy (replyKey, noiseState.m_CK + 32, 32); + i2p::crypto::HKDF (noiseState.m_CK, nullptr, 0, "SMTunnelLayerKey", noiseState.m_CK); + memcpy (layerKey, noiseState.m_CK + 32, 32); + bool isEndpoint = clearText[SHORT_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_ENDPOINT_FLAG; + if (isEndpoint) + { + i2p::crypto::HKDF (noiseState.m_CK, nullptr, 0, "TunnelLayerIVKey", noiseState.m_CK); + memcpy (ivKey, noiseState.m_CK + 32, 32); + } + else + { + if (!memcmp ((const uint8_t *)i2p::context.GetIdentHash (), clearText + SHORT_REQUEST_RECORD_NEXT_IDENT_OFFSET, 32)) // if next ident is now ours + { + LogPrint (eLogWarning, "I2NP: Next ident is ours in short request record"); + return; + } + memcpy (ivKey, noiseState.m_CK , 32); + } + + // check if we accept this tunnel + std::shared_ptr transitTunnel; + uint8_t retCode = 0; + if (!i2p::context.AcceptsTunnels () || i2p::context.GetCongestionLevel (false) >= CONGESTION_LEVEL_FULL) + retCode = 30; + if (!retCode) + { + // create new transit tunnel + transitTunnel = i2p::tunnel::CreateTransitTunnel ( + bufbe32toh (clearText + SHORT_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET), + clearText + SHORT_REQUEST_RECORD_NEXT_IDENT_OFFSET, + bufbe32toh (clearText + SHORT_REQUEST_RECORD_NEXT_TUNNEL_OFFSET), + layerKey, ivKey, + clearText[SHORT_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_GATEWAY_FLAG, + clearText[SHORT_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_ENDPOINT_FLAG); + if (!i2p::tunnel::tunnels.AddTransitTunnel (transitTunnel)) + retCode = 30; + } + + // encrypt reply + uint8_t nonce[12]; + memset (nonce, 0, 12); + uint8_t * reply = buf + 1; + for (int j = 0; j < num; j++) + { + nonce[4] = j; // nonce is record # + if (j == i) + { + memset (reply + SHORT_RESPONSE_RECORD_OPTIONS_OFFSET, 0, 2); // no options + reply[SHORT_RESPONSE_RECORD_RET_OFFSET] = retCode; + if (!i2p::crypto::AEADChaCha20Poly1305 (reply, SHORT_TUNNEL_BUILD_RECORD_SIZE - 16, + noiseState.m_H, 32, replyKey, nonce, reply, SHORT_TUNNEL_BUILD_RECORD_SIZE, true)) // encrypt + { + LogPrint (eLogWarning, "I2NP: Short reply AEAD encryption failed"); + return; + } + } + else + i2p::crypto::ChaCha20 (reply, SHORT_TUNNEL_BUILD_RECORD_SIZE, replyKey, nonce, reply); + reply += SHORT_TUNNEL_BUILD_RECORD_SIZE; + } + // send reply + auto onDrop = [transitTunnel]() + { + if (transitTunnel) + { + auto t = transitTunnel->GetCreationTime (); + if (t > i2p::tunnel::TUNNEL_EXPIRATION_TIMEOUT) + // make transit tunnel expired + transitTunnel->SetCreationTime (t - i2p::tunnel::TUNNEL_EXPIRATION_TIMEOUT); + } + }; + if (isEndpoint) + { + auto replyMsg = NewI2NPShortMessage (); + replyMsg->Concat (buf, len); + replyMsg->FillI2NPMessageHeader (eI2NPShortTunnelBuildReply, bufbe32toh (clearText + SHORT_REQUEST_RECORD_SEND_MSG_ID_OFFSET)); + if (transitTunnel) replyMsg->onDrop = onDrop; + if (memcmp ((const uint8_t *)i2p::context.GetIdentHash (), + clearText + SHORT_REQUEST_RECORD_NEXT_IDENT_OFFSET, 32)) // reply IBGW is not local? + { + i2p::crypto::HKDF (noiseState.m_CK, nullptr, 0, "RGarlicKeyAndTag", noiseState.m_CK); + uint64_t tag; + memcpy (&tag, noiseState.m_CK, 8); + // we send it to reply tunnel + transports.SendMessage (clearText + SHORT_REQUEST_RECORD_NEXT_IDENT_OFFSET, + CreateTunnelGatewayMsg (bufbe32toh (clearText + SHORT_REQUEST_RECORD_NEXT_TUNNEL_OFFSET), + i2p::garlic::WrapECIESX25519Message (replyMsg, noiseState.m_CK + 32, tag))); + } + else + { + // IBGW is local + uint32_t tunnelID = bufbe32toh (clearText + SHORT_REQUEST_RECORD_NEXT_TUNNEL_OFFSET); + auto tunnel = i2p::tunnel::tunnels.GetTunnel (tunnelID); + if (tunnel) + { + tunnel->SendTunnelDataMsg (replyMsg); + tunnel->FlushTunnelDataMsgs (); + } + else + LogPrint (eLogWarning, "I2NP: Tunnel ", tunnelID, " not found for short tunnel build reply"); + } + } + else + { + auto msg = CreateI2NPMessage (eI2NPShortTunnelBuild, buf, len, + bufbe32toh (clearText + SHORT_REQUEST_RECORD_SEND_MSG_ID_OFFSET)); + if (transitTunnel) msg->onDrop = onDrop; + transports.SendMessage (clearText + SHORT_REQUEST_RECORD_NEXT_IDENT_OFFSET, msg); + } + return; + } + record += SHORT_TUNNEL_BUILD_RECORD_SIZE; + } + } + std::shared_ptr CreateTunnelDataMsg (const uint8_t * buf) { auto msg = NewI2NPTunnelMessage (false); @@ -466,6 +802,41 @@ namespace i2p return l; } + void HandleTunnelBuildI2NPMessage (std::shared_ptr msg) + { + if (msg) + { + uint8_t typeID = msg->GetTypeID(); + uint32_t msgID = msg->GetMsgID(); + LogPrint (eLogDebug, "I2NP: Handling tunnel build message with len=", msg->GetLength(),", type=", (int)typeID, ", msgID=", (unsigned int)msgID); + uint8_t * payload = msg->GetPayload(); + auto size = msg->GetPayloadLength(); + switch (typeID) + { + case eI2NPVariableTunnelBuild: + HandleVariableTunnelBuildMsg (msgID, payload, size); + break; + case eI2NPShortTunnelBuild: + HandleShortTunnelBuildMsg (msgID, payload, size); + break; + case eI2NPVariableTunnelBuildReply: + HandleTunnelBuildReplyMsg (msgID, payload, size, false); + break; + case eI2NPShortTunnelBuildReply: + HandleTunnelBuildReplyMsg (msgID, payload, size, true); + break; + case eI2NPTunnelBuild: + HandleTunnelBuildMsg (payload, size); + break; + case eI2NPTunnelBuildReply: + // TODO: + break; + default: + LogPrint (eLogError, "I2NP: Unexpected message with type", (int)typeID, " during handling TBM; skipping"); + } + } + } + void HandleI2NPMessage (std::shared_ptr msg) { if (msg) @@ -561,8 +932,14 @@ namespace i2p void I2NPMessagesHandler::Flush () { if (!m_TunnelMsgs.empty ()) + { i2p::tunnel::tunnels.PostTunnelData (m_TunnelMsgs); + m_TunnelMsgs.clear (); + } if (!m_TunnelGatewayMsgs.empty ()) + { i2p::tunnel::tunnels.PostTunnelData (m_TunnelGatewayMsgs); + m_TunnelGatewayMsgs.clear (); + } } } diff --git a/libi2pd/I2NPProtocol.h b/libi2pd/I2NPProtocol.h index 911a53bf..4e26fc94 100644 --- a/libi2pd/I2NPProtocol.h +++ b/libi2pd/I2NPProtocol.h @@ -13,7 +13,6 @@ #include #include #include -#include #include #include "Crypto.h" #include "I2PEndian.h" @@ -316,6 +315,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 HandleTunnelBuildI2NPMessage (std::shared_ptr msg); void HandleI2NPMessage (std::shared_ptr msg); class I2NPMessagesHandler @@ -328,7 +328,7 @@ namespace tunnel private: - std::list > m_TunnelMsgs, m_TunnelGatewayMsgs; + std::vector > m_TunnelMsgs, m_TunnelGatewayMsgs; }; } diff --git a/libi2pd/Identity.cpp b/libi2pd/Identity.cpp index 865beeb8..2b8b454e 100644 --- a/libi2pd/Identity.cpp +++ b/libi2pd/Identity.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2025, The PurpleI2P Project +* Copyright (c) 2013-2024, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -10,7 +10,6 @@ #include "I2PEndian.h" #include "Log.h" #include "Timestamp.h" -#include "CryptoKey.h" #include "Identity.h" namespace i2p @@ -28,15 +27,18 @@ namespace data size_t Identity::FromBuffer (const uint8_t * buf, size_t len) { - if (len < DEFAULT_IDENTITY_SIZE) return 0; // buffer too small, don't overflow - memcpy (this, buf, DEFAULT_IDENTITY_SIZE); + if ( len < DEFAULT_IDENTITY_SIZE ) { + // buffer too small, don't overflow + return 0; + } + memcpy (publicKey, buf, DEFAULT_IDENTITY_SIZE); return DEFAULT_IDENTITY_SIZE; } IdentHash Identity::Hash () const { IdentHash hash; - SHA256((const uint8_t *)this, DEFAULT_IDENTITY_SIZE, hash); + SHA256(publicKey, DEFAULT_IDENTITY_SIZE, hash); return hash; } @@ -120,17 +122,6 @@ namespace data memcpy (m_StandardIdentity.signingKey, signingKey, i2p::crypto::GOSTR3410_512_PUBLIC_KEY_LENGTH); break; } -#if OPENSSL_PQ - case SIGNING_KEY_TYPE_MLDSA44: - { - memcpy (m_StandardIdentity, signingKey, 384); - excessLen = i2p::crypto::MLDSA44_PUBLIC_KEY_LENGTH - 384; - excessBuf = new uint8_t[excessLen]; - memcpy (excessBuf, signingKey + 384, excessLen); - cryptoType = 0xFF; // crypto key is not used - break; - } -#endif default: LogPrint (eLogError, "Identity: Signing key type ", (int)type, " is not supported"); } @@ -143,15 +134,12 @@ namespace data htobe16buf (m_ExtendedBuffer + 2, cryptoType); if (excessLen && excessBuf) { - if (m_ExtendedLen > MAX_EXTENDED_BUFFER_SIZE) + if (excessLen > MAX_EXTENDED_BUFFER_SIZE - 4) { - auto newBuf = new uint8_t[m_ExtendedLen]; - memcpy (newBuf, m_ExtendedBuffer, 4); - memcpy (newBuf + 4, excessBuf, excessLen); - m_ExtendedBufferPtr = newBuf; + LogPrint (eLogError, "Identity: Unexpected excessive signing key len ", excessLen); + excessLen = MAX_EXTENDED_BUFFER_SIZE - 4; } - else - memcpy (m_ExtendedBuffer + 4, excessBuf, excessLen); + memcpy (m_ExtendedBuffer + 4, excessBuf, excessLen); delete[] excessBuf; } // calculate ident hash @@ -199,8 +187,6 @@ namespace data IdentityEx::~IdentityEx () { - if (m_ExtendedLen > MAX_EXTENDED_BUFFER_SIZE) - delete[] m_ExtendedBufferPtr; } IdentityEx& IdentityEx::operator=(const IdentityEx& other) @@ -208,29 +194,11 @@ namespace data memcpy (&m_StandardIdentity, &other.m_StandardIdentity, DEFAULT_IDENTITY_SIZE); m_IdentHash = other.m_IdentHash; - size_t oldLen = m_ExtendedLen; m_ExtendedLen = other.m_ExtendedLen; if (m_ExtendedLen > 0) { - if (m_ExtendedLen > MAX_EXTENDED_BUFFER_SIZE) - { - if (oldLen > MAX_EXTENDED_BUFFER_SIZE) - { - if (m_ExtendedLen > oldLen) - { - delete[] m_ExtendedBufferPtr; - m_ExtendedBufferPtr = new uint8_t[m_ExtendedLen]; - } - } - else - m_ExtendedBufferPtr = new uint8_t[m_ExtendedLen]; - memcpy (m_ExtendedBufferPtr, other.m_ExtendedBufferPtr, m_ExtendedLen); - } - else - { - if (oldLen > MAX_EXTENDED_BUFFER_SIZE) delete[] m_ExtendedBufferPtr; - memcpy (m_ExtendedBuffer, other.m_ExtendedBuffer, m_ExtendedLen); - } + if (m_ExtendedLen > MAX_EXTENDED_BUFFER_SIZE) m_ExtendedLen = MAX_EXTENDED_BUFFER_SIZE; + memcpy (m_ExtendedBuffer, other.m_ExtendedBuffer, m_ExtendedLen); } m_Verifier = nullptr; CreateVerifier (); @@ -259,28 +227,13 @@ namespace data } memcpy (&m_StandardIdentity, buf, DEFAULT_IDENTITY_SIZE); - size_t oldLen = m_ExtendedLen; m_ExtendedLen = bufbe16toh (m_StandardIdentity.certificate + 1); if (m_ExtendedLen) { if (m_ExtendedLen + DEFAULT_IDENTITY_SIZE <= len) { - if (m_ExtendedLen > MAX_EXTENDED_BUFFER_SIZE) - { - if (oldLen > MAX_EXTENDED_BUFFER_SIZE) - { - if (m_ExtendedLen > oldLen) - { - delete[] m_ExtendedBufferPtr; - m_ExtendedBufferPtr = new uint8_t[m_ExtendedLen]; - } - } - else - m_ExtendedBufferPtr = new uint8_t[m_ExtendedLen]; - memcpy (m_ExtendedBufferPtr, buf + DEFAULT_IDENTITY_SIZE, m_ExtendedLen); - } - else - memcpy (m_ExtendedBuffer, buf + DEFAULT_IDENTITY_SIZE, m_ExtendedLen); + if (m_ExtendedLen > MAX_EXTENDED_BUFFER_SIZE) m_ExtendedLen = MAX_EXTENDED_BUFFER_SIZE; + memcpy (m_ExtendedBuffer, buf + DEFAULT_IDENTITY_SIZE, m_ExtendedLen); } else { @@ -305,28 +258,27 @@ namespace data if (fullLen > len) return 0; // buffer is too small and may overflow somewhere else memcpy (buf, &m_StandardIdentity, DEFAULT_IDENTITY_SIZE); if (m_ExtendedLen > 0) - { - if (m_ExtendedLen > MAX_EXTENDED_BUFFER_SIZE) - memcpy (buf + DEFAULT_IDENTITY_SIZE, m_ExtendedBufferPtr, m_ExtendedLen); - else - memcpy (buf + DEFAULT_IDENTITY_SIZE, m_ExtendedBuffer, m_ExtendedLen); - } + memcpy (buf + DEFAULT_IDENTITY_SIZE, m_ExtendedBuffer, m_ExtendedLen); return fullLen; } - size_t IdentityEx::FromBase64(std::string_view s) + size_t IdentityEx::FromBase64(const std::string& s) { - std::vector buf(s.length ()); // binary data can't exceed base64 - auto len = Base64ToByteStream (s, buf.data(), buf.size ()); + const size_t slen = s.length(); + std::vector buf(slen); // binary data can't exceed base64 + const size_t len = Base64ToByteStream (s.c_str(), slen, buf.data(), slen); return FromBuffer (buf.data(), len); } std::string IdentityEx::ToBase64 () const { const size_t bufLen = GetFullLen(); + const size_t strLen = Base64EncodingBufferSize(bufLen); std::vector buf(bufLen); + std::vector str(strLen); size_t l = ToBuffer (buf.data(), bufLen); - return i2p::data::ByteStreamToBase64 (buf.data(), l); + size_t l1 = i2p::data::ByteStreamToBase64 (buf.data(), l, str.data(), strLen); + return std::string (str.data(), l1); } size_t IdentityEx::GetSigningPublicKeyLen () const @@ -339,7 +291,7 @@ namespace data const uint8_t * IdentityEx::GetSigningPublicKeyBuffer () const { auto keyLen = GetSigningPublicKeyLen (); - if (keyLen > 128) return nullptr; // P521 or PQ + if (keyLen > 128) return nullptr; // P521 return m_StandardIdentity.signingKey + 128 - keyLen; } @@ -366,7 +318,7 @@ namespace data SigningKeyType IdentityEx::GetSigningKeyType () const { if (m_StandardIdentity.certificate[0] == CERTIFICATE_TYPE_KEY && m_ExtendedLen >= 2) - return bufbe16toh (m_ExtendedLen <= MAX_EXTENDED_BUFFER_SIZE ? m_ExtendedBuffer : m_ExtendedBufferPtr); // signing key + return bufbe16toh (m_ExtendedBuffer); // signing key return SIGNING_KEY_TYPE_DSA_SHA1; } @@ -379,7 +331,7 @@ namespace data CryptoKeyType IdentityEx::GetCryptoKeyType () const { if (m_StandardIdentity.certificate[0] == CERTIFICATE_TYPE_KEY && m_ExtendedLen >= 4) - return bufbe16toh (m_ExtendedLen <= MAX_EXTENDED_BUFFER_SIZE ? m_ExtendedBuffer + 2 : m_ExtendedBufferPtr + 2); // crypto key + return bufbe16toh (m_ExtendedBuffer + 2); // crypto key return CRYPTO_KEY_TYPE_ELGAMAL; } @@ -403,10 +355,6 @@ namespace data return new i2p::crypto::GOSTR3410_512_Verifier (i2p::crypto::eGOSTR3410TC26A512); case SIGNING_KEY_TYPE_REDDSA_SHA512_ED25519: return new i2p::crypto::RedDSA25519Verifier (); -#if OPENSSL_PQ - case SIGNING_KEY_TYPE_MLDSA44: - return new i2p::crypto::MLDSA44Verifier (); -#endif case SIGNING_KEY_TYPE_RSA_SHA256_2048: case SIGNING_KEY_TYPE_RSA_SHA384_3072: case SIGNING_KEY_TYPE_RSA_SHA512_4096: @@ -428,18 +376,6 @@ namespace data auto keyLen = verifier->GetPublicKeyLen (); if (keyLen <= 128) verifier->SetPublicKey (m_StandardIdentity.signingKey + 128 - keyLen); -#if OPENSSL_PQ - else if (keyLen > 384) - { - // for post-quantum - uint8_t * signingKey = new uint8_t[keyLen]; - memcpy (signingKey, m_StandardIdentity, 384); - size_t excessLen = keyLen - 384; - memcpy (signingKey + 384, m_ExtendedBufferPtr + 4, excessLen); // right after signing and crypto key types - verifier->SetPublicKey (signingKey); - delete[] signingKey; - } -#endif else { // for P521 @@ -463,14 +399,15 @@ namespace data return std::make_shared(key); break; case CRYPTO_KEY_TYPE_ECIES_X25519_AEAD: - case CRYPTO_KEY_TYPE_ECIES_MLKEM512_X25519_AEAD: - case CRYPTO_KEY_TYPE_ECIES_MLKEM768_X25519_AEAD: - case CRYPTO_KEY_TYPE_ECIES_MLKEM1024_X25519_AEAD: return std::make_shared(key); break; case CRYPTO_KEY_TYPE_ECIES_P256_SHA256_AES256CBC: + case CRYPTO_KEY_TYPE_ECIES_P256_SHA256_AES256CBC_TEST: return std::make_shared(key); break; + case CRYPTO_KEY_TYPE_ECIES_GOSTR3410_CRYPTO_PRO_A_SHA256_AES256CBC: + return std::make_shared(key); + break; default: LogPrint (eLogError, "Identity: Unknown crypto key type ", (int)keyType); }; @@ -495,9 +432,7 @@ namespace data { m_Public = std::make_shared(Identity (keys)); memcpy (m_PrivateKey, keys.privateKey, 256); // 256 - size_t keyLen = m_Public->GetSigningPrivateKeyLen (); - if (keyLen > 128) m_SigningPrivateKey.resize (keyLen); - memcpy (m_SigningPrivateKey.data (), keys.signingPrivateKey, keyLen); + memcpy (m_SigningPrivateKey, keys.signingPrivateKey, m_Public->GetSigningPrivateKeyLen ()); m_OfflineSignature.resize (0); m_TransientSignatureLen = 0; m_TransientSigningPrivateKeyLen = 0; @@ -513,7 +448,7 @@ namespace data m_OfflineSignature = other.m_OfflineSignature; m_TransientSignatureLen = other.m_TransientSignatureLen; m_TransientSigningPrivateKeyLen = other.m_TransientSigningPrivateKeyLen; - m_SigningPrivateKey = other.m_SigningPrivateKey; + memcpy (m_SigningPrivateKey, other.m_SigningPrivateKey, m_TransientSigningPrivateKeyLen > 0 ? m_TransientSigningPrivateKeyLen : m_Public->GetSigningPrivateKeyLen ()); m_Signer = nullptr; CreateSigner (); return *this; @@ -536,9 +471,8 @@ namespace data memcpy (m_PrivateKey, buf + ret, cryptoKeyLen); ret += cryptoKeyLen; size_t signingPrivateKeySize = m_Public->GetSigningPrivateKeyLen (); - if (signingPrivateKeySize + ret > len) return 0; // overflow - m_SigningPrivateKey.resize (signingPrivateKeySize); - memcpy (m_SigningPrivateKey.data (), buf + ret, signingPrivateKeySize); + if(signingPrivateKeySize + ret > len || signingPrivateKeySize > 128) return 0; // overflow + memcpy (m_SigningPrivateKey, buf + ret, signingPrivateKeySize); ret += signingPrivateKeySize; m_Signer = nullptr; // check if signing private key is all zeros @@ -579,9 +513,8 @@ namespace data memcpy (m_OfflineSignature.data (), offlineInfo, offlineInfoLen); // override signing private key m_TransientSigningPrivateKeyLen = transientVerifier->GetPrivateKeyLen (); - if (m_TransientSigningPrivateKeyLen + ret > len) return 0; - if (m_TransientSigningPrivateKeyLen > 128) m_SigningPrivateKey.resize (m_TransientSigningPrivateKeyLen); - memcpy (m_SigningPrivateKey.data (), buf + ret, m_TransientSigningPrivateKeyLen); + if (m_TransientSigningPrivateKeyLen + ret > len || m_TransientSigningPrivateKeyLen > 128) return 0; + memcpy (m_SigningPrivateKey, buf + ret, m_TransientSigningPrivateKeyLen); ret += m_TransientSigningPrivateKeyLen; CreateSigner (keyType); } @@ -601,7 +534,7 @@ namespace data if (IsOfflineSignature ()) memset (buf + ret, 0, signingPrivateKeySize); else - memcpy (buf + ret, m_SigningPrivateKey.data (), signingPrivateKeySize); + memcpy (buf + ret, m_SigningPrivateKey, signingPrivateKeySize); ret += signingPrivateKeySize; if (IsOfflineSignature ()) { @@ -612,24 +545,32 @@ namespace data ret += offlineSignatureLen; // transient private key if (ret + m_TransientSigningPrivateKeyLen > len) return 0; - memcpy (buf + ret, m_SigningPrivateKey.data (), m_TransientSigningPrivateKeyLen); + memcpy (buf + ret, m_SigningPrivateKey, m_TransientSigningPrivateKeyLen); ret += m_TransientSigningPrivateKeyLen; } return ret; } - size_t PrivateKeys::FromBase64(std::string_view s) + size_t PrivateKeys::FromBase64(const std::string& s) { - std::vector buf(s.length ()); - size_t l = i2p::data::Base64ToByteStream (s, buf.data (), buf.size ()); - return FromBuffer (buf.data (), l); + uint8_t * buf = new uint8_t[s.length ()]; + size_t l = i2p::data::Base64ToByteStream (s.c_str (), s.length (), buf, s.length ()); + size_t ret = FromBuffer (buf, l); + delete[] buf; + return ret; } std::string PrivateKeys::ToBase64 () const { - std::vector buf(GetFullLen ()); - size_t l = ToBuffer (buf.data (), buf.size ()); - return i2p::data::ByteStreamToBase64 (buf.data (), l); + uint8_t * buf = new uint8_t[GetFullLen ()]; + char * str = new char[GetFullLen ()*2]; + size_t l = ToBuffer (buf, GetFullLen ()); + size_t l1 = i2p::data::ByteStreamToBase64 (buf, l, str, GetFullLen ()*2); + str[l1] = 0; + delete[] buf; + std::string ret(str); + delete[] str; + return ret; } void PrivateKeys::Sign (const uint8_t * buf, int len, uint8_t * signature) const @@ -651,13 +592,13 @@ namespace data { if (m_Signer) return; if (keyType == SIGNING_KEY_TYPE_DSA_SHA1) - m_Signer.reset (new i2p::crypto::DSASigner (m_SigningPrivateKey.data (), m_Public->GetStandardIdentity ().signingKey)); + 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.data (), m_Public->GetStandardIdentity ().signingKey + (sizeof(Identity::signingKey) - 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 - auto signer = CreateSigner (keyType, m_SigningPrivateKey.data ()); + auto signer = CreateSigner (keyType, m_SigningPrivateKey); if (signer) m_Signer.reset (signer); } } @@ -692,11 +633,6 @@ namespace data case SIGNING_KEY_TYPE_REDDSA_SHA512_ED25519: return new i2p::crypto::RedDSA25519Signer (priv); break; -#if OPENSSL_PQ - case SIGNING_KEY_TYPE_MLDSA44: - return new i2p::crypto::MLDSA44Signer (priv); - break; -#endif default: LogPrint (eLogError, "Identity: Signing key type ", (int)keyType, " is not supported"); } @@ -710,7 +646,8 @@ namespace data size_t PrivateKeys::GetPrivateKeyLen () const { - return i2p::crypto::GetCryptoPrivateKeyLen (m_Public->GetCryptoKeyType ()); + // private key length always 256, but type 4 + return (m_Public->GetCryptoKeyType () == CRYPTO_KEY_TYPE_ECIES_X25519_AEAD) ? 32 : 256; } uint8_t * PrivateKeys::GetPadding() @@ -736,14 +673,15 @@ namespace data return std::make_shared(key); break; case CRYPTO_KEY_TYPE_ECIES_X25519_AEAD: - case CRYPTO_KEY_TYPE_ECIES_MLKEM512_X25519_AEAD: - case CRYPTO_KEY_TYPE_ECIES_MLKEM768_X25519_AEAD: - case CRYPTO_KEY_TYPE_ECIES_MLKEM1024_X25519_AEAD: return std::make_shared(key); break; case CRYPTO_KEY_TYPE_ECIES_P256_SHA256_AES256CBC: + case CRYPTO_KEY_TYPE_ECIES_P256_SHA256_AES256CBC_TEST: return std::make_shared(key); break; + case CRYPTO_KEY_TYPE_ECIES_GOSTR3410_CRYPTO_PRO_A_SHA256_AES256CBC: + return std::make_shared(key); + break; default: LogPrint (eLogError, "Identity: Unknown crypto key type ", (int)cryptoType); }; @@ -756,10 +694,8 @@ namespace data { PrivateKeys keys; // signature - std::unique_ptr verifier (IdentityEx::CreateVerifier (type)); - std::vector signingPublicKey(verifier->GetPublicKeyLen ()); - keys.m_SigningPrivateKey.resize (verifier->GetPrivateKeyLen ()); - GenerateSigningKeyPair (type, keys.m_SigningPrivateKey.data (), signingPublicKey.data ()); + uint8_t signingPublicKey[512]; // signing public key is 512 bytes max + GenerateSigningKeyPair (type, keys.m_SigningPrivateKey, signingPublicKey); // encryption uint8_t publicKey[256]; if (isDestination) @@ -767,7 +703,7 @@ namespace data else GenerateCryptoKeyPair (cryptoType, keys.m_PrivateKey, publicKey); // identity - keys.m_Public = std::make_shared (isDestination ? nullptr : publicKey, signingPublicKey.data (), type, cryptoType); + keys.m_Public = std::make_shared (isDestination ? nullptr : publicKey, signingPublicKey, type, cryptoType); keys.CreateSigner (); return keys; @@ -792,7 +728,9 @@ namespace data case SIGNING_KEY_TYPE_RSA_SHA384_3072: case SIGNING_KEY_TYPE_RSA_SHA512_4096: LogPrint (eLogWarning, "Identity: RSA signature type is not supported. Creating EdDSA"); +#if (__cplusplus >= 201703L) // C++ 17 or higher [[fallthrough]]; +#endif // no break here case SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519: i2p::crypto::CreateEDDSA25519RandomKeys (priv, pub); @@ -806,11 +744,6 @@ namespace data case SIGNING_KEY_TYPE_REDDSA_SHA512_ED25519: i2p::crypto::CreateRedDSA25519RandomKeys (priv, pub); break; -#if OPENSSL_PQ - case SIGNING_KEY_TYPE_MLDSA44: - i2p::crypto::CreateMLDSA44RandomKeys (priv, pub); - break; -#endif default: LogPrint (eLogWarning, "Identity: Signing key type ", (int)type, " is not supported. Create DSA-SHA1"); i2p::crypto::CreateDSARandomKeys (priv, pub); // DSA-SHA1 @@ -825,12 +758,13 @@ namespace data i2p::crypto::GenerateElGamalKeyPair(priv, pub); break; case CRYPTO_KEY_TYPE_ECIES_P256_SHA256_AES256CBC: + case CRYPTO_KEY_TYPE_ECIES_P256_SHA256_AES256CBC_TEST: i2p::crypto::CreateECIESP256RandomKeys (priv, pub); break; + case CRYPTO_KEY_TYPE_ECIES_GOSTR3410_CRYPTO_PRO_A_SHA256_AES256CBC: + i2p::crypto::CreateECIESGOSTR3410RandomKeys (priv, pub); + break; case CRYPTO_KEY_TYPE_ECIES_X25519_AEAD: - case CRYPTO_KEY_TYPE_ECIES_MLKEM512_X25519_AEAD: - case CRYPTO_KEY_TYPE_ECIES_MLKEM768_X25519_AEAD: - case CRYPTO_KEY_TYPE_ECIES_MLKEM1024_X25519_AEAD: i2p::crypto::CreateECIESX25519AEADRatchetRandomKeys (priv, pub); break; default: @@ -848,10 +782,9 @@ namespace data keys.m_TransientSigningPrivateKeyLen = verifier->GetPrivateKeyLen (); keys.m_TransientSignatureLen = verifier->GetSignatureLen (); keys.m_OfflineSignature.resize (pubKeyLen + m_Public->GetSignatureLen () + 6); - keys.m_SigningPrivateKey.resize (verifier->GetPrivateKeyLen ()); htobe32buf (keys.m_OfflineSignature.data (), expires); // expires htobe16buf (keys.m_OfflineSignature.data () + 4, type); // type - GenerateSigningKeyPair (type, keys.m_SigningPrivateKey.data (), keys.m_OfflineSignature.data () + 6); // public key + GenerateSigningKeyPair (type, keys.m_SigningPrivateKey, keys.m_OfflineSignature.data () + 6); // public key Sign (keys.m_OfflineSignature.data (), pubKeyLen + 6, keys.m_OfflineSignature.data () + 6 + pubKeyLen); // signature // recreate signer keys.m_Signer = nullptr; diff --git a/libi2pd/Identity.h b/libi2pd/Identity.h index c95ce000..5edd4545 100644 --- a/libi2pd/Identity.h +++ b/libi2pd/Identity.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2025, The PurpleI2P Project +* Copyright (c) 2013-2024, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -12,19 +12,14 @@ #include #include #include -#include #include #include #include "Base.h" #include "Signature.h" +#include "CryptoKey.h" namespace i2p { -namespace crypto -{ - class CryptoKeyEncryptor; - class CryptoKeyDecryptor; -} namespace data { typedef Tag<32> IdentHash; @@ -59,8 +54,6 @@ namespace data Identity& operator=(const Keys& keys); size_t FromBuffer (const uint8_t * buf, size_t len); IdentHash Hash () const; - operator uint8_t * () { return reinterpret_cast(this); } - operator const uint8_t * () const { return reinterpret_cast(this); } }; Keys CreateRandomKeys (); @@ -70,10 +63,9 @@ namespace data const uint16_t CRYPTO_KEY_TYPE_ELGAMAL = 0; const uint16_t CRYPTO_KEY_TYPE_ECIES_P256_SHA256_AES256CBC = 1; const uint16_t CRYPTO_KEY_TYPE_ECIES_X25519_AEAD = 4; - const uint16_t CRYPTO_KEY_TYPE_ECIES_MLKEM512_X25519_AEAD = 5; - const uint16_t CRYPTO_KEY_TYPE_ECIES_MLKEM768_X25519_AEAD = 6; - const uint16_t CRYPTO_KEY_TYPE_ECIES_MLKEM1024_X25519_AEAD = 7; - + const uint16_t CRYPTO_KEY_TYPE_ECIES_P256_SHA256_AES256CBC_TEST = 65280; // TODO: remove later + const uint16_t CRYPTO_KEY_TYPE_ECIES_GOSTR3410_CRYPTO_PRO_A_SHA256_AES256CBC = 65281; // TODO: use GOST R 34.11 instead SHA256 and GOST 28147-89 instead AES + const uint16_t SIGNING_KEY_TYPE_DSA_SHA1 = 0; const uint16_t SIGNING_KEY_TYPE_ECDSA_SHA256_P256 = 1; const uint16_t SIGNING_KEY_TYPE_ECDSA_SHA384_P384 = 2; @@ -82,12 +74,11 @@ namespace data const uint16_t SIGNING_KEY_TYPE_RSA_SHA384_3072 = 5; const uint16_t SIGNING_KEY_TYPE_RSA_SHA512_4096 = 6; const uint16_t SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519 = 7; - const uint16_t SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519ph = 8; // since openssl 3.0.0 + const uint16_t SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519ph = 8; // not implemented const uint16_t SIGNING_KEY_TYPE_GOSTR3410_CRYPTO_PRO_A_GOSTR3411_256 = 9; const uint16_t SIGNING_KEY_TYPE_GOSTR3410_TC26_A_512_GOSTR3411_512 = 10; // approved by FSB const uint16_t SIGNING_KEY_TYPE_REDDSA_SHA512_ED25519 = 11; // for LeaseSet2 only - const uint16_t SIGNING_KEY_TYPE_MLDSA44 = 12; - + typedef uint16_t SigningKeyType; typedef uint16_t CryptoKeyType; @@ -108,7 +99,7 @@ namespace data size_t FromBuffer (const uint8_t * buf, size_t len); size_t ToBuffer (uint8_t * buf, size_t len) const; - size_t FromBase64(std::string_view s); + size_t FromBase64(const std::string& s); std::string ToBase64 () const; const Identity& GetStandardIdentity () const { return m_StandardIdentity; }; @@ -142,11 +133,7 @@ namespace data IdentHash m_IdentHash; std::unique_ptr m_Verifier; size_t m_ExtendedLen; - union - { - uint8_t m_ExtendedBuffer[MAX_EXTENDED_BUFFER_SIZE]; - uint8_t * m_ExtendedBufferPtr; - }; + uint8_t m_ExtendedBuffer[MAX_EXTENDED_BUFFER_SIZE]; }; size_t GetIdentityBufferLen (const uint8_t * buf, size_t len); // return actual identity length in buffer @@ -164,7 +151,7 @@ namespace data std::shared_ptr GetPublic () const { return m_Public; }; const uint8_t * GetPrivateKey () const { return m_PrivateKey; }; - const uint8_t * GetSigningPrivateKey () const { return m_SigningPrivateKey.data (); }; + const uint8_t * GetSigningPrivateKey () const { return m_SigningPrivateKey; }; size_t GetSignatureLen () const; // might not match identity bool IsOfflineSignature () const { return m_TransientSignatureLen > 0; }; uint8_t * GetPadding(); @@ -175,7 +162,7 @@ namespace data size_t FromBuffer (const uint8_t * buf, size_t len); size_t ToBuffer (uint8_t * buf, size_t len) const; - size_t FromBase64(std::string_view s); + size_t FromBase64(const std::string& s); std::string ToBase64 () const; std::shared_ptr CreateDecryptor (const uint8_t * key) const; @@ -200,7 +187,7 @@ namespace data std::shared_ptr m_Public; uint8_t m_PrivateKey[256]; - std::vector m_SigningPrivateKey; + uint8_t m_SigningPrivateKey[128]; // assume private key doesn't exceed 128 bytes mutable std::unique_ptr m_Signer; std::vector m_OfflineSignature; // non zero length, if applicable size_t m_TransientSignatureLen = 0; diff --git a/libi2pd/LeaseSet.cpp b/libi2pd/LeaseSet.cpp index fc0e722d..66faed84 100644 --- a/libi2pd/LeaseSet.cpp +++ b/libi2pd/LeaseSet.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2025, The PurpleI2P Project +* Copyright (c) 2013-2024, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -14,7 +14,6 @@ #include "Timestamp.h" #include "NetDb.hpp" #include "Tunnel.h" -#include "CryptoKey.h" #include "LeaseSet.h" namespace i2p @@ -400,7 +399,6 @@ namespace data offset += propertiesLen; // skip for now. TODO: implement properties // key sections CryptoKeyType preferredKeyType = m_EncryptionType; - m_EncryptionType = 0; bool preferredKeyFound = false; if (offset + 1 > len) return 0; int numKeySections = buf[offset]; offset++; @@ -412,22 +410,14 @@ namespace data if (offset + encryptionKeyLen > len) return 0; if (IsStoreLeases () && !preferredKeyFound) // create encryptor with leases only { - // we pick max key type if preferred not found -#if !OPENSSL_PQ - if (keyType <= i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD) // skip PQ keys if not supported -#endif - { - if (keyType == preferredKeyType || !m_Encryptor || keyType > m_EncryptionType) - { - auto encryptor = i2p::data::IdentityEx::CreateEncryptor (keyType, buf + offset); - if (encryptor) - { - m_Encryptor = encryptor; // TODO: atomic - m_EncryptionType = keyType; - if (keyType == preferredKeyType) preferredKeyFound = true; - } - } - } + // we pick first valid key if preferred not found + auto encryptor = i2p::data::IdentityEx::CreateEncryptor (keyType, buf + offset); + if (encryptor && (!m_Encryptor || keyType == preferredKeyType)) + { + m_Encryptor = encryptor; // TODO: atomic + m_EncryptionType = keyType; + if (keyType == preferredKeyType) preferredKeyFound = true; + } } offset += encryptionKeyLen; } @@ -435,16 +425,6 @@ namespace data if (offset + 1 > len) return 0; int numLeases = buf[offset]; offset++; auto ts = i2p::util::GetMillisecondsSinceEpoch (); - if (GetExpirationTime () > ts + LEASESET_EXPIRATION_TIME_THRESHOLD) - { - LogPrint (eLogWarning, "LeaseSet2: Expiration time is from future ", GetExpirationTime ()/1000LL); - return 0; - } - if (ts > m_PublishedTimestamp*1000LL + LEASESET_EXPIRATION_TIME_THRESHOLD) - { - LogPrint (eLogWarning, "LeaseSet2: Published time is too old ", m_PublishedTimestamp); - return 0; - } if (IsStoreLeases ()) { UpdateLeasesBegin (); @@ -455,11 +435,6 @@ namespace data lease.tunnelGateway = buf + offset; offset += 32; // gateway lease.tunnelID = bufbe32toh (buf + offset); offset += 4; // tunnel ID lease.endDate = bufbe32toh (buf + offset)*1000LL; offset += 4; // end date - if (lease.endDate > ts + LEASESET_EXPIRATION_TIME_THRESHOLD) - { - LogPrint (eLogWarning, "LeaseSet2: Lease end date is from future ", lease.endDate); - return 0; - } UpdateLease (lease, ts); } UpdateLeasesEnd (); @@ -848,7 +823,7 @@ namespace data } LocalLeaseSet2::LocalLeaseSet2 (uint8_t storeType, const i2p::data::PrivateKeys& keys, - const EncryptionKeys& encryptionKeys, const std::vector >& tunnels, + const KeySections& encryptionKeys, const std::vector >& tunnels, bool isPublic, uint64_t publishedTimestamp, bool isPublishedEncrypted): LocalLeaseSet (keys.GetPublic (), nullptr, 0) { @@ -858,7 +833,7 @@ namespace data if (num > MAX_NUM_LEASES) num = MAX_NUM_LEASES; size_t keySectionsLen = 0; for (const auto& it: encryptionKeys) - keySectionsLen += 2/*key type*/ + 2/*key len*/ + it->pub.size()/*key*/; + keySectionsLen += 2/*key type*/ + 2/*key len*/ + it.keyLen/*key*/; m_BufferLen = identity->GetFullLen () + 4/*published*/ + 2/*expires*/ + 2/*flag*/ + 2/*properties len*/ + 1/*num keys*/ + keySectionsLen + 1/*num leases*/ + num*LEASE2_SIZE + keys.GetSignatureLen (); uint16_t flags = 0; @@ -893,9 +868,9 @@ namespace data m_Buffer[offset] = encryptionKeys.size (); offset++; // 1 key for (const auto& it: encryptionKeys) { - htobe16buf (m_Buffer + offset, it->keyType); offset += 2; // key type - htobe16buf (m_Buffer + offset, it->pub.size()); offset += 2; // key len - memcpy (m_Buffer + offset, it->pub.data(), it->pub.size()); offset += it->pub.size(); // key + htobe16buf (m_Buffer + offset, it.keyType); offset += 2; // key type + htobe16buf (m_Buffer + offset, it.keyLen); offset += 2; // key len + memcpy (m_Buffer + offset, it.encryptionPublicKey, it.keyLen); offset += it.keyLen; // key } // leases uint32_t expirationTime = 0; // in seconds diff --git a/libi2pd/LeaseSet.h b/libi2pd/LeaseSet.h index f5197eb5..7eea3aed 100644 --- a/libi2pd/LeaseSet.h +++ b/libi2pd/LeaseSet.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2025, The PurpleI2P Project +* Copyright (c) 2013-2024, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -12,14 +12,12 @@ #include #include #include -#include #include #include #include "Identity.h" #include "Timestamp.h" #include "I2PEndian.h" #include "Blinding.h" -#include "CryptoKey.h" namespace i2p { @@ -59,16 +57,12 @@ namespace data }; typedef std::function LeaseInspectFunc; -#if OPENSSL_PQ - const size_t MAX_LS_BUFFER_SIZE = 8192; -#else - const size_t MAX_LS_BUFFER_SIZE = 4096; -#endif + + const size_t MAX_LS_BUFFER_SIZE = 3072; const size_t LEASE_SIZE = 44; // 32 + 4 + 8 const size_t LEASE2_SIZE = 40; // 32 + 4 + 4 const uint8_t MAX_NUM_LEASES = 16; - const uint64_t LEASESET_EXPIRATION_TIME_THRESHOLD = 12*60*1000; // in milliseconds - + const uint8_t NETDB_STORE_TYPE_LEASESET = 1; class LeaseSet: public RoutingDestination { @@ -155,8 +149,8 @@ namespace data public: LeaseSet2 (uint8_t storeType): LeaseSet (true), m_StoreType (storeType) {}; // for update - LeaseSet2 (uint8_t storeType, const uint8_t * buf, size_t len, bool storeLeases = true, CryptoKeyType preferredCrypto = CRYPTO_KEY_TYPE_ECIES_X25519_AEAD); - LeaseSet2 (const uint8_t * buf, size_t len, std::shared_ptr key, const uint8_t * secret = nullptr, CryptoKeyType preferredCrypto = CRYPTO_KEY_TYPE_ECIES_X25519_AEAD); // store type 5, called from local netdb only + LeaseSet2 (uint8_t storeType, const uint8_t * buf, size_t len, bool storeLeases = true, CryptoKeyType preferredCrypto = CRYPTO_KEY_TYPE_ELGAMAL); + LeaseSet2 (const uint8_t * buf, size_t len, std::shared_ptr key, const uint8_t * secret = nullptr, CryptoKeyType preferredCrypto = CRYPTO_KEY_TYPE_ELGAMAL); // store type 5, called from local netdb only uint8_t GetStoreType () const { return m_StoreType; }; uint32_t GetPublishedTimestamp () const { return m_PublishedTimestamp; }; bool IsPublic () const { return m_IsPublic; }; @@ -186,7 +180,7 @@ namespace data private: uint8_t m_StoreType; - uint32_t m_PublishedTimestamp = 0; // seconds + uint32_t m_PublishedTimestamp = 0; bool m_IsPublic = true, m_IsPublishedEncrypted = false; std::shared_ptr m_TransientVerifier; CryptoKeyType m_EncryptionType; @@ -252,10 +246,15 @@ namespace data { public: - typedef std::list > EncryptionKeys; + struct KeySection + { + uint16_t keyType, keyLen; + const uint8_t * encryptionPublicKey; + }; + typedef std::vector KeySections; LocalLeaseSet2 (uint8_t storeType, const i2p::data::PrivateKeys& keys, - const EncryptionKeys& encryptionKeys, + const KeySections& encryptionKeys, const std::vector >& tunnels, bool isPublic, uint64_t publishedTimestamp, bool isPublishedEncrypted = false); diff --git a/libi2pd/NTCP2.cpp b/libi2pd/NTCP2.cpp index b3a51488..728ac01d 100644 --- a/libi2pd/NTCP2.cpp +++ b/libi2pd/NTCP2.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2025, The PurpleI2P Project +* Copyright (c) 2013-2024, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -42,29 +42,28 @@ namespace transport delete[] m_SessionConfirmedBuffer; } - bool NTCP2Establisher::KeyDerivationFunction1 (const uint8_t * pub, i2p::crypto::X25519Keys& priv, const uint8_t * rs, const uint8_t * epub) + void NTCP2Establisher::KeyDerivationFunction1 (const uint8_t * pub, i2p::crypto::X25519Keys& priv, const uint8_t * rs, const uint8_t * epub) { i2p::crypto::InitNoiseXKState (*this, rs); // h = SHA256(h || epub) MixHash (epub, 32); // x25519 between pub and priv uint8_t inputKeyMaterial[32]; - if (!priv.Agree (pub, inputKeyMaterial)) return false; + priv.Agree (pub, inputKeyMaterial); MixKey (inputKeyMaterial); - return true; } - bool NTCP2Establisher::KDF1Alice () + void NTCP2Establisher::KDF1Alice () { - return KeyDerivationFunction1 (m_RemoteStaticKey, *m_EphemeralKeys, m_RemoteStaticKey, GetPub ()); + KeyDerivationFunction1 (m_RemoteStaticKey, *m_EphemeralKeys, m_RemoteStaticKey, GetPub ()); } - bool NTCP2Establisher::KDF1Bob () + void NTCP2Establisher::KDF1Bob () { - return KeyDerivationFunction1 (GetRemotePub (), i2p::context.GetNTCP2StaticKeys (), i2p::context.GetNTCP2StaticPublicKey (), GetRemotePub ()); + KeyDerivationFunction1 (GetRemotePub (), i2p::context.GetNTCP2StaticKeys (), i2p::context.GetNTCP2StaticPublicKey (), GetRemotePub ()); } - bool NTCP2Establisher::KeyDerivationFunction2 (const uint8_t * sessionRequest, size_t sessionRequestLen, const uint8_t * epub) + void NTCP2Establisher::KeyDerivationFunction2 (const uint8_t * sessionRequest, size_t sessionRequestLen, const uint8_t * epub) { MixHash (sessionRequest + 32, 32); // encrypted payload @@ -75,35 +74,33 @@ namespace transport // x25519 between remote pub and ephemaral priv uint8_t inputKeyMaterial[32]; - if (!m_EphemeralKeys->Agree (GetRemotePub (), inputKeyMaterial)) return false; + m_EphemeralKeys->Agree (GetRemotePub (), inputKeyMaterial); + MixKey (inputKeyMaterial); - return true; } - bool NTCP2Establisher::KDF2Alice () + void NTCP2Establisher::KDF2Alice () { - return KeyDerivationFunction2 (m_SessionRequestBuffer, m_SessionRequestBufferLen, GetRemotePub ()); + KeyDerivationFunction2 (m_SessionRequestBuffer, m_SessionRequestBufferLen, GetRemotePub ()); } - bool NTCP2Establisher::KDF2Bob () + void NTCP2Establisher::KDF2Bob () { - return KeyDerivationFunction2 (m_SessionRequestBuffer, m_SessionRequestBufferLen, GetPub ()); + KeyDerivationFunction2 (m_SessionRequestBuffer, m_SessionRequestBufferLen, GetPub ()); } - bool NTCP2Establisher::KDF3Alice () + void NTCP2Establisher::KDF3Alice () { uint8_t inputKeyMaterial[32]; - if (!i2p::context.GetNTCP2StaticKeys ().Agree (GetRemotePub (), inputKeyMaterial)) return false; + i2p::context.GetNTCP2StaticKeys ().Agree (GetRemotePub (), inputKeyMaterial); MixKey (inputKeyMaterial); - return true; } - bool NTCP2Establisher::KDF3Bob () + void NTCP2Establisher::KDF3Bob () { uint8_t inputKeyMaterial[32]; - if (!m_EphemeralKeys->Agree (m_RemoteStaticKey, inputKeyMaterial)) return false; + m_EphemeralKeys->Agree (m_RemoteStaticKey, inputKeyMaterial); MixKey (inputKeyMaterial); - return true; } void NTCP2Establisher::CreateEphemeralKey () @@ -111,7 +108,7 @@ namespace transport m_EphemeralKeys = i2p::transport::transports.GetNextX25519KeysPair (); } - bool NTCP2Establisher::CreateSessionRequestMessage (std::mt19937& rng) + void NTCP2Establisher::CreateSessionRequestMessage (std::mt19937& rng) { // create buffer and fill padding auto paddingLength = rng () % (NTCP2_SESSION_REQUEST_MAX_SIZE - 64); // message length doesn't exceed 287 bytes @@ -120,10 +117,11 @@ namespace transport // encrypt X i2p::crypto::CBCEncryption encryption; encryption.SetKey (m_RemoteIdentHash); - encryption.Encrypt (GetPub (), 32, m_IV, m_SessionRequestBuffer); // X - memcpy (m_IV, m_SessionRequestBuffer + 16, 16); // save last block as IV for SessionCreated + encryption.SetIV (m_IV); + encryption.Encrypt (GetPub (), 32, m_SessionRequestBuffer); // X + encryption.GetIV (m_IV); // save IV for SessionCreated // encryption key for next block - if (!KDF1Alice ()) return false; + KDF1Alice (); // fill options uint8_t options[32]; // actual options size is 16 bytes memset (options, 0, 16); @@ -145,16 +143,13 @@ namespace transport // 2 bytes reserved htobe32buf (options + 8, (i2p::util::GetMillisecondsSinceEpoch () + 500)/1000); // tsA, rounded to seconds // 4 bytes reserved - // encrypt options - if (!Encrypt (options, m_SessionRequestBuffer + 32, 16)) - { - LogPrint (eLogWarning, "NTCP2: SessionRequest failed to encrypt options"); - return false; - } - return true; + // sign and encrypt options, use m_H as AD + uint8_t nonce[12]; + memset (nonce, 0, 12); // set nonce to zero + i2p::crypto::AEADChaCha20Poly1305 (options, 16, GetH (), 32, GetK (), nonce, m_SessionRequestBuffer + 32, 32, true); // encrypt } - bool NTCP2Establisher::CreateSessionCreatedMessage (std::mt19937& rng) + void NTCP2Establisher::CreateSessionCreatedMessage (std::mt19937& rng) { auto paddingLen = rng () % (NTCP2_SESSION_CREATED_MAX_SIZE - 64); m_SessionCreatedBufferLen = paddingLen + 64; @@ -162,23 +157,22 @@ namespace transport // encrypt Y i2p::crypto::CBCEncryption encryption; encryption.SetKey (i2p::context.GetIdentHash ()); - encryption.Encrypt (GetPub (), 32, m_IV, m_SessionCreatedBuffer); // Y + encryption.SetIV (m_IV); + encryption.Encrypt (GetPub (), 32, m_SessionCreatedBuffer); // Y // encryption key for next block (m_K) - if (!KDF2Bob ()) return false; + KDF2Bob (); uint8_t options[16]; memset (options, 0, 16); htobe16buf (options + 2, paddingLen); // padLen htobe32buf (options + 8, (i2p::util::GetMillisecondsSinceEpoch () + 500)/1000); // tsB, rounded to seconds - // encrypt options - if (!Encrypt (options, m_SessionCreatedBuffer + 32, 16)) - { - LogPrint (eLogWarning, "NTCP2: SessionCreated failed to encrypt options"); - return false; - } - return true; + // sign and encrypt options, use m_H as AD + uint8_t nonce[12]; + memset (nonce, 0, 12); // set nonce to zero + i2p::crypto::AEADChaCha20Poly1305 (options, 16, GetH (), 32, GetK (), nonce, m_SessionCreatedBuffer + 32, 32, true); // encrypt + } - bool NTCP2Establisher::CreateSessionConfirmedMessagePart1 () + void NTCP2Establisher::CreateSessionConfirmedMessagePart1 (const uint8_t * nonce) { // update AD MixHash (m_SessionCreatedBuffer + 32, 32); // encrypted payload @@ -186,31 +180,21 @@ namespace transport if (paddingLength > 0) MixHash (m_SessionCreatedBuffer + 64, paddingLength); - // part1 48 bytes, n = 1 - if (!Encrypt (i2p::context.GetNTCP2StaticPublicKey (), m_SessionConfirmedBuffer, 32)) - { - LogPrint (eLogWarning, "NTCP2: SessionConfirmed failed to encrypt part1"); - return false; - } - return true; + // part1 48 bytes + i2p::crypto::AEADChaCha20Poly1305 (i2p::context.GetNTCP2StaticPublicKey (), 32, GetH (), 32, GetK (), nonce, m_SessionConfirmedBuffer, 48, true); // encrypt } - bool NTCP2Establisher::CreateSessionConfirmedMessagePart2 () + void NTCP2Establisher::CreateSessionConfirmedMessagePart2 (const uint8_t * nonce) { // part 2 // update AD again MixHash (m_SessionConfirmedBuffer, 48); // encrypt m3p2, it must be filled in SessionRequest - if (!KDF3Alice ()) return false; // MixKey, n = 0 + KDF3Alice (); uint8_t * m3p2 = m_SessionConfirmedBuffer + 48; - if (!Encrypt (m3p2, m3p2, m3p2Len - 16)) - { - LogPrint (eLogWarning, "NTCP2: SessionConfirmed failed to encrypt part2"); - return false; - } + i2p::crypto::AEADChaCha20Poly1305 (m3p2, m3p2Len - 16, GetH (), 32, GetK (), nonce, m3p2, m3p2Len, true); // encrypt // update h again MixHash (m3p2, m3p2Len); //h = SHA256(h || ciphertext) - return true; } bool NTCP2Establisher::ProcessSessionRequestMessage (uint16_t& paddingLen, bool& clockSkew) @@ -219,17 +203,15 @@ namespace transport // decrypt X i2p::crypto::CBCDecryption decryption; decryption.SetKey (i2p::context.GetIdentHash ()); - decryption.Decrypt (m_SessionRequestBuffer, 32, i2p::context.GetNTCP2IV (), GetRemotePub ()); - memcpy (m_IV, m_SessionRequestBuffer + 16, 16); // save last block as IV for SessionCreated + decryption.SetIV (i2p::context.GetNTCP2IV ()); + decryption.Decrypt (m_SessionRequestBuffer, 32, GetRemotePub ()); + decryption.GetIV (m_IV); // save IV for SessionCreated // decryption key for next block - if (!KDF1Bob ()) - { - LogPrint (eLogWarning, "NTCP2: SessionRequest KDF failed"); - return false; - } - // verify MAC and decrypt options block (32 bytes) - uint8_t options[16]; - if (Decrypt (m_SessionRequestBuffer + 32, options, 16)) + KDF1Bob (); + // verify MAC and decrypt options block (32 bytes), use m_H as AD + uint8_t nonce[12], options[16]; + memset (nonce, 0, 12); // set nonce to zero + if (i2p::crypto::AEADChaCha20Poly1305 (m_SessionRequestBuffer + 32, 16, GetH (), 32, GetK (), nonce, options, 16, false)) // decrypt { // options if (options[0] && options[0] != i2p::context.GetNetID ()) @@ -277,16 +259,15 @@ namespace transport // decrypt Y i2p::crypto::CBCDecryption decryption; decryption.SetKey (m_RemoteIdentHash); - decryption.Decrypt (m_SessionCreatedBuffer, 32, m_IV, GetRemotePub ()); + decryption.SetIV (m_IV); + decryption.Decrypt (m_SessionCreatedBuffer, 32, GetRemotePub ()); // decryption key for next block (m_K) - if (!KDF2Alice ()) - { - LogPrint (eLogWarning, "NTCP2: SessionCreated KDF failed"); - return false; - } + KDF2Alice (); // decrypt and verify MAC uint8_t payload[16]; - if (Decrypt (m_SessionCreatedBuffer + 32, payload, 16)) + uint8_t nonce[12]; + memset (nonce, 0, 12); // set nonce to zero + if (i2p::crypto::AEADChaCha20Poly1305 (m_SessionCreatedBuffer + 32, 16, GetH (), 32, GetK (), nonce, payload, 16, false)) // decrypt { // options paddingLen = bufbe16toh(payload + 2); @@ -307,7 +288,7 @@ namespace transport return true; } - bool NTCP2Establisher::ProcessSessionConfirmedMessagePart1 () + bool NTCP2Establisher::ProcessSessionConfirmedMessagePart1 (const uint8_t * nonce) { // update AD MixHash (m_SessionCreatedBuffer + 32, 32); // encrypted payload @@ -315,8 +296,7 @@ namespace transport if (paddingLength > 0) MixHash (m_SessionCreatedBuffer + 64, paddingLength); - // decrypt S, n = 1 - if (!Decrypt (m_SessionConfirmedBuffer, m_RemoteStaticKey, 32)) + if (!i2p::crypto::AEADChaCha20Poly1305 (m_SessionConfirmedBuffer, 32, GetH (), 32, GetK (), nonce, m_RemoteStaticKey, 32, false)) // decrypt S { LogPrint (eLogWarning, "NTCP2: SessionConfirmed Part1 AEAD verification failed "); return false; @@ -324,17 +304,13 @@ namespace transport return true; } - bool NTCP2Establisher::ProcessSessionConfirmedMessagePart2 (uint8_t * m3p2Buf) + bool NTCP2Establisher::ProcessSessionConfirmedMessagePart2 (const uint8_t * nonce, uint8_t * m3p2Buf) { // update AD again MixHash (m_SessionConfirmedBuffer, 48); - if (!KDF3Bob ()) // MixKey, n = 0 - { - LogPrint (eLogWarning, "NTCP2: SessionConfirmed Part2 KDF failed"); - return false; - } - if (Decrypt (m_SessionConfirmedBuffer + 48, m3p2Buf, m3p2Len - 16)) + KDF3Bob (); + if (i2p::crypto::AEADChaCha20Poly1305 (m_SessionConfirmedBuffer + 48, m3p2Len - 16, GetH (), 32, GetK (), nonce, m3p2Buf, m3p2Len - 16, false)) // decrypt // calculate new h again for KDF data MixHash (m_SessionConfirmedBuffer + 48, m3p2Len); // h = SHA256(h || ciphertext) else @@ -351,7 +327,6 @@ namespace transport m_Server (server), m_Socket (m_Server.GetService ()), m_IsEstablished (false), m_IsTerminated (false), m_Establisher (new NTCP2Establisher), - m_SendKey (nullptr), m_ReceiveKey (nullptr), #if OPENSSL_SIPHASH m_SendMDCtx(nullptr), m_ReceiveMDCtx (nullptr), #else @@ -400,8 +375,6 @@ namespace transport m_Socket.close (); transports.PeerDisconnected (shared_from_this ()); m_Server.RemoveNTCP2Session (shared_from_this ()); - if (!m_IntermediateQueue.empty ()) - m_SendQueue.splice (m_SendQueue.end (), m_IntermediateQueue); for (auto& it: m_SendQueue) it->Drop (); m_SendQueue.clear (); @@ -431,7 +404,7 @@ namespace transport void NTCP2Session::Done () { - boost::asio::post (m_Server.GetService (), std::bind (&NTCP2Session::Terminate, shared_from_this ())); + m_Server.GetService ().post (std::bind (&NTCP2Session::Terminate, shared_from_this ())); } void NTCP2Session::Established () @@ -491,12 +464,7 @@ namespace transport void NTCP2Session::SendSessionRequest () { - if (!m_Establisher->CreateSessionRequestMessage (m_Server.GetRng ())) - { - LogPrint (eLogWarning, "NTCP2: Send SessionRequest KDF failed"); - boost::asio::post (m_Server.GetService (), std::bind (&NTCP2Session::Terminate, shared_from_this ())); - return; - } + m_Establisher->CreateSessionRequestMessage (m_Server.GetRng ()); // send message m_HandshakeInterval = i2p::util::GetMillisecondsSinceEpoch (); boost::asio::async_write (m_Socket, boost::asio::buffer (m_Establisher->m_SessionRequestBuffer, m_Establisher->m_SessionRequestBufferLen), boost::asio::transfer_all (), @@ -521,6 +489,7 @@ namespace transport void NTCP2Session::HandleSessionRequestReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred) { + (void) bytes_transferred; if (ecode) { LogPrint (eLogWarning, "NTCP2: SessionRequest read error: ", ecode.message ()); @@ -528,47 +497,38 @@ namespace transport } else { - boost::asio::post (m_Server.GetEstablisherService (), - [s = shared_from_this (), bytes_transferred] () + LogPrint (eLogDebug, "NTCP2: SessionRequest received ", bytes_transferred); + uint16_t paddingLen = 0; + bool clockSkew = false; + if (m_Establisher->ProcessSessionRequestMessage (paddingLen, clockSkew)) + { + if (clockSkew) { - s->ProcessSessionRequest (bytes_transferred);; - }); + // we don't care about padding, send SessionCreated and close session + SendSessionCreated (); + m_Server.GetService ().post (std::bind (&NTCP2Session::Terminate, shared_from_this ())); + } + else if (paddingLen > 0) + { + if (paddingLen <= NTCP2_SESSION_REQUEST_MAX_SIZE - 64) // session request is 287 bytes max + { + boost::asio::async_read (m_Socket, boost::asio::buffer(m_Establisher->m_SessionRequestBuffer + 64, paddingLen), boost::asio::transfer_all (), + std::bind(&NTCP2Session::HandleSessionRequestPaddingReceived, shared_from_this (), std::placeholders::_1, std::placeholders::_2)); + } + else + { + LogPrint (eLogWarning, "NTCP2: SessionRequest padding length ", (int)paddingLen, " is too long"); + Terminate (); + } + } + else + SendSessionCreated (); + } + else + Terminate (); } } - void NTCP2Session::ProcessSessionRequest (size_t len) - { - LogPrint (eLogDebug, "NTCP2: SessionRequest received ", len); - uint16_t paddingLen = 0; - bool clockSkew = false; - if (m_Establisher->ProcessSessionRequestMessage (paddingLen, clockSkew)) - { - if (clockSkew) - { - // we don't care about padding, send SessionCreated and close session - SendSessionCreated (); - boost::asio::post (m_Server.GetService (), std::bind (&NTCP2Session::Terminate, shared_from_this ())); - } - else if (paddingLen > 0) - { - if (paddingLen <= NTCP2_SESSION_REQUEST_MAX_SIZE - 64) // session request is 287 bytes max - { - boost::asio::async_read (m_Socket, boost::asio::buffer(m_Establisher->m_SessionRequestBuffer + 64, paddingLen), boost::asio::transfer_all (), - std::bind(&NTCP2Session::HandleSessionRequestPaddingReceived, shared_from_this (), std::placeholders::_1, std::placeholders::_2)); - } - else - { - LogPrint (eLogWarning, "NTCP2: SessionRequest padding length ", (int)paddingLen, " is too long"); - boost::asio::post (m_Server.GetService (), std::bind (&NTCP2Session::Terminate, shared_from_this ())); - } - } - else - SendSessionCreated (); - } - else - boost::asio::post (m_Server.GetService (), std::bind (&NTCP2Session::Terminate, shared_from_this ())); - } - void NTCP2Session::HandleSessionRequestPaddingReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred) { if (ecode) @@ -577,23 +537,12 @@ namespace transport Terminate (); } else - { - boost::asio::post (m_Server.GetEstablisherService (), - [s = shared_from_this ()] () - { - s->SendSessionCreated (); - }); - } + SendSessionCreated (); } void NTCP2Session::SendSessionCreated () { - if (!m_Establisher->CreateSessionCreatedMessage (m_Server.GetRng ())) - { - LogPrint (eLogWarning, "NTCP2: Send SessionCreated KDF failed"); - boost::asio::post (m_Server.GetService (), std::bind (&NTCP2Session::Terminate, shared_from_this ())); - return; - } + m_Establisher->CreateSessionCreatedMessage (m_Server.GetRng ()); // send message m_HandshakeInterval = i2p::util::GetMillisecondsSinceEpoch (); boost::asio::async_write (m_Socket, boost::asio::buffer (m_Establisher->m_SessionCreatedBuffer, m_Establisher->m_SessionCreatedBufferLen), boost::asio::transfer_all (), @@ -610,44 +559,35 @@ namespace transport else { m_HandshakeInterval = i2p::util::GetMillisecondsSinceEpoch () - m_HandshakeInterval; - boost::asio::post (m_Server.GetEstablisherService (), - [s = shared_from_this (), bytes_transferred] () + LogPrint (eLogDebug, "NTCP2: SessionCreated received ", bytes_transferred); + uint16_t paddingLen = 0; + if (m_Establisher->ProcessSessionCreatedMessage (paddingLen)) + { + if (paddingLen > 0) { - s->ProcessSessionCreated (bytes_transferred); - }); + if (paddingLen <= NTCP2_SESSION_CREATED_MAX_SIZE - 64) // session created is 287 bytes max + { + boost::asio::async_read (m_Socket, boost::asio::buffer(m_Establisher->m_SessionCreatedBuffer + 64, paddingLen), boost::asio::transfer_all (), + std::bind(&NTCP2Session::HandleSessionCreatedPaddingReceived, shared_from_this (), std::placeholders::_1, std::placeholders::_2)); + } + else + { + LogPrint (eLogWarning, "NTCP2: SessionCreated padding length ", (int)paddingLen, " is too long"); + Terminate (); + } + } + else + SendSessionConfirmed (); + } + else + { + if (GetRemoteIdentity ()) + i2p::data::netdb.SetUnreachable (GetRemoteIdentity ()->GetIdentHash (), true); // assume wrong s key + Terminate (); + } } } - void NTCP2Session::ProcessSessionCreated (size_t len) - { - LogPrint (eLogDebug, "NTCP2: SessionCreated received ", len); - uint16_t paddingLen = 0; - if (m_Establisher->ProcessSessionCreatedMessage (paddingLen)) - { - if (paddingLen > 0) - { - if (paddingLen <= NTCP2_SESSION_CREATED_MAX_SIZE - 64) // session created is 287 bytes max - { - boost::asio::async_read (m_Socket, boost::asio::buffer(m_Establisher->m_SessionCreatedBuffer + 64, paddingLen), boost::asio::transfer_all (), - std::bind(&NTCP2Session::HandleSessionCreatedPaddingReceived, shared_from_this (), std::placeholders::_1, std::placeholders::_2)); - } - else - { - LogPrint (eLogWarning, "NTCP2: SessionCreated padding length ", (int)paddingLen, " is too long"); - boost::asio::post (m_Server.GetService (), std::bind (&NTCP2Session::Terminate, shared_from_this ())); - } - } - else - SendSessionConfirmed (); - } - else - { - if (GetRemoteIdentity ()) - i2p::data::netdb.SetUnreachable (GetRemoteIdentity ()->GetIdentHash (), true); // assume wrong s key - boost::asio::post (m_Server.GetService (), std::bind (&NTCP2Session::Terminate, shared_from_this ())); - } - } - void NTCP2Session::HandleSessionCreatedPaddingReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred) { if (ecode) @@ -658,27 +598,17 @@ namespace transport else { m_Establisher->m_SessionCreatedBufferLen += bytes_transferred; - boost::asio::post (m_Server.GetEstablisherService (), - [s = shared_from_this ()] () - { - s->SendSessionConfirmed (); - }); + SendSessionConfirmed (); } } void NTCP2Session::SendSessionConfirmed () { - if (!m_Establisher->CreateSessionConfirmedMessagePart1 ()) - { - boost::asio::post (m_Server.GetService (), std::bind (&NTCP2Session::Terminate, shared_from_this ())); - return; - } - if (!m_Establisher->CreateSessionConfirmedMessagePart2 ()) - { - LogPrint (eLogWarning, "NTCP2: Send SessionConfirmed Part2 KDF failed"); - boost::asio::post (m_Server.GetService (), std::bind (&NTCP2Session::Terminate, shared_from_this ())); - return; - } + uint8_t nonce[12]; + CreateNonce (1, nonce); // set nonce to 1 + m_Establisher->CreateSessionConfirmedMessagePart1 (nonce); + memset (nonce, 0, 12); // set nonce back to 0 + m_Establisher->CreateSessionConfirmedMessagePart2 (nonce); // send message boost::asio::async_write (m_Socket, boost::asio::buffer (m_Establisher->m_SessionConfirmedBuffer, m_Establisher->m3p2Len + 48), boost::asio::transfer_all (), std::bind(&NTCP2Session::HandleSessionConfirmedSent, shared_from_this (), std::placeholders::_1, std::placeholders::_2)); @@ -730,7 +660,6 @@ namespace transport void NTCP2Session::HandleSessionConfirmedReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred) { - (void) bytes_transferred; if (ecode) { LogPrint (eLogWarning, "NTCP2: SessionConfirmed read error: ", ecode.message ()); @@ -739,149 +668,122 @@ namespace transport else { m_HandshakeInterval = i2p::util::GetMillisecondsSinceEpoch () - m_HandshakeInterval; - boost::asio::post (m_Server.GetEstablisherService (), - [s = shared_from_this ()] () + LogPrint (eLogDebug, "NTCP2: SessionConfirmed received"); + // part 1 + uint8_t nonce[12]; + CreateNonce (1, nonce); + if (m_Establisher->ProcessSessionConfirmedMessagePart1 (nonce)) + { + // part 2 + std::vector buf(m_Establisher->m3p2Len - 16); // -MAC + memset (nonce, 0, 12); // set nonce to 0 again + if (m_Establisher->ProcessSessionConfirmedMessagePart2 (nonce, buf.data ())) { - s->ProcessSessionConfirmed ();; - }); + KeyDerivationFunctionDataPhase (); + // Bob data phase keys + m_SendKey = m_Kba; + m_ReceiveKey = m_Kab; + SetSipKeys (m_Sipkeysba, m_Sipkeysab); + memcpy (m_ReceiveIV.buf, m_Sipkeysab + 16, 8); + memcpy (m_SendIV.buf, m_Sipkeysba + 16, 8); + // payload + // process RI + if (buf[0] != eNTCP2BlkRouterInfo) + { + LogPrint (eLogWarning, "NTCP2: Unexpected block ", (int)buf[0], " in SessionConfirmed"); + Terminate (); + return; + } + auto size = bufbe16toh (buf.data () + 1); + if (size > buf.size () - 3 || size > i2p::data::MAX_RI_BUFFER_SIZE + 1) + { + LogPrint (eLogError, "NTCP2: Unexpected RouterInfo size ", size, " in SessionConfirmed"); + Terminate (); + return; + } + // TODO: check flag + i2p::data::RouterInfo ri (buf.data () + 4, size - 1); // 1 byte block type + 2 bytes size + 1 byte flag + if (ri.IsUnreachable ()) + { + LogPrint (eLogError, "NTCP2: RouterInfo verification failed in SessionConfirmed from ", GetRemoteEndpoint ()); + SendTerminationAndTerminate (eNTCP2RouterInfoSignatureVerificationFail); + return; + } + LogPrint(eLogDebug, "NTCP2: SessionConfirmed from ", GetRemoteEndpoint (), + " (", i2p::data::GetIdentHashAbbreviation (ri.GetIdentHash ()), ")"); + auto ts = i2p::util::GetMillisecondsSinceEpoch (); + if (ts > ri.GetTimestamp () + i2p::data::NETDB_MIN_EXPIRATION_TIMEOUT*1000LL) // 90 minutes + { + LogPrint (eLogError, "NTCP2: RouterInfo is too old in SessionConfirmed for ", (ts - ri.GetTimestamp ())/1000LL, " seconds"); + SendTerminationAndTerminate (eNTCP2Message3Error); + return; + } + if (ts + i2p::data::NETDB_EXPIRATION_TIMEOUT_THRESHOLD*1000LL < ri.GetTimestamp ()) // 2 minutes + { + LogPrint (eLogError, "NTCP2: RouterInfo is from future for ", (ri.GetTimestamp () - ts)/1000LL, " seconds"); + SendTerminationAndTerminate (eNTCP2Message3Error); + return; + } + // update RouterInfo in netdb + auto ri1 = i2p::data::netdb.AddRouterInfo (ri.GetBuffer (), ri.GetBufferLen ()); // ri1 points to one from netdb now + if (!ri1) + { + LogPrint (eLogError, "NTCP2: Couldn't update RouterInfo from SessionConfirmed in netdb"); + Terminate (); + return; + } + std::shared_ptr profile; // not null if older + if (ri.GetTimestamp () + i2p::data::NETDB_EXPIRATION_TIMEOUT_THRESHOLD*1000LL < ri1->GetTimestamp ()) + { + // received RouterInfo is older than one in netdb + profile = i2p::data::GetRouterProfile (ri1->GetIdentHash ()); // retrieve profile + if (profile && profile->IsDuplicated ()) + { + SendTerminationAndTerminate (eNTCP2Banned); + return; + } + } + + auto addr = m_RemoteEndpoint.address ().is_v4 () ? ri1->GetNTCP2V4Address () : + (i2p::util::net::IsYggdrasilAddress (m_RemoteEndpoint.address ()) ? ri1->GetYggdrasilAddress () : ri1->GetNTCP2V6Address ()); + if (!addr || memcmp (m_Establisher->m_RemoteStaticKey, addr->s, 32)) + { + LogPrint (eLogError, "NTCP2: Wrong static key in SessionConfirmed"); + Terminate (); + return; + } + if (addr->IsPublishedNTCP2 () && m_RemoteEndpoint.address () != addr->host && + (!m_RemoteEndpoint.address ().is_v6 () || (i2p::util::net::IsYggdrasilAddress (m_RemoteEndpoint.address ()) ? + memcmp (m_RemoteEndpoint.address ().to_v6 ().to_bytes ().data () + 1, addr->host.to_v6 ().to_bytes ().data () + 1, 7) : // from the same yggdrasil subnet + memcmp (m_RemoteEndpoint.address ().to_v6 ().to_bytes ().data (), addr->host.to_v6 ().to_bytes ().data (), 8)))) // temporary address + { + if (profile) // older router? + profile->Duplicated (); // mark router as duplicated in profile + else + LogPrint (eLogInfo, "NTCP2: Host mismatch between published address ", addr->host, " and actual endpoint ", m_RemoteEndpoint.address ()); + SendTerminationAndTerminate (eNTCP2Banned); + return; + } + // TODO: process options + + // ready to communicate + SetRemoteIdentity (ri1->GetRouterIdentity ()); + if (m_Server.AddNTCP2Session (shared_from_this (), true)) + { + Established (); + ReceiveLength (); + } + else + Terminate (); + } + else + Terminate (); + } + else + Terminate (); } } - void NTCP2Session::ProcessSessionConfirmed () - { - // run on establisher thread - LogPrint (eLogDebug, "NTCP2: SessionConfirmed received"); - // part 1 - if (m_Establisher->ProcessSessionConfirmedMessagePart1 ()) - { - // part 2 - auto buf = std::make_shared > (m_Establisher->m3p2Len - 16); // -MAC - if (m_Establisher->ProcessSessionConfirmedMessagePart2 (buf->data ())) // TODO:handle in establisher thread - { - // payload - // RI block must be first - if ((*buf)[0] != eNTCP2BlkRouterInfo) - { - LogPrint (eLogWarning, "NTCP2: Unexpected block ", (int)(*buf)[0], " in SessionConfirmed"); - boost::asio::post (m_Server.GetService (), std::bind (&NTCP2Session::Terminate, shared_from_this ())); - return; - } - auto size = bufbe16toh (buf->data () + 1); - if (size > buf->size () - 3 || size > i2p::data::MAX_RI_BUFFER_SIZE + 1) - { - LogPrint (eLogError, "NTCP2: Unexpected RouterInfo size ", size, " in SessionConfirmed"); - boost::asio::post (m_Server.GetService (), std::bind (&NTCP2Session::Terminate, shared_from_this ())); - return; - } - boost::asio::post (m_Server.GetService (), - [s = shared_from_this (), buf, size] () - { - s->EstablishSessionAfterSessionConfirmed (buf, size); - }); - } - else - boost::asio::post (m_Server.GetService (), std::bind (&NTCP2Session::Terminate, shared_from_this ())); - } - else - boost::asio::post (m_Server.GetService (), std::bind (&NTCP2Session::Terminate, shared_from_this ())); - } - - void NTCP2Session::EstablishSessionAfterSessionConfirmed (std::shared_ptr > buf, size_t size) - { - // run on main NTCP2 thread - KeyDerivationFunctionDataPhase (); - // Bob data phase keys - m_SendKey = m_Kba; - m_ReceiveKey = m_Kab; - SetSipKeys (m_Sipkeysba, m_Sipkeysab); - memcpy (m_ReceiveIV.buf, m_Sipkeysab + 16, 8); - memcpy (m_SendIV.buf, m_Sipkeysba + 16, 8); - // we need to set keys for SendTerminationAndTerminate - // TODO: check flag - i2p::data::RouterInfo ri (buf->data () + 4, size - 1); // 1 byte block type + 2 bytes size + 1 byte flag - if (ri.IsUnreachable ()) - { - LogPrint (eLogError, "NTCP2: RouterInfo verification failed in SessionConfirmed from ", GetRemoteEndpoint ()); - SendTerminationAndTerminate (eNTCP2RouterInfoSignatureVerificationFail); - return; - } - LogPrint(eLogDebug, "NTCP2: SessionConfirmed from ", GetRemoteEndpoint (), - " (", i2p::data::GetIdentHashAbbreviation (ri.GetIdentHash ()), ")"); - auto ts = i2p::util::GetMillisecondsSinceEpoch (); - if (ts > ri.GetTimestamp () + i2p::data::NETDB_MIN_EXPIRATION_TIMEOUT*1000LL) // 90 minutes - { - LogPrint (eLogError, "NTCP2: RouterInfo is too old in SessionConfirmed for ", (ts - ri.GetTimestamp ())/1000LL, " seconds"); - SendTerminationAndTerminate (eNTCP2Message3Error); - return; - } - if (ts + i2p::data::NETDB_EXPIRATION_TIMEOUT_THRESHOLD*1000LL < ri.GetTimestamp ()) // 2 minutes - { - LogPrint (eLogError, "NTCP2: RouterInfo is from future for ", (ri.GetTimestamp () - ts)/1000LL, " seconds"); - SendTerminationAndTerminate (eNTCP2Message3Error); - return; - } - // update RouterInfo in netdb - auto ri1 = i2p::data::netdb.AddRouterInfo (ri.GetBuffer (), ri.GetBufferLen ()); // ri1 points to one from netdb now - if (!ri1) - { - LogPrint (eLogError, "NTCP2: Couldn't update RouterInfo from SessionConfirmed in netdb"); - Terminate (); - return; - } - - bool isOlder = false; - if (ri.GetTimestamp () + i2p::data::NETDB_EXPIRATION_TIMEOUT_THRESHOLD*1000LL < ri1->GetTimestamp ()) - { - // received RouterInfo is older than one in netdb - isOlder = true; - if (ri1->HasProfile ()) - { - auto profile = i2p::data::GetRouterProfile (ri1->GetIdentHash ()); // retrieve profile - if (profile && profile->IsDuplicated ()) - { - SendTerminationAndTerminate (eNTCP2Banned); - return; - } - } - } - - auto addr = m_RemoteEndpoint.address ().is_v4 () ? ri1->GetNTCP2V4Address () : - (i2p::util::net::IsYggdrasilAddress (m_RemoteEndpoint.address ()) ? ri1->GetYggdrasilAddress () : ri1->GetNTCP2V6Address ()); - if (!addr || memcmp (m_Establisher->m_RemoteStaticKey, addr->s, 32)) - { - LogPrint (eLogError, "NTCP2: Wrong static key in SessionConfirmed"); - Terminate (); - return; - } - if (addr->IsPublishedNTCP2 () && m_RemoteEndpoint.address () != addr->host && - (!m_RemoteEndpoint.address ().is_v6 () || (i2p::util::net::IsYggdrasilAddress (m_RemoteEndpoint.address ()) ? - memcmp (m_RemoteEndpoint.address ().to_v6 ().to_bytes ().data () + 1, addr->host.to_v6 ().to_bytes ().data () + 1, 7) : // from the same yggdrasil subnet - memcmp (m_RemoteEndpoint.address ().to_v6 ().to_bytes ().data (), addr->host.to_v6 ().to_bytes ().data (), 8)))) // temporary address - { - if (isOlder) // older router? - i2p::data::UpdateRouterProfile (ri1->GetIdentHash (), - [](std::shared_ptr profile) - { - if (profile) profile->Duplicated (); // mark router as duplicated in profile - }); - else - LogPrint (eLogInfo, "NTCP2: Host mismatch between published address ", addr->host, " and actual endpoint ", m_RemoteEndpoint.address ()); - SendTerminationAndTerminate (eNTCP2Banned); - return; - } - // TODO: process options block - - // ready to communicate - SetRemoteIdentity (ri1->GetRouterIdentity ()); - if (m_Server.AddNTCP2Session (shared_from_this (), true)) - { - Established (); - ReceiveLength (); - } - else - Terminate (); - } - void NTCP2Session::SetSipKeys (const uint8_t * sendSipKey, const uint8_t * receiveSipKey) { #if OPENSSL_SIPHASH @@ -907,11 +809,7 @@ namespace transport void NTCP2Session::ClientLogin () { m_Establisher->CreateEphemeralKey (); - boost::asio::post (m_Server.GetEstablisherService (), - [s = shared_from_this ()] () - { - s->SendSessionRequest (); - }); + SendSessionRequest (); } void NTCP2Session::ServerLogin () @@ -1009,7 +907,7 @@ namespace transport i2p::transport::transports.UpdateReceivedBytes (bytes_transferred + 2); uint8_t nonce[12]; CreateNonce (m_ReceiveSequenceNumber, nonce); m_ReceiveSequenceNumber++; - if (m_Server.AEADChaCha20Poly1305Decrypt (m_NextReceivedBuffer, m_NextReceivedLen-16, nullptr, 0, m_ReceiveKey, nonce, m_NextReceivedBuffer, m_NextReceivedLen)) + if (i2p::crypto::AEADChaCha20Poly1305 (m_NextReceivedBuffer, m_NextReceivedLen-16, nullptr, 0, m_ReceiveKey, nonce, m_NextReceivedBuffer, m_NextReceivedLen, false)) { LogPrint (eLogDebug, "NTCP2: Received message decrypted"); ProcessNextFrame (m_NextReceivedBuffer, m_NextReceivedLen-16); @@ -1198,7 +1096,7 @@ namespace transport } uint8_t nonce[12]; CreateNonce (m_SendSequenceNumber, nonce); m_SendSequenceNumber++; - m_Server.AEADChaCha20Poly1305Encrypt (encryptBufs, m_SendKey, nonce, macBuf); // encrypt buffers + i2p::crypto::AEADChaCha20Poly1305Encrypt (encryptBufs, m_SendKey, nonce, macBuf); // encrypt buffers SetNextSentFrameLength (totalLen + 16, first->GetNTCP2Header () - 5); // frame length right before first block // send buffers @@ -1229,7 +1127,7 @@ namespace transport // encrypt uint8_t nonce[12]; CreateNonce (m_SendSequenceNumber, nonce); m_SendSequenceNumber++; - m_Server.AEADChaCha20Poly1305Encrypt ({ {m_NextSendBuffer + 2, payloadLen} }, m_SendKey, nonce, m_NextSendBuffer + payloadLen + 2); + i2p::crypto::AEADChaCha20Poly1305Encrypt ({ {m_NextSendBuffer + 2, payloadLen} }, m_SendKey, nonce, m_NextSendBuffer + payloadLen + 2); SetNextSentFrameLength (payloadLen + 16, m_NextSendBuffer); // send m_IsSending = true; @@ -1309,7 +1207,7 @@ namespace transport void NTCP2Session::MoveSendQueue (std::shared_ptr other) { if (!other || m_SendQueue.empty ()) return; - std::list > msgs; + std::vector > msgs; auto ts = i2p::util::GetMillisecondsSinceEpoch (); for (auto it: m_SendQueue) if (!it->IsExpired (ts)) @@ -1318,7 +1216,7 @@ namespace transport it->Drop (); m_SendQueue.clear (); if (!msgs.empty ()) - other->SendI2NPMessages (msgs); + other->PostI2NPMessages (msgs); } size_t NTCP2Session::CreatePaddingBlock (size_t msgLen, uint8_t * buf, size_t len) @@ -1396,45 +1294,23 @@ namespace transport void NTCP2Session::SendTerminationAndTerminate (NTCP2TerminationReason reason) { SendTermination (reason); - boost::asio::post (m_Server.GetService (), std::bind (&NTCP2Session::Terminate, shared_from_this ())); // let termination message go + m_Server.GetService ().post (std::bind (&NTCP2Session::Terminate, shared_from_this ())); // let termination message go } - void NTCP2Session::SendI2NPMessages (std::list >& msgs) + void NTCP2Session::SendI2NPMessages (const std::vector >& msgs) { - if (m_IsTerminated || msgs.empty ()) - { - msgs.clear (); - return; - } - bool empty = false; - { - std::lock_guard l(m_IntermediateQueueMutex); - empty = m_IntermediateQueue.empty (); - m_IntermediateQueue.splice (m_IntermediateQueue.end (), msgs); - } - if (empty) - boost::asio::post (m_Server.GetService (), std::bind (&NTCP2Session::PostI2NPMessages, shared_from_this ())); + m_Server.GetService ().post (std::bind (&NTCP2Session::PostI2NPMessages, shared_from_this (), msgs)); } - void NTCP2Session::PostI2NPMessages () + void NTCP2Session::PostI2NPMessages (std::vector > msgs) { if (m_IsTerminated) return; - std::list > msgs; - { - std::lock_guard l(m_IntermediateQueueMutex); - m_IntermediateQueue.swap (msgs); - } bool isSemiFull = m_SendQueue.size () > NTCP2_MAX_OUTGOING_QUEUE_SIZE/2; - if (isSemiFull) - { - for (auto it: msgs) - if (it->onDrop) - it->Drop (); // drop earlier because we can handle it - else - m_SendQueue.push_back (std::move (it)); - } - else - m_SendQueue.splice (m_SendQueue.end (), msgs); + for (auto it: msgs) + if (isSemiFull && it->onDrop) + it->Drop (); // drop earlier because we can handle it + else + m_SendQueue.push_back (std::move (it)); if (!m_IsSending && m_IsEstablished) SendQueue (); @@ -1450,15 +1326,9 @@ namespace transport void NTCP2Session::SendLocalRouterInfo (bool update) { if (update || !IsOutgoing ()) // we send it in SessionConfirmed for outgoing session - boost::asio::post (m_Server.GetService (), std::bind (&NTCP2Session::SendRouterInfo, shared_from_this ())); + m_Server.GetService ().post (std::bind (&NTCP2Session::SendRouterInfo, shared_from_this ())); } - i2p::data::RouterInfo::SupportedTransports NTCP2Session::GetTransportType () const - { - if (m_RemoteEndpoint.address ().is_v4 ()) return i2p::data::RouterInfo::eNTCP2V4; - return i2p::util::net::IsYggdrasilAddress (m_RemoteEndpoint.address ()) ? i2p::data::RouterInfo::eNTCP2V6Mesh : i2p::data::RouterInfo::eNTCP2V6; - } - NTCP2Server::NTCP2Server (): RunnableServiceWithWork ("NTCP2"), m_TerminationTimer (GetService ()), m_ProxyType(eNoProxy), m_Resolver(GetService ()), @@ -1473,7 +1343,6 @@ namespace transport void NTCP2Server::Start () { - m_EstablisherService.Start (); if (!IsRunning ()) { StartIOService (); @@ -1481,13 +1350,14 @@ namespace transport { LogPrint(eLogInfo, "NTCP2: Using proxy to connect to peers"); // TODO: resolve proxy until it is resolved + boost::asio::ip::tcp::resolver::query q(m_ProxyAddress, std::to_string(m_ProxyPort)); boost::system::error_code e; - auto itr = m_Resolver.resolve(m_ProxyAddress, std::to_string(m_ProxyPort), e); + auto itr = m_Resolver.resolve(q, e); if(e) LogPrint(eLogCritical, "NTCP2: Failed to resolve proxy ", e.message()); else { - m_ProxyEndpoint.reset (new boost::asio::ip::tcp::endpoint(*itr.begin ())); + m_ProxyEndpoint.reset (new boost::asio::ip::tcp::endpoint(*itr)); if (m_ProxyEndpoint) LogPrint(eLogDebug, "NTCP2: m_ProxyEndpoint ", *m_ProxyEndpoint); } @@ -1568,7 +1438,6 @@ namespace transport void NTCP2Server::Stop () { - m_EstablisherService.Stop (); { // we have to copy it because Terminate changes m_NTCP2Sessions auto ntcpSessions = m_NTCP2Sessions; @@ -1648,7 +1517,7 @@ namespace transport } LogPrint (eLogDebug, "NTCP2: Connecting to ", conn->GetRemoteEndpoint (), " (", i2p::data::GetIdentHashAbbreviation (conn->GetRemoteIdentity ()->GetIdentHash ()), ")"); - boost::asio::post (GetService (), [this, conn]() + GetService ().post([this, conn]() { if (this->AddNTCP2Session (conn)) { @@ -1869,7 +1738,7 @@ namespace transport LogPrint (eLogError, "NTCP2: Can't connect to unspecified address"); return; } - boost::asio::post (GetService(), [this, conn]() + GetService().post([this, conn]() { if (this->AddNTCP2Session (conn)) { @@ -1953,7 +1822,7 @@ namespace transport LogPrint(eLogError, "NTCP2: HTTP proxy write error ", ec.message()); }); - auto readbuff = std::make_shared(); + boost::asio::streambuf * readbuff = new boost::asio::streambuf; boost::asio::async_read_until(conn->GetSocket(), *readbuff, "\r\n\r\n", [readbuff, timer, conn] (const boost::system::error_code & ec, std::size_t transferred) { @@ -1967,12 +1836,13 @@ namespace transport { readbuff->commit(transferred); i2p::http::HTTPRes res; - if(res.parse(std::string {boost::asio::buffers_begin(readbuff->data ()), boost::asio::buffers_begin(readbuff->data ()) + readbuff->size ()}) > 0) + if(res.parse(boost::asio::buffer_cast(readbuff->data()), readbuff->size()) > 0) { if(res.code == 200) { timer->cancel(); conn->ClientLogin(); + delete readbuff; return; } else @@ -1982,6 +1852,7 @@ namespace transport LogPrint(eLogError, "NTCP2: HTTP proxy gave malformed response"); timer->cancel(); conn->Terminate(); + delete readbuff; } }); break; @@ -2004,17 +1875,5 @@ namespace transport else m_Address4 = addr; } - - void NTCP2Server::AEADChaCha20Poly1305Encrypt (const std::vector >& bufs, - const uint8_t * key, const uint8_t * nonce, uint8_t * mac) - { - return m_Encryptor.Encrypt (bufs, key, nonce, mac); - } - - bool NTCP2Server::AEADChaCha20Poly1305Decrypt (const uint8_t * msg, size_t msgLen, - const uint8_t * ad, size_t adLen, const uint8_t * key, const uint8_t * nonce, uint8_t * buf, size_t len) - { - return m_Decryptor.Decrypt (msg, msgLen, ad, adLen, key, nonce, buf, len); - } } } diff --git a/libi2pd/NTCP2.h b/libi2pd/NTCP2.h index 5ad5b955..f7912b54 100644 --- a/libi2pd/NTCP2.h +++ b/libi2pd/NTCP2.h @@ -91,29 +91,30 @@ namespace transport const uint8_t * GetRemotePub () const { return m_RemoteEphemeralPublicKey; }; // Y for Alice and X for Bob uint8_t * GetRemotePub () { return m_RemoteEphemeralPublicKey; }; // to set + const uint8_t * GetK () const { return m_CK + 32; }; const uint8_t * GetCK () const { return m_CK; }; const uint8_t * GetH () const { return m_H; }; - bool KDF1Alice (); - bool KDF1Bob (); - bool KDF2Alice (); - bool KDF2Bob (); - bool KDF3Alice (); // for SessionConfirmed part 2 - bool KDF3Bob (); + void KDF1Alice (); + void KDF1Bob (); + void KDF2Alice (); + void KDF2Bob (); + void KDF3Alice (); // for SessionConfirmed part 2 + void KDF3Bob (); - bool KeyDerivationFunction1 (const uint8_t * pub, i2p::crypto::X25519Keys& priv, const uint8_t * rs, const uint8_t * epub); // for SessionRequest, (pub, priv) for DH - bool KeyDerivationFunction2 (const uint8_t * sessionRequest, size_t sessionRequestLen, const uint8_t * epub); // for SessionCreate + void KeyDerivationFunction1 (const uint8_t * pub, i2p::crypto::X25519Keys& priv, const uint8_t * rs, const uint8_t * epub); // for SessionRequest, (pub, priv) for DH + void KeyDerivationFunction2 (const uint8_t * sessionRequest, size_t sessionRequestLen, const uint8_t * epub); // for SessionCreate void CreateEphemeralKey (); - bool CreateSessionRequestMessage (std::mt19937& rng); - bool CreateSessionCreatedMessage (std::mt19937& rng); - bool CreateSessionConfirmedMessagePart1 (); - bool CreateSessionConfirmedMessagePart2 (); + void CreateSessionRequestMessage (std::mt19937& rng); + void CreateSessionCreatedMessage (std::mt19937& rng); + void CreateSessionConfirmedMessagePart1 (const uint8_t * nonce); + void CreateSessionConfirmedMessagePart2 (const uint8_t * nonce); bool ProcessSessionRequestMessage (uint16_t& paddingLen, bool& clockSkew); bool ProcessSessionCreatedMessage (uint16_t& paddingLen); - bool ProcessSessionConfirmedMessagePart1 (); - bool ProcessSessionConfirmedMessagePart2 (uint8_t * m3p2Buf); + bool ProcessSessionConfirmedMessagePart1 (const uint8_t * nonce); + bool ProcessSessionConfirmedMessagePart2 (const uint8_t * nonce, uint8_t * m3p2Buf); std::shared_ptr m_EphemeralKeys; uint8_t m_RemoteEphemeralPublicKey[32]; // x25519 @@ -146,14 +147,13 @@ namespace transport void SetRemoteEndpoint (const boost::asio::ip::tcp::endpoint& ep) { m_RemoteEndpoint = ep; }; bool IsEstablished () const override { return m_IsEstablished; }; - i2p::data::RouterInfo::SupportedTransports GetTransportType () const override; bool IsTerminated () const { return m_IsTerminated; }; void ClientLogin (); // Alice void ServerLogin (); // Bob void SendLocalRouterInfo (bool update) override; // after handshake or by update - void SendI2NPMessages (std::list >& msgs) override; + void SendI2NPMessages (const std::vector >& msgs) override; void MoveSendQueue (std::shared_ptr other); private: @@ -172,17 +172,13 @@ namespace transport void HandleSessionRequestSent (const boost::system::error_code& ecode, std::size_t bytes_transferred); void HandleSessionRequestReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred); - void ProcessSessionRequest (size_t len); void HandleSessionRequestPaddingReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred); void HandleSessionCreatedSent (const boost::system::error_code& ecode, std::size_t bytes_transferred); void HandleSessionCreatedReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred); - void ProcessSessionCreated (size_t len); void HandleSessionCreatedPaddingReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred); void HandleSessionConfirmedSent (const boost::system::error_code& ecode, std::size_t bytes_transferred); void HandleSessionConfirmedReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred); - void ProcessSessionConfirmed (); - void EstablishSessionAfterSessionConfirmed (std::shared_ptr > buf, size_t size); - + // data void ReceiveLength (); void HandleReceivedLength (const boost::system::error_code& ecode, std::size_t bytes_transferred); @@ -200,7 +196,7 @@ namespace transport void SendRouterInfo (); void SendTermination (NTCP2TerminationReason reason); void SendTerminationAndTerminate (NTCP2TerminationReason reason); - void PostI2NPMessages (); + void PostI2NPMessages (std::vector > msgs); private: @@ -233,28 +229,13 @@ namespace transport bool m_IsSending, m_IsReceiving; std::list > m_SendQueue; uint64_t m_NextRouterInfoResendTime; // seconds since epoch - - std::list > m_IntermediateQueue; // from transports - mutable std::mutex m_IntermediateQueueMutex; - + uint16_t m_PaddingSizes[16]; int m_NextPaddingSize; }; class NTCP2Server: private i2p::util::RunnableServiceWithWork { - private: - - class EstablisherService: public i2p::util::RunnableServiceWithWork - { - public: - - EstablisherService (): RunnableServiceWithWork ("NTCP2e") {}; - auto& GetService () { return GetIOService (); }; - void Start () { StartIOService (); }; - void Stop () { StopIOService (); }; - }; - public: enum ProxyType @@ -263,20 +244,14 @@ namespace transport eSocksProxy, eHTTPProxy }; - + NTCP2Server (); ~NTCP2Server (); void Start (); void Stop (); - auto& GetService () { return GetIOService (); }; - auto& GetEstablisherService () { return m_EstablisherService.GetService (); }; + boost::asio::io_service& GetService () { return GetIOService (); }; std::mt19937& GetRng () { return m_Rng; }; - void AEADChaCha20Poly1305Encrypt (const std::vector >& bufs, - const uint8_t * key, const uint8_t * nonce, uint8_t * mac); - bool AEADChaCha20Poly1305Decrypt (const uint8_t * msg, size_t msgLen, const uint8_t * ad, size_t adLen, - const uint8_t * key, const uint8_t * nonce, uint8_t * buf, size_t len); - bool AddNTCP2Session (std::shared_ptr session, bool incoming = false); void RemoveNTCP2Session (std::shared_ptr session); @@ -314,13 +289,9 @@ namespace transport uint16_t m_ProxyPort; boost::asio::ip::tcp::resolver m_Resolver; std::unique_ptr m_ProxyEndpoint; - std::shared_ptr m_Address4, m_Address6, m_YggdrasilAddress; std::mt19937 m_Rng; - EstablisherService m_EstablisherService; - i2p::crypto::AEADChaCha20Poly1305Encryptor m_Encryptor; - i2p::crypto::AEADChaCha20Poly1305Decryptor m_Decryptor; - + public: // for HTTP/I2PControl diff --git a/libi2pd/NetDb.cpp b/libi2pd/NetDb.cpp index e53738e5..24269015 100644 --- a/libi2pd/NetDb.cpp +++ b/libi2pd/NetDb.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2025, The PurpleI2P Project +* Copyright (c) 2013-2024, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -10,6 +10,7 @@ #include #include #include +#include #include #include @@ -39,7 +40,7 @@ namespace data NetDb::NetDb (): m_IsRunning (false), m_Thread (nullptr), m_Reseeder (nullptr), m_Storage("netDb", "r", "routerInfo-", "dat"), m_PersistProfiles (true), - m_LastExploratorySelectionUpdateTime (0), m_Rng(i2p::util::GetMonotonicMicroseconds () % 1000000LL) + m_LastExploratorySelectionUpdateTime (0) { } @@ -118,22 +119,19 @@ namespace data i2p::util::SetThreadName("NetDB"); uint64_t lastManage = 0; - uint64_t lastProfilesCleanup = i2p::util::GetMonotonicMilliseconds (), - lastObsoleteProfilesCleanup = lastProfilesCleanup, lastApplyingProfileUpdates = lastProfilesCleanup; - int16_t profilesCleanupVariance = 0, obsoleteProfilesCleanVariance = 0, applyingProfileUpdatesVariance = 0; + uint64_t lastProfilesCleanup = i2p::util::GetMonotonicMilliseconds (), lastObsoleteProfilesCleanup = lastProfilesCleanup; + int16_t profilesCleanupVariance = 0, obsoleteProfilesCleanVariance = 0; - std::list > msgs; while (m_IsRunning) { try { - if (m_Queue.Wait (1,0)) // 1 sec + auto msg = m_Queue.GetNextWithTimeout (1000); // 1 sec + if (msg) { - m_Queue.GetWholeQueue (msgs); - while (!msgs.empty ()) + int numMsgs = 0; + while (msg) { - auto msg = msgs.front (); msgs.pop_front (); - if (!msg) continue; LogPrint(eLogDebug, "NetDb: Got request with type ", (int) msg->GetTypeID ()); switch (msg->GetTypeID ()) { @@ -147,6 +145,9 @@ namespace data LogPrint (eLogError, "NetDb: Unexpected message type ", (int) msg->GetTypeID ()); //i2p::HandleI2NPMessage (msg); } + if (numMsgs > 100) break; + msg = m_Queue.Get (); + numMsgs++; } } if (!m_IsRunning) break; @@ -181,7 +182,7 @@ namespace data LogPrint (eLogWarning, "NetDb: Can't persist profiles. Profiles are being saved to disk"); } lastProfilesCleanup = mts; - profilesCleanupVariance = m_Rng () % i2p::data::PEER_PROFILE_AUTOCLEAN_VARIANCE; + profilesCleanupVariance = rand () % i2p::data::PEER_PROFILE_AUTOCLEAN_VARIANCE; } if (mts >= lastObsoleteProfilesCleanup + (uint64_t)(i2p::data::PEER_PROFILE_OBSOLETE_PROFILES_CLEAN_TIMEOUT + obsoleteProfilesCleanVariance)*1000) @@ -197,20 +198,7 @@ namespace data else LogPrint (eLogWarning, "NetDb: Can't delete profiles. Profiles are being deleted from disk"); lastObsoleteProfilesCleanup = mts; - obsoleteProfilesCleanVariance = m_Rng () % i2p::data::PEER_PROFILE_OBSOLETE_PROFILES_CLEAN_VARIANCE; - } - if (mts >= lastApplyingProfileUpdates + i2p::data::PEER_PROFILE_APPLY_POSTPONED_TIMEOUT + applyingProfileUpdatesVariance) - { - bool isApplying = m_ApplyingProfileUpdates.valid (); - if (isApplying && m_ApplyingProfileUpdates.wait_for(std::chrono::seconds(0)) == std::future_status::ready) // still active? - { - m_ApplyingProfileUpdates.get (); - isApplying = false; - } - if (!isApplying) - m_ApplyingProfileUpdates = i2p::data::FlushPostponedRouterProfileUpdates (); - lastApplyingProfileUpdates = mts; - applyingProfileUpdatesVariance = m_Rng () % i2p::data::PEER_PROFILE_APPLY_POSTPONED_TIMEOUT_VARIANCE; + obsoleteProfilesCleanVariance = rand () % i2p::data::PEER_PROFILE_OBSOLETE_PROFILES_CLEAN_VARIANCE; } } catch (std::exception& ex) @@ -294,7 +282,6 @@ namespace data } else { - r->CancelBufferToDelete (); // since an update received if (CheckLogLevel (eLogDebug)) LogPrint (eLogDebug, "NetDb: RouterInfo is older: ", ident.ToBase64()); updated = false; @@ -398,7 +385,8 @@ namespace data if (it == m_LeaseSets.end () || it->second->GetStoreType () != storeType || leaseSet->GetPublishedTimestamp () > it->second->GetPublishedTimestamp ()) { - if (leaseSet->IsPublic () && !leaseSet->IsExpired ()) + if (leaseSet->IsPublic () && !leaseSet->IsExpired () && + i2p::util::GetSecondsSinceEpoch () + NETDB_EXPIRATION_TIMEOUT_THRESHOLD > leaseSet->GetPublishedTimestamp ()) { // TODO: implement actual update if (CheckLogLevel (eLogInfo)) @@ -493,7 +481,7 @@ namespace data void NetDb::ReseedFromFloodfill(const RouterInfo & ri, int numRouters, int numFloodfills) { LogPrint(eLogInfo, "NetDB: Reseeding from floodfill ", ri.GetIdentHashBase64()); - std::list > requests; + std::vector > requests; i2p::data::IdentHash ourIdent = i2p::context.GetIdentHash(); i2p::data::IdentHash ih = ri.GetIdentHash(); @@ -516,7 +504,7 @@ namespace data } // send them off - i2p::transport::transports.SendMessages(ih, std::move (requests)); + i2p::transport::transports.SendMessages(ih, requests); } bool NetDb::LoadRouterInfo (const std::string& path, uint64_t ts) @@ -571,7 +559,7 @@ namespace data while(n > 0) { std::lock_guard lock(m_RouterInfosMutex); - uint32_t idx = m_Rng () % m_RouterInfos.size (); + uint32_t idx = rand () % m_RouterInfos.size (); uint32_t i = 0; for (const auto & it : m_RouterInfos) { if(i >= idx) // are we at the random start point? @@ -651,80 +639,70 @@ namespace data if (checkForExpiration && uptime > i2p::transport::SSU2_TO_INTRODUCER_SESSION_DURATION) // 1 hour expirationTimeout = i2p::context.IsFloodfill () ? NETDB_FLOODFILL_EXPIRATION_TIMEOUT*1000LL : NETDB_MIN_EXPIRATION_TIMEOUT*1000LL + (NETDB_MAX_EXPIRATION_TIMEOUT - NETDB_MIN_EXPIRATION_TIMEOUT)*1000LL*NETDB_MIN_ROUTERS/total; - bool isOffline = checkForExpiration && i2p::transport::transports.GetNumPeers () < NETDB_MIN_TRANSPORTS; // enough routers and uptime, but no transports - + std::list > > saveToDisk; std::list removeFromDisk; auto own = i2p::context.GetSharedRouterInfo (); - for (auto [ident, r]: m_RouterInfos) + for (auto& it: m_RouterInfos) { - if (!r || r == own) continue; // skip own - if (r->IsBufferScheduledToDelete ()) // from previous SaveUpdated, we assume m_PersistingRouters complete + if (!it.second || it.second == own) continue; // skip own + std::string ident = it.second->GetIdentHashBase64(); + if (it.second->IsUpdated ()) { - std::lock_guard l(m_RouterInfosMutex); // possible collision between DeleteBuffer and Update - r->DeleteBuffer (); - } - if (r->IsUpdated ()) - { - if (r->GetBuffer () && !r->IsUnreachable ()) + if (it.second->GetBuffer ()) { // we have something to save std::shared_ptr buffer; { std::lock_guard l(m_RouterInfosMutex); // possible collision between DeleteBuffer and Update - buffer = r->CopyBuffer (); + buffer = it.second->GetSharedBuffer (); + it.second->DeleteBuffer (); } - if (!i2p::transport::transports.IsConnected (ident)) - r->ScheduleBufferToDelete (); - if (buffer) - saveToDisk.emplace_back(ident.ToBase64 (), buffer); + if (buffer && !it.second->IsUnreachable ()) // don't save bad router + saveToDisk.push_back(std::make_pair(ident, buffer)); + it.second->SetUnreachable (false); } - r->SetUpdated (false); + it.second->SetUpdated (false); updatedCount++; continue; } - else if (r->GetBuffer () && ts > r->GetTimestamp () + NETDB_MIN_EXPIRATION_TIMEOUT*1000LL) - // since update was long time ago we assume that router is not connected anymore - r->ScheduleBufferToDelete (); - - if (r->HasProfile () && r->GetProfile ()->IsUnreachable ()) - r->SetUnreachable (true); + if (it.second->GetProfile ()->IsUnreachable ()) + it.second->SetUnreachable (true); // make router reachable back if too few routers or floodfills - if (r->IsUnreachable () && (total - deletedCount < NETDB_MIN_ROUTERS || isLowRate || isOffline || - (r->IsFloodfill () && totalFloodfills - deletedFloodfillsCount < NETDB_MIN_FLOODFILLS))) - r->SetUnreachable (false); - if (!r->IsUnreachable ()) + if (it.second->IsUnreachable () && (total - deletedCount < NETDB_MIN_ROUTERS || isLowRate || + (it.second->IsFloodfill () && totalFloodfills - deletedFloodfillsCount < NETDB_MIN_FLOODFILLS))) + it.second->SetUnreachable (false); + if (!it.second->IsUnreachable ()) { // find & mark expired routers - if (!r->GetCompatibleTransports (true)) // non reachable by any transport - r->SetUnreachable (true); - else if (ts + NETDB_EXPIRATION_TIMEOUT_THRESHOLD*1000LL < r->GetTimestamp ()) + 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 ", (r->GetTimestamp () - ts)/1000LL, " seconds"); - r->SetUnreachable (true); + LogPrint (eLogWarning, "NetDb: RouterInfo is from future for ", (it.second->GetTimestamp () - ts)/1000LL, " seconds"); + it.second->SetUnreachable (true); } else if (checkForExpiration) { - if (ts > r->GetTimestamp () + expirationTimeout) - r->SetUnreachable (true); - else if ((ts > r->GetTimestamp () + expirationTimeout/2) && // more than half of expiration - total > NETDB_NUM_ROUTERS_THRESHOLD && !r->IsHighBandwidth() && // low bandwidth - !r->IsFloodfill() && (!i2p::context.IsFloodfill () || // non floodfill - (CreateRoutingKey (ident) ^ i2p::context.GetIdentHash ()).metric[0] >= 0x02)) // different first 7 bits - r->SetUnreachable (true); + 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); } } - // make router reachable back if connected now or trusted router - if (r->IsUnreachable () && (i2p::transport::transports.IsConnected (ident) || - i2p::transport::transports.IsTrustedRouter (ident))) - r->SetUnreachable (false); + // make router reachable back if connected now + if (it.second->IsUnreachable () && i2p::transport::transports.IsConnected (it.second->GetIdentHash ())) + it.second->SetUnreachable (false); - if (r->IsUnreachable ()) + if (it.second->IsUnreachable ()) { - if (r->IsFloodfill ()) deletedFloodfillsCount++; + if (it.second->IsFloodfill ()) deletedFloodfillsCount++; // delete RI file - removeFromDisk.emplace_back (ident.ToBase64()); + removeFromDisk.push_back (ident); deletedCount++; if (total - deletedCount < NETDB_MIN_ROUTERS) checkForExpiration = false; } @@ -951,13 +929,14 @@ namespace data LogPrint (eLogError, "NetDb: DatabaseLookup for zero ident. Ignored"); return; } - std::string key; - if (CheckLogLevel (eLogInfo)) - key = i2p::data::ByteStreamToBase64 (buf, 32); + char key[48]; + int l = i2p::data::ByteStreamToBase64 (buf, 32, key, 48); + key[l] = 0; IdentHash replyIdent(buf + 32); uint8_t flag = buf[64]; + LogPrint (eLogDebug, "NetDb: DatabaseLookup for ", key, " received flags=", (int)flag); uint8_t lookupType = flag & DATABASE_LOOKUP_TYPE_FLAGS_MASK; const uint8_t * excluded = buf + 65; @@ -1350,7 +1329,7 @@ namespace data if (eligible.size () > NETDB_MAX_EXPLORATORY_SELECTION_SIZE) { std::sample (eligible.begin(), eligible.end(), std::back_inserter(m_ExploratorySelection), - NETDB_MAX_EXPLORATORY_SELECTION_SIZE, m_Rng); + NETDB_MAX_EXPLORATORY_SELECTION_SIZE, std::mt19937(ts)); } else std::swap (m_ExploratorySelection, eligible); diff --git a/libi2pd/NetDb.hpp b/libi2pd/NetDb.hpp index f2a7019b..b84387de 100644 --- a/libi2pd/NetDb.hpp +++ b/libi2pd/NetDb.hpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2025, The PurpleI2P Project +* Copyright (c) 2013-2024, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -16,7 +16,6 @@ #include #include #include -#include #include "Base.h" #include "Gzip.h" @@ -40,7 +39,6 @@ namespace data { const int NETDB_MIN_ROUTERS = 90; const int NETDB_MIN_FLOODFILLS = 5; - const int NETDB_MIN_TRANSPORTS = 10 ; // otherwise assume offline const int NETDB_NUM_FLOODFILLS_THRESHOLD = 1200; const int NETDB_NUM_ROUTERS_THRESHOLD = 4*NETDB_NUM_FLOODFILLS_THRESHOLD; const int NETDB_TUNNEL_CREATION_RATE_THRESHOLD = 10; // in % @@ -53,7 +51,6 @@ namespace data const int NETDB_MIN_HIGHBANDWIDTH_VERSION = MAKE_VERSION_NUMBER(0, 9, 58); // 0.9.58 const int NETDB_MIN_FLOODFILL_VERSION = MAKE_VERSION_NUMBER(0, 9, 59); // 0.9.59 const int NETDB_MIN_SHORT_TUNNEL_BUILD_VERSION = MAKE_VERSION_NUMBER(0, 9, 51); // 0.9.51 - const int NETDB_MIN_PEER_TEST_VERSION = MAKE_VERSION_NUMBER(0, 9, 62); // 0.9.62 const size_t NETDB_MAX_NUM_SEARCH_REPLY_PEER_HASHES = 16; const size_t NETDB_MAX_EXPLORATORY_SELECTION_SIZE = 500; const int NETDB_EXPLORATORY_SELECTION_UPDATE_INTERVAL = 82; // in seconds. for floodfill @@ -187,11 +184,10 @@ namespace data std::shared_ptr m_Requests; bool m_PersistProfiles; - std::future m_SavingProfiles, m_DeletingProfiles, m_ApplyingProfileUpdates, m_PersistingRouters; + std::future m_SavingProfiles, m_DeletingProfiles, m_PersistingRouters; std::vector > m_ExploratorySelection; uint64_t m_LastExploratorySelectionUpdateTime; // in monotonic seconds - std::mt19937 m_Rng; i2p::util::MemoryPoolMt m_RouterInfoBuffersPool; i2p::util::MemoryPoolMt m_RouterInfoAddressesPool; diff --git a/libi2pd/NetDbRequests.cpp b/libi2pd/NetDbRequests.cpp index 94633e10..0e632c26 100644 --- a/libi2pd/NetDbRequests.cpp +++ b/libi2pd/NetDbRequests.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2025, The PurpleI2P Project +* Copyright (c) 2013-2024, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -20,10 +20,8 @@ namespace i2p namespace data { RequestedDestination::RequestedDestination (const IdentHash& destination, bool isExploratory, bool direct): - m_Destination (destination), m_IsExploratory (isExploratory), m_IsDirect (direct), - m_IsActive (true), m_IsSentDirectly (false), - m_CreationTime (i2p::util::GetMillisecondsSinceEpoch ()), - m_LastRequestTime (0), m_NumAttempts (0) + m_Destination (destination), m_IsExploratory (isExploratory), m_IsDirect (direct), m_IsActive (true), + m_CreationTime (i2p::util::GetSecondsSinceEpoch ()), m_LastRequestTime (0), m_NumAttempts (0) { if (i2p::context.IsFloodfill ()) m_ExcludedPeers.insert (i2p::context.GetIdentHash ()); // exclude self if floodfill @@ -46,9 +44,8 @@ namespace data msg = i2p::CreateRouterInfoDatabaseLookupMsg(m_Destination, i2p::context.GetIdentHash(), 0, m_IsExploratory, &m_ExcludedPeers); if(router) m_ExcludedPeers.insert (router->GetIdentHash ()); - m_LastRequestTime = i2p::util::GetMillisecondsSinceEpoch (); + m_LastRequestTime = i2p::util::GetSecondsSinceEpoch (); m_NumAttempts++; - m_IsSentDirectly = false; return msg; } @@ -58,8 +55,7 @@ namespace data i2p::context.GetRouterInfo ().GetIdentHash () , 0, false, &m_ExcludedPeers); m_ExcludedPeers.insert (floodfill); m_NumAttempts++; - m_LastRequestTime = i2p::util::GetMillisecondsSinceEpoch (); - m_IsSentDirectly = true; + m_LastRequestTime = i2p::util::GetSecondsSinceEpoch (); return msg; } @@ -183,7 +179,7 @@ namespace data void NetDbRequests::RequestComplete (const IdentHash& ident, std::shared_ptr r) { - boost::asio::post (GetIOService (), [this, ident, r]() + GetIOService ().post ([this, ident, r]() { std::shared_ptr request; auto it = m_RequestedDestinations.find (ident); @@ -214,7 +210,7 @@ namespace data void NetDbRequests::ManageRequests () { - uint64_t ts = i2p::util::GetMillisecondsSinceEpoch (); + uint64_t ts = i2p::util::GetSecondsSinceEpoch (); for (auto it = m_RequestedDestinations.begin (); it != m_RequestedDestinations.end ();) { auto& dest = it->second; @@ -226,8 +222,7 @@ namespace data bool done = false; if (ts < dest->GetCreationTime () + MAX_REQUEST_TIME) { - if (ts > dest->GetLastRequestTime () + (dest->IsSentDirectly () ? MIN_DIRECT_REQUEST_TIME : MIN_REQUEST_TIME)) - // try next floodfill if no response after min interval + if (ts > dest->GetLastRequestTime () + MIN_REQUEST_TIME) // try next floodfill if no response after min interval done = !SendNextRequest (dest); } else // request is expired @@ -272,7 +267,7 @@ namespace data { if (dest->IsActive ()) { - boost::asio::post (s->GetIOService (), [s, dest]() + s->GetIOService ().post ([s, dest]() { if (dest->IsActive ()) s->SendNextRequest (dest); }); @@ -333,8 +328,7 @@ namespace data void NetDbRequests::ScheduleManageRequests () { - m_ManageRequestsTimer.expires_from_now (boost::posix_time::milliseconds(MANAGE_REQUESTS_INTERVAL + - m_Rng () % MANAGE_REQUESTS_INTERVAL_VARIANCE)); + m_ManageRequestsTimer.expires_from_now (boost::posix_time::seconds(MANAGE_REQUESTS_INTERVAL)); m_ManageRequestsTimer.async_wait (std::bind (&NetDbRequests::HandleManageRequestsTimer, this, std::placeholders::_1)); } @@ -351,7 +345,7 @@ namespace data void NetDbRequests::PostDatabaseSearchReplyMsg (std::shared_ptr msg) { - boost::asio::post (GetIOService (), [this, msg]() + GetIOService ().post ([this, msg]() { HandleDatabaseSearchReplyMsg (msg); }); @@ -360,12 +354,11 @@ namespace data void NetDbRequests::HandleDatabaseSearchReplyMsg (std::shared_ptr msg) { const uint8_t * buf = msg->GetPayload (); - std::string key; + char key[48]; + int l = i2p::data::ByteStreamToBase64 (buf, 32, key, 48); + key[l] = 0; size_t num = buf[32]; // num - if (CheckLogLevel (eLogInfo)) - key = i2p::data::ByteStreamToBase64 (buf, 32); LogPrint (eLogDebug, "NetDbReq: DatabaseSearchReply for ", key, " num=", num); - IdentHash ident (buf); bool isExploratory = false; auto dest = FindRequest (ident); @@ -438,7 +431,7 @@ namespace data void NetDbRequests::PostRequestDestination (const IdentHash& destination, const RequestedDestination::RequestComplete& requestComplete, bool direct) { - boost::asio::post (GetIOService (), [this, destination, requestComplete, direct]() + GetIOService ().post ([this, destination, requestComplete, direct]() { RequestDestination (destination, requestComplete, direct); }); diff --git a/libi2pd/NetDbRequests.h b/libi2pd/NetDbRequests.h index 53af2c6a..1758d498 100644 --- a/libi2pd/NetDbRequests.h +++ b/libi2pd/NetDbRequests.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2025, The PurpleI2P Project +* Copyright (c) 2013-2024, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -24,17 +24,15 @@ namespace i2p namespace data { const int MAX_NUM_REQUEST_ATTEMPTS = 5; - const uint64_t MANAGE_REQUESTS_INTERVAL = 400; // in milliseconds - const uint64_t MANAGE_REQUESTS_INTERVAL_VARIANCE = 300; // in milliseconds - const uint64_t MIN_REQUEST_TIME = 1200; // in milliseconds - const uint64_t MAX_REQUEST_TIME = MAX_NUM_REQUEST_ATTEMPTS * (MIN_REQUEST_TIME + MANAGE_REQUESTS_INTERVAL + MANAGE_REQUESTS_INTERVAL_VARIANCE); - const uint64_t MIN_DIRECT_REQUEST_TIME = 600; // in milliseconds + const uint64_t MANAGE_REQUESTS_INTERVAL = 1; // in seconds + const uint64_t MIN_REQUEST_TIME = 5; // in seconds + const uint64_t MAX_REQUEST_TIME = MAX_NUM_REQUEST_ATTEMPTS * (MIN_REQUEST_TIME + MANAGE_REQUESTS_INTERVAL); const uint64_t EXPLORATORY_REQUEST_INTERVAL = 55; // in seconds const uint64_t EXPLORATORY_REQUEST_INTERVAL_VARIANCE = 170; // in seconds const uint64_t DISCOVERED_REQUEST_INTERVAL = 360; // in milliseconds const uint64_t DISCOVERED_REQUEST_INTERVAL_VARIANCE = 540; // in milliseconds - const uint64_t MAX_EXPLORATORY_REQUEST_TIME = 30000; // in milliseconds - const uint64_t REQUEST_CACHE_TIME = MAX_REQUEST_TIME + 40000; // in milliseconds + const uint64_t MAX_EXPLORATORY_REQUEST_TIME = 30; // in seconds + const uint64_t REQUEST_CACHE_TIME = MAX_REQUEST_TIME + 40; // in seconds const uint64_t REQUESTED_DESTINATIONS_POOL_CLEANUP_INTERVAL = 191; // in seconds class RequestedDestination @@ -53,7 +51,6 @@ namespace data bool IsExploratory () const { return m_IsExploratory; }; bool IsDirect () const { return m_IsDirect; }; bool IsActive () const { return m_IsActive; }; - bool IsSentDirectly () const { return m_IsSentDirectly; }; bool IsExcluded (const IdentHash& ident) const; uint64_t GetCreationTime () const { return m_CreationTime; }; uint64_t GetLastRequestTime () const { return m_LastRequestTime; }; @@ -72,9 +69,9 @@ namespace data private: IdentHash m_Destination; - bool m_IsExploratory, m_IsDirect, m_IsActive, m_IsSentDirectly; + bool m_IsExploratory, m_IsDirect, m_IsActive; std::unordered_set m_ExcludedPeers; - uint64_t m_CreationTime, m_LastRequestTime; // in milliseconds + uint64_t m_CreationTime, m_LastRequestTime; // in seconds std::list m_RequestComplete; int m_NumAttempts; }; @@ -118,9 +115,9 @@ namespace data private: - i2p::util::MemoryPoolMt m_RequestedDestinationsPool; std::unordered_map > m_RequestedDestinations; std::list m_DiscoveredRouterHashes; + i2p::util::MemoryPoolMt m_RequestedDestinationsPool; boost::asio::deadline_timer m_ManageRequestsTimer, m_ExploratoryTimer, m_CleanupTimer, m_DiscoveredRoutersTimer; std::mt19937 m_Rng; diff --git a/libi2pd/PostQuantum.cpp b/libi2pd/PostQuantum.cpp deleted file mode 100644 index fa268828..00000000 --- a/libi2pd/PostQuantum.cpp +++ /dev/null @@ -1,160 +0,0 @@ -/* -* Copyright (c) 2025, The PurpleI2P Project -* -* This file is part of Purple i2pd project and licensed under BSD3 -* -* See full license text in LICENSE file at top of project tree -*/ - -#include "Log.h" -#include "PostQuantum.h" - -#if OPENSSL_PQ - -#include -#include - -namespace i2p -{ -namespace crypto -{ - MLKEMKeys::MLKEMKeys (MLKEMTypes type): - m_Name (std::get<0>(MLKEMS[type])), m_KeyLen (std::get<1>(MLKEMS[type])), - m_CTLen (std::get<2>(MLKEMS[type])), m_Pkey (nullptr) - { - } - - MLKEMKeys::~MLKEMKeys () - { - if (m_Pkey) EVP_PKEY_free (m_Pkey); - } - - void MLKEMKeys::GenerateKeys () - { - if (m_Pkey) EVP_PKEY_free (m_Pkey); - m_Pkey = EVP_PKEY_Q_keygen(NULL, NULL, m_Name.c_str ()); - } - - void MLKEMKeys::GetPublicKey (uint8_t * pub) const - { - if (m_Pkey) - { - size_t len = m_KeyLen; - EVP_PKEY_get_octet_string_param (m_Pkey, OSSL_PKEY_PARAM_PUB_KEY, pub, m_KeyLen, &len); - } - } - - void MLKEMKeys::SetPublicKey (const uint8_t * pub) - { - if (m_Pkey) - { - EVP_PKEY_free (m_Pkey); - m_Pkey = nullptr; - } - OSSL_PARAM params[] = - { - OSSL_PARAM_octet_string (OSSL_PKEY_PARAM_PUB_KEY, (uint8_t *)pub, m_KeyLen), - OSSL_PARAM_END - }; - EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new_from_name (NULL, m_Name.c_str (), NULL); - if (ctx) - { - EVP_PKEY_fromdata_init (ctx); - EVP_PKEY_fromdata (ctx, &m_Pkey, OSSL_KEYMGMT_SELECT_PUBLIC_KEY, params); - EVP_PKEY_CTX_free (ctx); - } - else - LogPrint (eLogError, "MLKEM can't create PKEY context"); - } - - void MLKEMKeys::Encaps (uint8_t * ciphertext, uint8_t * shared) - { - if (!m_Pkey) return; - auto ctx = EVP_PKEY_CTX_new_from_pkey (NULL, m_Pkey, NULL); - if (ctx) - { - EVP_PKEY_encapsulate_init (ctx, NULL); - size_t len = m_CTLen, sharedLen = 32; - EVP_PKEY_encapsulate (ctx, ciphertext, &len, shared, &sharedLen); - EVP_PKEY_CTX_free (ctx); - } - else - LogPrint (eLogError, "MLKEM can't create PKEY context"); - } - - void MLKEMKeys::Decaps (const uint8_t * ciphertext, uint8_t * shared) - { - if (!m_Pkey) return; - auto ctx = EVP_PKEY_CTX_new_from_pkey (NULL, m_Pkey, NULL); - if (ctx) - { - EVP_PKEY_decapsulate_init (ctx, NULL); - size_t sharedLen = 32; - EVP_PKEY_decapsulate (ctx, shared, &sharedLen, ciphertext, m_CTLen); - EVP_PKEY_CTX_free (ctx); - } - else - LogPrint (eLogError, "MLKEM can't create PKEY context"); - } - - std::unique_ptr CreateMLKEMKeys (i2p::data::CryptoKeyType type) - { - if (type <= i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD || - type - i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD > (int)MLKEMS.size ()) return nullptr; - return std::make_unique((MLKEMTypes)(type - i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD - 1)); - } - - static constexpr std::array, std::array >, 3> NoiseIKInitMLKEMKeys = - { - std::make_pair - ( - std::array - { - 0xb0, 0x8f, 0xb1, 0x73, 0x92, 0x66, 0xc9, 0x90, 0x45, 0x7f, 0xdd, 0xc6, 0x4e, 0x55, 0x40, 0xd8, - 0x0a, 0x37, 0x99, 0x06, 0x92, 0x2a, 0x78, 0xc4, 0xb1, 0xef, 0x86, 0x06, 0xd0, 0x15, 0x9f, 0x4d - }, // SHA256("Noise_IKhfselg2_25519+MLKEM512_ChaChaPoly_SHA256") - std::array - { - 0x95, 0x8d, 0xf6, 0x6c, 0x95, 0xce, 0xa9, 0xf7, 0x42, 0xfc, 0xfa, 0x62, 0x71, 0x36, 0x1e, 0xa7, - 0xdc, 0x7a, 0xc0, 0x75, 0x01, 0xcf, 0xf9, 0xfc, 0x9f, 0xdb, 0x4c, 0x68, 0x3a, 0x53, 0x49, 0xeb - } // SHA256 (first) - ), - std::make_pair - ( - std::array - { - 0x36, 0x03, 0x90, 0x2d, 0xf9, 0xa2, 0x2a, 0x5e, 0xc9, 0x3d, 0xdb, 0x8f, 0xa8, 0x1b, 0xdb, 0x4b, - 0xae, 0x9d, 0x93, 0x9c, 0xdf, 0xaf, 0xde, 0x55, 0x49, 0x13, 0xfe, 0x98, 0xf8, 0x4a, 0xd4, 0xbd - }, // SHA256("Noise_IKhfselg2_25519+MLKEM768_ChaChaPoly_SHA256") - std::array - { - 0x15, 0x44, 0x89, 0xbf, 0x30, 0xf0, 0xc9, 0x77, 0x66, 0x10, 0xcb, 0xb1, 0x57, 0x3f, 0xab, 0x68, - 0x79, 0x57, 0x39, 0x57, 0x0a, 0xe7, 0xc0, 0x31, 0x8a, 0xa2, 0x96, 0xef, 0xbf, 0xa9, 0x6a, 0xbb - } // SHA256 (first) - ), - std::make_pair - ( - std::array - { - 0x86, 0xa5, 0x36, 0x44, 0xc6, 0x12, 0xd5, 0x71, 0xa1, 0x2d, 0xd8, 0xb6, 0x0a, 0x00, 0x9f, 0x2c, - 0x1a, 0xa8, 0x7d, 0x22, 0xa4, 0xff, 0x2b, 0xcd, 0x61, 0x34, 0x97, 0x6d, 0xa1, 0x49, 0xeb, 0x4a - }, // SHA256("Noise_IKhfselg2_25519+MLKEM1024_ChaChaPoly_SHA256") - std::array - { - 0x42, 0x0d, 0xc2, 0x1c, 0x7b, 0x18, 0x61, 0xb7, 0x4a, 0x04, 0x3d, 0xae, 0x0f, 0xdc, 0xf2, 0x71, - 0xb9, 0xba, 0x19, 0xbb, 0xbd, 0x5f, 0xd4, 0x9c, 0x3f, 0x4b, 0x01, 0xed, 0x6d, 0x13, 0x1d, 0xa2 - } // SHA256 (first) - ) - }; - - void InitNoiseIKStateMLKEM (NoiseSymmetricState& state, i2p::data::CryptoKeyType type, const uint8_t * pub) - { - if (type <= i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD || - type - i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD > (int)NoiseIKInitMLKEMKeys.size ()) return; - auto ind = type - i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD - 1; - state.Init (NoiseIKInitMLKEMKeys[ind].first.data(), NoiseIKInitMLKEMKeys[ind].second.data(), pub); - } -} -} - -#endif \ No newline at end of file diff --git a/libi2pd/PostQuantum.h b/libi2pd/PostQuantum.h deleted file mode 100644 index f426d661..00000000 --- a/libi2pd/PostQuantum.h +++ /dev/null @@ -1,88 +0,0 @@ -/* -* Copyright (c) 2025, 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 POST_QUANTUM_H__ -#define POST_QUANTUM_H__ - -#include -#include -#include -#include -#include "Crypto.h" -#include "Identity.h" - -#if OPENSSL_PQ - -namespace i2p -{ -namespace crypto -{ - enum MLKEMTypes - { - eMLKEM512 = 0, - eMLKEM768, - eMLKEM1024 - }; - - constexpr size_t MLKEM512_KEY_LENGTH = 800; - constexpr size_t MLKEM512_CIPHER_TEXT_LENGTH = 768; - constexpr size_t MLKEM768_KEY_LENGTH = 1184; - constexpr size_t MLKEM768_CIPHER_TEXT_LENGTH = 1088; - constexpr size_t MLKEM1024_KEY_LENGTH = 1568; - constexpr size_t MLKEM1024_CIPHER_TEXT_LENGTH = 1568; - - constexpr std::array, 3> MLKEMS = - { - std::make_tuple ("ML-KEM-512", MLKEM512_KEY_LENGTH, MLKEM512_CIPHER_TEXT_LENGTH), - std::make_tuple ("ML-KEM-768", MLKEM768_KEY_LENGTH, MLKEM768_CIPHER_TEXT_LENGTH), - std::make_tuple ("ML-KEM-1024", MLKEM1024_KEY_LENGTH, MLKEM1024_CIPHER_TEXT_LENGTH) - }; - - constexpr size_t GetMLKEMPublicKeyLen (i2p::data::CryptoKeyType type) - { - if (type <= i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD || - type - i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD > (int)MLKEMS.size ()) return 0; - return std::get<1>(MLKEMS[type - i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD - 1]); - } - - constexpr size_t GetMLKEMCipherTextLen (i2p::data::CryptoKeyType type) - { - if (type <= i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD || - type - i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD > (int)MLKEMS.size ()) return 0; - return std::get<2>(MLKEMS[type - i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD - 1]); - } - - class MLKEMKeys - { - public: - - MLKEMKeys (MLKEMTypes type); - ~MLKEMKeys (); - - void GenerateKeys (); - void GetPublicKey (uint8_t * pub) const; - void SetPublicKey (const uint8_t * pub); - void Encaps (uint8_t * ciphertext, uint8_t * shared); - void Decaps (const uint8_t * ciphertext, uint8_t * shared); - - private: - - const std::string m_Name; - const size_t m_KeyLen, m_CTLen; - EVP_PKEY * m_Pkey; - }; - - std::unique_ptr CreateMLKEMKeys (i2p::data::CryptoKeyType type); - - void InitNoiseIKStateMLKEM (NoiseSymmetricState& state, i2p::data::CryptoKeyType type, const uint8_t * pub); // Noise_IK (ratchets) PQ ML-KEM5 -} -} - -#endif - -#endif diff --git a/libi2pd/Profiling.cpp b/libi2pd/Profiling.cpp index fe7f9905..27925434 100644 --- a/libi2pd/Profiling.cpp +++ b/libi2pd/Profiling.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2025, The PurpleI2P Project +* Copyright (c) 2013-2024, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -27,15 +27,13 @@ namespace data static i2p::fs::HashedStorage g_ProfilesStorage("peerProfiles", "p", "profile-", "txt"); static std::unordered_map > g_Profiles; static std::mutex g_ProfilesMutex; - static std::list)> > > g_PostponedUpdates; - static std::mutex g_PostponedUpdatesMutex; - + RouterProfile::RouterProfile (): m_IsUpdated (false), m_LastDeclineTime (0), m_LastUnreachableTime (0), - m_LastUpdateTime (i2p::util::GetSecondsSinceEpoch ()), m_LastAccessTime (0), - m_LastPersistTime (0), m_NumTunnelsAgreed (0), m_NumTunnelsDeclined (0), - m_NumTunnelsNonReplied (0),m_NumTimesTaken (0), m_NumTimesRejected (0), - m_HasConnected (false), m_IsDuplicated (false) + m_LastUpdateTime (i2p::util::GetSecondsSinceEpoch ()), + m_NumTunnelsAgreed (0), m_NumTunnelsDeclined (0), m_NumTunnelsNonReplied (0), + m_NumTimesTaken (0), m_NumTimesRejected (0), m_HasConnected (false), + m_IsDuplicated (false) { } @@ -80,7 +78,6 @@ namespace data void RouterProfile::Load (const IdentHash& identHash) { - m_IsUpdated = false; std::string ident = identHash.ToBase64 (); std::string path = g_ProfilesStorage.Path(ident); boost::property_tree::ptree pt; @@ -209,9 +206,10 @@ namespace data return m_NumTunnelsNonReplied > 10*(total + 1); } - bool RouterProfile::IsDeclinedRecently (uint64_t ts) + bool RouterProfile::IsDeclinedRecently () { if (!m_LastDeclineTime) return false; + auto ts = i2p::util::GetSecondsSinceEpoch (); if (ts > m_LastDeclineTime + PEER_PROFILE_DECLINED_RECENTLY_INTERVAL || ts + PEER_PROFILE_DECLINED_RECENTLY_INTERVAL < m_LastDeclineTime) m_LastDeclineTime = 0; @@ -220,10 +218,7 @@ namespace data bool RouterProfile::IsBad () { - if (IsUnreachable () || m_IsDuplicated) return true; - auto ts = i2p::util::GetSecondsSinceEpoch (); - if (ts > PEER_PROFILE_MAX_DECLINED_INTERVAL + m_LastDeclineTime) return false; - if (IsDeclinedRecently (ts)) return true; + if (IsDeclinedRecently () || IsUnreachable () || m_IsDuplicated) return true; auto isBad = IsAlwaysDeclining () || IsLowPartcipationRate () /*|| IsLowReplyRate ()*/; if (isBad && m_NumTimesRejected > 10*(m_NumTimesTaken + 1)) { @@ -258,42 +253,30 @@ namespace data std::unique_lock l(g_ProfilesMutex); auto it = g_Profiles.find (identHash); if (it != g_Profiles.end ()) - { - it->second->SetLastAccessTime (i2p::util::GetSecondsSinceEpoch ()); return it->second; - } } auto profile = netdb.NewRouterProfile (); profile->Load (identHash); // if possible - std::lock_guard l(g_ProfilesMutex); + std::unique_lock l(g_ProfilesMutex); g_Profiles.emplace (identHash, profile); return profile; } bool IsRouterBanned (const IdentHash& identHash) { - std::lock_guard l(g_ProfilesMutex); + std::unique_lock l(g_ProfilesMutex); auto it = g_Profiles.find (identHash); if (it != g_Profiles.end ()) return it->second->IsUnreachable (); return false; } - - bool IsRouterDuplicated (const IdentHash& identHash) - { - std::lock_guard l(g_ProfilesMutex); - auto it = g_Profiles.find (identHash); - if (it != g_Profiles.end ()) - return it->second->IsDuplicated (); - return false; - } void InitProfilesStorage () { g_ProfilesStorage.SetPlace(i2p::fs::GetDataDir()); g_ProfilesStorage.Init(i2p::data::GetBase64SubstitutionTable(), 64); } - + static void SaveProfilesToDisk (std::list > >&& profiles) { for (auto& it: profiles) @@ -305,17 +288,15 @@ namespace data auto ts = i2p::util::GetSecondsSinceEpoch (); std::list > > tmp; { - std::lock_guard l(g_ProfilesMutex); + std::unique_lock l(g_ProfilesMutex); for (auto it = g_Profiles.begin (); it != g_Profiles.end ();) { - if (it->second->IsUpdated () && ts > it->second->GetLastPersistTime () + PEER_PROFILE_PERSIST_INTERVAL) + if (ts - it->second->GetLastUpdateTime () > PEER_PROFILE_PERSIST_INTERVAL) { - tmp.push_back (*it); - it->second->SetLastPersistTime (ts); - it->second->SetUpdated (false); - } - if (!it->second->IsUpdated () && ts > std::max (it->second->GetLastUpdateTime (), it->second->GetLastAccessTime ()) + PEER_PROFILE_PERSIST_INTERVAL) + if (it->second->IsUpdated ()) + tmp.push_back (std::make_pair (it->first, it->second)); it = g_Profiles.erase (it); + } else it++; } @@ -329,7 +310,7 @@ namespace data { std::unordered_map > tmp; { - std::lock_guard l(g_ProfilesMutex); + std::unique_lock l(g_ProfilesMutex); std::swap (tmp, g_Profiles); } auto ts = i2p::util::GetSecondsSinceEpoch (); @@ -364,7 +345,7 @@ namespace data { { auto ts = i2p::util::GetSecondsSinceEpoch (); - std::lock_guard l(g_ProfilesMutex); + std::unique_lock l(g_ProfilesMutex); for (auto it = g_Profiles.begin (); it != g_Profiles.end ();) { if (ts - it->second->GetLastUpdateTime () >= PEER_PROFILE_EXPIRATION_TIMEOUT) @@ -376,47 +357,5 @@ namespace data return std::async (std::launch::async, DeleteFilesFromDisk); } - - bool UpdateRouterProfile (const IdentHash& identHash, std::function)> update) - { - if (!update) return true; - std::shared_ptr profile; - { - std::lock_guard l(g_ProfilesMutex); - auto it = g_Profiles.find (identHash); - if (it != g_Profiles.end ()) - profile = it->second; - } - if (profile) - { - update (profile); - return true; - } - // postpone - std::lock_guard l(g_PostponedUpdatesMutex); - g_PostponedUpdates.emplace_back (identHash, update); - return false; - } - - static void ApplyPostponedUpdates (std::list)> > >&& updates) - { - for (const auto& [ident, update] : updates) - { - auto profile = GetRouterProfile (ident); - update (profile); - } - } - - std::future FlushPostponedRouterProfileUpdates () - { - if (g_PostponedUpdates.empty ()) return std::future(); - - std::list)> > > updates; - { - std::lock_guard l(g_PostponedUpdatesMutex); - g_PostponedUpdates.swap (updates); - } - return std::async (std::launch::async, ApplyPostponedUpdates, std::move (updates)); - } } } diff --git a/libi2pd/Profiling.h b/libi2pd/Profiling.h index 59995b3f..1846f08e 100644 --- a/libi2pd/Profiling.h +++ b/libi2pd/Profiling.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2025, The PurpleI2P Project +* Copyright (c) 2013-2024, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -11,8 +11,6 @@ #include #include -#include -#include #include "Identity.h" namespace i2p @@ -39,15 +37,11 @@ namespace data const int PEER_PROFILE_AUTOCLEAN_VARIANCE = 900; // in seconds (15 minutes) const int PEER_PROFILE_OBSOLETE_PROFILES_CLEAN_TIMEOUT = 5400; // in seconds (1.5 hours) const int PEER_PROFILE_OBSOLETE_PROFILES_CLEAN_VARIANCE = 2400; // in seconds (40 minutes) - const int PEER_PROFILE_DECLINED_RECENTLY_INTERVAL = 330; // in seconds (5.5 minutes) - const int PEER_PROFILE_MAX_DECLINED_INTERVAL = 4400; // in second (1.5 hours) - const int PEER_PROFILE_PERSIST_INTERVAL = 1320; // in seconds (22 minutes) + 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 = 480; // in seconds (8 minutes) const int PEER_PROFILE_USEFUL_THRESHOLD = 3; - const int PEER_PROFILE_ALWAYS_DECLINING_NUM = 5; // num declines in row to consider always declined - const int PEER_PROFILE_APPLY_POSTPONED_TIMEOUT = 2100; // in milliseconds - const int PEER_PROFILE_APPLY_POSTPONED_TIMEOUT_VARIANCE = 500; // in milliseconds - + class RouterProfile { public: @@ -70,19 +64,9 @@ namespace data uint64_t GetLastUpdateTime () const { return m_LastUpdateTime; }; bool IsUpdated () const { return m_IsUpdated; }; - void SetUpdated (bool updated) { m_IsUpdated = updated; } - uint64_t GetLastAccessTime () const { return m_LastAccessTime; }; - void SetLastAccessTime (uint64_t ts) { m_LastAccessTime = ts; }; - uint64_t GetLastPersistTime () const { return m_LastPersistTime; }; - void SetLastPersistTime (uint64_t ts) { m_LastPersistTime = ts; }; bool IsUseful() const; bool IsDuplicated () const { return m_IsDuplicated; }; - - const boost::asio::ip::udp::endpoint& GetLastEndpoint () const { return m_LastEndpoint; } - void SetLastEndpoint (const boost::asio::ip::udp::endpoint& ep) { m_LastEndpoint = ep; } - bool HasLastEndpoint (bool v4) const { return !m_LastEndpoint.address ().is_unspecified () && m_LastEndpoint.port () && - ((v4 && m_LastEndpoint.address ().is_v4 ()) || (!v4 && m_LastEndpoint.address ().is_v6 ())); } private: @@ -91,13 +75,12 @@ namespace data bool IsAlwaysDeclining () const { return !m_NumTunnelsAgreed && m_NumTunnelsDeclined >= 5; }; bool IsLowPartcipationRate () const; bool IsLowReplyRate () const; - bool IsDeclinedRecently (uint64_t ts); + bool IsDeclinedRecently (); private: bool m_IsUpdated; - uint64_t m_LastDeclineTime, m_LastUnreachableTime, m_LastUpdateTime, - m_LastAccessTime, m_LastPersistTime; // in seconds + uint64_t m_LastDeclineTime, m_LastUnreachableTime, m_LastUpdateTime; // in seconds // participation uint32_t m_NumTunnelsAgreed; uint32_t m_NumTunnelsDeclined; @@ -107,19 +90,14 @@ namespace data uint32_t m_NumTimesRejected; bool m_HasConnected; // successful trusted(incoming or NTCP2) connection bool m_IsDuplicated; - // connectivity - boost::asio::ip::udp::endpoint m_LastEndpoint; // SSU2 for non-published addresses }; std::shared_ptr GetRouterProfile (const IdentHash& identHash); bool IsRouterBanned (const IdentHash& identHash); // check only existing profiles - bool IsRouterDuplicated (const IdentHash& identHash); // check only existing profiles void InitProfilesStorage (); std::future DeleteObsoleteProfiles (); void SaveProfiles (); std::future PersistProfiles (); - bool UpdateRouterProfile (const IdentHash& identHash, std::function)> update); // return true if updated immediately, and false if postponed - std::future FlushPostponedRouterProfileUpdates (); } } diff --git a/libi2pd/Queue.h b/libi2pd/Queue.h index 0e3e4fde..441f8c3a 100644 --- a/libi2pd/Queue.h +++ b/libi2pd/Queue.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2024, The PurpleI2P Project +* Copyright (c) 2013-2020, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -9,7 +9,8 @@ #ifndef QUEUE_H__ #define QUEUE_H__ -#include +#include +#include #include #include #include @@ -28,20 +29,22 @@ namespace util void Put (Element e) { std::unique_lock l(m_QueueMutex); - m_Queue.push_back (std::move(e)); + m_Queue.push (std::move(e)); m_NonEmpty.notify_one (); } - void Put (std::list& list) + templateclass Container, typename... R> + void Put (const Container& vec) { - if (!list.empty ()) + if (!vec.empty ()) { std::unique_lock l(m_QueueMutex); - m_Queue.splice (m_Queue.end (), list); + for (const auto& it: vec) + m_Queue.push (std::move(it)); m_NonEmpty.notify_one (); - } - } - + } + } + Element GetNext () { std::unique_lock l(m_QueueMutex); @@ -84,7 +87,7 @@ namespace util return m_Queue.empty (); } - int GetSize () const + int GetSize () { std::unique_lock l(m_QueueMutex); return m_Queue.size (); @@ -104,28 +107,15 @@ namespace util return GetNonThreadSafe (true); } - void GetWholeQueue (std::list& queue) - { - if (!queue.empty ()) - { - std::list newQueue; - queue.swap (newQueue); - } - { - std::unique_lock l(m_QueueMutex); - m_Queue.swap (queue); - } - } - private: - + Element GetNonThreadSafe (bool peek = false) { if (!m_Queue.empty ()) { auto el = m_Queue.front (); if (!peek) - m_Queue.pop_front (); + m_Queue.pop (); return el; } return nullptr; @@ -133,8 +123,8 @@ namespace util private: - std::list m_Queue; - mutable std::mutex m_QueueMutex; + std::queue m_Queue; + std::mutex m_QueueMutex; std::condition_variable m_NonEmpty; }; } diff --git a/libi2pd/Reseed.cpp b/libi2pd/Reseed.cpp index e58e898b..f8307a56 100644 --- a/libi2pd/Reseed.cpp +++ b/libi2pd/Reseed.cpp @@ -552,7 +552,7 @@ namespace data if (!url.port) url.port = 443; - boost::asio::io_context service; + boost::asio::io_service service; boost::system::error_code ecode; boost::asio::ssl::context ctx(boost::asio::ssl::context::sslv23); @@ -562,10 +562,11 @@ namespace data if(proxyUrl.schema.size()) { // proxy connection - auto it = boost::asio::ip::tcp::resolver(service).resolve (proxyUrl.host, std::to_string(proxyUrl.port), ecode); + auto it = boost::asio::ip::tcp::resolver(service).resolve ( + boost::asio::ip::tcp::resolver::query (proxyUrl.host, std::to_string(proxyUrl.port)), ecode); if(!ecode) { - s.lowest_layer().connect(*it.begin (), ecode); + s.lowest_layer().connect(*it, ecode); if(!ecode) { auto & sock = s.next_layer(); @@ -598,7 +599,7 @@ namespace data LogPrint(eLogError, "Reseed: HTTP CONNECT read error: ", ecode.message()); return ""; } - if(proxyRes.parse(std::string {boost::asio::buffers_begin(readbuf.data ()), boost::asio::buffers_begin(readbuf.data ()) + readbuf.size ()}) <= 0) + if(proxyRes.parse(boost::asio::buffer_cast(readbuf.data()), readbuf.size()) <= 0) { sock.close(); LogPrint(eLogError, "Reseed: HTTP CONNECT malformed reply"); @@ -637,13 +638,15 @@ namespace data else { // direct connection - auto endpoints = boost::asio::ip::tcp::resolver(service).resolve (url.host, std::to_string(url.port), ecode); + auto it = boost::asio::ip::tcp::resolver(service).resolve ( + boost::asio::ip::tcp::resolver::query (url.host, std::to_string(url.port)), ecode); if (!ecode) { bool connected = false; - for (const auto& it: endpoints) + boost::asio::ip::tcp::resolver::iterator end; + while (it != end) { - boost::asio::ip::tcp::endpoint ep = it; + boost::asio::ip::tcp::endpoint ep = *it; bool supported = false; if (!ep.address ().is_unspecified ()) { @@ -663,6 +666,7 @@ namespace data break; } } + it++; } if (!connected) { @@ -742,16 +746,19 @@ namespace data if (!url.port) url.port = 80; boost::system::error_code ecode; - boost::asio::io_context service; + boost::asio::io_service service; boost::asio::ip::tcp::socket s(service, boost::asio::ip::tcp::v6()); - auto endpoints = boost::asio::ip::tcp::resolver(service).resolve (url.host, std::to_string(url.port), ecode); + auto it = boost::asio::ip::tcp::resolver(service).resolve ( + boost::asio::ip::tcp::resolver::query (url.host, std::to_string(url.port)), ecode); + if (!ecode) { bool connected = false; - for (const auto& it: endpoints) + boost::asio::ip::tcp::resolver::iterator end; + while (it != end) { - boost::asio::ip::tcp::endpoint ep = it; + boost::asio::ip::tcp::endpoint ep = *it; if ( i2p::util::net::IsYggdrasilAddress (ep.address ()) && i2p::context.SupportsMesh () @@ -765,6 +772,7 @@ namespace data break; } } + it++; } if (!connected) { diff --git a/libi2pd/RouterContext.cpp b/libi2pd/RouterContext.cpp index 33fb5487..1efb25db 100644 --- a/libi2pd/RouterContext.cpp +++ b/libi2pd/RouterContext.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2025, The PurpleI2P Project +* Copyright (c) 2013-2024, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -22,7 +22,6 @@ #include "ECIESX25519AEADRatchetSession.h" #include "Transports.h" #include "Tunnel.h" -#include "CryptoKey.h" #include "RouterContext.h" namespace i2p @@ -34,14 +33,13 @@ namespace i2p m_ShareRatio (100), m_Status (eRouterStatusUnknown), m_StatusV6 (eRouterStatusUnknown), m_Error (eRouterErrorNone), m_ErrorV6 (eRouterErrorNone), m_Testing (false), m_TestingV6 (false), m_NetID (I2PD_NET_ID), - m_PublishReplyToken (0), m_IsHiddenMode (false), - m_Rng(i2p::util::GetMonotonicMicroseconds () % 1000000LL), m_IsSaving (false) + m_PublishReplyToken (0), m_IsHiddenMode (false) { } void RouterContext::Init () { - srand (m_Rng () % 1000); + srand (i2p::util::GetMillisecondsSinceEpoch () % 1000); m_StartupTime = i2p::util::GetMonotonicSeconds (); if (!Load ()) @@ -78,7 +76,7 @@ namespace i2p m_CongestionUpdateTimer->cancel (); m_Service->Stop (); CleanUp (); // GarlicDestination - } + } } std::shared_ptr RouterContext::CopyRouterInfoBuffer () const @@ -142,7 +140,7 @@ namespace i2p { boost::asio::ip::address addr; if (!host.empty ()) - addr = boost::asio::ip::make_address (host); + addr = boost::asio::ip::address::from_string (host); if (!addr.is_v4()) addr = boost::asio::ip::address_v4 (); routerInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv, addr, ntcp2Port); @@ -163,7 +161,7 @@ namespace i2p { boost::asio::ip::address addr; if (!host.empty ()) - addr = boost::asio::ip::make_address (host); + addr = boost::asio::ip::address::from_string (host); if (!addr.is_v4()) addr = boost::asio::ip::address_v4 (); routerInfo.AddSSU2Address (m_SSU2Keys->staticPublicKey, m_SSU2Keys->intro, addr, ssu2Port); @@ -194,7 +192,7 @@ namespace i2p ntcp2Host = host; boost::asio::ip::address addr; if (!ntcp2Host.empty ()) - addr = boost::asio::ip::make_address (ntcp2Host); + addr = boost::asio::ip::address::from_string (ntcp2Host); if (!addr.is_v6()) addr = boost::asio::ip::address_v6 (); routerInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv, addr, ntcp2Port); @@ -213,7 +211,7 @@ namespace i2p { boost::asio::ip::address addr; if (!host.empty ()) - addr = boost::asio::ip::make_address (host); + addr = boost::asio::ip::address::from_string (host); if (!addr.is_v6()) addr = boost::asio::ip::address_v6 (); routerInfo.AddSSU2Address (m_SSU2Keys->staticPublicKey, m_SSU2Keys->intro, addr, ssu2Port); @@ -255,36 +253,11 @@ namespace i2p void RouterContext::UpdateRouterInfo () { - std::shared_ptr buffer; { std::lock_guard l(m_RouterInfoMutex); m_RouterInfo.CreateBuffer (m_Keys); - buffer = m_RouterInfo.CopyBuffer (); } - { - // update save buffer to latest - std::lock_guard l(m_SaveBufferMutex); - m_SaveBuffer = buffer; - } - bool isSaving = false; - if (m_IsSaving.compare_exchange_strong (isSaving, true)) // try to save only if not being saved - { - auto savingRouterInfo = std::async (std::launch::async, [this]() - { - std::shared_ptr buffer; - while (m_SaveBuffer) - { - { - std::lock_guard l(m_SaveBufferMutex); - buffer = m_SaveBuffer; - m_SaveBuffer = nullptr; - } - if (buffer) - i2p::data::RouterInfo::SaveToFile (i2p::fs::DataDirPath (ROUTER_INFO), buffer); - } - m_IsSaving = false; - }); - } + m_RouterInfo.SaveToFile (i2p::fs::DataDirPath (ROUTER_INFO)); m_LastUpdateTime = i2p::util::GetSecondsSinceEpoch (); } @@ -350,11 +323,9 @@ namespace i2p case eRouterStatusFirewalled: SetUnreachable (true, false); // ipv4 break; - case eRouterStatusMesh: - m_RouterInfo.UpdateCaps (m_RouterInfo.GetCaps () | i2p::data::RouterInfo::eReachable); - break; case eRouterStatusProxy: - m_RouterInfo.UpdateCaps (m_RouterInfo.GetCaps () | i2p::data::RouterInfo::eUnreachable); + m_AcceptsTunnels = false; + UpdateCongestion (); break; default: ; @@ -676,12 +647,12 @@ namespace i2p void RouterContext::SetBandwidth (int limit) { - if (limit > (int)i2p::data::EXTRA_BANDWIDTH_LIMIT) { SetBandwidth('X'); } - else if (limit > (int)i2p::data::HIGH_BANDWIDTH_LIMIT) { SetBandwidth('P'); } - else if (limit > 128) { SetBandwidth('O'); } - else if (limit > 64) { SetBandwidth('N'); } - else if (limit > (int)i2p::data::LOW_BANDWIDTH_LIMIT) { SetBandwidth('M'); } - else if (limit > 12) { SetBandwidth('L'); } + if (limit > 2000) { SetBandwidth('X'); } + else if (limit > 256) { SetBandwidth('P'); } + else if (limit > 128) { SetBandwidth('O'); } + else if (limit > 64) { SetBandwidth('N'); } + else if (limit > 48) { SetBandwidth('M'); } + else if (limit > 12) { SetBandwidth('L'); } else { SetBandwidth('K'); } m_BandwidthLimit = limit; // set precise limit } @@ -829,7 +800,7 @@ namespace i2p i2p::config::GetOption("host", ntcp2Host); if (!ntcp2Host.empty () && ntcp2Port) { - auto addr = boost::asio::ip::make_address (ntcp2Host); + auto addr = boost::asio::ip::address::from_string (ntcp2Host); if (addr.is_v6 ()) { m_RouterInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv, addr, ntcp2Port); @@ -858,7 +829,7 @@ namespace i2p std::string host; i2p::config::GetOption("host", host); if (!host.empty ()) { - auto addr = boost::asio::ip::make_address (host); + auto addr = boost::asio::ip::address::from_string (host); if (addr.is_v6 ()) { m_RouterInfo.AddSSU2Address (m_SSU2Keys->staticPublicKey, m_SSU2Keys->intro, addr, ssu2Port); @@ -927,7 +898,7 @@ namespace i2p std::string host; i2p::config::GetOption("host", host); if (!host.empty ()) { - auto addr = boost::asio::ip::make_address (host); + auto addr = boost::asio::ip::address::from_string (host); if (addr.is_v4 ()) { m_RouterInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv, addr, ntcp2Port); @@ -957,7 +928,7 @@ namespace i2p std::string host; i2p::config::GetOption("host", host); if (!host.empty ()) { - auto addr = boost::asio::ip::make_address (host); + auto addr = boost::asio::ip::address::from_string (host); if (addr.is_v4 ()) { m_RouterInfo.AddSSU2Address (m_SSU2Keys->staticPublicKey, m_SSU2Keys->intro, addr, ssu2Port); @@ -1194,8 +1165,7 @@ namespace i2p i2p::HandleI2NPMessage (CreateI2NPMessage (buf, GetI2NPMessageLength (buf, len))); } - bool RouterContext::HandleCloveI2NPMessage (I2NPMessageType typeID, const uint8_t * payload, - size_t len, uint32_t msgID, i2p::garlic::ECIESX25519AEADRatchetSession * from) + bool RouterContext::HandleCloveI2NPMessage (I2NPMessageType typeID, const uint8_t * payload, size_t len, uint32_t msgID) { if (typeID == eI2NPTunnelTest) { @@ -1213,7 +1183,7 @@ namespace i2p void RouterContext::ProcessGarlicMessage (std::shared_ptr msg) { if (m_Service) - boost::asio::post (m_Service->GetService (), std::bind (&RouterContext::PostGarlicMessage, this, msg)); + m_Service->GetService ().post (std::bind (&RouterContext::PostGarlicMessage, this, msg)); else LogPrint (eLogError, "Router: service is NULL"); } @@ -1241,7 +1211,7 @@ namespace i2p void RouterContext::ProcessDeliveryStatusMessage (std::shared_ptr msg) { if (m_Service) - boost::asio::post (m_Service->GetService (), std::bind (&RouterContext::PostDeliveryStatusMessage, this, msg)); + m_Service->GetService ().post (std::bind (&RouterContext::PostDeliveryStatusMessage, this, msg)); else LogPrint (eLogError, "Router: service is NULL"); } @@ -1270,7 +1240,7 @@ namespace i2p } data; memcpy (data.k, key, 32); data.t = tag; - boost::asio::post (m_Service->GetService (), [this,data](void) + m_Service->GetService ().post ([this,data](void) { AddECIESx25519Key (data.k, data.t); }); @@ -1387,7 +1357,7 @@ namespace i2p { m_PublishTimer->cancel (); m_PublishTimer->expires_from_now (boost::posix_time::seconds(ROUTER_INFO_PUBLISH_INTERVAL + - m_Rng () % ROUTER_INFO_PUBLISH_INTERVAL_VARIANCE)); + rand () % ROUTER_INFO_PUBLISH_INTERVAL_VARIANCE)); m_PublishTimer->async_wait (std::bind (&RouterContext::HandlePublishTimer, this, std::placeholders::_1)); } @@ -1436,7 +1406,7 @@ namespace i2p auto onDrop = [this]() { if (m_Service) - boost::asio::post (m_Service->GetService (), [this]() { HandlePublishResendTimer (boost::system::error_code ()); }); + m_Service->GetService ().post ([this]() { HandlePublishResendTimer (boost::system::error_code ()); }); }; if (i2p::transport::transports.IsConnected (floodfill->GetIdentHash ()) || // already connected (floodfill->IsReachableFrom (i2p::context.GetRouterInfo ()) && // are we able to connect @@ -1462,7 +1432,7 @@ namespace i2p i2p::garlic::WrapECIESX25519MessageForRouter (msg, floodfill->GetIdentity ()->GetEncryptionPublicKey ())); } else - LogPrint (eLogInfo, "Router: Can't publish our RouterInfo. No tunnels. Try again in ", ROUTER_INFO_CONFIRMATION_TIMEOUT, " milliseconds"); + LogPrint (eLogInfo, "Router: Can't publish our RouterInfo. No tunnles. Try again in ", ROUTER_INFO_CONFIRMATION_TIMEOUT, " seconds"); } m_PublishExcluded.insert (floodfill->GetIdentHash ()); m_PublishReplyToken = replyToken; @@ -1476,7 +1446,7 @@ namespace i2p if (m_PublishTimer) { m_PublishTimer->cancel (); - m_PublishTimer->expires_from_now (boost::posix_time::milliseconds(ROUTER_INFO_CONFIRMATION_TIMEOUT)); + m_PublishTimer->expires_from_now (boost::posix_time::seconds(ROUTER_INFO_CONFIRMATION_TIMEOUT)); m_PublishTimer->async_wait (std::bind (&RouterContext::HandlePublishResendTimer, this, std::placeholders::_1)); } @@ -1499,8 +1469,7 @@ namespace i2p if (m_CongestionUpdateTimer) { m_CongestionUpdateTimer->cancel (); - m_CongestionUpdateTimer->expires_from_now (boost::posix_time::seconds( - ROUTER_INFO_CONGESTION_UPDATE_INTERVAL + m_Rng () % ROUTER_INFO_CONGESTION_UPDATE_INTERVAL_VARIANCE)); + m_CongestionUpdateTimer->expires_from_now (boost::posix_time::seconds(ROUTER_INFO_CONGESTION_UPDATE_INTERVAL)); m_CongestionUpdateTimer->async_wait (std::bind (&RouterContext::HandleCongestionUpdateTimer, this, std::placeholders::_1)); } @@ -1520,7 +1489,7 @@ namespace i2p void RouterContext::UpdateCongestion () { auto c = i2p::data::RouterInfo::eLowCongestion; - if (!AcceptsTunnels () || !m_ShareRatio) + if (!AcceptsTunnels () || !m_ShareRatio) c = i2p::data::RouterInfo::eRejectAll; else { @@ -1539,7 +1508,7 @@ namespace i2p if (m_CleanupTimer) { m_CleanupTimer->cancel (); - m_CleanupTimer->expires_from_now (boost::posix_time::seconds(ROUTER_INFO_CLEANUP_INTERVAL)); + 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)); } diff --git a/libi2pd/RouterContext.h b/libi2pd/RouterContext.h index 754c49da..c620f8b1 100644 --- a/libi2pd/RouterContext.h +++ b/libi2pd/RouterContext.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2025, The PurpleI2P Project +* Copyright (c) 2013-2024, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -12,7 +12,6 @@ #include #include #include -#include #include #include #include "Identity.h" @@ -35,11 +34,10 @@ namespace garlic const int ROUTER_INFO_PUBLISH_INTERVAL = 39*60; // in seconds const int ROUTER_INFO_INITIAL_PUBLISH_INTERVAL = 10; // in seconds const int ROUTER_INFO_PUBLISH_INTERVAL_VARIANCE = 105;// in seconds - const int ROUTER_INFO_CONFIRMATION_TIMEOUT = 1600; // in milliseconds + 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 = 11*60; // in seconds - const int ROUTER_INFO_CONGESTION_UPDATE_INTERVAL_VARIANCE = 130; // in seconds - const int ROUTER_INFO_CLEANUP_INTERVAL = 102; // in seconds + const int ROUTER_INFO_CONGESTION_UPDATE_INTERVAL = 12*60; // in seconds + const int ROUTER_INFO_CLEANUP_INTERVAL = 5; // in minutes enum RouterStatus { @@ -92,7 +90,7 @@ namespace garlic public: RouterService (): RunnableServiceWithWork ("Router") {}; - auto& GetService () { return GetIOService (); }; + boost::asio::io_service& GetService () { return GetIOService (); }; void Start () { StartIOService (); }; void Stop () { StopIOService (); }; }; @@ -148,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 @@ -187,25 +186,24 @@ namespace garlic void UpdateTimestamp (uint64_t ts); // in seconds, called from NetDb before publishing // implements LocalDestination - std::shared_ptr GetIdentity () const override{ return m_Keys.GetPublic (); }; - bool Decrypt (const uint8_t * encrypted, uint8_t * data, i2p::data::CryptoKeyType preferredCrypto) const override; - void SetLeaseSetUpdated (bool post) override {}; + std::shared_ptr GetIdentity () const { return m_Keys.GetPublic (); }; + bool Decrypt (const uint8_t * encrypted, uint8_t * data, i2p::data::CryptoKeyType preferredCrypto) const; + void Sign (const uint8_t * buf, int len, uint8_t * signature) const { m_Keys.Sign (buf, len, signature); }; + void SetLeaseSetUpdated () {}; // implements GarlicDestination - std::shared_ptr GetLeaseSet () override { return nullptr; }; - std::shared_ptr GetTunnelPool () const override; + std::shared_ptr GetLeaseSet () { return nullptr; }; + std::shared_ptr GetTunnelPool () const; // override GarlicDestination - void ProcessGarlicMessage (std::shared_ptr msg) override; - void ProcessDeliveryStatusMessage (std::shared_ptr msg) override; - void SubmitECIESx25519Key (const uint8_t * key, uint64_t tag) override; + void ProcessGarlicMessage (std::shared_ptr msg); + void ProcessDeliveryStatusMessage (std::shared_ptr msg); protected: // implements GarlicDestination - void HandleI2NPMessage (const uint8_t * buf, size_t len) override; - bool HandleCloveI2NPMessage (I2NPMessageType typeID, const uint8_t * payload, - size_t len, uint32_t msgID, i2p::garlic::ECIESX25519AEADRatchetSession * from) override; + void HandleI2NPMessage (const uint8_t * buf, size_t len); + bool HandleCloveI2NPMessage (I2NPMessageType typeID, const uint8_t * payload, size_t len, uint32_t msgID); private: @@ -218,7 +216,6 @@ namespace garlic void UpdateSSU2Keys (); bool Load (); void SaveKeys (); - void Sign (const uint8_t * buf, int len, uint8_t * signature) const { m_Keys.Sign (buf, len, signature); }; uint16_t SelectRandomPort () const; void PublishNTCP2Address (std::shared_ptr address, int port, bool publish) const; @@ -266,10 +263,6 @@ namespace garlic uint32_t m_PublishReplyToken; bool m_IsHiddenMode; // not publish mutable std::mutex m_RouterInfoMutex; - std::mt19937 m_Rng; - std::shared_ptr m_SaveBuffer; - std::mutex m_SaveBufferMutex; // TODO: make m_SaveBuffer atomic - std::atomic m_IsSaving; }; extern RouterContext context; diff --git a/libi2pd/RouterInfo.cpp b/libi2pd/RouterInfo.cpp index 4af32f57..2da40ae8 100644 --- a/libi2pd/RouterInfo.cpp +++ b/libi2pd/RouterInfo.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2025, The PurpleI2P Project +* Copyright (c) 2013-2024, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -11,7 +11,7 @@ #include "I2PEndian.h" #include #include -#include +#include #include // for boost::to_lower #ifndef __cpp_lib_atomic_shared_ptr #include @@ -25,7 +25,6 @@ #include "Transports.h" #include "NetDb.hpp" #include "RouterContext.h" -#include "CryptoKey.h" #include "RouterInfo.h" namespace i2p @@ -46,9 +45,8 @@ namespace data RouterInfo::RouterInfo (const std::string& fullPath): m_FamilyID (0), m_IsUpdated (false), m_IsUnreachable (false), m_IsFloodfill (false), - m_IsBufferScheduledToDelete (false), m_SupportedTransports (0), - m_ReachableTransports (0), m_PublishedTransports (0), m_Caps (0), m_Version (0), - m_Congestion (eLowCongestion) + m_SupportedTransports (0),m_ReachableTransports (0), m_PublishedTransports (0), + m_Caps (0), m_Version (0), m_Congestion (eLowCongestion) { m_Addresses = AddressesPtr(new Addresses ()); // create empty list m_Buffer = RouterInfo::NewBuffer (); // always RouterInfo's @@ -57,7 +55,7 @@ namespace data RouterInfo::RouterInfo (std::shared_ptr&& buf, size_t len): m_FamilyID (0), m_IsUpdated (true), m_IsUnreachable (false), m_IsFloodfill (false), - m_IsBufferScheduledToDelete (false), m_SupportedTransports (0), m_ReachableTransports (0), m_PublishedTransports (0), + m_SupportedTransports (0), m_ReachableTransports (0), m_PublishedTransports (0), m_Caps (0), m_Version (0), m_Congestion (eLowCongestion) { if (len <= MAX_RI_BUFFER_SIZE) @@ -107,7 +105,8 @@ namespace data // skip identity size_t identityLen = m_RouterIdentity->GetFullLen (); // read new RI - ReadFromBuffer (buf + identityLen, len - identityLen); + std::stringstream str (std::string ((char *)buf + identityLen, len - identityLen)); + ReadFromStream (str); if (!m_IsUnreachable) UpdateBuffer (buf, len); // save buffer // don't delete buffer until saved to the file @@ -195,34 +194,39 @@ namespace data } } // parse RI - if (!ReadFromBuffer (m_Buffer->data () + identityLen, bufferLen - identityLen)) + std::stringstream str; + str.write ((const char *)m_Buffer->data () + identityLen, bufferLen - identityLen); + ReadFromStream (str); + if (!str) { LogPrint (eLogError, "RouterInfo: Malformed message"); m_IsUnreachable = true; - } + } } - bool RouterInfo::ReadFromBuffer (const uint8_t * buf, size_t len) + void RouterInfo::ReadFromStream (std::istream& s) { - if (len < 9) return false; + if (!s) return; m_Caps = 0; m_Congestion = eLowCongestion; - m_Timestamp = bufbe64toh (buf); - size_t offset = 8; // timestamp + s.read ((char *)&m_Timestamp, sizeof (m_Timestamp)); + m_Timestamp = be64toh (m_Timestamp); // read addresses auto addresses = NewAddresses (); - uint8_t numAddresses = buf[offset]; offset++; + uint8_t numAddresses; + s.read ((char *)&numAddresses, sizeof (numAddresses)); for (int i = 0; i < numAddresses; i++) { - if (offset + 9 > len) return false; // 1 byte cost + 8 bytes date uint8_t supportedTransports = 0; auto address = NewAddress (); - offset++; // cost, ignore - address->date = bufbe64toh (buf + offset); offset += 8; // date + uint8_t cost; // ignore + s.read ((char *)&cost, sizeof (cost)); + s.read ((char *)&address->date, sizeof (address->date)); bool isHost = false, isStaticKey = false, isV2 = false, isIntroKey = false; - auto transportStyle = ExtractString (buf + offset, len - offset); offset += transportStyle.length () + 1; - if (!transportStyle.compare (0, 4, "NTCP")) // NTCP or NTCP2 + char transportStyle[6]; + ReadString (transportStyle, 6, s); + if (!strncmp (transportStyle, "NTCP", 4)) // NTCP or NTCP2 address->transportStyle = eTransportNTCP2; - else if (!transportStyle.compare (0, 3, "SSU")) // SSU or SSU2 + else if (!strncmp (transportStyle, "SSU", 3)) // SSU or SSU2 { address->transportStyle = eTransportSSU2; address->ssu.reset (new SSUExt ()); @@ -232,25 +236,27 @@ namespace data address->transportStyle = eTransportUnknown; address->caps = 0; address->port = 0; - if (offset + 2 > len) return false; - uint16_t size = bufbe16toh (buf + offset); offset += 2; // size - if (offset + size >= len) return false; + uint16_t size, r = 0; + s.read ((char *)&size, sizeof (size)); if (!s) return; + size = be16toh (size); if (address->transportStyle == eTransportUnknown) { // skip unknown address - offset += size; - continue; + s.seekg (size, std::ios_base::cur); + if (s) continue; else return; } - size_t r = 0; while (r < size) { - auto [key, value, sz] = ExtractParam (buf + offset, len - offset); - r += sz; offset += sz; - if (key.empty ()) continue; - if (key == "host") + char key[255], value[255]; + r += ReadString (key, 255, s); + s.seekg (1, std::ios_base::cur); r++; // = + r += ReadString (value, 255, s); + s.seekg (1, std::ios_base::cur); r++; // ; + if (!s) return; + if (!strcmp (key, "host")) { boost::system::error_code ecode; - address->host = boost::asio::ip::make_address (value, ecode); + address->host = boost::asio::ip::address::from_string (value, ecode); if (!ecode && !address->host.is_unspecified ()) { if (!i2p::transport::transports.IsInReservedRange (address->host) || @@ -261,53 +267,63 @@ namespace data address->transportStyle = eTransportUnknown; } } - else if (key == "port") + else if (!strcmp (key, "port")) { - auto res = std::from_chars(value.data(), value.data() + value.size(), address->port); - if (res.ec != std::errc()) - LogPrint (eLogWarning, "RouterInfo: 'port' conversion error: ", std::make_error_code (res.ec).message ()); + try + { + address->port = boost::lexical_cast(value); + } + catch (std::exception& ex) + { + LogPrint (eLogWarning, "RouterInfo: 'port' exception ", ex.what ()); + } } - else if (key == "mtu") + else if (!strcmp (key, "mtu")) { if (address->ssu) { - auto res = std::from_chars(value.data(), value.data() + value.size(), address->ssu->mtu); - if (res.ec != std::errc()) - LogPrint (eLogWarning, "RouterInfo: 'mtu' conversion error: ", std::make_error_code (res.ec).message ()); + try + { + address->ssu->mtu = boost::lexical_cast(value); + } + catch (std::exception& ex) + { + LogPrint (eLogWarning, "RouterInfo: 'mtu' exception ", ex.what ()); + } } else LogPrint (eLogWarning, "RouterInfo: Unexpected field 'mtu' for NTCP2"); } - else if (key == "caps") + else if (!strcmp (key, "caps")) address->caps = ExtractAddressCaps (value); - else if (key == "s") // ntcp2 or ssu2 static key + else if (!strcmp (key, "s")) // ntcp2 or ssu2 static key { - if (Base64ToByteStream (value, address->s, 32) == 32 && + if (Base64ToByteStream (value, strlen (value), address->s, 32) == 32 && !(address->s[31] & 0x80)) // check if x25519 public key isStaticKey = true; else address->transportStyle = eTransportUnknown; // invalid address } - else if (key == "i") // ntcp2 iv or ssu2 intro + else if (!strcmp (key, "i")) // ntcp2 iv or ssu2 intro { if (address->IsNTCP2 ()) { - if (Base64ToByteStream (value, address->i, 16) == 16) + if (Base64ToByteStream (value, strlen (value), address->i, 16) == 16) address->published = true; // presence of "i" means "published" NTCP2 else address->transportStyle = eTransportUnknown; // invalid address } else if (address->IsSSU2 ()) { - if (Base64ToByteStream (value, address->i, 32) == 32) + if (Base64ToByteStream (value, strlen (value), address->i, 32) == 32) isIntroKey = true; else address->transportStyle = eTransportUnknown; // invalid address } } - else if (key == "v") + else if (!strcmp (key, "v")) { - if (value == "2") + if (!strcmp (value, "2")) isV2 = true; else { @@ -323,11 +339,13 @@ namespace data LogPrint (eLogError, "RouterInfo: Introducer is presented for non-SSU address. Skipped"); continue; } - unsigned char index = key[key.length () - 1] - '0'; // TODO: + size_t l = strlen(key); + unsigned char index = key[l-1] - '0'; // TODO: + key[l-1] = 0; if (index > 9) { LogPrint (eLogError, "RouterInfo: Unexpected introducer's index ", index, " skipped"); - continue; + if (s) continue; else return; } if (index >= address->ssu->introducers.size ()) { @@ -336,23 +354,34 @@ namespace data address->ssu->introducers.resize (index + 1); } Introducer& introducer = address->ssu->introducers.at (index); - auto key1 = key.substr(0, key.length () - 1); - if (key1 == "itag") + if (!strcmp (key, "itag")) { - auto res = std::from_chars(value.data(), value.data() + value.size(), introducer.iTag); - if (res.ec != std::errc()) - LogPrint (eLogWarning, "RouterInfo: 'itag' conversion error: ", std::make_error_code (res.ec).message ()); + try + { + introducer.iTag = boost::lexical_cast(value); + } + catch (std::exception& ex) + { + LogPrint (eLogWarning, "RouterInfo: 'itag' exception ", ex.what ()); + } } - else if (key1 == "ih") - Base64ToByteStream (value, introducer.iH, 32); - else if (key1 == "iexp") + else if (!strcmp (key, "ih")) + Base64ToByteStream (value, strlen (value), introducer.iH, 32); + else if (!strcmp (key, "iexp")) { - auto res = std::from_chars(value.data(), value.data() + value.size(), introducer.iExp); - if (res.ec != std::errc()) - LogPrint (eLogWarning, "RouterInfo: 'iexp' conversion error: ", std::make_error_code (res.ec).message ()); + try + { + introducer.iExp = boost::lexical_cast(value); + } + catch (std::exception& ex) + { + LogPrint (eLogWarning, "RouterInfo: 'iexp' exception ", ex.what ()); + } } } - } + if (!s) return; + } + if (address->transportStyle == eTransportNTCP2) { if (isStaticKey) @@ -416,73 +445,66 @@ namespace data boost::atomic_store (&m_Addresses, addresses); #endif // read peers - if (offset + 1 > len) return false; - uint8_t numPeers = buf[offset]; offset++; // num peers - offset += numPeers*32; // TODO: read peers + uint8_t numPeers; + s.read ((char *)&numPeers, sizeof (numPeers)); if (!s) return; + s.seekg (numPeers*32, std::ios_base::cur); // TODO: read peers // read properties - if (offset + 2 > len) return false; m_Version = 0; bool isNetId = false; std::string family; - uint16_t size = bufbe16toh (buf + offset); offset += 2; // size - if (offset + size > len) return false; - size_t r = 0; + uint16_t size, r = 0; + s.read ((char *)&size, sizeof (size)); if (!s) return; + size = be16toh (size); while (r < size) { - auto [key, value, sz] = ExtractParam (buf + offset, len - offset); - r += sz; offset += sz; - if (key.empty ()) continue; + char key[255], value[255]; + r += ReadString (key, 255, s); + s.seekg (1, std::ios_base::cur); r++; // = + r += ReadString (value, 255, s); + s.seekg (1, std::ios_base::cur); r++; // ; + if (!s) return; SetProperty (key, value); // extract caps - if (key == "caps") + if (!strcmp (key, "caps")) { ExtractCaps (value); m_IsFloodfill = IsDeclaredFloodfill (); } // extract version - else if (key == ROUTER_INFO_PROPERTY_VERSION) + else if (!strcmp (key, ROUTER_INFO_PROPERTY_VERSION)) { m_Version = 0; - for (auto ch: value) + char * ch = value; + while (*ch) { - if (ch >= '0' && ch <= '9') + if (*ch >= '0' && *ch <= '9') { m_Version *= 10; - m_Version += (ch - '0'); + m_Version += (*ch - '0'); } + ch++; } - if (m_Version < NETDB_MIN_PEER_TEST_VERSION && (m_SupportedTransports & (eSSU2V4 | eSSU2V6))) - { - auto addresses = GetAddresses (); - if (addresses) - { - if ((*addresses)[eSSU2V4Idx]) (*addresses)[eSSU2V4Idx]->caps &= ~eSSUTesting; - if ((*addresses)[eSSU2V6Idx]) (*addresses)[eSSU2V6Idx]->caps &= ~eSSUTesting; - } - } } // check netId - else if (key == ROUTER_INFO_PROPERTY_NETID) + else if (!strcmp (key, ROUTER_INFO_PROPERTY_NETID)) { isNetId = true; - int netID; - auto res = std::from_chars(value.data(), value.data() + value.size(), netID); - if (res.ec != std::errc() || netID != i2p::context.GetNetID ()) + if (atoi (value) != i2p::context.GetNetID ()) { LogPrint (eLogError, "RouterInfo: Unexpected ", ROUTER_INFO_PROPERTY_NETID, "=", value); m_IsUnreachable = true; } } // family - else if (key == ROUTER_INFO_PROPERTY_FAMILY) + else if (!strcmp (key, ROUTER_INFO_PROPERTY_FAMILY)) { family = value; boost::to_lower (family); } - else if (key == ROUTER_INFO_PROPERTY_FAMILY_SIG) + else if (!strcmp (key, ROUTER_INFO_PROPERTY_FAMILY_SIG)) { - if (netdb.GetFamilies ().VerifyFamily (family, GetIdentHash (), value)) // TODO + if (netdb.GetFamilies ().VerifyFamily (family, GetIdentHash (), value)) m_FamilyID = netdb.GetFamilies ().GetFamilyID (family); else { @@ -490,24 +512,25 @@ namespace data SetUnreachable (true); } } + + if (!s) return; } if (!m_SupportedTransports || !isNetId || !m_Version) SetUnreachable (true); - - return true; - } - + } + bool RouterInfo::IsFamily (FamilyID famid) const { return m_FamilyID == famid; } - void RouterInfo::ExtractCaps (std::string_view value) + void RouterInfo::ExtractCaps (const char * value) { - for (auto cap: value) + const char * cap = value; + while (*cap) { - switch (cap) + switch (*cap) { case CAPS_FLAG_FLOODFILL: m_Caps |= Caps::eFloodfill; @@ -516,16 +539,16 @@ namespace data case CAPS_FLAG_LOW_BANDWIDTH2: case CAPS_FLAG_LOW_BANDWIDTH3: case CAPS_FLAG_LOW_BANDWIDTH4: - m_BandwidthCap = cap; + m_BandwidthCap = *cap; break; case CAPS_FLAG_HIGH_BANDWIDTH: m_Caps |= Caps::eHighBandwidth; - m_BandwidthCap = cap; + m_BandwidthCap = *cap; break; case CAPS_FLAG_EXTRA_BANDWIDTH1: case CAPS_FLAG_EXTRA_BANDWIDTH2: m_Caps |= Caps::eExtraBandwidth | Caps::eHighBandwidth; - m_BandwidthCap = cap; + m_BandwidthCap = *cap; break; case CAPS_FLAG_HIDDEN: m_Caps |= Caps::eHidden; @@ -547,15 +570,17 @@ namespace data break; default: ; } + cap++; } - } - - uint8_t RouterInfo::ExtractAddressCaps (std::string_view value) const + } + + uint8_t RouterInfo::ExtractAddressCaps (const char * value) const { uint8_t caps = 0; - for (auto cap: value) + const char * cap = value; + while (*cap) { - switch (cap) + switch (*cap) { case CAPS_FLAG_V4: caps |= AddressCaps::eV4; @@ -571,10 +596,11 @@ namespace data break; default: ; } + cap++; } return caps; - } - + } + void RouterInfo::UpdateIntroducers (std::shared_ptr
address, uint64_t ts) { if (!address || !address->ssu) return; @@ -634,41 +660,25 @@ namespace data return SaveToFile (fullPath, m_Buffer); } - std::string_view RouterInfo::ExtractString (const uint8_t * buf, size_t len) const + size_t RouterInfo::ReadString (char * str, size_t len, std::istream& s) const { - uint8_t l = buf[0]; - if (l > len) + uint8_t l; + s.read ((char *)&l, 1); + if (l < len) + { + s.read (str, l); + if (!s) l = 0; // failed, return empty string + str[l] = 0; + } + else { LogPrint (eLogWarning, "RouterInfo: String length ", (int)l, " exceeds buffer size ", len); - l = len; - } - return { (const char *)(buf + 1), l }; + s.seekg (l, std::ios::cur); // skip + str[0] = 0; + } + return l+1; } - std::tuple RouterInfo::ExtractParam (const uint8_t * buf, size_t len) const - { - auto key = ExtractString (buf, len); - size_t offset = key.length () + 1; - if (offset >= len) return { std::string_view(), std::string_view(), len }; - if (buf[offset] != '=') - { - LogPrint (eLogWarning, "RouterInfo: Unexpected character ", buf[offset], " instead '=' after ", key); - key = std::string_view(); - } - offset++; - if (offset >= len) return { key, std::string_view(), len }; - auto value = ExtractString (buf + offset, len - offset); - offset += value.length () + 1; - if (offset >= len) return { key, std::string_view(), len }; - if (buf[offset] != ';') - { - LogPrint (eLogWarning, "RouterInfo: Unexpected character ", buf[offset], " instead ';' after ", value); - value = std::string_view(); - } - offset++; - return { key, value, offset }; - } - void RouterInfo::AddNTCP2Address (const uint8_t * staticKey, const uint8_t * iv,int port, uint8_t caps) { auto addr = std::make_shared
(); @@ -1125,7 +1135,6 @@ namespace data void RouterInfo::UpdateBuffer (const uint8_t * buf, size_t len) { - m_IsBufferScheduledToDelete = false; if (!m_Buffer) m_Buffer = NewBuffer (); if (len > m_Buffer->size ()) len = m_Buffer->size (); @@ -1184,19 +1193,6 @@ namespace data return false; } } - - std::string RouterInfo::GetTransportName (SupportedTransports tr) - { - switch (tr) - { - case eNTCP2V4: return "NTCP2V4"; - case eNTCP2V6: return "NTCP2V6"; - case eSSU2V4: return "SSU2V4"; - case eSSU2V6: return "SSU2V6"; - case eNTCP2V6Mesh: return "Mesh"; - default: return ""; - } - } void LocalRouterInfo::CreateBuffer (const PrivateKeys& privateKeys) { @@ -1382,9 +1378,9 @@ namespace data if (!introducer.iTag) continue; if (introducer.iExp) // expiration is specified { - WriteString ("iexp" + std::to_string(i), properties); + WriteString ("iexp" + boost::lexical_cast(i), properties); properties << '='; - WriteString (std::to_string(introducer.iExp), properties); + WriteString (boost::lexical_cast(introducer.iExp), properties); properties << ';'; } i++; @@ -1393,9 +1389,11 @@ namespace data for (const auto& introducer: address.ssu->introducers) { if (!introducer.iTag) continue; - WriteString ("ih" + std::to_string(i), properties); + WriteString ("ih" + boost::lexical_cast(i), properties); properties << '='; - auto value = ByteStreamToBase64 (introducer.iH, 32); + char value[64]; + size_t l = ByteStreamToBase64 (introducer.iH, 32, value, 64); + value[l] = 0; WriteString (value, properties); properties << ';'; i++; @@ -1404,9 +1402,9 @@ namespace data for (const auto& introducer: address.ssu->introducers) { if (!introducer.iTag) continue; - WriteString ("itag" + std::to_string(i), properties); + WriteString ("itag" + boost::lexical_cast(i), properties); properties << '='; - WriteString (std::to_string(introducer.iTag), properties); + WriteString (boost::lexical_cast(introducer.iTag), properties); properties << ';'; i++; } @@ -1420,7 +1418,7 @@ namespace data { WriteString ("mtu", properties); properties << '='; - WriteString (std::to_string(address.ssu->mtu), properties); + WriteString (boost::lexical_cast(address.ssu->mtu), properties); properties << ';'; } } @@ -1428,7 +1426,7 @@ namespace data { WriteString ("port", properties); properties << '='; - WriteString (std::to_string(address.port), properties); + WriteString (boost::lexical_cast(address.port), properties); properties << ';'; } if (address.IsNTCP2 () || address.IsSSU2 ()) @@ -1463,11 +1461,9 @@ namespace data s.write (properties.str ().c_str (), properties.str ().size ()); } - void LocalRouterInfo::SetProperty (std::string_view key, std::string_view value) + void LocalRouterInfo::SetProperty (const std::string& key, const std::string& value) { - auto [it, inserted] = m_Properties.emplace (key, value); - if (!inserted) - it->second = value; + m_Properties[key] = value; } void LocalRouterInfo::DeleteProperty (const std::string& key) diff --git a/libi2pd/RouterInfo.h b/libi2pd/RouterInfo.h index cb3ae499..72521797 100644 --- a/libi2pd/RouterInfo.h +++ b/libi2pd/RouterInfo.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2025, The PurpleI2P Project +* Copyright (c) 2013-2024, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -11,8 +11,6 @@ #include #include -#include -#include #include #include #include @@ -210,8 +208,8 @@ namespace data typedef boost::shared_ptr AddressesPtr; #endif RouterInfo (const std::string& fullPath); - RouterInfo (const RouterInfo& ) = delete; - RouterInfo& operator=(const RouterInfo& ) = delete; + RouterInfo (const RouterInfo& ) = default; + RouterInfo& operator=(const RouterInfo& ) = default; RouterInfo (std::shared_ptr&& buf, size_t len); RouterInfo (const uint8_t * buf, size_t len); virtual ~RouterInfo (); @@ -221,7 +219,7 @@ namespace data std::string GetIdentHashBase64 () const { return GetIdentHash ().ToBase64 (); }; uint64_t GetTimestamp () const { return m_Timestamp; }; int GetVersion () const { return m_Version; }; - virtual void SetProperty (std::string_view key, std::string_view value) {}; + virtual void SetProperty (const std::string& key, const std::string& value) {}; virtual void ClearProperties () {}; AddressesPtr GetAddresses () const; // should be called for local RI only, otherwise must return shared_ptr std::shared_ptr GetNTCP2V4Address () const; @@ -292,12 +290,9 @@ namespace data const uint8_t * GetBuffer () const { return m_Buffer ? m_Buffer->data () : nullptr; }; const uint8_t * LoadBuffer (const std::string& fullPath); // load if necessary size_t GetBufferLen () const { return m_Buffer ? m_Buffer->GetBufferLen () : 0; }; - void DeleteBuffer () { m_Buffer = nullptr; m_IsBufferScheduledToDelete = false; }; + void DeleteBuffer () { m_Buffer = nullptr; }; std::shared_ptr GetSharedBuffer () const { return m_Buffer; }; std::shared_ptr CopyBuffer () const; - void ScheduleBufferToDelete () { m_IsBufferScheduledToDelete = true; }; - void CancelBufferToDelete () { m_IsBufferScheduledToDelete = false; }; - bool IsBufferScheduledToDelete () const { return m_IsBufferScheduledToDelete; }; bool IsUpdated () const { return m_IsUpdated; }; void SetUpdated (bool updated) { m_IsUpdated = updated; }; @@ -335,12 +330,11 @@ namespace data bool LoadFile (const std::string& fullPath); void ReadFromFile (const std::string& fullPath); - bool ReadFromBuffer (const uint8_t * buf, size_t len); // return false if malformed + void ReadFromStream (std::istream& s); void ReadFromBuffer (bool verifySignature); - std::string_view ExtractString (const uint8_t * buf, size_t len) const; - std::tuple ExtractParam (const uint8_t * buf, size_t len) const; - void ExtractCaps (std::string_view value); - uint8_t ExtractAddressCaps (std::string_view value) const; + 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; @@ -360,17 +354,13 @@ namespace data #else AddressesPtr m_Addresses; #endif - bool m_IsUpdated, m_IsUnreachable, m_IsFloodfill, m_IsBufferScheduledToDelete; + bool m_IsUpdated, m_IsUnreachable, m_IsFloodfill; CompatibleTransports m_SupportedTransports, m_ReachableTransports, m_PublishedTransports; uint8_t m_Caps; char m_BandwidthCap; int m_Version; Congestion m_Congestion; mutable std::shared_ptr m_Profile; - - public: - - static std::string GetTransportName (SupportedTransports tr); }; class LocalRouterInfo: public RouterInfo @@ -382,7 +372,7 @@ namespace data void UpdateCaps (uint8_t caps); bool UpdateCongestion (Congestion c); // returns true if updated - void SetProperty (std::string_view key, std::string_view value) override; + void SetProperty (const std::string& key, const std::string& value) override; void DeleteProperty (const std::string& key); std::string GetProperty (const std::string& key) const; void ClearProperties () override { m_Properties.clear (); }; diff --git a/libi2pd/SSU2.cpp b/libi2pd/SSU2.cpp index fc2355a5..1a1965e1 100644 --- a/libi2pd/SSU2.cpp +++ b/libi2pd/SSU2.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2022-2025, The PurpleI2P Project +* Copyright (c) 2022-2024, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -81,7 +81,7 @@ namespace transport found = true; LogPrint (eLogDebug, "SSU2: Opening IPv4 socket at Start"); OpenSocket (boost::asio::ip::udp::endpoint (m_AddressV4, port)); - boost::asio::post (m_ReceiveService.GetService (), + m_ReceiveService.GetService ().post( [this]() { Receive (m_SocketV4); @@ -93,8 +93,8 @@ namespace transport found = true; LogPrint (eLogDebug, "SSU2: Opening IPv6 socket at Start"); OpenSocket (boost::asio::ip::udp::endpoint (m_AddressV6, port)); - boost::asio::post (m_ReceiveService.GetService (), - [this]() + m_ReceiveService.GetService ().post( + [this]() { Receive (m_SocketV6); }); @@ -157,9 +157,6 @@ namespace transport m_IntroducersV6.clear (); m_ConnectedRecently.clear (); m_RequestedPeerTests.clear (); - - m_PacketsPool.ReleaseMt (m_ReceivedPacketsQueue); - m_ReceivedPacketsQueue.clear (); } void SSU2Server::SetLocalAddress (const boost::asio::ip::address& localAddress) @@ -216,16 +213,15 @@ namespace transport return ep.port (); } - bool SSU2Server::IsConnectedRecently (const boost::asio::ip::udp::endpoint& ep, bool max) + bool SSU2Server::IsConnectedRecently (const boost::asio::ip::udp::endpoint& ep) { if (!ep.port () || ep.address ().is_unspecified ()) return false; - std::lock_guard l(m_ConnectedRecentlyMutex); auto it = m_ConnectedRecently.find (ep); if (it != m_ConnectedRecently.end ()) { - if (i2p::util::GetSecondsSinceEpoch () <= it->second + (max ? SSU2_MAX_HOLE_PUNCH_EXPIRATION : SSU2_MIN_HOLE_PUNCH_EXPIRATION)) + if (i2p::util::GetSecondsSinceEpoch () <= it->second + SSU2_HOLE_PUNCH_EXPIRATION) return true; - else if (max) + else m_ConnectedRecently.erase (it); } return false; @@ -234,8 +230,7 @@ namespace transport void SSU2Server::AddConnectedRecently (const boost::asio::ip::udp::endpoint& ep, uint64_t ts) { if (!ep.port () || ep.address ().is_unspecified () || - i2p::util::GetSecondsSinceEpoch () > ts + SSU2_MAX_HOLE_PUNCH_EXPIRATION) return; - std::lock_guard l(m_ConnectedRecentlyMutex); + i2p::util::GetSecondsSinceEpoch () > ts + SSU2_HOLE_PUNCH_EXPIRATION) return; auto [it, added] = m_ConnectedRecently.try_emplace (ep, ts); if (!added && ts > it->second) it->second = ts; // renew timestamp of existing endpoint @@ -369,22 +364,28 @@ namespace transport return; } packet->len = bytes_transferred; - + boost::system::error_code ec; size_t moreBytes = socket.available (ec); if (!ec && moreBytes) { - std::list packets; - packets.push_back (packet); - while (moreBytes && packets.size () < SSU2_MAX_NUM_PACKETS_PER_BATCH) - { + auto packets = m_PacketsArrayPool.AcquireMt (); + packets->AddPacket (packet); + while (moreBytes && packets->numPackets < SSU2_MAX_NUM_PACKETS_PER_BATCH) + { packet = m_PacketsPool.AcquireMt (); packet->len = socket.receive_from (boost::asio::buffer (packet->buf, SSU2_MAX_PACKET_SIZE), packet->from, 0, ec); if (!ec) { i2p::transport::transports.UpdateReceivedBytes (packet->len); if (packet->len >= SSU2_MIN_RECEIVED_PACKET_SIZE) - packets.push_back (packet); + { + if (!packets->AddPacket (packet)) + { + LogPrint (eLogError, "SSU2: Received packets array is full"); + m_PacketsPool.ReleaseMt (packet); + } + } else // drop too short packets m_PacketsPool.ReleaseMt (packet); moreBytes = socket.available(ec); @@ -397,10 +398,10 @@ namespace transport break; } } - InsertToReceivedPacketsQueue (packets); + GetService ().post (std::bind (&SSU2Server::HandleReceivedPackets, this, packets)); } else - InsertToReceivedPacketsQueue (packet); + GetService ().post (std::bind (&SSU2Server::HandleReceivedPacket, this, packet)); Receive (socket); } else @@ -427,75 +428,49 @@ namespace transport } } - void SSU2Server::HandleReceivedPackets (std::list&& packets) + void SSU2Server::HandleReceivedPacket (Packet * packet) { - if (packets.empty ()) return; + if (packet) + { + if (m_IsThroughProxy) + ProcessNextPacketFromProxy (packet->buf, packet->len); + else + ProcessNextPacket (packet->buf, packet->len, packet->from); + m_PacketsPool.ReleaseMt (packet); + if (m_LastSession && m_LastSession->GetState () != eSSU2SessionStateTerminated) + m_LastSession->FlushData (); + } + } + + void SSU2Server::HandleReceivedPackets (Packets * packets) + { + if (!packets) return; if (m_IsThroughProxy) - for (auto it: packets) - ProcessNextPacketFromProxy (it->buf, it->len); + for (size_t i = 0; i < packets->numPackets; i++) + { + auto& packet = (*packets)[i]; + ProcessNextPacketFromProxy (packet->buf, packet->len); + } else - for (auto it: packets) - ProcessNextPacket (it->buf, it->len, it->from); - m_PacketsPool.ReleaseMt (packets); + for (size_t i = 0; i < packets->numPackets; i++) + { + auto& packet = (*packets)[i]; + ProcessNextPacket (packet->buf, packet->len, packet->from); + } + m_PacketsPool.ReleaseMt (packets->data (), packets->numPackets); + m_PacketsArrayPool.ReleaseMt (packets); if (m_LastSession && m_LastSession->GetState () != eSSU2SessionStateTerminated) m_LastSession->FlushData (); } - void SSU2Server::InsertToReceivedPacketsQueue (Packet * packet) - { - if (!packet) return; - bool empty = false; - { - std::lock_guard l(m_ReceivedPacketsQueueMutex); - empty = m_ReceivedPacketsQueue.empty (); - m_ReceivedPacketsQueue.push_back (packet); - } - if (empty) - boost::asio::post (GetService (), [this]() { HandleReceivedPacketsQueue (); }); - } - - void SSU2Server::InsertToReceivedPacketsQueue (std::list& packets) - { - if (packets.empty ()) return; - size_t queueSize = 0; - { - std::lock_guard l(m_ReceivedPacketsQueueMutex); - queueSize = m_ReceivedPacketsQueue.size (); - if (queueSize < SSU2_MAX_RECEIVED_QUEUE_SIZE) - m_ReceivedPacketsQueue.splice (m_ReceivedPacketsQueue.end (), packets); - else - { - LogPrint (eLogError, "SSU2: Received queue size ", queueSize, " exceeds max size", SSU2_MAX_RECEIVED_QUEUE_SIZE); - m_PacketsPool.ReleaseMt (packets); - queueSize = 0; // invoke processing just in case - } - } - if (!queueSize) - boost::asio::post (GetService (), [this]() { HandleReceivedPacketsQueue (); }); - } - - void SSU2Server::HandleReceivedPacketsQueue () - { - std::list receivedPackets; - { - std::lock_guard l(m_ReceivedPacketsQueueMutex); - m_ReceivedPacketsQueue.swap (receivedPackets); - } - HandleReceivedPackets (std::move (receivedPackets)); - } - - bool SSU2Server::AddSession (std::shared_ptr session) + void SSU2Server::AddSession (std::shared_ptr session) { if (session) { - if (m_Sessions.emplace (session->GetConnID (), session).second) - { - if (session->GetState () != eSSU2SessionStatePeerTest) - AddSessionByRouterHash (session); - return true; - } + m_Sessions.emplace (session->GetConnID (), session); + if (session->GetState () != eSSU2SessionStatePeerTest) + AddSessionByRouterHash (session); } - return false; } void SSU2Server::RemoveSession (uint64_t connID) @@ -522,7 +497,7 @@ namespace transport void SSU2Server::RequestRemoveSession (uint64_t connID) { - boost::asio::post (GetService (), [connID, this]() { RemoveSession (connID); }); + GetService ().post ([connID, this]() { RemoveSession (connID); }); } void SSU2Server::AddSessionByRouterHash (std::shared_ptr session) @@ -550,7 +525,7 @@ namespace transport // move unsent msgs to new session oldSession->MoveSendQueue (session); // terminate existing - boost::asio::post (GetService (), std::bind (&SSU2Session::RequestTermination, oldSession, eSSU2TerminationReasonReplacedByNewSession)); + GetService ().post (std::bind (&SSU2Session::RequestTermination, oldSession, eSSU2TerminationReasonReplacedByNewSession)); } } } @@ -728,9 +703,6 @@ namespace transport m_LastSession->SetRemoteEndpoint (senderEndpoint); m_LastSession->ProcessPeerTest (buf, len); break; - case eSSU2SessionStateHolePunch: - m_LastSession->ProcessFirstIncomingMessage (connID, buf, len); // SessionRequest - break; case eSSU2SessionStateClosing: m_LastSession->ProcessData (buf, len, senderEndpoint); // we might receive termintaion block if (m_LastSession && m_LastSession->GetState () == eSSU2SessionStateClosing) @@ -844,29 +816,6 @@ namespace transport } } - bool SSU2Server::CheckPendingOutgoingSession (const boost::asio::ip::udp::endpoint& ep, bool peerTest) - { - auto s = FindPendingOutgoingSession (ep); - if (s) - { - if (peerTest) - { - // if peer test requested add it to the list for pending session - auto onEstablished = s->GetOnEstablished (); - if (onEstablished) - s->SetOnEstablished ([s, onEstablished]() - { - onEstablished (); - s->SendPeerTest (); - }); - else - s->SetOnEstablished ([s]() { s->SendPeerTest (); }); - } - return true; - } - return false; - } - bool SSU2Server::CreateSession (std::shared_ptr router, std::shared_ptr address, bool peerTest) { @@ -878,7 +827,7 @@ namespace transport { // session with router found, trying to send peer test if requested if (peerTest && existingSession->IsEstablished ()) - boost::asio::post (GetService (), [existingSession]() { existingSession->SendPeerTest (); }); + GetService ().post ([existingSession]() { existingSession->SendPeerTest (); }); return false; } // check is no pending session @@ -886,28 +835,34 @@ namespace transport if (isValidEndpoint) { if (i2p::transport::transports.IsInReservedRange(address->host)) return false; - if (CheckPendingOutgoingSession (boost::asio::ip::udp::endpoint (address->host, address->port), peerTest)) return false; + auto s = FindPendingOutgoingSession (boost::asio::ip::udp::endpoint (address->host, address->port)); + if (s) + { + if (peerTest) + { + // if peer test requested add it to the list for pending session + auto onEstablished = s->GetOnEstablished (); + if (onEstablished) + s->SetOnEstablished ([s, onEstablished]() + { + onEstablished (); + s->SendPeerTest (); + }); + else + s->SetOnEstablished ([s]() { s->SendPeerTest (); }); + } + return false; + } } auto session = std::make_shared (*this, router, address); - if (!isValidEndpoint && router->HasProfile () && router->GetProfile ()->HasLastEndpoint (address->IsV4 ())) - { - // router doesn't publish endpoint, but we connected before and hole punch might be alive - auto ep = router->GetProfile ()->GetLastEndpoint (); - if (IsConnectedRecently (ep, false)) - { - if (CheckPendingOutgoingSession (ep, peerTest)) return false; - session->SetRemoteEndpoint (ep); - isValidEndpoint = true; - } - } if (peerTest) session->SetOnEstablished ([session]() {session->SendPeerTest (); }); - if (isValidEndpoint) // we know endpoint - boost::asio::post (GetService (), [session]() { session->Connect (); }); - else if (address->UsesIntroducer ()) // we don't know endpoint yet - boost::asio::post (GetService (), std::bind (&SSU2Server::ConnectThroughIntroducer, this, session)); + if (address->UsesIntroducer ()) + GetService ().post (std::bind (&SSU2Server::ConnectThroughIntroducer, this, session)); + else if (isValidEndpoint) // we can't connect without endpoint + GetService ().post ([session]() { session->Connect (); }); else return false; } @@ -1050,7 +1005,7 @@ namespace transport if (!remoteAddr || !remoteAddr->IsPeerTesting () || (v4 && !remoteAddr->IsV4 ()) || (!v4 && !remoteAddr->IsV6 ())) return false; if (session->IsEstablished ()) - boost::asio::post (GetService (), [session]() { session->SendPeerTest (); }); + GetService ().post ([session]() { session->SendPeerTest (); }); else session->SetOnEstablished ([session]() { session->SendPeerTest (); }); return true; @@ -1157,7 +1112,7 @@ namespace transport for (auto it = m_ConnectedRecently.begin (); it != m_ConnectedRecently.end (); ) { - if (ts > it->second + SSU2_MAX_HOLE_PUNCH_EXPIRATION) + if (ts > it->second + SSU2_HOLE_PUNCH_EXPIRATION) it = m_ConnectedRecently.erase (it); else it++; @@ -1183,6 +1138,7 @@ namespace transport } m_PacketsPool.CleanUpMt (); + m_PacketsArrayPool.CleanUpMt (); m_SentPacketsPool.CleanUp (); m_IncompleteMessagesPool.CleanUp (); m_FragmentsPool.CleanUp (); @@ -1251,21 +1207,18 @@ namespace transport } uint64_t token; RAND_bytes ((uint8_t *)&token, 8); - if (!token) token = 1; // token can't be zero - m_IncomingTokens.try_emplace (ep, token, uint32_t(ts + SSU2_TOKEN_EXPIRATION_TIMEOUT)); + m_IncomingTokens.emplace (ep, std::make_pair (token, uint32_t(ts + SSU2_TOKEN_EXPIRATION_TIMEOUT))); return token; } std::pair SSU2Server::NewIncomingToken (const boost::asio::ip::udp::endpoint& ep) { + m_IncomingTokens.erase (ep); // drop previous uint64_t token; RAND_bytes ((uint8_t *)&token, 8); - if (!token) token = 1; // token can't be zero - uint32_t expires = i2p::util::GetSecondsSinceEpoch () + SSU2_NEXT_TOKEN_EXPIRATION_TIMEOUT; - auto [it, inserted] = m_IncomingTokens.try_emplace (ep, token, expires); - if (!inserted) - it->second = { token, expires }; // override - return it->second; + auto ret = std::make_pair (token, uint32_t(i2p::util::GetSecondsSinceEpoch () + SSU2_NEXT_TOKEN_EXPIRATION_TIMEOUT)); + m_IncomingTokens.emplace (ep, ret); + return ret; } std::vector > SSU2Server::FindIntroducers (int maxNumIntroducers, @@ -1286,11 +1239,8 @@ namespace transport (!v4 && (s.second->GetRemoteTransports () & i2p::data::RouterInfo::eSSU2V6)))) eligible.push_back (s.second); } - - if (eligible.size () <= (size_t)maxNumIntroducers) - return eligible; - else - std::sample (eligible.begin(), eligible.end(), std::back_inserter(ret), maxNumIntroducers, m_Rng); + + std::sample (eligible.begin(), eligible.end(), std::back_inserter(ret), maxNumIntroducers, m_Rng); return ret; } @@ -1392,7 +1342,7 @@ namespace transport excluded.insert (ident); } - // session about to expire are not counted + // sesssion about to expire are not counted for (auto i = introducers.size (); i < SSU2_MAX_NUM_INTRODUCERS + numOldSessions; i++) { auto introducer = i2p::data::netdb.GetRandomSSU2Introducer (v4, excluded); @@ -1519,23 +1469,6 @@ namespace transport } } - bool SSU2Server::AEADChaCha20Poly1305Encrypt (const uint8_t * msg, size_t msgLen, - const uint8_t * ad, size_t adLen, const uint8_t * key, const uint8_t * nonce, uint8_t * buf, size_t len) - { - return m_Encryptor.Encrypt (msg, msgLen, ad, adLen, key, nonce, buf, len); - } - - bool SSU2Server::AEADChaCha20Poly1305Decrypt (const uint8_t * msg, size_t msgLen, - const uint8_t * ad, size_t adLen, const uint8_t * key, const uint8_t * nonce, uint8_t * buf, size_t len) - { - return m_Decryptor.Decrypt (msg, msgLen, ad, adLen, key, nonce, buf, len); - } - - void SSU2Server::ChaCha20 (const uint8_t * msg, size_t msgLen, const uint8_t * key, const uint8_t * nonce, uint8_t * out) - { - m_ChaCha20 (msg, msgLen, key, nonce, out); - } - void SSU2Server::SendThroughProxy (const uint8_t * header, size_t headerLen, const uint8_t * headerX, size_t headerXLen, const uint8_t * payload, size_t payloadLen, const boost::asio::ip::udp::endpoint& to) { @@ -1784,7 +1717,7 @@ namespace transport bool SSU2Server::SetProxy (const std::string& address, uint16_t port) { boost::system::error_code ecode; - auto addr = boost::asio::ip::make_address (address, ecode); + auto addr = boost::asio::ip::address::from_string (address, ecode); if (!ecode && !addr.is_unspecified () && port) { m_IsThroughProxy = true; diff --git a/libi2pd/SSU2.h b/libi2pd/SSU2.h index a8598ce3..2b97bd25 100644 --- a/libi2pd/SSU2.h +++ b/libi2pd/SSU2.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2022-2025, The PurpleI2P Project +* Copyright (c) 2022-2024, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -12,13 +12,11 @@ #include #include #include -#include #include #include #include #include "util.h" #include "SSU2Session.h" -#include "SSU2OutOfSession.h" #include "Socks5.h" namespace i2p @@ -37,15 +35,13 @@ namespace transport const uint64_t SSU2_SOCKET_MAX_BUFFER_SIZE = 4 * 1024 * 1024; const size_t SSU2_MAX_NUM_INTRODUCERS = 3; const size_t SSU2_MIN_RECEIVED_PACKET_SIZE = 40; // 16 byte short header + 8 byte minimum payload + 16 byte MAC - const size_t SSU2_MAX_RECEIVED_QUEUE_SIZE = 2500; // in packets const int SSU2_TO_INTRODUCER_SESSION_DURATION = 3600; // 1 hour const int SSU2_TO_INTRODUCER_SESSION_EXPIRATION = 4800; // 80 minutes const int SSU2_KEEP_ALIVE_INTERVAL = 15; // in seconds const int SSU2_KEEP_ALIVE_INTERVAL_VARIANCE = 4; // in seconds const int SSU2_PROXY_CONNECT_RETRY_TIMEOUT = 30; // in seconds - const int SSU2_MIN_HOLE_PUNCH_EXPIRATION = 30; // in seconds - const int SSU2_MAX_HOLE_PUNCH_EXPIRATION = 160; // in seconds - const size_t SSU2_MAX_NUM_PACKETS_PER_BATCH = 64; + const int SSU2_HOLE_PUNCH_EXPIRATION = 150; // in seconds + const size_t SSU2_MAX_NUM_PACKETS_PER_BATCH = 32; class SSU2Server: private i2p::util::RunnableServiceWithWork { @@ -55,13 +51,27 @@ namespace transport size_t len; boost::asio::ip::udp::endpoint from; }; + + struct Packets: public std::array + { + size_t numPackets = 0; + bool AddPacket (Packet *p) + { + if (p && numPackets < size ()) + { + data()[numPackets] = p; numPackets++; + return true; + } + return false; + } + }; class ReceiveService: public i2p::util::RunnableService { public: ReceiveService (const std::string& name): RunnableService (name) {}; - auto& GetService () { return GetIOService (); }; + boost::asio::io_service& GetService () { return GetIOService (); }; void Start () { StartIOService (); }; void Stop () { StopIOService (); }; }; @@ -73,25 +83,20 @@ namespace transport void Start (); void Stop (); - auto& GetService () { return GetIOService (); }; + boost::asio::io_service& GetService () { return GetIOService (); }; void SetLocalAddress (const boost::asio::ip::address& localAddress); bool SetProxy (const std::string& address, uint16_t port); bool UsesProxy () const { return m_IsThroughProxy; }; bool IsSupported (const boost::asio::ip::address& addr) const; uint16_t GetPort (bool v4) const; - bool IsConnectedRecently (const boost::asio::ip::udp::endpoint& ep, bool max = true); + bool IsConnectedRecently (const boost::asio::ip::udp::endpoint& ep); void AddConnectedRecently (const boost::asio::ip::udp::endpoint& ep, uint64_t ts); std::mt19937& GetRng () { return m_Rng; } - bool AEADChaCha20Poly1305Encrypt (const uint8_t * msg, size_t msgLen, const uint8_t * ad, size_t adLen, - const uint8_t * key, const uint8_t * nonce, uint8_t * buf, size_t len); - bool AEADChaCha20Poly1305Decrypt (const uint8_t * msg, size_t msgLen, const uint8_t * ad, size_t adLen, - const uint8_t * key, const uint8_t * nonce, uint8_t * buf, size_t len); - void ChaCha20 (const uint8_t * msg, size_t msgLen, const uint8_t * key, const uint8_t * nonce, uint8_t * out); bool IsMaxNumIntroducers (bool v4) const { return (v4 ? m_Introducers.size () : m_IntroducersV6.size ()) >= SSU2_MAX_NUM_INTRODUCERS; } bool IsSyncClockFromPeers () const { return m_IsSyncClockFromPeers; }; void AdjustTimeOffset (int64_t offset, std::shared_ptr from); - bool AddSession (std::shared_ptr session); + void AddSession (std::shared_ptr session); void RemoveSession (uint64_t connID); void RequestRemoveSession (uint64_t connID); void AddSessionByRouterHash (std::shared_ptr session); @@ -139,12 +144,10 @@ namespace transport void Receive (boost::asio::ip::udp::socket& socket); void HandleReceivedFrom (const boost::system::error_code& ecode, size_t bytes_transferred, Packet * packet, boost::asio::ip::udp::socket& socket); - void HandleReceivedPackets (std::list&& packets); + void HandleReceivedPacket (Packet * packet); + void HandleReceivedPackets (Packets * packets); void ProcessNextPacket (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint); - void InsertToReceivedPacketsQueue (Packet * packet); - void InsertToReceivedPacketsQueue (std::list& packets); - void HandleReceivedPacketsQueue (); - + void ScheduleTermination (); void HandleTerminationTimer (const boost::system::error_code& ecode); @@ -154,7 +157,6 @@ namespace transport void ScheduleResend (bool more); void HandleResendTimer (const boost::system::error_code& ecode); - bool CheckPendingOutgoingSession (const boost::asio::ip::udp::endpoint& ep, bool peerTest); void ConnectThroughIntroducer (std::shared_ptr session); std::vector > FindIntroducers (int maxNumIntroducers, bool v4, const std::unordered_set& excluded); @@ -189,6 +191,7 @@ namespace transport std::unordered_map, uint64_t > > m_PeerTests; // nonce->(Alice, timestamp). We are Bob std::list > m_Introducers, m_IntroducersV6; // introducers we are connected to i2p::util::MemoryPoolMt m_PacketsPool; + i2p::util::MemoryPoolMt m_PacketsArrayPool; i2p::util::MemoryPool m_SentPacketsPool; i2p::util::MemoryPool m_IncompleteMessagesPool; i2p::util::MemoryPool m_FragmentsPool; @@ -201,13 +204,7 @@ namespace transport std::shared_ptr m_PendingTimeOffsetFrom; std::mt19937 m_Rng; std::map m_ConnectedRecently; // endpoint -> last activity time in seconds - mutable std::mutex m_ConnectedRecentlyMutex; std::unordered_map, uint64_t > > m_RequestedPeerTests; // nonce->(Alice, timestamp) - std::list m_ReceivedPacketsQueue; - mutable std::mutex m_ReceivedPacketsQueueMutex; - i2p::crypto::AEADChaCha20Poly1305Encryptor m_Encryptor; - i2p::crypto::AEADChaCha20Poly1305Decryptor m_Decryptor; - i2p::crypto::ChaCha20Context m_ChaCha20; // proxy bool m_IsThroughProxy; diff --git a/libi2pd/SSU2OutOfSession.cpp b/libi2pd/SSU2OutOfSession.cpp deleted file mode 100644 index 3760e329..00000000 --- a/libi2pd/SSU2OutOfSession.cpp +++ /dev/null @@ -1,345 +0,0 @@ -/* -* Copyright (c) 2024-2025, The PurpleI2P Project -* -* This file is part of Purple i2pd project and licensed under BSD3 -* -* See full license text in LICENSE file at top of project tree -*/ - -#include "Log.h" -#include "SSU2.h" -#include "SSU2OutOfSession.h" - -namespace i2p -{ -namespace transport -{ - SSU2PeerTestSession::SSU2PeerTestSession (SSU2Server& server, uint64_t sourceConnID, uint64_t destConnID): - SSU2Session (server, nullptr, nullptr, false), - m_MsgNumReceived (0), m_NumResends (0),m_IsConnectedRecently (false), m_IsStatusChanged (false), - m_PeerTestResendTimer (server.GetService ()) - { - if (!sourceConnID) sourceConnID = ~destConnID; - if (!destConnID) destConnID = ~sourceConnID; - SetSourceConnID (sourceConnID); - SetDestConnID (destConnID); - SetState (eSSU2SessionStatePeerTest); - SetTerminationTimeout (SSU2_PEER_TEST_EXPIRATION_TIMEOUT); - } - - bool SSU2PeerTestSession::ProcessPeerTest (uint8_t * buf, size_t len) - { - // we are Alice or Charlie, msgs 5,6,7 - Header header; - memcpy (header.buf, buf, 16); - header.ll[0] ^= CreateHeaderMask (i2p::context.GetSSU2IntroKey (), buf + (len - 24)); - header.ll[1] ^= CreateHeaderMask (i2p::context.GetSSU2IntroKey (), buf + (len - 12)); - if (header.h.type != eSSU2PeerTest) - { - LogPrint (eLogWarning, "SSU2: Unexpected message type ", (int)header.h.type, " instead ", (int)eSSU2PeerTest); - return false; - } - if (len < 48) - { - LogPrint (eLogWarning, "SSU2: PeerTest message too short ", len); - return false; - } - uint8_t nonce[12] = {0}; - uint64_t headerX[2]; // sourceConnID, token - GetServer ().ChaCha20 (buf + 16, 16, i2p::context.GetSSU2IntroKey (), nonce, (uint8_t *)headerX); - SetDestConnID (headerX[0]); - // decrypt and handle payload - uint8_t * payload = buf + 32; - CreateNonce (be32toh (header.h.packetNum), nonce); - uint8_t h[32]; - memcpy (h, header.buf, 16); - memcpy (h + 16, &headerX, 16); - if (!i2p::crypto::AEADChaCha20Poly1305 (payload, len - 48, h, 32, - i2p::context.GetSSU2IntroKey (), nonce, payload, len - 48, false)) - { - LogPrint (eLogWarning, "SSU2: PeerTest AEAD verification failed "); - return false; - } - HandlePayload (payload, len - 48); - SetIsDataReceived (false); - return true; - } - - void SSU2PeerTestSession::HandleAddress (const uint8_t * buf, size_t len) - { - if (!ExtractEndpoint (buf, len, m_OurEndpoint)) - LogPrint (eLogWarning, "SSU2: Can't handle address block from peer test message"); - } - - void SSU2PeerTestSession::HandlePeerTest (const uint8_t * buf, size_t len) - { - // msgs 5-7 - if (len < 8) return; - uint8_t msg = buf[0]; - if (msg <= m_MsgNumReceived) - { - LogPrint (eLogDebug, "SSU2: PeerTest msg num ", msg, " received after ", m_MsgNumReceived, ". Ignored"); - return; - } - size_t offset = 3; // points to signed data after msg + code + flag - uint32_t nonce = bufbe32toh (buf + offset + 1); // 1 - ver - switch (msg) // msg - { - case 5: // Alice from Charlie 1 - { - if (htobe64 (((uint64_t)nonce << 32) | nonce) == GetSourceConnID ()) - { - m_PeerTestResendTimer.cancel (); // cancel delayed msg 6 if any - m_IsConnectedRecently = GetServer ().IsConnectedRecently (GetRemoteEndpoint ()); - if (GetAddress ()) - { - if (!m_IsConnectedRecently) - SetRouterStatus (eRouterStatusOK); - else if (m_IsStatusChanged && GetRouterStatus () == eRouterStatusFirewalled) - SetRouterStatus (eRouterStatusUnknown); - SendPeerTest (6, buf + offset, len - offset); - } - } - else - LogPrint (eLogWarning, "SSU2: Peer test 5 nonce mismatch ", nonce, " connID=", GetSourceConnID ()); - break; - } - case 6: // Charlie from Alice - { - m_PeerTestResendTimer.cancel (); // no more msg 5 resends - if (GetAddress ()) - SendPeerTest (7, buf + offset, len - offset); - else - LogPrint (eLogWarning, "SSU2: Unknown address for peer test 6"); - GetServer ().RequestRemoveSession (GetConnID ()); - break; - } - case 7: // Alice from Charlie 2 - { - m_PeerTestResendTimer.cancel (); // no more msg 6 resends - if (m_MsgNumReceived < 5 && m_OurEndpoint.port ()) // msg 5 was not received - { - if (m_OurEndpoint.address ().is_v4 ()) // ipv4 - { - if (i2p::context.GetStatus () == eRouterStatusFirewalled) - { - if (m_OurEndpoint.port () != GetServer ().GetPort (true)) - i2p::context.SetError (eRouterErrorSymmetricNAT); - else if (i2p::context.GetError () == eRouterErrorSymmetricNAT) - i2p::context.SetError (eRouterErrorNone); - } - } - else - { - if (i2p::context.GetStatusV6 () == eRouterStatusFirewalled) - { - if (m_OurEndpoint.port () != GetServer ().GetPort (false)) - i2p::context.SetErrorV6 (eRouterErrorSymmetricNAT); - else if (i2p::context.GetErrorV6 () == eRouterErrorSymmetricNAT) - i2p::context.SetErrorV6 (eRouterErrorNone); - } - } - } - GetServer ().RequestRemoveSession (GetConnID ()); - break; - } - default: - LogPrint (eLogWarning, "SSU2: PeerTest unexpected msg num ", msg); - return; - } - m_MsgNumReceived = msg; - } - - void SSU2PeerTestSession::SendPeerTest (uint8_t msg) - { - auto addr = GetAddress (); - if (!addr) return; - Header header; - uint8_t h[32], payload[SSU2_MAX_PACKET_SIZE]; - // fill packet - header.h.connID = GetDestConnID (); // dest id - RAND_bytes (header.buf + 8, 4); // random packet num - header.h.type = eSSU2PeerTest; - header.h.flags[0] = 2; // ver - header.h.flags[1] = (uint8_t)i2p::context.GetNetID (); // netID - header.h.flags[2] = 0; // flag - memcpy (h, header.buf, 16); - htobuf64 (h + 16, GetSourceConnID ()); // source id - // payload - payload[0] = eSSU2BlkDateTime; - htobe16buf (payload + 1, 4); - htobe32buf (payload + 3, (i2p::util::GetMillisecondsSinceEpoch () + 500)/1000); - size_t payloadSize = 7; - if (msg == 6 || msg == 7) - payloadSize += CreateAddressBlock (payload + payloadSize, GetMaxPayloadSize () - payloadSize, GetRemoteEndpoint ()); - payloadSize += CreatePeerTestBlock (payload + payloadSize, GetMaxPayloadSize () - payloadSize, - msg, eSSU2PeerTestCodeAccept, nullptr, m_SignedData.data (), m_SignedData.size ()); - payloadSize += CreatePaddingBlock (payload + payloadSize, GetMaxPayloadSize () - payloadSize); - // encrypt - uint8_t n[12]; - CreateNonce (be32toh (header.h.packetNum), n); - i2p::crypto::AEADChaCha20Poly1305 (payload, payloadSize, h, 32, addr->i, n, payload, payloadSize + 16, true); - payloadSize += 16; - header.ll[0] ^= CreateHeaderMask (addr->i, payload + (payloadSize - 24)); - header.ll[1] ^= CreateHeaderMask (addr->i, payload + (payloadSize - 12)); - memset (n, 0, 12); - GetServer ().ChaCha20 (h + 16, 16, addr->i, n, h + 16); - // send - GetServer ().Send (header.buf, 16, h + 16, 16, payload, payloadSize, GetRemoteEndpoint ()); - UpdateNumSentBytes (payloadSize + 32); - } - - void SSU2PeerTestSession::SendPeerTest (uint8_t msg, const uint8_t * signedData, size_t signedDataLen, bool delayed) - { - m_SignedData.assign (signedData, signedData + signedDataLen); - if (!delayed) - SendPeerTest (msg); - // schedule resend for msgs 5 or 6 - if (msg == 5 || msg == 6) - ScheduleResend (msg); - } - - void SSU2PeerTestSession::SendPeerTest (uint8_t msg, const uint8_t * signedData, size_t signedDataLen, - std::shared_ptr addr, bool delayed) - { - if (!addr) return; - SetAddress (addr); - SendPeerTest (msg, signedData, signedDataLen, delayed); - } - - void SSU2PeerTestSession::Connect () - { - LogPrint (eLogError, "SSU2: Can't connect peer test session"); - } - - bool SSU2PeerTestSession::ProcessFirstIncomingMessage (uint64_t connID, uint8_t * buf, size_t len) - { - LogPrint (eLogError, "SSU2: Can't handle incoming message in peer test session"); - return false; - } - - void SSU2PeerTestSession::ScheduleResend (uint8_t msg) - { - if (m_NumResends < SSU2_PEER_TEST_MAX_NUM_RESENDS) - { - m_PeerTestResendTimer.expires_from_now (boost::posix_time::milliseconds( - SSU2_PEER_TEST_RESEND_INTERVAL + GetServer ().GetRng ()() % SSU2_PEER_TEST_RESEND_INTERVAL_VARIANCE)); - std::weak_ptr s(std::static_pointer_cast(shared_from_this ())); - m_PeerTestResendTimer.async_wait ([s, msg](const boost::system::error_code& ecode) - { - if (ecode != boost::asio::error::operation_aborted) - { - auto s1 = s.lock (); - if (s1) - { - if (msg > s1->m_MsgNumReceived) - { - s1->SendPeerTest (msg); - s1->m_NumResends++; - s1->ScheduleResend (msg); - } - } - } - }); - } - } - - SSU2HolePunchSession::SSU2HolePunchSession (SSU2Server& server, uint32_t nonce, - const boost::asio::ip::udp::endpoint& remoteEndpoint, - std::shared_ptr addr): - SSU2Session (server), // we create full incoming session - m_NumResends (0), m_HolePunchResendTimer (server.GetService ()) - { - // we are Charlie - uint64_t destConnID = htobe64 (((uint64_t)nonce << 32) | nonce); // dest id - uint64_t sourceConnID = ~destConnID; - SetSourceConnID (sourceConnID); - SetDestConnID (destConnID); - SetState (eSSU2SessionStateHolePunch); - SetRemoteEndpoint (remoteEndpoint); - SetAddress (addr); - SetTerminationTimeout (SSU2_RELAY_NONCE_EXPIRATION_TIMEOUT); - } - - void SSU2HolePunchSession::SendHolePunch () - { - auto addr = GetAddress (); - if (!addr) return; - auto& ep = GetRemoteEndpoint (); - LogPrint (eLogDebug, "SSU2: Sending HolePunch to ", ep); - Header header; - uint8_t h[32], payload[SSU2_MAX_PACKET_SIZE]; - // fill packet - header.h.connID = GetDestConnID (); // dest id - RAND_bytes (header.buf + 8, 4); // random packet num - header.h.type = eSSU2HolePunch; - header.h.flags[0] = 2; // ver - header.h.flags[1] = (uint8_t)i2p::context.GetNetID (); // netID - header.h.flags[2] = 0; // flag - memcpy (h, header.buf, 16); - htobuf64 (h + 16, GetSourceConnID ()); // source id - RAND_bytes (h + 24, 8); // header token, to be ignored by Alice - // payload - payload[0] = eSSU2BlkDateTime; - htobe16buf (payload + 1, 4); - htobe32buf (payload + 3, (i2p::util::GetMillisecondsSinceEpoch () + 500)/1000); - size_t payloadSize = 7; - payloadSize += CreateAddressBlock (payload + payloadSize, GetMaxPayloadSize () - payloadSize, ep); - // relay response block - if (payloadSize + m_RelayResponseBlock.size () < GetMaxPayloadSize ()) - { - memcpy (payload + payloadSize, m_RelayResponseBlock.data (), m_RelayResponseBlock.size ()); - payloadSize += m_RelayResponseBlock.size (); - } - payloadSize += CreatePaddingBlock (payload + payloadSize, GetMaxPayloadSize () - payloadSize); - // encrypt - uint8_t n[12]; - CreateNonce (be32toh (header.h.packetNum), n); - i2p::crypto::AEADChaCha20Poly1305 (payload, payloadSize, h, 32, addr->i, n, payload, payloadSize + 16, true); - payloadSize += 16; - header.ll[0] ^= CreateHeaderMask (addr->i, payload + (payloadSize - 24)); - header.ll[1] ^= CreateHeaderMask (addr->i, payload + (payloadSize - 12)); - memset (n, 0, 12); - GetServer ().ChaCha20 (h + 16, 16, addr->i, n, h + 16); - // send - GetServer ().Send (header.buf, 16, h + 16, 16, payload, payloadSize, ep); - UpdateNumSentBytes (payloadSize + 32); - } - - void SSU2HolePunchSession::SendHolePunch (const uint8_t * relayResponseBlock, size_t relayResponseBlockLen) - { - m_RelayResponseBlock.assign (relayResponseBlock, relayResponseBlock + relayResponseBlockLen); - SendHolePunch (); - ScheduleResend (); - } - - void SSU2HolePunchSession::ScheduleResend () - { - if (m_NumResends < SSU2_HOLE_PUNCH_MAX_NUM_RESENDS) - { - m_HolePunchResendTimer.expires_from_now (boost::posix_time::milliseconds( - SSU2_HOLE_PUNCH_RESEND_INTERVAL + GetServer ().GetRng ()() % SSU2_HOLE_PUNCH_RESEND_INTERVAL_VARIANCE)); - std::weak_ptr s(std::static_pointer_cast(shared_from_this ())); - m_HolePunchResendTimer.async_wait ([s](const boost::system::error_code& ecode) - { - if (ecode != boost::asio::error::operation_aborted) - { - auto s1 = s.lock (); - if (s1 && s1->GetState () == eSSU2SessionStateHolePunch) - { - s1->SendHolePunch (); - s1->m_NumResends++; - s1->ScheduleResend (); - } - } - }); - } - } - - bool SSU2HolePunchSession::ProcessFirstIncomingMessage (uint64_t connID, uint8_t * buf, size_t len) - { - m_HolePunchResendTimer.cancel (); - return SSU2Session::ProcessFirstIncomingMessage (connID, buf, len); - } -} -} diff --git a/libi2pd/SSU2OutOfSession.h b/libi2pd/SSU2OutOfSession.h deleted file mode 100644 index e8c55c3c..00000000 --- a/libi2pd/SSU2OutOfSession.h +++ /dev/null @@ -1,86 +0,0 @@ -/* -* Copyright (c) 2024, The PurpleI2P Project -* -* This file is part of Purple i2pd project and licensed under BSD3 -* -* See full license text in LICENSE file at top of project tree -*/ - -#ifndef SSU2_OUT_OF_SESSION_H__ -#define SSU2_OUT_OF_SESSION_H__ - -#include -#include "SSU2Session.h" - -namespace i2p -{ -namespace transport -{ - const int SSU2_PEER_TEST_RESEND_INTERVAL = 3000; // in milliseconds - const int SSU2_PEER_TEST_RESEND_INTERVAL_VARIANCE = 2000; // in milliseconds - const int SSU2_PEER_TEST_MAX_NUM_RESENDS = 3; - - class SSU2PeerTestSession: public SSU2Session // for PeerTest msgs 5,6,7 - { - public: - - SSU2PeerTestSession (SSU2Server& server, uint64_t sourceConnID, uint64_t destConnID); - - uint8_t GetMsgNumReceived () const { return m_MsgNumReceived; } - bool IsConnectedRecently () const { return m_IsConnectedRecently; } - void SetStatusChanged () { m_IsStatusChanged = true; } - - void SendPeerTest (uint8_t msg, const uint8_t * signedData, size_t signedDataLen, - std::shared_ptr addr, bool delayed = false); - bool ProcessPeerTest (uint8_t * buf, size_t len) override; - void Connect () override; // outgoing - bool ProcessFirstIncomingMessage (uint64_t connID, uint8_t * buf, size_t len) override; // incoming - - private: - - void SendPeerTest (uint8_t msg, const uint8_t * signedData, size_t signedDataLen, bool delayed = false); // PeerTest message - void SendPeerTest (uint8_t msg); // send or resend m_SignedData - void HandlePeerTest (const uint8_t * buf, size_t len) override; - void HandleAddress (const uint8_t * buf, size_t len) override; - - void ScheduleResend (uint8_t msg); - - private: - - uint8_t m_MsgNumReceived, m_NumResends; - bool m_IsConnectedRecently, m_IsStatusChanged; - std::vector m_SignedData; // for resends - boost::asio::deadline_timer m_PeerTestResendTimer; - boost::asio::ip::udp::endpoint m_OurEndpoint; // as seen by peer - }; - - const int SSU2_HOLE_PUNCH_RESEND_INTERVAL = 1000; // in milliseconds - const int SSU2_HOLE_PUNCH_RESEND_INTERVAL_VARIANCE = 500; // in milliseconds - const int SSU2_HOLE_PUNCH_MAX_NUM_RESENDS = 3; - - class SSU2HolePunchSession: public SSU2Session // Charlie - { - public: - - SSU2HolePunchSession (SSU2Server& server, uint32_t nonce, const boost::asio::ip::udp::endpoint& remoteEndpoint, - std::shared_ptr addr); - - void SendHolePunch (const uint8_t * relayResponseBlock, size_t relayResponseBlockLen); - - bool ProcessFirstIncomingMessage (uint64_t connID, uint8_t * buf, size_t len) override; // SessionRequest - - private: - - void SendHolePunch (); - void ScheduleResend (); - - private: - - int m_NumResends; - std::vector m_RelayResponseBlock; - boost::asio::deadline_timer m_HolePunchResendTimer; - }; -} -} - -#endif diff --git a/libi2pd/SSU2Session.cpp b/libi2pd/SSU2Session.cpp index cb10f848..6213c614 100644 --- a/libi2pd/SSU2Session.cpp +++ b/libi2pd/SSU2Session.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2022-2025, The PurpleI2P Project +* Copyright (c) 2022-2024, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -13,12 +13,17 @@ #include "Gzip.h" #include "NetDb.hpp" #include "SSU2.h" -#include "SSU2Session.h" namespace i2p { namespace transport { + static inline void CreateNonce (uint64_t seqn, uint8_t * nonce) + { + memset (nonce, 0, 4); + htole64buf (nonce + 4, seqn); + } + void SSU2IncompleteMessage::AttachNextFragment (const uint8_t * fragment, size_t fragmentSize) { if (msg->len + fragmentSize > msg->maxLen) @@ -83,7 +88,7 @@ namespace transport std::shared_ptr addr, bool noise): TransportSession (in_RemoteRouter, SSU2_CONNECT_TIMEOUT), m_Server (server), m_Address (addr), m_RemoteTransports (0), m_RemotePeerTestTransports (0), - m_RemoteVersion (0), m_DestConnID (0), m_SourceConnID (0), m_State (eSSU2SessionStateUnknown), + m_DestConnID (0), m_SourceConnID (0), m_State (eSSU2SessionStateUnknown), m_SendPacketNum (0), m_ReceivePacketNum (0), m_LastDatetimeSentPacketNum (0), m_IsDataReceived (false), m_RTT (SSU2_UNKNOWN_RTT), m_MsgLocalExpirationTimeout (I2NP_MESSAGE_LOCAL_EXPIRATION_TIMEOUT_MAX), @@ -92,7 +97,7 @@ namespace transport m_RTO (SSU2_INITIAL_RTO), m_RelayTag (0),m_ConnectTimer (server.GetService ()), m_TerminationReason (eSSU2TerminationReasonNormalClose), m_MaxPayloadSize (SSU2_MIN_PACKET_SIZE - IPV6_HEADER_SIZE - UDP_HEADER_SIZE - 32), // min size - m_LastResendTime (0), m_LastResendAttemptTime (0), m_NumRanges (0) + m_LastResendTime (0), m_LastResendAttemptTime (0) { if (noise) m_NoiseState.reset (new i2p::crypto::NoiseSymmetricState); @@ -103,7 +108,6 @@ namespace transport InitNoiseXKState1 (*m_NoiseState, m_Address->s); m_RemoteEndpoint = boost::asio::ip::udp::endpoint (m_Address->host, m_Address->port); m_RemoteTransports = in_RemoteRouter->GetCompatibleTransports (false); - m_RemoteVersion = in_RemoteRouter->GetVersion (); if (in_RemoteRouter->IsSSU2PeerTesting (true)) m_RemotePeerTestTransports |= i2p::data::RouterInfo::eSSU2V4; if (in_RemoteRouter->IsSSU2PeerTesting (false)) m_RemotePeerTestTransports |= i2p::data::RouterInfo::eSSU2V6; RAND_bytes ((uint8_t *)&m_DestConnID, 8); @@ -189,7 +193,7 @@ namespace transport if (!asz) return false; payload[17] = asz; packet->payloadSize = asz + 18; - SignedData<128> s; + SignedData s; s.Insert ((const uint8_t *)"RelayRequestData", 16); // prologue s.Insert (GetRemoteIdentity ()->GetIdentHash (), 32); // bhash s.Insert (session->GetRemoteIdentity ()->GetIdentHash (), 32); // chash @@ -227,13 +231,6 @@ namespace transport if (m_Server.AddPendingOutgoingSession (shared_from_this ())) { m_Server.RemoveSession (GetConnID ()); - // update endpoint in profile because we know it now - auto identity = GetRemoteIdentity (); - if (identity) - { - auto profile = i2p::data::GetRouterProfile (identity->GetIdentHash ()); - if (profile) profile->SetLastEndpoint (m_RemoteEndpoint); - } // connect LogPrint (eLogDebug, "SSU2: Connecting after introduction to ", GetIdentHashBase64()); Connect (); @@ -293,8 +290,6 @@ namespace transport m_SentHandshakePacket.reset (nullptr); m_SessionConfirmedFragment.reset (nullptr); m_PathChallenge.reset (nullptr); - if (!m_IntermediateQueue.empty ()) - m_SendQueue.splice (m_SendQueue.end (), m_IntermediateQueue); for (auto& it: m_SendQueue) it->Drop (); m_SendQueue.clear (); @@ -337,19 +332,18 @@ namespace transport SetTerminationTimeout (SSU2_TERMINATION_TIMEOUT); SendQueue (); transports.PeerConnected (shared_from_this ()); - - LogPrint(eLogDebug, "SSU2: Session with ", GetRemoteEndpoint (), - " (", i2p::data::GetIdentHashAbbreviation (GetRemoteIdentity ()->GetIdentHash ()), ") established"); if (m_OnEstablished) { m_OnEstablished (); m_OnEstablished = nullptr; } + LogPrint(eLogDebug, "SSU2: Session with ", GetRemoteEndpoint (), + " (", i2p::data::GetIdentHashAbbreviation (GetRemoteIdentity ()->GetIdentHash ()), ") established"); } void SSU2Session::Done () { - boost::asio::post (m_Server.GetService (), std::bind (&SSU2Session::Terminate, shared_from_this ())); + m_Server.GetService ().post (std::bind (&SSU2Session::Terminate, shared_from_this ())); } void SSU2Session::SendLocalRouterInfo (bool update) @@ -357,7 +351,7 @@ namespace transport if (update || !IsOutgoing ()) { auto s = shared_from_this (); - boost::asio::post (m_Server.GetService (), [s]() + m_Server.GetService ().post ([s]() { if (!s->IsEstablished ()) return; uint8_t payload[SSU2_MAX_PACKET_SIZE]; @@ -375,31 +369,14 @@ namespace transport } - void SSU2Session::SendI2NPMessages (std::list >& msgs) + void SSU2Session::SendI2NPMessages (const std::vector >& msgs) { - if (m_State == eSSU2SessionStateTerminated || msgs.empty ()) - { - msgs.clear (); - return; - } - bool empty = false; - { - std::lock_guard l(m_IntermediateQueueMutex); - empty = m_IntermediateQueue.empty (); - m_IntermediateQueue.splice (m_IntermediateQueue.end (), msgs); - } - if (empty) - boost::asio::post (m_Server.GetService (), std::bind (&SSU2Session::PostI2NPMessages, shared_from_this ())); + m_Server.GetService ().post (std::bind (&SSU2Session::PostI2NPMessages, shared_from_this (), msgs)); } - void SSU2Session::PostI2NPMessages () + void SSU2Session::PostI2NPMessages (std::vector > msgs) { if (m_State == eSSU2SessionStateTerminated) return; - std::list > msgs; - { - std::lock_guard l(m_IntermediateQueueMutex); - m_IntermediateQueue.swap (msgs); - } uint64_t mts = i2p::util::GetMonotonicMicroseconds (); bool isSemiFull = false; if (m_SendQueue.size ()) @@ -413,24 +390,16 @@ namespace transport " is semi-full (size = ", m_SendQueue.size (), ", lag = ", queueLag / 1000, ", rtt = ", (int)m_RTT, ")"); } } - if (isSemiFull) - { - for (auto it: msgs) - { - if (it->onDrop) - it->Drop (); // drop earlier because we can handle it - else - { - it->SetEnqueueTime (mts); - m_SendQueue.push_back (std::move (it)); - } - } - } - else + for (auto it: msgs) { - for (auto& it: msgs) it->SetEnqueueTime (mts); - m_SendQueue.splice (m_SendQueue.end (), msgs); - } + if (isSemiFull && it->onDrop) + it->Drop (); // drop earlier because we can handle it + else + { + it->SetEnqueueTime (mts); + m_SendQueue.push_back (std::move (it)); + } + } if (IsEstablished ()) { SendQueue (); @@ -443,7 +412,7 @@ namespace transport void SSU2Session::MoveSendQueue (std::shared_ptr other) { if (!other || m_SendQueue.empty ()) return; - std::list > msgs; + std::vector > msgs; auto ts = i2p::util::GetMillisecondsSinceEpoch (); for (auto it: m_SendQueue) if (!it->IsExpired (ts)) @@ -452,7 +421,7 @@ namespace transport it->Drop (); m_SendQueue.clear (); if (!msgs.empty ()) - other->SendI2NPMessages (msgs); + other->PostI2NPMessages (msgs); } bool SSU2Session::SendQueue () @@ -623,8 +592,7 @@ namespace transport } else { - uint32_t packetNum = SendData (it->second->payload, it->second->payloadSize, - it->second->numResends > 1 ? SSU2_FLAG_IMMEDIATE_ACK_REQUESTED : 0); + uint32_t packetNum = SendData (it->second->payload, it->second->payloadSize); it->second->numResends++; it->second->sendTime = ts; resentPackets.emplace (packetNum, it->second); @@ -683,7 +651,7 @@ namespace transport } const uint8_t nonce[12] = {0}; uint64_t headerX[2]; - m_Server.ChaCha20 (buf + 16, 16, i2p::context.GetSSU2IntroKey (), nonce, (uint8_t *)headerX); + i2p::crypto::ChaCha20 (buf + 16, 16, i2p::context.GetSSU2IntroKey (), nonce, (uint8_t *)headerX); LogPrint (eLogWarning, "SSU2: Unexpected PeerTest message SourceConnID=", connID, " DestConnID=", headerX[0]); break; } @@ -749,7 +717,7 @@ namespace transport payloadSize += 16; header.ll[0] ^= CreateHeaderMask (m_Address->i, payload + (payloadSize - 24)); header.ll[1] ^= CreateHeaderMask (m_Address->i, payload + (payloadSize - 12)); - m_Server.ChaCha20 (headerX, 48, m_Address->i, nonce, headerX); + i2p::crypto::ChaCha20 (headerX, 48, m_Address->i, nonce, headerX); m_NoiseState->MixHash (payload, payloadSize); // h = SHA256(h || encrypted payload from Session Request) for SessionCreated m_SentHandshakePacket->payloadSize = payloadSize; // send @@ -776,7 +744,7 @@ namespace transport } const uint8_t nonce[12] = {0}; uint8_t headerX[48]; - m_Server.ChaCha20 (buf + 16, 48, i2p::context.GetSSU2IntroKey (), nonce, headerX); + i2p::crypto::ChaCha20 (buf + 16, 48, i2p::context.GetSSU2IntroKey (), nonce, headerX); memcpy (&m_DestConnID, headerX, 8); uint64_t token; memcpy (&token, headerX + 8, 8); @@ -875,7 +843,7 @@ namespace transport m_NoiseState->MixHash (payload, payloadSize); // h = SHA256(h || encrypted Noise payload from Session Created) header.ll[0] ^= CreateHeaderMask (i2p::context.GetSSU2IntroKey (), payload + (payloadSize - 24)); header.ll[1] ^= CreateHeaderMask (kh2, payload + (payloadSize - 12)); - m_Server.ChaCha20 (headerX, 48, kh2, nonce, headerX); + i2p::crypto::ChaCha20 (headerX, 48, kh2, nonce, headerX); m_State = eSSU2SessionStateSessionCreatedSent; m_SentHandshakePacket->payloadSize = payloadSize; // send @@ -903,7 +871,7 @@ namespace transport m_HandshakeInterval = i2p::util::GetMillisecondsSinceEpoch () - m_HandshakeInterval; const uint8_t nonce[12] = {0}; uint8_t headerX[48]; - m_Server.ChaCha20 (buf + 16, 48, kh2, nonce, headerX); + i2p::crypto::ChaCha20 (buf + 16, 48, kh2, nonce, headerX); // KDF for SessionCreated m_NoiseState->MixHash ( { {header.buf, 16}, {headerX, 16} } ); // h = SHA256(h || header) m_NoiseState->MixHash (headerX + 16, 32); // h = SHA256(h || bepk); @@ -1179,18 +1147,13 @@ namespace transport LogPrint (eLogError, "SSU2: Couldn't update RouterInfo from SessionConfirmed in netdb"); return false; } - - bool isOlder = false; + std::shared_ptr profile; // not null if older if (ri->GetTimestamp () + i2p::data::NETDB_EXPIRATION_TIMEOUT_THRESHOLD*1000LL < ri1->GetTimestamp ()) { // received RouterInfo is older than one in netdb - isOlder = true; - if (ri->HasProfile ()) - { - auto profile = i2p::data::GetRouterProfile (ri->GetIdentHash ()); // retrieve profile - if (profile && profile->IsDuplicated ()) - return false; - } + profile = i2p::data::GetRouterProfile (ri->GetIdentHash ()); // retrieve profile + if (profile && profile->IsDuplicated ()) + return false; } ri = ri1; @@ -1204,28 +1167,13 @@ namespace transport (!m_RemoteEndpoint.address ().is_v6 () || memcmp (m_RemoteEndpoint.address ().to_v6 ().to_bytes ().data (), m_Address->host.to_v6 ().to_bytes ().data (), 8))) // temporary address { - if (isOlder) // older router? - i2p::data::UpdateRouterProfile (ri->GetIdentHash (), - [](std::shared_ptr profile) - { - if (profile) profile->Duplicated (); // mark router as duplicated in profile - }); + if (profile) // older router? + profile->Duplicated (); // mark router as duplicated in profile else LogPrint (eLogInfo, "SSU2: Host mismatch between published address ", m_Address->host, " and actual endpoint ", m_RemoteEndpoint.address (), " from ", i2p::data::GetIdentHashAbbreviation (ri->GetIdentHash ())); return false; } - if (!m_Address->published) - { - if (ri->HasProfile ()) - ri->GetProfile ()->SetLastEndpoint (m_RemoteEndpoint); - else - i2p::data::UpdateRouterProfile (ri->GetIdentHash (), - [ep = m_RemoteEndpoint](std::shared_ptr profile) - { - if (profile) profile->SetLastEndpoint (ep); - }); - } SetRemoteIdentity (ri->GetRouterIdentity ()); AdjustMaxPayloadSize (); m_Server.AddSessionByRouterHash (shared_from_this ()); // we know remote router now @@ -1233,8 +1181,7 @@ namespace transport m_RemotePeerTestTransports = 0; if (ri->IsSSU2PeerTesting (true)) m_RemotePeerTestTransports |= i2p::data::RouterInfo::eSSU2V4; if (ri->IsSSU2PeerTesting (false)) m_RemotePeerTestTransports |= i2p::data::RouterInfo::eSSU2V6; - m_RemoteVersion = ri->GetVersion (); - + // handle other blocks HandlePayload (decryptedPayload.data () + riSize + 3, decryptedPayload.size () - riSize - 3); Established (); @@ -1283,7 +1230,7 @@ namespace transport header.ll[0] ^= CreateHeaderMask (m_Address->i, payload + (payloadSize - 24)); header.ll[1] ^= CreateHeaderMask (m_Address->i, payload + (payloadSize - 12)); memset (nonce, 0, 12); - m_Server.ChaCha20 (h + 16, 16, m_Address->i, nonce, h + 16); + i2p::crypto::ChaCha20 (h + 16, 16, m_Address->i, nonce, h + 16); // send if (m_Server.AddPendingOutgoingSession (shared_from_this ())) m_Server.Send (header.buf, 16, h + 16, 16, payload, payloadSize, m_RemoteEndpoint); @@ -1305,7 +1252,7 @@ namespace transport uint8_t nonce[12] = {0}; uint8_t h[32]; memcpy (h, header.buf, 16); - m_Server.ChaCha20 (buf + 16, 16, i2p::context.GetSSU2IntroKey (), nonce, h + 16); + i2p::crypto::ChaCha20 (buf + 16, 16, i2p::context.GetSSU2IntroKey (), nonce, h + 16); memcpy (&m_DestConnID, h + 16, 8); // decrypt CreateNonce (be32toh (header.h.packetNum), nonce); @@ -1357,7 +1304,7 @@ namespace transport header.ll[0] ^= CreateHeaderMask (i2p::context.GetSSU2IntroKey (), payload + (payloadSize - 24)); header.ll[1] ^= CreateHeaderMask (i2p::context.GetSSU2IntroKey (), payload + (payloadSize - 12)); memset (nonce, 0, 12); - m_Server.ChaCha20 (h + 16, 16, i2p::context.GetSSU2IntroKey (), nonce, h + 16); + i2p::crypto::ChaCha20 (h + 16, 16, i2p::context.GetSSU2IntroKey (), nonce, h + 16); // send m_Server.Send (header.buf, 16, h + 16, 16, payload, payloadSize, m_RemoteEndpoint); } @@ -1381,7 +1328,7 @@ namespace transport } uint8_t nonce[12] = {0}; uint64_t headerX[2]; // sourceConnID, token - m_Server.ChaCha20 (buf + 16, 16, m_Address->i, nonce, (uint8_t *)headerX); + i2p::crypto::ChaCha20 (buf + 16, 16, m_Address->i, nonce, (uint8_t *)headerX); uint64_t token = headerX[1]; if (token) m_Server.UpdateOutgoingToken (m_RemoteEndpoint, token, i2p::util::GetSecondsSinceEpoch () + SSU2_TOKEN_EXPIRATION_TIMEOUT); @@ -1409,7 +1356,47 @@ namespace transport SendSessionRequest (token); return true; } - + + void SSU2Session::SendHolePunch (uint32_t nonce, const boost::asio::ip::udp::endpoint& ep, + const uint8_t * introKey, uint64_t token) + { + // we are Charlie + LogPrint (eLogDebug, "SSU2: Sending HolePunch to ", ep); + Header header; + uint8_t h[32], payload[SSU2_MAX_PACKET_SIZE]; + // fill packet + header.h.connID = htobe64 (((uint64_t)nonce << 32) | nonce); // dest id + RAND_bytes (header.buf + 8, 4); // random packet num + header.h.type = eSSU2HolePunch; + header.h.flags[0] = 2; // ver + header.h.flags[1] = (uint8_t)i2p::context.GetNetID (); // netID + header.h.flags[2] = 0; // flag + memcpy (h, header.buf, 16); + uint64_t c = ~header.h.connID; + memcpy (h + 16, &c, 8); // source id + RAND_bytes (h + 24, 8); // token + // payload + payload[0] = eSSU2BlkDateTime; + htobe16buf (payload + 1, 4); + htobe32buf (payload + 3, (i2p::util::GetMillisecondsSinceEpoch () + 500)/1000); + size_t payloadSize = 7; + payloadSize += CreateAddressBlock (payload + payloadSize, m_MaxPayloadSize - payloadSize, ep); + payloadSize += CreateRelayResponseBlock (payload + payloadSize, m_MaxPayloadSize - payloadSize, + eSSU2RelayResponseCodeAccept, nonce, token, ep.address ().is_v4 ()); + payloadSize += CreatePaddingBlock (payload + payloadSize, m_MaxPayloadSize - payloadSize); + // encrypt + uint8_t n[12]; + CreateNonce (be32toh (header.h.packetNum), n); + i2p::crypto::AEADChaCha20Poly1305 (payload, payloadSize, h, 32, introKey, n, payload, payloadSize + 16, true); + payloadSize += 16; + header.ll[0] ^= CreateHeaderMask (introKey, payload + (payloadSize - 24)); + header.ll[1] ^= CreateHeaderMask (introKey, payload + (payloadSize - 12)); + memset (n, 0, 12); + i2p::crypto::ChaCha20 (h + 16, 16, introKey, n, h + 16); + // send + m_Server.Send (header.buf, 16, h + 16, 16, payload, payloadSize, ep); + } + bool SSU2Session::ProcessHolePunch (uint8_t * buf, size_t len) { // we are Alice @@ -1430,7 +1417,7 @@ namespace transport } uint8_t nonce[12] = {0}; uint64_t headerX[2]; // sourceConnID, token - m_Server.ChaCha20 (buf + 16, 16, i2p::context.GetSSU2IntroKey (), nonce, (uint8_t *)headerX); + i2p::crypto::ChaCha20 (buf + 16, 16, i2p::context.GetSSU2IntroKey (), nonce, (uint8_t *)headerX); m_DestConnID = headerX[0]; // decrypt and handle payload uint8_t * payload = buf + 32; @@ -1474,7 +1461,7 @@ namespace transport uint8_t nonce[12]; CreateNonce (m_SendPacketNum, nonce); uint8_t payload[SSU2_MAX_PACKET_SIZE]; - m_Server.AEADChaCha20Poly1305Encrypt (buf, len, header.buf, 16, m_KeyDataSend, nonce, payload, SSU2_MAX_PACKET_SIZE); + i2p::crypto::AEADChaCha20Poly1305 (buf, len, header.buf, 16, m_KeyDataSend, nonce, payload, SSU2_MAX_PACKET_SIZE, true); header.ll[0] ^= CreateHeaderMask (m_Address->i, payload + (len - 8)); header.ll[1] ^= CreateHeaderMask (m_KeyDataSend + 32, payload + (len + 4)); m_Server.Send (header.buf, 16, payload, len + 16, m_RemoteEndpoint); @@ -1498,11 +1485,11 @@ namespace transport ResendHandshakePacket (); // assume we receive return; } - if (from != m_RemoteEndpoint && !i2p::transport::transports.IsInReservedRange (from.address ()) && - (!m_PathChallenge || from != m_PathChallenge->second)) // path challenge was not sent to this endpoint yet + if (from != m_RemoteEndpoint && !i2p::transport::transports.IsInReservedRange (from.address ())) { LogPrint (eLogInfo, "SSU2: Remote endpoint update ", m_RemoteEndpoint, "->", from); - SendPathChallenge (from); + m_RemoteEndpoint = from; + SendPathChallenge (); } if (len < 32) { @@ -1514,8 +1501,8 @@ namespace transport uint32_t packetNum = be32toh (header.h.packetNum); uint8_t nonce[12]; CreateNonce (packetNum, nonce); - if (!m_Server.AEADChaCha20Poly1305Decrypt (buf + 16, payloadSize, header.buf, 16, - m_KeyDataReceive, nonce, payload, payloadSize)) + if (!i2p::crypto::AEADChaCha20Poly1305 (buf + 16, payloadSize, header.buf, 16, + m_KeyDataReceive, nonce, payload, payloadSize, false)) { LogPrint (eLogWarning, "SSU2: Data AEAD verification failed "); return; @@ -1660,11 +1647,10 @@ namespace transport LogPrint (eLogDebug, "SSU2: Path response"); if (m_PathChallenge) { - if (buf64toh (buf + offset) == m_PathChallenge->first) - { - m_RemoteEndpoint = m_PathChallenge->second; + i2p::data::IdentHash hash; + SHA256 (buf + offset, size, hash); + if (hash == *m_PathChallenge) m_PathChallenge.reset (nullptr); - } } break; } @@ -1764,7 +1750,6 @@ namespace transport HandleAckRange (firstPacketNum, ackThrough, i2p::util::GetMillisecondsSinceEpoch ()); // acnt // ranges len -= 5; - if (!len || m_SentPackets.empty ()) return; // don't handle ranges if nothing to acknowledge const uint8_t * ranges = buf + 5; while (len > 0 && firstPacketNum && ackThrough - firstPacketNum < SSU2_MAX_NUM_ACK_PACKETS) { @@ -1966,99 +1951,85 @@ namespace transport void SSU2Session::HandleRelayRequest (const uint8_t * buf, size_t len) { // we are Bob - if (len < 9) return; - auto mts = i2p::util::GetMillisecondsSinceEpoch (); - uint32_t nonce = bufbe32toh (buf + 1); // nonce uint32_t relayTag = bufbe32toh (buf + 5); // relay tag auto session = m_Server.FindRelaySession (relayTag); if (!session) { LogPrint (eLogWarning, "SSU2: RelayRequest session with relay tag ", relayTag, " not found"); // send relay response back to Alice - auto packet = m_Server.GetSentPacketsPool ().AcquireShared (); - packet->payloadSize = CreateAckBlock (packet->payload, m_MaxPayloadSize); - packet->payloadSize += CreateRelayResponseBlock (packet->payload + packet->payloadSize, m_MaxPayloadSize - packet->payloadSize, - eSSU2RelayResponseCodeBobRelayTagNotFound, nonce, 0, false); - packet->payloadSize += CreatePaddingBlock (packet->payload + packet->payloadSize, m_MaxPayloadSize - packet->payloadSize); - uint32_t packetNum = SendData (packet->payload, packet->payloadSize); - if (m_RemoteVersion >= SSU2_MIN_RELAY_RESPONSE_RESEND_VERSION) - { - // sometimes Alice doesn't ack this RelayResponse in older versions - packet->sendTime = mts; - m_SentPackets.emplace (packetNum, packet); - } + uint8_t payload[SSU2_MAX_PACKET_SIZE]; + size_t payloadSize = CreateRelayResponseBlock (payload, m_MaxPayloadSize, + eSSU2RelayResponseCodeBobRelayTagNotFound, bufbe32toh (buf + 1), 0, false); + payloadSize += CreatePaddingBlock (payload + payloadSize, m_MaxPayloadSize - payloadSize); + SendData (payload, payloadSize); return; } - if (session->m_RelaySessions.emplace (nonce, std::make_pair (shared_from_this (), mts/1000)).second) - { - // send relay intro to Charlie - auto r = i2p::data::netdb.FindRouter (GetRemoteIdentity ()->GetIdentHash ()); // Alice's RI - if (r && (r->IsUnreachable () || !i2p::data::netdb.PopulateRouterInfoBuffer (r))) r = nullptr; - if (!r) LogPrint (eLogWarning, "SSU2: RelayRequest Alice's router info not found"); + auto mts = i2p::util::GetMillisecondsSinceEpoch (); + session->m_RelaySessions.emplace (bufbe32toh (buf + 1), // nonce + std::make_pair (shared_from_this (), mts/1000) ); - auto packet = m_Server.GetSentPacketsPool ().AcquireShared (); - packet->payloadSize = r ? CreateRouterInfoBlock (packet->payload, m_MaxPayloadSize - len - 32, r) : 0; - if (!packet->payloadSize && r) - session->SendFragmentedMessage (CreateDatabaseStoreMsg (r)); - packet->payloadSize += CreateRelayIntroBlock (packet->payload + packet->payloadSize, m_MaxPayloadSize - packet->payloadSize, buf + 1, len - 1); - if (packet->payloadSize < m_MaxPayloadSize) - packet->payloadSize += CreatePaddingBlock (packet->payload + packet->payloadSize, m_MaxPayloadSize - packet->payloadSize); - uint32_t packetNum = session->SendData (packet->payload, packet->payloadSize); - packet->sendTime = mts; - // Charlie always responds with RelayResponse - session->m_SentPackets.emplace (packetNum, packet); - } - else - LogPrint (eLogInfo, "SSU2: Relay request nonce ", nonce, " already exists. Ignore"); + // send relay intro to Charlie + auto r = i2p::data::netdb.FindRouter (GetRemoteIdentity ()->GetIdentHash ()); // Alice's RI + if (r && (r->IsUnreachable () || !i2p::data::netdb.PopulateRouterInfoBuffer (r))) r = nullptr; + if (!r) LogPrint (eLogWarning, "SSU2: RelayRequest Alice's router info not found"); + + auto packet = m_Server.GetSentPacketsPool ().AcquireShared (); + packet->payloadSize = r ? CreateRouterInfoBlock (packet->payload, m_MaxPayloadSize - len - 32, r) : 0; + if (!packet->payloadSize && r) + session->SendFragmentedMessage (CreateDatabaseStoreMsg (r)); + packet->payloadSize += CreateRelayIntroBlock (packet->payload + packet->payloadSize, m_MaxPayloadSize - packet->payloadSize, buf + 1, len -1); + if (packet->payloadSize < m_MaxPayloadSize) + packet->payloadSize += CreatePaddingBlock (packet->payload + packet->payloadSize, m_MaxPayloadSize - packet->payloadSize); + uint32_t packetNum = session->SendData (packet->payload, packet->payloadSize); + packet->sendTime = mts; + // Charlie always responds with RelayResponse + session->m_SentPackets.emplace (packetNum, packet); } void SSU2Session::HandleRelayIntro (const uint8_t * buf, size_t len, int attempts) { // we are Charlie - if (len < 47) return; + auto mts = i2p::util::GetMillisecondsSinceEpoch (); SSU2RelayResponseCode code = eSSU2RelayResponseCodeAccept; - boost::asio::ip::udp::endpoint ep; - std::shared_ptr addr; + uint64_t token = 0; + bool isV4 = false; auto r = i2p::data::netdb.FindRouter (buf + 1); // Alice if (r) { - SignedData<128> s; + SignedData s; s.Insert ((const uint8_t *)"RelayRequestData", 16); // prologue s.Insert (GetRemoteIdentity ()->GetIdentHash (), 32); // bhash s.Insert (i2p::context.GetIdentHash (), 32); // chash s.Insert (buf + 33, 14); // nonce, relay tag, timestamp, ver, asz uint8_t asz = buf[46]; - if (asz + 47 + r->GetIdentity ()->GetSignatureLen () > len) - { - LogPrint (eLogWarning, "SSU2: Malformed RelayIntro len=", len); - return; - } s.Insert (buf + 47, asz); // Alice Port, Alice IP if (s.Verify (r->GetIdentity (), buf + 47 + asz)) { - // obtain and check endpoint and address for HolePunch + // send HolePunch + boost::asio::ip::udp::endpoint ep; if (ExtractEndpoint (buf + 47, asz, ep)) { + std::shared_ptr addr; if (!ep.address ().is_unspecified () && ep.port ()) + addr = ep.address ().is_v6 () ? r->GetSSU2V6Address () : r->GetSSU2V4Address (); + if (addr) { if (m_Server.IsSupported (ep.address ())) { - addr = ep.address ().is_v6 () ? r->GetSSU2V6Address () : r->GetSSU2V4Address (); - if (!addr) - { - LogPrint (eLogWarning, "SSU2: RelayIntro address for endpoint not found"); - code = eSSU2RelayResponseCodeCharlieAliceIsUnknown; - } + token = m_Server.GetIncomingToken (ep); + isV4 = ep.address ().is_v4 (); + SendHolePunch (bufbe32toh (buf + 33), ep, addr->i, token); + m_Server.AddConnectedRecently (ep, mts/1000); } else { LogPrint (eLogWarning, "SSU2: RelayIntro unsupported address"); code = eSSU2RelayResponseCodeCharlieUnsupportedAddress; - } + } } else { - LogPrint (eLogWarning, "SSU2: RelayIntro invalid endpoint"); + LogPrint (eLogWarning, "SSU2: RelayIntro unknown address"); code = eSSU2RelayResponseCodeCharlieAliceIsUnknown; } } @@ -2080,7 +2051,7 @@ namespace transport auto vec = std::make_shared >(len); memcpy (vec->data (), buf, len); auto s = shared_from_this (); - boost::asio::post (m_Server.GetService (), [s, vec, attempts]() + m_Server.GetService ().post ([s, vec, attempts]() { LogPrint (eLogDebug, "SSU2: RelayIntro attempt ", attempts + 1); s->HandleRelayIntro (vec->data (), vec->size (), attempts + 1); @@ -2094,34 +2065,18 @@ namespace transport } // send relay response to Bob auto packet = m_Server.GetSentPacketsPool ().AcquireShared (); - uint32_t nonce = bufbe32toh (buf + 33); packet->payloadSize = CreateRelayResponseBlock (packet->payload, m_MaxPayloadSize, - code, nonce, m_Server.GetIncomingToken (ep), ep.address ().is_v4 ()); - if (code == eSSU2RelayResponseCodeAccept && addr) - { - // send HolePunch - auto holePunchSession = std::make_shared(m_Server, nonce, ep, addr); - if (m_Server.AddSession (holePunchSession)) - holePunchSession->SendHolePunch (packet->payload, packet->payloadSize); // relay response block - else - { - LogPrint (eLogInfo, "SSU2: Relay intro nonce ", nonce, " already exists. Ignore"); - return; - } - } + code, bufbe32toh (buf + 33), token, isV4); packet->payloadSize += CreatePaddingBlock (packet->payload + packet->payloadSize, m_MaxPayloadSize - packet->payloadSize); - uint32_t packetNum = SendData (packet->payload, packet->payloadSize); - if (m_RemoteVersion >= SSU2_MIN_RELAY_RESPONSE_RESEND_VERSION) - { - // sometimes Bob doesn't ack this RelayResponse in older versions - packet->sendTime = i2p::util::GetMillisecondsSinceEpoch (); - m_SentPackets.emplace (packetNum, packet); - } + /*uint32_t packetNum = */SendData (packet->payload, packet->payloadSize); + // sometimes Bob doesn't ack this RelayResponse + // TODO: uncomment line below once the problem is resolved + //packet->sendTime = mts; + //m_SentPackets.emplace (packetNum, packet); } void SSU2Session::HandleRelayResponse (const uint8_t * buf, size_t len) { - if (len < 6) return; uint32_t nonce = bufbe32toh (buf + 2); if (m_State == eSSU2SessionStateIntroduced) { @@ -2142,9 +2097,7 @@ namespace transport auto it = m_RelaySessions.find (nonce); if (it != m_RelaySessions.end ()) { - auto relaySession = it->second.first; - m_RelaySessions.erase (it); - if (relaySession && relaySession->IsEstablished ()) + if (it->second.first && it->second.first->IsEstablished ()) { // we are Bob, message from Charlie auto packet = m_Server.GetSentPacketsPool ().AcquireShared (); @@ -2154,13 +2107,11 @@ namespace transport memcpy (payload + 3, buf, len); // forward to Alice as is packet->payloadSize = len + 3; packet->payloadSize += CreatePaddingBlock (payload + packet->payloadSize, m_MaxPayloadSize - packet->payloadSize); - uint32_t packetNum = relaySession->SendData (packet->payload, packet->payloadSize); - if (m_RemoteVersion >= SSU2_MIN_RELAY_RESPONSE_RESEND_VERSION) - { - // sometimes Alice doesn't ack this RelayResponse in older versions - packet->sendTime = i2p::util::GetMillisecondsSinceEpoch (); - relaySession->m_SentPackets.emplace (packetNum, packet); - } + /*uint32_t packetNum = */it->second.first->SendData (packet->payload, packet->payloadSize); + // sometimes Alice doesn't ack this RelayResponse + // TODO: uncomment line below once the problem is resolved + //packet->sendTime = i2p::util::GetMillisecondsSinceEpoch (); + //it->second.first->m_SentPackets.emplace (packetNum, packet); } else { @@ -2168,31 +2119,25 @@ namespace transport if (!buf[1]) // status code accepted? { // verify signature - uint8_t csz = (len >= 12) ? buf[11] : 0; - if (csz + 12 + relaySession->GetRemoteIdentity ()->GetSignatureLen () > len) - { - LogPrint (eLogWarning, "SSU2: Malformed RelayResponse len=", len); - relaySession->Done (); - return; - } - SignedData<128> s; + uint8_t csz = buf[11]; + SignedData s; s.Insert ((const uint8_t *)"RelayAgreementOK", 16); // prologue s.Insert (GetRemoteIdentity ()->GetIdentHash (), 32); // bhash s.Insert (buf + 2, 10 + csz); // nonce, timestamp, ver, csz and Charlie's endpoint - if (s.Verify (relaySession->GetRemoteIdentity (), buf + 12 + csz)) + if (s.Verify (it->second.first->GetRemoteIdentity (), buf + 12 + csz)) { - if (relaySession->m_State == eSSU2SessionStateIntroduced) // HolePunch not received yet + if (it->second.first->m_State == eSSU2SessionStateIntroduced) // HolePunch not received yet { // update Charlie's endpoint - if (ExtractEndpoint (buf + 12, csz, relaySession->m_RemoteEndpoint)) + if (ExtractEndpoint (buf + 12, csz, it->second.first->m_RemoteEndpoint)) { // update token uint64_t token; memcpy (&token, buf + len - 8, 8); - m_Server.UpdateOutgoingToken (relaySession->m_RemoteEndpoint, + m_Server.UpdateOutgoingToken (it->second.first->m_RemoteEndpoint, token, i2p::util::GetSecondsSinceEpoch () + SSU2_TOKEN_EXPIRATION_TIMEOUT); // connect to Charlie, HolePunch will be ignored - relaySession->ConnectAfterIntroduction (); + it->second.first->ConnectAfterIntroduction (); } else LogPrint (eLogWarning, "SSU2: RelayResponse can't extract endpoint"); @@ -2201,15 +2146,16 @@ namespace transport else { LogPrint (eLogWarning, "SSU2: RelayResponse signature verification failed"); - relaySession->Done (); + it->second.first->Done (); } } else { LogPrint (eLogInfo, "SSU2: RelayResponse status code=", (int)buf[1], " nonce=", bufbe32toh (buf + 2)); - relaySession->Done (); + it->second.first->Done (); } } + m_RelaySessions.erase (it); } else LogPrint (eLogDebug, "SSU2: RelayResponse unknown nonce ", bufbe32toh (buf + 2)); @@ -2233,33 +2179,29 @@ namespace transport GetRemoteIdentity ()->GetIdentHash ()); if (session) // session with Charlie { - if (m_Server.AddPeerTest (nonce, shared_from_this (), ts/1000)) - { - auto packet = m_Server.GetSentPacketsPool ().AcquireShared (); - // Alice's RouterInfo - auto r = i2p::data::netdb.FindRouter (GetRemoteIdentity ()->GetIdentHash ()); - if (r && (r->IsUnreachable () || !i2p::data::netdb.PopulateRouterInfoBuffer (r))) r = nullptr; - packet->payloadSize = r ? CreateRouterInfoBlock (packet->payload, m_MaxPayloadSize - len - 32, r) : 0; - if (!packet->payloadSize && r) - session->SendFragmentedMessage (CreateDatabaseStoreMsg (r)); - if (packet->payloadSize + len + 48 > m_MaxPayloadSize) - { - // doesn't fit one message, send RouterInfo in separate message - uint32_t packetNum = session->SendData (packet->payload, packet->payloadSize, SSU2_FLAG_IMMEDIATE_ACK_REQUESTED); - packet->sendTime = ts; - session->m_SentPackets.emplace (packetNum, packet); - packet = m_Server.GetSentPacketsPool ().AcquireShared (); // new packet - } - // PeerTest to Charlie - packet->payloadSize += CreatePeerTestBlock (packet->payload + packet->payloadSize, m_MaxPayloadSize - packet->payloadSize, 2, - eSSU2PeerTestCodeAccept, GetRemoteIdentity ()->GetIdentHash (), buf + offset, len - offset); - packet->payloadSize += CreatePaddingBlock (packet->payload + packet->payloadSize, m_MaxPayloadSize - packet->payloadSize); + m_Server.AddPeerTest (nonce, shared_from_this (), ts/1000); + auto packet = m_Server.GetSentPacketsPool ().AcquireShared (); + // Alice's RouterInfo + auto r = i2p::data::netdb.FindRouter (GetRemoteIdentity ()->GetIdentHash ()); + if (r && (r->IsUnreachable () || !i2p::data::netdb.PopulateRouterInfoBuffer (r))) r = nullptr; + packet->payloadSize = r ? CreateRouterInfoBlock (packet->payload, m_MaxPayloadSize - len - 32, r) : 0; + if (!packet->payloadSize && r) + session->SendFragmentedMessage (CreateDatabaseStoreMsg (r)); + if (packet->payloadSize + len + 48 > m_MaxPayloadSize) + { + // doesn't fit one message, send RouterInfo in separate message uint32_t packetNum = session->SendData (packet->payload, packet->payloadSize, SSU2_FLAG_IMMEDIATE_ACK_REQUESTED); packet->sendTime = ts; session->m_SentPackets.emplace (packetNum, packet); + packet = m_Server.GetSentPacketsPool ().AcquireShared (); // new packet } - else - LogPrint (eLogInfo, "SSU2: Peer test 1 nonce ", nonce, " already exists. Ignored"); + // PeerTest to Charlie + packet->payloadSize += CreatePeerTestBlock (packet->payload + packet->payloadSize, m_MaxPayloadSize - packet->payloadSize, 2, + eSSU2PeerTestCodeAccept, GetRemoteIdentity ()->GetIdentHash (), buf + offset, len - offset); + packet->payloadSize += CreatePaddingBlock (packet->payload + packet->payloadSize, m_MaxPayloadSize - packet->payloadSize); + uint32_t packetNum = session->SendData (packet->payload, packet->payloadSize, SSU2_FLAG_IMMEDIATE_ACK_REQUESTED); + packet->sendTime = ts; + session->m_SentPackets.emplace (packetNum, packet); } else { @@ -2278,13 +2220,10 @@ namespace transport case 2: // Charlie from Bob { // sign with Charlie's key - if (len < offset + 9) return; uint8_t asz = buf[offset + 9]; - size_t l = asz + 10 + i2p::context.GetIdentity ()->GetSignatureLen (); - if (len < offset + l) return; - std::vector newSignedData (l); + std::vector newSignedData (asz + 10 + i2p::context.GetIdentity ()->GetSignatureLen ()); memcpy (newSignedData.data (), buf + offset, asz + 10); - SignedData<128> s; + SignedData s; s.Insert ((const uint8_t *)"PeerTestValidate", 16); // prologue s.Insert (GetRemoteIdentity ()->GetIdentHash (), 32); // bhash s.Insert (buf + 3, 32); // ahash @@ -2392,16 +2331,10 @@ namespace transport if (GetRouterStatus () == eRouterStatusUnknown) SetTestingState (true); auto r = i2p::data::netdb.FindRouter (buf + 3); // find Charlie - if (r && len >= offset + 9) + if (r) { uint8_t asz = buf[offset + 9]; - if (len < offset + asz + 10 + r->GetIdentity ()->GetSignatureLen ()) - { - LogPrint (eLogWarning, "Malformed PeerTest 4 len=", len); - session->Done (); - return; - } - SignedData<128> s; + SignedData s; s.Insert ((const uint8_t *)"PeerTestValidate", 16); // prologue s.Insert (GetRemoteIdentity ()->GetIdentHash (), 32); // bhash s.Insert (i2p::context.GetIdentity ()->GetIdentHash (), 32); // ahash @@ -2410,16 +2343,16 @@ namespace transport { session->SetRemoteIdentity (r->GetIdentity ()); auto addr = r->GetSSU2Address (m_Address->IsV4 ()); - if (addr && addr->IsPeerTesting ()) + if (addr) { if (session->GetMsgNumReceived () >= 5) { - // msg 5 already received and we know remote endpoint + // msg 5 already received if (session->GetMsgNumReceived () == 5) { if (!session->IsConnectedRecently ()) SetRouterStatus (eRouterStatusOK); - // send msg 6 immeditely + // send msg 6 session->SendPeerTest (6, buf + offset, len - offset, addr); } else @@ -2430,12 +2363,6 @@ namespace transport session->m_Address = addr; if (GetTestingState ()) { - // schedule msg 6 with delay - if (!addr->host.is_unspecified () && addr->port) - { - session->SetRemoteEndpoint (boost::asio::ip::udp::endpoint (addr->host, addr->port)); - session->SendPeerTest (6, buf + offset, len - offset, addr, true); - } SetTestingState (false); if (GetRouterStatus () != eRouterStatusFirewalled && addr->IsPeerTesting ()) { @@ -2453,7 +2380,7 @@ namespace transport } else { - LogPrint (eLogWarning, "SSU2: Peer test 4 address not found or not supported"); + LogPrint (eLogWarning, "SSU2: Peer test 4 address not found"); session->Done (); } } @@ -2560,19 +2487,17 @@ namespace transport return nullptr; } - void SSU2Session::AdjustMaxPayloadSize (size_t maxMtu) + void SSU2Session::AdjustMaxPayloadSize () { auto addr = FindLocalAddress (); if (addr && addr->ssu) { int mtu = addr->ssu->mtu; if (!mtu && addr->IsV4 ()) mtu = SSU2_MAX_PACKET_SIZE; - if (mtu > (int)maxMtu) mtu = maxMtu; if (m_Address && m_Address->ssu && (!mtu || m_Address->ssu->mtu < mtu)) mtu = m_Address->ssu->mtu; if (mtu) { - if (mtu > (int)SSU2_MAX_PACKET_SIZE) mtu = SSU2_MAX_PACKET_SIZE; if (mtu < (int)SSU2_MIN_PACKET_SIZE) mtu = SSU2_MIN_PACKET_SIZE; m_MaxPayloadSize = mtu - (addr->IsV6 () ? IPV6_HEADER_SIZE: IPV4_HEADER_SIZE) - UDP_HEADER_SIZE - 32; LogPrint (eLogDebug, "SSU2: Session MTU=", mtu, ", max payload size=", m_MaxPayloadSize); @@ -2671,17 +2596,17 @@ namespace transport size_t SSU2Session::CreateAckBlock (uint8_t * buf, size_t len) { if (len < 8) return 0; + int maxNumRanges = (len - 8) >> 1; + if (maxNumRanges > SSU2_MAX_NUM_ACK_RANGES) maxNumRanges = SSU2_MAX_NUM_ACK_RANGES; buf[0] = eSSU2BlkAck; uint32_t ackThrough = m_OutOfSequencePackets.empty () ? m_ReceivePacketNum : *m_OutOfSequencePackets.rbegin (); htobe32buf (buf + 3, ackThrough); // Ack Through uint16_t acnt = 0; + int numRanges = 0; if (ackThrough) { if (m_OutOfSequencePackets.empty ()) - { acnt = std::min ((int)ackThrough, SSU2_MAX_NUM_ACNT); // no gaps - m_NumRanges = 0; - } else { auto it = m_OutOfSequencePackets.rbegin (); it++; // prev packet num @@ -2694,102 +2619,93 @@ namespace transport it++; } // ranges - if (!m_NumRanges) - { - int maxNumRanges = (len - 8) >> 1; - if (maxNumRanges > SSU2_MAX_NUM_ACK_RANGES) maxNumRanges = SSU2_MAX_NUM_ACK_RANGES; - int numRanges = 0; - uint32_t lastNum = ackThrough - acnt; - if (acnt > SSU2_MAX_NUM_ACNT) + uint32_t lastNum = ackThrough - acnt; + if (acnt > SSU2_MAX_NUM_ACNT) + { + auto d = std::div (acnt - SSU2_MAX_NUM_ACNT, SSU2_MAX_NUM_ACNT); + acnt = SSU2_MAX_NUM_ACNT; + if (d.quot > maxNumRanges) { - auto d = std::div (acnt - SSU2_MAX_NUM_ACNT, SSU2_MAX_NUM_ACNT); - acnt = SSU2_MAX_NUM_ACNT; - if (d.quot > maxNumRanges) - { - d.quot = maxNumRanges; - d.rem = 0; - } - // Acks only ranges for acnt - for (int i = 0; i < d.quot; i++) - { - m_Ranges[numRanges*2] = 0; m_Ranges[numRanges*2 + 1] = SSU2_MAX_NUM_ACNT; // NACKs 0, Acks 255 - numRanges++; - } - if (d.rem > 0) - { - m_Ranges[numRanges*2] = 0; m_Ranges[numRanges*2 + 1] = d.rem; - numRanges++; - } + d.quot = maxNumRanges; + d.rem = 0; } - int numPackets = acnt + numRanges*SSU2_MAX_NUM_ACNT; - while (it != m_OutOfSequencePackets.rend () && - numRanges < maxNumRanges && numPackets < SSU2_MAX_NUM_ACK_PACKETS) + // Acks only ranges for acnt + for (int i = 0; i < d.quot; i++) { - if (lastNum - (*it) > SSU2_MAX_NUM_ACNT) - { - // NACKs only ranges - if (lastNum > (*it) + SSU2_MAX_NUM_ACNT*(maxNumRanges - numRanges)) break; // too many NACKs - while (lastNum - (*it) > SSU2_MAX_NUM_ACNT) - { - m_Ranges[numRanges*2] = SSU2_MAX_NUM_ACNT; m_Ranges[numRanges*2 + 1] = 0; // NACKs 255, Acks 0 - lastNum -= SSU2_MAX_NUM_ACNT; - numRanges++; - numPackets += SSU2_MAX_NUM_ACNT; - } - } - // NACKs and Acks ranges - m_Ranges[numRanges*2] = lastNum - (*it) - 1; // NACKs - numPackets += m_Ranges[numRanges*2]; - lastNum = *it; it++; - int numAcks = 1; - while (it != m_OutOfSequencePackets.rend () && lastNum > 0 && *it == lastNum - 1) - { - numAcks++; lastNum--; - it++; - } - while (numAcks > SSU2_MAX_NUM_ACNT) - { - // Acks only ranges - m_Ranges[numRanges*2 + 1] = SSU2_MAX_NUM_ACNT; // Acks 255 - numAcks -= SSU2_MAX_NUM_ACNT; - numRanges++; - numPackets += SSU2_MAX_NUM_ACNT; - m_Ranges[numRanges*2] = 0; // NACKs 0 - if (numRanges >= maxNumRanges || numPackets >= SSU2_MAX_NUM_ACK_PACKETS) break; - } - if (numAcks > SSU2_MAX_NUM_ACNT) numAcks = SSU2_MAX_NUM_ACNT; - m_Ranges[numRanges*2 + 1] = (uint8_t)numAcks; // Acks - numPackets += numAcks; + buf[8 + numRanges*2] = 0; buf[8 + numRanges*2 + 1] = SSU2_MAX_NUM_ACNT; // NACKs 0, Acks 255 numRanges++; } - if (it == m_OutOfSequencePackets.rend () && - numRanges < maxNumRanges && numPackets < SSU2_MAX_NUM_ACK_PACKETS) + if (d.rem > 0) { - // add range between out-of-sequence and received - int nacks = *m_OutOfSequencePackets.begin () - m_ReceivePacketNum - 1; - if (nacks > 0) + buf[8 + numRanges*2] = 0; buf[8 + numRanges*2 + 1] = d.rem; + numRanges++; + } + } + int numPackets = acnt + numRanges*SSU2_MAX_NUM_ACNT; + while (it != m_OutOfSequencePackets.rend () && + numRanges < maxNumRanges && numPackets < SSU2_MAX_NUM_ACK_PACKETS) + { + if (lastNum - (*it) > SSU2_MAX_NUM_ACNT) + { + // NACKs only ranges + if (lastNum > (*it) + SSU2_MAX_NUM_ACNT*(maxNumRanges - numRanges)) break; // too many NACKs + while (lastNum - (*it) > SSU2_MAX_NUM_ACNT) { - if (nacks > SSU2_MAX_NUM_ACNT) nacks = SSU2_MAX_NUM_ACNT; - m_Ranges[numRanges*2] = nacks; - m_Ranges[numRanges*2 + 1] = std::min ((int)m_ReceivePacketNum + 1, SSU2_MAX_NUM_ACNT); + buf[8 + numRanges*2] = SSU2_MAX_NUM_ACNT; buf[8 + numRanges*2 + 1] = 0; // NACKs 255, Acks 0 + lastNum -= SSU2_MAX_NUM_ACNT; numRanges++; + numPackets += SSU2_MAX_NUM_ACNT; } } - m_NumRanges = numRanges; - } - if (m_NumRanges) - memcpy (buf + 8, m_Ranges, m_NumRanges*2); + // NACKs and Acks ranges + buf[8 + numRanges*2] = lastNum - (*it) - 1; // NACKs + numPackets += buf[8 + numRanges*2]; + lastNum = *it; it++; + int numAcks = 1; + while (it != m_OutOfSequencePackets.rend () && lastNum > 0 && *it == lastNum - 1) + { + numAcks++; lastNum--; + it++; + } + while (numAcks > SSU2_MAX_NUM_ACNT) + { + // Acks only ranges + buf[8 + numRanges*2 + 1] = SSU2_MAX_NUM_ACNT; // Acks 255 + numAcks -= SSU2_MAX_NUM_ACNT; + numRanges++; + numPackets += SSU2_MAX_NUM_ACNT; + buf[8 + numRanges*2] = 0; // NACKs 0 + if (numRanges >= maxNumRanges || numPackets >= SSU2_MAX_NUM_ACK_PACKETS) break; + } + if (numAcks > SSU2_MAX_NUM_ACNT) numAcks = SSU2_MAX_NUM_ACNT; + buf[8 + numRanges*2 + 1] = (uint8_t)numAcks; // Acks + numPackets += numAcks; + numRanges++; + } + if (it == m_OutOfSequencePackets.rend () && + numRanges < maxNumRanges && numPackets < SSU2_MAX_NUM_ACK_PACKETS) + { + // add range between out-of-sequence and received + int nacks = *m_OutOfSequencePackets.begin () - m_ReceivePacketNum - 1; + if (nacks > 0) + { + if (nacks > SSU2_MAX_NUM_ACNT) nacks = SSU2_MAX_NUM_ACNT; + buf[8 + numRanges*2] = nacks; + buf[8 + numRanges*2 + 1] = std::min ((int)m_ReceivePacketNum + 1, SSU2_MAX_NUM_ACNT); + numRanges++; + } + } } } buf[7] = (uint8_t)acnt; // acnt - htobe16buf (buf + 1, 5 + m_NumRanges*2); - return 8 + m_NumRanges*2; + htobe16buf (buf + 1, 5 + numRanges*2); + return 8 + numRanges*2; } size_t SSU2Session::CreatePaddingBlock (uint8_t * buf, size_t len, size_t minSize) { if (len < 3 || len < minSize) return 0; - size_t paddingSize = m_Server.GetRng ()() & 0x1F; // 0 - 31 + size_t paddingSize = m_Server.GetRng ()() & 0x0F; // 0 - 15 if (paddingSize + 3 > len) paddingSize = len - 3; else if (paddingSize + 3 < minSize) paddingSize = minSize - 3; buf[0] = eSSU2BlkPadding; @@ -2891,7 +2807,7 @@ namespace transport LogPrint (eLogError, "SSU2: Buffer for RelayResponse signature is too small ", len); return 0; } - SignedData<128> s; + SignedData s; s.Insert ((const uint8_t *)"RelayAgreementOK", 16); // prologue if (code == eSSU2RelayResponseCodeAccept || code >= 64) // Charlie s.Insert (GetRemoteIdentity ()->GetIdentHash (), 32); // bhash @@ -2953,7 +2869,7 @@ namespace transport size_t asz = CreateEndpoint (signedData + 10, 86, boost::asio::ip::udp::endpoint (localAddress->host, localAddress->port)); signedData[9] = asz; // signature - SignedData<128> s; + SignedData s; s.Insert ((const uint8_t *)"PeerTestValidate", 16); // prologue s.Insert (GetRemoteIdentity ()->GetIdentHash (), 32); // bhash s.Insert (signedData, 10 + asz); // ver, nonce, ts, asz, Alice's endpoint @@ -3017,17 +2933,11 @@ namespace transport } m_OutOfSequencePackets.erase (m_OutOfSequencePackets.begin (), it); } - m_NumRanges = 0; // recalculate ranges when create next Ack } m_ReceivePacketNum = packetNum; } else - { - if (m_NumRanges && (m_OutOfSequencePackets.empty () || - packetNum != (*m_OutOfSequencePackets.rbegin ()) + 1)) - m_NumRanges = 0; // reset ranges if received packet is not next m_OutOfSequencePackets.insert (packetNum); - } return true; } @@ -3058,67 +2968,38 @@ namespace transport void SSU2Session::SendPathResponse (const uint8_t * data, size_t len) { - uint8_t payload[SSU2_MAX_PACKET_SIZE]; - size_t payloadSize = 0; - // datetime block - payload[0] = eSSU2BlkDateTime; - htobe16buf (payload + 1, 4); - htobe32buf (payload + 3, (i2p::util::GetMillisecondsSinceEpoch () + 500)/1000); - payloadSize += 7; - // address block - payloadSize += CreateAddressBlock (payload + payloadSize, m_MaxPayloadSize - payloadSize, m_RemoteEndpoint); - // path response - if (payloadSize + len > m_MaxPayloadSize) + if (len > m_MaxPayloadSize - 3) { LogPrint (eLogWarning, "SSU2: Incorrect data size for path response ", len); return; - } - payload[payloadSize] = eSSU2BlkPathResponse; - htobe16buf (payload + payloadSize + 1, len); - memcpy (payload + payloadSize + 3, data, len); - payloadSize += len + 3; - // ack block + } + uint8_t payload[SSU2_MAX_PACKET_SIZE]; + payload[0] = eSSU2BlkPathResponse; + htobe16buf (payload + 1, len); + memcpy (payload + 3, data, len); + size_t payloadSize = len + 3; if (payloadSize < m_MaxPayloadSize) - payloadSize += CreateAckBlock (payload + payloadSize, m_MaxPayloadSize - payloadSize); - // padding - if (payloadSize < m_MaxPayloadSize) - payloadSize += CreatePaddingBlock (payload + payloadSize, m_MaxPayloadSize - payloadSize); + payloadSize += CreatePaddingBlock (payload + payloadSize, m_MaxPayloadSize - payloadSize, payloadSize < 8 ? 8 : 0); SendData (payload, payloadSize); } - void SSU2Session::SendPathChallenge (const boost::asio::ip::udp::endpoint& to) + void SSU2Session::SendPathChallenge () { - AdjustMaxPayloadSize (SSU2_MIN_PACKET_SIZE); // reduce to minimum - m_WindowSize = SSU2_MIN_WINDOW_SIZE; // reduce window to minimum - uint8_t payload[SSU2_MAX_PACKET_SIZE]; - size_t payloadSize = 0; - // datetime block - payload[0] = eSSU2BlkDateTime; - htobe16buf (payload + 1, 4); - htobe32buf (payload + 3, (i2p::util::GetMillisecondsSinceEpoch () + 500)/1000); - payloadSize += 7; - // address block with new address - payloadSize += CreateAddressBlock (payload + payloadSize, m_MaxPayloadSize - payloadSize, to); - // path challenge block - payload[payloadSize] = eSSU2BlkPathChallenge; - uint64_t challenge; - RAND_bytes ((uint8_t *)&challenge, 8); - htobe16buf (payload + payloadSize + 1, 8); // always 8 bytes - htobuf64 (payload + payloadSize + 3, challenge); - payloadSize += 11; - m_PathChallenge = std::make_unique >(challenge, to); - // ack block - if (payloadSize < m_MaxPayloadSize) - payloadSize += CreateAckBlock (payload + payloadSize, m_MaxPayloadSize - payloadSize); - // padding block - if (payloadSize < m_MaxPayloadSize) - payloadSize += CreatePaddingBlock (payload + payloadSize, m_MaxPayloadSize - payloadSize); - // send to new endpoint - auto existing = m_RemoteEndpoint; - m_RemoteEndpoint = to; // send path challenge to new endpoint - SendData (payload, payloadSize); - m_RemoteEndpoint = existing; // restore endpoint back until path response received + payload[0] = eSSU2BlkPathChallenge; + size_t len = m_Server.GetRng ()() % (m_MaxPayloadSize - 3); + htobe16buf (payload + 1, len); + if (len > 0) + { + RAND_bytes (payload + 3, len); + i2p::data::IdentHash * hash = new i2p::data::IdentHash (); + SHA256 (payload + 3, len, *hash); + m_PathChallenge.reset (hash); + } + len += 3; + if (len < m_MaxPayloadSize) + len += CreatePaddingBlock (payload + len, m_MaxPayloadSize - len, len < 8 ? 8 : 0); + SendData (payload, len); } void SSU2Session::CleanUp (uint64_t ts) @@ -3181,7 +3062,7 @@ namespace transport { if (ts > it->second.second + SSU2_RELAY_NONCE_EXPIRATION_TIMEOUT) { - LogPrint (eLogInfo, "SSU2: Relay nonce ", it->first, " was not responded in ", SSU2_RELAY_NONCE_EXPIRATION_TIMEOUT, " seconds, deleted"); + LogPrint (eLogWarning, "SSU2: Relay nonce ", it->first, " was not responded in ", SSU2_RELAY_NONCE_EXPIRATION_TIMEOUT, " seconds, deleted"); it = m_RelaySessions.erase (it); } else @@ -3206,9 +3087,215 @@ namespace transport Resend (i2p::util::GetMillisecondsSinceEpoch ()); // than right time to resend } - i2p::data::RouterInfo::SupportedTransports SSU2Session::GetTransportType () const + SSU2PeerTestSession::SSU2PeerTestSession (SSU2Server& server, uint64_t sourceConnID, uint64_t destConnID): + SSU2Session (server, nullptr, nullptr, false), + m_MsgNumReceived (0), m_NumResends (0),m_IsConnectedRecently (false), m_IsStatusChanged (false), + m_PeerTestResendTimer (server.GetService ()) { - return m_RemoteEndpoint.address ().is_v4 () ? i2p::data::RouterInfo::eSSU2V4 : i2p::data::RouterInfo::eSSU2V6; + if (!sourceConnID) sourceConnID = ~destConnID; + if (!destConnID) destConnID = ~sourceConnID; + SetSourceConnID (sourceConnID); + SetDestConnID (destConnID); + SetState (eSSU2SessionStatePeerTest); + SetTerminationTimeout (SSU2_PEER_TEST_EXPIRATION_TIMEOUT); + } + + bool SSU2PeerTestSession::ProcessPeerTest (uint8_t * buf, size_t len) + { + // we are Alice or Charlie, msgs 5,6,7 + Header header; + memcpy (header.buf, buf, 16); + header.ll[0] ^= CreateHeaderMask (i2p::context.GetSSU2IntroKey (), buf + (len - 24)); + header.ll[1] ^= CreateHeaderMask (i2p::context.GetSSU2IntroKey (), buf + (len - 12)); + if (header.h.type != eSSU2PeerTest) + { + LogPrint (eLogWarning, "SSU2: Unexpected message type ", (int)header.h.type, " instead ", (int)eSSU2PeerTest); + return false; + } + if (len < 48) + { + LogPrint (eLogWarning, "SSU2: PeerTest message too short ", len); + return false; + } + uint8_t nonce[12] = {0}; + uint64_t headerX[2]; // sourceConnID, token + i2p::crypto::ChaCha20 (buf + 16, 16, i2p::context.GetSSU2IntroKey (), nonce, (uint8_t *)headerX); + SetDestConnID (headerX[0]); + // decrypt and handle payload + uint8_t * payload = buf + 32; + CreateNonce (be32toh (header.h.packetNum), nonce); + uint8_t h[32]; + memcpy (h, header.buf, 16); + memcpy (h + 16, &headerX, 16); + if (!i2p::crypto::AEADChaCha20Poly1305 (payload, len - 48, h, 32, + i2p::context.GetSSU2IntroKey (), nonce, payload, len - 48, false)) + { + LogPrint (eLogWarning, "SSU2: PeerTest AEAD verification failed "); + return false; + } + HandlePayload (payload, len - 48); + SetIsDataReceived (false); + return true; + } + + void SSU2PeerTestSession::HandlePeerTest (const uint8_t * buf, size_t len) + { + // msgs 5-7 + if (len < 8) return; + uint8_t msg = buf[0]; + if (msg <= m_MsgNumReceived) + { + LogPrint (eLogDebug, "SSU2: PeerTest msg num ", msg, " received after ", m_MsgNumReceived, ". Ignored"); + return; + } + size_t offset = 3; // points to signed data after msg + code + flag + uint32_t nonce = bufbe32toh (buf + offset + 1); // 1 - ver + switch (msg) // msg + { + case 5: // Alice from Charlie 1 + { + if (htobe64 (((uint64_t)nonce << 32) | nonce) == GetSourceConnID ()) + { + m_IsConnectedRecently = GetServer ().IsConnectedRecently (GetRemoteEndpoint ()); + if (GetAddress ()) + { + if (!m_IsConnectedRecently) + SetRouterStatus (eRouterStatusOK); + else if (m_IsStatusChanged && GetRouterStatus () == eRouterStatusFirewalled) + SetRouterStatus (eRouterStatusUnknown); + SendPeerTest (6, buf + offset, len - offset); + } + } + else + LogPrint (eLogWarning, "SSU2: Peer test 5 nonce mismatch ", nonce, " connID=", GetSourceConnID ()); + break; + } + case 6: // Charlie from Alice + { + m_PeerTestResendTimer.cancel (); // no more msg 5 resends + if (GetAddress ()) + SendPeerTest (7, buf + offset, len - offset); + else + LogPrint (eLogWarning, "SSU2: Unknown address for peer test 6"); + GetServer ().AddConnectedRecently (GetRemoteEndpoint (), i2p::util::GetSecondsSinceEpoch ()); + GetServer ().RequestRemoveSession (GetConnID ()); + break; + } + case 7: // Alice from Charlie 2 + { + m_PeerTestResendTimer.cancel (); // no more msg 6 resends + auto addr = GetAddress (); + if (addr && addr->IsV6 ()) + i2p::context.SetStatusV6 (eRouterStatusOK); // set status OK for ipv6 even if from SSU2 + GetServer ().AddConnectedRecently (GetRemoteEndpoint (), i2p::util::GetSecondsSinceEpoch ()); + GetServer ().RequestRemoveSession (GetConnID ()); + break; + } + default: + LogPrint (eLogWarning, "SSU2: PeerTest unexpected msg num ", msg); + return; + } + m_MsgNumReceived = msg; + } + + void SSU2PeerTestSession::SendPeerTest (uint8_t msg) + { + auto addr = GetAddress (); + if (!addr) return; + Header header; + uint8_t h[32], payload[SSU2_MAX_PACKET_SIZE]; + // fill packet + header.h.connID = GetDestConnID (); // dest id + RAND_bytes (header.buf + 8, 4); // random packet num + header.h.type = eSSU2PeerTest; + header.h.flags[0] = 2; // ver + header.h.flags[1] = (uint8_t)i2p::context.GetNetID (); // netID + header.h.flags[2] = 0; // flag + memcpy (h, header.buf, 16); + htobuf64 (h + 16, GetSourceConnID ()); // source id + // payload + payload[0] = eSSU2BlkDateTime; + htobe16buf (payload + 1, 4); + htobe32buf (payload + 3, (i2p::util::GetMillisecondsSinceEpoch () + 500)/1000); + size_t payloadSize = 7; + if (msg == 6 || msg == 7) + payloadSize += CreateAddressBlock (payload + payloadSize, GetMaxPayloadSize () - payloadSize, GetRemoteEndpoint ()); + payloadSize += CreatePeerTestBlock (payload + payloadSize, GetMaxPayloadSize () - payloadSize, + msg, eSSU2PeerTestCodeAccept, nullptr, m_SignedData.data (), m_SignedData.size ()); + payloadSize += CreatePaddingBlock (payload + payloadSize, GetMaxPayloadSize () - payloadSize); + // encrypt + uint8_t n[12]; + CreateNonce (be32toh (header.h.packetNum), n); + i2p::crypto::AEADChaCha20Poly1305 (payload, payloadSize, h, 32, addr->i, n, payload, payloadSize + 16, true); + payloadSize += 16; + header.ll[0] ^= CreateHeaderMask (addr->i, payload + (payloadSize - 24)); + header.ll[1] ^= CreateHeaderMask (addr->i, payload + (payloadSize - 12)); + memset (n, 0, 12); + i2p::crypto::ChaCha20 (h + 16, 16, addr->i, n, h + 16); + // send + GetServer ().Send (header.buf, 16, h + 16, 16, payload, payloadSize, GetRemoteEndpoint ()); + } + + void SSU2PeerTestSession::SendPeerTest (uint8_t msg, const uint8_t * signedData, size_t signedDataLen) + { +#if __cplusplus >= 202002L // C++20 + m_SignedData.assign (signedData, signedData + signedDataLen); +#else + m_SignedData.resize (signedDataLen); + memcpy (m_SignedData.data (), signedData, signedDataLen); +#endif + SendPeerTest (msg); + // schedule resend for msgs 5 or 6 + if (msg == 5 || msg == 6) + ScheduleResend (); + } + + void SSU2PeerTestSession::SendPeerTest (uint8_t msg, const uint8_t * signedData, size_t signedDataLen, + std::shared_ptr addr) + { + if (!addr) return; + SetAddress (addr); + SendPeerTest (msg, signedData, signedDataLen); + } + + void SSU2PeerTestSession::Connect () + { + LogPrint (eLogError, "SSU2: Can't connect peer test session"); + } + + bool SSU2PeerTestSession::ProcessFirstIncomingMessage (uint64_t connID, uint8_t * buf, size_t len) + { + LogPrint (eLogError, "SSU2: Can't handle incoming message in peer test session"); + return false; + } + + void SSU2PeerTestSession::ScheduleResend () + { + if (m_NumResends < SSU2_PEER_TEST_MAX_NUM_RESENDS) + { + m_PeerTestResendTimer.expires_from_now (boost::posix_time::milliseconds( + SSU2_PEER_TEST_RESEND_INTERVAL + GetServer ().GetRng ()() % SSU2_PEER_TEST_RESEND_INTERVAL_VARIANCE)); + std::weak_ptr s(std::static_pointer_cast(shared_from_this ())); + m_PeerTestResendTimer.async_wait ([s](const boost::system::error_code& ecode) + { + if (ecode != boost::asio::error::operation_aborted) + { + auto s1 = s.lock (); + if (s1) + { + int msg = 0; + if (s1->m_MsgNumReceived < 6) + msg = (s1->m_MsgNumReceived == 5) ? 6 : 5; + if (msg) // 5 or 6 + { + s1->SendPeerTest (msg); + s1->ScheduleResend (); + } + } + } + }); + m_NumResends++; + } } } } diff --git a/libi2pd/SSU2Session.h b/libi2pd/SSU2Session.h index ee26255f..49bd3be6 100644 --- a/libi2pd/SSU2Session.h +++ b/libi2pd/SSU2Session.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2022-2025, The PurpleI2P Project +* Copyright (c) 2022-2024, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -15,7 +15,6 @@ #include #include #include -#include "version.h" #include "Crypto.h" #include "RouterInfo.h" #include "RouterContext.h" @@ -56,7 +55,6 @@ namespace transport const int SSU2_MAX_NUM_ACK_RANGES = 32; // to send const uint8_t SSU2_MAX_NUM_FRAGMENTS = 64; const int SSU2_SEND_DATETIME_NUM_PACKETS = 256; - const int SSU2_MIN_RELAY_RESPONSE_RESEND_VERSION = MAKE_VERSION_NUMBER(0, 9, 64); // 0.9.64 // flags const uint8_t SSU2_FLAG_IMMEDIATE_ACK_REQUESTED = 0x01; @@ -114,7 +112,6 @@ namespace transport eSSU2SessionStateTerminated, eSSU2SessionStateFailed, eSSU2SessionStateIntroduced, - eSSU2SessionStateHolePunch, eSSU2SessionStatePeerTest, eSSU2SessionStateTokenRequestReceived }; @@ -261,13 +258,12 @@ namespace transport void FlushData (); void Done () override; void SendLocalRouterInfo (bool update) override; - void SendI2NPMessages (std::list >& msgs) override; + void SendI2NPMessages (const std::vector >& msgs) override; void MoveSendQueue (std::shared_ptr other); uint32_t GetRelayTag () const override { return m_RelayTag; }; size_t Resend (uint64_t ts); // return number of resent packets uint64_t GetLastResendTime () const { return m_LastResendTime; }; bool IsEstablished () const override { return m_State == eSSU2SessionStateEstablished; }; - i2p::data::RouterInfo::SupportedTransports GetTransportType () const override; uint64_t GetConnID () const { return m_SourceConnID; }; SSU2SessionState GetState () const { return m_State; }; void SetState (SSU2SessionState state) { m_State = state; }; @@ -299,8 +295,6 @@ namespace transport size_t CreateAddressBlock (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& ep); size_t CreatePaddingBlock (uint8_t * buf, size_t len, size_t minSize = 0); size_t CreatePeerTestBlock (uint8_t * buf, size_t len, uint8_t msg, SSU2PeerTestCode code, const uint8_t * routerHash, const uint8_t * signedData, size_t signedDataLen); - - bool ExtractEndpoint (const uint8_t * buf, size_t size, boost::asio::ip::udp::endpoint& ep); private: @@ -308,7 +302,7 @@ namespace transport void Established (); void ScheduleConnectTimer (); void HandleConnectTimer (const boost::system::error_code& ecode); - void PostI2NPMessages (); + void PostI2NPMessages (std::vector > msgs); bool SendQueue (); // returns true if ack block was sent bool SendFragmentedMessage (std::shared_ptr msg); void ResendHandshakePacket (); @@ -326,17 +320,19 @@ namespace transport uint32_t SendData (const uint8_t * buf, size_t len, uint8_t flags = 0); // returns packet num void SendQuickAck (); void SendTermination (); + void SendHolePunch (uint32_t nonce, const boost::asio::ip::udp::endpoint& ep, const uint8_t * introKey, uint64_t token); void SendPathResponse (const uint8_t * data, size_t len); - void SendPathChallenge (const boost::asio::ip::udp::endpoint& to); + void SendPathChallenge (); void HandleDateTime (const uint8_t * buf, size_t len); void HandleRouterInfo (const uint8_t * buf, size_t len); void HandleAck (const uint8_t * buf, size_t len); void HandleAckRange (uint32_t firstPacketNum, uint32_t lastPacketNum, uint64_t ts); - virtual void HandleAddress (const uint8_t * buf, size_t len); + void HandleAddress (const uint8_t * buf, size_t len); + bool ExtractEndpoint (const uint8_t * buf, size_t size, boost::asio::ip::udp::endpoint& ep); size_t CreateEndpoint (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& ep); std::shared_ptr FindLocalAddress () const; - void AdjustMaxPayloadSize (size_t maxMtu = SSU2_MAX_PACKET_SIZE); + void AdjustMaxPayloadSize (); bool GetTestingState () const; void SetTestingState(bool testing) const; std::shared_ptr ExtractRouterInfo (const uint8_t * buf, size_t size); @@ -357,7 +353,6 @@ namespace transport size_t CreateFollowOnFragmentBlock (uint8_t * buf, size_t len, std::shared_ptr msg, uint8_t& fragmentNum, uint32_t msgID); size_t CreateRelayIntroBlock (uint8_t * buf, size_t len, const uint8_t * introData, size_t introDataLen); size_t CreateRelayResponseBlock (uint8_t * buf, size_t len, SSU2RelayResponseCode code, uint32_t nonce, uint64_t token, bool v4); - size_t CreatePeerTestBlock (uint8_t * buf, size_t len, uint32_t nonce); // Alice size_t CreateTerminationBlock (uint8_t * buf, size_t len); @@ -371,7 +366,6 @@ namespace transport std::shared_ptr m_Address; boost::asio::ip::udp::endpoint m_RemoteEndpoint; i2p::data::RouterInfo::CompatibleTransports m_RemoteTransports, m_RemotePeerTestTransports; - int m_RemoteVersion; uint64_t m_DestConnID, m_SourceConnID; SSU2SessionState m_State; uint8_t m_KeyDataSend[64], m_KeyDataReceive[64]; @@ -382,8 +376,6 @@ namespace transport std::unordered_map, uint64_t > > m_RelaySessions; // nonce->(Alice, timestamp) for Bob or nonce->(Charlie, timestamp) for Alice std::list > m_SendQueue; i2p::I2NPMessagesHandler m_Handler; - std::list > m_IntermediateQueue; // from transports - mutable std::mutex m_IntermediateQueueMutex; bool m_IsDataReceived; double m_RTT; int m_MsgLocalExpirationTimeout; @@ -394,12 +386,47 @@ namespace transport boost::asio::deadline_timer m_ConnectTimer; SSU2TerminationReason m_TerminationReason; size_t m_MaxPayloadSize; - std::unique_ptr > m_PathChallenge; + std::unique_ptr m_PathChallenge; std::unordered_map m_ReceivedI2NPMsgIDs; // msgID -> timestamp in seconds uint64_t m_LastResendTime, m_LastResendAttemptTime; // in milliseconds - int m_NumRanges; - uint8_t m_Ranges[SSU2_MAX_NUM_ACK_RANGES*2]; // ranges sent with previous Ack if any }; + + + const int SSU2_PEER_TEST_RESEND_INTERVAL = 3000; // in milliseconds + const int SSU2_PEER_TEST_RESEND_INTERVAL_VARIANCE = 2000; // in milliseconds + const int SSU2_PEER_TEST_MAX_NUM_RESENDS = 3; + + class SSU2PeerTestSession: public SSU2Session // for PeerTest msgs 5,6,7 + { + public: + + SSU2PeerTestSession (SSU2Server& server, uint64_t sourceConnID, uint64_t destConnID); + + uint8_t GetMsgNumReceived () const { return m_MsgNumReceived; } + bool IsConnectedRecently () const { return m_IsConnectedRecently; } + void SetStatusChanged () { m_IsStatusChanged = true; } + + void SendPeerTest (uint8_t msg, const uint8_t * signedData, size_t signedDataLen, + std::shared_ptr addr); + bool ProcessPeerTest (uint8_t * buf, size_t len) override; + void Connect () override; // outgoing + bool ProcessFirstIncomingMessage (uint64_t connID, uint8_t * buf, size_t len) override; // incoming + + private: + + void SendPeerTest (uint8_t msg, const uint8_t * signedData, size_t signedDataLen); // PeerTest message + void SendPeerTest (uint8_t msg); // send or resend m_SignedData + void HandlePeerTest (const uint8_t * buf, size_t len) override; + + void ScheduleResend (); + + private: + + uint8_t m_MsgNumReceived, m_NumResends; + bool m_IsConnectedRecently, m_IsStatusChanged; + std::vector m_SignedData; // for resends + boost::asio::deadline_timer m_PeerTestResendTimer; + }; inline uint64_t CreateHeaderMask (const uint8_t * kh, const uint8_t * nonce) { @@ -407,12 +434,6 @@ namespace transport i2p::crypto::ChaCha20 ((uint8_t *)&data, 8, kh, nonce, (uint8_t *)&data); return data; } - - inline void CreateNonce (uint64_t seqn, uint8_t * nonce) - { - memset (nonce, 0, 4); - htole64buf (nonce + 4, seqn); - } } } diff --git a/libi2pd/Signature.cpp b/libi2pd/Signature.cpp index 3e4b451b..342b6d03 100644 --- a/libi2pd/Signature.cpp +++ b/libi2pd/Signature.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2025, The PurpleI2P Project +* Copyright (c) 2013-2023, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -7,10 +7,6 @@ */ #include -#include -#if (OPENSSL_VERSION_NUMBER >= 0x030000000) // since 3.0.0 -#include -#endif #include "Log.h" #include "Signature.h" @@ -18,163 +14,6 @@ namespace i2p { namespace crypto { -#if (OPENSSL_VERSION_NUMBER >= 0x030000000) // since 3.0.0 - DSAVerifier::DSAVerifier (): - m_PublicKey (nullptr) - { - } - - DSAVerifier::~DSAVerifier () - { - if (m_PublicKey) - EVP_PKEY_free (m_PublicKey); - } - - void DSAVerifier::SetPublicKey (const uint8_t * signingKey) - { - if (m_PublicKey) - EVP_PKEY_free (m_PublicKey); - BIGNUM * pub = BN_bin2bn (signingKey, DSA_PUBLIC_KEY_LENGTH, NULL); - m_PublicKey = CreateDSA (pub); - BN_free (pub); - } - - bool DSAVerifier::Verify (const uint8_t * buf, size_t len, const uint8_t * signature) const - { - // calculate SHA1 digest - uint8_t digest[20], sign[48]; - SHA1 (buf, len, digest); - // signature - DSA_SIG * sig = DSA_SIG_new(); - DSA_SIG_set0 (sig, BN_bin2bn (signature, DSA_SIGNATURE_LENGTH/2, NULL), BN_bin2bn (signature + DSA_SIGNATURE_LENGTH/2, DSA_SIGNATURE_LENGTH/2, NULL)); - // to DER format - uint8_t * s = sign; - auto l = i2d_DSA_SIG (sig, &s); - DSA_SIG_free(sig); - // verify - auto ctx = EVP_PKEY_CTX_new (m_PublicKey, NULL); - EVP_PKEY_verify_init(ctx); - EVP_PKEY_CTX_set_signature_md(ctx, EVP_sha1()); - bool ret = EVP_PKEY_verify(ctx, sign, l, digest, 20); - EVP_PKEY_CTX_free(ctx); - return ret; - } - - DSASigner::DSASigner (const uint8_t * signingPrivateKey, const uint8_t * signingPublicKey) - { - BIGNUM * priv = BN_bin2bn (signingPrivateKey, DSA_PRIVATE_KEY_LENGTH, NULL); - m_PrivateKey = CreateDSA (nullptr, priv); - BN_free (priv); - } - - DSASigner::~DSASigner () - { - if (m_PrivateKey) - EVP_PKEY_free (m_PrivateKey); - } - - void DSASigner::Sign (const uint8_t * buf, int len, uint8_t * signature) const - { - uint8_t digest[20], sign[48]; - SHA1 (buf, len, digest); - auto ctx = EVP_PKEY_CTX_new (m_PrivateKey, NULL); - EVP_PKEY_sign_init(ctx); - EVP_PKEY_CTX_set_signature_md(ctx, EVP_sha1()); - size_t l = 48; - EVP_PKEY_sign(ctx, sign, &l, digest, 20); - const uint8_t * s1 = sign; - DSA_SIG * sig = d2i_DSA_SIG (NULL, &s1, l); - const BIGNUM * r, * s; - DSA_SIG_get0 (sig, &r, &s); - bn2buf (r, signature, DSA_SIGNATURE_LENGTH/2); - bn2buf (s, signature + DSA_SIGNATURE_LENGTH/2, DSA_SIGNATURE_LENGTH/2); - DSA_SIG_free(sig); - EVP_PKEY_CTX_free(ctx); - } - - void CreateDSARandomKeys (uint8_t * signingPrivateKey, uint8_t * signingPublicKey) - { - EVP_PKEY * paramskey = CreateDSA(); - EVP_PKEY_CTX * ctx = EVP_PKEY_CTX_new_from_pkey(NULL, paramskey, NULL); - EVP_PKEY_keygen_init(ctx); - EVP_PKEY * pkey = nullptr; - EVP_PKEY_keygen(ctx, &pkey); - BIGNUM * pub = NULL, * priv = NULL; - EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_PUB_KEY, &pub); - bn2buf (pub, signingPublicKey, DSA_PUBLIC_KEY_LENGTH); - EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_PRIV_KEY, &priv); - bn2buf (priv, signingPrivateKey, DSA_PRIVATE_KEY_LENGTH); - BN_free (pub); BN_free (priv); - EVP_PKEY_free (pkey); - EVP_PKEY_free (paramskey); - EVP_PKEY_CTX_free (ctx); - } -#else - - DSAVerifier::DSAVerifier () - { - m_PublicKey = CreateDSA (); - } - - DSAVerifier::~DSAVerifier () - { - DSA_free (m_PublicKey); - } - - void DSAVerifier::SetPublicKey (const uint8_t * signingKey) - { - DSA_set0_key (m_PublicKey, BN_bin2bn (signingKey, DSA_PUBLIC_KEY_LENGTH, NULL), NULL); - } - - bool DSAVerifier::Verify (const uint8_t * buf, size_t len, const uint8_t * signature) const - { - // calculate SHA1 digest - uint8_t digest[20]; - SHA1 (buf, len, digest); - // signature - DSA_SIG * sig = DSA_SIG_new(); - DSA_SIG_set0 (sig, BN_bin2bn (signature, DSA_SIGNATURE_LENGTH/2, NULL), BN_bin2bn (signature + DSA_SIGNATURE_LENGTH/2, DSA_SIGNATURE_LENGTH/2, NULL)); - // DSA verification - int ret = DSA_do_verify (digest, 20, sig, m_PublicKey); - DSA_SIG_free(sig); - return ret; - } - - DSASigner::DSASigner (const uint8_t * signingPrivateKey, const uint8_t * signingPublicKey) - { - m_PrivateKey = CreateDSA (); - DSA_set0_key (m_PrivateKey, BN_bin2bn (signingPublicKey, DSA_PUBLIC_KEY_LENGTH, NULL), BN_bin2bn (signingPrivateKey, DSA_PRIVATE_KEY_LENGTH, NULL)); - } - - DSASigner::~DSASigner () - { - DSA_free (m_PrivateKey); - } - - void DSASigner::Sign (const uint8_t * buf, int len, uint8_t * signature) const - { - uint8_t digest[20]; - SHA1 (buf, len, digest); - DSA_SIG * sig = DSA_do_sign (digest, 20, m_PrivateKey); - const BIGNUM * r, * s; - DSA_SIG_get0 (sig, &r, &s); - bn2buf (r, signature, DSA_SIGNATURE_LENGTH/2); - bn2buf (s, signature + DSA_SIGNATURE_LENGTH/2, DSA_SIGNATURE_LENGTH/2); - DSA_SIG_free(sig); - } - - void CreateDSARandomKeys (uint8_t * signingPrivateKey, uint8_t * signingPublicKey) - { - DSA * dsa = CreateDSA (); - DSA_generate_key (dsa); - const BIGNUM * pub_key, * priv_key; - DSA_get0_key(dsa, &pub_key, &priv_key); - bn2buf (priv_key, signingPrivateKey, DSA_PRIVATE_KEY_LENGTH); - bn2buf (pub_key, signingPublicKey, DSA_PUBLIC_KEY_LENGTH); - DSA_free (dsa); - } -#endif - #if OPENSSL_EDDSA EDDSA25519Verifier::EDDSA25519Verifier (): m_Pkey (nullptr) @@ -310,178 +149,5 @@ namespace crypto LogPrint (eLogError, "EdDSA signing key is not set"); } #endif - -#if (OPENSSL_VERSION_NUMBER >= 0x030000000) - static const OSSL_PARAM EDDSA25519phParams[] = - { - OSSL_PARAM_utf8_string ("instance", (char *)"Ed25519ph", 9), - OSSL_PARAM_END - }; - - bool EDDSA25519phVerifier::Verify (const uint8_t * buf, size_t len, const uint8_t * signature) const - { - auto pkey = GetPkey (); - if (pkey) - { - uint8_t digest[64]; - SHA512 (buf, len, digest); - EVP_MD_CTX * ctx = EVP_MD_CTX_create (); - EVP_DigestVerifyInit_ex (ctx, NULL, NULL, NULL, NULL, pkey, EDDSA25519phParams); - auto ret = EVP_DigestVerify (ctx, signature, 64, digest, 64); - EVP_MD_CTX_destroy (ctx); - return ret; - } - else - LogPrint (eLogError, "EdDSA verification key is not set"); - return false; - } - - EDDSA25519phSigner::EDDSA25519phSigner (const uint8_t * signingPrivateKey): - EDDSA25519Signer (signingPrivateKey) - { - } - - void EDDSA25519phSigner::Sign (const uint8_t * buf, int len, uint8_t * signature) const - { - auto pkey = GetPkey (); - if (pkey) - { - uint8_t digest[64]; - SHA512 (buf, len, digest); - EVP_MD_CTX * ctx = EVP_MD_CTX_create (); - size_t l = 64; - uint8_t sig[64]; - EVP_DigestSignInit_ex (ctx, NULL, NULL, NULL, NULL, pkey, EDDSA25519phParams); - if (!EVP_DigestSign (ctx, sig, &l, digest, 64)) - LogPrint (eLogError, "EdDSA signing failed"); - memcpy (signature, sig, 64); - EVP_MD_CTX_destroy (ctx); - } - else - LogPrint (eLogError, "EdDSA signing key is not set"); - } -#endif - -#if OPENSSL_PQ - - MLDSA44Verifier::MLDSA44Verifier (): - m_Pkey (nullptr) - { - } - - MLDSA44Verifier::~MLDSA44Verifier () - { - EVP_PKEY_free (m_Pkey); - } - - void MLDSA44Verifier::SetPublicKey (const uint8_t * signingKey) - { - if (m_Pkey) - { - EVP_PKEY_free (m_Pkey); - m_Pkey = nullptr; - } - OSSL_PARAM params[] = - { - OSSL_PARAM_octet_string (OSSL_PKEY_PARAM_PUB_KEY, (uint8_t *)signingKey, GetPublicKeyLen ()), - OSSL_PARAM_END - }; - EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new_from_name (NULL, "ML-DSA-44", NULL); - if (ctx) - { - EVP_PKEY_fromdata_init (ctx); - EVP_PKEY_fromdata (ctx, &m_Pkey, OSSL_KEYMGMT_SELECT_PUBLIC_KEY, params); - EVP_PKEY_CTX_free (ctx); - } - else - LogPrint (eLogError, "MLDSA44 can't create PKEY context"); - } - - bool MLDSA44Verifier::Verify (const uint8_t * buf, size_t len, const uint8_t * signature) const - { - bool ret = false; - if (m_Pkey) - { - EVP_PKEY_CTX * vctx = EVP_PKEY_CTX_new_from_pkey (NULL, m_Pkey, NULL); - if (vctx) - { - EVP_SIGNATURE * sig = EVP_SIGNATURE_fetch (NULL, "ML-DSA-44", NULL); - if (sig) - { - int encode = 1; - OSSL_PARAM params[] = - { - OSSL_PARAM_int(OSSL_SIGNATURE_PARAM_MESSAGE_ENCODING, &encode), - OSSL_PARAM_END - }; - EVP_PKEY_verify_message_init (vctx, sig, params); - ret = EVP_PKEY_verify (vctx, signature, GetSignatureLen (), buf, len); - EVP_SIGNATURE_free (sig); - } - EVP_PKEY_CTX_free (vctx); - } - else - LogPrint (eLogError, "MLDSA44 can't obtain context from PKEY"); - } - else - LogPrint (eLogError, "MLDSA44 verification key is not set"); - return ret; - } - - MLDSA44Signer::MLDSA44Signer (const uint8_t * signingPrivateKey): - m_Pkey (nullptr) - { - OSSL_PARAM params[] = - { - OSSL_PARAM_octet_string (OSSL_PKEY_PARAM_PRIV_KEY, (uint8_t *)signingPrivateKey, MLDSA44_PRIVATE_KEY_LENGTH), - OSSL_PARAM_END - }; - EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new_from_name (NULL, "ML-DSA-44", NULL); - if (ctx) - { - EVP_PKEY_fromdata_init (ctx); - EVP_PKEY_fromdata (ctx, &m_Pkey, OSSL_KEYMGMT_SELECT_PRIVATE_KEY, params); - EVP_PKEY_CTX_free (ctx); - } - else - LogPrint (eLogError, "MLDSA44 can't create PKEY context"); - } - - MLDSA44Signer::~MLDSA44Signer () - { - if (m_Pkey) EVP_PKEY_free (m_Pkey); - } - - void MLDSA44Signer::Sign (const uint8_t * buf, int len, uint8_t * signature) const - { - if (m_Pkey) - { - EVP_PKEY_CTX * sctx = EVP_PKEY_CTX_new_from_pkey (NULL, m_Pkey, NULL); - if (sctx) - { - EVP_SIGNATURE * sig = EVP_SIGNATURE_fetch (NULL, "ML-DSA-44", NULL); - if (sig) - { - int encode = 1; - OSSL_PARAM params[] = - { - OSSL_PARAM_int(OSSL_SIGNATURE_PARAM_MESSAGE_ENCODING, &encode), - OSSL_PARAM_END - }; - EVP_PKEY_sign_message_init (sctx, sig, params); - size_t siglen = MLDSA44_SIGNATURE_LENGTH; - EVP_PKEY_sign (sctx, signature, &siglen, buf, len); - EVP_SIGNATURE_free (sig); - } - EVP_PKEY_CTX_free (sctx); - } - else - LogPrint (eLogError, "MLDSA44 can't obtain context from PKEY"); - } - else - LogPrint (eLogError, "MLDSA44 signing key is not set"); - } - -#endif } } diff --git a/libi2pd/Signature.h b/libi2pd/Signature.h index 20c7e11b..8bd94357 100644 --- a/libi2pd/Signature.h +++ b/libi2pd/Signature.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2025, The PurpleI2P Project +* Copyright (c) 2013-2023, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -43,55 +43,94 @@ namespace crypto virtual void Sign (const uint8_t * buf, int len, uint8_t * signature) const = 0; }; - // DSA const size_t DSA_PUBLIC_KEY_LENGTH = 128; const size_t DSA_SIGNATURE_LENGTH = 40; const size_t DSA_PRIVATE_KEY_LENGTH = DSA_SIGNATURE_LENGTH/2; class DSAVerifier: public Verifier { public: - - DSAVerifier (); - ~DSAVerifier (); - // implements Verifier - void SetPublicKey (const uint8_t * signingKey) override; - bool Verify (const uint8_t * buf, size_t len, const uint8_t * signature) const override; - size_t GetPublicKeyLen () const override { return DSA_PUBLIC_KEY_LENGTH; }; - size_t GetSignatureLen () const override { return DSA_SIGNATURE_LENGTH; }; - + DSAVerifier () + { + m_PublicKey = CreateDSA (); + } + + void SetPublicKey (const uint8_t * signingKey) + { + DSA_set0_key (m_PublicKey, BN_bin2bn (signingKey, DSA_PUBLIC_KEY_LENGTH, NULL), NULL); + } + + ~DSAVerifier () + { + DSA_free (m_PublicKey); + } + + bool Verify (const uint8_t * buf, size_t len, const uint8_t * signature) const + { + // calculate SHA1 digest + uint8_t digest[20]; + SHA1 (buf, len, digest); + // signature + DSA_SIG * sig = DSA_SIG_new(); + DSA_SIG_set0 (sig, BN_bin2bn (signature, DSA_SIGNATURE_LENGTH/2, NULL), BN_bin2bn (signature + DSA_SIGNATURE_LENGTH/2, DSA_SIGNATURE_LENGTH/2, NULL)); + // DSA verification + int ret = DSA_do_verify (digest, 20, sig, m_PublicKey); + DSA_SIG_free(sig); + return ret; + } + + size_t GetPublicKeyLen () const { return DSA_PUBLIC_KEY_LENGTH; }; + size_t GetSignatureLen () const { return DSA_SIGNATURE_LENGTH; }; + private: -#if (OPENSSL_VERSION_NUMBER >= 0x030000000) // since 3.0.0 - EVP_PKEY * m_PublicKey; -#else DSA * m_PublicKey; -#endif }; class DSASigner: public Signer { public: - DSASigner (const uint8_t * signingPrivateKey, const uint8_t * signingPublicKey); + DSASigner (const uint8_t * signingPrivateKey, const uint8_t * signingPublicKey) // openssl 1.1 always requires DSA public key even for signing - ~DSASigner (); + { + m_PrivateKey = CreateDSA (); + DSA_set0_key (m_PrivateKey, BN_bin2bn (signingPublicKey, DSA_PUBLIC_KEY_LENGTH, NULL), BN_bin2bn (signingPrivateKey, DSA_PRIVATE_KEY_LENGTH, NULL)); + } - // implements Signer - void Sign (const uint8_t * buf, int len, uint8_t * signature) const override; + ~DSASigner () + { + DSA_free (m_PrivateKey); + } + + void Sign (const uint8_t * buf, int len, uint8_t * signature) const + { + uint8_t digest[20]; + SHA1 (buf, len, digest); + DSA_SIG * sig = DSA_do_sign (digest, 20, m_PrivateKey); + const BIGNUM * r, * s; + DSA_SIG_get0 (sig, &r, &s); + bn2buf (r, signature, DSA_SIGNATURE_LENGTH/2); + bn2buf (s, signature + DSA_SIGNATURE_LENGTH/2, DSA_SIGNATURE_LENGTH/2); + DSA_SIG_free(sig); + } private: -#if (OPENSSL_VERSION_NUMBER >= 0x030000000) // since 3.0.0 - EVP_PKEY * m_PrivateKey; -#else DSA * m_PrivateKey; -#endif }; - void CreateDSARandomKeys (uint8_t * signingPrivateKey, uint8_t * signingPublicKey); + inline void CreateDSARandomKeys (uint8_t * signingPrivateKey, uint8_t * signingPublicKey) + { + DSA * dsa = CreateDSA (); + DSA_generate_key (dsa); + const BIGNUM * pub_key, * priv_key; + DSA_get0_key(dsa, &pub_key, &priv_key); + bn2buf (priv_key, signingPrivateKey, DSA_PRIVATE_KEY_LENGTH); + bn2buf (pub_key, signingPublicKey, DSA_PUBLIC_KEY_LENGTH); + DSA_free (dsa); + } - // ECDSA struct SHA256Hash { static void CalculateHash (const uint8_t * buf, size_t len, uint8_t * digest) @@ -122,6 +161,7 @@ namespace crypto enum { hashLen = 64 }; }; + // EcDSA template class ECDSAVerifier: public Verifier { @@ -263,28 +303,14 @@ namespace crypto private: -#if OPENSSL_EDDSA - +#if OPENSSL_EDDSA EVP_PKEY * m_Pkey; - - protected: - - EVP_PKEY * GetPkey () const { return m_Pkey; }; #else EDDSAPoint m_PublicKey; uint8_t m_PublicKeyEncoded[EDDSA25519_PUBLIC_KEY_LENGTH]; #endif }; -#if (OPENSSL_VERSION_NUMBER >= 0x030000000) // since 3.0.0 - class EDDSA25519phVerifier: public EDDSA25519Verifier - { - public: - - bool Verify (const uint8_t * buf, size_t len, const uint8_t * signature) const; - }; -#endif - class EDDSA25519SignerCompat: public Signer { public: @@ -313,10 +339,6 @@ namespace crypto void Sign (const uint8_t * buf, int len, uint8_t * signature) const; - protected: - - EVP_PKEY * GetPkey () const { return m_Pkey; }; - private: EVP_PKEY * m_Pkey; @@ -328,18 +350,6 @@ namespace crypto #endif -#if (OPENSSL_VERSION_NUMBER >= 0x030000000) // since 3.0.0 - class EDDSA25519phSigner: public EDDSA25519Signer - { - public: - - EDDSA25519phSigner (const uint8_t * signingPrivateKey); - - void Sign (const uint8_t * buf, int len, uint8_t * signature) const; - }; - -#endif - inline void CreateEDDSA25519RandomKeys (uint8_t * signingPrivateKey, uint8_t * signingPublicKey) { #if OPENSSL_EDDSA @@ -520,57 +530,6 @@ namespace crypto RedDSA25519Signer signer (signingPrivateKey); memcpy (signingPublicKey, signer.GetPublicKey (), EDDSA25519_PUBLIC_KEY_LENGTH); } - -#if OPENSSL_PQ -#include - - // Post-Quantum - const size_t MLDSA44_PUBLIC_KEY_LENGTH = 1312; - const size_t MLDSA44_SIGNATURE_LENGTH = 2420; - const size_t MLDSA44_PRIVATE_KEY_LENGTH = 2560; - class MLDSA44Verifier: public Verifier - { - public: - - MLDSA44Verifier (); - void SetPublicKey (const uint8_t * signingKey); - ~MLDSA44Verifier (); - - bool Verify (const uint8_t * buf, size_t len, const uint8_t * signature) const; - - size_t GetPublicKeyLen () const { return MLDSA44_PUBLIC_KEY_LENGTH; }; - size_t GetSignatureLen () const { return MLDSA44_SIGNATURE_LENGTH; }; - size_t GetPrivateKeyLen () const { return MLDSA44_PRIVATE_KEY_LENGTH; }; - - private: - - EVP_PKEY * m_Pkey; - }; - - class MLDSA44Signer: public Signer - { - public: - - MLDSA44Signer (const uint8_t * signingPrivateKey); - ~MLDSA44Signer (); - - void Sign (const uint8_t * buf, int len, uint8_t * signature) const; - - private: - - EVP_PKEY * m_Pkey; - }; - - inline void CreateMLDSA44RandomKeys (uint8_t * signingPrivateKey, uint8_t * signingPublicKey) - { - EVP_PKEY * pkey = EVP_PKEY_Q_keygen (NULL, NULL, "ML-DSA-44"); - size_t len = MLDSA44_PUBLIC_KEY_LENGTH; - EVP_PKEY_get_octet_string_param (pkey, OSSL_PKEY_PARAM_PUB_KEY, signingPublicKey, MLDSA44_PUBLIC_KEY_LENGTH, &len); - len = MLDSA44_PRIVATE_KEY_LENGTH; - EVP_PKEY_get_octet_string_param (pkey, OSSL_PKEY_PARAM_PRIV_KEY, signingPrivateKey, MLDSA44_PRIVATE_KEY_LENGTH, &len); - EVP_PKEY_free (pkey); - } -#endif } } diff --git a/libi2pd/Streaming.cpp b/libi2pd/Streaming.cpp index 99da5fd2..c30c5d39 100644 --- a/libi2pd/Streaming.cpp +++ b/libi2pd/Streaming.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2025, The PurpleI2P Project +* Copyright (c) 2013-2024, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -19,55 +19,39 @@ namespace i2p { namespace stream { - void SendBufferQueue::Add (std::shared_ptr&& buf) + void SendBufferQueue::Add (std::shared_ptr buf) { if (buf) { + m_Buffers.push_back (buf); m_Size += buf->len; - m_Buffers.push_back (std::move (buf)); } } size_t SendBufferQueue::Get (uint8_t * buf, size_t len) { - if (!m_Size) return 0; size_t offset = 0; - if (len >= m_Size) + while (!m_Buffers.empty () && offset < len) { - for (auto& it: m_Buffers) + auto nextBuffer = m_Buffers.front (); + auto rem = nextBuffer->GetRemainingSize (); + if (offset + rem <= len) { - auto rem = it->GetRemainingSize (); - memcpy (buf + offset, it->GetRemaningBuffer (), rem); + // whole buffer + memcpy (buf + offset, nextBuffer->GetRemaningBuffer (), rem); offset += rem; + m_Buffers.pop_front (); // delete it } - m_Buffers.clear (); - m_Size = 0; - return offset; - } - else - { - while (!m_Buffers.empty () && offset < len) + else { - auto nextBuffer = m_Buffers.front (); - auto rem = nextBuffer->GetRemainingSize (); - if (offset + rem <= len) - { - // whole buffer - memcpy (buf + offset, nextBuffer->GetRemaningBuffer (), rem); - offset += rem; - m_Buffers.pop_front (); // delete it - } - else - { - // partially - rem = len - offset; - memcpy (buf + offset, nextBuffer->GetRemaningBuffer (), rem); - nextBuffer->offset += rem; - offset = len; // break - } + // partially + rem = len - offset; + memcpy (buf + offset, nextBuffer->GetRemaningBuffer (), rem); + nextBuffer->offset += rem; + offset = len; // break } - m_Size -= offset; - } + } + m_Size -= offset; return offset; } @@ -75,28 +59,28 @@ namespace stream { if (!m_Buffers.empty ()) { - for (auto& it: m_Buffers) + for (auto it: m_Buffers) it->Cancel (); m_Buffers.clear (); m_Size = 0; } } - Stream::Stream (boost::asio::io_context& service, StreamingDestination& local, + Stream::Stream (boost::asio::io_service& service, StreamingDestination& local, std::shared_ptr remote, int port): m_Service (service), m_SendStreamID (0), m_SequenceNumber (0), m_DropWindowDelaySequenceNumber (0), m_TunnelsChangeSequenceNumber (0), m_LastReceivedSequenceNumber (-1), m_PreviousReceivedSequenceNumber (-1), m_LastConfirmedReceivedSequenceNumber (0), // for limit inbound speed - m_Status (eStreamStatusNew), m_IsIncoming (false), m_IsAckSendScheduled (false), m_IsNAcked (false), m_IsFirstACK (false), - m_IsResendNeeded (false), m_IsFirstRttSample (false), m_IsSendTime (true), m_IsWinDropped (false), m_IsClientChoked (false), - m_IsTimeOutResend (false), m_IsImmediateAckRequested (false), m_IsRemoteLeaseChangeInProgress (false), m_DoubleWinIncCounter (false), m_LocalDestination (local), + m_Status (eStreamStatusNew), m_IsAckSendScheduled (false), m_IsNAcked (false), m_IsFirstACK (false), + m_IsResendNeeded (false), m_IsFirstRttSample (false), m_IsSendTime (true), m_IsWinDropped (false), + m_IsTimeOutResend (false), m_IsImmediateAckRequested (false), m_LocalDestination (local), m_RemoteLeaseSet (remote), m_ReceiveTimer (m_Service), m_SendTimer (m_Service), m_ResendTimer (m_Service), m_AckSendTimer (m_Service), m_NumSentBytes (0), m_NumReceivedBytes (0), m_Port (port), m_RTT (INITIAL_RTT), m_SlowRTT (INITIAL_RTT), m_SlowRTT2 (INITIAL_RTT), m_WindowSize (INITIAL_WINDOW_SIZE), m_LastWindowDropSize (0), m_WindowDropTargetSize (0), m_WindowIncCounter (0), m_RTO (INITIAL_RTO), - m_AckDelay (local.GetOwner ()->GetStreamingAckDelay ()), m_PrevRTTSample (INITIAL_RTT), m_WindowSizeTail (0), + m_AckDelay (local.GetOwner ()->GetStreamingAckDelay ()), m_PrevRTTSample (INITIAL_RTT), m_Jitter (0), m_MinPacingTime (0), - m_PacingTime (INITIAL_PACING_TIME), m_PacingTimeRem (0), m_LastSendTime (0), m_LastACKRecieveTime (0), m_ACKRecieveInterval (local.GetOwner ()->GetStreamingAckDelay ()), m_RemoteLeaseChangeTime (0), m_LastWindowIncTime (0), + m_PacingTime (INITIAL_PACING_TIME), m_PacingTimeRem (0), m_LastSendTime (0), m_LastACKSendTime (0), m_PacketACKInterval (1), m_PacketACKIntervalRem (0), // for limit inbound speed m_NumResendAttempts (0), m_NumPacketsToSend (0), m_MTU (STREAMING_MTU) { @@ -111,19 +95,19 @@ namespace stream m_PacketACKInterval = (1000000LL*STREAMING_MTU)/inboundSpeed; } - Stream::Stream (boost::asio::io_context& service, StreamingDestination& local): + Stream::Stream (boost::asio::io_service& service, StreamingDestination& local): m_Service (service), m_SendStreamID (0), m_SequenceNumber (0), m_DropWindowDelaySequenceNumber (0), m_TunnelsChangeSequenceNumber (0), m_LastReceivedSequenceNumber (-1), m_PreviousReceivedSequenceNumber (-1), m_LastConfirmedReceivedSequenceNumber (0), // for limit inbound speed - m_Status (eStreamStatusNew), m_IsIncoming (true), m_IsAckSendScheduled (false), m_IsNAcked (false), m_IsFirstACK (false), - m_IsResendNeeded (false), m_IsFirstRttSample (false), m_IsSendTime (true), m_IsWinDropped (false), m_IsClientChoked (false), - m_IsTimeOutResend (false), m_IsImmediateAckRequested (false), m_IsRemoteLeaseChangeInProgress (false), m_DoubleWinIncCounter (false), m_LocalDestination (local), + m_Status (eStreamStatusNew), m_IsAckSendScheduled (false), m_IsNAcked (false), m_IsFirstACK (false), + m_IsResendNeeded (false), m_IsFirstRttSample (false), m_IsSendTime (true), m_IsWinDropped (false), + m_IsTimeOutResend (false), m_IsImmediateAckRequested (false), m_LocalDestination (local), m_ReceiveTimer (m_Service), m_SendTimer (m_Service), m_ResendTimer (m_Service), m_AckSendTimer (m_Service), m_NumSentBytes (0), m_NumReceivedBytes (0), m_Port (0), m_RTT (INITIAL_RTT), m_SlowRTT (INITIAL_RTT), m_SlowRTT2 (INITIAL_RTT), m_WindowSize (INITIAL_WINDOW_SIZE), m_LastWindowDropSize (0), m_WindowDropTargetSize (0), m_WindowIncCounter (0), m_RTO (INITIAL_RTO), m_AckDelay (local.GetOwner ()->GetStreamingAckDelay ()), - m_PrevRTTSample (INITIAL_RTT), m_WindowSizeTail (0), m_Jitter (0), m_MinPacingTime (0), - m_PacingTime (INITIAL_PACING_TIME), m_PacingTimeRem (0), m_LastSendTime (0), m_LastACKRecieveTime (0), m_ACKRecieveInterval (local.GetOwner ()->GetStreamingAckDelay ()), m_RemoteLeaseChangeTime (0), m_LastWindowIncTime (0), + m_PrevRTTSample (INITIAL_RTT), m_Jitter (0), m_MinPacingTime (0), + m_PacingTime (INITIAL_PACING_TIME), m_PacingTimeRem (0), m_LastSendTime (0), m_LastACKSendTime (0), m_PacketACKInterval (1), m_PacketACKIntervalRem (0), // for limit inbound speed m_NumResendAttempts (0), m_NumPacketsToSend (0), m_MTU (STREAMING_MTU) { @@ -272,7 +256,6 @@ namespace stream if (receivedSeqn <= m_PreviousReceivedSequenceNumber || receivedSeqn == m_LastReceivedSequenceNumber) { m_CurrentOutboundTunnel = m_LocalDestination.GetOwner ()->GetTunnelPool ()->GetNextOutboundTunnel (m_CurrentOutboundTunnel); - CancelRemoteLeaseChange (); UpdateCurrentRemoteLease (); } m_PreviousReceivedSequenceNumber = receivedSeqn; @@ -380,14 +363,12 @@ namespace stream } if (delayRequested >= DELAY_CHOKING) { - if (!m_IsClientChoked) + if (!m_IsWinDropped) { - LogPrint (eLogDebug, "Streaming: Client choked, set min. window size"); m_WindowDropTargetSize = MIN_WINDOW_SIZE; m_LastWindowDropSize = 0; m_WindowIncCounter = 0; - m_IsClientChoked = true; - m_IsWinDropped = false; + m_IsWinDropped = true; // don't drop window twice m_DropWindowDelaySequenceNumber = m_SequenceNumber; UpdatePacingTime (); } @@ -450,42 +431,29 @@ namespace stream if (flags & PACKET_FLAG_SIGNATURE_INCLUDED) { - bool verified = false; + uint8_t signature[256]; auto signatureLen = m_TransientVerifier ? m_TransientVerifier->GetSignatureLen () : m_RemoteIdentity->GetSignatureLen (); - if (signatureLen > packet->GetLength ()) + if(signatureLen <= sizeof(signature)) + { + memcpy (signature, optionData, signatureLen); + memset (const_cast(optionData), 0, signatureLen); + bool verified = m_TransientVerifier ? + m_TransientVerifier->Verify (packet->GetBuffer (), packet->GetLength (), signature) : + m_RemoteIdentity->Verify (packet->GetBuffer (), packet->GetLength (), signature); + if (!verified) + { + LogPrint (eLogError, "Streaming: Signature verification failed, sSID=", m_SendStreamID, ", rSID=", m_RecvStreamID); + Close (); + flags |= PACKET_FLAG_CLOSE; + } + memcpy (const_cast(optionData), signature, signatureLen); + optionData += signatureLen; + } + else { LogPrint (eLogError, "Streaming: Signature too big, ", signatureLen, " bytes"); return false; - } - if(signatureLen <= 256) - { - // standard - uint8_t signature[256]; - memcpy (signature, optionData, signatureLen); - memset (const_cast(optionData), 0, signatureLen); - verified = m_TransientVerifier ? - m_TransientVerifier->Verify (packet->GetBuffer (), packet->GetLength (), signature) : - m_RemoteIdentity->Verify (packet->GetBuffer (), packet->GetLength (), signature); - if (verified) - memcpy (const_cast(optionData), signature, signatureLen); } - else - { - // post quantum - std::vector signature(signatureLen); - memcpy (signature.data (), optionData, signatureLen); - memset (const_cast(optionData), 0, signatureLen); - verified = m_TransientVerifier ? - m_TransientVerifier->Verify (packet->GetBuffer (), packet->GetLength (), signature.data ()) : - m_RemoteIdentity->Verify (packet->GetBuffer (), packet->GetLength (), signature.data ()); - } - if (verified) - optionData += signatureLen; - else - { - LogPrint (eLogError, "Streaming: Signature verification failed, sSID=", m_SendStreamID, ", rSID=", m_RecvStreamID); - return false; - } } if (immediateAckRequested) SendQuickAck (); @@ -526,7 +494,6 @@ namespace stream return; } int rttSample = INT_MAX; - int incCounter = 0; m_IsNAcked = false; m_IsResendNeeded = false; int nackCount = packet->GetNACKCount (); @@ -568,21 +535,13 @@ namespace stream m_SentPackets.erase (it++); m_LocalDestination.DeletePacket (sentPacket); acknowledged = true; - if (m_WindowIncCounter < MAX_WINDOW_SIZE && !m_IsFirstACK && !m_IsWinDropped) - incCounter++; + if (m_WindowSize < MAX_WINDOW_SIZE && !m_IsFirstACK) + if (m_RTT < m_LocalDestination.GetRandom () % INITIAL_RTT) // dirty + m_WindowIncCounter++; } else break; } - if (m_LastACKRecieveTime) - { - uint64_t interval = ts - m_LastACKRecieveTime; - if (m_ACKRecieveInterval) - m_ACKRecieveInterval = (m_ACKRecieveInterval + interval) / 2; - else - m_ACKRecieveInterval = interval; - } - m_LastACKRecieveTime = ts; if (rttSample != INT_MAX) { if (m_IsFirstRttSample && !m_IsFirstACK) @@ -592,7 +551,7 @@ namespace stream m_SlowRTT2 = rttSample; m_PrevRTTSample = rttSample; m_Jitter = rttSample / 10; // 10% - m_Jitter += 15; // for low-latency connections + m_Jitter += 5; // for low-latency connections m_IsFirstRttSample = false; } else @@ -609,50 +568,41 @@ namespace stream jitter = m_PrevRTTSample - rttSample; else jitter = rttSample / 10; // 10% - jitter += 15; // for low-latency connections + jitter += 5; // for low-latency connections m_Jitter = (0.05 * jitter) + (1.0 - 0.05) * m_Jitter; } - if (rttSample > m_SlowRTT) - { - incCounter = 0; - m_DoubleWinIncCounter = 1; - } - else if (rttSample < m_SlowRTT) - { - if (m_DoubleWinIncCounter) - { - incCounter = incCounter * 2; - m_DoubleWinIncCounter = 0; - } - } - m_WindowIncCounter = m_WindowIncCounter + incCounter; // // delay-based CC - if ((m_SlowRTT2 > m_SlowRTT + m_Jitter && rttSample > m_SlowRTT2 && rttSample > m_PrevRTTSample) && !m_IsWinDropped && !m_IsClientChoked) // Drop window if RTT grows too fast, late detection - { - LogPrint (eLogDebug, "Streaming: Congestion detected, reduce window size"); + if ((m_SlowRTT2 > m_SlowRTT + m_Jitter && rttSample > m_SlowRTT2 && rttSample > m_PrevRTTSample) && !m_IsWinDropped) // Drop window if RTT grows too fast, late detection ProcessWindowDrop (); - } UpdatePacingTime (); m_PrevRTTSample = rttSample; bool wasInitial = m_RTO == INITIAL_RTO; - m_RTO = std::max (MIN_RTO, (int)(m_RTT * 1.3 + m_Jitter + m_ACKRecieveInterval)); // TODO: implement it better + m_RTO = std::max (MIN_RTO, (int)(m_RTT * 1.3 + m_Jitter)); // TODO: implement it better if (wasInitial) ScheduleResend (); } - if (m_IsClientChoked && ackThrough >= m_DropWindowDelaySequenceNumber) - m_IsClientChoked = false; if (m_IsWinDropped && ackThrough > m_DropWindowDelaySequenceNumber) { m_IsFirstRttSample = true; m_IsWinDropped = false; } - if (m_WindowDropTargetSize && int(m_SentPackets.size ()) <= m_WindowDropTargetSize) + if (m_WindowDropTargetSize && m_WindowSize <= m_WindowDropTargetSize) { - m_WindowSize = m_WindowDropTargetSize; m_WindowDropTargetSize = 0; + m_DropWindowDelaySequenceNumber = m_SequenceNumber; + } + if (acknowledged && m_WindowDropTargetSize && m_WindowSize > m_WindowDropTargetSize) + { + m_RTO = std::max (MIN_RTO, (int)(m_RTT * 1.5 + m_Jitter)); // we assume that the next rtt sample may be much larger than the current + m_IsResendNeeded = true; + m_WindowSize = m_SentPackets.size () + 1; // if there are no packets to resend, just send one regular packet + if (m_WindowSize < MIN_WINDOW_SIZE) m_WindowSize = MIN_WINDOW_SIZE; + if (m_WindowSize > MAX_WINDOW_SIZE) m_WindowSize = MAX_WINDOW_SIZE; + m_WindowIncCounter = 0; + UpdatePacingTime (); } if (acknowledged || m_IsNAcked) { @@ -661,14 +611,12 @@ namespace stream if (m_SendBuffer.IsEmpty () && m_SentPackets.size () > 0) // tail loss { m_IsResendNeeded = true; - m_RTO = std::max (MIN_RTO, (int)(m_RTT * 1.5 + m_Jitter + m_ACKRecieveInterval)); // to prevent spurious retransmit + m_RTO = std::max (MIN_RTO, (int)(m_RTT * 1.5 + m_Jitter)); // to prevent spurious retransmit } if (m_SentPackets.empty () && m_SendBuffer.IsEmpty ()) { m_ResendTimer.cancel (); m_SendTimer.cancel (); - m_LastACKRecieveTime = 0; - m_ACKRecieveInterval = m_AckDelay; } if (acknowledged && m_IsFirstACK) { @@ -718,7 +666,7 @@ namespace stream { // make sure that AsycReceive complete auto s = shared_from_this(); - boost::asio::post (m_Service, [s]() + m_Service.post ([s]() { s->m_ReceiveTimer.cancel (); }); @@ -746,18 +694,17 @@ namespace stream else if (handler) handler(boost::system::error_code ()); auto s = shared_from_this (); - boost::asio::post (m_Service, [s, buffer = std::move(buffer)]() mutable + m_Service.post ([s, buffer]() { if (buffer) - s->m_SendBuffer.Add (std::move(buffer)); + s->m_SendBuffer.Add (buffer); s->SendBuffer (); }); } void Stream::SendBuffer () { - if (m_RemoteLeaseSet) // don't scheudle send for first SYN for incoming stream - ScheduleSend (); + ScheduleSend (); auto ts = i2p::util::GetMillisecondsSinceEpoch (); int numMsgs = m_WindowSize - m_SentPackets.size (); if (numMsgs <= 0 || !m_IsSendTime) // window is full @@ -808,8 +755,8 @@ namespace stream if (!m_RemoteLeaseSet) m_RemoteLeaseSet = m_LocalDestination.GetOwner ()->FindLeaseSet (m_RemoteIdentity->GetIdentHash ());; if (m_RemoteLeaseSet) { - m_RoutingSession = m_LocalDestination.GetOwner ()->GetRoutingSession (m_RemoteLeaseSet, true, !m_IsIncoming); - m_MTU = (m_RoutingSession && m_RoutingSession->IsRatchets ()) ? STREAMING_MTU_RATCHETS : STREAMING_MTU; + 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; @@ -995,7 +942,7 @@ namespace stream if (choking || requestImmediateAck) { htobe16buf (packet + size, 2); // 2 bytes delay interval - htobe16buf (packet + size + 2, choking ? DELAY_CHOKING : 0); // set choking or immediate ack interval + htobe16buf (packet + size + 2, choking ? DELAY_CHOKING : 0); // set choking or immediated ack interval size += 2; if (requestImmediateAck) // ack request sent { @@ -1109,7 +1056,7 @@ namespace stream m_LocalDestination.GetOwner ()->Sign (packet, size, signature); p->len = size; - boost::asio::post (m_Service, std::bind (&Stream::SendPacket, shared_from_this (), p)); + m_Service.post (std::bind (&Stream::SendPacket, shared_from_this (), p)); LogPrint (eLogDebug, "Streaming: FIN sent, sSID=", m_SendStreamID); } @@ -1157,7 +1104,6 @@ namespace stream { if (!m_RemoteLeaseSet) { - CancelRemoteLeaseChange (); UpdateCurrentRemoteLease (); if (!m_RemoteLeaseSet) { @@ -1166,15 +1112,7 @@ namespace stream } } if (!m_RoutingSession || m_RoutingSession->IsTerminated () || !m_RoutingSession->IsReadyToSend ()) // expired and detached or new session sent - { - m_RoutingSession = m_LocalDestination.GetOwner ()->GetRoutingSession (m_RemoteLeaseSet, true, !m_IsIncoming || m_SequenceNumber > 1); - if (!m_RoutingSession) - { - LogPrint (eLogError, "Streaming: Can't obtain routing session, sSID=", m_SendStreamID); - Terminate (); - return; - } - } + m_RoutingSession = m_LocalDestination.GetOwner ()->GetRoutingSession (m_RemoteLeaseSet, true); if (!m_CurrentOutboundTunnel && m_RoutingSession) // first message to send { // try to get shared path first @@ -1184,35 +1122,14 @@ namespace stream m_CurrentOutboundTunnel = routingPath->outboundTunnel; m_CurrentRemoteLease = routingPath->remoteLease; m_RTT = routingPath->rtt; + m_RTO = std::max (MIN_RTO, (int)(m_RTT * 1.3 + m_Jitter)); // TODO: implement it better } } auto ts = i2p::util::GetMillisecondsSinceEpoch (); - if (!m_CurrentRemoteLease || !m_CurrentRemoteLease->endDate) // excluded from LeaseSet - { - CancelRemoteLeaseChange (); + if (!m_CurrentRemoteLease || !m_CurrentRemoteLease->endDate || // excluded from LeaseSet + ts >= m_CurrentRemoteLease->endDate - i2p::data::LEASE_ENDDATE_THRESHOLD) UpdateCurrentRemoteLease (true); - } - if (m_RemoteLeaseChangeTime && m_IsRemoteLeaseChangeInProgress && ts > m_RemoteLeaseChangeTime + INITIAL_RTO) - { - LogPrint (eLogDebug, "Streaming: RemoteLease changed, set initial window size"); - CancelRemoteLeaseChange (); - m_CurrentRemoteLease = m_NextRemoteLease; - ResetWindowSize (); - } - auto currentRemoteLease = m_CurrentRemoteLease; - if (!m_IsRemoteLeaseChangeInProgress && m_RemoteLeaseSet && m_CurrentRemoteLease && ts >= m_CurrentRemoteLease->endDate - i2p::data::LEASE_ENDDATE_THRESHOLD) - { - auto leases = m_RemoteLeaseSet->GetNonExpiredLeases (false); - if (leases.size ()) - { - m_IsRemoteLeaseChangeInProgress = true; - UpdateCurrentRemoteLease (true); - m_NextRemoteLease = m_CurrentRemoteLease; - } - else - UpdateCurrentRemoteLease (true); - } if (m_CurrentRemoteLease && ts < m_CurrentRemoteLease->endDate + i2p::data::LEASE_ENDDATE_THRESHOLD) { bool freshTunnel = false; @@ -1233,8 +1150,7 @@ namespace stream } if (freshTunnel) { - LogPrint (eLogDebug, "Streaming: OutboundTunnel changed, set initial window size"); - ResetWindowSize (); + m_RTO = INITIAL_RTO; // m_TunnelsChangeSequenceNumber = m_SequenceNumber; // should be determined more precisely } @@ -1250,11 +1166,6 @@ namespace stream msg }); m_NumSentBytes += it->GetLength (); - if (m_IsRemoteLeaseChangeInProgress && !m_RemoteLeaseChangeTime) - { - m_RemoteLeaseChangeTime = ts; - m_CurrentRemoteLease = currentRemoteLease; // change it back before new lease is confirmed - } } m_CurrentOutboundTunnel->SendTunnelDataMsgs (msgs); } @@ -1298,8 +1209,7 @@ namespace stream if (m_Status != eStreamStatusTerminated) { m_SendTimer.cancel (); - m_SendTimer.expires_from_now (boost::posix_time::microseconds( - SEND_INTERVAL + m_LocalDestination.GetRandom () % SEND_INTERVAL_VARIANCE)); + m_SendTimer.expires_from_now (boost::posix_time::microseconds(SEND_INTERVAL)); m_SendTimer.async_wait (std::bind (&Stream::HandleSendTimer, shared_from_this (), std::placeholders::_1)); } @@ -1312,76 +1222,34 @@ namespace stream auto ts = i2p::util::GetMillisecondsSinceEpoch (); if (m_LastSendTime && ts*1000 > m_LastSendTime*1000 + m_PacingTime) { - if (m_PacingTime) - { - auto numPackets = std::lldiv (m_PacingTimeRem + ts*1000 - m_LastSendTime*1000, m_PacingTime); - m_NumPacketsToSend = numPackets.quot; - m_PacingTimeRem = numPackets.rem; - } - else - { - LogPrint (eLogError, "Streaming: pacing time is zero"); - m_NumPacketsToSend = 1; m_PacingTimeRem = 0; - } + m_NumPacketsToSend = ((ts*1000 - m_LastSendTime*1000) + m_PacingTimeRem) / m_PacingTime; + m_PacingTimeRem = ((ts*1000 - m_LastSendTime*1000) + m_PacingTimeRem) - (m_NumPacketsToSend * m_PacingTime); m_IsSendTime = true; - if (m_WindowIncCounter && (m_WindowSize < MAX_WINDOW_SIZE || m_WindowDropTargetSize) && !m_SendBuffer.IsEmpty () && m_PacingTime > m_MinPacingTime && m_RTT <= m_SlowRTT) + if (m_WindowIncCounter && m_WindowSize < MAX_WINDOW_SIZE && !m_SendBuffer.IsEmpty ()) { - float winSize = m_WindowSize; - if (m_WindowDropTargetSize) - winSize = m_WindowDropTargetSize; - float maxWinSize = MAX_WINDOW_SIZE; - if (m_LastWindowIncTime) - maxWinSize = (ts - m_LastWindowIncTime) / (m_RTT / MAX_WINDOW_SIZE_INC_PER_RTT) + winSize; for (int i = 0; i < m_NumPacketsToSend; i++) { if (m_WindowIncCounter) { - if (m_WindowDropTargetSize) - { - if (m_LastWindowDropSize && (m_LastWindowDropSize >= m_WindowDropTargetSize)) - m_WindowDropTargetSize += 1 - (1 / ((m_LastWindowDropSize + PREV_SPEED_KEEP_TIME_COEFF) / m_WindowDropTargetSize)); // some magic here - else if (m_LastWindowDropSize && (m_LastWindowDropSize < m_WindowDropTargetSize)) - m_WindowDropTargetSize += (m_WindowDropTargetSize - (m_LastWindowDropSize - PREV_SPEED_KEEP_TIME_COEFF)) / m_WindowDropTargetSize; // some magic here - else - m_WindowDropTargetSize += (m_WindowDropTargetSize - (1 - PREV_SPEED_KEEP_TIME_COEFF)) / m_WindowDropTargetSize; - if (m_WindowDropTargetSize > MAX_WINDOW_SIZE) m_WindowDropTargetSize = MAX_WINDOW_SIZE; - m_WindowIncCounter--; - if (m_WindowDropTargetSize >= maxWinSize) - { - m_WindowDropTargetSize = maxWinSize; - break; - } - } + if (m_LastWindowDropSize && (m_LastWindowDropSize >= m_WindowSize)) + m_WindowSize += 1 - (1 / ((m_LastWindowDropSize + PREV_SPEED_KEEP_TIME_COEFF) / m_WindowSize)); // some magic here + else if (m_LastWindowDropSize && (m_LastWindowDropSize < m_WindowSize)) + m_WindowSize += (m_WindowSize - (m_LastWindowDropSize - PREV_SPEED_KEEP_TIME_COEFF)) / m_WindowSize; // some magic here else - { - if (m_LastWindowDropSize && (m_LastWindowDropSize >= m_WindowSize)) - m_WindowSize += 1 - (1 / ((m_LastWindowDropSize + PREV_SPEED_KEEP_TIME_COEFF) / m_WindowSize)); // some magic here - else if (m_LastWindowDropSize && (m_LastWindowDropSize < m_WindowSize)) - m_WindowSize += (m_WindowSize - (m_LastWindowDropSize - PREV_SPEED_KEEP_TIME_COEFF)) / m_WindowSize; // some magic here - else - m_WindowSize += (m_WindowSize - (1 - PREV_SPEED_KEEP_TIME_COEFF)) / m_WindowSize; - if (m_WindowSize > MAX_WINDOW_SIZE) m_WindowSize = MAX_WINDOW_SIZE; - m_WindowIncCounter--; - if (m_WindowSize >= maxWinSize) - { - m_WindowSize = maxWinSize; - break; - } - } + m_WindowSize += (m_WindowSize - (1 - PREV_SPEED_KEEP_TIME_COEFF)) / m_WindowSize; + if (m_WindowSize > MAX_WINDOW_SIZE) m_WindowSize = MAX_WINDOW_SIZE; + m_WindowIncCounter --; + UpdatePacingTime (); } - else - break; } - m_LastWindowIncTime = ts; - UpdatePacingTime (); } - else if (m_WindowIncCounter && m_WindowSize == MAX_WINDOW_SIZE && !m_SendBuffer.IsEmpty () && m_PacingTime > m_MinPacingTime) - { - m_WindowSizeTail = m_WindowSizeTail + m_WindowIncCounter; - if (m_WindowSizeTail > MAX_WINDOW_SIZE) m_WindowSizeTail = MAX_WINDOW_SIZE; - } - if (m_IsNAcked || m_IsResendNeeded || m_IsClientChoked) // resend packets + if (m_IsNAcked) ResendPacket (); + else if (m_IsResendNeeded) // resend packets + ResendPacket (); + // delay-based CC + else if (!m_IsWinDropped && int(m_SentPackets.size ()) == m_WindowSize) // we sending packets too fast, early detection + ProcessWindowDrop (); else if (m_WindowSize > int(m_SentPackets.size ())) // send packets SendBuffer (); } @@ -1420,111 +1288,97 @@ namespace stream void Stream::ResendPacket () { - // check for resend attempts - if (m_IsIncoming && m_SequenceNumber == 1 && m_NumResendAttempts > 0) - { - LogPrint (eLogWarning, "Streaming: SYNACK packet was not ACKed after ", m_NumResendAttempts, " attempts, terminate, rSID=", m_RecvStreamID, ", sSID=", m_SendStreamID); - m_Status = eStreamStatusReset; - Close (); - return; - } - if (m_NumResendAttempts >= MAX_NUM_RESEND_ATTEMPTS) - { - LogPrint (eLogWarning, "Streaming: packet was not ACKed after ", MAX_NUM_RESEND_ATTEMPTS, " attempts, terminate, rSID=", m_RecvStreamID, ", sSID=", m_SendStreamID); - m_Status = eStreamStatusReset; - Close (); - return; - } + // check for resend attempts + if (m_NumResendAttempts >= MAX_NUM_RESEND_ATTEMPTS) + { + LogPrint (eLogWarning, "Streaming: packet was not ACKed after ", MAX_NUM_RESEND_ATTEMPTS, " attempts, terminate, rSID=", m_RecvStreamID, ", sSID=", m_SendStreamID); + m_Status = eStreamStatusReset; + Close (); + return; + } - // collect packets to resend - auto ts = i2p::util::GetMillisecondsSinceEpoch (); - std::vector packets; - if (m_IsNAcked) - { - for (auto it : m_NACKedPackets) + // collect packets to resend + auto ts = i2p::util::GetMillisecondsSinceEpoch (); + std::vector packets; + if (m_IsNAcked) { - if (ts >= it->sendTime + m_RTO) + for (auto it : m_NACKedPackets) { - if (ts < it->sendTime + m_RTO*2) - it->resent = true; + if (ts >= it->sendTime + m_RTO) + { + if (ts < it->sendTime + m_RTO*2) + it->resent = true; + else + it->resent = false; + it->sendTime = ts; + packets.push_back (it); + if ((int)packets.size () >= m_NumPacketsToSend) break; + } + } + } + else + { + for (auto it : m_SentPackets) + { + if (ts >= it->sendTime + m_RTO) + { + if (ts < it->sendTime + m_RTO*2) + it->resent = true; + else + it->resent = false; + it->sendTime = ts; + packets.push_back (it); + if ((int)packets.size () >= m_NumPacketsToSend) break; + } + } + } + + // select tunnels if necessary and send + if (packets.size () > 0 && m_IsSendTime) + { + if (m_IsNAcked) m_NumResendAttempts = 1; + else if (m_IsTimeOutResend) m_NumResendAttempts++; + if (m_NumResendAttempts == 1 && m_RTO != INITIAL_RTO) + { + // loss-based CC + if (!m_IsWinDropped && LOSS_BASED_CONTROL_ENABLED) + ProcessWindowDrop (); + } + else if (m_IsTimeOutResend) + { + m_IsTimeOutResend = false; + m_RTO = INITIAL_RTO; // drop RTO to initial upon tunnels pair change + m_WindowDropTargetSize = INITIAL_WINDOW_SIZE; + m_LastWindowDropSize = 0; + m_WindowIncCounter = 0; + m_IsWinDropped = true; + m_IsFirstRttSample = true; + m_DropWindowDelaySequenceNumber = 0; + m_IsFirstACK = true; + UpdatePacingTime (); + if (m_RoutingSession) m_RoutingSession->SetSharedRoutingPath (nullptr); + if (m_NumResendAttempts & 1) + { + // pick another outbound tunnel + m_CurrentOutboundTunnel = m_LocalDestination.GetOwner ()->GetTunnelPool ()->GetNextOutboundTunnel (m_CurrentOutboundTunnel); + LogPrint (eLogWarning, "Streaming: Resend #", m_NumResendAttempts, + ", another outbound tunnel has been selected for stream with sSID=", m_SendStreamID); + } else - it->resent = false; - it->sendTime = ts; - packets.push_back (it); - if ((int)packets.size () >= m_NumPacketsToSend) break; + { + UpdateCurrentRemoteLease (); // pick another lease + LogPrint (eLogWarning, "Streaming: Resend #", m_NumResendAttempts, + ", another remote lease has been selected for stream with rSID=", m_RecvStreamID, ", sSID=", m_SendStreamID); + } } + SendPackets (packets); + m_LastSendTime = ts; + m_IsSendTime = false; + if (m_IsNAcked || m_IsResendNeeded) ScheduleSend (); } - } - else - { - for (auto it : m_SentPackets) - { - if (ts >= it->sendTime + m_RTO) - { - if (ts < it->sendTime + m_RTO*2) - it->resent = true; - else - it->resent = false; - it->sendTime = ts; - packets.push_back (it); - if ((int)packets.size () >= m_NumPacketsToSend) break; - } - } - } - - // select tunnels if necessary and send - if (packets.size () > 0 && m_IsSendTime) - { - if (m_IsNAcked) m_NumResendAttempts = 1; - else if (m_IsTimeOutResend) m_NumResendAttempts++; - if (m_NumResendAttempts == 1 && m_RTO != INITIAL_RTO) - { - // loss-based CC - if (!m_IsWinDropped && LOSS_BASED_CONTROL_ENABLED && !m_IsClientChoked) - { - LogPrint (eLogDebug, "Streaming: Packet loss, reduce window size"); - ProcessWindowDrop (); - } - } - else if (m_IsTimeOutResend) - { - m_IsTimeOutResend = false; - m_RTO = INITIAL_RTO; // drop RTO to initial upon tunnels pair change - m_WindowDropTargetSize = INITIAL_WINDOW_SIZE; - m_LastWindowDropSize = 0; - m_WindowIncCounter = 0; - m_IsWinDropped = true; - m_IsFirstRttSample = true; - m_DropWindowDelaySequenceNumber = 0; - m_IsFirstACK = true; - m_LastACKRecieveTime = 0; - m_ACKRecieveInterval = m_AckDelay; - UpdatePacingTime (); - if (m_RoutingSession) m_RoutingSession->SetSharedRoutingPath (nullptr); - if (m_NumResendAttempts & 1) - { - // pick another outbound tunnel - m_CurrentOutboundTunnel = m_LocalDestination.GetOwner ()->GetTunnelPool ()->GetNextOutboundTunnel (m_CurrentOutboundTunnel); - LogPrint (eLogWarning, "Streaming: Resend #", m_NumResendAttempts, - ", another outbound tunnel has been selected for stream with sSID=", m_SendStreamID); - } - else - { - CancelRemoteLeaseChange (); - UpdateCurrentRemoteLease (); // pick another lease - LogPrint (eLogWarning, "Streaming: Resend #", m_NumResendAttempts, - ", another remote lease has been selected for stream with rSID=", m_RecvStreamID, ", sSID=", m_SendStreamID); - } - } - SendPackets (packets); - m_LastSendTime = ts; - m_IsSendTime = false; - if (m_IsNAcked || m_IsResendNeeded || m_IsClientChoked) ScheduleSend (); - } - else if (!m_IsClientChoked) - SendBuffer (); - if (!m_IsNAcked && !m_IsResendNeeded) ScheduleResend (); - if (m_IsClientChoked) ScheduleSend (); + else + SendBuffer (); + if (!m_IsNAcked && !m_IsResendNeeded) ScheduleResend (); } void Stream::ScheduleAck (int timeout) @@ -1576,26 +1430,17 @@ namespace stream if (!remoteLeaseSet) { LogPrint (eLogWarning, "Streaming: LeaseSet ", m_RemoteIdentity->GetIdentHash ().ToBase64 (), m_RemoteLeaseSet ? " expired" : " not found"); - if (!m_IsIncoming) // outgoing - { - if (m_RemoteLeaseSet && m_RemoteLeaseSet->IsPublishedEncrypted ()) - { - m_LocalDestination.GetOwner ()->RequestDestinationWithEncryptedLeaseSet ( - std::make_shared(m_RemoteIdentity)); - return; // we keep m_RemoteLeaseSet for possible next request - } - else - { - m_RemoteLeaseSet = nullptr; - m_LocalDestination.GetOwner ()->RequestDestination (m_RemoteIdentity->GetIdentHash ()); // try to request for a next attempt - } - } - else // incoming + if (m_RemoteLeaseSet && m_RemoteLeaseSet->IsPublishedEncrypted ()) { - // just close the socket without sending FIN or RST - m_Status = eStreamStatusClosed; - AsyncClose (); - } + m_LocalDestination.GetOwner ()->RequestDestinationWithEncryptedLeaseSet ( + std::make_shared(m_RemoteIdentity)); + return; // we keep m_RemoteLeaseSet for possible next request + } + else + { + m_RemoteLeaseSet = nullptr; + m_LocalDestination.GetOwner ()->RequestDestination (m_RemoteIdentity->GetIdentHash ()); // try to request for a next attempt + } } else { @@ -1661,10 +1506,22 @@ namespace stream LogPrint (eLogWarning, "Streaming: Remote LeaseSet not found"); m_CurrentRemoteLease = nullptr; } - if (isLeaseChanged && !m_IsRemoteLeaseChangeInProgress) + if (isLeaseChanged) { - LogPrint (eLogDebug, "Streaming: RemoteLease changed, set initial window size"); - ResetWindowSize (); + // drop window to initial upon RemoteLease change + m_RTO = INITIAL_RTO; + if (m_WindowSize > INITIAL_WINDOW_SIZE) + { + m_WindowDropTargetSize = std::max (m_WindowSize/2, (float)INITIAL_WINDOW_SIZE); + m_IsWinDropped = true; + } + else + m_WindowSize = INITIAL_WINDOW_SIZE; + m_LastWindowDropSize = 0; + m_WindowIncCounter = 0; + m_IsFirstRttSample = true; + m_IsFirstACK = true; + UpdatePacingTime (); } } @@ -1680,69 +1537,29 @@ namespace stream void Stream::UpdatePacingTime () { - if (m_WindowDropTargetSize) - m_PacingTime = std::round (m_RTT*1000/m_WindowDropTargetSize); - else - m_PacingTime = std::round (m_RTT*1000/m_WindowSize); + m_PacingTime = std::round (m_RTT*1000/m_WindowSize); if (m_MinPacingTime && m_PacingTime < m_MinPacingTime) m_PacingTime = m_MinPacingTime; } void Stream::ProcessWindowDrop () { - if (m_WindowDropTargetSize) - m_WindowDropTargetSize = (m_WindowDropTargetSize / 2) * 0.75; // congestion window size and -25% to drain queue + if (m_WindowSize > m_LastWindowDropSize) + m_LastWindowDropSize = (m_LastWindowDropSize + m_WindowSize) / 2; else - { - if (m_WindowSize < m_LastWindowDropSize) - { - m_LastWindowDropSize = std::max ((m_WindowSize - MAX_WINDOW_SIZE_INC_PER_RTT), (m_WindowSize - (m_LastWindowDropSize - m_WindowSize))); - if (m_LastWindowDropSize < MIN_WINDOW_SIZE) m_LastWindowDropSize = MIN_WINDOW_SIZE; - } - else - { - m_LastWindowDropSize = std::max ((m_WindowSize - MAX_WINDOW_SIZE_INC_PER_RTT), ((m_LastWindowDropSize + m_WindowSize + m_WindowSizeTail) / 2)); - if (m_LastWindowDropSize > MAX_WINDOW_SIZE) m_LastWindowDropSize = MAX_WINDOW_SIZE; - } - m_WindowDropTargetSize = m_LastWindowDropSize * 0.75; // -25% to drain queue - } - if (m_WindowDropTargetSize < MIN_WINDOW_SIZE) - m_WindowDropTargetSize = MIN_WINDOW_SIZE; + m_LastWindowDropSize = m_WindowSize; + m_WindowDropTargetSize = m_LastWindowDropSize - (m_LastWindowDropSize / 4); // -25%; + if (m_WindowDropTargetSize < MIN_WINDOW_SIZE + 1) + m_WindowDropTargetSize = MIN_WINDOW_SIZE + 1; + m_WindowSize = m_SentPackets.size (); // stop sending now + if (m_WindowSize < MIN_WINDOW_SIZE) m_WindowSize = MIN_WINDOW_SIZE; m_WindowIncCounter = 0; // disable window growth - m_DropWindowDelaySequenceNumber = m_SequenceNumber + int(m_WindowDropTargetSize); + m_DropWindowDelaySequenceNumber = m_SequenceNumber; m_IsFirstACK = true; // ignore first RTT sample m_IsWinDropped = true; // don't drop window twice - m_WindowSizeTail = 0; UpdatePacingTime (); } - - void Stream::ResetWindowSize () - { - m_RTO = INITIAL_RTO; - if (!m_IsClientChoked) - { - if (m_WindowSize > INITIAL_WINDOW_SIZE) - { - m_WindowDropTargetSize = (float)INITIAL_WINDOW_SIZE; - m_IsWinDropped = true; - } - else - m_WindowSize = INITIAL_WINDOW_SIZE; - } - m_LastWindowDropSize = 0; - m_WindowIncCounter = 0; - m_IsFirstRttSample = true; - m_IsFirstACK = true; - m_WindowSizeTail = 0; - UpdatePacingTime (); - } - - void Stream::CancelRemoteLeaseChange () - { - m_RemoteLeaseChangeTime = 0; - m_IsRemoteLeaseChangeInProgress = false; - } - + StreamingDestination::StreamingDestination (std::shared_ptr owner, uint16_t localPort, bool gzip): m_Owner (owner), m_LocalPort (localPort), m_Gzip (gzip), m_PendingIncomingTimer (m_Owner->GetService ()), @@ -1828,20 +1645,9 @@ namespace stream DeletePacket (packet); // drop it, because previous should be connected return; } - if (m_Owner->GetStreamingMaxConcurrentStreams () > 0 && (int)m_Streams.size () > m_Owner->GetStreamingMaxConcurrentStreams ()) - { - LogPrint(eLogWarning, "Streaming: Number of streams exceeds ", m_Owner->GetStreamingMaxConcurrentStreams ()); - DeletePacket (packet); - return; - } auto incomingStream = CreateNewIncomingStream (receiveStreamID); incomingStream->HandleNextPacket (packet); // SYN - if (!incomingStream->GetRemoteLeaseSet ()) - { - LogPrint (eLogWarning, "Streaming: No remote LeaseSet for incoming stream. Terminated"); - incomingStream->Terminate (); // can't send FIN anyway - return; - } + auto ident = incomingStream->GetRemoteIdentity(); // handle saved packets if any { @@ -1943,8 +1749,7 @@ namespace stream { std::unique_lock l(m_StreamsMutex); m_Streams.erase (stream->GetRecvStreamID ()); - if (stream->IsIncoming ()) - m_IncomingStreams.erase (stream->GetSendStreamID ()); + m_IncomingStreams.erase (stream->GetSendStreamID ()); if (m_LastStream == stream) m_LastStream = nullptr; } auto ts = i2p::util::GetSecondsSinceEpoch (); @@ -1962,7 +1767,7 @@ namespace stream if (it == m_Streams.end ()) return false; auto s = it->second; - boost::asio::post (m_Owner->GetService (), [this, s] () + m_Owner->GetService ().post ([this, s] () { s->Close (); // try to send FIN s->Terminate (false); @@ -1975,7 +1780,7 @@ namespace stream { m_Acceptor = acceptor; // we must set it immediately for IsAcceptorSet auto s = shared_from_this (); - boost::asio::post (m_Owner->GetService (), [s](void) + m_Owner->GetService ().post([s](void) { // take care about incoming queue for (auto& it: s->m_PendingIncomingStreams) @@ -1994,7 +1799,7 @@ namespace stream void StreamingDestination::AcceptOnce (const Acceptor& acceptor) { - boost::asio::post (m_Owner->GetService (), [acceptor, this](void) + m_Owner->GetService ().post([acceptor, this](void) { if (!m_PendingIncomingStreams.empty ()) { diff --git a/libi2pd/Streaming.h b/libi2pd/Streaming.h index 570fdd1d..9ac84990 100644 --- a/libi2pd/Streaming.h +++ b/libi2pd/Streaming.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2025, 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,6 @@ #include #include #include "Base.h" -#include "Gzip.h" #include "I2PEndian.h" #include "Identity.h" #include "LeaseSet.h" @@ -51,22 +50,17 @@ namespace stream const size_t STREAMING_MTU = 1730; const size_t STREAMING_MTU_RATCHETS = 1812; -#if OPENSSL_PQ - const size_t MAX_PACKET_SIZE = 8192; -#else const size_t MAX_PACKET_SIZE = 4096; -#endif const size_t COMPRESSION_THRESHOLD_SIZE = 66; const int MAX_NUM_RESEND_ATTEMPTS = 10; const int INITIAL_WINDOW_SIZE = 10; - const int MIN_WINDOW_SIZE = 3; + const int MIN_WINDOW_SIZE = 2; const int MAX_WINDOW_SIZE = 512; - const int MAX_WINDOW_SIZE_INC_PER_RTT = 16; const double RTT_EWMA_ALPHA = 0.25; const double SLOWRTT_EWMA_ALPHA = 0.05; const double PREV_SPEED_KEEP_TIME_COEFF = 0.35; // 0.1 - 1 // how long will the window size stay around the previous drop level, less is longer const int MIN_RTO = 20; // in milliseconds - const int INITIAL_RTT = 1500; // in milliseconds + const int INITIAL_RTT = 8000; // in milliseconds const int INITIAL_RTO = 9000; // in milliseconds const int INITIAL_PACING_TIME = 1000 * INITIAL_RTT / INITIAL_WINDOW_SIZE; // in microseconds const int MIN_SEND_ACK_TIMEOUT = 2; // in milliseconds @@ -75,8 +69,7 @@ namespace stream const int PENDING_INCOMING_TIMEOUT = 10; // in seconds const int MAX_RECEIVE_TIMEOUT = 20; // in seconds const uint16_t DELAY_CHOKING = 60000; // in milliseconds - const uint64_t SEND_INTERVAL = 10000; // in microseconds - const uint64_t SEND_INTERVAL_VARIANCE = 2000; // in microseconds + const uint64_t SEND_INTERVAL = 1000; // in microseconds const uint64_t REQUEST_IMMEDIATE_ACK_INTERVAL = 7500; // in milliseconds const uint64_t REQUEST_IMMEDIATE_ACK_INTERVAL_VARIANCE = 3200; // in milliseconds const bool LOSS_BASED_CONTROL_ENABLED = 1; // 0/1 @@ -154,7 +147,7 @@ namespace stream SendBufferQueue (): m_Size (0) {}; ~SendBufferQueue () { CleanUp (); }; - void Add (std::shared_ptr&& buf); + void Add (std::shared_ptr buf); size_t Get (uint8_t * buf, size_t len); size_t GetSize () const { return m_Size; }; bool IsEmpty () const { return m_Buffers.empty (); }; @@ -181,9 +174,9 @@ namespace stream { public: - Stream (boost::asio::io_context& service, StreamingDestination& local, + Stream (boost::asio::io_service& service, StreamingDestination& local, std::shared_ptr remote, int port = 0); // outgoing - Stream (boost::asio::io_context& service, StreamingDestination& local); // incoming + Stream (boost::asio::io_service& service, StreamingDestination& local); // incoming ~Stream (); uint32_t GetSendStreamID () const { return m_SendStreamID; }; @@ -192,7 +185,6 @@ namespace stream std::shared_ptr GetRemoteIdentity () const { return m_RemoteIdentity; }; bool IsOpen () const { return m_Status == eStreamStatusOpen; }; bool IsEstablished () const { return m_SendStreamID; }; - bool IsIncoming () const { return m_IsIncoming; }; StreamStatus GetStatus () const { return m_Status; }; StreamingDestination& GetLocalDestination () { return m_LocalDestination; }; void ResetRoutingPath (); @@ -208,7 +200,7 @@ namespace stream size_t ReadSome (uint8_t * buf, size_t len) { return ConcatenatePackets (buf, len); }; size_t Receive (uint8_t * buf, size_t len, int timeout); - void AsyncClose() { boost::asio::post(m_Service, std::bind(&Stream::Close, shared_from_this())); }; + void AsyncClose() { m_Service.post(std::bind(&Stream::Close, shared_from_this())); }; /** only call close from destination thread, use Stream::AsyncClose for other threads */ void Close (); @@ -244,7 +236,7 @@ namespace stream void UpdateCurrentRemoteLease (bool expired = false); template - void HandleReceiveTimer (const boost::system::error_code& ecode, Buffer& buffer, ReceiveHandler handler, int remainingTimeout); + void HandleReceiveTimer (const boost::system::error_code& ecode, const Buffer& buffer, ReceiveHandler handler, int remainingTimeout); void ScheduleSend (); void HandleSendTimer (const boost::system::error_code& ecode); @@ -256,12 +248,10 @@ namespace stream void UpdatePacingTime (); void ProcessWindowDrop (); - void ResetWindowSize (); - void CancelRemoteLeaseChange (); private: - boost::asio::io_context& m_Service; + boost::asio::io_service& m_Service; uint32_t m_SendStreamID, m_RecvStreamID, m_SequenceNumber; uint32_t m_DropWindowDelaySequenceNumber; uint32_t m_TunnelsChangeSequenceNumber; @@ -269,7 +259,6 @@ namespace stream int32_t m_PreviousReceivedSequenceNumber; int32_t m_LastConfirmedReceivedSequenceNumber; // for limit inbound speed StreamStatus m_Status; - bool m_IsIncoming; bool m_IsAckSendScheduled; bool m_IsNAcked; bool m_IsFirstACK; @@ -277,18 +266,14 @@ namespace stream bool m_IsFirstRttSample; bool m_IsSendTime; bool m_IsWinDropped; - bool m_IsClientChoked; bool m_IsTimeOutResend; bool m_IsImmediateAckRequested; - bool m_IsRemoteLeaseChangeInProgress; - bool m_DoubleWinIncCounter; StreamingDestination& m_LocalDestination; std::shared_ptr m_RemoteIdentity; std::shared_ptr m_TransientVerifier; // in case of offline key std::shared_ptr m_RemoteLeaseSet; std::shared_ptr m_RoutingSession; std::shared_ptr m_CurrentRemoteLease; - std::shared_ptr m_NextRemoteLease; std::shared_ptr m_CurrentOutboundTunnel; std::queue m_ReceiveQueue; std::set m_SavedPackets; @@ -301,10 +286,10 @@ namespace stream SendBufferQueue m_SendBuffer; double m_RTT, m_SlowRTT, m_SlowRTT2; float m_WindowSize, m_LastWindowDropSize, m_WindowDropTargetSize; - int m_WindowIncCounter, m_RTO, m_AckDelay, m_PrevRTTSample, m_WindowSizeTail; + int m_WindowIncCounter, m_RTO, m_AckDelay, m_PrevRTTSample; double m_Jitter; uint64_t m_MinPacingTime, m_PacingTime, m_PacingTimeRem, // microseconds - m_LastSendTime, m_LastACKRecieveTime, m_ACKRecieveInterval, m_RemoteLeaseChangeTime, m_LastWindowIncTime; // milliseconds + m_LastSendTime; // miliseconds uint64_t m_LastACKSendTime, m_PacketACKInterval, m_PacketACKIntervalRem; // for limit inbound speed int m_NumResendAttempts, m_NumPacketsToSend; size_t m_MTU; @@ -326,7 +311,6 @@ namespace stream void SendPing (std::shared_ptr remote); void DeleteStream (std::shared_ptr stream); bool DeleteStream (uint32_t recvStreamID); - size_t GetNumStreams () const { return m_Streams.size (); }; void SetAcceptor (const Acceptor& acceptor); void ResetAcceptor (); bool IsAcceptorSet () const { return m_Acceptor != nullptr; }; @@ -384,7 +368,7 @@ namespace stream void Stream::AsyncReceive (const Buffer& buffer, ReceiveHandler handler, int timeout) { auto s = shared_from_this(); - boost::asio::post (m_Service, [s, buffer, handler, timeout](void) + m_Service.post ([s, buffer, handler, timeout](void) { if (!s->m_ReceiveQueue.empty () || s->m_Status == eStreamStatusReset) s->HandleReceiveTimer (boost::asio::error::make_error_code (boost::asio::error::operation_aborted), buffer, handler, 0); @@ -403,9 +387,9 @@ namespace stream } template - void Stream::HandleReceiveTimer (const boost::system::error_code& ecode, Buffer& buffer, ReceiveHandler handler, int remainingTimeout) + void Stream::HandleReceiveTimer (const boost::system::error_code& ecode, const Buffer& buffer, ReceiveHandler handler, int remainingTimeout) { - size_t received = ConcatenatePackets ((uint8_t *)buffer.data (), buffer.size ()); + size_t received = ConcatenatePackets (boost::asio::buffer_cast(buffer), boost::asio::buffer_size(buffer)); if (received > 0) handler (boost::system::error_code (), received); else if (ecode == boost::asio::error::operation_aborted) diff --git a/libi2pd/Tag.h b/libi2pd/Tag.h index 30b7708d..72f181a2 100644 --- a/libi2pd/Tag.h +++ b/libi2pd/Tag.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2025, The PurpleI2P Project +* Copyright (c) 2013-2023, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -12,14 +12,10 @@ #include #include #include -#include -#include #include "Base.h" -namespace i2p -{ -namespace data -{ +namespace i2p { +namespace data { template class Tag { @@ -62,22 +58,26 @@ namespace data std::string ToBase64 (size_t len = sz) const { - return i2p::data::ByteStreamToBase64 (m_Buf, len); + char str[sz*2]; + size_t l = i2p::data::ByteStreamToBase64 (m_Buf, len, str, sz*2); + return std::string (str, str + l); } std::string ToBase32 (size_t len = sz) const { - return i2p::data::ByteStreamToBase32 (m_Buf, len); + char str[sz*2]; + size_t l = i2p::data::ByteStreamToBase32 (m_Buf, len, str, sz*2); + return std::string (str, str + l); } - size_t FromBase32 (std::string_view s) + size_t FromBase32 (const std::string& s) { - return i2p::data::Base32ToByteStream (s, m_Buf, sz); + return i2p::data::Base32ToByteStream (s.c_str (), s.length (), m_Buf, sz); } - size_t FromBase64 (std::string_view s) + size_t FromBase64 (const std::string& s) { - return i2p::data::Base64ToByteStream (s, m_Buf, sz); + return i2p::data::Base64ToByteStream (s.c_str (), s.length (), m_Buf, sz); } uint8_t GetBit (int i) const diff --git a/libi2pd/Timestamp.cpp b/libi2pd/Timestamp.cpp index a22e9bde..c5c37cd7 100644 --- a/libi2pd/Timestamp.cpp +++ b/libi2pd/Timestamp.cpp @@ -60,16 +60,18 @@ namespace util static void SyncTimeWithNTP (const std::string& address) { LogPrint (eLogInfo, "Timestamp: NTP request to ", address); - boost::asio::io_context service; + boost::asio::io_service service; boost::system::error_code ec; - auto endpoints = boost::asio::ip::udp::resolver (service).resolve (address, "ntp", ec); + auto it = boost::asio::ip::udp::resolver (service).resolve ( + boost::asio::ip::udp::resolver::query (address, "ntp"), ec); if (!ec) { bool found = false; + boost::asio::ip::udp::resolver::iterator end; boost::asio::ip::udp::endpoint ep; - for (const auto& it: endpoints) + while (it != end) { - ep = it; + ep = *it; if (!ep.address ().is_unspecified ()) { if (ep.address ().is_v4 ()) @@ -86,6 +88,7 @@ namespace util } } if (found) break; + it++; } if (!found) { @@ -151,7 +154,7 @@ namespace util { m_IsRunning = true; LogPrint(eLogInfo, "Timestamp: NTP time sync starting"); - boost::asio::post (m_Service, std::bind (&NTPTimeSync::Sync, this)); + m_Service.post (std::bind (&NTPTimeSync::Sync, this)); m_Thread.reset (new std::thread (std::bind (&NTPTimeSync::Run, this))); } else diff --git a/libi2pd/Timestamp.h b/libi2pd/Timestamp.h index 00c60433..d949d416 100644 --- a/libi2pd/Timestamp.h +++ b/libi2pd/Timestamp.h @@ -52,7 +52,7 @@ namespace util bool m_IsRunning; std::unique_ptr m_Thread; - boost::asio::io_context m_Service; + boost::asio::io_service m_Service; boost::asio::deadline_timer m_Timer; int m_SyncInterval; std::vector m_NTPServersList; diff --git a/libi2pd/TransitTunnel.cpp b/libi2pd/TransitTunnel.cpp index b24c8ac5..6c2c52a7 100644 --- a/libi2pd/TransitTunnel.cpp +++ b/libi2pd/TransitTunnel.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2025, The PurpleI2P Project +* Copyright (c) 2013-2022, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -8,14 +8,9 @@ #include #include "I2PEndian.h" -#include "Crypto.h" #include "Log.h" -#include "Identity.h" -#include "RouterInfo.h" #include "RouterContext.h" #include "I2NPProtocol.h" -#include "Garlic.h" -#include "ECIESX25519AEADRatchetSession.h" #include "Tunnel.h" #include "Transports.h" #include "TransitTunnel.h" @@ -43,21 +38,6 @@ namespace tunnel i2p::transport::transports.UpdateTotalTransitTransmittedBytes (TUNNEL_DATA_MSG_SIZE); } - std::string TransitTunnel::GetNextPeerName () const - { - return i2p::data::GetIdentHashAbbreviation (GetNextIdentHash ()); - } - - void TransitTunnel::SendTunnelDataMsg (std::shared_ptr msg) - { - LogPrint (eLogError, "TransitTunnel: We are not a gateway for ", GetTunnelID ()); - } - - void TransitTunnel::HandleTunnelDataMsg (std::shared_ptr&& tunnelMsg) - { - LogPrint (eLogError, "TransitTunnel: Incoming tunnel message is not supported ", GetTunnelID ()); - } - TransitTunnelParticipant::~TransitTunnelParticipant () { } @@ -79,100 +59,45 @@ namespace tunnel auto num = m_TunnelDataMsgs.size (); if (num > 1) LogPrint (eLogDebug, "TransitTunnel: ", GetTunnelID (), "->", GetNextTunnelID (), " ", num); - if (!m_Sender) m_Sender = std::make_unique(); - m_Sender->SendMessagesTo (GetNextIdentHash (), m_TunnelDataMsgs); // send and clear + i2p::transport::transports.SendMessages (GetNextIdentHash (), m_TunnelDataMsgs); + m_TunnelDataMsgs.clear (); } } - std::string TransitTunnelParticipant::GetNextPeerName () const + void TransitTunnel::SendTunnelDataMsg (std::shared_ptr msg) { - if (m_Sender) - { - auto transport = m_Sender->GetCurrentTransport (); - if (transport) - return TransitTunnel::GetNextPeerName () + "-" + - i2p::data::RouterInfo::GetTransportName (transport->GetTransportType ()); - } - return TransitTunnel::GetNextPeerName (); - } - + LogPrint (eLogError, "TransitTunnel: We are not a gateway for ", GetTunnelID ()); + } + + void TransitTunnel::HandleTunnelDataMsg (std::shared_ptr&& tunnelMsg) + { + LogPrint (eLogError, "TransitTunnel: Incoming tunnel message is not supported ", GetTunnelID ()); + } + void TransitTunnelGateway::SendTunnelDataMsg (std::shared_ptr msg) { TunnelMessageBlock block; block.deliveryType = eDeliveryTypeLocal; block.data = msg; - std::lock_guard l(m_SendMutex); + std::unique_lock l(m_SendMutex); m_Gateway.PutTunnelDataMsg (block); } void TransitTunnelGateway::FlushTunnelDataMsgs () { - std::lock_guard l(m_SendMutex); + std::unique_lock l(m_SendMutex); m_Gateway.SendBuffer (); } - std::string TransitTunnelGateway::GetNextPeerName () const - { - const auto& sender = m_Gateway.GetSender (); - if (sender) - { - auto transport = sender->GetCurrentTransport (); - if (transport) - return TransitTunnel::GetNextPeerName () + "-" + - i2p::data::RouterInfo::GetTransportName (transport->GetTransportType ()); - } - return TransitTunnel::GetNextPeerName (); - } - void TransitTunnelEndpoint::HandleTunnelDataMsg (std::shared_ptr&& tunnelMsg) { auto newMsg = CreateEmptyTunnelDataMsg (true); EncryptTunnelMsg (tunnelMsg, newMsg); LogPrint (eLogDebug, "TransitTunnel: handle msg for endpoint ", GetTunnelID ()); - std::lock_guard l(m_HandleMutex); - if (!m_Endpoint) m_Endpoint = std::make_unique(false); // transit endpoint is always outbound - m_Endpoint->HandleDecryptedTunnelDataMsg (newMsg); + m_Endpoint.HandleDecryptedTunnelDataMsg (newMsg); } - void TransitTunnelEndpoint::FlushTunnelDataMsgs () - { - if (m_Endpoint) - { - std::lock_guard l(m_HandleMutex); - m_Endpoint->FlushI2NPMsgs (); - } - } - - void TransitTunnelEndpoint::Cleanup () - { - if (m_Endpoint) - { - std::lock_guard l(m_HandleMutex); - m_Endpoint->Cleanup (); - } - } - - std::string TransitTunnelEndpoint::GetNextPeerName () const - { - if (!m_Endpoint) return ""; - auto hash = m_Endpoint->GetCurrentHash (); - if (hash) - { - const auto& sender = m_Endpoint->GetSender (); - if (sender) - { - auto transport = sender->GetCurrentTransport (); - if (transport) - return i2p::data::GetIdentHashAbbreviation (*hash) + "-" + - i2p::data::RouterInfo::GetTransportName (transport->GetTransportType ()); - else - return i2p::data::GetIdentHashAbbreviation (*hash); - } - } - return ""; - } - std::shared_ptr CreateTransitTunnel (uint32_t receiveTunnelID, const i2p::data::IdentHash& nextIdent, uint32_t nextTunnelID, const i2p::crypto::AESKey& layerKey, const i2p::crypto::AESKey& ivKey, @@ -194,440 +119,5 @@ namespace tunnel return std::make_shared (receiveTunnelID, nextIdent, nextTunnelID, layerKey, ivKey); } } - - TransitTunnels::TransitTunnels (): - m_IsRunning (false), m_Rng(i2p::util::GetMonotonicMicroseconds ()%1000000LL) - { - } - - TransitTunnels::~TransitTunnels () - { - Stop (); - } - - void TransitTunnels::Start () - { - m_IsRunning = true; - m_Thread.reset (new std::thread (std::bind (&TransitTunnels::Run, this))); - } - - void TransitTunnels::Stop () - { - m_IsRunning = false; - m_TunnelBuildMsgQueue.WakeUp (); - if (m_Thread) - { - m_Thread->join (); - m_Thread = nullptr; - } - m_TransitTunnels.clear (); - } - - void TransitTunnels::Run () - { - i2p::util::SetThreadName("TBM"); - uint64_t lastTs = 0; - std::list > msgs; - while (m_IsRunning) - { - try - { - if (m_TunnelBuildMsgQueue.Wait (TRANSIT_TUNNELS_QUEUE_WAIT_INTERVAL, 0)) - { - m_TunnelBuildMsgQueue.GetWholeQueue (msgs); - while (!msgs.empty ()) - { - auto msg = msgs.front (); msgs.pop_front (); - if (!msg) continue; - uint8_t typeID = msg->GetTypeID (); - switch (typeID) - { - case eI2NPShortTunnelBuild: - HandleShortTransitTunnelBuildMsg (std::move (msg)); - break; - case eI2NPVariableTunnelBuild: - HandleVariableTransitTunnelBuildMsg (std::move (msg)); - break; - default: - LogPrint (eLogWarning, "TransitTunnel: Unexpected message type ", (int) typeID); - } - if (!m_IsRunning) break; - } - } - if (m_IsRunning) - { - uint64_t ts = i2p::util::GetSecondsSinceEpoch (); - if (ts >= lastTs + TUNNEL_MANAGE_INTERVAL || ts + TUNNEL_MANAGE_INTERVAL < lastTs) - { - ManageTransitTunnels (ts); - lastTs = ts; - } - } - } - catch (std::exception& ex) - { - LogPrint (eLogError, "TransitTunnel: Runtime exception: ", ex.what ()); - } - } - } - - void TransitTunnels::PostTransitTunnelBuildMsg (std::shared_ptr&& msg) - { - if (msg) m_TunnelBuildMsgQueue.Put (msg); - } - - void TransitTunnels::HandleShortTransitTunnelBuildMsg (std::shared_ptr&& msg) - { - if (!msg) return; - uint8_t * buf = msg->GetPayload(); - size_t len = msg->GetPayloadLength(); - int num = buf[0]; - LogPrint (eLogDebug, "TransitTunnel: ShortTunnelBuild ", num, " records"); - if (num > i2p::tunnel::MAX_NUM_RECORDS) - { - LogPrint (eLogError, "TransitTunnel: Too many records in ShortTunnelBuild message ", num); - return; - } - if (len < num*SHORT_TUNNEL_BUILD_RECORD_SIZE + 1) - { - LogPrint (eLogError, "TransitTunnel: ShortTunnelBuild message of ", num, " records is too short ", len); - return; - } - const uint8_t * record = buf + 1; - for (int i = 0; i < num; i++) - { - if (!memcmp (record, (const uint8_t *)i2p::context.GetRouterInfo ().GetIdentHash (), 16)) - { - LogPrint (eLogDebug, "TransitTunnel: Short request record ", i, " is ours"); - uint8_t clearText[SHORT_REQUEST_RECORD_CLEAR_TEXT_SIZE]; - if (!i2p::context.DecryptTunnelShortRequestRecord (record + SHORT_REQUEST_RECORD_ENCRYPTED_OFFSET, clearText)) - { - LogPrint (eLogWarning, "TransitTunnel: Can't decrypt short request record ", i); - return; - } - if (clearText[SHORT_REQUEST_RECORD_LAYER_ENCRYPTION_TYPE]) // not AES - { - LogPrint (eLogWarning, "TransitTunnel: Unknown layer encryption type ", clearText[SHORT_REQUEST_RECORD_LAYER_ENCRYPTION_TYPE], " in short request record"); - return; - } - auto& noiseState = i2p::context.GetCurrentNoiseState (); - uint8_t replyKey[32]; // AEAD/Chacha20/Poly1305 - i2p::crypto::AESKey layerKey, ivKey; // AES - i2p::crypto::HKDF (noiseState.m_CK, nullptr, 0, "SMTunnelReplyKey", noiseState.m_CK); - memcpy (replyKey, noiseState.m_CK + 32, 32); - i2p::crypto::HKDF (noiseState.m_CK, nullptr, 0, "SMTunnelLayerKey", noiseState.m_CK); - memcpy (layerKey, noiseState.m_CK + 32, 32); - bool isEndpoint = clearText[SHORT_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_ENDPOINT_FLAG; - if (isEndpoint) - { - i2p::crypto::HKDF (noiseState.m_CK, nullptr, 0, "TunnelLayerIVKey", noiseState.m_CK); - memcpy (ivKey, noiseState.m_CK + 32, 32); - } - else - { - if (!memcmp ((const uint8_t *)i2p::context.GetIdentHash (), clearText + SHORT_REQUEST_RECORD_NEXT_IDENT_OFFSET, 32)) // if next ident is now ours - { - LogPrint (eLogWarning, "TransitTunnel: Next ident is ours in short request record"); - return; - } - memcpy (ivKey, noiseState.m_CK , 32); - } - - // check if we accept this tunnel - std::shared_ptr transitTunnel; - uint8_t retCode = 0; - if (i2p::context.AcceptsTunnels ()) - { - auto congestionLevel = i2p::context.GetCongestionLevel (false); - if (congestionLevel < CONGESTION_LEVEL_FULL) - { - if (congestionLevel >= CONGESTION_LEVEL_MEDIUM) - { - // random reject depending on congestion level - int level = m_Rng () % (CONGESTION_LEVEL_FULL - CONGESTION_LEVEL_MEDIUM) + CONGESTION_LEVEL_MEDIUM; - if (congestionLevel > level) - retCode = 30; - } - } - else - retCode = 30; - } - else - retCode = 30; - - if (!retCode) - { - i2p::data::IdentHash nextIdent(clearText + SHORT_REQUEST_RECORD_NEXT_IDENT_OFFSET); - bool isEndpoint = clearText[SHORT_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_ENDPOINT_FLAG; - if (isEndpoint || !i2p::data::IsRouterDuplicated (nextIdent)) - { - // create new transit tunnel - transitTunnel = CreateTransitTunnel ( - bufbe32toh (clearText + SHORT_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET), - nextIdent, - bufbe32toh (clearText + SHORT_REQUEST_RECORD_NEXT_TUNNEL_OFFSET), - layerKey, ivKey, - clearText[SHORT_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_GATEWAY_FLAG, - isEndpoint); - if (!AddTransitTunnel (transitTunnel)) - retCode = 30; - } - else - // decline tunnel going to duplicated router - retCode = 30; - } - - // encrypt reply - uint8_t nonce[12]; - memset (nonce, 0, 12); - uint8_t * reply = buf + 1; - for (int j = 0; j < num; j++) - { - nonce[4] = j; // nonce is record # - if (j == i) - { - memset (reply + SHORT_RESPONSE_RECORD_OPTIONS_OFFSET, 0, 2); // no options - reply[SHORT_RESPONSE_RECORD_RET_OFFSET] = retCode; - if (!i2p::crypto::AEADChaCha20Poly1305 (reply, SHORT_TUNNEL_BUILD_RECORD_SIZE - 16, - noiseState.m_H, 32, replyKey, nonce, reply, SHORT_TUNNEL_BUILD_RECORD_SIZE, true)) // encrypt - { - LogPrint (eLogWarning, "TransitTunnel: Short reply AEAD encryption failed"); - return; - } - } - else - i2p::crypto::ChaCha20 (reply, SHORT_TUNNEL_BUILD_RECORD_SIZE, replyKey, nonce, reply); - reply += SHORT_TUNNEL_BUILD_RECORD_SIZE; - } - // send reply - auto onDrop = [transitTunnel]() - { - if (transitTunnel) - { - LogPrint (eLogDebug, "TransitTunnel: Failed to send reply for transit tunnel ", transitTunnel->GetTunnelID ()); - auto t = transitTunnel->GetCreationTime (); - if (t > i2p::tunnel::TUNNEL_EXPIRATION_TIMEOUT) - // make transit tunnel expired - transitTunnel->SetCreationTime (t - i2p::tunnel::TUNNEL_EXPIRATION_TIMEOUT); - } - }; - if (isEndpoint) - { - auto replyMsg = NewI2NPShortMessage (); - replyMsg->Concat (buf, len); - replyMsg->FillI2NPMessageHeader (eI2NPShortTunnelBuildReply, bufbe32toh (clearText + SHORT_REQUEST_RECORD_SEND_MSG_ID_OFFSET)); - if (transitTunnel) replyMsg->onDrop = onDrop; - if (memcmp ((const uint8_t *)i2p::context.GetIdentHash (), - clearText + SHORT_REQUEST_RECORD_NEXT_IDENT_OFFSET, 32)) // reply IBGW is not local? - { - i2p::crypto::HKDF (noiseState.m_CK, nullptr, 0, "RGarlicKeyAndTag", noiseState.m_CK); - uint64_t tag; - memcpy (&tag, noiseState.m_CK, 8); - // we send it to reply tunnel - i2p::transport::transports.SendMessage (clearText + SHORT_REQUEST_RECORD_NEXT_IDENT_OFFSET, - CreateTunnelGatewayMsg (bufbe32toh (clearText + SHORT_REQUEST_RECORD_NEXT_TUNNEL_OFFSET), - i2p::garlic::WrapECIESX25519Message (replyMsg, noiseState.m_CK + 32, tag))); - } - else - { - // IBGW is local - uint32_t tunnelID = bufbe32toh (clearText + SHORT_REQUEST_RECORD_NEXT_TUNNEL_OFFSET); - auto tunnel = i2p::tunnel::tunnels.GetTunnel (tunnelID); - if (tunnel) - { - tunnel->SendTunnelDataMsg (replyMsg); - tunnel->FlushTunnelDataMsgs (); - } - else - LogPrint (eLogWarning, "I2NP: Tunnel ", tunnelID, " not found for short tunnel build reply"); - } - } - else - { - auto msg = CreateI2NPMessage (eI2NPShortTunnelBuild, buf, len, - bufbe32toh (clearText + SHORT_REQUEST_RECORD_SEND_MSG_ID_OFFSET)); - if (transitTunnel) msg->onDrop = onDrop; - i2p::transport::transports.SendMessage (clearText + SHORT_REQUEST_RECORD_NEXT_IDENT_OFFSET, msg); - } - return; - } - record += SHORT_TUNNEL_BUILD_RECORD_SIZE; - } - } - - bool TransitTunnels::HandleBuildRequestRecords (int num, uint8_t * records, uint8_t * clearText) - { - for (int i = 0; i < num; i++) - { - uint8_t * record = records + i*TUNNEL_BUILD_RECORD_SIZE; - if (!memcmp (record + BUILD_REQUEST_RECORD_TO_PEER_OFFSET, (const uint8_t *)i2p::context.GetRouterInfo ().GetIdentHash (), 16)) - { - LogPrint (eLogDebug, "TransitTunnel: Build request record ", i, " is ours"); - if (!i2p::context.DecryptTunnelBuildRecord (record + BUILD_REQUEST_RECORD_ENCRYPTED_OFFSET, clearText)) - { - LogPrint (eLogWarning, "TransitTunnel: Failed to decrypt tunnel build record"); - return false; - } - if (!memcmp ((const uint8_t *)i2p::context.GetIdentHash (), clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET, 32) && // if next ident is now ours - !(clearText[ECIES_BUILD_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_ENDPOINT_FLAG)) // and not endpoint - { - LogPrint (eLogWarning, "TransitTunnel: Next ident is ours in tunnel build record"); - return false; - } - uint8_t retCode = 0; - // decide if we should accept tunnel - bool accept = i2p::context.AcceptsTunnels (); - if (accept) - { - auto congestionLevel = i2p::context.GetCongestionLevel (false); - if (congestionLevel >= CONGESTION_LEVEL_MEDIUM) - { - if (congestionLevel < CONGESTION_LEVEL_FULL) - { - // random reject depending on congestion level - int level = m_Rng () % (CONGESTION_LEVEL_FULL - CONGESTION_LEVEL_MEDIUM) + CONGESTION_LEVEL_MEDIUM; - if (congestionLevel > level) - accept = false; - } - else - accept = false; - } - } - - if (accept) - { - i2p::data::IdentHash nextIdent(clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET); - bool isEndpoint = clearText[ECIES_BUILD_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_ENDPOINT_FLAG; - if (isEndpoint || !i2p::data::IsRouterDuplicated (nextIdent)) - { - auto transitTunnel = CreateTransitTunnel ( - bufbe32toh (clearText + ECIES_BUILD_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET), - nextIdent, - bufbe32toh (clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET), - clearText + ECIES_BUILD_REQUEST_RECORD_LAYER_KEY_OFFSET, - clearText + ECIES_BUILD_REQUEST_RECORD_IV_KEY_OFFSET, - clearText[ECIES_BUILD_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_GATEWAY_FLAG, - isEndpoint); - if (!AddTransitTunnel (transitTunnel)) - retCode = 30; - } - else - // decline tunnel going to duplicated router - retCode = 30; - } - else - retCode = 30; // always reject with bandwidth reason (30) - - // replace record to reply - memset (record + ECIES_BUILD_RESPONSE_RECORD_OPTIONS_OFFSET, 0, 2); // no options - record[ECIES_BUILD_RESPONSE_RECORD_RET_OFFSET] = retCode; - // encrypt reply - i2p::crypto::CBCEncryption encryption; - for (int j = 0; j < num; j++) - { - uint8_t * reply = records + j*TUNNEL_BUILD_RECORD_SIZE; - if (j == i) - { - uint8_t nonce[12]; - memset (nonce, 0, 12); - auto& noiseState = i2p::context.GetCurrentNoiseState (); - if (!i2p::crypto::AEADChaCha20Poly1305 (reply, TUNNEL_BUILD_RECORD_SIZE - 16, - noiseState.m_H, 32, noiseState.m_CK, nonce, reply, TUNNEL_BUILD_RECORD_SIZE, true)) // encrypt - { - LogPrint (eLogWarning, "TransitTunnel: Reply AEAD encryption failed"); - return false; - } - } - else - { - encryption.SetKey (clearText + ECIES_BUILD_REQUEST_RECORD_REPLY_KEY_OFFSET); - encryption.Encrypt(reply, TUNNEL_BUILD_RECORD_SIZE, clearText + ECIES_BUILD_REQUEST_RECORD_REPLY_IV_OFFSET, reply); - } - } - return true; - } - } - return false; - } - - void TransitTunnels::HandleVariableTransitTunnelBuildMsg (std::shared_ptr&& msg) - { - if (!msg) return; - uint8_t * buf = msg->GetPayload(); - size_t len = msg->GetPayloadLength(); - int num = buf[0]; - LogPrint (eLogDebug, "TransitTunnel: VariableTunnelBuild ", num, " records"); - if (num > i2p::tunnel::MAX_NUM_RECORDS) - { - LogPrint (eLogError, "TransitTunnle: Too many records in VaribleTunnelBuild message ", num); - return; - } - if (len < num*TUNNEL_BUILD_RECORD_SIZE + 1) - { - LogPrint (eLogError, "TransitTunnel: VaribleTunnelBuild message of ", num, " records is too short ", len); - return; - } - uint8_t clearText[ECIES_BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE]; - if (HandleBuildRequestRecords (num, buf + 1, clearText)) - { - if (clearText[ECIES_BUILD_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_ENDPOINT_FLAG) // we are endpoint of outboud tunnel - { - // so we send it to reply tunnel - i2p::transport::transports.SendMessage (clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET, - CreateTunnelGatewayMsg (bufbe32toh (clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET), - eI2NPVariableTunnelBuildReply, buf, len, - bufbe32toh (clearText + ECIES_BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET))); - } - else - i2p::transport::transports.SendMessage (clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET, - CreateI2NPMessage (eI2NPVariableTunnelBuild, buf, len, - bufbe32toh (clearText + ECIES_BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET))); - } - } - - bool TransitTunnels::AddTransitTunnel (std::shared_ptr tunnel) - { - if (tunnels.AddTunnel (tunnel)) - m_TransitTunnels.push_back (tunnel); - else - { - LogPrint (eLogError, "TransitTunnel: Tunnel with id ", tunnel->GetTunnelID (), " already exists"); - return false; - } - return true; - } - - void TransitTunnels::ManageTransitTunnels (uint64_t ts) - { - for (auto it = m_TransitTunnels.begin (); it != m_TransitTunnels.end ();) - { - auto tunnel = *it; - if (ts > tunnel->GetCreationTime () + TUNNEL_EXPIRATION_TIMEOUT || - ts + TUNNEL_EXPIRATION_TIMEOUT < tunnel->GetCreationTime ()) - { - LogPrint (eLogDebug, "TransitTunnel: Transit tunnel with id ", tunnel->GetTunnelID (), " expired"); - tunnels.RemoveTunnel (tunnel->GetTunnelID ()); - it = m_TransitTunnels.erase (it); - } - else - { - tunnel->Cleanup (); - it++; - } - } - } - - int TransitTunnels::GetTransitTunnelsExpirationTimeout () - { - int timeout = 0; - uint32_t ts = i2p::util::GetSecondsSinceEpoch (); - // TODO: possible race condition with I2PControl - for (const auto& it : m_TransitTunnels) - { - int t = it->GetCreationTime () + TUNNEL_EXPIRATION_TIMEOUT - ts; - if (t > timeout) timeout = t; - } - return timeout; - } } } diff --git a/libi2pd/TransitTunnel.h b/libi2pd/TransitTunnel.h index 34bcc79f..f83007a9 100644 --- a/libi2pd/TransitTunnel.h +++ b/libi2pd/TransitTunnel.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2025, The PurpleI2P Project +* Copyright (c) 2013-2023, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -10,11 +10,10 @@ #define TRANSIT_TUNNEL_H__ #include -#include +#include #include #include #include "Crypto.h" -#include "Queue.h" #include "I2NPProtocol.h" #include "TunnelEndpoint.h" #include "TunnelGateway.h" @@ -33,13 +32,11 @@ namespace tunnel const i2p::crypto::AESKey& layerKey, const i2p::crypto::AESKey& ivKey); virtual size_t GetNumTransmittedBytes () const { return 0; }; - virtual std::string GetNextPeerName () const; // implements TunnelBase void SendTunnelDataMsg (std::shared_ptr msg) override; void HandleTunnelDataMsg (std::shared_ptr&& tunnelMsg) override; void EncryptTunnelMsg (std::shared_ptr in, std::shared_ptr out) override; - private: i2p::crypto::AESKey m_LayerKey, m_IVKey; @@ -58,15 +55,13 @@ namespace tunnel ~TransitTunnelParticipant (); size_t GetNumTransmittedBytes () const override { return m_NumTransmittedBytes; }; - std::string GetNextPeerName () const override; void HandleTunnelDataMsg (std::shared_ptr&& tunnelMsg) override; void FlushTunnelDataMsgs () override; private: size_t m_NumTransmittedBytes; - std::list > m_TunnelDataMsgs; - std::unique_ptr m_Sender; + std::vector > m_TunnelDataMsgs; }; class TransitTunnelGateway: public TransitTunnel @@ -77,13 +72,12 @@ namespace tunnel const i2p::data::IdentHash& nextIdent, uint32_t nextTunnelID, const i2p::crypto::AESKey& layerKey, const i2p::crypto::AESKey& ivKey): TransitTunnel (receiveTunnelID, nextIdent, nextTunnelID, - layerKey, ivKey), m_Gateway(*this) {}; + layerKey, ivKey), m_Gateway(this) {}; void SendTunnelDataMsg (std::shared_ptr msg) override; void FlushTunnelDataMsgs () override; size_t GetNumTransmittedBytes () const override { return m_Gateway.GetNumSentBytes (); }; - std::string GetNextPeerName () const override; - + private: std::mutex m_SendMutex; @@ -97,68 +91,23 @@ namespace tunnel TransitTunnelEndpoint (uint32_t receiveTunnelID, const i2p::data::IdentHash& nextIdent, uint32_t nextTunnelID, const i2p::crypto::AESKey& layerKey, const i2p::crypto::AESKey& ivKey): - TransitTunnel (receiveTunnelID, nextIdent, nextTunnelID, layerKey, ivKey) {}; + TransitTunnel (receiveTunnelID, nextIdent, nextTunnelID, layerKey, ivKey), + m_Endpoint (false) {}; // transit endpoint is always outbound + + void Cleanup () override { m_Endpoint.Cleanup (); } - void Cleanup () override; - void HandleTunnelDataMsg (std::shared_ptr&& tunnelMsg) override; - void FlushTunnelDataMsgs () override; - size_t GetNumTransmittedBytes () const override { return m_Endpoint ? m_Endpoint->GetNumReceivedBytes () : 0; } - std::string GetNextPeerName () const override; - + size_t GetNumTransmittedBytes () const override { return m_Endpoint.GetNumReceivedBytes (); } + private: - std::mutex m_HandleMutex; - std::unique_ptr m_Endpoint; + TunnelEndpoint m_Endpoint; }; std::shared_ptr CreateTransitTunnel (uint32_t receiveTunnelID, const i2p::data::IdentHash& nextIdent, uint32_t nextTunnelID, const i2p::crypto::AESKey& layerKey, const i2p::crypto::AESKey& ivKey, bool isGateway, bool isEndpoint); - - - const int TRANSIT_TUNNELS_QUEUE_WAIT_INTERVAL = 10; // in seconds - - class TransitTunnels - { - public: - - TransitTunnels (); - ~TransitTunnels (); - - void Start (); - void Stop (); - void PostTransitTunnelBuildMsg (std::shared_ptr&& msg); - - size_t GetNumTransitTunnels () const { return m_TransitTunnels.size (); } - int GetTransitTunnelsExpirationTimeout (); - - private: - - bool AddTransitTunnel (std::shared_ptr tunnel); - void ManageTransitTunnels (uint64_t ts); - - void HandleShortTransitTunnelBuildMsg (std::shared_ptr&& msg); - void HandleVariableTransitTunnelBuildMsg (std::shared_ptr&& msg); - bool HandleBuildRequestRecords (int num, uint8_t * records, uint8_t * clearText); - - void Run (); - - private: - - volatile bool m_IsRunning; - std::unique_ptr m_Thread; - std::list > m_TransitTunnels; - i2p::util::Queue > m_TunnelBuildMsgQueue; - std::mt19937 m_Rng; - - public: - - // for HTTP only - const auto& GetTransitTunnels () const { return m_TransitTunnels; }; - size_t GetTunnelBuildMsgQueueSize () const { return m_TunnelBuildMsgQueue.GetSize (); }; - }; } } diff --git a/libi2pd/TransportSession.h b/libi2pd/TransportSession.h index 2cff0b1f..c6bf0de3 100644 --- a/libi2pd/TransportSession.h +++ b/libi2pd/TransportSession.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2025, The PurpleI2P Project +* Copyright (c) 2013-2024, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -10,7 +10,7 @@ #define TRANSPORT_SESSION_H__ #include -#include +#include #include #include #include @@ -28,51 +28,45 @@ namespace transport const size_t IPV6_HEADER_SIZE = 40; const size_t UDP_HEADER_SIZE = 8; - template class SignedData { public: - SignedData (): m_Size(0) {} + SignedData () {} SignedData (const SignedData& other) { - m_Size = other.m_Size; - memcpy (m_Buf, other.m_Buf, m_Size); + m_Stream << other.m_Stream.rdbuf (); } void Reset () { - m_Size = 0; + m_Stream.str(""); } - size_t Insert (const uint8_t * buf, size_t len) + void Insert (const uint8_t * buf, size_t len) { - if (m_Size + len > sz) len = sz - m_Size; - memcpy (m_Buf + m_Size, buf, len); - m_Size += len; - return len; + m_Stream.write ((char *)buf, len); } template void Insert (T t) { - Insert ((const uint8_t *)&t, sizeof (T)); + m_Stream.write ((char *)&t, sizeof (T)); } bool Verify (std::shared_ptr ident, const uint8_t * signature) const { - return ident->Verify (m_Buf, m_Size, signature); + return ident->Verify ((const uint8_t *)m_Stream.str ().c_str (), m_Stream.str ().size (), signature); } void Sign (const i2p::data::PrivateKeys& keys, uint8_t * signature) const { - keys.Sign (m_Buf, m_Size, signature); + keys.Sign ((const uint8_t *)m_Stream.str ().c_str (), m_Stream.str ().size (), signature); } private: - uint8_t m_Buf[sz]; - size_t m_Size; + std::stringstream m_Stream; }; const int64_t TRANSPORT_SESSION_SLOWNESS_THRESHOLD = 500; // in milliseconds @@ -150,14 +144,9 @@ namespace transport void SetLastActivityTimestamp (uint64_t ts) { m_LastActivityTimestamp = ts; }; virtual uint32_t GetRelayTag () const { return 0; }; - virtual void SendLocalRouterInfo (bool update = false) - { - std::list > msgs{ CreateDatabaseStoreMsg () }; - SendI2NPMessages (msgs); - }; - virtual void SendI2NPMessages (std::list >& msgs) = 0; + virtual void SendLocalRouterInfo (bool update = false) { SendI2NPMessages ({ CreateDatabaseStoreMsg () }); }; + virtual void SendI2NPMessages (const std::vector >& msgs) = 0; virtual bool IsEstablished () const = 0; - virtual i2p::data::RouterInfo::SupportedTransports GetTransportType () const = 0; private: diff --git a/libi2pd/Transports.cpp b/libi2pd/Transports.cpp index 98dbcd94..549efb63 100644 --- a/libi2pd/Transports.cpp +++ b/libi2pd/Transports.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2025, 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 transport { template EphemeralKeysSupplier::EphemeralKeysSupplier (int size): - m_QueueSize (size), m_IsRunning (false) + m_QueueSize (size), m_IsRunning (false), m_Thread (nullptr) { } @@ -39,7 +39,7 @@ namespace transport void EphemeralKeysSupplier::Start () { m_IsRunning = true; - m_Thread.reset (new std::thread (std::bind (&EphemeralKeysSupplier::Run, this))); + m_Thread = new std::thread (std::bind (&EphemeralKeysSupplier::Run, this)); } template @@ -53,15 +53,9 @@ namespace transport if (m_Thread) { m_Thread->join (); - m_Thread = nullptr; + delete m_Thread; + m_Thread = 0; } - if (!m_Queue.empty ()) - { - // clean up queue - std::queue > tmp; - std::swap (m_Queue, tmp); - } - m_KeysPool.CleanUpMt (); } template @@ -72,19 +66,18 @@ namespace transport while (m_IsRunning) { int num, total = 0; - while ((num = m_QueueSize - (int)m_Queue.size ()) > 0 && total < m_QueueSize) + while ((num = m_QueueSize - (int)m_Queue.size ()) > 0 && total < 10) { CreateEphemeralKeys (num); total += num; } - if (total > m_QueueSize) + if (total >= 10) { LogPrint (eLogWarning, "Transports: ", total, " ephemeral keys generated at the time"); std::this_thread::sleep_for (std::chrono::seconds(1)); // take a break } else { - m_KeysPool.CleanUpMt (); std::unique_lock l(m_AcquiredMutex); if (!m_IsRunning) break; m_Acquired.wait (l); // wait for element gets acquired @@ -99,7 +92,7 @@ namespace transport { for (int i = 0; i < num; i++) { - auto pair = m_KeysPool.AcquireSharedMt (); + auto pair = std::make_shared (); pair->GenerateKeys (); std::unique_lock l(m_AcquiredMutex); m_Queue.push (pair); @@ -121,7 +114,7 @@ namespace transport } } // queue is empty, create new - auto pair = m_KeysPool.AcquireSharedMt (); + auto pair = std::make_shared (); pair->GenerateKeys (); return pair; } @@ -131,12 +124,12 @@ namespace transport { if (pair) { - std::unique_lock l(m_AcquiredMutex); + std::unique_lockl(m_AcquiredMutex); if ((int)m_Queue.size () < 2*m_QueueSize) m_Queue.push (pair); } else - LogPrint(eLogError, "Transports: Return null keys"); + LogPrint(eLogError, "Transports: Return null DHKeys"); } void Peer::UpdateParams (std::shared_ptr router) @@ -156,12 +149,11 @@ namespace transport m_IsOnline (true), m_IsRunning (false), m_IsNAT (true), m_CheckReserved(true), m_Thread (nullptr), m_Service (nullptr), m_Work (nullptr), m_PeerCleanupTimer (nullptr), m_PeerTestTimer (nullptr), m_UpdateBandwidthTimer (nullptr), m_SSU2Server (nullptr), m_NTCP2Server (nullptr), - m_X25519KeysPairSupplier (NUM_X25519_PRE_GENERATED_KEYS), + 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_InBandwidth15s (0), m_OutBandwidth15s (0), m_TransitBandwidth15s (0), - m_InBandwidth5m (0), m_OutBandwidth5m (0), m_TransitBandwidth5m (0), - m_Rng(i2p::util::GetMonotonicMicroseconds () % 1000000LL) + m_InBandwidth5m (0), m_OutBandwidth5m (0), m_TransitBandwidth5m (0) { } @@ -182,8 +174,8 @@ namespace transport { if (!m_Service) { - m_Service = new boost::asio::io_context (); - m_Work = new boost::asio::executor_work_guard (m_Service->get_executor ()); + m_Service = new boost::asio::io_service (); + m_Work = new boost::asio::io_service::work (*m_Service); m_PeerCleanupTimer = new boost::asio::deadline_timer (*m_Service); m_PeerTestTimer = new boost::asio::deadline_timer (*m_Service); m_UpdateBandwidthTimer = new boost::asio::deadline_timer (*m_Service); @@ -257,7 +249,7 @@ namespace transport if (!address.empty ()) { boost::system::error_code ec; - auto addr = boost::asio::ip::make_address (address, ec); + auto addr = boost::asio::ip::address::from_string (address, ec); if (!ec) { if (m_NTCP2Server) m_NTCP2Server->SetLocalAddress (addr); @@ -283,7 +275,7 @@ namespace transport if (!address.empty ()) { boost::system::error_code ec; - auto addr = boost::asio::ip::make_address (address, ec); + auto addr = boost::asio::ip::address::from_string (address, ec); if (!ec) { if (m_NTCP2Server) m_NTCP2Server->SetLocalAddress (addr); @@ -310,7 +302,7 @@ namespace transport if (!address.empty ()) { boost::system::error_code ec; - auto addr = boost::asio::ip::make_address (address, ec); + auto addr = boost::asio::ip::address::from_string (address, ec); if (!ec && m_NTCP2Server && i2p::util::net::IsYggdrasilAddress (addr)) m_NTCP2Server->SetLocalAddress (addr); } @@ -339,7 +331,7 @@ namespace transport if (m_IsNAT) { - m_PeerTestTimer->expires_from_now (boost::posix_time::seconds(PEER_TEST_INTERVAL + m_Rng() % PEER_TEST_INTERVAL_VARIANCE)); + m_PeerTestTimer->expires_from_now (boost::posix_time::minutes(PEER_TEST_INTERVAL)); m_PeerTestTimer->async_wait (std::bind (&Transports::HandlePeerTestTimer, this, std::placeholders::_1)); } } @@ -455,22 +447,18 @@ namespace transport return std::max (bwCongestionLevel, tbwCongestionLevel); } - std::future > Transports::SendMessage (const i2p::data::IdentHash& ident, std::shared_ptr msg) + void Transports::SendMessage (const i2p::data::IdentHash& ident, std::shared_ptr msg) { if (m_IsOnline) - return SendMessages (ident, { msg }); - return {}; // invalid future + SendMessages (ident, std::vector > {msg }); } - std::future > Transports::SendMessages (const i2p::data::IdentHash& ident, std::list >&& msgs) + void Transports::SendMessages (const i2p::data::IdentHash& ident, const std::vector >& msgs) { - return boost::asio::post (*m_Service, boost::asio::use_future ([this, ident, msgs = std::move(msgs)] () mutable - { - return PostMessages (ident, msgs); - })); - } - - std::shared_ptr Transports::PostMessages (const i2p::data::IdentHash& ident, std::list >& msgs) + m_Service->post (std::bind (&Transports::PostMessages, this, ident, msgs)); + } + + void Transports::PostMessages (i2p::data::IdentHash ident, std::vector > msgs) { if (ident == i2p::context.GetRouterInfo ().GetIdentHash ()) { @@ -478,30 +466,25 @@ namespace transport for (auto& it: msgs) m_LoopbackHandler.PutNextMessage (std::move (it)); m_LoopbackHandler.Flush (); - return nullptr; + return; } - if(RoutesRestricted() && !IsRestrictedPeer(ident)) return nullptr; + if(RoutesRestricted() && !IsRestrictedPeer(ident)) return; std::shared_ptr peer; - { - std::lock_guard l(m_PeersMutex); - auto it = m_Peers.find (ident); - if (it != m_Peers.end ()) - peer = it->second; - } - if (!peer) + auto it = m_Peers.find (ident); + if (it == m_Peers.end ()) { // check if not banned - if (i2p::data::IsRouterBanned (ident)) return nullptr; // don't create peer to unreachable router + if (i2p::data::IsRouterBanned (ident)) return; // don't create peer to unreachable router // try to connect bool connected = false; try { auto r = netdb.FindRouter (ident); - if (r && (r->IsUnreachable () || !r->IsReachableFrom (i2p::context.GetRouterInfo ()))) return nullptr; // router found but non-reachable - - peer = std::make_shared(r, i2p::util::GetSecondsSinceEpoch ()); - { - std::lock_guard l(m_PeersMutex); + if (r && (r->IsUnreachable () || !r->IsReachableFrom (i2p::context.GetRouterInfo ()))) return; // router found but non-reachable + { + auto ts = i2p::util::GetSecondsSinceEpoch (); + peer = std::make_shared(r, ts); + std::unique_lock l(m_PeersMutex); peer = m_Peers.emplace (ident, peer).first->second; } if (peer) @@ -511,16 +494,14 @@ namespace transport { LogPrint (eLogError, "Transports: PostMessages exception:", ex.what ()); } - if (!connected) return nullptr; + if (!connected) return; } + else + peer = it->second; - if (!peer) return nullptr; + if (!peer) return; if (peer->IsConnected ()) - { - auto session = peer->sessions.front (); - if (session) session->SendI2NPMessages (msgs); - return session; - } + peer->sessions.front ()->SendI2NPMessages (msgs); else { auto sz = peer->delayedMessages.size (); @@ -531,44 +512,31 @@ namespace transport if (i2p::data::IsRouterBanned (ident)) { LogPrint (eLogWarning, "Transports: Router ", ident.ToBase64 (), " is banned. Peer dropped"); - std::lock_guard l(m_PeersMutex); + std::unique_lock l(m_PeersMutex); m_Peers.erase (ident); - return nullptr; + return; } } - if (sz > MAX_NUM_DELAYED_MESSAGES/2) - { - for (auto& it1: msgs) - if (it1->onDrop) - it1->Drop (); // drop earlier because we can handle it - else - peer->delayedMessages.push_back (it1); - } - else - peer->delayedMessages.splice (peer->delayedMessages.end (), msgs); + for (auto& it1: msgs) + if (sz > MAX_NUM_DELAYED_MESSAGES/2 && it1->onDrop) + it1->Drop (); // drop earlier because we can handle it + else + peer->delayedMessages.push_back (it1); } else { LogPrint (eLogWarning, "Transports: Delayed messages queue size to ", ident.ToBase64 (), " exceeds ", MAX_NUM_DELAYED_MESSAGES); - std::lock_guard l(m_PeersMutex); + std::unique_lock l(m_PeersMutex); m_Peers.erase (ident); } } - return nullptr; } bool Transports::ConnectToPeer (const i2p::data::IdentHash& ident, std::shared_ptr peer) { if (!peer->router) // reconnect - { - auto r = netdb.FindRouter (ident); // try to get new one from netdb - if (r) - { - peer->SetRouter (r); - r->CancelBufferToDelete (); - } - } + peer->SetRouter (netdb.FindRouter (ident)); // try to get new one from netdb if (peer->router) // we have RI already { if (peer->priority.empty ()) @@ -634,7 +602,7 @@ namespace transport if (!i2p::context.IsLimitedConnectivity () && peer->router->IsReachableFrom (i2p::context.GetRouterInfo ())) i2p::data::netdb.SetUnreachable (ident, true); // we are here because all connection attempts failed but router claimed them peer->Done (); - std::lock_guard l(m_PeersMutex); + std::unique_lock l(m_PeersMutex); m_Peers.erase (ident); return false; } @@ -642,7 +610,7 @@ namespace transport { LogPrint (eLogWarning, "Transports: Router ", ident.ToBase64 (), " is banned. Peer dropped"); peer->Done (); - std::lock_guard l(m_PeersMutex); + std::unique_lock l(m_PeersMutex); m_Peers.erase (ident); return false; } @@ -655,7 +623,7 @@ namespace transport return true; } - void Transports::SetPriority (std::shared_ptr peer) + void Transports::SetPriority (std::shared_ptr peer) const { static const std::vector ntcp2Priority = @@ -680,21 +648,8 @@ namespace transport auto directTransports = compatibleTransports & peer->router->GetPublishedTransports (); peer->numAttempts = 0; peer->priority.clear (); - - std::shared_ptr profile; - if (peer->router->HasProfile ()) profile = peer->router->GetProfile (); // only if in memory - bool ssu2 = false; // NTCP2 by default - bool isReal = profile ? profile->IsReal () : true; - if (isReal) - { - ssu2 = m_Rng () & 1; // 1/2 - if (ssu2 && !profile) - { - profile = peer->router->GetProfile (); // load profile if necessary - isReal = profile->IsReal (); - if (!isReal) ssu2 = false; // try NTCP2 if router is not confirmed real - } - } + bool isReal = peer->router->GetProfile ()->IsReal (); + bool ssu2 = isReal ? (rand () & 1) : false; // try NTCP2 if router is not confirmed real const auto& priority = ssu2 ? ssu2Priority : ntcp2Priority; if (directTransports) { @@ -717,64 +672,32 @@ namespace transport if (transport & compatibleTransports) peer->priority.push_back (transport); } - if (peer->priority.empty ()) - { - // try recently connected SSU2 if any - auto supportedTransports = context.GetRouterInfo ().GetCompatibleTransports (false) & - peer->router->GetCompatibleTransports (false); - if ((supportedTransports & (i2p::data::RouterInfo::eSSU2V4 | i2p::data::RouterInfo::eSSU2V6)) && - peer->router->HasProfile ()) - { - auto ep = peer->router->GetProfile ()->GetLastEndpoint (); - if (!ep.address ().is_unspecified () && ep.port ()) - { - if (ep.address ().is_v4 ()) - { - if ((supportedTransports & i2p::data::RouterInfo::eSSU2V4) && - m_SSU2Server->IsConnectedRecently (ep, false)) - peer->priority.push_back (i2p::data::RouterInfo::eSSU2V4); - } - else if (ep.address ().is_v6 ()) - { - if ((supportedTransports & i2p::data::RouterInfo::eSSU2V6) && - m_SSU2Server->IsConnectedRecently (ep)) - peer->priority.push_back (i2p::data::RouterInfo::eSSU2V6); - } - } - } - } } void Transports::RequestComplete (std::shared_ptr r, const i2p::data::IdentHash& ident) { - boost::asio::post (*m_Service, std::bind (&Transports::HandleRequestComplete, this, r, ident)); + m_Service->post (std::bind (&Transports::HandleRequestComplete, this, r, ident)); } void Transports::HandleRequestComplete (std::shared_ptr r, i2p::data::IdentHash ident) { - std::shared_ptr peer; + auto it = m_Peers.find (ident); + if (it != m_Peers.end ()) { - std::lock_guard l(m_PeersMutex); - auto it = m_Peers.find (ident); - if (it != m_Peers.end ()) + if (r) { - if (r) - peer = it->second; - else - m_Peers.erase (it); - } + LogPrint (eLogDebug, "Transports: RouterInfo for ", ident.ToBase64 (), " found, trying to connect"); + it->second->SetRouter (r); + if (!it->second->IsConnected ()) + ConnectToPeer (ident, it->second); + } + else + { + LogPrint (eLogWarning, "Transports: RouterInfo not found, failed to send messages"); + std::unique_lock l(m_PeersMutex); + m_Peers.erase (it); + } } - - if (peer && !peer->router && r) - { - LogPrint (eLogDebug, "Transports: RouterInfo for ", ident.ToBase64 (), " found, trying to connect"); - peer->SetRouter (r); - if (!peer->IsConnected ()) - ConnectToPeer (ident, peer); - } - else if (!r) - LogPrint (eLogInfo, "Transports: RouterInfo not found, failed to send messages"); - } void Transports::DetectExternalIP () @@ -813,7 +736,7 @@ namespace transport } else { - testDelay += PEER_TEST_DELAY_INTERVAL + m_Rng() % PEER_TEST_DELAY_INTERVAL_VARIANCE; + testDelay += PEER_TEST_DELAY_INTERVAL + rand() % PEER_TEST_DELAY_INTERVAL_VARIANCE; if (m_Service) { auto delayTimer = std::make_shared(*m_Service); @@ -851,7 +774,7 @@ namespace transport } else { - testDelay += PEER_TEST_DELAY_INTERVAL + m_Rng() % PEER_TEST_DELAY_INTERVAL_VARIANCE; + testDelay += PEER_TEST_DELAY_INTERVAL + rand() % PEER_TEST_DELAY_INTERVAL_VARIANCE; if (m_Service) { auto delayTimer = std::make_shared(*m_Service); @@ -884,7 +807,7 @@ namespace transport void Transports::PeerConnected (std::shared_ptr session) { - boost::asio::post (*m_Service, [session, this]() + m_Service->post([session, this]() { auto remoteIdentity = session->GetRemoteIdentity (); if (!remoteIdentity) return; @@ -908,11 +831,7 @@ namespace transport auto transport = peer->priority[peer->numAttempts-1]; if (transport == i2p::data::RouterInfo::eNTCP2V4 || transport == i2p::data::RouterInfo::eNTCP2V6 || transport == i2p::data::RouterInfo::eNTCP2V6Mesh) - i2p::data::UpdateRouterProfile (ident, - [](std::shared_ptr profile) - { - if (profile) profile->Connected (); // outgoing NTCP2 connection if always real - }); + peer->router->GetProfile ()->Connected (); // outgoing NTCP2 connection if always real i2p::data::netdb.SetUnreachable (ident, false); // clear unreachable } peer->numAttempts = 0; @@ -921,7 +840,7 @@ namespace transport if (it->second->delayedMessages.size () > 0) { // check if first message is our DatabaseStore (publishing) - auto firstMsg = peer->delayedMessages.front (); + auto firstMsg = peer->delayedMessages[0]; if (firstMsg && firstMsg->GetTypeID () == eI2NPDatabaseStore && i2p::data::IdentHash(firstMsg->GetPayload () + DATABASE_STORE_KEY_OFFSET) == i2p::context.GetIdentHash ()) sendDatabaseStore = false; // we have it in the list already @@ -931,7 +850,8 @@ namespace transport else session->SetTerminationTimeout (10); // most likely it's publishing, no follow-up messages expected, set timeout to 10 seconds peer->sessions.push_back (session); - session->SendI2NPMessages (peer->delayedMessages); // send and clear + session->SendI2NPMessages (peer->delayedMessages); + peer->delayedMessages.clear (); } else // incoming connection or peer test { @@ -942,21 +862,14 @@ namespace transport return; } if (!session->IsOutgoing ()) // incoming - { - std::list > msgs{ CreateDatabaseStoreMsg () }; - session->SendI2NPMessages (msgs); // send DatabaseStore - } + session->SendI2NPMessages ({ CreateDatabaseStoreMsg () }); // send DatabaseStore auto r = i2p::data::netdb.FindRouter (ident); // router should be in netdb after SessionConfirmed - i2p::data::UpdateRouterProfile (ident, - [](std::shared_ptr profile) - { - if (profile) profile->Connected (); - }); + if (r) r->GetProfile ()->Connected (); auto ts = i2p::util::GetSecondsSinceEpoch (); auto peer = std::make_shared(r, ts); peer->sessions.push_back (session); peer->router = nullptr; - std::lock_guard l(m_PeersMutex); + std::unique_lock l(m_PeersMutex); m_Peers.emplace (ident, peer); } }); @@ -964,7 +877,7 @@ namespace transport void Transports::PeerDisconnected (std::shared_ptr session) { - boost::asio::post (*m_Service, [session, this]() + m_Service->post([session, this]() { auto remoteIdentity = session->GetRemoteIdentity (); if (!remoteIdentity) return; @@ -985,13 +898,8 @@ namespace transport } else { - { - std::lock_guard l(m_PeersMutex); - m_Peers.erase (it); - } - // delete buffer of just disconnected router - auto r = i2p::data::netdb.FindRouter (ident); - if (r && !r->IsUpdated ()) r->ScheduleBufferToDelete (); + std::unique_lock l(m_PeersMutex); + m_Peers.erase (it); } } } @@ -1000,13 +908,9 @@ namespace transport bool Transports::IsConnected (const i2p::data::IdentHash& ident) const { - std::lock_guard l(m_PeersMutex); -#if __cplusplus >= 202002L // C++20 - return m_Peers.contains (ident); -#else + std::unique_lock l(m_PeersMutex); auto it = m_Peers.find (ident); return it != m_Peers.end (); -#endif } void Transports::HandlePeerCleanupTimer (const boost::system::error_code& ecode) @@ -1030,7 +934,7 @@ namespace transport auto profile = i2p::data::GetRouterProfile (it->first); if (profile) profile->Unreachable (); } */ - std::lock_guard l(m_PeersMutex); + std::unique_lock l(m_PeersMutex); it = m_Peers.erase (it); } else @@ -1041,7 +945,7 @@ namespace transport if (session) session->SendLocalRouterInfo (true); it->second->nextRouterInfoUpdateTime = ts + PEER_ROUTER_INFO_UPDATE_INTERVAL + - m_Rng() % PEER_ROUTER_INFO_UPDATE_INTERVAL_VARIANCE; + rand () % PEER_ROUTER_INFO_UPDATE_INTERVAL_VARIANCE; } ++it; } @@ -1055,7 +959,7 @@ namespace transport // if still testing or unknown, repeat peer test if (ipv4Testing || ipv6Testing) PeerTest (ipv4Testing, ipv6Testing); - m_PeerCleanupTimer->expires_from_now (boost::posix_time::seconds(2 * SESSION_CREATION_TIMEOUT + m_Rng() % SESSION_CREATION_TIMEOUT)); + m_PeerCleanupTimer->expires_from_now (boost::posix_time::seconds(3 * SESSION_CREATION_TIMEOUT)); m_PeerCleanupTimer->async_wait (std::bind (&Transports::HandlePeerCleanupTimer, this, std::placeholders::_1)); } } @@ -1065,7 +969,7 @@ namespace transport if (ecode != boost::asio::error::operation_aborted) { PeerTest (); - m_PeerTestTimer->expires_from_now (boost::posix_time::seconds(PEER_TEST_INTERVAL + m_Rng() % PEER_TEST_INTERVAL_VARIANCE)); + m_PeerTestTimer->expires_from_now (boost::posix_time::minutes(PEER_TEST_INTERVAL)); m_PeerTestTimer->async_wait (std::bind (&Transports::HandlePeerTestTimer, this, std::placeholders::_1)); } } @@ -1080,7 +984,7 @@ namespace transport { uint16_t inds[3]; RAND_bytes ((uint8_t *)inds, sizeof (inds)); - std::lock_guard l(m_PeersMutex); + std::unique_lock l(m_PeersMutex); auto count = m_Peers.size (); if(count == 0) return nullptr; inds[0] %= count; @@ -1195,7 +1099,7 @@ namespace transport std::lock_guard lock(m_TrustedRoutersMutex); m_TrustedRouters.clear(); for (const auto & ri : routers ) - m_TrustedRouters.insert(ri); + m_TrustedRouters.push_back(ri); } bool Transports::RoutesRestricted() const @@ -1212,7 +1116,7 @@ namespace transport } /** XXX: if routes are not restricted this dies */ - std::shared_ptr Transports::GetRestrictedPeer() + std::shared_ptr Transports::GetRestrictedPeer() const { { std::lock_guard l(m_FamilyMutex); @@ -1221,7 +1125,7 @@ namespace transport if(sz > 1) { auto it = m_TrustedFamilies.begin (); - std::advance(it, m_Rng() % sz); + std::advance(it, rand() % sz); fam = *it; } else if (sz == 1) @@ -1236,32 +1140,23 @@ namespace transport auto sz = m_TrustedRouters.size(); if (sz) { + if(sz == 1) + return i2p::data::netdb.FindRouter(m_TrustedRouters[0]); auto it = m_TrustedRouters.begin(); - if(sz > 1) - std::advance(it, m_Rng() % sz); + std::advance(it, rand() % sz); return i2p::data::netdb.FindRouter(*it); } } return nullptr; } - bool Transports::IsTrustedRouter (const i2p::data::IdentHash& ih) const + bool Transports::IsRestrictedPeer(const i2p::data::IdentHash & ih) const { - if (m_TrustedRouters.empty ()) return false; - std::lock_guard l(m_TrustedRoutersMutex); -#if __cplusplus >= 202002L // C++20 - if (m_TrustedRouters.contains (ih)) -#else - if (m_TrustedRouters.count (ih) > 0) -#endif - return true; - return false; - } - - bool Transports::IsRestrictedPeer(const i2p::data::IdentHash& ih) const - { - if (IsTrustedRouter (ih)) return true; - + { + std::lock_guard l(m_TrustedRoutersMutex); + for (const auto & r : m_TrustedRouters ) + if ( r == ih ) return true; + } { std::lock_guard l(m_FamilyMutex); auto ri = i2p::data::netdb.FindRouter(ih); @@ -1326,7 +1221,7 @@ namespace transport std::string yggaddress; i2p::config::GetOption ("meshnets.yggaddress", yggaddress); if (!yggaddress.empty ()) { - yggaddr = boost::asio::ip::make_address (yggaddress).to_v6 (); + yggaddr = boost::asio::ip::address_v6::from_string (yggaddress); if (yggaddr.is_unspecified () || !i2p::util::net::IsYggdrasilAddress (yggaddr) || !i2p::util::net::IsLocalAddress (yggaddr)) { @@ -1371,7 +1266,7 @@ namespace transport if (ipv6) { std::string ipv6Addr; i2p::config::GetOption("ntcp2.addressv6", ipv6Addr); - auto addr = boost::asio::ip::make_address (ipv6Addr).to_v6 (); + auto addr = boost::asio::ip::address_v6::from_string (ipv6Addr); if (!addr.is_unspecified () && addr != boost::asio::ip::address_v6::any ()) i2p::context.UpdateNTCP2V6Address (addr); // set ipv6 address if configured } diff --git a/libi2pd/Transports.h b/libi2pd/Transports.h index fcd2cfc6..70273094 100644 --- a/libi2pd/Transports.h +++ b/libi2pd/Transports.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2025, The PurpleI2P Project +* Copyright (c) 2013-2024, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -11,17 +11,14 @@ #include #include -#include #include #include #include -#include #include #include #include #include #include -#include #include #include "TransportSession.h" #include "SSU2.h" @@ -29,7 +26,6 @@ #include "RouterInfo.h" #include "I2NPProtocol.h" #include "Identity.h" -#include "util.h" namespace i2p { @@ -56,11 +52,10 @@ namespace transport private: const int m_QueueSize; - i2p::util::MemoryPoolMt m_KeysPool; std::queue > m_Queue; bool m_IsRunning; - std::unique_ptr m_Thread; + std::thread * m_Thread; std::condition_variable m_Acquired; std::mutex m_AcquiredMutex; }; @@ -76,7 +71,7 @@ namespace transport std::shared_ptr router; std::list > sessions; uint64_t creationTime, nextRouterInfoUpdateTime, lastSelectionTime; - std::list > delayedMessages; + std::vector > delayedMessages; std::vector priority; bool isHighBandwidth, isEligible; @@ -108,14 +103,12 @@ namespace transport }; const uint64_t SESSION_CREATION_TIMEOUT = 15; // in seconds - const int PEER_TEST_INTERVAL = 68*60; // in seconds - const int PEER_TEST_INTERVAL_VARIANCE = 3*60; // 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 NUM_X25519_PRE_GENERATED_KEYS = 25; // pre-generated x25519 keys pairs - + const int TRAFFIC_SAMPLE_COUNT = 301; // seconds struct TrafficSample @@ -143,12 +136,12 @@ namespace transport bool IsOnline() const { return m_IsOnline; }; void SetOnline (bool online); - auto& GetService () { return *m_Service; }; + boost::asio::io_service& GetService () { return *m_Service; }; std::shared_ptr GetNextX25519KeysPair (); void ReuseX25519KeysPair (std::shared_ptr pair); - std::future > SendMessage (const i2p::data::IdentHash& ident, std::shared_ptr msg); - std::future > SendMessages (const i2p::data::IdentHash& ident, std::list >&& msgs); + void SendMessage (const i2p::data::IdentHash& ident, std::shared_ptr msg); + void SendMessages (const i2p::data::IdentHash& ident, const std::vector >& msgs); void PeerConnected (std::shared_ptr session); void PeerDisconnected (std::shared_ptr session); @@ -171,7 +164,7 @@ namespace transport std::shared_ptr GetRandomPeer (bool isHighBandwidth) const; /** get a trusted first hop for restricted routes */ - std::shared_ptr GetRestrictedPeer(); + std::shared_ptr GetRestrictedPeer() const; /** do we want to use restricted routes? */ bool RoutesRestricted() const; /** restrict routes to use only these router families for first hops */ @@ -179,8 +172,7 @@ namespace transport /** restrict routes to use only these routers for first hops */ void RestrictRoutesToRouters(const std::set& routers); - bool IsTrustedRouter (const i2p::data::IdentHash& ih) const; - bool IsRestrictedPeer(const i2p::data::IdentHash& ih) const; + bool IsRestrictedPeer(const i2p::data::IdentHash & ident) const; void PeerTest (bool ipv4 = true, bool ipv6 = true); @@ -193,9 +185,9 @@ namespace transport void Run (); void RequestComplete (std::shared_ptr r, const i2p::data::IdentHash& ident); void HandleRequestComplete (std::shared_ptr r, i2p::data::IdentHash ident); - std::shared_ptr PostMessages (const i2p::data::IdentHash& ident, std::list >& msgs); + void PostMessages (i2p::data::IdentHash ident, std::vector > msgs); bool ConnectToPeer (const i2p::data::IdentHash& ident, std::shared_ptr peer); - void SetPriority (std::shared_ptr peer); + void SetPriority (std::shared_ptr peer) const; void HandlePeerCleanupTimer (const boost::system::error_code& ecode); void HandlePeerTestTimer (const boost::system::error_code& ecode); void HandleUpdateBandwidthTimer (const boost::system::error_code& ecode); @@ -211,8 +203,8 @@ namespace transport volatile bool m_IsOnline; bool m_IsRunning, m_IsNAT, m_CheckReserved; std::thread * m_Thread; - boost::asio::io_context * m_Service; - boost::asio::executor_work_guard * m_Work; + boost::asio::io_service * m_Service; + boost::asio::io_service::work * m_Work; boost::asio::deadline_timer * m_PeerCleanupTimer, * m_PeerTestTimer, * m_UpdateBandwidthTimer; SSU2Server * m_SSU2Server; @@ -239,11 +231,10 @@ namespace transport mutable std::mutex m_FamilyMutex; /** which routers for first hop to trust */ - std::unordered_set m_TrustedRouters; + std::vector m_TrustedRouters; mutable std::mutex m_TrustedRoutersMutex; i2p::I2NPMessagesHandler m_LoopbackHandler; - std::mt19937 m_Rng; public: diff --git a/libi2pd/Tunnel.cpp b/libi2pd/Tunnel.cpp index 1b317121..1b63b7a7 100644 --- a/libi2pd/Tunnel.cpp +++ b/libi2pd/Tunnel.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2025, The PurpleI2P Project +* Copyright (c) 2013-2024, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -103,7 +103,7 @@ namespace tunnel if (m_Config->IsShort ()) { auto ident = m_Config->GetFirstHop () ? m_Config->GetFirstHop ()->ident : nullptr; - if (ident && ident->GetIdentHash () != outboundTunnel->GetEndpointIdentHash ()) // don't encrypt if IBGW = OBEP + if (ident && ident->GetIdentHash () != outboundTunnel->GetNextIdentHash ()) // don't encrypt if IBGW = OBEP { auto msg1 = i2p::garlic::WrapECIESX25519MessageForRouter (msg, ident->GetEncryptionPublicKey ()); if (msg1) msg = msg1; @@ -130,19 +130,8 @@ namespace tunnel bool Tunnel::HandleTunnelBuildResponse (uint8_t * msg, size_t len) { - int num = msg[0]; - LogPrint (eLogDebug, "Tunnel: TunnelBuildResponse ", num, " records."); - if (num > MAX_NUM_RECORDS) - { - LogPrint (eLogError, "Tunnel: Too many records in TunnelBuildResponse", num); - return false; - } - if (len < num*m_Config->GetRecordSize () + 1) - { - LogPrint (eLogError, "Tunnel: TunnelBuildResponse of ", num, " records is too short ", len); - return false; - } - + LogPrint (eLogDebug, "Tunnel: TunnelBuildResponse ", (int)msg[0], " records."); + TunnelHopConfig * hop = m_Config->GetLastHop (); while (hop) { @@ -163,7 +152,7 @@ namespace tunnel while (hop1) { auto idx = hop1->recordIndex; - if (idx >= 0 && idx < num) + if (idx >= 0 && idx < msg[0]) hop->DecryptRecord (msg + 1, idx); else LogPrint (eLogWarning, "Tunnel: Hop index ", idx, " is out of range"); @@ -179,12 +168,9 @@ namespace tunnel { uint8_t ret = hop->GetRetCode (msg + 1); LogPrint (eLogDebug, "Tunnel: Build response ret code=", (int)ret); - if (hop->ident) - i2p::data::UpdateRouterProfile (hop->ident->GetIdentHash (), - [ret](std::shared_ptr profile) - { - if (profile) profile->TunnelBuildResponse (ret); - }); + auto profile = i2p::data::netdb.FindRouterProfile (hop->ident->GetIdentHash ()); + if (profile) + profile->TunnelBuildResponse (ret); if (ret) // if any of participants declined the tunnel is not established established = false; @@ -264,38 +250,12 @@ namespace tunnel void InboundTunnel::HandleTunnelDataMsg (std::shared_ptr&& msg) { - if (!IsEstablished () && GetState () != eTunnelStateExpiring) - { - // incoming messages means a tunnel is alive - SetState (eTunnelStateEstablished); - auto pool = GetTunnelPool (); - if (pool) - { - // update LeaseSet - auto dest = pool->GetLocalDestination (); - if (dest) dest->SetLeaseSetUpdated (true); - } - } + if (GetState () != eTunnelStateExpiring) SetState (eTunnelStateEstablished); // incoming messages means a tunnel is alive EncryptTunnelMsg (msg, msg); msg->from = GetSharedFromThis (); m_Endpoint.HandleDecryptedTunnelDataMsg (msg); } - bool InboundTunnel::Recreate () - { - if (!IsRecreated ()) - { - auto pool = GetTunnelPool (); - if (pool) - { - SetRecreated (true); - pool->RecreateInboundTunnel (std::static_pointer_cast(shared_from_this ())); - return true; - } - } - return false; - } - ZeroHopsInboundTunnel::ZeroHopsInboundTunnel (): InboundTunnel (std::make_shared ()), m_NumReceivedBytes (0) @@ -315,28 +275,22 @@ namespace tunnel void OutboundTunnel::SendTunnelDataMsgTo (const uint8_t * gwHash, uint32_t gwTunnel, std::shared_ptr msg) { TunnelMessageBlock block; - block.tunnelID = 0; // Initialize tunnelID to a default value - if (gwHash) { block.hash = gwHash; if (gwTunnel) { block.deliveryType = eDeliveryTypeTunnel; - block.tunnelID = gwTunnel; // Set tunnelID only if gwTunnel is non-zero + block.tunnelID = gwTunnel; } else - { block.deliveryType = eDeliveryTypeRouter; - } } else - { block.deliveryType = eDeliveryTypeLocal; - } - block.data = msg; - SendTunnelDataMsgs({block}); + + SendTunnelDataMsgs ({block}); } void OutboundTunnel::SendTunnelDataMsgs (const std::vector& msgs) @@ -352,21 +306,6 @@ namespace tunnel LogPrint (eLogError, "Tunnel: Incoming message for outbound tunnel ", GetTunnelID ()); } - bool OutboundTunnel::Recreate () - { - if (!IsRecreated ()) - { - auto pool = GetTunnelPool (); - if (pool) - { - SetRecreated (true); - pool->RecreateOutboundTunnel (std::static_pointer_cast(shared_from_this ())); - return true; - } - } - return false; - } - ZeroHopsOutboundTunnel::ZeroHopsOutboundTunnel (): OutboundTunnel (std::make_shared ()), m_NumSentBytes (0) @@ -400,8 +339,7 @@ namespace tunnel Tunnels::Tunnels (): m_IsRunning (false), m_Thread (nullptr), m_MaxNumTransitTunnels (DEFAULT_MAX_NUM_TRANSIT_TUNNELS), m_TotalNumSuccesiveTunnelCreations (0), m_TotalNumFailedTunnelCreations (0), // for normal average - m_TunnelCreationSuccessRate (TCSR_START_VALUE), m_TunnelCreationAttemptsNum(0), - m_Rng(i2p::util::GetMonotonicMicroseconds ()%1000000LL) + m_TunnelCreationSuccessRate (TCSR_START_VALUE), m_TunnelCreationAttemptsNum(0) { } @@ -412,26 +350,12 @@ namespace tunnel std::shared_ptr Tunnels::GetTunnel (uint32_t tunnelID) { - std::lock_guard l(m_TunnelsMutex); auto it = m_Tunnels.find(tunnelID); if (it != m_Tunnels.end ()) return it->second; return nullptr; } - bool Tunnels::AddTunnel (std::shared_ptr tunnel) - { - if (!tunnel) return false; - std::lock_guard l(m_TunnelsMutex); - return m_Tunnels.emplace (tunnel->GetTunnelID (), tunnel).second; - } - - void Tunnels::RemoveTunnel (uint32_t tunnelID) - { - std::lock_guard l(m_TunnelsMutex); - m_Tunnels.erase (tunnelID); - } - std::shared_ptr Tunnels::GetPendingInboundTunnel (uint32_t replyMsgID) { return GetPendingTunnel (replyMsgID, m_PendingInboundTunnels); @@ -473,7 +397,7 @@ namespace tunnel std::shared_ptr Tunnels::GetNextOutboundTunnel () { if (m_OutboundTunnels.empty ()) return nullptr; - uint32_t ind = m_Rng () % m_OutboundTunnels.size (), i = 0; + uint32_t ind = rand () % m_OutboundTunnels.size (), i = 0; std::shared_ptr tunnel; for (const auto& it: m_OutboundTunnels) { @@ -519,16 +443,26 @@ namespace tunnel } } + bool Tunnels::AddTransitTunnel (std::shared_ptr tunnel) + { + if (m_Tunnels.emplace (tunnel->GetTunnelID (), tunnel).second) + m_TransitTunnels.push_back (tunnel); + else + { + LogPrint (eLogError, "Tunnel: Tunnel with id ", tunnel->GetTunnelID (), " already exists"); + return false; + } + return true; + } + void Tunnels::Start () { m_IsRunning = true; m_Thread = new std::thread (std::bind (&Tunnels::Run, this)); - m_TransitTunnels.Start (); } void Tunnels::Stop () { - m_TransitTunnels.Stop (); m_IsRunning = false; m_Queue.WakeUp (); if (m_Thread) @@ -545,21 +479,18 @@ namespace tunnel std::this_thread::sleep_for (std::chrono::seconds(1)); // wait for other parts are ready uint64_t lastTs = 0, lastPoolsTs = 0, lastMemoryPoolTs = 0; - std::list > msgs; while (m_IsRunning) { try { - if (m_Queue.Wait (1,0)) // 1 sec + auto msg = m_Queue.GetNextWithTimeout (1000); // 1 sec + if (msg) { - m_Queue.GetWholeQueue (msgs); int numMsgs = 0; uint32_t prevTunnelID = 0, tunnelID = 0; std::shared_ptr prevTunnel; - while (!msgs.empty ()) + do { - auto msg = msgs.front (); msgs.pop_front (); - if (!msg) continue; std::shared_ptr tunnel; uint8_t typeID = msg->GetTypeID (); switch (typeID) @@ -587,38 +518,29 @@ namespace tunnel break; } - case eI2NPShortTunnelBuild: - HandleShortTunnelBuildMsg (msg); - break; case eI2NPVariableTunnelBuild: - HandleVariableTunnelBuildMsg (msg); - break; - case eI2NPShortTunnelBuildReply: - HandleTunnelBuildReplyMsg (msg, true); - break; case eI2NPVariableTunnelBuildReply: - HandleTunnelBuildReplyMsg (msg, false); - break; + case eI2NPShortTunnelBuild: + case eI2NPShortTunnelBuildReply: case eI2NPTunnelBuild: case eI2NPTunnelBuildReply: - LogPrint (eLogWarning, "Tunnel: TunnelBuild is too old for ECIES router"); - break; + HandleTunnelBuildI2NPMessage (msg); + break; default: LogPrint (eLogWarning, "Tunnel: Unexpected message type ", (int) typeID); } - prevTunnelID = tunnelID; - prevTunnel = tunnel; - numMsgs++; - - if (msgs.empty ()) - { - if (numMsgs < MAX_TUNNEL_MSGS_BATCH_SIZE && !m_Queue.IsEmpty ()) - m_Queue.GetWholeQueue (msgs); // try more - else if (tunnel) - tunnel->FlushTunnelDataMsgs (); // otherwise flush last - } + msg = (numMsgs <= MAX_TUNNEL_MSGS_BATCH_SIZE) ? m_Queue.Get () : nullptr; + if (msg) + { + prevTunnelID = tunnelID; + prevTunnel = tunnel; + numMsgs++; + } + else if (tunnel) + tunnel->FlushTunnelDataMsgs (); } + while (msg); } if (i2p::transport::transports.IsOnline()) @@ -675,92 +597,12 @@ namespace tunnel tunnel->SendTunnelDataMsg (msg); } - void Tunnels::HandleShortTunnelBuildMsg (std::shared_ptr msg) - { - if (!msg) return; - auto tunnel = GetPendingInboundTunnel (msg->GetMsgID()); // replyMsgID - if (tunnel) - { - // endpoint of inbound tunnel - LogPrint (eLogDebug, "Tunnel: ShortTunnelBuild reply for tunnel ", tunnel->GetTunnelID ()); - if (tunnel->HandleTunnelBuildResponse (msg->GetPayload(), msg->GetPayloadLength())) - { - LogPrint (eLogInfo, "Tunnel: Inbound tunnel ", tunnel->GetTunnelID (), " has been created"); - tunnel->SetState (eTunnelStateEstablished); - AddInboundTunnel (tunnel); - } - else - { - LogPrint (eLogInfo, "Tunnel: Inbound tunnel ", tunnel->GetTunnelID (), " has been declined"); - tunnel->SetState (eTunnelStateBuildFailed); - } - return; - } - else - m_TransitTunnels.PostTransitTunnelBuildMsg (std::move (msg)); - } - - void Tunnels::HandleVariableTunnelBuildMsg (std::shared_ptr msg) - { - auto tunnel = GetPendingInboundTunnel (msg->GetMsgID()); // replyMsgID - if (tunnel) - { - // endpoint of inbound tunnel - LogPrint (eLogDebug, "Tunnel: VariableTunnelBuild reply for tunnel ", tunnel->GetTunnelID ()); - if (tunnel->HandleTunnelBuildResponse (msg->GetPayload(), msg->GetPayloadLength())) - { - LogPrint (eLogInfo, "Tunnel: Inbound tunnel ", tunnel->GetTunnelID (), " has been created"); - tunnel->SetState (eTunnelStateEstablished); - AddInboundTunnel (tunnel); - } - else - { - LogPrint (eLogInfo, "Tunnel: Inbound tunnel ", tunnel->GetTunnelID (), " has been declined"); - tunnel->SetState (eTunnelStateBuildFailed); - } - } - else - m_TransitTunnels.PostTransitTunnelBuildMsg (std::move (msg)); - } - - void Tunnels::HandleTunnelBuildReplyMsg (std::shared_ptr msg, bool isShort) - { - auto tunnel = GetPendingOutboundTunnel (msg->GetMsgID()); // replyMsgID - if (tunnel) - { - // reply for outbound tunnel - LogPrint (eLogDebug, "Tunnel: TunnelBuildReply for tunnel ", tunnel->GetTunnelID ()); - if (tunnel->HandleTunnelBuildResponse (msg->GetPayload(), msg->GetPayloadLength())) - { - LogPrint (eLogInfo, "Tunnel: Outbound tunnel ", tunnel->GetTunnelID (), " has been created"); - tunnel->SetState (eTunnelStateEstablished); - AddOutboundTunnel (tunnel); - } - else - { - LogPrint (eLogInfo, "Tunnel: Outbound tunnel ", tunnel->GetTunnelID (), " has been declined"); - tunnel->SetState (eTunnelStateBuildFailed); - } - } - else - LogPrint (eLogWarning, "Tunnel: Pending tunnel for message ", msg->GetMsgID(), " not found"); - - } - void Tunnels::ManageTunnels (uint64_t ts) { ManagePendingTunnels (ts); - std::vector > tunnelsToRecreate; - ManageInboundTunnels (ts, tunnelsToRecreate); - ManageOutboundTunnels (ts, tunnelsToRecreate); - // rec-create in random order - if (!tunnelsToRecreate.empty ()) - { - if (tunnelsToRecreate.size () > 1) - std::shuffle (tunnelsToRecreate.begin(), tunnelsToRecreate.end(), m_Rng); - for (auto& it: tunnelsToRecreate) - it->Recreate (); - } + ManageInboundTunnels (ts); + ManageOutboundTunnels (ts); + ManageTransitTunnels (ts); } void Tunnels::ManagePendingTunnels (uint64_t ts) @@ -791,11 +633,11 @@ namespace tunnel while (hop) { if (hop->ident) - i2p::data::UpdateRouterProfile (hop->ident->GetIdentHash (), - [](std::shared_ptr profile) - { - if (profile) profile->TunnelNonReplied (); - }); + { + auto profile = i2p::data::netdb.FindRouterProfile (hop->ident->GetIdentHash ()); + if (profile) + profile->TunnelNonReplied (); + } hop = hop->next; } } @@ -823,7 +665,7 @@ namespace tunnel } } - void Tunnels::ManageOutboundTunnels (uint64_t ts, std::vector >& toRecreate) + void Tunnels::ManageOutboundTunnels (uint64_t ts) { for (auto it = m_OutboundTunnels.begin (); it != m_OutboundTunnels.end ();) { @@ -847,7 +689,10 @@ namespace tunnel auto pool = tunnel->GetTunnelPool (); // let it die if the tunnel pool has been reconfigured and this is old if (pool && tunnel->GetNumHops() == pool->GetNumOutboundHops()) - toRecreate.push_back (tunnel); + { + tunnel->SetRecreated (true); + pool->RecreateOutboundTunnel (tunnel); + } } if (ts + TUNNEL_EXPIRATION_THRESHOLD > tunnel->GetCreationTime () + TUNNEL_EXPIRATION_TIMEOUT) tunnel->SetState (eTunnelStateExpiring); @@ -872,7 +717,7 @@ namespace tunnel } } - void Tunnels::ManageInboundTunnels (uint64_t ts, std::vector >& toRecreate) + void Tunnels::ManageInboundTunnels (uint64_t ts) { for (auto it = m_InboundTunnels.begin (); it != m_InboundTunnels.end ();) { @@ -884,7 +729,7 @@ namespace tunnel auto pool = tunnel->GetTunnelPool (); if (pool) pool->TunnelExpired (tunnel); - RemoveTunnel (tunnel->GetTunnelID ()); + m_Tunnels.erase (tunnel->GetTunnelID ()); it = m_InboundTunnels.erase (it); } else @@ -896,7 +741,10 @@ namespace tunnel auto pool = tunnel->GetTunnelPool (); // let it die if the tunnel pool was reconfigured and has different number of hops if (pool && tunnel->GetNumHops() == pool->GetNumInboundHops()) - toRecreate.push_back (tunnel); + { + tunnel->SetRecreated (true); + pool->RecreateInboundTunnel (tunnel); + } } if (ts + TUNNEL_EXPIRATION_THRESHOLD > tunnel->GetCreationTime () + TUNNEL_EXPIRATION_TIMEOUT) @@ -943,6 +791,26 @@ namespace tunnel } } + void Tunnels::ManageTransitTunnels (uint64_t ts) + { + for (auto it = m_TransitTunnels.begin (); it != m_TransitTunnels.end ();) + { + auto tunnel = *it; + if (ts > tunnel->GetCreationTime () + TUNNEL_EXPIRATION_TIMEOUT || + ts + TUNNEL_EXPIRATION_TIMEOUT < tunnel->GetCreationTime ()) + { + LogPrint (eLogDebug, "Tunnel: Transit tunnel with id ", tunnel->GetTunnelID (), " expired"); + m_Tunnels.erase (tunnel->GetTunnelID ()); + it = m_TransitTunnels.erase (it); + } + else + { + tunnel->Cleanup (); + it++; + } + } + } + void Tunnels::ManageTunnelPools (uint64_t ts) { std::unique_lock l(m_PoolsMutex); @@ -958,7 +826,7 @@ namespace tunnel if (msg) m_Queue.Put (msg); } - void Tunnels::PostTunnelData (std::list >& msgs) + void Tunnels::PostTunnelData (const std::vector >& msgs) { m_Queue.Put (msgs); } @@ -1016,7 +884,7 @@ namespace tunnel void Tunnels::AddInboundTunnel (std::shared_ptr newTunnel) { - if (AddTunnel (newTunnel)) + if (m_Tunnels.emplace (newTunnel->GetTunnelID (), newTunnel).second) { m_InboundTunnels.push_back (newTunnel); auto pool = newTunnel->GetTunnelPool (); @@ -1046,7 +914,7 @@ namespace tunnel inboundTunnel->SetTunnelPool (pool); inboundTunnel->SetState (eTunnelStateEstablished); m_InboundTunnels.push_back (inboundTunnel); - AddTunnel (inboundTunnel); + m_Tunnels[inboundTunnel->GetTunnelID ()] = inboundTunnel; return inboundTunnel; } @@ -1080,12 +948,21 @@ namespace tunnel int Tunnels::GetTransitTunnelsExpirationTimeout () { - return m_TransitTunnels.GetTransitTunnelsExpirationTimeout (); + int timeout = 0; + uint32_t ts = i2p::util::GetSecondsSinceEpoch (); + // TODO: possible race condition with I2PControl + for (const auto& it : m_TransitTunnels) + { + int t = it->GetCreationTime () + TUNNEL_EXPIRATION_TIMEOUT - ts; + if (t > timeout) timeout = t; + } + return timeout; } size_t Tunnels::CountTransitTunnels() const { - return m_TransitTunnels.GetNumTransitTunnels (); + // TODO: locking + return m_TransitTunnels.size(); } size_t Tunnels::CountInboundTunnels() const diff --git a/libi2pd/Tunnel.h b/libi2pd/Tunnel.h index 5d21cd8b..6b014af2 100644 --- a/libi2pd/Tunnel.h +++ b/libi2pd/Tunnel.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2025, The PurpleI2P Project +* Copyright (c) 2013-2024, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -18,7 +18,6 @@ #include #include #include -#include #include "util.h" #include "Queue.h" #include "Crypto.h" @@ -99,7 +98,6 @@ namespace tunnel void SetRecreated (bool recreated) { m_IsRecreated = recreated; }; int GetNumHops () const { return m_Hops.size (); }; virtual bool IsInbound() const = 0; - virtual bool Recreate () = 0; std::shared_ptr GetTunnelPool () const { return m_Pool; }; void SetTunnelPool (std::shared_ptr pool) { m_Pool = pool; }; @@ -140,7 +138,7 @@ namespace tunnel public: OutboundTunnel (std::shared_ptr config): - Tunnel (config), m_Gateway (*this), m_EndpointIdentHash (config->GetLastIdentHash ()) {}; + Tunnel (config), m_Gateway (this), m_EndpointIdentHash (config->GetLastIdentHash ()) {}; void SendTunnelDataMsgTo (const uint8_t * gwHash, uint32_t gwTunnel, std::shared_ptr msg); virtual void SendTunnelDataMsgs (const std::vector& msgs); // multiple messages @@ -151,7 +149,6 @@ namespace tunnel void HandleTunnelDataMsg (std::shared_ptr&& tunnelMsg) override; bool IsInbound() const override { return false; } - bool Recreate () override; private: @@ -168,7 +165,6 @@ namespace tunnel void HandleTunnelDataMsg (std::shared_ptr&& msg) override; virtual size_t GetNumReceivedBytes () const { return m_Endpoint.GetNumReceivedBytes (); }; bool IsInbound() const override { return true; } - bool Recreate () override; // override TunnelBase void Cleanup () override { m_Endpoint.Cleanup (); }; @@ -226,15 +222,14 @@ namespace tunnel std::shared_ptr GetNextOutboundTunnel (); std::shared_ptr GetExploratoryPool () const { return m_ExploratoryPool; }; std::shared_ptr GetTunnel (uint32_t tunnelID); - bool AddTunnel (std::shared_ptr tunnel); - void RemoveTunnel (uint32_t tunnelID); int GetTransitTunnelsExpirationTimeout (); + bool AddTransitTunnel (std::shared_ptr tunnel); void AddOutboundTunnel (std::shared_ptr newTunnel); void AddInboundTunnel (std::shared_ptr newTunnel); std::shared_ptr CreateInboundTunnel (std::shared_ptr config, std::shared_ptr pool, std::shared_ptr outboundTunnel); std::shared_ptr CreateOutboundTunnel (std::shared_ptr config, std::shared_ptr pool); void PostTunnelData (std::shared_ptr msg); - void PostTunnelData (std::list >& msgs); // and cleanup msgs + void PostTunnelData (const std::vector >& msgs); void AddPendingTunnel (uint32_t replyMsgID, std::shared_ptr tunnel); void AddPendingTunnel (uint32_t replyMsgID, std::shared_ptr tunnel); std::shared_ptr CreateTunnelPool (int numInboundHops, @@ -247,8 +242,8 @@ namespace tunnel void SetMaxNumTransitTunnels (uint32_t maxNumTransitTunnels); uint32_t GetMaxNumTransitTunnels () const { return m_MaxNumTransitTunnels; }; - int GetCongestionLevel() const { return m_MaxNumTransitTunnels ? CONGESTION_LEVEL_FULL * m_TransitTunnels.GetNumTransitTunnels () / m_MaxNumTransitTunnels : CONGESTION_LEVEL_FULL; } - + int GetCongestionLevel() const { return m_MaxNumTransitTunnels ? CONGESTION_LEVEL_FULL * m_TransitTunnels.size() / m_MaxNumTransitTunnels : CONGESTION_LEVEL_FULL; } + private: template @@ -259,14 +254,12 @@ namespace tunnel std::shared_ptr GetPendingTunnel (uint32_t replyMsgID, const std::map >& pendingTunnels); void HandleTunnelGatewayMsg (std::shared_ptr tunnel, std::shared_ptr msg); - void HandleShortTunnelBuildMsg (std::shared_ptr msg); - void HandleVariableTunnelBuildMsg (std::shared_ptr msg); - void HandleTunnelBuildReplyMsg (std::shared_ptr msg, bool isShort); - + void Run (); void ManageTunnels (uint64_t ts); - void ManageOutboundTunnels (uint64_t ts, std::vector >& toRecreate); - void ManageInboundTunnels (uint64_t ts, std::vector >& toRecreate); + void ManageOutboundTunnels (uint64_t ts); + void ManageInboundTunnels (uint64_t ts); + void ManageTransitTunnels (uint64_t ts); void ManagePendingTunnels (uint64_t ts); template void ManagePendingTunnels (PendingTunnels& pendingTunnels, uint64_t ts); @@ -297,39 +290,36 @@ namespace tunnel bool m_IsRunning; std::thread * m_Thread; - i2p::util::MemoryPoolMt > m_I2NPTunnelEndpointMessagesMemoryPool; - i2p::util::MemoryPoolMt > m_I2NPTunnelMessagesMemoryPool; std::map > m_PendingInboundTunnels; // by replyMsgID std::map > m_PendingOutboundTunnels; // by replyMsgID std::list > m_InboundTunnels; std::list > m_OutboundTunnels; - mutable std::mutex m_TunnelsMutex; + std::list > m_TransitTunnels; std::unordered_map > m_Tunnels; // tunnelID->tunnel known by this id - mutable std::mutex m_PoolsMutex; + std::mutex m_PoolsMutex; std::list> m_Pools; std::shared_ptr m_ExploratoryPool; i2p::util::Queue > m_Queue; + i2p::util::MemoryPoolMt > m_I2NPTunnelEndpointMessagesMemoryPool; + i2p::util::MemoryPoolMt > m_I2NPTunnelMessagesMemoryPool; uint32_t m_MaxNumTransitTunnels; // count of tunnels for total TCSR algorithm int m_TotalNumSuccesiveTunnelCreations, m_TotalNumFailedTunnelCreations; double m_TunnelCreationSuccessRate; int m_TunnelCreationAttemptsNum; - std::mt19937 m_Rng; - TransitTunnels m_TransitTunnels; - + public: // for HTTP only const decltype(m_OutboundTunnels)& GetOutboundTunnels () const { return m_OutboundTunnels; }; const decltype(m_InboundTunnels)& GetInboundTunnels () const { return m_InboundTunnels; }; - const auto& GetTransitTunnels () const { return m_TransitTunnels.GetTransitTunnels (); }; + const decltype(m_TransitTunnels)& GetTransitTunnels () const { return m_TransitTunnels; }; size_t CountTransitTunnels() const; size_t CountInboundTunnels() const; size_t CountOutboundTunnels() const; - size_t GetQueueSize () const { return m_Queue.GetSize (); }; - size_t GetTBMQueueSize () const { return m_TransitTunnels.GetTunnelBuildMsgQueueSize (); }; + 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 diff --git a/libi2pd/TunnelBase.cpp b/libi2pd/TunnelBase.cpp deleted file mode 100644 index b5a4a0b3..00000000 --- a/libi2pd/TunnelBase.cpp +++ /dev/null @@ -1,71 +0,0 @@ -/* -* Copyright (c) 2024, The PurpleI2P Project -* -* This file is part of Purple i2pd project and licensed under BSD3 -* -* See full license text in LICENSE file at top of project tree -* -*/ - -#include "Transports.h" -#include "TunnelBase.h" - -namespace i2p -{ -namespace tunnel -{ - void TunnelTransportSender::SendMessagesTo (const i2p::data::IdentHash& to, - std::list >&& msgs) - { - if (msgs.empty ()) return; - auto currentTransport = m_CurrentTransport.lock (); - if (!currentTransport) - { - // try to obtain transport from pending request or send thought transport is not complete - if (m_PendingTransport.valid ()) // pending request? - { - if (m_PendingTransport.wait_for(std::chrono::seconds(0)) == std::future_status::ready) - { - // pending request complete - currentTransport = m_PendingTransport.get (); // take transports used in pending request - if (currentTransport) - { - if (currentTransport->IsEstablished ()) - m_CurrentTransport = currentTransport; - else - currentTransport = nullptr; - } - } - else // still pending - { - // send through transports, but don't update pending transport - i2p::transport::transports.SendMessages (to, std::move (msgs)); - return; - } - } - } - if (currentTransport) // session is good - // send to session directly - currentTransport->SendI2NPMessages (msgs); - else // no session yet - // send through transports - m_PendingTransport = i2p::transport::transports.SendMessages (to, std::move (msgs)); - - } - - void TunnelTransportSender::SendMessagesTo (const i2p::data::IdentHash& to, - std::list >& msgs) - { - std::list > msgs1; - msgs.swap (msgs1); - SendMessagesTo (to, std::move (msgs1)); - } - - void TunnelTransportSender::Reset () - { - m_CurrentTransport.reset (); - if (m_PendingTransport.valid ()) - m_PendingTransport = std::future >(); - } -} -} diff --git a/libi2pd/TunnelBase.h b/libi2pd/TunnelBase.h index 39d6e780..d58ec2d7 100644 --- a/libi2pd/TunnelBase.h +++ b/libi2pd/TunnelBase.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2024, The PurpleI2P Project +* Copyright (c) 2013-2022, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -11,19 +11,12 @@ #include #include -#include -#include #include "Timestamp.h" #include "I2NPProtocol.h" #include "Identity.h" namespace i2p { -namespace transport -{ - class TransportSession; -} - namespace tunnel { const size_t TUNNEL_DATA_MSG_SIZE = 1028; @@ -83,25 +76,6 @@ namespace tunnel return t1 < t2; } }; - - class TunnelTransportSender final - { - public: - - TunnelTransportSender () = default; - ~TunnelTransportSender () = default; - - void SendMessagesTo (const i2p::data::IdentHash& to, std::list >&& msgs); - void SendMessagesTo (const i2p::data::IdentHash& to, std::list >& msgs); // send and clear - - std::shared_ptr GetCurrentTransport () const { return m_CurrentTransport.lock (); } - void Reset (); - - private: - - std::weak_ptr m_CurrentTransport; - std::future > m_PendingTransport; - }; } } diff --git a/libi2pd/TunnelConfig.cpp b/libi2pd/TunnelConfig.cpp index fe0e8573..e19b515d 100644 --- a/libi2pd/TunnelConfig.cpp +++ b/libi2pd/TunnelConfig.cpp @@ -79,7 +79,8 @@ namespace tunnel uint8_t * record = records + index*TUNNEL_BUILD_RECORD_SIZE; i2p::crypto::CBCDecryption decryption; decryption.SetKey (replyKey); - decryption.Decrypt(record, TUNNEL_BUILD_RECORD_SIZE, replyIV, record); + decryption.SetIV (replyIV); + decryption.Decrypt(record, TUNNEL_BUILD_RECORD_SIZE, record); } void ECIESTunnelHopConfig::EncryptECIES (const uint8_t * plainText, size_t len, uint8_t * encrypted) diff --git a/libi2pd/TunnelConfig.h b/libi2pd/TunnelConfig.h index 718a6fdb..9dcf2c02 100644 --- a/libi2pd/TunnelConfig.h +++ b/libi2pd/TunnelConfig.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2024, The PurpleI2P Project +* Copyright (c) 2013-2021, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -181,8 +181,6 @@ namespace tunnel return peers; } - size_t GetRecordSize () const { return m_IsShort ? SHORT_TUNNEL_BUILD_RECORD_SIZE : TUNNEL_BUILD_RECORD_SIZE; }; - protected: // this constructor can't be called from outside diff --git a/libi2pd/TunnelEndpoint.cpp b/libi2pd/TunnelEndpoint.cpp index 66b7effa..3dc0dc07 100644 --- a/libi2pd/TunnelEndpoint.cpp +++ b/libi2pd/TunnelEndpoint.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2025, The PurpleI2P Project +* Copyright (c) 2013-2023, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -21,7 +21,10 @@ namespace i2p { namespace tunnel { - + TunnelEndpoint::~TunnelEndpoint () + { + } + void TunnelEndpoint::HandleDecryptedTunnelDataMsg (std::shared_ptr msg) { m_NumReceivedBytes += TUNNEL_DATA_MSG_SIZE; @@ -258,8 +261,9 @@ namespace tunnel void TunnelEndpoint::AddOutOfSequenceFragment (uint32_t msgID, uint8_t fragmentNum, bool isLastFragment, const uint8_t * fragment, size_t size) { - if (!m_OutOfSequenceFragments.try_emplace ((uint64_t)msgID << 32 | fragmentNum, - isLastFragment, i2p::util::GetMillisecondsSinceEpoch (), fragment, size).second) + std::unique_ptr f(new Fragment (isLastFragment, i2p::util::GetMillisecondsSinceEpoch (), size)); + memcpy (f->data.data (), fragment, size); + if (!m_OutOfSequenceFragments.emplace ((uint64_t)msgID << 32 | fragmentNum, std::move (f)).second) LogPrint (eLogInfo, "TunnelMessage: Duplicate out-of-sequence fragment ", fragmentNum, " of message ", msgID); } @@ -289,7 +293,7 @@ namespace tunnel if (it != m_OutOfSequenceFragments.end ()) { LogPrint (eLogDebug, "TunnelMessage: Out-of-sequence fragment ", (int)msg.nextFragmentNum, " of message ", msgID, " found"); - size_t size = it->second.data.size (); + size_t size = it->second->data.size (); if (msg.data->len + size > msg.data->maxLen) { LogPrint (eLogWarning, "TunnelMessage: Tunnel endpoint I2NP message size ", msg.data->maxLen, " is not enough"); @@ -297,9 +301,9 @@ namespace tunnel *newMsg = *(msg.data); msg.data = newMsg; } - if (msg.data->Concat (it->second.data.data (), size) < size) // concatenate out-of-sync fragment + if (msg.data->Concat (it->second->data.data (), size) < size) // concatenate out-of-sync fragment LogPrint (eLogError, "TunnelMessage: Tunnel endpoint I2NP buffer overflow ", msg.data->maxLen); - if (it->second.isLastFragment) + if (it->second->isLastFragment) // message complete msg.nextFragmentNum = 0; else @@ -327,13 +331,13 @@ namespace tunnel break; case eDeliveryTypeTunnel: if (!m_IsInbound) // outbound transit tunnel - SendMessageTo (msg.hash, i2p::CreateTunnelGatewayMsg (msg.tunnelID, msg.data)); + i2p::transport::transports.SendMessage (msg.hash, i2p::CreateTunnelGatewayMsg (msg.tunnelID, msg.data)); else LogPrint (eLogError, "TunnelMessage: Delivery type 'tunnel' arrived from an inbound tunnel, dropped"); break; case eDeliveryTypeRouter: if (!m_IsInbound) // outbound transit tunnel - i2p::transport::transports.SendMessage (msg.hash, msg.data); // send right away, because most likely it's single message + i2p::transport::transports.SendMessage (msg.hash, msg.data); else // we shouldn't send this message. possible leakage LogPrint (eLogError, "TunnelMessage: Delivery type 'router' arrived from an inbound tunnel, dropped"); break; @@ -348,7 +352,7 @@ namespace tunnel // out-of-sequence fragments for (auto it = m_OutOfSequenceFragments.begin (); it != m_OutOfSequenceFragments.end ();) { - if (ts > it->second.receiveTime + i2p::I2NP_MESSAGE_EXPIRATION_TIMEOUT) + if (ts > it->second->receiveTime + i2p::I2NP_MESSAGE_EXPIRATION_TIMEOUT) it = m_OutOfSequenceFragments.erase (it); else ++it; @@ -362,35 +366,5 @@ namespace tunnel ++it; } } - - void TunnelEndpoint::SendMessageTo (const i2p::data::IdentHash& to, std::shared_ptr msg) - { - if (msg) - { - if (!m_Sender && m_I2NPMsgs.empty ()) // first message - m_CurrentHash = to; - else if (m_CurrentHash != to) // new target router - { - FlushI2NPMsgs (); // flush message to previous - if (m_Sender) m_Sender->Reset (); // reset sender - m_CurrentHash = to; // set new target router - } // otherwise add msg to the list for current target router - m_I2NPMsgs.push_back (msg); - } - } - - void TunnelEndpoint::FlushI2NPMsgs () - { - if (!m_I2NPMsgs.empty ()) - { - if (!m_Sender) m_Sender = std::make_unique(); - m_Sender->SendMessagesTo (m_CurrentHash, m_I2NPMsgs); // send and clear - } - } - - const i2p::data::IdentHash * TunnelEndpoint::GetCurrentHash () const - { - return (m_Sender || !m_I2NPMsgs.empty ()) ? &m_CurrentHash : nullptr; - } } } diff --git a/libi2pd/TunnelEndpoint.h b/libi2pd/TunnelEndpoint.h index 1e81c445..17590a5f 100644 --- a/libi2pd/TunnelEndpoint.h +++ b/libi2pd/TunnelEndpoint.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2025, The PurpleI2P Project +* Copyright (c) 2013-2021, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -11,10 +11,8 @@ #include #include -#include #include #include -#include #include "I2NPProtocol.h" #include "TunnelBase.h" @@ -22,7 +20,7 @@ namespace i2p { namespace tunnel { - class TunnelEndpoint final + class TunnelEndpoint { struct TunnelMessageBlockEx: public TunnelMessageBlock { @@ -32,8 +30,7 @@ namespace tunnel struct Fragment { - Fragment (bool last, uint64_t t, const uint8_t * buf, size_t size): - isLastFragment (last), receiveTime (t), data (size) { memcpy (data.data(), buf, size); }; + Fragment (bool last, uint64_t t, size_t size): isLastFragment (last), receiveTime (t), data (size) {}; bool isLastFragment; uint64_t receiveTime; // milliseconds since epoch std::vector data; @@ -42,23 +39,18 @@ namespace tunnel public: TunnelEndpoint (bool isInbound): m_IsInbound (isInbound), m_NumReceivedBytes (0), m_CurrentMsgID (0) {}; - ~TunnelEndpoint () = default; + ~TunnelEndpoint (); size_t GetNumReceivedBytes () const { return m_NumReceivedBytes; }; void Cleanup (); void HandleDecryptedTunnelDataMsg (std::shared_ptr msg); - void FlushI2NPMsgs (); - const i2p::data::IdentHash * GetCurrentHash () const; // return null if not available - const std::unique_ptr& GetSender () const { return m_Sender; }; - private: void HandleFollowOnFragment (uint32_t msgID, bool isLastFragment, uint8_t fragmentNum, const uint8_t * fragment, size_t size); bool ConcatFollowOnFragment (TunnelMessageBlockEx& msg, const uint8_t * fragment, size_t size) const; // true if success void HandleCurrenMessageFollowOnFragment (const uint8_t * fragment, size_t size, bool isLastFragment); void HandleNextMessage (const TunnelMessageBlock& msg); - void SendMessageTo (const i2p::data::IdentHash& to, std::shared_ptr msg); void AddOutOfSequenceFragment (uint32_t msgID, uint8_t fragmentNum, bool isLastFragment, const uint8_t * fragment, size_t size); bool ConcatNextOutOfSequenceFragment (uint32_t msgID, TunnelMessageBlockEx& msg); // true if something added @@ -68,15 +60,11 @@ namespace tunnel private: std::unordered_map m_IncompleteMessages; - std::unordered_map m_OutOfSequenceFragments; // ((msgID << 8) + fragment#)->fragment + std::unordered_map > m_OutOfSequenceFragments; // ((msgID << 8) + fragment#)->fragment bool m_IsInbound; size_t m_NumReceivedBytes; TunnelMessageBlockEx m_CurrentMessage; uint32_t m_CurrentMsgID; - // I2NP messages to send - std::list > m_I2NPMsgs; // to send - i2p::data::IdentHash m_CurrentHash; // send msgs to - std::unique_ptr m_Sender; }; } } diff --git a/libi2pd/TunnelGateway.cpp b/libi2pd/TunnelGateway.cpp index 9e27d207..85ff224e 100644 --- a/libi2pd/TunnelGateway.cpp +++ b/libi2pd/TunnelGateway.cpp @@ -220,24 +220,21 @@ namespace tunnel void TunnelGateway::SendBuffer () { - // create list or tunnel messages m_Buffer.CompleteCurrentTunnelDataMessage (); - std::list > newTunnelMsgs; + std::vector > newTunnelMsgs; const auto& tunnelDataMsgs = m_Buffer.GetTunnelDataMsgs (); for (auto& tunnelMsg : tunnelDataMsgs) { auto newMsg = CreateEmptyTunnelDataMsg (false); - m_Tunnel.EncryptTunnelMsg (tunnelMsg, newMsg); - htobe32buf (newMsg->GetPayload (), m_Tunnel.GetNextTunnelID ()); + m_Tunnel->EncryptTunnelMsg (tunnelMsg, newMsg); + htobe32buf (newMsg->GetPayload (), m_Tunnel->GetNextTunnelID ()); newMsg->FillI2NPMessageHeader (eI2NPTunnelData); if (tunnelMsg->onDrop) newMsg->onDrop = tunnelMsg->onDrop; newTunnelMsgs.push_back (newMsg); m_NumSentBytes += TUNNEL_DATA_MSG_SIZE; } m_Buffer.ClearTunnelDataMsgs (); - // send - if (!m_Sender) m_Sender = std::make_unique(); - m_Sender->SendMessagesTo (m_Tunnel.GetNextIdentHash (), std::move (newTunnelMsgs)); + i2p::transport::transports.SendMessages (m_Tunnel->GetNextIdentHash (), newTunnelMsgs); } } } diff --git a/libi2pd/TunnelGateway.h b/libi2pd/TunnelGateway.h index 75f27581..741bbe84 100644 --- a/libi2pd/TunnelGateway.h +++ b/libi2pd/TunnelGateway.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2024, The PurpleI2P Project +* Copyright (c) 2013-2021, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -45,20 +45,18 @@ namespace tunnel { public: - TunnelGateway (TunnelBase& tunnel): + TunnelGateway (TunnelBase * tunnel): m_Tunnel (tunnel), m_NumSentBytes (0) {}; void SendTunnelDataMsg (const TunnelMessageBlock& block); void PutTunnelDataMsg (const TunnelMessageBlock& block); void SendBuffer (); size_t GetNumSentBytes () const { return m_NumSentBytes; }; - const std::unique_ptr& GetSender () const { return m_Sender; }; private: - TunnelBase& m_Tunnel; + TunnelBase * m_Tunnel; TunnelGatewayBuffer m_Buffer; size_t m_NumSentBytes; - std::unique_ptr m_Sender; }; } } diff --git a/libi2pd/TunnelPool.cpp b/libi2pd/TunnelPool.cpp index 26367aa6..5af42373 100644 --- a/libi2pd/TunnelPool.cpp +++ b/libi2pd/TunnelPool.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2025, The PurpleI2P Project +* Copyright (c) 2013-2024, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -141,7 +141,7 @@ namespace tunnel m_InboundTunnels.insert (createdTunnel); } if (m_LocalDestination) - m_LocalDestination->SetLeaseSetUpdated (true); + m_LocalDestination->SetLeaseSetUpdated (); } void TunnelPool::TunnelExpired (std::shared_ptr expiredTunnel) @@ -330,7 +330,7 @@ namespace tunnel } if (num < m_NumInboundTunnels && m_NumInboundHops <= 0 && m_LocalDestination) // zero hops IB - m_LocalDestination->SetLeaseSetUpdated (true); // update LeaseSet immediately + m_LocalDestination->SetLeaseSetUpdated (); // update LeaseSet immediately } void TunnelPool::TestTunnels () @@ -351,13 +351,10 @@ namespace tunnel { it.second.first->SetState (eTunnelStateFailed); std::unique_lock l(m_OutboundTunnelsMutex); - if (m_OutboundTunnels.size () > 1) // don't fail last tunnel + 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); - CreateOutboundTunnel (); // create new tunnel immediately because last one failed - } } else if (it.second.first->GetState () != eTunnelStateExpiring) it.second.first->SetState (eTunnelStateTestFailed); @@ -371,22 +368,19 @@ namespace tunnel bool failed = false; { std::unique_lock l(m_InboundTunnelsMutex); - if (m_InboundTunnels.size () > 1) // don't fail last tunnel + if (m_InboundTunnels.size () > 1 || m_NumInboundTunnels <= 1) // don't fail last tunnel { m_InboundTunnels.erase (it.second.second); failed = true; } else - { it.second.second->SetState (eTunnelStateTestFailed); - CreateInboundTunnel (); // create new tunnel immediately because last one failed - } } if (failed && m_LocalDestination) - m_LocalDestination->SetLeaseSetUpdated (true); + m_LocalDestination->SetLeaseSetUpdated (); } if (m_LocalDestination) - m_LocalDestination->SetLeaseSetUpdated (true); + m_LocalDestination->SetLeaseSetUpdated (); } else if (it.second.second->GetState () != eTunnelStateExpiring) it.second.second->SetState (eTunnelStateTestFailed); @@ -566,7 +560,7 @@ namespace tunnel i2p::data::netdb.GetRandomRouter (prevHop, reverse, endpoint, false); if (hop) { - if (!hop->HasProfile () || !hop->GetProfile ()->IsBad ()) + if (!hop->GetProfile ()->IsBad ()) break; } else if (tryClient) @@ -594,7 +588,7 @@ namespace tunnel (inbound && i2p::transport::transports.GetNumPeers () > 25)) { auto r = i2p::transport::transports.GetRandomPeer (m_IsHighBandwidth && !i2p::context.IsLimitedConnectivity ()); - if (r && r->IsECIES () && (!r->HasProfile () || !r->GetProfile ()->IsBad ()) && + if (r && r->IsECIES () && !r->GetProfile ()->IsBad () && (numHops > 1 || (r->IsV4 () && (!inbound || r->IsPublished (true))))) // first inbound must be published ipv4 { prevHop = r; diff --git a/libi2pd/api.cpp b/libi2pd/api.cpp index 7dc11157..05f962f3 100644 --- a/libi2pd/api.cpp +++ b/libi2pd/api.cpp @@ -37,7 +37,9 @@ namespace api i2p::fs::Init(); bool precomputation; i2p::config::GetOption("precomputation.elgamal", precomputation); - i2p::crypto::InitCrypto (precomputation); + bool aesni; i2p::config::GetOption("cpuext.aesni", aesni); + bool forceCpuExt; i2p::config::GetOption("cpuext.force", forceCpuExt); + i2p::crypto::InitCrypto (precomputation, aesni, forceCpuExt); int netID; i2p::config::GetOption("netid", netID); i2p::context.SetNetID (netID); diff --git a/libi2pd/util.cpp b/libi2pd/util.cpp index 925cf629..d1ed9992 100644 --- a/libi2pd/util.cpp +++ b/libi2pd/util.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2025, The PurpleI2P Project +* Copyright (c) 2013-2023, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -8,7 +8,6 @@ #include #include -#include #include #include @@ -124,8 +123,8 @@ const char *inet_ntop_xp(int af, const void *src, char *dst, socklen_t size) #endif #endif -#define address_pair_v4(a,b) std::pair{ boost::asio::ip::make_address (a).to_v4 ().to_uint (), boost::asio::ip::make_address(b).to_v4 ().to_uint () } -#define address_pair_v6(a,b) std::pair{ boost::asio::ip::make_address (a).to_v6 ().to_bytes (), boost::asio::ip::make_address(b).to_v6 ().to_bytes () } +#define address_pair_v4(a,b) { boost::asio::ip::address_v4::from_string (a).to_ulong (), boost::asio::ip::address_v4::from_string (b).to_ulong () } +#define address_pair_v6(a,b) { boost::asio::ip::address_v6::from_string (a).to_bytes (), boost::asio::ip::address_v6::from_string (b).to_bytes () } namespace i2p { @@ -172,14 +171,6 @@ namespace util } } - void RunnableService::SetName (std::string_view name) - { - if (name.length() < 16) - m_Name = name; - else - m_Name = name.substr(0,15); - } - void SetThreadName (const char *name) { #if defined(__APPLE__) # if (!defined(MAC_OS_X_VERSION_10_6) || \ @@ -455,9 +446,9 @@ namespace net #ifdef _WIN32 LogPrint(eLogError, "NetIface: Cannot get address by interface name, not implemented on WIN32"); if (ipv6) - return boost::asio::ip::make_address("::1"); + return boost::asio::ip::address::from_string("::1"); else - return boost::asio::ip::make_address("127.0.0.1"); + return boost::asio::ip::address::from_string("127.0.0.1"); #else int af = (ipv6 ? AF_INET6 : AF_INET); ifaddrs *addrs; @@ -479,7 +470,7 @@ namespace net inet_ntop(af, &((sockaddr_in6 *)cur->ifa_addr)->sin6_addr, addr, INET6_ADDRSTRLEN); freeifaddrs(addrs); std::string cur_ifaddr(addr); - return boost::asio::ip::make_address(cur_ifaddr); + return boost::asio::ip::address::from_string(cur_ifaddr); } } } @@ -499,7 +490,7 @@ namespace net fallback = "127.0.0.1"; LogPrint(eLogWarning, "NetIface: Cannot find IPv4 address for interface ", ifname); } - return boost::asio::ip::make_address(fallback); + return boost::asio::ip::address::from_string(fallback); #endif } @@ -648,8 +639,7 @@ namespace net if (host.is_unspecified ()) return false; if (host.is_v4()) { - static const std::array, 14> reservedIPv4Ranges - { + static const std::vector< std::pair > reservedIPv4Ranges { address_pair_v4("0.0.0.0", "0.255.255.255"), address_pair_v4("10.0.0.0", "10.255.255.255"), address_pair_v4("100.64.0.0", "100.127.255.255"), @@ -666,7 +656,7 @@ namespace net address_pair_v4("224.0.0.0", "255.255.255.255") }; - uint32_t ipv4_address = host.to_v4 ().to_uint (); + uint32_t ipv4_address = host.to_v4 ().to_ulong (); for (const auto& it : reservedIPv4Ranges) { if (ipv4_address >= it.first && ipv4_address <= it.second) return true; @@ -674,8 +664,7 @@ namespace net } if (host.is_v6()) { - static const std::array, 7> reservedIPv6Ranges - { + 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"), diff --git a/libi2pd/util.h b/libi2pd/util.h index 7bd35e67..e39a9259 100644 --- a/libi2pd/util.h +++ b/libi2pd/util.h @@ -137,8 +137,8 @@ namespace util std::lock_guard l(m_Mutex); for (size_t i = 0; i < num; i++) this->Release (arr[i]); - } - + } + templateclass C, typename... R> void ReleaseMt(const C& c) { @@ -146,7 +146,7 @@ namespace util for (auto& it: c) this->Release (it); } - + template std::shared_ptr AcquireSharedMt (TArgs&&... args) { @@ -177,14 +177,12 @@ namespace util RunnableService (const std::string& name): m_Name (name), m_IsRunning (false) {} virtual ~RunnableService () {} - auto& GetIOService () { return m_Service; } + boost::asio::io_service& GetIOService () { return m_Service; } bool IsRunning () const { return m_IsRunning; }; void StartIOService (); void StopIOService (); - void SetName (std::string_view name); - private: void Run (); @@ -194,7 +192,7 @@ namespace util std::string m_Name; volatile bool m_IsRunning; std::unique_ptr m_Thread; - boost::asio::io_context m_Service; + boost::asio::io_service m_Service; }; class RunnableServiceWithWork: public RunnableService @@ -202,11 +200,11 @@ namespace util protected: RunnableServiceWithWork (const std::string& name): - RunnableService (name), m_Work (GetIOService ().get_executor ()) {} + RunnableService (name), m_Work (GetIOService ()) {} private: - boost::asio::executor_work_guard m_Work; + boost::asio::io_service::work m_Work; }; void SetThreadName (const char *name); diff --git a/libi2pd/version.h b/libi2pd/version.h index 1e63ae08..40d07845 100644 --- a/libi2pd/version.h +++ b/libi2pd/version.h @@ -18,7 +18,7 @@ #define MAKE_VERSION_NUMBER(a,b,c) ((a*100+b)*100+c) #define I2PD_VERSION_MAJOR 2 -#define I2PD_VERSION_MINOR 56 +#define I2PD_VERSION_MINOR 54 #define I2PD_VERSION_MICRO 0 #define I2PD_VERSION_PATCH 0 #ifdef GITVER @@ -33,7 +33,7 @@ #define I2P_VERSION_MAJOR 0 #define I2P_VERSION_MINOR 9 -#define I2P_VERSION_MICRO 65 +#define I2P_VERSION_MICRO 64 #define I2P_VERSION_PATCH 0 #define I2P_VERSION MAKE_VERSION(I2P_VERSION_MAJOR, I2P_VERSION_MINOR, I2P_VERSION_MICRO) #define I2P_VERSION_NUMBER MAKE_VERSION_NUMBER(I2P_VERSION_MAJOR, I2P_VERSION_MINOR, I2P_VERSION_MICRO) diff --git a/libi2pd_client/AddressBook.cpp b/libi2pd_client/AddressBook.cpp index 8333e87d..14599cf7 100644 --- a/libi2pd_client/AddressBook.cpp +++ b/libi2pd_client/AddressBook.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2025, The PurpleI2P Project +* Copyright (c) 2013-2024, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -9,7 +9,7 @@ #include #include #include -#include +#include #include #include #include @@ -17,7 +17,6 @@ #include #include "Base.h" #include "util.h" -#include "Timestamp.h" #include "Identity.h" #include "FS.h" #include "Log.h" @@ -50,23 +49,22 @@ namespace client if (m_IsPersist) i2p::config::GetOption("addressbook.hostsfile", m_HostsFile); } - std::shared_ptr GetAddress (const i2p::data::IdentHash& ident) override; - void AddAddress (std::shared_ptr address) override; - void RemoveAddress (const i2p::data::IdentHash& ident) override; - void CleanUpCache () override; + std::shared_ptr GetAddress (const i2p::data::IdentHash& ident) const; + void AddAddress (std::shared_ptr address); + void RemoveAddress (const i2p::data::IdentHash& ident); - bool Init () override; - int Load (Addresses& addresses) override; - int LoadLocal (Addresses& addresses) override; - int Save (const Addresses& addresses) override; + bool Init (); + int Load (std::map > & addresses); + int LoadLocal (std::map >& addresses); + int Save (const std::map >& addresses); + + void SaveEtag (const i2p::data::IdentHash& subsciption, const std::string& etag, const std::string& lastModified); + bool GetEtag (const i2p::data::IdentHash& subscription, std::string& etag, std::string& lastModified); + void ResetEtags (); - void SaveEtag (const i2p::data::IdentHash& subsciption, const std::string& etag, const std::string& lastModified) override; - bool GetEtag (const i2p::data::IdentHash& subscription, std::string& etag, std::string& lastModified) override; - void ResetEtags () override; - private: - int LoadFromFile (const std::string& filename, Addresses& addresses); // returns -1 if can't open file, otherwise number of records + int LoadFromFile (const std::string& filename, std::map >& addresses); // returns -1 if can't open file, otherwise number of records private: @@ -74,8 +72,6 @@ namespace client std::string etagsPath, indexPath, localPath; bool m_IsPersist; std::string m_HostsFile; // file to dump hosts.txt, empty if not used - std::unordered_map, uint64_t> > m_FullAddressCache; // ident hash -> (full ident buffer, last access timestamp) - std::mutex m_FullAddressCacheMutex; }; bool AddressBookFilesystemStorage::Init() @@ -96,19 +92,8 @@ namespace client return false; } - std::shared_ptr AddressBookFilesystemStorage::GetAddress (const i2p::data::IdentHash& ident) + std::shared_ptr AddressBookFilesystemStorage::GetAddress (const i2p::data::IdentHash& ident) const { - auto ts = i2p::util::GetMonotonicSeconds (); - { - std::lock_guard l(m_FullAddressCacheMutex); - auto it = m_FullAddressCache.find (ident); - if (it != m_FullAddressCache.end ()) - { - it->second.second = ts; - return std::make_shared(it->second.first.data (), it->second.first.size ()); - } - } - if (!m_IsPersist) { LogPrint(eLogDebug, "Addressbook: Persistence is disabled"); @@ -116,72 +101,48 @@ namespace client } std::string filename = storage.Path(ident.ToBase32()); std::ifstream f(filename, std::ifstream::binary); - if (!f.is_open ()) - { + if (!f.is_open ()) { LogPrint(eLogDebug, "Addressbook: Requested, but not found: ", filename); return nullptr; } f.seekg (0,std::ios::end); size_t len = f.tellg (); - if (len < i2p::data::DEFAULT_IDENTITY_SIZE) - { + if (len < i2p::data::DEFAULT_IDENTITY_SIZE) { LogPrint (eLogError, "Addressbook: File ", filename, " is too short: ", len); return nullptr; } f.seekg(0, std::ios::beg); - std::vector buf(len); - f.read((char *)buf.data (), len); - if (!f) - { - LogPrint (eLogError, "Addressbook: Couldn't read ", filename); - return nullptr; - } - { - std::lock_guard l(m_FullAddressCacheMutex); - m_FullAddressCache.try_emplace (ident, buf, ts); - } - return std::make_shared(buf.data (), len); + uint8_t * buf = new uint8_t[len]; + f.read((char *)buf, len); + auto address = std::make_shared(buf, len); + delete[] buf; + return address; } void AddressBookFilesystemStorage::AddAddress (std::shared_ptr address) { - if (!address) return; - size_t len = address->GetFullLen (); - std::vector buf; - if (!len) return; // invalid address - { - std::lock_guard l(m_FullAddressCacheMutex); - auto [it, inserted] = m_FullAddressCache.try_emplace (address->GetIdentHash(), len, i2p::util::GetMonotonicSeconds ()); - if (inserted) - address->ToBuffer (it->second.first.data (), len); - if (m_IsPersist) - buf = it->second.first; + if (!m_IsPersist) return; + std::string path = storage.Path( address->GetIdentHash().ToBase32() ); + std::ofstream f (path, std::ofstream::binary | std::ofstream::out); + if (!f.is_open ()) { + LogPrint (eLogError, "Addressbook: Can't open file ", path); + return; } - if (m_IsPersist && !buf.empty ()) - { - std::string path = storage.Path(address->GetIdentHash().ToBase32()); - std::ofstream f (path, std::ofstream::binary | std::ofstream::out); - if (!f.is_open ()) - { - LogPrint (eLogError, "Addressbook: Can't open file ", path); - return; - } - f.write ((const char *)buf.data (), len); - } + size_t len = address->GetFullLen (); + uint8_t * buf = new uint8_t[len]; + address->ToBuffer (buf, len); + f.write ((char *)buf, len); + delete[] buf; } void AddressBookFilesystemStorage::RemoveAddress (const i2p::data::IdentHash& ident) { - { - std::lock_guard l(m_FullAddressCacheMutex); - m_FullAddressCache.erase (ident); - } if (!m_IsPersist) return; storage.Remove( ident.ToBase32() ); } - int AddressBookFilesystemStorage::LoadFromFile (const std::string& filename, Addresses& addresses) + int AddressBookFilesystemStorage::LoadFromFile (const std::string& filename, std::map >& addresses) { int num = 0; std::ifstream f (filename, std::ifstream::in); // in text mode @@ -207,7 +168,7 @@ namespace client return num; } - int AddressBookFilesystemStorage::Load (Addresses& addresses) + int AddressBookFilesystemStorage::Load (std::map >& addresses) { int num = LoadFromFile (indexPath, addresses); if (num < 0) @@ -221,7 +182,7 @@ namespace client return num; } - int AddressBookFilesystemStorage::LoadLocal (Addresses& addresses) + int AddressBookFilesystemStorage::LoadLocal (std::map >& addresses) { int num = LoadFromFile (localPath, addresses); if (num < 0) return 0; @@ -229,7 +190,7 @@ namespace client return num; } - int AddressBookFilesystemStorage::Save (const Addresses& addresses) + int AddressBookFilesystemStorage::Save (const std::map >& addresses) { if (addresses.empty()) { @@ -320,22 +281,9 @@ namespace client } } - void AddressBookFilesystemStorage::CleanUpCache () - { - auto ts = i2p::util::GetMonotonicSeconds (); - std::lock_guard l(m_FullAddressCacheMutex); - for (auto it = m_FullAddressCache.begin (); it != m_FullAddressCache.end ();) - { - if (ts > it->second.second + ADDRESS_CACHE_EXPIRATION_TIMEOUT) - it = m_FullAddressCache.erase (it); - else - it++; - } - } - //--------------------------------------------------------------------- - Address::Address (std::string_view b32): + Address::Address (const std::string& b32): addressType (eAddressInvalid) { if (b32.length () <= B33_ADDRESS_THRESHOLD) @@ -357,7 +305,7 @@ namespace client identHash = hash; } - AddressBook::AddressBook (): m_Storage(nullptr), m_IsLoaded (false), + AddressBook::AddressBook (): m_Storage(nullptr), m_IsLoaded (false), m_IsDownloading (false), m_NumRetries (0), m_DefaultSubscription (nullptr), m_SubscriptionsUpdateTimer (nullptr), m_IsEnabled (true) { @@ -379,7 +327,6 @@ namespace client LoadHosts (); /* try storage, then hosts.txt, then download */ StartSubscriptions (); StartLookups (); - ScheduleCacheUpdate (); } } @@ -394,36 +341,23 @@ namespace client StopSubscriptions (); if (m_SubscriptionsUpdateTimer) { - m_SubscriptionsUpdateTimer->cancel (); + delete m_SubscriptionsUpdateTimer; m_SubscriptionsUpdateTimer = nullptr; } - if (m_AddressCacheUpdateTimer) + if (m_IsDownloading) { - m_AddressCacheUpdateTimer->cancel (); - m_AddressCacheUpdateTimer = nullptr; - } - bool isDownloading = m_Downloading.valid (); - if (isDownloading) - { - if (m_Downloading.wait_for(std::chrono::seconds(0)) == std::future_status::ready) - isDownloading = false; - else - { - LogPrint (eLogInfo, "Addressbook: Subscriptions are downloading, abort"); - for (int i = 0; i < 30; i++) + LogPrint (eLogInfo, "Addressbook: Subscriptions are downloading, abort"); + for (int i = 0; i < 30; i++) + { + if (!m_IsDownloading) { - if (m_Downloading.wait_for(std::chrono::seconds(1)) == std::future_status::ready) // wait for 1 seconds - { - isDownloading = false; - LogPrint (eLogInfo, "Addressbook: Subscriptions download complete"); - break; - } + LogPrint (eLogInfo, "Addressbook: Subscriptions download complete"); + break; } - } - if (!isDownloading) - m_Downloading.get (); - else - LogPrint (eLogError, "Addressbook: Subscription download timeout"); + std::this_thread::sleep_for (std::chrono::seconds (1)); // wait for 1 seconds + } + LogPrint (eLogError, "Addressbook: Subscription download timeout"); + m_IsDownloading = false; } if (m_Storage) { @@ -435,7 +369,7 @@ namespace client m_Subscriptions.clear (); } - std::shared_ptr AddressBook::GetAddress (std::string_view address) + std::shared_ptr AddressBook::GetAddress (const std::string& address) { auto pos = address.find(".b32.i2p"); if (pos != std::string::npos) @@ -443,18 +377,17 @@ namespace client auto addr = std::make_shared(address.substr (0, pos)); return addr->IsValid () ? addr : nullptr; } - else -#if __cplusplus >= 202002L // C++20 - if (address.ends_with (".i2p")) -#else - if (address.find (".i2p") != std::string::npos) -#endif + else { - if (!m_IsEnabled) return nullptr; - auto addr = FindAddress (address); - if (!addr) - LookupAddress (address); // TODO: - return addr; + pos = address.find (".i2p"); + if (pos != std::string::npos) + { + if (!m_IsEnabled) return nullptr; + auto addr = FindAddress (address); + if (!addr) + LookupAddress (address); // TODO: + return addr; + } } // if not .b32 we assume full base64 address i2p::data::IdentityEx dest; @@ -463,7 +396,7 @@ namespace client return std::make_shared(dest.GetIdentHash ()); } - std::shared_ptr AddressBook::FindAddress (std::string_view address) + std::shared_ptr AddressBook::FindAddress (const std::string& address) { auto it = m_Addresses.find (address); if (it != m_Addresses.end ()) @@ -567,35 +500,29 @@ namespace client if (pos != std::string::npos) { - std::string_view name = std::string_view(s).substr(0, pos++); - std::string_view addr = std::string_view(s).substr(pos); + std::string name = s.substr(0, pos++); + std::string addr = s.substr(pos); size_t pos = addr.find('#'); - if (pos != addr.npos) + if (pos != std::string::npos) addr = addr.substr(0, pos); // remove comments -#if __cplusplus >= 202002L // C++20 - if (name.ends_with (".b32.i2p")) -#else - if (name.find(".b32.i2p") != name.npos) -#endif + + pos = name.find(".b32.i2p"); + if (pos != std::string::npos) { LogPrint (eLogError, "Addressbook: Skipped adding of b32 address: ", name); continue; } -#if __cplusplus >= 202002L // C++20 - if (!name.ends_with (".i2p")) -#else - if (name.find(".i2p") == name.npos) -#endif + pos = name.find(".i2p"); + if (pos == std::string::npos) { LogPrint (eLogError, "Addressbook: Malformed domain: ", name); continue; } auto ident = std::make_shared (); - if (!ident->FromBase64(addr)) - { + if (!ident->FromBase64(addr)) { LogPrint (eLogError, "Addressbook: Malformed address ", addr, " for ", name); incomplete = f.eof (); continue; @@ -655,15 +582,16 @@ namespace client } else { - LogPrint (eLogInfo, "Addressbook: Loading subscriptions from config"); + LogPrint (eLogInfo, "Addressbook: Loading subscriptions from config file"); // using config file items std::string subscriptionURLs; i2p::config::GetOption("addressbook.subscriptions", subscriptionURLs); std::vector subsList; boost::split(subsList, subscriptionURLs, boost::is_any_of(","), boost::token_compress_on); for (const auto& s: subsList) - if (!s.empty ()) - m_Subscriptions.push_back (std::make_shared (*this, s)); + { + m_Subscriptions.push_back (std::make_shared (*this, s)); + } LogPrint (eLogInfo, "Addressbook: ", m_Subscriptions.size (), " subscriptions urls loaded"); } } @@ -674,7 +602,7 @@ namespace client void AddressBook::LoadLocal () { if (!m_Storage) return; - AddressBookStorage::Addresses localAddresses; + std::map> localAddresses; m_Storage->LoadLocal (localAddresses); for (const auto& it: localAddresses) { @@ -717,6 +645,7 @@ namespace client void AddressBook::DownloadComplete (bool success, const i2p::data::IdentHash& subscription, const std::string& etag, const std::string& lastModified) { + m_IsDownloading = false; m_NumRetries++; int nextUpdateTimeout = m_NumRetries*CONTINIOUS_SUBSCRIPTION_RETRY_TIMEOUT; if (m_NumRetries > CONTINIOUS_SUBSCRIPTION_MAX_NUM_RETRIES || nextUpdateTimeout > CONTINIOUS_SUBSCRIPTION_UPDATE_TIMEOUT) @@ -747,7 +676,7 @@ namespace client auto dest = i2p::client::context.GetSharedLocalDestination (); if (dest) { - m_SubscriptionsUpdateTimer = std::make_unique(dest->GetService ()); + m_SubscriptionsUpdateTimer = new boost::asio::deadline_timer (dest->GetService ()); m_SubscriptionsUpdateTimer->expires_from_now (boost::posix_time::minutes(INITIAL_SUBSCRIPTION_UPDATE_TIMEOUT)); m_SubscriptionsUpdateTimer->async_wait (std::bind (&AddressBook::HandleSubscriptionsUpdateTimer, this, std::placeholders::_1)); @@ -771,13 +700,7 @@ namespace client LogPrint(eLogWarning, "Addressbook: Missing local destination, skip subscription update"); return; } - bool isDownloading = m_Downloading.valid (); - if (isDownloading && m_Downloading.wait_for(std::chrono::seconds(0)) == std::future_status::ready) // still active? - { - m_Downloading.get (); - isDownloading = false; - } - if (!isDownloading && dest->IsReady ()) + if (!m_IsDownloading && dest->IsReady ()) { if (!m_IsLoaded) { @@ -786,15 +709,17 @@ namespace client std::string defaultSubURL; i2p::config::GetOption("addressbook.defaulturl", defaultSubURL); if (!m_DefaultSubscription) m_DefaultSubscription = std::make_shared(*this, defaultSubURL); - m_Downloading = std::async (std::launch::async, - std::bind (&AddressBookSubscription::CheckUpdates, m_DefaultSubscription)); + m_IsDownloading = true; + std::thread load_hosts(std::bind (&AddressBookSubscription::CheckUpdates, m_DefaultSubscription)); + load_hosts.detach(); // TODO: use join } else if (!m_Subscriptions.empty ()) { // pick random subscription auto ind = rand () % m_Subscriptions.size(); - m_Downloading = std::async (std::launch::async, - std::bind (&AddressBookSubscription::CheckUpdates, m_Subscriptions[ind])); + m_IsDownloading = true; + std::thread load_hosts(std::bind (&AddressBookSubscription::CheckUpdates, m_Subscriptions[ind])); + load_hosts.detach(); // TODO: use join } } else @@ -831,7 +756,7 @@ namespace client } } - void AddressBook::LookupAddress (std::string_view address) + void AddressBook::LookupAddress (const std::string& address) { std::shared_ptr addr; auto dot = address.find ('.'); @@ -861,7 +786,7 @@ namespace client memset (buf, 0, 4); htobe32buf (buf + 4, nonce); buf[8] = address.length (); - memcpy (buf + 9, address.data (), address.length ()); + memcpy (buf + 9, address.c_str (), address.length ()); datagram->SendDatagramTo (buf, len, addr->identHash, ADDRESS_RESPONSE_DATAGRAM_PORT, ADDRESS_RESOLVER_DATAGRAM_PORT); delete[] buf; } @@ -898,30 +823,7 @@ namespace client } } - void AddressBook::ScheduleCacheUpdate () - { - if (!m_AddressCacheUpdateTimer) - { - auto dest = i2p::client::context.GetSharedLocalDestination (); - if(dest) - m_AddressCacheUpdateTimer = std::make_unique(dest->GetService ()); - } - if (m_AddressCacheUpdateTimer) - { - m_AddressCacheUpdateTimer->expires_from_now (boost::posix_time::seconds(ADDRESS_CACHE_UPDATE_INTERVAL )); - m_AddressCacheUpdateTimer->async_wait ( - [this](const boost::system::error_code& ecode) - { - if (ecode != boost::asio::error::operation_aborted) - { - if (m_Storage) m_Storage->CleanUpCache (); - ScheduleCacheUpdate (); - } - }); - } - } - - AddressBookSubscription::AddressBookSubscription (AddressBook& book, std::string_view link): + AddressBookSubscription::AddressBookSubscription (AddressBook& book, const std::string& link): m_Book (book), m_Link (link) { } diff --git a/libi2pd_client/AddressBook.h b/libi2pd_client/AddressBook.h index 8b32aa93..fc4f19a7 100644 --- a/libi2pd_client/AddressBook.h +++ b/libi2pd_client/AddressBook.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2025, The PurpleI2P Project +* Copyright (c) 2013-2024, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -11,12 +11,10 @@ #include #include -#include #include #include #include #include -#include #include #include #include "Base.h" @@ -34,9 +32,7 @@ namespace client const int CONTINIOUS_SUBSCRIPTION_UPDATE_TIMEOUT = 720; // in minutes (12 hours) const int CONTINIOUS_SUBSCRIPTION_RETRY_TIMEOUT = 5; // in minutes const int CONTINIOUS_SUBSCRIPTION_MAX_NUM_RETRIES = 10; // then update timeout - const int SUBSCRIPTION_REQUEST_TIMEOUT = 120; //in seconds - const int ADDRESS_CACHE_EXPIRATION_TIMEOUT = 710; // in seconds - const int ADDRESS_CACHE_UPDATE_INTERVAL = 76; // in seconds + const int SUBSCRIPTION_REQUEST_TIMEOUT = 120; //in second const uint16_t ADDRESS_RESOLVER_DATAGRAM_PORT = 53; const uint16_t ADDRESS_RESPONSE_DATAGRAM_PORT = 54; @@ -49,7 +45,7 @@ namespace client i2p::data::IdentHash identHash; std::shared_ptr blindedPublicKey; - Address (std::string_view b32); + Address (const std::string& b32); Address (const i2p::data::IdentHash& hash); bool IsIdentHash () const { return addressType == eAddressIndentHash; }; bool IsValid () const { return addressType != eAddressInvalid; }; @@ -61,18 +57,15 @@ namespace client { public: - typedef std::map, std::less<> > Addresses; - virtual ~AddressBookStorage () {}; - virtual std::shared_ptr GetAddress (const i2p::data::IdentHash& ident) = 0; + virtual std::shared_ptr GetAddress (const i2p::data::IdentHash& ident) const = 0; virtual void AddAddress (std::shared_ptr address) = 0; virtual void RemoveAddress (const i2p::data::IdentHash& ident) = 0; - virtual void CleanUpCache () = 0; virtual bool Init () = 0; - virtual int Load (Addresses& addresses) = 0; - virtual int LoadLocal (Addresses& addresses) = 0; - virtual int Save (const Addresses& addresses) = 0; + virtual int Load (std::map >& addresses) = 0; + virtual int LoadLocal (std::map >& addresses) = 0; + virtual int Save (const std::map >& addresses) = 0; virtual void SaveEtag (const i2p::data::IdentHash& subscription, const std::string& etag, const std::string& lastModified) = 0; virtual bool GetEtag (const i2p::data::IdentHash& subscription, std::string& etag, std::string& lastModified) = 0; @@ -84,16 +77,16 @@ namespace client class AddressBook { public: - + AddressBook (); ~AddressBook (); void Start (); void StartResolvers (); void Stop (); - std::shared_ptr GetAddress (std::string_view address); + std::shared_ptr GetAddress (const std::string& address); std::shared_ptr GetFullAddress (const std::string& address); - std::shared_ptr FindAddress (std::string_view address); - void LookupAddress (std::string_view address); + std::shared_ptr FindAddress (const std::string& address); + void LookupAddress (const std::string& address); void InsertAddress (const std::string& address, const std::string& jump); // for jump links void InsertFullAddress (std::shared_ptr address); @@ -123,22 +116,19 @@ namespace client void StopLookups (); void HandleLookupResponse (const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len); - void ScheduleCacheUpdate (); - private: std::mutex m_AddressBookMutex; - AddressBookStorage::Addresses m_Addresses; + std::map > m_Addresses; std::map > m_Resolvers; // local destination->resolver std::mutex m_LookupsMutex; std::map m_Lookups; // nonce -> address AddressBookStorage * m_Storage; - volatile bool m_IsLoaded; - std::future m_Downloading; + volatile bool m_IsLoaded, m_IsDownloading; int m_NumRetries; std::vector > m_Subscriptions; std::shared_ptr m_DefaultSubscription; // in case if we don't know any addresses yet - std::unique_ptr m_SubscriptionsUpdateTimer, m_AddressCacheUpdateTimer; + boost::asio::deadline_timer * m_SubscriptionsUpdateTimer; bool m_IsEnabled; }; @@ -146,7 +136,7 @@ namespace client { public: - AddressBookSubscription (AddressBook& book, std::string_view link); + AddressBookSubscription (AddressBook& book, const std::string& link); void CheckUpdates (); private: diff --git a/libi2pd_client/BOB.cpp b/libi2pd_client/BOB.cpp index 8d94e94b..23c3b72f 100644 --- a/libi2pd_client/BOB.cpp +++ b/libi2pd_client/BOB.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2025, The PurpleI2P Project +* Copyright (c) 2013-2023, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -16,24 +16,6 @@ namespace i2p { namespace client { - void BOBI2PTunnelIncomingConnection::Established () - { - if (m_IsQuiet) - StreamReceive (); - else - { - // send destination first like received from I2P - std::string dest = GetStream ()->GetRemoteIdentity ()->ToBase64 (); - dest += "\n"; - if (dest.size() <= I2P_TUNNEL_CONNECTION_BUFFER_SIZE) - memcpy (GetStreamBuffer (), dest.c_str (), dest.size ()); - else - memset (GetStreamBuffer (), 0, I2P_TUNNEL_CONNECTION_BUFFER_SIZE); - HandleStreamReceive (boost::system::error_code (), dest.size ()); - } - Receive (); - } - BOBI2PInboundTunnel::BOBI2PInboundTunnel (const boost::asio::ip::tcp::endpoint& ep, std::shared_ptr localDestination): BOBI2PTunnel (localDestination), m_Acceptor (localDestination->GetService (), ep) { @@ -147,7 +129,7 @@ namespace client BOBI2POutboundTunnel::BOBI2POutboundTunnel (const std::string& outhost, uint16_t port, std::shared_ptr localDestination, bool quiet): BOBI2PTunnel (localDestination), - m_Endpoint (boost::asio::ip::make_address (outhost), port), m_IsQuiet (quiet) + m_Endpoint (boost::asio::ip::address::from_string (outhost), port), m_IsQuiet (quiet) { } @@ -174,7 +156,7 @@ namespace client { if (stream) { - auto conn = std::make_shared (this, stream, m_Endpoint, m_IsQuiet); + auto conn = std::make_shared (this, stream, m_Endpoint, m_IsQuiet); AddHandler (conn); conn->Connect (); } @@ -238,7 +220,7 @@ namespace client if (!inhost.empty ()) { boost::system::error_code ec; - auto addr = boost::asio::ip::make_address (inhost, ec); + auto addr = boost::asio::ip::address::from_string (inhost, ec); if (!ec) ep.address (addr); else @@ -443,7 +425,7 @@ namespace client { // TODO: FIXME: temporary validation, until hostname support is added boost::system::error_code ec; - boost::asio::ip::make_address(m_InHost, ec); + boost::asio::ip::address::from_string(m_InHost, ec); if (ec) { SendReplyError("inhost must be a valid IPv4 address."); @@ -454,7 +436,7 @@ namespace client { // TODO: FIXME: temporary validation, until hostname support is added boost::system::error_code ec; - boost::asio::ip::make_address(m_OutHost, ec); + boost::asio::ip::address::from_string(m_OutHost, ec); if (ec) { SendReplyError("outhost must be a IPv4 address."); @@ -846,7 +828,7 @@ namespace client BOBCommandChannel::BOBCommandChannel (const std::string& address, uint16_t port): RunnableService ("BOB"), - m_Acceptor (GetIOService (), boost::asio::ip::tcp::endpoint(boost::asio::ip::make_address(address), port)) + m_Acceptor (GetIOService (), boost::asio::ip::tcp::endpoint(boost::asio::ip::address::from_string(address), port)) { // command -> handler m_CommandHandlers[BOB_COMMAND_ZAP] = &BOBCommandSession::ZapCommandHandler; diff --git a/libi2pd_client/BOB.h b/libi2pd_client/BOB.h index f5aefd0a..1f5fda5f 100644 --- a/libi2pd_client/BOB.h +++ b/libi2pd_client/BOB.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2025, The PurpleI2P Project +* Copyright (c) 2013-2023, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -71,23 +71,6 @@ namespace client const char BOB_HELP_STATUS[] = "status - Display status of a nicknamed tunnel."; const char BOB_HELP_HELP [] = "help - Get help on a command."; - class BOBI2PTunnelIncomingConnection: public I2PTunnelConnection - { - public: - - BOBI2PTunnelIncomingConnection (I2PService * owner, std::shared_ptr stream, - const boost::asio::ip::tcp::endpoint& target, bool quiet): - I2PTunnelConnection (owner, stream, target), m_IsQuiet (quiet) {}; - - protected: - - void Established () override; - - private: - - bool m_IsQuiet; // don't send destination - }; - class BOBI2PTunnel: public I2PService { public: @@ -271,7 +254,7 @@ namespace client void Start (); void Stop (); - auto& GetService () { return GetIOService (); }; + boost::asio::io_service& GetService () { return GetIOService (); }; void AddDestination (const std::string& name, std::shared_ptr dest); void DeleteDestination (const std::string& name); std::shared_ptr FindDestination (const std::string& name); diff --git a/libi2pd_client/ClientContext.cpp b/libi2pd_client/ClientContext.cpp index d26e33ab..cf72d204 100644 --- a/libi2pd_client/ClientContext.cpp +++ b/libi2pd_client/ClientContext.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2025, The PurpleI2P Project +* Copyright (c) 2013-2024, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -265,15 +265,11 @@ namespace client } } - bool ClientContext::LoadPrivateKeys (i2p::data::PrivateKeys& keys, std::string_view filename, + bool ClientContext::LoadPrivateKeys (i2p::data::PrivateKeys& keys, const std::string& filename, i2p::data::SigningKeyType sigType, i2p::data::CryptoKeyType cryptoType) { -#if __cplusplus >= 202002L // C++20 - if (filename.starts_with ("transient")) -#else - std::string_view transient("transient"); + static const std::string transient("transient"); if (!filename.compare (0, transient.length (), transient)) // starts with transient -#endif { keys = i2p::data::PrivateKeys::CreateRandomKeys (sigType, cryptoType, true); LogPrint (eLogInfo, "Clients: New transient keys address ", m_AddressBook.ToAddress(keys.GetPublic ()->GetIdentHash ()), " created"); @@ -301,7 +297,7 @@ namespace client } else { - LogPrint (eLogInfo, "Clients: Can't open file ", fullPath, " Creating new one with signature type ", sigType, " crypto type ", cryptoType); + LogPrint (eLogCritical, "Clients: Can't open file ", fullPath, " Creating new one with signature type ", sigType, " crypto type ", cryptoType); keys = i2p::data::PrivateKeys::CreateRandomKeys (sigType, cryptoType, true); std::ofstream f (fullPath, std::ofstream::binary | std::ofstream::out); size_t len = keys.GetFullLen (); @@ -349,7 +345,7 @@ namespace client } std::shared_ptr ClientContext::CreateNewLocalDestination ( - boost::asio::io_context& service, bool isPublic, + boost::asio::io_service& service, bool isPublic, i2p::data::SigningKeyType sigType, i2p::data::CryptoKeyType cryptoType, const std::map * params) { @@ -403,7 +399,7 @@ namespace client return localDestination; } - std::shared_ptr ClientContext::CreateNewLocalDestination (boost::asio::io_context& service, + std::shared_ptr ClientContext::CreateNewLocalDestination (boost::asio::io_service& service, const i2p::data::PrivateKeys& keys, bool isPublic, const std::map * params) { auto it = m_Destinations.find (keys.GetPublic ()->GetIdentHash ()); @@ -420,10 +416,13 @@ namespace client void ClientContext::CreateNewSharedLocalDestination () { - std::map params; - ReadI2CPOptionsFromConfig ("shareddest.", params); - params[I2CP_PARAM_OUTBOUND_NICKNAME] = "SharedDest"; - + std::map params + { + { I2CP_PARAM_INBOUND_TUNNELS_QUANTITY, "3" }, + { I2CP_PARAM_OUTBOUND_TUNNELS_QUANTITY, "3" }, + { I2CP_PARAM_LEASESET_TYPE, "3" }, + { I2CP_PARAM_LEASESET_ENCRYPTION_TYPE, "0,4" } + }; m_SharedLocalDestination = CreateNewLocalDestination (false, i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519, i2p::data::CRYPTO_KEY_TYPE_ELGAMAL, ¶ms); // non-public, EDDSA m_SharedLocalDestination->Acquire (); @@ -474,9 +473,8 @@ namespace client options[I2CP_PARAM_STREAMING_INITIAL_ACK_DELAY] = GetI2CPOption(section, I2CP_PARAM_STREAMING_INITIAL_ACK_DELAY, DEFAULT_INITIAL_ACK_DELAY); options[I2CP_PARAM_STREAMING_MAX_OUTBOUND_SPEED] = GetI2CPOption(section, I2CP_PARAM_STREAMING_MAX_OUTBOUND_SPEED, DEFAULT_MAX_OUTBOUND_SPEED); options[I2CP_PARAM_STREAMING_MAX_INBOUND_SPEED] = GetI2CPOption(section, I2CP_PARAM_STREAMING_MAX_INBOUND_SPEED, DEFAULT_MAX_INBOUND_SPEED); - options[I2CP_PARAM_STREAMING_MAX_CONCURRENT_STREAMS] = GetI2CPOption(section, I2CP_PARAM_STREAMING_MAX_CONCURRENT_STREAMS, DEFAULT_MAX_CONCURRENT_STREAMS); options[I2CP_PARAM_STREAMING_ANSWER_PINGS] = GetI2CPOption(section, I2CP_PARAM_STREAMING_ANSWER_PINGS, isServer ? DEFAULT_ANSWER_PINGS : false); - options[I2CP_PARAM_STREAMING_PROFILE] = GetI2CPOption(section, I2CP_PARAM_STREAMING_PROFILE, DEFAULT_STREAMING_PROFILE); + options[I2CP_PARAM_STREAMING_PROFILE] = GetI2CPOption(section, I2CP_PARAM_STREAMING_PROFILE, DEFAULT_STREAMING_PROFILE); options[I2CP_PARAM_LEASESET_TYPE] = GetI2CPOption(section, I2CP_PARAM_LEASESET_TYPE, DEFAULT_LEASESET_TYPE); std::string encType = GetI2CPStringOption(section, I2CP_PARAM_LEASESET_ENCRYPTION_TYPE, isServer ? "4" : "0,4"); if (encType.length () > 0) options[I2CP_PARAM_LEASESET_ENCRYPTION_TYPE] = encType; @@ -547,11 +545,7 @@ namespace client { for (auto& it: files) { -#if __cplusplus >= 202002L // C++20 - if (!it.ends_with (".conf")) continue; -#else if (it.substr(it.size() - 5) != ".conf") continue; // skip files which not ends with ".conf" -#endif LogPrint(eLogDebug, "Clients: Tunnels extra config file: ", it); ReadTunnels (it, numClientTunnels, numServerTunnels); } @@ -601,15 +595,8 @@ namespace client std::map options; ReadI2CPOptions (section, false, options); - // Set I2CP name if not set - auto itopt = options.find (I2CP_PARAM_OUTBOUND_NICKNAME); - if (itopt == options.end ()) - options[I2CP_PARAM_OUTBOUND_NICKNAME] = name; - std::shared_ptr localDestination = nullptr; - if (keys == "shareddest") - localDestination = m_SharedLocalDestination; - else if (keys.length () > 0) + if (keys.length () > 0) { auto it = destinations.find (keys); if (it != destinations.end ()) @@ -636,7 +623,7 @@ namespace client if (type == I2P_TUNNELS_SECTION_TYPE_UDPCLIENT) { // udp client // TODO: hostnames - boost::asio::ip::udp::endpoint end (boost::asio::ip::make_address(address), port); + boost::asio::ip::udp::endpoint end (boost::asio::ip::address::from_string(address), port); if (!localDestination) localDestination = m_SharedLocalDestination; @@ -680,7 +667,7 @@ namespace client std::string outproxy = section.second.get("outproxy", ""); bool addresshelper = section.second.get("addresshelper", true); bool senduseragent = section.second.get("senduseragent", false); - auto tun = std::make_shared(name, address, port, + auto tun = std::make_shared(name, address, port, outproxy, addresshelper, senduseragent, localDestination); clientTunnel = tun; clientEndpoint = tun->GetLocalEndpoint (); @@ -762,42 +749,32 @@ namespace client std::map options; ReadI2CPOptions (section, true, options); - // Set I2CP name if not set - auto itopt = options.find (I2CP_PARAM_INBOUND_NICKNAME); - if (itopt == options.end ()) - options[I2CP_PARAM_INBOUND_NICKNAME] = name; - std::shared_ptr localDestination = nullptr; - if (keys == "shareddest") - localDestination = m_SharedLocalDestination; + auto it = destinations.find (keys); + if (it != destinations.end ()) + { + localDestination = it->second; + localDestination->SetPublic (true); + } else - { - auto it = destinations.find (keys); - if (it != destinations.end ()) + { + i2p::data::PrivateKeys k; + if(!LoadPrivateKeys (k, keys, sigType, cryptoType)) + continue; + localDestination = FindLocalDestination (k.GetPublic ()->GetIdentHash ()); + if (!localDestination) { - localDestination = it->second; - localDestination->SetPublic (true); + localDestination = CreateNewLocalDestination (k, true, &options); + destinations[keys] = localDestination; } else - { - i2p::data::PrivateKeys k; - if(!LoadPrivateKeys (k, keys, sigType, cryptoType)) - continue; - localDestination = FindLocalDestination (k.GetPublic ()->GetIdentHash ()); - if (!localDestination) - { - localDestination = CreateNewLocalDestination (k, true, &options); - destinations[keys] = localDestination; - } - else - localDestination->SetPublic (true); - } - } + localDestination->SetPublic (true); + } if (type == I2P_TUNNELS_SECTION_TYPE_UDPSERVER) { // udp server tunnel // TODO: hostnames - boost::asio::ip::udp::endpoint endpoint(boost::asio::ip::make_address(host), port); + boost::asio::ip::udp::endpoint endpoint(boost::asio::ip::address::from_string(host), port); if (address.empty ()) { if (!endpoint.address ().is_unspecified () && endpoint.address ().is_v6 ()) @@ -805,7 +782,7 @@ namespace client else address = "127.0.0.1"; } - auto localAddress = boost::asio::ip::make_address(address); + auto localAddress = boost::asio::ip::address::from_string(address); auto serverTunnel = std::make_shared(name, localDestination, localAddress, endpoint, inPort, gzip); if(!isUniqueLocal) { @@ -886,7 +863,7 @@ namespace client } else - LogPrint (eLogError, "Clients: Unknown section type = ", type, " of ", name, " in ", tunConf); + LogPrint (eLogWarning, "Clients: Unknown section type = ", type, " of ", name, " in ", tunConf); } catch (std::exception& ex) { @@ -912,19 +889,13 @@ namespace client i2p::config::GetOption("addressbook.enabled", httpAddresshelper); // addresshelper is not supported without address book i2p::data::SigningKeyType sigType; i2p::config::GetOption("httpproxy.signaturetype", sigType); LogPrint(eLogInfo, "Clients: Starting HTTP Proxy at ", httpProxyAddr, ":", httpProxyPort); - if (httpProxyKeys == "shareddest") - { - localDestination = m_SharedLocalDestination; - localDestination->Acquire (); - } - else if (httpProxyKeys.length () > 0) + if (httpProxyKeys.length () > 0) { i2p::data::PrivateKeys keys; if(LoadPrivateKeys (keys, httpProxyKeys, sigType)) { std::map params; ReadI2CPOptionsFromConfig ("httpproxy.", params); - params[I2CP_PARAM_OUTBOUND_NICKNAME] = "HTTPProxy"; localDestination = CreateNewLocalDestination (keys, false, ¶ms); if (localDestination) localDestination->Acquire (); } @@ -933,7 +904,7 @@ namespace client } try { - m_HttpProxy = new i2p::proxy::HTTPProxy("HTTP Proxy", httpProxyAddr, httpProxyPort, + m_HttpProxy = new i2p::proxy::HTTPProxy("HTTP Proxy", httpProxyAddr, httpProxyPort, httpOutProxyURL, httpAddresshelper, httpSendUserAgent, localDestination); m_HttpProxy->Start(); } @@ -961,12 +932,7 @@ namespace client uint16_t socksOutProxyPort; i2p::config::GetOption("socksproxy.outproxyport", socksOutProxyPort); i2p::data::SigningKeyType sigType; i2p::config::GetOption("socksproxy.signaturetype", sigType); LogPrint(eLogInfo, "Clients: Starting SOCKS Proxy at ", socksProxyAddr, ":", socksProxyPort); - if (socksProxyKeys == "shareddest") - { - localDestination = m_SharedLocalDestination; - localDestination->Acquire (); - } - else if (httpProxyKeys == socksProxyKeys && m_HttpProxy) + if (httpProxyKeys == socksProxyKeys && m_HttpProxy) { localDestination = m_HttpProxy->GetLocalDestination (); localDestination->Acquire (); @@ -978,7 +944,6 @@ namespace client { std::map params; ReadI2CPOptionsFromConfig ("socksproxy.", params); - params[I2CP_PARAM_OUTBOUND_NICKNAME] = "SOCKSProxy"; localDestination = CreateNewLocalDestination (keys, false, ¶ms); if (localDestination) localDestination->Acquire (); } diff --git a/libi2pd_client/ClientContext.h b/libi2pd_client/ClientContext.h index 3f7eaf9a..adec607a 100644 --- a/libi2pd_client/ClientContext.h +++ b/libi2pd_client/ClientContext.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2025, The PurpleI2P Project +* Copyright (c) 2013-2024, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -79,20 +79,20 @@ namespace client i2p::data::SigningKeyType sigType = i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519, i2p::data::CryptoKeyType cryptoType = i2p::data::CRYPTO_KEY_TYPE_ELGAMAL, const std::map * params = nullptr); // used by SAM only - std::shared_ptr CreateNewLocalDestination (boost::asio::io_context& service, + std::shared_ptr CreateNewLocalDestination (boost::asio::io_service& service, bool isPublic = false, i2p::data::SigningKeyType sigType = i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519, i2p::data::CryptoKeyType cryptoType = i2p::data::CRYPTO_KEY_TYPE_ELGAMAL, const std::map * params = nullptr); // same as previous but on external io_service std::shared_ptr CreateNewLocalDestination (const i2p::data::PrivateKeys& keys, bool isPublic = true, const std::map * params = nullptr); - std::shared_ptr CreateNewLocalDestination (boost::asio::io_context& service, + std::shared_ptr CreateNewLocalDestination (boost::asio::io_service& service, const i2p::data::PrivateKeys& keys, bool isPublic = true, const std::map * params = nullptr); // same as previous but on external io_service std::shared_ptr CreateNewMatchedTunnelDestination(const i2p::data::PrivateKeys &keys, const std::string & name, const std::map * params = nullptr); void DeleteLocalDestination (std::shared_ptr destination); std::shared_ptr FindLocalDestination (const i2p::data::IdentHash& destination) const; - bool LoadPrivateKeys (i2p::data::PrivateKeys& keys, std::string_view filename, + bool LoadPrivateKeys (i2p::data::PrivateKeys& keys, const std::string& filename, i2p::data::SigningKeyType sigType = i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519, i2p::data::CryptoKeyType cryptoType = i2p::data::CRYPTO_KEY_TYPE_ELGAMAL); diff --git a/libi2pd_client/HTTPProxy.cpp b/libi2pd_client/HTTPProxy.cpp index 4c2771b5..dba65815 100644 --- a/libi2pd_client/HTTPProxy.cpp +++ b/libi2pd_client/HTTPProxy.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2025, The PurpleI2P Project +* Copyright (c) 2013-2024, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -60,7 +60,7 @@ namespace proxy { "\r\n" ; - static bool str_rmatch(std::string_view str, const char *suffix) + static bool str_rmatch(std::string & str, const char *suffix) { auto pos = str.rfind (suffix); if (pos == std::string::npos) @@ -84,21 +84,21 @@ namespace proxy { void SentHTTPFailed(const boost::system::error_code & ecode); void HandleStreamRequestComplete (std::shared_ptr stream); /* error helpers */ - void GenericProxyError(std::string_view title, std::string_view description); - void GenericProxyInfo(std::string_view title, std::string_view description); - void HostNotFound(std::string_view host); - void SendProxyError(std::string_view content); + void GenericProxyError(const std::string& title, const std::string& description); + void GenericProxyInfo(const std::string& title, const std::string& description); + void HostNotFound(const std::string& host); + void SendProxyError(const std::string& content); void SendRedirect(const std::string& address); void ForwardToUpstreamProxy(); void HandleUpstreamHTTPProxyConnect(const boost::system::error_code & ec); void HandleUpstreamSocksProxyConnect(const boost::system::error_code & ec); - void HTTPConnect(std::string_view host, uint16_t port); + void HTTPConnect(const std::string & host, uint16_t port); void HandleHTTPConnectStreamRequestComplete(std::shared_ptr stream); typedef std::function ProxyResolvedHandler; - void HandleUpstreamProxyResolved(const boost::system::error_code & ecode, boost::asio::ip::tcp::resolver::results_type endpoints, ProxyResolvedHandler handler); + void HandleUpstreamProxyResolved(const boost::system::error_code & ecode, boost::asio::ip::tcp::resolver::iterator itr, ProxyResolvedHandler handler); void SocksProxySuccess(); void HandoverToUpstreamProxy(); @@ -162,23 +162,23 @@ namespace proxy { Done(shared_from_this()); } - void HTTPReqHandler::GenericProxyError(std::string_view title, std::string_view description) - { + void HTTPReqHandler::GenericProxyError(const std::string& title, const std::string& description) { std::stringstream ss; ss << "

" << tr("Proxy error") << ": " << title << "

\r\n"; ss << "

" << description << "

\r\n"; - SendProxyError(ss.str ()); + std::string content = ss.str(); + SendProxyError(content); } - void HTTPReqHandler::GenericProxyInfo(std::string_view title, std::string_view description) - { + void HTTPReqHandler::GenericProxyInfo(const std::string& title, const std::string& description) { std::stringstream ss; ss << "

" << tr("Proxy info") << ": " << title << "

\r\n"; ss << "

" << description << "

\r\n"; - SendProxyError(ss.str ()); + std::string content = ss.str(); + SendProxyError(content); } - void HTTPReqHandler::HostNotFound(std::string_view host) + void HTTPReqHandler::HostNotFound(const std::string& host) { std::stringstream ss; ss << "

" << tr("Proxy error: Host not found") << "

\r\n" @@ -192,10 +192,11 @@ namespace proxy { ss << "
  • second << host << "\">" << js->first << "
  • \r\n"; } ss << "\r\n"; - SendProxyError(ss.str ()); + std::string content = ss.str(); + SendProxyError(content); } - void HTTPReqHandler::SendProxyError(std::string_view content) + void HTTPReqHandler::SendProxyError(const std::string& content) { i2p::http::HTTPRes res; res.code = 500; @@ -472,7 +473,7 @@ namespace proxy { if (dest_host != "") { /* absolute url, replace 'Host' header */ - std::string h (dest_host); + std::string h = dest_host; if (dest_port != 0 && dest_port != 80) h += ":" + std::to_string(dest_port); m_ClientRequest.UpdateHeader("Host", h); @@ -512,7 +513,7 @@ namespace proxy { GenericProxyError(tr("Outproxy failure"), tr("Bad outproxy settings")); } else { LogPrint (eLogWarning, "HTTPProxy: Outproxy failure for ", dest_host, ": no outproxy enabled"); - std::stringstream ss; ss << tr("Host %s is not inside I2P network, but outproxy is not enabled", dest_host.c_str ()); + std::stringstream ss; ss << tr("Host %s is not inside I2P network, but outproxy is not enabled", dest_host.c_str()); GenericProxyError(tr("Outproxy failure"), ss.str()); } return true; @@ -583,22 +584,20 @@ namespace proxy { } else { - m_proxy_resolver.async_resolve(m_ProxyURL.host, std::to_string(m_ProxyURL.port), std::bind(&HTTPReqHandler::HandleUpstreamProxyResolved, this, - std::placeholders::_1, std::placeholders::_2, [&](boost::asio::ip::tcp::endpoint ep) - { - m_proxysock->async_connect(ep, std::bind(&HTTPReqHandler::HandleUpstreamHTTPProxyConnect, this, std::placeholders::_1)); - })); + boost::asio::ip::tcp::resolver::query q(m_ProxyURL.host, std::to_string(m_ProxyURL.port)); + m_proxy_resolver.async_resolve(q, std::bind(&HTTPReqHandler::HandleUpstreamProxyResolved, this, std::placeholders::_1, std::placeholders::_2, [&](boost::asio::ip::tcp::endpoint ep) { + m_proxysock->async_connect(ep, std::bind(&HTTPReqHandler::HandleUpstreamHTTPProxyConnect, this, std::placeholders::_1)); + })); } } else if (m_ProxyURL.schema == "socks") { /* handle upstream socks proxy */ if (!m_ProxyURL.port) m_ProxyURL.port = 9050; // default to tor default if not specified - m_proxy_resolver.async_resolve(m_ProxyURL.host, std::to_string(m_ProxyURL.port), std::bind(&HTTPReqHandler::HandleUpstreamProxyResolved, this, - std::placeholders::_1, std::placeholders::_2, [&](boost::asio::ip::tcp::endpoint ep) - { - m_proxysock->async_connect(ep, std::bind(&HTTPReqHandler::HandleUpstreamSocksProxyConnect, this, std::placeholders::_1)); - })); + boost::asio::ip::tcp::resolver::query q(m_ProxyURL.host, std::to_string(m_ProxyURL.port)); + m_proxy_resolver.async_resolve(q, std::bind(&HTTPReqHandler::HandleUpstreamProxyResolved, this, std::placeholders::_1, std::placeholders::_2, [&](boost::asio::ip::tcp::endpoint ep) { + m_proxysock->async_connect(ep, std::bind(&HTTPReqHandler::HandleUpstreamSocksProxyConnect, this, std::placeholders::_1)); + })); } else { @@ -607,10 +606,10 @@ namespace proxy { } } - void HTTPReqHandler::HandleUpstreamProxyResolved(const boost::system::error_code & ec, boost::asio::ip::tcp::resolver::results_type endpoints, ProxyResolvedHandler handler) + void HTTPReqHandler::HandleUpstreamProxyResolved(const boost::system::error_code & ec, boost::asio::ip::tcp::resolver::iterator it, ProxyResolvedHandler handler) { if(ec) GenericProxyError(tr("Cannot resolve upstream proxy"), ec.message()); - else handler(*endpoints.begin ()); + else handler(*it); } void HTTPReqHandler::HandleUpstreamSocksProxyConnect(const boost::system::error_code & ec) @@ -652,10 +651,11 @@ namespace proxy { Terminate(); } - void HTTPReqHandler::HTTPConnect(std::string_view host, uint16_t port) + void HTTPReqHandler::HTTPConnect(const std::string & host, uint16_t port) { LogPrint(eLogDebug, "HTTPProxy: CONNECT ",host, ":", port); - if(str_rmatch(host, ".i2p")) + std::string hostname(host); + if(str_rmatch(hostname, ".i2p")) GetOwner()->CreateStream (std::bind (&HTTPReqHandler::HandleHTTPConnectStreamRequestComplete, shared_from_this(), std::placeholders::_1), host, port); else diff --git a/libi2pd_client/I2CP.cpp b/libi2pd_client/I2CP.cpp index 11278e7a..fc6d1b40 100644 --- a/libi2pd_client/I2CP.cpp +++ b/libi2pd_client/I2CP.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2025, The PurpleI2P Project +* Copyright (c) 2013-2024, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -24,20 +24,18 @@ namespace i2p namespace client { - I2CPDestination::I2CPDestination (boost::asio::io_context& service, std::shared_ptr owner, + I2CPDestination::I2CPDestination (boost::asio::io_service& service, std::shared_ptr owner, std::shared_ptr identity, bool isPublic, bool isSameThread, const std::map& params): LeaseSetDestination (service, isPublic, ¶ms), m_Owner (owner), m_Identity (identity), m_EncryptionKeyType (m_Identity->GetCryptoKeyType ()), - m_IsCreatingLeaseSet (false), m_IsSameThread (isSameThread), - m_LeaseSetCreationTimer (service), m_ReadinessCheckTimer (service) + m_IsCreatingLeaseSet (false), m_IsSameThread (isSameThread), m_LeaseSetCreationTimer (service) { } void I2CPDestination::Stop () { m_LeaseSetCreationTimer.cancel (); - m_ReadinessCheckTimer.cancel (); LeaseSetDestination::Stop (); m_Owner = nullptr; } @@ -79,13 +77,7 @@ namespace client return keyType == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD ? (bool)m_ECIESx25519Decryptor : m_EncryptionKeyType == keyType; } - i2p::data::CryptoKeyType I2CPDestination::GetPreferredCryptoType () const - { - if (m_ECIESx25519Decryptor) - return i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD; - return i2p::data::CRYPTO_KEY_TYPE_ELGAMAL; - } - + void I2CPDestination::HandleDataMessage (const uint8_t * buf, size_t len) { uint32_t length = bufbe32toh (buf); @@ -96,7 +88,7 @@ namespace client void I2CPDestination::CreateNewLeaseSet (const std::vector >& tunnels) { - boost::asio::post (GetService (), std::bind (&I2CPDestination::PostCreateNewLeaseSet, GetSharedFromThis (), tunnels)); + GetService ().post (std::bind (&I2CPDestination::PostCreateNewLeaseSet, this, tunnels)); } void I2CPDestination::PostCreateNewLeaseSet (std::vector > tunnels) @@ -106,20 +98,6 @@ namespace client LogPrint (eLogInfo, "I2CP: LeaseSet is being created"); return; } - m_ReadinessCheckTimer.cancel (); - auto pool = GetTunnelPool (); - if (!pool || pool->GetOutboundTunnels ().empty ()) - { - // try again later - m_ReadinessCheckTimer.expires_from_now (boost::posix_time::seconds(I2CP_DESTINATION_READINESS_CHECK_INTERVAL)); - m_ReadinessCheckTimer.async_wait( - [s=GetSharedFromThis (), tunnels=std::move(tunnels)](const boost::system::error_code& ecode) - { - if (ecode != boost::asio::error::operation_aborted) - s->PostCreateNewLeaseSet (tunnels); - }); - return; - } uint8_t priv[256] = {0}; i2p::data::LocalLeaseSet ls (m_Identity, priv, tunnels); // we don't care about encryption key, we need leases only m_LeaseSetExpirationTime = ls.GetExpirationTime (); @@ -192,7 +170,7 @@ namespace client { // send in destination's thread auto s = GetSharedFromThis (); - boost::asio::post (GetService (), + GetService ().post ( [s, msg, remote, nonce]() { bool sent = s->SendMsg (msg, remote); @@ -533,7 +511,7 @@ namespace client if (sendBuf) { if (m_SendQueue.GetSize () < I2CP_MAX_SEND_QUEUE_SIZE) - m_SendQueue.Add (std::move(sendBuf)); + m_SendQueue.Add (sendBuf); else { LogPrint (eLogWarning, "I2CP: Send queue size exceeds ", I2CP_MAX_SEND_QUEUE_SIZE); @@ -577,30 +555,30 @@ namespace client m_IsSending = false; } - std::string_view I2CPSession::ExtractString (const uint8_t * buf, size_t len) const + std::string I2CPSession::ExtractString (const uint8_t * buf, size_t len) { uint8_t l = buf[0]; if (l > len) l = len; - return { (const char *)(buf + 1), l }; + return std::string ((const char *)(buf + 1), l); } - size_t I2CPSession::PutString (uint8_t * buf, size_t len, std::string_view str) + size_t I2CPSession::PutString (uint8_t * buf, size_t len, const std::string& str) { auto l = str.length (); if (l + 1 >= len) l = len - 1; if (l > 255) l = 255; // 1 byte max buf[0] = l; - memcpy (buf + 1, str.data (), l); + memcpy (buf + 1, str.c_str (), l); return l + 1; } - void I2CPSession::ExtractMapping (const uint8_t * buf, size_t len, std::map& mapping) const + void I2CPSession::ExtractMapping (const uint8_t * buf, size_t len, std::map& mapping) // TODO: move to Base.cpp { size_t offset = 0; while (offset < len) { - auto param = ExtractString (buf + offset, len - offset); + std::string param = ExtractString (buf + offset, len - offset); offset += param.length () + 1; if (buf[offset] != '=') { @@ -609,7 +587,7 @@ namespace client } offset++; - auto value = ExtractString (buf + offset, len - offset); + std::string value = ExtractString (buf + offset, len - offset); offset += value.length () + 1; if (buf[offset] != ';') { @@ -617,7 +595,7 @@ namespace client break; } offset++; - mapping.emplace (param, value); + mapping.insert (std::make_pair (param, value)); } } @@ -786,7 +764,6 @@ namespace client void I2CPSession::AddRoutingSession (const i2p::data::IdentHash& signingKey, std::shared_ptr remoteSession) { if (!remoteSession) return; - remoteSession->SetAckRequestInterval (I2CP_SESSION_ACK_REQUEST_INTERVAL); std::lock_guard l(m_RoutingSessionsMutex); m_RoutingSessions[signingKey] = remoteSession; } @@ -902,7 +879,7 @@ namespace client if (!remoteSession || !m_Destination->SendMsg (buf + offset, payloadLen, remoteSession, nonce)) { i2p::data::IdentHash identHash; - SHA256(ident, identSize, identHash); // calculate ident hash, because we don't need full identity + SHA256(ident, identSize, identHash); // caclulate ident hash, because we don't need full identity m_Destination->SendMsgTo (buf + offset, payloadLen, identHash, nonce); } } @@ -1079,7 +1056,7 @@ namespace client if (sendBuf) { if (m_SendQueue.GetSize () < I2CP_MAX_SEND_QUEUE_SIZE) - m_SendQueue.Add (std::move(sendBuf)); + m_SendQueue.Add (sendBuf); else { LogPrint (eLogWarning, "I2CP: Send queue size exceeds ", I2CP_MAX_SEND_QUEUE_SIZE); @@ -1102,7 +1079,7 @@ namespace client I2CPServer::I2CPServer (const std::string& interface, uint16_t port, bool isSingleThread): RunnableService ("I2CP"), m_IsSingleThread (isSingleThread), m_Acceptor (GetIOService (), - boost::asio::ip::tcp::endpoint(boost::asio::ip::make_address(interface), port)) + boost::asio::ip::tcp::endpoint(boost::asio::ip::address::from_string(interface), port)) { memset (m_MessagesHandlers, 0, sizeof (m_MessagesHandlers)); m_MessagesHandlers[I2CP_GET_DATE_MESSAGE] = &I2CPSession::GetDateMessageHandler; @@ -1133,12 +1110,12 @@ namespace client void I2CPServer::Stop () { m_Acceptor.cancel (); - - decltype(m_Sessions) sessions; - m_Sessions.swap (sessions); - for (auto& it: sessions) - it.second->Stop (); - + { + auto sessions = m_Sessions; + for (auto& it: sessions) + it.second->Stop (); + } + m_Sessions.clear (); StopIOService (); } diff --git a/libi2pd_client/I2CP.h b/libi2pd_client/I2CP.h index 37c14dbb..620ff52e 100644 --- a/libi2pd_client/I2CP.h +++ b/libi2pd_client/I2CP.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2025, The PurpleI2P Project +* Copyright (c) 2013-2024, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -11,7 +11,6 @@ #include #include -#include #include #include #include @@ -21,7 +20,6 @@ #include "util.h" #include "Destination.h" #include "Streaming.h" -#include "CryptoKey.h" namespace i2p { @@ -32,8 +30,6 @@ namespace client const size_t I2CP_MAX_MESSAGE_LENGTH = 65535; const size_t I2CP_MAX_SEND_QUEUE_SIZE = 1024*1024; // in bytes, 1M const int I2CP_LEASESET_CREATION_TIMEOUT = 10; // in seconds - const int I2CP_DESTINATION_READINESS_CHECK_INTERVAL = 5; // in seconds - const int I2CP_SESSION_ACK_REQUEST_INTERVAL = 12100; // in milliseconds const size_t I2CP_HEADER_LENGTH_OFFSET = 0; const size_t I2CP_HEADER_TYPE_OFFSET = I2CP_HEADER_LENGTH_OFFSET + 4; @@ -85,12 +81,12 @@ namespace client { public: - I2CPDestination (boost::asio::io_context& service, std::shared_ptr owner, + I2CPDestination (boost::asio::io_service& service, std::shared_ptr owner, std::shared_ptr identity, bool isPublic, bool isSameThread, const std::map& params); ~I2CPDestination () {}; - void Stop () override; + void Stop (); void SetEncryptionPrivateKey (const uint8_t * key); void SetEncryptionType (i2p::data::CryptoKeyType keyType) { m_EncryptionKeyType = keyType; }; @@ -101,25 +97,18 @@ namespace client bool SendMsg (const uint8_t * payload, size_t len, std::shared_ptr remoteSession, uint32_t nonce); // implements LocalDestination - bool Decrypt (const uint8_t * encrypted, uint8_t * data, i2p::data::CryptoKeyType preferredCrypto) const override; - bool SupportsEncryptionType (i2p::data::CryptoKeyType keyType) const override; - const uint8_t * GetEncryptionPublicKey (i2p::data::CryptoKeyType keyType) const override; // for 4 only - std::shared_ptr GetIdentity () const override { return m_Identity; }; + bool Decrypt (const uint8_t * encrypted, uint8_t * data, i2p::data::CryptoKeyType preferredCrypto) const; + bool SupportsEncryptionType (i2p::data::CryptoKeyType keyType) const; + const uint8_t * GetEncryptionPublicKey (i2p::data::CryptoKeyType keyType) const; // for 4 only + std::shared_ptr GetIdentity () const { return m_Identity; }; protected: - // GarlicDestination - i2p::data::CryptoKeyType GetRatchetsHighestCryptoType () const override - { - return m_ECIESx25519Decryptor ? i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD : 0; - } - // LeaseSetDestination - void CleanupDestination () override; - i2p::data::CryptoKeyType GetPreferredCryptoType () const override; + void CleanupDestination (); // I2CP - void HandleDataMessage (const uint8_t * buf, size_t len) override; - void CreateNewLeaseSet (const std::vector >& tunnels) override; - + void HandleDataMessage (const uint8_t * buf, size_t len); + void CreateNewLeaseSet (const std::vector >& tunnels); + private: std::shared_ptr GetSharedFromThis () @@ -140,7 +129,7 @@ namespace client uint8_t m_ECIESx25519PrivateKey[32]; uint64_t m_LeaseSetExpirationTime; bool m_IsCreatingLeaseSet, m_IsSameThread; - boost::asio::deadline_timer m_LeaseSetCreationTimer, m_ReadinessCheckTimer; + boost::asio::deadline_timer m_LeaseSetCreationTimer; i2p::util::MemoryPoolMt > m_I2NPMsgsPool; }; @@ -202,9 +191,9 @@ namespace client void HandleI2CPMessageSent (const boost::system::error_code& ecode, std::size_t bytes_transferred); - std::string_view ExtractString (const uint8_t * buf, size_t len) const; - size_t PutString (uint8_t * buf, size_t len, std::string_view str); - void ExtractMapping (const uint8_t * buf, size_t len, std::map& mapping) const; + std::string ExtractString (const uint8_t * buf, size_t len); + size_t PutString (uint8_t * buf, size_t len, const std::string& str); + void ExtractMapping (const uint8_t * buf, size_t len, std::map& mapping); void SendSessionStatusMessage (I2CPSessionStatus status); void SendHostReplyMessage (uint32_t requestID, std::shared_ptr identity); @@ -238,7 +227,7 @@ namespace client void Start (); void Stop (); - auto& GetService () { return GetIOService (); }; + boost::asio::io_service& GetService () { return GetIOService (); }; bool IsSingleThread () const { return m_IsSingleThread; }; bool InsertSession (std::shared_ptr session); diff --git a/libi2pd_client/I2PService.cpp b/libi2pd_client/I2PService.cpp index 4ec2648a..e9513e48 100644 --- a/libi2pd_client/I2PService.cpp +++ b/libi2pd_client/I2PService.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2025, 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, std::string_view dest, uint16_t 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) diff --git a/libi2pd_client/I2PService.h b/libi2pd_client/I2PService.h index d19c28e2..d35c954d 100644 --- a/libi2pd_client/I2PService.h +++ b/libi2pd_client/I2PService.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2025, The PurpleI2P Project +* Copyright (c) 2013-2024, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -59,9 +59,9 @@ namespace client if (dest) dest->Acquire (); m_LocalDestination = dest; } - void CreateStream (StreamRequestComplete streamRequestComplete, std::string_view dest, uint16_t port = 0); + void CreateStream (StreamRequestComplete streamRequestComplete, const std::string& dest, uint16_t port = 0); void CreateStream(StreamRequestComplete complete, std::shared_ptr address, uint16_t port); - auto& GetService () { return m_LocalDestination->GetService (); } + inline boost::asio::io_service& GetService () { return m_LocalDestination->GetService (); } virtual void Start () = 0; virtual void Stop () = 0; @@ -283,7 +283,7 @@ namespace client public: TCPIPAcceptor (const std::string& address, uint16_t port, std::shared_ptr localDestination = nullptr) : - ServiceAcceptor (boost::asio::ip::tcp::endpoint (boost::asio::ip::make_address(address), port), localDestination) {} + 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 ae9849db..ad4e14b8 100644 --- a/libi2pd_client/I2PTunnel.cpp +++ b/libi2pd_client/I2PTunnel.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2025, The PurpleI2P Project +* Copyright (c) 2013-2023, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -33,7 +33,7 @@ namespace client I2PTunnelConnection::I2PTunnelConnection (I2PService * owner, std::shared_ptr socket, std::shared_ptr leaseSet, uint16_t port): I2PServiceHandler(owner), m_Socket (socket), m_RemoteEndpoint (socket->remote_endpoint ()), - m_IsReceiving (false) + m_IsQuiet (true) { m_Stream = GetOwner()->GetLocalDestination ()->CreateStream (leaseSet, port); } @@ -41,13 +41,14 @@ namespace client I2PTunnelConnection::I2PTunnelConnection (I2PService * owner, std::shared_ptr socket, std::shared_ptr stream): I2PServiceHandler(owner), m_Socket (socket), m_Stream (stream), - m_RemoteEndpoint (socket->remote_endpoint ()), m_IsReceiving (false) + m_RemoteEndpoint (socket->remote_endpoint ()), m_IsQuiet (true) { } I2PTunnelConnection::I2PTunnelConnection (I2PService * owner, std::shared_ptr stream, - const boost::asio::ip::tcp::endpoint& target,std::shared_ptr sslCtx): - I2PServiceHandler(owner), m_Stream (stream), m_RemoteEndpoint (target), m_IsReceiving (false) + const boost::asio::ip::tcp::endpoint& target, bool quiet, + std::shared_ptr sslCtx): + I2PServiceHandler(owner), m_Stream (stream), m_RemoteEndpoint (target), m_IsQuiet (quiet) { m_Socket = std::make_shared (owner->GetService ()); if (sslCtx) @@ -150,23 +151,18 @@ namespace client void I2PTunnelConnection::Receive () { - if (m_IsReceiving) return; // already receiving - size_t unsentSize = m_Stream ? m_Stream->GetSendBufferSize () : 0; - if (unsentSize >= I2P_TUNNEL_CONNECTION_BUFFER_SIZE) return; // buffer is full - m_IsReceiving = true; if (m_SSL) - m_SSL->async_read_some (boost::asio::buffer(m_Buffer, I2P_TUNNEL_CONNECTION_BUFFER_SIZE - unsentSize), + m_SSL->async_read_some (boost::asio::buffer(m_Buffer, I2P_TUNNEL_CONNECTION_BUFFER_SIZE), std::bind(&I2PTunnelConnection::HandleReceive, shared_from_this (), std::placeholders::_1, std::placeholders::_2)); else - m_Socket->async_read_some (boost::asio::buffer(m_Buffer, I2P_TUNNEL_CONNECTION_BUFFER_SIZE - unsentSize), + m_Socket->async_read_some (boost::asio::buffer(m_Buffer, I2P_TUNNEL_CONNECTION_BUFFER_SIZE), std::bind(&I2PTunnelConnection::HandleReceive, shared_from_this (), std::placeholders::_1, std::placeholders::_2)); } void I2PTunnelConnection::HandleReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred) { - m_IsReceiving = false; if (ecode) { if (ecode != boost::asio::error::operation_aborted) @@ -176,18 +172,16 @@ namespace client } } else - { WriteToStream (m_Buffer, bytes_transferred); - Receive (); // try to receive more while being sent to stream - } } void I2PTunnelConnection::WriteToStream (const uint8_t * buf, size_t len) { if (m_Stream) { + auto s = shared_from_this (); m_Stream->AsyncSend (buf, len, - [s = shared_from_this ()](const boost::system::error_code& ecode) + [s](const boost::system::error_code& ecode) { if (!ecode) s->Receive (); @@ -298,7 +292,18 @@ namespace client void I2PTunnelConnection::Established () { - StreamReceive (); + if (m_IsQuiet) + StreamReceive (); + else + { + // send destination first like received from I2P + std::string dest = m_Stream->GetRemoteIdentity ()->ToBase64 (); + dest += "\n"; + if(sizeof(m_StreamBuffer) >= dest.size()) { + memcpy (m_StreamBuffer, dest.c_str (), dest.size ()); + } + HandleStreamReceive (boost::system::error_code (), dest.size ()); + } Receive (); } @@ -370,10 +375,10 @@ namespace client } I2PServerTunnelConnectionHTTP::I2PServerTunnelConnectionHTTP (I2PService * owner, std::shared_ptr stream, - const boost::asio::ip::tcp::endpoint& target, const std::string& host, const std::string& XI2P, + const boost::asio::ip::tcp::endpoint& target, const std::string& host, std::shared_ptr sslCtx): - I2PTunnelConnection (owner, stream, target, sslCtx), m_Host (host), m_XI2P (XI2P), - m_HeaderSent (false), m_ResponseHeaderSent (false) + I2PTunnelConnection (owner, stream, target, true, sslCtx), m_Host (host), + m_HeaderSent (false), m_ResponseHeaderSent (false), m_From (stream->GetRemoteIdentity ()) { if (sslCtx) SSL_set_tlsext_host_name(GetSSL ()->native_handle(), host.c_str ()); @@ -399,7 +404,7 @@ namespace client else { // strip up some headers - static const std::array excluded // list of excluded headers + static const std::vector excluded // list of excluded headers { "Keep-Alive:", "X-I2P" }; @@ -443,12 +448,17 @@ namespace client if (!connection) m_OutHeader << "Connection: close\r\n"; // add X-I2P fields - m_OutHeader << m_XI2P; - // end of header - m_OutHeader << "\r\n"; - + if (m_From) + { + m_OutHeader << X_I2P_DEST_B32 << ": " << context.GetAddressBook ().ToAddress(m_From->GetIdentHash ()) << "\r\n"; + m_OutHeader << X_I2P_DEST_HASH << ": " << m_From->GetIdentHash ().ToBase64 () << "\r\n"; + m_OutHeader << X_I2P_DEST_B64 << ": " << m_From->ToBase64 () << "\r\n"; + } + + m_OutHeader << "\r\n"; // end of header m_OutHeader << m_InHeader.str ().substr (m_InHeader.tellg ()); // data right after header m_InHeader.str (""); + m_From = nullptr; m_HeaderSent = true; I2PTunnelConnection::Write ((uint8_t *)m_OutHeader.str ().c_str (), m_OutHeader.str ().length ()); } @@ -482,7 +492,7 @@ namespace client if (line == "\r") endOfHeader = true; else { - static const std::array excluded // list of excluded headers + static const std::vector excluded // list of excluded headers { "Server:", "Date:", "X-Runtime:", "X-Powered-By:", "Proxy" }; @@ -523,7 +533,7 @@ namespace client I2PTunnelConnectionIRC::I2PTunnelConnectionIRC (I2PService * owner, std::shared_ptr stream, const boost::asio::ip::tcp::endpoint& target, const std::string& webircpass, std::shared_ptr sslCtx): - I2PTunnelConnection (owner, stream, target, sslCtx), m_From (stream->GetRemoteIdentity ()), + I2PTunnelConnection (owner, stream, target, true, sslCtx), m_From (stream->GetRemoteIdentity ()), m_NeedsWebIrc (webircpass.length() ? true : false), m_WebircPass (webircpass) { } @@ -707,7 +717,7 @@ namespace client { m_Endpoint.port (m_Port); boost::system::error_code ec; - auto addr = boost::asio::ip::make_address (m_Address, ec); + auto addr = boost::asio::ip::address::from_string (m_Address, ec); if (!ec) { m_Endpoint.address (addr); @@ -716,7 +726,7 @@ namespace client else { auto resolver = std::make_shared(GetService ()); - resolver->async_resolve (m_Address, "", + resolver->async_resolve (boost::asio::ip::tcp::resolver::query (m_Address, ""), std::bind (&I2PServerTunnel::HandleResolve, this, std::placeholders::_1, std::placeholders::_2, resolver)); } @@ -733,7 +743,7 @@ namespace client ClearHandlers (); } - void I2PServerTunnel::HandleResolve (const boost::system::error_code& ecode, boost::asio::ip::tcp::resolver::results_type endpoints, + void I2PServerTunnel::HandleResolve (const boost::system::error_code& ecode, boost::asio::ip::tcp::resolver::iterator it, std::shared_ptr resolver) { if (!ecode) @@ -742,9 +752,10 @@ namespace client boost::asio::ip::tcp::endpoint ep; if (m_LocalAddress) { - for (const auto& it: endpoints) + boost::asio::ip::tcp::resolver::iterator end; + while (it != end) { - ep = it; + ep = *it; if (!ep.address ().is_unspecified ()) { if (ep.address ().is_v4 ()) @@ -763,26 +774,27 @@ namespace client } } if (found) break; + it++; } } else { found = true; - ep = *endpoints.begin (); // first available + ep = *it; // first available } if (!found) { - LogPrint (eLogError, "I2PTunnel: Unable to resolve ", m_Address, " to compatible address"); + LogPrint (eLogError, "I2PTunnel: Unable to resolve to compatible address"); return; } auto addr = ep.address (); - LogPrint (eLogInfo, "I2PTunnel: Server tunnel ", (*endpoints.begin ()).host_name (), " has been resolved to ", addr); + LogPrint (eLogInfo, "I2PTunnel: Server tunnel ", (*it).host_name (), " has been resolved to ", addr); m_Endpoint.address (addr); Accept (); } else - LogPrint (eLogError, "I2PTunnel: Unable to resolve server tunnel address ", m_Address, ": ", ecode.message ()); + LogPrint (eLogError, "I2PTunnel: Unable to resolve server tunnel address: ", ecode.message ()); } void I2PServerTunnel::SetAccessList (const std::set& accessList) @@ -794,7 +806,7 @@ namespace client void I2PServerTunnel::SetLocalAddress (const std::string& localAddress) { boost::system::error_code ec; - auto addr = boost::asio::ip::make_address(localAddress, ec); + auto addr = boost::asio::ip::address::from_string(localAddress, ec); if (!ec) m_LocalAddress.reset (new boost::asio::ip::address (addr)); else @@ -852,7 +864,7 @@ namespace client std::shared_ptr I2PServerTunnel::CreateI2PConnection (std::shared_ptr stream) { - return std::make_shared (this, stream, GetEndpoint (), m_SSLCtx); + return std::make_shared (this, stream, GetEndpoint (), true, m_SSLCtx); } @@ -866,17 +878,7 @@ namespace client std::shared_ptr I2PServerTunnelHTTP::CreateI2PConnection (std::shared_ptr stream) { - if (m_XI2P.empty () || stream->GetRemoteIdentity () != m_From.lock ()) - { - auto from = stream->GetRemoteIdentity (); - m_From = from; - std::stringstream ss; - ss << X_I2P_DEST_B32 << ": " << context.GetAddressBook ().ToAddress(from->GetIdentHash ()) << "\r\n"; - ss << X_I2P_DEST_HASH << ": " << from->GetIdentHash ().ToBase64 () << "\r\n"; - ss << X_I2P_DEST_B64 << ": " << from->ToBase64 () << "\r\n"; - m_XI2P = ss.str (); - } - return std::make_shared (this, stream, GetEndpoint (), m_Host, m_XI2P, GetSSLCtx ()); + return std::make_shared (this, stream, GetEndpoint (), m_Host, GetSSLCtx ()); } I2PServerTunnelIRC::I2PServerTunnelIRC (const std::string& name, const std::string& address, diff --git a/libi2pd_client/I2PTunnel.h b/libi2pd_client/I2PTunnel.h index be35cfec..b94eb9e4 100644 --- a/libi2pd_client/I2PTunnel.h +++ b/libi2pd_client/I2PTunnel.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2025, The PurpleI2P Project +* Copyright (c) 2013-2023, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -27,13 +27,13 @@ namespace i2p { namespace client { - const size_t I2P_TUNNEL_CONNECTION_BUFFER_SIZE = 16384; + const size_t I2P_TUNNEL_CONNECTION_BUFFER_SIZE = 65536; const int I2P_TUNNEL_CONNECTION_MAX_IDLE = 3600; // in seconds const int I2P_TUNNEL_DESTINATION_REQUEST_TIMEOUT = 10; // in seconds // for HTTP tunnels - constexpr char X_I2P_DEST_HASH[] = "X-I2P-DestHash"; // hash in base64 - constexpr char X_I2P_DEST_B64[] = "X-I2P-DestB64"; // full address in base64 - constexpr char X_I2P_DEST_B32[] = "X-I2P-DestB32"; // .b32.i2p address + const char X_I2P_DEST_HASH[] = "X-I2P-DestHash"; // hash in base64 + const char X_I2P_DEST_B64[] = "X-I2P-DestB64"; // full address in base64 + const char X_I2P_DEST_B32[] = "X-I2P-DestB32"; // .b32.i2p address const int I2P_TUNNEL_HTTP_MAX_HEADER_SIZE = 8192; class I2PTunnelConnection: public I2PServiceHandler, public std::enable_shared_from_this @@ -45,7 +45,7 @@ namespace client I2PTunnelConnection (I2PService * owner, std::shared_ptr socket, std::shared_ptr stream); // to I2P using simplified API I2PTunnelConnection (I2PService * owner, std::shared_ptr stream, - const boost::asio::ip::tcp::endpoint& target, + const boost::asio::ip::tcp::endpoint& target, bool quiet = true, std::shared_ptr sslCtx = nullptr); // from I2P ~I2PTunnelConnection (); void I2PConnect (const uint8_t * msg = nullptr, size_t len = 0); @@ -54,27 +54,25 @@ namespace client protected: - virtual void Established (); void Terminate (); void Receive (); void StreamReceive (); - void HandleStreamReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred); virtual void Write (const uint8_t * buf, size_t len); // can be overloaded virtual void WriteToStream (const uint8_t * buf, size_t len); // can be overloaded std::shared_ptr GetSocket () const { return m_Socket; }; - std::shared_ptr GetStream () const { return m_Stream; }; std::shared_ptr > GetSSL () const { return m_SSL; }; - uint8_t * GetStreamBuffer () { return m_StreamBuffer; }; - + private: void HandleConnect (const boost::system::error_code& ecode); void HandleHandshake (const boost::system::error_code& ecode); + void Established (); void HandleReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred); void HandleWrite (const boost::system::error_code& ecode); - + void HandleStreamReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred); + private: uint8_t m_Buffer[I2P_TUNNEL_CONNECTION_BUFFER_SIZE], m_StreamBuffer[I2P_TUNNEL_CONNECTION_BUFFER_SIZE]; @@ -82,7 +80,7 @@ namespace client std::shared_ptr > m_SSL; std::shared_ptr m_Stream; boost::asio::ip::tcp::endpoint m_RemoteEndpoint; - bool m_IsReceiving; + bool m_IsQuiet; // don't send destination }; class I2PClientTunnelConnectionHTTP: public I2PTunnelConnection @@ -96,7 +94,7 @@ namespace client protected: - void Write (const uint8_t * buf, size_t len) override; + void Write (const uint8_t * buf, size_t len); private: @@ -109,19 +107,20 @@ namespace client public: I2PServerTunnelConnectionHTTP (I2PService * owner, std::shared_ptr stream, - const boost::asio::ip::tcp::endpoint& target, const std::string& host, const std::string& XI2P, + const boost::asio::ip::tcp::endpoint& target, const std::string& host, std::shared_ptr sslCtx = nullptr); protected: - void Write (const uint8_t * buf, size_t len) override; - void WriteToStream (const uint8_t * buf, size_t len) override; + void Write (const uint8_t * buf, size_t len); + void WriteToStream (const uint8_t * buf, size_t len); private: - std::string m_Host, m_XI2P; + std::string m_Host; std::stringstream m_InHeader, m_OutHeader; bool m_HeaderSent, m_ResponseHeaderSent; + std::shared_ptr m_From; }; class I2PTunnelConnectionIRC: public I2PTunnelConnection @@ -134,7 +133,7 @@ namespace client protected: - void Write (const uint8_t * buf, size_t len) override; + void Write (const uint8_t * buf, size_t len); private: @@ -209,7 +208,7 @@ namespace client private: - void HandleResolve (const boost::system::error_code& ecode, boost::asio::ip::tcp::resolver::results_type endpoints, + void HandleResolve (const boost::system::error_code& ecode, boost::asio::ip::tcp::resolver::iterator it, std::shared_ptr resolver); void Accept (); @@ -243,8 +242,7 @@ namespace client private: - std::string m_Host, m_XI2P; - std::weak_ptr m_From; + std::string m_Host; }; class I2PServerTunnelIRC: public I2PServerTunnel diff --git a/libi2pd_client/SAM.cpp b/libi2pd_client/SAM.cpp index 2c0f8d92..e4cbae8a 100644 --- a/libi2pd_client/SAM.cpp +++ b/libi2pd_client/SAM.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2025, The PurpleI2P Project +* Copyright (c) 2013-2024, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -43,21 +43,23 @@ namespace client m_Stream->AsyncClose (); m_Stream = nullptr; } + auto Session = m_Owner.FindSession(m_ID); switch (m_SocketType) { case eSAMSocketTypeSession: m_Owner.CloseSession (m_ID); break; case eSAMSocketTypeStream: - break; + { + break; + } case eSAMSocketTypeAcceptor: case eSAMSocketTypeForward: { - auto session = m_Owner.FindSession(m_ID); - if (session) + if (Session) { - if (m_IsAccepting && session->GetLocalDestination ()) - session->GetLocalDestination ()->StopAcceptingStreams (); + if (m_IsAccepting && Session->GetLocalDestination ()) + Session->GetLocalDestination ()->StopAcceptingStreams (); } break; } @@ -369,7 +371,7 @@ namespace client // udp forward selected boost::system::error_code e; // TODO: support hostnames in udp forward - auto addr = boost::asio::ip::make_address(params[SAM_PARAM_HOST], e); + auto addr = boost::asio::ip::address::from_string(params[SAM_PARAM_HOST], e); if (e) { // not an ip address @@ -413,7 +415,7 @@ namespace client { session->UDPEndpoint = forward; auto dest = session->GetLocalDestination ()->CreateDatagramDestination (); - auto port = forward ? std::stoi(params[SAM_PARAM_PORT]) : 0; + 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), @@ -468,11 +470,15 @@ namespace client auto session = m_Owner.FindSession(m_ID); if (session) { - std::string priv = session->GetLocalDestination ()->GetPrivateKeys ().ToBase64 (); + uint8_t buf[1024]; + char priv[1024]; + size_t l = session->GetLocalDestination ()->GetPrivateKeys ().ToBuffer (buf, 1024); + size_t l1 = i2p::data::ByteStreamToBase64 (buf, l, priv, 1024); + priv[l1] = 0; #ifdef _MSC_VER - size_t l2 = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_SESSION_CREATE_REPLY_OK, priv.c_str ()); + size_t l2 = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_SESSION_CREATE_REPLY_OK, priv); #else - size_t l2 = snprintf (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_SESSION_CREATE_REPLY_OK, priv.c_str ()); + size_t l2 = snprintf (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_SESSION_CREATE_REPLY_OK, priv); #endif SendMessageReply (m_Buffer, l2, false); } @@ -618,7 +624,7 @@ namespace client auto socket = session->acceptQueue.front ().first; session->acceptQueue.pop_front (); if (socket) - boost::asio::post (m_Owner.GetService (), std::bind(&SAMSocket::TerminateClose, socket)); + m_Owner.GetService ().post (std::bind(&SAMSocket::TerminateClose, socket)); } if (session->acceptQueue.size () < SAM_SESSION_MAX_ACCEPT_QUEUE_SIZE) { @@ -1040,13 +1046,13 @@ namespace client else { auto s = shared_from_this (); - boost::asio::post (m_Owner.GetService (), [s] { s->Terminate ("stream read error"); }); + m_Owner.GetService ().post ([s] { s->Terminate ("stream read error"); }); } } else { auto s = shared_from_this (); - boost::asio::post (m_Owner.GetService (), [s] { s->Terminate ("stream read error (op aborted)"); }); + m_Owner.GetService ().post ([s] { s->Terminate ("stream read error (op aborted)"); }); } } else @@ -1096,7 +1102,7 @@ namespace client auto socket = session->acceptQueue.front ().first; session->acceptQueue.pop_front (); if (socket) - boost::asio::post (m_Owner.GetService (), std::bind(&SAMSocket::TerminateClose, socket)); + m_Owner.GetService ().post (std::bind(&SAMSocket::TerminateClose, socket)); } if (!session->acceptQueue.empty ()) { @@ -1110,19 +1116,18 @@ namespace client } } if (!m_IsSilent) - { - if (m_SocketType != eSAMSocketTypeTerminated) - { - // get remote peer address - auto ident = std::make_shared(stream->GetRemoteIdentity()->ToBase64 ()); // we need to keep it until sent - ident->push_back ('\n'); - // send remote peer address back to client like received from stream - boost::asio::async_write (m_Socket, boost::asio::buffer (ident->data (), ident->size ()), boost::asio::transfer_all(), - [ident, s = shared_from_this ()](const boost::system::error_code& ecode, size_t bytes_transferred) - { - s->HandleWriteI2PData (ecode, bytes_transferred); - }); - } + { + // get remote peer address + auto ident_ptr = stream->GetRemoteIdentity(); + const size_t ident_len = ident_ptr->GetFullLen(); + uint8_t* ident = new uint8_t[ident_len]; + + // send remote peer address as base64 + const size_t l = ident_ptr->ToBuffer (ident, ident_len); + const size_t l1 = i2p::data::ByteStreamToBase64 (ident, l, (char *)m_StreamBuffer, SAM_SOCKET_BUFFER_SIZE); + delete[] ident; + m_StreamBuffer[l1] = '\n'; + HandleI2PReceive (boost::system::error_code (), l1 +1); // we send identity like it has been received from stream } else I2PReceive (); @@ -1231,7 +1236,7 @@ namespace client void SAMSocket::HandleStreamSend(const boost::system::error_code & ec) { - boost::asio::post (m_Owner.GetService (), std::bind( !ec ? &SAMSocket::Receive : &SAMSocket::TerminateClose, shared_from_this())); + m_Owner.GetService ().post (std::bind( !ec ? &SAMSocket::Receive : &SAMSocket::TerminateClose, shared_from_this())); } SAMSession::SAMSession (SAMBridge & parent, const std::string & id, SAMSessionType type): @@ -1305,8 +1310,8 @@ namespace client SAMBridge::SAMBridge (const std::string& address, uint16_t portTCP, uint16_t portUDP, bool singleThread): RunnableService ("SAM"), m_IsSingleThread (singleThread), - m_Acceptor (GetIOService (), boost::asio::ip::tcp::endpoint(boost::asio::ip::make_address(address), portTCP)), - m_DatagramEndpoint (boost::asio::ip::make_address(address), (!portUDP) ? portTCP-1 : portUDP), m_DatagramSocket (GetIOService (), m_DatagramEndpoint), + m_Acceptor (GetIOService (), boost::asio::ip::tcp::endpoint(boost::asio::ip::address::from_string(address), portTCP)), + m_DatagramEndpoint (boost::asio::ip::address::from_string(address), (!portUDP) ? portTCP-1 : portUDP), m_DatagramSocket (GetIOService (), m_DatagramEndpoint), m_SignatureTypes { {"DSA_SHA1", i2p::data::SIGNING_KEY_TYPE_DSA_SHA1}, @@ -1345,14 +1350,12 @@ namespace client LogPrint (eLogError, "SAM: Runtime exception: ", ex.what ()); } - decltype(m_Sessions) sessions; { std::unique_lock l(m_SessionsMutex); - m_Sessions.swap (sessions); - } - for (auto& it: sessions) - it.second->Close (); - + for (auto& it: m_Sessions) + it.second->Close (); + m_Sessions.clear (); + } StopIOService (); } @@ -1477,39 +1480,17 @@ namespace client session->StopLocalDestination (); session->Close (); if (m_IsSingleThread) - ScheduleSessionCleanupTimer (session); // let all session's streams close + { + auto timer = std::make_shared(GetService ()); + timer->expires_from_now (boost::posix_time::seconds(5)); // postpone destination clean for 5 seconds + timer->async_wait ([timer, session](const boost::system::error_code& ecode) + { + // session's destructor is called here + }); + } } } - void SAMBridge::ScheduleSessionCleanupTimer (std::shared_ptr session) - { - auto timer = std::make_shared(GetService ()); - timer->expires_from_now (boost::posix_time::seconds(5)); // postpone destination clean for 5 seconds - timer->async_wait (std::bind (&SAMBridge::HandleSessionCleanupTimer, this, std::placeholders::_1, session, timer)); - } - - void SAMBridge::HandleSessionCleanupTimer (const boost::system::error_code& ecode, - std::shared_ptr session, std::shared_ptr timer) - { - if (ecode != boost::asio::error::operation_aborted && session) - { - auto dest = session->GetLocalDestination (); - if (dest) - { - auto streamingDest = dest->GetStreamingDestination (); - auto numStreams = streamingDest->GetNumStreams (); - if (numStreams > 0) - { - LogPrint (eLogInfo, "SAM: Session ", session->Name, " still has ", numStreams, " streams"); - ScheduleSessionCleanupTimer (session); - } - else - LogPrint (eLogDebug, "SAM: Session ", session->Name, " terminated"); - } - } - // session's destructor is called here unless rescheduled - } - std::shared_ptr SAMBridge::FindSession (const std::string& id) const { std::unique_lock l(m_SessionsMutex); diff --git a/libi2pd_client/SAM.h b/libi2pd_client/SAM.h index 1886324a..3ed8f00c 100644 --- a/libi2pd_client/SAM.h +++ b/libi2pd_client/SAM.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2025, The PurpleI2P Project +* Copyright (c) 2013-2024, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -246,7 +246,7 @@ namespace client void Start (); void Stop (); - auto& GetService () { return GetIOService (); }; + boost::asio::io_service& GetService () { return GetIOService (); }; std::shared_ptr CreateSession (const std::string& id, SAMSessionType type, const std::string& destination, // empty string means transient const std::map * params); bool AddSession (std::shared_ptr session); @@ -271,10 +271,6 @@ namespace client void ReceiveDatagram (); void HandleReceivedDatagram (const boost::system::error_code& ecode, std::size_t bytes_transferred); - void ScheduleSessionCleanupTimer (std::shared_ptr session); - void HandleSessionCleanupTimer (const boost::system::error_code& ecode, - std::shared_ptr session, std::shared_ptr timer); - private: bool m_IsSingleThread; diff --git a/libi2pd_client/SOCKS.cpp b/libi2pd_client/SOCKS.cpp index 27df33c8..0961690b 100644 --- a/libi2pd_client/SOCKS.cpp +++ b/libi2pd_client/SOCKS.cpp @@ -126,8 +126,9 @@ namespace proxy void HandleSockRecv(const boost::system::error_code & ecode, std::size_t bytes_transfered); void Terminate(); void AsyncSockRead(); - boost::asio::const_buffer GenerateSOCKS4Response(errTypes error, uint32_t ip, uint16_t port); - boost::asio::const_buffer GenerateSOCKS5Response(errTypes error, addrTypes type, const address &addr, uint16_t port); + 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); bool Socks5ChooseAuth(); void Socks5UserPasswdResponse (); void SocksRequestFailed(errTypes error); @@ -144,9 +145,9 @@ namespace proxy template void SendUpstreamRequest(std::shared_ptr& upstreamSock); void HandleUpstreamConnected(const boost::system::error_code & ecode, - const boost::asio::ip::tcp::endpoint& ep); + boost::asio::ip::tcp::resolver::iterator itr); void HandleUpstreamResolved(const boost::system::error_code & ecode, - boost::asio::ip::tcp::resolver::results_type endpoints); + boost::asio::ip::tcp::resolver::iterator itr); boost::asio::ip::tcp::resolver m_proxy_resolver; uint8_t m_sock_buff[socks_buffer_size]; @@ -232,17 +233,17 @@ namespace proxy Done(shared_from_this()); } - boost::asio::const_buffer SOCKSHandler::GenerateSOCKS4Response(SOCKSHandler::errTypes error, uint32_t ip, uint16_t port) + boost::asio::const_buffers_1 SOCKSHandler::GenerateSOCKS4Response(SOCKSHandler::errTypes error, uint32_t ip, uint16_t port) { assert(error >= SOCKS4_OK); m_response[0] = '\x00'; // version m_response[1] = error; // response code htobe16buf(m_response + 2, port); // port htobe32buf(m_response + 4, ip); // IP - return boost::asio::const_buffer (m_response,8); + return boost::asio::const_buffers_1(m_response,8); } - boost::asio::const_buffer SOCKSHandler::GenerateSOCKS5Response(SOCKSHandler::errTypes error, SOCKSHandler::addrTypes type, const SOCKSHandler::address &addr, uint16_t port) + boost::asio::const_buffers_1 SOCKSHandler::GenerateSOCKS5Response(SOCKSHandler::errTypes error, SOCKSHandler::addrTypes type, const SOCKSHandler::address &addr, uint16_t port) { size_t size = 6; // header + port assert(error <= SOCKS5_ADDR_UNSUP); @@ -279,14 +280,14 @@ namespace proxy } break; } - return boost::asio::const_buffer (m_response, size); + return boost::asio::const_buffers_1(m_response, size); } bool SOCKSHandler::Socks5ChooseAuth() { m_response[0] = '\x05'; // Version m_response[1] = m_authchosen; // Response code - boost::asio::const_buffer response(m_response, 2); + boost::asio::const_buffers_1 response(m_response, 2); if (m_authchosen == AUTH_UNACCEPTABLE) { LogPrint(eLogWarning, "SOCKS: v5 authentication negotiation failed"); @@ -306,14 +307,14 @@ namespace proxy m_response[0] = 1; // Version of the subnegotiation m_response[1] = 0; // Response code LogPrint(eLogDebug, "SOCKS: v5 user/password response"); - boost::asio::async_write(*m_sock, boost::asio::const_buffer(m_response, 2), + 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) { - boost::asio::const_buffer response(nullptr,0); + boost::asio::const_buffers_1 response(nullptr,0); assert(error != SOCKS4_OK && error != SOCKS5_OK); switch (m_socksv) { @@ -333,7 +334,7 @@ namespace proxy void SOCKSHandler::SocksRequestSuccess() { - boost::asio::const_buffer response(nullptr,0); + boost::asio::const_buffers_1 response(nullptr,0); // TODO: this should depend on things like the command type and callbacks may change switch (m_socksv) { @@ -690,8 +691,9 @@ namespace proxy if (m_UpstreamProxyPort) // TCP { EnterState(UPSTREAM_RESOLVE); - m_proxy_resolver.async_resolve(m_UpstreamProxyAddress, std::to_string(m_UpstreamProxyPort), - std::bind(&SOCKSHandler::HandleUpstreamResolved, shared_from_this(), std::placeholders::_1, std::placeholders::_2)); + 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 { @@ -727,7 +729,7 @@ namespace proxy void SOCKSHandler::SocksUpstreamSuccess(std::shared_ptr& upstreamSock) { LogPrint(eLogInfo, "SOCKS: Upstream success"); - boost::asio::const_buffer response(nullptr, 0); + boost::asio::const_buffers_1 response(nullptr, 0); switch (m_socksv) { case SOCKS4: @@ -773,8 +775,7 @@ namespace proxy LogPrint(eLogError, "SOCKS: No upstream socket to send handshake to"); } - void SOCKSHandler::HandleUpstreamConnected(const boost::system::error_code & ecode, - const boost::asio::ip::tcp::endpoint& ep) + void SOCKSHandler::HandleUpstreamConnected(const boost::system::error_code & ecode, boost::asio::ip::tcp::resolver::iterator itr) { if (ecode) { LogPrint(eLogWarning, "SOCKS: Could not connect to upstream proxy: ", ecode.message()); @@ -785,8 +786,7 @@ namespace proxy SendUpstreamRequest(m_upstreamSock); } - void SOCKSHandler::HandleUpstreamResolved(const boost::system::error_code & ecode, - boost::asio::ip::tcp::resolver::results_type endpoints) + void SOCKSHandler::HandleUpstreamResolved(const boost::system::error_code & ecode, boost::asio::ip::tcp::resolver::iterator itr) { if (ecode) { // error resolving @@ -798,7 +798,7 @@ namespace proxy EnterState(UPSTREAM_CONNECT); auto & service = GetOwner()->GetService(); m_upstreamSock = std::make_shared(service); - boost::asio::async_connect(*m_upstreamSock, endpoints, + boost::asio::async_connect(*m_upstreamSock, itr, std::bind(&SOCKSHandler::HandleUpstreamConnected, shared_from_this(), std::placeholders::_1, std::placeholders::_2)); } diff --git a/libi2pd_client/UDPTunnel.cpp b/libi2pd_client/UDPTunnel.cpp index b173fc0f..cd17bbf0 100644 --- a/libi2pd_client/UDPTunnel.cpp +++ b/libi2pd_client/UDPTunnel.cpp @@ -86,7 +86,7 @@ namespace client } else { - LogPrint(eLogWarning, "UDPServer: Session with from ", remotePort, " and to ", localPort, " ports already exists. But from different address. Removed"); + LogPrint(eLogWarning, "UDPServer: Session with from ", remotePort, " and to ", localPort, " ports already exists. But from differend address. Removed"); m_Sessions.erase (it); } } @@ -203,7 +203,7 @@ namespace client std::vector > sessions; std::lock_guard lock (m_SessionsMutex); - for (const auto &it: m_Sessions) + for (auto it: m_Sessions) { auto s = it.second; if (!s->m_Destination) continue; diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index fb03d434..de319f5d 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -49,6 +49,10 @@ set(test-gost-sig_SRCS test-gost-sig.cpp ) +set(test-x25519_SRCS + test-x25519.cpp +) + set(test-aeadchacha20poly1305_SRCS test-aeadchacha20poly1305.cpp ) @@ -65,10 +69,6 @@ set(test-eddsa_SRCS test-eddsa.cpp ) -set(test-aes_SRCS - test-aes.cpp -) - add_executable(test-http-merge_chunked ${test-http-merge_chunked_SRCS}) add_executable(test-http-req ${test-http-req_SRCS}) add_executable(test-http-res ${test-http-res_SRCS}) @@ -77,11 +77,11 @@ add_executable(test-http-url ${test-http-url_SRCS}) add_executable(test-base-64 ${test-base-64_SRCS}) add_executable(test-gost ${test-gost_SRCS}) add_executable(test-gost-sig ${test-gost-sig_SRCS}) +add_executable(test-x25519 ${test-x25519_SRCS}) add_executable(test-aeadchacha20poly1305 ${test-aeadchacha20poly1305_SRCS}) add_executable(test-blinding ${test-blinding_SRCS}) add_executable(test-elligator ${test-elligator_SRCS}) add_executable(test-eddsa ${test-eddsa_SRCS}) -add_executable(test-aes ${test-aes_SRCS}) set(LIBS libi2pd @@ -102,11 +102,11 @@ target_link_libraries(test-http-url ${LIBS}) target_link_libraries(test-base-64 ${LIBS}) target_link_libraries(test-gost ${LIBS}) target_link_libraries(test-gost-sig ${LIBS}) +target_link_libraries(test-x25519 ${LIBS}) target_link_libraries(test-aeadchacha20poly1305 ${LIBS}) target_link_libraries(test-blinding ${LIBS}) target_link_libraries(test-elligator ${LIBS}) target_link_libraries(test-eddsa ${LIBS}) -target_link_libraries(test-aes ${LIBS}) add_test(test-http-merge_chunked ${TEST_PATH}/test-http-merge_chunked) add_test(test-http-req ${TEST_PATH}/test-http-req) @@ -116,8 +116,8 @@ add_test(test-http-url ${TEST_PATH}/test-http-url) add_test(test-base-64 ${TEST_PATH}/test-base-64) add_test(test-gost ${TEST_PATH}/test-gost) add_test(test-gost-sig ${TEST_PATH}/test-gost-sig) +add_test(test-x25519 ${TEST_PATH}/test-x25519) add_test(test-aeadchacha20poly1305 ${TEST_PATH}/test-aeadchacha20poly1305) add_test(test-blinding ${TEST_PATH}/test-blinding) add_test(test-elligator ${TEST_PATH}/test-elligator) add_test(test-eddsa ${TEST_PATH}/test-eddsa) -add_test(test-aes ${TEST_PATH}/test-aes) diff --git a/tests/Makefile b/tests/Makefile index b020427d..798fab42 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -7,8 +7,8 @@ LIBI2PD = ../libi2pd.a TESTS = \ test-http-merge_chunked test-http-req test-http-res test-http-url test-http-url_decode \ - test-gost test-gost-sig test-base-64 test-aeadchacha20poly1305 test-blinding \ - test-elligator test-eddsa test-aes + 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 @@ -44,6 +44,9 @@ test-gost: test-gost.cpp $(LIBI2PD) test-gost-sig: test-gost-sig.cpp $(LIBI2PD) $(CXX) $(CXXFLAGS) $(NEEDED_CXXFLAGS) $(INCFLAGS) $(LDFLAGS) -o $@ $^ $(LDLIBS) +test-x25519: test-x25519.cpp $(LIBI2PD) + $(CXX) $(CXXFLAGS) $(NEEDED_CXXFLAGS) $(INCFLAGS) $(LDFLAGS) -o $@ $^ $(LDLIBS) + test-aeadchacha20poly1305: test-aeadchacha20poly1305.cpp $(LIBI2PD) $(CXX) $(CXXFLAGS) $(NEEDED_CXXFLAGS) $(INCFLAGS) $(LDFLAGS) -o $@ $^ $(LDLIBS) @@ -56,9 +59,6 @@ test-elligator: test-elligator.cpp $(LIBI2PD) test-eddsa: test-eddsa.cpp $(LIBI2PD) $(CXX) $(CXXFLAGS) $(NEEDED_CXXFLAGS) $(INCFLAGS) $(LDFLAGS) -o $@ $^ $(LDLIBS) -test-aes: test-aes.cpp $(LIBI2PD) - $(CXX) $(CXXFLAGS) $(NEEDED_CXXFLAGS) $(INCFLAGS) $(LDFLAGS) -o $@ $^ $(LDLIBS) - run: $(TESTS) @for TEST in $(TESTS); do echo Running $$TEST; ./$$TEST ; done diff --git a/tests/test-aeadchacha20poly1305.cpp b/tests/test-aeadchacha20poly1305.cpp index 2ba6a253..64a0f358 100644 --- a/tests/test-aeadchacha20poly1305.cpp +++ b/tests/test-aeadchacha20poly1305.cpp @@ -43,20 +43,18 @@ uint8_t encrypted[114] = int main () { uint8_t buf[114+16]; - i2p::crypto::AEADChaCha20Poly1305Encryptor encryptor; // test encryption - encryptor.Encrypt ((uint8_t *)text, 114, ad, 12, key, nonce, buf, 114 + 16); + i2p::crypto::AEADChaCha20Poly1305 ((uint8_t *)text, 114, ad, 12, key, nonce, buf, 114 + 16, true); assert (memcmp (buf, encrypted, 114) == 0); assert (memcmp (buf + 114, tag, 16) == 0); // test decryption uint8_t buf1[114]; - i2p::crypto::AEADChaCha20Poly1305Decryptor decryptor; - assert (decryptor.Decrypt (buf, 114, ad, 12, key, nonce, buf1, 114)); + assert (i2p::crypto::AEADChaCha20Poly1305 (buf, 114, ad, 12, key, nonce, buf1, 114, false)); assert (memcmp (buf1, text, 114) == 0); // test encryption of multiple buffers memcpy (buf, text, 114); std::vector > bufs{ std::make_pair (buf, 20), std::make_pair (buf + 20, 10), std::make_pair (buf + 30, 70), std::make_pair (buf + 100, 14) }; - encryptor.Encrypt (bufs, key, nonce, buf + 114); - decryptor.Decrypt (buf, 114, nullptr, 0, key, nonce, buf1, 114); + i2p::crypto::AEADChaCha20Poly1305Encrypt (bufs, key, nonce, buf + 114); + i2p::crypto::AEADChaCha20Poly1305 (buf, 114, nullptr, 0, key, nonce, buf1, 114, false); assert (memcmp (buf1, text, 114) == 0); } diff --git a/tests/test-aes.cpp b/tests/test-aes.cpp deleted file mode 100644 index 15f4de1e..00000000 --- a/tests/test-aes.cpp +++ /dev/null @@ -1,69 +0,0 @@ -#include -#include -#include - -#include "Crypto.h" - -uint8_t ecb_key1[32] = -{ - 0x60, 0x3d, 0xeb, 0x10, 0x15, 0xca, 0x71, 0xbe, 0x2b, 0x73, 0xae, 0xf0, 0x85, 0x7d, 0x77, 0x81, - 0x1f, 0x35, 0x2c, 0x07, 0x3b, 0x61, 0x08, 0xd7, 0x2d, 0x98, 0x10, 0xa3, 0x09, 0x14, 0xdf, 0xf4 -}; - -uint8_t ecb_plain1[16] = -{ - 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a -}; - -uint8_t ecb_cipher1[16] = -{ - 0xf3, 0xee, 0xd1, 0xbd, 0xb5, 0xd2, 0xa0, 0x3c, 0x06, 0x4b, 0x5a, 0x7e, 0x3d, 0xb1, 0x81, 0xf8 -}; - -uint8_t cbc_key1[32] = -{ - 0x60, 0x3d, 0xeb, 0x10, 0x15, 0xca, 0x71, 0xbe, 0x2b, 0x73, 0xae, 0xf0, 0x85, 0x7d, 0x77, 0x81, - 0x1f, 0x35, 0x2c, 0x07, 0x3b, 0x61, 0x08, 0xd7, 0x2d, 0x98, 0x10, 0xa3, 0x09, 0x14, 0xdf, 0xf4 -}; - -uint8_t cbc_iv1[16] = -{ - 0xF5, 0x8C, 0x4C, 0x04, 0xD6, 0xE5, 0xF1, 0xBA, 0x77, 0x9E, 0xAB, 0xFB, 0x5F, 0x7B, 0xFB, 0xD6 -}; - -uint8_t cbc_plain1[16] = -{ - 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51 -}; - -uint8_t cbc_cipher1[16] = -{ - 0x9c, 0xfc, 0x4e, 0x96, 0x7e, 0xdb, 0x80, 0x8d, 0x67, 0x9f, 0x77, 0x7b, 0xc6, 0x70, 0x2c, 0x7d -}; - -int main () -{ - // ECB encrypt test1 - i2p::crypto::ECBEncryption ecbencryption; - ecbencryption.SetKey (ecb_key1); - uint8_t out[16]; - ecbencryption.Encrypt (ecb_plain1, out); - assert (memcmp (ecb_cipher1, out, 16) == 0); - - // ECB decrypt test1 - i2p::crypto::ECBDecryption ecbdecryption; - ecbdecryption.SetKey (ecb_key1); - ecbdecryption.Decrypt (ecb_cipher1, out); - assert (memcmp (ecb_plain1, out, 16) == 0); - // CBC encrypt test - i2p::crypto::CBCEncryption cbcencryption; - cbcencryption.SetKey (cbc_key1); - cbcencryption.Encrypt (cbc_plain1, 16, cbc_iv1, out); - assert (memcmp (cbc_cipher1, out, 16) == 0); - // CBC decrypt test - i2p::crypto::CBCDecryption cbcdecryption; - cbcdecryption.SetKey (cbc_key1); - cbcdecryption.Decrypt (cbc_cipher1, 16, cbc_iv1, out); - assert (memcmp (cbc_plain1, out, 16) == 0); -} - diff --git a/tests/test-x25519.cpp b/tests/test-x25519.cpp new file mode 100644 index 00000000..a1f3f424 --- /dev/null +++ b/tests/test-x25519.cpp @@ -0,0 +1,38 @@ +#include +#include +#include + +#include "Ed25519.h" + +const uint8_t k[32] = +{ + 0xa5, 0x46, 0xe3, 0x6b, 0xf0, 0x52, 0x7c, 0x9d, 0x3b, 0x16, 0x15, + 0x4b, 0x82, 0x46, 0x5e, 0xdd, 0x62, 0x14, 0x4c, 0x0a, 0xc1, 0xfc, + 0x5a, 0x18, 0x50, 0x6a, 0x22, 0x44, 0xba, 0x44, 0x9a, 0xc4 +}; + +const uint8_t u[32] = +{ + 0xe6, 0xdb, 0x68, 0x67, 0x58, 0x30, 0x30, 0xdb, 0x35, 0x94, 0xc1, + 0xa4, 0x24, 0xb1, 0x5f, 0x7c, 0x72, 0x66, 0x24, 0xec, 0x26, 0xb3, + 0x35, 0x3b, 0x10, 0xa9, 0x03, 0xa6, 0xd0, 0xab, 0x1c, 0x4c +}; + +uint8_t p[32] = +{ + 0xc3, 0xda, 0x55, 0x37, 0x9d, 0xe9, 0xc6, 0x90, 0x8e, 0x94, 0xea, + 0x4d, 0xf2, 0x8d, 0x08, 0x4f, 0x32, 0xec, 0xcf, 0x03, 0x49, 0x1c, + 0x71, 0xf7, 0x54, 0xb4, 0x07, 0x55, 0x77, 0xa2, 0x85, 0x52 +}; + +int main () +{ +#if !OPENSSL_X25519 +// we test it for openssl < 1.1.0 + uint8_t buf[32]; + BN_CTX * ctx = BN_CTX_new (); + i2p::crypto::GetEd25519 ()->ScalarMul (u, k, buf, ctx); + BN_CTX_free (ctx); + assert(memcmp (buf, p, 32) == 0); +#endif +}