Codebase list i2pd / 73cbc51
Merge tag '2.39.0' 2.39.0 yangfl 2 years ago
94 changed file(s) with 4717 addition(s) and 1290 deletion(s). Raw diff Collapse all Expand all
99 - uses: actions/checkout@v2
1010 - name: Test in FreeBSD
1111 id: test
12 uses: vmactions/freebsd-vm@v0.1.2
12 uses: vmactions/freebsd-vm@v0.1.4
1313 with:
1414 usesh: true
1515 prepare: pkg install -y devel/cmake devel/gmake devel/boost-libs security/openssl net/miniupnpc
22 on: [push, pull_request]
33
44 jobs:
5 build:
6 name: With USE_UPNP=${{ matrix.with_upnp }}
7 runs-on: ubuntu-16.04
5 build-make:
6 name: Make with USE_UPNP=${{ matrix.with_upnp }}
7 runs-on: ubuntu-18.04
88 strategy:
99 fail-fast: true
1010 matrix:
1818 sudo apt-get install build-essential libboost1.74-dev libminiupnpc-dev libssl-dev zlib1g-dev
1919 - name: build application
2020 run: make USE_UPNP=${{ matrix.with_upnp }} -j3
21 build-cmake:
22 name: CMake with -DWITH_UPNP=${{ matrix.with_upnp }}
23 runs-on: ubuntu-18.04
24 strategy:
25 fail-fast: true
26 matrix:
27 with_upnp: ['ON', 'OFF']
28 steps:
29 - uses: actions/checkout@v2
30 - name: install packages
31 run: |
32 sudo add-apt-repository ppa:mhier/libboost-latest
33 sudo apt-get update
34 sudo apt-get install build-essential cmake libboost1.74-dev libminiupnpc-dev libssl-dev zlib1g-dev
35 - name: build application
36 run: |
37 cd build
38 cmake -DWITH_UPNP=${{ matrix.with_upnp }} .
39 make -j3
40 build-deb-stretch:
41 name: Build package for stretch
42 runs-on: ubuntu-latest
43 steps:
44 - uses: actions/checkout@v2
45 with:
46 fetch-depth: 0
47 - name: change debian changelog
48 run: |
49 sudo apt-get update
50 sudo apt-get install devscripts
51 debchange -v "`git describe --tags`-stretch" -M --distribution stretch "trunk build"
52 - uses: singingwolfboy/build-dpkg-stretch@v1
53 id: build
54 with:
55 args: --unsigned-source --unsigned-changes -b
56 - uses: actions/upload-artifact@v1
57 with:
58 name: ${{ steps.build.outputs.filename }}
59 path: ${{ steps.build.outputs.filename }}
60 - uses: actions/upload-artifact@v1
61 with:
62 name: ${{ steps.build.outputs.filename-dbgsym }}
63 path: ${{ steps.build.outputs.filename-dbgsym }}
64 build-deb-buster:
65 name: Build package for buster
66 runs-on: ubuntu-latest
67 steps:
68 - uses: actions/checkout@v2
69 with:
70 fetch-depth: 0
71 - name: change debian changelog
72 run: |
73 sudo apt-get update
74 sudo apt-get install devscripts
75 debchange -v "`git describe --tags`-buster" -M --distribution buster "trunk build"
76 - uses: singingwolfboy/build-dpkg-buster@v1
77 id: build
78 with:
79 args: --unsigned-source --unsigned-changes -b
80 - uses: actions/upload-artifact@v1
81 with:
82 name: ${{ steps.build.outputs.filename }}
83 path: ${{ steps.build.outputs.filename }}
84 - uses: actions/upload-artifact@v1
85 with:
86 name: ${{ steps.build.outputs.filename-dbgsym }}
87 path: ${{ steps.build.outputs.filename-dbgsym }}
0 name: Build containers
1
2 on: [push]
3
4 jobs:
5 docker:
6 runs-on: ubuntu-latest
7 permissions:
8 packages: write
9 contents: read
10
11 steps:
12 - name: Checkout
13 uses: actions/checkout@v2
14
15 - name: Set up QEMU
16 uses: docker/setup-qemu-action@v1
17
18 - name: Set up Docker Buildx
19 uses: docker/setup-buildx-action@v1
20
21 - name: Login to DockerHub
22 uses: docker/login-action@v1
23 with:
24 username: ${{ secrets.DOCKERHUB_USERNAME }}
25 password: ${{ secrets.DOCKERHUB_TOKEN }}
26
27 - name: Login to GitHub Container registry
28 uses: docker/login-action@v1
29 with:
30 registry: ghcr.io
31 username: ${{ github.actor }}
32 password: ${{ secrets.GITHUB_TOKEN }}
33
34 - name: Build and push trunk container
35 if: ${{ !startsWith(github.ref, 'refs/tags/') }}
36 uses: docker/build-push-action@v2
37 with:
38 context: ./contrib/docker
39 file: ./contrib/docker/Dockerfile
40 platforms: linux/amd64,linux/386,linux/arm64,linux/arm/v7
41 push: true
42 tags: |
43 purplei2p/i2pd:latest
44 ghcr.io/purplei2p/i2pd:latest
45
46 - name: Set env
47 if: ${{ startsWith(github.ref, 'refs/tags/') }}
48 run: echo "RELEASE_VERSION=${GITHUB_REF:10}" >> $GITHUB_ENV
49
50 - name: Build and push release container
51 if: ${{ startsWith(github.ref, 'refs/tags/') }}
52 uses: docker/build-push-action@v2
53 with:
54 context: ./contrib/docker
55 file: ./contrib/docker/Dockerfile
56 platforms: linux/amd64,linux/386,linux/arm64,linux/arm/v7
57 push: true
58 tags: |
59 purplei2p/i2pd:latest
60 purplei2p/i2pd:release-${{ env.RELEASE_VERSION }}
61 ghcr.io/purplei2p/i2pd:latest
62 ghcr.io/purplei2p/i2pd:release-${{ env.RELEASE_VERSION }}
66 /i2pd
77 /libi2pd.a
88 /libi2pdclient.a
9 /libi2pdlang.a
910 /libi2pd.so
1011 /libi2pdclient.so
12 /libi2pdlang.so
13 /libi2pd.dll
14 /libi2pdclient.dll
15 /libi2pdlang.dll
1116 *.exe
1217
1318
+0
-54
.travis.yml less more
0 language: cpp
1 cache:
2 apt: true
3 os:
4 - linux
5 #- osx
6 dist: xenial
7 sudo: required
8 compiler:
9 - g++
10 - clang++
11 env:
12 global:
13 - MAKEFLAGS="-j 2"
14 matrix:
15 - BUILD_TYPE=make UPNP=ON MAKE_UPNP=yes
16 - BUILD_TYPE=make UPNP=OFF MAKE_UPNP=no
17 - BUILD_TYPE=cmake UPNP=ON MAKE_UPNP=yes
18 - BUILD_TYPE=cmake UPNP=OFF MAKE_UPNP=no
19 matrix:
20 exclude:
21 - os: osx
22 env: BUILD_TYPE=cmake UPNP=ON MAKE_UPNP=yes
23 - os: osx
24 env: BUILD_TYPE=cmake UPNP=OFF MAKE_UPNP=no
25 - os: linux
26 compiler: clang++
27 env: BUILD_TYPE=make UPNP=ON MAKE_UPNP=yes
28 - os: linux
29 compiler: clang++
30 env: BUILD_TYPE=make UPNP=OFF MAKE_UPNP=no
31 addons:
32 apt:
33 packages:
34 - build-essential
35 - cmake
36 - g++
37 - clang
38 - libboost-chrono-dev
39 - libboost-date-time-dev
40 - libboost-filesystem-dev
41 - libboost-program-options-dev
42 - libboost-system-dev
43 - libboost-thread-dev
44 - libminiupnpc-dev
45 - libssl-dev
46 before_install:
47 - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew update ; fi
48 - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew install libressl miniupnpc ; fi
49 - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew outdated boost || brew upgrade boost ; fi
50 script:
51 - if [[ "$TRAVIS_OS_NAME" == "linux" && "$BUILD_TYPE" == "cmake" ]]; then cd build && cmake -DCMAKE_BUILD_TYPE=Release -DWITH_UPNP=${UPNP} && make ; fi
52 - if [[ "$TRAVIS_OS_NAME" == "linux" && "$BUILD_TYPE" == "make" ]]; then make USE_UPNP=${MAKE_UPNP} ; fi
53 - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then make HOMEBREW=1 USE_UPNP=${MAKE_UPNP} ; fi
00 # for this file format description,
11 # see https://github.com/olivierlacan/keep-a-changelog
22
3 ## [2.38.0] - 2021-03-17
3 ## [2.39.0] - 2021-08-23
4 ### Added
5 - Short tunnel build messages
6 - Localization. To: Russian, Ukrainian, Turkmen, Uzbek and Afrikaans
7 - Custom CSS styles for webconsole
8 - Avoid slow tunnels with more than 250 ms per hop
9 - Process DELAY_REQUESTED streaming option
10 - "certsdir" options for certificates location
11 - Keep own RouterInfo in NetBb
12 - Pick ECIES routers only for tunnels on non-x64
13 - NTP sync through ipv6
14 - Allow ipv6 addresses for UDP server tunnels
15 ### Changed
16 - Rekey of all routers to ECIES
17 - Better distribution for random tunnel's peer selection
18 - Yggdrasil reseed for v0.4, added two more
19 - Encryption type 0,4 by default for server tunnels
20 - Handle i2cp.dontPublishLeaseSet param for all destinations
21 - reg.i2p for subscriptions
22 - LeaseSet type 3 by default
23 - Don't allocate payload buffer for every single ECIESx25519 message
24 - Prefer public ipv6 instead rfc4941
25 - Optimal padding for one-time ECIESx25519 message
26 - Don't send datetime block for one-time ECIESx25519 message with one-time key
27 - Router with expired introducer is still valid
28 - Don't disable floodfill if still reachable by ipv6
29 - Set minimal version for floodfill to 0.9.38
30 - Eliminate extra lookups for sequential fragments on tunnel endpoint
31 - Consistent path for explicit peers
32 - Always create new tunnel from exploratory pool
33 - Don't try to connect to a router not reachable from us
34 - Mark additional ipv6 addresses/nets as reserved (#1679)
35 ### Fixed
36 - Zero-hop tunnels
37 - Crash upon SAM session termination
38 - Build with boost < 1.55.0
39 - Address type for NTCP2 acceptors
40 - Check of ipv4/ipv6 address
41 - Request router to send to if not in NetDb
42 - Count outbound traffic for zero-hop tunnels
43 - URLdecode domain for registration string generator in webconsole
44
45 ## [2.38.0] - 2021-05-17
446 ### Added
547 - Publish ipv6 introducers
648 - Bind ipv6 or yggdrasil NTCP2 acceptor to specified address
00 SYS := $(shell $(CXX) -dumpmachine)
1 SHLIB := libi2pd.so
1
2 ifneq (, $(findstring darwin, $(SYS)))
3 SHARED_SUFFIX = dylib
4 else ifneq (, $(findstring mingw, $(SYS))$(findstring cygwin, $(SYS)))
5 SHARED_SUFFIX = dll
6 else
7 SHARED_SUFFIX = so
8 endif
9
10 SHLIB := libi2pd.$(SHARED_SUFFIX)
211 ARLIB := libi2pd.a
3 SHLIB_CLIENT := libi2pdclient.so
12 SHLIB_LANG := libi2pdlang.$(SHARED_SUFFIX)
13 ARLIB_LANG := libi2pdlang.a
14 SHLIB_CLIENT := libi2pdclient.$(SHARED_SUFFIX)
415 ARLIB_CLIENT := libi2pdclient.a
16 SHLIB_WRAP := libi2pdwrapper.$(SHARED_SUFFIX)
17 ARLIB_WRAP := libi2pdwrapper.a
518 I2PD := i2pd
619
720 LIB_SRC_DIR := libi2pd
821 LIB_CLIENT_SRC_DIR := libi2pd_client
22 WRAP_SRC_DIR := libi2pd_wrapper
23 LANG_SRC_DIR := i18n
924 DAEMON_SRC_DIR := daemon
1025
1126 # import source files lists
1227 include filelist.mk
1328
14 USE_AESNI := yes
15 USE_STATIC := no
16 USE_MESHNET := no
17 USE_UPNP := no
18 DEBUG := yes
29 USE_AESNI := $(or $(USE_AESNI),yes)
30 USE_STATIC := $(or $(USE_STATIC),no)
31 USE_MESHNET := $(or $(USE_MESHNET),no)
32 USE_UPNP := $(or $(USE_UPNP),no)
33 DEBUG := $(or $(DEBUG),yes)
1934
2035 ifeq ($(DEBUG),yes)
2136 CXX_DEBUG = -g
4863 NEEDED_CXXFLAGS += -DMESHNET
4964 endif
5065
51 NEEDED_CXXFLAGS += -MMD -MP -I$(LIB_SRC_DIR) -I$(LIB_CLIENT_SRC_DIR)
66 NEEDED_CXXFLAGS += -MMD -MP -I$(LIB_SRC_DIR) -I$(LIB_CLIENT_SRC_DIR) -I$(LANG_SRC_DIR)
5267
5368 LIB_OBJS += $(patsubst %.cpp,obj/%.o,$(LIB_SRC))
5469 LIB_CLIENT_OBJS += $(patsubst %.cpp,obj/%.o,$(LIB_CLIENT_SRC))
70 LANG_OBJS += $(patsubst %.cpp,obj/%.o,$(LANG_SRC))
5571 DAEMON_OBJS += $(patsubst %.cpp,obj/%.o,$(DAEMON_SRC))
56 DEPS += $(LIB_OBJS:.o=.d) $(LIB_CLIENT_OBJS:.o=.d) $(DAEMON_OBJS:.o=.d)
72 WRAP_LIB_OBJS += $(patsubst %.cpp,obj/%.o,$(WRAP_LIB_SRC))
73 DEPS += $(LIB_OBJS:.o=.d) $(LIB_CLIENT_OBJS:.o=.d) $(LANG_OBJS:.o=.d) $(DAEMON_OBJS:.o=.d) $(WRAP_LIB_OBJS:.o=.d)
5774
58 all: mk_obj_dir $(ARLIB) $(ARLIB_CLIENT) $(I2PD)
75 ## Build all code (libi2pd, libi2pdclient, libi2pdlang), link it to .a and build binary
76 all: $(ARLIB) $(ARLIB_CLIENT) $(ARLIB_LANG) $(I2PD)
5977
6078 mk_obj_dir:
61 @mkdir -p obj
62 @mkdir -p obj/Win32
6379 @mkdir -p obj/$(LIB_SRC_DIR)
6480 @mkdir -p obj/$(LIB_CLIENT_SRC_DIR)
81 @mkdir -p obj/$(LANG_SRC_DIR)
6582 @mkdir -p obj/$(DAEMON_SRC_DIR)
83 @mkdir -p obj/$(WRAP_SRC_DIR)
84 @mkdir -p obj/Win32
6685
67 api: mk_obj_dir $(SHLIB) $(ARLIB)
68 client: mk_obj_dir $(SHLIB_CLIENT) $(ARLIB_CLIENT)
69 api_client: mk_obj_dir $(SHLIB) $(ARLIB) $(SHLIB_CLIENT) $(ARLIB_CLIENT)
86 api: $(SHLIB) $(ARLIB)
87 client: $(SHLIB_CLIENT) $(ARLIB_CLIENT)
88 lang: $(SHLIB_LANG) $(ARLIB_LANG)
89 api_client: api client lang
90 wrapper: api_client $(SHLIB_WRAP) $(ARLIB_WRAP)
7091
7192 ## NOTE: The NEEDED_CXXFLAGS are here so that CXXFLAGS can be specified at build time
7293 ## **without** overwriting the CXXFLAGS which we need in order to build.
7596 ## -std=c++11. If you want to remove this variable please do so in a way that allows setting
7697 ## custom FLAGS to work at build-time.
7798
78 obj/%.o: %.cpp
99 obj/%.o: %.cpp | mk_obj_dir
79100 $(CXX) $(CXXFLAGS) $(NEEDED_CXXFLAGS) $(INCFLAGS) -c -o $@ $<
80101
81102 # '-' is 'ignore if missing' on first run
82103 -include $(DEPS)
83104
84 $(I2PD): $(DAEMON_OBJS) $(ARLIB) $(ARLIB_CLIENT)
105 $(I2PD): $(DAEMON_OBJS) $(ARLIB) $(ARLIB_CLIENT) $(ARLIB_LANG)
85106 $(CXX) -o $@ $(LDFLAGS) $^ $(LDLIBS)
86107
87 $(SHLIB): $(LIB_OBJS)
108 $(SHLIB): $(LIB_OBJS) $(SHLIB_LANG)
109 ifneq ($(USE_STATIC),yes)
110 $(CXX) $(LDFLAGS) -shared -o $@ $^ $(LDLIBS) $(SHLIB_LANG)
111 endif
112
113 $(SHLIB_CLIENT): $(LIB_CLIENT_OBJS) $(SHLIB) $(SHLIB_LANG)
114 ifneq ($(USE_STATIC),yes)
115 $(CXX) $(LDFLAGS) -shared -o $@ $^ $(LDLIBS) $(SHLIB) $(SHLIB_LANG)
116 endif
117
118 $(SHLIB_WRAP): $(WRAP_LIB_OBJS)
88119 ifneq ($(USE_STATIC),yes)
89120 $(CXX) $(LDFLAGS) -shared -o $@ $^ $(LDLIBS)
90121 endif
91122
92 $(SHLIB_CLIENT): $(LIB_CLIENT_OBJS)
123 $(SHLIB_LANG): $(LANG_OBJS)
93124 ifneq ($(USE_STATIC),yes)
94 $(CXX) $(LDFLAGS) -shared -o $@ $^ $(LDLIBS) $(SHLIB)
125 $(CXX) $(LDFLAGS) -shared -o $@ $^ $(LDLIBS)
95126 endif
96127
97128 $(ARLIB): $(LIB_OBJS)
100131 $(ARLIB_CLIENT): $(LIB_CLIENT_OBJS)
101132 $(AR) -r $@ $^
102133
134 $(ARLIB_WRAP): $(WRAP_LIB_OBJS)
135 $(AR) -r $@ $^
136
137 $(ARLIB_LANG): $(LANG_OBJS)
138 $(AR) -r $@ $^
139
103140 clean:
104141 $(RM) -r obj
105142 $(RM) -r docs/generated
106 $(RM) $(I2PD) $(SHLIB) $(ARLIB) $(SHLIB_CLIENT) $(ARLIB_CLIENT)
143 $(RM) $(I2PD) $(SHLIB) $(ARLIB) $(SHLIB_CLIENT) $(ARLIB_CLIENT) $(SHLIB_LANG) $(ARLIB_LANG) $(SHLIB_WRAP) $(ARLIB_WRAP)
107144
108 strip: $(I2PD) $(SHLIB_CLIENT) $(SHLIB)
145 strip: $(I2PD) $(SHLIB) $(SHLIB_CLIENT) $(SHLIB_LANG)
109146 strip $^
110147
111148 LATEST_TAG=$(shell git describe --tags --abbrev=0 openssl)
129166 .PHONY: api
130167 .PHONY: api_client
131168 .PHONY: client
169 .PHONY: lang
132170 .PHONY: mk_obj_dir
133171 .PHONY: install
134172 .PHONY: strip
22
33 WINDRES = windres
44
5 CXXFLAGS := $(CXX_DEBUG) -D_MT -DWIN32_LEAN_AND_MEAN -fPIC -msse
5 CXXFLAGS := $(CXX_DEBUG) -DWIN32_LEAN_AND_MEAN -fPIC -msse
66 INCFLAGS = -I$(DAEMON_SRC_DIR) -IWin32
7 LDFLAGS := ${LD_DEBUG} -Wl,-Bstatic -static-libgcc
7 LDFLAGS := ${LD_DEBUG} -static
88
99 # detect proper flag for c++11 support by compilers
1010 CXXVER := $(shell $(CXX) -dumpversion)
6060 LDFLAGS += -Wl,--nxcompat -Wl,--high-entropy-va -Wl,--dynamicbase,--export-all-symbols
6161 endif
6262
63 obj/%.o : %.rc
63 obj/%.o : %.rc | mk_obj_dir
6464 $(WINDRES) -i $< -o $@
22 [![License](https://img.shields.io/github/license/PurpleI2P/i2pd.svg)](https://github.com/PurpleI2P/i2pd/blob/openssl/LICENSE)
33 [![Packaging status](https://repology.org/badge/tiny-repos/i2pd.svg)](https://repology.org/project/i2pd/versions)
44 [![Docker Pulls](https://img.shields.io/docker/pulls/purplei2p/i2pd)](https://hub.docker.com/r/purplei2p/i2pd)
5 [![Crowdin](https://badges.crowdin.net/i2pd/localized.svg)](https://crowdin.com/project/i2pd)
56
67 *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*
78
6768
6869 **Supported systems:**
6970
70 * GNU/Linux - [![Build Status](https://travis-ci.org/PurpleI2P/i2pd.svg?branch=openssl)](https://travis-ci.org/PurpleI2P/i2pd)
71 * GNU/Linux - [![Build on Ubuntu](https://github.com/PurpleI2P/i2pd/actions/workflows/build.yml/badge.svg)](https://github.com/PurpleI2P/i2pd/actions/workflows/build.yml)
7172 * CentOS / Fedora / Mageia - [![Build Status](https://copr.fedorainfracloud.org/coprs/supervillain/i2pd/package/i2pd-git/status_image/last_build.png)](https://copr.fedorainfracloud.org/coprs/supervillain/i2pd/package/i2pd-git/)
7273 * Alpine, ArchLinux, openSUSE, Gentoo, Debian, Ubuntu, etc.
73 * Windows - [![Build status](https://ci.appveyor.com/api/projects/status/1908qe4p48ff1x23?svg=true)](https://ci.appveyor.com/project/PurpleI2P/i2pd)
74 * Mac OS X - [![Build Status](https://travis-ci.org/PurpleI2P/i2pd.svg?branch=openssl)](https://travis-ci.org/PurpleI2P/i2pd)
75 * Docker image - [![Build Status](https://img.shields.io/docker/cloud/build/purplei2p/i2pd)](https://hub.docker.com/r/purplei2p/i2pd/builds/)
76 * Snap
77 * FreeBSD
78 * Android
74 * 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)
75 * 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)
76 * 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)
77 * 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)
78 * 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)
79 * 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)
7980 * iOS
8081
8182 Using i2pd
8384
8485 See [documentation](https://i2pd.readthedocs.io/en/latest/user-guide/run/) and
8586 [example config file](https://github.com/PurpleI2P/i2pd/blob/openssl/contrib/i2pd.conf).
87
88 Localization
89 ------------
90
91 You can help us with translation i2pd to your language using Crowdin platform!
92 Translation project can be found [here](https://crowdin.com/project/i2pd).
93
94 New languages can be requested on project's [discussion page](https://crowdin.com/project/i2pd/discussions).
95
96 Current status: [![Crowdin](https://badges.crowdin.net/i2pd/localized.svg)](https://crowdin.com/project/i2pd)
8697
8798 Donations
8899 ---------
93104 DASH: Xw8YUrQpYzP9tZBmbjqxS3M97Q7v3vJKUF
94105 ZEC: t1cTckLuXsr1dwVrK4NDzfhehss4NvMadAJ
95106 GST: GbD2JSQHBHCKLa9WTHmigJRpyFgmBj4woG
107 XMR: 497pJc7X4xqKvcLBLpSUtRgWqMMyo24u4btCos3cak6gbMkpobgSU6492ztUcUBghyeHpYeczB55s38NpuHoH5WGNSPDRMH
96108
97109 License
98110 -------
130130 transfer >>= 10;
131131 auto mbytes = transfer & 0x03ff;
132132 transfer >>= 10;
133 auto gbytes = transfer & 0x03ff;
133 auto gbytes = transfer;
134134
135135 if (gbytes)
136136 s << gbytes << " GB, ";
+0
-57
appveyor.yml less more
0 version: 2.38.0.{build}
1 pull_requests:
2 do_not_increment_build_number: true
3 branches:
4 only:
5 - openssl
6 skip_tags: true
7 os: Visual Studio 2015
8 shallow_clone: true
9 clone_depth: 1
10
11 # avoid building 32-bit if 64-bit failed already
12 matrix:
13 fast_finish: true
14
15 environment:
16 APPVEYOR_SAVE_CACHE_ON_ERROR: true
17 MSYS2_PATH_TYPE: inherit
18 CHERE_INVOKING: enabled_from_arguments
19 matrix:
20 - MSYSTEM: MINGW64
21 - MSYSTEM: MINGW32
22
23 cache:
24 - c:\msys64\var\cache\pacman\pkg\
25
26 install:
27 # install new signing keyring
28 - 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"
29 - 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"
30 - c:\msys64\usr\bin\bash -lc "pacman-key --verify msys2-keyring-r21.b39fb11-1-any.pkg.tar.xz.sig"
31 - c:\msys64\usr\bin\bash -lc "pacman --noconfirm -U msys2-keyring-r21.b39fb11-1-any.pkg.tar.xz"
32 # remove packages which can break build
33 - 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"
34 # update runtime
35 - c:\msys64\usr\bin\bash -lc "pacman --noconfirm -Syuu"
36 # Kill bash before next try
37 - taskkill /T /F /IM bash.exe /IM gpg.exe /IM gpg-agent.exe | exit /B 0
38 # update packages and install required
39 - c:\msys64\usr\bin\bash -lc "pacman --noconfirm -Syuu $MINGW_PACKAGE_PREFIX-boost $MINGW_PACKAGE_PREFIX-miniupnpc"
40
41 build_script:
42 - c:\msys64\usr\bin\bash -lc "make USE_UPNP=yes DEBUG=no -j3"
43 # prepare archive for uploading
44 - set "FILELIST=i2pd.exe README.txt contrib/i2pd.conf contrib/tunnels.conf contrib/certificates contrib/tunnels.d"
45 - echo This is development build, use it carefully! For running in portable mode, move all files from contrib directory here. > README.txt
46 - 7z a -tzip -mx9 -mmt i2pd-%APPVEYOR_BUILD_VERSION%-%APPVEYOR_REPO_COMMIT:~0,7%-mingw-win%MSYSTEM:~-2%.zip %FILELIST%
47
48 after_build:
49 - c:\msys64\usr\bin\bash -lc "pacman --noconfirm -Sc"
50
51 test: off
52
53 deploy: off
54
55 artifacts:
56 - path: i2pd-*.zip
22 /i2pd
33 /libi2pd.a
44 /libi2pdclient.a
5 /libi2pdlang.a
56 /cmake_install.cmake
67 /CMakeCache.txt
78 /CPackConfig.cmake
1011 /arch.c
1112 # windows build script
1213 i2pd*.zip
13 build*.log
14 build*.log
2626 set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake_modules")
2727 set(CMAKE_SOURCE_DIR "..")
2828
29 #Handle paths nicely
30 include(GNUInstallDirs)
31
2932 # architecture
3033 include(TargetArch)
3134 target_architecture(ARCHITECTURE)
3235
3336 set(LIBI2PD_SRC_DIR ../libi2pd)
3437 set(LIBI2PD_CLIENT_SRC_DIR ../libi2pd_client)
38 set(LANG_SRC_DIR ../i18n)
3539 set(DAEMON_SRC_DIR ../daemon)
3640
3741 include_directories(${LIBI2PD_SRC_DIR})
3842 include_directories(${LIBI2PD_CLIENT_SRC_DIR})
43 include_directories(${LANG_SRC_DIR})
3944 include_directories(${DAEMON_SRC_DIR})
4045
41 set(LIBI2PD_SRC
42 "${LIBI2PD_SRC_DIR}/api.cpp"
43 "${LIBI2PD_SRC_DIR}/Base.cpp"
44 "${LIBI2PD_SRC_DIR}/Blinding.cpp"
45 "${LIBI2PD_SRC_DIR}/BloomFilter.cpp"
46 "${LIBI2PD_SRC_DIR}/ChaCha20.cpp"
47 "${LIBI2PD_SRC_DIR}/Config.cpp"
48 "${LIBI2PD_SRC_DIR}/CPU.cpp"
49 "${LIBI2PD_SRC_DIR}/Crypto.cpp"
50 "${LIBI2PD_SRC_DIR}/CryptoKey.cpp"
51 "${LIBI2PD_SRC_DIR}/Datagram.cpp"
52 "${LIBI2PD_SRC_DIR}/Destination.cpp"
53 "${LIBI2PD_SRC_DIR}/ECIESX25519AEADRatchetSession.cpp"
54 "${LIBI2PD_SRC_DIR}/Ed25519.cpp"
55 "${LIBI2PD_SRC_DIR}/Elligator.cpp"
56 "${LIBI2PD_SRC_DIR}/Family.cpp"
57 "${LIBI2PD_SRC_DIR}/FS.cpp"
58 "${LIBI2PD_SRC_DIR}/Garlic.cpp"
59 "${LIBI2PD_SRC_DIR}/Gost.cpp"
60 "${LIBI2PD_SRC_DIR}/Gzip.cpp"
61 "${LIBI2PD_SRC_DIR}/HTTP.cpp"
62 "${LIBI2PD_SRC_DIR}/I2NPProtocol.cpp"
63 "${LIBI2PD_SRC_DIR}/Identity.cpp"
64 "${LIBI2PD_SRC_DIR}/LeaseSet.cpp"
65 "${LIBI2PD_SRC_DIR}/Log.cpp"
66 "${LIBI2PD_SRC_DIR}/NetDb.cpp"
67 "${LIBI2PD_SRC_DIR}/NetDbRequests.cpp"
68 "${LIBI2PD_SRC_DIR}/NTCP2.cpp"
69 "${LIBI2PD_SRC_DIR}/Poly1305.cpp"
70 "${LIBI2PD_SRC_DIR}/Profiling.cpp"
71 "${LIBI2PD_SRC_DIR}/Reseed.cpp"
72 "${LIBI2PD_SRC_DIR}/RouterContext.cpp"
73 "${LIBI2PD_SRC_DIR}/RouterInfo.cpp"
74 "${LIBI2PD_SRC_DIR}/Signature.cpp"
75 "${LIBI2PD_SRC_DIR}/SSU.cpp"
76 "${LIBI2PD_SRC_DIR}/SSUData.cpp"
77 "${LIBI2PD_SRC_DIR}/SSUSession.cpp"
78 "${LIBI2PD_SRC_DIR}/Streaming.cpp"
79 "${LIBI2PD_SRC_DIR}/Timestamp.cpp"
80 "${LIBI2PD_SRC_DIR}/TransitTunnel.cpp"
81 "${LIBI2PD_SRC_DIR}/Transports.cpp"
82 "${LIBI2PD_SRC_DIR}/Tunnel.cpp"
83 "${LIBI2PD_SRC_DIR}/TunnelEndpoint.cpp"
84 "${LIBI2PD_SRC_DIR}/TunnelGateway.cpp"
85 "${LIBI2PD_SRC_DIR}/TunnelPool.cpp"
86 "${LIBI2PD_SRC_DIR}/TunnelConfig.cpp"
87 "${LIBI2PD_SRC_DIR}/util.cpp"
88 )
89
46 FILE(GLOB LIBI2PD_SRC ${LIBI2PD_SRC_DIR}/*.cpp)
9047 add_library(libi2pd ${LIBI2PD_SRC})
9148 set_target_properties(libi2pd PROPERTIES PREFIX "")
9249
9350 if(WITH_LIBRARY)
9451 install(TARGETS libi2pd
9552 EXPORT libi2pd
96 ARCHIVE DESTINATION lib
97 LIBRARY DESTINATION lib
53 ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
54 LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
9855 COMPONENT Libraries)
9956 # TODO Make libi2pd available to 3rd party projects via CMake as imported target
10057 # FIXME This pulls stdafx
10158 # install(EXPORT libi2pd DESTINATION ${CMAKE_INSTALL_LIBDIR})
10259 endif()
10360
104 set(CLIENT_SRC
105 "${LIBI2PD_CLIENT_SRC_DIR}/AddressBook.cpp"
106 "${LIBI2PD_CLIENT_SRC_DIR}/BOB.cpp"
107 "${LIBI2PD_CLIENT_SRC_DIR}/ClientContext.cpp"
108 "${LIBI2PD_CLIENT_SRC_DIR}/MatchedDestination.cpp"
109 "${LIBI2PD_CLIENT_SRC_DIR}/I2PTunnel.cpp"
110 "${LIBI2PD_CLIENT_SRC_DIR}/I2PService.cpp"
111 "${LIBI2PD_CLIENT_SRC_DIR}/SAM.cpp"
112 "${LIBI2PD_CLIENT_SRC_DIR}/SOCKS.cpp"
113 "${LIBI2PD_CLIENT_SRC_DIR}/HTTPProxy.cpp"
114 "${LIBI2PD_CLIENT_SRC_DIR}/I2CP.cpp"
115 )
116
61 FILE(GLOB CLIENT_SRC ${LIBI2PD_CLIENT_SRC_DIR}/*.cpp)
11762 add_library(libi2pdclient ${CLIENT_SRC})
11863 set_target_properties(libi2pdclient PROPERTIES PREFIX "")
11964
12065 if(WITH_LIBRARY)
12166 install(TARGETS libi2pdclient
12267 EXPORT libi2pdclient
123 ARCHIVE DESTINATION lib
124 LIBRARY DESTINATION lib
68 ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
69 LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
70 COMPONENT Libraries)
71 endif()
72
73 FILE(GLOB LANG_SRC ${LANG_SRC_DIR}/*.cpp)
74 add_library(libi2pdlang ${LANG_SRC})
75 set_target_properties(libi2pdlang PROPERTIES PREFIX "")
76
77 if(WITH_LIBRARY)
78 install(TARGETS libi2pdlang
79 EXPORT libi2pdlang
80 ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
81 LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
12582 COMPONENT Libraries)
12683 endif()
12784
253210 )
254211 target_compile_options(libi2pd PRIVATE -include libi2pd/stdafx.h)
255212 target_compile_options(libi2pdclient PRIVATE -include libi2pd/stdafx.h)
213 target_compile_options(libi2pdlang PRIVATE -include libi2pd/stdafx.h)
256214 target_link_libraries(libi2pd stdafx)
257215 endif()
258216
259 target_link_libraries(libi2pdclient libi2pd)
217 target_link_libraries(libi2pdclient libi2pd libi2pdlang)
260218
261219 find_package(Boost COMPONENTS system filesystem program_options date_time REQUIRED)
262220 if(NOT DEFINED Boost_INCLUDE_DIRS)
316274 message(STATUS " THREADSANITIZER : ${WITH_THREADSANITIZER}")
317275 message(STATUS "---------------------------------------")
318276
319 #Handle paths nicely
320 include(GNUInstallDirs)
321
322277 if(WITH_BINARY)
323278 add_executable("${PROJECT_NAME}" ${DAEMON_SRC})
324279
350305 endif()
351306
352307 target_link_libraries(libi2pd ${Boost_LIBRARIES} ${ZLIB_LIBRARY})
353 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})
308 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})
354309
355310 install(TARGETS "${PROJECT_NAME}" RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT Runtime)
356311 set(APPS "\${CMAKE_INSTALL_PREFIX}/bin/${PROJECT_NAME}${CMAKE_EXECUTABLE_SUFFIX}")
+0
-34
build/docker/README.md less more
0 Howto build & run
1 ==================
2
3 **Build**
4
5 Assuming you're in the root directory of the anoncoin source code.
6
7 $ `cd build/docker`
8 $ `docker -t meeh/i2pd:latest .`
9
10 **Run**
11
12 To run either the local build, or if not found - fetched prebuild from hub.docker.io, run the following command.
13
14 $ `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`
15
16 All the ports ( -p HOSTPORT:DOCKERPORT ) is optional. However the command above enable all features (Webconsole, HTTP Proxy, BOB, SAM, i2cp, etc)
17
18 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.
19
20 **Options**
21
22 Options are set via docker environment variables. This can be set at run with -e parameters.
23
24 * **ENABLE_IPV6** - Enable IPv6 support. Any value can be used - it triggers as long as it's not empty.
25 * **LOGLEVEL** - Set the loglevel.
26 * **ENABLE_AUTH** - Enable auth for the webconsole. Username and password needs to be set manually in i2pd.conf cause security reasons.
27
28 **Logging**
29
30 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. :)
31
32
33
+0
-11
build/docker/old-ubuntu-based/Dockerfile less more
0 FROM ubuntu
1
2 RUN apt-get update && apt-get install -y libboost-dev libboost-filesystem-dev \
3 libboost-program-options-dev libboost-date-time-dev \
4 libssl-dev git build-essential
5
6 RUN git clone https://github.com/PurpleI2P/i2pd.git
7 WORKDIR /i2pd
8 RUN make
9
10 CMD ./i2pd
+0
-2
build/fig.yml less more
0 i2pd:
1 build: .
0 -----BEGIN CERTIFICATE-----
1 MIIFfzCCA2egAwIBAgIEbNbRPjANBgkqhkiG9w0BAQ0FADBwMQswCQYDVQQGEwJY
2 WDELMAkGA1UECAwCWFgxCzAJBgNVBAcMAlhYMR4wHAYDVQQKDBVJMlAgQW5vbnlt
3 b3VzIE5ldHdvcmsxDDAKBgNVBAsMA0kyUDEZMBcGA1UEAwwQb3JpZ25hbEBtYWls
4 LmkycDAeFw0yMTA3MDYyMjExMDFaFw0zMTA3MDQyMjExMDFaMHAxCzAJBgNVBAYT
5 AlhYMQswCQYDVQQIDAJYWDELMAkGA1UEBwwCWFgxHjAcBgNVBAoMFUkyUCBBbm9u
6 eW1vdXMgTmV0d29yazEMMAoGA1UECwwDSTJQMRkwFwYDVQQDDBBvcmlnbmFsQG1h
7 aWwuaTJwMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAvNJz2KGuAkHP
8 tGFobfLvpybtxB50fkcEsTc9opmiy7wBKK9rSI01VS616IhABkWKZVfK2A9NqpGv
9 v/CyhTKoaeSNeXY7+zORUWgWK/zA9fA4GRZFqlW8j4tbompDwcLYNqRBCsn1C0OY
10 YA5JhXPBixMcnXl8N8x4sXhQ4l9R3+QrydhUHRvgDc8dOxRyIX7zuQAyf8tmA2Xo
11 xZLdvDcCJdLBIbFwxhIceIhgcOwaOx7oRkZDZdYcLJd3zjyPbu8JtOM2ZkwH7r+0
12 ro5PktuDp2LAS6SII5yYNcwcrvPZGPqhLdifIw1BrdTIb/rIkQZ5iXOOdyPmT7e8
13 IwAJcPFlfvrS4Vbi9oDqyx3aDUBoubgmFnO1TirL56ck83R/ubcKtdnyzAn5dp+f
14 ZNYW6/foSBpDDOCViylbFAR5H0HJEbBns7PZx6mGEEI4tUAJdNYl7Ly7Df60a9Rz
15 cD/gz08U9UwFXYKoT6roEjToADGAzb5MI4cVlAb2AmQaMNXNe04HcDL1bU50mkNU
16 amqPv8nxf72fBQCEmZz2G57T6QiYTtcCwiWS1QdWsuaOtCo9zO0MKcjzSdUxuxEc
17 dXhjQdNegsgg/Xk7bJ8lKOsACqMpFftdPmuyeZU2t+3RPuBpV/0j2qUfg/y6kb0z
18 CxAOYmlcL4kqw4VT+5V/EeZLIG0h9I0CAwEAAaMhMB8wHQYDVR0OBBYEFD/wJObg
19 CCDuhMJCVWTSTj+B3rsUMA0GCSqGSIb3DQEBDQUAA4ICAQC0PjsTSPWlGbLNeeI8
20 F0B5xAwXYJzZ7/LRxh8u42HDUqVIDjqkuls1l3v9D7htty2Gr3Ws2dcvcOr2KcOy
21 mEWg+jdP/N3vt9IkZeVS4YQoPgq6orn7lVkk00bcKb24f7ZnoQnnVV0/m42Y5P4j
22 LLh+8MBxsez9azXyZbDVEkgsMUAkdVO6KNz6scqz7wb8egV2GAMAp7cwChC6lanK
23 gv9ZyJhG/HdTv6VyuMZhJy6rX4geM97tm1iHu1VLsQcIzBKAdEvWJv8ofMeiyINe
24 hqAP9NYaeowKi975NOrmf+XZwxd0niApIohV684RCVUfL8H7HSPbdXhBJ/WslyDP
25 cTGhA2BLqEXZBn/nLQknlnl0SZTQxG2n4fEgD1E5YS/aoBrig/uXtWm2Zdf8U3mM
26 +bNXhbi9s7LneN2ye8LlNJBSRklNn/bNo8OmzLII1RQwf1+vaHT96lASbTVepMZ/
27 Y9VcC8fAmho/zfQEKueLEB03K+gr2dGD+1crmMtUBjWJ9vPjtooZArtkDbh+kVYA
28 cx4N4NXULRwxVWZe5wTQOqcZ3qSS1ClMwaziwychGaj8xRAirHMZnlPOZO1UK4+5
29 8F4RMJktyZjNgSLP76XPS4rJK5fobuPqFeA4OpDFn/5+/XeQFF6i6wntx1tzztzH
30 zc+BrVZOdcYPqu9iLXyRQ9JwwA==
31 -----END CERTIFICATE-----
+0
-31
contrib/certificates/router/orignal_at_mail.i2p.crt less more
0 -----BEGIN CERTIFICATE-----
1 MIIFVDCCAzwCCQC2r1XWYtqtAzANBgkqhkiG9w0BAQsFADBsMQswCQYDVQQGEwJY
2 WDELMAkGA1UECAwCWFgxCzAJBgNVBAcMAlhYMRMwEQYDVQQKDApQdXJwbGUgSTJQ
3 MQ0wCwYDVQQLDARJMlBEMR8wHQYJKoZIhvcNAQkBFhBvcmlnbmFsQG1haWwuaTJw
4 MB4XDTE1MDIyMjEzNTgxOFoXDTI1MDIxOTEzNTgxOFowbDELMAkGA1UEBhMCWFgx
5 CzAJBgNVBAgMAlhYMQswCQYDVQQHDAJYWDETMBEGA1UECgwKUHVycGxlIEkyUDEN
6 MAsGA1UECwwESTJQRDEfMB0GCSqGSIb3DQEJARYQb3JpZ25hbEBtYWlsLmkycDCC
7 AiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBALp3D/gdvFjrMm+IE8tHZCWE
8 hQ6Pp0CCgCGDBC3WQFLqR98bqVPl4UwRG/MKY/LY7Woai06JNmGcpfw0LMoNnHxT
9 bvKtDRe/8kQdhdLHhgIkWKSbMvTAl7uUdV6FzsPgDR0x7scoFVWEhkF0wfmzGF2V
10 yr/WCBQejFPu69z03m5tRQ8Xjp2txWV45RawUmFu50bgbZvLCSLfTkIvxmfJzgPN
11 pJ3sPa/g7TBZl2uEiAu4uaEKvTuuzStOWCGgFaHYFVlTfFXTvmhFMqHfaidtzrlu
12 H35WGrmIWTDl6uGPC5QkSppvkj73rDj5aEyPzWMz5DN3YeECoVSchN+OJJCM6m7+
13 rLFYXghVEp2h+T9O1GBRfcHlQ2E3CrWWvxhmK8dfteJmd501dyNX2paeuIg/aPFO
14 54/8m2r11uyF29hgY8VWLdXtqvwhKuK36PCzofEwDp9QQX8GRsEV4pZTrn4bDhGo
15 kb9BF7TZTqtL3uyiRmIyBXrNNiYlA1Xm4fyKRtxl0mrPaUXdgdnCt3KxOAJ8WM2B
16 7L/kk9U8C/nexHbMxIZfTap49XcUg5dxSO9kOBosIOcCUms8sAzBPDV2tWAByhYF
17 jI/Tutbd3F0+fvcmTcIFOlGbOxKgO2SfwXjv/44g/3LMK6IAMFB9UOc8KhnnJP0f
18 uAHvMXn1ahRs4pM1VizLAgMBAAEwDQYJKoZIhvcNAQELBQADggIBAIOxdaXT+wfu
19 nv/+1hy5T4TlRMNNsuj79ROcy6Mp+JwMG50HjTc0qTlXh8C7nHybDJn4v7DA+Nyn
20 RxT0J5I+Gqn+Na9TaC9mLeX/lwe8/KomyhBWxjrsyWj1V6v/cLO924S2rtcfzMDm
21 l3SFh9YHM1KF/R9N1XYBwtMzr3bupWDnE1yycYp1F4sMLr5SMzMQ0svQpQEM2/y5
22 kly8+eUzryhm+ag9x1686uEG5gxhQ1eHQoZEaClHUOsV+28+d5If7cqcYx9Hf5Tt
23 CiVjJQzdxBF+6GeiJtKxnLtevqlkbyIJt6Cm9/7YIy/ovRGF2AKSYN6oCwmZQ6i1
24 8nRnFq5zE7O94m+GXconWZxy0wVqA6472HThMi7S+Tk/eLYen2ilGY+KCb9a0FH5
25 5MOuWSoJZ8/HfW2VeQmL8EjhWm5F2ybg28wgXK4BOGR3jQi03Fsc+AFidnWxSKo0
26 aiJoPgOsfyu8/fnCcAi07kSmjzUKIWskApgcpGQLNXHFK9mtg7+VA8esRnfLlKtP
27 tJf+nNAPY1sqHfGBzh7WWGWal5RGHF5nEm3ta3oiFF5sMKCJ6C87zVwFkEcRytGC
28 xOGmiG1O1RPrO5NG7rZUaQ4y1OKl2Y1H+nGONzZ3mvoAOvxEq6JtUnU2kZscpPlk
29 fpeOSDoGBYJGbIpzDreBDhxaZrwGq36k
30 -----END CERTIFICATE-----
2424 # 1. install deps, clone and build.
2525 # 2. strip binaries.
2626 # 3. Purge all dependencies and other unrelated packages, including build directory.
27 RUN apk --no-cache --virtual build-dependendencies add make gcc g++ libtool zlib-dev boost-dev build-base openssl-dev openssl git \
27 RUN apk update \
28 && apk --no-cache --virtual build-dependendencies add make gcc g++ libtool zlib-dev boost-dev build-base openssl-dev openssl miniupnpc-dev git \
2829 && mkdir -p /tmp/build \
2930 && cd /tmp/build && git clone -b ${GIT_BRANCH} ${REPO_URL} \
3031 && cd i2pd \
3132 && if [ -n "${GIT_TAG}" ]; then git checkout tags/${GIT_TAG}; fi \
32 && make \
33 && make USE_UPNP=yes \
3334 && cp -R contrib/certificates /i2pd_certificates \
3435 && mkdir -p /usr/local/bin \
3536 && mv i2pd /usr/local/bin \
3637 && cd /usr/local/bin \
3738 && strip i2pd \
3839 && rm -fr /tmp/build && apk --no-cache --purge del build-dependendencies build-base fortify-headers boost-dev zlib-dev openssl-dev \
39 boost-python3 python3 gdbm boost-unit_test_framework linux-headers boost-prg_exec_monitor \
40 miniupnpc-dev boost-python3 python3 gdbm boost-unit_test_framework linux-headers boost-prg_exec_monitor \
4041 boost-serialization boost-wave boost-wserialization boost-math boost-graph boost-regex git pcre2 \
4142 libtool g++ gcc
4243
4344 # 2. Adding required libraries to run i2pd to ensure it will run.
44 RUN apk --no-cache add boost-filesystem boost-system boost-program_options boost-date_time boost-thread boost-iostreams openssl musl-utils libstdc++
45 RUN apk --no-cache add boost-filesystem boost-system boost-program_options boost-date_time boost-thread boost-iostreams openssl miniupnpc musl-utils libstdc++
4546
4647 COPY entrypoint.sh /entrypoint.sh
4748 RUN chmod a+x /entrypoint.sh
0 # i2pd
1 # Copyright (C) 2021 PurpleI2P team
2 # This file is distributed under the same license as the i2pd package.
3 # R4SAS <r4sas@i2pmail.org>, 2021.
4 #
5 msgid ""
6 msgstr ""
7 "Project-Id-Version: i2pd\n"
8 "Report-Msgid-Bugs-To: https://github.com/PurpleI2P/i2pd/issues\n"
9 "POT-Creation-Date: 2021-08-06 17:12\n"
10 "MIME-Version: 1.0\n"
11 "Content-Type: text/plain; charset=UTF-8\n"
12 "Content-Transfer-Encoding: 8bit\n"
13 "X-Generator: Poedit 3.0\n"
14 "X-Poedit-SourceCharset: UTF-8\n"
15 "X-Poedit-Basepath: .\n"
16 "X-Poedit-KeywordsList: ;tr\n"
17 "X-Poedit-SearchPath-0: daemon/HTTPServer.cpp\n"
18 "X-Poedit-SearchPath-1: libi2pd_client/HTTPProxy.cpp\n"
19
20 #: daemon/HTTPServer.cpp:177
21 msgid "day"
22 msgid_plural "days"
23 msgstr[0] ""
24 msgstr[1] ""
25
26 #: daemon/HTTPServer.cpp:181
27 msgid "hour"
28 msgid_plural "hours"
29 msgstr[0] ""
30 msgstr[1] ""
31
32 #: daemon/HTTPServer.cpp:185
33 msgid "minute"
34 msgid_plural "minutes"
35 msgstr[0] ""
36 msgstr[1] ""
37
38 #: daemon/HTTPServer.cpp:188
39 msgid "second"
40 msgid_plural "seconds"
41 msgstr[0] ""
42 msgstr[1] ""
43
44 #. tr: Kibibit
45 #: daemon/HTTPServer.cpp:196 daemon/HTTPServer.cpp:224
46 msgid "KiB"
47 msgstr ""
48
49 #. tr: Mebibit
50 #: daemon/HTTPServer.cpp:198
51 msgid "MiB"
52 msgstr ""
53
54 #. tr: Gibibit
55 #: daemon/HTTPServer.cpp:200
56 msgid "GiB"
57 msgstr ""
58
59 #: daemon/HTTPServer.cpp:217
60 msgid "building"
61 msgstr ""
62
63 #: daemon/HTTPServer.cpp:218
64 msgid "failed"
65 msgstr ""
66
67 #: daemon/HTTPServer.cpp:219
68 msgid "expiring"
69 msgstr ""
70
71 #: daemon/HTTPServer.cpp:220
72 msgid "established"
73 msgstr ""
74
75 #: daemon/HTTPServer.cpp:221
76 msgid "unknown"
77 msgstr ""
78
79 #: daemon/HTTPServer.cpp:223
80 msgid "exploratory"
81 msgstr ""
82
83 #: daemon/HTTPServer.cpp:259
84 msgid "<b>i2pd</b> webconsole"
85 msgstr ""
86
87 #: daemon/HTTPServer.cpp:262
88 msgid "Main page"
89 msgstr ""
90
91 #: daemon/HTTPServer.cpp:263 daemon/HTTPServer.cpp:725
92 msgid "Router commands"
93 msgstr ""
94
95 #: daemon/HTTPServer.cpp:264 daemon/HTTPServer.cpp:448
96 #: daemon/HTTPServer.cpp:460
97 msgid "Local Destinations"
98 msgstr ""
99
100 #: daemon/HTTPServer.cpp:266 daemon/HTTPServer.cpp:418
101 #: daemon/HTTPServer.cpp:504 daemon/HTTPServer.cpp:510
102 #: daemon/HTTPServer.cpp:641 daemon/HTTPServer.cpp:684
103 #: daemon/HTTPServer.cpp:688
104 msgid "LeaseSets"
105 msgstr ""
106
107 #: daemon/HTTPServer.cpp:268 daemon/HTTPServer.cpp:694
108 msgid "Tunnels"
109 msgstr ""
110
111 #: daemon/HTTPServer.cpp:269 daemon/HTTPServer.cpp:425
112 #: daemon/HTTPServer.cpp:787 daemon/HTTPServer.cpp:803
113 msgid "Transit Tunnels"
114 msgstr ""
115
116 #: daemon/HTTPServer.cpp:270 daemon/HTTPServer.cpp:852
117 msgid "Transports"
118 msgstr ""
119
120 #: daemon/HTTPServer.cpp:271
121 msgid "I2P tunnels"
122 msgstr ""
123
124 #: daemon/HTTPServer.cpp:273 daemon/HTTPServer.cpp:914
125 #: daemon/HTTPServer.cpp:924
126 msgid "SAM sessions"
127 msgstr ""
128
129 #: daemon/HTTPServer.cpp:289 daemon/HTTPServer.cpp:1306
130 #: daemon/HTTPServer.cpp:1309 daemon/HTTPServer.cpp:1312
131 #: daemon/HTTPServer.cpp:1326 daemon/HTTPServer.cpp:1371
132 #: daemon/HTTPServer.cpp:1374 daemon/HTTPServer.cpp:1377
133 msgid "ERROR"
134 msgstr ""
135
136 #: daemon/HTTPServer.cpp:296
137 msgid "OK"
138 msgstr ""
139
140 #: daemon/HTTPServer.cpp:297
141 msgid "Testing"
142 msgstr ""
143
144 #: daemon/HTTPServer.cpp:298
145 msgid "Firewalled"
146 msgstr ""
147
148 #: daemon/HTTPServer.cpp:299 daemon/HTTPServer.cpp:320
149 #: daemon/HTTPServer.cpp:406
150 msgid "Unknown"
151 msgstr ""
152
153 #: daemon/HTTPServer.cpp:300 daemon/HTTPServer.cpp:435
154 #: daemon/HTTPServer.cpp:436 daemon/HTTPServer.cpp:982
155 #: daemon/HTTPServer.cpp:991
156 msgid "Proxy"
157 msgstr ""
158
159 #: daemon/HTTPServer.cpp:301
160 msgid "Mesh"
161 msgstr ""
162
163 #: daemon/HTTPServer.cpp:304
164 msgid "Error"
165 msgstr ""
166
167 #: daemon/HTTPServer.cpp:308
168 msgid "Clock skew"
169 msgstr ""
170
171 #: daemon/HTTPServer.cpp:311
172 msgid "Offline"
173 msgstr ""
174
175 #: daemon/HTTPServer.cpp:314
176 msgid "Symmetric NAT"
177 msgstr ""
178
179 #: daemon/HTTPServer.cpp:326
180 msgid "Uptime"
181 msgstr ""
182
183 #: daemon/HTTPServer.cpp:329
184 msgid "Network status"
185 msgstr ""
186
187 #: daemon/HTTPServer.cpp:334
188 msgid "Network status v6"
189 msgstr ""
190
191 #: daemon/HTTPServer.cpp:340 daemon/HTTPServer.cpp:347
192 msgid "Stopping in"
193 msgstr ""
194
195 #: daemon/HTTPServer.cpp:354
196 msgid "Family"
197 msgstr ""
198
199 #: daemon/HTTPServer.cpp:355
200 msgid "Tunnel creation success rate"
201 msgstr ""
202
203 #: daemon/HTTPServer.cpp:356
204 msgid "Received"
205 msgstr ""
206
207 #. tr: Kibibit/s
208 #: daemon/HTTPServer.cpp:358 daemon/HTTPServer.cpp:361
209 #: daemon/HTTPServer.cpp:364
210 msgid "KiB/s"
211 msgstr ""
212
213 #: daemon/HTTPServer.cpp:359
214 msgid "Sent"
215 msgstr ""
216
217 #: daemon/HTTPServer.cpp:362
218 msgid "Transit"
219 msgstr ""
220
221 #: daemon/HTTPServer.cpp:365
222 msgid "Data path"
223 msgstr ""
224
225 #: daemon/HTTPServer.cpp:368
226 msgid "Hidden content. Press on text to see."
227 msgstr ""
228
229 #: daemon/HTTPServer.cpp:371
230 msgid "Router Ident"
231 msgstr ""
232
233 #: daemon/HTTPServer.cpp:373
234 msgid "Router Family"
235 msgstr ""
236
237 #: daemon/HTTPServer.cpp:374
238 msgid "Router Caps"
239 msgstr ""
240
241 #: daemon/HTTPServer.cpp:375
242 msgid "Version"
243 msgstr ""
244
245 #: daemon/HTTPServer.cpp:376
246 msgid "Our external address"
247 msgstr ""
248
249 #: daemon/HTTPServer.cpp:384
250 msgid "supported"
251 msgstr ""
252
253 #: daemon/HTTPServer.cpp:416
254 msgid "Routers"
255 msgstr ""
256
257 #: daemon/HTTPServer.cpp:417
258 msgid "Floodfills"
259 msgstr ""
260
261 #: daemon/HTTPServer.cpp:424 daemon/HTTPServer.cpp:968
262 msgid "Client Tunnels"
263 msgstr ""
264
265 #: daemon/HTTPServer.cpp:434
266 msgid "Services"
267 msgstr ""
268
269 #: daemon/HTTPServer.cpp:435 daemon/HTTPServer.cpp:436
270 #: daemon/HTTPServer.cpp:437 daemon/HTTPServer.cpp:438
271 #: daemon/HTTPServer.cpp:439 daemon/HTTPServer.cpp:440
272 msgid "Enabled"
273 msgstr ""
274
275 #: daemon/HTTPServer.cpp:435 daemon/HTTPServer.cpp:436
276 #: daemon/HTTPServer.cpp:437 daemon/HTTPServer.cpp:438
277 #: daemon/HTTPServer.cpp:439 daemon/HTTPServer.cpp:440
278 msgid "Disabled"
279 msgstr ""
280
281 #: daemon/HTTPServer.cpp:483
282 msgid "Encrypted B33 address"
283 msgstr ""
284
285 #: daemon/HTTPServer.cpp:492
286 msgid "Address registration line"
287 msgstr ""
288
289 #: daemon/HTTPServer.cpp:497
290 msgid "Domain"
291 msgstr ""
292
293 #: daemon/HTTPServer.cpp:498
294 msgid "Generate"
295 msgstr ""
296
297 #: daemon/HTTPServer.cpp:499
298 msgid ""
299 "<b>Note:</b> result string can be used only for registering 2LD domains "
300 "(example.i2p). For registering subdomains please use i2pd-tools."
301 msgstr ""
302
303 #: daemon/HTTPServer.cpp:505
304 msgid "Address"
305 msgstr ""
306
307 #: daemon/HTTPServer.cpp:505
308 msgid "Type"
309 msgstr ""
310
311 #: daemon/HTTPServer.cpp:505
312 msgid "EncType"
313 msgstr ""
314
315 #: daemon/HTTPServer.cpp:515 daemon/HTTPServer.cpp:699
316 msgid "Inbound tunnels"
317 msgstr ""
318
319 #. tr: Milliseconds
320 #: daemon/HTTPServer.cpp:520 daemon/HTTPServer.cpp:530
321 #: daemon/HTTPServer.cpp:704 daemon/HTTPServer.cpp:714
322 msgid "ms"
323 msgstr ""
324
325 #: daemon/HTTPServer.cpp:525 daemon/HTTPServer.cpp:709
326 msgid "Outbound tunnels"
327 msgstr ""
328
329 #: daemon/HTTPServer.cpp:537
330 msgid "Tags"
331 msgstr ""
332
333 #: daemon/HTTPServer.cpp:537
334 msgid "Incoming"
335 msgstr ""
336
337 #: daemon/HTTPServer.cpp:544 daemon/HTTPServer.cpp:547
338 msgid "Outgoing"
339 msgstr ""
340
341 #: daemon/HTTPServer.cpp:545 daemon/HTTPServer.cpp:561
342 msgid "Destination"
343 msgstr ""
344
345 #: daemon/HTTPServer.cpp:545
346 msgid "Amount"
347 msgstr ""
348
349 #: daemon/HTTPServer.cpp:552
350 msgid "Incoming Tags"
351 msgstr ""
352
353 #: daemon/HTTPServer.cpp:560 daemon/HTTPServer.cpp:563
354 msgid "Tags sessions"
355 msgstr ""
356
357 #: daemon/HTTPServer.cpp:561
358 msgid "Status"
359 msgstr ""
360
361 #: daemon/HTTPServer.cpp:570 daemon/HTTPServer.cpp:626
362 msgid "Local Destination"
363 msgstr ""
364
365 #: daemon/HTTPServer.cpp:580 daemon/HTTPServer.cpp:947
366 msgid "Streams"
367 msgstr ""
368
369 #: daemon/HTTPServer.cpp:602
370 msgid "Close stream"
371 msgstr ""
372
373 #: daemon/HTTPServer.cpp:631
374 msgid "I2CP session not found"
375 msgstr ""
376
377 #: daemon/HTTPServer.cpp:634
378 msgid "I2CP is not enabled"
379 msgstr ""
380
381 #: daemon/HTTPServer.cpp:660
382 msgid "Invalid"
383 msgstr ""
384
385 #: daemon/HTTPServer.cpp:663
386 msgid "Store type"
387 msgstr ""
388
389 #: daemon/HTTPServer.cpp:664
390 msgid "Expires"
391 msgstr ""
392
393 #: daemon/HTTPServer.cpp:669
394 msgid "Non Expired Leases"
395 msgstr ""
396
397 #: daemon/HTTPServer.cpp:672
398 msgid "Gateway"
399 msgstr ""
400
401 #: daemon/HTTPServer.cpp:673
402 msgid "TunnelID"
403 msgstr ""
404
405 #: daemon/HTTPServer.cpp:674
406 msgid "EndDate"
407 msgstr ""
408
409 #: daemon/HTTPServer.cpp:684
410 msgid "not floodfill"
411 msgstr ""
412
413 #: daemon/HTTPServer.cpp:695
414 msgid "Queue size"
415 msgstr ""
416
417 #: daemon/HTTPServer.cpp:726
418 msgid "Run peer test"
419 msgstr ""
420
421 #: daemon/HTTPServer.cpp:731
422 msgid "Decline transit tunnels"
423 msgstr ""
424
425 #: daemon/HTTPServer.cpp:733
426 msgid "Accept transit tunnels"
427 msgstr ""
428
429 #: daemon/HTTPServer.cpp:737 daemon/HTTPServer.cpp:742
430 msgid "Cancel graceful shutdown"
431 msgstr ""
432
433 #: daemon/HTTPServer.cpp:739 daemon/HTTPServer.cpp:744
434 msgid "Start graceful shutdown"
435 msgstr ""
436
437 #: daemon/HTTPServer.cpp:747
438 msgid "Force shutdown"
439 msgstr ""
440
441 #: daemon/HTTPServer.cpp:748
442 msgid "Reload external CSS styles"
443 msgstr ""
444
445 #: daemon/HTTPServer.cpp:751
446 msgid ""
447 "<b>Note:</b> any action done here are not persistent and not changes your "
448 "config files."
449 msgstr ""
450
451 #: daemon/HTTPServer.cpp:753
452 msgid "Logging level"
453 msgstr ""
454
455 #: daemon/HTTPServer.cpp:761
456 msgid "Transit tunnels limit"
457 msgstr ""
458
459 #: daemon/HTTPServer.cpp:766 daemon/HTTPServer.cpp:778
460 msgid "Change"
461 msgstr ""
462
463 #: daemon/HTTPServer.cpp:770
464 msgid "Change language"
465 msgstr ""
466
467 #: daemon/HTTPServer.cpp:803
468 msgid "no transit tunnels currently built"
469 msgstr ""
470
471 #: daemon/HTTPServer.cpp:908 daemon/HTTPServer.cpp:931
472 msgid "SAM disabled"
473 msgstr ""
474
475 #: daemon/HTTPServer.cpp:924
476 msgid "no sessions currently running"
477 msgstr ""
478
479 #: daemon/HTTPServer.cpp:937
480 msgid "SAM session not found"
481 msgstr ""
482
483 #: daemon/HTTPServer.cpp:942
484 msgid "SAM Session"
485 msgstr ""
486
487 #: daemon/HTTPServer.cpp:999
488 msgid "Server Tunnels"
489 msgstr ""
490
491 #: daemon/HTTPServer.cpp:1015
492 msgid "Client Forwards"
493 msgstr ""
494
495 #: daemon/HTTPServer.cpp:1029
496 msgid "Server Forwards"
497 msgstr ""
498
499 #: daemon/HTTPServer.cpp:1227
500 msgid "Unknown page"
501 msgstr ""
502
503 #: daemon/HTTPServer.cpp:1246
504 msgid "Invalid token"
505 msgstr ""
506
507 #: daemon/HTTPServer.cpp:1304 daemon/HTTPServer.cpp:1361
508 #: daemon/HTTPServer.cpp:1401
509 msgid "SUCCESS"
510 msgstr ""
511
512 #: daemon/HTTPServer.cpp:1304
513 msgid "Stream closed"
514 msgstr ""
515
516 #: daemon/HTTPServer.cpp:1306
517 msgid "Stream not found or already was closed"
518 msgstr ""
519
520 #: daemon/HTTPServer.cpp:1309
521 msgid "Destination not found"
522 msgstr ""
523
524 #: daemon/HTTPServer.cpp:1312
525 msgid "StreamID can't be null"
526 msgstr ""
527
528 #: daemon/HTTPServer.cpp:1314 daemon/HTTPServer.cpp:1379
529 msgid "Return to destination page"
530 msgstr ""
531
532 #: daemon/HTTPServer.cpp:1315 daemon/HTTPServer.cpp:1328
533 #: daemon/HTTPServer.cpp:1403
534 msgid "You will be redirected in 5 seconds"
535 msgstr ""
536
537 #: daemon/HTTPServer.cpp:1326
538 msgid "Transit tunnels count must not exceed 65535"
539 msgstr ""
540
541 #: daemon/HTTPServer.cpp:1327 daemon/HTTPServer.cpp:1402
542 msgid "Back to commands list"
543 msgstr ""
544
545 #: daemon/HTTPServer.cpp:1363
546 msgid "Register at reg.i2p"
547 msgstr ""
548
549 #: daemon/HTTPServer.cpp:1364
550 msgid "Description"
551 msgstr ""
552
553 #: daemon/HTTPServer.cpp:1364
554 msgid "A bit information about service on domain"
555 msgstr ""
556
557 #: daemon/HTTPServer.cpp:1365
558 msgid "Submit"
559 msgstr ""
560
561 #: daemon/HTTPServer.cpp:1371
562 msgid "Domain can't end with .b32.i2p"
563 msgstr ""
564
565 #: daemon/HTTPServer.cpp:1374
566 msgid "Domain must end with .i2p"
567 msgstr ""
568
569 #: daemon/HTTPServer.cpp:1377
570 msgid "Such destination is not found"
571 msgstr ""
572
573 #: daemon/HTTPServer.cpp:1397
574 msgid "Unknown command"
575 msgstr ""
576
577 #: daemon/HTTPServer.cpp:1401
578 msgid "Command accepted"
579 msgstr ""
580
581 #: libi2pd_client/HTTPProxy.cpp:157
582 msgid "Proxy error"
583 msgstr ""
584
585 #: libi2pd_client/HTTPProxy.cpp:165
586 msgid "Proxy info"
587 msgstr ""
588
589 #: libi2pd_client/HTTPProxy.cpp:173
590 msgid "Proxy error: Host not found"
591 msgstr ""
592
593 #: libi2pd_client/HTTPProxy.cpp:174
594 msgid "Remote host not found in router's addressbook"
595 msgstr ""
596
597 #: libi2pd_client/HTTPProxy.cpp:175
598 msgid "You may try to find this host on jump services below"
599 msgstr ""
600
601 #: libi2pd_client/HTTPProxy.cpp:273 libi2pd_client/HTTPProxy.cpp:288
602 #: libi2pd_client/HTTPProxy.cpp:322 libi2pd_client/HTTPProxy.cpp:365
603 msgid "Invalid request"
604 msgstr ""
605
606 #: libi2pd_client/HTTPProxy.cpp:273
607 msgid "Proxy unable to parse your request"
608 msgstr ""
609
610 #: libi2pd_client/HTTPProxy.cpp:288
611 msgid "addresshelper is not supported"
612 msgstr ""
613
614 #: libi2pd_client/HTTPProxy.cpp:297 libi2pd_client/HTTPProxy.cpp:306
615 #: libi2pd_client/HTTPProxy.cpp:385
616 msgid "Host"
617 msgstr ""
618
619 #: libi2pd_client/HTTPProxy.cpp:297
620 msgid "added to router's addressbook from helper"
621 msgstr ""
622
623 #: libi2pd_client/HTTPProxy.cpp:298
624 msgid "Click here to proceed:"
625 msgstr ""
626
627 #: libi2pd_client/HTTPProxy.cpp:298 libi2pd_client/HTTPProxy.cpp:308
628 msgid "Continue"
629 msgstr ""
630
631 #: libi2pd_client/HTTPProxy.cpp:299 libi2pd_client/HTTPProxy.cpp:309
632 msgid "Addresshelper found"
633 msgstr ""
634
635 #: libi2pd_client/HTTPProxy.cpp:306
636 msgid "already in router's addressbook"
637 msgstr ""
638
639 #: libi2pd_client/HTTPProxy.cpp:307
640 msgid "Click here to update record:"
641 msgstr ""
642
643 #: libi2pd_client/HTTPProxy.cpp:322
644 msgid "invalid request uri"
645 msgstr ""
646
647 #: libi2pd_client/HTTPProxy.cpp:365
648 msgid "Can't detect destination host from request"
649 msgstr ""
650
651 #: libi2pd_client/HTTPProxy.cpp:382 libi2pd_client/HTTPProxy.cpp:386
652 msgid "Outproxy failure"
653 msgstr ""
654
655 #: libi2pd_client/HTTPProxy.cpp:382
656 msgid "bad outproxy settings"
657 msgstr ""
658
659 #: libi2pd_client/HTTPProxy.cpp:385
660 msgid "not inside I2P network, but outproxy is not enabled"
661 msgstr ""
662
663 #: libi2pd_client/HTTPProxy.cpp:474
664 msgid "unknown outproxy url"
665 msgstr ""
666
667 #: libi2pd_client/HTTPProxy.cpp:480
668 msgid "cannot resolve upstream proxy"
669 msgstr ""
670
671 #: libi2pd_client/HTTPProxy.cpp:488
672 msgid "hostname too long"
673 msgstr ""
674
675 #: libi2pd_client/HTTPProxy.cpp:515
676 msgid "cannot connect to upstream socks proxy"
677 msgstr ""
678
679 #: libi2pd_client/HTTPProxy.cpp:521
680 msgid "Cannot negotiate with socks proxy"
681 msgstr ""
682
683 #: libi2pd_client/HTTPProxy.cpp:563
684 msgid "CONNECT error"
685 msgstr ""
686
687 #: libi2pd_client/HTTPProxy.cpp:563
688 msgid "Failed to Connect"
689 msgstr ""
690
691 #: libi2pd_client/HTTPProxy.cpp:574 libi2pd_client/HTTPProxy.cpp:600
692 msgid "socks proxy error"
693 msgstr ""
694
695 #: libi2pd_client/HTTPProxy.cpp:582
696 msgid "failed to send request to upstream"
697 msgstr ""
698
699 #: libi2pd_client/HTTPProxy.cpp:603
700 msgid "No Reply From socks proxy"
701 msgstr ""
702
703 #: libi2pd_client/HTTPProxy.cpp:610
704 msgid "cannot connect"
705 msgstr ""
706
707 #: libi2pd_client/HTTPProxy.cpp:610
708 msgid "http out proxy not implemented"
709 msgstr ""
710
711 #: libi2pd_client/HTTPProxy.cpp:611
712 msgid "cannot connect to upstream http proxy"
713 msgstr ""
714
715 #: libi2pd_client/HTTPProxy.cpp:644
716 msgid "Host is down"
717 msgstr ""
718
719 #: libi2pd_client/HTTPProxy.cpp:644
720 msgid ""
721 "Can't create connection to requested host, it may be down. Please try again "
722 "later."
723 msgstr ""
0 `xgettext` command for extracting translation
1 ---
2
3 ```
4 xgettext --omit-header -ctr: -ktr -ktr:1,2 daemon/HTTPServer.cpp libi2pd_client/HTTPProxy.cpp
5 ```
6
7 Regex for transforming gettext translations to our format:
8 ---
9
10 ```
11 in: msgid\ \"(.*)\"\nmsgid_plural\ \"(.*)\"\nmsgstr\[0\]\ \"(.*)\"\nmsgstr\[1\]\ \"(.*)\"\n(msgstr\[2\]\ \"(.*)\"\n)?(msgstr\[3\]\ \"(.*)\"\n)?(msgstr\[4\]\ \"(.*)\"\n)?(msgstr\[5\]\ \"(.*)\"\n)?
12 out: #{"$2", {"$3", "$4", "$6", "$8", "$10"}},\n
13 ```
14
15 ```
16 in: msgid\ \"(.*)\"\nmsgstr\ \"(.*)\"\n
17 out: {"$1", "$2"},\n
18 ```
19
20 ```
21 in: ^#[:.](.*)$\n
22 out: <to empty line>
23 ```
24
25 ```
26 in: \n\n
27 out: \n
28 ```
1313 ## Use that path to store separated tunnels in different config files.
1414 ## Default: ~/.i2pd/tunnels.d or /var/lib/i2pd/tunnels.d
1515 # tunnelsdir = /var/lib/i2pd/tunnels.d
16
17 ## Path to certificates used for verifying .su3, families
18 ## Default: ~/.i2pd/certificates or /var/lib/i2pd/certificates
19 # certsdir = /var/lib/i2pd/certificates
1620
1721 ## Where to write pidfile (default: i2pd.pid, not used in Windows)
1822 # pidfile = /run/i2pd.pid
102106 # auth = true
103107 # user = i2pd
104108 # pass = changeme
109 ## Select webconsole language
110 ## Currently supported english (default), afrikaans, russian, turkmen, ukrainian and uzbek languages
111 # lang = english
105112
106113 [httpproxy]
107114 ## Uncomment and set to 'false' to disable HTTP Proxy
0 #!/sbin/openrc-run
1
2 pidfile="/var/run/i2pd/i2pd.pid"
3 logfile="/var/log/i2pd/i2pd.log"
4 mainconf="/etc/i2pd/i2pd.conf"
5 tunconf="/etc/i2pd/tunnels.conf"
6 tundir="/etc/i2pd/tunnels.conf.d"
7
8 name="i2pd"
9 command="/usr/sbin/i2pd"
10 command_args="--service --daemon --log=file --logfile=$logfile --conf=$mainconf --tunconf=$tunconf --tunnelsdir=$tundir --pidfile=$pidfile"
11 description="i2p router written in C++"
12 required_dirs="/var/lib/i2pd"
13 required_files="$mainconf"
14 start_stop_daemon_args="--chuid i2pd"
15
16 depend() {
17 need mountall
18 use net
19 after bootmisc
20 }
21
22 start_pre() {
23 if [ -r /etc/default/i2pd ]; then
24 . /etc/default/i2pd
25 fi
26
27 if [ "x$I2PD_ENABLED" != "xyes" ]; then
28 ewarn "i2pd disabled in /etc/default/i2pd"
29 exit 1
30 fi
31
32 checkpath -f -o i2pd:adm $logfile
33 checkpath -f -o i2pd:adm $pidfile
34
35 if [ -n "$DAEMON_OPTS" ]; then
36 command_args="$command_args $DAEMON_OPTS"
37 fi
38 }
00 %define git_hash %(git rev-parse HEAD | cut -c -7)
11
22 Name: i2pd-git
3 Version: 2.38.0
3 Version: 2.39.0
44 Release: git%{git_hash}%{?dist}
55 Summary: I2P router written in C++
66 Conflicts: i2pd
5555 %endif
5656 %endif
5757
58
59 %if 0%{?fedora} >= 36
60 pushd redhat-linux-build
61 %else
5862 %if 0%{?fedora} >= 33
5963 pushd %{_target_platform}
6064 %endif
65 %endif
6166
6267 %if 0%{?mageia} > 7
6368 pushd build
7681 %install
7782 pushd build
7883
84 %if 0%{?fedora} >= 36
85 pushd redhat-linux-build
86 %else
7987 %if 0%{?fedora} >= 33
8088 pushd %{_target_platform}
89 %endif
8190 %endif
8291
8392 %if 0%{?mageia}
136145
137146
138147 %changelog
148 * Mon Aug 23 2021 orignal <i2porignal@yandex.ru> - 2.39.0
149 - update to 2.39.0
150 - fixed build on fedora 36
151
139152 * Mon May 17 2021 orignal <i2porignal@yandex.ru> - 2.38.0
140153 - update to 2.38.0
141154
00 Name: i2pd
1 Version: 2.38.0
1 Version: 2.39.0
22 Release: 1%{?dist}
33 Summary: I2P router written in C++
44 Conflicts: i2pd-git
5353 %endif
5454 %endif
5555
56 %if 0%{?fedora} >= 36
57 pushd redhat-linux-build
58 %else
5659 %if 0%{?fedora} >= 33
5760 pushd %{_target_platform}
5861 %endif
62 %endif
5963
6064 %if 0%{?mageia} > 7
6165 pushd build
7478 %install
7579 pushd build
7680
81 %if 0%{?fedora} >= 36
82 pushd redhat-linux-build
83 %else
7784 %if 0%{?fedora} >= 33
7885 pushd %{_target_platform}
86 %endif
7987 %endif
8088
8189 %if 0%{?mageia}
134142
135143
136144 %changelog
145 * Mon Aug 23 2021 orignal <i2porignal@yandex.ru> - 2.39.0
146 - update to 2.39.0
147 - fixed build on fedora 36
148
137149 * Mon May 17 2021 orignal <i2porignal@yandex.ru> - 2.38.0
138150 - update to 2.38.0
139151
0 [IRC-IRC2P]
0 [IRC-ILITA]
11 type = client
22 address = 127.0.0.1
33 port = 6668
4 destination = irc.postman.i2p
4 destination = irc.ilita.i2p
55 destinationport = 6667
66 keys = irc-keys.dat
77
8 #[IRC-ILITA]
8 #[IRC-IRC2P]
99 #type = client
1010 #address = 127.0.0.1
1111 #port = 6669
12 #destination = irc.ilita.i2p
12 #destination = irc.postman.i2p
1313 #destinationport = 6667
1414 #keys = irc-keys.dat
1515
0 description "i2p client daemon"
1
2 start on runlevel [2345]
3 stop on runlevel [016] or unmounting-filesystem
4
5 # these can be overridden in /etc/init/i2pd.override
6 env LOGFILE="/var/log/i2pd/i2pd.log"
7
8 expect fork
9
10 exec /usr/sbin/i2pd --daemon --service --log=file --logfile=$LOGFILE
0 body {
1 font: 100%/1.5em sans-serif;
2 margin: 0;
3 padding: 1.5em;
4 background: #FAFAFA;
5 color: #103456;
6 }
7
8 a, .slide label {
9 text-decoration: none;
10 color: #894C84;
11 }
12
13 a:hover, .slide label:hover {
14 color: #FAFAFA;
15 background: #894C84;
16 }
17
18 a.button {
19 -webkit-appearance: button;
20 -moz-appearance: button;
21 appearance: button;
22 text-decoration: none;
23 padding: 0 5px;
24 border: 1px solid #894C84;
25 }
26
27 .header {
28 font-size: 2.5em;
29 text-align: center;
30 margin: 1em 0;
31 color: #894C84;
32 }
33
34 .wrapper {
35 margin: 0 auto;
36 padding: 1em;
37 max-width: 64em;
38 }
39
40 .menu {
41 display: block;
42 float: left;
43 overflow: hidden;
44 max-width: 12em;
45 white-space: nowrap;
46 text-overflow: ellipsis;
47 }
48
49 .listitem {
50 display: block;
51 font-family: monospace;
52 font-size: 1.2em;
53 white-space: nowrap;
54 }
55
56 .tableitem {
57 font-family: monospace;
58 font-size: 1.2em;
59 white-space: nowrap;
60 }
61
62 .content {
63 float: left;
64 font-size: 1em;
65 margin-left: 4em;
66 max-width: 48em;
67 overflow: auto;
68 }
69
70 .tunnel.established {
71 color: #56B734;
72 }
73
74 .tunnel.expiring {
75 color: #D3AE3F;
76 }
77
78 .tunnel.failed {
79 color: #D33F3F;
80 }
81
82 .tunnel.building {
83 color: #434343;
84 }
85
86 caption {
87 font-size: 1.5em;
88 text-align: center;
89 color: #894C84;
90 }
91
92 table {
93 display: table;
94 border-collapse: collapse;
95 text-align: center;
96 }
97
98 table.extaddr {
99 text-align: left;
100 }
101
102 table.services {
103 width: 100%;
104 }
105
106 textarea {
107 word-break: break-all;
108 }
109
110 .streamdest {
111 width: 120px;
112 max-width: 240px;
113 overflow: hidden;
114 text-overflow: ellipsis;
115 }
116
117 .slide div.slidecontent, .slide [type="checkbox"] {
118 display: none;
119 }
120
121 .slide [type="checkbox"]:checked ~ div.slidecontent {
122 display: block;
123 margin-top: 0;
124 padding: 0;
125 }
126
127 .disabled {
128 color: #D33F3F;
129 }
130
131 .enabled {
132 color: #56B734;
133 }
134
135 @media screen and (max-width: 1150px) { /* adaptive style */
136 .wrapper {
137 max-width: 58em;
138 }
139
140 .menu {
141 max-width: 10em;
142 }
143
144 .content {
145 margin-left: 2em;
146 max-width: 42em;
147 }
148 }
149
150 @media screen and (max-width: 980px) {
151 body {
152 padding: 1.5em 0 0 0;
153 }
154
155 .menu {
156 width: 100%;
157 max-width: unset;
158 display: block;
159 float: none;
160 position: unset;
161 font-size: 16px;
162 text-align: center;
163 }
164
165 .menu a, .commands a {
166 display: inline-block;
167 padding: 4px;
168 }
169
170 .content {
171 float: none;
172 margin-left: unset;
173 margin-top: 16px;
174 max-width: 100%;
175 width: 100%;
176 text-align: center;
177 }
178
179 a, .slide label {
180 /* margin-right: 10px; */
181 display: block;
182 /* font-size: 18px; */
183 }
184
185 .header {
186 margin: unset;
187 font-size: 1.5em;
188 }
189
190 small {
191 display: block
192 }
193
194 a.button {
195 -webkit-appearance: button;
196 -moz-appearance: button;
197 appearance: button;
198 text-decoration: none;
199 margin-top: 10px;
200 padding: 6px;
201 border: 1px solid #894c84;
202 width: -webkit-fill-available;
203 }
204
205 input, select {
206 width: 35%;
207 text-align: center;
208 padding: 5px;
209 border: 2px solid #ccc;
210 -webkit-border-radius: 5px;
211 border-radius: 5px;
212 font-size: 18px;
213 }
214
215 table.extaddr {
216 margin: auto;
217 text-align: unset;
218 }
219
220 textarea {
221 width: -webkit-fill-available;
222 height: auto;
223 padding:5px;
224 border:2px solid #ccc;
225 -webkit-border-radius: 5px;
226 border-radius: 5px;
227 font-size: 12px;
228 }
229
230 button[type=submit] {
231 padding: 5px 15px;
232 background: #ccc;
233 border: 0 none;
234 cursor: pointer;
235 -webkit-border-radius: 5px;
236 border-radius: 5px;
237 position: relative;
238 height: 36px;
239 display: -webkit-inline-box;
240 margin-top: 10px;
241 }
242 }
3131 #include "UPnP.h"
3232 #include "Timestamp.h"
3333 #include "util.h"
34 #include "I18N.h"
3435
3536 namespace i2p
3637 {
9192 i2p::config::Finalize();
9293
9394 i2p::config::GetOption("daemon", isDaemon);
95
96 std::string certsdir; i2p::config::GetOption("certsdir", certsdir);
97 i2p::fs::SetCertsDir(certsdir);
98
99 certsdir = i2p::fs::GetCertsDir();
94100
95101 std::string logs = ""; i2p::config::GetOption("log", logs);
96102 std::string logfile = ""; i2p::config::GetOption("logfile", logfile);
130136 LogPrint(eLogNone, "i2pd v", VERSION, " starting");
131137 LogPrint(eLogDebug, "FS: main config file: ", config);
132138 LogPrint(eLogDebug, "FS: data directory: ", datadir);
139 LogPrint(eLogDebug, "FS: certificates directory: ", certsdir);
133140
134141 bool precomputation; i2p::config::GetOption("precomputation.elgamal", precomputation);
135142 bool aesni; i2p::config::GetOption("cpuext.aesni", aesni);
342349 LogPrint(eLogInfo, "Daemon: using hidden mode");
343350 i2p::data::netdb.SetHidden(true);
344351 }
352
353 std::string httpLang; i2p::config::GetOption("http.lang", httpLang);
354 i2p::i18n::SetLanguage(httpLang);
355
345356 return true;
346357 }
347358
2929 #include "Daemon.h"
3030 #include "util.h"
3131 #include "ECIESX25519AEADRatchetSession.h"
32 #include "I18N.h"
3233
3334 #ifdef WIN32_APP
3435 #include "Win32App.h"
3940
4041 namespace i2p {
4142 namespace http {
42 const char *itoopieFavicon =
43 const std::string itoopieFavicon =
4344 "data:image/png;base64,"
4445 "iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACx"
4546 "jwv8YQUAAAAJcEhZcwAALiIAAC4iAari3ZIAAAAHdElNRQfgCQsUNSZrkhi1AAAAGXRFWHRTb2Z0"
5758 "JHYnlIsfzJjIp9xZKswL5YKBHL+coKJoRDaUSzoozxHVrygQU4JykQADAwAT5b1NHtwZugAAAABJ"
5859 "RU5ErkJggg==";
5960
60 const char *cssStyles =
61 // Bundled style
62 const std::string internalCSS =
6163 "<style>\r\n"
6264 " body { font: 100%/1.5em sans-serif; margin: 0; padding: 1.5em; background: #FAFAFA; color: #103456; }\r\n"
6365 " a, .slide label { text-decoration: none; color: #894C84; }\r\n"
6466 " a:hover, .slide label:hover { color: #FAFAFA; background: #894C84; }\r\n"
6567 " a.button { -webkit-appearance: button; -moz-appearance: button; appearance: button; text-decoration: none;\r\n"
66 " color: initial; padding: 0 5px; border: 1px solid #894C84; }\r\n"
68 " padding: 0 5px; border: 1px solid #894C84; }\r\n"
6769 " .header { font-size: 2.5em; text-align: center; margin: 1em 0; color: #894C84; }\r\n"
68 " .wrapper { margin: 0 auto; padding: 1em; max-width: 58em; }\r\n"
69 " .menu { float: left; } .menu a, .commands a { display: block; }\r\n"
70 " .wrapper { margin: 0 auto; padding: 1em; max-width: 64em; }\r\n"
71 " .menu { display: block; float: left; overflow: hidden; max-width: 12em; white-space: nowrap; text-overflow: ellipsis; }\r\n"
7072 " .listitem { display: block; font-family: monospace; font-size: 1.2em; white-space: nowrap; }\r\n"
7173 " .tableitem { font-family: monospace; font-size: 1.2em; white-space: nowrap; }\r\n"
72 " .content { float: left; font-size: 1em; margin-left: 4em; max-width: 45em; overflow: auto; }\r\n"
74 " .content { float: left; font-size: 1em; margin-left: 4em; max-width: 48em; overflow: auto; }\r\n"
7375 " .tunnel.established { color: #56B734; } .tunnel.expiring { color: #D3AE3F; }\r\n"
7476 " .tunnel.failed { color: #D33F3F; } .tunnel.building { color: #434343; }\r\n"
7577 " caption { font-size: 1.5em; text-align: center; color: #894C84; }\r\n"
7981 " .streamdest { width: 120px; max-width: 240px; overflow: hidden; text-overflow: ellipsis;}\r\n"
8082 " .slide div.slidecontent, .slide [type=\"checkbox\"] { display: none; }\r\n"
8183 " .slide [type=\"checkbox\"]:checked ~ div.slidecontent { display: block; margin-top: 0; padding: 0; }\r\n"
82 " .disabled:after { color: #D33F3F; content: \"Disabled\" }\r\n"
83 " .enabled:after { color: #56B734; content: \"Enabled\" }\r\n"
84 " @media screen and (max-width: 980px) {\r\n" /* adaptive style */
84 " .disabled { color: #D33F3F; } .enabled { color: #56B734; }\r\n"
85 " @media screen and (max-width: 1150px) {\r\n" /* adaptive style */
86 " .wrapper { max-width: 58em; } .menu { max-width: 10em; }\r\n"
87 " .content { margin-left: 2em; max-width: 42em; }\r\n"
88 " }\r\n"
89 " @media screen and (max-width: 980px) {\r\n"
8590 " body { padding: 1.5em 0 0 0; }\r\n"
86 " .menu { width: 100%; display: block; float: none; position: unset; font-size: 16px;\r\n"
91 " .menu { width: 100%; max-width: unset; display: block; float: none; position: unset; font-size: 16px;\r\n"
8792 " text-align: center; }\r\n"
88 " .menu a, .commands a { padding: 2px; }\r\n"
93 " .menu a, .commands a { display: inline-block; padding: 4px; }\r\n"
8994 " .content { float: none; margin-left: unset; margin-top: 16px; max-width: 100%; width: 100%;\r\n"
9095 " text-align: center; }\r\n"
9196 " a, .slide label { /* margin-right: 10px; */ display: block; /* font-size: 18px; */ }\r\n"
9297 " .header { margin: unset; font-size: 1.5em; } small {display: block}\r\n"
9398 " a.button { -webkit-appearance: button; -moz-appearance: button; appearance: button; text-decoration: none;\r\n"
94 " color: initial; margin-top: 10px; padding: 6px; border: 1px solid #894c84; width: -webkit-fill-available; }\r\n"
95 " input { width: 35%; text-align: center; padding: 5px;\r\n"
99 " margin-top: 10px; padding: 6px; border: 1px solid #894c84; width: -webkit-fill-available; }\r\n"
100 " input, select { width: 35%; text-align: center; padding: 5px;\r\n"
96101 " border: 2px solid #ccc; -webkit-border-radius: 5px; border-radius: 5px; font-size: 18px; }\r\n"
102 " table.extaddr { margin: auto; text-align: unset; }\r\n"
97103 " textarea { width: -webkit-fill-available; height: auto; padding:5px; border:2px solid #ccc;\r\n"
98104 " -webkit-border-radius: 5px; border-radius: 5px; font-size: 12px; }\r\n"
99105 " button[type=submit] { padding: 5px 15px; background: #ccc; border: 0 none; cursor: pointer;\r\n"
100106 " -webkit-border-radius: 5px; border-radius: 5px; position: relative; height: 36px; display: -webkit-inline-box; margin-top: 10px; }\r\n"
101 " }\r\n" /* adaptive style */
107 " }\r\n"
102108 "</style>\r\n";
109
110 // for external style sheet
111 std::string externalCSS;
112
113 static void LoadExtCSS ()
114 {
115 std::stringstream s;
116 std::string styleFile = i2p::fs::DataDirPath ("webconsole/style.css");
117 if (i2p::fs::Exists(styleFile)) {
118 std::ifstream f(styleFile, std::ifstream::binary);
119 s << f.rdbuf();
120 externalCSS = s.str();
121 } else if (externalCSS.length() != 0) { // clean up external style if file was removed
122 externalCSS = "";
123 }
124 }
125
126 static void GetStyles (std::stringstream& s)
127 {
128 if (externalCSS.length() != 0)
129 s << "<style>\r\n" << externalCSS << "</style>\r\n";
130 else
131 s << internalCSS;
132 }
103133
104134 const char HTTP_PAGE_TUNNELS[] = "tunnels";
105135 const char HTTP_PAGE_TRANSIT_TUNNELS[] = "transit_tunnels";
123153 const char HTTP_COMMAND_KILLSTREAM[] = "closestream";
124154 const char HTTP_COMMAND_LIMITTRANSIT[] = "limittransit";
125155 const char HTTP_COMMAND_GET_REG_STRING[] = "get_reg_string";
156 const char HTTP_COMMAND_SETLANGUAGE[] = "setlanguage";
157 const char HTTP_COMMAND_RELOAD_CSS[] = "reload_css";
126158 const char HTTP_PARAM_SAM_SESSION_ID[] = "id";
127159 const char HTTP_PARAM_ADDRESS[] = "address";
128160
129 static std::string ConvertTime (uint64_t time);
130 std::map<uint32_t, uint32_t> HTTPConnection::m_Tokens;
161 static std::string ConvertTime (uint64_t time)
162 {
163 lldiv_t divTime = lldiv(time, 1000);
164 time_t t = divTime.quot;
165 struct tm *tm = localtime(&t);
166 char date[128];
167 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);
168 return date;
169 }
131170
132171 static void ShowUptime (std::stringstream& s, int seconds)
133172 {
134173 int num;
135174
136175 if ((num = seconds / 86400) > 0) {
137 s << num << " days, ";
176 s << num << " " << tr("day", "days", num) << ", ";
138177 seconds -= num * 86400;
139178 }
140179 if ((num = seconds / 3600) > 0) {
141 s << num << " hours, ";
180 s << num << " " << tr("hour", "hours", num) << ", ";
142181 seconds -= num * 3600;
143182 }
144183 if ((num = seconds / 60) > 0) {
145 s << num << " min, ";
184 s << num << " " << tr("minute", "minutes", num) << ", ";
146185 seconds -= num * 60;
147186 }
148 s << seconds << " seconds";
187 s << seconds << " " << tr("second", "seconds", seconds);
149188 }
150189
151190 static void ShowTraffic (std::stringstream& s, uint64_t bytes)
153192 s << std::fixed << std::setprecision(2);
154193 auto numKBytes = (double) bytes / 1024;
155194 if (numKBytes < 1024)
156 s << numKBytes << " KiB";
195 s << numKBytes << " " << tr(/* tr: Kibibit */ "KiB");
157196 else if (numKBytes < 1024 * 1024)
158 s << numKBytes / 1024 << " MiB";
197 s << numKBytes / 1024 << " " << tr(/* tr: Mebibit */ "MiB");
159198 else
160 s << numKBytes / 1024 / 1024 << " GiB";
199 s << numKBytes / 1024 / 1024 << " " << tr(/* tr: Gibibit */ "GiB");
161200 }
162201
163202 static void ShowTunnelDetails (std::stringstream& s, enum i2p::tunnel::TunnelState eState, bool explr, int bytes)
164203 {
165 std::string state;
204 std::string state, stateText;
166205 switch (eState) {
167206 case i2p::tunnel::eTunnelStateBuildReplyReceived :
168 case i2p::tunnel::eTunnelStatePending : state = "building"; break;
207 case i2p::tunnel::eTunnelStatePending : state = "building"; break;
169208 case i2p::tunnel::eTunnelStateBuildFailed :
170209 case i2p::tunnel::eTunnelStateTestFailed :
171 case i2p::tunnel::eTunnelStateFailed : state = "failed"; break;
172 case i2p::tunnel::eTunnelStateExpiring : state = "expiring"; break;
210 case i2p::tunnel::eTunnelStateFailed : state = "failed"; break;
211 case i2p::tunnel::eTunnelStateExpiring : state = "expiring"; break;
173212 case i2p::tunnel::eTunnelStateEstablished : state = "established"; break;
174213 default: state = "unknown"; break;
175214 }
176 s << "<span class=\"tunnel " << state << "\"> " << state << ((explr) ? " (exploratory)" : "") << "</span>, ";
177 s << " " << (int) (bytes / 1024) << "&nbsp;KiB\r\n";
215
216 if (state == "building") stateText = tr("building");
217 else if (state == "failed") stateText = tr("failed");
218 else if (state == "expiring") stateText = tr("expiring");
219 else if (state == "established") stateText = tr("established");
220 else stateText = tr("unknown");
221
222 s << "<span class=\"tunnel " << state << "\"> " << stateText << ((explr) ? " (" + tr("exploratory") + ")" : "") << "</span>, ";
223 s << " " << (int) (bytes / 1024) << "&nbsp;" << tr(/* tr: Kibibit */ "KiB") << "\r\n";
178224 }
179225
180226 static void SetLogLevel (const std::string& level)
190236
191237 static void ShowPageHead (std::stringstream& s)
192238 {
193 std::string webroot;
194 i2p::config::GetOption("http.webroot", webroot);
239 std::string webroot; i2p::config::GetOption("http.webroot", webroot);
240
241 // Page language
242 std::string currLang = i2p::context.GetLanguage ()->GetLanguage(); // get current used language
243 auto it = i2p::i18n::languages.find(currLang);
244 std::string langCode = it->second.ShortCode;
245
195246 s <<
196247 "<!DOCTYPE html>\r\n"
197 "<html lang=\"en\">\r\n" /* TODO: Add support for locale */
248 "<html lang=\"" << langCode << "\">\r\n"
198249 " <head>\r\n" /* TODO: Find something to parse html/template system. This is horrible. */
199 #if (!defined(WIN32))
200250 " <meta charset=\"UTF-8\">\r\n"
201 #else
202 " <meta charset=\"windows-1251\">\r\n"
203 #endif
204251 " <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\r\n"
205252 " <link rel=\"shortcut icon\" href=\"" << itoopieFavicon << "\">\r\n"
206 " <title>Purple I2P " VERSION " Webconsole</title>\r\n"
207 << cssStyles <<
208 "</head>\r\n";
253 " <title>Purple I2P " VERSION " Webconsole</title>\r\n";
254 GetStyles(s);
209255 s <<
256 "</head>\r\n"
210257 "<body>\r\n"
211 "<div class=\"header\"><b>i2pd</b> webconsole</div>\r\n"
258 "<div class=\"header\">" << tr("<b>i2pd</b> webconsole") << "</div>\r\n"
212259 "<div class=\"wrapper\">\r\n"
213260 "<div class=\"menu\">\r\n"
214 " <a href=\"" << webroot << "\">Main page</a><br>\r\n"
215 " <a href=\"" << webroot << "?page=" << HTTP_PAGE_COMMANDS << "\">Router commands</a>\r\n"
216 " <a href=\"" << webroot << "?page=" << HTTP_PAGE_LOCAL_DESTINATIONS << "\">Local destinations</a>\r\n";
261 " <a href=\"" << webroot << "\">" << tr("Main page") << "</a><br><br>\r\n"
262 " <a href=\"" << webroot << "?page=" << HTTP_PAGE_COMMANDS << "\">" << tr("Router commands") << "</a><br>\r\n"
263 " <a href=\"" << webroot << "?page=" << HTTP_PAGE_LOCAL_DESTINATIONS << "\">" << tr("Local Destinations") << "</a><br>\r\n";
217264 if (i2p::context.IsFloodfill ())
218 s << " <a href=\"" << webroot << "?page=" << HTTP_PAGE_LEASESETS << "\">LeaseSets</a>\r\n";
265 s << " <a href=\"" << webroot << "?page=" << HTTP_PAGE_LEASESETS << "\">" << tr("LeaseSets") << "</a><br>\r\n";
219266 s <<
220 " <a href=\"" << webroot << "?page=" << HTTP_PAGE_TUNNELS << "\">Tunnels</a>\r\n"
221 " <a href=\"" << webroot << "?page=" << HTTP_PAGE_TRANSIT_TUNNELS << "\">Transit tunnels</a>\r\n"
222 " <a href=\"" << webroot << "?page=" << HTTP_PAGE_TRANSPORTS << "\">Transports</a>\r\n"
223 " <a href=\"" << webroot << "?page=" << HTTP_PAGE_I2P_TUNNELS << "\">I2P tunnels</a>\r\n";
267 " <a href=\"" << webroot << "?page=" << HTTP_PAGE_TUNNELS << "\">" << tr("Tunnels") << "</a><br>\r\n"
268 " <a href=\"" << webroot << "?page=" << HTTP_PAGE_TRANSIT_TUNNELS << "\">" << tr("Transit Tunnels") << "</a><br>\r\n"
269 " <a href=\"" << webroot << "?page=" << HTTP_PAGE_TRANSPORTS << "\">" << tr ("Transports") << "</a><br>\r\n"
270 " <a href=\"" << webroot << "?page=" << HTTP_PAGE_I2P_TUNNELS << "\">" << tr("I2P tunnels") << "</a><br>\r\n";
224271 if (i2p::client::context.GetSAMBridge ())
225 s << " <a href=\"" << webroot << "?page=" << HTTP_PAGE_SAM_SESSIONS << "\">SAM sessions</a>\r\n";
272 s << " <a href=\"" << webroot << "?page=" << HTTP_PAGE_SAM_SESSIONS << "\">" << tr("SAM sessions") << "</a><br>\r\n";
226273 s <<
227274 "</div>\r\n"
228275 "<div class=\"content\">";
238285
239286 static void ShowError(std::stringstream& s, const std::string& string)
240287 {
241 s << "<b>ERROR:</b>&nbsp;" << string << "<br>\r\n";
288 s << "<b>" << tr("ERROR") << ":</b>&nbsp;" << string << "<br>\r\n";
242289 }
243290
244291 static void ShowNetworkStatus (std::stringstream& s, RouterStatus status)
245292 {
246293 switch (status)
247294 {
248 case eRouterStatusOK: s << "OK"; break;
249 case eRouterStatusTesting: s << "Testing"; break;
250 case eRouterStatusFirewalled: s << "Firewalled"; break;
251 case eRouterStatusUnknown: s << "Unknown"; break;
252 case eRouterStatusProxy: s << "Proxy"; break;
253 case eRouterStatusMesh: s << "Mesh"; break;
295 case eRouterStatusOK: s << tr("OK"); break;
296 case eRouterStatusTesting: s << tr("Testing"); break;
297 case eRouterStatusFirewalled: s << tr("Firewalled"); break;
298 case eRouterStatusUnknown: s << tr("Unknown"); break;
299 case eRouterStatusProxy: s << tr("Proxy"); break;
300 case eRouterStatusMesh: s << tr("Mesh"); break;
254301 case eRouterStatusError:
255302 {
256 s << "Error";
303 s << tr("Error");
257304 switch (i2p::context.GetError ())
258305 {
259306 case eRouterErrorClockSkew:
260 s << " - Clock skew";
307 s << " - " << tr("Clock skew");
261308 break;
262309 case eRouterErrorOffline:
263 s << " - Offline";
310 s << " - " << tr("Offline");
264311 break;
265312 case eRouterErrorSymmetricNAT:
266 s << " - Symmetric NAT";
313 s << " - " << tr("Symmetric NAT");
267314 break;
268315 default: ;
269316 }
270317 break;
271318 }
272 default: s << "Unknown";
319 default: s << tr("Unknown");
273320 }
274321 }
275322
276323 void ShowStatus (std::stringstream& s, bool includeHiddenContent, i2p::http::OutputFormatEnum outputFormat)
277324 {
278 s << "<b>Uptime:</b> ";
325 s << "<b>" << tr("Uptime") << ":</b> ";
279326 ShowUptime(s, i2p::context.GetUptime ());
280327 s << "<br>\r\n";
281 s << "<b>Network status:</b> ";
328 s << "<b>" << tr("Network status") << ":</b> ";
282329 ShowNetworkStatus (s, i2p::context.GetStatus ());
283330 s << "<br>\r\n";
284331 if (i2p::context.SupportsV6 ())
285332 {
286 s << "<b>Network status 6:</b> ";
333 s << "<b>" << tr("Network status v6") << ":</b> ";
287334 ShowNetworkStatus (s, i2p::context.GetStatusV6 ());
288335 s << "<br>\r\n";
289336 }
290337 #if ((!defined(WIN32) && !defined(QT_GUI_LIB) && !defined(ANDROID)) || defined(ANDROID_BINARY))
291338 if (auto remains = Daemon.gracefulShutdownInterval) {
292 s << "<b>Stopping in:</b> ";
339 s << "<b>" << tr("Stopping in") << ":</b> ";
293340 ShowUptime(s, remains);
294341 s << "<br>\r\n";
295342 }
296343 #elif defined(WIN32_APP)
297344 if (i2p::win32::g_GracefulShutdownEndtime != 0) {
298345 uint16_t remains = (i2p::win32::g_GracefulShutdownEndtime - GetTickCount()) / 1000;
299 s << "<b>Stopping in:</b> ";
346 s << "<b>" << tr("Stopping in") << ":</b> ";
300347 ShowUptime(s, remains);
301348 s << "<br>\r\n";
302349 }
303350 #endif
304351 auto family = i2p::context.GetFamily ();
305352 if (family.length () > 0)
306 s << "<b>Family:</b> " << family << "<br>\r\n";
307 s << "<b>Tunnel creation success rate:</b> " << i2p::tunnel::tunnels.GetTunnelCreationSuccessRate () << "%<br>\r\n";
308 s << "<b>Received:</b> ";
353 s << "<b>"<< tr("Family") << ":</b> " << family << "<br>\r\n";
354 s << "<b>" << tr("Tunnel creation success rate") << ":</b> " << i2p::tunnel::tunnels.GetTunnelCreationSuccessRate () << "%<br>\r\n";
355 s << "<b>" << tr("Received") << ":</b> ";
309356 ShowTraffic (s, i2p::transport::transports.GetTotalReceivedBytes ());
310 s << " (" << (double) i2p::transport::transports.GetInBandwidth () / 1024 << " KiB/s)<br>\r\n";
311 s << "<b>Sent:</b> ";
357 s << " (" << (double) i2p::transport::transports.GetInBandwidth () / 1024 << " " << tr(/* tr: Kibibit/s */ "KiB/s") << ")<br>\r\n";
358 s << "<b>" << tr("Sent") << ":</b> ";
312359 ShowTraffic (s, i2p::transport::transports.GetTotalSentBytes ());
313 s << " (" << (double) i2p::transport::transports.GetOutBandwidth () / 1024 << " KiB/s)<br>\r\n";
314 s << "<b>Transit:</b> ";
360 s << " (" << (double) i2p::transport::transports.GetOutBandwidth () / 1024 << " " << tr(/* tr: Kibibit/s */ "KiB/s") << ")<br>\r\n";
361 s << "<b>" << tr("Transit") << ":</b> ";
315362 ShowTraffic (s, i2p::transport::transports.GetTotalTransitTransmittedBytes ());
316 s << " (" << (double) i2p::transport::transports.GetTransitBandwidth () / 1024 << " KiB/s)<br>\r\n";
317 s << "<b>Data path:</b> " << i2p::fs::GetDataDir() << "<br>\r\n";
363 s << " (" << (double) i2p::transport::transports.GetTransitBandwidth () / 1024 << " " << tr(/* tr: Kibibit/s */ "KiB/s") << ")<br>\r\n";
364 s << "<b>" << tr("Data path") << ":</b> " << i2p::fs::GetUTF8DataDir() << "<br>\r\n";
318365 s << "<div class='slide'>";
319 if((outputFormat==OutputFormatEnum::forWebConsole)||!includeHiddenContent) {
320 s << "<label for=\"slide-info\">Hidden content. Press on text to see.</label>\r\n<input type=\"checkbox\" id=\"slide-info\" />\r\n<div class=\"slidecontent\">\r\n";
366 if((outputFormat == OutputFormatEnum::forWebConsole) || !includeHiddenContent) {
367 s << "<label for=\"slide-info\">" << tr("Hidden content. Press on text to see.") << "</label>\r\n<input type=\"checkbox\" id=\"slide-info\" />\r\n<div class=\"slidecontent\">\r\n";
321368 }
322369 if(includeHiddenContent) {
323 s << "<b>Router Ident:</b> " << i2p::context.GetRouterInfo().GetIdentHashBase64() << "<br>\r\n";
370 s << "<b>" << tr("Router Ident") << ":</b> " << i2p::context.GetRouterInfo().GetIdentHashBase64() << "<br>\r\n";
324371 if (!i2p::context.GetRouterInfo().GetProperty("family").empty())
325 s << "<b>Router Family:</b> " << i2p::context.GetRouterInfo().GetProperty("family") << "<br>\r\n";
326 s << "<b>Router Caps:</b> " << i2p::context.GetRouterInfo().GetProperty("caps") << "<br>\r\n";
327 s << "<b>Version:</b> " VERSION "<br>\r\n";
328 s << "<b>Our external address:</b>" << "<br>\r\n<table class=\"extaddr\"><tbody>\r\n";
372 s << "<b>" << tr("Router Family") << ":</b> " << i2p::context.GetRouterInfo().GetProperty("family") << "<br>\r\n";
373 s << "<b>" << tr("Router Caps") << ":</b> " << i2p::context.GetRouterInfo().GetProperty("caps") << "<br>\r\n";
374 s << "<b>" << tr("Version") << ":</b> " VERSION "<br>\r\n";
375 s << "<b>"<< tr("Our external address") << ":</b>" << "<br>\r\n<table class=\"extaddr\"><tbody>\r\n";
329376 for (const auto& address : i2p::context.GetRouterInfo().GetAddresses())
330377 {
331378 s << "<tr>\r\n";
333380 {
334381 s << "<td>NTCP2";
335382 if (address->host.is_v6 ()) s << "v6";
336 s << "</td><td>supported</td>\r\n</tr>\r\n";
383 s << "</td><td>" << tr("supported") << "</td>\r\n</tr>\r\n";
337384 continue;
338385 }
339386 switch (address->transportStyle)
355402 break;
356403 }
357404 default:
358 s << "<td>Unknown</td>\r\n";
405 s << "<td>" << tr("Unknown") << "</td>\r\n";
359406 }
360407 s << "<td>" << address->host.to_string() << ":" << address->port << "</td>\r\n</tr>\r\n";
361408 }
362409 s << "</tbody></table>\r\n";
363410 }
364411 s << "</div>\r\n</div>\r\n";
365 if(outputFormat==OutputFormatEnum::forQtUi) {
412 if(outputFormat == OutputFormatEnum::forQtUi) {
366413 s << "<br>";
367414 }
368 s << "<b>Routers:</b> " << i2p::data::netdb.GetNumRouters () << " ";
369 s << "<b>Floodfills:</b> " << i2p::data::netdb.GetNumFloodfills () << " ";
370 s << "<b>LeaseSets:</b> " << i2p::data::netdb.GetNumLeaseSets () << "<br>\r\n";
415 s << "<b>" << tr("Routers") << ":</b> " << i2p::data::netdb.GetNumRouters () << " ";
416 s << "<b>" << tr("Floodfills") << ":</b> " << i2p::data::netdb.GetNumFloodfills () << " ";
417 s << "<b>" << tr("LeaseSets") << ":</b> " << i2p::data::netdb.GetNumLeaseSets () << "<br>\r\n";
371418
372419 size_t clientTunnelCount = i2p::tunnel::tunnels.CountOutboundTunnels();
373420 clientTunnelCount += i2p::tunnel::tunnels.CountInboundTunnels();
374421 size_t transitTunnelCount = i2p::tunnel::tunnels.CountTransitTunnels();
375422
376 s << "<b>Client Tunnels:</b> " << std::to_string(clientTunnelCount) << " ";
377 s << "<b>Transit Tunnels:</b> " << std::to_string(transitTunnelCount) << "<br>\r\n<br>\r\n";
423 s << "<b>" << tr("Client Tunnels") << ":</b> " << std::to_string(clientTunnelCount) << " ";
424 s << "<b>" << tr("Transit Tunnels") << ":</b> " << std::to_string(transitTunnelCount) << "<br>\r\n<br>\r\n";
378425
379426 if(outputFormat==OutputFormatEnum::forWebConsole) {
380 bool i2pcontrol; i2p::config::GetOption("i2pcontrol.enabled", i2pcontrol);
381 s << "<table class=\"services\"><caption>Services</caption><tbody>\r\n";
382 s << "<tr><td>" << "HTTP Proxy" << "</td><td><div class='" << ((i2p::client::context.GetHttpProxy ()) ? "enabled" : "disabled") << "'></div></td></tr>\r\n";
383 s << "<tr><td>" << "SOCKS Proxy" << "</td><td><div class='" << ((i2p::client::context.GetSocksProxy ()) ? "enabled" : "disabled") << "'></div></td></tr>\r\n";
384 s << "<tr><td>" << "BOB" << "</td><td><div class='" << ((i2p::client::context.GetBOBCommandChannel ()) ? "enabled" : "disabled") << "'></div></td></tr>\r\n";
385 s << "<tr><td>" << "SAM" << "</td><td><div class='" << ((i2p::client::context.GetSAMBridge ()) ? "enabled" : "disabled") << "'></div></td></tr>\r\n";
386 s << "<tr><td>" << "I2CP" << "</td><td><div class='" << ((i2p::client::context.GetI2CPServer ()) ? "enabled" : "disabled") << "'></div></td></tr>\r\n";
387 s << "<tr><td>" << "I2PControl" << "</td><td><div class='" << ((i2pcontrol) ? "enabled" : "disabled") << "'></div></td></tr>\r\n";
427 bool httpproxy = i2p::client::context.GetHttpProxy () ? true : false;
428 bool socksproxy = i2p::client::context.GetSocksProxy () ? true : false;
429 bool bob = i2p::client::context.GetBOBCommandChannel () ? true : false;
430 bool sam = i2p::client::context.GetSAMBridge () ? true : false;
431 bool i2cp = i2p::client::context.GetI2CPServer () ? true : false;
432 bool i2pcontrol; i2p::config::GetOption("i2pcontrol.enabled", i2pcontrol);
433 s << "<table class=\"services\"><caption>" << tr("Services") << "</caption><tbody>\r\n";
434 s << "<tr><td>" << "HTTP " << tr("Proxy") << "</td><td class='" << (httpproxy ? "enabled" : "disabled") << "'>" << (httpproxy ? tr("Enabled") : tr("Disabled")) << "</td></tr>\r\n";
435 s << "<tr><td>" << "SOCKS " << tr("Proxy") << "</td><td class='" << (socksproxy ? "enabled" : "disabled") << "'>" << (socksproxy ? tr("Enabled") : tr("Disabled")) << "</td></tr>\r\n";
436 s << "<tr><td>" << "BOB" << "</td><td class='" << (bob ? "enabled" : "disabled") << "'>" << (bob ? tr("Enabled") : tr("Disabled")) << "</td></tr>\r\n";
437 s << "<tr><td>" << "SAM" << "</td><td class='" << (sam ? "enabled" : "disabled") << "'>" << (sam ? tr("Enabled") : tr("Disabled")) << "</td></tr>\r\n";
438 s << "<tr><td>" << "I2CP" << "</td><td class='" << (i2cp ? "enabled" : "disabled") << "'>" << (i2cp ? tr("Enabled") : tr("Disabled")) << "</td></tr>\r\n";
439 s << "<tr><td>" << "I2PControl" << "</td><td class='" << (i2pcontrol ? "enabled" : "disabled") << "'>" << (i2pcontrol ? tr("Enabled") : tr("Disabled")) << "</td></tr>\r\n";
388440 s << "</tbody></table>\r\n";
389441 }
390442 }
392444 void ShowLocalDestinations (std::stringstream& s)
393445 {
394446 std::string webroot; i2p::config::GetOption("http.webroot", webroot);
395 s << "<b>Local Destinations:</b><br>\r\n<div class=\"list\">\r\n";
447 s << "<b>" << tr("Local Destinations") << ":</b><br>\r\n<div class=\"list\">\r\n";
396448 for (auto& it: i2p::client::context.GetDestinations ())
397449 {
398450 auto ident = it.second->GetIdentHash ();
404456 auto i2cpServer = i2p::client::context.GetI2CPServer ();
405457 if (i2cpServer && !(i2cpServer->GetSessions ().empty ()))
406458 {
407 s << "<br><b>I2CP Local Destinations:</b><br>\r\n<div class=\"list\">\r\n";
459 s << "<br><b>I2CP "<< tr("Local Destinations") << ":</b><br>\r\n<div class=\"list\">\r\n";
408460 for (auto& it: i2cpServer->GetSessions ())
409461 {
410462 auto dest = it.second->GetDestination ();
427479 if (dest->IsEncryptedLeaseSet ())
428480 {
429481 i2p::data::BlindedPublicKey blinded (dest->GetIdentity (), dest->IsPerClientAuth ());
430 s << "<div class='slide'><label for='slide-b33'><b>Encrypted B33 address:</b></label>\r\n<input type=\"checkbox\" id=\"slide-b33\" />\r\n<div class=\"slidecontent\">\r\n";
482 s << "<div class='slide'><label for='slide-b33'><b>" << tr("Encrypted B33 address") << ":</b></label>\r\n<input type=\"checkbox\" id=\"slide-b33\" />\r\n<div class=\"slidecontent\">\r\n";
431483 s << blinded.ToB33 () << ".b32.i2p<br>\r\n";
432484 s << "</div>\r\n</div>\r\n";
433485 }
436488 {
437489 std::string webroot; i2p::config::GetOption("http.webroot", webroot);
438490 auto base32 = dest->GetIdentHash ().ToBase32 ();
439 s << "<div class='slide'><label for='slide-regaddr'><b>Address registration line</b></label>\r\n<input type=\"checkbox\" id=\"slide-regaddr\" />\r\n<div class=\"slidecontent\">\r\n"
491 s << "<div class='slide'><label for='slide-regaddr'><b>" << tr("Address registration line") << "</b></label>\r\n<input type=\"checkbox\" id=\"slide-regaddr\" />\r\n<div class=\"slidecontent\">\r\n"
440492 "<form method=\"get\" action=\"" << webroot << "\">\r\n"
441493 " <input type=\"hidden\" name=\"cmd\" value=\"" << HTTP_COMMAND_GET_REG_STRING << "\">\r\n"
442494 " <input type=\"hidden\" name=\"token\" value=\"" << token << "\">\r\n"
443495 " <input type=\"hidden\" name=\"b32\" value=\"" << base32 << "\">\r\n"
444 " <b>Domain:</b>\r\n<input type=\"text\" maxlength=\"67\" name=\"name\" placeholder=\"domain.i2p\" required>\r\n"
445 " <button type=\"submit\">Generate</button>\r\n"
446 "</form>\r\n<small><b>Note:</b> result string can be used only for registering 2LD domains (example.i2p). For registering subdomains please use i2pd-tools.</small>\r\n</div>\r\n</div>\r\n<br>\r\n";
496 " <b>" << tr("Domain") << ":</b>\r\n<input type=\"text\" maxlength=\"67\" name=\"name\" placeholder=\"domain.i2p\" required>\r\n"
497 " <button type=\"submit\">" << tr("Generate") << "</button>\r\n"
498 "</form>\r\n<small>" << tr("<b>Note:</b> result string can be used only for registering 2LD domains (example.i2p). For registering subdomains please use i2pd-tools.") << "</small>\r\n</div>\r\n</div>\r\n<br>\r\n";
447499 }
448500
449501 if(dest->GetNumRemoteLeaseSets())
450502 {
451 s << "<div class='slide'><label for='slide-lease'><b>LeaseSets:</b> <i>" << dest->GetNumRemoteLeaseSets ()
452 << "</i></label>\r\n<input type=\"checkbox\" id=\"slide-lease\" />\r\n<div class=\"slidecontent\">\r\n<table><thead><th>Address</th><th>Type</th><th>EncType</th></thead><tbody class=\"tableitem\">";
503 s << "<div class='slide'><label for='slide-lease'><b>" << tr("LeaseSets") << ":</b> <i>" << dest->GetNumRemoteLeaseSets ()
504 << "</i></label>\r\n<input type=\"checkbox\" id=\"slide-lease\" />\r\n<div class=\"slidecontent\">\r\n<table><thead><th>"<< tr("Address") << "</th><th>" << tr("Type") << "</th><th>" << tr("EncType") << "</th></thead><tbody class=\"tableitem\">";
453505 for(auto& it: dest->GetLeaseSets ())
454506 s << "<tr><td>" << it.first.ToBase32 () << "</td><td>" << (int)it.second->GetStoreType () << "</td><td>" << (int)it.second->GetEncryptionType () <<"</td></tr>\r\n";
455507 s << "</tbody></table>\r\n</div>\r\n</div>\r\n<br>\r\n";
456508 } else
457 s << "<b>LeaseSets:</b> <i>0</i><br>\r\n<br>\r\n";
509 s << "<b>" << tr("LeaseSets") << ":</b> <i>0</i><br>\r\n<br>\r\n";
458510
459511 auto pool = dest->GetTunnelPool ();
460512 if (pool)
461513 {
462 s << "<b>Inbound tunnels:</b><br>\r\n<div class=\"list\">\r\n";
514 s << "<b>" << tr("Inbound tunnels") << ":</b><br>\r\n<div class=\"list\">\r\n";
463515 for (auto & it : pool->GetInboundTunnels ()) {
464516 s << "<div class=\"listitem\">";
465517 it->Print(s);
466518 if(it->LatencyIsKnown())
467 s << " ( " << it->GetMeanLatency() << "ms )";
519 s << " ( " << it->GetMeanLatency() << tr(/* tr: Milliseconds */ "ms") << " )";
468520 ShowTunnelDetails(s, it->GetState (), false, it->GetNumReceivedBytes ());
469521 s << "</div>\r\n";
470522 }
471523 s << "<br>\r\n";
472 s << "<b>Outbound tunnels:</b><br>\r\n<div class=\"list\">\r\n";
524 s << "<b>" << tr("Outbound tunnels") << ":</b><br>\r\n<div class=\"list\">\r\n";
473525 for (auto & it : pool->GetOutboundTunnels ()) {
474526 s << "<div class=\"listitem\">";
475527 it->Print(s);
476528 if(it->LatencyIsKnown())
477 s << " ( " << it->GetMeanLatency() << "ms )";
529 s << " ( " << it->GetMeanLatency() << tr("ms") << " )";
478530 ShowTunnelDetails(s, it->GetState (), false, it->GetNumSentBytes ());
479531 s << "</div>\r\n";
480532 }
481533 }
482534 s << "<br>\r\n";
483535
484 s << "<b>Tags</b><br>\r\nIncoming: <i>" << dest->GetNumIncomingTags () << "</i><br>\r\n";
536 s << "<b>" << tr("Tags") << "</b><br>\r\n" << tr("Incoming") << ": <i>" << dest->GetNumIncomingTags () << "</i><br>\r\n";
485537 if (!dest->GetSessions ().empty ()) {
486538 std::stringstream tmp_s; uint32_t out_tags = 0;
487539 for (const auto& it: dest->GetSessions ()) {
488540 tmp_s << "<tr><td>" << i2p::client::context.GetAddressBook ().ToAddress(it.first) << "</td><td>" << it.second->GetNumOutgoingTags () << "</td></tr>\r\n";
489541 out_tags += it.second->GetNumOutgoingTags ();
490542 }
491 s << "<div class='slide'><label for='slide-tags'>Outgoing: <i>" << out_tags << "</i></label>\r\n<input type=\"checkbox\" id=\"slide-tags\" />\r\n"
492 << "<div class=\"slidecontent\">\r\n<table>\r\n<thead><th>Destination</th><th>Amount</th></thead>\r\n<tbody class=\"tableitem\">\r\n" << tmp_s.str () << "</tbody></table>\r\n</div>\r\n</div>\r\n";
543 s << "<div class='slide'><label for='slide-tags'>" << tr("Outgoing") << ": <i>" << out_tags << "</i></label>\r\n<input type=\"checkbox\" id=\"slide-tags\" />\r\n"
544 << "<div class=\"slidecontent\">\r\n<table>\r\n<thead><th>" << tr("Destination") << "</th><th>" << tr("Amount") << "</th></thead>\r\n<tbody class=\"tableitem\">\r\n" << tmp_s.str () << "</tbody></table>\r\n</div>\r\n</div>\r\n";
493545 } else
494 s << "Outgoing: <i>0</i><br>\r\n";
546 s << tr("Outgoing") << ": <i>0</i><br>\r\n";
495547 s << "<br>\r\n";
496548
497549 auto numECIESx25519Tags = dest->GetNumIncomingECIESx25519Tags ();
498550 if (numECIESx25519Tags > 0) {
499 s << "<b>ECIESx25519</b><br>\r\nIncoming Tags: <i>" << numECIESx25519Tags << "</i><br>\r\n";
551 s << "<b>ECIESx25519</b><br>\r\n" << tr("Incoming Tags") << ": <i>" << numECIESx25519Tags << "</i><br>\r\n";
500552 if (!dest->GetECIESx25519Sessions ().empty ())
501553 {
502554 std::stringstream tmp_s; uint32_t ecies_sessions = 0;
504556 tmp_s << "<tr><td>" << i2p::client::context.GetAddressBook ().ToAddress(it.second->GetDestination ()) << "</td><td>" << it.second->GetState () << "</td></tr>\r\n";
505557 ecies_sessions++;
506558 }
507 s << "<div class='slide'><label for='slide-ecies-sessions'>Tags sessions: <i>" << ecies_sessions << "</i></label>\r\n<input type=\"checkbox\" id=\"slide-ecies-sessions\" />\r\n"
508 << "<div class=\"slidecontent\">\r\n<table>\r\n<thead><th>Destination</th><th>Status</th></thead>\r\n<tbody class=\"tableitem\">\r\n" << tmp_s.str () << "</tbody></table>\r\n</div>\r\n</div>\r\n";
559 s << "<div class='slide'><label for='slide-ecies-sessions'>" << tr("Tags sessions") << ": <i>" << ecies_sessions << "</i></label>\r\n<input type=\"checkbox\" id=\"slide-ecies-sessions\" />\r\n"
560 << "<div class=\"slidecontent\">\r\n<table>\r\n<thead><th>" << tr("Destination") << "</th><th>" << tr("Status") << "</th></thead>\r\n<tbody class=\"tableitem\">\r\n" << tmp_s.str () << "</tbody></table>\r\n</div>\r\n</div>\r\n";
509561 } else
510 s << "Tags sessions: <i>0</i><br>\r\n";
562 s << tr("Tags sessions") << ": <i>0</i><br>\r\n";
511563 s << "<br>\r\n";
512564 }
513565 }
514566
515567 void ShowLocalDestination (std::stringstream& s, const std::string& b32, uint32_t token)
516568 {
517 s << "<b>Local Destination:</b><br>\r\n<br>\r\n";
569 s << "<b>" << tr("Local Destination") << ":</b><br>\r\n<br>\r\n";
518570 i2p::data::IdentHash ident;
519571 ident.FromBase32 (b32);
520572 auto dest = i2p::client::context.FindLocalDestination (ident);
522574 if (dest)
523575 {
524576 ShowLeaseSetDestination (s, dest, token);
525 // show streams
526 s << "<table>\r\n<caption>Streams</caption>\r\n<thead>\r\n<tr>";
577
578 // Print table with streams information
579 s << "<table>\r\n<caption>" << tr("Streams") << "</caption>\r\n<thead>\r\n<tr>";
527580 s << "<th style=\"width:25px;\">StreamID</th>";
528581 s << "<th style=\"width:5px;\" \\>"; // Stream closing button column
529582 s << "<th class=\"streamdest\">Destination</th>";
545598 s << "<td>" << it->GetRecvStreamID () << "</td>";
546599 if (it->GetRecvStreamID ()) {
547600 s << "<td><a class=\"button\" href=\"/?cmd=" << HTTP_COMMAND_KILLSTREAM << "&b32=" << b32 << "&streamID="
548 << it->GetRecvStreamID () << "&token=" << token << "\" title=\"Close stream\"> &#10008; </a></td>";
601 << it->GetRecvStreamID () << "&token=" << token << "\" title=\"" << tr("Close stream") << "\"> &#10008; </a></td>";
549602 } else {
550603 s << "<td \\>";
551604 }
569622 auto i2cpServer = i2p::client::context.GetI2CPServer ();
570623 if (i2cpServer)
571624 {
572 s << "<b>I2CP Local Destination:</b><br>\r\n<br>\r\n";
625 s << "<b>I2CP " << tr("Local Destination") << ":</b><br>\r\n<br>\r\n";
573626 auto it = i2cpServer->GetSessions ().find (std::stoi (id));
574627 if (it != i2cpServer->GetSessions ().end ())
575628 ShowLeaseSetDestination (s, it->second->GetDestination (), 0);
576629 else
577 ShowError(s, "I2CP session not found");
630 ShowError(s, tr("I2CP session not found"));
578631 }
579632 else
580 ShowError(s, "I2CP is not enabled");
633 ShowError(s, tr("I2CP is not enabled"));
581634 }
582635
583636 void ShowLeasesSets(std::stringstream& s)
584637 {
585638 if (i2p::data::netdb.GetNumLeaseSets ())
586639 {
587 s << "<b>LeaseSets:</b><br>\r\n<div class=\"list\">\r\n";
640 s << "<b>" << tr("LeaseSets") << ":</b><br>\r\n<div class=\"list\">\r\n";
588641 int counter = 1;
589642 // for each lease set
590643 i2p::data::netdb.VisitLeaseSets(
603656 s << " expired"; // additional css class for expired
604657 s << "\">\r\n";
605658 if (!ls->IsValid())
606 s << "<div class=\"invalid\">!! Invalid !! </div>\r\n";
659 s << "<div class=\"invalid\">!! " << tr("Invalid") << " !! </div>\r\n";
607660 s << "<div class=\"slide\"><label for=\"slide" << counter << "\">" << dest.ToBase32() << "</label>\r\n";
608661 s << "<input type=\"checkbox\" id=\"slide" << (counter++) << "\" />\r\n<div class=\"slidecontent\">\r\n";
609 s << "<b>Store type:</b> " << (int)storeType << "<br>\r\n";
610 s << "<b>Expires:</b> " << ConvertTime(ls->GetExpirationTime()) << "<br>\r\n";
662 s << "<b>" << tr("Store type") << ":</b> " << (int)storeType << "<br>\r\n";
663 s << "<b>" << tr("Expires") << ":</b> " << ConvertTime(ls->GetExpirationTime()) << "<br>\r\n";
611664 if (storeType == i2p::data::NETDB_STORE_TYPE_LEASESET || storeType == i2p::data::NETDB_STORE_TYPE_STANDARD_LEASESET2)
612665 {
613666 // leases information is available
614667 auto leases = ls->GetNonExpiredLeases();
615 s << "<b>Non Expired Leases: " << leases.size() << "</b><br>\r\n";
668 s << "<b>" << tr("Non Expired Leases") << ": " << leases.size() << "</b><br>\r\n";
616669 for ( auto & l : leases )
617670 {
618 s << "<b>Gateway:</b> " << l->tunnelGateway.ToBase64() << "<br>\r\n";
619 s << "<b>TunnelID:</b> " << l->tunnelID << "<br>\r\n";
620 s << "<b>EndDate:</b> " << ConvertTime(l->endDate) << "<br>\r\n";
671 s << "<b>" << tr("Gateway") << ":</b> " << l->tunnelGateway.ToBase64() << "<br>\r\n";
672 s << "<b>" << tr("TunnelID") << ":</b> " << l->tunnelID << "<br>\r\n";
673 s << "<b>" << tr("EndDate") << ":</b> " << ConvertTime(l->endDate) << "<br>\r\n";
621674 }
622675 }
623676 s << "</div>\r\n</div>\r\n</div>\r\n";
627680 }
628681 else if (!i2p::context.IsFloodfill ())
629682 {
630 s << "<b>LeaseSets:</b> not floodfill.<br>\r\n";
683 s << "<b>" << tr("LeaseSets") << ":</b> " << tr("not floodfill") << ".<br>\r\n";
631684 }
632685 else
633686 {
634 s << "<b>LeaseSets:</b> 0<br>\r\n";
687 s << "<b>" << tr("LeaseSets") << ":</b> 0<br>\r\n";
635688 }
636689 }
637690
638691 void ShowTunnels (std::stringstream& s)
639692 {
640 s << "<b>Tunnels:</b><br>\r\n";
641 s << "<b>Queue size:</b> " << i2p::tunnel::tunnels.GetQueueSize () << "<br>\r\n<br>\r\n";
693 s << "<b>" << tr("Tunnels") << ":</b><br>\r\n";
694 s << "<b>" << tr("Queue size") << ":</b> " << i2p::tunnel::tunnels.GetQueueSize () << "<br>\r\n<br>\r\n";
642695
643696 auto ExplPool = i2p::tunnel::tunnels.GetExploratoryPool ();
644697
645 s << "<b>Inbound tunnels:</b><br>\r\n<div class=\"list\">\r\n";
698 s << "<b>" << tr("Inbound tunnels") << ":</b><br>\r\n<div class=\"list\">\r\n";
646699 for (auto & it : i2p::tunnel::tunnels.GetInboundTunnels ()) {
647700 s << "<div class=\"listitem\">";
648701 it->Print(s);
649702 if(it->LatencyIsKnown())
650 s << " ( " << it->GetMeanLatency() << "ms )";
703 s << " ( " << it->GetMeanLatency() << tr("ms") << " )";
651704 ShowTunnelDetails(s, it->GetState (), (it->GetTunnelPool () == ExplPool), it->GetNumReceivedBytes ());
652705 s << "</div>\r\n";
653706 }
654707 s << "</div>\r\n<br>\r\n";
655 s << "<b>Outbound tunnels:</b><br>\r\n<div class=\"list\">\r\n";
708 s << "<b>" << tr("Outbound tunnels") << ":</b><br>\r\n<div class=\"list\">\r\n";
656709 for (auto & it : i2p::tunnel::tunnels.GetOutboundTunnels ()) {
657710 s << "<div class=\"listitem\">";
658711 it->Print(s);
659712 if(it->LatencyIsKnown())
660 s << " ( " << it->GetMeanLatency() << "ms )";
713 s << " ( " << it->GetMeanLatency() << tr("ms") << " )";
661714 ShowTunnelDetails(s, it->GetState (), (it->GetTunnelPool () == ExplPool), it->GetNumSentBytes ());
662715 s << "</div>\r\n";
663716 }
667720 static void ShowCommands (std::stringstream& s, uint32_t token)
668721 {
669722 std::string webroot; i2p::config::GetOption("http.webroot", webroot);
670 /* commands */
671 s << "<b>Router Commands</b><br>\r\n<br>\r\n<div class=\"commands\">\r\n";
672 s << " <a href=\"" << webroot << "?cmd=" << HTTP_COMMAND_RUN_PEER_TEST << "&token=" << token << "\">Run peer test</a>\r\n";
673 //s << " <a href=\"/?cmd=" << HTTP_COMMAND_RELOAD_CONFIG << "\">Reload config</a><br>\r\n";
723
724 s << "<b>" << tr("Router commands") << "</b><br>\r\n<br>\r\n<div class=\"commands\">\r\n";
725 s << " <a href=\"" << webroot << "?cmd=" << HTTP_COMMAND_RUN_PEER_TEST << "&token=" << token << "\">" << tr("Run peer test") << "</a><br>\r\n";
726
727 // s << " <a href=\"/?cmd=" << HTTP_COMMAND_RELOAD_CONFIG << "\">Reload config</a><br>\r\n";
728
674729 if (i2p::context.AcceptsTunnels ())
675 s << " <a href=\"" << webroot << "?cmd=" << HTTP_COMMAND_DISABLE_TRANSIT << "&token=" << token << "\">Decline transit tunnels</a>\r\n";
730 s << " <a href=\"" << webroot << "?cmd=" << HTTP_COMMAND_DISABLE_TRANSIT << "&token=" << token << "\">" << tr("Decline transit tunnels") << "</a><br>\r\n";
676731 else
677 s << " <a href=\"" << webroot << "?cmd=" << HTTP_COMMAND_ENABLE_TRANSIT << "&token=" << token << "\">Accept transit tunnels</a>\r\n";
732 s << " <a href=\"" << webroot << "?cmd=" << HTTP_COMMAND_ENABLE_TRANSIT << "&token=" << token << "\">" << tr("Accept transit tunnels") << "</a><br>\r\n";
733
678734 #if ((!defined(WIN32) && !defined(QT_GUI_LIB) && !defined(ANDROID)) || defined(ANDROID_BINARY))
679735 if (Daemon.gracefulShutdownInterval)
680 s << " <a href=\"" << webroot << "?cmd=" << HTTP_COMMAND_SHUTDOWN_CANCEL << "&token=" << token << "\">Cancel graceful shutdown</a>\r\n";
736 s << " <a href=\"" << webroot << "?cmd=" << HTTP_COMMAND_SHUTDOWN_CANCEL << "&token=" << token << "\">" << tr("Cancel graceful shutdown") << "</a><br>\r\n";
681737 else
682 s << " <a href=\"" << webroot << "?cmd=" << HTTP_COMMAND_SHUTDOWN_START << "&token=" << token << "\">Start graceful shutdown</a><br>\r\n";
738 s << " <a href=\"" << webroot << "?cmd=" << HTTP_COMMAND_SHUTDOWN_START << "&token=" << token << "\">" << tr("Start graceful shutdown") << "</a><br>\r\n";
683739 #elif defined(WIN32_APP)
684740 if (i2p::util::DaemonWin32::Instance().isGraceful)
685 s << " <a href=\"" << webroot << "?cmd=" << HTTP_COMMAND_SHUTDOWN_CANCEL << "&token=" << token << "\">Cancel graceful shutdown</a>\r\n";
741 s << " <a href=\"" << webroot << "?cmd=" << HTTP_COMMAND_SHUTDOWN_CANCEL << "&token=" << token << "\">" << tr("Cancel graceful shutdown") << "</a><br>\r\n";
686742 else
687 s << " <a href=\"" << webroot << "?cmd=" << HTTP_COMMAND_SHUTDOWN_START << "&token=" << token << "\">Graceful shutdown</a>\r\n";
743 s << " <a href=\"" << webroot << "?cmd=" << HTTP_COMMAND_SHUTDOWN_START << "&token=" << token << "\">" << tr("Start graceful shutdown") << "</a><br>\r\n";
688744 #endif
689 s << " <a href=\"" << webroot << "?cmd=" << HTTP_COMMAND_SHUTDOWN_NOW << "&token=" << token << "\">Force shutdown</a>\r\n";
745
746 s << " <a href=\"" << webroot << "?cmd=" << HTTP_COMMAND_SHUTDOWN_NOW << "&token=" << token << "\">" << tr("Force shutdown") << "</a><br><br>\r\n";
747 s << " <a href=\"" << webroot << "?cmd=" << HTTP_COMMAND_RELOAD_CSS << "&token=" << token << "\">" << tr("Reload external CSS styles") << "</a>\r\n";
690748 s << "</div>";
691749
692 s << "<br>\r\n<small><b>Note:</b> any action done here are not persistent and not changes your config files.</small>\r\n<br>\r\n";
693
694 s << "<b>Logging level</b><br>\r\n";
750 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";
751
752 s << "<b>" << tr("Logging level") << "</b><br>\r\n";
695753 s << " <a class=\"button\" href=\"" << webroot << "?cmd=" << HTTP_COMMAND_LOGLEVEL << "&level=none&token=" << token << "\"> none </a> \r\n";
696754 s << " <a class=\"button\" href=\"" << webroot << "?cmd=" << HTTP_COMMAND_LOGLEVEL << "&level=error&token=" << token << "\"> error </a> \r\n";
697755 s << " <a class=\"button\" href=\"" << webroot << "?cmd=" << HTTP_COMMAND_LOGLEVEL << "&level=warn&token=" << token << "\"> warn </a> \r\n";
699757 s << " <a class=\"button\" href=\"" << webroot << "?cmd=" << HTTP_COMMAND_LOGLEVEL << "&level=debug&token=" << token << "\"> debug </a><br>\r\n<br>\r\n";
700758
701759 uint16_t maxTunnels = GetMaxNumTransitTunnels ();
702 s << "<b>Transit tunnels limit</b><br>\r\n";
760 s << "<b>" << tr("Transit tunnels limit") << "</b><br>\r\n";
703761 s << "<form method=\"get\" action=\"" << webroot << "\">\r\n";
704762 s << " <input type=\"hidden\" name=\"cmd\" value=\"" << HTTP_COMMAND_LIMITTRANSIT << "\">\r\n";
705763 s << " <input type=\"hidden\" name=\"token\" value=\"" << token << "\">\r\n";
706764 s << " <input type=\"number\" min=\"0\" max=\"65535\" name=\"limit\" value=\"" << maxTunnels << "\">\r\n";
707 s << " <button type=\"submit\">Change</button>\r\n";
765 s << " <button type=\"submit\">" << tr("Change") << "</button>\r\n";
708766 s << "</form>\r\n<br>\r\n";
767
768 std::string currLang = i2p::context.GetLanguage ()->GetLanguage(); // get current used language
769 s << "<b>" << tr("Change language") << "</b><br>\r\n";
770 s << "<form method=\"get\" action=\"" << webroot << "\">\r\n";
771 s << " <input type=\"hidden\" name=\"cmd\" value=\"" << HTTP_COMMAND_SETLANGUAGE << "\">\r\n";
772 s << " <input type=\"hidden\" name=\"token\" value=\"" << token << "\">\r\n";
773 s << " <select name=\"lang\" id=\"lang\">\r\n";
774 for (const auto& it: i2p::i18n::languages)
775 s << " <option value=\"" << it.first << "\"" << ((it.first.compare(currLang) == 0) ? " selected" : "") << ">" << it.second.LocaleName << "</option>\r\n";
776 s << " </select>\r\n";
777 s << " <button type=\"submit\">" << tr("Change") << "</button>\r\n";
778 s << "</form>\r\n<br>\r\n";
779
709780 }
710781
711782 void ShowTransitTunnels (std::stringstream& s)
712783 {
713784 if(i2p::tunnel::tunnels.CountTransitTunnels())
714785 {
715 s << "<b>Transit tunnels:</b><br>\r\n<div class=\"list\">\r\n";
786 s << "<b>" << tr("Transit Tunnels") << ":</b><br>\r\n<div class=\"list\">\r\n";
716787 for (const auto& it: i2p::tunnel::tunnels.GetTransitTunnels ())
717788 {
718789 s << "<div class=\"listitem\">\r\n";
728799 }
729800 else
730801 {
731 s << "<b>Transit tunnels:</b> no transit tunnels currently built.<br>\r\n";
802 s << "<b>" << tr("Transit Tunnels") << ":</b> " << tr("no transit tunnels currently built") << ".<br>\r\n";
732803 }
733804 }
734805
777848
778849 void ShowTransports (std::stringstream& s)
779850 {
780 s << "<b>Transports:</b><br>\r\n";
851 s << "<b>" << tr("Transports") << ":</b><br>\r\n";
781852 auto ntcp2Server = i2p::transport::transports.GetNTCP2Server ();
782853 if (ntcp2Server)
783854 {
833904 auto sam = i2p::client::context.GetSAMBridge ();
834905 if (!sam)
835906 {
836 ShowError(s, "SAM disabled");
907 ShowError(s, tr("SAM disabled"));
837908 return;
838909 }
839910
840911 if(sam->GetSessions ().size ())
841912 {
842 s << "<b>SAM Sessions:</b><br>\r\n<div class=\"list\">\r\n";
913 s << "<b>" << tr("SAM sessions") << ":</b><br>\r\n<div class=\"list\">\r\n";
843914 for (auto& it: sam->GetSessions ())
844915 {
845916 auto& name = it.second->GetLocalDestination ()->GetNickname ();
849920 s << "</div>\r\n";
850921 }
851922 else
852 s << "<b>SAM Sessions:</b> no sessions currently running.<br>\r\n";
923 s << "<b>" << tr("SAM sessions") << ":</b> " << tr("no sessions currently running") << ".<br>\r\n";
853924 }
854925
855926 void ShowSAMSession (std::stringstream& s, const std::string& id)
856927 {
857928 auto sam = i2p::client::context.GetSAMBridge ();
858929 if (!sam) {
859 ShowError(s, "SAM disabled");
930 ShowError(s, tr("SAM disabled"));
860931 return;
861932 }
862933
863934 auto session = sam->FindSession (id);
864935 if (!session) {
865 ShowError(s, "SAM session not found");
936 ShowError(s, tr("SAM session not found"));
866937 return;
867938 }
868939
869940 std::string webroot; i2p::config::GetOption("http.webroot", webroot);
870 s << "<b>SAM Session:</b><br>\r\n<div class=\"list\">\r\n";
941 s << "<b>" << tr("SAM Session") << ":</b><br>\r\n<div class=\"list\">\r\n";
871942 auto& ident = session->GetLocalDestination ()->GetIdentHash();
872943 s << "<div class=\"listitem\"><a href=\"" << webroot << "?page=" << HTTP_PAGE_LOCAL_DESTINATION << "&b32=" << ident.ToBase32 () << "\">";
873944 s << i2p::client::context.GetAddressBook ().ToAddress(ident) << "</a></div>\r\n";
874945 s << "<br>\r\n";
875 s << "<b>Streams:</b><br>\r\n<div class=\"list\">\r\n";
946 s << "<b>" << tr("Streams") << ":</b><br>\r\n<div class=\"list\">\r\n";
876947 for (const auto& it: sam->ListSockets(id))
877948 {
878949 s << "<div class=\"listitem\">";
881952 case i2p::client::eSAMSocketTypeSession : s << "session"; break;
882953 case i2p::client::eSAMSocketTypeStream : s << "stream"; break;
883954 case i2p::client::eSAMSocketTypeAcceptor : s << "acceptor"; break;
884 case i2p::client::eSAMSocketTypeForward : s << "forward"; break;
955 case i2p::client::eSAMSocketTypeForward : s << "forward"; break;
885956 default: s << "unknown"; break;
886957 }
887958 s << " [" << it->GetSocket ().remote_endpoint() << "]";
893964 void ShowI2PTunnels (std::stringstream& s)
894965 {
895966 std::string webroot; i2p::config::GetOption("http.webroot", webroot);
896 s << "<b>Client Tunnels:</b><br>\r\n<div class=\"list\">\r\n";
967 s << "<b>" << tr("Client Tunnels") << ":</b><br>\r\n<div class=\"list\">\r\n";
897968 for (auto& it: i2p::client::context.GetClientTunnels ())
898969 {
899970 auto& ident = it.second->GetLocalDestination ()->GetIdentHash();
907978 {
908979 auto& ident = httpProxy->GetLocalDestination ()->GetIdentHash();
909980 s << "<div class=\"listitem\"><a href=\"" << webroot << "?page=" << HTTP_PAGE_LOCAL_DESTINATION << "&b32=" << ident.ToBase32 () << "\">";
910 s << "HTTP Proxy" << "</a> &#8656; ";
981 s << "HTTP " << tr("Proxy") << "</a> &#8656; ";
911982 s << i2p::client::context.GetAddressBook ().ToAddress(ident);
912983 s << "</div>\r\n"<< std::endl;
913984 }
916987 {
917988 auto& ident = socksProxy->GetLocalDestination ()->GetIdentHash();
918989 s << "<div class=\"listitem\"><a href=\"" << webroot << "?page=" << HTTP_PAGE_LOCAL_DESTINATION << "&b32=" << ident.ToBase32 () << "\">";
919 s << "SOCKS Proxy" << "</a> &#8656; ";
990 s << "SOCKS " << tr("Proxy") << "</a> &#8656; ";
920991 s << i2p::client::context.GetAddressBook ().ToAddress(ident);
921992 s << "</div>\r\n"<< std::endl;
922993 }
924995
925996 auto& serverTunnels = i2p::client::context.GetServerTunnels ();
926997 if (!serverTunnels.empty ()) {
927 s << "<br>\r\n<b>Server Tunnels:</b><br>\r\n<div class=\"list\">\r\n";
998 s << "<br>\r\n<b>" << tr("Server Tunnels") << ":</b><br>\r\n<div class=\"list\">\r\n";
928999 for (auto& it: serverTunnels)
9291000 {
9301001 auto& ident = it.second->GetLocalDestination ()->GetIdentHash();
9401011 auto& clientForwards = i2p::client::context.GetClientForwards ();
9411012 if (!clientForwards.empty ())
9421013 {
943 s << "<br>\r\n<b>Client Forwards:</b><br>\r\n<div class=\"list\">\r\n";
1014 s << "<br>\r\n<b>" << tr("Client Forwards") << ":</b><br>\r\n<div class=\"list\">\r\n";
9441015 for (auto& it: clientForwards)
9451016 {
9461017 auto& ident = it.second->GetLocalDestination ()->GetIdentHash();
9541025 auto& serverForwards = i2p::client::context.GetServerForwards ();
9551026 if (!serverForwards.empty ())
9561027 {
957 s << "<br>\r\n<b>Server Forwards:</b><br>\r\n<div class=\"list\">\r\n";
1028 s << "<br>\r\n<b>" << tr("Server Forwards") << ":</b><br>\r\n<div class=\"list\">\r\n";
9581029 for (auto& it: serverForwards)
9591030 {
9601031 auto& ident = it.second->GetLocalDestination ()->GetIdentHash();
9651036 }
9661037 s << "</div>\r\n";
9671038 }
968 }
969
970 std::string ConvertTime (uint64_t time)
971 {
972 lldiv_t divTime = lldiv(time, 1000);
973 time_t t = divTime.quot;
974 struct tm *tm = localtime(&t);
975 char date[128];
976 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);
977 return date;
9781039 }
9791040
9801041 HTTPConnection::HTTPConnection (std::string hostname, std::shared_ptr<boost::asio::ip::tcp::socket> socket):
10861147 return;
10871148 }
10881149 }
1089 // Html5 head start
1150 // HTML head start
10901151 ShowPageHead (s);
10911152 if (req.uri.find("page=") != std::string::npos) {
10921153 HandlePage (req, res, s);
11021163 content = s.str ();
11031164 SendReply (res, content);
11041165 }
1166
1167 std::map<uint32_t, uint32_t> HTTPConnection::m_Tokens;
11051168
11061169 uint32_t HTTPConnection::CreateToken ()
11071170 {
11601223 ShowLeasesSets(s);
11611224 else {
11621225 res.code = 400;
1163 ShowError(s, "Unknown page: " + page);
1226 ShowError(s, tr("Unknown page") + ": " + page);
11641227 return;
11651228 }
11661229 }
11791242
11801243 if (token.empty () || m_Tokens.find (std::stoi (token)) == m_Tokens.end ())
11811244 {
1182 ShowError(s, "Invalid token");
1245 ShowError(s, tr("Invalid token"));
11831246 return;
11841247 }
11851248
12371300 if (dest)
12381301 {
12391302 if(dest->DeleteStream (streamID))
1240 s << "<b>SUCCESS</b>:&nbsp;Stream closed<br>\r\n<br>\r\n";
1303 s << "<b>" << tr("SUCCESS") << "</b>:&nbsp;" << tr("Stream closed") << "<br>\r\n<br>\r\n";
12411304 else
1242 s << "<b>ERROR</b>:&nbsp;Stream not found or already was closed<br>\r\n<br>\r\n";
1305 s << "<b>" << tr("ERROR") << "</b>:&nbsp;" << tr("Stream not found or already was closed") << "<br>\r\n<br>\r\n";
12431306 }
12441307 else
1245 s << "<b>ERROR</b>:&nbsp;Destination not found<br>\r\n<br>\r\n";
1308 s << "<b>" << tr("ERROR") << "</b>:&nbsp;" << tr("Destination not found") << "<br>\r\n<br>\r\n";
12461309 }
12471310 else
1248 s << "<b>ERROR</b>:&nbsp;StreamID can be null<br>\r\n<br>\r\n";
1249
1250 s << "<a href=\"" << webroot << "?page=local_destination&b32=" << b32 << "\">Return to destination page</a><br>\r\n";
1251 s << "<p>You will be redirected back in 5 seconds</b>";
1311 s << "<b>" << tr("ERROR") << "</b>:&nbsp;" << tr("StreamID can't be null") << "<br>\r\n<br>\r\n";
1312
1313 s << "<a href=\"" << webroot << "?page=local_destination&b32=" << b32 << "\">" << tr("Return to destination page") << "</a><br>\r\n";
1314 s << "<p>" << tr("You will be redirected in 5 seconds") << "</b>";
12521315 redirect = "5; url=" + webroot + "?page=local_destination&b32=" + b32;
12531316 res.add_header("Refresh", redirect.c_str());
12541317 return;
12591322 if (limit > 0 && limit <= 65535)
12601323 SetMaxNumTransitTunnels (limit);
12611324 else {
1262 s << "<b>ERROR</b>:&nbsp;Transit tunnels count must not exceed 65535\r\n<br>\r\n<br>\r\n";
1263 s << "<a href=\"" << webroot << "?page=commands\">Back to commands list</a>\r\n<br>\r\n";
1264 s << "<p>You will be redirected back in 5 seconds</b>";
1325 s << "<b>" << tr("ERROR") << "</b>:&nbsp;" << tr("Transit tunnels count must not exceed 65535") << "\r\n<br>\r\n<br>\r\n";
1326 s << "<a href=\"" << webroot << "?page=commands\">" << tr("Back to commands list") << "</a>\r\n<br>\r\n";
1327 s << "<p>" << tr("You will be redirected in 5 seconds") << "</b>";
12651328 res.add_header("Refresh", redirect.c_str());
12661329 return;
12671330 }
12691332 else if (cmd == HTTP_COMMAND_GET_REG_STRING)
12701333 {
12711334 std::string b32 = params["b32"];
1272 std::string name = params["name"];
1335 std::string name = i2p::http::UrlDecode(params["name"]);
12731336
12741337 i2p::data::IdentHash ident;
12751338 ident.FromBase32 (b32);
12941357 auto len = i2p::data::ByteStreamToBase64 (signature, signatureLen, sig, signatureLen*2);
12951358 sig[len] = 0;
12961359 out << "#!sig=" << sig;
1297 s << "<b>SUCCESS</b>:<br>\r\n<form action=\"http://shx5vqsw7usdaunyzr2qmes2fq37oumybpudrd4jjj4e4vk4uusa.b32.i2p/add\" method=\"post\" rel=\"noreferrer\" target=\"_blank\">\r\n"
1360 s << "<b>" << tr("SUCCESS") << "</b>:<br>\r\n<form action=\"http://shx5vqsw7usdaunyzr2qmes2fq37oumybpudrd4jjj4e4vk4uusa.b32.i2p/add\" method=\"post\" rel=\"noreferrer\" target=\"_blank\">\r\n"
12981361 "<textarea readonly name=\"record\" cols=\"80\" rows=\"10\">" << out.str () << "</textarea>\r\n<br>\r\n<br>\r\n"
1299 "<b>Register at reg.i2p:</b>\r\n<br>\r\n"
1300 "<b>Description:</b>\r\n<input type=\"text\" maxlength=\"64\" name=\"desc\" placeholder=\"A bit information about service on domain\">\r\n"
1301 "<input type=\"submit\" value=\"Submit\">\r\n"
1362 "<b>" << tr("Register at reg.i2p") << ":</b>\r\n<br>\r\n"
1363 "<b>" << tr("Description") << ":</b>\r\n<input type=\"text\" maxlength=\"64\" name=\"desc\" placeholder=\"" << tr("A bit information about service on domain") << "\">\r\n"
1364 "<input type=\"submit\" value=\"" << tr("Submit") << "\">\r\n"
13021365 "</form>\r\n<br>\r\n";
13031366 delete[] signature;
13041367 delete[] sig;
13051368 }
13061369 else
1307 s << "<b>ERROR</b>:&nbsp;Domain can't end with .b32.i2p\r\n<br>\r\n<br>\r\n";
1370 s << "<b>" << tr("ERROR") << "</b>:&nbsp;" << tr("Domain can't end with .b32.i2p") << "\r\n<br>\r\n<br>\r\n";
13081371 }
13091372 else
1310 s << "<b>ERROR</b>:&nbsp;Domain must end with .i2p\r\n<br>\r\n<br>\r\n";
1373 s << "<b>" << tr("ERROR") << "</b>:&nbsp;" << tr("Domain must end with .i2p") << "\r\n<br>\r\n<br>\r\n";
13111374 }
13121375 else
1313 s << "<b>ERROR</b>:&nbsp;Such destination is not found\r\n<br>\r\n<br>\r\n";
1314
1315 s << "<a href=\"" << webroot << "?page=local_destination&b32=" << b32 << "\">Return to destination page</a>\r\n";
1376 s << "<b>" << tr("ERROR") << "</b>:&nbsp;" << tr("Such destination is not found") << "\r\n<br>\r\n<br>\r\n";
1377
1378 s << "<a href=\"" << webroot << "?page=local_destination&b32=" << b32 << "\">" << tr("Return to destination page") << "</a>\r\n";
13161379 return;
13171380 }
1381 else if (cmd == HTTP_COMMAND_SETLANGUAGE)
1382 {
1383 std::string lang = params["lang"];
1384 std::string currLang = i2p::context.GetLanguage ()->GetLanguage();
1385
1386 if (currLang.compare(lang) != 0)
1387 i2p::i18n::SetLanguage(lang);
1388 }
1389 else if (cmd == HTTP_COMMAND_RELOAD_CSS)
1390 {
1391 LoadExtCSS();
1392 }
13181393 else
13191394 {
13201395 res.code = 400;
1321 ShowError(s, "Unknown command: " + cmd);
1396 ShowError(s, tr("Unknown command") + ": " + cmd);
13221397 return;
13231398 }
13241399
1325 s << "<b>SUCCESS</b>:&nbsp;Command accepted<br><br>\r\n";
1326 s << "<a href=\"" << webroot << "?page=commands\">Back to commands list</a><br>\r\n";
1327 s << "<p>You will be redirected in 5 seconds</b>";
1400 s << "<b>" << tr("SUCCESS") << "</b>:&nbsp;" << tr("Command accepted") << "<br><br>\r\n";
1401 s << "<a href=\"" << webroot << "?page=commands\">" << tr("Back to commands list") << "</a><br>\r\n";
1402 s << "<p>" << tr("You will be redirected in 5 seconds") << "</b>";
13281403 res.add_header("Refresh", redirect.c_str());
13291404 }
13301405
13771452 m_Thread.reset (new std::thread (std::bind (&HTTPServer::Run, this)));
13781453 m_Acceptor.listen ();
13791454 Accept ();
1455
1456 LoadExtCSS();
13801457 }
13811458
13821459 void HTTPServer::Stop ()
405405
406406 void I2PControlService::UptimeHandler (std::ostringstream& results)
407407 {
408 InsertParam (results, "i2p.router.uptime", (int)i2p::context.GetUptime ()*1000);
408 InsertParam (results, "i2p.router.uptime", std::to_string (i2p::context.GetUptime ()*1000LL));
409409 }
410410
411411 void I2PControlService::VersionHandler (std::ostringstream& results)
1212 Architecture: any
1313 Pre-Depends: ${misc:Pre-Depends}, adduser,
1414 Depends: ${shlibs:Depends}, ${misc:Depends}, lsb-base,
15 Description: I2P Router written in C++
15 Description: Full-featured C++ implementation of I2P client
1616 I2P (Invisible Internet Protocol) is a universal anonymous network layer. All
1717 communications over I2P are anonymous and end-to-end encrypted, participants
1818 don't reveal their real IP addresses.
00 obj-*/i2pd usr/sbin/
1 contrib/i2pd.conf etc/i2pd/
1 contrib/i2pd.conf etc/i2pd/
22 contrib/tunnels.conf etc/i2pd/
33 contrib/subscriptions.txt etc/i2pd/
44 contrib/certificates/ usr/share/i2pd/
1818 #DAEMON_SRC = \
1919 # HTTPServer.cpp I2PControl.cpp UPnP.cpp Daemon.cpp i2pd.cpp
2020
21 LANG_SRC = $(wildcard $(LANG_SRC_DIR)/*.cpp)
22
23 WRAP_LIB_SRC = $(wildcard $(WRAP_SRC_DIR)/*.cpp)
24
2125 DAEMON_SRC = $(wildcard $(DAEMON_SRC_DIR)/*.cpp)
0 /*
1 * Copyright (c) 2021, The PurpleI2P Project
2 *
3 * This file is part of Purple i2pd project and licensed under BSD3
4 *
5 * See full license text in LICENSE file at top of project tree
6 */
7
8 #include <map>
9 #include <vector>
10 #include <string>
11 #include <memory>
12 #include "I18N.h"
13
14 // Afrikaans localization file
15
16 namespace i2p
17 {
18 namespace i18n
19 {
20 namespace afrikaans // language namespace
21 {
22 // language name in lowercase
23 static std::string language = "afrikaans";
24
25 // See for language plural forms here:
26 // https://localization-guide.readthedocs.io/en/latest/l10n/pluralforms.html
27 static int plural (int n) {
28 return n != 1 ? 1 : 0;
29 }
30
31 static std::map<std::string, std::string> strings
32 {
33 {"failed", "Het misluk"},
34 {"unknown", "onbekend"},
35 {"Tunnels", "Tonnels"},
36 {"I2P tunnels", "I2P tonnels"},
37 {"SAM sessions", "SAM sessies"},
38 {"OK", "LEKKER"},
39 {"Testing", "Besig om te toets"},
40 {"Firewalled", "Vuurmuur'd"},
41 {"Unknown", "Onbekend"},
42 {"Error", "Fout"},
43 {"Offline", "Aflyn"},
44 {"Uptime", "Optyd"},
45 {"Network status", "Netwerk status"},
46 {"Network status v6", "Netwerk status v6"},
47 {"Family", "Familie"},
48 {"Received", "Ontvang"},
49 {"Sent", "Gestuur"},
50 {"Hidden content. Press on text to see.", "Hidden content. Druk om te sien."},
51 {"Router Ident", "Router Ident"},
52 {"Router Family", "Router Familie"},
53 {"Enabled", "Geaktiveer"},
54 {"Disabled", "Gedeaktiveer"},
55 {"Change", "Verander"},
56 {"Change language", "Verander taal"},
57 {"Description", "Beskrywing"},
58 {"Submit", "Stuur"},
59 {"Proxy error", "Proxy-fout"},
60 {"Host", "Gasheer"},
61 {"", ""},
62 };
63
64 static std::map<std::string, std::vector<std::string>> plurals
65 {
66 {"days", {"dag", "dae"}},
67 {"hours", {"uur", "ure"}},
68 {"minutes", {"minuut", "minute"}},
69 {"seconds", {"seconde", "sekondes"}},
70 {"", {"", ""}},
71 };
72
73 std::shared_ptr<const i2p::i18n::Locale> GetLocale()
74 {
75 return std::make_shared<i2p::i18n::Locale>(language, strings, plurals, [] (int n)->int { return plural(n); });
76 }
77
78 } // language
79 } // i18n
80 } // i2p
0 /*
1 * Copyright (c) 2021, The PurpleI2P Project
2 *
3 * This file is part of Purple i2pd project and licensed under BSD3
4 *
5 * See full license text in LICENSE file at top of project tree
6 */
7
8 #include <map>
9 #include <vector>
10 #include <string>
11 #include <memory>
12 #include "I18N.h"
13
14 // English localization file
15 // This is an example translation file without strings in it.
16
17 namespace i2p
18 {
19 namespace i18n
20 {
21 namespace english // language namespace
22 {
23 // language name in lowercase
24 static std::string language = "english";
25
26 // See for language plural forms here:
27 // https://localization-guide.readthedocs.io/en/latest/l10n/pluralforms.html
28 static int plural (int n) {
29 return n != 1 ? 1 : 0;
30 }
31
32 static std::map<std::string, std::string> strings
33 {
34 {"", ""},
35 };
36
37 static std::map<std::string, std::vector<std::string>> plurals
38 {
39 {"", {"", ""}},
40 };
41
42 std::shared_ptr<const i2p::i18n::Locale> GetLocale()
43 {
44 return std::make_shared<i2p::i18n::Locale>(language, strings, plurals, [] (int n)->int { return plural(n); });
45 }
46
47 } // language
48 } // i18n
49 } // i2p
0 /*
1 * Copyright (c) 2021, The PurpleI2P Project
2 *
3 * This file is part of Purple i2pd project and licensed under BSD3
4 *
5 * See full license text in LICENSE file at top of project tree
6 */
7
8 #ifndef __I18N_H__
9 #define __I18N_H__
10
11 #include "RouterContext.h"
12
13 namespace i2p
14 {
15 namespace i18n
16 {
17 inline void SetLanguage(const std::string &lang)
18 {
19 const auto it = i2p::i18n::languages.find(lang);
20 if (it == i2p::i18n::languages.end()) // fallback
21 i2p::context.SetLanguage (i2p::i18n::english::GetLocale());
22 else
23 i2p::context.SetLanguage (it->second.LocaleFunc());
24 }
25
26 inline std::string translate (const std::string& arg)
27 {
28 return i2p::context.GetLanguage ()->GetString (arg);
29 }
30
31 inline std::string translate (const std::string& arg, const std::string& arg2, const int& n)
32 {
33 return i2p::context.GetLanguage ()->GetPlural (arg, arg2, n);
34 }
35 } // i18n
36 } // i2p
37
38 template<typename... TArgs>
39 std::string tr (TArgs&&... args)
40 {
41 return i2p::i18n::translate(std::forward<TArgs>(args)...);
42 }
43
44 #endif // __I18N_H__
0 /*
1 * Copyright (c) 2021, The PurpleI2P Project
2 *
3 * This file is part of Purple i2pd project and licensed under BSD3
4 *
5 * See full license text in LICENSE file at top of project tree
6 */
7
8 #ifndef __I18N_LANGS_H__
9 #define __I18N_LANGS_H__
10
11 namespace i2p
12 {
13 namespace i18n
14 {
15 class Locale
16 {
17 public:
18 Locale (
19 const std::string& language,
20 const std::map<std::string, std::string>& strings,
21 const std::map<std::string, std::vector<std::string>>& plurals,
22 std::function<int(int)> formula
23 ): m_Language (language), m_Strings (strings), m_Plurals (plurals), m_Formula (formula) { };
24
25 // Get activated language name for webconsole
26 std::string GetLanguage() const
27 {
28 return m_Language;
29 }
30
31 std::string GetString (const std::string& arg) const
32 {
33 const auto it = m_Strings.find(arg);
34 if (it == m_Strings.end())
35 {
36 return arg;
37 }
38 else
39 {
40 return it->second;
41 }
42 }
43
44 std::string GetPlural (const std::string& arg, const std::string& arg2, const int& n) const
45 {
46 const auto it = m_Plurals.find(arg2);
47 if (it == m_Plurals.end()) // not found, fallback to english
48 {
49 return n == 1 ? arg : arg2;
50 }
51 else
52 {
53 int form = m_Formula(n);
54 return it->second[form];
55 }
56 }
57
58 private:
59 const std::string m_Language;
60 const std::map<std::string, std::string> m_Strings;
61 const std::map<std::string, std::vector<std::string>> m_Plurals;
62 std::function<int(int)> m_Formula;
63 };
64
65 struct langData
66 {
67 std::string LocaleName; // localized name
68 std::string ShortCode; // short language code, like "en"
69 std::function<std::shared_ptr<const i2p::i18n::Locale> (void)> LocaleFunc;
70 };
71
72 // Add localization here with language name as namespace
73 namespace afrikaans { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); }
74 namespace english { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); }
75 namespace russian { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); }
76 namespace turkmen { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); }
77 namespace ukrainian { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); }
78 namespace uzbek { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); }
79
80 /**
81 * That map contains international language name lower-case and name in it's language
82 */
83 static std::map<std::string, langData> languages
84 {
85 { "afrikaans", {"Afrikaans", "af", i2p::i18n::afrikaans::GetLocale} },
86 { "english", {"English", "en", i2p::i18n::english::GetLocale} },
87 { "russian", {"русский язык", "ru", i2p::i18n::russian::GetLocale} },
88 { "turkmen", {"türkmen dili", "tk", i2p::i18n::turkmen::GetLocale} },
89 { "ukrainian", {"украї́нська мо́ва", "uk", i2p::i18n::ukrainian::GetLocale} },
90 { "uzbek", {"Oʻzbek", "uz", i2p::i18n::uzbek::GetLocale} },
91 };
92
93 } // i18n
94 } // i2p
95
96 #endif // __I18N_LANGS_H__
0 /*
1 * Copyright (c) 2021, The PurpleI2P Project
2 *
3 * This file is part of Purple i2pd project and licensed under BSD3
4 *
5 * See full license text in LICENSE file at top of project tree
6 */
7
8 #include <map>
9 #include <vector>
10 #include <string>
11 #include <memory>
12 #include "I18N.h"
13
14 // Russian localization file
15
16 namespace i2p
17 {
18 namespace i18n
19 {
20 namespace russian // language namespace
21 {
22 // language name in lowercase
23 static std::string language = "russian";
24
25 // See for language plural forms here:
26 // https://localization-guide.readthedocs.io/en/latest/l10n/pluralforms.html
27 static int plural (int n) {
28 return n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;
29 }
30
31 static std::map<std::string, std::string> strings
32 {
33 {"KiB", "КиБ"},
34 {"MiB", "МиБ"},
35 {"GiB", "ГиБ"},
36 {"building", "строится"},
37 {"failed", "неудачный"},
38 {"expiring", "истекает"},
39 {"established", "работает"},
40 {"unknown", "неизвестно"},
41 {"exploratory", "исследовательский"},
42 {"<b>i2pd</b> webconsole", "Веб-консоль <b>i2pd</b>"},
43 {"Main page", "Главная"},
44 {"Router commands", "Команды роутера"},
45 {"Local Destinations", "Локальные назначения"},
46 {"LeaseSets", "Лизсеты"},
47 {"Tunnels", "Туннели"},
48 {"Transit Tunnels", "Транзитные туннели"},
49 {"Transports", "Транспорты"},
50 {"I2P tunnels", "I2P туннели"},
51 {"SAM sessions", "SAM сессии"},
52 {"ERROR", "ОШИБКА"},
53 {"OK", "OK"},
54 {"Testing", "Тестирование"},
55 {"Firewalled", "Заблокировано извне"},
56 {"Unknown", "Неизвестно"},
57 {"Proxy", "Прокси"},
58 {"Mesh", "MESH-сеть"},
59 {"Error", "Ошибка"},
60 {"Clock skew", "Не точное время"},
61 {"Offline", "Оффлайн"},
62 {"Symmetric NAT", "Симметричный NAT"},
63 {"Uptime", "В сети"},
64 {"Network status", "Сетевой статус"},
65 {"Network status v6", "Сетевой статус v6"},
66 {"Stopping in", "Остановка через"},
67 {"Family", "Семейство"},
68 {"Tunnel creation success rate", "Успешно построенных туннелей"},
69 {"Received", "Получено"},
70 {"KiB/s", "КиБ/с"},
71 {"Sent", "Отправлено"},
72 {"Transit", "Транзит"},
73 {"Data path", "Путь к данным"},
74 {"Hidden content. Press on text to see.", "Скрытый контент. Нажмите на текст чтобы отобразить."},
75 {"Router Ident", "Идентификатор роутера"},
76 {"Router Family", "Семейство роутера"},
77 {"Router Caps", "Флаги роутера"},
78 {"Version", "Версия"},
79 {"Our external address", "Наш внешний адрес"},
80 {"supported", "поддерживается"},
81 {"Routers", "Роутеры"},
82 {"Floodfills", "Флудфилы"},
83 {"Client Tunnels", "Клиентские туннели"},
84 {"Services", "Сервисы"},
85 {"Enabled", "Включено"},
86 {"Disabled", "Выключено"},
87 {"Encrypted B33 address", "Шифрованные B33 адреса"},
88 {"Address registration line", "Строка регистрации адреса"},
89 {"Domain", "Домен"},
90 {"Generate", "Сгенерировать"},
91 {"<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."},
92 {"Address", "Адрес"},
93 {"Type", "Тип"},
94 {"EncType", "ТипШифр"},
95 {"Inbound tunnels", "Входящие туннели"},
96 {"ms", "мс"},
97 {"Outbound tunnels", "Исходящие туннели"},
98 {"Tags", "Теги"},
99 {"Incoming", "Входящие"},
100 {"Outgoing", "Исходящие"},
101 {"Destination", "Назначение"},
102 {"Amount", "Количество"},
103 {"Incoming Tags", "Входящие теги"},
104 {"Tags sessions", "Сессии тегов"},
105 {"Status", "Статус"},
106 {"Local Destination", "Локальное назначение"},
107 {"Streams", "Стримы"},
108 {"Close stream", "Закрыть стрим"},
109 {"I2CP session not found", "I2CP сессия не найдена"},
110 {"I2CP is not enabled", "I2CP не включен"},
111 {"Invalid", "Некорректный"},
112 {"Store type", "Тип хранилища"},
113 {"Expires", "Истекает"},
114 {"Non Expired Leases", "Не истекшие Lease-ы"},
115 {"Gateway", "Шлюз"},
116 {"TunnelID", "ID туннеля"},
117 {"EndDate", "Заканчивается"},
118 {"not floodfill", "не флудфил"},
119 {"Queue size", "Размер очереди"},
120 {"Run peer test", "Запустить тестирование"},
121 {"Decline transit tunnels", "Отклонять транзитные туннели"},
122 {"Accept transit tunnels", "Принимать транзитные туннели"},
123 {"Cancel graceful shutdown", "Отменить плавную остановку"},
124 {"Start graceful shutdown", "Запустить плавную остановку"},
125 {"Force shutdown", "Принудительная остановка"},
126 {"Reload external CSS styles", "Перезагрузить внешние CSS стили"},
127 {"<b>Note:</b> any action done here are not persistent and not changes your config files.", "<b>Примечание:</b> любое действие произведенное здесь не является постоянным и не изменяет ваши конфигурационные файлы."},
128 {"Logging level", "Уровень логирования"},
129 {"Transit tunnels limit", "Лимит транзитных туннелей"},
130 {"Change", "Изменить"},
131 {"Change language", "Изменение языка"},
132 {"no transit tunnels currently built", "нет построенных транзитных туннелей"},
133 {"SAM disabled", "SAM выключен"},
134 {"no sessions currently running", "нет запущенных сессий"},
135 {"SAM session not found", "SAM сессия не найдена"},
136 {"SAM Session", "SAM сессия"},
137 {"Server Tunnels", "Серверные туннели"},
138 {"Client Forwards", "Клиентские перенаправления"},
139 {"Server Forwards", "Серверные перенаправления"},
140 {"Unknown page", "Неизвестная страница"},
141 {"Invalid token", "Неверный токен"},
142 {"SUCCESS", "УСПЕШНО"},
143 {"Stream closed", "Стрим закрыт"},
144 {"Stream not found or already was closed", "Стрим не найден или уже закрыт"},
145 {"Destination not found", "Точка назначения не найдена"},
146 {"StreamID can't be null", "StreamID не может быть пустым"},
147 {"Return to destination page", "Вернуться на страницу точки назначения"},
148 {"You will be redirected in 5 seconds", "Вы будете переадресованы через 5 секунд"},
149 {"Transit tunnels count must not exceed 65535", "Число транзитных туннелей не должно превышать 65535"},
150 {"Back to commands list", "Вернуться к списку команд"},
151 {"Register at reg.i2p", "Зарегистрировать на reg.i2p"},
152 {"Description", "Описание"},
153 {"A bit information about service on domain", "Немного информации о сервисе на домене"},
154 {"Submit", "Отправить"},
155 {"Domain can't end with .b32.i2p", "Домен не может заканчиваться на .b32.i2p"},
156 {"Domain must end with .i2p", "Домен должен заканчиваться на .i2p"},
157 {"Such destination is not found", "Такая точка назначения не найдена"},
158 {"Unknown command", "Неизвестная команда"},
159 {"Command accepted", "Команда принята"},
160 {"Proxy error", "Ошибка прокси"},
161 {"Proxy info", "Информация прокси"},
162 {"Proxy error: Host not found", "Ошибка прокси: Узел не найден"},
163 {"Remote host not found in router's addressbook", "Запрошенный узел не найден в адресной книге роутера"},
164 {"You may try to find this host on jump services below", "Вы можете попробовать найти узел через джамп сервисы ниже"},
165 {"Invalid request", "Некорректный запрос"},
166 {"Proxy unable to parse your request", "Прокси не может разобрать ваш запрос"},
167 {"addresshelper is not supported", "addresshelper не поддерживается"},
168 {"Host", "Узел"},
169 {"added to router's addressbook from helper", "добавлен в адресную книгу роутера через хелпер"},
170 {"Click here to proceed:", "Нажмите здесь, чтобы продолжить:"},
171 {"Continue", "Продолжить"},
172 {"Addresshelper found", "Найден addresshelper"},
173 {"already in router's addressbook", "уже в адресной книге роутера"},
174 {"Click here to update record:", "Нажмите здесь, чтобы обновить запись:"},
175 {"invalid request uri", "некорректный URI запроса"},
176 {"Can't detect destination host from request", "Не удалось определить адрес назначения из запроса"},
177 {"Outproxy failure", "Ошибка внешнего прокси"},
178 {"bad outproxy settings", "некорректные настройки внешнего прокси"},
179 {"not inside I2P network, but outproxy is not enabled", "не в I2P сети, но внешний прокси не включен"},
180 {"unknown outproxy url", "неизвестный URL внешнего прокси"},
181 {"cannot resolve upstream proxy", "не удается определить вышестоящий прокси"},
182 {"hostname too long", "имя хоста слишком длинное"},
183 {"cannot connect to upstream socks proxy", "не удается подключиться к вышестоящему SOCKS прокси"},
184 {"Cannot negotiate with socks proxy", "Не удается договориться с вышестоящим SOCKS прокси"},
185 {"CONNECT error", "Ошибка CONNECT запроса"},
186 {"Failed to Connect", "Не удалось подключиться"},
187 {"socks proxy error", "ошибка SOCKS прокси"},
188 {"failed to send request to upstream", "не удалось отправить запрос вышестоящему прокси"},
189 {"No Reply From socks proxy", "Нет ответа от SOCKS прокси сервера"},
190 {"cannot connect", "не удалось подключиться"},
191 {"http out proxy not implemented", "поддержка внешнего HTTP прокси сервера не реализована"},
192 {"cannot connect to upstream http proxy", "не удалось подключиться к вышестоящему HTTP прокси серверу"},
193 {"Host is down", "Узел недоступен"},
194 {"Can't create connection to requested host, it may be down. Please try again later.", "Не удалось установить соединение к запрошенному узлу, возможно он не в сети. Попробуйте повторить запрос позже."},
195 {"", ""},
196 };
197
198 static std::map<std::string, std::vector<std::string>> plurals
199 {
200 {"days", {"день", "дня", "дней"}},
201 {"hours", {"час", "часа", "часов"}},
202 {"minutes", {"минуту", "минуты", "минут"}},
203 {"seconds", {"секунду", "секунды", "секунд"}},
204 {"", {"", "", ""}},
205 };
206
207 std::shared_ptr<const i2p::i18n::Locale> GetLocale()
208 {
209 return std::make_shared<i2p::i18n::Locale>(language, strings, plurals, [] (int n)->int { return plural(n); });
210 }
211
212 } // language
213 } // i18n
214 } // i2p
0 /*
1 * Copyright (c) 2021, The PurpleI2P Project
2 *
3 * This file is part of Purple i2pd project and licensed under BSD3
4 *
5 * See full license text in LICENSE file at top of project tree
6 */
7
8 #include <map>
9 #include <vector>
10 #include <string>
11 #include <memory>
12 #include "I18N.h"
13
14 // Turkmen localization file
15
16 namespace i2p
17 {
18 namespace i18n
19 {
20 namespace turkmen // language namespace
21 {
22 // language name in lowercase
23 static std::string language = "turkmen";
24
25 // See for language plural forms here:
26 // https://localization-guide.readthedocs.io/en/latest/l10n/pluralforms.html
27 static int plural (int n) {
28 return n != 1 ? 1 : 0;
29 }
30
31 static std::map<std::string, std::string> strings
32 {
33 {"KiB", "KiB"},
34 {"MiB", "MiB"},
35 {"GiB", "GiB"},
36 {"building", "bina"},
37 {"failed", "şowsuz"},
38 {"expiring", "möhleti gutarýar"},
39 {"established", "işleýär"},
40 {"unknown", "näbelli"},
41 {"exploratory", "gözleg"},
42 {"<b>i2pd</b> webconsole", "Web konsoly <b>i2pd</b>"},
43 {"Main page", "Esasy sahypa"},
44 {"Router commands", "Marşrutizator buýruklary"},
45 {"Local Destinations", "Ýerli ýerler"},
46 {"LeaseSets", "Lizset"},
47 {"Tunnels", "Tuneller"},
48 {"Transit Tunnels", "Tranzit Tunelleri"},
49 {"Transports", "Daşamak"},
50 {"I2P tunnels", "I2P tuneller"},
51 {"SAM sessions", "SAM Sessiýasy"},
52 {"ERROR", "Ýalňyşlyk"},
53 {"OK", "OK"},
54 {"Testing", "Synag etmek"},
55 {"Firewalled", "Daşynda petiklendi"},
56 {"Unknown", "Näbelli"},
57 {"Proxy", "Proksi"},
58 {"Mesh", "MESH-tor"},
59 {"Error", "Ýalňyşlyk"},
60 {"Clock skew", "Takyk wagt däl"},
61 {"Offline", "Awtonom"},
62 {"Symmetric NAT", "Simmetriklik NAT"},
63 {"Uptime", "Onlaýn onlaýn sözlügi"},
64 {"Network status", "Tor ýagdaýy"},
65 {"Network status v6", "Tor ýagdaýy v6"},
66 {"Stopping in", "Soň duruň"},
67 {"Family", "Maşgala"},
68 {"Tunnel creation success rate", "Gurlan teneller üstünlikli gurlan teneller"},
69 {"Received", "Alnan"},
70 {"KiB/s", "KiB/s"},
71 {"Sent", "Ýerleşdirildi"},
72 {"Transit", "Tranzit"},
73 {"Data path", "Maglumat ýoly"},
74 {"Hidden content. Press on text to see.", "Gizlin mazmun. Görkezmek üçin tekste basyň."},
75 {"Router Ident", "Marşrutly kesgitleýji"},
76 {"Router Family", "Marşrutler maşgalasy"},
77 {"Router Caps", "Baýdaklar marşruteri"},
78 {"Version", "Wersiýasy"},
79 {"Our external address", "Daşarky salgymyz"},
80 {"supported", "goldanýar"},
81 {"Routers", "Marşrutizatorlar"},
82 {"Floodfills", "Fludfillar"},
83 {"Client Tunnels", "Müşderi tunelleri"},
84 {"Services", "Hyzmatlar"},
85 {"Enabled", "Goşuldy"},
86 {"Disabled", "Öçürildi"},
87 {"Encrypted B33 address", "Şifrlenen B33 salgylar"},
88 {"Address registration line", "Hasaba alyş salgysy"},
89 {"Domain", "Domen"},
90 {"Generate", "Öndürmek"},
91 {"<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."},
92 {"Address", "Salgysy"},
93 {"Type", "Görnüş"},
94 {"EncType", "Şifrlemek görnüşi"},
95 {"Inbound tunnels", "Gelýän tuneller"},
96 {"ms", "ms"},
97 {"Outbound tunnels", "Çykýan tuneller"},
98 {"Tags", "Bellikler"},
99 {"Incoming", "Gelýän"},
100 {"Outgoing", "Çykýan"},
101 {"Destination", "Maksat"},
102 {"Amount", "Sany"},
103 {"Incoming Tags", "Gelýän bellikler"},
104 {"Tags sessions", "Sapaklar bellikler"},
105 {"Status", "Ýagdaýy"},
106 {"Local Destination", "Ýerli maksat"},
107 {"Streams", "Strimlary"},
108 {"Close stream", "Yap strim"},
109 {"I2CP session not found", "I2CP Sessiýa tapylmady"},
110 {"I2CP is not enabled", "I2CP goşulmaýar"},
111 {"Invalid", "Nädogry"},
112 {"Store type", "Ammar görnüşi"},
113 {"Expires", "Möhleti gutarýar"},
114 {"Non Expired Leases", "Möhleti gutarmady Lizsetlary"},
115 {"Gateway", "Derweze"},
116 {"TunnelID", "Tuneliň ID"},
117 {"EndDate", "Gutarýar"},
118 {"not floodfill", "fludfil däl"},
119 {"Queue size", "Nobatyň ululygy"},
120 {"Run peer test", "Synag başlaň"},
121 {"Decline transit tunnels", "Tranzit tunellerini ret ediň"},
122 {"Accept transit tunnels", "Tranzit tunellerini alyň"},
123 {"Cancel graceful shutdown", "Tekiz durmagy ýatyryň"},
124 {"Start graceful shutdown", "Tekiz durmak"},
125 {"Force shutdown", "Mejbury duralga"},
126 {"Reload external CSS styles", "Daşarky CSS stillerini täzeden ýükläň"},
127 {"<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."},
128 {"Logging level", "Giriş derejesi"},
129 {"Transit tunnels limit", "Tranzit tunelleriniň çägi"},
130 {"Change", "Üýtgetmek"},
131 {"Change language", "Dil üýtgetmek"},
132 {"no transit tunnels currently built", "gurlan tranzit tunelleri ýok"},
133 {"SAM disabled", "SAM öçürilen"},
134 {"no sessions currently running", "başlamagyň sessiýalary ýok"},
135 {"SAM session not found", "SAM Sessiýa tapylmady"},
136 {"SAM Session", "SAM Sessiýa"},
137 {"Server Tunnels", "Serwer tunelleri"},
138 {"Client Forwards", "Müşderi gönükdirýär"},
139 {"Server Forwards", "Serweriň täzeden düzlüleri"},
140 {"Unknown page", "Näbelli sahypa"},
141 {"Invalid token", "Nädogry token"},
142 {"SUCCESS", "Üstünlikli"},
143 {"Stream closed", "Strim ýapyk"},
144 {"Stream not found or already was closed", "Strim tapylmady ýa-da eýýäm ýapyldy"},
145 {"Destination not found", "Niýetlenen ýeri tapylmady"},
146 {"StreamID can't be null", "StreamID boş bolup bilmez"},
147 {"Return to destination page", "Barmaly nokadynyň nokadyna gaýdyp geliň"},
148 {"You will be redirected in 5 seconds", "5 sekuntdan soň täzeden ugrukdyrylarsyňyz"},
149 {"Transit tunnels count must not exceed 65535", "Tranzit tagtalaryň sany 65535-den geçmeli däldir"},
150 {"Back to commands list", "Topar sanawyna dolan"},
151 {"Register at reg.i2p", "Reg.i2P-de hasaba duruň"},
152 {"Description", "Beýany"},
153 {"A bit information about service on domain", "Domendäki hyzmat barada käbir maglumatlar"},
154 {"Submit", "Iber"},
155 {"Domain can't end with .b32.i2p", "Domain .b32.i2p bilen gutaryp bilmez"},
156 {"Domain must end with .i2p", "Domeni .i2p bilen gutarmaly"},
157 {"Such destination is not found", "Bu barmaly ýer tapylmady"},
158 {"Unknown command", "Näbelli topar"},
159 {"Command accepted", "Topar kabul edilýär"},
160 {"Proxy error", "Proksi ýalňyşlygy"},
161 {"Proxy info", "Proksi maglumat"},
162 {"Proxy error: Host not found", "Proksi ýalňyşlygy: Host tapylmady"},
163 {"Remote host not found in router's addressbook", "Uzakdaky öý eýesi marşruteriň salgy kitabynda tapylmady"},
164 {"You may try to find this host on jump services below", "Aşakdaky böküş hyzmatlarynda bu öý eýesini tapmaga synanyşyp bilersiňiz"},
165 {"Invalid request", "Nädogry haýyş"},
166 {"Proxy unable to parse your request", "Proksi haýyşyňyzy derňäp bilmeýär"},
167 {"addresshelper is not supported", "Salgylandyryjy goldanok"},
168 {"Host", "Adres"},
169 {"added to router's addressbook from helper", "marşruteriň adresini kömekçiden goşdy"},
170 {"Click here to proceed:", "Dowam etmek bu ýerde basyň:"},
171 {"Continue", "Dowam et"},
172 {"Addresshelper found", "Forgelper tapyldy"},
173 {"already in router's addressbook", "marşruteriň adres kitaby"},
174 {"Click here to update record:", "Recordazgyny täzelemek üçin bu ýerde basyň:"},
175 {"invalid request uri", "nädogry haýyş URI"},
176 {"Can't detect destination host from request", "Haýyşdan barmaly ýerini tapyp bilemok"},
177 {"Outproxy failure", "Daşarky proksi ýalňyşlyk"},
178 {"bad outproxy settings", "daşarky daşarky proksi sazlamalary nädogry"},
179 {"not inside I2P network, but outproxy is not enabled", "I2P torunda däl, ýöne daşarky proksi goşulmaýar"},
180 {"unknown outproxy url", "näbelli daşarky proksi URL"},
181 {"cannot resolve upstream proxy", "has ýokary proksi kesgitläp bilmeýär"},
182 {"hostname too long", "hoster eýesi ady gaty uzyn"},
183 {"cannot connect to upstream socks proxy", "ýokary jorap SOCKS proksi bilen birigip bolmaýar"},
184 {"Cannot negotiate with socks proxy", "Iň ýokary jorap SOCKS proksi bilen ylalaşyp bilmeýärler"},
185 {"CONNECT error", "Bagyr haýyşy säwligi"},
186 {"Failed to Connect", "Birikdirip bilmedi"},
187 {"socks proxy error", "socks proksi ýalňyşlygy"},
188 {"failed to send request to upstream", "öý eýesi proksi üçin haýyş iberip bilmedi"},
189 {"No Reply From socks proxy", "Jorap proksi serwerinden hiç hili jogap ýok"},
190 {"cannot connect", "birikdirip bilmedi"},
191 {"http out proxy not implemented", "daşarky HTTP proksi serwerini goldamak amala aşyrylmaýar"},
192 {"cannot connect to upstream http proxy", "ýokary akym HTTP proksi serwerine birigip bilmedi"},
193 {"Host is down", "Salgy elýeterli däl"},
194 {"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ň."},
195 {"", ""},
196 };
197
198 static std::map<std::string, std::vector<std::string>> plurals
199 {
200 // ShowUptime
201 {"days", {"gün", "gün"}},
202 {"hours", {"sagat", "sagat"}},
203 {"minutes", {"minut", "minut"}},
204 {"seconds", {"sekunt", "sekunt"}},
205 {"", {"", ""}},
206 };
207
208 std::shared_ptr<const i2p::i18n::Locale> GetLocale()
209 {
210 return std::make_shared<i2p::i18n::Locale>(language, strings, plurals, [] (int n)->int { return plural(n); });
211 }
212
213 } // language
214 } // i18n
215 } // i2p
0 /*
1 * Copyright (c) 2021, The PurpleI2P Project
2 *
3 * This file is part of Purple i2pd project and licensed under BSD3
4 *
5 * See full license text in LICENSE file at top of project tree
6 */
7
8 #include <map>
9 #include <vector>
10 #include <string>
11 #include <memory>
12 #include "I18N.h"
13
14 // Ukrainian localization file
15
16 namespace i2p
17 {
18 namespace i18n
19 {
20 namespace ukrainian // language namespace
21 {
22 // language name in lowercase
23 static std::string language = "ukrainian";
24
25 // See for language plural forms here:
26 // https://localization-guide.readthedocs.io/en/latest/l10n/pluralforms.html
27 static int plural (int n) {
28 return n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;
29 }
30
31 static std::map<std::string, std::string> strings
32 {
33 {"KiB", "КіБ"},
34 {"MiB", "МіБ"},
35 {"GiB", "ГіБ"},
36 {"building", "будується"},
37 {"failed", "невдалий"},
38 {"expiring", "завершується"},
39 {"established", "працює"},
40 {"unknown", "невідомо"},
41 {"exploratory", "дослідницький"},
42 {"<b>i2pd</b> webconsole", "Веб-консоль <b>i2pd</b>"},
43 {"Main page", "Головна"},
44 {"Router commands", "Команди маршрутизатора"},
45 {"Local Destinations", "Локальні Призначення"},
46 {"LeaseSets", "Лізсети"},
47 {"Tunnels", "Тунелі"},
48 {"Transit Tunnels", "Транзитні Тунелі"},
49 {"Transports", "Транспорти"},
50 {"I2P tunnels", "I2P тунелі"},
51 {"SAM sessions", "SAM сесії"},
52 {"ERROR", "ПОМИЛКА"},
53 {"OK", "OK"},
54 {"Testing", "Тестування"},
55 {"Firewalled", "Заблоковано ззовні"},
56 {"Unknown", "Невідомо"},
57 {"Proxy", "Проксі"},
58 {"Mesh", "MESH-мережа"},
59 {"Error", "Помилка"},
60 {"Clock skew", "Неточний час"},
61 {"Offline", "Офлайн"},
62 {"Symmetric NAT", "Симетричний NAT"},
63 {"Uptime", "У мережі"},
64 {"Network status", "Мережевий статус"},
65 {"Network status v6", "Мережевий статус v6"},
66 {"Stopping in", "Зупинка через"},
67 {"Family", "Сімейство"},
68 {"Tunnel creation success rate", "Успішно побудованих тунелів"},
69 {"Received", "Отримано"},
70 {"KiB/s", "КіБ/с"},
71 {"Sent", "Відправлено"},
72 {"Transit", "Транзит"},
73 {"Data path", "Шлях до даних"},
74 {"Hidden content. Press on text to see.", "Прихований вміст. Щоб відобразити, натисніть на текст."},
75 {"Router Ident", "Ідентифікатор маршрутизатора"},
76 {"Router Family", "Сімейство маршрутизатора"},
77 {"Router Caps", "Прапорці маршрутизатора"},
78 {"Version", "Версія"},
79 {"Our external address", "Наша зовнішня адреса"},
80 {"supported", "підтримується"},
81 {"Routers", "Маршрутизатори"},
82 {"Floodfills", "Флудфіли"},
83 {"Client Tunnels", "Клієнтські Тунелі"},
84 {"Services", "Сервіси"},
85 {"Enabled", "Увімкнуто"},
86 {"Disabled", "Вимкнуто"},
87 {"Encrypted B33 address", "Шифровані B33 адреси"},
88 {"Address registration line", "Рядок реєстрації адреси"},
89 {"Domain", "Домен"},
90 {"Generate", "Згенерувати"},
91 {"<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."},
92 {"Address", "Адреса"},
93 {"Type", "Тип"},
94 {"EncType", "ТипШифр"},
95 {"Inbound tunnels", "Вхідні тунелі"},
96 {"ms", "мс"},
97 {"Outbound tunnels", "Вихідні тунелі"},
98 {"Tags", "Теги"},
99 {"Incoming", "Вхідні"},
100 {"Outgoing", "Вихідні"},
101 {"Destination", "Призначення"},
102 {"Amount", "Кількість"},
103 {"Incoming Tags", "Вхідні Теги"},
104 {"Tags sessions", "Сесії Тегів"},
105 {"Status", "Статус"},
106 {"Local Destination", "Локальні Призначення"},
107 {"Streams", "Потоки"},
108 {"Close stream", "Закрити потік"},
109 {"I2CP session not found", "I2CP сесія не знайдена"},
110 {"I2CP is not enabled", "I2CP не увікнуто"},
111 {"Invalid", "Некоректний"},
112 {"Store type", "Тип сховища"},
113 {"Expires", "Завершується"},
114 {"Non Expired Leases", "Не завершені Lease-и"},
115 {"Gateway", "Шлюз"},
116 {"TunnelID", "ID тунеля"},
117 {"EndDate", "Закінчується"},
118 {"not floodfill", "не флудфіл"},
119 {"Queue size", "Розмір черги"},
120 {"Run peer test", "Запустити тестування"},
121 {"Decline transit tunnels", "Відхиляти транзитні тунелі"},
122 {"Accept transit tunnels", "Ухвалювати транзитні тунелі"},
123 {"Cancel graceful shutdown", "Скасувати плавну зупинку"},
124 {"Start graceful shutdown", "Запустити плавну зупинку"},
125 {"Force shutdown", "Примусова зупинка"},
126 {"Reload external CSS styles", "Перезавантажити зовнішні стилі CSS"},
127 {"<b>Note:</b> any action done here are not persistent and not changes your config files.", "<b>Примітка:</b> будь-яка зроблена тут дія не є постійною та не змінює ваші конфігураційні файли."},
128 {"Logging level", "Рівень логування"},
129 {"Transit tunnels limit", "Обмеження транзитних тунелів"},
130 {"Change", "Змінити"},
131 {"Change language", "Змінити мову"},
132 {"no transit tunnels currently built", "немає побудованих транзитних тунелів"},
133 {"SAM disabled", "SAM вимкнуто"},
134 {"no sessions currently running", "немає запущених сесій"},
135 {"SAM session not found", "SAM сесія не знайдена"},
136 {"SAM Session", "SAM сесія"},
137 {"Server Tunnels", "Серверні Тунелі"},
138 {"Client Forwards", "Клієнтські Переспрямування"},
139 {"Server Forwards", "Серверні Переспрямування"},
140 {"Unknown page", "Невідома сторінка"},
141 {"Invalid token", "Невірний токен"},
142 {"SUCCESS", "УСПІШНО"},
143 {"Stream closed", "Потік зачинений"},
144 {"Stream not found or already was closed", "Потік не знайдений або вже зачинений"},
145 {"Destination not found", "Точка призначення не знайдена"},
146 {"StreamID can't be null", "Ідентифікатор потоку не може бути порожнім"},
147 {"Return to destination page", "Повернутися на сторінку точки призначення"},
148 {"You will be redirected in 5 seconds", "Ви будете переадресовані через 5 секунд"},
149 {"Transit tunnels count must not exceed 65535", "Кількість транзитних тунелів не повинна перевищувати 65535"},
150 {"Back to commands list", "Повернутися до списку команд"},
151 {"Register at reg.i2p", "Зареєструвати на reg.i2p"},
152 {"Description", "Опис"},
153 {"A bit information about service on domain", "Трохи інформації про сервіс на домені"},
154 {"Submit", "Надіслати"},
155 {"Domain can't end with .b32.i2p", "Домен не може закінчуватися на .b32.i2p"},
156 {"Domain must end with .i2p", "Домен повинен закінчуватися на .i2p"},
157 {"Such destination is not found", "Така точка призначення не знайдена"},
158 {"Unknown command", "Невідома команда"},
159 {"Command accepted", "Команда прийнята"},
160 {"Proxy error", "Помилка проксі"},
161 {"Proxy info", "Інформація проксі"},
162 {"Proxy error: Host not found", "Помилка проксі: Адреса не знайдена"},
163 {"Remote host not found in router's addressbook", "Віддалена адреса не знайдена в адресній книзі маршрутизатора"},
164 {"You may try to find this host on jump services below", "Ви можете спробувати знайти дану адресу на джамп сервісах нижче"},
165 {"Invalid request", "Некоректний запит"},
166 {"Proxy unable to parse your request", "Проксі не може розібрати ваш запит"},
167 {"addresshelper is not supported", "addresshelper не підтримується"},
168 {"Host", "Адреса"},
169 {"added to router's addressbook from helper", "доданий в адресну книгу маршрутизатора через хелпер"},
170 {"Click here to proceed:", "Натисніть тут щоб продовжити:"},
171 {"Continue", "Продовжити"},
172 {"Addresshelper found", "Знайдено addresshelper"},
173 {"already in router's addressbook", "вже в адресній книзі маршрутизатора"},
174 {"Click here to update record:", "Натисніть тут щоб оновити запис:"},
175 {"invalid request uri", "некоректний URI запиту"},
176 {"Can't detect destination host from request", "Не вдалось визначити адресу призначення з запиту"},
177 {"Outproxy failure", "Помилка зовнішнього проксі"},
178 {"bad outproxy settings", "некоректні налаштування зовнішнього проксі"},
179 {"not inside I2P network, but outproxy is not enabled", "не в I2P мережі, але зовнішній проксі не включений"},
180 {"unknown outproxy url", "невідомий URL зовнішнього проксі"},
181 {"cannot resolve upstream proxy", "не вдається визначити висхідний проксі"},
182 {"hostname too long", "ім'я вузла надто довге"},
183 {"cannot connect to upstream socks proxy", "не вдається підключитися до висхідного SOCKS проксі"},
184 {"Cannot negotiate with socks proxy", "Не вдається домовитися з висхідним SOCKS проксі"},
185 {"CONNECT error", "Помилка CONNECT запиту"},
186 {"Failed to Connect", "Не вдалося підключитися"},
187 {"socks proxy error", "помилка SOCKS проксі"},
188 {"failed to send request to upstream", "не вдалося відправити запит висхідному проксі"},
189 {"No Reply From socks proxy", "Немає відповіді від SOCKS проксі сервера"},
190 {"cannot connect", "не вдалося підключитися"},
191 {"http out proxy not implemented", "підтримка зовнішнього HTTP проксі сервера не реалізована"},
192 {"cannot connect to upstream http proxy", "не вдалося підключитися до висхідного HTTP проксі сервера"},
193 {"Host is down", "Вузол недоступний"},
194 {"Can't create connection to requested host, it may be down. Please try again later.", "Не вдалося встановити з'єднання до запитаного вузла, можливо він не в мережі. Спробуйте повторити запит пізніше."},
195 {"", ""},
196 };
197
198 static std::map<std::string, std::vector<std::string>> plurals
199 {
200 {"days", {"день", "дня", "днів"}},
201 {"hours", {"годину", "години", "годин"}},
202 {"minutes", {"хвилину", "хвилини", "хвилин"}},
203 {"seconds", {"секунду", "секунди", "секунд"}},
204 {"", {"", "", ""}},
205 };
206
207 std::shared_ptr<const i2p::i18n::Locale> GetLocale()
208 {
209 return std::make_shared<i2p::i18n::Locale>(language, strings, plurals, [] (int n)->int { return plural(n); });
210 }
211
212 } // language
213 } // i18n
214 } // i2p
0 /*
1 * Copyright (c) 2021, The PurpleI2P Project
2 *
3 * This file is part of Purple i2pd project and licensed under BSD3
4 *
5 * See full license text in LICENSE file at top of project tree
6 */
7
8 #include <map>
9 #include <vector>
10 #include <string>
11 #include <memory>
12 #include "I18N.h"
13
14 // Ukrainian localization file
15
16 namespace i2p
17 {
18 namespace i18n
19 {
20 namespace uzbek // language namespace
21 {
22 // language name in lowercase
23 static std::string language = "uzbek";
24
25 // See for language plural forms here:
26 // https://localization-guide.readthedocs.io/en/latest/l10n/pluralforms.html
27 static int plural (int n) {
28 return n > 1 ? 1 : 0;
29 }
30
31 static std::map<std::string, std::string> strings
32 {
33 {"KiB", "KiB"},
34 {"MiB", "MiB"},
35 {"GiB", "GiB"},
36 {"building", "qurilish"},
37 {"failed", "muvaffaqiyatsiz"},
38 {"expiring", "muddati tugaydi"},
39 {"established", "aloqa o'rnatildi"},
40 {"unknown", "noma'lum"},
41 {"exploratory", "tadqiqiy"},
42 {"<b>i2pd</b> webconsole", "<b>i2pd</b> veb -konsoli"},
43 {"Main page", "Asosiy sahifa"},
44 {"Router commands", "Router buyruqlari"},
45 {"LeaseSets", "LeaseSets"},
46 {"Tunnels", "Tunnellar"},
47 {"Transit Tunnels", "Tranzit Tunellar"},
48 {"Transports", "Transportlar"},
49 {"I2P tunnels", "I2P tunnellar"},
50 {"SAM sessions", "SAM sessiyalari"},
51 {"ERROR", "XATO"},
52 {"OK", "OK"},
53 {"Testing", "Testlash"},
54 {"Firewalled", "Xavfsizlik devori bilan himoyalangan"},
55 {"Unknown", "Notanish"},
56 {"Proxy", "Proksi"},
57 {"Mesh", "Mesh To'r"},
58 {"Error", "Xato"},
59 {"Clock skew", "Aniq vaqt emas"},
60 {"Offline", "Oflayn"},
61 {"Symmetric NAT", "Simmetrik NAT"},
62 {"Uptime", "Ish vaqti"},
63 {"Network status", "Tarmoq holati"},
64 {"Network status v6", "Tarmoq holati v6"},
65 {"Stopping in", "Ichida to'xtatish"},
66 {"Family", "Oila"},
67 {"Tunnel creation success rate", "Tunnel yaratish muvaffaqiyat darajasi"},
68 {"Received", "Qabul qilindi"},
69 {"KiB/s", "KiB/s"},
70 {"Sent", "Yuborilgan"},
71 {"Transit", "Tranzit"},
72 {"Data path", "Ma'lumotlar yo'li"},
73 {"Hidden content. Press on text to see.", "Yashirin tarkib. Ko'rish uchun matn ustida bosing."},
74 {"Router Ident", "Router identifikatori"},
75 {"Router Family", "Router Oila"},
76 {"Router Caps", "Router bayroqlari"},
77 {"Version", "Versiya"},
78 {"Our external address", "Bizning tashqi manzilimiz"},
79 {"supported", "qo'llab -quvvatlanadi"},
80 {"Routers", "Routerlar"},
81 {"Floodfills", "Floodfills"},
82 {"Client Tunnels", "Mijoz tunellari"},
83 {"Services", "Xizmatlar"},
84 {"Enabled", "Yoqilgan"},
85 {"Disabled", "O'chirilgan"},
86 {"Encrypted B33 address", "Shifrlangan B33 manzil"},
87 {"Address registration line", "Manzilni ro'yxatga olish liniyasi"},
88 {"Domain", "Domen"},
89 {"Generate", "Varatish"},
90 {"<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."},
91 {"Address", "Manzil"},
92 {"Type", "Turi"},
93 {"EncType", "ShifrlashTuri"},
94 {"Inbound tunnels", "Kirish tunnellari"},
95 {"ms", "ms"},
96 {"Outbound tunnels", "Chiquvchi tunnellar"},
97 {"Tags", "Teglar"},
98 {"Incoming", "Kiruvchi"},
99 {"Outgoing", "Chiquvchi"},
100 {"Destination", "Manzilgoh"},
101 {"Amount", "Yig'indi"},
102 {"Incoming Tags", "Kiruvchi teglar"},
103 {"Tags sessions", "Teglar sessiyalari"},
104 {"Status", "Holat"},
105 {"Streams", "Strim"},
106 {"Close stream", "Strimni o'chirish"},
107 {"I2CP session not found", "I2CP sessiyasi topilmadi"},
108 {"I2CP is not enabled", "I2CP yoqilmagan"},
109 {"Invalid", "Noto'g'ri"},
110 {"Store type", "Saqlash turi"},
111 {"Expires", "Muddati tugaydi"},
112 {"Non Expired Leases", "Muddati O'tmagan Leases"},
113 {"Gateway", "Kirish yo'li"},
114 {"TunnelID", "TunnelID"},
115 {"EndDate", "Tugash Sanasi"},
116 {"not floodfill", "floodfill emas"},
117 {"Queue size", "Navbat hajmi"},
118 {"Run peer test", "Sinovni boshlang"},
119 {"Decline transit tunnels", "Tranzit tunnellarni rad etish"},
120 {"Accept transit tunnels", "Tranzit tunnellarni qabul qilish"},
121 {"Cancel graceful shutdown", "Yumshoq to'xtashni bekor qiling"},
122 {"Start graceful shutdown", "Yumshoq to'xtashni boshlang"},
123 {"Force shutdown", "Bizning tashqi manzilimiz"},
124 {"Reload external CSS styles", "Tashqi CSS uslublarini qayta yuklang"},
125 {"<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."},
126 {"Transit tunnels limit", "Tranzit tunellar chegarasi"},
127 {"Change", "O'zgartirish"},
128 {"Change language", "Tilni o'zgartirish"},
129 {"no transit tunnels currently built", "qurilgan tranzit tunnellari yo'q"},
130 {"SAM disabled", "SAM o'chirilgan"},
131 {"no sessions currently running", "hech qanday ishlaydigan sessiyalar yo'q"},
132 {"SAM session not found", "SAM sessiyasi topilmadi"},
133 {"SAM Session", "SAM sessiyasi"},
134 {"Server Tunnels", "Server Tunellari"},
135 {"Client Forwards", "Mijozlarni Yo'naltirish"},
136 {"Server Forwards", "Serverni Yo'naltirish"},
137 {"Unknown page", "Noma'lum sahifa"},
138 {"Invalid token", "Noto‘g‘ri belgi"},
139 {"SUCCESS", "Muvaffaqiyat"},
140 {"Stream closed", "Strim yopiq"},
141 {"Stream not found or already was closed", "Strim topilmadi yoki allaqachon yopilgan"},
142 {"Destination not found", "Yo'nalish topilmadi"},
143 {"StreamID can't be null", "StreamID bo'sh bo'lishi mumkin emas"},
144 {"Return to destination page", "Belgilangan sahifaga qaytish"},
145 {"You will be redirected in 5 seconds", "Siz 5 soniyada qayta yo'naltirilasiz"},
146 {"Transit tunnels count must not exceed 65535", "Tranzit tunnellar soni 65535 dan oshmasligi kerak"},
147 {"Back to commands list", "Buyruqlar ro'yxatiga qaytish"},
148 {"Register at reg.i2p", "Reg.i2p-da ro'yxatdan o'ting"},
149 {"Description", "Tavsif"},
150 {"A bit information about service on domain", "Domen xizmatlari haqida bir oz ma'lumot"},
151 {"Submit", "Yuborish"},
152 {"Domain can't end with .b32.i2p", "Domen .b32.i2p bilan tugashi mumkin emas"},
153 {"Domain must end with .i2p", "Domen .i2p bilan tugashi kerak"},
154 {"Such destination is not found", "Bunday yo'nalish topilmadi"},
155 {"Unknown command", "Noma'lum buyruq"},
156 {"Command accepted", "Buyruq qabul qilindi"},
157 {"Proxy error", "Proksi xatosi"},
158 {"Proxy info", "Proksi ma'lumotlari"},
159 {"Proxy error: Host not found", "Proksi xatosi: Xost topilmadi"},
160 {"Remote host not found in router's addressbook", "Masofaviy xost yo'riqnoma manzillar kitobida topilmadi"},
161 {"Invalid request", "Noto‘g‘ri so‘rov"},
162 {"Proxy unable to parse your request", "Proksi sizning so'rovingizni tahlil qila olmaydi"},
163 {"addresshelper is not supported", "addresshelper qo'llab -quvvatlanmaydi"},
164 {"Host", "Xost"},
165 {"Addresshelper found", "Addresshelper topildi"},
166 {"invalid request uri", "noto'g'ri URI so'rovi"},
167 {"Can't detect destination host from request", "So‘rov orqali manzil xostini aniqlab bo'lmayapti"},
168 {"Outproxy failure", "Tashqi proksi muvaffaqiyatsizligi"},
169 {"bad outproxy settings", "noto'g'ri tashqi proksi -server sozlamalari"},
170 {"not inside I2P network, but outproxy is not enabled", "I2P tarmog'ida emas, lekin tashqi proksi yoqilmagan"},
171 {"unknown outproxy url", "noma'lum outproxy url"},
172 {"cannot resolve upstream proxy", "yuqoridagi proksi -serverni aniqlab olib bolmaydi"},
173 {"hostname too long", "xost nomi juda uzun"},
174 {"cannot connect to upstream socks proxy", "yuqori soks proksi -serveriga ulanib bo'lmaydi"},
175 {"Cannot negotiate with socks proxy", "Soks proksi bilan muzokara olib bo'lmaydi"},
176 {"CONNECT error", "CONNECT xatosi"},
177 {"Failed to Connect", "Ulanmadi"},
178 {"socks proxy error", "soks proksi xatosi"},
179 {"failed to send request to upstream", "yuqori http proksi-serveriga ulanib bo'lmadi"},
180 {"No Reply From socks proxy", "Soks-proksidan javob yo'q"},
181 {"cannot connect", "ulab bo'lmaydi"},
182 {"http out proxy not implemented", "tashqi HTTP proksi -serverni qo'llab -quvvatlash amalga oshirilmagan"},
183 {"cannot connect to upstream http proxy", "yuqori http proksi-serveriga ulanib bo'lmadi"},
184 {"Host is down", "Xost ishlamayapti"},
185 {"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."},
186 {"", ""},
187 };
188
189 static std::map<std::string, std::vector<std::string>> plurals
190 {
191 {"days", {"kun", "kunlar"}},
192 {"hours", {"soat", "soat"}},
193 {"minutes", {"daqiqa", "daqiqalar"}},
194 {"seconds", {"soniya", "soniyalar"}},
195 {"", {"", ""}},
196 };
197
198 std::shared_ptr<const i2p::i18n::Locale> GetLocale()
199 {
200 return std::make_shared<i2p::i18n::Locale>(language, strings, plurals, [] (int n)->int { return plural(n); });
201 }
202
203 } // language
204 } // i18n
205 } // i2p
3636 ("conf", value<std::string>()->default_value(""), "Path to main i2pd config file (default: try ~/.i2pd/i2pd.conf or /var/lib/i2pd/i2pd.conf)")
3737 ("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)")
3838 ("tunnelsdir", value<std::string>()->default_value(""), "Path to extra tunnels' configs folder (default: ~/.i2pd/tunnels.d or /var/lib/i2pd/tunnels.d")
39 ("certsdir", value<std::string>()->default_value(""), "Path to certificates used for verifying .su3, families (default: ~/.i2pd/certificates or /var/lib/i2pd/certificates")
3940 ("pidfile", value<std::string>()->default_value(""), "Path to pidfile (default: ~/i2pd/i2pd.pid or /var/lib/i2pd/i2pd.pid)")
4041 ("log", value<std::string>()->default_value(""), "Logs destination: stdout, file, syslog (stdout if not set)")
4142 ("logfile", value<std::string>()->default_value(""), "Path to logfile (stdout if not set, autodetect if daemon)")
6162 ("floodfill", bool_switch()->default_value(false), "Router will be floodfill (default: disabled)")
6263 ("bandwidth", value<std::string>()->default_value(""), "Bandwidth limit: integer in KBps or letters: L (32), O (256), P (2048), X (>9000)")
6364 ("share", value<int>()->default_value(100), "Limit of transit traffic from max bandwidth in percents. (default: 100)")
64 ("ntcp", bool_switch()->default_value(false), "Deprecated option. Always false")
65 ("ntcp", bool_switch()->default_value(false), "Ignored. Always false")
6566 ("ssu", bool_switch()->default_value(true), "Enable SSU transport (default: enabled)")
66 ("ntcpproxy", value<std::string>()->default_value(""), "Deprecated option")
67 ("ntcpproxy", value<std::string>()->default_value(""), "Ignored")
6768 #ifdef _WIN32
68 ("svcctl", value<std::string>()->default_value(""), "Deprecated option")
69 ("svcctl", value<std::string>()->default_value(""), "Ignored")
6970 ("insomnia", bool_switch()->default_value(false), "Prevent system from sleeping (default: disabled)")
7071 ("close", value<std::string>()->default_value("ask"), "Action on close: minimize, exit, ask")
7172 #endif
7677 ("limits.coresize", value<uint32_t>()->default_value(0), "Maximum size of corefile in Kb (0 - use system limit)")
7778 ("limits.openfiles", value<uint16_t>()->default_value(0), "Maximum number of open files (0 - use system default)")
7879 ("limits.transittunnels", value<uint16_t>()->default_value(2500), "Maximum active transit sessions (default:2500)")
79 ("limits.ntcpsoft", value<uint16_t>()->default_value(0), "Deprecated option")
80 ("limits.ntcphard", value<uint16_t>()->default_value(0), "Deprecated option")
81 ("limits.ntcpthreads", value<uint16_t>()->default_value(1), "Deprecated option")
80 ("limits.ntcpsoft", value<uint16_t>()->default_value(0), "Threshold to start probabilistic backoff with ntcp sessions (default: use system limit)")
81 ("limits.ntcphard", value<uint16_t>()->default_value(0), "Maximum number of ntcp sessions (default: use system limit)")
82 ("limits.ntcpthreads", value<uint16_t>()->default_value(1), "Maximum number of threads used by NTCP DH worker (default: 1)")
8283 ;
8384
8485 options_description httpserver("HTTP Server options");
9293 ("http.strictheaders", value<bool>()->default_value(true), "Enable strict host checking on WebUI")
9394 ("http.hostname", value<std::string>()->default_value("localhost"), "Expected hostname for WebUI")
9495 ("http.webroot", value<std::string>()->default_value("/"), "WebUI root path (default: / )")
96 ("http.lang", value<std::string>()->default_value("english"), "WebUI language (default: english )")
9597 ;
9698
9799 options_description httpproxy("HTTP Proxy options");
112114 ("httpproxy.addresshelper", value<bool>()->default_value(true), "Enable or disable addresshelper")
113115 ("httpproxy.i2cp.leaseSetType", value<std::string>()->default_value("3"), "Local destination's LeaseSet type")
114116 ("httpproxy.i2cp.leaseSetEncType", value<std::string>()->default_value("0,4"), "Local destination's LeaseSet encryption type")
117 ("httpproxy.i2cp.leaseSetPrivKey", value<std::string>()->default_value(""), "LeaseSet private key")
115118 ;
116119
117120 options_description socksproxy("SOCKS Proxy options");
133136 ("socksproxy.outproxyport", value<uint16_t>()->default_value(9050), "Upstream outproxy port for SOCKS Proxy")
134137 ("socksproxy.i2cp.leaseSetType", value<std::string>()->default_value("3"), "Local destination's LeaseSet type")
135138 ("socksproxy.i2cp.leaseSetEncType", value<std::string>()->default_value("0,4"), "Local destination's LeaseSet encryption type")
139 ("socksproxy.i2cp.leaseSetPrivKey", value<std::string>()->default_value(""), "LeaseSet private key")
136140 ;
137141
138142 options_description sam("SAM bridge options");
208212 "https://i2p.novg.net/"
209213 ), "Reseed URLs, separated by comma")
210214 ("reseed.yggurls", value<std::string>()->default_value(
211 "http://[324:9de3:fea4:f6ac::ace]:7070/"
215 "http://[324:71e:281a:9ed3::ace]:7070/,"
216 "http://[301:65b9:c7cd:9a36::1]:18801/,"
217 "http://[320:8936:ec1a:31f1::216]/"
212218 ), "Reseed URLs through the Yggdrasil, separated by comma")
213219 ;
214220
217223 ("addressbook.defaulturl", value<std::string>()->default_value(
218224 "http://shx5vqsw7usdaunyzr2qmes2fq37oumybpudrd4jjj4e4vk4uusa.b32.i2p/hosts.txt"
219225 ), "AddressBook subscription URL for initial setup")
220 ("addressbook.subscriptions", value<std::string>()->default_value(""), "AddressBook subscriptions URLs, separated by comma")
226 ("addressbook.subscriptions", value<std::string>()->default_value("http://reg.i2p/hosts.txt"), "AddressBook subscriptions URLs, separated by comma")
221227 ("addressbook.hostsfile", value<std::string>()->default_value(""), "File to dump addresses in hosts.txt format");
222228
223229 options_description trust("Trust options");
8181 if (it != params->end ()) m_Nickname = it->second;
8282 // otherwise we set default nickname in Start when we know local address
8383 }
84 it = params->find (I2CP_PARAM_DONT_PUBLISH_LEASESET);
85 if (it != params->end ())
86 {
87 // oveeride isPublic
88 bool dontpublish = false;
89 i2p::config::GetOption (it->second, dontpublish);
90 m_IsPublic = !dontpublish;
91 }
8492 it = params->find (I2CP_PARAM_LEASESET_TYPE);
8593 if (it != params->end ())
8694 m_LeaseSetType = std::stoi(it->second);
336344 void LeaseSetDestination::HandleI2NPMessage (const uint8_t * buf, size_t len)
337345 {
338346 I2NPMessageType typeID = (I2NPMessageType)(buf[I2NP_HEADER_TYPEID_OFFSET]);
339 LeaseSetDestination::HandleCloveI2NPMessage (typeID, buf + I2NP_HEADER_SIZE, GetI2NPMessageLength(buf, len) - I2NP_HEADER_SIZE);
340 }
341
342 bool LeaseSetDestination::HandleCloveI2NPMessage (I2NPMessageType typeID, const uint8_t * payload, size_t len)
347 uint32_t msgID = bufbe32toh (buf + I2NP_HEADER_MSGID_OFFSET);
348 LeaseSetDestination::HandleCloveI2NPMessage (typeID, buf + I2NP_HEADER_SIZE, GetI2NPMessageLength(buf, len) - I2NP_HEADER_SIZE, msgID);
349 }
350
351 bool LeaseSetDestination::HandleCloveI2NPMessage (I2NPMessageType typeID, const uint8_t * payload, size_t len, uint32_t msgID)
343352 {
344353 switch (typeID)
345354 {
356365 case eI2NPDatabaseSearchReply:
357366 HandleDatabaseSearchReplyMessage (payload, len);
358367 break;
368 case eI2NPShortTunnelBuildReply: // might come as garlic encrypted
369 i2p::HandleI2NPMessage (CreateI2NPMessage (typeID, payload, len, msgID));
370 break;
359371 default:
360372 LogPrint (eLogWarning, "Destination: Unexpected I2NP message type ", typeID);
361373 return false;
508520 // schedule verification
509521 m_PublishVerificationTimer.expires_from_now (boost::posix_time::seconds(PUBLISH_VERIFICATION_TIMEOUT));
510522 m_PublishVerificationTimer.async_wait (std::bind (&LeaseSetDestination::HandlePublishVerificationTimer,
511 shared_from_this (), std::placeholders::_1));
523 shared_from_this (), std::placeholders::_1));
512524 }
513525 else
514526 i2p::garlic::GarlicDestination::HandleDeliveryStatusMessage (msgID);
591603 // assume it successive and try to verify
592604 m_PublishVerificationTimer.expires_from_now (boost::posix_time::seconds(PUBLISH_VERIFICATION_TIMEOUT));
593605 m_PublishVerificationTimer.async_wait (std::bind (&LeaseSetDestination::HandlePublishVerificationTimer,
594 shared_from_this (), std::placeholders::_1));
606 shared_from_this (), std::placeholders::_1));
607
595608 }
596609 }
597610 }
6060 const char I2CP_PARAM_RATCHET_OUTBOUND_TAGS[] = "crypto.ratchet.outboundTags"; // not used yet
6161 const char I2CP_PARAM_INBOUND_NICKNAME[] = "inbound.nickname";
6262 const char I2CP_PARAM_OUTBOUND_NICKNAME[] = "outbound.nickname";
63 const char I2CP_PARAM_DONT_PUBLISH_LEASESET[] = "i2cp.dontPublishLeaseSet";
6364 const char I2CP_PARAM_LEASESET_TYPE[] = "i2cp.leaseSetType";
64 const int DEFAULT_LEASESET_TYPE = 1;
65 const int DEFAULT_LEASESET_TYPE = 3;
6566 const char I2CP_PARAM_LEASESET_ENCRYPTION_TYPE[] = "i2cp.leaseSetEncType";
6667 const char I2CP_PARAM_LEASESET_PRIV_KEY[] = "i2cp.leaseSetPrivKey"; // PSK decryption key, base64
6768 const char I2CP_PARAM_LEASESET_AUTH_TYPE[] = "i2cp.leaseSetAuthType";
6869 const char I2CP_PARAM_LEASESET_CLIENT_DH[] = "i2cp.leaseSetClient.dh"; // group of i2cp.leaseSetClient.dh.nnn
69 const char I2CP_PARAM_LEASESET_CLIENT_PSK[] = "i2cp.leaseSetClient.psk"; // group of i2cp.leaseSetClient.psk.nnn
70
70 const char I2CP_PARAM_LEASESET_CLIENT_PSK[] = "i2cp.leaseSetClient.psk"; // group of i2cp.leaseSetClient.psk.nnn
71
7172 // latency
7273 const char I2CP_PARAM_MIN_TUNNEL_LATENCY[] = "latency.min";
7374 const int DEFAULT_MIN_TUNNEL_LATENCY = 0;
142143
143144 // implements GarlicDestination
144145 void HandleI2NPMessage (const uint8_t * buf, size_t len);
145 bool HandleCloveI2NPMessage (I2NPMessageType typeID, const uint8_t * payload, size_t len);
146 bool HandleCloveI2NPMessage (I2NPMessageType typeID, const uint8_t * payload, size_t len, uint32_t msgID);
146147
147148 void SetLeaseSet (std::shared_ptr<const i2p::data::LocalLeaseSet> newLeaseSet);
148149 int GetLeaseSetType () const { return m_LeaseSetType; };
00 /*
1 * Copyright (c) 2013-2020, The PurpleI2P Project
1 * Copyright (c) 2013-2021, The PurpleI2P Project
22 *
33 * This file is part of Purple i2pd project and licensed under BSD3
44 *
3030 uint8_t keydata[64];
3131 i2p::crypto::HKDF (rootKey, k, 32, "KDFDHRatchetStep", keydata); // keydata = HKDF(rootKey, k, "KDFDHRatchetStep", 64)
3232 memcpy (m_NextRootKey, keydata, 32); // nextRootKey = keydata[0:31]
33 i2p::crypto::HKDF (keydata + 32, nullptr, 0, "TagAndKeyGenKeys", m_KeyData.buf);
33 i2p::crypto::HKDF (keydata + 32, nullptr, 0, "TagAndKeyGenKeys", m_SessionTagKeyData);
3434 // [sessTag_ck, symmKey_ck] = HKDF(keydata[32:63], ZEROLEN, "TagAndKeyGenKeys", 64)
35 memcpy (m_SymmKeyCK, m_KeyData.buf + 32, 32);
35 memcpy (m_SymmKeyCK, (const uint8_t *)m_SessionTagKeyData + 32, 32);
3636 m_NextSymmKeyIndex = 0;
3737 }
3838
3939 void RatchetTagSet::NextSessionTagRatchet ()
4040 {
41 i2p::crypto::HKDF (m_KeyData.GetSessTagCK (), nullptr, 0, "STInitialization", m_KeyData.buf); // [sessTag_ck, sesstag_constant] = HKDF(sessTag_ck, ZEROLEN, "STInitialization", 64)
42 memcpy (m_SessTagConstant, m_KeyData.GetSessTagConstant (), 32);
41 i2p::crypto::HKDF (m_SessionTagKeyData, nullptr, 0, "STInitialization", m_SessionTagKeyData); // [sessTag_ck, sesstag_constant] = HKDF(sessTag_ck, ZEROLEN, "STInitialization", 64)
42 memcpy (m_SessTagConstant, (const uint8_t *)m_SessionTagKeyData + 32, 32); // SESSTAG_CONSTANT = keydata[32:63]
4343 m_NextIndex = 0;
4444 }
4545
5151 LogPrint (eLogError, "Garlic: Tagset ", GetTagSetID (), " is empty");
5252 return 0;
5353 }
54 i2p::crypto::HKDF (m_KeyData.GetSessTagCK (), m_SessTagConstant, 32, "SessionTagKeyGen", m_KeyData.buf); // [sessTag_ck, tag] = HKDF(sessTag_chainkey, SESSTAG_CONSTANT, "SessionTagKeyGen", 64)
55 return m_KeyData.GetTag ();
54 i2p::crypto::HKDF (m_SessionTagKeyData, m_SessTagConstant, 32, "SessionTagKeyGen", m_SessionTagKeyData); // [sessTag_ck, tag] = HKDF(sessTag_chainkey, SESSTAG_CONSTANT, "SessionTagKeyGen", 64)
55 return m_SessionTagKeyData.GetLL ()[4]; // tag = keydata[32:39]
5656 }
5757
5858 void RatchetTagSet::GetSymmKey (int index, uint8_t * key)
116116 return session->HandleNextMessage (buf, len, shared_from_this (), index);
117117 }
118118
119 DatabaseLookupTagSet::DatabaseLookupTagSet (GarlicDestination * destination, const uint8_t * key):
119 SymmetricKeyTagSet::SymmetricKeyTagSet (GarlicDestination * destination, const uint8_t * key):
120120 ReceiveRatchetTagSet (nullptr), m_Destination (destination)
121121 {
122122 memcpy (m_Key, key, 32);
123123 Expire ();
124124 }
125125
126 bool DatabaseLookupTagSet::HandleNextMessage (uint8_t * buf, size_t len, int index)
126 bool SymmetricKeyTagSet::HandleNextMessage (uint8_t * buf, size_t len, int index)
127127 {
128128 if (len < 24) return false;
129129 uint8_t nonce[12];
132132 len -= 16; // poly1305
133133 if (!i2p::crypto::AEADChaCha20Poly1305 (buf + offset, len - offset, buf, 8, m_Key, nonce, buf + offset, len - offset, false)) // decrypt
134134 {
135 LogPrint (eLogWarning, "Garlic: Lookup reply AEAD decryption failed");
135 LogPrint (eLogWarning, "Garlic: Symmetric key tagset AEAD decryption failed");
136136 return false;
137137 }
138138 // we assume 1 I2NP block with delivery type local
139139 if (offset + 3 > len)
140140 {
141 LogPrint (eLogWarning, "Garlic: Lookup reply is too short ", len);
141 LogPrint (eLogWarning, "Garlic: Symmetric key tagset is too short ", len);
142142 return false;
143143 }
144144 if (buf[offset] != eECIESx25519BlkGalicClove)
145145 {
146 LogPrint (eLogWarning, "Garlic: Lookup reply unexpected block ", (int)buf[offset]);
146 LogPrint (eLogWarning, "Garlic: Symmetric key tagset unexpected block ", (int)buf[offset]);
147147 return false;
148148 }
149149 offset++;
151151 offset += 2;
152152 if (offset + size > len)
153153 {
154 LogPrint (eLogWarning, "Garlic: Lookup reply block is too long ", size);
154 LogPrint (eLogWarning, "Garlic: Symmetric key tagset block is too long ", size);
155155 return false;
156156 }
157157 if (m_Destination)
517517 }
518518 return true;
519519 }
520
521 bool ECIESX25519AEADRatchetSession::NewOutgoingMessageForRouter (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen)
522 {
523 // we are Alice, router's bpk is m_RemoteStaticKey
524 i2p::crypto::InitNoiseNState (GetNoiseState (), m_RemoteStaticKey);
525 size_t offset = 0;
526 m_EphemeralKeys = i2p::transport::transports.GetNextX25519KeysPair ();
527 memcpy (out + offset, m_EphemeralKeys->GetPublicKey (), 32);
528 MixHash (out + offset, 32); // h = SHA256(h || aepk)
529 offset += 32;
530 uint8_t sharedSecret[32];
531 if (!m_EphemeralKeys->Agree (m_RemoteStaticKey, sharedSecret)) // x25519(aesk, bpk)
532 {
533 LogPrint (eLogWarning, "Garlic: Incorrect Bob static key");
534 return false;
535 }
536 MixKey (sharedSecret);
537 uint8_t nonce[12];
538 CreateNonce (0, nonce);
539 // encrypt payload
540 if (!i2p::crypto::AEADChaCha20Poly1305 (payload, len, m_H, 32, m_CK + 32, nonce, out + offset, len + 16, true)) // encrypt
541 {
542 LogPrint (eLogWarning, "Garlic: Payload for router AEAD encryption failed");
543 return false;
544 }
545
546 m_State = eSessionStateNewSessionSent;
547 return true;
548 }
549
520
550521 bool ECIESX25519AEADRatchetSession::NewSessionReplyMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen)
551522 {
552523 // we are Bob
823794
824795 std::shared_ptr<I2NPMessage> ECIESX25519AEADRatchetSession::WrapSingleMessage (std::shared_ptr<const I2NPMessage> msg)
825796 {
826 auto payload = CreatePayload (msg, m_State != eSessionStateEstablished);
827 size_t len = payload.size ();
797 uint8_t * payload = GetOwner ()->GetPayloadBuffer ();
798 if (!payload) return nullptr;
799 size_t len = CreatePayload (msg, m_State != eSessionStateEstablished, payload);
828800 if (!len) return nullptr;
829801 auto m = NewI2NPMessage (len + 100); // 96 + 4
830802 m->Align (12); // in order to get buf aligned to 16 (12 + 4)
833805 switch (m_State)
834806 {
835807 case eSessionStateEstablished:
836 if (!NewExistingSessionMessage (payload.data (), payload.size (), buf, m->maxLen))
808 if (!NewExistingSessionMessage (payload, len, buf, m->maxLen))
837809 return nullptr;
838810 len += 24;
839811 break;
840812 case eSessionStateNew:
841 if (!NewOutgoingSessionMessage (payload.data (), payload.size (), buf, m->maxLen))
813 if (!NewOutgoingSessionMessage (payload, len, buf, m->maxLen))
842814 return nullptr;
843815 len += 96;
844816 break;
845817 case eSessionStateNewSessionReceived:
846 if (!NewSessionReplyMessage (payload.data (), payload.size (), buf, m->maxLen))
818 if (!NewSessionReplyMessage (payload, len, buf, m->maxLen))
847819 return nullptr;
848820 len += 72;
849821 break;
850822 case eSessionStateNewSessionReplySent:
851 if (!NextNewSessionReplyMessage (payload.data (), payload.size (), buf, m->maxLen))
823 if (!NextNewSessionReplyMessage (payload, len, buf, m->maxLen))
852824 return nullptr;
853825 len += 72;
854826 break;
855827 case eSessionStateOneTime:
856 if (!NewOutgoingSessionMessage (payload.data (), payload.size (), buf, m->maxLen, false))
828 if (!NewOutgoingSessionMessage (payload, len, buf, m->maxLen, false))
857829 return nullptr;
858830 len += 96;
859 break;
860 case eSessionStateForRouter:
861 if (!NewOutgoingMessageForRouter (payload.data (), payload.size (), buf, m->maxLen))
862 return nullptr;
863 len += 48;
864831 break;
865832 default:
866833 return nullptr;
872839 return m;
873840 }
874841
875 std::shared_ptr<I2NPMessage> ECIESX25519AEADRatchetSession::WrapOneTimeMessage (std::shared_ptr<const I2NPMessage> msg, bool isForRouter)
876 {
877 m_State = isForRouter ? eSessionStateForRouter : eSessionStateOneTime;
842 std::shared_ptr<I2NPMessage> ECIESX25519AEADRatchetSession::WrapOneTimeMessage (std::shared_ptr<const I2NPMessage> msg)
843 {
844 m_State = eSessionStateOneTime;
878845 return WrapSingleMessage (msg);
879846 }
880847
881 std::vector<uint8_t> ECIESX25519AEADRatchetSession::CreatePayload (std::shared_ptr<const I2NPMessage> msg, bool first)
848 size_t ECIESX25519AEADRatchetSession::CreatePayload (std::shared_ptr<const I2NPMessage> msg, bool first, uint8_t * payload)
882849 {
883850 uint64_t ts = i2p::util::GetMillisecondsSinceEpoch ();
884851 size_t payloadLen = 0;
940907 payloadLen += paddingSize + 3;
941908 }
942909 }
943 std::vector<uint8_t> v(payloadLen);
944910 if (payloadLen)
945 {
911 {
912 if (payloadLen > I2NP_MAX_MESSAGE_SIZE)
913 {
914 LogPrint (eLogError, "Garlic: payload length ", payloadLen, " is too long");
915 return 0;
916 }
946917 m_LastSentTimestamp = ts;
947918 size_t offset = 0;
948919 // DateTime
949920 if (first)
950921 {
951 v[offset] = eECIESx25519BlkDateTime; offset++;
952 htobe16buf (v.data () + offset, 4); offset += 2;
953 htobe32buf (v.data () + offset, ts/1000); offset += 4; // in seconds
922 payload[offset] = eECIESx25519BlkDateTime; offset++;
923 htobe16buf (payload + offset, 4); offset += 2;
924 htobe32buf (payload + offset, ts/1000); offset += 4; // in seconds
954925 }
955926 // LeaseSet
956927 if (leaseSet)
957928 {
958 offset += CreateLeaseSetClove (leaseSet, ts, v.data () + offset, payloadLen - offset);
929 offset += CreateLeaseSetClove (leaseSet, ts, payload + offset, payloadLen - offset);
959930 if (!first)
960931 {
961932 // ack request
962 v[offset] = eECIESx25519BlkAckRequest; offset++;
963 htobe16buf (v.data () + offset, 1); offset += 2;
964 v[offset] = 0; offset++; // flags
933 payload[offset] = eECIESx25519BlkAckRequest; offset++;
934 htobe16buf (payload + offset, 1); offset += 2;
935 payload[offset] = 0; offset++; // flags
965936 }
966937 }
967938 // msg
968939 if (msg)
969 offset += CreateGarlicClove (msg, v.data () + offset, payloadLen - offset);
940 offset += CreateGarlicClove (msg, payload + offset, payloadLen - offset);
970941 // ack
971942 if (m_AckRequests.size () > 0)
972943 {
973 v[offset] = eECIESx25519BlkAck; offset++;
974 htobe16buf (v.data () + offset, m_AckRequests.size () * 4); offset += 2;
944 payload[offset] = eECIESx25519BlkAck; offset++;
945 htobe16buf (payload + offset, m_AckRequests.size () * 4); offset += 2;
975946 for (auto& it: m_AckRequests)
976947 {
977 htobe16buf (v.data () + offset, it.first); offset += 2;
978 htobe16buf (v.data () + offset, it.second); offset += 2;
948 htobe16buf (payload + offset, it.first); offset += 2;
949 htobe16buf (payload + offset, it.second); offset += 2;
979950 }
980951 m_AckRequests.clear ();
981952 }
982953 // next keys
983954 if (m_SendReverseKey)
984955 {
985 v[offset] = eECIESx25519BlkNextKey; offset++;
986 htobe16buf (v.data () + offset, m_NextReceiveRatchet->newKey ? 35 : 3); offset += 2;
987 v[offset] = ECIESX25519_NEXT_KEY_REVERSE_KEY_FLAG;
956 payload[offset] = eECIESx25519BlkNextKey; offset++;
957 htobe16buf (payload + offset, m_NextReceiveRatchet->newKey ? 35 : 3); offset += 2;
958 payload[offset] = ECIESX25519_NEXT_KEY_REVERSE_KEY_FLAG;
988959 int keyID = m_NextReceiveRatchet->keyID - 1;
989960 if (m_NextReceiveRatchet->newKey)
990961 {
991 v[offset] |= ECIESX25519_NEXT_KEY_KEY_PRESENT_FLAG;
962 payload[offset] |= ECIESX25519_NEXT_KEY_KEY_PRESENT_FLAG;
992963 keyID++;
993964 }
994965 offset++; // flag
995 htobe16buf (v.data () + offset, keyID); offset += 2; // keyid
966 htobe16buf (payload + offset, keyID); offset += 2; // keyid
996967 if (m_NextReceiveRatchet->newKey)
997968 {
998 memcpy (v.data () + offset, m_NextReceiveRatchet->key->GetPublicKey (), 32);
969 memcpy (payload + offset, m_NextReceiveRatchet->key->GetPublicKey (), 32);
999970 offset += 32; // public key
1000971 }
1001972 m_SendReverseKey = false;
1002973 }
1003974 if (m_SendForwardKey)
1004975 {
1005 v[offset] = eECIESx25519BlkNextKey; offset++;
1006 htobe16buf (v.data () + offset, m_NextSendRatchet->newKey ? 35 : 3); offset += 2;
1007 v[offset] = m_NextSendRatchet->newKey ? ECIESX25519_NEXT_KEY_KEY_PRESENT_FLAG : ECIESX25519_NEXT_KEY_REQUEST_REVERSE_KEY_FLAG;
1008 if (!m_NextSendRatchet->keyID) v[offset] |= ECIESX25519_NEXT_KEY_REQUEST_REVERSE_KEY_FLAG; // for first key only
976 payload[offset] = eECIESx25519BlkNextKey; offset++;
977 htobe16buf (payload + offset, m_NextSendRatchet->newKey ? 35 : 3); offset += 2;
978 payload[offset] = m_NextSendRatchet->newKey ? ECIESX25519_NEXT_KEY_KEY_PRESENT_FLAG : ECIESX25519_NEXT_KEY_REQUEST_REVERSE_KEY_FLAG;
979 if (!m_NextSendRatchet->keyID) payload[offset] |= ECIESX25519_NEXT_KEY_REQUEST_REVERSE_KEY_FLAG; // for first key only
1009980 offset++; // flag
1010 htobe16buf (v.data () + offset, m_NextSendRatchet->keyID); offset += 2; // keyid
981 htobe16buf (payload + offset, m_NextSendRatchet->keyID); offset += 2; // keyid
1011982 if (m_NextSendRatchet->newKey)
1012983 {
1013 memcpy (v.data () + offset, m_NextSendRatchet->key->GetPublicKey (), 32);
984 memcpy (payload + offset, m_NextSendRatchet->key->GetPublicKey (), 32);
1014985 offset += 32; // public key
1015986 }
1016987 }
1017988 // padding
1018989 if (paddingSize)
1019990 {
1020 v[offset] = eECIESx25519BlkPadding; offset++;
1021 htobe16buf (v.data () + offset, paddingSize); offset += 2;
1022 memset (v.data () + offset, 0, paddingSize); offset += paddingSize;
1023 }
1024 }
1025 return v;
991 payload[offset] = eECIESx25519BlkPadding; offset++;
992 htobe16buf (payload + offset, paddingSize); offset += 2;
993 memset (payload + offset, 0, paddingSize); offset += paddingSize;
994 }
995 }
996 return payloadLen;
1026997 }
1027998
1028999 size_t ECIESX25519AEADRatchetSession::CreateGarlicClove (std::shared_ptr<const I2NPMessage> msg, uint8_t * buf, size_t len)
11081079 bool RouterIncomingRatchetSession::HandleNextMessage (const uint8_t * buf, size_t len)
11091080 {
11101081 if (!GetOwner ()) return false;
1111 i2p::crypto::NoiseSymmetricState state (GetNoiseState ());
1082 m_CurrentNoiseState = GetNoiseState ();
11121083 // we are Bob
1113 state.MixHash (buf, 32);
1084 m_CurrentNoiseState.MixHash (buf, 32);
11141085 uint8_t sharedSecret[32];
11151086 if (!GetOwner ()->Decrypt (buf, sharedSecret, nullptr, i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD)) // x25519(bsk, aepk)
11161087 {
11171088 LogPrint (eLogWarning, "Garlic: Incorrect N ephemeral public key");
11181089 return false;
11191090 }
1120 state.MixKey (sharedSecret);
1091 m_CurrentNoiseState.MixKey (sharedSecret);
11211092 buf += 32; len -= 32;
11221093 uint8_t nonce[12];
11231094 CreateNonce (0, nonce);
11241095 std::vector<uint8_t> payload (len - 16);
1125 if (!i2p::crypto::AEADChaCha20Poly1305 (buf, len - 16, state.m_H, 32, state.m_CK + 32, nonce, payload.data (), len - 16, false)) // decrypt
1096 if (!i2p::crypto::AEADChaCha20Poly1305 (buf, len - 16, m_CurrentNoiseState.m_H, 32,
1097 m_CurrentNoiseState.m_CK + 32, nonce, payload.data (), len - 16, false)) // decrypt
11261098 {
11271099 LogPrint (eLogWarning, "Garlic: Payload for router AEAD verification failed");
11281100 return false;
11301102 HandlePayload (payload.data (), len - 16, nullptr, 0);
11311103 return true;
11321104 }
1105
1106 static size_t CreateGarlicPayload (std::shared_ptr<const I2NPMessage> msg, uint8_t * payload,
1107 bool datetime, size_t optimalSize)
1108 {
1109 size_t len = 0;
1110 if (datetime)
1111 {
1112 // DateTime
1113 payload[0] = eECIESx25519BlkDateTime;
1114 htobe16buf (payload + 1, 4);
1115 htobe32buf (payload + 3, i2p::util::GetSecondsSinceEpoch ());
1116 len = 7;
1117 }
1118 // I2NP
1119 payload += len;
1120 uint16_t cloveSize = msg->GetPayloadLength () + 10;
1121 payload[0] = eECIESx25519BlkGalicClove; // clove type
1122 htobe16buf (payload + 1, cloveSize); // size
1123 payload += 3;
1124 payload[0] = 0; // flag and delivery instructions
1125 payload[1] = msg->GetTypeID (); // I2NP msg type
1126 htobe32buf (payload + 2, msg->GetMsgID ()); // msgID
1127 htobe32buf (payload + 6, msg->GetExpiration () / 1000); // expiration in seconds
1128 memcpy (payload + 10, msg->GetPayload (), msg->GetPayloadLength ());
1129 len += cloveSize + 3;
1130 payload += cloveSize;
1131 // padding
1132 int delta = (int)optimalSize - (int)len;
1133 if (delta < 0 || delta > 3) // don't create padding if we are close to optimal size
1134 {
1135 uint8_t paddingSize = rand () & 0x0F; // 0 - 15
1136 if (delta > 3)
1137 {
1138 delta -= 3;
1139 if (paddingSize > delta) paddingSize %= delta;
1140 }
1141 payload[0] = eECIESx25519BlkPadding;
1142 htobe16buf (payload + 1, paddingSize);
1143 if (paddingSize) memset (payload + 3, 0, paddingSize);
1144 len += paddingSize + 3;
1145 }
1146 return len;
1147 }
11331148
1134 std::shared_ptr<I2NPMessage> WrapECIESX25519AEADRatchetMessage (std::shared_ptr<const I2NPMessage> msg, const uint8_t * key, uint64_t tag)
1149 std::shared_ptr<I2NPMessage> WrapECIESX25519Message (std::shared_ptr<const I2NPMessage> msg, const uint8_t * key, uint64_t tag)
11351150 {
11361151 auto m = NewI2NPMessage ();
11371152 m->Align (12); // in order to get buf aligned to 16 (12 + 4)
11381153 uint8_t * buf = m->GetPayload () + 4; // 4 bytes for length
1139 uint8_t nonce[12];
1140 memset (nonce, 0, 12); // n = 0
11411154 size_t offset = 0;
11421155 memcpy (buf + offset, &tag, 8); offset += 8;
11431156 auto payload = buf + offset;
1144 uint16_t cloveSize = msg->GetPayloadLength () + 9 + 1;
1145 size_t len = cloveSize + 3;
1146 payload[0] = eECIESx25519BlkGalicClove; // clove type
1147 htobe16buf (payload + 1, cloveSize); // size
1148 payload += 3;
1149 *payload = 0; payload++; // flag and delivery instructions
1150 *payload = msg->GetTypeID (); // I2NP msg type
1151 htobe32buf (payload + 1, msg->GetMsgID ()); // msgID
1152 htobe32buf (payload + 5, msg->GetExpiration () / 1000); // expiration in seconds
1153 memcpy (payload + 9, msg->GetPayload (), msg->GetPayloadLength ());
1154
1155 if (!i2p::crypto::AEADChaCha20Poly1305 (buf + offset, len, buf, 8, key, nonce, buf + offset, len + 16, true)) // encrypt
1157 size_t len = CreateGarlicPayload (msg, payload, false, 956); // 1003 - 8 tag - 16 Poly1305 hash - 16 I2NP header - 4 garlic length - 3 local tunnel delivery
1158 uint8_t nonce[12];
1159 memset (nonce, 0, 12); // n = 0
1160 if (!i2p::crypto::AEADChaCha20Poly1305 (payload, len, buf, 8, key, nonce, payload, len + 16, true)) // encrypt
11561161 {
11571162 LogPrint (eLogWarning, "Garlic: Payload section AEAD encryption failed");
11581163 return nullptr;
11591164 }
11601165 offset += len + 16;
1161
11621166 htobe32buf (m->GetPayload (), offset);
11631167 m->len += offset + 4;
11641168 m->FillI2NPMessageHeader (eI2NPGarlic);
11651169 return m;
11661170 }
11671171
1172 std::shared_ptr<I2NPMessage> WrapECIESX25519MessageForRouter (std::shared_ptr<const I2NPMessage> msg, const uint8_t * routerPublicKey)
1173 {
1174 // Noise_N, we are Alice, routerPublicKey is Bob's
1175 i2p::crypto::NoiseSymmetricState noiseState;
1176 i2p::crypto::InitNoiseNState (noiseState, routerPublicKey);
1177 auto m = NewI2NPMessage ();
1178 m->Align (12); // in order to get buf aligned to 16 (12 + 4)
1179 uint8_t * buf = m->GetPayload () + 4; // 4 bytes for length
1180 size_t offset = 0;
1181 auto ephemeralKeys = i2p::transport::transports.GetNextX25519KeysPair ();
1182 memcpy (buf + offset, ephemeralKeys->GetPublicKey (), 32);
1183 noiseState.MixHash (buf + offset, 32); // h = SHA256(h || aepk)
1184 offset += 32;
1185 uint8_t sharedSecret[32];
1186 if (!ephemeralKeys->Agree (routerPublicKey, sharedSecret)) // x25519(aesk, bpk)
1187 {
1188 LogPrint (eLogWarning, "Garlic: Incorrect Bob static key");
1189 return nullptr;
1190 }
1191 noiseState.MixKey (sharedSecret);
1192 auto payload = buf + offset;
1193 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
1194 uint8_t nonce[12];
1195 memset (nonce, 0, 12);
1196 // encrypt payload
1197 if (!i2p::crypto::AEADChaCha20Poly1305 (payload, len, noiseState.m_H, 32, noiseState.m_CK + 32, nonce, payload, len + 16, true)) // encrypt
1198 {
1199 LogPrint (eLogWarning, "Garlic: Payload for router AEAD encryption failed");
1200 return nullptr;
1201 }
1202 offset += len + 16;
1203 htobe32buf (m->GetPayload (), offset);
1204 m->len += offset + 4;
1205 m->FillI2NPMessageHeader (eI2NPGarlic);
1206 return m;
1207 }
11681208 }
11691209 }
0
01 /*
1 * Copyright (c) 2013-2020, The PurpleI2P Project
2 * Copyright (c) 2013-2021, The PurpleI2P Project
23 *
34 * This file is part of Purple i2pd project and licensed under BSD3
45 *
5859
5960 private:
6061
61 union
62 {
63 uint64_t ll[8];
64 uint8_t buf[64];
65
66 const uint8_t * GetSessTagCK () const { return buf; }; // sessTag_chainKey = keydata[0:31]
67 const uint8_t * GetSessTagConstant () const { return buf + 32; }; // SESSTAG_CONSTANT = keydata[32:63]
68 uint64_t GetTag () const { return ll[4]; }; // tag = keydata[32:39]
69
70 } m_KeyData;
62 i2p::data::Tag<64> m_SessionTagKeyData;
7163 uint8_t m_SessTagConstant[32], m_SymmKeyCK[32], m_CurrentSymmKeyCK[64], m_NextRootKey[32];
7264 int m_NextIndex, m_NextSymmKeyIndex;
7365 std::unordered_map<int, i2p::data::Tag<32> > m_ItermediateSymmKeys;
10395 uint64_t m_ExpirationTimestamp = 0;
10496 };
10597
106 class DatabaseLookupTagSet: public ReceiveRatchetTagSet
107 {
108 public:
109
110 DatabaseLookupTagSet (GarlicDestination * destination, const uint8_t * key);
98 class SymmetricKeyTagSet: public ReceiveRatchetTagSet
99 {
100 public:
101
102 SymmetricKeyTagSet (GarlicDestination * destination, const uint8_t * key);
111103
112104 bool IsIndexExpired (int index) const { return false; };
113105 bool HandleNextMessage (uint8_t * buf, size_t len, int index);
146138 eSessionStateNewSessionSent,
147139 eSessionStateNewSessionReplySent,
148140 eSessionStateEstablished,
149 eSessionStateOneTime,
150 eSessionStateForRouter
141 eSessionStateOneTime
151142 };
152143
153144 struct DHRatchet
165156
166157 bool HandleNextMessage (uint8_t * buf, size_t len, std::shared_ptr<ReceiveRatchetTagSet> receiveTagset, int index = 0);
167158 std::shared_ptr<I2NPMessage> WrapSingleMessage (std::shared_ptr<const I2NPMessage> msg);
168 std::shared_ptr<I2NPMessage> WrapOneTimeMessage (std::shared_ptr<const I2NPMessage> msg, bool isForRouter = false);
159 std::shared_ptr<I2NPMessage> WrapOneTimeMessage (std::shared_ptr<const I2NPMessage> msg);
169160
170161 const uint8_t * GetRemoteStaticKey () const { return m_RemoteStaticKey; }
171162 void SetRemoteStaticKey (const uint8_t * key) { memcpy (m_RemoteStaticKey, key, 32); }
206197 bool NewSessionReplyMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen);
207198 bool NextNewSessionReplyMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen);
208199 bool NewExistingSessionMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen);
209 bool NewOutgoingMessageForRouter (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen);
210
211 std::vector<uint8_t> CreatePayload (std::shared_ptr<const I2NPMessage> msg, bool first);
200
201 size_t CreatePayload (std::shared_ptr<const I2NPMessage> msg, bool first, uint8_t * payload);
212202 size_t CreateGarlicClove (std::shared_ptr<const I2NPMessage> msg, uint8_t * buf, size_t len);
213203 size_t CreateLeaseSetClove (std::shared_ptr<const i2p::data::LocalLeaseSet> ls, uint64_t ts, uint8_t * buf, size_t len);
214204
248238
249239 RouterIncomingRatchetSession (const i2p::crypto::NoiseSymmetricState& initState);
250240 bool HandleNextMessage (const uint8_t * buf, size_t len);
241 i2p::crypto::NoiseSymmetricState& GetCurrentNoiseState () { return m_CurrentNoiseState; };
242
243 private:
244
245 i2p::crypto::NoiseSymmetricState m_CurrentNoiseState;
251246 };
252247
253 std::shared_ptr<I2NPMessage> WrapECIESX25519AEADRatchetMessage (std::shared_ptr<const I2NPMessage> msg, const uint8_t * key, uint64_t tag);
248 std::shared_ptr<I2NPMessage> WrapECIESX25519Message (std::shared_ptr<const I2NPMessage> msg, const uint8_t * key, uint64_t tag);
249 std::shared_ptr<I2NPMessage> WrapECIESX25519MessageForRouter (std::shared_ptr<const I2NPMessage> msg, const uint8_t * routerPublicKey);
254250 }
255251 }
256252
257253 #endif
254
1111 #ifdef _WIN32
1212 #include <shlobj.h>
1313 #include <windows.h>
14 #include <codecvt>
1415 #endif
1516
1617 #include "Base.h"
2223 namespace fs {
2324 std::string appName = "i2pd";
2425 std::string dataDir = "";
26 std::string certsDir = "";
2527 #ifdef _WIN32
2628 std::string dirSep = "\\";
2729 #else
3840
3941 const std::string & GetDataDir () {
4042 return dataDir;
43 }
44
45 const std::string & GetCertsDir () {
46 return certsDir;
47 }
48
49 const std::string GetUTF8DataDir () {
50 #ifdef _WIN32
51 boost::filesystem::wpath path (dataDir);
52 auto loc = boost::filesystem::path::imbue(std::locale( std::locale(), new std::codecvt_utf8_utf16<wchar_t>() ) ); // convert path to UTF-8
53 auto dataDirUTF8 = path.string();
54 boost::filesystem::path::imbue(loc); // Return locale settings back
55 return dataDirUTF8;
56 #else
57 return dataDir; // linux, osx, android uses UTF-8 by default
58 #endif
4159 }
4260
4361 void DetectDataDir(const std::string & cmdline_param, bool isService) {
4664 return;
4765 }
4866 #ifdef _WIN32
49 char localAppData[MAX_PATH];
67 wchar_t localAppData[MAX_PATH];
5068
5169 // check executable directory first
52 if(!GetModuleFileName(NULL, localAppData, MAX_PATH))
70 if(!GetModuleFileNameW(NULL, localAppData, MAX_PATH))
5371 {
5472 #ifdef WIN32_APP
5573 MessageBox(NULL, TEXT("Unable to get application path!"), TEXT("I2Pd: error"), MB_ICONERROR | MB_OK);
6078 }
6179 else
6280 {
63 auto execPath = boost::filesystem::path(localAppData).parent_path();
81 auto execPath = boost::filesystem::wpath(localAppData).parent_path();
6482
6583 // if config file exists in .exe's folder use it
6684 if(boost::filesystem::exists(execPath/"i2pd.conf")) // TODO: magic string
85 {
6786 dataDir = execPath.string ();
68 else // otherwise %appdata%
87 } else // otherwise %appdata%
6988 {
70 if(SHGetFolderPath(NULL, CSIDL_APPDATA, NULL, 0, localAppData) != S_OK)
89 if(SHGetFolderPathW(NULL, CSIDL_APPDATA, NULL, 0, localAppData) != S_OK)
7190 {
7291 #ifdef WIN32_APP
7392 MessageBox(NULL, TEXT("Unable to get AppData path!"), TEXT("I2Pd: error"), MB_ICONERROR | MB_OK);
7796 exit(1);
7897 }
7998 else
80 dataDir = std::string(localAppData) + "\\" + appName;
99 {
100 dataDir = boost::filesystem::wpath(localAppData).string() + "\\" + appName;
101 }
81102 }
82103 }
83104 return;
109130 #endif
110131 }
111132
133 void SetCertsDir(const std::string & cmdline_certsdir) {
134 if (cmdline_certsdir != "")
135 {
136 if (cmdline_certsdir[cmdline_certsdir.length()-1] == '/')
137 certsDir = cmdline_certsdir.substr(0, cmdline_certsdir.size()-1); // strip trailing slash
138 else
139 certsDir = cmdline_certsdir;
140 }
141 else
142 {
143 certsDir = i2p::fs::DataDirPath("certificates");
144 }
145 return;
146 }
147
112148 bool Init() {
113149 if (!boost::filesystem::exists(dataDir))
114150 boost::filesystem::create_directory(dataDir);
7474 /** @brief Returns datadir path */
7575 const std::string & GetDataDir();
7676
77 /** @brief Returns certsdir path */
78 const std::string & GetCertsDir();
79
80 /** @brief Returns datadir path in UTF-8 encoding */
81 const std::string GetUTF8DataDir();
82
7783 /**
7884 * @brief Set datadir either from cmdline option or using autodetection
7985 * @param cmdline_param Value of cmdline parameter --datadir=<something>
8692 * Mac: /Library/Application Support/i2pd/ or ~/Library/Application Support/i2pd/
8793 * Unix: /var/lib/i2pd/ (system=1) >> ~/.i2pd/ or /tmp/i2pd/
8894 */
89 void DetectDataDir(const std::string & cmdline_datadir, bool isService = false);
95 void DetectDataDir(const std::string & cmdline_datadir, bool isService = false);
96
97 /**
98 * @brief Set certsdir either from cmdline option or using autodetection
99 * @param cmdline_param Value of cmdline parameter --certsdir=<something>
100 *
101 * Examples of autodetected paths:
102 *
103 * Windows < Vista: C:\Documents and Settings\Username\Application Data\i2pd\certificates
104 * Windows >= Vista: C:\Users\Username\AppData\Roaming\i2pd\certificates
105 * Mac: /Library/Application Support/i2pd/ or ~/Library/Application Support/i2pd/certificates
106 * Unix: /var/lib/i2pd/certificates (system=1) >> ~/.i2pd/ or /tmp/i2pd/certificates
107 */
108 void SetCertsDir(const std::string & cmdline_certsdir);
90109
91110 /**
92111 * @brief Create subdirectories inside datadir
1212 #include "FS.h"
1313 #include "Log.h"
1414 #include "Family.h"
15 #include "Config.h"
1516
1617 namespace i2p
1718 {
9798
9899 void Families::LoadCertificates ()
99100 {
100 std::string certDir = i2p::fs::DataDirPath("certificates", "family");
101 std::string certDir = i2p::fs::GetCertsDir() + i2p::fs::dirSep + "family";
102
101103 std::vector<std::string> files;
102104 int numCertificates = 0;
103105
432432 }
433433
434434 GarlicDestination::GarlicDestination (): m_NumTags (32), // 32 tags by default
435 m_NumRatchetInboundTags (0) // 0 means standard
435 m_PayloadBuffer (nullptr), m_NumRatchetInboundTags (0) // 0 means standard
436436 {
437437 m_Ctx = BN_CTX_new ();
438438 }
440440 GarlicDestination::~GarlicDestination ()
441441 {
442442 BN_CTX_free (m_Ctx);
443 if (m_PayloadBuffer)
444 delete[] m_PayloadBuffer;
443445 }
444446
445447 void GarlicDestination::CleanUp ()
470472 {
471473 uint64_t t;
472474 memcpy (&t, tag, 8);
473 auto tagset = std::make_shared<DatabaseLookupTagSet>(this, key);
474 m_ECIESx25519Tags.emplace (t, ECIESX25519AEADRatchetIndexTagset{0, tagset});
475 AddECIESx25519Key (key, t);
476 }
477
478 void GarlicDestination::AddECIESx25519Key (const uint8_t * key, uint64_t tag)
479 {
480 auto tagset = std::make_shared<SymmetricKeyTagSet>(this, key);
481 m_ECIESx25519Tags.emplace (tag, ECIESX25519AEADRatchetIndexTagset{0, tagset});
475482 }
476483
477484 bool GarlicDestination::SubmitSessionKey (const uint8_t * key, const uint8_t * tag)
493500 buf += 4; // length
494501
495502 bool found = false;
496 uint64_t tag;
497503 if (SupportsEncryptionType (i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD))
498 {
499504 // try ECIESx25519 tag
500 memcpy (&tag, buf, 8);
501 auto it1 = m_ECIESx25519Tags.find (tag);
502 if (it1 != m_ECIESx25519Tags.end ())
503 {
504 found = true;
505 if (it1->second.tagset->HandleNextMessage (buf, length, it1->second.index))
506 m_LastTagset = it1->second.tagset;
507 else
508 LogPrint (eLogError, "Garlic: can't handle ECIES-X25519-AEAD-Ratchet message");
509 m_ECIESx25519Tags.erase (it1);
510 }
511 }
505 found = HandleECIESx25519TagMessage (buf, length);
512506 if (!found)
513507 {
514508 auto it = !mod ? m_Tags.find (SessionTag(buf)) : m_Tags.end (); // AES block is multiple of 16
554548 // try to gererate more tags for last tagset
555549 if (m_LastTagset && (m_LastTagset->GetNextIndex () - m_LastTagset->GetTrimBehind () < 3*ECIESX25519_MAX_NUM_GENERATED_TAGS))
556550 {
551 uint64_t missingTag; memcpy (&missingTag, buf, 8);
557552 auto maxTags = std::max (m_NumRatchetInboundTags, ECIESX25519_MAX_NUM_GENERATED_TAGS);
558553 LogPrint (eLogWarning, "Garlic: trying to generate more ECIES-X25519-AEAD-Ratchet tags");
559554 for (int i = 0; i < maxTags; i++)
564559 LogPrint (eLogError, "Garlic: can't create new ECIES-X25519-AEAD-Ratchet tag for last tagset");
565560 break;
566561 }
567 if (nextTag == tag)
562 if (nextTag == missingTag)
568563 {
569564 LogPrint (eLogDebug, "Garlic: Missing ECIES-X25519-AEAD-Ratchet tag was generated");
570 if (m_LastTagset->HandleNextMessage (buf, length, m_ECIESx25519Tags[tag].index))
565 if (m_LastTagset->HandleNextMessage (buf, length, m_ECIESx25519Tags[nextTag].index))
571566 found = true;
572567 break;
573568 }
584579 }
585580 }
586581
582 bool GarlicDestination::HandleECIESx25519TagMessage (uint8_t * buf, size_t len)
583 {
584 uint64_t tag;
585 memcpy (&tag, buf, 8);
586 auto it = m_ECIESx25519Tags.find (tag);
587 if (it != m_ECIESx25519Tags.end ())
588 {
589 if (it->second.tagset->HandleNextMessage (buf, len, it->second.index))
590 m_LastTagset = it->second.tagset;
591 else
592 LogPrint (eLogError, "Garlic: can't handle ECIES-X25519-AEAD-Ratchet message");
593 m_ECIESx25519Tags.erase (it);
594 return true;
595 }
596 return false;
597 }
598
587599 void GarlicDestination::HandleAESBlock (uint8_t * buf, size_t len, std::shared_ptr<AESDecryption> decryption,
588600 std::shared_ptr<i2p::tunnel::InboundTunnel> from)
589601 {
748760 std::shared_ptr<I2NPMessage> msg)
749761 {
750762 if (router->GetEncryptionType () == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD)
751 {
752 auto session = std::make_shared<ECIESX25519AEADRatchetSession>(this, false);
753 session->SetRemoteStaticKey (router->GetIdentity ()->GetEncryptionPublicKey ());
754 return session->WrapOneTimeMessage (msg, true);
755 }
763 return WrapECIESX25519MessageForRouter (msg, router->GetIdentity ()->GetEncryptionPublicKey ());
756764 else
757765 {
758766 auto session = GetRoutingSession (router, false);
10351043 {
10361044 LogPrint (eLogDebug, "Garlic: type local");
10371045 I2NPMessageType typeID = (I2NPMessageType)(buf[0]); buf++; // typeid
1038 buf += (4 + 4); // msgID + expiration
1046 int32_t msgID = bufbe32toh (buf); buf += 4; // msgID
1047 buf += 4; // expiration
10391048 ptrdiff_t offset = buf - buf1;
10401049 if (offset <= (int)len)
1041 HandleCloveI2NPMessage (typeID, buf, len - offset);
1050 HandleCloveI2NPMessage (typeID, buf, len - offset, msgID);
10421051 else
10431052 LogPrint (eLogError, "Garlic: clove is too long");
10441053 break;
10571066 }
10581067 uint32_t gwTunnel = bufbe32toh (buf); buf += 4;
10591068 I2NPMessageType typeID = (I2NPMessageType)(buf[0]); buf++; // typeid
1060 buf += (4 + 4); // msgID + expiration
1069 uint32_t msgID = bufbe32toh (buf); buf += 4; // msgID
1070 buf += 4; // expiration
10611071 offset += 13;
10621072 if (GetTunnelPool ())
10631073 {
10641074 auto tunnel = GetTunnelPool ()->GetNextOutboundTunnel ();
10651075 if (tunnel)
1066 tunnel->SendTunnelDataMsg (gwHash, gwTunnel, CreateI2NPMessage (typeID, buf, len - offset));
1076 tunnel->SendTunnelDataMsg (gwHash, gwTunnel, CreateI2NPMessage (typeID, buf, len - offset, msgID));
10671077 else
10681078 LogPrint (eLogWarning, "Garlic: No outbound tunnels available for garlic clove");
10691079 }
11141124 m_ECIESx25519Sessions.erase (it);
11151125 }
11161126 }
1127
1128 uint8_t * GarlicDestination::GetPayloadBuffer ()
1129 {
1130 if (!m_PayloadBuffer)
1131 m_PayloadBuffer = new uint8_t[I2NP_MAX_MESSAGE_SIZE];
1132 return m_PayloadBuffer;
1133 }
11171134 }
11181135 }
242242 std::shared_ptr<I2NPMessage> msg);
243243
244244 void AddSessionKey (const uint8_t * key, const uint8_t * tag); // one tag
245 void AddECIESx25519Key (const uint8_t * key, const uint8_t * tag); // one tag
245 void AddECIESx25519Key (const uint8_t * key, uint64_t tag); // one tag
246246 virtual bool SubmitSessionKey (const uint8_t * key, const uint8_t * tag); // from different thread
247247 void DeliveryStatusSent (GarlicRoutingSessionPtr session, uint32_t msgID);
248248 uint64_t AddECIESx25519SessionNextTag (ReceiveRatchetTagSetPtr tagset);
249249 void AddECIESx25519Session (const uint8_t * staticKey, ECIESX25519AEADRatchetSessionPtr session);
250250 void RemoveECIESx25519Session (const uint8_t * staticKey);
251251 void HandleECIESx25519GarlicClove (const uint8_t * buf, size_t len);
252
252 uint8_t * GetPayloadBuffer ();
253
253254 virtual void ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg);
254255 virtual void ProcessDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg);
255256 virtual void SetLeaseSetUpdated ();
259260
260261 protected:
261262
263 void AddECIESx25519Key (const uint8_t * key, const uint8_t * tag); // one tag
264 bool HandleECIESx25519TagMessage (uint8_t * buf, size_t len); // return true if found
262265 virtual void HandleI2NPMessage (const uint8_t * buf, size_t len) = 0; // called from clove only
263 virtual bool HandleCloveI2NPMessage (I2NPMessageType typeID, const uint8_t * payload, size_t len) = 0;
266 virtual bool HandleCloveI2NPMessage (I2NPMessageType typeID, const uint8_t * payload, size_t len, uint32_t msgID) = 0;
264267 void HandleGarlicMessage (std::shared_ptr<I2NPMessage> msg);
265268 void HandleDeliveryStatusMessage (uint32_t msgID);
266269
281284 std::mutex m_SessionsMutex;
282285 std::unordered_map<i2p::data::IdentHash, ElGamalAESSessionPtr> m_Sessions;
283286 std::unordered_map<i2p::data::Tag<32>, ECIESX25519AEADRatchetSessionPtr> m_ECIESx25519Sessions; // static key -> session
287 uint8_t * m_PayloadBuffer; // for ECIESX25519AEADRatchet
284288 // incoming
285289 int m_NumRatchetInboundTags;
286290 std::unordered_map<SessionTag, std::shared_ptr<AESDecryption>, std::hash<i2p::data::Tag<32> > > m_Tags;
186186
187187 params.clear();
188188 for (const auto& it : tokens) {
189 if (!it.length()) // empty
190 continue;
189191 std::size_t eq = it.find ('=');
190192 if (eq != std::string::npos) {
191193 auto e = std::pair<std::string, std::string>(it.substr(0, eq), it.substr(eq + 1));
00 /*
1 * Copyright (c) 2013-2020, The PurpleI2P Project
1 * Copyright (c) 2013-2021, The PurpleI2P Project
22 *
33 * This file is part of Purple i2pd project and licensed under BSD3
44 *
1717 #include "Tunnel.h"
1818 #include "Transports.h"
1919 #include "Garlic.h"
20 #include "ECIESX25519AEADRatchetSession.h"
2021 #include "I2NPProtocol.h"
2122 #include "version.h"
2223
3435 return std::make_shared<I2NPMessageBuffer<I2NP_MAX_SHORT_MESSAGE_SIZE> >();
3536 }
3637
37 std::shared_ptr<I2NPMessage> NewI2NPTunnelMessage ()
38 {
39 auto msg = new I2NPMessageBuffer<i2p::tunnel::TUNNEL_DATA_MSG_SIZE + I2NP_HEADER_SIZE + 34>(); // reserved for alignment and NTCP 16 + 6 + 12
40 msg->Align (12);
38 std::shared_ptr<I2NPMessage> NewI2NPTunnelMessage (bool endpoint)
39 {
40 I2NPMessage * msg = nullptr;
41 if (endpoint)
42 {
43 // should fit two tunnel message + tunnel gateway header, enough for one garlic encrypted streaming packet
44 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
45 msg->Align (6);
46 msg->offset += TUNNEL_GATEWAY_HEADER_SIZE; // reserve room for TunnelGateway header
47 }
48 else
49 {
50 msg = new I2NPMessageBuffer<i2p::tunnel::TUNNEL_DATA_MSG_SIZE + I2NP_HEADER_SIZE + 34>(); // reserved for alignment and NTCP 16 + 6 + 12
51 msg->Align (12);
52 }
4153 return std::shared_ptr<I2NPMessage>(msg);
4254 }
4355
249261 if (!router) // we send own RouterInfo
250262 router = context.GetSharedRouterInfo ();
251263
264 if (!router->GetBuffer ())
265 {
266 LogPrint (eLogError, "I2NP: Invalid RouterInfo buffer for DatabaseStore");
267 return nullptr;
268 }
269
252270 auto m = NewI2NPShortMessage ();
253271 uint8_t * payload = m->GetPayload ();
254272
362380 return g_MaxNumTransitTunnels;
363381 }
364382
365 bool HandleBuildRequestRecords (int num, uint8_t * records, uint8_t * clearText)
383 static bool HandleBuildRequestRecords (int num, uint8_t * records, uint8_t * clearText)
366384 {
367385 for (int i = 0; i < num; i++)
368386 {
386404 bufbe32toh (clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET),
387405 clearText + ECIES_BUILD_REQUEST_RECORD_LAYER_KEY_OFFSET,
388406 clearText + ECIES_BUILD_REQUEST_RECORD_IV_KEY_OFFSET,
389 clearText[ECIES_BUILD_REQUEST_RECORD_FLAG_OFFSET] & 0x80,
390 clearText[ECIES_BUILD_REQUEST_RECORD_FLAG_OFFSET] & 0x40) :
407 clearText[ECIES_BUILD_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_GATEWAY_FLAG,
408 clearText[ECIES_BUILD_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_ENDPOINT_FLAG) :
391409 i2p::tunnel::CreateTransitTunnel (
392410 bufbe32toh (clearText + BUILD_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET),
393411 clearText + BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET,
394412 bufbe32toh (clearText + BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET),
395413 clearText + BUILD_REQUEST_RECORD_LAYER_KEY_OFFSET,
396414 clearText + BUILD_REQUEST_RECORD_IV_KEY_OFFSET,
397 clearText[BUILD_REQUEST_RECORD_FLAG_OFFSET] & 0x80,
398 clearText[BUILD_REQUEST_RECORD_FLAG_OFFSET] & 0x40);
415 clearText[BUILD_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_GATEWAY_FLAG,
416 clearText[BUILD_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_ENDPOINT_FLAG);
399417 i2p::tunnel::tunnels.AddTransitTunnel (transitTunnel);
400418 }
401419 else
423441 {
424442 uint8_t nonce[12];
425443 memset (nonce, 0, 12);
426 auto noiseState = std::move (i2p::context.GetCurrentNoiseState ());
427 if (!noiseState || !i2p::crypto::AEADChaCha20Poly1305 (reply, TUNNEL_BUILD_RECORD_SIZE - 16,
428 noiseState->m_H, 32, noiseState->m_CK, nonce, reply, TUNNEL_BUILD_RECORD_SIZE, true)) // encrypt
444 auto& noiseState = i2p::context.GetCurrentNoiseState ();
445 if (!i2p::crypto::AEADChaCha20Poly1305 (reply, TUNNEL_BUILD_RECORD_SIZE - 16,
446 noiseState.m_H, 32, noiseState.m_CK, nonce, reply, TUNNEL_BUILD_RECORD_SIZE, true)) // encrypt
429447 {
430448 LogPrint (eLogWarning, "I2NP: Reply AEAD encryption failed");
431449 return false;
451469 return false;
452470 }
453471
454 void HandleVariableTunnelBuildMsg (uint32_t replyMsgID, uint8_t * buf, size_t len)
472 static void HandleVariableTunnelBuildMsg (uint32_t replyMsgID, uint8_t * buf, size_t len)
455473 {
456474 int num = buf[0];
457475 LogPrint (eLogDebug, "I2NP: VariableTunnelBuild ", num, " records");
458476 if (len < num*TUNNEL_BUILD_RECORD_SIZE + 1)
459477 {
460 LogPrint (eLogError, "VaribleTunnelBuild message of ", num, " records is too short ", len);
478 LogPrint (eLogError, "I2NP: VaribleTunnelBuild message of ", num, " records is too short ", len);
461479 return;
462480 }
463481
485503 uint8_t clearText[ECIES_BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE];
486504 if (HandleBuildRequestRecords (num, buf + 1, clearText))
487505 {
488 if (clearText[ECIES_BUILD_REQUEST_RECORD_FLAG_OFFSET] & 0x40) // we are endpoint of outboud tunnel
506 if (clearText[ECIES_BUILD_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_ENDPOINT_FLAG) // we are endpoint of outboud tunnel
489507 {
490508 // so we send it to reply tunnel
491509 transports.SendMessage (clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET,
504522 uint8_t clearText[BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE];
505523 if (HandleBuildRequestRecords (num, buf + 1, clearText))
506524 {
507 if (clearText[BUILD_REQUEST_RECORD_FLAG_OFFSET] & 0x40) // we are endpoint of outboud tunnel
525 if (clearText[BUILD_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_ENDPOINT_FLAG) // we are endpoint of outboud tunnel
508526 {
509527 // so we send it to reply tunnel
510528 transports.SendMessage (clearText + BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET,
521539 }
522540 }
523541
524 void HandleTunnelBuildMsg (uint8_t * buf, size_t len)
542 static void HandleTunnelBuildMsg (uint8_t * buf, size_t len)
525543 {
526544 if (i2p::context.IsECIES ())
527545 {
528 LogPrint (eLogWarning, "TunnelBuild is too old for ECIES router");
546 LogPrint (eLogWarning, "I2NP: TunnelBuild is too old for ECIES router");
529547 return;
530548 }
531549 if (len < NUM_TUNNEL_BUILD_RECORDS*TUNNEL_BUILD_RECORD_SIZE)
532550 {
533 LogPrint (eLogError, "TunnelBuild message is too short ", len);
551 LogPrint (eLogError, "I2NP: TunnelBuild message is too short ", len);
534552 return;
535553 }
536554 uint8_t clearText[BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE];
537555 if (HandleBuildRequestRecords (NUM_TUNNEL_BUILD_RECORDS, buf, clearText))
538556 {
539 if (clearText[BUILD_REQUEST_RECORD_FLAG_OFFSET] & 0x40) // we are endpoint of outbound tunnel
557 if (clearText[BUILD_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_ENDPOINT_FLAG) // we are endpoint of outbound tunnel
540558 {
541559 // so we send it to reply tunnel
542560 transports.SendMessage (clearText + BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET,
551569 }
552570 }
553571
554 void HandleVariableTunnelBuildReplyMsg (uint32_t replyMsgID, uint8_t * buf, size_t len)
572 static void HandleTunnelBuildReplyMsg (uint32_t replyMsgID, uint8_t * buf, size_t len, bool isShort)
555573 {
556574 int num = buf[0];
557 LogPrint (eLogDebug, "I2NP: VariableTunnelBuildReplyMsg of ", num, " records replyMsgID=", replyMsgID);
558 if (len < num*BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE + 1)
559 {
560 LogPrint (eLogError, "VaribleTunnelBuildReply message of ", num, " records is too short ", len);
575 LogPrint (eLogDebug, "I2NP: TunnelBuildReplyMsg of ", num, " records replyMsgID=", replyMsgID);
576 size_t recordSize = isShort ? SHORT_TUNNEL_BUILD_RECORD_SIZE : TUNNEL_BUILD_RECORD_SIZE;
577 if (len < num*recordSize + 1)
578 {
579 LogPrint (eLogError, "I2NP: TunnelBuildReply message of ", num, " records is too short ", len);
561580 return;
562581 }
563582
581600 LogPrint (eLogWarning, "I2NP: Pending tunnel for message ", replyMsgID, " not found");
582601 }
583602
584
603 static void HandleShortTunnelBuildMsg (uint32_t replyMsgID, uint8_t * buf, size_t len)
604 {
605 if (!i2p::context.IsECIES ())
606 {
607 LogPrint (eLogWarning, "I2NP: ShortTunnelBuild can be handled by ECIES router only");
608 return;
609 }
610 int num = buf[0];
611 LogPrint (eLogDebug, "I2NP: ShortTunnelBuild ", num, " records");
612 if (len < num*SHORT_TUNNEL_BUILD_RECORD_SIZE + 1)
613 {
614 LogPrint (eLogError, "I2NP: ShortTunnelBuild message of ", num, " records is too short ", len);
615 return;
616 }
617 auto tunnel = i2p::tunnel::tunnels.GetPendingInboundTunnel (replyMsgID);
618 if (tunnel)
619 {
620 // endpoint of inbound tunnel
621 LogPrint (eLogDebug, "I2NP: ShortTunnelBuild reply for tunnel ", tunnel->GetTunnelID ());
622 if (tunnel->HandleTunnelBuildResponse (buf, len))
623 {
624 LogPrint (eLogInfo, "I2NP: Inbound tunnel ", tunnel->GetTunnelID (), " has been created");
625 tunnel->SetState (i2p::tunnel::eTunnelStateEstablished);
626 i2p::tunnel::tunnels.AddInboundTunnel (tunnel);
627 }
628 else
629 {
630 LogPrint (eLogInfo, "I2NP: Inbound tunnel ", tunnel->GetTunnelID (), " has been declined");
631 tunnel->SetState (i2p::tunnel::eTunnelStateBuildFailed);
632 }
633 return;
634 }
635 const uint8_t * record = buf + 1;
636 for (int i = 0; i < num; i++)
637 {
638 if (!memcmp (record, (const uint8_t *)i2p::context.GetRouterInfo ().GetIdentHash (), 16))
639 {
640 LogPrint (eLogDebug, "I2NP: Short request record ", i, " is ours");
641 uint8_t clearText[SHORT_REQUEST_RECORD_CLEAR_TEXT_SIZE];
642 if (!i2p::context.DecryptTunnelShortRequestRecord (record + SHORT_REQUEST_RECORD_ENCRYPTED_OFFSET, clearText))
643 {
644 LogPrint (eLogWarning, "I2NP: Can't decrypt short request record ", i);
645 return;
646 }
647 if (clearText[SHORT_REQUEST_RECORD_LAYER_ENCRYPTION_TYPE]) // not AES
648 {
649 LogPrint (eLogWarning, "I2NP: Unknown layer encryption type ", clearText[SHORT_REQUEST_RECORD_LAYER_ENCRYPTION_TYPE], " in short request record");
650 return;
651 }
652 auto& noiseState = i2p::context.GetCurrentNoiseState ();
653 uint8_t replyKey[32], layerKey[32], ivKey[32];
654 i2p::crypto::HKDF (noiseState.m_CK, nullptr, 0, "SMTunnelReplyKey", noiseState.m_CK);
655 memcpy (replyKey, noiseState.m_CK + 32, 32);
656 i2p::crypto::HKDF (noiseState.m_CK, nullptr, 0, "SMTunnelLayerKey", noiseState.m_CK);
657 memcpy (layerKey, noiseState.m_CK + 32, 32);
658 bool isEndpoint = clearText[SHORT_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_ENDPOINT_FLAG;
659 if (isEndpoint)
660 {
661 i2p::crypto::HKDF (noiseState.m_CK, nullptr, 0, "TunnelLayerIVKey", noiseState.m_CK);
662 memcpy (ivKey, noiseState.m_CK + 32, 32);
663 }
664 else
665 memcpy (ivKey, noiseState.m_CK , 32);
666
667 // check if we accept this tunnel
668 uint8_t retCode = 0;
669 if (!i2p::context.AcceptsTunnels () ||
670 i2p::tunnel::tunnels.GetTransitTunnels ().size () > g_MaxNumTransitTunnels ||
671 i2p::transport::transports.IsBandwidthExceeded () ||
672 i2p::transport::transports.IsTransitBandwidthExceeded ())
673 retCode = 30;
674 if (!retCode)
675 {
676 // create new transit tunnel
677 auto transitTunnel = i2p::tunnel::CreateTransitTunnel (
678 bufbe32toh (clearText + SHORT_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET),
679 clearText + SHORT_REQUEST_RECORD_NEXT_IDENT_OFFSET,
680 bufbe32toh (clearText + SHORT_REQUEST_RECORD_NEXT_TUNNEL_OFFSET),
681 layerKey, ivKey,
682 clearText[SHORT_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_GATEWAY_FLAG,
683 clearText[SHORT_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_ENDPOINT_FLAG);
684 i2p::tunnel::tunnels.AddTransitTunnel (transitTunnel);
685 }
686
687 // encrypt reply
688 uint8_t nonce[12];
689 memset (nonce, 0, 12);
690 uint8_t * reply = buf + 1;
691 for (int j = 0; j < num; j++)
692 {
693 nonce[4] = j; // nonce is record #
694 if (j == i)
695 {
696 memset (reply + SHORT_RESPONSE_RECORD_OPTIONS_OFFSET, 0, 2); // no options
697 reply[SHORT_RESPONSE_RECORD_RET_OFFSET] = retCode;
698 if (!i2p::crypto::AEADChaCha20Poly1305 (reply, SHORT_TUNNEL_BUILD_RECORD_SIZE - 16,
699 noiseState.m_H, 32, replyKey, nonce, reply, SHORT_TUNNEL_BUILD_RECORD_SIZE, true)) // encrypt
700 {
701 LogPrint (eLogWarning, "I2NP: Short reply AEAD encryption failed");
702 return;
703 }
704 }
705 else
706 i2p::crypto::ChaCha20 (reply, SHORT_TUNNEL_BUILD_RECORD_SIZE, replyKey, nonce, reply);
707 reply += SHORT_TUNNEL_BUILD_RECORD_SIZE;
708 }
709 // send reply
710 if (isEndpoint)
711 {
712 auto replyMsg = NewI2NPShortMessage ();
713 replyMsg->Concat (buf, len);
714 replyMsg->FillI2NPMessageHeader (eI2NPShortTunnelBuildReply, bufbe32toh (clearText + SHORT_REQUEST_RECORD_SEND_MSG_ID_OFFSET));
715 if (memcmp ((const uint8_t *)i2p::context.GetIdentHash (),
716 clearText + SHORT_REQUEST_RECORD_NEXT_IDENT_OFFSET, 32)) // reply IBGW is not local?
717 {
718 i2p::crypto::HKDF (noiseState.m_CK, nullptr, 0, "RGarlicKeyAndTag", noiseState.m_CK);
719 uint64_t tag;
720 memcpy (&tag, noiseState.m_CK, 8);
721 // we send it to reply tunnel
722 transports.SendMessage (clearText + SHORT_REQUEST_RECORD_NEXT_IDENT_OFFSET,
723 CreateTunnelGatewayMsg (bufbe32toh (clearText + SHORT_REQUEST_RECORD_NEXT_TUNNEL_OFFSET),
724 i2p::garlic::WrapECIESX25519Message (replyMsg, noiseState.m_CK + 32, tag)));
725 }
726 else
727 {
728 // IBGW is local
729 uint32_t tunnelID = bufbe32toh (clearText + SHORT_REQUEST_RECORD_NEXT_TUNNEL_OFFSET);
730 auto tunnel = i2p::tunnel::tunnels.GetTunnel (tunnelID);
731 if (tunnel)
732 tunnel->SendTunnelDataMsg (replyMsg);
733 else
734 LogPrint (eLogWarning, "I2NP: Tunnel ", tunnelID, " not found for short tunnel build reply");
735 }
736 }
737 else
738 transports.SendMessage (clearText + SHORT_REQUEST_RECORD_NEXT_IDENT_OFFSET,
739 CreateI2NPMessage (eI2NPShortTunnelBuild, buf, len,
740 bufbe32toh (clearText + SHORT_REQUEST_RECORD_SEND_MSG_ID_OFFSET)));
741 return;
742 }
743 record += SHORT_TUNNEL_BUILD_RECORD_SIZE;
744 }
745 }
746
585747 std::shared_ptr<I2NPMessage> CreateTunnelDataMsg (const uint8_t * buf)
586748 {
587 auto msg = NewI2NPTunnelMessage ();
749 auto msg = NewI2NPTunnelMessage (false);
588750 msg->Concat (buf, i2p::tunnel::TUNNEL_DATA_MSG_SIZE);
589751 msg->FillI2NPMessageHeader (eI2NPTunnelData);
590752 return msg;
592754
593755 std::shared_ptr<I2NPMessage> CreateTunnelDataMsg (uint32_t tunnelID, const uint8_t * payload)
594756 {
595 auto msg = NewI2NPTunnelMessage ();
757 auto msg = NewI2NPTunnelMessage (false);
596758 htobe32buf (msg->GetPayload (), tunnelID);
597759 msg->len += 4; // tunnelID
598760 msg->Concat (payload, i2p::tunnel::TUNNEL_DATA_MSG_SIZE - 4);
600762 return msg;
601763 }
602764
603 std::shared_ptr<I2NPMessage> CreateEmptyTunnelDataMsg ()
604 {
605 auto msg = NewI2NPTunnelMessage ();
765 std::shared_ptr<I2NPMessage> CreateEmptyTunnelDataMsg (bool endpoint)
766 {
767 auto msg = NewI2NPTunnelMessage (endpoint);
606768 msg->len += i2p::tunnel::TUNNEL_DATA_MSG_SIZE;
607769 return msg;
608770 }
696858 case eI2NPVariableTunnelBuild:
697859 HandleVariableTunnelBuildMsg (msgID, buf, size);
698860 break;
861 case eI2NPShortTunnelBuild:
862 HandleShortTunnelBuildMsg (msgID, buf, size);
863 break;
699864 case eI2NPVariableTunnelBuildReply:
700 HandleVariableTunnelBuildReplyMsg (msgID, buf, size);
865 HandleTunnelBuildReplyMsg (msgID, buf, size, false);
701866 break;
867 case eI2NPShortTunnelBuildReply:
868 HandleTunnelBuildReplyMsg (msgID, buf, size, true);
869 break;
702870 case eI2NPTunnelBuild:
703871 HandleTunnelBuildMsg (buf, size);
704872 break;
755923 case eI2NPVariableTunnelBuildReply:
756924 case eI2NPTunnelBuild:
757925 case eI2NPTunnelBuildReply:
926 case eI2NPShortTunnelBuild:
927 case eI2NPShortTunnelBuildReply:
758928 // forward to tunnel thread
759929 i2p::tunnel::tunnels.PostTunnelData (msg);
760930 break;
00 /*
1 * Copyright (c) 2013-2020, The PurpleI2P Project
1 * Copyright (c) 2013-2021, The PurpleI2P Project
22 *
33 * This file is part of Purple i2pd project and licensed under BSD3
44 *
5454
5555 // TunnelBuild
5656 const size_t TUNNEL_BUILD_RECORD_SIZE = 528;
57
57 const size_t SHORT_TUNNEL_BUILD_RECORD_SIZE = 218;
58
5859 //BuildRequestRecordClearText
5960 const size_t BUILD_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET = 0;
6061 const size_t BUILD_REQUEST_RECORD_OUR_IDENT_OFFSET = BUILD_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET + 4;
99100 // ECIES BuildResponseRecord
100101 const size_t ECIES_BUILD_RESPONSE_RECORD_OPTIONS_OFFSET = 0;
101102 const size_t ECIES_BUILD_RESPONSE_RECORD_RET_OFFSET = 511;
103
104 // ShortRequestRecordClearText
105 const size_t SHORT_REQUEST_RECORD_ENCRYPTED_OFFSET = 16;
106 const size_t SHORT_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET = 0;
107 const size_t SHORT_REQUEST_RECORD_NEXT_TUNNEL_OFFSET = SHORT_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET + 4;
108 const size_t SHORT_REQUEST_RECORD_NEXT_IDENT_OFFSET = SHORT_REQUEST_RECORD_NEXT_TUNNEL_OFFSET + 4;
109 const size_t SHORT_REQUEST_RECORD_FLAG_OFFSET = SHORT_REQUEST_RECORD_NEXT_IDENT_OFFSET + 32;
110 const size_t SHORT_REQUEST_RECORD_MORE_FLAGS_OFFSET = SHORT_REQUEST_RECORD_FLAG_OFFSET + 1;
111 const size_t SHORT_REQUEST_RECORD_LAYER_ENCRYPTION_TYPE = SHORT_REQUEST_RECORD_MORE_FLAGS_OFFSET + 2;
112 const size_t SHORT_REQUEST_RECORD_REQUEST_TIME_OFFSET = SHORT_REQUEST_RECORD_LAYER_ENCRYPTION_TYPE + 1;
113 const size_t SHORT_REQUEST_RECORD_REQUEST_EXPIRATION_OFFSET = SHORT_REQUEST_RECORD_REQUEST_TIME_OFFSET + 4;
114 const size_t SHORT_REQUEST_RECORD_SEND_MSG_ID_OFFSET = SHORT_REQUEST_RECORD_REQUEST_EXPIRATION_OFFSET + 4;
115 const size_t SHORT_REQUEST_RECORD_PADDING_OFFSET = SHORT_REQUEST_RECORD_SEND_MSG_ID_OFFSET + 4;
116 const size_t SHORT_REQUEST_RECORD_CLEAR_TEXT_SIZE = 154;
117
118 // ShortResponseRecord
119 const size_t SHORT_RESPONSE_RECORD_OPTIONS_OFFSET = 0;
120 const size_t SHORT_RESPONSE_RECORD_RET_OFFSET = 201;
102121
103122 enum I2NPMessageType
104123 {
114133 eI2NPTunnelBuild = 21,
115134 eI2NPTunnelBuildReply = 22,
116135 eI2NPVariableTunnelBuild = 23,
117 eI2NPVariableTunnelBuildReply = 24
136 eI2NPVariableTunnelBuildReply = 24,
137 eI2NPShortTunnelBuild = 25,
138 eI2NPShortTunnelBuildReply = 26
118139 };
119140
141 const uint8_t TUNNEL_BUILD_RECORD_GATEWAY_FLAG = 0x80;
142 const uint8_t TUNNEL_BUILD_RECORD_ENDPOINT_FLAG = 0x40;
120143 const int NUM_TUNNEL_BUILD_RECORDS = 8;
121144
122145 // DatabaseLookup flags
259282
260283 std::shared_ptr<I2NPMessage> NewI2NPMessage ();
261284 std::shared_ptr<I2NPMessage> NewI2NPShortMessage ();
262 std::shared_ptr<I2NPMessage> NewI2NPTunnelMessage ();
285 std::shared_ptr<I2NPMessage> NewI2NPTunnelMessage (bool endpoint);
263286 std::shared_ptr<I2NPMessage> NewI2NPMessage (size_t len);
264287
265288 std::shared_ptr<I2NPMessage> CreateI2NPMessage (I2NPMessageType msgType, const uint8_t * buf, size_t len, uint32_t replyMsgID = 0);
280303 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);
281304 bool IsRouterInfoMsg (std::shared_ptr<I2NPMessage> msg);
282305
283 bool HandleBuildRequestRecords (int num, uint8_t * records, uint8_t * clearText);
284 void HandleVariableTunnelBuildMsg (uint32_t replyMsgID, uint8_t * buf, size_t len);
285 void HandleVariableTunnelBuildReplyMsg (uint32_t replyMsgID, uint8_t * buf, size_t len);
286 void HandleTunnelBuildMsg (uint8_t * buf, size_t len);
287
288306 std::shared_ptr<I2NPMessage> CreateTunnelDataMsg (const uint8_t * buf);
289307 std::shared_ptr<I2NPMessage> CreateTunnelDataMsg (uint32_t tunnelID, const uint8_t * payload);
290 std::shared_ptr<I2NPMessage> CreateEmptyTunnelDataMsg ();
308 std::shared_ptr<I2NPMessage> CreateEmptyTunnelDataMsg (bool endpoint);
291309
292310 std::shared_ptr<I2NPMessage> CreateTunnelGatewayMsg (uint32_t tunnelID, const uint8_t * buf, size_t len);
293311 std::shared_ptr<I2NPMessage> CreateTunnelGatewayMsg (uint32_t tunnelID, I2NPMessageType msgType,
2121 #include "NTCP2.h"
2222 #include "HTTP.h"
2323 #include "util.h"
24
25 #ifdef __linux__
26 #include <linux/in6.h>
27 #endif
2428
2529 namespace i2p
2630 {
327331 m_SendMDCtx(nullptr), m_ReceiveMDCtx (nullptr),
328332 #endif
329333 m_NextReceivedLen (0), m_NextReceivedBuffer (nullptr), m_NextSendBuffer (nullptr),
330 m_ReceiveSequenceNumber (0), m_SendSequenceNumber (0), m_IsSending (false)
334 m_ReceiveSequenceNumber (0), m_SendSequenceNumber (0), m_IsSending (false),
335 m_NextPaddingSize (16)
331336 {
332337 if (in_RemoteRouter) // Alice
333338 {
10531058 size_t paddingSize = (msgLen*NTCP2_MAX_PADDING_RATIO)/100;
10541059 if (msgLen + paddingSize + 3 > NTCP2_UNENCRYPTED_FRAME_MAX_SIZE) paddingSize = NTCP2_UNENCRYPTED_FRAME_MAX_SIZE - msgLen -3;
10551060 if (paddingSize > len) paddingSize = len;
1056 if (paddingSize) paddingSize = rand () % paddingSize;
1061 if (paddingSize)
1062 {
1063 if (m_NextPaddingSize >= 16)
1064 {
1065 RAND_bytes ((uint8_t *)m_PaddingSizes, sizeof (m_PaddingSizes));
1066 m_NextPaddingSize = 0;
1067 }
1068 paddingSize = m_PaddingSizes[m_NextPaddingSize++] % paddingSize;
1069 }
10571070 buf[0] = eNTCP2BlkPadding; // blk
10581071 htobe16buf (buf + 1, paddingSize); // size
10591072 memset (buf + 3, 0, paddingSize);
11651178 if (!address) continue;
11661179 if (address->IsPublishedNTCP2 () && address->port)
11671180 {
1168 if (address->host.is_v4())
1181 if (address->IsV4())
11691182 {
11701183 try
11711184 {
11841197 auto conn = std::make_shared<NTCP2Session>(*this);
11851198 m_NTCP2Acceptor->async_accept(conn->GetSocket (), std::bind (&NTCP2Server::HandleAccept, this, conn, std::placeholders::_1));
11861199 }
1187 else if (address->host.is_v6() && (context.SupportsV6 () || context.SupportsMesh ()))
1200 else if (address->IsV6() && (context.SupportsV6 () || context.SupportsMesh ()))
11881201 {
11891202 m_NTCP2V6Acceptor.reset (new boost::asio::ip::tcp::acceptor (GetService ()));
11901203 try
11921205 m_NTCP2V6Acceptor->open (boost::asio::ip::tcp::v6());
11931206 m_NTCP2V6Acceptor->set_option (boost::asio::ip::v6_only (true));
11941207 m_NTCP2V6Acceptor->set_option (boost::asio::socket_base::reuse_address (true));
1208 #ifdef __linux__
1209 if (!m_Address6 && !m_YggdrasilAddress) // only if not binded to address
1210 {
1211 // Set preference to use public IPv6 address -- tested on linux, not works on windows, and not tested on others
1212 #if (BOOST_VERSION >= 105500)
1213 typedef boost::asio::detail::socket_option::integer<BOOST_ASIO_OS_DEF(IPPROTO_IPV6), IPV6_ADDR_PREFERENCES> ipv6PreferAddr;
1214 #else
1215 typedef boost::asio::detail::socket_option::integer<IPPROTO_IPV6, IPV6_ADDR_PREFERENCES> ipv6PreferAddr;
1216 #endif
1217 m_NTCP2V6Acceptor->set_option (ipv6PreferAddr(IPV6_PREFER_SRC_PUBLIC | IPV6_PREFER_SRC_HOME | IPV6_PREFER_SRC_NONCGA));
1218 }
1219 #endif
11951220 auto ep = boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v6(), address->port);
11961221 if (m_Address6 && !context.SupportsMesh ())
11971222 ep = boost::asio::ip::tcp::endpoint (m_Address6->address(), address->port);
217217 bool m_IsSending;
218218 std::list<std::shared_ptr<I2NPMessage> > m_SendQueue;
219219 uint64_t m_NextRouterInfoResendTime; // seconds since epoch
220
221 uint16_t m_PaddingSizes[16];
222 int m_NextPaddingSize;
220223 };
221224
222225 class NTCP2Server: private i2p::util::RunnableServiceWithWork
5858 Reseed ();
5959 else if (!GetRandomRouter (i2p::context.GetSharedRouterInfo (), false))
6060 Reseed (); // we don't have a router we can connect to. Trying to reseed
61
62 auto it = m_RouterInfos.find (i2p::context.GetIdentHash ());
63 if (it != m_RouterInfos.end ())
64 {
65 // remove own router
66 m_RouterInfos.erase (it);
67 m_Floodfills.remove (it->second);
68 }
69 // insert own router
70 m_RouterInfos.emplace (i2p::context.GetIdentHash (), i2p::context.GetSharedRouterInfo ());
71 if (i2p::context.IsFloodfill ())
72 m_Floodfills.push_back (i2p::context.GetSharedRouterInfo ());
6173
6274 i2p::config::GetOption("persist.profiles", m_PersistProfiles);
6375
161173 bool publish = false;
162174 if (m_PublishReplyToken)
163175 {
176 // next publishing attempt
164177 if (ts - lastPublish >= NETDB_PUBLISH_CONFIRMATION_TIMEOUT) publish = true;
165178 }
166179 else if (i2p::context.GetLastUpdateTime () > lastPublish ||
167 ts - lastPublish >= NETDB_PUBLISH_INTERVAL) publish = true;
180 ts - lastPublish >= NETDB_PUBLISH_INTERVAL)
181 {
182 // new publish
183 m_PublishExcluded.clear ();
184 if (i2p::context.IsFloodfill ())
185 m_PublishExcluded.insert (i2p::context.GetIdentHash ()); // do publish to ourselves
186 publish = true;
187 }
168188 if (publish) // update timestamp and publish
169189 {
170190 i2p::context.UpdateTimestamp (ts);
452472 bool NetDb::LoadRouterInfo (const std::string & path)
453473 {
454474 auto r = std::make_shared<RouterInfo>(path);
455 if (r->GetRouterIdentity () && !r->IsUnreachable () &&
456 (r->IsReachable () || !r->IsSSU (false) || m_LastLoad < r->GetTimestamp () + NETDB_INTRODUCEE_EXPIRATION_TIMEOUT*1000LL)) // 1 hour
457 {
475 if (r->GetRouterIdentity () && !r->IsUnreachable () && r->HasValidAddresses ())
476 {
458477 r->DeleteBuffer ();
459478 r->ClearProperties (); // properties are not used for regular routers
460 m_RouterInfos[r->GetIdentHash ()] = r;
461 if (r->IsFloodfill () && r->IsReachable ()) // floodfill must be reachable
462 m_Floodfills.push_back (r);
479 if (m_RouterInfos.emplace (r->GetIdentHash (), r).second)
480 {
481 if (r->IsFloodfill () && r->IsEligibleFloodfill ())
482 m_Floodfills.push_back (r);
483 }
463484 }
464485 else
465486 {
566587 expirationTimeout = i2p::context.IsFloodfill () ? NETDB_FLOODFILL_EXPIRATION_TIMEOUT*1000LL :
567588 NETDB_MIN_EXPIRATION_TIMEOUT*1000LL + (NETDB_MAX_EXPIRATION_TIMEOUT - NETDB_MIN_EXPIRATION_TIMEOUT)*1000LL*NETDB_MIN_ROUTERS/total;
568589
590 auto own = i2p::context.GetSharedRouterInfo ();
569591 for (auto& it: m_RouterInfos)
570592 {
593 if (it.second == own) continue; // skip own
571594 std::string ident = it.second->GetIdentHashBase64();
572595 std::string path = m_Storage.Path(ident);
573596 if (it.second->IsUpdated ())
9871010 {
9881011 uint64_t tag;
9891012 memcpy (&tag, excluded + 33, 8);
990 replyMsg = i2p::garlic::WrapECIESX25519AEADRatchetMessage (replyMsg, sessionKey, tag);
1013 replyMsg = i2p::garlic::WrapECIESX25519Message (replyMsg, sessionKey, tag);
9911014 }
9921015 else
9931016 {
11861209 (reverse ? compatibleWith->IsReachableFrom (*router) :
11871210 router->IsReachableFrom (*compatibleWith)) &&
11881211 (router->GetCaps () & RouterInfo::eHighBandwidth) &&
1212 #if defined(__x86_64__)
11891213 router->GetVersion () >= NETDB_MIN_HIGHBANDWIDTH_VERSION;
1214 #else
1215 router->GetIdentity ()->GetCryptoKeyType () == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD;
1216 #endif
11901217 });
11911218 }
11921219
11951222 {
11961223 if (m_RouterInfos.empty())
11971224 return 0;
1198 uint32_t ind = rand () % m_RouterInfos.size ();
1199 for (int j = 0; j < 2; j++)
1200 {
1201 uint32_t i = 0;
1202 std::unique_lock<std::mutex> l(m_RouterInfosMutex);
1203 for (const auto& it: m_RouterInfos)
1204 {
1205 if (i >= ind)
1206 {
1207 if (!it.second->IsUnreachable () && filter (it.second))
1208 return it.second;
1209 }
1210 else
1211 i++;
1212 }
1213 // we couldn't find anything, try second pass
1214 ind = 0;
1225 uint16_t inds[3];
1226 RAND_bytes ((uint8_t *)inds, sizeof (inds));
1227 std::unique_lock<std::mutex> l(m_RouterInfosMutex);
1228 inds[0] %= m_RouterInfos.size ();
1229 auto it = m_RouterInfos.begin ();
1230 std::advance (it, inds[0]);
1231 // try random router
1232 if (it != m_RouterInfos.end () && !it->second->IsUnreachable () && filter (it->second))
1233 return it->second;
1234 // try some routers around
1235 auto it1 = m_RouterInfos.begin ();
1236 if (inds[0])
1237 {
1238 // before
1239 inds[1] %= inds[0];
1240 std::advance (it1, (inds[1] + inds[0])/2);
1241 }
1242 else
1243 it1 = it;
1244 auto it2 = it;
1245 if (inds[0] < m_RouterInfos.size () - 1)
1246 {
1247 // after
1248 inds[2] %= (m_RouterInfos.size () - 1 - inds[0]); inds[2] /= 2;
1249 std::advance (it2, inds[2]);
1250 }
1251 // it1 - from, it2 - to
1252 it = it1;
1253 while (it != it2 && it != m_RouterInfos.end ())
1254 {
1255 if (!it->second->IsUnreachable () && filter (it->second))
1256 return it->second;
1257 it++;
1258 }
1259 // still not found, try from the begining
1260 it = m_RouterInfos.begin ();
1261 while (it != it1 && it != m_RouterInfos.end ())
1262 {
1263 if (!it->second->IsUnreachable () && filter (it->second))
1264 return it->second;
1265 it++;
1266 }
1267 // still not found, try to the begining
1268 it = it2;
1269 while (it != m_RouterInfos.end ())
1270 {
1271 if (!it->second->IsUnreachable () && filter (it->second))
1272 return it->second;
1273 it++;
12151274 }
12161275 return nullptr; // seems we have too few routers
12171276 }
4343 const int NETDB_PUBLISH_CONFIRMATION_TIMEOUT = 5; // in seconds
4444 const int NETDB_MAX_PUBLISH_EXCLUDED_FLOODFILLS = 15;
4545 const int NETDB_MIN_HIGHBANDWIDTH_VERSION = MAKE_VERSION_NUMBER(0, 9, 36); // 0.9.36
46 const int NETDB_MIN_FLOODFILL_VERSION = MAKE_VERSION_NUMBER(0, 9, 28); // 0.9.28
46 const int NETDB_MIN_FLOODFILL_VERSION = MAKE_VERSION_NUMBER(0, 9, 38); // 0.9.38
47 const int NETDB_MIN_SHORT_TUNNEL_BUILD_VERSION = MAKE_VERSION_NUMBER(0, 9, 51); // 0.9.51
4748
4849 /** function for visiting a leaseset stored in a floodfill */
4950 typedef std::function<void(const IdentHash, std::shared_ptr<LeaseSet>)> LeaseSetVisitor;
496496
497497 void Reseeder::LoadCertificates ()
498498 {
499 std::string certDir = i2p::fs::DataDirPath("certificates", "reseed");
499 std::string certDir = i2p::fs::GetCertsDir() + i2p::fs::dirSep + "reseed";
500
500501 std::vector<std::string> files;
501502 int numCertificates = 0;
502503
4444 UpdateRouterInfo ();
4545 if (IsECIES ())
4646 {
47 auto initState = new i2p::crypto::NoiseSymmetricState ();
48 i2p::crypto::InitNoiseNState (*initState, GetIdentity ()->GetEncryptionPublicKey ());
49 m_InitialNoiseState.reset (initState);
50 m_ECIESSession = std::make_shared<i2p::garlic::RouterIncomingRatchetSession>(*initState);
47 i2p::crypto::InitNoiseNState (m_InitialNoiseState, GetIdentity ()->GetEncryptionPublicKey ());
48 m_ECIESSession = std::make_shared<i2p::garlic::RouterIncomingRatchetSession>(m_InitialNoiseState);
5149 }
5250 }
5351
471469 uint8_t caps = m_RouterInfo.GetCaps ();
472470 caps &= ~i2p::data::RouterInfo::eReachable;
473471 caps |= i2p::data::RouterInfo::eUnreachable;
474 caps &= ~i2p::data::RouterInfo::eFloodfill; // can't be floodfill
472 if (v6 || !SupportsV6 ())
473 caps &= ~i2p::data::RouterInfo::eFloodfill; // can't be floodfill
475474 m_RouterInfo.SetCaps (caps);
476475 }
477476 uint16_t port = 0;
485484 addr->ssu->introducers.clear ();
486485 port = addr->port;
487486 }
488 // unpiblish NTCP2 addreeses
487 // unpublish NTCP2 addreeses
489488 bool ntcp2; i2p::config::GetOption("ntcp2.enabled", ntcp2);
490489 if (ntcp2)
491490 PublishNTCP2Address (port, false, v4, v6, false);
492491 // update
492 m_RouterInfo.UpdateSupportedTransports ();
493493 UpdateRouterInfo ();
494494 }
495495
529529 }
530530 }
531531 // update
532 m_RouterInfo.UpdateSupportedTransports ();
532533 UpdateRouterInfo ();
533534 }
534535
567568 {
568569 bool ntcp2; i2p::config::GetOption("ntcp2.enabled", ntcp2);
569570 bool ntcp2Published; i2p::config::GetOption("ntcp2.published", ntcp2Published);
570 if (ntcp2 && ntcp2Published)
571 {
572 std::string ntcp2Host;
573 if (!i2p::config::IsDefault ("ntcp2.addressv6"))
574 i2p::config::GetOption ("ntcp2.addressv6", ntcp2Host);
571 if (ntcp2)
572 {
573 if (ntcp2Published)
574 {
575 std::string ntcp2Host;
576 if (!i2p::config::IsDefault ("ntcp2.addressv6"))
577 i2p::config::GetOption ("ntcp2.addressv6", ntcp2Host);
578 else
579 ntcp2Host = "::1";
580 uint16_t ntcp2Port; i2p::config::GetOption ("ntcp2.port", ntcp2Port);
581 if (!ntcp2Port) ntcp2Port = port;
582 m_RouterInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv, boost::asio::ip::address::from_string (ntcp2Host), ntcp2Port);
583 }
575584 else
576 ntcp2Host = "::1";
577 uint16_t ntcp2Port; i2p::config::GetOption ("ntcp2.port", ntcp2Port);
578 if (!ntcp2Port) ntcp2Port = port;
579 m_RouterInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv, boost::asio::ip::address::from_string (ntcp2Host), ntcp2Port);
585 m_RouterInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv, boost::asio::ip::address(), 0, i2p::data::RouterInfo::eV6);
580586 }
581587 }
582588 m_RouterInfo.EnableV6 ();
631637 m_RouterInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv, boost::asio::ip::address::from_string (host), ntcp2Port);
632638 }
633639 else
634 m_RouterInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv);
640 m_RouterInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv, boost::asio::ip::address(), 0, i2p::data::RouterInfo::eV4);
635641 }
636642 }
637643 m_RouterInfo.EnableV4 ();
678684 if (addr->IsPublishedNTCP2 ())
679685 {
680686 bool isYgg1 = i2p::util::net::IsYggdrasilAddress (addr->host);
681 if (addr->host.is_v6 () && ((isYgg && isYgg1) || (!isYgg && !isYgg1)))
687 if (addr->IsV6 () && ((isYgg && isYgg1) || (!isYgg && !isYgg1)))
682688 {
683689 if (addr->host != host)
684690 {
735741 }
736742 }
737743 std::shared_ptr<const i2p::data::IdentityEx> oldIdentity;
738 bool rekey = m_Keys.GetPublic ()->GetSigningKeyType () == i2p::data::SIGNING_KEY_TYPE_DSA_SHA1;
739 if (!rekey && m_Keys.GetPublic ()->GetCryptoKeyType () == i2p::data::CRYPTO_KEY_TYPE_ELGAMAL)
740 {
741 // rekey routers with bandwidth = L (or default) this time
742 bool isFloodfill; i2p::config::GetOption("floodfill", isFloodfill);
743 if (!isFloodfill) rekey = true;
744 }
745 if (rekey)
744 if (m_Keys.GetPublic ()->GetSigningKeyType () == i2p::data::SIGNING_KEY_TYPE_DSA_SHA1 ||
745 m_Keys.GetPublic ()->GetCryptoKeyType () == i2p::data::CRYPTO_KEY_TYPE_ELGAMAL)
746746 {
747747 // update keys
748748 LogPrint (eLogInfo, "Router: router keys are obsolete. Creating new");
820820 i2p::HandleI2NPMessage (CreateI2NPMessage (buf, GetI2NPMessageLength (buf, len)));
821821 }
822822
823 bool RouterContext::HandleCloveI2NPMessage (I2NPMessageType typeID, const uint8_t * payload, size_t len)
824 {
825 auto msg = CreateI2NPMessage (typeID, payload, len);
823 bool RouterContext::HandleCloveI2NPMessage (I2NPMessageType typeID, const uint8_t * payload, size_t len, uint32_t msgID)
824 {
825 auto msg = CreateI2NPMessage (typeID, payload, len, msgID);
826826 if (!msg) return false;
827827 i2p::HandleI2NPMessage (msg);
828828 return true;
842842 return;
843843 }
844844 buf += 4;
845 if (m_ECIESSession)
846 m_ECIESSession->HandleNextMessage (buf, len);
847 else
848 LogPrint (eLogError, "Router: Session is not set for ECIES router");
845 if (!HandleECIESx25519TagMessage (buf, len)) // try tag first
846 {
847 // then Noise_N one-time decryption
848 if (m_ECIESSession)
849 m_ECIESSession->HandleNextMessage (buf, len);
850 else
851 LogPrint (eLogError, "Router: Session is not set for ECIES router");
852 }
849853 }
850854 else
851855 i2p::garlic::GarlicDestination::ProcessGarlicMessage (msg);
880884
881885 bool RouterContext::DecryptTunnelBuildRecord (const uint8_t * encrypted, uint8_t * data)
882886 {
883 if (!m_TunnelDecryptor) return false;
884887 if (IsECIES ())
885 {
886 if (!m_InitialNoiseState) return false;
887 // m_InitialNoiseState is h = SHA256(h || hepk)
888 m_CurrentNoiseState.reset (new i2p::crypto::NoiseSymmetricState (*m_InitialNoiseState));
889 m_CurrentNoiseState->MixHash (encrypted, 32); // h = SHA256(h || sepk)
890 uint8_t sharedSecret[32];
891 if (!m_TunnelDecryptor->Decrypt (encrypted, sharedSecret, nullptr, false))
892 {
893 LogPrint (eLogWarning, "Router: Incorrect ephemeral public key");
894 return false;
895 }
896 m_CurrentNoiseState->MixKey (sharedSecret);
897 encrypted += 32;
898 uint8_t nonce[12];
899 memset (nonce, 0, 12);
900 if (!i2p::crypto::AEADChaCha20Poly1305 (encrypted, ECIES_BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE,
901 m_CurrentNoiseState->m_H, 32, m_CurrentNoiseState->m_CK + 32, nonce, data, ECIES_BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE, false)) // decrypt
902 {
903 LogPrint (eLogWarning, "Router: Tunnel record AEAD decryption failed");
904 return false;
905 }
906 m_CurrentNoiseState->MixHash (encrypted, ECIES_BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE + 16); // h = SHA256(h || ciphertext)
907 return true;
908 }
909 else
910 {
888 return DecryptECIESTunnelBuildRecord (encrypted, data, ECIES_BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE);
889 else
890 {
891 if (!m_TunnelDecryptor) return false;
911892 BN_CTX * ctx = BN_CTX_new ();
912893 bool success = m_TunnelDecryptor->Decrypt (encrypted, data, ctx, false);
913894 BN_CTX_free (ctx);
915896 }
916897 }
917898
899 bool RouterContext::DecryptECIESTunnelBuildRecord (const uint8_t * encrypted, uint8_t * data, size_t clearTextSize)
900 {
901 // m_InitialNoiseState is h = SHA256(h || hepk)
902 m_CurrentNoiseState = m_InitialNoiseState;
903 m_CurrentNoiseState.MixHash (encrypted, 32); // h = SHA256(h || sepk)
904 uint8_t sharedSecret[32];
905 if (!m_TunnelDecryptor->Decrypt (encrypted, sharedSecret, nullptr, false))
906 {
907 LogPrint (eLogWarning, "Router: Incorrect ephemeral public key");
908 return false;
909 }
910 m_CurrentNoiseState.MixKey (sharedSecret);
911 encrypted += 32;
912 uint8_t nonce[12];
913 memset (nonce, 0, 12);
914 if (!i2p::crypto::AEADChaCha20Poly1305 (encrypted, clearTextSize, m_CurrentNoiseState.m_H, 32,
915 m_CurrentNoiseState.m_CK + 32, nonce, data, clearTextSize, false)) // decrypt
916 {
917 LogPrint (eLogWarning, "Router: Tunnel record AEAD decryption failed");
918 return false;
919 }
920 m_CurrentNoiseState.MixHash (encrypted, clearTextSize + 16); // h = SHA256(h || ciphertext)
921 return true;
922 }
923
924 bool RouterContext::DecryptTunnelShortRequestRecord (const uint8_t * encrypted, uint8_t * data)
925 {
926 if (IsECIES ())
927 return DecryptECIESTunnelBuildRecord (encrypted, data, SHORT_REQUEST_RECORD_CLEAR_TEXT_SIZE);
928 else
929 {
930 LogPrint (eLogWarning, "Router: Can't decrypt short request record on non-ECIES router");
931 return false;
932 }
933 }
934
918935 i2p::crypto::X25519Keys& RouterContext::GetStaticKeys ()
919936 {
920937 if (!m_StaticKeys)
1717 #include "Identity.h"
1818 #include "RouterInfo.h"
1919 #include "Garlic.h"
20 #include "I18N_langs.h"
2021
2122 namespace i2p
2223 {
2324 namespace garlic
2425 {
2526 class RouterIncomingRatchetSession;
26 }
27 }
2728
2829 const char ROUTER_INFO[] = "router.info";
2930 const char ROUTER_KEYS[] = "router.keys";
3839 eRouterStatusError = 3,
3940 eRouterStatusUnknown = 4,
4041 eRouterStatusProxy = 5,
41 eRouterStatusMesh = 6
42 eRouterStatusMesh = 6
4243 };
4344
4445 enum RouterError
4849 eRouterErrorOffline = 2,
4950 eRouterErrorSymmetricNAT = 3
5051 };
51
52
5253 class RouterContext: public i2p::garlic::GarlicDestination
5354 {
5455 private:
6667 void Init ();
6768
6869 const i2p::data::PrivateKeys& GetPrivateKeys () const { return m_Keys; };
69 i2p::data::RouterInfo& GetRouterInfo () { return m_RouterInfo; };
70 std::shared_ptr<const i2p::data::RouterInfo> GetSharedRouterInfo () const
70 i2p::data::RouterInfo& GetRouterInfo () { return m_RouterInfo; };
71 std::shared_ptr<i2p::data::RouterInfo> GetSharedRouterInfo ()
7172 {
72 return std::shared_ptr<const i2p::data::RouterInfo> (&m_RouterInfo,
73 [](const i2p::data::RouterInfo *) {});
73 return std::shared_ptr<i2p::data::RouterInfo> (&m_RouterInfo,
74 [](i2p::data::RouterInfo *) {});
7475 }
7576 std::shared_ptr<i2p::garlic::GarlicDestination> GetSharedDestination ()
7677 {
9596 int GetNetID () const { return m_NetID; };
9697 void SetNetID (int netID) { m_NetID = netID; };
9798 bool DecryptTunnelBuildRecord (const uint8_t * encrypted, uint8_t * data);
98
99 bool DecryptTunnelShortRequestRecord (const uint8_t * encrypted, uint8_t * data);
100
99101 void UpdatePort (int port); // called from Daemon
100 void UpdateAddress (const boost::asio::ip::address& host); // called from SSU or Daemon
102 void UpdateAddress (const boost::asio::ip::address& host); // called from SSU or Daemon
101103 void PublishNTCP2Address (int port, bool publish, bool v4, bool v6, bool ygg);
102104 void UpdateNTCP2Address (bool enable);
103105 void RemoveNTCPAddress (bool v4only = true); // delete NTCP address for older routers. TODO: remove later
122124 void SetSupportsV4 (bool supportsV4);
123125 void SetSupportsMesh (bool supportsmesh, const boost::asio::ip::address_v6& host);
124126 bool IsECIES () const { return GetIdentity ()->GetCryptoKeyType () == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD; };
125 std::unique_ptr<i2p::crypto::NoiseSymmetricState>& GetCurrentNoiseState () { return m_CurrentNoiseState; };
126
127 i2p::crypto::NoiseSymmetricState& GetCurrentNoiseState () { return m_CurrentNoiseState; };
128
127129 void UpdateNTCP2V6Address (const boost::asio::ip::address& host); // called from Daemon. TODO: remove
128130 void UpdateStats ();
129131 void UpdateTimestamp (uint64_t ts); // in seconds, called from NetDb before publishing
130 void CleanupDestination (); // garlic destination
132 void CleanupDestination (); // garlic destination
131133
132134 // implements LocalDestination
133135 std::shared_ptr<const i2p::data::IdentityEx> GetIdentity () const { return m_Keys.GetPublic (); };
143145 void ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg);
144146 void ProcessDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg);
145147
148 // i18n
149 std::shared_ptr<const i2p::i18n::Locale> GetLanguage () { return m_Language; };
150 void SetLanguage (const std::shared_ptr<const i2p::i18n::Locale> language) { m_Language = language; };
151
146152 protected:
147153
148154 // implements GarlicDestination
149155 void HandleI2NPMessage (const uint8_t * buf, size_t len);
150 bool HandleCloveI2NPMessage (I2NPMessageType typeID, const uint8_t * payload, size_t len);
156 bool HandleCloveI2NPMessage (I2NPMessageType typeID, const uint8_t * payload, size_t len, uint32_t msgID);
151157
152158 private:
153159
158164 bool Load ();
159165 void SaveKeys ();
160166
167 bool DecryptECIESTunnelBuildRecord (const uint8_t * encrypted, uint8_t * data, size_t clearTextSize);
168
161169 private:
162170
163171 i2p::data::RouterInfo m_RouterInfo;
176184 std::unique_ptr<NTCP2PrivateKeys> m_NTCP2Keys;
177185 std::unique_ptr<i2p::crypto::X25519Keys> m_StaticKeys;
178186 // for ECIESx25519
179 std::unique_ptr<i2p::crypto::NoiseSymmetricState> m_InitialNoiseState, m_CurrentNoiseState;
187 i2p::crypto::NoiseSymmetricState m_InitialNoiseState, m_CurrentNoiseState;
188
189 // i18n
190 std::shared_ptr<const i2p::i18n::Locale> m_Language;
180191 };
181192
182193 extern RouterContext context;
3535
3636 RouterInfo::RouterInfo (const std::string& fullPath):
3737 m_FullPath (fullPath), m_IsUpdated (false), m_IsUnreachable (false),
38 m_SupportedTransports (0), m_Caps (0), m_Version (0)
38 m_SupportedTransports (0), m_ReachableTransports (0), m_Caps (0), m_Version (0)
3939 {
4040 m_Addresses = boost::make_shared<Addresses>(); // create empty list
4141 m_Buffer = new uint8_t[MAX_RI_BUFFER_SIZE];
4444
4545 RouterInfo::RouterInfo (const uint8_t * buf, int len):
4646 m_IsUpdated (true), m_IsUnreachable (false), m_SupportedTransports (0),
47 m_Caps (0), m_Version (0)
47 m_ReachableTransports (0), m_Caps (0), m_Version (0)
4848 {
4949 m_Addresses = boost::make_shared<Addresses>(); // create empty list
5050 if (len <= MAX_RI_BUFFER_SIZE)
8383 m_IsUpdated = true;
8484 m_IsUnreachable = false;
8585 m_SupportedTransports = 0;
86 m_ReachableTransports = 0;
8687 m_Caps = 0;
8788 // don't clean up m_Addresses, it will be replaced in ReadFromStream
8889 m_Properties.clear ();
304305 if (isHost)
305306 {
306307 if (address->host.is_v6 ())
307 supportedTransports |= i2p::util::net::IsYggdrasilAddress (address->host) ? eNTCP2V6Mesh : eNTCP2V6;
308 supportedTransports |= (i2p::util::net::IsYggdrasilAddress (address->host) ? eNTCP2V6Mesh : eNTCP2V6);
308309 else
309310 supportedTransports |= eNTCP2V4;
311 m_ReachableTransports |= supportedTransports;
310312 }
311313 else if (!address->published)
312314 {
340342 int numValid = 0;
341343 for (auto& it: address->ssu->introducers)
342344 {
343 if ((!it.iExp || ts <= it.iExp) && it.iPort > 0 &&
345 if (!it.iExp) it.iExp = m_Timestamp/1000 + NETDB_INTRODUCEE_EXPIRATION_TIMEOUT;
346 if (ts <= it.iExp && it.iPort > 0 &&
344347 ((it.iHost.is_v4 () && address->IsV4 ()) || (it.iHost.is_v6 () && address->IsV6 ())))
345348 numValid++;
346349 else
347350 it.iPort = 0;
348351 }
349 if (!numValid) address->ssu->introducers.resize (0);
352 if (numValid)
353 m_ReachableTransports |= supportedTransports;
354 else
355 address->ssu->introducers.resize (0);
350356 }
351357 else if (isHost && address->port)
358 {
352359 address->published = true;
360 m_ReachableTransports |= supportedTransports;
361 }
353362 }
354363 }
355364 if (supportedTransports)
545554 if (address.IsNTCP2 ())
546555 {
547556 WriteString ("NTCP2", s);
548 if (address.IsPublishedNTCP2 () && !address.host.is_unspecified ())
557 if (address.IsPublishedNTCP2 () && !address.host.is_unspecified () && address.port)
549558 isPublished = true;
550559 else
551560 {
831840 for (const auto& it: *m_Addresses) // don't insert same address twice
832841 if (*it == *addr) return;
833842 m_SupportedTransports |= addr->host.is_v6 () ? eSSUV6 : eSSUV4;
843 m_ReachableTransports |= addr->host.is_v6 () ? eSSUV6 : eSSUV4;
834844 m_Addresses->push_back(std::move(addr));
835845 }
836846
837 void RouterInfo::AddNTCP2Address (const uint8_t * staticKey, const uint8_t * iv, const boost::asio::ip::address& host, int port)
847 void RouterInfo::AddNTCP2Address (const uint8_t * staticKey, const uint8_t * iv,
848 const boost::asio::ip::address& host, int port, uint8_t caps)
838849 {
839850 auto addr = std::make_shared<Address>();
840851 addr->host = host;
841852 addr->port = port;
842853 addr->transportStyle = eTransportNTCP;
843 addr->caps = 0;
854 addr->caps = caps;
844855 addr->date = 0;
845856 addr->ntcp2.reset (new NTCP2Ext ());
846857 if (port) addr->published = true;
847858 memcpy (addr->ntcp2->staticKey, staticKey, 32);
848859 memcpy (addr->ntcp2->iv, iv, 16);
860 if (addr->IsV4 ())
861 {
862 m_SupportedTransports |= eNTCP2V4;
863 if (addr->published) m_ReachableTransports |= eNTCP2V4;
864 }
865 if (addr->IsV6 ())
866 {
867 m_SupportedTransports |= eNTCP2V6;
868 if (addr->published) m_ReachableTransports |= eNTCP2V6;
869 }
849870 m_Addresses->push_back(std::move(addr));
850871 }
851872
859880 for (auto& intro: addr->ssu->introducers)
860881 if (intro.iTag == introducer.iTag) return false; // already presented
861882 addr->ssu->introducers.push_back (introducer);
883 m_ReachableTransports |= (addr->IsV4 () ? eSSUV4 : eSSUV6);
862884 return true;
863885 }
864886 }
876898 if (boost::asio::ip::udp::endpoint (it->iHost, it->iPort) == e)
877899 {
878900 addr->ssu->introducers.erase (it);
901 if (addr->ssu->introducers.empty ())
902 m_ReachableTransports &= ~(addr->IsV4 () ? eSSUV4 : eSSUV6);
879903 return true;
880904 }
881905 }
959983 {
960984 if (!IsV6 ())
961985 {
962 m_SupportedTransports |= eSSUV6 | eNTCP2V6;
963986 uint8_t addressCaps = AddressCaps::eV6;
964987 if (IsV4 ()) addressCaps |= AddressCaps::eV4;
965988 SetUnreachableAddressesTransportCaps (addressCaps);
989 UpdateSupportedTransports ();
966990 }
967991 }
968992
970994 {
971995 if (!IsV4 ())
972996 {
973 m_SupportedTransports |= eSSUV4 | eNTCP2V4;
974997 uint8_t addressCaps = AddressCaps::eV4;
975998 if (IsV6 ()) addressCaps |= AddressCaps::eV6;
976999 SetUnreachableAddressesTransportCaps (addressCaps);
1000 UpdateSupportedTransports ();
9771001 }
9781002 }
9791003
9821006 {
9831007 if (IsV6 ())
9841008 {
985 m_SupportedTransports &= ~(eSSUV6 | eNTCP2V6);
9861009 for (auto it = m_Addresses->begin (); it != m_Addresses->end ();)
9871010 {
9881011 auto addr = *it;
989 addr->caps &= ~AddressCaps::eV6;
990 if (addr->host.is_v6 ())
991 it = m_Addresses->erase (it);
1012 if (addr->IsV6 ())
1013 {
1014 if (addr->IsV4 ())
1015 {
1016 addr->caps &= ~AddressCaps::eV6;
1017 ++it;
1018 }
1019 else
1020 it = m_Addresses->erase (it);
1021 }
9921022 else
9931023 ++it;
9941024 }
1025 UpdateSupportedTransports ();
9951026 }
9961027 }
9971028
9991030 {
10001031 if (IsV4 ())
10011032 {
1002 m_SupportedTransports &= ~(eSSUV4 | eNTCP2V4);
10031033 for (auto it = m_Addresses->begin (); it != m_Addresses->end ();)
10041034 {
10051035 auto addr = *it;
1006 addr->caps &= ~AddressCaps::eV4;
1007 if (addr->host.is_v4 ())
1008 it = m_Addresses->erase (it);
1036 if (addr->IsV4 ())
1037 {
1038 if (addr->IsV6 ())
1039 {
1040 addr->caps &= ~AddressCaps::eV4;
1041 ++it;
1042 }
1043 else
1044 it = m_Addresses->erase (it);
1045 }
10091046 else
10101047 ++it;
10111048 }
1049 UpdateSupportedTransports ();
10121050 }
10131051 }
10141052
10151053 void RouterInfo::EnableMesh ()
10161054 {
10171055 if (!IsMesh ())
1056 {
10181057 m_SupportedTransports |= eNTCP2V6Mesh;
1058 m_ReachableTransports |= eNTCP2V6Mesh;
1059 }
10191060 }
10201061
10211062 void RouterInfo::DisableMesh ()
10231064 if (IsMesh ())
10241065 {
10251066 m_SupportedTransports &= ~eNTCP2V6Mesh;
1067 m_ReachableTransports &= ~eNTCP2V6Mesh;
10261068 for (auto it = m_Addresses->begin (); it != m_Addresses->end ();)
10271069 {
10281070 auto addr = *it;
11211163
11221164 bool RouterInfo::IsEligibleFloodfill () const
11231165 {
1124 // floodfill must be reachable somehow, >= 0.9.28 and not DSA
1125 return (IsReachable () || (m_SupportedTransports & eSSUV4)) &&
1126 m_Version >= NETDB_MIN_FLOODFILL_VERSION &&
1166 // floodfill must be reachable by ipv4, >= 0.9.38 and not DSA
1167 return IsReachableBy (eNTCP2V4 | eSSUV4) && m_Version >= NETDB_MIN_FLOODFILL_VERSION &&
11271168 GetIdentity ()->GetSigningKeyType () != SIGNING_KEY_TYPE_DSA_SHA1;
11281169 }
11291170
11491190 });
11501191 }
11511192
1152 bool RouterInfo::IsReachableFrom (const RouterInfo& other) const
1153 {
1154 auto commonTransports = m_SupportedTransports & other.m_SupportedTransports;
1155 if (!commonTransports) return false;
1156 if (commonTransports & eNTCP2V6Mesh) return true;
1157 return (bool)GetAddress (
1158 [commonTransports](std::shared_ptr<const RouterInfo::Address> address)->bool
1159 {
1160 if (address->IsPublishedNTCP2 ())
1161 {
1162 if ((commonTransports & eNTCP2V4) && address->IsV4 ()) return true;
1163 if ((commonTransports & eNTCP2V6) && address->IsV6 ()) return true;
1164 }
1165 else if (address->IsReachableSSU ())
1166 {
1167 if ((commonTransports & eSSUV4) && address->IsV4 ()) return true;
1168 if ((commonTransports & eSSUV6) && address->IsV6 ()) return true;
1169 }
1170 return false;
1171 });
1172 }
1173
11741193 void RouterInfo::SetUnreachableAddressesTransportCaps (uint8_t transports)
11751194 {
11761195 for (auto& addr: *m_Addresses)
11771196 {
11781197 // TODO: implement SSU
1179 if (addr->transportStyle == eTransportNTCP && (!addr->IsPublishedNTCP2 () || addr->port))
1198 if (addr->transportStyle == eTransportNTCP && !addr->IsPublishedNTCP2 ())
11801199 {
11811200 addr->caps &= ~(eV4 | eV6);
11821201 addr->caps |= transports;
11831202 }
11841203 }
11851204 }
1205
1206 void RouterInfo::UpdateSupportedTransports ()
1207 {
1208 m_SupportedTransports = 0;
1209 m_ReachableTransports = 0;
1210 for (const auto& addr: *m_Addresses)
1211 {
1212 uint8_t transports = 0;
1213 if (addr->transportStyle == eTransportNTCP)
1214 {
1215 if (addr->IsV4 ()) transports |= eNTCP2V4;
1216 if (addr->IsV6 ())
1217 transports |= (i2p::util::net::IsYggdrasilAddress (addr->host) ? eNTCP2V6Mesh : eNTCP2V6);
1218 if (addr->IsPublishedNTCP2 ())
1219 m_ReachableTransports |= transports;
1220 }
1221 else if (addr->transportStyle == eTransportSSU)
1222 {
1223 if (addr->IsV4 ()) transports |= eSSUV4;
1224 if (addr->IsV6 ()) transports |= eSSUV6;
1225 if (addr->IsReachableSSU ())
1226 m_ReachableTransports |= transports;
1227 }
1228 m_SupportedTransports |= transports;
1229 }
1230 }
11861231 }
11871232 }
5858 {
5959 public:
6060
61 enum SupportedTranports
61 enum SupportedTransports
6262 {
6363 eNTCP2V4 = 0x01,
6464 eNTCP2V6 = 0x02,
146146
147147 bool IsNTCP2 () const { return (bool)ntcp2; };
148148 bool IsPublishedNTCP2 () const { return IsNTCP2 () && published; };
149 bool IsReachableSSU () const { return (bool)ssu && (!host.is_unspecified () || !ssu->introducers.empty ()); };
149 bool IsReachableSSU () const { return (bool)ssu && (published || !ssu->introducers.empty ()); };
150150 bool UsesIntroducer () const { return (bool)ssu && !ssu->introducers.empty (); };
151151
152152 bool IsIntroducer () const { return caps & eSSUIntroducer; };
178178 std::shared_ptr<const Address> GetYggdrasilAddress () const;
179179
180180 void AddSSUAddress (const char * host, int port, const uint8_t * key, int mtu = 0);
181 void AddNTCP2Address (const uint8_t * staticKey, const uint8_t * iv, const boost::asio::ip::address& host = boost::asio::ip::address(), int port = 0);
181 void AddNTCP2Address (const uint8_t * staticKey, const uint8_t * iv,
182 const boost::asio::ip::address& host = boost::asio::ip::address(), int port = 0, uint8_t caps = 0);
182183 bool AddIntroducer (const Introducer& introducer);
183184 bool RemoveIntroducer (const boost::asio::ip::udp::endpoint& e);
184185 void SetProperty (const std::string& key, const std::string& value); // called from RouterContext only
186187 std::string GetProperty (const std::string& key) const; // called from RouterContext only
187188 void ClearProperties () { m_Properties.clear (); };
188189 void SetUnreachableAddressesTransportCaps (uint8_t transports); // bitmask of AddressCaps
190 void UpdateSupportedTransports ();
189191 bool IsFloodfill () const { return m_Caps & Caps::eFloodfill; };
190192 bool IsReachable () const { return m_Caps & Caps::eReachable; };
191193 bool IsSSU (bool v4only = true) const;
202204 void EnableMesh ();
203205 void DisableMesh ();
204206 bool IsCompatible (const RouterInfo& other) const { return m_SupportedTransports & other.m_SupportedTransports; };
205 bool IsReachableFrom (const RouterInfo& other) const;
207 bool IsReachableFrom (const RouterInfo& other) const { return m_ReachableTransports & other.m_SupportedTransports; };
208 bool IsReachableBy (uint8_t transports) const { return m_ReachableTransports & transports; };
206209 bool HasValidAddresses () const { return m_SupportedTransports; };
207210 bool IsHidden () const { return m_Caps & eHidden; };
208211 bool IsHighBandwidth () const { return m_Caps & RouterInfo::eHighBandwidth; };
268271 boost::shared_ptr<Addresses> m_Addresses; // TODO: use std::shared_ptr and std::atomic_store for gcc >= 4.9
269272 std::map<std::string, std::string> m_Properties;
270273 bool m_IsUpdated, m_IsUnreachable;
271 uint8_t m_SupportedTransports, m_Caps;
274 uint8_t m_SupportedTransports, m_ReachableTransports, m_Caps;
272275 int m_Version;
273276 mutable std::shared_ptr<RouterProfile> m_Profile;
274277 };
1212 #include "NetDb.hpp"
1313 #include "SSU.h"
1414 #include "util.h"
15
16 #ifdef __linux__
17 #include <linux/in6.h>
18 #endif
1519
1620 #ifdef _WIN32
1721 #include <boost/winapi/error_codes.hpp>
6165 m_SocketV6.set_option (boost::asio::ip::v6_only (true));
6266 m_SocketV6.set_option (boost::asio::socket_base::receive_buffer_size (SSU_SOCKET_RECEIVE_BUFFER_SIZE));
6367 m_SocketV6.set_option (boost::asio::socket_base::send_buffer_size (SSU_SOCKET_SEND_BUFFER_SIZE));
68 #ifdef __linux__
69 if (m_EndpointV6.address() == boost::asio::ip::address().from_string("::")) // only if not binded to address
70 {
71 // Set preference to use public IPv6 address -- tested on linux, not works on windows, and not tested on others
72 #if (BOOST_VERSION >= 105500)
73 typedef boost::asio::detail::socket_option::integer<BOOST_ASIO_OS_DEF(IPPROTO_IPV6), IPV6_ADDR_PREFERENCES> ipv6PreferAddr;
74 #else
75 typedef boost::asio::detail::socket_option::integer<IPPROTO_IPV6, IPV6_ADDR_PREFERENCES> ipv6PreferAddr;
76 #endif
77 m_SocketV6.set_option (ipv6PreferAddr(IPV6_PREFER_SRC_PUBLIC | IPV6_PREFER_SRC_HOME | IPV6_PREFER_SRC_NONCGA));
78 }
79 #endif
6480 m_SocketV6.bind (m_EndpointV6);
6581 LogPrint (eLogInfo, "SSU: Start listening v6 port ", m_EndpointV6.port());
6682 }
382382 {
383383 // tell out peer to now assign relay tag
384384 flag = SSU_HEADER_EXTENDED_OPTIONS_INCLUDED;
385 *payload = 2; payload++; // 1 byte length
385 *payload = 2; payload++; // 1 byte length
386386 uint16_t flags = 0; // clear EXTENDED_OPTIONS_FLAG_REQUEST_RELAY_TAG
387387 htobe16buf (payload, flags);
388388 payload += 2;
10721072 LogPrint (eLogDebug, "SSU: peer test from Charlie. We are Bob");
10731073 auto session = m_Server.GetPeerTestSession (nonce); // session with Alice from PeerTest
10741074 if (session && session->m_State == eSessionStateEstablished)
1075 session->Send (PAYLOAD_TYPE_PEER_TEST, buf, len); // back to Alice
1075 {
1076 const auto& ep = session->GetRemoteEndpoint (); // Alice's endpoint as known to Bob
1077 session->SendPeerTest (nonce, ep.address (), ep.port (), introKey, false, true); // send back to Alice
1078 }
10761079 m_Server.RemovePeerTest (nonce); // nonce has been used
10771080 break;
10781081 }
10921095 if (port)
10931096 {
10941097 LogPrint (eLogDebug, "SSU: peer test from Bob. We are Charlie");
1095 m_Server.NewPeerTest (nonce, ePeerTestParticipantCharlie);
10961098 Send (PAYLOAD_TYPE_PEER_TEST, buf, len); // back to Bob
1097 SendPeerTest (nonce, addr, port, introKey); // to Alice with her address received from Bob
1099 if (!addr.is_unspecified () && !i2p::util::net::IsInReservedRange(addr))
1100 {
1101 m_Server.NewPeerTest (nonce, ePeerTestParticipantCharlie);
1102 SendPeerTest (nonce, addr, port, introKey); // to Alice with her address received from Bob
1103 }
10981104 }
10991105 else
11001106 {
103103
104104 void Stream::Terminate (bool deleteFromDestination) // shoudl be called from StreamingDestination::Stop only
105105 {
106 m_Status = eStreamStatusTerminated;
106107 m_AckSendTimer.cancel ();
107108 m_ReceiveTimer.cancel ();
108109 m_ResendTimer.cancel ();
275276 const uint8_t * optionData = packet->GetOptionData ();
276277 size_t optionSize = packet->GetOptionSize ();
277278 if (flags & PACKET_FLAG_DELAY_REQUESTED)
279 {
280 if (!m_IsAckSendScheduled)
281 {
282 uint16_t delayRequested = bufbe16toh (optionData);
283 if (delayRequested > 0 && delayRequested < m_RTT)
284 {
285 m_IsAckSendScheduled = true;
286 m_AckSendTimer.expires_from_now (boost::posix_time::milliseconds(delayRequested));
287 m_AckSendTimer.async_wait (std::bind (&Stream::HandleAckSendTimer,
288 shared_from_this (), std::placeholders::_1));
289 }
290 }
278291 optionData += 2;
292 }
279293
280294 if (flags & PACKET_FLAG_FROM_INCLUDED)
281295 {
792806 if (m_CurrentRemoteLease && ts < m_CurrentRemoteLease->endDate + i2p::data::LEASE_ENDDATE_THRESHOLD)
793807 {
794808 std::vector<i2p::tunnel::TunnelMessageBlock> msgs;
795 for (auto it: packets)
809 for (const auto& it: packets)
796810 {
797811 auto msg = m_RoutingSession->WrapSingleMessage (m_LocalDestination.CreateDataMessage (
798812 it->GetBuffer (), it->GetLength (), m_Port, !m_RoutingSession->IsRatchets ()));
843857
844858 void Stream::ScheduleResend ()
845859 {
846 m_ResendTimer.cancel ();
847 // check for invalid value
848 if (m_RTO <= 0) m_RTO = INITIAL_RTO;
849 m_ResendTimer.expires_from_now (boost::posix_time::milliseconds(m_RTO));
850 m_ResendTimer.async_wait (std::bind (&Stream::HandleResendTimer,
851 shared_from_this (), std::placeholders::_1));
860 if (m_Status != eStreamStatusTerminated)
861 {
862 m_ResendTimer.cancel ();
863 // check for invalid value
864 if (m_RTO <= 0) m_RTO = INITIAL_RTO;
865 m_ResendTimer.expires_from_now (boost::posix_time::milliseconds(m_RTO));
866 m_ResendTimer.async_wait (std::bind (&Stream::HandleResendTimer,
867 shared_from_this (), std::placeholders::_1));
868 }
852869 }
853870
854871 void Stream::HandleResendTimer (const boost::system::error_code& ecode)
10491066 it.second->Terminate (false); // we delete here
10501067 m_Streams.clear ();
10511068 m_IncomingStreams.clear ();
1069 m_LastStream = nullptr;
10521070 }
10531071 }
10541072
10571075 uint32_t sendStreamID = packet->GetSendStreamID ();
10581076 if (sendStreamID)
10591077 {
1060 auto it = m_Streams.find (sendStreamID);
1061 if (it != m_Streams.end ())
1062 it->second->HandleNextPacket (packet);
1078 if (!m_LastStream || sendStreamID != m_LastStream->GetRecvStreamID ())
1079 {
1080 auto it = m_Streams.find (sendStreamID);
1081 if (it != m_Streams.end ())
1082 m_LastStream = it->second;
1083 else
1084 m_LastStream = nullptr;
1085 }
1086 if (m_LastStream)
1087 m_LastStream->HandleNextPacket (packet);
10631088 else if (packet->IsEcho () && m_Owner->IsStreamingAnswerPings ())
10641089 {
10651090 // ping
11651190 {
11661191 auto s = std::make_shared<Stream> (m_Owner->GetService (), *this, remote, port);
11671192 std::unique_lock<std::mutex> l(m_StreamsMutex);
1168 m_Streams[s->GetRecvStreamID ()] = s;
1193 m_Streams.emplace (s->GetRecvStreamID (), s);
11691194 return s;
11701195 }
11711196
11731198 {
11741199 auto s = std::make_shared<Stream> (m_Owner->GetService (), *this);
11751200 std::unique_lock<std::mutex> l(m_StreamsMutex);
1176 m_Streams[s->GetRecvStreamID ()] = s;
1177 m_IncomingStreams[receiveStreamID] = s;
1201 m_Streams.emplace (s->GetRecvStreamID (), s);
1202 m_IncomingStreams.emplace (receiveStreamID, s);
11781203 return s;
11791204 }
11801205
11851210 std::unique_lock<std::mutex> l(m_StreamsMutex);
11861211 m_Streams.erase (stream->GetRecvStreamID ());
11871212 m_IncomingStreams.erase (stream->GetSendStreamID ());
1213 if (m_LastStream == stream) m_LastStream = nullptr;
11881214 }
11891215 }
11901216
00 /*
1 * Copyright (c) 2013-2020, The PurpleI2P Project
1 * Copyright (c) 2013-2021, The PurpleI2P Project
22 *
33 * This file is part of Purple i2pd project and licensed under BSD3
44 *
1010
1111 #include <inttypes.h>
1212 #include <string>
13 #include <map>
13 #include <unordered_map>
1414 #include <set>
1515 #include <queue>
1616 #include <functional>
151151 eStreamStatusOpen,
152152 eStreamStatusReset,
153153 eStreamStatusClosing,
154 eStreamStatusClosed
154 eStreamStatusClosed,
155 eStreamStatusTerminated
155156 };
156157
157158 class StreamingDestination;
296297 uint16_t m_LocalPort;
297298 bool m_Gzip; // gzip compression of data messages
298299 std::mutex m_StreamsMutex;
299 std::map<uint32_t, std::shared_ptr<Stream> > m_Streams; // sendStreamID->stream
300 std::map<uint32_t, std::shared_ptr<Stream> > m_IncomingStreams; // receiveStreamID->stream
300 std::unordered_map<uint32_t, std::shared_ptr<Stream> > m_Streams; // sendStreamID->stream
301 std::unordered_map<uint32_t, std::shared_ptr<Stream> > m_IncomingStreams; // receiveStreamID->stream
302 std::shared_ptr<Stream> m_LastStream;
301303 Acceptor m_Acceptor;
302304 std::list<std::shared_ptr<Stream> > m_PendingIncomingStreams;
303305 boost::asio::deadline_timer m_PendingIncomingTimer;
304 std::map<uint32_t, std::list<Packet *> > m_SavedPackets; // receiveStreamID->packets, arrived before SYN
306 std::unordered_map<uint32_t, std::list<Packet *> > m_SavedPackets; // receiveStreamID->packets, arrived before SYN
305307
306308 i2p::util::MemoryPool<Packet> m_PacketsPool;
307309 i2p::util::MemoryPool<I2NPMessageBuffer<I2NP_MAX_MESSAGE_SIZE> > m_I2NPMsgsPool;
00 /*
1 * Copyright (c) 2013-2020, The PurpleI2P Project
1 * Copyright (c) 2013-2021, The PurpleI2P Project
22 *
33 * This file is part of Purple i2pd project and licensed under BSD3
44 *
1515 #include <boost/algorithm/string.hpp>
1616 #include "Config.h"
1717 #include "Log.h"
18 #include "RouterContext.h"
1819 #include "I2PEndian.h"
1920 #include "Timestamp.h"
2021 #include "util.h"
5960 {
6061 LogPrint (eLogInfo, "Timestamp: NTP request to ", address);
6162 boost::asio::io_service service;
62 boost::asio::ip::udp::resolver::query query (boost::asio::ip::udp::v4 (), address, "ntp");
6363 boost::system::error_code ec;
64 auto it = boost::asio::ip::udp::resolver (service).resolve (query, ec);
65 if (!ec && it != boost::asio::ip::udp::resolver::iterator())
66 {
67 auto ep = (*it).endpoint (); // take first one
64 auto it = boost::asio::ip::udp::resolver (service).resolve (
65 boost::asio::ip::udp::resolver::query (address, "ntp"), ec);
66 if (!ec)
67 {
68 bool found = false;
69 boost::asio::ip::udp::resolver::iterator end;
70 boost::asio::ip::udp::endpoint ep;
71 while (it != end)
72 {
73 ep = *it;
74 if (!ep.address ().is_unspecified ())
75 {
76 if (ep.address ().is_v4 ())
77 {
78 if (i2p::context.SupportsV4 ()) found = true;
79 }
80 else if (ep.address ().is_v6 ())
81 {
82 if (i2p::util::net::IsYggdrasilAddress (ep.address ()))
83 {
84 if (i2p::context.SupportsMesh ()) found = true;
85 }
86 else if (i2p::context.SupportsV6 ()) found = true;
87 }
88 }
89 if (found) break;
90 it++;
91 }
92 if (!found)
93 {
94 LogPrint (eLogError, "Timestamp: can't find compatible address for ", address);
95 return;
96 }
97
6898 boost::asio::ip::udp::socket socket (service);
69 socket.open (boost::asio::ip::udp::v4 (), ec);
99 socket.open (ep.protocol (), ec);
70100 if (!ec)
71101 {
72102 uint8_t buf[48];// 48 bytes NTP request/response
102132 LogPrint (eLogError, "Timestamp: Couldn't open UDP socket");
103133 }
104134 else
105 LogPrint (eLogError, "Timestamp: Couldn't resove address ", address);
135 LogPrint (eLogError, "Timestamp: Couldn't resolve address ", address);
106136 }
107137
108138 NTPTimeSync::NTPTimeSync (): m_IsRunning (false), m_Timer (m_Service)
3838
3939 void TransitTunnelParticipant::HandleTunnelDataMsg (std::shared_ptr<const i2p::I2NPMessage> tunnelMsg)
4040 {
41 auto newMsg = CreateEmptyTunnelDataMsg ();
41 auto newMsg = CreateEmptyTunnelDataMsg (false);
4242 EncryptTunnelMsg (tunnelMsg, newMsg);
4343
4444 m_NumTransmittedBytes += tunnelMsg->GetLength ();
8686
8787 void TransitTunnelEndpoint::HandleTunnelDataMsg (std::shared_ptr<const i2p::I2NPMessage> tunnelMsg)
8888 {
89 auto newMsg = CreateEmptyTunnelDataMsg ();
89 auto newMsg = CreateEmptyTunnelDataMsg (true);
9090 EncryptTunnelMsg (tunnelMsg, newMsg);
9191
9292 LogPrint (eLogDebug, "TransitTunnel: handle msg for endpoint ", GetTunnelID ());
400400 try
401401 {
402402 auto r = netdb.FindRouter (ident);
403 if (!r || r->IsUnreachable () || !r->IsCompatible (i2p::context.GetRouterInfo ())) return;
403 if (r && (r->IsUnreachable () || !r->IsReachableFrom (i2p::context.GetRouterInfo ()))) return; // router found but non-reachable
404404 {
405405 std::unique_lock<std::mutex> l(m_PeersMutex);
406406 it = m_Peers.insert (std::pair<i2p::data::IdentHash, Peer>(ident, { 0, r, {},
446446 std::shared_ptr<const RouterInfo::Address> address;
447447 if (!peer.numAttempts) // NTCP2 ipv6
448448 {
449 if (context.GetRouterInfo ().IsNTCP2V6 () && peer.router->IsNTCP2V6 ())
449 if (context.GetRouterInfo ().IsNTCP2V6 () && peer.router->IsReachableBy (RouterInfo::eNTCP2V6))
450450 {
451451 address = peer.router->GetPublishedNTCP2V6Address ();
452452 if (address && m_CheckReserved && i2p::util::net::IsInReservedRange(address->host))
456456 }
457457 if (!address && peer.numAttempts == 1) // NTCP2 ipv4
458458 {
459 if (context.GetRouterInfo ().IsNTCP2 (true) && peer.router->IsNTCP2 (true) && !peer.router->IsUnreachable ())
459 if (context.GetRouterInfo ().IsNTCP2 (true) && peer.router->IsReachableBy (RouterInfo::eNTCP2V4))
460460 {
461461 address = peer.router->GetPublishedNTCP2V4Address ();
462462 if (address && m_CheckReserved && i2p::util::net::IsInReservedRange(address->host))
484484 std::shared_ptr<const RouterInfo::Address> address;
485485 if (peer.numAttempts == 2) // SSU ipv6
486486 {
487 if (context.GetRouterInfo ().IsSSUV6 () && peer.router->IsSSUV6 ())
487 if (context.GetRouterInfo ().IsSSUV6 () && peer.router->IsReachableBy (RouterInfo::eSSUV6))
488488 {
489489 address = peer.router->GetSSUV6Address ();
490490 if (address && m_CheckReserved && i2p::util::net::IsInReservedRange(address->host))
494494 }
495495 if (!address && peer.numAttempts == 3) // SSU ipv4
496496 {
497 if (context.GetRouterInfo ().IsSSU (true) && peer.router->IsSSU (true))
497 if (context.GetRouterInfo ().IsSSU (true) && peer.router->IsReachableBy (RouterInfo::eSSUV4))
498498 {
499499 address = peer.router->GetSSUAddress (true);
500500 if (address && m_CheckReserved && i2p::util::net::IsInReservedRange(address->host))
2222 #include "Tunnel.h"
2323 #include "TunnelPool.h"
2424 #include "util.h"
25 #include "ECIESX25519AEADRatchetSession.h"
2526
2627 namespace i2p
2728 {
4142 void Tunnel::Build (uint32_t replyMsgID, std::shared_ptr<OutboundTunnel> outboundTunnel)
4243 {
4344 auto numHops = m_Config->GetNumHops ();
44 int numRecords = numHops <= STANDARD_NUM_RECORDS ? STANDARD_NUM_RECORDS : MAX_NUM_RECORDS;
45 const int numRecords = numHops <= STANDARD_NUM_RECORDS ? STANDARD_NUM_RECORDS : MAX_NUM_RECORDS;
4546 auto msg = numRecords <= STANDARD_NUM_RECORDS ? NewI2NPShortMessage () : NewI2NPMessage ();
4647 *msg->GetPayload () = numRecords;
47 msg->len += numRecords*TUNNEL_BUILD_RECORD_SIZE + 1;
48 const size_t recordSize = m_Config->IsShort () ? SHORT_TUNNEL_BUILD_RECORD_SIZE : TUNNEL_BUILD_RECORD_SIZE;
49 msg->len += numRecords*recordSize + 1;
4850 // shuffle records
4951 std::vector<int> recordIndicies;
5052 for (int i = 0; i < numRecords; i++) recordIndicies.push_back(i);
5456 uint8_t * records = msg->GetPayload () + 1;
5557 TunnelHopConfig * hop = m_Config->GetFirstHop ();
5658 int i = 0;
57 BN_CTX * ctx = BN_CTX_new ();
5859 while (hop)
5960 {
6061 uint32_t msgID;
6263 RAND_bytes ((uint8_t *)&msgID, 4);
6364 else
6465 msgID = replyMsgID;
65 int idx = recordIndicies[i];
66 hop->CreateBuildRequestRecord (records + idx*TUNNEL_BUILD_RECORD_SIZE, msgID, ctx);
67 hop->recordIndex = idx;
68 i++;
66 hop->recordIndex = recordIndicies[i]; i++;
67 hop->CreateBuildRequestRecord (records, msgID);
6968 hop = hop->next;
7069 }
71 BN_CTX_free (ctx);
7270 // fill up fake records with random data
7371 for (int i = numHops; i < numRecords; i++)
7472 {
7573 int idx = recordIndicies[i];
76 RAND_bytes (records + idx*TUNNEL_BUILD_RECORD_SIZE, TUNNEL_BUILD_RECORD_SIZE);
74 RAND_bytes (records + idx*recordSize, recordSize);
7775 }
7876
7977 // decrypt real records
80 i2p::crypto::CBCDecryption decryption;
8178 hop = m_Config->GetLastHop ()->prev;
8279 while (hop)
8380 {
84 decryption.SetKey (hop->replyKey);
8581 // decrypt records after current hop
8682 TunnelHopConfig * hop1 = hop->next;
8783 while (hop1)
8884 {
89 decryption.SetIV (hop->replyIV);
90 uint8_t * record = records + hop1->recordIndex*TUNNEL_BUILD_RECORD_SIZE;
91 decryption.Decrypt(record, TUNNEL_BUILD_RECORD_SIZE, record);
85 hop->DecryptRecord (records, hop1->recordIndex);
9286 hop1 = hop1->next;
9387 }
9488 hop = hop->prev;
9589 }
96 msg->FillI2NPMessageHeader (eI2NPVariableTunnelBuild);
90 msg->FillI2NPMessageHeader (m_Config->IsShort () ? eI2NPShortTunnelBuild : eI2NPVariableTunnelBuild);
9791
9892 // send message
9993 if (outboundTunnel)
94 {
95 if (m_Config->IsShort ())
96 {
97 auto ident = m_Config->GetFirstHop () ? m_Config->GetFirstHop ()->ident : nullptr;
98 if (ident && ident->GetIdentHash () != outboundTunnel->GetNextIdentHash ()) // don't encrypt if IBGW = OBEP
99 {
100 auto msg1 = i2p::garlic::WrapECIESX25519MessageForRouter (msg, ident->GetEncryptionPublicKey ());
101 if (msg1) msg = msg1;
102 }
103 }
100104 outboundTunnel->SendTunnelDataMsg (GetNextIdentHash (), 0, msg);
105 }
101106 else
107 {
108 if (m_Config->IsShort () && m_Config->GetLastHop () &&
109 m_Config->GetLastHop ()->ident->GetIdentHash () != m_Config->GetLastHop ()->nextIdent)
110 {
111 // add garlic key/tag for reply
112 uint8_t key[32];
113 uint64_t tag = m_Config->GetLastHop ()->GetGarlicKey (key);
114 if (m_Pool && m_Pool->GetLocalDestination ())
115 m_Pool->GetLocalDestination ()->AddECIESx25519Key (key, tag);
116 else
117 i2p::context.AddECIESx25519Key (key, tag);
118 }
102119 i2p::transport::transports.SendMessage (GetNextIdentHash (), msg);
120 }
103121 }
104122
105123 bool Tunnel::HandleTunnelBuildResponse (uint8_t * msg, size_t len)
106124 {
107125 LogPrint (eLogDebug, "Tunnel: TunnelBuildResponse ", (int)msg[0], " records.");
108126
109 i2p::crypto::CBCDecryption decryption;
110127 TunnelHopConfig * hop = m_Config->GetLastHop ();
111128 while (hop)
112129 {
113 decryption.SetKey (hop->replyKey);
114 // decrypt records before and current hop
115 TunnelHopConfig * hop1 = hop;
130 // decrypt current hop
131 if (hop->recordIndex >= 0 && hop->recordIndex < msg[0])
132 {
133 if (!hop->DecryptBuildResponseRecord (msg + 1))
134 return false;
135 }
136 else
137 {
138 LogPrint (eLogWarning, "Tunnel: hop index ", hop->recordIndex, " is out of range");
139 return false;
140 }
141
142 // decrypt records before current hop
143 TunnelHopConfig * hop1 = hop->prev;
116144 while (hop1)
117145 {
118146 auto idx = hop1->recordIndex;
119147 if (idx >= 0 && idx < msg[0])
120 {
121 uint8_t * record = msg + 1 + idx*TUNNEL_BUILD_RECORD_SIZE;
122 if (hop1 == hop && hop1->IsECIES ())
123 {
124 uint8_t nonce[12];
125 memset (nonce, 0, 12);
126 if (!i2p::crypto::AEADChaCha20Poly1305 (record, TUNNEL_BUILD_RECORD_SIZE - 16,
127 hop->m_H, 32, hop->m_CK, nonce, record, TUNNEL_BUILD_RECORD_SIZE - 16, false)) // decrypt
128 {
129 LogPrint (eLogWarning, "Tunnel: Response AEAD decryption failed");
130 return false;
131 }
132 }
133 else
134 {
135 decryption.SetIV (hop->replyIV);
136 decryption.Decrypt(record, TUNNEL_BUILD_RECORD_SIZE, record);
137 }
138 }
148 hop->DecryptRecord (msg + 1, idx);
139149 else
140150 LogPrint (eLogWarning, "Tunnel: hop index ", idx, " is out of range");
141151 hop1 = hop1->prev;
147157 hop = m_Config->GetFirstHop ();
148158 while (hop)
149159 {
150 const uint8_t * record = msg + 1 + hop->recordIndex*TUNNEL_BUILD_RECORD_SIZE;
151 uint8_t ret = record[hop->IsECIES () ? ECIES_BUILD_RESPONSE_RECORD_RET_OFFSET : BUILD_RESPONSE_RECORD_RET_OFFSET];
160 uint8_t ret = hop->GetRetCode (msg + 1);
152161 LogPrint (eLogDebug, "Tunnel: Build response ret code=", (int)ret);
153162 auto profile = i2p::data::netdb.FindRouterProfile (hop->ident->GetIdentHash ());
154163 if (profile)
233242 void InboundTunnel::HandleTunnelDataMsg (std::shared_ptr<const I2NPMessage> msg)
234243 {
235244 if (IsFailed ()) SetState (eTunnelStateEstablished); // incoming messages means a tunnel is alive
236 auto newMsg = CreateEmptyTunnelDataMsg ();
245 auto newMsg = CreateEmptyTunnelDataMsg (true);
237246 EncryptTunnelMsg (msg, newMsg);
238247 newMsg->from = shared_from_this ();
239248 m_Endpoint.HandleDecryptedTunnelDataMsg (newMsg);
318327 for (auto& msg : msgs)
319328 {
320329 if (!msg.data) continue;
330 m_NumSentBytes += msg.data->GetLength ();
321331 switch (msg.deliveryType)
322332 {
323333 case eDeliveryTypeLocal:
324 i2p::HandleI2NPMessage (msg.data);
334 HandleI2NPMessage (msg.data);
325335 break;
326336 case eDeliveryTypeTunnel:
327337 i2p::transport::transports.SendMessage (msg.hash, i2p::CreateTunnelGatewayMsg (msg.tunnelID, msg.data));
516526 }
517527 case eI2NPVariableTunnelBuild:
518528 case eI2NPVariableTunnelBuildReply:
529 case eI2NPShortTunnelBuild:
530 case eI2NPShortTunnelBuildReply:
519531 case eI2NPTunnelBuild:
520 case eI2NPTunnelBuildReply:
532 case eI2NPTunnelBuildReply:
521533 HandleI2NPMessage (msg->GetBuffer (), msg->GetLength ());
522534 break;
523535 default:
702714 LogPrint (eLogDebug, "Tunnel: creating one hop outbound tunnel");
703715 CreateTunnel<OutboundTunnel> (
704716 std::make_shared<TunnelConfig> (std::vector<std::shared_ptr<const i2p::data::IdentityEx> > { router->GetRouterIdentity () },
705 inboundTunnel->GetNextTunnelID (), inboundTunnel->GetNextIdentHash ())
717 inboundTunnel->GetNextTunnelID (), inboundTunnel->GetNextIdentHash ()), nullptr
706718 );
707719 }
708720 }
751763 if (m_InboundTunnels.empty ())
752764 {
753765 LogPrint (eLogDebug, "Tunnel: Creating zero hops inbound tunnel");
754 CreateZeroHopsInboundTunnel ();
755 CreateZeroHopsOutboundTunnel ();
766 CreateZeroHopsInboundTunnel (nullptr);
767 CreateZeroHopsOutboundTunnel (nullptr);
756768 if (!m_ExploratoryPool)
757769 {
758770 int ibLen; i2p::config::GetOption("exploratory.inbound.length", ibLen);
778790 }
779791 LogPrint (eLogDebug, "Tunnel: creating one hop inbound tunnel");
780792 CreateTunnel<InboundTunnel> (
781 std::make_shared<TunnelConfig> (std::vector<std::shared_ptr<const i2p::data::IdentityEx> > { router->GetRouterIdentity () })
793 std::make_shared<TunnelConfig> (std::vector<std::shared_ptr<const i2p::data::IdentityEx> > { router->GetRouterIdentity () }), nullptr
782794 );
783795 }
784796 }
824836 }
825837
826838 template<class TTunnel>
827 std::shared_ptr<TTunnel> Tunnels::CreateTunnel (std::shared_ptr<TunnelConfig> config, std::shared_ptr<OutboundTunnel> outboundTunnel)
839 std::shared_ptr<TTunnel> Tunnels::CreateTunnel (std::shared_ptr<TunnelConfig> config,
840 std::shared_ptr<TunnelPool> pool, std::shared_ptr<OutboundTunnel> outboundTunnel)
828841 {
829842 auto newTunnel = std::make_shared<TTunnel> (config);
843 newTunnel->SetTunnelPool (pool);
830844 uint32_t replyMsgID;
831845 RAND_bytes ((uint8_t *)&replyMsgID, 4);
832846 AddPendingTunnel (replyMsgID, newTunnel);
834848 return newTunnel;
835849 }
836850
837 std::shared_ptr<InboundTunnel> Tunnels::CreateInboundTunnel (std::shared_ptr<TunnelConfig> config, std::shared_ptr<OutboundTunnel> outboundTunnel)
851 std::shared_ptr<InboundTunnel> Tunnels::CreateInboundTunnel (std::shared_ptr<TunnelConfig> config,
852 std::shared_ptr<TunnelPool> pool, std::shared_ptr<OutboundTunnel> outboundTunnel)
838853 {
839854 if (config)
840 return CreateTunnel<InboundTunnel>(config, outboundTunnel);
855 return CreateTunnel<InboundTunnel>(config, pool, outboundTunnel);
841856 else
842 return CreateZeroHopsInboundTunnel ();
843 }
844
845 std::shared_ptr<OutboundTunnel> Tunnels::CreateOutboundTunnel (std::shared_ptr<TunnelConfig> config)
857 return CreateZeroHopsInboundTunnel (pool);
858 }
859
860 std::shared_ptr<OutboundTunnel> Tunnels::CreateOutboundTunnel (std::shared_ptr<TunnelConfig> config, std::shared_ptr<TunnelPool> pool)
846861 {
847862 if (config)
848 return CreateTunnel<OutboundTunnel>(config);
863 return CreateTunnel<OutboundTunnel>(config, pool);
849864 else
850 return CreateZeroHopsOutboundTunnel ();
865 return CreateZeroHopsOutboundTunnel (pool);
851866 }
852867
853868 void Tunnels::AddPendingTunnel (uint32_t replyMsgID, std::shared_ptr<InboundTunnel> tunnel)
881896 {
882897 // build symmetric outbound tunnel
883898 CreateTunnel<OutboundTunnel> (std::make_shared<TunnelConfig>(newTunnel->GetInvertedPeers (),
884 newTunnel->GetNextTunnelID (), newTunnel->GetNextIdentHash ()),
899 newTunnel->GetNextTunnelID (), newTunnel->GetNextIdentHash ()), nullptr,
885900 GetNextOutboundTunnel ());
886901 }
887902 else
897912 }
898913
899914
900 std::shared_ptr<ZeroHopsInboundTunnel> Tunnels::CreateZeroHopsInboundTunnel ()
915 std::shared_ptr<ZeroHopsInboundTunnel> Tunnels::CreateZeroHopsInboundTunnel (std::shared_ptr<TunnelPool> pool)
901916 {
902917 auto inboundTunnel = std::make_shared<ZeroHopsInboundTunnel> ();
918 inboundTunnel->SetTunnelPool (pool);
903919 inboundTunnel->SetState (eTunnelStateEstablished);
904920 m_InboundTunnels.push_back (inboundTunnel);
905921 m_Tunnels[inboundTunnel->GetTunnelID ()] = inboundTunnel;
906922 return inboundTunnel;
907923 }
908924
909 std::shared_ptr<ZeroHopsOutboundTunnel> Tunnels::CreateZeroHopsOutboundTunnel ()
925 std::shared_ptr<ZeroHopsOutboundTunnel> Tunnels::CreateZeroHopsOutboundTunnel (std::shared_ptr<TunnelPool> pool)
910926 {
911927 auto outboundTunnel = std::make_shared<ZeroHopsOutboundTunnel> ();
928 outboundTunnel->SetTunnelPool (pool);
912929 outboundTunnel->SetState (eTunnelStateEstablished);
913930 m_OutboundTunnels.push_back (outboundTunnel);
914931 // we don't insert into m_Tunnels
3737 const int TUNNEL_CREATION_TIMEOUT = 30; // 30 seconds
3838 const int STANDARD_NUM_RECORDS = 4; // in VariableTunnelBuild message
3939 const int MAX_NUM_RECORDS = 8;
40 const int HIGH_LATENCY_PER_HOP = 250; // in milliseconds
4041
4142 enum TunnelState
4243 {
9798 bool LatencyFitsRange(uint64_t lowerbound, uint64_t upperbound) const;
9899
99100 bool LatencyIsKnown() const { return m_Latency > 0; }
101 bool IsSlow () const { return LatencyIsKnown() && (int)m_Latency > HIGH_LATENCY_PER_HOP*GetNumHops (); }
102
100103 protected:
101104
102105 void PrintHops (std::stringstream& s) const;
201204 void AddTransitTunnel (std::shared_ptr<TransitTunnel> tunnel);
202205 void AddOutboundTunnel (std::shared_ptr<OutboundTunnel> newTunnel);
203206 void AddInboundTunnel (std::shared_ptr<InboundTunnel> newTunnel);
204 std::shared_ptr<InboundTunnel> CreateInboundTunnel (std::shared_ptr<TunnelConfig> config, std::shared_ptr<OutboundTunnel> outboundTunnel);
205 std::shared_ptr<OutboundTunnel> CreateOutboundTunnel (std::shared_ptr<TunnelConfig> config);
207 std::shared_ptr<InboundTunnel> CreateInboundTunnel (std::shared_ptr<TunnelConfig> config, std::shared_ptr<TunnelPool> pool, std::shared_ptr<OutboundTunnel> outboundTunnel);
208 std::shared_ptr<OutboundTunnel> CreateOutboundTunnel (std::shared_ptr<TunnelConfig> config, std::shared_ptr<TunnelPool> pool);
206209 void PostTunnelData (std::shared_ptr<I2NPMessage> msg);
207210 void PostTunnelData (const std::vector<std::shared_ptr<I2NPMessage> >& msgs);
208211 void AddPendingTunnel (uint32_t replyMsgID, std::shared_ptr<InboundTunnel> tunnel);
215218 private:
216219
217220 template<class TTunnel>
218 std::shared_ptr<TTunnel> CreateTunnel (std::shared_ptr<TunnelConfig> config, std::shared_ptr<OutboundTunnel> outboundTunnel = nullptr);
221 std::shared_ptr<TTunnel> CreateTunnel (std::shared_ptr<TunnelConfig> config,
222 std::shared_ptr<TunnelPool> pool, std::shared_ptr<OutboundTunnel> outboundTunnel = nullptr);
219223
220224 template<class TTunnel>
221225 std::shared_ptr<TTunnel> GetPendingTunnel (uint32_t replyMsgID, const std::map<uint32_t, std::shared_ptr<TTunnel> >& pendingTunnels);
232236 void ManagePendingTunnels (PendingTunnels& pendingTunnels);
233237 void ManageTunnelPools (uint64_t ts);
234238
235 std::shared_ptr<ZeroHopsInboundTunnel> CreateZeroHopsInboundTunnel ();
236 std::shared_ptr<ZeroHopsOutboundTunnel> CreateZeroHopsOutboundTunnel ();
239 std::shared_ptr<ZeroHopsInboundTunnel> CreateZeroHopsInboundTunnel (std::shared_ptr<TunnelPool> pool);
240 std::shared_ptr<ZeroHopsOutboundTunnel> CreateZeroHopsOutboundTunnel (std::shared_ptr<TunnelPool> pool);
237241
238242 private:
239243
00 /*
1 * Copyright (c) 2013-2020, The PurpleI2P Project
1 * Copyright (c) 2013-2021, The PurpleI2P Project
22 *
33 * This file is part of Purple i2pd project and licensed under BSD3
44 *
2222 {
2323 TunnelHopConfig::TunnelHopConfig (std::shared_ptr<const i2p::data::IdentityEx> r)
2424 {
25 RAND_bytes (layerKey, 32);
26 RAND_bytes (ivKey, 32);
27 RAND_bytes (replyKey, 32);
28 RAND_bytes (replyIV, 16);
2925 RAND_bytes ((uint8_t *)&tunnelID, 4);
3026 if (!tunnelID) tunnelID = 1; // tunnelID can't be zero
3127 isGateway = true;
7672 isGateway = false;
7773 }
7874 }
75
76 void TunnelHopConfig::DecryptRecord (uint8_t * records, int index) const
77 {
78 uint8_t * record = records + index*TUNNEL_BUILD_RECORD_SIZE;
79 i2p::crypto::CBCDecryption decryption;
80 decryption.SetKey (replyKey);
81 decryption.SetIV (replyIV);
82 decryption.Decrypt(record, TUNNEL_BUILD_RECORD_SIZE, record);
83 }
7984
80 void TunnelHopConfig::CreateBuildRequestRecord (uint8_t * record, uint32_t replyMsgID, BN_CTX * ctx)
81 {
85 void ElGamalTunnelHopConfig::CreateBuildRequestRecord (uint8_t * records, uint32_t replyMsgID)
86 {
87 // generate keys
88 RAND_bytes (layerKey, 32);
89 RAND_bytes (ivKey, 32);
90 RAND_bytes (replyKey, 32);
91 RAND_bytes (replyIV, 16);
92 // fill clear text
8293 uint8_t flag = 0;
83 if (isGateway) flag |= 0x80;
84 if (isEndpoint) flag |= 0x40;
94 if (isGateway) flag |= TUNNEL_BUILD_RECORD_GATEWAY_FLAG;
95 if (isEndpoint) flag |= TUNNEL_BUILD_RECORD_ENDPOINT_FLAG;
96 uint8_t clearText[BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE];
97 htobe32buf (clearText + BUILD_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET, tunnelID);
98 memcpy (clearText + BUILD_REQUEST_RECORD_OUR_IDENT_OFFSET, ident->GetIdentHash (), 32);
99 htobe32buf (clearText + BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET, nextTunnelID);
100 memcpy (clearText + BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET, nextIdent, 32);
101 memcpy (clearText + BUILD_REQUEST_RECORD_LAYER_KEY_OFFSET, layerKey, 32);
102 memcpy (clearText + BUILD_REQUEST_RECORD_IV_KEY_OFFSET, ivKey, 32);
103 memcpy (clearText + BUILD_REQUEST_RECORD_REPLY_KEY_OFFSET, replyKey, 32);
104 memcpy (clearText + BUILD_REQUEST_RECORD_REPLY_IV_OFFSET, replyIV, 16);
105 clearText[BUILD_REQUEST_RECORD_FLAG_OFFSET] = flag;
106 htobe32buf (clearText + BUILD_REQUEST_RECORD_REQUEST_TIME_OFFSET, i2p::util::GetHoursSinceEpoch ());
107 htobe32buf (clearText + BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET, replyMsgID);
108 RAND_bytes (clearText + BUILD_REQUEST_RECORD_PADDING_OFFSET, BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE - BUILD_REQUEST_RECORD_PADDING_OFFSET);
109 // encrypt
110 uint8_t * record = records + recordIndex*TUNNEL_BUILD_RECORD_SIZE;
85111 auto encryptor = ident->CreateEncryptor (nullptr);
86 if (IsECIES ())
87 {
88 uint8_t clearText[ECIES_BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE];
89 htobe32buf (clearText + ECIES_BUILD_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET, tunnelID);
90 htobe32buf (clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET, nextTunnelID);
91 memcpy (clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET, nextIdent, 32);
92 memcpy (clearText + ECIES_BUILD_REQUEST_RECORD_LAYER_KEY_OFFSET, layerKey, 32);
93 memcpy (clearText + ECIES_BUILD_REQUEST_RECORD_IV_KEY_OFFSET, ivKey, 32);
94 memcpy (clearText + ECIES_BUILD_REQUEST_RECORD_REPLY_KEY_OFFSET, replyKey, 32);
95 memcpy (clearText + ECIES_BUILD_REQUEST_RECORD_REPLY_IV_OFFSET, replyIV, 16);
96 clearText[ECIES_BUILD_REQUEST_RECORD_FLAG_OFFSET] = flag;
97 memset (clearText + ECIES_BUILD_REQUEST_RECORD_MORE_FLAGS_OFFSET, 0, 3); // set to 0 for compatibility
98 htobe32buf (clearText + ECIES_BUILD_REQUEST_RECORD_REQUEST_TIME_OFFSET, i2p::util::GetMinutesSinceEpoch ());
99 htobe32buf (clearText + ECIES_BUILD_REQUEST_RECORD_REQUEST_EXPIRATION_OFFSET, 600); // +10 minutes
100 htobe32buf (clearText + ECIES_BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET, replyMsgID);
101 memset (clearText + ECIES_BUILD_REQUEST_RECORD_PADDING_OFFSET, 0, ECIES_BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE - ECIES_BUILD_REQUEST_RECORD_PADDING_OFFSET);
102 if (encryptor)
103 EncryptECIES (encryptor, clearText, record + BUILD_REQUEST_RECORD_ENCRYPTED_OFFSET, ctx);
104 }
105 else
106 {
107 uint8_t clearText[BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE];
108 htobe32buf (clearText + BUILD_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET, tunnelID);
109 memcpy (clearText + BUILD_REQUEST_RECORD_OUR_IDENT_OFFSET, ident->GetIdentHash (), 32);
110 htobe32buf (clearText + BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET, nextTunnelID);
111 memcpy (clearText + BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET, nextIdent, 32);
112 memcpy (clearText + BUILD_REQUEST_RECORD_LAYER_KEY_OFFSET, layerKey, 32);
113 memcpy (clearText + BUILD_REQUEST_RECORD_IV_KEY_OFFSET, ivKey, 32);
114 memcpy (clearText + BUILD_REQUEST_RECORD_REPLY_KEY_OFFSET, replyKey, 32);
115 memcpy (clearText + BUILD_REQUEST_RECORD_REPLY_IV_OFFSET, replyIV, 16);
116 clearText[BUILD_REQUEST_RECORD_FLAG_OFFSET] = flag;
117 htobe32buf (clearText + BUILD_REQUEST_RECORD_REQUEST_TIME_OFFSET, i2p::util::GetHoursSinceEpoch ());
118 htobe32buf (clearText + BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET, replyMsgID);
119 RAND_bytes (clearText + BUILD_REQUEST_RECORD_PADDING_OFFSET, BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE - BUILD_REQUEST_RECORD_PADDING_OFFSET);
120 if (encryptor)
121 encryptor->Encrypt (clearText, record + BUILD_REQUEST_RECORD_ENCRYPTED_OFFSET, ctx, false);
112 if (encryptor)
113 {
114 BN_CTX * ctx = BN_CTX_new ();
115 encryptor->Encrypt (clearText, record + BUILD_REQUEST_RECORD_ENCRYPTED_OFFSET, ctx, false);
116 BN_CTX_free (ctx);
122117 }
123118 memcpy (record + BUILD_REQUEST_RECORD_TO_PEER_OFFSET, (const uint8_t *)ident->GetIdentHash (), 16);
124 }
125
126 void TunnelHopConfig::EncryptECIES (std::shared_ptr<i2p::crypto::CryptoKeyEncryptor>& encryptor,
127 const uint8_t * plainText, uint8_t * encrypted, BN_CTX * ctx)
128 {
129 uint8_t hepk[32];
130 encryptor->Encrypt (nullptr, hepk, nullptr, false);
131 i2p::crypto::InitNoiseNState (*this, hepk);
119 }
120
121 bool ElGamalTunnelHopConfig::DecryptBuildResponseRecord (uint8_t * records) const
122 {
123 uint8_t * record = records + recordIndex*TUNNEL_BUILD_RECORD_SIZE;
124 i2p::crypto::CBCDecryption decryption;
125 decryption.SetKey (replyKey);
126 decryption.SetIV (replyIV);
127 decryption.Decrypt (record, TUNNEL_BUILD_RECORD_SIZE, record);
128 return true;
129 }
130
131 void ECIESTunnelHopConfig::EncryptECIES (const uint8_t * plainText, size_t len, uint8_t * encrypted)
132 {
133 if (!ident) return;
134 i2p::crypto::InitNoiseNState (*this, ident->GetEncryptionPublicKey ());
132135 auto ephemeralKeys = i2p::transport::transports.GetNextX25519KeysPair ();
133136 memcpy (encrypted, ephemeralKeys->GetPublicKey (), 32);
134137 MixHash (encrypted, 32); // h = SHA256(h || sepk)
135138 encrypted += 32;
136139 uint8_t sharedSecret[32];
137 ephemeralKeys->Agree (hepk, sharedSecret); // x25519(sesk, hepk)
140 ephemeralKeys->Agree (ident->GetEncryptionPublicKey (), sharedSecret); // x25519(sesk, hepk)
138141 MixKey (sharedSecret);
139142 uint8_t nonce[12];
140143 memset (nonce, 0, 12);
141 if (!i2p::crypto::AEADChaCha20Poly1305 (plainText, ECIES_BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE, m_H, 32,
142 m_CK + 32, nonce, encrypted, ECIES_BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE + 16, true)) // encrypt
144 if (!i2p::crypto::AEADChaCha20Poly1305 (plainText, len, m_H, 32, m_CK + 32, nonce, encrypted, len + 16, true)) // encrypt
143145 {
144146 LogPrint (eLogWarning, "Tunnel: Plaintext AEAD encryption failed");
145147 return;
146148 }
147 MixHash (encrypted, ECIES_BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE + 16); // h = SHA256(h || ciphertext)
149 MixHash (encrypted, len + 16); // h = SHA256(h || ciphertext)
150 }
151
152 bool ECIESTunnelHopConfig::DecryptECIES (const uint8_t * key, const uint8_t * nonce, const uint8_t * encrypted, size_t len, uint8_t * clearText) const
153 {
154 return i2p::crypto::AEADChaCha20Poly1305 (encrypted, len - 16, m_H, 32, key, nonce, clearText, len - 16, false); // decrypt
155 }
156
157 void LongECIESTunnelHopConfig::CreateBuildRequestRecord (uint8_t * records, uint32_t replyMsgID)
158 {
159 // generate keys
160 RAND_bytes (layerKey, 32);
161 RAND_bytes (ivKey, 32);
162 RAND_bytes (replyKey, 32);
163 RAND_bytes (replyIV, 16);
164 // fill clear text
165 uint8_t flag = 0;
166 if (isGateway) flag |= TUNNEL_BUILD_RECORD_GATEWAY_FLAG;
167 if (isEndpoint) flag |= TUNNEL_BUILD_RECORD_ENDPOINT_FLAG;
168 uint8_t clearText[ECIES_BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE];
169 htobe32buf (clearText + ECIES_BUILD_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET, tunnelID);
170 htobe32buf (clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET, nextTunnelID);
171 memcpy (clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET, nextIdent, 32);
172 memcpy (clearText + ECIES_BUILD_REQUEST_RECORD_LAYER_KEY_OFFSET, layerKey, 32);
173 memcpy (clearText + ECIES_BUILD_REQUEST_RECORD_IV_KEY_OFFSET, ivKey, 32);
174 memcpy (clearText + ECIES_BUILD_REQUEST_RECORD_REPLY_KEY_OFFSET, replyKey, 32);
175 memcpy (clearText + ECIES_BUILD_REQUEST_RECORD_REPLY_IV_OFFSET, replyIV, 16);
176 clearText[ECIES_BUILD_REQUEST_RECORD_FLAG_OFFSET] = flag;
177 memset (clearText + ECIES_BUILD_REQUEST_RECORD_MORE_FLAGS_OFFSET, 0, 3); // set to 0 for compatibility
178 htobe32buf (clearText + ECIES_BUILD_REQUEST_RECORD_REQUEST_TIME_OFFSET, i2p::util::GetMinutesSinceEpoch ());
179 htobe32buf (clearText + ECIES_BUILD_REQUEST_RECORD_REQUEST_EXPIRATION_OFFSET, 600); // +10 minutes
180 htobe32buf (clearText + ECIES_BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET, replyMsgID);
181 memset (clearText + ECIES_BUILD_REQUEST_RECORD_PADDING_OFFSET, 0, ECIES_BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE - ECIES_BUILD_REQUEST_RECORD_PADDING_OFFSET);
182 // encrypt
183 uint8_t * record = records + recordIndex*TUNNEL_BUILD_RECORD_SIZE;
184 EncryptECIES (clearText, ECIES_BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE, record + BUILD_REQUEST_RECORD_ENCRYPTED_OFFSET);
185 memcpy (record + BUILD_REQUEST_RECORD_TO_PEER_OFFSET, (const uint8_t *)ident->GetIdentHash (), 16);
186 }
187
188 bool LongECIESTunnelHopConfig::DecryptBuildResponseRecord (uint8_t * records) const
189 {
190 uint8_t * record = records + recordIndex*TUNNEL_BUILD_RECORD_SIZE;
191 uint8_t nonce[12];
192 memset (nonce, 0, 12);
193 if (!DecryptECIES (m_CK, nonce, record, TUNNEL_BUILD_RECORD_SIZE, record))
194 {
195 LogPrint (eLogWarning, "Tunnel: Response AEAD decryption failed");
196 return false;
197 }
198 return true;
199 }
200
201 void ShortECIESTunnelHopConfig::CreateBuildRequestRecord (uint8_t * records, uint32_t replyMsgID)
202 {
203 // fill clear text
204 uint8_t flag = 0;
205 if (isGateway) flag |= TUNNEL_BUILD_RECORD_GATEWAY_FLAG;
206 if (isEndpoint) flag |= TUNNEL_BUILD_RECORD_ENDPOINT_FLAG;
207 uint8_t clearText[SHORT_REQUEST_RECORD_CLEAR_TEXT_SIZE ];
208 htobe32buf (clearText + SHORT_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET, tunnelID);
209 htobe32buf (clearText + SHORT_REQUEST_RECORD_NEXT_TUNNEL_OFFSET, nextTunnelID);
210 memcpy (clearText + SHORT_REQUEST_RECORD_NEXT_IDENT_OFFSET, nextIdent, 32);
211 clearText[SHORT_REQUEST_RECORD_FLAG_OFFSET] = flag;
212 memset (clearText + SHORT_REQUEST_RECORD_MORE_FLAGS_OFFSET, 0, 2);
213 clearText[SHORT_REQUEST_RECORD_LAYER_ENCRYPTION_TYPE] = 0; // AES
214 htobe32buf (clearText + SHORT_REQUEST_RECORD_REQUEST_TIME_OFFSET, i2p::util::GetMinutesSinceEpoch ());
215 htobe32buf (clearText + SHORT_REQUEST_RECORD_REQUEST_EXPIRATION_OFFSET , 600); // +10 minutes
216 htobe32buf (clearText + SHORT_REQUEST_RECORD_SEND_MSG_ID_OFFSET, replyMsgID);
217 memset (clearText + SHORT_REQUEST_RECORD_PADDING_OFFSET, 0, SHORT_REQUEST_RECORD_CLEAR_TEXT_SIZE - SHORT_REQUEST_RECORD_PADDING_OFFSET);
218 // encrypt
219 uint8_t * record = records + recordIndex*SHORT_TUNNEL_BUILD_RECORD_SIZE;
220 EncryptECIES (clearText, SHORT_REQUEST_RECORD_CLEAR_TEXT_SIZE, record + SHORT_REQUEST_RECORD_ENCRYPTED_OFFSET);
221 // derive keys
222 i2p::crypto::HKDF (m_CK, nullptr, 0, "SMTunnelReplyKey", m_CK);
223 memcpy (replyKey, m_CK + 32, 32);
224 i2p::crypto::HKDF (m_CK, nullptr, 0, "SMTunnelLayerKey", m_CK);
225 memcpy (layerKey, m_CK + 32, 32);
226 if (isEndpoint)
227 {
228 i2p::crypto::HKDF (m_CK, nullptr, 0, "TunnelLayerIVKey", m_CK);
229 memcpy (ivKey, m_CK + 32, 32);
230 i2p::crypto::HKDF (m_CK, nullptr, 0, "RGarlicKeyAndTag", m_CK); // OTBRM garlic key m_CK + 32, tag first 8 bytes of m_CK
231 }
232 else
233 memcpy (ivKey, m_CK, 32); // last HKDF
234 memcpy (record + BUILD_REQUEST_RECORD_TO_PEER_OFFSET, (const uint8_t *)ident->GetIdentHash (), 16);
235 }
236
237 bool ShortECIESTunnelHopConfig::DecryptBuildResponseRecord (uint8_t * records) const
238 {
239 uint8_t * record = records + recordIndex*SHORT_TUNNEL_BUILD_RECORD_SIZE;
240 uint8_t nonce[12];
241 memset (nonce, 0, 12);
242 nonce[4] = recordIndex; // nonce is record index
243 if (!DecryptECIES (replyKey, nonce, record, SHORT_TUNNEL_BUILD_RECORD_SIZE, record))
244 {
245 LogPrint (eLogWarning, "Tunnel: Response AEAD decryption failed");
246 return false;
247 }
248 return true;
249 }
250
251 void ShortECIESTunnelHopConfig::DecryptRecord (uint8_t * records, int index) const
252 {
253 uint8_t * record = records + index*SHORT_TUNNEL_BUILD_RECORD_SIZE;
254 uint8_t nonce[12];
255 memset (nonce, 0, 12);
256 nonce[4] = index; // nonce is index
257 i2p::crypto::ChaCha20 (record, SHORT_TUNNEL_BUILD_RECORD_SIZE, replyKey, nonce, record);
258 }
259
260 uint64_t ShortECIESTunnelHopConfig::GetGarlicKey (uint8_t * key) const
261 {
262 uint64_t tag;
263 memcpy (&tag, m_CK, 8);
264 memcpy (key, m_CK + 32, 32);
265 return tag;
148266 }
149267 }
150268 }
1717 {
1818 namespace tunnel
1919 {
20 struct TunnelHopConfig: public i2p::crypto::NoiseSymmetricState
20 struct TunnelHopConfig
2121 {
2222 std::shared_ptr<const i2p::data::IdentityEx> ident;
2323 i2p::data::IdentHash nextIdent;
3232 int recordIndex; // record # in tunnel build message
3333
3434 TunnelHopConfig (std::shared_ptr<const i2p::data::IdentityEx> r);
35 virtual ~TunnelHopConfig () {};
3536
3637 void SetNextIdent (const i2p::data::IdentHash& ident);
3738 void SetReplyHop (uint32_t replyTunnelID, const i2p::data::IdentHash& replyIdent);
3839 void SetNext (TunnelHopConfig * n);
3940 void SetPrev (TunnelHopConfig * p);
4041
41 bool IsECIES () const { return ident->GetCryptoKeyType () == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD; };
42 void CreateBuildRequestRecord (uint8_t * record, uint32_t replyMsgID, BN_CTX * ctx);
43 void EncryptECIES (std::shared_ptr<i2p::crypto::CryptoKeyEncryptor>& encryptor,
44 const uint8_t * clearText, uint8_t * encrypted, BN_CTX * ctx);
45 };
42 virtual uint8_t GetRetCode (const uint8_t * records) const = 0;
43 virtual void CreateBuildRequestRecord (uint8_t * records, uint32_t replyMsgID) = 0;
44 virtual bool DecryptBuildResponseRecord (uint8_t * records) const = 0;
45 virtual void DecryptRecord (uint8_t * records, int index) const; // AES
46 virtual uint64_t GetGarlicKey (uint8_t * key) const { return 0; }; // return tag
47 };
48
49 struct ElGamalTunnelHopConfig: public TunnelHopConfig
50 {
51 ElGamalTunnelHopConfig (std::shared_ptr<const i2p::data::IdentityEx> r):
52 TunnelHopConfig (r) {};
53 uint8_t GetRetCode (const uint8_t * records) const
54 { return (records + recordIndex*TUNNEL_BUILD_RECORD_SIZE)[BUILD_RESPONSE_RECORD_RET_OFFSET]; };
55 void CreateBuildRequestRecord (uint8_t * records, uint32_t replyMsgID);
56 bool DecryptBuildResponseRecord (uint8_t * records) const;
57 };
58
59 struct ECIESTunnelHopConfig: public TunnelHopConfig, public i2p::crypto::NoiseSymmetricState
60 {
61 ECIESTunnelHopConfig (std::shared_ptr<const i2p::data::IdentityEx> r):
62 TunnelHopConfig (r) {};
63 void EncryptECIES (const uint8_t * clearText, size_t len, uint8_t * encrypted);
64 bool DecryptECIES (const uint8_t * key, const uint8_t * nonce, const uint8_t * encrypted, size_t len, uint8_t * clearText) const;
65 };
66
67 struct LongECIESTunnelHopConfig: public ECIESTunnelHopConfig
68 {
69 LongECIESTunnelHopConfig (std::shared_ptr<const i2p::data::IdentityEx> r):
70 ECIESTunnelHopConfig (r) {};
71 uint8_t GetRetCode (const uint8_t * records) const
72 { return (records + recordIndex*TUNNEL_BUILD_RECORD_SIZE)[ECIES_BUILD_RESPONSE_RECORD_RET_OFFSET]; };
73 void CreateBuildRequestRecord (uint8_t * records, uint32_t replyMsgID);
74 bool DecryptBuildResponseRecord (uint8_t * records) const;
75 };
76
77 struct ShortECIESTunnelHopConfig: public ECIESTunnelHopConfig
78 {
79 ShortECIESTunnelHopConfig (std::shared_ptr<const i2p::data::IdentityEx> r):
80 ECIESTunnelHopConfig (r) {};
81 uint8_t GetRetCode (const uint8_t * records) const
82 { return (records + recordIndex*SHORT_TUNNEL_BUILD_RECORD_SIZE)[SHORT_RESPONSE_RECORD_RET_OFFSET]; };
83 void CreateBuildRequestRecord (uint8_t * records, uint32_t replyMsgID);
84 bool DecryptBuildResponseRecord (uint8_t * records) const;
85 void DecryptRecord (uint8_t * records, int index) const override; // Chacha20
86 uint64_t GetGarlicKey (uint8_t * key) const override;
87 };
4688
4789 class TunnelConfig
4890 {
4991 public:
5092
51 TunnelConfig (std::vector<std::shared_ptr<const i2p::data::IdentityEx> > peers) // inbound
93 TunnelConfig (const std::vector<std::shared_ptr<const i2p::data::IdentityEx> >& peers,
94 bool isShort = false): // inbound
95 m_IsShort (isShort)
5296 {
5397 CreatePeers (peers);
5498 m_LastHop->SetNextIdent (i2p::context.GetIdentHash ());
5599 }
56100
57 TunnelConfig (std::vector<std::shared_ptr<const i2p::data::IdentityEx> > peers,
58 uint32_t replyTunnelID, const i2p::data::IdentHash& replyIdent) // outbound
101 TunnelConfig (const std::vector<std::shared_ptr<const i2p::data::IdentityEx> >& peers,
102 uint32_t replyTunnelID, const i2p::data::IdentHash& replyIdent, bool isShort = false): // outbound
103 m_IsShort (isShort)
59104 {
60105 CreatePeers (peers);
61106 m_FirstHop->isGateway = false;
74119 }
75120 }
76121
122 bool IsShort () const { return m_IsShort; }
123
77124 TunnelHopConfig * GetFirstHop () const
78125 {
79126 return m_FirstHop;
140187 protected:
141188
142189 // this constructor can't be called from outside
143 TunnelConfig (): m_FirstHop (nullptr), m_LastHop (nullptr)
190 TunnelConfig (): m_FirstHop (nullptr), m_LastHop (nullptr), m_IsShort (false)
144191 {
145192 }
146193
147194 private:
148195
149 template<class Peers>
150 void CreatePeers (const Peers& peers)
196 void CreatePeers (const std::vector<std::shared_ptr<const i2p::data::IdentityEx> >& peers)
151197 {
152198 TunnelHopConfig * prev = nullptr;
153199 for (const auto& it: peers)
154200 {
155 auto hop = new TunnelHopConfig (it);
201 TunnelHopConfig * hop;
202 if (m_IsShort)
203 hop = new ShortECIESTunnelHopConfig (it);
204 else
205 {
206 if (it->GetCryptoKeyType () == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD)
207 hop = new LongECIESTunnelHopConfig (it);
208 else
209 hop = new ElGamalTunnelHopConfig (it);
210 }
156211 if (prev)
157212 prev->SetNext (hop);
158213 else
165220 private:
166221
167222 TunnelHopConfig * m_FirstHop, * m_LastHop;
223 bool m_IsShort;
168224 };
169225
170226 class ZeroHopsTunnelConfig: public TunnelConfig
5151 bool isFollowOnFragment = flag & 0x80, isLastFragment = true;
5252 uint32_t msgID = 0;
5353 int fragmentNum = 0;
54 TunnelMessageBlockEx m;
5554 if (!isFollowOnFragment)
5655 {
5756 // first fragment
58
59 m.deliveryType = (TunnelDeliveryType)((flag >> 5) & 0x03);
60 switch (m.deliveryType)
57 if (m_CurrentMsgID)
58 AddIncompleteCurrentMessage (); // we have got a new message while previous is not complete
59
60 m_CurrentMessage.deliveryType = (TunnelDeliveryType)((flag >> 5) & 0x03);
61 switch (m_CurrentMessage.deliveryType)
6162 {
6263 case eDeliveryTypeLocal: // 0
6364 break;
6465 case eDeliveryTypeTunnel: // 1
65 m.tunnelID = bufbe32toh (fragment);
66 m_CurrentMessage.tunnelID = bufbe32toh (fragment);
6667 fragment += 4; // tunnelID
67 m.hash = i2p::data::IdentHash (fragment);
68 m_CurrentMessage.hash = i2p::data::IdentHash (fragment);
6869 fragment += 32; // hash
6970 break;
7071 case eDeliveryTypeRouter: // 2
71 m.hash = i2p::data::IdentHash (fragment);
72 m_CurrentMessage.hash = i2p::data::IdentHash (fragment);
7273 fragment += 32; // to hash
7374 break;
7475 default: ;
8081 // Message ID
8182 msgID = bufbe32toh (fragment);
8283 fragment += 4;
84 m_CurrentMsgID = msgID;
8385 isLastFragment = false;
8486 }
8587 }
9597 uint16_t size = bufbe16toh (fragment);
9698 fragment += 2;
9799
98 msg->offset = fragment - msg->buf;
99 msg->len = msg->offset + size;
100 if (msg->len > msg->maxLen)
101 {
102 LogPrint (eLogError, "TunnelMessage: fragment is too long ", (int)size);
103 return;
104 }
105 if (fragment + size < decrypted + TUNNEL_DATA_ENCRYPTED_SIZE)
106 {
107 // this is not last message. we have to copy it
108 m.data = NewI2NPTunnelMessage ();
109 m.data->offset += TUNNEL_GATEWAY_HEADER_SIZE; // reserve room for TunnelGateway header
110 m.data->len += TUNNEL_GATEWAY_HEADER_SIZE;
111 *(m.data) = *msg;
112 }
100 // handle fragment
101 if (isFollowOnFragment)
102 {
103 // existing message
104 if (m_CurrentMsgID && m_CurrentMsgID == msgID && m_CurrentMessage.nextFragmentNum == fragmentNum)
105 HandleCurrenMessageFollowOnFragment (fragment, size, isLastFragment); // previous
106 else
107 {
108 HandleFollowOnFragment (msgID, isLastFragment, fragmentNum, fragment, size); // another
109 m_CurrentMsgID = 0; m_CurrentMessage.data = nullptr;
110 }
111 }
113112 else
114 m.data = msg;
115
116 if (!isFollowOnFragment && isLastFragment)
117 HandleNextMessage (m);
118 else
119 {
120 if (msgID) // msgID is presented, assume message is fragmented
121 {
122 if (!isFollowOnFragment) // create new incomlete message
123 {
124 m.nextFragmentNum = 1;
125 m.receiveTime = i2p::util::GetMillisecondsSinceEpoch ();
126 auto ret = m_IncompleteMessages.insert (std::pair<uint32_t, TunnelMessageBlockEx>(msgID, m));
127 if (ret.second)
128 HandleOutOfSequenceFragments (msgID, ret.first->second);
129 else
130 LogPrint (eLogError, "TunnelMessage: Incomplete message ", msgID, " already exists");
131 }
132 else
133 {
134 m.nextFragmentNum = fragmentNum;
135 HandleFollowOnFragment (msgID, isLastFragment, m);
136 }
113 {
114 // new message
115 msg->offset = fragment - msg->buf;
116 msg->len = msg->offset + size;
117 // check message size
118 if (msg->len > msg->maxLen)
119 {
120 LogPrint (eLogError, "TunnelMessage: fragment is too long ", (int)size);
121 m_CurrentMsgID = 0; m_CurrentMessage.data = nullptr;
122 return;
123 }
124 // create new or assign I2NP message
125 if (fragment + size < decrypted + TUNNEL_DATA_ENCRYPTED_SIZE)
126 {
127 // this is not last message. we have to copy it
128 m_CurrentMessage.data = NewI2NPTunnelMessage (true);
129 *(m_CurrentMessage.data) = *msg;
137130 }
138131 else
132 m_CurrentMessage.data = msg;
133
134 if (isLastFragment)
135 {
136 // single message
137 HandleNextMessage (m_CurrentMessage);
138 m_CurrentMsgID = 0; m_CurrentMessage.data = nullptr;
139 }
140 else if (msgID)
141 {
142 // first fragment of a new message
143 m_CurrentMessage.nextFragmentNum = 1;
144 m_CurrentMessage.receiveTime = i2p::util::GetMillisecondsSinceEpoch ();
145 HandleOutOfSequenceFragments (msgID, m_CurrentMessage);
146 }
147 else
148 {
139149 LogPrint (eLogError, "TunnelMessage: Message is fragmented, but msgID is not presented");
140 }
141
150 m_CurrentMsgID = 0; m_CurrentMessage.data = nullptr;
151 }
152 }
153
142154 fragment += size;
143155 }
144156 }
146158 LogPrint (eLogError, "TunnelMessage: zero not found");
147159 }
148160
149 void TunnelEndpoint::HandleFollowOnFragment (uint32_t msgID, bool isLastFragment, const TunnelMessageBlockEx& m)
150 {
151 auto fragment = m.data->GetBuffer ();
152 auto size = m.data->GetLength ();
161 void TunnelEndpoint::HandleFollowOnFragment (uint32_t msgID, bool isLastFragment,
162 uint8_t fragmentNum, const uint8_t * fragment, size_t size)
163 {
153164 auto it = m_IncompleteMessages.find (msgID);
154165 if (it != m_IncompleteMessages.end())
155166 {
156167 auto& msg = it->second;
157 if (m.nextFragmentNum == msg.nextFragmentNum)
158 {
159 if (msg.data->len + size < I2NP_MAX_MESSAGE_SIZE) // check if message is not too long
160 {
161 if (msg.data->len + size > msg.data->maxLen)
162 {
163 // LogPrint (eLogWarning, "TunnelMessage: I2NP message size ", msg.data->maxLen, " is not enough");
164 auto newMsg = NewI2NPMessage ();
165 *newMsg = *(msg.data);
166 msg.data = newMsg;
167 }
168 if (msg.data->Concat (fragment, size) < size) // concatenate fragment
169 LogPrint (eLogError, "TunnelMessage: I2NP buffer overflow ", msg.data->maxLen);
168 if (fragmentNum == msg.nextFragmentNum)
169 {
170 if (ConcatFollowOnFragment (msg, fragment, size))
171 {
170172 if (isLastFragment)
171173 {
172174 // message complete
181183 }
182184 else
183185 {
184 LogPrint (eLogError, "TunnelMessage: Fragment ", m.nextFragmentNum, " of message ", msgID, "exceeds max I2NP message size, message dropped");
186 LogPrint (eLogError, "TunnelMessage: Fragment ", fragmentNum, " of message ", msgID, "exceeds max I2NP message size, message dropped");
185187 m_IncompleteMessages.erase (it);
186188 }
187189 }
188190 else
189191 {
190 LogPrint (eLogWarning, "TunnelMessage: Unexpected fragment ", (int)m.nextFragmentNum, " instead ", (int)msg.nextFragmentNum, " of message ", msgID, ", saved");
191 AddOutOfSequenceFragment (msgID, m.nextFragmentNum, isLastFragment, m.data);
192 LogPrint (eLogWarning, "TunnelMessage: Unexpected fragment ", (int)fragmentNum, " instead ", (int)msg.nextFragmentNum, " of message ", msgID, ", saved");
193 AddOutOfSequenceFragment (msgID, fragmentNum, isLastFragment, fragment, size);
192194 }
193195 }
194196 else
195197 {
196 LogPrint (eLogWarning, "TunnelMessage: First fragment of message ", msgID, " not found, saved");
197 AddOutOfSequenceFragment (msgID, m.nextFragmentNum, isLastFragment, m.data);
198 }
199 }
200
201 void TunnelEndpoint::AddOutOfSequenceFragment (uint32_t msgID, uint8_t fragmentNum, bool isLastFragment, std::shared_ptr<I2NPMessage> data)
202 {
203 if (!m_OutOfSequenceFragments.insert ({{msgID, fragmentNum}, {isLastFragment, data, i2p::util::GetMillisecondsSinceEpoch () }}).second)
198 LogPrint (eLogDebug, "TunnelMessage: First fragment of message ", msgID, " not found, saved");
199 AddOutOfSequenceFragment (msgID, fragmentNum, isLastFragment, fragment, size);
200 }
201 }
202
203 bool TunnelEndpoint::ConcatFollowOnFragment (TunnelMessageBlockEx& msg, const uint8_t * fragment, size_t size) const
204 {
205 if (msg.data->len + size < I2NP_MAX_MESSAGE_SIZE) // check if message is not too long
206 {
207 if (msg.data->len + size > msg.data->maxLen)
208 {
209 // LogPrint (eLogWarning, "TunnelMessage: I2NP message size ", msg.data->maxLen, " is not enough");
210 auto newMsg = NewI2NPMessage ();
211 *newMsg = *(msg.data);
212 msg.data = newMsg;
213 }
214 if (msg.data->Concat (fragment, size) < size) // concatenate fragment
215 {
216 LogPrint (eLogError, "TunnelMessage: I2NP buffer overflow ", msg.data->maxLen);
217 return false;
218 }
219 }
220 else
221 return false;
222 return true;
223 }
224
225 void TunnelEndpoint::HandleCurrenMessageFollowOnFragment (const uint8_t * fragment, size_t size, bool isLastFragment)
226 {
227 if (ConcatFollowOnFragment (m_CurrentMessage, fragment, size))
228 {
229 if (isLastFragment)
230 {
231 // message complete
232 HandleNextMessage (m_CurrentMessage);
233 m_CurrentMsgID = 0; m_CurrentMessage.data = nullptr;
234 }
235 else
236 {
237 m_CurrentMessage.nextFragmentNum++;
238 HandleOutOfSequenceFragments (m_CurrentMsgID, m_CurrentMessage);
239 }
240 }
241 else
242 {
243 LogPrint (eLogError, "TunnelMessage: Fragment ", m_CurrentMessage.nextFragmentNum, " of message ", m_CurrentMsgID, " exceeds max I2NP message size, message dropped");
244 m_CurrentMsgID = 0; m_CurrentMessage.data = nullptr;
245 }
246 }
247
248 void TunnelEndpoint::AddIncompleteCurrentMessage ()
249 {
250 if (m_CurrentMsgID)
251 {
252 auto ret = m_IncompleteMessages.emplace (m_CurrentMsgID, m_CurrentMessage);
253 if (!ret.second)
254 LogPrint (eLogError, "TunnelMessage: Incomplete message ", m_CurrentMsgID, " already exists");
255 m_CurrentMessage.data = nullptr;
256 m_CurrentMsgID = 0;
257 }
258 }
259
260 void TunnelEndpoint::AddOutOfSequenceFragment (uint32_t msgID, uint8_t fragmentNum,
261 bool isLastFragment, const uint8_t * fragment, size_t size)
262 {
263 std::unique_ptr<Fragment> f(new Fragment (isLastFragment, i2p::util::GetMillisecondsSinceEpoch (), size));
264 memcpy (f->data.data (), fragment, size);
265 if (!m_OutOfSequenceFragments.emplace ((uint64_t)msgID << 32 | fragmentNum, std::move (f)).second)
204266 LogPrint (eLogInfo, "TunnelMessage: duplicate out-of-sequence fragment ", fragmentNum, " of message ", msgID);
205267 }
206268
211273 if (!msg.nextFragmentNum) // message complete
212274 {
213275 HandleNextMessage (msg);
214 m_IncompleteMessages.erase (msgID);
276 if (&msg == &m_CurrentMessage)
277 {
278 m_CurrentMsgID = 0;
279 m_CurrentMessage.data = nullptr;
280 }
281 else
282 m_IncompleteMessages.erase (msgID);
283 LogPrint (eLogDebug, "TunnelMessage: All fragments of message ", msgID, " found");
215284 break;
216285 }
217286 }
219288
220289 bool TunnelEndpoint::ConcatNextOutOfSequenceFragment (uint32_t msgID, TunnelMessageBlockEx& msg)
221290 {
222 auto it = m_OutOfSequenceFragments.find ({msgID, msg.nextFragmentNum});
291 auto it = m_OutOfSequenceFragments.find ((uint64_t)msgID << 32 | msg.nextFragmentNum);
223292 if (it != m_OutOfSequenceFragments.end ())
224293 {
225294 LogPrint (eLogDebug, "TunnelMessage: Out-of-sequence fragment ", (int)msg.nextFragmentNum, " of message ", msgID, " found");
226 size_t size = it->second.data->GetLength ();
295 size_t size = it->second->data.size ();
227296 if (msg.data->len + size > msg.data->maxLen)
228297 {
229298 LogPrint (eLogWarning, "TunnelMessage: Tunnel endpoint I2NP message size ", msg.data->maxLen, " is not enough");
231300 *newMsg = *(msg.data);
232301 msg.data = newMsg;
233302 }
234 if (msg.data->Concat (it->second.data->GetBuffer (), size) < size) // concatenate out-of-sync fragment
303 if (msg.data->Concat (it->second->data.data (), size) < size) // concatenate out-of-sync fragment
235304 LogPrint (eLogError, "TunnelMessage: Tunnel endpoint I2NP buffer overflow ", msg.data->maxLen);
236 if (it->second.isLastFragment)
305 if (it->second->isLastFragment)
237306 // message complete
238307 msg.nextFragmentNum = 0;
239308 else
286355 // out-of-sequence fragments
287356 for (auto it = m_OutOfSequenceFragments.begin (); it != m_OutOfSequenceFragments.end ();)
288357 {
289 if (ts > it->second.receiveTime + i2p::I2NP_MESSAGE_EXPIRATION_TIMEOUT)
358 if (ts > it->second->receiveTime + i2p::I2NP_MESSAGE_EXPIRATION_TIMEOUT)
290359 it = m_OutOfSequenceFragments.erase (it);
291360 else
292361 ++it;
00 /*
1 * Copyright (c) 2013-2020, The PurpleI2P Project
1 * Copyright (c) 2013-2021, The PurpleI2P Project
22 *
33 * This file is part of Purple i2pd project and licensed under BSD3
44 *
99 #define TUNNEL_ENDPOINT_H__
1010
1111 #include <inttypes.h>
12 #include <map>
12 #include <unordered_map>
13 #include <vector>
1314 #include <string>
1415 #include "I2NPProtocol.h"
1516 #include "TunnelBase.h"
2829
2930 struct Fragment
3031 {
32 Fragment (bool last, uint64_t t, size_t size): isLastFragment (last), receiveTime (t), data (size) {};
3133 bool isLastFragment;
32 std::shared_ptr<I2NPMessage> data;
3334 uint64_t receiveTime; // milliseconds since epoch
35 std::vector<uint8_t> data;
3436 };
3537
3638 public:
3739
38 TunnelEndpoint (bool isInbound): m_IsInbound (isInbound), m_NumReceivedBytes (0) {};
40 TunnelEndpoint (bool isInbound): m_IsInbound (isInbound), m_NumReceivedBytes (0), m_CurrentMsgID (0) {};
3941 ~TunnelEndpoint ();
4042 size_t GetNumReceivedBytes () const { return m_NumReceivedBytes; };
4143 void Cleanup ();
4446
4547 private:
4648
47 void HandleFollowOnFragment (uint32_t msgID, bool isLastFragment, const TunnelMessageBlockEx& m);
49 void HandleFollowOnFragment (uint32_t msgID, bool isLastFragment, uint8_t fragmentNum, const uint8_t * fragment, size_t size);
50 bool ConcatFollowOnFragment (TunnelMessageBlockEx& msg, const uint8_t * fragment, size_t size) const; // true if success
51 void HandleCurrenMessageFollowOnFragment (const uint8_t * frgament, size_t size, bool isLastFragment);
4852 void HandleNextMessage (const TunnelMessageBlock& msg);
4953
50 void AddOutOfSequenceFragment (uint32_t msgID, uint8_t fragmentNum, bool isLastFragment, std::shared_ptr<I2NPMessage> data);
54 void AddOutOfSequenceFragment (uint32_t msgID, uint8_t fragmentNum, bool isLastFragment, const uint8_t * fragment, size_t size);
5155 bool ConcatNextOutOfSequenceFragment (uint32_t msgID, TunnelMessageBlockEx& msg); // true if something added
5256 void HandleOutOfSequenceFragments (uint32_t msgID, TunnelMessageBlockEx& msg);
53
57 void AddIncompleteCurrentMessage ();
58
5459 private:
5560
56 std::map<uint32_t, TunnelMessageBlockEx> m_IncompleteMessages;
57 std::map<std::pair<uint32_t, uint8_t>, Fragment> m_OutOfSequenceFragments; // (msgID, fragment#)->fragment
61 std::unordered_map<uint32_t, TunnelMessageBlockEx> m_IncompleteMessages;
62 std::unordered_map<uint64_t, std::unique_ptr<Fragment> > m_OutOfSequenceFragments; // ((msgID << 8) + fragment#)->fragment
5863 bool m_IsInbound;
5964 size_t m_NumReceivedBytes;
65 TunnelMessageBlockEx m_CurrentMessage;
66 uint32_t m_CurrentMsgID;
6067 };
6168 }
6269 }
214214 const auto& tunnelDataMsgs = m_Buffer.GetTunnelDataMsgs ();
215215 for (auto& tunnelMsg : tunnelDataMsgs)
216216 {
217 auto newMsg = CreateEmptyTunnelDataMsg ();
217 auto newMsg = CreateEmptyTunnelDataMsg (false);
218218 m_Tunnel->EncryptTunnelMsg (tunnelMsg, newMsg);
219219 htobe32buf (newMsg->GetPayload (), m_Tunnel->GetNextTunnelID ());
220220 newMsg->FillI2NPMessageHeader (eI2NPTunnelData);
2323 {
2424 namespace tunnel
2525 {
26 void Path::Add (std::shared_ptr<const i2p::data::RouterInfo> r)
27 {
28 if (r)
29 {
30 peers.push_back (r->GetRouterIdentity ());
31 if (r->GetVersion () < i2p::data::NETDB_MIN_SHORT_TUNNEL_BUILD_VERSION ||
32 r->GetRouterIdentity ()->GetCryptoKeyType () != i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD)
33 isShort = false;
34 }
35 }
36
37 void Path::Reverse ()
38 {
39 std::reverse (peers.begin (), peers.end ());
40 }
41
2642 TunnelPool::TunnelPool (int numInboundHops, int numOutboundHops, int numInboundTunnels, int numOutboundTunnels):
2743 m_NumInboundHops (numInboundHops), m_NumOutboundHops (numOutboundHops),
2844 m_NumInboundTunnels (numInboundTunnels), m_NumOutboundTunnels (numOutboundTunnels),
141157 {
142158 std::vector<std::shared_ptr<InboundTunnel> > v;
143159 int i = 0;
160 std::shared_ptr<InboundTunnel> slowTunnel;
144161 std::unique_lock<std::mutex> l(m_InboundTunnelsMutex);
145162 for (const auto& it : m_InboundTunnels)
146163 {
147164 if (i >= num) break;
148165 if (it->IsEstablished ())
149166 {
150 v.push_back (it);
151 i++;
152 }
153 }
167 if (it->IsSlow () && !slowTunnel)
168 slowTunnel = it;
169 else
170 {
171 v.push_back (it);
172 i++;
173 }
174 }
175 }
176 if (slowTunnel && (int)v.size () < (num/2+1))
177 v.push_back (slowTunnel);
154178 return v;
155179 }
156180
171195 {
172196 if (tunnels.empty ()) return nullptr;
173197 uint32_t ind = rand () % (tunnels.size ()/2 + 1), i = 0;
198 bool skipped = false;
174199 typename TTunnels::value_type tunnel = nullptr;
175200 for (const auto& it: tunnels)
176201 {
177202 if (it->IsEstablished () && it != excluded)
178203 {
179 if(HasLatencyRequirement() && it->LatencyIsKnown() && !it->LatencyFitsRange(m_MinLatency, m_MaxLatency)) {
180 i ++;
204 if (it->IsSlow () || (HasLatencyRequirement() && it->LatencyIsKnown() &&
205 !it->LatencyFitsRange(m_MinLatency, m_MaxLatency)))
206 {
207 i++; skipped = true;
181208 continue;
182209 }
183210 tunnel = it;
185212 }
186213 if (i > ind && tunnel) break;
187214 }
188 if(HasLatencyRequirement() && !tunnel) {
215 if (!tunnel && skipped)
216 {
189217 ind = rand () % (tunnels.size ()/2 + 1), i = 0;
190218 for (const auto& it: tunnels)
191219 {
392420 }
393421 }
394422
423 bool TunnelPool::IsExploratory () const
424 {
425 return i2p::tunnel::tunnels.GetExploratoryPool () == shared_from_this ();
426 }
427
395428 std::shared_ptr<const i2p::data::RouterInfo> TunnelPool::SelectNextHop (std::shared_ptr<const i2p::data::RouterInfo> prevHop, bool reverse) const
396429 {
397 bool isExploratory = (i2p::tunnel::tunnels.GetExploratoryPool () == shared_from_this ());
398 auto hop = isExploratory ? i2p::data::netdb.GetRandomRouter (prevHop, reverse):
430 auto hop = IsExploratory () ? i2p::data::netdb.GetRandomRouter (prevHop, reverse):
399431 i2p::data::netdb.GetHighBandwidthRandomRouter (prevHop, reverse);
400432
401433 if (!hop || hop->GetProfile ()->IsBad ())
403435 return hop;
404436 }
405437
406 bool StandardSelectPeers(Path & peers, int numHops, bool inbound, SelectHopFunc nextHop)
438 bool StandardSelectPeers(Path & path, int numHops, bool inbound, SelectHopFunc nextHop)
407439 {
408440 int start = 0;
409 auto prevHop = i2p::context.GetSharedRouterInfo ();
441 std::shared_ptr<const i2p::data::RouterInfo> prevHop = i2p::context.GetSharedRouterInfo ();
410442 if(i2p::transport::transports.RoutesRestricted())
411443 {
412444 /** if routes are restricted prepend trusted first hop */
413445 auto hop = i2p::transport::transports.GetRestrictedPeer();
414446 if(!hop) return false;
415 peers.push_back(hop->GetRouterIdentity());
447 path.Add (hop);
416448 prevHop = hop;
417449 start++;
418450 }
424456 (numHops > 1 || (r->IsV4 () && (!inbound || r->IsReachable ())))) // first inbound must be reachable
425457 {
426458 prevHop = r;
427 peers.push_back (r->GetRouterIdentity ());
459 path.Add (r);
428460 start++;
429461 }
430462 }
449481 if (hop1) hop = hop1;
450482 }
451483 prevHop = hop;
452 peers.push_back (hop->GetRouterIdentity ());
484 path.Add (hop);
453485 }
454486 return true;
455487 }
456488
457 bool TunnelPool::SelectPeers (std::vector<std::shared_ptr<const i2p::data::IdentityEx> >& peers, bool isInbound)
489 bool TunnelPool::SelectPeers (Path& path, bool isInbound)
458490 {
459491 int numHops = isInbound ? m_NumInboundHops : m_NumOutboundHops;
460492 // peers is empty
463495 {
464496 std::lock_guard<std::mutex> lock(m_CustomPeerSelectorMutex);
465497 if (m_CustomPeerSelector)
466 return m_CustomPeerSelector->SelectPeers(peers, numHops, isInbound);
498 return m_CustomPeerSelector->SelectPeers(path, numHops, isInbound);
467499 }
468500 // explicit peers in use
469 if (m_ExplicitPeers) return SelectExplicitPeers (peers, isInbound);
470 return StandardSelectPeers(peers, numHops, isInbound, std::bind(&TunnelPool::SelectNextHop, this, std::placeholders::_1, std::placeholders::_2));
471 }
472
473 bool TunnelPool::SelectExplicitPeers (std::vector<std::shared_ptr<const i2p::data::IdentityEx> >& peers, bool isInbound)
474 {
475 int size = m_ExplicitPeers->size ();
476 std::vector<int> peerIndicies;
477 for (int i = 0; i < size; i++) peerIndicies.push_back(i);
478 std::shuffle (peerIndicies.begin(), peerIndicies.end(), std::mt19937(std::random_device()()));
479
501 if (m_ExplicitPeers) return SelectExplicitPeers (path, isInbound);
502 return StandardSelectPeers(path, numHops, isInbound, std::bind(&TunnelPool::SelectNextHop, this, std::placeholders::_1, std::placeholders::_2));
503 }
504
505 bool TunnelPool::SelectExplicitPeers (Path& path, bool isInbound)
506 {
480507 int numHops = isInbound ? m_NumInboundHops : m_NumOutboundHops;
508 if (numHops > (int)m_ExplicitPeers->size ()) numHops = m_ExplicitPeers->size ();
509 if (!numHops) return false;
481510 for (int i = 0; i < numHops; i++)
482511 {
483 auto& ident = (*m_ExplicitPeers)[peerIndicies[i]];
512 auto& ident = (*m_ExplicitPeers)[i];
484513 auto r = i2p::data::netdb.FindRouter (ident);
485514 if (r)
486 peers.push_back (r->GetRouterIdentity ());
515 path.Add (r);
487516 else
488517 {
489518 LogPrint (eLogInfo, "Tunnels: Can't find router for ", ident.ToBase64 ());
500529 if (!outboundTunnel)
501530 outboundTunnel = tunnels.GetNextOutboundTunnel ();
502531 LogPrint (eLogDebug, "Tunnels: Creating destination inbound tunnel...");
503 std::vector<std::shared_ptr<const i2p::data::IdentityEx> > peers;
504 if (SelectPeers (peers, true))
532 Path path;
533 if (SelectPeers (path, true))
505534 {
506535 std::shared_ptr<TunnelConfig> config;
507536 if (m_NumInboundHops > 0)
508537 {
509 std::reverse (peers.begin (), peers.end ());
510 config = std::make_shared<TunnelConfig> (peers);
511 }
512 auto tunnel = tunnels.CreateInboundTunnel (config, outboundTunnel);
513 tunnel->SetTunnelPool (shared_from_this ());
538 path.Reverse ();
539 config = std::make_shared<TunnelConfig> (path.peers, path.isShort);
540 }
541 auto tunnel = tunnels.CreateInboundTunnel (config, shared_from_this (), outboundTunnel);
514542 if (tunnel->IsEstablished ()) // zero hops
515543 TunnelCreated (tunnel);
516544 }
520548
521549 void TunnelPool::RecreateInboundTunnel (std::shared_ptr<InboundTunnel> tunnel)
522550 {
551 if (IsExploratory () || tunnel->IsSlow ()) // always create new exploratory tunnel or if slow
552 {
553 CreateInboundTunnel ();
554 return;
555 }
523556 auto outboundTunnel = GetNextOutboundTunnel ();
524557 if (!outboundTunnel)
525558 outboundTunnel = tunnels.GetNextOutboundTunnel ();
529562 {
530563 config = std::make_shared<TunnelConfig>(tunnel->GetPeers ());
531564 }
532 if (m_NumInboundHops == 0 || config)
533 {
534 auto newTunnel = tunnels.CreateInboundTunnel (config, outboundTunnel);
535 newTunnel->SetTunnelPool (shared_from_this());
565 if (!m_NumInboundHops || config)
566 {
567 auto newTunnel = tunnels.CreateInboundTunnel (config, shared_from_this(), outboundTunnel);
536568 if (newTunnel->IsEstablished ()) // zero hops
537569 TunnelCreated (newTunnel);
538570 }
546578 if (inboundTunnel)
547579 {
548580 LogPrint (eLogDebug, "Tunnels: Creating destination outbound tunnel...");
549 std::vector<std::shared_ptr<const i2p::data::IdentityEx> > peers;
550 if (SelectPeers (peers, false))
581 Path path;
582 if (SelectPeers (path, false))
551583 {
552584 std::shared_ptr<TunnelConfig> config;
553585 if (m_NumOutboundHops > 0)
554 config = std::make_shared<TunnelConfig>(peers, inboundTunnel->GetNextTunnelID (), inboundTunnel->GetNextIdentHash ());
555 auto tunnel = tunnels.CreateOutboundTunnel (config);
556 tunnel->SetTunnelPool (shared_from_this ());
557 if (tunnel->IsEstablished ()) // zero hops
586 config = std::make_shared<TunnelConfig>(path.peers, inboundTunnel->GetNextTunnelID (), inboundTunnel->GetNextIdentHash (), path.isShort);
587
588 std::shared_ptr<OutboundTunnel> tunnel;
589 if (path.isShort)
590 {
591 // TODO: implement it better
592 tunnel = tunnels.CreateOutboundTunnel (config, inboundTunnel->GetTunnelPool ());
593 tunnel->SetTunnelPool (shared_from_this ());
594 }
595 else
596 tunnel = tunnels.CreateOutboundTunnel (config, shared_from_this ());
597 if (tunnel && tunnel->IsEstablished ()) // zero hops
558598 TunnelCreated (tunnel);
559599 }
560600 else
566606
567607 void TunnelPool::RecreateOutboundTunnel (std::shared_ptr<OutboundTunnel> tunnel)
568608 {
609 if (IsExploratory () || tunnel->IsSlow ()) // always create new exploratory tunnel or if slow
610 {
611 CreateOutboundTunnel ();
612 return;
613 }
569614 auto inboundTunnel = GetNextInboundTunnel ();
570615 if (!inboundTunnel)
571616 inboundTunnel = tunnels.GetNextInboundTunnel ();
579624 }
580625 if (!m_NumOutboundHops || config)
581626 {
582 auto newTunnel = tunnels.CreateOutboundTunnel (config);
583 newTunnel->SetTunnelPool (shared_from_this ());
627 auto newTunnel = tunnels.CreateOutboundTunnel (config, shared_from_this ());
584628 if (newTunnel->IsEstablished ()) // zero hops
585629 TunnelCreated (newTunnel);
586630 }
594638 LogPrint (eLogDebug, "Tunnels: Creating paired inbound tunnel...");
595639 auto tunnel = tunnels.CreateInboundTunnel (
596640 m_NumOutboundHops > 0 ? std::make_shared<TunnelConfig>(outboundTunnel->GetInvertedPeers ()) : nullptr,
597 outboundTunnel);
598 tunnel->SetTunnelPool (shared_from_this ());
641 shared_from_this (), outboundTunnel);
599642 if (tunnel->IsEstablished ()) // zero hops
600643 TunnelCreated (tunnel);
601644 }
3535 class OutboundTunnel;
3636
3737 typedef std::shared_ptr<const i2p::data::IdentityEx> Peer;
38 typedef std::vector<Peer> Path;
38 struct Path
39 {
40 std::vector<Peer> peers;
41 bool isShort = true;
42
43 void Add (std::shared_ptr<const i2p::data::RouterInfo> r);
44 void Reverse ();
45 };
3946
4047 /** interface for custom tunnel peer selection algorithm */
4148 struct ITunnelPeerSelector
4653
4754
4855 typedef std::function<std::shared_ptr<const i2p::data::RouterInfo>(std::shared_ptr<const i2p::data::RouterInfo>, bool)> SelectHopFunc;
49 // standard peer selection algorithm
50 bool StandardSelectPeers(Path & path, int hops, bool inbound, SelectHopFunc nextHop);
51
56 bool StandardSelectPeers(Path & path, int numHops, bool inbound, SelectHopFunc nextHop);
57
5258 class TunnelPool: public std::enable_shared_from_this<TunnelPool> // per local destination
5359 {
5460 public:
7682 void ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg);
7783 void ProcessDeliveryStatus (std::shared_ptr<I2NPMessage> msg);
7884
85 bool IsExploratory () const;
7986 bool IsActive () const { return m_IsActive; };
8087 void SetActive (bool isActive) { m_IsActive = isActive; };
8188 void DetachTunnels ();
112119 void CreatePairedInboundTunnel (std::shared_ptr<OutboundTunnel> outboundTunnel);
113120 template<class TTunnels>
114121 typename TTunnels::value_type GetNextTunnel (TTunnels& tunnels, typename TTunnels::value_type excluded) const;
115 bool SelectPeers (std::vector<std::shared_ptr<const i2p::data::IdentityEx> >& hops, bool isInbound);
116 bool SelectExplicitPeers (std::vector<std::shared_ptr<const i2p::data::IdentityEx> >& hops, bool isInbound);
122 bool SelectPeers (Path& path, bool isInbound);
123 bool SelectExplicitPeers (Path& path, bool isInbound);
117124
118125 private:
119126
343343 if(fd > 0)
344344 {
345345 ifreq ifr;
346 strncpy(ifr.ifr_name, ifa->ifa_name, IFNAMSIZ); // set interface for query
346 strncpy(ifr.ifr_name, ifa->ifa_name, IFNAMSIZ-1); // set interface for query
347347 if(ioctl(fd, SIOCGIFMTU, &ifr) >= 0)
348348 mtu = ifr.ifr_mtu; // MTU
349349 else
554554 static const std::vector< std::pair<boost::asio::ip::address_v6::bytes_type, boost::asio::ip::address_v6::bytes_type> > reservedIPv6Ranges {
555555 address_pair_v6("2001:db8::", "2001:db8:ffff:ffff:ffff:ffff:ffff:ffff"),
556556 address_pair_v6("fc00::", "fdff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"),
557 address_pair_v6("fe80::", "febf:ffff:ffff:ffff:ffff:ffff:ffff:ffff")
557 address_pair_v6("fe80::", "febf:ffff:ffff:ffff:ffff:ffff:ffff:ffff"),
558 address_pair_v6("ff00::", "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"),
559 address_pair_v6("::", "::"),
560 address_pair_v6("::1", "::1")
558561 };
559562
560563 boost::asio::ip::address_v6::bytes_type ipv6_address = host.to_v6 ().to_bytes ();
176176
177177 SaveStateHelper (T& orig): m_Original (orig), m_Copy (orig) {};
178178 ~SaveStateHelper () { m_Original = m_Copy; };
179
179
180180 private:
181181
182182 T& m_Original;
183183 T m_Copy;
184 };
185
184 };
185
186186 namespace net
187187 {
188188 int GetMTU (const boost::asio::ip::address& localAddress);
00 /*
1 * Copyright (c) 2013-2020, The PurpleI2P Project
1 * Copyright (c) 2013-2021, The PurpleI2P Project
22 *
33 * This file is part of Purple i2pd project and licensed under BSD3
44 *
1515 #define MAKE_VERSION_NUMBER(a,b,c) ((a*100+b)*100+c)
1616
1717 #define I2PD_VERSION_MAJOR 2
18 #define I2PD_VERSION_MINOR 38
18 #define I2PD_VERSION_MINOR 39
1919 #define I2PD_VERSION_MICRO 0
2020 #define I2PD_VERSION_PATCH 0
2121 #define I2PD_VERSION MAKE_VERSION(I2PD_VERSION_MAJOR, I2PD_VERSION_MINOR, I2PD_VERSION_MICRO)
2929
3030 #define I2P_VERSION_MAJOR 0
3131 #define I2P_VERSION_MINOR 9
32 #define I2P_VERSION_MICRO 50
32 #define I2P_VERSION_MICRO 51
3333 #define I2P_VERSION_PATCH 0
3434 #define I2P_VERSION MAKE_VERSION(I2P_VERSION_MAJOR, I2P_VERSION_MINOR, I2P_VERSION_MICRO)
3535 #define I2P_VERSION_NUMBER MAKE_VERSION_NUMBER(I2P_VERSION_MAJOR, I2P_VERSION_MINOR, I2P_VERSION_MICRO)
456456 options[I2CP_PARAM_STREAMING_INITIAL_ACK_DELAY] = GetI2CPOption(section, I2CP_PARAM_STREAMING_INITIAL_ACK_DELAY, DEFAULT_INITIAL_ACK_DELAY);
457457 options[I2CP_PARAM_STREAMING_ANSWER_PINGS] = GetI2CPOption(section, I2CP_PARAM_STREAMING_ANSWER_PINGS, isServer ? DEFAULT_ANSWER_PINGS : false);
458458 options[I2CP_PARAM_LEASESET_TYPE] = GetI2CPOption(section, I2CP_PARAM_LEASESET_TYPE, DEFAULT_LEASESET_TYPE);
459 std::string encType = GetI2CPStringOption(section, I2CP_PARAM_LEASESET_ENCRYPTION_TYPE, isServer ? "" : "0,4");
459 std::string encType = GetI2CPStringOption(section, I2CP_PARAM_LEASESET_ENCRYPTION_TYPE, "0,4");
460460 if (encType.length () > 0) options[I2CP_PARAM_LEASESET_ENCRYPTION_TYPE] = encType;
461461 std::string privKey = GetI2CPStringOption(section, I2CP_PARAM_LEASESET_PRIV_KEY, "");
462462 if (privKey.length () > 0) options[I2CP_PARAM_LEASESET_PRIV_KEY] = privKey;
494494 options[I2CP_PARAM_LEASESET_TYPE] = value;
495495 if (i2p::config::GetOption(prefix + I2CP_PARAM_LEASESET_ENCRYPTION_TYPE, value))
496496 options[I2CP_PARAM_LEASESET_ENCRYPTION_TYPE] = value;
497 if (i2p::config::GetOption(prefix + I2CP_PARAM_LEASESET_PRIV_KEY, value) && !value.empty ())
498 options[I2CP_PARAM_LEASESET_PRIV_KEY] = value;
497499 }
498500
499501 void ClientContext::ReadTunnels ()
717719 {
718720 // udp server tunnel
719721 // TODO: hostnames
720 if (address.empty ()) address = "127.0.0.1";
721 auto localAddress = boost::asio::ip::address::from_string(address);
722722 boost::asio::ip::udp::endpoint endpoint(boost::asio::ip::address::from_string(host), port);
723 if (address.empty ())
724 {
725 if (!endpoint.address ().is_unspecified () && endpoint.address ().is_v6 ())
726 address = "::1";
727 else
728 address = "127.0.0.1";
729 }
730 auto localAddress = boost::asio::ip::address::from_string(address);
723731 auto serverTunnel = std::make_shared<I2PUDPServerTunnel>(name, localDestination, localAddress, endpoint, port, gzip);
724732 if(!isUniqueLocal)
725733 {
2727 #include "I2PTunnel.h"
2828 #include "Config.h"
2929 #include "HTTP.h"
30 #include "I18N.h"
3031
3132 namespace i2p {
3233 namespace proxy {
7071 void SentHTTPFailed(const boost::system::error_code & ecode);
7172 void HandleStreamRequestComplete (std::shared_ptr<i2p::stream::Stream> stream);
7273 /* error helpers */
73 void GenericProxyError(const char *title, const char *description);
74 void GenericProxyInfo(const char *title, const char *description);
74 void GenericProxyError(const std::string& title, const std::string& description);
75 void GenericProxyInfo(const std::string& title, const std::string& description);
7576 void HostNotFound(std::string & host);
7677 void SendProxyError(std::string & content);
7778
150151 Done(shared_from_this());
151152 }
152153
153 void HTTPReqHandler::GenericProxyError(const char *title, const char *description) {
154 void HTTPReqHandler::GenericProxyError(const std::string& title, const std::string& description) {
154155 std::stringstream ss;
155 ss << "<h1>Proxy error: " << title << "</h1>\r\n";
156 ss << "<h1>" << tr("Proxy error") << ": " << title << "</h1>\r\n";
156157 ss << "<p>" << description << "</p>\r\n";
157158 std::string content = ss.str();
158159 SendProxyError(content);
159160 }
160161
161 void HTTPReqHandler::GenericProxyInfo(const char *title, const char *description) {
162 void HTTPReqHandler::GenericProxyInfo(const std::string& title, const std::string& description) {
162163 std::stringstream ss;
163 ss << "<h1>Proxy info: " << title << "</h1>\r\n";
164 ss << "<h1>" << tr("Proxy info") << ": " << title << "</h1>\r\n";
164165 ss << "<p>" << description << "</p>\r\n";
165166 std::string content = ss.str();
166167 SendProxyError(content);
168169
169170 void HTTPReqHandler::HostNotFound(std::string & host) {
170171 std::stringstream ss;
171 ss << "<h1>Proxy error: Host not found</h1>\r\n"
172 << "<p>Remote host not found in router's addressbook</p>\r\n"
173 << "<p>You may try to find this host on jump services below:</p>\r\n"
172 ss << "<h1>" << tr("Proxy error: Host not found") << "</h1>\r\n"
173 << "<p>" << tr("Remote host not found in router's addressbook") << "</p>\r\n"
174 << "<p>" << tr("You may try to find this host on jump services below") << ":</p>\r\n"
174175 << "<ul>\r\n";
175176 for (const auto& js : jumpservices) {
176177 ss << " <li><a href=\"" << js.second << host << "\">" << js.first << "</a></li>\r\n";
215216 b64 = i2p::http::UrlDecode(value);
216217 // if we need update exists, request formed with update param
217218 if (params["update"] == "true") { len += std::strlen("&update=true"); confirm = true; }
219 if (pos != 0 && url.query[pos-1] == '&') { pos--; len++; } // if helper is not only one query option
218220 url.query.replace(pos, len, "");
219221 return true;
220222 }
267269
268270 if (m_req_len < 0) {
269271 LogPrint(eLogError, "HTTPProxy: unable to parse request");
270 GenericProxyError("Invalid request", "Proxy unable to parse your request");
272 GenericProxyError(tr("Invalid request"), tr("Proxy unable to parse your request"));
271273 return true; /* parse error */
272274 }
273275
282284 if (!m_Addresshelper)
283285 {
284286 LogPrint(eLogWarning, "HTTPProxy: addresshelper request rejected");
285 GenericProxyError("Invalid request", "addresshelper is not supported");
287 GenericProxyError(tr("Invalid request"), tr("addresshelper is not supported"));
286288 return true;
287289 }
288290 if (!i2p::client::context.GetAddressBook ().FindAddress (m_RequestURL.host) || m_Confirm)
291293 LogPrint (eLogInfo, "HTTPProxy: added address from addresshelper for ", m_RequestURL.host);
292294 std::string full_url = m_RequestURL.to_string();
293295 std::stringstream ss;
294 ss << "Host " << m_RequestURL.host << " added to router's addressbook from helper. "
295 << "Click <a href=\"" << full_url << "\">here</a> to proceed.";
296 GenericProxyInfo("Addresshelper found", ss.str().c_str());
296 ss << tr("Host") <<" " << m_RequestURL.host << " " << tr("added to router's addressbook from helper") << ". ";
297 ss << tr("Click here to proceed:") << " <a href=\"" << full_url << "\">" << tr("Continue") << "</a>.";
298 GenericProxyInfo(tr("Addresshelper found"), ss.str());
297299 return true; /* request processed */
298300 }
299301 else
300302 {
303 std::string full_url = m_RequestURL.to_string();
301304 std::stringstream ss;
302 ss << "Host " << m_RequestURL.host << " <font color=red>already in router's addressbook</font>. "
303 << "Click <a href=\"" << m_RequestURL.query << "?i2paddresshelper=" << jump << "&update=true\">here</a> to update record.";
304 GenericProxyInfo("Addresshelper found", ss.str().c_str());
305 ss << tr("Host") << " " << m_RequestURL.host << " <font color=red>" << tr("already in router's addressbook") << "</font>. ";
306 ss << tr("Click here to update record:") << " <a href=\"" << full_url << (full_url.find('?') != std::string::npos ? "&i2paddresshelper=" : "?i2paddresshelper=");
307 ss << jump << "&update=true\">" << tr("Continue") << "</a>.";
308 GenericProxyInfo(tr("Addresshelper found"), ss.str());
305309 return true; /* request processed */
306310 }
307311 }
314318 auto pos = uri.find(":");
315319 if(pos == std::string::npos || pos == uri.size() - 1)
316320 {
317 GenericProxyError("Invalid Request", "invalid request uri");
321 GenericProxyError(tr("Invalid request"), tr("invalid request uri"));
318322 return true;
319323 }
320324 else
357361 else
358362 {
359363 /* relative url and missing 'Host' header */
360 GenericProxyError("Invalid request", "Can't detect destination host from request");
364 GenericProxyError(tr("Invalid request"), tr("Can't detect destination host from request"));
361365 return true;
362366 }
363367 }
374378 if(m_ProxyURL.parse(m_OutproxyUrl))
375379 ForwardToUpstreamProxy();
376380 else
377 GenericProxyError("Outproxy failure", "bad outproxy settings");
381 GenericProxyError(tr("Outproxy failure"), tr("bad outproxy settings"));
378382 } else {
379383 LogPrint (eLogWarning, "HTTPProxy: outproxy failure for ", dest_host, ": no outproxy enabled");
380 std::string message = "Host " + dest_host + " not inside I2P network, but outproxy is not enabled";
381 GenericProxyError("Outproxy failure", message.c_str());
384 std::stringstream ss; ss << tr("Host") << " " << dest_host << " " << tr("not inside I2P network, but outproxy is not enabled");
385 GenericProxyError(tr("Outproxy failure"), ss.str());
382386 }
383387 return true;
384388 }
466470 else
467471 {
468472 // unknown type, complain
469 GenericProxyError("unknown outproxy url", m_ProxyURL.to_string().c_str());
473 GenericProxyError(tr("unknown outproxy url"), m_ProxyURL.to_string());
470474 }
471475 }
472476
473477 void HTTPReqHandler::HandleUpstreamProxyResolved(const boost::system::error_code & ec, boost::asio::ip::tcp::resolver::iterator it, ProxyResolvedHandler handler)
474478 {
475 if(ec) GenericProxyError("cannot resolve upstream proxy", ec.message().c_str());
479 if(ec) GenericProxyError(tr("cannot resolve upstream proxy"), ec.message());
476480 else handler(*it);
477481 }
478482
480484 {
481485 if(!ec) {
482486 if(m_RequestURL.host.size() > 255) {
483 GenericProxyError("hostname too long", m_RequestURL.host.c_str());
487 GenericProxyError(tr("hostname too long"), m_RequestURL.host);
484488 return;
485489 }
486490 uint16_t port = m_RequestURL.port;
507511 reqsize += host.size();
508512 m_socks_buf[++reqsize] = 0;
509513 boost::asio::async_write(*m_proxysock, boost::asio::buffer(m_socks_buf, reqsize), boost::asio::transfer_all(), std::bind(&HTTPReqHandler::HandleSocksProxySendHandshake, this, std::placeholders::_1, std::placeholders::_2));
510 } else GenericProxyError("cannot connect to upstream socks proxy", ec.message().c_str());
514 } else GenericProxyError(tr("cannot connect to upstream socks proxy"), ec.message());
511515 }
512516
513517 void HTTPReqHandler::HandleSocksProxySendHandshake(const boost::system::error_code & ec, std::size_t bytes_transferred)
514518 {
515519 LogPrint(eLogDebug, "HTTPProxy: upstream socks handshake sent");
516 if(ec) GenericProxyError("Cannot negotiate with socks proxy", ec.message().c_str());
520 if(ec) GenericProxyError(tr("Cannot negotiate with socks proxy"), ec.message());
517521 else m_proxysock->async_read_some(boost::asio::buffer(m_socks_buf, 8), std::bind(&HTTPReqHandler::HandleSocksProxyReply, this, std::placeholders::_1, std::placeholders::_2));
518522 }
519523
555559 }
556560 else
557561 {
558 GenericProxyError("CONNECT error", "Failed to Connect");
562 GenericProxyError(tr("CONNECT error"), tr("Failed to Connect"));
559563 }
560564 }
561565
566570 m_send_buf = m_ClientResponse.to_string();
567571 boost::asio::async_write(*m_sock, boost::asio::buffer(m_send_buf), boost::asio::transfer_all(), [&] (const boost::system::error_code & ec, std::size_t transferred)
568572 {
569 if(ec) GenericProxyError("socks proxy error", ec.message().c_str());
573 if(ec) GenericProxyError(tr("socks proxy error"), ec.message());
570574 else HandoverToUpstreamProxy();
571575 });
572576 } else {
574578 LogPrint(eLogDebug, "HTTPProxy: send ", m_send_buf.size(), " bytes");
575579 boost::asio::async_write(*m_proxysock, boost::asio::buffer(m_send_buf), boost::asio::transfer_all(), [&](const boost::system::error_code & ec, std::size_t transferred)
576580 {
577 if(ec) GenericProxyError("failed to send request to upstream", ec.message().c_str());
581 if(ec) GenericProxyError(tr("failed to send request to upstream"), ec.message());
578582 else HandoverToUpstreamProxy();
579583 });
580584 }
592596 ss << "error code: ";
593597 ss << (int) m_socks_buf[1];
594598 std::string msg = ss.str();
595 GenericProxyError("Socks Proxy error", msg.c_str());
596 }
597 }
598 else GenericProxyError("No Reply From socks proxy", ec.message().c_str());
599 GenericProxyError(tr("socks proxy error"), msg);
600 }
601 }
602 else GenericProxyError(tr("No Reply From socks proxy"), ec.message());
599603 }
600604
601605 void HTTPReqHandler::HandleUpstreamHTTPProxyConnect(const boost::system::error_code & ec)
602606 {
603607 if(!ec) {
604608 LogPrint(eLogDebug, "HTTPProxy: connected to http upstream");
605 GenericProxyError("cannot connect", "http out proxy not implemented");
606 } else GenericProxyError("cannot connect to upstream http proxy", ec.message().c_str());
609 GenericProxyError(tr("cannot connect"), tr("http out proxy not implemented"));
610 } else GenericProxyError(tr("cannot connect to upstream http proxy"), ec.message());
607611 }
608612
609613 /* will be called after some data received from client */
636640 {
637641 if (!stream) {
638642 LogPrint (eLogError, "HTTPProxy: error when creating the stream, check the previous warnings for more info");
639 GenericProxyError("Host is down", "Can't create connection to requested host, it may be down. Please try again later.");
643 GenericProxyError(tr("Host is down"), tr("Can't create connection to requested host, it may be down. Please try again later."));
640644 return;
641645 }
642646 if (Kill())
543543 offset += 8; // date
544544 if (identity->Verify (buf, offset, buf + offset)) // signature
545545 {
546 bool isPublic = true;
547 if (params[I2CP_PARAM_DONT_PUBLISH_LEASESET] == "true") isPublic = false;
548546 if (!m_Destination)
549547 {
550548 m_Destination = m_Owner.IsSingleThread () ?
551 std::make_shared<I2CPDestination>(m_Owner.GetService (), shared_from_this (), identity, isPublic, params):
552 std::make_shared<RunnableI2CPDestination>(shared_from_this (), identity, isPublic, params);
549 std::make_shared<I2CPDestination>(m_Owner.GetService (), shared_from_this (), identity, true, params):
550 std::make_shared<RunnableI2CPDestination>(shared_from_this (), identity, true, params);
553551 SendSessionStatusMessage (1); // created
554552 LogPrint (eLogDebug, "I2CP: session ", m_SessionID, " created");
555553 m_Destination->Start ();
6161 };
6262
6363 // params
64 const char I2CP_PARAM_DONT_PUBLISH_LEASESET[] = "i2cp.dontPublishLeaseSet";
6564 const char I2CP_PARAM_MESSAGE_RELIABILITY[] = "i2cp.messageReliability";
6665
6766 class I2CPSession;
604604 {
605605 if (!ecode)
606606 {
607 auto addr = (*it).endpoint ().address ();
607 bool found = false;
608 boost::asio::ip::tcp::endpoint ep;
609 if (m_LocalAddress)
610 {
611 boost::asio::ip::tcp::resolver::iterator end;
612 while (it != end)
613 {
614 ep = *it;
615 if (!ep.address ().is_unspecified ())
616 {
617 if (ep.address ().is_v4 ())
618 {
619 if (m_LocalAddress->is_v4 ()) found = true;
620 }
621 else if (ep.address ().is_v6 ())
622 {
623 if (i2p::util::net::IsYggdrasilAddress (ep.address ()))
624 {
625 if (i2p::util::net::IsYggdrasilAddress (*m_LocalAddress))
626 found = true;
627 }
628 else if (m_LocalAddress->is_v6 ())
629 found = true;
630 }
631 }
632 if (found) break;
633 it++;
634 }
635 }
636 else
637 {
638 found = true;
639 ep = *it; // first available
640 }
641 if (!found)
642 {
643 LogPrint (eLogError, "I2PTunnel: Unable to resolve to compatible address");
644 return;
645 }
646
647 auto addr = ep.address ();
608648 LogPrint (eLogInfo, "I2PTunnel: server tunnel ", (*it).host_name (), " has been resolved to ", addr);
609649 m_Endpoint.address (addr);
610650 Accept ();
7878 if(!inbound && m_RemoteLeaseSet)
7979 {
8080 if(m_RemoteLeaseSet->IsExpired())
81 {
8281 ResolveCurrentLeaseSet();
83 }
8482 if(m_RemoteLeaseSet && !m_RemoteLeaseSet->IsExpired())
8583 {
8684 // remote lease set is good
8785 auto leases = m_RemoteLeaseSet->GetNonExpiredLeases();
8886 // pick lease
8987 std::shared_ptr<i2p::data::RouterInfo> obep;
90 while(!obep && leases.size() > 0) {
88 while(!obep && leases.size() > 0)
89 {
9190 auto idx = rand() % leases.size();
9291 auto lease = leases[idx];
9392 obep = i2p::data::netdb.FindRouter(lease->tunnelGateway);
9493 leases.erase(leases.begin()+idx);
9594 }
96 if(obep) {
97 path.push_back(obep->GetRouterIdentity());
95 if(obep)
96 {
97 path.Add (obep);
9898 LogPrint(eLogDebug, "Destination: found OBEP matching IBGW");
9999 } else
100100 LogPrint(eLogWarning, "Destination: could not find proper IBGW for matched outbound tunnel");
12061206 void SAMSingleSession::StopLocalDestination ()
12071207 {
12081208 localDestination->Release ();
1209 // stop accepting new streams
12091210 localDestination->StopAcceptingStreams ();
1211 // terminate existing streams
1212 auto s = localDestination->GetStreamingDestination (); // TODO: take care about datagrams
1213 if (s) s->Stop ();
12101214 }
12111215
12121216 void SAMMasterSession::Close ()
0 package api
1
2 /*
3 * Copyright (c) 2013-2020, The PurpleI2P Project
4 *
5 * This file is part of Purple i2pd project and licensed under BSD3
6 *
7 * See full license text in LICENSE file at top of project tree
8 */
9
10 /*
11 #cgo CXXFLAGS: -I${SRCDIR}/../i18n -I${SRCDIR}/../libi2pd_client -I${SRCDIR}/../libi2pd -g -Wall -Wextra -Wno-unused-parameter -pedantic -Wno-psabi -fPIC -D__AES__ -maes
12 #cgo LDFLAGS: -L${SRCDIR}/../ -l:libi2pd.a -l:libi2pdlang.a -latomic -lcrypto -lssl -lz -lboost_system -lboost_date_time -lboost_filesystem -lboost_program_options -lpthread -lstdc++
13 */
14 import "C"
0 // See swig.org for more inteface options,
1 // e.g. map std::string to Go string
2
3 %{
4 #include "capi.h"
5 %}
6
7 %include "capi.h"
0 /*
1 * Copyright (c) 2013-2020, The PurpleI2P Project
2 *
3 * This file is part of Purple i2pd project and licensed under BSD3
4 *
5 * See full license text in LICENSE file at top of project tree
6 */
7
8 #include "api.h"
9 #include "capi.h"
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13 #include <assert.h>
14
15
16 // Uses the example from: https://stackoverflow.com/a/9210560
17 // See also https://stackoverflow.com/questions/9210528/split-string-with-delimiters-in-c/9210560#
18 // Does not handle consecutive delimiters, this is only for passing
19 // lists of arguments by value to InitI2P from C_InitI2P
20 char** str_split(char* a_str, const char a_delim)
21 {
22 char** result = 0;
23 size_t count = 0;
24 char* tmp = a_str;
25 char* last_comma = 0;
26 char delim[2];
27 delim[0] = a_delim;
28 delim[1] = 0;
29
30 /* Count how many elements will be extracted. */
31 while (*tmp)
32 {
33 if (a_delim == *tmp)
34 {
35 count++;
36 last_comma = tmp;
37 }
38 tmp++;
39 }
40
41 /* Add space for trailing token. */
42 count += last_comma < (a_str + strlen(a_str) - 1);
43
44 /* Add space for terminating null string so caller
45 knows where the list of returned strings ends. */
46 count++;
47
48 result = (char**) malloc(sizeof(char*) * count);
49
50 if (result)
51 {
52 size_t idx = 0;
53 char* token = strtok(a_str, delim);
54
55 while (token)
56 {
57 assert(idx < count);
58 *(result + idx++) = strdup(token);
59 token = strtok(0, delim);
60 }
61 assert(idx == count - 1);
62 *(result + idx) = 0;
63 }
64
65 return result;
66 }
67
68
69 #ifdef __cplusplus
70 extern "C" {
71 #endif
72
73 void C_InitI2P (int argc, char argv[], const char * appName)
74 {
75 const char* delim = " ";
76 char* vargs = strdup(argv);
77 char** args = str_split(vargs, *delim);
78 std::cout << argv;
79 return i2p::api::InitI2P(argc, args, appName);
80 }
81
82 void C_TerminateI2P ()
83 {
84 return i2p::api::TerminateI2P();
85 }
86
87 void C_StartI2P ()
88 {
89 std::shared_ptr<std::ostream> logStream;
90 return i2p::api::StartI2P(logStream);
91 }
92
93 void C_StopI2P ()
94 {
95 return i2p::api::StopI2P();
96 }
97
98 void C_RunPeerTest ()
99 {
100 return i2p::api::RunPeerTest();
101 }
102
103 #ifdef __cplusplus
104 }
105 #endif
0 /*
1 * Copyright (c) 2013-2020, The PurpleI2P Project
2 *
3 * This file is part of Purple i2pd project and licensed under BSD3
4 *
5 * See full license text in LICENSE file at top of project tree
6 */
7
8 #ifndef CAPI_H__
9 #define CAPI_H__
10
11 #ifdef __cplusplus
12 extern "C" {
13 #endif
14
15 // initialization start and stop
16 void C_InitI2P (int argc, char argv[], const char * appName);
17 //void C_InitI2P (int argc, char** argv, const char * appName);
18 void C_TerminateI2P ();
19 void C_StartI2P ();
20 // write system log to logStream, if not specified to <appName>.log in application's folder
21 void C_StopI2P ();
22 void C_RunPeerTest (); // should be called after UPnP
23
24 #ifdef __cplusplus
25 }
26 #endif
27
28 #endif