mirror of
https://github.com/PurpleI2P/i2pd.git
synced 2025-01-24 22:37:16 +01:00
commit
9685754511
63
.github/workflows/docker.yml
vendored
Normal file
63
.github/workflows/docker.yml
vendored
Normal 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
5
.gitignore
vendored
|
@ -7,8 +7,13 @@ netDb
|
|||
/i2pd
|
||||
/libi2pd.a
|
||||
/libi2pdclient.a
|
||||
/libi2pdlang.a
|
||||
/libi2pd.so
|
||||
/libi2pdclient.so
|
||||
/libi2pdlang.so
|
||||
/libi2pd.dll
|
||||
/libi2pdclient.dll
|
||||
/libi2pdlang.dll
|
||||
*.exe
|
||||
|
||||
|
||||
|
|
54
.travis.yml
54
.travis.yml
|
@ -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
|
44
ChangeLog
44
ChangeLog
|
@ -1,7 +1,49 @@
|
|||
# for this file format description,
|
||||
# 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
|
||||
- Publish ipv6 introducers
|
||||
- Bind ipv6 or yggdrasil NTCP2 acceptor to specified address
|
||||
|
|
77
Makefile
77
Makefile
|
@ -1,23 +1,37 @@
|
|||
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
|
||||
SHLIB_CLIENT := libi2pdclient.so
|
||||
SHLIB_LANG := libi2pdlang.$(SHARED_SUFFIX)
|
||||
ARLIB_LANG := libi2pdlang.a
|
||||
SHLIB_CLIENT := libi2pdclient.$(SHARED_SUFFIX)
|
||||
ARLIB_CLIENT := libi2pdclient.a
|
||||
SHLIB_WRAP := libi2pdwrapper.$(SHARED_SUFFIX)
|
||||
ARLIB_WRAP := libi2pdwrapper.a
|
||||
I2PD := i2pd
|
||||
|
||||
LIB_SRC_DIR := libi2pd
|
||||
LIB_CLIENT_SRC_DIR := libi2pd_client
|
||||
WRAP_SRC_DIR := libi2pd_wrapper
|
||||
LANG_SRC_DIR := i18n
|
||||
DAEMON_SRC_DIR := daemon
|
||||
|
||||
# import source files lists
|
||||
include filelist.mk
|
||||
|
||||
USE_AESNI := yes
|
||||
USE_STATIC := no
|
||||
USE_MESHNET := no
|
||||
USE_UPNP := no
|
||||
DEBUG := yes
|
||||
USE_AESNI := $(or $(USE_AESNI),yes)
|
||||
USE_STATIC := $(or $(USE_STATIC),no)
|
||||
USE_MESHNET := $(or $(USE_MESHNET),no)
|
||||
USE_UPNP := $(or $(USE_UPNP),no)
|
||||
DEBUG := $(or $(DEBUG),yes)
|
||||
|
||||
ifeq ($(DEBUG),yes)
|
||||
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))
|
||||
LANG_OBJS += $(patsubst %.cpp,obj/%.o,$(LANG_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:
|
||||
@mkdir -p obj
|
||||
@mkdir -p obj/Win32
|
||||
@mkdir -p obj/$(LIB_SRC_DIR)
|
||||
@mkdir -p obj/$(LIB_CLIENT_SRC_DIR)
|
||||
@mkdir -p obj/$(LANG_SRC_DIR)
|
||||
@mkdir -p obj/$(DAEMON_SRC_DIR)
|
||||
@mkdir -p obj/$(WRAP_SRC_DIR)
|
||||
@mkdir -p obj/Win32
|
||||
|
||||
api: mk_obj_dir $(SHLIB) $(ARLIB)
|
||||
client: mk_obj_dir $(SHLIB_CLIENT) $(ARLIB_CLIENT)
|
||||
api_client: mk_obj_dir $(SHLIB) $(ARLIB) $(SHLIB_CLIENT) $(ARLIB_CLIENT)
|
||||
api: $(SHLIB) $(ARLIB)
|
||||
client: $(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
|
||||
## **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
|
||||
## custom FLAGS to work at build-time.
|
||||
|
||||
obj/%.o: %.cpp
|
||||
obj/%.o: %.cpp | mk_obj_dir
|
||||
$(CXX) $(CXXFLAGS) $(NEEDED_CXXFLAGS) $(INCFLAGS) -c -o $@ $<
|
||||
|
||||
# '-' is 'ignore if missing' on first run
|
||||
-include $(DEPS)
|
||||
|
||||
$(I2PD): $(LANG_OBJS) $(DAEMON_OBJS) $(ARLIB) $(ARLIB_CLIENT)
|
||||
$(I2PD): $(DAEMON_OBJS) $(ARLIB) $(ARLIB_CLIENT) $(ARLIB_LANG)
|
||||
$(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)
|
||||
$(CXX) $(LDFLAGS) -shared -o $@ $^ $(LDLIBS)
|
||||
endif
|
||||
|
||||
$(SHLIB_CLIENT): $(LIB_CLIENT_OBJS)
|
||||
$(SHLIB_LANG): $(LANG_OBJS)
|
||||
ifneq ($(USE_STATIC),yes)
|
||||
$(CXX) $(LDFLAGS) -shared -o $@ $^ $(LDLIBS) $(SHLIB)
|
||||
$(CXX) $(LDFLAGS) -shared -o $@ $^ $(LDLIBS)
|
||||
endif
|
||||
|
||||
$(ARLIB): $(LIB_OBJS)
|
||||
|
@ -104,12 +132,18 @@ $(ARLIB): $(LIB_OBJS)
|
|||
$(ARLIB_CLIENT): $(LIB_CLIENT_OBJS)
|
||||
$(AR) -r $@ $^
|
||||
|
||||
$(ARLIB_WRAP): $(WRAP_LIB_OBJS)
|
||||
$(AR) -r $@ $^
|
||||
|
||||
$(ARLIB_LANG): $(LANG_OBJS)
|
||||
$(AR) -r $@ $^
|
||||
|
||||
clean:
|
||||
$(RM) -r obj
|
||||
$(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 $^
|
||||
|
||||
LATEST_TAG=$(shell git describe --tags --abbrev=0 openssl)
|
||||
|
@ -133,6 +167,7 @@ doxygen:
|
|||
.PHONY: api
|
||||
.PHONY: api_client
|
||||
.PHONY: client
|
||||
.PHONY: lang
|
||||
.PHONY: mk_obj_dir
|
||||
.PHONY: install
|
||||
.PHONY: strip
|
||||
|
|
|
@ -3,9 +3,9 @@ USE_WIN32_APP := yes
|
|||
|
||||
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
|
||||
LDFLAGS := ${LD_DEBUG} -Wl,-Bstatic -static-libgcc
|
||||
LDFLAGS := ${LD_DEBUG} -static
|
||||
|
||||
# detect proper flag for c++11 support by compilers
|
||||
CXXVER := $(shell $(CXX) -dumpversion)
|
||||
|
@ -61,5 +61,5 @@ ifeq ($(USE_ASLR),yes)
|
|||
LDFLAGS += -Wl,--nxcompat -Wl,--high-entropy-va -Wl,--dynamicbase,--export-all-symbols
|
||||
endif
|
||||
|
||||
obj/%.o : %.rc
|
||||
obj/%.o : %.rc | mk_obj_dir
|
||||
$(WINDRES) -i $< -o $@
|
||||
|
|
13
README.md
13
README.md
|
@ -3,6 +3,7 @@
|
|||
[![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)
|
||||
[![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*
|
||||
|
||||
|
@ -73,7 +74,7 @@ Build instructions:
|
|||
* 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)
|
||||
* 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)
|
||||
* 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)
|
||||
|
@ -85,6 +86,16 @@ Using i2pd
|
|||
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).
|
||||
|
||||
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
|
||||
---------
|
||||
|
||||
|
|
57
appveyor.yml
57
appveyor.yml
|
@ -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
1
build/.gitignore
vendored
|
@ -3,6 +3,7 @@
|
|||
/i2pd
|
||||
/libi2pd.a
|
||||
/libi2pdclient.a
|
||||
/libi2pdlang.a
|
||||
/cmake_install.cmake
|
||||
/CMakeCache.txt
|
||||
/CPackConfig.cmake
|
||||
|
|
|
@ -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_SOURCE_DIR "..")
|
||||
|
||||
#Handle paths nicely
|
||||
include(GNUInstallDirs)
|
||||
|
||||
# architecture
|
||||
include(TargetArch)
|
||||
target_architecture(ARCHITECTURE)
|
||||
|
@ -48,8 +51,8 @@ set_target_properties(libi2pd PROPERTIES PREFIX "")
|
|||
if(WITH_LIBRARY)
|
||||
install(TARGETS libi2pd
|
||||
EXPORT libi2pd
|
||||
ARCHIVE DESTINATION lib
|
||||
LIBRARY DESTINATION lib
|
||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
COMPONENT Libraries)
|
||||
# TODO Make libi2pd available to 3rd party projects via CMake as imported target
|
||||
# FIXME This pulls stdafx
|
||||
|
@ -63,12 +66,22 @@ set_target_properties(libi2pdclient PROPERTIES PREFIX "")
|
|||
if(WITH_LIBRARY)
|
||||
install(TARGETS libi2pdclient
|
||||
EXPORT libi2pdclient
|
||||
ARCHIVE DESTINATION lib
|
||||
LIBRARY DESTINATION lib
|
||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
COMPONENT Libraries)
|
||||
endif()
|
||||
|
||||
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
|
||||
"${DAEMON_SRC_DIR}/Daemon.cpp"
|
||||
|
@ -198,10 +211,11 @@ if(WITH_PCH)
|
|||
)
|
||||
target_compile_options(libi2pd 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)
|
||||
endif()
|
||||
|
||||
target_link_libraries(libi2pdclient libi2pd)
|
||||
target_link_libraries(libi2pdclient libi2pd libi2pdlang)
|
||||
|
||||
find_package(Boost COMPONENTS system filesystem program_options date_time REQUIRED)
|
||||
if(NOT DEFINED Boost_INCLUDE_DIRS)
|
||||
|
@ -261,11 +275,8 @@ message(STATUS " ADDRSANITIZER : ${WITH_ADDRSANITIZER}")
|
|||
message(STATUS " THREADSANITIZER : ${WITH_THREADSANITIZER}")
|
||||
message(STATUS "---------------------------------------")
|
||||
|
||||
#Handle paths nicely
|
||||
include(GNUInstallDirs)
|
||||
|
||||
if(WITH_BINARY)
|
||||
add_executable("${PROJECT_NAME}" ${LANG_SRC} ${DAEMON_SRC})
|
||||
add_executable("${PROJECT_NAME}" ${DAEMON_SRC})
|
||||
|
||||
if(WITH_STATIC)
|
||||
set_target_properties("${PROJECT_NAME}" PROPERTIES LINK_FLAGS "-static")
|
||||
|
@ -295,7 +306,7 @@ if(WITH_BINARY)
|
|||
endif()
|
||||
|
||||
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)
|
||||
set(APPS "\${CMAKE_INSTALL_PREFIX}/bin/${PROJECT_NAME}${CMAKE_EXECUTABLE_SUFFIX}")
|
||||
|
|
|
@ -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. :)
|
||||
|
||||
|
||||
|
|
@ -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
|
|
@ -1,2 +0,0 @@
|
|||
i2pd:
|
||||
build: .
|
32
contrib/certificates/reseed/orignal_at_mail.i2p.crt
Normal file
32
contrib/certificates/reseed/orignal_at_mail.i2p.crt
Normal 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-----
|
|
@ -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-----
|
|
@ -25,7 +25,8 @@ RUN mkdir -p "$I2PD_HOME" "$DATA_DIR" \
|
|||
# 1. install deps, clone and build.
|
||||
# 2. strip binaries.
|
||||
# 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 \
|
||||
&& cd /tmp/build && git clone -b ${GIT_BRANCH} ${REPO_URL} \
|
||||
&& cd i2pd \
|
||||
|
|
|
@ -7,7 +7,7 @@ msgid ""
|
|||
msgstr ""
|
||||
"Project-Id-Version: i2pd\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"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
@ -18,559 +18,567 @@ msgstr ""
|
|||
"X-Poedit-SearchPath-0: daemon/HTTPServer.cpp\n"
|
||||
"X-Poedit-SearchPath-1: libi2pd_client/HTTPProxy.cpp\n"
|
||||
|
||||
#: daemon/HTTPServer.cpp:85
|
||||
msgid "Disabled"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:86
|
||||
msgid "Enabled"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:141
|
||||
#: daemon/HTTPServer.cpp:177
|
||||
msgid "day"
|
||||
msgid_plural "days"
|
||||
msgstr[0] ""
|
||||
msgstr[1] ""
|
||||
#: daemon/HTTPServer.cpp:145
|
||||
|
||||
#: daemon/HTTPServer.cpp:181
|
||||
msgid "hour"
|
||||
msgid_plural "hours"
|
||||
msgstr[0] ""
|
||||
msgstr[1] ""
|
||||
#: daemon/HTTPServer.cpp:149
|
||||
|
||||
#: daemon/HTTPServer.cpp:185
|
||||
msgid "minute"
|
||||
msgid_plural "minutes"
|
||||
msgstr[0] ""
|
||||
msgstr[1] ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:152
|
||||
#: daemon/HTTPServer.cpp:188
|
||||
msgid "second"
|
||||
msgid_plural "seconds"
|
||||
msgstr[0] ""
|
||||
msgstr[1] ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:160 daemon/HTTPServer.cpp:188
|
||||
#. tr: Kibibit
|
||||
#: daemon/HTTPServer.cpp:196 daemon/HTTPServer.cpp:224
|
||||
msgid "KiB"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:162
|
||||
#. tr: Mebibit
|
||||
#: daemon/HTTPServer.cpp:198
|
||||
msgid "MiB"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:164
|
||||
#. tr: Gibibit
|
||||
#: daemon/HTTPServer.cpp:200
|
||||
msgid "GiB"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:181
|
||||
#: daemon/HTTPServer.cpp:217
|
||||
msgid "building"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:182
|
||||
#: daemon/HTTPServer.cpp:218
|
||||
msgid "failed"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:183
|
||||
#: daemon/HTTPServer.cpp:219
|
||||
msgid "expiring"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:184
|
||||
#: daemon/HTTPServer.cpp:220
|
||||
msgid "established"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:185
|
||||
#: daemon/HTTPServer.cpp:221
|
||||
msgid "unknown"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:187
|
||||
#: daemon/HTTPServer.cpp:223
|
||||
msgid "exploratory"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:223
|
||||
#: daemon/HTTPServer.cpp:259
|
||||
msgid "<b>i2pd</b> webconsole"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:226
|
||||
#: daemon/HTTPServer.cpp:262
|
||||
msgid "Main page"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:227 daemon/HTTPServer.cpp:683
|
||||
#: daemon/HTTPServer.cpp:263 daemon/HTTPServer.cpp:725
|
||||
msgid "Router commands"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:228
|
||||
msgid "Local destinations"
|
||||
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
|
||||
#: daemon/HTTPServer.cpp:264 daemon/HTTPServer.cpp:448
|
||||
#: daemon/HTTPServer.cpp:460
|
||||
msgid "Local Destinations"
|
||||
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"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:451
|
||||
#: daemon/HTTPServer.cpp:492
|
||||
msgid "Address registration line"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:456
|
||||
#: daemon/HTTPServer.cpp:497
|
||||
msgid "Domain"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:457
|
||||
#: daemon/HTTPServer.cpp:498
|
||||
msgid "Generate"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:458
|
||||
#: daemon/HTTPServer.cpp:499
|
||||
msgid ""
|
||||
"<b>Note:</b> result string can be used only for registering 2LD domains "
|
||||
"(example.i2p). For registering subdomains please use i2pd-tools."
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:464
|
||||
#: daemon/HTTPServer.cpp:505
|
||||
msgid "Address"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:464
|
||||
#: daemon/HTTPServer.cpp:505
|
||||
msgid "Type"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:464
|
||||
#: daemon/HTTPServer.cpp:505
|
||||
msgid "EncType"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:474 daemon/HTTPServer.cpp:657
|
||||
#: daemon/HTTPServer.cpp:515 daemon/HTTPServer.cpp:699
|
||||
msgid "Inbound tunnels"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:479 daemon/HTTPServer.cpp:489
|
||||
#: daemon/HTTPServer.cpp:662 daemon/HTTPServer.cpp:672
|
||||
#: Means milliseconds
|
||||
#. tr: Milliseconds
|
||||
#: daemon/HTTPServer.cpp:520 daemon/HTTPServer.cpp:530
|
||||
#: daemon/HTTPServer.cpp:704 daemon/HTTPServer.cpp:714
|
||||
msgid "ms"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:484 daemon/HTTPServer.cpp:667
|
||||
#: daemon/HTTPServer.cpp:525 daemon/HTTPServer.cpp:709
|
||||
msgid "Outbound tunnels"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:496
|
||||
#: daemon/HTTPServer.cpp:537
|
||||
msgid "Tags"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:496
|
||||
#: daemon/HTTPServer.cpp:537
|
||||
msgid "Incoming"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:503 daemon/HTTPServer.cpp:506
|
||||
#: daemon/HTTPServer.cpp:544 daemon/HTTPServer.cpp:547
|
||||
msgid "Outgoing"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:504 daemon/HTTPServer.cpp:520
|
||||
#: daemon/HTTPServer.cpp:545 daemon/HTTPServer.cpp:561
|
||||
msgid "Destination"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:504
|
||||
#: daemon/HTTPServer.cpp:545
|
||||
msgid "Amount"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:511
|
||||
#: daemon/HTTPServer.cpp:552
|
||||
msgid "Incoming Tags"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:519 daemon/HTTPServer.cpp:522
|
||||
#: daemon/HTTPServer.cpp:560 daemon/HTTPServer.cpp:563
|
||||
msgid "Tags sessions"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:520
|
||||
#: daemon/HTTPServer.cpp:561
|
||||
msgid "Status"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:529 daemon/HTTPServer.cpp:584
|
||||
#: daemon/HTTPServer.cpp:570 daemon/HTTPServer.cpp:626
|
||||
msgid "Local Destination"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:538 daemon/HTTPServer.cpp:887
|
||||
#: daemon/HTTPServer.cpp:580 daemon/HTTPServer.cpp:947
|
||||
msgid "Streams"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:560
|
||||
#: daemon/HTTPServer.cpp:602
|
||||
msgid "Close stream"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:589
|
||||
#: daemon/HTTPServer.cpp:631
|
||||
msgid "I2CP session not found"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:592
|
||||
#: daemon/HTTPServer.cpp:634
|
||||
msgid "I2CP is not enabled"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:618
|
||||
#: daemon/HTTPServer.cpp:660
|
||||
msgid "Invalid"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:621
|
||||
#: daemon/HTTPServer.cpp:663
|
||||
msgid "Store type"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:622
|
||||
#: daemon/HTTPServer.cpp:664
|
||||
msgid "Expires"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:627
|
||||
#: daemon/HTTPServer.cpp:669
|
||||
msgid "Non Expired Leases"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:630
|
||||
#: daemon/HTTPServer.cpp:672
|
||||
msgid "Gateway"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:631
|
||||
#: daemon/HTTPServer.cpp:673
|
||||
msgid "TunnelID"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:632
|
||||
#: daemon/HTTPServer.cpp:674
|
||||
msgid "EndDate"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:642
|
||||
#: daemon/HTTPServer.cpp:684
|
||||
msgid "not floodfill"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:653
|
||||
#: daemon/HTTPServer.cpp:695
|
||||
msgid "Queue size"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:684
|
||||
#: daemon/HTTPServer.cpp:726
|
||||
msgid "Run peer test"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:687
|
||||
#: daemon/HTTPServer.cpp:731
|
||||
msgid "Decline transit tunnels"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:689
|
||||
#: daemon/HTTPServer.cpp:733
|
||||
msgid "Accept transit tunnels"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:692 daemon/HTTPServer.cpp:697
|
||||
#: daemon/HTTPServer.cpp:737 daemon/HTTPServer.cpp:742
|
||||
msgid "Cancel graceful shutdown"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:694 daemon/HTTPServer.cpp:699
|
||||
#: daemon/HTTPServer.cpp:739 daemon/HTTPServer.cpp:744
|
||||
msgid "Start graceful shutdown"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:701
|
||||
#: daemon/HTTPServer.cpp:747
|
||||
msgid "Force shutdown"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:704
|
||||
#: daemon/HTTPServer.cpp:748
|
||||
msgid "Reload external CSS styles"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:751
|
||||
msgid ""
|
||||
"<b>Note:</b> any action done here are not persistent and not changes your "
|
||||
"config files."
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:706
|
||||
#: daemon/HTTPServer.cpp:753
|
||||
msgid "Logging level"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:714
|
||||
#: daemon/HTTPServer.cpp:761
|
||||
msgid "Transit tunnels limit"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:719
|
||||
#: daemon/HTTPServer.cpp:766 daemon/HTTPServer.cpp:778
|
||||
msgid "Change"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:743
|
||||
#: daemon/HTTPServer.cpp:770
|
||||
msgid "Change language"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:803
|
||||
msgid "no transit tunnels currently built"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:848 daemon/HTTPServer.cpp:871
|
||||
#: daemon/HTTPServer.cpp:908 daemon/HTTPServer.cpp:931
|
||||
msgid "SAM disabled"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:864
|
||||
#: daemon/HTTPServer.cpp:924
|
||||
msgid "no sessions currently running"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:877
|
||||
#: daemon/HTTPServer.cpp:937
|
||||
msgid "SAM session not found"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:882
|
||||
#: daemon/HTTPServer.cpp:942
|
||||
msgid "SAM Session"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:939
|
||||
#: daemon/HTTPServer.cpp:999
|
||||
msgid "Server Tunnels"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:955
|
||||
#: daemon/HTTPServer.cpp:1015
|
||||
msgid "Client Forwards"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:969
|
||||
#: daemon/HTTPServer.cpp:1029
|
||||
msgid "Server Forwards"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:1175
|
||||
#: daemon/HTTPServer.cpp:1227
|
||||
msgid "Unknown page"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:1194
|
||||
#: daemon/HTTPServer.cpp:1246
|
||||
msgid "Invalid token"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:1252 daemon/HTTPServer.cpp:1309
|
||||
#: daemon/HTTPServer.cpp:1337
|
||||
#: daemon/HTTPServer.cpp:1304 daemon/HTTPServer.cpp:1361
|
||||
#: daemon/HTTPServer.cpp:1401
|
||||
msgid "SUCCESS"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:1252
|
||||
#: daemon/HTTPServer.cpp:1304
|
||||
msgid "Stream closed"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:1254
|
||||
#: daemon/HTTPServer.cpp:1306
|
||||
msgid "Stream not found or already was closed"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:1257
|
||||
#: daemon/HTTPServer.cpp:1309
|
||||
msgid "Destination not found"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:1260
|
||||
#: daemon/HTTPServer.cpp:1312
|
||||
msgid "StreamID can't be null"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:1262 daemon/HTTPServer.cpp:1327
|
||||
#: daemon/HTTPServer.cpp:1314 daemon/HTTPServer.cpp:1379
|
||||
msgid "Return to destination page"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:1263 daemon/HTTPServer.cpp:1276
|
||||
msgid "You will be redirected back in 5 seconds"
|
||||
#: daemon/HTTPServer.cpp:1315 daemon/HTTPServer.cpp:1328
|
||||
#: daemon/HTTPServer.cpp:1403
|
||||
msgid "You will be redirected in 5 seconds"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:1274
|
||||
#: daemon/HTTPServer.cpp:1326
|
||||
msgid "Transit tunnels count must not exceed 65535"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:1275 daemon/HTTPServer.cpp:1338
|
||||
#: daemon/HTTPServer.cpp:1327 daemon/HTTPServer.cpp:1402
|
||||
msgid "Back to commands list"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:1311
|
||||
#: daemon/HTTPServer.cpp:1363
|
||||
msgid "Register at reg.i2p"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:1312
|
||||
#: daemon/HTTPServer.cpp:1364
|
||||
msgid "Description"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:1312
|
||||
#: daemon/HTTPServer.cpp:1364
|
||||
msgid "A bit information about service on domain"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:1313
|
||||
#: daemon/HTTPServer.cpp:1365
|
||||
msgid "Submit"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:1319
|
||||
#: daemon/HTTPServer.cpp:1371
|
||||
msgid "Domain can't end with .b32.i2p"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:1322
|
||||
#: daemon/HTTPServer.cpp:1374
|
||||
msgid "Domain must end with .i2p"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:1325
|
||||
#: daemon/HTTPServer.cpp:1377
|
||||
msgid "Such destination is not found"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:1333
|
||||
#: daemon/HTTPServer.cpp:1397
|
||||
msgid "Unknown command"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:1337
|
||||
#: daemon/HTTPServer.cpp:1401
|
||||
msgid "Command accepted"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:1339
|
||||
msgid "You will be redirected in 5 seconds"
|
||||
msgstr ""
|
||||
|
||||
#: libi2pd_client/HTTPProxy.cpp:157
|
||||
msgid "Proxy error"
|
||||
msgstr ""
|
||||
|
@ -592,7 +600,7 @@ msgid "You may try to find this host on jump services below"
|
|||
msgstr ""
|
||||
|
||||
#: 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"
|
||||
msgstr ""
|
||||
|
||||
|
@ -613,16 +621,12 @@ msgstr ""
|
|||
msgid "added to router's addressbook from helper"
|
||||
msgstr ""
|
||||
|
||||
#: libi2pd_client/HTTPProxy.cpp:298 libi2pd_client/HTTPProxy.cpp:307
|
||||
msgid "Click"
|
||||
#: libi2pd_client/HTTPProxy.cpp:298
|
||||
msgid "Click here to proceed:"
|
||||
msgstr ""
|
||||
|
||||
#: libi2pd_client/HTTPProxy.cpp:298 libi2pd_client/HTTPProxy.cpp:308
|
||||
msgid "here"
|
||||
msgstr ""
|
||||
|
||||
#: libi2pd_client/HTTPProxy.cpp:298
|
||||
msgid "to proceed"
|
||||
msgid "Continue"
|
||||
msgstr ""
|
||||
|
||||
#: libi2pd_client/HTTPProxy.cpp:299 libi2pd_client/HTTPProxy.cpp:309
|
||||
|
@ -633,12 +637,8 @@ msgstr ""
|
|||
msgid "already in router's addressbook"
|
||||
msgstr ""
|
||||
|
||||
#: libi2pd_client/HTTPProxy.cpp:308
|
||||
msgid "to update record"
|
||||
msgstr ""
|
||||
|
||||
#: libi2pd_client/HTTPProxy.cpp:322
|
||||
msgid "Invalid Request"
|
||||
#: libi2pd_client/HTTPProxy.cpp:307
|
||||
msgid "Click here to update record:"
|
||||
msgstr ""
|
||||
|
||||
#: libi2pd_client/HTTPProxy.cpp:322
|
||||
|
|
29
contrib/i18n/README.md
Normal file
29
contrib/i18n/README.md
Normal 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
|
||||
```
|
|
@ -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
|
|
@ -15,6 +15,10 @@
|
|||
## Default: ~/.i2pd/tunnels.d or /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)
|
||||
# pidfile = /run/i2pd.pid
|
||||
|
||||
|
@ -104,7 +108,7 @@ port = 7070
|
|||
# user = i2pd
|
||||
# pass = changeme
|
||||
## 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
|
||||
|
||||
[httpproxy]
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
%define git_hash %(git rev-parse HEAD | cut -c -7)
|
||||
|
||||
Name: i2pd-git
|
||||
Version: 2.38.0
|
||||
Version: 2.39.0
|
||||
Release: git%{git_hash}%{?dist}
|
||||
Summary: I2P router written in C++
|
||||
Conflicts: i2pd
|
||||
|
@ -56,9 +56,14 @@ cd build
|
|||
%endif
|
||||
%endif
|
||||
|
||||
|
||||
%if 0%{?fedora} >= 35
|
||||
pushd redhat-linux-build
|
||||
%else
|
||||
%if 0%{?fedora} >= 33
|
||||
pushd %{_target_platform}
|
||||
%endif
|
||||
%endif
|
||||
|
||||
%if 0%{?mageia} > 7
|
||||
pushd build
|
||||
|
@ -77,9 +82,13 @@ popd
|
|||
%install
|
||||
pushd build
|
||||
|
||||
%if 0%{?fedora} >= 35
|
||||
pushd redhat-linux-build
|
||||
%else
|
||||
%if 0%{?fedora} >= 33
|
||||
pushd %{_target_platform}
|
||||
%endif
|
||||
%endif
|
||||
|
||||
%if 0%{?mageia}
|
||||
pushd build
|
||||
|
@ -137,6 +146,13 @@ getent passwd i2pd >/dev/null || \
|
|||
|
||||
|
||||
%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
|
||||
- update to 2.38.0
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
Name: i2pd
|
||||
Version: 2.38.0
|
||||
Release: 1%{?dist}
|
||||
Version: 2.39.0
|
||||
Release: 2%{?dist}
|
||||
Summary: I2P router written in C++
|
||||
Conflicts: i2pd-git
|
||||
|
||||
|
@ -54,9 +54,13 @@ cd build
|
|||
%endif
|
||||
%endif
|
||||
|
||||
%if 0%{?fedora} >= 35
|
||||
pushd redhat-linux-build
|
||||
%else
|
||||
%if 0%{?fedora} >= 33
|
||||
pushd %{_target_platform}
|
||||
%endif
|
||||
%endif
|
||||
|
||||
%if 0%{?mageia} > 7
|
||||
pushd build
|
||||
|
@ -75,9 +79,13 @@ popd
|
|||
%install
|
||||
pushd build
|
||||
|
||||
%if 0%{?fedora} >= 35
|
||||
pushd redhat-linux-build
|
||||
%else
|
||||
%if 0%{?fedora} >= 33
|
||||
pushd %{_target_platform}
|
||||
%endif
|
||||
%endif
|
||||
|
||||
%if 0%{?mageia}
|
||||
pushd build
|
||||
|
@ -135,6 +143,13 @@ getent passwd i2pd >/dev/null || \
|
|||
|
||||
|
||||
%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
|
||||
- update to 2.38.0
|
||||
|
||||
|
|
|
@ -1,16 +1,16 @@
|
|||
[IRC-IRC2P]
|
||||
[IRC-ILITA]
|
||||
type = client
|
||||
address = 127.0.0.1
|
||||
port = 6668
|
||||
destination = irc.postman.i2p
|
||||
destination = irc.ilita.i2p
|
||||
destinationport = 6667
|
||||
keys = irc-keys.dat
|
||||
|
||||
#[IRC-ILITA]
|
||||
#[IRC-IRC2P]
|
||||
#type = client
|
||||
#address = 127.0.0.1
|
||||
#port = 6669
|
||||
#destination = irc.ilita.i2p
|
||||
#destination = irc.postman.i2p
|
||||
#destinationport = 6667
|
||||
#keys = irc-keys.dat
|
||||
|
||||
|
|
243
contrib/webconsole/style.css
Normal file
243
contrib/webconsole/style.css
Normal 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;
|
||||
}
|
||||
}
|
|
@ -94,6 +94,11 @@ namespace util
|
|||
|
||||
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 logfile = ""; i2p::config::GetOption("logfile", logfile);
|
||||
std::string loglevel = ""; i2p::config::GetOption("loglevel", loglevel);
|
||||
|
@ -132,6 +137,7 @@ namespace util
|
|||
LogPrint(eLogNone, "i2pd v", VERSION, " starting");
|
||||
LogPrint(eLogDebug, "FS: main config file: ", config);
|
||||
LogPrint(eLogDebug, "FS: data directory: ", datadir);
|
||||
LogPrint(eLogDebug, "FS: certificates directory: ", certsdir);
|
||||
|
||||
bool precomputation; i2p::config::GetOption("precomputation.elgamal", precomputation);
|
||||
bool aesni; i2p::config::GetOption("cpuext.aesni", aesni);
|
||||
|
|
|
@ -36,73 +36,31 @@
|
|||
#include "Win32App.h"
|
||||
#endif
|
||||
|
||||
// For image and info
|
||||
// For image, style and info
|
||||
#include "version.h"
|
||||
#include "HTTPServerResources.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==";
|
||||
static void LoadExtCSS ()
|
||||
{
|
||||
std::stringstream s;
|
||||
std::string styleFile = i2p::fs::DataDirPath ("webconsole/style.css");
|
||||
if (i2p::fs::Exists(styleFile)) {
|
||||
std::ifstream f(styleFile, std::ifstream::binary);
|
||||
s << f.rdbuf();
|
||||
externalCSS = s.str();
|
||||
} else if (externalCSS.length() != 0) { // clean up external style if file was removed
|
||||
externalCSS = "";
|
||||
}
|
||||
}
|
||||
|
||||
static void GetStyles (std::stringstream& s)
|
||||
{
|
||||
s << "<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"
|
||||
<< " 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";
|
||||
if (externalCSS.length() != 0)
|
||||
s << "<style>\r\n" << externalCSS << "</style>\r\n";
|
||||
else
|
||||
s << internalCSS;
|
||||
}
|
||||
|
||||
const char HTTP_PAGE_TUNNELS[] = "tunnels";
|
||||
|
@ -127,11 +85,20 @@ namespace http {
|
|||
const char HTTP_COMMAND_KILLSTREAM[] = "closestream";
|
||||
const char HTTP_COMMAND_LIMITTRANSIT[] = "limittransit";
|
||||
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_ADDRESS[] = "address";
|
||||
|
||||
static std::string ConvertTime (uint64_t time);
|
||||
std::map<uint32_t, uint32_t> HTTPConnection::m_Tokens;
|
||||
static 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;
|
||||
}
|
||||
|
||||
static void ShowUptime (std::stringstream& s, int seconds)
|
||||
{
|
||||
|
@ -157,11 +124,11 @@ namespace http {
|
|||
s << std::fixed << std::setprecision(2);
|
||||
auto numKBytes = (double) bytes / 1024;
|
||||
if (numKBytes < 1024)
|
||||
s << numKBytes << " " << tr("KiB");
|
||||
s << numKBytes << " " << tr(/* tr: Kibibit */ "KiB");
|
||||
else if (numKBytes < 1024 * 1024)
|
||||
s << numKBytes / 1024 << " " << tr("MiB");
|
||||
s << numKBytes / 1024 << " " << tr(/* tr: Mebibit */ "MiB");
|
||||
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)
|
||||
|
@ -185,7 +152,7 @@ namespace http {
|
|||
else stateText = tr("unknown");
|
||||
|
||||
s << "<span class=\"tunnel " << state << "\"> " << stateText << ((explr) ? " (" + tr("exploratory") + ")" : "") << "</span>, ";
|
||||
s << " " << (int) (bytes / 1024) << " " << tr("KiB") << "\r\n";
|
||||
s << " " << (int) (bytes / 1024) << " " << tr(/* tr: Kibibit */ "KiB") << "\r\n";
|
||||
}
|
||||
|
||||
static void SetLogLevel (const std::string& level)
|
||||
|
@ -204,9 +171,9 @@ namespace http {
|
|||
std::string webroot; i2p::config::GetOption("http.webroot", webroot);
|
||||
|
||||
// Page language
|
||||
std::string lang, langCode; i2p::config::GetOption("http.lang", lang);
|
||||
if (lang == "russian") langCode = "ru";
|
||||
else langCode = "en";
|
||||
std::string currLang = i2p::client::context.GetLanguage ()->GetLanguage(); // get current used language
|
||||
auto it = i2p::i18n::languages.find(currLang);
|
||||
std::string langCode = it->second.ShortCode;
|
||||
|
||||
s <<
|
||||
"<!DOCTYPE html>\r\n"
|
||||
|
@ -223,18 +190,18 @@ namespace http {
|
|||
"<div class=\"header\">" << tr("<b>i2pd</b> webconsole") << "</div>\r\n"
|
||||
"<div class=\"wrapper\">\r\n"
|
||||
"<div class=\"menu\">\r\n"
|
||||
" <a href=\"" << webroot << "\">" << tr("Main page") << "</a><br>\r\n"
|
||||
" <a href=\"" << webroot << "?page=" << HTTP_PAGE_COMMANDS << "\">" << tr("Router commands") << "</a>\r\n"
|
||||
" <a href=\"" << webroot << "?page=" << HTTP_PAGE_LOCAL_DESTINATIONS << "\">" << tr("Local destinations") << "</a>\r\n";
|
||||
" <a href=\"" << webroot << "\">" << tr("Main page") << "</a><br><br>\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><br>\r\n";
|
||||
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 <<
|
||||
" <a href=\"" << webroot << "?page=" << HTTP_PAGE_TUNNELS << "\">" << tr("Tunnels") << "</a>\r\n"
|
||||
" <a href=\"" << webroot << "?page=" << HTTP_PAGE_TRANSIT_TUNNELS << "\">" << tr("Transit tunnels") << "</a>\r\n"
|
||||
" <a href=\"" << webroot << "?page=" << HTTP_PAGE_TRANSPORTS << "\">" << tr ("Transports") << "</a>\r\n"
|
||||
" <a href=\"" << webroot << "?page=" << HTTP_PAGE_I2P_TUNNELS << "\">" << tr("I2P 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><br>\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><br>\r\n";
|
||||
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 <<
|
||||
"</div>\r\n"
|
||||
"<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("Received") << ":</b> ";
|
||||
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> ";
|
||||
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> ";
|
||||
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 << "<div class='slide'>";
|
||||
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";
|
||||
|
||||
if(outputFormat==OutputFormatEnum::forWebConsole) {
|
||||
bool i2pcontrol; i2p::config::GetOption("i2pcontrol.enabled", i2pcontrol);
|
||||
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);
|
||||
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>" << "SOCKS " << tr("Proxy") << "</td><td><div class='" << ((i2p::client::context.GetSocksProxy ()) ? "enabled" : "disabled") << "'></div></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>" << "SAM" << "</td><td><div class='" << ((i2p::client::context.GetSAMBridge ()) ? "enabled" : "disabled") << "'></div></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>" << "I2PControl" << "</td><td><div class='" << ((i2pcontrol) ? "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 class='" << (socksproxy ? "enabled" : "disabled") << "'>" << (socksproxy ? tr("Enabled") : tr("Disabled")) << "</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 class='" << (sam ? "enabled" : "disabled") << "'>" << (sam ? tr("Enabled") : tr("Disabled")) << "</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 class='" << (i2pcontrol ? "enabled" : "disabled") << "'>" << (i2pcontrol ? tr("Enabled") : tr("Disabled")) << "</td></tr>\r\n";
|
||||
s << "</tbody></table>\r\n";
|
||||
}
|
||||
}
|
||||
|
@ -476,7 +448,7 @@ namespace http {
|
|||
s << "<div class=\"listitem\">";
|
||||
it->Print(s);
|
||||
if(it->LatencyIsKnown())
|
||||
s << " ( " << it->GetMeanLatency() << tr("ms") << " )";
|
||||
s << " ( " << it->GetMeanLatency() << tr(/* tr: Milliseconds */ "ms") << " )";
|
||||
ShowTunnelDetails(s, it->GetState (), false, it->GetNumReceivedBytes ());
|
||||
s << "</div>\r\n";
|
||||
}
|
||||
|
@ -486,7 +458,7 @@ namespace http {
|
|||
s << "<div class=\"listitem\">";
|
||||
it->Print(s);
|
||||
if(it->LatencyIsKnown())
|
||||
s << " ( " << it->GetMeanLatency() << tr("ms") << " )";
|
||||
s << " ( " << it->GetMeanLatency() << tr("ms") << " )";
|
||||
ShowTunnelDetails(s, it->GetState (), false, it->GetNumSentBytes ());
|
||||
s << "</div>\r\n";
|
||||
}
|
||||
|
@ -534,7 +506,8 @@ namespace http {
|
|||
if (dest)
|
||||
{
|
||||
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 << "<th style=\"width:25px;\">StreamID</th>";
|
||||
s << "<th style=\"width:5px;\" \\>"; // Stream closing button column
|
||||
|
@ -679,26 +652,31 @@ namespace http {
|
|||
static void ShowCommands (std::stringstream& s, uint32_t token)
|
||||
{
|
||||
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 << " <a href=\"" << webroot << "?cmd=" << HTTP_COMMAND_RUN_PEER_TEST << "&token=" << token << "\">" << tr("Run peer test") << "</a>\r\n";
|
||||
//s << " <a href=\"/?cmd=" << HTTP_COMMAND_RELOAD_CONFIG << "\">Reload config</a><br>\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";
|
||||
|
||||
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
|
||||
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 (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
|
||||
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)
|
||||
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
|
||||
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
|
||||
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 << "<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 << " <button type=\"submit\">" << tr("Change") << "</button>\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)
|
||||
{
|
||||
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 ())
|
||||
{
|
||||
s << "<div class=\"listitem\">\r\n";
|
||||
|
@ -740,7 +731,7 @@ namespace http {
|
|||
}
|
||||
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):
|
||||
m_Socket (socket), m_BufferLen (0), expected_host(hostname)
|
||||
{
|
||||
|
@ -1115,6 +1096,8 @@ namespace http {
|
|||
SendReply (res, content);
|
||||
}
|
||||
|
||||
std::map<uint32_t, uint32_t> HTTPConnection::m_Tokens;
|
||||
|
||||
uint32_t HTTPConnection::CreateToken ()
|
||||
{
|
||||
uint32_t token;
|
||||
|
@ -1260,7 +1243,7 @@ namespace http {
|
|||
s << "<b>" << tr("ERROR") << "</b>: " << 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 << "<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;
|
||||
res.add_header("Refresh", redirect.c_str());
|
||||
return;
|
||||
|
@ -1273,7 +1256,7 @@ namespace http {
|
|||
else {
|
||||
s << "<b>" << tr("ERROR") << "</b>: " << 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 << "<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());
|
||||
return;
|
||||
}
|
||||
|
@ -1281,7 +1264,7 @@ namespace http {
|
|||
else if (cmd == HTTP_COMMAND_GET_REG_STRING)
|
||||
{
|
||||
std::string b32 = params["b32"];
|
||||
std::string name = params["name"];
|
||||
std::string name = i2p::http::UrlDecode(params["name"]);
|
||||
|
||||
i2p::data::IdentHash ident;
|
||||
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";
|
||||
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
|
||||
{
|
||||
res.code = 400;
|
||||
|
@ -1389,6 +1384,8 @@ namespace http {
|
|||
m_Thread.reset (new std::thread (std::bind (&HTTPServer::Run, this)));
|
||||
m_Acceptor.listen ();
|
||||
Accept ();
|
||||
|
||||
LoadExtCSS();
|
||||
}
|
||||
|
||||
void HTTPServer::Stop ()
|
||||
|
|
89
daemon/HTTPServerResources.h
Normal file
89
daemon/HTTPServerResources.h
Normal 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__ */
|
|
@ -406,7 +406,7 @@ namespace client
|
|||
|
||||
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)
|
||||
|
|
6
debian/changelog
vendored
6
debian/changelog
vendored
|
@ -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
|
||||
|
||||
* updated to version 2.38.0/0.9.50
|
||||
|
|
14
debian/patches/02-upnp.patch
vendored
14
debian/patches/02-upnp.patch
vendored
|
@ -6,12 +6,12 @@ Last-Update: 2021-01-16
|
|||
|
||||
--- i2pd.orig/Makefile
|
||||
+++ i2pd/Makefile
|
||||
@@ -15,7 +15,7 @@ include filelist.mk
|
||||
USE_AESNI := yes
|
||||
USE_STATIC := no
|
||||
USE_MESHNET := no
|
||||
-USE_UPNP := no
|
||||
+USE_UPNP := yes
|
||||
DEBUG := yes
|
||||
@@ -21,7 +21,7 @@ include filelist.mk
|
||||
USE_AESNI := $(or $(USE_AESNI),yes)
|
||||
USE_STATIC := $(or $(USE_STATIC),no)
|
||||
USE_MESHNET := $(or $(USE_MESHNET),no)
|
||||
-USE_UPNP := $(or $(USE_UPNP),no)
|
||||
+USE_UPNP := $(or $(USE_UPNP),yes)
|
||||
DEBUG := $(or $(DEBUG),yes)
|
||||
|
||||
ifeq ($(DEBUG),yes)
|
||||
|
|
|
@ -21,4 +21,6 @@ LIB_CLIENT_SRC = $(wildcard $(LIB_CLIENT_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)
|
||||
|
|
|
@ -13,14 +13,16 @@
|
|||
#include "I18N.h"
|
||||
|
||||
// Afrikaans localization file
|
||||
// This is an example translation file without strings in it.
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace i18n
|
||||
{
|
||||
namespace afrikaans // language
|
||||
namespace afrikaans // language namespace
|
||||
{
|
||||
// language name in lowercase
|
||||
static std::string language = "afrikaans";
|
||||
|
||||
// See for language plural forms here:
|
||||
// https://localization-guide.readthedocs.io/en/latest/l10n/pluralforms.html
|
||||
static int plural (int n) {
|
||||
|
@ -29,12 +31,9 @@ namespace afrikaans // language
|
|||
|
||||
static std::map<std::string, std::string> strings
|
||||
{
|
||||
{"Disabled", "Gedeaktiveer"},
|
||||
{"Enabled", "Geaktiveer"},
|
||||
{"failed", "Het misluk"},
|
||||
{"unknown", "onbekend"},
|
||||
{"Tunnels", "Tonnels"},
|
||||
{"Transit tunnels", "Deurgang tonnels"},
|
||||
{"I2P tunnels", "I2P tonnels"},
|
||||
{"SAM sessions", "SAM sessies"},
|
||||
{"OK", "LEKKER"},
|
||||
|
@ -52,6 +51,14 @@ namespace afrikaans // language
|
|||
{"Hidden content. Press on text to see.", "Hidden content. Druk om te sien."},
|
||||
{"Router Ident", "Router Ident"},
|
||||
{"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()
|
||||
{
|
||||
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
|
||||
|
|
|
@ -19,8 +19,11 @@ namespace i2p
|
|||
{
|
||||
namespace i18n
|
||||
{
|
||||
namespace english // language
|
||||
namespace english // language namespace
|
||||
{
|
||||
// language name in lowercase
|
||||
static std::string language = "english";
|
||||
|
||||
// See for language plural forms here:
|
||||
// https://localization-guide.readthedocs.io/en/latest/l10n/pluralforms.html
|
||||
static int plural (int n) {
|
||||
|
@ -39,7 +42,7 @@ namespace english // language
|
|||
|
||||
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
|
||||
|
|
21
i18n/I18N.h
21
i18n/I18N.h
|
@ -9,7 +9,7 @@
|
|||
#ifndef __I18N_H__
|
||||
#define __I18N_H__
|
||||
|
||||
#include "RouterContext.h"
|
||||
#include "ClientContext.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
|
@ -17,26 +17,21 @@ namespace i18n
|
|||
{
|
||||
inline void SetLanguage(const std::string &lang)
|
||||
{
|
||||
if (!lang.compare("afrikaans"))
|
||||
i2p::context.SetLanguage (i2p::i18n::afrikaans::GetLocale());
|
||||
else if (!lang.compare("russian"))
|
||||
i2p::context.SetLanguage (i2p::i18n::russian::GetLocale());
|
||||
else if (!lang.compare("turkmen"))
|
||||
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());
|
||||
const auto it = i2p::i18n::languages.find(lang);
|
||||
if (it == i2p::i18n::languages.end()) // fallback
|
||||
i2p::client::context.SetLanguage (i2p::i18n::english::GetLocale());
|
||||
else
|
||||
i2p::client::context.SetLanguage (it->second.LocaleFunc());
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
return i2p::context.GetLanguage ()->GetPlural (arg, arg2, n);
|
||||
return i2p::client::context.GetLanguage ()->GetPlural (arg, arg2, n);
|
||||
}
|
||||
} // i18n
|
||||
} // i2p
|
||||
|
|
|
@ -17,10 +17,17 @@ namespace i18n
|
|||
{
|
||||
public:
|
||||
Locale (
|
||||
const std::string& language,
|
||||
const std::map<std::string, std::string>& strings,
|
||||
const std::map<std::string, std::vector<std::string>>& plurals,
|
||||
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
|
||||
{
|
||||
|
@ -50,17 +57,39 @@ namespace i18n
|
|||
}
|
||||
|
||||
private:
|
||||
const std::string m_Language;
|
||||
const std::map<std::string, std::string> m_Strings;
|
||||
const std::map<std::string, std::vector<std::string>> m_Plurals;
|
||||
std::function<int(int)> m_Formula;
|
||||
};
|
||||
|
||||
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
|
||||
namespace afrikaans { 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 turkmen { 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
|
||||
} // i2p
|
||||
|
|
312
i18n/Russian.cpp
312
i18n/Russian.cpp
|
@ -18,8 +18,11 @@ namespace i2p
|
|||
{
|
||||
namespace i18n
|
||||
{
|
||||
namespace russian // language
|
||||
namespace russian // language namespace
|
||||
{
|
||||
// language name in lowercase
|
||||
static std::string language = "russian";
|
||||
|
||||
// See for language plural forms here:
|
||||
// https://localization-guide.readthedocs.io/en/latest/l10n/pluralforms.html
|
||||
static int plural (int n) {
|
||||
|
@ -28,23 +31,148 @@ namespace russian // language
|
|||
|
||||
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 info", "Информация прокси"},
|
||||
{"Proxy error: Host not found", "Ошибка прокси: Адрес не найден"},
|
||||
{"Remote host not found in router's addressbook", "Запрошенный адрес не найден в адресной книге роутера"},
|
||||
{"You may try to find this host on jump services below", "Вы можете попробовать найти адрес на джамп сервисах ниже"},
|
||||
{"Proxy error: Host not found", "Ошибка прокси: Узел не найден"},
|
||||
{"Remote host not found in router's addressbook", "Запрошенный узел не найден в адресной книге роутера"},
|
||||
{"You may try to find this host on jump services below", "Вы можете попробовать найти узел через джамп сервисы ниже"},
|
||||
{"Invalid request", "Некорректный запрос"},
|
||||
{"Proxy unable to parse your request", "Прокси не может разобрать ваш запрос"},
|
||||
{"addresshelper is not supported", "addresshelper не поддерживается"},
|
||||
{"Host", "Адрес"},
|
||||
{"Host", "Узел"},
|
||||
{"added to router's addressbook from helper", "добавлен в адресную книгу роутера через хелпер"},
|
||||
{"already in router's addressbook", "уже в адресной книге роутера"},
|
||||
{"Click", "Нажмите"},
|
||||
{"here", "здесь"},
|
||||
{"to proceed", "чтобы продолжить"},
|
||||
{"to update record", "чтобы обновить запись"},
|
||||
{"Click here to proceed:", "Нажмите здесь, чтобы продолжить:"},
|
||||
{"Continue", "Продолжить"},
|
||||
{"Addresshelper found", "Найден addresshelper"},
|
||||
{"already in router's addressbook", "уже в адресной книге роутера"},
|
||||
{"Click here to update record:", "Нажмите здесь, чтобы обновить запись:"},
|
||||
{"invalid request uri", "некорректный URI запроса"},
|
||||
{"Can't detect destination host from request", "Не удалось определить адрес назначения из запроса"},
|
||||
{"Outproxy failure", "Ошибка внешнего прокси"},
|
||||
|
@ -63,169 +191,13 @@ namespace russian // language
|
|||
{"cannot connect", "не удалось подключиться"},
|
||||
{"http out proxy not implemented", "поддержка внешнего HTTP прокси сервера не реализована"},
|
||||
{"cannot connect to upstream http proxy", "не удалось подключиться к вышестоящему HTTP прокси серверу"},
|
||||
{"Host is down", "Адрес недоступен"},
|
||||
{"Can't create connection to requested host, it may be down. Please try again later.",
|
||||
"Не удалось установить соединение к запрошенному адресу, возможно он не в сети. Попробуйте повторить запрос позже."},
|
||||
|
||||
// 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", "Такая точка назначения не найдена"},
|
||||
{"Host is down", "Узел недоступен"},
|
||||
{"Can't create connection to requested host, it may be down. Please try again later.", "Не удалось установить соединение к запрошенному узлу, возможно он не в сети. Попробуйте повторить запрос позже."},
|
||||
{"", ""},
|
||||
};
|
||||
|
||||
static std::map<std::string, std::vector<std::string>> plurals
|
||||
{
|
||||
// ShowUptime
|
||||
{"days", {"день", "дня", "дней"}},
|
||||
{"hours", {"час", "часа", "часов"}},
|
||||
{"minutes", {"минуту", "минуты", "минут"}},
|
||||
|
@ -235,7 +207,7 @@ namespace russian // language
|
|||
|
||||
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
|
||||
|
|
300
i18n/Turkmen.cpp
300
i18n/Turkmen.cpp
|
@ -18,8 +18,11 @@ namespace i2p
|
|||
{
|
||||
namespace i18n
|
||||
{
|
||||
namespace turkmen // language
|
||||
namespace turkmen // language namespace
|
||||
{
|
||||
// language name in lowercase
|
||||
static std::string language = "turkmen";
|
||||
|
||||
// See for language plural forms here:
|
||||
// https://localization-guide.readthedocs.io/en/latest/l10n/pluralforms.html
|
||||
static int plural (int n) {
|
||||
|
@ -28,7 +31,133 @@ namespace turkmen // language
|
|||
|
||||
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 info", "Proksi maglumat"},
|
||||
{"Proxy error: Host not found", "Proksi ýalňyşlygy: Host tapylmady"},
|
||||
|
@ -39,12 +168,11 @@ namespace turkmen // language
|
|||
{"addresshelper is not supported", "Salgylandyryjy goldanok"},
|
||||
{"Host", "Adres"},
|
||||
{"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", "Basyň"},
|
||||
{"here", "bu ýerde"},
|
||||
{"to proceed", "dowam etmek"},
|
||||
{"to update record", "recordazgyny täzelemek üçin"},
|
||||
{"Click here to proceed:", "Dowam etmek bu ýerde basyň:"},
|
||||
{"Continue", "Dowam et"},
|
||||
{"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"},
|
||||
{"Can't detect destination host from request", "Haýyşdan barmaly ýerini tapyp bilemok"},
|
||||
{"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"},
|
||||
{"cannot connect to upstream http proxy", "ýokary akym HTTP proksi serwerine birigip bilmedi"},
|
||||
{"Host is down", "Salgy elýeterli däl"},
|
||||
{"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ň."},
|
||||
|
||||
// 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"},
|
||||
{"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ň."},
|
||||
{"", ""},
|
||||
};
|
||||
|
||||
|
@ -234,7 +208,7 @@ namespace turkmen // language
|
|||
|
||||
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
|
||||
|
|
|
@ -18,8 +18,11 @@ namespace i2p
|
|||
{
|
||||
namespace i18n
|
||||
{
|
||||
namespace ukrainian // language
|
||||
namespace ukrainian // language namespace
|
||||
{
|
||||
// language name in lowercase
|
||||
static std::string language = "ukrainian";
|
||||
|
||||
// See for language plural forms here:
|
||||
// https://localization-guide.readthedocs.io/en/latest/l10n/pluralforms.html
|
||||
static int plural (int n) {
|
||||
|
@ -28,23 +31,148 @@ namespace ukrainian // language
|
|||
|
||||
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 info", "Інформація проксі"},
|
||||
{"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", "Ви можете спробувати знайти дану адресу на джамп сервісах нижче"},
|
||||
{"Invalid request", "Некоректний запит"},
|
||||
{"Proxy unable to parse your request", "Проксі не може розібрати ваш запит"},
|
||||
{"addresshelper is not supported", "addresshelper не підтримується"},
|
||||
{"Host", "Адреса"},
|
||||
{"added to router's addressbook from helper", "доданий в адресну книгу роутера через хелпер"},
|
||||
{"already in router's addressbook", "вже в адресній книзі роутера"},
|
||||
{"Click", "Натисніть"},
|
||||
{"here", "тут"},
|
||||
{"to proceed", "щоб продовжити"},
|
||||
{"to update record", "щоб оновити запис"},
|
||||
{"added to router's addressbook from helper", "доданий в адресну книгу маршрутизатора через хелпер"},
|
||||
{"Click here to proceed:", "Натисніть тут щоб продовжити:"},
|
||||
{"Continue", "Продовжити"},
|
||||
{"Addresshelper found", "Знайдено addresshelper"},
|
||||
{"already in router's addressbook", "вже в адресній книзі маршрутизатора"},
|
||||
{"Click here to update record:", "Натисніть тут щоб оновити запис:"},
|
||||
{"invalid request uri", "некоректний URI запиту"},
|
||||
{"Can't detect destination host from request", "Не вдалось визначити адресу призначення з запиту"},
|
||||
{"Outproxy failure", "Помилка зовнішнього проксі"},
|
||||
|
@ -64,169 +192,14 @@ namespace ukrainian // language
|
|||
{"http out proxy not implemented", "підтримка зовнішнього HTTP проксі сервера не реалізована"},
|
||||
{"cannot connect to upstream http proxy", "не вдалося підключитися до висхідного HTTP проксі сервера"},
|
||||
{"Host is down", "Вузол недоступний"},
|
||||
{"Can't create connection to requested host, it may be down. Please try again later.",
|
||||
"Не вдалося встановити з'єднання до запитаного вузла, можливо він не в мережі. Спробуйте повторити запит пізніше."},
|
||||
|
||||
// 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", "Така точка призначення не знайдена"},
|
||||
{"Can't create connection to requested host, it may be down. Please try again later.", "Не вдалося встановити з'єднання до запитаного вузла, можливо він не в мережі. Спробуйте повторити запит пізніше."},
|
||||
{"", ""},
|
||||
};
|
||||
|
||||
static std::map<std::string, std::vector<std::string>> plurals
|
||||
{
|
||||
// ShowUptime
|
||||
{"days", {"день", "дня", "днів"}},
|
||||
{"hours", {"годину", "години", "годин"}},
|
||||
{"days", {"день", "дня", "днів"}},
|
||||
{"hours", {"годину", "години", "годин"}},
|
||||
{"minutes", {"хвилину", "хвилини", "хвилин"}},
|
||||
{"seconds", {"секунду", "секунди", "секунд"}},
|
||||
{"", {"", "", ""}},
|
||||
|
@ -234,7 +207,7 @@ namespace ukrainian // language
|
|||
|
||||
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
|
||||
|
|
206
i18n/Uzbek.cpp
Normal file
206
i18n/Uzbek.cpp
Normal 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", "Noto‘g‘ri 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", "Noto‘g‘ri so‘rov"},
|
||||
{"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", "So‘rov 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
|
|
@ -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)")
|
||||
("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")
|
||||
("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)")
|
||||
("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)")
|
||||
|
@ -212,7 +213,9 @@ namespace config {
|
|||
"https://i2p.novg.net/"
|
||||
), "Reseed URLs, separated by comma")
|
||||
("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")
|
||||
;
|
||||
|
||||
|
@ -284,7 +287,7 @@ namespace config {
|
|||
|
||||
options_description meshnets("Meshnet transports 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")
|
||||
;
|
||||
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
@ -398,8 +398,9 @@ namespace crypto
|
|||
}
|
||||
|
||||
// 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);
|
||||
// everything, but a, because a might come from table
|
||||
BIGNUM * k = BN_CTX_get (ctx);
|
||||
|
@ -435,37 +436,32 @@ namespace crypto
|
|||
BN_bin2bn (m, 255, b);
|
||||
BN_mod_mul (b, b1, b, elgp, ctx);
|
||||
// copy a and b
|
||||
if (zeroPadding)
|
||||
{
|
||||
encrypted[0] = 0;
|
||||
bn2buf (a, encrypted + 1, 256);
|
||||
encrypted[257] = 0;
|
||||
bn2buf (b, encrypted + 258, 256);
|
||||
}
|
||||
else
|
||||
{
|
||||
bn2buf (a, encrypted, 256);
|
||||
bn2buf (b, encrypted + 256, 256);
|
||||
}
|
||||
encrypted[0] = 0;
|
||||
bn2buf (a, encrypted + 1, 256);
|
||||
encrypted[257] = 0;
|
||||
bn2buf (b, encrypted + 258, 256);
|
||||
|
||||
BN_free (a);
|
||||
BN_CTX_end (ctx);
|
||||
BN_CTX_free (ctx);
|
||||
}
|
||||
|
||||
bool ElGamalDecrypt (const uint8_t * key, const uint8_t * encrypted,
|
||||
uint8_t * data, BN_CTX * ctx, bool zeroPadding)
|
||||
bool ElGamalDecrypt (const uint8_t * key, const uint8_t * encrypted, uint8_t * data)
|
||||
{
|
||||
BN_CTX * ctx = BN_CTX_new ();
|
||||
BN_CTX_start (ctx);
|
||||
BIGNUM * x = BN_CTX_get (ctx), * a = BN_CTX_get (ctx), * b = BN_CTX_get (ctx);
|
||||
BN_bin2bn (key, 256, x);
|
||||
BN_sub (x, elgp, x); BN_sub_word (x, 1); // x = elgp - x- 1
|
||||
BN_bin2bn (zeroPadding ? encrypted + 1 : encrypted, 256, a);
|
||||
BN_bin2bn (zeroPadding ? encrypted + 258 : encrypted + 256, 256, b);
|
||||
BN_bin2bn (encrypted + 1, 256, a);
|
||||
BN_bin2bn (encrypted + 258, 256, b);
|
||||
// m = b*(a^x mod p) mod p
|
||||
BN_mod_exp (x, a, x, elgp, ctx);
|
||||
BN_mod_mul (b, b, x, elgp, ctx);
|
||||
uint8_t m[255];
|
||||
bn2buf (b, m, 255);
|
||||
BN_CTX_end (ctx);
|
||||
BN_CTX_free (ctx);
|
||||
uint8_t hash[32];
|
||||
SHA256 (m + 33, 222, hash);
|
||||
if (memcmp (m + 1, hash, 32))
|
||||
|
@ -499,8 +495,9 @@ namespace crypto
|
|||
}
|
||||
|
||||
// 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);
|
||||
BIGNUM * q = BN_CTX_get (ctx);
|
||||
EC_GROUP_get_order(curve, q, ctx);
|
||||
|
@ -512,19 +509,10 @@ namespace crypto
|
|||
EC_POINT_mul (curve, p, k, nullptr, nullptr, ctx);
|
||||
BIGNUM * x = BN_CTX_get (ctx), * y = BN_CTX_get (ctx);
|
||||
EC_POINT_get_affine_coordinates_GFp (curve, p, x, y, nullptr);
|
||||
if (zeroPadding)
|
||||
{
|
||||
encrypted[0] = 0;
|
||||
bn2buf (x, encrypted + 1, len);
|
||||
bn2buf (y, encrypted + 1 + len, 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);
|
||||
}
|
||||
encrypted[0] = 0;
|
||||
bn2buf (x, encrypted + 1, len);
|
||||
bn2buf (y, encrypted + 1 + len, len);
|
||||
RAND_bytes (encrypted + 1 + 2*len, 256 - 2*len);
|
||||
// encryption key and iv
|
||||
EC_POINT_mul (curve, p, nullptr, key, k, ctx);
|
||||
EC_POINT_get_affine_coordinates_GFp (curve, p, x, y, nullptr);
|
||||
|
@ -541,36 +529,25 @@ namespace crypto
|
|||
CBCEncryption encryption;
|
||||
encryption.SetKey (shared);
|
||||
encryption.SetIV (iv);
|
||||
if (zeroPadding)
|
||||
{
|
||||
encrypted[257] = 0;
|
||||
encryption.Encrypt (m, 256, encrypted + 258);
|
||||
}
|
||||
else
|
||||
encryption.Encrypt (m, 256, encrypted + 256);
|
||||
encrypted[257] = 0;
|
||||
encryption.Encrypt (m, 256, encrypted + 258);
|
||||
EC_POINT_free (p);
|
||||
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;
|
||||
BN_CTX * ctx = BN_CTX_new ();
|
||||
BN_CTX_start (ctx);
|
||||
BIGNUM * q = BN_CTX_get (ctx);
|
||||
EC_GROUP_get_order(curve, q, ctx);
|
||||
int len = BN_num_bytes (q);
|
||||
// point for shared secret
|
||||
BIGNUM * x = BN_CTX_get (ctx), * y = BN_CTX_get (ctx);
|
||||
if (zeroPadding)
|
||||
{
|
||||
BN_bin2bn (encrypted + 1, len, x);
|
||||
BN_bin2bn (encrypted + 1 + len, len, y);
|
||||
}
|
||||
else
|
||||
{
|
||||
BN_bin2bn (encrypted, len, x);
|
||||
BN_bin2bn (encrypted + len, len, y);
|
||||
}
|
||||
BN_bin2bn (encrypted + 1, len, x);
|
||||
BN_bin2bn (encrypted + 1 + len, len, y);
|
||||
auto p = EC_POINT_new (curve);
|
||||
if (EC_POINT_set_affine_coordinates_GFp (curve, p, x, y, nullptr))
|
||||
{
|
||||
|
@ -587,10 +564,7 @@ namespace crypto
|
|||
CBCDecryption decryption;
|
||||
decryption.SetKey (shared);
|
||||
decryption.SetIV (iv);
|
||||
if (zeroPadding)
|
||||
decryption.Decrypt (encrypted + 258, 256, m);
|
||||
else
|
||||
decryption.Decrypt (encrypted + 256, 256, m);
|
||||
decryption.Decrypt (encrypted + 258, 256, m);
|
||||
// verify and copy
|
||||
uint8_t hash[32];
|
||||
SHA256 (m + 33, 222, hash);
|
||||
|
@ -610,6 +584,7 @@ namespace crypto
|
|||
|
||||
EC_POINT_free (p);
|
||||
BN_CTX_end (ctx);
|
||||
BN_CTX_free (ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
@ -108,13 +108,13 @@ namespace crypto
|
|||
};
|
||||
|
||||
// ElGamal
|
||||
void ElGamalEncrypt (const uint8_t * key, const uint8_t * data, uint8_t * encrypted, BN_CTX * ctx, bool zeroPadding = false);
|
||||
bool ElGamalDecrypt (const uint8_t * key, const uint8_t * encrypted, uint8_t * data, 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); // 514 bytes encrypted, 222 data
|
||||
void GenerateElGamalKeyPair (uint8_t * priv, uint8_t * pub);
|
||||
|
||||
// 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
|
||||
bool ECIESDecrypt (const EC_GROUP * curve, const BIGNUM * key, const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx, bool zeroPadding = false);
|
||||
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); // 514 bytes encrypted, 222 data
|
||||
void GenerateECIESKeyPair (const EC_GROUP * curve, BIGNUM *& priv, EC_POINT *& pub);
|
||||
|
||||
// HMAC
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
@ -20,10 +20,9 @@ namespace crypto
|
|||
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, ctx, zeroPadding);
|
||||
ElGamalEncrypt (m_PublicKey, data, encrypted);
|
||||
}
|
||||
|
||||
ElGamalDecryptor::ElGamalDecryptor (const uint8_t * priv)
|
||||
|
@ -31,10 +30,9 @@ namespace crypto
|
|||
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, ctx, zeroPadding);
|
||||
return ElGamalDecrypt (m_PrivateKey, encrypted, data);
|
||||
}
|
||||
|
||||
ECIESP256Encryptor::ECIESP256Encryptor (const uint8_t * pub)
|
||||
|
@ -54,10 +52,10 @@ namespace crypto
|
|||
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)
|
||||
ECIESEncrypt (m_Curve, m_PublicKey, data, encrypted, ctx, zeroPadding);
|
||||
ECIESEncrypt (m_Curve, m_PublicKey, data, encrypted);
|
||||
}
|
||||
|
||||
ECIESP256Decryptor::ECIESP256Decryptor (const uint8_t * priv)
|
||||
|
@ -72,10 +70,10 @@ namespace crypto
|
|||
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)
|
||||
return ECIESDecrypt (m_Curve, m_PrivateKey, encrypted, data, ctx, zeroPadding);
|
||||
return ECIESDecrypt (m_Curve, m_PrivateKey, encrypted, data);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -114,10 +112,10 @@ namespace crypto
|
|||
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)
|
||||
ECIESEncrypt (GetGOSTR3410Curve (eGOSTR3410CryptoProA)->GetGroup (), m_PublicKey, data, encrypted, ctx, zeroPadding);
|
||||
ECIESEncrypt (GetGOSTR3410Curve (eGOSTR3410CryptoProA)->GetGroup (), m_PublicKey, data, encrypted);
|
||||
}
|
||||
|
||||
ECIESGOSTR3410Decryptor::ECIESGOSTR3410Decryptor (const uint8_t * priv)
|
||||
|
@ -130,10 +128,10 @@ namespace crypto
|
|||
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)
|
||||
return ECIESDecrypt (GetGOSTR3410Curve (eGOSTR3410CryptoProA)->GetGroup (), m_PrivateKey, encrypted, data, ctx, zeroPadding);
|
||||
return ECIESDecrypt (GetGOSTR3410Curve (eGOSTR3410CryptoProA)->GetGroup (), m_PrivateKey, encrypted, data);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -161,7 +159,7 @@ namespace crypto
|
|||
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);
|
||||
}
|
||||
|
@ -171,7 +169,7 @@ namespace crypto
|
|||
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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
@ -21,7 +21,7 @@ namespace crypto
|
|||
public:
|
||||
|
||||
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
|
||||
|
@ -29,7 +29,7 @@ namespace crypto
|
|||
public:
|
||||
|
||||
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
|
||||
};
|
||||
|
||||
|
@ -39,7 +39,7 @@ namespace crypto
|
|||
public:
|
||||
|
||||
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:
|
||||
|
||||
|
@ -51,8 +51,8 @@ namespace crypto
|
|||
public:
|
||||
|
||||
ElGamalDecryptor (const uint8_t * priv);
|
||||
bool Decrypt (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx, bool zeroPadding);
|
||||
size_t GetPublicKeyLen () const { return 256; };
|
||||
bool Decrypt (const uint8_t * encrypted, uint8_t * data) override; // 514 bytes encrypted, 222 bytes data
|
||||
size_t GetPublicKeyLen () const override { return 256; };
|
||||
|
||||
private:
|
||||
|
||||
|
@ -67,7 +67,7 @@ namespace crypto
|
|||
|
||||
ECIESP256Encryptor (const uint8_t * pub);
|
||||
~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:
|
||||
|
||||
|
@ -82,8 +82,8 @@ namespace crypto
|
|||
|
||||
ECIESP256Decryptor (const uint8_t * priv);
|
||||
~ECIESP256Decryptor ();
|
||||
bool Decrypt (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx, bool zeroPadding);
|
||||
size_t GetPublicKeyLen () const { return 64; };
|
||||
bool Decrypt (const uint8_t * encrypted, uint8_t * data) override;
|
||||
size_t GetPublicKeyLen () const override { return 64; };
|
||||
|
||||
private:
|
||||
|
||||
|
@ -101,7 +101,7 @@ namespace crypto
|
|||
|
||||
ECIESGOSTR3410Encryptor (const uint8_t * pub);
|
||||
~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:
|
||||
|
||||
|
@ -115,8 +115,8 @@ namespace crypto
|
|||
|
||||
ECIESGOSTR3410Decryptor (const uint8_t * priv);
|
||||
~ECIESGOSTR3410Decryptor ();
|
||||
bool Decrypt (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx, bool zeroPadding);
|
||||
size_t GetPublicKeyLen () const { return 64; };
|
||||
bool Decrypt (const uint8_t * encrypted, uint8_t * data) override;
|
||||
size_t GetPublicKeyLen () const override { return 64; };
|
||||
|
||||
private:
|
||||
|
||||
|
@ -133,7 +133,7 @@ namespace crypto
|
|||
|
||||
ECIESX25519AEADRatchetEncryptor (const uint8_t * pub);
|
||||
~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
|
||||
|
||||
private:
|
||||
|
@ -147,9 +147,9 @@ namespace crypto
|
|||
|
||||
ECIESX25519AEADRatchetDecryptor (const uint8_t * priv, bool calculatePublic = false);
|
||||
~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)
|
||||
size_t GetPublicKeyLen () const { return 32; };
|
||||
size_t GetPublicKeyLen () const override { return 32; };
|
||||
const uint8_t * GetPubicKey () const { return m_StaticKeys.GetPublicKey (); };
|
||||
|
||||
private:
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
@ -21,6 +21,9 @@ namespace datagram
|
|||
DatagramDestination::DatagramDestination (std::shared_ptr<i2p::client::ClientDestination> owner, bool 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 ();
|
||||
m_From.resize (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,
|
||||
uint16_t fromPort, uint16_t toPort, bool isRaw, bool checksum)
|
||||
{
|
||||
size_t size;
|
||||
auto msg = m_I2NPMsgsPool.AcquireShared ();
|
||||
uint8_t * buf = msg->GetPayload ();
|
||||
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)
|
||||
{
|
||||
htobe32buf (msg->GetPayload (), size); // length
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
@ -164,7 +164,7 @@ namespace datagram
|
|||
std::map<uint16_t, Receiver> m_ReceiversByPorts;
|
||||
|
||||
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;
|
||||
i2p::util::MemoryPool<I2NPMessageBuffer<I2NP_MAX_MESSAGE_SIZE> > m_I2NPMsgsPool;
|
||||
};
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
@ -345,10 +345,11 @@ namespace client
|
|||
void LeaseSetDestination::HandleI2NPMessage (const uint8_t * buf, size_t len)
|
||||
{
|
||||
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)
|
||||
{
|
||||
|
@ -365,6 +366,9 @@ namespace client
|
|||
case eI2NPDatabaseSearchReply:
|
||||
HandleDatabaseSearchReplyMessage (payload, len);
|
||||
break;
|
||||
case eI2NPShortTunnelBuildReply: // might come as garlic encrypted
|
||||
i2p::HandleI2NPMessage (CreateI2NPMessage (typeID, payload, len, msgID));
|
||||
break;
|
||||
default:
|
||||
LogPrint (eLogWarning, "Destination: Unexpected I2NP message type ", typeID);
|
||||
return false;
|
||||
|
@ -1092,6 +1096,35 @@ namespace client
|
|||
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
|
||||
{
|
||||
if (port)
|
||||
|
@ -1241,13 +1274,13 @@ namespace client
|
|||
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 (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)
|
||||
return m_StandardEncryptionKey->decryptor->Decrypt (encrypted, data, ctx, true);
|
||||
return m_StandardEncryptionKey->decryptor->Decrypt (encrypted, data);
|
||||
else
|
||||
LogPrint (eLogError, "Destinations: decryptor is not set");
|
||||
return false;
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
@ -139,12 +139,13 @@ namespace client
|
|||
void SetLeaseSetUpdated ();
|
||||
|
||||
bool IsPublic () const { return m_IsPublic; };
|
||||
void SetPublic (bool pub) { m_IsPublic = pub; };
|
||||
|
||||
protected:
|
||||
|
||||
// implements GarlicDestination
|
||||
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);
|
||||
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, 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);
|
||||
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 StopAcceptingStreams ();
|
||||
bool IsAcceptingStreams () const;
|
||||
|
@ -254,7 +257,7 @@ namespace client
|
|||
i2p::datagram::DatagramDestination * CreateDatagramDestination (bool gzip = true);
|
||||
|
||||
// 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 (); };
|
||||
bool SupportsEncryptionType (i2p::data::CryptoKeyType keyType) const;
|
||||
const uint8_t * GetEncryptionPublicKey (i2p::data::CryptoKeyType keyType) const;
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
@ -31,16 +31,16 @@ namespace garlic
|
|||
uint8_t keydata[64];
|
||||
i2p::crypto::HKDF (rootKey, k, 32, "KDFDHRatchetStep", keydata); // keydata = HKDF(rootKey, k, "KDFDHRatchetStep", 64)
|
||||
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)
|
||||
memcpy (m_SymmKeyCK, m_KeyData.buf + 32, 32);
|
||||
memcpy (m_SymmKeyCK, (const uint8_t *)m_SessionTagKeyData + 32, 32);
|
||||
m_NextSymmKeyIndex = 0;
|
||||
}
|
||||
|
||||
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)
|
||||
memcpy (m_SessTagConstant, m_KeyData.GetSessTagConstant (), 32);
|
||||
i2p::crypto::HKDF (m_SessionTagKeyData, nullptr, 0, "STInitialization", m_SessionTagKeyData); // [sessTag_ck, sesstag_constant] = HKDF(sessTag_ck, ZEROLEN, "STInitialization", 64)
|
||||
memcpy (m_SessTagConstant, (const uint8_t *)m_SessionTagKeyData + 32, 32); // SESSTAG_CONSTANT = keydata[32:63]
|
||||
m_NextIndex = 0;
|
||||
}
|
||||
|
||||
|
@ -52,8 +52,8 @@ namespace garlic
|
|||
LogPrint (eLogError, "Garlic: Tagset ", GetTagSetID (), " is empty");
|
||||
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)
|
||||
return m_KeyData.GetTag ();
|
||||
i2p::crypto::HKDF (m_SessionTagKeyData, m_SessTagConstant, 32, "SessionTagKeyGen", m_SessionTagKeyData); // [sessTag_ck, tag] = HKDF(sessTag_chainkey, SESSTAG_CONSTANT, "SessionTagKeyGen", 64)
|
||||
return m_SessionTagKeyData.GetLL ()[4]; // tag = keydata[32:39]
|
||||
}
|
||||
|
||||
void RatchetTagSet::GetSymmKey (int index, uint8_t * key)
|
||||
|
@ -239,7 +239,7 @@ namespace garlic
|
|||
MixHash (m_Aepk, 32); // h = SHA256(h || aepk)
|
||||
|
||||
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");
|
||||
return false;
|
||||
|
@ -263,7 +263,7 @@ namespace garlic
|
|||
{
|
||||
// static key, fs is apk
|
||||
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");
|
||||
return false;
|
||||
|
@ -492,7 +492,7 @@ namespace garlic
|
|||
// KDF2
|
||||
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);
|
||||
}
|
||||
else
|
||||
|
@ -519,35 +519,6 @@ namespace garlic
|
|||
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)
|
||||
{
|
||||
// we are Bob
|
||||
|
@ -668,7 +639,7 @@ namespace garlic
|
|||
return false;
|
||||
}
|
||||
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);
|
||||
|
||||
uint8_t nonce[12];
|
||||
|
@ -824,8 +795,9 @@ namespace garlic
|
|||
|
||||
std::shared_ptr<I2NPMessage> ECIESX25519AEADRatchetSession::WrapSingleMessage (std::shared_ptr<const I2NPMessage> msg)
|
||||
{
|
||||
auto payload = CreatePayload (msg, m_State != eSessionStateEstablished);
|
||||
size_t len = payload.size ();
|
||||
uint8_t * payload = GetOwner ()->GetPayloadBuffer ();
|
||||
if (!payload) return nullptr;
|
||||
size_t len = CreatePayload (msg, m_State != eSessionStateEstablished, payload);
|
||||
if (!len) return nullptr;
|
||||
auto m = NewI2NPMessage (len + 100); // 96 + 4
|
||||
m->Align (12); // in order to get buf aligned to 16 (12 + 4)
|
||||
|
@ -834,35 +806,30 @@ namespace garlic
|
|||
switch (m_State)
|
||||
{
|
||||
case eSessionStateEstablished:
|
||||
if (!NewExistingSessionMessage (payload.data (), payload.size (), buf, m->maxLen))
|
||||
if (!NewExistingSessionMessage (payload, len, buf, m->maxLen))
|
||||
return nullptr;
|
||||
len += 24;
|
||||
break;
|
||||
case eSessionStateNew:
|
||||
if (!NewOutgoingSessionMessage (payload.data (), payload.size (), buf, m->maxLen))
|
||||
if (!NewOutgoingSessionMessage (payload, len, buf, m->maxLen))
|
||||
return nullptr;
|
||||
len += 96;
|
||||
break;
|
||||
case eSessionStateNewSessionReceived:
|
||||
if (!NewSessionReplyMessage (payload.data (), payload.size (), buf, m->maxLen))
|
||||
if (!NewSessionReplyMessage (payload, len, buf, m->maxLen))
|
||||
return nullptr;
|
||||
len += 72;
|
||||
break;
|
||||
case eSessionStateNewSessionReplySent:
|
||||
if (!NextNewSessionReplyMessage (payload.data (), payload.size (), buf, m->maxLen))
|
||||
if (!NextNewSessionReplyMessage (payload, len, buf, m->maxLen))
|
||||
return nullptr;
|
||||
len += 72;
|
||||
break;
|
||||
case eSessionStateOneTime:
|
||||
if (!NewOutgoingSessionMessage (payload.data (), payload.size (), buf, m->maxLen, false))
|
||||
if (!NewOutgoingSessionMessage (payload, len, buf, m->maxLen, false))
|
||||
return nullptr;
|
||||
len += 96;
|
||||
break;
|
||||
case eSessionStateForRouter:
|
||||
if (!NewOutgoingMessageForRouter (payload.data (), payload.size (), buf, m->maxLen))
|
||||
return nullptr;
|
||||
len += 48;
|
||||
break;
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -873,13 +840,13 @@ namespace garlic
|
|||
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);
|
||||
}
|
||||
|
||||
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 ();
|
||||
size_t payloadLen = 0;
|
||||
|
@ -941,89 +908,93 @@ namespace garlic
|
|||
payloadLen += paddingSize + 3;
|
||||
}
|
||||
}
|
||||
std::vector<uint8_t> v(payloadLen);
|
||||
if (payloadLen)
|
||||
{
|
||||
if (payloadLen > I2NP_MAX_MESSAGE_SIZE)
|
||||
{
|
||||
LogPrint (eLogError, "Garlic: payload length ", payloadLen, " is too long");
|
||||
return 0;
|
||||
}
|
||||
m_LastSentTimestamp = ts;
|
||||
size_t offset = 0;
|
||||
// DateTime
|
||||
if (first)
|
||||
{
|
||||
v[offset] = eECIESx25519BlkDateTime; offset++;
|
||||
htobe16buf (v.data () + offset, 4); offset += 2;
|
||||
htobe32buf (v.data () + offset, ts/1000); offset += 4; // in seconds
|
||||
payload[offset] = eECIESx25519BlkDateTime; offset++;
|
||||
htobe16buf (payload + offset, 4); offset += 2;
|
||||
htobe32buf (payload + offset, ts/1000); offset += 4; // in seconds
|
||||
}
|
||||
// LeaseSet
|
||||
if (leaseSet)
|
||||
{
|
||||
offset += CreateLeaseSetClove (leaseSet, ts, v.data () + offset, payloadLen - offset);
|
||||
offset += CreateLeaseSetClove (leaseSet, ts, payload + offset, payloadLen - offset);
|
||||
if (!first)
|
||||
{
|
||||
// ack request
|
||||
v[offset] = eECIESx25519BlkAckRequest; offset++;
|
||||
htobe16buf (v.data () + offset, 1); offset += 2;
|
||||
v[offset] = 0; offset++; // flags
|
||||
payload[offset] = eECIESx25519BlkAckRequest; offset++;
|
||||
htobe16buf (payload + offset, 1); offset += 2;
|
||||
payload[offset] = 0; offset++; // flags
|
||||
}
|
||||
}
|
||||
// msg
|
||||
if (msg)
|
||||
offset += CreateGarlicClove (msg, v.data () + offset, payloadLen - offset);
|
||||
offset += CreateGarlicClove (msg, payload + offset, payloadLen - offset);
|
||||
// ack
|
||||
if (m_AckRequests.size () > 0)
|
||||
{
|
||||
v[offset] = eECIESx25519BlkAck; offset++;
|
||||
htobe16buf (v.data () + offset, m_AckRequests.size () * 4); offset += 2;
|
||||
payload[offset] = eECIESx25519BlkAck; offset++;
|
||||
htobe16buf (payload + offset, m_AckRequests.size () * 4); offset += 2;
|
||||
for (auto& it: m_AckRequests)
|
||||
{
|
||||
htobe16buf (v.data () + offset, it.first); offset += 2;
|
||||
htobe16buf (v.data () + offset, it.second); offset += 2;
|
||||
htobe16buf (payload + offset, it.first); offset += 2;
|
||||
htobe16buf (payload + offset, it.second); offset += 2;
|
||||
}
|
||||
m_AckRequests.clear ();
|
||||
}
|
||||
// next keys
|
||||
if (m_SendReverseKey)
|
||||
{
|
||||
v[offset] = eECIESx25519BlkNextKey; offset++;
|
||||
htobe16buf (v.data () + offset, m_NextReceiveRatchet->newKey ? 35 : 3); offset += 2;
|
||||
v[offset] = ECIESX25519_NEXT_KEY_REVERSE_KEY_FLAG;
|
||||
payload[offset] = eECIESx25519BlkNextKey; offset++;
|
||||
htobe16buf (payload + offset, m_NextReceiveRatchet->newKey ? 35 : 3); offset += 2;
|
||||
payload[offset] = ECIESX25519_NEXT_KEY_REVERSE_KEY_FLAG;
|
||||
int keyID = m_NextReceiveRatchet->keyID - 1;
|
||||
if (m_NextReceiveRatchet->newKey)
|
||||
{
|
||||
v[offset] |= ECIESX25519_NEXT_KEY_KEY_PRESENT_FLAG;
|
||||
payload[offset] |= ECIESX25519_NEXT_KEY_KEY_PRESENT_FLAG;
|
||||
keyID++;
|
||||
}
|
||||
offset++; // flag
|
||||
htobe16buf (v.data () + offset, keyID); offset += 2; // keyid
|
||||
htobe16buf (payload + offset, keyID); offset += 2; // keyid
|
||||
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
|
||||
}
|
||||
m_SendReverseKey = false;
|
||||
}
|
||||
if (m_SendForwardKey)
|
||||
{
|
||||
v[offset] = eECIESx25519BlkNextKey; offset++;
|
||||
htobe16buf (v.data () + 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;
|
||||
if (!m_NextSendRatchet->keyID) v[offset] |= ECIESX25519_NEXT_KEY_REQUEST_REVERSE_KEY_FLAG; // for first key only
|
||||
payload[offset] = eECIESx25519BlkNextKey; offset++;
|
||||
htobe16buf (payload + offset, m_NextSendRatchet->newKey ? 35 : 3); offset += 2;
|
||||
payload[offset] = m_NextSendRatchet->newKey ? ECIESX25519_NEXT_KEY_KEY_PRESENT_FLAG : ECIESX25519_NEXT_KEY_REQUEST_REVERSE_KEY_FLAG;
|
||||
if (!m_NextSendRatchet->keyID) payload[offset] |= ECIESX25519_NEXT_KEY_REQUEST_REVERSE_KEY_FLAG; // for first key only
|
||||
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)
|
||||
{
|
||||
memcpy (v.data () + offset, m_NextSendRatchet->key->GetPublicKey (), 32);
|
||||
memcpy (payload + offset, m_NextSendRatchet->key->GetPublicKey (), 32);
|
||||
offset += 32; // public key
|
||||
}
|
||||
}
|
||||
// padding
|
||||
if (paddingSize)
|
||||
{
|
||||
v[offset] = eECIESx25519BlkPadding; offset++;
|
||||
htobe16buf (v.data () + offset, paddingSize); offset += 2;
|
||||
memset (v.data () + offset, 0, paddingSize); offset += paddingSize;
|
||||
payload[offset] = eECIESx25519BlkPadding; offset++;
|
||||
htobe16buf (payload + offset, paddingSize); offset += 2;
|
||||
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)
|
||||
|
@ -1113,7 +1084,7 @@ namespace garlic
|
|||
// we are Bob
|
||||
m_CurrentNoiseState.MixHash (buf, 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");
|
||||
return false;
|
||||
|
@ -1133,39 +1104,107 @@ namespace garlic
|
|||
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 ();
|
||||
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 nonce[12];
|
||||
memset (nonce, 0, 12); // n = 0
|
||||
size_t offset = 0;
|
||||
memcpy (buf + offset, &tag, 8); offset += 8;
|
||||
auto payload = buf + offset;
|
||||
uint16_t cloveSize = msg->GetPayloadLength () + 9 + 1;
|
||||
size_t len = cloveSize + 3;
|
||||
payload[0] = eECIESx25519BlkGalicClove; // clove type
|
||||
htobe16buf (payload + 1, cloveSize); // size
|
||||
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
|
||||
size_t len = CreateGarlicPayload (msg, payload, false, 956); // 1003 - 8 tag - 16 Poly1305 hash - 16 I2NP header - 4 garlic length - 3 local tunnel delivery
|
||||
uint8_t nonce[12];
|
||||
memset (nonce, 0, 12); // n = 0
|
||||
if (!i2p::crypto::AEADChaCha20Poly1305 (payload, len, buf, 8, key, nonce, payload, len + 16, true)) // encrypt
|
||||
{
|
||||
LogPrint (eLogWarning, "Garlic: Payload section AEAD encryption failed");
|
||||
return nullptr;
|
||||
}
|
||||
offset += len + 16;
|
||||
|
||||
htobe32buf (m->GetPayload (), offset);
|
||||
m->len += offset + 4;
|
||||
m->FillI2NPMessageHeader (eI2NPGarlic);
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
@ -59,16 +60,7 @@ namespace garlic
|
|||
|
||||
private:
|
||||
|
||||
union
|
||||
{
|
||||
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;
|
||||
i2p::data::Tag<64> m_SessionTagKeyData;
|
||||
uint8_t m_SessTagConstant[32], m_SymmKeyCK[32], m_CurrentSymmKeyCK[64], m_NextRootKey[32];
|
||||
int m_NextIndex, m_NextSymmKeyIndex;
|
||||
std::unordered_map<int, i2p::data::Tag<32> > m_ItermediateSymmKeys;
|
||||
|
@ -147,8 +139,7 @@ namespace garlic
|
|||
eSessionStateNewSessionSent,
|
||||
eSessionStateNewSessionReplySent,
|
||||
eSessionStateEstablished,
|
||||
eSessionStateOneTime,
|
||||
eSessionStateForRouter
|
||||
eSessionStateOneTime
|
||||
};
|
||||
|
||||
struct DHRatchet
|
||||
|
@ -166,7 +157,7 @@ namespace garlic
|
|||
|
||||
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> 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; }
|
||||
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 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 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 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;
|
||||
};
|
||||
|
||||
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
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@ namespace i2p {
|
|||
namespace fs {
|
||||
std::string appName = "i2pd";
|
||||
std::string dataDir = "";
|
||||
std::string certsDir = "";
|
||||
#ifdef _WIN32
|
||||
std::string dirSep = "\\";
|
||||
#else
|
||||
|
@ -42,6 +43,10 @@ namespace fs {
|
|||
return dataDir;
|
||||
}
|
||||
|
||||
const std::string & GetCertsDir () {
|
||||
return certsDir;
|
||||
}
|
||||
|
||||
const std::string GetUTF8DataDir () {
|
||||
#ifdef _WIN32
|
||||
boost::filesystem::wpath path (dataDir);
|
||||
|
@ -126,6 +131,21 @@ namespace fs {
|
|||
#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() {
|
||||
if (!boost::filesystem::exists(dataDir))
|
||||
boost::filesystem::create_directory(dataDir);
|
||||
|
|
18
libi2pd/FS.h
18
libi2pd/FS.h
|
@ -75,6 +75,9 @@ namespace fs {
|
|||
/** @brief Returns datadir path */
|
||||
const std::string & GetDataDir();
|
||||
|
||||
/** @brief Returns certsdir path */
|
||||
const std::string & GetCertsDir();
|
||||
|
||||
/** @brief Returns datadir path in UTF-8 encoding */
|
||||
const std::string GetUTF8DataDir();
|
||||
|
||||
|
@ -90,7 +93,20 @@ namespace fs {
|
|||
* Mac: /Library/Application Support/i2pd/ or ~/Library/Application Support/i2pd/
|
||||
* Unix: /var/lib/i2pd/ (system=1) >> ~/.i2pd/ or /tmp/i2pd/
|
||||
*/
|
||||
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
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include "FS.h"
|
||||
#include "Log.h"
|
||||
#include "Family.h"
|
||||
#include "Config.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
|
@ -98,7 +99,8 @@ namespace data
|
|||
|
||||
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;
|
||||
int numCertificates = 0;
|
||||
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
@ -164,9 +164,7 @@ namespace garlic
|
|||
RAND_bytes (elGamal.preIV, 32); // Pre-IV
|
||||
uint8_t iv[32]; // IV is first 16 bytes
|
||||
SHA256(elGamal.preIV, 32, iv);
|
||||
BN_CTX * ctx = BN_CTX_new ();
|
||||
m_Destination->Encrypt ((uint8_t *)&elGamal, buf, ctx);
|
||||
BN_CTX_free (ctx);
|
||||
m_Destination->Encrypt ((uint8_t *)&elGamal, buf);
|
||||
m_Encryption.SetIV (iv);
|
||||
buf += 514;
|
||||
len += 514;
|
||||
|
@ -433,14 +431,14 @@ namespace garlic
|
|||
}
|
||||
|
||||
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 ()
|
||||
{
|
||||
BN_CTX_free (m_Ctx);
|
||||
if (m_PayloadBuffer)
|
||||
delete[] m_PayloadBuffer;
|
||||
}
|
||||
|
||||
void GarlicDestination::CleanUp ()
|
||||
|
@ -471,8 +469,13 @@ namespace garlic
|
|||
{
|
||||
uint64_t t;
|
||||
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);
|
||||
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)
|
||||
|
@ -494,22 +497,9 @@ namespace garlic
|
|||
buf += 4; // length
|
||||
|
||||
bool found = false;
|
||||
uint64_t tag;
|
||||
if (SupportsEncryptionType (i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD))
|
||||
{
|
||||
// try ECIESx25519 tag
|
||||
memcpy (&tag, buf, 8);
|
||||
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);
|
||||
}
|
||||
}
|
||||
found = HandleECIESx25519TagMessage (buf, length);
|
||||
if (!found)
|
||||
{
|
||||
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
|
||||
ElGamalBlock 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);
|
||||
uint8_t iv[32]; // IV is first 16 bytes
|
||||
|
@ -555,6 +545,7 @@ namespace garlic
|
|||
// try to gererate more tags for last tagset
|
||||
if (m_LastTagset && (m_LastTagset->GetNextIndex () - m_LastTagset->GetTrimBehind () < 3*ECIESX25519_MAX_NUM_GENERATED_TAGS))
|
||||
{
|
||||
uint64_t missingTag; memcpy (&missingTag, buf, 8);
|
||||
auto maxTags = std::max (m_NumRatchetInboundTags, ECIESX25519_MAX_NUM_GENERATED_TAGS);
|
||||
LogPrint (eLogWarning, "Garlic: trying to generate more ECIES-X25519-AEAD-Ratchet tags");
|
||||
for (int i = 0; i < maxTags; i++)
|
||||
|
@ -565,10 +556,10 @@ namespace garlic
|
|||
LogPrint (eLogError, "Garlic: can't create new ECIES-X25519-AEAD-Ratchet tag for last tagset");
|
||||
break;
|
||||
}
|
||||
if (nextTag == tag)
|
||||
if (nextTag == missingTag)
|
||||
{
|
||||
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;
|
||||
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,
|
||||
std::shared_ptr<i2p::tunnel::InboundTunnel> from)
|
||||
{
|
||||
|
@ -749,11 +757,7 @@ namespace garlic
|
|||
std::shared_ptr<I2NPMessage> msg)
|
||||
{
|
||||
if (router->GetEncryptionType () == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD)
|
||||
{
|
||||
auto session = std::make_shared<ECIESX25519AEADRatchetSession>(this, false);
|
||||
session->SetRemoteStaticKey (router->GetIdentity ()->GetEncryptionPublicKey ());
|
||||
return session->WrapOneTimeMessage (msg, true);
|
||||
}
|
||||
return WrapECIESX25519MessageForRouter (msg, router->GetIdentity ()->GetEncryptionPublicKey ());
|
||||
else
|
||||
{
|
||||
auto session = GetRoutingSession (router, false);
|
||||
|
@ -769,7 +773,7 @@ namespace garlic
|
|||
{
|
||||
ECIESX25519AEADRatchetSessionPtr session;
|
||||
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);
|
||||
if (it != m_ECIESx25519Sessions.end ())
|
||||
{
|
||||
|
@ -1036,10 +1040,11 @@ namespace garlic
|
|||
{
|
||||
LogPrint (eLogDebug, "Garlic: type local");
|
||||
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;
|
||||
if (offset <= (int)len)
|
||||
HandleCloveI2NPMessage (typeID, buf, len - offset);
|
||||
HandleCloveI2NPMessage (typeID, buf, len - offset, msgID);
|
||||
else
|
||||
LogPrint (eLogError, "Garlic: clove is too long");
|
||||
break;
|
||||
|
@ -1058,13 +1063,14 @@ namespace garlic
|
|||
}
|
||||
uint32_t gwTunnel = bufbe32toh (buf); buf += 4;
|
||||
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;
|
||||
if (GetTunnelPool ())
|
||||
{
|
||||
auto tunnel = GetTunnelPool ()->GetNextOutboundTunnel ();
|
||||
if (tunnel)
|
||||
tunnel->SendTunnelDataMsg (gwHash, gwTunnel, CreateI2NPMessage (typeID, buf, len - offset));
|
||||
tunnel->SendTunnelDataMsg (gwHash, gwTunnel, CreateI2NPMessage (typeID, buf, len - offset, msgID));
|
||||
else
|
||||
LogPrint (eLogWarning, "Garlic: No outbound tunnels available for garlic clove");
|
||||
}
|
||||
|
@ -1115,5 +1121,12 @@ namespace garlic
|
|||
m_ECIESx25519Sessions.erase (it);
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t * GarlicDestination::GetPayloadBuffer ()
|
||||
{
|
||||
if (!m_PayloadBuffer)
|
||||
m_PayloadBuffer = new uint8_t[I2NP_MAX_MESSAGE_SIZE];
|
||||
return m_PayloadBuffer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
@ -243,13 +243,14 @@ namespace garlic
|
|||
std::shared_ptr<I2NPMessage> msg);
|
||||
|
||||
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
|
||||
void DeliveryStatusSent (GarlicRoutingSessionPtr session, uint32_t msgID);
|
||||
uint64_t AddECIESx25519SessionNextTag (ReceiveRatchetTagSetPtr tagset);
|
||||
void AddECIESx25519Session (const uint8_t * staticKey, ECIESX25519AEADRatchetSessionPtr session);
|
||||
void RemoveECIESx25519Session (const uint8_t * staticKey);
|
||||
void HandleECIESx25519GarlicClove (const uint8_t * buf, size_t len);
|
||||
uint8_t * GetPayloadBuffer ();
|
||||
|
||||
virtual void ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg);
|
||||
virtual void ProcessDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg);
|
||||
|
@ -260,8 +261,10 @@ namespace garlic
|
|||
|
||||
protected:
|
||||
|
||||
void AddECIESx25519Key (const uint8_t * key, const uint8_t * tag); // one tag
|
||||
bool HandleECIESx25519TagMessage (uint8_t * buf, size_t len); // return true if found
|
||||
virtual void HandleI2NPMessage (const uint8_t * buf, size_t len) = 0; // called from clove only
|
||||
virtual bool HandleCloveI2NPMessage (I2NPMessageType typeID, const uint8_t * payload, size_t len) = 0;
|
||||
virtual bool HandleCloveI2NPMessage (I2NPMessageType typeID, const uint8_t * payload, size_t len, uint32_t msgID) = 0;
|
||||
void HandleGarlicMessage (std::shared_ptr<I2NPMessage> msg);
|
||||
void HandleDeliveryStatusMessage (uint32_t msgID);
|
||||
|
||||
|
@ -276,12 +279,12 @@ namespace garlic
|
|||
|
||||
private:
|
||||
|
||||
BN_CTX * m_Ctx; // incoming
|
||||
// outgoing sessions
|
||||
int m_NumTags;
|
||||
std::mutex m_SessionsMutex;
|
||||
std::unordered_map<i2p::data::IdentHash, ElGamalAESSessionPtr> m_Sessions;
|
||||
std::unordered_map<i2p::data::Tag<32>, ECIESX25519AEADRatchetSessionPtr> m_ECIESx25519Sessions; // static key -> session
|
||||
uint8_t * m_PayloadBuffer; // for ECIESX25519AEADRatchet
|
||||
// incoming
|
||||
int m_NumRatchetInboundTags;
|
||||
std::unordered_map<SessionTag, std::shared_ptr<AESDecryption>, std::hash<i2p::data::Tag<32> > > m_Tags;
|
||||
|
|
|
@ -36,10 +36,21 @@ namespace i2p
|
|||
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
|
||||
msg->Align (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);
|
||||
}
|
||||
return std::shared_ptr<I2NPMessage>(msg);
|
||||
}
|
||||
|
||||
|
@ -251,6 +262,12 @@ namespace i2p
|
|||
if (!router) // we send own RouterInfo
|
||||
router = context.GetSharedRouterInfo ();
|
||||
|
||||
if (!router->GetBuffer ())
|
||||
{
|
||||
LogPrint (eLogError, "I2NP: Invalid RouterInfo buffer for DatabaseStore");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto m = NewI2NPShortMessage ();
|
||||
uint8_t * payload = m->GetPayload ();
|
||||
|
||||
|
@ -364,7 +381,7 @@ namespace i2p
|
|||
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++)
|
||||
{
|
||||
|
@ -374,76 +391,48 @@ namespace i2p
|
|||
LogPrint (eLogDebug, "I2NP: Build request record ", i, " is ours");
|
||||
if (!i2p::context.DecryptTunnelBuildRecord (record + BUILD_REQUEST_RECORD_ENCRYPTED_OFFSET, clearText)) return false;
|
||||
uint8_t retCode = 0;
|
||||
bool isECIES = i2p::context.IsECIES ();
|
||||
// replace record to reply
|
||||
if (i2p::context.AcceptsTunnels () &&
|
||||
i2p::tunnel::tunnels.GetTransitTunnels ().size () <= g_MaxNumTransitTunnels &&
|
||||
!i2p::transport::transports.IsBandwidthExceeded () &&
|
||||
!i2p::transport::transports.IsTransitBandwidthExceeded ())
|
||||
{
|
||||
auto transitTunnel = isECIES ?
|
||||
i2p::tunnel::CreateTransitTunnel (
|
||||
auto transitTunnel = i2p::tunnel::CreateTransitTunnel (
|
||||
bufbe32toh (clearText + ECIES_BUILD_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET),
|
||||
clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET,
|
||||
bufbe32toh (clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET),
|
||||
clearText + ECIES_BUILD_REQUEST_RECORD_LAYER_KEY_OFFSET,
|
||||
clearText + ECIES_BUILD_REQUEST_RECORD_IV_KEY_OFFSET,
|
||||
clearText[ECIES_BUILD_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_GATEWAY_FLAG,
|
||||
clearText[ECIES_BUILD_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_ENDPOINT_FLAG) :
|
||||
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);
|
||||
clearText[ECIES_BUILD_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_ENDPOINT_FLAG);
|
||||
i2p::tunnel::tunnels.AddTransitTunnel (transitTunnel);
|
||||
}
|
||||
else
|
||||
retCode = 30; // always reject with bandwidth reason (30)
|
||||
|
||||
if (isECIES)
|
||||
{
|
||||
memset (record + ECIES_BUILD_RESPONSE_RECORD_OPTIONS_OFFSET, 0, 2); // no options
|
||||
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);
|
||||
}
|
||||
memset (record + ECIES_BUILD_RESPONSE_RECORD_OPTIONS_OFFSET, 0, 2); // no options
|
||||
record[ECIES_BUILD_RESPONSE_RECORD_RET_OFFSET] = retCode;
|
||||
// encrypt reply
|
||||
i2p::crypto::CBCEncryption encryption;
|
||||
for (int j = 0; j < num; j++)
|
||||
{
|
||||
uint8_t * reply = records + j*TUNNEL_BUILD_RECORD_SIZE;
|
||||
if (isECIES)
|
||||
if (j == i)
|
||||
{
|
||||
if (j == i)
|
||||
uint8_t nonce[12];
|
||||
memset (nonce, 0, 12);
|
||||
auto& noiseState = i2p::context.GetCurrentNoiseState ();
|
||||
if (!i2p::crypto::AEADChaCha20Poly1305 (reply, TUNNEL_BUILD_RECORD_SIZE - 16,
|
||||
noiseState.m_H, 32, noiseState.m_CK, nonce, reply, TUNNEL_BUILD_RECORD_SIZE, true)) // encrypt
|
||||
{
|
||||
uint8_t nonce[12];
|
||||
memset (nonce, 0, 12);
|
||||
auto& noiseState = i2p::context.GetCurrentNoiseState ();
|
||||
if (!i2p::crypto::AEADChaCha20Poly1305 (reply, TUNNEL_BUILD_RECORD_SIZE - 16,
|
||||
noiseState.m_H, 32, noiseState.m_CK, nonce, reply, TUNNEL_BUILD_RECORD_SIZE, true)) // encrypt
|
||||
{
|
||||
LogPrint (eLogWarning, "I2NP: Reply AEAD encryption failed");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
encryption.SetKey (clearText + ECIES_BUILD_REQUEST_RECORD_REPLY_KEY_OFFSET);
|
||||
encryption.SetIV (clearText + ECIES_BUILD_REQUEST_RECORD_REPLY_IV_OFFSET);
|
||||
encryption.Encrypt(reply, TUNNEL_BUILD_RECORD_SIZE, reply);
|
||||
LogPrint (eLogWarning, "I2NP: Reply AEAD encryption failed");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
encryption.SetKey (clearText + BUILD_REQUEST_RECORD_REPLY_KEY_OFFSET);
|
||||
encryption.SetIV (clearText + BUILD_REQUEST_RECORD_REPLY_IV_OFFSET);
|
||||
encryption.SetKey (clearText + ECIES_BUILD_REQUEST_RECORD_REPLY_KEY_OFFSET);
|
||||
encryption.SetIV (clearText + ECIES_BUILD_REQUEST_RECORD_REPLY_IV_OFFSET);
|
||||
encryption.Encrypt(reply, TUNNEL_BUILD_RECORD_SIZE, reply);
|
||||
}
|
||||
}
|
||||
|
@ -453,7 +442,7 @@ namespace i2p
|
|||
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];
|
||||
LogPrint (eLogDebug, "I2NP: VariableTunnelBuild ", num, " records");
|
||||
|
@ -482,84 +471,38 @@ namespace i2p
|
|||
}
|
||||
else
|
||||
{
|
||||
if (i2p::context.IsECIES ())
|
||||
uint8_t clearText[ECIES_BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE];
|
||||
if (HandleBuildRequestRecords (num, buf + 1, clearText))
|
||||
{
|
||||
uint8_t clearText[ECIES_BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE];
|
||||
if (HandleBuildRequestRecords (num, buf + 1, clearText))
|
||||
if (clearText[ECIES_BUILD_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_ENDPOINT_FLAG) // we are endpoint of outboud tunnel
|
||||
{
|
||||
if (clearText[ECIES_BUILD_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_ENDPOINT_FLAG) // we are endpoint of outboud tunnel
|
||||
{
|
||||
// so we send it to reply tunnel
|
||||
transports.SendMessage (clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET,
|
||||
CreateTunnelGatewayMsg (bufbe32toh (clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET),
|
||||
eI2NPVariableTunnelBuildReply, buf, len,
|
||||
bufbe32toh (clearText + ECIES_BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET)));
|
||||
}
|
||||
else
|
||||
transports.SendMessage (clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET,
|
||||
CreateI2NPMessage (eI2NPVariableTunnelBuild, buf, len,
|
||||
bufbe32toh (clearText + ECIES_BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET)));
|
||||
}
|
||||
}
|
||||
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)));
|
||||
// so we send it to reply tunnel
|
||||
transports.SendMessage (clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET,
|
||||
CreateTunnelGatewayMsg (bufbe32toh (clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET),
|
||||
eI2NPVariableTunnelBuildReply, buf, len,
|
||||
bufbe32toh (clearText + ECIES_BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET)));
|
||||
}
|
||||
else
|
||||
transports.SendMessage (clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET,
|
||||
CreateI2NPMessage (eI2NPVariableTunnelBuild, buf, len,
|
||||
bufbe32toh (clearText + ECIES_BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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");
|
||||
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)));
|
||||
}
|
||||
LogPrint (eLogWarning, "I2NP: TunnelBuild is too old for ECIES router");
|
||||
}
|
||||
|
||||
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];
|
||||
LogPrint (eLogDebug, "I2NP: VariableTunnelBuildReplyMsg of ", num, " records replyMsgID=", replyMsgID);
|
||||
if (len < num*BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE + 1)
|
||||
LogPrint (eLogDebug, "I2NP: TunnelBuildReplyMsg of ", num, " records replyMsgID=", replyMsgID);
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -583,13 +526,8 @@ namespace i2p
|
|||
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];
|
||||
LogPrint (eLogDebug, "I2NP: ShortTunnelBuild ", num, " records");
|
||||
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);
|
||||
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;
|
||||
for (int i = 0; i < num; i++)
|
||||
{
|
||||
|
@ -610,79 +565,100 @@ namespace i2p
|
|||
LogPrint (eLogWarning, "I2NP: Can't decrypt short request record ", i);
|
||||
return;
|
||||
}
|
||||
auto& noiseState = i2p::context.GetCurrentNoiseState ();
|
||||
uint8_t layerKeys[64]; // (layer key, iv key)
|
||||
i2p::crypto::HKDF (noiseState.m_CK + 32, nullptr, 0, "LayerAndIVKeys", layerKeys); // TODO: correct domain
|
||||
auto transitTunnel = i2p::tunnel::CreateTransitTunnel (
|
||||
bufbe32toh (clearText + SHORT_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET),
|
||||
clearText + SHORT_REQUEST_RECORD_NEXT_IDENT_OFFSET,
|
||||
bufbe32toh (clearText + SHORT_REQUEST_RECORD_NEXT_TUNNEL_OFFSET),
|
||||
layerKeys, layerKeys + 32,
|
||||
clearText[SHORT_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_GATEWAY_FLAG,
|
||||
clearText[SHORT_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_ENDPOINT_FLAG);
|
||||
i2p::tunnel::tunnels.AddTransitTunnel (transitTunnel);
|
||||
if (clearText[SHORT_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_ENDPOINT_FLAG)
|
||||
if (clearText[SHORT_REQUEST_RECORD_LAYER_ENCRYPTION_TYPE]) // not AES
|
||||
{
|
||||
// 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)
|
||||
{
|
||||
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)));
|
||||
LogPrint (eLogWarning, "I2NP: Unknown layer encryption type ", clearText[SHORT_REQUEST_RECORD_LAYER_ENCRYPTION_TYPE], " in short request record");
|
||||
return;
|
||||
}
|
||||
auto& noiseState = i2p::context.GetCurrentNoiseState ();
|
||||
uint8_t replyKey[32], layerKey[32], ivKey[32];
|
||||
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)
|
||||
{
|
||||
// we are participant, encrypt reply
|
||||
uint8_t nonce[12];
|
||||
memset (nonce, 0, 12);
|
||||
uint8_t * reply = buf + 1;
|
||||
for (int j = 0; j < num; j++)
|
||||
// create new transit tunnel
|
||||
auto transitTunnel = i2p::tunnel::CreateTransitTunnel (
|
||||
bufbe32toh (clearText + SHORT_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET),
|
||||
clearText + SHORT_REQUEST_RECORD_NEXT_IDENT_OFFSET,
|
||||
bufbe32toh (clearText + SHORT_REQUEST_RECORD_NEXT_TUNNEL_OFFSET),
|
||||
layerKey, ivKey,
|
||||
clearText[SHORT_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_GATEWAY_FLAG,
|
||||
clearText[SHORT_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_ENDPOINT_FLAG);
|
||||
i2p::tunnel::tunnels.AddTransitTunnel (transitTunnel);
|
||||
}
|
||||
|
||||
// encrypt reply
|
||||
uint8_t nonce[12];
|
||||
memset (nonce, 0, 12);
|
||||
uint8_t * reply = buf + 1;
|
||||
for (int j = 0; j < num; j++)
|
||||
{
|
||||
nonce[4] = j; // nonce is record #
|
||||
if (j == i)
|
||||
{
|
||||
nonce[4] = j; // nonce is record #
|
||||
if (j == i)
|
||||
memset (reply + SHORT_RESPONSE_RECORD_OPTIONS_OFFSET, 0, 2); // no options
|
||||
reply[SHORT_RESPONSE_RECORD_RET_OFFSET] = retCode;
|
||||
if (!i2p::crypto::AEADChaCha20Poly1305 (reply, SHORT_TUNNEL_BUILD_RECORD_SIZE - 16,
|
||||
noiseState.m_H, 32, replyKey, nonce, reply, SHORT_TUNNEL_BUILD_RECORD_SIZE, true)) // encrypt
|
||||
{
|
||||
// TODO: fill reply
|
||||
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
|
||||
{
|
||||
LogPrint (eLogWarning, "I2NP: Short reply AEAD encryption failed");
|
||||
return;
|
||||
}
|
||||
LogPrint (eLogWarning, "I2NP: Short reply AEAD encryption failed");
|
||||
return;
|
||||
}
|
||||
else
|
||||
i2p::crypto::ChaCha20 (reply, SHORT_TUNNEL_BUILD_RECORD_SIZE, noiseState.m_CK, nonce, reply);
|
||||
reply += SHORT_TUNNEL_BUILD_RECORD_SIZE;
|
||||
}
|
||||
transports.SendMessage (clearText + SHORT_REQUEST_RECORD_NEXT_TUNNEL_OFFSET,
|
||||
else
|
||||
i2p::crypto::ChaCha20 (reply, SHORT_TUNNEL_BUILD_RECORD_SIZE, replyKey, nonce, reply);
|
||||
reply += SHORT_TUNNEL_BUILD_RECORD_SIZE;
|
||||
}
|
||||
// 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,
|
||||
bufbe32toh (clearText + SHORT_REQUEST_RECORD_SEND_MSG_ID_OFFSET)));
|
||||
}
|
||||
return;
|
||||
}
|
||||
record += SHORT_TUNNEL_BUILD_RECORD_SIZE;
|
||||
|
@ -691,7 +667,7 @@ namespace i2p
|
|||
|
||||
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->FillI2NPMessageHeader (eI2NPTunnelData);
|
||||
return msg;
|
||||
|
@ -699,7 +675,7 @@ namespace i2p
|
|||
|
||||
std::shared_ptr<I2NPMessage> CreateTunnelDataMsg (uint32_t tunnelID, const uint8_t * payload)
|
||||
{
|
||||
auto msg = NewI2NPTunnelMessage ();
|
||||
auto msg = NewI2NPTunnelMessage (false);
|
||||
htobe32buf (msg->GetPayload (), tunnelID);
|
||||
msg->len += 4; // tunnelID
|
||||
msg->Concat (payload, i2p::tunnel::TUNNEL_DATA_MSG_SIZE - 4);
|
||||
|
@ -707,9 +683,9 @@ namespace i2p
|
|||
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;
|
||||
return msg;
|
||||
}
|
||||
|
@ -803,12 +779,15 @@ namespace i2p
|
|||
case eI2NPVariableTunnelBuild:
|
||||
HandleVariableTunnelBuildMsg (msgID, buf, size);
|
||||
break;
|
||||
case eI2NPVariableTunnelBuildReply:
|
||||
HandleVariableTunnelBuildReplyMsg (msgID, buf, size);
|
||||
break;
|
||||
case eI2NPShortTunnelBuild:
|
||||
HandleShortTunnelBuildMsg (msgID, buf, size);
|
||||
break;
|
||||
case eI2NPVariableTunnelBuildReply:
|
||||
HandleTunnelBuildReplyMsg (msgID, buf, size, false);
|
||||
break;
|
||||
case eI2NPShortTunnelBuildReply:
|
||||
HandleTunnelBuildReplyMsg (msgID, buf, size, true);
|
||||
break;
|
||||
case eI2NPTunnelBuild:
|
||||
HandleTunnelBuildMsg (buf, size);
|
||||
break;
|
||||
|
@ -866,6 +845,7 @@ namespace i2p
|
|||
case eI2NPTunnelBuild:
|
||||
case eI2NPTunnelBuildReply:
|
||||
case eI2NPShortTunnelBuild:
|
||||
case eI2NPShortTunnelBuildReply:
|
||||
// forward to tunnel thread
|
||||
i2p::tunnel::tunnels.PostTunnelData (msg);
|
||||
break;
|
||||
|
|
|
@ -55,33 +55,12 @@ namespace i2p
|
|||
|
||||
// TunnelBuild
|
||||
const size_t TUNNEL_BUILD_RECORD_SIZE = 528;
|
||||
const size_t SHORT_TUNNEL_BUILD_RECORD_SIZE = 236;
|
||||
|
||||
//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;
|
||||
const size_t SHORT_TUNNEL_BUILD_RECORD_SIZE = 218;
|
||||
|
||||
// BuildRequestRecordEncrypted
|
||||
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;
|
||||
|
||||
// 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
|
||||
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;
|
||||
|
@ -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_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_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
|
||||
{
|
||||
|
@ -131,7 +115,7 @@ namespace i2p
|
|||
eI2NPVariableTunnelBuild = 23,
|
||||
eI2NPVariableTunnelBuildReply = 24,
|
||||
eI2NPShortTunnelBuild = 25,
|
||||
eI2NPOutboundTunnelBuildReply = 26
|
||||
eI2NPShortTunnelBuildReply = 26
|
||||
};
|
||||
|
||||
const uint8_t TUNNEL_BUILD_RECORD_GATEWAY_FLAG = 0x80;
|
||||
|
@ -278,7 +262,7 @@ namespace tunnel
|
|||
|
||||
std::shared_ptr<I2NPMessage> NewI2NPMessage ();
|
||||
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> 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);
|
||||
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 (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, I2NPMessageType msgType,
|
||||
|
|
|
@ -42,7 +42,7 @@ namespace data
|
|||
}
|
||||
|
||||
IdentityEx::IdentityEx ():
|
||||
m_ExtendedLen (0), m_ExtendedBuffer (nullptr)
|
||||
m_ExtendedLen (0)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -119,11 +119,15 @@ namespace data
|
|||
m_StandardIdentity.certificate[0] = CERTIFICATE_TYPE_KEY;
|
||||
htobe16buf (m_StandardIdentity.certificate + 1, m_ExtendedLen);
|
||||
// fill extended buffer
|
||||
m_ExtendedBuffer = new uint8_t[m_ExtendedLen];
|
||||
htobe16buf (m_ExtendedBuffer, type);
|
||||
htobe16buf (m_ExtendedBuffer + 2, cryptoType);
|
||||
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);
|
||||
delete[] excessBuf;
|
||||
}
|
||||
|
@ -136,7 +140,6 @@ namespace data
|
|||
memset (m_StandardIdentity.certificate, 0, sizeof (m_StandardIdentity.certificate));
|
||||
m_IdentHash = m_StandardIdentity.Hash ();
|
||||
m_ExtendedLen = 0;
|
||||
m_ExtendedBuffer = nullptr;
|
||||
}
|
||||
CreateVerifier ();
|
||||
}
|
||||
|
@ -154,26 +157,25 @@ namespace data
|
|||
}
|
||||
|
||||
IdentityEx::IdentityEx (const uint8_t * buf, size_t len):
|
||||
m_ExtendedLen (0), m_ExtendedBuffer (nullptr)
|
||||
m_ExtendedLen (0)
|
||||
{
|
||||
FromBuffer (buf, len);
|
||||
}
|
||||
|
||||
IdentityEx::IdentityEx (const IdentityEx& other):
|
||||
m_ExtendedLen (0), m_ExtendedBuffer (nullptr)
|
||||
m_ExtendedLen (0)
|
||||
{
|
||||
*this = other;
|
||||
}
|
||||
|
||||
IdentityEx::IdentityEx (const Identity& standard):
|
||||
m_ExtendedLen (0), m_ExtendedBuffer (nullptr)
|
||||
m_ExtendedLen (0)
|
||||
{
|
||||
*this = standard;
|
||||
}
|
||||
|
||||
IdentityEx::~IdentityEx ()
|
||||
{
|
||||
delete[] m_ExtendedBuffer;
|
||||
delete m_Verifier;
|
||||
}
|
||||
|
||||
|
@ -182,15 +184,12 @@ namespace data
|
|||
memcpy (&m_StandardIdentity, &other.m_StandardIdentity, DEFAULT_IDENTITY_SIZE);
|
||||
m_IdentHash = other.m_IdentHash;
|
||||
|
||||
delete[] m_ExtendedBuffer;
|
||||
m_ExtendedLen = other.m_ExtendedLen;
|
||||
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);
|
||||
}
|
||||
else
|
||||
m_ExtendedBuffer = nullptr;
|
||||
|
||||
delete m_Verifier;
|
||||
m_Verifier = nullptr;
|
||||
|
@ -203,8 +202,6 @@ namespace data
|
|||
m_StandardIdentity = standard;
|
||||
m_IdentHash = m_StandardIdentity.Hash ();
|
||||
|
||||
delete[] m_ExtendedBuffer;
|
||||
m_ExtendedBuffer = nullptr;
|
||||
m_ExtendedLen = 0;
|
||||
|
||||
delete m_Verifier;
|
||||
|
@ -222,15 +219,12 @@ namespace data
|
|||
}
|
||||
memcpy (&m_StandardIdentity, buf, DEFAULT_IDENTITY_SIZE);
|
||||
|
||||
if(m_ExtendedBuffer) delete[] m_ExtendedBuffer;
|
||||
m_ExtendedBuffer = nullptr;
|
||||
|
||||
m_ExtendedLen = bufbe16toh (m_StandardIdentity.certificate + 1);
|
||||
if (m_ExtendedLen)
|
||||
{
|
||||
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);
|
||||
}
|
||||
else
|
||||
|
@ -241,10 +235,7 @@ namespace data
|
|||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_ExtendedLen = 0;
|
||||
m_ExtendedBuffer = nullptr;
|
||||
}
|
||||
SHA256(buf, GetFullLen (), m_IdentHash);
|
||||
|
||||
delete m_Verifier;
|
||||
|
@ -258,7 +249,7 @@ namespace data
|
|||
const size_t fullLen = GetFullLen();
|
||||
if (fullLen > len) return 0; // buffer is too small and may overflow somewhere else
|
||||
memcpy (buf, &m_StandardIdentity, DEFAULT_IDENTITY_SIZE);
|
||||
if (m_ExtendedLen > 0 && m_ExtendedBuffer)
|
||||
if (m_ExtendedLen > 0)
|
||||
memcpy (buf + DEFAULT_IDENTITY_SIZE, m_ExtendedBuffer, m_ExtendedLen);
|
||||
return fullLen;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
@ -84,6 +84,7 @@ namespace data
|
|||
typedef uint16_t SigningKeyType;
|
||||
typedef uint16_t CryptoKeyType;
|
||||
|
||||
const size_t MAX_EXTENDED_BUFFER_SIZE = 8; // cryptoKeyType + signingKeyType + 4 extra bytes of P521
|
||||
class IdentityEx
|
||||
{
|
||||
public:
|
||||
|
@ -137,7 +138,7 @@ namespace data
|
|||
mutable i2p::crypto::Verifier * m_Verifier = nullptr;
|
||||
mutable std::mutex m_VerifierMutex;
|
||||
size_t m_ExtendedLen;
|
||||
uint8_t * m_ExtendedBuffer;
|
||||
uint8_t m_ExtendedBuffer[MAX_EXTENDED_BUFFER_SIZE];
|
||||
};
|
||||
|
||||
class PrivateKeys // for eepsites
|
||||
|
@ -222,7 +223,7 @@ namespace data
|
|||
virtual ~RoutingDestination () {};
|
||||
|
||||
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
|
||||
|
||||
const IdentHash& GetIdentHash () const { return GetIdentity ()->GetIdentHash (); };
|
||||
|
@ -234,7 +235,7 @@ namespace data
|
|||
public:
|
||||
|
||||
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;
|
||||
|
||||
const IdentHash& GetIdentHash () const { return GetIdentity ()->GetIdentHash (); };
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
@ -254,12 +254,12 @@ namespace data
|
|||
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;
|
||||
auto encryptor = m_Identity->CreateEncryptor (m_EncryptionKey);
|
||||
if (encryptor)
|
||||
encryptor->Encrypt (data, encrypted, ctx, true);
|
||||
encryptor->Encrypt (data, encrypted);
|
||||
}
|
||||
|
||||
void LeaseSet::SetBuffer (const uint8_t * buf, size_t len)
|
||||
|
@ -658,11 +658,11 @@ namespace data
|
|||
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
|
||||
if (encryptor)
|
||||
encryptor->Encrypt (data, encrypted, ctx, true);
|
||||
encryptor->Encrypt (data, encrypted);
|
||||
}
|
||||
|
||||
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
|
||||
size_t publicKeyLen = blindedKey.BlindPrivateKey (keys.GetSigningPrivateKey (), date, blindedPriv, blindedPub);
|
||||
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;
|
||||
htobe16buf (m_Buffer + offset, blindedKey.GetBlindedSigType ()); offset += 2; // Blinded Public Key Sig Type
|
||||
memcpy (m_Buffer + offset, blindedPub, publicKeyLen); offset += publicKeyLen; // Blinded Public Key
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
@ -93,7 +93,7 @@ namespace data
|
|||
|
||||
// implements RoutingDestination
|
||||
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; };
|
||||
|
||||
protected:
|
||||
|
@ -156,7 +156,7 @@ namespace data
|
|||
bool IsNewer (const uint8_t * buf, size_t len) const;
|
||||
|
||||
// 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; };
|
||||
|
||||
private:
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
@ -32,14 +32,12 @@ namespace i2p
|
|||
namespace transport
|
||||
{
|
||||
NTCP2Establisher::NTCP2Establisher ():
|
||||
m_SessionRequestBuffer (nullptr), m_SessionCreatedBuffer (nullptr), m_SessionConfirmedBuffer (nullptr)
|
||||
m_SessionConfirmedBuffer (nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
NTCP2Establisher::~NTCP2Establisher ()
|
||||
{
|
||||
delete[] m_SessionRequestBuffer;
|
||||
delete[] m_SessionCreatedBuffer;
|
||||
delete[] m_SessionConfirmedBuffer;
|
||||
}
|
||||
|
||||
|
@ -112,9 +110,8 @@ namespace transport
|
|||
void NTCP2Establisher::CreateSessionRequestMessage ()
|
||||
{
|
||||
// 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_SessionRequestBuffer = new uint8_t[m_SessionRequestBufferLen];
|
||||
RAND_bytes (m_SessionRequestBuffer + 64, paddingLength);
|
||||
// encrypt X
|
||||
i2p::crypto::CBCEncryption encryption;
|
||||
|
@ -152,9 +149,8 @@ namespace transport
|
|||
|
||||
void NTCP2Establisher::CreateSessionCreatedMessage ()
|
||||
{
|
||||
auto paddingLen = rand () % (287 - 64);
|
||||
auto paddingLen = rand () % (NTCP2_SESSION_CREATED_MAX_SIZE - 64);
|
||||
m_SessionCreatedBufferLen = paddingLen + 64;
|
||||
m_SessionCreatedBuffer = new uint8_t[m_SessionCreatedBufferLen];
|
||||
RAND_bytes (m_SessionCreatedBuffer + 64, paddingLen);
|
||||
// encrypt Y
|
||||
i2p::crypto::CBCEncryption encryption;
|
||||
|
@ -332,7 +328,8 @@ namespace transport
|
|||
m_SendMDCtx(nullptr), m_ReceiveMDCtx (nullptr),
|
||||
#endif
|
||||
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
|
||||
{
|
||||
|
@ -404,6 +401,29 @@ namespace transport
|
|||
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 ()
|
||||
{
|
||||
|
@ -439,7 +459,6 @@ namespace transport
|
|||
}
|
||||
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
|
||||
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));
|
||||
|
@ -462,7 +481,7 @@ namespace transport
|
|||
{
|
||||
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 (),
|
||||
std::bind(&NTCP2Session::HandleSessionRequestPaddingReceived, shared_from_this (), std::placeholders::_1, std::placeholders::_2));
|
||||
|
@ -515,7 +534,7 @@ namespace transport
|
|||
{
|
||||
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 (),
|
||||
std::bind(&NTCP2Session::HandleSessionCreatedPaddingReceived, shared_from_this (), std::placeholders::_1, std::placeholders::_2));
|
||||
|
@ -718,7 +737,6 @@ namespace transport
|
|||
void NTCP2Session::ServerLogin ()
|
||||
{
|
||||
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 (),
|
||||
std::bind(&NTCP2Session::HandleSessionRequestReceived, shared_from_this (),
|
||||
std::placeholders::_1, std::placeholders::_2));
|
||||
|
@ -758,8 +776,7 @@ namespace transport
|
|||
LogPrint (eLogDebug, "NTCP2: received length ", m_NextReceivedLen);
|
||||
if (m_NextReceivedLen >= 16)
|
||||
{
|
||||
if (m_NextReceivedBuffer) delete[] m_NextReceivedBuffer;
|
||||
m_NextReceivedBuffer = new uint8_t[m_NextReceivedLen];
|
||||
CreateNextReceivedBuffer (m_NextReceivedLen);
|
||||
boost::system::error_code ec;
|
||||
size_t moreBytes = m_Socket.available(ec);
|
||||
if (!ec && moreBytes >= m_NextReceivedLen)
|
||||
|
@ -786,6 +803,7 @@ namespace transport
|
|||
const int one = 1;
|
||||
setsockopt(m_Socket.native_handle(), IPPROTO_TCP, TCP_QUICKACK, &one, sizeof(one));
|
||||
#endif
|
||||
m_IsReceiving = true;
|
||||
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));
|
||||
}
|
||||
|
@ -809,7 +827,7 @@ namespace transport
|
|||
{
|
||||
LogPrint (eLogDebug, "NTCP2: received message decrypted");
|
||||
ProcessNextFrame (m_NextReceivedBuffer, m_NextReceivedLen-16);
|
||||
delete[] m_NextReceivedBuffer; m_NextReceivedBuffer = nullptr; // we don't need received buffer anymore
|
||||
m_IsReceiving = false;
|
||||
ReceiveLength ();
|
||||
}
|
||||
else
|
||||
|
@ -1058,7 +1076,15 @@ namespace transport
|
|||
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 (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
|
||||
htobe16buf (buf + 1, paddingSize); // size
|
||||
memset (buf + 3, 0, paddingSize);
|
||||
|
@ -1439,6 +1465,8 @@ namespace transport
|
|||
LogPrint (eLogDebug, "NTCP2: No activity for ", session->GetTerminationTimeout (), " seconds");
|
||||
session->TerminateByTimeout (); // it doesn't change m_NTCP2Session right a way
|
||||
}
|
||||
else
|
||||
it.second->DeleteNextReceiveBuffer (ts);
|
||||
// pending
|
||||
for (auto it = m_PendingIncomingSessions.begin (); it != m_PendingIncomingSessions.end ();)
|
||||
{
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
@ -28,12 +28,15 @@ namespace transport
|
|||
{
|
||||
|
||||
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_CONNECT_TIMEOUT = 5; // 5 seconds
|
||||
const int NTCP2_ESTABLISH_TIMEOUT = 10; // 10 seconds
|
||||
const int NTCP2_TERMINATION_TIMEOUT = 120; // 2 minutes
|
||||
const int NTCP2_TERMINATION_CHECK_TIMEOUT = 30; // 30 seconds
|
||||
const int NTCP2_RECEIVE_BUFFER_DELETION_TIMEOUT = 3; // 3 seconds
|
||||
const int NTCP2_ROUTERINFO_RESEND_INTERVAL = 25*60; // 25 minuntes in seconds
|
||||
const int NTCP2_ROUTERINFO_RESEND_INTERVAL_THRESHOLD = 25*60; // 25 minuntes
|
||||
|
||||
|
@ -115,7 +118,8 @@ namespace transport
|
|||
i2p::data::IdentHash m_RemoteIdentHash;
|
||||
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;
|
||||
|
||||
};
|
||||
|
@ -132,6 +136,7 @@ namespace transport
|
|||
void TerminateByTimeout ();
|
||||
void Done ();
|
||||
void Close () { m_Socket.close (); }; // for accept
|
||||
void DeleteNextReceiveBuffer (uint64_t ts);
|
||||
|
||||
boost::asio::ip::tcp::socket& GetSocket () { return m_Socket; };
|
||||
const boost::asio::ip::tcp::endpoint& GetRemoteEndpoint () { return m_RemoteEndpoint; };
|
||||
|
@ -151,6 +156,7 @@ namespace transport
|
|||
void Established ();
|
||||
|
||||
void CreateNonce (uint64_t seqn, uint8_t * nonce);
|
||||
void CreateNextReceivedBuffer (size_t size);
|
||||
void KeyDerivationFunctionDataPhase ();
|
||||
void SetSipKeys (const uint8_t * sendSipKey, const uint8_t * receiveSipKey);
|
||||
|
||||
|
@ -206,6 +212,7 @@ namespace transport
|
|||
#endif
|
||||
uint16_t m_NextReceivedLen;
|
||||
uint8_t * m_NextReceivedBuffer, * m_NextSendBuffer;
|
||||
size_t m_NextReceivedBufferSize;
|
||||
union
|
||||
{
|
||||
uint8_t buf[8];
|
||||
|
@ -215,9 +222,12 @@ namespace transport
|
|||
|
||||
i2p::I2NPMessagesHandler m_Handler;
|
||||
|
||||
bool m_IsSending;
|
||||
bool m_IsSending, m_IsReceiving;
|
||||
std::list<std::shared_ptr<I2NPMessage> > m_SendQueue;
|
||||
uint64_t m_NextRouterInfoResendTime; // seconds since epoch
|
||||
|
||||
uint16_t m_PaddingSizes[16];
|
||||
int m_NextPaddingSize;
|
||||
};
|
||||
|
||||
class NTCP2Server: private i2p::util::RunnableServiceWithWork
|
||||
|
|
|
@ -55,11 +55,25 @@ namespace data
|
|||
Load ();
|
||||
|
||||
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 ();
|
||||
}
|
||||
else if (!GetRandomRouter (i2p::context.GetSharedRouterInfo (), false))
|
||||
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);
|
||||
|
||||
m_IsRunning = true;
|
||||
|
@ -162,10 +176,18 @@ namespace data
|
|||
bool publish = false;
|
||||
if (m_PublishReplyToken)
|
||||
{
|
||||
// next publishing attempt
|
||||
if (ts - lastPublish >= NETDB_PUBLISH_CONFIRMATION_TIMEOUT) publish = true;
|
||||
}
|
||||
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
|
||||
{
|
||||
i2p::context.UpdateTimestamp (ts);
|
||||
|
@ -453,14 +475,15 @@ namespace data
|
|||
bool NetDb::LoadRouterInfo (const std::string & path)
|
||||
{
|
||||
auto r = std::make_shared<RouterInfo>(path);
|
||||
if (r->GetRouterIdentity () && !r->IsUnreachable () &&
|
||||
(r->IsReachable () || !r->IsSSU (false) || m_LastLoad < r->GetTimestamp () + NETDB_INTRODUCEE_EXPIRATION_TIMEOUT*1000LL)) // 1 hour
|
||||
if (r->GetRouterIdentity () && !r->IsUnreachable () && r->HasValidAddresses ())
|
||||
{
|
||||
r->DeleteBuffer ();
|
||||
r->ClearProperties (); // properties are not used for regular routers
|
||||
m_RouterInfos[r->GetIdentHash ()] = r;
|
||||
if (r->IsFloodfill () && r->IsReachable ()) // floodfill must be reachable
|
||||
m_Floodfills.push_back (r);
|
||||
if (m_RouterInfos.emplace (r->GetIdentHash (), r).second)
|
||||
{
|
||||
if (r->IsFloodfill () && r->IsEligibleFloodfill ())
|
||||
m_Floodfills.push_back (r);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -556,8 +579,9 @@ namespace data
|
|||
|
||||
void NetDb::SaveUpdated ()
|
||||
{
|
||||
int updatedCount = 0, deletedCount = 0;
|
||||
int updatedCount = 0, deletedCount = 0, deletedFloodfillsCount = 0;
|
||||
auto total = m_RouterInfos.size ();
|
||||
auto totalFloodfills = m_Floodfills.size ();
|
||||
uint64_t expirationTimeout = NETDB_MAX_EXPIRATION_TIMEOUT*1000LL;
|
||||
uint64_t ts = i2p::util::GetMillisecondsSinceEpoch();
|
||||
auto uptime = i2p::context.GetUptime ();
|
||||
|
@ -567,8 +591,10 @@ namespace data
|
|||
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;
|
||||
|
||||
auto own = i2p::context.GetSharedRouterInfo ();
|
||||
for (auto& it: m_RouterInfos)
|
||||
{
|
||||
if (it.second == own) continue; // skip own
|
||||
std::string ident = it.second->GetIdentHashBase64();
|
||||
std::string path = m_Storage.Path(ident);
|
||||
if (it.second->IsUpdated ())
|
||||
|
@ -580,8 +606,9 @@ namespace data
|
|||
updatedCount++;
|
||||
continue;
|
||||
}
|
||||
// make router reachable back if too few routers
|
||||
if (it.second->IsUnreachable () && total - deletedCount < NETDB_MIN_ROUTERS)
|
||||
// make router reachable back if too few routers or floodfills
|
||||
if (it.second->IsUnreachable () && (total - deletedCount < NETDB_MIN_ROUTERS ||
|
||||
(it.second->IsFloodfill () && totalFloodfills - deletedFloodfillsCount < NETDB_MIN_FLOODFILLS)))
|
||||
it.second->SetUnreachable (false);
|
||||
// find & mark expired routers
|
||||
if (!it.second->IsReachable () && it.second->IsSSU (false))
|
||||
|
@ -595,6 +622,7 @@ namespace data
|
|||
|
||||
if (it.second->IsUnreachable ())
|
||||
{
|
||||
if (it.second->IsFloodfill ()) deletedFloodfillsCount++;
|
||||
// delete RI file
|
||||
m_Storage.Remove(ident);
|
||||
deletedCount++;
|
||||
|
@ -935,7 +963,8 @@ namespace data
|
|||
if (router)
|
||||
{
|
||||
LogPrint (eLogDebug, "NetDb: requested RouterInfo ", key, " found");
|
||||
router->LoadBuffer ();
|
||||
if (!router->GetBuffer ())
|
||||
router->LoadBuffer (m_Storage.Path (router->GetIdentHashBase64 ()));
|
||||
if (router->GetBuffer ())
|
||||
replyMsg = CreateDatabaseStoreMsg (router);
|
||||
}
|
||||
|
@ -988,7 +1017,7 @@ namespace data
|
|||
{
|
||||
uint64_t tag;
|
||||
memcpy (&tag, excluded + 33, 8);
|
||||
replyMsg = i2p::garlic::WrapECIESX25519AEADRatchetMessage (replyMsg, sessionKey, tag);
|
||||
replyMsg = i2p::garlic::WrapECIESX25519Message (replyMsg, sessionKey, tag);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -1145,7 +1174,8 @@ namespace data
|
|||
{
|
||||
return !router->IsHidden () && router != compatibleWith &&
|
||||
(reverse ? compatibleWith->IsReachableFrom (*router) :
|
||||
router->IsReachableFrom (*compatibleWith));
|
||||
router->IsReachableFrom (*compatibleWith)) &&
|
||||
router->IsECIES ();
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -1154,8 +1184,8 @@ namespace data
|
|||
return GetRandomRouter (
|
||||
[v4, &excluded](std::shared_ptr<const RouterInfo> router)->bool
|
||||
{
|
||||
return !router->IsHidden () && router->IsPeerTesting (v4) &&
|
||||
!excluded.count (router->GetIdentHash ());
|
||||
return !router->IsHidden () && router->IsECIES () &&
|
||||
router->IsPeerTesting (v4) && !excluded.count (router->GetIdentHash ());
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -1164,7 +1194,7 @@ namespace data
|
|||
return GetRandomRouter (
|
||||
[](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 (
|
||||
[v4, &excluded](std::shared_ptr<const RouterInfo> router)->bool
|
||||
{
|
||||
return router->IsIntroducer (v4) && !excluded.count (router->GetIdentHash ()) &&
|
||||
!router->IsHidden () && !router->IsFloodfill (); // floodfills don't send relay tag
|
||||
return !router->IsHidden () && router->IsECIES () && !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) :
|
||||
router->IsReachableFrom (*compatibleWith)) &&
|
||||
(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())
|
||||
return 0;
|
||||
uint32_t ind = rand () % m_RouterInfos.size ();
|
||||
for (int j = 0; j < 2; j++)
|
||||
uint16_t inds[3];
|
||||
RAND_bytes ((uint8_t *)inds, sizeof (inds));
|
||||
std::unique_lock<std::mutex> l(m_RouterInfosMutex);
|
||||
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])
|
||||
{
|
||||
uint32_t i = 0;
|
||||
std::unique_lock<std::mutex> l(m_RouterInfosMutex);
|
||||
for (const auto& it: m_RouterInfos)
|
||||
{
|
||||
if (i >= ind)
|
||||
{
|
||||
if (!it.second->IsUnreachable () && filter (it.second))
|
||||
return it.second;
|
||||
}
|
||||
else
|
||||
i++;
|
||||
}
|
||||
// we couldn't find anything, try second pass
|
||||
ind = 0;
|
||||
// before
|
||||
inds[1] %= inds[0];
|
||||
std::advance (it1, (inds[1] + inds[0])/2);
|
||||
}
|
||||
else
|
||||
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]);
|
||||
}
|
||||
// it1 - from, it2 - to
|
||||
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
|
||||
}
|
||||
|
|
|
@ -36,6 +36,7 @@ namespace i2p
|
|||
namespace data
|
||||
{
|
||||
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_INTRODUCEE_EXPIRATION_TIMEOUT = 65 * 60;
|
||||
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_MAX_PUBLISH_EXCLUDED_FLOODFILLS = 15;
|
||||
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 */
|
||||
typedef std::function<void(const IdentHash, std::shared_ptr<LeaseSet>)> LeaseSetVisitor;
|
||||
|
|
|
@ -497,7 +497,8 @@ namespace data
|
|||
|
||||
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;
|
||||
int numCertificates = 0;
|
||||
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
@ -43,11 +43,8 @@ namespace i2p
|
|||
m_Decryptor = m_Keys.CreateDecryptor (nullptr);
|
||||
m_TunnelDecryptor = m_Keys.CreateDecryptor (nullptr);
|
||||
UpdateRouterInfo ();
|
||||
if (IsECIES ())
|
||||
{
|
||||
i2p::crypto::InitNoiseNState (m_InitialNoiseState, GetIdentity ()->GetEncryptionPublicKey ());
|
||||
m_ECIESSession = std::make_shared<i2p::garlic::RouterIncomingRatchetSession>(m_InitialNoiseState);
|
||||
}
|
||||
i2p::crypto::InitNoiseNState (m_InitialNoiseState, GetIdentity ()->GetEncryptionPublicKey ());
|
||||
m_ECIESSession = std::make_shared<i2p::garlic::RouterIncomingRatchetSession>(m_InitialNoiseState);
|
||||
}
|
||||
|
||||
void RouterContext::CreateNewRouter ()
|
||||
|
@ -470,7 +467,8 @@ namespace i2p
|
|||
uint8_t caps = m_RouterInfo.GetCaps ();
|
||||
caps &= ~i2p::data::RouterInfo::eReachable;
|
||||
caps |= i2p::data::RouterInfo::eUnreachable;
|
||||
caps &= ~i2p::data::RouterInfo::eFloodfill; // can't be floodfill
|
||||
if (v6 || !SupportsV6 ())
|
||||
caps &= ~i2p::data::RouterInfo::eFloodfill; // can't be floodfill
|
||||
m_RouterInfo.SetCaps (caps);
|
||||
}
|
||||
uint16_t port = 0;
|
||||
|
@ -489,6 +487,7 @@ namespace i2p
|
|||
if (ntcp2)
|
||||
PublishNTCP2Address (port, false, v4, v6, false);
|
||||
// update
|
||||
m_RouterInfo.UpdateSupportedTransports ();
|
||||
UpdateRouterInfo ();
|
||||
}
|
||||
|
||||
|
@ -528,6 +527,7 @@ namespace i2p
|
|||
}
|
||||
}
|
||||
// update
|
||||
m_RouterInfo.UpdateSupportedTransports ();
|
||||
UpdateRouterInfo ();
|
||||
}
|
||||
|
||||
|
@ -566,16 +566,21 @@ namespace i2p
|
|||
{
|
||||
bool ntcp2; i2p::config::GetOption("ntcp2.enabled", ntcp2);
|
||||
bool ntcp2Published; i2p::config::GetOption("ntcp2.published", ntcp2Published);
|
||||
if (ntcp2 && ntcp2Published)
|
||||
if (ntcp2)
|
||||
{
|
||||
std::string ntcp2Host;
|
||||
if (!i2p::config::IsDefault ("ntcp2.addressv6"))
|
||||
i2p::config::GetOption ("ntcp2.addressv6", ntcp2Host);
|
||||
if (ntcp2Published)
|
||||
{
|
||||
std::string ntcp2Host;
|
||||
if (!i2p::config::IsDefault ("ntcp2.addressv6"))
|
||||
i2p::config::GetOption ("ntcp2.addressv6", ntcp2Host);
|
||||
else
|
||||
ntcp2Host = "::1";
|
||||
uint16_t ntcp2Port; i2p::config::GetOption ("ntcp2.port", ntcp2Port);
|
||||
if (!ntcp2Port) ntcp2Port = port;
|
||||
m_RouterInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv, boost::asio::ip::address::from_string (ntcp2Host), ntcp2Port);
|
||||
}
|
||||
else
|
||||
ntcp2Host = "::1";
|
||||
uint16_t ntcp2Port; i2p::config::GetOption ("ntcp2.port", ntcp2Port);
|
||||
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(), 0, i2p::data::RouterInfo::eV6);
|
||||
}
|
||||
}
|
||||
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);
|
||||
}
|
||||
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 ();
|
||||
|
@ -813,9 +818,9 @@ namespace i2p
|
|||
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;
|
||||
i2p::HandleI2NPMessage (msg);
|
||||
return true;
|
||||
|
@ -825,23 +830,22 @@ namespace i2p
|
|||
void RouterContext::ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg)
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_GarlicMutex);
|
||||
if (IsECIES ())
|
||||
uint8_t * buf = msg->GetPayload ();
|
||||
uint32_t len = bufbe32toh (buf);
|
||||
if (len > msg->GetLength ())
|
||||
{
|
||||
uint8_t * buf = msg->GetPayload ();
|
||||
uint32_t len = bufbe32toh (buf);
|
||||
if (len > msg->GetLength ())
|
||||
{
|
||||
LogPrint (eLogWarning, "Router: garlic message length ", len, " exceeds I2NP message length ", msg->GetLength ());
|
||||
return;
|
||||
}
|
||||
buf += 4;
|
||||
LogPrint (eLogWarning, "Router: garlic message length ", len, " exceeds I2NP message length ", msg->GetLength ());
|
||||
return;
|
||||
}
|
||||
buf += 4;
|
||||
if (!HandleECIESx25519TagMessage (buf, len)) // try tag first
|
||||
{
|
||||
// then Noise_N one-time decryption
|
||||
if (m_ECIESSession)
|
||||
m_ECIESSession->HandleNextMessage (buf, len);
|
||||
else
|
||||
LogPrint (eLogError, "Router: Session is not set for ECIES router");
|
||||
}
|
||||
else
|
||||
i2p::garlic::GarlicDestination::ProcessGarlicMessage (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 ();
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
if (IsECIES ())
|
||||
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;
|
||||
}
|
||||
return DecryptECIESTunnelBuildRecord (encrypted, data, ECIES_BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE);
|
||||
}
|
||||
|
||||
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.MixHash (encrypted, 32); // h = SHA256(h || sepk)
|
||||
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");
|
||||
return false;
|
||||
|
@ -912,13 +907,7 @@ namespace i2p
|
|||
|
||||
bool RouterContext::DecryptTunnelShortRequestRecord (const uint8_t * encrypted, uint8_t * data)
|
||||
{
|
||||
if (IsECIES ())
|
||||
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;
|
||||
}
|
||||
return DecryptECIESTunnelBuildRecord (encrypted, data, SHORT_REQUEST_RECORD_CLEAR_TEXT_SIZE);
|
||||
}
|
||||
|
||||
i2p::crypto::X25519Keys& RouterContext::GetStaticKeys ()
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
@ -18,7 +18,6 @@
|
|||
#include "Identity.h"
|
||||
#include "RouterInfo.h"
|
||||
#include "Garlic.h"
|
||||
#include "I18N_langs.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
|
@ -69,10 +68,10 @@ namespace garlic
|
|||
|
||||
const i2p::data::PrivateKeys& GetPrivateKeys () const { return m_Keys; };
|
||||
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,
|
||||
[](const i2p::data::RouterInfo *) {});
|
||||
return std::shared_ptr<i2p::data::RouterInfo> (&m_RouterInfo,
|
||||
[](i2p::data::RouterInfo *) {});
|
||||
}
|
||||
std::shared_ptr<i2p::garlic::GarlicDestination> GetSharedDestination ()
|
||||
{
|
||||
|
@ -124,7 +123,6 @@ namespace garlic
|
|||
void SetSupportsV6 (bool supportsV6);
|
||||
void SetSupportsV4 (bool supportsV4);
|
||||
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; };
|
||||
|
||||
void UpdateNTCP2V6Address (const boost::asio::ip::address& host); // called from Daemon. TODO: remove
|
||||
|
@ -134,7 +132,7 @@ namespace garlic
|
|||
|
||||
// implements LocalDestination
|
||||
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 SetLeaseSetUpdated () {};
|
||||
|
||||
|
@ -146,15 +144,11 @@ namespace garlic
|
|||
void ProcessGarlicMessage (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:
|
||||
|
||||
// implements GarlicDestination
|
||||
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:
|
||||
|
||||
|
@ -186,9 +180,6 @@ namespace garlic
|
|||
std::unique_ptr<i2p::crypto::X25519Keys> m_StaticKeys;
|
||||
// for ECIESx25519
|
||||
i2p::crypto::NoiseSymmetricState m_InitialNoiseState, m_CurrentNoiseState;
|
||||
|
||||
// i18n
|
||||
std::shared_ptr<const i2p::i18n::Locale> m_Language;
|
||||
};
|
||||
|
||||
extern RouterContext context;
|
||||
|
|
|
@ -35,12 +35,12 @@ namespace data
|
|||
}
|
||||
|
||||
RouterInfo::RouterInfo (const std::string& fullPath):
|
||||
m_FullPath (fullPath), m_IsUpdated (false), m_IsUnreachable (false),
|
||||
m_SupportedTransports (0), m_ReachableTransports (0), m_Caps (0), m_Version (0)
|
||||
m_IsUpdated (false), m_IsUnreachable (false), m_SupportedTransports (0),
|
||||
m_ReachableTransports (0), m_Caps (0), m_Version (0)
|
||||
{
|
||||
m_Addresses = boost::make_shared<Addresses>(); // create empty list
|
||||
m_Buffer = new uint8_t[MAX_RI_BUFFER_SIZE];
|
||||
ReadFromFile ();
|
||||
ReadFromFile (fullPath);
|
||||
}
|
||||
|
||||
RouterInfo::RouterInfo (const uint8_t * buf, int len):
|
||||
|
@ -113,16 +113,16 @@ namespace data
|
|||
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 ())
|
||||
{
|
||||
s.seekg (0,std::ios::end);
|
||||
m_BufferLen = s.tellg ();
|
||||
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;
|
||||
}
|
||||
s.seekg(0, std::ios::beg);
|
||||
|
@ -131,15 +131,15 @@ namespace data
|
|||
}
|
||||
else
|
||||
{
|
||||
LogPrint (eLogError, "RouterInfo: Can't open file ", m_FullPath);
|
||||
LogPrint (eLogError, "RouterInfo: Can't open file ", fullPath);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void RouterInfo::ReadFromFile ()
|
||||
void RouterInfo::ReadFromFile (const std::string& fullPath)
|
||||
{
|
||||
if (LoadFile ())
|
||||
if (LoadFile (fullPath))
|
||||
ReadFromBuffer (false);
|
||||
else
|
||||
m_IsUnreachable = true;
|
||||
|
@ -187,13 +187,15 @@ namespace data
|
|||
|
||||
void RouterInfo::ReadFromStream (std::istream& s)
|
||||
{
|
||||
if (!s) return;
|
||||
m_Caps = 0;
|
||||
s.read ((char *)&m_Timestamp, sizeof (m_Timestamp));
|
||||
m_Timestamp = be64toh (m_Timestamp);
|
||||
// read addresses
|
||||
auto addresses = boost::make_shared<Addresses>();
|
||||
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++)
|
||||
{
|
||||
uint8_t supportedTransports = 0;
|
||||
|
@ -281,7 +283,11 @@ namespace data
|
|||
if (s) continue; else return;
|
||||
}
|
||||
if (index >= address->ssu->introducers.size ())
|
||||
{
|
||||
if (address->ssu->introducers.empty ()) // first time
|
||||
address->ssu->introducers.reserve (3);
|
||||
address->ssu->introducers.resize (index + 1);
|
||||
}
|
||||
Introducer& introducer = address->ssu->introducers.at (index);
|
||||
if (!strcmp (key, "ihost"))
|
||||
{
|
||||
|
@ -343,7 +349,8 @@ namespace data
|
|||
int numValid = 0;
|
||||
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 ())))
|
||||
numValid++;
|
||||
else
|
||||
|
@ -747,11 +754,11 @@ namespace data
|
|||
return bufbe64toh (buf + size) > m_Timestamp;
|
||||
}
|
||||
|
||||
const uint8_t * RouterInfo::LoadBuffer ()
|
||||
const uint8_t * RouterInfo::LoadBuffer (const std::string& fullPath)
|
||||
{
|
||||
if (!m_Buffer)
|
||||
{
|
||||
if (LoadFile ())
|
||||
if (LoadFile (fullPath))
|
||||
LogPrint (eLogDebug, "RouterInfo: Buffer for ", GetIdentHashAbbreviation (GetIdentHash ()), " loaded from file");
|
||||
}
|
||||
return m_Buffer;
|
||||
|
@ -782,8 +789,8 @@ namespace data
|
|||
|
||||
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");
|
||||
return false;
|
||||
}
|
||||
|
@ -840,21 +847,33 @@ namespace data
|
|||
for (const auto& it: *m_Addresses) // don't insert same address twice
|
||||
if (*it == *addr) return;
|
||||
m_SupportedTransports |= addr->host.is_v6 () ? eSSUV6 : eSSUV4;
|
||||
m_ReachableTransports |= addr->host.is_v6 () ? eSSUV6 : eSSUV4;
|
||||
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>();
|
||||
addr->host = host;
|
||||
addr->port = port;
|
||||
addr->transportStyle = eTransportNTCP;
|
||||
addr->caps = 0;
|
||||
addr->caps = caps;
|
||||
addr->date = 0;
|
||||
addr->ntcp2.reset (new NTCP2Ext ());
|
||||
if (port) addr->published = true;
|
||||
memcpy (addr->ntcp2->staticKey, staticKey, 32);
|
||||
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));
|
||||
}
|
||||
|
||||
|
@ -971,10 +990,10 @@ namespace data
|
|||
{
|
||||
if (!IsV6 ())
|
||||
{
|
||||
m_SupportedTransports |= eSSUV6 | eNTCP2V6;
|
||||
uint8_t addressCaps = AddressCaps::eV6;
|
||||
if (IsV4 ()) addressCaps |= AddressCaps::eV4;
|
||||
SetUnreachableAddressesTransportCaps (addressCaps);
|
||||
UpdateSupportedTransports ();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -982,10 +1001,10 @@ namespace data
|
|||
{
|
||||
if (!IsV4 ())
|
||||
{
|
||||
m_SupportedTransports |= eSSUV4 | eNTCP2V4;
|
||||
uint8_t addressCaps = AddressCaps::eV4;
|
||||
if (IsV6 ()) addressCaps |= AddressCaps::eV6;
|
||||
SetUnreachableAddressesTransportCaps (addressCaps);
|
||||
UpdateSupportedTransports ();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -994,7 +1013,6 @@ namespace data
|
|||
{
|
||||
if (IsV6 ())
|
||||
{
|
||||
m_SupportedTransports &= ~(eSSUV6 | eNTCP2V6);
|
||||
for (auto it = m_Addresses->begin (); it != m_Addresses->end ();)
|
||||
{
|
||||
auto addr = *it;
|
||||
|
@ -1011,6 +1029,7 @@ namespace data
|
|||
else
|
||||
++it;
|
||||
}
|
||||
UpdateSupportedTransports ();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1018,7 +1037,6 @@ namespace data
|
|||
{
|
||||
if (IsV4 ())
|
||||
{
|
||||
m_SupportedTransports &= ~(eSSUV4 | eNTCP2V4);
|
||||
for (auto it = m_Addresses->begin (); it != m_Addresses->end ();)
|
||||
{
|
||||
auto addr = *it;
|
||||
|
@ -1035,13 +1053,17 @@ namespace data
|
|||
else
|
||||
++it;
|
||||
}
|
||||
UpdateSupportedTransports ();
|
||||
}
|
||||
}
|
||||
|
||||
void RouterInfo::EnableMesh ()
|
||||
{
|
||||
if (!IsMesh ())
|
||||
{
|
||||
m_SupportedTransports |= eNTCP2V6Mesh;
|
||||
m_ReachableTransports |= eNTCP2V6Mesh;
|
||||
}
|
||||
}
|
||||
|
||||
void RouterInfo::DisableMesh ()
|
||||
|
@ -1049,6 +1071,7 @@ namespace data
|
|||
if (IsMesh ())
|
||||
{
|
||||
m_SupportedTransports &= ~eNTCP2V6Mesh;
|
||||
m_ReachableTransports &= ~eNTCP2V6Mesh;
|
||||
for (auto it = m_Addresses->begin (); it != m_Addresses->end ();)
|
||||
{
|
||||
auto addr = *it;
|
||||
|
@ -1138,18 +1161,17 @@ namespace data
|
|||
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);
|
||||
if (encryptor)
|
||||
encryptor->Encrypt (data, encrypted, ctx, true);
|
||||
encryptor->Encrypt (data, encrypted);
|
||||
}
|
||||
|
||||
bool RouterInfo::IsEligibleFloodfill () const
|
||||
{
|
||||
// floodfill must be reachable somehow, >= 0.9.28 and not DSA
|
||||
return (IsReachable () || (m_SupportedTransports & eSSUV4)) &&
|
||||
m_Version >= NETDB_MIN_FLOODFILL_VERSION &&
|
||||
// floodfill must be reachable by ipv4, >= 0.9.38 and not DSA
|
||||
return IsReachableBy (eNTCP2V4 | eSSUV4) && m_Version >= NETDB_MIN_FLOODFILL_VERSION &&
|
||||
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)
|
||||
{
|
||||
for (auto& addr: *m_Addresses)
|
||||
|
|
|
@ -13,7 +13,6 @@
|
|||
#include <string>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <list>
|
||||
#include <iostream>
|
||||
#include <boost/asio.hpp>
|
||||
#include <boost/shared_ptr.hpp>
|
||||
|
@ -67,6 +66,7 @@ namespace data
|
|||
eSSUV6 = 0x08,
|
||||
eNTCP2V6Mesh = 0x10
|
||||
};
|
||||
typedef uint8_t CompatibleTransports;
|
||||
|
||||
enum Caps
|
||||
{
|
||||
|
@ -156,7 +156,7 @@ namespace data
|
|||
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 ()); };
|
||||
};
|
||||
typedef std::list<std::shared_ptr<Address> > Addresses;
|
||||
typedef std::vector<std::shared_ptr<Address> > Addresses;
|
||||
|
||||
RouterInfo ();
|
||||
RouterInfo (const std::string& fullPath);
|
||||
|
@ -179,7 +179,8 @@ namespace data
|
|||
std::shared_ptr<const Address> GetYggdrasilAddress () const;
|
||||
|
||||
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 RemoveIntroducer (const boost::asio::ip::udp::endpoint& e);
|
||||
void SetProperty (const std::string& key, const std::string& value); // called from RouterContext only
|
||||
|
@ -190,6 +191,7 @@ namespace data
|
|||
void UpdateSupportedTransports ();
|
||||
bool IsFloodfill () const { return m_Caps & Caps::eFloodfill; };
|
||||
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 IsSSUV6 () const;
|
||||
bool IsNTCP2 (bool v4only = true) const;
|
||||
|
@ -204,7 +206,9 @@ namespace data
|
|||
void EnableMesh ();
|
||||
void DisableMesh ();
|
||||
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 IsHidden () const { return m_Caps & eHidden; };
|
||||
bool IsHighBandwidth () const { return m_Caps & RouterInfo::eHighBandwidth; };
|
||||
|
@ -221,7 +225,7 @@ namespace data
|
|||
bool IsUnreachable () const { return m_IsUnreachable; };
|
||||
|
||||
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; };
|
||||
void CreateBuffer (const PrivateKeys& privateKeys);
|
||||
|
||||
|
@ -241,14 +245,14 @@ namespace data
|
|||
|
||||
// implements RoutingDestination
|
||||
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; };
|
||||
|
||||
private:
|
||||
|
||||
bool LoadFile ();
|
||||
void ReadFromFile ();
|
||||
bool LoadFile (const std::string& fullPath);
|
||||
void ReadFromFile (const std::string& fullPath);
|
||||
void ReadFromStream (std::istream& s);
|
||||
void ReadFromBuffer (bool verifySignature);
|
||||
void WriteToStream (std::ostream& s) const;
|
||||
|
@ -262,7 +266,7 @@ namespace data
|
|||
|
||||
private:
|
||||
|
||||
std::string m_FullPath, m_Family;
|
||||
std::string m_Family;
|
||||
std::shared_ptr<const IdentityEx> m_RouterIdentity;
|
||||
uint8_t * m_Buffer;
|
||||
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
|
||||
std::map<std::string, std::string> m_Properties;
|
||||
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;
|
||||
mutable std::shared_ptr<RouterProfile> m_Profile;
|
||||
};
|
||||
|
|
|
@ -218,7 +218,7 @@ namespace transport
|
|||
|
||||
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)
|
||||
|
@ -255,14 +255,14 @@ namespace transport
|
|||
|
||||
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,
|
||||
std::bind (&SSUServer::HandleReceivedFrom, this, std::placeholders::_1, std::placeholders::_2, packet));
|
||||
}
|
||||
|
||||
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,
|
||||
std::bind (&SSUServer::HandleReceivedFromV6, this, std::placeholders::_1, std::placeholders::_2, packet));
|
||||
}
|
||||
|
@ -293,7 +293,7 @@ namespace transport
|
|||
{
|
||||
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);
|
||||
if (!ec)
|
||||
{
|
||||
|
@ -304,7 +304,7 @@ namespace transport
|
|||
else
|
||||
{
|
||||
LogPrint (eLogError, "SSU: receive_from error: code ", ec.value(), ": ", ec.message ());
|
||||
delete packet;
|
||||
m_PacketsPool.ReleaseMt (packet);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -315,7 +315,7 @@ namespace transport
|
|||
}
|
||||
else
|
||||
{
|
||||
delete packet;
|
||||
m_PacketsPool.ReleaseMt (packet);
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
{
|
||||
LogPrint (eLogError, "SSU: receive error: code ", ecode.value(), ": ", ecode.message ());
|
||||
|
@ -352,7 +352,7 @@ namespace transport
|
|||
{
|
||||
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);
|
||||
if (!ec)
|
||||
{
|
||||
|
@ -363,7 +363,7 @@ namespace transport
|
|||
else
|
||||
{
|
||||
LogPrint (eLogError, "SSU: v6 receive_from error: code ", ec.value(), ": ", ec.message ());
|
||||
delete packet;
|
||||
m_PacketsPool.ReleaseMt (packet);;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -374,7 +374,7 @@ namespace transport
|
|||
}
|
||||
else
|
||||
{
|
||||
delete packet;
|
||||
m_PacketsPool.ReleaseMt (packet);
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
{
|
||||
LogPrint (eLogError, "SSU: v6 receive error: code ", ecode.value(), ": ", ecode.message ());
|
||||
|
@ -421,8 +421,8 @@ namespace transport
|
|||
if (session) session->FlushData ();
|
||||
session = nullptr;
|
||||
}
|
||||
delete packet;
|
||||
}
|
||||
m_PacketsPool.ReleaseMt (packets);
|
||||
if (session) session->FlushData ();
|
||||
}
|
||||
|
||||
|
@ -919,13 +919,19 @@ namespace transport
|
|||
}
|
||||
if (numDeleted > 0)
|
||||
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 ();
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
this, std::placeholders::_1));
|
||||
}
|
||||
|
@ -947,13 +953,16 @@ namespace transport
|
|||
session->Failed ();
|
||||
});
|
||||
}
|
||||
else
|
||||
it.second->CleanUp (ts);
|
||||
ScheduleTermination ();
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
this, std::placeholders::_1));
|
||||
}
|
||||
|
@ -975,6 +984,8 @@ namespace transport
|
|||
session->Failed ();
|
||||
});
|
||||
}
|
||||
else
|
||||
it.second->CleanUp (ts);
|
||||
ScheduleTerminationV6 ();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
@ -18,6 +18,7 @@
|
|||
#include <mutex>
|
||||
#include <boost/asio.hpp>
|
||||
#include "Crypto.h"
|
||||
#include "util.h"
|
||||
#include "I2PEndian.h"
|
||||
#include "Identity.h"
|
||||
#include "RouterInfo.h"
|
||||
|
@ -63,6 +64,10 @@ namespace transport
|
|||
void DeleteAllSessions ();
|
||||
|
||||
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 (); };
|
||||
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, 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:
|
||||
// for HTTP only
|
||||
const decltype(m_Sessions)& GetSessions () const { return m_Sessions; };
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
@ -33,7 +33,6 @@ namespace transport
|
|||
|
||||
SSUData::SSUData (SSUSession& session):
|
||||
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_PacketSize (m_MaxPacketSize), m_LastMessageReceivedTime (0)
|
||||
{
|
||||
|
@ -45,13 +44,11 @@ namespace transport
|
|||
|
||||
void SSUData::Start ()
|
||||
{
|
||||
ScheduleIncompleteMessagesCleanup ();
|
||||
}
|
||||
|
||||
void SSUData::Stop ()
|
||||
{
|
||||
m_ResendTimer.cancel ();
|
||||
m_IncompleteMessagesCleanupTimer.cancel ();
|
||||
m_IncompleteMessages.clear ();
|
||||
m_SentMessages.clear ();
|
||||
m_ReceivedMessages.clear ();
|
||||
|
@ -140,7 +137,7 @@ namespace transport
|
|||
if (bitfield & mask)
|
||||
{
|
||||
if (fragment < numSentFragments)
|
||||
it->second->fragments[fragment].reset (nullptr);
|
||||
it->second->fragments[fragment] = nullptr;
|
||||
}
|
||||
fragment++;
|
||||
mask <<= 1;
|
||||
|
@ -182,9 +179,9 @@ namespace transport
|
|||
auto msg = NewI2NPShortMessage ();
|
||||
msg->len -= I2NP_SHORT_HEADER_SIZE;
|
||||
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
|
||||
if (fragmentNum < 64)
|
||||
incompleteMessage->receivedFragmentsBits |= (0x01 << fragmentNum);
|
||||
|
@ -224,8 +221,8 @@ namespace transport
|
|||
{
|
||||
// missing fragment
|
||||
LogPrint (eLogWarning, "SSU: Missing fragments from ", (int)incompleteMessage->nextFragmentNum, " to ", fragmentNum - 1, " of message ", msgID);
|
||||
auto savedFragment = new Fragment (fragmentNum, buf, fragmentSize, isLast);
|
||||
if (incompleteMessage->savedFragments.insert (std::unique_ptr<Fragment>(savedFragment)).second)
|
||||
auto savedFragment = m_Session.GetServer ().GetFragmentsPool ().AcquireShared (fragmentNum, buf, fragmentSize, isLast);
|
||||
if (incompleteMessage->savedFragments.insert (savedFragment).second)
|
||||
incompleteMessage->lastFragmentInsertTime = i2p::util::GetSecondsSinceEpoch ();
|
||||
else
|
||||
LogPrint (eLogWarning, "SSU: Fragment ", (int)fragmentNum, " of message ", msgID, " already saved");
|
||||
|
@ -246,8 +243,8 @@ namespace transport
|
|||
{
|
||||
if (!m_ReceivedMessages.count (msgID))
|
||||
{
|
||||
m_ReceivedMessages.insert (msgID);
|
||||
m_LastMessageReceivedTime = i2p::util::GetSecondsSinceEpoch ();
|
||||
m_ReceivedMessages.emplace (msgID, m_LastMessageReceivedTime);
|
||||
if (!msg->IsExpired ())
|
||||
{
|
||||
m_Handler.PutNextMessage (msg);
|
||||
|
@ -313,8 +310,8 @@ namespace transport
|
|||
if (m_SentMessages.empty ()) // schedule resend at first message only
|
||||
ScheduleResend ();
|
||||
|
||||
auto ret = m_SentMessages.insert (std::make_pair (msgID, std::unique_ptr<SentMessage>(new SentMessage)));
|
||||
std::unique_ptr<SentMessage>& sentMessage = ret.first->second;
|
||||
auto ret = m_SentMessages.emplace (msgID, m_Session.GetServer ().GetSentMessagesPool ().AcquireShared ());
|
||||
auto& sentMessage = ret.first->second;
|
||||
if (ret.second)
|
||||
{
|
||||
sentMessage->nextResendTime = i2p::util::GetSecondsSinceEpoch () + RESEND_INTERVAL;
|
||||
|
@ -328,7 +325,7 @@ namespace transport
|
|||
uint32_t fragmentNum = 0;
|
||||
while (len > 0 && fragmentNum <= 127)
|
||||
{
|
||||
Fragment * fragment = new Fragment;
|
||||
auto fragment = m_Session.GetServer ().GetFragmentsPool ().AcquireShared ();
|
||||
fragment->fragmentNum = fragmentNum;
|
||||
uint8_t * payload = fragment->buf + sizeof (SSUHeader);
|
||||
*payload = DATA_FLAG_WANT_REPLY; // for compatibility
|
||||
|
@ -358,7 +355,7 @@ namespace transport
|
|||
size += padding;
|
||||
}
|
||||
fragment->len = size;
|
||||
fragments.push_back (std::unique_ptr<Fragment> (fragment));
|
||||
fragments.push_back (fragment);
|
||||
|
||||
// encrypt message with session key
|
||||
uint8_t buf[SSU_V4_MAX_PACKET_SIZE + 18];
|
||||
|
@ -487,36 +484,32 @@ 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)
|
||||
for (auto it = m_IncompleteMessages.begin (); it != m_IncompleteMessages.end ();)
|
||||
{
|
||||
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
|
||||
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)
|
||||
{
|
||||
LogPrint (eLogWarning, "SSU: message ", it->first, " was not completed in ", INCOMPLETE_MESSAGES_CLEANUP_TIMEOUT, " seconds, deleted");
|
||||
it = m_IncompleteMessages.erase (it);
|
||||
}
|
||||
LogPrint (eLogWarning, "SSU: message ", it->first, " was not completed in ", INCOMPLETE_MESSAGES_CLEANUP_TIMEOUT, " seconds, deleted");
|
||||
it = m_IncompleteMessages.erase (it);
|
||||
}
|
||||
else
|
||||
++it;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
// decay
|
||||
if (m_ReceivedMessages.size () > MAX_NUM_RECEIVED_MESSAGES ||
|
||||
i2p::util::GetSecondsSinceEpoch () > m_LastMessageReceivedTime + DECAY_INTERVAL)
|
||||
m_ReceivedMessages.clear ();
|
||||
|
||||
ScheduleIncompleteMessagesCleanup ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
@ -11,9 +11,9 @@
|
|||
|
||||
#include <inttypes.h>
|
||||
#include <string.h>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include <unordered_set>
|
||||
#include <map>
|
||||
#include <unordered_map>
|
||||
#include <memory>
|
||||
#include <boost/asio.hpp>
|
||||
#include "I2NPProtocol.h"
|
||||
|
@ -40,6 +40,7 @@ namespace transport
|
|||
const int MAX_NUM_RESENDS = 5;
|
||||
const int DECAY_INTERVAL = 20; // 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 int MAX_OUTGOING_WINDOW_SIZE = 200; // how many unacked message we can store
|
||||
// data flags
|
||||
|
@ -64,7 +65,7 @@ namespace transport
|
|||
|
||||
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;
|
||||
};
|
||||
|
@ -76,7 +77,7 @@ namespace transport
|
|||
int nextFragmentNum;
|
||||
uint32_t lastFragmentInsertTime; // in seconds
|
||||
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),
|
||||
lastFragmentInsertTime (0), receivedFragmentsBits (0) {};
|
||||
|
@ -85,7 +86,7 @@ namespace transport
|
|||
|
||||
struct SentMessage
|
||||
{
|
||||
std::vector<std::unique_ptr<Fragment> > fragments;
|
||||
std::vector<std::shared_ptr<Fragment> > fragments;
|
||||
uint32_t nextResendTime; // in seconds
|
||||
int numResends;
|
||||
};
|
||||
|
@ -100,6 +101,7 @@ namespace transport
|
|||
|
||||
void Start ();
|
||||
void Stop ();
|
||||
void CleanUp (uint64_t ts);
|
||||
|
||||
void ProcessMessage (uint8_t * buf, size_t len);
|
||||
void FlushReceivedMessage ();
|
||||
|
@ -119,17 +121,13 @@ namespace transport
|
|||
void ScheduleResend ();
|
||||
void HandleResendTimer (const boost::system::error_code& ecode);
|
||||
|
||||
void ScheduleIncompleteMessagesCleanup ();
|
||||
void HandleIncompleteMessagesCleanupTimer (const boost::system::error_code& ecode);
|
||||
|
||||
|
||||
private:
|
||||
|
||||
SSUSession& m_Session;
|
||||
std::unordered_map<uint32_t, std::unique_ptr<IncompleteMessage> > m_IncompleteMessages;
|
||||
std::unordered_map<uint32_t, std::unique_ptr<SentMessage> > m_SentMessages;
|
||||
std::unordered_set<uint32_t> m_ReceivedMessages;
|
||||
boost::asio::deadline_timer m_ResendTimer, m_IncompleteMessagesCleanupTimer;
|
||||
std::map<uint32_t, std::shared_ptr<IncompleteMessage> > m_IncompleteMessages;
|
||||
std::map<uint32_t, std::shared_ptr<SentMessage> > m_SentMessages;
|
||||
std::unordered_map<uint32_t, uint64_t> m_ReceivedMessages; // msgID -> timestamp in seconds
|
||||
boost::asio::deadline_timer m_ResendTimer;
|
||||
int m_MaxPacketSize, m_PacketSize;
|
||||
i2p::I2NPMessagesHandler m_Handler;
|
||||
uint32_t m_LastMessageReceivedTime; // in second
|
||||
|
|
|
@ -730,7 +730,7 @@ namespace transport
|
|||
(remoteIP.is_v6 () && i2p::context.GetStatusV6 () == eRouterStatusFirewalled))
|
||||
m_Server.Send (buf, 0, remoteEndpoint); // send HolePunch
|
||||
// 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
|
||||
m_RelayRequests.erase (it);
|
||||
|
@ -905,7 +905,8 @@ namespace transport
|
|||
}
|
||||
uint32_t nonce;
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
{
|
||||
uint32_t nonce = bufbe32toh (buf); // 4 bytes
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
@ -89,6 +89,7 @@ namespace transport
|
|||
void Done ();
|
||||
void Failed ();
|
||||
const boost::asio::ip::udp::endpoint& GetRemoteEndpoint () { return m_RemoteEndpoint; };
|
||||
SSUServer& GetServer () { return m_Server; };
|
||||
|
||||
bool IsV6 () const { return m_RemoteEndpoint.address ().is_v6 (); };
|
||||
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 FlushData ();
|
||||
void CleanUp (uint64_t ts);
|
||||
|
||||
private:
|
||||
|
||||
|
@ -168,7 +170,7 @@ namespace transport
|
|||
SSUData m_Data;
|
||||
bool m_IsDataReceived;
|
||||
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
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
@ -104,6 +104,7 @@ namespace stream
|
|||
|
||||
void Stream::Terminate (bool deleteFromDestination) // shoudl be called from StreamingDestination::Stop only
|
||||
{
|
||||
m_Status = eStreamStatusTerminated;
|
||||
m_AckSendTimer.cancel ();
|
||||
m_ReceiveTimer.cancel ();
|
||||
m_ResendTimer.cancel ();
|
||||
|
@ -276,7 +277,20 @@ namespace stream
|
|||
const uint8_t * optionData = packet->GetOptionData ();
|
||||
size_t optionSize = packet->GetOptionSize ();
|
||||
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;
|
||||
}
|
||||
|
||||
if (flags & PACKET_FLAG_FROM_INCLUDED)
|
||||
{
|
||||
|
@ -651,6 +665,42 @@ namespace stream
|
|||
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 ()
|
||||
{
|
||||
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)
|
||||
{
|
||||
std::vector<i2p::tunnel::TunnelMessageBlock> msgs;
|
||||
for (auto it: packets)
|
||||
for (const auto& it: packets)
|
||||
{
|
||||
auto msg = m_RoutingSession->WrapSingleMessage (m_LocalDestination.CreateDataMessage (
|
||||
it->GetBuffer (), it->GetLength (), m_Port, !m_RoutingSession->IsRatchets ()));
|
||||
|
@ -844,12 +894,15 @@ namespace stream
|
|||
|
||||
void Stream::ScheduleResend ()
|
||||
{
|
||||
m_ResendTimer.cancel ();
|
||||
// check for invalid value
|
||||
if (m_RTO <= 0) m_RTO = INITIAL_RTO;
|
||||
m_ResendTimer.expires_from_now (boost::posix_time::milliseconds(m_RTO));
|
||||
m_ResendTimer.async_wait (std::bind (&Stream::HandleResendTimer,
|
||||
shared_from_this (), std::placeholders::_1));
|
||||
if (m_Status != eStreamStatusTerminated)
|
||||
{
|
||||
m_ResendTimer.cancel ();
|
||||
// check for invalid value
|
||||
if (m_RTO <= 0) m_RTO = INITIAL_RTO;
|
||||
m_ResendTimer.expires_from_now (boost::posix_time::milliseconds(m_RTO));
|
||||
m_ResendTimer.async_wait (std::bind (&Stream::HandleResendTimer,
|
||||
shared_from_this (), std::placeholders::_1));
|
||||
}
|
||||
}
|
||||
|
||||
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_PendingIncomingTimer (m_Owner->GetService ())
|
||||
{
|
||||
if (m_Gzip)
|
||||
m_Deflator.reset (new i2p::data::GzipDeflator);
|
||||
}
|
||||
|
||||
StreamingDestination::~StreamingDestination ()
|
||||
|
@ -1050,6 +1105,7 @@ namespace stream
|
|||
it.second->Terminate (false); // we delete here
|
||||
m_Streams.clear ();
|
||||
m_IncomingStreams.clear ();
|
||||
m_LastStream = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1058,9 +1114,16 @@ namespace stream
|
|||
uint32_t sendStreamID = packet->GetSendStreamID ();
|
||||
if (sendStreamID)
|
||||
{
|
||||
auto it = m_Streams.find (sendStreamID);
|
||||
if (it != m_Streams.end ())
|
||||
it->second->HandleNextPacket (packet);
|
||||
if (!m_LastStream || sendStreamID != m_LastStream->GetRecvStreamID ())
|
||||
{
|
||||
auto it = m_Streams.find (sendStreamID);
|
||||
if (it != m_Streams.end ())
|
||||
m_LastStream = it->second;
|
||||
else
|
||||
m_LastStream = nullptr;
|
||||
}
|
||||
if (m_LastStream)
|
||||
m_LastStream->HandleNextPacket (packet);
|
||||
else if (packet->IsEcho () && m_Owner->IsStreamingAnswerPings ())
|
||||
{
|
||||
// ping
|
||||
|
@ -1076,6 +1139,13 @@ namespace stream
|
|||
}
|
||||
else
|
||||
{
|
||||
if (packet->IsEcho ())
|
||||
{
|
||||
// pong
|
||||
LogPrint (eLogInfo, "Streaming: Pong received rSID=", packet->GetReceiveStreamID ());
|
||||
DeletePacket (packet);
|
||||
return;
|
||||
}
|
||||
if (packet->IsSYN () && !packet->GetSeqn ()) // new incoming stream
|
||||
{
|
||||
uint32_t receiveStreamID = packet->GetReceiveStreamID ();
|
||||
|
@ -1166,16 +1236,22 @@ namespace stream
|
|||
{
|
||||
auto s = std::make_shared<Stream> (m_Owner->GetService (), *this, remote, port);
|
||||
std::unique_lock<std::mutex> l(m_StreamsMutex);
|
||||
m_Streams[s->GetRecvStreamID ()] = s;
|
||||
m_Streams.emplace (s->GetRecvStreamID (), 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)
|
||||
{
|
||||
auto s = std::make_shared<Stream> (m_Owner->GetService (), *this);
|
||||
std::unique_lock<std::mutex> l(m_StreamsMutex);
|
||||
m_Streams[s->GetRecvStreamID ()] = s;
|
||||
m_IncomingStreams[receiveStreamID] = s;
|
||||
m_Streams.emplace (s->GetRecvStreamID (), s);
|
||||
m_IncomingStreams.emplace (receiveStreamID, s);
|
||||
return s;
|
||||
}
|
||||
|
||||
|
@ -1186,6 +1262,7 @@ namespace stream
|
|||
std::unique_lock<std::mutex> l(m_StreamsMutex);
|
||||
m_Streams.erase (stream->GetRecvStreamID ());
|
||||
m_IncomingStreams.erase (stream->GetSendStreamID ());
|
||||
if (m_LastStream == stream) m_LastStream = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1270,13 +1347,17 @@ namespace stream
|
|||
std::shared_ptr<I2NPMessage> StreamingDestination::CreateDataMessage (
|
||||
const uint8_t * payload, size_t len, uint16_t toPort, bool checksum)
|
||||
{
|
||||
size_t size;
|
||||
auto msg = m_I2NPMsgsPool.AcquireShared ();
|
||||
uint8_t * buf = msg->GetPayload ();
|
||||
buf += 4; // reserve for lengthlength
|
||||
msg->len += 4;
|
||||
size_t size = (!m_Gzip || len <= i2p::stream::COMPRESSION_THRESHOLD_SIZE)?
|
||||
i2p::data::GzipNoCompression (payload, len, buf, msg->maxLen - msg->len):
|
||||
m_Deflator.Deflate (payload, len, buf, msg->maxLen - msg->len);
|
||||
|
||||
if (m_Gzip && m_Deflator)
|
||||
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)
|
||||
{
|
||||
htobe32buf (msg->GetPayload (), size); // length
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
@ -11,7 +11,7 @@
|
|||
|
||||
#include <inttypes.h>
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <unordered_map>
|
||||
#include <set>
|
||||
#include <queue>
|
||||
#include <functional>
|
||||
|
@ -152,7 +152,8 @@ namespace stream
|
|||
eStreamStatusOpen,
|
||||
eStreamStatusReset,
|
||||
eStreamStatusClosing,
|
||||
eStreamStatusClosed
|
||||
eStreamStatusClosed,
|
||||
eStreamStatusTerminated
|
||||
};
|
||||
|
||||
class StreamingDestination;
|
||||
|
@ -178,6 +179,7 @@ namespace stream
|
|||
void HandlePing (Packet * packet);
|
||||
size_t Send (const uint8_t * buf, size_t len);
|
||||
void AsyncSend (const uint8_t * buf, size_t len, SendHandler handler);
|
||||
void SendPing ();
|
||||
|
||||
template<typename Buffer, typename ReceiveHandler>
|
||||
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;
|
||||
|
||||
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 ();
|
||||
|
||||
void Start ();
|
||||
void Stop ();
|
||||
|
||||
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);
|
||||
bool DeleteStream (uint32_t recvStreamID);
|
||||
void SetAcceptor (const Acceptor& acceptor);
|
||||
|
@ -297,12 +300,13 @@ namespace stream
|
|||
uint16_t m_LocalPort;
|
||||
bool m_Gzip; // gzip compression of data messages
|
||||
std::mutex m_StreamsMutex;
|
||||
std::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_Streams; // sendStreamID->stream
|
||||
std::unordered_map<uint32_t, std::shared_ptr<Stream> > m_IncomingStreams; // receiveStreamID->stream
|
||||
std::shared_ptr<Stream> m_LastStream;
|
||||
Acceptor m_Acceptor;
|
||||
std::list<std::shared_ptr<Stream> > m_PendingIncomingStreams;
|
||||
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<I2NPMessageBuffer<I2NP_MAX_MESSAGE_SIZE> > m_I2NPMsgsPool;
|
||||
|
@ -310,7 +314,7 @@ namespace stream
|
|||
public:
|
||||
|
||||
i2p::data::GzipInflator m_Inflator;
|
||||
i2p::data::GzipDeflator m_Deflator;
|
||||
std::unique_ptr<i2p::data::GzipDeflator> m_Deflator;
|
||||
|
||||
// for HTTP only
|
||||
const decltype(m_Streams)& GetStreams () const { return m_Streams; };
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
@ -16,6 +16,7 @@
|
|||
#include <boost/algorithm/string.hpp>
|
||||
#include "Config.h"
|
||||
#include "Log.h"
|
||||
#include "RouterContext.h"
|
||||
#include "I2PEndian.h"
|
||||
#include "Timestamp.h"
|
||||
#include "util.h"
|
||||
|
@ -60,14 +61,43 @@ namespace util
|
|||
{
|
||||
LogPrint (eLogInfo, "Timestamp: NTP request to ", address);
|
||||
boost::asio::io_service service;
|
||||
boost::asio::ip::udp::resolver::query query (boost::asio::ip::udp::v4 (), address, "ntp");
|
||||
boost::system::error_code ec;
|
||||
auto it = boost::asio::ip::udp::resolver (service).resolve (query, ec);
|
||||
if (!ec && it != boost::asio::ip::udp::resolver::iterator())
|
||||
auto it = boost::asio::ip::udp::resolver (service).resolve (
|
||||
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);
|
||||
socket.open (boost::asio::ip::udp::v4 (), ec);
|
||||
socket.open (ep.protocol (), ec);
|
||||
if (!ec)
|
||||
{
|
||||
uint8_t buf[48];// 48 bytes NTP request/response
|
||||
|
@ -103,7 +133,7 @@ namespace util
|
|||
LogPrint (eLogError, "Timestamp: Couldn't open UDP socket");
|
||||
}
|
||||
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)
|
||||
|
|
|
@ -39,7 +39,7 @@ namespace tunnel
|
|||
|
||||
void TransitTunnelParticipant::HandleTunnelDataMsg (std::shared_ptr<const i2p::I2NPMessage> tunnelMsg)
|
||||
{
|
||||
auto newMsg = CreateEmptyTunnelDataMsg ();
|
||||
auto newMsg = CreateEmptyTunnelDataMsg (false);
|
||||
EncryptTunnelMsg (tunnelMsg, newMsg);
|
||||
|
||||
m_NumTransmittedBytes += tunnelMsg->GetLength ();
|
||||
|
@ -87,7 +87,7 @@ namespace tunnel
|
|||
|
||||
void TransitTunnelEndpoint::HandleTunnelDataMsg (std::shared_ptr<const i2p::I2NPMessage> tunnelMsg)
|
||||
{
|
||||
auto newMsg = CreateEmptyTunnelDataMsg ();
|
||||
auto newMsg = CreateEmptyTunnelDataMsg (true);
|
||||
EncryptTunnelMsg (tunnelMsg, newMsg);
|
||||
|
||||
LogPrint (eLogDebug, "TransitTunnel: handle msg for endpoint ", GetTunnelID ());
|
||||
|
|
|
@ -401,7 +401,7 @@ namespace transport
|
|||
try
|
||||
{
|
||||
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);
|
||||
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;
|
||||
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 ();
|
||||
if (address && m_CheckReserved && i2p::util::net::IsInReservedRange(address->host))
|
||||
|
@ -457,7 +457,7 @@ namespace transport
|
|||
}
|
||||
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 ();
|
||||
if (address && m_CheckReserved && i2p::util::net::IsInReservedRange(address->host))
|
||||
|
@ -485,7 +485,7 @@ namespace transport
|
|||
std::shared_ptr<const RouterInfo::Address> address;
|
||||
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 ();
|
||||
if (address && m_CheckReserved && i2p::util::net::IsInReservedRange(address->host))
|
||||
|
@ -495,7 +495,7 @@ namespace transport
|
|||
}
|
||||
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);
|
||||
if (address && m_CheckReserved && i2p::util::net::IsInReservedRange(address->host))
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
@ -23,6 +23,7 @@
|
|||
#include "Tunnel.h"
|
||||
#include "TunnelPool.h"
|
||||
#include "util.h"
|
||||
#include "ECIESX25519AEADRatchetSession.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
|
@ -30,8 +31,9 @@ namespace tunnel
|
|||
{
|
||||
Tunnel::Tunnel (std::shared_ptr<const TunnelConfig> config):
|
||||
TunnelBase (config->GetTunnelID (), config->GetNextTunnelID (), config->GetNextIdentHash ()),
|
||||
m_Config (config), m_Pool (nullptr), m_State (eTunnelStatePending), m_IsRecreated (false),
|
||||
m_Latency (0)
|
||||
m_Config (config), m_IsShortBuildMessage (false), m_Pool (nullptr),
|
||||
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)
|
||||
{
|
||||
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 ();
|
||||
*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
|
||||
std::vector<int> recordIndicies;
|
||||
for (int i = 0; i < numRecords; i++) recordIndicies.push_back(i);
|
||||
|
@ -55,7 +58,6 @@ namespace tunnel
|
|||
uint8_t * records = msg->GetPayload () + 1;
|
||||
TunnelHopConfig * hop = m_Config->GetFirstHop ();
|
||||
int i = 0;
|
||||
BN_CTX * ctx = BN_CTX_new ();
|
||||
while (hop)
|
||||
{
|
||||
uint32_t msgID;
|
||||
|
@ -63,80 +65,89 @@ namespace tunnel
|
|||
RAND_bytes ((uint8_t *)&msgID, 4);
|
||||
else
|
||||
msgID = replyMsgID;
|
||||
int idx = recordIndicies[i];
|
||||
hop->CreateBuildRequestRecord (records + idx*TUNNEL_BUILD_RECORD_SIZE, msgID, ctx);
|
||||
hop->recordIndex = idx;
|
||||
i++;
|
||||
hop->recordIndex = recordIndicies[i]; i++;
|
||||
hop->CreateBuildRequestRecord (records, msgID);
|
||||
hop = hop->next;
|
||||
}
|
||||
BN_CTX_free (ctx);
|
||||
// fill up fake records with random data
|
||||
for (int i = numHops; i < numRecords; 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
|
||||
i2p::crypto::CBCDecryption decryption;
|
||||
hop = m_Config->GetLastHop ()->prev;
|
||||
while (hop)
|
||||
{
|
||||
decryption.SetKey (hop->replyKey);
|
||||
// decrypt records after current hop
|
||||
TunnelHopConfig * hop1 = hop->next;
|
||||
while (hop1)
|
||||
{
|
||||
decryption.SetIV (hop->replyIV);
|
||||
uint8_t * record = records + hop1->recordIndex*TUNNEL_BUILD_RECORD_SIZE;
|
||||
decryption.Decrypt(record, TUNNEL_BUILD_RECORD_SIZE, record);
|
||||
hop->DecryptRecord (records, hop1->recordIndex);
|
||||
hop1 = hop1->next;
|
||||
}
|
||||
hop = hop->prev;
|
||||
}
|
||||
msg->FillI2NPMessageHeader (eI2NPVariableTunnelBuild);
|
||||
msg->FillI2NPMessageHeader (m_Config->IsShort () ? eI2NPShortTunnelBuild : eI2NPVariableTunnelBuild);
|
||||
|
||||
// send message
|
||||
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);
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
bool Tunnel::HandleTunnelBuildResponse (uint8_t * msg, size_t len)
|
||||
{
|
||||
LogPrint (eLogDebug, "Tunnel: TunnelBuildResponse ", (int)msg[0], " records.");
|
||||
|
||||
i2p::crypto::CBCDecryption decryption;
|
||||
TunnelHopConfig * hop = m_Config->GetLastHop ();
|
||||
while (hop)
|
||||
{
|
||||
decryption.SetKey (hop->replyKey);
|
||||
// decrypt records before and current hop
|
||||
TunnelHopConfig * hop1 = hop;
|
||||
// decrypt current hop
|
||||
if (hop->recordIndex >= 0 && hop->recordIndex < msg[0])
|
||||
{
|
||||
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)
|
||||
{
|
||||
auto idx = hop1->recordIndex;
|
||||
if (idx >= 0 && idx < msg[0])
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
hop->DecryptRecord (msg + 1, idx);
|
||||
else
|
||||
LogPrint (eLogWarning, "Tunnel: hop index ", idx, " is out of range");
|
||||
hop1 = hop1->prev;
|
||||
|
@ -148,8 +159,7 @@ namespace tunnel
|
|||
hop = m_Config->GetFirstHop ();
|
||||
while (hop)
|
||||
{
|
||||
const uint8_t * record = msg + 1 + hop->recordIndex*TUNNEL_BUILD_RECORD_SIZE;
|
||||
uint8_t ret = record[hop->IsECIES () ? ECIES_BUILD_RESPONSE_RECORD_RET_OFFSET : BUILD_RESPONSE_RECORD_RET_OFFSET];
|
||||
uint8_t ret = hop->GetRetCode (msg + 1);
|
||||
LogPrint (eLogDebug, "Tunnel: Build response ret code=", (int)ret);
|
||||
auto profile = i2p::data::netdb.FindRouterProfile (hop->ident->GetIdentHash ());
|
||||
if (profile)
|
||||
|
@ -171,6 +181,8 @@ namespace tunnel
|
|||
m_Hops.push_back (std::unique_ptr<TunnelHop>(tunnelHop));
|
||||
hop = hop->prev;
|
||||
}
|
||||
m_IsShortBuildMessage = m_Config->IsShort ();
|
||||
m_FarEndTransports = m_Config->GetFarEndTransports ();
|
||||
m_Config = nullptr;
|
||||
}
|
||||
if (established) m_State = eTunnelStateEstablished;
|
||||
|
@ -234,7 +246,7 @@ namespace tunnel
|
|||
void InboundTunnel::HandleTunnelDataMsg (std::shared_ptr<const I2NPMessage> msg)
|
||||
{
|
||||
if (IsFailed ()) SetState (eTunnelStateEstablished); // incoming messages means a tunnel is alive
|
||||
auto newMsg = CreateEmptyTunnelDataMsg ();
|
||||
auto newMsg = CreateEmptyTunnelDataMsg (true);
|
||||
EncryptTunnelMsg (msg, newMsg);
|
||||
newMsg->from = shared_from_this ();
|
||||
m_Endpoint.HandleDecryptedTunnelDataMsg (newMsg);
|
||||
|
@ -319,10 +331,11 @@ namespace tunnel
|
|||
for (auto& msg : msgs)
|
||||
{
|
||||
if (!msg.data) continue;
|
||||
m_NumSentBytes += msg.data->GetLength ();
|
||||
switch (msg.deliveryType)
|
||||
{
|
||||
case eDeliveryTypeLocal:
|
||||
i2p::HandleI2NPMessage (msg.data);
|
||||
HandleI2NPMessage (msg.data);
|
||||
break;
|
||||
case eDeliveryTypeTunnel:
|
||||
i2p::transport::transports.SendMessage (msg.hash, i2p::CreateTunnelGatewayMsg (msg.tunnelID, msg.data));
|
||||
|
@ -517,6 +530,8 @@ namespace tunnel
|
|||
}
|
||||
case eI2NPVariableTunnelBuild:
|
||||
case eI2NPVariableTunnelBuildReply:
|
||||
case eI2NPShortTunnelBuild:
|
||||
case eI2NPShortTunnelBuildReply:
|
||||
case eI2NPTunnelBuild:
|
||||
case eI2NPTunnelBuildReply:
|
||||
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
|
||||
if (pool && tunnel->GetNumHops() == pool->GetNumOutboundHops())
|
||||
{
|
||||
tunnel->SetIsRecreated ();
|
||||
tunnel->SetRecreated (true);
|
||||
pool->RecreateOutboundTunnel (tunnel);
|
||||
}
|
||||
}
|
||||
|
@ -703,7 +718,7 @@ namespace tunnel
|
|||
LogPrint (eLogDebug, "Tunnel: creating one hop outbound tunnel");
|
||||
CreateTunnel<OutboundTunnel> (
|
||||
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
|
||||
if (pool && tunnel->GetNumHops() == pool->GetNumInboundHops())
|
||||
{
|
||||
tunnel->SetIsRecreated ();
|
||||
tunnel->SetRecreated (true);
|
||||
pool->RecreateInboundTunnel (tunnel);
|
||||
}
|
||||
}
|
||||
|
@ -752,8 +767,8 @@ namespace tunnel
|
|||
if (m_InboundTunnels.empty ())
|
||||
{
|
||||
LogPrint (eLogDebug, "Tunnel: Creating zero hops inbound tunnel");
|
||||
CreateZeroHopsInboundTunnel ();
|
||||
CreateZeroHopsOutboundTunnel ();
|
||||
CreateZeroHopsInboundTunnel (nullptr);
|
||||
CreateZeroHopsOutboundTunnel (nullptr);
|
||||
if (!m_ExploratoryPool)
|
||||
{
|
||||
int ibLen; i2p::config::GetOption("exploratory.inbound.length", ibLen);
|
||||
|
@ -779,7 +794,7 @@ namespace tunnel
|
|||
}
|
||||
LogPrint (eLogDebug, "Tunnel: creating one hop inbound tunnel");
|
||||
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>
|
||||
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);
|
||||
newTunnel->SetTunnelPool (pool);
|
||||
uint32_t replyMsgID;
|
||||
RAND_bytes ((uint8_t *)&replyMsgID, 4);
|
||||
AddPendingTunnel (replyMsgID, newTunnel);
|
||||
|
@ -835,20 +852,21 @@ namespace tunnel
|
|||
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)
|
||||
return CreateTunnel<InboundTunnel>(config, outboundTunnel);
|
||||
return CreateTunnel<InboundTunnel>(config, pool, outboundTunnel);
|
||||
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)
|
||||
return CreateTunnel<OutboundTunnel>(config);
|
||||
return CreateTunnel<OutboundTunnel>(config, pool);
|
||||
else
|
||||
return CreateZeroHopsOutboundTunnel ();
|
||||
return CreateZeroHopsOutboundTunnel (pool);
|
||||
}
|
||||
|
||||
void Tunnels::AddPendingTunnel (uint32_t replyMsgID, std::shared_ptr<InboundTunnel> tunnel)
|
||||
|
@ -882,7 +900,7 @@ namespace tunnel
|
|||
{
|
||||
// build symmetric outbound tunnel
|
||||
CreateTunnel<OutboundTunnel> (std::make_shared<TunnelConfig>(newTunnel->GetInvertedPeers (),
|
||||
newTunnel->GetNextTunnelID (), newTunnel->GetNextIdentHash ()),
|
||||
newTunnel->GetNextTunnelID (), newTunnel->GetNextIdentHash (), false), nullptr,
|
||||
GetNextOutboundTunnel ());
|
||||
}
|
||||
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> ();
|
||||
inboundTunnel->SetTunnelPool (pool);
|
||||
inboundTunnel->SetState (eTunnelStateEstablished);
|
||||
m_InboundTunnels.push_back (inboundTunnel);
|
||||
m_Tunnels[inboundTunnel->GetTunnelID ()] = 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> ();
|
||||
outboundTunnel->SetTunnelPool (pool);
|
||||
outboundTunnel->SetState (eTunnelStateEstablished);
|
||||
m_OutboundTunnels.push_back (outboundTunnel);
|
||||
// we don't insert into m_Tunnels
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
@ -71,12 +71,13 @@ namespace tunnel
|
|||
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> > GetInvertedPeers () const;
|
||||
bool IsShortBuildMessage () const { return m_IsShortBuildMessage; };
|
||||
TunnelState GetState () const { return m_State; };
|
||||
void SetState (TunnelState state);
|
||||
bool IsEstablished () const { return m_State == eTunnelStateEstablished; };
|
||||
bool IsFailed () const { return m_State == eTunnelStateFailed; };
|
||||
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 (); };
|
||||
virtual bool IsInbound() const = 0;
|
||||
|
||||
|
@ -109,9 +110,11 @@ namespace tunnel
|
|||
|
||||
std::shared_ptr<const TunnelConfig> m_Config;
|
||||
std::vector<std::unique_ptr<TunnelHop> > m_Hops;
|
||||
bool m_IsShortBuildMessage;
|
||||
std::shared_ptr<TunnelPool> m_Pool; // pool, tunnel belongs to, or null
|
||||
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
|
||||
};
|
||||
|
||||
|
@ -205,8 +208,8 @@ namespace tunnel
|
|||
void AddTransitTunnel (std::shared_ptr<TransitTunnel> tunnel);
|
||||
void AddOutboundTunnel (std::shared_ptr<OutboundTunnel> 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<OutboundTunnel> CreateOutboundTunnel (std::shared_ptr<TunnelConfig> config);
|
||||
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<TunnelPool> pool);
|
||||
void PostTunnelData (std::shared_ptr<I2NPMessage> msg);
|
||||
void PostTunnelData (const std::vector<std::shared_ptr<I2NPMessage> >& msgs);
|
||||
void AddPendingTunnel (uint32_t replyMsgID, std::shared_ptr<InboundTunnel> tunnel);
|
||||
|
@ -219,7 +222,8 @@ namespace tunnel
|
|||
private:
|
||||
|
||||
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>
|
||||
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 ManageTunnelPools (uint64_t ts);
|
||||
|
||||
std::shared_ptr<ZeroHopsInboundTunnel> CreateZeroHopsInboundTunnel ();
|
||||
std::shared_ptr<ZeroHopsOutboundTunnel> CreateZeroHopsOutboundTunnel ();
|
||||
std::shared_ptr<ZeroHopsInboundTunnel> CreateZeroHopsInboundTunnel (std::shared_ptr<TunnelPool> pool);
|
||||
std::shared_ptr<ZeroHopsOutboundTunnel> CreateZeroHopsOutboundTunnel (std::shared_ptr<TunnelPool> pool);
|
||||
|
||||
private:
|
||||
|
||||
|
|
|
@ -23,10 +23,6 @@ namespace tunnel
|
|||
{
|
||||
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);
|
||||
if (!tunnelID) tunnelID = 1; // tunnelID can't be zero
|
||||
isGateway = true;
|
||||
|
@ -78,74 +74,177 @@ 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 flag = 0;
|
||||
if (isGateway) flag |= TUNNEL_BUILD_RECORD_GATEWAY_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];
|
||||
htobe32buf (clearText + ECIES_BUILD_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET, tunnelID);
|
||||
htobe32buf (clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET, nextTunnelID);
|
||||
memcpy (clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET, nextIdent, 32);
|
||||
memcpy (clearText + ECIES_BUILD_REQUEST_RECORD_LAYER_KEY_OFFSET, layerKey, 32);
|
||||
memcpy (clearText + ECIES_BUILD_REQUEST_RECORD_IV_KEY_OFFSET, ivKey, 32);
|
||||
memcpy (clearText + ECIES_BUILD_REQUEST_RECORD_REPLY_KEY_OFFSET, replyKey, 32);
|
||||
memcpy (clearText + ECIES_BUILD_REQUEST_RECORD_REPLY_IV_OFFSET, replyIV, 16);
|
||||
clearText[ECIES_BUILD_REQUEST_RECORD_FLAG_OFFSET] = flag;
|
||||
memset (clearText + ECIES_BUILD_REQUEST_RECORD_MORE_FLAGS_OFFSET, 0, 3); // set to 0 for compatibility
|
||||
htobe32buf (clearText + ECIES_BUILD_REQUEST_RECORD_REQUEST_TIME_OFFSET, i2p::util::GetMinutesSinceEpoch ());
|
||||
htobe32buf (clearText + ECIES_BUILD_REQUEST_RECORD_REQUEST_EXPIRATION_OFFSET, 600); // +10 minutes
|
||||
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);
|
||||
if (encryptor)
|
||||
EncryptECIES (encryptor, clearText, record + BUILD_REQUEST_RECORD_ENCRYPTED_OFFSET, ctx);
|
||||
}
|
||||
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);
|
||||
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 TunnelHopConfig::EncryptECIES (std::shared_ptr<i2p::crypto::CryptoKeyEncryptor>& encryptor,
|
||||
const uint8_t * plainText, uint8_t * encrypted, BN_CTX * ctx)
|
||||
void ECIESTunnelHopConfig::EncryptECIES (const uint8_t * plainText, size_t len, uint8_t * encrypted)
|
||||
{
|
||||
uint8_t hepk[32];
|
||||
encryptor->Encrypt (nullptr, hepk, nullptr, false);
|
||||
i2p::crypto::InitNoiseNState (*this, hepk);
|
||||
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 (hepk, sharedSecret); // x25519(sesk, hepk)
|
||||
ephemeralKeys->Agree (ident->GetEncryptionPublicKey (), sharedSecret); // x25519(sesk, hepk)
|
||||
MixKey (sharedSecret);
|
||||
uint8_t nonce[12];
|
||||
memset (nonce, 0, 12);
|
||||
if (!i2p::crypto::AEADChaCha20Poly1305 (plainText, ECIES_BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE, m_H, 32,
|
||||
m_CK + 32, nonce, encrypted, ECIES_BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE + 16, true)) // encrypt
|
||||
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, ECIES_BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE + 16); // h = SHA256(h || ciphertext)
|
||||
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;
|
||||
if (isGateway) flag |= TUNNEL_BUILD_RECORD_GATEWAY_FLAG;
|
||||
if (isEndpoint) flag |= TUNNEL_BUILD_RECORD_ENDPOINT_FLAG;
|
||||
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_NEXT_TUNNEL_OFFSET, nextTunnelID);
|
||||
memcpy (clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET, nextIdent, 32);
|
||||
memcpy (clearText + ECIES_BUILD_REQUEST_RECORD_LAYER_KEY_OFFSET, layerKey, 32);
|
||||
memcpy (clearText + ECIES_BUILD_REQUEST_RECORD_IV_KEY_OFFSET, ivKey, 32);
|
||||
memcpy (clearText + ECIES_BUILD_REQUEST_RECORD_REPLY_KEY_OFFSET, replyKey, 32);
|
||||
memcpy (clearText + ECIES_BUILD_REQUEST_RECORD_REPLY_IV_OFFSET, replyIV, 16);
|
||||
clearText[ECIES_BUILD_REQUEST_RECORD_FLAG_OFFSET] = flag;
|
||||
memset (clearText + ECIES_BUILD_REQUEST_RECORD_MORE_FLAGS_OFFSET, 0, 3); // set to 0 for compatibility
|
||||
htobe32buf (clearText + ECIES_BUILD_REQUEST_RECORD_REQUEST_TIME_OFFSET, i2p::util::GetMinutesSinceEpoch ());
|
||||
htobe32buf (clearText + ECIES_BUILD_REQUEST_RECORD_REQUEST_EXPIRATION_OFFSET, 600); // +10 minutes
|
||||
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);
|
||||
// encrypt
|
||||
uint8_t * record = records + recordIndex*TUNNEL_BUILD_RECORD_SIZE;
|
||||
EncryptECIES (clearText, ECIES_BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE, record + BUILD_REQUEST_RECORD_ENCRYPTED_OFFSET);
|
||||
memcpy (record + BUILD_REQUEST_RECORD_TO_PEER_OFFSET, (const uint8_t *)ident->GetIdentHash (), 16);
|
||||
}
|
||||
|
||||
bool LongECIESTunnelHopConfig::DecryptBuildResponseRecord (uint8_t * records) const
|
||||
{
|
||||
uint8_t * record = records + recordIndex*TUNNEL_BUILD_RECORD_SIZE;
|
||||
uint8_t nonce[12];
|
||||
memset (nonce, 0, 12);
|
||||
if (!DecryptECIES (m_CK, nonce, record, TUNNEL_BUILD_RECORD_SIZE, record))
|
||||
{
|
||||
LogPrint (eLogWarning, "Tunnel: Response AEAD decryption failed");
|
||||
return false;
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
*
|
||||
|
@ -18,7 +18,7 @@ namespace i2p
|
|||
{
|
||||
namespace tunnel
|
||||
{
|
||||
struct TunnelHopConfig: public i2p::crypto::NoiseSymmetricState
|
||||
struct TunnelHopConfig
|
||||
{
|
||||
std::shared_ptr<const i2p::data::IdentityEx> ident;
|
||||
i2p::data::IdentHash nextIdent;
|
||||
|
@ -33,30 +33,66 @@ namespace tunnel
|
|||
int recordIndex; // record # in tunnel build message
|
||||
|
||||
TunnelHopConfig (std::shared_ptr<const i2p::data::IdentityEx> r);
|
||||
virtual ~TunnelHopConfig () {};
|
||||
|
||||
void SetNextIdent (const i2p::data::IdentHash& ident);
|
||||
void SetReplyHop (uint32_t replyTunnelID, const i2p::data::IdentHash& replyIdent);
|
||||
void SetNext (TunnelHopConfig * n);
|
||||
void SetPrev (TunnelHopConfig * p);
|
||||
|
||||
bool IsECIES () const { return ident->GetCryptoKeyType () == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD; };
|
||||
void CreateBuildRequestRecord (uint8_t * record, uint32_t replyMsgID, BN_CTX * ctx);
|
||||
void EncryptECIES (std::shared_ptr<i2p::crypto::CryptoKeyEncryptor>& encryptor,
|
||||
const uint8_t * clearText, uint8_t * encrypted, BN_CTX * ctx);
|
||||
virtual uint8_t GetRetCode (const uint8_t * records) const = 0;
|
||||
virtual void CreateBuildRequestRecord (uint8_t * records, uint32_t replyMsgID) = 0;
|
||||
virtual bool DecryptBuildResponseRecord (uint8_t * records) const = 0;
|
||||
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
|
||||
{
|
||||
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);
|
||||
m_LastHop->SetNextIdent (i2p::context.GetIdentHash ());
|
||||
}
|
||||
|
||||
TunnelConfig (std::vector<std::shared_ptr<const i2p::data::IdentityEx> > peers,
|
||||
uint32_t replyTunnelID, const i2p::data::IdentHash& replyIdent) // outbound
|
||||
TunnelConfig (const std::vector<std::shared_ptr<const i2p::data::IdentityEx> >& peers,
|
||||
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);
|
||||
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
|
||||
{
|
||||
return m_FirstHop;
|
||||
|
@ -141,31 +184,20 @@ namespace tunnel
|
|||
protected:
|
||||
|
||||
// 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:
|
||||
|
||||
template<class 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;
|
||||
}
|
||||
void CreatePeers (const std::vector<std::shared_ptr<const i2p::data::IdentityEx> >& peers);
|
||||
|
||||
private:
|
||||
|
||||
TunnelHopConfig * m_FirstHop, * m_LastHop;
|
||||
bool m_IsShort;
|
||||
i2p::data::RouterInfo::CompatibleTransports m_FarEndTransports;
|
||||
};
|
||||
|
||||
class ZeroHopsTunnelConfig: public TunnelConfig
|
||||
|
|
|
@ -52,24 +52,25 @@ namespace tunnel
|
|||
bool isFollowOnFragment = flag & 0x80, isLastFragment = true;
|
||||
uint32_t msgID = 0;
|
||||
int fragmentNum = 0;
|
||||
TunnelMessageBlockEx m;
|
||||
if (!isFollowOnFragment)
|
||||
{
|
||||
// first fragment
|
||||
if (m_CurrentMsgID)
|
||||
AddIncompleteCurrentMessage (); // we have got a new message while previous is not complete
|
||||
|
||||
m.deliveryType = (TunnelDeliveryType)((flag >> 5) & 0x03);
|
||||
switch (m.deliveryType)
|
||||
m_CurrentMessage.deliveryType = (TunnelDeliveryType)((flag >> 5) & 0x03);
|
||||
switch (m_CurrentMessage.deliveryType)
|
||||
{
|
||||
case eDeliveryTypeLocal: // 0
|
||||
break;
|
||||
case eDeliveryTypeTunnel: // 1
|
||||
m.tunnelID = bufbe32toh (fragment);
|
||||
m_CurrentMessage.tunnelID = bufbe32toh (fragment);
|
||||
fragment += 4; // tunnelID
|
||||
m.hash = i2p::data::IdentHash (fragment);
|
||||
m_CurrentMessage.hash = i2p::data::IdentHash (fragment);
|
||||
fragment += 32; // hash
|
||||
break;
|
||||
case eDeliveryTypeRouter: // 2
|
||||
m.hash = i2p::data::IdentHash (fragment);
|
||||
m_CurrentMessage.hash = i2p::data::IdentHash (fragment);
|
||||
fragment += 32; // to hash
|
||||
break;
|
||||
default: ;
|
||||
|
@ -81,6 +82,7 @@ namespace tunnel
|
|||
// Message ID
|
||||
msgID = bufbe32toh (fragment);
|
||||
fragment += 4;
|
||||
m_CurrentMsgID = msgID;
|
||||
isLastFragment = false;
|
||||
}
|
||||
}
|
||||
|
@ -96,48 +98,58 @@ namespace tunnel
|
|||
uint16_t size = bufbe16toh (fragment);
|
||||
fragment += 2;
|
||||
|
||||
msg->offset = fragment - msg->buf;
|
||||
msg->len = msg->offset + size;
|
||||
if (msg->len > msg->maxLen)
|
||||
// handle fragment
|
||||
if (isFollowOnFragment)
|
||||
{
|
||||
LogPrint (eLogError, "TunnelMessage: fragment is too long ", (int)size);
|
||||
return;
|
||||
}
|
||||
if (fragment + size < decrypted + TUNNEL_DATA_ENCRYPTED_SIZE)
|
||||
{
|
||||
// this is not last message. we have to copy it
|
||||
m.data = NewI2NPTunnelMessage ();
|
||||
m.data->offset += TUNNEL_GATEWAY_HEADER_SIZE; // reserve room for TunnelGateway header
|
||||
m.data->len += TUNNEL_GATEWAY_HEADER_SIZE;
|
||||
*(m.data) = *msg;
|
||||
}
|
||||
else
|
||||
m.data = msg;
|
||||
|
||||
if (!isFollowOnFragment && isLastFragment)
|
||||
HandleNextMessage (m);
|
||||
else
|
||||
{
|
||||
if (msgID) // msgID is presented, assume message is fragmented
|
||||
// existing message
|
||||
if (m_CurrentMsgID && m_CurrentMsgID == msgID && m_CurrentMessage.nextFragmentNum == fragmentNum)
|
||||
HandleCurrenMessageFollowOnFragment (fragment, size, isLastFragment); // previous
|
||||
else
|
||||
{
|
||||
if (!isFollowOnFragment) // create new incomlete message
|
||||
{
|
||||
m.nextFragmentNum = 1;
|
||||
m.receiveTime = i2p::util::GetMillisecondsSinceEpoch ();
|
||||
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
|
||||
{
|
||||
m.nextFragmentNum = fragmentNum;
|
||||
HandleFollowOnFragment (msgID, isLastFragment, m);
|
||||
}
|
||||
HandleFollowOnFragment (msgID, isLastFragment, fragmentNum, fragment, size); // another
|
||||
m_CurrentMsgID = 0; m_CurrentMessage.data = nullptr;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// new message
|
||||
msg->offset = fragment - msg->buf;
|
||||
msg->len = msg->offset + size;
|
||||
// check message size
|
||||
if (msg->len > msg->maxLen)
|
||||
{
|
||||
LogPrint (eLogError, "TunnelMessage: fragment is too long ", (int)size);
|
||||
m_CurrentMsgID = 0; m_CurrentMessage.data = nullptr;
|
||||
return;
|
||||
}
|
||||
// create new or assign I2NP message
|
||||
if (fragment + size < decrypted + TUNNEL_DATA_ENCRYPTED_SIZE)
|
||||
{
|
||||
// this is not last message. we have to copy it
|
||||
m_CurrentMessage.data = NewI2NPTunnelMessage (true);
|
||||
*(m_CurrentMessage.data) = *msg;
|
||||
}
|
||||
else
|
||||
m_CurrentMessage.data = msg;
|
||||
|
||||
if (isLastFragment)
|
||||
{
|
||||
// single message
|
||||
HandleNextMessage (m_CurrentMessage);
|
||||
m_CurrentMsgID = 0; m_CurrentMessage.data = nullptr;
|
||||
}
|
||||
else if (msgID)
|
||||
{
|
||||
// first fragment of a new message
|
||||
m_CurrentMessage.nextFragmentNum = 1;
|
||||
m_CurrentMessage.receiveTime = i2p::util::GetMillisecondsSinceEpoch ();
|
||||
HandleOutOfSequenceFragments (msgID, m_CurrentMessage);
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint (eLogError, "TunnelMessage: Message is fragmented, but msgID is not presented");
|
||||
m_CurrentMsgID = 0; m_CurrentMessage.data = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
fragment += size;
|
||||
|
@ -147,27 +159,17 @@ namespace tunnel
|
|||
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);
|
||||
if (it != m_IncompleteMessages.end())
|
||||
{
|
||||
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)
|
||||
{
|
||||
// message complete
|
||||
|
@ -182,26 +184,86 @@ namespace tunnel
|
|||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint (eLogWarning, "TunnelMessage: Unexpected fragment ", (int)m.nextFragmentNum, " instead ", (int)msg.nextFragmentNum, " of message ", msgID, ", saved");
|
||||
AddOutOfSequenceFragment (msgID, m.nextFragmentNum, isLastFragment, m.data);
|
||||
LogPrint (eLogWarning, "TunnelMessage: Unexpected fragment ", (int)fragmentNum, " instead ", (int)msg.nextFragmentNum, " of message ", msgID, ", saved");
|
||||
AddOutOfSequenceFragment (msgID, fragmentNum, isLastFragment, fragment, size);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint (eLogWarning, "TunnelMessage: First fragment of message ", msgID, " not found, saved");
|
||||
AddOutOfSequenceFragment (msgID, m.nextFragmentNum, isLastFragment, m.data);
|
||||
LogPrint (eLogDebug, "TunnelMessage: First fragment of message ", msgID, " not found, saved");
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -212,7 +274,14 @@ namespace tunnel
|
|||
if (!msg.nextFragmentNum) // message complete
|
||||
{
|
||||
HandleNextMessage (msg);
|
||||
m_IncompleteMessages.erase (msgID);
|
||||
if (&msg == &m_CurrentMessage)
|
||||
{
|
||||
m_CurrentMsgID = 0;
|
||||
m_CurrentMessage.data = nullptr;
|
||||
}
|
||||
else
|
||||
m_IncompleteMessages.erase (msgID);
|
||||
LogPrint (eLogDebug, "TunnelMessage: All fragments of message ", msgID, " found");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -220,11 +289,11 @@ namespace tunnel
|
|||
|
||||
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 ())
|
||||
{
|
||||
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)
|
||||
{
|
||||
LogPrint (eLogWarning, "TunnelMessage: Tunnel endpoint I2NP message size ", msg.data->maxLen, " is not enough");
|
||||
|
@ -232,9 +301,9 @@ namespace tunnel
|
|||
*newMsg = *(msg.data);
|
||||
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);
|
||||
if (it->second.isLastFragment)
|
||||
if (it->second->isLastFragment)
|
||||
// message complete
|
||||
msg.nextFragmentNum = 0;
|
||||
else
|
||||
|
@ -287,7 +356,7 @@ namespace tunnel
|
|||
// out-of-sequence fragments
|
||||
for (auto it = m_OutOfSequenceFragments.begin (); it != m_OutOfSequenceFragments.end ();)
|
||||
{
|
||||
if (ts > it->second.receiveTime + i2p::I2NP_MESSAGE_EXPIRATION_TIMEOUT)
|
||||
if (ts > it->second->receiveTime + i2p::I2NP_MESSAGE_EXPIRATION_TIMEOUT)
|
||||
it = m_OutOfSequenceFragments.erase (it);
|
||||
else
|
||||
++it;
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
@ -10,8 +10,9 @@
|
|||
#define TUNNEL_ENDPOINT_H__
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include "I2NPProtocol.h"
|
||||
#include "TunnelBase.h"
|
||||
|
||||
|
@ -29,14 +30,15 @@ namespace tunnel
|
|||
|
||||
struct Fragment
|
||||
{
|
||||
Fragment (bool last, uint64_t t, size_t size): isLastFragment (last), receiveTime (t), data (size) {};
|
||||
bool isLastFragment;
|
||||
std::shared_ptr<I2NPMessage> data;
|
||||
uint64_t receiveTime; // milliseconds since epoch
|
||||
std::vector<uint8_t> data;
|
||||
};
|
||||
|
||||
public:
|
||||
|
||||
TunnelEndpoint (bool isInbound): m_IsInbound (isInbound), m_NumReceivedBytes (0) {};
|
||||
TunnelEndpoint (bool isInbound): m_IsInbound (isInbound), m_NumReceivedBytes (0), m_CurrentMsgID (0) {};
|
||||
~TunnelEndpoint ();
|
||||
size_t GetNumReceivedBytes () const { return m_NumReceivedBytes; };
|
||||
void Cleanup ();
|
||||
|
@ -45,19 +47,24 @@ namespace tunnel
|
|||
|
||||
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 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
|
||||
void HandleOutOfSequenceFragments (uint32_t msgID, TunnelMessageBlockEx& msg);
|
||||
void AddIncompleteCurrentMessage ();
|
||||
|
||||
private:
|
||||
|
||||
std::map<uint32_t, TunnelMessageBlockEx> m_IncompleteMessages;
|
||||
std::map<std::pair<uint32_t, uint8_t>, Fragment> m_OutOfSequenceFragments; // (msgID, fragment#)->fragment
|
||||
std::unordered_map<uint32_t, TunnelMessageBlockEx> m_IncompleteMessages;
|
||||
std::unordered_map<uint64_t, std::unique_ptr<Fragment> > m_OutOfSequenceFragments; // ((msgID << 8) + fragment#)->fragment
|
||||
bool m_IsInbound;
|
||||
size_t m_NumReceivedBytes;
|
||||
TunnelMessageBlockEx m_CurrentMessage;
|
||||
uint32_t m_CurrentMsgID;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -215,7 +215,7 @@ namespace tunnel
|
|||
const auto& tunnelDataMsgs = m_Buffer.GetTunnelDataMsgs ();
|
||||
for (auto& tunnelMsg : tunnelDataMsgs)
|
||||
{
|
||||
auto newMsg = CreateEmptyTunnelDataMsg ();
|
||||
auto newMsg = CreateEmptyTunnelDataMsg (false);
|
||||
m_Tunnel->EncryptTunnelMsg (tunnelMsg, newMsg);
|
||||
htobe32buf (newMsg->GetPayload (), m_Tunnel->GetNextTunnelID ());
|
||||
newMsg->FillI2NPMessageHeader (eI2NPTunnelData);
|
||||
|
|
|
@ -24,6 +24,22 @@ namespace i2p
|
|||
{
|
||||
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):
|
||||
m_NumInboundHops (numInboundHops), m_NumOutboundHops (numOutboundHops),
|
||||
m_NumInboundTunnels (numInboundTunnels), m_NumOutboundTunnels (numOutboundTunnels),
|
||||
|
@ -97,6 +113,17 @@ namespace tunnel
|
|||
if (!m_IsActive) return;
|
||||
{
|
||||
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);
|
||||
}
|
||||
if (m_LocalDestination)
|
||||
|
@ -420,16 +447,16 @@ namespace tunnel
|
|||
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;
|
||||
auto prevHop = i2p::context.GetSharedRouterInfo ();
|
||||
std::shared_ptr<const i2p::data::RouterInfo> prevHop = i2p::context.GetSharedRouterInfo ();
|
||||
if(i2p::transport::transports.RoutesRestricted())
|
||||
{
|
||||
/** if routes are restricted prepend trusted first hop */
|
||||
auto hop = i2p::transport::transports.GetRestrictedPeer();
|
||||
if(!hop) return false;
|
||||
peers.push_back(hop->GetRouterIdentity());
|
||||
path.Add (hop);
|
||||
prevHop = hop;
|
||||
start++;
|
||||
}
|
||||
|
@ -437,11 +464,11 @@ namespace tunnel
|
|||
(inbound && i2p::transport::transports.GetNumPeers () > 25))
|
||||
{
|
||||
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
|
||||
{
|
||||
prevHop = r;
|
||||
peers.push_back (r->GetRouterIdentity ());
|
||||
path.Add (r);
|
||||
start++;
|
||||
}
|
||||
}
|
||||
|
@ -453,6 +480,7 @@ namespace tunnel
|
|||
{
|
||||
LogPrint (eLogInfo, "Tunnels: Can't select first hop for a tunnel. Trying already connected");
|
||||
hop = i2p::transport::transports.GetRandomPeer ();
|
||||
if (hop && !hop->IsECIES ()) hop = nullptr;
|
||||
}
|
||||
if (!hop)
|
||||
{
|
||||
|
@ -466,12 +494,14 @@ namespace tunnel
|
|||
if (hop1) hop = hop1;
|
||||
}
|
||||
prevHop = hop;
|
||||
peers.push_back (hop->GetRouterIdentity ());
|
||||
path.Add (hop);
|
||||
if (i == numHops - 1)
|
||||
path.farEndTransports = hop->GetCompatibleTransports (inbound);
|
||||
}
|
||||
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;
|
||||
// peers is empty
|
||||
|
@ -480,27 +510,36 @@ namespace tunnel
|
|||
{
|
||||
std::lock_guard<std::mutex> lock(m_CustomPeerSelectorMutex);
|
||||
if (m_CustomPeerSelector)
|
||||
return m_CustomPeerSelector->SelectPeers(peers, numHops, isInbound);
|
||||
return m_CustomPeerSelector->SelectPeers(path, numHops, isInbound);
|
||||
}
|
||||
// explicit peers in use
|
||||
if (m_ExplicitPeers) return SelectExplicitPeers (peers, isInbound);
|
||||
return StandardSelectPeers(peers, numHops, isInbound, std::bind(&TunnelPool::SelectNextHop, this, std::placeholders::_1, std::placeholders::_2));
|
||||
if (m_ExplicitPeers) return SelectExplicitPeers (path, isInbound);
|
||||
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;
|
||||
if (numHops > (int)m_ExplicitPeers->size ()) numHops = m_ExplicitPeers->size ();
|
||||
if (!numHops) return false;
|
||||
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);
|
||||
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
|
||||
{
|
||||
LogPrint (eLogInfo, "Tunnels: Can't find router for ", ident.ToBase64 ());
|
||||
|
@ -517,17 +556,16 @@ namespace tunnel
|
|||
if (!outboundTunnel)
|
||||
outboundTunnel = tunnels.GetNextOutboundTunnel ();
|
||||
LogPrint (eLogDebug, "Tunnels: Creating destination inbound tunnel...");
|
||||
std::vector<std::shared_ptr<const i2p::data::IdentityEx> > peers;
|
||||
if (SelectPeers (peers, true))
|
||||
Path path;
|
||||
if (SelectPeers (path, true))
|
||||
{
|
||||
std::shared_ptr<TunnelConfig> config;
|
||||
if (m_NumInboundHops > 0)
|
||||
{
|
||||
std::reverse (peers.begin (), peers.end ());
|
||||
config = std::make_shared<TunnelConfig> (peers);
|
||||
path.Reverse ();
|
||||
config = std::make_shared<TunnelConfig> (path.peers, path.isShort, path.farEndTransports);
|
||||
}
|
||||
auto tunnel = tunnels.CreateInboundTunnel (config, outboundTunnel);
|
||||
tunnel->SetTunnelPool (shared_from_this ());
|
||||
auto tunnel = tunnels.CreateInboundTunnel (config, shared_from_this (), outboundTunnel);
|
||||
if (tunnel->IsEstablished ()) // zero hops
|
||||
TunnelCreated (tunnel);
|
||||
}
|
||||
|
@ -549,14 +587,15 @@ namespace tunnel
|
|||
std::shared_ptr<TunnelConfig> config;
|
||||
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);
|
||||
newTunnel->SetTunnelPool (shared_from_this());
|
||||
auto newTunnel = tunnels.CreateInboundTunnel (config, shared_from_this(), outboundTunnel);
|
||||
if (newTunnel->IsEstablished ()) // zero hops
|
||||
TunnelCreated (newTunnel);
|
||||
else
|
||||
newTunnel->SetRecreated (true);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -568,15 +607,27 @@ namespace tunnel
|
|||
if (inboundTunnel)
|
||||
{
|
||||
LogPrint (eLogDebug, "Tunnels: Creating destination outbound tunnel...");
|
||||
std::vector<std::shared_ptr<const i2p::data::IdentityEx> > peers;
|
||||
if (SelectPeers (peers, false))
|
||||
Path path;
|
||||
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;
|
||||
if (m_NumOutboundHops > 0)
|
||||
config = std::make_shared<TunnelConfig>(peers, inboundTunnel->GetNextTunnelID (), inboundTunnel->GetNextIdentHash ());
|
||||
auto tunnel = tunnels.CreateOutboundTunnel (config);
|
||||
tunnel->SetTunnelPool (shared_from_this ());
|
||||
if (tunnel->IsEstablished ()) // zero hops
|
||||
config = std::make_shared<TunnelConfig>(path.peers, inboundTunnel->GetNextTunnelID (),
|
||||
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 ());
|
||||
}
|
||||
else
|
||||
tunnel = tunnels.CreateOutboundTunnel (config, shared_from_this ());
|
||||
if (tunnel && tunnel->IsEstablished ()) // zero hops
|
||||
TunnelCreated (tunnel);
|
||||
}
|
||||
else
|
||||
|
@ -602,12 +653,12 @@ namespace tunnel
|
|||
std::shared_ptr<TunnelConfig> config;
|
||||
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)
|
||||
{
|
||||
auto newTunnel = tunnels.CreateOutboundTunnel (config);
|
||||
newTunnel->SetTunnelPool (shared_from_this ());
|
||||
auto newTunnel = tunnels.CreateOutboundTunnel (config, shared_from_this ());
|
||||
if (newTunnel->IsEstablished ()) // zero hops
|
||||
TunnelCreated (newTunnel);
|
||||
}
|
||||
|
@ -620,9 +671,9 @@ namespace tunnel
|
|||
{
|
||||
LogPrint (eLogDebug, "Tunnels: Creating paired inbound tunnel...");
|
||||
auto tunnel = tunnels.CreateInboundTunnel (
|
||||
m_NumOutboundHops > 0 ? std::make_shared<TunnelConfig>(outboundTunnel->GetInvertedPeers ()) : nullptr,
|
||||
outboundTunnel);
|
||||
tunnel->SetTunnelPool (shared_from_this ());
|
||||
m_NumOutboundHops > 0 ? std::make_shared<TunnelConfig>(outboundTunnel->GetInvertedPeers (),
|
||||
outboundTunnel->IsShortBuildMessage ()) : nullptr,
|
||||
shared_from_this (), outboundTunnel);
|
||||
if (tunnel->IsEstablished ()) // zero hops
|
||||
TunnelCreated (tunnel);
|
||||
}
|
||||
|
|
|
@ -36,7 +36,15 @@ namespace tunnel
|
|||
class OutboundTunnel;
|
||||
|
||||
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 */
|
||||
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;
|
||||
// standard peer selection algorithm
|
||||
bool StandardSelectPeers(Path & path, int hops, bool inbound, SelectHopFunc nextHop);
|
||||
bool StandardSelectPeers(Path & path, int numHops, bool inbound, SelectHopFunc nextHop);
|
||||
|
||||
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);
|
||||
template<class TTunnels>
|
||||
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 SelectExplicitPeers (std::vector<std::shared_ptr<const i2p::data::IdentityEx> >& hops, bool isInbound);
|
||||
bool SelectPeers (Path& path, bool isInbound);
|
||||
bool SelectExplicitPeers (Path& path, bool isInbound);
|
||||
|
||||
private:
|
||||
|
||||
|
|
|
@ -129,7 +129,7 @@ namespace util
|
|||
pthread_set_name_np(pthread_self(), name);
|
||||
#elif defined(__NetBSD__)
|
||||
pthread_setname_np(pthread_self(), "%s", (void *)name);
|
||||
#else
|
||||
#elif !defined(__gnu_hurd__)
|
||||
pthread_setname_np(pthread_self(), name);
|
||||
#endif
|
||||
}
|
||||
|
@ -344,7 +344,7 @@ namespace net
|
|||
if(fd > 0)
|
||||
{
|
||||
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)
|
||||
mtu = ifr.ifr_mtu; // MTU
|
||||
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 {
|
||||
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("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 ();
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
@ -50,6 +50,11 @@ namespace util
|
|||
|
||||
MemoryPool (): m_Head (nullptr) {}
|
||||
~MemoryPool ()
|
||||
{
|
||||
CleanUp ();
|
||||
}
|
||||
|
||||
void CleanUp ()
|
||||
{
|
||||
while (m_Head)
|
||||
{
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
@ -16,7 +16,7 @@
|
|||
#define MAKE_VERSION_NUMBER(a,b,c) ((a*100+b)*100+c)
|
||||
|
||||
#define I2PD_VERSION_MAJOR 2
|
||||
#define I2PD_VERSION_MINOR 38
|
||||
#define I2PD_VERSION_MINOR 39
|
||||
#define I2PD_VERSION_MICRO 0
|
||||
#define I2PD_VERSION_PATCH 0
|
||||
#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_MINOR 9
|
||||
#define I2P_VERSION_MICRO 50
|
||||
#define I2P_VERSION_MICRO 51
|
||||
#define I2P_VERSION_PATCH 0
|
||||
#define I2P_VERSION MAKE_VERSION(I2P_VERSION_MAJOR, I2P_VERSION_MINOR, I2P_VERSION_MICRO)
|
||||
#define I2P_VERSION_NUMBER MAKE_VERSION_NUMBER(I2P_VERSION_MAJOR, I2P_VERSION_MINOR, I2P_VERSION_MICRO)
|
||||
|
|
|
@ -470,6 +470,20 @@ namespace client
|
|||
if (pos != std::string::npos)
|
||||
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> ();
|
||||
if (!ident->FromBase64(addr)) {
|
||||
LogPrint (eLogError, "Addressbook: malformed address ", addr, " for ", name);
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
@ -185,7 +185,7 @@ namespace client
|
|||
LogPrint(eLogInfo, "Clients: stopping AddressBook");
|
||||
m_AddressBook.Stop ();
|
||||
|
||||
{
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_ForwardsMutex);
|
||||
m_ServerForwards.clear();
|
||||
m_ClientForwards.clear();
|
||||
|
@ -200,6 +200,8 @@ namespace client
|
|||
for (auto& it: m_Destinations)
|
||||
it.second->Stop ();
|
||||
m_Destinations.clear ();
|
||||
|
||||
m_SharedLocalDestination->Release ();
|
||||
m_SharedLocalDestination = nullptr;
|
||||
}
|
||||
|
||||
|
@ -209,14 +211,6 @@ namespace client
|
|||
/*std::string config; i2p::config::GetOption("conf", 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
|
||||
m_SharedLocalDestination->Release ();
|
||||
CreateNewSharedLocalDestination ();
|
||||
|
@ -225,6 +219,7 @@ namespace client
|
|||
if (m_HttpProxy)
|
||||
{
|
||||
m_HttpProxy->Stop ();
|
||||
delete m_HttpProxy;
|
||||
m_HttpProxy = nullptr;
|
||||
}
|
||||
ReadHttpProxy ();
|
||||
|
@ -233,10 +228,19 @@ namespace client
|
|||
if (m_SocksProxy)
|
||||
{
|
||||
m_SocksProxy->Stop ();
|
||||
delete m_SocksProxy;
|
||||
m_SocksProxy = nullptr;
|
||||
}
|
||||
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
|
||||
std::unique_lock<std::mutex> l(m_DestinationsMutex);
|
||||
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_ANSWER_PINGS] = GetI2CPOption(section, I2CP_PARAM_STREAMING_ANSWER_PINGS, isServer ? DEFAULT_ANSWER_PINGS : false);
|
||||
options[I2CP_PARAM_LEASESET_TYPE] = GetI2CPOption(section, I2CP_PARAM_LEASESET_TYPE, DEFAULT_LEASESET_TYPE);
|
||||
std::string encType = GetI2CPStringOption(section, I2CP_PARAM_LEASESET_ENCRYPTION_TYPE, 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;
|
||||
std::string privKey = GetI2CPStringOption(section, I2CP_PARAM_LEASESET_PRIV_KEY, "");
|
||||
if (privKey.length () > 0) options[I2CP_PARAM_LEASESET_PRIV_KEY] = privKey;
|
||||
|
@ -504,20 +508,15 @@ namespace client
|
|||
int numClientTunnels = 0, numServerTunnels = 0;
|
||||
std::string tunConf; i2p::config::GetOption("tunconf", tunConf);
|
||||
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);
|
||||
ReadTunnels (tunConf, numClientTunnels, numServerTunnels);
|
||||
|
||||
std::string tunDir; i2p::config::GetOption("tunnelsdir", tunDir);
|
||||
if (tunDir.empty ())
|
||||
tunDir = i2p::fs::DataDirPath ("tunnels.d");
|
||||
|
||||
if (i2p::fs::Exists (tunDir))
|
||||
{
|
||||
std::vector<std::string> files;
|
||||
|
@ -609,10 +608,18 @@ namespace client
|
|||
|
||||
bool gzip = section.second.get (I2P_CLIENT_TUNNEL_GZIP, true);
|
||||
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();
|
||||
numClientTunnels++;
|
||||
}
|
||||
else
|
||||
{
|
||||
ins.first->second->isUpdated = true;
|
||||
LogPrint(eLogError, "Clients: I2P Client forward for endpoint ", end, " already exists");
|
||||
}
|
||||
|
||||
} else {
|
||||
boost::asio::ip::tcp::endpoint clientEndpoint;
|
||||
|
@ -645,6 +652,13 @@ namespace client
|
|||
auto tun = std::make_shared<I2PClientTunnel> (name, dest, address, port, localDestination, destinationPort);
|
||||
clientTunnel = tun;
|
||||
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);
|
||||
|
@ -666,13 +680,16 @@ namespace client
|
|||
if (ins.first->second->GetLocalDestination () != clientTunnel->GetLocalDestination ())
|
||||
{
|
||||
LogPrint (eLogInfo, "Clients: I2P client tunnel destination updated");
|
||||
ins.first->second->Stop ();
|
||||
ins.first->second->SetLocalDestination (clientTunnel->GetLocalDestination ());
|
||||
ins.first->second->Start ();
|
||||
}
|
||||
ins.first->second->isUpdated = true;
|
||||
LogPrint (eLogInfo, "Clients: I2P client tunnel for endpoint ", clientEndpoint, " already exists");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else if (type == I2P_TUNNELS_SECTION_TYPE_SERVER
|
||||
|| type == I2P_TUNNELS_SECTION_TYPE_HTTP
|
||||
|| type == I2P_TUNNELS_SECTION_TYPE_IRC
|
||||
|
@ -689,7 +706,7 @@ namespace client
|
|||
accessList=section.second.get (I2P_SERVER_TUNNEL_WHITE_LIST, "");
|
||||
std::string hostOverride = section.second.get (I2P_SERVER_TUNNEL_HOST_OVERRIDE, "");
|
||||
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::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;
|
||||
auto it = destinations.find (keys);
|
||||
if (it != destinations.end ())
|
||||
{
|
||||
localDestination = it->second;
|
||||
localDestination->SetPublic (true);
|
||||
}
|
||||
else
|
||||
{
|
||||
i2p::data::PrivateKeys k;
|
||||
|
@ -715,14 +735,22 @@ namespace client
|
|||
localDestination = CreateNewLocalDestination (k, true, &options);
|
||||
destinations[keys] = localDestination;
|
||||
}
|
||||
else
|
||||
localDestination->SetPublic (true);
|
||||
}
|
||||
if (type == I2P_TUNNELS_SECTION_TYPE_UDPSERVER)
|
||||
{
|
||||
// udp server tunnel
|
||||
// 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);
|
||||
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);
|
||||
if(!isUniqueLocal)
|
||||
{
|
||||
|
@ -730,17 +758,19 @@ namespace client
|
|||
serverTunnel->SetUniqueLocal(isUniqueLocal);
|
||||
}
|
||||
std::lock_guard<std::mutex> lock(m_ForwardsMutex);
|
||||
if(m_ServerForwards.insert(
|
||||
std::make_pair(
|
||||
std::make_pair(
|
||||
localDestination->GetIdentHash(), port),
|
||||
serverTunnel)).second)
|
||||
auto ins = m_ServerForwards.insert(std::make_pair(
|
||||
std::make_pair(localDestination->GetIdentHash(), port),
|
||||
serverTunnel));
|
||||
if (ins.second)
|
||||
{
|
||||
serverTunnel->Start();
|
||||
LogPrint(eLogInfo, "Clients: I2P Server Forward created for UDP Endpoint ", host, ":", port, " bound on ", address, " for ",localDestination->GetIdentHash().ToBase32());
|
||||
}
|
||||
else
|
||||
LogPrint(eLogError, "Clients: I2P Server Forward for destination/port ", m_AddressBook.ToAddress(localDestination->GetIdentHash()), "/", port, "already exists");
|
||||
{
|
||||
ins.first->second->isUpdated = true;
|
||||
LogPrint(eLogError, "Clients: I2P Server Forward for destination/port ", m_AddressBook.ToAddress(localDestination->GetIdentHash()), "/", port, " already exists");
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
@ -789,7 +819,9 @@ namespace client
|
|||
if (ins.first->second->GetLocalDestination () != serverTunnel->GetLocalDestination ())
|
||||
{
|
||||
LogPrint (eLogInfo, "Clients: I2P server tunnel destination updated");
|
||||
ins.first->second->Stop ();
|
||||
ins.first->second->SetLocalDestination (serverTunnel->GetLocalDestination ());
|
||||
ins.first->second->Start ();
|
||||
}
|
||||
ins.first->second->isUpdated = true;
|
||||
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 VisitTunnelsContainer (Container& c, Visitor v)
|
||||
void ClientContext::VisitTunnels (bool clean)
|
||||
{
|
||||
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 = c.erase (it);
|
||||
}
|
||||
else
|
||||
it = m_ClientTunnels.erase(it);
|
||||
} else {
|
||||
it->second->isUpdated = false;
|
||||
it++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Visitor>
|
||||
void ClientContext::VisitTunnels (Visitor v)
|
||||
{
|
||||
VisitTunnelsContainer (m_ClientTunnels, v);
|
||||
VisitTunnelsContainer (m_ServerTunnels, v);
|
||||
// TODO: implement UDP forwards
|
||||
for (auto it = m_ServerTunnels.begin (); it != m_ServerTunnels.end ();)
|
||||
{
|
||||
if(clean && !it->second->isUpdated) {
|
||||
it->second->Stop ();
|
||||
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++;
|
||||
}
|
||||
} */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
@ -22,6 +22,7 @@
|
|||
#include "BOB.h"
|
||||
#include "I2CP.h"
|
||||
#include "AddressBook.h"
|
||||
#include "I18N_langs.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
|
@ -47,6 +48,7 @@ namespace client
|
|||
const char I2P_CLIENT_TUNNEL_DESTINATION_PORT[] = "destinationport";
|
||||
const char I2P_CLIENT_TUNNEL_MATCH_TUNNELS[] = "matchtunnels";
|
||||
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_OVERRIDE[] = "hostoverride";
|
||||
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);
|
||||
|
||||
// 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:
|
||||
|
||||
void ReadTunnels ();
|
||||
|
@ -121,8 +127,7 @@ namespace client
|
|||
void CleanupUDP(const boost::system::error_code & ecode);
|
||||
void ScheduleCleanupUDP();
|
||||
|
||||
template<typename Visitor>
|
||||
void VisitTunnels (Visitor v); // Visitor: (I2PService *) -> bool, true means retain
|
||||
void VisitTunnels (bool clean);
|
||||
|
||||
void CreateNewSharedLocalDestination ();
|
||||
void AddLocalDestination (std::shared_ptr<ClientDestination> localDestination);
|
||||
|
@ -150,6 +155,9 @@ namespace client
|
|||
|
||||
std::unique_ptr<boost::asio::deadline_timer> m_CleanupUDPTimer;
|
||||
|
||||
// i18n
|
||||
std::shared_ptr<const i2p::i18n::Locale> m_Language;
|
||||
|
||||
public:
|
||||
|
||||
// for HTTP
|
||||
|
|
|
@ -295,7 +295,7 @@ namespace proxy {
|
|||
std::string full_url = m_RequestURL.to_string();
|
||||
std::stringstream ss;
|
||||
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());
|
||||
return true; /* request processed */
|
||||
}
|
||||
|
@ -304,8 +304,8 @@ namespace proxy {
|
|||
std::string full_url = m_RequestURL.to_string();
|
||||
std::stringstream ss;
|
||||
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 << jump << "&update=true\">" << tr("here") << "</a> " << tr("to update record") << ".";
|
||||
ss << tr("Click here to update record:") << " <a href=\"" << full_url << (full_url.find('?') != std::string::npos ? "&i2paddresshelper=" : "?i2paddresshelper=");
|
||||
ss << jump << "&update=true\">" << tr("Continue") << "</a>.";
|
||||
GenericProxyInfo(tr("Addresshelper found"), ss.str());
|
||||
return true; /* request processed */
|
||||
}
|
||||
|
@ -319,7 +319,7 @@ namespace proxy {
|
|||
auto pos = uri.find(":");
|
||||
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;
|
||||
}
|
||||
else
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
@ -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)
|
||||
return m_ECIESx25519Decryptor->Decrypt (encrypted, data, ctx, true);
|
||||
return m_ECIESx25519Decryptor->Decrypt (encrypted, data);
|
||||
if (m_Decryptor)
|
||||
return m_Decryptor->Decrypt (encrypted, data, ctx, true);
|
||||
return m_Decryptor->Decrypt (encrypted, data);
|
||||
else
|
||||
LogPrint (eLogError, "I2CP: decryptor is not set");
|
||||
return false;
|
||||
|
|
|
@ -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
|
||||
|
||||
// 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;
|
||||
const uint8_t * GetEncryptionPublicKey (i2p::data::CryptoKeyType keyType) const; // for 4 only
|
||||
std::shared_ptr<const i2p::data::IdentityEx> GetIdentity () const { return m_Identity; };
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
@ -303,7 +303,7 @@ namespace client
|
|||
m_ProxyConnectionSent = true;
|
||||
}
|
||||
else
|
||||
m_OutHeader << line << "\n";
|
||||
m_OutHeader << line << "\n";
|
||||
}
|
||||
}
|
||||
else
|
||||
|
@ -532,7 +532,7 @@ namespace client
|
|||
I2PClientTunnel::I2PClientTunnel (const std::string& name, const std::string& destination,
|
||||
const std::string& address, int port, std::shared_ptr<ClientDestination> localDestination, int destinationPort):
|
||||
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 ();
|
||||
GetAddress ();
|
||||
if (m_KeepAliveInterval)
|
||||
ScheduleKeepAliveTimer ();
|
||||
}
|
||||
|
||||
void I2PClientTunnel::Stop ()
|
||||
{
|
||||
TCPIPAcceptor::Stop();
|
||||
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 */
|
||||
|
@ -569,6 +579,31 @@ namespace client
|
|||
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,
|
||||
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)
|
||||
|
@ -605,7 +640,47 @@ namespace client
|
|||
{
|
||||
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);
|
||||
m_Endpoint.address (addr);
|
||||
Accept ();
|
||||
|
@ -848,7 +923,8 @@ namespace client
|
|||
LogPrint(eLogInfo, "UDPServer: done");
|
||||
}
|
||||
|
||||
void I2PUDPServerTunnel::Start() {
|
||||
void I2PUDPServerTunnel::Start()
|
||||
{
|
||||
m_LocalDest->Start();
|
||||
}
|
||||
|
||||
|
@ -888,7 +964,8 @@ namespace client
|
|||
RemotePort(remotePort), m_LastPort (0),
|
||||
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);
|
||||
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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
if(!m_RemoteIdent) {
|
||||
|
@ -1025,7 +1103,8 @@ namespace client
|
|||
LogPrint(eLogWarning, "UDP Client: not tracking udp session using port ", (int) toPort);
|
||||
}
|
||||
|
||||
I2PUDPClientTunnel::~I2PUDPClientTunnel() {
|
||||
I2PUDPClientTunnel::~I2PUDPClientTunnel()
|
||||
{
|
||||
auto dgram = m_LocalDest->GetDatagramDestination();
|
||||
if (dgram) dgram->ResetReceiver();
|
||||
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue