Merge pull request #1696 from PurpleI2P/openssl

recent changes
This commit is contained in:
orignal 2021-10-15 13:02:32 -04:00 committed by GitHub
commit 9685754511
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
108 changed files with 3902 additions and 2408 deletions

63
.github/workflows/docker.yml vendored Normal file
View File

@ -0,0 +1,63 @@
name: Build containers
on: [push]
jobs:
docker:
runs-on: ubuntu-latest
permissions:
packages: write
contents: read
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Set up QEMU
uses: docker/setup-qemu-action@v1
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
- name: Login to DockerHub
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Login to GitHub Container registry
uses: docker/login-action@v1
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push trunk container
if: ${{ !startsWith(github.ref, 'refs/tags/') }}
uses: docker/build-push-action@v2
with:
context: ./contrib/docker
file: ./contrib/docker/Dockerfile
platforms: linux/amd64,linux/386,linux/arm64,linux/arm/v7
push: true
tags: |
purplei2p/i2pd:latest
ghcr.io/purplei2p/i2pd:latest
- name: Set env
if: ${{ startsWith(github.ref, 'refs/tags/') }}
run: echo "RELEASE_VERSION=${GITHUB_REF:10}" >> $GITHUB_ENV
- name: Build and push release container
if: ${{ startsWith(github.ref, 'refs/tags/') }}
uses: docker/build-push-action@v2
with:
context: ./contrib/docker
file: ./contrib/docker/Dockerfile
platforms: linux/amd64,linux/386,linux/arm64,linux/arm/v7
push: true
tags: |
purplei2p/i2pd:latest
purplei2p/i2pd:release-${{ env.RELEASE_VERSION }}
ghcr.io/purplei2p/i2pd:latest
ghcr.io/purplei2p/i2pd:release-${{ env.RELEASE_VERSION }}

5
.gitignore vendored
View File

@ -7,8 +7,13 @@ netDb
/i2pd /i2pd
/libi2pd.a /libi2pd.a
/libi2pdclient.a /libi2pdclient.a
/libi2pdlang.a
/libi2pd.so /libi2pd.so
/libi2pdclient.so /libi2pdclient.so
/libi2pdlang.so
/libi2pd.dll
/libi2pdclient.dll
/libi2pdlang.dll
*.exe *.exe

View File

@ -1,54 +0,0 @@
language: cpp
cache:
apt: true
os:
- linux
#- osx
dist: xenial
sudo: required
compiler:
- g++
- clang++
env:
global:
- MAKEFLAGS="-j 2"
matrix:
- BUILD_TYPE=make UPNP=ON MAKE_UPNP=yes
- BUILD_TYPE=make UPNP=OFF MAKE_UPNP=no
- BUILD_TYPE=cmake UPNP=ON MAKE_UPNP=yes
- BUILD_TYPE=cmake UPNP=OFF MAKE_UPNP=no
matrix:
exclude:
- os: osx
env: BUILD_TYPE=cmake UPNP=ON MAKE_UPNP=yes
- os: osx
env: BUILD_TYPE=cmake UPNP=OFF MAKE_UPNP=no
- os: linux
compiler: clang++
env: BUILD_TYPE=make UPNP=ON MAKE_UPNP=yes
- os: linux
compiler: clang++
env: BUILD_TYPE=make UPNP=OFF MAKE_UPNP=no
addons:
apt:
packages:
- build-essential
- cmake
- g++
- clang
- libboost-chrono-dev
- libboost-date-time-dev
- libboost-filesystem-dev
- libboost-program-options-dev
- libboost-system-dev
- libboost-thread-dev
- libminiupnpc-dev
- libssl-dev
before_install:
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew update ; fi
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew install libressl miniupnpc ; fi
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew outdated boost || brew upgrade boost ; fi
script:
- if [[ "$TRAVIS_OS_NAME" == "linux" && "$BUILD_TYPE" == "cmake" ]]; then cd build && cmake -DCMAKE_BUILD_TYPE=Release -DWITH_UPNP=${UPNP} && make ; fi
- if [[ "$TRAVIS_OS_NAME" == "linux" && "$BUILD_TYPE" == "make" ]]; then make USE_UPNP=${MAKE_UPNP} ; fi
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then make HOMEBREW=1 USE_UPNP=${MAKE_UPNP} ; fi

View File

@ -1,7 +1,49 @@
# for this file format description, # for this file format description,
# see https://github.com/olivierlacan/keep-a-changelog # see https://github.com/olivierlacan/keep-a-changelog
## [2.38.0] - 2021-03-17 ## [2.39.0] - 2021-08-23
### Added
- Short tunnel build messages
- Localization. To: Russian, Ukrainian, Turkmen, Uzbek and Afrikaans
- Custom CSS styles for webconsole
- Avoid slow tunnels with more than 250 ms per hop
- Process DELAY_REQUESTED streaming option
- "certsdir" options for certificates location
- Keep own RouterInfo in NetBb
- Pick ECIES routers only for tunnels on non-x64
- NTP sync through ipv6
- Allow ipv6 addresses for UDP server tunnels
### Changed
- Rekey of all routers to ECIES
- Better distribution for random tunnel's peer selection
- Yggdrasil reseed for v0.4, added two more
- Encryption type 0,4 by default for server tunnels
- Handle i2cp.dontPublishLeaseSet param for all destinations
- reg.i2p for subscriptions
- LeaseSet type 3 by default
- Don't allocate payload buffer for every single ECIESx25519 message
- Prefer public ipv6 instead rfc4941
- Optimal padding for one-time ECIESx25519 message
- Don't send datetime block for one-time ECIESx25519 message with one-time key
- Router with expired introducer is still valid
- Don't disable floodfill if still reachable by ipv6
- Set minimal version for floodfill to 0.9.38
- Eliminate extra lookups for sequential fragments on tunnel endpoint
- Consistent path for explicit peers
- Always create new tunnel from exploratory pool
- Don't try to connect to a router not reachable from us
- Mark additional ipv6 addresses/nets as reserved (#1679)
### Fixed
- Zero-hop tunnels
- Crash upon SAM session termination
- Build with boost < 1.55.0
- Address type for NTCP2 acceptors
- Check of ipv4/ipv6 address
- Request router to send to if not in NetDb
- Count outbound traffic for zero-hop tunnels
- URLdecode domain for registration string generator in webconsole
## [2.38.0] - 2021-05-17
### Added ### Added
- Publish ipv6 introducers - Publish ipv6 introducers
- Bind ipv6 or yggdrasil NTCP2 acceptor to specified address - Bind ipv6 or yggdrasil NTCP2 acceptor to specified address

View File

@ -1,23 +1,37 @@
SYS := $(shell $(CXX) -dumpmachine) SYS := $(shell $(CXX) -dumpmachine)
SHLIB := libi2pd.so
ifneq (, $(findstring darwin, $(SYS)))
SHARED_SUFFIX = dylib
else ifneq (, $(findstring mingw, $(SYS))$(findstring cygwin, $(SYS)))
SHARED_SUFFIX = dll
else
SHARED_SUFFIX = so
endif
SHLIB := libi2pd.$(SHARED_SUFFIX)
ARLIB := libi2pd.a ARLIB := libi2pd.a
SHLIB_CLIENT := libi2pdclient.so SHLIB_LANG := libi2pdlang.$(SHARED_SUFFIX)
ARLIB_LANG := libi2pdlang.a
SHLIB_CLIENT := libi2pdclient.$(SHARED_SUFFIX)
ARLIB_CLIENT := libi2pdclient.a ARLIB_CLIENT := libi2pdclient.a
SHLIB_WRAP := libi2pdwrapper.$(SHARED_SUFFIX)
ARLIB_WRAP := libi2pdwrapper.a
I2PD := i2pd I2PD := i2pd
LIB_SRC_DIR := libi2pd LIB_SRC_DIR := libi2pd
LIB_CLIENT_SRC_DIR := libi2pd_client LIB_CLIENT_SRC_DIR := libi2pd_client
WRAP_SRC_DIR := libi2pd_wrapper
LANG_SRC_DIR := i18n LANG_SRC_DIR := i18n
DAEMON_SRC_DIR := daemon DAEMON_SRC_DIR := daemon
# import source files lists # import source files lists
include filelist.mk include filelist.mk
USE_AESNI := yes USE_AESNI := $(or $(USE_AESNI),yes)
USE_STATIC := no USE_STATIC := $(or $(USE_STATIC),no)
USE_MESHNET := no USE_MESHNET := $(or $(USE_MESHNET),no)
USE_UPNP := no USE_UPNP := $(or $(USE_UPNP),no)
DEBUG := yes DEBUG := $(or $(DEBUG),yes)
ifeq ($(DEBUG),yes) ifeq ($(DEBUG),yes)
CXX_DEBUG = -g CXX_DEBUG = -g
@ -56,21 +70,25 @@ LIB_OBJS += $(patsubst %.cpp,obj/%.o,$(LIB_SRC))
LIB_CLIENT_OBJS += $(patsubst %.cpp,obj/%.o,$(LIB_CLIENT_SRC)) LIB_CLIENT_OBJS += $(patsubst %.cpp,obj/%.o,$(LIB_CLIENT_SRC))
LANG_OBJS += $(patsubst %.cpp,obj/%.o,$(LANG_SRC)) LANG_OBJS += $(patsubst %.cpp,obj/%.o,$(LANG_SRC))
DAEMON_OBJS += $(patsubst %.cpp,obj/%.o,$(DAEMON_SRC)) DAEMON_OBJS += $(patsubst %.cpp,obj/%.o,$(DAEMON_SRC))
DEPS += $(LIB_OBJS:.o=.d) $(LIB_CLIENT_OBJS:.o=.d) $(LANG_OBJS:.o=.d) $(DAEMON_OBJS:.o=.d) WRAP_LIB_OBJS += $(patsubst %.cpp,obj/%.o,$(WRAP_LIB_SRC))
DEPS += $(LIB_OBJS:.o=.d) $(LIB_CLIENT_OBJS:.o=.d) $(LANG_OBJS:.o=.d) $(DAEMON_OBJS:.o=.d) $(WRAP_LIB_OBJS:.o=.d)
all: mk_obj_dir $(ARLIB) $(ARLIB_CLIENT) $(I2PD) ## Build all code (libi2pd, libi2pdclient, libi2pdlang), link it to .a and build binary
all: $(ARLIB) $(ARLIB_CLIENT) $(ARLIB_LANG) $(I2PD)
mk_obj_dir: mk_obj_dir:
@mkdir -p obj
@mkdir -p obj/Win32
@mkdir -p obj/$(LIB_SRC_DIR) @mkdir -p obj/$(LIB_SRC_DIR)
@mkdir -p obj/$(LIB_CLIENT_SRC_DIR) @mkdir -p obj/$(LIB_CLIENT_SRC_DIR)
@mkdir -p obj/$(LANG_SRC_DIR) @mkdir -p obj/$(LANG_SRC_DIR)
@mkdir -p obj/$(DAEMON_SRC_DIR) @mkdir -p obj/$(DAEMON_SRC_DIR)
@mkdir -p obj/$(WRAP_SRC_DIR)
@mkdir -p obj/Win32
api: mk_obj_dir $(SHLIB) $(ARLIB) api: $(SHLIB) $(ARLIB)
client: mk_obj_dir $(SHLIB_CLIENT) $(ARLIB_CLIENT) client: $(SHLIB_CLIENT) $(ARLIB_CLIENT)
api_client: mk_obj_dir $(SHLIB) $(ARLIB) $(SHLIB_CLIENT) $(ARLIB_CLIENT) lang: $(SHLIB_LANG) $(ARLIB_LANG)
api_client: api client lang
wrapper: api_client $(SHLIB_WRAP) $(ARLIB_WRAP)
## NOTE: The NEEDED_CXXFLAGS are here so that CXXFLAGS can be specified at build time ## NOTE: The NEEDED_CXXFLAGS are here so that CXXFLAGS can be specified at build time
## **without** overwriting the CXXFLAGS which we need in order to build. ## **without** overwriting the CXXFLAGS which we need in order to build.
@ -79,23 +97,33 @@ api_client: mk_obj_dir $(SHLIB) $(ARLIB) $(SHLIB_CLIENT) $(ARLIB_CLIENT)
## -std=c++11. If you want to remove this variable please do so in a way that allows setting ## -std=c++11. If you want to remove this variable please do so in a way that allows setting
## custom FLAGS to work at build-time. ## custom FLAGS to work at build-time.
obj/%.o: %.cpp obj/%.o: %.cpp | mk_obj_dir
$(CXX) $(CXXFLAGS) $(NEEDED_CXXFLAGS) $(INCFLAGS) -c -o $@ $< $(CXX) $(CXXFLAGS) $(NEEDED_CXXFLAGS) $(INCFLAGS) -c -o $@ $<
# '-' is 'ignore if missing' on first run # '-' is 'ignore if missing' on first run
-include $(DEPS) -include $(DEPS)
$(I2PD): $(LANG_OBJS) $(DAEMON_OBJS) $(ARLIB) $(ARLIB_CLIENT) $(I2PD): $(DAEMON_OBJS) $(ARLIB) $(ARLIB_CLIENT) $(ARLIB_LANG)
$(CXX) -o $@ $(LDFLAGS) $^ $(LDLIBS) $(CXX) -o $@ $(LDFLAGS) $^ $(LDLIBS)
$(SHLIB): $(LIB_OBJS) $(SHLIB): $(LIB_OBJS) $(SHLIB_LANG)
ifneq ($(USE_STATIC),yes)
$(CXX) $(LDFLAGS) -shared -o $@ $^ $(LDLIBS) $(SHLIB_LANG)
endif
$(SHLIB_CLIENT): $(LIB_CLIENT_OBJS) $(SHLIB) $(SHLIB_LANG)
ifneq ($(USE_STATIC),yes)
$(CXX) $(LDFLAGS) -shared -o $@ $^ $(LDLIBS) $(SHLIB) $(SHLIB_LANG)
endif
$(SHLIB_WRAP): $(WRAP_LIB_OBJS)
ifneq ($(USE_STATIC),yes) ifneq ($(USE_STATIC),yes)
$(CXX) $(LDFLAGS) -shared -o $@ $^ $(LDLIBS) $(CXX) $(LDFLAGS) -shared -o $@ $^ $(LDLIBS)
endif endif
$(SHLIB_CLIENT): $(LIB_CLIENT_OBJS) $(SHLIB_LANG): $(LANG_OBJS)
ifneq ($(USE_STATIC),yes) ifneq ($(USE_STATIC),yes)
$(CXX) $(LDFLAGS) -shared -o $@ $^ $(LDLIBS) $(SHLIB) $(CXX) $(LDFLAGS) -shared -o $@ $^ $(LDLIBS)
endif endif
$(ARLIB): $(LIB_OBJS) $(ARLIB): $(LIB_OBJS)
@ -104,12 +132,18 @@ $(ARLIB): $(LIB_OBJS)
$(ARLIB_CLIENT): $(LIB_CLIENT_OBJS) $(ARLIB_CLIENT): $(LIB_CLIENT_OBJS)
$(AR) -r $@ $^ $(AR) -r $@ $^
$(ARLIB_WRAP): $(WRAP_LIB_OBJS)
$(AR) -r $@ $^
$(ARLIB_LANG): $(LANG_OBJS)
$(AR) -r $@ $^
clean: clean:
$(RM) -r obj $(RM) -r obj
$(RM) -r docs/generated $(RM) -r docs/generated
$(RM) $(I2PD) $(SHLIB) $(ARLIB) $(SHLIB_CLIENT) $(ARLIB_CLIENT) $(RM) $(I2PD) $(SHLIB) $(ARLIB) $(SHLIB_CLIENT) $(ARLIB_CLIENT) $(SHLIB_LANG) $(ARLIB_LANG) $(SHLIB_WRAP) $(ARLIB_WRAP)
strip: $(I2PD) $(SHLIB_CLIENT) $(SHLIB) strip: $(I2PD) $(SHLIB) $(SHLIB_CLIENT) $(SHLIB_LANG)
strip $^ strip $^
LATEST_TAG=$(shell git describe --tags --abbrev=0 openssl) LATEST_TAG=$(shell git describe --tags --abbrev=0 openssl)
@ -133,6 +167,7 @@ doxygen:
.PHONY: api .PHONY: api
.PHONY: api_client .PHONY: api_client
.PHONY: client .PHONY: client
.PHONY: lang
.PHONY: mk_obj_dir .PHONY: mk_obj_dir
.PHONY: install .PHONY: install
.PHONY: strip .PHONY: strip

View File

@ -3,9 +3,9 @@ USE_WIN32_APP := yes
WINDRES = windres WINDRES = windres
CXXFLAGS := $(CXX_DEBUG) -D_MT -DWIN32_LEAN_AND_MEAN -fPIC -msse CXXFLAGS := $(CXX_DEBUG) -DWIN32_LEAN_AND_MEAN -fPIC -msse
INCFLAGS = -I$(DAEMON_SRC_DIR) -IWin32 INCFLAGS = -I$(DAEMON_SRC_DIR) -IWin32
LDFLAGS := ${LD_DEBUG} -Wl,-Bstatic -static-libgcc LDFLAGS := ${LD_DEBUG} -static
# detect proper flag for c++11 support by compilers # detect proper flag for c++11 support by compilers
CXXVER := $(shell $(CXX) -dumpversion) CXXVER := $(shell $(CXX) -dumpversion)
@ -61,5 +61,5 @@ ifeq ($(USE_ASLR),yes)
LDFLAGS += -Wl,--nxcompat -Wl,--high-entropy-va -Wl,--dynamicbase,--export-all-symbols LDFLAGS += -Wl,--nxcompat -Wl,--high-entropy-va -Wl,--dynamicbase,--export-all-symbols
endif endif
obj/%.o : %.rc obj/%.o : %.rc | mk_obj_dir
$(WINDRES) -i $< -o $@ $(WINDRES) -i $< -o $@

View File

@ -3,6 +3,7 @@
[![License](https://img.shields.io/github/license/PurpleI2P/i2pd.svg)](https://github.com/PurpleI2P/i2pd/blob/openssl/LICENSE) [![License](https://img.shields.io/github/license/PurpleI2P/i2pd.svg)](https://github.com/PurpleI2P/i2pd/blob/openssl/LICENSE)
[![Packaging status](https://repology.org/badge/tiny-repos/i2pd.svg)](https://repology.org/project/i2pd/versions) [![Packaging status](https://repology.org/badge/tiny-repos/i2pd.svg)](https://repology.org/project/i2pd/versions)
[![Docker Pulls](https://img.shields.io/docker/pulls/purplei2p/i2pd)](https://hub.docker.com/r/purplei2p/i2pd) [![Docker Pulls](https://img.shields.io/docker/pulls/purplei2p/i2pd)](https://hub.docker.com/r/purplei2p/i2pd)
[![Crowdin](https://badges.crowdin.net/i2pd/localized.svg)](https://crowdin.com/project/i2pd)
*note: i2pd for Android can be found in [i2pd-android](https://github.com/PurpleI2P/i2pd-android) repository and with Qt GUI in [i2pd-qt](https://github.com/PurpleI2P/i2pd-qt) repository* *note: i2pd for Android can be found in [i2pd-android](https://github.com/PurpleI2P/i2pd-android) repository and with Qt GUI in [i2pd-qt](https://github.com/PurpleI2P/i2pd-qt) repository*
@ -73,7 +74,7 @@ Build instructions:
* Alpine, ArchLinux, openSUSE, Gentoo, Debian, Ubuntu, etc. * Alpine, ArchLinux, openSUSE, Gentoo, Debian, Ubuntu, etc.
* Windows - [![Build on Windows](https://github.com/PurpleI2P/i2pd/actions/workflows/build-windows.yml/badge.svg)](https://github.com/PurpleI2P/i2pd/actions/workflows/build-windows.yml) * Windows - [![Build on Windows](https://github.com/PurpleI2P/i2pd/actions/workflows/build-windows.yml/badge.svg)](https://github.com/PurpleI2P/i2pd/actions/workflows/build-windows.yml)
* Mac OS X - [![Build on OSX](https://github.com/PurpleI2P/i2pd/actions/workflows/build-osx.yml/badge.svg)](https://github.com/PurpleI2P/i2pd/actions/workflows/build-osx.yml) * Mac OS X - [![Build on OSX](https://github.com/PurpleI2P/i2pd/actions/workflows/build-osx.yml/badge.svg)](https://github.com/PurpleI2P/i2pd/actions/workflows/build-osx.yml)
* Docker image - [![Build Status](https://img.shields.io/docker/cloud/build/purplei2p/i2pd)](https://hub.docker.com/r/purplei2p/i2pd/builds/) * Docker image - [![Build Status](https://img.shields.io/docker/cloud/build/purplei2p/i2pd)](https://hub.docker.com/r/purplei2p/i2pd/builds/) [![Build containers](https://github.com/PurpleI2P/i2pd/actions/workflows/docker.yml/badge.svg)](https://github.com/PurpleI2P/i2pd/actions/workflows/docker.yml)
* Snap - [![i2pd](https://snapcraft.io/i2pd/badge.svg)](https://snapcraft.io/i2pd) [![i2pd](https://snapcraft.io/i2pd/trending.svg?name=0)](https://snapcraft.io/i2pd) * Snap - [![i2pd](https://snapcraft.io/i2pd/badge.svg)](https://snapcraft.io/i2pd) [![i2pd](https://snapcraft.io/i2pd/trending.svg?name=0)](https://snapcraft.io/i2pd)
* FreeBSD - [![Build on FreeBSD](https://github.com/PurpleI2P/i2pd/actions/workflows/build-freebsd.yml/badge.svg)](https://github.com/PurpleI2P/i2pd/actions/workflows/build-freebsd.yml) * FreeBSD - [![Build on FreeBSD](https://github.com/PurpleI2P/i2pd/actions/workflows/build-freebsd.yml/badge.svg)](https://github.com/PurpleI2P/i2pd/actions/workflows/build-freebsd.yml)
* Android - [![Android CI](https://github.com/PurpleI2P/i2pd-android/actions/workflows/android.yml/badge.svg)](https://github.com/PurpleI2P/i2pd-android/actions/workflows/android.yml) * Android - [![Android CI](https://github.com/PurpleI2P/i2pd-android/actions/workflows/android.yml/badge.svg)](https://github.com/PurpleI2P/i2pd-android/actions/workflows/android.yml)
@ -85,6 +86,16 @@ Using i2pd
See [documentation](https://i2pd.readthedocs.io/en/latest/user-guide/run/) and See [documentation](https://i2pd.readthedocs.io/en/latest/user-guide/run/) and
[example config file](https://github.com/PurpleI2P/i2pd/blob/openssl/contrib/i2pd.conf). [example config file](https://github.com/PurpleI2P/i2pd/blob/openssl/contrib/i2pd.conf).
Localization
------------
You can help us with translation i2pd to your language using Crowdin platform!
Translation project can be found [here](https://crowdin.com/project/i2pd).
New languages can be requested on project's [discussion page](https://crowdin.com/project/i2pd/discussions).
Current status: [![Crowdin](https://badges.crowdin.net/i2pd/localized.svg)](https://crowdin.com/project/i2pd)
Donations Donations
--------- ---------

View File

@ -1,57 +0,0 @@
version: 2.38.0.{build}
pull_requests:
do_not_increment_build_number: true
branches:
only:
- openssl
skip_tags: true
os: Visual Studio 2015
shallow_clone: true
clone_depth: 1
# avoid building 32-bit if 64-bit failed already
matrix:
fast_finish: true
environment:
APPVEYOR_SAVE_CACHE_ON_ERROR: true
MSYS2_PATH_TYPE: inherit
CHERE_INVOKING: enabled_from_arguments
matrix:
- MSYSTEM: MINGW64
- MSYSTEM: MINGW32
cache:
- c:\msys64\var\cache\pacman\pkg\
install:
# install new signing keyring
- c:\msys64\usr\bin\bash -lc "curl -O https://mirror.selfnet.de/msys2/msys/x86_64/msys2-keyring-r21.b39fb11-1-any.pkg.tar.xz"
- c:\msys64\usr\bin\bash -lc "curl -O https://mirror.selfnet.de/msys2/msys/x86_64/msys2-keyring-r21.b39fb11-1-any.pkg.tar.xz.sig"
- c:\msys64\usr\bin\bash -lc "pacman-key --verify msys2-keyring-r21.b39fb11-1-any.pkg.tar.xz.sig"
- c:\msys64\usr\bin\bash -lc "pacman --noconfirm -U msys2-keyring-r21.b39fb11-1-any.pkg.tar.xz"
# remove packages which can break build
- c:\msys64\usr\bin\bash -lc "pacman --noconfirm -Rns gcc-fortran gcc mingw-w64-{i686,x86_64}-gcc-ada mingw-w64-{i686,x86_64}-gcc-objc"
# update runtime
- c:\msys64\usr\bin\bash -lc "pacman --noconfirm -Syuu"
# Kill bash before next try
- taskkill /T /F /IM bash.exe /IM gpg.exe /IM gpg-agent.exe | exit /B 0
# update packages and install required
- c:\msys64\usr\bin\bash -lc "pacman --noconfirm -Syuu $MINGW_PACKAGE_PREFIX-boost $MINGW_PACKAGE_PREFIX-miniupnpc"
build_script:
- c:\msys64\usr\bin\bash -lc "make USE_UPNP=yes DEBUG=no -j3"
# prepare archive for uploading
- set "FILELIST=i2pd.exe README.txt contrib/i2pd.conf contrib/tunnels.conf contrib/certificates contrib/tunnels.d"
- echo This is development build, use it carefully! For running in portable mode, move all files from contrib directory here. > README.txt
- 7z a -tzip -mx9 -mmt i2pd-%APPVEYOR_BUILD_VERSION%-%APPVEYOR_REPO_COMMIT:~0,7%-mingw-win%MSYSTEM:~-2%.zip %FILELIST%
after_build:
- c:\msys64\usr\bin\bash -lc "pacman --noconfirm -Sc"
test: off
deploy: off
artifacts:
- path: i2pd-*.zip

1
build/.gitignore vendored
View File

@ -3,6 +3,7 @@
/i2pd /i2pd
/libi2pd.a /libi2pd.a
/libi2pdclient.a /libi2pdclient.a
/libi2pdlang.a
/cmake_install.cmake /cmake_install.cmake
/CMakeCache.txt /CMakeCache.txt
/CPackConfig.cmake /CPackConfig.cmake

View File

@ -27,6 +27,9 @@ option(WITH_THREADSANITIZER "Build with thread sanitizer unix only" OFF)
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake_modules") set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake_modules")
set(CMAKE_SOURCE_DIR "..") set(CMAKE_SOURCE_DIR "..")
#Handle paths nicely
include(GNUInstallDirs)
# architecture # architecture
include(TargetArch) include(TargetArch)
target_architecture(ARCHITECTURE) target_architecture(ARCHITECTURE)
@ -48,8 +51,8 @@ set_target_properties(libi2pd PROPERTIES PREFIX "")
if(WITH_LIBRARY) if(WITH_LIBRARY)
install(TARGETS libi2pd install(TARGETS libi2pd
EXPORT libi2pd EXPORT libi2pd
ARCHIVE DESTINATION lib ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
LIBRARY DESTINATION lib LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
COMPONENT Libraries) COMPONENT Libraries)
# TODO Make libi2pd available to 3rd party projects via CMake as imported target # TODO Make libi2pd available to 3rd party projects via CMake as imported target
# FIXME This pulls stdafx # FIXME This pulls stdafx
@ -63,12 +66,22 @@ set_target_properties(libi2pdclient PROPERTIES PREFIX "")
if(WITH_LIBRARY) if(WITH_LIBRARY)
install(TARGETS libi2pdclient install(TARGETS libi2pdclient
EXPORT libi2pdclient EXPORT libi2pdclient
ARCHIVE DESTINATION lib ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
LIBRARY DESTINATION lib LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
COMPONENT Libraries) COMPONENT Libraries)
endif() endif()
FILE(GLOB LANG_SRC ${LANG_SRC_DIR}/*.cpp) FILE(GLOB LANG_SRC ${LANG_SRC_DIR}/*.cpp)
add_library(libi2pdlang ${LANG_SRC})
set_target_properties(libi2pdlang PROPERTIES PREFIX "")
if(WITH_LIBRARY)
install(TARGETS libi2pdlang
EXPORT libi2pdlang
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
COMPONENT Libraries)
endif()
set(DAEMON_SRC set(DAEMON_SRC
"${DAEMON_SRC_DIR}/Daemon.cpp" "${DAEMON_SRC_DIR}/Daemon.cpp"
@ -198,10 +211,11 @@ if(WITH_PCH)
) )
target_compile_options(libi2pd PRIVATE -include libi2pd/stdafx.h) target_compile_options(libi2pd PRIVATE -include libi2pd/stdafx.h)
target_compile_options(libi2pdclient PRIVATE -include libi2pd/stdafx.h) target_compile_options(libi2pdclient PRIVATE -include libi2pd/stdafx.h)
target_compile_options(libi2pdlang PRIVATE -include libi2pd/stdafx.h)
target_link_libraries(libi2pd stdafx) target_link_libraries(libi2pd stdafx)
endif() endif()
target_link_libraries(libi2pdclient libi2pd) target_link_libraries(libi2pdclient libi2pd libi2pdlang)
find_package(Boost COMPONENTS system filesystem program_options date_time REQUIRED) find_package(Boost COMPONENTS system filesystem program_options date_time REQUIRED)
if(NOT DEFINED Boost_INCLUDE_DIRS) if(NOT DEFINED Boost_INCLUDE_DIRS)
@ -261,11 +275,8 @@ message(STATUS " ADDRSANITIZER : ${WITH_ADDRSANITIZER}")
message(STATUS " THREADSANITIZER : ${WITH_THREADSANITIZER}") message(STATUS " THREADSANITIZER : ${WITH_THREADSANITIZER}")
message(STATUS "---------------------------------------") message(STATUS "---------------------------------------")
#Handle paths nicely
include(GNUInstallDirs)
if(WITH_BINARY) if(WITH_BINARY)
add_executable("${PROJECT_NAME}" ${LANG_SRC} ${DAEMON_SRC}) add_executable("${PROJECT_NAME}" ${DAEMON_SRC})
if(WITH_STATIC) if(WITH_STATIC)
set_target_properties("${PROJECT_NAME}" PROPERTIES LINK_FLAGS "-static") set_target_properties("${PROJECT_NAME}" PROPERTIES LINK_FLAGS "-static")
@ -295,7 +306,7 @@ if(WITH_BINARY)
endif() endif()
target_link_libraries(libi2pd ${Boost_LIBRARIES} ${ZLIB_LIBRARY}) target_link_libraries(libi2pd ${Boost_LIBRARIES} ${ZLIB_LIBRARY})
target_link_libraries("${PROJECT_NAME}" libi2pd libi2pdclient ${DL_LIB} ${Boost_LIBRARIES} ${OPENSSL_LIBRARIES} ${UPNP_LIB} ${ZLIB_LIBRARY} ${CMAKE_THREAD_LIBS_INIT} ${MINGW_EXTRA} ${DL_LIB} ${CMAKE_REQUIRED_LIBRARIES}) target_link_libraries("${PROJECT_NAME}" libi2pd libi2pdclient libi2pdlang ${DL_LIB} ${Boost_LIBRARIES} ${OPENSSL_LIBRARIES} ${UPNP_LIB} ${ZLIB_LIBRARY} ${CMAKE_THREAD_LIBS_INIT} ${MINGW_EXTRA} ${DL_LIB} ${CMAKE_REQUIRED_LIBRARIES})
install(TARGETS "${PROJECT_NAME}" RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT Runtime) install(TARGETS "${PROJECT_NAME}" RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT Runtime)
set(APPS "\${CMAKE_INSTALL_PREFIX}/bin/${PROJECT_NAME}${CMAKE_EXECUTABLE_SUFFIX}") set(APPS "\${CMAKE_INSTALL_PREFIX}/bin/${PROJECT_NAME}${CMAKE_EXECUTABLE_SUFFIX}")

View File

@ -1,34 +0,0 @@
Howto build & run
==================
**Build**
Assuming you're in the root directory of the anoncoin source code.
$ `cd build/docker`
$ `docker -t meeh/i2pd:latest .`
**Run**
To run either the local build, or if not found - fetched prebuild from hub.docker.io, run the following command.
$ `docker run --name anonnode -v /path/to/i2pd/datadir/on/host:/var/lib/i2pd -p 7070:7070 -p 4444:4444 -p 4447:4447 -p 7656:7656 -p 2827:2827 -p 7654:7654 -p 7650:7650 -d meeh/i2pd`
All the ports ( -p HOSTPORT:DOCKERPORT ) is optional. However the command above enable all features (Webconsole, HTTP Proxy, BOB, SAM, i2cp, etc)
The volume ( -v HOSTDIR:DOCKERDIR ) is also optional, but if you don't use it, your config, routerid and private keys will die along with the container.
**Options**
Options are set via docker environment variables. This can be set at run with -e parameters.
* **ENABLE_IPV6** - Enable IPv6 support. Any value can be used - it triggers as long as it's not empty.
* **LOGLEVEL** - Set the loglevel.
* **ENABLE_AUTH** - Enable auth for the webconsole. Username and password needs to be set manually in i2pd.conf cause security reasons.
**Logging**
Logging happens to STDOUT as the best practise with docker containers, since infrastructure systems like kubernetes with ELK integration can automatically forward the log to say, kibana or greylog without manual setup. :)

View File

@ -1,11 +0,0 @@
FROM ubuntu
RUN apt-get update && apt-get install -y libboost-dev libboost-filesystem-dev \
libboost-program-options-dev libboost-date-time-dev \
libssl-dev git build-essential
RUN git clone https://github.com/PurpleI2P/i2pd.git
WORKDIR /i2pd
RUN make
CMD ./i2pd

View File

@ -1,2 +0,0 @@
i2pd:
build: .

View File

@ -0,0 +1,32 @@
-----BEGIN CERTIFICATE-----
MIIFfzCCA2egAwIBAgIEbNbRPjANBgkqhkiG9w0BAQ0FADBwMQswCQYDVQQGEwJY
WDELMAkGA1UECAwCWFgxCzAJBgNVBAcMAlhYMR4wHAYDVQQKDBVJMlAgQW5vbnlt
b3VzIE5ldHdvcmsxDDAKBgNVBAsMA0kyUDEZMBcGA1UEAwwQb3JpZ25hbEBtYWls
LmkycDAeFw0yMTA3MDYyMjExMDFaFw0zMTA3MDQyMjExMDFaMHAxCzAJBgNVBAYT
AlhYMQswCQYDVQQIDAJYWDELMAkGA1UEBwwCWFgxHjAcBgNVBAoMFUkyUCBBbm9u
eW1vdXMgTmV0d29yazEMMAoGA1UECwwDSTJQMRkwFwYDVQQDDBBvcmlnbmFsQG1h
aWwuaTJwMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAvNJz2KGuAkHP
tGFobfLvpybtxB50fkcEsTc9opmiy7wBKK9rSI01VS616IhABkWKZVfK2A9NqpGv
v/CyhTKoaeSNeXY7+zORUWgWK/zA9fA4GRZFqlW8j4tbompDwcLYNqRBCsn1C0OY
YA5JhXPBixMcnXl8N8x4sXhQ4l9R3+QrydhUHRvgDc8dOxRyIX7zuQAyf8tmA2Xo
xZLdvDcCJdLBIbFwxhIceIhgcOwaOx7oRkZDZdYcLJd3zjyPbu8JtOM2ZkwH7r+0
ro5PktuDp2LAS6SII5yYNcwcrvPZGPqhLdifIw1BrdTIb/rIkQZ5iXOOdyPmT7e8
IwAJcPFlfvrS4Vbi9oDqyx3aDUBoubgmFnO1TirL56ck83R/ubcKtdnyzAn5dp+f
ZNYW6/foSBpDDOCViylbFAR5H0HJEbBns7PZx6mGEEI4tUAJdNYl7Ly7Df60a9Rz
cD/gz08U9UwFXYKoT6roEjToADGAzb5MI4cVlAb2AmQaMNXNe04HcDL1bU50mkNU
amqPv8nxf72fBQCEmZz2G57T6QiYTtcCwiWS1QdWsuaOtCo9zO0MKcjzSdUxuxEc
dXhjQdNegsgg/Xk7bJ8lKOsACqMpFftdPmuyeZU2t+3RPuBpV/0j2qUfg/y6kb0z
CxAOYmlcL4kqw4VT+5V/EeZLIG0h9I0CAwEAAaMhMB8wHQYDVR0OBBYEFD/wJObg
CCDuhMJCVWTSTj+B3rsUMA0GCSqGSIb3DQEBDQUAA4ICAQC0PjsTSPWlGbLNeeI8
F0B5xAwXYJzZ7/LRxh8u42HDUqVIDjqkuls1l3v9D7htty2Gr3Ws2dcvcOr2KcOy
mEWg+jdP/N3vt9IkZeVS4YQoPgq6orn7lVkk00bcKb24f7ZnoQnnVV0/m42Y5P4j
LLh+8MBxsez9azXyZbDVEkgsMUAkdVO6KNz6scqz7wb8egV2GAMAp7cwChC6lanK
gv9ZyJhG/HdTv6VyuMZhJy6rX4geM97tm1iHu1VLsQcIzBKAdEvWJv8ofMeiyINe
hqAP9NYaeowKi975NOrmf+XZwxd0niApIohV684RCVUfL8H7HSPbdXhBJ/WslyDP
cTGhA2BLqEXZBn/nLQknlnl0SZTQxG2n4fEgD1E5YS/aoBrig/uXtWm2Zdf8U3mM
+bNXhbi9s7LneN2ye8LlNJBSRklNn/bNo8OmzLII1RQwf1+vaHT96lASbTVepMZ/
Y9VcC8fAmho/zfQEKueLEB03K+gr2dGD+1crmMtUBjWJ9vPjtooZArtkDbh+kVYA
cx4N4NXULRwxVWZe5wTQOqcZ3qSS1ClMwaziwychGaj8xRAirHMZnlPOZO1UK4+5
8F4RMJktyZjNgSLP76XPS4rJK5fobuPqFeA4OpDFn/5+/XeQFF6i6wntx1tzztzH
zc+BrVZOdcYPqu9iLXyRQ9JwwA==
-----END CERTIFICATE-----

View File

@ -1,31 +0,0 @@
-----BEGIN CERTIFICATE-----
MIIFVDCCAzwCCQC2r1XWYtqtAzANBgkqhkiG9w0BAQsFADBsMQswCQYDVQQGEwJY
WDELMAkGA1UECAwCWFgxCzAJBgNVBAcMAlhYMRMwEQYDVQQKDApQdXJwbGUgSTJQ
MQ0wCwYDVQQLDARJMlBEMR8wHQYJKoZIhvcNAQkBFhBvcmlnbmFsQG1haWwuaTJw
MB4XDTE1MDIyMjEzNTgxOFoXDTI1MDIxOTEzNTgxOFowbDELMAkGA1UEBhMCWFgx
CzAJBgNVBAgMAlhYMQswCQYDVQQHDAJYWDETMBEGA1UECgwKUHVycGxlIEkyUDEN
MAsGA1UECwwESTJQRDEfMB0GCSqGSIb3DQEJARYQb3JpZ25hbEBtYWlsLmkycDCC
AiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBALp3D/gdvFjrMm+IE8tHZCWE
hQ6Pp0CCgCGDBC3WQFLqR98bqVPl4UwRG/MKY/LY7Woai06JNmGcpfw0LMoNnHxT
bvKtDRe/8kQdhdLHhgIkWKSbMvTAl7uUdV6FzsPgDR0x7scoFVWEhkF0wfmzGF2V
yr/WCBQejFPu69z03m5tRQ8Xjp2txWV45RawUmFu50bgbZvLCSLfTkIvxmfJzgPN
pJ3sPa/g7TBZl2uEiAu4uaEKvTuuzStOWCGgFaHYFVlTfFXTvmhFMqHfaidtzrlu
H35WGrmIWTDl6uGPC5QkSppvkj73rDj5aEyPzWMz5DN3YeECoVSchN+OJJCM6m7+
rLFYXghVEp2h+T9O1GBRfcHlQ2E3CrWWvxhmK8dfteJmd501dyNX2paeuIg/aPFO
54/8m2r11uyF29hgY8VWLdXtqvwhKuK36PCzofEwDp9QQX8GRsEV4pZTrn4bDhGo
kb9BF7TZTqtL3uyiRmIyBXrNNiYlA1Xm4fyKRtxl0mrPaUXdgdnCt3KxOAJ8WM2B
7L/kk9U8C/nexHbMxIZfTap49XcUg5dxSO9kOBosIOcCUms8sAzBPDV2tWAByhYF
jI/Tutbd3F0+fvcmTcIFOlGbOxKgO2SfwXjv/44g/3LMK6IAMFB9UOc8KhnnJP0f
uAHvMXn1ahRs4pM1VizLAgMBAAEwDQYJKoZIhvcNAQELBQADggIBAIOxdaXT+wfu
nv/+1hy5T4TlRMNNsuj79ROcy6Mp+JwMG50HjTc0qTlXh8C7nHybDJn4v7DA+Nyn
RxT0J5I+Gqn+Na9TaC9mLeX/lwe8/KomyhBWxjrsyWj1V6v/cLO924S2rtcfzMDm
l3SFh9YHM1KF/R9N1XYBwtMzr3bupWDnE1yycYp1F4sMLr5SMzMQ0svQpQEM2/y5
kly8+eUzryhm+ag9x1686uEG5gxhQ1eHQoZEaClHUOsV+28+d5If7cqcYx9Hf5Tt
CiVjJQzdxBF+6GeiJtKxnLtevqlkbyIJt6Cm9/7YIy/ovRGF2AKSYN6oCwmZQ6i1
8nRnFq5zE7O94m+GXconWZxy0wVqA6472HThMi7S+Tk/eLYen2ilGY+KCb9a0FH5
5MOuWSoJZ8/HfW2VeQmL8EjhWm5F2ybg28wgXK4BOGR3jQi03Fsc+AFidnWxSKo0
aiJoPgOsfyu8/fnCcAi07kSmjzUKIWskApgcpGQLNXHFK9mtg7+VA8esRnfLlKtP
tJf+nNAPY1sqHfGBzh7WWGWal5RGHF5nEm3ta3oiFF5sMKCJ6C87zVwFkEcRytGC
xOGmiG1O1RPrO5NG7rZUaQ4y1OKl2Y1H+nGONzZ3mvoAOvxEq6JtUnU2kZscpPlk
fpeOSDoGBYJGbIpzDreBDhxaZrwGq36k
-----END CERTIFICATE-----

View File

@ -25,7 +25,8 @@ RUN mkdir -p "$I2PD_HOME" "$DATA_DIR" \
# 1. install deps, clone and build. # 1. install deps, clone and build.
# 2. strip binaries. # 2. strip binaries.
# 3. Purge all dependencies and other unrelated packages, including build directory. # 3. Purge all dependencies and other unrelated packages, including build directory.
RUN apk --no-cache --virtual build-dependendencies add make gcc g++ libtool zlib-dev boost-dev build-base openssl-dev openssl miniupnpc-dev git \ RUN apk update \
&& apk --no-cache --virtual build-dependendencies add make gcc g++ libtool zlib-dev boost-dev build-base openssl-dev openssl miniupnpc-dev git \
&& mkdir -p /tmp/build \ && mkdir -p /tmp/build \
&& cd /tmp/build && git clone -b ${GIT_BRANCH} ${REPO_URL} \ && cd /tmp/build && git clone -b ${GIT_BRANCH} ${REPO_URL} \
&& cd i2pd \ && cd i2pd \

View File

@ -7,7 +7,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: i2pd\n" "Project-Id-Version: i2pd\n"
"Report-Msgid-Bugs-To: https://github.com/PurpleI2P/i2pd/issues\n" "Report-Msgid-Bugs-To: https://github.com/PurpleI2P/i2pd/issues\n"
"POT-Creation-Date: 2021-06-15 17:40\n" "POT-Creation-Date: 2021-08-06 17:12\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
@ -18,559 +18,567 @@ msgstr ""
"X-Poedit-SearchPath-0: daemon/HTTPServer.cpp\n" "X-Poedit-SearchPath-0: daemon/HTTPServer.cpp\n"
"X-Poedit-SearchPath-1: libi2pd_client/HTTPProxy.cpp\n" "X-Poedit-SearchPath-1: libi2pd_client/HTTPProxy.cpp\n"
#: daemon/HTTPServer.cpp:85 #: daemon/HTTPServer.cpp:177
msgid "Disabled"
msgstr ""
#: daemon/HTTPServer.cpp:86
msgid "Enabled"
msgstr ""
#: daemon/HTTPServer.cpp:141
msgid "day" msgid "day"
msgid_plural "days" msgid_plural "days"
msgstr[0] "" msgstr[0] ""
msgstr[1] "" msgstr[1] ""
#: daemon/HTTPServer.cpp:145
#: daemon/HTTPServer.cpp:181
msgid "hour" msgid "hour"
msgid_plural "hours" msgid_plural "hours"
msgstr[0] "" msgstr[0] ""
msgstr[1] "" msgstr[1] ""
#: daemon/HTTPServer.cpp:149
#: daemon/HTTPServer.cpp:185
msgid "minute" msgid "minute"
msgid_plural "minutes" msgid_plural "minutes"
msgstr[0] "" msgstr[0] ""
msgstr[1] "" msgstr[1] ""
#: daemon/HTTPServer.cpp:152 #: daemon/HTTPServer.cpp:188
msgid "second" msgid "second"
msgid_plural "seconds" msgid_plural "seconds"
msgstr[0] "" msgstr[0] ""
msgstr[1] "" msgstr[1] ""
#: daemon/HTTPServer.cpp:160 daemon/HTTPServer.cpp:188 #. tr: Kibibit
#: daemon/HTTPServer.cpp:196 daemon/HTTPServer.cpp:224
msgid "KiB" msgid "KiB"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:162 #. tr: Mebibit
#: daemon/HTTPServer.cpp:198
msgid "MiB" msgid "MiB"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:164 #. tr: Gibibit
#: daemon/HTTPServer.cpp:200
msgid "GiB" msgid "GiB"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:181 #: daemon/HTTPServer.cpp:217
msgid "building" msgid "building"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:182 #: daemon/HTTPServer.cpp:218
msgid "failed" msgid "failed"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:183 #: daemon/HTTPServer.cpp:219
msgid "expiring" msgid "expiring"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:184 #: daemon/HTTPServer.cpp:220
msgid "established" msgid "established"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:185 #: daemon/HTTPServer.cpp:221
msgid "unknown" msgid "unknown"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:187 #: daemon/HTTPServer.cpp:223
msgid "exploratory" msgid "exploratory"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:223 #: daemon/HTTPServer.cpp:259
msgid "<b>i2pd</b> webconsole" msgid "<b>i2pd</b> webconsole"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:226 #: daemon/HTTPServer.cpp:262
msgid "Main page" msgid "Main page"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:227 daemon/HTTPServer.cpp:683 #: daemon/HTTPServer.cpp:263 daemon/HTTPServer.cpp:725
msgid "Router commands" msgid "Router commands"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:228 #: daemon/HTTPServer.cpp:264 daemon/HTTPServer.cpp:448
msgid "Local destinations" #: daemon/HTTPServer.cpp:460
msgstr ""
#: daemon/HTTPServer.cpp:230 daemon/HTTPServer.cpp:382
#: daemon/HTTPServer.cpp:463 daemon/HTTPServer.cpp:469
#: daemon/HTTPServer.cpp:599 daemon/HTTPServer.cpp:642
#: daemon/HTTPServer.cpp:646
msgid "LeaseSets"
msgstr ""
#: daemon/HTTPServer.cpp:232 daemon/HTTPServer.cpp:652
msgid "Tunnels"
msgstr ""
#: daemon/HTTPServer.cpp:233 daemon/HTTPServer.cpp:727
#: daemon/HTTPServer.cpp:743
msgid "Transit tunnels"
msgstr ""
#: daemon/HTTPServer.cpp:234 daemon/HTTPServer.cpp:792
msgid "Transports"
msgstr ""
#: daemon/HTTPServer.cpp:235
msgid "I2P tunnels"
msgstr ""
#: daemon/HTTPServer.cpp:237 daemon/HTTPServer.cpp:854
#: daemon/HTTPServer.cpp:864
msgid "SAM sessions"
msgstr ""
#: daemon/HTTPServer.cpp:253 daemon/HTTPServer.cpp:1254
#: daemon/HTTPServer.cpp:1257 daemon/HTTPServer.cpp:1260
#: daemon/HTTPServer.cpp:1274 daemon/HTTPServer.cpp:1319
#: daemon/HTTPServer.cpp:1322 daemon/HTTPServer.cpp:1325
msgid "ERROR"
msgstr ""
#: daemon/HTTPServer.cpp:260
msgid "OK"
msgstr ""
#: daemon/HTTPServer.cpp:261
msgid "Testing"
msgstr ""
#: daemon/HTTPServer.cpp:262
msgid "Firewalled"
msgstr ""
#: daemon/HTTPServer.cpp:263 daemon/HTTPServer.cpp:284
#: daemon/HTTPServer.cpp:370
msgid "Unknown"
msgstr ""
#: daemon/HTTPServer.cpp:264 daemon/HTTPServer.cpp:394
#: daemon/HTTPServer.cpp:395 daemon/HTTPServer.cpp:922
#: daemon/HTTPServer.cpp:931
msgid "Proxy"
msgstr ""
#: daemon/HTTPServer.cpp:265
msgid "Mesh"
msgstr ""
#: daemon/HTTPServer.cpp:268
msgid "Error"
msgstr ""
#: daemon/HTTPServer.cpp:272
msgid "Clock skew"
msgstr ""
#: daemon/HTTPServer.cpp:275
msgid "Offline"
msgstr ""
#: daemon/HTTPServer.cpp:278
msgid "Symmetric NAT"
msgstr ""
#: daemon/HTTPServer.cpp:290
msgid "Uptime"
msgstr ""
#: daemon/HTTPServer.cpp:293
msgid "Network status"
msgstr ""
#: daemon/HTTPServer.cpp:298
msgid "Network status v6"
msgstr ""
#: daemon/HTTPServer.cpp:304 daemon/HTTPServer.cpp:311
msgid "Stopping in"
msgstr ""
#: daemon/HTTPServer.cpp:318
msgid "Family"
msgstr ""
#: daemon/HTTPServer.cpp:319
msgid "Tunnel creation success rate"
msgstr ""
#: daemon/HTTPServer.cpp:320
msgid "Received"
msgstr ""
#: daemon/HTTPServer.cpp:322 daemon/HTTPServer.cpp:325
#: daemon/HTTPServer.cpp:328
msgid "KiB/s"
msgstr ""
#: daemon/HTTPServer.cpp:323
msgid "Sent"
msgstr ""
#: daemon/HTTPServer.cpp:326
msgid "Transit"
msgstr ""
#: daemon/HTTPServer.cpp:329
msgid "Data path"
msgstr ""
#: daemon/HTTPServer.cpp:332
msgid "Hidden content. Press on text to see."
msgstr ""
#: daemon/HTTPServer.cpp:335
msgid "Router Ident"
msgstr ""
#: daemon/HTTPServer.cpp:337
msgid "Router Family"
msgstr ""
#: daemon/HTTPServer.cpp:338
msgid "Router Caps"
msgstr ""
#: daemon/HTTPServer.cpp:339
msgid "Version"
msgstr ""
#: daemon/HTTPServer.cpp:340
msgid "Our external address"
msgstr ""
#: daemon/HTTPServer.cpp:348
msgid "supported"
msgstr ""
#: daemon/HTTPServer.cpp:380
msgid "Routers"
msgstr ""
#: daemon/HTTPServer.cpp:381
msgid "Floodfills"
msgstr ""
#: daemon/HTTPServer.cpp:388 daemon/HTTPServer.cpp:908
msgid "Client Tunnels"
msgstr ""
#: daemon/HTTPServer.cpp:389
msgid "Transit Tunnels"
msgstr ""
#: daemon/HTTPServer.cpp:393
msgid "Services"
msgstr ""
#: daemon/HTTPServer.cpp:407 daemon/HTTPServer.cpp:419
msgid "Local Destinations" msgid "Local Destinations"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:442 #: daemon/HTTPServer.cpp:266 daemon/HTTPServer.cpp:418
#: daemon/HTTPServer.cpp:504 daemon/HTTPServer.cpp:510
#: daemon/HTTPServer.cpp:641 daemon/HTTPServer.cpp:684
#: daemon/HTTPServer.cpp:688
msgid "LeaseSets"
msgstr ""
#: daemon/HTTPServer.cpp:268 daemon/HTTPServer.cpp:694
msgid "Tunnels"
msgstr ""
#: daemon/HTTPServer.cpp:269 daemon/HTTPServer.cpp:425
#: daemon/HTTPServer.cpp:787 daemon/HTTPServer.cpp:803
msgid "Transit Tunnels"
msgstr ""
#: daemon/HTTPServer.cpp:270 daemon/HTTPServer.cpp:852
msgid "Transports"
msgstr ""
#: daemon/HTTPServer.cpp:271
msgid "I2P tunnels"
msgstr ""
#: daemon/HTTPServer.cpp:273 daemon/HTTPServer.cpp:914
#: daemon/HTTPServer.cpp:924
msgid "SAM sessions"
msgstr ""
#: daemon/HTTPServer.cpp:289 daemon/HTTPServer.cpp:1306
#: daemon/HTTPServer.cpp:1309 daemon/HTTPServer.cpp:1312
#: daemon/HTTPServer.cpp:1326 daemon/HTTPServer.cpp:1371
#: daemon/HTTPServer.cpp:1374 daemon/HTTPServer.cpp:1377
msgid "ERROR"
msgstr ""
#: daemon/HTTPServer.cpp:296
msgid "OK"
msgstr ""
#: daemon/HTTPServer.cpp:297
msgid "Testing"
msgstr ""
#: daemon/HTTPServer.cpp:298
msgid "Firewalled"
msgstr ""
#: daemon/HTTPServer.cpp:299 daemon/HTTPServer.cpp:320
#: daemon/HTTPServer.cpp:406
msgid "Unknown"
msgstr ""
#: daemon/HTTPServer.cpp:300 daemon/HTTPServer.cpp:435
#: daemon/HTTPServer.cpp:436 daemon/HTTPServer.cpp:982
#: daemon/HTTPServer.cpp:991
msgid "Proxy"
msgstr ""
#: daemon/HTTPServer.cpp:301
msgid "Mesh"
msgstr ""
#: daemon/HTTPServer.cpp:304
msgid "Error"
msgstr ""
#: daemon/HTTPServer.cpp:308
msgid "Clock skew"
msgstr ""
#: daemon/HTTPServer.cpp:311
msgid "Offline"
msgstr ""
#: daemon/HTTPServer.cpp:314
msgid "Symmetric NAT"
msgstr ""
#: daemon/HTTPServer.cpp:326
msgid "Uptime"
msgstr ""
#: daemon/HTTPServer.cpp:329
msgid "Network status"
msgstr ""
#: daemon/HTTPServer.cpp:334
msgid "Network status v6"
msgstr ""
#: daemon/HTTPServer.cpp:340 daemon/HTTPServer.cpp:347
msgid "Stopping in"
msgstr ""
#: daemon/HTTPServer.cpp:354
msgid "Family"
msgstr ""
#: daemon/HTTPServer.cpp:355
msgid "Tunnel creation success rate"
msgstr ""
#: daemon/HTTPServer.cpp:356
msgid "Received"
msgstr ""
#. tr: Kibibit/s
#: daemon/HTTPServer.cpp:358 daemon/HTTPServer.cpp:361
#: daemon/HTTPServer.cpp:364
msgid "KiB/s"
msgstr ""
#: daemon/HTTPServer.cpp:359
msgid "Sent"
msgstr ""
#: daemon/HTTPServer.cpp:362
msgid "Transit"
msgstr ""
#: daemon/HTTPServer.cpp:365
msgid "Data path"
msgstr ""
#: daemon/HTTPServer.cpp:368
msgid "Hidden content. Press on text to see."
msgstr ""
#: daemon/HTTPServer.cpp:371
msgid "Router Ident"
msgstr ""
#: daemon/HTTPServer.cpp:373
msgid "Router Family"
msgstr ""
#: daemon/HTTPServer.cpp:374
msgid "Router Caps"
msgstr ""
#: daemon/HTTPServer.cpp:375
msgid "Version"
msgstr ""
#: daemon/HTTPServer.cpp:376
msgid "Our external address"
msgstr ""
#: daemon/HTTPServer.cpp:384
msgid "supported"
msgstr ""
#: daemon/HTTPServer.cpp:416
msgid "Routers"
msgstr ""
#: daemon/HTTPServer.cpp:417
msgid "Floodfills"
msgstr ""
#: daemon/HTTPServer.cpp:424 daemon/HTTPServer.cpp:968
msgid "Client Tunnels"
msgstr ""
#: daemon/HTTPServer.cpp:434
msgid "Services"
msgstr ""
#: daemon/HTTPServer.cpp:435 daemon/HTTPServer.cpp:436
#: daemon/HTTPServer.cpp:437 daemon/HTTPServer.cpp:438
#: daemon/HTTPServer.cpp:439 daemon/HTTPServer.cpp:440
msgid "Enabled"
msgstr ""
#: daemon/HTTPServer.cpp:435 daemon/HTTPServer.cpp:436
#: daemon/HTTPServer.cpp:437 daemon/HTTPServer.cpp:438
#: daemon/HTTPServer.cpp:439 daemon/HTTPServer.cpp:440
msgid "Disabled"
msgstr ""
#: daemon/HTTPServer.cpp:483
msgid "Encrypted B33 address" msgid "Encrypted B33 address"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:451 #: daemon/HTTPServer.cpp:492
msgid "Address registration line" msgid "Address registration line"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:456 #: daemon/HTTPServer.cpp:497
msgid "Domain" msgid "Domain"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:457 #: daemon/HTTPServer.cpp:498
msgid "Generate" msgid "Generate"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:458 #: daemon/HTTPServer.cpp:499
msgid "" msgid ""
"<b>Note:</b> result string can be used only for registering 2LD domains " "<b>Note:</b> result string can be used only for registering 2LD domains "
"(example.i2p). For registering subdomains please use i2pd-tools." "(example.i2p). For registering subdomains please use i2pd-tools."
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:464 #: daemon/HTTPServer.cpp:505
msgid "Address" msgid "Address"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:464 #: daemon/HTTPServer.cpp:505
msgid "Type" msgid "Type"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:464 #: daemon/HTTPServer.cpp:505
msgid "EncType" msgid "EncType"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:474 daemon/HTTPServer.cpp:657 #: daemon/HTTPServer.cpp:515 daemon/HTTPServer.cpp:699
msgid "Inbound tunnels" msgid "Inbound tunnels"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:479 daemon/HTTPServer.cpp:489 #. tr: Milliseconds
#: daemon/HTTPServer.cpp:662 daemon/HTTPServer.cpp:672 #: daemon/HTTPServer.cpp:520 daemon/HTTPServer.cpp:530
#: Means milliseconds #: daemon/HTTPServer.cpp:704 daemon/HTTPServer.cpp:714
msgid "ms" msgid "ms"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:484 daemon/HTTPServer.cpp:667 #: daemon/HTTPServer.cpp:525 daemon/HTTPServer.cpp:709
msgid "Outbound tunnels" msgid "Outbound tunnels"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:496 #: daemon/HTTPServer.cpp:537
msgid "Tags" msgid "Tags"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:496 #: daemon/HTTPServer.cpp:537
msgid "Incoming" msgid "Incoming"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:503 daemon/HTTPServer.cpp:506 #: daemon/HTTPServer.cpp:544 daemon/HTTPServer.cpp:547
msgid "Outgoing" msgid "Outgoing"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:504 daemon/HTTPServer.cpp:520 #: daemon/HTTPServer.cpp:545 daemon/HTTPServer.cpp:561
msgid "Destination" msgid "Destination"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:504 #: daemon/HTTPServer.cpp:545
msgid "Amount" msgid "Amount"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:511 #: daemon/HTTPServer.cpp:552
msgid "Incoming Tags" msgid "Incoming Tags"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:519 daemon/HTTPServer.cpp:522 #: daemon/HTTPServer.cpp:560 daemon/HTTPServer.cpp:563
msgid "Tags sessions" msgid "Tags sessions"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:520 #: daemon/HTTPServer.cpp:561
msgid "Status" msgid "Status"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:529 daemon/HTTPServer.cpp:584 #: daemon/HTTPServer.cpp:570 daemon/HTTPServer.cpp:626
msgid "Local Destination" msgid "Local Destination"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:538 daemon/HTTPServer.cpp:887 #: daemon/HTTPServer.cpp:580 daemon/HTTPServer.cpp:947
msgid "Streams" msgid "Streams"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:560 #: daemon/HTTPServer.cpp:602
msgid "Close stream" msgid "Close stream"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:589 #: daemon/HTTPServer.cpp:631
msgid "I2CP session not found" msgid "I2CP session not found"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:592 #: daemon/HTTPServer.cpp:634
msgid "I2CP is not enabled" msgid "I2CP is not enabled"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:618 #: daemon/HTTPServer.cpp:660
msgid "Invalid" msgid "Invalid"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:621 #: daemon/HTTPServer.cpp:663
msgid "Store type" msgid "Store type"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:622 #: daemon/HTTPServer.cpp:664
msgid "Expires" msgid "Expires"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:627 #: daemon/HTTPServer.cpp:669
msgid "Non Expired Leases" msgid "Non Expired Leases"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:630 #: daemon/HTTPServer.cpp:672
msgid "Gateway" msgid "Gateway"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:631 #: daemon/HTTPServer.cpp:673
msgid "TunnelID" msgid "TunnelID"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:632 #: daemon/HTTPServer.cpp:674
msgid "EndDate" msgid "EndDate"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:642 #: daemon/HTTPServer.cpp:684
msgid "not floodfill" msgid "not floodfill"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:653 #: daemon/HTTPServer.cpp:695
msgid "Queue size" msgid "Queue size"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:684 #: daemon/HTTPServer.cpp:726
msgid "Run peer test" msgid "Run peer test"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:687 #: daemon/HTTPServer.cpp:731
msgid "Decline transit tunnels" msgid "Decline transit tunnels"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:689 #: daemon/HTTPServer.cpp:733
msgid "Accept transit tunnels" msgid "Accept transit tunnels"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:692 daemon/HTTPServer.cpp:697 #: daemon/HTTPServer.cpp:737 daemon/HTTPServer.cpp:742
msgid "Cancel graceful shutdown" msgid "Cancel graceful shutdown"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:694 daemon/HTTPServer.cpp:699 #: daemon/HTTPServer.cpp:739 daemon/HTTPServer.cpp:744
msgid "Start graceful shutdown" msgid "Start graceful shutdown"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:701 #: daemon/HTTPServer.cpp:747
msgid "Force shutdown" msgid "Force shutdown"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:704 #: daemon/HTTPServer.cpp:748
msgid "Reload external CSS styles"
msgstr ""
#: daemon/HTTPServer.cpp:751
msgid "" msgid ""
"<b>Note:</b> any action done here are not persistent and not changes your " "<b>Note:</b> any action done here are not persistent and not changes your "
"config files." "config files."
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:706 #: daemon/HTTPServer.cpp:753
msgid "Logging level" msgid "Logging level"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:714 #: daemon/HTTPServer.cpp:761
msgid "Transit tunnels limit" msgid "Transit tunnels limit"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:719 #: daemon/HTTPServer.cpp:766 daemon/HTTPServer.cpp:778
msgid "Change" msgid "Change"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:743 #: daemon/HTTPServer.cpp:770
msgid "Change language"
msgstr ""
#: daemon/HTTPServer.cpp:803
msgid "no transit tunnels currently built" msgid "no transit tunnels currently built"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:848 daemon/HTTPServer.cpp:871 #: daemon/HTTPServer.cpp:908 daemon/HTTPServer.cpp:931
msgid "SAM disabled" msgid "SAM disabled"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:864 #: daemon/HTTPServer.cpp:924
msgid "no sessions currently running" msgid "no sessions currently running"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:877 #: daemon/HTTPServer.cpp:937
msgid "SAM session not found" msgid "SAM session not found"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:882 #: daemon/HTTPServer.cpp:942
msgid "SAM Session" msgid "SAM Session"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:939 #: daemon/HTTPServer.cpp:999
msgid "Server Tunnels" msgid "Server Tunnels"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:955 #: daemon/HTTPServer.cpp:1015
msgid "Client Forwards" msgid "Client Forwards"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:969 #: daemon/HTTPServer.cpp:1029
msgid "Server Forwards" msgid "Server Forwards"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:1175 #: daemon/HTTPServer.cpp:1227
msgid "Unknown page" msgid "Unknown page"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:1194 #: daemon/HTTPServer.cpp:1246
msgid "Invalid token" msgid "Invalid token"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:1252 daemon/HTTPServer.cpp:1309 #: daemon/HTTPServer.cpp:1304 daemon/HTTPServer.cpp:1361
#: daemon/HTTPServer.cpp:1337 #: daemon/HTTPServer.cpp:1401
msgid "SUCCESS" msgid "SUCCESS"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:1252 #: daemon/HTTPServer.cpp:1304
msgid "Stream closed" msgid "Stream closed"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:1254 #: daemon/HTTPServer.cpp:1306
msgid "Stream not found or already was closed" msgid "Stream not found or already was closed"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:1257 #: daemon/HTTPServer.cpp:1309
msgid "Destination not found" msgid "Destination not found"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:1260 #: daemon/HTTPServer.cpp:1312
msgid "StreamID can't be null" msgid "StreamID can't be null"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:1262 daemon/HTTPServer.cpp:1327 #: daemon/HTTPServer.cpp:1314 daemon/HTTPServer.cpp:1379
msgid "Return to destination page" msgid "Return to destination page"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:1263 daemon/HTTPServer.cpp:1276 #: daemon/HTTPServer.cpp:1315 daemon/HTTPServer.cpp:1328
msgid "You will be redirected back in 5 seconds" #: daemon/HTTPServer.cpp:1403
msgid "You will be redirected in 5 seconds"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:1274 #: daemon/HTTPServer.cpp:1326
msgid "Transit tunnels count must not exceed 65535" msgid "Transit tunnels count must not exceed 65535"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:1275 daemon/HTTPServer.cpp:1338 #: daemon/HTTPServer.cpp:1327 daemon/HTTPServer.cpp:1402
msgid "Back to commands list" msgid "Back to commands list"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:1311 #: daemon/HTTPServer.cpp:1363
msgid "Register at reg.i2p" msgid "Register at reg.i2p"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:1312 #: daemon/HTTPServer.cpp:1364
msgid "Description" msgid "Description"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:1312 #: daemon/HTTPServer.cpp:1364
msgid "A bit information about service on domain" msgid "A bit information about service on domain"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:1313 #: daemon/HTTPServer.cpp:1365
msgid "Submit" msgid "Submit"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:1319 #: daemon/HTTPServer.cpp:1371
msgid "Domain can't end with .b32.i2p" msgid "Domain can't end with .b32.i2p"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:1322 #: daemon/HTTPServer.cpp:1374
msgid "Domain must end with .i2p" msgid "Domain must end with .i2p"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:1325 #: daemon/HTTPServer.cpp:1377
msgid "Such destination is not found" msgid "Such destination is not found"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:1333 #: daemon/HTTPServer.cpp:1397
msgid "Unknown command" msgid "Unknown command"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:1337 #: daemon/HTTPServer.cpp:1401
msgid "Command accepted" msgid "Command accepted"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:1339
msgid "You will be redirected in 5 seconds"
msgstr ""
#: libi2pd_client/HTTPProxy.cpp:157 #: libi2pd_client/HTTPProxy.cpp:157
msgid "Proxy error" msgid "Proxy error"
msgstr "" msgstr ""
@ -592,7 +600,7 @@ msgid "You may try to find this host on jump services below"
msgstr "" msgstr ""
#: libi2pd_client/HTTPProxy.cpp:273 libi2pd_client/HTTPProxy.cpp:288 #: libi2pd_client/HTTPProxy.cpp:273 libi2pd_client/HTTPProxy.cpp:288
#: libi2pd_client/HTTPProxy.cpp:365 #: libi2pd_client/HTTPProxy.cpp:322 libi2pd_client/HTTPProxy.cpp:365
msgid "Invalid request" msgid "Invalid request"
msgstr "" msgstr ""
@ -613,16 +621,12 @@ msgstr ""
msgid "added to router's addressbook from helper" msgid "added to router's addressbook from helper"
msgstr "" msgstr ""
#: libi2pd_client/HTTPProxy.cpp:298 libi2pd_client/HTTPProxy.cpp:307 #: libi2pd_client/HTTPProxy.cpp:298
msgid "Click" msgid "Click here to proceed:"
msgstr "" msgstr ""
#: libi2pd_client/HTTPProxy.cpp:298 libi2pd_client/HTTPProxy.cpp:308 #: libi2pd_client/HTTPProxy.cpp:298 libi2pd_client/HTTPProxy.cpp:308
msgid "here" msgid "Continue"
msgstr ""
#: libi2pd_client/HTTPProxy.cpp:298
msgid "to proceed"
msgstr "" msgstr ""
#: libi2pd_client/HTTPProxy.cpp:299 libi2pd_client/HTTPProxy.cpp:309 #: libi2pd_client/HTTPProxy.cpp:299 libi2pd_client/HTTPProxy.cpp:309
@ -633,12 +637,8 @@ msgstr ""
msgid "already in router's addressbook" msgid "already in router's addressbook"
msgstr "" msgstr ""
#: libi2pd_client/HTTPProxy.cpp:308 #: libi2pd_client/HTTPProxy.cpp:307
msgid "to update record" msgid "Click here to update record:"
msgstr ""
#: libi2pd_client/HTTPProxy.cpp:322
msgid "Invalid Request"
msgstr "" msgstr ""
#: libi2pd_client/HTTPProxy.cpp:322 #: libi2pd_client/HTTPProxy.cpp:322

29
contrib/i18n/README.md Normal file
View File

@ -0,0 +1,29 @@
`xgettext` command for extracting translation
---
```
xgettext --omit-header -ctr: -ktr -ktr:1,2 daemon/HTTPServer.cpp libi2pd_client/HTTPProxy.cpp
```
Regex for transforming gettext translations to our format:
---
```
in: msgid\ \"(.*)\"\nmsgid_plural\ \"(.*)\"\nmsgstr\[0\]\ \"(.*)\"\nmsgstr\[1\]\ \"(.*)\"\n(msgstr\[2\]\ \"(.*)\"\n)?(msgstr\[3\]\ \"(.*)\"\n)?(msgstr\[4\]\ \"(.*)\"\n)?(msgstr\[5\]\ \"(.*)\"\n)?
out: #{"$2", {"$3", "$4", "$6", "$8", "$10"}},\n
```
```
in: msgid\ \"(.*)\"\nmsgstr\ \"(.*)\"\n
out: {"$1", "$2"},\n
```
```
in: ^#[:.](.*)$\n
out: <to empty line>
```
```
in: \n\n
out: \n
```

View File

@ -1,7 +0,0 @@
Regex for transforming gettext translations to our format
msgid\ \"(.*)\"\nmsgid_plural\ \"(.*)\"\nmsgstr\[0\]\ \"(.*)\"\nmsgstr\[1\]\ \"(.*)\"\n(msgstr\[2\]\ \"(.*)\"\n)?(msgstr\[3\]\ \"(.*)\"\n)?(msgstr\[4\]\ \"(.*)\"\n)?(msgstr\[5\]\ \"(.*)\"\n)?
#{"$2", {"$3", "$4", "$6", "$8", "$10"}},\n
msgid\ \"(.*)\"\nmsgstr\ \"(.*)\"\n
{"$1", "$2"},\n

View File

@ -15,6 +15,10 @@
## Default: ~/.i2pd/tunnels.d or /var/lib/i2pd/tunnels.d ## Default: ~/.i2pd/tunnels.d or /var/lib/i2pd/tunnels.d
# tunnelsdir = /var/lib/i2pd/tunnels.d # tunnelsdir = /var/lib/i2pd/tunnels.d
## Path to certificates used for verifying .su3, families
## Default: ~/.i2pd/certificates or /var/lib/i2pd/certificates
# certsdir = /var/lib/i2pd/certificates
## Where to write pidfile (default: i2pd.pid, not used in Windows) ## Where to write pidfile (default: i2pd.pid, not used in Windows)
# pidfile = /run/i2pd.pid # pidfile = /run/i2pd.pid
@ -104,7 +108,7 @@ port = 7070
# user = i2pd # user = i2pd
# pass = changeme # pass = changeme
## Select webconsole language ## Select webconsole language
## Currently supported english (default), russian, turkmen and ukrainian languages ## Currently supported english (default), afrikaans, russian, turkmen, ukrainian and uzbek languages
# lang = english # lang = english
[httpproxy] [httpproxy]

View File

@ -1,7 +1,7 @@
%define git_hash %(git rev-parse HEAD | cut -c -7) %define git_hash %(git rev-parse HEAD | cut -c -7)
Name: i2pd-git Name: i2pd-git
Version: 2.38.0 Version: 2.39.0
Release: git%{git_hash}%{?dist} Release: git%{git_hash}%{?dist}
Summary: I2P router written in C++ Summary: I2P router written in C++
Conflicts: i2pd Conflicts: i2pd
@ -56,9 +56,14 @@ cd build
%endif %endif
%endif %endif
%if 0%{?fedora} >= 35
pushd redhat-linux-build
%else
%if 0%{?fedora} >= 33 %if 0%{?fedora} >= 33
pushd %{_target_platform} pushd %{_target_platform}
%endif %endif
%endif
%if 0%{?mageia} > 7 %if 0%{?mageia} > 7
pushd build pushd build
@ -77,9 +82,13 @@ popd
%install %install
pushd build pushd build
%if 0%{?fedora} >= 35
pushd redhat-linux-build
%else
%if 0%{?fedora} >= 33 %if 0%{?fedora} >= 33
pushd %{_target_platform} pushd %{_target_platform}
%endif %endif
%endif
%if 0%{?mageia} %if 0%{?mageia}
pushd build pushd build
@ -137,6 +146,13 @@ getent passwd i2pd >/dev/null || \
%changelog %changelog
* Mon Aug 24 2021 r4sas <r4sas@i2pmail.org> - 2.39.0-2
- changed if statements to cover fedora 35
* Mon Aug 23 2021 orignal <i2porignal@yandex.ru> - 2.39.0
- update to 2.39.0
- fixed build on fedora 36
* Mon May 17 2021 orignal <i2porignal@yandex.ru> - 2.38.0 * Mon May 17 2021 orignal <i2porignal@yandex.ru> - 2.38.0
- update to 2.38.0 - update to 2.38.0

View File

@ -1,6 +1,6 @@
Name: i2pd Name: i2pd
Version: 2.38.0 Version: 2.39.0
Release: 1%{?dist} Release: 2%{?dist}
Summary: I2P router written in C++ Summary: I2P router written in C++
Conflicts: i2pd-git Conflicts: i2pd-git
@ -54,9 +54,13 @@ cd build
%endif %endif
%endif %endif
%if 0%{?fedora} >= 35
pushd redhat-linux-build
%else
%if 0%{?fedora} >= 33 %if 0%{?fedora} >= 33
pushd %{_target_platform} pushd %{_target_platform}
%endif %endif
%endif
%if 0%{?mageia} > 7 %if 0%{?mageia} > 7
pushd build pushd build
@ -75,9 +79,13 @@ popd
%install %install
pushd build pushd build
%if 0%{?fedora} >= 35
pushd redhat-linux-build
%else
%if 0%{?fedora} >= 33 %if 0%{?fedora} >= 33
pushd %{_target_platform} pushd %{_target_platform}
%endif %endif
%endif
%if 0%{?mageia} %if 0%{?mageia}
pushd build pushd build
@ -135,6 +143,13 @@ getent passwd i2pd >/dev/null || \
%changelog %changelog
* Mon Aug 24 2021 r4sas <r4sas@i2pmail.org> - 2.39.0-2
- changed if statements to cover fedora 35
* Mon Aug 23 2021 orignal <i2porignal@yandex.ru> - 2.39.0
- update to 2.39.0
- fixed build on fedora 36
* Mon May 17 2021 orignal <i2porignal@yandex.ru> - 2.38.0 * Mon May 17 2021 orignal <i2porignal@yandex.ru> - 2.38.0
- update to 2.38.0 - update to 2.38.0

View File

@ -1,16 +1,16 @@
[IRC-IRC2P] [IRC-ILITA]
type = client type = client
address = 127.0.0.1 address = 127.0.0.1
port = 6668 port = 6668
destination = irc.postman.i2p destination = irc.ilita.i2p
destinationport = 6667 destinationport = 6667
keys = irc-keys.dat keys = irc-keys.dat
#[IRC-ILITA] #[IRC-IRC2P]
#type = client #type = client
#address = 127.0.0.1 #address = 127.0.0.1
#port = 6669 #port = 6669
#destination = irc.ilita.i2p #destination = irc.postman.i2p
#destinationport = 6667 #destinationport = 6667
#keys = irc-keys.dat #keys = irc-keys.dat

View File

@ -0,0 +1,243 @@
body {
font: 100%/1.5em sans-serif;
margin: 0;
padding: 1.5em;
background: #FAFAFA;
color: #103456;
}
a, .slide label {
text-decoration: none;
color: #894C84;
}
a:hover, .slide label:hover {
color: #FAFAFA;
background: #894C84;
}
a.button {
-webkit-appearance: button;
-moz-appearance: button;
appearance: button;
text-decoration: none;
padding: 0 5px;
border: 1px solid #894C84;
}
.header {
font-size: 2.5em;
text-align: center;
margin: 1em 0;
color: #894C84;
}
.wrapper {
margin: 0 auto;
padding: 1em;
max-width: 64em;
}
.menu {
display: block;
float: left;
overflow: hidden;
max-width: 12em;
white-space: nowrap;
text-overflow: ellipsis;
}
.listitem {
display: block;
font-family: monospace;
font-size: 1.2em;
white-space: nowrap;
}
.tableitem {
font-family: monospace;
font-size: 1.2em;
white-space: nowrap;
}
.content {
float: left;
font-size: 1em;
margin-left: 4em;
max-width: 48em;
overflow: auto;
}
.tunnel.established {
color: #56B734;
}
.tunnel.expiring {
color: #D3AE3F;
}
.tunnel.failed {
color: #D33F3F;
}
.tunnel.building {
color: #434343;
}
caption {
font-size: 1.5em;
text-align: center;
color: #894C84;
}
table {
display: table;
border-collapse: collapse;
text-align: center;
}
table.extaddr {
text-align: left;
}
table.services {
width: 100%;
}
textarea {
word-break: break-all;
}
.streamdest {
width: 120px;
max-width: 240px;
overflow: hidden;
text-overflow: ellipsis;
}
.slide div.slidecontent, .slide [type="checkbox"] {
display: none;
}
.slide [type="checkbox"]:checked ~ div.slidecontent {
display: block;
margin-top: 0;
padding: 0;
}
.disabled {
color: #D33F3F;
}
.enabled {
color: #56B734;
}
@media screen and (max-width: 1150px) { /* adaptive style */
.wrapper {
max-width: 58em;
}
.menu {
max-width: 10em;
}
.content {
margin-left: 2em;
max-width: 42em;
}
}
@media screen and (max-width: 980px) {
body {
padding: 1.5em 0 0 0;
}
.menu {
width: 100%;
max-width: unset;
display: block;
float: none;
position: unset;
font-size: 16px;
text-align: center;
}
.menu a, .commands a {
display: inline-block;
padding: 4px;
}
.content {
float: none;
margin-left: unset;
margin-top: 16px;
max-width: 100%;
width: 100%;
text-align: center;
}
a, .slide label {
/* margin-right: 10px; */
display: block;
/* font-size: 18px; */
}
.header {
margin: unset;
font-size: 1.5em;
}
small {
display: block
}
a.button {
-webkit-appearance: button;
-moz-appearance: button;
appearance: button;
text-decoration: none;
margin-top: 10px;
padding: 6px;
border: 1px solid #894c84;
width: -webkit-fill-available;
}
input, select {
width: 35%;
text-align: center;
padding: 5px;
border: 2px solid #ccc;
-webkit-border-radius: 5px;
border-radius: 5px;
font-size: 18px;
}
table.extaddr {
margin: auto;
text-align: unset;
}
textarea {
width: -webkit-fill-available;
height: auto;
padding:5px;
border:2px solid #ccc;
-webkit-border-radius: 5px;
border-radius: 5px;
font-size: 12px;
}
button[type=submit] {
padding: 5px 15px;
background: #ccc;
border: 0 none;
cursor: pointer;
-webkit-border-radius: 5px;
border-radius: 5px;
position: relative;
height: 36px;
display: -webkit-inline-box;
margin-top: 10px;
}
}

View File

@ -94,6 +94,11 @@ namespace util
i2p::config::GetOption("daemon", isDaemon); i2p::config::GetOption("daemon", isDaemon);
std::string certsdir; i2p::config::GetOption("certsdir", certsdir);
i2p::fs::SetCertsDir(certsdir);
certsdir = i2p::fs::GetCertsDir();
std::string logs = ""; i2p::config::GetOption("log", logs); std::string logs = ""; i2p::config::GetOption("log", logs);
std::string logfile = ""; i2p::config::GetOption("logfile", logfile); std::string logfile = ""; i2p::config::GetOption("logfile", logfile);
std::string loglevel = ""; i2p::config::GetOption("loglevel", loglevel); std::string loglevel = ""; i2p::config::GetOption("loglevel", loglevel);
@ -132,6 +137,7 @@ namespace util
LogPrint(eLogNone, "i2pd v", VERSION, " starting"); LogPrint(eLogNone, "i2pd v", VERSION, " starting");
LogPrint(eLogDebug, "FS: main config file: ", config); LogPrint(eLogDebug, "FS: main config file: ", config);
LogPrint(eLogDebug, "FS: data directory: ", datadir); LogPrint(eLogDebug, "FS: data directory: ", datadir);
LogPrint(eLogDebug, "FS: certificates directory: ", certsdir);
bool precomputation; i2p::config::GetOption("precomputation.elgamal", precomputation); bool precomputation; i2p::config::GetOption("precomputation.elgamal", precomputation);
bool aesni; i2p::config::GetOption("cpuext.aesni", aesni); bool aesni; i2p::config::GetOption("cpuext.aesni", aesni);

View File

@ -36,73 +36,31 @@
#include "Win32App.h" #include "Win32App.h"
#endif #endif
// For image and info // For image, style and info
#include "version.h" #include "version.h"
#include "HTTPServerResources.h"
namespace i2p { namespace i2p {
namespace http { namespace http {
const std::string itoopieFavicon = static void LoadExtCSS ()
"data:image/png;base64," {
"iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACx" std::stringstream s;
"jwv8YQUAAAAJcEhZcwAALiIAAC4iAari3ZIAAAAHdElNRQfgCQsUNSZrkhi1AAAAGXRFWHRTb2Z0" std::string styleFile = i2p::fs::DataDirPath ("webconsole/style.css");
"d2FyZQBwYWludC5uZXQgNC4wLjEyQwRr7AAAAoJJREFUOE9jwAUqi4Q1oEwwcDTV1+5sETaBclGB" if (i2p::fs::Exists(styleFile)) {
"vb09C5QJB6kWpvFQJoOCeLC5kmjEHCgXE2SlyETLi3h6QrkM4VL+ssWSCZUgtopITLKqaOotRTEn" std::ifstream f(styleFile, std::ifstream::binary);
"cbAkLqAkGtOqLBLVAWLXyWSVFkkmRiqLxuaqiWb/VBYJMAYrwgckJY25VEUzniqKhjU2y+RtCRSP" s << f.rdbuf();
"6lUXy/1jIBV5tlYxZUaFVMq2NInwIi9hO8fSfOEAqDZUoCwal6MulvOvyS7gi69K4j9zxZT/m0ps" externalCSS = s.str();
"/28ptvvvquXXryIa7QYMMdTwqi0WNtVi0GIDseXl7TnUxFKfnGlxAGp0+D8j2eH/8Ub7/9e7nf7X" } else if (externalCSS.length() != 0) { // clean up external style if file was removed
"+Af/B7rwt6pI0h0l0WhQADOC9DBkhSirpImHNVZKp24ukkyoshGLnN8d5fA/y13t/44Kq/8hlnL/" externalCSS = "";
"z7fZ/58f6vcxSNpbVUVFhV1RLNBVTsQzVYZPSwhsCAhkiIfpNMrkbO6TLf071Sfk/5ZSi/+7q6z/" }
"P5ns+v9mj/P/CpuI/20y+aeNGYxZoVoYGmsF3aFMBAAZlCwftnF9ke3//bU2//fXWP8/UGv731Am" }
"+V+DdNblSqnUYqhSTKAiYSOqJBrVqiaa+S3UNPr/gmyH/xuKXf63hnn/B8bIP0UxHfEyyeSNQKVM"
"EB1AEB2twhcTLp+gIBJUoyKasEpVJHmqskh8qryovUG/ffCHHRU2q/Tk/YuB6eGPsbExa7ZkpLu1"
"oLEcVDtuUCgV1w60rQzElpRUE1EVSX0BYidHiInXF4nagNhYQW60EF+ApH1ktni0A1SIITSUgVlZ"
"JHYnlIsfzJjIp9xZKswL5YKBHL+coKJoRDaUSzoozxHVrygQU4JykQADAwAT5b1NHtwZugAAAABJ"
"RU5ErkJggg==";
static void GetStyles (std::stringstream& s) static void GetStyles (std::stringstream& s)
{ {
s << "<style>\r\n" if (externalCSS.length() != 0)
<< " body { font: 100%/1.5em sans-serif; margin: 0; padding: 1.5em; background: #FAFAFA; color: #103456; }\r\n" s << "<style>\r\n" << externalCSS << "</style>\r\n";
<< " a, .slide label { text-decoration: none; color: #894C84; }\r\n" else
<< " a:hover, .slide label:hover { color: #FAFAFA; background: #894C84; }\r\n" s << internalCSS;
<< " a.button { -webkit-appearance: button; -moz-appearance: button; appearance: button; text-decoration: none;\r\n"
<< " color: initial; padding: 0 5px; border: 1px solid #894C84; }\r\n"
<< " .header { font-size: 2.5em; text-align: center; margin: 1em 0; color: #894C84; }\r\n"
<< " .wrapper { margin: 0 auto; padding: 1em; max-width: 58em; }\r\n"
<< " .menu { float: left; } .menu a, .commands a { display: block; }\r\n"
<< " .listitem { display: block; font-family: monospace; font-size: 1.2em; white-space: nowrap; }\r\n"
<< " .tableitem { font-family: monospace; font-size: 1.2em; white-space: nowrap; }\r\n"
<< " .content { float: left; font-size: 1em; margin-left: 4em; max-width: 45em; overflow: auto; }\r\n"
<< " .tunnel.established { color: #56B734; } .tunnel.expiring { color: #D3AE3F; }\r\n"
<< " .tunnel.failed { color: #D33F3F; } .tunnel.building { color: #434343; }\r\n"
<< " caption { font-size: 1.5em; text-align: center; color: #894C84; }\r\n"
<< " table { display: table; border-collapse: collapse; text-align: center; }\r\n"
<< " table.extaddr { text-align: left; } table.services { width: 100%; }\r\n"
<< " textarea { word-break: break-all; }\r\n"
<< " .streamdest { width: 120px; max-width: 240px; overflow: hidden; text-overflow: ellipsis;}\r\n"
<< " .slide div.slidecontent, .slide [type=\"checkbox\"] { display: none; }\r\n"
<< " .slide [type=\"checkbox\"]:checked ~ div.slidecontent { display: block; margin-top: 0; padding: 0; }\r\n"
<< " .disabled:after { color: #D33F3F; content: \"" << tr("Disabled") << "\" }\r\n"
<< " .enabled:after { color: #56B734; content: \"" << tr("Enabled") << "\" }\r\n"
<< " @media screen and (max-width: 980px) {\r\n" /* adaptive style */
<< " body { padding: 1.5em 0 0 0; }\r\n"
<< " .menu { width: 100%; display: block; float: none; position: unset; font-size: 16px;\r\n"
<< " text-align: center; }\r\n"
<< " .menu a, .commands a { padding: 2px; }\r\n"
<< " .content { float: none; margin-left: unset; margin-top: 16px; max-width: 100%; width: 100%;\r\n"
<< " text-align: center; }\r\n"
<< " a, .slide label { /* margin-right: 10px; */ display: block; /* font-size: 18px; */ }\r\n"
<< " .header { margin: unset; font-size: 1.5em; } small {display: block}\r\n"
<< " a.button { -webkit-appearance: button; -moz-appearance: button; appearance: button; text-decoration: none;\r\n"
<< " color: initial; margin-top: 10px; padding: 6px; border: 1px solid #894c84; width: -webkit-fill-available; }\r\n"
<< " input { width: 35%; text-align: center; padding: 5px;\r\n"
<< " border: 2px solid #ccc; -webkit-border-radius: 5px; border-radius: 5px; font-size: 18px; }\r\n"
<< " textarea { width: -webkit-fill-available; height: auto; padding:5px; border:2px solid #ccc;\r\n"
<< " -webkit-border-radius: 5px; border-radius: 5px; font-size: 12px; }\r\n"
<< " button[type=submit] { padding: 5px 15px; background: #ccc; border: 0 none; cursor: pointer;\r\n"
<< " -webkit-border-radius: 5px; border-radius: 5px; position: relative; height: 36px; display: -webkit-inline-box; margin-top: 10px; }\r\n"
<< " }\r\n" /* adaptive style */
<< "</style>\r\n";
} }
const char HTTP_PAGE_TUNNELS[] = "tunnels"; const char HTTP_PAGE_TUNNELS[] = "tunnels";
@ -127,11 +85,20 @@ namespace http {
const char HTTP_COMMAND_KILLSTREAM[] = "closestream"; const char HTTP_COMMAND_KILLSTREAM[] = "closestream";
const char HTTP_COMMAND_LIMITTRANSIT[] = "limittransit"; const char HTTP_COMMAND_LIMITTRANSIT[] = "limittransit";
const char HTTP_COMMAND_GET_REG_STRING[] = "get_reg_string"; const char HTTP_COMMAND_GET_REG_STRING[] = "get_reg_string";
const char HTTP_COMMAND_SETLANGUAGE[] = "setlanguage";
const char HTTP_COMMAND_RELOAD_CSS[] = "reload_css";
const char HTTP_PARAM_SAM_SESSION_ID[] = "id"; const char HTTP_PARAM_SAM_SESSION_ID[] = "id";
const char HTTP_PARAM_ADDRESS[] = "address"; const char HTTP_PARAM_ADDRESS[] = "address";
static std::string ConvertTime (uint64_t time); static std::string ConvertTime (uint64_t time)
std::map<uint32_t, uint32_t> HTTPConnection::m_Tokens; {
lldiv_t divTime = lldiv(time, 1000);
time_t t = divTime.quot;
struct tm *tm = localtime(&t);
char date[128];
snprintf(date, sizeof(date), "%02d/%02d/%d %02d:%02d:%02d.%03lld", tm->tm_mday, tm->tm_mon + 1, tm->tm_year + 1900, tm->tm_hour, tm->tm_min, tm->tm_sec, divTime.rem);
return date;
}
static void ShowUptime (std::stringstream& s, int seconds) static void ShowUptime (std::stringstream& s, int seconds)
{ {
@ -157,11 +124,11 @@ namespace http {
s << std::fixed << std::setprecision(2); s << std::fixed << std::setprecision(2);
auto numKBytes = (double) bytes / 1024; auto numKBytes = (double) bytes / 1024;
if (numKBytes < 1024) if (numKBytes < 1024)
s << numKBytes << " " << tr("KiB"); s << numKBytes << " " << tr(/* tr: Kibibit */ "KiB");
else if (numKBytes < 1024 * 1024) else if (numKBytes < 1024 * 1024)
s << numKBytes / 1024 << " " << tr("MiB"); s << numKBytes / 1024 << " " << tr(/* tr: Mebibit */ "MiB");
else else
s << numKBytes / 1024 / 1024 << " " << tr("GiB"); s << numKBytes / 1024 / 1024 << " " << tr(/* tr: Gibibit */ "GiB");
} }
static void ShowTunnelDetails (std::stringstream& s, enum i2p::tunnel::TunnelState eState, bool explr, int bytes) static void ShowTunnelDetails (std::stringstream& s, enum i2p::tunnel::TunnelState eState, bool explr, int bytes)
@ -185,7 +152,7 @@ namespace http {
else stateText = tr("unknown"); else stateText = tr("unknown");
s << "<span class=\"tunnel " << state << "\"> " << stateText << ((explr) ? " (" + tr("exploratory") + ")" : "") << "</span>, "; s << "<span class=\"tunnel " << state << "\"> " << stateText << ((explr) ? " (" + tr("exploratory") + ")" : "") << "</span>, ";
s << " " << (int) (bytes / 1024) << "&nbsp;" << tr("KiB") << "\r\n"; s << " " << (int) (bytes / 1024) << "&nbsp;" << tr(/* tr: Kibibit */ "KiB") << "\r\n";
} }
static void SetLogLevel (const std::string& level) static void SetLogLevel (const std::string& level)
@ -204,9 +171,9 @@ namespace http {
std::string webroot; i2p::config::GetOption("http.webroot", webroot); std::string webroot; i2p::config::GetOption("http.webroot", webroot);
// Page language // Page language
std::string lang, langCode; i2p::config::GetOption("http.lang", lang); std::string currLang = i2p::client::context.GetLanguage ()->GetLanguage(); // get current used language
if (lang == "russian") langCode = "ru"; auto it = i2p::i18n::languages.find(currLang);
else langCode = "en"; std::string langCode = it->second.ShortCode;
s << s <<
"<!DOCTYPE html>\r\n" "<!DOCTYPE html>\r\n"
@ -223,18 +190,18 @@ namespace http {
"<div class=\"header\">" << tr("<b>i2pd</b> webconsole") << "</div>\r\n" "<div class=\"header\">" << tr("<b>i2pd</b> webconsole") << "</div>\r\n"
"<div class=\"wrapper\">\r\n" "<div class=\"wrapper\">\r\n"
"<div class=\"menu\">\r\n" "<div class=\"menu\">\r\n"
" <a href=\"" << webroot << "\">" << tr("Main page") << "</a><br>\r\n" " <a href=\"" << webroot << "\">" << tr("Main page") << "</a><br><br>\r\n"
" <a href=\"" << webroot << "?page=" << HTTP_PAGE_COMMANDS << "\">" << tr("Router commands") << "</a>\r\n" " <a href=\"" << webroot << "?page=" << HTTP_PAGE_COMMANDS << "\">" << tr("Router commands") << "</a><br>\r\n"
" <a href=\"" << webroot << "?page=" << HTTP_PAGE_LOCAL_DESTINATIONS << "\">" << tr("Local destinations") << "</a>\r\n"; " <a href=\"" << webroot << "?page=" << HTTP_PAGE_LOCAL_DESTINATIONS << "\">" << tr("Local Destinations") << "</a><br>\r\n";
if (i2p::context.IsFloodfill ()) if (i2p::context.IsFloodfill ())
s << " <a href=\"" << webroot << "?page=" << HTTP_PAGE_LEASESETS << "\">" << tr("LeaseSets") << "</a>\r\n"; s << " <a href=\"" << webroot << "?page=" << HTTP_PAGE_LEASESETS << "\">" << tr("LeaseSets") << "</a><br>\r\n";
s << s <<
" <a href=\"" << webroot << "?page=" << HTTP_PAGE_TUNNELS << "\">" << tr("Tunnels") << "</a>\r\n" " <a href=\"" << webroot << "?page=" << HTTP_PAGE_TUNNELS << "\">" << tr("Tunnels") << "</a><br>\r\n"
" <a href=\"" << webroot << "?page=" << HTTP_PAGE_TRANSIT_TUNNELS << "\">" << tr("Transit tunnels") << "</a>\r\n" " <a href=\"" << webroot << "?page=" << HTTP_PAGE_TRANSIT_TUNNELS << "\">" << tr("Transit Tunnels") << "</a><br>\r\n"
" <a href=\"" << webroot << "?page=" << HTTP_PAGE_TRANSPORTS << "\">" << tr ("Transports") << "</a>\r\n" " <a href=\"" << webroot << "?page=" << HTTP_PAGE_TRANSPORTS << "\">" << tr ("Transports") << "</a><br>\r\n"
" <a href=\"" << webroot << "?page=" << HTTP_PAGE_I2P_TUNNELS << "\">" << tr("I2P tunnels") << "</a>\r\n"; " <a href=\"" << webroot << "?page=" << HTTP_PAGE_I2P_TUNNELS << "\">" << tr("I2P tunnels") << "</a><br>\r\n";
if (i2p::client::context.GetSAMBridge ()) if (i2p::client::context.GetSAMBridge ())
s << " <a href=\"" << webroot << "?page=" << HTTP_PAGE_SAM_SESSIONS << "\">" << tr("SAM sessions") << "</a>\r\n"; s << " <a href=\"" << webroot << "?page=" << HTTP_PAGE_SAM_SESSIONS << "\">" << tr("SAM sessions") << "</a><br>\r\n";
s << s <<
"</div>\r\n" "</div>\r\n"
"<div class=\"content\">"; "<div class=\"content\">";
@ -319,13 +286,13 @@ namespace http {
s << "<b>" << tr("Tunnel creation success rate") << ":</b> " << i2p::tunnel::tunnels.GetTunnelCreationSuccessRate () << "%<br>\r\n"; s << "<b>" << tr("Tunnel creation success rate") << ":</b> " << i2p::tunnel::tunnels.GetTunnelCreationSuccessRate () << "%<br>\r\n";
s << "<b>" << tr("Received") << ":</b> "; s << "<b>" << tr("Received") << ":</b> ";
ShowTraffic (s, i2p::transport::transports.GetTotalReceivedBytes ()); ShowTraffic (s, i2p::transport::transports.GetTotalReceivedBytes ());
s << " (" << (double) i2p::transport::transports.GetInBandwidth () / 1024 << " " << tr("KiB/s") << ")<br>\r\n"; s << " (" << (double) i2p::transport::transports.GetInBandwidth () / 1024 << " " << tr(/* tr: Kibibit/s */ "KiB/s") << ")<br>\r\n";
s << "<b>" << tr("Sent") << ":</b> "; s << "<b>" << tr("Sent") << ":</b> ";
ShowTraffic (s, i2p::transport::transports.GetTotalSentBytes ()); ShowTraffic (s, i2p::transport::transports.GetTotalSentBytes ());
s << " (" << (double) i2p::transport::transports.GetOutBandwidth () / 1024 << " " << tr("KiB/s") << ")<br>\r\n"; s << " (" << (double) i2p::transport::transports.GetOutBandwidth () / 1024 << " " << tr(/* tr: Kibibit/s */ "KiB/s") << ")<br>\r\n";
s << "<b>" << tr("Transit") << ":</b> "; s << "<b>" << tr("Transit") << ":</b> ";
ShowTraffic (s, i2p::transport::transports.GetTotalTransitTransmittedBytes ()); ShowTraffic (s, i2p::transport::transports.GetTotalTransitTransmittedBytes ());
s << " (" << (double) i2p::transport::transports.GetTransitBandwidth () / 1024 << " " << tr("KiB/s") << ")<br>\r\n"; s << " (" << (double) i2p::transport::transports.GetTransitBandwidth () / 1024 << " " << tr(/* tr: Kibibit/s */ "KiB/s") << ")<br>\r\n";
s << "<b>" << tr("Data path") << ":</b> " << i2p::fs::GetUTF8DataDir() << "<br>\r\n"; s << "<b>" << tr("Data path") << ":</b> " << i2p::fs::GetUTF8DataDir() << "<br>\r\n";
s << "<div class='slide'>"; s << "<div class='slide'>";
if((outputFormat == OutputFormatEnum::forWebConsole) || !includeHiddenContent) { if((outputFormat == OutputFormatEnum::forWebConsole) || !includeHiddenContent) {
@ -389,14 +356,19 @@ namespace http {
s << "<b>" << tr("Transit Tunnels") << ":</b> " << std::to_string(transitTunnelCount) << "<br>\r\n<br>\r\n"; s << "<b>" << tr("Transit Tunnels") << ":</b> " << std::to_string(transitTunnelCount) << "<br>\r\n<br>\r\n";
if(outputFormat==OutputFormatEnum::forWebConsole) { if(outputFormat==OutputFormatEnum::forWebConsole) {
bool httpproxy = i2p::client::context.GetHttpProxy () ? true : false;
bool socksproxy = i2p::client::context.GetSocksProxy () ? true : false;
bool bob = i2p::client::context.GetBOBCommandChannel () ? true : false;
bool sam = i2p::client::context.GetSAMBridge () ? true : false;
bool i2cp = i2p::client::context.GetI2CPServer () ? true : false;
bool i2pcontrol; i2p::config::GetOption("i2pcontrol.enabled", i2pcontrol); bool i2pcontrol; i2p::config::GetOption("i2pcontrol.enabled", i2pcontrol);
s << "<table class=\"services\"><caption>" << tr("Services") << "</caption><tbody>\r\n"; s << "<table class=\"services\"><caption>" << tr("Services") << "</caption><tbody>\r\n";
s << "<tr><td>" << "HTTP " << tr("Proxy") << "</td><td><div class='" << ((i2p::client::context.GetHttpProxy ()) ? "enabled" : "disabled") << "'></div></td></tr>\r\n"; s << "<tr><td>" << "HTTP " << tr("Proxy") << "</td><td class='" << (httpproxy ? "enabled" : "disabled") << "'>" << (httpproxy ? tr("Enabled") : tr("Disabled")) << "</td></tr>\r\n";
s << "<tr><td>" << "SOCKS " << tr("Proxy") << "</td><td><div class='" << ((i2p::client::context.GetSocksProxy ()) ? "enabled" : "disabled") << "'></div></td></tr>\r\n"; s << "<tr><td>" << "SOCKS " << tr("Proxy") << "</td><td class='" << (socksproxy ? "enabled" : "disabled") << "'>" << (socksproxy ? tr("Enabled") : tr("Disabled")) << "</td></tr>\r\n";
s << "<tr><td>" << "BOB" << "</td><td><div class='" << ((i2p::client::context.GetBOBCommandChannel ()) ? "enabled" : "disabled") << "'></div></td></tr>\r\n"; s << "<tr><td>" << "BOB" << "</td><td class='" << (bob ? "enabled" : "disabled") << "'>" << (bob ? tr("Enabled") : tr("Disabled")) << "</td></tr>\r\n";
s << "<tr><td>" << "SAM" << "</td><td><div class='" << ((i2p::client::context.GetSAMBridge ()) ? "enabled" : "disabled") << "'></div></td></tr>\r\n"; s << "<tr><td>" << "SAM" << "</td><td class='" << (sam ? "enabled" : "disabled") << "'>" << (sam ? tr("Enabled") : tr("Disabled")) << "</td></tr>\r\n";
s << "<tr><td>" << "I2CP" << "</td><td><div class='" << ((i2p::client::context.GetI2CPServer ()) ? "enabled" : "disabled") << "'></div></td></tr>\r\n"; s << "<tr><td>" << "I2CP" << "</td><td class='" << (i2cp ? "enabled" : "disabled") << "'>" << (i2cp ? tr("Enabled") : tr("Disabled")) << "</td></tr>\r\n";
s << "<tr><td>" << "I2PControl" << "</td><td><div class='" << ((i2pcontrol) ? "enabled" : "disabled") << "'></div></td></tr>\r\n"; s << "<tr><td>" << "I2PControl" << "</td><td class='" << (i2pcontrol ? "enabled" : "disabled") << "'>" << (i2pcontrol ? tr("Enabled") : tr("Disabled")) << "</td></tr>\r\n";
s << "</tbody></table>\r\n"; s << "</tbody></table>\r\n";
} }
} }
@ -476,7 +448,7 @@ namespace http {
s << "<div class=\"listitem\">"; s << "<div class=\"listitem\">";
it->Print(s); it->Print(s);
if(it->LatencyIsKnown()) if(it->LatencyIsKnown())
s << " ( " << it->GetMeanLatency() << tr("ms") << " )"; s << " ( " << it->GetMeanLatency() << tr(/* tr: Milliseconds */ "ms") << " )";
ShowTunnelDetails(s, it->GetState (), false, it->GetNumReceivedBytes ()); ShowTunnelDetails(s, it->GetState (), false, it->GetNumReceivedBytes ());
s << "</div>\r\n"; s << "</div>\r\n";
} }
@ -534,7 +506,8 @@ namespace http {
if (dest) if (dest)
{ {
ShowLeaseSetDestination (s, dest, token); ShowLeaseSetDestination (s, dest, token);
// show streams
// Print table with streams information
s << "<table>\r\n<caption>" << tr("Streams") << "</caption>\r\n<thead>\r\n<tr>"; s << "<table>\r\n<caption>" << tr("Streams") << "</caption>\r\n<thead>\r\n<tr>";
s << "<th style=\"width:25px;\">StreamID</th>"; s << "<th style=\"width:25px;\">StreamID</th>";
s << "<th style=\"width:5px;\" \\>"; // Stream closing button column s << "<th style=\"width:5px;\" \\>"; // Stream closing button column
@ -679,26 +652,31 @@ namespace http {
static void ShowCommands (std::stringstream& s, uint32_t token) static void ShowCommands (std::stringstream& s, uint32_t token)
{ {
std::string webroot; i2p::config::GetOption("http.webroot", webroot); std::string webroot; i2p::config::GetOption("http.webroot", webroot);
/* commands */
s << "<b>" << tr("Router commands") << "</b><br>\r\n<br>\r\n<div class=\"commands\">\r\n"; s << "<b>" << tr("Router commands") << "</b><br>\r\n<br>\r\n<div class=\"commands\">\r\n";
s << " <a href=\"" << webroot << "?cmd=" << HTTP_COMMAND_RUN_PEER_TEST << "&token=" << token << "\">" << tr("Run peer test") << "</a>\r\n"; s << " <a href=\"" << webroot << "?cmd=" << HTTP_COMMAND_RUN_PEER_TEST << "&token=" << token << "\">" << tr("Run peer test") << "</a><br>\r\n";
// s << " <a href=\"/?cmd=" << HTTP_COMMAND_RELOAD_CONFIG << "\">Reload config</a><br>\r\n"; // s << " <a href=\"/?cmd=" << HTTP_COMMAND_RELOAD_CONFIG << "\">Reload config</a><br>\r\n";
if (i2p::context.AcceptsTunnels ()) if (i2p::context.AcceptsTunnels ())
s << " <a href=\"" << webroot << "?cmd=" << HTTP_COMMAND_DISABLE_TRANSIT << "&token=" << token << "\">" << tr("Decline transit tunnels") << "</a>\r\n"; s << " <a href=\"" << webroot << "?cmd=" << HTTP_COMMAND_DISABLE_TRANSIT << "&token=" << token << "\">" << tr("Decline transit tunnels") << "</a><br>\r\n";
else else
s << " <a href=\"" << webroot << "?cmd=" << HTTP_COMMAND_ENABLE_TRANSIT << "&token=" << token << "\">" << tr("Accept transit tunnels") << "</a>\r\n"; s << " <a href=\"" << webroot << "?cmd=" << HTTP_COMMAND_ENABLE_TRANSIT << "&token=" << token << "\">" << tr("Accept transit tunnels") << "</a><br>\r\n";
#if ((!defined(WIN32) && !defined(QT_GUI_LIB) && !defined(ANDROID)) || defined(ANDROID_BINARY)) #if ((!defined(WIN32) && !defined(QT_GUI_LIB) && !defined(ANDROID)) || defined(ANDROID_BINARY))
if (Daemon.gracefulShutdownInterval) if (Daemon.gracefulShutdownInterval)
s << " <a href=\"" << webroot << "?cmd=" << HTTP_COMMAND_SHUTDOWN_CANCEL << "&token=" << token << "\">" << tr("Cancel graceful shutdown") << "</a>\r\n"; s << " <a href=\"" << webroot << "?cmd=" << HTTP_COMMAND_SHUTDOWN_CANCEL << "&token=" << token << "\">" << tr("Cancel graceful shutdown") << "</a><br>\r\n";
else else
s << " <a href=\"" << webroot << "?cmd=" << HTTP_COMMAND_SHUTDOWN_START << "&token=" << token << "\">" << tr("Start graceful shutdown") << "</a>\r\n"; s << " <a href=\"" << webroot << "?cmd=" << HTTP_COMMAND_SHUTDOWN_START << "&token=" << token << "\">" << tr("Start graceful shutdown") << "</a><br>\r\n";
#elif defined(WIN32_APP) #elif defined(WIN32_APP)
if (i2p::util::DaemonWin32::Instance().isGraceful) if (i2p::util::DaemonWin32::Instance().isGraceful)
s << " <a href=\"" << webroot << "?cmd=" << HTTP_COMMAND_SHUTDOWN_CANCEL << "&token=" << token << "\">" << tr("Cancel graceful shutdown") << "</a>\r\n"; s << " <a href=\"" << webroot << "?cmd=" << HTTP_COMMAND_SHUTDOWN_CANCEL << "&token=" << token << "\">" << tr("Cancel graceful shutdown") << "</a><br>\r\n";
else else
s << " <a href=\"" << webroot << "?cmd=" << HTTP_COMMAND_SHUTDOWN_START << "&token=" << token << "\">" << tr("Start graceful shutdown") << "</a>\r\n"; s << " <a href=\"" << webroot << "?cmd=" << HTTP_COMMAND_SHUTDOWN_START << "&token=" << token << "\">" << tr("Start graceful shutdown") << "</a><br>\r\n";
#endif #endif
s << " <a href=\"" << webroot << "?cmd=" << HTTP_COMMAND_SHUTDOWN_NOW << "&token=" << token << "\">" << tr("Force shutdown") << "</a>\r\n";
s << " <a href=\"" << webroot << "?cmd=" << HTTP_COMMAND_SHUTDOWN_NOW << "&token=" << token << "\">" << tr("Force shutdown") << "</a><br><br>\r\n";
s << " <a href=\"" << webroot << "?cmd=" << HTTP_COMMAND_RELOAD_CSS << "&token=" << token << "\">" << tr("Reload external CSS styles") << "</a>\r\n";
s << "</div>"; s << "</div>";
s << "<br>\r\n<small>" << tr("<b>Note:</b> any action done here are not persistent and not changes your config files.") << "</small>\r\n<br>\r\n"; s << "<br>\r\n<small>" << tr("<b>Note:</b> any action done here are not persistent and not changes your config files.") << "</small>\r\n<br>\r\n";
@ -718,13 +696,26 @@ namespace http {
s << " <input type=\"number\" min=\"0\" max=\"65535\" name=\"limit\" value=\"" << maxTunnels << "\">\r\n"; s << " <input type=\"number\" min=\"0\" max=\"65535\" name=\"limit\" value=\"" << maxTunnels << "\">\r\n";
s << " <button type=\"submit\">" << tr("Change") << "</button>\r\n"; s << " <button type=\"submit\">" << tr("Change") << "</button>\r\n";
s << "</form>\r\n<br>\r\n"; s << "</form>\r\n<br>\r\n";
std::string currLang = i2p::client::context.GetLanguage ()->GetLanguage(); // get current used language
s << "<b>" << tr("Change language") << "</b><br>\r\n";
s << "<form method=\"get\" action=\"" << webroot << "\">\r\n";
s << " <input type=\"hidden\" name=\"cmd\" value=\"" << HTTP_COMMAND_SETLANGUAGE << "\">\r\n";
s << " <input type=\"hidden\" name=\"token\" value=\"" << token << "\">\r\n";
s << " <select name=\"lang\" id=\"lang\">\r\n";
for (const auto& it: i2p::i18n::languages)
s << " <option value=\"" << it.first << "\"" << ((it.first.compare(currLang) == 0) ? " selected" : "") << ">" << it.second.LocaleName << "</option>\r\n";
s << " </select>\r\n";
s << " <button type=\"submit\">" << tr("Change") << "</button>\r\n";
s << "</form>\r\n<br>\r\n";
} }
void ShowTransitTunnels (std::stringstream& s) void ShowTransitTunnels (std::stringstream& s)
{ {
if(i2p::tunnel::tunnels.CountTransitTunnels()) if(i2p::tunnel::tunnels.CountTransitTunnels())
{ {
s << "<b>" << tr("Transit tunnels") << ":</b><br>\r\n<div class=\"list\">\r\n"; s << "<b>" << tr("Transit Tunnels") << ":</b><br>\r\n<div class=\"list\">\r\n";
for (const auto& it: i2p::tunnel::tunnels.GetTransitTunnels ()) for (const auto& it: i2p::tunnel::tunnels.GetTransitTunnels ())
{ {
s << "<div class=\"listitem\">\r\n"; s << "<div class=\"listitem\">\r\n";
@ -740,7 +731,7 @@ namespace http {
} }
else else
{ {
s << "<b>" << tr("Transit tunnels") << ":</b> " << tr("no transit tunnels currently built") << ".<br>\r\n"; s << "<b>" << tr("Transit Tunnels") << ":</b> " << tr("no transit tunnels currently built") << ".<br>\r\n";
} }
} }
@ -979,16 +970,6 @@ namespace http {
} }
} }
std::string ConvertTime (uint64_t time)
{
lldiv_t divTime = lldiv(time, 1000);
time_t t = divTime.quot;
struct tm *tm = localtime(&t);
char date[128];
snprintf(date, sizeof(date), "%02d/%02d/%d %02d:%02d:%02d.%03lld", tm->tm_mday, tm->tm_mon + 1, tm->tm_year + 1900, tm->tm_hour, tm->tm_min, tm->tm_sec, divTime.rem);
return date;
}
HTTPConnection::HTTPConnection (std::string hostname, std::shared_ptr<boost::asio::ip::tcp::socket> socket): HTTPConnection::HTTPConnection (std::string hostname, std::shared_ptr<boost::asio::ip::tcp::socket> socket):
m_Socket (socket), m_BufferLen (0), expected_host(hostname) m_Socket (socket), m_BufferLen (0), expected_host(hostname)
{ {
@ -1115,6 +1096,8 @@ namespace http {
SendReply (res, content); SendReply (res, content);
} }
std::map<uint32_t, uint32_t> HTTPConnection::m_Tokens;
uint32_t HTTPConnection::CreateToken () uint32_t HTTPConnection::CreateToken ()
{ {
uint32_t token; uint32_t token;
@ -1260,7 +1243,7 @@ namespace http {
s << "<b>" << tr("ERROR") << "</b>:&nbsp;" << tr("StreamID can't be null") << "<br>\r\n<br>\r\n"; s << "<b>" << tr("ERROR") << "</b>:&nbsp;" << tr("StreamID can't be null") << "<br>\r\n<br>\r\n";
s << "<a href=\"" << webroot << "?page=local_destination&b32=" << b32 << "\">" << tr("Return to destination page") << "</a><br>\r\n"; s << "<a href=\"" << webroot << "?page=local_destination&b32=" << b32 << "\">" << tr("Return to destination page") << "</a><br>\r\n";
s << "<p>" << tr("You will be redirected back in 5 seconds") << "</b>"; s << "<p>" << tr("You will be redirected in 5 seconds") << "</b>";
redirect = "5; url=" + webroot + "?page=local_destination&b32=" + b32; redirect = "5; url=" + webroot + "?page=local_destination&b32=" + b32;
res.add_header("Refresh", redirect.c_str()); res.add_header("Refresh", redirect.c_str());
return; return;
@ -1273,7 +1256,7 @@ namespace http {
else { else {
s << "<b>" << tr("ERROR") << "</b>:&nbsp;" << tr("Transit tunnels count must not exceed 65535") << "\r\n<br>\r\n<br>\r\n"; s << "<b>" << tr("ERROR") << "</b>:&nbsp;" << tr("Transit tunnels count must not exceed 65535") << "\r\n<br>\r\n<br>\r\n";
s << "<a href=\"" << webroot << "?page=commands\">" << tr("Back to commands list") << "</a>\r\n<br>\r\n"; s << "<a href=\"" << webroot << "?page=commands\">" << tr("Back to commands list") << "</a>\r\n<br>\r\n";
s << "<p>" << tr("You will be redirected back in 5 seconds") << "</b>"; s << "<p>" << tr("You will be redirected in 5 seconds") << "</b>";
res.add_header("Refresh", redirect.c_str()); res.add_header("Refresh", redirect.c_str());
return; return;
} }
@ -1281,7 +1264,7 @@ namespace http {
else if (cmd == HTTP_COMMAND_GET_REG_STRING) else if (cmd == HTTP_COMMAND_GET_REG_STRING)
{ {
std::string b32 = params["b32"]; std::string b32 = params["b32"];
std::string name = params["name"]; std::string name = i2p::http::UrlDecode(params["name"]);
i2p::data::IdentHash ident; i2p::data::IdentHash ident;
ident.FromBase32 (b32); ident.FromBase32 (b32);
@ -1327,6 +1310,18 @@ namespace http {
s << "<a href=\"" << webroot << "?page=local_destination&b32=" << b32 << "\">" << tr("Return to destination page") << "</a>\r\n"; s << "<a href=\"" << webroot << "?page=local_destination&b32=" << b32 << "\">" << tr("Return to destination page") << "</a>\r\n";
return; return;
} }
else if (cmd == HTTP_COMMAND_SETLANGUAGE)
{
std::string lang = params["lang"];
std::string currLang = i2p::client::context.GetLanguage ()->GetLanguage();
if (currLang.compare(lang) != 0)
i2p::i18n::SetLanguage(lang);
}
else if (cmd == HTTP_COMMAND_RELOAD_CSS)
{
LoadExtCSS();
}
else else
{ {
res.code = 400; res.code = 400;
@ -1389,6 +1384,8 @@ namespace http {
m_Thread.reset (new std::thread (std::bind (&HTTPServer::Run, this))); m_Thread.reset (new std::thread (std::bind (&HTTPServer::Run, this)));
m_Acceptor.listen (); m_Acceptor.listen ();
Accept (); Accept ();
LoadExtCSS();
} }
void HTTPServer::Stop () void HTTPServer::Stop ()

View File

@ -0,0 +1,89 @@
/*
* Copyright (c) 2013-2021, The PurpleI2P Project
*
* This file is part of Purple i2pd project and licensed under BSD3
*
* See full license text in LICENSE file at top of project tree
*/
#ifndef HTTP_SERVER_RESOURCES_H__
#define HTTP_SERVER_RESOURCES_H__
namespace i2p
{
namespace http
{
const std::string itoopieFavicon =
"data:image/png;base64,"
"iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACx"
"jwv8YQUAAAAJcEhZcwAALiIAAC4iAari3ZIAAAAHdElNRQfgCQsUNSZrkhi1AAAAGXRFWHRTb2Z0"
"d2FyZQBwYWludC5uZXQgNC4wLjEyQwRr7AAAAoJJREFUOE9jwAUqi4Q1oEwwcDTV1+5sETaBclGB"
"vb09C5QJB6kWpvFQJoOCeLC5kmjEHCgXE2SlyETLi3h6QrkM4VL+ssWSCZUgtopITLKqaOotRTEn"
"cbAkLqAkGtOqLBLVAWLXyWSVFkkmRiqLxuaqiWb/VBYJMAYrwgckJY25VEUzniqKhjU2y+RtCRSP"
"6lUXy/1jIBV5tlYxZUaFVMq2NInwIi9hO8fSfOEAqDZUoCwal6MulvOvyS7gi69K4j9zxZT/m0ps"
"/28ptvvvquXXryIa7QYMMdTwqi0WNtVi0GIDseXl7TnUxFKfnGlxAGp0+D8j2eH/8Ub7/9e7nf7X"
"+Af/B7rwt6pI0h0l0WhQADOC9DBkhSirpImHNVZKp24ukkyoshGLnN8d5fA/y13t/44Kq/8hlnL/"
"z7fZ/58f6vcxSNpbVUVFhV1RLNBVTsQzVYZPSwhsCAhkiIfpNMrkbO6TLf071Sfk/5ZSi/+7q6z/"
"P5ns+v9mj/P/CpuI/20y+aeNGYxZoVoYGmsF3aFMBAAZlCwftnF9ke3//bU2//fXWP8/UGv731Am"
"+V+DdNblSqnUYqhSTKAiYSOqJBrVqiaa+S3UNPr/gmyH/xuKXf63hnn/B8bIP0UxHfEyyeSNQKVM"
"EB1AEB2twhcTLp+gIBJUoyKasEpVJHmqskh8qryovUG/ffCHHRU2q/Tk/YuB6eGPsbExa7ZkpLu1"
"oLEcVDtuUCgV1w60rQzElpRUE1EVSX0BYidHiInXF4nagNhYQW60EF+ApH1ktni0A1SIITSUgVlZ"
"JHYnlIsfzJjIp9xZKswL5YKBHL+coKJoRDaUSzoozxHVrygQU4JykQADAwAT5b1NHtwZugAAAABJ"
"RU5ErkJggg==";
// bundled style sheet
const std::string internalCSS =
"<style>\r\n"
" body { font: 100%/1.5em sans-serif; margin: 0; padding: 1.5em; background: #FAFAFA; color: #103456; }\r\n"
" a, .slide label { text-decoration: none; color: #894C84; }\r\n"
" a:hover, .slide label:hover { color: #FAFAFA; background: #894C84; }\r\n"
" a.button { -webkit-appearance: button; -moz-appearance: button; appearance: button; text-decoration: none;\r\n"
" padding: 0 5px; border: 1px solid #894C84; }\r\n"
" .header { font-size: 2.5em; text-align: center; margin: 1em 0; color: #894C84; }\r\n"
" .wrapper { margin: 0 auto; padding: 1em; max-width: 64em; }\r\n"
" .menu { display: block; float: left; overflow: hidden; max-width: 12em; white-space: nowrap; text-overflow: ellipsis; }\r\n"
" .listitem { display: block; font-family: monospace; font-size: 1.2em; white-space: nowrap; }\r\n"
" .tableitem { font-family: monospace; font-size: 1.2em; white-space: nowrap; }\r\n"
" .content { float: left; font-size: 1em; margin-left: 4em; max-width: 48em; overflow: auto; }\r\n"
" .tunnel.established { color: #56B734; } .tunnel.expiring { color: #D3AE3F; }\r\n"
" .tunnel.failed { color: #D33F3F; } .tunnel.building { color: #434343; }\r\n"
" caption { font-size: 1.5em; text-align: center; color: #894C84; }\r\n"
" table { display: table; border-collapse: collapse; text-align: center; }\r\n"
" table.extaddr { text-align: left; } table.services { width: 100%; }\r\n"
" textarea { word-break: break-all; }\r\n"
" .streamdest { width: 120px; max-width: 240px; overflow: hidden; text-overflow: ellipsis;}\r\n"
" .slide div.slidecontent, .slide [type=\"checkbox\"] { display: none; }\r\n"
" .slide [type=\"checkbox\"]:checked ~ div.slidecontent { display: block; margin-top: 0; padding: 0; }\r\n"
" .disabled { color: #D33F3F; } .enabled { color: #56B734; }\r\n"
" @media screen and (max-width: 1150px) {\r\n" /* adaptive style */
" .wrapper { max-width: 58em; } .menu { max-width: 10em; }\r\n"
" .content { margin-left: 2em; max-width: 42em; }\r\n"
" }\r\n"
" @media screen and (max-width: 980px) {\r\n"
" body { padding: 1.5em 0 0 0; }\r\n"
" .menu { width: 100%; max-width: unset; display: block; float: none; position: unset; font-size: 16px;\r\n"
" text-align: center; }\r\n"
" .menu a, .commands a { display: inline-block; padding: 4px; }\r\n"
" .content { float: none; margin-left: unset; margin-top: 16px; max-width: 100%; width: 100%;\r\n"
" text-align: center; }\r\n"
" a, .slide label { /* margin-right: 10px; */ display: block; /* font-size: 18px; */ }\r\n"
" .header { margin: unset; font-size: 1.5em; } small {display: block}\r\n"
" a.button { -webkit-appearance: button; -moz-appearance: button; appearance: button; text-decoration: none;\r\n"
" margin-top: 10px; padding: 6px; border: 1px solid #894c84; width: -webkit-fill-available; }\r\n"
" input, select { width: 35%; text-align: center; padding: 5px;\r\n"
" border: 2px solid #ccc; -webkit-border-radius: 5px; border-radius: 5px; font-size: 18px; }\r\n"
" table.extaddr { margin: auto; text-align: unset; }\r\n"
" textarea { width: -webkit-fill-available; height: auto; padding:5px; border:2px solid #ccc;\r\n"
" -webkit-border-radius: 5px; border-radius: 5px; font-size: 12px; }\r\n"
" button[type=submit] { padding: 5px 15px; background: #ccc; border: 0 none; cursor: pointer;\r\n"
" -webkit-border-radius: 5px; border-radius: 5px; position: relative; height: 36px; display: -webkit-inline-box; margin-top: 10px; }\r\n"
" }\r\n"
"</style>\r\n";
// for external style sheet
std::string externalCSS;
} // http
} // i2p
#endif /* HTTP_SERVER_RESOURCES_H__ */

View File

@ -406,7 +406,7 @@ namespace client
void I2PControlService::UptimeHandler (std::ostringstream& results) void I2PControlService::UptimeHandler (std::ostringstream& results)
{ {
InsertParam (results, "i2p.router.uptime", (int)i2p::context.GetUptime ()*1000); InsertParam (results, "i2p.router.uptime", std::to_string (i2p::context.GetUptime ()*1000LL));
} }
void I2PControlService::VersionHandler (std::ostringstream& results) void I2PControlService::VersionHandler (std::ostringstream& results)

6
debian/changelog vendored
View File

@ -1,3 +1,9 @@
i2pd (2.39.0-1) unstable; urgency=medium
* updated to version 2.39.0/0.9.51
-- orignal <orignal@i2pmail.org> Mon, 23 Aug 2021 16:00:00 +0000
i2pd (2.38.0-1) unstable; urgency=medium i2pd (2.38.0-1) unstable; urgency=medium
* updated to version 2.38.0/0.9.50 * updated to version 2.38.0/0.9.50

View File

@ -6,12 +6,12 @@ Last-Update: 2021-01-16
--- i2pd.orig/Makefile --- i2pd.orig/Makefile
+++ i2pd/Makefile +++ i2pd/Makefile
@@ -15,7 +15,7 @@ include filelist.mk @@ -21,7 +21,7 @@ include filelist.mk
USE_AESNI := yes USE_AESNI := $(or $(USE_AESNI),yes)
USE_STATIC := no USE_STATIC := $(or $(USE_STATIC),no)
USE_MESHNET := no USE_MESHNET := $(or $(USE_MESHNET),no)
-USE_UPNP := no -USE_UPNP := $(or $(USE_UPNP),no)
+USE_UPNP := yes +USE_UPNP := $(or $(USE_UPNP),yes)
DEBUG := yes DEBUG := $(or $(DEBUG),yes)
ifeq ($(DEBUG),yes) ifeq ($(DEBUG),yes)

View File

@ -21,4 +21,6 @@ LIB_CLIENT_SRC = $(wildcard $(LIB_CLIENT_SRC_DIR)/*.cpp)
LANG_SRC = $(wildcard $(LANG_SRC_DIR)/*.cpp) LANG_SRC = $(wildcard $(LANG_SRC_DIR)/*.cpp)
WRAP_LIB_SRC = $(wildcard $(WRAP_SRC_DIR)/*.cpp)
DAEMON_SRC = $(wildcard $(DAEMON_SRC_DIR)/*.cpp) DAEMON_SRC = $(wildcard $(DAEMON_SRC_DIR)/*.cpp)

View File

@ -13,14 +13,16 @@
#include "I18N.h" #include "I18N.h"
// Afrikaans localization file // Afrikaans localization file
// This is an example translation file without strings in it.
namespace i2p namespace i2p
{ {
namespace i18n namespace i18n
{ {
namespace afrikaans // language namespace afrikaans // language namespace
{ {
// language name in lowercase
static std::string language = "afrikaans";
// See for language plural forms here: // See for language plural forms here:
// https://localization-guide.readthedocs.io/en/latest/l10n/pluralforms.html // https://localization-guide.readthedocs.io/en/latest/l10n/pluralforms.html
static int plural (int n) { static int plural (int n) {
@ -29,12 +31,9 @@ namespace afrikaans // language
static std::map<std::string, std::string> strings static std::map<std::string, std::string> strings
{ {
{"Disabled", "Gedeaktiveer"},
{"Enabled", "Geaktiveer"},
{"failed", "Het misluk"}, {"failed", "Het misluk"},
{"unknown", "onbekend"}, {"unknown", "onbekend"},
{"Tunnels", "Tonnels"}, {"Tunnels", "Tonnels"},
{"Transit tunnels", "Deurgang tonnels"},
{"I2P tunnels", "I2P tonnels"}, {"I2P tunnels", "I2P tonnels"},
{"SAM sessions", "SAM sessies"}, {"SAM sessions", "SAM sessies"},
{"OK", "LEKKER"}, {"OK", "LEKKER"},
@ -52,6 +51,14 @@ namespace afrikaans // language
{"Hidden content. Press on text to see.", "Hidden content. Druk om te sien."}, {"Hidden content. Press on text to see.", "Hidden content. Druk om te sien."},
{"Router Ident", "Router Ident"}, {"Router Ident", "Router Ident"},
{"Router Family", "Router Familie"}, {"Router Family", "Router Familie"},
{"Enabled", "Geaktiveer"},
{"Disabled", "Gedeaktiveer"},
{"Change", "Verander"},
{"Change language", "Verander taal"},
{"Description", "Beskrywing"},
{"Submit", "Stuur"},
{"Proxy error", "Proxy-fout"},
{"Host", "Gasheer"},
{"", ""}, {"", ""},
}; };
@ -66,7 +73,7 @@ namespace afrikaans // language
std::shared_ptr<const i2p::i18n::Locale> GetLocale() std::shared_ptr<const i2p::i18n::Locale> GetLocale()
{ {
return std::make_shared<i2p::i18n::Locale>(strings, plurals, [] (int n)->int { return plural(n); }); return std::make_shared<i2p::i18n::Locale>(language, strings, plurals, [] (int n)->int { return plural(n); });
} }
} // language } // language

View File

@ -19,8 +19,11 @@ namespace i2p
{ {
namespace i18n namespace i18n
{ {
namespace english // language namespace english // language namespace
{ {
// language name in lowercase
static std::string language = "english";
// See for language plural forms here: // See for language plural forms here:
// https://localization-guide.readthedocs.io/en/latest/l10n/pluralforms.html // https://localization-guide.readthedocs.io/en/latest/l10n/pluralforms.html
static int plural (int n) { static int plural (int n) {
@ -39,7 +42,7 @@ namespace english // language
std::shared_ptr<const i2p::i18n::Locale> GetLocale() std::shared_ptr<const i2p::i18n::Locale> GetLocale()
{ {
return std::make_shared<i2p::i18n::Locale>(strings, plurals, [] (int n)->int { return plural(n); }); return std::make_shared<i2p::i18n::Locale>(language, strings, plurals, [] (int n)->int { return plural(n); });
} }
} // language } // language

View File

@ -9,7 +9,7 @@
#ifndef __I18N_H__ #ifndef __I18N_H__
#define __I18N_H__ #define __I18N_H__
#include "RouterContext.h" #include "ClientContext.h"
namespace i2p namespace i2p
{ {
@ -17,26 +17,21 @@ namespace i18n
{ {
inline void SetLanguage(const std::string &lang) inline void SetLanguage(const std::string &lang)
{ {
if (!lang.compare("afrikaans")) const auto it = i2p::i18n::languages.find(lang);
i2p::context.SetLanguage (i2p::i18n::afrikaans::GetLocale()); if (it == i2p::i18n::languages.end()) // fallback
else if (!lang.compare("russian")) i2p::client::context.SetLanguage (i2p::i18n::english::GetLocale());
i2p::context.SetLanguage (i2p::i18n::russian::GetLocale()); else
else if (!lang.compare("turkmen")) i2p::client::context.SetLanguage (it->second.LocaleFunc());
i2p::context.SetLanguage (i2p::i18n::turkmen::GetLocale());
else if (!lang.compare("ukrainian"))
i2p::context.SetLanguage (i2p::i18n::ukrainian::GetLocale());
else // fallback
i2p::context.SetLanguage (i2p::i18n::english::GetLocale());
} }
inline std::string translate (const std::string& arg) inline std::string translate (const std::string& arg)
{ {
return i2p::context.GetLanguage ()->GetString (arg); return i2p::client::context.GetLanguage ()->GetString (arg);
} }
inline std::string translate (const std::string& arg, const std::string& arg2, const int& n) inline std::string translate (const std::string& arg, const std::string& arg2, const int& n)
{ {
return i2p::context.GetLanguage ()->GetPlural (arg, arg2, n); return i2p::client::context.GetLanguage ()->GetPlural (arg, arg2, n);
} }
} // i18n } // i18n
} // i2p } // i2p

View File

@ -17,10 +17,17 @@ namespace i18n
{ {
public: public:
Locale ( Locale (
const std::string& language,
const std::map<std::string, std::string>& strings, const std::map<std::string, std::string>& strings,
const std::map<std::string, std::vector<std::string>>& plurals, const std::map<std::string, std::vector<std::string>>& plurals,
std::function<int(int)> formula std::function<int(int)> formula
): m_Strings (strings), m_Plurals (plurals), m_Formula (formula) { }; ): m_Language (language), m_Strings (strings), m_Plurals (plurals), m_Formula (formula) { };
// Get activated language name for webconsole
std::string GetLanguage() const
{
return m_Language;
}
std::string GetString (const std::string& arg) const std::string GetString (const std::string& arg) const
{ {
@ -50,17 +57,39 @@ namespace i18n
} }
private: private:
const std::string m_Language;
const std::map<std::string, std::string> m_Strings; const std::map<std::string, std::string> m_Strings;
const std::map<std::string, std::vector<std::string>> m_Plurals; const std::map<std::string, std::vector<std::string>> m_Plurals;
std::function<int(int)> m_Formula; std::function<int(int)> m_Formula;
}; };
struct langData
{
std::string LocaleName; // localized name
std::string ShortCode; // short language code, like "en"
std::function<std::shared_ptr<const i2p::i18n::Locale> (void)> LocaleFunc;
};
// Add localization here with language name as namespace // Add localization here with language name as namespace
namespace afrikaans { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); } namespace afrikaans { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); }
namespace english { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); } namespace english { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); }
namespace russian { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); } namespace russian { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); }
namespace turkmen { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); } namespace turkmen { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); }
namespace ukrainian { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); } namespace ukrainian { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); }
namespace uzbek { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); }
/**
* That map contains international language name lower-case and name in it's language
*/
static std::map<std::string, langData> languages
{
{ "afrikaans", {"Afrikaans", "af", i2p::i18n::afrikaans::GetLocale} },
{ "english", {"English", "en", i2p::i18n::english::GetLocale} },
{ "russian", {"русский язык", "ru", i2p::i18n::russian::GetLocale} },
{ "turkmen", {"türkmen dili", "tk", i2p::i18n::turkmen::GetLocale} },
{ "ukrainian", {"украї́нська мо́ва", "uk", i2p::i18n::ukrainian::GetLocale} },
{ "uzbek", {"Oʻzbek", "uz", i2p::i18n::uzbek::GetLocale} },
};
} // i18n } // i18n
} // i2p } // i2p

View File

@ -18,8 +18,11 @@ namespace i2p
{ {
namespace i18n namespace i18n
{ {
namespace russian // language namespace russian // language namespace
{ {
// language name in lowercase
static std::string language = "russian";
// See for language plural forms here: // See for language plural forms here:
// https://localization-guide.readthedocs.io/en/latest/l10n/pluralforms.html // https://localization-guide.readthedocs.io/en/latest/l10n/pluralforms.html
static int plural (int n) { static int plural (int n) {
@ -28,23 +31,148 @@ namespace russian // language
static std::map<std::string, std::string> strings static std::map<std::string, std::string> strings
{ {
// HTTP Proxy {"KiB", "КиБ"},
{"MiB", "МиБ"},
{"GiB", "ГиБ"},
{"building", "строится"},
{"failed", "неудачный"},
{"expiring", "истекает"},
{"established", "работает"},
{"unknown", "неизвестно"},
{"exploratory", "исследовательский"},
{"<b>i2pd</b> webconsole", "Веб-консоль <b>i2pd</b>"},
{"Main page", "Главная"},
{"Router commands", "Команды роутера"},
{"Local Destinations", "Локальные назначения"},
{"LeaseSets", "Лизсеты"},
{"Tunnels", "Туннели"},
{"Transit Tunnels", "Транзитные туннели"},
{"Transports", "Транспорты"},
{"I2P tunnels", "I2P туннели"},
{"SAM sessions", "SAM сессии"},
{"ERROR", "ОШИБКА"},
{"OK", "OK"},
{"Testing", "Тестирование"},
{"Firewalled", "Заблокировано извне"},
{"Unknown", "Неизвестно"},
{"Proxy", "Прокси"},
{"Mesh", "MESH-сеть"},
{"Error", "Ошибка"},
{"Clock skew", "Не точное время"},
{"Offline", "Оффлайн"},
{"Symmetric NAT", "Симметричный NAT"},
{"Uptime", "В сети"},
{"Network status", "Сетевой статус"},
{"Network status v6", "Сетевой статус v6"},
{"Stopping in", "Остановка через"},
{"Family", "Семейство"},
{"Tunnel creation success rate", "Успешно построенных туннелей"},
{"Received", "Получено"},
{"KiB/s", "КиБ/с"},
{"Sent", "Отправлено"},
{"Transit", "Транзит"},
{"Data path", "Путь к данным"},
{"Hidden content. Press on text to see.", "Скрытый контент. Нажмите на текст чтобы отобразить."},
{"Router Ident", "Идентификатор роутера"},
{"Router Family", "Семейство роутера"},
{"Router Caps", "Флаги роутера"},
{"Version", "Версия"},
{"Our external address", "Наш внешний адрес"},
{"supported", "поддерживается"},
{"Routers", "Роутеры"},
{"Floodfills", "Флудфилы"},
{"Client Tunnels", "Клиентские туннели"},
{"Services", "Сервисы"},
{"Enabled", "Включено"},
{"Disabled", "Выключено"},
{"Encrypted B33 address", "Шифрованные B33 адреса"},
{"Address registration line", "Строка регистрации адреса"},
{"Domain", "Домен"},
{"Generate", "Сгенерировать"},
{"<b>Note:</b> result string can be used only for registering 2LD domains (example.i2p). For registering subdomains please use i2pd-tools.", "<b>Примечание:</b> полученная строка может быть использована только для регистрации доменов второго уровня (example.i2p). Для регистрации поддоменов используйте i2pd-tools."},
{"Address", "Адрес"},
{"Type", "Тип"},
{"EncType", "ТипШифр"},
{"Inbound tunnels", "Входящие туннели"},
{"ms", "мс"},
{"Outbound tunnels", "Исходящие туннели"},
{"Tags", "Теги"},
{"Incoming", "Входящие"},
{"Outgoing", "Исходящие"},
{"Destination", "Назначение"},
{"Amount", "Количество"},
{"Incoming Tags", "Входящие теги"},
{"Tags sessions", "Сессии тегов"},
{"Status", "Статус"},
{"Local Destination", "Локальное назначение"},
{"Streams", "Стримы"},
{"Close stream", "Закрыть стрим"},
{"I2CP session not found", "I2CP сессия не найдена"},
{"I2CP is not enabled", "I2CP не включен"},
{"Invalid", "Некорректный"},
{"Store type", "Тип хранилища"},
{"Expires", "Истекает"},
{"Non Expired Leases", "Не истекшие Lease-ы"},
{"Gateway", "Шлюз"},
{"TunnelID", "ID туннеля"},
{"EndDate", "Заканчивается"},
{"not floodfill", "не флудфил"},
{"Queue size", "Размер очереди"},
{"Run peer test", "Запустить тестирование"},
{"Decline transit tunnels", "Отклонять транзитные туннели"},
{"Accept transit tunnels", "Принимать транзитные туннели"},
{"Cancel graceful shutdown", "Отменить плавную остановку"},
{"Start graceful shutdown", "Запустить плавную остановку"},
{"Force shutdown", "Принудительная остановка"},
{"Reload external CSS styles", "Перезагрузить внешние CSS стили"},
{"<b>Note:</b> any action done here are not persistent and not changes your config files.", "<b>Примечание:</b> любое действие произведенное здесь не является постоянным и не изменяет ваши конфигурационные файлы."},
{"Logging level", "Уровень логирования"},
{"Transit tunnels limit", "Лимит транзитных туннелей"},
{"Change", "Изменить"},
{"Change language", "Изменение языка"},
{"no transit tunnels currently built", "нет построенных транзитных туннелей"},
{"SAM disabled", "SAM выключен"},
{"no sessions currently running", "нет запущенных сессий"},
{"SAM session not found", "SAM сессия не найдена"},
{"SAM Session", "SAM сессия"},
{"Server Tunnels", "Серверные туннели"},
{"Client Forwards", "Клиентские перенаправления"},
{"Server Forwards", "Серверные перенаправления"},
{"Unknown page", "Неизвестная страница"},
{"Invalid token", "Неверный токен"},
{"SUCCESS", "УСПЕШНО"},
{"Stream closed", "Стрим закрыт"},
{"Stream not found or already was closed", "Стрим не найден или уже закрыт"},
{"Destination not found", "Точка назначения не найдена"},
{"StreamID can't be null", "StreamID не может быть пустым"},
{"Return to destination page", "Вернуться на страницу точки назначения"},
{"You will be redirected in 5 seconds", "Вы будете переадресованы через 5 секунд"},
{"Transit tunnels count must not exceed 65535", "Число транзитных туннелей не должно превышать 65535"},
{"Back to commands list", "Вернуться к списку команд"},
{"Register at reg.i2p", "Зарегистрировать на reg.i2p"},
{"Description", "Описание"},
{"A bit information about service on domain", "Немного информации о сервисе на домене"},
{"Submit", "Отправить"},
{"Domain can't end with .b32.i2p", "Домен не может заканчиваться на .b32.i2p"},
{"Domain must end with .i2p", "Домен должен заканчиваться на .i2p"},
{"Such destination is not found", "Такая точка назначения не найдена"},
{"Unknown command", "Неизвестная команда"},
{"Command accepted", "Команда принята"},
{"Proxy error", "Ошибка прокси"}, {"Proxy error", "Ошибка прокси"},
{"Proxy info", "Информация прокси"}, {"Proxy info", "Информация прокси"},
{"Proxy error: Host not found", "Ошибка прокси: Адрес не найден"}, {"Proxy error: Host not found", "Ошибка прокси: Узел не найден"},
{"Remote host not found in router's addressbook", "Запрошенный адрес не найден в адресной книге роутера"}, {"Remote host not found in router's addressbook", "Запрошенный узел не найден в адресной книге роутера"},
{"You may try to find this host on jump services below", "Вы можете попробовать найти адрес на джамп сервисах ниже"}, {"You may try to find this host on jump services below", "Вы можете попробовать найти узел через джамп сервисы ниже"},
{"Invalid request", "Некорректный запрос"}, {"Invalid request", "Некорректный запрос"},
{"Proxy unable to parse your request", "Прокси не может разобрать ваш запрос"}, {"Proxy unable to parse your request", "Прокси не может разобрать ваш запрос"},
{"addresshelper is not supported", "addresshelper не поддерживается"}, {"addresshelper is not supported", "addresshelper не поддерживается"},
{"Host", "Адрес"}, {"Host", "Узел"},
{"added to router's addressbook from helper", "добавлен в адресную книгу роутера через хелпер"}, {"added to router's addressbook from helper", "добавлен в адресную книгу роутера через хелпер"},
{"already in router's addressbook", "уже в адресной книге роутера"}, {"Click here to proceed:", "Нажмите здесь, чтобы продолжить:"},
{"Click", "Нажмите"}, {"Continue", "Продолжить"},
{"here", "здесь"},
{"to proceed", "чтобы продолжить"},
{"to update record", "чтобы обновить запись"},
{"Addresshelper found", "Найден addresshelper"}, {"Addresshelper found", "Найден addresshelper"},
{"already in router's addressbook", "уже в адресной книге роутера"},
{"Click here to update record:", "Нажмите здесь, чтобы обновить запись:"},
{"invalid request uri", "некорректный URI запроса"}, {"invalid request uri", "некорректный URI запроса"},
{"Can't detect destination host from request", "Не удалось определить адрес назначения из запроса"}, {"Can't detect destination host from request", "Не удалось определить адрес назначения из запроса"},
{"Outproxy failure", "Ошибка внешнего прокси"}, {"Outproxy failure", "Ошибка внешнего прокси"},
@ -63,169 +191,13 @@ namespace russian // language
{"cannot connect", "не удалось подключиться"}, {"cannot connect", "не удалось подключиться"},
{"http out proxy not implemented", "поддержка внешнего HTTP прокси сервера не реализована"}, {"http out proxy not implemented", "поддержка внешнего HTTP прокси сервера не реализована"},
{"cannot connect to upstream http proxy", "не удалось подключиться к вышестоящему HTTP прокси серверу"}, {"cannot connect to upstream http proxy", "не удалось подключиться к вышестоящему HTTP прокси серверу"},
{"Host is down", "Адрес недоступен"}, {"Host is down", "Узел недоступен"},
{"Can't create connection to requested host, it may be down. Please try again later.", {"Can't create connection to requested host, it may be down. Please try again later.", "Не удалось установить соединение к запрошенному узлу, возможно он не в сети. Попробуйте повторить запрос позже."},
"Не удалось установить соединение к запрошенному адресу, возможно он не в сети. Попробуйте повторить запрос позже."},
// Webconsole //
// cssStyles
{"Disabled", "Выключено"},
{"Enabled", "Включено"},
// ShowTraffic
{"KiB", "КиБ"},
{"MiB", "МиБ"},
{"GiB", "ГиБ"},
// ShowTunnelDetails
{"building", "строится"},
{"failed", "неудачный"},
{"expiring", "истекает"},
{"established", "работает"},
{"exploratory", "исследовательский"},
{"unknown", "неизвестно"},
{"<b>i2pd</b> webconsole", "Веб-консоль <b>i2pd</b>"},
// ShowPageHead
{"Main page", "Главная"},
{"Router commands", "Команды роутера"},
{"Local destinations", "Локальные назнач."},
{"LeaseSets", "Лизсеты"},
{"Tunnels", "Туннели"},
{"Transit tunnels", "Транзит. туннели"},
{"Transports", "Транспорты"},
{"I2P tunnels", "I2P туннели"},
{"SAM sessions", "SAM сессии"},
// Network Status
{"OK", "OK"},
{"Testing", "Тестирование"},
{"Firewalled", "Заблокировано извне"},
{"Unknown", "Неизвестно"},
{"Proxy", "Прокси"},
{"Mesh", "MESH-сеть"},
{"Error", "Ошибка"},
{"Clock skew", "Не точное время"},
{"Offline", "Оффлайн"},
{"Symmetric NAT", "Симметричный NAT"},
// Status
{"Uptime", "В сети"},
{"Network status", "Сетевой статус"},
{"Network status v6", "Сетевой статус v6"},
{"Stopping in", "Остановка через"},
{"Family", "Семейство"},
{"Tunnel creation success rate", "Успешно построенных туннелей"},
{"Received", "Получено"},
{"Sent", "Отправлено"},
{"Transit", "Транзит"},
{"KiB/s", "КиБ/с"},
{"Data path", "Путь к данным"},
{"Hidden content. Press on text to see.", "Скрытый контент. Нажмите на текст чтобы отобразить."},
{"Router Ident", "Идентификатор роутера"},
{"Router Family", "Семейство роутера"},
{"Router Caps", "Флаги роутера"},
{"Version", "Версия"},
{"Our external address", "Наш внешний адрес"},
{"supported", "поддерживается"},
{"Routers", "Роутеры"},
{"Floodfills", "Флудфилы"},
{"LeaseSets", "Лизсеты"},
{"Client Tunnels", "Клиентские туннели"},
{"Transit Tunnels", "Транзитные туннели"},
{"Services", "Сервисы"},
// ShowLocalDestinations
{"Local Destinations", "Локальные назначения"},
// ShowLeaseSetDestination
{"Encrypted B33 address", "Шифрованные B33 адреса"},
{"Address registration line", "Строка регистрации адреса"},
{"Domain", "Домен"},
{"Generate", "Сгенерировать"},
{"<b>Note:</b> result string can be used only for registering 2LD domains (example.i2p). For registering subdomains please use i2pd-tools.",
"<b>Примечание:</b> полученная строка может быть использована только для регистрации доменов второго уровня. Для регистрации поддоменов используйте i2pd-tools."},
{"Address", "Адрес"},
{"Type", "Тип"},
{"EncType", "ТипШифр"},
{"Inbound tunnels", "Входящие туннели"},
{"Outbound tunnels", "Исходящие туннели"},
{"ms", "мс"}, // milliseconds
{"Tags", "Теги"},
{"Incoming", "Входящие"},
{"Outgoing", "Исходящие"},
{"Destination", "Назначение"},
{"Amount", "Количество"},
{"Incoming Tags", "Входящие Теги"},
{"Tags sessions", "Сессии Тегов"},
{"Status", "Статус"},
// ShowLocalDestination
{"Local Destination", "Локальное назначение"},
{"Streams", "Стримы"},
{"Close stream", "Закрыть стрим"},
// ShowI2CPLocalDestination
{"I2CP session not found", "I2CP сессия не найдена"},
{"I2CP is not enabled", "I2CP не включен"},
// ShowLeasesSets
{"Invalid", "Некорректный"},
{"Store type", "Тип хранилища"},
{"Expires", "Истекает"},
{"Non Expired Leases", "Не истекшие Lease-ы"},
{"Gateway", "Шлюз"},
{"TunnelID", "ID туннеля"},
{"EndDate", "Заканчивается"},
{"not floodfill", "не флудфил"},
// ShowTunnels
{"Queue size", "Размер очереди"},
// ShowCommands
{"Run peer test", "Запустить тестирование"},
{"Decline transit tunnels", "Отклонять транзитные туннели"},
{"Accept transit tunnels", "Принимать транзитные туннели"},
{"Cancel graceful shutdown", "Отменить плавную остановку"},
{"Start graceful shutdown", "Запустить плавную остановку"},
{"Force shutdown", "Принудительная остановка"},
{"<b>Note:</b> any action done here are not persistent and not changes your config files.",
"<b>Примечание:</b> любое действие произведенное здесь не является постоянным и не изменяет ваши конфигурационные файлы."},
{"Logging level", "Уровень логирования"},
{"Transit tunnels limit", "Лимит транзитных туннелей"},
{"Change", "Изменить"},
// ShowTransitTunnels
{"no transit tunnels currently built", "нет построенных транзитных туннелей"},
// ShowSAMSessions/ShowSAMSession
{"SAM disabled", "SAM выключен"},
{"SAM session not found", "SAM сессия не найдена"},
{"no sessions currently running", "нет запущенных сессий"},
{"SAM Session", "SAM сессия"},
// ShowI2PTunnels
{"Server Tunnels", "Серверные туннели"},
{"Client Forwards", "Клиентские перенаправления"},
{"Server Forwards", "Серверные перенаправления"},
// HandlePage
{"Unknown page", "Неизвестная страница"},
// HandleCommand, ShowError
{"Invalid token", "Неверный токен"},
{"SUCCESS", "УСПЕШНО"},
{"ERROR", "ОШИБКА"},
{"Unknown command", "Неизвестная команда"},
{"Command accepted", "Команда принята"},
{"Back to commands list", "Вернуться к списку команд"},
{"You will be redirected in 5 seconds", "Вы будете переадресованы через 5 секунд"},
// HTTP_COMMAND_KILLSTREAM
{"Stream closed", "Стрим закрыт"},
{"Stream not found or already was closed", "Стрим не найден или уже закрыт"},
{"Destination not found", "Точка назначения не найдена"},
{"StreamID can't be null", "StreamID не может быть пустым"},
{"Return to destination page", "Вернуться на страницу точки назначения"},
{"You will be redirected back in 5 seconds", "Вы будете переадресованы назад через 5 секунд"},
// HTTP_COMMAND_LIMITTRANSIT
{"Transit tunnels count must not exceed 65535", "Число транзитных туннелей не должно превышать 65535"},
// HTTP_COMMAND_GET_REG_STRING
{"Register at reg.i2p", "Зарегистрировать на reg.i2p"},
{"Description", "Описание"},
{"A bit information about service on domain", "Немного информации о сервисе на домене"},
{"Submit", "Отправить"},
{"Domain can't end with .b32.i2p", "Домен не может заканчиваться на .b32.i2p"},
{"Domain must end with .i2p", "Домен должен заканчиваться на .i2p"},
{"Such destination is not found", "Такая точка назначения не найдена"},
{"", ""}, {"", ""},
}; };
static std::map<std::string, std::vector<std::string>> plurals static std::map<std::string, std::vector<std::string>> plurals
{ {
// ShowUptime
{"days", {"день", "дня", "дней"}}, {"days", {"день", "дня", "дней"}},
{"hours", {"час", "часа", "часов"}}, {"hours", {"час", "часа", "часов"}},
{"minutes", {"минуту", "минуты", "минут"}}, {"minutes", {"минуту", "минуты", "минут"}},
@ -235,7 +207,7 @@ namespace russian // language
std::shared_ptr<const i2p::i18n::Locale> GetLocale() std::shared_ptr<const i2p::i18n::Locale> GetLocale()
{ {
return std::make_shared<i2p::i18n::Locale>(strings, plurals, [] (int n)->int { return plural(n); }); return std::make_shared<i2p::i18n::Locale>(language, strings, plurals, [] (int n)->int { return plural(n); });
} }
} // language } // language

View File

@ -18,8 +18,11 @@ namespace i2p
{ {
namespace i18n namespace i18n
{ {
namespace turkmen // language namespace turkmen // language namespace
{ {
// language name in lowercase
static std::string language = "turkmen";
// See for language plural forms here: // See for language plural forms here:
// https://localization-guide.readthedocs.io/en/latest/l10n/pluralforms.html // https://localization-guide.readthedocs.io/en/latest/l10n/pluralforms.html
static int plural (int n) { static int plural (int n) {
@ -28,7 +31,133 @@ namespace turkmen // language
static std::map<std::string, std::string> strings static std::map<std::string, std::string> strings
{ {
// HTTP Proxy {"KiB", "KiB"},
{"MiB", "MiB"},
{"GiB", "GiB"},
{"building", "bina"},
{"failed", "şowsuz"},
{"expiring", "möhleti gutarýar"},
{"established", "işleýär"},
{"unknown", "näbelli"},
{"exploratory", "gözleg"},
{"<b>i2pd</b> webconsole", "Web konsoly <b>i2pd</b>"},
{"Main page", "Esasy sahypa"},
{"Router commands", "Marşrutizator buýruklary"},
{"Local Destinations", "Ýerli ýerler"},
{"LeaseSets", "Lizset"},
{"Tunnels", "Tuneller"},
{"Transit Tunnels", "Tranzit Tunelleri"},
{"Transports", "Daşamak"},
{"I2P tunnels", "I2P tuneller"},
{"SAM sessions", "SAM Sessiýasy"},
{"ERROR", "Ýalňyşlyk"},
{"OK", "OK"},
{"Testing", "Synag etmek"},
{"Firewalled", "Daşynda petiklendi"},
{"Unknown", "Näbelli"},
{"Proxy", "Proksi"},
{"Mesh", "MESH-tor"},
{"Error", "Ýalňyşlyk"},
{"Clock skew", "Takyk wagt däl"},
{"Offline", "Awtonom"},
{"Symmetric NAT", "Simmetriklik NAT"},
{"Uptime", "Onlaýn onlaýn sözlügi"},
{"Network status", "Tor ýagdaýy"},
{"Network status v6", "Tor ýagdaýy v6"},
{"Stopping in", "Soň duruň"},
{"Family", "Maşgala"},
{"Tunnel creation success rate", "Gurlan teneller üstünlikli gurlan teneller"},
{"Received", "Alnan"},
{"KiB/s", "KiB/s"},
{"Sent", "Ýerleşdirildi"},
{"Transit", "Tranzit"},
{"Data path", "Maglumat ýoly"},
{"Hidden content. Press on text to see.", "Gizlin mazmun. Görkezmek üçin tekste basyň."},
{"Router Ident", "Marşrutly kesgitleýji"},
{"Router Family", "Marşrutler maşgalasy"},
{"Router Caps", "Baýdaklar marşruteri"},
{"Version", "Wersiýasy"},
{"Our external address", "Daşarky salgymyz"},
{"supported", "goldanýar"},
{"Routers", "Marşrutizatorlar"},
{"Floodfills", "Fludfillar"},
{"Client Tunnels", "Müşderi tunelleri"},
{"Services", "Hyzmatlar"},
{"Enabled", "Goşuldy"},
{"Disabled", "Öçürildi"},
{"Encrypted B33 address", "Şifrlenen B33 salgylar"},
{"Address registration line", "Hasaba alyş salgysy"},
{"Domain", "Domen"},
{"Generate", "Öndürmek"},
{"<b>Note:</b> result string can be used only for registering 2LD domains (example.i2p). For registering subdomains please use i2pd-tools.", "<b>Bellik:</b> Alnan setir diňe ikinji derejeli domenleri bellige almak üçin ulanylyp bilner (example.i2p). Subýutmalary hasaba almak üçin i2pd ulanyň-tools."},
{"Address", "Salgysy"},
{"Type", "Görnüş"},
{"EncType", "Şifrlemek görnüşi"},
{"Inbound tunnels", "Gelýän tuneller"},
{"ms", "ms"},
{"Outbound tunnels", "Çykýan tuneller"},
{"Tags", "Bellikler"},
{"Incoming", "Gelýän"},
{"Outgoing", "Çykýan"},
{"Destination", "Maksat"},
{"Amount", "Sany"},
{"Incoming Tags", "Gelýän bellikler"},
{"Tags sessions", "Sapaklar bellikler"},
{"Status", "Ýagdaýy"},
{"Local Destination", "Ýerli maksat"},
{"Streams", "Strimlary"},
{"Close stream", "Yap strim"},
{"I2CP session not found", "I2CP Sessiýa tapylmady"},
{"I2CP is not enabled", "I2CP goşulmaýar"},
{"Invalid", "Nädogry"},
{"Store type", "Ammar görnüşi"},
{"Expires", "Möhleti gutarýar"},
{"Non Expired Leases", "Möhleti gutarmady Lizsetlary"},
{"Gateway", "Derweze"},
{"TunnelID", "Tuneliň ID"},
{"EndDate", "Gutarýar"},
{"not floodfill", "fludfil däl"},
{"Queue size", "Nobatyň ululygy"},
{"Run peer test", "Synag başlaň"},
{"Decline transit tunnels", "Tranzit tunellerini ret ediň"},
{"Accept transit tunnels", "Tranzit tunellerini alyň"},
{"Cancel graceful shutdown", "Tekiz durmagy ýatyryň"},
{"Start graceful shutdown", "Tekiz durmak"},
{"Force shutdown", "Mejbury duralga"},
{"Reload external CSS styles", "Daşarky CSS stillerini täzeden ýükläň"},
{"<b>Note:</b> any action done here are not persistent and not changes your config files.", "<b>Bellik:</b> Bu ýerde öndürilen islendik çäre hemişelik däl we konfigurasiýa faýllaryňyzy üýtgetmeýär."},
{"Logging level", "Giriş derejesi"},
{"Transit tunnels limit", "Tranzit tunelleriniň çägi"},
{"Change", "Üýtgetmek"},
{"Change language", "Dil üýtgetmek"},
{"no transit tunnels currently built", "gurlan tranzit tunelleri ýok"},
{"SAM disabled", "SAM öçürilen"},
{"no sessions currently running", "başlamagyň sessiýalary ýok"},
{"SAM session not found", "SAM Sessiýa tapylmady"},
{"SAM Session", "SAM Sessiýa"},
{"Server Tunnels", "Serwer tunelleri"},
{"Client Forwards", "Müşderi gönükdirýär"},
{"Server Forwards", "Serweriň täzeden düzlüleri"},
{"Unknown page", "Näbelli sahypa"},
{"Invalid token", "Nädogry token"},
{"SUCCESS", "Üstünlikli"},
{"Stream closed", "Strim ýapyk"},
{"Stream not found or already was closed", "Strim tapylmady ýa-da eýýäm ýapyldy"},
{"Destination not found", "Niýetlenen ýeri tapylmady"},
{"StreamID can't be null", "StreamID boş bolup bilmez"},
{"Return to destination page", "Barmaly nokadynyň nokadyna gaýdyp geliň"},
{"You will be redirected in 5 seconds", "5 sekuntdan soň täzeden ugrukdyrylarsyňyz"},
{"Transit tunnels count must not exceed 65535", "Tranzit tagtalaryň sany 65535-den geçmeli däldir"},
{"Back to commands list", "Topar sanawyna dolan"},
{"Register at reg.i2p", "Reg.i2P-de hasaba duruň"},
{"Description", "Beýany"},
{"A bit information about service on domain", "Domendäki hyzmat barada käbir maglumatlar"},
{"Submit", "Iber"},
{"Domain can't end with .b32.i2p", "Domain .b32.i2p bilen gutaryp bilmez"},
{"Domain must end with .i2p", "Domeni .i2p bilen gutarmaly"},
{"Such destination is not found", "Bu barmaly ýer tapylmady"},
{"Unknown command", "Näbelli topar"},
{"Command accepted", "Topar kabul edilýär"},
{"Proxy error", "Proksi ýalňyşlygy"}, {"Proxy error", "Proksi ýalňyşlygy"},
{"Proxy info", "Proksi maglumat"}, {"Proxy info", "Proksi maglumat"},
{"Proxy error: Host not found", "Proksi ýalňyşlygy: Host tapylmady"}, {"Proxy error: Host not found", "Proksi ýalňyşlygy: Host tapylmady"},
@ -39,12 +168,11 @@ namespace turkmen // language
{"addresshelper is not supported", "Salgylandyryjy goldanok"}, {"addresshelper is not supported", "Salgylandyryjy goldanok"},
{"Host", "Adres"}, {"Host", "Adres"},
{"added to router's addressbook from helper", "marşruteriň adresini kömekçiden goşdy"}, {"added to router's addressbook from helper", "marşruteriň adresini kömekçiden goşdy"},
{"already in router's addressbook", "marşruteriň adres kitaby"}, {"Click here to proceed:", "Dowam etmek bu ýerde basyň:"},
{"Click", "Basyň"}, {"Continue", "Dowam et"},
{"here", "bu ýerde"},
{"to proceed", "dowam etmek"},
{"to update record", "recordazgyny täzelemek üçin"},
{"Addresshelper found", "Forgelper tapyldy"}, {"Addresshelper found", "Forgelper tapyldy"},
{"already in router's addressbook", "marşruteriň adres kitaby"},
{"Click here to update record:", "Recordazgyny täzelemek üçin bu ýerde basyň:"},
{"invalid request uri", "nädogry haýyş URI"}, {"invalid request uri", "nädogry haýyş URI"},
{"Can't detect destination host from request", "Haýyşdan barmaly ýerini tapyp bilemok"}, {"Can't detect destination host from request", "Haýyşdan barmaly ýerini tapyp bilemok"},
{"Outproxy failure", "Daşarky proksi ýalňyşlyk"}, {"Outproxy failure", "Daşarky proksi ýalňyşlyk"},
@ -64,161 +192,7 @@ namespace turkmen // language
{"http out proxy not implemented", "daşarky HTTP proksi serwerini goldamak amala aşyrylmaýar"}, {"http out proxy not implemented", "daşarky HTTP proksi serwerini goldamak amala aşyrylmaýar"},
{"cannot connect to upstream http proxy", "ýokary akym HTTP proksi serwerine birigip bilmedi"}, {"cannot connect to upstream http proxy", "ýokary akym HTTP proksi serwerine birigip bilmedi"},
{"Host is down", "Salgy elýeterli däl"}, {"Host is down", "Salgy elýeterli däl"},
{"Can't create connection to requested host, it may be down. Please try again later.", {"Can't create connection to requested host, it may be down. Please try again later.", "Talap edilýän salgyda birikmäni gurup bilmedim, onlaýn bolup bilmez. Soňra haýyşy soň gaýtalamaga synanyşyň."},
"Talap edilýän salgyda birikmäni gurup bilmedim, onlaýn bolup bilmez. Soňra haýyşy soň gaýtalamaga synanyşyň."},
// Webconsole //
// cssStyles
{"Disabled", "Öçürildi"},
{"Enabled", "Goşuldy"},
// ShowTraffic
{"KiB", "KiB"},
{"MiB", "MiB"},
{"GiB", "GiB"},
// ShowTunnelDetails
{"building", "bina"},
{"failed", "şowsuz"},
{"expiring", "möhleti gutarýar"},
{"established", "işleýär"},
{"exploratory", "gözleg"},
{"unknown", "näbelli"},
{"<b>i2pd</b> webconsole", "Web konsoly <b>i2pd</b>"},
// ShowPageHead
{"Main page", "Esasy sahypa"},
{"Router commands", "Marşrutizator buýruklary"},
{"Local destinations", "Ýerli ýerler"},
{"LeaseSets", "Lizset"},
{"Tunnels", "Tuneller"},
{"Transit tunnels", "Tranzit tunels"},
{"Transports", "Daşamak"},
{"I2P tunnels", "I2P tuneller"},
{"SAM sessions", "SAM Sessiýasy"},
// Network Status
{"OK", "OK"},
{"Testing", "Synag etmek"},
{"Firewalled", "Daşynda petiklendi"},
{"Unknown", "Näbelli"},
{"Proxy", "Proksi"},
{"Mesh", "MESH-tor"},
{"Error", "Ýalňyşlyk"},
{"Clock skew", "Takyk wagt däl"},
{"Offline", "Awtonom"},
{"Symmetric NAT", "Simmetriklik NAT"},
// Status
{"Uptime", "Onlaýn onlaýn sözlügi"},
{"Network status", "Tor ýagdaýy"},
{"Network status v6", "Tor ýagdaýy v6"},
{"Stopping in", "Soň duruň"},
{"Family", "Maşgala"},
{"Tunnel creation success rate", "Gurlan teneller üstünlikli gurlan teneller"},
{"Received", "Alnan"},
{"Sent", "Ýerleşdirildi"},
{"Transit", "Tranzit"},
{"KiB/s", "KiB/s"},
{"Data path", "Maglumat ýoly"},
{"Hidden content. Press on text to see.", "Gizlin mazmun. Görkezmek üçin tekste basyň."},
{"Router Ident", "Marşrutly kesgitleýji"},
{"Router Family", "Marşrutler maşgalasy"},
{"Router Caps", "Baýdaklar marşruteri"},
{"Version", "Wersiýasy"},
{"Our external address", "Daşarky salgymyz"},
{"supported", "goldanýar"},
{"Routers", "Marşrutizatorlar"},
{"Floodfills", "Fludfillar"},
{"Client Tunnels", "Müşderi tunelleri"},
{"Transit Tunnels", "Tranzit Tunelleri"},
{"Services", "Hyzmatlar"},
// ShowLocalDestinations
{"Local Destinations", "Ýerli ýerler"},
// ShowLeaseSetDestination
{"Encrypted B33 address", "Şifrlenen B33 salgylar"},
{"Address registration line", "Hasaba alyş salgysy"},
{"Domain", "Domen"},
{"Generate", "Öndürmek"},
{"<b>Note:</b> result string can be used only for registering 2LD domains (example.i2p). For registering subdomains please use i2pd-tools.",
"<b>Bellik:</b> Alnan setir diňe ikinji derejeli domenleri bellige almak üçin ulanylyp bilner. Subýutmalary hasaba almak üçin i2pd ulanyň-tools."},
{"Address", "Salgysy"},
{"Type", "Görnüş"},
{"EncType", "Şifrlemek görnüşi"},
{"Inbound tunnels", "Gelýän tuneller"},
{"Outbound tunnels", "Çykýan tuneller"},
{"ms", "ms"}, // milliseconds
{"Tags", "Bellikler"},
{"Incoming", "Gelýän"},
{"Outgoing", "Çykýan"},
{"Destination", "Maksat"},
{"Amount", "Sany"},
{"Incoming Tags", "Gelýän bellikler"},
{"Tags sessions", "Sapaklar bellikler"},
{"Status", "Ýagdaýy"},
// ShowLocalDestination
{"Local Destination", "Ýerli maksat"},
{"Streams", "Strimlary"},
{"Close stream", "Yap strim"},
// ShowI2CPLocalDestination
{"I2CP session not found", "I2CP Sessiýa tapylmady"},
{"I2CP is not enabled", "I2CP goşulmaýar"},
// ShowLeasesSets
{"Invalid", "Nädogry"},
{"Store type", "Ammar görnüşi"},
{"Expires", "Möhleti gutarýar"},
{"Non Expired Leases", "Möhleti gutarmady Lizsetlary"},
{"Gateway", "Derweze"},
{"TunnelID", "Tuneliň ID"},
{"EndDate", "Gutarýar"},
{"not floodfill", "fludfil däl"},
// ShowTunnels
{"Queue size", "Nobatyň ululygy"},
// ShowCommands
{"Run peer test", "Synag başlaň"},
{"Decline transit tunnels", "Tranzit tunellerini ret ediň"},
{"Accept transit tunnels", "Tranzit tunellerini alyň"},
{"Cancel graceful shutdown", "Tekiz durmagy ýatyryň"},
{"Start graceful shutdown", "Tekiz durmak"},
{"Force shutdown", "Mejbury duralga"},
{"<b>Note:</b> any action done here are not persistent and not changes your config files.",
"<b>Bellik:</b> Bu ýerde öndürilen islendik çäre hemişelik däl we konfigurasiýa faýllaryňyzy üýtgetmeýär."},
{"Logging level", "Giriş derejesi"},
{"Transit tunnels limit", "Tranzit tunelleriniň çägi"},
{"Change", "Üýtgetmek"},
// ShowTransitTunnels
{"no transit tunnels currently built", "gurlan tranzit tunelleri ýok"},
// ShowSAMSessions/ShowSAMSession
{"SAM disabled", "SAM öçürilen"},
{"SAM session not found", "SAM Sessiýa tapylmady"},
{"no sessions currently running", "başlamagyň sessiýalary ýok"},
{"SAM Session", "SAM Sessiýa"},
// ShowI2PTunnels
{"Server Tunnels", "Serwer tunelleri"},
{"Client Forwards", "Müşderi gönükdirýär"},
{"Server Forwards", "Serweriň täzeden düzlüleri"},
// HandlePage
{"Unknown page", "Näbelli sahypa"},
// HandleCommand, ShowError
{"Invalid token", "Nädogry token"},
{"SUCCESS", "Üstünlikli"},
{"ERROR", "Ýalňyşlyk"},
{"Unknown command", "Näbelli topar"},
{"Command accepted", "Topar kabul edilýär"},
{"Back to commands list", "Topar sanawyna dolan"},
{"You will be redirected in 5 seconds", "5 sekuntdan soň täzeden ugrukdyrylarsyňyz"},
// HTTP_COMMAND_KILLSTREAM
{"Stream closed", "Strim ýapyk"},
{"Stream not found or already was closed", "Strim tapylmady ýa-da eýýäm ýapyldy"},
{"Destination not found", "Niýetlenen ýeri tapylmady"},
{"StreamID can't be null", "StreamID boş bolup bilmez"},
{"Return to destination page", "Barmaly nokadynyň nokadyna gaýdyp geliň"},
{"You will be redirected back in 5 seconds", "5 sekuntda yzyna iberiler"},
// HTTP_COMMAND_LIMITTRANSIT
{"Transit tunnels count must not exceed 65535", "Tranzit tagtalaryň sany 65535-den geçmeli däldir"},
// HTTP_COMMAND_GET_REG_STRING
{"Register at reg.i2p", "Reg.i2P-de hasaba duruň"},
{"Description", "Beýany"},
{"A bit information about service on domain", "Domendäki hyzmat barada käbir maglumatlar"},
{"Submit", "Iber"},
{"Domain can't end with .b32.i2p", "Domain .b32.i2p bilen gutaryp bilmez"},
{"Domain must end with .i2p", "Domeni .i2p bilen gutarmaly"},
{"Such destination is not found", "Bu barmaly ýer tapylmady"},
{"", ""}, {"", ""},
}; };
@ -234,7 +208,7 @@ namespace turkmen // language
std::shared_ptr<const i2p::i18n::Locale> GetLocale() std::shared_ptr<const i2p::i18n::Locale> GetLocale()
{ {
return std::make_shared<i2p::i18n::Locale>(strings, plurals, [] (int n)->int { return plural(n); }); return std::make_shared<i2p::i18n::Locale>(language, strings, plurals, [] (int n)->int { return plural(n); });
} }
} // language } // language

View File

@ -18,8 +18,11 @@ namespace i2p
{ {
namespace i18n namespace i18n
{ {
namespace ukrainian // language namespace ukrainian // language namespace
{ {
// language name in lowercase
static std::string language = "ukrainian";
// See for language plural forms here: // See for language plural forms here:
// https://localization-guide.readthedocs.io/en/latest/l10n/pluralforms.html // https://localization-guide.readthedocs.io/en/latest/l10n/pluralforms.html
static int plural (int n) { static int plural (int n) {
@ -28,23 +31,148 @@ namespace ukrainian // language
static std::map<std::string, std::string> strings static std::map<std::string, std::string> strings
{ {
// HTTP Proxy {"KiB", "КіБ"},
{"MiB", "МіБ"},
{"GiB", "ГіБ"},
{"building", "будується"},
{"failed", "невдалий"},
{"expiring", "завершується"},
{"established", "працює"},
{"unknown", "невідомо"},
{"exploratory", "дослідницький"},
{"<b>i2pd</b> webconsole", "Веб-консоль <b>i2pd</b>"},
{"Main page", "Головна"},
{"Router commands", "Команди маршрутизатора"},
{"Local Destinations", "Локальні Призначення"},
{"LeaseSets", "Лізсети"},
{"Tunnels", "Тунелі"},
{"Transit Tunnels", "Транзитні Тунелі"},
{"Transports", "Транспорти"},
{"I2P tunnels", "I2P тунелі"},
{"SAM sessions", "SAM сесії"},
{"ERROR", "ПОМИЛКА"},
{"OK", "OK"},
{"Testing", "Тестування"},
{"Firewalled", "Заблоковано ззовні"},
{"Unknown", "Невідомо"},
{"Proxy", "Проксі"},
{"Mesh", "MESH-мережа"},
{"Error", "Помилка"},
{"Clock skew", "Неточний час"},
{"Offline", "Офлайн"},
{"Symmetric NAT", "Симетричний NAT"},
{"Uptime", "У мережі"},
{"Network status", "Мережевий статус"},
{"Network status v6", "Мережевий статус v6"},
{"Stopping in", "Зупинка через"},
{"Family", "Сімейство"},
{"Tunnel creation success rate", "Успішно побудованих тунелів"},
{"Received", "Отримано"},
{"KiB/s", "КіБ/с"},
{"Sent", "Відправлено"},
{"Transit", "Транзит"},
{"Data path", "Шлях до даних"},
{"Hidden content. Press on text to see.", "Прихований вміст. Щоб відобразити, натисніть на текст."},
{"Router Ident", "Ідентифікатор маршрутизатора"},
{"Router Family", "Сімейство маршрутизатора"},
{"Router Caps", "Прапорці маршрутизатора"},
{"Version", "Версія"},
{"Our external address", "Наша зовнішня адреса"},
{"supported", "підтримується"},
{"Routers", "Маршрутизатори"},
{"Floodfills", "Флудфіли"},
{"Client Tunnels", "Клієнтські Тунелі"},
{"Services", "Сервіси"},
{"Enabled", "Увімкнуто"},
{"Disabled", "Вимкнуто"},
{"Encrypted B33 address", "Шифровані B33 адреси"},
{"Address registration line", "Рядок реєстрації адреси"},
{"Domain", "Домен"},
{"Generate", "Згенерувати"},
{"<b>Note:</b> result string can be used only for registering 2LD domains (example.i2p). For registering subdomains please use i2pd-tools.", "<b>Примітка:</b> отриманий рядок може бути використаний тільки для реєстрації доменів другого рівня (example.i2p). Для реєстрації піддоменів використовуйте i2pd-tools."},
{"Address", "Адреса"},
{"Type", "Тип"},
{"EncType", "ТипШифр"},
{"Inbound tunnels", "Вхідні тунелі"},
{"ms", "мс"},
{"Outbound tunnels", "Вихідні тунелі"},
{"Tags", "Теги"},
{"Incoming", "Вхідні"},
{"Outgoing", "Вихідні"},
{"Destination", "Призначення"},
{"Amount", "Кількість"},
{"Incoming Tags", "Вхідні Теги"},
{"Tags sessions", "Сесії Тегів"},
{"Status", "Статус"},
{"Local Destination", "Локальні Призначення"},
{"Streams", "Потоки"},
{"Close stream", "Закрити потік"},
{"I2CP session not found", "I2CP сесія не знайдена"},
{"I2CP is not enabled", "I2CP не увікнуто"},
{"Invalid", "Некоректний"},
{"Store type", "Тип сховища"},
{"Expires", "Завершується"},
{"Non Expired Leases", "Не завершені Lease-и"},
{"Gateway", "Шлюз"},
{"TunnelID", "ID тунеля"},
{"EndDate", "Закінчується"},
{"not floodfill", "не флудфіл"},
{"Queue size", "Розмір черги"},
{"Run peer test", "Запустити тестування"},
{"Decline transit tunnels", "Відхиляти транзитні тунелі"},
{"Accept transit tunnels", "Ухвалювати транзитні тунелі"},
{"Cancel graceful shutdown", "Скасувати плавну зупинку"},
{"Start graceful shutdown", "Запустити плавну зупинку"},
{"Force shutdown", "Примусова зупинка"},
{"Reload external CSS styles", "Перезавантажити зовнішні стилі CSS"},
{"<b>Note:</b> any action done here are not persistent and not changes your config files.", "<b>Примітка:</b> будь-яка зроблена тут дія не є постійною та не змінює ваші конфігураційні файли."},
{"Logging level", "Рівень логування"},
{"Transit tunnels limit", "Обмеження транзитних тунелів"},
{"Change", "Змінити"},
{"Change language", "Змінити мову"},
{"no transit tunnels currently built", "немає побудованих транзитних тунелів"},
{"SAM disabled", "SAM вимкнуто"},
{"no sessions currently running", "немає запущених сесій"},
{"SAM session not found", "SAM сесія не знайдена"},
{"SAM Session", "SAM сесія"},
{"Server Tunnels", "Серверні Тунелі"},
{"Client Forwards", "Клієнтські Переспрямування"},
{"Server Forwards", "Серверні Переспрямування"},
{"Unknown page", "Невідома сторінка"},
{"Invalid token", "Невірний токен"},
{"SUCCESS", "УСПІШНО"},
{"Stream closed", "Потік зачинений"},
{"Stream not found or already was closed", "Потік не знайдений або вже зачинений"},
{"Destination not found", "Точка призначення не знайдена"},
{"StreamID can't be null", "Ідентифікатор потоку не може бути порожнім"},
{"Return to destination page", "Повернутися на сторінку точки призначення"},
{"You will be redirected in 5 seconds", "Ви будете переадресовані через 5 секунд"},
{"Transit tunnels count must not exceed 65535", "Кількість транзитних тунелів не повинна перевищувати 65535"},
{"Back to commands list", "Повернутися до списку команд"},
{"Register at reg.i2p", "Зареєструвати на reg.i2p"},
{"Description", "Опис"},
{"A bit information about service on domain", "Трохи інформації про сервіс на домені"},
{"Submit", "Надіслати"},
{"Domain can't end with .b32.i2p", "Домен не може закінчуватися на .b32.i2p"},
{"Domain must end with .i2p", "Домен повинен закінчуватися на .i2p"},
{"Such destination is not found", "Така точка призначення не знайдена"},
{"Unknown command", "Невідома команда"},
{"Command accepted", "Команда прийнята"},
{"Proxy error", "Помилка проксі"}, {"Proxy error", "Помилка проксі"},
{"Proxy info", "Інформація проксі"}, {"Proxy info", "Інформація проксі"},
{"Proxy error: Host not found", "Помилка проксі: Адреса не знайдена"}, {"Proxy error: Host not found", "Помилка проксі: Адреса не знайдена"},
{"Remote host not found in router's addressbook", "Віддалена адреса не знайдена в адресній книзі роутера"}, {"Remote host not found in router's addressbook", "Віддалена адреса не знайдена в адресній книзі маршрутизатора"},
{"You may try to find this host on jump services below", "Ви можете спробувати знайти дану адресу на джамп сервісах нижче"}, {"You may try to find this host on jump services below", "Ви можете спробувати знайти дану адресу на джамп сервісах нижче"},
{"Invalid request", "Некоректний запит"}, {"Invalid request", "Некоректний запит"},
{"Proxy unable to parse your request", "Проксі не може розібрати ваш запит"}, {"Proxy unable to parse your request", "Проксі не може розібрати ваш запит"},
{"addresshelper is not supported", "addresshelper не підтримується"}, {"addresshelper is not supported", "addresshelper не підтримується"},
{"Host", "Адреса"}, {"Host", "Адреса"},
{"added to router's addressbook from helper", "доданий в адресну книгу роутера через хелпер"}, {"added to router's addressbook from helper", "доданий в адресну книгу маршрутизатора через хелпер"},
{"already in router's addressbook", "вже в адресній книзі роутера"}, {"Click here to proceed:", "Натисніть тут щоб продовжити:"},
{"Click", "Натисніть"}, {"Continue", "Продовжити"},
{"here", "тут"},
{"to proceed", "щоб продовжити"},
{"to update record", "щоб оновити запис"},
{"Addresshelper found", "Знайдено addresshelper"}, {"Addresshelper found", "Знайдено addresshelper"},
{"already in router's addressbook", "вже в адресній книзі маршрутизатора"},
{"Click here to update record:", "Натисніть тут щоб оновити запис:"},
{"invalid request uri", "некоректний URI запиту"}, {"invalid request uri", "некоректний URI запиту"},
{"Can't detect destination host from request", "Не вдалось визначити адресу призначення з запиту"}, {"Can't detect destination host from request", "Не вдалось визначити адресу призначення з запиту"},
{"Outproxy failure", "Помилка зовнішнього проксі"}, {"Outproxy failure", "Помилка зовнішнього проксі"},
@ -64,167 +192,12 @@ namespace ukrainian // language
{"http out proxy not implemented", "підтримка зовнішнього HTTP проксі сервера не реалізована"}, {"http out proxy not implemented", "підтримка зовнішнього HTTP проксі сервера не реалізована"},
{"cannot connect to upstream http proxy", "не вдалося підключитися до висхідного HTTP проксі сервера"}, {"cannot connect to upstream http proxy", "не вдалося підключитися до висхідного HTTP проксі сервера"},
{"Host is down", "Вузол недоступний"}, {"Host is down", "Вузол недоступний"},
{"Can't create connection to requested host, it may be down. Please try again later.", {"Can't create connection to requested host, it may be down. Please try again later.", "Не вдалося встановити з'єднання до запитаного вузла, можливо він не в мережі. Спробуйте повторити запит пізніше."},
"Не вдалося встановити з'єднання до запитаного вузла, можливо він не в мережі. Спробуйте повторити запит пізніше."},
// Webconsole //
// cssStyles
{"Disabled", "Вимкнуто"},
{"Enabled", "Увімкнуто"},
// ShowTraffic
{"KiB", "КіБ"},
{"MiB", "МіБ"},
{"GiB", "ГіБ"},
// ShowTunnelDetails
{"building", "будується"},
{"failed", "невдалий"},
{"expiring", "завершується"},
{"established", "працює"},
{"exploratory", "дослідницький"},
{"unknown", "невідомо"},
{"<b>i2pd</b> webconsole", "Веб-консоль <b>i2pd</b>"},
// ShowPageHead
{"Main page", "Головна"},
{"Router commands", "Команди роутера"},
{"Local destinations", "Локальні призначення"},
{"LeaseSets", "Лізсети"},
{"Tunnels", "Тунелі"},
{"Transit tunnels", "Транзитні тунелі"},
{"Transports", "Транспорти"},
{"I2P tunnels", "I2P тунелі"},
{"SAM sessions", "SAM сесії"},
// Network Status
{"OK", "OK"},
{"Testing", "Тестування"},
{"Firewalled", "Заблоковано ззовні"},
{"Unknown", "Невідомо"},
{"Proxy", "Проксі"},
{"Mesh", "MESH-мережа"},
{"Error", "Помилка"},
{"Clock skew", "Неточний час"},
{"Offline", "Офлайн"},
{"Symmetric NAT", "Симетричний NAT"},
// Status
{"Uptime", "В мережі"},
{"Network status", "Мережевий статус"},
{"Network status v6", "Мережевий статус v6"},
{"Stopping in", "Зупинка через"},
{"Family", "Сімейство"},
{"Tunnel creation success rate", "Успішно побудованих тунелів"},
{"Received", "Отримано"},
{"Sent", "Відправлено"},
{"Transit", "Транзит"},
{"KiB/s", "КіБ/с"},
{"Data path", "Шлях до даних"},
{"Hidden content. Press on text to see.", "Прихований вміст. Натисніть на текст щоб відобразити."},
{"Router Ident", "Ідентифікатор Роутера"},
{"Router Family", "Сімейство Роутера"},
{"Router Caps", "Прапорці Роутера"},
{"Version", "Версія"},
{"Our external address", "Наша зовнішня адреса"},
{"supported", "підтримується"},
{"Routers", "Роутери"},
{"Floodfills", "Флудфіли"},
{"Client Tunnels", "Клієнтські Тунелі"},
{"Transit Tunnels", "Транзитні Тунелі"},
{"Services", "Сервіси"},
// ShowLocalDestinations
{"Local Destinations", "Локальні Призначення"},
// ShowLeaseSetDestination
{"Encrypted B33 address", "Шифровані B33 адреси"},
{"Address registration line", "Рядок реєстрації адреси"},
{"Domain", "Домен"},
{"Generate", "Згенерувати"},
{"<b>Note:</b> result string can be used only for registering 2LD domains (example.i2p). For registering subdomains please use i2pd-tools.",
"<b>Примітка:</b> отриманий рядок може бути використаний тільки для реєстрації доменів другого рівня. Для реєстрації піддоменів використовуйте i2pd-tools."},
{"Address", "Адреса"},
{"Type", "Тип"},
{"EncType", "ТипШифр"},
{"Inbound tunnels", "Вхідні тунелі"},
{"Outbound tunnels", "Вихідні тунелі"},
{"ms", "мс"}, // milliseconds
{"Tags", "Теги"},
{"Incoming", "Вхідні"},
{"Outgoing", "Вихідні"},
{"Destination", "Призначення"},
{"Amount", "Кількість"},
{"Incoming Tags", "Вхідні Теги"},
{"Tags sessions", "Сесії тегів"},
{"Status", "Статус"},
// ShowLocalDestination
{"Local Destination", "Локальні Призначення"},
{"Streams", "Потоки"},
{"Close stream", "Закрити потік"},
// ShowI2CPLocalDestination
{"I2CP session not found", "I2CP сесія не знайдена"},
{"I2CP is not enabled", "I2CP не увікнуто"},
// ShowLeasesSets
{"Invalid", "Некоректний"},
{"Store type", "Тип сховища"},
{"Expires", "Завершується"},
{"Non Expired Leases", "Не завершені Lease-и"},
{"Gateway", "Шлюз"},
{"TunnelID", "ID тунеля"},
{"EndDate", "Закінчується"},
{"not floodfill", "не флудфіл"},
// ShowTunnels
{"Queue size", "Розмір черги"},
// ShowCommands
{"Run peer test", "Запустити тестування"},
{"Decline transit tunnels", "Відхиляти транзитні тунелі"},
{"Accept transit tunnels", "Ухвалювати транзитні тунелі"},
{"Cancel graceful shutdown", "Скасувати плавну зупинку"},
{"Start graceful shutdown", "Запустити плавну зупинку"},
{"Force shutdown", "Примусова зупинка"},
{"<b>Note:</b> any action done here are not persistent and not changes your config files.",
"<b>Примітка:</b> будь-яка зроблена тут дія не є постійною та не змінює ваші конфігураційні файли."},
{"Logging level", "Рівень логування"},
{"Transit tunnels limit", "Обмеження транзитних тунелів"},
{"Change", "Змінити"},
// ShowTransitTunnels
{"no transit tunnels currently built", "немає побудованих транзитних тунелів"},
// ShowSAMSessions/ShowSAMSession
{"SAM disabled", "SAM вимкнуто"},
{"SAM session not found", "SAM сесія не знайдена"},
{"no sessions currently running", "немає запущених сесій"},
{"SAM Session", "SAM сесія"},
// ShowI2PTunnels
{"Server Tunnels", "Серверні Тунелі"},
{"Client Forwards", "Клієнтські Переспрямування"},
{"Server Forwards", "Серверні Переспрямування"},
// HandlePage
{"Unknown page", "Невідома сторінка"},
// HandleCommand, ShowError
{"Invalid token", "Невірний токен"},
{"SUCCESS", "УСПІШНО"},
{"ERROR", "ПОМИЛКА"},
{"Unknown command", "Невідома команда"},
{"Command accepted", "Команда прийнята"},
{"Back to commands list", "Повернутися до списку команд"},
{"You will be redirected in 5 seconds", "Ви будете переадресовані через 5 секунд"},
// HTTP_COMMAND_KILLSTREAM
{"Stream closed", "Потік зачинений"},
{"Stream not found or already was closed", "Потік не знайдений або вже зачинений"},
{"Destination not found", "Точка призначення не знайдена"},
{"StreamID can't be null", "Ідентифікатор потоку не може бути порожнім"},
{"Return to destination page", "Повернутися на сторінку точки призначення"},
{"You will be redirected back in 5 seconds", "Ви будете переадресовані назад через 5 секунд"},
// HTTP_COMMAND_LIMITTRANSIT
{"Transit tunnels count must not exceed 65535", "Кількість транзитних тунелів не повинна перевищувати 65535"},
// HTTP_COMMAND_GET_REG_STRING
{"Register at reg.i2p", "Зареєструвати на reg.i2p"},
{"Description", "Опис"},
{"A bit information about service on domain", "Трохи інформації про сервіс на домені"},
{"Submit", "Надіслати"},
{"Domain can't end with .b32.i2p", "Домен не може закінчуватися на .b32.i2p"},
{"Domain must end with .i2p", "Домен повинен закінчуватися на .i2p"},
{"Such destination is not found", "Така точка призначення не знайдена"},
{"", ""}, {"", ""},
}; };
static std::map<std::string, std::vector<std::string>> plurals static std::map<std::string, std::vector<std::string>> plurals
{ {
// ShowUptime
{"days", {"день", "дня", "днів"}}, {"days", {"день", "дня", "днів"}},
{"hours", {"годину", "години", "годин"}}, {"hours", {"годину", "години", "годин"}},
{"minutes", {"хвилину", "хвилини", "хвилин"}}, {"minutes", {"хвилину", "хвилини", "хвилин"}},
@ -234,7 +207,7 @@ namespace ukrainian // language
std::shared_ptr<const i2p::i18n::Locale> GetLocale() std::shared_ptr<const i2p::i18n::Locale> GetLocale()
{ {
return std::make_shared<i2p::i18n::Locale>(strings, plurals, [] (int n)->int { return plural(n); }); return std::make_shared<i2p::i18n::Locale>(language, strings, plurals, [] (int n)->int { return plural(n); });
} }
} // language } // language

206
i18n/Uzbek.cpp Normal file
View File

@ -0,0 +1,206 @@
/*
* Copyright (c) 2021, The PurpleI2P Project
*
* This file is part of Purple i2pd project and licensed under BSD3
*
* See full license text in LICENSE file at top of project tree
*/
#include <map>
#include <vector>
#include <string>
#include <memory>
#include "I18N.h"
// Ukrainian localization file
namespace i2p
{
namespace i18n
{
namespace uzbek // language namespace
{
// language name in lowercase
static std::string language = "uzbek";
// See for language plural forms here:
// https://localization-guide.readthedocs.io/en/latest/l10n/pluralforms.html
static int plural (int n) {
return n > 1 ? 1 : 0;
}
static std::map<std::string, std::string> strings
{
{"KiB", "KiB"},
{"MiB", "MiB"},
{"GiB", "GiB"},
{"building", "qurilish"},
{"failed", "muvaffaqiyatsiz"},
{"expiring", "muddati tugaydi"},
{"established", "aloqa o'rnatildi"},
{"unknown", "noma'lum"},
{"exploratory", "tadqiqiy"},
{"<b>i2pd</b> webconsole", "<b>i2pd</b> veb -konsoli"},
{"Main page", "Asosiy sahifa"},
{"Router commands", "Router buyruqlari"},
{"LeaseSets", "LeaseSets"},
{"Tunnels", "Tunnellar"},
{"Transit Tunnels", "Tranzit Tunellar"},
{"Transports", "Transportlar"},
{"I2P tunnels", "I2P tunnellar"},
{"SAM sessions", "SAM sessiyalari"},
{"ERROR", "XATO"},
{"OK", "OK"},
{"Testing", "Testlash"},
{"Firewalled", "Xavfsizlik devori bilan himoyalangan"},
{"Unknown", "Notanish"},
{"Proxy", "Proksi"},
{"Mesh", "Mesh To'r"},
{"Error", "Xato"},
{"Clock skew", "Aniq vaqt emas"},
{"Offline", "Oflayn"},
{"Symmetric NAT", "Simmetrik NAT"},
{"Uptime", "Ish vaqti"},
{"Network status", "Tarmoq holati"},
{"Network status v6", "Tarmoq holati v6"},
{"Stopping in", "Ichida to'xtatish"},
{"Family", "Oila"},
{"Tunnel creation success rate", "Tunnel yaratish muvaffaqiyat darajasi"},
{"Received", "Qabul qilindi"},
{"KiB/s", "KiB/s"},
{"Sent", "Yuborilgan"},
{"Transit", "Tranzit"},
{"Data path", "Ma'lumotlar yo'li"},
{"Hidden content. Press on text to see.", "Yashirin tarkib. Ko'rish uchun matn ustida bosing."},
{"Router Ident", "Router identifikatori"},
{"Router Family", "Router Oila"},
{"Router Caps", "Router bayroqlari"},
{"Version", "Versiya"},
{"Our external address", "Bizning tashqi manzilimiz"},
{"supported", "qo'llab -quvvatlanadi"},
{"Routers", "Routerlar"},
{"Floodfills", "Floodfills"},
{"Client Tunnels", "Mijoz tunellari"},
{"Services", "Xizmatlar"},
{"Enabled", "Yoqilgan"},
{"Disabled", "O'chirilgan"},
{"Encrypted B33 address", "Shifrlangan B33 manzil"},
{"Address registration line", "Manzilni ro'yxatga olish liniyasi"},
{"Domain", "Domen"},
{"Generate", "Varatish"},
{"<b>Note:</b> result string can be used only for registering 2LD domains (example.i2p). For registering subdomains please use i2pd-tools.", "<b>Eslatma:</b> natija satridan faqat 2LD domenlarini ro'yxatdan o'tkazish uchun foydalanish mumkin (example.i2p). Subdomenlarni ro'yxatdan o'tkazish uchun i2pd-tools dan foydalaning."},
{"Address", "Manzil"},
{"Type", "Turi"},
{"EncType", "ShifrlashTuri"},
{"Inbound tunnels", "Kirish tunnellari"},
{"ms", "ms"},
{"Outbound tunnels", "Chiquvchi tunnellar"},
{"Tags", "Teglar"},
{"Incoming", "Kiruvchi"},
{"Outgoing", "Chiquvchi"},
{"Destination", "Manzilgoh"},
{"Amount", "Yig'indi"},
{"Incoming Tags", "Kiruvchi teglar"},
{"Tags sessions", "Teglar sessiyalari"},
{"Status", "Holat"},
{"Streams", "Strim"},
{"Close stream", "Strimni o'chirish"},
{"I2CP session not found", "I2CP sessiyasi topilmadi"},
{"I2CP is not enabled", "I2CP yoqilmagan"},
{"Invalid", "Noto'g'ri"},
{"Store type", "Saqlash turi"},
{"Expires", "Muddati tugaydi"},
{"Non Expired Leases", "Muddati O'tmagan Leases"},
{"Gateway", "Kirish yo'li"},
{"TunnelID", "TunnelID"},
{"EndDate", "Tugash Sanasi"},
{"not floodfill", "floodfill emas"},
{"Queue size", "Navbat hajmi"},
{"Run peer test", "Sinovni boshlang"},
{"Decline transit tunnels", "Tranzit tunnellarni rad etish"},
{"Accept transit tunnels", "Tranzit tunnellarni qabul qilish"},
{"Cancel graceful shutdown", "Yumshoq to'xtashni bekor qiling"},
{"Start graceful shutdown", "Yumshoq to'xtashni boshlang"},
{"Force shutdown", "Bizning tashqi manzilimiz"},
{"Reload external CSS styles", "Tashqi CSS uslublarini qayta yuklang"},
{"<b>Note:</b> any action done here are not persistent and not changes your config files.", "<b>Eslatma:</b> bu erda qilingan har qanday harakat doimiy emas va konfiguratsiya fayllarini o'zgartirmaydi."},
{"Transit tunnels limit", "Tranzit tunellar chegarasi"},
{"Change", "O'zgartirish"},
{"Change language", "Tilni o'zgartirish"},
{"no transit tunnels currently built", "qurilgan tranzit tunnellari yo'q"},
{"SAM disabled", "SAM o'chirilgan"},
{"no sessions currently running", "hech qanday ishlaydigan sessiyalar yo'q"},
{"SAM session not found", "SAM sessiyasi topilmadi"},
{"SAM Session", "SAM sessiyasi"},
{"Server Tunnels", "Server Tunellari"},
{"Client Forwards", "Mijozlarni Yo'naltirish"},
{"Server Forwards", "Serverni Yo'naltirish"},
{"Unknown page", "Noma'lum sahifa"},
{"Invalid token", "Notogri belgi"},
{"SUCCESS", "Muvaffaqiyat"},
{"Stream closed", "Strim yopiq"},
{"Stream not found or already was closed", "Strim topilmadi yoki allaqachon yopilgan"},
{"Destination not found", "Yo'nalish topilmadi"},
{"StreamID can't be null", "StreamID bo'sh bo'lishi mumkin emas"},
{"Return to destination page", "Belgilangan sahifaga qaytish"},
{"You will be redirected in 5 seconds", "Siz 5 soniyada qayta yo'naltirilasiz"},
{"Transit tunnels count must not exceed 65535", "Tranzit tunnellar soni 65535 dan oshmasligi kerak"},
{"Back to commands list", "Buyruqlar ro'yxatiga qaytish"},
{"Register at reg.i2p", "Reg.i2p-da ro'yxatdan o'ting"},
{"Description", "Tavsif"},
{"A bit information about service on domain", "Domen xizmatlari haqida bir oz ma'lumot"},
{"Submit", "Yuborish"},
{"Domain can't end with .b32.i2p", "Domen .b32.i2p bilan tugashi mumkin emas"},
{"Domain must end with .i2p", "Domen .i2p bilan tugashi kerak"},
{"Such destination is not found", "Bunday yo'nalish topilmadi"},
{"Unknown command", "Noma'lum buyruq"},
{"Command accepted", "Buyruq qabul qilindi"},
{"Proxy error", "Proksi xatosi"},
{"Proxy info", "Proksi ma'lumotlari"},
{"Proxy error: Host not found", "Proksi xatosi: Xost topilmadi"},
{"Remote host not found in router's addressbook", "Masofaviy xost yo'riqnoma manzillar kitobida topilmadi"},
{"Invalid request", "Notogri sorov"},
{"Proxy unable to parse your request", "Proksi sizning so'rovingizni tahlil qila olmaydi"},
{"addresshelper is not supported", "addresshelper qo'llab -quvvatlanmaydi"},
{"Host", "Xost"},
{"Addresshelper found", "Addresshelper topildi"},
{"invalid request uri", "noto'g'ri URI so'rovi"},
{"Can't detect destination host from request", "Sorov orqali manzil xostini aniqlab bo'lmayapti"},
{"Outproxy failure", "Tashqi proksi muvaffaqiyatsizligi"},
{"bad outproxy settings", "noto'g'ri tashqi proksi -server sozlamalari"},
{"not inside I2P network, but outproxy is not enabled", "I2P tarmog'ida emas, lekin tashqi proksi yoqilmagan"},
{"unknown outproxy url", "noma'lum outproxy url"},
{"cannot resolve upstream proxy", "yuqoridagi proksi -serverni aniqlab olib bolmaydi"},
{"hostname too long", "xost nomi juda uzun"},
{"cannot connect to upstream socks proxy", "yuqori soks proksi -serveriga ulanib bo'lmaydi"},
{"Cannot negotiate with socks proxy", "Soks proksi bilan muzokara olib bo'lmaydi"},
{"CONNECT error", "CONNECT xatosi"},
{"Failed to Connect", "Ulanmadi"},
{"socks proxy error", "soks proksi xatosi"},
{"failed to send request to upstream", "yuqori http proksi-serveriga ulanib bo'lmadi"},
{"No Reply From socks proxy", "Soks-proksidan javob yo'q"},
{"cannot connect", "ulab bo'lmaydi"},
{"http out proxy not implemented", "tashqi HTTP proksi -serverni qo'llab -quvvatlash amalga oshirilmagan"},
{"cannot connect to upstream http proxy", "yuqori http proksi-serveriga ulanib bo'lmadi"},
{"Host is down", "Xost ishlamayapti"},
{"Can't create connection to requested host, it may be down. Please try again later.", "Talab qilingan xost bilan aloqa o'rnatilmadi, u ishlamay qolishi mumkin. Iltimos keyinroq qayta urinib ko'ring."},
{"", ""},
};
static std::map<std::string, std::vector<std::string>> plurals
{
{"days", {"kun", "kunlar"}},
{"hours", {"soat", "soat"}},
{"minutes", {"daqiqa", "daqiqalar"}},
{"seconds", {"soniya", "soniyalar"}},
{"", {"", ""}},
};
std::shared_ptr<const i2p::i18n::Locale> GetLocale()
{
return std::make_shared<i2p::i18n::Locale>(language, strings, plurals, [] (int n)->int { return plural(n); });
}
} // language
} // i18n
} // i2p

View File

@ -37,6 +37,7 @@ namespace config {
("conf", value<std::string>()->default_value(""), "Path to main i2pd config file (default: try ~/.i2pd/i2pd.conf or /var/lib/i2pd/i2pd.conf)") ("conf", value<std::string>()->default_value(""), "Path to main i2pd config file (default: try ~/.i2pd/i2pd.conf or /var/lib/i2pd/i2pd.conf)")
("tunconf", value<std::string>()->default_value(""), "Path to config with tunnels list and options (default: try ~/.i2pd/tunnels.conf or /var/lib/i2pd/tunnels.conf)") ("tunconf", value<std::string>()->default_value(""), "Path to config with tunnels list and options (default: try ~/.i2pd/tunnels.conf or /var/lib/i2pd/tunnels.conf)")
("tunnelsdir", value<std::string>()->default_value(""), "Path to extra tunnels' configs folder (default: ~/.i2pd/tunnels.d or /var/lib/i2pd/tunnels.d") ("tunnelsdir", value<std::string>()->default_value(""), "Path to extra tunnels' configs folder (default: ~/.i2pd/tunnels.d or /var/lib/i2pd/tunnels.d")
("certsdir", value<std::string>()->default_value(""), "Path to certificates used for verifying .su3, families (default: ~/.i2pd/certificates or /var/lib/i2pd/certificates")
("pidfile", value<std::string>()->default_value(""), "Path to pidfile (default: ~/i2pd/i2pd.pid or /var/lib/i2pd/i2pd.pid)") ("pidfile", value<std::string>()->default_value(""), "Path to pidfile (default: ~/i2pd/i2pd.pid or /var/lib/i2pd/i2pd.pid)")
("log", value<std::string>()->default_value(""), "Logs destination: stdout, file, syslog (stdout if not set)") ("log", value<std::string>()->default_value(""), "Logs destination: stdout, file, syslog (stdout if not set)")
("logfile", value<std::string>()->default_value(""), "Path to logfile (stdout if not set, autodetect if daemon)") ("logfile", value<std::string>()->default_value(""), "Path to logfile (stdout if not set, autodetect if daemon)")
@ -212,7 +213,9 @@ namespace config {
"https://i2p.novg.net/" "https://i2p.novg.net/"
), "Reseed URLs, separated by comma") ), "Reseed URLs, separated by comma")
("reseed.yggurls", value<std::string>()->default_value( ("reseed.yggurls", value<std::string>()->default_value(
"http://[324:9de3:fea4:f6ac::ace]:7070/" "http://[324:71e:281a:9ed3::ace]:7070/,"
"http://[301:65b9:c7cd:9a36::1]:18801/,"
"http://[320:8936:ec1a:31f1::216]/"
), "Reseed URLs through the Yggdrasil, separated by comma") ), "Reseed URLs through the Yggdrasil, separated by comma")
; ;
@ -284,7 +287,7 @@ namespace config {
options_description meshnets("Meshnet transports options"); options_description meshnets("Meshnet transports options");
meshnets.add_options() meshnets.add_options()
("meshnets.yggdrasil", bool_switch()->default_value(false), "Support transports through the Yggdrasil (deafult: false)") ("meshnets.yggdrasil", bool_switch()->default_value(false), "Support transports through the Yggdrasil (default: false)")
("meshnets.yggaddress", value<std::string>()->default_value(""), "Yggdrasil address to publish") ("meshnets.yggaddress", value<std::string>()->default_value(""), "Yggdrasil address to publish")
; ;

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2020, The PurpleI2P Project * Copyright (c) 2013-2021, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@ -398,8 +398,9 @@ namespace crypto
} }
// ElGamal // ElGamal
void ElGamalEncrypt (const uint8_t * key, const uint8_t * data, uint8_t * encrypted, BN_CTX * ctx, bool zeroPadding) void ElGamalEncrypt (const uint8_t * key, const uint8_t * data, uint8_t * encrypted)
{ {
BN_CTX * ctx = BN_CTX_new ();
BN_CTX_start (ctx); BN_CTX_start (ctx);
// everything, but a, because a might come from table // everything, but a, because a might come from table
BIGNUM * k = BN_CTX_get (ctx); BIGNUM * k = BN_CTX_get (ctx);
@ -435,37 +436,32 @@ namespace crypto
BN_bin2bn (m, 255, b); BN_bin2bn (m, 255, b);
BN_mod_mul (b, b1, b, elgp, ctx); BN_mod_mul (b, b1, b, elgp, ctx);
// copy a and b // copy a and b
if (zeroPadding)
{
encrypted[0] = 0; encrypted[0] = 0;
bn2buf (a, encrypted + 1, 256); bn2buf (a, encrypted + 1, 256);
encrypted[257] = 0; encrypted[257] = 0;
bn2buf (b, encrypted + 258, 256); bn2buf (b, encrypted + 258, 256);
}
else
{
bn2buf (a, encrypted, 256);
bn2buf (b, encrypted + 256, 256);
}
BN_free (a); BN_free (a);
BN_CTX_end (ctx); BN_CTX_end (ctx);
BN_CTX_free (ctx);
} }
bool ElGamalDecrypt (const uint8_t * key, const uint8_t * encrypted, bool ElGamalDecrypt (const uint8_t * key, const uint8_t * encrypted, uint8_t * data)
uint8_t * data, BN_CTX * ctx, bool zeroPadding)
{ {
BN_CTX * ctx = BN_CTX_new ();
BN_CTX_start (ctx); BN_CTX_start (ctx);
BIGNUM * x = BN_CTX_get (ctx), * a = BN_CTX_get (ctx), * b = BN_CTX_get (ctx); BIGNUM * x = BN_CTX_get (ctx), * a = BN_CTX_get (ctx), * b = BN_CTX_get (ctx);
BN_bin2bn (key, 256, x); BN_bin2bn (key, 256, x);
BN_sub (x, elgp, x); BN_sub_word (x, 1); // x = elgp - x- 1 BN_sub (x, elgp, x); BN_sub_word (x, 1); // x = elgp - x- 1
BN_bin2bn (zeroPadding ? encrypted + 1 : encrypted, 256, a); BN_bin2bn (encrypted + 1, 256, a);
BN_bin2bn (zeroPadding ? encrypted + 258 : encrypted + 256, 256, b); BN_bin2bn (encrypted + 258, 256, b);
// m = b*(a^x mod p) mod p // m = b*(a^x mod p) mod p
BN_mod_exp (x, a, x, elgp, ctx); BN_mod_exp (x, a, x, elgp, ctx);
BN_mod_mul (b, b, x, elgp, ctx); BN_mod_mul (b, b, x, elgp, ctx);
uint8_t m[255]; uint8_t m[255];
bn2buf (b, m, 255); bn2buf (b, m, 255);
BN_CTX_end (ctx); BN_CTX_end (ctx);
BN_CTX_free (ctx);
uint8_t hash[32]; uint8_t hash[32];
SHA256 (m + 33, 222, hash); SHA256 (m + 33, 222, hash);
if (memcmp (m + 1, hash, 32)) if (memcmp (m + 1, hash, 32))
@ -499,8 +495,9 @@ namespace crypto
} }
// ECIES // ECIES
void ECIESEncrypt (const EC_GROUP * curve, const EC_POINT * key, const uint8_t * data, uint8_t * encrypted, BN_CTX * ctx, bool zeroPadding) void ECIESEncrypt (const EC_GROUP * curve, const EC_POINT * key, const uint8_t * data, uint8_t * encrypted)
{ {
BN_CTX * ctx = BN_CTX_new ();
BN_CTX_start (ctx); BN_CTX_start (ctx);
BIGNUM * q = BN_CTX_get (ctx); BIGNUM * q = BN_CTX_get (ctx);
EC_GROUP_get_order(curve, q, ctx); EC_GROUP_get_order(curve, q, ctx);
@ -512,19 +509,10 @@ namespace crypto
EC_POINT_mul (curve, p, k, nullptr, nullptr, ctx); EC_POINT_mul (curve, p, k, nullptr, nullptr, ctx);
BIGNUM * x = BN_CTX_get (ctx), * y = BN_CTX_get (ctx); BIGNUM * x = BN_CTX_get (ctx), * y = BN_CTX_get (ctx);
EC_POINT_get_affine_coordinates_GFp (curve, p, x, y, nullptr); EC_POINT_get_affine_coordinates_GFp (curve, p, x, y, nullptr);
if (zeroPadding)
{
encrypted[0] = 0; encrypted[0] = 0;
bn2buf (x, encrypted + 1, len); bn2buf (x, encrypted + 1, len);
bn2buf (y, encrypted + 1 + len, len); bn2buf (y, encrypted + 1 + len, len);
RAND_bytes (encrypted + 1 + 2*len, 256 - 2*len); RAND_bytes (encrypted + 1 + 2*len, 256 - 2*len);
}
else
{
bn2buf (x, encrypted, len);
bn2buf (y, encrypted + len, len);
RAND_bytes (encrypted + 2*len, 256 - 2*len);
}
// encryption key and iv // encryption key and iv
EC_POINT_mul (curve, p, nullptr, key, k, ctx); EC_POINT_mul (curve, p, nullptr, key, k, ctx);
EC_POINT_get_affine_coordinates_GFp (curve, p, x, y, nullptr); EC_POINT_get_affine_coordinates_GFp (curve, p, x, y, nullptr);
@ -541,36 +529,25 @@ namespace crypto
CBCEncryption encryption; CBCEncryption encryption;
encryption.SetKey (shared); encryption.SetKey (shared);
encryption.SetIV (iv); encryption.SetIV (iv);
if (zeroPadding)
{
encrypted[257] = 0; encrypted[257] = 0;
encryption.Encrypt (m, 256, encrypted + 258); encryption.Encrypt (m, 256, encrypted + 258);
}
else
encryption.Encrypt (m, 256, encrypted + 256);
EC_POINT_free (p); EC_POINT_free (p);
BN_CTX_end (ctx); BN_CTX_end (ctx);
BN_CTX_free (ctx);
} }
bool ECIESDecrypt (const EC_GROUP * curve, const BIGNUM * key, const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx, bool zeroPadding) bool ECIESDecrypt (const EC_GROUP * curve, const BIGNUM * key, const uint8_t * encrypted, uint8_t * data)
{ {
bool ret = true; bool ret = true;
BN_CTX * ctx = BN_CTX_new ();
BN_CTX_start (ctx); BN_CTX_start (ctx);
BIGNUM * q = BN_CTX_get (ctx); BIGNUM * q = BN_CTX_get (ctx);
EC_GROUP_get_order(curve, q, ctx); EC_GROUP_get_order(curve, q, ctx);
int len = BN_num_bytes (q); int len = BN_num_bytes (q);
// point for shared secret // point for shared secret
BIGNUM * x = BN_CTX_get (ctx), * y = BN_CTX_get (ctx); BIGNUM * x = BN_CTX_get (ctx), * y = BN_CTX_get (ctx);
if (zeroPadding)
{
BN_bin2bn (encrypted + 1, len, x); BN_bin2bn (encrypted + 1, len, x);
BN_bin2bn (encrypted + 1 + len, len, y); BN_bin2bn (encrypted + 1 + len, len, y);
}
else
{
BN_bin2bn (encrypted, len, x);
BN_bin2bn (encrypted + len, len, y);
}
auto p = EC_POINT_new (curve); auto p = EC_POINT_new (curve);
if (EC_POINT_set_affine_coordinates_GFp (curve, p, x, y, nullptr)) if (EC_POINT_set_affine_coordinates_GFp (curve, p, x, y, nullptr))
{ {
@ -587,10 +564,7 @@ namespace crypto
CBCDecryption decryption; CBCDecryption decryption;
decryption.SetKey (shared); decryption.SetKey (shared);
decryption.SetIV (iv); decryption.SetIV (iv);
if (zeroPadding)
decryption.Decrypt (encrypted + 258, 256, m); decryption.Decrypt (encrypted + 258, 256, m);
else
decryption.Decrypt (encrypted + 256, 256, m);
// verify and copy // verify and copy
uint8_t hash[32]; uint8_t hash[32];
SHA256 (m + 33, 222, hash); SHA256 (m + 33, 222, hash);
@ -610,6 +584,7 @@ namespace crypto
EC_POINT_free (p); EC_POINT_free (p);
BN_CTX_end (ctx); BN_CTX_end (ctx);
BN_CTX_free (ctx);
return ret; return ret;
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2020, The PurpleI2P Project * Copyright (c) 2013-2021, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@ -108,13 +108,13 @@ namespace crypto
}; };
// ElGamal // ElGamal
void ElGamalEncrypt (const uint8_t * key, const uint8_t * data, uint8_t * encrypted, BN_CTX * ctx, bool zeroPadding = false); void ElGamalEncrypt (const uint8_t * key, const uint8_t * data, uint8_t * encrypted); // 222 bytes data, 514 bytes encrypted
bool ElGamalDecrypt (const uint8_t * key, const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx, bool zeroPadding = false); bool ElGamalDecrypt (const uint8_t * key, const uint8_t * encrypted, uint8_t * data); // 514 bytes encrypted, 222 data
void GenerateElGamalKeyPair (uint8_t * priv, uint8_t * pub); void GenerateElGamalKeyPair (uint8_t * priv, uint8_t * pub);
// ECIES // ECIES
void ECIESEncrypt (const EC_GROUP * curve, const EC_POINT * key, const uint8_t * data, uint8_t * encrypted, BN_CTX * ctx, bool zeroPadding = false); // 222 bytes data, 514 bytes encrypted with zeropadding, 512 without void ECIESEncrypt (const EC_GROUP * curve, const EC_POINT * key, const uint8_t * data, uint8_t * encrypted); // 222 bytes data, 514 bytes encrypted
bool ECIESDecrypt (const EC_GROUP * curve, const BIGNUM * key, const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx, bool zeroPadding = false); bool ECIESDecrypt (const EC_GROUP * curve, const BIGNUM * key, const uint8_t * encrypted, uint8_t * data); // 514 bytes encrypted, 222 data
void GenerateECIESKeyPair (const EC_GROUP * curve, BIGNUM *& priv, EC_POINT *& pub); void GenerateECIESKeyPair (const EC_GROUP * curve, BIGNUM *& priv, EC_POINT *& pub);
// HMAC // HMAC

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2020, The PurpleI2P Project * Copyright (c) 2013-2021, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@ -20,10 +20,9 @@ namespace crypto
memcpy (m_PublicKey, pub, 256); memcpy (m_PublicKey, pub, 256);
} }
void ElGamalEncryptor::Encrypt (const uint8_t * data, uint8_t * encrypted, BN_CTX * ctx, bool zeroPadding) void ElGamalEncryptor::Encrypt (const uint8_t * data, uint8_t * encrypted)
{ {
if (!ctx) return; ElGamalEncrypt (m_PublicKey, data, encrypted);
ElGamalEncrypt (m_PublicKey, data, encrypted, ctx, zeroPadding);
} }
ElGamalDecryptor::ElGamalDecryptor (const uint8_t * priv) ElGamalDecryptor::ElGamalDecryptor (const uint8_t * priv)
@ -31,10 +30,9 @@ namespace crypto
memcpy (m_PrivateKey, priv, 256); memcpy (m_PrivateKey, priv, 256);
} }
bool ElGamalDecryptor::Decrypt (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx, bool zeroPadding) bool ElGamalDecryptor::Decrypt (const uint8_t * encrypted, uint8_t * data)
{ {
if (!ctx) return false; return ElGamalDecrypt (m_PrivateKey, encrypted, data);
return ElGamalDecrypt (m_PrivateKey, encrypted, data, ctx, zeroPadding);
} }
ECIESP256Encryptor::ECIESP256Encryptor (const uint8_t * pub) ECIESP256Encryptor::ECIESP256Encryptor (const uint8_t * pub)
@ -54,10 +52,10 @@ namespace crypto
if (m_PublicKey) EC_POINT_free (m_PublicKey); if (m_PublicKey) EC_POINT_free (m_PublicKey);
} }
void ECIESP256Encryptor::Encrypt (const uint8_t * data, uint8_t * encrypted, BN_CTX * ctx, bool zeroPadding) void ECIESP256Encryptor::Encrypt (const uint8_t * data, uint8_t * encrypted)
{ {
if (m_Curve && m_PublicKey) if (m_Curve && m_PublicKey)
ECIESEncrypt (m_Curve, m_PublicKey, data, encrypted, ctx, zeroPadding); ECIESEncrypt (m_Curve, m_PublicKey, data, encrypted);
} }
ECIESP256Decryptor::ECIESP256Decryptor (const uint8_t * priv) ECIESP256Decryptor::ECIESP256Decryptor (const uint8_t * priv)
@ -72,10 +70,10 @@ namespace crypto
if (m_PrivateKey) BN_free (m_PrivateKey); if (m_PrivateKey) BN_free (m_PrivateKey);
} }
bool ECIESP256Decryptor::Decrypt (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx, bool zeroPadding) bool ECIESP256Decryptor::Decrypt (const uint8_t * encrypted, uint8_t * data)
{ {
if (m_Curve && m_PrivateKey) if (m_Curve && m_PrivateKey)
return ECIESDecrypt (m_Curve, m_PrivateKey, encrypted, data, ctx, zeroPadding); return ECIESDecrypt (m_Curve, m_PrivateKey, encrypted, data);
return false; return false;
} }
@ -114,10 +112,10 @@ namespace crypto
if (m_PublicKey) EC_POINT_free (m_PublicKey); if (m_PublicKey) EC_POINT_free (m_PublicKey);
} }
void ECIESGOSTR3410Encryptor::Encrypt (const uint8_t * data, uint8_t * encrypted, BN_CTX * ctx, bool zeroPadding) void ECIESGOSTR3410Encryptor::Encrypt (const uint8_t * data, uint8_t * encrypted)
{ {
if (m_PublicKey) if (m_PublicKey)
ECIESEncrypt (GetGOSTR3410Curve (eGOSTR3410CryptoProA)->GetGroup (), m_PublicKey, data, encrypted, ctx, zeroPadding); ECIESEncrypt (GetGOSTR3410Curve (eGOSTR3410CryptoProA)->GetGroup (), m_PublicKey, data, encrypted);
} }
ECIESGOSTR3410Decryptor::ECIESGOSTR3410Decryptor (const uint8_t * priv) ECIESGOSTR3410Decryptor::ECIESGOSTR3410Decryptor (const uint8_t * priv)
@ -130,10 +128,10 @@ namespace crypto
if (m_PrivateKey) BN_free (m_PrivateKey); if (m_PrivateKey) BN_free (m_PrivateKey);
} }
bool ECIESGOSTR3410Decryptor::Decrypt (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx, bool zeroPadding) bool ECIESGOSTR3410Decryptor::Decrypt (const uint8_t * encrypted, uint8_t * data)
{ {
if (m_PrivateKey) if (m_PrivateKey)
return ECIESDecrypt (GetGOSTR3410Curve (eGOSTR3410CryptoProA)->GetGroup (), m_PrivateKey, encrypted, data, ctx, zeroPadding); return ECIESDecrypt (GetGOSTR3410Curve (eGOSTR3410CryptoProA)->GetGroup (), m_PrivateKey, encrypted, data);
return false; return false;
} }
@ -161,7 +159,7 @@ namespace crypto
memcpy (m_PublicKey, pub, 32); memcpy (m_PublicKey, pub, 32);
} }
void ECIESX25519AEADRatchetEncryptor::Encrypt (const uint8_t *, uint8_t * pub, BN_CTX *, bool) void ECIESX25519AEADRatchetEncryptor::Encrypt (const uint8_t *, uint8_t * pub)
{ {
memcpy (pub, m_PublicKey, 32); memcpy (pub, m_PublicKey, 32);
} }
@ -171,7 +169,7 @@ namespace crypto
m_StaticKeys.SetPrivateKey (priv, calculatePublic); m_StaticKeys.SetPrivateKey (priv, calculatePublic);
} }
bool ECIESX25519AEADRatchetDecryptor::Decrypt (const uint8_t * epub, uint8_t * sharedSecret, BN_CTX * ctx, bool zeroPadding) bool ECIESX25519AEADRatchetDecryptor::Decrypt (const uint8_t * epub, uint8_t * sharedSecret)
{ {
return m_StaticKeys.Agree (epub, sharedSecret); return m_StaticKeys.Agree (epub, sharedSecret);
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2020, The PurpleI2P Project * Copyright (c) 2013-2021, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@ -21,7 +21,7 @@ namespace crypto
public: public:
virtual ~CryptoKeyEncryptor () {}; virtual ~CryptoKeyEncryptor () {};
virtual void Encrypt (const uint8_t * data, uint8_t * encrypted, BN_CTX * ctx, bool zeroPadding) = 0; // 222 bytes data, 512/514 bytes encrypted virtual void Encrypt (const uint8_t * data, uint8_t * encrypted) = 0;
}; };
class CryptoKeyDecryptor class CryptoKeyDecryptor
@ -29,7 +29,7 @@ namespace crypto
public: public:
virtual ~CryptoKeyDecryptor () {}; virtual ~CryptoKeyDecryptor () {};
virtual bool Decrypt (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx, bool zeroPadding) = 0; // 512/514 bytes encrypted, 222 bytes data virtual bool Decrypt (const uint8_t * encrypted, uint8_t * data) = 0;
virtual size_t GetPublicKeyLen () const = 0; // we need it to set key in LS2 virtual size_t GetPublicKeyLen () const = 0; // we need it to set key in LS2
}; };
@ -39,7 +39,7 @@ namespace crypto
public: public:
ElGamalEncryptor (const uint8_t * pub); ElGamalEncryptor (const uint8_t * pub);
void Encrypt (const uint8_t * data, uint8_t * encrypted, BN_CTX * ctx, bool zeroPadding); void Encrypt (const uint8_t * data, uint8_t * encrypted) override; // 222 bytes data, 514 bytes encrypted
private: private:
@ -51,8 +51,8 @@ namespace crypto
public: public:
ElGamalDecryptor (const uint8_t * priv); ElGamalDecryptor (const uint8_t * priv);
bool Decrypt (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx, bool zeroPadding); bool Decrypt (const uint8_t * encrypted, uint8_t * data) override; // 514 bytes encrypted, 222 bytes data
size_t GetPublicKeyLen () const { return 256; }; size_t GetPublicKeyLen () const override { return 256; };
private: private:
@ -67,7 +67,7 @@ namespace crypto
ECIESP256Encryptor (const uint8_t * pub); ECIESP256Encryptor (const uint8_t * pub);
~ECIESP256Encryptor (); ~ECIESP256Encryptor ();
void Encrypt (const uint8_t * data, uint8_t * encrypted, BN_CTX * ctx, bool zeroPadding); void Encrypt (const uint8_t * data, uint8_t * encrypted) override;
private: private:
@ -82,8 +82,8 @@ namespace crypto
ECIESP256Decryptor (const uint8_t * priv); ECIESP256Decryptor (const uint8_t * priv);
~ECIESP256Decryptor (); ~ECIESP256Decryptor ();
bool Decrypt (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx, bool zeroPadding); bool Decrypt (const uint8_t * encrypted, uint8_t * data) override;
size_t GetPublicKeyLen () const { return 64; }; size_t GetPublicKeyLen () const override { return 64; };
private: private:
@ -101,7 +101,7 @@ namespace crypto
ECIESGOSTR3410Encryptor (const uint8_t * pub); ECIESGOSTR3410Encryptor (const uint8_t * pub);
~ECIESGOSTR3410Encryptor (); ~ECIESGOSTR3410Encryptor ();
void Encrypt (const uint8_t * data, uint8_t * encrypted, BN_CTX * ctx, bool zeroPadding); void Encrypt (const uint8_t * data, uint8_t * encrypted) override;
private: private:
@ -115,8 +115,8 @@ namespace crypto
ECIESGOSTR3410Decryptor (const uint8_t * priv); ECIESGOSTR3410Decryptor (const uint8_t * priv);
~ECIESGOSTR3410Decryptor (); ~ECIESGOSTR3410Decryptor ();
bool Decrypt (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx, bool zeroPadding); bool Decrypt (const uint8_t * encrypted, uint8_t * data) override;
size_t GetPublicKeyLen () const { return 64; }; size_t GetPublicKeyLen () const override { return 64; };
private: private:
@ -133,7 +133,7 @@ namespace crypto
ECIESX25519AEADRatchetEncryptor (const uint8_t * pub); ECIESX25519AEADRatchetEncryptor (const uint8_t * pub);
~ECIESX25519AEADRatchetEncryptor () {}; ~ECIESX25519AEADRatchetEncryptor () {};
void Encrypt (const uint8_t *, uint8_t * pub, BN_CTX *, bool); void Encrypt (const uint8_t *, uint8_t * pub) override;
// copies m_PublicKey to pub // copies m_PublicKey to pub
private: private:
@ -147,9 +147,9 @@ namespace crypto
ECIESX25519AEADRatchetDecryptor (const uint8_t * priv, bool calculatePublic = false); ECIESX25519AEADRatchetDecryptor (const uint8_t * priv, bool calculatePublic = false);
~ECIESX25519AEADRatchetDecryptor () {}; ~ECIESX25519AEADRatchetDecryptor () {};
bool Decrypt (const uint8_t * epub, uint8_t * sharedSecret, BN_CTX * ctx, bool zeroPadding); bool Decrypt (const uint8_t * epub, uint8_t * sharedSecret) override;
// agree with static and return in sharedSecret (32 bytes) // agree with static and return in sharedSecret (32 bytes)
size_t GetPublicKeyLen () const { return 32; }; size_t GetPublicKeyLen () const override { return 32; };
const uint8_t * GetPubicKey () const { return m_StaticKeys.GetPublicKey (); }; const uint8_t * GetPubicKey () const { return m_StaticKeys.GetPublicKey (); };
private: private:

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2020, The PurpleI2P Project * Copyright (c) 2013-2021, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@ -21,6 +21,9 @@ namespace datagram
DatagramDestination::DatagramDestination (std::shared_ptr<i2p::client::ClientDestination> owner, bool gzip): DatagramDestination::DatagramDestination (std::shared_ptr<i2p::client::ClientDestination> owner, bool gzip):
m_Owner (owner), m_Receiver (nullptr), m_RawReceiver (nullptr), m_Gzip (gzip) m_Owner (owner), m_Receiver (nullptr), m_RawReceiver (nullptr), m_Gzip (gzip)
{ {
if (m_Gzip)
m_Deflator.reset (new i2p::data::GzipDeflator);
auto identityLen = m_Owner->GetIdentity ()->GetFullLen (); auto identityLen = m_Owner->GetIdentity ()->GetFullLen ();
m_From.resize (identityLen); m_From.resize (identityLen);
m_Owner->GetIdentity ()->ToBuffer (m_From.data (), identityLen); m_Owner->GetIdentity ()->ToBuffer (m_From.data (), identityLen);
@ -152,11 +155,16 @@ namespace datagram
const std::vector<std::pair<const uint8_t *, size_t> >& payloads, const std::vector<std::pair<const uint8_t *, size_t> >& payloads,
uint16_t fromPort, uint16_t toPort, bool isRaw, bool checksum) uint16_t fromPort, uint16_t toPort, bool isRaw, bool checksum)
{ {
size_t size;
auto msg = m_I2NPMsgsPool.AcquireShared (); auto msg = m_I2NPMsgsPool.AcquireShared ();
uint8_t * buf = msg->GetPayload (); uint8_t * buf = msg->GetPayload ();
buf += 4; // reserve for length buf += 4; // reserve for length
size_t size = m_Gzip ? m_Deflator.Deflate (payloads, buf, msg->maxLen - msg->len) :
i2p::data::GzipNoCompression (payloads, buf, msg->maxLen - msg->len); if (m_Gzip && m_Deflator)
size = m_Deflator->Deflate (payloads, buf, msg->maxLen - msg->len);
else
size = i2p::data::GzipNoCompression (payloads, buf, msg->maxLen - msg->len);
if (size) if (size)
{ {
htobe32buf (msg->GetPayload (), size); // length htobe32buf (msg->GetPayload (), size); // length

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2020, The PurpleI2P Project * Copyright (c) 2013-2021, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@ -164,7 +164,7 @@ namespace datagram
std::map<uint16_t, Receiver> m_ReceiversByPorts; std::map<uint16_t, Receiver> m_ReceiversByPorts;
i2p::data::GzipInflator m_Inflator; i2p::data::GzipInflator m_Inflator;
i2p::data::GzipDeflator m_Deflator; std::unique_ptr<i2p::data::GzipDeflator> m_Deflator;
std::vector<uint8_t> m_From, m_Signature; std::vector<uint8_t> m_From, m_Signature;
i2p::util::MemoryPool<I2NPMessageBuffer<I2NP_MAX_MESSAGE_SIZE> > m_I2NPMsgsPool; i2p::util::MemoryPool<I2NPMessageBuffer<I2NP_MAX_MESSAGE_SIZE> > m_I2NPMsgsPool;
}; };

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2020, The PurpleI2P Project * Copyright (c) 2013-2021, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@ -345,10 +345,11 @@ namespace client
void LeaseSetDestination::HandleI2NPMessage (const uint8_t * buf, size_t len) void LeaseSetDestination::HandleI2NPMessage (const uint8_t * buf, size_t len)
{ {
I2NPMessageType typeID = (I2NPMessageType)(buf[I2NP_HEADER_TYPEID_OFFSET]); I2NPMessageType typeID = (I2NPMessageType)(buf[I2NP_HEADER_TYPEID_OFFSET]);
LeaseSetDestination::HandleCloveI2NPMessage (typeID, buf + I2NP_HEADER_SIZE, GetI2NPMessageLength(buf, len) - I2NP_HEADER_SIZE); uint32_t msgID = bufbe32toh (buf + I2NP_HEADER_MSGID_OFFSET);
LeaseSetDestination::HandleCloveI2NPMessage (typeID, buf + I2NP_HEADER_SIZE, GetI2NPMessageLength(buf, len) - I2NP_HEADER_SIZE, msgID);
} }
bool LeaseSetDestination::HandleCloveI2NPMessage (I2NPMessageType typeID, const uint8_t * payload, size_t len) bool LeaseSetDestination::HandleCloveI2NPMessage (I2NPMessageType typeID, const uint8_t * payload, size_t len, uint32_t msgID)
{ {
switch (typeID) switch (typeID)
{ {
@ -365,6 +366,9 @@ namespace client
case eI2NPDatabaseSearchReply: case eI2NPDatabaseSearchReply:
HandleDatabaseSearchReplyMessage (payload, len); HandleDatabaseSearchReplyMessage (payload, len);
break; break;
case eI2NPShortTunnelBuildReply: // might come as garlic encrypted
i2p::HandleI2NPMessage (CreateI2NPMessage (typeID, payload, len, msgID));
break;
default: default:
LogPrint (eLogWarning, "Destination: Unexpected I2NP message type ", typeID); LogPrint (eLogWarning, "Destination: Unexpected I2NP message type ", typeID);
return false; return false;
@ -1092,6 +1096,35 @@ namespace client
return nullptr; return nullptr;
} }
void ClientDestination::SendPing (const i2p::data::IdentHash& to)
{
if (m_StreamingDestination)
{
auto leaseSet = FindLeaseSet (to);
if (leaseSet)
m_StreamingDestination->SendPing (leaseSet);
else
{
auto s = m_StreamingDestination;
RequestDestination (to,
[s](std::shared_ptr<const i2p::data::LeaseSet> ls)
{
if (ls) s->SendPing (ls);
});
}
}
}
void ClientDestination::SendPing (std::shared_ptr<const i2p::data::BlindedPublicKey> to)
{
auto s = m_StreamingDestination;
RequestDestinationWithEncryptedLeaseSet (to,
[s](std::shared_ptr<const i2p::data::LeaseSet> ls)
{
if (ls) s->SendPing (ls);
});
}
std::shared_ptr<i2p::stream::StreamingDestination> ClientDestination::GetStreamingDestination (int port) const std::shared_ptr<i2p::stream::StreamingDestination> ClientDestination::GetStreamingDestination (int port) const
{ {
if (port) if (port)
@ -1241,13 +1274,13 @@ namespace client
if (m_DatagramDestination) m_DatagramDestination->CleanUp (); if (m_DatagramDestination) m_DatagramDestination->CleanUp ();
} }
bool ClientDestination::Decrypt (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx, i2p::data::CryptoKeyType preferredCrypto) const bool ClientDestination::Decrypt (const uint8_t * encrypted, uint8_t * data, i2p::data::CryptoKeyType preferredCrypto) const
{ {
if (preferredCrypto == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD) if (preferredCrypto == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD)
if (m_ECIESx25519EncryptionKey && m_ECIESx25519EncryptionKey->decryptor) if (m_ECIESx25519EncryptionKey && m_ECIESx25519EncryptionKey->decryptor)
return m_ECIESx25519EncryptionKey->decryptor->Decrypt (encrypted, data, ctx, true); return m_ECIESx25519EncryptionKey->decryptor->Decrypt (encrypted, data);
if (m_StandardEncryptionKey && m_StandardEncryptionKey->decryptor) if (m_StandardEncryptionKey && m_StandardEncryptionKey->decryptor)
return m_StandardEncryptionKey->decryptor->Decrypt (encrypted, data, ctx, true); return m_StandardEncryptionKey->decryptor->Decrypt (encrypted, data);
else else
LogPrint (eLogError, "Destinations: decryptor is not set"); LogPrint (eLogError, "Destinations: decryptor is not set");
return false; return false;

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2020, The PurpleI2P Project * Copyright (c) 2013-2021, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@ -139,12 +139,13 @@ namespace client
void SetLeaseSetUpdated (); void SetLeaseSetUpdated ();
bool IsPublic () const { return m_IsPublic; }; bool IsPublic () const { return m_IsPublic; };
void SetPublic (bool pub) { m_IsPublic = pub; };
protected: protected:
// implements GarlicDestination // implements GarlicDestination
void HandleI2NPMessage (const uint8_t * buf, size_t len); void HandleI2NPMessage (const uint8_t * buf, size_t len);
bool HandleCloveI2NPMessage (I2NPMessageType typeID, const uint8_t * payload, size_t len); bool HandleCloveI2NPMessage (I2NPMessageType typeID, const uint8_t * payload, size_t len, uint32_t msgID);
void SetLeaseSet (std::shared_ptr<const i2p::data::LocalLeaseSet> newLeaseSet); void SetLeaseSet (std::shared_ptr<const i2p::data::LocalLeaseSet> newLeaseSet);
int GetLeaseSetType () const { return m_LeaseSetType; }; int GetLeaseSetType () const { return m_LeaseSetType; };
@ -242,6 +243,8 @@ namespace client
void CreateStream (StreamRequestComplete streamRequestComplete, const i2p::data::IdentHash& dest, int port = 0); void CreateStream (StreamRequestComplete streamRequestComplete, const i2p::data::IdentHash& dest, int port = 0);
void CreateStream (StreamRequestComplete streamRequestComplete, std::shared_ptr<const i2p::data::BlindedPublicKey> dest, int port = 0); void CreateStream (StreamRequestComplete streamRequestComplete, std::shared_ptr<const i2p::data::BlindedPublicKey> dest, int port = 0);
std::shared_ptr<i2p::stream::Stream> CreateStream (std::shared_ptr<const i2p::data::LeaseSet> remote, int port = 0); std::shared_ptr<i2p::stream::Stream> CreateStream (std::shared_ptr<const i2p::data::LeaseSet> remote, int port = 0);
void SendPing (const i2p::data::IdentHash& to);
void SendPing (std::shared_ptr<const i2p::data::BlindedPublicKey> to);
void AcceptStreams (const i2p::stream::StreamingDestination::Acceptor& acceptor); void AcceptStreams (const i2p::stream::StreamingDestination::Acceptor& acceptor);
void StopAcceptingStreams (); void StopAcceptingStreams ();
bool IsAcceptingStreams () const; bool IsAcceptingStreams () const;
@ -254,7 +257,7 @@ namespace client
i2p::datagram::DatagramDestination * CreateDatagramDestination (bool gzip = true); i2p::datagram::DatagramDestination * CreateDatagramDestination (bool gzip = true);
// implements LocalDestination // implements LocalDestination
bool Decrypt (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx, i2p::data::CryptoKeyType preferredCrypto) const; bool Decrypt (const uint8_t * encrypted, uint8_t * data, i2p::data::CryptoKeyType preferredCrypto) const;
std::shared_ptr<const i2p::data::IdentityEx> GetIdentity () const { return m_Keys.GetPublic (); }; std::shared_ptr<const i2p::data::IdentityEx> GetIdentity () const { return m_Keys.GetPublic (); };
bool SupportsEncryptionType (i2p::data::CryptoKeyType keyType) const; bool SupportsEncryptionType (i2p::data::CryptoKeyType keyType) const;
const uint8_t * GetEncryptionPublicKey (i2p::data::CryptoKeyType keyType) const; const uint8_t * GetEncryptionPublicKey (i2p::data::CryptoKeyType keyType) const;

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2020, The PurpleI2P Project * Copyright (c) 2013-2021, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@ -31,16 +31,16 @@ namespace garlic
uint8_t keydata[64]; uint8_t keydata[64];
i2p::crypto::HKDF (rootKey, k, 32, "KDFDHRatchetStep", keydata); // keydata = HKDF(rootKey, k, "KDFDHRatchetStep", 64) i2p::crypto::HKDF (rootKey, k, 32, "KDFDHRatchetStep", keydata); // keydata = HKDF(rootKey, k, "KDFDHRatchetStep", 64)
memcpy (m_NextRootKey, keydata, 32); // nextRootKey = keydata[0:31] memcpy (m_NextRootKey, keydata, 32); // nextRootKey = keydata[0:31]
i2p::crypto::HKDF (keydata + 32, nullptr, 0, "TagAndKeyGenKeys", m_KeyData.buf); i2p::crypto::HKDF (keydata + 32, nullptr, 0, "TagAndKeyGenKeys", m_SessionTagKeyData);
// [sessTag_ck, symmKey_ck] = HKDF(keydata[32:63], ZEROLEN, "TagAndKeyGenKeys", 64) // [sessTag_ck, symmKey_ck] = HKDF(keydata[32:63], ZEROLEN, "TagAndKeyGenKeys", 64)
memcpy (m_SymmKeyCK, m_KeyData.buf + 32, 32); memcpy (m_SymmKeyCK, (const uint8_t *)m_SessionTagKeyData + 32, 32);
m_NextSymmKeyIndex = 0; m_NextSymmKeyIndex = 0;
} }
void RatchetTagSet::NextSessionTagRatchet () void RatchetTagSet::NextSessionTagRatchet ()
{ {
i2p::crypto::HKDF (m_KeyData.GetSessTagCK (), nullptr, 0, "STInitialization", m_KeyData.buf); // [sessTag_ck, sesstag_constant] = HKDF(sessTag_ck, ZEROLEN, "STInitialization", 64) i2p::crypto::HKDF (m_SessionTagKeyData, nullptr, 0, "STInitialization", m_SessionTagKeyData); // [sessTag_ck, sesstag_constant] = HKDF(sessTag_ck, ZEROLEN, "STInitialization", 64)
memcpy (m_SessTagConstant, m_KeyData.GetSessTagConstant (), 32); memcpy (m_SessTagConstant, (const uint8_t *)m_SessionTagKeyData + 32, 32); // SESSTAG_CONSTANT = keydata[32:63]
m_NextIndex = 0; m_NextIndex = 0;
} }
@ -52,8 +52,8 @@ namespace garlic
LogPrint (eLogError, "Garlic: Tagset ", GetTagSetID (), " is empty"); LogPrint (eLogError, "Garlic: Tagset ", GetTagSetID (), " is empty");
return 0; return 0;
} }
i2p::crypto::HKDF (m_KeyData.GetSessTagCK (), m_SessTagConstant, 32, "SessionTagKeyGen", m_KeyData.buf); // [sessTag_ck, tag] = HKDF(sessTag_chainkey, SESSTAG_CONSTANT, "SessionTagKeyGen", 64) i2p::crypto::HKDF (m_SessionTagKeyData, m_SessTagConstant, 32, "SessionTagKeyGen", m_SessionTagKeyData); // [sessTag_ck, tag] = HKDF(sessTag_chainkey, SESSTAG_CONSTANT, "SessionTagKeyGen", 64)
return m_KeyData.GetTag (); return m_SessionTagKeyData.GetLL ()[4]; // tag = keydata[32:39]
} }
void RatchetTagSet::GetSymmKey (int index, uint8_t * key) void RatchetTagSet::GetSymmKey (int index, uint8_t * key)
@ -239,7 +239,7 @@ namespace garlic
MixHash (m_Aepk, 32); // h = SHA256(h || aepk) MixHash (m_Aepk, 32); // h = SHA256(h || aepk)
uint8_t sharedSecret[32]; uint8_t sharedSecret[32];
if (!GetOwner ()->Decrypt (m_Aepk, sharedSecret, nullptr, i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD)) // x25519(bsk, aepk) if (!GetOwner ()->Decrypt (m_Aepk, sharedSecret, i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD)) // x25519(bsk, aepk)
{ {
LogPrint (eLogWarning, "Garlic: Incorrect Alice ephemeral key"); LogPrint (eLogWarning, "Garlic: Incorrect Alice ephemeral key");
return false; return false;
@ -263,7 +263,7 @@ namespace garlic
{ {
// static key, fs is apk // static key, fs is apk
memcpy (m_RemoteStaticKey, fs, 32); memcpy (m_RemoteStaticKey, fs, 32);
if (!GetOwner ()->Decrypt (fs, sharedSecret, nullptr, i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD)) // x25519(bsk, apk) if (!GetOwner ()->Decrypt (fs, sharedSecret, i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD)) // x25519(bsk, apk)
{ {
LogPrint (eLogWarning, "Garlic: Incorrect Alice static key"); LogPrint (eLogWarning, "Garlic: Incorrect Alice static key");
return false; return false;
@ -492,7 +492,7 @@ namespace garlic
// KDF2 // KDF2
if (isStatic) if (isStatic)
{ {
GetOwner ()->Decrypt (m_RemoteStaticKey, sharedSecret, nullptr, i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD); // x25519 (ask, bpk) GetOwner ()->Decrypt (m_RemoteStaticKey, sharedSecret, i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD); // x25519 (ask, bpk)
MixKey (sharedSecret); MixKey (sharedSecret);
} }
else else
@ -519,35 +519,6 @@ namespace garlic
return true; return true;
} }
bool ECIESX25519AEADRatchetSession::NewOutgoingMessageForRouter (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen)
{
// we are Alice, router's bpk is m_RemoteStaticKey
i2p::crypto::InitNoiseNState (GetNoiseState (), m_RemoteStaticKey);
size_t offset = 0;
m_EphemeralKeys = i2p::transport::transports.GetNextX25519KeysPair ();
memcpy (out + offset, m_EphemeralKeys->GetPublicKey (), 32);
MixHash (out + offset, 32); // h = SHA256(h || aepk)
offset += 32;
uint8_t sharedSecret[32];
if (!m_EphemeralKeys->Agree (m_RemoteStaticKey, sharedSecret)) // x25519(aesk, bpk)
{
LogPrint (eLogWarning, "Garlic: Incorrect Bob static key");
return false;
}
MixKey (sharedSecret);
uint8_t nonce[12];
CreateNonce (0, nonce);
// encrypt payload
if (!i2p::crypto::AEADChaCha20Poly1305 (payload, len, m_H, 32, m_CK + 32, nonce, out + offset, len + 16, true)) // encrypt
{
LogPrint (eLogWarning, "Garlic: Payload for router AEAD encryption failed");
return false;
}
m_State = eSessionStateNewSessionSent;
return true;
}
bool ECIESX25519AEADRatchetSession::NewSessionReplyMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen) bool ECIESX25519AEADRatchetSession::NewSessionReplyMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen)
{ {
// we are Bob // we are Bob
@ -668,7 +639,7 @@ namespace garlic
return false; return false;
} }
MixKey (sharedSecret); MixKey (sharedSecret);
GetOwner ()->Decrypt (bepk, sharedSecret, nullptr, i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD); // x25519 (ask, bepk) GetOwner ()->Decrypt (bepk, sharedSecret, i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD); // x25519 (ask, bepk)
MixKey (sharedSecret); MixKey (sharedSecret);
uint8_t nonce[12]; uint8_t nonce[12];
@ -824,8 +795,9 @@ namespace garlic
std::shared_ptr<I2NPMessage> ECIESX25519AEADRatchetSession::WrapSingleMessage (std::shared_ptr<const I2NPMessage> msg) std::shared_ptr<I2NPMessage> ECIESX25519AEADRatchetSession::WrapSingleMessage (std::shared_ptr<const I2NPMessage> msg)
{ {
auto payload = CreatePayload (msg, m_State != eSessionStateEstablished); uint8_t * payload = GetOwner ()->GetPayloadBuffer ();
size_t len = payload.size (); if (!payload) return nullptr;
size_t len = CreatePayload (msg, m_State != eSessionStateEstablished, payload);
if (!len) return nullptr; if (!len) return nullptr;
auto m = NewI2NPMessage (len + 100); // 96 + 4 auto m = NewI2NPMessage (len + 100); // 96 + 4
m->Align (12); // in order to get buf aligned to 16 (12 + 4) m->Align (12); // in order to get buf aligned to 16 (12 + 4)
@ -834,35 +806,30 @@ namespace garlic
switch (m_State) switch (m_State)
{ {
case eSessionStateEstablished: case eSessionStateEstablished:
if (!NewExistingSessionMessage (payload.data (), payload.size (), buf, m->maxLen)) if (!NewExistingSessionMessage (payload, len, buf, m->maxLen))
return nullptr; return nullptr;
len += 24; len += 24;
break; break;
case eSessionStateNew: case eSessionStateNew:
if (!NewOutgoingSessionMessage (payload.data (), payload.size (), buf, m->maxLen)) if (!NewOutgoingSessionMessage (payload, len, buf, m->maxLen))
return nullptr; return nullptr;
len += 96; len += 96;
break; break;
case eSessionStateNewSessionReceived: case eSessionStateNewSessionReceived:
if (!NewSessionReplyMessage (payload.data (), payload.size (), buf, m->maxLen)) if (!NewSessionReplyMessage (payload, len, buf, m->maxLen))
return nullptr; return nullptr;
len += 72; len += 72;
break; break;
case eSessionStateNewSessionReplySent: case eSessionStateNewSessionReplySent:
if (!NextNewSessionReplyMessage (payload.data (), payload.size (), buf, m->maxLen)) if (!NextNewSessionReplyMessage (payload, len, buf, m->maxLen))
return nullptr; return nullptr;
len += 72; len += 72;
break; break;
case eSessionStateOneTime: case eSessionStateOneTime:
if (!NewOutgoingSessionMessage (payload.data (), payload.size (), buf, m->maxLen, false)) if (!NewOutgoingSessionMessage (payload, len, buf, m->maxLen, false))
return nullptr; return nullptr;
len += 96; len += 96;
break; break;
case eSessionStateForRouter:
if (!NewOutgoingMessageForRouter (payload.data (), payload.size (), buf, m->maxLen))
return nullptr;
len += 48;
break;
default: default:
return nullptr; return nullptr;
} }
@ -873,13 +840,13 @@ namespace garlic
return m; return m;
} }
std::shared_ptr<I2NPMessage> ECIESX25519AEADRatchetSession::WrapOneTimeMessage (std::shared_ptr<const I2NPMessage> msg, bool isForRouter) std::shared_ptr<I2NPMessage> ECIESX25519AEADRatchetSession::WrapOneTimeMessage (std::shared_ptr<const I2NPMessage> msg)
{ {
m_State = isForRouter ? eSessionStateForRouter : eSessionStateOneTime; m_State = eSessionStateOneTime;
return WrapSingleMessage (msg); return WrapSingleMessage (msg);
} }
std::vector<uint8_t> ECIESX25519AEADRatchetSession::CreatePayload (std::shared_ptr<const I2NPMessage> msg, bool first) size_t ECIESX25519AEADRatchetSession::CreatePayload (std::shared_ptr<const I2NPMessage> msg, bool first, uint8_t * payload)
{ {
uint64_t ts = i2p::util::GetMillisecondsSinceEpoch (); uint64_t ts = i2p::util::GetMillisecondsSinceEpoch ();
size_t payloadLen = 0; size_t payloadLen = 0;
@ -941,89 +908,93 @@ namespace garlic
payloadLen += paddingSize + 3; payloadLen += paddingSize + 3;
} }
} }
std::vector<uint8_t> v(payloadLen);
if (payloadLen) if (payloadLen)
{ {
if (payloadLen > I2NP_MAX_MESSAGE_SIZE)
{
LogPrint (eLogError, "Garlic: payload length ", payloadLen, " is too long");
return 0;
}
m_LastSentTimestamp = ts; m_LastSentTimestamp = ts;
size_t offset = 0; size_t offset = 0;
// DateTime // DateTime
if (first) if (first)
{ {
v[offset] = eECIESx25519BlkDateTime; offset++; payload[offset] = eECIESx25519BlkDateTime; offset++;
htobe16buf (v.data () + offset, 4); offset += 2; htobe16buf (payload + offset, 4); offset += 2;
htobe32buf (v.data () + offset, ts/1000); offset += 4; // in seconds htobe32buf (payload + offset, ts/1000); offset += 4; // in seconds
} }
// LeaseSet // LeaseSet
if (leaseSet) if (leaseSet)
{ {
offset += CreateLeaseSetClove (leaseSet, ts, v.data () + offset, payloadLen - offset); offset += CreateLeaseSetClove (leaseSet, ts, payload + offset, payloadLen - offset);
if (!first) if (!first)
{ {
// ack request // ack request
v[offset] = eECIESx25519BlkAckRequest; offset++; payload[offset] = eECIESx25519BlkAckRequest; offset++;
htobe16buf (v.data () + offset, 1); offset += 2; htobe16buf (payload + offset, 1); offset += 2;
v[offset] = 0; offset++; // flags payload[offset] = 0; offset++; // flags
} }
} }
// msg // msg
if (msg) if (msg)
offset += CreateGarlicClove (msg, v.data () + offset, payloadLen - offset); offset += CreateGarlicClove (msg, payload + offset, payloadLen - offset);
// ack // ack
if (m_AckRequests.size () > 0) if (m_AckRequests.size () > 0)
{ {
v[offset] = eECIESx25519BlkAck; offset++; payload[offset] = eECIESx25519BlkAck; offset++;
htobe16buf (v.data () + offset, m_AckRequests.size () * 4); offset += 2; htobe16buf (payload + offset, m_AckRequests.size () * 4); offset += 2;
for (auto& it: m_AckRequests) for (auto& it: m_AckRequests)
{ {
htobe16buf (v.data () + offset, it.first); offset += 2; htobe16buf (payload + offset, it.first); offset += 2;
htobe16buf (v.data () + offset, it.second); offset += 2; htobe16buf (payload + offset, it.second); offset += 2;
} }
m_AckRequests.clear (); m_AckRequests.clear ();
} }
// next keys // next keys
if (m_SendReverseKey) if (m_SendReverseKey)
{ {
v[offset] = eECIESx25519BlkNextKey; offset++; payload[offset] = eECIESx25519BlkNextKey; offset++;
htobe16buf (v.data () + offset, m_NextReceiveRatchet->newKey ? 35 : 3); offset += 2; htobe16buf (payload + offset, m_NextReceiveRatchet->newKey ? 35 : 3); offset += 2;
v[offset] = ECIESX25519_NEXT_KEY_REVERSE_KEY_FLAG; payload[offset] = ECIESX25519_NEXT_KEY_REVERSE_KEY_FLAG;
int keyID = m_NextReceiveRatchet->keyID - 1; int keyID = m_NextReceiveRatchet->keyID - 1;
if (m_NextReceiveRatchet->newKey) if (m_NextReceiveRatchet->newKey)
{ {
v[offset] |= ECIESX25519_NEXT_KEY_KEY_PRESENT_FLAG; payload[offset] |= ECIESX25519_NEXT_KEY_KEY_PRESENT_FLAG;
keyID++; keyID++;
} }
offset++; // flag offset++; // flag
htobe16buf (v.data () + offset, keyID); offset += 2; // keyid htobe16buf (payload + offset, keyID); offset += 2; // keyid
if (m_NextReceiveRatchet->newKey) if (m_NextReceiveRatchet->newKey)
{ {
memcpy (v.data () + offset, m_NextReceiveRatchet->key->GetPublicKey (), 32); memcpy (payload + offset, m_NextReceiveRatchet->key->GetPublicKey (), 32);
offset += 32; // public key offset += 32; // public key
} }
m_SendReverseKey = false; m_SendReverseKey = false;
} }
if (m_SendForwardKey) if (m_SendForwardKey)
{ {
v[offset] = eECIESx25519BlkNextKey; offset++; payload[offset] = eECIESx25519BlkNextKey; offset++;
htobe16buf (v.data () + offset, m_NextSendRatchet->newKey ? 35 : 3); offset += 2; htobe16buf (payload + offset, m_NextSendRatchet->newKey ? 35 : 3); offset += 2;
v[offset] = m_NextSendRatchet->newKey ? ECIESX25519_NEXT_KEY_KEY_PRESENT_FLAG : ECIESX25519_NEXT_KEY_REQUEST_REVERSE_KEY_FLAG; payload[offset] = m_NextSendRatchet->newKey ? ECIESX25519_NEXT_KEY_KEY_PRESENT_FLAG : ECIESX25519_NEXT_KEY_REQUEST_REVERSE_KEY_FLAG;
if (!m_NextSendRatchet->keyID) v[offset] |= ECIESX25519_NEXT_KEY_REQUEST_REVERSE_KEY_FLAG; // for first key only if (!m_NextSendRatchet->keyID) payload[offset] |= ECIESX25519_NEXT_KEY_REQUEST_REVERSE_KEY_FLAG; // for first key only
offset++; // flag offset++; // flag
htobe16buf (v.data () + offset, m_NextSendRatchet->keyID); offset += 2; // keyid htobe16buf (payload + offset, m_NextSendRatchet->keyID); offset += 2; // keyid
if (m_NextSendRatchet->newKey) if (m_NextSendRatchet->newKey)
{ {
memcpy (v.data () + offset, m_NextSendRatchet->key->GetPublicKey (), 32); memcpy (payload + offset, m_NextSendRatchet->key->GetPublicKey (), 32);
offset += 32; // public key offset += 32; // public key
} }
} }
// padding // padding
if (paddingSize) if (paddingSize)
{ {
v[offset] = eECIESx25519BlkPadding; offset++; payload[offset] = eECIESx25519BlkPadding; offset++;
htobe16buf (v.data () + offset, paddingSize); offset += 2; htobe16buf (payload + offset, paddingSize); offset += 2;
memset (v.data () + offset, 0, paddingSize); offset += paddingSize; memset (payload + offset, 0, paddingSize); offset += paddingSize;
} }
} }
return v; return payloadLen;
} }
size_t ECIESX25519AEADRatchetSession::CreateGarlicClove (std::shared_ptr<const I2NPMessage> msg, uint8_t * buf, size_t len) size_t ECIESX25519AEADRatchetSession::CreateGarlicClove (std::shared_ptr<const I2NPMessage> msg, uint8_t * buf, size_t len)
@ -1113,7 +1084,7 @@ namespace garlic
// we are Bob // we are Bob
m_CurrentNoiseState.MixHash (buf, 32); m_CurrentNoiseState.MixHash (buf, 32);
uint8_t sharedSecret[32]; uint8_t sharedSecret[32];
if (!GetOwner ()->Decrypt (buf, sharedSecret, nullptr, i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD)) // x25519(bsk, aepk) if (!GetOwner ()->Decrypt (buf, sharedSecret, i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD)) // x25519(bsk, aepk)
{ {
LogPrint (eLogWarning, "Garlic: Incorrect N ephemeral public key"); LogPrint (eLogWarning, "Garlic: Incorrect N ephemeral public key");
return false; return false;
@ -1133,39 +1104,107 @@ namespace garlic
return true; return true;
} }
std::shared_ptr<I2NPMessage> WrapECIESX25519AEADRatchetMessage (std::shared_ptr<const I2NPMessage> msg, const uint8_t * key, uint64_t tag) static size_t CreateGarlicPayload (std::shared_ptr<const I2NPMessage> msg, uint8_t * payload,
bool datetime, size_t optimalSize)
{
size_t len = 0;
if (datetime)
{
// DateTime
payload[0] = eECIESx25519BlkDateTime;
htobe16buf (payload + 1, 4);
htobe32buf (payload + 3, i2p::util::GetSecondsSinceEpoch ());
len = 7;
}
// I2NP
payload += len;
uint16_t cloveSize = msg->GetPayloadLength () + 10;
payload[0] = eECIESx25519BlkGalicClove; // clove type
htobe16buf (payload + 1, cloveSize); // size
payload += 3;
payload[0] = 0; // flag and delivery instructions
payload[1] = msg->GetTypeID (); // I2NP msg type
htobe32buf (payload + 2, msg->GetMsgID ()); // msgID
htobe32buf (payload + 6, msg->GetExpiration () / 1000); // expiration in seconds
memcpy (payload + 10, msg->GetPayload (), msg->GetPayloadLength ());
len += cloveSize + 3;
payload += cloveSize;
// padding
int delta = (int)optimalSize - (int)len;
if (delta < 0 || delta > 3) // don't create padding if we are close to optimal size
{
uint8_t paddingSize = rand () & 0x0F; // 0 - 15
if (delta > 3)
{
delta -= 3;
if (paddingSize > delta) paddingSize %= delta;
}
payload[0] = eECIESx25519BlkPadding;
htobe16buf (payload + 1, paddingSize);
if (paddingSize) memset (payload + 3, 0, paddingSize);
len += paddingSize + 3;
}
return len;
}
std::shared_ptr<I2NPMessage> WrapECIESX25519Message (std::shared_ptr<const I2NPMessage> msg, const uint8_t * key, uint64_t tag)
{ {
auto m = NewI2NPMessage (); auto m = NewI2NPMessage ();
m->Align (12); // in order to get buf aligned to 16 (12 + 4) m->Align (12); // in order to get buf aligned to 16 (12 + 4)
uint8_t * buf = m->GetPayload () + 4; // 4 bytes for length uint8_t * buf = m->GetPayload () + 4; // 4 bytes for length
uint8_t nonce[12];
memset (nonce, 0, 12); // n = 0
size_t offset = 0; size_t offset = 0;
memcpy (buf + offset, &tag, 8); offset += 8; memcpy (buf + offset, &tag, 8); offset += 8;
auto payload = buf + offset; auto payload = buf + offset;
uint16_t cloveSize = msg->GetPayloadLength () + 9 + 1; size_t len = CreateGarlicPayload (msg, payload, false, 956); // 1003 - 8 tag - 16 Poly1305 hash - 16 I2NP header - 4 garlic length - 3 local tunnel delivery
size_t len = cloveSize + 3; uint8_t nonce[12];
payload[0] = eECIESx25519BlkGalicClove; // clove type memset (nonce, 0, 12); // n = 0
htobe16buf (payload + 1, cloveSize); // size if (!i2p::crypto::AEADChaCha20Poly1305 (payload, len, buf, 8, key, nonce, payload, len + 16, true)) // encrypt
payload += 3;
*payload = 0; payload++; // flag and delivery instructions
*payload = msg->GetTypeID (); // I2NP msg type
htobe32buf (payload + 1, msg->GetMsgID ()); // msgID
htobe32buf (payload + 5, msg->GetExpiration () / 1000); // expiration in seconds
memcpy (payload + 9, msg->GetPayload (), msg->GetPayloadLength ());
if (!i2p::crypto::AEADChaCha20Poly1305 (buf + offset, len, buf, 8, key, nonce, buf + offset, len + 16, true)) // encrypt
{ {
LogPrint (eLogWarning, "Garlic: Payload section AEAD encryption failed"); LogPrint (eLogWarning, "Garlic: Payload section AEAD encryption failed");
return nullptr; return nullptr;
} }
offset += len + 16; offset += len + 16;
htobe32buf (m->GetPayload (), offset); htobe32buf (m->GetPayload (), offset);
m->len += offset + 4; m->len += offset + 4;
m->FillI2NPMessageHeader (eI2NPGarlic); m->FillI2NPMessageHeader (eI2NPGarlic);
return m; return m;
} }
std::shared_ptr<I2NPMessage> WrapECIESX25519MessageForRouter (std::shared_ptr<const I2NPMessage> msg, const uint8_t * routerPublicKey)
{
// Noise_N, we are Alice, routerPublicKey is Bob's
i2p::crypto::NoiseSymmetricState noiseState;
i2p::crypto::InitNoiseNState (noiseState, routerPublicKey);
auto m = NewI2NPMessage ();
m->Align (12); // in order to get buf aligned to 16 (12 + 4)
uint8_t * buf = m->GetPayload () + 4; // 4 bytes for length
size_t offset = 0;
auto ephemeralKeys = i2p::transport::transports.GetNextX25519KeysPair ();
memcpy (buf + offset, ephemeralKeys->GetPublicKey (), 32);
noiseState.MixHash (buf + offset, 32); // h = SHA256(h || aepk)
offset += 32;
uint8_t sharedSecret[32];
if (!ephemeralKeys->Agree (routerPublicKey, sharedSecret)) // x25519(aesk, bpk)
{
LogPrint (eLogWarning, "Garlic: Incorrect Bob static key");
return nullptr;
}
noiseState.MixKey (sharedSecret);
auto payload = buf + offset;
size_t len = CreateGarlicPayload (msg, payload, true, 900); // 1003 - 32 eph key - 16 Poly1305 hash - 16 I2NP header - 4 garlic length - 35 router tunnel delivery
uint8_t nonce[12];
memset (nonce, 0, 12);
// encrypt payload
if (!i2p::crypto::AEADChaCha20Poly1305 (payload, len, noiseState.m_H, 32, noiseState.m_CK + 32, nonce, payload, len + 16, true)) // encrypt
{
LogPrint (eLogWarning, "Garlic: Payload for router AEAD encryption failed");
return nullptr;
}
offset += len + 16;
htobe32buf (m->GetPayload (), offset);
m->len += offset + 4;
m->FillI2NPMessageHeader (eI2NPGarlic);
return m;
}
} }
} }

View File

@ -1,5 +1,6 @@
/* /*
* Copyright (c) 2013-2020, The PurpleI2P Project * Copyright (c) 2013-2021, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@ -59,16 +60,7 @@ namespace garlic
private: private:
union i2p::data::Tag<64> m_SessionTagKeyData;
{
uint64_t ll[8];
uint8_t buf[64];
const uint8_t * GetSessTagCK () const { return buf; }; // sessTag_chainKey = keydata[0:31]
const uint8_t * GetSessTagConstant () const { return buf + 32; }; // SESSTAG_CONSTANT = keydata[32:63]
uint64_t GetTag () const { return ll[4]; }; // tag = keydata[32:39]
} m_KeyData;
uint8_t m_SessTagConstant[32], m_SymmKeyCK[32], m_CurrentSymmKeyCK[64], m_NextRootKey[32]; uint8_t m_SessTagConstant[32], m_SymmKeyCK[32], m_CurrentSymmKeyCK[64], m_NextRootKey[32];
int m_NextIndex, m_NextSymmKeyIndex; int m_NextIndex, m_NextSymmKeyIndex;
std::unordered_map<int, i2p::data::Tag<32> > m_ItermediateSymmKeys; std::unordered_map<int, i2p::data::Tag<32> > m_ItermediateSymmKeys;
@ -147,8 +139,7 @@ namespace garlic
eSessionStateNewSessionSent, eSessionStateNewSessionSent,
eSessionStateNewSessionReplySent, eSessionStateNewSessionReplySent,
eSessionStateEstablished, eSessionStateEstablished,
eSessionStateOneTime, eSessionStateOneTime
eSessionStateForRouter
}; };
struct DHRatchet struct DHRatchet
@ -166,7 +157,7 @@ namespace garlic
bool HandleNextMessage (uint8_t * buf, size_t len, std::shared_ptr<ReceiveRatchetTagSet> receiveTagset, int index = 0); bool HandleNextMessage (uint8_t * buf, size_t len, std::shared_ptr<ReceiveRatchetTagSet> receiveTagset, int index = 0);
std::shared_ptr<I2NPMessage> WrapSingleMessage (std::shared_ptr<const I2NPMessage> msg); std::shared_ptr<I2NPMessage> WrapSingleMessage (std::shared_ptr<const I2NPMessage> msg);
std::shared_ptr<I2NPMessage> WrapOneTimeMessage (std::shared_ptr<const I2NPMessage> msg, bool isForRouter = false); std::shared_ptr<I2NPMessage> WrapOneTimeMessage (std::shared_ptr<const I2NPMessage> msg);
const uint8_t * GetRemoteStaticKey () const { return m_RemoteStaticKey; } const uint8_t * GetRemoteStaticKey () const { return m_RemoteStaticKey; }
void SetRemoteStaticKey (const uint8_t * key) { memcpy (m_RemoteStaticKey, key, 32); } void SetRemoteStaticKey (const uint8_t * key) { memcpy (m_RemoteStaticKey, key, 32); }
@ -207,9 +198,8 @@ namespace garlic
bool NewSessionReplyMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen); bool NewSessionReplyMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen);
bool NextNewSessionReplyMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen); bool NextNewSessionReplyMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen);
bool NewExistingSessionMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen); bool NewExistingSessionMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen);
bool NewOutgoingMessageForRouter (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen);
std::vector<uint8_t> CreatePayload (std::shared_ptr<const I2NPMessage> msg, bool first); size_t CreatePayload (std::shared_ptr<const I2NPMessage> msg, bool first, uint8_t * payload);
size_t CreateGarlicClove (std::shared_ptr<const I2NPMessage> msg, uint8_t * buf, size_t len); size_t CreateGarlicClove (std::shared_ptr<const I2NPMessage> msg, uint8_t * buf, size_t len);
size_t CreateLeaseSetClove (std::shared_ptr<const i2p::data::LocalLeaseSet> ls, uint64_t ts, uint8_t * buf, size_t len); size_t CreateLeaseSetClove (std::shared_ptr<const i2p::data::LocalLeaseSet> ls, uint64_t ts, uint8_t * buf, size_t len);
@ -256,8 +246,10 @@ namespace garlic
i2p::crypto::NoiseSymmetricState m_CurrentNoiseState; i2p::crypto::NoiseSymmetricState m_CurrentNoiseState;
}; };
std::shared_ptr<I2NPMessage> WrapECIESX25519AEADRatchetMessage (std::shared_ptr<const I2NPMessage> msg, const uint8_t * key, uint64_t tag); std::shared_ptr<I2NPMessage> WrapECIESX25519Message (std::shared_ptr<const I2NPMessage> msg, const uint8_t * key, uint64_t tag);
std::shared_ptr<I2NPMessage> WrapECIESX25519MessageForRouter (std::shared_ptr<const I2NPMessage> msg, const uint8_t * routerPublicKey);
} }
} }
#endif #endif

View File

@ -24,6 +24,7 @@ namespace i2p {
namespace fs { namespace fs {
std::string appName = "i2pd"; std::string appName = "i2pd";
std::string dataDir = ""; std::string dataDir = "";
std::string certsDir = "";
#ifdef _WIN32 #ifdef _WIN32
std::string dirSep = "\\"; std::string dirSep = "\\";
#else #else
@ -42,6 +43,10 @@ namespace fs {
return dataDir; return dataDir;
} }
const std::string & GetCertsDir () {
return certsDir;
}
const std::string GetUTF8DataDir () { const std::string GetUTF8DataDir () {
#ifdef _WIN32 #ifdef _WIN32
boost::filesystem::wpath path (dataDir); boost::filesystem::wpath path (dataDir);
@ -126,6 +131,21 @@ namespace fs {
#endif #endif
} }
void SetCertsDir(const std::string & cmdline_certsdir) {
if (cmdline_certsdir != "")
{
if (cmdline_certsdir[cmdline_certsdir.length()-1] == '/')
certsDir = cmdline_certsdir.substr(0, cmdline_certsdir.size()-1); // strip trailing slash
else
certsDir = cmdline_certsdir;
}
else
{
certsDir = i2p::fs::DataDirPath("certificates");
}
return;
}
bool Init() { bool Init() {
if (!boost::filesystem::exists(dataDir)) if (!boost::filesystem::exists(dataDir))
boost::filesystem::create_directory(dataDir); boost::filesystem::create_directory(dataDir);

View File

@ -75,6 +75,9 @@ namespace fs {
/** @brief Returns datadir path */ /** @brief Returns datadir path */
const std::string & GetDataDir(); const std::string & GetDataDir();
/** @brief Returns certsdir path */
const std::string & GetCertsDir();
/** @brief Returns datadir path in UTF-8 encoding */ /** @brief Returns datadir path in UTF-8 encoding */
const std::string GetUTF8DataDir(); const std::string GetUTF8DataDir();
@ -92,6 +95,19 @@ namespace fs {
*/ */
void DetectDataDir(const std::string & cmdline_datadir, bool isService = false); void DetectDataDir(const std::string & cmdline_datadir, bool isService = false);
/**
* @brief Set certsdir either from cmdline option or using autodetection
* @param cmdline_param Value of cmdline parameter --certsdir=<something>
*
* Examples of autodetected paths:
*
* Windows < Vista: C:\Documents and Settings\Username\Application Data\i2pd\certificates
* Windows >= Vista: C:\Users\Username\AppData\Roaming\i2pd\certificates
* Mac: /Library/Application Support/i2pd/ or ~/Library/Application Support/i2pd/certificates
* Unix: /var/lib/i2pd/certificates (system=1) >> ~/.i2pd/ or /tmp/i2pd/certificates
*/
void SetCertsDir(const std::string & cmdline_certsdir);
/** /**
* @brief Create subdirectories inside datadir * @brief Create subdirectories inside datadir
*/ */

View File

@ -13,6 +13,7 @@
#include "FS.h" #include "FS.h"
#include "Log.h" #include "Log.h"
#include "Family.h" #include "Family.h"
#include "Config.h"
namespace i2p namespace i2p
{ {
@ -98,7 +99,8 @@ namespace data
void Families::LoadCertificates () void Families::LoadCertificates ()
{ {
std::string certDir = i2p::fs::DataDirPath("certificates", "family"); std::string certDir = i2p::fs::GetCertsDir() + i2p::fs::dirSep + "family";
std::vector<std::string> files; std::vector<std::string> files;
int numCertificates = 0; int numCertificates = 0;

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2020, The PurpleI2P Project * Copyright (c) 2013-2021, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@ -164,9 +164,7 @@ namespace garlic
RAND_bytes (elGamal.preIV, 32); // Pre-IV RAND_bytes (elGamal.preIV, 32); // Pre-IV
uint8_t iv[32]; // IV is first 16 bytes uint8_t iv[32]; // IV is first 16 bytes
SHA256(elGamal.preIV, 32, iv); SHA256(elGamal.preIV, 32, iv);
BN_CTX * ctx = BN_CTX_new (); m_Destination->Encrypt ((uint8_t *)&elGamal, buf);
m_Destination->Encrypt ((uint8_t *)&elGamal, buf, ctx);
BN_CTX_free (ctx);
m_Encryption.SetIV (iv); m_Encryption.SetIV (iv);
buf += 514; buf += 514;
len += 514; len += 514;
@ -433,14 +431,14 @@ namespace garlic
} }
GarlicDestination::GarlicDestination (): m_NumTags (32), // 32 tags by default GarlicDestination::GarlicDestination (): m_NumTags (32), // 32 tags by default
m_NumRatchetInboundTags (0) // 0 means standard m_PayloadBuffer (nullptr), m_NumRatchetInboundTags (0) // 0 means standard
{ {
m_Ctx = BN_CTX_new ();
} }
GarlicDestination::~GarlicDestination () GarlicDestination::~GarlicDestination ()
{ {
BN_CTX_free (m_Ctx); if (m_PayloadBuffer)
delete[] m_PayloadBuffer;
} }
void GarlicDestination::CleanUp () void GarlicDestination::CleanUp ()
@ -471,8 +469,13 @@ namespace garlic
{ {
uint64_t t; uint64_t t;
memcpy (&t, tag, 8); memcpy (&t, tag, 8);
AddECIESx25519Key (key, t);
}
void GarlicDestination::AddECIESx25519Key (const uint8_t * key, uint64_t tag)
{
auto tagset = std::make_shared<SymmetricKeyTagSet>(this, key); auto tagset = std::make_shared<SymmetricKeyTagSet>(this, key);
m_ECIESx25519Tags.emplace (t, ECIESX25519AEADRatchetIndexTagset{0, tagset}); m_ECIESx25519Tags.emplace (tag, ECIESX25519AEADRatchetIndexTagset{0, tagset});
} }
bool GarlicDestination::SubmitSessionKey (const uint8_t * key, const uint8_t * tag) bool GarlicDestination::SubmitSessionKey (const uint8_t * key, const uint8_t * tag)
@ -494,22 +497,9 @@ namespace garlic
buf += 4; // length buf += 4; // length
bool found = false; bool found = false;
uint64_t tag;
if (SupportsEncryptionType (i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD)) if (SupportsEncryptionType (i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD))
{
// try ECIESx25519 tag // try ECIESx25519 tag
memcpy (&tag, buf, 8); found = HandleECIESx25519TagMessage (buf, length);
auto it1 = m_ECIESx25519Tags.find (tag);
if (it1 != m_ECIESx25519Tags.end ())
{
found = true;
if (it1->second.tagset->HandleNextMessage (buf, length, it1->second.index))
m_LastTagset = it1->second.tagset;
else
LogPrint (eLogError, "Garlic: can't handle ECIES-X25519-AEAD-Ratchet message");
m_ECIESx25519Tags.erase (it1);
}
}
if (!found) if (!found)
{ {
auto it = !mod ? m_Tags.find (SessionTag(buf)) : m_Tags.end (); // AES block is multiple of 16 auto it = !mod ? m_Tags.find (SessionTag(buf)) : m_Tags.end (); // AES block is multiple of 16
@ -537,7 +527,7 @@ namespace garlic
// try ElGamal/AES first if leading block is 514 // try ElGamal/AES first if leading block is 514
ElGamalBlock elGamal; ElGamalBlock elGamal;
if (mod == 2 && length >= 514 && SupportsEncryptionType (i2p::data::CRYPTO_KEY_TYPE_ELGAMAL) && if (mod == 2 && length >= 514 && SupportsEncryptionType (i2p::data::CRYPTO_KEY_TYPE_ELGAMAL) &&
Decrypt (buf, (uint8_t *)&elGamal, m_Ctx, i2p::data::CRYPTO_KEY_TYPE_ELGAMAL)) Decrypt (buf, (uint8_t *)&elGamal, i2p::data::CRYPTO_KEY_TYPE_ELGAMAL))
{ {
auto decryption = std::make_shared<AESDecryption>(elGamal.sessionKey); auto decryption = std::make_shared<AESDecryption>(elGamal.sessionKey);
uint8_t iv[32]; // IV is first 16 bytes uint8_t iv[32]; // IV is first 16 bytes
@ -555,6 +545,7 @@ namespace garlic
// try to gererate more tags for last tagset // try to gererate more tags for last tagset
if (m_LastTagset && (m_LastTagset->GetNextIndex () - m_LastTagset->GetTrimBehind () < 3*ECIESX25519_MAX_NUM_GENERATED_TAGS)) if (m_LastTagset && (m_LastTagset->GetNextIndex () - m_LastTagset->GetTrimBehind () < 3*ECIESX25519_MAX_NUM_GENERATED_TAGS))
{ {
uint64_t missingTag; memcpy (&missingTag, buf, 8);
auto maxTags = std::max (m_NumRatchetInboundTags, ECIESX25519_MAX_NUM_GENERATED_TAGS); auto maxTags = std::max (m_NumRatchetInboundTags, ECIESX25519_MAX_NUM_GENERATED_TAGS);
LogPrint (eLogWarning, "Garlic: trying to generate more ECIES-X25519-AEAD-Ratchet tags"); LogPrint (eLogWarning, "Garlic: trying to generate more ECIES-X25519-AEAD-Ratchet tags");
for (int i = 0; i < maxTags; i++) for (int i = 0; i < maxTags; i++)
@ -565,10 +556,10 @@ namespace garlic
LogPrint (eLogError, "Garlic: can't create new ECIES-X25519-AEAD-Ratchet tag for last tagset"); LogPrint (eLogError, "Garlic: can't create new ECIES-X25519-AEAD-Ratchet tag for last tagset");
break; break;
} }
if (nextTag == tag) if (nextTag == missingTag)
{ {
LogPrint (eLogDebug, "Garlic: Missing ECIES-X25519-AEAD-Ratchet tag was generated"); LogPrint (eLogDebug, "Garlic: Missing ECIES-X25519-AEAD-Ratchet tag was generated");
if (m_LastTagset->HandleNextMessage (buf, length, m_ECIESx25519Tags[tag].index)) if (m_LastTagset->HandleNextMessage (buf, length, m_ECIESx25519Tags[nextTag].index))
found = true; found = true;
break; break;
} }
@ -585,6 +576,23 @@ namespace garlic
} }
} }
bool GarlicDestination::HandleECIESx25519TagMessage (uint8_t * buf, size_t len)
{
uint64_t tag;
memcpy (&tag, buf, 8);
auto it = m_ECIESx25519Tags.find (tag);
if (it != m_ECIESx25519Tags.end ())
{
if (it->second.tagset->HandleNextMessage (buf, len, it->second.index))
m_LastTagset = it->second.tagset;
else
LogPrint (eLogError, "Garlic: can't handle ECIES-X25519-AEAD-Ratchet message");
m_ECIESx25519Tags.erase (it);
return true;
}
return false;
}
void GarlicDestination::HandleAESBlock (uint8_t * buf, size_t len, std::shared_ptr<AESDecryption> decryption, void GarlicDestination::HandleAESBlock (uint8_t * buf, size_t len, std::shared_ptr<AESDecryption> decryption,
std::shared_ptr<i2p::tunnel::InboundTunnel> from) std::shared_ptr<i2p::tunnel::InboundTunnel> from)
{ {
@ -749,11 +757,7 @@ namespace garlic
std::shared_ptr<I2NPMessage> msg) std::shared_ptr<I2NPMessage> msg)
{ {
if (router->GetEncryptionType () == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD) if (router->GetEncryptionType () == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD)
{ return WrapECIESX25519MessageForRouter (msg, router->GetIdentity ()->GetEncryptionPublicKey ());
auto session = std::make_shared<ECIESX25519AEADRatchetSession>(this, false);
session->SetRemoteStaticKey (router->GetIdentity ()->GetEncryptionPublicKey ());
return session->WrapOneTimeMessage (msg, true);
}
else else
{ {
auto session = GetRoutingSession (router, false); auto session = GetRoutingSession (router, false);
@ -769,7 +773,7 @@ namespace garlic
{ {
ECIESX25519AEADRatchetSessionPtr session; ECIESX25519AEADRatchetSessionPtr session;
uint8_t staticKey[32]; uint8_t staticKey[32];
destination->Encrypt (nullptr, staticKey, nullptr); // we are supposed to get static key destination->Encrypt (nullptr, staticKey); // we are supposed to get static key
auto it = m_ECIESx25519Sessions.find (staticKey); auto it = m_ECIESx25519Sessions.find (staticKey);
if (it != m_ECIESx25519Sessions.end ()) if (it != m_ECIESx25519Sessions.end ())
{ {
@ -1036,10 +1040,11 @@ namespace garlic
{ {
LogPrint (eLogDebug, "Garlic: type local"); LogPrint (eLogDebug, "Garlic: type local");
I2NPMessageType typeID = (I2NPMessageType)(buf[0]); buf++; // typeid I2NPMessageType typeID = (I2NPMessageType)(buf[0]); buf++; // typeid
buf += (4 + 4); // msgID + expiration int32_t msgID = bufbe32toh (buf); buf += 4; // msgID
buf += 4; // expiration
ptrdiff_t offset = buf - buf1; ptrdiff_t offset = buf - buf1;
if (offset <= (int)len) if (offset <= (int)len)
HandleCloveI2NPMessage (typeID, buf, len - offset); HandleCloveI2NPMessage (typeID, buf, len - offset, msgID);
else else
LogPrint (eLogError, "Garlic: clove is too long"); LogPrint (eLogError, "Garlic: clove is too long");
break; break;
@ -1058,13 +1063,14 @@ namespace garlic
} }
uint32_t gwTunnel = bufbe32toh (buf); buf += 4; uint32_t gwTunnel = bufbe32toh (buf); buf += 4;
I2NPMessageType typeID = (I2NPMessageType)(buf[0]); buf++; // typeid I2NPMessageType typeID = (I2NPMessageType)(buf[0]); buf++; // typeid
buf += (4 + 4); // msgID + expiration uint32_t msgID = bufbe32toh (buf); buf += 4; // msgID
buf += 4; // expiration
offset += 13; offset += 13;
if (GetTunnelPool ()) if (GetTunnelPool ())
{ {
auto tunnel = GetTunnelPool ()->GetNextOutboundTunnel (); auto tunnel = GetTunnelPool ()->GetNextOutboundTunnel ();
if (tunnel) if (tunnel)
tunnel->SendTunnelDataMsg (gwHash, gwTunnel, CreateI2NPMessage (typeID, buf, len - offset)); tunnel->SendTunnelDataMsg (gwHash, gwTunnel, CreateI2NPMessage (typeID, buf, len - offset, msgID));
else else
LogPrint (eLogWarning, "Garlic: No outbound tunnels available for garlic clove"); LogPrint (eLogWarning, "Garlic: No outbound tunnels available for garlic clove");
} }
@ -1115,5 +1121,12 @@ namespace garlic
m_ECIESx25519Sessions.erase (it); m_ECIESx25519Sessions.erase (it);
} }
} }
uint8_t * GarlicDestination::GetPayloadBuffer ()
{
if (!m_PayloadBuffer)
m_PayloadBuffer = new uint8_t[I2NP_MAX_MESSAGE_SIZE];
return m_PayloadBuffer;
}
} }
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2020, The PurpleI2P Project * Copyright (c) 2013-2021, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@ -243,13 +243,14 @@ namespace garlic
std::shared_ptr<I2NPMessage> msg); std::shared_ptr<I2NPMessage> msg);
void AddSessionKey (const uint8_t * key, const uint8_t * tag); // one tag void AddSessionKey (const uint8_t * key, const uint8_t * tag); // one tag
void AddECIESx25519Key (const uint8_t * key, const uint8_t * tag); // one tag void AddECIESx25519Key (const uint8_t * key, uint64_t tag); // one tag
virtual bool SubmitSessionKey (const uint8_t * key, const uint8_t * tag); // from different thread virtual bool SubmitSessionKey (const uint8_t * key, const uint8_t * tag); // from different thread
void DeliveryStatusSent (GarlicRoutingSessionPtr session, uint32_t msgID); void DeliveryStatusSent (GarlicRoutingSessionPtr session, uint32_t msgID);
uint64_t AddECIESx25519SessionNextTag (ReceiveRatchetTagSetPtr tagset); uint64_t AddECIESx25519SessionNextTag (ReceiveRatchetTagSetPtr tagset);
void AddECIESx25519Session (const uint8_t * staticKey, ECIESX25519AEADRatchetSessionPtr session); void AddECIESx25519Session (const uint8_t * staticKey, ECIESX25519AEADRatchetSessionPtr session);
void RemoveECIESx25519Session (const uint8_t * staticKey); void RemoveECIESx25519Session (const uint8_t * staticKey);
void HandleECIESx25519GarlicClove (const uint8_t * buf, size_t len); void HandleECIESx25519GarlicClove (const uint8_t * buf, size_t len);
uint8_t * GetPayloadBuffer ();
virtual void ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg); virtual void ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg);
virtual void ProcessDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg); virtual void ProcessDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg);
@ -260,8 +261,10 @@ namespace garlic
protected: protected:
void AddECIESx25519Key (const uint8_t * key, const uint8_t * tag); // one tag
bool HandleECIESx25519TagMessage (uint8_t * buf, size_t len); // return true if found
virtual void HandleI2NPMessage (const uint8_t * buf, size_t len) = 0; // called from clove only virtual void HandleI2NPMessage (const uint8_t * buf, size_t len) = 0; // called from clove only
virtual bool HandleCloveI2NPMessage (I2NPMessageType typeID, const uint8_t * payload, size_t len) = 0; virtual bool HandleCloveI2NPMessage (I2NPMessageType typeID, const uint8_t * payload, size_t len, uint32_t msgID) = 0;
void HandleGarlicMessage (std::shared_ptr<I2NPMessage> msg); void HandleGarlicMessage (std::shared_ptr<I2NPMessage> msg);
void HandleDeliveryStatusMessage (uint32_t msgID); void HandleDeliveryStatusMessage (uint32_t msgID);
@ -276,12 +279,12 @@ namespace garlic
private: private:
BN_CTX * m_Ctx; // incoming
// outgoing sessions // outgoing sessions
int m_NumTags; int m_NumTags;
std::mutex m_SessionsMutex; std::mutex m_SessionsMutex;
std::unordered_map<i2p::data::IdentHash, ElGamalAESSessionPtr> m_Sessions; std::unordered_map<i2p::data::IdentHash, ElGamalAESSessionPtr> m_Sessions;
std::unordered_map<i2p::data::Tag<32>, ECIESX25519AEADRatchetSessionPtr> m_ECIESx25519Sessions; // static key -> session std::unordered_map<i2p::data::Tag<32>, ECIESX25519AEADRatchetSessionPtr> m_ECIESx25519Sessions; // static key -> session
uint8_t * m_PayloadBuffer; // for ECIESX25519AEADRatchet
// incoming // incoming
int m_NumRatchetInboundTags; int m_NumRatchetInboundTags;
std::unordered_map<SessionTag, std::shared_ptr<AESDecryption>, std::hash<i2p::data::Tag<32> > > m_Tags; std::unordered_map<SessionTag, std::shared_ptr<AESDecryption>, std::hash<i2p::data::Tag<32> > > m_Tags;

View File

@ -36,10 +36,21 @@ namespace i2p
return std::make_shared<I2NPMessageBuffer<I2NP_MAX_SHORT_MESSAGE_SIZE> >(); return std::make_shared<I2NPMessageBuffer<I2NP_MAX_SHORT_MESSAGE_SIZE> >();
} }
std::shared_ptr<I2NPMessage> NewI2NPTunnelMessage () std::shared_ptr<I2NPMessage> NewI2NPTunnelMessage (bool endpoint)
{ {
auto msg = new I2NPMessageBuffer<i2p::tunnel::TUNNEL_DATA_MSG_SIZE + I2NP_HEADER_SIZE + 34>(); // reserved for alignment and NTCP 16 + 6 + 12 I2NPMessage * msg = nullptr;
if (endpoint)
{
// should fit two tunnel message + tunnel gateway header, enough for one garlic encrypted streaming packet
msg = new I2NPMessageBuffer<2*i2p::tunnel::TUNNEL_DATA_MSG_SIZE + I2NP_HEADER_SIZE + TUNNEL_GATEWAY_HEADER_SIZE + 28>(); // reserved for alignment and NTCP 16 + 6 + 6
msg->Align (6);
msg->offset += TUNNEL_GATEWAY_HEADER_SIZE; // reserve room for TunnelGateway header
}
else
{
msg = new I2NPMessageBuffer<i2p::tunnel::TUNNEL_DATA_MSG_SIZE + I2NP_HEADER_SIZE + 34>(); // reserved for alignment and NTCP 16 + 6 + 12
msg->Align (12); msg->Align (12);
}
return std::shared_ptr<I2NPMessage>(msg); return std::shared_ptr<I2NPMessage>(msg);
} }
@ -251,6 +262,12 @@ namespace i2p
if (!router) // we send own RouterInfo if (!router) // we send own RouterInfo
router = context.GetSharedRouterInfo (); router = context.GetSharedRouterInfo ();
if (!router->GetBuffer ())
{
LogPrint (eLogError, "I2NP: Invalid RouterInfo buffer for DatabaseStore");
return nullptr;
}
auto m = NewI2NPShortMessage (); auto m = NewI2NPShortMessage ();
uint8_t * payload = m->GetPayload (); uint8_t * payload = m->GetPayload ();
@ -364,7 +381,7 @@ namespace i2p
return g_MaxNumTransitTunnels; return g_MaxNumTransitTunnels;
} }
bool HandleBuildRequestRecords (int num, uint8_t * records, uint8_t * clearText) static bool HandleBuildRequestRecords (int num, uint8_t * records, uint8_t * clearText)
{ {
for (int i = 0; i < num; i++) for (int i = 0; i < num; i++)
{ {
@ -374,53 +391,32 @@ namespace i2p
LogPrint (eLogDebug, "I2NP: Build request record ", i, " is ours"); LogPrint (eLogDebug, "I2NP: Build request record ", i, " is ours");
if (!i2p::context.DecryptTunnelBuildRecord (record + BUILD_REQUEST_RECORD_ENCRYPTED_OFFSET, clearText)) return false; if (!i2p::context.DecryptTunnelBuildRecord (record + BUILD_REQUEST_RECORD_ENCRYPTED_OFFSET, clearText)) return false;
uint8_t retCode = 0; uint8_t retCode = 0;
bool isECIES = i2p::context.IsECIES ();
// replace record to reply // replace record to reply
if (i2p::context.AcceptsTunnels () && if (i2p::context.AcceptsTunnels () &&
i2p::tunnel::tunnels.GetTransitTunnels ().size () <= g_MaxNumTransitTunnels && i2p::tunnel::tunnels.GetTransitTunnels ().size () <= g_MaxNumTransitTunnels &&
!i2p::transport::transports.IsBandwidthExceeded () && !i2p::transport::transports.IsBandwidthExceeded () &&
!i2p::transport::transports.IsTransitBandwidthExceeded ()) !i2p::transport::transports.IsTransitBandwidthExceeded ())
{ {
auto transitTunnel = isECIES ? auto transitTunnel = i2p::tunnel::CreateTransitTunnel (
i2p::tunnel::CreateTransitTunnel (
bufbe32toh (clearText + ECIES_BUILD_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET), bufbe32toh (clearText + ECIES_BUILD_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET),
clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET, clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET,
bufbe32toh (clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET), bufbe32toh (clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET),
clearText + ECIES_BUILD_REQUEST_RECORD_LAYER_KEY_OFFSET, clearText + ECIES_BUILD_REQUEST_RECORD_LAYER_KEY_OFFSET,
clearText + ECIES_BUILD_REQUEST_RECORD_IV_KEY_OFFSET, clearText + ECIES_BUILD_REQUEST_RECORD_IV_KEY_OFFSET,
clearText[ECIES_BUILD_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_GATEWAY_FLAG, clearText[ECIES_BUILD_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_GATEWAY_FLAG,
clearText[ECIES_BUILD_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_ENDPOINT_FLAG) : clearText[ECIES_BUILD_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_ENDPOINT_FLAG);
i2p::tunnel::CreateTransitTunnel (
bufbe32toh (clearText + BUILD_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET),
clearText + BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET,
bufbe32toh (clearText + BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET),
clearText + BUILD_REQUEST_RECORD_LAYER_KEY_OFFSET,
clearText + BUILD_REQUEST_RECORD_IV_KEY_OFFSET,
clearText[BUILD_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_GATEWAY_FLAG,
clearText[BUILD_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_ENDPOINT_FLAG);
i2p::tunnel::tunnels.AddTransitTunnel (transitTunnel); i2p::tunnel::tunnels.AddTransitTunnel (transitTunnel);
} }
else else
retCode = 30; // always reject with bandwidth reason (30) retCode = 30; // always reject with bandwidth reason (30)
if (isECIES)
{
memset (record + ECIES_BUILD_RESPONSE_RECORD_OPTIONS_OFFSET, 0, 2); // no options memset (record + ECIES_BUILD_RESPONSE_RECORD_OPTIONS_OFFSET, 0, 2); // no options
record[ECIES_BUILD_RESPONSE_RECORD_RET_OFFSET] = retCode; record[ECIES_BUILD_RESPONSE_RECORD_RET_OFFSET] = retCode;
}
else
{
record[BUILD_RESPONSE_RECORD_RET_OFFSET] = retCode;
SHA256 (record + BUILD_RESPONSE_RECORD_PADDING_OFFSET, BUILD_RESPONSE_RECORD_PADDING_SIZE + 1, // + 1 byte of ret
record + BUILD_RESPONSE_RECORD_HASH_OFFSET);
}
// encrypt reply // encrypt reply
i2p::crypto::CBCEncryption encryption; i2p::crypto::CBCEncryption encryption;
for (int j = 0; j < num; j++) for (int j = 0; j < num; j++)
{ {
uint8_t * reply = records + j*TUNNEL_BUILD_RECORD_SIZE; uint8_t * reply = records + j*TUNNEL_BUILD_RECORD_SIZE;
if (isECIES)
{
if (j == i) if (j == i)
{ {
uint8_t nonce[12]; uint8_t nonce[12];
@ -440,20 +436,13 @@ namespace i2p
encryption.Encrypt(reply, TUNNEL_BUILD_RECORD_SIZE, reply); encryption.Encrypt(reply, TUNNEL_BUILD_RECORD_SIZE, reply);
} }
} }
else
{
encryption.SetKey (clearText + BUILD_REQUEST_RECORD_REPLY_KEY_OFFSET);
encryption.SetIV (clearText + BUILD_REQUEST_RECORD_REPLY_IV_OFFSET);
encryption.Encrypt(reply, TUNNEL_BUILD_RECORD_SIZE, reply);
}
}
return true; return true;
} }
} }
return false; return false;
} }
void HandleVariableTunnelBuildMsg (uint32_t replyMsgID, uint8_t * buf, size_t len) static void HandleVariableTunnelBuildMsg (uint32_t replyMsgID, uint8_t * buf, size_t len)
{ {
int num = buf[0]; int num = buf[0];
LogPrint (eLogDebug, "I2NP: VariableTunnelBuild ", num, " records"); LogPrint (eLogDebug, "I2NP: VariableTunnelBuild ", num, " records");
@ -481,8 +470,6 @@ namespace i2p
} }
} }
else else
{
if (i2p::context.IsECIES ())
{ {
uint8_t clearText[ECIES_BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE]; uint8_t clearText[ECIES_BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE];
if (HandleBuildRequestRecords (num, buf + 1, clearText)) if (HandleBuildRequestRecords (num, buf + 1, clearText))
@ -501,65 +488,21 @@ namespace i2p
bufbe32toh (clearText + ECIES_BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET))); bufbe32toh (clearText + ECIES_BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET)));
} }
} }
else
{
uint8_t clearText[BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE];
if (HandleBuildRequestRecords (num, buf + 1, clearText))
{
if (clearText[BUILD_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_ENDPOINT_FLAG) // we are endpoint of outboud tunnel
{
// so we send it to reply tunnel
transports.SendMessage (clearText + BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET,
CreateTunnelGatewayMsg (bufbe32toh (clearText + BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET),
eI2NPVariableTunnelBuildReply, buf, len,
bufbe32toh (clearText + BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET)));
}
else
transports.SendMessage (clearText + BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET,
CreateI2NPMessage (eI2NPVariableTunnelBuild, buf, len,
bufbe32toh (clearText + BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET)));
}
}
}
} }
void HandleTunnelBuildMsg (uint8_t * buf, size_t len) static void HandleTunnelBuildMsg (uint8_t * buf, size_t len)
{
if (i2p::context.IsECIES ())
{ {
LogPrint (eLogWarning, "I2NP: TunnelBuild is too old for ECIES router"); LogPrint (eLogWarning, "I2NP: TunnelBuild is too old for ECIES router");
return;
}
if (len < NUM_TUNNEL_BUILD_RECORDS*TUNNEL_BUILD_RECORD_SIZE)
{
LogPrint (eLogError, "I2NP: TunnelBuild message is too short ", len);
return;
}
uint8_t clearText[BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE];
if (HandleBuildRequestRecords (NUM_TUNNEL_BUILD_RECORDS, buf, clearText))
{
if (clearText[BUILD_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_ENDPOINT_FLAG) // we are endpoint of outbound tunnel
{
// so we send it to reply tunnel
transports.SendMessage (clearText + BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET,
CreateTunnelGatewayMsg (bufbe32toh (clearText + BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET),
eI2NPTunnelBuildReply, buf, len,
bufbe32toh (clearText + BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET)));
}
else
transports.SendMessage (clearText + BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET,
CreateI2NPMessage (eI2NPTunnelBuild, buf, len,
bufbe32toh (clearText + BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET)));
}
} }
void HandleVariableTunnelBuildReplyMsg (uint32_t replyMsgID, uint8_t * buf, size_t len) static void HandleTunnelBuildReplyMsg (uint32_t replyMsgID, uint8_t * buf, size_t len, bool isShort)
{ {
int num = buf[0]; int num = buf[0];
LogPrint (eLogDebug, "I2NP: VariableTunnelBuildReplyMsg of ", num, " records replyMsgID=", replyMsgID); LogPrint (eLogDebug, "I2NP: TunnelBuildReplyMsg of ", num, " records replyMsgID=", replyMsgID);
if (len < num*BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE + 1) size_t recordSize = isShort ? SHORT_TUNNEL_BUILD_RECORD_SIZE : TUNNEL_BUILD_RECORD_SIZE;
if (len < num*recordSize + 1)
{ {
LogPrint (eLogError, "I2NP: VaribleTunnelBuildReply message of ", num, " records is too short ", len); LogPrint (eLogError, "I2NP: TunnelBuildReply message of ", num, " records is too short ", len);
return; return;
} }
@ -583,13 +526,8 @@ namespace i2p
LogPrint (eLogWarning, "I2NP: Pending tunnel for message ", replyMsgID, " not found"); LogPrint (eLogWarning, "I2NP: Pending tunnel for message ", replyMsgID, " not found");
} }
void HandleShortTunnelBuildMsg (uint32_t replyMsgID, uint8_t * buf, size_t len) static void HandleShortTunnelBuildMsg (uint32_t replyMsgID, uint8_t * buf, size_t len)
{ {
if (!i2p::context.IsECIES ())
{
LogPrint (eLogWarning, "I2NP: ShortTunnelBuild can be handled by ECIES router only");
return;
}
int num = buf[0]; int num = buf[0];
LogPrint (eLogDebug, "I2NP: ShortTunnelBuild ", num, " records"); LogPrint (eLogDebug, "I2NP: ShortTunnelBuild ", num, " records");
if (len < num*SHORT_TUNNEL_BUILD_RECORD_SIZE + 1) if (len < num*SHORT_TUNNEL_BUILD_RECORD_SIZE + 1)
@ -597,7 +535,24 @@ namespace i2p
LogPrint (eLogError, "I2NP: ShortTunnelBuild message of ", num, " records is too short ", len); LogPrint (eLogError, "I2NP: ShortTunnelBuild message of ", num, " records is too short ", len);
return; return;
} }
// TODO: check replyMsgID auto tunnel = i2p::tunnel::tunnels.GetPendingInboundTunnel (replyMsgID);
if (tunnel)
{
// endpoint of inbound tunnel
LogPrint (eLogDebug, "I2NP: ShortTunnelBuild reply for tunnel ", tunnel->GetTunnelID ());
if (tunnel->HandleTunnelBuildResponse (buf, len))
{
LogPrint (eLogInfo, "I2NP: Inbound tunnel ", tunnel->GetTunnelID (), " has been created");
tunnel->SetState (i2p::tunnel::eTunnelStateEstablished);
i2p::tunnel::tunnels.AddInboundTunnel (tunnel);
}
else
{
LogPrint (eLogInfo, "I2NP: Inbound tunnel ", tunnel->GetTunnelID (), " has been declined");
tunnel->SetState (i2p::tunnel::eTunnelStateBuildFailed);
}
return;
}
const uint8_t * record = buf + 1; const uint8_t * record = buf + 1;
for (int i = 0; i < num; i++) for (int i = 0; i < num; i++)
{ {
@ -610,55 +565,47 @@ namespace i2p
LogPrint (eLogWarning, "I2NP: Can't decrypt short request record ", i); LogPrint (eLogWarning, "I2NP: Can't decrypt short request record ", i);
return; return;
} }
if (clearText[SHORT_REQUEST_RECORD_LAYER_ENCRYPTION_TYPE]) // not AES
{
LogPrint (eLogWarning, "I2NP: Unknown layer encryption type ", clearText[SHORT_REQUEST_RECORD_LAYER_ENCRYPTION_TYPE], " in short request record");
return;
}
auto& noiseState = i2p::context.GetCurrentNoiseState (); auto& noiseState = i2p::context.GetCurrentNoiseState ();
uint8_t layerKeys[64]; // (layer key, iv key) uint8_t replyKey[32], layerKey[32], ivKey[32];
i2p::crypto::HKDF (noiseState.m_CK + 32, nullptr, 0, "LayerAndIVKeys", layerKeys); // TODO: correct domain i2p::crypto::HKDF (noiseState.m_CK, nullptr, 0, "SMTunnelReplyKey", noiseState.m_CK);
memcpy (replyKey, noiseState.m_CK + 32, 32);
i2p::crypto::HKDF (noiseState.m_CK, nullptr, 0, "SMTunnelLayerKey", noiseState.m_CK);
memcpy (layerKey, noiseState.m_CK + 32, 32);
bool isEndpoint = clearText[SHORT_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_ENDPOINT_FLAG;
if (isEndpoint)
{
i2p::crypto::HKDF (noiseState.m_CK, nullptr, 0, "TunnelLayerIVKey", noiseState.m_CK);
memcpy (ivKey, noiseState.m_CK + 32, 32);
}
else
memcpy (ivKey, noiseState.m_CK , 32);
// check if we accept this tunnel
uint8_t retCode = 0;
if (!i2p::context.AcceptsTunnels () ||
i2p::tunnel::tunnels.GetTransitTunnels ().size () > g_MaxNumTransitTunnels ||
i2p::transport::transports.IsBandwidthExceeded () ||
i2p::transport::transports.IsTransitBandwidthExceeded ())
retCode = 30;
if (!retCode)
{
// create new transit tunnel
auto transitTunnel = i2p::tunnel::CreateTransitTunnel ( auto transitTunnel = i2p::tunnel::CreateTransitTunnel (
bufbe32toh (clearText + SHORT_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET), bufbe32toh (clearText + SHORT_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET),
clearText + SHORT_REQUEST_RECORD_NEXT_IDENT_OFFSET, clearText + SHORT_REQUEST_RECORD_NEXT_IDENT_OFFSET,
bufbe32toh (clearText + SHORT_REQUEST_RECORD_NEXT_TUNNEL_OFFSET), bufbe32toh (clearText + SHORT_REQUEST_RECORD_NEXT_TUNNEL_OFFSET),
layerKeys, layerKeys + 32, layerKey, ivKey,
clearText[SHORT_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_GATEWAY_FLAG, clearText[SHORT_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_GATEWAY_FLAG,
clearText[SHORT_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_ENDPOINT_FLAG); clearText[SHORT_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_ENDPOINT_FLAG);
i2p::tunnel::tunnels.AddTransitTunnel (transitTunnel); i2p::tunnel::tunnels.AddTransitTunnel (transitTunnel);
if (clearText[SHORT_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_ENDPOINT_FLAG)
{
// we are endpoint, create OutboundTunnelBuildReply
auto otbrm = NewI2NPShortMessage ();
auto payload = otbrm->GetPayload ();
payload[0] = num; // num
payload[1] = i; // slot
payload +=2;
// reply
htobe16buf (payload, 3); payload += 2; // length, TODO
memset (payload, 0, 3); payload += 3; // ClearText: no options, and zero ret code. TODO
// ShortBuildReplyRecords. Exclude ours
uint8_t * records = buf + 1;
if (i > 0)
{
memcpy (payload, records, i*SHORT_TUNNEL_BUILD_RECORD_SIZE);
payload += i*SHORT_TUNNEL_BUILD_RECORD_SIZE;
records += i*SHORT_TUNNEL_BUILD_RECORD_SIZE;
} }
if (i < num-1)
{ // encrypt reply
memcpy (payload, records, (num-1-i)*SHORT_TUNNEL_BUILD_RECORD_SIZE);
payload += (num-1-i)*SHORT_TUNNEL_BUILD_RECORD_SIZE;
}
otbrm->len += (payload - otbrm->GetPayload ());
otbrm->FillI2NPMessageHeader (eI2NPOutboundTunnelBuildReply, bufbe32toh (clearText + SHORT_REQUEST_RECORD_SEND_MSG_ID_OFFSET));
uint8_t replyKeys[64]; // (reply key, tag)
i2p::crypto::HKDF (noiseState.m_CK, nullptr, 0, "ReplyKeyAndTag", replyKeys); // TODO: correct domain
uint64_t tag;
memcpy (&tag, replyKeys + 32, 8);
// send garlic to reply tunnel
transports.SendMessage (clearText + SHORT_REQUEST_RECORD_NEXT_IDENT_OFFSET,
CreateTunnelGatewayMsg (bufbe32toh (clearText + SHORT_REQUEST_RECORD_NEXT_TUNNEL_OFFSET),
i2p::garlic::WrapECIESX25519AEADRatchetMessage (otbrm, replyKeys, tag)));
}
else
{
// we are participant, encrypt reply
uint8_t nonce[12]; uint8_t nonce[12];
memset (nonce, 0, 12); memset (nonce, 0, 12);
uint8_t * reply = buf + 1; uint8_t * reply = buf + 1;
@ -667,22 +614,51 @@ namespace i2p
nonce[4] = j; // nonce is record # nonce[4] = j; // nonce is record #
if (j == i) if (j == i)
{ {
// TODO: fill reply memset (reply + SHORT_RESPONSE_RECORD_OPTIONS_OFFSET, 0, 2); // no options
reply[SHORT_RESPONSE_RECORD_RET_OFFSET] = retCode;
if (!i2p::crypto::AEADChaCha20Poly1305 (reply, SHORT_TUNNEL_BUILD_RECORD_SIZE - 16, if (!i2p::crypto::AEADChaCha20Poly1305 (reply, SHORT_TUNNEL_BUILD_RECORD_SIZE - 16,
noiseState.m_H, 32, noiseState.m_CK, nonce, reply, SHORT_TUNNEL_BUILD_RECORD_SIZE, true)) // encrypt noiseState.m_H, 32, replyKey, nonce, reply, SHORT_TUNNEL_BUILD_RECORD_SIZE, true)) // encrypt
{ {
LogPrint (eLogWarning, "I2NP: Short reply AEAD encryption failed"); LogPrint (eLogWarning, "I2NP: Short reply AEAD encryption failed");
return; return;
} }
} }
else else
i2p::crypto::ChaCha20 (reply, SHORT_TUNNEL_BUILD_RECORD_SIZE, noiseState.m_CK, nonce, reply); i2p::crypto::ChaCha20 (reply, SHORT_TUNNEL_BUILD_RECORD_SIZE, replyKey, nonce, reply);
reply += SHORT_TUNNEL_BUILD_RECORD_SIZE; reply += SHORT_TUNNEL_BUILD_RECORD_SIZE;
} }
transports.SendMessage (clearText + SHORT_REQUEST_RECORD_NEXT_TUNNEL_OFFSET, // send reply
if (isEndpoint)
{
auto replyMsg = NewI2NPShortMessage ();
replyMsg->Concat (buf, len);
replyMsg->FillI2NPMessageHeader (eI2NPShortTunnelBuildReply, bufbe32toh (clearText + SHORT_REQUEST_RECORD_SEND_MSG_ID_OFFSET));
if (memcmp ((const uint8_t *)i2p::context.GetIdentHash (),
clearText + SHORT_REQUEST_RECORD_NEXT_IDENT_OFFSET, 32)) // reply IBGW is not local?
{
i2p::crypto::HKDF (noiseState.m_CK, nullptr, 0, "RGarlicKeyAndTag", noiseState.m_CK);
uint64_t tag;
memcpy (&tag, noiseState.m_CK, 8);
// we send it to reply tunnel
transports.SendMessage (clearText + SHORT_REQUEST_RECORD_NEXT_IDENT_OFFSET,
CreateTunnelGatewayMsg (bufbe32toh (clearText + SHORT_REQUEST_RECORD_NEXT_TUNNEL_OFFSET),
i2p::garlic::WrapECIESX25519Message (replyMsg, noiseState.m_CK + 32, tag)));
}
else
{
// IBGW is local
uint32_t tunnelID = bufbe32toh (clearText + SHORT_REQUEST_RECORD_NEXT_TUNNEL_OFFSET);
auto tunnel = i2p::tunnel::tunnels.GetTunnel (tunnelID);
if (tunnel)
tunnel->SendTunnelDataMsg (replyMsg);
else
LogPrint (eLogWarning, "I2NP: Tunnel ", tunnelID, " not found for short tunnel build reply");
}
}
else
transports.SendMessage (clearText + SHORT_REQUEST_RECORD_NEXT_IDENT_OFFSET,
CreateI2NPMessage (eI2NPShortTunnelBuild, buf, len, CreateI2NPMessage (eI2NPShortTunnelBuild, buf, len,
bufbe32toh (clearText + SHORT_REQUEST_RECORD_SEND_MSG_ID_OFFSET))); bufbe32toh (clearText + SHORT_REQUEST_RECORD_SEND_MSG_ID_OFFSET)));
}
return; return;
} }
record += SHORT_TUNNEL_BUILD_RECORD_SIZE; record += SHORT_TUNNEL_BUILD_RECORD_SIZE;
@ -691,7 +667,7 @@ namespace i2p
std::shared_ptr<I2NPMessage> CreateTunnelDataMsg (const uint8_t * buf) std::shared_ptr<I2NPMessage> CreateTunnelDataMsg (const uint8_t * buf)
{ {
auto msg = NewI2NPTunnelMessage (); auto msg = NewI2NPTunnelMessage (false);
msg->Concat (buf, i2p::tunnel::TUNNEL_DATA_MSG_SIZE); msg->Concat (buf, i2p::tunnel::TUNNEL_DATA_MSG_SIZE);
msg->FillI2NPMessageHeader (eI2NPTunnelData); msg->FillI2NPMessageHeader (eI2NPTunnelData);
return msg; return msg;
@ -699,7 +675,7 @@ namespace i2p
std::shared_ptr<I2NPMessage> CreateTunnelDataMsg (uint32_t tunnelID, const uint8_t * payload) std::shared_ptr<I2NPMessage> CreateTunnelDataMsg (uint32_t tunnelID, const uint8_t * payload)
{ {
auto msg = NewI2NPTunnelMessage (); auto msg = NewI2NPTunnelMessage (false);
htobe32buf (msg->GetPayload (), tunnelID); htobe32buf (msg->GetPayload (), tunnelID);
msg->len += 4; // tunnelID msg->len += 4; // tunnelID
msg->Concat (payload, i2p::tunnel::TUNNEL_DATA_MSG_SIZE - 4); msg->Concat (payload, i2p::tunnel::TUNNEL_DATA_MSG_SIZE - 4);
@ -707,9 +683,9 @@ namespace i2p
return msg; return msg;
} }
std::shared_ptr<I2NPMessage> CreateEmptyTunnelDataMsg () std::shared_ptr<I2NPMessage> CreateEmptyTunnelDataMsg (bool endpoint)
{ {
auto msg = NewI2NPTunnelMessage (); auto msg = NewI2NPTunnelMessage (endpoint);
msg->len += i2p::tunnel::TUNNEL_DATA_MSG_SIZE; msg->len += i2p::tunnel::TUNNEL_DATA_MSG_SIZE;
return msg; return msg;
} }
@ -803,12 +779,15 @@ namespace i2p
case eI2NPVariableTunnelBuild: case eI2NPVariableTunnelBuild:
HandleVariableTunnelBuildMsg (msgID, buf, size); HandleVariableTunnelBuildMsg (msgID, buf, size);
break; break;
case eI2NPVariableTunnelBuildReply:
HandleVariableTunnelBuildReplyMsg (msgID, buf, size);
break;
case eI2NPShortTunnelBuild: case eI2NPShortTunnelBuild:
HandleShortTunnelBuildMsg (msgID, buf, size); HandleShortTunnelBuildMsg (msgID, buf, size);
break; break;
case eI2NPVariableTunnelBuildReply:
HandleTunnelBuildReplyMsg (msgID, buf, size, false);
break;
case eI2NPShortTunnelBuildReply:
HandleTunnelBuildReplyMsg (msgID, buf, size, true);
break;
case eI2NPTunnelBuild: case eI2NPTunnelBuild:
HandleTunnelBuildMsg (buf, size); HandleTunnelBuildMsg (buf, size);
break; break;
@ -866,6 +845,7 @@ namespace i2p
case eI2NPTunnelBuild: case eI2NPTunnelBuild:
case eI2NPTunnelBuildReply: case eI2NPTunnelBuildReply:
case eI2NPShortTunnelBuild: case eI2NPShortTunnelBuild:
case eI2NPShortTunnelBuildReply:
// forward to tunnel thread // forward to tunnel thread
i2p::tunnel::tunnels.PostTunnelData (msg); i2p::tunnel::tunnels.PostTunnelData (msg);
break; break;

View File

@ -55,33 +55,12 @@ namespace i2p
// TunnelBuild // TunnelBuild
const size_t TUNNEL_BUILD_RECORD_SIZE = 528; const size_t TUNNEL_BUILD_RECORD_SIZE = 528;
const size_t SHORT_TUNNEL_BUILD_RECORD_SIZE = 236; const size_t SHORT_TUNNEL_BUILD_RECORD_SIZE = 218;
//BuildRequestRecordClearText
const size_t BUILD_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET = 0;
const size_t BUILD_REQUEST_RECORD_OUR_IDENT_OFFSET = BUILD_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET + 4;
const size_t BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET = BUILD_REQUEST_RECORD_OUR_IDENT_OFFSET + 32;
const size_t BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET = BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET + 4;
const size_t BUILD_REQUEST_RECORD_LAYER_KEY_OFFSET = BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET + 32;
const size_t BUILD_REQUEST_RECORD_IV_KEY_OFFSET = BUILD_REQUEST_RECORD_LAYER_KEY_OFFSET + 32;
const size_t BUILD_REQUEST_RECORD_REPLY_KEY_OFFSET = BUILD_REQUEST_RECORD_IV_KEY_OFFSET + 32;
const size_t BUILD_REQUEST_RECORD_REPLY_IV_OFFSET = BUILD_REQUEST_RECORD_REPLY_KEY_OFFSET + 32;
const size_t BUILD_REQUEST_RECORD_FLAG_OFFSET = BUILD_REQUEST_RECORD_REPLY_IV_OFFSET + 16;
const size_t BUILD_REQUEST_RECORD_REQUEST_TIME_OFFSET = BUILD_REQUEST_RECORD_FLAG_OFFSET + 1;
const size_t BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET = BUILD_REQUEST_RECORD_REQUEST_TIME_OFFSET + 4;
const size_t BUILD_REQUEST_RECORD_PADDING_OFFSET = BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET + 4;
const size_t BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE = 222;
// BuildRequestRecordEncrypted // BuildRequestRecordEncrypted
const size_t BUILD_REQUEST_RECORD_TO_PEER_OFFSET = 0; const size_t BUILD_REQUEST_RECORD_TO_PEER_OFFSET = 0;
const size_t BUILD_REQUEST_RECORD_ENCRYPTED_OFFSET = BUILD_REQUEST_RECORD_TO_PEER_OFFSET + 16; const size_t BUILD_REQUEST_RECORD_ENCRYPTED_OFFSET = BUILD_REQUEST_RECORD_TO_PEER_OFFSET + 16;
// BuildResponseRecord
const size_t BUILD_RESPONSE_RECORD_HASH_OFFSET = 0;
const size_t BUILD_RESPONSE_RECORD_PADDING_OFFSET = 32;
const size_t BUILD_RESPONSE_RECORD_PADDING_SIZE = 495;
const size_t BUILD_RESPONSE_RECORD_RET_OFFSET = BUILD_RESPONSE_RECORD_PADDING_OFFSET + BUILD_RESPONSE_RECORD_PADDING_SIZE;
// ECIES BuildRequestRecordClearText // ECIES BuildRequestRecordClearText
const size_t ECIES_BUILD_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET = 0; const size_t ECIES_BUILD_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET = 0;
const size_t ECIES_BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET = ECIES_BUILD_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET + 4; const size_t ECIES_BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET = ECIES_BUILD_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET + 4;
@ -113,7 +92,12 @@ namespace i2p
const size_t SHORT_REQUEST_RECORD_REQUEST_TIME_OFFSET = SHORT_REQUEST_RECORD_LAYER_ENCRYPTION_TYPE + 1; const size_t SHORT_REQUEST_RECORD_REQUEST_TIME_OFFSET = SHORT_REQUEST_RECORD_LAYER_ENCRYPTION_TYPE + 1;
const size_t SHORT_REQUEST_RECORD_REQUEST_EXPIRATION_OFFSET = SHORT_REQUEST_RECORD_REQUEST_TIME_OFFSET + 4; const size_t SHORT_REQUEST_RECORD_REQUEST_EXPIRATION_OFFSET = SHORT_REQUEST_RECORD_REQUEST_TIME_OFFSET + 4;
const size_t SHORT_REQUEST_RECORD_SEND_MSG_ID_OFFSET = SHORT_REQUEST_RECORD_REQUEST_EXPIRATION_OFFSET + 4; const size_t SHORT_REQUEST_RECORD_SEND_MSG_ID_OFFSET = SHORT_REQUEST_RECORD_REQUEST_EXPIRATION_OFFSET + 4;
const size_t SHORT_REQUEST_RECORD_CLEAR_TEXT_SIZE = 172; const size_t SHORT_REQUEST_RECORD_PADDING_OFFSET = SHORT_REQUEST_RECORD_SEND_MSG_ID_OFFSET + 4;
const size_t SHORT_REQUEST_RECORD_CLEAR_TEXT_SIZE = 154;
// ShortResponseRecord
const size_t SHORT_RESPONSE_RECORD_OPTIONS_OFFSET = 0;
const size_t SHORT_RESPONSE_RECORD_RET_OFFSET = 201;
enum I2NPMessageType enum I2NPMessageType
{ {
@ -131,7 +115,7 @@ namespace i2p
eI2NPVariableTunnelBuild = 23, eI2NPVariableTunnelBuild = 23,
eI2NPVariableTunnelBuildReply = 24, eI2NPVariableTunnelBuildReply = 24,
eI2NPShortTunnelBuild = 25, eI2NPShortTunnelBuild = 25,
eI2NPOutboundTunnelBuildReply = 26 eI2NPShortTunnelBuildReply = 26
}; };
const uint8_t TUNNEL_BUILD_RECORD_GATEWAY_FLAG = 0x80; const uint8_t TUNNEL_BUILD_RECORD_GATEWAY_FLAG = 0x80;
@ -278,7 +262,7 @@ namespace tunnel
std::shared_ptr<I2NPMessage> NewI2NPMessage (); std::shared_ptr<I2NPMessage> NewI2NPMessage ();
std::shared_ptr<I2NPMessage> NewI2NPShortMessage (); std::shared_ptr<I2NPMessage> NewI2NPShortMessage ();
std::shared_ptr<I2NPMessage> NewI2NPTunnelMessage (); std::shared_ptr<I2NPMessage> NewI2NPTunnelMessage (bool endpoint);
std::shared_ptr<I2NPMessage> NewI2NPMessage (size_t len); std::shared_ptr<I2NPMessage> NewI2NPMessage (size_t len);
std::shared_ptr<I2NPMessage> CreateI2NPMessage (I2NPMessageType msgType, const uint8_t * buf, size_t len, uint32_t replyMsgID = 0); std::shared_ptr<I2NPMessage> CreateI2NPMessage (I2NPMessageType msgType, const uint8_t * buf, size_t len, uint32_t replyMsgID = 0);
@ -299,15 +283,9 @@ namespace tunnel
std::shared_ptr<I2NPMessage> CreateDatabaseStoreMsg (std::shared_ptr<const i2p::data::LocalLeaseSet> leaseSet, uint32_t replyToken = 0, std::shared_ptr<const i2p::tunnel::InboundTunnel> replyTunnel = nullptr); std::shared_ptr<I2NPMessage> CreateDatabaseStoreMsg (std::shared_ptr<const i2p::data::LocalLeaseSet> leaseSet, uint32_t replyToken = 0, std::shared_ptr<const i2p::tunnel::InboundTunnel> replyTunnel = nullptr);
bool IsRouterInfoMsg (std::shared_ptr<I2NPMessage> msg); bool IsRouterInfoMsg (std::shared_ptr<I2NPMessage> msg);
bool HandleBuildRequestRecords (int num, uint8_t * records, uint8_t * clearText);
void HandleVariableTunnelBuildMsg (uint32_t replyMsgID, uint8_t * buf, size_t len);
void HandleVariableTunnelBuildReplyMsg (uint32_t replyMsgID, uint8_t * buf, size_t len);
void HandleShortTunnelBuildMsg (uint32_t replyMsgID, uint8_t * buf, size_t len);
void HandleTunnelBuildMsg (uint8_t * buf, size_t len);
std::shared_ptr<I2NPMessage> CreateTunnelDataMsg (const uint8_t * buf); std::shared_ptr<I2NPMessage> CreateTunnelDataMsg (const uint8_t * buf);
std::shared_ptr<I2NPMessage> CreateTunnelDataMsg (uint32_t tunnelID, const uint8_t * payload); std::shared_ptr<I2NPMessage> CreateTunnelDataMsg (uint32_t tunnelID, const uint8_t * payload);
std::shared_ptr<I2NPMessage> CreateEmptyTunnelDataMsg (); std::shared_ptr<I2NPMessage> CreateEmptyTunnelDataMsg (bool endpoint);
std::shared_ptr<I2NPMessage> CreateTunnelGatewayMsg (uint32_t tunnelID, const uint8_t * buf, size_t len); std::shared_ptr<I2NPMessage> CreateTunnelGatewayMsg (uint32_t tunnelID, const uint8_t * buf, size_t len);
std::shared_ptr<I2NPMessage> CreateTunnelGatewayMsg (uint32_t tunnelID, I2NPMessageType msgType, std::shared_ptr<I2NPMessage> CreateTunnelGatewayMsg (uint32_t tunnelID, I2NPMessageType msgType,

View File

@ -42,7 +42,7 @@ namespace data
} }
IdentityEx::IdentityEx (): IdentityEx::IdentityEx ():
m_ExtendedLen (0), m_ExtendedBuffer (nullptr) m_ExtendedLen (0)
{ {
} }
@ -119,11 +119,15 @@ namespace data
m_StandardIdentity.certificate[0] = CERTIFICATE_TYPE_KEY; m_StandardIdentity.certificate[0] = CERTIFICATE_TYPE_KEY;
htobe16buf (m_StandardIdentity.certificate + 1, m_ExtendedLen); htobe16buf (m_StandardIdentity.certificate + 1, m_ExtendedLen);
// fill extended buffer // fill extended buffer
m_ExtendedBuffer = new uint8_t[m_ExtendedLen];
htobe16buf (m_ExtendedBuffer, type); htobe16buf (m_ExtendedBuffer, type);
htobe16buf (m_ExtendedBuffer + 2, cryptoType); htobe16buf (m_ExtendedBuffer + 2, cryptoType);
if (excessLen && excessBuf) if (excessLen && excessBuf)
{ {
if (excessLen > MAX_EXTENDED_BUFFER_SIZE - 4)
{
LogPrint (eLogError, "Identity: Unexpected excessive signing key len ", excessLen);
excessLen = MAX_EXTENDED_BUFFER_SIZE - 4;
}
memcpy (m_ExtendedBuffer + 4, excessBuf, excessLen); memcpy (m_ExtendedBuffer + 4, excessBuf, excessLen);
delete[] excessBuf; delete[] excessBuf;
} }
@ -136,7 +140,6 @@ namespace data
memset (m_StandardIdentity.certificate, 0, sizeof (m_StandardIdentity.certificate)); memset (m_StandardIdentity.certificate, 0, sizeof (m_StandardIdentity.certificate));
m_IdentHash = m_StandardIdentity.Hash (); m_IdentHash = m_StandardIdentity.Hash ();
m_ExtendedLen = 0; m_ExtendedLen = 0;
m_ExtendedBuffer = nullptr;
} }
CreateVerifier (); CreateVerifier ();
} }
@ -154,26 +157,25 @@ namespace data
} }
IdentityEx::IdentityEx (const uint8_t * buf, size_t len): IdentityEx::IdentityEx (const uint8_t * buf, size_t len):
m_ExtendedLen (0), m_ExtendedBuffer (nullptr) m_ExtendedLen (0)
{ {
FromBuffer (buf, len); FromBuffer (buf, len);
} }
IdentityEx::IdentityEx (const IdentityEx& other): IdentityEx::IdentityEx (const IdentityEx& other):
m_ExtendedLen (0), m_ExtendedBuffer (nullptr) m_ExtendedLen (0)
{ {
*this = other; *this = other;
} }
IdentityEx::IdentityEx (const Identity& standard): IdentityEx::IdentityEx (const Identity& standard):
m_ExtendedLen (0), m_ExtendedBuffer (nullptr) m_ExtendedLen (0)
{ {
*this = standard; *this = standard;
} }
IdentityEx::~IdentityEx () IdentityEx::~IdentityEx ()
{ {
delete[] m_ExtendedBuffer;
delete m_Verifier; delete m_Verifier;
} }
@ -182,15 +184,12 @@ namespace data
memcpy (&m_StandardIdentity, &other.m_StandardIdentity, DEFAULT_IDENTITY_SIZE); memcpy (&m_StandardIdentity, &other.m_StandardIdentity, DEFAULT_IDENTITY_SIZE);
m_IdentHash = other.m_IdentHash; m_IdentHash = other.m_IdentHash;
delete[] m_ExtendedBuffer;
m_ExtendedLen = other.m_ExtendedLen; m_ExtendedLen = other.m_ExtendedLen;
if (m_ExtendedLen > 0) if (m_ExtendedLen > 0)
{ {
m_ExtendedBuffer = new uint8_t[m_ExtendedLen]; if (m_ExtendedLen > MAX_EXTENDED_BUFFER_SIZE) m_ExtendedLen = MAX_EXTENDED_BUFFER_SIZE;
memcpy (m_ExtendedBuffer, other.m_ExtendedBuffer, m_ExtendedLen); memcpy (m_ExtendedBuffer, other.m_ExtendedBuffer, m_ExtendedLen);
} }
else
m_ExtendedBuffer = nullptr;
delete m_Verifier; delete m_Verifier;
m_Verifier = nullptr; m_Verifier = nullptr;
@ -203,8 +202,6 @@ namespace data
m_StandardIdentity = standard; m_StandardIdentity = standard;
m_IdentHash = m_StandardIdentity.Hash (); m_IdentHash = m_StandardIdentity.Hash ();
delete[] m_ExtendedBuffer;
m_ExtendedBuffer = nullptr;
m_ExtendedLen = 0; m_ExtendedLen = 0;
delete m_Verifier; delete m_Verifier;
@ -222,15 +219,12 @@ namespace data
} }
memcpy (&m_StandardIdentity, buf, DEFAULT_IDENTITY_SIZE); memcpy (&m_StandardIdentity, buf, DEFAULT_IDENTITY_SIZE);
if(m_ExtendedBuffer) delete[] m_ExtendedBuffer;
m_ExtendedBuffer = nullptr;
m_ExtendedLen = bufbe16toh (m_StandardIdentity.certificate + 1); m_ExtendedLen = bufbe16toh (m_StandardIdentity.certificate + 1);
if (m_ExtendedLen) if (m_ExtendedLen)
{ {
if (m_ExtendedLen + DEFAULT_IDENTITY_SIZE <= len) if (m_ExtendedLen + DEFAULT_IDENTITY_SIZE <= len)
{ {
m_ExtendedBuffer = new uint8_t[m_ExtendedLen]; if (m_ExtendedLen > MAX_EXTENDED_BUFFER_SIZE) m_ExtendedLen = MAX_EXTENDED_BUFFER_SIZE;
memcpy (m_ExtendedBuffer, buf + DEFAULT_IDENTITY_SIZE, m_ExtendedLen); memcpy (m_ExtendedBuffer, buf + DEFAULT_IDENTITY_SIZE, m_ExtendedLen);
} }
else else
@ -241,10 +235,7 @@ namespace data
} }
} }
else else
{
m_ExtendedLen = 0; m_ExtendedLen = 0;
m_ExtendedBuffer = nullptr;
}
SHA256(buf, GetFullLen (), m_IdentHash); SHA256(buf, GetFullLen (), m_IdentHash);
delete m_Verifier; delete m_Verifier;
@ -258,7 +249,7 @@ namespace data
const size_t fullLen = GetFullLen(); const size_t fullLen = GetFullLen();
if (fullLen > len) return 0; // buffer is too small and may overflow somewhere else if (fullLen > len) return 0; // buffer is too small and may overflow somewhere else
memcpy (buf, &m_StandardIdentity, DEFAULT_IDENTITY_SIZE); memcpy (buf, &m_StandardIdentity, DEFAULT_IDENTITY_SIZE);
if (m_ExtendedLen > 0 && m_ExtendedBuffer) if (m_ExtendedLen > 0)
memcpy (buf + DEFAULT_IDENTITY_SIZE, m_ExtendedBuffer, m_ExtendedLen); memcpy (buf + DEFAULT_IDENTITY_SIZE, m_ExtendedBuffer, m_ExtendedLen);
return fullLen; return fullLen;
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2020, The PurpleI2P Project * Copyright (c) 2013-2021, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@ -84,6 +84,7 @@ namespace data
typedef uint16_t SigningKeyType; typedef uint16_t SigningKeyType;
typedef uint16_t CryptoKeyType; typedef uint16_t CryptoKeyType;
const size_t MAX_EXTENDED_BUFFER_SIZE = 8; // cryptoKeyType + signingKeyType + 4 extra bytes of P521
class IdentityEx class IdentityEx
{ {
public: public:
@ -137,7 +138,7 @@ namespace data
mutable i2p::crypto::Verifier * m_Verifier = nullptr; mutable i2p::crypto::Verifier * m_Verifier = nullptr;
mutable std::mutex m_VerifierMutex; mutable std::mutex m_VerifierMutex;
size_t m_ExtendedLen; size_t m_ExtendedLen;
uint8_t * m_ExtendedBuffer; uint8_t m_ExtendedBuffer[MAX_EXTENDED_BUFFER_SIZE];
}; };
class PrivateKeys // for eepsites class PrivateKeys // for eepsites
@ -222,7 +223,7 @@ namespace data
virtual ~RoutingDestination () {}; virtual ~RoutingDestination () {};
virtual std::shared_ptr<const IdentityEx> GetIdentity () const = 0; virtual std::shared_ptr<const IdentityEx> GetIdentity () const = 0;
virtual void Encrypt (const uint8_t * data, uint8_t * encrypted, BN_CTX * ctx) const = 0; // encrypt data for virtual void Encrypt (const uint8_t * data, uint8_t * encrypted) const = 0; // encrypt data for
virtual bool IsDestination () const = 0; // for garlic virtual bool IsDestination () const = 0; // for garlic
const IdentHash& GetIdentHash () const { return GetIdentity ()->GetIdentHash (); }; const IdentHash& GetIdentHash () const { return GetIdentity ()->GetIdentHash (); };
@ -234,7 +235,7 @@ namespace data
public: public:
virtual ~LocalDestination() {}; virtual ~LocalDestination() {};
virtual bool Decrypt (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx, CryptoKeyType preferredCrypto = CRYPTO_KEY_TYPE_ELGAMAL) const = 0; virtual bool Decrypt (const uint8_t * encrypted, uint8_t * data, CryptoKeyType preferredCrypto = CRYPTO_KEY_TYPE_ELGAMAL) const = 0;
virtual std::shared_ptr<const IdentityEx> GetIdentity () const = 0; virtual std::shared_ptr<const IdentityEx> GetIdentity () const = 0;
const IdentHash& GetIdentHash () const { return GetIdentity ()->GetIdentHash (); }; const IdentHash& GetIdentHash () const { return GetIdentity ()->GetIdentHash (); };

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2020, The PurpleI2P Project * Copyright (c) 2013-2021, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@ -254,12 +254,12 @@ namespace data
return ts > m_ExpirationTime; return ts > m_ExpirationTime;
} }
void LeaseSet::Encrypt (const uint8_t * data, uint8_t * encrypted, BN_CTX * ctx) const void LeaseSet::Encrypt (const uint8_t * data, uint8_t * encrypted) const
{ {
if (!m_EncryptionKey) return; if (!m_EncryptionKey) return;
auto encryptor = m_Identity->CreateEncryptor (m_EncryptionKey); auto encryptor = m_Identity->CreateEncryptor (m_EncryptionKey);
if (encryptor) if (encryptor)
encryptor->Encrypt (data, encrypted, ctx, true); encryptor->Encrypt (data, encrypted);
} }
void LeaseSet::SetBuffer (const uint8_t * buf, size_t len) void LeaseSet::SetBuffer (const uint8_t * buf, size_t len)
@ -658,11 +658,11 @@ namespace data
return offset - 1; return offset - 1;
} }
void LeaseSet2::Encrypt (const uint8_t * data, uint8_t * encrypted, BN_CTX * ctx) const void LeaseSet2::Encrypt (const uint8_t * data, uint8_t * encrypted) const
{ {
auto encryptor = m_Encryptor; // TODO: atomic auto encryptor = m_Encryptor; // TODO: atomic
if (encryptor) if (encryptor)
encryptor->Encrypt (data, encrypted, ctx, true); encryptor->Encrypt (data, encrypted);
} }
uint64_t LeaseSet2::ExtractExpirationTimestamp (const uint8_t * buf, size_t len) const uint64_t LeaseSet2::ExtractExpirationTimestamp (const uint8_t * buf, size_t len) const
@ -912,6 +912,11 @@ namespace data
uint8_t blindedPriv[64], blindedPub[128]; // 64 and 128 max uint8_t blindedPriv[64], blindedPub[128]; // 64 and 128 max
size_t publicKeyLen = blindedKey.BlindPrivateKey (keys.GetSigningPrivateKey (), date, blindedPriv, blindedPub); size_t publicKeyLen = blindedKey.BlindPrivateKey (keys.GetSigningPrivateKey (), date, blindedPriv, blindedPub);
std::unique_ptr<i2p::crypto::Signer> blindedSigner (i2p::data::PrivateKeys::CreateSigner (blindedKey.GetBlindedSigType (), blindedPriv)); std::unique_ptr<i2p::crypto::Signer> blindedSigner (i2p::data::PrivateKeys::CreateSigner (blindedKey.GetBlindedSigType (), blindedPriv));
if (!blindedSigner)
{
LogPrint (eLogError, "LeaseSet2: Can't create blinded signer for signature type ", blindedKey.GetSigType ());
return;
}
auto offset = 1; auto offset = 1;
htobe16buf (m_Buffer + offset, blindedKey.GetBlindedSigType ()); offset += 2; // Blinded Public Key Sig Type htobe16buf (m_Buffer + offset, blindedKey.GetBlindedSigType ()); offset += 2; // Blinded Public Key Sig Type
memcpy (m_Buffer + offset, blindedPub, publicKeyLen); offset += publicKeyLen; // Blinded Public Key memcpy (m_Buffer + offset, blindedPub, publicKeyLen); offset += publicKeyLen; // Blinded Public Key

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2020, The PurpleI2P Project * Copyright (c) 2013-2021, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@ -93,7 +93,7 @@ namespace data
// implements RoutingDestination // implements RoutingDestination
std::shared_ptr<const IdentityEx> GetIdentity () const { return m_Identity; }; std::shared_ptr<const IdentityEx> GetIdentity () const { return m_Identity; };
void Encrypt (const uint8_t * data, uint8_t * encrypted, BN_CTX * ctx) const; void Encrypt (const uint8_t * data, uint8_t * encrypted) const;
bool IsDestination () const { return true; }; bool IsDestination () const { return true; };
protected: protected:
@ -156,7 +156,7 @@ namespace data
bool IsNewer (const uint8_t * buf, size_t len) const; bool IsNewer (const uint8_t * buf, size_t len) const;
// implements RoutingDestination // implements RoutingDestination
void Encrypt (const uint8_t * data, uint8_t * encrypted, BN_CTX * ctx) const; void Encrypt (const uint8_t * data, uint8_t * encrypted) const;
CryptoKeyType GetEncryptionType () const { return m_EncryptionType; }; CryptoKeyType GetEncryptionType () const { return m_EncryptionType; };
private: private:

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2020, The PurpleI2P Project * Copyright (c) 2013-2021, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@ -32,14 +32,12 @@ namespace i2p
namespace transport namespace transport
{ {
NTCP2Establisher::NTCP2Establisher (): NTCP2Establisher::NTCP2Establisher ():
m_SessionRequestBuffer (nullptr), m_SessionCreatedBuffer (nullptr), m_SessionConfirmedBuffer (nullptr) m_SessionConfirmedBuffer (nullptr)
{ {
} }
NTCP2Establisher::~NTCP2Establisher () NTCP2Establisher::~NTCP2Establisher ()
{ {
delete[] m_SessionRequestBuffer;
delete[] m_SessionCreatedBuffer;
delete[] m_SessionConfirmedBuffer; delete[] m_SessionConfirmedBuffer;
} }
@ -112,9 +110,8 @@ namespace transport
void NTCP2Establisher::CreateSessionRequestMessage () void NTCP2Establisher::CreateSessionRequestMessage ()
{ {
// create buffer and fill padding // create buffer and fill padding
auto paddingLength = rand () % (287 - 64); // message length doesn't exceed 287 bytes auto paddingLength = rand () % (NTCP2_SESSION_REQUEST_MAX_SIZE - 64); // message length doesn't exceed 287 bytes
m_SessionRequestBufferLen = paddingLength + 64; m_SessionRequestBufferLen = paddingLength + 64;
m_SessionRequestBuffer = new uint8_t[m_SessionRequestBufferLen];
RAND_bytes (m_SessionRequestBuffer + 64, paddingLength); RAND_bytes (m_SessionRequestBuffer + 64, paddingLength);
// encrypt X // encrypt X
i2p::crypto::CBCEncryption encryption; i2p::crypto::CBCEncryption encryption;
@ -152,9 +149,8 @@ namespace transport
void NTCP2Establisher::CreateSessionCreatedMessage () void NTCP2Establisher::CreateSessionCreatedMessage ()
{ {
auto paddingLen = rand () % (287 - 64); auto paddingLen = rand () % (NTCP2_SESSION_CREATED_MAX_SIZE - 64);
m_SessionCreatedBufferLen = paddingLen + 64; m_SessionCreatedBufferLen = paddingLen + 64;
m_SessionCreatedBuffer = new uint8_t[m_SessionCreatedBufferLen];
RAND_bytes (m_SessionCreatedBuffer + 64, paddingLen); RAND_bytes (m_SessionCreatedBuffer + 64, paddingLen);
// encrypt Y // encrypt Y
i2p::crypto::CBCEncryption encryption; i2p::crypto::CBCEncryption encryption;
@ -332,7 +328,8 @@ namespace transport
m_SendMDCtx(nullptr), m_ReceiveMDCtx (nullptr), m_SendMDCtx(nullptr), m_ReceiveMDCtx (nullptr),
#endif #endif
m_NextReceivedLen (0), m_NextReceivedBuffer (nullptr), m_NextSendBuffer (nullptr), m_NextReceivedLen (0), m_NextReceivedBuffer (nullptr), m_NextSendBuffer (nullptr),
m_ReceiveSequenceNumber (0), m_SendSequenceNumber (0), m_IsSending (false) m_NextReceivedBufferSize (0), m_ReceiveSequenceNumber (0), m_SendSequenceNumber (0),
m_IsSending (false), m_IsReceiving (false), m_NextPaddingSize (16)
{ {
if (in_RemoteRouter) // Alice if (in_RemoteRouter) // Alice
{ {
@ -404,6 +401,29 @@ namespace transport
htole64buf (nonce + 4, seqn); htole64buf (nonce + 4, seqn);
} }
void NTCP2Session::CreateNextReceivedBuffer (size_t size)
{
if (m_NextReceivedBuffer)
{
if (size <= m_NextReceivedBufferSize)
return; // buffer is good, do nothing
else
delete[] m_NextReceivedBuffer;
}
m_NextReceivedBuffer = new uint8_t[size];
m_NextReceivedBufferSize = size;
}
void NTCP2Session::DeleteNextReceiveBuffer (uint64_t ts)
{
if (m_NextReceivedBuffer && !m_IsReceiving &&
ts > m_LastActivityTimestamp + NTCP2_RECEIVE_BUFFER_DELETION_TIMEOUT)
{
delete[] m_NextReceivedBuffer;
m_NextReceivedBuffer = nullptr;
m_NextReceivedBufferSize = 0;
}
}
void NTCP2Session::KeyDerivationFunctionDataPhase () void NTCP2Session::KeyDerivationFunctionDataPhase ()
{ {
@ -439,7 +459,6 @@ namespace transport
} }
else else
{ {
m_Establisher->m_SessionCreatedBuffer = new uint8_t[287]; // TODO: determine actual max size
// we receive first 64 bytes (32 Y, and 32 ChaCha/Poly frame) first // we receive first 64 bytes (32 Y, and 32 ChaCha/Poly frame) first
boost::asio::async_read (m_Socket, boost::asio::buffer(m_Establisher->m_SessionCreatedBuffer, 64), boost::asio::transfer_all (), boost::asio::async_read (m_Socket, boost::asio::buffer(m_Establisher->m_SessionCreatedBuffer, 64), boost::asio::transfer_all (),
std::bind(&NTCP2Session::HandleSessionCreatedReceived, shared_from_this (), std::placeholders::_1, std::placeholders::_2)); std::bind(&NTCP2Session::HandleSessionCreatedReceived, shared_from_this (), std::placeholders::_1, std::placeholders::_2));
@ -462,7 +481,7 @@ namespace transport
{ {
if (paddingLen > 0) if (paddingLen > 0)
{ {
if (paddingLen <= 287 - 64) // session request is 287 bytes max if (paddingLen <= NTCP2_SESSION_REQUEST_MAX_SIZE - 64) // session request is 287 bytes max
{ {
boost::asio::async_read (m_Socket, boost::asio::buffer(m_Establisher->m_SessionRequestBuffer + 64, paddingLen), boost::asio::transfer_all (), boost::asio::async_read (m_Socket, boost::asio::buffer(m_Establisher->m_SessionRequestBuffer + 64, paddingLen), boost::asio::transfer_all (),
std::bind(&NTCP2Session::HandleSessionRequestPaddingReceived, shared_from_this (), std::placeholders::_1, std::placeholders::_2)); std::bind(&NTCP2Session::HandleSessionRequestPaddingReceived, shared_from_this (), std::placeholders::_1, std::placeholders::_2));
@ -515,7 +534,7 @@ namespace transport
{ {
if (paddingLen > 0) if (paddingLen > 0)
{ {
if (paddingLen <= 287 - 64) // session created is 287 bytes max if (paddingLen <= NTCP2_SESSION_CREATED_MAX_SIZE - 64) // session created is 287 bytes max
{ {
boost::asio::async_read (m_Socket, boost::asio::buffer(m_Establisher->m_SessionCreatedBuffer + 64, paddingLen), boost::asio::transfer_all (), boost::asio::async_read (m_Socket, boost::asio::buffer(m_Establisher->m_SessionCreatedBuffer + 64, paddingLen), boost::asio::transfer_all (),
std::bind(&NTCP2Session::HandleSessionCreatedPaddingReceived, shared_from_this (), std::placeholders::_1, std::placeholders::_2)); std::bind(&NTCP2Session::HandleSessionCreatedPaddingReceived, shared_from_this (), std::placeholders::_1, std::placeholders::_2));
@ -718,7 +737,6 @@ namespace transport
void NTCP2Session::ServerLogin () void NTCP2Session::ServerLogin ()
{ {
m_Establisher->CreateEphemeralKey (); m_Establisher->CreateEphemeralKey ();
m_Establisher->m_SessionRequestBuffer = new uint8_t[287]; // 287 bytes max for now
boost::asio::async_read (m_Socket, boost::asio::buffer(m_Establisher->m_SessionRequestBuffer, 64), boost::asio::transfer_all (), boost::asio::async_read (m_Socket, boost::asio::buffer(m_Establisher->m_SessionRequestBuffer, 64), boost::asio::transfer_all (),
std::bind(&NTCP2Session::HandleSessionRequestReceived, shared_from_this (), std::bind(&NTCP2Session::HandleSessionRequestReceived, shared_from_this (),
std::placeholders::_1, std::placeholders::_2)); std::placeholders::_1, std::placeholders::_2));
@ -758,8 +776,7 @@ namespace transport
LogPrint (eLogDebug, "NTCP2: received length ", m_NextReceivedLen); LogPrint (eLogDebug, "NTCP2: received length ", m_NextReceivedLen);
if (m_NextReceivedLen >= 16) if (m_NextReceivedLen >= 16)
{ {
if (m_NextReceivedBuffer) delete[] m_NextReceivedBuffer; CreateNextReceivedBuffer (m_NextReceivedLen);
m_NextReceivedBuffer = new uint8_t[m_NextReceivedLen];
boost::system::error_code ec; boost::system::error_code ec;
size_t moreBytes = m_Socket.available(ec); size_t moreBytes = m_Socket.available(ec);
if (!ec && moreBytes >= m_NextReceivedLen) if (!ec && moreBytes >= m_NextReceivedLen)
@ -786,6 +803,7 @@ namespace transport
const int one = 1; const int one = 1;
setsockopt(m_Socket.native_handle(), IPPROTO_TCP, TCP_QUICKACK, &one, sizeof(one)); setsockopt(m_Socket.native_handle(), IPPROTO_TCP, TCP_QUICKACK, &one, sizeof(one));
#endif #endif
m_IsReceiving = true;
boost::asio::async_read (m_Socket, boost::asio::buffer(m_NextReceivedBuffer, m_NextReceivedLen), boost::asio::transfer_all (), boost::asio::async_read (m_Socket, boost::asio::buffer(m_NextReceivedBuffer, m_NextReceivedLen), boost::asio::transfer_all (),
std::bind(&NTCP2Session::HandleReceived, shared_from_this (), std::placeholders::_1, std::placeholders::_2)); std::bind(&NTCP2Session::HandleReceived, shared_from_this (), std::placeholders::_1, std::placeholders::_2));
} }
@ -809,7 +827,7 @@ namespace transport
{ {
LogPrint (eLogDebug, "NTCP2: received message decrypted"); LogPrint (eLogDebug, "NTCP2: received message decrypted");
ProcessNextFrame (m_NextReceivedBuffer, m_NextReceivedLen-16); ProcessNextFrame (m_NextReceivedBuffer, m_NextReceivedLen-16);
delete[] m_NextReceivedBuffer; m_NextReceivedBuffer = nullptr; // we don't need received buffer anymore m_IsReceiving = false;
ReceiveLength (); ReceiveLength ();
} }
else else
@ -1058,7 +1076,15 @@ namespace transport
size_t paddingSize = (msgLen*NTCP2_MAX_PADDING_RATIO)/100; size_t paddingSize = (msgLen*NTCP2_MAX_PADDING_RATIO)/100;
if (msgLen + paddingSize + 3 > NTCP2_UNENCRYPTED_FRAME_MAX_SIZE) paddingSize = NTCP2_UNENCRYPTED_FRAME_MAX_SIZE - msgLen -3; if (msgLen + paddingSize + 3 > NTCP2_UNENCRYPTED_FRAME_MAX_SIZE) paddingSize = NTCP2_UNENCRYPTED_FRAME_MAX_SIZE - msgLen -3;
if (paddingSize > len) paddingSize = len; if (paddingSize > len) paddingSize = len;
if (paddingSize) paddingSize = rand () % paddingSize; if (paddingSize)
{
if (m_NextPaddingSize >= 16)
{
RAND_bytes ((uint8_t *)m_PaddingSizes, sizeof (m_PaddingSizes));
m_NextPaddingSize = 0;
}
paddingSize = m_PaddingSizes[m_NextPaddingSize++] % paddingSize;
}
buf[0] = eNTCP2BlkPadding; // blk buf[0] = eNTCP2BlkPadding; // blk
htobe16buf (buf + 1, paddingSize); // size htobe16buf (buf + 1, paddingSize); // size
memset (buf + 3, 0, paddingSize); memset (buf + 3, 0, paddingSize);
@ -1439,6 +1465,8 @@ namespace transport
LogPrint (eLogDebug, "NTCP2: No activity for ", session->GetTerminationTimeout (), " seconds"); LogPrint (eLogDebug, "NTCP2: No activity for ", session->GetTerminationTimeout (), " seconds");
session->TerminateByTimeout (); // it doesn't change m_NTCP2Session right a way session->TerminateByTimeout (); // it doesn't change m_NTCP2Session right a way
} }
else
it.second->DeleteNextReceiveBuffer (ts);
// pending // pending
for (auto it = m_PendingIncomingSessions.begin (); it != m_PendingIncomingSessions.end ();) for (auto it = m_PendingIncomingSessions.begin (); it != m_PendingIncomingSessions.end ();)
{ {

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2020, The PurpleI2P Project * Copyright (c) 2013-2021, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@ -28,12 +28,15 @@ namespace transport
{ {
const size_t NTCP2_UNENCRYPTED_FRAME_MAX_SIZE = 65519; const size_t NTCP2_UNENCRYPTED_FRAME_MAX_SIZE = 65519;
const size_t NTCP2_SESSION_REQUEST_MAX_SIZE = 287;
const size_t NTCP2_SESSION_CREATED_MAX_SIZE = 287;
const int NTCP2_MAX_PADDING_RATIO = 6; // in % const int NTCP2_MAX_PADDING_RATIO = 6; // in %
const int NTCP2_CONNECT_TIMEOUT = 5; // 5 seconds const int NTCP2_CONNECT_TIMEOUT = 5; // 5 seconds
const int NTCP2_ESTABLISH_TIMEOUT = 10; // 10 seconds const int NTCP2_ESTABLISH_TIMEOUT = 10; // 10 seconds
const int NTCP2_TERMINATION_TIMEOUT = 120; // 2 minutes const int NTCP2_TERMINATION_TIMEOUT = 120; // 2 minutes
const int NTCP2_TERMINATION_CHECK_TIMEOUT = 30; // 30 seconds const int NTCP2_TERMINATION_CHECK_TIMEOUT = 30; // 30 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 = 25*60; // 25 minuntes in seconds
const int NTCP2_ROUTERINFO_RESEND_INTERVAL_THRESHOLD = 25*60; // 25 minuntes const int NTCP2_ROUTERINFO_RESEND_INTERVAL_THRESHOLD = 25*60; // 25 minuntes
@ -115,7 +118,8 @@ namespace transport
i2p::data::IdentHash m_RemoteIdentHash; i2p::data::IdentHash m_RemoteIdentHash;
uint16_t m3p2Len; uint16_t m3p2Len;
uint8_t * m_SessionRequestBuffer, * m_SessionCreatedBuffer, * m_SessionConfirmedBuffer; uint8_t m_SessionRequestBuffer[NTCP2_SESSION_REQUEST_MAX_SIZE],
m_SessionCreatedBuffer[NTCP2_SESSION_CREATED_MAX_SIZE], * m_SessionConfirmedBuffer;
size_t m_SessionRequestBufferLen, m_SessionCreatedBufferLen; size_t m_SessionRequestBufferLen, m_SessionCreatedBufferLen;
}; };
@ -132,6 +136,7 @@ namespace transport
void TerminateByTimeout (); void TerminateByTimeout ();
void Done (); void Done ();
void Close () { m_Socket.close (); }; // for accept void Close () { m_Socket.close (); }; // for accept
void DeleteNextReceiveBuffer (uint64_t ts);
boost::asio::ip::tcp::socket& GetSocket () { return m_Socket; }; boost::asio::ip::tcp::socket& GetSocket () { return m_Socket; };
const boost::asio::ip::tcp::endpoint& GetRemoteEndpoint () { return m_RemoteEndpoint; }; const boost::asio::ip::tcp::endpoint& GetRemoteEndpoint () { return m_RemoteEndpoint; };
@ -151,6 +156,7 @@ namespace transport
void Established (); void Established ();
void CreateNonce (uint64_t seqn, uint8_t * nonce); void CreateNonce (uint64_t seqn, uint8_t * nonce);
void CreateNextReceivedBuffer (size_t size);
void KeyDerivationFunctionDataPhase (); void KeyDerivationFunctionDataPhase ();
void SetSipKeys (const uint8_t * sendSipKey, const uint8_t * receiveSipKey); void SetSipKeys (const uint8_t * sendSipKey, const uint8_t * receiveSipKey);
@ -206,6 +212,7 @@ namespace transport
#endif #endif
uint16_t m_NextReceivedLen; uint16_t m_NextReceivedLen;
uint8_t * m_NextReceivedBuffer, * m_NextSendBuffer; uint8_t * m_NextReceivedBuffer, * m_NextSendBuffer;
size_t m_NextReceivedBufferSize;
union union
{ {
uint8_t buf[8]; uint8_t buf[8];
@ -215,9 +222,12 @@ namespace transport
i2p::I2NPMessagesHandler m_Handler; i2p::I2NPMessagesHandler m_Handler;
bool m_IsSending; bool m_IsSending, m_IsReceiving;
std::list<std::shared_ptr<I2NPMessage> > m_SendQueue; std::list<std::shared_ptr<I2NPMessage> > m_SendQueue;
uint64_t m_NextRouterInfoResendTime; // seconds since epoch uint64_t m_NextRouterInfoResendTime; // seconds since epoch
uint16_t m_PaddingSizes[16];
int m_NextPaddingSize;
}; };
class NTCP2Server: private i2p::util::RunnableServiceWithWork class NTCP2Server: private i2p::util::RunnableServiceWithWork

View File

@ -55,11 +55,25 @@ namespace data
Load (); Load ();
uint16_t threshold; i2p::config::GetOption("reseed.threshold", threshold); uint16_t threshold; i2p::config::GetOption("reseed.threshold", threshold);
if (m_RouterInfos.size () < threshold) // reseed if # of router less than threshold if (m_RouterInfos.size () < threshold || m_Floodfills.size () < NETDB_MIN_FLOODFILLS) // reseed if # of router less than threshold or too few floodfiils
{
Reseed (); Reseed ();
}
else if (!GetRandomRouter (i2p::context.GetSharedRouterInfo (), false)) else if (!GetRandomRouter (i2p::context.GetSharedRouterInfo (), false))
Reseed (); // we don't have a router we can connect to. Trying to reseed Reseed (); // we don't have a router we can connect to. Trying to reseed
auto it = m_RouterInfos.find (i2p::context.GetIdentHash ());
if (it != m_RouterInfos.end ())
{
// remove own router
m_RouterInfos.erase (it);
m_Floodfills.remove (it->second);
}
// insert own router
m_RouterInfos.emplace (i2p::context.GetIdentHash (), i2p::context.GetSharedRouterInfo ());
if (i2p::context.IsFloodfill ())
m_Floodfills.push_back (i2p::context.GetSharedRouterInfo ());
i2p::config::GetOption("persist.profiles", m_PersistProfiles); i2p::config::GetOption("persist.profiles", m_PersistProfiles);
m_IsRunning = true; m_IsRunning = true;
@ -162,10 +176,18 @@ namespace data
bool publish = false; bool publish = false;
if (m_PublishReplyToken) if (m_PublishReplyToken)
{ {
// next publishing attempt
if (ts - lastPublish >= NETDB_PUBLISH_CONFIRMATION_TIMEOUT) publish = true; if (ts - lastPublish >= NETDB_PUBLISH_CONFIRMATION_TIMEOUT) publish = true;
} }
else if (i2p::context.GetLastUpdateTime () > lastPublish || else if (i2p::context.GetLastUpdateTime () > lastPublish ||
ts - lastPublish >= NETDB_PUBLISH_INTERVAL) publish = true; ts - lastPublish >= NETDB_PUBLISH_INTERVAL)
{
// new publish
m_PublishExcluded.clear ();
if (i2p::context.IsFloodfill ())
m_PublishExcluded.insert (i2p::context.GetIdentHash ()); // do publish to ourselves
publish = true;
}
if (publish) // update timestamp and publish if (publish) // update timestamp and publish
{ {
i2p::context.UpdateTimestamp (ts); i2p::context.UpdateTimestamp (ts);
@ -453,15 +475,16 @@ namespace data
bool NetDb::LoadRouterInfo (const std::string & path) bool NetDb::LoadRouterInfo (const std::string & path)
{ {
auto r = std::make_shared<RouterInfo>(path); auto r = std::make_shared<RouterInfo>(path);
if (r->GetRouterIdentity () && !r->IsUnreachable () && if (r->GetRouterIdentity () && !r->IsUnreachable () && r->HasValidAddresses ())
(r->IsReachable () || !r->IsSSU (false) || m_LastLoad < r->GetTimestamp () + NETDB_INTRODUCEE_EXPIRATION_TIMEOUT*1000LL)) // 1 hour
{ {
r->DeleteBuffer (); r->DeleteBuffer ();
r->ClearProperties (); // properties are not used for regular routers r->ClearProperties (); // properties are not used for regular routers
m_RouterInfos[r->GetIdentHash ()] = r; if (m_RouterInfos.emplace (r->GetIdentHash (), r).second)
if (r->IsFloodfill () && r->IsReachable ()) // floodfill must be reachable {
if (r->IsFloodfill () && r->IsEligibleFloodfill ())
m_Floodfills.push_back (r); m_Floodfills.push_back (r);
} }
}
else else
{ {
LogPrint(eLogWarning, "NetDb: RI from ", path, " is invalid. Delete"); LogPrint(eLogWarning, "NetDb: RI from ", path, " is invalid. Delete");
@ -556,8 +579,9 @@ namespace data
void NetDb::SaveUpdated () void NetDb::SaveUpdated ()
{ {
int updatedCount = 0, deletedCount = 0; int updatedCount = 0, deletedCount = 0, deletedFloodfillsCount = 0;
auto total = m_RouterInfos.size (); auto total = m_RouterInfos.size ();
auto totalFloodfills = m_Floodfills.size ();
uint64_t expirationTimeout = NETDB_MAX_EXPIRATION_TIMEOUT*1000LL; uint64_t expirationTimeout = NETDB_MAX_EXPIRATION_TIMEOUT*1000LL;
uint64_t ts = i2p::util::GetMillisecondsSinceEpoch(); uint64_t ts = i2p::util::GetMillisecondsSinceEpoch();
auto uptime = i2p::context.GetUptime (); auto uptime = i2p::context.GetUptime ();
@ -567,8 +591,10 @@ namespace data
expirationTimeout = i2p::context.IsFloodfill () ? NETDB_FLOODFILL_EXPIRATION_TIMEOUT*1000LL : 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; NETDB_MIN_EXPIRATION_TIMEOUT*1000LL + (NETDB_MAX_EXPIRATION_TIMEOUT - NETDB_MIN_EXPIRATION_TIMEOUT)*1000LL*NETDB_MIN_ROUTERS/total;
auto own = i2p::context.GetSharedRouterInfo ();
for (auto& it: m_RouterInfos) for (auto& it: m_RouterInfos)
{ {
if (it.second == own) continue; // skip own
std::string ident = it.second->GetIdentHashBase64(); std::string ident = it.second->GetIdentHashBase64();
std::string path = m_Storage.Path(ident); std::string path = m_Storage.Path(ident);
if (it.second->IsUpdated ()) if (it.second->IsUpdated ())
@ -580,8 +606,9 @@ namespace data
updatedCount++; updatedCount++;
continue; continue;
} }
// make router reachable back if too few routers // make router reachable back if too few routers or floodfills
if (it.second->IsUnreachable () && total - deletedCount < NETDB_MIN_ROUTERS) if (it.second->IsUnreachable () && (total - deletedCount < NETDB_MIN_ROUTERS ||
(it.second->IsFloodfill () && totalFloodfills - deletedFloodfillsCount < NETDB_MIN_FLOODFILLS)))
it.second->SetUnreachable (false); it.second->SetUnreachable (false);
// find & mark expired routers // find & mark expired routers
if (!it.second->IsReachable () && it.second->IsSSU (false)) if (!it.second->IsReachable () && it.second->IsSSU (false))
@ -595,6 +622,7 @@ namespace data
if (it.second->IsUnreachable ()) if (it.second->IsUnreachable ())
{ {
if (it.second->IsFloodfill ()) deletedFloodfillsCount++;
// delete RI file // delete RI file
m_Storage.Remove(ident); m_Storage.Remove(ident);
deletedCount++; deletedCount++;
@ -935,7 +963,8 @@ namespace data
if (router) if (router)
{ {
LogPrint (eLogDebug, "NetDb: requested RouterInfo ", key, " found"); LogPrint (eLogDebug, "NetDb: requested RouterInfo ", key, " found");
router->LoadBuffer (); if (!router->GetBuffer ())
router->LoadBuffer (m_Storage.Path (router->GetIdentHashBase64 ()));
if (router->GetBuffer ()) if (router->GetBuffer ())
replyMsg = CreateDatabaseStoreMsg (router); replyMsg = CreateDatabaseStoreMsg (router);
} }
@ -988,7 +1017,7 @@ namespace data
{ {
uint64_t tag; uint64_t tag;
memcpy (&tag, excluded + 33, 8); memcpy (&tag, excluded + 33, 8);
replyMsg = i2p::garlic::WrapECIESX25519AEADRatchetMessage (replyMsg, sessionKey, tag); replyMsg = i2p::garlic::WrapECIESX25519Message (replyMsg, sessionKey, tag);
} }
else else
{ {
@ -1145,7 +1174,8 @@ namespace data
{ {
return !router->IsHidden () && router != compatibleWith && return !router->IsHidden () && router != compatibleWith &&
(reverse ? compatibleWith->IsReachableFrom (*router) : (reverse ? compatibleWith->IsReachableFrom (*router) :
router->IsReachableFrom (*compatibleWith)); router->IsReachableFrom (*compatibleWith)) &&
router->IsECIES ();
}); });
} }
@ -1154,8 +1184,8 @@ namespace data
return GetRandomRouter ( return GetRandomRouter (
[v4, &excluded](std::shared_ptr<const RouterInfo> router)->bool [v4, &excluded](std::shared_ptr<const RouterInfo> router)->bool
{ {
return !router->IsHidden () && router->IsPeerTesting (v4) && return !router->IsHidden () && router->IsECIES () &&
!excluded.count (router->GetIdentHash ()); router->IsPeerTesting (v4) && !excluded.count (router->GetIdentHash ());
}); });
} }
@ -1164,7 +1194,7 @@ namespace data
return GetRandomRouter ( return GetRandomRouter (
[](std::shared_ptr<const RouterInfo> router)->bool [](std::shared_ptr<const RouterInfo> router)->bool
{ {
return !router->IsHidden () && router->IsSSUV6 (); return !router->IsHidden () && router->IsECIES () && router->IsSSUV6 ();
}); });
} }
@ -1173,8 +1203,8 @@ namespace data
return GetRandomRouter ( return GetRandomRouter (
[v4, &excluded](std::shared_ptr<const RouterInfo> router)->bool [v4, &excluded](std::shared_ptr<const RouterInfo> router)->bool
{ {
return router->IsIntroducer (v4) && !excluded.count (router->GetIdentHash ()) && return !router->IsHidden () && router->IsECIES () && !router->IsFloodfill () && // floodfills don't send relay tag
!router->IsHidden () && !router->IsFloodfill (); // floodfills don't send relay tag router->IsIntroducer (v4) && !excluded.count (router->GetIdentHash ());
}); });
} }
@ -1187,7 +1217,8 @@ namespace data
(reverse ? compatibleWith->IsReachableFrom (*router) : (reverse ? compatibleWith->IsReachableFrom (*router) :
router->IsReachableFrom (*compatibleWith)) && router->IsReachableFrom (*compatibleWith)) &&
(router->GetCaps () & RouterInfo::eHighBandwidth) && (router->GetCaps () & RouterInfo::eHighBandwidth) &&
router->GetVersion () >= NETDB_MIN_HIGHBANDWIDTH_VERSION; router->GetVersion () >= NETDB_MIN_HIGHBANDWIDTH_VERSION &&
router->IsECIES ();
}); });
} }
@ -1196,23 +1227,55 @@ namespace data
{ {
if (m_RouterInfos.empty()) if (m_RouterInfos.empty())
return 0; return 0;
uint32_t ind = rand () % m_RouterInfos.size (); uint16_t inds[3];
for (int j = 0; j < 2; j++) RAND_bytes ((uint8_t *)inds, sizeof (inds));
{
uint32_t i = 0;
std::unique_lock<std::mutex> l(m_RouterInfosMutex); std::unique_lock<std::mutex> l(m_RouterInfosMutex);
for (const auto& it: m_RouterInfos) inds[0] %= m_RouterInfos.size ();
auto it = m_RouterInfos.begin ();
std::advance (it, inds[0]);
// try random router
if (it != m_RouterInfos.end () && !it->second->IsUnreachable () && filter (it->second))
return it->second;
// try some routers around
auto it1 = m_RouterInfos.begin ();
if (inds[0])
{ {
if (i >= ind) // before
{ inds[1] %= inds[0];
if (!it.second->IsUnreachable () && filter (it.second)) std::advance (it1, (inds[1] + inds[0])/2);
return it.second;
} }
else else
i++; it1 = it;
auto it2 = it;
if (inds[0] < m_RouterInfos.size () - 1)
{
// after
inds[2] %= (m_RouterInfos.size () - 1 - inds[0]); inds[2] /= 2;
std::advance (it2, inds[2]);
} }
// we couldn't find anything, try second pass // it1 - from, it2 - to
ind = 0; it = it1;
while (it != it2 && it != m_RouterInfos.end ())
{
if (!it->second->IsUnreachable () && filter (it->second))
return it->second;
it++;
}
// still not found, try from the begining
it = m_RouterInfos.begin ();
while (it != it1 && it != m_RouterInfos.end ())
{
if (!it->second->IsUnreachable () && filter (it->second))
return it->second;
it++;
}
// still not found, try to the begining
it = it2;
while (it != m_RouterInfos.end ())
{
if (!it->second->IsUnreachable () && filter (it->second))
return it->second;
it++;
} }
return nullptr; // seems we have too few routers return nullptr; // seems we have too few routers
} }

View File

@ -36,6 +36,7 @@ namespace i2p
namespace data namespace data
{ {
const int NETDB_MIN_ROUTERS = 90; const int NETDB_MIN_ROUTERS = 90;
const int NETDB_MIN_FLOODFILLS = 5;
const int NETDB_FLOODFILL_EXPIRATION_TIMEOUT = 60 * 60; // 1 hour, in seconds const int NETDB_FLOODFILL_EXPIRATION_TIMEOUT = 60 * 60; // 1 hour, in seconds
const int NETDB_INTRODUCEE_EXPIRATION_TIMEOUT = 65 * 60; const int NETDB_INTRODUCEE_EXPIRATION_TIMEOUT = 65 * 60;
const int NETDB_MIN_EXPIRATION_TIMEOUT = 90 * 60; // 1.5 hours const int NETDB_MIN_EXPIRATION_TIMEOUT = 90 * 60; // 1.5 hours
@ -44,7 +45,8 @@ namespace data
const int NETDB_PUBLISH_CONFIRMATION_TIMEOUT = 5; // in seconds const int NETDB_PUBLISH_CONFIRMATION_TIMEOUT = 5; // in seconds
const int NETDB_MAX_PUBLISH_EXCLUDED_FLOODFILLS = 15; const int NETDB_MAX_PUBLISH_EXCLUDED_FLOODFILLS = 15;
const int NETDB_MIN_HIGHBANDWIDTH_VERSION = MAKE_VERSION_NUMBER(0, 9, 36); // 0.9.36 const int NETDB_MIN_HIGHBANDWIDTH_VERSION = MAKE_VERSION_NUMBER(0, 9, 36); // 0.9.36
const int NETDB_MIN_FLOODFILL_VERSION = MAKE_VERSION_NUMBER(0, 9, 28); // 0.9.28 const int NETDB_MIN_FLOODFILL_VERSION = MAKE_VERSION_NUMBER(0, 9, 38); // 0.9.38
const int NETDB_MIN_SHORT_TUNNEL_BUILD_VERSION = MAKE_VERSION_NUMBER(0, 9, 51); // 0.9.51
/** function for visiting a leaseset stored in a floodfill */ /** function for visiting a leaseset stored in a floodfill */
typedef std::function<void(const IdentHash, std::shared_ptr<LeaseSet>)> LeaseSetVisitor; typedef std::function<void(const IdentHash, std::shared_ptr<LeaseSet>)> LeaseSetVisitor;

View File

@ -497,7 +497,8 @@ namespace data
void Reseeder::LoadCertificates () void Reseeder::LoadCertificates ()
{ {
std::string certDir = i2p::fs::DataDirPath("certificates", "reseed"); std::string certDir = i2p::fs::GetCertsDir() + i2p::fs::dirSep + "reseed";
std::vector<std::string> files; std::vector<std::string> files;
int numCertificates = 0; int numCertificates = 0;

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2020, The PurpleI2P Project * Copyright (c) 2013-2021, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@ -43,12 +43,9 @@ namespace i2p
m_Decryptor = m_Keys.CreateDecryptor (nullptr); m_Decryptor = m_Keys.CreateDecryptor (nullptr);
m_TunnelDecryptor = m_Keys.CreateDecryptor (nullptr); m_TunnelDecryptor = m_Keys.CreateDecryptor (nullptr);
UpdateRouterInfo (); UpdateRouterInfo ();
if (IsECIES ())
{
i2p::crypto::InitNoiseNState (m_InitialNoiseState, GetIdentity ()->GetEncryptionPublicKey ()); i2p::crypto::InitNoiseNState (m_InitialNoiseState, GetIdentity ()->GetEncryptionPublicKey ());
m_ECIESSession = std::make_shared<i2p::garlic::RouterIncomingRatchetSession>(m_InitialNoiseState); m_ECIESSession = std::make_shared<i2p::garlic::RouterIncomingRatchetSession>(m_InitialNoiseState);
} }
}
void RouterContext::CreateNewRouter () void RouterContext::CreateNewRouter ()
{ {
@ -470,6 +467,7 @@ namespace i2p
uint8_t caps = m_RouterInfo.GetCaps (); uint8_t caps = m_RouterInfo.GetCaps ();
caps &= ~i2p::data::RouterInfo::eReachable; caps &= ~i2p::data::RouterInfo::eReachable;
caps |= i2p::data::RouterInfo::eUnreachable; caps |= i2p::data::RouterInfo::eUnreachable;
if (v6 || !SupportsV6 ())
caps &= ~i2p::data::RouterInfo::eFloodfill; // can't be floodfill caps &= ~i2p::data::RouterInfo::eFloodfill; // can't be floodfill
m_RouterInfo.SetCaps (caps); m_RouterInfo.SetCaps (caps);
} }
@ -489,6 +487,7 @@ namespace i2p
if (ntcp2) if (ntcp2)
PublishNTCP2Address (port, false, v4, v6, false); PublishNTCP2Address (port, false, v4, v6, false);
// update // update
m_RouterInfo.UpdateSupportedTransports ();
UpdateRouterInfo (); UpdateRouterInfo ();
} }
@ -528,6 +527,7 @@ namespace i2p
} }
} }
// update // update
m_RouterInfo.UpdateSupportedTransports ();
UpdateRouterInfo (); UpdateRouterInfo ();
} }
@ -566,7 +566,9 @@ namespace i2p
{ {
bool ntcp2; i2p::config::GetOption("ntcp2.enabled", ntcp2); bool ntcp2; i2p::config::GetOption("ntcp2.enabled", ntcp2);
bool ntcp2Published; i2p::config::GetOption("ntcp2.published", ntcp2Published); bool ntcp2Published; i2p::config::GetOption("ntcp2.published", ntcp2Published);
if (ntcp2 && ntcp2Published) if (ntcp2)
{
if (ntcp2Published)
{ {
std::string ntcp2Host; std::string ntcp2Host;
if (!i2p::config::IsDefault ("ntcp2.addressv6")) if (!i2p::config::IsDefault ("ntcp2.addressv6"))
@ -577,6 +579,9 @@ namespace i2p
if (!ntcp2Port) ntcp2Port = port; if (!ntcp2Port) ntcp2Port = port;
m_RouterInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv, boost::asio::ip::address::from_string (ntcp2Host), ntcp2Port); m_RouterInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv, boost::asio::ip::address::from_string (ntcp2Host), ntcp2Port);
} }
else
m_RouterInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv, boost::asio::ip::address(), 0, i2p::data::RouterInfo::eV6);
}
} }
m_RouterInfo.EnableV6 (); m_RouterInfo.EnableV6 ();
} }
@ -630,7 +635,7 @@ namespace i2p
m_RouterInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv, boost::asio::ip::address::from_string (host), ntcp2Port); m_RouterInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv, boost::asio::ip::address::from_string (host), ntcp2Port);
} }
else else
m_RouterInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv); m_RouterInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv, boost::asio::ip::address(), 0, i2p::data::RouterInfo::eV4);
} }
} }
m_RouterInfo.EnableV4 (); m_RouterInfo.EnableV4 ();
@ -813,9 +818,9 @@ namespace i2p
i2p::HandleI2NPMessage (CreateI2NPMessage (buf, GetI2NPMessageLength (buf, len))); i2p::HandleI2NPMessage (CreateI2NPMessage (buf, GetI2NPMessageLength (buf, len)));
} }
bool RouterContext::HandleCloveI2NPMessage (I2NPMessageType typeID, const uint8_t * payload, size_t len) bool RouterContext::HandleCloveI2NPMessage (I2NPMessageType typeID, const uint8_t * payload, size_t len, uint32_t msgID)
{ {
auto msg = CreateI2NPMessage (typeID, payload, len); auto msg = CreateI2NPMessage (typeID, payload, len, msgID);
if (!msg) return false; if (!msg) return false;
i2p::HandleI2NPMessage (msg); i2p::HandleI2NPMessage (msg);
return true; return true;
@ -825,8 +830,6 @@ namespace i2p
void RouterContext::ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg) void RouterContext::ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg)
{ {
std::unique_lock<std::mutex> l(m_GarlicMutex); std::unique_lock<std::mutex> l(m_GarlicMutex);
if (IsECIES ())
{
uint8_t * buf = msg->GetPayload (); uint8_t * buf = msg->GetPayload ();
uint32_t len = bufbe32toh (buf); uint32_t len = bufbe32toh (buf);
if (len > msg->GetLength ()) if (len > msg->GetLength ())
@ -835,13 +838,14 @@ namespace i2p
return; return;
} }
buf += 4; buf += 4;
if (!HandleECIESx25519TagMessage (buf, len)) // try tag first
{
// then Noise_N one-time decryption
if (m_ECIESSession) if (m_ECIESSession)
m_ECIESSession->HandleNextMessage (buf, len); m_ECIESSession->HandleNextMessage (buf, len);
else else
LogPrint (eLogError, "Router: Session is not set for ECIES router"); LogPrint (eLogError, "Router: Session is not set for ECIES router");
} }
else
i2p::garlic::GarlicDestination::ProcessGarlicMessage (msg);
} }
void RouterContext::ProcessDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg) void RouterContext::ProcessDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg)
@ -866,23 +870,14 @@ namespace i2p
return std::chrono::duration_cast<std::chrono::seconds> (std::chrono::steady_clock::now() - m_StartupTime).count (); return std::chrono::duration_cast<std::chrono::seconds> (std::chrono::steady_clock::now() - m_StartupTime).count ();
} }
bool RouterContext::Decrypt (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx, i2p::data::CryptoKeyType preferredCrypto) const bool RouterContext::Decrypt (const uint8_t * encrypted, uint8_t * data, i2p::data::CryptoKeyType preferredCrypto) const
{ {
return m_Decryptor ? m_Decryptor->Decrypt (encrypted, data, ctx, true) : false; return m_Decryptor ? m_Decryptor->Decrypt (encrypted, data) : false;
} }
bool RouterContext::DecryptTunnelBuildRecord (const uint8_t * encrypted, uint8_t * data) bool RouterContext::DecryptTunnelBuildRecord (const uint8_t * encrypted, uint8_t * data)
{ {
if (IsECIES ())
return DecryptECIESTunnelBuildRecord (encrypted, data, ECIES_BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE); return DecryptECIESTunnelBuildRecord (encrypted, data, ECIES_BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE);
else
{
if (!m_TunnelDecryptor) return false;
BN_CTX * ctx = BN_CTX_new ();
bool success = m_TunnelDecryptor->Decrypt (encrypted, data, ctx, false);
BN_CTX_free (ctx);
return success;
}
} }
bool RouterContext::DecryptECIESTunnelBuildRecord (const uint8_t * encrypted, uint8_t * data, size_t clearTextSize) bool RouterContext::DecryptECIESTunnelBuildRecord (const uint8_t * encrypted, uint8_t * data, size_t clearTextSize)
@ -891,7 +886,7 @@ namespace i2p
m_CurrentNoiseState = m_InitialNoiseState; m_CurrentNoiseState = m_InitialNoiseState;
m_CurrentNoiseState.MixHash (encrypted, 32); // h = SHA256(h || sepk) m_CurrentNoiseState.MixHash (encrypted, 32); // h = SHA256(h || sepk)
uint8_t sharedSecret[32]; uint8_t sharedSecret[32];
if (!m_TunnelDecryptor->Decrypt (encrypted, sharedSecret, nullptr, false)) if (!m_TunnelDecryptor->Decrypt (encrypted, sharedSecret))
{ {
LogPrint (eLogWarning, "Router: Incorrect ephemeral public key"); LogPrint (eLogWarning, "Router: Incorrect ephemeral public key");
return false; return false;
@ -912,13 +907,7 @@ namespace i2p
bool RouterContext::DecryptTunnelShortRequestRecord (const uint8_t * encrypted, uint8_t * data) bool RouterContext::DecryptTunnelShortRequestRecord (const uint8_t * encrypted, uint8_t * data)
{ {
if (IsECIES ())
return DecryptECIESTunnelBuildRecord (encrypted, data, SHORT_REQUEST_RECORD_CLEAR_TEXT_SIZE); return DecryptECIESTunnelBuildRecord (encrypted, data, SHORT_REQUEST_RECORD_CLEAR_TEXT_SIZE);
else
{
LogPrint (eLogWarning, "Router: Can't decrypt short request record on non-ECIES router");
return false;
}
} }
i2p::crypto::X25519Keys& RouterContext::GetStaticKeys () i2p::crypto::X25519Keys& RouterContext::GetStaticKeys ()

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2020, The PurpleI2P Project * Copyright (c) 2013-2021, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@ -18,7 +18,6 @@
#include "Identity.h" #include "Identity.h"
#include "RouterInfo.h" #include "RouterInfo.h"
#include "Garlic.h" #include "Garlic.h"
#include "I18N_langs.h"
namespace i2p namespace i2p
{ {
@ -69,10 +68,10 @@ namespace garlic
const i2p::data::PrivateKeys& GetPrivateKeys () const { return m_Keys; }; const i2p::data::PrivateKeys& GetPrivateKeys () const { return m_Keys; };
i2p::data::RouterInfo& GetRouterInfo () { return m_RouterInfo; }; i2p::data::RouterInfo& GetRouterInfo () { return m_RouterInfo; };
std::shared_ptr<const i2p::data::RouterInfo> GetSharedRouterInfo () const std::shared_ptr<i2p::data::RouterInfo> GetSharedRouterInfo ()
{ {
return std::shared_ptr<const i2p::data::RouterInfo> (&m_RouterInfo, return std::shared_ptr<i2p::data::RouterInfo> (&m_RouterInfo,
[](const i2p::data::RouterInfo *) {}); [](i2p::data::RouterInfo *) {});
} }
std::shared_ptr<i2p::garlic::GarlicDestination> GetSharedDestination () std::shared_ptr<i2p::garlic::GarlicDestination> GetSharedDestination ()
{ {
@ -124,7 +123,6 @@ namespace garlic
void SetSupportsV6 (bool supportsV6); void SetSupportsV6 (bool supportsV6);
void SetSupportsV4 (bool supportsV4); void SetSupportsV4 (bool supportsV4);
void SetSupportsMesh (bool supportsmesh, const boost::asio::ip::address_v6& host); void SetSupportsMesh (bool supportsmesh, const boost::asio::ip::address_v6& host);
bool IsECIES () const { return GetIdentity ()->GetCryptoKeyType () == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD; };
i2p::crypto::NoiseSymmetricState& GetCurrentNoiseState () { return m_CurrentNoiseState; }; i2p::crypto::NoiseSymmetricState& GetCurrentNoiseState () { return m_CurrentNoiseState; };
void UpdateNTCP2V6Address (const boost::asio::ip::address& host); // called from Daemon. TODO: remove void UpdateNTCP2V6Address (const boost::asio::ip::address& host); // called from Daemon. TODO: remove
@ -134,7 +132,7 @@ namespace garlic
// implements LocalDestination // implements LocalDestination
std::shared_ptr<const i2p::data::IdentityEx> GetIdentity () const { return m_Keys.GetPublic (); }; std::shared_ptr<const i2p::data::IdentityEx> GetIdentity () const { return m_Keys.GetPublic (); };
bool Decrypt (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx, i2p::data::CryptoKeyType preferredCrypto) const; bool Decrypt (const uint8_t * encrypted, uint8_t * data, i2p::data::CryptoKeyType preferredCrypto) const;
void Sign (const uint8_t * buf, int len, uint8_t * signature) const { m_Keys.Sign (buf, len, signature); }; void Sign (const uint8_t * buf, int len, uint8_t * signature) const { m_Keys.Sign (buf, len, signature); };
void SetLeaseSetUpdated () {}; void SetLeaseSetUpdated () {};
@ -146,15 +144,11 @@ namespace garlic
void ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg); void ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg);
void ProcessDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg); void ProcessDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg);
// i18n
std::shared_ptr<const i2p::i18n::Locale> GetLanguage () { return m_Language; };
void SetLanguage (const std::shared_ptr<const i2p::i18n::Locale> language) { m_Language = language; };
protected: protected:
// implements GarlicDestination // implements GarlicDestination
void HandleI2NPMessage (const uint8_t * buf, size_t len); void HandleI2NPMessage (const uint8_t * buf, size_t len);
bool HandleCloveI2NPMessage (I2NPMessageType typeID, const uint8_t * payload, size_t len); bool HandleCloveI2NPMessage (I2NPMessageType typeID, const uint8_t * payload, size_t len, uint32_t msgID);
private: private:
@ -186,9 +180,6 @@ namespace garlic
std::unique_ptr<i2p::crypto::X25519Keys> m_StaticKeys; std::unique_ptr<i2p::crypto::X25519Keys> m_StaticKeys;
// for ECIESx25519 // for ECIESx25519
i2p::crypto::NoiseSymmetricState m_InitialNoiseState, m_CurrentNoiseState; i2p::crypto::NoiseSymmetricState m_InitialNoiseState, m_CurrentNoiseState;
// i18n
std::shared_ptr<const i2p::i18n::Locale> m_Language;
}; };
extern RouterContext context; extern RouterContext context;

View File

@ -35,12 +35,12 @@ namespace data
} }
RouterInfo::RouterInfo (const std::string& fullPath): RouterInfo::RouterInfo (const std::string& fullPath):
m_FullPath (fullPath), m_IsUpdated (false), m_IsUnreachable (false), m_IsUpdated (false), m_IsUnreachable (false), m_SupportedTransports (0),
m_SupportedTransports (0), m_ReachableTransports (0), m_Caps (0), m_Version (0) m_ReachableTransports (0), m_Caps (0), m_Version (0)
{ {
m_Addresses = boost::make_shared<Addresses>(); // create empty list m_Addresses = boost::make_shared<Addresses>(); // create empty list
m_Buffer = new uint8_t[MAX_RI_BUFFER_SIZE]; m_Buffer = new uint8_t[MAX_RI_BUFFER_SIZE];
ReadFromFile (); ReadFromFile (fullPath);
} }
RouterInfo::RouterInfo (const uint8_t * buf, int len): RouterInfo::RouterInfo (const uint8_t * buf, int len):
@ -113,16 +113,16 @@ namespace data
m_Timestamp = i2p::util::GetMillisecondsSinceEpoch (); m_Timestamp = i2p::util::GetMillisecondsSinceEpoch ();
} }
bool RouterInfo::LoadFile () bool RouterInfo::LoadFile (const std::string& fullPath)
{ {
std::ifstream s(m_FullPath, std::ifstream::binary); std::ifstream s(fullPath, std::ifstream::binary);
if (s.is_open ()) if (s.is_open ())
{ {
s.seekg (0,std::ios::end); s.seekg (0,std::ios::end);
m_BufferLen = s.tellg (); m_BufferLen = s.tellg ();
if (m_BufferLen < 40 || m_BufferLen > MAX_RI_BUFFER_SIZE) if (m_BufferLen < 40 || m_BufferLen > MAX_RI_BUFFER_SIZE)
{ {
LogPrint(eLogError, "RouterInfo: File", m_FullPath, " is malformed"); LogPrint(eLogError, "RouterInfo: File", fullPath, " is malformed");
return false; return false;
} }
s.seekg(0, std::ios::beg); s.seekg(0, std::ios::beg);
@ -131,15 +131,15 @@ namespace data
} }
else else
{ {
LogPrint (eLogError, "RouterInfo: Can't open file ", m_FullPath); LogPrint (eLogError, "RouterInfo: Can't open file ", fullPath);
return false; return false;
} }
return true; return true;
} }
void RouterInfo::ReadFromFile () void RouterInfo::ReadFromFile (const std::string& fullPath)
{ {
if (LoadFile ()) if (LoadFile (fullPath))
ReadFromBuffer (false); ReadFromBuffer (false);
else else
m_IsUnreachable = true; m_IsUnreachable = true;
@ -187,13 +187,15 @@ namespace data
void RouterInfo::ReadFromStream (std::istream& s) void RouterInfo::ReadFromStream (std::istream& s)
{ {
if (!s) return;
m_Caps = 0; m_Caps = 0;
s.read ((char *)&m_Timestamp, sizeof (m_Timestamp)); s.read ((char *)&m_Timestamp, sizeof (m_Timestamp));
m_Timestamp = be64toh (m_Timestamp); m_Timestamp = be64toh (m_Timestamp);
// read addresses // read addresses
auto addresses = boost::make_shared<Addresses>(); auto addresses = boost::make_shared<Addresses>();
uint8_t numAddresses; uint8_t numAddresses;
s.read ((char *)&numAddresses, sizeof (numAddresses)); if (!s) return; s.read ((char *)&numAddresses, sizeof (numAddresses));
addresses->reserve (numAddresses);
for (int i = 0; i < numAddresses; i++) for (int i = 0; i < numAddresses; i++)
{ {
uint8_t supportedTransports = 0; uint8_t supportedTransports = 0;
@ -281,7 +283,11 @@ namespace data
if (s) continue; else return; if (s) continue; else return;
} }
if (index >= address->ssu->introducers.size ()) if (index >= address->ssu->introducers.size ())
{
if (address->ssu->introducers.empty ()) // first time
address->ssu->introducers.reserve (3);
address->ssu->introducers.resize (index + 1); address->ssu->introducers.resize (index + 1);
}
Introducer& introducer = address->ssu->introducers.at (index); Introducer& introducer = address->ssu->introducers.at (index);
if (!strcmp (key, "ihost")) if (!strcmp (key, "ihost"))
{ {
@ -343,7 +349,8 @@ namespace data
int numValid = 0; int numValid = 0;
for (auto& it: address->ssu->introducers) for (auto& it: address->ssu->introducers)
{ {
if ((!it.iExp || ts <= it.iExp) && it.iPort > 0 && if (!it.iExp) it.iExp = m_Timestamp/1000 + NETDB_INTRODUCEE_EXPIRATION_TIMEOUT;
if (ts <= it.iExp && it.iPort > 0 &&
((it.iHost.is_v4 () && address->IsV4 ()) || (it.iHost.is_v6 () && address->IsV6 ()))) ((it.iHost.is_v4 () && address->IsV4 ()) || (it.iHost.is_v6 () && address->IsV6 ())))
numValid++; numValid++;
else else
@ -747,11 +754,11 @@ namespace data
return bufbe64toh (buf + size) > m_Timestamp; return bufbe64toh (buf + size) > m_Timestamp;
} }
const uint8_t * RouterInfo::LoadBuffer () const uint8_t * RouterInfo::LoadBuffer (const std::string& fullPath)
{ {
if (!m_Buffer) if (!m_Buffer)
{ {
if (LoadFile ()) if (LoadFile (fullPath))
LogPrint (eLogDebug, "RouterInfo: Buffer for ", GetIdentHashAbbreviation (GetIdentHash ()), " loaded from file"); LogPrint (eLogDebug, "RouterInfo: Buffer for ", GetIdentHashAbbreviation (GetIdentHash ()), " loaded from file");
} }
return m_Buffer; return m_Buffer;
@ -782,8 +789,8 @@ namespace data
bool RouterInfo::SaveToFile (const std::string& fullPath) bool RouterInfo::SaveToFile (const std::string& fullPath)
{ {
m_FullPath = fullPath; if (!m_Buffer)
if (!m_Buffer) { {
LogPrint (eLogError, "RouterInfo: Can't save, m_Buffer == NULL"); LogPrint (eLogError, "RouterInfo: Can't save, m_Buffer == NULL");
return false; return false;
} }
@ -840,21 +847,33 @@ namespace data
for (const auto& it: *m_Addresses) // don't insert same address twice for (const auto& it: *m_Addresses) // don't insert same address twice
if (*it == *addr) return; if (*it == *addr) return;
m_SupportedTransports |= addr->host.is_v6 () ? eSSUV6 : eSSUV4; m_SupportedTransports |= addr->host.is_v6 () ? eSSUV6 : eSSUV4;
m_ReachableTransports |= addr->host.is_v6 () ? eSSUV6 : eSSUV4;
m_Addresses->push_back(std::move(addr)); m_Addresses->push_back(std::move(addr));
} }
void RouterInfo::AddNTCP2Address (const uint8_t * staticKey, const uint8_t * iv, const boost::asio::ip::address& host, int port) void RouterInfo::AddNTCP2Address (const uint8_t * staticKey, const uint8_t * iv,
const boost::asio::ip::address& host, int port, uint8_t caps)
{ {
auto addr = std::make_shared<Address>(); auto addr = std::make_shared<Address>();
addr->host = host; addr->host = host;
addr->port = port; addr->port = port;
addr->transportStyle = eTransportNTCP; addr->transportStyle = eTransportNTCP;
addr->caps = 0; addr->caps = caps;
addr->date = 0; addr->date = 0;
addr->ntcp2.reset (new NTCP2Ext ()); addr->ntcp2.reset (new NTCP2Ext ());
if (port) addr->published = true; if (port) addr->published = true;
memcpy (addr->ntcp2->staticKey, staticKey, 32); memcpy (addr->ntcp2->staticKey, staticKey, 32);
memcpy (addr->ntcp2->iv, iv, 16); memcpy (addr->ntcp2->iv, iv, 16);
if (addr->IsV4 ())
{
m_SupportedTransports |= eNTCP2V4;
if (addr->published) m_ReachableTransports |= eNTCP2V4;
}
if (addr->IsV6 ())
{
m_SupportedTransports |= eNTCP2V6;
if (addr->published) m_ReachableTransports |= eNTCP2V6;
}
m_Addresses->push_back(std::move(addr)); m_Addresses->push_back(std::move(addr));
} }
@ -971,10 +990,10 @@ namespace data
{ {
if (!IsV6 ()) if (!IsV6 ())
{ {
m_SupportedTransports |= eSSUV6 | eNTCP2V6;
uint8_t addressCaps = AddressCaps::eV6; uint8_t addressCaps = AddressCaps::eV6;
if (IsV4 ()) addressCaps |= AddressCaps::eV4; if (IsV4 ()) addressCaps |= AddressCaps::eV4;
SetUnreachableAddressesTransportCaps (addressCaps); SetUnreachableAddressesTransportCaps (addressCaps);
UpdateSupportedTransports ();
} }
} }
@ -982,10 +1001,10 @@ namespace data
{ {
if (!IsV4 ()) if (!IsV4 ())
{ {
m_SupportedTransports |= eSSUV4 | eNTCP2V4;
uint8_t addressCaps = AddressCaps::eV4; uint8_t addressCaps = AddressCaps::eV4;
if (IsV6 ()) addressCaps |= AddressCaps::eV6; if (IsV6 ()) addressCaps |= AddressCaps::eV6;
SetUnreachableAddressesTransportCaps (addressCaps); SetUnreachableAddressesTransportCaps (addressCaps);
UpdateSupportedTransports ();
} }
} }
@ -994,7 +1013,6 @@ namespace data
{ {
if (IsV6 ()) if (IsV6 ())
{ {
m_SupportedTransports &= ~(eSSUV6 | eNTCP2V6);
for (auto it = m_Addresses->begin (); it != m_Addresses->end ();) for (auto it = m_Addresses->begin (); it != m_Addresses->end ();)
{ {
auto addr = *it; auto addr = *it;
@ -1011,6 +1029,7 @@ namespace data
else else
++it; ++it;
} }
UpdateSupportedTransports ();
} }
} }
@ -1018,7 +1037,6 @@ namespace data
{ {
if (IsV4 ()) if (IsV4 ())
{ {
m_SupportedTransports &= ~(eSSUV4 | eNTCP2V4);
for (auto it = m_Addresses->begin (); it != m_Addresses->end ();) for (auto it = m_Addresses->begin (); it != m_Addresses->end ();)
{ {
auto addr = *it; auto addr = *it;
@ -1035,13 +1053,17 @@ namespace data
else else
++it; ++it;
} }
UpdateSupportedTransports ();
} }
} }
void RouterInfo::EnableMesh () void RouterInfo::EnableMesh ()
{ {
if (!IsMesh ()) if (!IsMesh ())
{
m_SupportedTransports |= eNTCP2V6Mesh; m_SupportedTransports |= eNTCP2V6Mesh;
m_ReachableTransports |= eNTCP2V6Mesh;
}
} }
void RouterInfo::DisableMesh () void RouterInfo::DisableMesh ()
@ -1049,6 +1071,7 @@ namespace data
if (IsMesh ()) if (IsMesh ())
{ {
m_SupportedTransports &= ~eNTCP2V6Mesh; m_SupportedTransports &= ~eNTCP2V6Mesh;
m_ReachableTransports &= ~eNTCP2V6Mesh;
for (auto it = m_Addresses->begin (); it != m_Addresses->end ();) for (auto it = m_Addresses->begin (); it != m_Addresses->end ();)
{ {
auto addr = *it; auto addr = *it;
@ -1138,18 +1161,17 @@ namespace data
return m_Profile; return m_Profile;
} }
void RouterInfo::Encrypt (const uint8_t * data, uint8_t * encrypted, BN_CTX * ctx) const void RouterInfo::Encrypt (const uint8_t * data, uint8_t * encrypted) const
{ {
auto encryptor = m_RouterIdentity->CreateEncryptor (nullptr); auto encryptor = m_RouterIdentity->CreateEncryptor (nullptr);
if (encryptor) if (encryptor)
encryptor->Encrypt (data, encrypted, ctx, true); encryptor->Encrypt (data, encrypted);
} }
bool RouterInfo::IsEligibleFloodfill () const bool RouterInfo::IsEligibleFloodfill () const
{ {
// floodfill must be reachable somehow, >= 0.9.28 and not DSA // floodfill must be reachable by ipv4, >= 0.9.38 and not DSA
return (IsReachable () || (m_SupportedTransports & eSSUV4)) && return IsReachableBy (eNTCP2V4 | eSSUV4) && m_Version >= NETDB_MIN_FLOODFILL_VERSION &&
m_Version >= NETDB_MIN_FLOODFILL_VERSION &&
GetIdentity ()->GetSigningKeyType () != SIGNING_KEY_TYPE_DSA_SHA1; GetIdentity ()->GetSigningKeyType () != SIGNING_KEY_TYPE_DSA_SHA1;
} }
@ -1175,28 +1197,6 @@ namespace data
}); });
} }
bool RouterInfo::IsReachableFrom (const RouterInfo& other) const
{
auto commonTransports = m_SupportedTransports & other.m_SupportedTransports;
if (!commonTransports) return false;
if (commonTransports & eNTCP2V6Mesh) return true;
return (bool)GetAddress (
[commonTransports](std::shared_ptr<const RouterInfo::Address> address)->bool
{
if (address->IsPublishedNTCP2 ())
{
if ((commonTransports & eNTCP2V4) && address->IsV4 ()) return true;
if ((commonTransports & eNTCP2V6) && address->IsV6 ()) return true;
}
else if (address->IsReachableSSU ())
{
if ((commonTransports & eSSUV4) && address->IsV4 ()) return true;
if ((commonTransports & eSSUV6) && address->IsV6 ()) return true;
}
return false;
});
}
void RouterInfo::SetUnreachableAddressesTransportCaps (uint8_t transports) void RouterInfo::SetUnreachableAddressesTransportCaps (uint8_t transports)
{ {
for (auto& addr: *m_Addresses) for (auto& addr: *m_Addresses)

View File

@ -13,7 +13,6 @@
#include <string> #include <string>
#include <map> #include <map>
#include <vector> #include <vector>
#include <list>
#include <iostream> #include <iostream>
#include <boost/asio.hpp> #include <boost/asio.hpp>
#include <boost/shared_ptr.hpp> #include <boost/shared_ptr.hpp>
@ -67,6 +66,7 @@ namespace data
eSSUV6 = 0x08, eSSUV6 = 0x08,
eNTCP2V6Mesh = 0x10 eNTCP2V6Mesh = 0x10
}; };
typedef uint8_t CompatibleTransports;
enum Caps enum Caps
{ {
@ -156,7 +156,7 @@ namespace data
bool IsV4 () const { return (caps & AddressCaps::eV4) || (host.is_v4 () && !host.is_unspecified ()); }; bool IsV4 () const { return (caps & AddressCaps::eV4) || (host.is_v4 () && !host.is_unspecified ()); };
bool IsV6 () const { return (caps & AddressCaps::eV6) || (host.is_v6 () && !host.is_unspecified ()); }; bool IsV6 () const { return (caps & AddressCaps::eV6) || (host.is_v6 () && !host.is_unspecified ()); };
}; };
typedef std::list<std::shared_ptr<Address> > Addresses; typedef std::vector<std::shared_ptr<Address> > Addresses;
RouterInfo (); RouterInfo ();
RouterInfo (const std::string& fullPath); RouterInfo (const std::string& fullPath);
@ -179,7 +179,8 @@ namespace data
std::shared_ptr<const Address> GetYggdrasilAddress () const; std::shared_ptr<const Address> GetYggdrasilAddress () const;
void AddSSUAddress (const char * host, int port, const uint8_t * key, int mtu = 0); void AddSSUAddress (const char * host, int port, const uint8_t * key, int mtu = 0);
void AddNTCP2Address (const uint8_t * staticKey, const uint8_t * iv, const boost::asio::ip::address& host = boost::asio::ip::address(), int port = 0); void AddNTCP2Address (const uint8_t * staticKey, const uint8_t * iv,
const boost::asio::ip::address& host = boost::asio::ip::address(), int port = 0, uint8_t caps = 0);
bool AddIntroducer (const Introducer& introducer); bool AddIntroducer (const Introducer& introducer);
bool RemoveIntroducer (const boost::asio::ip::udp::endpoint& e); bool RemoveIntroducer (const boost::asio::ip::udp::endpoint& e);
void SetProperty (const std::string& key, const std::string& value); // called from RouterContext only void SetProperty (const std::string& key, const std::string& value); // called from RouterContext only
@ -190,6 +191,7 @@ namespace data
void UpdateSupportedTransports (); void UpdateSupportedTransports ();
bool IsFloodfill () const { return m_Caps & Caps::eFloodfill; }; bool IsFloodfill () const { return m_Caps & Caps::eFloodfill; };
bool IsReachable () const { return m_Caps & Caps::eReachable; }; bool IsReachable () const { return m_Caps & Caps::eReachable; };
bool IsECIES () const { return m_RouterIdentity->GetCryptoKeyType () == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD; };
bool IsSSU (bool v4only = true) const; bool IsSSU (bool v4only = true) const;
bool IsSSUV6 () const; bool IsSSUV6 () const;
bool IsNTCP2 (bool v4only = true) const; bool IsNTCP2 (bool v4only = true) const;
@ -204,7 +206,9 @@ namespace data
void EnableMesh (); void EnableMesh ();
void DisableMesh (); void DisableMesh ();
bool IsCompatible (const RouterInfo& other) const { return m_SupportedTransports & other.m_SupportedTransports; }; bool IsCompatible (const RouterInfo& other) const { return m_SupportedTransports & other.m_SupportedTransports; };
bool IsReachableFrom (const RouterInfo& other) const; 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; };
bool HasValidAddresses () const { return m_SupportedTransports; }; bool HasValidAddresses () const { return m_SupportedTransports; };
bool IsHidden () const { return m_Caps & eHidden; }; bool IsHidden () const { return m_Caps & eHidden; };
bool IsHighBandwidth () const { return m_Caps & RouterInfo::eHighBandwidth; }; bool IsHighBandwidth () const { return m_Caps & RouterInfo::eHighBandwidth; };
@ -221,7 +225,7 @@ namespace data
bool IsUnreachable () const { return m_IsUnreachable; }; bool IsUnreachable () const { return m_IsUnreachable; };
const uint8_t * GetBuffer () const { return m_Buffer; }; const uint8_t * GetBuffer () const { return m_Buffer; };
const uint8_t * LoadBuffer (); // load if necessary const uint8_t * LoadBuffer (const std::string& fullPath); // load if necessary
int GetBufferLen () const { return m_BufferLen; }; int GetBufferLen () const { return m_BufferLen; };
void CreateBuffer (const PrivateKeys& privateKeys); void CreateBuffer (const PrivateKeys& privateKeys);
@ -241,14 +245,14 @@ namespace data
// implements RoutingDestination // implements RoutingDestination
std::shared_ptr<const IdentityEx> GetIdentity () const { return m_RouterIdentity; }; std::shared_ptr<const IdentityEx> GetIdentity () const { return m_RouterIdentity; };
void Encrypt (const uint8_t * data, uint8_t * encrypted, BN_CTX * ctx) const; void Encrypt (const uint8_t * data, uint8_t * encrypted) const;
bool IsDestination () const { return false; }; bool IsDestination () const { return false; };
private: private:
bool LoadFile (); bool LoadFile (const std::string& fullPath);
void ReadFromFile (); void ReadFromFile (const std::string& fullPath);
void ReadFromStream (std::istream& s); void ReadFromStream (std::istream& s);
void ReadFromBuffer (bool verifySignature); void ReadFromBuffer (bool verifySignature);
void WriteToStream (std::ostream& s) const; void WriteToStream (std::ostream& s) const;
@ -262,7 +266,7 @@ namespace data
private: private:
std::string m_FullPath, m_Family; std::string m_Family;
std::shared_ptr<const IdentityEx> m_RouterIdentity; std::shared_ptr<const IdentityEx> m_RouterIdentity;
uint8_t * m_Buffer; uint8_t * m_Buffer;
size_t m_BufferLen; size_t m_BufferLen;
@ -270,7 +274,8 @@ namespace data
boost::shared_ptr<Addresses> m_Addresses; // TODO: use std::shared_ptr and std::atomic_store for gcc >= 4.9 boost::shared_ptr<Addresses> m_Addresses; // TODO: use std::shared_ptr and std::atomic_store for gcc >= 4.9
std::map<std::string, std::string> m_Properties; std::map<std::string, std::string> m_Properties;
bool m_IsUpdated, m_IsUnreachable; bool m_IsUpdated, m_IsUnreachable;
uint8_t m_SupportedTransports, m_ReachableTransports, m_Caps; CompatibleTransports m_SupportedTransports, m_ReachableTransports;
uint8_t m_Caps;
int m_Version; int m_Version;
mutable std::shared_ptr<RouterProfile> m_Profile; mutable std::shared_ptr<RouterProfile> m_Profile;
}; };

View File

@ -218,7 +218,7 @@ namespace transport
void SSUServer::AddRelay (uint32_t tag, std::shared_ptr<SSUSession> relay) void SSUServer::AddRelay (uint32_t tag, std::shared_ptr<SSUSession> relay)
{ {
m_Relays[tag] = relay; m_Relays.emplace (tag, relay);
} }
void SSUServer::RemoveRelay (uint32_t tag) void SSUServer::RemoveRelay (uint32_t tag)
@ -255,14 +255,14 @@ namespace transport
void SSUServer::Receive () void SSUServer::Receive ()
{ {
SSUPacket * packet = new SSUPacket (); SSUPacket * packet = m_PacketsPool.AcquireMt ();
m_Socket.async_receive_from (boost::asio::buffer (packet->buf, SSU_MTU_V4), packet->from, m_Socket.async_receive_from (boost::asio::buffer (packet->buf, SSU_MTU_V4), packet->from,
std::bind (&SSUServer::HandleReceivedFrom, this, std::placeholders::_1, std::placeholders::_2, packet)); std::bind (&SSUServer::HandleReceivedFrom, this, std::placeholders::_1, std::placeholders::_2, packet));
} }
void SSUServer::ReceiveV6 () void SSUServer::ReceiveV6 ()
{ {
SSUPacket * packet = new SSUPacket (); SSUPacket * packet = m_PacketsPool.AcquireMt ();
m_SocketV6.async_receive_from (boost::asio::buffer (packet->buf, SSU_MTU_V6), packet->from, m_SocketV6.async_receive_from (boost::asio::buffer (packet->buf, SSU_MTU_V6), packet->from,
std::bind (&SSUServer::HandleReceivedFromV6, this, std::placeholders::_1, std::placeholders::_2, packet)); std::bind (&SSUServer::HandleReceivedFromV6, this, std::placeholders::_1, std::placeholders::_2, packet));
} }
@ -293,7 +293,7 @@ namespace transport
{ {
while (moreBytes && packets.size () < 25) while (moreBytes && packets.size () < 25)
{ {
packet = new SSUPacket (); packet = m_PacketsPool.AcquireMt ();
packet->len = m_Socket.receive_from (boost::asio::buffer (packet->buf, SSU_MTU_V4), packet->from, 0, ec); packet->len = m_Socket.receive_from (boost::asio::buffer (packet->buf, SSU_MTU_V4), packet->from, 0, ec);
if (!ec) if (!ec)
{ {
@ -304,7 +304,7 @@ namespace transport
else else
{ {
LogPrint (eLogError, "SSU: receive_from error: code ", ec.value(), ": ", ec.message ()); LogPrint (eLogError, "SSU: receive_from error: code ", ec.value(), ": ", ec.message ());
delete packet; m_PacketsPool.ReleaseMt (packet);
break; break;
} }
} }
@ -315,7 +315,7 @@ namespace transport
} }
else else
{ {
delete packet; m_PacketsPool.ReleaseMt (packet);
if (ecode != boost::asio::error::operation_aborted) if (ecode != boost::asio::error::operation_aborted)
{ {
LogPrint (eLogError, "SSU: receive error: code ", ecode.value(), ": ", ecode.message ()); LogPrint (eLogError, "SSU: receive error: code ", ecode.value(), ": ", ecode.message ());
@ -352,7 +352,7 @@ namespace transport
{ {
while (moreBytes && packets.size () < 25) while (moreBytes && packets.size () < 25)
{ {
packet = new SSUPacket (); packet = m_PacketsPool.AcquireMt ();
packet->len = m_SocketV6.receive_from (boost::asio::buffer (packet->buf, SSU_MTU_V6), packet->from, 0, ec); packet->len = m_SocketV6.receive_from (boost::asio::buffer (packet->buf, SSU_MTU_V6), packet->from, 0, ec);
if (!ec) if (!ec)
{ {
@ -363,7 +363,7 @@ namespace transport
else else
{ {
LogPrint (eLogError, "SSU: v6 receive_from error: code ", ec.value(), ": ", ec.message ()); LogPrint (eLogError, "SSU: v6 receive_from error: code ", ec.value(), ": ", ec.message ());
delete packet; m_PacketsPool.ReleaseMt (packet);;
break; break;
} }
} }
@ -374,7 +374,7 @@ namespace transport
} }
else else
{ {
delete packet; m_PacketsPool.ReleaseMt (packet);
if (ecode != boost::asio::error::operation_aborted) if (ecode != boost::asio::error::operation_aborted)
{ {
LogPrint (eLogError, "SSU: v6 receive error: code ", ecode.value(), ": ", ecode.message ()); LogPrint (eLogError, "SSU: v6 receive error: code ", ecode.value(), ": ", ecode.message ());
@ -421,8 +421,8 @@ namespace transport
if (session) session->FlushData (); if (session) session->FlushData ();
session = nullptr; session = nullptr;
} }
delete packet;
} }
m_PacketsPool.ReleaseMt (packets);
if (session) session->FlushData (); if (session) session->FlushData ();
} }
@ -919,13 +919,19 @@ namespace transport
} }
if (numDeleted > 0) if (numDeleted > 0)
LogPrint (eLogDebug, "SSU: ", numDeleted, " peer tests have been expired"); LogPrint (eLogDebug, "SSU: ", numDeleted, " peer tests have been expired");
// some cleaups. TODO: use separate timer
m_FragmentsPool.CleanUp ();
m_IncompleteMessagesPool.CleanUp ();
m_SentMessagesPool.CleanUp ();
SchedulePeerTestsCleanupTimer (); SchedulePeerTestsCleanupTimer ();
} }
} }
void SSUServer::ScheduleTermination () void SSUServer::ScheduleTermination ()
{ {
m_TerminationTimer.expires_from_now (boost::posix_time::seconds(SSU_TERMINATION_CHECK_TIMEOUT)); uint64_t timeout = SSU_TERMINATION_CHECK_TIMEOUT + (rand () % SSU_TERMINATION_CHECK_TIMEOUT)/5;
m_TerminationTimer.expires_from_now (boost::posix_time::seconds(timeout));
m_TerminationTimer.async_wait (std::bind (&SSUServer::HandleTerminationTimer, m_TerminationTimer.async_wait (std::bind (&SSUServer::HandleTerminationTimer,
this, std::placeholders::_1)); this, std::placeholders::_1));
} }
@ -947,13 +953,16 @@ namespace transport
session->Failed (); session->Failed ();
}); });
} }
else
it.second->CleanUp (ts);
ScheduleTermination (); ScheduleTermination ();
} }
} }
void SSUServer::ScheduleTerminationV6 () void SSUServer::ScheduleTerminationV6 ()
{ {
m_TerminationTimerV6.expires_from_now (boost::posix_time::seconds(SSU_TERMINATION_CHECK_TIMEOUT)); uint64_t timeout = SSU_TERMINATION_CHECK_TIMEOUT + (rand () % SSU_TERMINATION_CHECK_TIMEOUT)/5;
m_TerminationTimerV6.expires_from_now (boost::posix_time::seconds(timeout));
m_TerminationTimerV6.async_wait (std::bind (&SSUServer::HandleTerminationTimerV6, m_TerminationTimerV6.async_wait (std::bind (&SSUServer::HandleTerminationTimerV6,
this, std::placeholders::_1)); this, std::placeholders::_1));
} }
@ -975,6 +984,8 @@ namespace transport
session->Failed (); session->Failed ();
}); });
} }
else
it.second->CleanUp (ts);
ScheduleTerminationV6 (); ScheduleTerminationV6 ();
} }
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2020, The PurpleI2P Project * Copyright (c) 2013-2021, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@ -18,6 +18,7 @@
#include <mutex> #include <mutex>
#include <boost/asio.hpp> #include <boost/asio.hpp>
#include "Crypto.h" #include "Crypto.h"
#include "util.h"
#include "I2PEndian.h" #include "I2PEndian.h"
#include "Identity.h" #include "Identity.h"
#include "RouterInfo.h" #include "RouterInfo.h"
@ -63,6 +64,10 @@ namespace transport
void DeleteAllSessions (); void DeleteAllSessions ();
boost::asio::io_service& GetService () { return m_Service; }; boost::asio::io_service& GetService () { return m_Service; };
i2p::util::MemoryPool<Fragment>& GetFragmentsPool () { return m_FragmentsPool; };
i2p::util::MemoryPool<IncompleteMessage>& GetIncompleteMessagesPool () { return m_IncompleteMessagesPool; };
i2p::util::MemoryPool<SentMessage>& GetSentMessagesPool () { return m_SentMessagesPool; };
uint16_t GetPort () const { return m_Endpoint.port (); }; uint16_t GetPort () const { return m_Endpoint.port (); };
void SetLocalAddress (const boost::asio::ip::address& localAddress); void SetLocalAddress (const boost::asio::ip::address& localAddress);
@ -136,6 +141,11 @@ namespace transport
std::map<uint32_t, std::shared_ptr<SSUSession> > m_Relays; // we are introducer std::map<uint32_t, std::shared_ptr<SSUSession> > m_Relays; // we are introducer
std::map<uint32_t, PeerTest> m_PeerTests; // nonce -> creation time in milliseconds std::map<uint32_t, PeerTest> m_PeerTests; // nonce -> creation time in milliseconds
i2p::util::MemoryPool<Fragment> m_FragmentsPool;
i2p::util::MemoryPool<IncompleteMessage> m_IncompleteMessagesPool;
i2p::util::MemoryPool<SentMessage> m_SentMessagesPool;
i2p::util::MemoryPoolMt<SSUPacket> m_PacketsPool;
public: public:
// for HTTP only // for HTTP only
const decltype(m_Sessions)& GetSessions () const { return m_Sessions; }; const decltype(m_Sessions)& GetSessions () const { return m_Sessions; };

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2020, The PurpleI2P Project * Copyright (c) 2013-2021, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@ -33,7 +33,6 @@ namespace transport
SSUData::SSUData (SSUSession& session): SSUData::SSUData (SSUSession& session):
m_Session (session), m_ResendTimer (session.GetService ()), m_Session (session), m_ResendTimer (session.GetService ()),
m_IncompleteMessagesCleanupTimer (session.GetService ()),
m_MaxPacketSize (session.IsV6 () ? SSU_V6_MAX_PACKET_SIZE : SSU_V4_MAX_PACKET_SIZE), m_MaxPacketSize (session.IsV6 () ? SSU_V6_MAX_PACKET_SIZE : SSU_V4_MAX_PACKET_SIZE),
m_PacketSize (m_MaxPacketSize), m_LastMessageReceivedTime (0) m_PacketSize (m_MaxPacketSize), m_LastMessageReceivedTime (0)
{ {
@ -45,13 +44,11 @@ namespace transport
void SSUData::Start () void SSUData::Start ()
{ {
ScheduleIncompleteMessagesCleanup ();
} }
void SSUData::Stop () void SSUData::Stop ()
{ {
m_ResendTimer.cancel (); m_ResendTimer.cancel ();
m_IncompleteMessagesCleanupTimer.cancel ();
m_IncompleteMessages.clear (); m_IncompleteMessages.clear ();
m_SentMessages.clear (); m_SentMessages.clear ();
m_ReceivedMessages.clear (); m_ReceivedMessages.clear ();
@ -140,7 +137,7 @@ namespace transport
if (bitfield & mask) if (bitfield & mask)
{ {
if (fragment < numSentFragments) if (fragment < numSentFragments)
it->second->fragments[fragment].reset (nullptr); it->second->fragments[fragment] = nullptr;
} }
fragment++; fragment++;
mask <<= 1; mask <<= 1;
@ -182,9 +179,9 @@ namespace transport
auto msg = NewI2NPShortMessage (); auto msg = NewI2NPShortMessage ();
msg->len -= I2NP_SHORT_HEADER_SIZE; msg->len -= I2NP_SHORT_HEADER_SIZE;
it = m_IncompleteMessages.insert (std::make_pair (msgID, it = m_IncompleteMessages.insert (std::make_pair (msgID,
std::unique_ptr<IncompleteMessage>(new IncompleteMessage (msg)))).first; m_Session.GetServer ().GetIncompleteMessagesPool ().AcquireShared (msg))).first;
} }
std::unique_ptr<IncompleteMessage>& incompleteMessage = it->second; auto& incompleteMessage = it->second;
// mark fragment as received // mark fragment as received
if (fragmentNum < 64) if (fragmentNum < 64)
incompleteMessage->receivedFragmentsBits |= (0x01 << fragmentNum); incompleteMessage->receivedFragmentsBits |= (0x01 << fragmentNum);
@ -224,8 +221,8 @@ namespace transport
{ {
// missing fragment // missing fragment
LogPrint (eLogWarning, "SSU: Missing fragments from ", (int)incompleteMessage->nextFragmentNum, " to ", fragmentNum - 1, " of message ", msgID); LogPrint (eLogWarning, "SSU: Missing fragments from ", (int)incompleteMessage->nextFragmentNum, " to ", fragmentNum - 1, " of message ", msgID);
auto savedFragment = new Fragment (fragmentNum, buf, fragmentSize, isLast); auto savedFragment = m_Session.GetServer ().GetFragmentsPool ().AcquireShared (fragmentNum, buf, fragmentSize, isLast);
if (incompleteMessage->savedFragments.insert (std::unique_ptr<Fragment>(savedFragment)).second) if (incompleteMessage->savedFragments.insert (savedFragment).second)
incompleteMessage->lastFragmentInsertTime = i2p::util::GetSecondsSinceEpoch (); incompleteMessage->lastFragmentInsertTime = i2p::util::GetSecondsSinceEpoch ();
else else
LogPrint (eLogWarning, "SSU: Fragment ", (int)fragmentNum, " of message ", msgID, " already saved"); LogPrint (eLogWarning, "SSU: Fragment ", (int)fragmentNum, " of message ", msgID, " already saved");
@ -246,8 +243,8 @@ namespace transport
{ {
if (!m_ReceivedMessages.count (msgID)) if (!m_ReceivedMessages.count (msgID))
{ {
m_ReceivedMessages.insert (msgID);
m_LastMessageReceivedTime = i2p::util::GetSecondsSinceEpoch (); m_LastMessageReceivedTime = i2p::util::GetSecondsSinceEpoch ();
m_ReceivedMessages.emplace (msgID, m_LastMessageReceivedTime);
if (!msg->IsExpired ()) if (!msg->IsExpired ())
{ {
m_Handler.PutNextMessage (msg); m_Handler.PutNextMessage (msg);
@ -313,8 +310,8 @@ namespace transport
if (m_SentMessages.empty ()) // schedule resend at first message only if (m_SentMessages.empty ()) // schedule resend at first message only
ScheduleResend (); ScheduleResend ();
auto ret = m_SentMessages.insert (std::make_pair (msgID, std::unique_ptr<SentMessage>(new SentMessage))); auto ret = m_SentMessages.emplace (msgID, m_Session.GetServer ().GetSentMessagesPool ().AcquireShared ());
std::unique_ptr<SentMessage>& sentMessage = ret.first->second; auto& sentMessage = ret.first->second;
if (ret.second) if (ret.second)
{ {
sentMessage->nextResendTime = i2p::util::GetSecondsSinceEpoch () + RESEND_INTERVAL; sentMessage->nextResendTime = i2p::util::GetSecondsSinceEpoch () + RESEND_INTERVAL;
@ -328,7 +325,7 @@ namespace transport
uint32_t fragmentNum = 0; uint32_t fragmentNum = 0;
while (len > 0 && fragmentNum <= 127) while (len > 0 && fragmentNum <= 127)
{ {
Fragment * fragment = new Fragment; auto fragment = m_Session.GetServer ().GetFragmentsPool ().AcquireShared ();
fragment->fragmentNum = fragmentNum; fragment->fragmentNum = fragmentNum;
uint8_t * payload = fragment->buf + sizeof (SSUHeader); uint8_t * payload = fragment->buf + sizeof (SSUHeader);
*payload = DATA_FLAG_WANT_REPLY; // for compatibility *payload = DATA_FLAG_WANT_REPLY; // for compatibility
@ -358,7 +355,7 @@ namespace transport
size += padding; size += padding;
} }
fragment->len = size; fragment->len = size;
fragments.push_back (std::unique_ptr<Fragment> (fragment)); fragments.push_back (fragment);
// encrypt message with session key // encrypt message with session key
uint8_t buf[SSU_V4_MAX_PACKET_SIZE + 18]; uint8_t buf[SSU_V4_MAX_PACKET_SIZE + 18];
@ -487,20 +484,8 @@ namespace transport
} }
} }
void SSUData::ScheduleIncompleteMessagesCleanup () void SSUData::CleanUp (uint64_t ts)
{ {
m_IncompleteMessagesCleanupTimer.cancel ();
m_IncompleteMessagesCleanupTimer.expires_from_now (boost::posix_time::seconds(INCOMPLETE_MESSAGES_CLEANUP_TIMEOUT));
auto s = m_Session.shared_from_this();
m_IncompleteMessagesCleanupTimer.async_wait ([s](const boost::system::error_code& ecode)
{ s->m_Data.HandleIncompleteMessagesCleanupTimer (ecode); });
}
void SSUData::HandleIncompleteMessagesCleanupTimer (const boost::system::error_code& ecode)
{
if (ecode != boost::asio::error::operation_aborted)
{
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
for (auto it = m_IncompleteMessages.begin (); it != m_IncompleteMessages.end ();) for (auto it = m_IncompleteMessages.begin (); it != m_IncompleteMessages.end ();)
{ {
if (ts > it->second->lastFragmentInsertTime + INCOMPLETE_MESSAGES_CLEANUP_TIMEOUT) if (ts > it->second->lastFragmentInsertTime + INCOMPLETE_MESSAGES_CLEANUP_TIMEOUT)
@ -511,12 +496,20 @@ namespace transport
else else
++it; ++it;
} }
// decay
if (m_ReceivedMessages.size () > MAX_NUM_RECEIVED_MESSAGES ||
i2p::util::GetSecondsSinceEpoch () > m_LastMessageReceivedTime + DECAY_INTERVAL)
m_ReceivedMessages.clear ();
ScheduleIncompleteMessagesCleanup (); if (m_ReceivedMessages.size () > MAX_NUM_RECEIVED_MESSAGES || ts > m_LastMessageReceivedTime + DECAY_INTERVAL)
// decay
m_ReceivedMessages.clear ();
else
{
// delete old received messages
for (auto it = m_ReceivedMessages.begin (); it != m_ReceivedMessages.end ();)
{
if (ts > it->second + RECEIVED_MESSAGES_CLEANUP_TIMEOUT)
it = m_ReceivedMessages.erase (it);
else
++it;
}
} }
} }
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2020, The PurpleI2P Project * Copyright (c) 2013-2021, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@ -11,9 +11,9 @@
#include <inttypes.h> #include <inttypes.h>
#include <string.h> #include <string.h>
#include <unordered_map>
#include <vector> #include <vector>
#include <unordered_set> #include <map>
#include <unordered_map>
#include <memory> #include <memory>
#include <boost/asio.hpp> #include <boost/asio.hpp>
#include "I2NPProtocol.h" #include "I2NPProtocol.h"
@ -40,6 +40,7 @@ namespace transport
const int MAX_NUM_RESENDS = 5; const int MAX_NUM_RESENDS = 5;
const int DECAY_INTERVAL = 20; // in seconds const int DECAY_INTERVAL = 20; // in seconds
const int INCOMPLETE_MESSAGES_CLEANUP_TIMEOUT = 30; // in seconds const int INCOMPLETE_MESSAGES_CLEANUP_TIMEOUT = 30; // in seconds
const int RECEIVED_MESSAGES_CLEANUP_TIMEOUT = 40; // in seconds
const unsigned int MAX_NUM_RECEIVED_MESSAGES = 1000; // how many msgID we store for duplicates check const unsigned int MAX_NUM_RECEIVED_MESSAGES = 1000; // how many msgID we store for duplicates check
const int MAX_OUTGOING_WINDOW_SIZE = 200; // how many unacked message we can store const int MAX_OUTGOING_WINDOW_SIZE = 200; // how many unacked message we can store
// data flags // data flags
@ -64,7 +65,7 @@ namespace transport
struct FragmentCmp struct FragmentCmp
{ {
bool operator() (const std::unique_ptr<Fragment>& f1, const std::unique_ptr<Fragment>& f2) const bool operator() (const std::shared_ptr<Fragment>& f1, const std::shared_ptr<Fragment>& f2) const
{ {
return f1->fragmentNum < f2->fragmentNum; return f1->fragmentNum < f2->fragmentNum;
}; };
@ -76,7 +77,7 @@ namespace transport
int nextFragmentNum; int nextFragmentNum;
uint32_t lastFragmentInsertTime; // in seconds uint32_t lastFragmentInsertTime; // in seconds
uint64_t receivedFragmentsBits; uint64_t receivedFragmentsBits;
std::set<std::unique_ptr<Fragment>, FragmentCmp> savedFragments; std::set<std::shared_ptr<Fragment>, FragmentCmp> savedFragments;
IncompleteMessage (std::shared_ptr<I2NPMessage> m): msg (m), nextFragmentNum (0), IncompleteMessage (std::shared_ptr<I2NPMessage> m): msg (m), nextFragmentNum (0),
lastFragmentInsertTime (0), receivedFragmentsBits (0) {}; lastFragmentInsertTime (0), receivedFragmentsBits (0) {};
@ -85,7 +86,7 @@ namespace transport
struct SentMessage struct SentMessage
{ {
std::vector<std::unique_ptr<Fragment> > fragments; std::vector<std::shared_ptr<Fragment> > fragments;
uint32_t nextResendTime; // in seconds uint32_t nextResendTime; // in seconds
int numResends; int numResends;
}; };
@ -100,6 +101,7 @@ namespace transport
void Start (); void Start ();
void Stop (); void Stop ();
void CleanUp (uint64_t ts);
void ProcessMessage (uint8_t * buf, size_t len); void ProcessMessage (uint8_t * buf, size_t len);
void FlushReceivedMessage (); void FlushReceivedMessage ();
@ -119,17 +121,13 @@ namespace transport
void ScheduleResend (); void ScheduleResend ();
void HandleResendTimer (const boost::system::error_code& ecode); void HandleResendTimer (const boost::system::error_code& ecode);
void ScheduleIncompleteMessagesCleanup ();
void HandleIncompleteMessagesCleanupTimer (const boost::system::error_code& ecode);
private: private:
SSUSession& m_Session; SSUSession& m_Session;
std::unordered_map<uint32_t, std::unique_ptr<IncompleteMessage> > m_IncompleteMessages; std::map<uint32_t, std::shared_ptr<IncompleteMessage> > m_IncompleteMessages;
std::unordered_map<uint32_t, std::unique_ptr<SentMessage> > m_SentMessages; std::map<uint32_t, std::shared_ptr<SentMessage> > m_SentMessages;
std::unordered_set<uint32_t> m_ReceivedMessages; std::unordered_map<uint32_t, uint64_t> m_ReceivedMessages; // msgID -> timestamp in seconds
boost::asio::deadline_timer m_ResendTimer, m_IncompleteMessagesCleanupTimer; boost::asio::deadline_timer m_ResendTimer;
int m_MaxPacketSize, m_PacketSize; int m_MaxPacketSize, m_PacketSize;
i2p::I2NPMessagesHandler m_Handler; i2p::I2NPMessagesHandler m_Handler;
uint32_t m_LastMessageReceivedTime; // in second uint32_t m_LastMessageReceivedTime; // in second

View File

@ -730,7 +730,7 @@ namespace transport
(remoteIP.is_v6 () && i2p::context.GetStatusV6 () == eRouterStatusFirewalled)) (remoteIP.is_v6 () && i2p::context.GetStatusV6 () == eRouterStatusFirewalled))
m_Server.Send (buf, 0, remoteEndpoint); // send HolePunch m_Server.Send (buf, 0, remoteEndpoint); // send HolePunch
// we assume that HolePunch has been sent by this time and our SessionRequest will go through // we assume that HolePunch has been sent by this time and our SessionRequest will go through
m_Server.CreateDirectSession (it->second, remoteEndpoint, false); m_Server.CreateDirectSession (it->second.first, remoteEndpoint, false);
} }
// delete request // delete request
m_RelayRequests.erase (it); m_RelayRequests.erase (it);
@ -905,7 +905,8 @@ namespace transport
} }
uint32_t nonce; uint32_t nonce;
RAND_bytes ((uint8_t *)&nonce, 4); RAND_bytes ((uint8_t *)&nonce, 4);
m_RelayRequests[nonce] = to; auto ts = i2p::util::GetSecondsSinceEpoch ();
m_RelayRequests.emplace (nonce, std::make_pair (to, ts));
SendRelayRequest (introducer, nonce); SendRelayRequest (introducer, nonce);
} }
@ -1004,6 +1005,18 @@ namespace transport
} }
} }
void SSUSession::CleanUp (uint64_t ts)
{
m_Data.CleanUp (ts);
for (auto it = m_RelayRequests.begin (); it != m_RelayRequests.end ();)
{
if (ts > it->second.second + SSU_CONNECT_TIMEOUT)
it = m_RelayRequests.erase (it);
else
++it;
}
}
void SSUSession::ProcessPeerTest (const uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint) void SSUSession::ProcessPeerTest (const uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint)
{ {
uint32_t nonce = bufbe32toh (buf); // 4 bytes uint32_t nonce = bufbe32toh (buf); // 4 bytes

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2020, The PurpleI2P Project * Copyright (c) 2013-2021, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@ -89,6 +89,7 @@ namespace transport
void Done (); void Done ();
void Failed (); void Failed ();
const boost::asio::ip::udp::endpoint& GetRemoteEndpoint () { return m_RemoteEndpoint; }; const boost::asio::ip::udp::endpoint& GetRemoteEndpoint () { return m_RemoteEndpoint; };
SSUServer& GetServer () { return m_Server; };
bool IsV6 () const { return m_RemoteEndpoint.address ().is_v6 (); }; bool IsV6 () const { return m_RemoteEndpoint.address ().is_v6 (); };
void SendI2NPMessages (const std::vector<std::shared_ptr<I2NPMessage> >& msgs); void SendI2NPMessages (const std::vector<std::shared_ptr<I2NPMessage> >& msgs);
@ -105,6 +106,7 @@ namespace transport
void SetCreationTime (uint32_t ts) { m_CreationTime = ts; }; // for introducers void SetCreationTime (uint32_t ts) { m_CreationTime = ts; }; // for introducers
void FlushData (); void FlushData ();
void CleanUp (uint64_t ts);
private: private:
@ -168,7 +170,7 @@ namespace transport
SSUData m_Data; SSUData m_Data;
bool m_IsDataReceived; bool m_IsDataReceived;
std::unique_ptr<SignedData> m_SignedData; // we need it for SessionConfirmed only std::unique_ptr<SignedData> m_SignedData; // we need it for SessionConfirmed only
std::map<uint32_t, std::shared_ptr<const i2p::data::RouterInfo> > m_RelayRequests; // nonce->Charlie std::map<uint32_t, std::pair <std::shared_ptr<const i2p::data::RouterInfo>, uint64_t > > m_RelayRequests; // nonce->(Charlie, timestamp)
std::shared_ptr<i2p::crypto::DHKeys> m_DHKeysPair; // X - for client and Y - for server std::shared_ptr<i2p::crypto::DHKeys> m_DHKeysPair; // X - for client and Y - for server
}; };
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2020, The PurpleI2P Project * Copyright (c) 2013-2021, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@ -104,6 +104,7 @@ namespace stream
void Stream::Terminate (bool deleteFromDestination) // shoudl be called from StreamingDestination::Stop only void Stream::Terminate (bool deleteFromDestination) // shoudl be called from StreamingDestination::Stop only
{ {
m_Status = eStreamStatusTerminated;
m_AckSendTimer.cancel (); m_AckSendTimer.cancel ();
m_ReceiveTimer.cancel (); m_ReceiveTimer.cancel ();
m_ResendTimer.cancel (); m_ResendTimer.cancel ();
@ -276,7 +277,20 @@ namespace stream
const uint8_t * optionData = packet->GetOptionData (); const uint8_t * optionData = packet->GetOptionData ();
size_t optionSize = packet->GetOptionSize (); size_t optionSize = packet->GetOptionSize ();
if (flags & PACKET_FLAG_DELAY_REQUESTED) if (flags & PACKET_FLAG_DELAY_REQUESTED)
{
if (!m_IsAckSendScheduled)
{
uint16_t delayRequested = bufbe16toh (optionData);
if (delayRequested > 0 && delayRequested < m_RTT)
{
m_IsAckSendScheduled = true;
m_AckSendTimer.expires_from_now (boost::posix_time::milliseconds(delayRequested));
m_AckSendTimer.async_wait (std::bind (&Stream::HandleAckSendTimer,
shared_from_this (), std::placeholders::_1));
}
}
optionData += 2; optionData += 2;
}
if (flags & PACKET_FLAG_FROM_INCLUDED) if (flags & PACKET_FLAG_FROM_INCLUDED)
{ {
@ -651,6 +665,42 @@ namespace stream
LogPrint (eLogDebug, "Streaming: Quick Ack sent. ", (int)numNacks, " NACKs"); LogPrint (eLogDebug, "Streaming: Quick Ack sent. ", (int)numNacks, " NACKs");
} }
void Stream::SendPing ()
{
Packet p;
uint8_t * packet = p.GetBuffer ();
size_t size = 0;
htobe32buf (packet, m_RecvStreamID);
size += 4; // sendStreamID
memset (packet + size, 0, 14);
size += 14; // all zeroes
uint16_t flags = PACKET_FLAG_ECHO | PACKET_FLAG_SIGNATURE_INCLUDED | PACKET_FLAG_FROM_INCLUDED;
bool isOfflineSignature = m_LocalDestination.GetOwner ()->GetPrivateKeys ().IsOfflineSignature ();
if (isOfflineSignature) flags |= PACKET_FLAG_OFFLINE_SIGNATURE;
htobe16buf (packet + size, flags);
size += 2; // flags
size_t identityLen = m_LocalDestination.GetOwner ()->GetIdentity ()->GetFullLen ();
size_t signatureLen = m_LocalDestination.GetOwner ()->GetPrivateKeys ().GetSignatureLen ();
uint8_t * optionsSize = packet + size; // set options size later
size += 2; // options size
m_LocalDestination.GetOwner ()->GetIdentity ()->ToBuffer (packet + size, identityLen);
size += identityLen; // from
if (isOfflineSignature)
{
const auto& offlineSignature = m_LocalDestination.GetOwner ()->GetPrivateKeys ().GetOfflineSignature ();
memcpy (packet + size, offlineSignature.data (), offlineSignature.size ());
size += offlineSignature.size (); // offline signature
}
uint8_t * signature = packet + size; // set it later
memset (signature, 0, signatureLen); // zeroes for now
size += signatureLen; // signature
htobe16buf (optionsSize, packet + size - 2 - optionsSize); // actual options size
m_LocalDestination.GetOwner ()->Sign (packet, size, signature);
p.len = size;
SendPackets (std::vector<Packet *> { &p });
LogPrint (eLogDebug, "Streaming: Ping of ", p.len, " bytes sent");
}
void Stream::Close () void Stream::Close ()
{ {
LogPrint(eLogDebug, "Streaming: closing stream with sSID=", m_SendStreamID, ", rSID=", m_RecvStreamID, ", status=", m_Status); LogPrint(eLogDebug, "Streaming: closing stream with sSID=", m_SendStreamID, ", rSID=", m_RecvStreamID, ", status=", m_Status);
@ -793,7 +843,7 @@ namespace stream
if (m_CurrentRemoteLease && ts < m_CurrentRemoteLease->endDate + i2p::data::LEASE_ENDDATE_THRESHOLD) if (m_CurrentRemoteLease && ts < m_CurrentRemoteLease->endDate + i2p::data::LEASE_ENDDATE_THRESHOLD)
{ {
std::vector<i2p::tunnel::TunnelMessageBlock> msgs; std::vector<i2p::tunnel::TunnelMessageBlock> msgs;
for (auto it: packets) for (const auto& it: packets)
{ {
auto msg = m_RoutingSession->WrapSingleMessage (m_LocalDestination.CreateDataMessage ( auto msg = m_RoutingSession->WrapSingleMessage (m_LocalDestination.CreateDataMessage (
it->GetBuffer (), it->GetLength (), m_Port, !m_RoutingSession->IsRatchets ())); it->GetBuffer (), it->GetLength (), m_Port, !m_RoutingSession->IsRatchets ()));
@ -843,6 +893,8 @@ namespace stream
} }
void Stream::ScheduleResend () void Stream::ScheduleResend ()
{
if (m_Status != eStreamStatusTerminated)
{ {
m_ResendTimer.cancel (); m_ResendTimer.cancel ();
// check for invalid value // check for invalid value
@ -851,6 +903,7 @@ namespace stream
m_ResendTimer.async_wait (std::bind (&Stream::HandleResendTimer, m_ResendTimer.async_wait (std::bind (&Stream::HandleResendTimer,
shared_from_this (), std::placeholders::_1)); shared_from_this (), std::placeholders::_1));
} }
}
void Stream::HandleResendTimer (const boost::system::error_code& ecode) void Stream::HandleResendTimer (const boost::system::error_code& ecode)
{ {
@ -1023,6 +1076,8 @@ namespace stream
m_Owner (owner), m_LocalPort (localPort), m_Gzip (gzip), m_Owner (owner), m_LocalPort (localPort), m_Gzip (gzip),
m_PendingIncomingTimer (m_Owner->GetService ()) m_PendingIncomingTimer (m_Owner->GetService ())
{ {
if (m_Gzip)
m_Deflator.reset (new i2p::data::GzipDeflator);
} }
StreamingDestination::~StreamingDestination () StreamingDestination::~StreamingDestination ()
@ -1050,6 +1105,7 @@ namespace stream
it.second->Terminate (false); // we delete here it.second->Terminate (false); // we delete here
m_Streams.clear (); m_Streams.clear ();
m_IncomingStreams.clear (); m_IncomingStreams.clear ();
m_LastStream = nullptr;
} }
} }
@ -1057,10 +1113,17 @@ namespace stream
{ {
uint32_t sendStreamID = packet->GetSendStreamID (); uint32_t sendStreamID = packet->GetSendStreamID ();
if (sendStreamID) if (sendStreamID)
{
if (!m_LastStream || sendStreamID != m_LastStream->GetRecvStreamID ())
{ {
auto it = m_Streams.find (sendStreamID); auto it = m_Streams.find (sendStreamID);
if (it != m_Streams.end ()) if (it != m_Streams.end ())
it->second->HandleNextPacket (packet); m_LastStream = it->second;
else
m_LastStream = nullptr;
}
if (m_LastStream)
m_LastStream->HandleNextPacket (packet);
else if (packet->IsEcho () && m_Owner->IsStreamingAnswerPings ()) else if (packet->IsEcho () && m_Owner->IsStreamingAnswerPings ())
{ {
// ping // ping
@ -1076,6 +1139,13 @@ namespace stream
} }
else else
{ {
if (packet->IsEcho ())
{
// pong
LogPrint (eLogInfo, "Streaming: Pong received rSID=", packet->GetReceiveStreamID ());
DeletePacket (packet);
return;
}
if (packet->IsSYN () && !packet->GetSeqn ()) // new incoming stream if (packet->IsSYN () && !packet->GetSeqn ()) // new incoming stream
{ {
uint32_t receiveStreamID = packet->GetReceiveStreamID (); uint32_t receiveStreamID = packet->GetReceiveStreamID ();
@ -1166,16 +1236,22 @@ namespace stream
{ {
auto s = std::make_shared<Stream> (m_Owner->GetService (), *this, remote, port); auto s = std::make_shared<Stream> (m_Owner->GetService (), *this, remote, port);
std::unique_lock<std::mutex> l(m_StreamsMutex); std::unique_lock<std::mutex> l(m_StreamsMutex);
m_Streams[s->GetRecvStreamID ()] = s; m_Streams.emplace (s->GetRecvStreamID (), s);
return s; return s;
} }
void StreamingDestination::SendPing (std::shared_ptr<const i2p::data::LeaseSet> remote)
{
auto s = std::make_shared<Stream> (m_Owner->GetService (), *this, remote, 0);
s->SendPing ();
}
std::shared_ptr<Stream> StreamingDestination::CreateNewIncomingStream (uint32_t receiveStreamID) std::shared_ptr<Stream> StreamingDestination::CreateNewIncomingStream (uint32_t receiveStreamID)
{ {
auto s = std::make_shared<Stream> (m_Owner->GetService (), *this); auto s = std::make_shared<Stream> (m_Owner->GetService (), *this);
std::unique_lock<std::mutex> l(m_StreamsMutex); std::unique_lock<std::mutex> l(m_StreamsMutex);
m_Streams[s->GetRecvStreamID ()] = s; m_Streams.emplace (s->GetRecvStreamID (), s);
m_IncomingStreams[receiveStreamID] = s; m_IncomingStreams.emplace (receiveStreamID, s);
return s; return s;
} }
@ -1186,6 +1262,7 @@ namespace stream
std::unique_lock<std::mutex> l(m_StreamsMutex); std::unique_lock<std::mutex> l(m_StreamsMutex);
m_Streams.erase (stream->GetRecvStreamID ()); m_Streams.erase (stream->GetRecvStreamID ());
m_IncomingStreams.erase (stream->GetSendStreamID ()); m_IncomingStreams.erase (stream->GetSendStreamID ());
if (m_LastStream == stream) m_LastStream = nullptr;
} }
} }
@ -1270,13 +1347,17 @@ namespace stream
std::shared_ptr<I2NPMessage> StreamingDestination::CreateDataMessage ( std::shared_ptr<I2NPMessage> StreamingDestination::CreateDataMessage (
const uint8_t * payload, size_t len, uint16_t toPort, bool checksum) const uint8_t * payload, size_t len, uint16_t toPort, bool checksum)
{ {
size_t size;
auto msg = m_I2NPMsgsPool.AcquireShared (); auto msg = m_I2NPMsgsPool.AcquireShared ();
uint8_t * buf = msg->GetPayload (); uint8_t * buf = msg->GetPayload ();
buf += 4; // reserve for lengthlength buf += 4; // reserve for lengthlength
msg->len += 4; msg->len += 4;
size_t size = (!m_Gzip || len <= i2p::stream::COMPRESSION_THRESHOLD_SIZE)?
i2p::data::GzipNoCompression (payload, len, buf, msg->maxLen - msg->len): if (m_Gzip && m_Deflator)
m_Deflator.Deflate (payload, len, buf, msg->maxLen - msg->len); size = m_Deflator->Deflate (payload, len, buf, msg->maxLen - msg->len);
else
size = i2p::data::GzipNoCompression (payload, len, buf, msg->maxLen - msg->len);
if (size) if (size)
{ {
htobe32buf (msg->GetPayload (), size); // length htobe32buf (msg->GetPayload (), size); // length

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2020, The PurpleI2P Project * Copyright (c) 2013-2021, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@ -11,7 +11,7 @@
#include <inttypes.h> #include <inttypes.h>
#include <string> #include <string>
#include <map> #include <unordered_map>
#include <set> #include <set>
#include <queue> #include <queue>
#include <functional> #include <functional>
@ -152,7 +152,8 @@ namespace stream
eStreamStatusOpen, eStreamStatusOpen,
eStreamStatusReset, eStreamStatusReset,
eStreamStatusClosing, eStreamStatusClosing,
eStreamStatusClosed eStreamStatusClosed,
eStreamStatusTerminated
}; };
class StreamingDestination; class StreamingDestination;
@ -178,6 +179,7 @@ namespace stream
void HandlePing (Packet * packet); void HandlePing (Packet * packet);
size_t Send (const uint8_t * buf, size_t len); size_t Send (const uint8_t * buf, size_t len);
void AsyncSend (const uint8_t * buf, size_t len, SendHandler handler); void AsyncSend (const uint8_t * buf, size_t len, SendHandler handler);
void SendPing ();
template<typename Buffer, typename ReceiveHandler> template<typename Buffer, typename ReceiveHandler>
void AsyncReceive (const Buffer& buffer, ReceiveHandler handler, int timeout = 0); void AsyncReceive (const Buffer& buffer, ReceiveHandler handler, int timeout = 0);
@ -260,13 +262,14 @@ namespace stream
typedef std::function<void (std::shared_ptr<Stream>)> Acceptor; typedef std::function<void (std::shared_ptr<Stream>)> Acceptor;
StreamingDestination (std::shared_ptr<i2p::client::ClientDestination> owner, uint16_t localPort = 0, bool gzip = true); StreamingDestination (std::shared_ptr<i2p::client::ClientDestination> owner, uint16_t localPort = 0, bool gzip = false);
~StreamingDestination (); ~StreamingDestination ();
void Start (); void Start ();
void Stop (); void Stop ();
std::shared_ptr<Stream> CreateNewOutgoingStream (std::shared_ptr<const i2p::data::LeaseSet> remote, int port = 0); std::shared_ptr<Stream> CreateNewOutgoingStream (std::shared_ptr<const i2p::data::LeaseSet> remote, int port = 0);
void SendPing (std::shared_ptr<const i2p::data::LeaseSet> remote);
void DeleteStream (std::shared_ptr<Stream> stream); void DeleteStream (std::shared_ptr<Stream> stream);
bool DeleteStream (uint32_t recvStreamID); bool DeleteStream (uint32_t recvStreamID);
void SetAcceptor (const Acceptor& acceptor); void SetAcceptor (const Acceptor& acceptor);
@ -297,12 +300,13 @@ namespace stream
uint16_t m_LocalPort; uint16_t m_LocalPort;
bool m_Gzip; // gzip compression of data messages bool m_Gzip; // gzip compression of data messages
std::mutex m_StreamsMutex; std::mutex m_StreamsMutex;
std::map<uint32_t, std::shared_ptr<Stream> > m_Streams; // sendStreamID->stream std::unordered_map<uint32_t, std::shared_ptr<Stream> > m_Streams; // sendStreamID->stream
std::map<uint32_t, std::shared_ptr<Stream> > m_IncomingStreams; // receiveStreamID->stream std::unordered_map<uint32_t, std::shared_ptr<Stream> > m_IncomingStreams; // receiveStreamID->stream
std::shared_ptr<Stream> m_LastStream;
Acceptor m_Acceptor; Acceptor m_Acceptor;
std::list<std::shared_ptr<Stream> > m_PendingIncomingStreams; std::list<std::shared_ptr<Stream> > m_PendingIncomingStreams;
boost::asio::deadline_timer m_PendingIncomingTimer; boost::asio::deadline_timer m_PendingIncomingTimer;
std::map<uint32_t, std::list<Packet *> > m_SavedPackets; // receiveStreamID->packets, arrived before SYN std::unordered_map<uint32_t, std::list<Packet *> > m_SavedPackets; // receiveStreamID->packets, arrived before SYN
i2p::util::MemoryPool<Packet> m_PacketsPool; i2p::util::MemoryPool<Packet> m_PacketsPool;
i2p::util::MemoryPool<I2NPMessageBuffer<I2NP_MAX_MESSAGE_SIZE> > m_I2NPMsgsPool; i2p::util::MemoryPool<I2NPMessageBuffer<I2NP_MAX_MESSAGE_SIZE> > m_I2NPMsgsPool;
@ -310,7 +314,7 @@ namespace stream
public: public:
i2p::data::GzipInflator m_Inflator; i2p::data::GzipInflator m_Inflator;
i2p::data::GzipDeflator m_Deflator; std::unique_ptr<i2p::data::GzipDeflator> m_Deflator;
// for HTTP only // for HTTP only
const decltype(m_Streams)& GetStreams () const { return m_Streams; }; const decltype(m_Streams)& GetStreams () const { return m_Streams; };

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2020, The PurpleI2P Project * Copyright (c) 2013-2021, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@ -16,6 +16,7 @@
#include <boost/algorithm/string.hpp> #include <boost/algorithm/string.hpp>
#include "Config.h" #include "Config.h"
#include "Log.h" #include "Log.h"
#include "RouterContext.h"
#include "I2PEndian.h" #include "I2PEndian.h"
#include "Timestamp.h" #include "Timestamp.h"
#include "util.h" #include "util.h"
@ -60,14 +61,43 @@ namespace util
{ {
LogPrint (eLogInfo, "Timestamp: NTP request to ", address); LogPrint (eLogInfo, "Timestamp: NTP request to ", address);
boost::asio::io_service service; boost::asio::io_service service;
boost::asio::ip::udp::resolver::query query (boost::asio::ip::udp::v4 (), address, "ntp");
boost::system::error_code ec; boost::system::error_code ec;
auto it = boost::asio::ip::udp::resolver (service).resolve (query, ec); auto it = boost::asio::ip::udp::resolver (service).resolve (
if (!ec && it != boost::asio::ip::udp::resolver::iterator()) boost::asio::ip::udp::resolver::query (address, "ntp"), ec);
if (!ec)
{ {
auto ep = (*it).endpoint (); // take first one bool found = false;
boost::asio::ip::udp::resolver::iterator end;
boost::asio::ip::udp::endpoint ep;
while (it != end)
{
ep = *it;
if (!ep.address ().is_unspecified ())
{
if (ep.address ().is_v4 ())
{
if (i2p::context.SupportsV4 ()) found = true;
}
else if (ep.address ().is_v6 ())
{
if (i2p::util::net::IsYggdrasilAddress (ep.address ()))
{
if (i2p::context.SupportsMesh ()) found = true;
}
else if (i2p::context.SupportsV6 ()) found = true;
}
}
if (found) break;
it++;
}
if (!found)
{
LogPrint (eLogError, "Timestamp: can't find compatible address for ", address);
return;
}
boost::asio::ip::udp::socket socket (service); boost::asio::ip::udp::socket socket (service);
socket.open (boost::asio::ip::udp::v4 (), ec); socket.open (ep.protocol (), ec);
if (!ec) if (!ec)
{ {
uint8_t buf[48];// 48 bytes NTP request/response uint8_t buf[48];// 48 bytes NTP request/response
@ -103,7 +133,7 @@ namespace util
LogPrint (eLogError, "Timestamp: Couldn't open UDP socket"); LogPrint (eLogError, "Timestamp: Couldn't open UDP socket");
} }
else else
LogPrint (eLogError, "Timestamp: Couldn't resove address ", address); LogPrint (eLogError, "Timestamp: Couldn't resolve address ", address);
} }
NTPTimeSync::NTPTimeSync (): m_IsRunning (false), m_Timer (m_Service) NTPTimeSync::NTPTimeSync (): m_IsRunning (false), m_Timer (m_Service)

View File

@ -39,7 +39,7 @@ namespace tunnel
void TransitTunnelParticipant::HandleTunnelDataMsg (std::shared_ptr<const i2p::I2NPMessage> tunnelMsg) void TransitTunnelParticipant::HandleTunnelDataMsg (std::shared_ptr<const i2p::I2NPMessage> tunnelMsg)
{ {
auto newMsg = CreateEmptyTunnelDataMsg (); auto newMsg = CreateEmptyTunnelDataMsg (false);
EncryptTunnelMsg (tunnelMsg, newMsg); EncryptTunnelMsg (tunnelMsg, newMsg);
m_NumTransmittedBytes += tunnelMsg->GetLength (); m_NumTransmittedBytes += tunnelMsg->GetLength ();
@ -87,7 +87,7 @@ namespace tunnel
void TransitTunnelEndpoint::HandleTunnelDataMsg (std::shared_ptr<const i2p::I2NPMessage> tunnelMsg) void TransitTunnelEndpoint::HandleTunnelDataMsg (std::shared_ptr<const i2p::I2NPMessage> tunnelMsg)
{ {
auto newMsg = CreateEmptyTunnelDataMsg (); auto newMsg = CreateEmptyTunnelDataMsg (true);
EncryptTunnelMsg (tunnelMsg, newMsg); EncryptTunnelMsg (tunnelMsg, newMsg);
LogPrint (eLogDebug, "TransitTunnel: handle msg for endpoint ", GetTunnelID ()); LogPrint (eLogDebug, "TransitTunnel: handle msg for endpoint ", GetTunnelID ());

View File

@ -401,7 +401,7 @@ namespace transport
try try
{ {
auto r = netdb.FindRouter (ident); auto r = netdb.FindRouter (ident);
if (!r || r->IsUnreachable () || !r->IsCompatible (i2p::context.GetRouterInfo ())) return; if (r && (r->IsUnreachable () || !r->IsReachableFrom (i2p::context.GetRouterInfo ()))) return; // router found but non-reachable
{ {
std::unique_lock<std::mutex> l(m_PeersMutex); std::unique_lock<std::mutex> l(m_PeersMutex);
it = m_Peers.insert (std::pair<i2p::data::IdentHash, Peer>(ident, { 0, r, {}, it = m_Peers.insert (std::pair<i2p::data::IdentHash, Peer>(ident, { 0, r, {},
@ -447,7 +447,7 @@ namespace transport
std::shared_ptr<const RouterInfo::Address> address; std::shared_ptr<const RouterInfo::Address> address;
if (!peer.numAttempts) // NTCP2 ipv6 if (!peer.numAttempts) // NTCP2 ipv6
{ {
if (context.GetRouterInfo ().IsNTCP2V6 () && peer.router->IsNTCP2V6 ()) if (context.GetRouterInfo ().IsNTCP2V6 () && peer.router->IsReachableBy (RouterInfo::eNTCP2V6))
{ {
address = peer.router->GetPublishedNTCP2V6Address (); address = peer.router->GetPublishedNTCP2V6Address ();
if (address && m_CheckReserved && i2p::util::net::IsInReservedRange(address->host)) if (address && m_CheckReserved && i2p::util::net::IsInReservedRange(address->host))
@ -457,7 +457,7 @@ namespace transport
} }
if (!address && peer.numAttempts == 1) // NTCP2 ipv4 if (!address && peer.numAttempts == 1) // NTCP2 ipv4
{ {
if (context.GetRouterInfo ().IsNTCP2 (true) && peer.router->IsNTCP2 (true) && !peer.router->IsUnreachable ()) if (context.GetRouterInfo ().IsNTCP2 (true) && peer.router->IsReachableBy (RouterInfo::eNTCP2V4))
{ {
address = peer.router->GetPublishedNTCP2V4Address (); address = peer.router->GetPublishedNTCP2V4Address ();
if (address && m_CheckReserved && i2p::util::net::IsInReservedRange(address->host)) if (address && m_CheckReserved && i2p::util::net::IsInReservedRange(address->host))
@ -485,7 +485,7 @@ namespace transport
std::shared_ptr<const RouterInfo::Address> address; std::shared_ptr<const RouterInfo::Address> address;
if (peer.numAttempts == 2) // SSU ipv6 if (peer.numAttempts == 2) // SSU ipv6
{ {
if (context.GetRouterInfo ().IsSSUV6 () && peer.router->IsSSUV6 ()) if (context.GetRouterInfo ().IsSSUV6 () && peer.router->IsReachableBy (RouterInfo::eSSUV6))
{ {
address = peer.router->GetSSUV6Address (); address = peer.router->GetSSUV6Address ();
if (address && m_CheckReserved && i2p::util::net::IsInReservedRange(address->host)) if (address && m_CheckReserved && i2p::util::net::IsInReservedRange(address->host))
@ -495,7 +495,7 @@ namespace transport
} }
if (!address && peer.numAttempts == 3) // SSU ipv4 if (!address && peer.numAttempts == 3) // SSU ipv4
{ {
if (context.GetRouterInfo ().IsSSU (true) && peer.router->IsSSU (true)) if (context.GetRouterInfo ().IsSSU (true) && peer.router->IsReachableBy (RouterInfo::eSSUV4))
{ {
address = peer.router->GetSSUAddress (true); address = peer.router->GetSSUAddress (true);
if (address && m_CheckReserved && i2p::util::net::IsInReservedRange(address->host)) if (address && m_CheckReserved && i2p::util::net::IsInReservedRange(address->host))

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2020, The PurpleI2P Project * Copyright (c) 2013-2021, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@ -23,6 +23,7 @@
#include "Tunnel.h" #include "Tunnel.h"
#include "TunnelPool.h" #include "TunnelPool.h"
#include "util.h" #include "util.h"
#include "ECIESX25519AEADRatchetSession.h"
namespace i2p namespace i2p
{ {
@ -30,8 +31,9 @@ namespace tunnel
{ {
Tunnel::Tunnel (std::shared_ptr<const TunnelConfig> config): Tunnel::Tunnel (std::shared_ptr<const TunnelConfig> config):
TunnelBase (config->GetTunnelID (), config->GetNextTunnelID (), config->GetNextIdentHash ()), TunnelBase (config->GetTunnelID (), config->GetNextTunnelID (), config->GetNextIdentHash ()),
m_Config (config), m_Pool (nullptr), m_State (eTunnelStatePending), m_IsRecreated (false), m_Config (config), m_IsShortBuildMessage (false), m_Pool (nullptr),
m_Latency (0) m_State (eTunnelStatePending), m_FarEndTransports (0),
m_IsRecreated (false), m_Latency (0)
{ {
} }
@ -42,10 +44,11 @@ namespace tunnel
void Tunnel::Build (uint32_t replyMsgID, std::shared_ptr<OutboundTunnel> outboundTunnel) void Tunnel::Build (uint32_t replyMsgID, std::shared_ptr<OutboundTunnel> outboundTunnel)
{ {
auto numHops = m_Config->GetNumHops (); auto numHops = m_Config->GetNumHops ();
int numRecords = numHops <= STANDARD_NUM_RECORDS ? STANDARD_NUM_RECORDS : MAX_NUM_RECORDS; const int numRecords = numHops <= STANDARD_NUM_RECORDS ? STANDARD_NUM_RECORDS : MAX_NUM_RECORDS;
auto msg = numRecords <= STANDARD_NUM_RECORDS ? NewI2NPShortMessage () : NewI2NPMessage (); auto msg = numRecords <= STANDARD_NUM_RECORDS ? NewI2NPShortMessage () : NewI2NPMessage ();
*msg->GetPayload () = numRecords; *msg->GetPayload () = numRecords;
msg->len += numRecords*TUNNEL_BUILD_RECORD_SIZE + 1; const size_t recordSize = m_Config->IsShort () ? SHORT_TUNNEL_BUILD_RECORD_SIZE : TUNNEL_BUILD_RECORD_SIZE;
msg->len += numRecords*recordSize + 1;
// shuffle records // shuffle records
std::vector<int> recordIndicies; std::vector<int> recordIndicies;
for (int i = 0; i < numRecords; i++) recordIndicies.push_back(i); for (int i = 0; i < numRecords; i++) recordIndicies.push_back(i);
@ -55,7 +58,6 @@ namespace tunnel
uint8_t * records = msg->GetPayload () + 1; uint8_t * records = msg->GetPayload () + 1;
TunnelHopConfig * hop = m_Config->GetFirstHop (); TunnelHopConfig * hop = m_Config->GetFirstHop ();
int i = 0; int i = 0;
BN_CTX * ctx = BN_CTX_new ();
while (hop) while (hop)
{ {
uint32_t msgID; uint32_t msgID;
@ -63,80 +65,89 @@ namespace tunnel
RAND_bytes ((uint8_t *)&msgID, 4); RAND_bytes ((uint8_t *)&msgID, 4);
else else
msgID = replyMsgID; msgID = replyMsgID;
int idx = recordIndicies[i]; hop->recordIndex = recordIndicies[i]; i++;
hop->CreateBuildRequestRecord (records + idx*TUNNEL_BUILD_RECORD_SIZE, msgID, ctx); hop->CreateBuildRequestRecord (records, msgID);
hop->recordIndex = idx;
i++;
hop = hop->next; hop = hop->next;
} }
BN_CTX_free (ctx);
// fill up fake records with random data // fill up fake records with random data
for (int i = numHops; i < numRecords; i++) for (int i = numHops; i < numRecords; i++)
{ {
int idx = recordIndicies[i]; int idx = recordIndicies[i];
RAND_bytes (records + idx*TUNNEL_BUILD_RECORD_SIZE, TUNNEL_BUILD_RECORD_SIZE); RAND_bytes (records + idx*recordSize, recordSize);
} }
// decrypt real records // decrypt real records
i2p::crypto::CBCDecryption decryption;
hop = m_Config->GetLastHop ()->prev; hop = m_Config->GetLastHop ()->prev;
while (hop) while (hop)
{ {
decryption.SetKey (hop->replyKey);
// decrypt records after current hop // decrypt records after current hop
TunnelHopConfig * hop1 = hop->next; TunnelHopConfig * hop1 = hop->next;
while (hop1) while (hop1)
{ {
decryption.SetIV (hop->replyIV); hop->DecryptRecord (records, hop1->recordIndex);
uint8_t * record = records + hop1->recordIndex*TUNNEL_BUILD_RECORD_SIZE;
decryption.Decrypt(record, TUNNEL_BUILD_RECORD_SIZE, record);
hop1 = hop1->next; hop1 = hop1->next;
} }
hop = hop->prev; hop = hop->prev;
} }
msg->FillI2NPMessageHeader (eI2NPVariableTunnelBuild); msg->FillI2NPMessageHeader (m_Config->IsShort () ? eI2NPShortTunnelBuild : eI2NPVariableTunnelBuild);
// send message // send message
if (outboundTunnel) if (outboundTunnel)
{
if (m_Config->IsShort ())
{
auto ident = m_Config->GetFirstHop () ? m_Config->GetFirstHop ()->ident : nullptr;
if (ident && ident->GetIdentHash () != outboundTunnel->GetNextIdentHash ()) // don't encrypt if IBGW = OBEP
{
auto msg1 = i2p::garlic::WrapECIESX25519MessageForRouter (msg, ident->GetEncryptionPublicKey ());
if (msg1) msg = msg1;
}
}
outboundTunnel->SendTunnelDataMsg (GetNextIdentHash (), 0, msg); outboundTunnel->SendTunnelDataMsg (GetNextIdentHash (), 0, msg);
}
else else
{
if (m_Config->IsShort () && m_Config->GetLastHop () &&
m_Config->GetLastHop ()->ident->GetIdentHash () != m_Config->GetLastHop ()->nextIdent)
{
// add garlic key/tag for reply
uint8_t key[32];
uint64_t tag = m_Config->GetLastHop ()->GetGarlicKey (key);
if (m_Pool && m_Pool->GetLocalDestination ())
m_Pool->GetLocalDestination ()->AddECIESx25519Key (key, tag);
else
i2p::context.AddECIESx25519Key (key, tag);
}
i2p::transport::transports.SendMessage (GetNextIdentHash (), msg); i2p::transport::transports.SendMessage (GetNextIdentHash (), msg);
} }
}
bool Tunnel::HandleTunnelBuildResponse (uint8_t * msg, size_t len) bool Tunnel::HandleTunnelBuildResponse (uint8_t * msg, size_t len)
{ {
LogPrint (eLogDebug, "Tunnel: TunnelBuildResponse ", (int)msg[0], " records."); LogPrint (eLogDebug, "Tunnel: TunnelBuildResponse ", (int)msg[0], " records.");
i2p::crypto::CBCDecryption decryption;
TunnelHopConfig * hop = m_Config->GetLastHop (); TunnelHopConfig * hop = m_Config->GetLastHop ();
while (hop) while (hop)
{ {
decryption.SetKey (hop->replyKey); // decrypt current hop
// decrypt records before and current hop if (hop->recordIndex >= 0 && hop->recordIndex < msg[0])
TunnelHopConfig * hop1 = hop; {
if (!hop->DecryptBuildResponseRecord (msg + 1))
return false;
}
else
{
LogPrint (eLogWarning, "Tunnel: hop index ", hop->recordIndex, " is out of range");
return false;
}
// decrypt records before current hop
TunnelHopConfig * hop1 = hop->prev;
while (hop1) while (hop1)
{ {
auto idx = hop1->recordIndex; auto idx = hop1->recordIndex;
if (idx >= 0 && idx < msg[0]) if (idx >= 0 && idx < msg[0])
{ hop->DecryptRecord (msg + 1, idx);
uint8_t * record = msg + 1 + idx*TUNNEL_BUILD_RECORD_SIZE;
if (hop1 == hop && hop1->IsECIES ())
{
uint8_t nonce[12];
memset (nonce, 0, 12);
if (!i2p::crypto::AEADChaCha20Poly1305 (record, TUNNEL_BUILD_RECORD_SIZE - 16,
hop->m_H, 32, hop->m_CK, nonce, record, TUNNEL_BUILD_RECORD_SIZE - 16, false)) // decrypt
{
LogPrint (eLogWarning, "Tunnel: Response AEAD decryption failed");
return false;
}
}
else
{
decryption.SetIV (hop->replyIV);
decryption.Decrypt(record, TUNNEL_BUILD_RECORD_SIZE, record);
}
}
else else
LogPrint (eLogWarning, "Tunnel: hop index ", idx, " is out of range"); LogPrint (eLogWarning, "Tunnel: hop index ", idx, " is out of range");
hop1 = hop1->prev; hop1 = hop1->prev;
@ -148,8 +159,7 @@ namespace tunnel
hop = m_Config->GetFirstHop (); hop = m_Config->GetFirstHop ();
while (hop) while (hop)
{ {
const uint8_t * record = msg + 1 + hop->recordIndex*TUNNEL_BUILD_RECORD_SIZE; uint8_t ret = hop->GetRetCode (msg + 1);
uint8_t ret = record[hop->IsECIES () ? ECIES_BUILD_RESPONSE_RECORD_RET_OFFSET : BUILD_RESPONSE_RECORD_RET_OFFSET];
LogPrint (eLogDebug, "Tunnel: Build response ret code=", (int)ret); LogPrint (eLogDebug, "Tunnel: Build response ret code=", (int)ret);
auto profile = i2p::data::netdb.FindRouterProfile (hop->ident->GetIdentHash ()); auto profile = i2p::data::netdb.FindRouterProfile (hop->ident->GetIdentHash ());
if (profile) if (profile)
@ -171,6 +181,8 @@ namespace tunnel
m_Hops.push_back (std::unique_ptr<TunnelHop>(tunnelHop)); m_Hops.push_back (std::unique_ptr<TunnelHop>(tunnelHop));
hop = hop->prev; hop = hop->prev;
} }
m_IsShortBuildMessage = m_Config->IsShort ();
m_FarEndTransports = m_Config->GetFarEndTransports ();
m_Config = nullptr; m_Config = nullptr;
} }
if (established) m_State = eTunnelStateEstablished; if (established) m_State = eTunnelStateEstablished;
@ -234,7 +246,7 @@ namespace tunnel
void InboundTunnel::HandleTunnelDataMsg (std::shared_ptr<const I2NPMessage> msg) void InboundTunnel::HandleTunnelDataMsg (std::shared_ptr<const I2NPMessage> msg)
{ {
if (IsFailed ()) SetState (eTunnelStateEstablished); // incoming messages means a tunnel is alive if (IsFailed ()) SetState (eTunnelStateEstablished); // incoming messages means a tunnel is alive
auto newMsg = CreateEmptyTunnelDataMsg (); auto newMsg = CreateEmptyTunnelDataMsg (true);
EncryptTunnelMsg (msg, newMsg); EncryptTunnelMsg (msg, newMsg);
newMsg->from = shared_from_this (); newMsg->from = shared_from_this ();
m_Endpoint.HandleDecryptedTunnelDataMsg (newMsg); m_Endpoint.HandleDecryptedTunnelDataMsg (newMsg);
@ -319,10 +331,11 @@ namespace tunnel
for (auto& msg : msgs) for (auto& msg : msgs)
{ {
if (!msg.data) continue; if (!msg.data) continue;
m_NumSentBytes += msg.data->GetLength ();
switch (msg.deliveryType) switch (msg.deliveryType)
{ {
case eDeliveryTypeLocal: case eDeliveryTypeLocal:
i2p::HandleI2NPMessage (msg.data); HandleI2NPMessage (msg.data);
break; break;
case eDeliveryTypeTunnel: case eDeliveryTypeTunnel:
i2p::transport::transports.SendMessage (msg.hash, i2p::CreateTunnelGatewayMsg (msg.tunnelID, msg.data)); i2p::transport::transports.SendMessage (msg.hash, i2p::CreateTunnelGatewayMsg (msg.tunnelID, msg.data));
@ -517,6 +530,8 @@ namespace tunnel
} }
case eI2NPVariableTunnelBuild: case eI2NPVariableTunnelBuild:
case eI2NPVariableTunnelBuildReply: case eI2NPVariableTunnelBuildReply:
case eI2NPShortTunnelBuild:
case eI2NPShortTunnelBuildReply:
case eI2NPTunnelBuild: case eI2NPTunnelBuild:
case eI2NPTunnelBuildReply: case eI2NPTunnelBuildReply:
HandleI2NPMessage (msg->GetBuffer (), msg->GetLength ()); HandleI2NPMessage (msg->GetBuffer (), msg->GetLength ());
@ -680,7 +695,7 @@ namespace tunnel
// let it die if the tunnel pool has been reconfigured and this is old // let it die if the tunnel pool has been reconfigured and this is old
if (pool && tunnel->GetNumHops() == pool->GetNumOutboundHops()) if (pool && tunnel->GetNumHops() == pool->GetNumOutboundHops())
{ {
tunnel->SetIsRecreated (); tunnel->SetRecreated (true);
pool->RecreateOutboundTunnel (tunnel); pool->RecreateOutboundTunnel (tunnel);
} }
} }
@ -703,7 +718,7 @@ namespace tunnel
LogPrint (eLogDebug, "Tunnel: creating one hop outbound tunnel"); LogPrint (eLogDebug, "Tunnel: creating one hop outbound tunnel");
CreateTunnel<OutboundTunnel> ( CreateTunnel<OutboundTunnel> (
std::make_shared<TunnelConfig> (std::vector<std::shared_ptr<const i2p::data::IdentityEx> > { router->GetRouterIdentity () }, std::make_shared<TunnelConfig> (std::vector<std::shared_ptr<const i2p::data::IdentityEx> > { router->GetRouterIdentity () },
inboundTunnel->GetNextTunnelID (), inboundTunnel->GetNextIdentHash ()) inboundTunnel->GetNextTunnelID (), inboundTunnel->GetNextIdentHash (), false), nullptr
); );
} }
} }
@ -734,7 +749,7 @@ namespace tunnel
// let it die if the tunnel pool was reconfigured and has different number of hops // let it die if the tunnel pool was reconfigured and has different number of hops
if (pool && tunnel->GetNumHops() == pool->GetNumInboundHops()) if (pool && tunnel->GetNumHops() == pool->GetNumInboundHops())
{ {
tunnel->SetIsRecreated (); tunnel->SetRecreated (true);
pool->RecreateInboundTunnel (tunnel); pool->RecreateInboundTunnel (tunnel);
} }
} }
@ -752,8 +767,8 @@ namespace tunnel
if (m_InboundTunnels.empty ()) if (m_InboundTunnels.empty ())
{ {
LogPrint (eLogDebug, "Tunnel: Creating zero hops inbound tunnel"); LogPrint (eLogDebug, "Tunnel: Creating zero hops inbound tunnel");
CreateZeroHopsInboundTunnel (); CreateZeroHopsInboundTunnel (nullptr);
CreateZeroHopsOutboundTunnel (); CreateZeroHopsOutboundTunnel (nullptr);
if (!m_ExploratoryPool) if (!m_ExploratoryPool)
{ {
int ibLen; i2p::config::GetOption("exploratory.inbound.length", ibLen); int ibLen; i2p::config::GetOption("exploratory.inbound.length", ibLen);
@ -779,7 +794,7 @@ namespace tunnel
} }
LogPrint (eLogDebug, "Tunnel: creating one hop inbound tunnel"); LogPrint (eLogDebug, "Tunnel: creating one hop inbound tunnel");
CreateTunnel<InboundTunnel> ( CreateTunnel<InboundTunnel> (
std::make_shared<TunnelConfig> (std::vector<std::shared_ptr<const i2p::data::IdentityEx> > { router->GetRouterIdentity () }) std::make_shared<TunnelConfig> (std::vector<std::shared_ptr<const i2p::data::IdentityEx> > { router->GetRouterIdentity () }, false), nullptr
); );
} }
} }
@ -825,9 +840,11 @@ namespace tunnel
} }
template<class TTunnel> template<class TTunnel>
std::shared_ptr<TTunnel> Tunnels::CreateTunnel (std::shared_ptr<TunnelConfig> config, std::shared_ptr<OutboundTunnel> outboundTunnel) std::shared_ptr<TTunnel> Tunnels::CreateTunnel (std::shared_ptr<TunnelConfig> config,
std::shared_ptr<TunnelPool> pool, std::shared_ptr<OutboundTunnel> outboundTunnel)
{ {
auto newTunnel = std::make_shared<TTunnel> (config); auto newTunnel = std::make_shared<TTunnel> (config);
newTunnel->SetTunnelPool (pool);
uint32_t replyMsgID; uint32_t replyMsgID;
RAND_bytes ((uint8_t *)&replyMsgID, 4); RAND_bytes ((uint8_t *)&replyMsgID, 4);
AddPendingTunnel (replyMsgID, newTunnel); AddPendingTunnel (replyMsgID, newTunnel);
@ -835,20 +852,21 @@ namespace tunnel
return newTunnel; return newTunnel;
} }
std::shared_ptr<InboundTunnel> Tunnels::CreateInboundTunnel (std::shared_ptr<TunnelConfig> config, std::shared_ptr<OutboundTunnel> outboundTunnel) std::shared_ptr<InboundTunnel> Tunnels::CreateInboundTunnel (std::shared_ptr<TunnelConfig> config,
std::shared_ptr<TunnelPool> pool, std::shared_ptr<OutboundTunnel> outboundTunnel)
{ {
if (config) if (config)
return CreateTunnel<InboundTunnel>(config, outboundTunnel); return CreateTunnel<InboundTunnel>(config, pool, outboundTunnel);
else else
return CreateZeroHopsInboundTunnel (); return CreateZeroHopsInboundTunnel (pool);
} }
std::shared_ptr<OutboundTunnel> Tunnels::CreateOutboundTunnel (std::shared_ptr<TunnelConfig> config) std::shared_ptr<OutboundTunnel> Tunnels::CreateOutboundTunnel (std::shared_ptr<TunnelConfig> config, std::shared_ptr<TunnelPool> pool)
{ {
if (config) if (config)
return CreateTunnel<OutboundTunnel>(config); return CreateTunnel<OutboundTunnel>(config, pool);
else else
return CreateZeroHopsOutboundTunnel (); return CreateZeroHopsOutboundTunnel (pool);
} }
void Tunnels::AddPendingTunnel (uint32_t replyMsgID, std::shared_ptr<InboundTunnel> tunnel) void Tunnels::AddPendingTunnel (uint32_t replyMsgID, std::shared_ptr<InboundTunnel> tunnel)
@ -882,7 +900,7 @@ namespace tunnel
{ {
// build symmetric outbound tunnel // build symmetric outbound tunnel
CreateTunnel<OutboundTunnel> (std::make_shared<TunnelConfig>(newTunnel->GetInvertedPeers (), CreateTunnel<OutboundTunnel> (std::make_shared<TunnelConfig>(newTunnel->GetInvertedPeers (),
newTunnel->GetNextTunnelID (), newTunnel->GetNextIdentHash ()), newTunnel->GetNextTunnelID (), newTunnel->GetNextIdentHash (), false), nullptr,
GetNextOutboundTunnel ()); GetNextOutboundTunnel ());
} }
else else
@ -898,18 +916,20 @@ namespace tunnel
} }
std::shared_ptr<ZeroHopsInboundTunnel> Tunnels::CreateZeroHopsInboundTunnel () std::shared_ptr<ZeroHopsInboundTunnel> Tunnels::CreateZeroHopsInboundTunnel (std::shared_ptr<TunnelPool> pool)
{ {
auto inboundTunnel = std::make_shared<ZeroHopsInboundTunnel> (); auto inboundTunnel = std::make_shared<ZeroHopsInboundTunnel> ();
inboundTunnel->SetTunnelPool (pool);
inboundTunnel->SetState (eTunnelStateEstablished); inboundTunnel->SetState (eTunnelStateEstablished);
m_InboundTunnels.push_back (inboundTunnel); m_InboundTunnels.push_back (inboundTunnel);
m_Tunnels[inboundTunnel->GetTunnelID ()] = inboundTunnel; m_Tunnels[inboundTunnel->GetTunnelID ()] = inboundTunnel;
return inboundTunnel; return inboundTunnel;
} }
std::shared_ptr<ZeroHopsOutboundTunnel> Tunnels::CreateZeroHopsOutboundTunnel () std::shared_ptr<ZeroHopsOutboundTunnel> Tunnels::CreateZeroHopsOutboundTunnel (std::shared_ptr<TunnelPool> pool)
{ {
auto outboundTunnel = std::make_shared<ZeroHopsOutboundTunnel> (); auto outboundTunnel = std::make_shared<ZeroHopsOutboundTunnel> ();
outboundTunnel->SetTunnelPool (pool);
outboundTunnel->SetState (eTunnelStateEstablished); outboundTunnel->SetState (eTunnelStateEstablished);
m_OutboundTunnels.push_back (outboundTunnel); m_OutboundTunnels.push_back (outboundTunnel);
// we don't insert into m_Tunnels // we don't insert into m_Tunnels

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2020, The PurpleI2P Project * Copyright (c) 2013-2021, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@ -71,12 +71,13 @@ namespace tunnel
std::shared_ptr<const TunnelConfig> GetTunnelConfig () const { return m_Config; } std::shared_ptr<const TunnelConfig> GetTunnelConfig () const { return m_Config; }
std::vector<std::shared_ptr<const i2p::data::IdentityEx> > GetPeers () const; std::vector<std::shared_ptr<const i2p::data::IdentityEx> > GetPeers () const;
std::vector<std::shared_ptr<const i2p::data::IdentityEx> > GetInvertedPeers () const; std::vector<std::shared_ptr<const i2p::data::IdentityEx> > GetInvertedPeers () const;
bool IsShortBuildMessage () const { return m_IsShortBuildMessage; };
TunnelState GetState () const { return m_State; }; TunnelState GetState () const { return m_State; };
void SetState (TunnelState state); void SetState (TunnelState state);
bool IsEstablished () const { return m_State == eTunnelStateEstablished; }; bool IsEstablished () const { return m_State == eTunnelStateEstablished; };
bool IsFailed () const { return m_State == eTunnelStateFailed; }; bool IsFailed () const { return m_State == eTunnelStateFailed; };
bool IsRecreated () const { return m_IsRecreated; }; bool IsRecreated () const { return m_IsRecreated; };
void SetIsRecreated () { m_IsRecreated = true; }; void SetRecreated (bool recreated) { m_IsRecreated = recreated; };
int GetNumHops () const { return m_Hops.size (); }; int GetNumHops () const { return m_Hops.size (); };
virtual bool IsInbound() const = 0; virtual bool IsInbound() const = 0;
@ -109,9 +110,11 @@ namespace tunnel
std::shared_ptr<const TunnelConfig> m_Config; std::shared_ptr<const TunnelConfig> m_Config;
std::vector<std::unique_ptr<TunnelHop> > m_Hops; std::vector<std::unique_ptr<TunnelHop> > m_Hops;
bool m_IsShortBuildMessage;
std::shared_ptr<TunnelPool> m_Pool; // pool, tunnel belongs to, or null std::shared_ptr<TunnelPool> m_Pool; // pool, tunnel belongs to, or null
TunnelState m_State; TunnelState m_State;
bool m_IsRecreated; i2p::data::RouterInfo::CompatibleTransports m_FarEndTransports;
bool m_IsRecreated; // if tunnel is replaced by new, or new tunnel requested to replace
uint64_t m_Latency; // in milliseconds uint64_t m_Latency; // in milliseconds
}; };
@ -205,8 +208,8 @@ namespace tunnel
void AddTransitTunnel (std::shared_ptr<TransitTunnel> tunnel); void AddTransitTunnel (std::shared_ptr<TransitTunnel> tunnel);
void AddOutboundTunnel (std::shared_ptr<OutboundTunnel> newTunnel); void AddOutboundTunnel (std::shared_ptr<OutboundTunnel> newTunnel);
void AddInboundTunnel (std::shared_ptr<InboundTunnel> newTunnel); void AddInboundTunnel (std::shared_ptr<InboundTunnel> newTunnel);
std::shared_ptr<InboundTunnel> CreateInboundTunnel (std::shared_ptr<TunnelConfig> config, std::shared_ptr<OutboundTunnel> outboundTunnel); std::shared_ptr<InboundTunnel> CreateInboundTunnel (std::shared_ptr<TunnelConfig> config, std::shared_ptr<TunnelPool> pool, std::shared_ptr<OutboundTunnel> outboundTunnel);
std::shared_ptr<OutboundTunnel> CreateOutboundTunnel (std::shared_ptr<TunnelConfig> config); std::shared_ptr<OutboundTunnel> CreateOutboundTunnel (std::shared_ptr<TunnelConfig> config, std::shared_ptr<TunnelPool> pool);
void PostTunnelData (std::shared_ptr<I2NPMessage> msg); void PostTunnelData (std::shared_ptr<I2NPMessage> msg);
void PostTunnelData (const std::vector<std::shared_ptr<I2NPMessage> >& msgs); void PostTunnelData (const std::vector<std::shared_ptr<I2NPMessage> >& msgs);
void AddPendingTunnel (uint32_t replyMsgID, std::shared_ptr<InboundTunnel> tunnel); void AddPendingTunnel (uint32_t replyMsgID, std::shared_ptr<InboundTunnel> tunnel);
@ -219,7 +222,8 @@ namespace tunnel
private: private:
template<class TTunnel> template<class TTunnel>
std::shared_ptr<TTunnel> CreateTunnel (std::shared_ptr<TunnelConfig> config, std::shared_ptr<OutboundTunnel> outboundTunnel = nullptr); std::shared_ptr<TTunnel> CreateTunnel (std::shared_ptr<TunnelConfig> config,
std::shared_ptr<TunnelPool> pool, std::shared_ptr<OutboundTunnel> outboundTunnel = nullptr);
template<class TTunnel> template<class TTunnel>
std::shared_ptr<TTunnel> GetPendingTunnel (uint32_t replyMsgID, const std::map<uint32_t, std::shared_ptr<TTunnel> >& pendingTunnels); std::shared_ptr<TTunnel> GetPendingTunnel (uint32_t replyMsgID, const std::map<uint32_t, std::shared_ptr<TTunnel> >& pendingTunnels);
@ -236,8 +240,8 @@ namespace tunnel
void ManagePendingTunnels (PendingTunnels& pendingTunnels); void ManagePendingTunnels (PendingTunnels& pendingTunnels);
void ManageTunnelPools (uint64_t ts); void ManageTunnelPools (uint64_t ts);
std::shared_ptr<ZeroHopsInboundTunnel> CreateZeroHopsInboundTunnel (); std::shared_ptr<ZeroHopsInboundTunnel> CreateZeroHopsInboundTunnel (std::shared_ptr<TunnelPool> pool);
std::shared_ptr<ZeroHopsOutboundTunnel> CreateZeroHopsOutboundTunnel (); std::shared_ptr<ZeroHopsOutboundTunnel> CreateZeroHopsOutboundTunnel (std::shared_ptr<TunnelPool> pool);
private: private:

View File

@ -23,10 +23,6 @@ namespace tunnel
{ {
TunnelHopConfig::TunnelHopConfig (std::shared_ptr<const i2p::data::IdentityEx> r) TunnelHopConfig::TunnelHopConfig (std::shared_ptr<const i2p::data::IdentityEx> r)
{ {
RAND_bytes (layerKey, 32);
RAND_bytes (ivKey, 32);
RAND_bytes (replyKey, 32);
RAND_bytes (replyIV, 16);
RAND_bytes ((uint8_t *)&tunnelID, 4); RAND_bytes ((uint8_t *)&tunnelID, 4);
if (!tunnelID) tunnelID = 1; // tunnelID can't be zero if (!tunnelID) tunnelID = 1; // tunnelID can't be zero
isGateway = true; isGateway = true;
@ -78,14 +74,52 @@ namespace tunnel
} }
} }
void TunnelHopConfig::CreateBuildRequestRecord (uint8_t * record, uint32_t replyMsgID, BN_CTX * ctx) void TunnelHopConfig::DecryptRecord (uint8_t * records, int index) const
{ {
uint8_t * record = records + index*TUNNEL_BUILD_RECORD_SIZE;
i2p::crypto::CBCDecryption decryption;
decryption.SetKey (replyKey);
decryption.SetIV (replyIV);
decryption.Decrypt(record, TUNNEL_BUILD_RECORD_SIZE, record);
}
void ECIESTunnelHopConfig::EncryptECIES (const uint8_t * plainText, size_t len, uint8_t * encrypted)
{
if (!ident) return;
i2p::crypto::InitNoiseNState (*this, ident->GetEncryptionPublicKey ());
auto ephemeralKeys = i2p::transport::transports.GetNextX25519KeysPair ();
memcpy (encrypted, ephemeralKeys->GetPublicKey (), 32);
MixHash (encrypted, 32); // h = SHA256(h || sepk)
encrypted += 32;
uint8_t sharedSecret[32];
ephemeralKeys->Agree (ident->GetEncryptionPublicKey (), sharedSecret); // x25519(sesk, hepk)
MixKey (sharedSecret);
uint8_t nonce[12];
memset (nonce, 0, 12);
if (!i2p::crypto::AEADChaCha20Poly1305 (plainText, len, m_H, 32, m_CK + 32, nonce, encrypted, len + 16, true)) // encrypt
{
LogPrint (eLogWarning, "Tunnel: Plaintext AEAD encryption failed");
return;
}
MixHash (encrypted, len + 16); // h = SHA256(h || ciphertext)
}
bool ECIESTunnelHopConfig::DecryptECIES (const uint8_t * key, const uint8_t * nonce, const uint8_t * encrypted, size_t len, uint8_t * clearText) const
{
return i2p::crypto::AEADChaCha20Poly1305 (encrypted, len - 16, m_H, 32, key, nonce, clearText, len - 16, false); // decrypt
}
void LongECIESTunnelHopConfig::CreateBuildRequestRecord (uint8_t * records, uint32_t replyMsgID)
{
// generate keys
RAND_bytes (layerKey, 32);
RAND_bytes (ivKey, 32);
RAND_bytes (replyKey, 32);
RAND_bytes (replyIV, 16);
// fill clear text
uint8_t flag = 0; uint8_t flag = 0;
if (isGateway) flag |= TUNNEL_BUILD_RECORD_GATEWAY_FLAG; if (isGateway) flag |= TUNNEL_BUILD_RECORD_GATEWAY_FLAG;
if (isEndpoint) flag |= TUNNEL_BUILD_RECORD_ENDPOINT_FLAG; if (isEndpoint) flag |= TUNNEL_BUILD_RECORD_ENDPOINT_FLAG;
auto encryptor = ident->CreateEncryptor (nullptr);
if (IsECIES ())
{
uint8_t clearText[ECIES_BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE]; uint8_t clearText[ECIES_BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE];
htobe32buf (clearText + ECIES_BUILD_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET, tunnelID); htobe32buf (clearText + ECIES_BUILD_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET, tunnelID);
htobe32buf (clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET, nextTunnelID); htobe32buf (clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET, nextTunnelID);
@ -100,52 +134,117 @@ namespace tunnel
htobe32buf (clearText + ECIES_BUILD_REQUEST_RECORD_REQUEST_EXPIRATION_OFFSET, 600); // +10 minutes htobe32buf (clearText + ECIES_BUILD_REQUEST_RECORD_REQUEST_EXPIRATION_OFFSET, 600); // +10 minutes
htobe32buf (clearText + ECIES_BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET, replyMsgID); htobe32buf (clearText + ECIES_BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET, replyMsgID);
memset (clearText + ECIES_BUILD_REQUEST_RECORD_PADDING_OFFSET, 0, ECIES_BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE - ECIES_BUILD_REQUEST_RECORD_PADDING_OFFSET); memset (clearText + ECIES_BUILD_REQUEST_RECORD_PADDING_OFFSET, 0, ECIES_BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE - ECIES_BUILD_REQUEST_RECORD_PADDING_OFFSET);
if (encryptor) // encrypt
EncryptECIES (encryptor, clearText, record + BUILD_REQUEST_RECORD_ENCRYPTED_OFFSET, ctx); uint8_t * record = records + recordIndex*TUNNEL_BUILD_RECORD_SIZE;
} EncryptECIES (clearText, ECIES_BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE, record + BUILD_REQUEST_RECORD_ENCRYPTED_OFFSET);
else
{
uint8_t clearText[BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE];
htobe32buf (clearText + BUILD_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET, tunnelID);
memcpy (clearText + BUILD_REQUEST_RECORD_OUR_IDENT_OFFSET, ident->GetIdentHash (), 32);
htobe32buf (clearText + BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET, nextTunnelID);
memcpy (clearText + BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET, nextIdent, 32);
memcpy (clearText + BUILD_REQUEST_RECORD_LAYER_KEY_OFFSET, layerKey, 32);
memcpy (clearText + BUILD_REQUEST_RECORD_IV_KEY_OFFSET, ivKey, 32);
memcpy (clearText + BUILD_REQUEST_RECORD_REPLY_KEY_OFFSET, replyKey, 32);
memcpy (clearText + BUILD_REQUEST_RECORD_REPLY_IV_OFFSET, replyIV, 16);
clearText[BUILD_REQUEST_RECORD_FLAG_OFFSET] = flag;
htobe32buf (clearText + BUILD_REQUEST_RECORD_REQUEST_TIME_OFFSET, i2p::util::GetHoursSinceEpoch ());
htobe32buf (clearText + BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET, replyMsgID);
RAND_bytes (clearText + BUILD_REQUEST_RECORD_PADDING_OFFSET, BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE - BUILD_REQUEST_RECORD_PADDING_OFFSET);
if (encryptor)
encryptor->Encrypt (clearText, record + BUILD_REQUEST_RECORD_ENCRYPTED_OFFSET, ctx, false);
}
memcpy (record + BUILD_REQUEST_RECORD_TO_PEER_OFFSET, (const uint8_t *)ident->GetIdentHash (), 16); memcpy (record + BUILD_REQUEST_RECORD_TO_PEER_OFFSET, (const uint8_t *)ident->GetIdentHash (), 16);
} }
void TunnelHopConfig::EncryptECIES (std::shared_ptr<i2p::crypto::CryptoKeyEncryptor>& encryptor, bool LongECIESTunnelHopConfig::DecryptBuildResponseRecord (uint8_t * records) const
const uint8_t * plainText, uint8_t * encrypted, BN_CTX * ctx)
{ {
uint8_t hepk[32]; uint8_t * record = records + recordIndex*TUNNEL_BUILD_RECORD_SIZE;
encryptor->Encrypt (nullptr, hepk, nullptr, false);
i2p::crypto::InitNoiseNState (*this, hepk);
auto ephemeralKeys = i2p::transport::transports.GetNextX25519KeysPair ();
memcpy (encrypted, ephemeralKeys->GetPublicKey (), 32);
MixHash (encrypted, 32); // h = SHA256(h || sepk)
encrypted += 32;
uint8_t sharedSecret[32];
ephemeralKeys->Agree (hepk, sharedSecret); // x25519(sesk, hepk)
MixKey (sharedSecret);
uint8_t nonce[12]; uint8_t nonce[12];
memset (nonce, 0, 12); memset (nonce, 0, 12);
if (!i2p::crypto::AEADChaCha20Poly1305 (plainText, ECIES_BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE, m_H, 32, if (!DecryptECIES (m_CK, nonce, record, TUNNEL_BUILD_RECORD_SIZE, record))
m_CK + 32, nonce, encrypted, ECIES_BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE + 16, true)) // encrypt
{ {
LogPrint (eLogWarning, "Tunnel: Plaintext AEAD encryption failed"); LogPrint (eLogWarning, "Tunnel: Response AEAD decryption failed");
return; return false;
} }
MixHash (encrypted, ECIES_BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE + 16); // h = SHA256(h || ciphertext) return true;
}
void ShortECIESTunnelHopConfig::CreateBuildRequestRecord (uint8_t * records, uint32_t replyMsgID)
{
// fill clear text
uint8_t flag = 0;
if (isGateway) flag |= TUNNEL_BUILD_RECORD_GATEWAY_FLAG;
if (isEndpoint) flag |= TUNNEL_BUILD_RECORD_ENDPOINT_FLAG;
uint8_t clearText[SHORT_REQUEST_RECORD_CLEAR_TEXT_SIZE ];
htobe32buf (clearText + SHORT_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET, tunnelID);
htobe32buf (clearText + SHORT_REQUEST_RECORD_NEXT_TUNNEL_OFFSET, nextTunnelID);
memcpy (clearText + SHORT_REQUEST_RECORD_NEXT_IDENT_OFFSET, nextIdent, 32);
clearText[SHORT_REQUEST_RECORD_FLAG_OFFSET] = flag;
memset (clearText + SHORT_REQUEST_RECORD_MORE_FLAGS_OFFSET, 0, 2);
clearText[SHORT_REQUEST_RECORD_LAYER_ENCRYPTION_TYPE] = 0; // AES
htobe32buf (clearText + SHORT_REQUEST_RECORD_REQUEST_TIME_OFFSET, i2p::util::GetMinutesSinceEpoch ());
htobe32buf (clearText + SHORT_REQUEST_RECORD_REQUEST_EXPIRATION_OFFSET , 600); // +10 minutes
htobe32buf (clearText + SHORT_REQUEST_RECORD_SEND_MSG_ID_OFFSET, replyMsgID);
memset (clearText + SHORT_REQUEST_RECORD_PADDING_OFFSET, 0, SHORT_REQUEST_RECORD_CLEAR_TEXT_SIZE - SHORT_REQUEST_RECORD_PADDING_OFFSET);
// encrypt
uint8_t * record = records + recordIndex*SHORT_TUNNEL_BUILD_RECORD_SIZE;
EncryptECIES (clearText, SHORT_REQUEST_RECORD_CLEAR_TEXT_SIZE, record + SHORT_REQUEST_RECORD_ENCRYPTED_OFFSET);
// derive keys
i2p::crypto::HKDF (m_CK, nullptr, 0, "SMTunnelReplyKey", m_CK);
memcpy (replyKey, m_CK + 32, 32);
i2p::crypto::HKDF (m_CK, nullptr, 0, "SMTunnelLayerKey", m_CK);
memcpy (layerKey, m_CK + 32, 32);
if (isEndpoint)
{
i2p::crypto::HKDF (m_CK, nullptr, 0, "TunnelLayerIVKey", m_CK);
memcpy (ivKey, m_CK + 32, 32);
i2p::crypto::HKDF (m_CK, nullptr, 0, "RGarlicKeyAndTag", m_CK); // OTBRM garlic key m_CK + 32, tag first 8 bytes of m_CK
}
else
memcpy (ivKey, m_CK, 32); // last HKDF
memcpy (record + BUILD_REQUEST_RECORD_TO_PEER_OFFSET, (const uint8_t *)ident->GetIdentHash (), 16);
}
bool ShortECIESTunnelHopConfig::DecryptBuildResponseRecord (uint8_t * records) const
{
uint8_t * record = records + recordIndex*SHORT_TUNNEL_BUILD_RECORD_SIZE;
uint8_t nonce[12];
memset (nonce, 0, 12);
nonce[4] = recordIndex; // nonce is record index
if (!DecryptECIES (replyKey, nonce, record, SHORT_TUNNEL_BUILD_RECORD_SIZE, record))
{
LogPrint (eLogWarning, "Tunnel: Response AEAD decryption failed");
return false;
}
return true;
}
void ShortECIESTunnelHopConfig::DecryptRecord (uint8_t * records, int index) const
{
uint8_t * record = records + index*SHORT_TUNNEL_BUILD_RECORD_SIZE;
uint8_t nonce[12];
memset (nonce, 0, 12);
nonce[4] = index; // nonce is index
i2p::crypto::ChaCha20 (record, SHORT_TUNNEL_BUILD_RECORD_SIZE, replyKey, nonce, record);
}
uint64_t ShortECIESTunnelHopConfig::GetGarlicKey (uint8_t * key) const
{
uint64_t tag;
memcpy (&tag, m_CK, 8);
memcpy (key, m_CK + 32, 32);
return tag;
}
void TunnelConfig::CreatePeers (const std::vector<std::shared_ptr<const i2p::data::IdentityEx> >& peers)
{
TunnelHopConfig * prev = nullptr;
for (const auto& it: peers)
{
TunnelHopConfig * hop = nullptr;
if (m_IsShort)
hop = new ShortECIESTunnelHopConfig (it);
else
{
if (it->GetCryptoKeyType () == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD)
hop = new LongECIESTunnelHopConfig (it);
else
LogPrint (eLogError, "Tunnel: ElGamal router is not supported");
}
if (hop)
{
if (prev)
prev->SetNext (hop);
else
m_FirstHop = hop;
prev = hop;
}
}
m_LastHop = prev;
} }
} }
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2020, The PurpleI2P Project * Copyright (c) 2013-2021, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@ -18,7 +18,7 @@ namespace i2p
{ {
namespace tunnel namespace tunnel
{ {
struct TunnelHopConfig: public i2p::crypto::NoiseSymmetricState struct TunnelHopConfig
{ {
std::shared_ptr<const i2p::data::IdentityEx> ident; std::shared_ptr<const i2p::data::IdentityEx> ident;
i2p::data::IdentHash nextIdent; i2p::data::IdentHash nextIdent;
@ -33,30 +33,66 @@ namespace tunnel
int recordIndex; // record # in tunnel build message int recordIndex; // record # in tunnel build message
TunnelHopConfig (std::shared_ptr<const i2p::data::IdentityEx> r); TunnelHopConfig (std::shared_ptr<const i2p::data::IdentityEx> r);
virtual ~TunnelHopConfig () {};
void SetNextIdent (const i2p::data::IdentHash& ident); void SetNextIdent (const i2p::data::IdentHash& ident);
void SetReplyHop (uint32_t replyTunnelID, const i2p::data::IdentHash& replyIdent); void SetReplyHop (uint32_t replyTunnelID, const i2p::data::IdentHash& replyIdent);
void SetNext (TunnelHopConfig * n); void SetNext (TunnelHopConfig * n);
void SetPrev (TunnelHopConfig * p); void SetPrev (TunnelHopConfig * p);
bool IsECIES () const { return ident->GetCryptoKeyType () == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD; }; virtual uint8_t GetRetCode (const uint8_t * records) const = 0;
void CreateBuildRequestRecord (uint8_t * record, uint32_t replyMsgID, BN_CTX * ctx); virtual void CreateBuildRequestRecord (uint8_t * records, uint32_t replyMsgID) = 0;
void EncryptECIES (std::shared_ptr<i2p::crypto::CryptoKeyEncryptor>& encryptor, virtual bool DecryptBuildResponseRecord (uint8_t * records) const = 0;
const uint8_t * clearText, uint8_t * encrypted, BN_CTX * ctx); virtual void DecryptRecord (uint8_t * records, int index) const; // AES
virtual uint64_t GetGarlicKey (uint8_t * key) const { return 0; }; // return tag
};
struct ECIESTunnelHopConfig: public TunnelHopConfig, public i2p::crypto::NoiseSymmetricState
{
ECIESTunnelHopConfig (std::shared_ptr<const i2p::data::IdentityEx> r):
TunnelHopConfig (r) {};
void EncryptECIES (const uint8_t * clearText, size_t len, uint8_t * encrypted);
bool DecryptECIES (const uint8_t * key, const uint8_t * nonce, const uint8_t * encrypted, size_t len, uint8_t * clearText) const;
};
struct LongECIESTunnelHopConfig: public ECIESTunnelHopConfig
{
LongECIESTunnelHopConfig (std::shared_ptr<const i2p::data::IdentityEx> r):
ECIESTunnelHopConfig (r) {};
uint8_t GetRetCode (const uint8_t * records) const override
{ return (records + recordIndex*TUNNEL_BUILD_RECORD_SIZE)[ECIES_BUILD_RESPONSE_RECORD_RET_OFFSET]; };
void CreateBuildRequestRecord (uint8_t * records, uint32_t replyMsgID) override;
bool DecryptBuildResponseRecord (uint8_t * records) const override;
};
struct ShortECIESTunnelHopConfig: public ECIESTunnelHopConfig
{
ShortECIESTunnelHopConfig (std::shared_ptr<const i2p::data::IdentityEx> r):
ECIESTunnelHopConfig (r) {};
uint8_t GetRetCode (const uint8_t * records) const override
{ return (records + recordIndex*SHORT_TUNNEL_BUILD_RECORD_SIZE)[SHORT_RESPONSE_RECORD_RET_OFFSET]; };
void CreateBuildRequestRecord (uint8_t * records, uint32_t replyMsgID) override;
bool DecryptBuildResponseRecord (uint8_t * records) const override;
void DecryptRecord (uint8_t * records, int index) const override; // Chacha20
uint64_t GetGarlicKey (uint8_t * key) const override;
}; };
class TunnelConfig class TunnelConfig
{ {
public: public:
TunnelConfig (std::vector<std::shared_ptr<const i2p::data::IdentityEx> > peers) // inbound TunnelConfig (const std::vector<std::shared_ptr<const i2p::data::IdentityEx> >& peers,
bool isShort, i2p::data::RouterInfo::CompatibleTransports farEndTransports = 0): // inbound
m_IsShort (isShort), m_FarEndTransports (farEndTransports)
{ {
CreatePeers (peers); CreatePeers (peers);
m_LastHop->SetNextIdent (i2p::context.GetIdentHash ()); m_LastHop->SetNextIdent (i2p::context.GetIdentHash ());
} }
TunnelConfig (std::vector<std::shared_ptr<const i2p::data::IdentityEx> > peers, TunnelConfig (const std::vector<std::shared_ptr<const i2p::data::IdentityEx> >& peers,
uint32_t replyTunnelID, const i2p::data::IdentHash& replyIdent) // outbound uint32_t replyTunnelID, const i2p::data::IdentHash& replyIdent, bool isShort,
i2p::data::RouterInfo::CompatibleTransports farEndTransports = 0): // outbound
m_IsShort (isShort), m_FarEndTransports (farEndTransports)
{ {
CreatePeers (peers); CreatePeers (peers);
m_FirstHop->isGateway = false; m_FirstHop->isGateway = false;
@ -75,6 +111,13 @@ namespace tunnel
} }
} }
bool IsShort () const { return m_IsShort; }
i2p::data::RouterInfo::CompatibleTransports GetFarEndTransports () const
{
return m_FarEndTransports;
}
TunnelHopConfig * GetFirstHop () const TunnelHopConfig * GetFirstHop () const
{ {
return m_FirstHop; return m_FirstHop;
@ -141,31 +184,20 @@ namespace tunnel
protected: protected:
// this constructor can't be called from outside // this constructor can't be called from outside
TunnelConfig (): m_FirstHop (nullptr), m_LastHop (nullptr) TunnelConfig (): m_FirstHop (nullptr), m_LastHop (nullptr), m_IsShort (false),
m_FarEndTransports (0)
{ {
} }
private: private:
template<class Peers> void CreatePeers (const std::vector<std::shared_ptr<const i2p::data::IdentityEx> >& peers);
void CreatePeers (const Peers& peers)
{
TunnelHopConfig * prev = nullptr;
for (const auto& it: peers)
{
auto hop = new TunnelHopConfig (it);
if (prev)
prev->SetNext (hop);
else
m_FirstHop = hop;
prev = hop;
}
m_LastHop = prev;
}
private: private:
TunnelHopConfig * m_FirstHop, * m_LastHop; TunnelHopConfig * m_FirstHop, * m_LastHop;
bool m_IsShort;
i2p::data::RouterInfo::CompatibleTransports m_FarEndTransports;
}; };
class ZeroHopsTunnelConfig: public TunnelConfig class ZeroHopsTunnelConfig: public TunnelConfig

View File

@ -52,24 +52,25 @@ namespace tunnel
bool isFollowOnFragment = flag & 0x80, isLastFragment = true; bool isFollowOnFragment = flag & 0x80, isLastFragment = true;
uint32_t msgID = 0; uint32_t msgID = 0;
int fragmentNum = 0; int fragmentNum = 0;
TunnelMessageBlockEx m;
if (!isFollowOnFragment) if (!isFollowOnFragment)
{ {
// first fragment // first fragment
if (m_CurrentMsgID)
AddIncompleteCurrentMessage (); // we have got a new message while previous is not complete
m.deliveryType = (TunnelDeliveryType)((flag >> 5) & 0x03); m_CurrentMessage.deliveryType = (TunnelDeliveryType)((flag >> 5) & 0x03);
switch (m.deliveryType) switch (m_CurrentMessage.deliveryType)
{ {
case eDeliveryTypeLocal: // 0 case eDeliveryTypeLocal: // 0
break; break;
case eDeliveryTypeTunnel: // 1 case eDeliveryTypeTunnel: // 1
m.tunnelID = bufbe32toh (fragment); m_CurrentMessage.tunnelID = bufbe32toh (fragment);
fragment += 4; // tunnelID fragment += 4; // tunnelID
m.hash = i2p::data::IdentHash (fragment); m_CurrentMessage.hash = i2p::data::IdentHash (fragment);
fragment += 32; // hash fragment += 32; // hash
break; break;
case eDeliveryTypeRouter: // 2 case eDeliveryTypeRouter: // 2
m.hash = i2p::data::IdentHash (fragment); m_CurrentMessage.hash = i2p::data::IdentHash (fragment);
fragment += 32; // to hash fragment += 32; // to hash
break; break;
default: ; default: ;
@ -81,6 +82,7 @@ namespace tunnel
// Message ID // Message ID
msgID = bufbe32toh (fragment); msgID = bufbe32toh (fragment);
fragment += 4; fragment += 4;
m_CurrentMsgID = msgID;
isLastFragment = false; isLastFragment = false;
} }
} }
@ -96,48 +98,58 @@ namespace tunnel
uint16_t size = bufbe16toh (fragment); uint16_t size = bufbe16toh (fragment);
fragment += 2; fragment += 2;
// handle fragment
if (isFollowOnFragment)
{
// existing message
if (m_CurrentMsgID && m_CurrentMsgID == msgID && m_CurrentMessage.nextFragmentNum == fragmentNum)
HandleCurrenMessageFollowOnFragment (fragment, size, isLastFragment); // previous
else
{
HandleFollowOnFragment (msgID, isLastFragment, fragmentNum, fragment, size); // another
m_CurrentMsgID = 0; m_CurrentMessage.data = nullptr;
}
}
else
{
// new message
msg->offset = fragment - msg->buf; msg->offset = fragment - msg->buf;
msg->len = msg->offset + size; msg->len = msg->offset + size;
// check message size
if (msg->len > msg->maxLen) if (msg->len > msg->maxLen)
{ {
LogPrint (eLogError, "TunnelMessage: fragment is too long ", (int)size); LogPrint (eLogError, "TunnelMessage: fragment is too long ", (int)size);
m_CurrentMsgID = 0; m_CurrentMessage.data = nullptr;
return; return;
} }
// create new or assign I2NP message
if (fragment + size < decrypted + TUNNEL_DATA_ENCRYPTED_SIZE) if (fragment + size < decrypted + TUNNEL_DATA_ENCRYPTED_SIZE)
{ {
// this is not last message. we have to copy it // this is not last message. we have to copy it
m.data = NewI2NPTunnelMessage (); m_CurrentMessage.data = NewI2NPTunnelMessage (true);
m.data->offset += TUNNEL_GATEWAY_HEADER_SIZE; // reserve room for TunnelGateway header *(m_CurrentMessage.data) = *msg;
m.data->len += TUNNEL_GATEWAY_HEADER_SIZE;
*(m.data) = *msg;
} }
else else
m.data = msg; m_CurrentMessage.data = msg;
if (!isFollowOnFragment && isLastFragment) if (isLastFragment)
HandleNextMessage (m);
else
{ {
if (msgID) // msgID is presented, assume message is fragmented // single message
HandleNextMessage (m_CurrentMessage);
m_CurrentMsgID = 0; m_CurrentMessage.data = nullptr;
}
else if (msgID)
{ {
if (!isFollowOnFragment) // create new incomlete message // first fragment of a new message
{ m_CurrentMessage.nextFragmentNum = 1;
m.nextFragmentNum = 1; m_CurrentMessage.receiveTime = i2p::util::GetMillisecondsSinceEpoch ();
m.receiveTime = i2p::util::GetMillisecondsSinceEpoch (); HandleOutOfSequenceFragments (msgID, m_CurrentMessage);
auto ret = m_IncompleteMessages.insert (std::pair<uint32_t, TunnelMessageBlockEx>(msgID, m));
if (ret.second)
HandleOutOfSequenceFragments (msgID, ret.first->second);
else
LogPrint (eLogError, "TunnelMessage: Incomplete message ", msgID, " already exists");
} }
else else
{ {
m.nextFragmentNum = fragmentNum;
HandleFollowOnFragment (msgID, isLastFragment, m);
}
}
else
LogPrint (eLogError, "TunnelMessage: Message is fragmented, but msgID is not presented"); LogPrint (eLogError, "TunnelMessage: Message is fragmented, but msgID is not presented");
m_CurrentMsgID = 0; m_CurrentMessage.data = nullptr;
}
} }
fragment += size; fragment += size;
@ -147,27 +159,17 @@ namespace tunnel
LogPrint (eLogError, "TunnelMessage: zero not found"); LogPrint (eLogError, "TunnelMessage: zero not found");
} }
void TunnelEndpoint::HandleFollowOnFragment (uint32_t msgID, bool isLastFragment, const TunnelMessageBlockEx& m) void TunnelEndpoint::HandleFollowOnFragment (uint32_t msgID, bool isLastFragment,
uint8_t fragmentNum, const uint8_t * fragment, size_t size)
{ {
auto fragment = m.data->GetBuffer ();
auto size = m.data->GetLength ();
auto it = m_IncompleteMessages.find (msgID); auto it = m_IncompleteMessages.find (msgID);
if (it != m_IncompleteMessages.end()) if (it != m_IncompleteMessages.end())
{ {
auto& msg = it->second; auto& msg = it->second;
if (m.nextFragmentNum == msg.nextFragmentNum) if (fragmentNum == msg.nextFragmentNum)
{ {
if (msg.data->len + size < I2NP_MAX_MESSAGE_SIZE) // check if message is not too long if (ConcatFollowOnFragment (msg, fragment, size))
{ {
if (msg.data->len + size > msg.data->maxLen)
{
// LogPrint (eLogWarning, "TunnelMessage: I2NP message size ", msg.data->maxLen, " is not enough");
auto newMsg = NewI2NPMessage ();
*newMsg = *(msg.data);
msg.data = newMsg;
}
if (msg.data->Concat (fragment, size) < size) // concatenate fragment
LogPrint (eLogError, "TunnelMessage: I2NP buffer overflow ", msg.data->maxLen);
if (isLastFragment) if (isLastFragment)
{ {
// message complete // message complete
@ -182,26 +184,86 @@ namespace tunnel
} }
else else
{ {
LogPrint (eLogError, "TunnelMessage: Fragment ", m.nextFragmentNum, " of message ", msgID, "exceeds max I2NP message size, message dropped"); LogPrint (eLogError, "TunnelMessage: Fragment ", fragmentNum, " of message ", msgID, "exceeds max I2NP message size, message dropped");
m_IncompleteMessages.erase (it); m_IncompleteMessages.erase (it);
} }
} }
else else
{ {
LogPrint (eLogWarning, "TunnelMessage: Unexpected fragment ", (int)m.nextFragmentNum, " instead ", (int)msg.nextFragmentNum, " of message ", msgID, ", saved"); LogPrint (eLogWarning, "TunnelMessage: Unexpected fragment ", (int)fragmentNum, " instead ", (int)msg.nextFragmentNum, " of message ", msgID, ", saved");
AddOutOfSequenceFragment (msgID, m.nextFragmentNum, isLastFragment, m.data); AddOutOfSequenceFragment (msgID, fragmentNum, isLastFragment, fragment, size);
} }
} }
else else
{ {
LogPrint (eLogWarning, "TunnelMessage: First fragment of message ", msgID, " not found, saved"); LogPrint (eLogDebug, "TunnelMessage: First fragment of message ", msgID, " not found, saved");
AddOutOfSequenceFragment (msgID, m.nextFragmentNum, isLastFragment, m.data); AddOutOfSequenceFragment (msgID, fragmentNum, isLastFragment, fragment, size);
} }
} }
void TunnelEndpoint::AddOutOfSequenceFragment (uint32_t msgID, uint8_t fragmentNum, bool isLastFragment, std::shared_ptr<I2NPMessage> data) bool TunnelEndpoint::ConcatFollowOnFragment (TunnelMessageBlockEx& msg, const uint8_t * fragment, size_t size) const
{ {
if (!m_OutOfSequenceFragments.insert ({{msgID, fragmentNum}, {isLastFragment, data, i2p::util::GetMillisecondsSinceEpoch () }}).second) if (msg.data->len + size < I2NP_MAX_MESSAGE_SIZE) // check if message is not too long
{
if (msg.data->len + size > msg.data->maxLen)
{
// LogPrint (eLogWarning, "TunnelMessage: I2NP message size ", msg.data->maxLen, " is not enough");
auto newMsg = NewI2NPMessage ();
*newMsg = *(msg.data);
msg.data = newMsg;
}
if (msg.data->Concat (fragment, size) < size) // concatenate fragment
{
LogPrint (eLogError, "TunnelMessage: I2NP buffer overflow ", msg.data->maxLen);
return false;
}
}
else
return false;
return true;
}
void TunnelEndpoint::HandleCurrenMessageFollowOnFragment (const uint8_t * fragment, size_t size, bool isLastFragment)
{
if (ConcatFollowOnFragment (m_CurrentMessage, fragment, size))
{
if (isLastFragment)
{
// message complete
HandleNextMessage (m_CurrentMessage);
m_CurrentMsgID = 0; m_CurrentMessage.data = nullptr;
}
else
{
m_CurrentMessage.nextFragmentNum++;
HandleOutOfSequenceFragments (m_CurrentMsgID, m_CurrentMessage);
}
}
else
{
LogPrint (eLogError, "TunnelMessage: Fragment ", m_CurrentMessage.nextFragmentNum, " of message ", m_CurrentMsgID, " exceeds max I2NP message size, message dropped");
m_CurrentMsgID = 0; m_CurrentMessage.data = nullptr;
}
}
void TunnelEndpoint::AddIncompleteCurrentMessage ()
{
if (m_CurrentMsgID)
{
auto ret = m_IncompleteMessages.emplace (m_CurrentMsgID, m_CurrentMessage);
if (!ret.second)
LogPrint (eLogError, "TunnelMessage: Incomplete message ", m_CurrentMsgID, " already exists");
m_CurrentMessage.data = nullptr;
m_CurrentMsgID = 0;
}
}
void TunnelEndpoint::AddOutOfSequenceFragment (uint32_t msgID, uint8_t fragmentNum,
bool isLastFragment, const uint8_t * fragment, size_t size)
{
std::unique_ptr<Fragment> f(new Fragment (isLastFragment, i2p::util::GetMillisecondsSinceEpoch (), size));
memcpy (f->data.data (), fragment, size);
if (!m_OutOfSequenceFragments.emplace ((uint64_t)msgID << 32 | fragmentNum, std::move (f)).second)
LogPrint (eLogInfo, "TunnelMessage: duplicate out-of-sequence fragment ", fragmentNum, " of message ", msgID); LogPrint (eLogInfo, "TunnelMessage: duplicate out-of-sequence fragment ", fragmentNum, " of message ", msgID);
} }
@ -212,7 +274,14 @@ namespace tunnel
if (!msg.nextFragmentNum) // message complete if (!msg.nextFragmentNum) // message complete
{ {
HandleNextMessage (msg); HandleNextMessage (msg);
if (&msg == &m_CurrentMessage)
{
m_CurrentMsgID = 0;
m_CurrentMessage.data = nullptr;
}
else
m_IncompleteMessages.erase (msgID); m_IncompleteMessages.erase (msgID);
LogPrint (eLogDebug, "TunnelMessage: All fragments of message ", msgID, " found");
break; break;
} }
} }
@ -220,11 +289,11 @@ namespace tunnel
bool TunnelEndpoint::ConcatNextOutOfSequenceFragment (uint32_t msgID, TunnelMessageBlockEx& msg) bool TunnelEndpoint::ConcatNextOutOfSequenceFragment (uint32_t msgID, TunnelMessageBlockEx& msg)
{ {
auto it = m_OutOfSequenceFragments.find ({msgID, msg.nextFragmentNum}); auto it = m_OutOfSequenceFragments.find ((uint64_t)msgID << 32 | msg.nextFragmentNum);
if (it != m_OutOfSequenceFragments.end ()) if (it != m_OutOfSequenceFragments.end ())
{ {
LogPrint (eLogDebug, "TunnelMessage: Out-of-sequence fragment ", (int)msg.nextFragmentNum, " of message ", msgID, " found"); LogPrint (eLogDebug, "TunnelMessage: Out-of-sequence fragment ", (int)msg.nextFragmentNum, " of message ", msgID, " found");
size_t size = it->second.data->GetLength (); size_t size = it->second->data.size ();
if (msg.data->len + size > msg.data->maxLen) if (msg.data->len + size > msg.data->maxLen)
{ {
LogPrint (eLogWarning, "TunnelMessage: Tunnel endpoint I2NP message size ", msg.data->maxLen, " is not enough"); LogPrint (eLogWarning, "TunnelMessage: Tunnel endpoint I2NP message size ", msg.data->maxLen, " is not enough");
@ -232,9 +301,9 @@ namespace tunnel
*newMsg = *(msg.data); *newMsg = *(msg.data);
msg.data = newMsg; msg.data = newMsg;
} }
if (msg.data->Concat (it->second.data->GetBuffer (), size) < size) // concatenate out-of-sync fragment if (msg.data->Concat (it->second->data.data (), size) < size) // concatenate out-of-sync fragment
LogPrint (eLogError, "TunnelMessage: Tunnel endpoint I2NP buffer overflow ", msg.data->maxLen); LogPrint (eLogError, "TunnelMessage: Tunnel endpoint I2NP buffer overflow ", msg.data->maxLen);
if (it->second.isLastFragment) if (it->second->isLastFragment)
// message complete // message complete
msg.nextFragmentNum = 0; msg.nextFragmentNum = 0;
else else
@ -287,7 +356,7 @@ namespace tunnel
// out-of-sequence fragments // out-of-sequence fragments
for (auto it = m_OutOfSequenceFragments.begin (); it != m_OutOfSequenceFragments.end ();) for (auto it = m_OutOfSequenceFragments.begin (); it != m_OutOfSequenceFragments.end ();)
{ {
if (ts > it->second.receiveTime + i2p::I2NP_MESSAGE_EXPIRATION_TIMEOUT) if (ts > it->second->receiveTime + i2p::I2NP_MESSAGE_EXPIRATION_TIMEOUT)
it = m_OutOfSequenceFragments.erase (it); it = m_OutOfSequenceFragments.erase (it);
else else
++it; ++it;

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2020, The PurpleI2P Project * Copyright (c) 2013-2021, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@ -10,8 +10,9 @@
#define TUNNEL_ENDPOINT_H__ #define TUNNEL_ENDPOINT_H__
#include <inttypes.h> #include <inttypes.h>
#include <map> #include <vector>
#include <string> #include <string>
#include <unordered_map>
#include "I2NPProtocol.h" #include "I2NPProtocol.h"
#include "TunnelBase.h" #include "TunnelBase.h"
@ -29,14 +30,15 @@ namespace tunnel
struct Fragment struct Fragment
{ {
Fragment (bool last, uint64_t t, size_t size): isLastFragment (last), receiveTime (t), data (size) {};
bool isLastFragment; bool isLastFragment;
std::shared_ptr<I2NPMessage> data;
uint64_t receiveTime; // milliseconds since epoch uint64_t receiveTime; // milliseconds since epoch
std::vector<uint8_t> data;
}; };
public: public:
TunnelEndpoint (bool isInbound): m_IsInbound (isInbound), m_NumReceivedBytes (0) {}; TunnelEndpoint (bool isInbound): m_IsInbound (isInbound), m_NumReceivedBytes (0), m_CurrentMsgID (0) {};
~TunnelEndpoint (); ~TunnelEndpoint ();
size_t GetNumReceivedBytes () const { return m_NumReceivedBytes; }; size_t GetNumReceivedBytes () const { return m_NumReceivedBytes; };
void Cleanup (); void Cleanup ();
@ -45,19 +47,24 @@ namespace tunnel
private: private:
void HandleFollowOnFragment (uint32_t msgID, bool isLastFragment, const TunnelMessageBlockEx& m); void HandleFollowOnFragment (uint32_t msgID, bool isLastFragment, uint8_t fragmentNum, const uint8_t * fragment, size_t size);
bool ConcatFollowOnFragment (TunnelMessageBlockEx& msg, const uint8_t * fragment, size_t size) const; // true if success
void HandleCurrenMessageFollowOnFragment (const uint8_t * frgament, size_t size, bool isLastFragment);
void HandleNextMessage (const TunnelMessageBlock& msg); void HandleNextMessage (const TunnelMessageBlock& msg);
void AddOutOfSequenceFragment (uint32_t msgID, uint8_t fragmentNum, bool isLastFragment, std::shared_ptr<I2NPMessage> data); void AddOutOfSequenceFragment (uint32_t msgID, uint8_t fragmentNum, bool isLastFragment, const uint8_t * fragment, size_t size);
bool ConcatNextOutOfSequenceFragment (uint32_t msgID, TunnelMessageBlockEx& msg); // true if something added bool ConcatNextOutOfSequenceFragment (uint32_t msgID, TunnelMessageBlockEx& msg); // true if something added
void HandleOutOfSequenceFragments (uint32_t msgID, TunnelMessageBlockEx& msg); void HandleOutOfSequenceFragments (uint32_t msgID, TunnelMessageBlockEx& msg);
void AddIncompleteCurrentMessage ();
private: private:
std::map<uint32_t, TunnelMessageBlockEx> m_IncompleteMessages; std::unordered_map<uint32_t, TunnelMessageBlockEx> m_IncompleteMessages;
std::map<std::pair<uint32_t, uint8_t>, Fragment> m_OutOfSequenceFragments; // (msgID, fragment#)->fragment std::unordered_map<uint64_t, std::unique_ptr<Fragment> > m_OutOfSequenceFragments; // ((msgID << 8) + fragment#)->fragment
bool m_IsInbound; bool m_IsInbound;
size_t m_NumReceivedBytes; size_t m_NumReceivedBytes;
TunnelMessageBlockEx m_CurrentMessage;
uint32_t m_CurrentMsgID;
}; };
} }
} }

View File

@ -215,7 +215,7 @@ namespace tunnel
const auto& tunnelDataMsgs = m_Buffer.GetTunnelDataMsgs (); const auto& tunnelDataMsgs = m_Buffer.GetTunnelDataMsgs ();
for (auto& tunnelMsg : tunnelDataMsgs) for (auto& tunnelMsg : tunnelDataMsgs)
{ {
auto newMsg = CreateEmptyTunnelDataMsg (); auto newMsg = CreateEmptyTunnelDataMsg (false);
m_Tunnel->EncryptTunnelMsg (tunnelMsg, newMsg); m_Tunnel->EncryptTunnelMsg (tunnelMsg, newMsg);
htobe32buf (newMsg->GetPayload (), m_Tunnel->GetNextTunnelID ()); htobe32buf (newMsg->GetPayload (), m_Tunnel->GetNextTunnelID ());
newMsg->FillI2NPMessageHeader (eI2NPTunnelData); newMsg->FillI2NPMessageHeader (eI2NPTunnelData);

View File

@ -24,6 +24,22 @@ namespace i2p
{ {
namespace tunnel namespace tunnel
{ {
void Path::Add (std::shared_ptr<const i2p::data::RouterInfo> r)
{
if (r)
{
peers.push_back (r->GetRouterIdentity ());
if (r->GetVersion () < i2p::data::NETDB_MIN_SHORT_TUNNEL_BUILD_VERSION ||
r->GetRouterIdentity ()->GetCryptoKeyType () != i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD)
isShort = false;
}
}
void Path::Reverse ()
{
std::reverse (peers.begin (), peers.end ());
}
TunnelPool::TunnelPool (int numInboundHops, int numOutboundHops, int numInboundTunnels, int numOutboundTunnels): TunnelPool::TunnelPool (int numInboundHops, int numOutboundHops, int numInboundTunnels, int numOutboundTunnels):
m_NumInboundHops (numInboundHops), m_NumOutboundHops (numOutboundHops), m_NumInboundHops (numInboundHops), m_NumOutboundHops (numOutboundHops),
m_NumInboundTunnels (numInboundTunnels), m_NumOutboundTunnels (numOutboundTunnels), m_NumInboundTunnels (numInboundTunnels), m_NumOutboundTunnels (numOutboundTunnels),
@ -97,6 +113,17 @@ namespace tunnel
if (!m_IsActive) return; if (!m_IsActive) return;
{ {
std::unique_lock<std::mutex> l(m_InboundTunnelsMutex); std::unique_lock<std::mutex> l(m_InboundTunnelsMutex);
if (createdTunnel->IsRecreated ())
{
// find and mark old tunnel as expired
createdTunnel->SetRecreated (false);
for (auto& it: m_InboundTunnels)
if (it->IsRecreated () && it->GetNextIdentHash () == createdTunnel->GetNextIdentHash ())
{
it->SetState (eTunnelStateExpiring);
break;
}
}
m_InboundTunnels.insert (createdTunnel); m_InboundTunnels.insert (createdTunnel);
} }
if (m_LocalDestination) if (m_LocalDestination)
@ -420,16 +447,16 @@ namespace tunnel
return hop; return hop;
} }
bool StandardSelectPeers(Path & peers, int numHops, bool inbound, SelectHopFunc nextHop) bool StandardSelectPeers(Path & path, int numHops, bool inbound, SelectHopFunc nextHop)
{ {
int start = 0; int start = 0;
auto prevHop = i2p::context.GetSharedRouterInfo (); std::shared_ptr<const i2p::data::RouterInfo> prevHop = i2p::context.GetSharedRouterInfo ();
if(i2p::transport::transports.RoutesRestricted()) if(i2p::transport::transports.RoutesRestricted())
{ {
/** if routes are restricted prepend trusted first hop */ /** if routes are restricted prepend trusted first hop */
auto hop = i2p::transport::transports.GetRestrictedPeer(); auto hop = i2p::transport::transports.GetRestrictedPeer();
if(!hop) return false; if(!hop) return false;
peers.push_back(hop->GetRouterIdentity()); path.Add (hop);
prevHop = hop; prevHop = hop;
start++; start++;
} }
@ -437,11 +464,11 @@ namespace tunnel
(inbound && i2p::transport::transports.GetNumPeers () > 25)) (inbound && i2p::transport::transports.GetNumPeers () > 25))
{ {
auto r = i2p::transport::transports.GetRandomPeer (); auto r = i2p::transport::transports.GetRandomPeer ();
if (r && !r->GetProfile ()->IsBad () && if (r && r->IsECIES () && !r->GetProfile ()->IsBad () &&
(numHops > 1 || (r->IsV4 () && (!inbound || r->IsReachable ())))) // first inbound must be reachable (numHops > 1 || (r->IsV4 () && (!inbound || r->IsReachable ())))) // first inbound must be reachable
{ {
prevHop = r; prevHop = r;
peers.push_back (r->GetRouterIdentity ()); path.Add (r);
start++; start++;
} }
} }
@ -453,6 +480,7 @@ namespace tunnel
{ {
LogPrint (eLogInfo, "Tunnels: Can't select first hop for a tunnel. Trying already connected"); LogPrint (eLogInfo, "Tunnels: Can't select first hop for a tunnel. Trying already connected");
hop = i2p::transport::transports.GetRandomPeer (); hop = i2p::transport::transports.GetRandomPeer ();
if (hop && !hop->IsECIES ()) hop = nullptr;
} }
if (!hop) if (!hop)
{ {
@ -466,12 +494,14 @@ namespace tunnel
if (hop1) hop = hop1; if (hop1) hop = hop1;
} }
prevHop = hop; prevHop = hop;
peers.push_back (hop->GetRouterIdentity ()); path.Add (hop);
if (i == numHops - 1)
path.farEndTransports = hop->GetCompatibleTransports (inbound);
} }
return true; return true;
} }
bool TunnelPool::SelectPeers (std::vector<std::shared_ptr<const i2p::data::IdentityEx> >& peers, bool isInbound) bool TunnelPool::SelectPeers (Path& path, bool isInbound)
{ {
int numHops = isInbound ? m_NumInboundHops : m_NumOutboundHops; int numHops = isInbound ? m_NumInboundHops : m_NumOutboundHops;
// peers is empty // peers is empty
@ -480,27 +510,36 @@ namespace tunnel
{ {
std::lock_guard<std::mutex> lock(m_CustomPeerSelectorMutex); std::lock_guard<std::mutex> lock(m_CustomPeerSelectorMutex);
if (m_CustomPeerSelector) if (m_CustomPeerSelector)
return m_CustomPeerSelector->SelectPeers(peers, numHops, isInbound); return m_CustomPeerSelector->SelectPeers(path, numHops, isInbound);
} }
// explicit peers in use // explicit peers in use
if (m_ExplicitPeers) return SelectExplicitPeers (peers, isInbound); if (m_ExplicitPeers) return SelectExplicitPeers (path, isInbound);
return StandardSelectPeers(peers, numHops, isInbound, std::bind(&TunnelPool::SelectNextHop, this, std::placeholders::_1, std::placeholders::_2)); return StandardSelectPeers(path, numHops, isInbound, std::bind(&TunnelPool::SelectNextHop, this, std::placeholders::_1, std::placeholders::_2));
} }
bool TunnelPool::SelectExplicitPeers (std::vector<std::shared_ptr<const i2p::data::IdentityEx> >& peers, bool isInbound) bool TunnelPool::SelectExplicitPeers (Path& path, bool isInbound)
{ {
int size = m_ExplicitPeers->size ();
std::vector<int> peerIndicies;
for (int i = 0; i < size; i++) peerIndicies.push_back(i);
std::shuffle (peerIndicies.begin(), peerIndicies.end(), std::mt19937(std::random_device()()));
int numHops = isInbound ? m_NumInboundHops : m_NumOutboundHops; int numHops = isInbound ? m_NumInboundHops : m_NumOutboundHops;
if (numHops > (int)m_ExplicitPeers->size ()) numHops = m_ExplicitPeers->size ();
if (!numHops) return false;
for (int i = 0; i < numHops; i++) for (int i = 0; i < numHops; i++)
{ {
auto& ident = (*m_ExplicitPeers)[peerIndicies[i]]; auto& ident = (*m_ExplicitPeers)[i];
auto r = i2p::data::netdb.FindRouter (ident); auto r = i2p::data::netdb.FindRouter (ident);
if (r) if (r)
peers.push_back (r->GetRouterIdentity ()); {
if (r->IsECIES ())
{
path.Add (r);
if (i == numHops - 1)
path.farEndTransports = r->GetCompatibleTransports (isInbound);
}
else
{
LogPrint (eLogError, "Tunnels: ElGamal router ", ident.ToBase64 (), " is not supported");
return false;
}
}
else else
{ {
LogPrint (eLogInfo, "Tunnels: Can't find router for ", ident.ToBase64 ()); LogPrint (eLogInfo, "Tunnels: Can't find router for ", ident.ToBase64 ());
@ -517,17 +556,16 @@ namespace tunnel
if (!outboundTunnel) if (!outboundTunnel)
outboundTunnel = tunnels.GetNextOutboundTunnel (); outboundTunnel = tunnels.GetNextOutboundTunnel ();
LogPrint (eLogDebug, "Tunnels: Creating destination inbound tunnel..."); LogPrint (eLogDebug, "Tunnels: Creating destination inbound tunnel...");
std::vector<std::shared_ptr<const i2p::data::IdentityEx> > peers; Path path;
if (SelectPeers (peers, true)) if (SelectPeers (path, true))
{ {
std::shared_ptr<TunnelConfig> config; std::shared_ptr<TunnelConfig> config;
if (m_NumInboundHops > 0) if (m_NumInboundHops > 0)
{ {
std::reverse (peers.begin (), peers.end ()); path.Reverse ();
config = std::make_shared<TunnelConfig> (peers); config = std::make_shared<TunnelConfig> (path.peers, path.isShort, path.farEndTransports);
} }
auto tunnel = tunnels.CreateInboundTunnel (config, outboundTunnel); auto tunnel = tunnels.CreateInboundTunnel (config, shared_from_this (), outboundTunnel);
tunnel->SetTunnelPool (shared_from_this ());
if (tunnel->IsEstablished ()) // zero hops if (tunnel->IsEstablished ()) // zero hops
TunnelCreated (tunnel); TunnelCreated (tunnel);
} }
@ -549,14 +587,15 @@ namespace tunnel
std::shared_ptr<TunnelConfig> config; std::shared_ptr<TunnelConfig> config;
if (m_NumInboundHops > 0 && tunnel->GetPeers().size()) if (m_NumInboundHops > 0 && tunnel->GetPeers().size())
{ {
config = std::make_shared<TunnelConfig>(tunnel->GetPeers ()); config = std::make_shared<TunnelConfig>(tunnel->GetPeers (), tunnel->IsShortBuildMessage ());
} }
if (m_NumInboundHops == 0 || config) if (!m_NumInboundHops || config)
{ {
auto newTunnel = tunnels.CreateInboundTunnel (config, outboundTunnel); auto newTunnel = tunnels.CreateInboundTunnel (config, shared_from_this(), outboundTunnel);
newTunnel->SetTunnelPool (shared_from_this());
if (newTunnel->IsEstablished ()) // zero hops if (newTunnel->IsEstablished ()) // zero hops
TunnelCreated (newTunnel); TunnelCreated (newTunnel);
else
newTunnel->SetRecreated (true);
} }
} }
@ -568,15 +607,27 @@ namespace tunnel
if (inboundTunnel) if (inboundTunnel)
{ {
LogPrint (eLogDebug, "Tunnels: Creating destination outbound tunnel..."); LogPrint (eLogDebug, "Tunnels: Creating destination outbound tunnel...");
std::vector<std::shared_ptr<const i2p::data::IdentityEx> > peers; Path path;
if (SelectPeers (peers, false)) if (SelectPeers (path, false))
{ {
if (m_LocalDestination && !m_LocalDestination->SupportsEncryptionType (i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD))
path.isShort = false; // because can't handle ECIES encrypted reply
std::shared_ptr<TunnelConfig> config; std::shared_ptr<TunnelConfig> config;
if (m_NumOutboundHops > 0) if (m_NumOutboundHops > 0)
config = std::make_shared<TunnelConfig>(peers, inboundTunnel->GetNextTunnelID (), inboundTunnel->GetNextIdentHash ()); config = std::make_shared<TunnelConfig>(path.peers, inboundTunnel->GetNextTunnelID (),
auto tunnel = tunnels.CreateOutboundTunnel (config); inboundTunnel->GetNextIdentHash (), path.isShort, path.farEndTransports);
std::shared_ptr<OutboundTunnel> tunnel;
if (path.isShort)
{
// TODO: implement it better
tunnel = tunnels.CreateOutboundTunnel (config, inboundTunnel->GetTunnelPool ());
tunnel->SetTunnelPool (shared_from_this ()); tunnel->SetTunnelPool (shared_from_this ());
if (tunnel->IsEstablished ()) // zero hops }
else
tunnel = tunnels.CreateOutboundTunnel (config, shared_from_this ());
if (tunnel && tunnel->IsEstablished ()) // zero hops
TunnelCreated (tunnel); TunnelCreated (tunnel);
} }
else else
@ -602,12 +653,12 @@ namespace tunnel
std::shared_ptr<TunnelConfig> config; std::shared_ptr<TunnelConfig> config;
if (m_NumOutboundHops > 0 && tunnel->GetPeers().size()) if (m_NumOutboundHops > 0 && tunnel->GetPeers().size())
{ {
config = std::make_shared<TunnelConfig>(tunnel->GetPeers (), inboundTunnel->GetNextTunnelID (), inboundTunnel->GetNextIdentHash ()); config = std::make_shared<TunnelConfig>(tunnel->GetPeers (), inboundTunnel->GetNextTunnelID (),
inboundTunnel->GetNextIdentHash (), inboundTunnel->IsShortBuildMessage ());
} }
if (!m_NumOutboundHops || config) if (!m_NumOutboundHops || config)
{ {
auto newTunnel = tunnels.CreateOutboundTunnel (config); auto newTunnel = tunnels.CreateOutboundTunnel (config, shared_from_this ());
newTunnel->SetTunnelPool (shared_from_this ());
if (newTunnel->IsEstablished ()) // zero hops if (newTunnel->IsEstablished ()) // zero hops
TunnelCreated (newTunnel); TunnelCreated (newTunnel);
} }
@ -620,9 +671,9 @@ namespace tunnel
{ {
LogPrint (eLogDebug, "Tunnels: Creating paired inbound tunnel..."); LogPrint (eLogDebug, "Tunnels: Creating paired inbound tunnel...");
auto tunnel = tunnels.CreateInboundTunnel ( auto tunnel = tunnels.CreateInboundTunnel (
m_NumOutboundHops > 0 ? std::make_shared<TunnelConfig>(outboundTunnel->GetInvertedPeers ()) : nullptr, m_NumOutboundHops > 0 ? std::make_shared<TunnelConfig>(outboundTunnel->GetInvertedPeers (),
outboundTunnel); outboundTunnel->IsShortBuildMessage ()) : nullptr,
tunnel->SetTunnelPool (shared_from_this ()); shared_from_this (), outboundTunnel);
if (tunnel->IsEstablished ()) // zero hops if (tunnel->IsEstablished ()) // zero hops
TunnelCreated (tunnel); TunnelCreated (tunnel);
} }

View File

@ -36,7 +36,15 @@ namespace tunnel
class OutboundTunnel; class OutboundTunnel;
typedef std::shared_ptr<const i2p::data::IdentityEx> Peer; typedef std::shared_ptr<const i2p::data::IdentityEx> Peer;
typedef std::vector<Peer> Path; struct Path
{
std::vector<Peer> peers;
bool isShort = true;
i2p::data::RouterInfo::CompatibleTransports farEndTransports = 0;
void Add (std::shared_ptr<const i2p::data::RouterInfo> r);
void Reverse ();
};
/** interface for custom tunnel peer selection algorithm */ /** interface for custom tunnel peer selection algorithm */
struct ITunnelPeerSelector struct ITunnelPeerSelector
@ -47,8 +55,7 @@ namespace tunnel
typedef std::function<std::shared_ptr<const i2p::data::RouterInfo>(std::shared_ptr<const i2p::data::RouterInfo>, bool)> SelectHopFunc; typedef std::function<std::shared_ptr<const i2p::data::RouterInfo>(std::shared_ptr<const i2p::data::RouterInfo>, bool)> SelectHopFunc;
// standard peer selection algorithm bool StandardSelectPeers(Path & path, int numHops, bool inbound, SelectHopFunc nextHop);
bool StandardSelectPeers(Path & path, int hops, bool inbound, SelectHopFunc nextHop);
class TunnelPool: public std::enable_shared_from_this<TunnelPool> // per local destination class TunnelPool: public std::enable_shared_from_this<TunnelPool> // per local destination
{ {
@ -114,8 +121,8 @@ namespace tunnel
void CreatePairedInboundTunnel (std::shared_ptr<OutboundTunnel> outboundTunnel); void CreatePairedInboundTunnel (std::shared_ptr<OutboundTunnel> outboundTunnel);
template<class TTunnels> template<class TTunnels>
typename TTunnels::value_type GetNextTunnel (TTunnels& tunnels, typename TTunnels::value_type excluded) const; typename TTunnels::value_type GetNextTunnel (TTunnels& tunnels, typename TTunnels::value_type excluded) const;
bool SelectPeers (std::vector<std::shared_ptr<const i2p::data::IdentityEx> >& hops, bool isInbound); bool SelectPeers (Path& path, bool isInbound);
bool SelectExplicitPeers (std::vector<std::shared_ptr<const i2p::data::IdentityEx> >& hops, bool isInbound); bool SelectExplicitPeers (Path& path, bool isInbound);
private: private:

View File

@ -129,7 +129,7 @@ namespace util
pthread_set_name_np(pthread_self(), name); pthread_set_name_np(pthread_self(), name);
#elif defined(__NetBSD__) #elif defined(__NetBSD__)
pthread_setname_np(pthread_self(), "%s", (void *)name); pthread_setname_np(pthread_self(), "%s", (void *)name);
#else #elif !defined(__gnu_hurd__)
pthread_setname_np(pthread_self(), name); pthread_setname_np(pthread_self(), name);
#endif #endif
} }
@ -344,7 +344,7 @@ namespace net
if(fd > 0) if(fd > 0)
{ {
ifreq ifr; ifreq ifr;
strncpy(ifr.ifr_name, ifa->ifa_name, IFNAMSIZ); // set interface for query strncpy(ifr.ifr_name, ifa->ifa_name, IFNAMSIZ-1); // set interface for query
if(ioctl(fd, SIOCGIFMTU, &ifr) >= 0) if(ioctl(fd, SIOCGIFMTU, &ifr) >= 0)
mtu = ifr.ifr_mtu; // MTU mtu = ifr.ifr_mtu; // MTU
else else
@ -555,7 +555,10 @@ namespace net
static const std::vector< std::pair<boost::asio::ip::address_v6::bytes_type, boost::asio::ip::address_v6::bytes_type> > reservedIPv6Ranges { static const std::vector< std::pair<boost::asio::ip::address_v6::bytes_type, boost::asio::ip::address_v6::bytes_type> > reservedIPv6Ranges {
address_pair_v6("2001:db8::", "2001:db8:ffff:ffff:ffff:ffff:ffff:ffff"), address_pair_v6("2001:db8::", "2001:db8:ffff:ffff:ffff:ffff:ffff:ffff"),
address_pair_v6("fc00::", "fdff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"), address_pair_v6("fc00::", "fdff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"),
address_pair_v6("fe80::", "febf:ffff:ffff:ffff:ffff:ffff:ffff:ffff") address_pair_v6("fe80::", "febf:ffff:ffff:ffff:ffff:ffff:ffff:ffff"),
address_pair_v6("ff00::", "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"),
address_pair_v6("::", "::"),
address_pair_v6("::1", "::1")
}; };
boost::asio::ip::address_v6::bytes_type ipv6_address = host.to_v6 ().to_bytes (); boost::asio::ip::address_v6::bytes_type ipv6_address = host.to_v6 ().to_bytes ();

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2020, The PurpleI2P Project * Copyright (c) 2013-2021, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@ -50,6 +50,11 @@ namespace util
MemoryPool (): m_Head (nullptr) {} MemoryPool (): m_Head (nullptr) {}
~MemoryPool () ~MemoryPool ()
{
CleanUp ();
}
void CleanUp ()
{ {
while (m_Head) while (m_Head)
{ {

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2020, The PurpleI2P Project * Copyright (c) 2013-2021, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@ -16,7 +16,7 @@
#define MAKE_VERSION_NUMBER(a,b,c) ((a*100+b)*100+c) #define MAKE_VERSION_NUMBER(a,b,c) ((a*100+b)*100+c)
#define I2PD_VERSION_MAJOR 2 #define I2PD_VERSION_MAJOR 2
#define I2PD_VERSION_MINOR 38 #define I2PD_VERSION_MINOR 39
#define I2PD_VERSION_MICRO 0 #define I2PD_VERSION_MICRO 0
#define I2PD_VERSION_PATCH 0 #define I2PD_VERSION_PATCH 0
#define I2PD_VERSION MAKE_VERSION(I2PD_VERSION_MAJOR, I2PD_VERSION_MINOR, I2PD_VERSION_MICRO) #define I2PD_VERSION MAKE_VERSION(I2PD_VERSION_MAJOR, I2PD_VERSION_MINOR, I2PD_VERSION_MICRO)
@ -30,7 +30,7 @@
#define I2P_VERSION_MAJOR 0 #define I2P_VERSION_MAJOR 0
#define I2P_VERSION_MINOR 9 #define I2P_VERSION_MINOR 9
#define I2P_VERSION_MICRO 50 #define I2P_VERSION_MICRO 51
#define I2P_VERSION_PATCH 0 #define I2P_VERSION_PATCH 0
#define I2P_VERSION MAKE_VERSION(I2P_VERSION_MAJOR, I2P_VERSION_MINOR, I2P_VERSION_MICRO) #define I2P_VERSION MAKE_VERSION(I2P_VERSION_MAJOR, I2P_VERSION_MINOR, I2P_VERSION_MICRO)
#define I2P_VERSION_NUMBER MAKE_VERSION_NUMBER(I2P_VERSION_MAJOR, I2P_VERSION_MINOR, I2P_VERSION_MICRO) #define I2P_VERSION_NUMBER MAKE_VERSION_NUMBER(I2P_VERSION_MAJOR, I2P_VERSION_MINOR, I2P_VERSION_MICRO)

View File

@ -470,6 +470,20 @@ namespace client
if (pos != std::string::npos) if (pos != std::string::npos)
addr = addr.substr(0, pos); // remove comments addr = addr.substr(0, pos); // remove comments
pos = name.find(".b32.i2p");
if (pos != std::string::npos)
{
LogPrint (eLogError, "Addressbook: skipped adding of b32 address: ", name);
continue;
}
pos = name.find(".i2p");
if (pos == std::string::npos)
{
LogPrint (eLogError, "Addressbook: malformed domain: ", name);
continue;
}
auto ident = std::make_shared<i2p::data::IdentityEx> (); auto ident = std::make_shared<i2p::data::IdentityEx> ();
if (!ident->FromBase64(addr)) { if (!ident->FromBase64(addr)) {
LogPrint (eLogError, "Addressbook: malformed address ", addr, " for ", name); LogPrint (eLogError, "Addressbook: malformed address ", addr, " for ", name);

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2020, The PurpleI2P Project * Copyright (c) 2013-2021, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@ -200,6 +200,8 @@ namespace client
for (auto& it: m_Destinations) for (auto& it: m_Destinations)
it.second->Stop (); it.second->Stop ();
m_Destinations.clear (); m_Destinations.clear ();
m_SharedLocalDestination->Release ();
m_SharedLocalDestination = nullptr; m_SharedLocalDestination = nullptr;
} }
@ -209,14 +211,6 @@ namespace client
/*std::string config; i2p::config::GetOption("conf", config); /*std::string config; i2p::config::GetOption("conf", config);
i2p::config::ParseConfig(config);*/ i2p::config::ParseConfig(config);*/
// handle tunnels
// reset isUpdated for each tunnel
VisitTunnels ([](I2PService * s)->bool { s->isUpdated = false; return true; });
// reload tunnels
ReadTunnels();
// delete not updated tunnels (not in config anymore)
VisitTunnels ([](I2PService * s)->bool { return s->isUpdated; });
// change shared local destination // change shared local destination
m_SharedLocalDestination->Release (); m_SharedLocalDestination->Release ();
CreateNewSharedLocalDestination (); CreateNewSharedLocalDestination ();
@ -225,6 +219,7 @@ namespace client
if (m_HttpProxy) if (m_HttpProxy)
{ {
m_HttpProxy->Stop (); m_HttpProxy->Stop ();
delete m_HttpProxy;
m_HttpProxy = nullptr; m_HttpProxy = nullptr;
} }
ReadHttpProxy (); ReadHttpProxy ();
@ -233,10 +228,19 @@ namespace client
if (m_SocksProxy) if (m_SocksProxy)
{ {
m_SocksProxy->Stop (); m_SocksProxy->Stop ();
delete m_SocksProxy;
m_SocksProxy = nullptr; m_SocksProxy = nullptr;
} }
ReadSocksProxy (); ReadSocksProxy ();
// handle tunnels
// reset isUpdated for each tunnel
VisitTunnels (false);
// reload tunnels
ReadTunnels();
// delete not updated tunnels (not in config anymore)
VisitTunnels (true);
// delete unused destinations // delete unused destinations
std::unique_lock<std::mutex> l(m_DestinationsMutex); std::unique_lock<std::mutex> l(m_DestinationsMutex);
for (auto it = m_Destinations.begin (); it != m_Destinations.end ();) for (auto it = m_Destinations.begin (); it != m_Destinations.end ();)
@ -457,7 +461,7 @@ namespace client
options[I2CP_PARAM_STREAMING_INITIAL_ACK_DELAY] = GetI2CPOption(section, I2CP_PARAM_STREAMING_INITIAL_ACK_DELAY, DEFAULT_INITIAL_ACK_DELAY); options[I2CP_PARAM_STREAMING_INITIAL_ACK_DELAY] = GetI2CPOption(section, I2CP_PARAM_STREAMING_INITIAL_ACK_DELAY, DEFAULT_INITIAL_ACK_DELAY);
options[I2CP_PARAM_STREAMING_ANSWER_PINGS] = GetI2CPOption(section, I2CP_PARAM_STREAMING_ANSWER_PINGS, isServer ? DEFAULT_ANSWER_PINGS : false); 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); options[I2CP_PARAM_LEASESET_TYPE] = GetI2CPOption(section, I2CP_PARAM_LEASESET_TYPE, DEFAULT_LEASESET_TYPE);
std::string encType = GetI2CPStringOption(section, I2CP_PARAM_LEASESET_ENCRYPTION_TYPE, isServer ? "" : "0,4"); std::string encType = GetI2CPStringOption(section, I2CP_PARAM_LEASESET_ENCRYPTION_TYPE, "0,4");
if (encType.length () > 0) options[I2CP_PARAM_LEASESET_ENCRYPTION_TYPE] = encType; if (encType.length () > 0) options[I2CP_PARAM_LEASESET_ENCRYPTION_TYPE] = encType;
std::string privKey = GetI2CPStringOption(section, I2CP_PARAM_LEASESET_PRIV_KEY, ""); std::string privKey = GetI2CPStringOption(section, I2CP_PARAM_LEASESET_PRIV_KEY, "");
if (privKey.length () > 0) options[I2CP_PARAM_LEASESET_PRIV_KEY] = privKey; if (privKey.length () > 0) options[I2CP_PARAM_LEASESET_PRIV_KEY] = privKey;
@ -504,20 +508,15 @@ namespace client
int numClientTunnels = 0, numServerTunnels = 0; int numClientTunnels = 0, numServerTunnels = 0;
std::string tunConf; i2p::config::GetOption("tunconf", tunConf); std::string tunConf; i2p::config::GetOption("tunconf", tunConf);
if (tunConf.empty ()) if (tunConf.empty ())
{
// TODO: cleanup this in 2.8.0
tunConf = i2p::fs::DataDirPath ("tunnels.cfg");
if (i2p::fs::Exists(tunConf))
LogPrint(eLogWarning, "Clients: please rename tunnels.cfg -> tunnels.conf here: ", tunConf);
else
tunConf = i2p::fs::DataDirPath ("tunnels.conf"); tunConf = i2p::fs::DataDirPath ("tunnels.conf");
}
LogPrint(eLogDebug, "Clients: tunnels config file: ", tunConf); LogPrint(eLogDebug, "Clients: tunnels config file: ", tunConf);
ReadTunnels (tunConf, numClientTunnels, numServerTunnels); ReadTunnels (tunConf, numClientTunnels, numServerTunnels);
std::string tunDir; i2p::config::GetOption("tunnelsdir", tunDir); std::string tunDir; i2p::config::GetOption("tunnelsdir", tunDir);
if (tunDir.empty ()) if (tunDir.empty ())
tunDir = i2p::fs::DataDirPath ("tunnels.d"); tunDir = i2p::fs::DataDirPath ("tunnels.d");
if (i2p::fs::Exists (tunDir)) if (i2p::fs::Exists (tunDir))
{ {
std::vector<std::string> files; std::vector<std::string> files;
@ -609,10 +608,18 @@ namespace client
bool gzip = section.second.get (I2P_CLIENT_TUNNEL_GZIP, true); bool gzip = section.second.get (I2P_CLIENT_TUNNEL_GZIP, true);
auto clientTunnel = std::make_shared<I2PUDPClientTunnel>(name, dest, end, localDestination, destinationPort, gzip); auto clientTunnel = std::make_shared<I2PUDPClientTunnel>(name, dest, end, localDestination, destinationPort, gzip);
if(m_ClientForwards.insert(std::make_pair(end, clientTunnel)).second)
auto ins = m_ClientForwards.insert(std::make_pair(end, clientTunnel));
if (ins.second)
{
clientTunnel->Start(); clientTunnel->Start();
numClientTunnels++;
}
else else
{
ins.first->second->isUpdated = true;
LogPrint(eLogError, "Clients: I2P Client forward for endpoint ", end, " already exists"); LogPrint(eLogError, "Clients: I2P Client forward for endpoint ", end, " already exists");
}
} else { } else {
boost::asio::ip::tcp::endpoint clientEndpoint; boost::asio::ip::tcp::endpoint clientEndpoint;
@ -645,6 +652,13 @@ namespace client
auto tun = std::make_shared<I2PClientTunnel> (name, dest, address, port, localDestination, destinationPort); auto tun = std::make_shared<I2PClientTunnel> (name, dest, address, port, localDestination, destinationPort);
clientTunnel = tun; clientTunnel = tun;
clientEndpoint = tun->GetLocalEndpoint (); clientEndpoint = tun->GetLocalEndpoint ();
uint32_t keepAlive = section.second.get<uint32_t>(I2P_CLIENT_TUNNEL_KEEP_ALIVE_INTERVAL, 0);
if (keepAlive)
{
tun->SetKeepAliveInterval (keepAlive);
LogPrint(eLogInfo, "Clients: I2P Client tunnel keep alive interval set to ", keepAlive);
}
} }
uint32_t timeout = section.second.get<uint32_t>(I2P_CLIENT_TUNNEL_CONNECT_TIMEOUT, 0); uint32_t timeout = section.second.get<uint32_t>(I2P_CLIENT_TUNNEL_CONNECT_TIMEOUT, 0);
@ -666,13 +680,16 @@ namespace client
if (ins.first->second->GetLocalDestination () != clientTunnel->GetLocalDestination ()) if (ins.first->second->GetLocalDestination () != clientTunnel->GetLocalDestination ())
{ {
LogPrint (eLogInfo, "Clients: I2P client tunnel destination updated"); LogPrint (eLogInfo, "Clients: I2P client tunnel destination updated");
ins.first->second->Stop ();
ins.first->second->SetLocalDestination (clientTunnel->GetLocalDestination ()); ins.first->second->SetLocalDestination (clientTunnel->GetLocalDestination ());
ins.first->second->Start ();
} }
ins.first->second->isUpdated = true; ins.first->second->isUpdated = true;
LogPrint (eLogInfo, "Clients: I2P client tunnel for endpoint ", clientEndpoint, " already exists"); LogPrint (eLogInfo, "Clients: I2P client tunnel for endpoint ", clientEndpoint, " already exists");
} }
} }
} }
else if (type == I2P_TUNNELS_SECTION_TYPE_SERVER else if (type == I2P_TUNNELS_SECTION_TYPE_SERVER
|| type == I2P_TUNNELS_SECTION_TYPE_HTTP || type == I2P_TUNNELS_SECTION_TYPE_HTTP
|| type == I2P_TUNNELS_SECTION_TYPE_IRC || type == I2P_TUNNELS_SECTION_TYPE_IRC
@ -689,7 +706,7 @@ namespace client
accessList=section.second.get (I2P_SERVER_TUNNEL_WHITE_LIST, ""); accessList=section.second.get (I2P_SERVER_TUNNEL_WHITE_LIST, "");
std::string hostOverride = section.second.get (I2P_SERVER_TUNNEL_HOST_OVERRIDE, ""); std::string hostOverride = section.second.get (I2P_SERVER_TUNNEL_HOST_OVERRIDE, "");
std::string webircpass = section.second.get<std::string> (I2P_SERVER_TUNNEL_WEBIRC_PASSWORD, ""); std::string webircpass = section.second.get<std::string> (I2P_SERVER_TUNNEL_WEBIRC_PASSWORD, "");
bool gzip = section.second.get (I2P_SERVER_TUNNEL_GZIP, true); bool gzip = section.second.get (I2P_SERVER_TUNNEL_GZIP, false);
i2p::data::SigningKeyType sigType = section.second.get (I2P_SERVER_TUNNEL_SIGNATURE_TYPE, i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519); i2p::data::SigningKeyType sigType = section.second.get (I2P_SERVER_TUNNEL_SIGNATURE_TYPE, i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519);
i2p::data::CryptoKeyType cryptoType = section.second.get (I2P_CLIENT_TUNNEL_CRYPTO_TYPE, i2p::data::CRYPTO_KEY_TYPE_ELGAMAL); i2p::data::CryptoKeyType cryptoType = section.second.get (I2P_CLIENT_TUNNEL_CRYPTO_TYPE, i2p::data::CRYPTO_KEY_TYPE_ELGAMAL);
@ -703,7 +720,10 @@ namespace client
std::shared_ptr<ClientDestination> localDestination = nullptr; std::shared_ptr<ClientDestination> localDestination = nullptr;
auto it = destinations.find (keys); auto it = destinations.find (keys);
if (it != destinations.end ()) if (it != destinations.end ())
{
localDestination = it->second; localDestination = it->second;
localDestination->SetPublic (true);
}
else else
{ {
i2p::data::PrivateKeys k; i2p::data::PrivateKeys k;
@ -715,14 +735,22 @@ namespace client
localDestination = CreateNewLocalDestination (k, true, &options); localDestination = CreateNewLocalDestination (k, true, &options);
destinations[keys] = localDestination; destinations[keys] = localDestination;
} }
else
localDestination->SetPublic (true);
} }
if (type == I2P_TUNNELS_SECTION_TYPE_UDPSERVER) if (type == I2P_TUNNELS_SECTION_TYPE_UDPSERVER)
{ {
// udp server tunnel // udp server tunnel
// TODO: hostnames // TODO: hostnames
if (address.empty ()) address = "127.0.0.1";
auto localAddress = boost::asio::ip::address::from_string(address);
boost::asio::ip::udp::endpoint endpoint(boost::asio::ip::address::from_string(host), port); boost::asio::ip::udp::endpoint endpoint(boost::asio::ip::address::from_string(host), port);
if (address.empty ())
{
if (!endpoint.address ().is_unspecified () && endpoint.address ().is_v6 ())
address = "::1";
else
address = "127.0.0.1";
}
auto localAddress = boost::asio::ip::address::from_string(address);
auto serverTunnel = std::make_shared<I2PUDPServerTunnel>(name, localDestination, localAddress, endpoint, port, gzip); auto serverTunnel = std::make_shared<I2PUDPServerTunnel>(name, localDestination, localAddress, endpoint, port, gzip);
if(!isUniqueLocal) if(!isUniqueLocal)
{ {
@ -730,17 +758,19 @@ namespace client
serverTunnel->SetUniqueLocal(isUniqueLocal); serverTunnel->SetUniqueLocal(isUniqueLocal);
} }
std::lock_guard<std::mutex> lock(m_ForwardsMutex); std::lock_guard<std::mutex> lock(m_ForwardsMutex);
if(m_ServerForwards.insert( auto ins = m_ServerForwards.insert(std::make_pair(
std::make_pair( std::make_pair(localDestination->GetIdentHash(), port),
std::make_pair( serverTunnel));
localDestination->GetIdentHash(), port), if (ins.second)
serverTunnel)).second)
{ {
serverTunnel->Start(); serverTunnel->Start();
LogPrint(eLogInfo, "Clients: I2P Server Forward created for UDP Endpoint ", host, ":", port, " bound on ", address, " for ",localDestination->GetIdentHash().ToBase32()); LogPrint(eLogInfo, "Clients: I2P Server Forward created for UDP Endpoint ", host, ":", port, " bound on ", address, " for ",localDestination->GetIdentHash().ToBase32());
} }
else else
{
ins.first->second->isUpdated = true;
LogPrint(eLogError, "Clients: I2P Server Forward for destination/port ", m_AddressBook.ToAddress(localDestination->GetIdentHash()), "/", port, " already exists"); LogPrint(eLogError, "Clients: I2P Server Forward for destination/port ", m_AddressBook.ToAddress(localDestination->GetIdentHash()), "/", port, " already exists");
}
continue; continue;
} }
@ -789,7 +819,9 @@ namespace client
if (ins.first->second->GetLocalDestination () != serverTunnel->GetLocalDestination ()) if (ins.first->second->GetLocalDestination () != serverTunnel->GetLocalDestination ())
{ {
LogPrint (eLogInfo, "Clients: I2P server tunnel destination updated"); LogPrint (eLogInfo, "Clients: I2P server tunnel destination updated");
ins.first->second->Stop ();
ins.first->second->SetLocalDestination (serverTunnel->GetLocalDestination ()); ins.first->second->SetLocalDestination (serverTunnel->GetLocalDestination ());
ins.first->second->Start ();
} }
ins.first->second->isUpdated = true; ins.first->second->isUpdated = true;
LogPrint (eLogInfo, "Clients: I2P server tunnel for destination/port ", m_AddressBook.ToAddress(localDestination->GetIdentHash ()), "/", inPort, " already exists"); LogPrint (eLogInfo, "Clients: I2P server tunnel for destination/port ", m_AddressBook.ToAddress(localDestination->GetIdentHash ()), "/", inPort, " already exists");
@ -914,27 +946,52 @@ namespace client
} }
} }
template<typename Container, typename Visitor> void ClientContext::VisitTunnels (bool clean)
void VisitTunnelsContainer (Container& c, Visitor v)
{ {
for (auto it = c.begin (); it != c.end ();) for (auto it = m_ClientTunnels.begin (); it != m_ClientTunnels.end ();)
{
if (!v (it->second.get ()))
{ {
if(clean && !it->second->isUpdated) {
it->second->Stop (); it->second->Stop ();
it = c.erase (it); it = m_ClientTunnels.erase(it);
} } else {
else it->second->isUpdated = false;
it++; it++;
} }
} }
template<typename Visitor> for (auto it = m_ServerTunnels.begin (); it != m_ServerTunnels.end ();)
void ClientContext::VisitTunnels (Visitor v)
{ {
VisitTunnelsContainer (m_ClientTunnels, v); if(clean && !it->second->isUpdated) {
VisitTunnelsContainer (m_ServerTunnels, v); it->second->Stop ();
// TODO: implement UDP forwards it = m_ServerTunnels.erase(it);
} else {
it->second->isUpdated = false;
it++;
}
}
/* // TODO: Write correct UDP tunnels stop
for (auto it = m_ClientForwards.begin (); it != m_ClientForwards.end ();)
{
if(clean && !it->second->isUpdated) {
it->second = nullptr;
it = m_ClientForwards.erase(it);
} else {
it->second->isUpdated = false;
it++;
}
}
for (auto it = m_ServerForwards.begin (); it != m_ServerForwards.end ();)
{
if(clean && !it->second->isUpdated) {
it->second = nullptr;
it = m_ServerForwards.erase(it);
} else {
it->second->isUpdated = false;
it++;
}
} */
} }
} }
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2020, The PurpleI2P Project * Copyright (c) 2013-2021, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@ -22,6 +22,7 @@
#include "BOB.h" #include "BOB.h"
#include "I2CP.h" #include "I2CP.h"
#include "AddressBook.h" #include "AddressBook.h"
#include "I18N_langs.h"
namespace i2p namespace i2p
{ {
@ -47,6 +48,7 @@ namespace client
const char I2P_CLIENT_TUNNEL_DESTINATION_PORT[] = "destinationport"; const char I2P_CLIENT_TUNNEL_DESTINATION_PORT[] = "destinationport";
const char I2P_CLIENT_TUNNEL_MATCH_TUNNELS[] = "matchtunnels"; const char I2P_CLIENT_TUNNEL_MATCH_TUNNELS[] = "matchtunnels";
const char I2P_CLIENT_TUNNEL_CONNECT_TIMEOUT[] = "connecttimeout"; const char I2P_CLIENT_TUNNEL_CONNECT_TIMEOUT[] = "connecttimeout";
const char I2P_CLIENT_TUNNEL_KEEP_ALIVE_INTERVAL[] = "keepaliveinterval";
const char I2P_SERVER_TUNNEL_HOST[] = "host"; const char I2P_SERVER_TUNNEL_HOST[] = "host";
const char I2P_SERVER_TUNNEL_HOST_OVERRIDE[] = "hostoverride"; const char I2P_SERVER_TUNNEL_HOST_OVERRIDE[] = "hostoverride";
const char I2P_SERVER_TUNNEL_PORT[] = "port"; const char I2P_SERVER_TUNNEL_PORT[] = "port";
@ -102,6 +104,10 @@ namespace client
std::vector<std::shared_ptr<DatagramSessionInfo> > GetForwardInfosFor(const i2p::data::IdentHash & destination); std::vector<std::shared_ptr<DatagramSessionInfo> > GetForwardInfosFor(const i2p::data::IdentHash & destination);
// i18n
std::shared_ptr<const i2p::i18n::Locale> GetLanguage () { return m_Language; };
void SetLanguage (const std::shared_ptr<const i2p::i18n::Locale> language) { m_Language = language; };
private: private:
void ReadTunnels (); void ReadTunnels ();
@ -121,8 +127,7 @@ namespace client
void CleanupUDP(const boost::system::error_code & ecode); void CleanupUDP(const boost::system::error_code & ecode);
void ScheduleCleanupUDP(); void ScheduleCleanupUDP();
template<typename Visitor> void VisitTunnels (bool clean);
void VisitTunnels (Visitor v); // Visitor: (I2PService *) -> bool, true means retain
void CreateNewSharedLocalDestination (); void CreateNewSharedLocalDestination ();
void AddLocalDestination (std::shared_ptr<ClientDestination> localDestination); void AddLocalDestination (std::shared_ptr<ClientDestination> localDestination);
@ -150,6 +155,9 @@ namespace client
std::unique_ptr<boost::asio::deadline_timer> m_CleanupUDPTimer; std::unique_ptr<boost::asio::deadline_timer> m_CleanupUDPTimer;
// i18n
std::shared_ptr<const i2p::i18n::Locale> m_Language;
public: public:
// for HTTP // for HTTP

View File

@ -295,7 +295,7 @@ namespace proxy {
std::string full_url = m_RequestURL.to_string(); std::string full_url = m_RequestURL.to_string();
std::stringstream ss; std::stringstream ss;
ss << tr("Host") <<" " << m_RequestURL.host << " " << tr("added to router's addressbook from helper") << ". "; ss << tr("Host") <<" " << m_RequestURL.host << " " << tr("added to router's addressbook from helper") << ". ";
ss << tr("Click") << " <a href=\"" << full_url << "\">" << tr("here") << "</a> " << tr("to proceed") << "."; ss << tr("Click here to proceed:") << " <a href=\"" << full_url << "\">" << tr("Continue") << "</a>.";
GenericProxyInfo(tr("Addresshelper found"), ss.str()); GenericProxyInfo(tr("Addresshelper found"), ss.str());
return true; /* request processed */ return true; /* request processed */
} }
@ -304,8 +304,8 @@ namespace proxy {
std::string full_url = m_RequestURL.to_string(); std::string full_url = m_RequestURL.to_string();
std::stringstream ss; std::stringstream ss;
ss << tr("Host") << " " << m_RequestURL.host << " <font color=red>" << tr("already in router's addressbook") << "</font>. "; ss << tr("Host") << " " << m_RequestURL.host << " <font color=red>" << tr("already in router's addressbook") << "</font>. ";
ss << tr("Click") << " <a href=\"" << full_url << (full_url.find('?') != std::string::npos ? "&i2paddresshelper=" : "?i2paddresshelper="); ss << tr("Click here to update record:") << " <a href=\"" << full_url << (full_url.find('?') != std::string::npos ? "&i2paddresshelper=" : "?i2paddresshelper=");
ss << jump << "&update=true\">" << tr("here") << "</a> " << tr("to update record") << "."; ss << jump << "&update=true\">" << tr("Continue") << "</a>.";
GenericProxyInfo(tr("Addresshelper found"), ss.str()); GenericProxyInfo(tr("Addresshelper found"), ss.str());
return true; /* request processed */ return true; /* request processed */
} }
@ -319,7 +319,7 @@ namespace proxy {
auto pos = uri.find(":"); auto pos = uri.find(":");
if(pos == std::string::npos || pos == uri.size() - 1) if(pos == std::string::npos || pos == uri.size() - 1)
{ {
GenericProxyError(tr("Invalid Request"), tr("invalid request uri")); GenericProxyError(tr("Invalid request"), tr("invalid request uri"));
return true; return true;
} }
else else

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2020, The PurpleI2P Project * Copyright (c) 2013-2021, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@ -52,12 +52,12 @@ namespace client
} }
} }
bool I2CPDestination::Decrypt (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx, i2p::data::CryptoKeyType preferredCrypto) const bool I2CPDestination::Decrypt (const uint8_t * encrypted, uint8_t * data, i2p::data::CryptoKeyType preferredCrypto) const
{ {
if (preferredCrypto == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD && m_ECIESx25519Decryptor) if (preferredCrypto == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD && m_ECIESx25519Decryptor)
return m_ECIESx25519Decryptor->Decrypt (encrypted, data, ctx, true); return m_ECIESx25519Decryptor->Decrypt (encrypted, data);
if (m_Decryptor) if (m_Decryptor)
return m_Decryptor->Decrypt (encrypted, data, ctx, true); return m_Decryptor->Decrypt (encrypted, data);
else else
LogPrint (eLogError, "I2CP: decryptor is not set"); LogPrint (eLogError, "I2CP: decryptor is not set");
return false; return false;

View File

@ -83,7 +83,7 @@ namespace client
void SendMsgTo (const uint8_t * payload, size_t len, const i2p::data::IdentHash& ident, uint32_t nonce); // called from I2CPSession void SendMsgTo (const uint8_t * payload, size_t len, const i2p::data::IdentHash& ident, uint32_t nonce); // called from I2CPSession
// implements LocalDestination // implements LocalDestination
bool Decrypt (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx, i2p::data::CryptoKeyType preferredCrypto) const; bool Decrypt (const uint8_t * encrypted, uint8_t * data, i2p::data::CryptoKeyType preferredCrypto) const;
bool SupportsEncryptionType (i2p::data::CryptoKeyType keyType) const; bool SupportsEncryptionType (i2p::data::CryptoKeyType keyType) const;
const uint8_t * GetEncryptionPublicKey (i2p::data::CryptoKeyType keyType) const; // for 4 only const uint8_t * GetEncryptionPublicKey (i2p::data::CryptoKeyType keyType) const; // for 4 only
std::shared_ptr<const i2p::data::IdentityEx> GetIdentity () const { return m_Identity; }; std::shared_ptr<const i2p::data::IdentityEx> GetIdentity () const { return m_Identity; };

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2020, The PurpleI2P Project * Copyright (c) 2013-2021, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@ -532,7 +532,7 @@ namespace client
I2PClientTunnel::I2PClientTunnel (const std::string& name, const std::string& destination, I2PClientTunnel::I2PClientTunnel (const std::string& name, const std::string& destination,
const std::string& address, int port, std::shared_ptr<ClientDestination> localDestination, int destinationPort): const std::string& address, int port, std::shared_ptr<ClientDestination> localDestination, int destinationPort):
TCPIPAcceptor (address, port, localDestination), m_Name (name), m_Destination (destination), TCPIPAcceptor (address, port, localDestination), m_Name (name), m_Destination (destination),
m_DestinationPort (destinationPort) m_DestinationPort (destinationPort), m_KeepAliveInterval (0)
{ {
} }
@ -540,12 +540,22 @@ namespace client
{ {
TCPIPAcceptor::Start (); TCPIPAcceptor::Start ();
GetAddress (); GetAddress ();
if (m_KeepAliveInterval)
ScheduleKeepAliveTimer ();
} }
void I2PClientTunnel::Stop () void I2PClientTunnel::Stop ()
{ {
TCPIPAcceptor::Stop(); TCPIPAcceptor::Stop();
m_Address = nullptr; m_Address = nullptr;
if (m_KeepAliveTimer) m_KeepAliveTimer->cancel ();
}
void I2PClientTunnel::SetKeepAliveInterval (uint32_t keepAliveInterval)
{
m_KeepAliveInterval = keepAliveInterval;
if (m_KeepAliveInterval)
m_KeepAliveTimer.reset (new boost::asio::deadline_timer (GetLocalDestination ()->GetService ()));
} }
/* HACK: maybe we should create a caching IdentHash provider in AddressBook */ /* HACK: maybe we should create a caching IdentHash provider in AddressBook */
@ -569,6 +579,31 @@ namespace client
return nullptr; return nullptr;
} }
void I2PClientTunnel::ScheduleKeepAliveTimer ()
{
if (m_KeepAliveTimer)
{
m_KeepAliveTimer->expires_from_now (boost::posix_time::seconds(m_KeepAliveInterval));
m_KeepAliveTimer->async_wait (std::bind (&I2PClientTunnel::HandleKeepAliveTimer,
this, std::placeholders::_1));
}
}
void I2PClientTunnel::HandleKeepAliveTimer (const boost::system::error_code& ecode)
{
if (ecode != boost::asio::error::operation_aborted)
{
if (m_Address && m_Address->IsValid ())
{
if (m_Address->IsIdentHash ())
GetLocalDestination ()->SendPing (m_Address->identHash);
else
GetLocalDestination ()->SendPing (m_Address->blindedPublicKey);
}
ScheduleKeepAliveTimer ();
}
}
I2PServerTunnel::I2PServerTunnel (const std::string& name, const std::string& address, I2PServerTunnel::I2PServerTunnel (const std::string& name, const std::string& address,
int port, std::shared_ptr<ClientDestination> localDestination, int inport, bool gzip): int port, std::shared_ptr<ClientDestination> localDestination, int inport, bool gzip):
I2PService (localDestination), m_IsUniqueLocal(true), m_Name (name), m_Address (address), m_Port (port), m_IsAccessList (false) I2PService (localDestination), m_IsUniqueLocal(true), m_Name (name), m_Address (address), m_Port (port), m_IsAccessList (false)
@ -605,7 +640,47 @@ namespace client
{ {
if (!ecode) if (!ecode)
{ {
auto addr = (*it).endpoint ().address (); bool found = false;
boost::asio::ip::tcp::endpoint ep;
if (m_LocalAddress)
{
boost::asio::ip::tcp::resolver::iterator end;
while (it != end)
{
ep = *it;
if (!ep.address ().is_unspecified ())
{
if (ep.address ().is_v4 ())
{
if (m_LocalAddress->is_v4 ()) found = true;
}
else if (ep.address ().is_v6 ())
{
if (i2p::util::net::IsYggdrasilAddress (ep.address ()))
{
if (i2p::util::net::IsYggdrasilAddress (*m_LocalAddress))
found = true;
}
else if (m_LocalAddress->is_v6 ())
found = true;
}
}
if (found) break;
it++;
}
}
else
{
found = true;
ep = *it; // first available
}
if (!found)
{
LogPrint (eLogError, "I2PTunnel: Unable to resolve to compatible address");
return;
}
auto addr = ep.address ();
LogPrint (eLogInfo, "I2PTunnel: server tunnel ", (*it).host_name (), " has been resolved to ", addr); LogPrint (eLogInfo, "I2PTunnel: server tunnel ", (*it).host_name (), " has been resolved to ", addr);
m_Endpoint.address (addr); m_Endpoint.address (addr);
Accept (); Accept ();
@ -848,7 +923,8 @@ namespace client
LogPrint(eLogInfo, "UDPServer: done"); LogPrint(eLogInfo, "UDPServer: done");
} }
void I2PUDPServerTunnel::Start() { void I2PUDPServerTunnel::Start()
{
m_LocalDest->Start(); m_LocalDest->Start();
} }
@ -889,6 +965,7 @@ namespace client
m_cancel_resolve(false) m_cancel_resolve(false)
{ {
m_LocalSocket.set_option (boost::asio::socket_base::receive_buffer_size (I2P_UDP_MAX_MTU)); m_LocalSocket.set_option (boost::asio::socket_base::receive_buffer_size (I2P_UDP_MAX_MTU));
m_LocalSocket.set_option (boost::asio::socket_base::reuse_address (true));
auto dgram = m_LocalDest->CreateDatagramDestination(gzip); auto dgram = m_LocalDest->CreateDatagramDestination(gzip);
dgram->SetReceiver(std::bind(&I2PUDPClientTunnel::HandleRecvFromI2P, this, dgram->SetReceiver(std::bind(&I2PUDPClientTunnel::HandleRecvFromI2P, this,
@ -915,7 +992,8 @@ namespace client
void I2PUDPClientTunnel::HandleRecvFromLocal(const boost::system::error_code & ec, std::size_t transferred) void I2PUDPClientTunnel::HandleRecvFromLocal(const boost::system::error_code & ec, std::size_t transferred)
{ {
if(ec) { if(ec) {
LogPrint(eLogError, "UDP Client: ", ec.message()); LogPrint(eLogError, "UDP Client: Reading from socket error: ", ec.message(), ". Restarting listener...");
RecvFromLocal(); // Restart listener and continue work
return; return;
} }
if(!m_RemoteIdent) { if(!m_RemoteIdent) {
@ -1025,7 +1103,8 @@ namespace client
LogPrint(eLogWarning, "UDP Client: not tracking udp session using port ", (int) toPort); LogPrint(eLogWarning, "UDP Client: not tracking udp session using port ", (int) toPort);
} }
I2PUDPClientTunnel::~I2PUDPClientTunnel() { I2PUDPClientTunnel::~I2PUDPClientTunnel()
{
auto dgram = m_LocalDest->GetDatagramDestination(); auto dgram = m_LocalDest->GetDatagramDestination();
if (dgram) dgram->ResetReceiver(); if (dgram) dgram->ResetReceiver();

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