Merge pull request #2095 from PurpleI2P/openssl

recent changes
This commit is contained in:
orignal 2024-08-27 21:41:15 -04:00 committed by GitHub
commit 9037e8b2b1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
93 changed files with 2958 additions and 1974 deletions

View File

@ -32,26 +32,30 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Commit Hash
id: commit
uses: prompt/actions-commit-hash@v3.0.0
- name: Build package
uses: jtdor/build-deb-action@v1
with:
docker-image: debian:${{ matrix.dist }}-slim
buildpackage-opts: --build=binary --no-sign
before-build-hook: debchange --controlmaint --local "+${{ github.sha }}~${{ matrix.dist }}" -b --distribution ${{ matrix.dist }} "CI build"
before-build-hook: debchange --controlmaint --local "+${{ steps.commit.outputs.short }}~${{ matrix.dist }}" -b --distribution ${{ matrix.dist }} "CI build"
extra-build-deps: devscripts git
- name: Upload package
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: i2pd_${{ matrix.dist }}
path: debian/artifacts/i2pd_*.deb
- name: Upload debugging symbols
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: i2pd-dbgsym_${{ matrix.dist }}
path: debian/artifacts/i2pd-dbgsym_*.deb

View File

@ -13,7 +13,7 @@ on:
- libi2pd/**
- libi2pd_client/**
- Makefile
- Makefile.homebrew
- Makefile.bsd
tags:
- '*'
pull_request:
@ -27,7 +27,7 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Test in FreeBSD
id: test
@ -44,7 +44,7 @@ jobs:
gmake -j2
- name: Upload artifacts
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: i2pd-freebsd
path: build/i2pd

View File

@ -30,13 +30,16 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: install packages
- name: Install required formulae
run: |
find /usr/local/bin -lname '*/Library/Frameworks/Python.framework/*' -delete
brew update
brew install boost miniupnpc openssl@1.1
- name: build application
- name: List installed formulae
run: brew list
- name: Build application
run: make HOMEBREW=1 USE_UPNP=${{ matrix.with_upnp }} PREFIX=$GITHUB_WORKSPACE/output -j3

View File

@ -32,7 +32,7 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
fetch-depth: 0
@ -73,7 +73,7 @@ jobs:
run: cmake --build . --config Debug -- -m
- name: Upload artifacts
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: i2pd-msvc
path: build/Debug/i2pd.*

View File

@ -42,7 +42,7 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
fetch-depth: 0
@ -63,7 +63,7 @@ jobs:
make USE_UPNP=yes DEBUG=no USE_GIT_VERSION=yes -j3
- name: Upload artifacts
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: i2pd-${{ matrix.arch_short }}.exe
path: i2pd.exe
@ -84,7 +84,7 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
fetch-depth: 0
@ -102,7 +102,7 @@ jobs:
cmake --build . -- -j3
- name: Upload artifacts
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: i2pd-cmake-${{ matrix.arch_short }}.exe
path: build/i2pd.exe
@ -116,7 +116,7 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
fetch-depth: 0
@ -125,34 +125,117 @@ jobs:
with:
msystem: MINGW32
install: base-devel git mingw-w64-i686-gcc mingw-w64-i686-boost mingw-w64-i686-openssl mingw-w64-i686-miniupnpc
cache: true
update: true
- name: Build WinXP-capable CRT packages
run: |
git clone https://github.com/msys2/MINGW-packages
pushd MINGW-packages
pushd mingw-w64-headers-git
sed -i 's/0x601/0x501/' PKGBUILD
MINGW_ARCH=mingw32 makepkg-mingw -sCLf --noconfirm
pacman --noconfirm -U mingw-w64-i686-headers-git-*-any.pkg.tar.zst
popd
pushd mingw-w64-crt-git
MINGW_ARCH=mingw32 makepkg-mingw -sCLf --noconfirm
pacman --noconfirm -U mingw-w64-i686-crt-git-*-any.pkg.tar.zst
popd
pushd mingw-w64-winpthreads-git
MINGW_ARCH=mingw32 makepkg-mingw -sCLf --noconfirm
pacman --noconfirm -U mingw-w64-i686-libwinpthread-git-*-any.pkg.tar.zst mingw-w64-i686-winpthreads-git-*-any.pkg.tar.zst
popd
popd
- name: Clone MinGW packages repository
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: |
cd MINGW-packages/mingw-w64-headers-git
sed -i 's/0x601/0x501/' PKGBUILD
MINGW_ARCH=mingw32 makepkg-mingw -sCLf --noconfirm --nocheck
- name: Install headers package
run: pacman --noconfirm -U MINGW-packages/mingw-w64-headers-git/mingw-w64-i686-*-any.pkg.tar.zst
# CRT
- name: Get crt package version
id: version-crt
run: |
echo "version=$(pacman -Si mingw-w64-i686-crt-git | grep -Po '^Version\s*: \K.+')" >> $GITHUB_OUTPUT
- name: Cache crt package
uses: actions/cache@v4
id: cache-crt
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
run: |
mkdir -p obj/Win32 obj/libi2pd obj/libi2pd_client obj/daemon
make USE_UPNP=yes DEBUG=no USE_GIT_VERSION=yes USE_WINXP_FLAGS=yes -j3
- name: Upload artifacts
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: i2pd-xp.exe
path: i2pd.exe

View File

@ -32,7 +32,7 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: install packages
run: |
@ -53,7 +53,7 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: install packages
run: |

View File

@ -37,29 +37,29 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Set up QEMU
uses: docker/setup-qemu-action@v2
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
uses: docker/setup-buildx-action@v3
- name: Login to DockerHub
uses: docker/login-action@v2
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Login to GitHub Container registry
uses: docker/login-action@v2
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build container for ${{ matrix.archname }}
uses: docker/build-push-action@v3
uses: docker/build-push-action@v5
with:
context: ./contrib/docker
file: ./contrib/docker/Dockerfile
@ -82,22 +82,22 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Set up QEMU
uses: docker/setup-qemu-action@v2
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
uses: docker/setup-buildx-action@v3
- name: Login to DockerHub
uses: docker/login-action@v2
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Login to GitHub Container registry
uses: docker/login-action@v2
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}

View File

@ -1,6 +1,65 @@
# for this file format description,
# see https://github.com/olivierlacan/keep-a-changelog
## [2.53.1] - 2024-07-29
### Changed
- I2CP performance improvement
### Fixed
- 100% CPU usage after I2CP/SAM/BOB session termination
- Incorrect client limits returned through I2CP
- Build with LibreSSL
## [2.53.0] - 2024-07-19
### Added
- New congestion control algorithm for streaming
- Support miniupnp-2.2.8
- Limit stream's outbound speed
- Flood to next day closest floodfills before UTC midnight
- Recognize duplicated routers and bypass them
- Random SSU2 resend interval
### Changed
- Set minimal version to 0.9.69 for floodfills and 0.9.58 for client tunnels
- Removed openssl 1.0.2 support
- Move unsent I2NP messages to the new session if replaced
- Use mt19937 RNG instead rand()
- Update router's congestion caps before initial publishing
- Don't try introducer with invalid address
- Select newest introducers to publish
- Don't request relay tag for every session if we have enough introducers
- Update timestamp for non-reachable or hidden router
- Reset streaming routing path if duplicated SYN received
- Update LeaseSet if inbound tunnel failed
- Reseeds list
### Fixed
- Crash when a destination gets terminated
- Expired offline signature upon destination creation
- Race condition between local RouterInfo buffer creation and sending it through the transports
## [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

View File

@ -15,4 +15,4 @@ endif
DEFINES = -D_GLIBCXX_USE_NANOSLEEP=1
INCFLAGS = -I/usr/include/ -I/usr/local/include/
LDFLAGS = ${LD_DEBUG} -Wl,-rpath,/usr/local/lib -L/usr/local/lib
LDLIBS = -lcrypto -lssl -lz -lboost_system -lboost_date_time -lboost_filesystem -lboost_program_options -lpthread
LDLIBS = -lcrypto -lssl -lz -lboost_system -lboost_filesystem -lboost_program_options -lpthread

View File

@ -2,7 +2,7 @@ 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
LDLIBS = -lbe -lbsd -lnetwork -lz -lcrypto -lssl -lboost_system -lboost_filesystem -lboost_program_options -lpthread
ifeq ($(USE_UPNP),yes)
DEFINES += -DUSE_UPNP

View File

@ -1,41 +1,40 @@
# root directory holding homebrew
BREWROOT = /usr/local
BREWROOT = /opt/homebrew
BOOSTROOT = ${BREWROOT}/opt/boost
SSLROOT = ${BREWROOT}/opt/openssl@1.1
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}
ifndef TRAVIS
CXX = clang++
endif
CXXFLAGS ?= ${CXX_DEBUG} -Wall -Wno-overloaded-virtual
NEEDED_CXXFLAGS ?= -std=c++11
INCFLAGS ?= -I${SSLROOT}/include -I${BOOSTROOT}/include
LDFLAGS ?= ${LD_DEBUG}
DEFINES += -DMAC_OSX
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 -lpthread
LDLIBS = -lz ${SSLROOT}/lib/libcrypto.a ${SSLROOT}/lib/libssl.a ${BOOSTROOT}/lib/libboost_system.a ${BOOSTROOT}/lib/libboost_filesystem.a ${BOOSTROOT}/lib/libboost_program_options.a
ifeq ($(USE_UPNP),yes)
LDLIBS += ${UPNPROOT}/lib/libminiupnpc.a
endif
LDLIBS += -lpthread -ldl
else
LDFLAGS += -L${SSLROOT}/lib -L${BOOSTROOT}/lib
LDLIBS = -lz -lcrypto -lssl -lboost_system -lboost_date_time -lboost_filesystem -lboost_program_options -lpthread
LDLIBS = -lz -lcrypto -lssl -lboost_system -lboost_filesystem -lboost_program_options -lpthread
ifeq ($(USE_UPNP),yes)
LDFLAGS += -L${UPNPROOT}/lib
LDLIBS += -lminiupnpc
endif
endif
ifeq ($(USE_UPNP),yes)
LDFLAGS += -ldl
CXXFLAGS += -DUSE_UPNP
DEFINES += -DUSE_UPNP
INCFLAGS += -I${UPNPROOT}/include
ifeq ($(USE_STATIC),yes)
LDLIBS += ${UPNPROOT}/lib/libminiupnpc.a
else
LDFLAGS += -L${UPNPROOT}/lib
LDLIBS += -lminiupnpc
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)
CXXFLAGS += -D__AES__ -maes
ifneq (, $(findstring i386, $(SYS))$(findstring i686, $(SYS))$(findstring x86_64, $(SYS))) # only x86-based CPU supports that
NEEDED_CXXFLAGS += -maes
DEFINES += -D__AES__
endif
endif
install: all

View File

@ -39,7 +39,6 @@ ifeq ($(USE_STATIC),yes)
# the shared libraries from the glibc version used for linking
LIBDIR := /usr/lib/$(SYS)
LDLIBS += $(LIBDIR)/libboost_system.a
LDLIBS += $(LIBDIR)/libboost_date_time.a
LDLIBS += $(LIBDIR)/libboost_filesystem.a
LDLIBS += $(LIBDIR)/libboost_program_options.a
LDLIBS += $(LIBDIR)/libssl.a
@ -50,7 +49,7 @@ ifeq ($(USE_UPNP),yes)
endif
LDLIBS += -lpthread -ldl
else
LDLIBS += -lcrypto -lssl -lz -lboost_system -lboost_date_time -lboost_filesystem -lboost_program_options -lpthread
LDLIBS += -lcrypto -lssl -lz -lboost_system -lboost_filesystem -lboost_program_options -lpthread
ifeq ($(USE_UPNP),yes)
LDLIBS += -lminiupnpc
endif

View File

@ -18,7 +18,6 @@ endif
LDLIBS += \
$(MINGW_PREFIX)/lib/libboost_system-mt.a \
$(MINGW_PREFIX)/lib/libboost_date_time-mt.a \
$(MINGW_PREFIX)/lib/libboost_filesystem-mt.a \
$(MINGW_PREFIX)/lib/libboost_program_options-mt.a \
$(MINGW_PREFIX)/lib/libssl.a \

View File

@ -7,9 +7,9 @@ LDFLAGS += -Wl,-dead_strip
LDFLAGS += -Wl,-dead_strip_dylibs
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_filesystem.a /usr/local/lib/libboost_program_options.a -lpthread
else
LDLIBS = -lz -lcrypto -lssl -lboost_system -lboost_date_time -lboost_filesystem -lboost_program_options -lpthread
LDLIBS = -lz -lcrypto -lssl -lboost_system -lboost_filesystem -lboost_program_options -lpthread
endif
ifeq ($(USE_UPNP),yes)

View File

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

View File

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

View File

@ -277,14 +277,14 @@ else()
if(NOT MSVC)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC")
endif()
add_definitions(-DBOOST_ATOMIC_DYN_LINK -DBOOST_SYSTEM_DYN_LINK -DBOOST_FILESYSTEM_DYN_LINK -DBOOST_PROGRAM_OPTIONS_DYN_LINK -DBOOST_DATE_TIME_DYN_LINK -DBOOST_REGEX_DYN_LINK)
add_definitions(-DBOOST_ATOMIC_DYN_LINK -DBOOST_SYSTEM_DYN_LINK -DBOOST_FILESYSTEM_DYN_LINK -DBOOST_PROGRAM_OPTIONS_DYN_LINK)
if(WIN32)
set(Boost_USE_STATIC_LIBS OFF)
set(Boost_USE_STATIC_RUNTIME OFF)
endif()
endif()
find_package(Boost REQUIRED COMPONENTS system filesystem program_options date_time OPTIONAL_COMPONENTS atomic)
find_package(Boost REQUIRED COMPONENTS system filesystem program_options OPTIONAL_COMPONENTS atomic)
if(NOT DEFINED Boost_FOUND)
message(SEND_ERROR "Boost is not found, or your boost version was below 1.46. Please download Boost!")
endif()

View File

@ -24,7 +24,7 @@ ExtraDiskSpaceRequired=15
AppID={{621A23E0-3CF4-4BD6-97BC-4835EA5206A2}
AppVerName={#I2Pd_AppName}
AppCopyright=Copyright (c) 2013-2022, The PurpleI2P Project
AppCopyright=Copyright (c) 2013-2024, The PurpleI2P Project
AppPublisherURL=http://i2pd.website/
AppSupportURL=https://github.com/PurpleI2P/i2pd/issues
AppUpdatesURL=https://github.com/PurpleI2P/i2pd/releases

View File

@ -4,7 +4,7 @@
#
#include <tunables/global>
profile i2pd /{usr/,}sbin/i2pd {
profile i2pd /{usr/,}bin/i2pd {
#include <abstractions/base>
#include <abstractions/openssl>
#include <abstractions/nameservice>
@ -14,12 +14,12 @@ profile i2pd /{usr/,}sbin/i2pd {
/var/lib/i2pd/** rw,
/var/log/i2pd/i2pd.log w,
/{var/,}run/i2pd/i2pd.pid rwk,
/{usr/,}sbin/i2pd mr,
/{usr/,}bin/i2pd mr,
@{system_share_dirs}/i2pd/** r,
# user homedir (if started not by init.d or systemd)
owner @{HOME}/.i2pd/ rw,
owner @{HOME}/.i2pd/** rwk,
#include if exists <local/usr.sbin.i2pd>
#include if exists <local/usr.bin.i2pd>
}

View File

@ -1,32 +0,0 @@
-----BEGIN CERTIFICATE-----
MIIFdTCCA12gAwIBAgIEQ5vCxzANBgkqhkiG9w0BAQ0FADBrMQswCQYDVQQGEwJY
WDELMAkGA1UECAwCWFgxCzAJBgNVBAcMAlhYMR4wHAYDVQQKDBVJMlAgQW5vbnlt
b3VzIE5ldHdvcmsxDDAKBgNVBAsMA0kyUDEUMBIGA1UEAwwLbHNAbWFpbC5pMnAw
HhcNMjMxMDE2MjAwNTA5WhcNMzMxMDEzMjAwNTA5WjBrMQswCQYDVQQGEwJYWDEL
MAkGA1UECAwCWFgxCzAJBgNVBAcMAlhYMR4wHAYDVQQKDBVJMlAgQW5vbnltb3Vz
IE5ldHdvcmsxDDAKBgNVBAsMA0kyUDEUMBIGA1UEAwwLbHNAbWFpbC5pMnAwggIi
MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDPcbKRtf4PzrDa0iRit0XrwnmA
2c1fJhkBipdPor7gMOAlkR82H1lkZSizR7kTZnr7vYqjDrOQr7bl5Dy3qo8/YCbZ
jsnUCTIIgIJQUxUlR40RjaSXphqzUEiXKHR6b0RahhFisQ3hlbbgzSch5YgSLKws
hOLi+eDSXw+HlwHlWFlT1XOKxSTJ/F3Bv40gxqZVC2pbxiPOeRZHQ6Ojw75lxTSF
gww2WzgztiWt4X9BO1yepnVqhAVRPmTfGUMfKzq9jkMzZKeQFV4uZSP9nCqzEpYd
WNDUfpTWiAQ9F+BwFXGusXXA3tGVwS7s6IEoiJFM5fsoJYfRoWGh3/1eirhBXW7U
M6oubMSTADyrvfjLfJBMmMnc2hNblRlKr0ZKUjMfv8cnyT4kQxlXLAHHXY2P89TM
TEVODkU48gnv6tC4t1JCb1/Da+3yVMjNX6rCzQfUwnLFrWthrwiI0NivAKFtiZjq
w1/ZQcYke2YyeqcfXMn+NTUA22Sm2mJoMo7jUf+rbM9Pi27/DncJgRGj5qwY0D3S
gc7829EjuZNPttGBmae1EmO7WQMB32cqdmItnV2FXpMhnn9h0u5H52kYqwn+mdtc
dTJRcbfKG1RTr3UjFISaTwL8qigMIkVXIzcpnr/R/sSeEs8xCqfsJ6rb4dCyFx+M
hqQcOCL5tumyd4W/LQIDAQABoyEwHzAdBgNVHQ4EFgQUgfaOG5HCnlW82wZ5BahL
GRO06igwDQYJKoZIhvcNAQENBQADggIBAKdVpqS9qF7gGotgXaVA1iP5YNsWlTvG
daGqeA/87//U21W6gpq82FhzsmsvUtXZfIeVIlDPI7WNDzS+A3K/KKrwM7dLgSie
r9eMl3D8WYPU95QF4mAlRyl7PCCsYoVjyvfro0iq3/iudIA5476rjfLdTXRi5hAT
qemPj0S+6sRjKEldRtGXrQATFlvLIWVYpgHijdDDx5M2hAz2y0mFxlDZTlA4BhL4
DwtGlVKmbc2x5MvIQM4UhbQqkxYS4gXnzf5Qx9QIytHfTr/hmbrkhKR1GCO31BSk
x9LhZxdI8LlwKSo6YgwXEB9E0M/tplaK9iZJFv4HPYLZrVJpb4IklMumyLMrgW5P
fR0dgKn+R9lk0emJ1Cu+qyyzf1vsLycYBwaEztINn4VK+/HfDFpnVCvJOyNuDmj5
KBLIoGdGoVfylmnc+e8zAXe+DY41fgniHMISOO78P8Bx9vTB+rhqnOUr9MzlUxPB
sKGjbXy2YynEqiGb+9g344v/+ukTSDenqTPHVzJ5uOi0iedy+3ASzUNN6GJocovP
167VOhwaETM0FwiKe0VdZRLLbbZ79CtJC0tmgcgPQPRa9Ldr6KN7u1J3D6lUp6zl
byPom10ueKONRb36t7ai79l2SEUZRSMkx6AXIU0JJ1SMtQtav7b5LkpYJfdL7+vO
dDx2/Za0VmdD
-----END CERTIFICATE-----

View File

@ -2,7 +2,7 @@ Description: Disable LogsDirectory and LogsDirectoryMode options in service
Author: r4sas <r4sas@i2pmail.org>
Reviewed-By: r4sas <r4sas@i2pmail.org>
Last-Update: 2023-05-17
Last-Update: 2024-07-19
--- a/contrib/i2pd.service
+++ b/contrib/i2pd.service
@ -15,5 +15,5 @@ Last-Update: 2023-05-17
+#LogsDirectory=i2pd
+#LogsDirectoryMode=0700
Type=forking
ExecStart=/usr/sbin/i2pd --conf=/etc/i2pd/i2pd.conf --tunconf=/etc/i2pd/tunnels.conf --tunnelsdir=/etc/i2pd/tunnels.conf.d --pidfile=/run/i2pd/i2pd.pid --logfile=/var/log/i2pd/i2pd.log --daemon --service
ExecStart=/usr/bin/i2pd --conf=/etc/i2pd/i2pd.conf --tunconf=/etc/i2pd/tunnels.conf --tunnelsdir=/etc/i2pd/tunnels.conf.d --pidfile=/run/i2pd/i2pd.pid --logfile=/var/log/i2pd/i2pd.log --daemon --service
ExecReload=/bin/sh -c "kill -HUP $MAINPID"

View File

@ -2,7 +2,7 @@ Description: Disable LogsDirectory and LogsDirectoryMode options in service
Author: r4sas <r4sas@i2pmail.org>
Reviewed-By: r4sas <r4sas@i2pmail.org>
Last-Update: 2023-05-17
Last-Update: 2024-07-19
--- a/contrib/i2pd.service
+++ b/contrib/i2pd.service
@ -15,5 +15,5 @@ Last-Update: 2023-05-17
+#LogsDirectory=i2pd
+#LogsDirectoryMode=0700
Type=forking
ExecStart=/usr/sbin/i2pd --conf=/etc/i2pd/i2pd.conf --tunconf=/etc/i2pd/tunnels.conf --tunnelsdir=/etc/i2pd/tunnels.conf.d --pidfile=/run/i2pd/i2pd.pid --logfile=/var/log/i2pd/i2pd.log --daemon --service
ExecStart=/usr/bin/i2pd --conf=/etc/i2pd/i2pd.conf --tunconf=/etc/i2pd/tunnels.conf --tunnelsdir=/etc/i2pd/tunnels.conf.d --pidfile=/run/i2pd/i2pd.pid --logfile=/var/log/i2pd/i2pd.log --daemon --service
ExecReload=/bin/sh -c "kill -HUP $MAINPID"

View File

@ -11,7 +11,7 @@ RuntimeDirectoryMode=0700
LogsDirectory=i2pd
LogsDirectoryMode=0700
Type=forking
ExecStart=/usr/sbin/i2pd --conf=/etc/i2pd/i2pd.conf --tunconf=/etc/i2pd/tunnels.conf --tunnelsdir=/etc/i2pd/tunnels.conf.d --pidfile=/run/i2pd/i2pd.pid --logfile=/var/log/i2pd/i2pd.log --daemon --service
ExecStart=/usr/bin/i2pd --conf=/etc/i2pd/i2pd.conf --tunconf=/etc/i2pd/tunnels.conf --tunnelsdir=/etc/i2pd/tunnels.conf.d --pidfile=/run/i2pd/i2pd.pid --logfile=/var/log/i2pd/i2pd.log --daemon --service
ExecReload=/bin/sh -c "kill -HUP $MAINPID"
PIDFile=/run/i2pd/i2pd.pid
### Uncomment, if auto restart needed

View File

@ -7,7 +7,7 @@ tunconf="/etc/i2pd/tunnels.conf"
tundir="/etc/i2pd/tunnels.conf.d"
name="i2pd"
command="/usr/sbin/i2pd"
command="/usr/bin/i2pd"
command_args="--service --daemon --log=file --logfile=$logfile --conf=$mainconf --tunconf=$tunconf --tunnelsdir=$tundir --pidfile=$pidfile"
description="i2p router written in C++"
required_dirs="/var/lib/i2pd"

View File

@ -1,7 +1,7 @@
%define git_hash %(git rev-parse HEAD | cut -c -7)
Name: i2pd-git
Version: 2.51.0
Version: 2.53.1
Release: git%{git_hash}%{?dist}
Summary: I2P router written in C++
Conflicts: i2pd
@ -24,6 +24,10 @@ BuildRequires: openssl-devel
BuildRequires: miniupnpc-devel
BuildRequires: systemd-units
%if 0%{?fedora} > 40 || 0%{?eln}
BuildRequires: openssl-devel-engine
%endif
Requires: logrotate
Requires: systemd
Requires(pre): %{_sbindir}/useradd %{_sbindir}/groupadd
@ -93,7 +97,7 @@ pushd build
%endif
chrpath -d i2pd
%{__install} -D -m 755 i2pd %{buildroot}%{_sbindir}/i2pd
%{__install} -D -m 755 i2pd %{buildroot}%{_bindir}/i2pd
%{__install} -d -m 755 %{buildroot}%{_datadir}/i2pd
%{__install} -d -m 700 %{buildroot}%{_sharedstatedir}/i2pd
%{__install} -d -m 700 %{buildroot}%{_localstatedir}/log/i2pd
@ -129,7 +133,7 @@ getent passwd i2pd >/dev/null || \
%files
%doc LICENSE README.md contrib/i2pd.conf contrib/subscriptions.txt contrib/tunnels.conf contrib/tunnels.d
%{_sbindir}/i2pd
%{_bindir}/i2pd
%config(noreplace) %{_sysconfdir}/i2pd/*.conf
%config(noreplace) %{_sysconfdir}/i2pd/tunnels.conf.d/*.conf
%config %{_sysconfdir}/i2pd/subscriptions.txt
@ -144,6 +148,15 @@ getent passwd i2pd >/dev/null || \
%changelog
* Tue Jul 30 2024 orignal <orignal@i2pmail.org> - 2.53.1
- update to 2.53.1
* Fri Jul 19 2024 orignal <orignal@i2pmail.org> - 2.53.0
- update to 2.53.0
* 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

View File

@ -1,5 +1,5 @@
Name: i2pd
Version: 2.51.0
Version: 2.53.1
Release: 1%{?dist}
Summary: I2P router written in C++
Conflicts: i2pd-git
@ -22,6 +22,10 @@ BuildRequires: openssl-devel
BuildRequires: miniupnpc-devel
BuildRequires: systemd-units
%if 0%{?fedora} > 40 || 0%{?eln}
BuildRequires: openssl-devel-engine
%endif
Requires: logrotate
Requires: systemd
Requires(pre): %{_sbindir}/useradd %{_sbindir}/groupadd
@ -91,7 +95,7 @@ pushd build
%endif
chrpath -d i2pd
%{__install} -D -m 755 i2pd %{buildroot}%{_sbindir}/i2pd
%{__install} -D -m 755 i2pd %{buildroot}%{_bindir}/i2pd
%{__install} -d -m 755 %{buildroot}%{_datadir}/i2pd
%{__install} -d -m 700 %{buildroot}%{_sharedstatedir}/i2pd
%{__install} -d -m 700 %{buildroot}%{_localstatedir}/log/i2pd
@ -127,7 +131,7 @@ getent passwd i2pd >/dev/null || \
%files
%doc LICENSE README.md contrib/i2pd.conf contrib/subscriptions.txt contrib/tunnels.conf contrib/tunnels.d
%{_sbindir}/i2pd
%{_bindir}/i2pd
%config(noreplace) %{_sysconfdir}/i2pd/*.conf
%config(noreplace) %{_sysconfdir}/i2pd/tunnels.conf.d/*.conf
%config %{_sysconfdir}/i2pd/subscriptions.txt
@ -142,6 +146,15 @@ getent passwd i2pd >/dev/null || \
%changelog
* Tue Jul 30 2024 orignal <orignal@i2pmail.org> - 2.53.1
- update to 2.53.1
* Fri Jul 19 2024 orignal <orignal@i2pmail.org> - 2.53.0
- update to 2.53.0
* 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

View File

@ -8,4 +8,4 @@ env LOGFILE="/var/log/i2pd/i2pd.log"
expect fork
exec /usr/sbin/i2pd --daemon --service --log=file --logfile=$LOGFILE
exec /usr/bin/i2pd --daemon --service --log=file --logfile=$LOGFILE

View File

@ -188,7 +188,7 @@ namespace util
std::string bandwidth; i2p::config::GetOption("bandwidth", bandwidth);
if (bandwidth.length () > 0)
{
if (bandwidth[0] >= 'K' && bandwidth[0] <= 'X')
if (bandwidth.length () == 1 && ((bandwidth[0] >= 'K' && bandwidth[0] <= 'P') || bandwidth[0] == 'X' ))
{
i2p::context.SetBandwidth (bandwidth[0]);
LogPrint(eLogInfo, "Daemon: Bandwidth set to ", i2p::context.GetBandwidthLimit (), "KBps");

View File

@ -417,6 +417,15 @@ namespace http {
}
}
static void ShowHop(std::stringstream& s, const i2p::data::IdentityEx& ident)
{
auto identHash = ident.GetIdentHash();
auto router = i2p::data::netdb.FindRouter(identHash);
s << i2p::data::GetIdentHashAbbreviation(identHash);
if (router)
s << "<small style=\"color:gray\"> " << router->GetBandwidthCap() << "</small>";
}
static void ShowLeaseSetDestination (std::stringstream& s, std::shared_ptr<const i2p::client::LeaseSetDestination> dest, uint32_t token)
{
s << "<b>Base32:</b><br>\r\n<textarea readonly cols=\"80\" rows=\"1\">";
@ -482,7 +491,9 @@ namespace http {
it->VisitTunnelHops(
[&s](std::shared_ptr<const i2p::data::IdentityEx> hopIdent)
{
s << "&#8658; " << i2p::data::GetIdentHashAbbreviation (hopIdent->GetIdentHash ()) << " ";
s << "&#8658; ";
ShowHop(s, *hopIdent);
s << " ";
}
);
}
@ -503,7 +514,9 @@ namespace http {
it->VisitTunnelHops(
[&s](std::shared_ptr<const i2p::data::IdentityEx> hopIdent)
{
s << " " << i2p::data::GetIdentHashAbbreviation (hopIdent->GetIdentHash ()) << " &#8658;";
s << " ";
ShowHop(s, *hopIdent);
s << " &#8658;";
}
);
}
@ -699,7 +712,9 @@ namespace http {
it->VisitTunnelHops(
[&s](std::shared_ptr<const i2p::data::IdentityEx> hopIdent)
{
s << "&#8658; " << i2p::data::GetIdentHashAbbreviation (hopIdent->GetIdentHash ()) << " ";
s << "&#8658; ";
ShowHop(s, *hopIdent);
s << " ";
}
);
}
@ -720,7 +735,9 @@ namespace http {
it->VisitTunnelHops(
[&s](std::shared_ptr<const i2p::data::IdentityEx> hopIdent)
{
s << " " << i2p::data::GetIdentHashAbbreviation (hopIdent->GetIdentHash ()) << " &#8658;";
s << " ";
ShowHop(s, *hopIdent);
s << " &#8658;";
}
);
}

View File

@ -8,14 +8,12 @@
#include <stdio.h>
#include <sstream>
#include <iomanip>
#include <openssl/x509.h>
#include <openssl/pem.h>
// Use global placeholders from boost introduced when local_time.hpp is loaded
#define BOOST_BIND_GLOBAL_PLACEHOLDERS
#include <boost/date_time/local_time/local_time.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/property_tree/json_parser.hpp>
#include <boost/lexical_cast.hpp>
@ -258,9 +256,9 @@ namespace client
header << "Content-Length: " << boost::lexical_cast<std::string>(len) << "\r\n";
header << "Content-Type: application/json\r\n";
header << "Date: ";
auto facet = new boost::local_time::local_time_facet ("%a, %d %b %Y %H:%M:%S GMT");
header.imbue(std::locale (header.getloc(), facet));
header << boost::posix_time::second_clock::local_time() << "\r\n";
std::time_t t = std::time (nullptr);
std::tm tm = *std::gmtime (&t);
header << std::put_time(&tm, "%a, %d %b %Y %T GMT") << "\r\n";
header << "\r\n";
offset = header.str ().size ();
memcpy (buf->data (), header.str ().c_str (), offset);

View File

@ -1,10 +1,15 @@
/*
* 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
#include <string>
#include <thread>
#include <boost/thread/thread.hpp>
#include <boost/asio.hpp>
#include "Log.h"
#include "RouterContext.h"
@ -110,10 +115,16 @@ namespace transport
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));
#endif
m_upnpUrlsInitialized=err!=0;
if (err == UPNP_IGD_VALID_CONNECTED)
{
#if (MINIUPNPC_API_VERSION < 18)
err = UPNP_GetExternalIPAddress (m_upnpUrls.controlURL, m_upnpData.first.servicetype, m_externalIPAddress);
if(err != UPNPCOMMAND_SUCCESS)
{
@ -121,6 +132,7 @@ namespace transport
return;
}
else
#endif
{
LogPrint (eLogError, "UPnP: Found Internet Gateway Device ", m_upnpUrls.controlURL);
if (!m_externalIPAddress[0])
@ -166,11 +178,11 @@ namespace transport
if (address && !address->host.is_v6 () && address->port)
TryPortMapping (address);
}
m_Timer.expires_from_now (boost::posix_time::minutes(20)); // every 20 minutes
m_Timer.expires_from_now (boost::posix_time::minutes(UPNP_PORT_FORWARDING_INTERVAL)); // every 20 minutes
m_Timer.async_wait ([this](const boost::system::error_code& ecode)
{
if (ecode != boost::asio::error::operation_aborted)
PortMapping ();
PortMapping ();
});
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2013-2020, The PurpleI2P Project
* Copyright (c) 2013-2024, The PurpleI2P Project
*
* This file is part of Purple i2pd project and licensed under BSD3
*
@ -28,7 +28,8 @@ namespace i2p
namespace transport
{
const int UPNP_RESPONSE_TIMEOUT = 2000; // in milliseconds
const int UPNP_PORT_FORWARDING_INTERVAL = 20; // in minutes
enum
{
UPNP_IGD_NONE = 0,

View File

@ -162,12 +162,21 @@ namespace i2p
#ifndef ANDROID
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));
std::cerr << "i2pd: Could not lock pid file " << pidfile << ": " << strerror(errno) << std::endl;
return false;
}
#endif
char pid[10];
sprintf(pid, "%d\n", getpid());
ftruncate(pidFH, 0);

5
debian/NEWS vendored Normal file
View File

@ -0,0 +1,5 @@
i2pd (2.53.0-1) unstable; urgency=medium
i2pd binary moved from /usr/sbin to /usr/bin. Please check your scripts if you used the old path.
-- r4sas <r4sas@i2pmail.org> Fri, 19 Jul 2024 16:00:00 +0000

19
debian/changelog vendored
View File

@ -1,3 +1,22 @@
i2pd (2.53.1-1) unstable; urgency=medium
* updated to version 2.53.1
-- orignal <orignal@i2pmail.org> Tue, 30 Jul 2024 16:00:00 +0000
i2pd (2.53.0-1) unstable; urgency=medium
* updated to version 2.53.0/0.9.63
* binary moved from /usr/sbin to /usr/bin
-- r4sas <r4sas@i2pmail.org> Sat, 20 Jul 2024 15:10:00 +0000
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

2
debian/i2pd.init vendored
View File

@ -13,7 +13,7 @@
PATH=/sbin:/usr/sbin:/bin:/usr/bin
DESC=i2pd # Introduce a short description here
NAME=i2pd # Introduce the short server's name here
DAEMON=/usr/sbin/$NAME # Introduce the server's location here
DAEMON=/usr/bin/$NAME # Introduce the server's location here
DAEMON_OPTS="" # Arguments to run the daemon with
PIDFILE=/var/run/$NAME/$NAME.pid
I2PCONF=/etc/$NAME/i2pd.conf

4
debian/i2pd.install vendored
View File

@ -1,6 +1,6 @@
i2pd usr/sbin/
i2pd usr/bin/
contrib/i2pd.conf etc/i2pd/
contrib/tunnels.conf etc/i2pd/
contrib/certificates/ usr/share/i2pd/
contrib/tunnels.d/README etc/i2pd/tunnels.conf.d/
contrib/apparmor/usr.sbin.i2pd etc/apparmor.d
contrib/apparmor/usr.bin.i2pd etc/apparmor.d

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2022-2023, The PurpleI2P Project
* Copyright (c) 2022-2024, The PurpleI2P Project
*
* This file is part of Purple i2pd project and licensed under BSD3
*
@ -64,11 +64,12 @@ namespace chinese // language namespace
{"Full cone NAT", "全锥型NAT"},
{"No Descriptors", "无描述符"},
{"Uptime", "运行时间"},
{"Network status", "IPv4 网络状态"},
{"Network status", "网络状态"},
{"Network status v6", "IPv6 网络状态"},
{"Stopping in", "距停止还有:"},
{"Family", "家族"},
{"Tunnel creation success rate", "隧道创建成功率"},
{"Total tunnel creation success rate", "当前隧道创建成功率"},
{"Received", "已接收"},
{"%.2f KiB/s", "%.2f KiB/s"},
{"Sent", "已发送"},
@ -95,6 +96,7 @@ namespace chinese // language namespace
{"Address", "地址"},
{"Type", "类型"},
{"EncType", "加密类型"},
{"Expire LeaseSet", "到期租约集"},
{"Inbound tunnels", "入站隧道"},
{"%dms", "%dms"},
{"Outbound tunnels", "出站隧道"},
@ -151,6 +153,8 @@ namespace chinese // language namespace
{"StreamID can't be null", "StreamID 不能为空"},
{"Return to destination page", "返回目标页面"},
{"You will be redirected in %d seconds", "您将在%d秒内被重定向"},
{"LeaseSet expiration time updated", "租约集到期时间已更新"},
{"LeaseSet is not found or already expired", "租约集未找到或已过期"},
{"Transit tunnels count must not exceed %d", "中转隧道数量限制为 %d"},
{"Back to commands list", "返回命令列表"},
{"Register at reg.i2p", "在 reg.i2p 注册域名"},

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2022-2023, The PurpleI2P Project
* Copyright (c) 2022-2024, The PurpleI2P Project
*
* This file is part of Purple i2pd project and licensed under BSD3
*
@ -36,18 +36,18 @@ namespace czech // language namespace
{"%.2f GiB", "%.2f GiB"},
{"building", "vytváří se"},
{"failed", "selhalo"},
{"expiring", "končící"},
{"expiring", "vyprší platnost"},
{"established", "vytvořeno"},
{"unknown", "neznámý"},
{"exploratory", "průzkumné"},
{"Purple I2P Webconsole", "Purple I2P Webkonsole"},
{"<b>i2pd</b> webconsole", "<b>i2pd</b> webkonsole"},
{"Purple I2P Webconsole", "Purple I2P webová konzole"},
{"<b>i2pd</b> webconsole", "<b>i2pd</b> webová konzole"},
{"Main page", "Hlavní stránka"},
{"Router commands", "Router příkazy"},
{"Local Destinations", "Lokální destinace"},
{"LeaseSets", "LeaseSety"},
{"Local Destinations", "Místní cíle"},
{"LeaseSets", "Sety pronájmu"},
{"Tunnels", "Tunely"},
{"Transit Tunnels", "Transitní tunely"},
{"Transit Tunnels", "Tranzitní tunely"},
{"Transports", "Transporty"},
{"I2P tunnels", "I2P tunely"},
{"SAM sessions", "SAM relace"},
@ -61,18 +61,21 @@ namespace czech // language namespace
{"Clock skew", "Časová nesrovnalost"},
{"Offline", "Offline"},
{"Symmetric NAT", "Symetrický NAT"},
{"Full cone NAT", "Full cone NAT"},
{"No Descriptors", "Žádné popisovače"},
{"Uptime", "Doba provozu"},
{"Network status", "Status sítě"},
{"Network status v6", "Status sítě v6"},
{"Network status", "Stav sítě"},
{"Network status v6", "Stav sítě v6"},
{"Stopping in", "Zastavuji za"},
{"Family", "Rodina"},
{"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"},
{"%.2f KiB/s", "%.2f KiB/s"},
{"Sent", "Odesláno"},
{"Transit", "Tranzit"},
{"Data path", "Cesta k data souborům"},
{"Hidden content. Press on text to see.", "Skrytý kontent. Pro zobrazení, klikni na text."},
{"Data path", "Cesta k datovým souborům"},
{"Hidden content. Press on text to see.", "Skrytý obsah. Pro zobrazení klikněte sem."},
{"Router Ident", "Routerová Identita"},
{"Router Family", "Rodina routerů"},
{"Router Caps", "Omezení Routerů"},
@ -93,6 +96,7 @@ namespace czech // language namespace
{"Address", "Adresa"},
{"Type", "Typ"},
{"EncType", "EncType"},
{"Expire LeaseSet", "Zrušit platnost setu pronájmu"},
{"Inbound tunnels", "Příchozí tunely"},
{"%dms", "%dms"},
{"Outbound tunnels", "Odchozí tunely"},
@ -103,21 +107,24 @@ namespace czech // language namespace
{"Amount", "Množství"},
{"Incoming Tags", "Příchozí štítky"},
{"Tags sessions", "Relace štítků"},
{"Status", "Status"},
{"Local Destination", "Lokální Destinace"},
{"Status", "Stav"},
{"Local Destination", "Místní cíl"},
{"Streams", "Toky"},
{"Close stream", "Uzavřít tok"},
{"Such destination is not found", "Takováto destinace nebyla nalezena"},
{"I2CP session not found", "I2CP relace nenalezena"},
{"I2CP is not enabled", "I2CP není zapnuto"},
{"Invalid", "Neplatný"},
{"Store type", "Druh uložení"},
{"Expires", "Vyprší"},
{"Non Expired Leases", "Nevypršené Leasy"},
{"Non Expired Leases", "Pronájmy, kterým nevypršela platnost"},
{"Gateway", "Brána"},
{"TunnelID", "ID tunelu"},
{"EndDate", "Datum ukončení"},
{"floodfill mode is disabled", "režim floodfill je vypnut"},
{"Queue size", "Velikost fronty"},
{"Run peer test", "Spustit peer test"},
{"Reload tunnels configuration", "Znovu načíst nastavení tunelů"},
{"Decline transit tunnels", "Odmítnout tranzitní tunely"},
{"Accept transit tunnels", "Přijmout tranzitní tunely"},
{"Cancel graceful shutdown", "Zrušit hladké vypnutí"},
@ -145,14 +152,17 @@ namespace czech // language namespace
{"Destination not found", "Destinace nenalezena"},
{"StreamID can't be null", "StreamID nemůže být null"},
{"Return to destination page", "Zpět na stránku destinací"},
{"Back to commands list", "Zpět na list příkazů"},
{"You will be redirected in %d seconds", "Budete přesměrováni za %d sekund"},
{"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"},
{"Description", "Popis"},
{"A bit information about service on domain", "Trochu informací o službě na doméně"},
{"Submit", "Odeslat"},
{"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"},
{"Such destination is not found", "Takováto destinace nebyla nalezena"},
{"Unknown command", "Neznámý příkaz"},
{"Command accepted", "Příkaz přijat"},
{"Proxy error", "Chyba proxy serveru"},
@ -162,6 +172,15 @@ 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"},
{"Invalid request", "Neplatný 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"},
{"Can't detect destination host from request", "Nelze zjistit cílového hostitele z požadavku"},
{"Outproxy failure", "Outproxy selhání"},

View File

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

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2023, The PurpleI2P Project
* Copyright (c) 2023-2024, The PurpleI2P Project
*
* This file is part of Purple i2pd project and licensed under BSD3
*
@ -31,22 +31,186 @@ namespace polish // language namespace
static std::map<std::string, std::string> strings
{
{"%.2f KiB", "%.2f KiB"},
{"%.2f MiB", "%.2f MiB"},
{"%.2f GiB", "%.2f GiB"},
{"building", "Kompilowanie"},
{"failed", "nieudane"},
{"expiring", "wygasający"},
{"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"},
{"Router commands", "Komendy routera"},
{"Local Destinations", "Lokalne miejsca docelowe"},
{"LeaseSets", "ZestawyNajmu"},
{"Tunnels", "Tunele"},
{"Transit Tunnels", "Tunele Tranzytu"},
{"Transports", "Transportery"},
{"I2P tunnels", "Tunele I2P"},
{"SAM sessions", "Sesje SAM"},
{"ERROR", "BŁĄD"},
{"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"},
{"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"},
{"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
{
{"", {"", "", ""}},
{"%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()

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2023, The PurpleI2P Project
* Copyright (c) 2023-2024, The PurpleI2P Project
*
* This file is part of Purple i2pd project and licensed under BSD3
*
@ -58,7 +58,7 @@ namespace portuguese // language namespace
{"Unknown", "Desconhecido"},
{"Proxy", "Proxy"},
{"Mesh", "Malha"},
{"Clock skew", "Defasagem do Relógio"},
{"Clock skew", "Desvio de Relógio"},
{"Offline", "Desligado"},
{"Symmetric NAT", "NAT Simétrico"},
{"Full cone NAT", "Full cone NAT"},
@ -74,7 +74,7 @@ namespace portuguese // language namespace
{"%.2f KiB/s", "%.2f KiB/s"},
{"Sent", "Enviado"},
{"Transit", "Trânsito"},
{"Data path", "Diretório dos dados"},
{"Data path", "Diretório de dados"},
{"Hidden content. Press on text to see.", "Conteúdo oculto. Clique no texto para revelar."},
{"Router Ident", "Identidade do Roteador"},
{"Router Family", "Família do Roteador"},
@ -106,9 +106,9 @@ namespace portuguese // language namespace
{"Destination", "Destinos"},
{"Amount", "Quantidade"},
{"Incoming Tags", "Etiquetas de Entrada"},
{"Tags sessions", "Sessões de etiquetas"},
{"Tags sessions", "Sessões de Etiquetas"},
{"Status", "Estado"},
{"Local Destination", "Destinos Locais"},
{"Local Destination", "Destino Local"},
{"Streams", "Fluxos"},
{"Close stream", "Fechar fluxo"},
{"Such destination is not found", "Tal destino não foi encontrado"},
@ -148,7 +148,7 @@ namespace portuguese // language namespace
{"Invalid token", "Token Inválido"},
{"SUCCESS", "SUCESSO"},
{"Stream closed", "Fluxo fechado"},
{"Stream not found or already was closed", "Fluxo não encontrado ou já encerrado"},
{"Stream not found or already was closed", "Fluxo não encontrado ou já fechado"},
{"Destination not found", "Destino não encontrado"},
{"StreamID can't be null", "StreamID não pode ser nulo"},
{"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"},
{"Transit tunnels count must not exceed %d", "A contagem de túneis de trânsito não deve exceder %d"},
{"Back to commands list", "Voltar para a lista de comandos"},
{"Register at reg.i2p", "Registrar na reg.i2p"},
{"Register at reg.i2p", "Registrar em reg.i2p"},
{"Description", "Descrição"},
{"A bit information about service on domain", "Algumas informações sobre o serviço no domínio"},
{"Submit", "Enviar"},
@ -169,22 +169,22 @@ namespace portuguese // language namespace
{"Proxy info", "Informações do proxy"},
{"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"},
{"You may try to find this host on jump services below", "Você pode tentar encontrar este host nos jump services abaixo"},
{"You may try to find this host on jump services below", "Você pode tentar encontrar este host nos serviços de jump abaixo"},
{"Invalid request", "Requisição inválida"},
{"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"},
{"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"},
{"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 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 proceder: <a href='%s'> Continuar </a>."},
{"Addresshelper request", "Requisição ao 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>."},
{"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>."},
{"Addresshelper update", "Atualização do Auxiliar de Endereços"},
{"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"},
{"Outproxy failure", "Falha no outproxy"},
{"Bad outproxy settings", "Configurações ruins de outproxy"},
{"Bad outproxy settings", "Má configurações do 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"},
{"Unknown outproxy URL", "URL de outproxy desconhecida"},
{"Cannot resolve upstream proxy", "Não é possível resolver o proxy de entrada"},

View File

@ -1,137 +0,0 @@
/*
* Copyright (c) 2013-2020, 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
*
* Kovri go write your own code
*
*/
#include "I2PEndian.h"
#include "ChaCha20.h"
#if !OPENSSL_AEAD_CHACHA20_POLY1305
namespace i2p
{
namespace crypto
{
namespace chacha
{
void u32t8le(uint32_t v, uint8_t * p)
{
p[0] = v & 0xff;
p[1] = (v >> 8) & 0xff;
p[2] = (v >> 16) & 0xff;
p[3] = (v >> 24) & 0xff;
}
uint32_t u8t32le(const uint8_t * p)
{
uint32_t value = p[3];
value = (value << 8) | p[2];
value = (value << 8) | p[1];
value = (value << 8) | p[0];
return value;
}
uint32_t rotl32(uint32_t x, int n)
{
return x << n | (x >> (-n & 31));
}
void quarterround(uint32_t *x, int a, int b, int c, int d)
{
x[a] += x[b]; x[d] = rotl32(x[d] ^ x[a], 16);
x[c] += x[d]; x[b] = rotl32(x[b] ^ x[c], 12);
x[a] += x[b]; x[d] = rotl32(x[d] ^ x[a], 8);
x[c] += x[d]; x[b] = rotl32(x[b] ^ x[c], 7);
}
void Chacha20Block::operator << (const Chacha20State & st)
{
int i;
for (i = 0; i < 16; i++)
u32t8le(st.data[i], data + (i << 2));
}
void block (Chacha20State &input, int rounds)
{
int i;
Chacha20State x;
x.Copy(input);
for (i = rounds; i > 0; i -= 2)
{
quarterround(x.data, 0, 4, 8, 12);
quarterround(x.data, 1, 5, 9, 13);
quarterround(x.data, 2, 6, 10, 14);
quarterround(x.data, 3, 7, 11, 15);
quarterround(x.data, 0, 5, 10, 15);
quarterround(x.data, 1, 6, 11, 12);
quarterround(x.data, 2, 7, 8, 13);
quarterround(x.data, 3, 4, 9, 14);
}
x += input;
input.block << x;
}
void Chacha20Init (Chacha20State& state, const uint8_t * nonce, const uint8_t * key, uint32_t counter)
{
state.data[0] = 0x61707865;
state.data[1] = 0x3320646e;
state.data[2] = 0x79622d32;
state.data[3] = 0x6b206574;
for (size_t i = 0; i < 8; i++)
state.data[4 + i] = chacha::u8t32le(key + i * 4);
state.data[12] = htole32 (counter);
for (size_t i = 0; i < 3; i++)
state.data[13 + i] = chacha::u8t32le(nonce + i * 4);
}
void Chacha20SetCounter (Chacha20State& state, uint32_t counter)
{
state.data[12] = htole32 (counter);
state.offset = 0;
}
void Chacha20Encrypt (Chacha20State& state, uint8_t * buf, size_t sz)
{
if (state.offset > 0)
{
// previous block if any
auto s = chacha::blocksize - state.offset;
if (sz < s) s = sz;
for (size_t i = 0; i < s; i++)
buf[i] ^= state.block.data[state.offset + i];
buf += s;
sz -= s;
state.offset += s;
if (state.offset >= chacha::blocksize) state.offset = 0;
}
for (size_t i = 0; i < sz; i += chacha::blocksize)
{
chacha::block(state, chacha::rounds);
state.data[12]++;
for (size_t j = i; j < i + chacha::blocksize; j++)
{
if (j >= sz)
{
state.offset = j & 0x3F; // % 64
break;
}
buf[j] ^= state.block.data[j - i];
}
}
}
} // namespace chacha
} // namespace crypto
} // namespace i2p
#endif

View File

@ -1,72 +0,0 @@
/*
* Copyright (c) 2013-2020, 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
*
* Kovri go write your own code
*
*/
#ifndef LIBI2PD_CHACHA20_H
#define LIBI2PD_CHACHA20_H
#include <cstdint>
#include <cstring>
#include <inttypes.h>
#include <string.h>
#include "Crypto.h"
#if !OPENSSL_AEAD_CHACHA20_POLY1305
namespace i2p
{
namespace crypto
{
const std::size_t CHACHA20_KEY_BYTES = 32;
const std::size_t CHACHA20_NOUNCE_BYTES = 12;
namespace chacha
{
constexpr std::size_t blocksize = 64;
constexpr int rounds = 20;
struct Chacha20State;
struct Chacha20Block
{
Chacha20Block () {};
Chacha20Block (Chacha20Block &&) = delete;
uint8_t data[blocksize];
void operator << (const Chacha20State & st);
};
struct Chacha20State
{
Chacha20State (): offset (0) {};
Chacha20State (Chacha20State &&) = delete;
Chacha20State & operator += (const Chacha20State & other)
{
for(int i = 0; i < 16; i++)
data[i] += other.data[i];
return *this;
}
void Copy(const Chacha20State & other)
{
memcpy(data, other.data, sizeof(uint32_t) * 16);
}
uint32_t data[16];
Chacha20Block block;
size_t offset;
};
void Chacha20Init (Chacha20State& state, const uint8_t * nonce, const uint8_t * key, uint32_t counter);
void Chacha20SetCounter (Chacha20State& state, uint32_t counter);
void Chacha20Encrypt (Chacha20State& state, uint8_t * buf, size_t sz); // encrypt buf in place
} // namespace chacha
} // namespace crypto
} // namespace i2p
#endif
#endif

View File

@ -168,6 +168,8 @@ namespace config {
("i2cp.address", value<std::string>()->default_value("127.0.0.1"), "I2CP listen address")
("i2cp.port", value<uint16_t>()->default_value(7654), "I2CP listen port")
("i2cp.singlethread", value<bool>()->default_value(true), "Destinations run in the I2CP server's thread")
("i2cp.inboundlimit", value<uint32_t>()->default_value(0), "Client inbound limit in KBps to return in BandwidthLimitsMessage. Router's bandwidth by default")
("i2cp.outboundlimit", value<uint32_t>()->default_value(0), "Client outbound limit in KBps to return in BandwidthLimitsMessage. Router's bandwidth by default")
;
options_description i2pcontrol("I2PControl options");
@ -205,7 +207,7 @@ namespace config {
reseed.add_options()
("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.floodfill", value<std::string>()->default_value(""), "Path to router info of floodfill to reseed from")
("reseed.floodfill", value<std::string>()->default_value(""), "Ignored. Always empty")
("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.proxy", value<std::string>()->default_value(""), "url for reseed proxy, supports http/socks")
@ -229,8 +231,7 @@ namespace config {
"http://[301:65b9:c7cd:9a36::1]:18801/,"
"http://[320:8936:ec1a:31f1::216]/,"
"http://[306:3834:97b9:a00a::1]/,"
"http://[316:f9e0:f22e:a74f::216]/,"
"http://[300:eaff:7fab:181b::e621]:7170"
"http://[316:f9e0:f22e:a74f::216]/"
), "Reseed URLs through the Yggdrasil, separated by comma")
;

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2013-2023, The PurpleI2P Project
* Copyright (c) 2013-2024, The PurpleI2P Project
*
* This file is part of Purple i2pd project and licensed under BSD3
*
@ -19,16 +19,11 @@
#if OPENSSL_HKDF
#include <openssl/kdf.h>
#endif
#if !OPENSSL_AEAD_CHACHA20_POLY1305
#include "ChaCha20.h"
#include "Poly1305.h"
#endif
#include "Crypto.h"
#include "Ed25519.h"
#include "I2PEndian.h"
#include "Log.h"
namespace i2p
{
namespace crypto
@ -988,7 +983,6 @@ namespace crypto
if (len < msgLen) return false;
if (encrypt && len < msgLen + 16) return false;
bool ret = true;
#if OPENSSL_AEAD_CHACHA20_POLY1305
int outlen = 0;
EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new ();
if (encrypt)
@ -1003,6 +997,15 @@ namespace crypto
}
else
{
#if defined(LIBRESSL_VERSION_NUMBER)
std::vector<uint8_t> m(msgLen + 16);
if (msg == buf)
{
// we have to use different buffers otherwise verification fails
memcpy (m.data (), msg, msgLen + 16);
msg = m.data ();
}
#endif
EVP_DecryptInit_ex(ctx, EVP_chacha20_poly1305(), 0, 0, 0);
EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_IVLEN, 12, 0);
EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG, 16, (uint8_t *)(msg + msgLen));
@ -1013,73 +1016,12 @@ namespace crypto
}
EVP_CIPHER_CTX_free (ctx);
#else
chacha::Chacha20State state;
// generate one time poly key
chacha::Chacha20Init (state, nonce, key, 0);
uint64_t polyKey[8];
memset(polyKey, 0, sizeof(polyKey));
chacha::Chacha20Encrypt (state, (uint8_t *)polyKey, 64);
// create Poly1305 hash
Poly1305 polyHash (polyKey);
if (!ad) adLen = 0;
uint8_t padding[16]; memset (padding, 0, 16);
if (ad)
{
polyHash.Update (ad, adLen);// additional authenticated data
auto rem = adLen & 0x0F; // %16
if (rem)
{
// padding1
rem = 16 - rem;
polyHash.Update (padding, rem);
}
}
// encrypt/decrypt data and add to hash
Chacha20SetCounter (state, 1);
if (buf != msg)
memcpy (buf, msg, msgLen);
if (encrypt)
{
chacha::Chacha20Encrypt (state, buf, msgLen); // encrypt
polyHash.Update (buf, msgLen); // after encryption
}
else
{
polyHash.Update (buf, msgLen); // before decryption
chacha::Chacha20Encrypt (state, buf, msgLen); // decrypt
}
auto rem = msgLen & 0x0F; // %16
if (rem)
{
// padding2
rem = 16 - rem;
polyHash.Update (padding, rem);
}
// adLen and msgLen
htole64buf (padding, adLen);
htole64buf (padding + 8, msgLen);
polyHash.Update (padding, 16);
if (encrypt)
// calculate Poly1305 tag and write in after encrypted data
polyHash.Finish ((uint64_t *)(buf + msgLen));
else
{
uint64_t tag[4];
// calculate Poly1305 tag
polyHash.Finish (tag);
if (memcmp (tag, msg + msgLen, 16)) ret = false; // compare with provided
}
#endif
return ret;
}
void AEADChaCha20Poly1305Encrypt (const std::vector<std::pair<uint8_t *, size_t> >& bufs, const uint8_t * key, const uint8_t * nonce, uint8_t * mac)
{
if (bufs.empty ()) return;
#if OPENSSL_AEAD_CHACHA20_POLY1305
int outlen = 0;
EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new ();
EVP_EncryptInit_ex(ctx, EVP_chacha20_poly1305(), 0, 0, 0);
@ -1090,45 +1032,10 @@ namespace crypto
EVP_EncryptFinal_ex(ctx, NULL, &outlen);
EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_GET_TAG, 16, mac);
EVP_CIPHER_CTX_free (ctx);
#else
chacha::Chacha20State state;
// generate one time poly key
chacha::Chacha20Init (state, nonce, key, 0);
uint64_t polyKey[8];
memset(polyKey, 0, sizeof(polyKey));
chacha::Chacha20Encrypt (state, (uint8_t *)polyKey, 64);
Poly1305 polyHash (polyKey);
// encrypt buffers
Chacha20SetCounter (state, 1);
size_t size = 0;
for (const auto& it: bufs)
{
chacha::Chacha20Encrypt (state, it.first, it.second);
polyHash.Update (it.first, it.second); // after encryption
size += it.second;
}
// padding
uint8_t padding[16];
memset (padding, 0, 16);
auto rem = size & 0x0F; // %16
if (rem)
{
// padding2
rem = 16 - rem;
polyHash.Update (padding, rem);
}
// adLen and msgLen
// adLen is always zero
htole64buf (padding + 8, size);
polyHash.Update (padding, 16);
// MAC
polyHash.Finish ((uint64_t *)mac);
#endif
}
void ChaCha20 (const uint8_t * msg, size_t msgLen, const uint8_t * key, const uint8_t * nonce, uint8_t * out)
{
#if OPENSSL_AEAD_CHACHA20_POLY1305
EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new ();
uint32_t iv[4];
iv[0] = htole32 (1); memcpy (iv + 1, nonce, 12); // counter | nonce
@ -1137,12 +1044,6 @@ namespace crypto
EVP_EncryptUpdate(ctx, out, &outlen, msg, msgLen);
EVP_EncryptFinal_ex(ctx, NULL, &outlen);
EVP_CIPHER_CTX_free (ctx);
#else
chacha::Chacha20State state;
chacha::Chacha20Init (state, nonce, key, 1);
if (out != msg) memcpy (out, msg, msgLen);
chacha::Chacha20Encrypt (state, out, msgLen);
#endif
}
void HKDF (const uint8_t * salt, const uint8_t * key, size_t keyLen, const std::string& info,
@ -1295,9 +1196,6 @@ namespace crypto
void InitCrypto (bool precomputation, bool aesni, bool force)
{
i2p::cpu::Detect (aesni, force);
#if LEGACY_OPENSSL
SSL_library_init ();
#endif
/* auto numLocks = CRYPTO_num_locks();
for (int i = 0; i < numLocks; i++)
m_OpenSSLMutexes.emplace_back (new std::mutex);

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2013-2023, The PurpleI2P Project
* Copyright (c) 2013-2024, The PurpleI2P Project
*
* This file is part of Purple i2pd project and licensed under BSD3
*
@ -21,7 +21,6 @@
#include <openssl/sha.h>
#include <openssl/evp.h>
#include <openssl/rand.h>
#include <openssl/engine.h>
#include <openssl/opensslv.h>
#include "Base.h"
@ -29,24 +28,12 @@
#include "CPU.h"
// recognize openssl version and features
#if (defined(LIBRESSL_VERSION_NUMBER) && (LIBRESSL_VERSION_NUMBER >= 0x3050200fL)) // LibreSSL 3.5.2 and above
# define LEGACY_OPENSSL 0
#elif ((OPENSSL_VERSION_NUMBER < 0x010100000) || defined(LIBRESSL_VERSION_NUMBER)) // 1.0.2 and below or LibreSSL
# define LEGACY_OPENSSL 1
# define X509_getm_notBefore X509_get_notBefore
# define X509_getm_notAfter X509_get_notAfter
#else
# define LEGACY_OPENSSL 0
# if (OPENSSL_VERSION_NUMBER >= 0x010101000) // 1.1.1
# define OPENSSL_HKDF 1
# define OPENSSL_EDDSA 1
# define OPENSSL_X25519 1
# if (OPENSSL_VERSION_NUMBER != 0x030000000) // 3.0.0, regression in SipHash
# define OPENSSL_SIPHASH 1
# endif
# endif
# if !defined OPENSSL_NO_CHACHA && !defined OPENSSL_NO_POLY1305 // some builds might not include them
# define OPENSSL_AEAD_CHACHA20_POLY1305 1
#if (OPENSSL_VERSION_NUMBER >= 0x010101000) // 1.1.1
# define OPENSSL_HKDF 1
# define OPENSSL_EDDSA 1
# define OPENSSL_X25519 1
# if (!defined(LIBRESSL_VERSION_NUMBER) && (OPENSSL_VERSION_NUMBER != 0x030000000)) // 3.0.0, regression in SipHash, not implemented in LibreSSL
# define OPENSSL_SIPHASH 1
# endif
#endif
@ -312,79 +299,4 @@ namespace crypto
}
}
// take care about openssl below 1.1.0
#if LEGACY_OPENSSL
// define getters and setters introduced in 1.1.0
inline int DSA_set0_pqg(DSA *d, BIGNUM *p, BIGNUM *q, BIGNUM *g)
{
if (d->p) BN_free (d->p);
if (d->q) BN_free (d->q);
if (d->g) BN_free (d->g);
d->p = p; d->q = q; d->g = g; return 1;
}
inline int DSA_set0_key(DSA *d, BIGNUM *pub_key, BIGNUM *priv_key)
{
if (d->pub_key) BN_free (d->pub_key);
if (d->priv_key) BN_free (d->priv_key);
d->pub_key = pub_key; d->priv_key = priv_key; return 1;
}
inline void DSA_get0_key(const DSA *d, const BIGNUM **pub_key, const BIGNUM **priv_key)
{ *pub_key = d->pub_key; *priv_key = d->priv_key; }
inline int DSA_SIG_set0(DSA_SIG *sig, BIGNUM *r, BIGNUM *s)
{
if (sig->r) BN_free (sig->r);
if (sig->s) BN_free (sig->s);
sig->r = r; sig->s = s; return 1;
}
inline void DSA_SIG_get0(const DSA_SIG *sig, const BIGNUM **pr, const BIGNUM **ps)
{ *pr = sig->r; *ps = sig->s; }
inline int ECDSA_SIG_set0(ECDSA_SIG *sig, BIGNUM *r, BIGNUM *s)
{
if (sig->r) BN_free (sig->r);
if (sig->s) BN_free (sig->s);
sig->r = r; sig->s = s; return 1;
}
inline void ECDSA_SIG_get0(const ECDSA_SIG *sig, const BIGNUM **pr, const BIGNUM **ps)
{ *pr = sig->r; *ps = sig->s; }
inline int RSA_set0_key(RSA *r, BIGNUM *n, BIGNUM *e, BIGNUM *d)
{
if (r->n) BN_free (r->n);
if (r->e) BN_free (r->e);
if (r->d) BN_free (r->d);
r->n = n; r->e = e; r->d = d; return 1;
}
inline void RSA_get0_key(const RSA *r, const BIGNUM **n, const BIGNUM **e, const BIGNUM **d)
{ *n = r->n; *e = r->e; *d = r->d; }
inline int DH_set0_pqg(DH *dh, BIGNUM *p, BIGNUM *q, BIGNUM *g)
{
if (dh->p) BN_free (dh->p);
if (dh->q) BN_free (dh->q);
if (dh->g) BN_free (dh->g);
dh->p = p; dh->q = q; dh->g = g; return 1;
}
inline int DH_set0_key(DH *dh, BIGNUM *pub_key, BIGNUM *priv_key)
{
if (dh->pub_key) BN_free (dh->pub_key);
if (dh->priv_key) BN_free (dh->priv_key);
dh->pub_key = pub_key; dh->priv_key = priv_key; return 1;
}
inline void DH_get0_key(const DH *dh, const BIGNUM **pub_key, const BIGNUM **priv_key)
{ *pub_key = dh->pub_key; *priv_key = dh->priv_key; }
inline RSA *EVP_PKEY_get0_RSA(EVP_PKEY *pkey)
{ return pkey->pkey.rsa; }
inline EVP_MD_CTX *EVP_MD_CTX_new ()
{ return EVP_MD_CTX_create(); }
inline void EVP_MD_CTX_free (EVP_MD_CTX *ctx)
{ EVP_MD_CTX_destroy (ctx); }
// ssl
#define TLS_method TLSv1_method
#endif
#endif

View File

@ -288,8 +288,8 @@ namespace datagram
DatagramSession::DatagramSession(std::shared_ptr<i2p::client::ClientDestination> localDestination,
const i2p::data::IdentHash & remoteIdent) :
m_LocalDestination(localDestination),
m_RemoteIdent(remoteIdent),
m_LocalDestination(localDestination), m_RemoteIdent(remoteIdent),
m_LastUse (0), m_LastFlush (0),
m_RequestingLS(false)
{
}
@ -310,8 +310,12 @@ namespace datagram
if (msg || m_SendQueue.empty ())
m_SendQueue.push_back(msg);
// flush queue right away if full
if (!msg || m_SendQueue.size() >= DATAGRAM_SEND_QUEUE_MAX_SIZE)
if (!msg || m_SendQueue.size() >= DATAGRAM_SEND_QUEUE_MAX_SIZE ||
m_LastUse > m_LastFlush + DATAGRAM_MAX_FLUSH_INTERVAL)
{
FlushSendQueue();
m_LastFlush = m_LastUse;
}
}
DatagramSession::Info DatagramSession::GetSessionInfo() const
@ -344,7 +348,7 @@ namespace datagram
if(path)
path->updateTime = i2p::util::GetSecondsSinceEpoch ();
if (IsRatchets ())
SendMsg (nullptr); // send empty message in case if we have some data to send
SendMsg (nullptr); // send empty message in case if we don't have some data to send
}
std::shared_ptr<i2p::garlic::GarlicRoutingPath> DatagramSession::GetSharedRoutingPath ()
@ -383,8 +387,8 @@ namespace datagram
}
auto path = m_RoutingSession->GetSharedRoutingPath();
if (path && m_RoutingSession->IsRatchets () &&
m_LastUse > m_RoutingSession->GetLastActivityTimestamp ()*1000 + DATAGRAM_SESSION_PATH_TIMEOUT)
if (path && m_RoutingSession->IsRatchets () && (m_RoutingSession->CleanupUnconfirmedTags () ||
m_LastUse > m_RoutingSession->GetLastActivityTimestamp ()*1000 + DATAGRAM_SESSION_PATH_TIMEOUT))
{
m_RoutingSession->SetSharedRoutingPath (nullptr);
path = nullptr;
@ -413,7 +417,14 @@ namespace datagram
auto sz = ls.size();
if (sz)
{
auto idx = rand() % sz;
int idx = -1;
if (m_LocalDestination)
{
auto pool = m_LocalDestination->GetTunnelPool ();
if (pool)
idx = m_LocalDestination->GetTunnelPool ()->GetRng ()() % sz;
}
if (idx < 0) idx = rand () % sz;
path->remoteLease = ls[idx];
}
else
@ -439,7 +450,14 @@ namespace datagram
auto sz = ls.size();
if (sz)
{
auto idx = rand() % sz;
int idx = -1;
if (m_LocalDestination)
{
auto pool = m_LocalDestination->GetTunnelPool ();
if (pool)
idx = m_LocalDestination->GetTunnelPool ()->GetRng ()() % sz;
}
if (idx < 0) idx = rand () % sz;
path->remoteLease = ls[idx];
}
else

View File

@ -43,6 +43,7 @@ namespace datagram
const uint64_t DATAGRAM_SESSION_PATH_MIN_LIFETIME = 5 * 1000;
// max 64 messages buffered in send queue for each datagram session
const size_t DATAGRAM_SEND_QUEUE_MAX_SIZE = 64;
const uint64_t DATAGRAM_MAX_FLUSH_INTERVAL = 5; // in milliseconds
class DatagramSession : public std::enable_shared_from_this<DatagramSession>
{
@ -98,7 +99,7 @@ namespace datagram
std::shared_ptr<i2p::garlic::GarlicRoutingSession> m_RoutingSession;
std::vector<std::shared_ptr<i2p::garlic::GarlicRoutingSession> > m_PendingRoutingSessions;
std::vector<std::shared_ptr<I2NPMessage> > m_SendQueue;
uint64_t m_LastUse;
uint64_t m_LastUse, m_LastFlush; // milliseconds
bool m_RequestingLS;
};

View File

@ -801,7 +801,7 @@ namespace client
void LeaseSetDestination::RequestLeaseSet (const i2p::data::IdentHash& dest, RequestComplete requestComplete, std::shared_ptr<const i2p::data::BlindedPublicKey> requestedBlindedKey)
{
std::set<i2p::data::IdentHash> excluded;
std::unordered_set<i2p::data::IdentHash> excluded;
auto floodfill = i2p::data::netdb.GetClosestFloodfill (dest, excluded);
if (floodfill)
{
@ -979,8 +979,10 @@ namespace client
bool isPublic, const std::map<std::string, std::string> * params):
LeaseSetDestination (service, isPublic, params),
m_Keys (keys), m_StreamingAckDelay (DEFAULT_INITIAL_ACK_DELAY),
m_StreamingOutboundSpeed (DEFAULT_MAX_OUTBOUND_SPEED),
m_StreamingInboundSpeed (DEFAULT_MAX_INBOUND_SPEED),
m_IsStreamingAnswerPings (DEFAULT_ANSWER_PINGS), m_LastPort (0),
m_DatagramDestination (nullptr), m_RefCounter (0),
m_DatagramDestination (nullptr), m_RefCounter (0), m_LastPublishedTimestamp (0),
m_ReadyChecker(service)
{
if (keys.IsOfflineSignature () && GetLeaseSetType () == i2p::data::NETDB_STORE_TYPE_LEASESET)
@ -1047,6 +1049,12 @@ namespace client
auto it = params->find (I2CP_PARAM_STREAMING_INITIAL_ACK_DELAY);
if (it != params->end ())
m_StreamingAckDelay = std::stoi(it->second);
it = params->find (I2CP_PARAM_STREAMING_MAX_OUTBOUND_SPEED);
if (it != params->end ())
m_StreamingOutboundSpeed = std::stoi(it->second);
it = params->find (I2CP_PARAM_STREAMING_MAX_INBOUND_SPEED);
if (it != params->end ())
m_StreamingInboundSpeed = std::stoi(it->second);
it = params->find (I2CP_PARAM_STREAMING_ANSWER_PINGS);
if (it != params->end ())
m_IsStreamingAnswerPings = std::stoi (it->second); // 1 for true
@ -1097,7 +1105,6 @@ namespace client
void ClientDestination::Stop ()
{
LogPrint(eLogDebug, "Destination: Stopping destination ", GetIdentHash().ToBase32(), ".b32.i2p");
LeaseSetDestination::Stop ();
m_ReadyChecker.cancel();
LogPrint(eLogDebug, "Destination: -> Stopping Streaming Destination");
m_StreamingDestination->Stop ();
@ -1119,6 +1126,7 @@ namespace client
delete m_DatagramDestination;
m_DatagramDestination = nullptr;
}
LeaseSetDestination::Stop ();
LogPrint(eLogDebug, "Destination: -> Stopping done");
}
@ -1426,12 +1434,19 @@ namespace client
if (m_StandardEncryptionKey)
keySections.push_back ({m_StandardEncryptionKey->keyType, (uint16_t)m_StandardEncryptionKey->decryptor->GetPublicKeyLen (), m_StandardEncryptionKey->pub} );
auto publishedTimestamp = i2p::util::GetSecondsSinceEpoch ();
if (publishedTimestamp <= m_LastPublishedTimestamp)
{
LogPrint (eLogDebug, "Destination: LeaseSet update at the same second");
publishedTimestamp++; // force newer timestamp
}
bool isPublishedEncrypted = GetLeaseSetType () == i2p::data::NETDB_STORE_TYPE_ENCRYPTED_LEASESET2;
auto ls2 = std::make_shared<i2p::data::LocalLeaseSet2> (i2p::data::NETDB_STORE_TYPE_STANDARD_LEASESET2,
m_Keys, keySections, tunnels, IsPublic (), isPublishedEncrypted);
m_Keys, keySections, tunnels, IsPublic (), publishedTimestamp, isPublishedEncrypted);
if (isPublishedEncrypted) // encrypt if type 5
ls2 = std::make_shared<i2p::data::LocalEncryptedLeaseSet2> (ls2, m_Keys, GetAuthType (), m_AuthKeys);
leaseSet = ls2;
m_LastPublishedTimestamp = publishedTimestamp;
}
SetLeaseSet (leaseSet);
}

View File

@ -15,7 +15,7 @@
#include <memory>
#include <map>
#include <unordered_map>
#include <set>
#include <unordered_set>
#include <string>
#include <functional>
#include <boost/asio.hpp>
@ -84,6 +84,10 @@ namespace client
// streaming
const char I2CP_PARAM_STREAMING_INITIAL_ACK_DELAY[] = "i2p.streaming.initialAckDelay";
const int DEFAULT_INITIAL_ACK_DELAY = 200; // milliseconds
const char I2CP_PARAM_STREAMING_MAX_OUTBOUND_SPEED[] = "i2p.streaming.maxOutboundSpeed"; // bytes/sec
const int DEFAULT_MAX_OUTBOUND_SPEED = 1730000000; // no more than 1.73 Gbytes/s
const char I2CP_PARAM_STREAMING_MAX_INBOUND_SPEED[] = "i2p.streaming.maxInboundSpeed"; // bytes/sec
const int DEFAULT_MAX_INBOUND_SPEED = 1730000000; // no more than 1.73 Gbytes/s
const char I2CP_PARAM_STREAMING_ANSWER_PINGS[] = "i2p.streaming.answerPings";
const int DEFAULT_ANSWER_PINGS = true;
@ -97,7 +101,7 @@ namespace client
struct LeaseSetRequest
{
LeaseSetRequest (boost::asio::io_service& service): requestTime (0), requestTimeoutTimer (service) {};
std::set<i2p::data::IdentHash> excluded;
std::unordered_set<i2p::data::IdentHash> excluded;
uint64_t requestTime;
boost::asio::deadline_timer requestTimeoutTimer;
std::list<RequestComplete> requestComplete;
@ -195,7 +199,7 @@ namespace client
bool m_IsPublic;
uint32_t m_PublishReplyToken;
uint64_t m_LastSubmissionTime; // in seconds
std::set<i2p::data::IdentHash> m_ExcludedFloodfills; // for publishing
std::unordered_set<i2p::data::IdentHash> m_ExcludedFloodfills; // for publishing
boost::asio::deadline_timer m_PublishConfirmationTimer, m_PublishVerificationTimer,
m_PublishDelayTimer, m_CleanupTimer;
@ -259,6 +263,8 @@ namespace client
bool IsAcceptingStreams () const;
void AcceptOnce (const i2p::stream::StreamingDestination::Acceptor& acceptor);
int GetStreamingAckDelay () const { return m_StreamingAckDelay; }
int GetStreamingOutboundSpeed () const { return m_StreamingOutboundSpeed; }
int GetStreamingInboundSpeed () const { return m_StreamingInboundSpeed; }
bool IsStreamingAnswerPings () const { return m_IsStreamingAnswerPings; }
// datagram
@ -296,12 +302,15 @@ namespace client
std::unique_ptr<EncryptionKey> m_ECIESx25519EncryptionKey;
int m_StreamingAckDelay;
int m_StreamingOutboundSpeed;
int m_StreamingInboundSpeed;
bool m_IsStreamingAnswerPings;
std::shared_ptr<i2p::stream::StreamingDestination> m_StreamingDestination; // default
std::map<uint16_t, std::shared_ptr<i2p::stream::StreamingDestination> > m_StreamingDestinationsByPorts;
std::shared_ptr<i2p::stream::StreamingDestination> m_LastStreamingDestination; uint16_t m_LastPort; // for server tunnels
i2p::datagram::DatagramDestination * m_DatagramDestination;
int m_RefCounter; // how many clients(tunnels) use this destination
uint64_t m_LastPublishedTimestamp;
boost::asio::deadline_timer m_ReadyChecker;

View File

@ -229,6 +229,29 @@ namespace garlic
tagsetNsr->NextSessionTagRatchet ();
}
bool ECIESX25519AEADRatchetSession::MessageConfirmed (uint32_t msgID)
{
auto ret = GarlicRoutingSession::MessageConfirmed (msgID); // LeaseSet
if (m_AckRequestMsgID && m_AckRequestMsgID == msgID)
{
m_AckRequestMsgID = 0;
m_AckRequestNumAttempts = 0;
ret = true;
}
return ret;
}
bool ECIESX25519AEADRatchetSession::CleanupUnconfirmedTags ()
{
if (m_AckRequestMsgID && m_AckRequestNumAttempts > ECIESX25519_ACK_REQUEST_MAX_NUM_ATTEMPTS)
{
m_AckRequestMsgID = 0;
m_AckRequestNumAttempts = 0;
return true;
}
return false;
}
bool ECIESX25519AEADRatchetSession::HandleNewIncomingSession (const uint8_t * buf, size_t len)
{
if (!GetOwner ()) return false;
@ -333,8 +356,9 @@ namespace garlic
auto offset1 = offset;
for (auto i = 0; i < numAcks; i++)
{
offset1 += 2; // tagsetid
MessageConfirmed (bufbe16toh (buf + offset1)); offset1 += 2; // N
uint32_t tagsetid = bufbe16toh (buf + offset1); offset1 += 2; // tagsetid
uint16_t n = bufbe16toh (buf + offset1); offset1 += 2; // N
MessageConfirmed ((tagsetid << 16) + n); // msgid = (tagsetid << 16) + N
}
break;
}
@ -397,7 +421,6 @@ namespace garlic
{
uint16_t keyID = bufbe16toh (buf); buf += 2; // keyID
bool newKey = flag & ECIESX25519_NEXT_KEY_REQUEST_REVERSE_KEY_FLAG;
m_SendReverseKey = true;
if (!m_NextReceiveRatchet)
m_NextReceiveRatchet.reset (new DHRatchet ());
else
@ -409,15 +432,14 @@ namespace garlic
}
m_NextReceiveRatchet->keyID = keyID;
}
int tagsetID = 2*keyID;
if (newKey)
{
m_NextReceiveRatchet->key = i2p::transport::transports.GetNextX25519KeysPair ();
m_NextReceiveRatchet->newKey = true;
tagsetID++;
}
else
m_NextReceiveRatchet->newKey = false;
auto tagsetID = m_NextReceiveRatchet->GetReceiveTagSetID ();
if (flag & ECIESX25519_NEXT_KEY_KEY_PRESENT_FLAG)
memcpy (m_NextReceiveRatchet->remote, buf, 32);
@ -431,7 +453,9 @@ namespace garlic
GenerateMoreReceiveTags (newTagset, (GetOwner () && GetOwner ()->GetNumRatchetInboundTags () > 0) ?
GetOwner ()->GetNumRatchetInboundTags () : ECIESX25519_MAX_NUM_GENERATED_TAGS);
receiveTagset->Expire ();
LogPrint (eLogDebug, "Garlic: Next receive tagset ", tagsetID, " created");
m_SendReverseKey = true;
}
}
@ -753,7 +777,8 @@ namespace garlic
}
else
{
moreTags = ECIESX25519_MIN_NUM_GENERATED_TAGS + (index >> 2); // N/4
moreTags = (receiveTagset->GetTagSetID () > 0) ? ECIESX25519_MAX_NUM_GENERATED_TAGS : // for non first tagset
(ECIESX25519_MIN_NUM_GENERATED_TAGS + (index >> 1)); // N/2
if (moreTags > ECIESX25519_MAX_NUM_GENERATED_TAGS) moreTags = ECIESX25519_MAX_NUM_GENERATED_TAGS;
moreTags -= (receiveTagset->GetNextIndex () - index);
index -= ECIESX25519_MAX_NUM_GENERATED_TAGS; // trim behind
@ -780,6 +805,8 @@ namespace garlic
[[fallthrough]];
#endif
case eSessionStateEstablished:
if (m_SendReverseKey && receiveTagset->GetTagSetID () == m_NextReceiveRatchet->GetReceiveTagSetID ())
m_SendReverseKey = false; // tag received on new tagset
if (receiveTagset->IsNS ())
{
// our of sequence NSR
@ -857,6 +884,7 @@ namespace garlic
{
uint64_t ts = i2p::util::GetMillisecondsSinceEpoch ();
size_t payloadLen = 0;
bool sendAckRequest = false;
if (first) payloadLen += 7;// datatime
if (msg)
{
@ -875,13 +903,28 @@ namespace garlic
payloadLen += leaseSet->GetBufferLen () + DATABASE_STORE_HEADER_SIZE + 13;
if (!first)
{
// ack request
// ack request for LeaseSet
m_AckRequestMsgID = m_SendTagset->GetMsgID ();
sendAckRequest = true;
// update LeaseSet status
SetLeaseSetUpdateStatus (eLeaseSetSubmitted);
SetLeaseSetUpdateMsgID (m_SendTagset->GetNextIndex ());
SetLeaseSetUpdateMsgID (m_AckRequestMsgID);
SetLeaseSetSubmissionTime (ts);
payloadLen += 4;
}
}
if (!sendAckRequest && !first &&
((!m_AckRequestMsgID && ts > m_LastAckRequestSendTime + ECIESX25519_ACK_REQUEST_INTERVAL) || // regular request
(m_AckRequestMsgID && ts > m_LastAckRequestSendTime + LEASESET_CONFIRMATION_TIMEOUT))) // previous request failed. try again
{
// not LeaseSet
m_AckRequestMsgID = m_SendTagset->GetMsgID ();
if (m_AckRequestMsgID)
{
m_AckRequestNumAttempts++;
sendAckRequest = true;
}
}
if (sendAckRequest) payloadLen += 4;
if (m_AckRequests.size () > 0)
payloadLen += m_AckRequests.size ()*4 + 3;
if (m_SendReverseKey)
@ -933,16 +976,15 @@ namespace garlic
}
// LeaseSet
if (leaseSet)
{
offset += CreateLeaseSetClove (leaseSet, ts, payload + offset, payloadLen - offset);
if (!first)
{
// ack request
payload[offset] = eECIESx25519BlkAckRequest; offset++;
htobe16buf (payload + offset, 1); offset += 2;
payload[offset] = 0; offset++; // flags
}
}
// ack request
if (sendAckRequest)
{
payload[offset] = eECIESx25519BlkAckRequest; offset++;
htobe16buf (payload + offset, 1); offset += 2;
payload[offset] = 0; offset++; // flags
m_LastAckRequestSendTime = ts;
}
// msg
if (msg)
offset += CreateGarlicClove (msg, payload + offset, payloadLen - offset);
@ -977,7 +1019,6 @@ namespace garlic
memcpy (payload + offset, m_NextReceiveRatchet->key->GetPublicKey (), 32);
offset += 32; // public key
}
m_SendReverseKey = false;
}
if (m_SendForwardKey)
{

View File

@ -30,7 +30,9 @@ namespace garlic
const int ECIESX25519_SEND_INACTIVITY_TIMEOUT = 5000; // number of milliseconds we can send empty(pyaload only) packet after
const int ECIESX25519_SEND_EXPIRATION_TIMEOUT = 480; // in seconds
const int ECIESX25519_RECEIVE_EXPIRATION_TIMEOUT = 600; // in seconds
const int ECIESX25519_PREVIOUS_TAGSET_EXPIRATION_TIMEOUT = 180; // 180
const int ECIESX25519_PREVIOUS_TAGSET_EXPIRATION_TIMEOUT = 180; // in seconds
const int ECIESX25519_ACK_REQUEST_INTERVAL = 33000; // in milliseconds
const int ECIESX25519_ACK_REQUEST_MAX_NUM_ATTEMPTS = 3;
const int ECIESX25519_TAGSET_MAX_NUM_TAGS = 8192; // number of tags we request new tagset after
const int ECIESX25519_MIN_NUM_GENERATED_TAGS = 24;
const int ECIESX25519_MAX_NUM_GENERATED_TAGS = 320;
@ -57,6 +59,8 @@ namespace garlic
int GetTagSetID () const { return m_TagSetID; };
void SetTagSetID (int tagsetID) { m_TagSetID = tagsetID; };
uint32_t GetMsgID () const { return (m_TagSetID << 16) + m_NextIndex; }; // (tagsetid << 16) + N
private:
i2p::data::Tag<64> m_SessionTagKeyData;
@ -149,6 +153,7 @@ namespace garlic
std::shared_ptr<i2p::crypto::X25519Keys> key;
uint8_t remote[32]; // last remote public key
bool newKey = true;
int GetReceiveTagSetID () const { return newKey ? (2*keyID + 1) : 2*keyID; }
};
public:
@ -177,14 +182,16 @@ namespace garlic
bool IsReadyToSend () const { return m_State != eSessionStateNewSessionSent; };
bool IsTerminated () const { return m_IsTerminated; }
uint64_t GetLastActivityTimestamp () const { return m_LastActivityTimestamp; };
bool CleanupUnconfirmedTags (); // return true if unaswered Ack requests, called from I2CP
protected:
i2p::crypto::NoiseSymmetricState& GetNoiseState () { return *this; };
void SetNoiseState (const i2p::crypto::NoiseSymmetricState& state) { GetNoiseState () = state; };
void CreateNonce (uint64_t seqn, uint8_t * nonce);
void HandlePayload (const uint8_t * buf, size_t len, const std::shared_ptr<ReceiveRatchetTagSet>& receiveTagset, int index);
bool MessageConfirmed (uint32_t msgID);
private:
bool GenerateEphemeralKeysAndEncode (uint8_t * buf); // buf is 32 bytes
@ -218,11 +225,15 @@ namespace garlic
m_LastSentTimestamp = 0; // in milliseconds
std::shared_ptr<RatchetTagSet> m_SendTagset, m_NSRSendTagset;
std::unique_ptr<i2p::data::IdentHash> m_Destination;// TODO: might not need it
std::list<std::pair<uint16_t, int> > m_AckRequests; // (tagsetid, index)
std::list<std::pair<uint16_t, int> > m_AckRequests; // incoming (tagsetid, index)
bool m_SendReverseKey = false, m_SendForwardKey = false, m_IsTerminated = false;
std::unique_ptr<DHRatchet> m_NextReceiveRatchet, m_NextSendRatchet;
uint8_t m_PaddingSizes[32], m_NextPaddingSize;
uint64_t m_LastAckRequestSendTime = 0; // milliseconds
uint32_t m_AckRequestMsgID = 0;
int m_AckRequestNumAttempts = 0;
public:
// for HTTP only

View File

@ -54,7 +54,11 @@ namespace fs {
const std::string GetUTF8DataDir () {
#ifdef _WIN32
#if (BOOST_VERSION >= 108500)
boost::filesystem::path path (dataDir);
#else
boost::filesystem::wpath path (dataDir);
#endif
auto loc = boost::filesystem::path::imbue(std::locale( std::locale(), new std::codecvt_utf8_utf16<wchar_t>() ) ); // convert path to UTF-8
auto dataDirUTF8 = path.string();
boost::filesystem::path::imbue(loc); // Return locale settings back
@ -87,7 +91,11 @@ namespace fs {
}
else
{
#if (BOOST_VERSION >= 108500)
dataDir = boost::filesystem::path(commonAppData).string() + "\\" + appName;
#else
dataDir = boost::filesystem::wpath(commonAppData).string() + "\\" + appName;
#endif
}
#else
dataDir = "/var/lib/" + appName;
@ -112,7 +120,11 @@ namespace fs {
}
else
{
#if (BOOST_VERSION >= 108500)
auto execPath = boost::filesystem::path(localAppData).parent_path();
#else
auto execPath = boost::filesystem::wpath(localAppData).parent_path();
#endif
// if config file exists in .exe's folder use it
if(boost::filesystem::exists(execPath/"i2pd.conf")) // TODO: magic string
@ -131,7 +143,11 @@ namespace fs {
}
else
{
#if (BOOST_VERSION >= 108500)
dataDir = boost::filesystem::path(localAppData).string() + "\\" + appName;
#else
dataDir = boost::filesystem::wpath(localAppData).string() + "\\" + appName;
#endif
}
}
}
@ -257,19 +273,19 @@ namespace fs {
if (boost::filesystem::exists(p))
continue;
#if TARGET_OS_SIMULATOR
// ios simulator fs says it is case sensitive, but it is not
boost::system::error_code ec;
if (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__ );
}
// 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 */
#endif
return false;

View File

@ -45,22 +45,17 @@ namespace garlic
{
if (!m_SharedRoutingPath) return nullptr;
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
if (m_SharedRoutingPath->numTimesUsed >= ROUTING_PATH_MAX_NUM_TIMES_USED ||
!m_SharedRoutingPath->outboundTunnel->IsEstablished () ||
if (!m_SharedRoutingPath->outboundTunnel->IsEstablished () ||
ts*1000LL > m_SharedRoutingPath->remoteLease->endDate ||
ts > m_SharedRoutingPath->updateTime + ROUTING_PATH_EXPIRATION_TIMEOUT)
m_SharedRoutingPath = nullptr;
if (m_SharedRoutingPath) m_SharedRoutingPath->numTimesUsed++;
return m_SharedRoutingPath;
}
void GarlicRoutingSession::SetSharedRoutingPath (std::shared_ptr<GarlicRoutingPath> path)
{
if (path && path->outboundTunnel && path->remoteLease)
{
path->updateTime = i2p::util::GetSecondsSinceEpoch ();
path->numTimesUsed = 0;
}
else
path = nullptr;
m_SharedRoutingPath = path;

View File

@ -51,8 +51,7 @@ namespace garlic
const int OUTGOING_TAGS_EXPIRATION_TIMEOUT = 720; // 12 minutes
const int OUTGOING_TAGS_CONFIRMATION_TIMEOUT = 10; // 10 seconds
const int LEASESET_CONFIRMATION_TIMEOUT = 4000; // in milliseconds
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_EXPIRATION_TIMEOUT = 120; // in seconds
struct SessionTag: public i2p::data::Tag<32>
{
@ -89,7 +88,6 @@ namespace garlic
std::shared_ptr<const i2p::data::Lease> remoteLease;
int rtt; // RTT
uint32_t updateTime; // seconds since epoch
int numTimesUsed;
};
class GarlicDestination;
@ -111,7 +109,7 @@ namespace garlic
GarlicRoutingSession ();
virtual ~GarlicRoutingSession ();
virtual std::shared_ptr<I2NPMessage> WrapSingleMessage (std::shared_ptr<const I2NPMessage> msg) = 0;
virtual bool CleanupUnconfirmedTags () { return false; }; // for I2CP, override in ElGamalAESSession
virtual bool CleanupUnconfirmedTags () { return false; }; // for I2CP, override in ElGamalAESSession and ECIESX25519AEADRatchetSession
virtual bool MessageConfirmed (uint32_t msgID);
virtual bool IsRatchets () const { return false; };
virtual bool IsReadyToSend () const { return true; };

View File

@ -147,7 +147,7 @@ namespace i2p
}
std::shared_ptr<I2NPMessage> CreateRouterInfoDatabaseLookupMsg (const uint8_t * key, const uint8_t * from,
uint32_t replyTunnelID, bool exploratory, std::set<i2p::data::IdentHash> * excludedPeers)
uint32_t replyTunnelID, bool exploratory, std::unordered_set<i2p::data::IdentHash> * excludedPeers)
{
int cnt = excludedPeers ? excludedPeers->size () : 0;
auto m = cnt > 7 ? NewI2NPMessage () : NewI2NPShortMessage ();
@ -192,7 +192,7 @@ namespace i2p
}
std::shared_ptr<I2NPMessage> CreateLeaseSetDatabaseLookupMsg (const i2p::data::IdentHash& dest,
const std::set<i2p::data::IdentHash>& excludedFloodfills,
const std::unordered_set<i2p::data::IdentHash>& excludedFloodfills,
std::shared_ptr<const i2p::tunnel::InboundTunnel> replyTunnel, const uint8_t * replyKey,
const uint8_t * replyTag, bool replyECIES)
{
@ -862,12 +862,14 @@ namespace i2p
break;
}
case eI2NPDatabaseStore:
case eI2NPDatabaseSearchReply:
// forward to netDb if came directly or through exploratory tunnel as response to our request
if (!msg->from || !msg->from->GetTunnelPool () || msg->from->GetTunnelPool ()->IsExploratory ())
i2p::data::netdb.PostI2NPMsg (msg);
break;
case eI2NPDatabaseSearchReply:
if (!msg->from || !msg->from->GetTunnelPool () || msg->from->GetTunnelPool ()->IsExploratory ())
i2p::data::netdb.PostDatabaseSearchReplyMsg (msg);
break;
case eI2NPDatabaseLookup:
// forward to netDb if floodfill and came directly
if (!msg->from && i2p::context.IsFloodfill ())

View File

@ -11,7 +11,7 @@
#include <inttypes.h>
#include <string.h>
#include <set>
#include <unordered_set>
#include <memory>
#include <functional>
#include "Crypto.h"
@ -294,9 +294,9 @@ namespace tunnel
std::shared_ptr<I2NPMessage> CreateTunnelTestMsg (uint32_t msgID);
std::shared_ptr<I2NPMessage> CreateDeliveryStatusMsg (uint32_t msgID);
std::shared_ptr<I2NPMessage> CreateRouterInfoDatabaseLookupMsg (const uint8_t * key, const uint8_t * from,
uint32_t replyTunnelID, bool exploratory = false, std::set<i2p::data::IdentHash> * excludedPeers = nullptr);
uint32_t replyTunnelID, bool exploratory = false, std::unordered_set<i2p::data::IdentHash> * excludedPeers = nullptr);
std::shared_ptr<I2NPMessage> CreateLeaseSetDatabaseLookupMsg (const i2p::data::IdentHash& dest,
const std::set<i2p::data::IdentHash>& excludedFloodfills,
const std::unordered_set<i2p::data::IdentHash>& excludedFloodfills,
std::shared_ptr<const i2p::tunnel::InboundTunnel> replyTunnel,
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);

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2013-2023, The PurpleI2P Project
* Copyright (c) 2013-2024, The PurpleI2P Project
*
* This file is part of Purple i2pd project and licensed under BSD3
*
@ -420,6 +420,14 @@ namespace data
return CreateEncryptor (GetCryptoKeyType (), key);
}
size_t GetIdentityBufferLen (const uint8_t * buf, size_t len)
{
if (len < DEFAULT_IDENTITY_SIZE) return 0;
size_t l = DEFAULT_IDENTITY_SIZE + bufbe16toh (buf + DEFAULT_IDENTITY_SIZE - 2);
if (l > len) return 0;
return l;
}
PrivateKeys& PrivateKeys::operator=(const Keys& keys)
{
m_Public = std::make_shared<IdentityEx>(Identity (keys));
@ -479,7 +487,12 @@ namespace data
{
// offline information
const uint8_t * offlineInfo = buf + ret;
ret += 4; // expires timestamp
uint32_t expires = bufbe32toh (buf + ret); ret += 4; // expires timestamp
if (expires < i2p::util::GetSecondsSinceEpoch ())
{
LogPrint (eLogError, "Identity: Offline signature expired");
return 0;
}
SigningKeyType keyType = bufbe16toh (buf + ret); ret += 2; // key type
std::unique_ptr<i2p::crypto::Verifier> transientVerifier (IdentityEx::CreateVerifier (keyType));
if (!transientVerifier) return 0;
@ -790,11 +803,14 @@ namespace data
return keys;
}
IdentHash CreateRoutingKey (const IdentHash& ident)
IdentHash CreateRoutingKey (const IdentHash& ident, bool nextDay)
{
uint8_t buf[41]; // ident + yyyymmdd
memcpy (buf, (const uint8_t *)ident, 32);
i2p::util::GetCurrentDate ((char *)(buf + 32));
if (nextDay)
i2p::util::GetNextDayDate ((char *)(buf + 32));
else
i2p::util::GetCurrentDate ((char *)(buf + 32));
IdentHash key;
SHA256(buf, 40, key);
return key;

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2013-2023, The PurpleI2P Project
* Copyright (c) 2013-2024, The PurpleI2P Project
*
* This file is part of Purple i2pd project and licensed under BSD3
*
@ -136,6 +136,8 @@ namespace data
uint8_t m_ExtendedBuffer[MAX_EXTENDED_BUFFER_SIZE];
};
size_t GetIdentityBufferLen (const uint8_t * buf, size_t len); // return actual identity length in buffer
class PrivateKeys // for eepsites
{
public:
@ -206,7 +208,7 @@ namespace data
bool operator< (const XORMetric& other) const { return memcmp (metric, other.metric, 32) < 0; };
};
IdentHash CreateRoutingKey (const IdentHash& ident);
IdentHash CreateRoutingKey (const IdentHash& ident, bool nextDay = false);
XORMetric operator^(const IdentHash& key1, const IdentHash& key2);
// destination for delivery instructions

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2013-2023, The PurpleI2P Project
* Copyright (c) 2013-2024, The PurpleI2P Project
*
* This file is part of Purple i2pd project and licensed under BSD3
*
@ -728,25 +728,41 @@ namespace data
memset (m_Buffer + offset, 0, signingKeyLen);
offset += signingKeyLen;
// num leases
auto numLeasesPos = offset;
m_Buffer[offset] = num;
offset++;
// leases
m_Leases = m_Buffer + offset;
auto currentTime = i2p::util::GetMillisecondsSinceEpoch ();
int skipped = 0;
for (int i = 0; i < num; i++)
{
uint64_t ts = tunnels[i]->GetCreationTime () + i2p::tunnel::TUNNEL_EXPIRATION_TIMEOUT - i2p::tunnel::TUNNEL_EXPIRATION_THRESHOLD; // 1 minute before expiration
ts *= 1000; // in milliseconds
if (ts <= currentTime)
{
// already expired, skip
skipped++;
continue;
}
if (ts > m_ExpirationTime) m_ExpirationTime = ts;
// make sure leaseset is newer than previous, but adding some time to expiration date
ts += (currentTime - tunnels[i]->GetCreationTime ()*1000LL)*2/i2p::tunnel::TUNNEL_EXPIRATION_TIMEOUT; // up to 2 secs
memcpy (m_Buffer + offset, tunnels[i]->GetNextIdentHash (), 32);
offset += 32; // gateway id
htobe32buf (m_Buffer + offset, tunnels[i]->GetNextTunnelID ());
offset += 4; // tunnel id
uint64_t ts = tunnels[i]->GetCreationTime () + i2p::tunnel::TUNNEL_EXPIRATION_TIMEOUT - i2p::tunnel::TUNNEL_EXPIRATION_THRESHOLD; // 1 minute before expiration
ts *= 1000; // in milliseconds
if (ts > m_ExpirationTime) m_ExpirationTime = ts;
// make sure leaseset is newer than previous, but adding some time to expiration date
ts += (currentTime - tunnels[i]->GetCreationTime ()*1000LL)*2/i2p::tunnel::TUNNEL_EXPIRATION_TIMEOUT; // up to 2 secs
htobe64buf (m_Buffer + offset, ts);
offset += 8; // end date
}
if (skipped > 0)
{
// adjust num leases
if (skipped > num) skipped = num;
num -= skipped;
m_BufferLen -= skipped*LEASE_SIZE;
m_Buffer[numLeasesPos] = num;
}
// we don't sign it yet. must be signed later on
}
@ -808,7 +824,7 @@ namespace data
LocalLeaseSet2::LocalLeaseSet2 (uint8_t storeType, const i2p::data::PrivateKeys& keys,
const KeySections& encryptionKeys, const std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> >& tunnels,
bool isPublic, bool isPublishedEncrypted):
bool isPublic, uint64_t publishedTimestamp, bool isPublishedEncrypted):
LocalLeaseSet (keys.GetPublic (), nullptr, 0)
{
auto identity = keys.GetPublic ();
@ -837,8 +853,7 @@ namespace data
m_Buffer[0] = storeType;
// LS2 header
auto offset = identity->ToBuffer (m_Buffer + 1, m_BufferLen) + 1;
auto timestamp = i2p::util::GetSecondsSinceEpoch ();
htobe32buf (m_Buffer + offset, timestamp); offset += 4; // published timestamp (seconds)
htobe32buf (m_Buffer + offset, publishedTimestamp); offset += 4; // published timestamp (seconds)
uint8_t * expiresBuf = m_Buffer + offset; offset += 2; // expires, fill later
htobe16buf (m_Buffer + offset, flags); offset += 2; // flags
if (keys.IsOfflineSignature ())
@ -859,29 +874,44 @@ namespace data
}
// leases
uint32_t expirationTime = 0; // in seconds
int skipped = 0; auto numLeasesPos = offset;
m_Buffer[offset] = num; offset++; // num leases
for (int i = 0; i < num; i++)
{
auto ts = tunnels[i]->GetCreationTime () + i2p::tunnel::TUNNEL_EXPIRATION_TIMEOUT - i2p::tunnel::TUNNEL_EXPIRATION_THRESHOLD; // in seconds, 1 minute before expiration
if (ts <= publishedTimestamp)
{
// already expired, skip
skipped++;
continue;
}
if (ts > expirationTime) expirationTime = ts;
memcpy (m_Buffer + offset, tunnels[i]->GetNextIdentHash (), 32);
offset += 32; // gateway id
htobe32buf (m_Buffer + offset, tunnels[i]->GetNextTunnelID ());
offset += 4; // tunnel id
auto ts = tunnels[i]->GetCreationTime () + i2p::tunnel::TUNNEL_EXPIRATION_TIMEOUT - i2p::tunnel::TUNNEL_EXPIRATION_THRESHOLD; // in seconds, 1 minute before expiration
if (ts > expirationTime) expirationTime = ts;
htobe32buf (m_Buffer + offset, ts);
offset += 4; // end date
}
if (skipped > 0)
{
// adjust num leases
if (skipped > num) skipped = num;
num -= skipped;
m_BufferLen -= skipped*LEASE2_SIZE;
m_Buffer[numLeasesPos] = num;
}
// update expiration
if (expirationTime)
{
SetExpirationTime (expirationTime*1000LL);
auto expires = (int)expirationTime - timestamp;
auto expires = (int)expirationTime - publishedTimestamp;
htobe16buf (expiresBuf, expires > 0 ? expires : 0);
}
else
{
// no tunnels or withdraw
SetExpirationTime (timestamp*1000LL);
SetExpirationTime (publishedTimestamp*1000LL);
memset (expiresBuf, 0, 2); // expires immeditely
}
// sign

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2013-2023, The PurpleI2P Project
* Copyright (c) 2013-2024, The PurpleI2P Project
*
* This file is part of Purple i2pd project and licensed under BSD3
*
@ -256,7 +256,8 @@ namespace data
LocalLeaseSet2 (uint8_t storeType, const i2p::data::PrivateKeys& keys,
const KeySections& encryptionKeys,
const std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> >& tunnels,
bool isPublic, bool isPublishedEncrypted = false);
bool isPublic, uint64_t publishedTimestamp,
bool isPublishedEncrypted = false);
LocalLeaseSet2 (uint8_t storeType, std::shared_ptr<const IdentityEx> identity, const uint8_t * buf, size_t len); // from I2CP

View File

@ -108,10 +108,10 @@ namespace transport
m_EphemeralKeys = i2p::transport::transports.GetNextX25519KeysPair ();
}
void NTCP2Establisher::CreateSessionRequestMessage ()
void NTCP2Establisher::CreateSessionRequestMessage (std::mt19937& rng)
{
// create buffer and fill padding
auto paddingLength = rand () % (NTCP2_SESSION_REQUEST_MAX_SIZE - 64); // message length doesn't exceed 287 bytes
auto paddingLength = rng () % (NTCP2_SESSION_REQUEST_MAX_SIZE - 64); // message length doesn't exceed 287 bytes
m_SessionRequestBufferLen = paddingLength + 64;
RAND_bytes (m_SessionRequestBuffer + 64, paddingLength);
// encrypt X
@ -129,7 +129,8 @@ namespace transport
options[1] = 2; // ver
htobe16buf (options + 2, paddingLength); // padLen
// m3p2Len
auto bufLen = i2p::context.GetRouterInfo ().GetBufferLen ();
auto riBuffer = i2p::context.CopyRouterInfoBuffer ();
auto bufLen = riBuffer->GetBufferLen ();
m3p2Len = bufLen + 4 + 16; // (RI header + RI + MAC for now) TODO: implement options
htobe16buf (options + 4, m3p2Len);
// fill m3p2 payload (RouterInfo block)
@ -138,7 +139,7 @@ namespace transport
m3p2[0] = eNTCP2BlkRouterInfo; // block
htobe16buf (m3p2 + 1, bufLen + 1); // flag + RI
m3p2[3] = 0; // flag
memcpy (m3p2 + 4, i2p::context.GetRouterInfo ().GetBuffer (), bufLen); // TODO: own RI should be protected by mutex
memcpy (m3p2 + 4, riBuffer->data (), bufLen); // TODO: eliminate extra copy
// 2 bytes reserved
htobe32buf (options + 8, (i2p::util::GetMillisecondsSinceEpoch () + 500)/1000); // tsA, rounded to seconds
// 4 bytes reserved
@ -148,9 +149,9 @@ namespace transport
i2p::crypto::AEADChaCha20Poly1305 (options, 16, GetH (), 32, GetK (), nonce, m_SessionRequestBuffer + 32, 32, true); // encrypt
}
void NTCP2Establisher::CreateSessionCreatedMessage ()
void NTCP2Establisher::CreateSessionCreatedMessage (std::mt19937& rng)
{
auto paddingLen = rand () % (NTCP2_SESSION_CREATED_MAX_SIZE - 64);
auto paddingLen = rng () % (NTCP2_SESSION_CREATED_MAX_SIZE - 64);
m_SessionCreatedBufferLen = paddingLen + 64;
RAND_bytes (m_SessionCreatedBuffer + 64, paddingLen);
// encrypt Y
@ -348,7 +349,7 @@ namespace transport
LogPrint (eLogWarning, "NTCP2: Missing NTCP2 address");
}
m_NextRouterInfoResendTime = i2p::util::GetSecondsSinceEpoch () + NTCP2_ROUTERINFO_RESEND_INTERVAL +
rand ()%NTCP2_ROUTERINFO_RESEND_INTERVAL_THRESHOLD;
m_Server.GetRng ()() % NTCP2_ROUTERINFO_RESEND_INTERVAL_THRESHOLD;
}
NTCP2Session::~NTCP2Session ()
@ -410,7 +411,8 @@ namespace transport
{
m_IsEstablished = true;
m_Establisher.reset (nullptr);
SetTerminationTimeout (NTCP2_TERMINATION_TIMEOUT);
SetTerminationTimeout (NTCP2_TERMINATION_TIMEOUT + m_Server.GetRng ()() % NTCP2_TERMINATION_TIMEOUT_VARIANCE);
SendQueue ();
transports.PeerConnected (shared_from_this ());
}
@ -462,7 +464,7 @@ namespace transport
void NTCP2Session::SendSessionRequest ()
{
m_Establisher->CreateSessionRequestMessage ();
m_Establisher->CreateSessionRequestMessage (m_Server.GetRng ());
// send message
m_HandshakeInterval = i2p::util::GetMillisecondsSinceEpoch ();
boost::asio::async_write (m_Socket, boost::asio::buffer (m_Establisher->m_SessionRequestBuffer, m_Establisher->m_SessionRequestBufferLen), boost::asio::transfer_all (),
@ -540,7 +542,7 @@ namespace transport
void NTCP2Session::SendSessionCreated ()
{
m_Establisher->CreateSessionCreatedMessage ();
m_Establisher->CreateSessionCreatedMessage (m_Server.GetRng ());
// send message
m_HandshakeInterval = i2p::util::GetMillisecondsSinceEpoch ();
boost::asio::async_write (m_Socket, boost::asio::buffer (m_Establisher->m_SessionCreatedBuffer, m_Establisher->m_SessionCreatedBufferLen), boost::asio::transfer_all (),
@ -1119,7 +1121,7 @@ namespace transport
if (GetLastActivityTimestamp () > m_NextRouterInfoResendTime)
{
m_NextRouterInfoResendTime += NTCP2_ROUTERINFO_RESEND_INTERVAL +
rand ()%NTCP2_ROUTERINFO_RESEND_INTERVAL_THRESHOLD;
m_Server.GetRng ()() % NTCP2_ROUTERINFO_RESEND_INTERVAL_THRESHOLD;
SendRouterInfo ();
}
else
@ -1132,7 +1134,7 @@ namespace transport
void NTCP2Session::SendQueue ()
{
if (!m_SendQueue.empty ())
if (!m_SendQueue.empty () && m_IsEstablished)
{
std::vector<std::shared_ptr<I2NPMessage> > msgs;
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
@ -1169,6 +1171,21 @@ 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)
{
if (len < 3) return 0;
@ -1200,7 +1217,8 @@ namespace transport
void NTCP2Session::SendRouterInfo ()
{
if (!IsEstablished ()) return;
auto riLen = i2p::context.GetRouterInfo ().GetBufferLen ();
auto riBuffer = i2p::context.CopyRouterInfoBuffer ();
auto riLen = riBuffer->GetBufferLen ();
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
// DateTime block
@ -1211,7 +1229,7 @@ namespace transport
m_NextSendBuffer[9] = eNTCP2BlkRouterInfo;
htobe16buf (m_NextSendBuffer + 10, riLen + 1); // size
m_NextSendBuffer[12] = 0; // flag
memcpy (m_NextSendBuffer + 13, i2p::context.GetRouterInfo ().GetBuffer (), riLen);
memcpy (m_NextSendBuffer + 13, riBuffer->data (), riLen); // TODO: eliminate extra copy
// padding block
auto paddingSize = CreatePaddingBlock (payloadLen, m_NextSendBuffer + 2 + payloadLen, 64);
payloadLen += paddingSize;
@ -1261,7 +1279,7 @@ namespace transport
else
m_SendQueue.push_back (std::move (it));
if (!m_IsSending)
if (!m_IsSending && m_IsEstablished)
SendQueue ();
else if (m_SendQueue.size () > NTCP2_MAX_OUTGOING_QUEUE_SIZE)
{
@ -1280,7 +1298,8 @@ namespace transport
NTCP2Server::NTCP2Server ():
RunnableServiceWithWork ("NTCP2"), m_TerminationTimer (GetService ()),
m_ProxyType(eNoProxy), m_Resolver(GetService ())
m_ProxyType(eNoProxy), m_Resolver(GetService ()),
m_Rng(i2p::util::GetMonotonicMicroseconds ()%1000000LL)
{
}
@ -1424,6 +1443,7 @@ namespace transport
{
// replace by new session
auto s = it->second;
s->MoveSendQueue (session);
m_NTCP2Sessions.erase (it);
s->Terminate ();
}
@ -1622,7 +1642,8 @@ namespace transport
void NTCP2Server::ScheduleTermination ()
{
m_TerminationTimer.expires_from_now (boost::posix_time::seconds(NTCP2_TERMINATION_CHECK_TIMEOUT));
m_TerminationTimer.expires_from_now (boost::posix_time::seconds(
NTCP2_TERMINATION_CHECK_TIMEOUT + m_Rng () % NTCP2_TERMINATION_CHECK_TIMEOUT_VARIANCE));
m_TerminationTimer.async_wait (std::bind (&NTCP2Server::HandleTerminationTimer,
this, std::placeholders::_1));
}

View File

@ -14,6 +14,7 @@
#include <list>
#include <map>
#include <array>
#include <random>
#include <openssl/bn.h>
#include <openssl/evp.h>
#include <boost/asio.hpp>
@ -35,8 +36,10 @@ namespace transport
const int NTCP2_CONNECT_TIMEOUT = 5; // 5 seconds
const int NTCP2_ESTABLISH_TIMEOUT = 10; // 10 seconds
const int NTCP2_TERMINATION_TIMEOUT = 120; // 2 minutes
const int NTCP2_TERMINATION_CHECK_TIMEOUT = 30; // 30 seconds
const int NTCP2_TERMINATION_TIMEOUT = 115; // 2 minutes - 5 seconds
const int NTCP2_TERMINATION_TIMEOUT_VARIANCE = 10; // 10 seconds
const int NTCP2_TERMINATION_CHECK_TIMEOUT = 28; // 28 seconds
const int NTCP2_TERMINATION_CHECK_TIMEOUT_VARIANCE = 5; // 5 seconds
const int NTCP2_RECEIVE_BUFFER_DELETION_TIMEOUT = 3; // 3 seconds
const int NTCP2_ROUTERINFO_RESEND_INTERVAL = 25*60; // 25 minuntes in seconds
const int NTCP2_ROUTERINFO_RESEND_INTERVAL_THRESHOLD = 25*60; // 25 minuntes
@ -103,8 +106,8 @@ namespace transport
void KeyDerivationFunction2 (const uint8_t * sessionRequest, size_t sessionRequestLen, const uint8_t * epub); // for SessionCreate
void CreateEphemeralKey ();
void CreateSessionRequestMessage ();
void CreateSessionCreatedMessage ();
void CreateSessionRequestMessage (std::mt19937& rng);
void CreateSessionCreatedMessage (std::mt19937& rng);
void CreateSessionConfirmedMessagePart1 (const uint8_t * nonce);
void CreateSessionConfirmedMessagePart2 (const uint8_t * nonce);
@ -151,7 +154,8 @@ namespace transport
void SendLocalRouterInfo (bool update) override; // after handshake or by update
void SendI2NPMessages (const std::vector<std::shared_ptr<I2NPMessage> >& msgs) override;
void MoveSendQueue (std::shared_ptr<NTCP2Session> other);
private:
void Established ();
@ -247,6 +251,7 @@ namespace transport
void Start ();
void Stop ();
boost::asio::io_service& GetService () { return GetIOService (); };
std::mt19937& GetRng () { return m_Rng; };
bool AddNTCP2Session (std::shared_ptr<NTCP2Session> session, bool incoming = false);
void RemoveNTCP2Session (std::shared_ptr<NTCP2Session> session);
@ -285,6 +290,7 @@ namespace transport
boost::asio::ip::tcp::resolver m_Resolver;
std::unique_ptr<boost::asio::ip::tcp::endpoint> m_ProxyEndpoint;
std::shared_ptr<boost::asio::ip::tcp::endpoint> m_Address4, m_Address6, m_YggdrasilAddress;
std::mt19937 m_Rng;
public:

View File

@ -9,6 +9,8 @@
#include <string.h>
#include <fstream>
#include <vector>
#include <map>
#include <random>
#include <boost/asio.hpp>
#include <stdexcept>
@ -36,7 +38,9 @@ namespace data
{
NetDb netdb;
NetDb::NetDb (): m_IsRunning (false), m_Thread (nullptr), m_Reseeder (nullptr), m_Storage("netDb", "r", "routerInfo-", "dat"), m_PersistProfiles (true)
NetDb::NetDb (): m_IsRunning (false), m_Thread (nullptr), m_Reseeder (nullptr),
m_Storage("netDb", "r", "routerInfo-", "dat"), m_PersistProfiles (true),
m_LastExploratorySelectionUpdateTime (0)
{
}
@ -54,6 +58,12 @@ namespace data
m_Families.LoadCertificates ();
Load ();
if (!m_Requests)
{
m_Requests = std::make_shared<NetDbRequests>();
m_Requests->Start ();
}
uint16_t threshold; i2p::config::GetOption("reseed.threshold", threshold);
if (m_RouterInfos.size () < threshold || m_Floodfills.GetSize () < NETDB_MIN_FLOODFILLS) // reseed if # of router less than threshold or too few floodfiils
{
@ -75,13 +85,15 @@ namespace data
m_Floodfills.Insert (i2p::context.GetSharedRouterInfo ());
i2p::config::GetOption("persist.profiles", m_PersistProfiles);
m_IsRunning = true;
m_Thread = new std::thread (std::bind (&NetDb::Run, this));
}
void NetDb::Stop ()
{
if (m_Requests)
m_Requests->Stop ();
if (m_IsRunning)
{
if (m_PersistProfiles)
@ -98,23 +110,23 @@ namespace data
m_Thread = 0;
}
m_LeaseSets.clear();
m_Requests.Stop ();
}
m_Requests = nullptr;
}
void NetDb::Run ()
{
i2p::util::SetThreadName("NetDB");
uint64_t lastManage = 0, lastExploratory = 0, lastManageRequest = 0;
uint64_t lastProfilesCleanup = i2p::util::GetSecondsSinceEpoch ();
int16_t profilesCleanupVariance = 0;
uint64_t lastManage = 0;
uint64_t lastProfilesCleanup = i2p::util::GetMonotonicMilliseconds (), lastObsoleteProfilesCleanup = lastProfilesCleanup;
int16_t profilesCleanupVariance = 0, obsoleteProfilesCleanVariance = 0;
while (m_IsRunning)
{
try
{
auto msg = m_Queue.GetNextWithTimeout (15000); // 15 sec
auto msg = m_Queue.GetNextWithTimeout (1000); // 1 sec
if (msg)
{
int numMsgs = 0;
@ -126,9 +138,6 @@ namespace data
case eI2NPDatabaseStore:
HandleDatabaseStoreMsg (msg);
break;
case eI2NPDatabaseSearchReply:
HandleDatabaseSearchReplyMsg (msg);
break;
case eI2NPDatabaseLookup:
HandleDatabaseLookupMsg (msg);
break;
@ -146,53 +155,55 @@ namespace data
}
}
if (!m_IsRunning) break;
if (!i2p::transport::transports.IsOnline ()) continue; // don't manage netdb when offline
if (!i2p::transport::transports.IsOnline () || !i2p::transport::transports.IsRunning ())
continue; // don't manage netdb when offline or transports are not running
uint64_t ts = i2p::util::GetSecondsSinceEpoch ();
if (ts - lastManageRequest >= MANAGE_REQUESTS_INTERVAL || ts + MANAGE_REQUESTS_INTERVAL < lastManageRequest) // manage requests every 15 seconds
{
m_Requests.ManageRequests ();
lastManageRequest = ts;
}
if (ts - lastManage >= 60 || ts + 60 < lastManage) // manage routers and leasesets every minute
uint64_t mts = i2p::util::GetMonotonicMilliseconds ();
if (mts >= lastManage + 60000) // manage routers and leasesets every minute
{
if (lastManage)
{
ManageRouterInfos ();
ManageLeaseSets ();
}
lastManage = ts;
lastManage = mts;
}
if (ts - lastProfilesCleanup >= (uint64_t)(i2p::data::PEER_PROFILE_AUTOCLEAN_TIMEOUT + profilesCleanupVariance) ||
ts + i2p::data::PEER_PROFILE_AUTOCLEAN_TIMEOUT < lastProfilesCleanup)
if (mts >= lastProfilesCleanup + (uint64_t)(i2p::data::PEER_PROFILE_AUTOCLEAN_TIMEOUT + profilesCleanupVariance)*1000)
{
m_RouterProfilesPool.CleanUpMt ();
if (m_PersistProfiles) PersistProfiles ();
DeleteObsoleteProfiles ();
lastProfilesCleanup = ts;
profilesCleanupVariance = (rand () % (2 * i2p::data::PEER_PROFILE_AUTOCLEAN_VARIANCE) - i2p::data::PEER_PROFILE_AUTOCLEAN_VARIANCE);
if (m_PersistProfiles)
{
bool isSaving = m_SavingProfiles.valid ();
if (isSaving && m_SavingProfiles.wait_for(std::chrono::seconds(0)) == std::future_status::ready) // still active?
{
m_SavingProfiles.get ();
isSaving = false;
}
if (!isSaving)
m_SavingProfiles = PersistProfiles ();
else
LogPrint (eLogWarning, "NetDb: Can't persist profiles. Profiles are being saved to disk");
}
lastProfilesCleanup = mts;
profilesCleanupVariance = rand () % i2p::data::PEER_PROFILE_AUTOCLEAN_VARIANCE;
}
if (ts - lastExploratory >= 30 || ts + 30 < lastExploratory) // exploratory every 30 seconds
if (mts >= lastObsoleteProfilesCleanup + (uint64_t)(i2p::data::PEER_PROFILE_OBSOLETE_PROFILES_CLEAN_TIMEOUT + obsoleteProfilesCleanVariance)*1000)
{
auto numRouters = m_RouterInfos.size ();
if (!numRouters)
throw std::runtime_error("No known routers, reseed seems to be totally failed");
else // we have peers now
m_FloodfillBootstrap = nullptr;
if (numRouters < 2500 || ts - lastExploratory >= 90)
bool isDeleting = m_DeletingProfiles.valid ();
if (isDeleting && m_DeletingProfiles.wait_for(std::chrono::seconds(0)) == std::future_status::ready) // still active?
{
numRouters = 800/numRouters;
if (numRouters < 1) numRouters = 1;
if (numRouters > 9) numRouters = 9;
m_Requests.ManageRequests ();
if(!i2p::context.IsHidden ())
Explore (numRouters);
lastExploratory = ts;
}
}
m_DeletingProfiles.get ();
isDeleting = false;
}
if (!isDeleting)
m_DeletingProfiles = DeleteObsoleteProfiles ();
else
LogPrint (eLogWarning, "NetDb: Can't delete profiles. Profiles are being deleted from disk");
lastObsoleteProfilesCleanup = mts;
obsoleteProfilesCleanVariance = rand () % i2p::data::PEER_PROFILE_OBSOLETE_PROFILES_CLEAN_VARIANCE;
}
}
catch (std::exception& ex)
{
@ -238,7 +249,7 @@ namespace data
if (!r->Update (buf, len))
{
updated = false;
m_Requests.RequestComplete (ident, r);
m_Requests->RequestComplete (ident, r);
return r;
}
if (r->IsUnreachable () ||
@ -251,7 +262,7 @@ namespace data
std::lock_guard<std::mutex> l(m_FloodfillsMutex);
m_Floodfills.Remove (r->GetIdentHash ());
}
m_Requests.RequestComplete (ident, nullptr);
m_Requests->RequestComplete (ident, nullptr);
return nullptr;
}
}
@ -269,7 +280,7 @@ namespace data
if (m_Floodfills.GetSize () < NETDB_NUM_FLOODFILLS_THRESHOLD || r->GetProfile ()->IsReal ())
m_Floodfills.Insert (r);
else
r->ResetFlooldFill ();
r->ResetFloodfill ();
}
}
}
@ -283,8 +294,14 @@ namespace data
else
{
r = std::make_shared<RouterInfo> (buf, len);
if (!r->IsUnreachable () && r->HasValidAddresses () && (!r->IsFloodfill () || !r->GetProfile ()->IsUnreachable ()) &&
i2p::util::GetMillisecondsSinceEpoch () + NETDB_EXPIRATION_TIMEOUT_THRESHOLD*1000LL > r->GetTimestamp ())
bool isValid = !r->IsUnreachable () && r->HasValidAddresses () && (!r->IsFloodfill () || !r->GetProfile ()->IsUnreachable ());
if (isValid)
{
auto mts = i2p::util::GetMillisecondsSinceEpoch ();
isValid = mts + NETDB_EXPIRATION_TIMEOUT_THRESHOLD*1000LL > r->GetTimestamp () && // from future
mts < r->GetTimestamp () + NETDB_MAX_EXPIRATION_TIMEOUT*1000LL; // too old
}
if (isValid)
{
bool inserted = false;
{
@ -304,7 +321,7 @@ namespace data
m_Floodfills.Insert (r);
}
else
r->ResetFlooldFill ();
r->ResetFloodfill ();
}
}
else
@ -317,7 +334,7 @@ namespace data
updated = false;
}
// take care about requested destination
m_Requests.RequestComplete (ident, r);
m_Requests->RequestComplete (ident, r);
return r;
}
@ -429,7 +446,17 @@ namespace data
r->SetUnreachable (unreachable);
auto profile = r->GetProfile ();
if (profile)
{
profile->Unreachable (unreachable);
if (!unreachable && r->IsDeclaredFloodfill () && !r->IsFloodfill () &&
r->IsEligibleFloodfill () && profile->IsReal ())
{
// enable previously disabled floodfill
r->SetFloodfill ();
std::lock_guard<std::mutex> l(m_FloodfillsMutex);
m_Floodfills.Insert (r);
}
}
}
}
@ -451,28 +478,6 @@ namespace data
m_Reseeder->LoadCertificates (); // we need certificates for SU3 verification
}
// try reseeding from floodfill first if specified
std::string riPath; i2p::config::GetOption("reseed.floodfill", riPath);
if (!riPath.empty())
{
auto ri = std::make_shared<RouterInfo>(riPath);
if (ri->IsFloodfill())
{
const uint8_t * riData = ri->GetBuffer();
int riLen = ri->GetBufferLen();
if (!i2p::data::netdb.AddRouterInfo(riData, riLen))
{
// bad router info
LogPrint(eLogError, "NetDb: Bad router info");
return;
}
m_FloodfillBootstrap = ri;
ReseedFromFloodfill(*ri);
// don't try reseed servers if trying to bootstrap from floodfill
return;
}
}
m_Reseeder->Bootstrap ();
}
@ -612,6 +617,17 @@ namespace data
void NetDb::SaveUpdated ()
{
if (m_PersistingRouters.valid ())
{
if (m_PersistingRouters.wait_for(std::chrono::seconds(0)) == std::future_status::ready)
m_PersistingRouters.get ();
else
{
LogPrint (eLogWarning, "NetDb: Can't save updated routers. Routers are being saved to disk");
return;
}
}
int updatedCount = 0, deletedCount = 0, deletedFloodfillsCount = 0;
auto total = m_RouterInfos.size ();
auto totalFloodfills = m_Floodfills.GetSize ();
@ -622,11 +638,14 @@ namespace data
i2p::config::GetOption("limits.zombies", minTunnelCreationSuccessRate);
bool isLowRate = i2p::tunnel::tunnels.GetPreciseTunnelCreationSuccessRate () < minTunnelCreationSuccessRate;
// routers don't expire if less than 90 or uptime is less than 1 hour
bool checkForExpiration = total > NETDB_MIN_ROUTERS && uptime > 600; // 10 minutes
if (checkForExpiration && uptime > 3600) // 1 hour
bool checkForExpiration = total > NETDB_MIN_ROUTERS && uptime > NETDB_CHECK_FOR_EXPIRATION_UPTIME; // 10 minutes
if (checkForExpiration && uptime > i2p::transport::SSU2_TO_INTRODUCER_SESSION_DURATION) // 1 hour
expirationTimeout = i2p::context.IsFloodfill () ? NETDB_FLOODFILL_EXPIRATION_TIMEOUT*1000LL :
NETDB_MIN_EXPIRATION_TIMEOUT*1000LL + (NETDB_MAX_EXPIRATION_TIMEOUT - NETDB_MIN_EXPIRATION_TIMEOUT)*1000LL*NETDB_MIN_ROUTERS/total;
std::list<std::pair<std::string, std::shared_ptr<RouterInfo::Buffer> > > saveToDisk;
std::list<std::string> removeFromDisk;
auto own = i2p::context.GetSharedRouterInfo ();
for (auto& it: m_RouterInfos)
{
@ -637,10 +656,15 @@ namespace data
if (it.second->GetBuffer ())
{
// we have something to save
it.second->SaveToFile (m_Storage.Path(ident));
std::shared_ptr<RouterInfo::Buffer> buffer;
{
std::lock_guard<std::mutex> l(m_RouterInfosMutex); // possible collision between DeleteBuffer and Update
buffer = it.second->GetSharedBuffer ();
it.second->DeleteBuffer ();
}
if (buffer && !it.second->IsUnreachable ()) // don't save bad router
saveToDisk.push_back(std::make_pair(ident, buffer));
it.second->SetUnreachable (false);
std::lock_guard<std::mutex> l(m_RouterInfosMutex); // possible collision between DeleteBuffer and Update
it.second->DeleteBuffer ();
}
it.second->SetUpdated (false);
updatedCount++;
@ -681,12 +705,18 @@ namespace data
{
if (it.second->IsFloodfill ()) deletedFloodfillsCount++;
// delete RI file
m_Storage.Remove(ident);
removeFromDisk.push_back (ident);
deletedCount++;
if (total - deletedCount < NETDB_MIN_ROUTERS) checkForExpiration = false;
}
} // m_RouterInfos iteration
if (!saveToDisk.empty () || !removeFromDisk.empty ())
{
m_PersistingRouters = std::async (std::launch::async, &NetDb::PersistRouters,
this, std::move (saveToDisk), std::move (removeFromDisk));
}
m_RouterInfoBuffersPool.CleanUpMt ();
m_RouterInfoAddressesPool.CleanUpMt ();
m_RouterInfoAddressVectorsPool.CleanUpMt ();
@ -722,66 +752,22 @@ namespace data
}
}
void NetDb::PersistRouters (std::list<std::pair<std::string, std::shared_ptr<RouterInfo::Buffer> > >&& update,
std::list<std::string>&& remove)
{
for (auto it: update)
RouterInfo::SaveToFile (m_Storage.Path(it.first), it.second);
for (auto it: remove)
m_Storage.Remove (it);
}
void NetDb::RequestDestination (const IdentHash& destination, RequestedDestination::RequestComplete requestComplete, bool direct)
{
if (direct && i2p::transport::transports.RoutesRestricted ()) direct = false; // always use tunnels for restricted routes
auto dest = m_Requests.CreateRequest (destination, false, direct, requestComplete); // non-exploratory
if (!dest)
{
LogPrint (eLogWarning, "NetDb: Destination ", destination.ToBase64(), " is requested already");
return;
}
auto floodfill = GetClosestFloodfill (destination, dest->GetExcludedPeers ());
if (floodfill)
{
if (direct && !floodfill->IsReachableFrom (i2p::context.GetRouterInfo ()) &&
!i2p::transport::transports.IsConnected (floodfill->GetIdentHash ()))
direct = false; // floodfill can't be reached directly
if (direct)
{
auto msg = dest->CreateRequestMessage (floodfill->GetIdentHash ());
msg->onDrop = [this, dest]() { this->m_Requests.SendNextRequest (dest); };
transports.SendMessage (floodfill->GetIdentHash (), msg);
}
else
{
auto pool = i2p::tunnel::tunnels.GetExploratoryPool ();
auto outbound = pool ? pool->GetNextOutboundTunnel (nullptr, floodfill->GetCompatibleTransports (false)) : nullptr;
auto inbound = pool ? pool->GetNextInboundTunnel (nullptr, floodfill->GetCompatibleTransports (true)) : nullptr;
if (outbound && inbound)
{
auto msg = dest->CreateRequestMessage (floodfill, inbound);
msg->onDrop = [this, dest]() { this->m_Requests.SendNextRequest (dest); };
outbound->SendTunnelDataMsgTo (floodfill->GetIdentHash (), 0,
i2p::garlic::WrapECIESX25519MessageForRouter (msg, floodfill->GetIdentity ()->GetEncryptionPublicKey ()));
}
else
{
LogPrint (eLogError, "NetDb: ", destination.ToBase64(), " destination requested, but no tunnels found");
m_Requests.RequestComplete (destination, nullptr);
}
}
}
if (m_Requests)
m_Requests->PostRequestDestination (destination, requestComplete, direct);
else
{
LogPrint (eLogError, "NetDb: ", destination.ToBase64(), " destination requested, but no floodfills found");
m_Requests.RequestComplete (destination, nullptr);
}
}
void NetDb::RequestDestinationFrom (const IdentHash& destination, const IdentHash & from, bool exploratory, RequestedDestination::RequestComplete requestComplete)
{
auto dest = m_Requests.CreateRequest (destination, exploratory, true, requestComplete); // non-exploratory
if (!dest)
{
LogPrint (eLogWarning, "NetDb: Destination ", destination.ToBase64(), " is requested already");
return;
}
LogPrint(eLogInfo, "NetDb: Destination ", destination.ToBase64(), " being requested directly from ", from.ToBase64());
// direct
transports.SendMessage (from, dest->CreateRequestMessage (nullptr, nullptr));
LogPrint (eLogError, "NetDb: Requests is null");
}
void NetDb::HandleNTCP2RouterInfoMsg (std::shared_ptr<const I2NPMessage> m)
@ -926,61 +912,16 @@ namespace data
{
memcpy (payload + DATABASE_STORE_HEADER_SIZE, buf + payloadOffset, msgLen);
floodMsg->FillI2NPMessageHeader (eI2NPDatabaseStore);
Flood (ident, floodMsg);
int minutesBeforeMidnight = 24*60 - i2p::util::GetMinutesSinceEpoch () % (24*60);
bool andNextDay = storeType ? minutesBeforeMidnight < NETDB_NEXT_DAY_LEASESET_THRESHOLD:
minutesBeforeMidnight < NETDB_NEXT_DAY_ROUTER_INFO_THRESHOLD;
Flood (ident, floodMsg, andNextDay);
}
else
LogPrint (eLogError, "NetDb: Database store message is too long ", floodMsg->len);
}
}
void NetDb::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;
int num = buf[32]; // num
LogPrint (eLogDebug, "NetDb: DatabaseSearchReply for ", key, " num=", num);
IdentHash ident (buf);
auto dest = m_Requests.FindRequest (ident);
if (dest)
{
if (num > 0)
// try to send next requests
m_Requests.SendNextRequest (dest);
else
// no more requests for destination possible. delete it
m_Requests.RequestComplete (ident, nullptr);
}
else if(!m_FloodfillBootstrap)
LogPrint (eLogWarning, "NetDb: Requested destination for ", key, " not found");
// try responses
for (int i = 0; i < num; i++)
{
const uint8_t * router = buf + 33 + i*32;
char peerHash[48];
int l1 = i2p::data::ByteStreamToBase64 (router, 32, peerHash, 48);
peerHash[l1] = 0;
LogPrint (eLogDebug, "NetDb: ", i, ": ", peerHash);
auto r = FindRouter (router);
if (!r || i2p::util::GetMillisecondsSinceEpoch () > r->GetTimestamp () + 3600*1000LL)
{
// router with ident not found or too old (1 hour)
LogPrint (eLogDebug, "NetDb: Found new/outdated router. Requesting RouterInfo...");
if(m_FloodfillBootstrap)
RequestDestinationFrom(router, m_FloodfillBootstrap->GetIdentHash(), true);
else if (!IsRouterBanned (router))
RequestDestination (router);
else
LogPrint (eLogDebug, "NetDb: Router ", peerHash, " is banned. Skipped");
}
else
LogPrint (eLogDebug, "NetDb: [:|||:]");
}
}
void NetDb::HandleDatabaseLookupMsg (std::shared_ptr<const I2NPMessage> msg)
{
const uint8_t * buf = msg->GetPayload ();
@ -1024,24 +965,15 @@ namespace data
return;
}
LogPrint (eLogInfo, "NetDb: Exploratory close to ", key, " ", numExcluded, " excluded");
std::set<IdentHash> excludedRouters;
std::unordered_set<IdentHash> excludedRouters;
const uint8_t * excluded_ident = excluded;
for (int i = 0; i < numExcluded; i++)
{
excludedRouters.insert (excluded_ident);
excluded_ident += 32;
}
std::vector<IdentHash> routers;
for (int i = 0; i < 3; i++)
{
auto r = GetClosestNonFloodfill (ident, excludedRouters);
if (r)
{
routers.push_back (r->GetIdentHash ());
excludedRouters.insert (r->GetIdentHash ());
}
}
replyMsg = CreateDatabaseSearchReply (ident, routers);
replyMsg = CreateDatabaseSearchReply (ident, GetExploratoryNonFloodfill (ident,
NETDB_MAX_NUM_SEARCH_REPLY_PEER_HASHES, excludedRouters));
}
else
{
@ -1085,16 +1017,16 @@ namespace data
if (!replyMsg)
{
std::set<IdentHash> excludedRouters;
std::unordered_set<IdentHash> excludedRouters;
const uint8_t * exclude_ident = excluded;
for (int i = 0; i < numExcluded; i++)
{
excludedRouters.insert (exclude_ident);
exclude_ident += 32;
}
auto closestFloodfills = GetClosestFloodfills (ident, 3, excludedRouters, true);
auto closestFloodfills = GetClosestFloodfills (ident, 3, excludedRouters, false);
if (closestFloodfills.empty ())
LogPrint (eLogWarning, "NetDb: Requested ", key, " not found, ", numExcluded, " peers excluded");
LogPrint (eLogWarning, "NetDb: No more floodfills for ", key, " found. ", numExcluded, " peers excluded");
replyMsg = CreateDatabaseSearchReply (ident, closestFloodfills);
}
}
@ -1152,74 +1084,43 @@ namespace data
}
}
void NetDb::Explore (int numDestinations)
void NetDb::Flood (const IdentHash& ident, std::shared_ptr<I2NPMessage> floodMsg, bool andNextDay)
{
// 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, "NetDb: Exploring new ", numDestinations, " routers ...");
for (int i = 0; i < numDestinations; i++)
{
RAND_bytes (randomHash, 32);
auto dest = m_Requests.CreateRequest (randomHash, true, !throughTunnels); // exploratory
if (!dest)
{
LogPrint (eLogWarning, "NetDb: Exploratory destination is requested already");
return;
}
auto floodfill = 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
m_Requests.RequestComplete (randomHash, nullptr);
}
if (throughTunnels && msgs.size () > 0)
outbound->SendTunnelDataMsgs (msgs);
}
void NetDb::Flood (const IdentHash& ident, std::shared_ptr<I2NPMessage> floodMsg)
{
std::set<IdentHash> excluded;
std::unordered_set<IdentHash> excluded;
excluded.insert (i2p::context.GetIdentHash ()); // don't flood to itself
excluded.insert (ident); // don't flood back
for (int i = 0; i < 3; i++)
{
auto floodfill = GetClosestFloodfill (ident, excluded);
auto floodfill = GetClosestFloodfill (ident, excluded, false); // current day
if (floodfill)
{
auto h = floodfill->GetIdentHash();
LogPrint(eLogDebug, "NetDb: Flood lease set for ", ident.ToBase32(), " to ", h.ToBase64());
const auto& h = floodfill->GetIdentHash();
transports.SendMessage (h, CopyI2NPMessage(floodMsg));
excluded.insert (h);
}
else
break;
return; // no more floodfills
}
if (andNextDay)
{
// flood to two more closest flodfills for next day
std::unordered_set<IdentHash> excluded1;
excluded1.insert (i2p::context.GetIdentHash ()); // don't flood to itself
excluded1.insert (ident); // don't flood back
for (int i = 0; i < 2; i++)
{
auto floodfill = GetClosestFloodfill (ident, excluded1, true); // next day
if (floodfill)
{
const auto& h = floodfill->GetIdentHash();
if (!excluded.count (h)) // we didn't send for current day, otherwise skip
transports.SendMessage (h, CopyI2NPMessage(floodMsg));
excluded1.insert (h);
}
else
return;
}
}
}
std::shared_ptr<const RouterInfo> NetDb::GetRandomRouter () const
@ -1239,13 +1140,13 @@ namespace data
{
return !router->IsHidden () && router != compatibleWith &&
(reverse ? (compatibleWith->IsReachableFrom (*router) && router->GetCompatibleTransports (true)):
router->IsReachableFrom (*compatibleWith)) &&
router->IsReachableFrom (*compatibleWith)) && !router->IsNAT2NATOnly (*compatibleWith) &&
router->IsECIES () && !router->IsHighCongestion (false) &&
(!endpoint || (router->IsV4 () && (!reverse || router->IsPublished (true)))); // endpoint must be ipv4 and published if inbound(reverse)
});
}
std::shared_ptr<const RouterInfo> NetDb::GetRandomSSU2PeerTestRouter (bool v4, const std::set<IdentHash>& excluded) const
std::shared_ptr<const RouterInfo> NetDb::GetRandomSSU2PeerTestRouter (bool v4, const std::unordered_set<IdentHash>& excluded) const
{
return GetRandomRouter (
[v4, &excluded](std::shared_ptr<const RouterInfo> router)->bool
@ -1255,7 +1156,7 @@ namespace data
});
}
std::shared_ptr<const RouterInfo> NetDb::GetRandomSSU2Introducer (bool v4, const std::set<IdentHash>& excluded) const
std::shared_ptr<const RouterInfo> NetDb::GetRandomSSU2Introducer (bool v4, const std::unordered_set<IdentHash>& excluded) const
{
return GetRandomRouter (
[v4, &excluded](std::shared_ptr<const RouterInfo> router)->bool
@ -1268,15 +1169,18 @@ namespace data
std::shared_ptr<const RouterInfo> NetDb::GetHighBandwidthRandomRouter (std::shared_ptr<const RouterInfo> compatibleWith,
bool reverse, bool endpoint) const
{
bool checkIsReal = i2p::tunnel::tunnels.GetPreciseTunnelCreationSuccessRate () < NETDB_TUNNEL_CREATION_RATE_THRESHOLD && // too low rate
context.GetUptime () > NETDB_CHECK_FOR_EXPIRATION_UPTIME; // after 10 minutes uptime
return GetRandomRouter (
[compatibleWith, reverse, endpoint](std::shared_ptr<const RouterInfo> router)->bool
[compatibleWith, reverse, endpoint, checkIsReal](std::shared_ptr<const RouterInfo> router)->bool
{
return !router->IsHidden () && router != compatibleWith &&
(reverse ? (compatibleWith->IsReachableFrom (*router) && router->GetCompatibleTransports (true)) :
router->IsReachableFrom (*compatibleWith)) &&
router->IsReachableFrom (*compatibleWith)) && !router->IsNAT2NATOnly (*compatibleWith) &&
(router->GetCaps () & RouterInfo::eHighBandwidth) &&
router->GetVersion () >= NETDB_MIN_HIGHBANDWIDTH_VERSION &&
router->IsECIES () && !router->IsHighCongestion (true) &&
(!checkIsReal || router->GetProfile ()->IsReal ()) &&
(!endpoint || (router->IsV4 () && (!reverse || router->IsPublished (true)))); // endpoint must be ipv4 and published if inbound(reverse)
});
@ -1347,10 +1251,16 @@ namespace data
if (msg) m_Queue.Put (msg);
}
std::shared_ptr<const RouterInfo> NetDb::GetClosestFloodfill (const IdentHash& destination,
const std::set<IdentHash>& excluded) const
void NetDb::PostDatabaseSearchReplyMsg (std::shared_ptr<const I2NPMessage> msg)
{
IdentHash destKey = CreateRoutingKey (destination);
if (msg && m_Requests)
m_Requests->PostDatabaseSearchReplyMsg (msg);
}
std::shared_ptr<const RouterInfo> NetDb::GetClosestFloodfill (const IdentHash& destination,
const std::unordered_set<IdentHash>& excluded, bool nextDay) const
{
IdentHash destKey = CreateRoutingKey (destination, nextDay);
std::lock_guard<std::mutex> l(m_FloodfillsMutex);
return m_Floodfills.FindClosest (destKey, [&excluded](const std::shared_ptr<RouterInfo>& r)->bool
{
@ -1360,7 +1270,7 @@ namespace data
}
std::vector<IdentHash> NetDb::GetClosestFloodfills (const IdentHash& destination, size_t num,
std::set<IdentHash>& excluded, bool closeThanUsOnly) const
std::unordered_set<IdentHash>& excluded, bool closeThanUsOnly) const
{
std::vector<IdentHash> res;
IdentHash destKey = CreateRoutingKey (destination);
@ -1394,27 +1304,63 @@ namespace data
});
}
std::shared_ptr<const RouterInfo> NetDb::GetClosestNonFloodfill (const IdentHash& destination,
const std::set<IdentHash>& excluded) const
std::vector<IdentHash> NetDb::GetExploratoryNonFloodfill (const IdentHash& destination,
size_t num, const std::unordered_set<IdentHash>& excluded)
{
std::shared_ptr<const RouterInfo> r;
XORMetric minMetric;
IdentHash destKey = CreateRoutingKey (destination);
minMetric.SetMax ();
// must be called from NetDb thread only
for (const auto& it: m_RouterInfos)
std::vector<IdentHash> ret;
if (!num || m_RouterInfos.empty ()) return ret; // empty list
auto ts = i2p::util::GetMonotonicSeconds ();
if (ts > m_LastExploratorySelectionUpdateTime + NETDB_EXPLORATORY_SELECTION_UPDATE_INTERVAL)
{
if (!it.second->IsFloodfill ())
// update selection
m_ExploratorySelection.clear ();
#if (__cplusplus >= 201703L) // C++ 17 or higher
std::vector<std::shared_ptr<const RouterInfo> > eligible;
eligible.reserve (m_RouterInfos.size ());
#else
auto& eligible = m_ExploratorySelection;
#endif
{
XORMetric m = destKey ^ it.first;
if (m < minMetric && !excluded.count (it.first))
{
minMetric = m;
r = it.second;
}
// collect eligible from current netdb
bool checkIsReal = i2p::tunnel::tunnels.GetPreciseTunnelCreationSuccessRate () < NETDB_TUNNEL_CREATION_RATE_THRESHOLD; // too low rate
std::lock_guard<std::mutex> l(m_RouterInfosMutex);
for (const auto& it: m_RouterInfos)
if (!it.second->IsDeclaredFloodfill () &&
(!checkIsReal || (it.second->HasProfile () && it.second->GetProfile ()->IsReal ())))
eligible.push_back (it.second);
}
}
return r;
#if (__cplusplus >= 201703L) // C++ 17 or higher
if (eligible.size () > NETDB_MAX_EXPLORATORY_SELECTION_SIZE)
{
std::sample (eligible.begin(), eligible.end(), std::back_inserter(m_ExploratorySelection),
NETDB_MAX_EXPLORATORY_SELECTION_SIZE, std::mt19937(ts));
}
else
std::swap (m_ExploratorySelection, eligible);
#else
if (m_ExploratorySelection.size () > NETDB_MAX_EXPLORATORY_SELECTION_SIZE)
{
// reduce number of eligible to max selection size
std::shuffle (m_ExploratorySelection.begin(), m_ExploratorySelection.end(), std::mt19937(ts));
m_ExploratorySelection.resize (NETDB_MAX_EXPLORATORY_SELECTION_SIZE);
}
#endif
m_LastExploratorySelectionUpdateTime = ts;
}
// sort by distance
IdentHash destKey = CreateRoutingKey (destination);
std::map<XORMetric, std::shared_ptr<const RouterInfo> > sorted;
for (const auto& it: m_ExploratorySelection)
if (!excluded.count (it->GetIdentHash ()))
sorted.emplace (destKey ^ it->GetIdentHash (), it);
// return first num closest routers
for (const auto& it: sorted)
{
ret.push_back (it.second->GetIdentHash ());
if (ret.size () >= num) break;
}
return ret;
}
void NetDb::ManageRouterInfos ()

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2013-2023, The PurpleI2P Project
* Copyright (c) 2013-2024, The PurpleI2P Project
*
* This file is part of Purple i2pd project and licensed under BSD3
*
@ -10,11 +10,12 @@
#define NETDB_H__
// this file is called NetDb.hpp to resolve conflict with libc's netdb.h on case insensitive fs
#include <inttypes.h>
#include <set>
#include <unordered_set>
#include <unordered_map>
#include <string>
#include <thread>
#include <mutex>
#include <future>
#include "Base.h"
#include "Gzip.h"
@ -38,16 +39,23 @@ namespace data
{
const int NETDB_MIN_ROUTERS = 90;
const int NETDB_MIN_FLOODFILLS = 5;
const int NETDB_NUM_FLOODFILLS_THRESHOLD = 1500;
const int NETDB_NUM_FLOODFILLS_THRESHOLD = 1200;
const int NETDB_NUM_ROUTERS_THRESHOLD = 4*NETDB_NUM_FLOODFILLS_THRESHOLD;
const int NETDB_TUNNEL_CREATION_RATE_THRESHOLD = 10; // in %
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_MIN_EXPIRATION_TIMEOUT = 90 * 60; // 1.5 hours
const int NETDB_MAX_EXPIRATION_TIMEOUT = 27 * 60 * 60; // 27 hours
const int NETDB_MAX_OFFLINE_EXPIRATION_TIMEOUT = 180; // in days
const int NETDB_EXPIRATION_TIMEOUT_THRESHOLD = 2*60; // 2 minutes
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_HIGHBANDWIDTH_VERSION = MAKE_VERSION_NUMBER(0, 9, 58); // 0.9.58
const int NETDB_MIN_FLOODFILL_VERSION = MAKE_VERSION_NUMBER(0, 9, 59); // 0.9.59
const int NETDB_MIN_SHORT_TUNNEL_BUILD_VERSION = MAKE_VERSION_NUMBER(0, 9, 51); // 0.9.51
const 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 */
typedef std::function<void(const IdentHash, std::shared_ptr<LeaseSet>)> LeaseSetVisitor;
@ -77,27 +85,22 @@ namespace data
std::shared_ptr<RouterProfile> FindRouterProfile (const IdentHash& ident) const;
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 (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::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::set<IdentHash>& excluded) const;
std::shared_ptr<const RouterInfo> GetRandomSSU2PeerTestRouter (bool v4, const std::unordered_set<IdentHash>& excluded) const;
std::shared_ptr<const RouterInfo> GetRandomSSU2Introducer (bool v4, const std::unordered_set<IdentHash>& excluded) const;
std::shared_ptr<const RouterInfo> GetClosestFloodfill (const IdentHash& destination, const std::unordered_set<IdentHash>& excluded, bool nextDay = false) const;
std::vector<IdentHash> GetClosestFloodfills (const IdentHash& destination, size_t num,
std::set<IdentHash>& excluded, bool closeThanUsOnly = false) const;
std::shared_ptr<const RouterInfo> GetClosestNonFloodfill (const IdentHash& destination, const std::set<IdentHash>& excluded) const;
std::unordered_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> GetRandomRouterInFamily (FamilyID fam) const;
void SetUnreachable (const IdentHash& ident, bool unreachable);
void ExcludeReachableTransports (const IdentHash& ident, RouterInfo::CompatibleTransports transports);
void PostI2NPMsg (std::shared_ptr<const I2NPMessage> msg);
void PostDatabaseSearchReplyMsg (std::shared_ptr<const I2NPMessage> msg); // to NetdbReq thread
void Reseed ();
Families& GetFamilies () { return m_Families; };
@ -117,7 +120,11 @@ namespace data
size_t VisitRandomRouterInfos(RouterInfoFilter f, RouterInfoVisitor v, size_t n);
void ClearRouterInfos () { m_RouterInfos.clear (); };
std::shared_ptr<RouterInfo::Buffer> NewRouterInfoBuffer () { return m_RouterInfoBuffersPool.AcquireSharedMt (); };
template<typename... TArgs>
std::shared_ptr<RouterInfo::Buffer> NewRouterInfoBuffer (TArgs&&... args)
{
return m_RouterInfoBuffersPool.AcquireSharedMt (std::forward<TArgs>(args)...);
}
bool PopulateRouterInfoBuffer (std::shared_ptr<RouterInfo> r);
std::shared_ptr<RouterInfo::Address> NewRouterInfoAddress () { return m_RouterInfoAddressesPool.AcquireSharedMt (); };
boost::shared_ptr<RouterInfo::Addresses> NewRouterInfoAddresses ()
@ -131,16 +138,15 @@ namespace data
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 (); };
uint32_t GetPublishReplyToken () const { return m_PublishReplyToken; };
private:
void Load ();
bool LoadRouterInfo (const std::string& path, uint64_t ts);
void SaveUpdated ();
void Run (); // exploratory thread
void Explore (int numDestinations);
void Flood (const IdentHash& ident, std::shared_ptr<I2NPMessage> floodMsg);
void PersistRouters (std::list<std::pair<std::string, std::shared_ptr<RouterInfo::Buffer> > >&& update,
std::list<std::string>&& remove);
void Run ();
void Flood (const IdentHash& ident, std::shared_ptr<I2NPMessage> floodMsg, bool andNextDay = false);
void ManageRouterInfos ();
void ManageLeaseSets ();
void ManageRequests ();
@ -153,6 +159,10 @@ namespace data
template<typename Filter>
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:
mutable std::mutex m_LeaseSetsMutex;
@ -171,16 +181,13 @@ namespace data
Families m_Families;
i2p::fs::HashedStorage m_Storage;
friend class NetDbRequests;
NetDbRequests m_Requests;
std::shared_ptr<NetDbRequests> m_Requests;
bool m_PersistProfiles;
std::future<void> m_SavingProfiles, m_DeletingProfiles, m_PersistingRouters;
/** router info we are bootstrapping from or nullptr if we are not currently doing that*/
std::shared_ptr<RouterInfo> m_FloodfillBootstrap;
std::set<IdentHash> m_PublishExcluded;
uint32_t m_PublishReplyToken = 0;
std::vector<std::shared_ptr<const RouterInfo> > m_ExploratorySelection;
uint64_t m_LastExploratorySelectionUpdateTime; // in monotonic seconds
i2p::util::MemoryPoolMt<RouterInfo::Buffer> m_RouterInfoBuffersPool;
i2p::util::MemoryPoolMt<RouterInfo::Address> m_RouterInfoAddressesPool;

View File

@ -10,22 +10,26 @@
#include "I2NPProtocol.h"
#include "Transports.h"
#include "NetDb.hpp"
#include "NetDbRequests.h"
#include "ECIESX25519AEADRatchetSession.h"
#include "RouterContext.h"
#include "Timestamp.h"
#include "NetDbRequests.h"
namespace i2p
{
namespace data
{
RequestedDestination::RequestedDestination (const IdentHash& destination, bool isExploratory, bool direct):
m_Destination (destination), m_IsExploratory (isExploratory), m_IsDirect (direct),
m_CreationTime (i2p::util::GetSecondsSinceEpoch ()), m_LastRequestTime (0)
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 ()
{
if (m_RequestComplete) m_RequestComplete (nullptr);
InvokeRequestComplete (nullptr);
}
std::shared_ptr<I2NPMessage> RequestedDestination::CreateRequestMessage (std::shared_ptr<const RouterInfo> router,
@ -41,6 +45,7 @@ namespace data
if(router)
m_ExcludedPeers.insert (router->GetIdentHash ());
m_LastRequestTime = i2p::util::GetSecondsSinceEpoch ();
m_NumAttempts++;
return msg;
}
@ -49,101 +54,154 @@ namespace data
auto msg = i2p::CreateRouterInfoDatabaseLookupMsg (m_Destination,
i2p::context.GetRouterInfo ().GetIdentHash () , 0, false, &m_ExcludedPeers);
m_ExcludedPeers.insert (floodfill);
m_NumAttempts++;
m_LastRequestTime = i2p::util::GetSecondsSinceEpoch ();
return msg;
}
bool RequestedDestination::IsExcluded (const IdentHash& ident) const
{
return m_ExcludedPeers.count (ident);
}
void RequestedDestination::ClearExcludedPeers ()
{
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)
{
if (m_RequestComplete)
{
m_RequestComplete (r);
m_RequestComplete = nullptr;
}
if (m_IsActive)
{
m_IsActive = false;
InvokeRequestComplete (r);
}
}
void RequestedDestination::Fail ()
{
if (m_RequestComplete)
{
m_RequestComplete (nullptr);
m_RequestComplete = nullptr;
}
if (m_IsActive)
{
m_IsActive = false;
InvokeRequestComplete (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 ()
{
if (!IsRunning ())
{
StartIOService ();
ScheduleManageRequests ();
ScheduleCleanup ();
if (!i2p::context.IsHidden ())
ScheduleExploratory (EXPLORATORY_REQUEST_INTERVAL);
}
}
void NetDbRequests::Stop ()
{
m_RequestedDestinations.clear ();
if (IsRunning ())
{
m_ManageRequestsTimer.cancel ();
m_ExploratoryTimer.cancel ();
m_CleanupTimer.cancel ();
StopIOService ();
m_RequestedDestinations.clear ();
m_RequestedDestinationsPool.CleanUpMt ();
}
}
void NetDbRequests::ScheduleCleanup ()
{
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
auto dest = std::make_shared<RequestedDestination> (destination, isExploratory, direct);
dest->SetRequestComplete (requestComplete);
{
std::unique_lock<std::mutex> l(m_RequestedDestinationsMutex);
auto ret = m_RequestedDestinations.emplace (destination, dest);
if (!ret.second) // not inserted
auto dest = m_RequestedDestinationsPool.AcquireSharedMt (destination, isExploratory, direct);
if (requestComplete)
dest->AddRequestComplete (requestComplete);
auto ret = m_RequestedDestinations.emplace (destination, dest);
if (!ret.second) // not inserted
{
dest->ResetRequestComplete (); // don't call requestComplete in destructor
dest = ret.first->second; // existing one
if (requestComplete)
{
dest->SetRequestComplete (nullptr); // don't call requestComplete in destructor
if (requestComplete)
{
auto prev = ret.first->second->GetRequestComplete ();
if (prev) // if already set
ret.first->second->SetRequestComplete (
[requestComplete, prev](std::shared_ptr<RouterInfo> r)
{
prev (r); // call previous
requestComplete (r); // then new
});
else
ret.first->second->SetRequestComplete (requestComplete);
}
if (i2p::util::GetSecondsSinceEpoch () > ret.first->second->GetLastRequestTime () + MIN_REQUEST_TIME)
if (!SendNextRequest (ret.first->second)) // try next floodfill
m_RequestedDestinations.erase (ret.first); // delete request if failed
return nullptr;
if (dest->IsActive ())
dest->AddRequestComplete (requestComplete);
else
requestComplete (nullptr);
}
}
return nullptr;
}
return dest;
}
void NetDbRequests::RequestComplete (const IdentHash& ident, std::shared_ptr<RouterInfo> r)
{
std::shared_ptr<RequestedDestination> request;
{
std::unique_lock<std::mutex> l(m_RequestedDestinationsMutex);
auto it = m_RequestedDestinations.find (ident);
if (it != m_RequestedDestinations.end ())
{
request = it->second;
m_RequestedDestinations.erase (it);
}
}
if (request)
{
if (r)
request->Success (r);
else
request->Fail ();
}
GetIOService ().post ([this, ident, r]()
{
std::shared_ptr<RequestedDestination> request;
auto it = m_RequestedDestinations.find (ident);
if (it != m_RequestedDestinations.end ())
{
request = it->second;
if (request->IsExploratory ())
m_RequestedDestinations.erase (it);
// otherwise cache for a while
}
if (request)
{
if (r)
request->Success (r);
else
request->Fail ();
}
});
}
std::shared_ptr<RequestedDestination> NetDbRequests::FindRequest (const IdentHash& ident) const
{
std::unique_lock<std::mutex> l(m_RequestedDestinationsMutex);
auto it = m_RequestedDestinations.find (ident);
if (it != m_RequestedDestinations.end ())
return it->second;
@ -153,31 +211,48 @@ namespace data
void NetDbRequests::ManageRequests ()
{
uint64_t ts = i2p::util::GetSecondsSinceEpoch ();
std::unique_lock<std::mutex> l(m_RequestedDestinationsMutex);
for (auto it = m_RequestedDestinations.begin (); it != m_RequestedDestinations.end ();)
{
auto& dest = it->second;
bool done = false;
if (ts < dest->GetCreationTime () + MAX_REQUEST_TIME) // request becomes worthless
{
if (ts > dest->GetLastRequestTime () + MIN_REQUEST_TIME) // try next floodfill if no response after min interval
done = !SendNextRequest (dest);
}
else // delete obsolete request
done = true;
if (done)
it = m_RequestedDestinations.erase (it);
if (dest->IsActive () || ts < dest->GetCreationTime () + REQUEST_CACHE_TIME)
{
if (!dest->IsExploratory ())
{
// 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;
it = m_RequestedDestinations.erase (it);
}
}
bool NetDbRequests::SendNextRequest (std::shared_ptr<RequestedDestination> dest)
{
if (!dest) return false;
if (!dest || !dest->IsActive ()) return false;
bool ret = true;
auto count = dest->GetExcludedPeers ().size ();
auto count = dest->GetNumAttempts ();
if (!dest->IsExploratory () && count < MAX_NUM_REQUEST_ATTEMPTS)
{
auto nextFloodfill = netdb.GetClosestFloodfill (dest->GetDestination (), dest->GetExcludedPeers ());
@ -187,47 +262,294 @@ namespace data
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)
{
LogPrint (eLogDebug, "NetDbReq: Try ", dest->GetDestination ().ToBase64 (), " at ", count, " floodfill ", nextFloodfill->GetIdentHash ().ToBase64 (), " directly");
if (CheckLogLevel (eLogDebug))
LogPrint (eLogDebug, "NetDbReq: Try ", dest->GetDestination ().ToBase64 (), " at ", count, " floodfill ", nextFloodfill->GetIdentHash ().ToBase64 (), " directly");
auto msg = dest->CreateRequestMessage (nextFloodfill->GetIdentHash ());
msg->onDrop = [this, dest]() { this->SendNextRequest (dest); };
msg->onDrop = onDrop;
i2p::transport::transports.SendMessage (nextFloodfill->GetIdentHash (), msg);
}
else
{
auto pool = i2p::tunnel::tunnels.GetExploratoryPool ();
auto outbound = pool->GetNextOutboundTunnel ();
auto inbound = pool->GetNextInboundTunnel ();
if (nextFloodfill && outbound && inbound)
{
LogPrint (eLogDebug, "NetDbReq: Try ", dest->GetDestination ().ToBase64 (), " at ", count, " floodfill ", nextFloodfill->GetIdentHash ().ToBase64 (), " through tunnels");
auto msg = dest->CreateRequestMessage (nextFloodfill, inbound);
msg->onDrop = [this, dest]() { this->SendNextRequest (dest); };
outbound->SendTunnelDataMsgTo (nextFloodfill->GetIdentHash (), 0,
i2p::garlic::WrapECIESX25519MessageForRouter (msg, nextFloodfill->GetIdentity ()->GetEncryptionPublicKey ()));
if (pool)
{
auto outbound = pool->GetNextOutboundTunnel ();
auto inbound = pool->GetNextInboundTunnel ();
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,
i2p::garlic::WrapECIESX25519MessageForRouter (msg, nextFloodfill->GetIdentity ()->GetEncryptionPublicKey ()));
}
else
{
ret = false;
if (!inbound) LogPrint (eLogWarning, "NetDbReq: No inbound tunnels");
if (!outbound) LogPrint (eLogWarning, "NetDbReq: No outbound tunnels");
}
}
else
{
ret = false;
if (!inbound) LogPrint (eLogWarning, "NetDbReq: No inbound tunnels");
if (!outbound) LogPrint (eLogWarning, "NetDbReq: No outbound tunnels");
}
LogPrint (eLogWarning, "NetDbReq: Exploratory pool is not ready");
}
}
}
else
{
ret = false;
if (!nextFloodfill) LogPrint (eLogWarning, "NetDbReq: No more floodfills");
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 7 attempts");
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
RequestComplete (randomHash, nullptr);
}
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 ();
}
}
}
}
}

View File

@ -11,19 +11,29 @@
#include <inttypes.h>
#include <memory>
#include <set>
#include <random>
#include <unordered_set>
#include <unordered_map>
#include <list>
#include "Identity.h"
#include "RouterInfo.h"
#include "util.h"
namespace i2p
{
namespace data
{
const size_t MAX_NUM_REQUEST_ATTEMPTS = 7;
const uint64_t MANAGE_REQUESTS_INTERVAL = 15; // in seconds
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*MANAGE_REQUESTS_INTERVAL;
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
{
@ -35,50 +45,82 @@ namespace data
~RequestedDestination ();
const IdentHash& GetDestination () const { return m_Destination; };
int GetNumExcludedPeers () const { return m_ExcludedPeers.size (); };
const std::set<IdentHash>& GetExcludedPeers () { return m_ExcludedPeers; };
const std::unordered_set<IdentHash>& GetExcludedPeers () const { return m_ExcludedPeers; };
int GetNumAttempts () const { return m_NumAttempts; };
void ClearExcludedPeers ();
bool IsExploratory () const { return m_IsExploratory; };
bool IsDirect () const { return m_IsDirect; };
bool IsExcluded (const IdentHash& ident) const { return m_ExcludedPeers.count (ident); };
bool IsActive () const { return m_IsActive; };
bool IsExcluded (const IdentHash& ident) const;
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 (const IdentHash& floodfill);
void SetRequestComplete (const RequestComplete& requestComplete) { m_RequestComplete = requestComplete; };
RequestComplete GetRequestComplete () const { return m_RequestComplete; };
bool IsRequestComplete () const { return m_RequestComplete != nullptr; };
void AddRequestComplete (const RequestComplete& requestComplete) { m_RequestComplete.push_back (requestComplete); };
void ResetRequestComplete () { m_RequestComplete.clear (); };
void Success (std::shared_ptr<RouterInfo> r);
void Fail ();
private:
IdentHash m_Destination;
bool m_IsExploratory, m_IsDirect;
std::set<IdentHash> m_ExcludedPeers;
uint64_t m_CreationTime, m_LastRequestTime; // in seconds
RequestComplete m_RequestComplete;
};
class NetDbRequests
{
public:
void Start ();
void Stop ();
std::shared_ptr<RequestedDestination> CreateRequest (const IdentHash& destination, bool isExploratory,
bool direct = false, RequestedDestination::RequestComplete requestComplete = nullptr);
void RequestComplete (const IdentHash& ident, std::shared_ptr<RouterInfo> r);
std::shared_ptr<RequestedDestination> FindRequest (const IdentHash& ident) const;
void ManageRequests ();
bool SendNextRequest (std::shared_ptr<RequestedDestination> dest);
void InvokeRequestComplete (std::shared_ptr<RouterInfo> r);
private:
IdentHash m_Destination;
bool m_IsExploratory, m_IsDirect, m_IsActive;
std::unordered_set<IdentHash> m_ExcludedPeers;
uint64_t m_CreationTime, m_LastRequestTime; // in seconds
std::list<RequestComplete> m_RequestComplete;
int m_NumAttempts;
};
class NetDbRequests: public std::enable_shared_from_this<NetDbRequests>,
private i2p::util::RunnableServiceWithWork
{
public:
NetDbRequests ();
~NetDbRequests ();
void Start ();
void Stop ();
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;
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 ();
// 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:
mutable std::mutex m_RequestedDestinationsMutex;
std::unordered_map<IdentHash, std::shared_ptr<RequestedDestination> > m_RequestedDestinations;
std::list<IdentHash> m_DiscoveredRouterHashes;
i2p::util::MemoryPoolMt<RequestedDestination> m_RequestedDestinationsPool;
boost::asio::deadline_timer m_ManageRequestsTimer, m_ExploratoryTimer,
m_CleanupTimer, m_DiscoveredRoutersTimer;
std::mt19937 m_Rng;
};
}
}

View File

@ -1,25 +0,0 @@
/**
* This code is licensed under the MCGSI Public License
* Copyright 2018 Jeff Becker
*
*Kovri go write your own code
*
*/
#include "Poly1305.h"
#if !OPENSSL_AEAD_CHACHA20_POLY1305
namespace i2p
{
namespace crypto
{
void Poly1305HMAC(uint64_t * out, const uint64_t * key, const uint8_t * buf, std::size_t sz)
{
Poly1305 p(key);
p.Update(buf, sz);
p.Finish(out);
}
}
}
#endif

View File

@ -1,261 +0,0 @@
/**
* This code is licensed under the MCGSI Public License
* Copyright 2018 Jeff Becker
*
* Kovri go write your own code
*
*/
#ifndef LIBI2PD_POLY1305_H
#define LIBI2PD_POLY1305_H
#include <cstdint>
#include <cstring>
#include "Crypto.h"
#if !OPENSSL_AEAD_CHACHA20_POLY1305
namespace i2p
{
namespace crypto
{
const std::size_t POLY1305_DIGEST_BYTES = 16;
const std::size_t POLY1305_DIGEST_DWORDS = 4;
const std::size_t POLY1305_KEY_BYTES = 32;
const std::size_t POLY1305_KEY_DWORDS = 8;
const std::size_t POLY1305_BLOCK_BYTES = 16;
namespace poly1305
{
struct LongBlock
{
unsigned long data[17];
operator unsigned long * ()
{
return data;
}
};
struct Block
{
unsigned char data[17];
void Zero()
{
memset(data, 0, sizeof(data));
}
operator uint8_t * ()
{
return data;
}
Block & operator += (const Block & other)
{
unsigned short u;
unsigned int i;
for(u = 0, i = 0; i < 17; i++)
{
u += (unsigned short) data[i] + (unsigned short) other.data[i];
data[i] = (unsigned char) u & 0xff;
u >>= 8;
}
return *this;
}
Block & operator %=(const LongBlock & other)
{
unsigned long u;
unsigned int i;
u = 0;
for (i = 0; i < 16; i++) {
u += other.data[i];
data[i] = (unsigned char)u & 0xff;
u >>= 8;
}
u += other.data[16];
data[16] = (unsigned char)u & 0x03;
u >>= 2;
u += (u << 2);
for (i = 0; i < 16; i++) {
u += data[i];
data[i] = (unsigned char)u & 0xff;
u >>= 8;
}
data[16] += (unsigned char)u;
return *this;
}
Block & operator = (const Block & other)
{
memcpy(data, other.data, sizeof(data));
return *this;
}
Block & operator ~ ()
{
static const Block minusp = {
0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0xfc
};
Block orig;
unsigned char neg;
unsigned int i;
orig = *this;
*this += minusp;
neg = -(data[16] >> 7);
for(i = 0; i < 17; i++)
data[i] ^= neg & (orig.data[i] ^ data[i]);
return *this;
}
void PutKey(const uint64_t * key_l)
{
const uint8_t * key = (const uint8_t*) key_l;
data[0] = key[0] & 0xff;
data[1] = key[1] & 0xff;
data[2] = key[2] & 0xff;
data[3] = key[3] & 0x0f;
data[4] = key[4] & 0xfc;
data[5] = key[5] & 0xff;
data[6] = key[6] & 0xff;
data[7] = key[7] & 0x0f;
data[8] = key[8] & 0xfc;
data[9] = key[9] & 0xff;
data[10] = key[10] & 0xff;
data[11] = key[11] & 0x0f;
data[12] = key[12] & 0xfc;
data[13] = key[13] & 0xff;
data[14] = key[14] & 0xff;
data[15] = key[15] & 0x0f;
data[16] = 0;
}
template<typename Int_t>
void Put(const Int_t * d, uint8_t last=0)
{
memcpy(data, d, 16);
data[16] = last;
}
};
struct Buffer
{
uint8_t data[POLY1305_BLOCK_BYTES];
operator uint8_t * ()
{
return data;
}
};
}
struct Poly1305
{
Poly1305(const uint64_t * key)
{
m_Leftover = 0;
m_H.Zero();
m_Final = 0;
m_R.PutKey(key);
m_Pad.Put(key + 2);
}
void Update(const uint8_t * buf, size_t sz)
{
// process leftover
if(m_Leftover)
{
size_t want = POLY1305_BLOCK_BYTES - m_Leftover;
if(want > sz) want = sz;
memcpy(m_Buffer + m_Leftover, buf, want);
sz -= want;
buf += want;
m_Leftover += want;
if(m_Leftover < POLY1305_BLOCK_BYTES) return;
Blocks(m_Buffer, POLY1305_BLOCK_BYTES);
m_Leftover = 0;
}
// process blocks
if(sz >= POLY1305_BLOCK_BYTES)
{
size_t want = (sz & ~(POLY1305_BLOCK_BYTES - 1));
Blocks(buf, want);
buf += want;
sz -= want;
}
// leftover
if(sz)
{
memcpy(m_Buffer+m_Leftover, buf, sz);
m_Leftover += sz;
}
}
void Blocks(const uint8_t * buf, size_t sz)
{
const unsigned char hi = m_Final ^ 1;
while (sz >= POLY1305_BLOCK_BYTES) {
unsigned long u;
unsigned int i, j;
m_Msg.Put(buf, hi);
/* h += m */
m_H += m_Msg;
/* h *= r */
for (i = 0; i < 17; i++) {
u = 0;
for (j = 0; j <= i ; j++) {
u += (unsigned short)m_H.data[j] * m_R.data[i - j];
}
for (j = i + 1; j < 17; j++) {
unsigned long v = (unsigned short)m_H.data[j] * m_R.data[i + 17 - j];
v = ((v << 8) + (v << 6)); /* v *= (5 << 6); */
u += v;
}
m_HR[i] = u;
}
/* (partial) h %= p */
m_H %= m_HR;
buf += POLY1305_BLOCK_BYTES;
sz -= POLY1305_BLOCK_BYTES;
}
}
void Finish(uint64_t * out)
{
// process leftovers
if(m_Leftover)
{
size_t idx = m_Leftover;
m_Buffer[idx++] = 1;
for(; idx < POLY1305_BLOCK_BYTES; idx++)
m_Buffer[idx] = 0;
m_Final = 1;
Blocks(m_Buffer, POLY1305_BLOCK_BYTES);
}
// freeze H
~m_H;
// add pad
m_H += m_Pad;
// copy digest
memcpy(out, m_H, 16);
}
size_t m_Leftover;
poly1305::Buffer m_Buffer;
poly1305::Block m_H;
poly1305::Block m_R;
poly1305::Block m_Pad;
poly1305::Block m_Msg;
poly1305::LongBlock m_HR;
uint8_t m_Final;
};
void Poly1305HMAC(uint64_t * out, const uint64_t * key, const uint8_t * buf, std::size_t sz);
}
}
#endif
#endif

View File

@ -10,6 +10,7 @@
#include <unordered_map>
#include <list>
#include <thread>
#include <iomanip>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/ini_parser.hpp>
#include "Base.h"
@ -27,22 +28,18 @@ namespace data
static std::unordered_map<i2p::data::IdentHash, std::shared_ptr<RouterProfile> > g_Profiles;
static std::mutex g_ProfilesMutex;
static boost::posix_time::ptime GetTime ()
{
return boost::posix_time::second_clock::local_time();
}
RouterProfile::RouterProfile ():
m_LastUpdateTime (GetTime ()), m_IsUpdated (false),
m_LastDeclineTime (0), m_LastUnreachableTime (0),
m_IsUpdated (false), m_LastDeclineTime (0), m_LastUnreachableTime (0),
m_LastUpdateTime (i2p::util::GetSecondsSinceEpoch ()),
m_NumTunnelsAgreed (0), m_NumTunnelsDeclined (0), m_NumTunnelsNonReplied (0),
m_NumTimesTaken (0), m_NumTimesRejected (0), m_HasConnected (false)
m_NumTimesTaken (0), m_NumTimesRejected (0), m_HasConnected (false),
m_IsDuplicated (false)
{
}
void RouterProfile::UpdateTime ()
{
m_LastUpdateTime = GetTime ();
m_LastUpdateTime = i2p::util::GetSecondsSinceEpoch ();
m_IsUpdated = true;
}
@ -57,9 +54,11 @@ namespace data
usage.put (PEER_PROFILE_USAGE_TAKEN, m_NumTimesTaken);
usage.put (PEER_PROFILE_USAGE_REJECTED, m_NumTimesRejected);
usage.put (PEER_PROFILE_USAGE_CONNECTED, m_HasConnected);
if (m_IsDuplicated)
usage.put (PEER_PROFILE_USAGE_DUPLICATED, true);
// fill property tree
boost::property_tree::ptree pt;
pt.put (PEER_PROFILE_LAST_UPDATE_TIME, boost::posix_time::to_simple_string (m_LastUpdateTime));
pt.put (PEER_PROFILE_LAST_UPDATE_TIMESTAMP, m_LastUpdateTime);
if (m_LastUnreachableTime)
pt.put (PEER_PROFILE_LAST_UNREACHABLE_TIME, m_LastUnreachableTime);
pt.put_child (PEER_PROFILE_SECTION_PARTICIPATION, participation);
@ -101,10 +100,22 @@ namespace data
try
{
auto t = pt.get (PEER_PROFILE_LAST_UPDATE_TIME, "");
if (t.length () > 0)
m_LastUpdateTime = boost::posix_time::time_from_string (t);
if ((GetTime () - m_LastUpdateTime).hours () < PEER_PROFILE_EXPIRATION_TIMEOUT)
auto ts = pt.get (PEER_PROFILE_LAST_UPDATE_TIMESTAMP, 0);
if (ts)
m_LastUpdateTime = ts;
else
{
// try old lastupdatetime
auto ut = pt.get (PEER_PROFILE_LAST_UPDATE_TIME, "");
if (ut.length () > 0)
{
std::istringstream ss (ut); std::tm t;
ss >> std::get_time(&t, "%Y-%b-%d %H:%M:%S");
if (!ss.fail())
m_LastUpdateTime = mktime (&t); // t is local time
}
}
if (i2p::util::GetSecondsSinceEpoch () - m_LastUpdateTime < PEER_PROFILE_EXPIRATION_TIMEOUT)
{
m_LastUnreachableTime = pt.get (PEER_PROFILE_LAST_UNREACHABLE_TIME, 0);
try
@ -126,6 +137,7 @@ namespace data
m_NumTimesTaken = usage.get (PEER_PROFILE_USAGE_TAKEN, 0);
m_NumTimesRejected = usage.get (PEER_PROFILE_USAGE_REJECTED, 0);
m_HasConnected = usage.get (PEER_PROFILE_USAGE_CONNECTED, false);
m_IsDuplicated = usage.get (PEER_PROFILE_USAGE_DUPLICATED, false);
}
catch (boost::property_tree::ptree_bad_path& ex)
{
@ -178,6 +190,11 @@ namespace data
UpdateTime ();
}
void RouterProfile::Duplicated ()
{
m_IsDuplicated = true;
}
bool RouterProfile::IsLowPartcipationRate () const
{
return 4*m_NumTunnelsAgreed < m_NumTunnelsDeclined; // < 20% rate
@ -201,7 +218,7 @@ namespace data
bool RouterProfile::IsBad ()
{
if (IsDeclinedRecently () || IsUnreachable ()) return true;
if (IsDeclinedRecently () || IsUnreachable () || m_IsDuplicated) return true;
auto isBad = IsAlwaysDeclining () || IsLowPartcipationRate () /*|| IsLowReplyRate ()*/;
if (isBad && m_NumTimesRejected > 10*(m_NumTimesTaken + 1))
{
@ -260,15 +277,21 @@ namespace data
g_ProfilesStorage.Init(i2p::data::GetBase64SubstitutionTable(), 64);
}
void PersistProfiles ()
static void SaveProfilesToDisk (std::list<std::pair<i2p::data::IdentHash, std::shared_ptr<RouterProfile> > >&& profiles)
{
auto ts = GetTime ();
for (auto& it: profiles)
if (it.second) it.second->Save (it.first);
}
std::future<void> PersistProfiles ()
{
auto ts = i2p::util::GetSecondsSinceEpoch ();
std::list<std::pair<i2p::data::IdentHash, std::shared_ptr<RouterProfile> > > tmp;
{
std::unique_lock<std::mutex> l(g_ProfilesMutex);
for (auto it = g_Profiles.begin (); it != g_Profiles.end ();)
{
if ((ts - it->second->GetLastUpdateTime ()).total_seconds () > PEER_PROFILE_PERSIST_INTERVAL)
if (ts - it->second->GetLastUpdateTime () > PEER_PROFILE_PERSIST_INTERVAL)
{
if (it->second->IsUpdated ())
tmp.push_back (std::make_pair (it->first, it->second));
@ -278,8 +301,9 @@ namespace data
it++;
}
}
for (auto& it: tmp)
if (it.second) it.second->Save (it.first);
if (!tmp.empty ())
return std::async (std::launch::async, SaveProfilesToDisk, std::move (tmp));
return std::future<void>();
}
void SaveProfiles ()
@ -287,44 +311,51 @@ namespace data
std::unordered_map<i2p::data::IdentHash, std::shared_ptr<RouterProfile> > tmp;
{
std::unique_lock<std::mutex> l(g_ProfilesMutex);
tmp = g_Profiles;
g_Profiles.clear ();
std::swap (tmp, g_Profiles);
}
auto ts = GetTime ();
auto ts = i2p::util::GetSecondsSinceEpoch ();
for (auto& it: tmp)
if (it.second->IsUseful() && (it.second->IsUpdated () || (ts - it.second->GetLastUpdateTime ()).total_seconds () < PEER_PROFILE_EXPIRATION_TIMEOUT*3600))
if (it.second->IsUseful() && (it.second->IsUpdated () || ts - it.second->GetLastUpdateTime () < PEER_PROFILE_EXPIRATION_TIMEOUT))
it.second->Save (it.first);
}
void DeleteObsoleteProfiles ()
static void DeleteFilesFromDisk ()
{
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)
{
LogPrint(eLogDebug, "Profiling: Removing expired peer profile: ", path);
i2p::fs::Remove(path);
}
}
}
std::future<void> DeleteObsoleteProfiles ()
{
{
auto ts = GetTime ();
auto ts = i2p::util::GetSecondsSinceEpoch ();
std::unique_lock<std::mutex> l(g_ProfilesMutex);
for (auto it = g_Profiles.begin (); it != g_Profiles.end ();)
{
if ((ts - it->second->GetLastUpdateTime ()).total_seconds () >= PEER_PROFILE_EXPIRATION_TIMEOUT*3600)
if (ts - it->second->GetLastUpdateTime () >= PEER_PROFILE_EXPIRATION_TIMEOUT)
it = g_Profiles.erase (it);
else
it++;
}
}
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);
}
}
return std::async (std::launch::async, DeleteFilesFromDisk);
}
}
}

View File

@ -10,7 +10,7 @@
#define PROFILING_H__
#include <memory>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <future>
#include "Identity.h"
namespace i2p
@ -21,7 +21,8 @@ namespace data
const char PEER_PROFILE_SECTION_PARTICIPATION[] = "participation";
const char PEER_PROFILE_SECTION_USAGE[] = "usage";
// params
const char PEER_PROFILE_LAST_UPDATE_TIME[] = "lastupdatetime";
const char PEER_PROFILE_LAST_UPDATE_TIME[] = "lastupdatetime"; // deprecated
const char PEER_PROFILE_LAST_UPDATE_TIMESTAMP[] = "lastupdatetimestamp";
const char PEER_PROFILE_LAST_UNREACHABLE_TIME[] = "lastunreachabletime";
const char PEER_PROFILE_PARTICIPATION_AGREED[] = "agreed";
const char PEER_PROFILE_PARTICIPATION_DECLINED[] = "declined";
@ -29,10 +30,13 @@ namespace data
const char PEER_PROFILE_USAGE_TAKEN[] = "taken";
const char PEER_PROFILE_USAGE_REJECTED[] = "rejected";
const char PEER_PROFILE_USAGE_CONNECTED[] = "connected";
const char PEER_PROFILE_USAGE_DUPLICATED[] = "duplicated";
const int PEER_PROFILE_EXPIRATION_TIMEOUT = 36; // in hours (1.5 days)
const int PEER_PROFILE_AUTOCLEAN_TIMEOUT = 3 * 3600; // in seconds (3 hours)
const int PEER_PROFILE_AUTOCLEAN_VARIANCE = 3600; // in seconds (1 hour)
const int PEER_PROFILE_EXPIRATION_TIMEOUT = 36*60*60; // in seconds (1.5 days)
const int PEER_PROFILE_AUTOCLEAN_TIMEOUT = 1500; // in seconds (25 minutes)
const int PEER_PROFILE_AUTOCLEAN_VARIANCE = 900; // in seconds (15 minutes)
const int PEER_PROFILE_OBSOLETE_PROFILES_CLEAN_TIMEOUT = 5400; // in seconds (1.5 hours)
const int PEER_PROFILE_OBSOLETE_PROFILES_CLEAN_VARIANCE = 2400; // in seconds (40 minutes)
const int PEER_PROFILE_DECLINED_RECENTLY_INTERVAL = 150; // in seconds (2.5 minutes)
const int PEER_PROFILE_PERSIST_INTERVAL = 3300; // in seconds (55 minutes)
const int PEER_PROFILE_UNREACHABLE_INTERVAL = 480; // in seconds (8 minutes)
@ -56,11 +60,13 @@ namespace data
void Unreachable (bool unreachable);
void Connected ();
void Duplicated ();
boost::posix_time::ptime GetLastUpdateTime () const { return m_LastUpdateTime; };
uint64_t GetLastUpdateTime () const { return m_LastUpdateTime; };
bool IsUpdated () const { return m_IsUpdated; };
bool IsUseful() const;
bool IsDuplicated () const { return m_IsDuplicated; };
private:
@ -73,9 +79,8 @@ namespace data
private:
boost::posix_time::ptime m_LastUpdateTime; // TODO: use std::chrono
bool m_IsUpdated;
uint64_t m_LastDeclineTime, m_LastUnreachableTime; // in seconds
uint64_t m_LastDeclineTime, m_LastUnreachableTime, m_LastUpdateTime; // in seconds
// participation
uint32_t m_NumTunnelsAgreed;
uint32_t m_NumTunnelsDeclined;
@ -84,14 +89,15 @@ namespace data
uint32_t m_NumTimesTaken;
uint32_t m_NumTimesRejected;
bool m_HasConnected; // successful trusted(incoming or NTCP2) connection
bool m_IsDuplicated;
};
std::shared_ptr<RouterProfile> GetRouterProfile (const IdentHash& identHash);
bool IsRouterBanned (const IdentHash& identHash); // check only existing profiles
void InitProfilesStorage ();
void DeleteObsoleteProfiles ();
std::future<void> DeleteObsoleteProfiles ();
void SaveProfiles ();
void PersistProfiles ();
std::future<void> PersistProfiles ();
}
}

View File

@ -57,11 +57,8 @@ namespace i2p
{
m_Service.reset (new RouterService);
m_Service->Start ();
if (!m_IsHiddenMode)
{
m_PublishTimer.reset (new boost::asio::deadline_timer (m_Service->GetService ()));
ScheduleInitialPublish ();
}
m_PublishTimer.reset (new boost::asio::deadline_timer (m_Service->GetService ()));
ScheduleInitialPublish ();
m_CongestionUpdateTimer.reset (new boost::asio::deadline_timer (m_Service->GetService ()));
ScheduleCongestionUpdate ();
m_CleanupTimer.reset (new boost::asio::deadline_timer (m_Service->GetService ()));
@ -78,9 +75,16 @@ namespace i2p
if (m_CongestionUpdateTimer)
m_CongestionUpdateTimer->cancel ();
m_Service->Stop ();
CleanUp (); // GarlicDestination
}
}
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 ()
{
m_Keys = i2p::data::PrivateKeys::CreateRandomKeys (i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519,
@ -249,7 +253,10 @@ namespace i2p
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_LastUpdateTime = i2p::util::GetSecondsSinceEpoch ();
}
@ -560,10 +567,10 @@ namespace i2p
{
m_IsFloodfill = floodfill;
if (floodfill)
m_RouterInfo.UpdateCaps (m_RouterInfo.GetCaps () | i2p::data::RouterInfo::eFloodfill);
m_RouterInfo.UpdateFloodfillProperty (true);
else
{
m_RouterInfo.UpdateCaps (m_RouterInfo.GetCaps () & ~i2p::data::RouterInfo::eFloodfill);
m_RouterInfo.UpdateFloodfillProperty (false);
// 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_ROUTERS);
@ -1324,9 +1331,15 @@ namespace i2p
if (ecode != boost::asio::error::operation_aborted)
{
if (m_RouterInfo.IsReachableBy (i2p::data::RouterInfo::eAllTransports))
{
UpdateCongestion ();
HandlePublishTimer (ecode);
}
else
{
UpdateTimestamp (i2p::util::GetSecondsSinceEpoch ());
ScheduleInitialPublish ();
}
}
}
@ -1348,16 +1361,21 @@ namespace i2p
{
if (ecode != boost::asio::error::operation_aborted)
{
m_PublishExcluded.clear ();
m_PublishReplyToken = 0;
if (IsFloodfill ())
{
UpdateStats (); // for floodfill
m_PublishExcluded.insert (i2p::context.GetIdentHash ()); // don't publish to ourselves
}
UpdateTimestamp (i2p::util::GetSecondsSinceEpoch ());
Publish ();
SchedulePublishResend ();
if (!m_IsHiddenMode)
{
m_PublishExcluded.clear ();
m_PublishReplyToken = 0;
if (IsFloodfill ())
{
UpdateStats (); // for floodfill
m_PublishExcluded.insert (i2p::context.GetIdentHash ()); // don't publish to ourselves
}
Publish ();
SchedulePublishResend ();
}
else
SchedulePublish ();
}
}
@ -1382,8 +1400,9 @@ namespace i2p
if (m_Service)
m_Service->GetService ().post ([this]() { HandlePublishResendTimer (boost::system::error_code ()); });
};
if (floodfill->IsReachableFrom (i2p::context.GetRouterInfo ()) || // are we able to connect?
i2p::transport::transports.IsConnected (floodfill->GetIdentHash ())) // already connected ?
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
auto msg = CreateDatabaseStoreMsg (i2p::context.GetSharedRouterInfo (), replyToken);
@ -1454,23 +1473,28 @@ namespace i2p
{
if (ecode != boost::asio::error::operation_aborted)
{
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;
else if (congestionLevel > CONGESTION_LEVEL_MEDIUM)
c = i2p::data::RouterInfo::eMediumCongestion;
}
if (m_RouterInfo.UpdateCongestion (c))
UpdateRouterInfo ();
UpdateCongestion ();
ScheduleCongestionUpdate ();
}
}
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;
else if (congestionLevel > CONGESTION_LEVEL_MEDIUM)
c = i2p::data::RouterInfo::eMediumCongestion;
}
if (m_RouterInfo.UpdateCongestion (c))
UpdateRouterInfo ();
}
void RouterContext::ScheduleCleanupTimer ()
{
if (m_CleanupTimer)

View File

@ -12,7 +12,7 @@
#include <inttypes.h>
#include <string>
#include <memory>
#include <set>
#include <unordered_set>
#include <boost/asio.hpp>
#include "Identity.h"
#include "RouterInfo.h"
@ -114,7 +114,8 @@ namespace garlic
return std::shared_ptr<i2p::garlic::GarlicDestination> (this,
[](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 * GetNTCP2StaticPrivateKey () const { return m_NTCP2Keys ? m_NTCP2Keys->staticPrivateKey : nullptr; };
const uint8_t * GetNTCP2IV () const { return m_NTCP2Keys ? m_NTCP2Keys->iv : nullptr; };
@ -229,6 +230,7 @@ namespace garlic
void HandlePublishResendTimer (const boost::system::error_code& ecode);
void ScheduleCongestionUpdate ();
void HandleCongestionUpdateTimer (const boost::system::error_code& ecode);
void UpdateCongestion ();
void ScheduleCleanupTimer ();
void HandleCleanupTimer (const boost::system::error_code& ecode);
@ -255,9 +257,10 @@ namespace garlic
// publish
std::unique_ptr<RouterService> m_Service;
std::unique_ptr<boost::asio::deadline_timer> m_PublishTimer, m_CongestionUpdateTimer, m_CleanupTimer;
std::set<i2p::data::IdentHash> m_PublishExcluded;
std::unordered_set<i2p::data::IdentHash> m_PublishExcluded;
uint32_t m_PublishReplyToken;
bool m_IsHiddenMode; // not publish
mutable std::mutex m_RouterInfoMutex;
};
extern RouterContext context;

View File

@ -12,6 +12,7 @@
#include <fstream>
#include <boost/lexical_cast.hpp>
#include <boost/make_shared.hpp>
#include <boost/algorithm/string.hpp> // for boost::to_lower
#if (BOOST_VERSION >= 105300)
#include <boost/atomic.hpp>
#endif
@ -34,6 +35,7 @@ namespace data
{
if (len > size ()) len = size ();
memcpy (data (), buf, len);
m_BufferLen = len;
}
RouterInfo::RouterInfo (): m_Buffer (nullptr)
@ -42,8 +44,8 @@ namespace data
}
RouterInfo::RouterInfo (const std::string& fullPath):
m_FamilyID (0), m_IsUpdated (false), m_IsUnreachable (false),
m_SupportedTransports (0),m_ReachableTransports (0),
m_FamilyID (0), m_IsUpdated (false), m_IsUnreachable (false), m_IsFloodfill (false),
m_SupportedTransports (0),m_ReachableTransports (0), m_PublishedTransports (0),
m_Caps (0), m_Version (0), m_Congestion (eLowCongestion)
{
m_Addresses = boost::make_shared<Addresses>(); // create empty list
@ -52,15 +54,15 @@ namespace data
}
RouterInfo::RouterInfo (std::shared_ptr<Buffer>&& buf, size_t len):
m_FamilyID (0), m_IsUpdated (true), m_IsUnreachable (false),
m_SupportedTransports (0), m_ReachableTransports (0),
m_FamilyID (0), m_IsUpdated (true), m_IsUnreachable (false), m_IsFloodfill (false),
m_SupportedTransports (0), m_ReachableTransports (0), m_PublishedTransports (0),
m_Caps (0), m_Version (0), m_Congestion (eLowCongestion)
{
if (len <= MAX_RI_BUFFER_SIZE)
{
m_Addresses = boost::make_shared<Addresses>(); // create empty list
m_Buffer = buf;
m_BufferLen = len;
if (m_Buffer) m_Buffer->SetBufferLen (len);
ReadFromBuffer (true);
}
else
@ -96,7 +98,8 @@ namespace data
m_IsUnreachable = false;
m_SupportedTransports = 0;
m_ReachableTransports = 0;
m_Caps = 0;
m_PublishedTransports = 0;
m_Caps = 0; m_IsFloodfill = false;
// don't clean up m_Addresses, it will be replaced in ReadFromStream
ClearProperties ();
// skip identity
@ -128,8 +131,8 @@ namespace data
if (s.is_open ())
{
s.seekg (0,std::ios::end);
m_BufferLen = s.tellg ();
if (m_BufferLen < 40 || m_BufferLen > MAX_RI_BUFFER_SIZE)
size_t bufferLen = s.tellg ();
if (bufferLen < 40 || bufferLen > MAX_RI_BUFFER_SIZE)
{
LogPrint(eLogError, "RouterInfo: File ", fullPath, " is malformed");
return false;
@ -137,7 +140,8 @@ namespace data
s.seekg(0, std::ios::beg);
if (!m_Buffer)
m_Buffer = NewBuffer ();
s.read((char *)m_Buffer->data (), m_BufferLen);
s.read((char *)m_Buffer->data (), bufferLen);
m_Buffer->SetBufferLen (bufferLen);
}
else
{
@ -162,11 +166,12 @@ namespace data
m_IsUnreachable = true;
return;
}
m_RouterIdentity = NewIdentity (m_Buffer->data (), m_BufferLen);
size_t bufferLen = m_Buffer->GetBufferLen ();
m_RouterIdentity = NewIdentity (m_Buffer->data (), bufferLen);
size_t identityLen = m_RouterIdentity->GetFullLen ();
if (identityLen >= m_BufferLen)
if (identityLen >= bufferLen)
{
LogPrint (eLogError, "RouterInfo: Identity length ", identityLen, " exceeds buffer size ", m_BufferLen);
LogPrint (eLogError, "RouterInfo: Identity length ", identityLen, " exceeds buffer size ", bufferLen);
m_IsUnreachable = true;
return;
}
@ -180,7 +185,7 @@ namespace data
return;
}
// verify signature
int l = m_BufferLen - m_RouterIdentity->GetSignatureLen ();
int l = bufferLen - m_RouterIdentity->GetSignatureLen ();
if (l < 0 || !m_RouterIdentity->Verify ((uint8_t *)m_Buffer->data (), l, (uint8_t *)m_Buffer->data () + l))
{
LogPrint (eLogError, "RouterInfo: Signature verification failed");
@ -190,7 +195,7 @@ namespace data
}
// parse RI
std::stringstream str;
str.write ((const char *)m_Buffer->data () + identityLen, m_BufferLen - identityLen);
str.write ((const char *)m_Buffer->data () + identityLen, bufferLen - identityLen);
ReadFromStream (str);
if (!str)
{
@ -216,7 +221,7 @@ namespace data
uint8_t cost; // ignore
s.read ((char *)&cost, sizeof (cost));
s.read ((char *)&address->date, sizeof (address->date));
bool isHost = false, isStaticKey = false, isV2 = false;
bool isHost = false, isStaticKey = false, isV2 = false, isIntroKey = false;
char transportStyle[6];
ReadString (transportStyle, 6, s);
if (!strncmp (transportStyle, "NTCP", 4)) // NTCP or NTCP2
@ -293,26 +298,38 @@ namespace data
address->caps = ExtractAddressCaps (value);
else if (!strcmp (key, "s")) // ntcp2 or ssu2 static key
{
Base64ToByteStream (value, strlen (value), address->s, 32);
if (!(address->s[31] & 0x80)) // check if x25519 public key
isStaticKey = true;
if (Base64ToByteStream (value, strlen (value), address->s, 32) == 32 &&
!(address->s[31] & 0x80)) // check if x25519 public key
isStaticKey = true;
else
address->transportStyle = eTransportUnknown; // invalid address
}
else if (!strcmp (key, "i")) // ntcp2 iv or ssu2 intro
{
if (address->IsNTCP2 ())
{
Base64ToByteStream (value, strlen (value), address->i, 16);
address->published = true; // presence of "i" means "published" NTCP2
if (Base64ToByteStream (value, strlen (value), address->i, 16) == 16)
address->published = true; // presence of "i" means "published" NTCP2
else
address->transportStyle = eTransportUnknown; // invalid address
}
else if (address->IsSSU2 ())
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"))
{
if (!strcmp (value, "2"))
isV2 = true;
else
{
LogPrint (eLogWarning, "RouterInfo: Unexpected value ", value, " for v");
address->transportStyle = eTransportUnknown; // invalid address
}
}
else if (key[0] == 'i')
{
@ -375,7 +392,7 @@ namespace data
supportedTransports |= (i2p::util::net::IsYggdrasilAddress (address->host) ? eNTCP2V6Mesh : eNTCP2V6);
else
supportedTransports |= eNTCP2V4;
m_ReachableTransports |= supportedTransports;
m_PublishedTransports |= supportedTransports;
}
else
{
@ -390,17 +407,17 @@ namespace data
}
}
}
else if (address->transportStyle == eTransportSSU2 && isV2 && isStaticKey)
else if (address->transportStyle == eTransportSSU2 && isV2 && isStaticKey && isIntroKey)
{
if (address->IsV4 ()) supportedTransports |= eSSU2V4;
if (address->IsV6 ()) supportedTransports |= eSSU2V6;
if (isHost && address->port)
{
if (address->host.is_v4 ()) m_ReachableTransports |= eSSU2V4;
if (address->host.is_v6 ()) m_ReachableTransports |= eSSU2V6;
if (address->host.is_v4 ()) m_PublishedTransports |= eSSU2V4;
if (address->host.is_v6 ()) m_PublishedTransports |= eSSU2V6;
address->published = true;
}
if (address->ssu && !address->ssu->introducers.empty ())
else if (address->ssu && !address->ssu->introducers.empty ())
{
// exclude invalid introducers
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
@ -420,6 +437,7 @@ namespace data
m_SupportedTransports |= supportedTransports;
}
}
m_ReachableTransports |= m_PublishedTransports;
// update addresses
#if (BOOST_VERSION >= 105300)
boost::atomic_store (&m_Addresses, addresses);
@ -449,7 +467,10 @@ namespace data
// extract caps
if (!strcmp (key, "caps"))
{
ExtractCaps (value);
m_IsFloodfill = IsDeclaredFloodfill ();
}
// extract version
else if (!strcmp (key, ROUTER_INFO_PROPERTY_VERSION))
{
@ -514,13 +535,20 @@ namespace data
case CAPS_FLAG_FLOODFILL:
m_Caps |= Caps::eFloodfill;
break;
case CAPS_FLAG_LOW_BANDWIDTH1:
case CAPS_FLAG_LOW_BANDWIDTH2:
case CAPS_FLAG_LOW_BANDWIDTH3:
m_BandwidthCap = *cap;
break;
case CAPS_FLAG_HIGH_BANDWIDTH1:
case CAPS_FLAG_HIGH_BANDWIDTH2:
m_Caps |= Caps::eHighBandwidth;
m_BandwidthCap = *cap;
break;
case CAPS_FLAG_EXTRA_BANDWIDTH1:
case CAPS_FLAG_EXTRA_BANDWIDTH2:
m_Caps |= Caps::eExtraBandwidth | Caps::eHighBandwidth;
m_BandwidthCap = *cap;
break;
case CAPS_FLAG_HIDDEN:
m_Caps |= Caps::eHidden;
@ -608,6 +636,19 @@ namespace 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)
{
if (m_IsUnreachable) return false; // don't save bad router
@ -616,14 +657,7 @@ namespace data
LogPrint (eLogWarning, "RouterInfo: Can't save, m_Buffer == NULL");
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 *)m_Buffer->data (), m_BufferLen);
return true;
return SaveToFile (fullPath, m_Buffer);
}
size_t RouterInfo::ReadString (char * str, size_t len, std::istream& s) const
@ -988,21 +1022,28 @@ namespace data
bool RouterInfo::IsEligibleFloodfill () const
{
// floodfill must have published ipv4, >= 0.9.38 and not DSA
return m_Version >= NETDB_MIN_FLOODFILL_VERSION && IsPublished (true) &&
// floodfill must have published ipv4 or reachable ipv4 and published ipv6
// >= 0.9.59 and not DSA
return m_Version >= NETDB_MIN_FLOODFILL_VERSION && (IsPublished (true) ||
(IsReachableBy (eNTCP2V4 | eSSU2V4) && IsPublished (false))) &&
GetIdentity ()->GetSigningKeyType () != SIGNING_KEY_TYPE_DSA_SHA1;
}
bool RouterInfo::IsPublished (bool v4) const
{
if (m_Caps & (eUnreachable | eHidden)) return false; // if router sets U or H we assume that all addresses are not published
auto addr = GetAddresses ();
if (v4)
return ((*addr)[eNTCP2V4Idx] && ((*addr)[eNTCP2V4Idx])->published) ||
((*addr)[eSSU2V4Idx] && ((*addr)[eSSU2V4Idx])->published);
else
return ((*addr)[eNTCP2V6Idx] && ((*addr)[eNTCP2V6Idx])->published) ||
((*addr)[eSSU2V6Idx] && ((*addr)[eSSU2V6Idx])->published);
return IsPublishedOn (v4 ? (eNTCP2V4 | eSSU2V4) : (eNTCP2V6 | eSSU2V6));
}
bool RouterInfo::IsPublishedOn (CompatibleTransports transports) const
{
return m_PublishedTransports & transports;
}
bool RouterInfo::IsNAT2NATOnly (const RouterInfo& other) const
{
return !(m_PublishedTransports & other.m_SupportedTransports) &&
!(other.m_PublishedTransports & m_SupportedTransports);
}
bool RouterInfo::IsSSU2PeerTesting (bool v4) const
@ -1091,9 +1132,15 @@ namespace data
m_Buffer = NewBuffer ();
if (len > m_Buffer->size ()) len = m_Buffer->size ();
memcpy (m_Buffer->data (), buf, len);
m_BufferLen = len;
m_Buffer->SetBufferLen (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
{
return netdb.NewRouterInfoBuffer ();
@ -1425,6 +1472,20 @@ namespace data
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
{
uint8_t len = str.size ();

View File

@ -188,6 +188,14 @@ namespace data
Buffer () = default;
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;
@ -227,8 +235,9 @@ namespace data
void SetUnreachableAddressesTransportCaps (uint8_t transports); // bitmask of AddressCaps
void UpdateSupportedTransports ();
void UpdateIntroducers (uint64_t ts); // ts in seconds
bool IsFloodfill () const { return m_Caps & Caps::eFloodfill; };
void ResetFlooldFill () { m_Caps &= ~Caps::eFloodfill; };
bool IsFloodfill () const { return m_IsFloodfill; };
void SetFloodfill () { m_IsFloodfill = true; };
void ResetFloodfill () { m_IsFloodfill = false; };
bool IsECIES () const { return m_RouterIdentity->GetCryptoKeyType () == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD; };
bool IsNTCP2 (bool v4only = true) const;
bool IsNTCP2V6 () const { return m_SupportedTransports & eNTCP2V6; };
@ -247,17 +256,22 @@ namespace data
bool IsReachableFrom (const RouterInfo& other) const { return m_ReachableTransports & other.m_SupportedTransports; };
bool IsReachableBy (CompatibleTransports transports) const { return m_ReachableTransports & transports; };
CompatibleTransports GetCompatibleTransports (bool incoming) const { return incoming ? m_ReachableTransports : m_SupportedTransports; };
CompatibleTransports GetPublishedTransports () const { return m_PublishedTransports; };
bool HasValidAddresses () const { return m_SupportedTransports; };
bool IsHidden () const { return m_Caps & eHidden; };
bool IsHighBandwidth () const { return m_Caps & RouterInfo::eHighBandwidth; };
bool IsExtraBandwidth () const { return m_Caps & RouterInfo::eExtraBandwidth; };
bool IsEligibleFloodfill () const;
bool IsDeclaredFloodfill () const { return m_Caps & RouterInfo::eFloodfill; };
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 IsSSU2Introducer (bool v4) const;
bool IsHighCongestion (bool highBandwidth) const;
uint8_t GetCaps () const { return m_Caps; };
char GetBandwidthCap() const { return m_BandwidthCap; };
void SetCaps (uint8_t caps) { m_Caps = caps; };
Congestion GetCongestion () const { return m_Congestion; };
@ -268,17 +282,21 @@ namespace data
const uint8_t * GetBuffer () const { return m_Buffer ? m_Buffer->data () : nullptr; };
const uint8_t * LoadBuffer (const std::string& fullPath); // load if necessary
size_t GetBufferLen () const { return m_BufferLen; };
size_t GetBufferLen () const { return m_Buffer ? m_Buffer->GetBufferLen () : 0; };
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; };
void SetUpdated (bool updated) { m_IsUpdated = updated; };
bool SaveToFile (const std::string& fullPath);
static bool SaveToFile (const std::string& fullPath, std::shared_ptr<Buffer> buf);
std::shared_ptr<RouterProfile> GetProfile () const;
void DropProfile () { m_Profile = nullptr; };
bool HasProfile () const { return (bool)m_Profile; };
bool Update (const uint8_t * buf, size_t len);
void DeleteBuffer () { m_Buffer = nullptr; };
bool IsNewer (const uint8_t * buf, size_t len) const;
/** return true if we are in a router family and the signature is valid */
@ -295,7 +313,7 @@ namespace data
RouterInfo ();
uint8_t * GetBufferPointer (size_t offset = 0 ) { return m_Buffer->data () + offset; };
void UpdateBuffer (const uint8_t * buf, size_t len);
void SetBufferLen (size_t len) { m_BufferLen = len; };
void SetBufferLen (size_t len) { if (m_Buffer) m_Buffer->SetBufferLen (len); };
void RefreshTimestamp ();
CompatibleTransports GetReachableTransports () const { return m_ReachableTransports; };
void SetReachableTransports (CompatibleTransports transports) { m_ReachableTransports = transports; };
@ -323,12 +341,12 @@ namespace data
FamilyID m_FamilyID;
std::shared_ptr<const IdentityEx> m_RouterIdentity;
std::shared_ptr<Buffer> m_Buffer;
size_t m_BufferLen;
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
bool m_IsUpdated, m_IsUnreachable;
CompatibleTransports m_SupportedTransports, m_ReachableTransports;
bool m_IsUpdated, m_IsUnreachable, m_IsFloodfill;
CompatibleTransports m_SupportedTransports, m_ReachableTransports, m_PublishedTransports;
uint8_t m_Caps;
char m_BandwidthCap;
int m_Version;
Congestion m_Congestion;
mutable std::shared_ptr<RouterProfile> m_Profile;
@ -347,7 +365,8 @@ namespace data
void DeleteProperty (const std::string& key);
std::string GetProperty (const std::string& key) const;
void ClearProperties () override { m_Properties.clear (); };
void UpdateFloodfillProperty (bool floodfill);
bool AddSSU2Introducer (const Introducer& introducer, bool v4);
bool RemoveSSU2Introducer (const IdentHash& h, bool v4);

View File

@ -25,7 +25,7 @@ namespace transport
m_TerminationTimer (GetService ()), m_CleanupTimer (GetService ()), m_ResendTimer (GetService ()),
m_IntroducersUpdateTimer (GetService ()), m_IntroducersUpdateTimerV6 (GetService ()),
m_IsPublished (true), m_IsSyncClockFromPeers (true), m_PendingTimeOffset (0),
m_IsThroughProxy (false)
m_Rng(i2p::util::GetMonotonicMicroseconds ()%1000000LL), m_IsThroughProxy (false)
{
}
@ -455,10 +455,12 @@ namespace transport
if (ident)
{
auto ret = m_SessionsByRouterHash.emplace (ident->GetIdentHash (), session);
if (!ret.second)
if (!ret.second && ret.first->second != session)
{
// session already exists
LogPrint (eLogWarning, "SSU2: Session to ", ident->GetIdentHash ().ToBase64 (), " already exists");
// move unsent msgs to new session
ret.first->second->MoveSendQueue (session);
// terminate existing
GetService ().post (std::bind (&SSU2Session::RequestTermination, ret.first->second, eSSU2TerminationReasonReplacedByNewSession));
// update session
@ -499,17 +501,15 @@ namespace transport
}
std::shared_ptr<SSU2Session> SSU2Server::GetRandomPeerTestSession (
i2p::data::RouterInfo::CompatibleTransports remoteTransports, const i2p::data::IdentHash& excluded) const
i2p::data::RouterInfo::CompatibleTransports remoteTransports, const i2p::data::IdentHash& excluded)
{
if (m_Sessions.empty ()) return nullptr;
uint16_t ind;
RAND_bytes ((uint8_t *)&ind, sizeof (ind));
ind %= m_Sessions.size ();
int ind = m_Rng () % m_Sessions.size ();
auto it = m_Sessions.begin ();
std::advance (it, ind);
while (it != m_Sessions.end ())
{
if ((it->second->GetRemotePeerTestTransports () & remoteTransports) &&
if (it->second->IsEstablished () && (it->second->GetRemotePeerTestTransports () & remoteTransports) &&
it->second->GetRemoteIdentity ()->GetIdentHash () != excluded)
return it->second;
it++;
@ -518,7 +518,7 @@ namespace transport
it = m_Sessions.begin ();
while (it != m_Sessions.end () && ind)
{
if ((it->second->GetRemotePeerTestTransports () & remoteTransports) &&
if (it->second->IsEstablished () && (it->second->GetRemotePeerTestTransports () & remoteTransports) &&
it->second->GetRemoteIdentity ()->GetIdentHash () != excluded)
return it->second;
it++; ind--;
@ -773,82 +773,119 @@ namespace transport
auto address = session->GetAddress ();
if (!address) return;
session->WaitForIntroduction ();
auto ts = i2p::util::GetSecondsSinceEpoch ();
std::vector<int> indices; int i = 0;
// try to find existing session first
for (auto& it: address->ssu->introducers)
{
auto it1 = m_SessionsByRouterHash.find (it.iH);
if (it1 != m_SessionsByRouterHash.end ())
if (it.iTag && ts < it.iExp)
{
it1->second->Introduce (session, it.iTag);
return;
}
auto it1 = m_SessionsByRouterHash.find (it.iH);
if (it1 != m_SessionsByRouterHash.end ())
{
auto addr = it1->second->GetAddress ();
if (addr && addr->IsIntroducer ())
{
it1->second->Introduce (session, it.iTag);
return;
}
}
else
indices.push_back(i);
}
i++;
}
// we have to start a new session to an introducer
auto ts = i2p::util::GetSecondsSinceEpoch ();
std::vector<i2p::data::IdentHash> newRouters;
std::shared_ptr<i2p::data::RouterInfo> r;
std::shared_ptr<const i2p::data::RouterInfo::Address> addr;
uint32_t relayTag = 0;
if (!address->ssu->introducers.empty ())
if (!indices.empty ())
{
std::vector<int> indices;
for (int i = 0; i < (int)address->ssu->introducers.size (); i++) indices.push_back(i);
if (indices.size () > 1)
std::shuffle (indices.begin(), indices.end(), std::mt19937(std::random_device()()));
std::shuffle (indices.begin(), indices.end(), m_Rng);
for (auto i: indices)
for (auto ind: indices)
{
const auto& introducer = address->ssu->introducers[indices[i]];
if (introducer.iTag && ts < introducer.iExp)
{
r = i2p::data::netdb.FindRouter (introducer.iH);
if (r && r->IsReachableFrom (i2p::context.GetRouterInfo ()))
const auto& introducer = address->ssu->introducers[ind];
// introducer is not expired, because in indices
r = i2p::data::netdb.FindRouter (introducer.iH);
if (r)
{
if (r->IsPublishedOn (i2p::context.GetRouterInfo ().GetCompatibleTransports (false) & // outgoing
(i2p::data::RouterInfo::eSSU2V4 | i2p::data::RouterInfo::eSSU2V6)))
{
relayTag = introducer.iTag;
if (relayTag) break;
addr = address->IsV6 () ? r->GetSSU2V6Address () : r->GetSSU2V4Address ();
if (addr && addr->IsIntroducer () && !addr->host.is_unspecified () && addr->port &&
!i2p::transport::transports.IsInReservedRange(addr->host))
break;
else
{
// address is invalid or not intrudcer, 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->IsIntroducer () && !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;
r = nullptr;
}
}
}
}
else
r = nullptr;
}
else if (!i2p::data::IsRouterBanned (introducer.iH))
newRouters.push_back (introducer.iH);
}
}
if (r)
{
if (relayTag)
if (relayTag && addr)
{
// introducer and tag found connect to it through SSU2
auto addr = address->IsV6 () ? r->GetSSU2V6Address () : r->GetSSU2V4Address ();
if (addr)
auto s = FindPendingOutgoingSession (boost::asio::ip::udp::endpoint (addr->host, addr->port));
if (!s)
{
bool isValidEndpoint = !addr->host.is_unspecified () && addr->port &&
!i2p::transport::transports.IsInReservedRange(addr->host);
if (isValidEndpoint)
{
auto s = FindPendingOutgoingSession (boost::asio::ip::udp::endpoint (addr->host, addr->port));
if (!s)
{
s = std::make_shared<SSU2Session> (*this, r, addr);
s->SetOnEstablished ([session, s, relayTag]() { s->Introduce (session, relayTag); });
s->Connect ();
}
else
{
auto onEstablished = s->GetOnEstablished ();
if (onEstablished)
s->SetOnEstablished ([session, s, relayTag, onEstablished]()
{
onEstablished ();
s->Introduce (session, relayTag);
});
else
s->SetOnEstablished ([session, s, relayTag]() {s->Introduce (session, relayTag); });
}
}
s = std::make_shared<SSU2Session> (*this, r, addr);
s->SetOnEstablished ([session, s, relayTag]() { s->Introduce (session, relayTag); });
s->Connect ();
}
else
{
auto onEstablished = s->GetOnEstablished ();
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
{
// introducers not found, try to request them
for (auto& it: address->ssu->introducers)
if (it.iTag && ts < it.iExp)
i2p::data::netdb.RequestDestination (it.iH);
for (auto& it: newRouters)
i2p::data::netdb.RequestDestination (it);
session->Done (); // don't wait for connect timeout
}
}
@ -877,7 +914,8 @@ namespace transport
void SSU2Server::ScheduleTermination ()
{
m_TerminationTimer.expires_from_now (boost::posix_time::seconds(SSU2_TERMINATION_CHECK_TIMEOUT));
m_TerminationTimer.expires_from_now (boost::posix_time::seconds(
SSU2_TERMINATION_CHECK_TIMEOUT + m_Rng () % SSU2_TERMINATION_CHECK_TIMEOUT_VARIANCE));
m_TerminationTimer.async_wait (std::bind (&SSU2Server::HandleTerminationTimer,
this, std::placeholders::_1));
}
@ -973,8 +1011,9 @@ namespace transport
void SSU2Server::ScheduleResend (bool more)
{
m_ResendTimer.expires_from_now (boost::posix_time::milliseconds (more ? SSU2_RESEND_CHECK_MORE_TIMEOUT :
(SSU2_RESEND_CHECK_TIMEOUT + rand () % SSU2_RESEND_CHECK_TIMEOUT_VARIANCE)));
m_ResendTimer.expires_from_now (boost::posix_time::milliseconds (more ?
(SSU2_RESEND_CHECK_MORE_TIMEOUT + m_Rng () % SSU2_RESEND_CHECK_MORE_TIMEOUT_VARIANCE):
(SSU2_RESEND_CHECK_TIMEOUT + m_Rng () % SSU2_RESEND_CHECK_TIMEOUT_VARIANCE)));
m_ResendTimer.async_wait (std::bind (&SSU2Server::HandleResendTimer,
this, std::placeholders::_1));
}
@ -987,7 +1026,8 @@ namespace transport
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
for (auto it: m_Sessions)
{
resentPacketsNum += it.second->Resend (ts);
if (ts >= it.second->GetLastResendTime () + SSU2_RESEND_CHECK_TIMEOUT)
resentPacketsNum += it.second->Resend (ts);
if (resentPacketsNum > SSU2_MAX_RESEND_PACKETS) break;
}
for (auto it: m_PendingOutgoingSessions)
@ -1044,30 +1084,32 @@ namespace transport
return ret;
}
std::list<std::shared_ptr<SSU2Session> > SSU2Server::FindIntroducers (int maxNumIntroducers,
bool v4, const std::set<i2p::data::IdentHash>& excluded) const
std::vector<std::shared_ptr<SSU2Session> > SSU2Server::FindIntroducers (int maxNumIntroducers,
bool v4, const std::unordered_set<i2p::data::IdentHash>& excluded) const
{
std::list<std::shared_ptr<SSU2Session> > ret;
std::vector<std::shared_ptr<SSU2Session> > ret;
if (maxNumIntroducers <= 0) return ret;
auto newer = [](const std::shared_ptr<SSU2Session>& s1, const std::shared_ptr<SSU2Session>& s2) -> bool
{
auto t1 = s1->GetCreationTime (), t2 = s2->GetCreationTime ();
return (t1 != t2) ? (t1 > t2) : (s1->GetConnID () > s2->GetConnID ());
};
std::set<std::shared_ptr<SSU2Session>, decltype (newer)> introducers(newer);
for (const auto& s : m_Sessions)
{
if (s.second->IsEstablished () && (s.second->GetRelayTag () && s.second->IsOutgoing ()) &&
!excluded.count (s.second->GetRemoteIdentity ()->GetIdentHash ()) &&
((v4 && (s.second->GetRemoteTransports () & i2p::data::RouterInfo::eSSU2V4)) ||
(!v4 && (s.second->GetRemoteTransports () & i2p::data::RouterInfo::eSSU2V6))))
ret.push_back (s.second);
introducers.insert (s.second);
}
if ((int)ret.size () > maxNumIntroducers)
int i = 0;
for (auto it: introducers)
{
// shink ret randomly
int sz = ret.size () - maxNumIntroducers;
for (int i = 0; i < sz; i++)
{
auto ind = rand () % ret.size ();
auto it = ret.begin ();
std::advance (it, ind);
ret.erase (it);
}
}
ret.push_back (it);
i++;
if (i >= maxNumIntroducers) break;
}
return ret;
}
@ -1076,7 +1118,7 @@ namespace transport
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
std::list<i2p::data::IdentHash> newList, impliedList;
auto& introducers = v4 ? m_Introducers : m_IntroducersV6;
std::set<i2p::data::IdentHash> excluded;
std::unordered_set<i2p::data::IdentHash> excluded;
for (const auto& it : introducers)
{
std::shared_ptr<SSU2Session> session;
@ -1086,22 +1128,21 @@ namespace transport
session = it1->second;
excluded.insert (it);
}
if (session && session->IsEstablished () && session->GetRelayTag () && session->IsOutgoing ()) // still session with introducer?
{
if (ts < session->GetCreationTime () + SSU2_TO_INTRODUCER_SESSION_EXPIRATION)
if (session && session->IsEstablished () && session->GetRelayTag () && session->IsOutgoing () && // still session with introducer?
ts < session->GetCreationTime () + SSU2_TO_INTRODUCER_SESSION_EXPIRATION)
{
session->SendKeepAlive ();
if (ts < session->GetCreationTime () + SSU2_TO_INTRODUCER_SESSION_DURATION)
newList.push_back (it);
else
{
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 = nullptr;
}
}
else
session = nullptr;
}
impliedList.push_back (it); // keep in introducers list, but not publish
session = nullptr;
}
}
else
session = nullptr;
if (!session)
i2p::context.RemoveSSU2Introducer (it, v4);
}
@ -1180,7 +1221,7 @@ namespace transport
if (m_IsPublished)
{
m_IntroducersUpdateTimer.expires_from_now (boost::posix_time::seconds(
SSU2_KEEP_ALIVE_INTERVAL + rand () % SSU2_KEEP_ALIVE_INTERVAL_VARIANCE));
SSU2_KEEP_ALIVE_INTERVAL + m_Rng () % SSU2_KEEP_ALIVE_INTERVAL_VARIANCE));
m_IntroducersUpdateTimer.async_wait (std::bind (&SSU2Server::HandleIntroducersUpdateTimer,
this, std::placeholders::_1, true));
}
@ -1194,7 +1235,7 @@ namespace transport
i2p::context.ClearSSU2Introducers (true);
m_Introducers.clear ();
m_IntroducersUpdateTimer.expires_from_now (boost::posix_time::seconds(
(SSU2_KEEP_ALIVE_INTERVAL + rand () % SSU2_KEEP_ALIVE_INTERVAL_VARIANCE)/2));
(SSU2_KEEP_ALIVE_INTERVAL + m_Rng () % SSU2_KEEP_ALIVE_INTERVAL_VARIANCE)/2));
m_IntroducersUpdateTimer.async_wait (std::bind (&SSU2Server::HandleIntroducersUpdateTimer,
this, std::placeholders::_1, true));
}
@ -1205,7 +1246,7 @@ namespace transport
if (m_IsPublished)
{
m_IntroducersUpdateTimerV6.expires_from_now (boost::posix_time::seconds(
SSU2_KEEP_ALIVE_INTERVAL + rand () % SSU2_KEEP_ALIVE_INTERVAL_VARIANCE));
SSU2_KEEP_ALIVE_INTERVAL + m_Rng () % SSU2_KEEP_ALIVE_INTERVAL_VARIANCE));
m_IntroducersUpdateTimerV6.async_wait (std::bind (&SSU2Server::HandleIntroducersUpdateTimer,
this, std::placeholders::_1, false));
}
@ -1219,7 +1260,7 @@ namespace transport
i2p::context.ClearSSU2Introducers (false);
m_IntroducersV6.clear ();
m_IntroducersUpdateTimerV6.expires_from_now (boost::posix_time::seconds(
(SSU2_KEEP_ALIVE_INTERVAL + rand () % SSU2_KEEP_ALIVE_INTERVAL_VARIANCE)/2));
(SSU2_KEEP_ALIVE_INTERVAL + m_Rng () % SSU2_KEEP_ALIVE_INTERVAL_VARIANCE)/2));
m_IntroducersUpdateTimerV6.async_wait (std::bind (&SSU2Server::HandleIntroducersUpdateTimer,
this, std::placeholders::_1, false));
}

View File

@ -10,7 +10,10 @@
#define SSU2_H__
#include <unordered_map>
#include <unordered_set>
#include <vector>
#include <mutex>
#include <random>
#include "util.h"
#include "SSU2Session.h"
#include "Socks5.h"
@ -19,11 +22,13 @@ namespace i2p
{
namespace transport
{
const int SSU2_TERMINATION_CHECK_TIMEOUT = 25; // in seconds
const int SSU2_TERMINATION_CHECK_TIMEOUT = 23; // in seconds
const int SSU2_TERMINATION_CHECK_TIMEOUT_VARIANCE = 5; // 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_VARIANCE = 10; // in milliseconds
const int SSU2_RESEND_CHECK_MORE_TIMEOUT = 10; // in milliseconds
const int SSU2_RESEND_CHECK_MORE_TIMEOUT = 4; // 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 uint64_t SSU2_SOCKET_MIN_BUFFER_SIZE = 128 * 1024;
const uint64_t SSU2_SOCKET_MAX_BUFFER_SIZE = 4 * 1024 * 1024;
@ -67,6 +72,8 @@ namespace transport
bool UsesProxy () const { return m_IsThroughProxy; };
bool IsSupported (const boost::asio::ip::address& addr) 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; };
void AdjustTimeOffset (int64_t offset, std::shared_ptr<const i2p::data::IdentityEx> from);
@ -78,7 +85,7 @@ namespace transport
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> GetRandomPeerTestSession (i2p::data::RouterInfo::CompatibleTransports remoteTransports,
const i2p::data::IdentHash& excluded) const;
const i2p::data::IdentHash& excluded);
void AddRelay (uint32_t tag, std::shared_ptr<SSU2Session> relay);
void RemoveRelay (uint32_t tag);
@ -125,8 +132,8 @@ namespace transport
void HandleResendTimer (const boost::system::error_code& ecode);
void ConnectThroughIntroducer (std::shared_ptr<SSU2Session> session);
std::list<std::shared_ptr<SSU2Session> > FindIntroducers (int maxNumIntroducers,
bool v4, const std::set<i2p::data::IdentHash>& excluded) const;
std::vector<std::shared_ptr<SSU2Session> > FindIntroducers (int maxNumIntroducers,
bool v4, const std::unordered_set<i2p::data::IdentHash>& excluded) const;
void UpdateIntroducers (bool v4);
void ScheduleIntroducersUpdateTimer ();
void HandleIntroducersUpdateTimer (const boost::system::error_code& ecode, bool v4);
@ -166,6 +173,7 @@ namespace transport
bool m_IsSyncClockFromPeers;
int64_t m_PendingTimeOffset; // during peer test
std::shared_ptr<const i2p::data::IdentityEx> m_PendingTimeOffsetFrom;
std::mt19937 m_Rng;
// proxy
bool m_IsThroughProxy;

View File

@ -90,7 +90,8 @@ namespace transport
m_WindowSize (SSU2_MIN_WINDOW_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_MaxPayloadSize (SSU2_MIN_PACKET_SIZE - IPV6_HEADER_SIZE - UDP_HEADER_SIZE - 32), // min size
m_LastResendTime (0), m_LastResendAttemptTime (0)
{
m_NoiseState.reset (new i2p::crypto::NoiseSymmetricState);
if (in_RemoteRouter && m_Address)
@ -143,7 +144,7 @@ namespace transport
void SSU2Session::HandleConnectTimer (const boost::system::error_code& ecode)
{
if (!ecode)
if (!ecode && m_State != eSSU2SessionStateTerminated)
{
// timeout expired
if (m_State == eSSU2SessionStateIntroduced) // WaitForIntroducer
@ -312,6 +313,7 @@ namespace transport
m_SentHandshakePacket.reset (nullptr);
m_ConnectTimer.cancel ();
SetTerminationTimeout (SSU2_TERMINATION_TIMEOUT);
SendQueue ();
transports.PeerConnected (shared_from_this ());
if (m_OnEstablished)
{
@ -336,7 +338,7 @@ namespace transport
{
if (!s->IsEstablished ()) return;
uint8_t payload[SSU2_MAX_PACKET_SIZE];
size_t payloadSize = s->CreateRouterInfoBlock (payload, s->m_MaxPayloadSize - 32, i2p::context.GetSharedRouterInfo ());
size_t payloadSize = s->CreateRouterInfoBlock (payload, s->m_MaxPayloadSize - 32, i2p::context.CopyRouterInfoBuffer ());
if (payloadSize)
{
if (payloadSize < s->m_MaxPayloadSize)
@ -381,16 +383,33 @@ namespace transport
m_SendQueue.push_back (std::move (it));
}
}
SendQueue ();
if (m_SendQueue.size () > 0) // windows is full
Resend (i2p::util::GetMillisecondsSinceEpoch ());
if (IsEstablished ())
{
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 ()
{
if (!m_SendQueue.empty () && m_SentPackets.size () <= m_WindowSize)
if (!m_SendQueue.empty () && m_SentPackets.size () <= m_WindowSize && IsEstablished ())
{
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
uint64_t mts = i2p::util::GetMonotonicMicroseconds ();
@ -497,7 +516,7 @@ namespace transport
else
extraSize -= packet->payloadSize;
}
size_t offset = extraSize > 0 ? (rand () % extraSize) : 0;
size_t offset = extraSize > 0 ? (m_Server.GetRng ()() % extraSize) : 0;
if (offset + packet->payloadSize >= m_MaxPayloadSize) offset = 0;
auto size = CreateFirstFragmentBlock (packet->payload + packet->payloadSize, m_MaxPayloadSize - offset - packet->payloadSize, msg);
if (!size) return false;
@ -509,7 +528,7 @@ namespace transport
uint8_t fragmentNum = 0;
while (msg->offset < msg->len)
{
offset = extraSize > 0 ? (rand () % extraSize) : 0;
offset = extraSize > 0 ? (m_Server.GetRng ()() % extraSize) : 0;
packet = m_Server.GetSentPacketsPool ().AcquireShared ();
packet->payloadSize = CreateFollowOnFragmentBlock (packet->payload, m_MaxPayloadSize - offset, msg, fragmentNum, msgID);
extraSize -= offset;
@ -529,6 +548,8 @@ namespace transport
size_t SSU2Session::Resend (uint64_t ts)
{
if (ts + SSU2_RESEND_ATTEMPT_MIN_INTERVAL < m_LastResendAttemptTime) return 0;
m_LastResendAttemptTime = ts;
// resend handshake packet
if (m_SentHandshakePacket && ts >= m_SentHandshakePacket->sendTime + SSU2_HANDSHAKE_RESEND_INTERVAL)
{
@ -565,6 +586,7 @@ namespace transport
it++;
if (!resentPackets.empty ())
{
m_LastResendTime = ts;
#if (__cplusplus >= 201703L) // C++ 17 or higher
m_SentPackets.merge (resentPackets);
#else
@ -660,10 +682,14 @@ namespace transport
size_t payloadSize = 7;
if (GetRouterStatus () == eRouterStatusFirewalled && m_Address->IsIntroducer ())
{
// relay tag request
payload[payloadSize] = eSSU2BlkRelayTagRequest;
memset (payload + payloadSize + 1, 0, 2); // size = 0
payloadSize += 3;
if (!m_Server.IsMaxNumIntroducers (m_RemoteEndpoint.address ().is_v4 ()) ||
m_Server.GetRng ()() & 0x01) // request tag with probability 1/2 if we have enough introducers
{
// relay tag request
payload[payloadSize] = eSSU2BlkRelayTagRequest;
memset (payload + payloadSize + 1, 0, 2); // size = 0
payloadSize += 3;
}
}
payloadSize += CreatePaddingBlock (payload + payloadSize, 40 - payloadSize, 1);
// KDF for session request
@ -881,12 +907,12 @@ namespace transport
// payload
size_t maxPayloadSize = m_MaxPayloadSize - 48; // for part 2, 48 is part1
uint8_t * payload = m_SentHandshakePacket->payload;
size_t payloadSize = CreateRouterInfoBlock (payload, maxPayloadSize, i2p::context.GetSharedRouterInfo ());
size_t payloadSize = CreateRouterInfoBlock (payload, maxPayloadSize, i2p::context.CopyRouterInfoBuffer ());
if (!payloadSize)
{
// split by two fragments
maxPayloadSize += m_MaxPayloadSize;
payloadSize = CreateRouterInfoBlock (payload, maxPayloadSize, i2p::context.GetSharedRouterInfo ());
payloadSize = CreateRouterInfoBlock (payload, maxPayloadSize, i2p::context.CopyRouterInfoBuffer ());
header.h.flags[0] = 0x02; // frag 0, total fragments 2
// TODO: check if we need more fragments
}
@ -914,7 +940,7 @@ namespace transport
{
if (payloadSize > m_MaxPayloadSize - 48)
{
payloadSize = m_MaxPayloadSize - 48 - (rand () % 16);
payloadSize = m_MaxPayloadSize - 48 - (m_Server.GetRng ()() % 16);
if (m_SentHandshakePacket->payloadSize - payloadSize < 24)
payloadSize -= 24;
}
@ -1101,6 +1127,23 @@ namespace transport
LogPrint (eLogError, "SSU2: RouterInfo in SessionConfirmed is from future for ", (ri->GetTimestamp () - ts)/1000LL, " seconds");
return false;
}
// update RouterInfo in netdb
auto ri1 = i2p::data::netdb.AddRouterInfo (ri->GetBuffer (), ri->GetBufferLen ()); // ri points to one from netdb now
if (!ri1)
{
LogPrint (eLogError, "SSU2: Couldn't update RouterInfo from SessionConfirmed in netdb");
return false;
}
std::shared_ptr<i2p::data::RouterProfile> profile; // not null if older
if (ri->GetTimestamp () + i2p::data::NETDB_EXPIRATION_TIMEOUT_THRESHOLD*1000LL < ri1->GetTimestamp ())
{
// received RouterInfo is older than one in netdb
profile = i2p::data::GetRouterProfile (ri->GetIdentHash ()); // retrieve profile
if (profile && profile->IsDuplicated ())
return false;
}
ri = ri1;
m_Address = m_RemoteEndpoint.address ().is_v6 () ? ri->GetSSU2V6Address () : ri->GetSSU2V4Address ();
if (!m_Address || memcmp (S, m_Address->s, 32))
{
@ -1111,15 +1154,11 @@ namespace transport
(!m_RemoteEndpoint.address ().is_v6 () ||
memcmp (m_RemoteEndpoint.address ().to_v6 ().to_bytes ().data (), m_Address->host.to_v6 ().to_bytes ().data (), 8))) // temporary address
{
LogPrint (eLogError, "SSU2: Host mismatch between published address ", m_Address->host,
" and actual endpoint ", m_RemoteEndpoint.address (), " from ", i2p::data::GetIdentHashAbbreviation (ri->GetIdentHash ()));
return false;
}
// update RouterInfo in netdb
ri = i2p::data::netdb.AddRouterInfo (ri->GetBuffer (), ri->GetBufferLen ()); // ri points to one from netdb now
if (!ri)
{
LogPrint (eLogError, "SSU2: Couldn't update RouterInfo from SessionConfirmed in netdb");
if (profile) // older router?
profile->Duplicated (); // mark router as duplicated in profile
else
LogPrint (eLogError, "SSU2: Host mismatch between published address ", m_Address->host,
" and actual endpoint ", m_RemoteEndpoint.address (), " from ", i2p::data::GetIdentHashAbbreviation (ri->GetIdentHash ()));
return false;
}
SetRemoteIdentity (ri->GetRouterIdentity ());
@ -1551,14 +1590,9 @@ namespace transport
LogPrint (eLogDebug, "SSU2: Options");
break;
case eSSU2BlkRouterInfo:
{
// not from SessionConfirmed, we must add it instantly to use in next block
LogPrint (eLogDebug, "SSU2: RouterInfo");
auto ri = ExtractRouterInfo (buf + offset, size);
if (ri)
i2p::data::netdb.AddRouterInfo (ri->GetBuffer (), ri->GetBufferLen ()); // TODO: add ri
break;
}
HandleRouterInfo (buf + offset, size);
break;
case eSSU2BlkI2NPMessage:
{
LogPrint (eLogDebug, "SSU2: I2NP message");
@ -1633,8 +1667,12 @@ namespace transport
LogPrint (eLogDebug, "SSU2: RelayTagRequest");
if (!m_RelayTag)
{
RAND_bytes ((uint8_t *)&m_RelayTag, 4);
m_Server.AddRelay (m_RelayTag, shared_from_this ());
auto addr = FindLocalAddress ();
if (addr && addr->IsIntroducer ())
{
RAND_bytes ((uint8_t *)&m_RelayTag, 4);
m_Server.AddRelay (m_RelayTag, shared_from_this ());
}
}
break;
case eSSU2BlkRelayTag:
@ -1714,6 +1752,32 @@ 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)
{
if (m_State == eSSU2SessionStateSessionConfirmedSent)
@ -2454,6 +2518,8 @@ namespace transport
{
if (m_Address)
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;
}
@ -2535,27 +2601,34 @@ namespace transport
size_t SSU2Session::CreateRouterInfoBlock (uint8_t * buf, size_t len, std::shared_ptr<const i2p::data::RouterInfo> r)
{
if (!r || !r->GetBuffer () || len < 5) return 0;
if (!r || 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;
size_t size = r->GetBufferLen ();
size_t size = riBuffer->GetBufferLen ();
if (size + 5 < len)
{
memcpy (buf + 5, r->GetBuffer (), size);
memcpy (buf + 5, riBuffer->data (), size);
buf[3] = 0; // flag
}
else
{
i2p::data::GzipDeflator deflator;
deflator.SetCompressionLevel (9);
size = deflator.Deflate (r->GetBuffer (), r->GetBufferLen (), buf + 5, len - 5);
size = deflator.Deflate (riBuffer->data (), riBuffer->GetBufferLen (), buf + 5, len - 5);
if (!size) return 0; // doesn't fit
buf[3] = SSU2_ROUTER_INFO_FLAG_GZIP; // flag
}
htobe16buf (buf + 1, size + 2); // size
buf[4] = 1; // frag
return size + 5;
}
}
size_t SSU2Session::CreateAckBlock (uint8_t * buf, size_t len)
{
if (len < 8) return 0;
@ -2668,7 +2741,7 @@ namespace transport
size_t SSU2Session::CreatePaddingBlock (uint8_t * buf, size_t len, size_t minSize)
{
if (len < 3 || len < minSize) return 0;
size_t paddingSize = rand () & 0x0F; // 0 - 15
size_t paddingSize = m_Server.GetRng ()() & 0x0F; // 0 - 15
if (paddingSize + 3 > len) paddingSize = len - 3;
else if (paddingSize + 3 < minSize) paddingSize = minSize - 3;
buf[0] = eSSU2BlkPadding;
@ -2954,7 +3027,7 @@ namespace transport
{
uint8_t payload[SSU2_MAX_PACKET_SIZE];
payload[0] = eSSU2BlkPathChallenge;
size_t len = rand () % (m_MaxPayloadSize - 3);
size_t len = m_Server.GetRng ()() % (m_MaxPayloadSize - 3);
htobe16buf (payload + 1, len);
if (len > 0)
{
@ -3060,6 +3133,8 @@ namespace transport
m_Handler.Flush ();
m_IsDataReceived = false;
}
else if (!sent && !m_SentPackets.empty ()) // if only acks received, nothing sent and we still have something to resend
Resend (i2p::util::GetMillisecondsSinceEpoch ()); // than right time to resend
}
}

View File

@ -37,6 +37,7 @@ namespace transport
const size_t SSU2_MIN_PACKET_SIZE = 1280;
const int SSU2_HANDSHAKE_RESEND_INTERVAL = 1000; // in milliseconds
const int SSU2_MAX_NUM_RESENDS = 5;
const int SSU2_RESEND_ATTEMPT_MIN_INTERVAL = 3; // in milliseconds
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_RECEIVED_I2NP_MSGIDS_CLEANUP_TIMEOUT = 10; // in seconds
@ -255,8 +256,10 @@ namespace transport
void Done () override;
void SendLocalRouterInfo (bool update) 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; };
size_t Resend (uint64_t ts); // return number or resent packets
size_t Resend (uint64_t ts); // return number of resent packets
uint64_t GetLastResendTime () const { return m_LastResendTime; };
bool IsEstablished () const override { return m_State == eSSU2SessionStateEstablished; };
uint64_t GetConnID () const { return m_SourceConnID; };
SSU2SessionState GetState () const { return m_State; };
@ -301,6 +304,7 @@ namespace transport
void HandlePayload (const uint8_t * buf, size_t len);
void HandleDateTime (const uint8_t * buf, size_t len);
void HandleRouterInfo (const uint8_t * buf, size_t len);
void HandleAck (const uint8_t * buf, size_t len);
void HandleAckRange (uint32_t firstPacketNum, uint32_t lastPacketNum, uint64_t ts);
void HandleAddress (const uint8_t * buf, size_t len);
@ -325,6 +329,7 @@ namespace transport
size_t CreateAddressBlock (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& ep);
size_t CreateRouterInfoBlock (uint8_t * buf, size_t len, std::shared_ptr<const i2p::data::RouterInfo> r);
size_t CreateRouterInfoBlock (uint8_t * buf, size_t len, std::shared_ptr<const i2p::data::RouterInfo::Buffer> riBuffer);
size_t CreateAckBlock (uint8_t * buf, size_t len);
size_t CreatePaddingBlock (uint8_t * buf, size_t len, size_t minSize = 0);
size_t CreateI2NPBlock (uint8_t * buf, size_t len, std::shared_ptr<I2NPMessage>&& msg);
@ -369,6 +374,7 @@ namespace transport
size_t m_MaxPayloadSize;
std::unique_ptr<i2p::data::IdentHash> m_PathChallenge;
std::unordered_map<uint32_t, uint32_t> m_ReceivedI2NPMsgIDs; // msgID -> timestamp in seconds
uint64_t m_LastResendTime, m_LastResendAttemptTime; // in milliseconds
};
inline uint64_t CreateHeaderMask (const uint8_t * kh, const uint8_t * nonce)

View File

@ -69,28 +69,55 @@ namespace stream
Stream::Stream (boost::asio::io_service& service, StreamingDestination& local,
std::shared_ptr<const i2p::data::LeaseSet> remote, int port): m_Service (service),
m_SendStreamID (0), m_SequenceNumber (0),
m_TunnelsChangeSequenceNumber (0), m_LastReceivedSequenceNumber (-1),
m_Status (eStreamStatusNew), m_IsAckSendScheduled (false), m_LocalDestination (local),
m_RemoteLeaseSet (remote), m_ReceiveTimer (m_Service), m_ResendTimer (m_Service),
m_TunnelsChangeSequenceNumber (0), m_LastReceivedSequenceNumber (-1), m_PreviousReceivedSequenceNumber (-1),
m_LastConfirmedReceivedSequenceNumber (0), // for limit inbound speed
m_Status (eStreamStatusNew), m_IsAckSendScheduled (false), m_IsNAcked (false), m_IsFirstACK (false),
m_IsResendNeeded (false), m_IsFirstRttSample (false), m_IsSendTime (true), m_IsWinDropped (true),
m_IsTimeOutResend (false), m_LocalDestination (local),
m_RemoteLeaseSet (remote), m_ReceiveTimer (m_Service), m_SendTimer (m_Service), m_ResendTimer (m_Service),
m_AckSendTimer (m_Service), m_NumSentBytes (0), m_NumReceivedBytes (0), m_Port (port),
m_RTT (INITIAL_RTT), m_WindowSize (MIN_WINDOW_SIZE), m_RTO (INITIAL_RTO),
m_AckDelay (local.GetOwner ()->GetStreamingAckDelay ()),
m_LastWindowSizeIncreaseTime (0), m_NumResendAttempts (0), m_MTU (STREAMING_MTU)
m_RTT (INITIAL_RTT), m_SlowRTT (INITIAL_RTT), m_WindowSize (INITIAL_WINDOW_SIZE), m_LastWindowDropSize (0), m_WindowIncCounter (0), m_RTO (INITIAL_RTO),
m_AckDelay (local.GetOwner ()->GetStreamingAckDelay ()), m_PrevRTTSample (INITIAL_RTT),
m_PrevRTT (INITIAL_RTT), m_Jitter (0), m_MinPacingTime (0),
m_PacingTime (INITIAL_PACING_TIME), m_PacingTimeRem (0), m_DropWindowDelayTime (0), m_LastSendTime (0),
m_LastACKSendTime (0), m_PacketACKInterval (1), m_PacketACKIntervalRem (0), // for limit inbound speed
m_NumResendAttempts (0), m_NumPacketsToSend (0), m_MTU (STREAMING_MTU)
{
RAND_bytes ((uint8_t *)&m_RecvStreamID, 4);
m_RemoteIdentity = remote->GetIdentity ();
auto outboundSpeed = local.GetOwner ()->GetStreamingOutboundSpeed ();
if (outboundSpeed)
m_MinPacingTime = (1000000LL*STREAMING_MTU)/outboundSpeed;
auto inboundSpeed = local.GetOwner ()->GetStreamingInboundSpeed (); // for limit inbound speed
if (inboundSpeed)
m_PacketACKInterval = (1000000LL*STREAMING_MTU)/inboundSpeed;
}
Stream::Stream (boost::asio::io_service& service, StreamingDestination& local):
m_Service (service), m_SendStreamID (0), m_SequenceNumber (0),
m_TunnelsChangeSequenceNumber (0), m_LastReceivedSequenceNumber (-1),
m_Status (eStreamStatusNew), m_IsAckSendScheduled (false), m_LocalDestination (local),
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_WindowSize (MIN_WINDOW_SIZE), m_RTO (INITIAL_RTO), m_AckDelay (local.GetOwner ()->GetStreamingAckDelay ()),
m_LastWindowSizeIncreaseTime (0), m_NumResendAttempts (0), m_MTU (STREAMING_MTU)
m_TunnelsChangeSequenceNumber (0), m_LastReceivedSequenceNumber (-1), m_PreviousReceivedSequenceNumber (-1),
m_LastConfirmedReceivedSequenceNumber (0), // for limit inbound speed
m_Status (eStreamStatusNew), m_IsAckSendScheduled (false), m_IsNAcked (false), m_IsFirstACK (false),
m_IsResendNeeded (false), m_IsFirstRttSample (false), m_IsSendTime (true), m_IsWinDropped (true),
m_IsTimeOutResend (false), m_LocalDestination (local),
m_ReceiveTimer (m_Service), m_SendTimer (m_Service), m_ResendTimer (m_Service), m_AckSendTimer (m_Service),
m_NumSentBytes (0), m_NumReceivedBytes (0), m_Port (0), m_RTT (INITIAL_RTT), m_SlowRTT (INITIAL_RTT),
m_WindowSize (INITIAL_WINDOW_SIZE), m_LastWindowDropSize (0), m_WindowIncCounter (0),
m_RTO (INITIAL_RTO), m_AckDelay (local.GetOwner ()->GetStreamingAckDelay ()),
m_PrevRTTSample (INITIAL_RTT), m_PrevRTT (INITIAL_RTT), m_Jitter (0), m_MinPacingTime (0),
m_PacingTime (INITIAL_PACING_TIME), m_PacingTimeRem (0), m_DropWindowDelayTime (0), m_LastSendTime (0),
m_LastACKSendTime (0), m_PacketACKInterval (1), m_PacketACKIntervalRem (0), // for limit inbound speed
m_NumResendAttempts (0), m_NumPacketsToSend (0), m_MTU (STREAMING_MTU)
{
RAND_bytes ((uint8_t *)&m_RecvStreamID, 4);
auto outboundSpeed = local.GetOwner ()->GetStreamingOutboundSpeed ();
if (outboundSpeed)
m_MinPacingTime = (1000000LL*STREAMING_MTU)/outboundSpeed;
auto inboundSpeed = local.GetOwner ()->GetStreamingInboundSpeed (); // for limit inbound speed
if (inboundSpeed)
m_PacketACKInterval = (1000000LL*STREAMING_MTU)/inboundSpeed;
}
Stream::~Stream ()
@ -105,6 +132,7 @@ namespace stream
m_AckSendTimer.cancel ();
m_ReceiveTimer.cancel ();
m_ResendTimer.cancel ();
m_SendTimer.cancel ();
//CleanUp (); /* Need to recheck - broke working on windows */
if (deleteFromDestination)
m_LocalDestination.DeleteStream (shared_from_this ());
@ -119,6 +147,8 @@ namespace stream
m_ReceiveQueue.pop ();
m_LocalDestination.DeletePacket (packet);
}
m_NACKedPackets.clear ();
for (auto it: m_SentPackets)
m_LocalDestination.DeletePacket (it);
@ -203,8 +233,20 @@ namespace stream
{
// we have received duplicate
LogPrint (eLogWarning, "Streaming: Duplicate message ", receivedSeqn, " on sSID=", m_SendStreamID);
SendQuickAck (); // resend ack for previous message again
if (receivedSeqn <= m_PreviousReceivedSequenceNumber || receivedSeqn == m_LastReceivedSequenceNumber)
{
m_CurrentOutboundTunnel = m_LocalDestination.GetOwner ()->GetTunnelPool ()->GetNextOutboundTunnel (m_CurrentOutboundTunnel);
UpdateCurrentRemoteLease ();
}
m_PreviousReceivedSequenceNumber = receivedSeqn;
m_LocalDestination.DeletePacket (packet); // packet dropped
if (!m_IsAckSendScheduled)
{
SendQuickAck (); // resend ack for previous message again
auto ackTimeout = m_RTT/10;
if (ackTimeout > m_AckDelay) ackTimeout = m_AckDelay;
ScheduleAck (ackTimeout);
}
}
else
{
@ -216,7 +258,8 @@ namespace stream
if (!m_IsAckSendScheduled)
{
// send NACKs for missing messages
int ackTimeout = MIN_SEND_ACK_TIMEOUT*m_SavedPackets.size ();
SendQuickAck ();
auto ackTimeout = m_RTT/10;
if (ackTimeout > m_AckDelay) ackTimeout = m_AckDelay;
ScheduleAck (ackTimeout);
}
@ -277,6 +320,11 @@ namespace stream
{
const uint8_t * optionData = packet->GetOptionData ();
size_t optionSize = packet->GetOptionSize ();
if (optionSize > packet->len)
{
LogPrint (eLogInfo, "Streaming: Invalid option size ", optionSize, " Discarded");
return false;
}
if (flags & PACKET_FLAG_DELAY_REQUESTED)
{
if (!m_IsAckSendScheduled)
@ -290,7 +338,10 @@ namespace stream
shared_from_this (), std::placeholders::_1));
}
if (delayRequested >= DELAY_CHOKING)
{
m_WindowSize = 1;
m_WindowIncCounter = 0;
}
}
optionData += 2;
}
@ -403,13 +454,15 @@ namespace stream
bool acknowledged = false;
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
uint32_t ackThrough = packet->GetAckThrough ();
m_NACKedPackets.clear ();
if (ackThrough > m_SequenceNumber)
{
LogPrint (eLogError, "Streaming: Unexpected ackThrough=", ackThrough, " > seqn=", m_SequenceNumber);
return;
}
int rttSample = INT_MAX;
bool firstRttSample = false;
m_IsNAcked = false;
m_IsResendNeeded = false;
int nackCount = packet->GetNACKCount ();
for (auto it = m_SentPackets.begin (); it != m_SentPackets.end ();)
{
@ -422,6 +475,8 @@ namespace stream
for (int i = 0; i < nackCount; i++)
if (seqn == packet->GetNACK (i))
{
m_NACKedPackets.insert (*it);
m_IsNAcked = true;
nacked = true;
break;
}
@ -438,7 +493,7 @@ namespace stream
LogPrint (eLogError, "Streaming: Packet ", seqn, "sent from the future, sendTime=", sentPacket->sendTime);
if (!seqn)
{
firstRttSample = true;
m_IsFirstRttSample = true;
rttSample = rtt < 0 ? 1 : rtt;
}
else if (!sentPacket->resent && seqn > m_TunnelsChangeSequenceNumber && rtt >= 0)
@ -447,42 +502,86 @@ namespace stream
m_SentPackets.erase (it++);
m_LocalDestination.DeletePacket (sentPacket);
acknowledged = true;
if (m_WindowSize < WINDOW_SIZE)
m_WindowSize++; // slow start
if (m_WindowSize < MAX_WINDOW_SIZE && !m_IsFirstACK)
m_WindowIncCounter++;
}
else
break;
}
if (rttSample != INT_MAX)
{
if (firstRttSample)
if (m_IsFirstRttSample)
{
m_RTT = rttSample;
m_SlowRTT = rttSample;
m_PrevRTTSample = rttSample;
if (m_RoutingSession)
m_RoutingSession->SetSharedRoutingPath (
std::make_shared<i2p::garlic::GarlicRoutingPath> (
i2p::garlic::GarlicRoutingPath{m_CurrentOutboundTunnel, m_CurrentRemoteLease, (int)m_RTT, 0}));
m_IsFirstRttSample = false;
}
else
m_RTT = RTT_EWMA_ALPHA * rttSample + (1.0 - RTT_EWMA_ALPHA) * m_RTT;
m_RTT = RTT_EWMA_ALPHA * m_RTT + (1.0 - RTT_EWMA_ALPHA) * rttSample;
// calculate jitter
int jitter = 0;
if (rttSample > m_PrevRTTSample)
jitter = rttSample - m_PrevRTTSample;
else if (rttSample < m_PrevRTTSample)
jitter = m_PrevRTTSample - rttSample;
else
jitter = std::round (rttSample / 10); // 10%
jitter += 5; // for low-latency connections
m_Jitter = std::round (RTT_EWMA_ALPHA * jitter + (1.0 - RTT_EWMA_ALPHA) * m_Jitter);
m_PrevRTTSample = rttSample;
//
// delay-based CC
if ((m_PrevRTT > m_SlowRTT + m_Jitter) && (m_RTT > m_SlowRTT + m_Jitter) && !m_IsWinDropped) // Drop window if RTT grows too fast, late detection
{
if (m_LastWindowDropSize)
m_LastWindowDropSize = (m_LastWindowDropSize + m_WindowSize) / 2;
else
m_LastWindowDropSize = m_WindowSize;
m_WindowSize = m_WindowSize / 2; // /2
if (m_WindowSize < MIN_WINDOW_SIZE) m_WindowSize = MIN_WINDOW_SIZE;
m_WindowIncCounter = 0;
m_DropWindowDelayTime = ts + m_SlowRTT;
m_IsFirstACK = true;
m_IsWinDropped = true; // don't drop window twice
}
UpdatePacingTime ();
if (rttSample < m_RTT) // need for delay-based CC
m_SlowRTT = RTT_EWMA_ALPHA * rttSample + (1.0 - RTT_EWMA_ALPHA) * m_SlowRTT;
else
m_SlowRTT = RTT_EWMA_ALPHA * m_RTT + (1.0 - RTT_EWMA_ALPHA) * m_SlowRTT;
m_PrevRTT = m_RTT;
bool wasInitial = m_RTO == INITIAL_RTO;
m_RTO = std::max (MIN_RTO, (int)(m_RTT * 1.5)); // TODO: implement it better
m_RTO = std::max (MIN_RTO, (int)(m_RTT * 1.3 + m_Jitter)); // TODO: implement it better
if (wasInitial)
ScheduleResend ();
}
if (acknowledged && m_WindowSize >= WINDOW_SIZE)
if ( ts > m_DropWindowDelayTime)
m_IsWinDropped = false;
if (acknowledged || m_IsNAcked)
{
// linear growth
if (ts > m_LastWindowSizeIncreaseTime + m_RTT)
{
m_WindowSize++;
if (m_WindowSize > MAX_WINDOW_SIZE) m_WindowSize = MAX_WINDOW_SIZE;
m_LastWindowSizeIncreaseTime = ts;
}
ScheduleResend ();
}
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_SendBuffer.IsEmpty () && m_SentPackets.size () > 0) // tail loss
|| int(m_SentPackets.size ()) > m_WindowSize) // or we drop window
{
m_IsResendNeeded = true;
}
if (m_SentPackets.empty () && m_SendBuffer.IsEmpty ())
{
m_ResendTimer.cancel ();
m_SendTimer.cancel ();
}
if (acknowledged)
{
m_NumResendAttempts = 0;
m_IsFirstACK = false;
SendBuffer ();
}
if (m_Status == eStreamStatusClosed)
@ -557,9 +656,16 @@ namespace stream
void Stream::SendBuffer ()
{
ScheduleSend ();
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
int numMsgs = m_WindowSize - m_SentPackets.size ();
if (numMsgs <= 0) return; // window is full
if (numMsgs <= 0 || !m_IsSendTime) // window is full
{
m_LastSendTime = ts;
return;
}
else if (numMsgs > m_NumPacketsToSend)
numMsgs = m_NumPacketsToSend;
bool isNoAck = m_LastReceivedSequenceNumber < 0; // first packet
std::vector<Packet *> packets;
while ((m_Status == eStreamStatusNew) || (IsEstablished () && !m_SendBuffer.IsEmpty () && numMsgs > 0))
@ -653,13 +759,15 @@ namespace stream
m_AckSendTimer.cancel ();
}
bool isEmpty = m_SentPackets.empty ();
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
// auto ts = i2p::util::GetMillisecondsSinceEpoch ();
for (auto& it: packets)
{
it->sendTime = ts;
m_SentPackets.insert (it);
}
SendPackets (packets);
m_LastSendTime = ts;
m_IsSendTime = false;
if (m_Status == eStreamStatusClosing && m_SendBuffer.IsEmpty ())
SendClose ();
if (isEmpty)
@ -670,10 +778,26 @@ namespace stream
void Stream::SendQuickAck ()
{
int32_t lastReceivedSeqn = m_LastReceivedSequenceNumber;
// for limit inbound speed
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
int numPackets = 0;
int64_t passedTime = m_PacketACKInterval * INITIAL_WINDOW_SIZE; // in microseconds // while m_LastACKSendTime == 0
if (m_LastACKSendTime)
passedTime = (ts - m_LastACKSendTime)*1000; // in microseconds
numPackets = (passedTime + m_PacketACKIntervalRem) / m_PacketACKInterval;
m_PacketACKIntervalRem = (passedTime + m_PacketACKIntervalRem) - (numPackets * m_PacketACKInterval);
if (m_LastConfirmedReceivedSequenceNumber + numPackets < m_LastReceivedSequenceNumber)
lastReceivedSeqn = m_LastConfirmedReceivedSequenceNumber + numPackets;
if (numPackets == 0) return;
// for limit inbound speed
if (!m_SavedPackets.empty ())
{
int32_t seqn = (*m_SavedPackets.rbegin ())->GetSeqn ();
if (seqn > lastReceivedSeqn) lastReceivedSeqn = seqn;
for (auto it: m_SavedPackets)
{
auto seqn = it->GetSeqn ();
if (m_LastConfirmedReceivedSequenceNumber + numPackets < int(seqn)) break; // for limit inbound speed
if ((int)seqn > lastReceivedSeqn) lastReceivedSeqn = seqn;
}
}
if (lastReceivedSeqn < 0)
{
@ -702,6 +826,11 @@ namespace stream
for (auto it: m_SavedPackets)
{
auto seqn = it->GetSeqn ();
if (m_LastConfirmedReceivedSequenceNumber + numPackets < int(seqn)) // for limit inbound speed
{
htobe32buf (packet + 12, nextSeqn - 1);
break;
}
if (numNacks + (seqn - nextSeqn) >= 256)
{
LogPrint (eLogError, "Streaming: Number of NACKs exceeds 256. seqn=", seqn, " nextSeqn=", nextSeqn);
@ -743,6 +872,8 @@ namespace stream
p.len = size;
SendPackets (std::vector<Packet *> { &p });
m_LastACKSendTime = ts; // for limit inbound speed
m_LastConfirmedReceivedSequenceNumber = lastReceivedSeqn; // for limit inbound speed
LogPrint (eLogDebug, "Streaming: Quick Ack sent. ", (int)numNacks, " NACKs");
}
@ -873,6 +1004,7 @@ namespace stream
m_IsAckSendScheduled = false;
m_AckSendTimer.cancel ();
}
if (!packet->sendTime) packet->sendTime = i2p::util::GetMillisecondsSinceEpoch ();
SendPackets (std::vector<Packet *> { packet });
bool isEmpty = m_SentPackets.empty ();
m_SentPackets.insert (packet);
@ -906,7 +1038,7 @@ namespace stream
m_CurrentOutboundTunnel = routingPath->outboundTunnel;
m_CurrentRemoteLease = routingPath->remoteLease;
m_RTT = routingPath->rtt;
m_RTO = std::max (MIN_RTO, (int)(m_RTT * 1.5)); // TODO: implement it better
m_RTO = std::max (MIN_RTO, (int)(m_RTT * 1.3 + m_Jitter)); // TODO: implement it better
}
}
@ -925,12 +1057,7 @@ namespace stream
freshTunnel = true;
}
else if (!m_CurrentOutboundTunnel->IsEstablished ())
{
auto oldOutboundTunnel = m_CurrentOutboundTunnel;
m_CurrentOutboundTunnel = m_LocalDestination.GetOwner ()->GetTunnelPool ()->GetNewOutboundTunnel (m_CurrentOutboundTunnel);
if (m_CurrentOutboundTunnel && oldOutboundTunnel->GetEndpointIdentHash() != m_CurrentOutboundTunnel->GetEndpointIdentHash())
freshTunnel = true;
}
std::tie(m_CurrentOutboundTunnel, freshTunnel) = m_LocalDestination.GetOwner ()->GetTunnelPool ()->GetNewOutboundTunnel (m_CurrentOutboundTunnel);
if (!m_CurrentOutboundTunnel)
{
LogPrint (eLogError, "Streaming: No outbound tunnels in the pool, sSID=", m_SendStreamID);
@ -940,7 +1067,7 @@ namespace stream
if (freshTunnel)
{
m_RTO = INITIAL_RTO;
m_TunnelsChangeSequenceNumber = m_SequenceNumber; // should be determined more precisely
// m_TunnelsChangeSequenceNumber = m_SequenceNumber; // should be determined more precisely
}
std::vector<i2p::tunnel::TunnelMessageBlock> msgs;
@ -993,6 +1120,68 @@ namespace stream
SendQuickAck ();
}
void Stream::ScheduleSend ()
{
if (m_Status != eStreamStatusTerminated)
{
m_SendTimer.cancel ();
m_SendTimer.expires_from_now (boost::posix_time::microseconds(SEND_INTERVAL));
m_SendTimer.async_wait (std::bind (&Stream::HandleSendTimer,
shared_from_this (), std::placeholders::_1));
}
}
void Stream::HandleSendTimer (const boost::system::error_code& ecode)
{
if (ecode != boost::asio::error::operation_aborted)
{
if (m_WindowIncCounter && m_WindowSize < MAX_WINDOW_SIZE)
{
if (m_LastWindowDropSize && (m_LastWindowDropSize > m_WindowSize))
{
m_WindowSize += 2.001-(2/((m_LastWindowDropSize+(1/m_WindowSize))/m_WindowSize)); // some magic here
m_WindowIncCounter --;
}
else
{
m_WindowSize += 1;
m_WindowIncCounter --;
}
if (m_WindowSize > MAX_WINDOW_SIZE) m_WindowSize = MAX_WINDOW_SIZE;
UpdatePacingTime ();
}
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
if (m_LastSendTime && ts*1000 > m_LastSendTime*1000 + m_PacingTime)
{
m_NumPacketsToSend = ((ts*1000 - m_LastSendTime*1000) + m_PacingTimeRem) / m_PacingTime;
m_PacingTimeRem = ((ts*1000 - m_LastSendTime*1000) + m_PacingTimeRem) - (m_NumPacketsToSend * m_PacingTime);
m_IsSendTime = true;
if (m_IsNAcked || m_IsResendNeeded) // resend packets
ResendPacket ();
// delay-based CC
else if (!m_IsWinDropped && int(m_SentPackets.size ()) == m_WindowSize) // we sending packets too fast, early detection
{
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
if (m_LastWindowDropSize)
m_LastWindowDropSize = (m_LastWindowDropSize + m_WindowSize) / 2;
else
m_LastWindowDropSize = m_WindowSize;
m_WindowSize = m_WindowSize / 2; // /2
if (m_WindowSize < MIN_WINDOW_SIZE) m_WindowSize = MIN_WINDOW_SIZE;
m_WindowIncCounter = 0;
m_DropWindowDelayTime = ts + m_SlowRTT;
m_IsFirstACK = true;
m_IsWinDropped = true; // don't drop window twice
UpdatePacingTime ();
}
else if (m_WindowSize > int(m_SentPackets.size ())) // send packets
SendBuffer ();
}
else // pass
ScheduleSend ();
}
}
void Stream::ScheduleResend ()
{
if (m_Status != eStreamStatusTerminated)
@ -1010,6 +1199,19 @@ namespace stream
{
if (ecode != boost::asio::error::operation_aborted)
{
m_IsSendTime = true;
if (m_RTO > INITIAL_RTO) m_RTO = INITIAL_RTO;
m_SendTimer.cancel (); // if no ack's in RTO, disable fast retransmit
m_IsTimeOutResend = true;
m_IsNAcked = false;
m_IsResendNeeded = false;
m_NumPacketsToSend = 1;
ResendPacket (); // send one packet per RTO, waiting for ack
}
}
void Stream::ResendPacket ()
{
// check for resend attempts
if (m_NumResendAttempts >= MAX_NUM_RESEND_ATTEMPTS)
{
@ -1022,31 +1224,74 @@ namespace stream
// collect packets to resend
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
std::vector<Packet *> packets;
for (auto it : m_SentPackets)
if (m_IsNAcked)
{
if (ts >= it->sendTime + m_RTO)
for (auto it : m_NACKedPackets)
{
it->resent = true;
it->sendTime = ts;
packets.push_back (it);
if (ts >= it->sendTime + m_RTO)
{
if (ts < it->sendTime + m_RTO*2)
it->resent = true;
else
it->resent = false;
it->sendTime = ts;
packets.push_back (it);
if ((int)packets.size () >= m_NumPacketsToSend) break;
}
}
}
// select tunnels if necessary and send
if (packets.size () > 0)
else
{
m_NumResendAttempts++;
for (auto it : m_SentPackets)
{
if (ts >= it->sendTime + m_RTO)
{
if (ts < it->sendTime + m_RTO*2)
it->resent = true;
else
it->resent = false;
it->sendTime = ts;
packets.push_back (it);
if ((int)packets.size () >= m_NumPacketsToSend) break;
}
}
}
// select tunnels if necessary and send
if (packets.size () > 0 && m_IsSendTime)
{
if (m_IsNAcked) m_NumResendAttempts = 1;
else if (m_IsTimeOutResend) m_NumResendAttempts++;
if (m_NumResendAttempts == 1 && m_RTO != INITIAL_RTO)
{
// 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;
// loss-based CC
if (!m_IsWinDropped)
{
if (m_LastWindowDropSize)
m_LastWindowDropSize = (m_LastWindowDropSize + m_WindowSize) / 2;
else
m_LastWindowDropSize = m_WindowSize;
m_WindowSize = m_WindowSize / 2; // /2
if (m_WindowSize < MIN_WINDOW_SIZE) m_WindowSize = MIN_WINDOW_SIZE;
m_WindowIncCounter = 0;
m_IsWinDropped = true; // don't drop window twice
m_DropWindowDelayTime = ts + m_SlowRTT;
m_IsFirstACK = true;
UpdatePacingTime ();
}
}
else
else if (m_IsTimeOutResend)
{
m_TunnelsChangeSequenceNumber = m_SequenceNumber;
m_IsTimeOutResend = false;
m_RTO = INITIAL_RTO; // drop RTO to initial upon tunnels pair change
m_WindowSize = INITIAL_WINDOW_SIZE;
m_LastWindowDropSize = 0;
m_WindowIncCounter = 0;
m_IsWinDropped = true;
m_IsFirstRttSample = true;
m_DropWindowDelayTime = 0;
m_IsFirstACK = true;
UpdatePacingTime ();
if (m_RoutingSession) m_RoutingSession->SetSharedRoutingPath (nullptr);
if (m_NumResendAttempts & 1)
{
@ -1063,9 +1308,13 @@ namespace stream
}
}
SendPackets (packets);
m_LastSendTime = ts;
m_IsSendTime = false;
if (m_IsNAcked || m_IsResendNeeded) ScheduleSend ();
}
ScheduleResend ();
}
else
SendBuffer ();
if (!m_IsNAcked && !m_IsResendNeeded) ScheduleResend ();
}
void Stream::ScheduleAck (int timeout)
@ -1187,8 +1436,35 @@ namespace stream
LogPrint (eLogWarning, "Streaming: Remote LeaseSet not found");
m_CurrentRemoteLease = nullptr;
}
// drop window to initial upon RemoteLease change
m_RTO = INITIAL_RTO;
m_WindowSize = INITIAL_WINDOW_SIZE;
m_LastWindowDropSize = 0;
m_WindowIncCounter = 0;
m_IsWinDropped = true;
m_IsFirstRttSample = true;
m_DropWindowDelayTime = 0;
m_IsFirstACK = true;
UpdatePacingTime ();
}
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
}
void Stream::UpdatePacingTime ()
{
m_PacingTime = std::round (m_RTT*1000/m_WindowSize);
if (m_MinPacingTime && m_PacingTime < m_MinPacingTime)
m_PacingTime = m_MinPacingTime;
}
StreamingDestination::StreamingDestination (std::shared_ptr<i2p::client::ClientDestination> owner, uint16_t localPort, bool gzip):
m_Owner (owner), m_LocalPort (localPort), m_Gzip (gzip),
m_PendingIncomingTimer (m_Owner->GetService ())
@ -1269,6 +1545,7 @@ namespace stream
{
// already pending
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
return;
}

View File

@ -52,21 +52,22 @@ namespace stream
const size_t STREAMING_MTU_RATCHETS = 1812;
const size_t MAX_PACKET_SIZE = 4096;
const size_t COMPRESSION_THRESHOLD_SIZE = 66;
const int MAX_NUM_RESEND_ATTEMPTS = 9;
const int WINDOW_SIZE = 6; // in messages
const int MAX_NUM_RESEND_ATTEMPTS = 10;
const int INITIAL_WINDOW_SIZE = 10;
const int MIN_WINDOW_SIZE = 1;
const int MAX_WINDOW_SIZE = 128;
const int WINDOW_SIZE_DROP_FRACTION = 10; // 1/10
const int MAX_WINDOW_SIZE = 1024;
const double RTT_EWMA_ALPHA = 0.125;
const int MIN_RTO = 20; // in milliseconds
const int INITIAL_RTT = 8000; // in milliseconds
const int INITIAL_RTO = 9000; // in milliseconds
const int INITIAL_PACING_TIME = 1000 * INITIAL_RTT / INITIAL_WINDOW_SIZE; // in microseconds
const int MIN_SEND_ACK_TIMEOUT = 2; // in milliseconds
const int SYN_TIMEOUT = 200; // how long we wait for SYN after follow-on, in milliseconds
const size_t MAX_PENDING_INCOMING_BACKLOG = 128;
const size_t MAX_PENDING_INCOMING_BACKLOG = 1024;
const int PENDING_INCOMING_TIMEOUT = 10; // in seconds
const int MAX_RECEIVE_TIMEOUT = 20; // in seconds
const uint16_t DELAY_CHOKING = 60000; // in milliseconds
const uint64_t SEND_INTERVAL = 1000; // in microseconds
struct Packet
{
@ -77,7 +78,7 @@ namespace stream
Packet (): len (0), offset (0), sendTime (0), resent (false) {};
uint8_t * GetBuffer () { return buf + offset; };
size_t GetLength () const { return len - offset; };
size_t GetLength () const { return len > offset ? len - offset : 0; };
uint32_t GetSendStreamID () const { return bufbe32toh (buf); };
uint32_t GetReceiveStreamID () const { return bufbe32toh (buf + 4); };
@ -180,6 +181,7 @@ namespace stream
bool IsEstablished () const { return m_SendStreamID; };
StreamStatus GetStatus () const { return m_Status; };
StreamingDestination& GetLocalDestination () { return m_LocalDestination; };
void ResetRoutingPath ();
void HandleNextPacket (Packet * packet);
void HandlePing (Packet * packet);
@ -230,19 +232,33 @@ namespace stream
template<typename Buffer, typename ReceiveHandler>
void HandleReceiveTimer (const boost::system::error_code& ecode, const Buffer& buffer, ReceiveHandler handler, int remainingTimeout);
void ScheduleSend ();
void HandleSendTimer (const boost::system::error_code& ecode);
void ScheduleResend ();
void HandleResendTimer (const boost::system::error_code& ecode);
void ResendPacket ();
void ScheduleAck (int timeout);
void HandleAckSendTimer (const boost::system::error_code& ecode);
void UpdatePacingTime ();
private:
boost::asio::io_service& m_Service;
uint32_t m_SendStreamID, m_RecvStreamID, m_SequenceNumber;
uint32_t m_TunnelsChangeSequenceNumber;
int32_t m_LastReceivedSequenceNumber;
int32_t m_PreviousReceivedSequenceNumber;
int32_t m_LastConfirmedReceivedSequenceNumber; // for limit inbound speed
StreamStatus m_Status;
bool m_IsAckSendScheduled;
bool m_IsNAcked;
bool m_IsFirstACK;
bool m_IsResendNeeded;
bool m_IsFirstRttSample;
bool m_IsSendTime;
bool m_IsWinDropped;
bool m_IsTimeOutResend;
StreamingDestination& m_LocalDestination;
std::shared_ptr<const i2p::data::IdentityEx> m_RemoteIdentity;
std::shared_ptr<const i2p::crypto::Verifier> m_TransientVerifier; // in case of offline key
@ -253,15 +269,18 @@ namespace stream
std::queue<Packet *> m_ReceiveQueue;
std::set<Packet *, PacketCmp> m_SavedPackets;
std::set<Packet *, PacketCmp> m_SentPackets;
boost::asio::deadline_timer m_ReceiveTimer, m_ResendTimer, m_AckSendTimer;
std::set<Packet *, PacketCmp> m_NACKedPackets;
boost::asio::deadline_timer m_ReceiveTimer, m_SendTimer, m_ResendTimer, m_AckSendTimer;
size_t m_NumSentBytes, m_NumReceivedBytes;
uint16_t m_Port;
SendBufferQueue m_SendBuffer;
double m_RTT;
int m_WindowSize, m_RTO, m_AckDelay;
uint64_t m_LastWindowSizeIncreaseTime;
int m_NumResendAttempts;
double m_RTT, m_SlowRTT;
float m_WindowSize, m_LastWindowDropSize;
int m_WindowIncCounter, m_RTO, m_AckDelay, m_PrevRTTSample, m_PrevRTT, m_Jitter;
uint64_t m_MinPacingTime, m_PacingTime, m_PacingTimeRem, m_DropWindowDelayTime, m_LastSendTime; // microseconds
uint64_t m_LastACKSendTime, m_PacketACKInterval, m_PacketACKIntervalRem; // for limit inbound speed
int m_NumResendAttempts, m_NumPacketsToSend;
size_t m_MTU;
};

View File

@ -255,6 +255,11 @@ namespace util
GetDateString (GetSecondsSinceEpoch (), date);
}
void GetNextDayDate (char * date)
{
GetDateString (GetSecondsSinceEpoch () + 24*60*60, date);
}
void GetDateString (uint64_t timestamp, char * date)
{
using clock = std::chrono::system_clock;

View File

@ -28,7 +28,8 @@ namespace util
uint64_t GetMonotonicMilliseconds ();
uint64_t GetMonotonicSeconds ();
void GetCurrentDate (char * date); // returns date as YYYYMMDD string, 9 bytes
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 AdjustTimeOffset (int64_t offset); // in seconds from current

View File

@ -6,6 +6,7 @@
* See full license text in LICENSE file at top of project tree
*/
#include <boost/algorithm/string.hpp> // for boost::to_lower
#include "Log.h"
#include "Crypto.h"
#include "RouterContext.h"
@ -131,6 +132,17 @@ namespace transport
LogPrint(eLogError, "Transports: Return null DHKeys");
}
void Peer::UpdateParams (std::shared_ptr<const i2p::data::RouterInfo> router)
{
if (router)
{
isHighBandwidth = router->IsHighBandwidth ();
isEligible =(bool)router->GetCompatibleTransports (true) && // reachable
router->GetCongestion () != i2p::data::RouterInfo::eRejectAll && // accepts tunnel
router->IsECIES () && router->GetVersion () >= NETDB_MIN_HIGHBANDWIDTH_VERSION; // not too old
}
}
Transports transports;
Transports::Transports ():
@ -328,7 +340,6 @@ namespace transport
{
if (m_PeerCleanupTimer) m_PeerCleanupTimer->cancel ();
if (m_PeerTestTimer) m_PeerTestTimer->cancel ();
m_Peers.clear ();
if (m_SSU2Server)
{
@ -353,6 +364,7 @@ namespace transport
delete m_Thread;
m_Thread = nullptr;
}
m_Peers.clear ();
}
void Transports::Run ()
@ -457,6 +469,7 @@ namespace transport
return;
}
if(RoutesRestricted() && !IsRestrictedPeer(ident)) return;
std::shared_ptr<Peer> peer;
auto it = m_Peers.find (ident);
if (it == m_Peers.end ())
{
@ -470,10 +483,12 @@ namespace transport
if (r && (r->IsUnreachable () || !r->IsReachableFrom (i2p::context.GetRouterInfo ()))) return; // router found but non-reachable
{
auto ts = i2p::util::GetSecondsSinceEpoch ();
peer = std::make_shared<Peer>(r, ts);
std::unique_lock<std::mutex> l(m_PeersMutex);
it = m_Peers.insert (std::pair<i2p::data::IdentHash, Peer>(ident, {r, ts})).first;
peer = m_Peers.emplace (ident, peer).first->second;
}
connected = ConnectToPeer (ident, it->second);
if (peer)
connected = ConnectToPeer (ident, peer);
}
catch (std::exception& ex)
{
@ -481,11 +496,15 @@ namespace transport
}
if (!connected) return;
}
if (it->second.IsConnected ())
it->second.sessions.front ()->SendI2NPMessages (msgs);
else
peer = it->second;
if (!peer) return;
if (peer->IsConnected ())
peer->sessions.front ()->SendI2NPMessages (msgs);
else
{
auto sz = it->second.delayedMessages.size ();
auto sz = peer->delayedMessages.size ();
if (sz < MAX_NUM_DELAYED_MESSAGES)
{
if (sz < CHECK_PROFILE_NUM_DELAYED_MESSAGES && sz + msgs.size () >= CHECK_PROFILE_NUM_DELAYED_MESSAGES)
@ -494,7 +513,7 @@ namespace transport
{
LogPrint (eLogWarning, "Transports: Router ", ident.ToBase64 (), " is banned. Peer dropped");
std::unique_lock<std::mutex> l(m_PeersMutex);
m_Peers.erase (it);
m_Peers.erase (ident);
return;
}
}
@ -502,30 +521,30 @@ namespace transport
if (sz > MAX_NUM_DELAYED_MESSAGES/2 && it1->onDrop)
it1->Drop (); // drop earlier because we can handle it
else
it->second.delayedMessages.push_back (it1);
peer->delayedMessages.push_back (it1);
}
else
{
LogPrint (eLogWarning, "Transports: Delayed messages queue size to ",
ident.ToBase64 (), " exceeds ", MAX_NUM_DELAYED_MESSAGES);
std::unique_lock<std::mutex> l(m_PeersMutex);
m_Peers.erase (it);
m_Peers.erase (ident);
}
}
}
bool Transports::ConnectToPeer (const i2p::data::IdentHash& ident, Peer& peer)
bool Transports::ConnectToPeer (const i2p::data::IdentHash& ident, std::shared_ptr<Peer> peer)
{
if (!peer.router) // reconnect
peer.SetRouter (netdb.FindRouter (ident)); // try to get new one from netdb
if (peer.router) // we have RI already
if (!peer->router) // reconnect
peer->SetRouter (netdb.FindRouter (ident)); // try to get new one from netdb
if (peer->router) // we have RI already
{
if (peer.priority.empty ())
if (peer->priority.empty ())
SetPriority (peer);
while (peer.numAttempts < (int)peer.priority.size ())
while (peer->numAttempts < (int)peer->priority.size ())
{
auto tr = peer.priority[peer.numAttempts];
peer.numAttempts++;
auto tr = peer->priority[peer->numAttempts];
peer->numAttempts++;
switch (tr)
{
case i2p::data::RouterInfo::eNTCP2V4:
@ -533,12 +552,12 @@ namespace transport
{
if (!m_NTCP2Server) continue;
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))
address = nullptr;
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())
m_NTCP2Server->ConnectWithProxy(s);
else
@ -552,12 +571,12 @@ namespace transport
{
if (!m_SSU2Server) continue;
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))
address = nullptr;
if (address && address->IsReachableSSU ())
{
if (m_SSU2Server->CreateSession (peer.router, address))
if (m_SSU2Server->CreateSession (peer->router, address))
return true;
}
break;
@ -565,10 +584,10 @@ namespace transport
case i2p::data::RouterInfo::eNTCP2V6Mesh:
{
if (!m_NTCP2Server) continue;
auto address = peer.router->GetYggdrasilAddress ();
auto address = peer->router->GetYggdrasilAddress ();
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);
return true;
}
@ -580,9 +599,9 @@ namespace transport
}
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
peer.Done ();
peer->Done ();
std::unique_lock<std::mutex> l(m_PeersMutex);
m_Peers.erase (ident);
return false;
@ -590,7 +609,7 @@ namespace transport
else if (i2p::data::IsRouterBanned (ident))
{
LogPrint (eLogWarning, "Transports: Router ", ident.ToBase64 (), " is banned. Peer dropped");
peer.Done ();
peer->Done ();
std::unique_lock<std::mutex> l(m_PeersMutex);
m_Peers.erase (ident);
return false;
@ -604,7 +623,7 @@ namespace transport
return true;
}
void Transports::SetPriority (Peer& peer) const
void Transports::SetPriority (std::shared_ptr<Peer> peer) const
{
static const std::vector<i2p::data::RouterInfo::SupportedTransports>
ntcp2Priority =
@ -623,16 +642,36 @@ namespace transport
i2p::data::RouterInfo::eNTCP2V4,
i2p::data::RouterInfo::eNTCP2V6Mesh
};
if (!peer.router) return;
if (!peer || !peer->router) return;
auto compatibleTransports = context.GetRouterInfo ().GetCompatibleTransports (false) &
peer.router->GetCompatibleTransports (true);
peer.numAttempts = 0;
peer.priority.clear ();
bool ssu2 = peer.router->GetProfile ()->IsReal () ? (rand () & 1) : false; // try NTCP2 if router is not confirmed real
peer->router->GetCompatibleTransports (true);
auto directTransports = compatibleTransports & peer->router->GetPublishedTransports ();
peer->numAttempts = 0;
peer->priority.clear ();
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;
for (auto transport: priority)
if (transport & compatibleTransports)
peer.priority.push_back (transport);
if (directTransports)
{
// direct connections have higher priority
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)
@ -648,8 +687,8 @@ namespace transport
if (r)
{
LogPrint (eLogDebug, "Transports: RouterInfo for ", ident.ToBase64 (), " found, trying to connect");
it->second.SetRouter (r);
if (!it->second.IsConnected ())
it->second->SetRouter (r);
if (!it->second->IsConnected ())
ConnectToPeer (ident, it->second);
}
else
@ -681,7 +720,7 @@ namespace transport
if (ipv4 && i2p::context.SupportsV4 ())
{
LogPrint (eLogInfo, "Transports: Started peer test IPv4");
std::set<i2p::data::IdentHash> excluded;
std::unordered_set<i2p::data::IdentHash> excluded;
excluded.insert (i2p::context.GetIdentHash ()); // don't pick own router
int testDelay = 0;
for (int i = 0; i < 5; i++)
@ -719,7 +758,7 @@ namespace transport
if (ipv6 && i2p::context.SupportsV6 ())
{
LogPrint (eLogInfo, "Transports: Started peer test IPv6");
std::set<i2p::data::IdentHash> excluded;
std::unordered_set<i2p::data::IdentHash> excluded;
excluded.insert (i2p::context.GetIdentHash ()); // don't pick own router
int testDelay = 0;
for (int i = 0; i < 5; i++)
@ -776,31 +815,32 @@ namespace transport
auto it = m_Peers.find (ident);
if (it != m_Peers.end ())
{
if (it->second.numAttempts > 1)
auto peer = it->second;
if (peer->numAttempts > 1)
{
// exclude failed transports
i2p::data::RouterInfo::CompatibleTransports transports = 0;
int numExcluded = it->second.numAttempts - 1;
if (numExcluded > (int)it->second.priority.size ()) numExcluded = it->second.priority.size ();
int numExcluded = peer->numAttempts - 1;
if (numExcluded > (int)peer->priority.size ()) numExcluded = peer->priority.size ();
for (int i = 0; i < numExcluded; i++)
transports |= it->second.priority[i];
transports |= peer->priority[i];
i2p::data::netdb.ExcludeReachableTransports (ident, transports);
}
if (it->second.router && it->second.numAttempts)
if (peer->router && peer->numAttempts)
{
auto transport = it->second.priority[it->second.numAttempts-1];
auto transport = peer->priority[peer->numAttempts-1];
if (transport == i2p::data::RouterInfo::eNTCP2V4 ||
transport == i2p::data::RouterInfo::eNTCP2V6 || transport == i2p::data::RouterInfo::eNTCP2V6Mesh)
it->second.router->GetProfile ()->Connected (); // outgoing NTCP2 connection if always real
peer->router->GetProfile ()->Connected (); // outgoing NTCP2 connection if always real
i2p::data::netdb.SetUnreachable (ident, false); // clear unreachable
}
it->second.numAttempts = 0;
it->second.router = nullptr; // we don't need RouterInfo after successive connect
peer->numAttempts = 0;
peer->router = nullptr; // we don't need RouterInfo after successive connect
bool sendDatabaseStore = true;
if (it->second.delayedMessages.size () > 0)
if (it->second->delayedMessages.size () > 0)
{
// check if first message is our DatabaseStore (publishing)
auto firstMsg = it->second.delayedMessages[0];
auto firstMsg = peer->delayedMessages[0];
if (firstMsg && firstMsg->GetTypeID () == eI2NPDatabaseStore &&
i2p::data::IdentHash(firstMsg->GetPayload () + DATABASE_STORE_KEY_OFFSET) == i2p::context.GetIdentHash ())
sendDatabaseStore = false; // we have it in the list already
@ -809,9 +849,9 @@ namespace transport
session->SendLocalRouterInfo ();
else
session->SetTerminationTimeout (10); // most likely it's publishing, no follow-up messages expected, set timeout to 10 seconds
it->second.sessions.push_back (session);
session->SendI2NPMessages (it->second.delayedMessages);
it->second.delayedMessages.clear ();
peer->sessions.push_back (session);
session->SendI2NPMessages (peer->delayedMessages);
peer->delayedMessages.clear ();
}
else // incoming connection or peer test
{
@ -826,10 +866,11 @@ namespace transport
auto r = i2p::data::netdb.FindRouter (ident); // router should be in netdb after SessionConfirmed
if (r) r->GetProfile ()->Connected ();
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);
auto it = m_Peers.insert (std::make_pair (ident, Peer{ r, ts })).first;
it->second.sessions.push_back (session);
it->second.router = nullptr;
m_Peers.emplace (ident, peer);
}
});
}
@ -844,15 +885,16 @@ namespace transport
auto it = m_Peers.find (ident);
if (it != m_Peers.end ())
{
bool wasConnected = it->second.IsConnected ();
it->second.sessions.remove (session);
if (!it->second.IsConnected ())
auto peer = it->second;
bool wasConnected = peer->IsConnected ();
peer->sessions.remove (session);
if (!peer->IsConnected ())
{
if (it->second.delayedMessages.size () > 0)
if (peer->delayedMessages.size () > 0)
{
if (wasConnected) // we had an active session before
it->second.numAttempts = 0; // start over
ConnectToPeer (ident, it->second);
peer->numAttempts = 0; // start over
ConnectToPeer (ident, peer);
}
else
{
@ -878,12 +920,12 @@ namespace transport
auto ts = i2p::util::GetSecondsSinceEpoch ();
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
{
return !session || !session->IsEstablished ();
});
if (!it->second.IsConnected () && ts > it->second.creationTime + SESSION_CREATION_TIMEOUT)
if (!it->second->IsConnected () && ts > it->second->creationTime + SESSION_CREATION_TIMEOUT)
{
LogPrint (eLogWarning, "Transports: Session to peer ", it->first.ToBase64 (), " has not been created in ", SESSION_CREATION_TIMEOUT, " seconds");
/* if (!it->second.router)
@ -897,12 +939,12 @@ namespace transport
}
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)
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;
}
++it;
@ -936,6 +978,7 @@ namespace transport
std::shared_ptr<const i2p::data::RouterInfo> Transports::GetRandomPeer (Filter filter) const
{
if (m_Peers.empty()) return nullptr;
auto ts = i2p::util::GetSecondsSinceEpoch ();
bool found = false;
i2p::data::IdentHash ident;
{
@ -976,9 +1019,11 @@ namespace transport
it = it1;
while (it != it2 && it != m_Peers.end ())
{
if (filter (it->second))
if (ts > it->second->lastSelectionTime + PEER_SELECTION_MIN_INTERVAL &&
filter (it->second))
{
ident = it->first;
it->second->lastSelectionTime = ts;
found = true;
break;
}
@ -990,9 +1035,11 @@ namespace transport
it = m_Peers.begin ();
while (it != it1 && it != m_Peers.end ())
{
if (filter (it->second))
if (ts > it->second->lastSelectionTime + PEER_SELECTION_MIN_INTERVAL &&
filter (it->second))
{
ident = it->first;
it->second->lastSelectionTime = ts;
found = true;
break;
}
@ -1004,9 +1051,11 @@ namespace transport
it = it2;
while (it != m_Peers.end ())
{
if (filter (it->second))
if (ts > it->second->lastSelectionTime + PEER_SELECTION_MIN_INTERVAL &&
filter (it->second))
{
ident = it->first;
it->second->lastSelectionTime = ts;
found = true;
break;
}
@ -1022,13 +1071,13 @@ namespace transport
std::shared_ptr<const i2p::data::RouterInfo> Transports::GetRandomPeer (bool isHighBandwidth) const
{
return GetRandomPeer (
[isHighBandwidth](const Peer& peer)->bool
[isHighBandwidth](std::shared_ptr<const Peer> peer)->bool
{
// connected, not overloaded and not slow
return !peer.router && peer.IsConnected () && peer.isReachable &&
peer.sessions.front ()->GetSendQueueSize () <= PEER_ROUTER_INFO_OVERLOAD_QUEUE_SIZE &&
!peer.sessions.front ()->IsSlow () && !peer.sessions.front ()->IsBandwidthExceeded (peer.isHighBandwidth) &&
(!isHighBandwidth || peer.isHighBandwidth);
return !peer->router && peer->IsConnected () && peer->isEligible &&
peer->sessions.front ()->GetSendQueueSize () <= PEER_ROUTER_INFO_OVERLOAD_QUEUE_SIZE &&
!peer->sessions.front ()->IsSlow () && !peer->sessions.front ()->IsBandwidthExceeded (peer->isHighBandwidth) &&
(!isHighBandwidth || peer->isHighBandwidth);
});
}

View File

@ -64,26 +64,23 @@ namespace transport
const int PEER_ROUTER_INFO_UPDATE_INTERVAL = 31*60; // in seconds
const int PEER_ROUTER_INFO_UPDATE_INTERVAL_VARIANCE = 7*60; // in seconds
const size_t PEER_ROUTER_INFO_OVERLOAD_QUEUE_SIZE = 25;
const int PEER_SELECTION_MIN_INTERVAL = 20; // in seconds
struct Peer
{
int numAttempts;
std::shared_ptr<const i2p::data::RouterInfo> router;
std::list<std::shared_ptr<TransportSession> > sessions;
uint64_t creationTime, nextRouterInfoUpdateTime;
uint64_t creationTime, nextRouterInfoUpdateTime, lastSelectionTime;
std::vector<std::shared_ptr<i2p::I2NPMessage> > delayedMessages;
std::vector<i2p::data::RouterInfo::SupportedTransports> priority;
bool isHighBandwidth, isReachable;
bool isHighBandwidth, isEligible;
Peer (std::shared_ptr<const i2p::data::RouterInfo> r, uint64_t ts):
numAttempts (0), router (r), creationTime (ts),
nextRouterInfoUpdateTime (ts + PEER_ROUTER_INFO_UPDATE_INTERVAL),
isHighBandwidth (false), isReachable (false)
lastSelectionTime (0), isHighBandwidth (false), isEligible (false)
{
if (router)
{
isHighBandwidth = router->IsHighBandwidth ();
isReachable = (bool)router->GetCompatibleTransports (true);
}
UpdateParams (router);
}
void Done ()
@ -98,14 +95,11 @@ namespace transport
void SetRouter (std::shared_ptr<const i2p::data::RouterInfo> r)
{
router = r;
if (router)
{
isHighBandwidth = router->IsHighBandwidth ();
isReachable = (bool)router->GetCompatibleTransports (true);
}
UpdateParams (router);
}
bool IsConnected () const { return !sessions.empty (); }
bool IsConnected () const { return !sessions.empty (); }
void UpdateParams (std::shared_ptr<const i2p::data::RouterInfo> router);
};
const uint64_t SESSION_CREATION_TIMEOUT = 15; // in seconds
@ -134,6 +128,7 @@ namespace transport
void Start (bool enableNTCP2=true, bool enableSSU2=true);
void Stop ();
bool IsRunning () const { return m_IsRunning; }
bool IsBoundSSU2() const { return m_SSU2Server != nullptr; }
bool IsBoundNTCP2() const { return m_NTCP2Server != nullptr; }
@ -191,8 +186,8 @@ namespace transport
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 PostMessages (i2p::data::IdentHash ident, std::vector<std::shared_ptr<i2p::I2NPMessage> > msgs);
bool ConnectToPeer (const i2p::data::IdentHash& ident, Peer& peer);
void SetPriority (Peer& peer) const;
bool ConnectToPeer (const i2p::data::IdentHash& ident, std::shared_ptr<Peer> peer);
void SetPriority (std::shared_ptr<Peer> peer) const;
void HandlePeerCleanupTimer (const boost::system::error_code& ecode);
void HandlePeerTestTimer (const boost::system::error_code& ecode);
void HandleUpdateBandwidthTimer (const boost::system::error_code& ecode);
@ -215,7 +210,7 @@ namespace transport
SSU2Server * m_SSU2Server;
NTCP2Server * m_NTCP2Server;
mutable std::mutex m_PeersMutex;
std::unordered_map<i2p::data::IdentHash, Peer> m_Peers;
std::unordered_map<i2p::data::IdentHash, std::shared_ptr<Peer> > m_Peers;
X25519KeysPairSupplier m_X25519KeysPairSupplier;

View File

@ -592,17 +592,6 @@ namespace tunnel
auto typeID = msg->GetTypeID ();
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);
}

View File

@ -45,7 +45,8 @@ namespace tunnel
m_NumInboundHops (numInboundHops), m_NumOutboundHops (numOutboundHops),
m_NumInboundTunnels (numInboundTunnels), m_NumOutboundTunnels (numOutboundTunnels),
m_InboundVariance (inboundVariance), m_OutboundVariance (outboundVariance),
m_IsActive (true), m_CustomPeerSelector(nullptr), m_Rng(m_Rd())
m_IsActive (true), m_CustomPeerSelector(nullptr),
m_Rng(i2p::util::GetMonotonicMicroseconds ()%1000000LL)
{
if (m_NumInboundTunnels > TUNNEL_POOL_MAX_INBOUND_TUNNELS_QUANTITY)
m_NumInboundTunnels = TUNNEL_POOL_MAX_INBOUND_TUNNELS_QUANTITY;
@ -59,7 +60,7 @@ namespace tunnel
m_InboundVariance = (m_NumInboundHops < STANDARD_NUM_RECORDS) ? STANDARD_NUM_RECORDS - m_NumInboundHops : 0;
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_NextManageTime = i2p::util::GetSecondsSinceEpoch () + rand () % TUNNEL_POOL_MANAGE_INTERVAL;
m_NextManageTime = i2p::util::GetSecondsSinceEpoch () + m_Rng () % TUNNEL_POOL_MANAGE_INTERVAL;
}
TunnelPool::~TunnelPool ()
@ -210,14 +211,14 @@ namespace tunnel
}
std::shared_ptr<OutboundTunnel> TunnelPool::GetNextOutboundTunnel (std::shared_ptr<OutboundTunnel> excluded,
i2p::data::RouterInfo::CompatibleTransports compatible) const
i2p::data::RouterInfo::CompatibleTransports compatible)
{
std::unique_lock<std::mutex> l(m_OutboundTunnelsMutex);
return GetNextTunnel (m_OutboundTunnels, excluded, compatible);
}
std::shared_ptr<InboundTunnel> TunnelPool::GetNextInboundTunnel (std::shared_ptr<InboundTunnel> excluded,
i2p::data::RouterInfo::CompatibleTransports compatible) const
i2p::data::RouterInfo::CompatibleTransports compatible)
{
std::unique_lock<std::mutex> l(m_InboundTunnelsMutex);
return GetNextTunnel (m_InboundTunnels, excluded, compatible);
@ -225,10 +226,10 @@ namespace tunnel
template<class TTunnels>
typename TTunnels::value_type TunnelPool::GetNextTunnel (TTunnels& tunnels,
typename TTunnels::value_type excluded, i2p::data::RouterInfo::CompatibleTransports compatible) const
typename TTunnels::value_type excluded, i2p::data::RouterInfo::CompatibleTransports compatible)
{
if (tunnels.empty ()) return nullptr;
uint32_t ind = rand () % (tunnels.size ()/2 + 1), i = 0;
uint32_t ind = m_Rng () % (tunnels.size ()/2 + 1), i = 0;
bool skipped = false;
typename TTunnels::value_type tunnel = nullptr;
for (const auto& it: tunnels)
@ -248,7 +249,7 @@ namespace tunnel
}
if (!tunnel && skipped)
{
ind = rand () % (tunnels.size ()/2 + 1), i = 0;
ind = m_Rng () % (tunnels.size ()/2 + 1), i = 0;
for (const auto& it: tunnels)
{
if (it->IsEstablished () && it != excluded)
@ -263,10 +264,11 @@ namespace tunnel
return tunnel;
}
std::shared_ptr<OutboundTunnel> TunnelPool::GetNewOutboundTunnel (std::shared_ptr<OutboundTunnel> old) const
std::pair<std::shared_ptr<OutboundTunnel>, bool> TunnelPool::GetNewOutboundTunnel (std::shared_ptr<OutboundTunnel> old)
{
if (old && old->IsEstablished ()) return old;
if (old && old->IsEstablished ()) return std::make_pair(old, false);
std::shared_ptr<OutboundTunnel> tunnel;
bool freshTunnel = false;
if (old)
{
std::unique_lock<std::mutex> l(m_OutboundTunnelsMutex);
@ -279,8 +281,11 @@ namespace tunnel
}
if (!tunnel)
{
tunnel = GetNextOutboundTunnel ();
return tunnel;
freshTunnel = true;
}
return std::make_pair(tunnel, freshTunnel);
}
void TunnelPool::CreateTunnels ()
@ -360,11 +365,19 @@ namespace tunnel
{
it.second.second->SetState (eTunnelStateFailed);
{
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);
else
it.second.second->SetState (eTunnelStateTestFailed);
bool failed = false;
{
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)
m_LocalDestination->SetLeaseSetUpdated ();
@ -451,7 +464,7 @@ namespace tunnel
{
CreateTunnels ();
TestTunnels ();
m_NextManageTime = ts + TUNNEL_POOL_MANAGE_INTERVAL + (rand () % TUNNEL_POOL_MANAGE_INTERVAL)/2;
m_NextManageTime = ts + TUNNEL_POOL_MANAGE_INTERVAL + (m_Rng () % TUNNEL_POOL_MANAGE_INTERVAL)/2;
}
}
@ -614,7 +627,7 @@ namespace tunnel
numHops = m_NumInboundHops;
if (m_InboundVariance)
{
int offset = rand () % (std::abs (m_InboundVariance) + 1);
int offset = m_Rng () % (std::abs (m_InboundVariance) + 1);
if (m_InboundVariance < 0) offset = -offset;
numHops += offset;
}
@ -624,7 +637,7 @@ namespace tunnel
numHops = m_NumOutboundHops;
if (m_OutboundVariance)
{
int offset = rand () % (std::abs (m_OutboundVariance) + 1);
int offset = m_Rng () % (std::abs (m_OutboundVariance) + 1);
if (m_OutboundVariance < 0) offset = -offset;
numHops += offset;
}

View File

@ -78,10 +78,10 @@ namespace tunnel
void RecreateOutboundTunnel (std::shared_ptr<OutboundTunnel> tunnel);
std::vector<std::shared_ptr<InboundTunnel> > GetInboundTunnels (int num) const;
std::shared_ptr<OutboundTunnel> GetNextOutboundTunnel (std::shared_ptr<OutboundTunnel> excluded = nullptr,
i2p::data::RouterInfo::CompatibleTransports compatible = i2p::data::RouterInfo::eAllTransports) const;
i2p::data::RouterInfo::CompatibleTransports compatible = i2p::data::RouterInfo::eAllTransports);
std::shared_ptr<InboundTunnel> GetNextInboundTunnel (std::shared_ptr<InboundTunnel> excluded = nullptr,
i2p::data::RouterInfo::CompatibleTransports compatible = i2p::data::RouterInfo::eAllTransports) const;
std::shared_ptr<OutboundTunnel> GetNewOutboundTunnel (std::shared_ptr<OutboundTunnel> old) const;
i2p::data::RouterInfo::CompatibleTransports compatible = i2p::data::RouterInfo::eAllTransports);
std::pair<std::shared_ptr<OutboundTunnel>, bool> GetNewOutboundTunnel (std::shared_ptr<OutboundTunnel> old);
void ManageTunnels (uint64_t ts);
void ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg);
void ProcessDeliveryStatus (std::shared_ptr<I2NPMessage> msg);
@ -129,7 +129,7 @@ namespace tunnel
void CreatePairedInboundTunnel (std::shared_ptr<OutboundTunnel> outboundTunnel);
template<class TTunnels>
typename TTunnels::value_type GetNextTunnel (TTunnels& tunnels,
typename TTunnels::value_type excluded, i2p::data::RouterInfo::CompatibleTransports compatible) const;
typename TTunnels::value_type excluded, i2p::data::RouterInfo::CompatibleTransports compatible);
bool SelectPeers (Path& path, bool isInbound);
bool SelectExplicitPeers (Path& path, bool isInbound);
bool ValidatePeers (std::vector<std::shared_ptr<const i2p::data::IdentityEx> >& peers) const;
@ -154,7 +154,6 @@ namespace tunnel
int 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
std::random_device m_Rd;
std::mt19937 m_Rng;
public:

View File

@ -18,8 +18,8 @@
#define MAKE_VERSION_NUMBER(a,b,c) ((a*100+b)*100+c)
#define I2PD_VERSION_MAJOR 2
#define I2PD_VERSION_MINOR 51
#define I2PD_VERSION_MICRO 0
#define I2PD_VERSION_MINOR 53
#define I2PD_VERSION_MICRO 1
#define I2PD_VERSION_PATCH 0
#ifdef GITVER
#define I2PD_VERSION XSTRINGIZE(GITVER)
@ -33,7 +33,7 @@
#define I2P_VERSION_MAJOR 0
#define I2P_VERSION_MINOR 9
#define I2P_VERSION_MICRO 62
#define I2P_VERSION_MICRO 63
#define I2P_VERSION_PATCH 0
#define I2P_VERSION MAKE_VERSION(I2P_VERSION_MAJOR, I2P_VERSION_MINOR, I2P_VERSION_MICRO)
#define I2P_VERSION_NUMBER MAKE_VERSION_NUMBER(I2P_VERSION_MAJOR, I2P_VERSION_MINOR, I2P_VERSION_MICRO)

View File

@ -471,6 +471,8 @@ namespace client
options[I2CP_PARAM_MIN_TUNNEL_LATENCY] = GetI2CPOption(section, I2CP_PARAM_MIN_TUNNEL_LATENCY, DEFAULT_MIN_TUNNEL_LATENCY);
options[I2CP_PARAM_MAX_TUNNEL_LATENCY] = GetI2CPOption(section, I2CP_PARAM_MAX_TUNNEL_LATENCY, DEFAULT_MAX_TUNNEL_LATENCY);
options[I2CP_PARAM_STREAMING_INITIAL_ACK_DELAY] = GetI2CPOption(section, I2CP_PARAM_STREAMING_INITIAL_ACK_DELAY, DEFAULT_INITIAL_ACK_DELAY);
options[I2CP_PARAM_STREAMING_MAX_OUTBOUND_SPEED] = GetI2CPOption(section, I2CP_PARAM_STREAMING_MAX_OUTBOUND_SPEED, DEFAULT_MAX_OUTBOUND_SPEED);
options[I2CP_PARAM_STREAMING_MAX_INBOUND_SPEED] = GetI2CPOption(section, I2CP_PARAM_STREAMING_MAX_INBOUND_SPEED, DEFAULT_MAX_INBOUND_SPEED);
options[I2CP_PARAM_STREAMING_ANSWER_PINGS] = GetI2CPOption(section, I2CP_PARAM_STREAMING_ANSWER_PINGS, isServer ? DEFAULT_ANSWER_PINGS : false);
options[I2CP_PARAM_LEASESET_TYPE] = GetI2CPOption(section, I2CP_PARAM_LEASESET_TYPE, DEFAULT_LEASESET_TYPE);
std::string encType = GetI2CPStringOption(section, I2CP_PARAM_LEASESET_ENCRYPTION_TYPE, "0,4");

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2013-2023, The PurpleI2P Project
* Copyright (c) 2013-2024, The PurpleI2P Project
*
* This file is part of Purple i2pd project and licensed under BSD3
*
@ -16,6 +16,7 @@
#include "ClientContext.h"
#include "Transports.h"
#include "Signature.h"
#include "Config.h"
#include "I2CP.h"
namespace i2p
@ -24,18 +25,19 @@ namespace client
{
I2CPDestination::I2CPDestination (boost::asio::io_service& service, std::shared_ptr<I2CPSession> owner,
std::shared_ptr<const i2p::data::IdentityEx> identity, bool isPublic, const std::map<std::string, std::string>& params):
std::shared_ptr<const i2p::data::IdentityEx> identity, bool isPublic, bool isSameThread,
const std::map<std::string, std::string>& params):
LeaseSetDestination (service, isPublic, &params),
m_Owner (owner), m_Identity (identity), m_EncryptionKeyType (m_Identity->GetCryptoKeyType ()),
m_IsCreatingLeaseSet (false), m_LeaseSetCreationTimer (service)
m_IsCreatingLeaseSet (false), m_IsSameThread (isSameThread), m_LeaseSetCreationTimer (service)
{
}
void I2CPDestination::Stop ()
{
m_LeaseSetCreationTimer.cancel ();
LeaseSetDestination::Stop ();
m_Owner = nullptr;
m_LeaseSetCreationTimer.cancel ();
}
void I2CPDestination::SetEncryptionPrivateKey (const uint8_t * key)
@ -100,15 +102,15 @@ namespace client
i2p::data::LocalLeaseSet ls (m_Identity, priv, tunnels); // we don't care about encryption key, we need leases only
m_LeaseSetExpirationTime = ls.GetExpirationTime ();
uint8_t * leases = ls.GetLeases ();
leases[-1] = tunnels.size ();
if (m_Owner)
int numLeases = leases[-1];
if (m_Owner && numLeases)
{
uint16_t sessionID = m_Owner->GetSessionID ();
if (sessionID != 0xFFFF)
{
m_IsCreatingLeaseSet = true;
htobe16buf (leases - 3, sessionID);
size_t l = 2/*sessionID*/ + 1/*num leases*/ + i2p::data::LEASE_SIZE*tunnels.size ();
size_t l = 2/*sessionID*/ + 1/*num leases*/ + i2p::data::LEASE_SIZE*numLeases;
m_Owner->SendI2CPMessage (I2CP_REQUEST_VARIABLE_LEASESET_MESSAGE, leases - 3, l);
m_LeaseSetCreationTimer.expires_from_now (boost::posix_time::seconds (I2CP_LEASESET_CREATION_TIMEOUT));
auto s = GetSharedFromThis ();
@ -122,6 +124,8 @@ namespace client
});
}
}
else
LogPrint (eLogError, "I2CP: Can't request LeaseSet");
}
void I2CPDestination::LeaseSetCreated (const uint8_t * buf, size_t len)
@ -152,20 +156,32 @@ namespace client
memcpy (buf + 4, payload, len);
msg->len += len + 4;
msg->FillI2NPMessageHeader (eI2NPData);
auto s = GetSharedFromThis ();
auto remote = FindLeaseSet (ident);
if (remote)
{
GetService ().post (
[s, msg, remote, nonce]()
{
bool sent = s->SendMsg (msg, remote);
if (s->m_Owner)
s->m_Owner->SendMessageStatusMessage (nonce, sent ? eI2CPMessageStatusGuaranteedSuccess : eI2CPMessageStatusGuaranteedFailure);
});
if (m_IsSameThread)
{
// send right a way
bool sent = SendMsg (msg, remote);
if (m_Owner)
m_Owner->SendMessageStatusMessage (nonce, sent ? eI2CPMessageStatusGuaranteedSuccess : eI2CPMessageStatusGuaranteedFailure);
}
else
{
// send in destination's thread
auto s = GetSharedFromThis ();
GetService ().post (
[s, msg, remote, nonce]()
{
bool sent = s->SendMsg (msg, remote);
if (s->m_Owner)
s->m_Owner->SendMessageStatusMessage (nonce, sent ? eI2CPMessageStatusGuaranteedSuccess : eI2CPMessageStatusGuaranteedFailure);
});
}
}
else
{
auto s = GetSharedFromThis ();
RequestDestination (ident,
[s, msg, nonce](std::shared_ptr<i2p::data::LeaseSet> ls)
{
@ -189,6 +205,7 @@ namespace client
LogPrint (eLogError, "I2CP: Failed to create remote session");
return false;
}
auto garlic = remoteSession->WrapSingleMessage (msg); // shared routing path mitgh be dropped here
auto path = remoteSession->GetSharedRoutingPath ();
std::shared_ptr<i2p::tunnel::OutboundTunnel> outboundTunnel;
std::shared_ptr<const i2p::data::Lease> remoteLease;
@ -202,7 +219,7 @@ namespace client
else
remoteSession->SetSharedRoutingPath (nullptr);
}
else
if (!outboundTunnel || !remoteLease)
{
auto leases = remote->GetNonExpiredLeases (false); // without threshold
if (leases.empty ())
@ -216,21 +233,28 @@ namespace client
}
if (remoteLease && outboundTunnel)
remoteSession->SetSharedRoutingPath (std::make_shared<i2p::garlic::GarlicRoutingPath> (
i2p::garlic::GarlicRoutingPath{outboundTunnel, remoteLease, 10000, 0, 0})); // 10 secs RTT
i2p::garlic::GarlicRoutingPath{outboundTunnel, remoteLease, 10000, 0})); // 10 secs RTT
else
remoteSession->SetSharedRoutingPath (nullptr);
}
m_Owner->AddRoutingSession (remote->GetIdentity ()->GetStandardIdentity ().signingKey + 96, remoteSession); // last 32 bytes
return SendMsg (garlic, outboundTunnel, remoteLease);
}
bool I2CPDestination::SendMsg (std::shared_ptr<I2NPMessage> garlic,
std::shared_ptr<i2p::tunnel::OutboundTunnel> outboundTunnel, std::shared_ptr<const i2p::data::Lease> remoteLease)
{
if (remoteLease && outboundTunnel)
{
std::vector<i2p::tunnel::TunnelMessageBlock> msgs;
auto garlic = remoteSession->WrapSingleMessage (msg);
msgs.push_back (i2p::tunnel::TunnelMessageBlock
{
i2p::tunnel::eDeliveryTypeTunnel,
remoteLease->tunnelGateway, remoteLease->tunnelID,
garlic
outboundTunnel->SendTunnelDataMsgs (
{
i2p::tunnel::TunnelMessageBlock
{
i2p::tunnel::eDeliveryTypeTunnel,
remoteLease->tunnelGateway, remoteLease->tunnelID,
garlic
}
});
outboundTunnel->SendTunnelDataMsgs (msgs);
return true;
}
else
@ -240,13 +264,55 @@ namespace client
else
LogPrint (eLogWarning, "I2CP: Failed to send message. No outbound tunnels");
return false;
}
}
bool I2CPDestination::SendMsg (const uint8_t * payload, size_t len,
std::shared_ptr<i2p::garlic::GarlicRoutingSession> remoteSession, uint32_t nonce)
{
if (!remoteSession) return false;
auto path = remoteSession->GetSharedRoutingPath ();
if (!path) return false;
// get tunnels
std::shared_ptr<i2p::tunnel::OutboundTunnel> outboundTunnel;
std::shared_ptr<const i2p::data::Lease> remoteLease;
if (!remoteSession->CleanupUnconfirmedTags ()) // no stuck tags
{
outboundTunnel = path->outboundTunnel;
remoteLease = path->remoteLease;
}
else
{
remoteSession->SetSharedRoutingPath (nullptr);
return false;
}
// create Data message
auto msg = m_I2NPMsgsPool.AcquireSharedMt ();
uint8_t * buf = msg->GetPayload ();
htobe32buf (buf, len);
memcpy (buf + 4, payload, len);
msg->len += len + 4;
msg->FillI2NPMessageHeader (eI2NPData);
// wrap in gralic
auto garlic = remoteSession->WrapSingleMessage (msg);
// send
bool sent = SendMsg (garlic, outboundTunnel, remoteLease);
m_Owner->SendMessageStatusMessage (nonce, sent ? eI2CPMessageStatusGuaranteedSuccess : eI2CPMessageStatusGuaranteedFailure);
if (!sent)
remoteSession->SetSharedRoutingPath (nullptr);
return sent;
}
void I2CPDestination::CleanupDestination ()
{
m_I2NPMsgsPool.CleanUpMt ();
if (m_Owner) m_Owner->CleanupRoutingSessions ();
}
RunnableI2CPDestination::RunnableI2CPDestination (std::shared_ptr<I2CPSession> owner,
std::shared_ptr<const i2p::data::IdentityEx> identity, bool isPublic, const std::map<std::string, std::string>& params):
RunnableService ("I2CP"),
I2CPDestination (GetIOService (), owner, identity, isPublic, params)
I2CPDestination (GetIOService (), owner, identity, isPublic, false, params)
{
}
@ -275,8 +341,8 @@ namespace client
}
I2CPSession::I2CPSession (I2CPServer& owner, std::shared_ptr<boost::asio::ip::tcp::socket> socket):
m_Owner (owner), m_Socket (socket), m_SessionID (0xFFFF),
m_MessageID (0), m_IsSendAccepted (true), m_IsSending (false)
m_Owner (owner), m_Socket (socket), m_SessionID (0xFFFF), m_MessageID (0),
m_IsSendAccepted (true), m_IsSending (false)
{
}
@ -287,6 +353,11 @@ namespace client
void I2CPSession::Start ()
{
if (m_Socket)
{
m_Socket->set_option (boost::asio::socket_base::receive_buffer_size (I2CP_MAX_MESSAGE_LENGTH));
m_Socket->set_option (boost::asio::socket_base::send_buffer_size (I2CP_MAX_MESSAGE_LENGTH));
}
ReadProtocolByte ();
}
@ -333,7 +404,27 @@ namespace client
if (m_PayloadLen > 0)
{
if (m_PayloadLen <= I2CP_MAX_MESSAGE_LENGTH)
ReceivePayload ();
{
if (!m_Socket) return;
boost::system::error_code ec;
size_t moreBytes = m_Socket->available(ec);
if (!ec)
{
if (moreBytes >= m_PayloadLen)
{
// read and process payload immediately if available
moreBytes = boost::asio::read (*m_Socket, boost::asio::buffer(m_Payload, m_PayloadLen), boost::asio::transfer_all (), ec);
HandleReceivedPayload (ec, moreBytes);
}
else
ReceivePayload ();
}
else
{
LogPrint (eLogWarning, "I2CP: Socket error: ", ec.message ());
Terminate ();
}
}
else
{
LogPrint (eLogError, "I2CP: Unexpected payload length ", m_PayloadLen);
@ -558,13 +649,13 @@ namespace client
if (!m_Destination)
{
m_Destination = m_Owner.IsSingleThread () ?
std::make_shared<I2CPDestination>(m_Owner.GetService (), shared_from_this (), identity, true, params):
std::make_shared<I2CPDestination>(m_Owner.GetService (), shared_from_this (), identity, true, true, params):
std::make_shared<RunnableI2CPDestination>(shared_from_this (), identity, true, params);
if (m_Owner.InsertSession (shared_from_this ()))
{
SendSessionStatusMessage (eI2CPSessionStatusCreated); // created
LogPrint (eLogDebug, "I2CP: Session ", m_SessionID, " created");
m_Destination->Start ();
SendSessionStatusMessage (eI2CPSessionStatusCreated); // created
}
else
{
@ -584,7 +675,7 @@ namespace client
SendSessionStatusMessage (eI2CPSessionStatusInvalid); // invalid
}
}
void I2CPSession::DestroySessionMessageHandler (const uint8_t * buf, size_t len)
{
SendSessionStatusMessage (eI2CPSessionStatusDestroyed); // destroy
@ -669,6 +760,25 @@ namespace client
SendI2CPMessage (I2CP_MESSAGE_STATUS_MESSAGE, buf, 15);
}
void I2CPSession::AddRoutingSession (const i2p::data::IdentHash& signingKey, std::shared_ptr<i2p::garlic::GarlicRoutingSession> remoteSession)
{
if (!remoteSession) return;
std::lock_guard<std::mutex> l(m_RoutingSessionsMutex);
m_RoutingSessions[signingKey] = remoteSession;
}
void I2CPSession::CleanupRoutingSessions ()
{
std::lock_guard<std::mutex> l(m_RoutingSessionsMutex);
for (auto it = m_RoutingSessions.begin (); it != m_RoutingSessions.end ();)
{
if (it->second->IsTerminated ())
it = m_RoutingSessions.erase (it);
else
it++;
}
}
void I2CPSession::CreateLeaseSetMessageHandler (const uint8_t * buf, size_t len)
{
uint16_t sessionID = bufbe16toh (buf);
@ -739,19 +849,44 @@ namespace client
size_t offset = 2;
if (m_Destination)
{
i2p::data::IdentityEx identity;
size_t identsize = identity.FromBuffer (buf + offset, len - offset);
if (identsize)
const uint8_t * ident = buf + offset;
size_t identSize = i2p::data::GetIdentityBufferLen (ident, len - offset);
if (identSize)
{
offset += identsize;
offset += identSize;
uint32_t payloadLen = bufbe32toh (buf + offset);
if (payloadLen + offset <= len)
{
offset += 4;
uint32_t nonce = bufbe32toh (buf + offset + payloadLen);
if (m_IsSendAccepted)
SendMessageStatusMessage (nonce, eI2CPMessageStatusAccepted); // accepted
m_Destination->SendMsgTo (buf + offset, payloadLen, identity.GetIdentHash (), nonce);
if (m_Destination->IsReady ())
{
if (m_IsSendAccepted)
SendMessageStatusMessage (nonce, eI2CPMessageStatusAccepted); // accepted
std::shared_ptr<i2p::garlic::GarlicRoutingSession> remoteSession;
{
std::lock_guard<std::mutex> l(m_RoutingSessionsMutex);
auto it = m_RoutingSessions.find (ident + i2p::data::DEFAULT_IDENTITY_SIZE - 35); // 32 bytes signing key
if (it != m_RoutingSessions.end ())
{
if (!it->second->IsTerminated ())
remoteSession = it->second;
else
m_RoutingSessions.erase (it);
}
}
if (!remoteSession || !m_Destination->SendMsg (buf + offset, payloadLen, remoteSession, nonce))
{
i2p::data::IdentHash identHash;
SHA256(ident, identSize, identHash); // caclulate ident hash, because we don't need full identity
m_Destination->SendMsgTo (buf + offset, payloadLen, identHash, nonce);
}
}
else
{
LogPrint(eLogInfo, "I2CP: Destination is not ready");
SendMessageStatusMessage (nonce, eI2CPMessageStatusNoLocalTunnels);
}
}
else
LogPrint(eLogError, "I2CP: Cannot send message, too big");
@ -891,8 +1026,12 @@ namespace client
{
uint8_t limits[64];
memset (limits, 0, 64);
htobe32buf (limits, i2p::transport::transports.GetInBandwidth ()); // inbound
htobe32buf (limits + 4, i2p::transport::transports.GetOutBandwidth ()); // outbound
uint32_t limit; i2p::config::GetOption("i2cp.inboundlimit", limit);
if (!limit) limit = i2p::context.GetBandwidthLimit ();
htobe32buf (limits, limit); // inbound
i2p::config::GetOption("i2cp.outboundlimit", limit);
if (!limit) limit = i2p::context.GetBandwidthLimit ();
htobe32buf (limits + 4, limit); // outbound
SendI2CPMessage (I2CP_BANDWIDTH_LIMITS_MESSAGE, limits, 64);
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2013-2023, The PurpleI2P Project
* Copyright (c) 2013-2024, The PurpleI2P Project
*
* This file is part of Purple i2pd project and licensed under BSD3
*
@ -12,8 +12,10 @@
#include <inttypes.h>
#include <string>
#include <memory>
#include <mutex>
#include <thread>
#include <map>
#include <unordered_map>
#include <boost/asio.hpp>
#include "util.h"
#include "Destination.h"
@ -58,6 +60,7 @@ namespace client
eI2CPMessageStatusAccepted = 1,
eI2CPMessageStatusGuaranteedSuccess = 4,
eI2CPMessageStatusGuaranteedFailure = 5,
eI2CPMessageStatusNoLocalTunnels = 16,
eI2CPMessageStatusNoLeaseSet = 21
};
@ -79,7 +82,8 @@ namespace client
public:
I2CPDestination (boost::asio::io_service& service, std::shared_ptr<I2CPSession> owner,
std::shared_ptr<const i2p::data::IdentityEx> identity, bool isPublic, const std::map<std::string, std::string>& params);
std::shared_ptr<const i2p::data::IdentityEx> identity, bool isPublic, bool isSameThread,
const std::map<std::string, std::string>& params);
~I2CPDestination () {};
void Stop ();
@ -90,7 +94,8 @@ namespace client
void LeaseSetCreated (const uint8_t * buf, size_t len); // called from I2CPSession
void LeaseSet2Created (uint8_t storeType, const uint8_t * buf, size_t len); // called from I2CPSession
void SendMsgTo (const uint8_t * payload, size_t len, const i2p::data::IdentHash& ident, uint32_t nonce); // called from I2CPSession
bool SendMsg (const uint8_t * payload, size_t len, std::shared_ptr<i2p::garlic::GarlicRoutingSession> remoteSession, uint32_t nonce);
// implements LocalDestination
bool Decrypt (const uint8_t * encrypted, uint8_t * data, i2p::data::CryptoKeyType preferredCrypto) const;
bool SupportsEncryptionType (i2p::data::CryptoKeyType keyType) const;
@ -99,6 +104,7 @@ namespace client
protected:
void CleanupDestination ();
// I2CP
void HandleDataMessage (const uint8_t * buf, size_t len);
void CreateNewLeaseSet (const std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> >& tunnels);
@ -108,7 +114,9 @@ namespace client
std::shared_ptr<I2CPDestination> GetSharedFromThis ()
{ return std::static_pointer_cast<I2CPDestination>(shared_from_this ()); }
bool SendMsg (std::shared_ptr<I2NPMessage> msg, std::shared_ptr<const i2p::data::LeaseSet> remote);
bool SendMsg (std::shared_ptr<I2NPMessage> garlic,
std::shared_ptr<i2p::tunnel::OutboundTunnel> outboundTunnel, std::shared_ptr<const i2p::data::Lease> remoteLease);
void PostCreateNewLeaseSet (std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> > tunnels);
private:
@ -120,7 +128,7 @@ namespace client
std::shared_ptr<i2p::crypto::ECIESX25519AEADRatchetDecryptor> m_ECIESx25519Decryptor;
uint8_t m_ECIESx25519PrivateKey[32];
uint64_t m_LeaseSetExpirationTime;
bool m_IsCreatingLeaseSet;
bool m_IsCreatingLeaseSet, m_IsSameThread;
boost::asio::deadline_timer m_LeaseSetCreationTimer;
i2p::util::MemoryPoolMt<I2NPMessageBuffer<I2NP_MAX_MESSAGE_SIZE> > m_I2NPMsgsPool;
};
@ -155,6 +163,8 @@ namespace client
void SendI2CPMessage (uint8_t type, const uint8_t * payload, size_t len);
void SendMessagePayloadMessage (const uint8_t * payload, size_t len);
void SendMessageStatusMessage (uint32_t nonce, I2CPMessageStatus status);
void AddRoutingSession (const i2p::data::IdentHash& signingKey, std::shared_ptr<i2p::garlic::GarlicRoutingSession> remoteSession);
void CleanupRoutingSessions ();
// message handlers
void GetDateMessageHandler (const uint8_t * buf, size_t len);
@ -186,7 +196,7 @@ namespace client
void ExtractMapping (const uint8_t * buf, size_t len, std::map<std::string, std::string>& mapping);
void SendSessionStatusMessage (I2CPSessionStatus status);
void SendHostReplyMessage (uint32_t requestID, std::shared_ptr<const i2p::data::IdentityEx> identity);
private:
I2CPServer& m_Owner;
@ -195,6 +205,8 @@ namespace client
size_t m_PayloadLen;
std::shared_ptr<I2CPDestination> m_Destination;
std::mutex m_RoutingSessionsMutex;
std::unordered_map<i2p::data::IdentHash, std::shared_ptr<i2p::garlic::GarlicRoutingSession> > m_RoutingSessions; // signing key->session
uint16_t m_SessionID;
uint32_t m_MessageID;
bool m_IsSendAccepted;
@ -224,8 +236,6 @@ namespace client
private:
void Run ();
void Accept ();
void HandleAccept(const boost::system::error_code& ecode, std::shared_ptr<boost::asio::ip::tcp::socket> socket);