Compare commits

..

No commits in common. 'openssl' and '2.49.0' have entirely different histories.

@ -1,24 +1,6 @@
name: Build Debian packages name: Build Debian packages
on: on: [push, pull_request]
push:
branches:
- '*'
paths:
- .github/workflows/build-deb.yml
- contrib/**
- daemon/**
- debian/**
- i18n/**
- libi2pd/**
- libi2pd_client/**
- Makefile
- Makefile.linux
tags:
- '*'
pull_request:
branches:
- '*'
jobs: jobs:
build: build:
@ -32,30 +14,26 @@ jobs:
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v3
with: with:
fetch-depth: 0 fetch-depth: 0
- name: Commit Hash
id: commit
uses: prompt/actions-commit-hash@v3.0.0
- name: Build package - name: Build package
uses: jtdor/build-deb-action@v1 uses: jtdor/build-deb-action@v1
with: with:
docker-image: debian:${{ matrix.dist }}-slim docker-image: debian:${{ matrix.dist }}-slim
buildpackage-opts: --build=binary --no-sign buildpackage-opts: --build=binary --no-sign
before-build-hook: debchange --controlmaint --local "+${{ steps.commit.outputs.short }}~${{ matrix.dist }}" -b --distribution ${{ matrix.dist }} "CI build" before-build-hook: debchange --controlmaint --local "+${{ github.sha }}~${{ matrix.dist }}" -b --distribution ${{ matrix.dist }} "CI build"
extra-build-deps: devscripts git extra-build-deps: devscripts git
- name: Upload package - name: Upload package
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v3
with: with:
name: i2pd_${{ matrix.dist }} name: i2pd_${{ matrix.dist }}
path: debian/artifacts/i2pd_*.deb path: debian/artifacts/i2pd_*.deb
- name: Upload debugging symbols - name: Upload debugging symbols
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v3
with: with:
name: i2pd-dbgsym_${{ matrix.dist }} name: i2pd-dbgsym_${{ matrix.dist }}
path: debian/artifacts/i2pd-dbgsym_*.deb path: debian/artifacts/i2pd-dbgsym_*.deb

@ -1,37 +1,19 @@
name: Build on FreeBSD name: Build on FreeBSD
on: on: [push, pull_request]
push:
branches:
- '*'
paths:
- .github/workflows/build-freebsd.yml
- build/CMakeLists.txt
- build/cmake_modules/**
- daemon/**
- i18n/**
- libi2pd/**
- libi2pd_client/**
- Makefile
- Makefile.bsd
tags:
- '*'
pull_request:
branches:
- '*'
jobs: jobs:
build: build:
runs-on: ubuntu-latest runs-on: macos-12
name: with UPnP name: with UPnP
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v3
- name: Test in FreeBSD - name: Test in FreeBSD
id: test id: test
uses: vmactions/freebsd-vm@v1 uses: vmactions/freebsd-vm@v0.3.0
with: with:
usesh: true usesh: true
mem: 2048 mem: 2048
@ -44,7 +26,7 @@ jobs:
gmake -j2 gmake -j2
- name: Upload artifacts - name: Upload artifacts
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v3
with: with:
name: i2pd-freebsd name: i2pd-freebsd
path: build/i2pd path: build/i2pd

@ -1,22 +1,6 @@
name: Build on OSX name: Build on OSX
on: on: [push, pull_request]
push:
branches:
- '*'
paths:
- .github/workflows/build-osx.yml
- daemon/**
- i18n/**
- libi2pd/**
- libi2pd_client/**
- Makefile
- Makefile.homebrew
tags:
- '*'
pull_request:
branches:
- '*'
jobs: jobs:
build: build:
@ -30,16 +14,13 @@ jobs:
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v3
- name: Install required formulae - name: install packages
run: | run: |
find /usr/local/bin -lname '*/Library/Frameworks/Python.framework/*' -delete find /usr/local/bin -lname '*/Library/Frameworks/Python.framework/*' -delete
brew update brew update
brew install boost miniupnpc openssl@1.1 brew install boost miniupnpc openssl@1.1
- name: List installed formulae - name: build application
run: brew list
- name: Build application
run: make HOMEBREW=1 USE_UPNP=${{ matrix.with_upnp }} PREFIX=$GITHUB_WORKSPACE/output -j3 run: make HOMEBREW=1 USE_UPNP=${{ matrix.with_upnp }} PREFIX=$GITHUB_WORKSPACE/output -j3

@ -0,0 +1,52 @@
name: Build on Windows with MSVC
on: [push, pull_request]
jobs:
build:
name: Build
runs-on: windows-latest
strategy:
fail-fast: false
steps:
- name: Checkout
uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Build and install zlib
run: |
powershell -Command "(Invoke-WebRequest -Uri https://raw.githubusercontent.com/r4sas/zlib.install/master/install.bat -OutFile install_zlib.bat)"
powershell -Command "(Get-Content install_zlib.bat) | Set-Content install_zlib.bat" # fixing line endings
set BUILD_TYPE=Debug
./install_zlib.bat
set BUILD_TYPE=Release
./install_zlib.bat
del install_zlib.bat
- name: Install Boost
uses: crazy-max/ghaction-chocolatey@v2
with:
args: install boost-msvc-14.3 --version=1.81.0
- name: Install OpenSSL
uses: crazy-max/ghaction-chocolatey@v2
with:
args: install openssl
- name: Configure
working-directory: build
run: cmake -DWITH_STATIC=ON .
- name: Build
working-directory: build
run: cmake --build . --config Debug -- -m
- name: Upload artifacts
uses: actions/upload-artifact@v3
with:
name: i2pd-msvc
path: build/Debug/i2pd.*

@ -1,80 +0,0 @@
name: Build on Windows with MSVC
on:
push:
branches:
- '*'
paths:
- .github/workflows/build-windows-msvc.yml
- build/CMakeLists.txt
- build/cmake_modules/**
- daemon/**
- i18n/**
- libi2pd/**
- libi2pd_client/**
- Win32/**
tags:
- '*'
pull_request:
branches:
- '*'
jobs:
build:
name: Build
runs-on: windows-latest
env:
boost_path: ${{ github.workspace }}\boost_1_83_0
openssl_path: ${{ github.workspace }}\openssl_3_2_1
strategy:
fail-fast: false
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Build and install zlib
run: |
powershell -Command "(Invoke-WebRequest -Uri https://raw.githubusercontent.com/r4sas/zlib.install/master/install.bat -OutFile install_zlib.bat)"
powershell -Command "(Get-Content install_zlib.bat) | Set-Content install_zlib.bat" # fixing line endings
set BUILD_TYPE=Debug
./install_zlib.bat
set BUILD_TYPE=Release
./install_zlib.bat
del install_zlib.bat
- name: Install Boost
run: |
powershell -Command "(Start-BitsTransfer -Source https://sourceforge.net/projects/boost/files/boost-binaries/1.83.0/boost_1_83_0-msvc-14.3-64.exe/download -Destination boost_1_83_0-msvc-14.3-64.exe)"
./boost_1_83_0-msvc-14.3-64.exe /DIR="${{env.boost_path}}" /VERYSILENT /SUPPRESSMSGBOXES /SP-
- name: Install OpenSSL
run: |
powershell -Command "(Start-BitsTransfer -Source https://slproweb.com/download/Win64OpenSSL-3_2_1.exe -Destination Win64OpenSSL-3_2_1.exe)"
./Win64OpenSSL-3_2_1.exe /DIR="${{env.openssl_path}}" /TASKS="copytobin" /VERYSILENT /SUPPRESSMSGBOXES /NORESTART /SP-
- name: Make copy of the OpenSSL libraries for CMake
run: |
dir ${{ github.workspace }}
dir ${{env.openssl_path}}\lib\VC
dir ${{env.openssl_path}}\lib\VC\x64\
dir ${{env.openssl_path}}\lib\VC\x64\MTd\
xcopy /s /y "${{env.openssl_path}}\lib\VC\x64\MTd" "${{env.openssl_path}}\lib"
- name: Configure
working-directory: build
run: cmake -DBoost_ROOT="${{env.boost_path}}" -DOPENSSL_ROOT_DIR="${{env.openssl_path}}" -DWITH_STATIC=ON .
- name: Build
working-directory: build
run: cmake --build . --config Debug -- -m
- name: Upload artifacts
uses: actions/upload-artifact@v4
with:
name: i2pd-msvc
path: build/Debug/i2pd.*

@ -1,25 +1,6 @@
name: Build on Windows name: Build on Windows
on: on: [push, pull_request]
push:
branches:
- '*'
paths:
- .github/workflows/build-windows.yml
- build/CMakeLists.txt
- build/cmake_modules/**
- daemon/**
- i18n/**
- libi2pd/**
- libi2pd_client/**
- Win32/**
- Makefile
- Makefile.mingw
tags:
- '*'
pull_request:
branches:
- '*'
defaults: defaults:
run: run:
@ -42,7 +23,7 @@ jobs:
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v3
with: with:
fetch-depth: 0 fetch-depth: 0
@ -63,7 +44,7 @@ jobs:
make USE_UPNP=yes DEBUG=no USE_GIT_VERSION=yes -j3 make USE_UPNP=yes DEBUG=no USE_GIT_VERSION=yes -j3
- name: Upload artifacts - name: Upload artifacts
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v3
with: with:
name: i2pd-${{ matrix.arch_short }}.exe name: i2pd-${{ matrix.arch_short }}.exe
path: i2pd.exe path: i2pd.exe
@ -84,7 +65,7 @@ jobs:
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v3
with: with:
fetch-depth: 0 fetch-depth: 0
@ -102,7 +83,7 @@ jobs:
cmake --build . -- -j3 cmake --build . -- -j3
- name: Upload artifacts - name: Upload artifacts
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v3
with: with:
name: i2pd-cmake-${{ matrix.arch_short }}.exe name: i2pd-cmake-${{ matrix.arch_short }}.exe
path: build/i2pd.exe path: build/i2pd.exe
@ -116,7 +97,7 @@ jobs:
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v3
with: with:
fetch-depth: 0 fetch-depth: 0
@ -125,117 +106,34 @@ jobs:
with: with:
msystem: MINGW32 msystem: MINGW32
install: base-devel git mingw-w64-i686-gcc mingw-w64-i686-boost mingw-w64-i686-openssl mingw-w64-i686-miniupnpc install: base-devel git mingw-w64-i686-gcc mingw-w64-i686-boost mingw-w64-i686-openssl mingw-w64-i686-miniupnpc
cache: true
update: true update: true
- name: Clone MinGW packages repository - name: Build WinXP-capable CRT packages
run: git clone https://github.com/msys2/MINGW-packages
# headers
- name: Get headers package version
id: version-headers
run: |
echo "version=$(pacman -Si mingw-w64-i686-headers-git | grep -Po '^Version\s*: \K.+')" >> $GITHUB_OUTPUT
- name: Cache headers package
uses: actions/cache@v4
id: cache-headers
with:
path: MINGW-packages/mingw-w64-headers-git/*.zst
key: winxp-headers-${{ steps.version-headers.outputs.version }}
- name: Build WinXP-capable headers package
if: steps.cache-headers.outputs.cache-hit != 'true'
run: | run: |
cd MINGW-packages/mingw-w64-headers-git git clone https://github.com/msys2/MINGW-packages
pushd MINGW-packages
pushd mingw-w64-headers-git
sed -i 's/0x601/0x501/' PKGBUILD sed -i 's/0x601/0x501/' PKGBUILD
MINGW_ARCH=mingw32 makepkg-mingw -sCLf --noconfirm --nocheck MINGW_ARCH=mingw32 makepkg-mingw -sCLf --noconfirm
- name: Install headers package pacman --noconfirm -U mingw-w64-i686-headers-git-*-any.pkg.tar.zst
run: pacman --noconfirm -U MINGW-packages/mingw-w64-headers-git/mingw-w64-i686-*-any.pkg.tar.zst popd
pushd mingw-w64-crt-git
# CRT MINGW_ARCH=mingw32 makepkg-mingw -sCLf --noconfirm
- name: Get crt package version pacman --noconfirm -U mingw-w64-i686-crt-git-*-any.pkg.tar.zst
id: version-crt popd
run: | pushd mingw-w64-winpthreads-git
echo "version=$(pacman -Si mingw-w64-i686-crt-git | grep -Po '^Version\s*: \K.+')" >> $GITHUB_OUTPUT MINGW_ARCH=mingw32 makepkg-mingw -sCLf --noconfirm
- name: Cache crt package pacman --noconfirm -U mingw-w64-i686-libwinpthread-git-*-any.pkg.tar.zst mingw-w64-i686-winpthreads-git-*-any.pkg.tar.zst
uses: actions/cache@v4 popd
id: cache-crt popd
with:
path: MINGW-packages/mingw-w64-crt-git/*.zst
key: winxp-crt-${{ steps.version-crt.outputs.version }}
- name: Build WinXP-capable crt package
if: steps.cache-crt.outputs.cache-hit != 'true'
run: |
cd MINGW-packages/mingw-w64-crt-git
MINGW_ARCH=mingw32 makepkg-mingw -sCLf --noconfirm --nocheck
- name: Install crt package
run: pacman --noconfirm -U MINGW-packages/mingw-w64-crt-git/mingw-w64-i686-*-any.pkg.tar.zst
# winpthreads
- name: Get winpthreads package version
id: version-winpthreads
run: |
echo "version=$(pacman -Si mingw-w64-i686-winpthreads-git | grep -Po '^Version\s*: \K.+')" >> $GITHUB_OUTPUT
- name: Cache winpthreads package
uses: actions/cache@v4
id: cache-winpthreads
with:
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-git
MINGW_ARCH=mingw32 makepkg-mingw -sCLf --noconfirm --nocheck
- name: Install winpthreads package
run: pacman --noconfirm -U MINGW-packages/mingw-w64-winpthreads-git/mingw-w64-i686-*-any.pkg.tar.zst
# OpenSSL
- name: Get openssl package version
id: version-openssl
run: |
echo "version=$(pacman -Si mingw-w64-i686-openssl | grep -Po '^Version\s*: \K.+')" >> $GITHUB_OUTPUT
- name: Cache openssl package
uses: actions/cache@v4
id: cache-openssl
with:
path: MINGW-packages/mingw-w64-openssl/*.zst
key: winxp-openssl-${{ steps.version-openssl.outputs.version }}
- name: Build WinXP-capable openssl package
if: steps.cache-openssl.outputs.cache-hit != 'true'
run: |
cd MINGW-packages/mingw-w64-openssl
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: Cache boost package
uses: actions/cache@v4
id: cache-boost
with:
path: MINGW-packages/mingw-w64-boost/*.zst
key: winxp-winpthreads-${{ steps.version-boost.outputs.version }}
- 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: Install boost package
run: pacman --noconfirm -U MINGW-packages/mingw-w64-boost/mingw-w64-i686-*-any.pkg.tar.zst
# Building i2pd
- name: Build application - name: Build application
run: | run: |
mkdir -p obj/Win32 obj/libi2pd obj/libi2pd_client obj/daemon 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 make USE_UPNP=yes DEBUG=no USE_GIT_VERSION=yes USE_WINXP_FLAGS=yes -j3
- name: Upload artifacts - name: Upload artifacts
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v3
with: with:
name: i2pd-xp.exe name: i2pd-xp.exe
path: i2pd.exe path: i2pd.exe

@ -1,24 +1,6 @@
name: Build on Ubuntu name: Build on Ubuntu
on: on: [push, pull_request]
push:
branches:
- '*'
paths:
- .github/workflows/build.yml
- build/CMakeLists.txt
- build/cmake_modules/**
- daemon/**
- i18n/**
- libi2pd/**
- libi2pd_client/**
- Makefile
- Makefile.linux
tags:
- '*'
pull_request:
branches:
- '*'
jobs: jobs:
build-make: build-make:
@ -32,7 +14,7 @@ jobs:
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v3
- name: install packages - name: install packages
run: | run: |
@ -53,7 +35,7 @@ jobs:
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v3
- name: install packages - name: install packages
run: | run: |

@ -5,16 +5,6 @@ on:
branches: branches:
- openssl - openssl
- docker - docker
paths:
- .github/workflows/docker.yml
- contrib/docker/**
- contrib/certificates/**
- daemon/**
- i18n/**
- libi2pd/**
- libi2pd_client/**
- Makefile
- Makefile.linux
tags: tags:
- '*' - '*'
@ -37,29 +27,29 @@ jobs:
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v3
- name: Set up QEMU - name: Set up QEMU
uses: docker/setup-qemu-action@v3 uses: docker/setup-qemu-action@v2
- name: Set up Docker Buildx - name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3 uses: docker/setup-buildx-action@v2
- name: Login to DockerHub - name: Login to DockerHub
uses: docker/login-action@v3 uses: docker/login-action@v2
with: with:
username: ${{ secrets.DOCKERHUB_USERNAME }} username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }} password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Login to GitHub Container registry - name: Login to GitHub Container registry
uses: docker/login-action@v3 uses: docker/login-action@v2
with: with:
registry: ghcr.io registry: ghcr.io
username: ${{ github.actor }} username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }} password: ${{ secrets.GITHUB_TOKEN }}
- name: Build container for ${{ matrix.archname }} - name: Build container for ${{ matrix.archname }}
uses: docker/build-push-action@v5 uses: docker/build-push-action@v3
with: with:
context: ./contrib/docker context: ./contrib/docker
file: ./contrib/docker/Dockerfile file: ./contrib/docker/Dockerfile
@ -82,22 +72,22 @@ jobs:
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v3
- name: Set up QEMU - name: Set up QEMU
uses: docker/setup-qemu-action@v3 uses: docker/setup-qemu-action@v2
- name: Set up Docker Buildx - name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3 uses: docker/setup-buildx-action@v2
- name: Login to DockerHub - name: Login to DockerHub
uses: docker/login-action@v3 uses: docker/login-action@v2
with: with:
username: ${{ secrets.DOCKERHUB_USERNAME }} username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }} password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Login to GitHub Container registry - name: Login to GitHub Container registry
uses: docker/login-action@v3 uses: docker/login-action@v2
with: with:
registry: ghcr.io registry: ghcr.io
username: ${{ github.actor }} username: ${{ github.actor }}

@ -1,116 +1,6 @@
# for this file format description, # for this file format description,
# see https://github.com/olivierlacan/keep-a-changelog # see https://github.com/olivierlacan/keep-a-changelog
## [2.52.0] - 2024-05-12
### Added
- Separate threads for persisting RouterInfos and profiles to disk
- Give preference to address with direct connection
- Exclude addresses with incorrect static or intro key
- Avoid two firewalled routers in the row in tunnel
- Drop unsolicited database search replies
### Changed
- Increase number of hashes to 16 in exploratory lookup reply
- Reduce number of a RouterInfo lookup attempts to 5
- Reset stream RTO if outbound tunnel was changed
- Insert previously excluded floodfill back when successfully connected
- Increase maximum stream resend attempts to 9
- Reply to exploratory lookups with only confirmed routers if low tunnel build rate
- Don't accept too old RouterInfo
- Build client tunnels through confirmed routers only if low tunnel build rate
- Manage netDb requests more frequently
- Don't reply with closer than us only floodfills for lookup
### Fixed
- Crash on router lookup if exploratory pool is not ready
- Race condition in excluded peers for next lookup
- Excessive number of lookups for same destination
- Race condition with transport peers during shutdown
- Corrupted RouterInfo files
## [2.51.0] - 2024-04-06
### Added
- Non-blocking mode for UDP sockets
- Set SSU2 socket buffer size based on bandwidth limit
- Encrypted tunnel tests
- Support for multiple UDP server tunnels on one destination
- Publish medium congestion indication
- Local domain sockets for SOCKS proxy upstream
- Tunnel status "declined" in web console
- SAM error reply "Incompatible crypto" if remote destination has incompatible crypto
- Reduce amount of traffic by handling local message drops
- Keep SSU2 socket open even if it fails to bind
- Lower SSU2 resend traffic spikes
- Expiration for messages in SSU2 send queue
- Use EWMA for stream RTT estimation
- Request choking delay if too many NACKs in stream
- Allow 0ms latency for tunnel
- Randomize tunnels selection for tests
### Changed
- Upstream SOCKS proxy from SOCKS4 to SOCKS5
- Transit tunnels limit to 4 bytes. Default value to 10K
- Reply CANT_REACH_PEER if connect to ourselves in SAM
- Don't send already expired I2NP messages
- Use monotonic timer to measure tunnel test latency
- Standard NTCP2 frame doesn't exceed 16K
- Always send request through tunnels in case of restricted routes
- Don't delete connected routers from NetDb
- Send lookup reply directly to reply tunnel gateway if possible
- Reduce unreachable router ban interval to 8 minutes
- Don't request banned routers / don't try to connect to unreachable router
- Consider 'M' routers as low bandwidth
- Limit minimal received SSU2 packet size to 40 bytes
- Bob picks peer test session only if Charlie's address supports peer testing
- Reject peer test msg 2 if peer testing is not supported
- Don't request termination if SSU2 session was not established
- Set maximum SSU2 queue size depending on RTT value
- New streaming RTT calculation algorithm
- Don't double initial RTO for streams when changing tunnels
- Restore failed tunnel if test or data for inbound tunnel received
- Don't fail last remaining tunnel in pool
- Publish LeasetSet again if local destination was not ready or no tunnels
- Make more attempts to pick high bandwidth hop for client tunnel
- Reduced SSU2 session termination timeout to 165 seconds
- Reseeds list
### Fixed
- ECIESx25519 symmetric key tagset early expiration
- Encrypted LeaseSet lookup
- Outbound tunnel build fails if it's endpoint is the same as reply tunnel gateway
- I2PControl RouterManager returns invalid JSON when unknown params are passed
- Mix of data between different UDP sessions on the same server
- TARGET_OS_SIMULATOR check
- Handling of "reservedrange" param
- New NTCP2 session gets teminated upon termination of old one
- New SSU2 session gets teminated upon termination of old one
- Peer test to non-supporting router
- Streaming ackThrough off 1 if number of NACKs exceeds 255
- Race condition in ECIESx25519 tags table
- Good tunnel becomes failed
- Crash when packet comes to terminated stream
- Stream hangs during LeaseSet update
## [2.50.2] - 2024-01-06
###Fixed
- Crash with OpenSSL 3.2.0
- False positive clock skew detection
## [2.50.1] - 2023-12-23
###Fixed
- Support for new EdDSA usage behavior in OpenSSL 3.2.0
## [2.50.0] - 2023-12-18
### Added
- Support of concurrent ACCEPTs on SAM 3.1
- Haiku OS support
- Low bandwidth and far routers can expire before 1 hour
### Changed
- Don't pick too active peer for first hop
- Try peer test again if status is Unknown
- Send peer tests with random delay
- Reseeds list
### Fixed
- XSS vulnerability in addresshelper
- Publishing NAT64 ipv6 addresses
- Deadlock in AsyncSend callback
## [2.49.0] - 2023-09-18 ## [2.49.0] - 2023-09-18
### Added ### Added
- Handle SOCK5 authorization with empty user/password - Handle SOCK5 authorization with empty user/password

@ -67,9 +67,6 @@ else ifneq (, $(findstring linux, $(SYS))$(findstring gnu, $(SYS)))
else ifneq (, $(findstring freebsd, $(SYS))$(findstring openbsd, $(SYS))) else ifneq (, $(findstring freebsd, $(SYS))$(findstring openbsd, $(SYS)))
DAEMON_SRC += $(DAEMON_SRC_DIR)/UnixDaemon.cpp DAEMON_SRC += $(DAEMON_SRC_DIR)/UnixDaemon.cpp
include Makefile.bsd include Makefile.bsd
else ifneq (, $(findstring haiku, $(SYS)))
DAEMON_SRC += $(DAEMON_SRC_DIR)/UnixDaemon.cpp
include Makefile.haiku
else # not supported else # not supported
$(error Not supported platform) $(error Not supported platform)
endif endif
@ -121,7 +118,7 @@ obj/%.o: %.cpp | mk_obj_dir
-include $(DEPS) -include $(DEPS)
$(I2PD): $(DAEMON_OBJS) $(ARLIB) $(ARLIB_CLIENT) $(ARLIB_LANG) $(I2PD): $(DAEMON_OBJS) $(ARLIB) $(ARLIB_CLIENT) $(ARLIB_LANG)
$(CXX) $(DEFINES) $(LDFLAGS) -o $@ $^ $(LDLIBS) $(CXX) -o $@ $(DEFINES) $(LDFLAGS) $^ $(LDLIBS)
$(SHLIB): $(LIB_OBJS) $(SHLIB): $(LIB_OBJS)
ifneq ($(USE_STATIC),yes) ifneq ($(USE_STATIC),yes)

@ -6,12 +6,7 @@ CXXFLAGS ?= ${CXX_DEBUG} -Wall -Wextra -Wno-unused-parameter -pedantic -Wno-misl
## (e.g. -fstack-protector-strong -Wformat -Werror=format-security), we do not want to remove ## (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 ## -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. ## custom FLAGS to work at build-time.
CXXVER := $(shell $(CXX) -dumpversion) NEEDED_CXXFLAGS = -std=c++11
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 DEFINES = -D_GLIBCXX_USE_NANOSLEEP=1
INCFLAGS = -I/usr/include/ -I/usr/local/include/ INCFLAGS = -I/usr/include/ -I/usr/local/include/
LDFLAGS = ${LD_DEBUG} -Wl,-rpath,/usr/local/lib -L/usr/local/lib LDFLAGS = ${LD_DEBUG} -Wl,-rpath,/usr/local/lib -L/usr/local/lib

@ -1,10 +0,0 @@
CXX = g++
CXXFLAGS := -Wall -std=c++11
INCFLAGS = -I/system/develop/headers
DEFINES = -D_DEFAULT_SOURCE -D_GNU_SOURCE
LDLIBS = -lbe -lbsd -lnetwork -lz -lcrypto -lssl -lboost_system -lboost_date_time -lboost_filesystem -lboost_program_options -lpthread
ifeq ($(USE_UPNP),yes)
DEFINES += -DUSE_UPNP
LDLIBS += -lminiupnpc
endif

@ -1,40 +1,41 @@
# root directory holding homebrew # root directory holding homebrew
BREWROOT = /opt/homebrew BREWROOT = /usr/local
BOOSTROOT = ${BREWROOT}/opt/boost BOOSTROOT = ${BREWROOT}/opt/boost
SSLROOT = ${BREWROOT}/opt/openssl@1.1 SSLROOT = ${BREWROOT}/opt/openssl@1.1
UPNPROOT = ${BREWROOT}/opt/miniupnpc UPNPROOT = ${BREWROOT}/opt/miniupnpc
CXXFLAGS = ${CXX_DEBUG} -Wall -std=c++11 -DMAC_OSX -Wno-overloaded-virtual
INCFLAGS = -I${SSLROOT}/include -I${BOOSTROOT}/include
LDFLAGS = ${LD_DEBUG}
CXXFLAGS ?= ${CXX_DEBUG} -Wall -Wno-overloaded-virtual ifndef TRAVIS
NEEDED_CXXFLAGS ?= -std=c++11 CXX = clang++
INCFLAGS ?= -I${SSLROOT}/include -I${BOOSTROOT}/include endif
LDFLAGS ?= ${LD_DEBUG}
DEFINES += -DMAC_OSX
ifeq ($(USE_STATIC),yes) ifeq ($(USE_STATIC),yes)
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 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 -lpthread
ifeq ($(USE_UPNP),yes)
LDLIBS += ${UPNPROOT}/lib/libminiupnpc.a
endif
LDLIBS += -lpthread -ldl
else else
LDFLAGS += -L${SSLROOT}/lib -L${BOOSTROOT}/lib LDFLAGS += -L${SSLROOT}/lib -L${BOOSTROOT}/lib
LDLIBS = -lz -lcrypto -lssl -lboost_system -lboost_date_time -lboost_filesystem -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
endif
endif endif
ifeq ($(USE_UPNP),yes) ifeq ($(USE_UPNP),yes)
DEFINES += -DUSE_UPNP LDFLAGS += -ldl
CXXFLAGS += -DUSE_UPNP
INCFLAGS += -I${UPNPROOT}/include INCFLAGS += -I${UPNPROOT}/include
ifeq ($(USE_STATIC),yes)
LDLIBS += ${UPNPROOT}/lib/libminiupnpc.a
else
LDFLAGS += -L${UPNPROOT}/lib
LDLIBS += -lminiupnpc
endif
endif endif
# OSX Notes
# http://www.hutsby.net/2011/08/macs-with-aes-ni.html
# Seems like all recent Mac's have AES-NI, after firmware upgrade 2.2
# Found no good way to detect it from command line. TODO: Might be some osx sysinfo magic
ifeq ($(USE_AESNI),yes) ifeq ($(USE_AESNI),yes)
ifneq (, $(findstring i386, $(SYS))$(findstring i686, $(SYS))$(findstring x86_64, $(SYS))) # only x86-based CPU supports that CXXFLAGS += -D__AES__ -maes
NEEDED_CXXFLAGS += -maes
DEFINES += -D__AES__
endif
endif endif
install: all install: all

@ -5,11 +5,14 @@ WINDRES = windres
CXXFLAGS := $(CXX_DEBUG) -fPIC -msse CXXFLAGS := $(CXX_DEBUG) -fPIC -msse
INCFLAGS := -I$(DAEMON_SRC_DIR) -IWin32 INCFLAGS := -I$(DAEMON_SRC_DIR) -IWin32
LDFLAGS := ${LD_DEBUG} -static -fPIC -msse LDFLAGS := ${LD_DEBUG} -static
NEEDED_CXXFLAGS += -std=c++17 NEEDED_CXXFLAGS += -std=c++17
DEFINES += -DWIN32_LEAN_AND_MEAN DEFINES += -DWIN32_LEAN_AND_MEAN
# Boost libraries suffix
BOOST_SUFFIX = -mt
# UPNP Support # UPNP Support
ifeq ($(USE_UPNP),yes) ifeq ($(USE_UPNP),yes)
DEFINES += -DUSE_UPNP -DMINIUPNP_STATICLIB DEFINES += -DUSE_UPNP -DMINIUPNP_STATICLIB
@ -17,18 +20,17 @@ ifeq ($(USE_UPNP),yes)
endif endif
LDLIBS += \ LDLIBS += \
$(MINGW_PREFIX)/lib/libboost_system-mt.a \ -lboost_system$(BOOST_SUFFIX) \
$(MINGW_PREFIX)/lib/libboost_date_time-mt.a \ -lboost_date_time$(BOOST_SUFFIX) \
$(MINGW_PREFIX)/lib/libboost_filesystem-mt.a \ -lboost_filesystem$(BOOST_SUFFIX) \
$(MINGW_PREFIX)/lib/libboost_program_options-mt.a \ -lboost_program_options$(BOOST_SUFFIX) \
$(MINGW_PREFIX)/lib/libssl.a \ -lssl \
$(MINGW_PREFIX)/lib/libcrypto.a \ -lcrypto \
$(MINGW_PREFIX)/lib/libz.a \ -lz \
-lwsock32 \ -lwsock32 \
-lws2_32 \ -lws2_32 \
-liphlpapi \
-lcrypt32 \
-lgdi32 \ -lgdi32 \
-liphlpapi \
-lole32 \ -lole32 \
-luuid \ -luuid \
-lpthread -lpthread
@ -46,7 +48,6 @@ endif
ifeq ($(USE_AESNI),yes) ifeq ($(USE_AESNI),yes)
NEEDED_CXXFLAGS += -maes NEEDED_CXXFLAGS += -maes
LDFLAGS += -maes
DEFINES += -D__AES__ DEFINES += -D__AES__
endif endif

@ -5,6 +5,7 @@ DEFINES := -DMAC_OSX
LDFLAGS := -Wl,-rpath,/usr/local/lib -L/usr/local/lib LDFLAGS := -Wl,-rpath,/usr/local/lib -L/usr/local/lib
LDFLAGS += -Wl,-dead_strip LDFLAGS += -Wl,-dead_strip
LDFLAGS += -Wl,-dead_strip_dylibs LDFLAGS += -Wl,-dead_strip_dylibs
LDFLAGS += -Wl,-bind_at_load
ifeq ($(USE_STATIC),yes) ifeq ($(USE_STATIC),yes)
LDLIBS = -lz /usr/local/lib/libcrypto.a /usr/local/lib/libssl.a /usr/local/lib/libboost_system.a /usr/local/lib/libboost_date_time.a /usr/local/lib/libboost_filesystem.a /usr/local/lib/libboost_program_options.a -lpthread 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

@ -99,7 +99,7 @@ Current status: [![Crowdin](https://badges.crowdin.net/i2pd/localized.svg)](http
Donations Donations
--------- ---------
**E-Mail**: ```i2porignal at yandex.com``` **E-Mail**: ```i2porignal at yandex.ru```
**BTC**: ```3MDoGJW9TLMTCDGrR9bLgWXfm6sjmgy86f``` **BTC**: ```3MDoGJW9TLMTCDGrR9bLgWXfm6sjmgy86f```

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2024, The PurpleI2P Project * Copyright (c) 2013-2022, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@ -145,7 +145,7 @@ namespace win32
s << bytes << " Bytes\n"; s << bytes << " Bytes\n";
} }
static void ShowNetworkStatus (std::stringstream& s, RouterStatus status, bool testing, RouterError error) static void ShowNetworkStatus (std::stringstream& s, RouterStatus status, bool testing)
{ {
switch (status) switch (status)
{ {
@ -158,24 +158,18 @@ namespace win32
}; };
if (testing) if (testing)
s << " (Test)"; s << " (Test)";
if (error != eRouterErrorNone) if (i2p::context.GetError () != eRouterErrorNone)
{ {
switch (error) switch (i2p::context.GetError ())
{ {
case eRouterErrorClockSkew: case eRouterErrorClockSkew:
s << " - " << tr("Clock skew"); s << " - Clock skew";
break; break;
case eRouterErrorOffline: case eRouterErrorOffline:
s << " - " << tr("Offline"); s << " - Offline";
break; break;
case eRouterErrorSymmetricNAT: case eRouterErrorSymmetricNAT:
s << " - " << tr("Symmetric NAT"); s << " - Symmetric NAT";
break;
case eRouterErrorFullConeNAT:
s << " - " << tr("Full cone NAT");
break;
case eRouterErrorNoDescriptors:
s << " - " << tr("No Descriptors");
break; break;
default: ; default: ;
} }
@ -186,11 +180,11 @@ namespace win32
{ {
s << "\n"; s << "\n";
s << "Status: "; s << "Status: ";
ShowNetworkStatus (s, i2p::context.GetStatus (), i2p::context.GetTesting(), i2p::context.GetError ()); ShowNetworkStatus (s, i2p::context.GetStatus (), i2p::context.GetTesting ());
if (i2p::context.SupportsV6 ()) if (i2p::context.SupportsV6 ())
{ {
s << " / "; s << " / ";
ShowNetworkStatus (s, i2p::context.GetStatusV6 (), i2p::context.GetTestingV6(), i2p::context.GetErrorV6 ()); ShowNetworkStatus (s, i2p::context.GetStatusV6 (), i2p::context.GetTestingV6 ());
} }
s << "; "; s << "; ";
s << "Success Rate: " << i2p::tunnel::tunnels.GetTunnelCreationSuccessRate() << "%\n"; s << "Success Rate: " << i2p::tunnel::tunnels.GetTunnelCreationSuccessRate() << "%\n";

@ -73,24 +73,16 @@ void UnSubscribeFromEvents()
} }
if (pNetEvent) if (pNetEvent)
{
pNetEvent->Release(); pNetEvent->Release();
}
if (pCPContainer) if (pCPContainer)
{
pCPContainer->Release(); pCPContainer->Release();
}
if (pNetworkListManager) if (pNetworkListManager)
{
pNetworkListManager->Release(); pNetworkListManager->Release();
}
if (pUnknown) if (pUnknown)
{
pUnknown->Release(); pUnknown->Release();
}
CoUninitialize(); CoUninitialize();
} }

@ -15,11 +15,10 @@
#include "Log.h" #include "Log.h"
#include "Transports.h" #include "Transports.h"
class CNetworkListManagerEvent final : public INetworkListManagerEvents class CNetworkListManagerEvent : public INetworkListManagerEvents
{ {
public: public:
CNetworkListManagerEvent() : m_ref(1) { } CNetworkListManagerEvent() : m_ref(1) { }
~CNetworkListManagerEvent() { }
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject) HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject)
{ {

@ -25,7 +25,7 @@ project(
i2pd i2pd
VERSION ${PROJECT_VERSION} VERSION ${PROJECT_VERSION}
HOMEPAGE_URL "https://i2pd.website/" HOMEPAGE_URL "https://i2pd.website/"
LANGUAGES C CXX LANGUAGES CXX
) )
# configurable options # configurable options
@ -121,7 +121,7 @@ if(WIN32)
) )
file(GLOB WIN32_RC ${WIN32_SRC_DIR}/*.rc) file(GLOB WIN32_RC ${WIN32_SRC_DIR}/*.rc)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DWIN32_APP -DWIN32_LEAN_AND_MEAN -DNOMINMAX") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DWIN32_APP -DWIN32_LEAN_AND_MEAN")
endif() endif()
@ -139,10 +139,6 @@ if(APPLE)
add_definitions(-DMAC_OSX) add_definitions(-DMAC_OSX)
endif() endif()
if(HAIKU)
add_definitions(-D_DEFAULT_SOURCE -D_GNU_SOURCE)
endif()
if(MSVC) if(MSVC)
add_definitions(-DWINVER=0x0600) add_definitions(-DWINVER=0x0600)
add_definitions(-D_WIN32_WINNT=0x0600) add_definitions(-D_WIN32_WINNT=0x0600)

@ -1,42 +0,0 @@
# _________________________________________
# / Copy this file to the right location \
# | then load with: |
# | |
# | apparmor_parser -r -W |
# | /etc/apparmor.d/docker-i2pd |
# | |
# | docker run --security-opt |
# | "apparmor=docker-i2pd" ... |
# | purplei2p/i2pd |
# | |
# \ And "aa-status" to verify it's loaded. /
# -----------------------------------------
# \ ^__^
# \ (oo)\_______
# (__)\ )\/\
# ||----w |
# || ||
#include <tunables/global>
profile docker-i2pd flags=(attach_disconnected,mediate_deleted) {
#include <abstractions/base>
#include <abstractions/openssl>
#include <abstractions/nameservice>
/bin/busybox ix,
/usr/local/bin/i2pd ix,
/entrypoint.sh ixr,
/i2pd_certificates/** r,
/home/i2pd/data/** rw,
/home/i2pd/data/i2pd.pid k,
deny /home/i2pd/data/i2pd.conf w,
deny /home/i2pd/data/tunnels.conf w,
deny /home/i2pd/data/tunnels.d/** w,
deny /home/i2pd/data/certificates/** w,
deny /home/i2pd/data/i2pd.log r,
}

@ -1,34 +0,0 @@
-----BEGIN CERTIFICATE-----
MIIF1zCCA7+gAwIBAgIRAMDqFR09Xuj8ZUu+oetSvAEwDQYJKoZIhvcNAQELBQAw
dTELMAkGA1UEBhMCWFgxCzAJBgNVBAcTAlhYMQswCQYDVQQJEwJYWDEeMBwGA1UE
ChMVSTJQIEFub255bW91cyBOZXR3b3JrMQwwCgYDVQQLEwNJMlAxHjAcBgNVBAMM
FWFkbWluQHN0b3JteWNsb3VkLm9yZzAeFw0yNDAxMjUxNDE1MzBaFw0zNDAxMjUx
NDE1MzBaMHUxCzAJBgNVBAYTAlhYMQswCQYDVQQHEwJYWDELMAkGA1UECRMCWFgx
HjAcBgNVBAoTFUkyUCBBbm9ueW1vdXMgTmV0d29yazEMMAoGA1UECxMDSTJQMR4w
HAYDVQQDDBVhZG1pbkBzdG9ybXljbG91ZC5vcmcwggIiMA0GCSqGSIb3DQEBAQUA
A4ICDwAwggIKAoICAQDbGX+GikPzQXr9zvkrhfO9g0l49KHLNQhUKYqd6T+PfnGo
Fm0d3ZZVVQZ045vWgroOXDGGZZWxUIlb2inRaR2DF1TxN3pPYt59RgY9ZQ9+TL7o
isY91krCRygY8EcAmHIjlfZQ9dBVcL7CfyT0MYZA5Efee9+NDHSewTfQP9T2faIE
83Fcyd93a2mIHYjKUbJnojng/wgsy8srbsEuuTok4MIQmDj+B5nz+za2FgI0/ydh
srlMt4aGJF4/DIem9z9d0zBCOkwrmtFIzjNF1mOSA8ES4m5YnKA/y9rZlRidLPGu
prbXhPVnqHeOnHMz2QCw1wbVo504kl0bMqyEz2tVWsO9ep7iZoQs2xkFAEaegYNT
QLUpwVGlyuq3wXXwopFRffOSimGSazICwWI6j+K0pOtgefNJaWrqKYvtkj1SbK2L
LBNUIENz6VnB7KPRckuX6zxC8PpOiBK9BcftfO+xAz/wC6qq3riBPw30KKSym0nC
Zp5KciDn4Phtw9PGq8Bkl8SyWl0jtFnfTB1tzJkisf2qKcNHaFTEe2JW763YLbh/
AU+8X8evFu40qLgvOgKoyy5DLy6i8zetX+3t9K0Fxt9+Vzzq6lm5V/RS8iIPPn+M
q1/3Z5kD0KQBG9h/Gl8BH+lB71ZxPAOZ3SMu8DJZcxBLVmDWqQPCr5CKnoz0swID
AQABo2IwYDAOBgNVHQ8BAf8EBAMCAoQwHQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsG
AQUFBwMBMA8GA1UdEwEB/wQFMAMBAf8wHgYDVR0OBBcEFWFkbWluQHN0b3JteWNs
b3VkLm9yZzANBgkqhkiG9w0BAQsFAAOCAgEARWOJ69vTHMneSXYscha+4Ytjg0RM
faewJNEGj8qy/Qvh9si2bWYNPRK6BlbHFS7pRYBLAnhaeLBGVv1CCR6GUMMe74zQ
UuMeAoWU6qMDmB3GfYoZJh8sIxpwHqyJeTdeccRbZ4sX4F6u3IHPXYiU/AgbYqH7
pYXQg2lCjXZYaDFAlEf5SlYUDOhhXe5kR8Edhlrsu32/JzA1DQK0JjxKCBp+DQmA
ltdOpQtAg03fHP4ssdj7VvjIDl28iIlATwBvHrdNm7T0tYWn6TWhvxbRqvfTxfaH
MvxnPdIJwNP4/9TyQkwjwHb1h+ucho3CnxI/AxspdOvT1ElMhP6Ce6rcS9pk11Rl
x0ChsqpWwDg7KYpg0qZFSKCTBp4zBq9xoMJ6BQcgMfyl736WbsCzFTEyfifp8beg
NxUa/Qk7w7cuSPGyMIKNOmOR7FLlFbtocy8sXVsUQdqnp/edelufdNe39U9uNtY6
yoXI9//Tc6NgOwy2Oyia0slZ5qHRkB7e4USXMRzJ3p4q9eCVKjAJs81Utp7O2U+9
vhbhwWP8CAnNTT1E5WS6EKtfrdqF7wjkV+noPGLDGmrXi01J1fSMAjMfVO+7/LOL
UN+G4ybKWnEhhOO27yidN8Xx6UrCS23DBlPPQAeA74dTsTExiOxf1o1EXzcQiMyO
LAj3/Ojbi1xkWhI=
-----END CERTIFICATE-----

@ -225,7 +225,7 @@ verify = true
## Default: "mainline" I2P Network reseeds ## Default: "mainline" I2P Network reseeds
# urls = https://reseed.i2p-projekt.de/,https://i2p.mooo.com/netDb/,https://netdb.i2p2.no/ # urls = https://reseed.i2p-projekt.de/,https://i2p.mooo.com/netDb/,https://netdb.i2p2.no/
## Reseed URLs through the Yggdrasil, separated by comma ## Reseed URLs through the Yggdrasil, separated by comma
# yggurls = http://[324:71e:281a:9ed3::ace]:7070/ # yggurls = http://[324:9de3:fea4:f6ac::ace]:7070/
## Path to local reseed data file (.su3) for manual reseeding ## Path to local reseed data file (.su3) for manual reseeding
# file = /path/to/i2pseeds.su3 # file = /path/to/i2pseeds.su3
## or HTTPS URL to reseed from ## or HTTPS URL to reseed from

@ -1,7 +1,7 @@
%define git_hash %(git rev-parse HEAD | cut -c -7) %define git_hash %(git rev-parse HEAD | cut -c -7)
Name: i2pd-git Name: i2pd-git
Version: 2.52.0 Version: 2.49.0
Release: git%{git_hash}%{?dist} Release: git%{git_hash}%{?dist}
Summary: I2P router written in C++ Summary: I2P router written in C++
Conflicts: i2pd Conflicts: i2pd
@ -144,21 +144,6 @@ getent passwd i2pd >/dev/null || \
%changelog %changelog
* Sun May 12 2024 orignal <orignal@i2pmail.org> - 2.52.0
- update to 2.52.0
* Sat Apr 06 2024 orignal <orignal@i2pmail.org> - 2.51.0
- update to 2.51.0
* Sat Jan 06 2024 orignal <orignal@i2pmail.org> - 2.50.2
- update to 2.50.2
* Sat Dec 23 2023 r4sas <r4sas@i2pmail.org> - 2.50.1
- update to 2.50.1
* Mon Dec 18 2023 orignal <orignal@i2pmail.org> - 2.50.0
- update to 2.50.0
* Mon Sep 18 2023 orignal <orignal@i2pmail.org> - 2.49.0 * Mon Sep 18 2023 orignal <orignal@i2pmail.org> - 2.49.0
- update to 2.49.0 - update to 2.49.0

@ -1,5 +1,5 @@
Name: i2pd Name: i2pd
Version: 2.52.0 Version: 2.49.0
Release: 1%{?dist} Release: 1%{?dist}
Summary: I2P router written in C++ Summary: I2P router written in C++
Conflicts: i2pd-git Conflicts: i2pd-git
@ -142,21 +142,6 @@ getent passwd i2pd >/dev/null || \
%changelog %changelog
* Sun May 12 2024 orignal <orignal@i2pmail.org> - 2.52.0
- update to 2.52.0
* Sat Apr 06 2024 orignal <orignal@i2pmail.org> - 2.51.0
- update to 2.51.0
* Sat Jan 06 2024 orignal <orignal@i2pmail.org> - 2.50.2
- update to 2.50.2
* Sat Dec 23 2023 r4sas <r4sas@i2pmail.org> - 2.50.1
- update to 2.50.1
* Mon Dec 18 2023 orignal <orignal@i2pmail.org> - 2.50.0
- update to 2.50.0
* Mon Sep 18 2023 orignal <orignal@i2pmail.org> - 2.49.0 * Mon Sep 18 2023 orignal <orignal@i2pmail.org> - 2.49.0
- update to 2.49.0 - update to 2.49.0

@ -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 * This file is part of Purple i2pd project and licensed under BSD3
* *
@ -160,10 +160,6 @@ namespace util
int netID; i2p::config::GetOption("netid", netID); int netID; i2p::config::GetOption("netid", netID);
i2p::context.SetNetID (netID); i2p::context.SetNetID (netID);
bool checkReserved; i2p::config::GetOption("reservedrange", checkReserved);
i2p::transport::transports.SetCheckReserved(checkReserved);
i2p::context.Init (); i2p::context.Init ();
i2p::transport::InitTransports (); i2p::transport::InitTransports ();
@ -179,7 +175,7 @@ namespace util
bool transit; i2p::config::GetOption("notransit", transit); bool transit; i2p::config::GetOption("notransit", transit);
i2p::context.SetAcceptsTunnels (!transit); i2p::context.SetAcceptsTunnels (!transit);
uint32_t transitTunnels; i2p::config::GetOption("limits.transittunnels", transitTunnels); uint16_t transitTunnels; i2p::config::GetOption("limits.transittunnels", transitTunnels);
if (isFloodfill && i2p::config::IsDefault ("limits.transittunnels")) if (isFloodfill && i2p::config::IsDefault ("limits.transittunnels"))
transitTunnels *= 2; // double default number of transit tunnels for floodfill transitTunnels *= 2; // double default number of transit tunnels for floodfill
i2p::tunnel::tunnels.SetMaxNumTransitTunnels (transitTunnels); i2p::tunnel::tunnels.SetMaxNumTransitTunnels (transitTunnels);
@ -188,7 +184,7 @@ namespace util
std::string bandwidth; i2p::config::GetOption("bandwidth", bandwidth); std::string bandwidth; i2p::config::GetOption("bandwidth", bandwidth);
if (bandwidth.length () > 0) if (bandwidth.length () > 0)
{ {
if (bandwidth.length () == 1 && ((bandwidth[0] >= 'K' && bandwidth[0] <= 'P') || bandwidth[0] == 'X' )) if (bandwidth[0] >= 'K' && bandwidth[0] <= 'X')
{ {
i2p::context.SetBandwidth (bandwidth[0]); i2p::context.SetBandwidth (bandwidth[0]);
LogPrint(eLogInfo, "Daemon: Bandwidth set to ", i2p::context.GetBandwidthLimit (), "KBps"); LogPrint(eLogInfo, "Daemon: Bandwidth set to ", i2p::context.GetBandwidthLimit (), "KBps");
@ -302,10 +298,12 @@ namespace util
bool ntcp2; i2p::config::GetOption("ntcp2.enabled", ntcp2); bool ntcp2; i2p::config::GetOption("ntcp2.enabled", ntcp2);
bool ssu2; i2p::config::GetOption("ssu2.enabled", ssu2); bool ssu2; i2p::config::GetOption("ssu2.enabled", ssu2);
bool checkInReserved; i2p::config::GetOption("reservedrange", checkInReserved);
LogPrint(eLogInfo, "Daemon: Starting Transports"); LogPrint(eLogInfo, "Daemon: Starting Transports");
if(!ssu2) LogPrint(eLogInfo, "Daemon: SSU2 disabled"); if(!ssu2) LogPrint(eLogInfo, "Daemon: SSU2 disabled");
if(!ntcp2) LogPrint(eLogInfo, "Daemon: NTCP2 disabled"); if(!ntcp2) LogPrint(eLogInfo, "Daemon: NTCP2 disabled");
i2p::transport::transports.SetCheckReserved(checkInReserved);
i2p::transport::transports.Start(ntcp2, ssu2); i2p::transport::transports.Start(ntcp2, ssu2);
if (i2p::transport::transports.IsBoundSSU2() || i2p::transport::transports.IsBoundNTCP2()) if (i2p::transport::transports.IsBoundSSU2() || i2p::transport::transports.IsBoundNTCP2())
LogPrint(eLogInfo, "Daemon: Transports started"); LogPrint(eLogInfo, "Daemon: Transports started");

@ -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 * This file is part of Purple i2pd project and licensed under BSD3
* *
@ -133,19 +133,23 @@ namespace http {
static void ShowTunnelDetails (std::stringstream& s, enum i2p::tunnel::TunnelState eState, bool explr, int bytes) static void ShowTunnelDetails (std::stringstream& s, enum i2p::tunnel::TunnelState eState, bool explr, int bytes)
{ {
std::string state, stateText; std::string state, stateText;
switch (eState) switch (eState) {
{
case i2p::tunnel::eTunnelStateBuildReplyReceived : case i2p::tunnel::eTunnelStateBuildReplyReceived :
case i2p::tunnel::eTunnelStatePending : state = "building"; break; case i2p::tunnel::eTunnelStatePending : state = "building"; break;
case i2p::tunnel::eTunnelStateBuildFailed : state = "failed"; stateText = "declined"; break; case i2p::tunnel::eTunnelStateBuildFailed :
case i2p::tunnel::eTunnelStateTestFailed : state = "failed"; stateText = "test failed"; break; case i2p::tunnel::eTunnelStateTestFailed :
case i2p::tunnel::eTunnelStateFailed : state = "failed"; break; case i2p::tunnel::eTunnelStateFailed : state = "failed"; break;
case i2p::tunnel::eTunnelStateExpiring : state = "expiring"; break; case i2p::tunnel::eTunnelStateExpiring : state = "expiring"; break;
case i2p::tunnel::eTunnelStateEstablished : state = "established"; break; case i2p::tunnel::eTunnelStateEstablished : state = "established"; break;
default: state = "unknown"; break; default: state = "unknown"; break;
} }
if (stateText.empty ()) stateText = tr(state);
if (state == "building") stateText = tr("building");
else if (state == "failed") stateText = tr("failed");
else if (state == "expiring") stateText = tr("expiring");
else if (state == "established") stateText = tr("established");
else stateText = tr("unknown");
s << "<span class=\"tunnel " << state << "\"> " << stateText << ((explr) ? " (" + tr("exploratory") + ")" : "") << "</span>, "; s << "<span class=\"tunnel " << state << "\"> " << stateText << ((explr) ? " (" + tr("exploratory") + ")" : "") << "</span>, ";
ShowTraffic(s, bytes); ShowTraffic(s, bytes);
s << "\r\n"; s << "\r\n";
@ -772,7 +776,7 @@ namespace http {
s << " <a class=\"button" << (loglevel == eLogInfo ? " selected" : "") << "\" href=\"" << webroot << "?cmd=" << HTTP_COMMAND_LOGLEVEL << "&level=info&token=" << token << "\"> info </a> \r\n"; s << " <a class=\"button" << (loglevel == eLogInfo ? " selected" : "") << "\" href=\"" << webroot << "?cmd=" << HTTP_COMMAND_LOGLEVEL << "&level=info&token=" << token << "\"> info </a> \r\n";
s << " <a class=\"button" << (loglevel == eLogDebug ? " selected" : "") << "\" href=\"" << webroot << "?cmd=" << HTTP_COMMAND_LOGLEVEL << "&level=debug&token=" << token << "\"> debug </a><br>\r\n<br>\r\n"; s << " <a class=\"button" << (loglevel == eLogDebug ? " selected" : "") << "\" href=\"" << webroot << "?cmd=" << HTTP_COMMAND_LOGLEVEL << "&level=debug&token=" << token << "\"> debug </a><br>\r\n<br>\r\n";
uint32_t maxTunnels = i2p::tunnel::tunnels.GetMaxNumTransitTunnels (); uint16_t maxTunnels = i2p::tunnel::tunnels.GetMaxNumTransitTunnels ();
s << "<b>" << tr("Transit tunnels limit") << "</b><br>\r\n"; s << "<b>" << tr("Transit tunnels limit") << "</b><br>\r\n";
s << "<form method=\"get\" action=\"" << webroot << "\">\r\n"; s << "<form method=\"get\" action=\"" << webroot << "\">\r\n";
s << " <input type=\"hidden\" name=\"cmd\" value=\"" << HTTP_COMMAND_LIMITTRANSIT << "\">\r\n"; s << " <input type=\"hidden\" name=\"cmd\" value=\"" << HTTP_COMMAND_LIMITTRANSIT << "\">\r\n";

@ -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 * This file is part of Purple i2pd project and licensed under BSD3
* *
@ -25,7 +25,7 @@ namespace http
const size_t HTTP_CONNECTION_BUFFER_SIZE = 8192; const size_t HTTP_CONNECTION_BUFFER_SIZE = 8192;
const int TOKEN_EXPIRATION_TIMEOUT = 30; // in seconds const int TOKEN_EXPIRATION_TIMEOUT = 30; // in seconds
const int COMMAND_REDIRECT_TIMEOUT = 5; // in seconds const int COMMAND_REDIRECT_TIMEOUT = 5; // in seconds
const int TRANSIT_TUNNELS_LIMIT = 1000000; const int TRANSIT_TUNNELS_LIMIT = 65535;
class HTTPConnection: public std::enable_shared_from_this<HTTPConnection> class HTTPConnection: public std::enable_shared_from_this<HTTPConnection>
{ {

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2024, The PurpleI2P Project * Copyright (c) 2013-2022, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@ -338,11 +338,10 @@ namespace client
{ {
for (auto it = params.begin (); it != params.end (); it++) for (auto it = params.begin (); it != params.end (); it++)
{ {
if (it != params.begin ()) results << ",";
LogPrint (eLogDebug, "I2PControl: RouterManager request: ", it->first); LogPrint (eLogDebug, "I2PControl: RouterManager request: ", it->first);
auto it1 = m_RouterManagerHandlers.find (it->first); auto it1 = m_RouterManagerHandlers.find (it->first);
if (it1 != m_RouterManagerHandlers.end ()) if (it1 != m_RouterManagerHandlers.end ()) {
{
if (it != params.begin ()) results << ",";
(this->*(it1->second))(results); (this->*(it1->second))(results);
} else } else
LogPrint (eLogError, "I2PControl: RouterManager unknown request: ", it->first); LogPrint (eLogError, "I2PControl: RouterManager unknown request: ", it->first);

@ -1,15 +1,10 @@
/*
* Copyright (c) 2013-2024, The PurpleI2P Project
*
* This file is part of Purple i2pd project and licensed under BSD3
*
* See full license text in LICENSE file at top of project tree
*/
#ifdef USE_UPNP #ifdef USE_UPNP
#include <string> #include <string>
#include <thread> #include <thread>
#include <boost/thread/thread.hpp>
#include <boost/asio.hpp>
#include "Log.h" #include "Log.h"
#include "RouterContext.h" #include "RouterContext.h"
@ -115,16 +110,10 @@ namespace transport
return; return;
} }
#if (MINIUPNPC_API_VERSION >= 18)
err = UPNP_GetValidIGD (m_Devlist, &m_upnpUrls, &m_upnpData, m_NetworkAddr, sizeof (m_NetworkAddr),
m_externalIPAddress, sizeof (m_externalIPAddress));
#else
err = UPNP_GetValidIGD (m_Devlist, &m_upnpUrls, &m_upnpData, m_NetworkAddr, sizeof (m_NetworkAddr)); err = UPNP_GetValidIGD (m_Devlist, &m_upnpUrls, &m_upnpData, m_NetworkAddr, sizeof (m_NetworkAddr));
#endif
m_upnpUrlsInitialized=err!=0; m_upnpUrlsInitialized=err!=0;
if (err == UPNP_IGD_VALID_CONNECTED) if (err == UPNP_IGD_VALID_CONNECTED)
{ {
#if (MINIUPNPC_API_VERSION < 18)
err = UPNP_GetExternalIPAddress (m_upnpUrls.controlURL, m_upnpData.first.servicetype, m_externalIPAddress); err = UPNP_GetExternalIPAddress (m_upnpUrls.controlURL, m_upnpData.first.servicetype, m_externalIPAddress);
if(err != UPNPCOMMAND_SUCCESS) if(err != UPNPCOMMAND_SUCCESS)
{ {
@ -132,7 +121,6 @@ namespace transport
return; return;
} }
else else
#endif
{ {
LogPrint (eLogError, "UPnP: Found Internet Gateway Device ", m_upnpUrls.controlURL); LogPrint (eLogError, "UPnP: Found Internet Gateway Device ", m_upnpUrls.controlURL);
if (!m_externalIPAddress[0]) if (!m_externalIPAddress[0])
@ -178,11 +166,11 @@ namespace transport
if (address && !address->host.is_v6 () && address->port) if (address && !address->host.is_v6 () && address->port)
TryPortMapping (address); TryPortMapping (address);
} }
m_Timer.expires_from_now (boost::posix_time::minutes(UPNP_PORT_FORWARDING_INTERVAL)); // every 20 minutes m_Timer.expires_from_now (boost::posix_time::minutes(20)); // every 20 minutes
m_Timer.async_wait ([this](const boost::system::error_code& ecode) m_Timer.async_wait ([this](const boost::system::error_code& ecode)
{ {
if (ecode != boost::asio::error::operation_aborted) if (ecode != boost::asio::error::operation_aborted)
PortMapping (); PortMapping ();
}); });
} }

@ -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 * This file is part of Purple i2pd project and licensed under BSD3
* *
@ -28,8 +28,7 @@ namespace i2p
namespace transport namespace transport
{ {
const int UPNP_RESPONSE_TIMEOUT = 2000; // in milliseconds const int UPNP_RESPONSE_TIMEOUT = 2000; // in milliseconds
const int UPNP_PORT_FORWARDING_INTERVAL = 20; // in minutes
enum enum
{ {
UPNP_IGD_NONE = 0, UPNP_IGD_NONE = 0,

@ -162,21 +162,12 @@ namespace i2p
#ifndef ANDROID #ifndef ANDROID
if (lockf(pidFH, F_TLOCK, 0) != 0) if (lockf(pidFH, F_TLOCK, 0) != 0)
#else
struct flock fl;
fl.l_len = 0;
fl.l_type = F_WRLCK;
fl.l_whence = SEEK_SET;
fl.l_start = 0;
if (fcntl(pidFH, F_SETLK, &fl) != 0)
#endif
{ {
LogPrint(eLogError, "Daemon: Could not lock pid file ", pidfile, ": ", strerror(errno)); LogPrint(eLogError, "Daemon: Could not lock pid file ", pidfile, ": ", strerror(errno));
std::cerr << "i2pd: Could not lock pid file " << pidfile << ": " << strerror(errno) << std::endl; std::cerr << "i2pd: Could not lock pid file " << pidfile << ": " << strerror(errno) << std::endl;
return false; return false;
} }
#endif
char pid[10]; char pid[10];
sprintf(pid, "%d\n", getpid()); sprintf(pid, "%d\n", getpid());
ftruncate(pidFH, 0); ftruncate(pidFH, 0);

30
debian/changelog vendored

@ -1,33 +1,3 @@
i2pd (2.52.0-1) unstable; urgency=medium
* updated to version 2.52.0
-- orignal <orignal@i2pmail.org> Sun, 12 May 2024 16:00:00 +0000
i2pd (2.51.0-1) unstable; urgency=medium
* updated to version 2.51.0/0.9.62
-- orignal <orignal@i2pmail.org> Sat, 06 Apr 2024 16:00:00 +0000
i2pd (2.50.2-1) unstable; urgency=medium
* updated to version 2.50.2/0.9.61
-- orignal <orignal@i2pmail.org> Sat, 06 Jan 2024 16:00:00 +0000
i2pd (2.50.1-1) unstable; urgency=medium
* updated to version 2.50.1/0.9.61
-- r4sas <r4sas@i2pmail.org> Sat, 23 Dec 2023 18:30:00 +0000
i2pd (2.50.0-1) unstable; urgency=medium
* updated to version 2.50.0/0.9.61
-- orignal <orignal@i2pmail.org> Mon, 18 Dec 2023 16:00:00 +0000
i2pd (2.49.0-1) unstable; urgency=medium i2pd (2.49.0-1) unstable; urgency=medium
* updated to version 2.49.0/0.9.60 * updated to version 2.49.0/0.9.60

2
debian/i2pd.1 vendored

@ -64,7 +64,7 @@ The network interface to bind to for IPv4 connections
The network interface to bind to for IPv6 connections The network interface to bind to for IPv6 connections
.TP .TP
\fB\-\-ipv4=\fR \fB\-\-ipv4=\fR
Enable communication through ipv4 (\fIenabled\fR by default) Enable communication through ipv6 (\fIenabled\fR by default)
.TP .TP
\fB\-\-ipv6\fR \fB\-\-ipv6\fR
Enable communication through ipv6 (\fIdisabled\fR by default) Enable communication through ipv6 (\fIdisabled\fR by default)

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2022-2024, The PurpleI2P Project * Copyright (c) 2022-2023, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@ -69,7 +69,6 @@ namespace chinese // language namespace
{"Stopping in", "距停止还有:"}, {"Stopping in", "距停止还有:"},
{"Family", "家族"}, {"Family", "家族"},
{"Tunnel creation success rate", "隧道创建成功率"}, {"Tunnel creation success rate", "隧道创建成功率"},
{"Total tunnel creation success rate", "当前隧道创建成功率"},
{"Received", "已接收"}, {"Received", "已接收"},
{"%.2f KiB/s", "%.2f KiB/s"}, {"%.2f KiB/s", "%.2f KiB/s"},
{"Sent", "已发送"}, {"Sent", "已发送"},
@ -96,7 +95,6 @@ namespace chinese // language namespace
{"Address", "地址"}, {"Address", "地址"},
{"Type", "类型"}, {"Type", "类型"},
{"EncType", "加密类型"}, {"EncType", "加密类型"},
{"Expire LeaseSet", "到期租约集"},
{"Inbound tunnels", "入站隧道"}, {"Inbound tunnels", "入站隧道"},
{"%dms", "%dms"}, {"%dms", "%dms"},
{"Outbound tunnels", "出站隧道"}, {"Outbound tunnels", "出站隧道"},
@ -153,8 +151,6 @@ namespace chinese // language namespace
{"StreamID can't be null", "StreamID 不能为空"}, {"StreamID can't be null", "StreamID 不能为空"},
{"Return to destination page", "返回目标页面"}, {"Return to destination page", "返回目标页面"},
{"You will be redirected in %d seconds", "您将在%d秒内被重定向"}, {"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"}, {"Transit tunnels count must not exceed %d", "中转隧道数量限制为 %d"},
{"Back to commands list", "返回命令列表"}, {"Back to commands list", "返回命令列表"},
{"Register at reg.i2p", "在 reg.i2p 注册域名"}, {"Register at reg.i2p", "在 reg.i2p 注册域名"},

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2022-2024, The PurpleI2P Project * Copyright (c) 2022-2023, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@ -36,18 +36,18 @@ namespace czech // language namespace
{"%.2f GiB", "%.2f GiB"}, {"%.2f GiB", "%.2f GiB"},
{"building", "vytváří se"}, {"building", "vytváří se"},
{"failed", "selhalo"}, {"failed", "selhalo"},
{"expiring", "vyprší platnost"}, {"expiring", "končící"},
{"established", "vytvořeno"}, {"established", "vytvořeno"},
{"unknown", "neznámý"}, {"unknown", "neznámý"},
{"exploratory", "průzkumné"}, {"exploratory", "průzkumné"},
{"Purple I2P Webconsole", "Purple I2P webová konzole"}, {"Purple I2P Webconsole", "Purple I2P Webkonsole"},
{"<b>i2pd</b> webconsole", "<b>i2pd</b> webová konzole"}, {"<b>i2pd</b> webconsole", "<b>i2pd</b> webkonsole"},
{"Main page", "Hlavní stránka"}, {"Main page", "Hlavní stránka"},
{"Router commands", "Router příkazy"}, {"Router commands", "Router příkazy"},
{"Local Destinations", "Místní cíle"}, {"Local Destinations", "Lokální destinace"},
{"LeaseSets", "Sety pronájmu"}, {"LeaseSets", "LeaseSety"},
{"Tunnels", "Tunely"}, {"Tunnels", "Tunely"},
{"Transit Tunnels", "Tranzitní tunely"}, {"Transit Tunnels", "Transitní tunely"},
{"Transports", "Transporty"}, {"Transports", "Transporty"},
{"I2P tunnels", "I2P tunely"}, {"I2P tunnels", "I2P tunely"},
{"SAM sessions", "SAM relace"}, {"SAM sessions", "SAM relace"},
@ -61,21 +61,18 @@ namespace czech // language namespace
{"Clock skew", "Časová nesrovnalost"}, {"Clock skew", "Časová nesrovnalost"},
{"Offline", "Offline"}, {"Offline", "Offline"},
{"Symmetric NAT", "Symetrický NAT"}, {"Symmetric NAT", "Symetrický NAT"},
{"Full cone NAT", "Full cone NAT"},
{"No Descriptors", "Žádné popisovače"},
{"Uptime", "Doba provozu"}, {"Uptime", "Doba provozu"},
{"Network status", "Stav sítě"}, {"Network status", "Status sítě"},
{"Network status v6", "Stav sítě v6"}, {"Network status v6", "Status sítě v6"},
{"Stopping in", "Zastavuji za"}, {"Stopping in", "Zastavuji za"},
{"Family", "Rodina"}, {"Family", "Rodina"},
{"Tunnel creation success rate", "Úspěšnost vytváření tunelů"}, {"Tunnel creation success rate", "Úspěšnost vytváření tunelů"},
{"Total tunnel creation success rate", "Celková míra úspěšnosti vytváření tunelů"},
{"Received", "Přijato"}, {"Received", "Přijato"},
{"%.2f KiB/s", "%.2f KiB/s"}, {"%.2f KiB/s", "%.2f KiB/s"},
{"Sent", "Odesláno"}, {"Sent", "Odesláno"},
{"Transit", "Tranzit"}, {"Transit", "Tranzit"},
{"Data path", "Cesta k datovým souborům"}, {"Data path", "Cesta k data souborům"},
{"Hidden content. Press on text to see.", "Skrytý obsah. Pro zobrazení klikněte sem."}, {"Hidden content. Press on text to see.", "Skrytý kontent. Pro zobrazení, klikni na text."},
{"Router Ident", "Routerová Identita"}, {"Router Ident", "Routerová Identita"},
{"Router Family", "Rodina routerů"}, {"Router Family", "Rodina routerů"},
{"Router Caps", "Omezení Routerů"}, {"Router Caps", "Omezení Routerů"},
@ -96,7 +93,6 @@ namespace czech // language namespace
{"Address", "Adresa"}, {"Address", "Adresa"},
{"Type", "Typ"}, {"Type", "Typ"},
{"EncType", "EncType"}, {"EncType", "EncType"},
{"Expire LeaseSet", "Zrušit platnost setu pronájmu"},
{"Inbound tunnels", "Příchozí tunely"}, {"Inbound tunnels", "Příchozí tunely"},
{"%dms", "%dms"}, {"%dms", "%dms"},
{"Outbound tunnels", "Odchozí tunely"}, {"Outbound tunnels", "Odchozí tunely"},
@ -107,24 +103,21 @@ namespace czech // language namespace
{"Amount", "Množství"}, {"Amount", "Množství"},
{"Incoming Tags", "Příchozí štítky"}, {"Incoming Tags", "Příchozí štítky"},
{"Tags sessions", "Relace štítků"}, {"Tags sessions", "Relace štítků"},
{"Status", "Stav"}, {"Status", "Status"},
{"Local Destination", "Místní cíl"}, {"Local Destination", "Lokální Destinace"},
{"Streams", "Toky"}, {"Streams", "Toky"},
{"Close stream", "Uzavřít tok"}, {"Close stream", "Uzavřít tok"},
{"Such destination is not found", "Takováto destinace nebyla nalezena"},
{"I2CP session not found", "I2CP relace nenalezena"}, {"I2CP session not found", "I2CP relace nenalezena"},
{"I2CP is not enabled", "I2CP není zapnuto"}, {"I2CP is not enabled", "I2CP není zapnuto"},
{"Invalid", "Neplatný"}, {"Invalid", "Neplatný"},
{"Store type", "Druh uložení"}, {"Store type", "Druh uložení"},
{"Expires", "Vyprší"}, {"Expires", "Vyprší"},
{"Non Expired Leases", "Pronájmy, kterým nevypršela platnost"}, {"Non Expired Leases", "Nevypršené Leasy"},
{"Gateway", "Brána"}, {"Gateway", "Brána"},
{"TunnelID", "ID tunelu"}, {"TunnelID", "ID tunelu"},
{"EndDate", "Datum ukončení"}, {"EndDate", "Datum ukončení"},
{"floodfill mode is disabled", "režim floodfill je vypnut"},
{"Queue size", "Velikost fronty"}, {"Queue size", "Velikost fronty"},
{"Run peer test", "Spustit peer test"}, {"Run peer test", "Spustit peer test"},
{"Reload tunnels configuration", "Znovu načíst nastavení tunelů"},
{"Decline transit tunnels", "Odmítnout tranzitní tunely"}, {"Decline transit tunnels", "Odmítnout tranzitní tunely"},
{"Accept transit tunnels", "Přijmout tranzitní tunely"}, {"Accept transit tunnels", "Přijmout tranzitní tunely"},
{"Cancel graceful shutdown", "Zrušit hladké vypnutí"}, {"Cancel graceful shutdown", "Zrušit hladké vypnutí"},
@ -152,17 +145,14 @@ namespace czech // language namespace
{"Destination not found", "Destinace nenalezena"}, {"Destination not found", "Destinace nenalezena"},
{"StreamID can't be null", "StreamID nemůže být null"}, {"StreamID can't be null", "StreamID nemůže být null"},
{"Return to destination page", "Zpět na stránku destinací"}, {"Return to destination page", "Zpět na stránku destinací"},
{"You will be redirected in %d seconds", "Budete přesměrováni za %d sekund"}, {"Back to commands list", "Zpět na list příkazů"},
{"LeaseSet expiration time updated", "Aktualizován čas vypršení platnosti setu pronájmu"},
{"LeaseSet is not found or already expired", "Set pronájmu není k nalezení nebo již vypršela jeho platnost"},
{"Transit tunnels count must not exceed %d", "Počet tranzitních tunelů nesmí překročit %d"},
{"Back to commands list", "Zpět na seznam příkazů"},
{"Register at reg.i2p", "Zaregistrovat na reg.i2p"}, {"Register at reg.i2p", "Zaregistrovat na reg.i2p"},
{"Description", "Popis"}, {"Description", "Popis"},
{"A bit information about service on domain", "Trochu informací o službě na doméně"}, {"A bit information about service on domain", "Trochu informací o službě na doméně"},
{"Submit", "Odeslat"}, {"Submit", "Odeslat"},
{"Domain can't end with .b32.i2p", "Doména nesmí končit na .b32.i2p"}, {"Domain can't end with .b32.i2p", "Doména nesmí končit na .b32.i2p"},
{"Domain must end with .i2p", "Doména musí končit s .i2p"}, {"Domain must end with .i2p", "Doména musí končit s .i2p"},
{"Such destination is not found", "Takováto destinace nebyla nalezena"},
{"Unknown command", "Neznámý příkaz"}, {"Unknown command", "Neznámý příkaz"},
{"Command accepted", "Příkaz přijat"}, {"Command accepted", "Příkaz přijat"},
{"Proxy error", "Chyba proxy serveru"}, {"Proxy error", "Chyba proxy serveru"},
@ -172,15 +162,6 @@ namespace czech // language namespace
{"You may try to find this host on jump services below", "Můžete se pokusit najít tohoto hostitele na startovacích službách níže"}, {"You may try to find this host on jump services below", "Můžete se pokusit najít tohoto hostitele na startovacích službách níže"},
{"Invalid request", "Neplatný požadavek"}, {"Invalid request", "Neplatný požadavek"},
{"Proxy unable to parse your request", "Proxy server nemohl zpracovat váš požadavek"}, {"Proxy unable to parse your request", "Proxy server nemohl zpracovat váš požadavek"},
{"Addresshelper is not supported", "Addresshelper není podporován"},
{"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>.", "Hostitel %s je <font color=red>již v adresáři routeru</font>. <b>Buďte opatrní: zdroj této URL může být škodlivý!</b> Klikněte zde pro aktualizaci záznamu: <a href=\"%s%s%s&update=true\">Pokračovat</a>."},
{"Addresshelper forced update rejected", "Addresshelperem vynucená aktualizace zamítnuta"},
{"To add host <b>%s</b> in router's addressbook, click here: <a href=\"%s%s%s\">Continue</a>.", "Pro přidání hostitele <b>%s</b> do adresáře routeru, klikněte zde: <a href=\"%s%s%s\">Pokračovat</a>."},
{"Addresshelper request", "Požadavek Addresshelperu"},
{"Host %s added to router's addressbook from helper. Click here to proceed: <a href=\"%s\">Continue</a>.", "Hostitel %s přidán do adresáře routeru od pomocníka. Klikněte zde pro pokračování: <a href=\"%s\">Pokračovat</a>."},
{"Addresshelper adding", "Addresshelper přidávání"},
{"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>.", "Hostitel %s je <font color=red>již v adresáři routeru</font>. Klikněte zde pro aktualizaci záznamu: <a href=\"%s%s%s&update=true\">Pokračovat</a>."},
{"Addresshelper update", "Addresshelper aktualizace"},
{"Invalid request URI", "Neplatný URI požadavek"}, {"Invalid request URI", "Neplatný URI požadavek"},
{"Can't detect destination host from request", "Nelze zjistit cílového hostitele z požadavku"}, {"Can't detect destination host from request", "Nelze zjistit cílového hostitele z požadavku"},
{"Outproxy failure", "Outproxy selhání"}, {"Outproxy failure", "Outproxy selhání"},

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2022-2024, The PurpleI2P Project * Copyright (c) 2022-2023, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@ -58,7 +58,7 @@ namespace french // language namespace
{"Unknown", "Inconnu"}, {"Unknown", "Inconnu"},
{"Proxy", "Proxy"}, {"Proxy", "Proxy"},
{"Mesh", "Maillé"}, {"Mesh", "Maillé"},
{"Clock skew", "Décalage de l'horloge"}, {"Clock skew", "Horloge décalée"},
{"Offline", "Hors ligne"}, {"Offline", "Hors ligne"},
{"Symmetric NAT", "NAT symétrique"}, {"Symmetric NAT", "NAT symétrique"},
{"Full cone NAT", "NAT à cône complet"}, {"Full cone NAT", "NAT à cône complet"},
@ -68,8 +68,8 @@ namespace french // language namespace
{"Network status v6", "État du réseau v6"}, {"Network status v6", "État du réseau v6"},
{"Stopping in", "Arrêt dans"}, {"Stopping in", "Arrêt dans"},
{"Family", "Famille"}, {"Family", "Famille"},
{"Tunnel creation success rate", "Taux de création de tunnel réussie"}, {"Tunnel creation success rate", "Taux de succès de création de tunnels"},
{"Total tunnel creation success rate", "Taux total de création de tunnel réussie"}, {"Total tunnel creation success rate", "Taux de réussite de création de tunnel"},
{"Received", "Reçu"}, {"Received", "Reçu"},
{"%.2f KiB/s", "%.2f Kio/s"}, {"%.2f KiB/s", "%.2f Kio/s"},
{"Sent", "Envoyé"}, {"Sent", "Envoyé"},

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2023-2024, The PurpleI2P Project * Copyright (c) 2023, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@ -31,186 +31,22 @@ namespace polish // language namespace
static std::map<std::string, std::string> strings static std::map<std::string, std::string> strings
{ {
{"%.2f KiB", "%.2f KiB"},
{"%.2f MiB", "%.2f MiB"},
{"%.2f GiB", "%.2f GiB"},
{"building", "Kompilowanie"}, {"building", "Kompilowanie"},
{"failed", "nieudane"}, {"failed", "nieudane"},
{"expiring", "wygasający"}, {"expiring", "wygasający"},
{"established", "ustanowiony"}, {"established", "ustanowiony"},
{"unknown", "nieznany"},
{"exploratory", "eksploracyjny"},
{"Purple I2P Webconsole", "Konsola webowa Purple I2P"},
{"<b>i2pd</b> webconsole", "<b>i2pd</b> konsola webowa"},
{"Main page", "Strona główna"}, {"Main page", "Strona główna"},
{"Router commands", "Komendy routera"}, {"Router commands", "Komendy routera"},
{"Local Destinations", "Lokalne miejsca docelowe"},
{"LeaseSets", "ZestawyNajmu"},
{"Tunnels", "Tunele"}, {"Tunnels", "Tunele"},
{"Transit Tunnels", "Tunele Tranzytu"},
{"Transports", "Transportery"},
{"I2P tunnels", "Tunele I2P"},
{"SAM sessions", "Sesje SAM"},
{"ERROR", "BŁĄD"},
{"OK", "Ok"}, {"OK", "Ok"},
{"Testing", "Testowanie"},
{"Firewalled", "Za zaporą sieciową"},
{"Unknown", "Nieznany"},
{"Proxy", "Proxy"},
{"Mesh", "Sieć"},
{"Clock skew", "Przesunięcie czasu"},
{"Offline", "Offline"},
{"Symmetric NAT", "Symetryczny NAT"},
{"Full cone NAT", "Pełny stożek NAT"},
{"No Descriptors", "Brak deskryptorów"},
{"Uptime", "Czas pracy"}, {"Uptime", "Czas pracy"},
{"Network status", "Stan sieci"},
{"Network status v6", "Stan sieci v6"},
{"Stopping in", "Zatrzymywanie za"},
{"Family", "Rodzina"},
{"Tunnel creation success rate", "Wskaźnik sukcesu tworzenia tunelu"},
{"Total tunnel creation success rate", "Całkowity wskaźnik sukcesu tworzenia tunelu"},
{"Received", "Odebrano"},
{"%.2f KiB/s", "%.2f KiB/s"},
{"Sent", "Wysłane"}, {"Sent", "Wysłane"},
{"Transit", "Tranzyt"},
{"Data path", "Ścieżka do danych"},
{"Hidden content. Press on text to see.", "Ukryta zawartość. Naciśnij tekst, aby zobaczyć."},
{"Router Ident", "Identyfikator routera"},
{"Router Family", "Rodzina routera"},
{"Router Caps", "Możliwości routera"},
{"Version", "Wersja"},
{"Our external address", "Nasz zewnętrzny adres"},
{"supported", "wspierane"},
{"Routers", "Routery"},
{"Floodfills", "Floodfille"},
{"Client Tunnels", "Tunele Klienta"},
{"Services", "Usługi"},
{"Enabled", "Aktywny"},
{"Disabled", "Wyłączony"},
{"Encrypted B33 address", "Zaszyfrowany adres B33"},
{"Address registration line", "Linia rejestracji adresu"},
{"Domain", "Domena"},
{"Generate", "Generuj"},
{"<b>Note:</b> result string can be used only for registering 2LD domains (example.i2p). For registering subdomains please use i2pd-tools.", "<b>Uwaga:</b> wynik string może być używany tylko do rejestracji domen 2LD (przykład.i2p). Do rejestracji subdomen należy użyć narzędzi i2pd."},
{"Address", "Adres"},
{"Type", "Typ"},
{"EncType", "TypEnkrypcji"},
{"Expire LeaseSet", "Wygaśnij LeaseSet"},
{"Inbound tunnels", "Tunele przychodzące"},
{"%dms", "%dms"},
{"Outbound tunnels", "Tunele wychodzące"},
{"Tags", "Tagi"},
{"Incoming", "Przychodzące"},
{"Outgoing", "Wychodzące"},
{"Destination", "Miejsce docelowe"},
{"Amount", "Ilość"},
{"Incoming Tags", "Przychodzące tagi"},
{"Tags sessions", "Sesje tagów"},
{"Status", "Status"},
{"Local Destination", "Lokalne miejsce docelowe"},
{"Streams", "Strumienie"},
{"Close stream", "Zamknij strumień"},
{"Such destination is not found", "Nie znaleziono takiego miejsca docelowego"},
{"I2CP session not found", "Sesja I2CP nie została znaleziona"},
{"I2CP is not enabled", "I2CP nie jest włączone"},
{"Invalid", "Niepoprawny"},
{"Store type", "Rodzaj przechowywania"},
{"Expires", "Wygasa za"},
{"Non Expired Leases", "Leasingi niewygasłe"},
{"Gateway", "Brama"},
{"TunnelID", "IDTunelu"},
{"EndDate", "DataZakończenia"},
{"floodfill mode is disabled", "tryb floodfill jest wyłączony"},
{"Queue size", "Wielkość kolejki"},
{"Run peer test", "Wykonaj test peer"},
{"Reload tunnels configuration", "Załaduj ponownie konfigurację tuneli"},
{"Decline transit tunnels", "Odrzuć tunele tranzytowe"},
{"Accept transit tunnels", "Akceptuj tunele tranzytowe"},
{"Cancel graceful shutdown", "Anuluj łagodne wyłączenie"},
{"Start graceful shutdown", "Rozpocznij łagodne wyłączenie"},
{"Force shutdown", "Wymuś wyłączenie"},
{"Reload external CSS styles", "Odśwież zewnętrzne style CSS"},
{"<b>Note:</b> any action done here are not persistent and not changes your config files.", "<b>Uwaga:</b> każda akcja wykonana tutaj nie jest trwała i nie zmienia Twoich plików konfiguracyjnych."},
{"Logging level", "Poziom logowania"},
{"Transit tunnels limit", "Limit tuneli tranzytowych"},
{"Change", "Zmień"},
{"Change language", "Zmień język"},
{"no transit tunnels currently built", "brak obecnie zbudowanych tuneli tranzytowych"},
{"SAM disabled", "SAM wyłączony"},
{"no sessions currently running", "brak aktualnie uruchomionych sesji"},
{"SAM session not found", "Sesja SAM nie została znaleziona"},
{"SAM Session", "Sesja SAM"},
{"Server Tunnels", "Tunele Serwera"},
{"Client Forwards", "Przekierowania Klienta"},
{"Server Forwards", "Przekierowania Serwera"},
{"Unknown page", "Nieznana strona"},
{"Invalid token", "Nieprawidłowy token"},
{"SUCCESS", "SUKCES"},
{"Stream closed", "Strumień zamknięty"},
{"Stream not found or already was closed", "Strumień nie został znaleziony lub został już zamknięty"},
{"Destination not found", "Nie znaleziono punktu docelowego"},
{"StreamID can't be null", "StreamID nie może być null"},
{"Return to destination page", "Wróć do strony miejsca docelowego"},
{"You will be redirected in %d seconds", "Zostaniesz prekierowany za %d sekund"},
{"LeaseSet expiration time updated", "Zaktualizowano czas wygaśnięcia LeaseSet"},
{"LeaseSet is not found or already expired", "LeaseSet nie został znaleziony lub już wygasł"},
{"Transit tunnels count must not exceed %d", "Liczba tuneli tranzytowych nie może przekraczać %d"},
{"Back to commands list", "Powrót do listy poleceń"},
{"Register at reg.i2p", "Zarejestruj się na reg.i2p"},
{"Description", "Opis"},
{"A bit information about service on domain", "Trochę informacji o usłudze w domenie"},
{"Submit", "Zatwierdź"},
{"Domain can't end with .b32.i2p", "Domena nie może kończyć się na .b32.i2p"},
{"Domain must end with .i2p", "Domena musi kończyć się na .i2p"},
{"Unknown command", "Nieznana komenda"},
{"Command accepted", "Polecenie zaakceptowane"},
{"Proxy error", "Błąd serwera proxy"},
{"Proxy info", "Informacje o proxy"},
{"Proxy error: Host not found", "Błąd proxy: Nie znaleziono hosta"},
{"Remote host not found in router's addressbook", "Nie znaleziono zdalnego hosta w książce adresowej routera"},
{"You may try to find this host on jump services below", "Możesz znaleźć tego hosta na poniższych usługach skoku"},
{"Invalid request", "Nieprawidłowe żądanie"},
{"Proxy unable to parse your request", "Serwer proxy nie może przetworzyć Twojego żądania"},
{"Addresshelper is not supported", "Adresshelper nie jest obsługiwany"},
{"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>.", "Host %s <font color=red>jest już w książce adresowej routera</font>. <b>Uważaj: źródło tego adresu URL może być szkodliwe!</b> Kliknij tutaj, aby zaktualizować rekord: <a href=\"%s%s%s&update=true\">Kontynuuj</a>."},
{"Addresshelper forced update rejected", "Wymuszona aktualizacja Addreshelper odrzucona"},
{"To add host <b>%s</b> in router's addressbook, click here: <a href=\"%s%s%s\">Continue</a>.", "Aby dodać host <b>%s</b> w książce adresowej routera, kliknij tutaj: <a href=\"%s%s%s\">Kontynuuj</a>."},
{"Addresshelper request", "Prośba Addresshelper"},
{"Host %s added to router's addressbook from helper. Click here to proceed: <a href=\"%s\">Continue</a>.", "Host %s dodany do książki adresowej routera od pomocnika. Kliknij tutaj, aby kontynuować: <a href=\"%s\">Kontynuuj</a>."},
{"Addresshelper adding", "Dodawanie 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>.", "Host %s jest <font color=red>już w książce adresowej routera</font>. Kliknij tutaj, aby zaktualizować rekord: <a href=\"%s%s%s&update=true\">Kontynuuj</a>."},
{"Addresshelper update", "Aktualizacja Adresshelper"},
{"Invalid request URI", "Nieprawidłowe URI żądania"},
{"Can't detect destination host from request", "Nie można wykryć hosta docelowego z żądania"},
{"Outproxy failure", "Błąd proxy wyjściowego"},
{"Bad outproxy settings", "Błędne ustawienia proxy wyjściowych"},
{"Host %s is not inside I2P network, but outproxy is not enabled", "Host %s nie jest wewnątrz sieci I2P, a proxy wyjściowe nie jest włączone"},
{"Unknown outproxy URL", "Nieznany adres URL proxy wyjściowego"},
{"Cannot resolve upstream proxy", "Nie można rozwiązać serwera proxy upstream"},
{"Hostname is too long", "Nazwa hosta jest zbyt długa"},
{"Cannot connect to upstream SOCKS proxy", "Nie można połączyć się z proxy SOCKS upstream"},
{"Cannot negotiate with SOCKS proxy", "Nie można negocjować z proxy SOCKS"},
{"CONNECT error", "Błąd POŁĄCZENIE"},
{"Failed to connect", "Nie udało się połączyć"},
{"SOCKS proxy error", "Błąd proxy SOCKS"},
{"Failed to send request to upstream", "Nie udało się wysłać żądania do upstream"},
{"No reply from SOCKS proxy", "Brak odpowiedzi od serwera proxy SOCKS"},
{"Cannot connect", "Nie można się połączyć"},
{"HTTP out proxy not implemented", "Serwer wyjściowy proxy HTTP nie został zaimplementowany"},
{"Cannot connect to upstream HTTP proxy", "Nie można połączyć się z proxy HTTP upstream"},
{"Host is down", "Host jest niedostępny"},
{"Can't create connection to requested host, it may be down. Please try again later.", "Nie można utworzyć połączenia z żądanym hostem, może być wyłączony. Spróbuj ponownie później."},
{"", ""}, {"", ""},
}; };
static std::map<std::string, std::vector<std::string>> plurals static std::map<std::string, std::vector<std::string>> plurals
{ {
{"%d days", {"%d dzień", "%d dni", "%d dni", "%d dni"}}, {"", {"", "", ""}},
{"%d hours", {"%d godzina", "%d godziny", "%d godzin", "%d godzin"}},
{"%d minutes", {"%d minuta", "%d minuty", "%d minut", "%d minut"}},
{"%d seconds", {"%d sekunda", "%d sekundy", "%d sekund", "%d sekund"}},
{"", {"", "", "", ""}},
}; };
std::shared_ptr<const i2p::i18n::Locale> GetLocale() std::shared_ptr<const i2p::i18n::Locale> GetLocale()

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2023-2024, The PurpleI2P Project * Copyright (c) 2023, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@ -58,7 +58,7 @@ namespace portuguese // language namespace
{"Unknown", "Desconhecido"}, {"Unknown", "Desconhecido"},
{"Proxy", "Proxy"}, {"Proxy", "Proxy"},
{"Mesh", "Malha"}, {"Mesh", "Malha"},
{"Clock skew", "Desvio de Relógio"}, {"Clock skew", "Defasagem do Relógio"},
{"Offline", "Desligado"}, {"Offline", "Desligado"},
{"Symmetric NAT", "NAT Simétrico"}, {"Symmetric NAT", "NAT Simétrico"},
{"Full cone NAT", "Full cone NAT"}, {"Full cone NAT", "Full cone NAT"},
@ -74,7 +74,7 @@ namespace portuguese // language namespace
{"%.2f KiB/s", "%.2f KiB/s"}, {"%.2f KiB/s", "%.2f KiB/s"},
{"Sent", "Enviado"}, {"Sent", "Enviado"},
{"Transit", "Trânsito"}, {"Transit", "Trânsito"},
{"Data path", "Diretório de dados"}, {"Data path", "Diretório dos dados"},
{"Hidden content. Press on text to see.", "Conteúdo oculto. Clique no texto para revelar."}, {"Hidden content. Press on text to see.", "Conteúdo oculto. Clique no texto para revelar."},
{"Router Ident", "Identidade do Roteador"}, {"Router Ident", "Identidade do Roteador"},
{"Router Family", "Família do Roteador"}, {"Router Family", "Família do Roteador"},
@ -106,9 +106,9 @@ namespace portuguese // language namespace
{"Destination", "Destinos"}, {"Destination", "Destinos"},
{"Amount", "Quantidade"}, {"Amount", "Quantidade"},
{"Incoming Tags", "Etiquetas de Entrada"}, {"Incoming Tags", "Etiquetas de Entrada"},
{"Tags sessions", "Sessões de Etiquetas"}, {"Tags sessions", "Sessões de etiquetas"},
{"Status", "Estado"}, {"Status", "Estado"},
{"Local Destination", "Destino Local"}, {"Local Destination", "Destinos Locais"},
{"Streams", "Fluxos"}, {"Streams", "Fluxos"},
{"Close stream", "Fechar fluxo"}, {"Close stream", "Fechar fluxo"},
{"Such destination is not found", "Tal destino não foi encontrado"}, {"Such destination is not found", "Tal destino não foi encontrado"},
@ -148,7 +148,7 @@ namespace portuguese // language namespace
{"Invalid token", "Token Inválido"}, {"Invalid token", "Token Inválido"},
{"SUCCESS", "SUCESSO"}, {"SUCCESS", "SUCESSO"},
{"Stream closed", "Fluxo fechado"}, {"Stream closed", "Fluxo fechado"},
{"Stream not found or already was closed", "Fluxo não encontrado ou já fechado"}, {"Stream not found or already was closed", "Fluxo não encontrado ou já encerrado"},
{"Destination not found", "Destino não encontrado"}, {"Destination not found", "Destino não encontrado"},
{"StreamID can't be null", "StreamID não pode ser nulo"}, {"StreamID can't be null", "StreamID não pode ser nulo"},
{"Return to destination page", "Retornar para à página de destino"}, {"Return to destination page", "Retornar para à página de destino"},
@ -157,7 +157,7 @@ namespace portuguese // language namespace
{"LeaseSet is not found or already expired", "LeaseSet não foi encontrado ou já expirou"}, {"LeaseSet is not found or already expired", "LeaseSet não foi encontrado ou já expirou"},
{"Transit tunnels count must not exceed %d", "A contagem de túneis de trânsito não deve exceder %d"}, {"Transit tunnels count must not exceed %d", "A contagem de túneis de trânsito não deve exceder %d"},
{"Back to commands list", "Voltar para a lista de comandos"}, {"Back to commands list", "Voltar para a lista de comandos"},
{"Register at reg.i2p", "Registrar em reg.i2p"}, {"Register at reg.i2p", "Registrar na reg.i2p"},
{"Description", "Descrição"}, {"Description", "Descrição"},
{"A bit information about service on domain", "Algumas informações sobre o serviço no domínio"}, {"A bit information about service on domain", "Algumas informações sobre o serviço no domínio"},
{"Submit", "Enviar"}, {"Submit", "Enviar"},
@ -169,22 +169,22 @@ namespace portuguese // language namespace
{"Proxy info", "Informações do proxy"}, {"Proxy info", "Informações do proxy"},
{"Proxy error: Host not found", "Erro no proxy: Host não encontrado"}, {"Proxy error: Host not found", "Erro no proxy: Host não encontrado"},
{"Remote host not found in router's addressbook", "O host remoto não foi encontrado no livro de endereços do roteador"}, {"Remote host not found in router's addressbook", "O host remoto não foi encontrado no livro de endereços do roteador"},
{"You may try to find this host on jump services below", "Você pode tentar encontrar este host nos serviços de jump abaixo"}, {"You may try to find this host on jump services below", "Você pode tentar encontrar este host nos jump services abaixo"},
{"Invalid request", "Requisição inválida"}, {"Invalid request", "Requisição inválida"},
{"Proxy unable to parse your request", "O proxy foi incapaz de processar a sua requisição"}, {"Proxy unable to parse your request", "O proxy foi incapaz de processar a sua requisição"},
{"Addresshelper is not supported", "O Auxiliar de Endereços não é suportado"}, {"Addresshelper is not supported", "O Auxiliar de Endereços não é suportado"},
{"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>.", "O host %s já <font color=red>está no catálogo de endereços do roteador</font>. <b>Cuidado: a fonte desta URL pode ser perigosa!</b> Clique aqui para atualizar o registro: <a href=\"%s%s%s&update=true\">Continuar</a>."}, {"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>.", "O host %s já <font color=red>está no catálogo de endereços do roteador</font>. <b>Cuidado: a fonte desta URL pode ser perigosa!</b> Clique aqui para atualizar o registro: <a href=\"%s%s%s&update=true\">Continuar</a>."},
{"Addresshelper forced update rejected", "A atualização forçada do Auxiliar de Endereços foi rejeitada"}, {"Addresshelper forced update rejected", "A atualização forçada do Auxiliar de Endereços foi rejeitada"},
{"To add host <b>%s</b> in router's addressbook, click here: <a href=\"%s%s%s\">Continue</a>.", "Para adicionar o host <b> %s </b> ao catálogo de endereços do roteador, clique aqui: <a href='%s%s%s'>Continuar </a>."}, {"To add host <b>%s</b> in router's addressbook, click here: <a href=\"%s%s%s\">Continue</a>.", "Para adicionar o host <b> %s </b> ao catálogo de endereços do roteador, clique aqui: <a href='%s%s%s'>Continuar </a>."},
{"Addresshelper request", "Requisição ao Auxiliar de Endereços"}, {"Addresshelper request", "Requisição do Auxiliar de Endereços"},
{"Host %s added to router's addressbook from helper. Click here to proceed: <a href=\"%s\">Continue</a>.", "O host %s foi adicionado ao catálogo de endereços do roteador por um auxiliar. Clique aqui para prosseguir: <a href='%s'> Continuar </a>."}, {"Host %s added to router's addressbook from helper. Click here to proceed: <a href=\"%s\">Continue</a>.", "O host %s foi adicionado ao catálogo de endereços do roteador por um auxiliar. Clique aqui para proceder: <a href='%s'> Continuar </a>."},
{"Addresshelper adding", "Auxiliar de Endereço adicionando"}, {"Addresshelper adding", "Auxiliar de Endereço adicionando"},
{"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>.", "O host %s já <font color=red>está no catálogo de endereços do roteador </font>. Clique aqui para atualizar o registro: <a href=\"%s%s%s&update=true\">Continuar</a>."}, {"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>.", "O host %s já <font color=red>está no catálogo de endereços do roteador </font>. Clique aqui para atualizar o registro: <a href=\"%s%s%s&update=true\">Continuar</a>."},
{"Addresshelper update", "Atualização do Auxiliar de Endereços"}, {"Addresshelper update", "Atualização do Auxiliar de Endereços"},
{"Invalid request URI", "A URI de requisição é inválida"}, {"Invalid request URI", "A URI de requisição é inválida"},
{"Can't detect destination host from request", "Incapaz de detectar o host de destino da requisição"}, {"Can't detect destination host from request", "Incapaz de detectar o host de destino da requisição"},
{"Outproxy failure", "Falha no outproxy"}, {"Outproxy failure", "Falha no outproxy"},
{"Bad outproxy settings", "Má configurações do outproxy"}, {"Bad outproxy settings", "Configurações ruins de outproxy"},
{"Host %s is not inside I2P network, but outproxy is not enabled", "O host %s não está dentro da rede I2P, mas o outproxy não está ativado"}, {"Host %s is not inside I2P network, but outproxy is not enabled", "O host %s não está dentro da rede I2P, mas o outproxy não está ativado"},
{"Unknown outproxy URL", "URL de outproxy desconhecida"}, {"Unknown outproxy URL", "URL de outproxy desconhecida"},
{"Cannot resolve upstream proxy", "Não é possível resolver o proxy de entrada"}, {"Cannot resolve upstream proxy", "Não é possível resolver o proxy de entrada"},

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2023, The PurpleI2P Project * Copyright (c) 2013-2022, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@ -28,11 +28,6 @@ namespace data
return T32; return T32;
} }
bool IsBase32 (char ch)
{
return (ch >= 'a' && ch <= 'z') || (ch >= '2' && ch <= '7');
}
static void iT64Build(void); static void iT64Build(void);
/* /*
@ -60,11 +55,6 @@ namespace data
return T64; 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) * Reverse Substitution Table (built in run time)
*/ */

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2023, The PurpleI2P Project * Copyright (c) 2013-2020, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@ -19,11 +19,9 @@ namespace data {
size_t Base64ToByteStream (const char * InBuffer, size_t InCount, uint8_t * OutBuffer, size_t len ); size_t Base64ToByteStream (const char * InBuffer, size_t InCount, uint8_t * OutBuffer, size_t len );
const char * GetBase32SubstitutionTable (); const char * GetBase32SubstitutionTable ();
const char * GetBase64SubstitutionTable (); const char * GetBase64SubstitutionTable ();
bool IsBase64 (char ch);
size_t Base32ToByteStream (const char * inBuf, size_t len, uint8_t * outBuf, size_t outLen); 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); 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 * Compute the size for a buffer to contain encoded base64 given that the size of the input is input_size bytes

@ -13,7 +13,7 @@
#define bit_AES (1 << 25) #define bit_AES (1 << 25)
#endif #endif
#if defined(__GNUC__) && __GNUC__ < 6 && IS_X86 #if defined(__GNUC__) && __GNUC__ < 5 && IS_X86
#include <cpuid.h> #include <cpuid.h>
#endif #endif
@ -35,10 +35,10 @@ namespace cpu
__builtin_cpu_init(); __builtin_cpu_init();
# endif # endif
return __builtin_cpu_supports("aes"); return __builtin_cpu_supports("aes");
#elif (defined(__GNUC__) && __GNUC__ >= 6) #elif (defined(__GNUC__) && __GNUC__ >= 5)
__builtin_cpu_init(); __builtin_cpu_init();
return __builtin_cpu_supports("aes"); return __builtin_cpu_supports("aes");
#elif (defined(__GNUC__) && __GNUC__ < 6) #elif (defined(__GNUC__) && __GNUC__ < 5)
int cpu_info[4]; int cpu_info[4];
bool flag = false; bool flag = false;
__cpuid(0, cpu_info[0], cpu_info[1], cpu_info[2], cpu_info[3]); __cpuid(0, cpu_info[0], cpu_info[1], cpu_info[2], cpu_info[3]);

@ -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 * This file is part of Purple i2pd project and licensed under BSD3
* *
@ -77,7 +77,7 @@ namespace config {
limits.add_options() limits.add_options()
("limits.coresize", value<uint32_t>()->default_value(0), "Maximum size of corefile in Kb (0 - use system limit)") ("limits.coresize", value<uint32_t>()->default_value(0), "Maximum size of corefile in Kb (0 - use system limit)")
("limits.openfiles", value<uint16_t>()->default_value(0), "Maximum number of open files (0 - use system default)") ("limits.openfiles", value<uint16_t>()->default_value(0), "Maximum number of open files (0 - use system default)")
("limits.transittunnels", value<uint32_t>()->default_value(10000), "Maximum active transit tunnels (default:10000)") ("limits.transittunnels", value<uint16_t>()->default_value(5000), "Maximum active transit tunnels (default:5000)")
("limits.zombies", value<double>()->default_value(0), "Minimum percentage of successfully created tunnels under which tunnel cleanup is paused (default [%]: 0.00)") ("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") ("limits.ntcpsoft", value<uint16_t>()->default_value(0), "Ignored")
("limits.ntcphard", value<uint16_t>()->default_value(0), "Ignored") ("limits.ntcphard", value<uint16_t>()->default_value(0), "Ignored")
@ -205,7 +205,7 @@ namespace config {
reseed.add_options() reseed.add_options()
("reseed.verify", value<bool>()->default_value(false), "Verify .su3 signature") ("reseed.verify", value<bool>()->default_value(false), "Verify .su3 signature")
("reseed.threshold", value<uint16_t>()->default_value(25), "Minimum number of known routers before requesting reseed") ("reseed.threshold", value<uint16_t>()->default_value(25), "Minimum number of known routers before requesting reseed")
("reseed.floodfill", value<std::string>()->default_value(""), "Ignored. Always empty") ("reseed.floodfill", value<std::string>()->default_value(""), "Path to router info of floodfill to reseed from")
("reseed.file", value<std::string>()->default_value(""), "Path to local .su3 file or HTTPS URL to reseed from") ("reseed.file", value<std::string>()->default_value(""), "Path to local .su3 file or HTTPS URL to reseed from")
("reseed.zipfile", value<std::string>()->default_value(""), "Path to local .zip file to reseed from") ("reseed.zipfile", value<std::string>()->default_value(""), "Path to local .zip file to reseed from")
("reseed.proxy", value<std::string>()->default_value(""), "url for reseed proxy, supports http/socks") ("reseed.proxy", value<std::string>()->default_value(""), "url for reseed proxy, supports http/socks")
@ -221,8 +221,7 @@ namespace config {
"https://reseed-pl.i2pd.xyz/," "https://reseed-pl.i2pd.xyz/,"
"https://www2.mk16.de/," "https://www2.mk16.de/,"
"https://i2p.ghativega.in/," "https://i2p.ghativega.in/,"
"https://i2p.novg.net/," "https://i2p.novg.net/"
"https://reseed.stormycloud.org/"
), "Reseed URLs, separated by comma") ), "Reseed URLs, separated by comma")
("reseed.yggurls", value<std::string>()->default_value( ("reseed.yggurls", value<std::string>()->default_value(
"http://[324:71e:281a:9ed3::ace]:7070/," "http://[324:71e:281a:9ed3::ace]:7070/,"

@ -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 * This file is part of Purple i2pd project and licensed under BSD3
* *
@ -19,7 +19,7 @@ namespace i2p
namespace datagram namespace datagram
{ {
DatagramDestination::DatagramDestination (std::shared_ptr<i2p::client::ClientDestination> owner, bool gzip): DatagramDestination::DatagramDestination (std::shared_ptr<i2p::client::ClientDestination> owner, bool gzip):
m_Owner (owner), m_DefaultReceiver (nullptr), m_DefaultRawReceiver (nullptr), m_Gzip (gzip) m_Owner (owner), m_Receiver (nullptr), m_RawReceiver (nullptr), m_Gzip (gzip)
{ {
if (m_Gzip) if (m_Gzip)
m_Deflator.reset (new i2p::data::GzipDeflator); m_Deflator.reset (new i2p::data::GzipDeflator);
@ -119,79 +119,19 @@ namespace datagram
void DatagramDestination::HandleRawDatagram (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len) void DatagramDestination::HandleRawDatagram (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len)
{ {
auto r = FindRawReceiver(toPort); if (m_RawReceiver)
m_RawReceiver (fromPort, toPort, buf, len);
if (r)
r (fromPort, toPort, buf, len);
else else
LogPrint (eLogWarning, "DatagramDestination: no receiver for raw datagram"); LogPrint (eLogWarning, "DatagramDestination: no receiver for raw datagram");
} }
void DatagramDestination::SetReceiver (const Receiver& receiver, uint16_t port)
{
std::lock_guard<std::mutex> lock(m_ReceiversMutex);
m_ReceiversByPorts[port] = receiver;
if (!m_DefaultReceiver) {
m_DefaultReceiver = receiver;
m_DefaultReceiverPort = port;
}
}
void DatagramDestination::ResetReceiver (uint16_t port)
{
std::lock_guard<std::mutex> lock(m_ReceiversMutex);
m_ReceiversByPorts.erase (port);
if (m_DefaultReceiverPort == port) {
m_DefaultReceiver = nullptr;
m_DefaultReceiverPort = 0;
}
}
void DatagramDestination::SetRawReceiver (const RawReceiver& receiver, uint16_t port)
{
std::lock_guard<std::mutex> lock(m_RawReceiversMutex);
m_RawReceiversByPorts[port] = receiver;
if (!m_DefaultRawReceiver) {
m_DefaultRawReceiver = receiver;
m_DefaultRawReceiverPort = port;
}
}
void DatagramDestination::ResetRawReceiver (uint16_t port)
{
std::lock_guard<std::mutex> lock(m_RawReceiversMutex);
m_RawReceiversByPorts.erase (port);
if (m_DefaultRawReceiverPort == port) {
m_DefaultRawReceiver = nullptr;
m_DefaultRawReceiverPort = 0;
}
}
DatagramDestination::Receiver DatagramDestination::FindReceiver(uint16_t port) DatagramDestination::Receiver DatagramDestination::FindReceiver(uint16_t port)
{ {
std::lock_guard<std::mutex> lock(m_ReceiversMutex); std::lock_guard<std::mutex> lock(m_ReceiversMutex);
Receiver r = nullptr; Receiver r = m_Receiver;
auto itr = m_ReceiversByPorts.find(port); auto itr = m_ReceiversByPorts.find(port);
if (itr != m_ReceiversByPorts.end()) if (itr != m_ReceiversByPorts.end())
r = itr->second; r = itr->second;
else {
r = m_DefaultReceiver;
}
return r;
}
DatagramDestination::RawReceiver DatagramDestination::FindRawReceiver(uint16_t port)
{
std::lock_guard<std::mutex> lock(m_RawReceiversMutex);
RawReceiver r = nullptr;
auto itr = m_RawReceiversByPorts.find(port);
if (itr != m_RawReceiversByPorts.end())
r = itr->second;
else {
r = m_DefaultRawReceiver;
}
return r; return r;
} }

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2024, The PurpleI2P Project * Copyright (c) 2013-2022, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@ -126,12 +126,14 @@ namespace datagram
void HandleDataMessagePayload (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len, bool isRaw = false); void HandleDataMessagePayload (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len, bool isRaw = false);
void SetReceiver (const Receiver& receiver) { m_Receiver = receiver; };
void ResetReceiver () { m_Receiver = nullptr; };
void SetReceiver (const Receiver& receiver, uint16_t port); void SetReceiver (const Receiver& receiver, uint16_t port) { std::lock_guard<std::mutex> lock(m_ReceiversMutex); m_ReceiversByPorts[port] = receiver; };
void ResetReceiver (uint16_t port); void ResetReceiver (uint16_t port) { std::lock_guard<std::mutex> lock(m_ReceiversMutex); m_ReceiversByPorts.erase (port); };
void SetRawReceiver (const RawReceiver& receiver, uint16_t port); void SetRawReceiver (const RawReceiver& receiver) { m_RawReceiver = receiver; };
void ResetRawReceiver (uint16_t port); void ResetRawReceiver () { m_RawReceiver = nullptr; };
std::shared_ptr<DatagramSession::Info> GetInfoForRemote(const i2p::data::IdentHash & remote); std::shared_ptr<DatagramSession::Info> GetInfoForRemote(const i2p::data::IdentHash & remote);
@ -148,26 +150,20 @@ namespace datagram
void HandleDatagram (uint16_t fromPort, uint16_t toPort, uint8_t *const& buf, size_t len); 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 HandleRawDatagram (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len);
/** find a receiver by port, if none by port is found try default receiever, otherwise returns nullptr */
Receiver FindReceiver(uint16_t port); Receiver FindReceiver(uint16_t port);
RawReceiver FindRawReceiver(uint16_t port);
private: private:
std::shared_ptr<i2p::client::ClientDestination> m_Owner; std::shared_ptr<i2p::client::ClientDestination> m_Owner;
Receiver m_Receiver; // default
RawReceiver m_RawReceiver; // default
bool m_Gzip; // gzip compression of data messages
std::mutex m_SessionsMutex; std::mutex m_SessionsMutex;
std::map<i2p::data::IdentHash, DatagramSession_ptr > m_Sessions; std::map<i2p::data::IdentHash, DatagramSession_ptr > m_Sessions;
Receiver m_DefaultReceiver;
RawReceiver m_DefaultRawReceiver;
uint16_t m_DefaultReceiverPort;
uint16_t m_DefaultRawReceiverPort;
std::mutex m_ReceiversMutex; std::mutex m_ReceiversMutex;
std::mutex m_RawReceiversMutex; std::map<uint16_t, Receiver> m_ReceiversByPorts;
std::unordered_map<uint16_t, Receiver> m_ReceiversByPorts;
std::unordered_map<uint16_t, RawReceiver> m_RawReceiversByPorts;
bool m_Gzip; // gzip compression of data messages
i2p::data::GzipInflator m_Inflator; i2p::data::GzipInflator m_Inflator;
std::unique_ptr<i2p::data::GzipDeflator> m_Deflator; std::unique_ptr<i2p::data::GzipDeflator> m_Deflator;
std::vector<uint8_t> m_From, m_Signature; std::vector<uint8_t> m_From, m_Signature;

@ -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 * This file is part of Purple i2pd project and licensed under BSD3
* *
@ -367,12 +367,9 @@ namespace client
HandleDataMessage (payload, len); HandleDataMessage (payload, len);
break; break;
case eI2NPDeliveryStatus: case eI2NPDeliveryStatus:
// we assume tunnel tests non-encrypted
HandleDeliveryStatusMessage (bufbe32toh (payload + DELIVERY_STATUS_MSGID_OFFSET)); HandleDeliveryStatusMessage (bufbe32toh (payload + DELIVERY_STATUS_MSGID_OFFSET));
break; break;
case eI2NPTunnelTest:
if (m_Pool)
m_Pool->ProcessTunnelTest (bufbe32toh (payload + TUNNEL_TEST_MSGID_OFFSET), bufbe64toh (payload + TUNNEL_TEST_TIMESTAMP_OFFSET));
break;
case eI2NPDatabaseStore: case eI2NPDatabaseStore:
HandleDatabaseStoreMessage (payload, len); HandleDatabaseStoreMessage (payload, len);
break; break;
@ -410,7 +407,6 @@ namespace client
} }
i2p::data::IdentHash key (buf + DATABASE_STORE_KEY_OFFSET); i2p::data::IdentHash key (buf + DATABASE_STORE_KEY_OFFSET);
std::shared_ptr<i2p::data::LeaseSet> leaseSet; std::shared_ptr<i2p::data::LeaseSet> leaseSet;
std::shared_ptr<LeaseSetRequest> request;
switch (buf[DATABASE_STORE_TYPE_OFFSET]) switch (buf[DATABASE_STORE_TYPE_OFFSET])
{ {
case i2p::data::NETDB_STORE_TYPE_LEASESET: // 1 case i2p::data::NETDB_STORE_TYPE_LEASESET: // 1
@ -466,59 +462,34 @@ namespace client
case i2p::data::NETDB_STORE_TYPE_ENCRYPTED_LEASESET2: // 5 case i2p::data::NETDB_STORE_TYPE_ENCRYPTED_LEASESET2: // 5
{ {
auto it2 = m_LeaseSetRequests.find (key); auto it2 = m_LeaseSetRequests.find (key);
if (it2 != m_LeaseSetRequests.end ()) if (it2 != m_LeaseSetRequests.end () && it2->second->requestedBlindedKey)
{ {
request = it2->second; auto ls2 = std::make_shared<i2p::data::LeaseSet2> (buf + offset, len - offset,
m_LeaseSetRequests.erase (it2); it2->second->requestedBlindedKey, m_LeaseSetPrivKey ? ((const uint8_t *)*m_LeaseSetPrivKey) : nullptr , GetPreferredCryptoType ());
if (request->requestedBlindedKey) if (ls2->IsValid () && !ls2->IsExpired ())
{ {
auto ls2 = std::make_shared<i2p::data::LeaseSet2> (buf + offset, len - offset, leaseSet = ls2;
request->requestedBlindedKey, m_LeaseSetPrivKey ? ((const uint8_t *)*m_LeaseSetPrivKey) : nullptr , GetPreferredCryptoType ()); std::lock_guard<std::mutex> lock(m_RemoteLeaseSetsMutex);
if (ls2->IsValid () && !ls2->IsExpired ()) m_RemoteLeaseSets[ls2->GetIdentHash ()] = ls2; // ident is not key
{ m_RemoteLeaseSets[key] = ls2; // also store as key for next lookup
leaseSet = ls2;
std::lock_guard<std::mutex> lock(m_RemoteLeaseSetsMutex);
m_RemoteLeaseSets[ls2->GetIdentHash ()] = ls2; // ident is not key
m_RemoteLeaseSets[key] = ls2; // also store as key for next lookup
}
else
LogPrint (eLogError, "Destination: New remote encrypted LeaseSet2 failed");
} }
else else
{ LogPrint (eLogError, "Destination: New remote encrypted LeaseSet2 failed");
// publishing verification doesn't have requestedBlindedKey
auto localLeaseSet = GetLeaseSetMt ();
if (localLeaseSet->GetStoreHash () == key)
{
auto ls = std::make_shared<i2p::data::LeaseSet2> (i2p::data::NETDB_STORE_TYPE_ENCRYPTED_LEASESET2,
localLeaseSet->GetBuffer (), localLeaseSet->GetBufferLen (), false);
leaseSet = ls;
}
else
LogPrint (eLogWarning, "Destination: Encrypted LeaseSet2 received for request without blinded key");
}
} }
else else
LogPrint (eLogWarning, "Destination: Couldn't find request for encrypted LeaseSet2"); LogPrint (eLogInfo, "Destination: Couldn't find request for encrypted LeaseSet2");
break; break;
} }
default: default:
LogPrint (eLogError, "Destination: Unexpected client's DatabaseStore type ", buf[DATABASE_STORE_TYPE_OFFSET], ", dropped"); LogPrint (eLogError, "Destination: Unexpected client's DatabaseStore type ", buf[DATABASE_STORE_TYPE_OFFSET], ", dropped");
} }
if (!request) auto it1 = m_LeaseSetRequests.find (key);
{ if (it1 != m_LeaseSetRequests.end ())
auto it1 = m_LeaseSetRequests.find (key);
if (it1 != m_LeaseSetRequests.end ())
{
request = it1->second;
m_LeaseSetRequests.erase (it1);
}
}
if (request)
{ {
request->requestTimeoutTimer.cancel (); it1->second->requestTimeoutTimer.cancel ();
request->Complete (leaseSet); if (it1->second) it1->second->Complete (leaseSet);
m_LeaseSetRequests.erase (it1);
} }
} }
@ -531,43 +502,38 @@ namespace client
if (it != m_LeaseSetRequests.end ()) if (it != m_LeaseSetRequests.end ())
{ {
auto request = it->second; auto request = it->second;
for (int i = 0; i < num; i++) bool found = false;
if (request->excluded.size () < MAX_NUM_FLOODFILLS_PER_REQUEST)
{ {
i2p::data::IdentHash peerHash (buf + 33 + i*32); for (int i = 0; i < num; i++)
if (!request->excluded.count (peerHash) && !i2p::data::netdb.FindRouter (peerHash))
{ {
LogPrint (eLogInfo, "Destination: Found new floodfill, request it"); i2p::data::IdentHash peerHash (buf + 33 + i*32);
i2p::data::netdb.RequestDestination (peerHash, nullptr, false); // through exploratory if (!request->excluded.count (peerHash) && !i2p::data::netdb.FindRouter (peerHash))
{
LogPrint (eLogInfo, "Destination: Found new floodfill, request it");
i2p::data::netdb.RequestDestination (peerHash, nullptr, false); // through exploratory
}
} }
auto floodfill = i2p::data::netdb.GetClosestFloodfill (key, request->excluded);
if (floodfill)
{
LogPrint (eLogInfo, "Destination: Requesting ", key.ToBase64 (), " at ", floodfill->GetIdentHash ().ToBase64 ());
if (SendLeaseSetRequest (key, floodfill, request))
found = true;
}
}
if (!found)
{
LogPrint (eLogInfo, "Destination: ", key.ToBase64 (), " was not found on ", MAX_NUM_FLOODFILLS_PER_REQUEST, " floodfills");
request->Complete (nullptr);
m_LeaseSetRequests.erase (key);
} }
SendNextLeaseSetRequest (key, request);
} }
else else
LogPrint (eLogWarning, "Destination: Request for ", key.ToBase64 (), " not found"); LogPrint (eLogWarning, "Destination: Request for ", key.ToBase64 (), " not found");
} }
void LeaseSetDestination::SendNextLeaseSetRequest (const i2p::data::IdentHash& key,
std::shared_ptr<LeaseSetRequest> request)
{
bool found = false;
if (request->excluded.size () < MAX_NUM_FLOODFILLS_PER_REQUEST)
{
auto floodfill = i2p::data::netdb.GetClosestFloodfill (key, request->excluded);
if (floodfill)
{
LogPrint (eLogInfo, "Destination: Requesting ", key.ToBase64 (), " at ", floodfill->GetIdentHash ().ToBase64 ());
if (SendLeaseSetRequest (key, floodfill, request))
found = true;
}
}
if (!found)
{
LogPrint (eLogInfo, "Destination: ", key.ToBase64 (), " was not found on ", MAX_NUM_FLOODFILLS_PER_REQUEST, " floodfills");
request->Complete (nullptr);
m_LeaseSetRequests.erase (key);
}
}
void LeaseSetDestination::HandleDeliveryStatusMessage (uint32_t msgID) void LeaseSetDestination::HandleDeliveryStatusMessage (uint32_t msgID)
{ {
if (msgID == m_PublishReplyToken) if (msgID == m_PublishReplyToken)
@ -612,7 +578,12 @@ namespace client
shared_from_this (), std::placeholders::_1)); shared_from_this (), std::placeholders::_1));
return; return;
} }
auto floodfill = i2p::data::netdb.GetClosestFloodfill (leaseSet->GetStoreHash (), m_ExcludedFloodfills); if (!m_Pool->GetInboundTunnels ().size () || !m_Pool->GetOutboundTunnels ().size ())
{
LogPrint (eLogError, "Destination: Can't publish LeaseSet. Destination is not ready");
return;
}
auto floodfill = i2p::data::netdb.GetClosestFloodfill (leaseSet->GetIdentHash (), m_ExcludedFloodfills);
if (!floodfill) if (!floodfill)
{ {
LogPrint (eLogError, "Destination: Can't publish LeaseSet, no more floodfills found"); LogPrint (eLogError, "Destination: Can't publish LeaseSet, no more floodfills found");
@ -623,39 +594,26 @@ namespace client
auto inbound = m_Pool->GetNextInboundTunnel (nullptr, floodfill->GetCompatibleTransports (true)); auto inbound = m_Pool->GetNextInboundTunnel (nullptr, floodfill->GetCompatibleTransports (true));
if (!outbound || !inbound) if (!outbound || !inbound)
{ {
if (!m_Pool->GetInboundTunnels ().empty () && !m_Pool->GetOutboundTunnels ().empty ()) LogPrint (eLogInfo, "Destination: No compatible tunnels with ", floodfill->GetIdentHash ().ToBase64 (), ". Trying another floodfill");
{ m_ExcludedFloodfills.insert (floodfill->GetIdentHash ());
LogPrint (eLogInfo, "Destination: No compatible tunnels with ", floodfill->GetIdentHash ().ToBase64 (), ". Trying another floodfill"); floodfill = i2p::data::netdb.GetClosestFloodfill (leaseSet->GetIdentHash (), m_ExcludedFloodfills);
m_ExcludedFloodfills.insert (floodfill->GetIdentHash ()); if (floodfill)
floodfill = i2p::data::netdb.GetClosestFloodfill (leaseSet->GetStoreHash (), m_ExcludedFloodfills); {
if (floodfill) outbound = m_Pool->GetNextOutboundTunnel (nullptr, floodfill->GetCompatibleTransports (false));
if (outbound)
{ {
outbound = m_Pool->GetNextOutboundTunnel (nullptr, floodfill->GetCompatibleTransports (false)); inbound = m_Pool->GetNextInboundTunnel (nullptr, floodfill->GetCompatibleTransports (true));
if (outbound) if (!inbound)
{ LogPrint (eLogError, "Destination: Can't publish LeaseSet. No inbound tunnels");
inbound = m_Pool->GetNextInboundTunnel (nullptr, floodfill->GetCompatibleTransports (true));
if (!inbound)
LogPrint (eLogError, "Destination: Can't publish LeaseSet. No inbound tunnels");
}
else
LogPrint (eLogError, "Destination: Can't publish LeaseSet. No outbound tunnels");
} }
else else
LogPrint (eLogError, "Destination: Can't publish LeaseSet, no more floodfills found"); LogPrint (eLogError, "Destination: Can't publish LeaseSet. No outbound tunnels");
} }
else else
LogPrint (eLogDebug, "Destination: No tunnels in pool"); LogPrint (eLogError, "Destination: Can't publish LeaseSet, no more floodfills found");
if (!floodfill || !outbound || !inbound) if (!floodfill || !outbound || !inbound)
{ {
// we can't publish now
m_ExcludedFloodfills.clear (); 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, " 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; return;
} }
} }
@ -663,15 +621,6 @@ namespace client
LogPrint (eLogDebug, "Destination: Publish LeaseSet of ", GetIdentHash ().ToBase32 ()); LogPrint (eLogDebug, "Destination: Publish LeaseSet of ", GetIdentHash ().ToBase32 ());
RAND_bytes ((uint8_t *)&m_PublishReplyToken, 4); RAND_bytes ((uint8_t *)&m_PublishReplyToken, 4);
auto msg = WrapMessageForRouter (floodfill, i2p::CreateDatabaseStoreMsg (leaseSet, m_PublishReplyToken, inbound)); auto msg = WrapMessageForRouter (floodfill, i2p::CreateDatabaseStoreMsg (leaseSet, m_PublishReplyToken, inbound));
auto s = shared_from_this ();
msg->onDrop = [s]()
{
s->GetService ().post([s]()
{
s->m_PublishConfirmationTimer.cancel ();
s->HandlePublishConfirmationTimer (boost::system::error_code());
});
};
m_PublishConfirmationTimer.expires_from_now (boost::posix_time::seconds(PUBLISH_CONFIRMATION_TIMEOUT)); m_PublishConfirmationTimer.expires_from_now (boost::posix_time::seconds(PUBLISH_CONFIRMATION_TIMEOUT));
m_PublishConfirmationTimer.async_wait (std::bind (&LeaseSetDestination::HandlePublishConfirmationTimer, m_PublishConfirmationTimer.async_wait (std::bind (&LeaseSetDestination::HandlePublishConfirmationTimer,
shared_from_this (), std::placeholders::_1)); shared_from_this (), std::placeholders::_1));
@ -688,7 +637,7 @@ namespace client
m_PublishReplyToken = 0; m_PublishReplyToken = 0;
if (GetIdentity ()->GetCryptoKeyType () == i2p::data::CRYPTO_KEY_TYPE_ELGAMAL) 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"); LogPrint (eLogWarning, "Destination: Publish confirmation was not received in ", PUBLISH_CONFIRMATION_TIMEOUT, " seconds, will try again");
Publish (); Publish ();
} }
else else
@ -801,7 +750,7 @@ namespace client
void LeaseSetDestination::RequestLeaseSet (const i2p::data::IdentHash& dest, RequestComplete requestComplete, std::shared_ptr<const i2p::data::BlindedPublicKey> requestedBlindedKey) void LeaseSetDestination::RequestLeaseSet (const i2p::data::IdentHash& dest, RequestComplete requestComplete, std::shared_ptr<const i2p::data::BlindedPublicKey> requestedBlindedKey)
{ {
std::unordered_set<i2p::data::IdentHash> excluded; std::set<i2p::data::IdentHash> excluded;
auto floodfill = i2p::data::netdb.GetClosestFloodfill (dest, excluded); auto floodfill = i2p::data::netdb.GetClosestFloodfill (dest, excluded);
if (floodfill) if (floodfill)
{ {
@ -873,17 +822,8 @@ namespace client
AddECIESx25519Key (replyKey, replyTag); AddECIESx25519Key (replyKey, replyTag);
else else
AddSessionKey (replyKey, replyTag); AddSessionKey (replyKey, replyTag);
auto msg = WrapMessageForRouter (nextFloodfill, CreateLeaseSetDatabaseLookupMsg (dest,
auto msg = WrapMessageForRouter (nextFloodfill, request->excluded, request->replyTunnel, replyKey, replyTag, isECIES));
CreateLeaseSetDatabaseLookupMsg (dest, request->excluded, request->replyTunnel, replyKey, replyTag, isECIES));
auto s = shared_from_this ();
msg->onDrop = [s, dest, request]()
{
s->GetService ().post([s, dest, request]()
{
s->SendNextLeaseSetRequest (dest, request);
});
};
request->outboundTunnel->SendTunnelDataMsgs ( request->outboundTunnel->SendTunnelDataMsgs (
{ {
i2p::tunnel::TunnelMessageBlock i2p::tunnel::TunnelMessageBlock

@ -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 * This file is part of Purple i2pd project and licensed under BSD3
* *
@ -15,7 +15,7 @@
#include <memory> #include <memory>
#include <map> #include <map>
#include <unordered_map> #include <unordered_map>
#include <unordered_set> #include <set>
#include <string> #include <string>
#include <functional> #include <functional>
#include <boost/asio.hpp> #include <boost/asio.hpp>
@ -97,7 +97,7 @@ namespace client
struct LeaseSetRequest struct LeaseSetRequest
{ {
LeaseSetRequest (boost::asio::io_service& service): requestTime (0), requestTimeoutTimer (service) {}; LeaseSetRequest (boost::asio::io_service& service): requestTime (0), requestTimeoutTimer (service) {};
std::unordered_set<i2p::data::IdentHash> excluded; std::set<i2p::data::IdentHash> excluded;
uint64_t requestTime; uint64_t requestTime;
boost::asio::deadline_timer requestTimeoutTimer; boost::asio::deadline_timer requestTimeoutTimer;
std::list<RequestComplete> requestComplete; std::list<RequestComplete> requestComplete;
@ -176,7 +176,6 @@ namespace client
void RequestLeaseSet (const i2p::data::IdentHash& dest, RequestComplete requestComplete, std::shared_ptr<const i2p::data::BlindedPublicKey> requestedBlindedKey = nullptr); void RequestLeaseSet (const i2p::data::IdentHash& dest, RequestComplete requestComplete, std::shared_ptr<const i2p::data::BlindedPublicKey> requestedBlindedKey = nullptr);
bool SendLeaseSetRequest (const i2p::data::IdentHash& dest, std::shared_ptr<const i2p::data::RouterInfo> nextFloodfill, std::shared_ptr<LeaseSetRequest> request); bool SendLeaseSetRequest (const i2p::data::IdentHash& dest, std::shared_ptr<const i2p::data::RouterInfo> nextFloodfill, std::shared_ptr<LeaseSetRequest> request);
void SendNextLeaseSetRequest (const i2p::data::IdentHash& key, std::shared_ptr<LeaseSetRequest> request);
void HandleRequestTimoutTimer (const boost::system::error_code& ecode, const i2p::data::IdentHash& dest); void HandleRequestTimoutTimer (const boost::system::error_code& ecode, const i2p::data::IdentHash& dest);
void HandleCleanupTimer (const boost::system::error_code& ecode); void HandleCleanupTimer (const boost::system::error_code& ecode);
void CleanupRemoteLeaseSets (); void CleanupRemoteLeaseSets ();
@ -195,7 +194,7 @@ namespace client
bool m_IsPublic; bool m_IsPublic;
uint32_t m_PublishReplyToken; uint32_t m_PublishReplyToken;
uint64_t m_LastSubmissionTime; // in seconds uint64_t m_LastSubmissionTime; // in seconds
std::unordered_set<i2p::data::IdentHash> m_ExcludedFloodfills; // for publishing std::set<i2p::data::IdentHash> m_ExcludedFloodfills; // for publishing
boost::asio::deadline_timer m_PublishConfirmationTimer, m_PublishVerificationTimer, boost::asio::deadline_timer m_PublishConfirmationTimer, m_PublishVerificationTimer,
m_PublishDelayTimer, m_CleanupTimer; m_PublishDelayTimer, m_CleanupTimer;

@ -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 * This file is part of Purple i2pd project and licensed under BSD3
* *
@ -117,12 +117,6 @@ namespace garlic
return session->HandleNextMessage (buf, len, shared_from_this (), index); return session->HandleNextMessage (buf, len, shared_from_this (), index);
} }
bool ReceiveRatchetTagSet::IsSessionTerminated () const
{
return !m_Session || m_Session->IsTerminated ();
}
SymmetricKeyTagSet::SymmetricKeyTagSet (GarlicDestination * destination, const uint8_t * key): SymmetricKeyTagSet::SymmetricKeyTagSet (GarlicDestination * destination, const uint8_t * key):
ReceiveRatchetTagSet (nullptr), m_Destination (destination) ReceiveRatchetTagSet (nullptr), m_Destination (destination)
{ {
@ -863,7 +857,7 @@ namespace garlic
payloadLen += msg->GetPayloadLength () + 13; payloadLen += msg->GetPayloadLength () + 13;
if (m_Destination) payloadLen += 32; if (m_Destination) payloadLen += 32;
} }
if (GetLeaseSetUpdateStatus () == eLeaseSetSubmitted && ts > GetLeaseSetSubmissionTime () + LEASESET_CONFIRMATION_TIMEOUT) if (GetLeaseSetUpdateStatus () == eLeaseSetSubmitted && ts > GetLeaseSetSubmissionTime () + LEASET_CONFIRMATION_TIMEOUT)
{ {
// resubmit non-confirmed LeaseSet // resubmit non-confirmed LeaseSet
SetLeaseSetUpdateStatus (eLeaseSetUpdated); SetLeaseSetUpdateStatus (eLeaseSetUpdated);
@ -1154,7 +1148,7 @@ namespace garlic
return len; return len;
} }
std::shared_ptr<I2NPMessage> WrapECIESX25519Message (std::shared_ptr<I2NPMessage> msg, const uint8_t * key, uint64_t tag) std::shared_ptr<I2NPMessage> WrapECIESX25519Message (std::shared_ptr<const I2NPMessage> msg, const uint8_t * key, uint64_t tag)
{ {
auto m = NewI2NPMessage ((msg ? msg->GetPayloadLength () : 0) + 128); auto m = NewI2NPMessage ((msg ? msg->GetPayloadLength () : 0) + 128);
m->Align (12); // in order to get buf aligned to 16 (12 + 4) m->Align (12); // in order to get buf aligned to 16 (12 + 4)
@ -1174,16 +1168,10 @@ namespace garlic
htobe32buf (m->GetPayload (), offset); htobe32buf (m->GetPayload (), offset);
m->len += offset + 4; m->len += offset + 4;
m->FillI2NPMessageHeader (eI2NPGarlic); m->FillI2NPMessageHeader (eI2NPGarlic);
if (msg->onDrop)
{
// move onDrop to the wrapping I2NP messages
m->onDrop = msg->onDrop;
msg->onDrop = nullptr;
}
return m; return m;
} }
std::shared_ptr<I2NPMessage> WrapECIESX25519MessageForRouter (std::shared_ptr<I2NPMessage> msg, const uint8_t * routerPublicKey) std::shared_ptr<I2NPMessage> WrapECIESX25519MessageForRouter (std::shared_ptr<const I2NPMessage> msg, const uint8_t * routerPublicKey)
{ {
// Noise_N, we are Alice, routerPublicKey is Bob's // Noise_N, we are Alice, routerPublicKey is Bob's
i2p::crypto::NoiseSymmetricState noiseState; i2p::crypto::NoiseSymmetricState noiseState;
@ -1217,12 +1205,6 @@ namespace garlic
htobe32buf (m->GetPayload (), offset); htobe32buf (m->GetPayload (), offset);
m->len += offset + 4; m->len += offset + 4;
m->FillI2NPMessageHeader (eI2NPGarlic); m->FillI2NPMessageHeader (eI2NPGarlic);
if (msg->onDrop)
{
// move onDrop to the wrapping I2NP messages
m->onDrop = msg->onDrop;
msg->onDrop = nullptr;
}
return m; return m;
} }
} }

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2024, The PurpleI2P Project * Copyright (c) 2013-2021, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@ -86,8 +86,7 @@ namespace garlic
virtual bool IsIndexExpired (int index) const; virtual bool IsIndexExpired (int index) const;
virtual bool HandleNextMessage (uint8_t * buf, size_t len, int index); virtual bool HandleNextMessage (uint8_t * buf, size_t len, int index);
virtual bool IsSessionTerminated () const;
private: private:
int m_TrimBehindIndex = 0; int m_TrimBehindIndex = 0;
@ -102,10 +101,9 @@ namespace garlic
SymmetricKeyTagSet (GarlicDestination * destination, const uint8_t * key); SymmetricKeyTagSet (GarlicDestination * destination, const uint8_t * key);
bool IsIndexExpired (int index) const override { return false; }; bool IsIndexExpired (int index) const { return false; };
bool HandleNextMessage (uint8_t * buf, size_t len, int index) override; bool HandleNextMessage (uint8_t * buf, size_t len, int index);
bool IsSessionTerminated () const override { return false; }
private: private:
GarlicDestination * m_Destination; GarlicDestination * m_Destination;
@ -247,8 +245,8 @@ namespace garlic
i2p::crypto::NoiseSymmetricState m_CurrentNoiseState; i2p::crypto::NoiseSymmetricState m_CurrentNoiseState;
}; };
std::shared_ptr<I2NPMessage> WrapECIESX25519Message (std::shared_ptr<I2NPMessage> msg, const uint8_t * key, uint64_t tag); std::shared_ptr<I2NPMessage> WrapECIESX25519Message (std::shared_ptr<const I2NPMessage> msg, const uint8_t * key, uint64_t tag);
std::shared_ptr<I2NPMessage> WrapECIESX25519MessageForRouter (std::shared_ptr<I2NPMessage> msg, const uint8_t * routerPublicKey); std::shared_ptr<I2NPMessage> WrapECIESX25519MessageForRouter (std::shared_ptr<const I2NPMessage> msg, const uint8_t * routerPublicKey);
} }
} }

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2024, The PurpleI2P Project * Copyright (c) 2013-2022, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@ -9,11 +9,6 @@
#include <algorithm> #include <algorithm>
#include <boost/filesystem.hpp> #include <boost/filesystem.hpp>
#if defined(MAC_OSX)
#include <boost/system/system_error.hpp>
#include <TargetConditionals.h>
#endif
#ifdef _WIN32 #ifdef _WIN32
#include <shlobj.h> #include <shlobj.h>
#include <windows.h> #include <windows.h>
@ -54,11 +49,7 @@ namespace fs {
const std::string GetUTF8DataDir () { const std::string GetUTF8DataDir () {
#ifdef _WIN32 #ifdef _WIN32
#if (BOOST_VERSION >= 108500)
boost::filesystem::path path (dataDir);
#else
boost::filesystem::wpath path (dataDir); 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 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(); auto dataDirUTF8 = path.string();
boost::filesystem::path::imbue(loc); // Return locale settings back boost::filesystem::path::imbue(loc); // Return locale settings back
@ -91,11 +82,7 @@ namespace fs {
} }
else else
{ {
#if (BOOST_VERSION >= 108500)
dataDir = boost::filesystem::path(commonAppData).string() + "\\" + appName;
#else
dataDir = boost::filesystem::wpath(commonAppData).string() + "\\" + appName; dataDir = boost::filesystem::wpath(commonAppData).string() + "\\" + appName;
#endif
} }
#else #else
dataDir = "/var/lib/" + appName; dataDir = "/var/lib/" + appName;
@ -120,11 +107,7 @@ namespace fs {
} }
else else
{ {
#if (BOOST_VERSION >= 108500)
auto execPath = boost::filesystem::path(localAppData).parent_path();
#else
auto execPath = boost::filesystem::wpath(localAppData).parent_path(); auto execPath = boost::filesystem::wpath(localAppData).parent_path();
#endif
// if config file exists in .exe's folder use it // if config file exists in .exe's folder use it
if(boost::filesystem::exists(execPath/"i2pd.conf")) // TODO: magic string if(boost::filesystem::exists(execPath/"i2pd.conf")) // TODO: magic string
@ -143,11 +126,7 @@ namespace fs {
} }
else else
{ {
#if (BOOST_VERSION >= 108500)
dataDir = boost::filesystem::path(localAppData).string() + "\\" + appName;
#else
dataDir = boost::filesystem::wpath(localAppData).string() + "\\" + appName; dataDir = boost::filesystem::wpath(localAppData).string() + "\\" + appName;
#endif
} }
} }
} }
@ -157,14 +136,6 @@ namespace fs {
dataDir = (home != NULL && strlen(home) > 0) ? home : ""; dataDir = (home != NULL && strlen(home) > 0) ? home : "";
dataDir += "/Library/Application Support/" + appName; dataDir += "/Library/Application Support/" + appName;
return; return;
#elif defined(__HAIKU__)
char *home = getenv("HOME");
if (home != NULL && strlen(home) > 0) {
dataDir = std::string(home) + "/config/settings/" + appName;
} else {
dataDir = "/tmp/" + appName;
}
return;
#else /* other unix */ #else /* other unix */
#if defined(ANDROID) #if defined(ANDROID)
const char * ext = getenv("EXTERNAL_STORAGE"); const char * ext = getenv("EXTERNAL_STORAGE");
@ -272,22 +243,8 @@ namespace fs {
auto p = root + i2p::fs::dirSep + prefix1 + chars[i]; auto p = root + i2p::fs::dirSep + prefix1 + chars[i];
if (boost::filesystem::exists(p)) if (boost::filesystem::exists(p))
continue; continue;
#if TARGET_OS_SIMULATOR
// ios simulator fs says it is case sensitive, but it is not
boost::system::error_code ec;
if (boost::filesystem::create_directory(p, ec))
continue;
switch (ec.value()) {
case boost::system::errc::file_exists:
case boost::system::errc::success:
continue;
default:
throw boost::system::system_error( ec, __func__ );
}
#else
if (boost::filesystem::create_directory(p)) if (boost::filesystem::create_directory(p))
continue; /* ^ throws exception on failure */ continue; /* ^ throws exception on failure */
#endif
return false; return false;
} }
return true; return true;

@ -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 * This file is part of Purple i2pd project and licensed under BSD3
* *
@ -80,7 +80,7 @@ namespace garlic
void GarlicRoutingSession::CleanupUnconfirmedLeaseSet (uint64_t ts) void GarlicRoutingSession::CleanupUnconfirmedLeaseSet (uint64_t ts)
{ {
if (m_LeaseSetUpdateMsgID && ts*1000LL > m_LeaseSetSubmissionTime + LEASESET_CONFIRMATION_TIMEOUT) if (m_LeaseSetUpdateMsgID && ts*1000LL > m_LeaseSetSubmissionTime + LEASET_CONFIRMATION_TIMEOUT)
{ {
if (GetOwner ()) if (GetOwner ())
GetOwner ()->RemoveDeliveryStatusSession (m_LeaseSetUpdateMsgID); GetOwner ()->RemoveDeliveryStatusSession (m_LeaseSetUpdateMsgID);
@ -232,7 +232,7 @@ namespace garlic
if (GetOwner ()) if (GetOwner ())
{ {
// resubmit non-confirmed LeaseSet // resubmit non-confirmed LeaseSet
if (GetLeaseSetUpdateStatus () == eLeaseSetSubmitted && ts > GetLeaseSetSubmissionTime () + LEASESET_CONFIRMATION_TIMEOUT) if (GetLeaseSetUpdateStatus () == eLeaseSetSubmitted && ts > GetLeaseSetSubmissionTime () + LEASET_CONFIRMATION_TIMEOUT)
{ {
SetLeaseSetUpdateStatus (eLeaseSetUpdated); SetLeaseSetUpdateStatus (eLeaseSetUpdated);
SetSharedRoutingPath (nullptr); // invalidate path since leaseset was not confirmed SetSharedRoutingPath (nullptr); // invalidate path since leaseset was not confirmed
@ -887,7 +887,8 @@ namespace garlic
} }
else else
{ {
if (it->second.tagset->IsSessionTerminated ()) auto session = it->second.tagset->GetSession ();
if (!session || session->IsTerminated())
{ {
it = m_ECIESx25519Tags.erase (it); it = m_ECIESx25519Tags.erase (it);
numExpiredTags++; numExpiredTags++;

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2024, The PurpleI2P Project * Copyright (c) 2013-2022, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@ -50,7 +50,7 @@ namespace garlic
const int INCOMING_TAGS_EXPIRATION_TIMEOUT = 960; // 16 minutes const int INCOMING_TAGS_EXPIRATION_TIMEOUT = 960; // 16 minutes
const int OUTGOING_TAGS_EXPIRATION_TIMEOUT = 720; // 12 minutes const int OUTGOING_TAGS_EXPIRATION_TIMEOUT = 720; // 12 minutes
const int OUTGOING_TAGS_CONFIRMATION_TIMEOUT = 10; // 10 seconds const int OUTGOING_TAGS_CONFIRMATION_TIMEOUT = 10; // 10 seconds
const int LEASESET_CONFIRMATION_TIMEOUT = 4000; // in milliseconds const int LEASET_CONFIRMATION_TIMEOUT = 4000; // in milliseconds
const int ROUTING_PATH_EXPIRATION_TIMEOUT = 30; // 30 seconds const int ROUTING_PATH_EXPIRATION_TIMEOUT = 30; // 30 seconds
const int ROUTING_PATH_MAX_NUM_TIMES_USED = 100; // how many times might be used const int ROUTING_PATH_MAX_NUM_TIMES_USED = 100; // how many times might be used
@ -221,7 +221,7 @@ namespace garlic
struct ECIESX25519AEADRatchetIndexTagset struct ECIESX25519AEADRatchetIndexTagset
{ {
int index; int index;
ReceiveRatchetTagSetPtr tagset; // null if used ReceiveRatchetTagSetPtr tagset;
}; };
class GarlicDestination: public i2p::data::LocalDestination class GarlicDestination: public i2p::data::LocalDestination

@ -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 * This file is part of Purple i2pd project and licensed under BSD3
* *
@ -72,15 +72,11 @@ namespace i2p
SetExpiration (i2p::util::GetMillisecondsSinceEpoch () + I2NP_MESSAGE_EXPIRATION_TIMEOUT); SetExpiration (i2p::util::GetMillisecondsSinceEpoch () + I2NP_MESSAGE_EXPIRATION_TIMEOUT);
} }
bool I2NPMessage::IsExpired (uint64_t ts) const bool I2NPMessage::IsExpired () const
{ {
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
auto exp = GetExpiration (); auto exp = GetExpiration ();
return (ts > exp + I2NP_MESSAGE_CLOCK_SKEW) || (ts < exp - 3*I2NP_MESSAGE_CLOCK_SKEW); // check if expired or too far in future return (ts > exp + I2NP_MESSAGE_CLOCK_SKEW) || (ts < exp - 3*I2NP_MESSAGE_CLOCK_SKEW); // check if expired or too far in future
}
bool I2NPMessage::IsExpired () const
{
return IsExpired (i2p::util::GetMillisecondsSinceEpoch ());
} }
std::shared_ptr<I2NPMessage> CreateI2NPMessage (I2NPMessageType msgType, const uint8_t * buf, size_t len, uint32_t replyMsgID) std::shared_ptr<I2NPMessage> CreateI2NPMessage (I2NPMessageType msgType, const uint8_t * buf, size_t len, uint32_t replyMsgID)
@ -115,17 +111,6 @@ namespace i2p
return newMsg; return newMsg;
} }
std::shared_ptr<I2NPMessage> CreateTunnelTestMsg (uint32_t msgID)
{
auto m = NewI2NPShortMessage ();
uint8_t * buf = m->GetPayload ();
htobe32buf (buf + TUNNEL_TEST_MSGID_OFFSET, msgID);
htobe64buf (buf + TUNNEL_TEST_TIMESTAMP_OFFSET, i2p::util::GetMonotonicMicroseconds ());
m->len += TUNNEL_TEST_SIZE;
m->FillI2NPMessageHeader (eI2NPTunnelTest);
return m;
}
std::shared_ptr<I2NPMessage> CreateDeliveryStatusMsg (uint32_t msgID) std::shared_ptr<I2NPMessage> CreateDeliveryStatusMsg (uint32_t msgID)
{ {
auto m = NewI2NPShortMessage (); auto m = NewI2NPShortMessage ();
@ -147,7 +132,7 @@ namespace i2p
} }
std::shared_ptr<I2NPMessage> CreateRouterInfoDatabaseLookupMsg (const uint8_t * key, const uint8_t * from, std::shared_ptr<I2NPMessage> CreateRouterInfoDatabaseLookupMsg (const uint8_t * key, const uint8_t * from,
uint32_t replyTunnelID, bool exploratory, std::unordered_set<i2p::data::IdentHash> * excludedPeers) uint32_t replyTunnelID, bool exploratory, std::set<i2p::data::IdentHash> * excludedPeers)
{ {
int cnt = excludedPeers ? excludedPeers->size () : 0; int cnt = excludedPeers ? excludedPeers->size () : 0;
auto m = cnt > 7 ? NewI2NPMessage () : NewI2NPShortMessage (); auto m = cnt > 7 ? NewI2NPMessage () : NewI2NPShortMessage ();
@ -192,7 +177,7 @@ namespace i2p
} }
std::shared_ptr<I2NPMessage> CreateLeaseSetDatabaseLookupMsg (const i2p::data::IdentHash& dest, std::shared_ptr<I2NPMessage> CreateLeaseSetDatabaseLookupMsg (const i2p::data::IdentHash& dest,
const std::unordered_set<i2p::data::IdentHash>& excludedFloodfills, const std::set<i2p::data::IdentHash>& excludedFloodfills,
std::shared_ptr<const i2p::tunnel::InboundTunnel> replyTunnel, const uint8_t * replyKey, std::shared_ptr<const i2p::tunnel::InboundTunnel> replyTunnel, const uint8_t * replyKey,
const uint8_t * replyTag, bool replyECIES) const uint8_t * replyTag, bool replyECIES)
{ {
@ -384,20 +369,10 @@ namespace i2p
if (!memcmp (record + BUILD_REQUEST_RECORD_TO_PEER_OFFSET, (const uint8_t *)i2p::context.GetRouterInfo ().GetIdentHash (), 16)) if (!memcmp (record + BUILD_REQUEST_RECORD_TO_PEER_OFFSET, (const uint8_t *)i2p::context.GetRouterInfo ().GetIdentHash (), 16))
{ {
LogPrint (eLogDebug, "I2NP: Build request record ", i, " is ours"); LogPrint (eLogDebug, "I2NP: Build request record ", i, " is ours");
if (!i2p::context.DecryptTunnelBuildRecord (record + BUILD_REQUEST_RECORD_ENCRYPTED_OFFSET, clearText)) if (!i2p::context.DecryptTunnelBuildRecord (record + BUILD_REQUEST_RECORD_ENCRYPTED_OFFSET, clearText)) return false;
{
LogPrint (eLogWarning, "I2NP: Failed to decrypt tunnel build record");
return false;
}
if (!memcmp ((const uint8_t *)i2p::context.GetIdentHash (), clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET, 32) && // if next ident is now ours
!(clearText[ECIES_BUILD_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_ENDPOINT_FLAG)) // and not endpoint
{
LogPrint (eLogWarning, "I2NP: Next ident is ours in tunnel build record");
return false;
}
uint8_t retCode = 0; uint8_t retCode = 0;
// replace record to reply // replace record to reply
if (i2p::context.AcceptsTunnels () && i2p::context.GetCongestionLevel (false) < CONGESTION_LEVEL_FULL) if (i2p::context.AcceptsTunnels () && !i2p::context.IsHighCongestion ())
{ {
auto transitTunnel = i2p::tunnel::CreateTransitTunnel ( auto transitTunnel = i2p::tunnel::CreateTransitTunnel (
bufbe32toh (clearText + ECIES_BUILD_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET), bufbe32toh (clearText + ECIES_BUILD_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET),
@ -602,24 +577,16 @@ namespace i2p
memcpy (ivKey, noiseState.m_CK + 32, 32); memcpy (ivKey, noiseState.m_CK + 32, 32);
} }
else else
{
if (!memcmp ((const uint8_t *)i2p::context.GetIdentHash (), clearText + SHORT_REQUEST_RECORD_NEXT_IDENT_OFFSET, 32)) // if next ident is now ours
{
LogPrint (eLogWarning, "I2NP: Next ident is ours in short request record");
return;
}
memcpy (ivKey, noiseState.m_CK , 32); memcpy (ivKey, noiseState.m_CK , 32);
}
// check if we accept this tunnel // check if we accept this tunnel
std::shared_ptr<i2p::tunnel::TransitTunnel> transitTunnel;
uint8_t retCode = 0; uint8_t retCode = 0;
if (!i2p::context.AcceptsTunnels () || i2p::context.GetCongestionLevel (false) >= CONGESTION_LEVEL_FULL) if (!i2p::context.AcceptsTunnels () || i2p::context.IsHighCongestion ())
retCode = 30; retCode = 30;
if (!retCode) if (!retCode)
{ {
// create new transit tunnel // create new transit tunnel
transitTunnel = i2p::tunnel::CreateTransitTunnel ( auto transitTunnel = i2p::tunnel::CreateTransitTunnel (
bufbe32toh (clearText + SHORT_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET), bufbe32toh (clearText + SHORT_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET),
clearText + SHORT_REQUEST_RECORD_NEXT_IDENT_OFFSET, clearText + SHORT_REQUEST_RECORD_NEXT_IDENT_OFFSET,
bufbe32toh (clearText + SHORT_REQUEST_RECORD_NEXT_TUNNEL_OFFSET), bufbe32toh (clearText + SHORT_REQUEST_RECORD_NEXT_TUNNEL_OFFSET),
@ -653,22 +620,11 @@ namespace i2p
reply += SHORT_TUNNEL_BUILD_RECORD_SIZE; reply += SHORT_TUNNEL_BUILD_RECORD_SIZE;
} }
// send reply // send reply
auto onDrop = [transitTunnel]()
{
if (transitTunnel)
{
auto t = transitTunnel->GetCreationTime ();
if (t > i2p::tunnel::TUNNEL_EXPIRATION_TIMEOUT)
// make transit tunnel expired
transitTunnel->SetCreationTime (t - i2p::tunnel::TUNNEL_EXPIRATION_TIMEOUT);
}
};
if (isEndpoint) if (isEndpoint)
{ {
auto replyMsg = NewI2NPShortMessage (); auto replyMsg = NewI2NPShortMessage ();
replyMsg->Concat (buf, len); replyMsg->Concat (buf, len);
replyMsg->FillI2NPMessageHeader (eI2NPShortTunnelBuildReply, bufbe32toh (clearText + SHORT_REQUEST_RECORD_SEND_MSG_ID_OFFSET)); replyMsg->FillI2NPMessageHeader (eI2NPShortTunnelBuildReply, bufbe32toh (clearText + SHORT_REQUEST_RECORD_SEND_MSG_ID_OFFSET));
if (transitTunnel) replyMsg->onDrop = onDrop;
if (memcmp ((const uint8_t *)i2p::context.GetIdentHash (), if (memcmp ((const uint8_t *)i2p::context.GetIdentHash (),
clearText + SHORT_REQUEST_RECORD_NEXT_IDENT_OFFSET, 32)) // reply IBGW is not local? clearText + SHORT_REQUEST_RECORD_NEXT_IDENT_OFFSET, 32)) // reply IBGW is not local?
{ {
@ -686,21 +642,15 @@ namespace i2p
uint32_t tunnelID = bufbe32toh (clearText + SHORT_REQUEST_RECORD_NEXT_TUNNEL_OFFSET); uint32_t tunnelID = bufbe32toh (clearText + SHORT_REQUEST_RECORD_NEXT_TUNNEL_OFFSET);
auto tunnel = i2p::tunnel::tunnels.GetTunnel (tunnelID); auto tunnel = i2p::tunnel::tunnels.GetTunnel (tunnelID);
if (tunnel) if (tunnel)
{
tunnel->SendTunnelDataMsg (replyMsg); tunnel->SendTunnelDataMsg (replyMsg);
tunnel->FlushTunnelDataMsgs ();
}
else else
LogPrint (eLogWarning, "I2NP: Tunnel ", tunnelID, " not found for short tunnel build reply"); LogPrint (eLogWarning, "I2NP: Tunnel ", tunnelID, " not found for short tunnel build reply");
} }
} }
else else
{ transports.SendMessage (clearText + SHORT_REQUEST_RECORD_NEXT_IDENT_OFFSET,
auto msg = CreateI2NPMessage (eI2NPShortTunnelBuild, buf, len, CreateI2NPMessage (eI2NPShortTunnelBuild, buf, len,
bufbe32toh (clearText + SHORT_REQUEST_RECORD_SEND_MSG_ID_OFFSET)); bufbe32toh (clearText + SHORT_REQUEST_RECORD_SEND_MSG_ID_OFFSET)));
if (transitTunnel) msg->onDrop = onDrop;
transports.SendMessage (clearText + SHORT_REQUEST_RECORD_NEXT_IDENT_OFFSET, msg);
}
return; return;
} }
record += SHORT_TUNNEL_BUILD_RECORD_SIZE; record += SHORT_TUNNEL_BUILD_RECORD_SIZE;
@ -760,11 +710,7 @@ namespace i2p
return msg; return msg;
} }
else else
{ return CreateTunnelGatewayMsg (tunnelID, msg->GetBuffer (), msg->GetLength ());
auto newMsg = CreateTunnelGatewayMsg (tunnelID, msg->GetBuffer (), msg->GetLength ());
if (msg->onDrop) newMsg->onDrop = msg->onDrop;
return newMsg;
}
} }
std::shared_ptr<I2NPMessage> CreateTunnelGatewayMsg (uint32_t tunnelID, I2NPMessageType msgType, std::shared_ptr<I2NPMessage> CreateTunnelGatewayMsg (uint32_t tunnelID, I2NPMessageType msgType,
@ -862,14 +808,12 @@ namespace i2p
break; break;
} }
case eI2NPDatabaseStore: case eI2NPDatabaseStore:
case eI2NPDatabaseSearchReply:
// forward to netDb if came directly or through exploratory tunnel as response to our request // forward to netDb if came directly or through exploratory tunnel as response to our request
if (!msg->from || !msg->from->GetTunnelPool () || msg->from->GetTunnelPool ()->IsExploratory ()) if (!msg->from || !msg->from->GetTunnelPool () || msg->from->GetTunnelPool ()->IsExploratory ())
i2p::data::netdb.PostI2NPMsg (msg); i2p::data::netdb.PostI2NPMsg (msg);
break; break;
case eI2NPDatabaseSearchReply:
if (!msg->from || !msg->from->GetTunnelPool () || msg->from->GetTunnelPool ()->IsExploratory ())
i2p::data::netdb.PostDatabaseSearchReplyMsg (msg);
break;
case eI2NPDatabaseLookup: case eI2NPDatabaseLookup:
// forward to netDb if floodfill and came directly // forward to netDb if floodfill and came directly
if (!msg->from && i2p::context.IsFloodfill ()) if (!msg->from && i2p::context.IsFloodfill ())
@ -883,10 +827,6 @@ namespace i2p
i2p::context.ProcessDeliveryStatusMessage (msg); i2p::context.ProcessDeliveryStatusMessage (msg);
break; break;
} }
case eI2NPTunnelTest:
if (msg->from && msg->from->GetTunnelPool ())
msg->from->GetTunnelPool ()->ProcessTunnelTest (msg);
break;
case eI2NPVariableTunnelBuild: case eI2NPVariableTunnelBuild:
case eI2NPTunnelBuild: case eI2NPTunnelBuild:
case eI2NPShortTunnelBuild: case eI2NPShortTunnelBuild:

@ -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 * This file is part of Purple i2pd project and licensed under BSD3
* *
@ -11,9 +11,8 @@
#include <inttypes.h> #include <inttypes.h>
#include <string.h> #include <string.h>
#include <unordered_set> #include <set>
#include <memory> #include <memory>
#include <functional>
#include "Crypto.h" #include "Crypto.h"
#include "I2PEndian.h" #include "I2PEndian.h"
#include "Identity.h" #include "Identity.h"
@ -48,11 +47,6 @@ namespace i2p
const size_t DELIVERY_STATUS_TIMESTAMP_OFFSET = DELIVERY_STATUS_MSGID_OFFSET + 4; const size_t DELIVERY_STATUS_TIMESTAMP_OFFSET = DELIVERY_STATUS_MSGID_OFFSET + 4;
const size_t DELIVERY_STATUS_SIZE = DELIVERY_STATUS_TIMESTAMP_OFFSET + 8; const size_t DELIVERY_STATUS_SIZE = DELIVERY_STATUS_TIMESTAMP_OFFSET + 8;
// TunnelTest
const size_t TUNNEL_TEST_MSGID_OFFSET = 0;
const size_t TUNNEL_TEST_TIMESTAMP_OFFSET = TUNNEL_TEST_MSGID_OFFSET + 4;
const size_t TUNNEL_TEST_SIZE = TUNNEL_TEST_TIMESTAMP_OFFSET + 8;
// DatabaseStore // DatabaseStore
const size_t DATABASE_STORE_KEY_OFFSET = 0; const size_t DATABASE_STORE_KEY_OFFSET = 0;
const size_t DATABASE_STORE_TYPE_OFFSET = DATABASE_STORE_KEY_OFFSET + 32; const size_t DATABASE_STORE_TYPE_OFFSET = DATABASE_STORE_KEY_OFFSET + 32;
@ -121,8 +115,7 @@ namespace i2p
eI2NPVariableTunnelBuild = 23, eI2NPVariableTunnelBuild = 23,
eI2NPVariableTunnelBuildReply = 24, eI2NPVariableTunnelBuildReply = 24,
eI2NPShortTunnelBuild = 25, eI2NPShortTunnelBuild = 25,
eI2NPShortTunnelBuildReply = 26, eI2NPShortTunnelBuildReply = 26
eI2NPTunnelTest = 231
}; };
const uint8_t TUNNEL_BUILD_RECORD_GATEWAY_FLAG = 0x80; const uint8_t TUNNEL_BUILD_RECORD_GATEWAY_FLAG = 0x80;
@ -145,16 +138,9 @@ namespace tunnel
class TunnelPool; class TunnelPool;
} }
const int CONGESTION_LEVEL_MEDIUM = 70;
const int CONGESTION_LEVEL_HIGH = 90;
const int CONGESTION_LEVEL_FULL = 100;
const size_t I2NP_MAX_MESSAGE_SIZE = 62708; const size_t I2NP_MAX_MESSAGE_SIZE = 62708;
const size_t I2NP_MAX_SHORT_MESSAGE_SIZE = 4096; const size_t I2NP_MAX_SHORT_MESSAGE_SIZE = 4096;
const size_t I2NP_MAX_MEDIUM_MESSAGE_SIZE = 16384; const size_t I2NP_MAX_MEDIUM_MESSAGE_SIZE = 16384;
const unsigned int I2NP_MESSAGE_LOCAL_EXPIRATION_TIMEOUT_FACTOR = 3; // multiples of RTT
const unsigned int I2NP_MESSAGE_LOCAL_EXPIRATION_TIMEOUT_MIN = 200000; // in microseconds
const unsigned int I2NP_MESSAGE_LOCAL_EXPIRATION_TIMEOUT_MAX = 2000000; // in microseconds
const unsigned int I2NP_MESSAGE_EXPIRATION_TIMEOUT = 8000; // in milliseconds (as initial RTT) const unsigned int I2NP_MESSAGE_EXPIRATION_TIMEOUT = 8000; // in milliseconds (as initial RTT)
const unsigned int I2NP_MESSAGE_CLOCK_SKEW = 60*1000; // 1 minute in milliseconds const unsigned int I2NP_MESSAGE_CLOCK_SKEW = 60*1000; // 1 minute in milliseconds
@ -163,11 +149,9 @@ namespace tunnel
uint8_t * buf; uint8_t * buf;
size_t len, offset, maxLen; size_t len, offset, maxLen;
std::shared_ptr<i2p::tunnel::InboundTunnel> from; std::shared_ptr<i2p::tunnel::InboundTunnel> from;
std::function<void ()> onDrop;
uint64_t enqueueTime; // monotonic microseconds
I2NPMessage (): buf (nullptr), len (I2NP_HEADER_SIZE + 2), I2NPMessage (): buf (nullptr),len (I2NP_HEADER_SIZE + 2),
offset(2), maxLen (0), from (nullptr), enqueueTime (0) {}; // reserve 2 bytes for NTCP header offset(2), maxLen (0), from (nullptr) {}; // reserve 2 bytes for NTCP header
// header accessors // header accessors
uint8_t * GetHeader () { return GetBuffer (); }; uint8_t * GetHeader () { return GetBuffer (); };
@ -177,9 +161,7 @@ namespace tunnel
void SetMsgID (uint32_t msgID) { htobe32buf (GetHeader () + I2NP_HEADER_MSGID_OFFSET, msgID); }; void SetMsgID (uint32_t msgID) { htobe32buf (GetHeader () + I2NP_HEADER_MSGID_OFFSET, msgID); };
uint32_t GetMsgID () const { return bufbe32toh (GetHeader () + I2NP_HEADER_MSGID_OFFSET); }; uint32_t GetMsgID () const { return bufbe32toh (GetHeader () + I2NP_HEADER_MSGID_OFFSET); };
void SetExpiration (uint64_t expiration) { htobe64buf (GetHeader () + I2NP_HEADER_EXPIRATION_OFFSET, expiration); }; void SetExpiration (uint64_t expiration) { htobe64buf (GetHeader () + I2NP_HEADER_EXPIRATION_OFFSET, expiration); };
void SetEnqueueTime (uint64_t mts) { enqueueTime = mts; };
uint64_t GetExpiration () const { return bufbe64toh (GetHeader () + I2NP_HEADER_EXPIRATION_OFFSET); }; uint64_t GetExpiration () const { return bufbe64toh (GetHeader () + I2NP_HEADER_EXPIRATION_OFFSET); };
uint64_t GetEnqueueTime () const { return enqueueTime; };
void SetSize (uint16_t size) { htobe16buf (GetHeader () + I2NP_HEADER_SIZE_OFFSET, size); }; void SetSize (uint16_t size) { htobe16buf (GetHeader () + I2NP_HEADER_SIZE_OFFSET, size); };
uint16_t GetSize () const { return bufbe16toh (GetHeader () + I2NP_HEADER_SIZE_OFFSET); }; uint16_t GetSize () const { return bufbe16toh (GetHeader () + I2NP_HEADER_SIZE_OFFSET); };
void UpdateSize () { SetSize (GetPayloadLength ()); }; void UpdateSize () { SetSize (GetPayloadLength ()); };
@ -259,6 +241,7 @@ namespace tunnel
SetSize (len - offset - I2NP_HEADER_SIZE); SetSize (len - offset - I2NP_HEADER_SIZE);
SetChks (0); SetChks (0);
} }
void ToNTCP2 () void ToNTCP2 ()
{ {
uint8_t * ntcp2 = GetNTCP2Header (); uint8_t * ntcp2 = GetNTCP2Header ();
@ -269,9 +252,6 @@ namespace tunnel
void FillI2NPMessageHeader (I2NPMessageType msgType, uint32_t replyMsgID = 0, bool checksum = true); void FillI2NPMessageHeader (I2NPMessageType msgType, uint32_t replyMsgID = 0, bool checksum = true);
void RenewI2NPMessageHeader (); void RenewI2NPMessageHeader ();
bool IsExpired () const; bool IsExpired () const;
bool IsExpired (uint64_t ts) const; // in milliseconds
void Drop () { if (onDrop) { onDrop (); onDrop = nullptr; }; }
}; };
template<int sz> template<int sz>
@ -291,12 +271,11 @@ namespace tunnel
std::shared_ptr<I2NPMessage> CreateI2NPMessage (const uint8_t * buf, size_t len, std::shared_ptr<i2p::tunnel::InboundTunnel> from = nullptr); std::shared_ptr<I2NPMessage> CreateI2NPMessage (const uint8_t * buf, size_t len, std::shared_ptr<i2p::tunnel::InboundTunnel> from = nullptr);
std::shared_ptr<I2NPMessage> CopyI2NPMessage (std::shared_ptr<I2NPMessage> msg); std::shared_ptr<I2NPMessage> CopyI2NPMessage (std::shared_ptr<I2NPMessage> msg);
std::shared_ptr<I2NPMessage> CreateTunnelTestMsg (uint32_t msgID);
std::shared_ptr<I2NPMessage> CreateDeliveryStatusMsg (uint32_t msgID); std::shared_ptr<I2NPMessage> CreateDeliveryStatusMsg (uint32_t msgID);
std::shared_ptr<I2NPMessage> CreateRouterInfoDatabaseLookupMsg (const uint8_t * key, const uint8_t * from, std::shared_ptr<I2NPMessage> CreateRouterInfoDatabaseLookupMsg (const uint8_t * key, const uint8_t * from,
uint32_t replyTunnelID, bool exploratory = false, std::unordered_set<i2p::data::IdentHash> * excludedPeers = nullptr); uint32_t replyTunnelID, bool exploratory = false, std::set<i2p::data::IdentHash> * excludedPeers = nullptr);
std::shared_ptr<I2NPMessage> CreateLeaseSetDatabaseLookupMsg (const i2p::data::IdentHash& dest, std::shared_ptr<I2NPMessage> CreateLeaseSetDatabaseLookupMsg (const i2p::data::IdentHash& dest,
const std::unordered_set<i2p::data::IdentHash>& excludedFloodfills, const std::set<i2p::data::IdentHash>& excludedFloodfills,
std::shared_ptr<const i2p::tunnel::InboundTunnel> replyTunnel, std::shared_ptr<const i2p::tunnel::InboundTunnel> replyTunnel,
const uint8_t * replyKey, const uint8_t * replyTag, bool replyECIES = false); const uint8_t * replyKey, const uint8_t * replyTag, bool replyECIES = false);
std::shared_ptr<I2NPMessage> CreateDatabaseSearchReply (const i2p::data::IdentHash& ident, std::vector<i2p::data::IdentHash> routers); std::shared_ptr<I2NPMessage> CreateDatabaseSearchReply (const i2p::data::IdentHash& ident, std::vector<i2p::data::IdentHash> routers);

@ -14,7 +14,7 @@
#if defined(__FreeBSD__) || defined(__NetBSD__) #if defined(__FreeBSD__) || defined(__NetBSD__)
#include <sys/endian.h> #include <sys/endian.h>
#elif defined(__linux__) || defined(__FreeBSD_kernel__) || defined(__OpenBSD__) || defined(__GLIBC__) || defined(__HAIKU__) #elif defined(__linux__) || defined(__FreeBSD_kernel__) || defined(__OpenBSD__) || defined(__GLIBC__)
#include <endian.h> #include <endian.h>
#elif defined(__APPLE__) && defined(__MACH__) #elif defined(__APPLE__) && defined(__MACH__)

@ -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 * This file is part of Purple i2pd project and licensed under BSD3
* *
@ -790,14 +790,11 @@ namespace data
return keys; return keys;
} }
IdentHash CreateRoutingKey (const IdentHash& ident, bool nextDay) IdentHash CreateRoutingKey (const IdentHash& ident)
{ {
uint8_t buf[41]; // ident + yyyymmdd uint8_t buf[41]; // ident + yyyymmdd
memcpy (buf, (const uint8_t *)ident, 32); memcpy (buf, (const uint8_t *)ident, 32);
if (nextDay) i2p::util::GetCurrentDate ((char *)(buf + 32));
i2p::util::GetNextDayDate ((char *)(buf + 32));
else
i2p::util::GetCurrentDate ((char *)(buf + 32));
IdentHash key; IdentHash key;
SHA256(buf, 40, key); SHA256(buf, 40, key);
return key; return key;

@ -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 * This file is part of Purple i2pd project and licensed under BSD3
* *
@ -206,7 +206,7 @@ namespace data
bool operator< (const XORMetric& other) const { return memcmp (metric, other.metric, 32) < 0; }; bool operator< (const XORMetric& other) const { return memcmp (metric, other.metric, 32) < 0; };
}; };
IdentHash CreateRoutingKey (const IdentHash& ident, bool nextDay = false); IdentHash CreateRoutingKey (const IdentHash& ident);
XORMetric operator^(const IdentHash& key1, const IdentHash& key2); XORMetric operator^(const IdentHash& key1, const IdentHash& key2);
// destination for delivery instructions // destination for delivery instructions

@ -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 * This file is part of Purple i2pd project and licensed under BSD3
* *
@ -87,8 +87,8 @@ namespace log {
Log (); Log ();
~Log (); ~Log ();
LogType GetLogType () const { return m_Destination; }; LogType GetLogType () { return m_Destination; };
LogLevel GetLogLevel () const { return m_MinLevel; }; LogLevel GetLogLevel () { return m_MinLevel; };
void Start (); void Start ();
void Stop (); void Stop ();
@ -160,11 +160,6 @@ namespace log {
} // log } // log
} // i2p } // i2p
inline bool CheckLogLevel (LogLevel level) noexcept
{
return level <= i2p::log::Logger().GetLogLevel ();
}
/** internal usage only -- folding args array to single string */ /** internal usage only -- folding args array to single string */
template<typename TValue> template<typename TValue>
void LogPrint (std::stringstream& s, TValue&& arg) noexcept void LogPrint (std::stringstream& s, TValue&& arg) noexcept
@ -190,7 +185,9 @@ void LogPrint (std::stringstream& s, TValue&& arg, TArgs&&... args) noexcept
template<typename... TArgs> template<typename... TArgs>
void LogPrint (LogLevel level, TArgs&&... args) noexcept void LogPrint (LogLevel level, TArgs&&... args) noexcept
{ {
if (!CheckLogLevel (level)) return; i2p::log::Log &log = i2p::log::Logger();
if (level > log.GetLogLevel ())
return;
// fold message to single string // fold message to single string
std::stringstream ss; std::stringstream ss;
@ -203,7 +200,7 @@ void LogPrint (LogLevel level, TArgs&&... args) noexcept
auto msg = std::make_shared<i2p::log::LogMsg>(level, std::time(nullptr), std::move(ss).str()); auto msg = std::make_shared<i2p::log::LogMsg>(level, std::time(nullptr), std::move(ss).str());
msg->tid = std::this_thread::get_id(); msg->tid = std::this_thread::get_id();
i2p::log::Logger().Append(msg); log.Append(msg);
} }
/** /**

@ -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 * This file is part of Purple i2pd project and licensed under BSD3
* *
@ -19,10 +19,9 @@
#include "RouterContext.h" #include "RouterContext.h"
#include "Transports.h" #include "Transports.h"
#include "NetDb.hpp" #include "NetDb.hpp"
#include "NTCP2.h"
#include "HTTP.h" #include "HTTP.h"
#include "util.h" #include "util.h"
#include "Socks5.h"
#include "NTCP2.h"
#if defined(__linux__) && !defined(_NETINET_IN_H) #if defined(__linux__) && !defined(_NETINET_IN_H)
#include <linux/in6.h> #include <linux/in6.h>
@ -129,8 +128,7 @@ namespace transport
options[1] = 2; // ver options[1] = 2; // ver
htobe16buf (options + 2, paddingLength); // padLen htobe16buf (options + 2, paddingLength); // padLen
// m3p2Len // m3p2Len
auto riBuffer = i2p::context.CopyRouterInfoBuffer (); auto bufLen = i2p::context.GetRouterInfo ().GetBufferLen ();
auto bufLen = riBuffer->GetBufferLen ();
m3p2Len = bufLen + 4 + 16; // (RI header + RI + MAC for now) TODO: implement options m3p2Len = bufLen + 4 + 16; // (RI header + RI + MAC for now) TODO: implement options
htobe16buf (options + 4, m3p2Len); htobe16buf (options + 4, m3p2Len);
// fill m3p2 payload (RouterInfo block) // fill m3p2 payload (RouterInfo block)
@ -139,7 +137,7 @@ namespace transport
m3p2[0] = eNTCP2BlkRouterInfo; // block m3p2[0] = eNTCP2BlkRouterInfo; // block
htobe16buf (m3p2 + 1, bufLen + 1); // flag + RI htobe16buf (m3p2 + 1, bufLen + 1); // flag + RI
m3p2[3] = 0; // flag m3p2[3] = 0; // flag
memcpy (m3p2 + 4, riBuffer->data (), bufLen); // TODO: eliminate extra copy memcpy (m3p2 + 4, i2p::context.GetRouterInfo ().GetBuffer (), bufLen); // TODO: own RI should be protected by mutex
// 2 bytes reserved // 2 bytes reserved
htobe32buf (options + 8, (i2p::util::GetMillisecondsSinceEpoch () + 500)/1000); // tsA, rounded to seconds htobe32buf (options + 8, (i2p::util::GetMillisecondsSinceEpoch () + 500)/1000); // tsA, rounded to seconds
// 4 bytes reserved // 4 bytes reserved
@ -375,15 +373,13 @@ namespace transport
m_Socket.close (); m_Socket.close ();
transports.PeerDisconnected (shared_from_this ()); transports.PeerDisconnected (shared_from_this ());
m_Server.RemoveNTCP2Session (shared_from_this ()); m_Server.RemoveNTCP2Session (shared_from_this ());
for (auto& it: m_SendQueue)
it->Drop ();
m_SendQueue.clear (); m_SendQueue.clear ();
SetSendQueueSize (0); m_SendQueueSize = 0;
auto remoteIdentity = GetRemoteIdentity (); auto remoteIdentity = GetRemoteIdentity ();
if (remoteIdentity) if (remoteIdentity)
{ {
LogPrint (eLogDebug, "NTCP2: Session with ", GetRemoteEndpoint (), LogPrint (eLogDebug, "NTCP2: Session with ", GetRemoteEndpoint (),
" (", i2p::data::GetIdentHashAbbreviation (remoteIdentity->GetIdentHash ()), ") terminated"); " (", i2p::data::GetIdentHashAbbreviation (GetRemoteIdentity ()->GetIdentHash ()), ") terminated");
} }
else else
{ {
@ -412,7 +408,6 @@ namespace transport
m_IsEstablished = true; m_IsEstablished = true;
m_Establisher.reset (nullptr); m_Establisher.reset (nullptr);
SetTerminationTimeout (NTCP2_TERMINATION_TIMEOUT); SetTerminationTimeout (NTCP2_TERMINATION_TIMEOUT);
SendQueue ();
transports.PeerConnected (shared_from_this ()); transports.PeerConnected (shared_from_this ());
} }
@ -438,7 +433,7 @@ namespace transport
void NTCP2Session::DeleteNextReceiveBuffer (uint64_t ts) void NTCP2Session::DeleteNextReceiveBuffer (uint64_t ts)
{ {
if (m_NextReceivedBuffer && !m_IsReceiving && if (m_NextReceivedBuffer && !m_IsReceiving &&
ts > GetLastActivityTimestamp () + NTCP2_RECEIVE_BUFFER_DELETION_TIMEOUT) ts > m_LastActivityTimestamp + NTCP2_RECEIVE_BUFFER_DELETION_TIMEOUT)
{ {
delete[] m_NextReceivedBuffer; delete[] m_NextReceivedBuffer;
m_NextReceivedBuffer = nullptr; m_NextReceivedBuffer = nullptr;
@ -794,7 +789,7 @@ namespace transport
void NTCP2Session::ServerLogin () void NTCP2Session::ServerLogin ()
{ {
SetTerminationTimeout (NTCP2_ESTABLISH_TIMEOUT); SetTerminationTimeout (NTCP2_ESTABLISH_TIMEOUT);
SetLastActivityTimestamp (i2p::util::GetSecondsSinceEpoch ()); m_LastActivityTimestamp = i2p::util::GetSecondsSinceEpoch ();
m_Establisher->CreateEphemeralKey (); m_Establisher->CreateEphemeralKey ();
boost::asio::async_read (m_Socket, boost::asio::buffer(m_Establisher->m_SessionRequestBuffer, 64), boost::asio::transfer_all (), boost::asio::async_read (m_Socket, boost::asio::buffer(m_Establisher->m_SessionRequestBuffer, 64), boost::asio::transfer_all (),
std::bind(&NTCP2Session::HandleSessionRequestReceived, shared_from_this (), std::bind(&NTCP2Session::HandleSessionRequestReceived, shared_from_this (),
@ -838,19 +833,14 @@ namespace transport
CreateNextReceivedBuffer (m_NextReceivedLen); CreateNextReceivedBuffer (m_NextReceivedLen);
boost::system::error_code ec; boost::system::error_code ec;
size_t moreBytes = m_Socket.available(ec); size_t moreBytes = m_Socket.available(ec);
if (!ec) if (!ec && moreBytes >= m_NextReceivedLen)
{ {
if (moreBytes >= m_NextReceivedLen) // read and process message immediately if available
{ moreBytes = boost::asio::read (m_Socket, boost::asio::buffer(m_NextReceivedBuffer, m_NextReceivedLen), boost::asio::transfer_all (), ec);
// read and process message immediately if available HandleReceived (ec, moreBytes);
moreBytes = boost::asio::read (m_Socket, boost::asio::buffer(m_NextReceivedBuffer, m_NextReceivedLen), boost::asio::transfer_all (), ec); }
HandleReceived (ec, moreBytes);
}
else
Receive ();
}
else else
LogPrint (eLogWarning, "NTCP2: Socket error: ", ec.message ()); Receive ();
} }
else else
{ {
@ -876,14 +866,15 @@ namespace transport
{ {
if (ecode) if (ecode)
{ {
if (ecode != boost::asio::error::operation_aborted) if (ecode != boost::asio::error::operation_aborted)
LogPrint (eLogWarning, "NTCP2: Receive read error: ", ecode.message ()); LogPrint (eLogWarning, "NTCP2: Receive read error: ", ecode.message ());
Terminate (); Terminate ();
} }
else else
{ {
UpdateNumReceivedBytes (bytes_transferred + 2); m_LastActivityTimestamp = i2p::util::GetSecondsSinceEpoch ();
i2p::transport::transports.UpdateReceivedBytes (bytes_transferred + 2); m_NumReceivedBytes += bytes_transferred + 2; // + length
i2p::transport::transports.UpdateReceivedBytes (bytes_transferred);
uint8_t nonce[12]; uint8_t nonce[12];
CreateNonce (m_ReceiveSequenceNumber, nonce); m_ReceiveSequenceNumber++; CreateNonce (m_ReceiveSequenceNumber, nonce); m_ReceiveSequenceNumber++;
if (i2p::crypto::AEADChaCha20Poly1305 (m_NextReceivedBuffer, m_NextReceivedLen-16, nullptr, 0, m_ReceiveKey, nonce, m_NextReceivedBuffer, m_NextReceivedLen, false)) if (i2p::crypto::AEADChaCha20Poly1305 (m_NextReceivedBuffer, m_NextReceivedLen-16, nullptr, 0, m_ReceiveKey, nonce, m_NextReceivedBuffer, m_NextReceivedLen, false))
@ -1056,11 +1047,6 @@ namespace transport
macBuf = m_NextSendBuffer + paddingLen; macBuf = m_NextSendBuffer + paddingLen;
totalLen += paddingLen; totalLen += paddingLen;
} }
if (totalLen > NTCP2_UNENCRYPTED_FRAME_MAX_SIZE)
{
LogPrint (eLogError, "NTCP2: Frame to send is too long ", totalLen);
return;
}
uint8_t nonce[12]; uint8_t nonce[12];
CreateNonce (m_SendSequenceNumber, nonce); m_SendSequenceNumber++; CreateNonce (m_SendSequenceNumber, nonce); m_SendSequenceNumber++;
i2p::crypto::AEADChaCha20Poly1305Encrypt (encryptBufs, m_SendKey, nonce, macBuf); // encrypt buffers i2p::crypto::AEADChaCha20Poly1305Encrypt (encryptBufs, m_SendKey, nonce, macBuf); // encrypt buffers
@ -1085,12 +1071,6 @@ namespace transport
delete[] m_NextSendBuffer; m_NextSendBuffer = nullptr; delete[] m_NextSendBuffer; m_NextSendBuffer = nullptr;
return; return;
} }
if (payloadLen > NTCP2_UNENCRYPTED_FRAME_MAX_SIZE)
{
LogPrint (eLogError, "NTCP2: Buffer to send is too long ", payloadLen);
delete[] m_NextSendBuffer; m_NextSendBuffer = nullptr;
return;
}
// encrypt // encrypt
uint8_t nonce[12]; uint8_t nonce[12];
CreateNonce (m_SendSequenceNumber, nonce); m_SendSequenceNumber++; CreateNonce (m_SendSequenceNumber, nonce); m_SendSequenceNumber++;
@ -1115,10 +1095,11 @@ namespace transport
} }
else else
{ {
UpdateNumSentBytes (bytes_transferred); m_LastActivityTimestamp = i2p::util::GetSecondsSinceEpoch ();
m_NumSentBytes += bytes_transferred;
i2p::transport::transports.UpdateSentBytes (bytes_transferred); i2p::transport::transports.UpdateSentBytes (bytes_transferred);
LogPrint (eLogDebug, "NTCP2: Next frame sent ", bytes_transferred); LogPrint (eLogDebug, "NTCP2: Next frame sent ", bytes_transferred);
if (GetLastActivityTimestamp () > m_NextRouterInfoResendTime) if (m_LastActivityTimestamp > m_NextRouterInfoResendTime)
{ {
m_NextRouterInfoResendTime += NTCP2_ROUTERINFO_RESEND_INTERVAL + m_NextRouterInfoResendTime += NTCP2_ROUTERINFO_RESEND_INTERVAL +
rand ()%NTCP2_ROUTERINFO_RESEND_INTERVAL_THRESHOLD; rand ()%NTCP2_ROUTERINFO_RESEND_INTERVAL_THRESHOLD;
@ -1127,41 +1108,30 @@ namespace transport
else else
{ {
SendQueue (); SendQueue ();
SetSendQueueSize (m_SendQueue.size ()); m_SendQueueSize = m_SendQueue.size ();
} }
} }
} }
void NTCP2Session::SendQueue () void NTCP2Session::SendQueue ()
{ {
if (!m_SendQueue.empty () && m_IsEstablished) if (!m_SendQueue.empty ())
{ {
std::vector<std::shared_ptr<I2NPMessage> > msgs; std::vector<std::shared_ptr<I2NPMessage> > msgs;
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
size_t s = 0; size_t s = 0;
while (!m_SendQueue.empty ()) while (!m_SendQueue.empty ())
{ {
auto msg = m_SendQueue.front (); auto msg = m_SendQueue.front ();
if (!msg || msg->IsExpired (ts))
{
// drop null or expired message
if (msg) msg->Drop ();
m_SendQueue.pop_front ();
continue;
}
size_t len = msg->GetNTCP2Length (); size_t len = msg->GetNTCP2Length ();
if (s + len + 3 <= NTCP2_UNENCRYPTED_FRAME_MAX_SIZE) // 3 bytes block header if (s + len + 3 <= NTCP2_UNENCRYPTED_FRAME_MAX_SIZE) // 3 bytes block header
{ {
msgs.push_back (msg); msgs.push_back (msg);
s += (len + 3); s += (len + 3);
m_SendQueue.pop_front (); m_SendQueue.pop_front ();
if (s >= NTCP2_SEND_AFTER_FRAME_SIZE)
break; // send frame right a way
} }
else if (len + 3 > NTCP2_UNENCRYPTED_FRAME_MAX_SIZE) else if (len + 3 > NTCP2_UNENCRYPTED_FRAME_MAX_SIZE)
{ {
LogPrint (eLogError, "NTCP2: I2NP message of size ", len, " can't be sent. Dropped"); LogPrint (eLogError, "NTCP2: I2NP message of size ", len, " can't be sent. Dropped");
msg->Drop ();
m_SendQueue.pop_front (); m_SendQueue.pop_front ();
} }
else else
@ -1171,33 +1141,13 @@ namespace transport
} }
} }
void NTCP2Session::MoveSendQueue (std::shared_ptr<NTCP2Session> other)
{
if (!other || m_SendQueue.empty ()) return;
std::vector<std::shared_ptr<I2NPMessage> > msgs;
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
for (auto it: m_SendQueue)
if (!it->IsExpired (ts))
msgs.push_back (it);
else
it->Drop ();
m_SendQueue.clear ();
if (!msgs.empty ())
other->PostI2NPMessages (msgs);
}
size_t NTCP2Session::CreatePaddingBlock (size_t msgLen, uint8_t * buf, size_t len) size_t NTCP2Session::CreatePaddingBlock (size_t msgLen, uint8_t * buf, size_t len)
{ {
if (len < 3) return 0; if (len < 3) return 0;
len -= 3; len -= 3;
if (msgLen < 256) msgLen = 256; // for short message padding should not be always zero if (msgLen < 256) msgLen = 256; // for short message padding should not be always zero
size_t paddingSize = (msgLen*NTCP2_MAX_PADDING_RATIO)/100; size_t paddingSize = (msgLen*NTCP2_MAX_PADDING_RATIO)/100;
if (msgLen + paddingSize + 3 > NTCP2_UNENCRYPTED_FRAME_MAX_SIZE) if (msgLen + paddingSize + 3 > NTCP2_UNENCRYPTED_FRAME_MAX_SIZE) paddingSize = NTCP2_UNENCRYPTED_FRAME_MAX_SIZE - msgLen -3;
{
int l = (int)NTCP2_UNENCRYPTED_FRAME_MAX_SIZE - msgLen -3;
if (l <= 0) return 0;
paddingSize = l;
}
if (paddingSize > len) paddingSize = len; if (paddingSize > len) paddingSize = len;
if (paddingSize) if (paddingSize)
{ {
@ -1206,7 +1156,7 @@ namespace transport
RAND_bytes ((uint8_t *)m_PaddingSizes, sizeof (m_PaddingSizes)); RAND_bytes ((uint8_t *)m_PaddingSizes, sizeof (m_PaddingSizes));
m_NextPaddingSize = 0; m_NextPaddingSize = 0;
} }
paddingSize = m_PaddingSizes[m_NextPaddingSize++] % (paddingSize + 1); paddingSize = m_PaddingSizes[m_NextPaddingSize++] % paddingSize;
} }
buf[0] = eNTCP2BlkPadding; // blk buf[0] = eNTCP2BlkPadding; // blk
htobe16buf (buf + 1, paddingSize); // size htobe16buf (buf + 1, paddingSize); // size
@ -1217,8 +1167,7 @@ namespace transport
void NTCP2Session::SendRouterInfo () void NTCP2Session::SendRouterInfo ()
{ {
if (!IsEstablished ()) return; if (!IsEstablished ()) return;
auto riBuffer = i2p::context.CopyRouterInfoBuffer (); auto riLen = i2p::context.GetRouterInfo ().GetBufferLen ();
auto riLen = riBuffer->GetBufferLen ();
size_t payloadLen = riLen + 3 + 1 + 7; // 3 bytes block header + 1 byte RI flag + 7 bytes DateTime size_t payloadLen = riLen + 3 + 1 + 7; // 3 bytes block header + 1 byte RI flag + 7 bytes DateTime
m_NextSendBuffer = new uint8_t[payloadLen + 16 + 2 + 64]; // up to 64 bytes padding m_NextSendBuffer = new uint8_t[payloadLen + 16 + 2 + 64]; // up to 64 bytes padding
// DateTime block // DateTime block
@ -1229,7 +1178,7 @@ namespace transport
m_NextSendBuffer[9] = eNTCP2BlkRouterInfo; m_NextSendBuffer[9] = eNTCP2BlkRouterInfo;
htobe16buf (m_NextSendBuffer + 10, riLen + 1); // size htobe16buf (m_NextSendBuffer + 10, riLen + 1); // size
m_NextSendBuffer[12] = 0; // flag m_NextSendBuffer[12] = 0; // flag
memcpy (m_NextSendBuffer + 13, riBuffer->data (), riLen); // TODO: eliminate extra copy memcpy (m_NextSendBuffer + 13, i2p::context.GetRouterInfo ().GetBuffer (), riLen);
// padding block // padding block
auto paddingSize = CreatePaddingBlock (payloadLen, m_NextSendBuffer + 2 + payloadLen, 64); auto paddingSize = CreatePaddingBlock (payloadLen, m_NextSendBuffer + 2 + payloadLen, 64);
payloadLen += paddingSize; payloadLen += paddingSize;
@ -1272,14 +1221,9 @@ namespace transport
void NTCP2Session::PostI2NPMessages (std::vector<std::shared_ptr<I2NPMessage> > msgs) void NTCP2Session::PostI2NPMessages (std::vector<std::shared_ptr<I2NPMessage> > msgs)
{ {
if (m_IsTerminated) return; if (m_IsTerminated) return;
bool isSemiFull = m_SendQueue.size () > NTCP2_MAX_OUTGOING_QUEUE_SIZE/2;
for (auto it: msgs) for (auto it: msgs)
if (isSemiFull && it->onDrop) m_SendQueue.push_back (std::move (it));
it->Drop (); // drop earlier because we can handle it if (!m_IsSending)
else
m_SendQueue.push_back (std::move (it));
if (!m_IsSending && m_IsEstablished)
SendQueue (); SendQueue ();
else if (m_SendQueue.size () > NTCP2_MAX_OUTGOING_QUEUE_SIZE) else if (m_SendQueue.size () > NTCP2_MAX_OUTGOING_QUEUE_SIZE)
{ {
@ -1287,12 +1231,12 @@ namespace transport
GetIdentHashBase64(), " exceeds ", NTCP2_MAX_OUTGOING_QUEUE_SIZE); GetIdentHashBase64(), " exceeds ", NTCP2_MAX_OUTGOING_QUEUE_SIZE);
Terminate (); Terminate ();
} }
SetSendQueueSize (m_SendQueue.size ()); m_SendQueueSize = m_SendQueue.size ();
} }
void NTCP2Session::SendLocalRouterInfo (bool update) void NTCP2Session::SendLocalRouterInfo (bool update)
{ {
if (update || !IsOutgoing ()) // we send it in SessionConfirmed for outgoing session if (update || !IsOutgoing ()) // we send it in SessionConfirmed for ougoing session
m_Server.GetService ().post (std::bind (&NTCP2Session::SendRouterInfo, shared_from_this ())); m_Server.GetService ().post (std::bind (&NTCP2Session::SendRouterInfo, shared_from_this ()));
} }
@ -1442,7 +1386,6 @@ namespace transport
{ {
// replace by new session // replace by new session
auto s = it->second; auto s = it->second;
s->MoveSendQueue (session);
m_NTCP2Sessions.erase (it); m_NTCP2Sessions.erase (it);
s->Terminate (); s->Terminate ();
} }
@ -1459,11 +1402,7 @@ namespace transport
void NTCP2Server::RemoveNTCP2Session (std::shared_ptr<NTCP2Session> session) void NTCP2Server::RemoveNTCP2Session (std::shared_ptr<NTCP2Session> session)
{ {
if (session && session->GetRemoteIdentity ()) if (session && session->GetRemoteIdentity ())
{ m_NTCP2Sessions.erase (session->GetRemoteIdentity ()->GetIdentHash ());
auto it = m_NTCP2Sessions.find (session->GetRemoteIdentity ()->GetIdentHash ());
if (it != m_NTCP2Sessions.end () && it->second == session)
m_NTCP2Sessions.erase (it);
}
} }
std::shared_ptr<NTCP2Session> NTCP2Server::FindNTCP2Session (const i2p::data::IdentHash& ident) std::shared_ptr<NTCP2Session> NTCP2Server::FindNTCP2Session (const i2p::data::IdentHash& ident)
@ -1553,7 +1492,7 @@ namespace transport
if (!ec) if (!ec)
{ {
LogPrint (eLogDebug, "NTCP2: Connected from ", ep); LogPrint (eLogDebug, "NTCP2: Connected from ", ep);
if (!i2p::transport::transports.IsInReservedRange(ep.address ())) if (!i2p::util::net::IsInReservedRange(ep.address ()))
{ {
if (m_PendingIncomingSessions.emplace (ep.address (), conn).second) if (m_PendingIncomingSessions.emplace (ep.address (), conn).second)
{ {
@ -1600,7 +1539,7 @@ namespace transport
if (!ec) if (!ec)
{ {
LogPrint (eLogDebug, "NTCP2: Connected from ", ep); LogPrint (eLogDebug, "NTCP2: Connected from ", ep);
if (!i2p::transport::transports.IsInReservedRange(ep.address ()) || if (!i2p::util::net::IsInReservedRange(ep.address ()) ||
i2p::util::net::IsYggdrasilAddress (ep.address ())) i2p::util::net::IsYggdrasilAddress (ep.address ()))
{ {
if (m_PendingIncomingSessions.emplace (ep.address (), conn).second) if (m_PendingIncomingSessions.emplace (ep.address (), conn).second)
@ -1748,18 +1687,47 @@ namespace transport
case eSocksProxy: case eSocksProxy:
{ {
// TODO: support username/password auth etc // TODO: support username/password auth etc
Socks5Handshake (conn->GetSocket(), conn->GetRemoteEndpoint (), static const uint8_t buff[3] = {SOCKS5_VER, 0x01, 0x00};
[conn, timer](const boost::system::error_code& ec) boost::asio::async_write(conn->GetSocket(), boost::asio::buffer(buff, 3), boost::asio::transfer_all(),
{ [] (const boost::system::error_code & ec, std::size_t transferred)
timer->cancel(); {
if (!ec) (void) transferred;
conn->ClientLogin(); if(ec)
else {
LogPrint(eLogWarning, "NTCP2: SOCKS5 write error ", ec.message());
}
});
auto readbuff = std::make_shared<std::vector<uint8_t> >(2);
boost::asio::async_read(conn->GetSocket(), boost::asio::buffer(readbuff->data (), 2),
[this, readbuff, timer, conn](const boost::system::error_code & ec, std::size_t transferred)
{
if(ec)
{ {
LogPrint(eLogError, "NTCP2: SOCKS proxy handshake error ", ec.message()); LogPrint(eLogError, "NTCP2: SOCKS5 read error ", ec.message());
conn->Terminate(); timer->cancel();
} conn->Terminate();
}); return;
}
else if(transferred == 2)
{
if((*readbuff)[1] == 0x00)
{
AfterSocksHandshake(conn, timer);
return;
}
else if ((*readbuff)[1] == 0xff)
{
LogPrint(eLogError, "NTCP2: SOCKS5 proxy rejected authentication");
timer->cancel();
conn->Terminate();
return;
}
LogPrint(eLogError, "NTCP2:", (int)(*readbuff)[1]);
}
LogPrint(eLogError, "NTCP2: SOCKS5 server gave invalid response");
timer->cancel();
conn->Terminate();
});
break; break;
} }
case eHTTPProxy: case eHTTPProxy:
@ -1827,6 +1795,71 @@ namespace transport
} }
} }
void NTCP2Server::AfterSocksHandshake(std::shared_ptr<NTCP2Session> conn, std::shared_ptr<boost::asio::deadline_timer> timer)
{
// build request
size_t sz = 6; // header + port
auto buff = std::make_shared<std::vector<int8_t> >(256);
auto readbuff = std::make_shared<std::vector<int8_t> >(256);
(*buff)[0] = SOCKS5_VER;
(*buff)[1] = SOCKS5_CMD_CONNECT;
(*buff)[2] = 0x00;
auto& ep = conn->GetRemoteEndpoint ();
if(ep.address ().is_v4 ())
{
(*buff)[3] = SOCKS5_ATYP_IPV4;
auto addrbytes = ep.address ().to_v4().to_bytes();
sz += 4;
memcpy(buff->data () + 4, addrbytes.data(), 4);
}
else if (ep.address ().is_v6 ())
{
(*buff)[3] = SOCKS5_ATYP_IPV6;
auto addrbytes = ep.address ().to_v6().to_bytes();
sz += 16;
memcpy(buff->data () + 4, addrbytes.data(), 16);
}
else
{
// We mustn't really fall here because all connections are made to IP addresses
LogPrint(eLogError, "NTCP2: Tried to connect to unexpected address via proxy");
return;
}
htobe16buf(buff->data () + sz - 2, ep.port ());
boost::asio::async_write(conn->GetSocket(), boost::asio::buffer(buff->data (), sz), boost::asio::transfer_all(),
[buff](const boost::system::error_code & ec, std::size_t written)
{
if(ec)
{
LogPrint(eLogError, "NTCP2: Failed to write handshake to socks proxy ", ec.message());
return;
}
});
boost::asio::async_read(conn->GetSocket(), boost::asio::buffer(readbuff->data (), SOCKS5_UDP_IPV4_REQUEST_HEADER_SIZE), // read min reply size
boost::asio::transfer_all(),
[timer, conn, readbuff](const boost::system::error_code & e, std::size_t transferred)
{
if (e)
LogPrint(eLogError, "NTCP2: SOCKS proxy read error ", e.message());
else if (!(*readbuff)[1]) // succeeded
{
boost::system::error_code ec;
size_t moreBytes = conn->GetSocket ().available(ec);
if (moreBytes) // read remaining portion of reply if ipv6 received
boost::asio::read (conn->GetSocket (), boost::asio::buffer(readbuff->data (), moreBytes), boost::asio::transfer_all (), ec);
timer->cancel();
conn->ClientLogin();
return;
}
else
LogPrint(eLogError, "NTCP2: Proxy reply error ", (int)(*readbuff)[1]);
timer->cancel();
conn->Terminate();
});
}
void NTCP2Server::SetLocalAddress (const boost::asio::ip::address& localAddress) void NTCP2Server::SetLocalAddress (const boost::asio::ip::address& localAddress)
{ {
auto addr = std::make_shared<boost::asio::ip::tcp::endpoint>(boost::asio::ip::tcp::endpoint(localAddress, 0)); auto addr = std::make_shared<boost::asio::ip::tcp::endpoint>(boost::asio::ip::tcp::endpoint(localAddress, 0));

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2024, The PurpleI2P Project * Copyright (c) 2013-2022, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@ -28,7 +28,6 @@ namespace transport
{ {
const size_t NTCP2_UNENCRYPTED_FRAME_MAX_SIZE = 65519; const size_t NTCP2_UNENCRYPTED_FRAME_MAX_SIZE = 65519;
const size_t NTCP2_SEND_AFTER_FRAME_SIZE = 16386; // send frame when exceeds this size
const size_t NTCP2_SESSION_REQUEST_MAX_SIZE = 287; const size_t NTCP2_SESSION_REQUEST_MAX_SIZE = 287;
const size_t NTCP2_SESSION_CREATED_MAX_SIZE = 287; const size_t NTCP2_SESSION_CREATED_MAX_SIZE = 287;
const int NTCP2_MAX_PADDING_RATIO = 6; // in % const int NTCP2_MAX_PADDING_RATIO = 6; // in %
@ -151,8 +150,7 @@ namespace transport
void SendLocalRouterInfo (bool update) override; // after handshake or by update void SendLocalRouterInfo (bool update) override; // after handshake or by update
void SendI2NPMessages (const std::vector<std::shared_ptr<I2NPMessage> >& msgs) override; void SendI2NPMessages (const std::vector<std::shared_ptr<I2NPMessage> >& msgs) override;
void MoveSendQueue (std::shared_ptr<NTCP2Session> other);
private: private:
void Established (); void Established ();
@ -268,7 +266,8 @@ namespace transport
void HandleConnect (const boost::system::error_code& ecode, std::shared_ptr<NTCP2Session> conn, std::shared_ptr<boost::asio::deadline_timer> timer); void HandleConnect (const boost::system::error_code& ecode, std::shared_ptr<NTCP2Session> conn, std::shared_ptr<boost::asio::deadline_timer> timer);
void HandleProxyConnect(const boost::system::error_code& ecode, std::shared_ptr<NTCP2Session> conn, std::shared_ptr<boost::asio::deadline_timer> timer); void HandleProxyConnect(const boost::system::error_code& ecode, std::shared_ptr<NTCP2Session> conn, std::shared_ptr<boost::asio::deadline_timer> timer);
void AfterSocksHandshake(std::shared_ptr<NTCP2Session> conn, std::shared_ptr<boost::asio::deadline_timer> timer);
// timer // timer
void ScheduleTermination (); void ScheduleTermination ();
void HandleTerminationTimer (const boost::system::error_code& ecode); void HandleTerminationTimer (const boost::system::error_code& ecode);

File diff suppressed because it is too large Load Diff

@ -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 * This file is part of Purple i2pd project and licensed under BSD3
* *
@ -10,12 +10,11 @@
#define NETDB_H__ #define NETDB_H__
// this file is called NetDb.hpp to resolve conflict with libc's netdb.h on case insensitive fs // this file is called NetDb.hpp to resolve conflict with libc's netdb.h on case insensitive fs
#include <inttypes.h> #include <inttypes.h>
#include <unordered_set> #include <set>
#include <unordered_map> #include <unordered_map>
#include <string> #include <string>
#include <thread> #include <thread>
#include <mutex> #include <mutex>
#include <future>
#include "Base.h" #include "Base.h"
#include "Gzip.h" #include "Gzip.h"
@ -39,23 +38,15 @@ namespace data
{ {
const int NETDB_MIN_ROUTERS = 90; const int NETDB_MIN_ROUTERS = 90;
const int NETDB_MIN_FLOODFILLS = 5; const int NETDB_MIN_FLOODFILLS = 5;
const int NETDB_NUM_FLOODFILLS_THRESHOLD = 1000; const int NETDB_NUM_FLOODFILLS_THRESHOLD = 1500;
const int NETDB_NUM_ROUTERS_THRESHOLD = 4*NETDB_NUM_FLOODFILLS_THRESHOLD;
const int NETDB_TUNNEL_CREATION_RATE_THRESHOLD = 10; // in %
const int NETDB_CHECK_FOR_EXPIRATION_UPTIME = 600; // 10 minutes, in seconds
const int NETDB_FLOODFILL_EXPIRATION_TIMEOUT = 60 * 60; // 1 hour, in seconds const int NETDB_FLOODFILL_EXPIRATION_TIMEOUT = 60 * 60; // 1 hour, in seconds
const int NETDB_MIN_EXPIRATION_TIMEOUT = 90 * 60; // 1.5 hours const int NETDB_MIN_EXPIRATION_TIMEOUT = 90 * 60; // 1.5 hours
const int NETDB_MAX_EXPIRATION_TIMEOUT = 27 * 60 * 60; // 27 hours const int NETDB_MAX_EXPIRATION_TIMEOUT = 27 * 60 * 60; // 27 hours
const int NETDB_MAX_OFFLINE_EXPIRATION_TIMEOUT = 180; // in days const int NETDB_MAX_OFFLINE_EXPIRATION_TIMEOUT = 180; // in days
const int NETDB_EXPIRATION_TIMEOUT_THRESHOLD = 2*60; // 2 minutes const int NETDB_EXPIRATION_TIMEOUT_THRESHOLD = 2*60; // 2 minutes
const int NETDB_MIN_HIGHBANDWIDTH_VERSION = MAKE_VERSION_NUMBER(0, 9, 58); // 0.9.58 const int NETDB_MIN_HIGHBANDWIDTH_VERSION = MAKE_VERSION_NUMBER(0, 9, 51); // 0.9.51
const int NETDB_MIN_FLOODFILL_VERSION = MAKE_VERSION_NUMBER(0, 9, 51); // 0.9.51 const int NETDB_MIN_FLOODFILL_VERSION = MAKE_VERSION_NUMBER(0, 9, 51); // 0.9.51
const int NETDB_MIN_SHORT_TUNNEL_BUILD_VERSION = MAKE_VERSION_NUMBER(0, 9, 51); // 0.9.51 const int NETDB_MIN_SHORT_TUNNEL_BUILD_VERSION = MAKE_VERSION_NUMBER(0, 9, 51); // 0.9.51
const size_t NETDB_MAX_NUM_SEARCH_REPLY_PEER_HASHES = 16;
const size_t NETDB_MAX_EXPLORATORY_SELECTION_SIZE = 500;
const int NETDB_EXPLORATORY_SELECTION_UPDATE_INTERVAL = 82; // in seconds. for floodfill
const int NETDB_NEXT_DAY_ROUTER_INFO_THRESHOLD = 45; // in minutes
const int NETDB_NEXT_DAY_LEASESET_THRESHOLD = 10; // in minutes
/** function for visiting a leaseset stored in a floodfill */ /** function for visiting a leaseset stored in a floodfill */
typedef std::function<void(const IdentHash, std::shared_ptr<LeaseSet>)> LeaseSetVisitor; typedef std::function<void(const IdentHash, std::shared_ptr<LeaseSet>)> LeaseSetVisitor;
@ -85,22 +76,27 @@ namespace data
std::shared_ptr<RouterProfile> FindRouterProfile (const IdentHash& ident) const; std::shared_ptr<RouterProfile> FindRouterProfile (const IdentHash& ident) const;
void RequestDestination (const IdentHash& destination, RequestedDestination::RequestComplete requestComplete = nullptr, bool direct = true); void RequestDestination (const IdentHash& destination, RequestedDestination::RequestComplete requestComplete = nullptr, bool direct = true);
void RequestDestinationFrom (const IdentHash& destination, const IdentHash & from, bool exploritory, RequestedDestination::RequestComplete requestComplete = nullptr);
void HandleDatabaseStoreMsg (std::shared_ptr<const I2NPMessage> msg);
void HandleDatabaseSearchReplyMsg (std::shared_ptr<const I2NPMessage> msg);
void HandleDatabaseLookupMsg (std::shared_ptr<const I2NPMessage> msg);
void HandleNTCP2RouterInfoMsg (std::shared_ptr<const I2NPMessage> m);
std::shared_ptr<const RouterInfo> GetRandomRouter () const; std::shared_ptr<const RouterInfo> GetRandomRouter () const;
std::shared_ptr<const RouterInfo> GetRandomRouter (std::shared_ptr<const RouterInfo> compatibleWith, bool reverse, bool endpoint) const; std::shared_ptr<const RouterInfo> GetRandomRouter (std::shared_ptr<const RouterInfo> compatibleWith, bool reverse, bool endpoint) const;
std::shared_ptr<const RouterInfo> GetHighBandwidthRandomRouter (std::shared_ptr<const RouterInfo> compatibleWith, bool reverse, bool endpoint) const; std::shared_ptr<const RouterInfo> GetHighBandwidthRandomRouter (std::shared_ptr<const RouterInfo> compatibleWith, bool reverse, bool endpoint) const;
std::shared_ptr<const RouterInfo> GetRandomSSU2PeerTestRouter (bool v4, const std::unordered_set<IdentHash>& excluded) const; std::shared_ptr<const RouterInfo> GetRandomSSU2PeerTestRouter (bool v4, const std::set<IdentHash>& excluded) const;
std::shared_ptr<const RouterInfo> GetRandomSSU2Introducer (bool v4, const std::unordered_set<IdentHash>& excluded) const; std::shared_ptr<const RouterInfo> GetRandomSSU2Introducer (bool v4, const std::set<IdentHash>& excluded) const;
std::shared_ptr<const RouterInfo> GetClosestFloodfill (const IdentHash& destination, const std::unordered_set<IdentHash>& excluded, bool nextDay = false) const; std::shared_ptr<const RouterInfo> GetClosestFloodfill (const IdentHash& destination, const std::set<IdentHash>& excluded) const;
std::vector<IdentHash> GetClosestFloodfills (const IdentHash& destination, size_t num, std::vector<IdentHash> GetClosestFloodfills (const IdentHash& destination, size_t num,
std::unordered_set<IdentHash>& excluded, bool closeThanUsOnly = false) const; std::set<IdentHash>& excluded, bool closeThanUsOnly = false) const;
std::vector<IdentHash> GetExploratoryNonFloodfill (const IdentHash& destination, size_t num, const std::unordered_set<IdentHash>& excluded); std::shared_ptr<const RouterInfo> GetClosestNonFloodfill (const IdentHash& destination, const std::set<IdentHash>& excluded) const;
std::shared_ptr<const RouterInfo> GetRandomRouterInFamily (FamilyID fam) const; std::shared_ptr<const RouterInfo> GetRandomRouterInFamily (FamilyID fam) const;
void SetUnreachable (const IdentHash& ident, bool unreachable); void SetUnreachable (const IdentHash& ident, bool unreachable);
void ExcludeReachableTransports (const IdentHash& ident, RouterInfo::CompatibleTransports transports); void ExcludeReachableTransports (const IdentHash& ident, RouterInfo::CompatibleTransports transports);
void PostI2NPMsg (std::shared_ptr<const I2NPMessage> msg); void PostI2NPMsg (std::shared_ptr<const I2NPMessage> msg);
void PostDatabaseSearchReplyMsg (std::shared_ptr<const I2NPMessage> msg); // to NetdbReq thread
void Reseed (); void Reseed ();
Families& GetFamilies () { return m_Families; }; Families& GetFamilies () { return m_Families; };
@ -120,11 +116,7 @@ namespace data
size_t VisitRandomRouterInfos(RouterInfoFilter f, RouterInfoVisitor v, size_t n); size_t VisitRandomRouterInfos(RouterInfoFilter f, RouterInfoVisitor v, size_t n);
void ClearRouterInfos () { m_RouterInfos.clear (); }; void ClearRouterInfos () { m_RouterInfos.clear (); };
template<typename... TArgs> std::shared_ptr<RouterInfo::Buffer> NewRouterInfoBuffer () { return m_RouterInfoBuffersPool.AcquireSharedMt (); };
std::shared_ptr<RouterInfo::Buffer> NewRouterInfoBuffer (TArgs&&... args)
{
return m_RouterInfoBuffersPool.AcquireSharedMt (std::forward<TArgs>(args)...);
}
bool PopulateRouterInfoBuffer (std::shared_ptr<RouterInfo> r); bool PopulateRouterInfoBuffer (std::shared_ptr<RouterInfo> r);
std::shared_ptr<RouterInfo::Address> NewRouterInfoAddress () { return m_RouterInfoAddressesPool.AcquireSharedMt (); }; std::shared_ptr<RouterInfo::Address> NewRouterInfoAddress () { return m_RouterInfoAddressesPool.AcquireSharedMt (); };
boost::shared_ptr<RouterInfo::Addresses> NewRouterInfoAddresses () boost::shared_ptr<RouterInfo::Addresses> NewRouterInfoAddresses ()
@ -138,15 +130,16 @@ namespace data
std::shared_ptr<IdentityEx> NewIdentity (const uint8_t * buf, size_t len) { return m_IdentitiesPool.AcquireSharedMt (buf, len); }; std::shared_ptr<IdentityEx> NewIdentity (const uint8_t * buf, size_t len) { return m_IdentitiesPool.AcquireSharedMt (buf, len); };
std::shared_ptr<RouterProfile> NewRouterProfile () { return m_RouterProfilesPool.AcquireSharedMt (); }; std::shared_ptr<RouterProfile> NewRouterProfile () { return m_RouterProfilesPool.AcquireSharedMt (); };
uint32_t GetPublishReplyToken () const { return m_PublishReplyToken; };
private: private:
void Load (); void Load ();
bool LoadRouterInfo (const std::string& path, uint64_t ts); bool LoadRouterInfo (const std::string& path, uint64_t ts);
void SaveUpdated (); void SaveUpdated ();
void PersistRouters (std::list<std::pair<std::string, std::shared_ptr<RouterInfo::Buffer> > >&& update, void Run (); // exploratory thread
std::list<std::string>&& remove); void Explore (int numDestinations);
void Run (); void Flood (const IdentHash& ident, std::shared_ptr<I2NPMessage> floodMsg);
void Flood (const IdentHash& ident, std::shared_ptr<I2NPMessage> floodMsg, bool andNextDay = false);
void ManageRouterInfos (); void ManageRouterInfos ();
void ManageLeaseSets (); void ManageLeaseSets ();
void ManageRequests (); void ManageRequests ();
@ -159,10 +152,6 @@ namespace data
template<typename Filter> template<typename Filter>
std::shared_ptr<const RouterInfo> GetRandomRouter (Filter filter) const; std::shared_ptr<const RouterInfo> GetRandomRouter (Filter filter) const;
void HandleDatabaseStoreMsg (std::shared_ptr<const I2NPMessage> msg);
void HandleDatabaseLookupMsg (std::shared_ptr<const I2NPMessage> msg);
void HandleNTCP2RouterInfoMsg (std::shared_ptr<const I2NPMessage> m);
private: private:
mutable std::mutex m_LeaseSetsMutex; mutable std::mutex m_LeaseSetsMutex;
@ -181,13 +170,16 @@ namespace data
Families m_Families; Families m_Families;
i2p::fs::HashedStorage m_Storage; i2p::fs::HashedStorage m_Storage;
std::shared_ptr<NetDbRequests> m_Requests; friend class NetDbRequests;
NetDbRequests m_Requests;
bool m_PersistProfiles; bool m_PersistProfiles;
std::future<void> m_SavingProfiles, m_DeletingProfiles, m_PersistingRouters;
std::vector<std::shared_ptr<const RouterInfo> > m_ExploratorySelection; /** router info we are bootstrapping from or nullptr if we are not currently doing that*/
uint64_t m_LastExploratorySelectionUpdateTime; // in monotonic seconds std::shared_ptr<RouterInfo> m_FloodfillBootstrap;
std::set<IdentHash> m_PublishExcluded;
uint32_t m_PublishReplyToken = 0;
i2p::util::MemoryPoolMt<RouterInfo::Buffer> m_RouterInfoBuffersPool; i2p::util::MemoryPoolMt<RouterInfo::Buffer> m_RouterInfoBuffersPool;
i2p::util::MemoryPoolMt<RouterInfo::Address> m_RouterInfoAddressesPool; i2p::util::MemoryPoolMt<RouterInfo::Address> m_RouterInfoAddressesPool;

@ -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 * This file is part of Purple i2pd project and licensed under BSD3
* *
@ -10,28 +10,12 @@
#include "I2NPProtocol.h" #include "I2NPProtocol.h"
#include "Transports.h" #include "Transports.h"
#include "NetDb.hpp" #include "NetDb.hpp"
#include "ECIESX25519AEADRatchetSession.h"
#include "RouterContext.h"
#include "Timestamp.h"
#include "NetDbRequests.h" #include "NetDbRequests.h"
namespace i2p namespace i2p
{ {
namespace data namespace data
{ {
RequestedDestination::RequestedDestination (const IdentHash& destination, bool isExploratory, bool direct):
m_Destination (destination), m_IsExploratory (isExploratory), m_IsDirect (direct), m_IsActive (true),
m_CreationTime (i2p::util::GetSecondsSinceEpoch ()), m_LastRequestTime (0), m_NumAttempts (0)
{
if (i2p::context.IsFloodfill ())
m_ExcludedPeers.insert (i2p::context.GetIdentHash ()); // exclude self if floodfill
}
RequestedDestination::~RequestedDestination ()
{
InvokeRequestComplete (nullptr);
}
std::shared_ptr<I2NPMessage> RequestedDestination::CreateRequestMessage (std::shared_ptr<const RouterInfo> router, std::shared_ptr<I2NPMessage> RequestedDestination::CreateRequestMessage (std::shared_ptr<const RouterInfo> router,
std::shared_ptr<const i2p::tunnel::InboundTunnel> replyTunnel) std::shared_ptr<const i2p::tunnel::InboundTunnel> replyTunnel)
{ {
@ -44,8 +28,7 @@ namespace data
msg = i2p::CreateRouterInfoDatabaseLookupMsg(m_Destination, i2p::context.GetIdentHash(), 0, m_IsExploratory, &m_ExcludedPeers); msg = i2p::CreateRouterInfoDatabaseLookupMsg(m_Destination, i2p::context.GetIdentHash(), 0, m_IsExploratory, &m_ExcludedPeers);
if(router) if(router)
m_ExcludedPeers.insert (router->GetIdentHash ()); m_ExcludedPeers.insert (router->GetIdentHash ());
m_LastRequestTime = i2p::util::GetSecondsSinceEpoch (); m_CreationTime = i2p::util::GetSecondsSinceEpoch ();
m_NumAttempts++;
return msg; return msg;
} }
@ -54,154 +37,80 @@ namespace data
auto msg = i2p::CreateRouterInfoDatabaseLookupMsg (m_Destination, auto msg = i2p::CreateRouterInfoDatabaseLookupMsg (m_Destination,
i2p::context.GetRouterInfo ().GetIdentHash () , 0, false, &m_ExcludedPeers); i2p::context.GetRouterInfo ().GetIdentHash () , 0, false, &m_ExcludedPeers);
m_ExcludedPeers.insert (floodfill); m_ExcludedPeers.insert (floodfill);
m_NumAttempts++; m_CreationTime = i2p::util::GetSecondsSinceEpoch ();
m_LastRequestTime = i2p::util::GetSecondsSinceEpoch ();
return msg; return msg;
} }
bool RequestedDestination::IsExcluded (const IdentHash& ident) const
{
return m_ExcludedPeers.count (ident);
}
void RequestedDestination::ClearExcludedPeers () void RequestedDestination::ClearExcludedPeers ()
{ {
m_ExcludedPeers.clear (); m_ExcludedPeers.clear ();
} }
void RequestedDestination::InvokeRequestComplete (std::shared_ptr<RouterInfo> r)
{
if (!m_RequestComplete.empty ())
{
for (auto it: m_RequestComplete)
if (it != nullptr) it (r);
m_RequestComplete.clear ();
}
}
void RequestedDestination::Success (std::shared_ptr<RouterInfo> r) void RequestedDestination::Success (std::shared_ptr<RouterInfo> r)
{ {
if (m_IsActive) if (m_RequestComplete)
{ {
m_IsActive = false; m_RequestComplete (r);
InvokeRequestComplete (r); m_RequestComplete = nullptr;
} }
} }
void RequestedDestination::Fail () void RequestedDestination::Fail ()
{ {
if (m_IsActive) if (m_RequestComplete)
{ {
m_IsActive = false; m_RequestComplete (nullptr);
InvokeRequestComplete (nullptr); m_RequestComplete = nullptr;
} }
} }
NetDbRequests::NetDbRequests ():
RunnableServiceWithWork ("NetDbReq"),
m_ManageRequestsTimer (GetIOService ()), m_ExploratoryTimer (GetIOService ()),
m_CleanupTimer (GetIOService ()), m_DiscoveredRoutersTimer (GetIOService ()),
m_Rng(i2p::util::GetMonotonicMicroseconds () % 1000000LL)
{
}
NetDbRequests::~NetDbRequests ()
{
Stop ();
}
void NetDbRequests::Start () void NetDbRequests::Start ()
{ {
if (!IsRunning ())
{
StartIOService ();
ScheduleManageRequests ();
ScheduleCleanup ();
if (!i2p::context.IsHidden ())
ScheduleExploratory (EXPLORATORY_REQUEST_INTERVAL);
}
} }
void NetDbRequests::Stop () void NetDbRequests::Stop ()
{ {
if (IsRunning ()) m_RequestedDestinations.clear ();
{
m_ManageRequestsTimer.cancel ();
m_ExploratoryTimer.cancel ();
m_CleanupTimer.cancel ();
StopIOService ();
m_RequestedDestinations.clear ();
m_RequestedDestinationsPool.CleanUpMt ();
}
} }
void NetDbRequests::ScheduleCleanup ()
{ std::shared_ptr<RequestedDestination> NetDbRequests::CreateRequest (const IdentHash& destination, bool isExploratory, RequestedDestination::RequestComplete requestComplete)
m_CleanupTimer.expires_from_now (boost::posix_time::seconds(REQUESTED_DESTINATIONS_POOL_CLEANUP_INTERVAL));
m_CleanupTimer.async_wait (std::bind (&NetDbRequests::HandleCleanupTimer,
this, std::placeholders::_1));
}
void NetDbRequests::HandleCleanupTimer (const boost::system::error_code& ecode)
{
if (ecode != boost::asio::error::operation_aborted)
{
m_RequestedDestinationsPool.CleanUpMt ();
ScheduleCleanup ();
}
}
std::shared_ptr<RequestedDestination> NetDbRequests::CreateRequest (const IdentHash& destination,
bool isExploratory, bool direct, RequestedDestination::RequestComplete requestComplete)
{ {
// request RouterInfo directly // request RouterInfo directly
auto dest = m_RequestedDestinationsPool.AcquireSharedMt (destination, isExploratory, direct); auto dest = std::make_shared<RequestedDestination> (destination, isExploratory);
if (requestComplete) dest->SetRequestComplete (requestComplete);
dest->AddRequestComplete (requestComplete); {
std::unique_lock<std::mutex> l(m_RequestedDestinationsMutex);
auto ret = m_RequestedDestinations.emplace (destination, dest); if (!m_RequestedDestinations.insert (std::make_pair (destination, dest)).second) // not inserted
if (!ret.second) // not inserted return nullptr;
{ }
dest->ResetRequestComplete (); // don't call requestComplete in destructor
dest = ret.first->second; // existing one
if (requestComplete)
{
if (dest->IsActive ())
dest->AddRequestComplete (requestComplete);
else
requestComplete (nullptr);
}
return nullptr;
}
return dest; return dest;
} }
void NetDbRequests::RequestComplete (const IdentHash& ident, std::shared_ptr<RouterInfo> r) void NetDbRequests::RequestComplete (const IdentHash& ident, std::shared_ptr<RouterInfo> r)
{ {
GetIOService ().post ([this, ident, r]() std::shared_ptr<RequestedDestination> request;
{ {
std::shared_ptr<RequestedDestination> request; std::unique_lock<std::mutex> l(m_RequestedDestinationsMutex);
auto it = m_RequestedDestinations.find (ident); auto it = m_RequestedDestinations.find (ident);
if (it != m_RequestedDestinations.end ()) if (it != m_RequestedDestinations.end ())
{ {
request = it->second; request = it->second;
if (request->IsExploratory ()) m_RequestedDestinations.erase (it);
m_RequestedDestinations.erase (it); }
// otherwise cache for a while }
} if (request)
if (request) {
{ if (r)
if (r) request->Success (r);
request->Success (r); else
else request->Fail ();
request->Fail (); }
}
});
} }
std::shared_ptr<RequestedDestination> NetDbRequests::FindRequest (const IdentHash& ident) const std::shared_ptr<RequestedDestination> NetDbRequests::FindRequest (const IdentHash& ident) const
{ {
std::unique_lock<std::mutex> l(m_RequestedDestinationsMutex);
auto it = m_RequestedDestinations.find (ident); auto it = m_RequestedDestinations.find (ident);
if (it != m_RequestedDestinations.end ()) if (it != m_RequestedDestinations.end ())
return it->second; return it->second;
@ -211,345 +120,49 @@ namespace data
void NetDbRequests::ManageRequests () void NetDbRequests::ManageRequests ()
{ {
uint64_t ts = i2p::util::GetSecondsSinceEpoch (); uint64_t ts = i2p::util::GetSecondsSinceEpoch ();
std::unique_lock<std::mutex> l(m_RequestedDestinationsMutex);
for (auto it = m_RequestedDestinations.begin (); it != m_RequestedDestinations.end ();) for (auto it = m_RequestedDestinations.begin (); it != m_RequestedDestinations.end ();)
{ {
auto& dest = it->second; auto& dest = it->second;
if (dest->IsActive () || ts < dest->GetCreationTime () + REQUEST_CACHE_TIME) bool done = false;
{ if (ts < dest->GetCreationTime () + 60) // request is worthless after 1 minute
if (!dest->IsExploratory ()) {
{ if (ts > dest->GetCreationTime () + 5) // no response for 5 seconds
// regular request
bool done = false;
if (ts < dest->GetCreationTime () + MAX_REQUEST_TIME)
{
if (ts > dest->GetLastRequestTime () + MIN_REQUEST_TIME) // try next floodfill if no response after min interval
done = !SendNextRequest (dest);
}
else // request is expired
done = true;
if (done)
dest->Fail ();
it++;
}
else
{
// exploratory
if (ts >= dest->GetCreationTime () + MAX_EXPLORATORY_REQUEST_TIME)
{
dest->Fail ();
it = m_RequestedDestinations.erase (it); // delete expired exploratory request right a way
}
else
it++;
}
}
else
it = m_RequestedDestinations.erase (it);
}
}
bool NetDbRequests::SendNextRequest (std::shared_ptr<RequestedDestination> dest)
{
if (!dest || !dest->IsActive ()) return false;
bool ret = true;
auto count = dest->GetNumAttempts ();
if (!dest->IsExploratory () && count < MAX_NUM_REQUEST_ATTEMPTS)
{
auto nextFloodfill = netdb.GetClosestFloodfill (dest->GetDestination (), dest->GetExcludedPeers ());
if (nextFloodfill)
{
bool direct = dest->IsDirect ();
if (direct && !nextFloodfill->IsReachableFrom (i2p::context.GetRouterInfo ()) &&
!i2p::transport::transports.IsConnected (nextFloodfill->GetIdentHash ()))
direct = false; // floodfill can't be reached directly
auto s = shared_from_this ();
auto onDrop = [s, dest]()
{
if (dest->IsActive ())
{
s->GetIOService ().post ([s, dest]()
{
if (dest->IsActive ()) s->SendNextRequest (dest);
});
}
};
if (direct)
{ {
if (CheckLogLevel (eLogDebug)) auto count = dest->GetExcludedPeers ().size ();
LogPrint (eLogDebug, "NetDbReq: Try ", dest->GetDestination ().ToBase64 (), " at ", count, " floodfill ", nextFloodfill->GetIdentHash ().ToBase64 (), " directly"); if (!dest->IsExploratory () && count < 7)
auto msg = dest->CreateRequestMessage (nextFloodfill->GetIdentHash ()); {
msg->onDrop = onDrop; auto pool = i2p::tunnel::tunnels.GetExploratoryPool ();
i2p::transport::transports.SendMessage (nextFloodfill->GetIdentHash (), msg);
}
else
{
auto pool = i2p::tunnel::tunnels.GetExploratoryPool ();
if (pool)
{
auto outbound = pool->GetNextOutboundTunnel (); auto outbound = pool->GetNextOutboundTunnel ();
auto inbound = pool->GetNextInboundTunnel (); auto inbound = pool->GetNextInboundTunnel ();
auto nextFloodfill = netdb.GetClosestFloodfill (dest->GetDestination (), dest->GetExcludedPeers ());
if (nextFloodfill && outbound && inbound) if (nextFloodfill && outbound && inbound)
{
if (CheckLogLevel (eLogDebug))
LogPrint (eLogDebug, "NetDbReq: Try ", dest->GetDestination ().ToBase64 (), " at ", count, " floodfill ", nextFloodfill->GetIdentHash ().ToBase64 (), " through tunnels");
auto msg = dest->CreateRequestMessage (nextFloodfill, inbound);
msg->onDrop = onDrop;
outbound->SendTunnelDataMsgTo (nextFloodfill->GetIdentHash (), 0, outbound->SendTunnelDataMsgTo (nextFloodfill->GetIdentHash (), 0,
i2p::garlic::WrapECIESX25519MessageForRouter (msg, nextFloodfill->GetIdentity ()->GetEncryptionPublicKey ())); dest->CreateRequestMessage (nextFloodfill, inbound));
}
else else
{ {
ret = false; done = true;
if (!inbound) LogPrint (eLogWarning, "NetDbReq: No inbound tunnels"); if (!inbound) LogPrint (eLogWarning, "NetDbReq: No inbound tunnels");
if (!outbound) LogPrint (eLogWarning, "NetDbReq: No outbound tunnels"); if (!outbound) LogPrint (eLogWarning, "NetDbReq: No outbound tunnels");
if (!nextFloodfill) LogPrint (eLogWarning, "NetDbReq: No more floodfills");
} }
} }
else else
{ {
ret = false; if (!dest->IsExploratory ())
LogPrint (eLogWarning, "NetDbReq: Exploratory pool is not ready"); LogPrint (eLogWarning, "NetDbReq: ", dest->GetDestination ().ToBase64 (), " not found after 7 attempts");
} done = true;
} }
}
else
{
ret = false;
LogPrint (eLogWarning, "NetDbReq: No more floodfills for ", dest->GetDestination ().ToBase64 (), " after ", count, "attempts");
}
}
else
{
if (!dest->IsExploratory ())
LogPrint (eLogWarning, "NetDbReq: ", dest->GetDestination ().ToBase64 (), " not found after ", MAX_NUM_REQUEST_ATTEMPTS," attempts");
ret = false;
}
return ret;
}
void NetDbRequests::ScheduleManageRequests ()
{
m_ManageRequestsTimer.expires_from_now (boost::posix_time::seconds(MANAGE_REQUESTS_INTERVAL));
m_ManageRequestsTimer.async_wait (std::bind (&NetDbRequests::HandleManageRequestsTimer,
this, std::placeholders::_1));
}
void NetDbRequests::HandleManageRequestsTimer (const boost::system::error_code& ecode)
{
if (ecode != boost::asio::error::operation_aborted)
{
if (i2p::tunnel::tunnels.GetExploratoryPool ()) // expolratory pool is ready?
ManageRequests ();
ScheduleManageRequests ();
}
}
void NetDbRequests::PostDatabaseSearchReplyMsg (std::shared_ptr<const I2NPMessage> msg)
{
GetIOService ().post ([this, msg]()
{
HandleDatabaseSearchReplyMsg (msg);
});
}
void NetDbRequests::HandleDatabaseSearchReplyMsg (std::shared_ptr<const I2NPMessage> msg)
{
const uint8_t * buf = msg->GetPayload ();
char key[48];
int l = i2p::data::ByteStreamToBase64 (buf, 32, key, 48);
key[l] = 0;
size_t num = buf[32]; // num
LogPrint (eLogDebug, "NetDbReq: DatabaseSearchReply for ", key, " num=", num);
IdentHash ident (buf);
bool isExploratory = false;
auto dest = FindRequest (ident);
if (dest && dest->IsActive ())
{
isExploratory = dest->IsExploratory ();
if (!isExploratory && (num > 0 || dest->GetNumAttempts () < 3)) // before 3-rd attempt might be just bad luck
{
// try to send next requests
if (!SendNextRequest (dest))
RequestComplete (ident, nullptr);
}
else
// no more requests for destination possible. delete it
RequestComplete (ident, nullptr);
}
else /*if (!m_FloodfillBootstrap)*/
{
LogPrint (eLogInfo, "NetDbReq: Unsolicited or late database search reply for ", key);
return;
}
// try responses
if (num > NETDB_MAX_NUM_SEARCH_REPLY_PEER_HASHES)
{
LogPrint (eLogWarning, "NetDbReq: Too many peer hashes ", num, " in database search reply, Reduced to ", NETDB_MAX_NUM_SEARCH_REPLY_PEER_HASHES);
num = NETDB_MAX_NUM_SEARCH_REPLY_PEER_HASHES;
}
if (isExploratory && !m_DiscoveredRouterHashes.empty ())
{
// request outstanding routers
for (auto it: m_DiscoveredRouterHashes)
RequestRouter (it);
m_DiscoveredRouterHashes.clear ();
m_DiscoveredRoutersTimer.cancel ();
}
for (size_t i = 0; i < num; i++)
{
IdentHash router (buf + 33 + i*32);
if (CheckLogLevel (eLogDebug))
LogPrint (eLogDebug, "NetDbReq: ", i, ": ", router.ToBase64 ());
if (isExploratory)
// postpone request
m_DiscoveredRouterHashes.push_back (router);
else
// send request right a way
RequestRouter (router);
}
if (isExploratory && !m_DiscoveredRouterHashes.empty ())
ScheduleDiscoveredRoutersRequest ();
}
void NetDbRequests::RequestRouter (const IdentHash& router)
{
auto r = netdb.FindRouter (router);
if (!r || i2p::util::GetMillisecondsSinceEpoch () > r->GetTimestamp () + 3600*1000LL)
{
// router with ident not found or too old (1 hour)
LogPrint (eLogDebug, "NetDbReq: Found new/outdated router. Requesting RouterInfo...");
if (!IsRouterBanned (router))
RequestDestination (router, nullptr, true);
else
LogPrint (eLogDebug, "NetDbReq: Router ", router.ToBase64 (), " is banned. Skipped");
}
else
LogPrint (eLogDebug, "NetDbReq: [:|||:]");
}
void NetDbRequests::PostRequestDestination (const IdentHash& destination,
const RequestedDestination::RequestComplete& requestComplete, bool direct)
{
GetIOService ().post ([this, destination, requestComplete, direct]()
{
RequestDestination (destination, requestComplete, direct);
});
}
void NetDbRequests::RequestDestination (const IdentHash& destination, const RequestedDestination::RequestComplete& requestComplete, bool direct)
{
auto dest = CreateRequest (destination, false, direct, requestComplete); // non-exploratory
if (dest)
{
if (!SendNextRequest (dest))
RequestComplete (destination, nullptr);
}
else
LogPrint (eLogWarning, "NetDbReq: Destination ", destination.ToBase64(), " is requested already or cached");
}
void NetDbRequests::Explore (int numDestinations)
{
// new requests
auto exploratoryPool = i2p::tunnel::tunnels.GetExploratoryPool ();
auto outbound = exploratoryPool ? exploratoryPool->GetNextOutboundTunnel () : nullptr;
auto inbound = exploratoryPool ? exploratoryPool->GetNextInboundTunnel () : nullptr;
bool throughTunnels = outbound && inbound;
uint8_t randomHash[32];
std::vector<i2p::tunnel::TunnelMessageBlock> msgs;
LogPrint (eLogInfo, "NetDbReq: Exploring new ", numDestinations, " routers ...");
for (int i = 0; i < numDestinations; i++)
{
RAND_bytes (randomHash, 32);
auto dest = CreateRequest (randomHash, true, !throughTunnels); // exploratory
if (!dest)
{
LogPrint (eLogWarning, "NetDbReq: Exploratory destination is requested already");
return;
}
auto floodfill = netdb.GetClosestFloodfill (randomHash, dest->GetExcludedPeers ());
if (floodfill)
{
if (i2p::transport::transports.IsConnected (floodfill->GetIdentHash ()))
throughTunnels = false;
if (throughTunnels)
{
msgs.push_back (i2p::tunnel::TunnelMessageBlock
{
i2p::tunnel::eDeliveryTypeRouter,
floodfill->GetIdentHash (), 0,
CreateDatabaseStoreMsg () // tell floodfill about us
});
msgs.push_back (i2p::tunnel::TunnelMessageBlock
{
i2p::tunnel::eDeliveryTypeRouter,
floodfill->GetIdentHash (), 0,
dest->CreateRequestMessage (floodfill, inbound) // explore
});
} }
else
i2p::transport::transports.SendMessage (floodfill->GetIdentHash (), dest->CreateRequestMessage (floodfill->GetIdentHash ()));
} }
else // delete obsolete request
done = true;
if (done)
it = m_RequestedDestinations.erase (it);
else else
RequestComplete (randomHash, nullptr); ++it;
} }
if (throughTunnels && msgs.size () > 0)
outbound->SendTunnelDataMsgs (msgs);
}
void NetDbRequests::ScheduleExploratory (uint64_t interval)
{
m_ExploratoryTimer.expires_from_now (boost::posix_time::seconds(interval));
m_ExploratoryTimer.async_wait (std::bind (&NetDbRequests::HandleExploratoryTimer,
this, std::placeholders::_1));
} }
void NetDbRequests::HandleExploratoryTimer (const boost::system::error_code& ecode)
{
if (ecode != boost::asio::error::operation_aborted)
{
auto numRouters = netdb.GetNumRouters ();
auto nextExploratoryInterval = numRouters < 2500 ? (EXPLORATORY_REQUEST_INTERVAL + m_Rng () % EXPLORATORY_REQUEST_INTERVAL)/2 :
EXPLORATORY_REQUEST_INTERVAL + m_Rng () % EXPLORATORY_REQUEST_INTERVAL_VARIANCE;
if (numRouters)
{
if (i2p::transport::transports.IsOnline () && i2p::transport::transports.IsRunning ())
{
// explore only if online
numRouters = 800/numRouters;
if (numRouters < 1) numRouters = 1;
if (numRouters > 9) numRouters = 9;
Explore (numRouters);
}
}
else
LogPrint (eLogError, "NetDbReq: No known routers, reseed seems to be totally failed");
ScheduleExploratory (nextExploratoryInterval);
}
}
void NetDbRequests::ScheduleDiscoveredRoutersRequest ()
{
m_DiscoveredRoutersTimer.expires_from_now (boost::posix_time::milliseconds(
DISCOVERED_REQUEST_INTERVAL + m_Rng () % DISCOVERED_REQUEST_INTERVAL_VARIANCE));
m_DiscoveredRoutersTimer.async_wait (std::bind (&NetDbRequests::HandleDiscoveredRoutersTimer,
this, std::placeholders::_1));
}
void NetDbRequests::HandleDiscoveredRoutersTimer (const boost::system::error_code& ecode)
{
if (ecode != boost::asio::error::operation_aborted)
{
if (!m_DiscoveredRouterHashes.empty ())
{
RequestRouter (m_DiscoveredRouterHashes.front ());
m_DiscoveredRouterHashes.pop_front ();
if (!m_DiscoveredRouterHashes.empty ()) // more hashes to request
ScheduleDiscoveredRoutersRequest ();
}
}
}
} }
} }

@ -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 * This file is part of Purple i2pd project and licensed under BSD3
* *
@ -9,118 +9,66 @@
#ifndef NETDB_REQUESTS_H__ #ifndef NETDB_REQUESTS_H__
#define NETDB_REQUESTS_H__ #define NETDB_REQUESTS_H__
#include <inttypes.h>
#include <memory> #include <memory>
#include <random> #include <set>
#include <unordered_set> #include <map>
#include <unordered_map>
#include <list>
#include "Identity.h" #include "Identity.h"
#include "RouterInfo.h" #include "RouterInfo.h"
#include "util.h"
namespace i2p namespace i2p
{ {
namespace data namespace data
{ {
const int MAX_NUM_REQUEST_ATTEMPTS = 5;
const uint64_t MANAGE_REQUESTS_INTERVAL = 1; // in seconds
const uint64_t MIN_REQUEST_TIME = 5; // in seconds
const uint64_t MAX_REQUEST_TIME = MAX_NUM_REQUEST_ATTEMPTS * (MIN_REQUEST_TIME + MANAGE_REQUESTS_INTERVAL);
const uint64_t EXPLORATORY_REQUEST_INTERVAL = 55; // in seconds
const uint64_t EXPLORATORY_REQUEST_INTERVAL_VARIANCE = 170; // in seconds
const uint64_t DISCOVERED_REQUEST_INTERVAL = 360; // in milliseconds
const uint64_t DISCOVERED_REQUEST_INTERVAL_VARIANCE = 540; // in milliseconds
const uint64_t MAX_EXPLORATORY_REQUEST_TIME = 30; // in seconds
const uint64_t REQUEST_CACHE_TIME = MAX_REQUEST_TIME + 40; // in seconds
const uint64_t REQUESTED_DESTINATIONS_POOL_CLEANUP_INTERVAL = 191; // in seconds
class RequestedDestination class RequestedDestination
{ {
public: public:
typedef std::function<void (std::shared_ptr<RouterInfo>)> RequestComplete; typedef std::function<void (std::shared_ptr<RouterInfo>)> RequestComplete;
RequestedDestination (const IdentHash& destination, bool isExploratory = false, bool direct = true); RequestedDestination (const IdentHash& destination, bool isExploratory = false):
~RequestedDestination (); m_Destination (destination), m_IsExploratory (isExploratory), m_CreationTime (0) {};
~RequestedDestination () { if (m_RequestComplete) m_RequestComplete (nullptr); };
const IdentHash& GetDestination () const { return m_Destination; }; const IdentHash& GetDestination () const { return m_Destination; };
const std::unordered_set<IdentHash>& GetExcludedPeers () const { return m_ExcludedPeers; }; int GetNumExcludedPeers () const { return m_ExcludedPeers.size (); };
int GetNumAttempts () const { return m_NumAttempts; }; const std::set<IdentHash>& GetExcludedPeers () { return m_ExcludedPeers; };
void ClearExcludedPeers (); void ClearExcludedPeers ();
bool IsExploratory () const { return m_IsExploratory; }; bool IsExploratory () const { return m_IsExploratory; };
bool IsDirect () const { return m_IsDirect; }; bool IsExcluded (const IdentHash& ident) const { return m_ExcludedPeers.count (ident); };
bool IsActive () const { return m_IsActive; };
bool IsExcluded (const IdentHash& ident) const;
uint64_t GetCreationTime () const { return m_CreationTime; }; uint64_t GetCreationTime () const { return m_CreationTime; };
uint64_t GetLastRequestTime () const { return m_LastRequestTime; };
std::shared_ptr<I2NPMessage> CreateRequestMessage (std::shared_ptr<const RouterInfo>, std::shared_ptr<const i2p::tunnel::InboundTunnel> replyTunnel); std::shared_ptr<I2NPMessage> CreateRequestMessage (std::shared_ptr<const RouterInfo>, std::shared_ptr<const i2p::tunnel::InboundTunnel> replyTunnel);
std::shared_ptr<I2NPMessage> CreateRequestMessage (const IdentHash& floodfill); std::shared_ptr<I2NPMessage> CreateRequestMessage (const IdentHash& floodfill);
void AddRequestComplete (const RequestComplete& requestComplete) { m_RequestComplete.push_back (requestComplete); }; void SetRequestComplete (const RequestComplete& requestComplete) { m_RequestComplete = requestComplete; };
void ResetRequestComplete () { m_RequestComplete.clear (); }; bool IsRequestComplete () const { return m_RequestComplete != nullptr; };
void Success (std::shared_ptr<RouterInfo> r); void Success (std::shared_ptr<RouterInfo> r);
void Fail (); void Fail ();
private:
void InvokeRequestComplete (std::shared_ptr<RouterInfo> r);
private: private:
IdentHash m_Destination; IdentHash m_Destination;
bool m_IsExploratory, m_IsDirect, m_IsActive; bool m_IsExploratory;
std::unordered_set<IdentHash> m_ExcludedPeers; std::set<IdentHash> m_ExcludedPeers;
uint64_t m_CreationTime, m_LastRequestTime; // in seconds uint64_t m_CreationTime;
std::list<RequestComplete> m_RequestComplete; RequestComplete m_RequestComplete;
int m_NumAttempts;
}; };
class NetDbRequests: public std::enable_shared_from_this<NetDbRequests>, class NetDbRequests
private i2p::util::RunnableServiceWithWork
{ {
public: public:
NetDbRequests ();
~NetDbRequests ();
void Start (); void Start ();
void Stop (); void Stop ();
std::shared_ptr<RequestedDestination> CreateRequest (const IdentHash& destination, bool isExploratory, RequestedDestination::RequestComplete requestComplete = nullptr);
void RequestComplete (const IdentHash& ident, std::shared_ptr<RouterInfo> r); void RequestComplete (const IdentHash& ident, std::shared_ptr<RouterInfo> r);
void PostDatabaseSearchReplyMsg (std::shared_ptr<const I2NPMessage> msg);
void PostRequestDestination (const IdentHash& destination, const RequestedDestination::RequestComplete& requestComplete, bool direct);
private:
std::shared_ptr<RequestedDestination> CreateRequest (const IdentHash& destination, bool isExploratory,
bool direct = false, RequestedDestination::RequestComplete requestComplete = nullptr);
std::shared_ptr<RequestedDestination> FindRequest (const IdentHash& ident) const; std::shared_ptr<RequestedDestination> FindRequest (const IdentHash& ident) const;
bool SendNextRequest (std::shared_ptr<RequestedDestination> dest);
void HandleDatabaseSearchReplyMsg (std::shared_ptr<const I2NPMessage> msg);
void RequestRouter (const IdentHash& router);
void RequestDestination (const IdentHash& destination, const RequestedDestination::RequestComplete& requestComplete, bool direct);
void Explore (int numDestinations);
void ManageRequests (); void ManageRequests ();
// timer
void ScheduleManageRequests ();
void HandleManageRequestsTimer (const boost::system::error_code& ecode);
void ScheduleExploratory (uint64_t interval);
void HandleExploratoryTimer (const boost::system::error_code& ecode);
void ScheduleCleanup ();
void HandleCleanupTimer (const boost::system::error_code& ecode);
void ScheduleDiscoveredRoutersRequest ();
void HandleDiscoveredRoutersTimer (const boost::system::error_code& ecode);
private: private:
std::unordered_map<IdentHash, std::shared_ptr<RequestedDestination> > m_RequestedDestinations; mutable std::mutex m_RequestedDestinationsMutex;
std::list<IdentHash> m_DiscoveredRouterHashes; std::map<IdentHash, std::shared_ptr<RequestedDestination> > m_RequestedDestinations;
i2p::util::MemoryPoolMt<RequestedDestination> m_RequestedDestinationsPool;
boost::asio::deadline_timer m_ManageRequestsTimer, m_ExploratoryTimer,
m_CleanupTimer, m_DiscoveredRoutersTimer;
std::mt19937 m_Rng;
}; };
} }
} }

@ -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 * This file is part of Purple i2pd project and licensed under BSD3
* *
@ -245,28 +245,13 @@ namespace data
return profile; return profile;
} }
bool IsRouterBanned (const IdentHash& identHash)
{
std::unique_lock<std::mutex> l(g_ProfilesMutex);
auto it = g_Profiles.find (identHash);
if (it != g_Profiles.end ())
return it->second->IsUnreachable ();
return false;
}
void InitProfilesStorage () void InitProfilesStorage ()
{ {
g_ProfilesStorage.SetPlace(i2p::fs::GetDataDir()); g_ProfilesStorage.SetPlace(i2p::fs::GetDataDir());
g_ProfilesStorage.Init(i2p::data::GetBase64SubstitutionTable(), 64); g_ProfilesStorage.Init(i2p::data::GetBase64SubstitutionTable(), 64);
} }
static void SaveProfilesToDisk (std::list<std::pair<i2p::data::IdentHash, std::shared_ptr<RouterProfile> > >&& profiles) void PersistProfiles ()
{
for (auto& it: profiles)
if (it.second) it.second->Save (it.first);
}
std::future<void> PersistProfiles ()
{ {
auto ts = GetTime (); auto ts = GetTime ();
std::list<std::pair<i2p::data::IdentHash, std::shared_ptr<RouterProfile> > > tmp; std::list<std::pair<i2p::data::IdentHash, std::shared_ptr<RouterProfile> > > tmp;
@ -284,9 +269,8 @@ namespace data
it++; it++;
} }
} }
if (!tmp.empty ()) for (auto& it: tmp)
return std::async (std::launch::async, SaveProfilesToDisk, std::move (tmp)); if (it.second) it.second->Save (it.first);
return std::future<void>();
} }
void SaveProfiles () void SaveProfiles ()
@ -294,7 +278,8 @@ namespace data
std::unordered_map<i2p::data::IdentHash, std::shared_ptr<RouterProfile> > tmp; std::unordered_map<i2p::data::IdentHash, std::shared_ptr<RouterProfile> > tmp;
{ {
std::unique_lock<std::mutex> l(g_ProfilesMutex); std::unique_lock<std::mutex> l(g_ProfilesMutex);
std::swap (tmp, g_Profiles); tmp = g_Profiles;
g_Profiles.clear ();
} }
auto ts = GetTime (); auto ts = GetTime ();
for (auto& it: tmp) for (auto& it: tmp)
@ -302,29 +287,7 @@ namespace data
it.second->Save (it.first); it.second->Save (it.first);
} }
static void DeleteFilesFromDisk () void DeleteObsoleteProfiles ()
{
std::vector<std::string> files;
g_ProfilesStorage.Traverse(files);
struct stat st;
std::time_t now = std::time(nullptr);
for (const auto& path: files)
{
if (stat(path.c_str(), &st) != 0)
{
LogPrint(eLogWarning, "Profiling: Can't stat(): ", path);
continue;
}
if (now - st.st_mtime >= PEER_PROFILE_EXPIRATION_TIMEOUT*3600)
{
LogPrint(eLogDebug, "Profiling: Removing expired peer profile: ", path);
i2p::fs::Remove(path);
}
}
}
std::future<void> DeleteObsoleteProfiles ()
{ {
{ {
auto ts = GetTime (); auto ts = GetTime ();
@ -338,7 +301,21 @@ namespace data
} }
} }
return std::async (std::launch::async, DeleteFilesFromDisk); struct stat st;
std::time_t now = std::time(nullptr);
std::vector<std::string> files;
g_ProfilesStorage.Traverse(files);
for (const auto& path: files) {
if (stat(path.c_str(), &st) != 0) {
LogPrint(eLogWarning, "Profiling: Can't stat(): ", path);
continue;
}
if (now - st.st_mtime >= PEER_PROFILE_EXPIRATION_TIMEOUT*3600) {
LogPrint(eLogDebug, "Profiling: Removing expired peer profile: ", path);
i2p::fs::Remove(path);
}
}
} }
} }
} }

@ -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 * This file is part of Purple i2pd project and licensed under BSD3
* *
@ -10,7 +10,6 @@
#define PROFILING_H__ #define PROFILING_H__
#include <memory> #include <memory>
#include <future>
#include <boost/date_time/posix_time/posix_time.hpp> #include <boost/date_time/posix_time/posix_time.hpp>
#include "Identity.h" #include "Identity.h"
@ -32,13 +31,11 @@ namespace data
const char PEER_PROFILE_USAGE_CONNECTED[] = "connected"; const char PEER_PROFILE_USAGE_CONNECTED[] = "connected";
const int PEER_PROFILE_EXPIRATION_TIMEOUT = 36; // in hours (1.5 days) const int PEER_PROFILE_EXPIRATION_TIMEOUT = 36; // in hours (1.5 days)
const int PEER_PROFILE_AUTOCLEAN_TIMEOUT = 1500; // in seconds (25 minutes) const int PEER_PROFILE_AUTOCLEAN_TIMEOUT = 3 * 3600; // in seconds (3 hours)
const int PEER_PROFILE_AUTOCLEAN_VARIANCE = 900; // in seconds (15 minutes) const int PEER_PROFILE_AUTOCLEAN_VARIANCE = 3600; // in seconds (1 hour)
const int PEER_PROFILE_OBSOLETE_PROFILES_CLEAN_TIMEOUT = 5400; // in seconds (1.5 hours)
const int PEER_PROFILE_OBSOLETE_PROFILES_CLEAN_VARIANCE = 2400; // in seconds (40 minutes)
const int PEER_PROFILE_DECLINED_RECENTLY_INTERVAL = 150; // in seconds (2.5 minutes) const int PEER_PROFILE_DECLINED_RECENTLY_INTERVAL = 150; // in seconds (2.5 minutes)
const int PEER_PROFILE_PERSIST_INTERVAL = 3300; // in seconds (55 minutes) const int PEER_PROFILE_PERSIST_INTERVAL = 3300; // in seconds (55 minutes)
const int PEER_PROFILE_UNREACHABLE_INTERVAL = 480; // in seconds (8 minutes) const int PEER_PROFILE_UNREACHABLE_INTERVAL = 2*3600; // on seconds (2 hours)
const int PEER_PROFILE_USEFUL_THRESHOLD = 3; const int PEER_PROFILE_USEFUL_THRESHOLD = 3;
class RouterProfile class RouterProfile
@ -90,11 +87,10 @@ namespace data
}; };
std::shared_ptr<RouterProfile> GetRouterProfile (const IdentHash& identHash); std::shared_ptr<RouterProfile> GetRouterProfile (const IdentHash& identHash);
bool IsRouterBanned (const IdentHash& identHash); // check only existing profiles
void InitProfilesStorage (); void InitProfilesStorage ();
std::future<void> DeleteObsoleteProfiles (); void DeleteObsoleteProfiles ();
void SaveProfiles (); void SaveProfiles ();
std::future<void> PersistProfiles (); void PersistProfiles ();
} }
} }

@ -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 * This file is part of Purple i2pd project and licensed under BSD3
* *
@ -26,7 +26,6 @@
#include "HTTP.h" #include "HTTP.h"
#include "util.h" #include "util.h"
#include "Config.h" #include "Config.h"
#include "Socks5.h"
namespace i2p namespace i2p
{ {
@ -616,21 +615,62 @@ namespace data
{ {
// assume socks if not http, is checked before this for other types // assume socks if not http, is checked before this for other types
// TODO: support username/password auth etc // TODO: support username/password auth etc
bool success = false; uint8_t hs_writebuf[3] = {0x05, 0x01, 0x00};
i2p::transport::Socks5Handshake (sock, std::make_pair(url.host, url.port), uint8_t hs_readbuf[2];
[&success](const boost::system::error_code& ec) boost::asio::write(sock, boost::asio::buffer(hs_writebuf, 3), boost::asio::transfer_all(), ecode);
{ if(ecode)
if (!ec)
success = true;
else
LogPrint (eLogError, "Reseed: SOCKS handshake failed: ", ec.message());
});
service.run (); // execute all async operations
if (!success)
{ {
sock.close(); sock.close();
LogPrint(eLogError, "Reseed: SOCKS handshake write failed: ", ecode.message());
return ""; return "";
} }
boost::asio::read(sock, boost::asio::buffer(hs_readbuf, 2), ecode);
if(ecode)
{
sock.close();
LogPrint(eLogError, "Reseed: SOCKS handshake read failed: ", ecode.message());
return "";
}
size_t sz = 0;
uint8_t buf[256];
buf[0] = 0x05;
buf[1] = 0x01;
buf[2] = 0x00;
buf[3] = 0x03;
sz += 4;
size_t hostsz = url.host.size();
if(1 + 2 + hostsz + sz > sizeof(buf))
{
sock.close();
LogPrint(eLogError, "Reseed: SOCKS handshake failed, hostname too big: ", url.host);
return "";
}
buf[4] = (uint8_t) hostsz;
memcpy(buf+5, url.host.c_str(), hostsz);
sz += hostsz + 1;
htobe16buf(buf+sz, url.port);
sz += 2;
boost::asio::write(sock, boost::asio::buffer(buf, sz), boost::asio::transfer_all(), ecode);
if(ecode)
{
sock.close();
LogPrint(eLogError, "Reseed: SOCKS handshake failed writing: ", ecode.message());
return "";
}
boost::asio::read(sock, boost::asio::buffer(buf, 10), ecode);
if(ecode)
{
sock.close();
LogPrint(eLogError, "Reseed: SOCKS handshake failed reading: ", ecode.message());
return "";
}
if(buf[1] != 0x00)
{
sock.close();
LogPrint(eLogError, "Reseed: SOCKS handshake bad reply code: ", std::to_string(buf[1]));
return "";
}
} }
} }
} }
@ -647,16 +687,18 @@ namespace data
while (it != end) while (it != end)
{ {
boost::asio::ip::tcp::endpoint ep = *it; boost::asio::ip::tcp::endpoint ep = *it;
bool supported = false; if (
if (!ep.address ().is_unspecified ()) (
{ !i2p::util::net::IsInReservedRange(ep.address ()) && (
if (ep.address ().is_v4 ()) (ep.address ().is_v4 () && i2p::context.SupportsV4 ()) ||
supported = i2p::context.SupportsV4 (); (ep.address ().is_v6 () && i2p::context.SupportsV6 ())
else if (ep.address ().is_v6 ()) )
supported = i2p::util::net::IsYggdrasilAddress (ep.address ()) ? ) ||
i2p::context.SupportsMesh () : i2p::context.SupportsV6 (); (
} i2p::util::net::IsYggdrasilAddress (ep.address ()) &&
if (supported) i2p::context.SupportsMesh ()
)
)
{ {
s.lowest_layer().connect (ep, ecode); s.lowest_layer().connect (ep, ecode);
if (!ecode) if (!ecode)

@ -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 * This file is part of Purple i2pd project and licensed under BSD3
* *
@ -40,7 +40,7 @@ namespace i2p
void RouterContext::Init () void RouterContext::Init ()
{ {
srand (i2p::util::GetMillisecondsSinceEpoch () % 1000); srand (i2p::util::GetMillisecondsSinceEpoch () % 1000);
m_StartupTime = i2p::util::GetMonotonicSeconds (); m_StartupTime = std::chrono::steady_clock::now();
if (!Load ()) if (!Load ())
CreateNewRouter (); CreateNewRouter ();
@ -57,12 +57,13 @@ namespace i2p
{ {
m_Service.reset (new RouterService); m_Service.reset (new RouterService);
m_Service->Start (); m_Service->Start ();
m_PublishTimer.reset (new boost::asio::deadline_timer (m_Service->GetService ())); if (!m_IsHiddenMode)
ScheduleInitialPublish (); {
m_CongestionUpdateTimer.reset (new boost::asio::deadline_timer (m_Service->GetService ())); m_PublishTimer.reset (new boost::asio::deadline_timer (m_Service->GetService ()));
ScheduleCongestionUpdate (); ScheduleInitialPublish ();
m_CleanupTimer.reset (new boost::asio::deadline_timer (m_Service->GetService ())); m_CongestionUpdateTimer.reset (new boost::asio::deadline_timer (m_Service->GetService ()));
ScheduleCleanupTimer (); ScheduleCongestionUpdate ();
}
} }
} }
@ -77,13 +78,7 @@ namespace i2p
m_Service->Stop (); m_Service->Stop ();
} }
} }
std::shared_ptr<i2p::data::RouterInfo::Buffer> RouterContext::CopyRouterInfoBuffer () const
{
std::lock_guard<std::mutex> l(m_RouterInfoMutex);
return m_RouterInfo.CopyBuffer ();
}
void RouterContext::CreateNewRouter () void RouterContext::CreateNewRouter ()
{ {
m_Keys = i2p::data::PrivateKeys::CreateRandomKeys (i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519, m_Keys = i2p::data::PrivateKeys::CreateRandomKeys (i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519,
@ -252,10 +247,7 @@ namespace i2p
void RouterContext::UpdateRouterInfo () void RouterContext::UpdateRouterInfo ()
{ {
{ m_RouterInfo.CreateBuffer (m_Keys);
std::lock_guard<std::mutex> l(m_RouterInfoMutex);
m_RouterInfo.CreateBuffer (m_Keys);
}
m_RouterInfo.SaveToFile (i2p::fs::DataDirPath (ROUTER_INFO)); m_RouterInfo.SaveToFile (i2p::fs::DataDirPath (ROUTER_INFO));
m_LastUpdateTime = i2p::util::GetSecondsSinceEpoch (); m_LastUpdateTime = i2p::util::GetSecondsSinceEpoch ();
} }
@ -311,8 +303,6 @@ namespace i2p
SetTesting (false); SetTesting (false);
if (status != m_Status) if (status != m_Status)
{ {
LogPrint(eLogInfo, "Router: network status v4 changed ",
ROUTER_STATUS_NAMES[m_Status], " -> ", ROUTER_STATUS_NAMES[status]);
m_Status = status; m_Status = status;
switch (m_Status) switch (m_Status)
{ {
@ -333,8 +323,6 @@ namespace i2p
SetTestingV6 (false); SetTestingV6 (false);
if (status != m_StatusV6) if (status != m_StatusV6)
{ {
LogPrint(eLogInfo, "Router: network status v6 changed ",
ROUTER_STATUS_NAMES[m_StatusV6], " -> ", ROUTER_STATUS_NAMES[status]);
m_StatusV6 = status; m_StatusV6 = status;
switch (m_StatusV6) switch (m_StatusV6)
{ {
@ -566,10 +554,10 @@ namespace i2p
{ {
m_IsFloodfill = floodfill; m_IsFloodfill = floodfill;
if (floodfill) if (floodfill)
m_RouterInfo.UpdateFloodfillProperty (true); m_RouterInfo.UpdateCaps (m_RouterInfo.GetCaps () | i2p::data::RouterInfo::eFloodfill);
else else
{ {
m_RouterInfo.UpdateFloodfillProperty (false); m_RouterInfo.UpdateCaps (m_RouterInfo.GetCaps () & ~i2p::data::RouterInfo::eFloodfill);
// we don't publish number of routers and leaseset for non-floodfill // we don't publish number of routers and leaseset for non-floodfill
m_RouterInfo.DeleteProperty (i2p::data::ROUTER_INFO_PROPERTY_LEASESETS); m_RouterInfo.DeleteProperty (i2p::data::ROUTER_INFO_PROPERTY_LEASESETS);
m_RouterInfo.DeleteProperty (i2p::data::ROUTER_INFO_PROPERTY_ROUTERS); m_RouterInfo.DeleteProperty (i2p::data::ROUTER_INFO_PROPERTY_ROUTERS);
@ -606,15 +594,15 @@ namespace i2p
/* detect parameters */ /* detect parameters */
switch (L) switch (L)
{ {
case i2p::data::CAPS_FLAG_LOW_BANDWIDTH1 : limit = 12; type = low; break; case i2p::data::CAPS_FLAG_LOW_BANDWIDTH1 : limit = 12; type = low; break;
case i2p::data::CAPS_FLAG_LOW_BANDWIDTH2 : limit = i2p::data::LOW_BANDWIDTH_LIMIT; type = low; break; // 48 case i2p::data::CAPS_FLAG_LOW_BANDWIDTH2 : limit = 48; type = low; break;
case i2p::data::CAPS_FLAG_LOW_BANDWIDTH3 : limit = 64; type = low; break; case i2p::data::CAPS_FLAG_HIGH_BANDWIDTH1 : limit = 64; type = high; break;
case i2p::data::CAPS_FLAG_HIGH_BANDWIDTH1 : limit = 128; type = high; break; case i2p::data::CAPS_FLAG_HIGH_BANDWIDTH2 : limit = 128; type = high; break;
case i2p::data::CAPS_FLAG_HIGH_BANDWIDTH2 : limit = i2p::data::HIGH_BANDWIDTH_LIMIT; type = high; break; // 256 case i2p::data::CAPS_FLAG_HIGH_BANDWIDTH3 : limit = 256; type = high; break;
case i2p::data::CAPS_FLAG_EXTRA_BANDWIDTH1 : limit = i2p::data::EXTRA_BANDWIDTH_LIMIT; type = extra; break; // 2048 case i2p::data::CAPS_FLAG_EXTRA_BANDWIDTH1 : limit = 2048; type = extra; break;
case i2p::data::CAPS_FLAG_EXTRA_BANDWIDTH2 : limit = 1000000; type = unlim; break; // 1Gbyte/s case i2p::data::CAPS_FLAG_EXTRA_BANDWIDTH2 : limit = 1000000; type = unlim; break; // 1Gbyte/s
default: default:
limit = i2p::data::LOW_BANDWIDTH_LIMIT; type = low; // 48 limit = 48; type = low;
} }
/* update caps & flags in RI */ /* update caps & flags in RI */
auto caps = m_RouterInfo.GetCaps (); auto caps = m_RouterInfo.GetCaps ();
@ -1143,14 +1131,13 @@ namespace i2p
return i2p::tunnel::tunnels.GetExploratoryPool (); return i2p::tunnel::tunnels.GetExploratoryPool ();
} }
int RouterContext::GetCongestionLevel (bool longTerm) const bool RouterContext::IsHighCongestion () const
{ {
return std::max ( return i2p::tunnel::tunnels.IsTooManyTransitTunnels () ||
i2p::tunnel::tunnels.GetCongestionLevel (), i2p::transport::transports.IsBandwidthExceeded () ||
i2p::transport::transports.GetCongestionLevel (longTerm) i2p::transport::transports.IsTransitBandwidthExceeded ();
); }
}
void RouterContext::HandleI2NPMessage (const uint8_t * buf, size_t len) void RouterContext::HandleI2NPMessage (const uint8_t * buf, size_t len)
{ {
i2p::HandleI2NPMessage (CreateI2NPMessage (buf, GetI2NPMessageLength (buf, len))); i2p::HandleI2NPMessage (CreateI2NPMessage (buf, GetI2NPMessageLength (buf, len)));
@ -1158,13 +1145,6 @@ namespace i2p
bool RouterContext::HandleCloveI2NPMessage (I2NPMessageType typeID, const uint8_t * payload, size_t len, uint32_t msgID) bool RouterContext::HandleCloveI2NPMessage (I2NPMessageType typeID, const uint8_t * payload, size_t len, uint32_t msgID)
{ {
if (typeID == eI2NPTunnelTest)
{
// try tunnel test
auto pool = GetTunnelPool ();
if (pool && pool->ProcessTunnelTest (bufbe32toh (payload + TUNNEL_TEST_MSGID_OFFSET), bufbe64toh (payload + TUNNEL_TEST_TIMESTAMP_OFFSET)))
return true;
}
auto msg = CreateI2NPMessage (typeID, payload, len, msgID); auto msg = CreateI2NPMessage (typeID, payload, len, msgID);
if (!msg) return false; if (!msg) return false;
i2p::HandleI2NPMessage (msg); i2p::HandleI2NPMessage (msg);
@ -1219,30 +1199,21 @@ namespace i2p
else else
i2p::garlic::GarlicDestination::ProcessDeliveryStatusMessage (msg); i2p::garlic::GarlicDestination::ProcessDeliveryStatusMessage (msg);
} }
void RouterContext::SubmitECIESx25519Key (const uint8_t * key, uint64_t tag) void RouterContext::CleanupDestination ()
{ {
if (m_Service) if (m_Service)
{ m_Service->GetService ().post ([this]()
struct {
{ this->i2p::garlic::GarlicDestination::CleanupExpiredTags ();
uint8_t k[32]; });
uint64_t t;
} data;
memcpy (data.k, key, 32);
data.t = tag;
m_Service->GetService ().post ([this,data](void)
{
AddECIESx25519Key (data.k, data.t);
});
}
else else
LogPrint (eLogError, "Router: service is NULL"); LogPrint (eLogError, "Router: service is NULL");
} }
uint32_t RouterContext::GetUptime () const uint32_t RouterContext::GetUptime () const
{ {
return i2p::util::GetMonotonicSeconds () - m_StartupTime; return std::chrono::duration_cast<std::chrono::seconds> (std::chrono::steady_clock::now() - m_StartupTime).count ();
} }
bool RouterContext::Decrypt (const uint8_t * encrypted, uint8_t * data, i2p::data::CryptoKeyType preferredCrypto) const bool RouterContext::Decrypt (const uint8_t * encrypted, uint8_t * data, i2p::data::CryptoKeyType preferredCrypto) const
@ -1330,15 +1301,9 @@ namespace i2p
if (ecode != boost::asio::error::operation_aborted) if (ecode != boost::asio::error::operation_aborted)
{ {
if (m_RouterInfo.IsReachableBy (i2p::data::RouterInfo::eAllTransports)) if (m_RouterInfo.IsReachableBy (i2p::data::RouterInfo::eAllTransports))
{
UpdateCongestion ();
HandlePublishTimer (ecode); HandlePublishTimer (ecode);
}
else else
{
UpdateTimestamp (i2p::util::GetSecondsSinceEpoch ());
ScheduleInitialPublish (); ScheduleInitialPublish ();
}
} }
} }
@ -1360,21 +1325,16 @@ namespace i2p
{ {
if (ecode != boost::asio::error::operation_aborted) if (ecode != boost::asio::error::operation_aborted)
{ {
UpdateTimestamp (i2p::util::GetSecondsSinceEpoch ()); m_PublishExcluded.clear ();
if (!m_IsHiddenMode) m_PublishReplyToken = 0;
if (IsFloodfill ())
{ {
m_PublishExcluded.clear (); UpdateStats (); // for floodfill
m_PublishReplyToken = 0; m_PublishExcluded.insert (i2p::context.GetIdentHash ()); // don't publish to ourselves
if (IsFloodfill ())
{
UpdateStats (); // for floodfill
m_PublishExcluded.insert (i2p::context.GetIdentHash ()); // don't publish to ourselves
}
Publish ();
SchedulePublishResend ();
} }
else UpdateTimestamp (i2p::util::GetSecondsSinceEpoch ());
SchedulePublish (); Publish ();
SchedulePublishResend ();
} }
} }
@ -1394,20 +1354,10 @@ namespace i2p
uint32_t replyToken; uint32_t replyToken;
RAND_bytes ((uint8_t *)&replyToken, 4); RAND_bytes ((uint8_t *)&replyToken, 4);
LogPrint (eLogInfo, "Router: Publishing our RouterInfo to ", i2p::data::GetIdentHashAbbreviation(floodfill->GetIdentHash ()), ". reply token=", replyToken); LogPrint (eLogInfo, "Router: Publishing our RouterInfo to ", i2p::data::GetIdentHashAbbreviation(floodfill->GetIdentHash ()), ". reply token=", replyToken);
auto onDrop = [this]() if (floodfill->IsReachableFrom (i2p::context.GetRouterInfo ()) || // are we able to connect?
{ i2p::transport::transports.IsConnected (floodfill->GetIdentHash ())) // already connected ?
if (m_Service)
m_Service->GetService ().post ([this]() { HandlePublishResendTimer (boost::system::error_code ()); });
};
if (i2p::transport::transports.IsConnected (floodfill->GetIdentHash ()) || // already connected
(floodfill->IsReachableFrom (i2p::context.GetRouterInfo ()) && // are we able to connect
!i2p::transport::transports.RoutesRestricted ())) // and routes not restricted
{
// send directly // send directly
auto msg = CreateDatabaseStoreMsg (i2p::context.GetSharedRouterInfo (), replyToken); i2p::transport::transports.SendMessage (floodfill->GetIdentHash (), CreateDatabaseStoreMsg (i2p::context.GetSharedRouterInfo (), replyToken));
msg->onDrop = onDrop;
i2p::transport::transports.SendMessage (floodfill->GetIdentHash (), msg);
}
else else
{ {
// otherwise through exploratory // otherwise through exploratory
@ -1418,7 +1368,6 @@ namespace i2p
{ {
// encrypt for floodfill // encrypt for floodfill
auto msg = CreateDatabaseStoreMsg (i2p::context.GetSharedRouterInfo (), replyToken, inbound); auto msg = CreateDatabaseStoreMsg (i2p::context.GetSharedRouterInfo (), replyToken, inbound);
msg->onDrop = onDrop;
outbound->SendTunnelDataMsgTo (floodfill->GetIdentHash (), 0, outbound->SendTunnelDataMsgTo (floodfill->GetIdentHash (), 0,
i2p::garlic::WrapECIESX25519MessageForRouter (msg, floodfill->GetIdentity ()->GetEncryptionPublicKey ())); i2p::garlic::WrapECIESX25519MessageForRouter (msg, floodfill->GetIdentity ()->GetEncryptionPublicKey ()));
} }
@ -1472,47 +1421,14 @@ namespace i2p
{ {
if (ecode != boost::asio::error::operation_aborted) if (ecode != boost::asio::error::operation_aborted)
{ {
UpdateCongestion (); auto c = i2p::data::RouterInfo::eLowCongestion;
ScheduleCongestionUpdate (); if (!AcceptsTunnels ())
} c = i2p::data::RouterInfo::eRejectAll;
} else if (IsHighCongestion ())
void RouterContext::UpdateCongestion ()
{
auto c = i2p::data::RouterInfo::eLowCongestion;
if (!AcceptsTunnels () || !m_ShareRatio)
c = i2p::data::RouterInfo::eRejectAll;
else
{
int congestionLevel = GetCongestionLevel (true);
if (congestionLevel > CONGESTION_LEVEL_HIGH)
c = i2p::data::RouterInfo::eHighCongestion; c = i2p::data::RouterInfo::eHighCongestion;
else if (congestionLevel > CONGESTION_LEVEL_MEDIUM) if (m_RouterInfo.UpdateCongestion (c))
c = i2p::data::RouterInfo::eMediumCongestion; UpdateRouterInfo ();
} ScheduleCongestionUpdate ();
if (m_RouterInfo.UpdateCongestion (c))
UpdateRouterInfo ();
}
void RouterContext::ScheduleCleanupTimer ()
{
if (m_CleanupTimer)
{
m_CleanupTimer->cancel ();
m_CleanupTimer->expires_from_now (boost::posix_time::minutes(ROUTER_INFO_CLEANUP_INTERVAL));
m_CleanupTimer->async_wait (std::bind (&RouterContext::HandleCleanupTimer,
this, std::placeholders::_1));
}
else
LogPrint (eLogError, "Router: Cleanup timer is NULL");
}
void RouterContext::HandleCleanupTimer (const boost::system::error_code& ecode)
{
if (ecode != boost::asio::error::operation_aborted)
{
CleanupExpiredTags ();
ScheduleCleanupTimer ();
} }
} }
} }

@ -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 * This file is part of Purple i2pd project and licensed under BSD3
* *
@ -12,7 +12,8 @@
#include <inttypes.h> #include <inttypes.h>
#include <string> #include <string>
#include <memory> #include <memory>
#include <unordered_set> #include <chrono>
#include <set>
#include <boost/asio.hpp> #include <boost/asio.hpp>
#include "Identity.h" #include "Identity.h"
#include "RouterInfo.h" #include "RouterInfo.h"
@ -37,7 +38,6 @@ namespace garlic
const int ROUTER_INFO_CONFIRMATION_TIMEOUT = 5; // in seconds const int ROUTER_INFO_CONFIRMATION_TIMEOUT = 5; // in seconds
const int ROUTER_INFO_MAX_PUBLISH_EXCLUDED_FLOODFILLS = 15; const int ROUTER_INFO_MAX_PUBLISH_EXCLUDED_FLOODFILLS = 15;
const int ROUTER_INFO_CONGESTION_UPDATE_INTERVAL = 12*60; // in seconds const int ROUTER_INFO_CONGESTION_UPDATE_INTERVAL = 12*60; // in seconds
const int ROUTER_INFO_CLEANUP_INTERVAL = 5; // in minutes
enum RouterStatus enum RouterStatus
{ {
@ -48,15 +48,6 @@ namespace garlic
eRouterStatusMesh = 4 eRouterStatusMesh = 4
}; };
const char* const ROUTER_STATUS_NAMES[] =
{
"OK", // 0
"Firewalled", // 1
"Unknown", // 2
"Proxy", // 3
"Mesh" // 4
};
enum RouterError enum RouterError
{ {
eRouterErrorNone = 0, eRouterErrorNone = 0,
@ -114,8 +105,7 @@ namespace garlic
return std::shared_ptr<i2p::garlic::GarlicDestination> (this, return std::shared_ptr<i2p::garlic::GarlicDestination> (this,
[](i2p::garlic::GarlicDestination *) {}); [](i2p::garlic::GarlicDestination *) {});
} }
std::shared_ptr<i2p::data::RouterInfo::Buffer> CopyRouterInfoBuffer () const;
const uint8_t * GetNTCP2StaticPublicKey () const { return m_NTCP2Keys ? m_NTCP2Keys->staticPublicKey : nullptr; }; const uint8_t * GetNTCP2StaticPublicKey () const { return m_NTCP2Keys ? m_NTCP2Keys->staticPublicKey : nullptr; };
const uint8_t * GetNTCP2StaticPrivateKey () const { return m_NTCP2Keys ? m_NTCP2Keys->staticPrivateKey : nullptr; }; const uint8_t * GetNTCP2StaticPrivateKey () const { return m_NTCP2Keys ? m_NTCP2Keys->staticPrivateKey : nullptr; };
const uint8_t * GetNTCP2IV () const { return m_NTCP2Keys ? m_NTCP2Keys->iv : nullptr; }; const uint8_t * GetNTCP2IV () const { return m_NTCP2Keys ? m_NTCP2Keys->iv : nullptr; };
@ -146,7 +136,6 @@ namespace garlic
void SetNetID (int netID) { m_NetID = netID; }; void SetNetID (int netID) { m_NetID = netID; };
bool DecryptTunnelBuildRecord (const uint8_t * encrypted, uint8_t * data); bool DecryptTunnelBuildRecord (const uint8_t * encrypted, uint8_t * data);
bool DecryptTunnelShortRequestRecord (const uint8_t * encrypted, uint8_t * data); bool DecryptTunnelShortRequestRecord (const uint8_t * encrypted, uint8_t * data);
void SubmitECIESx25519Key (const uint8_t * key, uint64_t tag);
void UpdatePort (int port); // called from Daemon void UpdatePort (int port); // called from Daemon
void UpdateAddress (const boost::asio::ip::address& host); // called from SSU2 or Daemon void UpdateAddress (const boost::asio::ip::address& host); // called from SSU2 or Daemon
@ -167,7 +156,7 @@ namespace garlic
void SetShareRatio (int percents); // 0 - 100 void SetShareRatio (int percents); // 0 - 100
bool AcceptsTunnels () const { return m_AcceptsTunnels; }; bool AcceptsTunnels () const { return m_AcceptsTunnels; };
void SetAcceptsTunnels (bool acceptsTunnels) { m_AcceptsTunnels = acceptsTunnels; }; void SetAcceptsTunnels (bool acceptsTunnels) { m_AcceptsTunnels = acceptsTunnels; };
int GetCongestionLevel (bool longTerm) const; bool IsHighCongestion () const;
bool SupportsV6 () const { return m_RouterInfo.IsV6 (); }; bool SupportsV6 () const { return m_RouterInfo.IsV6 (); };
bool SupportsV4 () const { return m_RouterInfo.IsV4 (); }; bool SupportsV4 () const { return m_RouterInfo.IsV4 (); };
bool SupportsMesh () const { return m_RouterInfo.IsMesh (); }; bool SupportsMesh () const { return m_RouterInfo.IsMesh (); };
@ -182,6 +171,7 @@ namespace garlic
void UpdateNTCP2V6Address (const boost::asio::ip::address& host); // called from Daemon. TODO: remove void UpdateNTCP2V6Address (const boost::asio::ip::address& host); // called from Daemon. TODO: remove
void UpdateStats (); void UpdateStats ();
void UpdateTimestamp (uint64_t ts); // in seconds, called from NetDb before publishing void UpdateTimestamp (uint64_t ts); // in seconds, called from NetDb before publishing
void CleanupDestination (); // garlic destination
// implements LocalDestination // implements LocalDestination
std::shared_ptr<const i2p::data::IdentityEx> GetIdentity () const { return m_Keys.GetPublic (); }; std::shared_ptr<const i2p::data::IdentityEx> GetIdentity () const { return m_Keys.GetPublic (); };
@ -230,9 +220,6 @@ namespace garlic
void HandlePublishResendTimer (const boost::system::error_code& ecode); void HandlePublishResendTimer (const boost::system::error_code& ecode);
void ScheduleCongestionUpdate (); void ScheduleCongestionUpdate ();
void HandleCongestionUpdateTimer (const boost::system::error_code& ecode); void HandleCongestionUpdateTimer (const boost::system::error_code& ecode);
void UpdateCongestion ();
void ScheduleCleanupTimer ();
void HandleCleanupTimer (const boost::system::error_code& ecode);
private: private:
@ -242,7 +229,7 @@ namespace garlic
std::shared_ptr<i2p::garlic::RouterIncomingRatchetSession> m_ECIESSession; std::shared_ptr<i2p::garlic::RouterIncomingRatchetSession> m_ECIESSession;
uint64_t m_LastUpdateTime; // in seconds uint64_t m_LastUpdateTime; // in seconds
bool m_AcceptsTunnels, m_IsFloodfill; bool m_AcceptsTunnels, m_IsFloodfill;
uint64_t m_StartupTime; // monotonic seconds std::chrono::time_point<std::chrono::steady_clock> m_StartupTime;
uint64_t m_BandwidthLimit; // allowed bandwidth uint64_t m_BandwidthLimit; // allowed bandwidth
int m_ShareRatio; int m_ShareRatio;
RouterStatus m_Status, m_StatusV6; RouterStatus m_Status, m_StatusV6;
@ -256,11 +243,10 @@ namespace garlic
i2p::crypto::NoiseSymmetricState m_InitialNoiseState, m_CurrentNoiseState; i2p::crypto::NoiseSymmetricState m_InitialNoiseState, m_CurrentNoiseState;
// publish // publish
std::unique_ptr<RouterService> m_Service; std::unique_ptr<RouterService> m_Service;
std::unique_ptr<boost::asio::deadline_timer> m_PublishTimer, m_CongestionUpdateTimer, m_CleanupTimer; std::unique_ptr<boost::asio::deadline_timer> m_PublishTimer, m_CongestionUpdateTimer;
std::unordered_set<i2p::data::IdentHash> m_PublishExcluded; std::set<i2p::data::IdentHash> m_PublishExcluded;
uint32_t m_PublishReplyToken; uint32_t m_PublishReplyToken;
bool m_IsHiddenMode; // not publish bool m_IsHiddenMode; // not publish
mutable std::mutex m_RouterInfoMutex;
}; };
extern RouterContext context; extern RouterContext context;

@ -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 * This file is part of Purple i2pd project and licensed under BSD3
* *
@ -21,7 +21,6 @@
#include "Base.h" #include "Base.h"
#include "Timestamp.h" #include "Timestamp.h"
#include "Log.h" #include "Log.h"
#include "Transports.h"
#include "NetDb.hpp" #include "NetDb.hpp"
#include "RouterContext.h" #include "RouterContext.h"
#include "RouterInfo.h" #include "RouterInfo.h"
@ -34,7 +33,6 @@ namespace data
{ {
if (len > size ()) len = size (); if (len > size ()) len = size ();
memcpy (data (), buf, len); memcpy (data (), buf, len);
m_BufferLen = len;
} }
RouterInfo::RouterInfo (): m_Buffer (nullptr) RouterInfo::RouterInfo (): m_Buffer (nullptr)
@ -43,8 +41,8 @@ namespace data
} }
RouterInfo::RouterInfo (const std::string& fullPath): RouterInfo::RouterInfo (const std::string& fullPath):
m_FamilyID (0), m_IsUpdated (false), m_IsUnreachable (false), m_IsFloodfill (false), m_FamilyID (0), m_IsUpdated (false), m_IsUnreachable (false),
m_SupportedTransports (0),m_ReachableTransports (0), m_PublishedTransports (0), m_SupportedTransports (0),m_ReachableTransports (0),
m_Caps (0), m_Version (0), m_Congestion (eLowCongestion) m_Caps (0), m_Version (0), m_Congestion (eLowCongestion)
{ {
m_Addresses = boost::make_shared<Addresses>(); // create empty list m_Addresses = boost::make_shared<Addresses>(); // create empty list
@ -53,15 +51,15 @@ namespace data
} }
RouterInfo::RouterInfo (std::shared_ptr<Buffer>&& buf, size_t len): RouterInfo::RouterInfo (std::shared_ptr<Buffer>&& buf, size_t len):
m_FamilyID (0), m_IsUpdated (true), m_IsUnreachable (false), m_IsFloodfill (false), m_FamilyID (0), m_IsUpdated (true), m_IsUnreachable (false),
m_SupportedTransports (0), m_ReachableTransports (0), m_PublishedTransports (0), m_SupportedTransports (0), m_ReachableTransports (0),
m_Caps (0), m_Version (0), m_Congestion (eLowCongestion) m_Caps (0), m_Version (0), m_Congestion (eLowCongestion)
{ {
if (len <= MAX_RI_BUFFER_SIZE) if (len <= MAX_RI_BUFFER_SIZE)
{ {
m_Addresses = boost::make_shared<Addresses>(); // create empty list m_Addresses = boost::make_shared<Addresses>(); // create empty list
m_Buffer = buf; m_Buffer = buf;
if (m_Buffer) m_Buffer->SetBufferLen (len); m_BufferLen = len;
ReadFromBuffer (true); ReadFromBuffer (true);
} }
else else
@ -97,8 +95,7 @@ namespace data
m_IsUnreachable = false; m_IsUnreachable = false;
m_SupportedTransports = 0; m_SupportedTransports = 0;
m_ReachableTransports = 0; m_ReachableTransports = 0;
m_PublishedTransports = 0; m_Caps = 0;
m_Caps = 0; m_IsFloodfill = false;
// don't clean up m_Addresses, it will be replaced in ReadFromStream // don't clean up m_Addresses, it will be replaced in ReadFromStream
ClearProperties (); ClearProperties ();
// skip identity // skip identity
@ -130,17 +127,16 @@ namespace data
if (s.is_open ()) if (s.is_open ())
{ {
s.seekg (0,std::ios::end); s.seekg (0,std::ios::end);
size_t bufferLen = s.tellg (); m_BufferLen = s.tellg ();
if (bufferLen < 40 || bufferLen > MAX_RI_BUFFER_SIZE) if (m_BufferLen < 40 || m_BufferLen > MAX_RI_BUFFER_SIZE)
{ {
LogPrint(eLogError, "RouterInfo: File ", fullPath, " is malformed"); LogPrint(eLogError, "RouterInfo: File", fullPath, " is malformed");
return false; return false;
} }
s.seekg(0, std::ios::beg); s.seekg(0, std::ios::beg);
if (!m_Buffer) if (!m_Buffer)
m_Buffer = NewBuffer (); m_Buffer = NewBuffer ();
s.read((char *)m_Buffer->data (), bufferLen); s.read((char *)m_Buffer->data (), m_BufferLen);
m_Buffer->SetBufferLen (bufferLen);
} }
else else
{ {
@ -165,12 +161,11 @@ namespace data
m_IsUnreachable = true; m_IsUnreachable = true;
return; return;
} }
size_t bufferLen = m_Buffer->GetBufferLen (); m_RouterIdentity = NewIdentity (m_Buffer->data (), m_BufferLen);
m_RouterIdentity = NewIdentity (m_Buffer->data (), bufferLen);
size_t identityLen = m_RouterIdentity->GetFullLen (); size_t identityLen = m_RouterIdentity->GetFullLen ();
if (identityLen >= bufferLen) if (identityLen >= m_BufferLen)
{ {
LogPrint (eLogError, "RouterInfo: Identity length ", identityLen, " exceeds buffer size ", bufferLen); LogPrint (eLogError, "RouterInfo: Identity length ", identityLen, " exceeds buffer size ", m_BufferLen);
m_IsUnreachable = true; m_IsUnreachable = true;
return; return;
} }
@ -184,7 +179,7 @@ namespace data
return; return;
} }
// verify signature // verify signature
int l = bufferLen - m_RouterIdentity->GetSignatureLen (); int l = m_BufferLen - m_RouterIdentity->GetSignatureLen ();
if (l < 0 || !m_RouterIdentity->Verify ((uint8_t *)m_Buffer->data (), l, (uint8_t *)m_Buffer->data () + l)) if (l < 0 || !m_RouterIdentity->Verify ((uint8_t *)m_Buffer->data (), l, (uint8_t *)m_Buffer->data () + l))
{ {
LogPrint (eLogError, "RouterInfo: Signature verification failed"); LogPrint (eLogError, "RouterInfo: Signature verification failed");
@ -194,7 +189,7 @@ namespace data
} }
// parse RI // parse RI
std::stringstream str; std::stringstream str;
str.write ((const char *)m_Buffer->data () + identityLen, bufferLen - identityLen); str.write ((const char *)m_Buffer->data () + identityLen, m_BufferLen - identityLen);
ReadFromStream (str); ReadFromStream (str);
if (!str) if (!str)
{ {
@ -220,7 +215,7 @@ namespace data
uint8_t cost; // ignore uint8_t cost; // ignore
s.read ((char *)&cost, sizeof (cost)); s.read ((char *)&cost, sizeof (cost));
s.read ((char *)&address->date, sizeof (address->date)); s.read ((char *)&address->date, sizeof (address->date));
bool isHost = false, isStaticKey = false, isV2 = false, isIntroKey = false; bool isHost = false, isStaticKey = false, isV2 = false;
char transportStyle[6]; char transportStyle[6];
ReadString (transportStyle, 6, s); ReadString (transportStyle, 6, s);
if (!strncmp (transportStyle, "NTCP", 4)) // NTCP or NTCP2 if (!strncmp (transportStyle, "NTCP", 4)) // NTCP or NTCP2
@ -258,7 +253,7 @@ namespace data
address->host = boost::asio::ip::address::from_string (value, ecode); address->host = boost::asio::ip::address::from_string (value, ecode);
if (!ecode && !address->host.is_unspecified ()) if (!ecode && !address->host.is_unspecified ())
{ {
if (!i2p::transport::transports.IsInReservedRange (address->host) || if (!i2p::util::net::IsInReservedRange (address->host) ||
i2p::util::net::IsYggdrasilAddress (address->host)) i2p::util::net::IsYggdrasilAddress (address->host))
isHost = true; isHost = true;
else else
@ -297,38 +292,26 @@ namespace data
address->caps = ExtractAddressCaps (value); address->caps = ExtractAddressCaps (value);
else if (!strcmp (key, "s")) // ntcp2 or ssu2 static key else if (!strcmp (key, "s")) // ntcp2 or ssu2 static key
{ {
if (Base64ToByteStream (value, strlen (value), address->s, 32) == 32 && Base64ToByteStream (value, strlen (value), address->s, 32);
!(address->s[31] & 0x80)) // check if x25519 public key if (!(address->s[31] & 0x80)) // check if x25519 public key
isStaticKey = true; isStaticKey = true;
else
address->transportStyle = eTransportUnknown; // invalid address
} }
else if (!strcmp (key, "i")) // ntcp2 iv or ssu2 intro else if (!strcmp (key, "i")) // ntcp2 iv or ssu2 intro
{ {
if (address->IsNTCP2 ()) if (address->IsNTCP2 ())
{ {
if (Base64ToByteStream (value, strlen (value), address->i, 16) == 16) Base64ToByteStream (value, strlen (value), address->i, 16);
address->published = true; // presence of "i" means "published" NTCP2 address->published = true; // presence of "i" means "published" NTCP2
else
address->transportStyle = eTransportUnknown; // invalid address
} }
else if (address->IsSSU2 ()) else if (address->IsSSU2 ())
{ Base64ToByteStream (value, strlen (value), address->i, 32);
if (Base64ToByteStream (value, strlen (value), address->i, 32) == 32)
isIntroKey = true;
else
address->transportStyle = eTransportUnknown; // invalid address
}
} }
else if (!strcmp (key, "v")) else if (!strcmp (key, "v"))
{ {
if (!strcmp (value, "2")) if (!strcmp (value, "2"))
isV2 = true; isV2 = true;
else else
{
LogPrint (eLogWarning, "RouterInfo: Unexpected value ", value, " for v"); LogPrint (eLogWarning, "RouterInfo: Unexpected value ", value, " for v");
address->transportStyle = eTransportUnknown; // invalid address
}
} }
else if (key[0] == 'i') else if (key[0] == 'i')
{ {
@ -391,7 +374,7 @@ namespace data
supportedTransports |= (i2p::util::net::IsYggdrasilAddress (address->host) ? eNTCP2V6Mesh : eNTCP2V6); supportedTransports |= (i2p::util::net::IsYggdrasilAddress (address->host) ? eNTCP2V6Mesh : eNTCP2V6);
else else
supportedTransports |= eNTCP2V4; supportedTransports |= eNTCP2V4;
m_PublishedTransports |= supportedTransports; m_ReachableTransports |= supportedTransports;
} }
else else
{ {
@ -406,17 +389,17 @@ namespace data
} }
} }
} }
else if (address->transportStyle == eTransportSSU2 && isV2 && isStaticKey && isIntroKey) else if (address->transportStyle == eTransportSSU2 && isV2 && isStaticKey)
{ {
if (address->IsV4 ()) supportedTransports |= eSSU2V4; if (address->IsV4 ()) supportedTransports |= eSSU2V4;
if (address->IsV6 ()) supportedTransports |= eSSU2V6; if (address->IsV6 ()) supportedTransports |= eSSU2V6;
if (isHost && address->port) if (isHost && address->port)
{ {
if (address->host.is_v4 ()) m_PublishedTransports |= eSSU2V4; if (address->host.is_v4 ()) m_ReachableTransports |= eSSU2V4;
if (address->host.is_v6 ()) m_PublishedTransports |= eSSU2V6; if (address->host.is_v6 ()) m_ReachableTransports |= eSSU2V6;
address->published = true; address->published = true;
} }
else if (address->ssu && !address->ssu->introducers.empty ()) if (address->ssu && !address->ssu->introducers.empty ())
{ {
// exclude invalid introducers // exclude invalid introducers
uint32_t ts = i2p::util::GetSecondsSinceEpoch (); uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
@ -436,7 +419,6 @@ namespace data
m_SupportedTransports |= supportedTransports; m_SupportedTransports |= supportedTransports;
} }
} }
m_ReachableTransports |= m_PublishedTransports;
// update addresses // update addresses
#if (BOOST_VERSION >= 105300) #if (BOOST_VERSION >= 105300)
boost::atomic_store (&m_Addresses, addresses); boost::atomic_store (&m_Addresses, addresses);
@ -466,10 +448,7 @@ namespace data
// extract caps // extract caps
if (!strcmp (key, "caps")) if (!strcmp (key, "caps"))
{
ExtractCaps (value); ExtractCaps (value);
m_IsFloodfill = IsDeclaredFloodfill ();
}
// extract version // extract version
else if (!strcmp (key, ROUTER_INFO_PROPERTY_VERSION)) else if (!strcmp (key, ROUTER_INFO_PROPERTY_VERSION))
{ {
@ -536,6 +515,7 @@ namespace data
break; break;
case CAPS_FLAG_HIGH_BANDWIDTH1: case CAPS_FLAG_HIGH_BANDWIDTH1:
case CAPS_FLAG_HIGH_BANDWIDTH2: case CAPS_FLAG_HIGH_BANDWIDTH2:
case CAPS_FLAG_HIGH_BANDWIDTH3:
m_Caps |= Caps::eHighBandwidth; m_Caps |= Caps::eHighBandwidth;
break; break;
case CAPS_FLAG_EXTRA_BANDWIDTH1: case CAPS_FLAG_EXTRA_BANDWIDTH1:
@ -628,19 +608,6 @@ namespace data
return m_Buffer->data (); return m_Buffer->data ();
} }
bool RouterInfo::SaveToFile (const std::string& fullPath, std::shared_ptr<Buffer> buf)
{
if (!buf) return false;
std::ofstream f (fullPath, std::ofstream::binary | std::ofstream::out);
if (!f.is_open ())
{
LogPrint (eLogError, "RouterInfo: Can't save to ", fullPath);
return false;
}
f.write ((char *)buf->data (), buf->GetBufferLen ());
return true;
}
bool RouterInfo::SaveToFile (const std::string& fullPath) bool RouterInfo::SaveToFile (const std::string& fullPath)
{ {
if (m_IsUnreachable) return false; // don't save bad router if (m_IsUnreachable) return false; // don't save bad router
@ -649,7 +616,14 @@ namespace data
LogPrint (eLogWarning, "RouterInfo: Can't save, m_Buffer == NULL"); LogPrint (eLogWarning, "RouterInfo: Can't save, m_Buffer == NULL");
return false; return false;
} }
return SaveToFile (fullPath, m_Buffer); std::ofstream f (fullPath, std::ofstream::binary | std::ofstream::out);
if (!f.is_open ())
{
LogPrint (eLogError, "RouterInfo: Can't save to ", fullPath);
return false;
}
f.write ((char *)m_Buffer->data (), m_BufferLen);
return true;
} }
size_t RouterInfo::ReadString (char * str, size_t len, std::istream& s) const size_t RouterInfo::ReadString (char * str, size_t len, std::istream& s) const
@ -1021,19 +995,14 @@ namespace data
bool RouterInfo::IsPublished (bool v4) const bool RouterInfo::IsPublished (bool v4) const
{ {
if (m_Caps & (eUnreachable | eHidden)) return false; // if router sets U or H we assume that all addresses are not published if (m_Caps & (eUnreachable | eHidden)) return false; // if router sets U or H we assume that all addreses are not published
return IsPublishedOn (v4 ? (eNTCP2V4 | eSSU2V4) : (eNTCP2V6 | eSSU2V6)); auto addr = GetAddresses ();
} if (v4)
return ((*addr)[eNTCP2V4Idx] && ((*addr)[eNTCP2V4Idx])->published) ||
bool RouterInfo::IsPublishedOn (CompatibleTransports transports) const ((*addr)[eSSU2V4Idx] && ((*addr)[eSSU2V4Idx])->published);
{ else
return m_PublishedTransports & transports; return ((*addr)[eNTCP2V6Idx] && ((*addr)[eNTCP2V6Idx])->published) ||
} ((*addr)[eSSU2V6Idx] && ((*addr)[eSSU2V6Idx])->published);
bool RouterInfo::IsNAT2NATOnly (const RouterInfo& other) const
{
return !(m_PublishedTransports & other.m_SupportedTransports) &&
!(other.m_PublishedTransports & m_SupportedTransports);
} }
bool RouterInfo::IsSSU2PeerTesting (bool v4) const bool RouterInfo::IsSSU2PeerTesting (bool v4) const
@ -1122,15 +1091,9 @@ namespace data
m_Buffer = NewBuffer (); m_Buffer = NewBuffer ();
if (len > m_Buffer->size ()) len = m_Buffer->size (); if (len > m_Buffer->size ()) len = m_Buffer->size ();
memcpy (m_Buffer->data (), buf, len); memcpy (m_Buffer->data (), buf, len);
m_Buffer->SetBufferLen (len); m_BufferLen = len;
} }
std::shared_ptr<RouterInfo::Buffer> RouterInfo::CopyBuffer () const
{
if (!m_Buffer) return nullptr;
return netdb.NewRouterInfoBuffer (*m_Buffer);
}
std::shared_ptr<RouterInfo::Buffer> RouterInfo::NewBuffer () const std::shared_ptr<RouterInfo::Buffer> RouterInfo::NewBuffer () const
{ {
return netdb.NewRouterInfoBuffer (); return netdb.NewRouterInfoBuffer ();
@ -1214,7 +1177,7 @@ namespace data
CAPS_FLAG_EXTRA_BANDWIDTH2 : // 'X' CAPS_FLAG_EXTRA_BANDWIDTH2 : // 'X'
CAPS_FLAG_EXTRA_BANDWIDTH1; // 'P' CAPS_FLAG_EXTRA_BANDWIDTH1; // 'P'
else else
caps += CAPS_FLAG_HIGH_BANDWIDTH2; // 'O' caps += CAPS_FLAG_HIGH_BANDWIDTH3; // 'O'
caps += CAPS_FLAG_FLOODFILL; // floodfill caps += CAPS_FLAG_FLOODFILL; // floodfill
} }
else else
@ -1222,7 +1185,7 @@ namespace data
if (c & eExtraBandwidth) if (c & eExtraBandwidth)
caps += (c & eHighBandwidth) ? CAPS_FLAG_EXTRA_BANDWIDTH2 /* 'X' */ : CAPS_FLAG_EXTRA_BANDWIDTH1; /*'P' */ caps += (c & eHighBandwidth) ? CAPS_FLAG_EXTRA_BANDWIDTH2 /* 'X' */ : CAPS_FLAG_EXTRA_BANDWIDTH1; /*'P' */
else else
caps += (c & eHighBandwidth) ? CAPS_FLAG_HIGH_BANDWIDTH2 /* 'O' */: CAPS_FLAG_LOW_BANDWIDTH2 /* 'L' */; // bandwidth caps += (c & eHighBandwidth) ? CAPS_FLAG_HIGH_BANDWIDTH3 /* 'O' */: CAPS_FLAG_LOW_BANDWIDTH2 /* 'L' */; // bandwidth
} }
if (c & eHidden) caps += CAPS_FLAG_HIDDEN; // hidden if (c & eHidden) caps += CAPS_FLAG_HIDDEN; // hidden
if (c & eReachable) caps += CAPS_FLAG_REACHABLE; // reachable if (c & eReachable) caps += CAPS_FLAG_REACHABLE; // reachable
@ -1462,20 +1425,6 @@ namespace data
return ""; return "";
} }
void LocalRouterInfo::UpdateFloodfillProperty (bool floodfill)
{
if (floodfill)
{
UpdateCaps (GetCaps () | i2p::data::RouterInfo::eFloodfill);
SetFloodfill ();
}
else
{
UpdateCaps (GetCaps () & ~i2p::data::RouterInfo::eFloodfill);
ResetFloodfill ();
}
}
void LocalRouterInfo::WriteString (const std::string& str, std::ostream& s) const void LocalRouterInfo::WriteString (const std::string& str, std::ostream& s) const
{ {
uint8_t len = str.size (); uint8_t len = str.size ();

@ -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 * This file is part of Purple i2pd project and licensed under BSD3
* *
@ -39,15 +39,11 @@ namespace data
/* bandwidth flags */ /* bandwidth flags */
const char CAPS_FLAG_LOW_BANDWIDTH1 = 'K'; /* < 12 KBps */ const char CAPS_FLAG_LOW_BANDWIDTH1 = 'K'; /* < 12 KBps */
const char CAPS_FLAG_LOW_BANDWIDTH2 = 'L'; /* 12-48 KBps */ const char CAPS_FLAG_LOW_BANDWIDTH2 = 'L'; /* 12-48 KBps */
const char CAPS_FLAG_LOW_BANDWIDTH3 = 'M'; /* 48-64 KBps */ const char CAPS_FLAG_HIGH_BANDWIDTH1 = 'M'; /* 48-64 KBps */
const char CAPS_FLAG_HIGH_BANDWIDTH1 = 'N'; /* 64-128 KBps */ const char CAPS_FLAG_HIGH_BANDWIDTH2 = 'N'; /* 64-128 KBps */
const char CAPS_FLAG_HIGH_BANDWIDTH2 = 'O'; /* 128-256 KBps */ const char CAPS_FLAG_HIGH_BANDWIDTH3 = 'O'; /* 128-256 KBps */
const char CAPS_FLAG_EXTRA_BANDWIDTH1 = 'P'; /* 256-2048 KBps */ const char CAPS_FLAG_EXTRA_BANDWIDTH1 = 'P'; /* 256-2000 KBps */
const char CAPS_FLAG_EXTRA_BANDWIDTH2 = 'X'; /* > 2048 KBps */ const char CAPS_FLAG_EXTRA_BANDWIDTH2 = 'X'; /* > 2000 KBps */
// bandwidth limits in kBps
const uint32_t LOW_BANDWIDTH_LIMIT = 48;
const uint32_t HIGH_BANDWIDTH_LIMIT = 256;
const uint32_t EXTRA_BANDWIDTH_LIMIT = 2048;
// congesion flags // congesion flags
const char CAPS_FLAG_MEDIUM_CONGESTION = 'D'; const char CAPS_FLAG_MEDIUM_CONGESTION = 'D';
const char CAPS_FLAG_HIGH_CONGESTION = 'E'; const char CAPS_FLAG_HIGH_CONGESTION = 'E';
@ -188,14 +184,6 @@ namespace data
Buffer () = default; Buffer () = default;
Buffer (const uint8_t * buf, size_t len); Buffer (const uint8_t * buf, size_t len);
Buffer (const Buffer& other): Buffer (other.data (), other.m_BufferLen) {};
size_t GetBufferLen () const { return m_BufferLen; };
void SetBufferLen (size_t len) { m_BufferLen = len; };
private:
size_t m_BufferLen = 0;
}; };
typedef std::array<std::shared_ptr<Address>, eNumTransports> Addresses; typedef std::array<std::shared_ptr<Address>, eNumTransports> Addresses;
@ -235,9 +223,8 @@ namespace data
void SetUnreachableAddressesTransportCaps (uint8_t transports); // bitmask of AddressCaps void SetUnreachableAddressesTransportCaps (uint8_t transports); // bitmask of AddressCaps
void UpdateSupportedTransports (); void UpdateSupportedTransports ();
void UpdateIntroducers (uint64_t ts); // ts in seconds void UpdateIntroducers (uint64_t ts); // ts in seconds
bool IsFloodfill () const { return m_IsFloodfill; }; bool IsFloodfill () const { return m_Caps & Caps::eFloodfill; };
void SetFloodfill () { m_IsFloodfill = true; }; void ResetFlooldFill () { m_Caps &= ~Caps::eFloodfill; };
void ResetFloodfill () { m_IsFloodfill = false; };
bool IsECIES () const { return m_RouterIdentity->GetCryptoKeyType () == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD; }; bool IsECIES () const { return m_RouterIdentity->GetCryptoKeyType () == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD; };
bool IsNTCP2 (bool v4only = true) const; bool IsNTCP2 (bool v4only = true) const;
bool IsNTCP2V6 () const { return m_SupportedTransports & eNTCP2V6; }; bool IsNTCP2V6 () const { return m_SupportedTransports & eNTCP2V6; };
@ -256,16 +243,12 @@ namespace data
bool IsReachableFrom (const RouterInfo& other) const { return m_ReachableTransports & other.m_SupportedTransports; }; bool IsReachableFrom (const RouterInfo& other) const { return m_ReachableTransports & other.m_SupportedTransports; };
bool IsReachableBy (CompatibleTransports transports) const { return m_ReachableTransports & transports; }; bool IsReachableBy (CompatibleTransports transports) const { return m_ReachableTransports & transports; };
CompatibleTransports GetCompatibleTransports (bool incoming) const { return incoming ? m_ReachableTransports : m_SupportedTransports; }; CompatibleTransports GetCompatibleTransports (bool incoming) const { return incoming ? m_ReachableTransports : m_SupportedTransports; };
CompatibleTransports GetPublishedTransports () const { return m_PublishedTransports; };
bool HasValidAddresses () const { return m_SupportedTransports; }; bool HasValidAddresses () const { return m_SupportedTransports; };
bool IsHidden () const { return m_Caps & eHidden; }; bool IsHidden () const { return m_Caps & eHidden; };
bool IsHighBandwidth () const { return m_Caps & RouterInfo::eHighBandwidth; }; bool IsHighBandwidth () const { return m_Caps & RouterInfo::eHighBandwidth; };
bool IsExtraBandwidth () const { return m_Caps & RouterInfo::eExtraBandwidth; }; bool IsExtraBandwidth () const { return m_Caps & RouterInfo::eExtraBandwidth; };
bool IsEligibleFloodfill () const; bool IsEligibleFloodfill () const;
bool IsDeclaredFloodfill () const { return m_Caps & RouterInfo::eFloodfill; };
bool IsPublished (bool v4) const; bool IsPublished (bool v4) const;
bool IsPublishedOn (CompatibleTransports transports) const;
bool IsNAT2NATOnly (const RouterInfo& other) const; // only NAT-to-NAT connection is possible
bool IsSSU2PeerTesting (bool v4) const; bool IsSSU2PeerTesting (bool v4) const;
bool IsSSU2Introducer (bool v4) const; bool IsSSU2Introducer (bool v4) const;
bool IsHighCongestion (bool highBandwidth) const; bool IsHighCongestion (bool highBandwidth) const;
@ -281,21 +264,17 @@ namespace data
const uint8_t * GetBuffer () const { return m_Buffer ? m_Buffer->data () : nullptr; }; const uint8_t * GetBuffer () const { return m_Buffer ? m_Buffer->data () : nullptr; };
const uint8_t * LoadBuffer (const std::string& fullPath); // load if necessary const uint8_t * LoadBuffer (const std::string& fullPath); // load if necessary
size_t GetBufferLen () const { return m_Buffer ? m_Buffer->GetBufferLen () : 0; }; size_t GetBufferLen () const { return m_BufferLen; };
void DeleteBuffer () { m_Buffer = nullptr; };
std::shared_ptr<Buffer> GetSharedBuffer () const { return m_Buffer; };
std::shared_ptr<Buffer> CopyBuffer () const;
bool IsUpdated () const { return m_IsUpdated; }; bool IsUpdated () const { return m_IsUpdated; };
void SetUpdated (bool updated) { m_IsUpdated = updated; }; void SetUpdated (bool updated) { m_IsUpdated = updated; };
bool SaveToFile (const std::string& fullPath); bool SaveToFile (const std::string& fullPath);
static bool SaveToFile (const std::string& fullPath, std::shared_ptr<Buffer> buf);
std::shared_ptr<RouterProfile> GetProfile () const; std::shared_ptr<RouterProfile> GetProfile () const;
void DropProfile () { m_Profile = nullptr; }; void DropProfile () { m_Profile = nullptr; };
bool HasProfile () const { return (bool)m_Profile; };
bool Update (const uint8_t * buf, size_t len); bool Update (const uint8_t * buf, size_t len);
void DeleteBuffer () { m_Buffer = nullptr; };
bool IsNewer (const uint8_t * buf, size_t len) const; bool IsNewer (const uint8_t * buf, size_t len) const;
/** return true if we are in a router family and the signature is valid */ /** return true if we are in a router family and the signature is valid */
@ -312,7 +291,7 @@ namespace data
RouterInfo (); RouterInfo ();
uint8_t * GetBufferPointer (size_t offset = 0 ) { return m_Buffer->data () + offset; }; uint8_t * GetBufferPointer (size_t offset = 0 ) { return m_Buffer->data () + offset; };
void UpdateBuffer (const uint8_t * buf, size_t len); void UpdateBuffer (const uint8_t * buf, size_t len);
void SetBufferLen (size_t len) { if (m_Buffer) m_Buffer->SetBufferLen (len); }; void SetBufferLen (size_t len) { m_BufferLen = len; };
void RefreshTimestamp (); void RefreshTimestamp ();
CompatibleTransports GetReachableTransports () const { return m_ReachableTransports; }; CompatibleTransports GetReachableTransports () const { return m_ReachableTransports; };
void SetReachableTransports (CompatibleTransports transports) { m_ReachableTransports = transports; }; void SetReachableTransports (CompatibleTransports transports) { m_ReachableTransports = transports; };
@ -340,10 +319,11 @@ namespace data
FamilyID m_FamilyID; FamilyID m_FamilyID;
std::shared_ptr<const IdentityEx> m_RouterIdentity; std::shared_ptr<const IdentityEx> m_RouterIdentity;
std::shared_ptr<Buffer> m_Buffer; std::shared_ptr<Buffer> m_Buffer;
size_t m_BufferLen;
uint64_t m_Timestamp; // in milliseconds uint64_t m_Timestamp; // in milliseconds
boost::shared_ptr<Addresses> m_Addresses; // TODO: use std::shared_ptr and std::atomic_store for gcc >= 4.9 boost::shared_ptr<Addresses> m_Addresses; // TODO: use std::shared_ptr and std::atomic_store for gcc >= 4.9
bool m_IsUpdated, m_IsUnreachable, m_IsFloodfill; bool m_IsUpdated, m_IsUnreachable;
CompatibleTransports m_SupportedTransports, m_ReachableTransports, m_PublishedTransports; CompatibleTransports m_SupportedTransports, m_ReachableTransports;
uint8_t m_Caps; uint8_t m_Caps;
int m_Version; int m_Version;
Congestion m_Congestion; Congestion m_Congestion;
@ -363,8 +343,7 @@ namespace data
void DeleteProperty (const std::string& key); void DeleteProperty (const std::string& key);
std::string GetProperty (const std::string& key) const; std::string GetProperty (const std::string& key) const;
void ClearProperties () override { m_Properties.clear (); }; void ClearProperties () override { m_Properties.clear (); };
void UpdateFloodfillProperty (bool floodfill);
bool AddSSU2Introducer (const Introducer& introducer, bool v4); bool AddSSU2Introducer (const Introducer& introducer, bool v4);
bool RemoveSSU2Introducer (const IdentHash& h, bool v4); bool RemoveSSU2Introducer (const IdentHash& h, bool v4);

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2022-2024, The PurpleI2P Project * Copyright (c) 2022-2023, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@ -24,8 +24,7 @@ namespace transport
m_AddressV4 (boost::asio::ip::address_v4()), m_AddressV6 (boost::asio::ip::address_v6()), m_AddressV4 (boost::asio::ip::address_v4()), m_AddressV6 (boost::asio::ip::address_v6()),
m_TerminationTimer (GetService ()), m_CleanupTimer (GetService ()), m_ResendTimer (GetService ()), m_TerminationTimer (GetService ()), m_CleanupTimer (GetService ()), m_ResendTimer (GetService ()),
m_IntroducersUpdateTimer (GetService ()), m_IntroducersUpdateTimerV6 (GetService ()), m_IntroducersUpdateTimer (GetService ()), m_IntroducersUpdateTimerV6 (GetService ()),
m_IsPublished (true), m_IsSyncClockFromPeers (true), m_PendingTimeOffset (0), m_IsPublished (true), m_IsSyncClockFromPeers (true), m_IsThroughProxy (false)
m_Rng(i2p::util::GetMonotonicMicroseconds ()%1000000LL), m_IsThroughProxy (false)
{ {
} }
@ -210,42 +209,6 @@ namespace transport
return ep.port (); return ep.port ();
} }
void SSU2Server::AdjustTimeOffset (int64_t offset, std::shared_ptr<const i2p::data::IdentityEx> from)
{
if (offset)
{
if (m_PendingTimeOffset) // one more
{
if (m_PendingTimeOffsetFrom && from &&
m_PendingTimeOffsetFrom->GetIdentHash ().GetLL()[0] != from->GetIdentHash ().GetLL()[0]) // from different routers
{
if (std::abs (m_PendingTimeOffset - offset) < SSU2_CLOCK_SKEW)
{
offset = (m_PendingTimeOffset + offset)/2; // average
LogPrint (eLogWarning, "SSU2: Clock adjusted by ", offset, " seconds");
i2p::util::AdjustTimeOffset (offset);
}
else
LogPrint (eLogWarning, "SSU2: Time offsets are too different. Clock not adjusted");
m_PendingTimeOffset = 0;
m_PendingTimeOffsetFrom = nullptr;
}
else
LogPrint (eLogWarning, "SSU2: Time offsets from same router. Clock not adjusted");
}
else
{
m_PendingTimeOffset = offset; // first
m_PendingTimeOffsetFrom = from;
}
}
else
{
m_PendingTimeOffset = 0; // reset
m_PendingTimeOffsetFrom = nullptr;
}
}
boost::asio::ip::udp::socket& SSU2Server::OpenSocket (const boost::asio::ip::udp::endpoint& localEndpoint) boost::asio::ip::udp::socket& SSU2Server::OpenSocket (const boost::asio::ip::udp::endpoint& localEndpoint)
{ {
boost::asio::ip::udp::socket& socket = localEndpoint.address ().is_v6 () ? m_SocketV6 : m_SocketV4; boost::asio::ip::udp::socket& socket = localEndpoint.address ().is_v6 () ? m_SocketV6 : m_SocketV4;
@ -256,49 +219,15 @@ namespace transport
socket.open (localEndpoint.protocol ()); socket.open (localEndpoint.protocol ());
if (localEndpoint.address ().is_v6 ()) if (localEndpoint.address ().is_v6 ())
socket.set_option (boost::asio::ip::v6_only (true)); socket.set_option (boost::asio::ip::v6_only (true));
socket.set_option (boost::asio::socket_base::receive_buffer_size (SSU2_SOCKET_RECEIVE_BUFFER_SIZE));
uint64_t bufferSize = i2p::context.GetBandwidthLimit() * 1024 / 5; // max lag = 200ms socket.set_option (boost::asio::socket_base::send_buffer_size (SSU2_SOCKET_SEND_BUFFER_SIZE));
bufferSize = std::max(SSU2_SOCKET_MIN_BUFFER_SIZE, std::min(bufferSize, SSU2_SOCKET_MAX_BUFFER_SIZE));
boost::asio::socket_base::receive_buffer_size receiveBufferSizeSet (bufferSize);
boost::asio::socket_base::send_buffer_size sendBufferSizeSet (bufferSize);
socket.set_option (receiveBufferSizeSet);
socket.set_option (sendBufferSizeSet);
boost::asio::socket_base::receive_buffer_size receiveBufferSizeGet;
boost::asio::socket_base::send_buffer_size sendBufferSizeGet;
socket.get_option (receiveBufferSizeGet);
socket.get_option (sendBufferSizeGet);
if (receiveBufferSizeGet.value () != receiveBufferSizeSet.value () ||
sendBufferSizeGet.value () != sendBufferSizeSet.value ())
{
LogPrint (eLogWarning, "SSU2: Socket receive buffer size: requested = ",
receiveBufferSizeSet.value (), ", got = ", receiveBufferSizeGet.value ());
LogPrint (eLogWarning, "SSU2: Socket send buffer size: requested = ",
sendBufferSizeSet.value (), ", got = ", sendBufferSizeGet.value ());
}
else
{
LogPrint (eLogInfo, "SSU2: Socket receive buffer size: ", receiveBufferSizeGet.value ());
LogPrint (eLogInfo, "SSU2: Socket send buffer size: ", sendBufferSizeGet.value ());
}
socket.non_blocking (true);
}
catch (std::exception& ex )
{
LogPrint (eLogCritical, "SSU2: Failed to open socket on ", localEndpoint.address (), ": ", ex.what());
ThrowFatal ("Unable to start SSU2 transport on ", localEndpoint.address (), ": ", ex.what ());
return socket;
}
try
{
socket.bind (localEndpoint); socket.bind (localEndpoint);
LogPrint (eLogInfo, "SSU2: Start listening on ", localEndpoint); LogPrint (eLogInfo, "SSU2: Start listening on ", localEndpoint);
} }
catch (std::exception& ex ) catch (std::exception& ex )
{ {
LogPrint (eLogWarning, "SSU2: Failed to bind to ", localEndpoint, ": ", ex.what(), ". Actual endpoint is ", socket.local_endpoint ()); LogPrint (eLogCritical, "SSU2: Failed to bind to ", localEndpoint, ": ", ex.what());
// we can continue without binding being firewalled ThrowFatal ("Unable to start SSU2 transport on ", localEndpoint, ": ", ex.what ());
} }
return socket; return socket;
} }
@ -330,15 +259,8 @@ namespace transport
// but better to find out which host were sent it and mark that router as unreachable // but better to find out which host were sent it and mark that router as unreachable
{ {
i2p::transport::transports.UpdateReceivedBytes (bytes_transferred); i2p::transport::transports.UpdateReceivedBytes (bytes_transferred);
if (bytes_transferred < SSU2_MIN_RECEIVED_PACKET_SIZE)
{
// drop too short packets
m_PacketsPool.ReleaseMt (packet);
Receive (socket);
return;
}
packet->len = bytes_transferred; packet->len = bytes_transferred;
boost::system::error_code ec; boost::system::error_code ec;
size_t moreBytes = socket.available (ec); size_t moreBytes = socket.available (ec);
if (!ec && moreBytes) if (!ec && moreBytes)
@ -436,11 +358,7 @@ namespace transport
{ {
auto ident = it->second->GetRemoteIdentity (); auto ident = it->second->GetRemoteIdentity ();
if (ident) if (ident)
{ m_SessionsByRouterHash.erase (ident->GetIdentHash ());
auto it1 = m_SessionsByRouterHash.find (ident->GetIdentHash ());
if (it1 != m_SessionsByRouterHash.end () && it->second == it1->second)
m_SessionsByRouterHash.erase (it1);
}
if (m_LastSession == it->second) if (m_LastSession == it->second)
m_LastSession = nullptr; m_LastSession = nullptr;
m_Sessions.erase (it); m_Sessions.erase (it);
@ -455,12 +373,10 @@ namespace transport
if (ident) if (ident)
{ {
auto ret = m_SessionsByRouterHash.emplace (ident->GetIdentHash (), session); auto ret = m_SessionsByRouterHash.emplace (ident->GetIdentHash (), session);
if (!ret.second && ret.first->second != session) if (!ret.second)
{ {
// session already exists // session already exists
LogPrint (eLogWarning, "SSU2: Session to ", ident->GetIdentHash ().ToBase64 (), " already exists"); LogPrint (eLogWarning, "SSU2: Session to ", ident->GetIdentHash ().ToBase64 (), " already exists");
// move unsent msgs to new session
ret.first->second->MoveSendQueue (session);
// terminate existing // terminate existing
GetService ().post (std::bind (&SSU2Session::RequestTermination, ret.first->second, eSSU2TerminationReasonReplacedByNewSession)); GetService ().post (std::bind (&SSU2Session::RequestTermination, ret.first->second, eSSU2TerminationReasonReplacedByNewSession));
// update session // update session
@ -500,7 +416,7 @@ namespace transport
m_PendingOutgoingSessions.erase (ep); m_PendingOutgoingSessions.erase (ep);
} }
std::shared_ptr<SSU2Session> SSU2Server::GetRandomPeerTestSession ( std::shared_ptr<SSU2Session> SSU2Server::GetRandomSession (
i2p::data::RouterInfo::CompatibleTransports remoteTransports, const i2p::data::IdentHash& excluded) const i2p::data::RouterInfo::CompatibleTransports remoteTransports, const i2p::data::IdentHash& excluded) const
{ {
if (m_Sessions.empty ()) return nullptr; if (m_Sessions.empty ()) return nullptr;
@ -511,7 +427,7 @@ namespace transport
std::advance (it, ind); std::advance (it, ind);
while (it != m_Sessions.end ()) while (it != m_Sessions.end ())
{ {
if ((it->second->GetRemotePeerTestTransports () & remoteTransports) && if ((it->second->GetRemoteTransports () & remoteTransports) &&
it->second->GetRemoteIdentity ()->GetIdentHash () != excluded) it->second->GetRemoteIdentity ()->GetIdentHash () != excluded)
return it->second; return it->second;
it++; it++;
@ -520,7 +436,7 @@ namespace transport
it = m_Sessions.begin (); it = m_Sessions.begin ();
while (it != m_Sessions.end () && ind) while (it != m_Sessions.end () && ind)
{ {
if ((it->second->GetRemotePeerTestTransports () & remoteTransports) && if ((it->second->GetRemoteTransports () & remoteTransports) &&
it->second->GetRemoteIdentity ()->GetIdentHash () != excluded) it->second->GetRemoteIdentity ()->GetIdentHash () != excluded)
return it->second; return it->second;
it++; ind--; it++; ind--;
@ -626,7 +542,7 @@ namespace transport
else else
it1->second->ProcessRetry (buf, len); it1->second->ProcessRetry (buf, len);
} }
else if (!i2p::transport::transports.IsInReservedRange(senderEndpoint.address ()) && senderEndpoint.port ()) else if (!i2p::util::net::IsInReservedRange(senderEndpoint.address ()) && senderEndpoint.port ())
{ {
// assume new incoming session // assume new incoming session
auto session = std::make_shared<SSU2Session> (*this); auto session = std::make_shared<SSU2Session> (*this);
@ -668,10 +584,7 @@ namespace transport
if (!ec) if (!ec)
i2p::transport::transports.UpdateSentBytes (headerLen + payloadLen); i2p::transport::transports.UpdateSentBytes (headerLen + payloadLen);
else else
{ LogPrint (eLogError, "SSU2: Send exception: ", ec.message (), " to ", to);
LogPrint (ec == boost::asio::error::would_block ? eLogInfo : eLogError,
"SSU2: Send exception: ", ec.message (), " to ", to);
}
} }
void SSU2Server::Send (const uint8_t * header, size_t headerLen, const uint8_t * headerX, size_t headerXLen, void SSU2Server::Send (const uint8_t * header, size_t headerLen, const uint8_t * headerX, size_t headerXLen,
@ -705,10 +618,7 @@ namespace transport
if (!ec) if (!ec)
i2p::transport::transports.UpdateSentBytes (headerLen + headerXLen + payloadLen); i2p::transport::transports.UpdateSentBytes (headerLen + headerXLen + payloadLen);
else else
{ LogPrint (eLogError, "SSU2: Send exception: ", ec.message (), " to ", to);
LogPrint (ec == boost::asio::error::would_block ? eLogInfo : eLogError,
"SSU2: Send exception: ", ec.message (), " to ", to);
}
} }
bool SSU2Server::CreateSession (std::shared_ptr<const i2p::data::RouterInfo> router, bool SSU2Server::CreateSession (std::shared_ptr<const i2p::data::RouterInfo> router,
@ -732,7 +642,7 @@ namespace transport
bool isValidEndpoint = !address->host.is_unspecified () && address->port; bool isValidEndpoint = !address->host.is_unspecified () && address->port;
if (isValidEndpoint) if (isValidEndpoint)
{ {
if (i2p::transport::transports.IsInReservedRange(address->host)) return false; if (i2p::util::net::IsInReservedRange(address->host)) return false;
auto s = FindPendingOutgoingSession (boost::asio::ip::udp::endpoint (address->host, address->port)); auto s = FindPendingOutgoingSession (boost::asio::ip::udp::endpoint (address->host, address->port));
if (s) if (s)
{ {
@ -775,112 +685,82 @@ namespace transport
auto address = session->GetAddress (); auto address = session->GetAddress ();
if (!address) return; if (!address) return;
session->WaitForIntroduction (); session->WaitForIntroduction ();
auto ts = i2p::util::GetSecondsSinceEpoch ();
std::vector<int> indices; int i = 0;
// try to find existing session first // try to find existing session first
for (auto& it: address->ssu->introducers) for (auto& it: address->ssu->introducers)
{ {
if (it.iTag && ts < it.iExp) auto it1 = m_SessionsByRouterHash.find (it.iH);
if (it1 != m_SessionsByRouterHash.end ())
{ {
auto it1 = m_SessionsByRouterHash.find (it.iH); it1->second->Introduce (session, it.iTag);
if (it1 != m_SessionsByRouterHash.end ()) return;
{ }
it1->second->Introduce (session, it.iTag);
return;
}
else
indices.push_back(i);
}
i++;
} }
// we have to start a new session to an introducer // we have to start a new session to an introducer
std::vector<i2p::data::IdentHash> newRouters; auto ts = i2p::util::GetSecondsSinceEpoch ();
std::shared_ptr<i2p::data::RouterInfo> r; std::shared_ptr<i2p::data::RouterInfo> r;
std::shared_ptr<const i2p::data::RouterInfo::Address> addr;
uint32_t relayTag = 0; uint32_t relayTag = 0;
if (!indices.empty ()) if (!address->ssu->introducers.empty ())
{ {
std::vector<int> indices;
for (int i = 0; i < (int)address->ssu->introducers.size (); i++) indices.push_back(i);
if (indices.size () > 1) if (indices.size () > 1)
std::shuffle (indices.begin(), indices.end(), m_Rng); std::shuffle (indices.begin(), indices.end(), std::mt19937(std::random_device()()));
for (auto ind: indices) for (auto i: indices)
{ {
const auto& introducer = address->ssu->introducers[ind]; const auto& introducer = address->ssu->introducers[indices[i]];
// introducer is not expired, because in indices if (introducer.iTag && ts < introducer.iExp)
r = i2p::data::netdb.FindRouter (introducer.iH); {
if (r) r = i2p::data::netdb.FindRouter (introducer.iH);
{ if (r && r->IsReachableFrom (i2p::context.GetRouterInfo ()))
if (r->IsPublishedOn (i2p::context.GetRouterInfo ().GetCompatibleTransports (false) & // outgoing
(i2p::data::RouterInfo::eSSU2V4 | i2p::data::RouterInfo::eSSU2V6)))
{ {
relayTag = introducer.iTag; relayTag = introducer.iTag;
addr = address->IsV6 () ? r->GetSSU2V6Address () : r->GetSSU2V4Address (); if (relayTag) break;
if (addr && !addr->host.is_unspecified () && addr->port &&
!i2p::transport::transports.IsInReservedRange(addr->host))
break;
else
{
// address is invalid try another SSU2 address if exists
if (address->IsV4 ())
{
if (i2p::context.SupportsV6 ())
addr = r->GetSSU2V6Address ();
}
else
{
if (i2p::context.SupportsV4 ())
addr = r->GetSSU2V4Address ();
}
if (addr && !addr->host.is_unspecified () && addr->port &&
!i2p::transport::transports.IsInReservedRange(addr->host))
break;
else
{
// all addresses are invalid, try next introducer
relayTag = 0;
addr = nullptr;
}
}
} }
} }
else if (!i2p::data::IsRouterBanned (introducer.iH))
newRouters.push_back (introducer.iH);
} }
} }
if (r) if (r)
{ {
if (relayTag && addr) if (relayTag)
{ {
// introducer and tag found connect to it through SSU2 // introducer and tag found connect to it through SSU2
auto s = FindPendingOutgoingSession (boost::asio::ip::udp::endpoint (addr->host, addr->port)); auto addr = address->IsV6 () ? r->GetSSU2V6Address () : r->GetSSU2V4Address ();
if (!s) if (addr)
{ {
s = std::make_shared<SSU2Session> (*this, r, addr); bool isValidEndpoint = !addr->host.is_unspecified () && addr->port &&
s->SetOnEstablished ([session, s, relayTag]() { s->Introduce (session, relayTag); }); !i2p::util::net::IsInReservedRange(addr->host);
s->Connect (); if (isValidEndpoint)
} {
else auto s = FindPendingOutgoingSession (boost::asio::ip::udp::endpoint (addr->host, addr->port));
{ if (!s)
auto onEstablished = s->GetOnEstablished (); {
if (onEstablished) s = std::make_shared<SSU2Session> (*this, r, addr);
s->SetOnEstablished ([session, s, relayTag, onEstablished]() s->SetOnEstablished ([session, s, relayTag]() { s->Introduce (session, relayTag); });
{ s->Connect ();
onEstablished (); }
s->Introduce (session, relayTag); else
}); {
else auto onEstablished = s->GetOnEstablished ();
s->SetOnEstablished ([session, s, relayTag]() {s->Introduce (session, relayTag); }); if (onEstablished)
s->SetOnEstablished ([session, s, relayTag, onEstablished]()
{
onEstablished ();
s->Introduce (session, relayTag);
});
else
s->SetOnEstablished ([session, s, relayTag]() {s->Introduce (session, relayTag); });
}
}
} }
} }
else
session->Done ();
} }
else else
{ {
// introducers not found, try to request them // introducers not found, try to request them
for (auto& it: newRouters) for (auto& it: address->ssu->introducers)
i2p::data::netdb.RequestDestination (it); if (it.iTag && ts < it.iExp)
session->Done (); // don't wait for connect timeout i2p::data::netdb.RequestDestination (it.iH);
} }
} }
@ -892,11 +772,8 @@ namespace transport
auto it = m_SessionsByRouterHash.find (router->GetIdentHash ()); auto it = m_SessionsByRouterHash.find (router->GetIdentHash ());
if (it != m_SessionsByRouterHash.end ()) if (it != m_SessionsByRouterHash.end ())
{ {
auto remoteAddr = it->second->GetAddress (); auto s = it->second;
if (!remoteAddr || !remoteAddr->IsPeerTesting () || if (it->second->IsEstablished ())
(v4 && !remoteAddr->IsV4 ()) || (!v4 && !remoteAddr->IsV6 ())) return false;
auto s = it->second;
if (s->IsEstablished ())
GetService ().post ([s]() { s->SendPeerTest (); }); GetService ().post ([s]() { s->SendPeerTest (); });
else else
s->SetOnEstablished ([s]() { s->SendPeerTest (); }); s->SetOnEstablished ([s]() { s->SendPeerTest (); });
@ -1005,9 +882,8 @@ namespace transport
void SSU2Server::ScheduleResend (bool more) void SSU2Server::ScheduleResend (bool more)
{ {
m_ResendTimer.expires_from_now (boost::posix_time::milliseconds (more ? m_ResendTimer.expires_from_now (boost::posix_time::milliseconds (more ? SSU2_RESEND_CHECK_MORE_TIMEOUT :
(SSU2_RESEND_CHECK_MORE_TIMEOUT + m_Rng () % SSU2_RESEND_CHECK_MORE_TIMEOUT_VARIANCE): (SSU2_RESEND_CHECK_TIMEOUT + rand () % SSU2_RESEND_CHECK_TIMEOUT_VARIANCE)));
(SSU2_RESEND_CHECK_TIMEOUT + m_Rng () % SSU2_RESEND_CHECK_TIMEOUT_VARIANCE)));
m_ResendTimer.async_wait (std::bind (&SSU2Server::HandleResendTimer, m_ResendTimer.async_wait (std::bind (&SSU2Server::HandleResendTimer,
this, std::placeholders::_1)); this, std::placeholders::_1));
} }
@ -1020,8 +896,7 @@ namespace transport
auto ts = i2p::util::GetMillisecondsSinceEpoch (); auto ts = i2p::util::GetMillisecondsSinceEpoch ();
for (auto it: m_Sessions) for (auto it: m_Sessions)
{ {
if (ts >= it.second->GetLastResendTime () + SSU2_RESEND_CHECK_TIMEOUT) resentPacketsNum += it.second->Resend (ts);
resentPacketsNum += it.second->Resend (ts);
if (resentPacketsNum > SSU2_MAX_RESEND_PACKETS) break; if (resentPacketsNum > SSU2_MAX_RESEND_PACKETS) break;
} }
for (auto it: m_PendingOutgoingSessions) for (auto it: m_PendingOutgoingSessions)
@ -1078,32 +953,30 @@ namespace transport
return ret; return ret;
} }
std::vector<std::shared_ptr<SSU2Session> > SSU2Server::FindIntroducers (int maxNumIntroducers, std::list<std::shared_ptr<SSU2Session> > SSU2Server::FindIntroducers (int maxNumIntroducers,
bool v4, const std::unordered_set<i2p::data::IdentHash>& excluded) const bool v4, const std::set<i2p::data::IdentHash>& excluded) const
{ {
std::vector<std::shared_ptr<SSU2Session> > ret; std::list<std::shared_ptr<SSU2Session> > ret;
if (maxNumIntroducers <= 0) return ret;
auto newer = [](const std::shared_ptr<SSU2Session>& s1, const std::shared_ptr<SSU2Session>& s2) -> bool
{
auto t1 = s1->GetCreationTime (), t2 = s2->GetCreationTime ();
return (t1 != t2) ? (t1 > t2) : (s1->GetConnID () > s2->GetConnID ());
};
std::set<std::shared_ptr<SSU2Session>, decltype (newer)> introducers(newer);
for (const auto& s : m_Sessions) for (const auto& s : m_Sessions)
{ {
if (s.second->IsEstablished () && (s.second->GetRelayTag () && s.second->IsOutgoing ()) && if (s.second->IsEstablished () && (s.second->GetRelayTag () && s.second->IsOutgoing ()) &&
!excluded.count (s.second->GetRemoteIdentity ()->GetIdentHash ()) && !excluded.count (s.second->GetRemoteIdentity ()->GetIdentHash ()) &&
((v4 && (s.second->GetRemoteTransports () & i2p::data::RouterInfo::eSSU2V4)) || ((v4 && (s.second->GetRemoteTransports () & i2p::data::RouterInfo::eSSU2V4)) ||
(!v4 && (s.second->GetRemoteTransports () & i2p::data::RouterInfo::eSSU2V6)))) (!v4 && (s.second->GetRemoteTransports () & i2p::data::RouterInfo::eSSU2V6))))
introducers.insert (s.second); ret.push_back (s.second);
} }
int i = 0; if ((int)ret.size () > maxNumIntroducers)
for (auto it: introducers)
{ {
ret.push_back (it); // shink ret randomly
i++; int sz = ret.size () - maxNumIntroducers;
if (i >= maxNumIntroducers) break; for (int i = 0; i < sz; i++)
} {
auto ind = rand () % ret.size ();
auto it = ret.begin ();
std::advance (it, ind);
ret.erase (it);
}
}
return ret; return ret;
} }
@ -1112,7 +985,7 @@ namespace transport
uint32_t ts = i2p::util::GetSecondsSinceEpoch (); uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
std::list<i2p::data::IdentHash> newList, impliedList; std::list<i2p::data::IdentHash> newList, impliedList;
auto& introducers = v4 ? m_Introducers : m_IntroducersV6; auto& introducers = v4 ? m_Introducers : m_IntroducersV6;
std::unordered_set<i2p::data::IdentHash> excluded; std::set<i2p::data::IdentHash> excluded;
for (const auto& it : introducers) for (const auto& it : introducers)
{ {
std::shared_ptr<SSU2Session> session; std::shared_ptr<SSU2Session> session;
@ -1122,21 +995,22 @@ namespace transport
session = it1->second; session = it1->second;
excluded.insert (it); excluded.insert (it);
} }
if (session && session->IsEstablished () && session->GetRelayTag () && session->IsOutgoing () && // still session with introducer? if (session && session->IsEstablished () && session->GetRelayTag () && session->IsOutgoing ()) // still session with introducer?
ts < session->GetCreationTime () + SSU2_TO_INTRODUCER_SESSION_EXPIRATION) {
{ if (ts < session->GetCreationTime () + SSU2_TO_INTRODUCER_SESSION_EXPIRATION)
session->SendKeepAlive ();
if (ts < session->GetCreationTime () + SSU2_TO_INTRODUCER_SESSION_DURATION)
newList.push_back (it);
else
{ {
impliedList.push_back (it); // keep in introducers list, but not publish session->SendKeepAlive ();
session = nullptr; if (ts < session->GetCreationTime () + SSU2_TO_INTRODUCER_SESSION_DURATION)
} newList.push_back (it);
} else
else {
session = nullptr; impliedList.push_back (it); // keep in introducers list, but not publish
session = nullptr;
}
}
else
session = nullptr;
}
if (!session) if (!session)
i2p::context.RemoveSSU2Introducer (it, v4); i2p::context.RemoveSSU2Introducer (it, v4);
} }
@ -1215,7 +1089,7 @@ namespace transport
if (m_IsPublished) if (m_IsPublished)
{ {
m_IntroducersUpdateTimer.expires_from_now (boost::posix_time::seconds( m_IntroducersUpdateTimer.expires_from_now (boost::posix_time::seconds(
SSU2_KEEP_ALIVE_INTERVAL + m_Rng () % SSU2_KEEP_ALIVE_INTERVAL_VARIANCE)); SSU2_KEEP_ALIVE_INTERVAL + rand () % SSU2_KEEP_ALIVE_INTERVAL_VARIANCE));
m_IntroducersUpdateTimer.async_wait (std::bind (&SSU2Server::HandleIntroducersUpdateTimer, m_IntroducersUpdateTimer.async_wait (std::bind (&SSU2Server::HandleIntroducersUpdateTimer,
this, std::placeholders::_1, true)); this, std::placeholders::_1, true));
} }
@ -1229,7 +1103,7 @@ namespace transport
i2p::context.ClearSSU2Introducers (true); i2p::context.ClearSSU2Introducers (true);
m_Introducers.clear (); m_Introducers.clear ();
m_IntroducersUpdateTimer.expires_from_now (boost::posix_time::seconds( m_IntroducersUpdateTimer.expires_from_now (boost::posix_time::seconds(
(SSU2_KEEP_ALIVE_INTERVAL + m_Rng () % SSU2_KEEP_ALIVE_INTERVAL_VARIANCE)/2)); (SSU2_KEEP_ALIVE_INTERVAL + rand () % SSU2_KEEP_ALIVE_INTERVAL_VARIANCE)/2));
m_IntroducersUpdateTimer.async_wait (std::bind (&SSU2Server::HandleIntroducersUpdateTimer, m_IntroducersUpdateTimer.async_wait (std::bind (&SSU2Server::HandleIntroducersUpdateTimer,
this, std::placeholders::_1, true)); this, std::placeholders::_1, true));
} }
@ -1240,7 +1114,7 @@ namespace transport
if (m_IsPublished) if (m_IsPublished)
{ {
m_IntroducersUpdateTimerV6.expires_from_now (boost::posix_time::seconds( m_IntroducersUpdateTimerV6.expires_from_now (boost::posix_time::seconds(
SSU2_KEEP_ALIVE_INTERVAL + m_Rng () % SSU2_KEEP_ALIVE_INTERVAL_VARIANCE)); SSU2_KEEP_ALIVE_INTERVAL + rand () % SSU2_KEEP_ALIVE_INTERVAL_VARIANCE));
m_IntroducersUpdateTimerV6.async_wait (std::bind (&SSU2Server::HandleIntroducersUpdateTimer, m_IntroducersUpdateTimerV6.async_wait (std::bind (&SSU2Server::HandleIntroducersUpdateTimer,
this, std::placeholders::_1, false)); this, std::placeholders::_1, false));
} }
@ -1254,7 +1128,7 @@ namespace transport
i2p::context.ClearSSU2Introducers (false); i2p::context.ClearSSU2Introducers (false);
m_IntroducersV6.clear (); m_IntroducersV6.clear ();
m_IntroducersUpdateTimerV6.expires_from_now (boost::posix_time::seconds( m_IntroducersUpdateTimerV6.expires_from_now (boost::posix_time::seconds(
(SSU2_KEEP_ALIVE_INTERVAL + m_Rng () % SSU2_KEEP_ALIVE_INTERVAL_VARIANCE)/2)); (SSU2_KEEP_ALIVE_INTERVAL + rand () % SSU2_KEEP_ALIVE_INTERVAL_VARIANCE)/2));
m_IntroducersUpdateTimerV6.async_wait (std::bind (&SSU2Server::HandleIntroducersUpdateTimer, m_IntroducersUpdateTimerV6.async_wait (std::bind (&SSU2Server::HandleIntroducersUpdateTimer,
this, std::placeholders::_1, false)); this, std::placeholders::_1, false));
} }

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2022-2024, The PurpleI2P Project * Copyright (c) 2022-2023, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@ -10,13 +10,9 @@
#define SSU2_H__ #define SSU2_H__
#include <unordered_map> #include <unordered_map>
#include <unordered_set>
#include <vector>
#include <mutex> #include <mutex>
#include <random>
#include "util.h" #include "util.h"
#include "SSU2Session.h" #include "SSU2Session.h"
#include "Socks5.h"
namespace i2p namespace i2p
{ {
@ -24,15 +20,13 @@ namespace transport
{ {
const int SSU2_TERMINATION_CHECK_TIMEOUT = 25; // in seconds const int SSU2_TERMINATION_CHECK_TIMEOUT = 25; // in seconds
const int SSU2_CLEANUP_INTERVAL = 72; // in seconds const int SSU2_CLEANUP_INTERVAL = 72; // in seconds
const int SSU2_RESEND_CHECK_TIMEOUT = 40; // in milliseconds const int SSU2_RESEND_CHECK_TIMEOUT = 400; // in milliseconds
const int SSU2_RESEND_CHECK_TIMEOUT_VARIANCE = 10; // in milliseconds const int SSU2_RESEND_CHECK_TIMEOUT_VARIANCE = 100; // in milliseconds
const int SSU2_RESEND_CHECK_MORE_TIMEOUT = 4; // in milliseconds const int SSU2_RESEND_CHECK_MORE_TIMEOUT = 10; // in milliseconds
const int SSU2_RESEND_CHECK_MORE_TIMEOUT_VARIANCE = 9; // in milliseconds
const size_t SSU2_MAX_RESEND_PACKETS = 128; // packets to resend at the time const size_t SSU2_MAX_RESEND_PACKETS = 128; // packets to resend at the time
const uint64_t SSU2_SOCKET_MIN_BUFFER_SIZE = 128 * 1024; const size_t SSU2_SOCKET_RECEIVE_BUFFER_SIZE = 0x1FFFF; // 128K
const uint64_t SSU2_SOCKET_MAX_BUFFER_SIZE = 4 * 1024 * 1024; const size_t SSU2_SOCKET_SEND_BUFFER_SIZE = 0x1FFFF; // 128K
const size_t SSU2_MAX_NUM_INTRODUCERS = 3; const size_t SSU2_MAX_NUM_INTRODUCERS = 3;
const size_t SSU2_MIN_RECEIVED_PACKET_SIZE = 40; // 16 byte short header + 8 byte minimum payload + 16 byte MAC
const int SSU2_TO_INTRODUCER_SESSION_DURATION = 3600; // 1 hour const int SSU2_TO_INTRODUCER_SESSION_DURATION = 3600; // 1 hour
const int SSU2_TO_INTRODUCER_SESSION_EXPIRATION = 4800; // 80 minutes const int SSU2_TO_INTRODUCER_SESSION_EXPIRATION = 4800; // 80 minutes
const int SSU2_KEEP_ALIVE_INTERVAL = 15; // in seconds const int SSU2_KEEP_ALIVE_INTERVAL = 15; // in seconds
@ -71,10 +65,7 @@ namespace transport
bool UsesProxy () const { return m_IsThroughProxy; }; bool UsesProxy () const { return m_IsThroughProxy; };
bool IsSupported (const boost::asio::ip::address& addr) const; bool IsSupported (const boost::asio::ip::address& addr) const;
uint16_t GetPort (bool v4) const; uint16_t GetPort (bool v4) const;
std::mt19937& GetRng () { return m_Rng; }
bool IsMaxNumIntroducers (bool v4) const { return (v4 ? m_Introducers.size () : m_IntroducersV6.size ()) >= SSU2_MAX_NUM_INTRODUCERS; }
bool IsSyncClockFromPeers () const { return m_IsSyncClockFromPeers; }; bool IsSyncClockFromPeers () const { return m_IsSyncClockFromPeers; };
void AdjustTimeOffset (int64_t offset, std::shared_ptr<const i2p::data::IdentityEx> from);
void AddSession (std::shared_ptr<SSU2Session> session); void AddSession (std::shared_ptr<SSU2Session> session);
void RemoveSession (uint64_t connID); void RemoveSession (uint64_t connID);
@ -83,7 +74,7 @@ namespace transport
void RemovePendingOutgoingSession (const boost::asio::ip::udp::endpoint& ep); void RemovePendingOutgoingSession (const boost::asio::ip::udp::endpoint& ep);
std::shared_ptr<SSU2Session> FindSession (const i2p::data::IdentHash& ident) const; std::shared_ptr<SSU2Session> FindSession (const i2p::data::IdentHash& ident) const;
std::shared_ptr<SSU2Session> FindPendingOutgoingSession (const boost::asio::ip::udp::endpoint& ep) const; std::shared_ptr<SSU2Session> FindPendingOutgoingSession (const boost::asio::ip::udp::endpoint& ep) const;
std::shared_ptr<SSU2Session> GetRandomPeerTestSession (i2p::data::RouterInfo::CompatibleTransports remoteTransports, std::shared_ptr<SSU2Session> GetRandomSession (i2p::data::RouterInfo::CompatibleTransports remoteTransports,
const i2p::data::IdentHash& excluded) const; const i2p::data::IdentHash& excluded) const;
void AddRelay (uint32_t tag, std::shared_ptr<SSU2Session> relay); void AddRelay (uint32_t tag, std::shared_ptr<SSU2Session> relay);
@ -131,8 +122,8 @@ namespace transport
void HandleResendTimer (const boost::system::error_code& ecode); void HandleResendTimer (const boost::system::error_code& ecode);
void ConnectThroughIntroducer (std::shared_ptr<SSU2Session> session); void ConnectThroughIntroducer (std::shared_ptr<SSU2Session> session);
std::vector<std::shared_ptr<SSU2Session> > FindIntroducers (int maxNumIntroducers, std::list<std::shared_ptr<SSU2Session> > FindIntroducers (int maxNumIntroducers,
bool v4, const std::unordered_set<i2p::data::IdentHash>& excluded) const; bool v4, const std::set<i2p::data::IdentHash>& excluded) const;
void UpdateIntroducers (bool v4); void UpdateIntroducers (bool v4);
void ScheduleIntroducersUpdateTimer (); void ScheduleIntroducersUpdateTimer ();
void HandleIntroducersUpdateTimer (const boost::system::error_code& ecode, bool v4); void HandleIntroducersUpdateTimer (const boost::system::error_code& ecode, bool v4);
@ -170,9 +161,6 @@ namespace transport
std::shared_ptr<SSU2Session> m_LastSession; std::shared_ptr<SSU2Session> m_LastSession;
bool m_IsPublished; // if we maintain introducers bool m_IsPublished; // if we maintain introducers
bool m_IsSyncClockFromPeers; bool m_IsSyncClockFromPeers;
int64_t m_PendingTimeOffset; // during peer test
std::shared_ptr<const i2p::data::IdentityEx> m_PendingTimeOffsetFrom;
std::mt19937 m_Rng;
// proxy // proxy
bool m_IsThroughProxy; bool m_IsThroughProxy;

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2022-2024, The PurpleI2P Project * Copyright (c) 2022-2023, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@ -81,17 +81,13 @@ namespace transport
SSU2Session::SSU2Session (SSU2Server& server, std::shared_ptr<const i2p::data::RouterInfo> in_RemoteRouter, SSU2Session::SSU2Session (SSU2Server& server, std::shared_ptr<const i2p::data::RouterInfo> in_RemoteRouter,
std::shared_ptr<const i2p::data::RouterInfo::Address> addr): std::shared_ptr<const i2p::data::RouterInfo::Address> addr):
TransportSession (in_RemoteRouter, SSU2_CONNECT_TIMEOUT), TransportSession (in_RemoteRouter, SSU2_CONNECT_TIMEOUT),
m_Server (server), m_Address (addr), m_RemoteTransports (0), m_RemotePeerTestTransports (0), m_Server (server), m_Address (addr), m_RemoteTransports (0),
m_DestConnID (0), m_SourceConnID (0), m_State (eSSU2SessionStateUnknown), m_DestConnID (0), m_SourceConnID (0), m_State (eSSU2SessionStateUnknown),
m_SendPacketNum (0), m_ReceivePacketNum (0), m_LastDatetimeSentPacketNum (0), m_SendPacketNum (0), m_ReceivePacketNum (0), m_LastDatetimeSentPacketNum (0),
m_IsDataReceived (false), m_RTT (SSU2_UNKNOWN_RTT), m_IsDataReceived (false), m_WindowSize (SSU2_MIN_WINDOW_SIZE),
m_MsgLocalExpirationTimeout (I2NP_MESSAGE_LOCAL_EXPIRATION_TIMEOUT_MAX), m_RTT (SSU2_RESEND_INTERVAL), m_RTO (SSU2_RESEND_INTERVAL*SSU2_kAPPA), m_RelayTag (0),
m_MsgLocalSemiExpirationTimeout (I2NP_MESSAGE_LOCAL_EXPIRATION_TIMEOUT_MAX / 2), m_ConnectTimer (server.GetService ()), m_TerminationReason (eSSU2TerminationReasonNormalClose),
m_WindowSize (SSU2_MIN_WINDOW_SIZE), m_MaxPayloadSize (SSU2_MIN_PACKET_SIZE - IPV6_HEADER_SIZE - UDP_HEADER_SIZE - 32) // min size
m_RTO (SSU2_INITIAL_RTO), m_RelayTag (0),m_ConnectTimer (server.GetService ()),
m_TerminationReason (eSSU2TerminationReasonNormalClose),
m_MaxPayloadSize (SSU2_MIN_PACKET_SIZE - IPV6_HEADER_SIZE - UDP_HEADER_SIZE - 32), // min size
m_LastResendTime (0)
{ {
m_NoiseState.reset (new i2p::crypto::NoiseSymmetricState); m_NoiseState.reset (new i2p::crypto::NoiseSymmetricState);
if (in_RemoteRouter && m_Address) if (in_RemoteRouter && m_Address)
@ -100,8 +96,6 @@ namespace transport
InitNoiseXKState1 (*m_NoiseState, m_Address->s); InitNoiseXKState1 (*m_NoiseState, m_Address->s);
m_RemoteEndpoint = boost::asio::ip::udp::endpoint (m_Address->host, m_Address->port); m_RemoteEndpoint = boost::asio::ip::udp::endpoint (m_Address->host, m_Address->port);
m_RemoteTransports = in_RemoteRouter->GetCompatibleTransports (false); m_RemoteTransports = in_RemoteRouter->GetCompatibleTransports (false);
if (in_RemoteRouter->IsSSU2PeerTesting (true)) m_RemotePeerTestTransports |= i2p::data::RouterInfo::eSSU2V4;
if (in_RemoteRouter->IsSSU2PeerTesting (false)) m_RemotePeerTestTransports |= i2p::data::RouterInfo::eSSU2V6;
RAND_bytes ((uint8_t *)&m_DestConnID, 8); RAND_bytes ((uint8_t *)&m_DestConnID, 8);
RAND_bytes ((uint8_t *)&m_SourceConnID, 8); RAND_bytes ((uint8_t *)&m_SourceConnID, 8);
} }
@ -268,10 +262,8 @@ namespace transport
m_SentHandshakePacket.reset (nullptr); m_SentHandshakePacket.reset (nullptr);
m_SessionConfirmedFragment.reset (nullptr); m_SessionConfirmedFragment.reset (nullptr);
m_PathChallenge.reset (nullptr); m_PathChallenge.reset (nullptr);
for (auto& it: m_SendQueue)
it->Drop ();
m_SendQueue.clear (); m_SendQueue.clear ();
SetSendQueueSize (0); m_SendQueueSize = 0;
m_SentPackets.clear (); m_SentPackets.clear ();
m_IncompleteMessages.clear (); m_IncompleteMessages.clear ();
m_RelaySessions.clear (); m_RelaySessions.clear ();
@ -283,7 +275,7 @@ namespace transport
if (remoteIdentity) if (remoteIdentity)
{ {
LogPrint (eLogDebug, "SSU2: Session with ", GetRemoteEndpoint (), LogPrint (eLogDebug, "SSU2: Session with ", GetRemoteEndpoint (),
" (", i2p::data::GetIdentHashAbbreviation (remoteIdentity->GetIdentHash ()), ") terminated"); " (", i2p::data::GetIdentHashAbbreviation (GetRemoteIdentity ()->GetIdentHash ()), ") terminated");
} }
else else
{ {
@ -298,10 +290,8 @@ namespace transport
{ {
m_TerminationReason = reason; m_TerminationReason = reason;
SendTermination (); SendTermination ();
m_State = eSSU2SessionStateClosing;
} }
else m_State = eSSU2SessionStateClosing;
Done ();
} }
void SSU2Session::Established () void SSU2Session::Established ()
@ -313,7 +303,6 @@ namespace transport
m_SentHandshakePacket.reset (nullptr); m_SentHandshakePacket.reset (nullptr);
m_ConnectTimer.cancel (); m_ConnectTimer.cancel ();
SetTerminationTimeout (SSU2_TERMINATION_TIMEOUT); SetTerminationTimeout (SSU2_TERMINATION_TIMEOUT);
SendQueue ();
transports.PeerConnected (shared_from_this ()); transports.PeerConnected (shared_from_this ());
if (m_OnEstablished) if (m_OnEstablished)
{ {
@ -338,7 +327,7 @@ namespace transport
{ {
if (!s->IsEstablished ()) return; if (!s->IsEstablished ()) return;
uint8_t payload[SSU2_MAX_PACKET_SIZE]; uint8_t payload[SSU2_MAX_PACKET_SIZE];
size_t payloadSize = s->CreateRouterInfoBlock (payload, s->m_MaxPayloadSize - 32, i2p::context.CopyRouterInfoBuffer ()); size_t payloadSize = s->CreateRouterInfoBlock (payload, s->m_MaxPayloadSize - 32, i2p::context.GetSharedRouterInfo ());
if (payloadSize) if (payloadSize)
{ {
if (payloadSize < s->m_MaxPayloadSize) if (payloadSize < s->m_MaxPayloadSize)
@ -360,59 +349,29 @@ namespace transport
void SSU2Session::PostI2NPMessages (std::vector<std::shared_ptr<I2NPMessage> > msgs) void SSU2Session::PostI2NPMessages (std::vector<std::shared_ptr<I2NPMessage> > msgs)
{ {
if (m_State == eSSU2SessionStateTerminated) return; if (m_State == eSSU2SessionStateTerminated) return;
uint64_t mts = i2p::util::GetMonotonicMicroseconds ();
bool isSemiFull = false;
if (m_SendQueue.size ())
{
int64_t queueLag = (int64_t)mts - (int64_t)m_SendQueue.front ()->GetEnqueueTime ();
isSemiFull = queueLag > m_MsgLocalSemiExpirationTimeout;
if (isSemiFull)
{
LogPrint (eLogWarning, "SSU2: Outgoing messages queue to ",
i2p::data::GetIdentHashAbbreviation (GetRemoteIdentity ()->GetIdentHash ()),
" is semi-full (size = ", m_SendQueue.size (), ", lag = ", queueLag / 1000, ", rtt = ", (int)m_RTT, ")");
}
}
for (auto it: msgs) for (auto it: msgs)
m_SendQueue.push_back (std::move (it));
SendQueue ();
if (m_SendQueue.size () > 0) // windows is full
{ {
if (isSemiFull && it->onDrop) if (m_SendQueue.size () <= SSU2_MAX_OUTGOING_QUEUE_SIZE)
it->Drop (); // drop earlier because we can handle it Resend (i2p::util::GetMillisecondsSinceEpoch ());
else else
{ {
it->SetEnqueueTime (mts); LogPrint (eLogWarning, "SSU2: Outgoing messages queue size to ",
m_SendQueue.push_back (std::move (it)); GetIdentHashBase64(), " exceeds ", SSU2_MAX_OUTGOING_QUEUE_SIZE);
RequestTermination (eSSU2TerminationReasonTimeout);
} }
} }
if (IsEstablished ()) m_SendQueueSize = m_SendQueue.size ();
{
SendQueue ();
if (m_SendQueue.size () > 0) // windows is full
Resend (i2p::util::GetMillisecondsSinceEpoch ());
}
SetSendQueueSize (m_SendQueue.size ());
} }
void SSU2Session::MoveSendQueue (std::shared_ptr<SSU2Session> other)
{
if (!other || m_SendQueue.empty ()) return;
std::vector<std::shared_ptr<I2NPMessage> > msgs;
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
for (auto it: m_SendQueue)
if (!it->IsExpired (ts))
msgs.push_back (it);
else
it->Drop ();
m_SendQueue.clear ();
if (!msgs.empty ())
other->PostI2NPMessages (msgs);
}
bool SSU2Session::SendQueue () bool SSU2Session::SendQueue ()
{ {
if (!m_SendQueue.empty () && m_SentPackets.size () <= m_WindowSize && IsEstablished ()) if (!m_SendQueue.empty () && m_SentPackets.size () <= m_WindowSize)
{ {
auto ts = i2p::util::GetMillisecondsSinceEpoch (); auto ts = i2p::util::GetMillisecondsSinceEpoch ();
uint64_t mts = i2p::util::GetMonotonicMicroseconds ();
auto packet = m_Server.GetSentPacketsPool ().AcquireShared (); auto packet = m_Server.GetSentPacketsPool ().AcquireShared ();
size_t ackBlockSize = CreateAckBlock (packet->payload, m_MaxPayloadSize); size_t ackBlockSize = CreateAckBlock (packet->payload, m_MaxPayloadSize);
bool ackBlockSent = false; bool ackBlockSent = false;
@ -420,10 +379,8 @@ namespace transport
while (!m_SendQueue.empty () && m_SentPackets.size () <= m_WindowSize) while (!m_SendQueue.empty () && m_SentPackets.size () <= m_WindowSize)
{ {
auto msg = m_SendQueue.front (); auto msg = m_SendQueue.front ();
if (!msg || msg->IsExpired (ts) || msg->GetEnqueueTime() + m_MsgLocalExpirationTimeout < mts) if (!msg)
{ {
// drop null or expired message
if (msg) msg->Drop ();
m_SendQueue.pop_front (); m_SendQueue.pop_front ();
continue; continue;
} }
@ -516,7 +473,7 @@ namespace transport
else else
extraSize -= packet->payloadSize; extraSize -= packet->payloadSize;
} }
size_t offset = extraSize > 0 ? (m_Server.GetRng ()() % extraSize) : 0; size_t offset = extraSize > 0 ? (rand () % extraSize) : 0;
if (offset + packet->payloadSize >= m_MaxPayloadSize) offset = 0; if (offset + packet->payloadSize >= m_MaxPayloadSize) offset = 0;
auto size = CreateFirstFragmentBlock (packet->payload + packet->payloadSize, m_MaxPayloadSize - offset - packet->payloadSize, msg); auto size = CreateFirstFragmentBlock (packet->payload + packet->payloadSize, m_MaxPayloadSize - offset - packet->payloadSize, msg);
if (!size) return false; if (!size) return false;
@ -528,7 +485,7 @@ namespace transport
uint8_t fragmentNum = 0; uint8_t fragmentNum = 0;
while (msg->offset < msg->len) while (msg->offset < msg->len)
{ {
offset = extraSize > 0 ? (m_Server.GetRng ()() % extraSize) : 0; offset = extraSize > 0 ? (rand () % extraSize) : 0;
packet = m_Server.GetSentPacketsPool ().AcquireShared (); packet = m_Server.GetSentPacketsPool ().AcquireShared ();
packet->payloadSize = CreateFollowOnFragmentBlock (packet->payload, m_MaxPayloadSize - offset, msg, fragmentNum, msgID); packet->payloadSize = CreateFollowOnFragmentBlock (packet->payload, m_MaxPayloadSize - offset, msg, fragmentNum, msgID);
extraSize -= offset; extraSize -= offset;
@ -560,14 +517,14 @@ namespace transport
if (m_SentPackets.empty ()) return 0; if (m_SentPackets.empty ()) return 0;
std::map<uint32_t, std::shared_ptr<SSU2SentPacket> > resentPackets; std::map<uint32_t, std::shared_ptr<SSU2SentPacket> > resentPackets;
for (auto it = m_SentPackets.begin (); it != m_SentPackets.end (); ) for (auto it = m_SentPackets.begin (); it != m_SentPackets.end (); )
if (ts >= it->second->sendTime + (it->second->numResends + 1) * m_RTO) if (ts >= it->second->sendTime + it->second->numResends*m_RTO)
{ {
if (it->second->numResends > SSU2_MAX_NUM_RESENDS) if (it->second->numResends > SSU2_MAX_NUM_RESENDS)
{ {
LogPrint (eLogInfo, "SSU2: Packet was not Acked after ", it->second->numResends, " attempts. Terminate session"); LogPrint (eLogInfo, "SSU2: Packet was not Acked after ", it->second->numResends, " attempts. Terminate session");
m_SentPackets.clear (); m_SentPackets.clear ();
m_SendQueue.clear (); m_SendQueue.clear ();
SetSendQueueSize (0); m_SendQueueSize = 0;
RequestTermination (eSSU2TerminationReasonTimeout); RequestTermination (eSSU2TerminationReasonTimeout);
return resentPackets.size (); return resentPackets.size ();
} }
@ -584,7 +541,6 @@ namespace transport
it++; it++;
if (!resentPackets.empty ()) if (!resentPackets.empty ())
{ {
m_LastResendTime = ts;
#if (__cplusplus >= 201703L) // C++ 17 or higher #if (__cplusplus >= 201703L) // C++ 17 or higher
m_SentPackets.merge (resentPackets); m_SentPackets.merge (resentPackets);
#else #else
@ -680,14 +636,10 @@ namespace transport
size_t payloadSize = 7; size_t payloadSize = 7;
if (GetRouterStatus () == eRouterStatusFirewalled && m_Address->IsIntroducer ()) if (GetRouterStatus () == eRouterStatusFirewalled && m_Address->IsIntroducer ())
{ {
if (!m_Server.IsMaxNumIntroducers (m_RemoteEndpoint.address ().is_v4 ()) || // relay tag request
m_Server.GetRng ()() & 0x01) // request tag with probability 1/2 if we have enough introducers payload[payloadSize] = eSSU2BlkRelayTagRequest;
{ memset (payload + payloadSize + 1, 0, 2); // size = 0
// relay tag request payloadSize += 3;
payload[payloadSize] = eSSU2BlkRelayTagRequest;
memset (payload + payloadSize + 1, 0, 2); // size = 0
payloadSize += 3;
}
} }
payloadSize += CreatePaddingBlock (payload + payloadSize, 40 - payloadSize, 1); payloadSize += CreatePaddingBlock (payload + payloadSize, 40 - payloadSize, 1);
// KDF for session request // KDF for session request
@ -905,12 +857,12 @@ namespace transport
// payload // payload
size_t maxPayloadSize = m_MaxPayloadSize - 48; // for part 2, 48 is part1 size_t maxPayloadSize = m_MaxPayloadSize - 48; // for part 2, 48 is part1
uint8_t * payload = m_SentHandshakePacket->payload; uint8_t * payload = m_SentHandshakePacket->payload;
size_t payloadSize = CreateRouterInfoBlock (payload, maxPayloadSize, i2p::context.CopyRouterInfoBuffer ()); size_t payloadSize = CreateRouterInfoBlock (payload, maxPayloadSize, i2p::context.GetSharedRouterInfo ());
if (!payloadSize) if (!payloadSize)
{ {
// split by two fragments // split by two fragments
maxPayloadSize += m_MaxPayloadSize; maxPayloadSize += m_MaxPayloadSize;
payloadSize = CreateRouterInfoBlock (payload, maxPayloadSize, i2p::context.CopyRouterInfoBuffer ()); payloadSize = CreateRouterInfoBlock (payload, maxPayloadSize, i2p::context.GetSharedRouterInfo ());
header.h.flags[0] = 0x02; // frag 0, total fragments 2 header.h.flags[0] = 0x02; // frag 0, total fragments 2
// TODO: check if we need more fragments // TODO: check if we need more fragments
} }
@ -938,7 +890,7 @@ namespace transport
{ {
if (payloadSize > m_MaxPayloadSize - 48) if (payloadSize > m_MaxPayloadSize - 48)
{ {
payloadSize = m_MaxPayloadSize - 48 - (m_Server.GetRng ()() % 16); payloadSize = m_MaxPayloadSize - 48 - (rand () % 16);
if (m_SentHandshakePacket->payloadSize - payloadSize < 24) if (m_SentHandshakePacket->payloadSize - payloadSize < 24)
payloadSize -= 24; payloadSize -= 24;
} }
@ -1150,10 +1102,6 @@ namespace transport
AdjustMaxPayloadSize (); AdjustMaxPayloadSize ();
m_Server.AddSessionByRouterHash (shared_from_this ()); // we know remote router now m_Server.AddSessionByRouterHash (shared_from_this ()); // we know remote router now
m_RemoteTransports = ri->GetCompatibleTransports (false); m_RemoteTransports = ri->GetCompatibleTransports (false);
m_RemotePeerTestTransports = 0;
if (ri->IsSSU2PeerTesting (true)) m_RemotePeerTestTransports |= i2p::data::RouterInfo::eSSU2V4;
if (ri->IsSSU2PeerTesting (false)) m_RemotePeerTestTransports |= i2p::data::RouterInfo::eSSU2V6;
// handle other blocks // handle other blocks
HandlePayload (decryptedPayload.data () + riSize + 3, decryptedPayload.size () - riSize - 3); HandlePayload (decryptedPayload.data () + riSize + 3, decryptedPayload.size () - riSize - 3);
Established (); Established ();
@ -1504,7 +1452,8 @@ namespace transport
header.ll[1] ^= CreateHeaderMask (m_KeyDataSend + 32, payload + (len + 4)); header.ll[1] ^= CreateHeaderMask (m_KeyDataSend + 32, payload + (len + 4));
m_Server.Send (header.buf, 16, payload, len + 16, m_RemoteEndpoint); m_Server.Send (header.buf, 16, payload, len + 16, m_RemoteEndpoint);
m_SendPacketNum++; m_SendPacketNum++;
UpdateNumSentBytes (len + 32); m_LastActivityTimestamp = i2p::util::GetSecondsSinceEpoch ();
m_NumSentBytes += len + 32;
return m_SendPacketNum - 1; return m_SendPacketNum - 1;
} }
@ -1523,7 +1472,7 @@ namespace transport
ResendHandshakePacket (); // assume we receive ResendHandshakePacket (); // assume we receive
return; return;
} }
if (from != m_RemoteEndpoint && !i2p::transport::transports.IsInReservedRange (from.address ())) if (from != m_RemoteEndpoint && !i2p::util::net::IsInReservedRange (from.address ()))
{ {
LogPrint (eLogInfo, "SSU2: Remote endpoint update ", m_RemoteEndpoint, "->", from); LogPrint (eLogInfo, "SSU2: Remote endpoint update ", m_RemoteEndpoint, "->", from);
m_RemoteEndpoint = from; m_RemoteEndpoint = from;
@ -1545,7 +1494,8 @@ namespace transport
LogPrint (eLogWarning, "SSU2: Data AEAD verification failed "); LogPrint (eLogWarning, "SSU2: Data AEAD verification failed ");
return; return;
} }
UpdateNumReceivedBytes (len); m_LastActivityTimestamp = i2p::util::GetSecondsSinceEpoch ();
m_NumReceivedBytes += len;
if (!packetNum || UpdateReceivePacketNum (packetNum)) if (!packetNum || UpdateReceivePacketNum (packetNum))
HandlePayload (payload, payloadSize); HandlePayload (payload, payloadSize);
} }
@ -1575,9 +1525,14 @@ namespace transport
LogPrint (eLogDebug, "SSU2: Options"); LogPrint (eLogDebug, "SSU2: Options");
break; break;
case eSSU2BlkRouterInfo: case eSSU2BlkRouterInfo:
{
// not from SessionConfirmed, we must add it instantly to use in next block
LogPrint (eLogDebug, "SSU2: RouterInfo"); LogPrint (eLogDebug, "SSU2: RouterInfo");
HandleRouterInfo (buf + offset, size); auto ri = ExtractRouterInfo (buf + offset, size);
break; if (ri)
i2p::data::netdb.AddRouterInfo (ri->GetBuffer (), ri->GetBufferLen ()); // TODO: add ri
break;
}
case eSSU2BlkI2NPMessage: case eSSU2BlkI2NPMessage:
{ {
LogPrint (eLogDebug, "SSU2: I2NP message"); LogPrint (eLogDebug, "SSU2: I2NP message");
@ -1652,12 +1607,8 @@ namespace transport
LogPrint (eLogDebug, "SSU2: RelayTagRequest"); LogPrint (eLogDebug, "SSU2: RelayTagRequest");
if (!m_RelayTag) if (!m_RelayTag)
{ {
auto addr = FindLocalAddress (); RAND_bytes ((uint8_t *)&m_RelayTag, 4);
if (addr && addr->IsIntroducer ()) m_Server.AddRelay (m_RelayTag, shared_from_this ());
{
RAND_bytes ((uint8_t *)&m_RelayTag, 4);
m_Server.AddRelay (m_RelayTag, shared_from_this ());
}
} }
break; break;
case eSSU2BlkRelayTag: case eSSU2BlkRelayTag:
@ -1719,12 +1670,10 @@ namespace transport
if (m_Server.IsSyncClockFromPeers ()) if (m_Server.IsSyncClockFromPeers ())
{ {
if (std::abs (offset) > SSU2_CLOCK_THRESHOLD) if (std::abs (offset) > SSU2_CLOCK_THRESHOLD)
{ {
LogPrint (eLogWarning, "SSU2: Time offset ", offset, " from ", m_RemoteEndpoint); LogPrint (eLogWarning, "SSU2: Clock adjusted by ", -offset, " seconds");
m_Server.AdjustTimeOffset (-offset, GetRemoteIdentity ()); i2p::util::AdjustTimeOffset (-offset);
} }
else
m_Server.AdjustTimeOffset (0, nullptr);
} }
else if (std::abs (offset) > SSU2_CLOCK_SKEW) else if (std::abs (offset) > SSU2_CLOCK_SKEW)
{ {
@ -1737,32 +1686,6 @@ namespace transport
}; };
} }
void SSU2Session::HandleRouterInfo (const uint8_t * buf, size_t len)
{
auto ri = ExtractRouterInfo (buf, len);
if (ri)
{
// not from SessionConfirmed, we must add it instantly to use in next block
auto newRi = i2p::data::netdb.AddRouterInfo (ri->GetBuffer (), ri->GetBufferLen ()); // TODO: add ri
if (newRi)
{
auto remoteIdentity = GetRemoteIdentity ();
if (remoteIdentity && remoteIdentity->GetIdentHash () == newRi->GetIdentHash ())
{
// peer's RouterInfo update
SetRemoteIdentity (newRi->GetIdentity ());
auto address = m_RemoteEndpoint.address ().is_v6 () ? newRi->GetSSU2V6Address () : newRi->GetSSU2V4Address ();
if (address)
{
m_Address = address;
if (IsOutgoing () && m_RelayTag && !address->IsIntroducer ())
m_RelayTag = 0; // not longer introducer
}
}
}
}
}
void SSU2Session::HandleAck (const uint8_t * buf, size_t len) void SSU2Session::HandleAck (const uint8_t * buf, size_t len)
{ {
if (m_State == eSSU2SessionStateSessionConfirmedSent) if (m_State == eSSU2SessionStateSessionConfirmedSent)
@ -1806,15 +1729,8 @@ namespace transport
if (ts > it1->second->sendTime) if (ts > it1->second->sendTime)
{ {
auto rtt = ts - it1->second->sendTime; auto rtt = ts - it1->second->sendTime;
if (m_RTT != SSU2_UNKNOWN_RTT) m_RTT = std::round ((m_RTT*m_SendPacketNum + rtt)/(m_SendPacketNum + 1.0));
m_RTT = SSU2_RTT_EWMA_ALPHA * rtt + (1.0 - SSU2_RTT_EWMA_ALPHA) * m_RTT;
else
m_RTT = rtt;
m_RTO = m_RTT*SSU2_kAPPA; m_RTO = m_RTT*SSU2_kAPPA;
m_MsgLocalExpirationTimeout = std::max (I2NP_MESSAGE_LOCAL_EXPIRATION_TIMEOUT_MIN,
std::min (I2NP_MESSAGE_LOCAL_EXPIRATION_TIMEOUT_MAX,
(unsigned int)(m_RTT * 1000 * I2NP_MESSAGE_LOCAL_EXPIRATION_TIMEOUT_FACTOR)));
m_MsgLocalSemiExpirationTimeout = m_MsgLocalExpirationTimeout / 2;
if (m_RTO < SSU2_MIN_RTO) m_RTO = SSU2_MIN_RTO; if (m_RTO < SSU2_MIN_RTO) m_RTO = SSU2_MIN_RTO;
if (m_RTO > SSU2_MAX_RTO) m_RTO = SSU2_MAX_RTO; if (m_RTO > SSU2_MAX_RTO) m_RTO = SSU2_MAX_RTO;
} }
@ -1837,7 +1753,7 @@ namespace transport
if (ExtractEndpoint (buf, len, ep)) if (ExtractEndpoint (buf, len, ep))
{ {
LogPrint (eLogInfo, "SSU2: Our external address is ", ep); LogPrint (eLogInfo, "SSU2: Our external address is ", ep);
if (!i2p::transport::transports.IsInReservedRange (ep.address ())) if (!i2p::util::net::IsInReservedRange (ep.address ()))
{ {
i2p::context.UpdateAddress (ep.address ()); i2p::context.UpdateAddress (ep.address ());
// check our port // check our port
@ -2185,7 +2101,7 @@ namespace transport
{ {
case 1: // Bob from Alice case 1: // Bob from Alice
{ {
auto session = m_Server.GetRandomPeerTestSession ((buf[12] == 6) ? i2p::data::RouterInfo::eSSU2V4 : i2p::data::RouterInfo::eSSU2V6, auto session = m_Server.GetRandomSession ((buf[12] == 6) ? i2p::data::RouterInfo::eSSU2V4 : i2p::data::RouterInfo::eSSU2V6,
GetRemoteIdentity ()->GetIdentHash ()); GetRemoteIdentity ()->GetIdentHash ());
if (session) // session with Charlie if (session) // session with Charlie
{ {
@ -2256,8 +2172,7 @@ namespace transport
std::shared_ptr<const i2p::data::RouterInfo::Address> addr; std::shared_ptr<const i2p::data::RouterInfo::Address> addr;
if (ExtractEndpoint (buf + offset + 10, asz, ep)) if (ExtractEndpoint (buf + offset + 10, asz, ep))
addr = r->GetSSU2Address (ep.address ().is_v4 ()); addr = r->GetSSU2Address (ep.address ().is_v4 ());
if (addr && m_Server.IsSupported (ep.address ()) && if (addr && m_Server.IsSupported (ep.address ()))
i2p::context.GetRouterInfo ().IsSSU2PeerTesting (ep.address ().is_v4 ()))
{ {
// send msg 5 to Alice // send msg 5 to Alice
auto session = std::make_shared<SSU2Session> (m_Server, r, addr); auto session = std::make_shared<SSU2Session> (m_Server, r, addr);
@ -2357,7 +2272,7 @@ namespace transport
if (GetTestingState ()) if (GetTestingState ())
{ {
SetTestingState (false); SetTestingState (false);
if (GetRouterStatus () != eRouterStatusFirewalled && addr->IsPeerTesting ()) if (GetRouterStatus () != eRouterStatusFirewalled)
{ {
SetRouterStatus (eRouterStatusFirewalled); SetRouterStatus (eRouterStatusFirewalled);
if (m_Address->IsV4 ()) if (m_Address->IsV4 ())
@ -2442,7 +2357,7 @@ namespace transport
if (!msg->IsExpired ()) if (!msg->IsExpired ())
{ {
// m_LastActivityTimestamp is updated in ProcessData before // m_LastActivityTimestamp is updated in ProcessData before
if (m_ReceivedI2NPMsgIDs.emplace (msgID, (uint32_t)GetLastActivityTimestamp ()).second) if (m_ReceivedI2NPMsgIDs.emplace (msgID, (uint32_t)m_LastActivityTimestamp).second)
m_Handler.PutNextMessage (std::move (msg)); m_Handler.PutNextMessage (std::move (msg));
else else
LogPrint (eLogDebug, "SSU2: Message ", msgID, " already received"); LogPrint (eLogDebug, "SSU2: Message ", msgID, " already received");
@ -2503,8 +2418,6 @@ namespace transport
{ {
if (m_Address) if (m_Address)
return i2p::context.GetRouterInfo ().GetSSU2Address (m_Address->IsV4 ()); return i2p::context.GetRouterInfo ().GetSSU2Address (m_Address->IsV4 ());
else if (!m_RemoteEndpoint.address ().is_unspecified ())
return i2p::context.GetRouterInfo ().GetSSU2Address (m_RemoteEndpoint.address ().is_v4 ());
return nullptr; return nullptr;
} }
@ -2570,8 +2483,6 @@ namespace transport
else if (m_Address->IsV6 ()) else if (m_Address->IsV6 ())
i2p::context.SetTestingV6 (testing); i2p::context.SetTestingV6 (testing);
} }
if (!testing)
m_Server.AdjustTimeOffset (0, nullptr); // reset time offset when testing is over
} }
size_t SSU2Session::CreateAddressBlock (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& ep) size_t SSU2Session::CreateAddressBlock (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& ep)
@ -2586,34 +2497,27 @@ namespace transport
size_t SSU2Session::CreateRouterInfoBlock (uint8_t * buf, size_t len, std::shared_ptr<const i2p::data::RouterInfo> r) size_t SSU2Session::CreateRouterInfoBlock (uint8_t * buf, size_t len, std::shared_ptr<const i2p::data::RouterInfo> r)
{ {
if (!r || len < 5) return 0; if (!r || !r->GetBuffer () || len < 5) return 0;
return CreateRouterInfoBlock (buf, len, r->GetSharedBuffer ());
}
size_t SSU2Session::CreateRouterInfoBlock (uint8_t * buf, size_t len, std::shared_ptr<const i2p::data::RouterInfo::Buffer> riBuffer)
{
if (!riBuffer || len < 5) return 0;
buf[0] = eSSU2BlkRouterInfo; buf[0] = eSSU2BlkRouterInfo;
size_t size = riBuffer->GetBufferLen (); size_t size = r->GetBufferLen ();
if (size + 5 < len) if (size + 5 < len)
{ {
memcpy (buf + 5, riBuffer->data (), size); memcpy (buf + 5, r->GetBuffer (), size);
buf[3] = 0; // flag buf[3] = 0; // flag
} }
else else
{ {
i2p::data::GzipDeflator deflator; i2p::data::GzipDeflator deflator;
deflator.SetCompressionLevel (9); deflator.SetCompressionLevel (9);
size = deflator.Deflate (riBuffer->data (), riBuffer->GetBufferLen (), buf + 5, len - 5); size = deflator.Deflate (r->GetBuffer (), r->GetBufferLen (), buf + 5, len - 5);
if (!size) return 0; // doesn't fit if (!size) return 0; // doesn't fit
buf[3] = SSU2_ROUTER_INFO_FLAG_GZIP; // flag buf[3] = SSU2_ROUTER_INFO_FLAG_GZIP; // flag
} }
htobe16buf (buf + 1, size + 2); // size htobe16buf (buf + 1, size + 2); // size
buf[4] = 1; // frag buf[4] = 1; // frag
return size + 5; return size + 5;
} }
size_t SSU2Session::CreateAckBlock (uint8_t * buf, size_t len) size_t SSU2Session::CreateAckBlock (uint8_t * buf, size_t len)
{ {
if (len < 8) return 0; if (len < 8) return 0;
@ -2726,7 +2630,7 @@ namespace transport
size_t SSU2Session::CreatePaddingBlock (uint8_t * buf, size_t len, size_t minSize) size_t SSU2Session::CreatePaddingBlock (uint8_t * buf, size_t len, size_t minSize)
{ {
if (len < 3 || len < minSize) return 0; if (len < 3 || len < minSize) return 0;
size_t paddingSize = m_Server.GetRng ()() & 0x0F; // 0 - 15 size_t paddingSize = rand () & 0x0F; // 0 - 15
if (paddingSize + 3 > len) paddingSize = len - 3; if (paddingSize + 3 > len) paddingSize = len - 3;
else if (paddingSize + 3 < minSize) paddingSize = minSize - 3; else if (paddingSize + 3 < minSize) paddingSize = minSize - 3;
buf[0] = eSSU2BlkPadding; buf[0] = eSSU2BlkPadding;
@ -3012,7 +2916,7 @@ namespace transport
{ {
uint8_t payload[SSU2_MAX_PACKET_SIZE]; uint8_t payload[SSU2_MAX_PACKET_SIZE];
payload[0] = eSSU2BlkPathChallenge; payload[0] = eSSU2BlkPathChallenge;
size_t len = m_Server.GetRng ()() % (m_MaxPayloadSize - 3); size_t len = rand () % (m_MaxPayloadSize - 3);
htobe16buf (payload + 1, len); htobe16buf (payload + 1, len);
if (len > 0) if (len > 0)
{ {
@ -3039,7 +2943,7 @@ namespace transport
else else
++it; ++it;
} }
if (m_ReceivedI2NPMsgIDs.size () > SSU2_MAX_NUM_RECEIVED_I2NP_MSGIDS || ts > GetLastActivityTimestamp () + SSU2_DECAY_INTERVAL) if (m_ReceivedI2NPMsgIDs.size () > SSU2_MAX_NUM_RECEIVED_I2NP_MSGIDS || ts > m_LastActivityTimestamp + SSU2_DECAY_INTERVAL)
// decay // decay
m_ReceivedI2NPMsgIDs.clear (); m_ReceivedI2NPMsgIDs.clear ();
else else
@ -3111,7 +3015,7 @@ namespace transport
{ {
bool sent = SendQueue (); // if we have something to send bool sent = SendQueue (); // if we have something to send
if (sent) if (sent)
SetSendQueueSize (m_SendQueue.size ()); m_SendQueueSize = m_SendQueue.size ();
if (m_IsDataReceived) if (m_IsDataReceived)
{ {
if (!sent) SendQuickAck (); if (!sent) SendQuickAck ();

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2022-2024, The PurpleI2P Project * Copyright (c) 2022-2023, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@ -25,7 +25,7 @@ namespace i2p
namespace transport namespace transport
{ {
const int SSU2_CONNECT_TIMEOUT = 5; // 5 seconds const int SSU2_CONNECT_TIMEOUT = 5; // 5 seconds
const int SSU2_TERMINATION_TIMEOUT = 165; // in seconds const int SSU2_TERMINATION_TIMEOUT = 330; // 5.5 minutes
const int SSU2_CLOCK_SKEW = 60; // in seconds const int SSU2_CLOCK_SKEW = 60; // in seconds
const int SSU2_CLOCK_THRESHOLD = 15; // in seconds, if more we should adjust const int SSU2_CLOCK_THRESHOLD = 15; // in seconds, if more we should adjust
const int SSU2_TOKEN_EXPIRATION_TIMEOUT = 9; // for Retry message, in seconds const int SSU2_TOKEN_EXPIRATION_TIMEOUT = 9; // for Retry message, in seconds
@ -36,6 +36,7 @@ namespace transport
const size_t SSU2_MAX_PACKET_SIZE = 1500; const size_t SSU2_MAX_PACKET_SIZE = 1500;
const size_t SSU2_MIN_PACKET_SIZE = 1280; const size_t SSU2_MIN_PACKET_SIZE = 1280;
const int SSU2_HANDSHAKE_RESEND_INTERVAL = 1000; // in milliseconds const int SSU2_HANDSHAKE_RESEND_INTERVAL = 1000; // in milliseconds
const int SSU2_RESEND_INTERVAL = 300; // in milliseconds
const int SSU2_MAX_NUM_RESENDS = 5; const int SSU2_MAX_NUM_RESENDS = 5;
const int SSU2_INCOMPLETE_MESSAGES_CLEANUP_TIMEOUT = 30; // in seconds const int SSU2_INCOMPLETE_MESSAGES_CLEANUP_TIMEOUT = 30; // in seconds
const int SSU2_MAX_NUM_RECEIVED_I2NP_MSGIDS = 5000; // how many msgID we store for duplicates check const int SSU2_MAX_NUM_RECEIVED_I2NP_MSGIDS = 5000; // how many msgID we store for duplicates check
@ -44,11 +45,9 @@ namespace transport
const size_t SSU2_MIN_WINDOW_SIZE = 16; // in packets const size_t SSU2_MIN_WINDOW_SIZE = 16; // in packets
const size_t SSU2_MAX_WINDOW_SIZE = 256; // in packets const size_t SSU2_MAX_WINDOW_SIZE = 256; // in packets
const size_t SSU2_MIN_RTO = 100; // in milliseconds const size_t SSU2_MIN_RTO = 100; // in milliseconds
const size_t SSU2_INITIAL_RTO = 540; // in milliseconds
const size_t SSU2_MAX_RTO = 2500; // in milliseconds const size_t SSU2_MAX_RTO = 2500; // in milliseconds
const double SSU2_UNKNOWN_RTT = -1;
const double SSU2_RTT_EWMA_ALPHA = 0.125;
const float SSU2_kAPPA = 1.8; const float SSU2_kAPPA = 1.8;
const size_t SSU2_MAX_OUTGOING_QUEUE_SIZE = 500; // in messages
const int SSU2_MAX_NUM_ACNT = 255; // acnt, acks or nacks const int SSU2_MAX_NUM_ACNT = 255; // acnt, acks or nacks
const int SSU2_MAX_NUM_ACK_PACKETS = 511; // ackthrough + acnt + 1 range const int SSU2_MAX_NUM_ACK_PACKETS = 511; // ackthrough + acnt + 1 range
const int SSU2_MAX_NUM_ACK_RANGES = 32; // to send const int SSU2_MAX_NUM_ACK_RANGES = 32; // to send
@ -239,7 +238,6 @@ namespace transport
void SetRemoteEndpoint (const boost::asio::ip::udp::endpoint& ep) { m_RemoteEndpoint = ep; }; void SetRemoteEndpoint (const boost::asio::ip::udp::endpoint& ep) { m_RemoteEndpoint = ep; };
const boost::asio::ip::udp::endpoint& GetRemoteEndpoint () const { return m_RemoteEndpoint; }; const boost::asio::ip::udp::endpoint& GetRemoteEndpoint () const { return m_RemoteEndpoint; };
i2p::data::RouterInfo::CompatibleTransports GetRemoteTransports () const { return m_RemoteTransports; }; i2p::data::RouterInfo::CompatibleTransports GetRemoteTransports () const { return m_RemoteTransports; };
i2p::data::RouterInfo::CompatibleTransports GetRemotePeerTestTransports () const { return m_RemotePeerTestTransports; };
std::shared_ptr<const i2p::data::RouterInfo::Address> GetAddress () const { return m_Address; }; std::shared_ptr<const i2p::data::RouterInfo::Address> GetAddress () const { return m_Address; };
void SetOnEstablished (OnEstablished e) { m_OnEstablished = e; }; void SetOnEstablished (OnEstablished e) { m_OnEstablished = e; };
OnEstablished GetOnEstablished () const { return m_OnEstablished; }; OnEstablished GetOnEstablished () const { return m_OnEstablished; };
@ -255,10 +253,8 @@ namespace transport
void Done () override; void Done () override;
void SendLocalRouterInfo (bool update) override; void SendLocalRouterInfo (bool update) override;
void SendI2NPMessages (const std::vector<std::shared_ptr<I2NPMessage> >& msgs) override; void SendI2NPMessages (const std::vector<std::shared_ptr<I2NPMessage> >& msgs) override;
void MoveSendQueue (std::shared_ptr<SSU2Session> other);
uint32_t GetRelayTag () const override { return m_RelayTag; }; uint32_t GetRelayTag () const override { return m_RelayTag; };
size_t Resend (uint64_t ts); // return number of resent packets size_t Resend (uint64_t ts); // return number or resent packets
uint64_t GetLastResendTime () const { return m_LastResendTime; };
bool IsEstablished () const override { return m_State == eSSU2SessionStateEstablished; }; bool IsEstablished () const override { return m_State == eSSU2SessionStateEstablished; };
uint64_t GetConnID () const { return m_SourceConnID; }; uint64_t GetConnID () const { return m_SourceConnID; };
SSU2SessionState GetState () const { return m_State; }; SSU2SessionState GetState () const { return m_State; };
@ -303,7 +299,6 @@ namespace transport
void HandlePayload (const uint8_t * buf, size_t len); void HandlePayload (const uint8_t * buf, size_t len);
void HandleDateTime (const uint8_t * buf, size_t len); void HandleDateTime (const uint8_t * buf, size_t len);
void HandleRouterInfo (const uint8_t * buf, size_t len);
void HandleAck (const uint8_t * buf, size_t len); void HandleAck (const uint8_t * buf, size_t len);
void HandleAckRange (uint32_t firstPacketNum, uint32_t lastPacketNum, uint64_t ts); void HandleAckRange (uint32_t firstPacketNum, uint32_t lastPacketNum, uint64_t ts);
void HandleAddress (const uint8_t * buf, size_t len); void HandleAddress (const uint8_t * buf, size_t len);
@ -328,7 +323,6 @@ namespace transport
size_t CreateAddressBlock (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& ep); size_t CreateAddressBlock (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& ep);
size_t CreateRouterInfoBlock (uint8_t * buf, size_t len, std::shared_ptr<const i2p::data::RouterInfo> r); size_t CreateRouterInfoBlock (uint8_t * buf, size_t len, std::shared_ptr<const i2p::data::RouterInfo> r);
size_t CreateRouterInfoBlock (uint8_t * buf, size_t len, std::shared_ptr<const i2p::data::RouterInfo::Buffer> riBuffer);
size_t CreateAckBlock (uint8_t * buf, size_t len); size_t CreateAckBlock (uint8_t * buf, size_t len);
size_t CreatePaddingBlock (uint8_t * buf, size_t len, size_t minSize = 0); size_t CreatePaddingBlock (uint8_t * buf, size_t len, size_t minSize = 0);
size_t CreateI2NPBlock (uint8_t * buf, size_t len, std::shared_ptr<I2NPMessage>&& msg); size_t CreateI2NPBlock (uint8_t * buf, size_t len, std::shared_ptr<I2NPMessage>&& msg);
@ -349,7 +343,7 @@ namespace transport
std::unique_ptr<HandshakePacket> m_SentHandshakePacket; // SessionRequest, SessionCreated or SessionConfirmed std::unique_ptr<HandshakePacket> m_SentHandshakePacket; // SessionRequest, SessionCreated or SessionConfirmed
std::shared_ptr<const i2p::data::RouterInfo::Address> m_Address; std::shared_ptr<const i2p::data::RouterInfo::Address> m_Address;
boost::asio::ip::udp::endpoint m_RemoteEndpoint; boost::asio::ip::udp::endpoint m_RemoteEndpoint;
i2p::data::RouterInfo::CompatibleTransports m_RemoteTransports, m_RemotePeerTestTransports; i2p::data::RouterInfo::CompatibleTransports m_RemoteTransports; // for peer tests
uint64_t m_DestConnID, m_SourceConnID; uint64_t m_DestConnID, m_SourceConnID;
SSU2SessionState m_State; SSU2SessionState m_State;
uint8_t m_KeyDataSend[64], m_KeyDataReceive[64]; uint8_t m_KeyDataSend[64], m_KeyDataReceive[64];
@ -362,10 +356,7 @@ namespace transport
std::list<std::shared_ptr<I2NPMessage> > m_SendQueue; std::list<std::shared_ptr<I2NPMessage> > m_SendQueue;
i2p::I2NPMessagesHandler m_Handler; i2p::I2NPMessagesHandler m_Handler;
bool m_IsDataReceived; bool m_IsDataReceived;
double m_RTT; size_t m_WindowSize, m_RTT, m_RTO;
int m_MsgLocalExpirationTimeout;
int m_MsgLocalSemiExpirationTimeout;
size_t m_WindowSize, m_RTO;
uint32_t m_RelayTag; // between Bob and Charlie uint32_t m_RelayTag; // between Bob and Charlie
OnEstablished m_OnEstablished; // callback from Established OnEstablished m_OnEstablished; // callback from Established
boost::asio::deadline_timer m_ConnectTimer; boost::asio::deadline_timer m_ConnectTimer;
@ -373,7 +364,6 @@ namespace transport
size_t m_MaxPayloadSize; size_t m_MaxPayloadSize;
std::unique_ptr<i2p::data::IdentHash> m_PathChallenge; std::unique_ptr<i2p::data::IdentHash> m_PathChallenge;
std::unordered_map<uint32_t, uint32_t> m_ReceivedI2NPMsgIDs; // msgID -> timestamp in seconds std::unordered_map<uint32_t, uint32_t> m_ReceivedI2NPMsgIDs; // msgID -> timestamp in seconds
uint64_t m_LastResendTime; // in milliseconds
}; };
inline uint64_t CreateHeaderMask (const uint8_t * kh, const uint8_t * nonce) inline uint64_t CreateHeaderMask (const uint8_t * kh, const uint8_t * nonce)

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2023, The PurpleI2P Project * Copyright (c) 2013-2021, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@ -15,35 +15,26 @@ namespace i2p
namespace crypto namespace crypto
{ {
#if OPENSSL_EDDSA #if OPENSSL_EDDSA
EDDSA25519Verifier::EDDSA25519Verifier (): EDDSA25519Verifier::EDDSA25519Verifier ()
m_Pkey (nullptr)
{ {
m_MDCtx = EVP_MD_CTX_create ();
} }
EDDSA25519Verifier::~EDDSA25519Verifier () EDDSA25519Verifier::~EDDSA25519Verifier ()
{ {
EVP_PKEY_free (m_Pkey); EVP_MD_CTX_destroy (m_MDCtx);
} }
void EDDSA25519Verifier::SetPublicKey (const uint8_t * signingKey) void EDDSA25519Verifier::SetPublicKey (const uint8_t * signingKey)
{ {
if (m_Pkey) EVP_PKEY_free (m_Pkey); EVP_PKEY * pkey = EVP_PKEY_new_raw_public_key (EVP_PKEY_ED25519, NULL, signingKey, 32);
m_Pkey = EVP_PKEY_new_raw_public_key (EVP_PKEY_ED25519, NULL, signingKey, 32); EVP_DigestVerifyInit (m_MDCtx, NULL, NULL, NULL, pkey);
EVP_PKEY_free (pkey);
} }
bool EDDSA25519Verifier::Verify (const uint8_t * buf, size_t len, const uint8_t * signature) const bool EDDSA25519Verifier::Verify (const uint8_t * buf, size_t len, const uint8_t * signature) const
{ {
if (m_Pkey) return EVP_DigestVerify (m_MDCtx, signature, 64, buf, len);
{
EVP_MD_CTX * ctx = EVP_MD_CTX_create ();
EVP_DigestVerifyInit (ctx, NULL, NULL, NULL, m_Pkey);
auto ret = EVP_DigestVerify (ctx, signature, 64, buf, len);
EVP_MD_CTX_destroy (ctx);
return ret;
}
else
LogPrint (eLogError, "EdDSA verification key is not set");
return false;
} }
#else #else
@ -108,45 +99,41 @@ namespace crypto
#if OPENSSL_EDDSA #if OPENSSL_EDDSA
EDDSA25519Signer::EDDSA25519Signer (const uint8_t * signingPrivateKey, const uint8_t * signingPublicKey): EDDSA25519Signer::EDDSA25519Signer (const uint8_t * signingPrivateKey, const uint8_t * signingPublicKey):
m_Pkey (nullptr), m_Fallback (nullptr) m_MDCtx (nullptr), m_Fallback (nullptr)
{ {
m_Pkey = EVP_PKEY_new_raw_private_key (EVP_PKEY_ED25519, NULL, signingPrivateKey, 32); EVP_PKEY * pkey = EVP_PKEY_new_raw_private_key (EVP_PKEY_ED25519, NULL, signingPrivateKey, 32);
uint8_t publicKey[EDDSA25519_PUBLIC_KEY_LENGTH]; uint8_t publicKey[EDDSA25519_PUBLIC_KEY_LENGTH];
size_t len = EDDSA25519_PUBLIC_KEY_LENGTH; size_t len = EDDSA25519_PUBLIC_KEY_LENGTH;
EVP_PKEY_get_raw_public_key (m_Pkey, publicKey, &len); EVP_PKEY_get_raw_public_key (pkey, publicKey, &len);
if (signingPublicKey && memcmp (publicKey, signingPublicKey, EDDSA25519_PUBLIC_KEY_LENGTH)) if (signingPublicKey && memcmp (publicKey, signingPublicKey, EDDSA25519_PUBLIC_KEY_LENGTH))
{ {
LogPrint (eLogWarning, "EdDSA public key mismatch. Fallback"); LogPrint (eLogWarning, "EdDSA public key mismatch. Fallback");
m_Fallback = new EDDSA25519SignerCompat (signingPrivateKey, signingPublicKey); m_Fallback = new EDDSA25519SignerCompat (signingPrivateKey, signingPublicKey);
EVP_PKEY_free (m_Pkey);
m_Pkey = nullptr;
} }
else
{
m_MDCtx = EVP_MD_CTX_create ();
EVP_DigestSignInit (m_MDCtx, NULL, NULL, NULL, pkey);
}
EVP_PKEY_free (pkey);
} }
EDDSA25519Signer::~EDDSA25519Signer () EDDSA25519Signer::~EDDSA25519Signer ()
{ {
if (m_Fallback) delete m_Fallback; if (m_Fallback) delete m_Fallback;
if (m_Pkey) EVP_PKEY_free (m_Pkey); EVP_MD_CTX_destroy (m_MDCtx);
} }
void EDDSA25519Signer::Sign (const uint8_t * buf, int len, uint8_t * signature) const void EDDSA25519Signer::Sign (const uint8_t * buf, int len, uint8_t * signature) const
{ {
if (m_Fallback) if (m_Fallback) return m_Fallback->Sign (buf, len, signature);
return m_Fallback->Sign (buf, len, signature); else
else if (m_Pkey)
{ {
EVP_MD_CTX * ctx = EVP_MD_CTX_create ();
size_t l = 64; size_t l = 64;
uint8_t sig[64]; // temporary buffer for signature. openssl issue #7232 uint8_t sig[64]; // temporary buffer for signature. openssl issue #7232
EVP_DigestSignInit (ctx, NULL, NULL, NULL, m_Pkey); EVP_DigestSign (m_MDCtx, sig, &l, buf, len);
if (!EVP_DigestSign (ctx, sig, &l, buf, len))
LogPrint (eLogError, "EdDSA signing failed");
memcpy (signature, sig, 64); memcpy (signature, sig, 64);
EVP_MD_CTX_destroy (ctx);
} }
else
LogPrint (eLogError, "EdDSA signing key is not set");
} }
#endif #endif
} }

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2023, The PurpleI2P Project * Copyright (c) 2013-2021, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@ -304,7 +304,7 @@ namespace crypto
private: private:
#if OPENSSL_EDDSA #if OPENSSL_EDDSA
EVP_PKEY * m_Pkey; EVP_MD_CTX * m_MDCtx;
#else #else
EDDSAPoint m_PublicKey; EDDSAPoint m_PublicKey;
uint8_t m_PublicKeyEncoded[EDDSA25519_PUBLIC_KEY_LENGTH]; uint8_t m_PublicKeyEncoded[EDDSA25519_PUBLIC_KEY_LENGTH];
@ -341,7 +341,7 @@ namespace crypto
private: private:
EVP_PKEY * m_Pkey; EVP_MD_CTX * m_MDCtx;
EDDSA25519SignerCompat * m_Fallback; EDDSA25519SignerCompat * m_Fallback;
}; };
#else #else

@ -1,210 +0,0 @@
/*
* Copyright (c) 2024, The PurpleI2P Project
*
* This file is part of Purple i2pd project and licensed under BSD3
*
* See full license text in LICENSE file at top of project tree
*
*/
#ifndef SOCKS5_H__
#define SOCKS5_H__
#include <string>
#include <memory>
#include <boost/asio.hpp>
#include "I2PEndian.h"
namespace i2p
{
namespace transport
{
// SOCKS5 constants
const uint8_t SOCKS5_VER = 0x05;
const uint8_t SOCKS5_CMD_CONNECT = 0x01;
const uint8_t SOCKS5_CMD_UDP_ASSOCIATE = 0x03;
const uint8_t SOCKS5_ATYP_IPV4 = 0x01;
const uint8_t SOCKS5_ATYP_IPV6 = 0x04;
const uint8_t SOCKS5_ATYP_NAME = 0x03;
const size_t SOCKS5_UDP_IPV4_REQUEST_HEADER_SIZE = 10;
const size_t SOCKS5_UDP_IPV6_REQUEST_HEADER_SIZE = 22;
const uint8_t SOCKS5_REPLY_SUCCESS = 0x00;
const uint8_t SOCKS5_REPLY_SERVER_FAILURE = 0x01;
const uint8_t SOCKS5_REPLY_CONNECTION_NOT_ALLOWED = 0x02;
const uint8_t SOCKS5_REPLY_NETWORK_UNREACHABLE = 0x03;
const uint8_t SOCKS5_REPLY_HOST_UNREACHABLE = 0x04;
const uint8_t SOCKS5_REPLY_CONNECTION_REFUSED = 0x05;
const uint8_t SOCKS5_REPLY_TTL_EXPIRED = 0x06;
const uint8_t SOCKS5_REPLY_COMMAND_NOT_SUPPORTED = 0x07;
const uint8_t SOCKS5_REPLY_ADDRESS_TYPE_NOT_SUPPORTED = 0x08;
// SOCKS5 handshake
template<typename Socket, typename Handler>
void Socks5ReadReply (Socket& s, Handler handler)
{
auto readbuff = std::make_shared<std::vector<int8_t> >(258); // max possible
boost::asio::async_read(s, boost::asio::buffer(readbuff->data (), 5), boost::asio::transfer_all(), // read 4 bytes of header + first byte of address
[readbuff, &s, handler](const boost::system::error_code& ec, std::size_t transferred)
{
if (!ec)
{
if ((*readbuff)[1] == SOCKS5_REPLY_SUCCESS)
{
size_t len = 0;
switch ((*readbuff)[3]) // ATYP
{
case SOCKS5_ATYP_IPV4: len = 3; break; // address length 4 bytes
case SOCKS5_ATYP_IPV6: len = 15; break; // address length 16 bytes
case SOCKS5_ATYP_NAME: len += (*readbuff)[4]; break; // first byte of address is length
default: ;
}
if (len)
{
len += 2; // port
boost::asio::async_read(s, boost::asio::buffer(readbuff->data (), len), boost::asio::transfer_all(),
[readbuff, handler](const boost::system::error_code& ec, std::size_t transferred)
{
if (!ec)
handler (boost::system::error_code ()); // success
else
handler (boost::asio::error::make_error_code (boost::asio::error::connection_aborted));
});
}
else
handler (boost::asio::error::make_error_code (boost::asio::error::fault)); // unknown address type
}
else
switch ((*readbuff)[1]) // REP
{
case SOCKS5_REPLY_SERVER_FAILURE:
handler (boost::asio::error::make_error_code (boost::asio::error::access_denied ));
break;
case SOCKS5_REPLY_CONNECTION_NOT_ALLOWED:
handler (boost::asio::error::make_error_code (boost::asio::error::no_permission));
break;
case SOCKS5_REPLY_HOST_UNREACHABLE:
handler (boost::asio::error::make_error_code (boost::asio::error::host_unreachable));
break;
case SOCKS5_REPLY_NETWORK_UNREACHABLE:
handler (boost::asio::error::make_error_code (boost::asio::error::network_unreachable));
break;
case SOCKS5_REPLY_CONNECTION_REFUSED:
handler (boost::asio::error::make_error_code (boost::asio::error::connection_refused));
break;
case SOCKS5_REPLY_TTL_EXPIRED:
handler (boost::asio::error::make_error_code (boost::asio::error::timed_out));
break;
case SOCKS5_REPLY_COMMAND_NOT_SUPPORTED:
handler (boost::asio::error::make_error_code (boost::asio::error::operation_not_supported));
break;
case SOCKS5_REPLY_ADDRESS_TYPE_NOT_SUPPORTED:
handler (boost::asio::error::make_error_code (boost::asio::error::no_protocol_option));
break;
default:
handler (boost::asio::error::make_error_code (boost::asio::error::connection_aborted));
}
}
else
handler (ec);
});
}
template<typename Socket, typename Handler>
void Socks5Connect (Socket& s, Handler handler, std::shared_ptr<std::vector<uint8_t> > buff, uint16_t port)
{
if (buff && buff->size () >= 6)
{
(*buff)[0] = SOCKS5_VER;
(*buff)[1] = SOCKS5_CMD_CONNECT;
(*buff)[2] = 0x00;
htobe16buf(buff->data () + buff->size () - 2, port);
boost::asio::async_write(s, boost::asio::buffer(*buff), boost::asio::transfer_all(),
[buff, &s, handler](const boost::system::error_code& ec, std::size_t transferred)
{
(void) transferred;
if (!ec)
Socks5ReadReply (s, handler);
else
handler (ec);
});
}
else
handler (boost::asio::error::make_error_code (boost::asio::error::no_buffer_space));
}
template<typename Socket, typename Handler>
void Socks5Connect (Socket& s, const boost::asio::ip::tcp::endpoint& ep, Handler handler)
{
std::shared_ptr<std::vector<uint8_t> > buff;
if(ep.address ().is_v4 ())
{
buff = std::make_shared<std::vector<uint8_t> >(10);
(*buff)[3] = SOCKS5_ATYP_IPV4;
auto addrbytes = ep.address ().to_v4().to_bytes();
memcpy(buff->data () + 4, addrbytes.data(), 4);
}
else if (ep.address ().is_v6 ())
{
buff = std::make_shared<std::vector<uint8_t> >(22);
(*buff)[3] = SOCKS5_ATYP_IPV6;
auto addrbytes = ep.address ().to_v6().to_bytes();
memcpy(buff->data () + 4, addrbytes.data(), 16);
}
if (buff)
Socks5Connect (s, handler, buff, ep.port ());
else
handler (boost::asio::error::make_error_code (boost::asio::error::fault));
}
template<typename Socket, typename Handler>
void Socks5Connect (Socket& s, const std::pair<std::string, uint16_t>& ep, Handler handler)
{
auto& addr = ep.first;
if (addr.length () <= 255)
{
auto buff = std::make_shared<std::vector<uint8_t> >(addr.length () + 7);
(*buff)[3] = SOCKS5_ATYP_NAME;
(*buff)[4] = addr.length ();
memcpy (buff->data () + 5, addr.c_str (), addr.length ());
Socks5Connect (s, handler, buff, ep.second);
}
else
handler (boost::asio::error::make_error_code (boost::asio::error::name_too_long));
}
template<typename Socket, typename Endpoint, typename Handler>
void Socks5Handshake (Socket& s, Endpoint ep, Handler handler)
{
static const uint8_t methodSelection[3] = { SOCKS5_VER, 0x01, 0x00 }; // 1 method, no auth
boost::asio::async_write(s, boost::asio::buffer(methodSelection, 3), boost::asio::transfer_all(),
[&s, ep, handler] (const boost::system::error_code& ec, std::size_t transferred)
{
(void) transferred;
if (!ec)
{
auto readbuff = std::make_shared<std::vector<uint8_t> >(2);
boost::asio::async_read(s, boost::asio::buffer(*readbuff), boost::asio::transfer_all(),
[&s, ep, handler, readbuff] (const boost::system::error_code& ec, std::size_t transferred)
{
if (!ec)
{
if (transferred == 2 && (*readbuff)[1] == 0x00) // no auth
Socks5Connect (s, ep, handler);
else
handler (boost::asio::error::make_error_code (boost::asio::error::invalid_argument));
}
else
handler (ec);
});
}
else
handler (ec);
});
}
}
}
#endif

@ -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 * This file is part of Purple i2pd project and licensed under BSD3
* *
@ -19,6 +19,11 @@ namespace i2p
{ {
namespace stream namespace stream
{ {
void SendBufferQueue::Add (const uint8_t * buf, size_t len, SendHandler handler)
{
Add (std::make_shared<SendBuffer>(buf, len, handler));
}
void SendBufferQueue::Add (std::shared_ptr<SendBuffer> buf) void SendBufferQueue::Add (std::shared_ptr<SendBuffer> buf)
{ {
if (buf) if (buf)
@ -68,12 +73,11 @@ namespace stream
Stream::Stream (boost::asio::io_service& service, StreamingDestination& local, Stream::Stream (boost::asio::io_service& service, StreamingDestination& local,
std::shared_ptr<const i2p::data::LeaseSet> remote, int port): m_Service (service), std::shared_ptr<const i2p::data::LeaseSet> remote, int port): m_Service (service),
m_SendStreamID (0), m_SequenceNumber (0), m_SendStreamID (0), m_SequenceNumber (0), m_LastReceivedSequenceNumber (-1),
m_TunnelsChangeSequenceNumber (0), m_LastReceivedSequenceNumber (-1),
m_Status (eStreamStatusNew), m_IsAckSendScheduled (false), m_LocalDestination (local), m_Status (eStreamStatusNew), m_IsAckSendScheduled (false), m_LocalDestination (local),
m_RemoteLeaseSet (remote), m_ReceiveTimer (m_Service), m_ResendTimer (m_Service), m_RemoteLeaseSet (remote), m_ReceiveTimer (m_Service), m_ResendTimer (m_Service),
m_AckSendTimer (m_Service), m_NumSentBytes (0), m_NumReceivedBytes (0), m_Port (port), m_AckSendTimer (m_Service), m_NumSentBytes (0), m_NumReceivedBytes (0), m_Port (port),
m_RTT (INITIAL_RTT), m_WindowSize (MIN_WINDOW_SIZE), m_RTO (INITIAL_RTO), m_WindowSize (MIN_WINDOW_SIZE), m_RTT (INITIAL_RTT), m_RTO (INITIAL_RTO),
m_AckDelay (local.GetOwner ()->GetStreamingAckDelay ()), m_AckDelay (local.GetOwner ()->GetStreamingAckDelay ()),
m_LastWindowSizeIncreaseTime (0), m_NumResendAttempts (0), m_MTU (STREAMING_MTU) m_LastWindowSizeIncreaseTime (0), m_NumResendAttempts (0), m_MTU (STREAMING_MTU)
{ {
@ -82,12 +86,11 @@ namespace stream
} }
Stream::Stream (boost::asio::io_service& service, StreamingDestination& local): Stream::Stream (boost::asio::io_service& service, StreamingDestination& local):
m_Service (service), m_SendStreamID (0), m_SequenceNumber (0), m_Service (service), m_SendStreamID (0), m_SequenceNumber (0), m_LastReceivedSequenceNumber (-1),
m_TunnelsChangeSequenceNumber (0), m_LastReceivedSequenceNumber (-1),
m_Status (eStreamStatusNew), m_IsAckSendScheduled (false), m_LocalDestination (local), m_Status (eStreamStatusNew), m_IsAckSendScheduled (false), m_LocalDestination (local),
m_ReceiveTimer (m_Service), m_ResendTimer (m_Service), m_AckSendTimer (m_Service), m_ReceiveTimer (m_Service), m_ResendTimer (m_Service), m_AckSendTimer (m_Service),
m_NumSentBytes (0), m_NumReceivedBytes (0), m_Port (0), m_RTT (INITIAL_RTT), m_NumSentBytes (0), m_NumReceivedBytes (0), m_Port (0), m_WindowSize (MIN_WINDOW_SIZE),
m_WindowSize (MIN_WINDOW_SIZE), m_RTO (INITIAL_RTO), m_AckDelay (local.GetOwner ()->GetStreamingAckDelay ()), m_RTT (INITIAL_RTT), m_RTO (INITIAL_RTO), m_AckDelay (local.GetOwner ()->GetStreamingAckDelay ()),
m_LastWindowSizeIncreaseTime (0), m_NumResendAttempts (0), m_MTU (STREAMING_MTU) m_LastWindowSizeIncreaseTime (0), m_NumResendAttempts (0), m_MTU (STREAMING_MTU)
{ {
RAND_bytes ((uint8_t *)&m_RecvStreamID, 4); RAND_bytes ((uint8_t *)&m_RecvStreamID, 4);
@ -112,7 +115,10 @@ namespace stream
void Stream::CleanUp () void Stream::CleanUp ()
{ {
m_SendBuffer.CleanUp (); {
std::unique_lock<std::mutex> l(m_SendBufferMutex);
m_SendBuffer.CleanUp ();
}
while (!m_ReceiveQueue.empty ()) while (!m_ReceiveQueue.empty ())
{ {
auto packet = m_ReceiveQueue.front (); auto packet = m_ReceiveQueue.front ();
@ -131,11 +137,6 @@ namespace stream
void Stream::HandleNextPacket (Packet * packet) void Stream::HandleNextPacket (Packet * packet)
{ {
if (m_Status == eStreamStatusTerminated)
{
m_LocalDestination.DeletePacket (packet);
return;
}
m_NumReceivedBytes += packet->GetLength (); m_NumReceivedBytes += packet->GetLength ();
if (!m_SendStreamID) if (!m_SendStreamID)
{ {
@ -166,8 +167,7 @@ namespace stream
{ {
// we have received next in sequence message // we have received next in sequence message
ProcessPacket (packet); ProcessPacket (packet);
if (m_Status == eStreamStatusTerminated) return;
// we should also try stored messages if any // we should also try stored messages if any
for (auto it = m_SavedPackets.begin (); it != m_SavedPackets.end ();) for (auto it = m_SavedPackets.begin (); it != m_SavedPackets.end ();)
{ {
@ -177,7 +177,6 @@ namespace stream
m_SavedPackets.erase (it++); m_SavedPackets.erase (it++);
ProcessPacket (savedPacket); ProcessPacket (savedPacket);
if (m_Status == eStreamStatusTerminated) return;
} }
else else
break; break;
@ -188,9 +187,13 @@ namespace stream
{ {
if (!m_IsAckSendScheduled) if (!m_IsAckSendScheduled)
{ {
m_IsAckSendScheduled = true;
auto ackTimeout = m_RTT/10; auto ackTimeout = m_RTT/10;
if (ackTimeout > m_AckDelay) ackTimeout = m_AckDelay; if (ackTimeout > m_AckDelay) ackTimeout = m_AckDelay;
ScheduleAck (ackTimeout); else if (ackTimeout < MIN_SEND_ACK_TIMEOUT) ackTimeout = MIN_SEND_ACK_TIMEOUT;
m_AckSendTimer.expires_from_now (boost::posix_time::milliseconds(ackTimeout));
m_AckSendTimer.async_wait (std::bind (&Stream::HandleAckSendTimer,
shared_from_this (), std::placeholders::_1));
} }
} }
else if (packet->IsSYN ()) else if (packet->IsSYN ())
@ -213,17 +216,22 @@ namespace stream
SavePacket (packet); SavePacket (packet);
if (m_LastReceivedSequenceNumber >= 0) if (m_LastReceivedSequenceNumber >= 0)
{ {
if (!m_IsAckSendScheduled) // send NACKs for missing messages ASAP
{ if (m_IsAckSendScheduled)
// send NACKs for missing messages {
int ackTimeout = MIN_SEND_ACK_TIMEOUT*m_SavedPackets.size (); m_IsAckSendScheduled = false;
if (ackTimeout > m_AckDelay) ackTimeout = m_AckDelay; m_AckSendTimer.cancel ();
ScheduleAck (ackTimeout); }
} SendQuickAck ();
} }
else else
{
// wait for SYN // wait for SYN
ScheduleAck (SYN_TIMEOUT); m_IsAckSendScheduled = true;
m_AckSendTimer.expires_from_now (boost::posix_time::milliseconds(SYN_TIMEOUT));
m_AckSendTimer.async_wait (std::bind (&Stream::HandleAckSendTimer,
shared_from_this (), std::placeholders::_1));
}
} }
} }
} }
@ -289,8 +297,6 @@ namespace stream
m_AckSendTimer.async_wait (std::bind (&Stream::HandleAckSendTimer, m_AckSendTimer.async_wait (std::bind (&Stream::HandleAckSendTimer,
shared_from_this (), std::placeholders::_1)); shared_from_this (), std::placeholders::_1));
} }
if (delayRequested >= DELAY_CHOKING)
m_WindowSize = 1;
} }
optionData += 2; optionData += 2;
} }
@ -408,8 +414,6 @@ namespace stream
LogPrint (eLogError, "Streaming: Unexpected ackThrough=", ackThrough, " > seqn=", m_SequenceNumber); LogPrint (eLogError, "Streaming: Unexpected ackThrough=", ackThrough, " > seqn=", m_SequenceNumber);
return; return;
} }
int rttSample = INT_MAX;
bool firstRttSample = false;
int nackCount = packet->GetNACKCount (); int nackCount = packet->GetNACKCount ();
for (auto it = m_SentPackets.begin (); it != m_SentPackets.end ();) for (auto it = m_SentPackets.begin (); it != m_SentPackets.end ();)
{ {
@ -433,51 +437,38 @@ namespace stream
} }
} }
auto sentPacket = *it; auto sentPacket = *it;
int64_t rtt = (int64_t)ts - (int64_t)sentPacket->sendTime; uint64_t rtt = ts - sentPacket->sendTime;
if (rtt < 0) if(ts < sentPacket->sendTime)
LogPrint (eLogError, "Streaming: Packet ", seqn, "sent from the future, sendTime=", sentPacket->sendTime);
if (!seqn)
{ {
firstRttSample = true; LogPrint(eLogError, "Streaming: Packet ", seqn, "sent from the future, sendTime=", sentPacket->sendTime);
rttSample = rtt < 0 ? 1 : rtt; rtt = 1;
} }
else if (!sentPacket->resent && seqn > m_TunnelsChangeSequenceNumber && rtt >= 0) m_RTT = std::round ((m_RTT*seqn + rtt)/(seqn + 1.0));
rttSample = std::min (rttSample, (int)rtt); m_RTO = m_RTT*1.5; // TODO: implement it better
LogPrint (eLogDebug, "Streaming: Packet ", seqn, " acknowledged rtt=", rtt, " sentTime=", sentPacket->sendTime); LogPrint (eLogDebug, "Streaming: Packet ", seqn, " acknowledged rtt=", rtt, " sentTime=", sentPacket->sendTime);
m_SentPackets.erase (it++); m_SentPackets.erase (it++);
m_LocalDestination.DeletePacket (sentPacket); m_LocalDestination.DeletePacket (sentPacket);
acknowledged = true; acknowledged = true;
if (m_WindowSize < WINDOW_SIZE) if (m_WindowSize < WINDOW_SIZE)
m_WindowSize++; // slow start m_WindowSize++; // slow start
else
{
// linear growth
if (ts > m_LastWindowSizeIncreaseTime + m_RTT)
{
m_WindowSize++;
if (m_WindowSize > MAX_WINDOW_SIZE) m_WindowSize = MAX_WINDOW_SIZE;
m_LastWindowSizeIncreaseTime = ts;
}
}
if (!seqn && m_RoutingSession) // first message confirmed
m_RoutingSession->SetSharedRoutingPath (
std::make_shared<i2p::garlic::GarlicRoutingPath> (
i2p::garlic::GarlicRoutingPath{m_CurrentOutboundTunnel, m_CurrentRemoteLease, m_RTT, 0, 0}));
} }
else else
break; break;
} }
if (rttSample != INT_MAX)
{
if (firstRttSample)
m_RTT = rttSample;
else
m_RTT = RTT_EWMA_ALPHA * rttSample + (1.0 - RTT_EWMA_ALPHA) * m_RTT;
bool wasInitial = m_RTO == INITIAL_RTO;
m_RTO = std::max (MIN_RTO, (int)(m_RTT * 1.5)); // TODO: implement it better
if (wasInitial)
ScheduleResend ();
}
if (acknowledged && m_WindowSize >= WINDOW_SIZE)
{
// linear growth
if (ts > m_LastWindowSizeIncreaseTime + m_RTT)
{
m_WindowSize++;
if (m_WindowSize > MAX_WINDOW_SIZE) m_WindowSize = MAX_WINDOW_SIZE;
m_LastWindowSizeIncreaseTime = ts;
}
}
if (firstRttSample && m_RoutingSession)
m_RoutingSession->SetSharedRoutingPath (
std::make_shared<i2p::garlic::GarlicRoutingPath> (
i2p::garlic::GarlicRoutingPath{m_CurrentOutboundTunnel, m_CurrentRemoteLease, (int)m_RTT, 0, 0}));
if (m_SentPackets.empty ()) if (m_SentPackets.empty ())
m_ResendTimer.cancel (); m_ResendTimer.cancel ();
if (acknowledged) if (acknowledged)
@ -541,18 +532,14 @@ namespace stream
void Stream::AsyncSend (const uint8_t * buf, size_t len, SendHandler handler) void Stream::AsyncSend (const uint8_t * buf, size_t len, SendHandler handler)
{ {
std::shared_ptr<i2p::stream::SendBuffer> buffer;
if (len > 0 && buf) if (len > 0 && buf)
buffer = std::make_shared<i2p::stream::SendBuffer>(buf, len, handler); {
std::unique_lock<std::mutex> l(m_SendBufferMutex);
m_SendBuffer.Add (buf, len, handler);
}
else if (handler) else if (handler)
handler(boost::system::error_code ()); handler(boost::system::error_code ());
auto s = shared_from_this (); m_Service.post (std::bind (&Stream::SendBuffer, shared_from_this ()));
m_Service.post ([s, buffer]()
{
if (buffer)
s->m_SendBuffer.Add (buffer);
s->SendBuffer ();
});
} }
void Stream::SendBuffer () void Stream::SendBuffer ()
@ -562,88 +549,91 @@ namespace stream
bool isNoAck = m_LastReceivedSequenceNumber < 0; // first packet bool isNoAck = m_LastReceivedSequenceNumber < 0; // first packet
std::vector<Packet *> packets; std::vector<Packet *> packets;
while ((m_Status == eStreamStatusNew) || (IsEstablished () && !m_SendBuffer.IsEmpty () && numMsgs > 0))
{ {
Packet * p = m_LocalDestination.NewPacket (); std::unique_lock<std::mutex> l(m_SendBufferMutex);
uint8_t * packet = p->GetBuffer (); while ((m_Status == eStreamStatusNew) || (IsEstablished () && !m_SendBuffer.IsEmpty () && numMsgs > 0))
// TODO: implement setters
size_t size = 0;
htobe32buf (packet + size, m_SendStreamID);
size += 4; // sendStreamID
htobe32buf (packet + size, m_RecvStreamID);
size += 4; // receiveStreamID
htobe32buf (packet + size, m_SequenceNumber++);
size += 4; // sequenceNum
if (isNoAck)
htobuf32 (packet + size, 0);
else
htobe32buf (packet + size, m_LastReceivedSequenceNumber);
size += 4; // ack Through
if (m_Status == eStreamStatusNew && !m_SendStreamID && m_RemoteIdentity)
{ {
// first SYN packet Packet * p = m_LocalDestination.NewPacket ();
packet[size] = 8; uint8_t * packet = p->GetBuffer ();
size++; // NACK count // TODO: implement setters
memcpy (packet + size, m_RemoteIdentity->GetIdentHash (), 32); size_t size = 0;
size += 32; htobe32buf (packet + size, m_SendStreamID);
} size += 4; // sendStreamID
else htobe32buf (packet + size, m_RecvStreamID);
{ size += 4; // receiveStreamID
packet[size] = 0; htobe32buf (packet + size, m_SequenceNumber++);
size++; // NACK count size += 4; // sequenceNum
} if (isNoAck)
packet[size] = m_RTO/1000; htobuf32 (packet + size, 0);
size++; // resend delay else
if (m_Status == eStreamStatusNew) htobe32buf (packet + size, m_LastReceivedSequenceNumber);
{ size += 4; // ack Through
// initial packet if (m_Status == eStreamStatusNew && !m_SendStreamID && m_RemoteIdentity)
m_Status = eStreamStatusOpen;
if (!m_RemoteLeaseSet) m_RemoteLeaseSet = m_LocalDestination.GetOwner ()->FindLeaseSet (m_RemoteIdentity->GetIdentHash ());;
if (m_RemoteLeaseSet)
{ {
m_RoutingSession = m_LocalDestination.GetOwner ()->GetRoutingSession (m_RemoteLeaseSet, true); // first SYN packet
m_MTU = m_RoutingSession->IsRatchets () ? STREAMING_MTU_RATCHETS : STREAMING_MTU; packet[size] = 8;
size++; // NACK count
memcpy (packet + size, m_RemoteIdentity->GetIdentHash (), 32);
size += 32;
}
else
{
packet[size] = 0;
size++; // NACK count
}
packet[size] = m_RTO/1000;
size++; // resend delay
if (m_Status == eStreamStatusNew)
{
// initial packet
m_Status = eStreamStatusOpen;
if (!m_RemoteLeaseSet) m_RemoteLeaseSet = m_LocalDestination.GetOwner ()->FindLeaseSet (m_RemoteIdentity->GetIdentHash ());;
if (m_RemoteLeaseSet)
{
m_RoutingSession = m_LocalDestination.GetOwner ()->GetRoutingSession (m_RemoteLeaseSet, true);
m_MTU = m_RoutingSession->IsRatchets () ? STREAMING_MTU_RATCHETS : STREAMING_MTU;
}
uint16_t flags = PACKET_FLAG_SYNCHRONIZE | PACKET_FLAG_FROM_INCLUDED |
PACKET_FLAG_SIGNATURE_INCLUDED | PACKET_FLAG_MAX_PACKET_SIZE_INCLUDED;
if (isNoAck) flags |= PACKET_FLAG_NO_ACK;
bool isOfflineSignature = m_LocalDestination.GetOwner ()->GetPrivateKeys ().IsOfflineSignature ();
if (isOfflineSignature) flags |= PACKET_FLAG_OFFLINE_SIGNATURE;
htobe16buf (packet + size, flags);
size += 2; // flags
size_t identityLen = m_LocalDestination.GetOwner ()->GetIdentity ()->GetFullLen ();
size_t signatureLen = m_LocalDestination.GetOwner ()->GetPrivateKeys ().GetSignatureLen ();
uint8_t * optionsSize = packet + size; // set options size later
size += 2; // options size
m_LocalDestination.GetOwner ()->GetIdentity ()->ToBuffer (packet + size, identityLen);
size += identityLen; // from
htobe16buf (packet + size, m_MTU);
size += 2; // max packet size
if (isOfflineSignature)
{
const auto& offlineSignature = m_LocalDestination.GetOwner ()->GetPrivateKeys ().GetOfflineSignature ();
memcpy (packet + size, offlineSignature.data (), offlineSignature.size ());
size += offlineSignature.size (); // offline signature
}
uint8_t * signature = packet + size; // set it later
memset (signature, 0, signatureLen); // zeroes for now
size += signatureLen; // signature
htobe16buf (optionsSize, packet + size - 2 - optionsSize); // actual options size
size += m_SendBuffer.Get (packet + size, m_MTU); // payload
m_LocalDestination.GetOwner ()->Sign (packet, size, signature);
} }
uint16_t flags = PACKET_FLAG_SYNCHRONIZE | PACKET_FLAG_FROM_INCLUDED | else
PACKET_FLAG_SIGNATURE_INCLUDED | PACKET_FLAG_MAX_PACKET_SIZE_INCLUDED;
if (isNoAck) flags |= PACKET_FLAG_NO_ACK;
bool isOfflineSignature = m_LocalDestination.GetOwner ()->GetPrivateKeys ().IsOfflineSignature ();
if (isOfflineSignature) flags |= PACKET_FLAG_OFFLINE_SIGNATURE;
htobe16buf (packet + size, flags);
size += 2; // flags
size_t identityLen = m_LocalDestination.GetOwner ()->GetIdentity ()->GetFullLen ();
size_t signatureLen = m_LocalDestination.GetOwner ()->GetPrivateKeys ().GetSignatureLen ();
uint8_t * optionsSize = packet + size; // set options size later
size += 2; // options size
m_LocalDestination.GetOwner ()->GetIdentity ()->ToBuffer (packet + size, identityLen);
size += identityLen; // from
htobe16buf (packet + size, m_MTU);
size += 2; // max packet size
if (isOfflineSignature)
{ {
const auto& offlineSignature = m_LocalDestination.GetOwner ()->GetPrivateKeys ().GetOfflineSignature (); // follow on packet
memcpy (packet + size, offlineSignature.data (), offlineSignature.size ()); htobuf16 (packet + size, 0);
size += offlineSignature.size (); // offline signature size += 2; // flags
htobuf16 (packet + size, 0); // no options
size += 2; // options size
size += m_SendBuffer.Get(packet + size, m_MTU); // payload
} }
uint8_t * signature = packet + size; // set it later p->len = size;
memset (signature, 0, signatureLen); // zeroes for now packets.push_back (p);
size += signatureLen; // signature numMsgs--;
htobe16buf (optionsSize, packet + size - 2 - optionsSize); // actual options size
size += m_SendBuffer.Get (packet + size, m_MTU); // payload
m_LocalDestination.GetOwner ()->Sign (packet, size, signature);
}
else
{
// follow on packet
htobuf16 (packet + size, 0);
size += 2; // flags
htobuf16 (packet + size, 0); // no options
size += 2; // options size
size += m_SendBuffer.Get(packet + size, m_MTU); // payload
} }
p->len = size;
packets.push_back (p);
numMsgs--;
} }
if (packets.size () > 0) if (packets.size () > 0)
{ {
@ -693,7 +683,6 @@ namespace stream
htobe32buf (packet + size, lastReceivedSeqn); htobe32buf (packet + size, lastReceivedSeqn);
size += 4; // ack Through size += 4; // ack Through
uint8_t numNacks = 0; uint8_t numNacks = 0;
bool choking = false;
if (lastReceivedSeqn > m_LastReceivedSequenceNumber) if (lastReceivedSeqn > m_LastReceivedSequenceNumber)
{ {
// fill NACKs // fill NACKs
@ -705,8 +694,7 @@ namespace stream
if (numNacks + (seqn - nextSeqn) >= 256) if (numNacks + (seqn - nextSeqn) >= 256)
{ {
LogPrint (eLogError, "Streaming: Number of NACKs exceeds 256. seqn=", seqn, " nextSeqn=", nextSeqn); LogPrint (eLogError, "Streaming: Number of NACKs exceeds 256. seqn=", seqn, " nextSeqn=", nextSeqn);
htobe32buf (packet + 12, nextSeqn - 1); // change ack Through back htobe32buf (packet + 12, nextSeqn); // change ack Through
choking = true;
break; break;
} }
for (uint32_t i = nextSeqn; i < seqn; i++) for (uint32_t i = nextSeqn; i < seqn; i++)
@ -728,17 +716,10 @@ namespace stream
size++; // NACK count size++; // NACK count
} }
packet[size] = 0; packet[size] = 0;
size++; // resend delay size++; // resend delay
htobuf16 (packet + size, choking ? PACKET_FLAG_DELAY_REQUESTED : 0); // no flags set or delay htobuf16 (packet + size, 0); // no flags set
size += 2; // flags size += 2; // flags
if (choking) htobuf16 (packet + size, 0); // no options
{
htobuf16 (packet + size, 2); // 2 bytes delay interval
htobuf16 (packet + size + 2, DELAY_CHOKING); // set choking interval
size += 2;
}
else
htobuf16 (packet + size, 0); // no options
size += 2; // options size size += 2; // options size
p.len = size; p.len = size;
@ -906,7 +887,7 @@ namespace stream
m_CurrentOutboundTunnel = routingPath->outboundTunnel; m_CurrentOutboundTunnel = routingPath->outboundTunnel;
m_CurrentRemoteLease = routingPath->remoteLease; m_CurrentRemoteLease = routingPath->remoteLease;
m_RTT = routingPath->rtt; m_RTT = routingPath->rtt;
m_RTO = std::max (MIN_RTO, (int)(m_RTT * 1.5)); // TODO: implement it better m_RTO = m_RTT*1.5; // TODO: implement it better
} }
} }
@ -916,27 +897,20 @@ namespace stream
UpdateCurrentRemoteLease (true); UpdateCurrentRemoteLease (true);
if (m_CurrentRemoteLease && ts < m_CurrentRemoteLease->endDate + i2p::data::LEASE_ENDDATE_THRESHOLD) if (m_CurrentRemoteLease && ts < m_CurrentRemoteLease->endDate + i2p::data::LEASE_ENDDATE_THRESHOLD)
{ {
bool freshTunnel = false;
if (!m_CurrentOutboundTunnel) if (!m_CurrentOutboundTunnel)
{ {
auto leaseRouter = i2p::data::netdb.FindRouter (m_CurrentRemoteLease->tunnelGateway); auto leaseRouter = i2p::data::netdb.FindRouter (m_CurrentRemoteLease->tunnelGateway);
m_CurrentOutboundTunnel = m_LocalDestination.GetOwner ()->GetTunnelPool ()->GetNextOutboundTunnel (nullptr, m_CurrentOutboundTunnel = m_LocalDestination.GetOwner ()->GetTunnelPool ()->GetNextOutboundTunnel (nullptr,
leaseRouter ? leaseRouter->GetCompatibleTransports (false) : (i2p::data::RouterInfo::CompatibleTransports)i2p::data::RouterInfo::eAllTransports); leaseRouter ? leaseRouter->GetCompatibleTransports (false) : (i2p::data::RouterInfo::CompatibleTransports)i2p::data::RouterInfo::eAllTransports);
freshTunnel = true;
} }
else if (!m_CurrentOutboundTunnel->IsEstablished ()) else if (!m_CurrentOutboundTunnel->IsEstablished ())
std::tie(m_CurrentOutboundTunnel, freshTunnel) = m_LocalDestination.GetOwner ()->GetTunnelPool ()->GetNewOutboundTunnel (m_CurrentOutboundTunnel); m_CurrentOutboundTunnel = m_LocalDestination.GetOwner ()->GetTunnelPool ()->GetNewOutboundTunnel (m_CurrentOutboundTunnel);
if (!m_CurrentOutboundTunnel) if (!m_CurrentOutboundTunnel)
{ {
LogPrint (eLogError, "Streaming: No outbound tunnels in the pool, sSID=", m_SendStreamID); LogPrint (eLogError, "Streaming: No outbound tunnels in the pool, sSID=", m_SendStreamID);
m_CurrentRemoteLease = nullptr; m_CurrentRemoteLease = nullptr;
return; return;
} }
if (freshTunnel)
{
m_RTO = INITIAL_RTO;
m_TunnelsChangeSequenceNumber = m_SequenceNumber; // should be determined more precisely
}
std::vector<i2p::tunnel::TunnelMessageBlock> msgs; std::vector<i2p::tunnel::TunnelMessageBlock> msgs;
for (const auto& it: packets) for (const auto& it: packets)
@ -968,10 +942,10 @@ namespace stream
if (m_RoutingSession->IsLeaseSetNonConfirmed ()) if (m_RoutingSession->IsLeaseSetNonConfirmed ())
{ {
auto ts = i2p::util::GetMillisecondsSinceEpoch (); auto ts = i2p::util::GetMillisecondsSinceEpoch ();
if (ts > m_RoutingSession->GetLeaseSetSubmissionTime () + i2p::garlic::LEASESET_CONFIRMATION_TIMEOUT) if (ts > m_RoutingSession->GetLeaseSetSubmissionTime () + i2p::garlic::LEASET_CONFIRMATION_TIMEOUT)
{ {
// LeaseSet was not confirmed, should try other tunnels // LeaseSet was not confirmed, should try other tunnels
LogPrint (eLogWarning, "Streaming: LeaseSet was not confirmed in ", i2p::garlic::LEASESET_CONFIRMATION_TIMEOUT, " milliseconds. Trying to resubmit"); LogPrint (eLogWarning, "Streaming: LeaseSet was not confirmed in ", i2p::garlic::LEASET_CONFIRMATION_TIMEOUT, " milliseconds. Trying to resubmit");
m_RoutingSession->SetSharedRoutingPath (nullptr); m_RoutingSession->SetSharedRoutingPath (nullptr);
m_CurrentOutboundTunnel = nullptr; m_CurrentOutboundTunnel = nullptr;
m_CurrentRemoteLease = nullptr; m_CurrentRemoteLease = nullptr;
@ -1021,7 +995,6 @@ namespace stream
{ {
if (ts >= it->sendTime + m_RTO) if (ts >= it->sendTime + m_RTO)
{ {
it->resent = true;
it->sendTime = ts; it->sendTime = ts;
packets.push_back (it); packets.push_back (it);
} }
@ -1031,31 +1004,31 @@ namespace stream
if (packets.size () > 0) if (packets.size () > 0)
{ {
m_NumResendAttempts++; m_NumResendAttempts++;
if (m_NumResendAttempts == 1 && m_RTO != INITIAL_RTO) m_RTO *= 2;
{ switch (m_NumResendAttempts)
// congestion avoidance
m_RTO *= 2;
m_WindowSize -= (m_WindowSize + WINDOW_SIZE_DROP_FRACTION) / WINDOW_SIZE_DROP_FRACTION; // adjustment >= 1
if (m_WindowSize < MIN_WINDOW_SIZE) m_WindowSize = MIN_WINDOW_SIZE;
}
else
{ {
m_TunnelsChangeSequenceNumber = m_SequenceNumber; case 1: // congesion avoidance
m_RTO = INITIAL_RTO; // drop RTO to initial upon tunnels pair change m_WindowSize >>= 1; // /2
if (m_RoutingSession) m_RoutingSession->SetSharedRoutingPath (nullptr); if (m_WindowSize < MIN_WINDOW_SIZE) m_WindowSize = MIN_WINDOW_SIZE;
if (m_NumResendAttempts & 1) break;
{ case 2:
m_RTO = INITIAL_RTO; // drop RTO to initial upon tunnels pair change first time
#if (__cplusplus >= 201703L) // C++ 17 or higher
[[fallthrough]];
#endif
// no break here
case 4:
if (m_RoutingSession) m_RoutingSession->SetSharedRoutingPath (nullptr);
UpdateCurrentRemoteLease (); // pick another lease
LogPrint (eLogWarning, "Streaming: Another remote lease has been selected for stream with rSID=", m_RecvStreamID, ", sSID=", m_SendStreamID);
break;
case 3:
// pick another outbound tunnel // pick another outbound tunnel
if (m_RoutingSession) m_RoutingSession->SetSharedRoutingPath (nullptr);
m_CurrentOutboundTunnel = m_LocalDestination.GetOwner ()->GetTunnelPool ()->GetNextOutboundTunnel (m_CurrentOutboundTunnel); m_CurrentOutboundTunnel = m_LocalDestination.GetOwner ()->GetTunnelPool ()->GetNextOutboundTunnel (m_CurrentOutboundTunnel);
LogPrint (eLogWarning, "Streaming: Resend #", m_NumResendAttempts, LogPrint (eLogWarning, "Streaming: Another outbound tunnel has been selected for stream with sSID=", m_SendStreamID);
", another outbound tunnel has been selected for stream with sSID=", m_SendStreamID); break;
} default: ;
else
{
UpdateCurrentRemoteLease (); // pick another lease
LogPrint (eLogWarning, "Streaming: Resend #", m_NumResendAttempts,
", another remote lease has been selected for stream with rSID=", m_RecvStreamID, ", sSID=", m_SendStreamID);
}
} }
SendPackets (packets); SendPackets (packets);
} }
@ -1063,17 +1036,6 @@ namespace stream
} }
} }
void Stream::ScheduleAck (int timeout)
{
if (m_IsAckSendScheduled)
m_AckSendTimer.cancel ();
m_IsAckSendScheduled = true;
if (timeout < MIN_SEND_ACK_TIMEOUT) timeout = MIN_SEND_ACK_TIMEOUT;
m_AckSendTimer.expires_from_now (boost::posix_time::milliseconds(timeout));
m_AckSendTimer.async_wait (std::bind (&Stream::HandleAckSendTimer,
shared_from_this (), std::placeholders::_1));
}
void Stream::HandleAckSendTimer (const boost::system::error_code& ecode) void Stream::HandleAckSendTimer (const boost::system::error_code& ecode)
{ {
if (m_IsAckSendScheduled) if (m_IsAckSendScheduled)
@ -1089,13 +1051,9 @@ namespace stream
{ {
if (m_RoutingSession && m_RoutingSession->IsLeaseSetNonConfirmed ()) if (m_RoutingSession && m_RoutingSession->IsLeaseSetNonConfirmed ())
{ {
auto ts = i2p::util::GetMillisecondsSinceEpoch (); // seems something went wrong and we should re-select tunnels
if (ts > m_RoutingSession->GetLeaseSetSubmissionTime () + i2p::garlic::LEASESET_CONFIRMATION_TIMEOUT) m_CurrentOutboundTunnel = nullptr;
{ m_CurrentRemoteLease = nullptr;
// seems something went wrong and we should re-select tunnels
m_CurrentOutboundTunnel = nullptr;
m_CurrentRemoteLease = nullptr;
}
} }
SendQuickAck (); SendQuickAck ();
} }
@ -1184,16 +1142,6 @@ namespace stream
} }
} }
void Stream::ResetRoutingPath ()
{
m_CurrentOutboundTunnel = nullptr;
m_CurrentRemoteLease = nullptr;
m_RTT = INITIAL_RTT;
m_RTO = INITIAL_RTO;
if (m_RoutingSession)
m_RoutingSession->SetSharedRoutingPath (nullptr); // TODO: count failures
}
StreamingDestination::StreamingDestination (std::shared_ptr<i2p::client::ClientDestination> owner, uint16_t localPort, bool gzip): StreamingDestination::StreamingDestination (std::shared_ptr<i2p::client::ClientDestination> owner, uint16_t localPort, bool gzip):
m_Owner (owner), m_LocalPort (localPort), m_Gzip (gzip), m_Owner (owner), m_LocalPort (localPort), m_Gzip (gzip),
m_PendingIncomingTimer (m_Owner->GetService ()) m_PendingIncomingTimer (m_Owner->GetService ())
@ -1274,7 +1222,6 @@ namespace stream
{ {
// already pending // already pending
LogPrint(eLogWarning, "Streaming: Incoming streaming with rSID=", receiveStreamID, " already exists"); LogPrint(eLogWarning, "Streaming: Incoming streaming with rSID=", receiveStreamID, " already exists");
it1->second->ResetRoutingPath (); // Ack was not delivered, changing path
DeletePacket (packet); // drop it, because previous should be connected DeletePacket (packet); // drop it, because previous should be connected
return; return;
} }

@ -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 * This file is part of Purple i2pd project and licensed under BSD3
* *
@ -52,13 +52,10 @@ namespace stream
const size_t STREAMING_MTU_RATCHETS = 1812; const size_t STREAMING_MTU_RATCHETS = 1812;
const size_t MAX_PACKET_SIZE = 4096; const size_t MAX_PACKET_SIZE = 4096;
const size_t COMPRESSION_THRESHOLD_SIZE = 66; const size_t COMPRESSION_THRESHOLD_SIZE = 66;
const int MAX_NUM_RESEND_ATTEMPTS = 9; const int MAX_NUM_RESEND_ATTEMPTS = 6;
const int WINDOW_SIZE = 6; // in messages const int WINDOW_SIZE = 6; // in messages
const int MIN_WINDOW_SIZE = 1; const int MIN_WINDOW_SIZE = 1;
const int MAX_WINDOW_SIZE = 128; const int MAX_WINDOW_SIZE = 128;
const int WINDOW_SIZE_DROP_FRACTION = 10; // 1/10
const double RTT_EWMA_ALPHA = 0.125;
const int MIN_RTO = 20; // in milliseconds
const int INITIAL_RTT = 8000; // in milliseconds const int INITIAL_RTT = 8000; // in milliseconds
const int INITIAL_RTO = 9000; // in milliseconds const int INITIAL_RTO = 9000; // in milliseconds
const int MIN_SEND_ACK_TIMEOUT = 2; // in milliseconds const int MIN_SEND_ACK_TIMEOUT = 2; // in milliseconds
@ -66,16 +63,14 @@ namespace stream
const size_t MAX_PENDING_INCOMING_BACKLOG = 128; const size_t MAX_PENDING_INCOMING_BACKLOG = 128;
const int PENDING_INCOMING_TIMEOUT = 10; // in seconds const int PENDING_INCOMING_TIMEOUT = 10; // in seconds
const int MAX_RECEIVE_TIMEOUT = 20; // in seconds const int MAX_RECEIVE_TIMEOUT = 20; // in seconds
const uint16_t DELAY_CHOKING = 60000; // in milliseconds
struct Packet struct Packet
{ {
size_t len, offset; size_t len, offset;
uint8_t buf[MAX_PACKET_SIZE]; uint8_t buf[MAX_PACKET_SIZE];
uint64_t sendTime; uint64_t sendTime;
bool resent;
Packet (): len (0), offset (0), sendTime (0), resent (false) {}; Packet (): len (0), offset (0), sendTime (0) {};
uint8_t * GetBuffer () { return buf + offset; }; uint8_t * GetBuffer () { return buf + offset; };
size_t GetLength () const { return len - offset; }; size_t GetLength () const { return len - offset; };
@ -140,6 +135,7 @@ namespace stream
SendBufferQueue (): m_Size (0) {}; SendBufferQueue (): m_Size (0) {};
~SendBufferQueue () { CleanUp (); }; ~SendBufferQueue () { CleanUp (); };
void Add (const uint8_t * buf, size_t len, SendHandler handler);
void Add (std::shared_ptr<SendBuffer> buf); void Add (std::shared_ptr<SendBuffer> buf);
size_t Get (uint8_t * buf, size_t len); size_t Get (uint8_t * buf, size_t len);
size_t GetSize () const { return m_Size; }; size_t GetSize () const { return m_Size; };
@ -180,7 +176,6 @@ namespace stream
bool IsEstablished () const { return m_SendStreamID; }; bool IsEstablished () const { return m_SendStreamID; };
StreamStatus GetStatus () const { return m_Status; }; StreamStatus GetStatus () const { return m_Status; };
StreamingDestination& GetLocalDestination () { return m_LocalDestination; }; StreamingDestination& GetLocalDestination () { return m_LocalDestination; };
void ResetRoutingPath ();
void HandleNextPacket (Packet * packet); void HandleNextPacket (Packet * packet);
void HandlePing (Packet * packet); void HandlePing (Packet * packet);
@ -233,14 +228,12 @@ namespace stream
void ScheduleResend (); void ScheduleResend ();
void HandleResendTimer (const boost::system::error_code& ecode); void HandleResendTimer (const boost::system::error_code& ecode);
void ScheduleAck (int timeout);
void HandleAckSendTimer (const boost::system::error_code& ecode); void HandleAckSendTimer (const boost::system::error_code& ecode);
private: private:
boost::asio::io_service& m_Service; boost::asio::io_service& m_Service;
uint32_t m_SendStreamID, m_RecvStreamID, m_SequenceNumber; uint32_t m_SendStreamID, m_RecvStreamID, m_SequenceNumber;
uint32_t m_TunnelsChangeSequenceNumber;
int32_t m_LastReceivedSequenceNumber; int32_t m_LastReceivedSequenceNumber;
StreamStatus m_Status; StreamStatus m_Status;
bool m_IsAckSendScheduled; bool m_IsAckSendScheduled;
@ -258,9 +251,9 @@ namespace stream
size_t m_NumSentBytes, m_NumReceivedBytes; size_t m_NumSentBytes, m_NumReceivedBytes;
uint16_t m_Port; uint16_t m_Port;
std::mutex m_SendBufferMutex;
SendBufferQueue m_SendBuffer; SendBufferQueue m_SendBuffer;
double m_RTT; int m_WindowSize, m_RTT, m_RTO, m_AckDelay;
int m_WindowSize, m_RTO, m_AckDelay;
uint64_t m_LastWindowSizeIncreaseTime; uint64_t m_LastWindowSizeIncreaseTime;
int m_NumResendAttempts; int m_NumResendAttempts;
size_t m_MTU; size_t m_MTU;

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2024, The PurpleI2P Project * Copyright (c) 2013-2022, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@ -232,34 +232,11 @@ namespace util
return GetLocalHoursSinceEpoch () + g_TimeOffset/3600; return GetLocalHoursSinceEpoch () + g_TimeOffset/3600;
} }
uint64_t GetMonotonicMicroseconds()
{
return std::chrono::duration_cast<std::chrono::microseconds>(
std::chrono::steady_clock::now().time_since_epoch()).count();
}
uint64_t GetMonotonicMilliseconds()
{
return std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::steady_clock::now().time_since_epoch()).count();
}
uint64_t GetMonotonicSeconds ()
{
return std::chrono::duration_cast<std::chrono::seconds>(
std::chrono::steady_clock::now().time_since_epoch()).count();
}
void GetCurrentDate (char * date) void GetCurrentDate (char * date)
{ {
GetDateString (GetSecondsSinceEpoch (), date); GetDateString (GetSecondsSinceEpoch (), date);
} }
void GetNextDayDate (char * date)
{
GetDateString (GetSecondsSinceEpoch () + 24*60*60, date);
}
void GetDateString (uint64_t timestamp, char * date) void GetDateString (uint64_t timestamp, char * date)
{ {
using clock = std::chrono::system_clock; using clock = std::chrono::system_clock;

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2024, The PurpleI2P Project * Copyright (c) 2013-2022, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@ -24,12 +24,7 @@ namespace util
uint32_t GetMinutesSinceEpoch (); uint32_t GetMinutesSinceEpoch ();
uint32_t GetHoursSinceEpoch (); uint32_t GetHoursSinceEpoch ();
uint64_t GetMonotonicMicroseconds (); void GetCurrentDate (char * date); // returns date as YYYYMMDD string, 9 bytes
uint64_t GetMonotonicMilliseconds ();
uint64_t GetMonotonicSeconds ();
void GetCurrentDate (char * date); // returns UTC date as YYYYMMDD string, 9 bytes
void GetNextDayDate (char * date); // returns next UTC day as YYYYMMDD string, 9 bytes
void GetDateString (uint64_t timestamp, char * date); // timestamp is seconds since epoch, returns date as YYYYMMDD string, 9 bytes void GetDateString (uint64_t timestamp, char * date); // timestamp is seconds since epoch, returns date as YYYYMMDD string, 9 bytes
void AdjustTimeOffset (int64_t offset); // in seconds from current void AdjustTimeOffset (int64_t offset); // in seconds from current

@ -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 * This file is part of Purple i2pd project and licensed under BSD3
* *
@ -71,17 +71,15 @@ namespace transport
const int64_t TRANSPORT_SESSION_SLOWNESS_THRESHOLD = 500; // in milliseconds const int64_t TRANSPORT_SESSION_SLOWNESS_THRESHOLD = 500; // in milliseconds
const int64_t TRANSPORT_SESSION_MAX_HANDSHAKE_INTERVAL = 10000; // in milliseconds const int64_t TRANSPORT_SESSION_MAX_HANDSHAKE_INTERVAL = 10000; // in milliseconds
const uint64_t TRANSPORT_SESSION_BANDWIDTH_UPDATE_MIN_INTERVAL = 5; // in seconds
class TransportSession class TransportSession
{ {
public: public:
TransportSession (std::shared_ptr<const i2p::data::RouterInfo> router, int terminationTimeout): TransportSession (std::shared_ptr<const i2p::data::RouterInfo> router, int terminationTimeout):
m_IsOutgoing (router), m_TerminationTimeout (terminationTimeout), m_HandshakeInterval (0), m_NumSentBytes (0), m_NumReceivedBytes (0), m_SendQueueSize (0),
m_SendQueueSize (0), m_NumSentBytes (0), m_NumReceivedBytes (0), m_IsOutgoing (router), m_TerminationTimeout (terminationTimeout),
m_LastBandWidthUpdateNumSentBytes (0), m_LastBandWidthUpdateNumReceivedBytes (0), m_LastActivityTimestamp (i2p::util::GetSecondsSinceEpoch ()),
m_LastActivityTimestamp (i2p::util::GetSecondsSinceEpoch ()), m_HandshakeInterval (0)
m_LastBandwidthUpdateTimestamp (m_LastActivityTimestamp), m_InBandwidth (0), m_OutBandwidth (0)
{ {
if (router) if (router)
m_RemoteIdentity = router->GetRouterIdentity (); m_RemoteIdentity = router->GetRouterIdentity ();
@ -105,29 +103,11 @@ namespace transport
} }
size_t GetNumSentBytes () const { return m_NumSentBytes; }; size_t GetNumSentBytes () const { return m_NumSentBytes; };
void UpdateNumSentBytes (size_t len)
{
m_LastActivityTimestamp = i2p::util::GetSecondsSinceEpoch ();
m_NumSentBytes += len;
UpdateBandwidth ();
}
size_t GetNumReceivedBytes () const { return m_NumReceivedBytes; }; size_t GetNumReceivedBytes () const { return m_NumReceivedBytes; };
void UpdateNumReceivedBytes (size_t len)
{
m_LastActivityTimestamp = i2p::util::GetSecondsSinceEpoch ();
m_NumReceivedBytes += len;
UpdateBandwidth ();
}
size_t GetSendQueueSize () const { return m_SendQueueSize; }; size_t GetSendQueueSize () const { return m_SendQueueSize; };
void SetSendQueueSize (size_t s) { m_SendQueueSize = s; };
bool IsOutgoing () const { return m_IsOutgoing; }; bool IsOutgoing () const { return m_IsOutgoing; };
bool IsSlow () const { return m_HandshakeInterval > TRANSPORT_SESSION_SLOWNESS_THRESHOLD && bool IsSlow () const { return m_HandshakeInterval > TRANSPORT_SESSION_SLOWNESS_THRESHOLD &&
m_HandshakeInterval < TRANSPORT_SESSION_MAX_HANDSHAKE_INTERVAL; }; m_HandshakeInterval < TRANSPORT_SESSION_MAX_HANDSHAKE_INTERVAL; };
bool IsBandwidthExceeded (bool isHighBandwidth) const
{
auto limit = isHighBandwidth ? i2p::data::HIGH_BANDWIDTH_LIMIT*1024 : i2p::data::LOW_BANDWIDTH_LIMIT*1024; // convert to bytes
return std::max (m_InBandwidth, m_OutBandwidth) > limit;
}
int GetTerminationTimeout () const { return m_TerminationTimeout; }; int GetTerminationTimeout () const { return m_TerminationTimeout; };
void SetTerminationTimeout (int terminationTimeout) { m_TerminationTimeout = terminationTimeout; }; void SetTerminationTimeout (int terminationTimeout) { m_TerminationTimeout = terminationTimeout; };
@ -140,53 +120,31 @@ namespace transport
uint32_t GetCreationTime () const { return m_CreationTime; }; uint32_t GetCreationTime () const { return m_CreationTime; };
void SetCreationTime (uint32_t ts) { m_CreationTime = ts; }; // for introducers void SetCreationTime (uint32_t ts) { m_CreationTime = ts; }; // for introducers
uint64_t GetLastActivityTimestamp () const { return m_LastActivityTimestamp; };
void SetLastActivityTimestamp (uint64_t ts) { m_LastActivityTimestamp = ts; };
virtual uint32_t GetRelayTag () const { return 0; }; virtual uint32_t GetRelayTag () const { return 0; };
virtual void SendLocalRouterInfo (bool update = false) { SendI2NPMessages ({ CreateDatabaseStoreMsg () }); }; virtual void SendLocalRouterInfo (bool update = false) { SendI2NPMessages ({ CreateDatabaseStoreMsg () }); };
virtual void SendI2NPMessages (const std::vector<std::shared_ptr<I2NPMessage> >& msgs) = 0; virtual void SendI2NPMessages (const std::vector<std::shared_ptr<I2NPMessage> >& msgs) = 0;
virtual bool IsEstablished () const = 0; virtual bool IsEstablished () const = 0;
private:
void UpdateBandwidth ()
{
int64_t interval = m_LastActivityTimestamp - m_LastBandwidthUpdateTimestamp;
if (interval < 0 || interval > 60*10) // 10 minutes
{
// clock was adjusted, copy new values
m_LastBandWidthUpdateNumSentBytes = m_NumSentBytes;
m_LastBandWidthUpdateNumReceivedBytes = m_NumReceivedBytes;
m_LastBandwidthUpdateTimestamp = m_LastActivityTimestamp;
return;
}
if ((uint64_t)interval > TRANSPORT_SESSION_BANDWIDTH_UPDATE_MIN_INTERVAL)
{
m_OutBandwidth = (m_NumSentBytes - m_LastBandWidthUpdateNumSentBytes)/interval;
m_LastBandWidthUpdateNumSentBytes = m_NumSentBytes;
m_InBandwidth = (m_NumReceivedBytes - m_LastBandWidthUpdateNumReceivedBytes)/interval;
m_LastBandWidthUpdateNumReceivedBytes = m_NumReceivedBytes;
m_LastBandwidthUpdateTimestamp = m_LastActivityTimestamp;
}
}
protected: protected:
std::shared_ptr<const i2p::data::IdentityEx> m_RemoteIdentity; std::shared_ptr<const i2p::data::IdentityEx> m_RemoteIdentity;
mutable std::mutex m_RemoteIdentityMutex; mutable std::mutex m_RemoteIdentityMutex;
size_t m_NumSentBytes, m_NumReceivedBytes, m_SendQueueSize;
bool m_IsOutgoing; bool m_IsOutgoing;
int m_TerminationTimeout; int m_TerminationTimeout;
uint64_t m_LastActivityTimestamp;
uint32_t m_CreationTime; // seconds since epoch uint32_t m_CreationTime; // seconds since epoch
int64_t m_HandshakeInterval; // in milliseconds between SessionRequest->SessionCreated or SessionCreated->SessionConfirmed int64_t m_HandshakeInterval; // in milliseconds between SessionRequest->SessionCreated or SessionCreated->SessionConfirmed
private:
size_t m_SendQueueSize, m_NumSentBytes, m_NumReceivedBytes,
m_LastBandWidthUpdateNumSentBytes, m_LastBandWidthUpdateNumReceivedBytes;
uint64_t m_LastActivityTimestamp, m_LastBandwidthUpdateTimestamp;
uint32_t m_InBandwidth, m_OutBandwidth;
}; };
// SOCKS5 proxy
const uint8_t SOCKS5_VER = 0x05;
const uint8_t SOCKS5_CMD_CONNECT = 0x01;
const uint8_t SOCKS5_CMD_UDP_ASSOCIATE = 0x03;
const uint8_t SOCKS5_ATYP_IPV4 = 0x01;
const uint8_t SOCKS5_ATYP_IPV6 = 0x04;
const size_t SOCKS5_UDP_IPV4_REQUEST_HEADER_SIZE = 10;
const size_t SOCKS5_UDP_IPV6_REQUEST_HEADER_SIZE = 22;
} }
} }

@ -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 * This file is part of Purple i2pd project and licensed under BSD3
* *
@ -140,8 +140,10 @@ namespace transport
m_X25519KeysPairSupplier (15), // 15 pre-generated keys m_X25519KeysPairSupplier (15), // 15 pre-generated keys
m_TotalSentBytes (0), m_TotalReceivedBytes (0), m_TotalTransitTransmittedBytes (0), m_TotalSentBytes (0), m_TotalReceivedBytes (0), m_TotalTransitTransmittedBytes (0),
m_InBandwidth (0), m_OutBandwidth (0), m_TransitBandwidth (0), m_InBandwidth (0), m_OutBandwidth (0), m_TransitBandwidth (0),
m_LastInBandwidthUpdateBytes (0), m_LastOutBandwidthUpdateBytes (0), m_LastTransitBandwidthUpdateBytes (0),
m_InBandwidth15s (0), m_OutBandwidth15s (0), m_TransitBandwidth15s (0), m_InBandwidth15s (0), m_OutBandwidth15s (0), m_TransitBandwidth15s (0),
m_InBandwidth5m (0), m_OutBandwidth5m (0), m_TransitBandwidth5m (0) m_LastInBandwidth15sUpdateBytes (0), m_LastOutBandwidth15sUpdateBytes (0), m_LastTransitBandwidth15sUpdateBytes (0),
m_LastBandwidth15sUpdateTime (0)
{ {
} }
@ -304,16 +306,6 @@ namespace transport
m_PeerCleanupTimer->expires_from_now (boost::posix_time::seconds(5 * SESSION_CREATION_TIMEOUT)); m_PeerCleanupTimer->expires_from_now (boost::posix_time::seconds(5 * SESSION_CREATION_TIMEOUT));
m_PeerCleanupTimer->async_wait (std::bind (&Transports::HandlePeerCleanupTimer, this, std::placeholders::_1)); m_PeerCleanupTimer->async_wait (std::bind (&Transports::HandlePeerCleanupTimer, this, std::placeholders::_1));
uint64_t ts = i2p::util::GetMillisecondsSinceEpoch();
for (int i = 0; i < TRAFFIC_SAMPLE_COUNT; i++)
{
m_TrafficSamples[i].Timestamp = ts - (TRAFFIC_SAMPLE_COUNT - i - 1) * 1000;
m_TrafficSamples[i].TotalReceivedBytes = 0;
m_TrafficSamples[i].TotalSentBytes = 0;
m_TrafficSamples[i].TotalTransitTransmittedBytes = 0;
}
m_TrafficSamplePtr = TRAFFIC_SAMPLE_COUNT - 1;
m_UpdateBandwidthTimer->expires_from_now (boost::posix_time::seconds(1)); m_UpdateBandwidthTimer->expires_from_now (boost::posix_time::seconds(1));
m_UpdateBandwidthTimer->async_wait (std::bind (&Transports::HandleUpdateBandwidthTimer, this, std::placeholders::_1)); m_UpdateBandwidthTimer->async_wait (std::bind (&Transports::HandleUpdateBandwidthTimer, this, std::placeholders::_1));
@ -328,6 +320,7 @@ namespace transport
{ {
if (m_PeerCleanupTimer) m_PeerCleanupTimer->cancel (); if (m_PeerCleanupTimer) m_PeerCleanupTimer->cancel ();
if (m_PeerTestTimer) m_PeerTestTimer->cancel (); if (m_PeerTestTimer) m_PeerTestTimer->cancel ();
m_Peers.clear ();
if (m_SSU2Server) if (m_SSU2Server)
{ {
@ -352,7 +345,6 @@ namespace transport
delete m_Thread; delete m_Thread;
m_Thread = nullptr; m_Thread = nullptr;
} }
m_Peers.clear ();
} }
void Transports::Run () void Transports::Run ()
@ -372,67 +364,51 @@ namespace transport
} }
} }
void Transports::UpdateBandwidthValues(int interval, uint32_t& in, uint32_t& out, uint32_t& transit)
{
TrafficSample& sample1 = m_TrafficSamples[m_TrafficSamplePtr];
TrafficSample& sample2 = m_TrafficSamples[(TRAFFIC_SAMPLE_COUNT + m_TrafficSamplePtr - interval) % TRAFFIC_SAMPLE_COUNT];
auto delta = (int64_t)sample1.Timestamp - (int64_t)sample2.Timestamp;
if (delta <= 0)
{
LogPrint (eLogError, "Transports: Backward clock jump detected, got ", delta, " instead of ", interval * 1000);
return;
}
in = (sample1.TotalReceivedBytes - sample2.TotalReceivedBytes) * 1000 / delta;
out = (sample1.TotalSentBytes - sample2.TotalSentBytes) * 1000 / delta;
transit = (sample1.TotalTransitTransmittedBytes - sample2.TotalTransitTransmittedBytes) * 1000 / delta;
}
void Transports::HandleUpdateBandwidthTimer (const boost::system::error_code& ecode) void Transports::HandleUpdateBandwidthTimer (const boost::system::error_code& ecode)
{ {
if (ecode != boost::asio::error::operation_aborted) if (ecode != boost::asio::error::operation_aborted)
{ {
m_TrafficSamplePtr++; uint64_t ts = i2p::util::GetMillisecondsSinceEpoch ();
if (m_TrafficSamplePtr == TRAFFIC_SAMPLE_COUNT)
m_TrafficSamplePtr = 0;
TrafficSample& sample = m_TrafficSamples[m_TrafficSamplePtr]; // updated every second
sample.Timestamp = i2p::util::GetMillisecondsSinceEpoch(); m_InBandwidth = m_TotalReceivedBytes - m_LastInBandwidthUpdateBytes;
sample.TotalReceivedBytes = m_TotalReceivedBytes; m_OutBandwidth = m_TotalSentBytes - m_LastOutBandwidthUpdateBytes;
sample.TotalSentBytes = m_TotalSentBytes; m_TransitBandwidth = m_TotalTransitTransmittedBytes - m_LastTransitBandwidthUpdateBytes;
sample.TotalTransitTransmittedBytes = m_TotalTransitTransmittedBytes;
UpdateBandwidthValues (1, m_InBandwidth, m_OutBandwidth, m_TransitBandwidth); m_LastInBandwidthUpdateBytes = m_TotalReceivedBytes;
UpdateBandwidthValues (15, m_InBandwidth15s, m_OutBandwidth15s, m_TransitBandwidth15s); m_LastOutBandwidthUpdateBytes = m_TotalSentBytes;
UpdateBandwidthValues (300, m_InBandwidth5m, m_OutBandwidth5m, m_TransitBandwidth5m); m_LastTransitBandwidthUpdateBytes = m_TotalTransitTransmittedBytes;
// updated every 15 seconds
auto delta = ts - m_LastBandwidth15sUpdateTime;
if (delta > 15 * 1000)
{
m_InBandwidth15s = (m_TotalReceivedBytes - m_LastInBandwidth15sUpdateBytes) * 1000 / delta;
m_OutBandwidth15s = (m_TotalSentBytes - m_LastOutBandwidth15sUpdateBytes) * 1000 / delta;
m_TransitBandwidth15s = (m_TotalTransitTransmittedBytes - m_LastTransitBandwidth15sUpdateBytes) * 1000 / delta;
m_LastBandwidth15sUpdateTime = ts;
m_LastInBandwidth15sUpdateBytes = m_TotalReceivedBytes;
m_LastOutBandwidth15sUpdateBytes = m_TotalSentBytes;
m_LastTransitBandwidth15sUpdateBytes = m_TotalTransitTransmittedBytes;
}
m_UpdateBandwidthTimer->expires_from_now (boost::posix_time::seconds(1)); m_UpdateBandwidthTimer->expires_from_now (boost::posix_time::seconds(1));
m_UpdateBandwidthTimer->async_wait (std::bind (&Transports::HandleUpdateBandwidthTimer, this, std::placeholders::_1)); m_UpdateBandwidthTimer->async_wait (std::bind (&Transports::HandleUpdateBandwidthTimer, this, std::placeholders::_1));
} }
} }
int Transports::GetCongestionLevel (bool longTerm) const bool Transports::IsBandwidthExceeded () const
{ {
auto bwLimit = i2p::context.GetBandwidthLimit () * 1024; // convert to bytes auto limit = i2p::context.GetBandwidthLimit() * 1024; // convert to bytes
auto tbwLimit = i2p::context.GetTransitBandwidthLimit () * 1024; // convert to bytes auto bw = std::max (m_InBandwidth15s, m_OutBandwidth15s);
return bw > limit;
if (tbwLimit == 0 || bwLimit == 0) }
return CONGESTION_LEVEL_FULL;
uint32_t bw; bool Transports::IsTransitBandwidthExceeded () const
uint32_t tbw; {
if (longTerm) auto limit = i2p::context.GetTransitBandwidthLimit() * 1024; // convert to bytes
{ return m_TransitBandwidth > limit;
bw = std::max (m_InBandwidth5m, m_OutBandwidth5m);
tbw = m_TransitBandwidth5m;
}
else
{
bw = std::max (m_InBandwidth15s, m_OutBandwidth15s);
tbw = m_TransitBandwidth;
}
auto bwCongestionLevel = CONGESTION_LEVEL_FULL * bw / bwLimit;
auto tbwCongestionLevel = CONGESTION_LEVEL_FULL * tbw / tbwLimit;
return std::max (bwCongestionLevel, tbwCongestionLevel);
} }
void Transports::SendMessage (const i2p::data::IdentHash& ident, std::shared_ptr<i2p::I2NPMessage> msg) void Transports::SendMessage (const i2p::data::IdentHash& ident, std::shared_ptr<i2p::I2NPMessage> msg)
@ -457,13 +433,9 @@ namespace transport
return; return;
} }
if(RoutesRestricted() && !IsRestrictedPeer(ident)) return; if(RoutesRestricted() && !IsRestrictedPeer(ident)) return;
std::shared_ptr<Peer> peer;
auto it = m_Peers.find (ident); auto it = m_Peers.find (ident);
if (it == m_Peers.end ()) if (it == m_Peers.end ())
{ {
// check if not banned
if (i2p::data::IsRouterBanned (ident)) return; // don't create peer to unreachable router
// try to connect
bool connected = false; bool connected = false;
try try
{ {
@ -471,12 +443,10 @@ namespace transport
if (r && (r->IsUnreachable () || !r->IsReachableFrom (i2p::context.GetRouterInfo ()))) return; // router found but non-reachable if (r && (r->IsUnreachable () || !r->IsReachableFrom (i2p::context.GetRouterInfo ()))) return; // router found but non-reachable
{ {
auto ts = i2p::util::GetSecondsSinceEpoch (); auto ts = i2p::util::GetSecondsSinceEpoch ();
peer = std::make_shared<Peer>(r, ts);
std::unique_lock<std::mutex> l(m_PeersMutex); std::unique_lock<std::mutex> l(m_PeersMutex);
peer = m_Peers.emplace (ident, peer).first->second; it = m_Peers.insert (std::pair<i2p::data::IdentHash, Peer>(ident, {r, ts})).first;
} }
if (peer) connected = ConnectToPeer (ident, it->second);
connected = ConnectToPeer (ident, peer);
} }
catch (std::exception& ex) catch (std::exception& ex)
{ {
@ -484,55 +454,49 @@ namespace transport
} }
if (!connected) return; if (!connected) return;
} }
else if (!it->second.sessions.empty ())
peer = it->second; it->second.sessions.front ()->SendI2NPMessages (msgs);
if (!peer) return;
if (peer->IsConnected ())
peer->sessions.front ()->SendI2NPMessages (msgs);
else else
{ {
auto sz = peer->delayedMessages.size (); auto sz = it->second.delayedMessages.size ();
if (sz < MAX_NUM_DELAYED_MESSAGES) if (sz < MAX_NUM_DELAYED_MESSAGES)
{ {
if (sz < CHECK_PROFILE_NUM_DELAYED_MESSAGES && sz + msgs.size () >= CHECK_PROFILE_NUM_DELAYED_MESSAGES) if (sz < CHECK_PROFILE_NUM_DELAYED_MESSAGES && sz + msgs.size () >= CHECK_PROFILE_NUM_DELAYED_MESSAGES)
{ {
if (i2p::data::IsRouterBanned (ident)) auto profile = i2p::data::GetRouterProfile (ident);
if (profile && profile->IsUnreachable ())
{ {
LogPrint (eLogWarning, "Transports: Router ", ident.ToBase64 (), " is banned. Peer dropped"); LogPrint (eLogWarning, "Transports: Peer profile for ", ident.ToBase64 (), " reports unreachable. Dropped");
std::unique_lock<std::mutex> l(m_PeersMutex); std::unique_lock<std::mutex> l(m_PeersMutex);
m_Peers.erase (ident); m_Peers.erase (it);
return; return;
} }
} }
for (auto& it1: msgs) for (auto& it1: msgs)
if (sz > MAX_NUM_DELAYED_MESSAGES/2 && it1->onDrop) it->second.delayedMessages.push_back (it1);
it1->Drop (); // drop earlier because we can handle it
else
peer->delayedMessages.push_back (it1);
} }
else else
{ {
LogPrint (eLogWarning, "Transports: Delayed messages queue size to ", LogPrint (eLogWarning, "Transports: Delayed messages queue size to ",
ident.ToBase64 (), " exceeds ", MAX_NUM_DELAYED_MESSAGES); ident.ToBase64 (), " exceeds ", MAX_NUM_DELAYED_MESSAGES);
std::unique_lock<std::mutex> l(m_PeersMutex); std::unique_lock<std::mutex> l(m_PeersMutex);
m_Peers.erase (ident); m_Peers.erase (it);
} }
} }
} }
bool Transports::ConnectToPeer (const i2p::data::IdentHash& ident, std::shared_ptr<Peer> peer) bool Transports::ConnectToPeer (const i2p::data::IdentHash& ident, Peer& peer)
{ {
if (!peer->router) // reconnect if (!peer.router) // reconnect
peer->SetRouter (netdb.FindRouter (ident)); // try to get new one from netdb peer.SetRouter (netdb.FindRouter (ident)); // try to get new one from netdb
if (peer->router) // we have RI already if (peer.router) // we have RI already
{ {
if (peer->priority.empty ()) if (peer.priority.empty ())
SetPriority (peer); SetPriority (peer);
while (peer->numAttempts < (int)peer->priority.size ()) while (peer.numAttempts < (int)peer.priority.size ())
{ {
auto tr = peer->priority[peer->numAttempts]; auto tr = peer.priority[peer.numAttempts];
peer->numAttempts++; peer.numAttempts++;
switch (tr) switch (tr)
{ {
case i2p::data::RouterInfo::eNTCP2V4: case i2p::data::RouterInfo::eNTCP2V4:
@ -540,12 +504,12 @@ namespace transport
{ {
if (!m_NTCP2Server) continue; if (!m_NTCP2Server) continue;
std::shared_ptr<const RouterInfo::Address> address = (tr == i2p::data::RouterInfo::eNTCP2V6) ? std::shared_ptr<const RouterInfo::Address> address = (tr == i2p::data::RouterInfo::eNTCP2V6) ?
peer->router->GetPublishedNTCP2V6Address () : peer->router->GetPublishedNTCP2V4Address (); peer.router->GetPublishedNTCP2V6Address () : peer.router->GetPublishedNTCP2V4Address ();
if (address && IsInReservedRange(address->host)) if (address && m_CheckReserved && i2p::util::net::IsInReservedRange(address->host))
address = nullptr; address = nullptr;
if (address) if (address)
{ {
auto s = std::make_shared<NTCP2Session> (*m_NTCP2Server, peer->router, address); auto s = std::make_shared<NTCP2Session> (*m_NTCP2Server, peer.router, address);
if( m_NTCP2Server->UsingProxy()) if( m_NTCP2Server->UsingProxy())
m_NTCP2Server->ConnectWithProxy(s); m_NTCP2Server->ConnectWithProxy(s);
else else
@ -559,12 +523,12 @@ namespace transport
{ {
if (!m_SSU2Server) continue; if (!m_SSU2Server) continue;
std::shared_ptr<const RouterInfo::Address> address = (tr == i2p::data::RouterInfo::eSSU2V6) ? std::shared_ptr<const RouterInfo::Address> address = (tr == i2p::data::RouterInfo::eSSU2V6) ?
peer->router->GetSSU2V6Address () : peer->router->GetSSU2V4Address (); peer.router->GetSSU2V6Address () : peer.router->GetSSU2V4Address ();
if (address && IsInReservedRange(address->host)) if (address && m_CheckReserved && i2p::util::net::IsInReservedRange(address->host))
address = nullptr; address = nullptr;
if (address && address->IsReachableSSU ()) if (address && address->IsReachableSSU ())
{ {
if (m_SSU2Server->CreateSession (peer->router, address)) if (m_SSU2Server->CreateSession (peer.router, address))
return true; return true;
} }
break; break;
@ -572,10 +536,10 @@ namespace transport
case i2p::data::RouterInfo::eNTCP2V6Mesh: case i2p::data::RouterInfo::eNTCP2V6Mesh:
{ {
if (!m_NTCP2Server) continue; if (!m_NTCP2Server) continue;
auto address = peer->router->GetYggdrasilAddress (); auto address = peer.router->GetYggdrasilAddress ();
if (address) if (address)
{ {
auto s = std::make_shared<NTCP2Session> (*m_NTCP2Server, peer->router, address); auto s = std::make_shared<NTCP2Session> (*m_NTCP2Server, peer.router, address);
m_NTCP2Server->Connect (s); m_NTCP2Server->Connect (s);
return true; return true;
} }
@ -587,17 +551,9 @@ namespace transport
} }
LogPrint (eLogInfo, "Transports: No compatible addresses available"); LogPrint (eLogInfo, "Transports: No compatible addresses available");
if (peer->router->IsReachableFrom (i2p::context.GetRouterInfo ())) if (peer.router->IsReachableFrom (i2p::context.GetRouterInfo ()))
i2p::data::netdb.SetUnreachable (ident, true); // we are here because all connection attempts failed but router claimed them i2p::data::netdb.SetUnreachable (ident, true); // we are here because all connection attempts failed but router claimed them
peer->Done (); peer.Done ();
std::unique_lock<std::mutex> l(m_PeersMutex);
m_Peers.erase (ident);
return false;
}
else if (i2p::data::IsRouterBanned (ident))
{
LogPrint (eLogWarning, "Transports: Router ", ident.ToBase64 (), " is banned. Peer dropped");
peer->Done ();
std::unique_lock<std::mutex> l(m_PeersMutex); std::unique_lock<std::mutex> l(m_PeersMutex);
m_Peers.erase (ident); m_Peers.erase (ident);
return false; return false;
@ -611,7 +567,7 @@ namespace transport
return true; return true;
} }
void Transports::SetPriority (std::shared_ptr<Peer> peer) const void Transports::SetPriority (Peer& peer) const
{ {
static const std::vector<i2p::data::RouterInfo::SupportedTransports> static const std::vector<i2p::data::RouterInfo::SupportedTransports>
ntcp2Priority = ntcp2Priority =
@ -630,36 +586,16 @@ namespace transport
i2p::data::RouterInfo::eNTCP2V4, i2p::data::RouterInfo::eNTCP2V4,
i2p::data::RouterInfo::eNTCP2V6Mesh i2p::data::RouterInfo::eNTCP2V6Mesh
}; };
if (!peer || !peer->router) return; if (!peer.router) return;
auto compatibleTransports = context.GetRouterInfo ().GetCompatibleTransports (false) & auto compatibleTransports = context.GetRouterInfo ().GetCompatibleTransports (false) &
peer->router->GetCompatibleTransports (true); peer.router->GetCompatibleTransports (true);
auto directTransports = compatibleTransports & peer->router->GetPublishedTransports (); peer.numAttempts = 0;
peer->numAttempts = 0; peer.priority.clear ();
peer->priority.clear (); bool ssu2 = peer.router->GetProfile ()->IsReal () ? (rand () & 1) : false; // try NTCP2 if router is not confirmed real
bool isReal = peer->router->GetProfile ()->IsReal ();
bool ssu2 = isReal ? (rand () & 1) : false; // try NTCP2 if router is not confirmed real
const auto& priority = ssu2 ? ssu2Priority : ntcp2Priority; const auto& priority = ssu2 ? ssu2Priority : ntcp2Priority;
if (directTransports) for (auto transport: priority)
{ if (transport & compatibleTransports)
// direct connections have higher priority peer.priority.push_back (transport);
if (!isReal && (directTransports & (i2p::data::RouterInfo::eNTCP2V4 | i2p::data::RouterInfo::eNTCP2V6)))
{
// Non-confirmed router and a NTCP2 direct connection is presented
compatibleTransports &= ~directTransports; // exclude SSU2 direct connections
directTransports &= ~(i2p::data::RouterInfo::eSSU2V4 | i2p::data::RouterInfo::eSSU2V6);
}
for (auto transport: priority)
if (transport & directTransports)
peer->priority.push_back (transport);
compatibleTransports &= ~directTransports;
}
if (compatibleTransports)
{
// then remaining
for (auto transport: priority)
if (transport & compatibleTransports)
peer->priority.push_back (transport);
}
} }
void Transports::RequestComplete (std::shared_ptr<const i2p::data::RouterInfo> r, const i2p::data::IdentHash& ident) void Transports::RequestComplete (std::shared_ptr<const i2p::data::RouterInfo> r, const i2p::data::IdentHash& ident)
@ -675,9 +611,8 @@ namespace transport
if (r) if (r)
{ {
LogPrint (eLogDebug, "Transports: RouterInfo for ", ident.ToBase64 (), " found, trying to connect"); LogPrint (eLogDebug, "Transports: RouterInfo for ", ident.ToBase64 (), " found, trying to connect");
it->second->SetRouter (r); it->second.SetRouter (r);
if (!it->second->IsConnected ()) ConnectToPeer (ident, it->second);
ConnectToPeer (ident, it->second);
} }
else else
{ {
@ -708,35 +643,16 @@ namespace transport
if (ipv4 && i2p::context.SupportsV4 ()) if (ipv4 && i2p::context.SupportsV4 ())
{ {
LogPrint (eLogInfo, "Transports: Started peer test IPv4"); LogPrint (eLogInfo, "Transports: Started peer test IPv4");
std::unordered_set<i2p::data::IdentHash> excluded; std::set<i2p::data::IdentHash> excluded;
excluded.insert (i2p::context.GetIdentHash ()); // don't pick own router excluded.insert (i2p::context.GetIdentHash ()); // don't pick own router
int testDelay = 0;
for (int i = 0; i < 5; i++) for (int i = 0; i < 5; i++)
{ {
auto router = i2p::data::netdb.GetRandomSSU2PeerTestRouter (true, excluded); // v4 auto router = i2p::data::netdb.GetRandomSSU2PeerTestRouter (true, excluded); // v4
if (router) if (router)
{ {
if (!i2p::context.GetTesting ()) if (!i2p::context.GetTesting ())
{
i2p::context.SetTesting (true); i2p::context.SetTesting (true);
// send first peer test immediately m_SSU2Server->StartPeerTest (router, true);
m_SSU2Server->StartPeerTest (router, true);
}
else
{
testDelay += PEER_TEST_DELAY_INTERVAL + rand() % PEER_TEST_DELAY_INTERVAL_VARIANCE;
if (m_Service)
{
auto delayTimer = std::make_shared<boost::asio::deadline_timer>(*m_Service);
delayTimer->expires_from_now (boost::posix_time::milliseconds (testDelay));
delayTimer->async_wait (
[this, router, delayTimer](const boost::system::error_code& ecode)
{
if (ecode != boost::asio::error::operation_aborted)
m_SSU2Server->StartPeerTest (router, true);
});
}
}
excluded.insert (router->GetIdentHash ()); excluded.insert (router->GetIdentHash ());
} }
} }
@ -746,35 +662,16 @@ namespace transport
if (ipv6 && i2p::context.SupportsV6 ()) if (ipv6 && i2p::context.SupportsV6 ())
{ {
LogPrint (eLogInfo, "Transports: Started peer test IPv6"); LogPrint (eLogInfo, "Transports: Started peer test IPv6");
std::unordered_set<i2p::data::IdentHash> excluded; std::set<i2p::data::IdentHash> excluded;
excluded.insert (i2p::context.GetIdentHash ()); // don't pick own router excluded.insert (i2p::context.GetIdentHash ()); // don't pick own router
int testDelay = 0;
for (int i = 0; i < 5; i++) for (int i = 0; i < 5; i++)
{ {
auto router = i2p::data::netdb.GetRandomSSU2PeerTestRouter (false, excluded); // v6 auto router = i2p::data::netdb.GetRandomSSU2PeerTestRouter (false, excluded); // v6
if (router) if (router)
{ {
if (!i2p::context.GetTestingV6 ()) if (!i2p::context.GetTestingV6 ())
{ i2p::context.SetTestingV6 (true);
i2p::context.SetTestingV6 (true); m_SSU2Server->StartPeerTest (router, false);
// send first peer test immediately
m_SSU2Server->StartPeerTest (router, false);
}
else
{
testDelay += PEER_TEST_DELAY_INTERVAL + rand() % PEER_TEST_DELAY_INTERVAL_VARIANCE;
if (m_Service)
{
auto delayTimer = std::make_shared<boost::asio::deadline_timer>(*m_Service);
delayTimer->expires_from_now (boost::posix_time::milliseconds (testDelay));
delayTimer->async_wait (
[this, router, delayTimer](const boost::system::error_code& ecode)
{
if (ecode != boost::asio::error::operation_aborted)
m_SSU2Server->StartPeerTest (router, false);
});
}
}
excluded.insert (router->GetIdentHash ()); excluded.insert (router->GetIdentHash ());
} }
} }
@ -803,32 +700,31 @@ namespace transport
auto it = m_Peers.find (ident); auto it = m_Peers.find (ident);
if (it != m_Peers.end ()) if (it != m_Peers.end ())
{ {
auto peer = it->second; if (it->second.numAttempts > 1)
if (peer->numAttempts > 1)
{ {
// exclude failed transports // exclude failed transports
i2p::data::RouterInfo::CompatibleTransports transports = 0; i2p::data::RouterInfo::CompatibleTransports transports = 0;
int numExcluded = peer->numAttempts - 1; int numExcluded = it->second.numAttempts - 1;
if (numExcluded > (int)peer->priority.size ()) numExcluded = peer->priority.size (); if (numExcluded > (int)it->second.priority.size ()) numExcluded = it->second.priority.size ();
for (int i = 0; i < numExcluded; i++) for (int i = 0; i < numExcluded; i++)
transports |= peer->priority[i]; transports |= it->second.priority[i];
i2p::data::netdb.ExcludeReachableTransports (ident, transports); i2p::data::netdb.ExcludeReachableTransports (ident, transports);
} }
if (peer->router && peer->numAttempts) if (it->second.router && it->second.numAttempts)
{ {
auto transport = peer->priority[peer->numAttempts-1]; auto transport = it->second.priority[it->second.numAttempts-1];
if (transport == i2p::data::RouterInfo::eNTCP2V4 || if (transport == i2p::data::RouterInfo::eNTCP2V4 ||
transport == i2p::data::RouterInfo::eNTCP2V6 || transport == i2p::data::RouterInfo::eNTCP2V6Mesh) transport == i2p::data::RouterInfo::eNTCP2V6 || transport == i2p::data::RouterInfo::eNTCP2V6Mesh)
peer->router->GetProfile ()->Connected (); // outgoing NTCP2 connection if always real it->second.router->GetProfile ()->Connected (); // outgoing NTCP2 connection if always real
i2p::data::netdb.SetUnreachable (ident, false); // clear unreachable i2p::data::netdb.SetUnreachable (ident, false); // clear unreachable
} }
peer->numAttempts = 0; it->second.numAttempts = 0;
peer->router = nullptr; // we don't need RouterInfo after successive connect it->second.router = nullptr; // we don't need RouterInfo after successive connect
bool sendDatabaseStore = true; bool sendDatabaseStore = true;
if (it->second->delayedMessages.size () > 0) if (it->second.delayedMessages.size () > 0)
{ {
// check if first message is our DatabaseStore (publishing) // check if first message is our DatabaseStore (publishing)
auto firstMsg = peer->delayedMessages[0]; auto firstMsg = it->second.delayedMessages[0];
if (firstMsg && firstMsg->GetTypeID () == eI2NPDatabaseStore && if (firstMsg && firstMsg->GetTypeID () == eI2NPDatabaseStore &&
i2p::data::IdentHash(firstMsg->GetPayload () + DATABASE_STORE_KEY_OFFSET) == i2p::context.GetIdentHash ()) i2p::data::IdentHash(firstMsg->GetPayload () + DATABASE_STORE_KEY_OFFSET) == i2p::context.GetIdentHash ())
sendDatabaseStore = false; // we have it in the list already sendDatabaseStore = false; // we have it in the list already
@ -837,9 +733,9 @@ namespace transport
session->SendLocalRouterInfo (); session->SendLocalRouterInfo ();
else else
session->SetTerminationTimeout (10); // most likely it's publishing, no follow-up messages expected, set timeout to 10 seconds session->SetTerminationTimeout (10); // most likely it's publishing, no follow-up messages expected, set timeout to 10 seconds
peer->sessions.push_back (session); it->second.sessions.push_back (session);
session->SendI2NPMessages (peer->delayedMessages); session->SendI2NPMessages (it->second.delayedMessages);
peer->delayedMessages.clear (); it->second.delayedMessages.clear ();
} }
else // incoming connection or peer test else // incoming connection or peer test
{ {
@ -854,11 +750,10 @@ namespace transport
auto r = i2p::data::netdb.FindRouter (ident); // router should be in netdb after SessionConfirmed auto r = i2p::data::netdb.FindRouter (ident); // router should be in netdb after SessionConfirmed
if (r) r->GetProfile ()->Connected (); if (r) r->GetProfile ()->Connected ();
auto ts = i2p::util::GetSecondsSinceEpoch (); auto ts = i2p::util::GetSecondsSinceEpoch ();
auto peer = std::make_shared<Peer>(r, ts);
peer->sessions.push_back (session);
peer->router = nullptr;
std::unique_lock<std::mutex> l(m_PeersMutex); std::unique_lock<std::mutex> l(m_PeersMutex);
m_Peers.emplace (ident, peer); auto it = m_Peers.insert (std::make_pair (ident, Peer{ r, ts })).first;
it->second.sessions.push_back (session);
it->second.router = nullptr;
} }
}); });
} }
@ -873,16 +768,15 @@ namespace transport
auto it = m_Peers.find (ident); auto it = m_Peers.find (ident);
if (it != m_Peers.end ()) if (it != m_Peers.end ())
{ {
auto peer = it->second; auto before = it->second.sessions.size ();
bool wasConnected = peer->IsConnected (); it->second.sessions.remove (session);
peer->sessions.remove (session); if (it->second.sessions.empty ())
if (!peer->IsConnected ())
{ {
if (peer->delayedMessages.size () > 0) if (it->second.delayedMessages.size () > 0)
{ {
if (wasConnected) // we had an active session before if (before > 0) // we had an active session before
peer->numAttempts = 0; // start over it->second.numAttempts = 0; // start over
ConnectToPeer (ident, peer); ConnectToPeer (ident, it->second);
} }
else else
{ {
@ -908,12 +802,12 @@ namespace transport
auto ts = i2p::util::GetSecondsSinceEpoch (); auto ts = i2p::util::GetSecondsSinceEpoch ();
for (auto it = m_Peers.begin (); it != m_Peers.end (); ) for (auto it = m_Peers.begin (); it != m_Peers.end (); )
{ {
it->second->sessions.remove_if ( it->second.sessions.remove_if (
[](std::shared_ptr<TransportSession> session)->bool [](std::shared_ptr<TransportSession> session)->bool
{ {
return !session || !session->IsEstablished (); return !session || !session->IsEstablished ();
}); });
if (!it->second->IsConnected () && ts > it->second->creationTime + SESSION_CREATION_TIMEOUT) if (it->second.sessions.empty () && ts > it->second.creationTime + SESSION_CREATION_TIMEOUT)
{ {
LogPrint (eLogWarning, "Transports: Session to peer ", it->first.ToBase64 (), " has not been created in ", SESSION_CREATION_TIMEOUT, " seconds"); LogPrint (eLogWarning, "Transports: Session to peer ", it->first.ToBase64 (), " has not been created in ", SESSION_CREATION_TIMEOUT, " seconds");
/* if (!it->second.router) /* if (!it->second.router)
@ -927,24 +821,20 @@ namespace transport
} }
else else
{ {
if (ts > it->second->nextRouterInfoUpdateTime) if (ts > it->second.nextRouterInfoUpdateTime)
{ {
auto session = it->second->sessions.front (); auto session = it->second.sessions.front ();
if (session) if (session)
session->SendLocalRouterInfo (true); session->SendLocalRouterInfo (true);
it->second->nextRouterInfoUpdateTime = ts + PEER_ROUTER_INFO_UPDATE_INTERVAL + it->second.nextRouterInfoUpdateTime = ts + PEER_ROUTER_INFO_UPDATE_INTERVAL +
rand () % PEER_ROUTER_INFO_UPDATE_INTERVAL_VARIANCE; rand () % PEER_ROUTER_INFO_UPDATE_INTERVAL_VARIANCE;
} }
++it; ++it;
} }
} }
bool ipv4Testing = i2p::context.GetTesting (); bool ipv4Testing = i2p::context.GetTesting ();
if (!ipv4Testing)
ipv4Testing = i2p::context.GetRouterInfo ().IsSSU2V4 () && (i2p::context.GetStatus() == eRouterStatusUnknown);
bool ipv6Testing = i2p::context.GetTestingV6 (); bool ipv6Testing = i2p::context.GetTestingV6 ();
if (!ipv6Testing) // if still testing, repeat peer test
ipv6Testing = i2p::context.GetRouterInfo ().IsSSU2V6 () && (i2p::context.GetStatusV6() == eRouterStatusUnknown);
// if still testing or unknown, repeat peer test
if (ipv4Testing || ipv6Testing) if (ipv4Testing || ipv6Testing)
PeerTest (ipv4Testing, ipv6Testing); PeerTest (ipv4Testing, ipv6Testing);
m_PeerCleanupTimer->expires_from_now (boost::posix_time::seconds(3 * SESSION_CREATION_TIMEOUT)); m_PeerCleanupTimer->expires_from_now (boost::posix_time::seconds(3 * SESSION_CREATION_TIMEOUT));
@ -1052,13 +942,13 @@ namespace transport
std::shared_ptr<const i2p::data::RouterInfo> Transports::GetRandomPeer (bool isHighBandwidth) const std::shared_ptr<const i2p::data::RouterInfo> Transports::GetRandomPeer (bool isHighBandwidth) const
{ {
return GetRandomPeer ( return GetRandomPeer (
[isHighBandwidth](std::shared_ptr<const Peer> peer)->bool [isHighBandwidth](const Peer& peer)->bool
{ {
// connected, not overloaded and not slow // connected, not overloaded and not slow
return !peer->router && peer->IsConnected () && peer->isReachable && return !peer.router && !peer.sessions.empty () && peer.isReachable &&
peer->sessions.front ()->GetSendQueueSize () <= PEER_ROUTER_INFO_OVERLOAD_QUEUE_SIZE && peer.sessions.front ()->GetSendQueueSize () <= PEER_ROUTER_INFO_OVERLOAD_QUEUE_SIZE &&
!peer->sessions.front ()->IsSlow () && !peer->sessions.front ()->IsBandwidthExceeded (peer->isHighBandwidth) && !peer.sessions.front ()->IsSlow () &&
(!isHighBandwidth || peer->isHighBandwidth); (!isHighBandwidth || peer.isHighBandwidth);
}); });
} }
@ -1075,25 +965,18 @@ namespace transport
} }
} }
void Transports::RestrictRoutesToRouters(const std::set<i2p::data::IdentHash>& routers) void Transports::RestrictRoutesToRouters(std::set<i2p::data::IdentHash> routers)
{ {
std::lock_guard<std::mutex> lock(m_TrustedRoutersMutex); std::unique_lock<std::mutex> lock(m_TrustedRoutersMutex);
m_TrustedRouters.clear(); m_TrustedRouters.clear();
for (const auto & ri : routers ) for (const auto & ri : routers )
m_TrustedRouters.push_back(ri); m_TrustedRouters.push_back(ri);
} }
bool Transports::RoutesRestricted() const bool Transports::RoutesRestricted() const {
{ std::unique_lock<std::mutex> famlock(m_FamilyMutex);
{ std::unique_lock<std::mutex> routerslock(m_TrustedRoutersMutex);
std::lock_guard<std::mutex> routerslock(m_TrustedRoutersMutex); return m_TrustedFamilies.size() > 0 || m_TrustedRouters.size() > 0;
if (!m_TrustedRouters.empty ()) return true;
}
{
std::lock_guard<std::mutex> famlock(m_FamilyMutex);
if (!m_TrustedFamilies.empty ()) return true;
}
return false;
} }
/** XXX: if routes are not restricted this dies */ /** XXX: if routes are not restricted this dies */
@ -1117,7 +1000,7 @@ namespace transport
return i2p::data::netdb.GetRandomRouterInFamily(fam); return i2p::data::netdb.GetRandomRouterInFamily(fam);
} }
{ {
std::lock_guard<std::mutex> l(m_TrustedRoutersMutex); std::unique_lock<std::mutex> l(m_TrustedRoutersMutex);
auto sz = m_TrustedRouters.size(); auto sz = m_TrustedRouters.size();
if (sz) if (sz)
{ {
@ -1134,12 +1017,12 @@ namespace transport
bool Transports::IsRestrictedPeer(const i2p::data::IdentHash & ih) const bool Transports::IsRestrictedPeer(const i2p::data::IdentHash & ih) const
{ {
{ {
std::lock_guard<std::mutex> l(m_TrustedRoutersMutex); std::unique_lock<std::mutex> l(m_TrustedRoutersMutex);
for (const auto & r : m_TrustedRouters ) for (const auto & r : m_TrustedRouters )
if ( r == ih ) return true; if ( r == ih ) return true;
} }
{ {
std::lock_guard<std::mutex> l(m_FamilyMutex); std::unique_lock<std::mutex> l(m_FamilyMutex);
auto ri = i2p::data::netdb.FindRouter(ih); auto ri = i2p::data::netdb.FindRouter(ih);
for (const auto & fam : m_TrustedFamilies) for (const auto & fam : m_TrustedFamilies)
if(ri->IsFamily(fam)) return true; if(ri->IsFamily(fam)) return true;
@ -1159,11 +1042,6 @@ namespace transport
} }
} }
bool Transports::IsInReservedRange (const boost::asio::ip::address& host) const
{
return IsCheckReserved () && i2p::util::net::IsInReservedRange (host);
}
void InitAddressFromIface () void InitAddressFromIface ()
{ {
bool ipv6; i2p::config::GetOption("ipv6", ipv6); bool ipv6; i2p::config::GetOption("ipv6", ipv6);
@ -1275,6 +1153,7 @@ namespace transport
else else
i2p::context.PublishSSU2Address (ssu2port, false, ipv4, ipv6); // unpublish i2p::context.PublishSSU2Address (ssu2port, false, ipv4, ipv6); // unpublish
} }
} }
} }
} }

@ -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 * This file is part of Purple i2pd project and licensed under BSD3
* *
@ -85,14 +85,11 @@ namespace transport
isReachable = (bool)router->GetCompatibleTransports (true); isReachable = (bool)router->GetCompatibleTransports (true);
} }
} }
void Done () void Done ()
{ {
for (auto& it: sessions) for (auto& it: sessions)
it->Done (); it->Done ();
// drop not sent delayed messages
for (auto& it: delayedMessages)
it->Drop ();
} }
void SetRouter (std::shared_ptr<const i2p::data::RouterInfo> r) void SetRouter (std::shared_ptr<const i2p::data::RouterInfo> r)
@ -104,27 +101,12 @@ namespace transport
isReachable = (bool)router->GetCompatibleTransports (true); isReachable = (bool)router->GetCompatibleTransports (true);
} }
} }
bool IsConnected () const { return !sessions.empty (); }
}; };
const uint64_t SESSION_CREATION_TIMEOUT = 15; // in seconds const uint64_t SESSION_CREATION_TIMEOUT = 15; // in seconds
const int PEER_TEST_INTERVAL = 71; // in minutes const int PEER_TEST_INTERVAL = 71; // in minutes
const int PEER_TEST_DELAY_INTERVAL = 20; // in milliseconds
const int PEER_TEST_DELAY_INTERVAL_VARIANCE = 30; // in milliseconds
const int MAX_NUM_DELAYED_MESSAGES = 150; const int MAX_NUM_DELAYED_MESSAGES = 150;
const int CHECK_PROFILE_NUM_DELAYED_MESSAGES = 15; // check profile after const int CHECK_PROFILE_NUM_DELAYED_MESSAGES = 15; // check profile after
const int TRAFFIC_SAMPLE_COUNT = 301; // seconds
struct TrafficSample
{
uint64_t Timestamp;
uint64_t TotalReceivedBytes;
uint64_t TotalSentBytes;
uint64_t TotalTransitTransmittedBytes;
};
class Transports class Transports
{ {
public: public:
@ -134,7 +116,6 @@ namespace transport
void Start (bool enableNTCP2=true, bool enableSSU2=true); void Start (bool enableNTCP2=true, bool enableSSU2=true);
void Stop (); void Stop ();
bool IsRunning () const { return m_IsRunning; }
bool IsBoundSSU2() const { return m_SSU2Server != nullptr; } bool IsBoundSSU2() const { return m_SSU2Server != nullptr; }
bool IsBoundNTCP2() const { return m_NTCP2Server != nullptr; } bool IsBoundNTCP2() const { return m_NTCP2Server != nullptr; }
@ -165,7 +146,8 @@ namespace transport
uint32_t GetInBandwidth15s () const { return m_InBandwidth15s; }; uint32_t GetInBandwidth15s () const { return m_InBandwidth15s; };
uint32_t GetOutBandwidth15s () const { return m_OutBandwidth15s; }; uint32_t GetOutBandwidth15s () const { return m_OutBandwidth15s; };
uint32_t GetTransitBandwidth15s () const { return m_TransitBandwidth15s; }; uint32_t GetTransitBandwidth15s () const { return m_TransitBandwidth15s; };
int GetCongestionLevel (bool longTerm) const; bool IsBandwidthExceeded () const;
bool IsTransitBandwidthExceeded () const;
size_t GetNumPeers () const { return m_Peers.size (); }; size_t GetNumPeers () const { return m_Peers.size (); };
std::shared_ptr<const i2p::data::RouterInfo> GetRandomPeer (bool isHighBandwidth) const; std::shared_ptr<const i2p::data::RouterInfo> GetRandomPeer (bool isHighBandwidth) const;
@ -176,15 +158,14 @@ namespace transport
/** restrict routes to use only these router families for first hops */ /** restrict routes to use only these router families for first hops */
void RestrictRoutesToFamilies(const std::set<std::string>& families); void RestrictRoutesToFamilies(const std::set<std::string>& families);
/** restrict routes to use only these routers for first hops */ /** restrict routes to use only these routers for first hops */
void RestrictRoutesToRouters(const std::set<i2p::data::IdentHash>& routers); void RestrictRoutesToRouters(std::set<i2p::data::IdentHash> routers);
bool IsRestrictedPeer(const i2p::data::IdentHash & ident) const; bool IsRestrictedPeer(const i2p::data::IdentHash & ident) const;
void PeerTest (bool ipv4 = true, bool ipv6 = true); void PeerTest (bool ipv4 = true, bool ipv6 = true);
void SetCheckReserved (bool check) { m_CheckReserved = check; }; void SetCheckReserved (bool check) { m_CheckReserved = check; };
bool IsCheckReserved () const { return m_CheckReserved; }; bool IsCheckReserved () { return m_CheckReserved; };
bool IsInReservedRange (const boost::asio::ip::address& host) const;
private: private:
@ -192,12 +173,11 @@ namespace transport
void RequestComplete (std::shared_ptr<const i2p::data::RouterInfo> r, const i2p::data::IdentHash& ident); void RequestComplete (std::shared_ptr<const i2p::data::RouterInfo> r, const i2p::data::IdentHash& ident);
void HandleRequestComplete (std::shared_ptr<const i2p::data::RouterInfo> r, i2p::data::IdentHash ident); void HandleRequestComplete (std::shared_ptr<const i2p::data::RouterInfo> r, i2p::data::IdentHash ident);
void PostMessages (i2p::data::IdentHash ident, std::vector<std::shared_ptr<i2p::I2NPMessage> > msgs); void PostMessages (i2p::data::IdentHash ident, std::vector<std::shared_ptr<i2p::I2NPMessage> > msgs);
bool ConnectToPeer (const i2p::data::IdentHash& ident, std::shared_ptr<Peer> peer); bool ConnectToPeer (const i2p::data::IdentHash& ident, Peer& peer);
void SetPriority (std::shared_ptr<Peer> peer) const; void SetPriority (Peer& peer) const;
void HandlePeerCleanupTimer (const boost::system::error_code& ecode); void HandlePeerCleanupTimer (const boost::system::error_code& ecode);
void HandlePeerTestTimer (const boost::system::error_code& ecode); void HandlePeerTestTimer (const boost::system::error_code& ecode);
void HandleUpdateBandwidthTimer (const boost::system::error_code& ecode); void HandleUpdateBandwidthTimer (const boost::system::error_code& ecode);
void UpdateBandwidthValues (int interval, uint32_t& in, uint32_t& out, uint32_t& transit);
void DetectExternalIP (); void DetectExternalIP ();
@ -216,21 +196,20 @@ namespace transport
SSU2Server * m_SSU2Server; SSU2Server * m_SSU2Server;
NTCP2Server * m_NTCP2Server; NTCP2Server * m_NTCP2Server;
mutable std::mutex m_PeersMutex; mutable std::mutex m_PeersMutex;
std::unordered_map<i2p::data::IdentHash, std::shared_ptr<Peer> > m_Peers; std::unordered_map<i2p::data::IdentHash, Peer> m_Peers;
X25519KeysPairSupplier m_X25519KeysPairSupplier; X25519KeysPairSupplier m_X25519KeysPairSupplier;
std::atomic<uint64_t> m_TotalSentBytes, m_TotalReceivedBytes, m_TotalTransitTransmittedBytes; std::atomic<uint64_t> m_TotalSentBytes, m_TotalReceivedBytes, m_TotalTransitTransmittedBytes;
TrafficSample m_TrafficSamples[TRAFFIC_SAMPLE_COUNT];
int m_TrafficSamplePtr;
// Bandwidth per second // Bandwidth per second
uint32_t m_InBandwidth, m_OutBandwidth, m_TransitBandwidth; uint32_t m_InBandwidth, m_OutBandwidth, m_TransitBandwidth;
// Bandwidth during last 15 seconds uint64_t m_LastInBandwidthUpdateBytes, m_LastOutBandwidthUpdateBytes, m_LastTransitBandwidthUpdateBytes;
// Bandwidth every 15 seconds
uint32_t m_InBandwidth15s, m_OutBandwidth15s, m_TransitBandwidth15s; uint32_t m_InBandwidth15s, m_OutBandwidth15s, m_TransitBandwidth15s;
// Bandwidth during last 5 minutes uint64_t m_LastInBandwidth15sUpdateBytes, m_LastOutBandwidth15sUpdateBytes, m_LastTransitBandwidth15sUpdateBytes;
uint32_t m_InBandwidth5m, m_OutBandwidth5m, m_TransitBandwidth5m; uint64_t m_LastBandwidth15sUpdateTime;
/** which router families to trust for first hops */ /** which router families to trust for first hops */
std::vector<i2p::data::FamilyID> m_TrustedFamilies; std::vector<i2p::data::FamilyID> m_TrustedFamilies;

@ -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 * This file is part of Purple i2pd project and licensed under BSD3
* *
@ -33,7 +33,7 @@ namespace tunnel
TunnelBase (config->GetTunnelID (), config->GetNextTunnelID (), config->GetNextIdentHash ()), TunnelBase (config->GetTunnelID (), config->GetNextTunnelID (), config->GetNextIdentHash ()),
m_Config (config), m_IsShortBuildMessage (false), m_Pool (nullptr), m_Config (config), m_IsShortBuildMessage (false), m_Pool (nullptr),
m_State (eTunnelStatePending), m_FarEndTransports (i2p::data::RouterInfo::eAllTransports), m_State (eTunnelStatePending), m_FarEndTransports (i2p::data::RouterInfo::eAllTransports),
m_IsRecreated (false), m_Latency (UNKNOWN_LATENCY) m_IsRecreated (false), m_Latency (0)
{ {
} }
@ -52,7 +52,7 @@ namespace tunnel
// shuffle records // shuffle records
std::vector<int> recordIndicies; std::vector<int> recordIndicies;
for (int i = 0; i < numRecords; i++) recordIndicies.push_back(i); for (int i = 0; i < numRecords; i++) recordIndicies.push_back(i);
std::shuffle (recordIndicies.begin(), recordIndicies.end(), m_Pool ? m_Pool->GetRng () : std::mt19937(std::random_device()())); std::shuffle (recordIndicies.begin(), recordIndicies.end(), std::mt19937(std::random_device()()));
// create real records // create real records
uint8_t * records = msg->GetPayload () + 1; uint8_t * records = msg->GetPayload () + 1;
@ -90,13 +90,7 @@ namespace tunnel
hop = hop->prev; hop = hop->prev;
} }
msg->FillI2NPMessageHeader (m_Config->IsShort () ? eI2NPShortTunnelBuild : eI2NPVariableTunnelBuild); msg->FillI2NPMessageHeader (m_Config->IsShort () ? eI2NPShortTunnelBuild : eI2NPVariableTunnelBuild);
auto s = shared_from_this ();
msg->onDrop = [s]()
{
LogPrint (eLogInfo, "I2NP: Tunnel ", s->GetTunnelID (), " request was not sent");
s->SetState (i2p::tunnel::eTunnelStateBuildFailed);
};
// send message // send message
if (outboundTunnel) if (outboundTunnel)
{ {
@ -122,7 +116,7 @@ namespace tunnel
if (m_Pool && m_Pool->GetLocalDestination ()) if (m_Pool && m_Pool->GetLocalDestination ())
m_Pool->GetLocalDestination ()->SubmitECIESx25519Key (key, tag); m_Pool->GetLocalDestination ()->SubmitECIESx25519Key (key, tag);
else else
i2p::context.SubmitECIESx25519Key (key, tag); i2p::context.AddECIESx25519Key (key, tag);
} }
i2p::transport::transports.SendMessage (GetNextIdentHash (), msg); i2p::transport::transports.SendMessage (GetNextIdentHash (), msg);
} }
@ -198,10 +192,10 @@ namespace tunnel
return established; return established;
} }
bool Tunnel::LatencyFitsRange(int lowerbound, int upperbound) const bool Tunnel::LatencyFitsRange(uint64_t lower, uint64_t upper) const
{ {
auto latency = GetMeanLatency(); auto latency = GetMeanLatency();
return latency >= lowerbound && latency <= upperbound; return latency >= lower && latency <= upper;
} }
void Tunnel::EncryptTunnelMsg (std::shared_ptr<const I2NPMessage> in, std::shared_ptr<I2NPMessage> out) void Tunnel::EncryptTunnelMsg (std::shared_ptr<const I2NPMessage> in, std::shared_ptr<I2NPMessage> out)
@ -250,9 +244,9 @@ namespace tunnel
void InboundTunnel::HandleTunnelDataMsg (std::shared_ptr<I2NPMessage>&& msg) void InboundTunnel::HandleTunnelDataMsg (std::shared_ptr<I2NPMessage>&& msg)
{ {
if (GetState () != eTunnelStateExpiring) SetState (eTunnelStateEstablished); // incoming messages means a tunnel is alive if (IsFailed ()) SetState (eTunnelStateEstablished); // incoming messages means a tunnel is alive
EncryptTunnelMsg (msg, msg); EncryptTunnelMsg (msg, msg);
msg->from = GetSharedFromThis (); msg->from = shared_from_this ();
m_Endpoint.HandleDecryptedTunnelDataMsg (msg); m_Endpoint.HandleDecryptedTunnelDataMsg (msg);
} }
@ -267,7 +261,7 @@ namespace tunnel
if (msg) if (msg)
{ {
m_NumReceivedBytes += msg->GetLength (); m_NumReceivedBytes += msg->GetLength ();
msg->from = GetSharedFromThis (); msg->from = shared_from_this ();
HandleI2NPMessage (msg); HandleI2NPMessage (msg);
} }
} }
@ -592,6 +586,17 @@ namespace tunnel
auto typeID = msg->GetTypeID (); auto typeID = msg->GetTypeID ();
LogPrint (eLogDebug, "Tunnel: Gateway of ", (int) len, " bytes for tunnel ", tunnel->GetTunnelID (), ", msg type ", (int)typeID); LogPrint (eLogDebug, "Tunnel: Gateway of ", (int) len, " bytes for tunnel ", tunnel->GetTunnelID (), ", msg type ", (int)typeID);
if (typeID == eI2NPDatabaseSearchReply)
// DatabaseSearchReply with new routers
i2p::data::netdb.PostI2NPMsg (CopyI2NPMessage (msg));
else if (IsRouterInfoMsg (msg))
{
// transit DatabaseStore might contain new/updated RI
auto m = CopyI2NPMessage (msg);
if (bufbe32toh (m->GetPayload () + DATABASE_STORE_REPLY_TOKEN_OFFSET))
memset (m->GetPayload () + DATABASE_STORE_REPLY_TOKEN_OFFSET, 0xFF, 4); // fake replyToken meaning no reply
i2p::data::netdb.PostI2NPMsg (m);
}
tunnel->SendTunnelDataMsg (msg); tunnel->SendTunnelDataMsg (msg);
} }
@ -668,10 +673,9 @@ namespace tunnel
for (auto it = m_OutboundTunnels.begin (); it != m_OutboundTunnels.end ();) for (auto it = m_OutboundTunnels.begin (); it != m_OutboundTunnels.end ();)
{ {
auto tunnel = *it; auto tunnel = *it;
if (tunnel->IsFailed () || ts > tunnel->GetCreationTime () + TUNNEL_EXPIRATION_TIMEOUT || if (ts > tunnel->GetCreationTime () + TUNNEL_EXPIRATION_TIMEOUT)
ts + TUNNEL_EXPIRATION_TIMEOUT < tunnel->GetCreationTime ())
{ {
LogPrint (eLogDebug, "Tunnel: Tunnel with id ", tunnel->GetTunnelID (), " expired or failed"); LogPrint (eLogDebug, "Tunnel: Tunnel with id ", tunnel->GetTunnelID (), " expired");
auto pool = tunnel->GetTunnelPool (); auto pool = tunnel->GetTunnelPool ();
if (pool) if (pool)
pool->TunnelExpired (tunnel); pool->TunnelExpired (tunnel);
@ -720,10 +724,10 @@ namespace tunnel
for (auto it = m_InboundTunnels.begin (); it != m_InboundTunnels.end ();) for (auto it = m_InboundTunnels.begin (); it != m_InboundTunnels.end ();)
{ {
auto tunnel = *it; auto tunnel = *it;
if (tunnel->IsFailed () || ts > tunnel->GetCreationTime () + TUNNEL_EXPIRATION_TIMEOUT || if (ts > tunnel->GetCreationTime () + TUNNEL_EXPIRATION_TIMEOUT ||
ts + TUNNEL_EXPIRATION_TIMEOUT < tunnel->GetCreationTime ()) ts + TUNNEL_EXPIRATION_TIMEOUT < tunnel->GetCreationTime ())
{ {
LogPrint (eLogDebug, "Tunnel: Tunnel with id ", tunnel->GetTunnelID (), " expired or failed"); LogPrint (eLogDebug, "Tunnel: Tunnel with id ", tunnel->GetTunnelID (), " expired");
auto pool = tunnel->GetTunnelPool (); auto pool = tunnel->GetTunnelPool ();
if (pool) if (pool)
pool->TunnelExpired (tunnel); pool->TunnelExpired (tunnel);
@ -975,7 +979,7 @@ namespace tunnel
return m_OutboundTunnels.size(); return m_OutboundTunnels.size();
} }
void Tunnels::SetMaxNumTransitTunnels (uint32_t maxNumTransitTunnels) void Tunnels::SetMaxNumTransitTunnels (uint16_t maxNumTransitTunnels)
{ {
if (maxNumTransitTunnels > 0 && m_MaxNumTransitTunnels != maxNumTransitTunnels) if (maxNumTransitTunnels > 0 && m_MaxNumTransitTunnels != maxNumTransitTunnels)
{ {

@ -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 * This file is part of Purple i2pd project and licensed under BSD3
* *
@ -39,8 +39,7 @@ namespace tunnel
const int TUNNEL_CREATION_TIMEOUT = 30; // 30 seconds const int TUNNEL_CREATION_TIMEOUT = 30; // 30 seconds
const int STANDARD_NUM_RECORDS = 4; // in VariableTunnelBuild message const int STANDARD_NUM_RECORDS = 4; // in VariableTunnelBuild message
const int MAX_NUM_RECORDS = 8; const int MAX_NUM_RECORDS = 8;
const int UNKNOWN_LATENCY = -1; const int HIGH_LATENCY_PER_HOP = 250; // in milliseconds
const int HIGH_LATENCY_PER_HOP = 250000; // in microseconds
const int MAX_TUNNEL_MSGS_BATCH_SIZE = 100; // handle messages without interrupt const int MAX_TUNNEL_MSGS_BATCH_SIZE = 100; // handle messages without interrupt
const uint16_t DEFAULT_MAX_NUM_TRANSIT_TUNNELS = 5000; const uint16_t DEFAULT_MAX_NUM_TRANSIT_TUNNELS = 5000;
const int TUNNEL_MANAGE_INTERVAL = 15; // in seconds const int TUNNEL_MANAGE_INTERVAL = 15; // in seconds
@ -66,8 +65,7 @@ namespace tunnel
class OutboundTunnel; class OutboundTunnel;
class InboundTunnel; class InboundTunnel;
class Tunnel: public TunnelBase, class Tunnel: public TunnelBase
public std::enable_shared_from_this<Tunnel>
{ {
struct TunnelHop struct TunnelHop
{ {
@ -92,7 +90,7 @@ namespace tunnel
i2p::data::RouterInfo::CompatibleTransports GetFarEndTransports () const { return m_FarEndTransports; }; i2p::data::RouterInfo::CompatibleTransports GetFarEndTransports () const { return m_FarEndTransports; };
TunnelState GetState () const { return m_State; }; TunnelState GetState () const { return m_State; };
void SetState (TunnelState state); void SetState (TunnelState state);
bool IsEstablished () const { return m_State == eTunnelStateEstablished || m_State == eTunnelStateTestFailed; }; bool IsEstablished () const { return m_State == eTunnelStateEstablished; };
bool IsFailed () const { return m_State == eTunnelStateFailed; }; bool IsFailed () const { return m_State == eTunnelStateFailed; };
bool IsRecreated () const { return m_IsRecreated; }; bool IsRecreated () const { return m_IsRecreated; };
void SetRecreated (bool recreated) { m_IsRecreated = recreated; }; void SetRecreated (bool recreated) { m_IsRecreated = recreated; };
@ -109,14 +107,14 @@ namespace tunnel
void EncryptTunnelMsg (std::shared_ptr<const I2NPMessage> in, std::shared_ptr<I2NPMessage> out) override; void EncryptTunnelMsg (std::shared_ptr<const I2NPMessage> in, std::shared_ptr<I2NPMessage> out) override;
/** @brief add latency sample */ /** @brief add latency sample */
void AddLatencySample(const int us) { m_Latency = LatencyIsKnown() ? (m_Latency + us) >> 1 : us; } void AddLatencySample(const uint64_t ms) { m_Latency = (m_Latency + ms) >> 1; }
/** @brief get this tunnel's estimated latency */ /** @brief get this tunnel's estimated latency */
int GetMeanLatency() const { return (m_Latency + 500) / 1000; } uint64_t GetMeanLatency() const { return m_Latency; }
/** @brief return true if this tunnel's latency fits in range [lowerbound, upperbound] */ /** @brief return true if this tunnel's latency fits in range [lowerbound, upperbound] */
bool LatencyFitsRange(int lowerbound, int upperbound) const; bool LatencyFitsRange(uint64_t lowerbound, uint64_t upperbound) const;
bool LatencyIsKnown() const { return m_Latency != UNKNOWN_LATENCY; } bool LatencyIsKnown() const { return m_Latency > 0; }
bool IsSlow () const { return LatencyIsKnown() && m_Latency > HIGH_LATENCY_PER_HOP*GetNumHops (); } bool IsSlow () const { return LatencyIsKnown() && (int)m_Latency > HIGH_LATENCY_PER_HOP*GetNumHops (); }
/** visit all hops we currently store */ /** visit all hops we currently store */
void VisitTunnelHops(TunnelHopVisitor v); void VisitTunnelHops(TunnelHopVisitor v);
@ -130,7 +128,7 @@ namespace tunnel
TunnelState m_State; TunnelState m_State;
i2p::data::RouterInfo::CompatibleTransports m_FarEndTransports; i2p::data::RouterInfo::CompatibleTransports m_FarEndTransports;
bool m_IsRecreated; // if tunnel is replaced by new, or new tunnel requested to replace bool m_IsRecreated; // if tunnel is replaced by new, or new tunnel requested to replace
int m_Latency; // in microseconds uint64_t m_Latency; // in milliseconds
}; };
class OutboundTunnel: public Tunnel class OutboundTunnel: public Tunnel
@ -157,7 +155,7 @@ namespace tunnel
i2p::data::IdentHash m_EndpointIdentHash; i2p::data::IdentHash m_EndpointIdentHash;
}; };
class InboundTunnel: public Tunnel class InboundTunnel: public Tunnel, public std::enable_shared_from_this<InboundTunnel>
{ {
public: public:
@ -169,13 +167,6 @@ namespace tunnel
// override TunnelBase // override TunnelBase
void Cleanup () override { m_Endpoint.Cleanup (); }; void Cleanup () override { m_Endpoint.Cleanup (); };
protected:
std::shared_ptr<InboundTunnel> GetSharedFromThis ()
{
return std::static_pointer_cast<InboundTunnel>(shared_from_this ());
}
private: private:
TunnelEndpoint m_Endpoint; TunnelEndpoint m_Endpoint;
@ -239,9 +230,9 @@ namespace tunnel
std::shared_ptr<I2NPMessage> NewI2NPTunnelMessage (bool endpoint); std::shared_ptr<I2NPMessage> NewI2NPTunnelMessage (bool endpoint);
void SetMaxNumTransitTunnels (uint32_t maxNumTransitTunnels); void SetMaxNumTransitTunnels (uint16_t maxNumTransitTunnels);
uint32_t GetMaxNumTransitTunnels () const { return m_MaxNumTransitTunnels; }; uint16_t GetMaxNumTransitTunnels () const { return m_MaxNumTransitTunnels; };
int GetCongestionLevel() const { return m_MaxNumTransitTunnels ? CONGESTION_LEVEL_FULL * m_TransitTunnels.size() / m_MaxNumTransitTunnels : CONGESTION_LEVEL_FULL; } bool IsTooManyTransitTunnels () const { return m_TransitTunnels.size () >= m_MaxNumTransitTunnels; };
private: private:
@ -301,7 +292,7 @@ namespace tunnel
i2p::util::Queue<std::shared_ptr<I2NPMessage> > m_Queue; i2p::util::Queue<std::shared_ptr<I2NPMessage> > m_Queue;
i2p::util::MemoryPoolMt<I2NPMessageBuffer<I2NP_TUNNEL_ENPOINT_MESSAGE_SIZE> > m_I2NPTunnelEndpointMessagesMemoryPool; i2p::util::MemoryPoolMt<I2NPMessageBuffer<I2NP_TUNNEL_ENPOINT_MESSAGE_SIZE> > m_I2NPTunnelEndpointMessagesMemoryPool;
i2p::util::MemoryPoolMt<I2NPMessageBuffer<I2NP_TUNNEL_MESSAGE_SIZE> > m_I2NPTunnelMessagesMemoryPool; i2p::util::MemoryPoolMt<I2NPMessageBuffer<I2NP_TUNNEL_MESSAGE_SIZE> > m_I2NPTunnelMessagesMemoryPool;
uint32_t m_MaxNumTransitTunnels; uint16_t m_MaxNumTransitTunnels;
// count of tunnels for total TCSR algorithm // count of tunnels for total TCSR algorithm
int m_TotalNumSuccesiveTunnelCreations, m_TotalNumFailedTunnelCreations; int m_TotalNumSuccesiveTunnelCreations, m_TotalNumFailedTunnelCreations;
double m_TunnelCreationSuccessRate; double m_TunnelCreationSuccessRate;

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2024, The PurpleI2P Project * Copyright (c) 2013-2021, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@ -35,13 +35,6 @@ namespace tunnel
if (!m_CurrentTunnelDataMsg) if (!m_CurrentTunnelDataMsg)
{ {
CreateCurrentTunnelDataMessage (); CreateCurrentTunnelDataMessage ();
if (block.data && block.data->onDrop)
{
// onDrop is called for the first fragment in tunnel message
// that's usually true for short TBMs or lookups
m_CurrentTunnelDataMsg->onDrop = block.data->onDrop;
block.data->onDrop = nullptr;
}
messageCreated = true; messageCreated = true;
} }
@ -162,6 +155,7 @@ namespace tunnel
void TunnelGatewayBuffer::CreateCurrentTunnelDataMessage () void TunnelGatewayBuffer::CreateCurrentTunnelDataMessage ()
{ {
m_CurrentTunnelDataMsg = nullptr;
m_CurrentTunnelDataMsg = NewI2NPTunnelMessage (true); // tunnel endpoint is at least of two tunnel messages size m_CurrentTunnelDataMsg = NewI2NPTunnelMessage (true); // tunnel endpoint is at least of two tunnel messages size
// we reserve space for padding // we reserve space for padding
m_CurrentTunnelDataMsg->offset += TUNNEL_DATA_MSG_SIZE + I2NP_HEADER_SIZE; m_CurrentTunnelDataMsg->offset += TUNNEL_DATA_MSG_SIZE + I2NP_HEADER_SIZE;
@ -229,7 +223,6 @@ namespace tunnel
m_Tunnel->EncryptTunnelMsg (tunnelMsg, newMsg); m_Tunnel->EncryptTunnelMsg (tunnelMsg, newMsg);
htobe32buf (newMsg->GetPayload (), m_Tunnel->GetNextTunnelID ()); htobe32buf (newMsg->GetPayload (), m_Tunnel->GetNextTunnelID ());
newMsg->FillI2NPMessageHeader (eI2NPTunnelData); newMsg->FillI2NPMessageHeader (eI2NPTunnelData);
if (tunnelMsg->onDrop) newMsg->onDrop = tunnelMsg->onDrop;
newTunnelMsgs.push_back (newMsg); newTunnelMsgs.push_back (newMsg);
m_NumSentBytes += TUNNEL_DATA_MSG_SIZE; m_NumSentBytes += TUNNEL_DATA_MSG_SIZE;
} }

@ -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 * This file is part of Purple i2pd project and licensed under BSD3
* *
@ -7,13 +7,13 @@
*/ */
#include <algorithm> #include <algorithm>
#include <random>
#include "I2PEndian.h" #include "I2PEndian.h"
#include "Crypto.h" #include "Crypto.h"
#include "Tunnel.h" #include "Tunnel.h"
#include "NetDb.hpp" #include "NetDb.hpp"
#include "Timestamp.h" #include "Timestamp.h"
#include "Garlic.h" #include "Garlic.h"
#include "ECIESX25519AEADRatchetSession.h"
#include "Transports.h" #include "Transports.h"
#include "Log.h" #include "Log.h"
#include "Tunnel.h" #include "Tunnel.h"
@ -45,8 +45,7 @@ namespace tunnel
m_NumInboundHops (numInboundHops), m_NumOutboundHops (numOutboundHops), m_NumInboundHops (numInboundHops), m_NumOutboundHops (numOutboundHops),
m_NumInboundTunnels (numInboundTunnels), m_NumOutboundTunnels (numOutboundTunnels), m_NumInboundTunnels (numInboundTunnels), m_NumOutboundTunnels (numOutboundTunnels),
m_InboundVariance (inboundVariance), m_OutboundVariance (outboundVariance), m_InboundVariance (inboundVariance), m_OutboundVariance (outboundVariance),
m_IsActive (true), m_CustomPeerSelector(nullptr), m_IsActive (true), m_CustomPeerSelector(nullptr)
m_Rng(i2p::util::GetMonotonicMicroseconds ()%1000000LL)
{ {
if (m_NumInboundTunnels > TUNNEL_POOL_MAX_INBOUND_TUNNELS_QUANTITY) if (m_NumInboundTunnels > TUNNEL_POOL_MAX_INBOUND_TUNNELS_QUANTITY)
m_NumInboundTunnels = TUNNEL_POOL_MAX_INBOUND_TUNNELS_QUANTITY; m_NumInboundTunnels = TUNNEL_POOL_MAX_INBOUND_TUNNELS_QUANTITY;
@ -60,7 +59,7 @@ namespace tunnel
m_InboundVariance = (m_NumInboundHops < STANDARD_NUM_RECORDS) ? STANDARD_NUM_RECORDS - m_NumInboundHops : 0; m_InboundVariance = (m_NumInboundHops < STANDARD_NUM_RECORDS) ? STANDARD_NUM_RECORDS - m_NumInboundHops : 0;
if (m_OutboundVariance > 0 && m_NumOutboundHops + m_OutboundVariance > STANDARD_NUM_RECORDS) if (m_OutboundVariance > 0 && m_NumOutboundHops + m_OutboundVariance > STANDARD_NUM_RECORDS)
m_OutboundVariance = (m_NumOutboundHops < STANDARD_NUM_RECORDS) ? STANDARD_NUM_RECORDS - m_NumOutboundHops : 0; m_OutboundVariance = (m_NumOutboundHops < STANDARD_NUM_RECORDS) ? STANDARD_NUM_RECORDS - m_NumOutboundHops : 0;
m_NextManageTime = i2p::util::GetSecondsSinceEpoch () + m_Rng () % TUNNEL_POOL_MANAGE_INTERVAL; m_NextManageTime = i2p::util::GetSecondsSinceEpoch () + rand () % TUNNEL_POOL_MANAGE_INTERVAL;
} }
TunnelPool::~TunnelPool () TunnelPool::~TunnelPool ()
@ -103,10 +102,7 @@ namespace tunnel
it->SetTunnelPool (nullptr); it->SetTunnelPool (nullptr);
m_OutboundTunnels.clear (); m_OutboundTunnels.clear ();
} }
{ m_Tests.clear ();
std::unique_lock<std::mutex> l(m_TestsMutex);
m_Tests.clear ();
}
} }
bool TunnelPool::Reconfigure(int inHops, int outHops, int inQuant, int outQuant) bool TunnelPool::Reconfigure(int inHops, int outHops, int inQuant, int outQuant)
@ -149,11 +145,8 @@ namespace tunnel
if (expiredTunnel) if (expiredTunnel)
{ {
expiredTunnel->SetTunnelPool (nullptr); expiredTunnel->SetTunnelPool (nullptr);
{ for (auto& it: m_Tests)
std::unique_lock<std::mutex> l(m_TestsMutex); if (it.second.second == expiredTunnel) it.second.second = nullptr;
for (auto& it: m_Tests)
if (it.second.second == expiredTunnel) it.second.second = nullptr;
}
std::unique_lock<std::mutex> l(m_InboundTunnelsMutex); std::unique_lock<std::mutex> l(m_InboundTunnelsMutex);
m_InboundTunnels.erase (expiredTunnel); m_InboundTunnels.erase (expiredTunnel);
@ -174,11 +167,8 @@ namespace tunnel
if (expiredTunnel) if (expiredTunnel)
{ {
expiredTunnel->SetTunnelPool (nullptr); expiredTunnel->SetTunnelPool (nullptr);
{ for (auto& it: m_Tests)
std::unique_lock<std::mutex> l(m_TestsMutex); if (it.second.first == expiredTunnel) it.second.first = nullptr;
for (auto& it: m_Tests)
if (it.second.first == expiredTunnel) it.second.first = nullptr;
}
std::unique_lock<std::mutex> l(m_OutboundTunnelsMutex); std::unique_lock<std::mutex> l(m_OutboundTunnelsMutex);
m_OutboundTunnels.erase (expiredTunnel); m_OutboundTunnels.erase (expiredTunnel);
@ -211,14 +201,14 @@ namespace tunnel
} }
std::shared_ptr<OutboundTunnel> TunnelPool::GetNextOutboundTunnel (std::shared_ptr<OutboundTunnel> excluded, std::shared_ptr<OutboundTunnel> TunnelPool::GetNextOutboundTunnel (std::shared_ptr<OutboundTunnel> excluded,
i2p::data::RouterInfo::CompatibleTransports compatible) i2p::data::RouterInfo::CompatibleTransports compatible) const
{ {
std::unique_lock<std::mutex> l(m_OutboundTunnelsMutex); std::unique_lock<std::mutex> l(m_OutboundTunnelsMutex);
return GetNextTunnel (m_OutboundTunnels, excluded, compatible); return GetNextTunnel (m_OutboundTunnels, excluded, compatible);
} }
std::shared_ptr<InboundTunnel> TunnelPool::GetNextInboundTunnel (std::shared_ptr<InboundTunnel> excluded, std::shared_ptr<InboundTunnel> TunnelPool::GetNextInboundTunnel (std::shared_ptr<InboundTunnel> excluded,
i2p::data::RouterInfo::CompatibleTransports compatible) i2p::data::RouterInfo::CompatibleTransports compatible) const
{ {
std::unique_lock<std::mutex> l(m_InboundTunnelsMutex); std::unique_lock<std::mutex> l(m_InboundTunnelsMutex);
return GetNextTunnel (m_InboundTunnels, excluded, compatible); return GetNextTunnel (m_InboundTunnels, excluded, compatible);
@ -226,10 +216,10 @@ namespace tunnel
template<class TTunnels> template<class TTunnels>
typename TTunnels::value_type TunnelPool::GetNextTunnel (TTunnels& tunnels, typename TTunnels::value_type TunnelPool::GetNextTunnel (TTunnels& tunnels,
typename TTunnels::value_type excluded, i2p::data::RouterInfo::CompatibleTransports compatible) typename TTunnels::value_type excluded, i2p::data::RouterInfo::CompatibleTransports compatible) const
{ {
if (tunnels.empty ()) return nullptr; if (tunnels.empty ()) return nullptr;
uint32_t ind = m_Rng () % (tunnels.size ()/2 + 1), i = 0; uint32_t ind = rand () % (tunnels.size ()/2 + 1), i = 0;
bool skipped = false; bool skipped = false;
typename TTunnels::value_type tunnel = nullptr; typename TTunnels::value_type tunnel = nullptr;
for (const auto& it: tunnels) for (const auto& it: tunnels)
@ -249,7 +239,7 @@ namespace tunnel
} }
if (!tunnel && skipped) if (!tunnel && skipped)
{ {
ind = m_Rng () % (tunnels.size ()/2 + 1), i = 0; ind = rand () % (tunnels.size ()/2 + 1), i = 0;
for (const auto& it: tunnels) for (const auto& it: tunnels)
{ {
if (it->IsEstablished () && it != excluded) if (it->IsEstablished () && it != excluded)
@ -264,11 +254,10 @@ namespace tunnel
return tunnel; return tunnel;
} }
std::pair<std::shared_ptr<OutboundTunnel>, bool> TunnelPool::GetNewOutboundTunnel (std::shared_ptr<OutboundTunnel> old) std::shared_ptr<OutboundTunnel> TunnelPool::GetNewOutboundTunnel (std::shared_ptr<OutboundTunnel> old) const
{ {
if (old && old->IsEstablished ()) return std::make_pair(old, false); if (old && old->IsEstablished ()) return old;
std::shared_ptr<OutboundTunnel> tunnel; std::shared_ptr<OutboundTunnel> tunnel;
bool freshTunnel = false;
if (old) if (old)
{ {
std::unique_lock<std::mutex> l(m_OutboundTunnelsMutex); std::unique_lock<std::mutex> l(m_OutboundTunnelsMutex);
@ -281,11 +270,8 @@ namespace tunnel
} }
if (!tunnel) if (!tunnel)
{
tunnel = GetNextOutboundTunnel (); tunnel = GetNextOutboundTunnel ();
freshTunnel = true; return tunnel;
}
return std::make_pair(tunnel, freshTunnel);
} }
void TunnelPool::CreateTunnels () void TunnelPool::CreateTunnels ()
@ -315,7 +301,7 @@ namespace tunnel
{ {
for (auto it: m_OutboundTunnels) for (auto it: m_OutboundTunnels)
{ {
// try to create inbound tunnel through the same path as successive outbound // try to create inbound tunnel through the same path as succesive outbound
CreatePairedInboundTunnel (it); CreatePairedInboundTunnel (it);
num++; num++;
if (num >= m_NumInboundTunnels) break; if (num >= m_NumInboundTunnels) break;
@ -351,12 +337,9 @@ namespace tunnel
{ {
it.second.first->SetState (eTunnelStateFailed); it.second.first->SetState (eTunnelStateFailed);
std::unique_lock<std::mutex> l(m_OutboundTunnelsMutex); std::unique_lock<std::mutex> l(m_OutboundTunnelsMutex);
if (m_OutboundTunnels.size () > 1 || m_NumOutboundTunnels <= 1) // don't fail last tunnel m_OutboundTunnels.erase (it.second.first);
m_OutboundTunnels.erase (it.second.first);
else
it.second.first->SetState (eTunnelStateTestFailed);
} }
else if (it.second.first->GetState () != eTunnelStateExpiring) else
it.second.first->SetState (eTunnelStateTestFailed); it.second.first->SetState (eTunnelStateTestFailed);
} }
if (it.second.second) if (it.second.second)
@ -365,97 +348,48 @@ namespace tunnel
{ {
it.second.second->SetState (eTunnelStateFailed); it.second.second->SetState (eTunnelStateFailed);
{ {
bool failed = false; std::unique_lock<std::mutex> l(m_InboundTunnelsMutex);
{ m_InboundTunnels.erase (it.second.second);
std::unique_lock<std::mutex> l(m_InboundTunnelsMutex);
if (m_InboundTunnels.size () > 1 || m_NumInboundTunnels <= 1) // don't fail last tunnel
{
m_InboundTunnels.erase (it.second.second);
failed = true;
}
else
it.second.second->SetState (eTunnelStateTestFailed);
}
if (failed && m_LocalDestination)
m_LocalDestination->SetLeaseSetUpdated ();
} }
if (m_LocalDestination) if (m_LocalDestination)
m_LocalDestination->SetLeaseSetUpdated (); m_LocalDestination->SetLeaseSetUpdated ();
} }
else if (it.second.second->GetState () != eTunnelStateExpiring) else
it.second.second->SetState (eTunnelStateTestFailed); it.second.second->SetState (eTunnelStateTestFailed);
} }
} }
// new tests // new tests
if (!m_LocalDestination) return; std::unique_lock<std::mutex> l1(m_OutboundTunnelsMutex);
std::vector<std::pair<std::shared_ptr<OutboundTunnel>, std::shared_ptr<InboundTunnel> > > newTests; auto it1 = m_OutboundTunnels.begin ();
std::vector<std::shared_ptr<OutboundTunnel> > outboundTunnels; std::unique_lock<std::mutex> l2(m_InboundTunnelsMutex);
{ auto it2 = m_InboundTunnels.begin ();
std::unique_lock<std::mutex> l(m_OutboundTunnelsMutex); while (it1 != m_OutboundTunnels.end () && it2 != m_InboundTunnels.end ())
for (auto& it: m_OutboundTunnels) {
if (it->IsEstablished ()) bool failed = false;
outboundTunnels.push_back (it); if ((*it1)->IsFailed ())
}
std::shuffle (outboundTunnels.begin(), outboundTunnels.end(), m_Rng);
std::vector<std::shared_ptr<InboundTunnel> > inboundTunnels;
{
std::unique_lock<std::mutex> l(m_InboundTunnelsMutex);
for (auto& it: m_InboundTunnels)
if (it->IsEstablished ())
inboundTunnels.push_back (it);
}
std::shuffle (inboundTunnels.begin(), inboundTunnels.end(), m_Rng);
auto it1 = outboundTunnels.begin ();
auto it2 = inboundTunnels.begin ();
while (it1 != outboundTunnels.end () && it2 != inboundTunnels.end ())
{
newTests.push_back(std::make_pair (*it1, *it2));
++it1; ++it2;
}
bool isECIES = m_LocalDestination->SupportsEncryptionType (i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD);
for (auto& it: newTests)
{
uint32_t msgID;
RAND_bytes ((uint8_t *)&msgID, 4);
{ {
std::unique_lock<std::mutex> l(m_TestsMutex); failed = true;
m_Tests[msgID] = it; ++it1;
} }
auto msg = CreateTunnelTestMsg (msgID); if ((*it2)->IsFailed ())
auto outbound = it.first;
auto s = shared_from_this ();
msg->onDrop = [msgID, outbound, s]()
{
// if test msg dropped locally it's outbound tunnel to blame
outbound->SetState (eTunnelStateFailed);
{
std::unique_lock<std::mutex> l(s->m_TestsMutex);
s->m_Tests.erase (msgID);
}
{
std::unique_lock<std::mutex> l(s->m_OutboundTunnelsMutex);
s->m_OutboundTunnels.erase (outbound);
}
};
// encrypt
if (isECIES)
{ {
uint8_t key[32]; RAND_bytes (key, 32); failed = true;
uint64_t tag; RAND_bytes ((uint8_t *)&tag, 8); ++it2;
m_LocalDestination->SubmitECIESx25519Key (key, tag);
msg = i2p::garlic::WrapECIESX25519Message (msg, key, tag);
} }
else if (!failed)
{ {
uint8_t key[32], tag[32]; uint32_t msgID;
RAND_bytes (key, 32); RAND_bytes (tag, 32); RAND_bytes ((uint8_t *)&msgID, 4);
m_LocalDestination->SubmitSessionKey (key, tag); {
i2p::garlic::ElGamalAESSession garlic (key, tag); std::unique_lock<std::mutex> l(m_TestsMutex);
msg = garlic.WrapSingleMessage (msg); m_Tests[msgID] = std::make_pair (*it1, *it2);
} }
outbound->SendTunnelDataMsgTo (it.second->GetNextIdentHash (), it.second->GetNextTunnelID (), msg); (*it1)->SendTunnelDataMsgTo ((*it2)->GetNextIdentHash (), (*it2)->GetNextTunnelID (),
} CreateDeliveryStatusMsg (msgID));
++it1; ++it2;
}
}
} }
void TunnelPool::ManageTunnels (uint64_t ts) void TunnelPool::ManageTunnels (uint64_t ts)
@ -464,7 +398,7 @@ namespace tunnel
{ {
CreateTunnels (); CreateTunnels ();
TestTunnels (); TestTunnels ();
m_NextManageTime = ts + TUNNEL_POOL_MANAGE_INTERVAL + (m_Rng () % TUNNEL_POOL_MANAGE_INTERVAL)/2; m_NextManageTime = ts + TUNNEL_POOL_MANAGE_INTERVAL + (rand () % TUNNEL_POOL_MANAGE_INTERVAL)/2;
} }
} }
@ -477,25 +411,12 @@ namespace tunnel
} }
void TunnelPool::ProcessDeliveryStatus (std::shared_ptr<I2NPMessage> msg) void TunnelPool::ProcessDeliveryStatus (std::shared_ptr<I2NPMessage> msg)
{
if (m_LocalDestination)
m_LocalDestination->ProcessDeliveryStatusMessage (msg);
else
LogPrint (eLogWarning, "Tunnels: Local destination doesn't exist, dropped");
}
void TunnelPool::ProcessTunnelTest (std::shared_ptr<I2NPMessage> msg)
{ {
const uint8_t * buf = msg->GetPayload (); const uint8_t * buf = msg->GetPayload ();
uint32_t msgID = bufbe32toh (buf); uint32_t msgID = bufbe32toh (buf);
buf += 4; buf += 4;
uint64_t timestamp = bufbe64toh (buf); uint64_t timestamp = bufbe64toh (buf);
ProcessTunnelTest (msgID, timestamp);
}
bool TunnelPool::ProcessTunnelTest (uint32_t msgID, uint64_t timestamp)
{
decltype(m_Tests)::mapped_type test; decltype(m_Tests)::mapped_type test;
bool found = false; bool found = false;
{ {
@ -510,37 +431,42 @@ namespace tunnel
} }
if (found) if (found)
{ {
int dlt = (uint64_t)i2p::util::GetMonotonicMicroseconds () - (int64_t)timestamp; uint64_t dlt = i2p::util::GetMillisecondsSinceEpoch () - timestamp;
LogPrint (eLogDebug, "Tunnels: Test of ", msgID, " successful. ", dlt, " microseconds"); LogPrint (eLogDebug, "Tunnels: Test of ", msgID, " successful. ", dlt, " milliseconds");
if (dlt < 0) dlt = 0; // should not happen
int numHops = 0; int numHops = 0;
if (test.first) numHops += test.first->GetNumHops (); if (test.first) numHops += test.first->GetNumHops ();
if (test.second) numHops += test.second->GetNumHops (); if (test.second) numHops += test.second->GetNumHops ();
// restore from test failed state if any // restore from test failed state if any
if (test.first) if (test.first)
{ {
if (test.first->GetState () != eTunnelStateExpiring) if (test.first->GetState () == eTunnelStateTestFailed)
test.first->SetState (eTunnelStateEstablished); test.first->SetState (eTunnelStateEstablished);
// update latency // update latency
int latency = 0; uint64_t latency = 0;
if (numHops) latency = dlt*test.first->GetNumHops ()/numHops; if (numHops) latency = dlt*test.first->GetNumHops ()/numHops;
if (!latency) latency = dlt/2; if (!latency) latency = dlt/2;
test.first->AddLatencySample (latency); test.first->AddLatencySample(latency);
} }
if (test.second) if (test.second)
{ {
if (test.second->GetState () != eTunnelStateExpiring) if (test.second->GetState () == eTunnelStateTestFailed)
test.second->SetState (eTunnelStateEstablished); test.second->SetState (eTunnelStateEstablished);
// update latency // update latency
int latency = 0; uint64_t latency = 0;
if (numHops) latency = dlt*test.second->GetNumHops ()/numHops; if (numHops) latency = dlt*test.second->GetNumHops ()/numHops;
if (!latency) latency = dlt/2; if (!latency) latency = dlt/2;
test.second->AddLatencySample (latency); test.second->AddLatencySample(latency);
} }
} }
return found; else
} {
if (m_LocalDestination)
m_LocalDestination->ProcessDeliveryStatusMessage (msg);
else
LogPrint (eLogWarning, "Tunnels: Local destination doesn't exist, dropped");
}
}
bool TunnelPool::IsExploratory () const bool TunnelPool::IsExploratory () const
{ {
return i2p::tunnel::tunnels.GetExploratoryPool () == shared_from_this (); return i2p::tunnel::tunnels.GetExploratoryPool () == shared_from_this ();
@ -549,23 +475,11 @@ namespace tunnel
std::shared_ptr<const i2p::data::RouterInfo> TunnelPool::SelectNextHop (std::shared_ptr<const i2p::data::RouterInfo> prevHop, std::shared_ptr<const i2p::data::RouterInfo> TunnelPool::SelectNextHop (std::shared_ptr<const i2p::data::RouterInfo> prevHop,
bool reverse, bool endpoint) const bool reverse, bool endpoint) const
{ {
bool tryHighBandwidth = !IsExploratory (); auto hop = IsExploratory () ? i2p::data::netdb.GetRandomRouter (prevHop, reverse, endpoint):
std::shared_ptr<const i2p::data::RouterInfo> hop; i2p::data::netdb.GetHighBandwidthRandomRouter (prevHop, reverse, endpoint);
for (int i = 0; i < TUNNEL_POOL_MAX_HOP_SELECTION_ATTEMPTS; i++)
{ if (!hop || hop->GetProfile ()->IsBad ())
hop = tryHighBandwidth ? hop = i2p::data::netdb.GetRandomRouter (prevHop, reverse, endpoint);
i2p::data::netdb.GetHighBandwidthRandomRouter (prevHop, reverse, endpoint) :
i2p::data::netdb.GetRandomRouter (prevHop, reverse, endpoint);
if (hop)
{
if (!hop->GetProfile ()->IsBad ())
break;
}
else if (tryHighBandwidth)
tryHighBandwidth = false;
else
return nullptr;
}
return hop; return hop;
} }
@ -627,7 +541,7 @@ namespace tunnel
numHops = m_NumInboundHops; numHops = m_NumInboundHops;
if (m_InboundVariance) if (m_InboundVariance)
{ {
int offset = m_Rng () % (std::abs (m_InboundVariance) + 1); int offset = rand () % (std::abs (m_InboundVariance) + 1);
if (m_InboundVariance < 0) offset = -offset; if (m_InboundVariance < 0) offset = -offset;
numHops += offset; numHops += offset;
} }
@ -637,7 +551,7 @@ namespace tunnel
numHops = m_NumOutboundHops; numHops = m_NumOutboundHops;
if (m_OutboundVariance) if (m_OutboundVariance)
{ {
int offset = m_Rng () % (std::abs (m_OutboundVariance) + 1); int offset = rand () % (std::abs (m_OutboundVariance) + 1);
if (m_OutboundVariance < 0) offset = -offset; if (m_OutboundVariance < 0) offset = -offset;
numHops += offset; numHops += offset;
} }
@ -857,7 +771,7 @@ namespace tunnel
{ {
std::shared_ptr<InboundTunnel> tun = nullptr; std::shared_ptr<InboundTunnel> tun = nullptr;
std::unique_lock<std::mutex> lock(m_InboundTunnelsMutex); std::unique_lock<std::mutex> lock(m_InboundTunnelsMutex);
int min = 1000000; uint64_t min = 1000000;
for (const auto & itr : m_InboundTunnels) { for (const auto & itr : m_InboundTunnels) {
if(!itr->LatencyIsKnown()) continue; if(!itr->LatencyIsKnown()) continue;
auto l = itr->GetMeanLatency(); auto l = itr->GetMeanLatency();
@ -873,7 +787,7 @@ namespace tunnel
{ {
std::shared_ptr<OutboundTunnel> tun = nullptr; std::shared_ptr<OutboundTunnel> tun = nullptr;
std::unique_lock<std::mutex> lock(m_OutboundTunnelsMutex); std::unique_lock<std::mutex> lock(m_OutboundTunnelsMutex);
int min = 1000000; uint64_t min = 1000000;
for (const auto & itr : m_OutboundTunnels) { for (const auto & itr : m_OutboundTunnels) {
if(!itr->LatencyIsKnown()) continue; if(!itr->LatencyIsKnown()) continue;
auto l = itr->GetMeanLatency(); auto l = itr->GetMeanLatency();

@ -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 * This file is part of Purple i2pd project and licensed under BSD3
* *
@ -15,7 +15,6 @@
#include <utility> #include <utility>
#include <mutex> #include <mutex>
#include <memory> #include <memory>
#include <random>
#include "Identity.h" #include "Identity.h"
#include "LeaseSet.h" #include "LeaseSet.h"
#include "RouterInfo.h" #include "RouterInfo.h"
@ -31,8 +30,7 @@ namespace tunnel
const int TUNNEL_POOL_MANAGE_INTERVAL = 10; // in seconds const int TUNNEL_POOL_MANAGE_INTERVAL = 10; // in seconds
const int TUNNEL_POOL_MAX_INBOUND_TUNNELS_QUANTITY = 16; const int TUNNEL_POOL_MAX_INBOUND_TUNNELS_QUANTITY = 16;
const int TUNNEL_POOL_MAX_OUTBOUND_TUNNELS_QUANTITY = 16; const int TUNNEL_POOL_MAX_OUTBOUND_TUNNELS_QUANTITY = 16;
const int TUNNEL_POOL_MAX_NUM_BUILD_REQUESTS = 3; const int TUNNEL_POOL_MAX_NUM_BUILD_REQUESTS = 2;
const int TUNNEL_POOL_MAX_HOP_SELECTION_ATTEMPTS = 3;
class Tunnel; class Tunnel;
class InboundTunnel; class InboundTunnel;
@ -78,15 +76,13 @@ namespace tunnel
void RecreateOutboundTunnel (std::shared_ptr<OutboundTunnel> tunnel); void RecreateOutboundTunnel (std::shared_ptr<OutboundTunnel> tunnel);
std::vector<std::shared_ptr<InboundTunnel> > GetInboundTunnels (int num) const; std::vector<std::shared_ptr<InboundTunnel> > GetInboundTunnels (int num) const;
std::shared_ptr<OutboundTunnel> GetNextOutboundTunnel (std::shared_ptr<OutboundTunnel> excluded = nullptr, std::shared_ptr<OutboundTunnel> GetNextOutboundTunnel (std::shared_ptr<OutboundTunnel> excluded = nullptr,
i2p::data::RouterInfo::CompatibleTransports compatible = i2p::data::RouterInfo::eAllTransports); i2p::data::RouterInfo::CompatibleTransports compatible = i2p::data::RouterInfo::eAllTransports) const;
std::shared_ptr<InboundTunnel> GetNextInboundTunnel (std::shared_ptr<InboundTunnel> excluded = nullptr, std::shared_ptr<InboundTunnel> GetNextInboundTunnel (std::shared_ptr<InboundTunnel> excluded = nullptr,
i2p::data::RouterInfo::CompatibleTransports compatible = i2p::data::RouterInfo::eAllTransports); i2p::data::RouterInfo::CompatibleTransports compatible = i2p::data::RouterInfo::eAllTransports) const;
std::pair<std::shared_ptr<OutboundTunnel>, bool> GetNewOutboundTunnel (std::shared_ptr<OutboundTunnel> old); std::shared_ptr<OutboundTunnel> GetNewOutboundTunnel (std::shared_ptr<OutboundTunnel> old) const;
void ManageTunnels (uint64_t ts); void ManageTunnels (uint64_t ts);
void ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg); void ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg);
void ProcessDeliveryStatus (std::shared_ptr<I2NPMessage> msg); void ProcessDeliveryStatus (std::shared_ptr<I2NPMessage> msg);
void ProcessTunnelTest (std::shared_ptr<I2NPMessage> msg);
bool ProcessTunnelTest (uint32_t msgID, uint64_t timestamp);
bool IsExploratory () const; bool IsExploratory () const;
bool IsActive () const { return m_IsActive; }; bool IsActive () const { return m_IsActive; };
@ -106,7 +102,7 @@ namespace tunnel
bool HasCustomPeerSelector(); bool HasCustomPeerSelector();
/** @brief make this tunnel pool yield tunnels that fit latency range [min, max] */ /** @brief make this tunnel pool yield tunnels that fit latency range [min, max] */
void RequireLatency(int min, int max) { m_MinLatency = min; m_MaxLatency = max; } void RequireLatency(uint64_t min, uint64_t max) { m_MinLatency = min; m_MaxLatency = max; }
/** @brief return true if this tunnel pool has a latency requirement */ /** @brief return true if this tunnel pool has a latency requirement */
bool HasLatencyRequirement() const { return m_MinLatency > 0 && m_MaxLatency > 0; } bool HasLatencyRequirement() const { return m_MinLatency > 0 && m_MaxLatency > 0; }
@ -119,8 +115,6 @@ namespace tunnel
std::shared_ptr<const i2p::data::RouterInfo> SelectNextHop (std::shared_ptr<const i2p::data::RouterInfo> prevHop, bool reverse, bool endpoint) const; std::shared_ptr<const i2p::data::RouterInfo> SelectNextHop (std::shared_ptr<const i2p::data::RouterInfo> prevHop, bool reverse, bool endpoint) const;
bool StandardSelectPeers(Path & path, int numHops, bool inbound, SelectHopFunc nextHop); bool StandardSelectPeers(Path & path, int numHops, bool inbound, SelectHopFunc nextHop);
std::mt19937& GetRng () { return m_Rng; }
private: private:
void TestTunnels (); void TestTunnels ();
@ -129,7 +123,7 @@ namespace tunnel
void CreatePairedInboundTunnel (std::shared_ptr<OutboundTunnel> outboundTunnel); void CreatePairedInboundTunnel (std::shared_ptr<OutboundTunnel> outboundTunnel);
template<class TTunnels> template<class TTunnels>
typename TTunnels::value_type GetNextTunnel (TTunnels& tunnels, typename TTunnels::value_type GetNextTunnel (TTunnels& tunnels,
typename TTunnels::value_type excluded, i2p::data::RouterInfo::CompatibleTransports compatible); typename TTunnels::value_type excluded, i2p::data::RouterInfo::CompatibleTransports compatible) const;
bool SelectPeers (Path& path, bool isInbound); bool SelectPeers (Path& path, bool isInbound);
bool SelectExplicitPeers (Path& path, bool isInbound); bool SelectExplicitPeers (Path& path, bool isInbound);
bool ValidatePeers (std::vector<std::shared_ptr<const i2p::data::IdentityEx> >& peers) const; bool ValidatePeers (std::vector<std::shared_ptr<const i2p::data::IdentityEx> >& peers) const;
@ -151,11 +145,9 @@ namespace tunnel
std::mutex m_CustomPeerSelectorMutex; std::mutex m_CustomPeerSelectorMutex;
ITunnelPeerSelector * m_CustomPeerSelector; ITunnelPeerSelector * m_CustomPeerSelector;
int m_MinLatency = 0; // if > 0 this tunnel pool will try building tunnels with minimum latency by ms uint64_t m_MinLatency = 0; // if > 0 this tunnel pool will try building tunnels with minimum latency by ms
int m_MaxLatency = 0; // if > 0 this tunnel pool will try building tunnels with maximum latency by ms uint64_t m_MaxLatency = 0; // if > 0 this tunnel pool will try building tunnels with maximum latency by ms
std::mt19937 m_Rng;
public: public:
// for HTTP only // for HTTP only

@ -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 * This file is part of Purple i2pd project and licensed under BSD3
* *
@ -44,9 +44,6 @@ namespace api
int netID; i2p::config::GetOption("netid", netID); int netID; i2p::config::GetOption("netid", netID);
i2p::context.SetNetID (netID); i2p::context.SetNetID (netID);
bool checkReserved; i2p::config::GetOption("reservedrange", checkReserved);
i2p::transport::transports.SetCheckReserved(checkReserved);
i2p::context.Init (); i2p::context.Init ();
} }

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2023, The PurpleI2P Project * Copyright (c) 2013-2022, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@ -27,17 +27,6 @@
# include <AvailabilityMacros.h> # include <AvailabilityMacros.h>
#endif #endif
#if defined(__HAIKU__)
#include <gnu/pthread.h>
#include <posix/pthread.h>
#include <posix/sys/sockio.h>
#include <posix/sys/ioctl.h>
#ifndef _DEFAULT_SOURCE
#define _DEFAULT_SOURCE
#include <bsd/ifaddrs.h>
#endif
#endif
#ifdef _WIN32 #ifdef _WIN32
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
@ -665,7 +654,6 @@ namespace net
if (host.is_v6()) if (host.is_v6())
{ {
static const std::vector< std::pair<boost::asio::ip::address_v6::bytes_type, boost::asio::ip::address_v6::bytes_type> > reservedIPv6Ranges { static const std::vector< std::pair<boost::asio::ip::address_v6::bytes_type, boost::asio::ip::address_v6::bytes_type> > reservedIPv6Ranges {
address_pair_v6("64:ff9b::", "64:ff9b:ffff:ffff:ffff:ffff:ffff:ffff"), // NAT64
address_pair_v6("2001:db8::", "2001:db8:ffff:ffff:ffff:ffff:ffff:ffff"), address_pair_v6("2001:db8::", "2001:db8:ffff:ffff:ffff:ffff:ffff:ffff"),
address_pair_v6("fc00::", "fdff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"), address_pair_v6("fc00::", "fdff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"),
address_pair_v6("fe80::", "febf:ffff:ffff:ffff:ffff:ffff:ffff:ffff"), address_pair_v6("fe80::", "febf:ffff:ffff:ffff:ffff:ffff:ffff:ffff"),

@ -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 * This file is part of Purple i2pd project and licensed under BSD3
* *
@ -18,7 +18,7 @@
#define MAKE_VERSION_NUMBER(a,b,c) ((a*100+b)*100+c) #define MAKE_VERSION_NUMBER(a,b,c) ((a*100+b)*100+c)
#define I2PD_VERSION_MAJOR 2 #define I2PD_VERSION_MAJOR 2
#define I2PD_VERSION_MINOR 52 #define I2PD_VERSION_MINOR 49
#define I2PD_VERSION_MICRO 0 #define I2PD_VERSION_MICRO 0
#define I2PD_VERSION_PATCH 0 #define I2PD_VERSION_PATCH 0
#ifdef GITVER #ifdef GITVER
@ -33,7 +33,7 @@
#define I2P_VERSION_MAJOR 0 #define I2P_VERSION_MAJOR 0
#define I2P_VERSION_MINOR 9 #define I2P_VERSION_MINOR 9
#define I2P_VERSION_MICRO 62 #define I2P_VERSION_MICRO 60
#define I2P_VERSION_PATCH 0 #define I2P_VERSION_PATCH 0
#define I2P_VERSION MAKE_VERSION(I2P_VERSION_MAJOR, I2P_VERSION_MINOR, I2P_VERSION_MICRO) #define I2P_VERSION MAKE_VERSION(I2P_VERSION_MAJOR, I2P_VERSION_MINOR, I2P_VERSION_MICRO)
#define I2P_VERSION_NUMBER MAKE_VERSION_NUMBER(I2P_VERSION_MAJOR, I2P_VERSION_MINOR, I2P_VERSION_MICRO) #define I2P_VERSION_NUMBER MAKE_VERSION_NUMBER(I2P_VERSION_MAJOR, I2P_VERSION_MINOR, I2P_VERSION_MICRO)

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2023, The PurpleI2P Project * Copyright (c) 2013-2022, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@ -403,20 +403,10 @@ namespace client
if (!addr) if (!addr)
return false; return false;
auto pos = jump.find(".b32.i2p"); i2p::data::IdentityEx ident;
if (pos != std::string::npos) if (ident.FromBase64 (jump) && ident.GetIdentHash () == addr->identHash)
{ return true;
i2p::data::IdentHash identHash;
if (identHash.FromBase32(jump.substr (0, pos)) && identHash == addr->identHash)
return true;
}
else
{
i2p::data::IdentityEx ident;
if (ident.FromBase64 (jump) && ident.GetIdentHash () == addr->identHash)
return true;
}
return false; return false;
} }

@ -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 * This file is part of Purple i2pd project and licensed under BSD3
* *
@ -16,7 +16,6 @@
#include "Identity.h" #include "Identity.h"
#include "util.h" #include "util.h"
#include "ClientContext.h" #include "ClientContext.h"
#include "HTTPProxy.h"
#include "SOCKS.h" #include "SOCKS.h"
#include "MatchedDestination.h" #include "MatchedDestination.h"
@ -776,7 +775,7 @@ namespace client
address = "127.0.0.1"; address = "127.0.0.1";
} }
auto localAddress = boost::asio::ip::address::from_string(address); auto localAddress = boost::asio::ip::address::from_string(address);
auto serverTunnel = std::make_shared<I2PUDPServerTunnel>(name, localDestination, localAddress, endpoint, inPort, gzip); auto serverTunnel = std::make_shared<I2PUDPServerTunnel>(name, localDestination, localAddress, endpoint, port, gzip);
if(!isUniqueLocal) if(!isUniqueLocal)
{ {
LogPrint(eLogInfo, "Clients: Disabling loopback address mapping"); LogPrint(eLogInfo, "Clients: Disabling loopback address mapping");

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2024, The PurpleI2P Project * Copyright (c) 2013-2022, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@ -15,6 +15,8 @@
#include <boost/asio.hpp> #include <boost/asio.hpp>
#include "Destination.h" #include "Destination.h"
#include "I2PService.h" #include "I2PService.h"
#include "HTTPProxy.h"
#include "SOCKS.h"
#include "I2PTunnel.h" #include "I2PTunnel.h"
#include "UDPTunnel.h" #include "UDPTunnel.h"
#include "SAM.h" #include "SAM.h"
@ -139,7 +141,8 @@ namespace client
AddressBook m_AddressBook; AddressBook m_AddressBook;
I2PService * m_HttpProxy, * m_SocksProxy; i2p::proxy::HTTPProxy * m_HttpProxy;
i2p::proxy::SOCKSProxy * m_SocksProxy;
std::map<boost::asio::ip::tcp::endpoint, std::shared_ptr<I2PService> > m_ClientTunnels; // local endpoint -> tunnel std::map<boost::asio::ip::tcp::endpoint, std::shared_ptr<I2PService> > m_ClientTunnels; // local endpoint -> tunnel
std::map<std::pair<i2p::data::IdentHash, int>, std::shared_ptr<I2PServerTunnel> > m_ServerTunnels; // <destination,port> -> tunnel std::map<std::pair<i2p::data::IdentHash, int>, std::shared_ptr<I2PServerTunnel> > m_ServerTunnels; // <destination,port> -> tunnel
@ -164,8 +167,8 @@ namespace client
const decltype(m_ServerTunnels)& GetServerTunnels () const { return m_ServerTunnels; }; const decltype(m_ServerTunnels)& GetServerTunnels () const { return m_ServerTunnels; };
const decltype(m_ClientForwards)& GetClientForwards () const { return m_ClientForwards; } const decltype(m_ClientForwards)& GetClientForwards () const { return m_ClientForwards; }
const decltype(m_ServerForwards)& GetServerForwards () const { return m_ServerForwards; } const decltype(m_ServerForwards)& GetServerForwards () const { return m_ServerForwards; }
const I2PService * GetHttpProxy () const { return m_HttpProxy; } const i2p::proxy::HTTPProxy * GetHttpProxy () const { return m_HttpProxy; }
const I2PService * GetSocksProxy () const { return m_SocksProxy; } const i2p::proxy::SOCKSProxy * GetSocksProxy () const { return m_SocksProxy; }
}; };
extern ClientContext context; extern ClientContext context;

@ -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 * This file is part of Purple i2pd project and licensed under BSD3
* *
@ -29,7 +29,6 @@
#include "Config.h" #include "Config.h"
#include "HTTP.h" #include "HTTP.h"
#include "I18N.h" #include "I18N.h"
#include "Socks5.h"
namespace i2p { namespace i2p {
namespace proxy { namespace proxy {
@ -76,9 +75,8 @@ namespace proxy {
void HandleSockRecv(const boost::system::error_code & ecode, std::size_t bytes_transfered); void HandleSockRecv(const boost::system::error_code & ecode, std::size_t bytes_transfered);
void Terminate(); void Terminate();
void AsyncSockRead(); void AsyncSockRead();
static bool ExtractAddressHelper(i2p::http::URL& url, std::string& jump, bool& confirm); bool ExtractAddressHelper(i2p::http::URL & url, std::string & b64, bool & confirm);
static bool VerifyAddressHelper (const std::string& jump); void SanitizeHTTPRequest(i2p::http::HTTPReq & req);
static void SanitizeHTTPRequest(i2p::http::HTTPReq& req);
void SentHTTPFailed(const boost::system::error_code & ecode); void SentHTTPFailed(const boost::system::error_code & ecode);
void HandleStreamRequestComplete (std::shared_ptr<i2p::stream::Stream> stream); void HandleStreamRequestComplete (std::shared_ptr<i2p::stream::Stream> stream);
/* error helpers */ /* error helpers */
@ -94,6 +92,9 @@ namespace proxy {
void HTTPConnect(const std::string & host, uint16_t port); void HTTPConnect(const std::string & host, uint16_t port);
void HandleHTTPConnectStreamRequestComplete(std::shared_ptr<i2p::stream::Stream> stream); void HandleHTTPConnectStreamRequestComplete(std::shared_ptr<i2p::stream::Stream> stream);
void HandleSocksProxySendHandshake(const boost::system::error_code & ec, std::size_t bytes_transfered);
void HandleSocksProxyReply(const boost::system::error_code & ec, std::size_t bytes_transfered);
typedef std::function<void(boost::asio::ip::tcp::endpoint)> ProxyResolvedHandler; typedef std::function<void(boost::asio::ip::tcp::endpoint)> ProxyResolvedHandler;
void HandleUpstreamProxyResolved(const boost::system::error_code & ecode, boost::asio::ip::tcp::resolver::iterator itr, ProxyResolvedHandler handler); void HandleUpstreamProxyResolved(const boost::system::error_code & ecode, boost::asio::ip::tcp::resolver::iterator itr, ProxyResolvedHandler handler);
@ -107,10 +108,11 @@ namespace proxy {
std::shared_ptr<boost::asio::ip::tcp::socket> m_sock; std::shared_ptr<boost::asio::ip::tcp::socket> m_sock;
std::shared_ptr<boost::asio::ip::tcp::socket> m_proxysock; std::shared_ptr<boost::asio::ip::tcp::socket> m_proxysock;
boost::asio::ip::tcp::resolver m_proxy_resolver; boost::asio::ip::tcp::resolver m_proxy_resolver;
std::string m_OutproxyUrl, m_Response; std::string m_OutproxyUrl;
bool m_Addresshelper; bool m_Addresshelper;
i2p::http::URL m_ProxyURL; i2p::http::URL m_ProxyURL;
i2p::http::URL m_RequestURL; i2p::http::URL m_RequestURL;
uint8_t m_socks_buf[255+8]; // for socks request/response
int m_req_len; int m_req_len;
i2p::http::URL m_ClientRequestURL; i2p::http::URL m_ClientRequestURL;
i2p::http::HTTPReq m_ClientRequest; i2p::http::HTTPReq m_ClientRequest;
@ -203,8 +205,8 @@ namespace proxy {
<< "<body>" << content << "</body>\r\n" << "<body>" << content << "</body>\r\n"
<< "</html>\r\n"; << "</html>\r\n";
res.body = ss.str(); res.body = ss.str();
m_Response = res.to_string(); std::string response = res.to_string();
boost::asio::async_write(*m_sock, boost::asio::buffer(m_Response), boost::asio::transfer_all(), boost::asio::async_write(*m_sock, boost::asio::buffer(response), boost::asio::transfer_all(),
std::bind(&HTTPReqHandler::SentHTTPFailed, shared_from_this(), std::placeholders::_1)); std::bind(&HTTPReqHandler::SentHTTPFailed, shared_from_this(), std::placeholders::_1));
} }
@ -214,12 +216,12 @@ namespace proxy {
res.code = 302; res.code = 302;
res.add_header("Location", address); res.add_header("Location", address);
res.add_header("Connection", "close"); res.add_header("Connection", "close");
m_Response = res.to_string(); std::string response = res.to_string();
boost::asio::async_write(*m_sock, boost::asio::buffer(m_Response), boost::asio::transfer_all(), boost::asio::async_write(*m_sock, boost::asio::buffer(response), boost::asio::transfer_all(),
std::bind(&HTTPReqHandler::SentHTTPFailed, shared_from_this(), std::placeholders::_1)); std::bind(&HTTPReqHandler::SentHTTPFailed, shared_from_this(), std::placeholders::_1));
} }
bool HTTPReqHandler::ExtractAddressHelper(i2p::http::URL& url, std::string& jump, bool& confirm) bool HTTPReqHandler::ExtractAddressHelper(i2p::http::URL & url, std::string & b64, bool & confirm)
{ {
confirm = false; confirm = false;
const char *param = "i2paddresshelper="; const char *param = "i2paddresshelper=";
@ -235,13 +237,8 @@ namespace proxy {
std::string value = params["i2paddresshelper"]; std::string value = params["i2paddresshelper"];
len += value.length(); len += value.length();
jump = i2p::http::UrlDecode(value); b64 = i2p::http::UrlDecode(value);
if (!VerifyAddressHelper (jump))
{
LogPrint (eLogError, "HTTPProxy: Malformed jump link ", jump);
return false;
}
// if we need update exists, request formed with update param // if we need update exists, request formed with update param
if (params["update"] == "true") if (params["update"] == "true")
{ {
@ -272,35 +269,7 @@ namespace proxy {
return true; return true;
} }
bool HTTPReqHandler::VerifyAddressHelper (const std::string& jump) void HTTPReqHandler::SanitizeHTTPRequest(i2p::http::HTTPReq & req)
{
auto pos = jump.find(".b32.i2p");
if (pos != std::string::npos)
{
auto b32 = jump.substr (0, pos);
for (auto& ch: b32)
if (!i2p::data::IsBase32(ch)) return false;
return true;
}
else
{
bool padding = false;
for (auto& ch: jump)
{
if (ch == '=')
padding = true;
else
{
if (padding) return false; // other chars after padding
if (!i2p::data::IsBase64(ch)) return false;
}
}
return true;
}
return false;
}
void HTTPReqHandler::SanitizeHTTPRequest(i2p::http::HTTPReq& req)
{ {
/* drop common headers */ /* drop common headers */
req.RemoveHeader("Via"); req.RemoveHeader("Via");
@ -609,36 +578,49 @@ namespace proxy {
void HTTPReqHandler::HandleUpstreamSocksProxyConnect(const boost::system::error_code & ec) void HTTPReqHandler::HandleUpstreamSocksProxyConnect(const boost::system::error_code & ec)
{ {
if(!ec) if(!ec) {
{ if(m_RequestURL.host.size() > 255) {
if(m_RequestURL.host.size() > 255)
{
GenericProxyError(tr("Hostname is too long"), m_RequestURL.host); GenericProxyError(tr("Hostname is too long"), m_RequestURL.host);
return; return;
} }
uint16_t port = m_RequestURL.port; uint16_t port = m_RequestURL.port;
if(!port) port = 80; if(!port) port = 80;
LogPrint(eLogDebug, "HTTPProxy: Connected to SOCKS upstream"); LogPrint(eLogDebug, "HTTPProxy: Connected to SOCKS upstream");
std::string host = m_RequestURL.host; std::string host = m_RequestURL.host;
auto s = shared_from_this (); std::size_t reqsize = 0;
i2p::transport::Socks5Handshake (*m_proxysock, std::make_pair(host, port), m_socks_buf[0] = '\x04';
[s](const boost::system::error_code& ec) m_socks_buf[1] = 1;
{ htobe16buf(m_socks_buf+2, port);
if (!ec) m_socks_buf[4] = 0;
s->SocksProxySuccess(); m_socks_buf[5] = 0;
else m_socks_buf[6] = 0;
s->GenericProxyError(tr("SOCKS proxy error"), ec.message ()); m_socks_buf[7] = 1;
}); // user id
m_socks_buf[8] = 'i';
} m_socks_buf[9] = '2';
else m_socks_buf[10] = 'p';
GenericProxyError(tr("Cannot connect to upstream SOCKS proxy"), ec.message()); m_socks_buf[11] = 'd';
m_socks_buf[12] = 0;
reqsize += 13;
memcpy(m_socks_buf+ reqsize, host.c_str(), host.size());
reqsize += host.size();
m_socks_buf[++reqsize] = 0;
boost::asio::async_write(*m_proxysock, boost::asio::buffer(m_socks_buf, reqsize), boost::asio::transfer_all(), std::bind(&HTTPReqHandler::HandleSocksProxySendHandshake, this, std::placeholders::_1, std::placeholders::_2));
} else GenericProxyError(tr("Cannot connect to upstream SOCKS proxy"), ec.message());
}
void HTTPReqHandler::HandleSocksProxySendHandshake(const boost::system::error_code & ec, std::size_t bytes_transferred)
{
LogPrint(eLogDebug, "HTTPProxy: Upstream SOCKS handshake sent");
if(ec) GenericProxyError(tr("Cannot negotiate with SOCKS proxy"), ec.message());
else m_proxysock->async_read_some(boost::asio::buffer(m_socks_buf, 8), std::bind(&HTTPReqHandler::HandleSocksProxyReply, this, std::placeholders::_1, std::placeholders::_2));
} }
void HTTPReqHandler::HandoverToUpstreamProxy() void HTTPReqHandler::HandoverToUpstreamProxy()
{ {
LogPrint(eLogDebug, "HTTPProxy: Handover to SOCKS proxy"); LogPrint(eLogDebug, "HTTPProxy: Handover to SOCKS proxy");
auto connection = CreateSocketsPipe (GetOwner(), m_proxysock, m_sock); auto connection = std::make_shared<i2p::client::TCPIPPipe>(GetOwner(), m_proxysock, m_sock);
m_sock = nullptr; m_sock = nullptr;
m_proxysock = nullptr; m_proxysock = nullptr;
GetOwner()->AddHandler(connection); GetOwner()->AddHandler(connection);
@ -698,6 +680,24 @@ namespace proxy {
} }
} }
void HTTPReqHandler::HandleSocksProxyReply(const boost::system::error_code & ec, std::size_t bytes_transferred)
{
if(!ec)
{
if(m_socks_buf[1] == 90) {
// success
SocksProxySuccess();
} else {
std::stringstream ss;
ss << "error code: ";
ss << (int) m_socks_buf[1];
std::string msg = ss.str();
GenericProxyError(tr("SOCKS proxy error"), msg);
}
}
else GenericProxyError(tr("No reply from SOCKS proxy"), ec.message());
}
void HTTPReqHandler::HandleUpstreamHTTPProxyConnect(const boost::system::error_code & ec) void HTTPReqHandler::HandleUpstreamHTTPProxyConnect(const boost::system::error_code & ec)
{ {
if(!ec) { if(!ec) {

@ -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 * This file is part of Purple i2pd project and licensed under BSD3
* *
@ -147,5 +147,196 @@ namespace client
m_LocalDestination->CreateStream (streamRequestComplete, address->blindedPublicKey, port); m_LocalDestination->CreateStream (streamRequestComplete, address->blindedPublicKey, port);
} }
} }
TCPIPPipe::TCPIPPipe(I2PService * owner, std::shared_ptr<boost::asio::ip::tcp::socket> upstream, std::shared_ptr<boost::asio::ip::tcp::socket> downstream) : I2PServiceHandler(owner), m_up(upstream), m_down(downstream)
{
boost::asio::socket_base::receive_buffer_size option(TCP_IP_PIPE_BUFFER_SIZE);
upstream->set_option(option);
downstream->set_option(option);
}
TCPIPPipe::~TCPIPPipe()
{
Terminate();
}
void TCPIPPipe::Start()
{
AsyncReceiveUpstream();
AsyncReceiveDownstream();
}
void TCPIPPipe::Terminate()
{
if(Kill()) return;
if (m_up)
{
if (m_up->is_open())
m_up->close();
m_up = nullptr;
}
if (m_down)
{
if (m_down->is_open())
m_down->close();
m_down = nullptr;
}
Done(shared_from_this());
}
void TCPIPPipe::AsyncReceiveUpstream()
{
if (m_up)
{
m_up->async_read_some(boost::asio::buffer(m_upstream_to_down_buf, TCP_IP_PIPE_BUFFER_SIZE),
std::bind(&TCPIPPipe::HandleUpstreamReceived, shared_from_this(),
std::placeholders::_1, std::placeholders::_2));
}
else
LogPrint(eLogError, "TCPIPPipe: Upstream receive: No socket");
}
void TCPIPPipe::AsyncReceiveDownstream()
{
if (m_down) {
m_down->async_read_some(boost::asio::buffer(m_downstream_to_up_buf, TCP_IP_PIPE_BUFFER_SIZE),
std::bind(&TCPIPPipe::HandleDownstreamReceived, shared_from_this(),
std::placeholders::_1, std::placeholders::_2));
}
else
LogPrint(eLogError, "TCPIPPipe: Downstream receive: No socket");
}
void TCPIPPipe::UpstreamWrite(size_t len)
{
if (m_up)
{
LogPrint(eLogDebug, "TCPIPPipe: Upstream: ", (int) len, " bytes written");
boost::asio::async_write(*m_up, boost::asio::buffer(m_upstream_buf, len),
boost::asio::transfer_all(),
std::bind(&TCPIPPipe::HandleUpstreamWrite,
shared_from_this(),
std::placeholders::_1));
}
else
LogPrint(eLogError, "TCPIPPipe: Upstream write: no socket");
}
void TCPIPPipe::DownstreamWrite(size_t len)
{
if (m_down)
{
LogPrint(eLogDebug, "TCPIPPipe: Downstream: ", (int) len, " bytes written");
boost::asio::async_write(*m_down, boost::asio::buffer(m_downstream_buf, len),
boost::asio::transfer_all(),
std::bind(&TCPIPPipe::HandleDownstreamWrite,
shared_from_this(),
std::placeholders::_1));
}
else
LogPrint(eLogError, "TCPIPPipe: Downstream write: No socket");
}
void TCPIPPipe::HandleDownstreamReceived(const boost::system::error_code & ecode, std::size_t bytes_transfered)
{
LogPrint(eLogDebug, "TCPIPPipe: Downstream: ", (int) bytes_transfered, " bytes received");
if (ecode)
{
LogPrint(eLogError, "TCPIPPipe: Downstream read error:" , ecode.message());
if (ecode != boost::asio::error::operation_aborted)
Terminate();
} else {
if (bytes_transfered > 0 )
memcpy(m_upstream_buf, m_downstream_to_up_buf, bytes_transfered);
UpstreamWrite(bytes_transfered);
}
}
void TCPIPPipe::HandleDownstreamWrite(const boost::system::error_code & ecode) {
if (ecode)
{
LogPrint(eLogError, "TCPIPPipe: Downstream write error:" , ecode.message());
if (ecode != boost::asio::error::operation_aborted)
Terminate();
}
else
AsyncReceiveUpstream();
}
void TCPIPPipe::HandleUpstreamWrite(const boost::system::error_code & ecode) {
if (ecode)
{
LogPrint(eLogError, "TCPIPPipe: Upstream write error:" , ecode.message());
if (ecode != boost::asio::error::operation_aborted)
Terminate();
}
else
AsyncReceiveDownstream();
}
void TCPIPPipe::HandleUpstreamReceived(const boost::system::error_code & ecode, std::size_t bytes_transfered)
{
LogPrint(eLogDebug, "TCPIPPipe: Upstream ", (int)bytes_transfered, " bytes received");
if (ecode)
{
LogPrint(eLogError, "TCPIPPipe: Upstream read error:" , ecode.message());
if (ecode != boost::asio::error::operation_aborted)
Terminate();
} else {
if (bytes_transfered > 0 )
memcpy(m_downstream_buf, m_upstream_to_down_buf, bytes_transfered);
DownstreamWrite(bytes_transfered);
}
}
void TCPIPAcceptor::Start ()
{
m_Acceptor.reset (new boost::asio::ip::tcp::acceptor (GetService (), m_LocalEndpoint));
// update the local end point in case port has been set zero and got updated now
m_LocalEndpoint = m_Acceptor->local_endpoint();
m_Acceptor->listen ();
Accept ();
}
void TCPIPAcceptor::Stop ()
{
if (m_Acceptor)
{
m_Acceptor->close();
m_Acceptor.reset (nullptr);
}
m_Timer.cancel ();
ClearHandlers();
}
void TCPIPAcceptor::Accept ()
{
auto newSocket = std::make_shared<boost::asio::ip::tcp::socket> (GetService ());
m_Acceptor->async_accept (*newSocket, std::bind (&TCPIPAcceptor::HandleAccept, this,
std::placeholders::_1, newSocket));
}
void TCPIPAcceptor::HandleAccept (const boost::system::error_code& ecode, std::shared_ptr<boost::asio::ip::tcp::socket> socket)
{
if (!ecode)
{
LogPrint(eLogDebug, "I2PService: ", GetName(), " accepted");
auto handler = CreateHandler(socket);
if (handler)
{
AddHandler(handler);
handler->Handle();
}
else
socket->close();
Accept();
}
else
{
if (ecode != boost::asio::error::operation_aborted)
LogPrint (eLogError, "I2PService: ", GetName(), " closing socket on accept because: ", ecode.message ());
}
}
} }
} }

@ -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 * This file is part of Purple i2pd project and licensed under BSD3
* *
@ -99,7 +99,6 @@ namespace client
virtual ~I2PServiceHandler() { } virtual ~I2PServiceHandler() { }
//If you override this make sure you call it from the children //If you override this make sure you call it from the children
virtual void Handle() {}; //Start handling the socket virtual void Handle() {}; //Start handling the socket
virtual void Start () {};
void Terminate () { Kill (); }; void Terminate () { Kill (); };
@ -120,171 +119,72 @@ namespace client
std::atomic<bool> m_Dead; //To avoid cleaning up multiple times std::atomic<bool> m_Dead; //To avoid cleaning up multiple times
}; };
const size_t SOCKETS_PIPE_BUFFER_SIZE = 8192 * 8; const size_t TCP_IP_PIPE_BUFFER_SIZE = 8192 * 8;
// bidirectional pipe for 2 stream sockets // bidirectional pipe for 2 tcp/ip sockets
template<typename SocketUpstream, typename SocketDownstream> class TCPIPPipe: public I2PServiceHandler, public std::enable_shared_from_this<TCPIPPipe>
class SocketsPipe: public I2PServiceHandler,
public std::enable_shared_from_this<SocketsPipe<SocketUpstream, SocketDownstream> >
{ {
public: public:
SocketsPipe(I2PService * owner, std::shared_ptr<SocketUpstream> upstream, std::shared_ptr<SocketDownstream> downstream): TCPIPPipe(I2PService * owner, std::shared_ptr<boost::asio::ip::tcp::socket> upstream, std::shared_ptr<boost::asio::ip::tcp::socket> downstream);
I2PServiceHandler(owner), m_up(upstream), m_down(downstream) ~TCPIPPipe();
{ void Start();
boost::asio::socket_base::receive_buffer_size option(SOCKETS_PIPE_BUFFER_SIZE);
upstream->set_option(option);
downstream->set_option(option);
}
~SocketsPipe() { Terminate(); }
void Start() override
{
Transfer (m_up, m_down, m_upstream_to_down_buf, SOCKETS_PIPE_BUFFER_SIZE); // receive from upstream
Transfer (m_down, m_up, m_downstream_to_up_buf, SOCKETS_PIPE_BUFFER_SIZE); // receive from upstream
}
private: protected:
void Terminate();
void AsyncReceiveUpstream();
void AsyncReceiveDownstream();
void HandleUpstreamReceived(const boost::system::error_code & ecode, std::size_t bytes_transferred);
void HandleDownstreamReceived(const boost::system::error_code & ecode, std::size_t bytes_transferred);
void HandleUpstreamWrite(const boost::system::error_code & ecode);
void HandleDownstreamWrite(const boost::system::error_code & ecode);
void UpstreamWrite(size_t len);
void DownstreamWrite(size_t len);
void Terminate()
{
if(Kill()) return;
if (m_up)
{
if (m_up->is_open())
m_up->close();
m_up = nullptr;
}
if (m_down)
{
if (m_down->is_open())
m_down->close();
m_down = nullptr;
}
Done(SocketsPipe<SocketUpstream, SocketDownstream>::shared_from_this());
}
template<typename From, typename To>
void Transfer (std::shared_ptr<From> from, std::shared_ptr<To> to, uint8_t * buf, size_t len)
{
if (!from || !to || !buf) return;
auto s = SocketsPipe<SocketUpstream, SocketDownstream>::shared_from_this ();
from->async_read_some(boost::asio::buffer(buf, len),
[from, to, s, buf, len](const boost::system::error_code& ecode, std::size_t transferred)
{
if (ecode == boost::asio::error::operation_aborted) return;
if (!ecode)
{
boost::asio::async_write(*to, boost::asio::buffer(buf, transferred), boost::asio::transfer_all(),
[from, to, s, buf, len](const boost::system::error_code& ecode, std::size_t transferred)
{
(void) transferred;
if (ecode == boost::asio::error::operation_aborted) return;
if (!ecode)
s->Transfer (from, to, buf, len);
else
{
LogPrint(eLogWarning, "SocketsPipe: Write error:" , ecode.message());
s->Terminate();
}
});
}
else
{
LogPrint(eLogWarning, "SocketsPipe: Read error:" , ecode.message());
s->Terminate();
}
});
}
private: private:
uint8_t m_upstream_to_down_buf[SOCKETS_PIPE_BUFFER_SIZE], m_downstream_to_up_buf[SOCKETS_PIPE_BUFFER_SIZE]; uint8_t m_upstream_to_down_buf[TCP_IP_PIPE_BUFFER_SIZE], m_downstream_to_up_buf[TCP_IP_PIPE_BUFFER_SIZE];
std::shared_ptr<SocketUpstream> m_up; uint8_t m_upstream_buf[TCP_IP_PIPE_BUFFER_SIZE], m_downstream_buf[TCP_IP_PIPE_BUFFER_SIZE];
std::shared_ptr<SocketDownstream> m_down; std::shared_ptr<boost::asio::ip::tcp::socket> m_up, m_down;
}; };
template<typename SocketUpstream, typename SocketDownstream> /* TODO: support IPv6 too */
std::shared_ptr<I2PServiceHandler> CreateSocketsPipe (I2PService * owner, std::shared_ptr<SocketUpstream> upstream, std::shared_ptr<SocketDownstream> downstream) //This is a service that listens for connections on the IP network and interacts with I2P
{ class TCPIPAcceptor: public I2PService
return std::make_shared<SocketsPipe<SocketUpstream, SocketDownstream> >(owner, upstream, downstream);
}
//This is a service that listens for connections on the IP network or local socket and interacts with I2P
template<typename Protocol>
class ServiceAcceptor: public I2PService
{ {
public: public:
ServiceAcceptor (const typename Protocol::endpoint& localEndpoint, std::shared_ptr<ClientDestination> localDestination = nullptr) : TCPIPAcceptor (const std::string& address, uint16_t port, std::shared_ptr<ClientDestination> localDestination = nullptr) :
I2PService(localDestination), m_LocalEndpoint (localEndpoint) {} I2PService(localDestination),
m_LocalEndpoint (boost::asio::ip::address::from_string(address), port),
virtual ~ServiceAcceptor () { Stop(); } m_Timer (GetService ()) {}
void Start () override TCPIPAcceptor (const std::string& address, uint16_t port, i2p::data::SigningKeyType kt) :
{ I2PService(kt),
m_Acceptor.reset (new typename Protocol::acceptor (GetService (), m_LocalEndpoint)); m_LocalEndpoint (boost::asio::ip::address::from_string(address), port),
// update the local end point in case port has been set zero and got updated now m_Timer (GetService ()) {}
m_LocalEndpoint = m_Acceptor->local_endpoint(); virtual ~TCPIPAcceptor () { TCPIPAcceptor::Stop(); }
m_Acceptor->listen (); //If you override this make sure you call it from the children
Accept (); void Start ();
} //If you override this make sure you call it from the children
void Stop () override void Stop ();
{
if (m_Acceptor)
{
m_Acceptor->close();
m_Acceptor.reset (nullptr);
}
ClearHandlers();
}
const typename Protocol::endpoint& GetLocalEndpoint () const { return m_LocalEndpoint; };
const char* GetName() override { return "Generic TCP/IP accepting daemon"; } const boost::asio::ip::tcp::endpoint& GetLocalEndpoint () const { return m_LocalEndpoint; };
virtual const char* GetName() { return "Generic TCP/IP accepting daemon"; }
protected: protected:
virtual std::shared_ptr<I2PServiceHandler> CreateHandler(std::shared_ptr<typename Protocol::socket> socket) = 0; virtual std::shared_ptr<I2PServiceHandler> CreateHandler(std::shared_ptr<boost::asio::ip::tcp::socket> socket) = 0;
private: private:
void Accept() void Accept();
{ void HandleAccept(const boost::system::error_code& ecode, std::shared_ptr<boost::asio::ip::tcp::socket> socket);
auto newSocket = std::make_shared<typename Protocol::socket> (GetService ()); boost::asio::ip::tcp::endpoint m_LocalEndpoint;
m_Acceptor->async_accept (*newSocket, std::unique_ptr<boost::asio::ip::tcp::acceptor> m_Acceptor;
[newSocket, this](const boost::system::error_code& ecode) boost::asio::deadline_timer m_Timer;
{
if (ecode == boost::asio::error::operation_aborted) return;
if (!ecode)
{
LogPrint(eLogDebug, "ServiceAcceptor: ", GetName(), " accepted");
auto handler = CreateHandler(newSocket);
if (handler)
{
AddHandler(handler);
handler->Handle();
}
else
newSocket->close();
Accept();
}
else
LogPrint (eLogError, "ServiceAcceptor: ", GetName(), " closing socket on accept because: ", ecode.message ());
});
}
private:
typename Protocol::endpoint m_LocalEndpoint;
std::unique_ptr<typename Protocol::acceptor> m_Acceptor;
}; };
class TCPIPAcceptor: public ServiceAcceptor<boost::asio::ip::tcp>
{
public:
TCPIPAcceptor (const std::string& address, uint16_t port, std::shared_ptr<ClientDestination> localDestination = nullptr) :
ServiceAcceptor (boost::asio::ip::tcp::endpoint (boost::asio::ip::address::from_string(address), port), localDestination) {}
};
} }
} }

@ -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 * This file is part of Purple i2pd project and licensed under BSD3
* *
@ -360,7 +360,7 @@ namespace client
if (type == eSAMSessionTypeUnknown) if (type == eSAMSessionTypeUnknown)
{ {
// unknown style // unknown style
SendSessionI2PError("Unknown STYLE"); SendI2PError("Unknown STYLE");
return; return;
} }
@ -375,14 +375,14 @@ namespace client
if (e) if (e)
{ {
// not an ip address // not an ip address
SendSessionI2PError("Invalid IP Address in HOST"); SendI2PError("Invalid IP Address in HOST");
return; return;
} }
auto port = std::stoi(params[SAM_PARAM_PORT]); auto port = std::stoi(params[SAM_PARAM_PORT]);
if (port == -1) if (port == -1)
{ {
SendSessionI2PError("Invalid port"); SendI2PError("Invalid port");
return; return;
} }
forward = std::make_shared<boost::asio::ip::udp::endpoint>(addr, port); forward = std::make_shared<boost::asio::ip::udp::endpoint>(addr, port);
@ -415,17 +415,12 @@ namespace client
{ {
session->UDPEndpoint = forward; session->UDPEndpoint = forward;
auto dest = session->GetLocalDestination ()->CreateDatagramDestination (); auto dest = session->GetLocalDestination ()->CreateDatagramDestination ();
auto port = std::stoi(params[SAM_PARAM_PORT]);
if (type == eSAMSessionTypeDatagram) if (type == eSAMSessionTypeDatagram)
dest->SetReceiver (std::bind (&SAMSocket::HandleI2PDatagramReceive, shared_from_this (), dest->SetReceiver (std::bind (&SAMSocket::HandleI2PDatagramReceive, shared_from_this (),
std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5), std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5));
port
);
else // raw else // raw
dest->SetRawReceiver (std::bind (&SAMSocket::HandleI2PRawDatagramReceive, shared_from_this (), dest->SetRawReceiver (std::bind (&SAMSocket::HandleI2PRawDatagramReceive, shared_from_this (),
std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4), std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4));
port
);
} }
if (session->GetLocalDestination ()->IsReady ()) if (session->GetLocalDestination ()->IsReady ())
@ -489,7 +484,7 @@ namespace client
LogPrint (eLogDebug, "SAM: Stream connect: ", buf); LogPrint (eLogDebug, "SAM: Stream connect: ", buf);
if ( m_SocketType != eSAMSocketTypeUnknown) if ( m_SocketType != eSAMSocketTypeUnknown)
{ {
SendSessionI2PError ("Socket already in use"); SendI2PError ("Socket already in use");
return; return;
} }
std::map<std::string, std::string> params; std::map<std::string, std::string> params;
@ -528,20 +523,15 @@ namespace client
{ {
if (addr->IsIdentHash ()) if (addr->IsIdentHash ())
{ {
if (session->GetLocalDestination ()->GetIdentHash () != addr->identHash) auto leaseSet = session->GetLocalDestination ()->FindLeaseSet(addr->identHash);
if (leaseSet)
Connect(leaseSet, session);
else
{ {
auto leaseSet = session->GetLocalDestination ()->FindLeaseSet(addr->identHash); session->GetLocalDestination ()->RequestDestination(addr->identHash,
if (leaseSet) std::bind(&SAMSocket::HandleConnectLeaseSetRequestComplete,
Connect(leaseSet, session); shared_from_this(), std::placeholders::_1));
else
{
session->GetLocalDestination ()->RequestDestination(addr->identHash,
std::bind(&SAMSocket::HandleConnectLeaseSetRequestComplete,
shared_from_this(), std::placeholders::_1));
}
} }
else
SendStreamCantReachPeer ("Can't connect to myself");
} }
else // B33 else // B33
session->GetLocalDestination ()->RequestDestinationWithEncryptedLeaseSet (addr->blindedPublicKey, session->GetLocalDestination ()->RequestDestinationWithEncryptedLeaseSet (addr->blindedPublicKey,
@ -560,22 +550,17 @@ namespace client
if (!session) session = m_Owner.FindSession(m_ID); if (!session) session = m_Owner.FindSession(m_ID);
if (session) if (session)
{ {
if (session->GetLocalDestination ()->SupportsEncryptionType (remote->GetEncryptionType ())) m_SocketType = eSAMSocketTypeStream;
m_Stream = session->GetLocalDestination ()->CreateStream (remote);
if (m_Stream)
{ {
m_SocketType = eSAMSocketTypeStream; m_Stream->Send ((uint8_t *)m_Buffer, m_BufferOffset); // connect and send
m_Stream = session->GetLocalDestination ()->CreateStream (remote); m_BufferOffset = 0;
if (m_Stream) I2PReceive ();
{ SendMessageReply (SAM_STREAM_STATUS_OK, strlen(SAM_STREAM_STATUS_OK), false);
m_Stream->Send ((uint8_t *)m_Buffer, m_BufferOffset); // connect and send
m_BufferOffset = 0;
I2PReceive ();
SendMessageReply (SAM_STREAM_STATUS_OK, strlen(SAM_STREAM_STATUS_OK), false);
}
else
SendMessageReply (SAM_STREAM_STATUS_INVALID_ID, strlen(SAM_STREAM_STATUS_INVALID_ID), true);
} }
else else
SendStreamCantReachPeer ("Incompatible crypto"); SendMessageReply (SAM_STREAM_STATUS_INVALID_ID, strlen(SAM_STREAM_STATUS_INVALID_ID), true);
} }
else else
SendMessageReply (SAM_STREAM_STATUS_INVALID_ID, strlen(SAM_STREAM_STATUS_INVALID_ID), true); SendMessageReply (SAM_STREAM_STATUS_INVALID_ID, strlen(SAM_STREAM_STATUS_INVALID_ID), true);
@ -588,7 +573,7 @@ namespace client
else else
{ {
LogPrint (eLogError, "SAM: Destination to connect not found"); LogPrint (eLogError, "SAM: Destination to connect not found");
SendStreamCantReachPeer ("LeaseSet not found"); SendMessageReply (SAM_STREAM_STATUS_CANT_REACH_PEER, strlen(SAM_STREAM_STATUS_CANT_REACH_PEER), true);
} }
} }
@ -597,7 +582,7 @@ namespace client
LogPrint (eLogDebug, "SAM: Stream accept: ", buf); LogPrint (eLogDebug, "SAM: Stream accept: ", buf);
if ( m_SocketType != eSAMSocketTypeUnknown) if ( m_SocketType != eSAMSocketTypeUnknown)
{ {
SendSessionI2PError ("Socket already in use"); SendI2PError ("Socket already in use");
return; return;
} }
std::map<std::string, std::string> params; std::map<std::string, std::string> params;
@ -613,31 +598,9 @@ namespace client
if (!session->GetLocalDestination ()->IsAcceptingStreams ()) if (!session->GetLocalDestination ()->IsAcceptingStreams ())
{ {
m_IsAccepting = true; m_IsAccepting = true;
SendMessageReply (SAM_STREAM_STATUS_OK, strlen(SAM_STREAM_STATUS_OK), false);
session->GetLocalDestination ()->AcceptOnce (std::bind (&SAMSocket::HandleI2PAccept, shared_from_this (), std::placeholders::_1)); session->GetLocalDestination ()->AcceptOnce (std::bind (&SAMSocket::HandleI2PAccept, shared_from_this (), std::placeholders::_1));
} }
else SendMessageReply (SAM_STREAM_STATUS_OK, strlen(SAM_STREAM_STATUS_OK), false);
{
auto ts = i2p::util::GetSecondsSinceEpoch ();
while (!session->acceptQueue.empty () && session->acceptQueue.front ().second + SAM_SESSION_MAX_ACCEPT_INTERVAL > ts)
{
auto socket = session->acceptQueue.front ().first;
session->acceptQueue.pop_front ();
if (socket)
m_Owner.GetService ().post (std::bind(&SAMSocket::TerminateClose, socket));
}
if (session->acceptQueue.size () < SAM_SESSION_MAX_ACCEPT_QUEUE_SIZE)
{
// already accepting, queue up
SendMessageReply (SAM_STREAM_STATUS_OK, strlen(SAM_STREAM_STATUS_OK), false);
session->acceptQueue.push_back (std::make_pair(shared_from_this(), ts));
}
else
{
LogPrint (eLogInfo, "SAM: Session ", m_ID, " accept queue is full ", session->acceptQueue.size ());
SendStreamI2PError ("Already accepting");
}
}
} }
else else
SendMessageReply (SAM_STREAM_STATUS_INVALID_ID, strlen(SAM_STREAM_STATUS_INVALID_ID), true); SendMessageReply (SAM_STREAM_STATUS_INVALID_ID, strlen(SAM_STREAM_STATUS_INVALID_ID), true);
@ -657,26 +620,26 @@ namespace client
} }
if (session->GetLocalDestination ()->IsAcceptingStreams ()) if (session->GetLocalDestination ()->IsAcceptingStreams ())
{ {
SendSessionI2PError ("Already accepting"); SendI2PError ("Already accepting");
return; return;
} }
auto it = params.find (SAM_PARAM_PORT); auto it = params.find (SAM_PARAM_PORT);
if (it == params.end ()) if (it == params.end ())
{ {
SendSessionI2PError ("PORT is missing"); SendI2PError ("PORT is missing");
return; return;
} }
auto port = std::stoi (it->second); auto port = std::stoi (it->second);
if (port <= 0 || port >= 0xFFFF) if (port <= 0 || port >= 0xFFFF)
{ {
SendSessionI2PError ("Invalid PORT"); SendI2PError ("Invalid PORT");
return; return;
} }
boost::system::error_code ec; boost::system::error_code ec;
auto ep = m_Socket.remote_endpoint (ec); auto ep = m_Socket.remote_endpoint (ec);
if (ec) if (ec)
{ {
SendSessionI2PError ("Socket error"); SendI2PError ("Socket error");
return; return;
} }
ep.port (port); ep.port (port);
@ -828,13 +791,13 @@ namespace client
if (type == eSAMSessionTypeUnknown) if (type == eSAMSessionTypeUnknown)
{ {
// unknown style // unknown style
SendSessionI2PError("Unsupported STYLE"); SendI2PError("Unsupported STYLE");
return; return;
} }
auto fromPort = std::stoi(params[SAM_PARAM_FROM_PORT]); auto fromPort = std::stoi(params[SAM_PARAM_FROM_PORT]);
if (fromPort == -1) if (fromPort == -1)
{ {
SendSessionI2PError("Invalid from port"); SendI2PError("Invalid from port");
return; return;
} }
auto subsession = std::make_shared<SAMSubSession>(masterSession, id, type, fromPort); auto subsession = std::make_shared<SAMSubSession>(masterSession, id, type, fromPort);
@ -847,7 +810,7 @@ namespace client
SendMessageReply (SAM_SESSION_CREATE_DUPLICATED_ID, strlen(SAM_SESSION_CREATE_DUPLICATED_ID), false); SendMessageReply (SAM_SESSION_CREATE_DUPLICATED_ID, strlen(SAM_SESSION_CREATE_DUPLICATED_ID), false);
} }
else else
SendSessionI2PError ("Wrong session type"); SendI2PError ("Wrong session type");
} }
void SAMSocket::ProcessSessionRemove (char * buf, size_t len) void SAMSocket::ProcessSessionRemove (char * buf, size_t len)
@ -869,36 +832,20 @@ namespace client
SendSessionCreateReplyOk (); SendSessionCreateReplyOk ();
} }
else else
SendSessionI2PError ("Wrong session type"); SendI2PError ("Wrong session type");
} }
void SAMSocket::SendReplyWithMessage (const char * reply, const std::string & msg) void SAMSocket::SendI2PError(const std::string & msg)
{ {
LogPrint (eLogError, "SAM: I2P error: ", msg);
#ifdef _MSC_VER #ifdef _MSC_VER
size_t len = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, reply, msg.c_str()); size_t len = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_SESSION_STATUS_I2P_ERROR, msg.c_str());
#else #else
size_t len = snprintf (m_Buffer, SAM_SOCKET_BUFFER_SIZE, reply, msg.c_str()); size_t len = snprintf (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_SESSION_STATUS_I2P_ERROR, msg.c_str());
#endif #endif
SendMessageReply (m_Buffer, len, true); SendMessageReply (m_Buffer, len, true);
} }
void SAMSocket::SendSessionI2PError(const std::string & msg)
{
LogPrint (eLogError, "SAM: Session I2P error: ", msg);
SendReplyWithMessage (SAM_SESSION_STATUS_I2P_ERROR, msg);
}
void SAMSocket::SendStreamI2PError(const std::string & msg)
{
LogPrint (eLogError, "SAM: Stream I2P error: ", msg);
SendReplyWithMessage (SAM_STREAM_STATUS_I2P_ERROR, msg);
}
void SAMSocket::SendStreamCantReachPeer(const std::string & msg)
{
SendReplyWithMessage (SAM_STREAM_STATUS_CANT_REACH_PEER, msg);
}
void SAMSocket::HandleNamingLookupLeaseSetRequestComplete (std::shared_ptr<i2p::data::LeaseSet> leaseSet, std::string name) void SAMSocket::HandleNamingLookupLeaseSetRequestComplete (std::shared_ptr<i2p::data::LeaseSet> leaseSet, std::string name)
{ {
if (leaseSet) if (leaseSet)
@ -1093,27 +1040,16 @@ namespace client
m_Stream = stream; m_Stream = stream;
context.GetAddressBook ().InsertFullAddress (stream->GetRemoteIdentity ()); context.GetAddressBook ().InsertFullAddress (stream->GetRemoteIdentity ());
auto session = m_Owner.FindSession (m_ID); auto session = m_Owner.FindSession (m_ID);
if (session && !session->acceptQueue.empty ()) if (session)
{ {
// pending acceptors // find more pending acceptors
auto ts = i2p::util::GetSecondsSinceEpoch (); for (auto & it: m_Owner.ListSockets (m_ID))
while (!session->acceptQueue.empty () && session->acceptQueue.front ().second + SAM_SESSION_MAX_ACCEPT_INTERVAL > ts) if (it->m_SocketType == eSAMSocketTypeAcceptor)
{
auto socket = session->acceptQueue.front ().first;
session->acceptQueue.pop_front ();
if (socket)
m_Owner.GetService ().post (std::bind(&SAMSocket::TerminateClose, socket));
}
if (!session->acceptQueue.empty ())
{
auto socket = session->acceptQueue.front ().first;
session->acceptQueue.pop_front ();
if (socket && socket->GetSocketType () == eSAMSocketTypeAcceptor)
{ {
socket->m_IsAccepting = true; it->m_IsAccepting = true;
session->GetLocalDestination ()->AcceptOnce (std::bind (&SAMSocket::HandleI2PAccept, socket, std::placeholders::_1)); session->GetLocalDestination ()->AcceptOnce (std::bind (&SAMSocket::HandleI2PAccept, it, std::placeholders::_1));
break;
} }
}
} }
if (!m_IsSilent) if (!m_IsSilent)
{ {

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save