mirror of
https://github.com/PurpleI2P/i2pd.git
synced 2025-11-13 21:10:12 +00:00
Compare commits
No commits in common. "openssl" and "2.53.1" have entirely different histories.
187 changed files with 6587 additions and 13025 deletions
2
.github/workflows/build-deb.yml
vendored
2
.github/workflows/build-deb.yml
vendored
|
|
@ -28,7 +28,7 @@ jobs:
|
|||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
dist: ['bullseye', 'bookworm', 'trixie']
|
||||
dist: ['buster', 'bullseye', 'bookworm']
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ on:
|
|||
branches:
|
||||
- '*'
|
||||
paths:
|
||||
- .github/workflows/build-macos.yml
|
||||
- .github/workflows/build-osx.yml
|
||||
- daemon/**
|
||||
- i18n/**
|
||||
- libi2pd/**
|
||||
|
|
@ -20,17 +20,13 @@ on:
|
|||
|
||||
jobs:
|
||||
build:
|
||||
name: Build on ${{ matrix.target-name }}
|
||||
runs-on: ${{ matrix.target }}
|
||||
name: With USE_UPNP=${{ matrix.with_upnp }}
|
||||
runs-on: macOS-latest
|
||||
|
||||
strategy:
|
||||
fail-fast: true
|
||||
matrix:
|
||||
include:
|
||||
- target: macos-15
|
||||
target-name: ARM64
|
||||
- target: macos-15-intel
|
||||
target-name: Intel x86_64
|
||||
with_upnp: ['yes', 'no']
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
|
|
@ -40,14 +36,10 @@ jobs:
|
|||
run: |
|
||||
find /usr/local/bin -lname '*/Library/Frameworks/Python.framework/*' -delete
|
||||
brew update
|
||||
brew install boost miniupnpc openssl@3.5
|
||||
brew install boost miniupnpc openssl@1.1
|
||||
|
||||
- name: List installed formulae
|
||||
run: brew list
|
||||
|
||||
- name: Build application
|
||||
run: make HOMEBREW=1 USE_UPNP=yes USE_STATIC=yes PREFIX=$GITHUB_WORKSPACE/output -j3
|
||||
|
||||
- name: Print binary linking
|
||||
run: otool -L i2pd
|
||||
|
||||
run: make HOMEBREW=1 USE_UPNP=${{ matrix.with_upnp }} PREFIX=$GITHUB_WORKSPACE/output -j3
|
||||
31
.github/workflows/build-windows.yml
vendored
31
.github/workflows/build-windows.yml
vendored
|
|
@ -108,7 +108,6 @@ jobs:
|
|||
path: build/i2pd.exe
|
||||
|
||||
build-xp:
|
||||
if: false
|
||||
name: XP
|
||||
runs-on: windows-latest
|
||||
|
||||
|
|
@ -129,13 +128,8 @@ jobs:
|
|||
cache: true
|
||||
update: true
|
||||
|
||||
- name: Clone MinGW packages repository and revert boost to 1.85.0
|
||||
run: |
|
||||
git clone https://github.com/msys2/MINGW-packages
|
||||
cd MINGW-packages
|
||||
git checkout 4cbb366edf2f268ac3146174b40ce38604646fc5 mingw-w64-boost
|
||||
cd mingw-w64-boost
|
||||
sed -i 's/boostorg.jfrog.io\/artifactory\/main/archives.boost.io/' PKGBUILD
|
||||
- name: Clone MinGW packages repository
|
||||
run: git clone https://github.com/msys2/MINGW-packages
|
||||
|
||||
# headers
|
||||
- name: Get headers package version
|
||||
|
|
@ -185,15 +179,15 @@ jobs:
|
|||
uses: actions/cache@v4
|
||||
id: cache-winpthreads
|
||||
with:
|
||||
path: MINGW-packages/mingw-w64-winpthreads/*.zst
|
||||
path: MINGW-packages/mingw-w64-winpthreads-git/*.zst
|
||||
key: winxp-winpthreads-${{ steps.version-winpthreads.outputs.version }}
|
||||
- name: Build WinXP-capable winpthreads package
|
||||
if: steps.cache-winpthreads.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
cd MINGW-packages/mingw-w64-winpthreads
|
||||
cd MINGW-packages/mingw-w64-winpthreads-git
|
||||
MINGW_ARCH=mingw32 makepkg-mingw -sCLf --noconfirm --nocheck
|
||||
- name: Install winpthreads package
|
||||
run: pacman --noconfirm -U MINGW-packages/mingw-w64-winpthreads/mingw-w64-i686-*-any.pkg.tar.zst
|
||||
run: pacman --noconfirm -U MINGW-packages/mingw-w64-winpthreads-git/mingw-w64-i686-*-any.pkg.tar.zst
|
||||
|
||||
# OpenSSL
|
||||
- name: Get openssl package version
|
||||
|
|
@ -210,29 +204,27 @@ jobs:
|
|||
if: steps.cache-openssl.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
cd MINGW-packages/mingw-w64-openssl
|
||||
curl https://openssl-library.org/source/pubkeys.asc | gpg --import
|
||||
gpg --recv-keys D894E2CE8B3D79F5
|
||||
MINGW_ARCH=mingw32 makepkg-mingw -sCLf --noconfirm --nocheck
|
||||
- name: Install openssl package
|
||||
run: pacman --noconfirm -U MINGW-packages/mingw-w64-openssl/mingw-w64-i686-*-any.pkg.tar.zst
|
||||
|
||||
# Boost
|
||||
#- name: Get boost package version
|
||||
# id: version-boost
|
||||
# run: |
|
||||
# echo "version=$(pacman -Si mingw-w64-i686-boost | grep -Po '^Version\s*: \K.+')" >> $GITHUB_OUTPUT
|
||||
- name: Get boost package version
|
||||
id: version-boost
|
||||
run: |
|
||||
echo "version=$(pacman -Si mingw-w64-i686-boost | grep -Po '^Version\s*: \K.+')" >> $GITHUB_OUTPUT
|
||||
- name: Cache boost package
|
||||
uses: actions/cache@v4
|
||||
id: cache-boost
|
||||
with:
|
||||
path: MINGW-packages/mingw-w64-boost/*.zst
|
||||
key: winxp-boost-1.85.0+crt-${{ steps.version-headers.outputs.version }}+ossl-${{ steps.version-openssl.outputs.version }}
|
||||
key: winxp-winpthreads-${{ steps.version-boost.outputs.version }}
|
||||
- name: Build WinXP-capable boost package
|
||||
if: steps.cache-boost.outputs.cache-hit != 'true'
|
||||
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
|
||||
|
||||
|
|
@ -241,6 +233,7 @@ jobs:
|
|||
run: |
|
||||
mkdir -p obj/Win32 obj/libi2pd obj/libi2pd_client obj/daemon
|
||||
make USE_UPNP=yes DEBUG=no USE_GIT_VERSION=yes USE_WINXP_FLAGS=yes -j3
|
||||
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
|
|
|
|||
24
.github/workflows/docker.yml
vendored
24
.github/workflows/docker.yml
vendored
|
|
@ -105,18 +105,18 @@ jobs:
|
|||
|
||||
- name: Create and push latest manifest image to Docker Hub
|
||||
if: ${{ !startsWith(github.ref, 'refs/tags/') }}
|
||||
uses: Noelware/docker-manifest-action@v1
|
||||
uses: Noelware/docker-manifest-action@master
|
||||
with:
|
||||
tags: purplei2p/i2pd:latest
|
||||
inputs: purplei2p/i2pd:latest-amd64,purplei2p/i2pd:latest-i386,purplei2p/i2pd:latest-arm64,purplei2p/i2pd:latest-armv7
|
||||
inputs: purplei2p/i2pd:latest
|
||||
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
|
||||
if: ${{ !startsWith(github.ref, 'refs/tags/') }}
|
||||
uses: Noelware/docker-manifest-action@v1
|
||||
uses: Noelware/docker-manifest-action@master
|
||||
with:
|
||||
tags: ghcr.io/purplei2p/i2pd:latest
|
||||
inputs: ghcr.io/purplei2p/i2pd:latest-amd64,ghcr.io/purplei2p/i2pd:latest-i386,ghcr.io/purplei2p/i2pd:latest-arm64,ghcr.io/purplei2p/i2pd:latest-armv7
|
||||
inputs: ghcr.io/purplei2p/i2pd:latest
|
||||
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
|
||||
|
|
@ -125,16 +125,16 @@ jobs:
|
|||
|
||||
- name: Create and push release manifest to Docker Hub
|
||||
if: ${{ startsWith(github.ref, 'refs/tags/') }}
|
||||
uses: Noelware/docker-manifest-action@v1
|
||||
uses: Noelware/docker-manifest-action@master
|
||||
with:
|
||||
tags: purplei2p/i2pd:latest,purplei2p/i2pd:latest-release,purplei2p/i2pd:release-${{ env.RELEASE_VERSION }}
|
||||
inputs: purplei2p/i2pd:latest-amd64,purplei2p/i2pd:latest-i386,purplei2p/i2pd:latest-arm64,purplei2p/i2pd:latest-armv7
|
||||
inputs: purplei2p/i2pd:latest,purplei2p/i2pd:latest-release,purplei2p/i2pd:release-${{ env.RELEASE_VERSION }}
|
||||
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
|
||||
if: ${{ startsWith(github.ref, 'refs/tags/') }}
|
||||
uses: Noelware/docker-manifest-action@v1
|
||||
uses: Noelware/docker-manifest-action@master
|
||||
with:
|
||||
tags: ghcr.io/purplei2p/i2pd:latest,ghcr.io/purplei2p/i2pd:latest-release,ghcr.io/purplei2p/i2pd:release-${{ env.RELEASE_VERSION }}
|
||||
inputs: ghcr.io/purplei2p/i2pd:latest-amd64,ghcr.io/purplei2p/i2pd:latest-i386,ghcr.io/purplei2p/i2pd:latest-arm64,ghcr.io/purplei2p/i2pd:latest-armv7
|
||||
inputs: ghcr.io/purplei2p/i2pd:latest,ghcr.io/purplei2p/i2pd:latest-release,ghcr.io/purplei2p/i2pd:release-${{ env.RELEASE_VERSION }}
|
||||
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
|
||||
|
|
|
|||
161
ChangeLog
161
ChangeLog
|
|
@ -1,167 +1,6 @@
|
|||
# for this file format description,
|
||||
# see https://github.com/olivierlacan/keep-a-changelog
|
||||
|
||||
## [2.58.0] - 2025-09-08
|
||||
### Added
|
||||
- Post-quantum end-to-end crypto(ML-KEM-512, ML-KEM-768, ML-KEM-1024) support if openssl >= 3.5.0
|
||||
- Datagram2 and Datagram3
|
||||
- SAM PING command
|
||||
- Support boost 1.89
|
||||
- Specify light or dark theme for webconsole
|
||||
- "ssu2.firewalled4" and "ssu2.firewalled6" params to force Firewalled even if network is OK
|
||||
- Streaming ping through BOB
|
||||
- Ability to specify bandwidth with "mb" and "gb" suffixes
|
||||
- "i2p.streaming.maxWindowSize" and "i2p.streaming.dontSign" params
|
||||
### Changed
|
||||
- Don't verify streaming SYN packet signature if comes from an ECIESx25519 session
|
||||
- Non-blocking mode for UDP sockets in UDP tunnels
|
||||
- Accept "HELLO VERSION" without "MIN" and "MAX" in SAM
|
||||
- Try to resolve host again in server tunnel if failed before
|
||||
- Don't call deprecated functions for openssl 3
|
||||
- Enable post-quantum crypto by default if supported
|
||||
- Create unique loopback address from fd00::/8 range for ::1 if explicitly set in server tunnels
|
||||
- Limit number of outbound streaming packets if the peer can't handle them
|
||||
- Don't show Network status if ipv6 only
|
||||
- Reseeds list
|
||||
### Fixed
|
||||
- Version in I2CP SetDate message
|
||||
- Crash when SAM session is getting closed
|
||||
- Max UDP buffer size for OpenBSD
|
||||
- Lack of file descriptors for Haiku
|
||||
- Outgoing stream constantly re-requests LeaseSet if the remote peer has gone away
|
||||
- Numeric value for "i2p.streaming.answerPings" param
|
||||
- 'R' and 'U' router caps together if operating through a proxy
|
||||
|
||||
## [2.57.0] - 2025-06-02
|
||||
### Added
|
||||
- Local domain sockets for I2PControl
|
||||
- "keys=shareddest" tunnel param to run on shared local destination
|
||||
- HTTP and SOCKS proxy through BOB
|
||||
- Localization to Hebrew and Hindi
|
||||
- NTCP2 probing resistance
|
||||
- Support SAM v1 datagram sessions without port
|
||||
- OpenIndiana support
|
||||
### Changed
|
||||
- Don't request LeaseSet until I2CP destination is ready
|
||||
- Keep receiving new data from I2PTunnel/SAM socket while previous is being sent to stream
|
||||
- Insert phony record to inbound tunnel build message with real x25519 ephemeral key
|
||||
- Set min peer test version to 0.9.62
|
||||
- Increase I2NP message expiration timeout in SSU2
|
||||
- Cleanup ECIESx25519 new session reply keys on Alice side
|
||||
- Reduced router profile persist interval to 22 minutes
|
||||
- SSU2 max padding size to 32 bytes
|
||||
- Send SSU2 path challenge of 8 bytes. Add datetime and address blocks
|
||||
- Don't delete trusted routers from netdb
|
||||
- Disable loss-control in streaming
|
||||
- Reseeds list
|
||||
### Fixed
|
||||
- Crash after SAM stream disconnect
|
||||
- FORWARD session host handling in SAM
|
||||
- x86 build for Haiku
|
||||
- SSU2 session's remote endpoint after receiving path response
|
||||
|
||||
## [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
|
||||
- Limited connectivity mode(through proxy)
|
||||
- "i2p.streaming.profile" tunnel's param to let tunnel select also low-bandwidth routers
|
||||
- Limit stream's inbound speed
|
||||
- Periodic ack requests in ratchets session
|
||||
- Set congestion cap G immediately if through proxy
|
||||
- Show tunnel's routers bandwidth caps in web console
|
||||
- Handle immediate ack requested flag in SSU2 data packets
|
||||
- Resend and ack peer test and relay messages
|
||||
- "senduseragent" HTTP proxy's param to pass through user's User-Agent
|
||||
### Changed
|
||||
- Exclude 'N' routers from high-bandwidth routers for client tunnels
|
||||
- C++11 support has been dropped, the minimal requirement is C++17 now, C++20 for some compilers
|
||||
- Removed dependency from boost::date_time and boost::filesystem
|
||||
- Set default i2cp.leaseSetEncType to 0,4 and to 4 for server tunnels
|
||||
- Handle i2cp.inboundlimit and i2cp.outboundlimit params in I2CP
|
||||
- Publish LeaseSet with new timestamp update if tunnel was replaced in the same second
|
||||
- Increase max number of generated tags to 800 per tagset
|
||||
- Routing path expiration by time instead num attempts
|
||||
- Save timestamp from epoch instead local time to profiles
|
||||
- Update introducer's iTag if session to introducer was replaced to new one
|
||||
- RTT, window size and number of NACKs calculation for streaming
|
||||
- Don't select same peer for tunnel too often
|
||||
- Use WinApi for data path UTF-8 conversion for Windows
|
||||
### Fixed
|
||||
- Jump link crash if address book is disabled
|
||||
- Race condition if connect through an introducer
|
||||
- "Date" header in I2PControl response
|
||||
- Incomplete response from web console
|
||||
- AEAD verification with LibreSSL
|
||||
- Number of generated tags and new keys for follow-on tagsets
|
||||
- Expired leases in LeaseSet
|
||||
- Attempts to send HolePunch to 0.0.0.0
|
||||
- Incorrect options size in quick ack streaming packet
|
||||
- Low bandwidth router appeared as first peer in high-bandwidth client tunnel
|
||||
|
||||
## [2.53.1] - 2024-07-29
|
||||
### Changed
|
||||
- I2CP performance improvement
|
||||
|
|
|
|||
2
LICENSE
2
LICENSE
|
|
@ -1,4 +1,4 @@
|
|||
Copyright (c) 2013-2025, The PurpleI2P Project
|
||||
Copyright (c) 2013-2023, The PurpleI2P Project
|
||||
|
||||
All rights reserved.
|
||||
|
||||
|
|
|
|||
8
Makefile
8
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)
|
||||
|
|
@ -51,6 +52,7 @@ ifneq (, $(DESTDIR))
|
|||
endif
|
||||
|
||||
ifneq (, $(findstring darwin, $(SYS)))
|
||||
DAEMON_SRC += $(DAEMON_SRC_DIR)/UnixDaemon.cpp
|
||||
ifeq ($(HOMEBREW),1)
|
||||
include Makefile.homebrew
|
||||
else
|
||||
|
|
@ -60,18 +62,20 @@ else ifneq (, $(findstring mingw, $(SYS))$(findstring windows-gnu, $(SYS))$(find
|
|||
DAEMON_SRC += Win32/DaemonWin32.cpp Win32/Win32App.cpp Win32/Win32Service.cpp Win32/Win32NetState.cpp
|
||||
include Makefile.mingw
|
||||
else ifneq (, $(findstring linux, $(SYS))$(findstring gnu, $(SYS)))
|
||||
DAEMON_SRC += $(DAEMON_SRC_DIR)/UnixDaemon.cpp
|
||||
include Makefile.linux
|
||||
else ifneq (, $(findstring freebsd, $(SYS))$(findstring openbsd, $(SYS)))
|
||||
DAEMON_SRC += $(DAEMON_SRC_DIR)/UnixDaemon.cpp
|
||||
include Makefile.bsd
|
||||
else ifneq (, $(findstring haiku, $(SYS)))
|
||||
DAEMON_SRC += $(DAEMON_SRC_DIR)/UnixDaemon.cpp
|
||||
include Makefile.haiku
|
||||
else ifneq (, $(findstring solaris, $(SYS)))
|
||||
include Makefile.solaris
|
||||
else # not supported
|
||||
$(error Not supported platform)
|
||||
endif
|
||||
|
||||
INCFLAGS += -I$(LIB_SRC_DIR) -I$(LIB_CLIENT_SRC_DIR) -I$(LANG_SRC_DIR)
|
||||
DEFINES += -DOPENSSL_SUPPRESS_DEPRECATED
|
||||
NEEDED_CXXFLAGS += -MMD -MP
|
||||
|
||||
ifeq ($(USE_GIT_VERSION),yes)
|
||||
|
|
|
|||
20
Makefile.bsd
20
Makefile.bsd
|
|
@ -1,22 +1,18 @@
|
|||
CXX = clang++
|
||||
CXXFLAGS ?= ${CXX_DEBUG} -Wall -Wextra -Wno-unused-parameter -pedantic -Wno-misleading-indentation
|
||||
DEFINES = -D_GLIBCXX_USE_NANOSLEEP=1
|
||||
INCFLAGS = -I/usr/include/ -I/usr/local/include/
|
||||
LDFLAGS = ${LD_DEBUG} -Wl,-rpath,/usr/local/lib -L/usr/local/lib
|
||||
LDLIBS = -lssl -lcrypto -lz -lpthread -lboost_program_options
|
||||
|
||||
## NOTE: NEEDED_CXXFLAGS is here so that custom CXXFLAGS can be specified at build time
|
||||
## **without** overwriting the CXXFLAGS which we need in order to build.
|
||||
## For example, when adding 'hardening flags' to the build
|
||||
## (e.g. -fstack-protector-strong -Wformat -Werror=format-security), we do not want to remove
|
||||
## -std=c++11. If you want to remove this variable please do so in a way that allows setting
|
||||
## custom FLAGS to work at build-time.
|
||||
CXXVER := $(shell $(CXX) -dumpversion|cut -c 1-2)
|
||||
ifeq (${CXXVER}, "4.") # older clang always returned 4.2.1
|
||||
$(error Compiler too old)
|
||||
else ifeq (${CXXVER}, ${filter ${CXXVER},16 17 18 19}) # clang 16 - 19
|
||||
NEEDED_CXXFLAGS = -std=c++20
|
||||
else
|
||||
CXXVER := $(shell $(CXX) -dumpversion)
|
||||
ifeq (${CXXVER}, "4.2.1") # older clang always returned 4.2.1
|
||||
NEEDED_CXXFLAGS = -std=c++11
|
||||
else # newer versions support C++17
|
||||
NEEDED_CXXFLAGS = -std=c++17
|
||||
endif
|
||||
|
||||
DEFINES = -D_GLIBCXX_USE_NANOSLEEP=1
|
||||
INCFLAGS = -I/usr/include/ -I/usr/local/include/
|
||||
LDFLAGS = ${LD_DEBUG} -Wl,-rpath,/usr/local/lib -L/usr/local/lib
|
||||
LDLIBS = -lcrypto -lssl -lz -lboost_system -lboost_date_time -lboost_filesystem -lboost_program_options -lpthread
|
||||
|
|
|
|||
|
|
@ -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++11
|
||||
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_date_time -lboost_filesystem -lboost_program_options -lpthread
|
||||
|
||||
ifeq ($(USE_UPNP),yes)
|
||||
DEFINES += -DUSE_UPNP
|
||||
|
|
|
|||
|
|
@ -1,35 +1,24 @@
|
|||
# root directory holding homebrew
|
||||
# brew install boost openssl@3.5 cmake make
|
||||
|
||||
ifeq ($(shell uname -m), x86_64)
|
||||
BREWROOT = /usr/local
|
||||
else
|
||||
BREWROOT = /opt/homebrew
|
||||
endif
|
||||
|
||||
BREWROOT = /opt/homebrew
|
||||
BOOSTROOT = ${BREWROOT}/opt/boost
|
||||
SSLROOT = ${BREWROOT}/opt/openssl@3.5
|
||||
SSLROOT = ${BREWROOT}/opt/openssl@1.1
|
||||
UPNPROOT = ${BREWROOT}/opt/miniupnpc
|
||||
|
||||
CXXFLAGS ?= ${CXX_DEBUG} -Wall -Wno-overloaded-virtual
|
||||
NEEDED_CXXFLAGS += -std=c++20
|
||||
NEEDED_CXXFLAGS ?= -std=c++11
|
||||
INCFLAGS ?= -I${SSLROOT}/include -I${BOOSTROOT}/include
|
||||
LDFLAGS ?= ${LD_DEBUG}
|
||||
DEFINES += -DMAC_OSX
|
||||
|
||||
ifeq ($(shell uname -m), x86_64)
|
||||
CXXFLAGS += -msse
|
||||
endif
|
||||
|
||||
ifeq ($(USE_STATIC),yes)
|
||||
LDLIBS = -lz ${SSLROOT}/lib/libcrypto.a ${SSLROOT}/lib/libssl.a ${BOOSTROOT}/lib/libboost_program_options.a
|
||||
LDLIBS = -lz ${SSLROOT}/lib/libcrypto.a ${SSLROOT}/lib/libssl.a ${BOOSTROOT}/lib/libboost_system.a ${BOOSTROOT}/lib/libboost_date_time.a ${BOOSTROOT}/lib/libboost_filesystem.a ${BOOSTROOT}/lib/libboost_program_options.a
|
||||
ifeq ($(USE_UPNP),yes)
|
||||
LDLIBS += ${UPNPROOT}/lib/libminiupnpc.a
|
||||
endif
|
||||
LDLIBS += -lpthread -ldl
|
||||
else
|
||||
LDFLAGS += -L${SSLROOT}/lib -L${BOOSTROOT}/lib
|
||||
LDLIBS = -lz -lssl -lcrypto -lboost_program_options -lpthread
|
||||
LDLIBS = -lz -lcrypto -lssl -lboost_system -lboost_date_time -lboost_filesystem -lboost_program_options -lpthread
|
||||
ifeq ($(USE_UPNP),yes)
|
||||
LDFLAGS += -L${UPNPROOT}/lib
|
||||
LDLIBS += -lminiupnpc
|
||||
|
|
@ -41,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
|
||||
|
|
|
|||
|
|
@ -9,17 +9,24 @@ LDFLAGS ?= ${LD_DEBUG}
|
|||
## -std=c++11. If you want to remove this variable please do so in a way that allows setting
|
||||
## custom FDLAGS to work at build-time.
|
||||
|
||||
# detect proper flag for c++17 support by compilers
|
||||
# detect proper flag for c++11 support by compilers
|
||||
CXXVER := $(shell $(CXX) -dumpversion)
|
||||
ifeq ($(shell expr match $(CXX) 'clang'),5)
|
||||
NEEDED_CXXFLAGS += -std=c++11
|
||||
else ifeq ($(shell expr match ${CXXVER} "4\.[0-9][0-9]"),4) # gcc >= 4.10
|
||||
NEEDED_CXXFLAGS += -std=c++11
|
||||
else ifeq ($(shell expr match ${CXXVER} "4\.[8-9]"),3) # gcc 4.8 - 4.9
|
||||
NEEDED_CXXFLAGS += -std=c++11 -D_GLIBCXX_USE_NANOSLEEP=1
|
||||
else ifeq ($(shell expr match ${CXXVER} "[5-6]"),1) # gcc 5 - 6
|
||||
NEEDED_CXXFLAGS += -std=c++11
|
||||
LDLIBS = -latomic
|
||||
else ifeq ($(shell expr match ${CXXVER} "[7-9]"),1) # gcc 7 - 9
|
||||
NEEDED_CXXFLAGS += -std=c++17
|
||||
else ifeq ($(shell expr match ${CXXVER} "[8-9]"),1) # gcc 8 - 9
|
||||
LDLIBS = -latomic
|
||||
else ifeq ($(shell expr match ${CXXVER} "1[0-9]"),2) # gcc 10+
|
||||
# NEEDED_CXXFLAGS += -std=c++20
|
||||
NEEDED_CXXFLAGS += -std=c++17
|
||||
LDLIBS = -lboost_system -lstdc++fs
|
||||
else ifeq ($(shell expr match ${CXXVER} "1[0-2]"),2) # gcc 10 - 12
|
||||
NEEDED_CXXFLAGS += -std=c++17
|
||||
else ifeq ($(shell expr match ${CXXVER} "1[3-9]"),2) # gcc 13+
|
||||
NEEDED_CXXFLAGS += -std=c++20
|
||||
LDLIBS = -latomic
|
||||
else # not supported
|
||||
$(error Compiler too old)
|
||||
endif
|
||||
|
|
@ -31,6 +38,9 @@ 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_date_time.a
|
||||
LDLIBS += $(LIBDIR)/libboost_filesystem.a
|
||||
LDLIBS += $(LIBDIR)/libboost_program_options.a
|
||||
LDLIBS += $(LIBDIR)/libssl.a
|
||||
LDLIBS += $(LIBDIR)/libcrypto.a
|
||||
|
|
@ -40,7 +50,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_date_time -lboost_filesystem -lboost_program_options -lpthread
|
||||
ifeq ($(USE_UPNP),yes)
|
||||
LDLIBS += -lminiupnpc
|
||||
endif
|
||||
|
|
@ -51,6 +61,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
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ CXXFLAGS := $(CXX_DEBUG) -fPIC -msse
|
|||
INCFLAGS := -I$(DAEMON_SRC_DIR) -IWin32
|
||||
LDFLAGS := ${LD_DEBUG} -static -fPIC -msse
|
||||
|
||||
NEEDED_CXXFLAGS += -std=c++20
|
||||
NEEDED_CXXFLAGS += -std=c++17
|
||||
DEFINES += -DWIN32_LEAN_AND_MEAN
|
||||
|
||||
# UPNP Support
|
||||
|
|
@ -16,11 +16,9 @@ ifeq ($(USE_UPNP),yes)
|
|||
LDLIBS = -lminiupnpc
|
||||
endif
|
||||
|
||||
ifeq ($(USE_WINXP_FLAGS), yes)
|
||||
DEFINES += -DWINVER=0x0501 -D_WIN32_WINNT=0x0501
|
||||
endif
|
||||
|
||||
LDLIBS += \
|
||||
$(MINGW_PREFIX)/lib/libboost_system-mt.a \
|
||||
$(MINGW_PREFIX)/lib/libboost_date_time-mt.a \
|
||||
$(MINGW_PREFIX)/lib/libboost_filesystem-mt.a \
|
||||
$(MINGW_PREFIX)/lib/libboost_program_options-mt.a \
|
||||
$(MINGW_PREFIX)/lib/libssl.a \
|
||||
|
|
@ -42,6 +40,16 @@ ifeq ($(USE_WIN32_APP), yes)
|
|||
DAEMON_OBJS += $(patsubst %.rc,obj/%.o,$(DAEMON_RC))
|
||||
endif
|
||||
|
||||
ifeq ($(USE_WINXP_FLAGS), yes)
|
||||
DEFINES += -DWINVER=0x0501 -D_WIN32_WINNT=0x0501
|
||||
endif
|
||||
|
||||
ifeq ($(USE_AESNI),yes)
|
||||
NEEDED_CXXFLAGS += -maes
|
||||
LDFLAGS += -maes
|
||||
DEFINES += -D__AES__
|
||||
endif
|
||||
|
||||
ifeq ($(USE_ASLR),yes)
|
||||
LDFLAGS += -Wl,--nxcompat -Wl,--high-entropy-va -Wl,--dynamicbase,--export-all-symbols
|
||||
endif
|
||||
|
|
|
|||
12
Makefile.osx
12
Makefile.osx
|
|
@ -1,5 +1,5 @@
|
|||
CXX = clang++
|
||||
CXXFLAGS := ${CXX_DEBUG} -Wall -std=c++17
|
||||
CXXFLAGS := ${CXX_DEBUG} -Wall -std=c++11
|
||||
INCFLAGS = -I/usr/local/include
|
||||
DEFINES := -DMAC_OSX
|
||||
LDFLAGS := -Wl,-rpath,/usr/local/lib -L/usr/local/lib
|
||||
|
|
@ -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_date_time.a /usr/local/lib/libboost_filesystem.a /usr/local/lib/libboost_program_options.a -lpthread
|
||||
else
|
||||
LDLIBS = -lz -lssl -lcrypto -lboost_program_options -lpthread
|
||||
LDLIBS = -lz -lcrypto -lssl -lboost_system -lboost_date_time -lboost_filesystem -lboost_program_options -lpthread
|
||||
endif
|
||||
|
||||
ifeq ($(USE_UPNP),yes)
|
||||
|
|
@ -24,6 +24,10 @@ endif
|
|||
|
||||
OSARCH = $(shell uname -p)
|
||||
|
||||
ifeq ($(shell uname -m), x86_64)
|
||||
ifneq ($(OSARCH),powerpc)
|
||||
ifeq ($(USE_AESNI),yes)
|
||||
CXXFLAGS += -D__AES__ -maes
|
||||
else
|
||||
CXXFLAGS += -msse
|
||||
endif
|
||||
endif
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -3,10 +3,9 @@
|
|||
[](https://github.com/PurpleI2P/i2pd/blob/openssl/LICENSE)
|
||||
[](https://repology.org/project/i2pd/versions)
|
||||
[](https://hub.docker.com/r/purplei2p/i2pd)
|
||||
[]()
|
||||
[](https://crowdin.com/project/i2pd)
|
||||
|
||||
*Note: i2pd for Android can be found in the [i2pd-android](https://github.com/PurpleI2P/i2pd-android) repository and with a Qt GUI in the [i2pd-qt](https://github.com/PurpleI2P/i2pd-qt) repository. For macOS, a GUI version can be found in the [gui-i2pd](https://github.com/MetanoicArmor/gui-i2pd) repository.*
|
||||
*note: i2pd for Android can be found in [i2pd-android](https://github.com/PurpleI2P/i2pd-android) repository and with Qt GUI in [i2pd-qt](https://github.com/PurpleI2P/i2pd-qt) repository*
|
||||
|
||||
i2pd
|
||||
====
|
||||
|
|
@ -78,7 +77,7 @@ Build instructions:
|
|||
* Docker image - [](https://github.com/PurpleI2P/i2pd/actions/workflows/docker.yml)
|
||||
* Snap - [](https://snapcraft.io/i2pd) [](https://snapcraft.io/i2pd)
|
||||
* FreeBSD - [](https://github.com/PurpleI2P/i2pd/actions/workflows/build-freebsd.yml)
|
||||
* Android
|
||||
* Android - [](https://github.com/PurpleI2P/i2pd-android/actions/workflows/android.yml)
|
||||
* iOS
|
||||
|
||||
Using i2pd
|
||||
|
|
|
|||
|
|
@ -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
|
||||
*
|
||||
|
|
@ -98,11 +98,6 @@ namespace util
|
|||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
int DaemonWin32::GetGracefulShutdownInterval () const
|
||||
{
|
||||
return i2p::win32::GetGracefulShutdownRemainingTime ();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif //_WIN32
|
||||
|
|
|
|||
|
|
@ -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
|
||||
*
|
||||
|
|
@ -44,7 +44,7 @@ namespace i2p
|
|||
{
|
||||
namespace win32
|
||||
{
|
||||
static DWORD g_GracefulShutdownEndtime = 0;
|
||||
DWORD g_GracefulShutdownEndtime = 0;
|
||||
bool g_isWinService;
|
||||
|
||||
static void ShowPopupMenu (HWND hWnd, POINT *curpos, int wDefaultItem)
|
||||
|
|
@ -107,6 +107,116 @@ namespace win32
|
|||
Shell_NotifyIcon (NIM_DELETE, &nid);
|
||||
}
|
||||
|
||||
static void ShowUptime (std::stringstream& s, int seconds)
|
||||
{
|
||||
int num;
|
||||
|
||||
if ((num = seconds / 86400) > 0) {
|
||||
s << num << " days, ";
|
||||
seconds -= num * 86400;
|
||||
}
|
||||
if ((num = seconds / 3600) > 0) {
|
||||
s << num << " hours, ";
|
||||
seconds -= num * 3600;
|
||||
}
|
||||
if ((num = seconds / 60) > 0) {
|
||||
s << num << " min, ";
|
||||
seconds -= num * 60;
|
||||
}
|
||||
s << seconds << " seconds\n";
|
||||
}
|
||||
|
||||
template <typename size> static void ShowTransfered (std::stringstream& s, size transfer)
|
||||
{
|
||||
auto bytes = transfer & 0x03ff;
|
||||
transfer >>= 10;
|
||||
auto kbytes = transfer & 0x03ff;
|
||||
transfer >>= 10;
|
||||
auto mbytes = transfer & 0x03ff;
|
||||
transfer >>= 10;
|
||||
auto gbytes = transfer;
|
||||
|
||||
if (gbytes)
|
||||
s << gbytes << " GB, ";
|
||||
if (mbytes)
|
||||
s << mbytes << " MB, ";
|
||||
if (kbytes)
|
||||
s << kbytes << " KB, ";
|
||||
s << bytes << " Bytes\n";
|
||||
}
|
||||
|
||||
static void ShowNetworkStatus (std::stringstream& s, RouterStatus status, bool testing, RouterError error)
|
||||
{
|
||||
switch (status)
|
||||
{
|
||||
case eRouterStatusOK: s << "OK"; break;
|
||||
case eRouterStatusFirewalled: s << "FW"; break;
|
||||
case eRouterStatusUnknown: s << "Unk"; break;
|
||||
case eRouterStatusProxy: s << "Proxy"; break;
|
||||
case eRouterStatusMesh: s << "Mesh"; break;
|
||||
default: s << "Unk";
|
||||
};
|
||||
if (testing)
|
||||
s << " (Test)";
|
||||
if (error != eRouterErrorNone)
|
||||
{
|
||||
switch (error)
|
||||
{
|
||||
case eRouterErrorClockSkew:
|
||||
s << " - " << tr("Clock skew");
|
||||
break;
|
||||
case eRouterErrorOffline:
|
||||
s << " - " << tr("Offline");
|
||||
break;
|
||||
case eRouterErrorSymmetricNAT:
|
||||
s << " - " << tr("Symmetric NAT");
|
||||
break;
|
||||
case eRouterErrorFullConeNAT:
|
||||
s << " - " << tr("Full cone NAT");
|
||||
break;
|
||||
case eRouterErrorNoDescriptors:
|
||||
s << " - " << tr("No Descriptors");
|
||||
break;
|
||||
default: ;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void PrintMainWindowText (std::stringstream& s)
|
||||
{
|
||||
s << "\n";
|
||||
s << "Status: ";
|
||||
ShowNetworkStatus (s, i2p::context.GetStatus (), i2p::context.GetTesting(), i2p::context.GetError ());
|
||||
if (i2p::context.SupportsV6 ())
|
||||
{
|
||||
s << " / ";
|
||||
ShowNetworkStatus (s, i2p::context.GetStatusV6 (), i2p::context.GetTestingV6(), i2p::context.GetErrorV6 ());
|
||||
}
|
||||
s << "; ";
|
||||
s << "Success Rate: " << i2p::tunnel::tunnels.GetTunnelCreationSuccessRate() << "%\n";
|
||||
s << "Uptime: "; ShowUptime(s, i2p::context.GetUptime ());
|
||||
if (g_GracefulShutdownEndtime != 0)
|
||||
{
|
||||
DWORD GracefulTimeLeft = (g_GracefulShutdownEndtime - GetTickCount()) / 1000;
|
||||
s << "Graceful shutdown, time left: "; ShowUptime(s, GracefulTimeLeft);
|
||||
}
|
||||
else
|
||||
s << "\n";
|
||||
s << "Inbound: " << i2p::transport::transports.GetInBandwidth() / 1024 << " KiB/s; ";
|
||||
s << "Outbound: " << i2p::transport::transports.GetOutBandwidth() / 1024 << " KiB/s\n";
|
||||
s << "Received: "; ShowTransfered (s, i2p::transport::transports.GetTotalReceivedBytes());
|
||||
s << "Sent: "; ShowTransfered (s, i2p::transport::transports.GetTotalSentBytes());
|
||||
s << "\n";
|
||||
s << "Routers: " << i2p::data::netdb.GetNumRouters () << "; ";
|
||||
s << "Floodfills: " << i2p::data::netdb.GetNumFloodfills () << "; ";
|
||||
s << "LeaseSets: " << i2p::data::netdb.GetNumLeaseSets () << "\n";
|
||||
s << "Tunnels: ";
|
||||
s << "In: " << i2p::tunnel::tunnels.CountInboundTunnels() << "; ";
|
||||
s << "Out: " << i2p::tunnel::tunnels.CountOutboundTunnels() << "; ";
|
||||
s << "Transit: " << i2p::tunnel::tunnels.CountTransitTunnels() << "\n";
|
||||
s << "\n";
|
||||
}
|
||||
|
||||
static LRESULT CALLBACK WndProc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
static UINT s_uTaskbarRestart;
|
||||
|
|
@ -203,7 +313,7 @@ namespace win32
|
|||
}
|
||||
case ID_DATADIR:
|
||||
{
|
||||
std::string datadir(i2p::fs::GetDataDir());
|
||||
std::string datadir(i2p::fs::GetUTF8DataDir());
|
||||
ShellExecute(NULL, "explore", datadir.c_str(), NULL, NULL, SW_SHOWNORMAL);
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -245,7 +355,9 @@ namespace win32
|
|||
}
|
||||
}
|
||||
}
|
||||
#if (__cplusplus >= 201703L) // C++ 17 or higher
|
||||
[[fallthrough]];
|
||||
#endif
|
||||
}
|
||||
case WM_TRAYICON:
|
||||
{
|
||||
|
|
@ -294,7 +406,7 @@ namespace win32
|
|||
PAINTSTRUCT ps;
|
||||
RECT rp;
|
||||
HFONT hFont;
|
||||
std::stringstream s; i2p::util::PrintMainWindowText (s);
|
||||
std::stringstream s; PrintMainWindowText (s);
|
||||
hDC = BeginPaint (hWnd, &ps);
|
||||
GetClientRect(hWnd, &rp);
|
||||
SetTextColor(hDC, 0x00D43B69);
|
||||
|
|
@ -388,13 +500,5 @@ namespace win32
|
|||
PostMessage (hWnd, WM_COMMAND, MAKEWPARAM(ID_STOP_GRACEFUL_SHUTDOWN, 0), 0);
|
||||
return hWnd;
|
||||
}
|
||||
|
||||
int GetGracefulShutdownRemainingTime ()
|
||||
{
|
||||
if (!g_GracefulShutdownEndtime) return 0;
|
||||
auto remains = (g_GracefulShutdownEndtime - GetTickCount()) / 1000;
|
||||
if (remains < 0) remains = 0;
|
||||
return remains;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
*
|
||||
|
|
@ -15,12 +15,13 @@ namespace i2p
|
|||
{
|
||||
namespace win32
|
||||
{
|
||||
extern DWORD g_GracefulShutdownEndtime;
|
||||
|
||||
bool StartWin32App (bool isWinService);
|
||||
void StopWin32App ();
|
||||
int RunWin32App ();
|
||||
bool GracefulShutdown ();
|
||||
bool StopGracefulShutdown ();
|
||||
int GetGracefulShutdownRemainingTime ();
|
||||
}
|
||||
}
|
||||
#endif // WIN32APP_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
|
||||
*
|
||||
|
|
|
|||
|
|
@ -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
|
||||
*
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
@ -155,6 +156,20 @@ else()
|
|||
endif()
|
||||
set(CMAKE_CXX_FLAGS_MINSIZEREL "${CMAKE_CXX_FLAGS_MINSIZEREL} -ffunction-sections -fdata-sections")
|
||||
set(CMAKE_EXE_LINKER_FLAGS_MINSIZEREL "-Wl,--gc-sections") # -flto is added from above
|
||||
|
||||
# check for c++17 & c++11 support
|
||||
include(CheckCXXCompilerFlag)
|
||||
|
||||
CHECK_CXX_COMPILER_FLAG("-std=c++17" CXX17_SUPPORTED)
|
||||
CHECK_CXX_COMPILER_FLAG("-std=c++11" CXX11_SUPPORTED)
|
||||
|
||||
if(CXX17_SUPPORTED)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++17")
|
||||
elseif(CXX11_SUPPORTED)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
|
||||
else()
|
||||
message(SEND_ERROR "C++17 nor C++11 standard not seems to be supported by compiler. Too old version?")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
||||
|
|
@ -184,6 +199,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")
|
||||
|
|
@ -198,10 +223,6 @@ if(WITH_THREADSANITIZER)
|
|||
endif()
|
||||
endif()
|
||||
|
||||
if (CMAKE_COMPILER_IS_GNUCC AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 10.0 AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 8.0) # gcc 8-9
|
||||
list(APPEND CMAKE_REQUIRED_LIBRARIES "stdc++fs")
|
||||
endif()
|
||||
|
||||
# Use std::atomic instead of GCC builtins on macOS PowerPC:
|
||||
# For more information refer to: https://github.com/PurpleI2P/i2pd/issues/1726#issuecomment-1306335111
|
||||
# This has been fixed in Boost 1.81, nevertheless we retain the setting for the sake of compatibility.
|
||||
|
|
@ -256,14 +277,14 @@ else()
|
|||
if(NOT MSVC)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC")
|
||||
endif()
|
||||
add_definitions(-DBOOST_ATOMIC_DYN_LINK -DBOOST_FILESYSTEM_DYN_LINK -DBOOST_PROGRAM_OPTIONS_DYN_LINK)
|
||||
add_definitions(-DBOOST_ATOMIC_DYN_LINK -DBOOST_SYSTEM_DYN_LINK -DBOOST_FILESYSTEM_DYN_LINK -DBOOST_PROGRAM_OPTIONS_DYN_LINK -DBOOST_DATE_TIME_DYN_LINK -DBOOST_REGEX_DYN_LINK)
|
||||
if(WIN32)
|
||||
set(Boost_USE_STATIC_LIBS OFF)
|
||||
set(Boost_USE_STATIC_RUNTIME OFF)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
find_package(Boost REQUIRED COMPONENTS filesystem program_options)
|
||||
find_package(Boost REQUIRED COMPONENTS system filesystem program_options date_time OPTIONAL_COMPONENTS atomic)
|
||||
if(NOT DEFINED Boost_FOUND)
|
||||
message(SEND_ERROR "Boost is not found, or your boost version was below 1.46. Please download Boost!")
|
||||
endif()
|
||||
|
|
@ -291,26 +312,6 @@ if(ZLIB_FOUND)
|
|||
link_directories(${ZLIB_ROOT}/lib)
|
||||
endif()
|
||||
|
||||
# C++ standard to use, based on compiler and version of boost
|
||||
if(NOT MSVC)
|
||||
# check for c++20 & c++17 support
|
||||
include(CheckCXXCompilerFlag)
|
||||
|
||||
if(Boost_VERSION VERSION_GREATER_EQUAL "1.83") # min boost version for c++20
|
||||
CHECK_CXX_COMPILER_FLAG("-std=c++20" CXX20_SUPPORTED)
|
||||
endif()
|
||||
CHECK_CXX_COMPILER_FLAG("-std=c++17" CXX17_SUPPORTED)
|
||||
|
||||
|
||||
if(CXX20_SUPPORTED)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++20")
|
||||
elseif(CXX17_SUPPORTED)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++17")
|
||||
else()
|
||||
message(SEND_ERROR "C++20 nor C++17 standard not seems to be supported by compiler. Too old version?")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# load includes
|
||||
include_directories(SYSTEM ${Boost_INCLUDE_DIRS} ${OPENSSL_INCLUDE_DIR} ${ZLIB_INCLUDE_DIR})
|
||||
|
||||
|
|
@ -321,9 +322,9 @@ message(STATUS "Compiler vendor : ${CMAKE_CXX_COMPILER_ID}")
|
|||
message(STATUS "Compiler version : ${CMAKE_CXX_COMPILER_VERSION}")
|
||||
message(STATUS "Compiler path : ${CMAKE_CXX_COMPILER}")
|
||||
message(STATUS "Architecture : ${ARCHITECTURE}")
|
||||
message(STATUS "Compiler flags : ${CMAKE_CXX_FLAGS}")
|
||||
message(STATUS "Install prefix: : ${CMAKE_INSTALL_PREFIX}")
|
||||
message(STATUS "Options:")
|
||||
message(STATUS " AESNI : ${WITH_AESNI}")
|
||||
message(STATUS " HARDENING : ${WITH_HARDENING}")
|
||||
message(STATUS " LIBRARY : ${WITH_LIBRARY}")
|
||||
message(STATUS " BINARY : ${WITH_BINARY}")
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ INCLUDE(CheckLibraryExists)
|
|||
|
||||
function(check_working_cxx_atomics varname)
|
||||
set(OLD_CMAKE_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS})
|
||||
set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -std=c++17")
|
||||
set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -std=c++11")
|
||||
CHECK_CXX_SOURCE_COMPILES("
|
||||
#include <atomic>
|
||||
std::atomic<int> x;
|
||||
|
|
@ -25,7 +25,7 @@ endfunction(check_working_cxx_atomics)
|
|||
|
||||
function(check_working_cxx_atomics64 varname)
|
||||
set(OLD_CMAKE_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS})
|
||||
set(CMAKE_REQUIRED_FLAGS "-std=c++17 ${CMAKE_REQUIRED_FLAGS}")
|
||||
set(CMAKE_REQUIRED_FLAGS "-std=c++11 ${CMAKE_REQUIRED_FLAGS}")
|
||||
CHECK_CXX_SOURCE_COMPILES("
|
||||
#include <atomic>
|
||||
#include <cstdint>
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
34
contrib/certificates/reseed/arnavbhatt288_at_mail.i2p.crt
Normal file
34
contrib/certificates/reseed/arnavbhatt288_at_mail.i2p.crt
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIF2TCCA8GgAwIBAgIQIHQPtSoFU+cUpYD8PZaWZjANBgkqhkiG9w0BAQsFADB2
|
||||
MQswCQYDVQQGEwJYWDELMAkGA1UEBxMCWFgxCzAJBgNVBAkTAlhYMR4wHAYDVQQK
|
||||
ExVJMlAgQW5vbnltb3VzIE5ldHdvcmsxDDAKBgNVBAsTA0kyUDEfMB0GA1UEAwwW
|
||||
YXJuYXZiaGF0dDI4OEBtYWlsLmkycDAeFw0yMzAxMjUxODUzNDFaFw0zMzAxMjUx
|
||||
ODUzNDFaMHYxCzAJBgNVBAYTAlhYMQswCQYDVQQHEwJYWDELMAkGA1UECRMCWFgx
|
||||
HjAcBgNVBAoTFUkyUCBBbm9ueW1vdXMgTmV0d29yazEMMAoGA1UECxMDSTJQMR8w
|
||||
HQYDVQQDDBZhcm5hdmJoYXR0Mjg4QG1haWwuaTJwMIICIjANBgkqhkiG9w0BAQEF
|
||||
AAOCAg8AMIICCgKCAgEAtwG73sC0jYd3fgEzZh0SveAdUd5yD35nINJRrdPSrSwY
|
||||
n3i1qGe3fNLj877PvUDU+qiHH0fFZfyFkXTaq3TUp1u4YkmvaoPHy6FZlojB08lK
|
||||
FBm+iJ1hifQ7MFmvIKUGv+cjlN6xSoQ0U6B2QOy6iZnBgFZ/7jbRY4iZOIj7VJtY
|
||||
aodeHfy0bWe447VJovbkUi7NJPFZQS65LMcAIWcWTxrC0Gj8SmdxL3a5+hxpmmg0
|
||||
+KCQvWQDdxAQjsc16sgUCdUc6cWYO4yw9H6fgdq9GJX+LnXR9OB58GsAjjlLlFoI
|
||||
CZxdARDpoqcIj6AoKIanALf8yfbIyrqqJE47cuaqV9bht5MWKnXbwHplEkT4ZNkh
|
||||
PnRDia7B5HY3uwbt39CBm264PEWXvWG2sozTWKQqBjmMN2cj/NFDUEqKv6BggMY1
|
||||
HcqxWFKRcgKCtRvrmTmfp5l0/ou+OtUaFUg0a6Qhtb93Hj10vK6wZzidBqj0ggzB
|
||||
eJDI95b89u8JgzRoOBriuMKTc91WTkOvBLkB3dgUbUpx2p8KHjvf/pppBH9u0oxp
|
||||
qJFFK840DbnJydEvjKezeVe5Ax6YRSRxyEdKzRoWdvKVxb3qBBKMdCKTYEPxHPBu
|
||||
JMEQVUCXJMti++1KEiQGhcfWvLyT7OewbcIZNk9XWNrxlKcGrTp9AOwaaNC5m1kC
|
||||
AwEAAaNjMGEwDgYDVR0PAQH/BAQDAgKEMB0GA1UdJQQWMBQGCCsGAQUFBwMCBggr
|
||||
BgEFBQcDATAPBgNVHRMBAf8EBTADAQH/MB8GA1UdDgQYBBZhcm5hdmJoYXR0Mjg4
|
||||
QG1haWwuaTJwMA0GCSqGSIb3DQEBCwUAA4ICAQAHiK0ld/1PF9DIhutD660/bzBg
|
||||
mF2Z76hcBqDZ8tnQai/u/RXYrH9wso9BYyrVsvk3fr6tpGT49Ian0MVpPOxMoTU2
|
||||
oBEmQlYrfclQLFsOLmA0y2r1ggXzIrt69jB710Vhwdnz09oOE8rS4E2T5oDD8Wvy
|
||||
Kony+AarRceqtkOlzyquc42KjzdrbHsosF7G2iGhNI6t+T3BfWJ+Q+d5sj3OIh6e
|
||||
gSfvHL44E4vZt6dtofRN3MAZ60kNLF5YWyaUo3Snv9Lso1IwIz3AVr5ehv+8sFL/
|
||||
KxaXdkZ5Yn2YUX7p1t4VQd+eXVPYjf1befg4PvrwSkylu3Jpee3fllZSKXeSVx9x
|
||||
jpJiq5vIakqk22pnWb1Vn7xzSW1vtEG7QLjobOr1WrcGiwdv+HKiWcXJXDzKoWXs
|
||||
h3VEfr51Kap8cIJv+D6lJIG9IcIhiQ6CXWBmtjWJvbdVwFBy1/3Fhaou9liHi+gK
|
||||
4Yh5a5OGCzc7xjtpGaTmoLEz7NzDNOdd/r840qRDOh70izzmFZd5Gwq4hoVcPJcS
|
||||
EAySwtgqK0/4d0zDd2Wg9ASJV9DnDf8QuSmHZgZ9Efs47XcWz9TvkWUS1E66AJsN
|
||||
mmI1NDQ3mv3dv5+WPq+dqqYFsnx3xWL1g5Z3buk0opeuXMzoHwM7UfN8h7Q1M5+t
|
||||
+XBgkaYA4iEwYKqlCQ==
|
||||
-----END CERTIFICATE-----
|
||||
32
contrib/certificates/reseed/hiduser0_at_mail.i2p.crt
Normal file
32
contrib/certificates/reseed/hiduser0_at_mail.i2p.crt
Normal file
|
|
@ -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-----
|
||||
33
contrib/certificates/reseed/hottuna_at_mail.i2p.crt
Normal file
33
contrib/certificates/reseed/hottuna_at_mail.i2p.crt
Normal file
|
|
@ -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-----
|
||||
|
|
@ -2,13 +2,13 @@ Description: Enable UPnP usage in package
|
|||
Author: r4sas <r4sas@i2pmail.org>
|
||||
|
||||
Reviewed-By: r4sas <r4sas@i2pmail.org>
|
||||
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)
|
||||
|
|
|
|||
|
|
@ -2,13 +2,13 @@ Description: Enable UPnP usage in package
|
|||
Author: r4sas <r4sas@i2pmail.org>
|
||||
|
||||
Reviewed-By: r4sas <r4sas@i2pmail.org>
|
||||
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)
|
||||
|
|
|
|||
|
|
@ -142,9 +142,7 @@ ipv6 = false
|
|||
## Address and port service will listen on (default: 127.0.0.1:4444)
|
||||
# address = 127.0.0.1
|
||||
# port = 4444
|
||||
## Optional keys file for proxy local destination (default: transient-proxy)
|
||||
## "Transient" means it changes on every new app launch
|
||||
## If you want not change address then set like http-proxy-keys.dat
|
||||
## Optional keys file for proxy local destination (default: http-proxy-keys.dat)
|
||||
# keys = http-proxy-keys.dat
|
||||
## Enable address helper for adding .i2p domains with "jump URLs" (default: true)
|
||||
## You should disable this feature if your i2pd HTTP Proxy is public,
|
||||
|
|
@ -160,12 +158,10 @@ ipv6 = false
|
|||
## Address and port service will listen on (default: 127.0.0.1:4447)
|
||||
# address = 127.0.0.1
|
||||
# port = 4447
|
||||
## Optional keys file for proxy local destination (default: transient-proxy)
|
||||
## "Transient" means it changes on every new app launch
|
||||
## If you don't want the address to change, set a file name like socks-proxy-keys.dat
|
||||
## Optional keys file for proxy local destination (default: socks-proxy-keys.dat)
|
||||
# keys = socks-proxy-keys.dat
|
||||
## Socks outproxy. Example below is set to use Tor for all connections except i2p
|
||||
## Enable SOCKS outproxy (works only with SOCKS4, default: false)
|
||||
## Enable using of SOCKS outproxy (works only with SOCKS4, default: false)
|
||||
# outproxy.enabled = false
|
||||
## Address and port of outproxy
|
||||
# outproxy = 127.0.0.1
|
||||
|
|
@ -247,12 +243,12 @@ 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: 10000)
|
||||
## Maximum active transit sessions (default: 5000)
|
||||
## This value is doubled if floodfill mode is enabled!
|
||||
# transittunnels = 10000
|
||||
# transittunnels = 5000
|
||||
## Limit number of open file descriptors (0 - use system limit)
|
||||
# openfiles = 0
|
||||
## Maximum size of corefile in Kb (0 - use system limit)
|
||||
|
|
@ -281,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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
%define git_hash %(git rev-parse HEAD | cut -c -7)
|
||||
|
||||
Name: i2pd-git
|
||||
Version: 2.58.0
|
||||
Version: 2.53.1
|
||||
Release: git%{git_hash}%{?dist}
|
||||
Summary: I2P router written in C++
|
||||
Conflicts: i2pd
|
||||
|
|
@ -10,10 +10,11 @@ License: BSD
|
|||
URL: https://github.com/PurpleI2P/i2pd
|
||||
Source0: https://github.com/PurpleI2P/i2pd/archive/openssl/i2pd-openssl.tar.gz
|
||||
|
||||
Provides: group(i2pd) = %{version}-%{release}
|
||||
Provides: user(i2pd) = %{version}-%{release}
|
||||
|
||||
%if 0%{?rhel} == 7
|
||||
BuildRequires: cmake3
|
||||
%else
|
||||
BuildRequires: cmake
|
||||
%endif
|
||||
|
||||
BuildRequires: chrpath
|
||||
BuildRequires: gcc-c++
|
||||
|
|
@ -23,7 +24,7 @@ BuildRequires: openssl-devel
|
|||
BuildRequires: miniupnpc-devel
|
||||
BuildRequires: systemd-units
|
||||
|
||||
%if 0%{?fedora} == 41
|
||||
%if 0%{?fedora} > 40 || 0%{?eln}
|
||||
BuildRequires: openssl-devel-engine
|
||||
%endif
|
||||
|
||||
|
|
@ -42,18 +43,26 @@ C++ implementation of I2P.
|
|||
|
||||
%build
|
||||
cd build
|
||||
%cmake \
|
||||
%if 0%{?rhel} == 7
|
||||
%cmake3 \
|
||||
-DWITH_LIBRARY=OFF \
|
||||
-DWITH_UPNP=ON \
|
||||
-DWITH_HARDENING=ON \
|
||||
%if 0%{?fedora} > 29
|
||||
-DBUILD_SHARED_LIBS:BOOL=OFF
|
||||
%else
|
||||
%cmake \
|
||||
-DWITH_LIBRARY=OFF \
|
||||
-DWITH_UPNP=ON \
|
||||
-DWITH_HARDENING=ON \
|
||||
%if 0%{?fedora} > 29
|
||||
-DBUILD_SHARED_LIBS:BOOL=OFF \
|
||||
.
|
||||
%else
|
||||
%else
|
||||
-DBUILD_SHARED_LIBS:BOOL=OFF
|
||||
%endif
|
||||
%endif
|
||||
|
||||
%if 0%{?rhel} >= 9 || 0%{?fedora} >= 35 || 0%{?eln}
|
||||
%if 0%{?rhel} == 9 || 0%{?fedora} >= 35 || 0%{?eln}
|
||||
pushd redhat-linux-build
|
||||
%else
|
||||
%if 0%{?fedora} >= 33
|
||||
|
|
@ -67,7 +76,7 @@ cd build
|
|||
|
||||
make %{?_smp_mflags}
|
||||
|
||||
%if 0%{?rhel} >= 9 || 0%{?fedora} >= 33 || 0%{?mageia} > 7
|
||||
%if 0%{?rhel} == 9 || 0%{?fedora} >= 33 || 0%{?mageia} > 7
|
||||
popd
|
||||
%endif
|
||||
|
||||
|
|
@ -75,7 +84,7 @@ make %{?_smp_mflags}
|
|||
%install
|
||||
pushd build
|
||||
|
||||
%if 0%{?rhel} >= 9 || 0%{?fedora} >= 35 || 0%{?eln}
|
||||
%if 0%{?rhel} == 9 || 0%{?fedora} >= 35 || 0%{?eln}
|
||||
pushd redhat-linux-build
|
||||
%else
|
||||
%if 0%{?fedora} >= 33
|
||||
|
|
@ -139,21 +148,6 @@ getent passwd i2pd >/dev/null || \
|
|||
|
||||
|
||||
%changelog
|
||||
* Mon Sep 08 2025 orignal <orignal@i2pmail.org> - 2.58.0
|
||||
- update to 2.58.0
|
||||
|
||||
* Mon Jun 02 2025 orignal <orignal@i2pmail.org> - 2.57.0
|
||||
- update to 2.57.0
|
||||
|
||||
* Tue Feb 11 2025 orignal <orignal@i2pmail.org> - 2.56.0
|
||||
- update to 2.56.0
|
||||
|
||||
* Mon Dec 30 2024 orignal <orignal@i2pmail.org> - 2.55.0
|
||||
- update to 2.55.0
|
||||
|
||||
* Sun Oct 6 2024 orignal <orignal@i2pmail.org> - 2.54.0
|
||||
- update to 2.54.0
|
||||
|
||||
* Tue Jul 30 2024 orignal <orignal@i2pmail.org> - 2.53.1
|
||||
- update to 2.53.1
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
Name: i2pd
|
||||
Version: 2.58.0
|
||||
Version: 2.53.1
|
||||
Release: 1%{?dist}
|
||||
Summary: I2P router written in C++
|
||||
Conflicts: i2pd-git
|
||||
|
|
@ -8,10 +8,11 @@ License: BSD
|
|||
URL: https://github.com/PurpleI2P/i2pd
|
||||
Source0: https://github.com/PurpleI2P/i2pd/archive/%{version}/%name-%version.tar.gz
|
||||
|
||||
Provides: group(i2pd) = %{version}-%{release}
|
||||
Provides: user(i2pd) = %{version}-%{release}
|
||||
|
||||
%if 0%{?rhel} == 7
|
||||
BuildRequires: cmake3
|
||||
%else
|
||||
BuildRequires: cmake
|
||||
%endif
|
||||
|
||||
BuildRequires: chrpath
|
||||
BuildRequires: gcc-c++
|
||||
|
|
@ -21,7 +22,7 @@ BuildRequires: openssl-devel
|
|||
BuildRequires: miniupnpc-devel
|
||||
BuildRequires: systemd-units
|
||||
|
||||
%if 0%{?fedora} == 41
|
||||
%if 0%{?fedora} > 40 || 0%{?eln}
|
||||
BuildRequires: openssl-devel-engine
|
||||
%endif
|
||||
|
||||
|
|
@ -40,18 +41,26 @@ C++ implementation of I2P.
|
|||
|
||||
%build
|
||||
cd build
|
||||
%cmake \
|
||||
%if 0%{?rhel} == 7
|
||||
%cmake3 \
|
||||
-DWITH_LIBRARY=OFF \
|
||||
-DWITH_UPNP=ON \
|
||||
-DWITH_HARDENING=ON \
|
||||
%if 0%{?fedora} > 29
|
||||
-DBUILD_SHARED_LIBS:BOOL=OFF
|
||||
%else
|
||||
%cmake \
|
||||
-DWITH_LIBRARY=OFF \
|
||||
-DWITH_UPNP=ON \
|
||||
-DWITH_HARDENING=ON \
|
||||
%if 0%{?fedora} > 29
|
||||
-DBUILD_SHARED_LIBS:BOOL=OFF \
|
||||
.
|
||||
%else
|
||||
%else
|
||||
-DBUILD_SHARED_LIBS:BOOL=OFF
|
||||
%endif
|
||||
%endif
|
||||
|
||||
%if 0%{?rhel} >= 9 || 0%{?fedora} >= 35 || 0%{?eln}
|
||||
%if 0%{?rhel} == 9 || 0%{?fedora} >= 35 || 0%{?eln}
|
||||
pushd redhat-linux-build
|
||||
%else
|
||||
%if 0%{?fedora} >= 33
|
||||
|
|
@ -65,7 +74,7 @@ cd build
|
|||
|
||||
make %{?_smp_mflags}
|
||||
|
||||
%if 0%{?rhel} >= 9 || 0%{?fedora} >= 33 || 0%{?mageia} > 7
|
||||
%if 0%{?rhel} == 9 || 0%{?fedora} >= 33 || 0%{?mageia} > 7
|
||||
popd
|
||||
%endif
|
||||
|
||||
|
|
@ -73,7 +82,7 @@ make %{?_smp_mflags}
|
|||
%install
|
||||
pushd build
|
||||
|
||||
%if 0%{?rhel} >= 9 || 0%{?fedora} >= 35 || 0%{?eln}
|
||||
%if 0%{?rhel} == 9 || 0%{?fedora} >= 35 || 0%{?eln}
|
||||
pushd redhat-linux-build
|
||||
%else
|
||||
%if 0%{?fedora} >= 33
|
||||
|
|
@ -137,21 +146,6 @@ getent passwd i2pd >/dev/null || \
|
|||
|
||||
|
||||
%changelog
|
||||
* Mon Sep 08 2025 orignal <orignal@i2pmail.org> - 2.58.0
|
||||
- update to 2.58.0
|
||||
|
||||
* Mon Jun 02 2025 orignal <orignal@i2pmail.org> - 2.57.0
|
||||
- update to 2.57.0
|
||||
|
||||
* Tue Feb 11 2025 orignal <orignal@i2pmail.org> - 2.56.0
|
||||
- update to 2.56.0
|
||||
|
||||
* Mon Dec 30 2024 orignal <orignal@i2pmail.org> - 2.55.0
|
||||
- update to 2.55.0
|
||||
|
||||
* Sun Oct 6 2024 orignal <orignal@i2pmail.org> - 2.54.0
|
||||
- update to 2.54.0
|
||||
|
||||
* Tue Jul 30 2024 orignal <orignal@i2pmail.org> - 2.53.1
|
||||
- update to 2.53.1
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ port = 6668
|
|||
destination = irc.ilita.i2p
|
||||
destinationport = 6667
|
||||
keys = irc-keys.dat
|
||||
i2p.streaming.profile=2
|
||||
|
||||
#[IRC-IRC2P]
|
||||
#type = client
|
||||
|
|
|
|||
|
|
@ -1,174 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2021-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
|
||||
*
|
||||
******************************************************************
|
||||
*
|
||||
* This is style sheet for webconsole, with @media selectors for adaptive
|
||||
* view on desktop and mobile devices, respecting preferred user's color
|
||||
* scheme used in system/browser.
|
||||
*
|
||||
* Minified copy of that style sheet is bundled inside i2pd sources.
|
||||
*/
|
||||
:root {
|
||||
--main-bg-color: #0b0b0b;
|
||||
--main-text-color: #00ff99;
|
||||
--main-link-color: #ff00ff;
|
||||
--main-link-hover-color: #00ffff;
|
||||
}
|
||||
|
||||
body {
|
||||
font: 100%/1.5em "Courier New", Courier, monospace;
|
||||
margin: 0;
|
||||
padding: 1.5em;
|
||||
background: var(--main-bg-color);
|
||||
color: var(--main-text-color);
|
||||
}
|
||||
|
||||
a, .slide label {
|
||||
text-decoration: none;
|
||||
color: var(--main-link-color);
|
||||
}
|
||||
|
||||
a:hover, a.button.selected, .slide label:hover, button[type=submit]:hover {
|
||||
color: var(--main-link-hover-color);
|
||||
background: var(--main-link-color);
|
||||
}
|
||||
.slidecontent {
|
||||
display:none;
|
||||
}
|
||||
#slide-info {
|
||||
display:none;
|
||||
}
|
||||
.enabled {
|
||||
color: green;
|
||||
text-shadow: 2px green;
|
||||
}
|
||||
.disabled {
|
||||
color: red;
|
||||
}
|
||||
#slide-info:checked ~ .slidecontent {
|
||||
display: block;
|
||||
}
|
||||
#slide_ntcp2 {
|
||||
display: none;
|
||||
}
|
||||
#slide_ssu2 {
|
||||
display: none;
|
||||
}
|
||||
#slide_ssu2:checked ~ .slidecontent {
|
||||
display: block;
|
||||
}
|
||||
#slide_ntcp2:checked ~ .slidecontent{
|
||||
display:block;
|
||||
}
|
||||
.header {
|
||||
font-size: 2.5em;
|
||||
text-align: center;
|
||||
margin: 1em 0;
|
||||
color: var(--main-link-color);
|
||||
text-shadow: 0 0 5px #ff00ff, 0 0 10px #ff00ff;
|
||||
}
|
||||
|
||||
.wrapper {
|
||||
margin: 0 auto;
|
||||
padding: 1em;
|
||||
max-width: 64em;
|
||||
}
|
||||
|
||||
.menu {
|
||||
display: block;
|
||||
float: left;
|
||||
overflow: hidden;
|
||||
padding: 4px;
|
||||
max-width: 12em;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.listitem, .tableitem {
|
||||
display: block;
|
||||
font-family: monospace;
|
||||
font-size: 1.2em;
|
||||
white-space: nowrap;
|
||||
color: #00ff99;
|
||||
}
|
||||
|
||||
.content {
|
||||
/* float: left;*/
|
||||
font-size: 1em;
|
||||
margin-left: 2em;
|
||||
padding: 4px;
|
||||
max-width: 50em;
|
||||
overflow: auto;
|
||||
left: 35%;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.tunnel.established {
|
||||
color: #0ff;
|
||||
}
|
||||
|
||||
.tunnel.expiring {
|
||||
color: #ff0;
|
||||
}
|
||||
|
||||
.tunnel.failed {
|
||||
color: #f00;
|
||||
}
|
||||
|
||||
.tunnel.building {
|
||||
color: #888;
|
||||
}
|
||||
|
||||
caption {
|
||||
font-size: 1.5em;
|
||||
text-align: center;
|
||||
color: var(--main-link-color);
|
||||
text-shadow: 0 0 3px #ff00ff;
|
||||
}
|
||||
|
||||
table {
|
||||
display: table;
|
||||
border-collapse: collapse;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
textarea, input, select {
|
||||
background-color: #111;
|
||||
color: var(--main-text-color);
|
||||
border: 1px solid var(--main-link-color);
|
||||
font-family: monospace;
|
||||
font-size: 14px;
|
||||
padding: 5px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
button[type=submit], a.button {
|
||||
background-color: transparent;
|
||||
color: var(--main-link-color);
|
||||
border: 1px solid var(--main-link-color);
|
||||
padding: 5px 10px;
|
||||
font-family: monospace;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
button[type=submit]:hover, a.button:hover {
|
||||
color: var(--main-link-hover-color);
|
||||
background-color: #222;
|
||||
box-shadow: 0 0 5px var(--main-link-hover-color);
|
||||
}
|
||||
|
||||
@media screen and (max-width: 980px) {
|
||||
.menu, .content, input, select, textarea, button[type=submit], a.button {
|
||||
width: 90%;
|
||||
margin: 0 auto 10px auto;
|
||||
display: block;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
*
|
||||
|
|
@ -92,14 +92,12 @@ a.button {
|
|||
}
|
||||
|
||||
.content {
|
||||
/* float: left;*/
|
||||
float: left;
|
||||
font-size: 1em;
|
||||
margin-left: 2em;
|
||||
padding: 4px;
|
||||
max-width: 50em;
|
||||
overflow: auto;
|
||||
left: 35%;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.tunnel.established {
|
||||
|
|
@ -232,8 +230,6 @@ input[type=number]::-webkit-inner-spin-button {
|
|||
max-width: 100%;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
a, .slide label {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
*
|
||||
|
|
@ -8,7 +8,6 @@
|
|||
|
||||
#include <thread>
|
||||
#include <memory>
|
||||
#include <regex>
|
||||
|
||||
#include "Daemon.h"
|
||||
|
||||
|
|
@ -62,9 +61,9 @@ namespace util
|
|||
return service;
|
||||
}
|
||||
|
||||
void Daemon_Singleton::setDataDir(std::string_view path)
|
||||
void Daemon_Singleton::setDataDir(std::string path)
|
||||
{
|
||||
if (!path.empty ())
|
||||
if (path != "")
|
||||
DaemonDataDir = path;
|
||||
}
|
||||
|
||||
|
|
@ -118,8 +117,8 @@ namespace util
|
|||
if (logclftime)
|
||||
i2p::log::Logger().SetTimeFormat ("[%d/%b/%Y:%H:%M:%S %z]");
|
||||
|
||||
#if defined(WIN32_APP) || defined(__HAIKU__)
|
||||
// Win32 app with GUI or Haiku supports only logging to file
|
||||
#ifdef WIN32_APP
|
||||
// Win32 app with GUI supports only logging to file
|
||||
logs = "file";
|
||||
#else
|
||||
if (isDaemon && (logs == "" || logs == "stdout"))
|
||||
|
|
@ -150,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
|
||||
|
||||
|
|
@ -187,41 +188,12 @@ namespace util
|
|||
std::string bandwidth; i2p::config::GetOption("bandwidth", bandwidth);
|
||||
if (bandwidth.length () > 0)
|
||||
{
|
||||
const auto NumBandwithRegex = std::regex(R"(^\d+$)");
|
||||
const auto BandwithRegex = std::regex(R"((\d+)(b|kb|mb|gb))");
|
||||
std::smatch bandWithMatch;
|
||||
|
||||
if (bandwidth.length () == 1 && ((bandwidth[0] >= 'K' && bandwidth[0] <= 'P') || bandwidth[0] == 'X' ))
|
||||
{
|
||||
i2p::context.SetBandwidth (bandwidth[0]);
|
||||
LogPrint(eLogInfo, "Daemon: Bandwidth set to ", i2p::context.GetBandwidthLimit (), "KBps");
|
||||
}
|
||||
else if (std::regex_match(bandwidth, bandWithMatch, BandwithRegex)) {
|
||||
const auto number = bandWithMatch[1].str();
|
||||
const auto unit = bandWithMatch[2].str();
|
||||
int limit = std::atoi(number.c_str());
|
||||
std::cout << unit;
|
||||
if (unit == "b")
|
||||
{
|
||||
limit /= 1000;
|
||||
}
|
||||
else if(unit == "mb")
|
||||
{
|
||||
limit *= 1000;
|
||||
} else if(unit == "gb")
|
||||
{
|
||||
limit *= 1000000;
|
||||
}
|
||||
// if limit more than 32 bits then its will be negative
|
||||
if (limit < 0)
|
||||
{
|
||||
LogPrint(eLogInfo, "Daemon: Unexpected bandwidth ", bandwidth, ". Set to 'low'");
|
||||
i2p::context.SetBandwidth (i2p::data::CAPS_FLAG_LOW_BANDWIDTH2);
|
||||
} else {
|
||||
i2p::context.SetBandwidth(limit);
|
||||
}
|
||||
}
|
||||
else if(std::regex_search(bandwidth, NumBandwithRegex))
|
||||
else
|
||||
{
|
||||
auto value = std::atoi(bandwidth.c_str());
|
||||
if (value > 0)
|
||||
|
|
@ -434,115 +406,5 @@ namespace util
|
|||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void ShowUptime (std::stringstream& s, int seconds)
|
||||
{
|
||||
int num;
|
||||
|
||||
if ((num = seconds / 86400) > 0) {
|
||||
s << num << " days, ";
|
||||
seconds -= num * 86400;
|
||||
}
|
||||
if ((num = seconds / 3600) > 0) {
|
||||
s << num << " hours, ";
|
||||
seconds -= num * 3600;
|
||||
}
|
||||
if ((num = seconds / 60) > 0) {
|
||||
s << num << " min, ";
|
||||
seconds -= num * 60;
|
||||
}
|
||||
s << seconds << " seconds\n";
|
||||
}
|
||||
|
||||
static void ShowTransfered (std::stringstream& s, size_t transfer)
|
||||
{
|
||||
auto bytes = transfer & 0x03ff;
|
||||
transfer >>= 10;
|
||||
auto kbytes = transfer & 0x03ff;
|
||||
transfer >>= 10;
|
||||
auto mbytes = transfer & 0x03ff;
|
||||
transfer >>= 10;
|
||||
auto gbytes = transfer;
|
||||
|
||||
if (gbytes)
|
||||
s << gbytes << " GB, ";
|
||||
if (mbytes)
|
||||
s << mbytes << " MB, ";
|
||||
if (kbytes)
|
||||
s << kbytes << " KB, ";
|
||||
s << bytes << " Bytes\n";
|
||||
}
|
||||
|
||||
static void ShowNetworkStatus (std::stringstream& s, RouterStatus status, bool testing, RouterError error)
|
||||
{
|
||||
switch (status)
|
||||
{
|
||||
case eRouterStatusOK: s << "OK"; break;
|
||||
case eRouterStatusFirewalled: s << "FW"; break;
|
||||
case eRouterStatusUnknown: s << "Unk"; break;
|
||||
case eRouterStatusProxy: s << "Proxy"; break;
|
||||
case eRouterStatusMesh: s << "Mesh"; break;
|
||||
default: s << "Unk";
|
||||
};
|
||||
if (testing)
|
||||
s << " (Test)";
|
||||
if (error != eRouterErrorNone)
|
||||
{
|
||||
switch (error)
|
||||
{
|
||||
case eRouterErrorClockSkew:
|
||||
s << " - " << tr("Clock skew");
|
||||
break;
|
||||
case eRouterErrorOffline:
|
||||
s << " - " << tr("Offline");
|
||||
break;
|
||||
case eRouterErrorSymmetricNAT:
|
||||
s << " - " << tr("Symmetric NAT");
|
||||
break;
|
||||
case eRouterErrorFullConeNAT:
|
||||
s << " - " << tr("Full cone NAT");
|
||||
break;
|
||||
case eRouterErrorNoDescriptors:
|
||||
s << " - " << tr("No Descriptors");
|
||||
break;
|
||||
default: ;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PrintMainWindowText (std::stringstream& s)
|
||||
{
|
||||
s << "\n";
|
||||
s << "Status: ";
|
||||
ShowNetworkStatus (s, i2p::context.GetStatus (), i2p::context.GetTesting(), i2p::context.GetError ());
|
||||
if (i2p::context.SupportsV6 ())
|
||||
{
|
||||
s << " / ";
|
||||
ShowNetworkStatus (s, i2p::context.GetStatusV6 (), i2p::context.GetTestingV6(), i2p::context.GetErrorV6 ());
|
||||
}
|
||||
s << "; ";
|
||||
s << "Success Rate: " << i2p::tunnel::tunnels.GetTunnelCreationSuccessRate() << "%\n";
|
||||
s << "Uptime: "; ShowUptime(s, i2p::context.GetUptime ());
|
||||
auto gracefulTimeLeft = Daemon.GetGracefulShutdownInterval ();
|
||||
if (gracefulTimeLeft > 0)
|
||||
{
|
||||
s << "Graceful shutdown, time left: "; ShowUptime(s, gracefulTimeLeft);
|
||||
}
|
||||
else
|
||||
s << "\n";
|
||||
s << "Inbound: " << i2p::transport::transports.GetInBandwidth() / 1024 << " KiB/s; ";
|
||||
s << "Outbound: " << i2p::transport::transports.GetOutBandwidth() / 1024 << " KiB/s\n";
|
||||
s << "Received: "; ShowTransfered (s, i2p::transport::transports.GetTotalReceivedBytes());
|
||||
s << "Sent: "; ShowTransfered (s, i2p::transport::transports.GetTotalSentBytes());
|
||||
s << "\n";
|
||||
s << "Routers: " << i2p::data::netdb.GetNumRouters () << "; ";
|
||||
s << "Floodfills: " << i2p::data::netdb.GetNumFloodfills () << "; ";
|
||||
s << "LeaseSets: " << i2p::data::netdb.GetNumLeaseSets () << "\n";
|
||||
s << "Tunnels: ";
|
||||
s << "In: " << i2p::tunnel::tunnels.CountInboundTunnels() << "; ";
|
||||
s << "Out: " << i2p::tunnel::tunnels.CountOutboundTunnels() << "; ";
|
||||
s << "Transit: " << i2p::tunnel::tunnels.CountTransitTunnels() << "\n";
|
||||
s << "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,9 +11,7 @@
|
|||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <ostream>
|
||||
#include <sstream>
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
|
|
@ -30,10 +28,7 @@ namespace util
|
|||
virtual bool stop ();
|
||||
virtual void run () {};
|
||||
|
||||
virtual int GetGracefulShutdownInterval () const { return 0; };
|
||||
void setDataDir (std::string_view path);
|
||||
|
||||
public:
|
||||
virtual void setDataDir (std::string path);
|
||||
|
||||
bool isDaemon;
|
||||
bool running;
|
||||
|
|
@ -54,8 +49,6 @@ namespace util
|
|||
std::string DaemonDataDir;
|
||||
};
|
||||
|
||||
void PrintMainWindowText (std::stringstream& s); // for GUI
|
||||
|
||||
#if defined(QT_GUI_LIB) // check if QT
|
||||
#define Daemon i2p::util::DaemonQT::Instance()
|
||||
// dummy, invoked from RunQT
|
||||
|
|
@ -87,15 +80,9 @@ namespace util
|
|||
bool stop();
|
||||
void run ();
|
||||
|
||||
int GetGracefulShutdownInterval () const;
|
||||
|
||||
public:
|
||||
|
||||
bool isGraceful;
|
||||
|
||||
private:
|
||||
|
||||
DaemonWin32 (): isGraceful(false) {}
|
||||
DaemonWin32 ():isGraceful(false) {}
|
||||
};
|
||||
#elif (defined(ANDROID) && !defined(ANDROID_BINARY))
|
||||
#define Daemon i2p::util::DaemonAndroid::Instance()
|
||||
|
|
@ -110,14 +97,15 @@ namespace util
|
|||
return instance;
|
||||
}
|
||||
};
|
||||
#else // Unix-like systems, including Linux
|
||||
class DaemonUnix : public Daemon_Singleton
|
||||
#else
|
||||
#define Daemon i2p::util::DaemonLinux::Instance()
|
||||
class DaemonLinux : public Daemon_Singleton
|
||||
{
|
||||
public:
|
||||
|
||||
static DaemonUnix& Instance()
|
||||
static DaemonLinux& Instance()
|
||||
{
|
||||
static DaemonUnix instance;
|
||||
static DaemonLinux instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
|
|
@ -125,8 +113,6 @@ namespace util
|
|||
bool stop();
|
||||
void run ();
|
||||
|
||||
int GetGracefulShutdownInterval () const { return gracefulShutdownInterval; };
|
||||
|
||||
private:
|
||||
|
||||
std::string pidfile;
|
||||
|
|
@ -136,25 +122,6 @@ namespace util
|
|||
|
||||
int gracefulShutdownInterval; // in seconds
|
||||
};
|
||||
#if !defined(__HAIKU__)
|
||||
#define Daemon i2p::util::DaemonUnix::Instance()
|
||||
#else
|
||||
class DaemonHaiku: public DaemonUnix
|
||||
{
|
||||
public:
|
||||
|
||||
static DaemonHaiku& Instance ()
|
||||
{
|
||||
static DaemonHaiku instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
bool start ();
|
||||
void run ();
|
||||
};
|
||||
#define Daemon i2p::util::DaemonHaiku::Instance()
|
||||
#endif
|
||||
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 <sstream>
|
||||
#include <thread>
|
||||
#include <memory>
|
||||
#include <regex>
|
||||
|
||||
#include <boost/asio.hpp>
|
||||
#include <boost/algorithm/string.hpp>
|
||||
|
|
@ -43,11 +42,10 @@
|
|||
|
||||
namespace i2p {
|
||||
namespace http {
|
||||
|
||||
static void LoadExtCSS (std::string fileName = "style")
|
||||
static void LoadExtCSS ()
|
||||
{
|
||||
std::stringstream s;
|
||||
std::string styleFile = i2p::fs::DataDirPath ("webconsole/"+fileName+".css");
|
||||
std::string styleFile = i2p::fs::DataDirPath ("webconsole/style.css");
|
||||
if (i2p::fs::Exists(styleFile)) {
|
||||
std::ifstream f(styleFile, std::ifstream::binary);
|
||||
s << f.rdbuf();
|
||||
|
|
@ -134,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 :
|
||||
|
|
@ -149,7 +146,7 @@ namespace http {
|
|||
}
|
||||
if (stateText.empty ()) stateText = tr(state);
|
||||
|
||||
s << "<span class=\"tunnel " << state << "\"> " << stateText << ((explr) ? " (" + std::string(tr("exploratory")) + ")" : "") << "</span>, "; // TODO:
|
||||
s << "<span class=\"tunnel " << state << "\"> " << stateText << ((explr) ? " (" + tr("exploratory") + ")" : "") << "</span>, ";
|
||||
ShowTraffic(s, bytes);
|
||||
s << "\r\n";
|
||||
}
|
||||
|
|
@ -168,46 +165,21 @@ namespace http {
|
|||
static void ShowPageHead (std::stringstream& s)
|
||||
{
|
||||
std::string webroot; i2p::config::GetOption("http.webroot", webroot);
|
||||
std::string theme; i2p::config::GetOption("http.theme", theme);
|
||||
|
||||
const auto isThemeRegex = std::regex("^(white|black|light)");
|
||||
if (!std::regex_search(theme, isThemeRegex))
|
||||
{
|
||||
LoadExtCSS(theme);
|
||||
}
|
||||
else
|
||||
{
|
||||
LoadExtCSS();
|
||||
}
|
||||
|
||||
// Page language
|
||||
std::string currLang = i2p::client::context.GetLanguage ()->GetLanguage(); // get current used language
|
||||
auto it = i2p::i18n::languages.find(currLang);
|
||||
std::string langCode = it->second.ShortCode;
|
||||
|
||||
// Right to Left language option
|
||||
bool rtl = i2p::client::context.GetLanguage ()->GetRTL();
|
||||
|
||||
s <<
|
||||
"<!DOCTYPE html>\r\n"
|
||||
"<html lang=\"" << langCode << "\"" << (rtl ? " dir=\"rtl\"" : "") << ">\r\n"
|
||||
"<html lang=\"" << langCode << "\">\r\n"
|
||||
" <head>\r\n" /* TODO: Find something to parse html/template system. This is horrible. */
|
||||
" <meta charset=\"UTF-8\">\r\n"
|
||||
" <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\r\n"
|
||||
" <link rel=\"shortcut icon\" href=\"" << itoopieFavicon << "\">\r\n"
|
||||
" <title>" << tr(/* tr: Webconsole page title */ "Purple I2P Webconsole") << "</title>\r\n";
|
||||
GetStyles(s);
|
||||
if (theme == "black")
|
||||
{
|
||||
s <<
|
||||
"<style>:root {\r\n"
|
||||
"--main-bg-color: #242424;\r\n"
|
||||
"--main-text-color: #17ab5c;\r\n"
|
||||
"--main-link-color: #bf64b7;\r\n"
|
||||
"--main-link-hover-color: #000000;\r\n"
|
||||
"}\r\n</style>";
|
||||
|
||||
}
|
||||
s <<
|
||||
"</head>\r\n"
|
||||
"<body>\r\n"
|
||||
|
|
@ -241,7 +213,7 @@ namespace http {
|
|||
"</html>\r\n";
|
||||
}
|
||||
|
||||
static void ShowError(std::stringstream& s, std::string_view string)
|
||||
static void ShowError(std::stringstream& s, const std::string& string)
|
||||
{
|
||||
s << "<b>" << tr("ERROR") << ":</b> " << string << "<br>\r\n";
|
||||
}
|
||||
|
|
@ -288,25 +260,29 @@ namespace http {
|
|||
s << "<b>" << tr("Uptime") << ":</b> ";
|
||||
ShowUptime(s, i2p::context.GetUptime ());
|
||||
s << "<br>\r\n";
|
||||
if (i2p::context.SupportsV4 () || i2p::context.GetStatus () != eRouterStatusUnknown) // don't show Unknown for ipv6-only
|
||||
{
|
||||
s << "<b>" << tr("Network status") << ":</b> ";
|
||||
ShowNetworkStatus (s, i2p::context.GetStatus (), i2p::context.GetTesting(), i2p::context.GetError ());
|
||||
s << "<br>\r\n";
|
||||
}
|
||||
if (i2p::context.SupportsV6 ())
|
||||
{
|
||||
s << "<b>" << tr("Network status v6") << ":</b> ";
|
||||
ShowNetworkStatus (s, i2p::context.GetStatusV6 (), i2p::context.GetTestingV6(), i2p::context.GetErrorV6 ());
|
||||
s << "<br>\r\n";
|
||||
}
|
||||
auto remains = Daemon.GetGracefulShutdownInterval ();
|
||||
if (remains > 0)
|
||||
{
|
||||
#if ((!defined(WIN32) && !defined(QT_GUI_LIB) && !defined(ANDROID)) || defined(ANDROID_BINARY))
|
||||
if (auto remains = Daemon.gracefulShutdownInterval) {
|
||||
s << "<b>" << tr("Stopping in") << ":</b> ";
|
||||
ShowUptime(s, remains);
|
||||
s << "<br>\r\n";
|
||||
}
|
||||
#elif defined(WIN32_APP)
|
||||
if (i2p::win32::g_GracefulShutdownEndtime != 0) {
|
||||
uint16_t remains = (i2p::win32::g_GracefulShutdownEndtime - GetTickCount()) / 1000;
|
||||
s << "<b>" << tr("Stopping in") << ":</b> ";
|
||||
ShowUptime(s, remains);
|
||||
s << "<br>\r\n";
|
||||
}
|
||||
#endif
|
||||
auto family = i2p::context.GetFamily ();
|
||||
if (family.length () > 0)
|
||||
s << "<b>"<< tr("Family") << ":</b> " << family << "<br>\r\n";
|
||||
|
|
@ -681,7 +657,7 @@ namespace http {
|
|||
else
|
||||
{
|
||||
ls.reset (new i2p::data::LeaseSet2 (storeType));
|
||||
ls->Update (leaseSet->GetBuffer(), leaseSet->GetBufferLen(), nullptr, false);
|
||||
ls->Update (leaseSet->GetBuffer(), leaseSet->GetBufferLen(), false);
|
||||
}
|
||||
if (!ls) return;
|
||||
s << "<div class=\"leaseset listitem";
|
||||
|
|
@ -725,7 +701,6 @@ namespace http {
|
|||
{
|
||||
s << "<b>" << tr("Tunnels") << ":</b><br>\r\n";
|
||||
s << "<b>" << tr("Queue size") << ":</b> " << i2p::tunnel::tunnels.GetQueueSize () << "<br>\r\n<br>\r\n";
|
||||
s << "<b>" << tr("TBM Queue size") << ":</b> " << i2p::tunnel::tunnels.GetTBMQueueSize () << "<br>\r\n<br>\r\n";
|
||||
|
||||
auto ExplPool = i2p::tunnel::tunnels.GetExploratoryPool ();
|
||||
|
||||
|
|
@ -850,7 +825,7 @@ namespace http {
|
|||
if (i2p::tunnel::tunnels.CountTransitTunnels())
|
||||
{
|
||||
s << "<b>" << tr("Transit Tunnels") << ":</b><br>\r\n";
|
||||
s << "<table><thead><th>⇒</th><th>ID</th><th>⇒</th><th>" << tr("Amount") << "</th><th>" << tr("Next") << "</th></thead><tbody class=\"tableitem\">";
|
||||
s << "<table><thead><th>⇒</th><th>ID</th><th>⇒</th><th>" << tr("Amount") << "</th></thead><tbody class=\"tableitem\">";
|
||||
for (const auto& it: i2p::tunnel::tunnels.GetTransitTunnels ())
|
||||
{
|
||||
if (std::dynamic_pointer_cast<i2p::tunnel::TransitTunnelGateway>(it))
|
||||
|
|
@ -860,7 +835,7 @@ namespace http {
|
|||
else
|
||||
s << "<tr><td>⇒</td><td>" << it->GetTunnelID () << "</td><td>⇒</td><td>";
|
||||
ShowTraffic(s, it->GetNumTransmittedBytes ());
|
||||
s << "</td><td>" << it->GetNextPeerName () << "</td></tr>\r\n";
|
||||
s << "</td></tr>\r\n";
|
||||
}
|
||||
s << "</tbody></table>\r\n";
|
||||
}
|
||||
|
|
@ -967,8 +942,7 @@ namespace http {
|
|||
for (auto& it: sam->GetSessions ())
|
||||
{
|
||||
auto& name = it.second->GetLocalDestination ()->GetNickname ();
|
||||
auto sam_id = i2p::data::ByteStreamToBase64 ((const uint8_t *)it.first.data (), it.first.length ()); // base64, becuase session name might be UTF-8
|
||||
s << "<div class=\"listitem\"><a href=\"" << webroot << "?page=" << HTTP_PAGE_SAM_SESSION << "&sam_id=" << sam_id << "\">";
|
||||
s << "<div class=\"listitem\"><a href=\"" << webroot << "?page=" << HTTP_PAGE_SAM_SESSION << "&sam_id=" << it.first << "\">";
|
||||
s << name << " (" << it.first << ")</a></div>\r\n" << std::endl;
|
||||
}
|
||||
s << "</div>\r\n";
|
||||
|
|
@ -980,26 +954,13 @@ namespace http {
|
|||
void ShowSAMSession (std::stringstream& s, const std::string& id)
|
||||
{
|
||||
auto sam = i2p::client::context.GetSAMBridge ();
|
||||
if (!sam)
|
||||
{
|
||||
if (!sam) {
|
||||
ShowError(s, tr("SAM disabled"));
|
||||
return;
|
||||
}
|
||||
if (id.empty ())
|
||||
{
|
||||
ShowError(s, tr("No sam_id"));
|
||||
return;
|
||||
}
|
||||
std::vector<uint8_t> sam_id(id.length ()); // id is in base64
|
||||
size_t l = i2p::data::Base64ToByteStream (id, sam_id.data (), sam_id.size ());
|
||||
if (!l)
|
||||
{
|
||||
ShowError(s, tr("Invalid sam_id"));
|
||||
return;
|
||||
}
|
||||
auto session = sam->FindSession ( { (const char *)sam_id.data (), l });
|
||||
if (!session)
|
||||
{
|
||||
|
||||
auto session = sam->FindSession (id);
|
||||
if (!session) {
|
||||
ShowError(s, tr("SAM session not found"));
|
||||
return;
|
||||
}
|
||||
|
|
@ -1011,18 +972,17 @@ namespace http {
|
|||
s << i2p::client::context.GetAddressBook ().ToAddress(ident) << "</a></div>\r\n";
|
||||
s << "<br>\r\n";
|
||||
s << "<b>" << tr("Streams") << ":</b><br>\r\n<div class=\"list\">\r\n";
|
||||
for (const auto& it: sam->ListSockets({ (const char *)sam_id.data (), l }))
|
||||
for (const auto& it: sam->ListSockets(id))
|
||||
{
|
||||
s << "<div class=\"listitem\">";
|
||||
switch (it->GetSocketType ())
|
||||
{
|
||||
case i2p::client::SAMSocketType::eSAMSocketTypeSession : s << "session"; break;
|
||||
case i2p::client::SAMSocketType::eSAMSocketTypeStream : s << "stream"; break;
|
||||
case i2p::client::SAMSocketType::eSAMSocketTypeAcceptor : s << "acceptor"; break;
|
||||
case i2p::client::SAMSocketType::eSAMSocketTypeForward : s << "forward"; break;
|
||||
case i2p::client::eSAMSocketTypeSession : s << "session"; break;
|
||||
case i2p::client::eSAMSocketTypeStream : s << "stream"; break;
|
||||
case i2p::client::eSAMSocketTypeAcceptor : s << "acceptor"; break;
|
||||
case i2p::client::eSAMSocketTypeForward : s << "forward"; break;
|
||||
default: s << "unknown"; break;
|
||||
}
|
||||
if (it->GetSocketType () != i2p::client::SAMSocketType::eSAMSocketTypeTerminated && it->GetSocket ().is_open ())
|
||||
s << " [" << it->GetSocket ().remote_endpoint() << "]";
|
||||
s << "</div>\r\n";
|
||||
}
|
||||
|
|
@ -1301,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;
|
||||
}
|
||||
}
|
||||
|
|
@ -1457,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 << "<b>" << tr("SUCCESS") << "</b>:<br>\r\n<form action=\"http://shx5vqsw7usdaunyzr2qmes2fq37oumybpudrd4jjj4e4vk4uusa.b32.i2p/add\" method=\"post\" rel=\"noreferrer\" target=\"_blank\">\r\n"
|
||||
"<textarea readonly name=\"record\" cols=\"80\" rows=\"10\">" << out.str () << "</textarea>\r\n<br>\r\n<br>\r\n"
|
||||
|
|
@ -1470,6 +1432,7 @@ namespace http {
|
|||
"<input type=\"submit\" value=\"" << tr("Submit") << "\">\r\n"
|
||||
"</form>\r\n<br>\r\n";
|
||||
delete[] signature;
|
||||
delete[] sig;
|
||||
}
|
||||
else
|
||||
s << "<b>" << tr("ERROR") << "</b>: " << tr("Domain can't end with .b32.i2p") << "\r\n<br>\r\n<br>\r\n";
|
||||
|
|
@ -1493,15 +1456,12 @@ namespace http {
|
|||
}
|
||||
else if (cmd == HTTP_COMMAND_RELOAD_CSS)
|
||||
{
|
||||
std::string theme; i2p::config::GetOption("http.theme", theme);
|
||||
|
||||
if (theme != "light" && theme != "black" && theme !="white") LoadExtCSS(theme);
|
||||
else LoadExtCSS();
|
||||
LoadExtCSS();
|
||||
}
|
||||
else
|
||||
{
|
||||
res.code = 400;
|
||||
ShowError(s, std::string (tr("Unknown command")) + ": " + cmd); // TODO
|
||||
ShowError(s, tr("Unknown command") + ": " + cmd);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -1520,13 +1480,13 @@ namespace http {
|
|||
reply.body = content;
|
||||
|
||||
m_SendBuffer = reply.to_string();
|
||||
boost::asio::async_write (*m_Socket, boost::asio::buffer(m_SendBuffer), boost::asio::transfer_all (),
|
||||
boost::asio::async_write (*m_Socket, boost::asio::buffer(m_SendBuffer),
|
||||
std::bind (&HTTPConnection::Terminate, shared_from_this (), std::placeholders::_1));
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
}
|
||||
|
|
|
|||
|
|
@ -83,8 +83,8 @@ namespace http
|
|||
|
||||
bool m_IsRunning;
|
||||
std::unique_ptr<std::thread> m_Thread;
|
||||
boost::asio::io_context m_Service;
|
||||
boost::asio::executor_work_guard<boost::asio::io_context::executor_type> 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;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ namespace http
|
|||
".menu { display: block; float: left; overflow: hidden; padding: 4px; max-width: 12em; white-space: nowrap; text-overflow: ellipsis ;}\r\n"
|
||||
".listitem { display: block; font-family: monospace; font-size: 1.2em; white-space: nowrap; }\r\n"
|
||||
".tableitem { font-family: monospace; font-size: 1.2em; white-space: nowrap; }\r\n"
|
||||
".content { font-size: 1em; margin-left: 2em; padding: 4px; max-width: 50em; overflow: auto; position: absolute; left: 35%; }\r\n"
|
||||
".content { float: left; font-size: 1em; margin-left: 2em; padding: 4px; max-width: 50em; overflow: auto; }\r\n"
|
||||
".tunnel.established { color: #56B734; }\r\n"
|
||||
".tunnel.expiring { color: #D3AE3F; }\r\n"
|
||||
".tunnel.failed { color: #D33F3F; }\r\n"
|
||||
|
|
@ -73,7 +73,7 @@ namespace http
|
|||
"@media screen and (max-width: 980px) { body { font: 100%/1.2em sans-serif; padding: 1.2em 0 0 0; }\r\n"
|
||||
" .menu { width: 100%; max-width: unset; display: block; float: none; position: unset; font-size: 16px; text-align: center; }\r\n"
|
||||
" .menu a, .commands a { display: inline-block; padding: 4px; }\r\n"
|
||||
" .content { float: none; margin-left: unset; margin-top: 16px; max-width: 100%; width: 100%; text-align: center; position: absolute; left: 0; }\r\n"
|
||||
" .content { float: none; margin-left: unset; margin-top: 16px; max-width: 100%; width: 100%; text-align: center; }\r\n"
|
||||
" a, .slide label { display: block; }\r\n"
|
||||
" .header { margin: unset; font-size: 1.5em; }\r\n"
|
||||
" small { display: block; }\r\n"
|
||||
|
|
|
|||
|
|
@ -1,220 +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
|
||||
*/
|
||||
|
||||
#if defined(__HAIKU__)
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <MenuItem.h>
|
||||
#include <MenuBar.h>
|
||||
#include <StringView.h>
|
||||
#include <Font.h>
|
||||
#include <MessageRunner.h>
|
||||
#include <Window.h>
|
||||
#include <Application.h>
|
||||
#include <Alert.h>
|
||||
|
||||
#include "version.h"
|
||||
#include "Log.h"
|
||||
#include "Config.h"
|
||||
#include "RouterContext.h"
|
||||
#include "Tunnel.h"
|
||||
#include "Transports.h"
|
||||
#include "Daemon.h"
|
||||
#include "ClientContext.h"
|
||||
|
||||
enum
|
||||
{
|
||||
M_GRACEFUL_SHUTDOWN = 1,
|
||||
M_RUN_PEER_TEST,
|
||||
M_DUMMY_COMMAND,
|
||||
C_GRACEFUL_SHUTDOWN_UPDATE,
|
||||
C_MAIN_VIEW_UPDATE
|
||||
};
|
||||
constexpr bigtime_t GRACEFUL_SHUTDOWN_UPDATE_INTERVAL = 1000*1100; // in microseconds, ~ 1 sec
|
||||
constexpr int GRACEFUL_SHUTDOWN_UPDATE_COUNT = 600; // 10 minutes
|
||||
constexpr bigtime_t MAIN_VIEW_UPDATE_INTERVAL = 5000*1000; // in miscroseconds, 5 secs
|
||||
|
||||
class MainWindow: public BWindow
|
||||
{
|
||||
public:
|
||||
MainWindow ();
|
||||
|
||||
private:
|
||||
void MessageReceived (BMessage * msg) override;
|
||||
bool QuitRequested () override;
|
||||
|
||||
void UpdateMainView ();
|
||||
|
||||
private:
|
||||
BMessenger m_Messenger;
|
||||
BStringView * m_MainView;
|
||||
std::unique_ptr<BMessageRunner> m_MainViewUpdateTimer, m_GracefulShutdownTimer;
|
||||
bool m_IsGracefulShutdownComplete = false;
|
||||
};
|
||||
|
||||
class I2PApp: public BApplication
|
||||
{
|
||||
public:
|
||||
|
||||
I2PApp ();
|
||||
void CreateMainWindow ();
|
||||
};
|
||||
|
||||
MainWindow::MainWindow ():
|
||||
BWindow (BRect(100, 100, 500, 400), "i2pd " VERSION, B_TITLED_WINDOW, B_QUIT_ON_WINDOW_CLOSE),
|
||||
m_Messenger (nullptr, this)
|
||||
{
|
||||
auto r = Bounds (); r.bottom = 20;
|
||||
auto menuBar = new BMenuBar (r, "menubar");
|
||||
AddChild (menuBar);
|
||||
auto runMenu = new BMenu ("Run");
|
||||
runMenu->AddItem (new BMenuItem ("Graceful shutdown", new BMessage (M_GRACEFUL_SHUTDOWN), 'G'));
|
||||
runMenu->AddItem (new BMenuItem ("Quit", new BMessage (B_QUIT_REQUESTED), 'Q'));
|
||||
menuBar->AddItem (runMenu);
|
||||
auto commandsMenu = new BMenu ("Commands");
|
||||
commandsMenu->AddItem (new BMenuItem ("Run peer test", new BMessage (M_RUN_PEER_TEST), 'P'));
|
||||
menuBar->AddItem (commandsMenu);
|
||||
auto tunnelsMenu = new BMenu ("Tunnels");
|
||||
for (auto& it: i2p::client::context.GetClientTunnels ())
|
||||
tunnelsMenu->AddItem (new BMenuItem (it.second->GetName (), new BMessage (M_DUMMY_COMMAND)));
|
||||
for (auto& it: i2p::client::context.GetServerTunnels ())
|
||||
tunnelsMenu->AddItem (new BMenuItem (it.second->GetName (), new BMessage (M_DUMMY_COMMAND)));
|
||||
menuBar->AddItem (tunnelsMenu);
|
||||
m_MainView = new BStringView (BRect (20, 21, 300, 250), nullptr, "Starting...", B_FOLLOW_ALL, B_WILL_DRAW);
|
||||
m_MainView->SetViewColor (255, 255, 255);
|
||||
m_MainView->SetHighColor (0xD4, 0x3B, 0x69);
|
||||
BFont font = *be_plain_font;
|
||||
font.SetSize (12);
|
||||
m_MainView->SetFont (&font);
|
||||
AddChild (m_MainView);
|
||||
m_MainViewUpdateTimer = std::make_unique<BMessageRunner>(m_Messenger,
|
||||
BMessage (C_MAIN_VIEW_UPDATE), MAIN_VIEW_UPDATE_INTERVAL);
|
||||
}
|
||||
|
||||
void MainWindow::UpdateMainView ()
|
||||
{
|
||||
std::stringstream s;
|
||||
i2p::util::PrintMainWindowText (s);
|
||||
m_MainView->SetText (s.str ().c_str ());
|
||||
}
|
||||
|
||||
void MainWindow::MessageReceived (BMessage * msg)
|
||||
{
|
||||
if (!msg) return;
|
||||
switch (msg->what)
|
||||
{
|
||||
case C_MAIN_VIEW_UPDATE:
|
||||
UpdateMainView ();
|
||||
break;
|
||||
case M_GRACEFUL_SHUTDOWN:
|
||||
if (!m_GracefulShutdownTimer)
|
||||
{
|
||||
i2p::context.SetAcceptsTunnels (false);
|
||||
Daemon.gracefulShutdownInterval = GRACEFUL_SHUTDOWN_UPDATE_COUNT;
|
||||
m_MainViewUpdateTimer = nullptr;
|
||||
m_GracefulShutdownTimer = std::make_unique<BMessageRunner>(m_Messenger,
|
||||
BMessage (C_GRACEFUL_SHUTDOWN_UPDATE), GRACEFUL_SHUTDOWN_UPDATE_INTERVAL);
|
||||
}
|
||||
break;
|
||||
case C_GRACEFUL_SHUTDOWN_UPDATE:
|
||||
if (Daemon.gracefulShutdownInterval > 0)
|
||||
{
|
||||
UpdateMainView ();
|
||||
Daemon.gracefulShutdownInterval--;
|
||||
}
|
||||
if (!Daemon.gracefulShutdownInterval || i2p::tunnel::tunnels.CountTransitTunnels () <= 0)
|
||||
{
|
||||
m_GracefulShutdownTimer = nullptr;
|
||||
Daemon.gracefulShutdownInterval = 0;
|
||||
m_IsGracefulShutdownComplete = true;
|
||||
m_Messenger.SendMessage (B_QUIT_REQUESTED);
|
||||
}
|
||||
break;
|
||||
case M_RUN_PEER_TEST:
|
||||
i2p::transport::transports.PeerTest ();
|
||||
break;
|
||||
default:
|
||||
BWindow::MessageReceived (msg);
|
||||
}
|
||||
}
|
||||
|
||||
bool MainWindow::QuitRequested ()
|
||||
{
|
||||
bool isQuit = true;
|
||||
if (!m_IsGracefulShutdownComplete)
|
||||
{
|
||||
auto alert = new BAlert (nullptr, "This will stop i2pd. Are you sure?", "Cancel", "Yes", "No",
|
||||
B_WIDTH_AS_USUAL, B_OFFSET_SPACING, B_WARNING_ALERT);
|
||||
alert->SetShortcut (0, B_ESCAPE);
|
||||
isQuit = alert->Go () == 1; // Yes
|
||||
}
|
||||
if (isQuit)
|
||||
{
|
||||
m_MainViewUpdateTimer = nullptr;
|
||||
m_GracefulShutdownTimer = nullptr;
|
||||
}
|
||||
return isQuit;
|
||||
}
|
||||
|
||||
I2PApp::I2PApp (): BApplication("application/x-vnd.purplei2p-i2pd")
|
||||
{
|
||||
}
|
||||
|
||||
void I2PApp::CreateMainWindow ()
|
||||
{
|
||||
auto mainWindow = new MainWindow ();
|
||||
mainWindow->Show ();
|
||||
}
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace util
|
||||
{
|
||||
bool DaemonHaiku::start ()
|
||||
{
|
||||
I2PApp * app = nullptr;
|
||||
if (!isDaemon)
|
||||
{
|
||||
app = new I2PApp(); // set be_app
|
||||
i2p::log::SetThrowFunction ([](const std::string& s)
|
||||
{
|
||||
auto alert = new BAlert (nullptr, s.c_str (), "Quit", nullptr, nullptr,
|
||||
B_WIDTH_AS_USUAL, B_OFFSET_SPACING, B_STOP_ALERT);
|
||||
alert->Go ();
|
||||
});
|
||||
}
|
||||
bool ret = false;
|
||||
if (app)
|
||||
{
|
||||
ret = Daemon_Singleton::start ();
|
||||
if (ret)
|
||||
app->CreateMainWindow ();
|
||||
}
|
||||
else
|
||||
ret = DaemonUnix::start ();
|
||||
return ret;
|
||||
}
|
||||
|
||||
void DaemonHaiku::run ()
|
||||
{
|
||||
if (be_app)
|
||||
{
|
||||
be_app->Run ();
|
||||
delete be_app;
|
||||
}
|
||||
else
|
||||
DaemonUnix::run ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
|
@ -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
|
||||
*
|
||||
|
|
@ -8,13 +8,16 @@
|
|||
|
||||
#include <stdio.h>
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
#include <openssl/x509.h>
|
||||
#include <openssl/pem.h>
|
||||
|
||||
// Use global placeholders from boost introduced when local_time.hpp is loaded
|
||||
#define BOOST_BIND_GLOBAL_PLACEHOLDERS
|
||||
|
||||
#include <boost/date_time/local_time/local_time.hpp>
|
||||
#include <boost/date_time/posix_time/posix_time.hpp>
|
||||
#include <boost/property_tree/json_parser.hpp>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
|
||||
#include "FS.h"
|
||||
#include "Log.h"
|
||||
|
|
@ -29,24 +32,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<boost::asio::ip::tcp::acceptor>(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<boost::asio::local::stream_protocol::acceptor>(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 +47,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.set_options (boost::asio::ssl::context::default_workarounds | boost::asio::ssl::context::no_sslv2 | boost::asio::ssl::context::single_dh_use);
|
||||
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 +86,7 @@ namespace client
|
|||
{
|
||||
Accept ();
|
||||
m_IsRunning = true;
|
||||
m_Thread = std::make_unique<std::thread>(std::bind (&I2PControlService::Run, this));
|
||||
m_Thread = new std::thread (std::bind (&I2PControlService::Run, this));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -119,19 +95,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 +122,40 @@ namespace client
|
|||
|
||||
void I2PControlService::Accept ()
|
||||
{
|
||||
if (m_Acceptor)
|
||||
{
|
||||
auto newSocket = std::make_shared<boost::asio::ssl::stream<boost::asio::ip::tcp::socket> > (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<boost::asio::ssl::stream<boost::asio::local::stream_protocol::socket> > (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<ssl_socket> (m_Service, m_SSLContext);
|
||||
m_Acceptor.async_accept (newSocket->lowest_layer(), std::bind (&I2PControlService::HandleAccept, this,
|
||||
std::placeholders::_1, newSocket));
|
||||
}
|
||||
|
||||
template<typename ssl_socket>
|
||||
void I2PControlService::HandleAccepted (const boost::system::error_code& ecode,
|
||||
std::shared_ptr<ssl_socket> newSocket)
|
||||
void I2PControlService::HandleAccept(const boost::system::error_code& ecode, std::shared_ptr<ssl_socket> 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);
|
||||
LogPrint (eLogDebug, "I2PControl: New request from ", socket->lowest_layer ().remote_endpoint ());
|
||||
Handshake (socket);
|
||||
}
|
||||
|
||||
template<typename ssl_socket>
|
||||
void I2PControlService::Handshake (std::shared_ptr<ssl_socket> socket)
|
||||
{
|
||||
socket->async_handshake(boost::asio::ssl::stream_base::server,
|
||||
[this, socket](const boost::system::error_code& ecode)
|
||||
{
|
||||
if (ecode)
|
||||
std::bind( &I2PControlService::HandleHandshake, this, std::placeholders::_1, socket));
|
||||
}
|
||||
|
||||
void I2PControlService::HandleHandshake (const boost::system::error_code& ecode, std::shared_ptr<ssl_socket> socket)
|
||||
{
|
||||
if (ecode) {
|
||||
LogPrint (eLogError, "I2PControl: Handshake error: ", ecode.message ());
|
||||
return;
|
||||
}
|
||||
//std::this_thread::sleep_for (std::chrono::milliseconds(5));
|
||||
ReadRequest (socket);
|
||||
});
|
||||
}
|
||||
|
||||
template<typename ssl_socket>
|
||||
void I2PControlService::ReadRequest (std::shared_ptr<ssl_socket> socket)
|
||||
{
|
||||
auto request = std::make_shared<I2PControlBuffer>();
|
||||
|
|
@ -216,13 +165,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<typename ssl_socket>
|
||||
void I2PControlService::HandleRequestReceived (const boost::system::error_code& ecode,
|
||||
size_t bytes_transferred, std::shared_ptr<ssl_socket> socket,
|
||||
std::shared_ptr<I2PControlBuffer> buf)
|
||||
|
|
@ -300,7 +246,6 @@ namespace client
|
|||
}
|
||||
}
|
||||
|
||||
template<typename ssl_socket>
|
||||
void I2PControlService::SendResponse (std::shared_ptr<ssl_socket> socket,
|
||||
std::shared_ptr<I2PControlBuffer> buf, std::ostringstream& response, bool isHtml)
|
||||
{
|
||||
|
|
@ -310,12 +255,12 @@ 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<std::string>(len) << "\r\n";
|
||||
header << "Content-Type: application/json\r\n";
|
||||
header << "Date: ";
|
||||
std::time_t t = std::time (nullptr);
|
||||
std::tm tm = *std::gmtime (&t);
|
||||
header << std::put_time(&tm, "%a, %d %b %Y %T GMT") << "\r\n";
|
||||
auto facet = new boost::local_time::local_time_facet ("%a, %d %b %Y %H:%M:%S GMT");
|
||||
header.imbue(std::locale (header.getloc(), facet));
|
||||
header << boost::posix_time::second_clock::local_time() << "\r\n";
|
||||
header << "\r\n";
|
||||
offset = header.str ().size ();
|
||||
memcpy (buf->data (), header.str ().c_str (), offset);
|
||||
|
|
@ -323,11 +268,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)
|
||||
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<ssl_socket> socket, std::shared_ptr<I2PControlBuffer> buf)
|
||||
{
|
||||
if (ecode)
|
||||
if (ecode) {
|
||||
LogPrint (eLogError, "I2PControl: Write error: ", ecode.message ());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// handlers
|
||||
|
|
@ -437,22 +387,14 @@ namespace client
|
|||
void I2PControlService::CreateCertificate (const char *crt_path, const char *key_path)
|
||||
{
|
||||
FILE *f = NULL;
|
||||
#if (OPENSSL_VERSION_NUMBER >= 0x030000000) // since 3.0.0
|
||||
EVP_PKEY * pkey = EVP_RSA_gen(4096); // e = 65537
|
||||
#else
|
||||
EVP_PKEY * pkey = EVP_PKEY_new ();
|
||||
RSA * rsa = RSA_new ();
|
||||
BIGNUM * e = BN_dup (i2p::crypto::GetRSAE ());
|
||||
RSA_generate_key_ex (rsa, 4096, e, NULL);
|
||||
BN_free (e);
|
||||
if (rsa) EVP_PKEY_assign_RSA (pkey, rsa);
|
||||
else
|
||||
if (rsa)
|
||||
{
|
||||
LogPrint (eLogError, "I2PControl: Can't create RSA key for certificate");
|
||||
EVP_PKEY_free (pkey);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
EVP_PKEY_assign_RSA (pkey, rsa);
|
||||
X509 * x509 = X509_new ();
|
||||
ASN1_INTEGER_set (X509_get_serialNumber (x509), 1);
|
||||
X509_gmtime_adj (X509_getm_notBefore (x509), 0);
|
||||
|
|
@ -463,28 +405,30 @@ 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)
|
||||
{
|
||||
if ((f = fopen (crt_path, "wb")) != NULL) {
|
||||
LogPrint (eLogInfo, "I2PControl: Saving new cert to ", crt_path);
|
||||
PEM_write_X509 (f, x509);
|
||||
fclose (f);
|
||||
}
|
||||
else
|
||||
} else {
|
||||
LogPrint (eLogError, "I2PControl: Can't write cert: ", strerror(errno));
|
||||
X509_free (x509);
|
||||
}
|
||||
|
||||
// save key
|
||||
if ((f = fopen (key_path, "wb")) != NULL)
|
||||
{
|
||||
if ((f = fopen (key_path, "wb")) != NULL) {
|
||||
LogPrint (eLogInfo, "I2PControl: saving cert key to ", key_path);
|
||||
PEM_write_PrivateKey (f, pkey, NULL, NULL, 0, NULL, NULL);
|
||||
fclose (f);
|
||||
}
|
||||
else
|
||||
} else {
|
||||
LogPrint (eLogError, "I2PControl: Can't write key: ", strerror(errno));
|
||||
}
|
||||
|
||||
X509_free (x509);
|
||||
} else {
|
||||
LogPrint (eLogError, "I2PControl: Can't create RSA key for certificate");
|
||||
}
|
||||
EVP_PKEY_free (pkey);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<boost::asio::ip::tcp::socket> ssl_socket;
|
||||
|
||||
public:
|
||||
|
||||
I2PControlService (const std::string& address, int port);
|
||||
|
|
@ -47,18 +49,16 @@ namespace client
|
|||
|
||||
void Run ();
|
||||
void Accept ();
|
||||
template<typename ssl_socket>
|
||||
void HandleAccepted (const boost::system::error_code& ecode, std::shared_ptr<ssl_socket> newSocket);
|
||||
template<typename ssl_socket>
|
||||
void HandleAccept(const boost::system::error_code& ecode, std::shared_ptr<ssl_socket> socket);
|
||||
void Handshake (std::shared_ptr<ssl_socket> socket);
|
||||
template<typename ssl_socket>
|
||||
void HandleHandshake (const boost::system::error_code& ecode, std::shared_ptr<ssl_socket> socket);
|
||||
void ReadRequest (std::shared_ptr<ssl_socket> socket);
|
||||
template<typename ssl_socket>
|
||||
void HandleRequestReceived (const boost::system::error_code& ecode, size_t bytes_transferred,
|
||||
std::shared_ptr<ssl_socket> socket, std::shared_ptr<I2PControlBuffer> buf);
|
||||
template<typename ssl_socket>
|
||||
void SendResponse (std::shared_ptr<ssl_socket> socket,
|
||||
std::shared_ptr<I2PControlBuffer> buf, std::ostringstream& response, bool isHtml);
|
||||
void HandleResponseSent (const boost::system::error_code& ecode, std::size_t bytes_transferred,
|
||||
std::shared_ptr<ssl_socket> socket, std::shared_ptr<I2PControlBuffer> 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<std::thread> m_Thread;
|
||||
std::thread * m_Thread;
|
||||
|
||||
boost::asio::io_context m_Service;
|
||||
std::unique_ptr<boost::asio::ip::tcp::acceptor> m_Acceptor;
|
||||
#if defined(BOOST_ASIO_HAS_LOCAL_SOCKETS)
|
||||
std::unique_ptr<boost::asio::local::stream_protocol::acceptor> 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<std::string> m_Tokens;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
*
|
||||
|
|
@ -37,7 +37,6 @@ namespace client
|
|||
m_RouterInfoHandlers["i2p.router.net.bw.outbound.1s"] = &I2PControlHandlers::OutboundBandwidth1S;
|
||||
m_RouterInfoHandlers["i2p.router.net.bw.outbound.15s"] = &I2PControlHandlers::OutboundBandwidth15S;
|
||||
m_RouterInfoHandlers["i2p.router.net.status"] = &I2PControlHandlers::NetStatusHandler;
|
||||
m_RouterInfoHandlers["i2p.router.net.status.v6"] = &I2PControlHandlers::NetStatusV6Handler;
|
||||
m_RouterInfoHandlers["i2p.router.net.tunnels.participating"] = &I2PControlHandlers::TunnelsParticipatingHandler;
|
||||
m_RouterInfoHandlers["i2p.router.net.tunnels.successrate"] = &I2PControlHandlers::TunnelsSuccessRateHandler;
|
||||
m_RouterInfoHandlers["i2p.router.net.total.received.bytes"] = &I2PControlHandlers::NetTotalReceivedBytes;
|
||||
|
|
@ -138,11 +137,6 @@ namespace client
|
|||
InsertParam (results, "i2p.router.net.status", (int)i2p::context.GetStatus ());
|
||||
}
|
||||
|
||||
void I2PControlHandlers::NetStatusV6Handler (std::ostringstream& results)
|
||||
{
|
||||
InsertParam (results, "i2p.router.net.status.v6", (int)i2p::context.GetStatusV6 ());
|
||||
}
|
||||
|
||||
void I2PControlHandlers::TunnelsParticipatingHandler (std::ostringstream& results)
|
||||
{
|
||||
int transit = i2p::tunnel::tunnels.GetTransitTunnels ().size ();
|
||||
|
|
@ -346,7 +340,7 @@ namespace client
|
|||
for (const auto& socket: sam->ListSockets(it.first))
|
||||
{
|
||||
boost::property_tree::ptree stream;
|
||||
stream.put("type", (int)socket->GetSocketType ());
|
||||
stream.put("type", socket->GetSocketType ());
|
||||
stream.put("peer", socket->GetSocket ().remote_endpoint());
|
||||
|
||||
sam_session_sockets.push_back(std::make_pair("", stream));
|
||||
|
|
|
|||
|
|
@ -47,7 +47,6 @@ namespace client
|
|||
void NetDbKnownPeersHandler (std::ostringstream& results);
|
||||
void NetDbActivePeersHandler (std::ostringstream& results);
|
||||
void NetStatusHandler (std::ostringstream& results);
|
||||
void NetStatusV6Handler (std::ostringstream& results);
|
||||
void TunnelsParticipatingHandler (std::ostringstream& results);
|
||||
void TunnelsSuccessRateHandler (std::ostringstream& results);
|
||||
void InboundBandwidth1S (std::ostringstream& results);
|
||||
|
|
|
|||
|
|
@ -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<std::mutex> 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 ();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -67,7 +67,7 @@ namespace transport
|
|||
std::unique_ptr<std::thread> 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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
*
|
||||
|
|
@ -25,7 +25,6 @@
|
|||
#include "RouterContext.h"
|
||||
#include "ClientContext.h"
|
||||
#include "Transports.h"
|
||||
#include "util.h"
|
||||
|
||||
void handle_signal(int sig)
|
||||
{
|
||||
|
|
@ -71,7 +70,7 @@ namespace i2p
|
|||
{
|
||||
namespace util
|
||||
{
|
||||
bool DaemonUnix::start()
|
||||
bool DaemonLinux::start()
|
||||
{
|
||||
if (isDaemon)
|
||||
{
|
||||
|
|
@ -213,15 +212,14 @@ namespace i2p
|
|||
return Daemon_Singleton::start();
|
||||
}
|
||||
|
||||
bool DaemonUnix::stop()
|
||||
bool DaemonLinux::stop()
|
||||
{
|
||||
i2p::fs::Remove(pidfile);
|
||||
return Daemon_Singleton::stop();
|
||||
}
|
||||
|
||||
void DaemonUnix::run ()
|
||||
void DaemonLinux::run ()
|
||||
{
|
||||
i2p::util::SetThreadName ("i2pd-daemon");
|
||||
while (running)
|
||||
{
|
||||
std::this_thread::sleep_for (std::chrono::seconds(1));
|
||||
|
|
|
|||
30
debian/changelog
vendored
30
debian/changelog
vendored
|
|
@ -1,33 +1,3 @@
|
|||
i2pd (2.58.0-1) unstable; urgency=medium
|
||||
|
||||
* updated to version 2.58.0/0.9.67
|
||||
|
||||
-- orignal <orignal@i2pmail.org> Mon, 08 Sep 2025 16:00:00 +0000
|
||||
|
||||
i2pd (2.57.0-1) unstable; urgency=medium
|
||||
|
||||
* updated to version 2.57.0/0.9.66
|
||||
|
||||
-- orignal <orignal@i2pmail.org> Mon, 02 Jun 2025 16:00:00 +0000
|
||||
|
||||
i2pd (2.56.0-1) unstable; urgency=medium
|
||||
|
||||
* updated to version 2.56.0/0.9.65
|
||||
|
||||
-- orignal <orignal@i2pmail.org> Tue, 11 Feb 2025 16:00:00 +0000
|
||||
|
||||
i2pd (2.55.0-1) unstable; urgency=medium
|
||||
|
||||
* updated to version 2.55.0
|
||||
|
||||
-- orignal <orignal@i2pmail.org> 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 <orignal@i2pmail.org> Sun, 6 Oct 2024 16:00:00 +0000
|
||||
|
||||
i2pd (2.53.1-1) unstable; urgency=medium
|
||||
|
||||
* updated to version 2.53.1
|
||||
|
|
|
|||
6
debian/patches/01-upnp.patch
vendored
6
debian/patches/01-upnp.patch
vendored
|
|
@ -2,13 +2,13 @@ Description: Enable UPnP usage in package
|
|||
Author: r4sas <r4sas@i2pmail.org>
|
||||
|
||||
Reviewed-By: r4sas <r4sas@i2pmail.org>
|
||||
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)
|
||||
|
|
|
|||
|
|
@ -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,10 +29,7 @@ namespace afrikaans // language namespace
|
|||
return n != 1 ? 1 : 0;
|
||||
}
|
||||
|
||||
// Right to Left language?
|
||||
static bool rtl = false;
|
||||
|
||||
static const LocaleStrings strings
|
||||
static std::map<std::string, std::string> strings
|
||||
{
|
||||
{"failed", "Het misluk"},
|
||||
{"unknown", "onbekend"},
|
||||
|
|
@ -76,7 +73,7 @@ namespace afrikaans // language namespace
|
|||
|
||||
std::shared_ptr<const i2p::i18n::Locale> GetLocale()
|
||||
{
|
||||
return std::make_shared<i2p::i18n::Locale>(language, rtl, strings, plurals, [] (int n)->int { return plural(n); });
|
||||
return std::make_shared<i2p::i18n::Locale>(language, strings, plurals, [] (int n)->int { return plural(n); });
|
||||
}
|
||||
|
||||
} // language
|
||||
|
|
|
|||
|
|
@ -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,10 +29,7 @@ namespace armenian // language namespace
|
|||
return n != 1 ? 1 : 0;
|
||||
}
|
||||
|
||||
// Right to Left language?
|
||||
static bool rtl = false;
|
||||
|
||||
static const LocaleStrings strings
|
||||
static std::map<std::string, std::string> strings
|
||||
{
|
||||
{"%.2f KiB", "%.2f ԿիԲ"},
|
||||
{"%.2f MiB", "%.2f ՄիԲ"},
|
||||
|
|
@ -199,7 +196,7 @@ namespace armenian // language namespace
|
|||
|
||||
std::shared_ptr<const i2p::i18n::Locale> GetLocale()
|
||||
{
|
||||
return std::make_shared<i2p::i18n::Locale>(language, rtl, strings, plurals, [] (int n)->int { return plural(n); });
|
||||
return std::make_shared<i2p::i18n::Locale>(language, strings, plurals, [] (int n)->int { return plural(n); });
|
||||
}
|
||||
|
||||
} // language
|
||||
|
|
|
|||
|
|
@ -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,10 +29,7 @@ namespace chinese // language namespace
|
|||
return 0;
|
||||
}
|
||||
|
||||
// Right to Left language?
|
||||
static bool rtl = false;
|
||||
|
||||
static const LocaleStrings strings
|
||||
static std::map<std::string, std::string> strings
|
||||
{
|
||||
{"%.2f KiB", "%.2f KiB"},
|
||||
{"%.2f MiB", "%.2f MiB"},
|
||||
|
|
@ -67,7 +64,7 @@ namespace chinese // language namespace
|
|||
{"Full cone NAT", "全锥型NAT"},
|
||||
{"No Descriptors", "无描述符"},
|
||||
{"Uptime", "运行时间"},
|
||||
{"Network status", "网络状态"},
|
||||
{"Network status", "IPv4 网络状态"},
|
||||
{"Network status v6", "IPv6 网络状态"},
|
||||
{"Stopping in", "距停止还有:"},
|
||||
{"Family", "家族"},
|
||||
|
|
@ -218,7 +215,7 @@ namespace chinese // language namespace
|
|||
|
||||
std::shared_ptr<const i2p::i18n::Locale> GetLocale()
|
||||
{
|
||||
return std::make_shared<i2p::i18n::Locale>(language, rtl, strings, plurals, [] (int n)->int { return plural(n); });
|
||||
return std::make_shared<i2p::i18n::Locale>(language, strings, plurals, [] (int n)->int { return plural(n); });
|
||||
}
|
||||
|
||||
} // language
|
||||
|
|
|
|||
|
|
@ -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,10 +29,7 @@ namespace czech // language namespace
|
|||
return (n == 1) ? 0 : (n >= 2 && n <= 4) ? 1 : 2;
|
||||
}
|
||||
|
||||
// Right to Left language?
|
||||
static bool rtl = false;
|
||||
|
||||
static const LocaleStrings strings
|
||||
static std::map<std::string, std::string> strings
|
||||
{
|
||||
{"%.2f KiB", "%.2f KiB"},
|
||||
{"%.2f MiB", "%.2f MiB"},
|
||||
|
|
@ -218,7 +215,7 @@ namespace czech // language namespace
|
|||
|
||||
std::shared_ptr<const i2p::i18n::Locale> GetLocale()
|
||||
{
|
||||
return std::make_shared<i2p::i18n::Locale>(language, rtl, strings, plurals, [] (int n)->int { return plural(n); });
|
||||
return std::make_shared<i2p::i18n::Locale>(language, strings, plurals, [] (int n)->int { return plural(n); });
|
||||
}
|
||||
|
||||
} // language
|
||||
|
|
|
|||
|
|
@ -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,10 +30,7 @@ namespace english // language namespace
|
|||
return n != 1 ? 1 : 0;
|
||||
}
|
||||
|
||||
// Right to Left language?
|
||||
static bool rtl = false;
|
||||
|
||||
static const LocaleStrings strings
|
||||
static std::map<std::string, std::string> strings
|
||||
{
|
||||
{"", ""},
|
||||
};
|
||||
|
|
@ -45,7 +42,7 @@ namespace english // language namespace
|
|||
|
||||
std::shared_ptr<const i2p::i18n::Locale> GetLocale()
|
||||
{
|
||||
return std::make_shared<i2p::i18n::Locale>(language, rtl, strings, plurals, [] (int n)->int { return plural(n); });
|
||||
return std::make_shared<i2p::i18n::Locale>(language, strings, plurals, [] (int n)->int { return plural(n); });
|
||||
}
|
||||
|
||||
} // language
|
||||
|
|
|
|||
|
|
@ -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,10 +29,7 @@ namespace french // language namespace
|
|||
return n != 1 ? 1 : 0;
|
||||
}
|
||||
|
||||
// Right to Left language?
|
||||
static bool rtl = false;
|
||||
|
||||
static const LocaleStrings strings
|
||||
static std::map<std::string, std::string> strings
|
||||
{
|
||||
{"%.2f KiB", "%.2f Kio"},
|
||||
{"%.2f MiB", "%.2f Mio"},
|
||||
|
|
@ -47,7 +44,7 @@ namespace french // language namespace
|
|||
{"<b>i2pd</b> webconsole", "Console web <b>i2pd</b>"},
|
||||
{"Main page", "Page principale"},
|
||||
{"Router commands", "Commandes du routeur"},
|
||||
{"Local Destinations", "Destinatioans localeAlger"},
|
||||
{"Local Destinations", "Destinations locales"},
|
||||
{"LeaseSets", "Jeu de baux"},
|
||||
{"Tunnels", "Tunnels"},
|
||||
{"Transit Tunnels", "Tunnels transitoires"},
|
||||
|
|
@ -218,7 +215,7 @@ namespace french // language namespace
|
|||
|
||||
std::shared_ptr<const i2p::i18n::Locale> GetLocale()
|
||||
{
|
||||
return std::make_shared<i2p::i18n::Locale>(language, rtl, strings, plurals, [] (int n)->int { return plural(n); });
|
||||
return std::make_shared<i2p::i18n::Locale>(language, strings, plurals, [] (int n)->int { return plural(n); });
|
||||
}
|
||||
|
||||
} // language
|
||||
|
|
|
|||
|
|
@ -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,10 +29,7 @@ namespace german // language namespace
|
|||
return n != 1 ? 1 : 0;
|
||||
}
|
||||
|
||||
// Right to Left language?
|
||||
static bool rtl = false;
|
||||
|
||||
static const LocaleStrings strings
|
||||
static std::map<std::string, std::string> strings
|
||||
{
|
||||
{"%.2f KiB", "%.2f KiB"},
|
||||
{"%.2f MiB", "%.2f MiB"},
|
||||
|
|
@ -213,7 +210,7 @@ namespace german // language namespace
|
|||
|
||||
std::shared_ptr<const i2p::i18n::Locale> GetLocale()
|
||||
{
|
||||
return std::make_shared<i2p::i18n::Locale>(language, rtl, strings, plurals, [] (int n)->int { return plural(n); });
|
||||
return std::make_shared<i2p::i18n::Locale>(language, strings, plurals, [] (int n)->int { return plural(n); });
|
||||
}
|
||||
|
||||
} // language
|
||||
|
|
|
|||
202
i18n/Hebrew.cpp
202
i18n/Hebrew.cpp
|
|
@ -1,202 +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 <map>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include "I18N.h"
|
||||
|
||||
// Hebrew localization file
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace i18n
|
||||
{
|
||||
namespace hebrew // language namespace
|
||||
{
|
||||
// language name in lowercase
|
||||
static std::string language = "hebrew";
|
||||
|
||||
// See for language plural forms here:
|
||||
// https://localization-guide.readthedocs.io/en/latest/l10n/pluralforms.html
|
||||
static int plural (int n) {
|
||||
return n % 100 == 1 ? 0 : n % 100 == 2 ? 1 : n % 100 == 3 || n % 100 == 4 ? 2 : 3;
|
||||
}
|
||||
|
||||
// Right to Left language?
|
||||
static bool rtl = true;
|
||||
|
||||
static const LocaleStrings strings
|
||||
{
|
||||
{"%.2f KiB", "%.2f קי״ב"},
|
||||
{"%.2f MiB", "%.2f מי״ב"},
|
||||
{"%.2f GiB", "%.2f קי״ב"},
|
||||
{"Purple I2P Webconsole", "קונסולת Purple I2P"},
|
||||
{"<b>i2pd</b> webconsole", "קונסולת <b>i2pd</b>"},
|
||||
{"Main page", "עמוד ראשי"},
|
||||
{"Router commands", "פקודות נתב"},
|
||||
{"Local Destinations", "יעדים מקומיים"},
|
||||
{"Tunnels", "מנהרות"},
|
||||
{"Transit Tunnels", "מנהרות מעבר"},
|
||||
{"Transports", "מובילים"},
|
||||
{"I2P tunnels", "מנהרות I2P"},
|
||||
{"SAM sessions", "הפעלות SAM"},
|
||||
{"Unknown", "לא מוכר"},
|
||||
{"Proxy", "פרוקסי"},
|
||||
{"Mesh", "סיבוך"},
|
||||
{"Clock skew", "לכסון שעון"},
|
||||
{"Offline", "לא מקוון"},
|
||||
{"Symmetric NAT", "NAT סימטרי"},
|
||||
{"Full cone NAT", "NAT חסום לחלוטין"},
|
||||
{"No Descriptors", "אין מתארים"},
|
||||
{"Uptime", "זמן הפעלה"},
|
||||
{"Network status", "מצב רשת תקשורת"},
|
||||
{"Network status v6", "מצב רשת תקשורת v6"},
|
||||
{"Stopping in", "מפסיק בעוד"},
|
||||
{"Family", "משפחה"},
|
||||
{"Tunnel creation success rate", "שיעור הצלחה של יצירת מנהרות"},
|
||||
{"Total tunnel creation success rate", "שיעור הצלחה כולל של יצירת מנהרות"},
|
||||
{"Received", "נתקבל"},
|
||||
{"%.2f KiB/s", "%.2f קי״ב/ש"},
|
||||
{"Sent", "נשלח"},
|
||||
{"Transit", "מעבר"},
|
||||
{"Data path", "נתיב מידע"},
|
||||
{"Hidden content. Press on text to see.", "תוכן מוסתר. לחץ על הטקסט כדי לראותו."},
|
||||
{"Router Ident", "מזהה נתב"},
|
||||
{"Router Family", "משפחת נתב"},
|
||||
{"Version", "גרסא"},
|
||||
{"Our external address", "הכתובת החיצונית שלנו"},
|
||||
{"supported", "נתמך"},
|
||||
{"Routers", "נתבים"},
|
||||
{"Client Tunnels", "מנהרות לקוח"},
|
||||
{"Services", "שירותים"},
|
||||
{"Enabled", "מאופשר"},
|
||||
{"Disabled", "מנוטרל"},
|
||||
{"Encrypted B33 address", "כתובת B33 מוצפנת"},
|
||||
{"Address registration line", "שורת רישום כתובת"},
|
||||
{"Domain", "תחום"},
|
||||
{"Generate", "צור"},
|
||||
{"<b>Note:</b> result string can be used only for registering 2LD domains (example.i2p). For registering subdomains please use i2pd-tools.", "<b>הערה</b> מחרוזת תוצאה יכולה להיות מועילה רק לצורך רישום תחומים 2LD (example.i2p). לשם רישום תתי-תחום עליך להיוועץ עם i2pd-tools."},
|
||||
{"Address", "כתובת"},
|
||||
{"Type", "טיפוס"},
|
||||
{"Expire LeaseSet", "פקיעת LeaseSet"},
|
||||
{"Inbound tunnels", "מנהרות פנימיות"},
|
||||
{"%dms", "מילישניות %d"},
|
||||
{"Outbound tunnels", "מנהרות חיצוניות"},
|
||||
{"Tags", "תוויות"},
|
||||
{"Incoming", "נכנס"},
|
||||
{"Outgoing", "יוצא"},
|
||||
{"Destination", "יעד"},
|
||||
{"Amount", "כמות"},
|
||||
{"Incoming Tags", "תוויות נכנסות"},
|
||||
{"Tags sessions", "הפעלות תוויות"},
|
||||
{"Status", "מצב"},
|
||||
{"Local Destination", "יעד מקומי"},
|
||||
{"Streams", "זרמים"},
|
||||
{"Close stream", "סגור זרם"},
|
||||
{"Such destination is not found", "יעד כזה לא נמצא"},
|
||||
{"I2CP session not found", "הפעלת I2CP לא נמצאה"},
|
||||
{"I2CP is not enabled", "I2CP לא מאופשר"},
|
||||
{"Invalid", "לא תקין"},
|
||||
{"Store type", "טיפוס אחסון"},
|
||||
{"Expires", "פוקע"},
|
||||
{"Non Expired Leases", "חכירות בלתי פקיעות"},
|
||||
{"Gateway", "שער-דרך"},
|
||||
{"TunnelID", "מזהה מנהרה"},
|
||||
{"EndDate", "תאריך סיום"},
|
||||
{"floodfill mode is disabled", "מצב floodfill הינו מנוטרל"},
|
||||
{"Queue size", "גודל תור"},
|
||||
{"Run peer test", "הרץ בדיקת עמית"},
|
||||
{"Reload tunnels configuration", "טען מחדש תצורת מנהרות"},
|
||||
{"Decline transit tunnels", "דחה מנהרות מעבר"},
|
||||
{"Accept transit tunnels", "קבל מנהרות מעבר"},
|
||||
{"Cancel graceful shutdown", "בטל כיבוי עדין"},
|
||||
{"Start graceful shutdown", "התחל כיבוי עדין"},
|
||||
{"Force shutdown", "כפה כיבוי"},
|
||||
{"Reload external CSS styles", "טען מחדש סגנונות CSS חיצוניים"},
|
||||
{"<b>Note:</b> any action done here are not persistent and not changes your config files.", "<b>הערה</b> כל פעולה אשר מבוצעת כאן אינה המשכית ולא משנה את קובצי התצורה שלך."},
|
||||
{"Logging level", "דרגת רישום יומן"},
|
||||
{"Transit tunnels limit", "מגבלת מנהרות מעבר"},
|
||||
{"Change", "שנה"},
|
||||
{"Change language", "שנה שפה"},
|
||||
{"no transit tunnels currently built", "אין מנהרות מעבר אשר בנויות כעת"},
|
||||
{"SAM disabled", "SAM מנוטרל"},
|
||||
{"no sessions currently running", "אין הפעלה אשר מורצת כעת"},
|
||||
{"SAM session not found", "הפעלת SAM לא נמצאה"},
|
||||
{"SAM Session", "הפעלת SAM"},
|
||||
{"Server Tunnels", "מנהרות שרת"},
|
||||
{"Unknown page", "עמוד לא מוכר"},
|
||||
{"Invalid token", "סימן לא תקין"},
|
||||
{"SUCCESS", "הצלחה"},
|
||||
{"Stream closed", "זרם סגור"},
|
||||
{"Stream not found or already was closed", "זרם לא נמצא או שהוא היה כבר סגור"},
|
||||
{"Destination not found", "יעד לא נמצא"},
|
||||
{"StreamID can't be null", "מזהה זרם (StreamID) לא יכול להיות אפסי"},
|
||||
{"Return to destination page", "חזור לעמוד יעד"},
|
||||
{"You will be redirected in %d seconds", "אתה תכוון מחדש בעוד %d שניות"},
|
||||
{"LeaseSet expiration time updated", "זמן פקיעה של LeaseSet עודכן"},
|
||||
{"LeaseSet is not found or already expired", "LeaseSet אינו נמצא או שהוא כבר פקע"},
|
||||
{"Transit tunnels count must not exceed %d", "אסור לספירת מנהרות מעבר לעלות על %d"},
|
||||
{"Back to commands list", "חזור לרשימת פקודות"},
|
||||
{"Register at reg.i2p", "הירשם באתר reg.i2p"},
|
||||
{"Description", "תיאור"},
|
||||
{"A bit information about service on domain", "מידע אודות שירות על תחום"},
|
||||
{"Submit", "שלח"},
|
||||
{"Domain can't end with .b32.i2p", "תחום לא יכול להסתיים עם ‎.b32.i2p"},
|
||||
{"Domain must end with .i2p", "תחום חייב להסתיים עם ‎.i2p"},
|
||||
{"Unknown command", "פקודה לא מוכרת"},
|
||||
{"Command accepted", "פקודה נתקבלה"},
|
||||
{"Proxy error", "שגיאת פרוקסי"},
|
||||
{"Proxy info", "מידע פרוקסי"},
|
||||
{"Proxy error: Host not found", "שגיאת פרוקסי: מארח לא נמצא"},
|
||||
{"Remote host not found in router's addressbook", "ארח מרוחק לא נמצא בתוך הפנקס כתובות של הנתב"},
|
||||
{"You may try to find this host on jump services below", "באפשרותך לנסות למצוא את מארח זה דרך שירותי קפיצה להלן"},
|
||||
{"Invalid request", "בקשה לא תקינה"},
|
||||
{"Proxy unable to parse your request", "פרוקסי לא מסוגל לנתח את בקשתך"},
|
||||
{"Addresshelper is not supported", "סייען-כתובות אינו נתמך"},
|
||||
{"Host %s is <font color=red>already in router's addressbook</font>. <b>Be careful: source of this URL may be harmful!</b> Click here to update record: <a href=\"%s%s%s&update=true\">Continue</a>.", "מארח %s is <font color=red>כבר נמצא בפנקס כתובות של הנתב</font>. <b>זהירות: מקור URL זה עלול להזיק!</b> לחץ כאן כדי לעדכן מרשם: <a href=\"%s%s%s&update=true\">המשך</a>."},
|
||||
{"Addresshelper forced update rejected", "אילוץ עדכון של סייען-כתובות נדחה"},
|
||||
{"To add host <b>%s</b> in router's addressbook, click here: <a href=\"%s%s%s\">Continue</a>.", "Tכדי להוסיף את מארח <b>%s</b> לפנקס כתובות של הנתב: <a href=\"%s%s%s\">המשך</a>."},
|
||||
{"Addresshelper request", "בקשת סייען-כתובות"},
|
||||
{"Host %s added to router's addressbook from helper. Click here to proceed: <a href=\"%s\">Continue</a>.", "מארח %s נתווסף לסייען-כתובות של הנתב דרך סייען. לחץ כאן כדי proceed: <a href=\"%s\">המשך</a>."},
|
||||
{"Addresshelper adding", "הוספת סייען-כתובות"},
|
||||
{"Host %s is <font color=red>already in router's addressbook</font>. Click here to update record: <a href=\"%s%s%s&update=true\">Continue</a>.", "מארח %s <font color=red>כבר נמצא בספר כתובות של הנתב</font>. לחץ כאן כדי לעדכן מרשם: <a href=\"%s%s%s&update=true\">המשך</a>."},
|
||||
{"Addresshelper update", "עדכון סייען-כתובות"},
|
||||
{"Invalid request URI", "בקשת URI לא תקינה"},
|
||||
{"Can't detect destination host from request", "לא יכול לאתר יעד מארח מתוך בקשה"},
|
||||
{"Host %s is not inside I2P network, but outproxy is not enabled", "מארח %s לא נמצא בתוך רשת I2P, אולם outproxy אינו מאופשר"},
|
||||
{"Hostname is too long", "שם-מארח הינו ארוך מדי"},
|
||||
{"Cannot negotiate with SOCKS proxy", "לא מסוגל להסדיר פרוקסי SOCKS"},
|
||||
{"CONNECT error", "שגיאת חיבור"},
|
||||
{"Failed to connect", "נכשל להתחבר"},
|
||||
{"SOCKS proxy error", "שגיאת פרוקסי SOCKS"},
|
||||
{"No reply from SOCKS proxy", "אין מענה מתוך פרוקסי SOCKS"},
|
||||
{"Cannot connect", "לא מסוגל להתחבר"},
|
||||
{"Host is down", "מארח הינו מושבת"},
|
||||
{"Can't create connection to requested host, it may be down. Please try again later.", "לא יכול ליצור חיבור למארח מבוקש, המארח עשוי להיות מושבת. אנא נסה שוב מאוחר יותר."},
|
||||
{"", ""},
|
||||
};
|
||||
|
||||
static std::map<std::string, std::vector<std::string>> plurals
|
||||
{
|
||||
{"%d days", {"יום %d", "יומיים", "ימים %d", "ימים %d"}},
|
||||
{"%d hours", {"שעה %d", "שעתיים", "שעות %d", "שעות %d"}},
|
||||
{"%d minutes", {"דקה %d", "שתי דקות", "דקות %d", "דקות %d"}},
|
||||
{"%d seconds", {"שניה %d", "שתי שניות", "שניות %d", "שניות %d"}},
|
||||
{"", {"", "", ""}},
|
||||
};
|
||||
|
||||
std::shared_ptr<const i2p::i18n::Locale> GetLocale()
|
||||
{
|
||||
return std::make_shared<i2p::i18n::Locale>(language, rtl, strings, plurals, [] (int n)->int { return plural(n); });
|
||||
}
|
||||
|
||||
} // language
|
||||
} // i18n
|
||||
} // i2p
|
||||
226
i18n/Hindi.cpp
226
i18n/Hindi.cpp
|
|
@ -1,226 +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 <map>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include "I18N.h"
|
||||
|
||||
// Hindi localization file
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace i18n
|
||||
{
|
||||
namespace hindi // language namespace
|
||||
{
|
||||
// language name in lowercase
|
||||
static std::string language = "hindi";
|
||||
|
||||
// See for language plural forms here:
|
||||
// https://localization-guide.readthedocs.io/en/latest/l10n/pluralforms.html
|
||||
static int plural (int n) {
|
||||
return n != 1 ? 1 : 0;
|
||||
}
|
||||
|
||||
// Right to Left language?
|
||||
static bool rtl = false;
|
||||
|
||||
static const LocaleStrings strings
|
||||
{
|
||||
{"%.2f KiB", "%.2f कीबी"},
|
||||
{"%.2f MiB", "%.2f मीबी"},
|
||||
{"%.2f GiB", "%.2f जीबी"},
|
||||
{"building", "निर्माण"},
|
||||
{"failed", "विफल"},
|
||||
{"expiring", "समाप्त होना"},
|
||||
{"established", "स्थापित"},
|
||||
{"unknown", "अज्ञात"},
|
||||
{"exploratory", "अन्वेषणात्मक"},
|
||||
{"Purple I2P Webconsole", "पर्पल I2P वेब कंसोल"},
|
||||
{"<b>i2pd</b> webconsole", "<b>i2pd</b> वेब कंसोल"},
|
||||
{"Main page", "मुख्य पृष्ठ"},
|
||||
{"Router commands", "राउटर आदेश"},
|
||||
{"Local Destinations", "स्थानीय गंतव्य"},
|
||||
{"LeaseSets", "पट्ट समुच्चय"},
|
||||
{"Tunnels", "सुरंग"},
|
||||
{"Transit Tunnels", "संचरण सुरंगें"},
|
||||
{"Transports", "परिवहन"},
|
||||
{"I2P tunnels", "I2P सुरंगें"},
|
||||
{"SAM sessions", "SAM सत्र"},
|
||||
{"ERROR", "त्रुटि"},
|
||||
{"OK", "ठीक है"},
|
||||
{"Testing", "परीक्षण"},
|
||||
{"Firewalled", "फायरवॉल"},
|
||||
{"Unknown", "अज्ञात"},
|
||||
{"Proxy", "प्रॉक्सी"},
|
||||
{"Mesh", "जाली"},
|
||||
{"Clock skew", "घड़ी संकेत विचलन"},
|
||||
{"Offline", "ऑफलाइन"},
|
||||
{"Symmetric NAT", "सममितीय NAT"},
|
||||
{"Full cone NAT", "पूर्णकोण NAT"},
|
||||
{"No Descriptors", "कोई वर्णनकर्त्तृ नहीं हैं"},
|
||||
{"Uptime", "संचालन समय"},
|
||||
{"Network status", "संपर्क स्थिति"},
|
||||
{"Network status v6", "संपर्क स्थिति v6"},
|
||||
{"Stopping in", "में अवसान प्रारंभ हो रहा है"},
|
||||
{"Family", "परिवार"},
|
||||
{"Tunnel creation success rate", "सुरंग निर्माण सफलता दर"},
|
||||
{"Total tunnel creation success rate", "कुल सुरंग निर्माण सफलता दर"},
|
||||
{"Received", "प्राप्त हुआ"},
|
||||
{"%.2f KiB/s", "%.2f कीबी/से"},
|
||||
{"Sent", "प्रेषित"},
|
||||
{"Transit", "संचरण"},
|
||||
{"Data path", "डेटा पथ"},
|
||||
{"Hidden content. Press on text to see.", "सामग्री छिपाई गई है। देखने हेतु पाठ पर दबाएँ।"},
|
||||
{"Router Ident", "राउटर परिचय"},
|
||||
{"Router Family", "राउटर परिवार"},
|
||||
{"Router Caps", "राउटर कैप्स"},
|
||||
{"Version", "संस्करण"},
|
||||
{"Our external address", "हमारा बाह्य पता"},
|
||||
{"supported", "समर्थित"},
|
||||
{"Routers", "राउटर"},
|
||||
{"Floodfills", "पूर्णक संवाहक"},
|
||||
{"Client Tunnels", "क्लाइंट सुरंगें"},
|
||||
{"Services", "सेवाएँ"},
|
||||
{"Enabled", "सक्षम है"},
|
||||
{"Disabled", "निष्क्रिय है"},
|
||||
{"Encrypted B33 address", "कूटलिखित B33 पता"},
|
||||
{"Address registration line", "पता पंजीकरण पंक्ति"},
|
||||
{"Domain", "डोमेन"},
|
||||
{"Generate", "सृजित करें"},
|
||||
{"<b>Note:</b> result string can be used only for registering 2LD domains (example.i2p). For registering subdomains please use i2pd-tools.", "<b>नोट:</b> परिणाम स्ट्रिंग का उपयोग केवल 2LD डोमेनों (जैसे example.i2p) को रजिस्टर करने के लिए किया जा सकता है। सबडोमेन रजिस्टर करने के लिए कृपया i2pd-tools का उपयोग करें।"},
|
||||
{"Address", "पता"},
|
||||
{"Type", "प्रकार"},
|
||||
{"EncType", "कूट प्रकार"},
|
||||
{"Expire LeaseSet", "पट्ट समुच्चय का अवसान करें"},
|
||||
{"Inbound tunnels", "आगमनशील सुरंगें"},
|
||||
{"%dms", "%dms"},
|
||||
{"Outbound tunnels", "प्रस्थानशील सुरंगें"},
|
||||
{"Tags", "चिन्हित"},
|
||||
{"Incoming", "आगामी"},
|
||||
{"Outgoing", "निर्गामी"},
|
||||
{"Destination", "गंतव्य"},
|
||||
{"Amount", "मात्रा"},
|
||||
{"Incoming Tags", "आगामी चिन्हित"},
|
||||
{"Tags sessions", "चिन्हित सत्र OR सत्र को चिन्हित करें"},
|
||||
{"Status", "स्थिति"},
|
||||
{"Local Destination", "स्थानीय गंतव्य"},
|
||||
{"Streams", "धाराएँ"},
|
||||
{"Close stream", "प्रवाह समाप्त करें"},
|
||||
{"Such destination is not found", "ऐसा गंतव्य नहीं मिला"},
|
||||
{"I2CP session not found", "I2CP सत्र नहीं मिला"},
|
||||
{"I2CP is not enabled", "I2CP निष्क्रिय है"},
|
||||
{"Invalid", "अमान्य"},
|
||||
{"Store type", "भण्डारगार का प्रकार"},
|
||||
{"Expires", "अवसान होता है"},
|
||||
{"Non Expired Leases", "अनवसित पट्ट"},
|
||||
{"Gateway", "प्रवेशद्वार"},
|
||||
{"TunnelID", "सुरंग ID"},
|
||||
{"EndDate", "समाप्ति तिथि"},
|
||||
{"floodfill mode is disabled", "पूर्णक संवाहक विधि निष्क्रिय है"},
|
||||
{"Queue size", "क्यू आकार"},
|
||||
{"Run peer test", "सहकर्मी परीक्षण चलाएँ"},
|
||||
{"Reload tunnels configuration", "सुरंग विन्यास पुनः लोड करें"},
|
||||
{"Decline transit tunnels", "संचरण सुरंगों को अस्वीकार करें"},
|
||||
{"Accept transit tunnels", "संचरण सुरंगों को स्वीकार करें"},
|
||||
{"Cancel graceful shutdown", "सौम्य अवसान निरस्त करें"},
|
||||
{"Start graceful shutdown", "सौम्य समापन प्रारंभ करें"},
|
||||
{"Force shutdown", "बाध्य अवसान"},
|
||||
{"Reload external CSS styles", "बाह्य CSS शैलियों को पुनः लोड करें"},
|
||||
{"<b>Note:</b> any action done here are not persistent and not changes your config files.", "<b>टिप्पणी:</b> यहाँ किए गए कोई भी क्रियाएँ स्थायी नहीं हैं और आपके विन्यास संचिका में कोई परिवर्तन नहीं करतीं।"},
|
||||
{"Logging level", "लॉगिंग स्तर"},
|
||||
{"Transit tunnels limit", "संचरण सुरंगों की सीमा"},
|
||||
{"Change", "बदलना"},
|
||||
{"Change language", "भाषा बदलें"},
|
||||
{"no transit tunnels currently built", "संचरण सुरंगों का निर्माण नहीं हुआ है"},
|
||||
{"SAM disabled", "SAM निष्क्रिय है"},
|
||||
{"no sessions currently running", "वर्तमान में कोई सत्र सक्रिय नहीं है"},
|
||||
{"SAM session not found", "SAM सत्र नहीं मिला"},
|
||||
{"SAM Session", "SAM सत्र"},
|
||||
{"Server Tunnels", "सर्वर सुरंग"},
|
||||
{"Client Forwards", "क्लाइंट फॉरवर्ड्स"},
|
||||
{"Server Forwards", "सर्वर फॉरवर्ड्स"},
|
||||
{"Unknown page", "अज्ञात पृष्ठ"},
|
||||
{"Invalid token", "अमान्य टोकन"},
|
||||
{"SUCCESS", "सफलता"},
|
||||
{"Stream closed", "प्रवाह समाप्त हो गया है"},
|
||||
{"Stream not found or already was closed", "प्रवाह प्राप्त नहीं हुआ अथवा इसका पूर्व में ही समापन हो चुका है"},
|
||||
{"Destination not found", "गंतव्य नहीं मिला"},
|
||||
{"StreamID can't be null", "प्रवाह ID शून्य नहीं हो सकता है"},
|
||||
{"Return to destination page", "गंतव्य पृष्ठ पर पुनः वापस जाएँ"},
|
||||
{"You will be redirected in %d seconds", "आपको %d सेकंड में पुनर्निर्देशित किया जाएगा"},
|
||||
{"LeaseSet expiration time updated", "पट्ट समुच्चय की अवसान समय को अद्यतित किया गया है"},
|
||||
{"LeaseSet is not found or already expired", "पट्ट समुच्चय प्राप्त नहीं हुआ या इसका पूर्वमेव अवसान हो चुका है"},
|
||||
{"Transit tunnels count must not exceed %d", "संचरण सुरंगों की संख्या %d से अधिक नहीं होनी चाहिए"},
|
||||
{"Back to commands list", "आदेश सूची पर पुनः लौटें"},
|
||||
{"Register at reg.i2p", "reg.i2p पर पंजीकरण करें"},
|
||||
{"Description", "विवरण"},
|
||||
{"A bit information about service on domain", "डोमेन पर सेवा से संबंधित थोड़ी जानकारी"},
|
||||
{"Submit", "प्रस्तुत करें"},
|
||||
{"Domain can't end with .b32.i2p", "डोमेन का अंत .b32.i2p से नहीं हो सकता"},
|
||||
{"Domain must end with .i2p", "डोमेन का अंत .i2p से होना आवश्यक है"},
|
||||
{"Unknown command", "अज्ञात आदेश"},
|
||||
{"Command accepted", "आदेश स्वीकार किया गया"},
|
||||
{"Proxy error", "प्रॉक्सी त्रुटि"},
|
||||
{"Proxy info", "प्रॉक्सी जानकारी"},
|
||||
{"Proxy error: Host not found", "प्रॉक्सी त्रुटि: होस्ट नहीं मिला"},
|
||||
{"Remote host not found in router's addressbook", "राउटर की पता पुस्तक में दूरस्थ होस्ट नहीं मिला"},
|
||||
{"You may try to find this host on jump services below", "आप नीचे दिए गए जंप सेवाओं में इस होस्ट को खोजने की कोशिश कर सकते हैं"},
|
||||
{"Invalid request", "अमान्य अनुरोध"},
|
||||
{"Proxy unable to parse your request", "प्रॉक्सी आपके अनुरोध को विश्लेषित करने में असमर्थ है"},
|
||||
{"Addresshelper is not supported", "Addresshelper समर्थित नहीं है"},
|
||||
{"Host %s is <font color=red>already in router's addressbook</font>. <b>Be careful: source of this URL may be harmful!</b> Click here to update record: <a href=\"%s%s%s&update=true\">Continue</a>.", "होस्ट %s पहले से ही राउटर की पता-पुस्तिका में <font color=red>उपस्थित है</font>। <b>सावधान रहें: इस URL का स्रोत हानिकारक हो सकता है!</b> अभिलेख को अद्यतन करने हेतु यहाँ क्लिक करें: <a href=\"%s%s%s&update=true\">जारी रखें</a>।"},
|
||||
{"Addresshelper forced update rejected", "Addresshelper का जबरन अद्यतन अस्वीकृत किया गया"},
|
||||
{"To add host <b>%s</b> in router's addressbook, click here: <a href=\"%s%s%s\">Continue</a>.", "राउटर की पता-पुस्तिका में होस्ट <b>%s</b> को जोड़ने हेतु, कृपया यहाँ क्लिक करें: <a href=\"%s%s%s\">जारी रखें</a>।"},
|
||||
{"Addresshelper request", "Addresshelper अनुरोध"},
|
||||
{"Host %s added to router's addressbook from helper. Click here to proceed: <a href=\"%s\">Continue</a>.", "सहायक से होस्ट %s राउटर की पता-पुस्तिका में जोड़ दिया गया है। आगे बढ़ने हेतु यहाँ क्लिक करें: <a href=\"%s\">जारी रखें</a>।"},
|
||||
{"Addresshelper adding", "Addresshelper जोड़ना"},
|
||||
{"Host %s is <font color=red>already in router's addressbook</font>. Click here to update record: <a href=\"%s%s%s&update=true\">Continue</a>.", "होस्ट %s पहले से ही राउटर की पता-पुस्तिका में <font color=red>उपस्थित है</font>। अभिलेख को अद्यतन करने हेतु यहाँ क्लिक करें: <a href=\"%s%s%s&update=true\">जारी रखें</a>।"},
|
||||
{"Addresshelper update", "Addresshelper अद्यतन करना"},
|
||||
{"Invalid request URI", "अमान्य अनुरोध URI"},
|
||||
{"Can't detect destination host from request", "अनुरोध से गंतव्य होस्ट का पता नहीं लगा सकते"},
|
||||
{"Outproxy failure", "आउटप्रॉक्सी विफलता"},
|
||||
{"Bad outproxy settings", "गलत आउटप्रॉक्सी सेटिंग्स"},
|
||||
{"Host %s is not inside I2P network, but outproxy is not enabled", "होस्ट %s I2P नेटवर्क के भीतर नहीं है, लेकिन आउटप्रॉक्सी सक्षम नहीं है"},
|
||||
{"Unknown outproxy URL", "अज्ञात आउटप्रॉक्सी URL"},
|
||||
{"Cannot resolve upstream proxy", "ऊर्ध्वधारा प्रॉक्सी का समाधान नहीं किया जा सका"},
|
||||
{"Hostname is too long", "होस्टनाम अत्यधिक लंबा है"},
|
||||
{"Cannot connect to upstream SOCKS proxy", "उर्ध्वधारा SOCKS प्रॉक्सी से संपर्क स्थापित नहीं हो पा रहा है"},
|
||||
{"Cannot negotiate with SOCKS proxy", "SOCKS प्रॉक्सी के साथ समन्वयन स्थापित नहीं किया जा सका"},
|
||||
{"CONNECT error", "संपर्क त्रुटि"},
|
||||
{"Failed to connect", "संपर्क स्थापित करने में विफल"},
|
||||
{"SOCKS proxy error", "SOCKS प्रॉक्सी त्रुटि"},
|
||||
{"Failed to send request to upstream", "ऊर्ध्ववाहिनी को अनुरोध प्रेषित करने में विफलता हुई"},
|
||||
{"No reply from SOCKS proxy", "SOCKS प्रॉक्सी से कोई प्रत्युत्तर प्राप्त नहीं हुआ"},
|
||||
{"Cannot connect", "संपर्क नहीं हो पा रहा है"},
|
||||
{"HTTP out proxy not implemented", "HTTP आउट प्रॉक्सी कार्यान्वित नहीं किया गया है"},
|
||||
{"Cannot connect to upstream HTTP proxy", "उर्ध्वधारा HTTP प्रॉक्सी से संपर्क स्थापित नहीं हो पा रहा है"},
|
||||
{"Host is down", "होस्ट अनुपलब्ध है"},
|
||||
{"Can't create connection to requested host, it may be down. Please try again later.", "अनुरोधित होस्ट से संपर्क स्थापित नहीं किया जा सका। संभवतः वह सक्रिय नहीं है। कृपया बाद में पुनः प्रयास करें।"},
|
||||
{"", ""},
|
||||
};
|
||||
|
||||
static std::map<std::string, std::vector<std::string>> plurals
|
||||
{
|
||||
{"%d days", {"%d दिन", "%d दिन"}},
|
||||
{"%d hours", {"%d घंटा", "%dघंटे"}},
|
||||
{"%d minutes", {"%d मिनट", "%d मिनट"}},
|
||||
{"%d seconds", {"%d सेकंड", "%d सेकंड"}},
|
||||
{"", {"", "", ""}},
|
||||
};
|
||||
|
||||
std::shared_ptr<const i2p::i18n::Locale> GetLocale()
|
||||
{
|
||||
return std::make_shared<i2p::i18n::Locale>(language, rtl, strings, plurals, [] (int n)->int { return plural(n); });
|
||||
}
|
||||
|
||||
} // language
|
||||
} // i18n
|
||||
} // i2p
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
33
i18n/I18N.h
33
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 <string>
|
||||
#include <string_view>
|
||||
#include <map>
|
||||
#include <utility>
|
||||
#include <functional>
|
||||
|
|
@ -19,17 +18,15 @@ namespace i2p
|
|||
{
|
||||
namespace i18n
|
||||
{
|
||||
typedef std::map<std::string_view, std::string_view> LocaleStrings;
|
||||
class Locale
|
||||
{
|
||||
public:
|
||||
Locale (
|
||||
const std::string& language,
|
||||
const bool& rtl,
|
||||
const LocaleStrings& strings,
|
||||
const std::map<std::string, std::string>& strings,
|
||||
const std::map<std::string, std::vector<std::string>>& plurals,
|
||||
std::function<int(int)> formula
|
||||
): m_Language (language), m_RTL (rtl), m_Strings (strings), m_Plurals (plurals), m_Formula (formula) { };
|
||||
): m_Language (language), m_Strings (strings), m_Plurals (plurals), m_Formula (formula) { };
|
||||
|
||||
// Get activated language name for webconsole
|
||||
std::string GetLanguage() const
|
||||
|
|
@ -37,12 +34,7 @@ namespace i18n
|
|||
return m_Language;
|
||||
}
|
||||
|
||||
bool GetRTL() const
|
||||
{
|
||||
return m_RTL;
|
||||
}
|
||||
|
||||
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())
|
||||
|
|
@ -55,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
|
||||
|
|
@ -71,15 +63,14 @@ namespace i18n
|
|||
|
||||
private:
|
||||
const std::string m_Language;
|
||||
const bool m_RTL;
|
||||
const LocaleStrings m_Strings;
|
||||
const std::map<std::string, std::string> m_Strings;
|
||||
const std::map<std::string, std::vector<std::string>> m_Plurals;
|
||||
std::function<int(int)> 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
|
||||
|
||||
|
|
@ -88,7 +79,7 @@ namespace i18n
|
|||
* @param arg String with message
|
||||
*/
|
||||
template<typename TValue>
|
||||
std::string_view tr (TValue&& arg)
|
||||
std::string tr (TValue&& arg)
|
||||
{
|
||||
return i2p::i18n::translate(std::forward<TValue>(arg));
|
||||
}
|
||||
|
|
@ -101,7 +92,7 @@ std::string_view tr (TValue&& arg)
|
|||
template<typename TValue, typename... TArgs>
|
||||
std::string tr (TValue&& arg, TArgs&&... args)
|
||||
{
|
||||
std::string tr_str = std::string (i2p::i18n::translate(std::forward<TValue>(arg))); // TODO:
|
||||
std::string tr_str = i2p::i18n::translate(std::forward<TValue>(arg));
|
||||
|
||||
size_t size = std::snprintf(NULL, 0, tr_str.c_str(), std::forward<TArgs>(args)...);
|
||||
std::string str(size, 0);
|
||||
|
|
@ -117,7 +108,7 @@ std::string tr (TValue&& arg, TArgs&&... args)
|
|||
* @param n Integer, used for selection of form
|
||||
*/
|
||||
template<typename TValue, typename TValue2>
|
||||
std::string ntr (TValue&& arg, TValue2&& arg2, int n)
|
||||
std::string ntr (TValue&& arg, TValue2&& arg2, int& n)
|
||||
{
|
||||
return i2p::i18n::translate(std::forward<TValue>(arg), std::forward<TValue2>(arg2), std::forward<int>(n));
|
||||
}
|
||||
|
|
@ -130,7 +121,7 @@ std::string ntr (TValue&& arg, TValue2&& arg2, int n)
|
|||
* @param args Array of arguments for string formatting
|
||||
*/
|
||||
template<typename TValue, typename TValue2, typename... TArgs>
|
||||
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<TValue>(arg), std::forward<TValue2>(arg2), std::forward<int>(n));
|
||||
|
||||
|
|
|
|||
|
|
@ -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,8 +30,6 @@ namespace i18n
|
|||
namespace english { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); }
|
||||
namespace french { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); }
|
||||
namespace german { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); }
|
||||
namespace hebrew { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); }
|
||||
namespace hindi { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); }
|
||||
namespace italian { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); }
|
||||
namespace polish { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); }
|
||||
namespace portuguese { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); }
|
||||
|
|
@ -55,8 +53,6 @@ namespace i18n
|
|||
{ "english", {"English", "en", i2p::i18n::english::GetLocale} },
|
||||
{ "french", {"Français", "fr", i2p::i18n::french::GetLocale} },
|
||||
{ "german", {"Deutsch", "de", i2p::i18n::german::GetLocale} },
|
||||
{ "hebrew", {"עִבְרִית", "he", i2p::i18n::hebrew::GetLocale} },
|
||||
{ "hindi", {"हिन्दी", "hi", i2p::i18n::hindi::GetLocale} },
|
||||
{ "italian", {"Italiano", "it", i2p::i18n::italian::GetLocale} },
|
||||
{ "polish", {"Polski", "pl", i2p::i18n::polish::GetLocale} },
|
||||
{ "portuguese", {"Português", "pt", i2p::i18n::portuguese::GetLocale} },
|
||||
|
|
|
|||
|
|
@ -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,10 +29,7 @@ namespace italian // language namespace
|
|||
return n != 1 ? 1 : 0;
|
||||
}
|
||||
|
||||
// Right to Left language?
|
||||
static bool rtl = false;
|
||||
|
||||
static const LocaleStrings strings
|
||||
static std::map<std::string, std::string> strings
|
||||
{
|
||||
{"%.2f KiB", "%.2f KiB"},
|
||||
{"%.2f MiB", "%.2f MiB"},
|
||||
|
|
@ -218,7 +215,7 @@ namespace italian // language namespace
|
|||
|
||||
std::shared_ptr<const i2p::i18n::Locale> GetLocale()
|
||||
{
|
||||
return std::make_shared<i2p::i18n::Locale>(language, rtl, strings, plurals, [] (int n)->int { return plural(n); });
|
||||
return std::make_shared<i2p::i18n::Locale>(language, strings, plurals, [] (int n)->int { return plural(n); });
|
||||
}
|
||||
|
||||
} // language
|
||||
|
|
|
|||
|
|
@ -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,10 +29,7 @@ namespace polish // language namespace
|
|||
return (n == 1 ? 0 : n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 10 || n % 100 >= 20) ? 1 : 2);
|
||||
}
|
||||
|
||||
// Right to Left language?
|
||||
static bool rtl = false;
|
||||
|
||||
static const LocaleStrings strings
|
||||
static std::map<std::string, std::string> strings
|
||||
{
|
||||
{"%.2f KiB", "%.2f KiB"},
|
||||
{"%.2f MiB", "%.2f MiB"},
|
||||
|
|
@ -218,7 +215,7 @@ namespace polish // language namespace
|
|||
|
||||
std::shared_ptr<const i2p::i18n::Locale> GetLocale()
|
||||
{
|
||||
return std::make_shared<i2p::i18n::Locale>(language, rtl, strings, plurals, [] (int n)->int { return plural(n); });
|
||||
return std::make_shared<i2p::i18n::Locale>(language, strings, plurals, [] (int n)->int { return plural(n); });
|
||||
}
|
||||
|
||||
} // language
|
||||
|
|
|
|||
|
|
@ -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,10 +29,7 @@ namespace portuguese // language namespace
|
|||
return n != 1 ? 1 : 0;
|
||||
}
|
||||
|
||||
// Right to Left language?
|
||||
static bool rtl = false;
|
||||
|
||||
static const LocaleStrings strings
|
||||
static std::map<std::string, std::string> strings
|
||||
{
|
||||
{"%.2f KiB", "%.2f KiB"},
|
||||
{"%.2f MiB", "%.2f MiB"},
|
||||
|
|
@ -218,7 +215,7 @@ namespace portuguese // language namespace
|
|||
|
||||
std::shared_ptr<const i2p::i18n::Locale> GetLocale()
|
||||
{
|
||||
return std::make_shared<i2p::i18n::Locale>(language, rtl, strings, plurals, [] (int n)->int { return plural(n); });
|
||||
return std::make_shared<i2p::i18n::Locale>(language, strings, plurals, [] (int n)->int { return plural(n); });
|
||||
}
|
||||
|
||||
} // language
|
||||
|
|
|
|||
|
|
@ -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,10 +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;
|
||||
}
|
||||
|
||||
// Right to Left language?
|
||||
static bool rtl = false;
|
||||
|
||||
static const LocaleStrings strings
|
||||
static std::map<std::string, std::string> strings
|
||||
{
|
||||
{"%.2f KiB", "%.2f КиБ"},
|
||||
{"%.2f MiB", "%.2f МиБ"},
|
||||
|
|
@ -218,7 +215,7 @@ namespace russian // language namespace
|
|||
|
||||
std::shared_ptr<const i2p::i18n::Locale> GetLocale()
|
||||
{
|
||||
return std::make_shared<i2p::i18n::Locale>(language, rtl, strings, plurals, [] (int n)->int { return plural(n); });
|
||||
return std::make_shared<i2p::i18n::Locale>(language, strings, plurals, [] (int n)->int { return plural(n); });
|
||||
}
|
||||
|
||||
} // language
|
||||
|
|
|
|||
|
|
@ -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,10 +29,7 @@ namespace spanish // language namespace
|
|||
return n != 1 ? 1 : 0;
|
||||
}
|
||||
|
||||
// Right to Left language?
|
||||
static bool rtl = false;
|
||||
|
||||
static const LocaleStrings strings
|
||||
static std::map<std::string, std::string> strings
|
||||
{
|
||||
{"%.2f KiB", "%.2f KiB"},
|
||||
{"%.2f MiB", "%.2f MiB"},
|
||||
|
|
@ -199,7 +196,7 @@ namespace spanish // language namespace
|
|||
|
||||
std::shared_ptr<const i2p::i18n::Locale> GetLocale()
|
||||
{
|
||||
return std::make_shared<i2p::i18n::Locale>(language, rtl, strings, plurals, [] (int n)->int { return plural(n); });
|
||||
return std::make_shared<i2p::i18n::Locale>(language, strings, plurals, [] (int n)->int { return plural(n); });
|
||||
}
|
||||
|
||||
} // language
|
||||
|
|
|
|||
|
|
@ -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,10 +29,7 @@ namespace swedish // language namespace
|
|||
return n != 1 ? 1 : 0;
|
||||
}
|
||||
|
||||
// Right to Left language?
|
||||
static bool rtl = false;
|
||||
|
||||
static const LocaleStrings strings
|
||||
static std::map<std::string, std::string> strings
|
||||
{
|
||||
{"%.2f KiB", "%.2f KiB"},
|
||||
{"%.2f MiB", "%.2f MiB"},
|
||||
|
|
@ -214,7 +211,7 @@ namespace swedish // language namespace
|
|||
|
||||
std::shared_ptr<const i2p::i18n::Locale> GetLocale()
|
||||
{
|
||||
return std::make_shared<i2p::i18n::Locale>(language, rtl, strings, plurals, [] (int n)->int { return plural(n); });
|
||||
return std::make_shared<i2p::i18n::Locale>(language, strings, plurals, [] (int n)->int { return plural(n); });
|
||||
}
|
||||
|
||||
} // language
|
||||
|
|
|
|||
|
|
@ -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,10 +29,7 @@ namespace turkish // language namespace
|
|||
return n != 1 ? 1 : 0;
|
||||
}
|
||||
|
||||
// Right to Left language?
|
||||
static bool rtl = false;
|
||||
|
||||
static const LocaleStrings strings
|
||||
static std::map<std::string, std::string> strings
|
||||
{
|
||||
{"%.2f KiB", "%.2f KiB"},
|
||||
{"%.2f MiB", "%.2f MiB"},
|
||||
|
|
@ -109,7 +106,7 @@ namespace turkish // language namespace
|
|||
|
||||
std::shared_ptr<const i2p::i18n::Locale> GetLocale()
|
||||
{
|
||||
return std::make_shared<i2p::i18n::Locale>(language, rtl, strings, plurals, [] (int n)->int { return plural(n); });
|
||||
return std::make_shared<i2p::i18n::Locale>(language, strings, plurals, [] (int n)->int { return plural(n); });
|
||||
}
|
||||
|
||||
} // language
|
||||
|
|
|
|||
|
|
@ -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,10 +29,7 @@ namespace turkmen // language namespace
|
|||
return n != 1 ? 1 : 0;
|
||||
}
|
||||
|
||||
// Right to Left language?
|
||||
static bool rtl = false;
|
||||
|
||||
static const LocaleStrings strings
|
||||
static std::map<std::string, std::string> strings
|
||||
{
|
||||
{"%.2f KiB", "%.2f KiB"},
|
||||
{"%.2f MiB", "%.2f MiB"},
|
||||
|
|
@ -199,7 +196,7 @@ namespace turkmen // language namespace
|
|||
|
||||
std::shared_ptr<const i2p::i18n::Locale> GetLocale()
|
||||
{
|
||||
return std::make_shared<i2p::i18n::Locale>(language, rtl, strings, plurals, [] (int n)->int { return plural(n); });
|
||||
return std::make_shared<i2p::i18n::Locale>(language, strings, plurals, [] (int n)->int { return plural(n); });
|
||||
}
|
||||
|
||||
} // language
|
||||
|
|
|
|||
|
|
@ -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,10 +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;
|
||||
}
|
||||
|
||||
// Right to Left language?
|
||||
static bool rtl = false;
|
||||
|
||||
static const LocaleStrings strings
|
||||
static std::map<std::string, std::string> strings
|
||||
{
|
||||
{"%.2f KiB", "%.2f КіБ"},
|
||||
{"%.2f MiB", "%.2f МіБ"},
|
||||
|
|
@ -218,7 +215,7 @@ namespace ukrainian // language namespace
|
|||
|
||||
std::shared_ptr<const i2p::i18n::Locale> GetLocale()
|
||||
{
|
||||
return std::make_shared<i2p::i18n::Locale>(language, rtl, strings, plurals, [] (int n)->int { return plural(n); });
|
||||
return std::make_shared<i2p::i18n::Locale>(language, strings, plurals, [] (int n)->int { return plural(n); });
|
||||
}
|
||||
|
||||
} // language
|
||||
|
|
|
|||
|
|
@ -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,10 +29,7 @@ namespace uzbek // language namespace
|
|||
return n > 1 ? 1 : 0;
|
||||
}
|
||||
|
||||
// Right to Left language?
|
||||
static bool rtl = false;
|
||||
|
||||
static const LocaleStrings strings
|
||||
static std::map<std::string, std::string> strings
|
||||
{
|
||||
{"%.2f KiB", "%.2f KiB"},
|
||||
{"%.2f MiB", "%.2f MiB"},
|
||||
|
|
@ -218,7 +215,7 @@ namespace uzbek // language namespace
|
|||
|
||||
std::shared_ptr<const i2p::i18n::Locale> GetLocale()
|
||||
{
|
||||
return std::make_shared<i2p::i18n::Locale>(language, rtl, strings, plurals, [] (int n)->int { return plural(n); });
|
||||
return std::make_shared<i2p::i18n::Locale>(language, strings, plurals, [] (int n)->int { return plural(n); });
|
||||
}
|
||||
|
||||
} // language
|
||||
|
|
|
|||
187
libi2pd/Base.cpp
187
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',
|
||||
|
|
@ -28,6 +28,11 @@ namespace data
|
|||
return T32;
|
||||
}
|
||||
|
||||
bool IsBase32 (char ch)
|
||||
{
|
||||
return (ch >= 'a' && ch <= 'z') || (ch >= '2' && ch <= '7');
|
||||
}
|
||||
|
||||
static void iT64Build(void);
|
||||
|
||||
/*
|
||||
|
|
@ -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',
|
||||
|
|
@ -55,16 +60,23 @@ 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,69 +86,78 @@ 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;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -144,42 +165,55 @@ namespace data
|
|||
* 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;
|
||||
|
||||
auto pos = base64Str.find_last_not_of (P64);
|
||||
if (pos == base64Str.npos) return 0;
|
||||
outCount -= (base64Str.length () - pos - 1);
|
||||
if (outCount > len) return 0;
|
||||
ps = (unsigned char *)(InBuffer + InCount - 1);
|
||||
while ( *ps-- == P64 )
|
||||
outCount--;
|
||||
ps = (unsigned char *)InBuffer;
|
||||
|
||||
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,13 +221,13 @@ 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;
|
||||
}
|
||||
|
|
@ -201,16 +235,31 @@ namespace data
|
|||
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')
|
||||
|
|
@ -258,13 +308,11 @@ namespace data
|
|||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,41 +11,26 @@
|
|||
|
||||
#include <inttypes.h>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <cstdlib>
|
||||
|
||||
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 <iostream>
|
||||
|
||||
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 (std::string_view in); // using standard table, for Proxy-Authorization
|
||||
std::string ToBase64Standard (const std::string& in); // using standard table, for Proxy-Authorization
|
||||
|
||||
} // data
|
||||
} // i2p
|
||||
|
|
|
|||
|
|
@ -1,17 +1,15 @@
|
|||
/*
|
||||
* 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
|
||||
*
|
||||
* See full license text in LICENSE file at top of project tree
|
||||
*/
|
||||
|
||||
#include <cstdint>
|
||||
#include <zlib.h> // for crc32
|
||||
#include <openssl/sha.h>
|
||||
#include <openssl/hmac.h>
|
||||
#include <openssl/ec.h>
|
||||
#include <openssl/evp.h>
|
||||
#include <openssl/bn.h>
|
||||
#include "Base.h"
|
||||
#include "Crypto.h"
|
||||
|
|
@ -67,10 +65,10 @@ namespace data
|
|||
BIGNUM * x = BN_bin2bn (pub, publicKeyLen/2, NULL);
|
||||
BIGNUM * y = BN_bin2bn (pub + publicKeyLen/2, publicKeyLen/2, NULL);
|
||||
EC_POINT * p = EC_POINT_new (group);
|
||||
EC_POINT_set_affine_coordinates (group, p, x, y, NULL);
|
||||
EC_POINT_set_affine_coordinates_GFp (group, p, x, y, NULL);
|
||||
EC_POINT * p1 = BlindPublicKeyECDSA (group, p, seed);
|
||||
EC_POINT_free (p);
|
||||
EC_POINT_get_affine_coordinates (group, p1, x, y, NULL);
|
||||
EC_POINT_get_affine_coordinates_GFp (group, p1, x, y, NULL);
|
||||
EC_POINT_free (p1);
|
||||
i2p::crypto::bn2buf (x, blindedPub, publicKeyLen/2);
|
||||
i2p::crypto::bn2buf (y, blindedPub + publicKeyLen/2, publicKeyLen/2);
|
||||
|
|
@ -90,7 +88,7 @@ namespace data
|
|||
BN_CTX_free (ctx);
|
||||
BN_free (a1);
|
||||
BIGNUM * x = BN_new(), * y = BN_new();
|
||||
EC_POINT_get_affine_coordinates (group, p, x, y, NULL);
|
||||
EC_POINT_get_affine_coordinates_GFp (group, p, x, y, NULL);
|
||||
EC_POINT_free (p);
|
||||
i2p::crypto::bn2buf (x, blindedPub, publicKeyLen/2);
|
||||
i2p::crypto::bn2buf (y, blindedPub + publicKeyLen/2, publicKeyLen/2);
|
||||
|
|
@ -154,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);
|
||||
|
|
@ -200,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
|
||||
|
|
@ -210,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
|
||||
|
|
@ -297,14 +296,12 @@ namespace data
|
|||
|
||||
void BlindedPublicKey::H (const std::string& p, const std::vector<std::pair<const uint8_t *, size_t> >& bufs, uint8_t * hash) const
|
||||
{
|
||||
EVP_MD_CTX *ctx = EVP_MD_CTX_new ();
|
||||
|
||||
EVP_DigestInit_ex(ctx, EVP_sha256 (), NULL);
|
||||
EVP_DigestUpdate (ctx, p.c_str (), p.length ());
|
||||
SHA256_CTX ctx;
|
||||
SHA256_Init (&ctx);
|
||||
SHA256_Update (&ctx, p.c_str (), p.length ());
|
||||
for (const auto& it: bufs)
|
||||
EVP_DigestUpdate (ctx, it.first, it.second);
|
||||
EVP_DigestFinal_ex (ctx, (uint8_t * )hash, nullptr);
|
||||
EVP_MD_CTX_free (ctx);
|
||||
SHA256_Update (&ctx, it.first, it.second);
|
||||
SHA256_Final (hash, &ctx);
|
||||
}
|
||||
|
||||
i2p::data::IdentHash BlindedPublicKey::GetStoreHash (const char * date) const
|
||||
|
|
@ -323,12 +320,11 @@ namespace data
|
|||
if (publicKeyLength)
|
||||
{
|
||||
auto stA1 = htobe16 (m_BlindedSigType);
|
||||
EVP_MD_CTX *ctx = EVP_MD_CTX_new ();
|
||||
EVP_DigestInit_ex(ctx, EVP_sha256(), NULL);
|
||||
EVP_DigestUpdate (ctx, (const uint8_t *)&stA1, 2);
|
||||
EVP_DigestUpdate (ctx, blinded, publicKeyLength);
|
||||
EVP_DigestFinal_ex (ctx, (uint8_t * )hash, nullptr);
|
||||
EVP_MD_CTX_free(ctx);
|
||||
SHA256_CTX ctx;
|
||||
SHA256_Init (&ctx);
|
||||
SHA256_Update (&ctx, (const uint8_t *)&stA1, 2);
|
||||
SHA256_Update (&ctx, blinded, publicKeyLength);
|
||||
SHA256_Final ((uint8_t *)hash, &ctx);
|
||||
}
|
||||
else
|
||||
LogPrint (eLogError, "Blinding: Blinded key type ", (int)m_BlindedSigType, " is not supported");
|
||||
|
|
|
|||
|
|
@ -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 <inttypes.h>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
#include "Identity.h"
|
||||
|
||||
|
|
@ -24,7 +23,7 @@ namespace data
|
|||
public:
|
||||
|
||||
BlindedPublicKey (std::shared_ptr<const IdentityEx> 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 (); };
|
||||
|
|
|
|||
68
libi2pd/CPU.cpp
Normal file
68
libi2pd/CPU.cpp
Normal file
|
|
@ -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 <cpuid.h>
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#include <intrin.h>
|
||||
#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"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
*
|
||||
|
|
@ -76,12 +76,7 @@ namespace config {
|
|||
options_description limits("Limits options");
|
||||
limits.add_options()
|
||||
("limits.coresize", value<uint32_t>()->default_value(0), "Maximum size of corefile in Kb (0 - use system limit)")
|
||||
#if defined(__HAIKU__)
|
||||
// Haiku's system default is 512, so we set 4096 explicitly
|
||||
("limits.openfiles", value<uint16_t>()->default_value(4096), "Maximum number of open files (4096 by default)")
|
||||
#else
|
||||
("limits.openfiles", value<uint16_t>()->default_value(0), "Maximum number of open files (0 - use system default)")
|
||||
#endif
|
||||
("limits.transittunnels", value<uint32_t>()->default_value(10000), "Maximum active transit tunnels (default:10000)")
|
||||
("limits.zombies", value<double>()->default_value(0), "Minimum percentage of successfully created tunnels under which tunnel cleanup is paused (default [%]: 0.00)")
|
||||
("limits.ntcpsoft", value<uint16_t>()->default_value(0), "Ignored")
|
||||
|
|
@ -102,7 +97,6 @@ namespace config {
|
|||
("http.webroot", value<std::string>()->default_value("/"), "WebUI root path (default: / )")
|
||||
("http.lang", value<std::string>()->default_value("english"), "WebUI language (default: english )")
|
||||
("http.showTotalTCSR", value<bool>()->default_value(false), "Show additional value with total TCSR since router's start (default: false)")
|
||||
("http.theme", value<std::string>()->default_value("light"), "Theme for http web console")
|
||||
;
|
||||
|
||||
options_description httpproxy("HTTP Proxy options");
|
||||
|
|
@ -123,18 +117,9 @@ namespace config {
|
|||
("httpproxy.latency.max", value<std::string>()->default_value("0"), "HTTP proxy max latency for tunnels")
|
||||
("httpproxy.outproxy", value<std::string>()->default_value(""), "HTTP proxy upstream out proxy url")
|
||||
("httpproxy.addresshelper", value<bool>()->default_value(true), "Enable or disable addresshelper")
|
||||
("httpproxy.senduseragent", value<bool>()->default_value(false), "Pass through user's User-Agent if enabled. Disabled by default")
|
||||
("httpproxy.i2cp.leaseSetType", value<std::string>()->default_value("3"), "Local destination's LeaseSet type")
|
||||
#if OPENSSL_PQ
|
||||
("httpproxy.i2cp.leaseSetEncType", value<std::string>()->default_value("6,4,0"), "Local destination's LeaseSet encryption type")
|
||||
#else
|
||||
("httpproxy.i2cp.leaseSetEncType", value<std::string>()->default_value("4,0"), "Local destination's LeaseSet encryption type")
|
||||
#endif
|
||||
("httpproxy.i2cp.leaseSetEncType", value<std::string>()->default_value("0,4"), "Local destination's LeaseSet encryption type")
|
||||
("httpproxy.i2cp.leaseSetPrivKey", value<std::string>()->default_value(""), "LeaseSet private key")
|
||||
("httpproxy.i2p.streaming.maxOutboundSpeed", value<std::string>()->default_value("1730000000"), "Max outbound speed of HTTP proxy stream in bytes/sec")
|
||||
("httpproxy.i2p.streaming.maxInboundSpeed", value<std::string>()->default_value("1730000000"), "Max inbound speed of HTTP proxy stream in bytes/sec")
|
||||
("httpproxy.i2p.streaming.profile", value<std::string>()->default_value("1"), "HTTP Proxy bandwidth usage profile. 1 - bulk(high), 2- interactive(low)")
|
||||
("httpproxy.i2p.streaming.maxWindowSize", value<std::string>()->default_value("512"), "HTTP Proxy stream max window size. 512 by default")
|
||||
;
|
||||
|
||||
options_description socksproxy("SOCKS Proxy options");
|
||||
|
|
@ -157,31 +142,8 @@ namespace config {
|
|||
("socksproxy.outproxy", value<std::string>()->default_value("127.0.0.1"), "Upstream outproxy address for SOCKS Proxy")
|
||||
("socksproxy.outproxyport", value<uint16_t>()->default_value(9050), "Upstream outproxy port for SOCKS Proxy")
|
||||
("socksproxy.i2cp.leaseSetType", value<std::string>()->default_value("3"), "Local destination's LeaseSet type")
|
||||
#if OPENSSL_PQ
|
||||
("socksproxy.i2cp.leaseSetEncType", value<std::string>()->default_value("6,4,0"), "Local destination's LeaseSet encryption type")
|
||||
#else
|
||||
("socksproxy.i2cp.leaseSetEncType", value<std::string>()->default_value("4,0"), "Local destination's LeaseSet encryption type")
|
||||
#endif
|
||||
("socksproxy.i2cp.leaseSetEncType", value<std::string>()->default_value("0,4"), "Local destination's LeaseSet encryption type")
|
||||
("socksproxy.i2cp.leaseSetPrivKey", value<std::string>()->default_value(""), "LeaseSet private key")
|
||||
("socksproxy.i2p.streaming.maxOutboundSpeed", value<std::string>()->default_value("1730000000"), "Max outbound speed of SOCKS proxy stream in bytes/sec")
|
||||
("socksproxy.i2p.streaming.maxInboundSpeed", value<std::string>()->default_value("1730000000"), "Max inbound speed of SOCKS proxy stream in bytes/sec")
|
||||
("socksproxy.i2p.streaming.profile", value<std::string>()->default_value("1"), "SOCKS Proxy bandwidth usage profile. 1 - bulk(high), 2- interactive(low)")
|
||||
("socksproxy.i2p.streaming.maxWindowSize", value<std::string>()->default_value("512"), "SOCKS Proxy stream max window size. 512 by default")
|
||||
;
|
||||
|
||||
options_description shareddest("Shared local destination options");
|
||||
shareddest.add_options()
|
||||
("shareddest.inbound.length", value<std::string>()->default_value("3"), "Shared local destination inbound tunnel length")
|
||||
("shareddest.outbound.length", value<std::string>()->default_value("3"), "Shared local destination outbound tunnel length")
|
||||
("shareddest.inbound.quantity", value<std::string>()->default_value("3"), "Shared local destination inbound tunnels quantity")
|
||||
("shareddest.outbound.quantity", value<std::string>()->default_value("3"), "Shared local destination outbound tunnels quantity")
|
||||
("shareddest.i2cp.leaseSetType", value<std::string>()->default_value("3"), "Shared local destination's LeaseSet type")
|
||||
#if OPENSSL_PQ
|
||||
("shareddest.i2cp.leaseSetEncType", value<std::string>()->default_value("6,4,0"), "Shared local destination's LeaseSet encryption type")
|
||||
#else
|
||||
("shareddest.i2cp.leaseSetEncType", value<std::string>()->default_value("4,0"), "Shared local destination's LeaseSet encryption type")
|
||||
#endif
|
||||
("shareddest.i2p.streaming.profile", value<std::string>()->default_value("2"), "Shared local destination bandwidth usage profile. 1 - bulk(high), 2- interactive(low)")
|
||||
;
|
||||
|
||||
options_description sam("SAM bridge options");
|
||||
|
|
@ -253,19 +215,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/"
|
||||
), "Reseed URLs, separated by comma")
|
||||
("reseed.yggurls", value<std::string>()->default_value(
|
||||
"http://[324:71e:281a:9ed3::ace]:7070/,"
|
||||
"http://[301:65b9:c7cd:9a36::1]:18801/,"
|
||||
"http://[320:f09a:f09f:7acd::216]/,"
|
||||
"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")
|
||||
;
|
||||
|
|
@ -322,8 +287,6 @@ namespace config {
|
|||
("ssu2.mtu4", value<uint16_t>()->default_value(0), "MTU for ipv4 address (default: detect)")
|
||||
("ssu2.mtu6", value<uint16_t>()->default_value(0), "MTU for ipv6 address (default: detect)")
|
||||
("ssu2.proxy", value<std::string>()->default_value(""), "Socks5 proxy URL for SSU2 transport")
|
||||
("ssu2.firewalled4", value<bool>()->default_value(false), "Set ipv4 network status to Firewalled even if OK (default: disabled)")
|
||||
("ssu2.firewalled6", value<bool>()->default_value(false), "Set ipv6 network status to Firewalled even if OK (default: disabled)")
|
||||
;
|
||||
|
||||
options_description nettime("Time sync options");
|
||||
|
|
@ -345,11 +308,11 @@ namespace config {
|
|||
("persist.addressbook", value<bool>()->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");
|
||||
|
|
@ -371,7 +334,6 @@ namespace config {
|
|||
.add(httpserver)
|
||||
.add(httpproxy)
|
||||
.add(socksproxy)
|
||||
.add(shareddest)
|
||||
.add(sam)
|
||||
.add(bob)
|
||||
.add(i2cp)
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
220
libi2pd/Crypto.h
220
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
|
||||
*
|
||||
|
|
@ -11,7 +11,6 @@
|
|||
|
||||
#include <inttypes.h>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
#include <openssl/bn.h>
|
||||
#include <openssl/dh.h>
|
||||
|
|
@ -26,13 +25,16 @@
|
|||
|
||||
#include "Base.h"
|
||||
#include "Tag.h"
|
||||
#include "CPU.h"
|
||||
|
||||
// recognize openssl version and features
|
||||
#if (!defined(LIBRESSL_VERSION_NUMBER) && (OPENSSL_VERSION_NUMBER != 0x030000000)) // 3.0.0, regression in SipHash, not implemented in LibreSSL
|
||||
#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
|
||||
|
|
@ -42,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 ();
|
||||
|
|
@ -72,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
|
||||
};
|
||||
|
||||
|
|
@ -88,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<uint32_t *>(buf)[i] ^= reinterpret_cast<const uint32_t *>(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<size_t sz>
|
||||
class AESAlignedBuffer // 16 bytes alignment
|
||||
{
|
||||
public:
|
||||
|
||||
ECBDecryption ();
|
||||
~ECBDecryption ();
|
||||
AESAlignedBuffer ()
|
||||
{
|
||||
m_Buf = m_UnalignedBuffer;
|
||||
uint8_t rem = ((size_t)m_Buf) & 0x0f;
|
||||
if (rem)
|
||||
m_Buf += (16 - rem);
|
||||
}
|
||||
|
||||
void SetKey (const uint8_t * key) { m_Key = key; };
|
||||
void Decrypt (const uint8_t * in, uint8_t * out);
|
||||
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 uint8_t * key) { m_Key = key; }; // 32 bytes
|
||||
void Encrypt (const uint8_t * in, size_t len, const uint8_t * iv, uint8_t * out);
|
||||
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; }
|
||||
|
||||
private:
|
||||
|
||||
AESKey m_Key;
|
||||
EVP_CIPHER_CTX * m_Ctx;
|
||||
AESAlignedBuffer<16> m_LastBlock;
|
||||
|
||||
ECBEncryption m_ECBEncryption;
|
||||
};
|
||||
|
||||
class CBCDecryption
|
||||
{
|
||||
public:
|
||||
|
||||
CBCDecryption ();
|
||||
~CBCDecryption ();
|
||||
CBCDecryption () { memset ((uint8_t *)m_IV, 0, 16); };
|
||||
|
||||
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);
|
||||
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
|
||||
|
|
@ -191,77 +266,26 @@ 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:
|
||||
|
||||
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<std::pair<uint8_t *, size_t> >& 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
|
||||
void AEADChaCha20Poly1305Encrypt (const std::vector<std::pair<uint8_t *, size_t> >& bufs, const uint8_t * key, const uint8_t * nonce, uint8_t * mac); // encrypt multiple buffers with zero ad
|
||||
|
||||
// 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, std::string_view info, uint8_t * out, size_t outLen = 64); // salt - 32, out - 32 or 64, info <= 32
|
||||
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
|
||||
|
||||
// Noise
|
||||
|
||||
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<std::pair<uint8_t *, size_t> >& 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)
|
||||
|
|
@ -270,7 +294,7 @@ namespace crypto
|
|||
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 ();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
*
|
||||
|
|
@ -41,7 +41,7 @@ namespace crypto
|
|||
m_PublicKey = EC_POINT_new (m_Curve);
|
||||
BIGNUM * x = BN_bin2bn (pub, 32, nullptr);
|
||||
BIGNUM * y = BN_bin2bn (pub + 32, 32, nullptr);
|
||||
if (!EC_POINT_set_affine_coordinates (m_Curve, m_PublicKey, x, y, nullptr))
|
||||
if (!EC_POINT_set_affine_coordinates_GFp (m_Curve, m_PublicKey, x, y, nullptr))
|
||||
LogPrint (eLogError, "ECICS P256 invalid public key");
|
||||
BN_free (x); BN_free (y);
|
||||
}
|
||||
|
|
@ -87,7 +87,7 @@ namespace crypto
|
|||
RAND_bytes (priv + 32, 224);
|
||||
BN_free (key);
|
||||
BIGNUM * x = BN_new (), * y = BN_new ();
|
||||
EC_POINT_get_affine_coordinates (curve, p, x, y, NULL);
|
||||
EC_POINT_get_affine_coordinates_GFp (curve, p, x, y, NULL);
|
||||
bn2buf (x, pub, 32);
|
||||
bn2buf (y, pub + 32, 32);
|
||||
RAND_bytes (pub + 64, 192);
|
||||
|
|
@ -102,7 +102,7 @@ namespace crypto
|
|||
m_PublicKey = EC_POINT_new (curve->GetGroup ());
|
||||
BIGNUM * x = BN_bin2bn (pub, 32, nullptr);
|
||||
BIGNUM * y = BN_bin2bn (pub + 32, 32, nullptr);
|
||||
if (!EC_POINT_set_affine_coordinates (curve->GetGroup (), m_PublicKey, x, y, nullptr))
|
||||
if (!EC_POINT_set_affine_coordinates_GFp (curve->GetGroup (), m_PublicKey, x, y, nullptr))
|
||||
LogPrint (eLogError, "ECICS GOST R 34.10 invalid public key");
|
||||
BN_free (x); BN_free (y);
|
||||
}
|
||||
|
|
@ -146,7 +146,7 @@ namespace crypto
|
|||
RAND_bytes (priv + 32, 224);
|
||||
BN_free (key);
|
||||
BIGNUM * x = BN_new (), * y = BN_new ();
|
||||
EC_POINT_get_affine_coordinates (curve->GetGroup (), p, x, y, NULL);
|
||||
EC_POINT_get_affine_coordinates_GFp (curve->GetGroup (), p, x, y, NULL);
|
||||
bn2buf (x, pub, 32);
|
||||
bn2buf (y, pub + 32, 32);
|
||||
RAND_bytes (pub + 64, 192);
|
||||
|
|
@ -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 ());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 <inttypes.h>
|
||||
#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 256; // actual size is 32, but we use 256 for compatibility with old keys files
|
||||
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<uint8_t> pub, priv;
|
||||
i2p::data::CryptoKeyType keyType;
|
||||
std::shared_ptr<CryptoKeyDecryptor> decryptor;
|
||||
|
||||
LocalEncryptionKey (i2p::data::CryptoKeyType t);
|
||||
void GenerateKeys ();
|
||||
void CreateDecryptor ();
|
||||
};
|
||||
void CreateECIESX25519AEADRatchetRandomKeys (uint8_t * priv, uint8_t * pub);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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,10 +18,8 @@ namespace i2p
|
|||
{
|
||||
namespace datagram
|
||||
{
|
||||
DatagramDestination::DatagramDestination (std::shared_ptr<i2p::client::ClientDestination> owner,
|
||||
bool gzip, DatagramVersion version):
|
||||
m_Owner (owner), m_DefaultReceiver (nullptr), m_DefaultRawReceiver (nullptr),
|
||||
m_Gzip (gzip), m_Version (version)
|
||||
DatagramDestination::DatagramDestination (std::shared_ptr<i2p::client::ClientDestination> owner, bool gzip):
|
||||
m_Owner (owner), m_DefaultReceiver (nullptr), m_DefaultRawReceiver (nullptr), m_Gzip (gzip)
|
||||
{
|
||||
if (m_Gzip)
|
||||
m_Deflator.reset (new i2p::data::GzipDeflator);
|
||||
|
|
@ -56,31 +54,9 @@ namespace datagram
|
|||
return ObtainSession(ident);
|
||||
}
|
||||
|
||||
void DatagramDestination::SendDatagram (std::shared_ptr<DatagramSession> session, const uint8_t * payload, size_t len,
|
||||
uint16_t fromPort, uint16_t toPort, const i2p::util::Mapping * options)
|
||||
void DatagramDestination::SendDatagram (std::shared_ptr<DatagramSession> session, const uint8_t * payload, size_t len, uint16_t fromPort, uint16_t toPort)
|
||||
{
|
||||
if (session)
|
||||
{
|
||||
std::shared_ptr<I2NPMessage> msg;
|
||||
switch (session->GetVersion ())
|
||||
{
|
||||
case eDatagramV3:
|
||||
{
|
||||
uint8_t flags[] = { 0x00, 0x03 }; // datagram3, no options
|
||||
if (options)
|
||||
{
|
||||
uint8_t optionsBuf[256]; // TODO: evaluate actual size
|
||||
size_t optionsLen = options->ToBuffer (optionsBuf, 256);
|
||||
if (optionsLen) flags[1] |= DATAGRAM3_FLAG_OPTIONS;
|
||||
msg = CreateDataMessage ({{m_Owner->GetIdentity ()->GetIdentHash (), 32}, {flags, 2},
|
||||
{optionsBuf, optionsLen}, {payload, len}}, fromPort, toPort, i2p::client::PROTOCOL_TYPE_DATAGRAM3, false); // datagram3
|
||||
}
|
||||
else
|
||||
msg = CreateDataMessage ({{m_Owner->GetIdentity ()->GetIdentHash (), 32},
|
||||
{flags, 2}, {payload, len}}, fromPort, toPort, i2p::client::PROTOCOL_TYPE_DATAGRAM3, false); // datagram3
|
||||
break;
|
||||
}
|
||||
case eDatagramV1:
|
||||
{
|
||||
if (m_Owner->GetIdentity ()->GetSigningKeyType () == i2p::data::SIGNING_KEY_TYPE_DSA_SHA1)
|
||||
{
|
||||
|
|
@ -90,36 +66,17 @@ namespace datagram
|
|||
}
|
||||
else
|
||||
m_Owner->Sign (payload, len, m_Signature.data ());
|
||||
msg = CreateDataMessage ({{m_From.data (), m_From.size ()}, {m_Signature.data (), m_Signature.size ()}, {payload, len}},
|
||||
fromPort, toPort, i2p::client::PROTOCOL_TYPE_DATAGRAM, !session->IsRatchets ()); // datagram1
|
||||
break;
|
||||
}
|
||||
case eDatagramV2:
|
||||
{
|
||||
constexpr uint8_t flags[] = { 0x00, 0x02 }; // datagram2, no options
|
||||
// signature
|
||||
std::vector<uint8_t> signedData (len + 32 + 2);
|
||||
memcpy (signedData.data (), m_Owner->GetIdentity ()->GetIdentHash (), 32);
|
||||
memcpy (signedData.data () + 32, flags, 2);
|
||||
memcpy (signedData.data () + 34, payload, len);
|
||||
m_Owner->Sign (signedData.data (), signedData.size (), m_Signature.data ());
|
||||
// TODO: offline signatures and options
|
||||
msg = CreateDataMessage ({{m_From.data (), m_From.size ()}, {flags, 2}, {payload, len},
|
||||
{m_Signature.data (), m_Signature.size ()}}, fromPort, toPort, i2p::client::PROTOCOL_TYPE_DATAGRAM2, false); // datagram2
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
LogPrint (eLogError, "Datagram: datagram type ", (int)session->GetVersion (), " is not supported");
|
||||
}
|
||||
if (msg) session->SendMsg(msg);
|
||||
auto msg = CreateDataMessage ({{m_From.data (), m_From.size ()}, {m_Signature.data (), m_Signature.size ()}, {payload, len}},
|
||||
fromPort, toPort, false, !session->IsRatchets ()); // datagram
|
||||
session->SendMsg(msg);
|
||||
}
|
||||
}
|
||||
|
||||
void DatagramDestination::SendRawDatagram (std::shared_ptr<DatagramSession> session, const uint8_t * payload, size_t len, uint16_t fromPort, uint16_t toPort)
|
||||
{
|
||||
if (session)
|
||||
session->SendMsg(CreateDataMessage ({{payload, len}}, fromPort, toPort, i2p::client::PROTOCOL_TYPE_RAW, !session->IsRatchets ())); // raw
|
||||
session->SendMsg(CreateDataMessage ({{payload, len}}, fromPort, toPort, true, !session->IsRatchets ())); // raw
|
||||
}
|
||||
|
||||
void DatagramDestination::FlushSendQueue (std::shared_ptr<DatagramSession> session)
|
||||
|
|
@ -128,36 +85,14 @@ namespace datagram
|
|||
session->FlushSendQueue ();
|
||||
}
|
||||
|
||||
void DatagramDestination::HandleDatagram (uint16_t fromPort, uint16_t toPort,
|
||||
const uint8_t * buf, size_t len, i2p::garlic::ECIESX25519AEADRatchetSession * from)
|
||||
void DatagramDestination::HandleDatagram (uint16_t fromPort, uint16_t toPort,uint8_t * const &buf, size_t len)
|
||||
{
|
||||
i2p::data::IdentityEx identity;
|
||||
size_t identityLen = identity.FromBuffer (buf, len);
|
||||
if (!identityLen) return;
|
||||
const uint8_t * signature = buf + identityLen;
|
||||
size_t headerLen = identityLen + identity.GetSignatureLen ();
|
||||
|
||||
std::shared_ptr<i2p::data::LeaseSet> ls;
|
||||
bool verified = false;
|
||||
if (from)
|
||||
{
|
||||
ls = m_Owner->FindLeaseSet (identity.GetIdentHash ());
|
||||
if (ls)
|
||||
{
|
||||
uint8_t staticKey[32];
|
||||
ls->Encrypt (nullptr, staticKey);
|
||||
if (!memcmp (from->GetRemoteStaticKey (), staticKey, 32))
|
||||
verified = true;
|
||||
else
|
||||
{
|
||||
LogPrint (eLogError, "Datagram: Remote LeaseSet static key mismatch for datagram from ",
|
||||
identity.GetIdentHash ().ToBase32 ());
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!verified)
|
||||
{
|
||||
if (identity.GetSigningKeyType () == i2p::data::SIGNING_KEY_TYPE_DSA_SHA1)
|
||||
{
|
||||
uint8_t hash[32];
|
||||
|
|
@ -166,16 +101,15 @@ namespace datagram
|
|||
}
|
||||
else
|
||||
verified = identity.Verify (buf + headerLen, len - headerLen, signature);
|
||||
}
|
||||
|
||||
if (verified)
|
||||
{
|
||||
auto session = ObtainSession (identity.GetIdentHash());
|
||||
if (ls) session->SetRemoteLeaseSet (ls);
|
||||
auto h = identity.GetIdentHash();
|
||||
auto session = ObtainSession(h);
|
||||
session->Ack();
|
||||
auto r = FindReceiver(toPort);
|
||||
if(r)
|
||||
r(identity, fromPort, toPort, buf + headerLen, len - headerLen, nullptr);
|
||||
r(identity, fromPort, toPort, buf + headerLen, len -headerLen);
|
||||
else
|
||||
LogPrint (eLogWarning, "DatagramDestination: no receiver for port ", toPort);
|
||||
}
|
||||
|
|
@ -193,157 +127,6 @@ namespace datagram
|
|||
LogPrint (eLogWarning, "DatagramDestination: no receiver for raw datagram");
|
||||
}
|
||||
|
||||
void DatagramDestination::HandleDatagram2 (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len,
|
||||
i2p::garlic::ECIESX25519AEADRatchetSession * from)
|
||||
{
|
||||
if (len < 433)
|
||||
{
|
||||
LogPrint (eLogWarning, "Datagram: datagram2 is too short ", len);
|
||||
return;
|
||||
}
|
||||
i2p::data::IdentityEx identity;
|
||||
size_t identityLen = identity.FromBuffer (buf, len);
|
||||
if (!identityLen) return;
|
||||
size_t signatureLen = identity.GetSignatureLen ();
|
||||
if (signatureLen + identityLen > len) return;
|
||||
|
||||
std::shared_ptr<i2p::data::LeaseSet> ls;
|
||||
bool verified = false;
|
||||
if (from)
|
||||
{
|
||||
ls = m_Owner->FindLeaseSet (identity.GetIdentHash ());
|
||||
if (ls)
|
||||
{
|
||||
uint8_t staticKey[32];
|
||||
ls->Encrypt (nullptr, staticKey);
|
||||
if (!memcmp (from->GetRemoteStaticKey (), staticKey, 32))
|
||||
verified = true;
|
||||
else
|
||||
{
|
||||
LogPrint (eLogError, "Datagram: Remote LeaseSet static key mismatch for datagram2 from ",
|
||||
identity.GetIdentHash ().ToBase32 ());
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
const uint8_t * flags = buf + identityLen;
|
||||
size_t offset = identityLen + 2;
|
||||
bool isOptions = false;
|
||||
if (flags[1] & DATAGRAM2_FLAG_OPTIONS)
|
||||
{
|
||||
isOptions = true;
|
||||
m_Options.CleanUp ();
|
||||
auto optionsLen = m_Options.FromBuffer (buf + offset, len - offset);
|
||||
if (optionsLen)
|
||||
offset += optionsLen;
|
||||
else
|
||||
{
|
||||
LogPrint (eLogWarning, "Datagram: datagram2 can't read options");
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (offset > len)
|
||||
{
|
||||
LogPrint (eLogWarning, "Datagram: datagram2 is too short ", len, " expected ", offset);
|
||||
return;
|
||||
}
|
||||
if (!verified)
|
||||
{
|
||||
std::shared_ptr<i2p::crypto::Verifier> transientVerifier;
|
||||
if (flags[1] & DATAGRAM2_FLAG_OFFLINE_SIGNATURE)
|
||||
{
|
||||
transientVerifier = i2p::data::ProcessOfflineSignature (&identity, buf, len, offset);
|
||||
if (!transientVerifier)
|
||||
{
|
||||
LogPrint (eLogWarning, "Datagram: datagram2 offline signature failed");
|
||||
return;
|
||||
}
|
||||
signatureLen = transientVerifier->GetSignatureLen ();
|
||||
}
|
||||
std::vector<uint8_t> signedData (len + 32 - identityLen - signatureLen);
|
||||
memcpy (signedData.data (), identity.GetIdentHash (), 32);
|
||||
memcpy (signedData.data () + 32, buf + identityLen, signedData.size () - 32);
|
||||
verified = transientVerifier ? transientVerifier->Verify (signedData.data (), signedData.size (), buf + len - signatureLen) :
|
||||
identity.Verify (signedData.data (), signedData.size (), buf + len - signatureLen);
|
||||
if (!verified)
|
||||
{
|
||||
LogPrint (eLogWarning, "Datagram: datagram2 signature verification failed");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
auto session = ObtainSession (identity.GetIdentHash());
|
||||
session->SetVersion (eDatagramV2);
|
||||
if (ls) session->SetRemoteLeaseSet (ls);
|
||||
session->Ack();
|
||||
auto r = FindReceiver(toPort);
|
||||
if(r)
|
||||
r(identity, fromPort, toPort, buf + offset, len - offset - signatureLen, isOptions ? &m_Options : nullptr);
|
||||
else
|
||||
LogPrint (eLogWarning, "DatagramDestination: no receiver for port ", toPort);
|
||||
}
|
||||
|
||||
void DatagramDestination::HandleDatagram3 (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len,
|
||||
i2p::garlic::ECIESX25519AEADRatchetSession * from)
|
||||
{
|
||||
if (len < 34)
|
||||
{
|
||||
LogPrint (eLogWarning, "Datagram: datagram3 is too short ", len);
|
||||
return;
|
||||
}
|
||||
if (from)
|
||||
{
|
||||
i2p::data::IdentHash ident(buf);
|
||||
auto ls = m_Owner->FindLeaseSet (ident);
|
||||
if (ls)
|
||||
{
|
||||
uint8_t staticKey[32];
|
||||
ls->Encrypt (nullptr, staticKey);
|
||||
if (!memcmp (from->GetRemoteStaticKey (), staticKey, 32))
|
||||
{
|
||||
auto session = ObtainSession (ident);
|
||||
session->SetVersion (eDatagramV3);
|
||||
session->SetRemoteLeaseSet (ls);
|
||||
auto r = FindReceiver(toPort);
|
||||
if (r)
|
||||
{
|
||||
const uint8_t * flags = buf + 32;
|
||||
size_t offset = 34;
|
||||
bool isOptions = false;
|
||||
if (flags[1] & DATAGRAM3_FLAG_OPTIONS)
|
||||
{
|
||||
isOptions = true;
|
||||
m_Options.CleanUp ();
|
||||
auto optionsLen = m_Options.FromBuffer (buf + offset, len - offset);
|
||||
if (optionsLen)
|
||||
offset += optionsLen;
|
||||
else
|
||||
{
|
||||
LogPrint (eLogWarning, "Datagram: datagram3 can't read options");
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (offset > len)
|
||||
{
|
||||
LogPrint (eLogWarning, "Datagram: datagram3 is too short ", len, " expected ", offset);
|
||||
return;
|
||||
}
|
||||
r(*ls->GetIdentity (), fromPort, toPort, buf + offset, len - offset, isOptions ? &m_Options : nullptr);
|
||||
}
|
||||
else
|
||||
LogPrint (eLogWarning, "Datagram: no receiver for port ", toPort);
|
||||
session->Ack ();
|
||||
}
|
||||
else
|
||||
LogPrint (eLogError, "Datagram: Remote LeaseSet static key mismatch for datagram3 from ", ident.ToBase32 ());
|
||||
}
|
||||
else
|
||||
LogPrint (eLogError, "Datagram: No remote LeaseSet for ", ident.ToBase32 ());
|
||||
}
|
||||
else
|
||||
LogPrint (eLogInfo, "Datagram: datagram3 received from non-ratchets session");
|
||||
}
|
||||
|
||||
void DatagramDestination::SetReceiver (const Receiver& receiver, uint16_t port)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_ReceiversMutex);
|
||||
|
|
@ -412,31 +195,17 @@ namespace datagram
|
|||
return r;
|
||||
}
|
||||
|
||||
void DatagramDestination::HandleDataMessagePayload (uint16_t fromPort, uint16_t toPort,
|
||||
const uint8_t * buf, size_t len, uint8_t protocolType, i2p::garlic::ECIESX25519AEADRatchetSession * from)
|
||||
void DatagramDestination::HandleDataMessagePayload (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len, bool isRaw)
|
||||
{
|
||||
// unzip it
|
||||
uint8_t uncompressed[MAX_DATAGRAM_SIZE];
|
||||
size_t uncompressedLen = m_Inflator.Inflate (buf, len, uncompressed, MAX_DATAGRAM_SIZE);
|
||||
if (uncompressedLen)
|
||||
{
|
||||
switch (protocolType)
|
||||
{
|
||||
case i2p::client::PROTOCOL_TYPE_RAW:
|
||||
if (isRaw)
|
||||
HandleRawDatagram (fromPort, toPort, uncompressed, uncompressedLen);
|
||||
break;
|
||||
case i2p::client::PROTOCOL_TYPE_DATAGRAM3:
|
||||
HandleDatagram3 (fromPort, toPort, uncompressed, uncompressedLen, from);
|
||||
break;
|
||||
case i2p::client::PROTOCOL_TYPE_DATAGRAM:
|
||||
HandleDatagram (fromPort, toPort, uncompressed, uncompressedLen, from);
|
||||
break;
|
||||
case i2p::client::PROTOCOL_TYPE_DATAGRAM2:
|
||||
HandleDatagram2 (fromPort, toPort, uncompressed, uncompressedLen, from);
|
||||
break;
|
||||
default:
|
||||
LogPrint (eLogInfo, "Datagram: unknown protocol type ", protocolType);
|
||||
};
|
||||
else
|
||||
HandleDatagram (fromPort, toPort, uncompressed, uncompressedLen);
|
||||
}
|
||||
else
|
||||
LogPrint (eLogWarning, "Datagram: decompression failed");
|
||||
|
|
@ -444,7 +213,7 @@ namespace datagram
|
|||
|
||||
std::shared_ptr<I2NPMessage> DatagramDestination::CreateDataMessage (
|
||||
const std::vector<std::pair<const uint8_t *, size_t> >& payloads,
|
||||
uint16_t fromPort, uint16_t toPort, uint8_t protocolType, bool checksum)
|
||||
uint16_t fromPort, uint16_t toPort, bool isRaw, bool checksum)
|
||||
{
|
||||
size_t size;
|
||||
auto msg = m_I2NPMsgsPool.AcquireShared ();
|
||||
|
|
@ -461,7 +230,7 @@ namespace datagram
|
|||
htobe32buf (msg->GetPayload (), size); // length
|
||||
htobe16buf (buf + 4, fromPort); // source port
|
||||
htobe16buf (buf + 6, toPort); // destination port
|
||||
buf[9] = protocolType; // raw or datagram protocol
|
||||
buf[9] = isRaw ? i2p::client::PROTOCOL_TYPE_RAW : i2p::client::PROTOCOL_TYPE_DATAGRAM; // raw or datagram protocol
|
||||
msg->len += size + 4;
|
||||
msg->FillI2NPMessageHeader (eI2NPData, 0, checksum);
|
||||
}
|
||||
|
|
@ -496,16 +265,14 @@ namespace datagram
|
|||
std::shared_ptr<DatagramSession> session = nullptr;
|
||||
std::lock_guard<std::mutex> lock(m_SessionsMutex);
|
||||
auto itr = m_Sessions.find(identity);
|
||||
if (itr == m_Sessions.end())
|
||||
{
|
||||
if (itr == m_Sessions.end()) {
|
||||
// not found, create new session
|
||||
session = std::make_shared<DatagramSession>(m_Owner, identity);
|
||||
session->SetVersion (m_Version);
|
||||
session->Start ();
|
||||
m_Sessions.emplace (identity, session);
|
||||
}
|
||||
else
|
||||
m_Sessions[identity] = session;
|
||||
} else {
|
||||
session = itr->second;
|
||||
}
|
||||
return session;
|
||||
}
|
||||
|
||||
|
|
@ -521,8 +288,9 @@ namespace datagram
|
|||
|
||||
DatagramSession::DatagramSession(std::shared_ptr<i2p::client::ClientDestination> localDestination,
|
||||
const i2p::data::IdentHash & remoteIdent) :
|
||||
m_LocalDestination(localDestination), m_RemoteIdent(remoteIdent),
|
||||
m_LastUse (0), m_LastFlush (0), m_RequestingLS (false), m_Version (eDatagramV1)
|
||||
m_LocalDestination(localDestination),
|
||||
m_RemoteIdent(remoteIdent),
|
||||
m_RequestingLS(false)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
@ -542,12 +310,8 @@ namespace datagram
|
|||
if (msg || m_SendQueue.empty ())
|
||||
m_SendQueue.push_back(msg);
|
||||
// flush queue right away if full
|
||||
if (!msg || m_SendQueue.size() >= DATAGRAM_SEND_QUEUE_MAX_SIZE ||
|
||||
m_LastUse > m_LastFlush + DATAGRAM_MAX_FLUSH_INTERVAL)
|
||||
{
|
||||
if (!msg || m_SendQueue.size() >= DATAGRAM_SEND_QUEUE_MAX_SIZE)
|
||||
FlushSendQueue();
|
||||
m_LastFlush = m_LastUse;
|
||||
}
|
||||
}
|
||||
|
||||
DatagramSession::Info DatagramSession::GetSessionInfo() const
|
||||
|
|
@ -580,7 +344,7 @@ namespace datagram
|
|||
if(path)
|
||||
path->updateTime = i2p::util::GetSecondsSinceEpoch ();
|
||||
if (IsRatchets ())
|
||||
SendMsg (nullptr); // send empty message in case if we don't have some data to send
|
||||
SendMsg (nullptr); // send empty message in case if we have some data to send
|
||||
}
|
||||
|
||||
std::shared_ptr<i2p::garlic::GarlicRoutingPath> DatagramSession::GetSharedRoutingPath ()
|
||||
|
|
@ -613,19 +377,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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto path = m_RoutingSession->GetSharedRoutingPath();
|
||||
if (path && m_RoutingSession->IsRatchets () && m_RoutingSession->CleanupUnconfirmedTags ())
|
||||
if (path && m_RoutingSession->IsRatchets () &&
|
||||
m_LastUse > m_RoutingSession->GetLastActivityTimestamp ()*1000 + DATAGRAM_SESSION_PATH_TIMEOUT)
|
||||
{
|
||||
LogPrint (eLogDebug, "Datagram: path reset");
|
||||
m_RoutingSession->SetSharedRoutingPath (nullptr);
|
||||
path = nullptr;
|
||||
}
|
||||
|
|
@ -653,14 +413,7 @@ namespace datagram
|
|||
auto sz = ls.size();
|
||||
if (sz)
|
||||
{
|
||||
int idx = -1;
|
||||
if (m_LocalDestination)
|
||||
{
|
||||
auto pool = m_LocalDestination->GetTunnelPool ();
|
||||
if (pool)
|
||||
idx = pool->GetRng ()() % sz;
|
||||
}
|
||||
if (idx < 0) idx = rand () % sz;
|
||||
auto idx = rand() % sz;
|
||||
path->remoteLease = ls[idx];
|
||||
}
|
||||
else
|
||||
|
|
@ -686,14 +439,7 @@ namespace datagram
|
|||
auto sz = ls.size();
|
||||
if (sz)
|
||||
{
|
||||
int idx = -1;
|
||||
if (m_LocalDestination)
|
||||
{
|
||||
auto pool = m_LocalDestination->GetTunnelPool ();
|
||||
if (pool)
|
||||
idx = pool->GetRng ()() % sz;
|
||||
}
|
||||
if (idx < 0) idx = rand () % sz;
|
||||
auto idx = rand() % sz;
|
||||
path->remoteLease = ls[idx];
|
||||
}
|
||||
else
|
||||
|
|
|
|||
|
|
@ -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,8 +20,6 @@
|
|||
#include "LeaseSet.h"
|
||||
#include "I2NPProtocol.h"
|
||||
#include "Garlic.h"
|
||||
#include "util.h"
|
||||
#include "ECIESX25519AEADRatchetSession.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
|
|
@ -33,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
|
||||
|
|
@ -43,19 +43,6 @@ namespace datagram
|
|||
const uint64_t DATAGRAM_SESSION_PATH_MIN_LIFETIME = 5 * 1000;
|
||||
// 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
|
||||
|
||||
enum DatagramVersion
|
||||
{
|
||||
eDatagramV1 = 1,
|
||||
eDatagramV2 = 2,
|
||||
eDatagramV3 = 3,
|
||||
};
|
||||
|
||||
constexpr uint8_t DATAGRAM2_FLAG_OPTIONS = 0x10;
|
||||
constexpr uint8_t DATAGRAM2_FLAG_OFFLINE_SIGNATURE = 0x20;
|
||||
constexpr uint8_t DATAGRAM3_FLAG_OPTIONS = 0x10;
|
||||
|
||||
class DatagramSession : public std::enable_shared_from_this<DatagramSession>
|
||||
{
|
||||
|
|
@ -78,12 +65,6 @@ namespace datagram
|
|||
uint64_t LastActivity() const { return m_LastUse; }
|
||||
|
||||
bool IsRatchets () const { return m_RoutingSession && m_RoutingSession->IsRatchets (); }
|
||||
void SetRemoteLeaseSet (std::shared_ptr<const i2p::data::LeaseSet> ls) { m_RemoteLeaseSet = ls; }
|
||||
|
||||
DatagramVersion GetVersion () const { return m_Version; }
|
||||
void SetVersion (DatagramVersion version) { m_Version = version; }
|
||||
|
||||
void DropSharedRoutingPath () { if (m_RoutingSession) m_RoutingSession->SetSharedRoutingPath (nullptr); }
|
||||
|
||||
struct Info
|
||||
{
|
||||
|
|
@ -117,9 +98,8 @@ namespace datagram
|
|||
std::shared_ptr<i2p::garlic::GarlicRoutingSession> m_RoutingSession;
|
||||
std::vector<std::shared_ptr<i2p::garlic::GarlicRoutingSession> > m_PendingRoutingSessions;
|
||||
std::vector<std::shared_ptr<I2NPMessage> > m_SendQueue;
|
||||
uint64_t m_LastUse, m_LastFlush; // milliseconds
|
||||
uint64_t m_LastUse;
|
||||
bool m_RequestingLS;
|
||||
DatagramVersion m_Version;
|
||||
};
|
||||
|
||||
typedef std::shared_ptr<DatagramSession> DatagramSession_ptr;
|
||||
|
|
@ -127,27 +107,25 @@ namespace datagram
|
|||
const size_t MAX_DATAGRAM_SIZE = 32768;
|
||||
class DatagramDestination
|
||||
{
|
||||
typedef std::function<void (const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort,
|
||||
const uint8_t * buf, size_t len, const i2p::util::Mapping * options)> Receiver;
|
||||
typedef std::function<void (const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len)> Receiver;
|
||||
typedef std::function<void (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len)> RawReceiver;
|
||||
|
||||
public:
|
||||
|
||||
DatagramDestination (std::shared_ptr<i2p::client::ClientDestination> owner, bool gzip, DatagramVersion version);
|
||||
DatagramDestination (std::shared_ptr<i2p::client::ClientDestination> owner, bool gzip);
|
||||
~DatagramDestination ();
|
||||
|
||||
void SendDatagramTo (const uint8_t * payload, size_t len, const i2p::data::IdentHash& ident, uint16_t fromPort = 0, uint16_t toPort = 0);
|
||||
void SendRawDatagramTo (const uint8_t * payload, size_t len, const i2p::data::IdentHash& ident, uint16_t fromPort = 0, uint16_t toPort = 0);
|
||||
void SendDatagramTo (const uint8_t * payload, size_t len, const i2p::data::IdentHash & ident, uint16_t fromPort = 0, uint16_t toPort = 0);
|
||||
void SendRawDatagramTo (const uint8_t * payload, size_t len, const i2p::data::IdentHash & ident, uint16_t fromPort = 0, uint16_t toPort = 0);
|
||||
// TODO: implement calls from other thread from SAM
|
||||
|
||||
std::shared_ptr<DatagramSession> GetSession(const i2p::data::IdentHash & ident);
|
||||
void SendDatagram (std::shared_ptr<DatagramSession> session, const uint8_t * payload, size_t len,
|
||||
uint16_t fromPort, uint16_t toPort, const i2p::util::Mapping * options = nullptr);
|
||||
void SendDatagram (std::shared_ptr<DatagramSession> session, const uint8_t * payload, size_t len, uint16_t fromPort, uint16_t toPort);
|
||||
void SendRawDatagram (std::shared_ptr<DatagramSession> session, const uint8_t * payload, size_t len, uint16_t fromPort, uint16_t toPort);
|
||||
void FlushSendQueue (std::shared_ptr<DatagramSession> session);
|
||||
|
||||
void HandleDataMessagePayload (uint16_t fromPort, uint16_t toPort,
|
||||
const uint8_t * buf, size_t len, uint8_t protocolType, i2p::garlic::ECIESX25519AEADRatchetSession * from);
|
||||
void HandleDataMessagePayload (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len, bool isRaw = false);
|
||||
|
||||
|
||||
void SetReceiver (const Receiver& receiver, uint16_t port);
|
||||
void ResetReceiver (uint16_t port);
|
||||
|
|
@ -165,15 +143,10 @@ namespace datagram
|
|||
std::shared_ptr<DatagramSession> ObtainSession(const i2p::data::IdentHash & ident);
|
||||
|
||||
std::shared_ptr<I2NPMessage> CreateDataMessage (const std::vector<std::pair<const uint8_t *, size_t> >& payloads,
|
||||
uint16_t fromPort, uint16_t toPort, uint8_t protocolType, bool checksum = true);
|
||||
uint16_t fromPort, uint16_t toPort, bool isRaw = false, bool checksum = true);
|
||||
|
||||
void HandleDatagram (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len,
|
||||
i2p::garlic::ECIESX25519AEADRatchetSession * from);
|
||||
void HandleDatagram (uint16_t fromPort, uint16_t toPort, uint8_t *const& buf, size_t len);
|
||||
void HandleRawDatagram (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len);
|
||||
void HandleDatagram2 (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len,
|
||||
i2p::garlic::ECIESX25519AEADRatchetSession * from);
|
||||
void HandleDatagram3 (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len,
|
||||
i2p::garlic::ECIESX25519AEADRatchetSession * from);
|
||||
|
||||
Receiver FindReceiver(uint16_t port);
|
||||
RawReceiver FindRawReceiver(uint16_t port);
|
||||
|
|
@ -195,10 +168,8 @@ namespace datagram
|
|||
std::unordered_map<uint16_t, RawReceiver> m_RawReceiversByPorts;
|
||||
|
||||
bool m_Gzip; // gzip compression of data messages
|
||||
DatagramVersion m_Version; // default for destination
|
||||
i2p::data::GzipInflator m_Inflator;
|
||||
std::unique_ptr<i2p::data::GzipDeflator> m_Deflator;
|
||||
i2p::util::Mapping m_Options;
|
||||
std::vector<uint8_t> m_From, m_Signature;
|
||||
i2p::util::MemoryPool<I2NPMessageBuffer<I2NP_MAX_MESSAGE_SIZE> > m_I2NPMsgsPool;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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,10 +11,8 @@
|
|||
#include <string>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
#include <charconv>
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include "Crypto.h"
|
||||
#include "ECIESX25519AEADRatchetSession.h"
|
||||
#include "Log.h"
|
||||
#include "FS.h"
|
||||
#include "Timestamp.h"
|
||||
|
|
@ -25,8 +23,8 @@ namespace i2p
|
|||
{
|
||||
namespace client
|
||||
{
|
||||
LeaseSetDestination::LeaseSetDestination (boost::asio::io_context& service,
|
||||
bool isPublic, const i2p::util::Mapping * params):
|
||||
LeaseSetDestination::LeaseSetDestination (boost::asio::io_service& service,
|
||||
bool isPublic, const std::map<std::string, std::string> * params):
|
||||
m_Service (service), m_IsPublic (isPublic), m_PublishReplyToken (0),
|
||||
m_LastSubmissionTime (0), m_PublishConfirmationTimer (m_Service),
|
||||
m_PublishVerificationTimer (m_Service), m_PublishDelayTimer (m_Service), m_CleanupTimer (m_Service),
|
||||
|
|
@ -39,29 +37,41 @@ namespace client
|
|||
int inVar = DEFAULT_INBOUND_TUNNELS_LENGTH_VARIANCE;
|
||||
int outVar = DEFAULT_OUTBOUND_TUNNELS_LENGTH_VARIANCE;
|
||||
int numTags = DEFAULT_TAGS_TO_SEND;
|
||||
bool isHighBandwidth = true;
|
||||
std::shared_ptr<std::vector<i2p::data::IdentHash> > explicitPeers;
|
||||
try
|
||||
{
|
||||
if (params)
|
||||
{
|
||||
params->Get (I2CP_PARAM_INBOUND_TUNNEL_LENGTH, inLen);
|
||||
params->Get (I2CP_PARAM_OUTBOUND_TUNNEL_LENGTH, outLen);
|
||||
params->Get (I2CP_PARAM_INBOUND_TUNNELS_QUANTITY, inQty);
|
||||
params->Get (I2CP_PARAM_OUTBOUND_TUNNELS_QUANTITY, outQty);
|
||||
params->Get (I2CP_PARAM_INBOUND_TUNNELS_LENGTH_VARIANCE, inVar);
|
||||
params->Get (I2CP_PARAM_OUTBOUND_TUNNELS_LENGTH_VARIANCE, outVar);
|
||||
params->Get (I2CP_PARAM_TAGS_TO_SEND, numTags);
|
||||
auto it = params->find (I2CP_PARAM_INBOUND_TUNNEL_LENGTH);
|
||||
if (it != params->end ())
|
||||
inLen = std::stoi(it->second);
|
||||
it = params->find (I2CP_PARAM_OUTBOUND_TUNNEL_LENGTH);
|
||||
if (it != params->end ())
|
||||
outLen = std::stoi(it->second);
|
||||
it = params->find (I2CP_PARAM_INBOUND_TUNNELS_QUANTITY);
|
||||
if (it != params->end ())
|
||||
inQty = std::stoi(it->second);
|
||||
it = params->find (I2CP_PARAM_OUTBOUND_TUNNELS_QUANTITY);
|
||||
if (it != params->end ())
|
||||
outQty = std::stoi(it->second);
|
||||
it = params->find (I2CP_PARAM_INBOUND_TUNNELS_LENGTH_VARIANCE);
|
||||
if (it != params->end ())
|
||||
inVar = std::stoi(it->second);
|
||||
it = params->find (I2CP_PARAM_OUTBOUND_TUNNELS_LENGTH_VARIANCE);
|
||||
if (it != params->end ())
|
||||
outVar = std::stoi(it->second);
|
||||
it = params->find (I2CP_PARAM_TAGS_TO_SEND);
|
||||
if (it != params->end ())
|
||||
numTags = std::stoi(it->second);
|
||||
LogPrint (eLogInfo, "Destination: Parameters for tunnel set to: ", inQty, " inbound (", inLen, " hops), ", outQty, " outbound (", outLen, " hops), ", numTags, " tags");
|
||||
int ratchetsInboundTags = 0;
|
||||
if (params->Get (I2CP_PARAM_RATCHET_INBOUND_TAGS, ratchetsInboundTags))
|
||||
SetNumRatchetInboundTags (ratchetsInboundTags);
|
||||
auto explicitPeersStr = (*params)[I2CP_PARAM_EXPLICIT_PEERS];
|
||||
if (!explicitPeersStr.empty ())
|
||||
it = params->find (I2CP_PARAM_RATCHET_INBOUND_TAGS);
|
||||
if (it != params->end ())
|
||||
SetNumRatchetInboundTags (std::stoi(it->second));
|
||||
it = params->find (I2CP_PARAM_EXPLICIT_PEERS);
|
||||
if (it != params->end ())
|
||||
{
|
||||
explicitPeers = std::make_shared<std::vector<i2p::data::IdentHash> >();
|
||||
std::string str (explicitPeersStr);
|
||||
std::stringstream ss(str);
|
||||
std::stringstream ss(it->second);
|
||||
std::string b64;
|
||||
while (std::getline (ss, b64, ','))
|
||||
{
|
||||
|
|
@ -71,37 +81,46 @@ namespace client
|
|||
LogPrint (eLogInfo, "Destination: Added to explicit peers list: ", b64);
|
||||
}
|
||||
}
|
||||
m_Nickname = (*params)[I2CP_PARAM_INBOUND_NICKNAME];
|
||||
if (m_Nickname.empty ()) // try outbound
|
||||
m_Nickname = (*params)[I2CP_PARAM_OUTBOUND_NICKNAME];
|
||||
it = params->find (I2CP_PARAM_INBOUND_NICKNAME);
|
||||
if (it != params->end ()) m_Nickname = it->second;
|
||||
else // try outbound
|
||||
{
|
||||
it = params->find (I2CP_PARAM_OUTBOUND_NICKNAME);
|
||||
if (it != params->end ()) m_Nickname = it->second;
|
||||
// otherwise we set default nickname in Start when we know local address
|
||||
params->Get (I2CP_PARAM_DONT_PUBLISH_LEASESET, m_IsPublic); // override isPublic
|
||||
params->Get (I2CP_PARAM_LEASESET_TYPE, m_LeaseSetType);
|
||||
}
|
||||
it = params->find (I2CP_PARAM_DONT_PUBLISH_LEASESET);
|
||||
if (it != params->end ())
|
||||
{
|
||||
// oveeride isPublic
|
||||
m_IsPublic = (it->second != "true");
|
||||
}
|
||||
it = params->find (I2CP_PARAM_LEASESET_TYPE);
|
||||
if (it != params->end ())
|
||||
m_LeaseSetType = std::stoi(it->second);
|
||||
if (m_LeaseSetType == i2p::data::NETDB_STORE_TYPE_ENCRYPTED_LEASESET2)
|
||||
{
|
||||
// authentication for encrypted LeaseSet
|
||||
int authType = 0;
|
||||
if (params->Get (I2CP_PARAM_LEASESET_AUTH_TYPE, authType))
|
||||
it = params->find (I2CP_PARAM_LEASESET_AUTH_TYPE);
|
||||
if (it != params->end ())
|
||||
{
|
||||
auto authType = std::stoi (it->second);
|
||||
if (authType >= i2p::data::ENCRYPTED_LEASESET_AUTH_TYPE_NONE && authType <= i2p::data::ENCRYPTED_LEASESET_AUTH_TYPE_PSK)
|
||||
m_AuthType = authType;
|
||||
else
|
||||
LogPrint (eLogError, "Destination: Unknown auth type: ", authType);
|
||||
}
|
||||
}
|
||||
auto leaseSetPrivKey = (*params)[I2CP_PARAM_LEASESET_PRIV_KEY];
|
||||
if (!leaseSetPrivKey.empty ())
|
||||
it = params->find (I2CP_PARAM_LEASESET_PRIV_KEY);
|
||||
if (it != params->end ())
|
||||
{
|
||||
m_LeaseSetPrivKey.reset (new i2p::data::Tag<32>());
|
||||
if (m_LeaseSetPrivKey->FromBase64 (leaseSetPrivKey) != 32)
|
||||
if (m_LeaseSetPrivKey->FromBase64 (it->second) != 32)
|
||||
{
|
||||
LogPrint(eLogCritical, "Destination: Invalid value i2cp.leaseSetPrivKey: ", leaseSetPrivKey);
|
||||
LogPrint(eLogCritical, "Destination: Invalid value i2cp.leaseSetPrivKey: ", it->second);
|
||||
m_LeaseSetPrivKey.reset (nullptr);
|
||||
}
|
||||
}
|
||||
int streamingProfile = 0;
|
||||
if (params->Get (I2CP_PARAM_STREAMING_PROFILE, streamingProfile))
|
||||
isHighBandwidth = streamingProfile != STREAMING_PROFILE_INTERACTIVE;
|
||||
}
|
||||
}
|
||||
catch (std::exception & ex)
|
||||
|
|
@ -109,22 +128,21 @@ namespace client
|
|||
LogPrint(eLogError, "Destination: Unable to parse parameters for destination: ", ex.what());
|
||||
}
|
||||
SetNumTags (numTags);
|
||||
m_Pool = i2p::tunnel::tunnels.CreateTunnelPool (inLen, outLen, inQty, outQty, inVar, outVar, isHighBandwidth);
|
||||
m_Pool = i2p::tunnel::tunnels.CreateTunnelPool (inLen, outLen, inQty, outQty, inVar, outVar);
|
||||
if (explicitPeers)
|
||||
m_Pool->SetExplicitPeers (explicitPeers);
|
||||
if(params)
|
||||
{
|
||||
int maxLatency = 0;
|
||||
if (params->Get (I2CP_PARAM_MAX_TUNNEL_LATENCY, maxLatency))
|
||||
{
|
||||
int minLatency = 0;
|
||||
if (params->Get (I2CP_PARAM_MIN_TUNNEL_LATENCY, minLatency))
|
||||
{
|
||||
if (minLatency > 0 && maxLatency > 0)
|
||||
{
|
||||
auto itr = params->find(I2CP_PARAM_MAX_TUNNEL_LATENCY);
|
||||
if (itr != params->end()) {
|
||||
auto maxlatency = std::stoi(itr->second);
|
||||
itr = params->find(I2CP_PARAM_MIN_TUNNEL_LATENCY);
|
||||
if (itr != params->end()) {
|
||||
auto minlatency = std::stoi(itr->second);
|
||||
if ( minlatency > 0 && maxlatency > 0 ) {
|
||||
// set tunnel pool latency
|
||||
LogPrint(eLogInfo, "Destination: Requiring tunnel latency [", minLatency, "ms, ", maxLatency, "ms]");
|
||||
m_Pool->RequireLatency(minLatency, maxLatency);
|
||||
LogPrint(eLogInfo, "Destination: Requiring tunnel latency [", minlatency, "ms, ", maxlatency, "ms]");
|
||||
m_Pool->RequireLatency(minlatency, maxlatency);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -146,7 +164,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));
|
||||
}
|
||||
|
|
@ -165,30 +183,41 @@ namespace client
|
|||
CleanUp (); // GarlicDestination
|
||||
}
|
||||
|
||||
bool LeaseSetDestination::Reconfigure(const i2p::util::Mapping& params)
|
||||
bool LeaseSetDestination::Reconfigure(std::map<std::string, std::string> params)
|
||||
{
|
||||
params.Get (I2CP_PARAM_DONT_PUBLISH_LEASESET, m_IsPublic);
|
||||
auto itr = params.find("i2cp.dontPublishLeaseSet");
|
||||
if (itr != params.end())
|
||||
{
|
||||
m_IsPublic = itr->second != "true";
|
||||
}
|
||||
|
||||
int inLen, outLen, inQuant, outQuant, numTags, minLatency, maxLatency;
|
||||
std::map<std::string, int&> intOpts = {
|
||||
{I2CP_PARAM_INBOUND_TUNNEL_LENGTH, inLen},
|
||||
{I2CP_PARAM_OUTBOUND_TUNNEL_LENGTH, outLen},
|
||||
{I2CP_PARAM_INBOUND_TUNNELS_QUANTITY, inQuant},
|
||||
{I2CP_PARAM_OUTBOUND_TUNNELS_QUANTITY, outQuant},
|
||||
{I2CP_PARAM_TAGS_TO_SEND, numTags},
|
||||
{I2CP_PARAM_MIN_TUNNEL_LATENCY, minLatency},
|
||||
{I2CP_PARAM_MAX_TUNNEL_LATENCY, maxLatency}
|
||||
};
|
||||
|
||||
auto numTags = GetNumTags ();
|
||||
params.Get (I2CP_PARAM_TAGS_TO_SEND, numTags);
|
||||
auto numRatchetInboundTags = GetNumRatchetInboundTags ();
|
||||
params.Get (I2CP_PARAM_RATCHET_INBOUND_TAGS, numRatchetInboundTags);
|
||||
auto pool = GetTunnelPool();
|
||||
auto inLen = pool->GetNumInboundHops();
|
||||
params.Get (I2CP_PARAM_INBOUND_TUNNEL_LENGTH, inLen);
|
||||
auto outLen = pool->GetNumOutboundHops();
|
||||
params.Get (I2CP_PARAM_OUTBOUND_TUNNEL_LENGTH, outLen);
|
||||
auto inQuant = pool->GetNumInboundTunnels();
|
||||
params.Get (I2CP_PARAM_INBOUND_TUNNELS_QUANTITY, inQuant);
|
||||
auto outQuant = pool->GetNumOutboundTunnels();
|
||||
params.Get (I2CP_PARAM_OUTBOUND_TUNNELS_QUANTITY, outQuant);
|
||||
int minLatency = 0;
|
||||
params.Get (I2CP_PARAM_MIN_TUNNEL_LATENCY, minLatency);
|
||||
int maxLatency = 0;
|
||||
params.Get (I2CP_PARAM_MAX_TUNNEL_LATENCY, maxLatency);
|
||||
inLen = pool->GetNumInboundHops();
|
||||
outLen = pool->GetNumOutboundHops();
|
||||
inQuant = pool->GetNumInboundTunnels();
|
||||
outQuant = pool->GetNumOutboundTunnels();
|
||||
minLatency = 0;
|
||||
maxLatency = 0;
|
||||
|
||||
SetNumTags (numTags);
|
||||
SetNumRatchetInboundTags (numRatchetInboundTags);
|
||||
for (auto & opt : intOpts)
|
||||
{
|
||||
itr = params.find(opt.first);
|
||||
if(itr != params.end())
|
||||
{
|
||||
opt.second = std::stoi(itr->second);
|
||||
}
|
||||
}
|
||||
pool->RequireLatency(minLatency, maxLatency);
|
||||
return pool->Reconfigure(inLen, outLen, inQuant, outQuant);
|
||||
}
|
||||
|
|
@ -261,7 +290,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 ();
|
||||
|
|
@ -289,7 +318,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);
|
||||
});
|
||||
|
|
@ -306,7 +335,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);
|
||||
});
|
||||
|
|
@ -314,47 +343,28 @@ namespace client
|
|||
|
||||
void LeaseSetDestination::ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg)
|
||||
{
|
||||
if (!msg) return;
|
||||
bool empty = false;
|
||||
{
|
||||
std::lock_guard<std::mutex> 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<std::shared_ptr<I2NPMessage> > receivedMsgs;
|
||||
{
|
||||
std::lock_guard<std::mutex> 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<I2NPMessage> 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)
|
||||
{
|
||||
case eI2NPData:
|
||||
HandleDataMessage (payload, len, from);
|
||||
HandleDataMessage (payload, len);
|
||||
break;
|
||||
case eI2NPDeliveryStatus:
|
||||
HandleDeliveryStatusMessage (bufbe32toh (payload + DELIVERY_STATUS_MSGID_OFFSET));
|
||||
|
|
@ -364,7 +374,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);
|
||||
|
|
@ -379,8 +389,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)
|
||||
{
|
||||
|
|
@ -416,7 +425,7 @@ namespace client
|
|||
leaseSet = it->second;
|
||||
if (leaseSet->IsNewer (buf + offset, len - offset))
|
||||
{
|
||||
leaseSet->Update (buf + offset, len - offset, shared_from_this(), true);
|
||||
leaseSet->Update (buf + offset, len - offset);
|
||||
if (leaseSet->IsValid () && leaseSet->GetIdentHash () == key && !leaseSet->IsExpired ())
|
||||
LogPrint (eLogDebug, "Destination: Remote LeaseSet updated");
|
||||
else
|
||||
|
|
@ -435,29 +444,13 @@ namespace client
|
|||
if (buf[DATABASE_STORE_TYPE_OFFSET] == i2p::data::NETDB_STORE_TYPE_LEASESET)
|
||||
leaseSet = std::make_shared<i2p::data::LeaseSet> (buf + offset, len - offset); // LeaseSet
|
||||
else
|
||||
{
|
||||
leaseSet = std::make_shared<i2p::data::LeaseSet2> (buf[DATABASE_STORE_TYPE_OFFSET],
|
||||
buf + offset, len - offset, true, shared_from_this (),
|
||||
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<i2p::data::LeaseSet2> (buf[DATABASE_STORE_TYPE_OFFSET], buf + offset, len - offset, true, GetPreferredCryptoType () ); // LeaseSet2
|
||||
if (leaseSet->IsValid () && leaseSet->GetIdentHash () == key && !leaseSet->IsExpired ())
|
||||
{
|
||||
if (leaseSet->GetIdentHash () != GetIdentHash ())
|
||||
{
|
||||
LogPrint (eLogDebug, "Destination: New remote LeaseSet added");
|
||||
m_RemoteLeaseSets.insert_or_assign (key, leaseSet);
|
||||
if (from)
|
||||
from->SetDestination (key);
|
||||
m_RemoteLeaseSets[key] = leaseSet;
|
||||
}
|
||||
else
|
||||
LogPrint (eLogDebug, "Destination: Own remote LeaseSet dropped");
|
||||
|
|
@ -480,8 +473,7 @@ namespace client
|
|||
if (request->requestedBlindedKey)
|
||||
{
|
||||
auto ls2 = std::make_shared<i2p::data::LeaseSet2> (buf + offset, len - offset,
|
||||
request->requestedBlindedKey, shared_from_this (),
|
||||
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;
|
||||
|
|
@ -584,8 +576,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));
|
||||
}
|
||||
|
|
@ -593,11 +584,8 @@ 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 ();
|
||||
}
|
||||
|
||||
|
|
@ -664,8 +652,8 @@ namespace client
|
|||
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;
|
||||
|
|
@ -678,13 +666,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);
|
||||
|
|
@ -697,10 +685,23 @@ namespace client
|
|||
{
|
||||
if (m_PublishReplyToken)
|
||||
{
|
||||
LogPrint (eLogWarning, "Destination: Publish confirmation was not received in ", PUBLISH_CONFIRMATION_TIMEOUT, " milliseconds or failed. will try again");
|
||||
m_PublishReplyToken = 0;
|
||||
if (GetIdentity ()->GetCryptoKeyType () == i2p::data::CRYPTO_KEY_TYPE_ELGAMAL)
|
||||
{
|
||||
LogPrint (eLogWarning, "Destination: Publish confirmation was not received in ", PUBLISH_CONFIRMATION_TIMEOUT, " seconds or failed. will try again");
|
||||
Publish ();
|
||||
}
|
||||
else
|
||||
{
|
||||
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));
|
||||
m_PublishVerificationTimer.async_wait (std::bind (&LeaseSetDestination::HandlePublishVerificationTimer,
|
||||
shared_from_this (), std::placeholders::_1));
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -750,10 +751,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;
|
||||
}
|
||||
|
||||
|
|
@ -762,7 +763,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 ();
|
||||
|
|
@ -770,17 +771,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 ())
|
||||
|
|
@ -808,7 +809,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<i2p::data::IdentHash, std::shared_ptr<LeaseSetRequest> >(dest,request));
|
||||
if (ret.second) // inserted
|
||||
{
|
||||
|
|
@ -878,7 +879,7 @@ namespace client
|
|||
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);
|
||||
});
|
||||
|
|
@ -891,7 +892,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));
|
||||
}
|
||||
|
|
@ -908,7 +909,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);
|
||||
|
|
@ -945,8 +946,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));
|
||||
}
|
||||
|
|
@ -960,7 +960,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
|
||||
|
|
@ -968,17 +968,21 @@ namespace client
|
|||
}
|
||||
}
|
||||
|
||||
ClientDestination::ClientDestination (boost::asio::io_context& service, const i2p::data::PrivateKeys& keys,
|
||||
bool isPublic, const i2p::util::Mapping * params):
|
||||
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<std::string, std::string> * 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_StreamingMaxWindowSize (i2p::stream::MAX_WINDOW_SIZE),
|
||||
m_IsStreamingAnswerPings (DEFAULT_ANSWER_PINGS), m_IsStreamingDontSign (DEFAULT_DONT_SIGN),
|
||||
m_LastPort (0), m_DatagramDestination (nullptr), m_RefCounter (0),
|
||||
m_LastPublishedTimestamp (0), m_ReadyChecker(service)
|
||||
m_IsStreamingAnswerPings (DEFAULT_ANSWER_PINGS), m_LastPort (0),
|
||||
m_DatagramDestination (nullptr), m_RefCounter (0),
|
||||
m_ReadyChecker(service)
|
||||
{
|
||||
if (keys.IsOfflineSignature () && GetLeaseSetType () == i2p::data::NETDB_STORE_TYPE_LEASESET)
|
||||
SetLeaseSetType (i2p::data::NETDB_STORE_TYPE_STANDARD_LEASESET2); // offline keys can be published with LS2 only
|
||||
|
|
@ -987,25 +991,17 @@ namespace client
|
|||
std::set<i2p::data::CryptoKeyType> encryptionKeyTypes;
|
||||
if (params)
|
||||
{
|
||||
auto encryptionTypesStr = (*params)[I2CP_PARAM_LEASESET_ENCRYPTION_TYPE];
|
||||
if (!encryptionTypesStr.empty ())
|
||||
auto it = params->find (I2CP_PARAM_LEASESET_ENCRYPTION_TYPE);
|
||||
if (it != params->end ())
|
||||
{
|
||||
// comma-separated values
|
||||
std::vector<std::string> values;
|
||||
boost::split(values, encryptionTypesStr, boost::is_any_of(","));
|
||||
boost::split(values, it->second, boost::is_any_of(","));
|
||||
for (auto& it1: values)
|
||||
{
|
||||
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)
|
||||
{
|
||||
|
|
@ -1016,24 +1012,29 @@ namespace client
|
|||
}
|
||||
}
|
||||
// if no param or valid crypto type use from identity
|
||||
bool isSingleKey = false;
|
||||
if (encryptionKeyTypes.empty ())
|
||||
encryptionKeyTypes.insert ( { GetIdentity ()->GetCryptoKeyType (),
|
||||
#if OPENSSL_PQ
|
||||
i2p::data::CRYPTO_KEY_TYPE_ECIES_MLKEM768_X25519_AEAD,
|
||||
#endif
|
||||
i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD }); // usually 0,4 or 0,6,4 if post quantum
|
||||
{
|
||||
isSingleKey = true;
|
||||
encryptionKeyTypes.insert (GetIdentity ()->GetCryptoKeyType ());
|
||||
}
|
||||
|
||||
for (auto& it: encryptionKeyTypes)
|
||||
{
|
||||
auto encryptionKey = std::make_shared<i2p::crypto::LocalEncryptionKey> (it);
|
||||
auto encryptionKey = new EncryptionKey (it);
|
||||
if (IsPublic ())
|
||||
PersistTemporaryKeys (encryptionKey);
|
||||
PersistTemporaryKeys (encryptionKey, isSingleKey);
|
||||
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 ())
|
||||
|
|
@ -1044,15 +1045,15 @@ namespace client
|
|||
if (params)
|
||||
{
|
||||
// extract streaming params
|
||||
params->Get (I2CP_PARAM_STREAMING_INITIAL_ACK_DELAY, m_StreamingAckDelay);
|
||||
params->Get (I2CP_PARAM_STREAMING_MAX_OUTBOUND_SPEED, m_StreamingOutboundSpeed);
|
||||
params->Get (I2CP_PARAM_STREAMING_MAX_INBOUND_SPEED, m_StreamingInboundSpeed);
|
||||
params->Get (I2CP_PARAM_STREAMING_MAX_CONCURRENT_STREAMS, m_StreamingMaxConcurrentStreams);
|
||||
if (params->Get (I2CP_PARAM_STREAMING_MAX_WINDOW_SIZE, m_StreamingMaxWindowSize) &&
|
||||
(m_StreamingMaxWindowSize < i2p::stream::MIN_WINDOW_SIZE))
|
||||
m_StreamingMaxWindowSize = i2p::stream::MIN_WINDOW_SIZE;
|
||||
params->Get (I2CP_PARAM_STREAMING_ANSWER_PINGS, m_IsStreamingAnswerPings);
|
||||
params->Get (I2CP_PARAM_STREAMING_DONT_SIGN, m_IsStreamingDontSign);
|
||||
auto it = params->find (I2CP_PARAM_STREAMING_INITIAL_ACK_DELAY);
|
||||
if (it != params->end ())
|
||||
m_StreamingAckDelay = std::stoi(it->second);
|
||||
it = params->find (I2CP_PARAM_STREAMING_MAX_OUTBOUND_SPEED);
|
||||
if (it != params->end ())
|
||||
m_StreamingOutboundSpeed = 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
|
||||
|
||||
if (GetLeaseSetType () == i2p::data::NETDB_STORE_TYPE_ENCRYPTED_LEASESET2)
|
||||
{
|
||||
|
|
@ -1100,6 +1101,7 @@ namespace client
|
|||
void ClientDestination::Stop ()
|
||||
{
|
||||
LogPrint(eLogDebug, "Destination: Stopping destination ", GetIdentHash().ToBase32(), ".b32.i2p");
|
||||
LeaseSetDestination::Stop ();
|
||||
m_ReadyChecker.cancel();
|
||||
LogPrint(eLogDebug, "Destination: -> Stopping Streaming Destination");
|
||||
m_StreamingDestination->Stop ();
|
||||
|
|
@ -1121,12 +1123,10 @@ namespace client
|
|||
delete m_DatagramDestination;
|
||||
m_DatagramDestination = nullptr;
|
||||
}
|
||||
LeaseSetDestination::Stop ();
|
||||
LogPrint(eLogDebug, "Destination: -> Stopping done");
|
||||
}
|
||||
|
||||
void ClientDestination::HandleDataMessage (const uint8_t * buf, size_t len,
|
||||
i2p::garlic::ECIESX25519AEADRatchetSession * from)
|
||||
void ClientDestination::HandleDataMessage (const uint8_t * buf, size_t len)
|
||||
{
|
||||
uint32_t length = bufbe32toh (buf);
|
||||
if(length > len - 4)
|
||||
|
|
@ -1151,21 +1151,25 @@ namespace client
|
|||
m_LastPort = toPort;
|
||||
}
|
||||
if (m_LastStreamingDestination)
|
||||
m_LastStreamingDestination->HandleDataMessagePayload (buf, length, from);
|
||||
m_LastStreamingDestination->HandleDataMessagePayload (buf, length);
|
||||
else
|
||||
LogPrint (eLogError, "Destination: Missing streaming destination");
|
||||
}
|
||||
break;
|
||||
case PROTOCOL_TYPE_DATAGRAM:
|
||||
case PROTOCOL_TYPE_RAW:
|
||||
case PROTOCOL_TYPE_DATAGRAM2:
|
||||
case PROTOCOL_TYPE_DATAGRAM3:
|
||||
// datagram protocol
|
||||
if (m_DatagramDestination)
|
||||
m_DatagramDestination->HandleDataMessagePayload (fromPort, toPort, buf, length, buf[9], from);
|
||||
m_DatagramDestination->HandleDataMessagePayload (fromPort, toPort, buf, length);
|
||||
else
|
||||
LogPrint (eLogError, "Destination: Missing datagram destination");
|
||||
break;
|
||||
case PROTOCOL_TYPE_RAW:
|
||||
// raw datagram
|
||||
if (m_DatagramDestination)
|
||||
m_DatagramDestination->HandleDataMessagePayload (fromPort, toPort, buf, length, true);
|
||||
else
|
||||
LogPrint (eLogError, "Destination: Missing raw datagram destination");
|
||||
break;
|
||||
default:
|
||||
LogPrint (eLogError, "Destination: Data: Unexpected protocol ", buf[9]);
|
||||
}
|
||||
|
|
@ -1182,7 +1186,7 @@ namespace client
|
|||
if (leaseSet)
|
||||
{
|
||||
auto stream = CreateStream (leaseSet, port);
|
||||
boost::asio::post (GetService (), [streamRequestComplete, stream]()
|
||||
GetService ().post ([streamRequestComplete, stream]()
|
||||
{
|
||||
streamRequestComplete(stream);
|
||||
});
|
||||
|
|
@ -1354,11 +1358,10 @@ namespace client
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
i2p::datagram::DatagramDestination * ClientDestination::CreateDatagramDestination (bool gzip,
|
||||
i2p::datagram::DatagramVersion version)
|
||||
i2p::datagram::DatagramDestination * ClientDestination::CreateDatagramDestination (bool gzip)
|
||||
{
|
||||
if (!m_DatagramDestination)
|
||||
m_DatagramDestination = new i2p::datagram::DatagramDestination (GetSharedFromThis (), gzip, version);
|
||||
if (m_DatagramDestination == nullptr)
|
||||
m_DatagramDestination = new i2p::datagram::DatagramDestination (GetSharedFromThis (), gzip);
|
||||
return m_DatagramDestination;
|
||||
}
|
||||
|
||||
|
|
@ -1376,56 +1379,32 @@ namespace client
|
|||
return ret;
|
||||
}
|
||||
|
||||
void ClientDestination::PersistTemporaryKeys (std::shared_ptr<i2p::crypto::LocalEncryptionKey> keys)
|
||||
void ClientDestination::PersistTemporaryKeys (EncryptionKey * keys, bool isSingleKey)
|
||||
{
|
||||
if (!keys) return;
|
||||
std::string ident = GetIdentHash().ToBase32();
|
||||
std::string path = i2p::fs::DataDirPath("destinations", ident + "." + std::to_string (keys->keyType) + ".dat");
|
||||
std::string path = i2p::fs::DataDirPath("destinations",
|
||||
isSingleKey ? (ident + ".dat") : (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)
|
||||
if (f) {
|
||||
f.read ((char *)keys->pub, 256);
|
||||
f.read ((char *)keys->priv, 256);
|
||||
return;
|
||||
else
|
||||
LogPrint(eLogWarning, "Destination: Can't read keys from ", path);
|
||||
}
|
||||
|
||||
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<std::shared_ptr<i2p::tunnel::InboundTunnel> >& tunnels)
|
||||
|
|
@ -1433,10 +1412,9 @@ namespace client
|
|||
std::shared_ptr<i2p::data::LocalLeaseSet> 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<i2p::data::LocalLeaseSet> (GetIdentity (), it->second->pub.data (), tunnels);
|
||||
leaseSet = std::make_shared<i2p::data::LocalLeaseSet> (GetIdentity (), m_StandardEncryptionKey->pub, tunnels);
|
||||
// sign
|
||||
Sign (leaseSet->GetBuffer (), leaseSet->GetBufferLen () - leaseSet->GetSignatureLen (), leaseSet->GetSignature ());
|
||||
}
|
||||
|
|
@ -1446,40 +1424,18 @@ namespace client
|
|||
else
|
||||
{
|
||||
// standard LS2 (type 3) first
|
||||
if (m_EncryptionKeys.empty ())
|
||||
{
|
||||
LogPrint (eLogError, "Destinations: No encryption keys");
|
||||
return;
|
||||
}
|
||||
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} );
|
||||
|
||||
i2p::data::LocalLeaseSet2::EncryptionKeys keySections;
|
||||
std::shared_ptr<const i2p::crypto::LocalEncryptionKey> 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
|
||||
|
||||
auto publishedTimestamp = i2p::util::GetSecondsSinceEpoch ();
|
||||
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::LocalLeaseSet2> (i2p::data::NETDB_STORE_TYPE_STANDARD_LEASESET2,
|
||||
m_Keys, keySections, tunnels, IsPublic (), publishedTimestamp, isPublishedEncrypted);
|
||||
m_Keys, keySections, tunnels, IsPublic (), isPublishedEncrypted);
|
||||
if (isPublishedEncrypted) // encrypt if type 5
|
||||
ls2 = std::make_shared<i2p::data::LocalEncryptedLeaseSet2> (ls2, m_Keys, GetAuthType (), m_AuthKeys);
|
||||
leaseSet = ls2;
|
||||
m_LastPublishedTimestamp = publishedTimestamp;
|
||||
}
|
||||
SetLeaseSet (leaseSet);
|
||||
}
|
||||
|
|
@ -1491,22 +1447,11 @@ namespace client
|
|||
|
||||
bool ClientDestination::Decrypt (const uint8_t * encrypted, uint8_t * data, i2p::data::CryptoKeyType preferredCrypto) const
|
||||
{
|
||||
std::shared_ptr<i2p::crypto::LocalEncryptionKey> 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;
|
||||
|
|
@ -1514,31 +1459,19 @@ 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
|
||||
}
|
||||
|
||||
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;
|
||||
return keyType == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD ? (bool)m_ECIESx25519EncryptionKey : (bool)m_StandardEncryptionKey;
|
||||
}
|
||||
|
||||
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 i2p::util::Mapping * params)
|
||||
void ClientDestination::ReadAuthKey (const std::string& group, const std::map<std::string, std::string> * params)
|
||||
{
|
||||
for (const auto& it: params->GetOptions ())
|
||||
for (auto it: *params)
|
||||
if (it.first.length () >= group.length () && !it.first.compare (0, group.length (), group))
|
||||
{
|
||||
auto pos = it.second.find (':');
|
||||
|
|
@ -1563,12 +1496,10 @@ namespace client
|
|||
return false;
|
||||
}
|
||||
|
||||
RunnableClientDestination::RunnableClientDestination (const i2p::data::PrivateKeys& keys,
|
||||
bool isPublic, const i2p::util::Mapping * params):
|
||||
RunnableService ("Destination"), ClientDestination (GetIOService (), keys, isPublic, params)
|
||||
RunnableClientDestination::RunnableClientDestination (const i2p::data::PrivateKeys& keys, bool isPublic, const std::map<std::string, std::string> * params):
|
||||
RunnableService ("Destination"),
|
||||
ClientDestination (GetIOService (), keys, isPublic, params)
|
||||
{
|
||||
if (!GetNickname ().empty ())
|
||||
RunnableService::SetName (GetNickname ());
|
||||
}
|
||||
|
||||
RunnableClientDestination::~RunnableClientDestination ()
|
||||
|
|
|
|||
|
|
@ -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,17 +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 uint8_t PROTOCOL_TYPE_DATAGRAM2 = 19;
|
||||
const uint8_t PROTOCOL_TYPE_DATAGRAM3 = 20;
|
||||
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
|
||||
|
|
@ -91,19 +86,8 @@ namespace client
|
|||
const int DEFAULT_INITIAL_ACK_DELAY = 200; // milliseconds
|
||||
const char I2CP_PARAM_STREAMING_MAX_OUTBOUND_SPEED[] = "i2p.streaming.maxOutboundSpeed"; // bytes/sec
|
||||
const int DEFAULT_MAX_OUTBOUND_SPEED = 1730000000; // no more than 1.73 Gbytes/s
|
||||
const char I2CP_PARAM_STREAMING_MAX_INBOUND_SPEED[] = "i2p.streaming.maxInboundSpeed"; // bytes/sec
|
||||
const int DEFAULT_MAX_INBOUND_SPEED = 1730000000; // no more than 1.73 Gbytes/s
|
||||
const char I2CP_PARAM_STREAMING_ANSWER_PINGS[] = "i2p.streaming.answerPings";
|
||||
const int DEFAULT_ANSWER_PINGS = true;
|
||||
const char I2CP_PARAM_STREAMING_PROFILE[] = "i2p.streaming.profile";
|
||||
const int STREAMING_PROFILE_BULK = 1; // high bandwidth
|
||||
const int STREAMING_PROFILE_INTERACTIVE = 2; // low bandwidth
|
||||
const int DEFAULT_STREAMING_PROFILE = STREAMING_PROFILE_BULK;
|
||||
const char I2CP_PARAM_STREAMING_MAX_CONCURRENT_STREAMS[] = "i2p.streaming.maxConcurrentStreams";
|
||||
const int DEFAULT_MAX_CONCURRENT_STREAMS = 2048;
|
||||
const char I2CP_PARAM_STREAMING_MAX_WINDOW_SIZE[] = "i2p.streaming.maxWindowSize";
|
||||
const char I2CP_PARAM_STREAMING_DONT_SIGN[] = "i2p.streaming.dontSign";
|
||||
const int DEFAULT_DONT_SIGN = false;
|
||||
|
||||
typedef std::function<void (std::shared_ptr<i2p::stream::Stream> stream)> StreamRequestComplete;
|
||||
|
||||
|
|
@ -114,7 +98,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<i2p::data::IdentHash> excluded;
|
||||
uint64_t requestTime;
|
||||
boost::asio::deadline_timer requestTimeoutTimer;
|
||||
|
|
@ -132,16 +116,16 @@ namespace client
|
|||
|
||||
public:
|
||||
|
||||
LeaseSetDestination (boost::asio::io_context& service, bool isPublic, const i2p::util::Mapping * params = nullptr);
|
||||
LeaseSetDestination (boost::asio::io_service& service, bool isPublic, const std::map<std::string, std::string> * 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 ();
|
||||
|
||||
/** i2cp reconfigure */
|
||||
virtual bool Reconfigure(const i2p::util::Mapping& i2cpOpts);
|
||||
virtual bool Reconfigure(std::map<std::string, std::string> i2cpOpts);
|
||||
|
||||
std::shared_ptr<i2p::tunnel::TunnelPool> GetTunnelPool () { return m_Pool; };
|
||||
bool IsReady () const { return m_LeaseSet && !m_LeaseSet->IsExpired () && m_Pool->GetOutboundTunnels ().size () > 0; };
|
||||
|
|
@ -152,15 +136,15 @@ namespace client
|
|||
void CancelDestinationRequestWithEncryptedLeaseSet (std::shared_ptr<const i2p::data::BlindedPublicKey> dest, bool notify = true);
|
||||
|
||||
// implements GarlicDestination
|
||||
std::shared_ptr<const i2p::data::LocalLeaseSet> GetLeaseSet () override;
|
||||
std::shared_ptr<i2p::tunnel::TunnelPool> GetTunnelPool () const override { return m_Pool; }
|
||||
std::shared_ptr<const i2p::data::LocalLeaseSet> GetLeaseSet ();
|
||||
std::shared_ptr<i2p::tunnel::TunnelPool> 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<I2NPMessage> msg) override;
|
||||
void ProcessDeliveryStatusMessage (std::shared_ptr<I2NPMessage> 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<I2NPMessage> msg);
|
||||
void ProcessDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg);
|
||||
void SetLeaseSetUpdated ();
|
||||
|
||||
bool IsPublic () const { return m_IsPublic; };
|
||||
void SetPublic (bool pub) { m_IsPublic = pub; };
|
||||
|
|
@ -168,18 +152,16 @@ 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<const i2p::data::LocalLeaseSet> 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, i2p::garlic::ECIESX25519AEADRatchetSession * from) = 0;
|
||||
virtual void HandleDataMessage (const uint8_t * buf, size_t len) = 0;
|
||||
virtual void CreateNewLeaseSet (const std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> >& tunnels) = 0;
|
||||
|
||||
private:
|
||||
|
|
@ -190,7 +172,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);
|
||||
|
||||
|
|
@ -200,17 +182,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<i2p::data::IdentHash, std::shared_ptr<i2p::data::LeaseSet> > m_RemoteLeaseSets;
|
||||
std::unordered_map<i2p::data::IdentHash, std::shared_ptr<LeaseSetRequest> > m_LeaseSetRequests;
|
||||
|
||||
std::list<std::shared_ptr<I2NPMessage> > m_IncomingMsgsQueue;
|
||||
mutable std::mutex m_IncomingMsgsQueueMutex;
|
||||
|
||||
std::shared_ptr<i2p::tunnel::TunnelPool> m_Pool;
|
||||
std::mutex m_LeaseSetMutex;
|
||||
std::shared_ptr<const i2p::data::LocalLeaseSet> m_LeaseSet;
|
||||
|
|
@ -236,14 +216,25 @@ namespace client
|
|||
|
||||
class ClientDestination: public LeaseSetDestination
|
||||
{
|
||||
struct EncryptionKey
|
||||
{
|
||||
uint8_t pub[256], priv[256];
|
||||
i2p::data::CryptoKeyType keyType;
|
||||
std::shared_ptr<i2p::crypto::CryptoKeyDecryptor> 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,
|
||||
bool isPublic, const i2p::util::Mapping * params = nullptr);
|
||||
ClientDestination (boost::asio::io_service& service, const i2p::data::PrivateKeys& keys,
|
||||
bool isPublic, const std::map<std::string, std::string> * 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); };
|
||||
|
|
@ -271,41 +262,32 @@ namespace client
|
|||
void AcceptOnce (const i2p::stream::StreamingDestination::Acceptor& acceptor);
|
||||
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; }
|
||||
bool IsStreamingDontSign () const { return m_IsStreamingDontSign; }
|
||||
int GetStreamingMaxWindowSize () const { return m_StreamingMaxWindowSize; }
|
||||
|
||||
// datagram
|
||||
i2p::datagram::DatagramDestination * GetDatagramDestination () const { return m_DatagramDestination; };
|
||||
i2p::datagram::DatagramDestination * CreateDatagramDestination (bool gzip = true,
|
||||
i2p::datagram::DatagramVersion version = i2p::datagram::eDatagramV1);
|
||||
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<const i2p::data::IdentityEx> 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<const i2p::data::IdentityEx> 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, i2p::garlic::ECIESX25519AEADRatchetSession * from) override;
|
||||
void CreateNewLeaseSet (const std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> >& tunnels) override;
|
||||
void HandleDataMessage (const uint8_t * buf, size_t len);
|
||||
void CreateNewLeaseSet (const std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> >& tunnels);
|
||||
|
||||
private:
|
||||
|
||||
std::shared_ptr<ClientDestination> GetSharedFromThis () {
|
||||
return std::static_pointer_cast<ClientDestination>(shared_from_this ());
|
||||
}
|
||||
void PersistTemporaryKeys (std::shared_ptr<i2p::crypto::LocalEncryptionKey> keys);
|
||||
void ReadAuthKey (const std::string& group, const i2p::util::Mapping * params);
|
||||
void PersistTemporaryKeys (EncryptionKey * keys, bool isSingleKey);
|
||||
void ReadAuthKey (const std::string& group, const std::map<std::string, std::string> * params);
|
||||
|
||||
template<typename Dest>
|
||||
std::shared_ptr<i2p::stream::Stream> CreateStreamSync (const Dest& dest, uint16_t port);
|
||||
|
|
@ -313,17 +295,17 @@ namespace client
|
|||
private:
|
||||
|
||||
i2p::data::PrivateKeys m_Keys;
|
||||
std::map<i2p::data::CryptoKeyType, std::shared_ptr<i2p::crypto::LocalEncryptionKey> > m_EncryptionKeys; // last is most preferable
|
||||
i2p::data::CryptoKeyType m_PreferredCryptoType;
|
||||
std::unique_ptr<EncryptionKey> m_StandardEncryptionKey;
|
||||
std::unique_ptr<EncryptionKey> m_ECIESx25519EncryptionKey;
|
||||
|
||||
int m_StreamingAckDelay,m_StreamingOutboundSpeed, m_StreamingInboundSpeed, m_StreamingMaxConcurrentStreams, m_StreamingMaxWindowSize;
|
||||
bool m_IsStreamingAnswerPings, m_IsStreamingDontSign;
|
||||
int m_StreamingAckDelay;
|
||||
int m_StreamingOutboundSpeed;
|
||||
bool m_IsStreamingAnswerPings;
|
||||
std::shared_ptr<i2p::stream::StreamingDestination> m_StreamingDestination; // default
|
||||
std::map<uint16_t, std::shared_ptr<i2p::stream::StreamingDestination> > m_StreamingDestinationsByPorts;
|
||||
std::shared_ptr<i2p::stream::StreamingDestination> m_LastStreamingDestination; uint16_t m_LastPort; // for server tunnels
|
||||
i2p::datagram::DatagramDestination * m_DatagramDestination;
|
||||
int m_RefCounter; // how many clients(tunnels) use this destination
|
||||
uint64_t m_LastPublishedTimestamp;
|
||||
|
||||
boost::asio::deadline_timer m_ReadyChecker;
|
||||
|
||||
|
|
@ -340,7 +322,7 @@ namespace client
|
|||
{
|
||||
public:
|
||||
|
||||
RunnableClientDestination (const i2p::data::PrivateKeys& keys, bool isPublic, const i2p::util::Mapping * params = nullptr);
|
||||
RunnableClientDestination (const i2p::data::PrivateKeys& keys, bool isPublic, const std::map<std::string, std::string> * params = nullptr);
|
||||
~RunnableClientDestination ();
|
||||
|
||||
void Start ();
|
||||
|
|
|
|||
|
|
@ -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<ECIESX25519AEADRatchetSession> 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;
|
||||
|
|
@ -241,42 +229,12 @@ namespace garlic
|
|||
tagsetNsr->NextSessionTagRatchet ();
|
||||
}
|
||||
|
||||
bool ECIESX25519AEADRatchetSession::MessageConfirmed (uint32_t msgID)
|
||||
{
|
||||
auto ret = GarlicRoutingSession::MessageConfirmed (msgID); // LeaseSet
|
||||
if (m_AckRequestMsgID && m_AckRequestMsgID == msgID)
|
||||
{
|
||||
m_AckRequestMsgID = 0;
|
||||
m_AckRequestNumAttempts = 0;
|
||||
ret = true;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool ECIESX25519AEADRatchetSession::CleanupUnconfirmedTags ()
|
||||
{
|
||||
if (m_AckRequestMsgID && m_AckRequestNumAttempts > ECIESX25519_ACK_REQUEST_MAX_NUM_ATTEMPTS)
|
||||
{
|
||||
m_AckRequestMsgID = 0;
|
||||
m_AckRequestNumAttempts = 0;
|
||||
return true;
|
||||
}
|
||||
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))
|
||||
{
|
||||
|
|
@ -284,61 +242,20 @@ namespace garlic
|
|||
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
|
||||
{
|
||||
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<uint8_t> 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;
|
||||
}
|
||||
}
|
||||
|
||||
// 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;
|
||||
|
|
@ -351,18 +268,20 @@ namespace garlic
|
|||
if (isStatic)
|
||||
{
|
||||
// static key, fs is apk
|
||||
SetRemoteStaticKey (cryptoType, fs);
|
||||
if (!GetOwner ()->Decrypt (fs, sharedSecret, m_RemoteStaticKeyType)) // x25519(bsk, 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<uint8_t> 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 +317,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");
|
||||
|
|
@ -416,7 +335,7 @@ namespace garlic
|
|||
{
|
||||
uint32_t tagsetid = bufbe16toh (buf + offset1); offset1 += 2; // tagsetid
|
||||
uint16_t n = bufbe16toh (buf + offset1); offset1 += 2; // N
|
||||
MessageConfirmed ((tagsetid << 16) + n); // msgid = (tagsetid << 16) + N
|
||||
MessageConfirmed ((tagsetid << 16) + n); // msgid
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
|
@ -479,6 +398,7 @@ namespace garlic
|
|||
{
|
||||
uint16_t keyID = bufbe16toh (buf); buf += 2; // keyID
|
||||
bool newKey = flag & ECIESX25519_NEXT_KEY_REQUEST_REVERSE_KEY_FLAG;
|
||||
m_SendReverseKey = true;
|
||||
if (!m_NextReceiveRatchet)
|
||||
m_NextReceiveRatchet.reset (new DHRatchet ());
|
||||
else
|
||||
|
|
@ -490,14 +410,15 @@ namespace garlic
|
|||
}
|
||||
m_NextReceiveRatchet->keyID = keyID;
|
||||
}
|
||||
int tagsetID = 2*keyID;
|
||||
if (newKey)
|
||||
{
|
||||
m_NextReceiveRatchet->key = i2p::transport::transports.GetNextX25519KeysPair ();
|
||||
m_NextReceiveRatchet->newKey = true;
|
||||
tagsetID++;
|
||||
}
|
||||
else
|
||||
m_NextReceiveRatchet->newKey = false;
|
||||
auto tagsetID = m_NextReceiveRatchet->GetReceiveTagSetID ();
|
||||
if (flag & ECIESX25519_NEXT_KEY_KEY_PRESENT_FLAG)
|
||||
memcpy (m_NextReceiveRatchet->remote, buf, 32);
|
||||
|
||||
|
|
@ -511,9 +432,7 @@ namespace garlic
|
|||
GenerateMoreReceiveTags (newTagset, (GetOwner () && GetOwner ()->GetNumRatchetInboundTags () > 0) ?
|
||||
GetOwner ()->GetNumRatchetInboundTags () : ECIESX25519_MAX_NUM_GENERATED_TAGS);
|
||||
receiveTagset->Expire ();
|
||||
|
||||
LogPrint (eLogDebug, "Garlic: Next receive tagset ", tagsetID, " created");
|
||||
m_SendReverseKey = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -550,15 +469,6 @@ 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
|
||||
MixHash (m_EphemeralKeys->GetPublicKey (), 32); // h = SHA256(h || aepk)
|
||||
uint8_t sharedSecret[32];
|
||||
|
|
@ -568,32 +478,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<uint8_t> 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 +500,13 @@ namespace garlic
|
|||
// KDF2
|
||||
if (isStatic)
|
||||
{
|
||||
GetOwner ()->Decrypt (m_RemoteStaticKey, sharedSecret, m_RemoteStaticKeyType); // x25519 (ask, bpk)
|
||||
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;
|
||||
|
|
@ -657,33 +555,16 @@ namespace garlic
|
|||
return false;
|
||||
}
|
||||
MixKey (sharedSecret);
|
||||
#if OPENSSL_PQ
|
||||
if (m_PQKeys)
|
||||
{
|
||||
size_t cipherTextLen = i2p::crypto::GetMLKEMCipherTextLen (m_RemoteStaticKeyType);
|
||||
std::vector<uint8_t> 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<std::vector<uint8_t> > (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 +585,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 +606,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 +647,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<uint8_t> 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 +678,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 +687,7 @@ namespace garlic
|
|||
if (m_State == eSessionStateNewSessionSent)
|
||||
{
|
||||
m_State = eSessionStateEstablished;
|
||||
// don't delete m_EpehemralKey and m_PQKeys because delayed NSR's might 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 +702,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 +709,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 +718,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,27 +737,24 @@ 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
|
||||
moreTags = ECIESX25519_MIN_NUM_GENERATED_TAGS + (index >> 2); // N/4
|
||||
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
|
||||
|
|
@ -924,6 +763,7 @@ namespace garlic
|
|||
GenerateMoreReceiveTags (receiveTagset, moreTags);
|
||||
if (index > 0)
|
||||
receiveTagset->SetTrimBehind (index);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -937,14 +777,10 @@ namespace garlic
|
|||
m_State = eSessionStateEstablished;
|
||||
m_NSRSendTagset = nullptr;
|
||||
m_EphemeralKeys = nullptr;
|
||||
#if OPENSSL_PQ
|
||||
m_PQKeys = nullptr;
|
||||
m_NSREncodedPQKey = nullptr;
|
||||
#endif
|
||||
#if (__cplusplus >= 201703L) // C++ 17 or higher
|
||||
[[fallthrough]];
|
||||
#endif
|
||||
case eSessionStateEstablished:
|
||||
if (m_SendReverseKey && receiveTagset->GetTagSetID () == m_NextReceiveRatchet->GetReceiveTagSetID ())
|
||||
m_SendReverseKey = false; // tag received on new tagset
|
||||
if (receiveTagset->IsNS ())
|
||||
{
|
||||
// our of sequence NSR
|
||||
|
|
@ -971,12 +807,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 +822,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))
|
||||
|
|
@ -1039,7 +858,6 @@ namespace garlic
|
|||
{
|
||||
uint64_t ts = i2p::util::GetMillisecondsSinceEpoch ();
|
||||
size_t payloadLen = 0;
|
||||
bool sendAckRequest = false;
|
||||
if (first) payloadLen += 7;// datatime
|
||||
if (msg)
|
||||
{
|
||||
|
|
@ -1058,28 +876,13 @@ namespace garlic
|
|||
payloadLen += leaseSet->GetBufferLen () + DATABASE_STORE_HEADER_SIZE + 13;
|
||||
if (!first)
|
||||
{
|
||||
// ack request for LeaseSet
|
||||
m_AckRequestMsgID = m_SendTagset->GetMsgID ();
|
||||
sendAckRequest = true;
|
||||
// update LeaseSet status
|
||||
// ack request
|
||||
SetLeaseSetUpdateStatus (eLeaseSetSubmitted);
|
||||
SetLeaseSetUpdateMsgID (m_AckRequestMsgID);
|
||||
SetLeaseSetUpdateMsgID ((m_SendTagset->GetTagSetID () << 16) + m_SendTagset->GetNextIndex ()); // (tagsetid << 16) + N
|
||||
SetLeaseSetSubmissionTime (ts);
|
||||
payloadLen += 4;
|
||||
}
|
||||
}
|
||||
if (!sendAckRequest && !first &&
|
||||
((!m_AckRequestMsgID && ts > m_LastAckRequestSendTime + m_AckRequestInterval) || // regular request
|
||||
(m_AckRequestMsgID && ts > m_LastAckRequestSendTime + LEASESET_CONFIRMATION_TIMEOUT))) // previous request failed. try again
|
||||
{
|
||||
// not LeaseSet
|
||||
m_AckRequestMsgID = m_SendTagset->GetMsgID ();
|
||||
if (m_AckRequestMsgID)
|
||||
{
|
||||
m_AckRequestNumAttempts++;
|
||||
sendAckRequest = true;
|
||||
}
|
||||
}
|
||||
if (sendAckRequest) payloadLen += 4;
|
||||
if (m_AckRequests.size () > 0)
|
||||
payloadLen += m_AckRequests.size ()*4 + 3;
|
||||
if (m_SendReverseKey)
|
||||
|
|
@ -1131,14 +934,15 @@ namespace garlic
|
|||
}
|
||||
// LeaseSet
|
||||
if (leaseSet)
|
||||
offset += CreateLeaseSetClove (leaseSet, ts, payload + offset, payloadLen - offset);
|
||||
// ack request
|
||||
if (sendAckRequest)
|
||||
{
|
||||
offset += CreateLeaseSetClove (leaseSet, ts, payload + offset, payloadLen - offset);
|
||||
if (!first)
|
||||
{
|
||||
// ack request
|
||||
payload[offset] = eECIESx25519BlkAckRequest; offset++;
|
||||
htobe16buf (payload + offset, 1); offset += 2;
|
||||
payload[offset] = 0; offset++; // flags
|
||||
m_LastAckRequestSendTime = ts;
|
||||
}
|
||||
}
|
||||
// msg
|
||||
if (msg)
|
||||
|
|
@ -1174,6 +978,7 @@ namespace garlic
|
|||
memcpy (payload + offset, m_NextReceiveRatchet->key->GetPublicKey (), 32);
|
||||
offset += 32; // public key
|
||||
}
|
||||
m_SendReverseKey = false;
|
||||
}
|
||||
if (m_SendForwardKey)
|
||||
{
|
||||
|
|
@ -1269,8 +1074,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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 <functional>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <array>
|
||||
#include <list>
|
||||
#include <unordered_map>
|
||||
#include "Identity.h"
|
||||
#include "Crypto.h"
|
||||
#include "PostQuantum.h"
|
||||
#include "Garlic.h"
|
||||
#include "Tag.h"
|
||||
|
||||
|
|
@ -32,14 +30,10 @@ 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_MAX_NUM_ATTEMPTS = 3;
|
||||
const int ECIESX25519_PREVIOUS_TAGSET_EXPIRATION_TIMEOUT = 180; // 180
|
||||
const int ECIESX25519_TAGSET_MAX_NUM_TAGS = 8192; // number of tags we request new tagset after
|
||||
const int ECIESX25519_MIN_NUM_GENERATED_TAGS = 24;
|
||||
const int ECIESX25519_MAX_NUM_GENERATED_TAGS = 800;
|
||||
const int ECIESX25519_MAX_NUM_GENERATED_TAGS = 320;
|
||||
const int ECIESX25519_NSR_NUM_GENERATED_TAGS = 12;
|
||||
|
||||
const size_t ECIESX25519_OPTIMAL_PAYLOAD_SIZE = 1912; // 1912 = 1956 /* to fit 2 tunnel messages */
|
||||
|
|
@ -63,8 +57,6 @@ namespace garlic
|
|||
int GetTagSetID () const { return m_TagSetID; };
|
||||
void SetTagSetID (int tagsetID) { m_TagSetID = tagsetID; };
|
||||
|
||||
uint32_t GetMsgID () const { return (m_TagSetID << 16) + m_NextIndex; }; // (tagsetid << 16) + N
|
||||
|
||||
private:
|
||||
|
||||
i2p::data::Tag<64> m_SessionTagKeyData;
|
||||
|
|
@ -81,8 +73,8 @@ namespace garlic
|
|||
{
|
||||
public:
|
||||
|
||||
ReceiveRatchetTagSet (std::shared_ptr<ECIESX25519AEADRatchetSession> session, bool isNS = false);
|
||||
~ReceiveRatchetTagSet () override;
|
||||
ReceiveRatchetTagSet (std::shared_ptr<ECIESX25519AEADRatchetSession> session, bool isNS = false):
|
||||
m_Session (session), m_IsNS (isNS) {};
|
||||
|
||||
bool IsNS () const { return m_IsNS; };
|
||||
std::shared_ptr<ECIESX25519AEADRatchetSession> GetSession () { return m_Session; };
|
||||
|
|
@ -157,7 +149,6 @@ namespace garlic
|
|||
std::shared_ptr<i2p::crypto::X25519Keys> key;
|
||||
uint8_t remote[32]; // last remote public key
|
||||
bool newKey = true;
|
||||
int GetReceiveTagSetID () const { return newKey ? (2*keyID + 1) : 2*keyID; }
|
||||
};
|
||||
|
||||
public:
|
||||
|
|
@ -166,33 +157,26 @@ namespace garlic
|
|||
~ECIESX25519AEADRatchetSession ();
|
||||
|
||||
bool HandleNextMessage (uint8_t * buf, size_t len, std::shared_ptr<ReceiveRatchetTagSet> receiveTagset, int index = 0);
|
||||
std::shared_ptr<I2NPMessage> WrapSingleMessage (std::shared_ptr<const I2NPMessage> msg) override;
|
||||
std::shared_ptr<I2NPMessage> WrapSingleMessage (std::shared_ptr<const I2NPMessage> msg);
|
||||
std::shared_ptr<I2NPMessage> WrapOneTimeMessage (std::shared_ptr<const I2NPMessage> 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));
|
||||
}
|
||||
const i2p::data::IdentHash * GetDestinationPtr () const { return m_Destination ? m_Destination.get () : nullptr; }; // for pongs
|
||||
|
||||
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; };
|
||||
|
||||
protected:
|
||||
|
||||
|
|
@ -200,7 +184,6 @@ 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<ReceiveRatchetTagSet>& receiveTagset, int index);
|
||||
bool MessageConfirmed (uint32_t msgID) override;
|
||||
|
||||
private:
|
||||
|
||||
|
|
@ -226,30 +209,20 @@ 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<i2p::crypto::X25519Keys> m_EphemeralKeys;
|
||||
#if OPENSSL_PQ
|
||||
std::unique_ptr<i2p::crypto::MLKEMKeys> m_PQKeys;
|
||||
std::unique_ptr<std::vector<uint8_t> > 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<RatchetTagSet> m_SendTagset, m_NSRSendTagset;
|
||||
std::unique_ptr<i2p::data::IdentHash> m_Destination;// must be set for NS if outgoing and NSR if incoming
|
||||
std::list<std::pair<uint16_t, int> > m_AckRequests; // incoming (tagsetid, index)
|
||||
std::unique_ptr<i2p::data::IdentHash> m_Destination;// TODO: might not need it
|
||||
std::list<std::pair<uint16_t, int> > m_AckRequests; // (tagsetid, index)
|
||||
bool m_SendReverseKey = false, m_SendForwardKey = false, m_IsTerminated = false;
|
||||
std::unique_ptr<DHRatchet> m_NextReceiveRatchet, m_NextSendRatchet;
|
||||
uint8_t m_PaddingSizes[32], m_NextPaddingSize;
|
||||
|
||||
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:
|
||||
|
||||
// for HTTP only
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
/*
|
||||
* 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
|
||||
*
|
||||
* See full license text in LICENSE file at top of project tree
|
||||
*/
|
||||
|
||||
#include <openssl/evp.h>
|
||||
#include <openssl/sha.h>
|
||||
#include "Log.h"
|
||||
#include "Crypto.h"
|
||||
#include "Ed25519.h"
|
||||
|
|
@ -134,27 +134,22 @@ namespace crypto
|
|||
{
|
||||
BN_CTX * bnCtx = BN_CTX_new ();
|
||||
// calculate r
|
||||
EVP_MD_CTX * ctx = EVP_MD_CTX_create ();
|
||||
EVP_DigestInit_ex (ctx, EVP_sha512(), NULL);
|
||||
EVP_DigestUpdate (ctx, expandedPrivateKey + EDDSA25519_PRIVATE_KEY_LENGTH, EDDSA25519_PRIVATE_KEY_LENGTH); // right half of expanded key
|
||||
EVP_DigestUpdate (ctx, buf, len); // data
|
||||
SHA512_CTX ctx;
|
||||
SHA512_Init (&ctx);
|
||||
SHA512_Update (&ctx, expandedPrivateKey + EDDSA25519_PRIVATE_KEY_LENGTH, EDDSA25519_PRIVATE_KEY_LENGTH); // right half of expanded key
|
||||
SHA512_Update (&ctx, buf, len); // data
|
||||
uint8_t digest[64];
|
||||
unsigned int dl = 64;
|
||||
EVP_DigestFinal_ex (ctx, digest, &dl);
|
||||
EVP_MD_CTX_destroy (ctx);
|
||||
SHA512_Final (digest, &ctx);
|
||||
BIGNUM * r = DecodeBN<32> (digest); // DecodeBN<64> (digest); // for test vectors
|
||||
// calculate R
|
||||
uint8_t R[EDDSA25519_SIGNATURE_LENGTH/2]; // we must use separate buffer because signature might be inside buf
|
||||
EncodePoint (Normalize (MulB (digest, bnCtx), bnCtx), R); // EncodePoint (Mul (B, r, bnCtx), R); // for test vectors
|
||||
// calculate S
|
||||
ctx = EVP_MD_CTX_create ();
|
||||
EVP_DigestInit_ex (ctx, EVP_sha512(), NULL);
|
||||
EVP_DigestUpdate (ctx, R, EDDSA25519_SIGNATURE_LENGTH/2); // R
|
||||
EVP_DigestUpdate (ctx, publicKeyEncoded, EDDSA25519_PUBLIC_KEY_LENGTH); // public key
|
||||
EVP_DigestUpdate (ctx, buf, len); // data
|
||||
dl = 64;
|
||||
EVP_DigestFinal_ex (ctx, digest, &dl);
|
||||
EVP_MD_CTX_destroy (ctx);
|
||||
SHA512_Init (&ctx);
|
||||
SHA512_Update (&ctx, R, EDDSA25519_SIGNATURE_LENGTH/2); // R
|
||||
SHA512_Update (&ctx, publicKeyEncoded, EDDSA25519_PUBLIC_KEY_LENGTH); // public key
|
||||
SHA512_Update (&ctx, buf, len); // data
|
||||
SHA512_Final (digest, &ctx);
|
||||
BIGNUM * h = DecodeBN<64> (digest);
|
||||
// S = (r + h*a) % l
|
||||
BIGNUM * a = DecodeBN<EDDSA25519_PRIVATE_KEY_LENGTH> (expandedPrivateKey); // left half of expanded key
|
||||
|
|
@ -174,15 +169,13 @@ namespace crypto
|
|||
uint8_t T[80];
|
||||
RAND_bytes (T, 80);
|
||||
// calculate r = H*(T || publickey || data)
|
||||
EVP_MD_CTX * ctx = EVP_MD_CTX_create ();
|
||||
EVP_DigestInit_ex (ctx, EVP_sha512(), NULL);
|
||||
EVP_DigestUpdate (ctx, T, 80);
|
||||
EVP_DigestUpdate (ctx, publicKeyEncoded, 32);
|
||||
EVP_DigestUpdate (ctx, buf, len); // data
|
||||
SHA512_CTX ctx;
|
||||
SHA512_Init (&ctx);
|
||||
SHA512_Update (&ctx, T, 80);
|
||||
SHA512_Update (&ctx, publicKeyEncoded, 32);
|
||||
SHA512_Update (&ctx, buf, len); // data
|
||||
uint8_t digest[64];
|
||||
unsigned int dl = 64;
|
||||
EVP_DigestFinal_ex (ctx, digest, &dl);
|
||||
EVP_MD_CTX_destroy (ctx);
|
||||
SHA512_Final (digest, &ctx);
|
||||
BIGNUM * r = DecodeBN<64> (digest);
|
||||
BN_mod (r, r, l, bnCtx); // % l
|
||||
EncodeBN (r, digest, 32);
|
||||
|
|
@ -190,14 +183,11 @@ namespace crypto
|
|||
uint8_t R[EDDSA25519_SIGNATURE_LENGTH/2]; // we must use separate buffer because signature might be inside buf
|
||||
EncodePoint (Normalize (MulB (digest, bnCtx), bnCtx), R);
|
||||
// calculate S
|
||||
ctx = EVP_MD_CTX_create ();
|
||||
EVP_DigestInit_ex (ctx, EVP_sha512(), NULL);
|
||||
EVP_DigestUpdate (ctx, R, EDDSA25519_SIGNATURE_LENGTH/2); // R
|
||||
EVP_DigestUpdate (ctx, publicKeyEncoded, EDDSA25519_PUBLIC_KEY_LENGTH); // public key
|
||||
EVP_DigestUpdate (ctx, buf, len); // data
|
||||
dl = 64;
|
||||
EVP_DigestFinal_ex (ctx, digest, &dl);
|
||||
EVP_MD_CTX_destroy (ctx);
|
||||
SHA512_Init (&ctx);
|
||||
SHA512_Update (&ctx, R, EDDSA25519_SIGNATURE_LENGTH/2); // R
|
||||
SHA512_Update (&ctx, publicKeyEncoded, EDDSA25519_PUBLIC_KEY_LENGTH); // public key
|
||||
SHA512_Update (&ctx, buf, len); // data
|
||||
SHA512_Final (digest, &ctx);
|
||||
BIGNUM * h = DecodeBN<64> (digest);
|
||||
// S = (r + h*a) % l
|
||||
BIGNUM * a = DecodeBN<EDDSA25519_PRIVATE_KEY_LENGTH> (privateKey);
|
||||
|
|
@ -467,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 ();
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
135
libi2pd/FS.cpp
135
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
|
||||
*
|
||||
|
|
@ -7,18 +7,13 @@
|
|||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <boost/filesystem.hpp>
|
||||
|
||||
#if defined(MAC_OSX)
|
||||
#if !STD_FILESYSTEM
|
||||
#include <boost/system/system_error.hpp>
|
||||
#endif
|
||||
#include <TargetConditionals.h>
|
||||
#endif
|
||||
|
||||
#if defined(__HAIKU__)
|
||||
#include <FindDirectory.h>
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <shlobj.h>
|
||||
#include <windows.h>
|
||||
|
|
@ -30,14 +25,6 @@
|
|||
#include "Log.h"
|
||||
#include "Garlic.h"
|
||||
|
||||
#if STD_FILESYSTEM
|
||||
#include <filesystem>
|
||||
namespace fs_lib = std::filesystem;
|
||||
#else
|
||||
#include <boost/filesystem.hpp>
|
||||
namespace fs_lib = boost::filesystem;
|
||||
#endif
|
||||
|
||||
namespace i2p {
|
||||
namespace fs {
|
||||
std::string appName = "i2pd";
|
||||
|
|
@ -67,17 +54,15 @@ namespace fs {
|
|||
|
||||
const std::string GetUTF8DataDir () {
|
||||
#ifdef _WIN32
|
||||
int size = MultiByteToWideChar(CP_ACP, 0,
|
||||
dataDir.c_str(), dataDir.size(), nullptr, 0);
|
||||
std::wstring utf16Str(size, L'\0');
|
||||
MultiByteToWideChar(CP_ACP, 0,
|
||||
dataDir.c_str(), dataDir.size(), &utf16Str[0], size);
|
||||
int utf8Size = WideCharToMultiByte(CP_UTF8, 0,
|
||||
utf16Str.c_str(), utf16Str.size(), nullptr, 0, nullptr, nullptr);
|
||||
std::string utf8Str(utf8Size, '\0');
|
||||
WideCharToMultiByte(CP_UTF8, 0,
|
||||
utf16Str.c_str(), utf16Str.size(), &utf8Str[0], utf8Size, nullptr, nullptr);
|
||||
return utf8Str;
|
||||
#if (BOOST_VERSION >= 108500)
|
||||
boost::filesystem::path path (dataDir);
|
||||
#else
|
||||
boost::filesystem::wpath path (dataDir);
|
||||
#endif
|
||||
auto loc = boost::filesystem::path::imbue(std::locale( std::locale(), new std::codecvt_utf8_utf16<wchar_t>() ) ); // convert path to UTF-8
|
||||
auto dataDirUTF8 = path.string();
|
||||
boost::filesystem::path::imbue(loc); // Return locale settings back
|
||||
return dataDirUTF8;
|
||||
#else
|
||||
return dataDir; // linux, osx, android uses UTF-8 by default
|
||||
#endif
|
||||
|
|
@ -106,10 +91,10 @@ namespace fs {
|
|||
}
|
||||
else
|
||||
{
|
||||
#if ((BOOST_VERSION >= 108500) || STD_FILESYSTEM)
|
||||
dataDir = fs_lib::path(commonAppData).string() + "\\" + appName;
|
||||
#if (BOOST_VERSION >= 108500)
|
||||
dataDir = boost::filesystem::path(commonAppData).string() + "\\" + appName;
|
||||
#else
|
||||
dataDir = fs_lib::wpath(commonAppData).string() + "\\" + appName;
|
||||
dataDir = boost::filesystem::wpath(commonAppData).string() + "\\" + appName;
|
||||
#endif
|
||||
}
|
||||
#else
|
||||
|
|
@ -135,14 +120,14 @@ namespace fs {
|
|||
}
|
||||
else
|
||||
{
|
||||
#if ((BOOST_VERSION >= 108500) || STD_FILESYSTEM)
|
||||
auto execPath = fs_lib::path(localAppData).parent_path();
|
||||
#if (BOOST_VERSION >= 108500)
|
||||
auto execPath = boost::filesystem::path(localAppData).parent_path();
|
||||
#else
|
||||
auto execPath = fs_lib::wpath(localAppData).parent_path();
|
||||
auto execPath = boost::filesystem::wpath(localAppData).parent_path();
|
||||
#endif
|
||||
|
||||
// if config file exists in .exe's folder use it
|
||||
if(fs_lib::exists(execPath/"i2pd.conf")) // TODO: magic string
|
||||
if(boost::filesystem::exists(execPath/"i2pd.conf")) // TODO: magic string
|
||||
{
|
||||
dataDir = execPath.string ();
|
||||
} else // otherwise %appdata%
|
||||
|
|
@ -158,10 +143,10 @@ namespace fs {
|
|||
}
|
||||
else
|
||||
{
|
||||
#if ((BOOST_VERSION >= 108500) || STD_FILESYSTEM)
|
||||
dataDir = fs_lib::path(localAppData).string() + "\\" + appName;
|
||||
#if (BOOST_VERSION >= 108500)
|
||||
dataDir = boost::filesystem::path(localAppData).string() + "\\" + appName;
|
||||
#else
|
||||
dataDir = fs_lib::wpath(localAppData).string() + "\\" + appName;
|
||||
dataDir = boost::filesystem::wpath(localAppData).string() + "\\" + appName;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
|
@ -173,17 +158,18 @@ 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)
|
||||
const char * ext = getenv("EXTERNAL_STORAGE");
|
||||
if (!ext) ext = "/sdcard";
|
||||
if (fs_lib::exists(ext))
|
||||
if (boost::filesystem::exists(ext))
|
||||
{
|
||||
dataDir = std::string (ext) + "/" + appName;
|
||||
return;
|
||||
|
|
@ -216,16 +202,16 @@ namespace fs {
|
|||
}
|
||||
|
||||
bool Init() {
|
||||
if (!fs_lib::exists(dataDir))
|
||||
fs_lib::create_directory(dataDir);
|
||||
if (!boost::filesystem::exists(dataDir))
|
||||
boost::filesystem::create_directory(dataDir);
|
||||
|
||||
std::string destinations = DataDirPath("destinations");
|
||||
if (!fs_lib::exists(destinations))
|
||||
fs_lib::create_directory(destinations);
|
||||
if (!boost::filesystem::exists(destinations))
|
||||
boost::filesystem::create_directory(destinations);
|
||||
|
||||
std::string tags = DataDirPath("tags");
|
||||
if (!fs_lib::exists(tags))
|
||||
fs_lib::create_directory(tags);
|
||||
if (!boost::filesystem::exists(tags))
|
||||
boost::filesystem::create_directory(tags);
|
||||
else
|
||||
i2p::garlic::CleanUpTagsFiles ();
|
||||
|
||||
|
|
@ -233,13 +219,13 @@ namespace fs {
|
|||
}
|
||||
|
||||
bool ReadDir(const std::string & path, std::vector<std::string> & files) {
|
||||
if (!fs_lib::exists(path))
|
||||
if (!boost::filesystem::exists(path))
|
||||
return false;
|
||||
fs_lib::directory_iterator it(path);
|
||||
fs_lib::directory_iterator end;
|
||||
boost::filesystem::directory_iterator it(path);
|
||||
boost::filesystem::directory_iterator end;
|
||||
|
||||
for ( ; it != end; it++) {
|
||||
if (!fs_lib::is_regular_file(it->status()))
|
||||
if (!boost::filesystem::is_regular_file(it->status()))
|
||||
continue;
|
||||
files.push_back(it->path().string());
|
||||
}
|
||||
|
|
@ -248,42 +234,29 @@ namespace fs {
|
|||
}
|
||||
|
||||
bool Exists(const std::string & path) {
|
||||
return fs_lib::exists(path);
|
||||
return boost::filesystem::exists(path);
|
||||
}
|
||||
|
||||
uint32_t GetLastUpdateTime (const std::string & path)
|
||||
{
|
||||
if (!fs_lib::exists(path))
|
||||
if (!boost::filesystem::exists(path))
|
||||
return 0;
|
||||
#if STD_FILESYSTEM
|
||||
std::error_code ec;
|
||||
auto t = std::filesystem::last_write_time (path, ec);
|
||||
if (ec) return 0;
|
||||
/*#if __cplusplus >= 202002L // C++ 20 or higher
|
||||
const auto sctp = std::chrono::clock_cast<std::chrono::system_clock>(t);
|
||||
#else */ // TODO: wait until implemented
|
||||
const auto sctp = std::chrono::time_point_cast<std::chrono::system_clock::duration>(
|
||||
t - decltype(t)::clock::now() + std::chrono::system_clock::now());
|
||||
/*#endif */
|
||||
return std::chrono::system_clock::to_time_t(sctp);
|
||||
#else
|
||||
boost::system::error_code ec;
|
||||
auto t = boost::filesystem::last_write_time (path, ec);
|
||||
return ec ? 0 : t;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool Remove(const std::string & path) {
|
||||
if (!fs_lib::exists(path))
|
||||
if (!boost::filesystem::exists(path))
|
||||
return false;
|
||||
return fs_lib::remove(path);
|
||||
return boost::filesystem::remove(path);
|
||||
}
|
||||
|
||||
bool CreateDirectory (const std::string& path)
|
||||
{
|
||||
if (fs_lib::exists(path) && fs_lib::is_directory (fs_lib::status (path)))
|
||||
if (boost::filesystem::exists(path) && boost::filesystem::is_directory (boost::filesystem::status (path)))
|
||||
return true;
|
||||
return fs_lib::create_directory(path);
|
||||
return boost::filesystem::create_directory(path);
|
||||
}
|
||||
|
||||
void HashedStorage::SetPlace(const std::string &path) {
|
||||
|
|
@ -291,18 +264,18 @@ namespace fs {
|
|||
}
|
||||
|
||||
bool HashedStorage::Init(const char * chars, size_t count) {
|
||||
if (!fs_lib::exists(root)) {
|
||||
fs_lib::create_directories(root);
|
||||
if (!boost::filesystem::exists(root)) {
|
||||
boost::filesystem::create_directories(root);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
auto p = root + i2p::fs::dirSep + prefix1 + chars[i];
|
||||
if (fs_lib::exists(p))
|
||||
if (boost::filesystem::exists(p))
|
||||
continue;
|
||||
#if TARGET_OS_SIMULATOR
|
||||
// ios simulator fs says it is case sensitive, but it is not
|
||||
boost::system::error_code ec;
|
||||
if (fs_lib::create_directory(p, ec))
|
||||
if (boost::filesystem::create_directory(p, ec))
|
||||
continue;
|
||||
switch (ec.value()) {
|
||||
case boost::system::errc::file_exists:
|
||||
|
|
@ -312,7 +285,7 @@ namespace fs {
|
|||
throw boost::system::system_error( ec, __func__ );
|
||||
}
|
||||
#else
|
||||
if (fs_lib::create_directory(p))
|
||||
if (boost::filesystem::create_directory(p))
|
||||
continue; /* ^ throws exception on failure */
|
||||
#endif
|
||||
return false;
|
||||
|
|
@ -335,9 +308,9 @@ namespace fs {
|
|||
|
||||
void HashedStorage::Remove(const std::string & ident) {
|
||||
std::string path = Path(ident);
|
||||
if (!fs_lib::exists(path))
|
||||
if (!boost::filesystem::exists(path))
|
||||
return;
|
||||
fs_lib::remove(path);
|
||||
boost::filesystem::remove(path);
|
||||
}
|
||||
|
||||
void HashedStorage::Traverse(std::vector<std::string> & files) {
|
||||
|
|
@ -348,12 +321,12 @@ namespace fs {
|
|||
|
||||
void HashedStorage::Iterate(FilenameVisitor v)
|
||||
{
|
||||
fs_lib::path p(root);
|
||||
fs_lib::recursive_directory_iterator it(p);
|
||||
fs_lib::recursive_directory_iterator end;
|
||||
boost::filesystem::path p(root);
|
||||
boost::filesystem::recursive_directory_iterator it(p);
|
||||
boost::filesystem::recursive_directory_iterator end;
|
||||
|
||||
for ( ; it != end; it++) {
|
||||
if (!fs_lib::is_regular_file( it->status() ))
|
||||
if (!boost::filesystem::is_regular_file( it->status() ))
|
||||
continue;
|
||||
const std::string & t = it->path().string();
|
||||
v(t);
|
||||
|
|
|
|||
12
libi2pd/FS.h
12
libi2pd/FS.h
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2013-2024, The PurpleI2P Project
|
||||
* Copyright (c) 2013-2020, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
|
|
@ -15,16 +15,6 @@
|
|||
#include <sstream>
|
||||
#include <functional>
|
||||
|
||||
#ifndef STD_FILESYSTEM
|
||||
# if (_WIN32 && __GNUG__) // MinGW GCC somehow incorrectly converts paths
|
||||
# define STD_FILESYSTEM 0
|
||||
# elif (!TARGET_OS_SIMULATOR && __has_include(<filesystem>)) // supports std::filesystem
|
||||
# define STD_FILESYSTEM 1
|
||||
# else
|
||||
# define STD_FILESYSTEM 0
|
||||
# endif
|
||||
#endif
|
||||
|
||||
namespace i2p {
|
||||
namespace fs {
|
||||
extern std::string dirSep;
|
||||
|
|
|
|||
|
|
@ -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 <string.h>
|
||||
#include <openssl/evp.h>
|
||||
#include <openssl/ssl.h>
|
||||
#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,29 +47,48 @@ namespace data
|
|||
cn += 3;
|
||||
char * family = strstr (cn, ".family");
|
||||
if (family) family[0] = 0;
|
||||
auto pkey = X509_get_pubkey (cert);
|
||||
if (pkey)
|
||||
{
|
||||
int curve = 0;
|
||||
#if (OPENSSL_VERSION_NUMBER >= 0x030000000) // since 3.0.0
|
||||
char groupName[20];
|
||||
if (EVP_PKEY_get_group_name(pkey, groupName, sizeof(groupName), NULL) == 1)
|
||||
curve = OBJ_txt2nid (groupName);
|
||||
else
|
||||
curve = -1;
|
||||
#endif
|
||||
if (!curve || curve == NID_X9_62_prime256v1)
|
||||
{
|
||||
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<i2p::crypto::ECDSAP256Verifier>();
|
||||
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);
|
||||
}
|
||||
|
|
@ -103,37 +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;
|
||||
}
|
||||
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 == 64)
|
||||
{
|
||||
ECDSA_SIG * sig = ECDSA_SIG_new();
|
||||
ECDSA_SIG_set0 (sig, BN_bin2bn (signatureBuf, 32, NULL), BN_bin2bn (signatureBuf + 32, 32, NULL));
|
||||
uint8_t sign[72];
|
||||
uint8_t * s = sign;
|
||||
auto l = i2d_ECDSA_SIG (sig, &s);
|
||||
ECDSA_SIG_free(sig);
|
||||
EVP_MD_CTX * ctx = EVP_MD_CTX_create ();
|
||||
EVP_DigestVerifyInit (ctx, NULL, EVP_sha256(), NULL, it->second.first);
|
||||
auto ret = EVP_DigestVerify (ctx, sign, l, buf, len) == 1;
|
||||
EVP_MD_CTX_destroy (ctx);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
Base64ToByteStream (signature, signatureLen, signatureBuf, 64);
|
||||
auto it = m_SigningKeys.find (family);
|
||||
if (it != m_SigningKeys.end ())
|
||||
return it->second.first->Verify (buf, len, signatureBuf);
|
||||
// TODO: process key
|
||||
return true;
|
||||
}
|
||||
|
|
@ -156,40 +160,34 @@ namespace data
|
|||
{
|
||||
SSL * ssl = SSL_new (ctx);
|
||||
EVP_PKEY * pkey = SSL_get_privatekey (ssl);
|
||||
int curve = 0;
|
||||
#if (OPENSSL_VERSION_NUMBER >= 0x030000000) // since 3.0.0
|
||||
char groupName[20];
|
||||
if (EVP_PKEY_get_group_name(pkey, groupName, sizeof(groupName), NULL) == 1)
|
||||
curve = OBJ_txt2nid (groupName);
|
||||
else
|
||||
curve = -1;
|
||||
#endif
|
||||
if (!curve || curve == NID_X9_62_prime256v1)
|
||||
EC_KEY * ecKey = EVP_PKEY_get1_EC_KEY (pkey);
|
||||
if (ecKey)
|
||||
{
|
||||
uint8_t buf[100], sign[72], signature[64];
|
||||
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 signingPrivateKey[32], buf[50], signature[64];
|
||||
i2p::crypto::bn2buf (EC_KEY_get0_private_key (ecKey), signingPrivateKey, 32);
|
||||
i2p::crypto::ECDSAP256Signer signer (signingPrivateKey);
|
||||
size_t len = family.length ();
|
||||
memcpy (buf, family.c_str (), len);
|
||||
memcpy (buf + len, (const uint8_t *)ident, 32);
|
||||
len += 32;
|
||||
|
||||
size_t l = 72;
|
||||
EVP_MD_CTX * mdctx = EVP_MD_CTX_create ();
|
||||
EVP_DigestSignInit (mdctx, NULL, EVP_sha256(), NULL, pkey);
|
||||
EVP_DigestSign (mdctx, sign, &l, buf, len);
|
||||
EVP_MD_CTX_destroy (mdctx);
|
||||
|
||||
const uint8_t * s1 = sign;
|
||||
ECDSA_SIG * sig1 = d2i_ECDSA_SIG (NULL, &s1, l);
|
||||
const BIGNUM * r, * s;
|
||||
ECDSA_SIG_get0 (sig1, &r, &s);
|
||||
i2p::crypto::bn2buf (r, signature, 32);
|
||||
i2p::crypto::bn2buf (s, signature + 32, 32);
|
||||
ECDSA_SIG_free(sig1);
|
||||
sig = ByteStreamToBase64 (signature, 64);
|
||||
signer.Sign (buf, len, signature);
|
||||
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");
|
||||
|
||||
}
|
||||
}
|
||||
SSL_free (ssl);
|
||||
}
|
||||
else
|
||||
|
|
|
|||
|
|
@ -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 <map>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <memory>
|
||||
#include <openssl/evp.h>
|
||||
#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<std::string, std::pair<EVP_PKEY *, FamilyID> > m_SigningKeys; // family -> (verification pkey, id)
|
||||
std::map<std::string, std::pair<std::shared_ptr<i2p::crypto::Verifier>, FamilyID> > m_SigningKeys; // family -> (verifier, id)
|
||||
};
|
||||
|
||||
std::string CreateFamilySignature (const std::string& family, const IdentHash& ident);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
*
|
||||
|
|
@ -28,7 +28,7 @@ namespace garlic
|
|||
{
|
||||
GarlicRoutingSession::GarlicRoutingSession (GarlicDestination * owner, bool attachLeaseSet):
|
||||
m_Owner (owner), m_LeaseSetUpdateStatus (attachLeaseSet ? eLeaseSetUpdated : eLeaseSetDoNotSend),
|
||||
m_LeaseSetUpdateMsgID (0), m_IsWithJava (false), m_NumSentPackets (0)
|
||||
m_LeaseSetUpdateMsgID (0)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
@ -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,43 @@ namespace garlic
|
|||
auto decryption = std::make_shared<AESDecryption>(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<ECIESX25519AEADRatchetSession> (this, false); // incoming
|
||||
if (session->HandleNextMessage (buf, length, nullptr, 0))
|
||||
m_LastIncomingSessionTimestamp = ts;
|
||||
else
|
||||
if (!session->HandleNextMessage (buf, length, nullptr, 0))
|
||||
{
|
||||
// try to generate more tags for last tagset
|
||||
if (m_LastTagset && (m_LastTagset->GetNextIndex () - m_LastTagset->GetTrimBehind () < 3*ECIESX25519_MAX_NUM_GENERATED_TAGS))
|
||||
{
|
||||
uint64_t missingTag; memcpy (&missingTag, buf, 8);
|
||||
auto maxTags = std::max (m_NumRatchetInboundTags, ECIESX25519_MAX_NUM_GENERATED_TAGS);
|
||||
LogPrint (eLogWarning, "Garlic: Trying to generate more ECIES-X25519-AEAD-Ratchet tags");
|
||||
for (int i = 0; i < maxTags; i++)
|
||||
{
|
||||
auto nextTag = AddECIESx25519SessionNextTag (m_LastTagset);
|
||||
if (!nextTag)
|
||||
{
|
||||
LogPrint (eLogError, "Garlic: Can't create new ECIES-X25519-AEAD-Ratchet tag for last tagset");
|
||||
break;
|
||||
}
|
||||
if (nextTag == missingTag)
|
||||
{
|
||||
LogPrint (eLogDebug, "Garlic: Missing ECIES-X25519-AEAD-Ratchet tag was generated");
|
||||
if (m_LastTagset->HandleNextMessage (buf, length, m_ECIESx25519Tags[nextTag].index))
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) m_LastTagset = nullptr;
|
||||
}
|
||||
if (!found)
|
||||
LogPrint (eLogError, "Garlic: Can't handle ECIES-X25519-AEAD-Ratchet message");
|
||||
}
|
||||
else
|
||||
LogPrint (eLogWarning, "Garlic: Incoming sessions come too often");
|
||||
}
|
||||
else
|
||||
LogPrint (eLogError, "Garlic: Failed to decrypt message");
|
||||
|
|
@ -564,7 +583,9 @@ namespace garlic
|
|||
auto it = m_ECIESx25519Tags.find (tag);
|
||||
if (it != m_ECIESx25519Tags.end ())
|
||||
{
|
||||
if (!it->second.tagset || !it->second.tagset->HandleNextMessage (buf, len, it->second.index))
|
||||
if (it->second.tagset && it->second.tagset->HandleNextMessage (buf, len, it->second.index))
|
||||
m_LastTagset = it->second.tagset;
|
||||
else
|
||||
LogPrint (eLogError, "Garlic: Can't handle ECIES-X25519-AEAD-Ratchet message");
|
||||
m_ECIESx25519Tags.erase (it);
|
||||
return true;
|
||||
|
|
@ -745,12 +766,10 @@ namespace garlic
|
|||
}
|
||||
|
||||
std::shared_ptr<GarlicRoutingSession> GarlicDestination::GetRoutingSession (
|
||||
std::shared_ptr<const i2p::data::RoutingDestination> destination, bool attachLeaseSet,
|
||||
bool requestNewIfNotFound)
|
||||
std::shared_ptr<const i2p::data::RoutingDestination> destination, bool attachLeaseSet)
|
||||
{
|
||||
if (destination->GetEncryptionType () >= i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD)
|
||||
{
|
||||
if (SupportsEncryptionType (destination->GetEncryptionType ()))
|
||||
if (destination->GetEncryptionType () == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD &&
|
||||
SupportsEncryptionType (i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD))
|
||||
{
|
||||
ECIESX25519AEADRatchetSessionPtr session;
|
||||
uint8_t staticKey[32];
|
||||
|
|
@ -762,22 +781,18 @@ namespace garlic
|
|||
if (session->IsInactive (i2p::util::GetSecondsSinceEpoch ()))
|
||||
{
|
||||
LogPrint (eLogDebug, "Garlic: Session restarted");
|
||||
requestNewIfNotFound = true; // it's not a new session
|
||||
session = nullptr;
|
||||
}
|
||||
}
|
||||
if (!session && requestNewIfNotFound)
|
||||
if (!session)
|
||||
{
|
||||
session = std::make_shared<ECIESX25519AEADRatchetSession> (this, true);
|
||||
session->SetRemoteStaticKey (destination->GetEncryptionType (), staticKey);
|
||||
session->SetRemoteStaticKey (staticKey);
|
||||
}
|
||||
if (session && destination->IsDestination ())
|
||||
session->SetDestination (destination->GetIdentHash ()); // NS or NSR
|
||||
if (destination->IsDestination ())
|
||||
session->SetDestination (destination->GetIdentHash ()); // TODO: remove
|
||||
return session;
|
||||
}
|
||||
else
|
||||
LogPrint (eLogError, "Garlic: Non-supported encryption type ", destination->GetEncryptionType ());
|
||||
}
|
||||
else
|
||||
{
|
||||
ElGamalAESSessionPtr session;
|
||||
|
|
@ -878,6 +893,8 @@ namespace garlic
|
|||
}
|
||||
if (numExpiredTags > 0)
|
||||
LogPrint (eLogDebug, "Garlic: ", numExpiredTags, " ECIESx25519 tags expired for ", GetIdentHash().ToBase64 ());
|
||||
if (m_LastTagset && m_LastTagset->IsExpired (ts))
|
||||
m_LastTagset = nullptr;
|
||||
}
|
||||
|
||||
void GarlicDestination::RemoveDeliveryStatusSession (uint32_t msgID)
|
||||
|
|
@ -911,7 +928,7 @@ namespace garlic
|
|||
}
|
||||
}
|
||||
|
||||
void GarlicDestination::SetLeaseSetUpdated (bool post)
|
||||
void GarlicDestination::SetLeaseSetUpdated ()
|
||||
{
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_SessionsMutex);
|
||||
|
|
@ -1004,8 +1021,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
|
||||
|
|
@ -1015,7 +1031,9 @@ namespace garlic
|
|||
case eGarlicDeliveryTypeDestination:
|
||||
LogPrint (eLogDebug, "Garlic: Type destination");
|
||||
buf += 32; // TODO: check destination
|
||||
#if (__cplusplus >= 201703L) // C++ 17 or higher
|
||||
[[fallthrough]];
|
||||
#endif
|
||||
// no break here
|
||||
case eGarlicDeliveryTypeLocal:
|
||||
{
|
||||
|
|
@ -1025,7 +1043,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 +1127,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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
{
|
||||
|
|
@ -87,7 +86,6 @@ namespace garlic
|
|||
{
|
||||
std::shared_ptr<i2p::tunnel::OutboundTunnel> outboundTunnel;
|
||||
std::shared_ptr<const i2p::data::Lease> remoteLease;
|
||||
// for streaming only
|
||||
int rtt; // RTT
|
||||
uint32_t updateTime; // seconds since epoch
|
||||
};
|
||||
|
|
@ -111,13 +109,12 @@ namespace garlic
|
|||
GarlicRoutingSession ();
|
||||
virtual ~GarlicRoutingSession ();
|
||||
virtual std::shared_ptr<I2NPMessage> WrapSingleMessage (std::shared_ptr<const I2NPMessage> msg) = 0;
|
||||
virtual bool CleanupUnconfirmedTags () { return false; }; // for I2CP, override in ElGamalAESSession and ECIESX25519AEADRatchetSession
|
||||
virtual bool CleanupUnconfirmedTags () { return false; }; // for I2CP, override in ElGamalAESSession
|
||||
virtual bool MessageConfirmed (uint32_t msgID);
|
||||
virtual bool IsRatchets () const { return false; };
|
||||
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 ()
|
||||
{
|
||||
|
|
@ -131,12 +128,6 @@ namespace garlic
|
|||
std::shared_ptr<GarlicRoutingPath> GetSharedRoutingPath ();
|
||||
void SetSharedRoutingPath (std::shared_ptr<GarlicRoutingPath> path);
|
||||
|
||||
bool IsWithJava () const { return m_IsWithJava; }
|
||||
void SetIsWithJava (bool isWithJava) { m_IsWithJava = isWithJava; }
|
||||
|
||||
int NumSentPackets () const { return m_NumSentPackets; }
|
||||
void SetNumSentPackets (int numSentPackets) { m_NumSentPackets = numSentPackets; }
|
||||
|
||||
GarlicDestination * GetOwner () const { return m_Owner; }
|
||||
void SetOwner (GarlicDestination * owner) { m_Owner = owner; }
|
||||
|
||||
|
|
@ -159,8 +150,6 @@ namespace garlic
|
|||
uint64_t m_LeaseSetSubmissionTime; // in milliseconds
|
||||
|
||||
std::shared_ptr<GarlicRoutingPath> m_SharedRoutingPath;
|
||||
bool m_IsWithJava; // based on choked value from streaming
|
||||
int m_NumSentPackets; // for limit number of sent messages in streaming
|
||||
|
||||
public:
|
||||
|
||||
|
|
@ -215,7 +204,6 @@ namespace garlic
|
|||
std::map<uint32_t, std::unique_ptr<UnconfirmedTags> > m_UnconfirmedTagsMsgs; // msgID->tags
|
||||
|
||||
i2p::crypto::CBCEncryption m_Encryption;
|
||||
i2p::data::Tag<16> m_IV;
|
||||
|
||||
public:
|
||||
|
||||
|
|
@ -246,18 +234,12 @@ namespace garlic
|
|||
int GetNumTags () const { return m_NumTags; };
|
||||
void SetNumRatchetInboundTags (int numTags) { m_NumRatchetInboundTags = numTags; };
|
||||
int GetNumRatchetInboundTags () const { return m_NumRatchetInboundTags; };
|
||||
std::shared_ptr<GarlicRoutingSession> GetRoutingSession (std::shared_ptr<const i2p::data::RoutingDestination> destination,
|
||||
bool attachLeaseSet, bool requestNewIfNotFound = true);
|
||||
std::shared_ptr<GarlicRoutingSession> GetRoutingSession (std::shared_ptr<const i2p::data::RoutingDestination> destination, bool attachLeaseSet);
|
||||
void CleanupExpiredTags ();
|
||||
void RemoveDeliveryStatusSession (uint32_t msgID);
|
||||
std::shared_ptr<I2NPMessage> WrapMessageForRouter (std::shared_ptr<const i2p::data::RouterInfo> router,
|
||||
std::shared_ptr<I2NPMessage> 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
|
||||
virtual bool SubmitSessionKey (const uint8_t * key, const uint8_t * tag); // from different thread
|
||||
|
|
@ -266,27 +248,22 @@ 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<I2NPMessage> msg);
|
||||
virtual void ProcessDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg);
|
||||
virtual void SetLeaseSetUpdated (bool post = false);
|
||||
virtual void SetLeaseSetUpdated ();
|
||||
|
||||
virtual std::shared_ptr<const i2p::data::LocalLeaseSet> GetLeaseSet () = 0; // TODO
|
||||
virtual std::shared_ptr<i2p::tunnel::TunnelPool> 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<I2NPMessage> msg);
|
||||
void HandleDeliveryStatusMessage (uint32_t msgID);
|
||||
|
||||
|
|
@ -295,7 +272,6 @@ namespace garlic
|
|||
|
||||
private:
|
||||
|
||||
bool SupportsRatchets () const { return GetRatchetsHighestCryptoType () > 0; }
|
||||
void HandleAESBlock (uint8_t * buf, size_t len, std::shared_ptr<AESDecryption> decryption,
|
||||
std::shared_ptr<i2p::tunnel::InboundTunnel> from);
|
||||
void HandleGarlicPayload (uint8_t * buf, size_t len, std::shared_ptr<i2p::tunnel::InboundTunnel> from);
|
||||
|
|
@ -308,17 +284,14 @@ namespace garlic
|
|||
std::unordered_map<i2p::data::IdentHash, ElGamalAESSessionPtr> m_Sessions;
|
||||
std::unordered_map<i2p::data::Tag<32>, 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<SessionTag, std::shared_ptr<AESDecryption>, std::hash<i2p::data::Tag<32> > > m_Tags;
|
||||
std::unordered_map<uint64_t, ECIESX25519AEADRatchetIndexTagset> m_ECIESx25519Tags; // session tag -> session
|
||||
ReceiveRatchetTagSetPtr m_LastTagset; // tagset last message came for
|
||||
// DeliveryStatus
|
||||
std::mutex m_DeliveryStatusSessionsMutex;
|
||||
std::unordered_map<uint32_t, GarlicRoutingSessionPtr> m_DeliveryStatusSessions; // msgID -> session
|
||||
// encryption
|
||||
i2p::crypto::AEADChaCha20Poly1305Encryptor m_Encryptor;
|
||||
i2p::crypto::AEADChaCha20Poly1305Decryptor m_Decryptor;
|
||||
|
||||
public:
|
||||
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ namespace crypto
|
|||
BN_CTX * ctx = BN_CTX_new ();
|
||||
m_Group = EC_GROUP_new_curve_GFp (p, a, b, ctx);
|
||||
EC_POINT * P = EC_POINT_new (m_Group);
|
||||
EC_POINT_set_affine_coordinates (m_Group, P, x, y, ctx);
|
||||
EC_POINT_set_affine_coordinates_GFp (m_Group, P, x, y, ctx);
|
||||
EC_GROUP_set_generator (m_Group, P, q, nullptr);
|
||||
EC_GROUP_set_curve_name (m_Group, NID_id_GostR3410_2001);
|
||||
EC_POINT_free(P);
|
||||
|
|
@ -50,13 +50,13 @@ namespace crypto
|
|||
|
||||
bool GOSTR3410Curve::GetXY (const EC_POINT * p, BIGNUM * x, BIGNUM * y) const
|
||||
{
|
||||
return EC_POINT_get_affine_coordinates (m_Group, p, x, y, nullptr);
|
||||
return EC_POINT_get_affine_coordinates_GFp (m_Group, p, x, y, nullptr);
|
||||
}
|
||||
|
||||
EC_POINT * GOSTR3410Curve::CreatePoint (const BIGNUM * x, const BIGNUM * y) const
|
||||
{
|
||||
EC_POINT * p = EC_POINT_new (m_Group);
|
||||
EC_POINT_set_affine_coordinates (m_Group, p, x, y, nullptr);
|
||||
EC_POINT_set_affine_coordinates_GFp (m_Group, p, x, y, nullptr);
|
||||
return p;
|
||||
}
|
||||
|
||||
|
|
@ -112,7 +112,7 @@ namespace crypto
|
|||
BN_CTX_start (ctx);
|
||||
EC_POINT * C = EC_POINT_new (m_Group); // C = k*P = (rx, ry)
|
||||
EC_POINT * Q = nullptr;
|
||||
if (EC_POINT_set_compressed_coordinates (m_Group, C, r, isNegativeY ? 1 : 0, ctx))
|
||||
if (EC_POINT_set_compressed_coordinates_GFp (m_Group, C, r, isNegativeY ? 1 : 0, ctx))
|
||||
{
|
||||
EC_POINT * S = EC_POINT_new (m_Group); // S = s*P
|
||||
EC_POINT_mul (m_Group, S, s, nullptr, nullptr, ctx);
|
||||
|
|
|
|||
152
libi2pd/HTTP.cpp
152
libi2pd/HTTP.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
|
||||
*
|
||||
|
|
@ -10,7 +10,6 @@
|
|||
#include <utility>
|
||||
#include <stdio.h>
|
||||
#include <ctime>
|
||||
#include <charconv>
|
||||
#include "util.h"
|
||||
#include "Base.h"
|
||||
#include "HTTP.h"
|
||||
|
|
@ -19,68 +18,58 @@ namespace i2p
|
|||
{
|
||||
namespace http
|
||||
{
|
||||
// list of valid HTTP methods
|
||||
static constexpr std::array<std::string_view, 16> HTTP_METHODS =
|
||||
{
|
||||
const std::vector<std::string> 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<std::string_view, 2> HTTP_VERSIONS =
|
||||
{
|
||||
const std::vector<std::string> HTTP_VERSIONS = {
|
||||
"HTTP/1.0", "HTTP/1.1"
|
||||
};
|
||||
|
||||
static constexpr std::array<const char *, 7> weekdays =
|
||||
{
|
||||
const std::vector<const char *> weekdays = {
|
||||
"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
|
||||
};
|
||||
|
||||
static constexpr std::array<const char *, 12> months =
|
||||
{
|
||||
const std::vector<const char *> 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<std::string_view> &tokens, char delim, std::size_t limit = 0)
|
||||
{
|
||||
size_t count = 1, pos;
|
||||
while ((pos = line.find (delim)) != line.npos)
|
||||
{
|
||||
void strsplit(const std::string & line, std::vector<std::string> &tokens, char delim, std::size_t limit = 0) {
|
||||
std::size_t count = 0;
|
||||
std::stringstream ss(line);
|
||||
std::string token;
|
||||
while (1) {
|
||||
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 std::pair<std::string, std::string> parse_header_line(std::string_view line)
|
||||
static std::pair<std::string, std::string> parse_header_line(const std::string& line)
|
||||
{
|
||||
std::size_t pos = 0;
|
||||
std::size_t len = 1; /*: */
|
||||
std::size_t max = line.length();
|
||||
if ((pos = line.find(':', pos)) == std::string::npos)
|
||||
return std::pair{"", ""}; // no ':' found
|
||||
return std::make_pair("", ""); // no ':' found
|
||||
if (pos + 1 < max) // ':' at the end of header is valid
|
||||
{
|
||||
while ((pos + len) < max && isspace(line.at(pos + len)))
|
||||
len++;
|
||||
if (len == 1)
|
||||
return std::pair{"", ""}; // no following space, but something else
|
||||
return std::make_pair("", ""); // no following space, but something else
|
||||
}
|
||||
return std::pair{std::string (line.substr(0, pos)), std::string (line.substr(pos + len))};
|
||||
return std::make_pair(line.substr(0, pos), line.substr(pos + len));
|
||||
}
|
||||
|
||||
void gen_rfc7231_date(std::string & out) {
|
||||
|
|
@ -94,18 +83,15 @@ namespace http
|
|||
out = buf;
|
||||
}
|
||||
|
||||
bool URL::parse(const char *str, std::size_t len)
|
||||
{
|
||||
return parse({str, len ? len : strlen(str)});
|
||||
bool URL::parse(const char *str, std::size_t len) {
|
||||
std::string url(str, len ? len : strlen(str));
|
||||
return parse(url);
|
||||
}
|
||||
|
||||
bool URL::parse(std::string_view url)
|
||||
{
|
||||
if (url.empty ()) return false;
|
||||
bool URL::parse(const std::string& url) {
|
||||
std::size_t pos_p = 0; /* < current parse position */
|
||||
std::size_t pos_c = 0; /* < work position */
|
||||
if(url.at(0) != '/' || pos_p > 0)
|
||||
{
|
||||
if(url.at(0) != '/' || pos_p > 0) {
|
||||
std::size_t pos_s = 0;
|
||||
|
||||
/* schema */
|
||||
|
|
@ -155,7 +141,7 @@ namespace http
|
|||
/* port[/path] */
|
||||
pos_p = pos_c + 1;
|
||||
pos_c = url.find('/', pos_p);
|
||||
std::string_view port_str = (pos_c == std::string::npos)
|
||||
std::string port_str = (pos_c == std::string::npos)
|
||||
? url.substr(pos_p, std::string::npos)
|
||||
: url.substr(pos_p, pos_c - pos_p);
|
||||
/* stoi throws exception on failure, we don't need it */
|
||||
|
|
@ -209,9 +195,8 @@ namespace http
|
|||
return true;
|
||||
}
|
||||
|
||||
bool URL::parse_query(std::map<std::string, std::string> & params)
|
||||
{
|
||||
std::vector<std::string_view> tokens;
|
||||
bool URL::parse_query(std::map<std::string, std::string> & params) {
|
||||
std::vector<std::string> tokens;
|
||||
strsplit(query, tokens, '&');
|
||||
|
||||
params.clear();
|
||||
|
|
@ -268,7 +253,7 @@ namespace http
|
|||
return host.rfind(".i2p") == ( host.size() - 4 );
|
||||
}
|
||||
|
||||
void HTTPMsg::add_header(const char *name, const std::string & value, bool replace) {
|
||||
void HTTPMsg::add_header(const char *name, std::string & value, bool replace) {
|
||||
add_header(name, value.c_str(), replace);
|
||||
}
|
||||
|
||||
|
|
@ -287,13 +272,12 @@ namespace http
|
|||
headers.erase(name);
|
||||
}
|
||||
|
||||
int HTTPReq::parse(const char *buf, size_t len)
|
||||
{
|
||||
return parse({buf, len});
|
||||
int HTTPReq::parse(const char *buf, size_t len) {
|
||||
std::string str(buf, len);
|
||||
return parse(str);
|
||||
}
|
||||
|
||||
int HTTPReq::parse(std::string_view str)
|
||||
{
|
||||
int HTTPReq::parse(const std::string& str) {
|
||||
enum { REQ_LINE, HEADER_LINE } expect = REQ_LINE;
|
||||
std::size_t eoh = str.find(HTTP_EOH); /* request head size */
|
||||
std::size_t eol = 0, pos = 0;
|
||||
|
|
@ -302,14 +286,11 @@ namespace http
|
|||
if (eoh == std::string::npos)
|
||||
return 0; /* str not contains complete request */
|
||||
|
||||
while ((eol = str.find(CRLF, pos)) != std::string::npos)
|
||||
{
|
||||
if (expect == REQ_LINE)
|
||||
{
|
||||
std::string_view line = str.substr(pos, eol - pos);
|
||||
std::vector<std::string_view> tokens;
|
||||
while ((eol = str.find(CRLF, pos)) != std::string::npos) {
|
||||
if (expect == REQ_LINE) {
|
||||
std::string line = str.substr(pos, eol - pos);
|
||||
std::vector<std::string> tokens;
|
||||
strsplit(line, tokens, ' ');
|
||||
|
||||
if (tokens.size() != 3)
|
||||
return -1;
|
||||
if (!is_http_method(tokens[0]))
|
||||
|
|
@ -326,18 +307,18 @@ namespace http
|
|||
}
|
||||
else
|
||||
{
|
||||
std::string_view line = str.substr(pos, eol - pos);
|
||||
std::string line = str.substr(pos, eol - pos);
|
||||
auto p = parse_header_line(line);
|
||||
if (p.first.length () > 0)
|
||||
headers.push_back (p);
|
||||
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 +362,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 +370,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)
|
||||
|
|
@ -432,13 +413,12 @@ namespace http
|
|||
return length;
|
||||
}
|
||||
|
||||
int HTTPRes::parse(const char *buf, size_t len)
|
||||
{
|
||||
return parse({buf,len});
|
||||
int HTTPRes::parse(const char *buf, size_t len) {
|
||||
std::string str(buf, len);
|
||||
return parse(str);
|
||||
}
|
||||
|
||||
int HTTPRes::parse(std::string_view str)
|
||||
{
|
||||
int HTTPRes::parse(const std::string& str) {
|
||||
enum { RES_LINE, HEADER_LINE } expect = RES_LINE;
|
||||
std::size_t eoh = str.find(HTTP_EOH); /* request head size */
|
||||
std::size_t eol = 0, pos = 0;
|
||||
|
|
@ -446,41 +426,35 @@ namespace http
|
|||
if (eoh == std::string::npos)
|
||||
return 0; /* str not contains complete request */
|
||||
|
||||
while ((eol = str.find(CRLF, pos)) != std::string::npos)
|
||||
{
|
||||
if (expect == RES_LINE)
|
||||
{
|
||||
std::string_view line = str.substr(pos, eol - pos);
|
||||
std::vector<std::string_view> tokens;
|
||||
while ((eol = str.find(CRLF, pos)) != std::string::npos) {
|
||||
if (expect == RES_LINE) {
|
||||
std::string line = str.substr(pos, eol - pos);
|
||||
std::vector<std::string> 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 */
|
||||
version = tokens[0];
|
||||
status = tokens[2];
|
||||
expect = HEADER_LINE;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string_view line = str.substr(pos, eol - pos);
|
||||
} else {
|
||||
std::string line = str.substr(pos, eol - pos);
|
||||
auto p = parse_header_line(line);
|
||||
if (p.first.length () > 0)
|
||||
headers.insert (p);
|
||||
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 +479,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;
|
||||
|
|
@ -536,14 +508,14 @@ namespace http
|
|||
return ptr;
|
||||
}
|
||||
|
||||
std::string UrlDecode(std::string_view data, bool allow_null)
|
||||
std::string UrlDecode(const std::string& data, bool allow_null)
|
||||
{
|
||||
std::string decoded(data);
|
||||
size_t pos = 0;
|
||||
while ((pos = decoded.find('%', pos)) != std::string::npos)
|
||||
{
|
||||
char c = std::stol(decoded.substr(pos + 1, 2), nullptr, 16);
|
||||
if (!c && !allow_null)
|
||||
char c = strtol(decoded.substr(pos + 1, 2).c_str(), NULL, 16);
|
||||
if (c == '\0' && !allow_null)
|
||||
{
|
||||
pos += 3;
|
||||
continue;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
*
|
||||
|
|
@ -14,15 +14,16 @@
|
|||
#include <list>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
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<std::string> HTTP_METHODS; /**< list of valid HTTP methods */
|
||||
extern const std::vector<std::string> HTTP_VERSIONS; /**< list of valid HTTP versions */
|
||||
|
||||
struct URL
|
||||
{
|
||||
|
|
@ -44,7 +45,7 @@ namespace http
|
|||
* @return true on success, false on invalid url
|
||||
*/
|
||||
bool parse (const char *str, std::size_t len = 0);
|
||||
bool parse (std::string_view url);
|
||||
bool parse (const std::string& url);
|
||||
|
||||
/**
|
||||
* @brief Parse query part of url to key/value map
|
||||
|
|
@ -68,7 +69,7 @@ namespace http
|
|||
{
|
||||
std::map<std::string, std::string> headers;
|
||||
|
||||
void add_header(const char *name, const std::string & value, bool replace = false);
|
||||
void add_header(const char *name, std::string & value, bool replace = false);
|
||||
void add_header(const char *name, const char *value, bool replace = false);
|
||||
void del_header(const char *name);
|
||||
|
||||
|
|
@ -91,7 +92,7 @@ namespace http
|
|||
* @note Positive return value is a size of header
|
||||
*/
|
||||
int parse(const char *buf, size_t len);
|
||||
int parse(std::string_view buf);
|
||||
int parse(const std::string& buf);
|
||||
|
||||
/** @brief Serialize HTTP request to string */
|
||||
std::string to_string();
|
||||
|
|
@ -101,8 +102,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 (); };
|
||||
};
|
||||
|
||||
|
|
@ -127,7 +128,7 @@ namespace http
|
|||
* @note Positive return value is a size of header
|
||||
*/
|
||||
int parse(const char *buf, size_t len);
|
||||
int parse(const std::string_view buf);
|
||||
int parse(const std::string& buf);
|
||||
|
||||
/**
|
||||
* @brief Serialize HTTP response to string
|
||||
|
|
@ -152,7 +153,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
|
||||
|
|
@ -160,7 +161,7 @@ namespace http
|
|||
* @param null If set to true - decode also %00 sequence, otherwise - skip
|
||||
* @return Decoded string
|
||||
*/
|
||||
std::string UrlDecode(std::string_view data, bool null = false);
|
||||
std::string UrlDecode(const std::string& data, bool null = false);
|
||||
|
||||
/**
|
||||
* @brief Merge HTTP response content with Transfer-Encoding: chunked
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue