Compare commits

..

111 Commits

Author SHA1 Message Date
R4SAS
a7a882582f [gha] test again with 20.04
Signed-off-by: R4SAS <r4sas@i2pmail.org>
2023-01-22 20:42:32 +03:00
R4SAS
250696a7b5 [gha] revert to 18.04, change static build options
Signed-off-by: R4SAS <r4sas@i2pmail.org>
2023-01-22 20:34:44 +03:00
R4SAS
af7905744e [gha] update ubuntu build base to 20.04
Signed-off-by: R4SAS <r4sas@i2pmail.org>
2023-01-22 19:12:01 +03:00
R4SAS
db2364e9aa [gha] add ubuntu static build
Signed-off-by: R4SAS <r4sas@i2pmail.org>
2023-01-22 19:03:49 +03:00
orignal
067fb45a25 exclude router from tunnel build for 2.5 minutes if doesn't reply too often 2023-01-21 19:40:43 -05:00
R4SAS
ac287a896c [websonsole] use a function to format the amount of tunnel traffic
Signed-off-by: R4SAS <r4sas@i2pmail.org>
2023-01-20 23:03:00 +00:00
R4SAS
8baf62eb2c [websonsole] fix int concatenation with char strings
Signed-off-by: R4SAS <r4sas@i2pmail.org>
2023-01-21 01:55:43 +03:00
R4SAS
e1ec79daf2 [webconsole] format transit tunnels with table
Signed-off-by: R4SAS <r4sas@i2pmail.org>
2023-01-21 00:51:07 +03:00
orignal
d4426118c5 exclude router from tunnel build for 2.5 minutes if declined 2023-01-20 15:34:40 -05:00
orignal
64fe56aa07 Merge pull request #1854 from Vort/server_forwards_style
use correct style for Server Forwards section
2023-01-20 11:58:10 -05:00
Vort
47eb49c34e use correct style for Server Forwards section 2023-01-20 18:52:56 +02:00
orignal
cd6d86c8c3 make sure that async CreateStream complete 2023-01-19 13:40:12 -05:00
R4SAS
84d4e074ce add loglevel checker, fix fields passing to translated string formatter
Signed-off-by: R4SAS <r4sas@i2pmail.org>
2023-01-19 08:33:03 +03:00
R4SAS
a0e71c4173 [i18n] update strings and translation file
Signed-off-by: R4SAS <r4sas@i2pmail.org>
2023-01-19 07:46:54 +03:00
R4SAS
533c8a8a55 [i18n] set decimal point based on language
Signed-off-by: R4SAS <r4sas@i2pmail.org>
2023-01-19 06:40:32 +03:00
R4SAS
a57ae4dc56 [i18n] add sweedish translation
Translation author: corona@mail.i2p

Signed-off-by: R4SAS <r4sas@i2pmail.org>
2023-01-19 05:53:29 +03:00
R4SAS
88dfe3ca4e [i18n] fix build on macos
Signed-off-by: R4SAS <r4sas@i2pmail.org>
2023-01-18 18:56:21 +03:00
R4SAS
d68c7f8ea7 [i18n] fix build on macos
Signed-off-by: R4SAS <r4sas@i2pmail.org>
2023-01-18 08:19:45 +03:00
R4SAS
e8ace998ba [i18n] add support of string formatting
Signed-off-by: R4SAS <r4sas@i2pmail.org>
2023-01-18 07:21:44 +03:00
orignal
e8be39af17 memrory pool for SSU2IncompleteMessage 2023-01-17 21:32:36 -05:00
orignal
7196db09d6 Merge pull request #1852 from freeacetone/openssl
HTTPProxy message stream correcting and comments
2023-01-17 07:48:05 -05:00
acetone
b290ee1aa0 Cfg example: verbose comments for Web Console auth and addresshelper for public proxy 2023-01-17 09:00:11 +03:00
acetone
d105ab11af Joining two strings to one and correct comments 2023-01-17 08:45:18 +03:00
orignal
bc888167a7 use linked list for out of sequence fragments 2023-01-16 21:40:23 -05:00
R4SAS
6ca6591c43 [make] set PREFIX from DESTDIR if it present
Signed-off-by: R4SAS <r4sas@i2pmail.org>
2023-01-17 01:30:31 +03:00
orignal
36cb707e47 Merge pull request #1849 from freeacetone/openssl
Addresshelper updating: referer check
2023-01-16 08:33:07 -05:00
acetone
013d5ff74f Addresshelper request validation by Refer-header 2023-01-16 16:16:16 +03:00
acetone
9af5a90757 Copyright year updated 2023-01-16 13:57:15 +03:00
acetone
d8b6f5438c log typo 2023-01-16 13:56:36 +03:00
acetone
10030a4e0d Addresshelper updating: referer check 2023-01-16 13:31:13 +03:00
orignal
993dc72ce6 use separate pointer to second fragment of incomplete message 2023-01-15 22:50:54 -05:00
orignal
324ace103b memoery pool for fragments 2023-01-14 17:05:09 -05:00
orignal
d530269e4f try to insert received msgid instead lookup 2023-01-13 19:23:26 -05:00
orignal
7146a4dbae check if session socket was closed before tunnels were built 2023-01-12 15:41:57 -05:00
R4SAS
f79900653b Update README.md 2023-01-12 15:49:02 +03:00
orignal
f172f44f32 Merge pull request #1833 from TomasGlgg/feature
Использование скользящего среднего для рассчета tunnel creation success rate
2023-01-11 15:36:37 -05:00
orignal
f34abe60fa 2.45.1 2023-01-11 13:52:20 -05:00
orignal
a3c305032a don't set Firewalled upon SessionCreated if ports mismatch 2023-01-08 08:25:23 -05:00
orignal
2921eaa055 differentiate symmetric and full cone NAT 2023-01-07 12:11:51 -05:00
orignal
1cc68ea402 differentiate symmetric and full cone NAT 2023-01-07 12:06:26 -05:00
orignal
c18e8f6c78 drop too long LeaseSet without processing 2023-01-07 10:54:49 -05:00
orignal
e59ca8420e temporary change back to C++17 2023-01-06 18:20:26 -05:00
orignal
a6f9a56e40 support C++20 2023-01-06 14:08:39 -05:00
R4SAS
4011502f5f [docker] put config in correct place
Signed-off-by: R4SAS <r4sas@i2pmail.org>
2023-01-06 03:59:03 +03:00
orignal
acd6af709e don't use netdb memory pool for local RouterInfo 2023-01-05 18:16:36 -05:00
orignal
55704ece3a drop duplicated I2NP messages 2023-01-05 15:33:41 -05:00
orignal
0d3ede56cb reject duplicated trnsit tunnel 2023-01-05 11:59:47 -05:00
R4SAS
06fae976b3 [style] update editorconfig
Signed-off-by: R4SAS <r4sas@i2pmail.org>
2023-01-04 17:10:06 +03:00
orignal
e95035c143 Merge pull request #1835 from kleenexi2p/openssl
Fix segfault when UPnP is enabled
2023-01-04 08:45:17 -05:00
kleenex
55be5c74f0 Fix segfault when UPnP is enabled
Added null check.

Signed-off-by: kleenex <kleenex@i2pmail.org>
2023-01-04 22:41:54 +09:00
R4SAS
a1c16e129d [rpm] fix date
Signed-off-by: R4SAS <r4sas@i2pmail.org>
2023-01-04 16:28:42 +03:00
R4SAS
321c7cb7cf [docs] note about doubled transit tunnels limit for floodfill mode
Signed-off-by: R4SAS <r4sas@i2pmail.org>
2023-01-03 21:42:41 +03:00
R4SAS
5f8d45154d [docs] update transit limit description in config file
Signed-off-by: R4SAS <r4sas@i2pmail.org>
2023-01-03 18:34:49 +00:00
R4SAS
503f522cc3 [style] clean trailing spaces and tabs
Signed-off-by: R4SAS <r4sas@i2pmail.org>
2023-01-03 21:25:19 +03:00
R4SAS
22179400c7 2.45.0
Signed-off-by: R4SAS <r4sas@i2pmail.org>
2023-01-03 18:16:34 +00:00
Tomas Globis
66f82cb43f Use moving average to calculate tunnel creation success rate 2023-01-03 03:07:07 +03:00
orignal
1df67bd43c 2.45.0 2023-01-02 18:59:13 -05:00
orignal
d5b03f214b double default number of transit tunnels for floodfill 2023-01-02 15:03:00 -05:00
R4SAS
cfb773351d [debian] update packaging files
Signed-off-by: R4SAS <r4sas@i2pmail.org>
2023-01-02 21:36:51 +03:00
orignal
6942c20879 don't select overloaded peer 2023-01-01 19:42:40 -05:00
orignal
7b341d5d30 update and show send queue size for transports 2023-01-01 16:03:53 -05:00
R4SAS
e93718456f [debian] update patch series list
Signed-off-by: R4SAS <r4sas@i2pmail.org>
2023-01-01 14:32:53 +03:00
R4SAS
af838196be [debian] remove #1210 patch
Signed-off-by: R4SAS <r4sas@i2pmail.org>
2023-01-01 14:30:04 +03:00
R4SAS
f8ba5b8c63 increase nofile limit in service
Signed-off-by: R4SAS <r4sas@i2pmail.org>
2023-01-01 14:29:58 +03:00
R4SAS
446bbf6b93 Merge pull request #1812 from barracuda156/tests
Add implementation of tests with CMake, plus a few minor fixes
2023-01-01 10:55:29 +00:00
R4SAS
2c19da9aa7 fix warnings about unused code
Signed-off-by: R4SAS <r4sas@i2pmail.org>
2023-01-01 13:48:41 +03:00
R4SAS
01fc21ffb9 [webconsole] remove newline break
Signed-off-by: R4SAS <r4sas@i2pmail.org>
2022-12-31 11:55:29 +00:00
orignal
3e3e2c41bd unlock mutex for good after wait 2022-12-30 22:12:49 -05:00
orignal
cb139226df ilimit umner of simultaneous tunnel build requests per pool 2022-12-30 18:06:47 -05:00
orignal
84d6028454 limit number of message to handle at the time 2022-12-29 17:26:57 -05:00
orignal
126dc0ebe0 inseer incoming session into sessions list 2022-12-28 16:47:50 -05:00
orignal
099d9d977f terminate duplcated session properly 2022-12-28 16:47:12 -05:00
orignal
5a167316cb don't filter yggdrasil address 2022-12-27 22:24:20 -05:00
orignal
85e31f84ec reset termination timer for new incoming connection 2022-12-27 22:07:26 -05:00
orignal
edb7a0e23c Refuse dulicated incoming pending session from same IP 2022-12-27 21:54:32 -05:00
R4SAS
f401ccf5dd Merge pull request #1829 from rex4539/typos
Fix typos
2022-12-27 09:21:18 +00:00
Dimitris Apostolou
9f9e8bfa14 Fix typos 2022-12-27 10:44:23 +02:00
R4SAS
61168d5c9d Merge pull request #1828 from hack3ric/openssl
Fix CheckAtomic.cmake
2022-12-26 20:24:08 +00:00
R4SAS
8500aaa26f [gha] fix MacOS build
More info: https://github.com/orgs/Homebrew/discussions/3895

Signed-off-by: R4SAS <r4sas@i2pmail.org>
2022-12-26 09:43:29 +03:00
R4SAS
445cff0025 add editorconfig
Signed-off-by: R4SAS <r4sas@i2pmail.org>
2022-12-26 09:42:35 +03:00
orignal
99356fd24d make sure that AsycReceive complete 2022-12-25 15:35:00 -05:00
orignal
5ef5f5a170 don't create encryption for new transit tunnel immediately 2022-12-25 09:36:16 -05:00
Eric Long
54b7d4ef32 Fix CheckAtomic.cmake 2022-12-24 22:04:57 +08:00
R4SAS
6376328c98 [http proxy] do not remove X-Requested-With for *HttpRequest (#1816)
Signed-off-by: R4SAS <r4sas@i2pmail.org>
2022-12-22 17:55:51 +00:00
orignal
b6f83dfe9f set error before status 2022-12-22 08:36:07 -05:00
orignal
3f728149ab bump default max number of transit tunnels to 5000 2022-12-21 18:27:42 -05:00
orignal
36501fe31e change network status back to OK if port in msg 7 matches 2022-12-21 18:14:19 -05:00
orignal
e4ddc883d2 drop future RouterInfo and LeaseSet upon receive 2022-12-20 18:41:19 -05:00
orignal
5ac01ddce8 delete routers with timestmep from future 2022-12-20 15:23:54 -05:00
orignal
d3656fcb3f don't publish ::1 2022-12-20 14:16:23 -05:00
orignal
d6c101d261 try to handle RelayIntro again is Alice's RouterInfo not found 2022-12-19 13:28:21 -05:00
orignal
eeea02d834 rounded to seconds timestamps 2022-12-19 12:56:19 -05:00
R4SAS
0e0cd555eb [i18n] odd space in localized name
Signed-off-by: R4SAS <r4sas@i2pmail.org>
2022-12-19 01:00:50 +03:00
R4SAS
5dfe483152 [i18n] add Czech translation
Signed-off-by: R4SAS <r4sas@i2pmail.org>
2022-12-19 00:56:41 +03:00
R4SAS
c210553a39 getting rid of old c99 array designators
Signed-off-by: R4SAS <r4sas@i2pmail.org>
2022-12-18 23:26:18 +03:00
R4SAS
a315e4ce62 more overrides
Signed-off-by: R4SAS <r4sas@i2pmail.org>
2022-12-18 22:53:14 +03:00
R4SAS
96cfd9acc2 fix override
Signed-off-by: R4SAS <r4sas@i2pmail.org>
2022-12-18 22:37:26 +03:00
R4SAS
d869bb25ed fix missing override warning
Signed-off-by: R4SAS <r4sas@i2pmail.org>
2022-12-18 22:32:23 +03:00
orignal
476e6aae35 don't iterate through addresses when it's not necessary 2022-12-16 19:57:19 -05:00
orignal
d30ee99cf1 fixed crash 2022-12-16 15:33:56 -05:00
orignal
84d9c8f1b8 access to RouterInfo's addresses by index 2022-12-16 15:12:30 -05:00
orignal
df737a65b2 SessionRequest must be min 88 bytes 2022-12-15 15:08:56 -05:00
orignal
c5230ca44b don't accept incoming session from invalid endpoint 2022-12-14 15:44:31 -05:00
R4SAS
3471e6fe16 use deadline_timer for bandwidth calculation timer
Signed-off-by: R4SAS <r4sas@i2pmail.org>
2022-12-13 23:25:16 +03:00
R4SAS
f1437feede SSU2: handle standard network errors more correctly
Signed-off-by: R4SAS <r4sas@i2pmail.org>
2022-12-13 22:04:21 +03:00
R4SAS
0d523bd2a6 use 15s average bw for transit limits check
Signed-off-by: R4SAS <r4sas@i2pmail.org>
2022-12-13 22:04:20 +03:00
R4SAS
8943200ffa calculate bandwidth every 1 and 15 seconds
Signed-off-by: R4SAS <r4sas@i2pmail.org>
2022-12-13 22:04:19 +03:00
orignal
a902d68669 enable compressible padding 2022-12-12 18:36:29 -05:00
orignal
f6ca7c19af allow zero length padding 2022-12-12 14:18:36 -05:00
barracuda156
aa5e6400e4 CMakeLists: use a fix for atomics on macOS PPC, not PPC in general 2022-11-26 06:35:28 +08:00
barracuda156
61bcfebcc8 Add CMake support for tests 2022-11-26 06:32:56 +08:00
102 changed files with 2714 additions and 1496 deletions

32
.editorconfig Normal file
View File

@@ -0,0 +1,32 @@
# editorconfig.org
root = true
[*]
# Unix style files
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[Makefile,Makefile.*]
indent_style = tab
indent_size = 4
[*.cmd]
indent_style = space
indent_size = 2
end_of_line = crlf
[*.{h,cpp}]
indent_style = tab
indent_size = 4
[*.rc]
indent_style = space
indent_size = 4
[*.{md,markdown}]
indent_style = space
indent_size = 2
trim_trailing_whitespace = false

View File

@@ -14,6 +14,7 @@ jobs:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- name: install packages - name: install packages
run: | run: |
find /usr/local/bin -lname '*/Library/Frameworks/Python.framework/*' -delete
brew update brew update
brew install boost miniupnpc openssl@1.1 brew install boost miniupnpc openssl@1.1
- name: build application - name: build application

View File

@@ -5,7 +5,7 @@ on: [push, pull_request]
jobs: jobs:
build-make: build-make:
name: Make with USE_UPNP=${{ matrix.with_upnp }} name: Make with USE_UPNP=${{ matrix.with_upnp }}
runs-on: ubuntu-18.04 runs-on: ubuntu-20.04
strategy: strategy:
fail-fast: true fail-fast: true
matrix: matrix:
@@ -14,14 +14,13 @@ jobs:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- name: install packages - name: install packages
run: | run: |
sudo add-apt-repository ppa:mhier/libboost-latest
sudo apt-get update sudo apt-get update
sudo apt-get install build-essential libboost1.74-dev libminiupnpc-dev libssl-dev zlib1g-dev sudo apt-get install build-essential libboost-all-dev libminiupnpc-dev libssl-dev zlib1g-dev
- name: build application - name: build application
run: make USE_UPNP=${{ matrix.with_upnp }} -j3 run: make USE_UPNP=${{ matrix.with_upnp }} -j3
build-cmake: build-cmake:
name: CMake with -DWITH_UPNP=${{ matrix.with_upnp }} name: CMake with -DWITH_UPNP=${{ matrix.with_upnp }}
runs-on: ubuntu-18.04 runs-on: ubuntu-20.04
strategy: strategy:
fail-fast: true fail-fast: true
matrix: matrix:
@@ -30,11 +29,26 @@ jobs:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- name: install packages - name: install packages
run: | run: |
sudo add-apt-repository ppa:mhier/libboost-latest
sudo apt-get update sudo apt-get update
sudo apt-get install build-essential cmake libboost1.74-dev libminiupnpc-dev libssl-dev zlib1g-dev sudo apt-get install build-essential cmake libboost-all-dev libminiupnpc-dev libssl-dev zlib1g-dev
- name: build application - name: build application
run: | run: |
cd build cd build
cmake -DWITH_UPNP=${{ matrix.with_upnp }} . cmake -DWITH_UPNP=${{ matrix.with_upnp }} .
make -j3 make -j3
build-static:
name: Static build with UPnP
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v2
- name: install packages
run: |
sudo apt-get update
sudo apt-get install build-essential libboost-all-dev libminiupnpc-dev libssl-dev zlib1g-dev
- name: build application
run: make DEBUG=no USE_STATIC=yes USE_UPNP=yes -j`nproc`
- name: Upload artifacts
uses: actions/upload-artifact@v2
with:
name: i2pd-amd64-static
path: i2pd

View File

@@ -1,6 +1,50 @@
# for this file format description, # for this file format description,
# see https://github.com/olivierlacan/keep-a-changelog # see https://github.com/olivierlacan/keep-a-changelog
## [2.45.1] - 2023-01-11
### Added
- Full Cone NAT status error
### Changed
- Drop duplicated I2NP messages in SSU2
- Set rejection code 30 if tunnel with id already exists
- Network status is always OK if peer test msg 5 received
### Fixed
- UPnP crash if SSU2 or NTCP2 is disabled
- Crash on termination for some platforms
## [2.45.0] - 2023-01-03
### Added
- Test for Symmetric NAT with peer test msgs 6 and 7
- Webconsole "No Descriptors" router error state
- 1 and 15 seconds bandwidth calculation for i2pcontrol
- Show non-zero send queue size for transports in web console
- Compressible padding for I2P addresses
- Localization to Czech
- Don't accept incoming session from invalid/reserved addresses for NTCP2 and SSU2
- Limit simultaneous tunnel build requests by 4 per pool
### Changed
- Removed SSU support
- Reduced bandwidth calculation interval from 60 to 15 seconds
- Increased default max transit tunnels number from 2500 to 5000 or 10000 for floodfill
- Transit tunnels limit is doubled if floodfill mode is enabled
- NTCP2 and SSU2 timestamps are rounded to seconds
- Drop RouterInfos and LeaseSets with timestamp from future
- Don't delete unreachable routers if tunnel creation success rate is too low
- Refuse duplicated incoming pending NTCP2 session from same IP
- Don't send SSU2 termination again if termination received block received
- Handle standard network error for SSU2 without throwing an exception
- Don't select overloaded peer for next tunnel
- Remove "X-Requested-With" in HTTP Proxy for non-AJAX requests
### Fixed
- File descriptors leak
- Random crash on AddressBook update
- Crash if incorrect LeaseSet size
- Spamming to log if no descriptors
- ::1 address in RouterInfo
- SSU2 network error handling (especially for Windows)
- Race condition with pending outgoing SSU2 sessions
- RTT self-reduction for long-live streams
## [2.44.0] - 2022-11-20 ## [2.44.0] - 2022-11-20
### Added ### Added
- SSL connection for server I2P tunnels - SSL connection for server I2P tunnels
@@ -11,7 +55,7 @@
- SSU2 send and verify path challenge - SSU2 send and verify path challenge
- Configurable ssu2.mtu4 and ssu2.mtu6 - Configurable ssu2.mtu4 and ssu2.mtu6
### Changed ### Changed
- SSU2 is enbaled and SSU is disabled by default - SSU2 is enabled and SSU is disabled by default
- Separate network status and error - Separate network status and error
- Random selection between NTCP2 and SSU2 priority - Random selection between NTCP2 and SSU2 priority
- Added notbob.i2p to jump services - Added notbob.i2p to jump services

View File

@@ -47,6 +47,10 @@ else
LD_DEBUG = -s LD_DEBUG = -s
endif endif
ifneq (, $(DESTDIR))
PREFIX = $(DESTDIR)
endif
ifneq (, $(findstring darwin, $(SYS))) ifneq (, $(findstring darwin, $(SYS)))
DAEMON_SRC += $(DAEMON_SRC_DIR)/UnixDaemon.cpp DAEMON_SRC += $(DAEMON_SRC_DIR)/UnixDaemon.cpp
ifeq ($(HOMEBREW),1) ifeq ($(HOMEBREW),1)

View File

@@ -20,7 +20,11 @@ else ifeq ($(shell expr match ${CXXVER} "4\.[8-9]"),3) # gcc 4.8 - 4.9
else ifeq ($(shell expr match ${CXXVER} "[5-6]"),1) # gcc 5 - 6 else ifeq ($(shell expr match ${CXXVER} "[5-6]"),1) # gcc 5 - 6
NEEDED_CXXFLAGS += -std=c++11 NEEDED_CXXFLAGS += -std=c++11
LDLIBS = -latomic LDLIBS = -latomic
else ifeq ($(shell expr match ${CXXVER} "[1,7-9]"),1) # gcc >= 7 else ifeq ($(shell expr match ${CXXVER} "[7-9]"),1) # gcc 7 - 9
NEEDED_CXXFLAGS += -std=c++17
LDLIBS = -latomic
else ifeq ($(shell expr match ${CXXVER} "1[0-9]"),2) # gcc 10+
# NEEDED_CXXFLAGS += -std=c++20
NEEDED_CXXFLAGS += -std=c++17 NEEDED_CXXFLAGS += -std=c++17
LDLIBS = -latomic LDLIBS = -latomic
else # not supported else # not supported
@@ -29,34 +33,23 @@ endif
NEEDED_CXXFLAGS += -fPIC NEEDED_CXXFLAGS += -fPIC
ifeq ($(USE_STATIC),yes) LDLIBS += -lboost_system -lboost_date_time -lboost_filesystem -lboost_program_options -lssl -lcrypto -lz
# NOTE: on glibc you will get this warning:
# Using 'getaddrinfo' in statically linked applications requires at runtime
# the shared libraries from the glibc version used for linking
LIBDIR := /usr/lib/$(SYS)
LDLIBS += $(LIBDIR)/libboost_system.a
LDLIBS += $(LIBDIR)/libboost_date_time.a
LDLIBS += $(LIBDIR)/libboost_filesystem.a
LDLIBS += $(LIBDIR)/libboost_program_options.a
LDLIBS += $(LIBDIR)/libssl.a
LDLIBS += $(LIBDIR)/libcrypto.a
LDLIBS += $(LIBDIR)/libz.a
ifeq ($(USE_UPNP),yes)
LDLIBS += $(LIBDIR)/libminiupnpc.a
endif
LDLIBS += -lpthread -ldl
else
LDLIBS += -lcrypto -lssl -lz -lboost_system -lboost_date_time -lboost_filesystem -lboost_program_options -lpthread
ifeq ($(USE_UPNP),yes)
LDLIBS += -lminiupnpc
endif
endif
# UPNP Support (miniupnpc 1.5 and higher) # UPNP Support (miniupnpc 1.5 and higher)
ifeq ($(USE_UPNP),yes) ifeq ($(USE_UPNP),yes)
LDLIBS += -lminiupnpc
NEEDED_CXXFLAGS += -DUSE_UPNP NEEDED_CXXFLAGS += -DUSE_UPNP
endif endif
# NOTE: on glibc you will get this warning:
# Using 'getaddrinfo' in statically linked applications requires at runtime
# the shared libraries from the glibc version used for linking
ifeq ($(USE_STATIC),yes)
LDLIBS += -static -ldl -lpthread -static-libgcc -static-libstdc++
else
LDLIBS += -lpthread
endif
ifeq ($(USE_AESNI),yes) ifeq ($(USE_AESNI),yes)
ifneq (, $(findstring i386, $(SYS))$(findstring i686, $(SYS))$(findstring x86_64, $(SYS))) # only x86-based CPU supports that ifneq (, $(findstring i386, $(SYS))$(findstring i686, $(SYS))$(findstring x86_64, $(SYS))) # only x86-based CPU supports that
NEEDED_CXXFLAGS += -D__AES__ -maes NEEDED_CXXFLAGS += -D__AES__ -maes

View File

@@ -69,12 +69,12 @@ Build instructions:
**Supported systems:** **Supported systems:**
* 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) * GNU/Linux (Debian, Ubuntu, etc) - [![Build on Ubuntu](https://github.com/PurpleI2P/i2pd/actions/workflows/build.yml/badge.svg)](https://github.com/PurpleI2P/i2pd/actions/workflows/build.yml)
* 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/) * 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/)
* Alpine, ArchLinux, openSUSE, Gentoo, Debian, Ubuntu, etc. * Alpine, ArchLinux, openSUSE, Gentoo, etc.
* Windows - [![Build on Windows](https://github.com/PurpleI2P/i2pd/actions/workflows/build-windows.yml/badge.svg)](https://github.com/PurpleI2P/i2pd/actions/workflows/build-windows.yml) * Windows - [![Build on Windows](https://github.com/PurpleI2P/i2pd/actions/workflows/build-windows.yml/badge.svg)](https://github.com/PurpleI2P/i2pd/actions/workflows/build-windows.yml)
* Mac OS X - [![Build on OSX](https://github.com/PurpleI2P/i2pd/actions/workflows/build-osx.yml/badge.svg)](https://github.com/PurpleI2P/i2pd/actions/workflows/build-osx.yml) * Mac OS - [![Build on OSX](https://github.com/PurpleI2P/i2pd/actions/workflows/build-osx.yml/badge.svg)](https://github.com/PurpleI2P/i2pd/actions/workflows/build-osx.yml)
* Docker image - [![Build Status](https://img.shields.io/docker/cloud/build/purplei2p/i2pd)](https://hub.docker.com/r/purplei2p/i2pd/builds/) [![Build containers](https://github.com/PurpleI2P/i2pd/actions/workflows/docker.yml/badge.svg)](https://github.com/PurpleI2P/i2pd/actions/workflows/docker.yml) * Docker image - [![Build containers](https://github.com/PurpleI2P/i2pd/actions/workflows/docker.yml/badge.svg)](https://github.com/PurpleI2P/i2pd/actions/workflows/docker.yml)
* Snap - [![i2pd](https://snapcraft.io/i2pd/badge.svg)](https://snapcraft.io/i2pd) [![i2pd](https://snapcraft.io/i2pd/trending.svg?name=0)](https://snapcraft.io/i2pd) * Snap - [![i2pd](https://snapcraft.io/i2pd/badge.svg)](https://snapcraft.io/i2pd) [![i2pd](https://snapcraft.io/i2pd/trending.svg?name=0)](https://snapcraft.io/i2pd)
* FreeBSD - [![Build on FreeBSD](https://github.com/PurpleI2P/i2pd/actions/workflows/build-freebsd.yml/badge.svg)](https://github.com/PurpleI2P/i2pd/actions/workflows/build-freebsd.yml) * FreeBSD - [![Build on FreeBSD](https://github.com/PurpleI2P/i2pd/actions/workflows/build-freebsd.yml/badge.svg)](https://github.com/PurpleI2P/i2pd/actions/workflows/build-freebsd.yml)
* Android - [![Android CI](https://github.com/PurpleI2P/i2pd-android/actions/workflows/android.yml/badge.svg)](https://github.com/PurpleI2P/i2pd-android/actions/workflows/android.yml) * Android - [![Android CI](https://github.com/PurpleI2P/i2pd-android/actions/workflows/android.yml/badge.svg)](https://github.com/PurpleI2P/i2pd-android/actions/workflows/android.yml)

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2022, The PurpleI2P Project * Copyright (c) 2013-2023, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@@ -29,7 +29,7 @@ namespace util
setlocale(LC_CTYPE, ""); setlocale(LC_CTYPE, "");
SetConsoleCP(1251); SetConsoleCP(1251);
SetConsoleOutputCP(1251); SetConsoleOutputCP(1251);
setlocale(LC_ALL, "Russian"); //setlocale(LC_ALL, "Russian");
setlocale(LC_TIME, "C"); setlocale(LC_TIME, "C");
i2p::log::SetThrowFunction ([](const std::string& s) i2p::log::SetThrowFunction ([](const std::string& s)
@@ -61,7 +61,7 @@ namespace util
setlocale(LC_CTYPE, ""); setlocale(LC_CTYPE, "");
SetConsoleCP(1251); SetConsoleCP(1251);
SetConsoleOutputCP(1251); SetConsoleOutputCP(1251);
setlocale(LC_ALL, "Russian"); //setlocale(LC_ALL, "Russian");
setlocale(LC_TIME, "C"); setlocale(LC_TIME, "C");
#ifdef WIN32_APP #ifdef WIN32_APP
if (!i2p::win32::StartWin32App ()) return false; if (!i2p::win32::StartWin32App ()) return false;

View File

@@ -20,12 +20,17 @@ option(WITH_UPNP "Include support for UPnP client" OFF)
option(WITH_GIT_VERSION "Use git commit info as version" OFF) option(WITH_GIT_VERSION "Use git commit info as version" OFF)
option(WITH_ADDRSANITIZER "Build with address sanitizer unix only" OFF) option(WITH_ADDRSANITIZER "Build with address sanitizer unix only" OFF)
option(WITH_THREADSANITIZER "Build with thread sanitizer unix only" OFF) option(WITH_THREADSANITIZER "Build with thread sanitizer unix only" OFF)
option(BUILD_TESTING "Build tests" OFF)
IF(BUILD_TESTING)
enable_testing()
ENDIF()
# paths # paths
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake_modules") set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake_modules")
set(CMAKE_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/..") set(CMAKE_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/..")
#Handle paths nicely # Handle paths nicely
include(GNUInstallDirs) include(GNUInstallDirs)
# architecture # architecture
@@ -171,14 +176,12 @@ if(WITH_THREADSANITIZER)
endif() endif()
endif() endif()
# Use std::atomic instead of GCC builtins on macOS PowerPC:
# Enable usage of STD's Atomic instead of Boost's on PowerPC # For more information refer to: https://github.com/PurpleI2P/i2pd/issues/1726#issuecomment-1306335111
# For more information refer to https://github.com/PurpleI2P/i2pd/issues/1726#issuecomment-1306335111 if(APPLE AND CMAKE_OSX_ARCHITECTURES MATCHES "ppc")
if(ARCHITECTURE MATCHES "ppc")
add_definitions(-DBOOST_SP_USE_STD_ATOMIC) add_definitions(-DBOOST_SP_USE_STD_ATOMIC)
endif() endif()
# libraries # libraries
set(THREADS_PREFER_PTHREAD_FLAG ON) set(THREADS_PREFER_PTHREAD_FLAG ON)
find_package(Threads REQUIRED) find_package(Threads REQUIRED)
@@ -275,9 +278,13 @@ if(WITH_BINARY)
set(DL_LIB ${CMAKE_DL_LIBS}) set(DL_LIB ${CMAKE_DL_LIBS})
endif() endif()
target_link_libraries("${PROJECT_NAME}" libi2pd libi2pdclient libi2pdlang ${DL_LIB} ${Boost_LIBRARIES} OpenSSL::SSL OpenSSL::Crypto ${MINIUPNPC_LIBRARY} ZLIB::ZLIB Threads::Threads ${DL_LIB} ${CMAKE_REQUIRED_LIBRARIES}) target_link_libraries("${PROJECT_NAME}" libi2pd libi2pdclient libi2pdlang ${Boost_LIBRARIES} OpenSSL::SSL OpenSSL::Crypto ${MINIUPNPC_LIBRARY} ZLIB::ZLIB Threads::Threads ${DL_LIB} ${CMAKE_REQUIRED_LIBRARIES})
install(TARGETS "${PROJECT_NAME}" RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT Runtime) install(TARGETS "${PROJECT_NAME}" RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT Runtime)
set(APPS "\${CMAKE_INSTALL_PREFIX}/bin/${PROJECT_NAME}${CMAKE_EXECUTABLE_SUFFIX}") set(APPS "\${CMAKE_INSTALL_PREFIX}/bin/${PROJECT_NAME}${CMAKE_EXECUTABLE_SUFFIX}")
set(DIRS "${Boost_LIBRARY_DIR};${OPENSSL_INCLUDE_DIR}/../bin;${ZLIB_INCLUDE_DIR}/../bin;/mingw32/bin") set(DIRS "${Boost_LIBRARY_DIR};${OPENSSL_INCLUDE_DIR}/../bin;${ZLIB_INCLUDE_DIR}/../bin;/mingw32/bin")
endif() endif()
if(BUILD_TESTING)
add_subdirectory(${CMAKE_SOURCE_DIR}/tests ${CMAKE_CURRENT_BINARY_DIR}/tests)
endif()

View File

@@ -1,18 +1,23 @@
# atomic builtins are required for threading support. # atomic builtins are required for threading support.
INCLUDE(CheckCXXSourceCompiles) INCLUDE(CheckCXXSourceCompiles)
INCLUDE(CheckLibraryExists)
# Sometimes linking against libatomic is required for atomic ops, if # Sometimes linking against libatomic is required for atomic ops, if
# the platform doesn't support lock-free atomics. # the platform doesn't support lock-free atomics.
function(check_working_cxx_atomics varname) function(check_working_cxx_atomics varname)
set(OLD_CMAKE_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS}) set(OLD_CMAKE_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS})
set(CMAKE_REQUIRED_FLAGS "-std=c++11") set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -std=c++11")
CHECK_CXX_SOURCE_COMPILES(" CHECK_CXX_SOURCE_COMPILES("
#include <atomic> #include <atomic>
std::atomic<int> x; std::atomic<int> x;
std::atomic<short> y;
std::atomic<char> z;
int main() { int main() {
return x; ++z;
++y;
return ++x;
} }
" ${varname}) " ${varname})
set(CMAKE_REQUIRED_FLAGS ${OLD_CMAKE_REQUIRED_FLAGS}) set(CMAKE_REQUIRED_FLAGS ${OLD_CMAKE_REQUIRED_FLAGS})
@@ -27,6 +32,7 @@ function(check_working_cxx_atomics64 varname)
std::atomic<uint64_t> x (0); std::atomic<uint64_t> x (0);
int main() { int main() {
uint64_t i = x.load(std::memory_order_relaxed); uint64_t i = x.load(std::memory_order_relaxed);
(void)i;
return 0; return 0;
} }
" ${varname}) " ${varname})
@@ -34,15 +40,16 @@ int main() {
endfunction(check_working_cxx_atomics64) endfunction(check_working_cxx_atomics64)
# This isn't necessary on MSVC, so avoid command-line switch annoyance # Check for (non-64-bit) atomic operations.
# by only running on GCC-like hosts. if(MSVC)
if (LLVM_COMPILER_IS_GCC_COMPATIBLE) set(HAVE_CXX_ATOMICS_WITHOUT_LIB True)
else()
# First check if atomics work without the library. # First check if atomics work without the library.
check_working_cxx_atomics(HAVE_CXX_ATOMICS_WITHOUT_LIB) check_working_cxx_atomics(HAVE_CXX_ATOMICS_WITHOUT_LIB)
# If not, check if the library exists, and atomics work with it. # If not, check if the library exists, and atomics work with it.
if(NOT HAVE_CXX_ATOMICS_WITHOUT_LIB) if(NOT HAVE_CXX_ATOMICS_WITHOUT_LIB)
check_library_exists(atomic __atomic_fetch_add_4 "" HAVE_LIBATOMIC) check_library_exists(atomic __atomic_fetch_add_4 "" HAVE_LIBATOMIC)
if( HAVE_LIBATOMIC ) if(HAVE_LIBATOMIC)
list(APPEND CMAKE_REQUIRED_LIBRARIES "atomic") list(APPEND CMAKE_REQUIRED_LIBRARIES "atomic")
check_working_cxx_atomics(HAVE_CXX_ATOMICS_WITH_LIB) check_working_cxx_atomics(HAVE_CXX_ATOMICS_WITH_LIB)
if (NOT HAVE_CXX_ATOMICS_WITH_LIB) if (NOT HAVE_CXX_ATOMICS_WITH_LIB)
@@ -58,20 +65,20 @@ endif()
if(MSVC) if(MSVC)
set(HAVE_CXX_ATOMICS64_WITHOUT_LIB True) set(HAVE_CXX_ATOMICS64_WITHOUT_LIB True)
else() else()
# First check if atomics work without the library.
check_working_cxx_atomics64(HAVE_CXX_ATOMICS64_WITHOUT_LIB) check_working_cxx_atomics64(HAVE_CXX_ATOMICS64_WITHOUT_LIB)
endif() # If not, check if the library exists, and atomics work with it.
if(NOT HAVE_CXX_ATOMICS64_WITHOUT_LIB)
# If not, check if the library exists, and atomics work with it. check_library_exists(atomic __atomic_load_8 "" HAVE_CXX_LIBATOMICS64)
if(NOT HAVE_CXX_ATOMICS64_WITHOUT_LIB) if(HAVE_CXX_LIBATOMICS64)
check_library_exists(atomic __atomic_load_8 "" HAVE_CXX_LIBATOMICS64) list(APPEND CMAKE_REQUIRED_LIBRARIES "atomic")
if(HAVE_CXX_LIBATOMICS64) check_working_cxx_atomics64(HAVE_CXX_ATOMICS64_WITH_LIB)
list(APPEND CMAKE_REQUIRED_LIBRARIES "atomic") if (NOT HAVE_CXX_ATOMICS64_WITH_LIB)
check_working_cxx_atomics64(HAVE_CXX_ATOMICS64_WITH_LIB) message(FATAL_ERROR "Host compiler must support 64-bit std::atomic!")
if (NOT HAVE_CXX_ATOMICS64_WITH_LIB) endif()
message(FATAL_ERROR "Host compiler must support std::atomic!") else()
message(FATAL_ERROR "Host compiler appears to require libatomic for 64-bit operations, but cannot find it.")
endif() endif()
else()
message(FATAL_ERROR "Host compiler appears to require libatomic, but cannot find it.")
endif() endif()
endif() endif()
@@ -80,7 +87,6 @@ endif()
## assumes C++11 <atomic> works. ## assumes C++11 <atomic> works.
CHECK_CXX_SOURCE_COMPILES(" CHECK_CXX_SOURCE_COMPILES("
#ifdef _MSC_VER #ifdef _MSC_VER
#include <Intrin.h> /* Workaround for PR19898. */
#include <windows.h> #include <windows.h>
#endif #endif
int main() { int main() {

View File

@@ -0,0 +1,55 @@
# - Try to find the CHECK libraries
# Once done this will define
#
# CHECK_FOUND - system has check
# CHECK_INCLUDE_DIRS - the check include directory
# CHECK_LIBRARIES - check library
#
# Copyright (c) 2007 Daniel Gollub <gollub@b1-systems.de>
# Copyright (c) 2007-2009 Bjoern Ricks <bjoern.ricks@gmail.com>
#
# Redistribution and use is allowed according to the terms of the New
# BSD license.
# For details see the accompanying COPYING-CMAKE-SCRIPTS file.
INCLUDE( FindPkgConfig )
IF ( Check_FIND_REQUIRED )
SET( _pkgconfig_REQUIRED "REQUIRED" )
ELSE( Check_FIND_REQUIRED )
SET( _pkgconfig_REQUIRED "" )
ENDIF ( Check_FIND_REQUIRED )
IF ( CHECK_MIN_VERSION )
PKG_SEARCH_MODULE( CHECK ${_pkgconfig_REQUIRED} check>=${CHECK_MIN_VERSION} )
ELSE ( CHECK_MIN_VERSION )
PKG_SEARCH_MODULE( CHECK ${_pkgconfig_REQUIRED} check )
ENDIF ( CHECK_MIN_VERSION )
# Look for CHECK include dir and libraries
IF( NOT CHECK_FOUND AND NOT PKG_CONFIG_FOUND )
FIND_PATH( CHECK_INCLUDE_DIRS check.h )
FIND_LIBRARY( CHECK_LIBRARIES NAMES check )
IF ( CHECK_INCLUDE_DIRS AND CHECK_LIBRARIES )
SET( CHECK_FOUND 1 )
IF ( NOT Check_FIND_QUIETLY )
MESSAGE ( STATUS "Found CHECK: ${CHECK_LIBRARIES}" )
ENDIF ( NOT Check_FIND_QUIETLY )
ELSE ( CHECK_INCLUDE_DIRS AND CHECK_LIBRARIES )
IF ( Check_FIND_REQUIRED )
MESSAGE( FATAL_ERROR "Could NOT find CHECK" )
ELSE ( Check_FIND_REQUIRED )
IF ( NOT Check_FIND_QUIETLY )
MESSAGE( STATUS "Could NOT find CHECK" )
ENDIF ( NOT Check_FIND_QUIETLY )
ENDIF ( Check_FIND_REQUIRED )
ENDIF ( CHECK_INCLUDE_DIRS AND CHECK_LIBRARIES )
ENDIF( NOT CHECK_FOUND AND NOT PKG_CONFIG_FOUND )
# Hide advanced variables from CMake GUIs
MARK_AS_ADVANCED( CHECK_INCLUDE_DIRS CHECK_LIBRARIES )

View File

@@ -60,8 +60,8 @@ RUN apk update \
RUN apk --no-cache add boost-filesystem boost-system boost-program_options boost-date_time boost-thread boost-iostreams openssl miniupnpc musl-utils libstdc++ RUN apk --no-cache add boost-filesystem boost-system boost-program_options boost-date_time boost-thread boost-iostreams openssl miniupnpc musl-utils libstdc++
# 3. Copy preconfigured config file and entrypoint # 3. Copy preconfigured config file and entrypoint
COPY i2pd-docker.conf "$I2PD_HOME/i2pd.conf" COPY i2pd-docker.conf "$DATA_DIR/i2pd.conf"
RUN chown i2pd:nobody "$I2PD_HOME/i2pd.conf" RUN chown i2pd:nobody "$DATA_DIR/i2pd.conf"
COPY entrypoint.sh /entrypoint.sh COPY entrypoint.sh /entrypoint.sh
RUN chmod a+x /entrypoint.sh RUN chmod a+x /entrypoint.sh

File diff suppressed because it is too large Load Diff

View File

@@ -122,11 +122,13 @@ port = 7070
## Path to web console, default "/" ## Path to web console, default "/"
# webroot = / # webroot = /
## Uncomment following lines to enable Web Console authentication ## Uncomment following lines to enable Web Console authentication
## You should not use Web Console via public networks without additional encryption.
## HTTP authentication is not encryption layer!
# auth = true # auth = true
# user = i2pd # user = i2pd
# pass = changeme # pass = changeme
## Select webconsole language ## Select webconsole language
## Currently supported english (default), afrikaans, armenian, chinese, french, ## Currently supported english (default), afrikaans, armenian, chinese, czech, french,
## german, italian, russian, spanish, turkmen, ukrainian and uzbek languages ## german, italian, russian, spanish, turkmen, ukrainian and uzbek languages
# lang = english # lang = english
@@ -139,6 +141,8 @@ port = 4444
## Optional keys file for proxy local destination ## Optional keys file for proxy local destination
# keys = http-proxy-keys.dat # keys = http-proxy-keys.dat
## Enable address helper for adding .i2p domains with "jump URLs" (default: true) ## Enable address helper for adding .i2p domains with "jump URLs" (default: true)
## You should disable this feature if your i2pd HTTP Proxy is public,
## because anyone could spoof the short domain via addresshelper and forward other users to phishing links
# addresshelper = true # addresshelper = true
## Address of a proxy server inside I2P, which is used to visit regular Internet ## Address of a proxy server inside I2P, which is used to visit regular Internet
# outproxy = http://false.i2p # outproxy = http://false.i2p
@@ -237,8 +241,9 @@ verify = true
# subscriptions = http://reg.i2p/hosts.txt,http://identiguy.i2p/hosts.txt,http://stats.i2p/cgi-bin/newhosts.txt,http://rus.i2p/hosts.txt # subscriptions = http://reg.i2p/hosts.txt,http://identiguy.i2p/hosts.txt,http://stats.i2p/cgi-bin/newhosts.txt,http://rus.i2p/hosts.txt
[limits] [limits]
## Maximum active transit sessions (default:2500) ## Maximum active transit sessions (default: 5000)
# transittunnels = 2500 ## This value is doubled if floodfill mode is enabled!
# transittunnels = 5000
## Limit number of open file descriptors (0 - use system limit) ## Limit number of open file descriptors (0 - use system limit)
# openfiles = 0 # openfiles = 0
## Maximum size of corefile in Kb (0 - use system limit) ## Maximum size of corefile in Kb (0 - use system limit)

View File

@@ -29,7 +29,7 @@ SendSIGKILL=yes
#TimeoutStopSec=10m #TimeoutStopSec=10m
# If you have problems with hanging i2pd, you can try increase this # If you have problems with hanging i2pd, you can try increase this
LimitNOFILE=4096 LimitNOFILE=8192
# To enable write of coredump uncomment this # To enable write of coredump uncomment this
#LimitCORE=infinity #LimitCORE=infinity

View File

@@ -1,7 +1,7 @@
%define git_hash %(git rev-parse HEAD | cut -c -7) %define git_hash %(git rev-parse HEAD | cut -c -7)
Name: i2pd-git Name: i2pd-git
Version: 2.44.0 Version: 2.45.1
Release: git%{git_hash}%{?dist} Release: git%{git_hash}%{?dist}
Summary: I2P router written in C++ Summary: I2P router written in C++
Conflicts: i2pd Conflicts: i2pd
@@ -158,6 +158,12 @@ getent passwd i2pd >/dev/null || \
%changelog %changelog
* Wed Jan 11 2023 orignal <orignal@i2pmail.org> - 2.45.1
- update to 2.45.1
* Tue Jan 3 2023 orignal <orignal@i2pmail.org> - 2.45.0
- update to 2.45.0
* Sun Nov 20 2022 orignal <orignal@i2pmail.org> - 2.44.0 * Sun Nov 20 2022 orignal <orignal@i2pmail.org> - 2.44.0
- update to 2.44.0 - update to 2.44.0

View File

@@ -1,5 +1,5 @@
Name: i2pd Name: i2pd
Version: 2.44.0 Version: 2.45.1
Release: 1%{?dist} Release: 1%{?dist}
Summary: I2P router written in C++ Summary: I2P router written in C++
Conflicts: i2pd-git Conflicts: i2pd-git
@@ -155,6 +155,12 @@ getent passwd i2pd >/dev/null || \
%changelog %changelog
* Wed Jan 11 2023 orignal <orignal@i2pmail.org> - 2.45.1
- update to 2.45.1
* Tue Jan 3 2023 orignal <orignal@i2pmail.org> - 2.45.0
- update to 2.45.0
* Sun Nov 20 2022 orignal <orignal@i2pmail.org> - 2.44.0 * Sun Nov 20 2022 orignal <orignal@i2pmail.org> - 2.44.0
- update to 2.44.0 - update to 2.44.0

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2021-2022, The PurpleI2P Project * Copyright (c) 2021-2023, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@@ -43,7 +43,7 @@ a, .slide label {
color: var(--main-link-color); color: var(--main-link-color);
} }
a:hover, .slide label:hover, button[type=submit]:hover { a:hover, a.button.selected, .slide label:hover, button[type=submit]:hover {
color: var(--main-link-hover-color); color: var(--main-link-hover-color);
background: var(--main-link-color); background: var(--main-link-color);
} }

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2022, The PurpleI2P Project * Copyright (c) 2013-2023, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@@ -165,20 +165,21 @@ namespace util
i2p::transport::InitTransports (); i2p::transport::InitTransports ();
bool transit; i2p::config::GetOption("notransit", transit);
i2p::context.SetAcceptsTunnels (!transit);
uint16_t transitTunnels; i2p::config::GetOption("limits.transittunnels", transitTunnels);
SetMaxNumTransitTunnels (transitTunnels);
bool isFloodfill; i2p::config::GetOption("floodfill", isFloodfill); bool isFloodfill; i2p::config::GetOption("floodfill", isFloodfill);
if (isFloodfill) { if (isFloodfill)
{
LogPrint(eLogInfo, "Daemon: Router configured as floodfill"); LogPrint(eLogInfo, "Daemon: Router configured as floodfill");
i2p::context.SetFloodfill (true); i2p::context.SetFloodfill (true);
} }
else else
{
i2p::context.SetFloodfill (false); i2p::context.SetFloodfill (false);
}
bool transit; i2p::config::GetOption("notransit", transit);
i2p::context.SetAcceptsTunnels (!transit);
uint16_t transitTunnels; i2p::config::GetOption("limits.transittunnels", transitTunnels);
if (isFloodfill && i2p::config::IsDefault ("limits.transittunnels"))
transitTunnels *= 2; // double default number of transit tunnels for floodfill
SetMaxNumTransitTunnels (transitTunnels);
/* this section also honors 'floodfill' flag, if set above */ /* this section also honors 'floodfill' flag, if set above */
std::string bandwidth; i2p::config::GetOption("bandwidth", bandwidth); std::string bandwidth; i2p::config::GetOption("bandwidth", bandwidth);

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2022, The PurpleI2P Project * Copyright (c) 2013-2023, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@@ -87,8 +87,6 @@ namespace http {
const char HTTP_COMMAND_GET_REG_STRING[] = "get_reg_string"; const char HTTP_COMMAND_GET_REG_STRING[] = "get_reg_string";
const char HTTP_COMMAND_SETLANGUAGE[] = "setlanguage"; const char HTTP_COMMAND_SETLANGUAGE[] = "setlanguage";
const char HTTP_COMMAND_RELOAD_CSS[] = "reload_css"; const char HTTP_COMMAND_RELOAD_CSS[] = "reload_css";
const char HTTP_PARAM_SAM_SESSION_ID[] = "id";
const char HTTP_PARAM_ADDRESS[] = "address";
static std::string ConvertTime (uint64_t time) static std::string ConvertTime (uint64_t time)
{ {
@@ -105,18 +103,18 @@ namespace http {
int num; int num;
if ((num = seconds / 86400) > 0) { if ((num = seconds / 86400) > 0) {
s << num << " " << tr("day", "days", num) << ", "; s << ntr("%d day", "%d days", num, num) << ", ";
seconds -= num * 86400; seconds -= num * 86400;
} }
if ((num = seconds / 3600) > 0) { if ((num = seconds / 3600) > 0) {
s << num << " " << tr("hour", "hours", num) << ", "; s << ntr("%d hour", "%d hours", num, num) << ", ";
seconds -= num * 3600; seconds -= num * 3600;
} }
if ((num = seconds / 60) > 0) { if ((num = seconds / 60) > 0) {
s << num << " " << tr("minute", "minutes", num) << ", "; s << ntr("%d minute", "%d minutes", num, num) << ", ";
seconds -= num * 60; seconds -= num * 60;
} }
s << seconds << " " << tr("second", "seconds", seconds); s << ntr("%d second", "%d seconds", seconds, seconds);
} }
static void ShowTraffic (std::stringstream& s, uint64_t bytes) static void ShowTraffic (std::stringstream& s, uint64_t bytes)
@@ -124,11 +122,11 @@ namespace http {
s << std::fixed << std::setprecision(2); s << std::fixed << std::setprecision(2);
auto numKBytes = (double) bytes / 1024; auto numKBytes = (double) bytes / 1024;
if (numKBytes < 1024) if (numKBytes < 1024)
s << numKBytes << " " << tr(/* tr: Kibibit */ "KiB"); s << tr(/* tr: Kibibyte */ "%.2f KiB", numKBytes);
else if (numKBytes < 1024 * 1024) else if (numKBytes < 1024 * 1024)
s << numKBytes / 1024 << " " << tr(/* tr: Mebibit */ "MiB"); s << tr(/* tr: Mebibyte */ "%.2f MiB", numKBytes / 1024);
else else
s << numKBytes / 1024 / 1024 << " " << tr(/* tr: Gibibit */ "GiB"); s << tr(/* tr: Gibibyte */ "%.2f GiB", numKBytes / 1024 / 1024);
} }
static void ShowTunnelDetails (std::stringstream& s, enum i2p::tunnel::TunnelState eState, bool explr, int bytes) static void ShowTunnelDetails (std::stringstream& s, enum i2p::tunnel::TunnelState eState, bool explr, int bytes)
@@ -152,7 +150,8 @@ namespace http {
else stateText = tr("unknown"); else stateText = tr("unknown");
s << "<span class=\"tunnel " << state << "\"> " << stateText << ((explr) ? " (" + tr("exploratory") + ")" : "") << "</span>, "; s << "<span class=\"tunnel " << state << "\"> " << stateText << ((explr) ? " (" + tr("exploratory") + ")" : "") << "</span>, ";
s << " " << (int) (bytes / 1024) << "&nbsp;" << tr(/* tr: Kibibit */ "KiB") << "\r\n"; ShowTraffic(s, bytes);
s << "\r\n";
} }
static void SetLogLevel (const std::string& level) static void SetLogLevel (const std::string& level)
@@ -200,7 +199,7 @@ namespace http {
if (i2p::context.AcceptsTunnels () || i2p::tunnel::tunnels.CountTransitTunnels()) if (i2p::context.AcceptsTunnels () || i2p::tunnel::tunnels.CountTransitTunnels())
s << " <a href=\"" << webroot << "?page=" << HTTP_PAGE_TRANSIT_TUNNELS << "\">" << tr("Transit Tunnels") << "</a><br>\r\n"; s << " <a href=\"" << webroot << "?page=" << HTTP_PAGE_TRANSIT_TUNNELS << "\">" << tr("Transit Tunnels") << "</a><br>\r\n";
s << s <<
" <a href=\"" << webroot << "?page=" << HTTP_PAGE_TRANSPORTS << "\">" << tr ("Transports") << "</a><br>\r\n" " <a href=\"" << webroot << "?page=" << HTTP_PAGE_TRANSPORTS << "\">" << tr("Transports") << "</a><br>\r\n"
" <a href=\"" << webroot << "?page=" << HTTP_PAGE_I2P_TUNNELS << "\">" << tr("I2P tunnels") << "</a><br>\r\n"; " <a href=\"" << webroot << "?page=" << HTTP_PAGE_I2P_TUNNELS << "\">" << tr("I2P tunnels") << "</a><br>\r\n";
if (i2p::client::context.GetSAMBridge ()) if (i2p::client::context.GetSAMBridge ())
s << " <a href=\"" << webroot << "?page=" << HTTP_PAGE_SAM_SESSIONS << "\">" << tr("SAM sessions") << "</a><br>\r\n"; s << " <a href=\"" << webroot << "?page=" << HTTP_PAGE_SAM_SESSIONS << "\">" << tr("SAM sessions") << "</a><br>\r\n";
@@ -236,7 +235,6 @@ namespace http {
} }
if (error != eRouterErrorNone) if (error != eRouterErrorNone)
{ {
s << "<br>";
switch (error) switch (error)
{ {
case eRouterErrorClockSkew: case eRouterErrorClockSkew:
@@ -248,6 +246,9 @@ namespace http {
case eRouterErrorSymmetricNAT: case eRouterErrorSymmetricNAT:
s << " - " << tr("Symmetric NAT"); s << " - " << tr("Symmetric NAT");
break; break;
case eRouterErrorFullConeNAT:
s << " - " << tr("Full cone NAT");
break;
case eRouterErrorNoDescriptors: case eRouterErrorNoDescriptors:
s << " - " << tr("No Descriptors"); s << " - " << tr("No Descriptors");
break; break;
@@ -290,13 +291,13 @@ namespace http {
s << "<b>" << tr("Tunnel creation success rate") << ":</b> " << i2p::tunnel::tunnels.GetTunnelCreationSuccessRate () << "%<br>\r\n"; s << "<b>" << tr("Tunnel creation success rate") << ":</b> " << i2p::tunnel::tunnels.GetTunnelCreationSuccessRate () << "%<br>\r\n";
s << "<b>" << tr("Received") << ":</b> "; s << "<b>" << tr("Received") << ":</b> ";
ShowTraffic (s, i2p::transport::transports.GetTotalReceivedBytes ()); ShowTraffic (s, i2p::transport::transports.GetTotalReceivedBytes ());
s << " (" << (double) i2p::transport::transports.GetInBandwidth15s () / 1024 << " " << tr(/* tr: Kibibit/s */ "KiB/s") << ")<br>\r\n"; s << " (" << tr(/* tr: Kibibyte/s */ "%.2f KiB/s", (double) i2p::transport::transports.GetInBandwidth15s () / 1024) << ")<br>\r\n";
s << "<b>" << tr("Sent") << ":</b> "; s << "<b>" << tr("Sent") << ":</b> ";
ShowTraffic (s, i2p::transport::transports.GetTotalSentBytes ()); ShowTraffic (s, i2p::transport::transports.GetTotalSentBytes ());
s << " (" << (double) i2p::transport::transports.GetOutBandwidth15s () / 1024 << " " << tr(/* tr: Kibibit/s */ "KiB/s") << ")<br>\r\n"; s << " (" << tr(/* tr: Kibibyte/s */ "%.2f KiB/s", (double) i2p::transport::transports.GetOutBandwidth15s () / 1024) << ")<br>\r\n";
s << "<b>" << tr("Transit") << ":</b> "; s << "<b>" << tr("Transit") << ":</b> ";
ShowTraffic (s, i2p::transport::transports.GetTotalTransitTransmittedBytes ()); ShowTraffic (s, i2p::transport::transports.GetTotalTransitTransmittedBytes ());
s << " (" << (double) i2p::transport::transports.GetTransitBandwidth15s () / 1024 << " " << tr(/* tr: Kibibit/s */ "KiB/s") << ")<br>\r\n"; s << " (" << tr(/* tr: Kibibyte/s */ "%.2f KiB/s", (double) i2p::transport::transports.GetTransitBandwidth15s () / 1024) << ")<br>\r\n";
s << "<b>" << tr("Data path") << ":</b> " << i2p::fs::GetUTF8DataDir() << "<br>\r\n"; s << "<b>" << tr("Data path") << ":</b> " << i2p::fs::GetUTF8DataDir() << "<br>\r\n";
s << "<div class='slide'>"; s << "<div class='slide'>";
if ((outputFormat == OutputFormatEnum::forWebConsole) || !includeHiddenContent) { if ((outputFormat == OutputFormatEnum::forWebConsole) || !includeHiddenContent) {
@@ -315,6 +316,7 @@ namespace http {
{ {
for (const auto& address : *addresses) for (const auto& address : *addresses)
{ {
if (!address) continue;
s << "<tr>\r\n<td>"; s << "<tr>\r\n<td>";
switch (address->transportStyle) switch (address->transportStyle)
{ {
@@ -337,7 +339,7 @@ namespace http {
s << "<td>" << address->host.to_string() << ":" << address->port << "</td>\r\n"; s << "<td>" << address->host.to_string() << ":" << address->port << "</td>\r\n";
else else
{ {
s << "<td>" << tr("supported"); s << "<td>" << tr(/* tr: Shown when router doesn't publish itself and have "Firewalled" state */ "supported");
if (address->port) if (address->port)
s << " :" << address->port; s << " :" << address->port;
s << "</td>\r\n"; s << "</td>\r\n";
@@ -465,7 +467,7 @@ namespace http {
} }
s << "&#8658; " << it->GetTunnelID () << ":me"; s << "&#8658; " << it->GetTunnelID () << ":me";
if (it->LatencyIsKnown()) if (it->LatencyIsKnown())
s << " ( " << it->GetMeanLatency() << tr(/* tr: Milliseconds */ "ms") << " )"; s << " ( " << tr(/* tr: Milliseconds */ "%dms", it->GetMeanLatency()) << " )";
ShowTunnelDetails(s, it->GetState (), false, it->GetNumReceivedBytes ()); ShowTunnelDetails(s, it->GetState (), false, it->GetNumReceivedBytes ());
s << "</div>\r\n"; s << "</div>\r\n";
} }
@@ -485,22 +487,26 @@ namespace http {
); );
} }
if (it->LatencyIsKnown()) if (it->LatencyIsKnown())
s << " ( " << it->GetMeanLatency() << tr("ms") << " )"; s << " ( " << tr("%dms", it->GetMeanLatency()) << " )";
ShowTunnelDetails(s, it->GetState (), false, it->GetNumSentBytes ()); ShowTunnelDetails(s, it->GetState (), false, it->GetNumSentBytes ());
s << "</div>\r\n"; s << "</div>\r\n";
} }
} }
s << "<br>\r\n"; s << "<br>\r\n";
s << "<b>" << tr("Tags") << "</b><br>\r\n" << tr("Incoming") << ": <i>" << dest->GetNumIncomingTags () << "</i><br>\r\n"; s << "<b>" << tr("Tags") << "</b><br>\r\n"
<< tr("Incoming") << ": <i>" << dest->GetNumIncomingTags () << "</i><br>\r\n";
if (!dest->GetSessions ().empty ()) { if (!dest->GetSessions ().empty ()) {
std::stringstream tmp_s; uint32_t out_tags = 0; std::stringstream tmp_s; uint32_t out_tags = 0;
for (const auto& it: dest->GetSessions ()) { for (const auto& it: dest->GetSessions ()) {
tmp_s << "<tr><td>" << i2p::client::context.GetAddressBook ().ToAddress(it.first) << "</td><td>" << it.second->GetNumOutgoingTags () << "</td></tr>\r\n"; tmp_s << "<tr><td>" << i2p::client::context.GetAddressBook ().ToAddress(it.first) << "</td><td>" << it.second->GetNumOutgoingTags () << "</td></tr>\r\n";
out_tags += it.second->GetNumOutgoingTags (); out_tags += it.second->GetNumOutgoingTags ();
} }
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" s << "<div class='slide'><label for='slide-tags'>" << tr("Outgoing") << ": <i>" << out_tags << "</i></label>\r\n"
<< "<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"; << "<input type=\"checkbox\" id=\"slide-tags\" />\r\n"
<< "<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";
} else } else
s << tr("Outgoing") << ": <i>0</i><br>\r\n"; s << tr("Outgoing") << ": <i>0</i><br>\r\n";
s << "<br>\r\n"; s << "<br>\r\n";
@@ -515,8 +521,11 @@ namespace http {
tmp_s << "<tr><td>" << i2p::client::context.GetAddressBook ().ToAddress(it.second->GetDestination ()) << "</td><td>" << it.second->GetState () << "</td></tr>\r\n"; tmp_s << "<tr><td>" << i2p::client::context.GetAddressBook ().ToAddress(it.second->GetDestination ()) << "</td><td>" << it.second->GetState () << "</td></tr>\r\n";
ecies_sessions++; ecies_sessions++;
} }
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" s << "<div class='slide'><label for='slide-ecies-sessions'>" << tr("Tags sessions") << ": <i>" << ecies_sessions << "</i></label>\r\n"
<< "<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"; << "<input type=\"checkbox\" id=\"slide-ecies-sessions\" />\r\n"
<< "<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";
} else } else
s << tr("Tags sessions") << ": <i>0</i><br>\r\n"; s << tr("Tags sessions") << ": <i>0</i><br>\r\n";
s << "<br>\r\n"; s << "<br>\r\n";
@@ -641,7 +650,7 @@ namespace http {
} }
else if (!i2p::context.IsFloodfill ()) else if (!i2p::context.IsFloodfill ())
{ {
s << "<b>" << tr("LeaseSets") << ":</b> " << tr("not floodfill") << ".<br>\r\n"; s << "<b>" << tr("LeaseSets") << ":</b> " << tr(/* Message on LeaseSets page */ "floodfill mode is disabled") << ".<br>\r\n";
} }
else else
{ {
@@ -670,7 +679,7 @@ namespace http {
} }
s << "&#8658; " << it->GetTunnelID () << ":me"; s << "&#8658; " << it->GetTunnelID () << ":me";
if (it->LatencyIsKnown()) if (it->LatencyIsKnown())
s << " ( " << it->GetMeanLatency() << tr("ms") << " )"; s << " ( " << tr("%dms", it->GetMeanLatency()) << " )";
ShowTunnelDetails(s, it->GetState (), (it->GetTunnelPool () == ExplPool), it->GetNumReceivedBytes ()); ShowTunnelDetails(s, it->GetState (), (it->GetTunnelPool () == ExplPool), it->GetNumReceivedBytes ());
s << "</div>\r\n"; s << "</div>\r\n";
} }
@@ -690,7 +699,7 @@ namespace http {
); );
} }
if (it->LatencyIsKnown()) if (it->LatencyIsKnown())
s << " ( " << it->GetMeanLatency() << tr("ms") << " )"; s << " ( " << tr("%dms", it->GetMeanLatency()) << " )";
ShowTunnelDetails(s, it->GetState (), (it->GetTunnelPool () == ExplPool), it->GetNumSentBytes ()); ShowTunnelDetails(s, it->GetState (), (it->GetTunnelPool () == ExplPool), it->GetNumSentBytes ());
s << "</div>\r\n"; s << "</div>\r\n";
} }
@@ -728,19 +737,20 @@ namespace http {
s << "<br>\r\n<small>" << tr("<b>Note:</b> any action done here are not persistent and not changes your config files.") << "</small>\r\n<br>\r\n"; s << "<br>\r\n<small>" << tr("<b>Note:</b> any action done here are not persistent and not changes your config files.") << "</small>\r\n<br>\r\n";
auto loglevel = i2p::log::Logger().GetLogLevel();
s << "<b>" << tr("Logging level") << "</b><br>\r\n"; s << "<b>" << tr("Logging level") << "</b><br>\r\n";
s << " <a class=\"button\" href=\"" << webroot << "?cmd=" << HTTP_COMMAND_LOGLEVEL << "&level=none&token=" << token << "\"> none </a> \r\n"; s << " <a class=\"button" << (loglevel == eLogNone ? " selected" : "") << "\" href=\"" << webroot << "?cmd=" << HTTP_COMMAND_LOGLEVEL << "&level=none&token=" << token << "\"> none </a> \r\n";
s << " <a class=\"button\" href=\"" << webroot << "?cmd=" << HTTP_COMMAND_LOGLEVEL << "&level=error&token=" << token << "\"> error </a> \r\n"; s << " <a class=\"button" << (loglevel == eLogError ? " selected" : "") << "\" href=\"" << webroot << "?cmd=" << HTTP_COMMAND_LOGLEVEL << "&level=error&token=" << token << "\"> error </a> \r\n";
s << " <a class=\"button\" href=\"" << webroot << "?cmd=" << HTTP_COMMAND_LOGLEVEL << "&level=warn&token=" << token << "\"> warn </a> \r\n"; s << " <a class=\"button" << (loglevel == eLogWarning ? " selected" : "") << "\" href=\"" << webroot << "?cmd=" << HTTP_COMMAND_LOGLEVEL << "&level=warn&token=" << token << "\"> warn </a> \r\n";
s << " <a class=\"button\" href=\"" << webroot << "?cmd=" << HTTP_COMMAND_LOGLEVEL << "&level=info&token=" << token << "\"> info </a> \r\n"; s << " <a class=\"button" << (loglevel == eLogInfo ? " selected" : "") << "\" href=\"" << webroot << "?cmd=" << HTTP_COMMAND_LOGLEVEL << "&level=info&token=" << token << "\"> info </a> \r\n";
s << " <a class=\"button\" href=\"" << webroot << "?cmd=" << HTTP_COMMAND_LOGLEVEL << "&level=debug&token=" << token << "\"> debug </a><br>\r\n<br>\r\n"; s << " <a class=\"button" << (loglevel == eLogDebug ? " selected" : "") << "\" href=\"" << webroot << "?cmd=" << HTTP_COMMAND_LOGLEVEL << "&level=debug&token=" << token << "\"> debug </a><br>\r\n<br>\r\n";
uint16_t maxTunnels = GetMaxNumTransitTunnels (); uint16_t maxTunnels = GetMaxNumTransitTunnels ();
s << "<b>" << tr("Transit tunnels limit") << "</b><br>\r\n"; s << "<b>" << tr("Transit tunnels limit") << "</b><br>\r\n";
s << "<form method=\"get\" action=\"" << webroot << "\">\r\n"; s << "<form method=\"get\" action=\"" << webroot << "\">\r\n";
s << " <input type=\"hidden\" name=\"cmd\" value=\"" << HTTP_COMMAND_LIMITTRANSIT << "\">\r\n"; s << " <input type=\"hidden\" name=\"cmd\" value=\"" << HTTP_COMMAND_LIMITTRANSIT << "\">\r\n";
s << " <input type=\"hidden\" name=\"token\" value=\"" << token << "\">\r\n"; s << " <input type=\"hidden\" name=\"token\" value=\"" << token << "\">\r\n";
s << " <input type=\"number\" min=\"0\" max=\"65535\" name=\"limit\" value=\"" << maxTunnels << "\">\r\n"; s << " <input type=\"number\" min=\"0\" max=\"" << TRANSIT_TUNNELS_LIMIT <<"\" name=\"limit\" value=\"" << maxTunnels << "\">\r\n";
s << " <button type=\"submit\">" << tr("Change") << "</button>\r\n"; s << " <button type=\"submit\">" << tr("Change") << "</button>\r\n";
s << "</form>\r\n<br>\r\n"; s << "</form>\r\n<br>\r\n";
@@ -770,23 +780,24 @@ namespace http {
{ {
if (i2p::tunnel::tunnels.CountTransitTunnels()) if (i2p::tunnel::tunnels.CountTransitTunnels())
{ {
s << "<b>" << tr("Transit Tunnels") << ":</b><br>\r\n<div class=\"list\">\r\n"; s << "<b>" << tr("Transit Tunnels") << ":</b><br>\r\n";
s << "<table><thead><th>&#8658;</th><th>ID</th><th>&#8658;</th><th>" << tr("Amount") << "</th></thead><tbody class=\"tableitem\">";
for (const auto& it: i2p::tunnel::tunnels.GetTransitTunnels ()) for (const auto& it: i2p::tunnel::tunnels.GetTransitTunnels ())
{ {
s << "<div class=\"listitem\">\r\n";
if (std::dynamic_pointer_cast<i2p::tunnel::TransitTunnelGateway>(it)) if (std::dynamic_pointer_cast<i2p::tunnel::TransitTunnelGateway>(it))
s << it->GetTunnelID () << " &#8658; "; s << "<tr><td></td><td>" << it->GetTunnelID () << "</td><td>&#8658;</td><td>";
else if (std::dynamic_pointer_cast<i2p::tunnel::TransitTunnelEndpoint>(it)) else if (std::dynamic_pointer_cast<i2p::tunnel::TransitTunnelEndpoint>(it))
s << " &#8658; " << it->GetTunnelID (); s << "<tr><td>&#8658;</td><td>" << it->GetTunnelID () << "</td><td></td><td>";
else else
s << " &#8658; " << it->GetTunnelID () << " &#8658; "; s << "<tr><td>&#8658;</td><td>" << it->GetTunnelID () << "</td><td>&#8658;</td><td>";
s << " " << it->GetNumTransmittedBytes () << "</div>\r\n"; ShowTraffic(s, it->GetNumTransmittedBytes ());
s << "</td></tr>\r\n";
} }
s << "</div>\r\n"; s << "</tbody></table>\r\n";
} }
else else
{ {
s << "<b>" << tr("Transit Tunnels") << ":</b> " << tr("no transit tunnels currently built") << ".<br>\r\n"; s << "<b>" << tr("Transit Tunnels") << ":</b> " << tr(/* Message on transit tunnels page */ "no transit tunnels currently built") << ".<br>\r\n";
} }
} }
@@ -807,6 +818,8 @@ namespace http {
tmp_s << " [" << it.second->GetNumSentBytes () << ":" << it.second->GetNumReceivedBytes () << "]"; tmp_s << " [" << it.second->GetNumSentBytes () << ":" << it.second->GetNumReceivedBytes () << "]";
if (it.second->GetRelayTag ()) if (it.second->GetRelayTag ())
tmp_s << " [itag:" << it.second->GetRelayTag () << "]"; tmp_s << " [itag:" << it.second->GetRelayTag () << "]";
if (it.second->GetSendQueueSize () > 0)
tmp_s << " [queue:" << it.second->GetSendQueueSize () << "]";
tmp_s << "</div>\r\n" << std::endl; tmp_s << "</div>\r\n" << std::endl;
cnt++; cnt++;
} }
@@ -820,6 +833,8 @@ namespace http {
tmp_s6 << " [" << it.second->GetNumSentBytes () << ":" << it.second->GetNumReceivedBytes () << "]"; tmp_s6 << " [" << it.second->GetNumSentBytes () << ":" << it.second->GetNumReceivedBytes () << "]";
if (it.second->GetRelayTag ()) if (it.second->GetRelayTag ())
tmp_s6 << " [itag:" << it.second->GetRelayTag () << "]"; tmp_s6 << " [itag:" << it.second->GetRelayTag () << "]";
if (it.second->GetSendQueueSize () > 0)
tmp_s6 << " [queue:" << it.second->GetSendQueueSize () << "]";
tmp_s6 << "</div>\r\n" << std::endl; tmp_s6 << "</div>\r\n" << std::endl;
cnt6++; cnt6++;
} }
@@ -879,7 +894,7 @@ namespace http {
s << "</div>\r\n"; s << "</div>\r\n";
} }
else else
s << "<b>" << tr("SAM sessions") << ":</b> " << tr("no sessions currently running") << ".<br>\r\n"; s << "<b>" << tr("SAM sessions") << ":</b> " << tr(/* Message on SAM sessions page */ "no sessions currently running") << ".<br>\r\n";
} }
void ShowSAMSession (std::stringstream& s, const std::string& id) void ShowSAMSession (std::stringstream& s, const std::string& id)
@@ -988,7 +1003,7 @@ namespace http {
for (auto& it: serverForwards) for (auto& it: serverForwards)
{ {
auto& ident = it.second->GetLocalDestination ()->GetIdentHash(); auto& ident = it.second->GetLocalDestination ()->GetIdentHash();
s << "<a href=\"" << webroot << "?page=" << HTTP_PAGE_LOCAL_DESTINATION << "&b32=" << ident.ToBase32 () << "\">"; s << "<div class=\"listitem\"><a href=\"" << webroot << "?page=" << HTTP_PAGE_LOCAL_DESTINATION << "&b32=" << ident.ToBase32 () << "\">";
s << it.second->GetName () << "</a> &#8656; "; s << it.second->GetName () << "</a> &#8656; ";
s << i2p::client::context.GetAddressBook ().ToAddress(ident); s << i2p::client::context.GetAddressBook ().ToAddress(ident);
s << "</div>\r\n"<< std::endl; s << "</div>\r\n"<< std::endl;
@@ -1198,7 +1213,7 @@ namespace http {
url.parse_query(params); url.parse_query(params);
std::string webroot; i2p::config::GetOption("http.webroot", webroot); std::string webroot; i2p::config::GetOption("http.webroot", webroot);
std::string redirect = "5; url=" + webroot + "?page=commands"; std::string redirect = std::to_string(COMMAND_REDIRECT_TIMEOUT) + "; url=" + webroot + "?page=commands";
std::string token = params["token"]; std::string token = params["token"];
if (token.empty () || m_Tokens.find (std::stoi (token)) == m_Tokens.end ()) if (token.empty () || m_Tokens.find (std::stoi (token)) == m_Tokens.end ())
@@ -1272,20 +1287,20 @@ namespace http {
s << "<b>" << tr("ERROR") << "</b>:&nbsp;" << tr("StreamID can't be null") << "<br>\r\n<br>\r\n"; s << "<b>" << tr("ERROR") << "</b>:&nbsp;" << tr("StreamID can't be null") << "<br>\r\n<br>\r\n";
s << "<a href=\"" << webroot << "?page=local_destination&b32=" << b32 << "\">" << tr("Return to destination page") << "</a><br>\r\n"; s << "<a href=\"" << webroot << "?page=local_destination&b32=" << b32 << "\">" << tr("Return to destination page") << "</a><br>\r\n";
s << "<p>" << tr("You will be redirected in 5 seconds") << "</b>"; s << "<p>" << tr("You will be redirected in %d seconds", COMMAND_REDIRECT_TIMEOUT) << "</b>";
redirect = "5; url=" + webroot + "?page=local_destination&b32=" + b32; redirect = std::to_string(COMMAND_REDIRECT_TIMEOUT) + "; url=" + webroot + "?page=local_destination&b32=" + b32;
res.add_header("Refresh", redirect.c_str()); res.add_header("Refresh", redirect.c_str());
return; return;
} }
else if (cmd == HTTP_COMMAND_LIMITTRANSIT) else if (cmd == HTTP_COMMAND_LIMITTRANSIT)
{ {
uint32_t limit = std::stoul(params["limit"], nullptr); uint32_t limit = std::stoul(params["limit"], nullptr);
if (limit > 0 && limit <= 65535) if (limit > 0 && limit <= TRANSIT_TUNNELS_LIMIT)
SetMaxNumTransitTunnels (limit); SetMaxNumTransitTunnels (limit);
else { else {
s << "<b>" << tr("ERROR") << "</b>:&nbsp;" << tr("Transit tunnels count must not exceed 65535") << "\r\n<br>\r\n<br>\r\n"; s << "<b>" << tr("ERROR") << "</b>:&nbsp;" << tr("Transit tunnels count must not exceed %d", TRANSIT_TUNNELS_LIMIT) << "\r\n<br>\r\n<br>\r\n";
s << "<a href=\"" << webroot << "?page=commands\">" << tr("Back to commands list") << "</a>\r\n<br>\r\n"; s << "<a href=\"" << webroot << "?page=commands\">" << tr("Back to commands list") << "</a>\r\n<br>\r\n";
s << "<p>" << tr("You will be redirected in 5 seconds") << "</b>"; s << "<p>" << tr("You will be redirected in %d seconds", COMMAND_REDIRECT_TIMEOUT) << "</b>";
res.add_header("Refresh", redirect.c_str()); res.add_header("Refresh", redirect.c_str());
return; return;
} }
@@ -1360,7 +1375,7 @@ namespace http {
s << "<b>" << tr("SUCCESS") << "</b>:&nbsp;" << tr("Command accepted") << "<br><br>\r\n"; s << "<b>" << tr("SUCCESS") << "</b>:&nbsp;" << tr("Command accepted") << "<br><br>\r\n";
s << "<a href=\"" << webroot << "?page=commands\">" << tr("Back to commands list") << "</a><br>\r\n"; s << "<a href=\"" << webroot << "?page=commands\">" << tr("Back to commands list") << "</a><br>\r\n";
s << "<p>" << tr("You will be redirected in 5 seconds") << "</b>"; s << "<p>" << tr("You will be redirected in %d seconds", COMMAND_REDIRECT_TIMEOUT) << "</b>";
res.add_header("Refresh", redirect.c_str()); res.add_header("Refresh", redirect.c_str());
} }

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2020, The PurpleI2P Project * Copyright (c) 2013-2023, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@@ -24,6 +24,8 @@ namespace http
{ {
const size_t HTTP_CONNECTION_BUFFER_SIZE = 8192; const size_t HTTP_CONNECTION_BUFFER_SIZE = 8192;
const int TOKEN_EXPIRATION_TIMEOUT = 30; // in seconds const int TOKEN_EXPIRATION_TIMEOUT = 30; // in seconds
const int COMMAND_REDIRECT_TIMEOUT = 5; // in seconds
const int TRANSIT_TUNNELS_LIMIT = 65535;
class HTTPConnection: public std::enable_shared_from_this<HTTPConnection> class HTTPConnection: public std::enable_shared_from_this<HTTPConnection>
{ {

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2022, The PurpleI2P Project * Copyright (c) 2013-2023, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@@ -38,7 +38,7 @@ namespace http
"@media (prefers-color-scheme: dark) { :root { --main-bg-color: #242424; --main-text-color: #17ab5c; --main-link-color: #bf64b7; --main-link-hover-color: #000000; } }\r\n" "@media (prefers-color-scheme: dark) { :root { --main-bg-color: #242424; --main-text-color: #17ab5c; --main-link-color: #bf64b7; --main-link-hover-color: #000000; } }\r\n"
"body { font: 100%/1.5em sans-serif; margin: 0; padding: 1.5em; background: var(--main-bg-color); color: var(--main-text-color); }\r\n" "body { font: 100%/1.5em sans-serif; margin: 0; padding: 1.5em; background: var(--main-bg-color); color: var(--main-text-color); }\r\n"
"a, .slide label { text-decoration: none; color: var(--main-link-color); }\r\n" "a, .slide label { text-decoration: none; color: var(--main-link-color); }\r\n"
"a:hover, .slide label:hover, button[type=submit]:hover { color: var(--main-link-hover-color); background: var(--main-link-color); }\r\n" "a:hover, a.button.selected, .slide label:hover, button[type=submit]:hover { color: var(--main-link-hover-color); background: var(--main-link-color); }\r\n"
"a.button { appearance: button; text-decoration: none; padding: 0 5px; border: 1px solid var(--main-link-color); }\r\n" "a.button { appearance: button; text-decoration: none; padding: 0 5px; border: 1px solid var(--main-link-color); }\r\n"
".header { font-size: 2.5em; text-align: center; margin: 1em 0; color: var(--main-link-color); }\r\n" ".header { font-size: 2.5em; text-align: center; margin: 1em 0; color: var(--main-link-color); }\r\n"
".wrapper { margin: 0 auto; padding: 1em; max-width: 64em; }\r\n" ".wrapper { margin: 0 auto; padding: 1em; max-width: 64em; }\r\n"

View File

@@ -163,7 +163,7 @@ namespace transport
if (!a) return; if (!a) return;
for (const auto& address : *a) for (const auto& address : *a)
{ {
if (!address->host.is_v6 () && address->port) if (address && !address->host.is_v6 () && address->port)
TryPortMapping (address); TryPortMapping (address);
} }
m_Timer.expires_from_now (boost::posix_time::minutes(20)); // every 20 minutes m_Timer.expires_from_now (boost::posix_time::minutes(20)); // every 20 minutes
@@ -215,7 +215,7 @@ namespace transport
if (!a) return; if (!a) return;
for (const auto& address : *a) for (const auto& address : *a)
{ {
if (!address->host.is_v6 () && address->port) if (address && !address->host.is_v6 () && address->port)
CloseMapping (address); CloseMapping (address);
} }
} }

17
debian/changelog vendored
View File

@@ -1,3 +1,20 @@
i2pd (2.45.1-1) unstable; urgency=medium
* updated to version 2.45.1/0.9.57
-- orignal <orignal@i2pmail.org> Wed, 11 Jan 2023 19:00:00 +0000
i2pd (2.45.0-1) unstable; urgency=high
* updated to version 2.45.0/0.9.57
* compat level 12
* standards version 4.3.0
* increased nofile limit in service and init.d to 8192
* added conffiles
* removed #1210 patch
-- r4sas <r4sas@i2pmail.org> Tue, 3 Jan 2023 18:00:00 +0000
i2pd (2.44.0-1) unstable; urgency=medium i2pd (2.44.0-1) unstable; urgency=medium
* updated to version 2.44.0/0.9.56 * updated to version 2.44.0/0.9.56

2
debian/compat vendored
View File

@@ -1 +1 @@
9 12

2
debian/conffiles vendored Normal file
View File

@@ -0,0 +1,2 @@
/etc/i2pd/i2pd.conf
/etc/i2pd/tunnels.conf

4
debian/control vendored
View File

@@ -2,8 +2,8 @@ Source: i2pd
Section: net Section: net
Priority: optional Priority: optional
Maintainer: r4sas <r4sas@i2pmail.org> Maintainer: r4sas <r4sas@i2pmail.org>
Build-Depends: debhelper (>= 9), dpkg-dev (>= 1.17.2~), gcc (>= 4.7) | clang (>= 3.3), libboost-system-dev (>= 1.46), libboost-date-time-dev (>= 1.46), libboost-filesystem-dev (>= 1.46), libboost-program-options-dev (>= 1.46), libminiupnpc-dev, libssl-dev, zlib1g-dev Build-Depends: debhelper (>= 12~), libboost-system-dev (>= 1.46), libboost-date-time-dev (>= 1.46), libboost-filesystem-dev (>= 1.46), libboost-program-options-dev (>= 1.46), libminiupnpc-dev, libssl-dev, zlib1g-dev
Standards-Version: 3.9.8 Standards-Version: 4.3.0
Homepage: http://i2pd.website/ Homepage: http://i2pd.website/
Vcs-Git: git://github.com/PurpleI2P/i2pd.git Vcs-Git: git://github.com/PurpleI2P/i2pd.git
Vcs-Browser: https://github.com/PurpleI2P/i2pd Vcs-Browser: https://github.com/PurpleI2P/i2pd

6
debian/copyright vendored
View File

@@ -3,18 +3,18 @@ Upstream-Name: i2pd
Source: https://github.com/PurpleI2P Source: https://github.com/PurpleI2P
Files: * Files: *
Copyright: 2013-2020 PurpleI2P Copyright: 2013-2023 PurpleI2P
License: BSD-3-clause License: BSD-3-clause
Files: debian/* Files: debian/*
Copyright: 2013-2015 Kill Your TV <killyourtv@i2pmail.org> Copyright: 2013-2015 Kill Your TV <killyourtv@i2pmail.org>
2014-2016 hagen <hagen@i2pmail.org> 2014-2016 hagen <hagen@i2pmail.org>
2016-2020 R4SAS <r4sas@i2pmail.org> 2016-2023 R4SAS <r4sas@i2pmail.org>
2017-2020 Yangfl <mmyangfl@gmail.com> 2017-2020 Yangfl <mmyangfl@gmail.com>
License: GPL-2+ License: GPL-2+
License: BSD-3-clause License: BSD-3-clause
Copyright (c) 2013-2017, The PurpleI2P Project Copyright (c) 2013-2023, The PurpleI2P Project
. .
All rights reserved. All rights reserved.
. .

2
debian/i2pd.default vendored
View File

@@ -8,4 +8,4 @@ I2PD_ENABLED="yes"
DAEMON_OPTS="" DAEMON_OPTS=""
# If you have problems with hunging i2pd, you can try enable this # If you have problems with hunging i2pd, you can try enable this
ulimit -n 4096 ulimit -n 8192

1
debian/i2pd.install vendored
View File

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

3
debian/i2pd.links vendored
View File

@@ -1,5 +1,4 @@
etc/i2pd/i2pd.conf var/lib/i2pd/i2pd.conf etc/i2pd/i2pd.conf var/lib/i2pd/i2pd.conf
etc/i2pd/tunnels.conf var/lib/i2pd/tunnels.conf etc/i2pd/tunnels.conf var/lib/i2pd/tunnels.conf
etc/i2pd/subscriptions.txt var/lib/i2pd/subscriptions.txt
etc/i2pd/tunnels.conf.d var/lib/i2pd/tunnels.d etc/i2pd/tunnels.conf.d var/lib/i2pd/tunnels.d
usr/share/i2pd/certificates var/lib/i2pd/certificates usr/share/i2pd/certificates var/lib/i2pd/certificates

View File

@@ -1,27 +0,0 @@
Description: fix #1210
Disables two options, which not presented in old systemd versions
Author: r4sas <r4sas@i2pmail.org>
Bug: https://github.com/PurpleI2P/i2pd/issues/1210
Reviewed-By: r4sas <r4sas@i2pmail.org>
Last-Update: 2020-05-25
Index: i2pd/contrib/i2pd.service
===================================================================
--- i2pd.orig/contrib/i2pd.service
+++ i2pd/contrib/i2pd.service
@@ -6,10 +6,10 @@ After=network.target
[Service]
User=i2pd
Group=i2pd
-RuntimeDirectory=i2pd
-RuntimeDirectoryMode=0700
-LogsDirectory=i2pd
-LogsDirectoryMode=0700
+#RuntimeDirectory=i2pd
+#RuntimeDirectoryMode=0700
+#LogsDirectory=i2pd
+#LogsDirectoryMode=0700
Type=forking
ExecStart=/usr/sbin/i2pd --conf=/etc/i2pd/i2pd.conf --tunconf=/etc/i2pd/tunnels.conf --tunnelsdir=/etc/i2pd/tunnels.conf.d --pidfile=/run/i2pd/i2pd.pid --logfile=/var/log/i2pd/i2pd.log --daemon --service
ExecReload=/bin/sh -c "kill -HUP $MAINPID"

View File

@@ -1,2 +1 @@
01-fix-1210.patch 01-upnp.patch
02-upnp.patch

3
debian/watch vendored
View File

@@ -1,3 +1,4 @@
version=4 opts="filenamemangle=s%(?:.*?)?v?(\d[\d.]*)\.tar\.gz%i2pd-$1.tar.gz%" \ version=4
opts="filenamemangle=s%(?:.*?)?v?(\d[\d.]*)\.tar\.gz%i2pd-$1.tar.gz%" \
https://github.com/PurpleI2P/i2pd/tags \ https://github.com/PurpleI2P/i2pd/tags \
(?:.*?/)?(\d[\d.]*)\.tar\.gz debian uupdate (?:.*?/)?(\d[\d.]*)\.tar\.gz debian uupdate

View File

@@ -64,10 +64,10 @@ namespace afrikaans // language namespace
static std::map<std::string, std::vector<std::string>> plurals static std::map<std::string, std::vector<std::string>> plurals
{ {
{"days", {"dag", "dae"}}, {"%d days", {"%d dag", "%d dae"}},
{"hours", {"uur", "ure"}}, {"%d hours", {"%d uur", "%d ure"}},
{"minutes", {"minuut", "minute"}}, {"%d minutes", {"%d minuut", "%d minute"}},
{"seconds", {"seconde", "sekondes"}}, {"%d seconds", {"%d seconde", "%d sekondes"}},
{"", {"", ""}}, {"", {"", ""}},
}; };

View File

@@ -31,9 +31,9 @@ namespace armenian // language namespace
static std::map<std::string, std::string> strings static std::map<std::string, std::string> strings
{ {
{"KiB", "ԿիԲ"}, {"%.2f KiB", "%.2f ԿիԲ"},
{"MiB", "ՄիԲ"}, {"%.2f MiB", "%.2f ՄիԲ"},
{"GiB", "ԳիԲ"}, {"%.2f GiB", "%.2f ԳիԲ"},
{"building", "կառուցվում է"}, {"building", "կառուցվում է"},
{"failed", "Անհաջող"}, {"failed", "Անհաջող"},
{"expiring", "Լրանում է"}, {"expiring", "Լրանում է"},
@@ -68,7 +68,7 @@ namespace armenian // language namespace
{"Family", "Խմբատեսակ"}, {"Family", "Խմբատեսակ"},
{"Tunnel creation success rate", "Հաջողությամբ կառուցված թունելներ"}, {"Tunnel creation success rate", "Հաջողությամբ կառուցված թունելներ"},
{"Received", "Ստացվել է"}, {"Received", "Ստացվել է"},
{"KiB/s", "ԿիԲ/վ"}, {"%.2f KiB/s", "%.2f ԿիԲ/վ"},
{"Sent", "Ուղարկվել է"}, {"Sent", "Ուղարկվել է"},
{"Transit", "Տարանցում"}, {"Transit", "Տարանցում"},
{"Data path", "Տվյալների ուղին"}, {"Data path", "Տվյալների ուղին"},
@@ -94,7 +94,7 @@ namespace armenian // language namespace
{"Type", "Տեսակը"}, {"Type", "Տեսակը"},
{"EncType", "Գաղտնագրի տեսակը"}, {"EncType", "Գաղտնագրի տեսակը"},
{"Inbound tunnels", "Մուտքային թունելներ"}, {"Inbound tunnels", "Մուտքային թունելներ"},
{"ms", "մլվ"}, {"%dms", "%dմլվ"},
{"Outbound tunnels", "Ելքային թունելներ"}, {"Outbound tunnels", "Ելքային թունելներ"},
{"Tags", "Թեգեր"}, {"Tags", "Թեգեր"},
{"Incoming", "Մուտքային"}, {"Incoming", "Մուտքային"},
@@ -198,10 +198,10 @@ namespace armenian // language namespace
static std::map<std::string, std::vector<std::string>> plurals static std::map<std::string, std::vector<std::string>> plurals
{ {
{"days", {"օր", "օր"}}, {"%d days", {"%d օր", "%d օր"}},
{"hours", {"ժամ", "ժամ"}}, {"%d hours", {"%d ժամ", "%d ժամ"}},
{"minutes", {"րոպե", "րոպե"}}, {"%d minutes", {"%d րոպե", "%d րոպե"}},
{"seconds", {"վարկյան", "վարկյան"}}, {"%d seconds", {"%d վարկյան", "%d վարկյան"}},
{"", {"", ""}}, {"", {"", ""}},
}; };

View File

@@ -32,9 +32,9 @@ namespace chinese // language namespace
static std::map<std::string, std::string> strings static std::map<std::string, std::string> strings
{ {
{"KiB", "KiB"}, {"%.2f KiB", "%.2f KiB"},
{"MiB", "MiB"}, {"%.2f MiB", "%.2f MiB"},
{"GiB", "GiB"}, {"%.2f GiB", "%.2f GiB"},
{"building", "正在构建"}, {"building", "正在构建"},
{"failed", "连接失败"}, {"failed", "连接失败"},
{"expiring", "即将过期"}, {"expiring", "即将过期"},
@@ -70,7 +70,7 @@ namespace chinese // language namespace
{"Family", "家族"}, {"Family", "家族"},
{"Tunnel creation success rate", "隧道创建成功率"}, {"Tunnel creation success rate", "隧道创建成功率"},
{"Received", "已接收"}, {"Received", "已接收"},
{"KiB/s", "KiB/s"}, {"%.2f KiB/s", "%.2f KiB/s"},
{"Sent", "已发送"}, {"Sent", "已发送"},
{"Transit", "中转"}, {"Transit", "中转"},
{"Data path", "数据文件路径"}, {"Data path", "数据文件路径"},
@@ -96,7 +96,7 @@ namespace chinese // language namespace
{"Type", "类型"}, {"Type", "类型"},
{"EncType", "加密类型"}, {"EncType", "加密类型"},
{"Inbound tunnels", "入站隧道"}, {"Inbound tunnels", "入站隧道"},
{"ms", "毫秒"}, {"%dms", "%d毫秒"},
{"Outbound tunnels", "出站隧道"}, {"Outbound tunnels", "出站隧道"},
{"Tags", "标签"}, {"Tags", "标签"},
{"Incoming", "传入"}, {"Incoming", "传入"},
@@ -200,10 +200,10 @@ namespace chinese // language namespace
static std::map<std::string, std::vector<std::string>> plurals static std::map<std::string, std::vector<std::string>> plurals
{ {
{"days", {""}}, {"%d days", {"%d "}},
{"hours", {""}}, {"%d hours", {"%d "}},
{"minutes", {""}}, {"%d minutes", {"%d "}},
{"seconds", {""}}, {"%d seconds", {"%d "}},
{"", {""}}, {"", {""}},
}; };

216
i18n/Czech.cpp Normal file
View File

@@ -0,0 +1,216 @@
/*
* Copyright (c) 2022, The PurpleI2P Project
*
* This file is part of Purple i2pd project and licensed under BSD3
*
* See full license text in LICENSE file at top of project tree
*/
#include <map>
#include <vector>
#include <string>
#include <memory>
#include "I18N.h"
// Czech localization file
namespace i2p
{
namespace i18n
{
namespace czech // language namespace
{
// language name in lowercase
static std::string language = "czech";
// See for language plural forms here:
// https://localization-guide.readthedocs.io/en/latest/l10n/pluralforms.html
static int plural (int n) {
return (n == 1) ? 0 : (n >= 2 && n <= 4) ? 1 : 2;
}
static std::map<std::string, std::string> strings
{
{"%.2f KiB", "%.2f KiB"},
{"%.2f MiB", "%.2f MiB"},
{"%.2f GiB", "%.2f GiB"},
{"building", "vytváří se"},
{"failed", "selhalo"},
{"expiring", "končící"},
{"established", "vytvořeno"},
{"unknown", "neznámý"},
{"exploratory", "průzkumné"},
{"Purple I2P Webconsole", "Purple I2P Webkonsole"},
{"<b>i2pd</b> webconsole", "<b>i2pd</b> webkonsole"},
{"Main page", "Hlavní stránka"},
{"Router commands", "Router příkazy"},
{"Local Destinations", "Lokální destinace"},
{"LeaseSets", "LeaseSety"},
{"Tunnels", "Tunely"},
{"Transit Tunnels", "Transitní tunely"},
{"Transports", "Transporty"},
{"I2P tunnels", "I2P tunely"},
{"SAM sessions", "SAM relace"},
{"ERROR", "CHYBA"},
{"OK", "OK"},
{"Testing", "Testuji"},
{"Firewalled", "Za Firewallem"},
{"Unknown", "Neznámý"},
{"Proxy", "Proxy"},
{"Mesh", "Síť"},
{"Error", "Chyba"},
{"Clock skew", "Časová nesrovnalost"},
{"Offline", "Offline"},
{"Symmetric NAT", "Symetrický NAT"},
{"Uptime", "Doba provozu"},
{"Network status", "Status sítě"},
{"Network status v6", "Status sítě v6"},
{"Stopping in", "Zastavuji za"},
{"Family", "Rodina"},
{"Tunnel creation success rate", "Úspěšnost vytváření tunelů"},
{"Received", "Přijato"},
{"%.2f KiB/s", "%.2f KiB/s"},
{"Sent", "Odesláno"},
{"Transit", "Tranzit"},
{"Data path", "Cesta k data souborům"},
{"Hidden content. Press on text to see.", "Skrytý kontent. Pro zobrazení, klikni na text."},
{"Router Ident", "Routerová Identita"},
{"Router Family", "Rodina routerů"},
{"Router Caps", "Omezení Routerů"},
{"Version", "Verze"},
{"Our external address", "Naše externí adresa"},
{"supported", "podporováno"},
{"Routers", "Routery"},
{"Floodfills", "Floodfilly"},
{"Client Tunnels", "Klientské tunely"},
{"Services", "Služby"},
{"Enabled", "Zapnuto"},
{"Disabled", "Vypnuto"},
{"Encrypted B33 address", "Šifrovaná adresa B33"},
{"Address registration line", "Registrační řádek adresy"},
{"Domain", "Doména"},
{"Generate", "Vygenerovat"},
{"<b>Note:</b> result string can be used only for registering 2LD domains (example.i2p). For registering subdomains please use i2pd-tools.", "<b>Poznámka:</b> výsledný řetězec může být použit pouze pro registraci 2LD domén (example.i2p). Pro registraci subdomén použijte prosím i2pd-tools."},
{"Address", "Adresa"},
{"Type", "Typ"},
{"EncType", "EncType"},
{"Inbound tunnels", "Příchozí tunely"},
{"%dms", "%dms"},
{"Outbound tunnels", "Odchozí tunely"},
{"Tags", "Štítky"},
{"Incoming", "Příchozí"},
{"Outgoing", "Odchozí"},
{"Destination", "Destinace"},
{"Amount", "Množství"},
{"Incoming Tags", "Příchozí štítky"},
{"Tags sessions", "Relace štítků"},
{"Status", "Status"},
{"Local Destination", "Lokální Destinace"},
{"Streams", "Toky"},
{"Close stream", "Uzavřít tok"},
{"I2CP session not found", "I2CP relace nenalezena"},
{"I2CP is not enabled", "I2CP není zapnuto"},
{"Invalid", "Neplatný"},
{"Store type", "Druh uložení"},
{"Expires", "Vyprší"},
{"Non Expired Leases", "Nevypršené Leasy"},
{"Gateway", "Brána"},
{"TunnelID", "ID tunelu"},
{"EndDate", "Datum ukončení"},
{"not floodfill", "není floodfill"},
{"Queue size", "Velikost fronty"},
{"Run peer test", "Spustit peer test"},
{"Decline transit tunnels", "Odmítnout tranzitní tunely"},
{"Accept transit tunnels", "Přijmout tranzitní tunely"},
{"Cancel graceful shutdown", "Zrušit hladké vypnutí"},
{"Start graceful shutdown", "Zahájit hladké vypnutí"},
{"Force shutdown", "Vynutit vypnutí"},
{"Reload external CSS styles", "Znovu načíst externí CSS"},
{"<b>Note:</b> any action done here are not persistent and not changes your config files.", "<b>Poznámka:</b> žádná vykonaná akce zde není trvalá a nemění konfigurační soubory."},
{"Logging level", "Úroveň logování"},
{"Transit tunnels limit", "Limit tranzitních tunelů"},
{"Change", "Změnit"},
{"Change language", "Změnit jazyk"},
{"no transit tunnels currently built", "Žádný tranzitní tunel není momentálně vytvořen"},
{"SAM disabled", "SAM vypnutý"},
{"no sessions currently running", "Momentálně nejsou spuštěné žádné relace"},
{"SAM session not found", "SAM relace nenalezena"},
{"SAM Session", "SAM Relace"},
{"Server Tunnels", "Server Tunely"},
{"Client Forwards", "Přesměrování Klienta"},
{"Server Forwards", "Přesměrování Serveru"},
{"Unknown page", "Neznámá stránka"},
{"Invalid token", "Neplatný token"},
{"SUCCESS", "ÚSPĚCH"},
{"Stream closed", "Tok uzavřen"},
{"Stream not found or already was closed", "Tok nenalezen nebo byl již uzavřen"},
{"Destination not found", "Destinace nenalezena"},
{"StreamID can't be null", "StreamID nemůže být null"},
{"Return to destination page", "Zpět na stránku destinací"},
{"You will be redirected in 5 seconds", "Budete přesměrováni za 5 vteřin"},
{"Transit tunnels count must not exceed 65535", "Počet tranzitních tunelů nesmí přesáhnout 65535"},
{"Back to commands list", "Zpět na list příkazů"},
{"Register at reg.i2p", "Zaregistrovat na reg.i2p"},
{"Description", "Popis"},
{"A bit information about service on domain", "Trochu informací o službě na doméně"},
{"Submit", "Odeslat"},
{"Domain can't end with .b32.i2p", "Doména nesmí končit na .b32.i2p"},
{"Domain must end with .i2p", "Doména musí končit s .i2p"},
{"Such destination is not found", "Takováto destinace nebyla nalezena"},
{"Unknown command", "Neznámý příkaz"},
{"Command accepted", "Příkaz přijat"},
{"Proxy error", "Chyba proxy serveru"},
{"Proxy info", "Proxy informace"},
{"Proxy error: Host not found", "Chyba proxy serveru: Hostitel nenalezen"},
{"Remote host not found in router's addressbook", "Vzdálený hostitel nebyl nalezen v adresáři routeru"},
{"You may try to find this host on jump services below", "Můžete se pokusit najít tohoto hostitele na startovacích službách níže"},
{"Invalid request", "Neplatný požadavek"},
{"Proxy unable to parse your request", "Proxy server nemohl zpracovat váš požadavek"},
{"addresshelper is not supported", "Adresshelper není podporován"},
{"Host", "Hostitel"},
{"added to router's addressbook from helper", "přidáno do adresáře routeru od pomocníka"},
{"Click here to proceed:", "Pro pokračování, klikněte zde:"},
{"Continue", "Pokračovat"},
{"Addresshelper found", "Adresář nalezen"},
{"already in router's addressbook", "je již v adresáři routeru"},
{"Click here to update record:", "Pro aktualizaci záznamu, klikněte zde:"},
{"invalid request uri", "neplatný URI požadavek"},
{"Can't detect destination host from request", "Nelze zjistit cílového hostitele z požadavku"},
{"Outproxy failure", "Outproxy selhání"},
{"bad outproxy settings", "špatné outproxy nastavení"},
{"not inside I2P network, but outproxy is not enabled", "není uvnitř I2P sítě a outproxy není nastavena"},
{"unknown outproxy url", "neznámá outproxy URL"},
{"cannot resolve upstream proxy", "nelze rozluštit upstream proxy server"},
{"hostname too long", "Název hostitele je příliš dlouhý"},
{"cannot connect to upstream socks proxy", "nelze se připojit k upstream socks proxy serveru"},
{"Cannot negotiate with socks proxy", "Nelze vyjednávat se socks proxy serverem"},
{"CONNECT error", "Chyba PŘIPOJENÍ"},
{"Failed to Connect", "Připojení se nezdařilo"},
{"socks proxy error", "chyba socks proxy serveru"},
{"failed to send request to upstream", "odeslání žádosti upstream serveru se nezdařilo"},
{"No Reply From socks proxy", "Žádná odpověď od socks proxy serveru"},
{"cannot connect", "nelze se připojit"},
{"http out proxy not implemented", "http out proxy není implementován"},
{"cannot connect to upstream http proxy", "nelze se připojit k upstream socks proxy serveru"},
{"Host is down", "Hostitel je nedostupný"},
{"Can't create connection to requested host, it may be down. Please try again later.", "Připojení k požadovanému hostiteli nelze vytvořit, může být nedostupný. Zkuste to, prosím, znovu později."},
{"", ""},
};
static std::map<std::string, std::vector<std::string>> plurals
{
{"%d days", {"%d den", "%d dny", "%d dní", "%d dní"}},
{"%d hours", {"%d hodina", "%d hodiny", "%d hodin", "%d hodin"}},
{"%d minutes", {"%d minuta", "%d minuty", "%d minut", "%d minut"}},
{"%d seconds", {"%d vteřina", "%d vteřiny", "%d vteřin", "%d vteřin"}},
{"", {"", "", "", ""}},
};
std::shared_ptr<const i2p::i18n::Locale> GetLocale()
{
return std::make_shared<i2p::i18n::Locale>(language, strings, plurals, [] (int n)->int { return plural(n); });
}
} // language
} // i18n
} // i2p

View File

@@ -31,9 +31,9 @@ namespace french // language namespace
static std::map<std::string, std::string> strings static std::map<std::string, std::string> strings
{ {
{"KiB", "Kio"}, {"%.2f KiB", "%.2f Kio"},
{"MiB", "Mio"}, {"%.2f MiB", "%.2f Mio"},
{"GiB", "Gio"}, {"%.2f GiB", "%.2f Gio"},
{"building", "En construction"}, {"building", "En construction"},
{"failed", "échoué"}, {"failed", "échoué"},
{"expiring", "expiré"}, {"expiring", "expiré"},
@@ -69,7 +69,7 @@ namespace french // language namespace
{"Family", "Famille"}, {"Family", "Famille"},
{"Tunnel creation success rate", "Taux de succès de création de tunnels"}, {"Tunnel creation success rate", "Taux de succès de création de tunnels"},
{"Received", "Reçu"}, {"Received", "Reçu"},
{"KiB/s", "kio/s"}, {"%.2f KiB/s", "%.2f kio/s"},
{"Sent", "Envoyé"}, {"Sent", "Envoyé"},
{"Transit", "Transité"}, {"Transit", "Transité"},
{"Data path", "Emplacement des données"}, {"Data path", "Emplacement des données"},
@@ -93,7 +93,7 @@ namespace french // language namespace
{"Address", "Adresse"}, {"Address", "Adresse"},
{"Type", "Type"}, {"Type", "Type"},
{"Inbound tunnels", "Tunnels entrants"}, {"Inbound tunnels", "Tunnels entrants"},
{"ms", "ms"}, {"%dms", "%dms"},
{"Outbound tunnels", "Tunnels sortants"}, {"Outbound tunnels", "Tunnels sortants"},
{"Tags", "Balises"}, {"Tags", "Balises"},
{"Incoming", "Entrant"}, {"Incoming", "Entrant"},
@@ -194,10 +194,10 @@ namespace french // language namespace
static std::map<std::string, std::vector<std::string>> plurals static std::map<std::string, std::vector<std::string>> plurals
{ {
{"days", {"jour", "jours"}}, {"%d days", {"%d jour", "%d jours"}},
{"hours", {"heure", "heures"}}, {"%d hours", {"%d heure", "%d heures"}},
{"minutes", {"minute", "minutes"}}, {"%d minutes", {"%d minute", "%d minutes"}},
{"seconds", {"seconde", "secondes"}}, {"%d seconds", {"%d seconde", "%d secondes"}},
{"", {"", ""}}, {"", {"", ""}},
}; };

View File

@@ -31,9 +31,9 @@ namespace german // language namespace
static std::map<std::string, std::string> strings static std::map<std::string, std::string> strings
{ {
{"KiB", "KiB"}, {"%.2f KiB", "%.2f KiB"},
{"MiB", "MiB"}, {"%.2f MiB", "%.2f MiB"},
{"GiB", "GiB"}, {"%.2f GiB", "%.2f GiB"},
{"building", "In Bau"}, {"building", "In Bau"},
{"failed", "fehlgeschlagen"}, {"failed", "fehlgeschlagen"},
{"expiring", "läuft ab"}, {"expiring", "läuft ab"},
@@ -69,7 +69,7 @@ namespace german // language namespace
{"Family", "Familie"}, {"Family", "Familie"},
{"Tunnel creation success rate", "Erfolgsrate der Tunnelerstellung"}, {"Tunnel creation success rate", "Erfolgsrate der Tunnelerstellung"},
{"Received", "Eingegangen"}, {"Received", "Eingegangen"},
{"KiB/s", "KiB/s"}, {"%.2f KiB/s", "%.2f KiB/s"},
{"Sent", "Gesendet"}, {"Sent", "Gesendet"},
{"Transit", "Transit"}, {"Transit", "Transit"},
{"Data path", "Datenpfad"}, {"Data path", "Datenpfad"},
@@ -95,7 +95,7 @@ namespace german // language namespace
{"Type", "Typ"}, {"Type", "Typ"},
{"EncType", "Verschlüsselungstyp"}, {"EncType", "Verschlüsselungstyp"},
{"Inbound tunnels", "Eingehende Tunnel"}, {"Inbound tunnels", "Eingehende Tunnel"},
{"ms", "ms"}, {"%dms", "%dms"},
{"Outbound tunnels", "Ausgehende Tunnel"}, {"Outbound tunnels", "Ausgehende Tunnel"},
{"Tags", "Tags"}, {"Tags", "Tags"},
{"Incoming", "Eingehend"}, {"Incoming", "Eingehend"},
@@ -199,10 +199,10 @@ namespace german // language namespace
static std::map<std::string, std::vector<std::string>> plurals static std::map<std::string, std::vector<std::string>> plurals
{ {
{"days", {"Tag", "Tage"}}, {"%d days", {"%d Tag", "%d Tage"}},
{"hours", {"Stunde", "Stunden"}}, {"%d hours", {"%d Stunde", "%d Stunden"}},
{"minutes", {"Minute", "Minuten"}}, {"%d minutes", {"%d Minute", "%d Minuten"}},
{"seconds", {"Sekunde", "Sekunden"}}, {"%d seconds", {"%d Sekunde", "%d Sekunden"}},
{"", {"", ""}}, {"", {"", ""}},
}; };

View File

@@ -1,11 +1,12 @@
/* /*
* Copyright (c) 2021-2022, The PurpleI2P Project * Copyright (c) 2021-2023, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
* See full license text in LICENSE file at top of project tree * See full license text in LICENSE file at top of project tree
*/ */
#include <clocale>
#include "ClientContext.h" #include "ClientContext.h"
#include "I18N_langs.h" #include "I18N_langs.h"
#include "I18N.h" #include "I18N.h"
@@ -18,9 +19,15 @@ namespace i18n
{ {
const auto it = i2p::i18n::languages.find(lang); const auto it = i2p::i18n::languages.find(lang);
if (it == i2p::i18n::languages.end()) // fallback if (it == i2p::i18n::languages.end()) // fallback
{
i2p::client::context.SetLanguage (i2p::i18n::english::GetLocale()); i2p::client::context.SetLanguage (i2p::i18n::english::GetLocale());
setlocale(LC_NUMERIC, "english");
}
else else
{
i2p::client::context.SetLanguage (it->second.LocaleFunc()); i2p::client::context.SetLanguage (it->second.LocaleFunc());
setlocale(LC_NUMERIC, lang.c_str()); // set decimal point based on language
}
} }
std::string translate (const std::string& arg) std::string translate (const std::string& arg)

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2021-2022, The PurpleI2P Project * Copyright (c) 2021-2023, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@@ -74,10 +74,64 @@ namespace i18n
} // i18n } // i18n
} // i2p } // i2p
template<typename... TArgs> /**
std::string tr (TArgs&&... args) * @brief Get translation of string
* @param arg String with message
*/
template<typename TValue>
std::string tr (TValue&& arg)
{ {
return i2p::i18n::translate(std::forward<TArgs>(args)...); return i2p::i18n::translate(std::forward<TValue>(arg));
}
/**
* @brief Get translation of string and format it
* @param arg String with message
* @param args Array of arguments for string formatting
*/
template<typename TValue, typename... TArgs>
std::string tr (TValue&& arg, TArgs&&... args)
{
std::string tr_str = i2p::i18n::translate(std::forward<TValue>(arg));
size_t size = std::snprintf(NULL, 0, tr_str.c_str(), std::forward<TArgs>(args)...);
size = size + 1;
std::string str(size, 0);
std::snprintf(&str.front(), size, tr_str.c_str(), std::forward<TArgs>(args)...);
return str;
}
/**
* @brief Get translation of string with plural forms
* @param arg String with message in singular form
* @param arg2 String with message in plural form
* @param n Integer, used for selection of form
*/
template<typename TValue, typename TValue2>
std::string ntr (TValue&& arg, TValue2&& arg2, int& n)
{
return i2p::i18n::translate(std::forward<TValue>(arg), std::forward<TValue2>(arg2), std::forward<int>(n));
}
/**
* @brief Get translation of string with plural forms and format it
* @param arg String with message in singular form
* @param arg2 String with message in plural form
* @param n Integer, used for selection of form
* @param args Array of arguments for string formatting
*/
template<typename TValue, typename TValue2, typename... TArgs>
std::string ntr (TValue&& arg, TValue2&& arg2, int& n, TArgs&&... args)
{
std::string tr_str = i2p::i18n::translate(std::forward<TValue>(arg), std::forward<TValue2>(arg2), std::forward<int>(n));
size_t size = std::snprintf(NULL, 0, tr_str.c_str(), std::forward<TArgs>(args)...);
size = size + 1;
std::string str(size, 0);
std::snprintf(&str.front(), size, tr_str.c_str(), std::forward<TArgs>(args)...);
return str;
} }
#endif // __I18N_H__ #endif // __I18N_H__

View File

@@ -26,12 +26,14 @@ namespace i18n
namespace afrikaans { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); } namespace afrikaans { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); }
namespace armenian { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); } namespace armenian { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); }
namespace chinese { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); } namespace chinese { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); }
namespace czech { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); }
namespace english { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); } namespace english { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); }
namespace french { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); } namespace french { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); }
namespace german { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); } namespace german { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); }
namespace italian { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); } namespace italian { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); }
namespace russian { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); } namespace russian { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); }
namespace spanish { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); } namespace spanish { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); }
namespace swedish { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); }
namespace turkmen { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); } namespace turkmen { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); }
namespace ukrainian { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); } namespace ukrainian { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); }
namespace uzbek { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); } namespace uzbek { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); }
@@ -44,12 +46,14 @@ namespace i18n
{ "afrikaans", {"Afrikaans", "af", i2p::i18n::afrikaans::GetLocale} }, { "afrikaans", {"Afrikaans", "af", i2p::i18n::afrikaans::GetLocale} },
{ "armenian", {"hայերէն", "hy", i2p::i18n::armenian::GetLocale} }, { "armenian", {"hայերէն", "hy", i2p::i18n::armenian::GetLocale} },
{ "chinese", {"简体字", "zh-CN", i2p::i18n::chinese::GetLocale} }, { "chinese", {"简体字", "zh-CN", i2p::i18n::chinese::GetLocale} },
{ "czech", {"čeština", "cs", i2p::i18n::czech::GetLocale} },
{ "english", {"English", "en", i2p::i18n::english::GetLocale} }, { "english", {"English", "en", i2p::i18n::english::GetLocale} },
{ "french", {"Français", "fr", i2p::i18n::french::GetLocale} }, { "french", {"Français", "fr", i2p::i18n::french::GetLocale} },
{ "german", {"Deutsch", "de", i2p::i18n::german::GetLocale} }, { "german", {"Deutsch", "de", i2p::i18n::german::GetLocale} },
{ "italian", {"Italiano", "it", i2p::i18n::italian::GetLocale} }, { "italian", {"Italiano", "it", i2p::i18n::italian::GetLocale} },
{ "russian", {"Русский язык", "ru", i2p::i18n::russian::GetLocale} }, { "russian", {"Русский язык", "ru", i2p::i18n::russian::GetLocale} },
{ "spanish", {"Español", "es", i2p::i18n::spanish::GetLocale} }, { "spanish", {"Español", "es", i2p::i18n::spanish::GetLocale} },
{ "swedish", {"Svenska", "sv", i2p::i18n::swedish::GetLocale} },
{ "turkmen", {"Türkmen dili", "tk", i2p::i18n::turkmen::GetLocale} }, { "turkmen", {"Türkmen dili", "tk", i2p::i18n::turkmen::GetLocale} },
{ "ukrainian", {"Украї́нська мо́ва", "uk", i2p::i18n::ukrainian::GetLocale} }, { "ukrainian", {"Украї́нська мо́ва", "uk", i2p::i18n::ukrainian::GetLocale} },
{ "uzbek", {"Oʻzbek", "uz", i2p::i18n::uzbek::GetLocale} }, { "uzbek", {"Oʻzbek", "uz", i2p::i18n::uzbek::GetLocale} },

View File

@@ -31,9 +31,9 @@ namespace italian // language namespace
static std::map<std::string, std::string> strings static std::map<std::string, std::string> strings
{ {
{"KiB", "KiB"}, {"%.2f KiB", "%.2f KiB"},
{"MiB", "MiB"}, {"%.2f MiB", "%.2f MiB"},
{"GiB", "GiB"}, {"%.2f GiB", "%.2f GiB"},
{"building", "in costruzione"}, {"building", "in costruzione"},
{"failed", "fallito"}, {"failed", "fallito"},
{"expiring", "in scadenza"}, {"expiring", "in scadenza"},
@@ -69,7 +69,7 @@ namespace italian // language namespace
{"Family", "Famiglia"}, {"Family", "Famiglia"},
{"Tunnel creation success rate", "Percentuale di tunnel creati con successo"}, {"Tunnel creation success rate", "Percentuale di tunnel creati con successo"},
{"Received", "Ricevuti"}, {"Received", "Ricevuti"},
{"KiB/s", "KiB/s"}, {"%.2f KiB/s", "%.2f KiB/s"},
{"Sent", "Inviati"}, {"Sent", "Inviati"},
{"Transit", "Transitati"}, {"Transit", "Transitati"},
{"Data path", "Percorso dati"}, {"Data path", "Percorso dati"},
@@ -95,7 +95,7 @@ namespace italian // language namespace
{"Type", "Tipologia"}, {"Type", "Tipologia"},
{"EncType", "Tipo di crittografia"}, {"EncType", "Tipo di crittografia"},
{"Inbound tunnels", "Tunnel in entrata"}, {"Inbound tunnels", "Tunnel in entrata"},
{"ms", "ms"}, {"%dms", "%dms"},
{"Outbound tunnels", "Tunnel in uscita"}, {"Outbound tunnels", "Tunnel in uscita"},
{"Tags", "Tag"}, {"Tags", "Tag"},
{"Incoming", "In entrata"}, {"Incoming", "In entrata"},
@@ -199,10 +199,10 @@ namespace italian // language namespace
static std::map<std::string, std::vector<std::string>> plurals static std::map<std::string, std::vector<std::string>> plurals
{ {
{"days", {"giorno", "giorni"}}, {"%d days", {"%d giorno", "%d giorni"}},
{"hours", {"ora", "ore"}}, {"%d hours", {"%d ora", "%d ore"}},
{"minutes", {"minuto", "minuti"}}, {"%d minutes", {"%d minuto", "%d minuti"}},
{"seconds", {"secondo", "secondi"}}, {"%d seconds", {"%d secondo", "%d secondi"}},
{"", {"", ""}}, {"", {"", ""}},
}; };

View File

@@ -31,9 +31,9 @@ namespace russian // language namespace
static std::map<std::string, std::string> strings static std::map<std::string, std::string> strings
{ {
{"KiB", "КиБ"}, {"%.2f KiB", "%.2f КиБ"},
{"MiB", "МиБ"}, {"%.2f MiB", "%.2f МиБ"},
{"GiB", "ГиБ"}, {"%.2f GiB", "%.2f ГиБ"},
{"building", "строится"}, {"building", "строится"},
{"failed", "неудачный"}, {"failed", "неудачный"},
{"expiring", "истекает"}, {"expiring", "истекает"},
@@ -68,7 +68,7 @@ namespace russian // language namespace
{"Family", "Семейство"}, {"Family", "Семейство"},
{"Tunnel creation success rate", "Успешно построенных туннелей"}, {"Tunnel creation success rate", "Успешно построенных туннелей"},
{"Received", "Получено"}, {"Received", "Получено"},
{"KiB/s", "КиБ/с"}, {"%.2f KiB/s", "%.2f КиБ/с"},
{"Sent", "Отправлено"}, {"Sent", "Отправлено"},
{"Transit", "Транзит"}, {"Transit", "Транзит"},
{"Data path", "Путь к данным"}, {"Data path", "Путь к данным"},
@@ -94,7 +94,7 @@ namespace russian // language namespace
{"Type", "Тип"}, {"Type", "Тип"},
{"EncType", "ТипШифр"}, {"EncType", "ТипШифр"},
{"Inbound tunnels", "Входящие туннели"}, {"Inbound tunnels", "Входящие туннели"},
{"ms", "мс"}, {"%dms", "%dмс"},
{"Outbound tunnels", "Исходящие туннели"}, {"Outbound tunnels", "Исходящие туннели"},
{"Tags", "Теги"}, {"Tags", "Теги"},
{"Incoming", "Входящие"}, {"Incoming", "Входящие"},
@@ -198,10 +198,10 @@ namespace russian // language namespace
static std::map<std::string, std::vector<std::string>> plurals static std::map<std::string, std::vector<std::string>> plurals
{ {
{"days", {"день", "дня", "дней"}}, {"%d days", {"%d день", "%d дня", "%d дней"}},
{"hours", {"час", "часа", "часов"}}, {"%d hours", {"%d час", "%d часа", "%d часов"}},
{"minutes", {"минуту", "минуты", "минут"}}, {"%d minutes", {"%d минуту", "%d минуты", "%d минут"}},
{"seconds", {"секунду", "секунды", "секунд"}}, {"%d seconds", {"%d секунду", "%d секунды", "%d секунд"}},
{"", {"", "", ""}}, {"", {"", "", ""}},
}; };

View File

@@ -31,9 +31,9 @@ namespace spanish // language namespace
static std::map<std::string, std::string> strings static std::map<std::string, std::string> strings
{ {
{"KiB", "KiB"}, {"%.2f KiB", "%.2f KiB"},
{"MiB", "MiB"}, {"%.2f MiB", "%.2f MiB"},
{"GiB", "GiB"}, {"%.2f GiB", "%.2f GiB"},
{"building", "pendiente"}, {"building", "pendiente"},
{"failed", "fallido"}, {"failed", "fallido"},
{"expiring", "expiró"}, {"expiring", "expiró"},
@@ -69,7 +69,7 @@ namespace spanish // language namespace
{"Family", "Familia"}, {"Family", "Familia"},
{"Tunnel creation success rate", "Tasa de éxito de creación de túneles"}, {"Tunnel creation success rate", "Tasa de éxito de creación de túneles"},
{"Received", "Recibido"}, {"Received", "Recibido"},
{"KiB/s", "KiB/s"}, {"%.2f KiB/s", "%.2f KiB/s"},
{"Sent", "Enviado"}, {"Sent", "Enviado"},
{"Transit", "Tránsito"}, {"Transit", "Tránsito"},
{"Data path", "Ruta de datos"}, {"Data path", "Ruta de datos"},
@@ -95,7 +95,7 @@ namespace spanish // language namespace
{"Type", "Tipo"}, {"Type", "Tipo"},
{"EncType", "TipoEncrip"}, {"EncType", "TipoEncrip"},
{"Inbound tunnels", "Túneles entrantes"}, {"Inbound tunnels", "Túneles entrantes"},
{"ms", "ms"}, {"%dms", "%dms"},
{"Outbound tunnels", "Túneles salientes"}, {"Outbound tunnels", "Túneles salientes"},
{"Tags", "Etiquetas"}, {"Tags", "Etiquetas"},
{"Incoming", "Entrante"}, {"Incoming", "Entrante"},
@@ -199,10 +199,10 @@ namespace spanish // language namespace
static std::map<std::string, std::vector<std::string>> plurals static std::map<std::string, std::vector<std::string>> plurals
{ {
{"days", {"día", "días"}}, {"%d days", {"%d día", "%d días"}},
{"hours", {"hora", "horas"}}, {"%d hours", {"%d hora", "%d horas"}},
{"minutes", {"minuto", "minutos"}}, {"%d minutes", {"%d minuto", "%d minutos"}},
{"seconds", {"segundo", "segundos"}}, {"%d seconds", {"%d segundo", "%d segundos"}},
{"", {"", ""}}, {"", {"", ""}},
}; };

217
i18n/Swedish.cpp Normal file
View File

@@ -0,0 +1,217 @@
/*
* Copyright (c) 2023, The PurpleI2P Project
*
* This file is part of Purple i2pd project and licensed under BSD3
*
* See full license text in LICENSE file at top of project tree
*/
#include <map>
#include <vector>
#include <string>
#include <memory>
#include "I18N.h"
// Swedish localization file
namespace i2p
{
namespace i18n
{
namespace swedish // language namespace
{
// language name in lowercase
static std::string language = "swedish";
// See for language plural forms here:
// https://localization-guide.readthedocs.io/en/latest/l10n/pluralforms.html
static int plural (int n) {
return n != 1 ? 1 : 0;
}
static std::map<std::string, std::string> strings
{
{"%.2f KiB", "%.2f KiB"},
{"%.2f MiB", "%.2f MiB"},
{"%.2f GiB", "%.2f GiB"},
{"building", "bygger"},
{"failed", "misslyckad"},
{"expiring", "utgår"},
{"established", "upprättad"},
{"unknown", "okänt"},
{"exploratory", "utforskande"},
{"Purple I2P Webconsole", "Purple I2P Webbkonsoll"},
{"<b>i2pd</b> webbkonsoll", "<b>i2pd</b>-Webbkonsoll"},
{"Main page", "Huvudsida"},
{"Router commands", "Routerkommandon"},
{"Local Destinations", "Lokala Platser"},
{"LeaseSets", "Hyresuppsättningar"},
{"Tunnels", "Tunnlar"},
{"Transit Tunnels", "Förmedlande Tunnlar"},
{"Transports", "Transporter"},
{"I2P tunnels", "I2P-tunnlar"},
{"SAM sessions", "SAM-perioder"},
{"ERROR", "FEL"},
{"OK", "OK"},
{"Testing", "Prövar"},
{"Firewalled", "Bakom Brandvägg"},
{"Unknown", "Okänt"},
{"Proxy", "Proxy"},
{"Mesh", "Mesh"},
{"Error", "Fel"},
{"Clock skew", "Tidsförskjutning"},
{"Offline", "Nedkopplad"},
{"Symmetric NAT", "Symmetrisk NAT"},
{"Uptime", "Upptid"},
{"Network status", "Nätverkstillstånd"},
{"Network status v6", "Nätverkstillstånd v6"},
{"Stopping in", "Avstängd om"},
{"Family", "Familj"},
{"Tunnel creation success rate", "Andel framgångsrika tunnlar"},
{"Received", "Mottaget"},
{"%.2f KiB/s", "%.2f KiB/s"},
{"Sent", "Skickat"},
{"Transit", "Förmedlat"},
{"Data path", "Sökväg"},
{"Hidden content. Press on text to see.", "Dolt innehåll. Tryck för att visa."},
{"Router Ident", "Routeridentitet"},
{"Router Family", "Routerfamilj"},
{"Router Caps", "Routerbegränsningar"},
{"Version", "Version"},
{"Our external address", "Vår externa adress"},
{"supported", "stöds"},
{"Routers", "Routrar"},
{"Floodfills", "Översvämningsfyllare"},
{"Client Tunnels", "Klienttunnlar"},
{"Services", "Tjänster"},
{"Enabled", "Påslaget"},
{"Disabled", "Avslaget"},
{"Encrypted B33 address", "Krypterad B33-Adress"},
{"Address registration line", "Adressregistreringsrad"},
{"Domain", "Domän"},
{"Generate", "Skapa"},
{"<b>Note:</b> result string can be used only for registering 2LD domains (example.i2p). For registering subdomains please use i2pd-tools.", "<b>Uppmärksamma:</b> den resulterande strängen kan enbart användas för att registrera 2LD-domäner (exempel.i2p). För att registrera underdomäner, vänligen använd i2pd-tools."},
{"Address", "Adress"},
{"Type", "Typ"},
{"EncType", "EncTyp"},
{"Inbound tunnels", "Ingående Tunnlar"},
{"%dms", "%dms"},
{"Outbound tunnels", "Utgående Tunnlar"},
{"Tags", "Taggar"},
{"Incoming", "Ingående"},
{"Outgoing", "Utgående"},
{"Destination", "Plats"},
{"Amount", "Mängd"},
{"Incoming Tags", "Ingående Taggar"},
{"Tags sessions", "Tagg-perioder"},
{"Status", "Tillstånd"},
{"Local Destination", "Lokal Plats"},
{"Streams", "Strömmar"},
{"Close stream", "Stäng strömmen"},
{"I2CP session not found", "I2CP-period hittades inte"},
{"I2CP is not enabled", "I2CP är inte påslaget"},
{"Invalid", "Ogiltig"},
{"Store type", "Lagringstyp"},
{"Expires", "Utgångsdatum"},
{"Non Expired Leases", "Ickeutgångna Hyresuppsättningar"},
{"Gateway", "Gateway"},
{"TunnelID", "TunnelID"},
{"EndDate", "EndDate"},
{"not floodfill", "inte Översvämningsfyllare"},
{"Queue size", "Köstorlek"},
{"Run peer test", "Utför utsiktstest"},
{"Decline transit tunnels", "Avvisa förmedlande tunnlar"},
{"Accept transit tunnels", "Tillåt förmedlande tunnlar"},
{"Cancel graceful shutdown", "Avbryt välvillig avstängning"},
{"Start graceful shutdown", "Påbörja välvillig avstängning"},
{"Force shutdown", "Tvingad avstängning"},
{"Reload external CSS styles", "Ladda om externa CSS-stilar"},
{"<b>Note:</b> any action done here are not persistent and not changes your config files.", "<b>Uppmärksamma:</b> inga ändringar här är beständiga eller påverkar dina inställningsfiler."},
{"Logging level", "Protokollförningsnivå"},
{"Transit tunnels limit", "Begränsa förmedlande tunnlar"},
{"Change", "Ändra"},
{"Change language", "Ändra språk"},
{"no transit tunnels currently built", "inga förmedlande tunnlar har byggts"},
{"SAM disabled", "SAM avslaget"},
{"no sessions currently running", "inga perioder igång"},
{"SAM session not found", "SAM-perioder hittades ej"},
{"SAM Session", "SAM-period"},
{"Server Tunnels", "Värdtunnlar"},
{"Client Forwards", "Klientförpassningar"},
{"Server Forwards", "Värdförpassningar"},
{"Unknown page", "Okänd sida"},
{"Invalid token", "Ogiltig polett"},
{"SUCCESS", "FRAMGÅNG"},
{"Stream closed", "Ström stängd"},
{"Stream not found or already was closed", "Strömmen hittades inte eller var redan avslutad"},
{"Destination not found", "Plats hittades ej"},
{"StreamID can't be null", "Ström-ID kan inte vara null"},
{"Return to destination page", "Återvänd till platssidan"},
{"You will be redirected in 5 seconds", "Du omdirigeras inom fem sekunder"},
{"Transit tunnels count must not exceed 65535", "Förmedlande tunnlar får inte överstiga 65535"},
{"Back to commands list", "Tillbaka till kommandolistan"},
{"Register at reg.i2p", "Registrera vid reg.i2p"},
{"Description", "Beskrivning"},
{"A bit information about service on domain", "Ett stycke information om domänens tjänst"},
{"Submit", "Skicka"},
{"Domain can't end with .b32.i2p", "Domänen får inte sluta med .b32.i2p"},
{"Domain must end with .i2p", "Domänen måste sluta med .i2p"},
{"Such destination is not found", "En sådan plats hittas ej"},
{"Unknown command", "Okänt kommando"},
{"Command accepted", "Kommando accepterades"},
{"Proxy error", "Proxyfel"},
{"Proxy info", "Proxyinfo"},
{"Proxy error: Host not found", "Proxyfel: Värden hittades ej"},
{"Remote host not found in router's addressbook", "Främmande värd hittades inte i routerns adressbok"},
{"You may try to find this host on jump services below", "Du kan försöka att hitta värden genom hopptjänsterna nedan"},
{"Invalid request", "Ogiltig förfrågan"},
{"Proxy unable to parse your request", "Proxyt kan inte behandla din förfrågan"},
{"addresshelper is not supported", "adresshjälparen stöds ej"},
{"Host", "Värd"},
{"added to router's addressbook from helper", "tillagd i routerns adressbok från adresshjälparen"},
{"Click here to proceed:", "Tryck här för att fortsätta:"},
{"Continue", "Fortsätt"},
{"Addresshelper found", "Adresshjälpare hittad"},
{"already in router's addressbook", "finns redan i routerns adressbok"},
{"Click here to update record:", "Tryck här för att uppdatera:"},
{"invalid request uri", "ogiltig förfrågnings-URI"},
{"Can't detect destination host from request", "Kan inte upptäcka platsvärden från förfrågan"},
{"Outproxy failure", "Utproxyfel"},
{"bad outproxy settings", "ogiltig utproxyinställning"},
{"not inside I2P network, but outproxy is not enabled", "adressen är inte inom I2P-näverket, men utproxy är inte påslaget"},
{"unknown outproxy url", "okänt Utproxy-URL"},
{"cannot resolve upstream proxy", "hittar inte uppströmsproxyt"},
{"hostname too long", "värdnamnet är för långt"},
{"cannot connect to upstream socks proxy", "kan inte ansluta till uppströmsproxyt"},
{"Cannot negotiate with socks proxy", "Kan inte förhandla med socksproxyt"},
{"CONNECT error", "CONNECT-fel"},
{"Failed to Connect", "Anslutningen misslyckades"},
{"socks proxy error", "Socksproxyfel"},
{"failed to send request to upstream", "förfrågan uppströms kunde ej skickas"},
{"No Reply From socks proxy", "Fick inget svar från socksproxyt"},
{"cannot connect", "kan inte ansluta"},
{"http out proxy not implemented", "HTTP-Utproxy ej implementerat"},
{"cannot connect to upstream http proxy", "Kan inte ansluta till uppströms HTTP-proxy"},
{"Host is down", "Värden är nere"},
{"Can't create connection to requested host, it may be down. Please try again later.", "Kan inte ansluta till värden, den kan vara nere. Vänligen försök senare."},
{"", ""},
};
static std::map<std::string, std::vector<std::string>> plurals
{
{"%d days", {"%d Dag", "%d Dagar"}},
{"%d hours", {"%d Timme", "%d Timmar"}},
{"%d minutes", {"%d Minut", "%d Minuter"}},
{"%d seconds", {"%d Sekund", "%d Sekunder"}},
{"", {"", ""}},
};
std::shared_ptr<const i2p::i18n::Locale> GetLocale()
{
return std::make_shared<i2p::i18n::Locale>(language, strings, plurals, [] (int n)->int { return plural(n); });
}
} // language
} // i18n
} // i2p

View File

@@ -31,9 +31,9 @@ namespace turkmen // language namespace
static std::map<std::string, std::string> strings static std::map<std::string, std::string> strings
{ {
{"KiB", "KiB"}, {"%.2f KiB", "%.2f KiB"},
{"MiB", "MiB"}, {"%.2f MiB", "%.2f MiB"},
{"GiB", "GiB"}, {"%.2f GiB", "%.2f GiB"},
{"building", "bina"}, {"building", "bina"},
{"failed", "şowsuz"}, {"failed", "şowsuz"},
{"expiring", "möhleti gutarýar"}, {"expiring", "möhleti gutarýar"},
@@ -68,7 +68,7 @@ namespace turkmen // language namespace
{"Family", "Maşgala"}, {"Family", "Maşgala"},
{"Tunnel creation success rate", "Gurlan teneller üstünlikli gurlan teneller"}, {"Tunnel creation success rate", "Gurlan teneller üstünlikli gurlan teneller"},
{"Received", "Alnan"}, {"Received", "Alnan"},
{"KiB/s", "KiB/s"}, {"%.2f KiB/s", "%.2f KiB/s"},
{"Sent", "Ýerleşdirildi"}, {"Sent", "Ýerleşdirildi"},
{"Transit", "Tranzit"}, {"Transit", "Tranzit"},
{"Data path", "Maglumat ýoly"}, {"Data path", "Maglumat ýoly"},
@@ -94,7 +94,7 @@ namespace turkmen // language namespace
{"Type", "Görnüş"}, {"Type", "Görnüş"},
{"EncType", "Şifrlemek görnüşi"}, {"EncType", "Şifrlemek görnüşi"},
{"Inbound tunnels", "Gelýän tuneller"}, {"Inbound tunnels", "Gelýän tuneller"},
{"ms", "ms"}, {"%dms", "%dms"},
{"Outbound tunnels", "Çykýan tuneller"}, {"Outbound tunnels", "Çykýan tuneller"},
{"Tags", "Bellikler"}, {"Tags", "Bellikler"},
{"Incoming", "Gelýän"}, {"Incoming", "Gelýän"},
@@ -198,10 +198,10 @@ namespace turkmen // language namespace
static std::map<std::string, std::vector<std::string>> plurals static std::map<std::string, std::vector<std::string>> plurals
{ {
{"days", {"gün", "gün"}}, {"%d days", {"%d gün", "%d gün"}},
{"hours", {"sagat", "sagat"}}, {"%d hours", {"%d sagat", "%d sagat"}},
{"minutes", {"minut", "minut"}}, {"%d minutes", {"%d minut", "%d minut"}},
{"seconds", {"sekunt", "sekunt"}}, {"%d seconds", {"%d sekunt", "%d sekunt"}},
{"", {"", ""}}, {"", {"", ""}},
}; };

View File

@@ -31,9 +31,9 @@ namespace ukrainian // language namespace
static std::map<std::string, std::string> strings static std::map<std::string, std::string> strings
{ {
{"KiB", "КіБ"}, {"%.2f KiB", "%.2f КіБ"},
{"MiB", "МіБ"}, {"%.2f MiB", "%.2f МіБ"},
{"GiB", "ГіБ"}, {"%.2f GiB", "%.2f ГіБ"},
{"building", "будується"}, {"building", "будується"},
{"failed", "невдалий"}, {"failed", "невдалий"},
{"expiring", "завершується"}, {"expiring", "завершується"},
@@ -68,7 +68,7 @@ namespace ukrainian // language namespace
{"Family", "Сімейство"}, {"Family", "Сімейство"},
{"Tunnel creation success rate", "Успішно побудованих тунелів"}, {"Tunnel creation success rate", "Успішно побудованих тунелів"},
{"Received", "Отримано"}, {"Received", "Отримано"},
{"KiB/s", "КіБ/с"}, {"%.2f KiB/s", "%.2f КіБ/с"},
{"Sent", "Відправлено"}, {"Sent", "Відправлено"},
{"Transit", "Транзит"}, {"Transit", "Транзит"},
{"Data path", "Шлях до даних"}, {"Data path", "Шлях до даних"},
@@ -94,7 +94,7 @@ namespace ukrainian // language namespace
{"Type", "Тип"}, {"Type", "Тип"},
{"EncType", "ТипШифр"}, {"EncType", "ТипШифр"},
{"Inbound tunnels", "Вхідні тунелі"}, {"Inbound tunnels", "Вхідні тунелі"},
{"ms", "мс"}, {"%dms", "%dмс"},
{"Outbound tunnels", "Вихідні тунелі"}, {"Outbound tunnels", "Вихідні тунелі"},
{"Tags", "Теги"}, {"Tags", "Теги"},
{"Incoming", "Вхідні"}, {"Incoming", "Вхідні"},
@@ -198,10 +198,10 @@ namespace ukrainian // language namespace
static std::map<std::string, std::vector<std::string>> plurals static std::map<std::string, std::vector<std::string>> plurals
{ {
{"days", {"день", "дня", "днів"}}, {"%d days", {"%d день", "%d дня", "%d днів"}},
{"hours", {"годину", "години", "годин"}}, {"%d hours", {"%d годину", "%d години", "%d годин"}},
{"minutes", {"хвилину", "хвилини", "хвилин"}}, {"%d minutes", {"%d хвилину", "%d хвилини", "%d хвилин"}},
{"seconds", {"секунду", "секунди", "секунд"}}, {"%d seconds", {"%d секунду", "%d секунди", "%d секунд"}},
{"", {"", "", ""}}, {"", {"", "", ""}},
}; };

View File

@@ -31,9 +31,9 @@ namespace uzbek // language namespace
static std::map<std::string, std::string> strings static std::map<std::string, std::string> strings
{ {
{"KiB", "KiB"}, {"%.2f KiB", "%.2f KiB"},
{"MiB", "MiB"}, {"%.2f MiB", "%.2f MiB"},
{"GiB", "GiB"}, {"%.2f GiB", "%.2f GiB"},
{"building", "yaratilmoqda"}, {"building", "yaratilmoqda"},
{"failed", "muvaffaqiyatsiz"}, {"failed", "muvaffaqiyatsiz"},
{"expiring", "muddati tugaydi"}, {"expiring", "muddati tugaydi"},
@@ -68,7 +68,7 @@ namespace uzbek // language namespace
{"Family", "Oila"}, {"Family", "Oila"},
{"Tunnel creation success rate", "Tunnel yaratish muvaffaqiyat darajasi"}, {"Tunnel creation success rate", "Tunnel yaratish muvaffaqiyat darajasi"},
{"Received", "Qabul qilindi"}, {"Received", "Qabul qilindi"},
{"KiB/s", "KiB/s"}, {"%.2f KiB/s", "%.2f KiB/s"},
{"Sent", "Yuborilgan"}, {"Sent", "Yuborilgan"},
{"Transit", "Tranzit"}, {"Transit", "Tranzit"},
{"Data path", "Ma'lumotlar joylanishi"}, {"Data path", "Ma'lumotlar joylanishi"},
@@ -94,7 +94,7 @@ namespace uzbek // language namespace
{"Type", "Turi"}, {"Type", "Turi"},
{"EncType", "ShifrlashTuri"}, {"EncType", "ShifrlashTuri"},
{"Inbound tunnels", "Kirish tunnellari"}, {"Inbound tunnels", "Kirish tunnellari"},
{"ms", "ms"}, {"%dms", "%dms"},
{"Outbound tunnels", "Chiquvchi tunnellar"}, {"Outbound tunnels", "Chiquvchi tunnellar"},
{"Tags", "Teglar"}, {"Tags", "Teglar"},
{"Incoming", "Kiruvchi"}, {"Incoming", "Kiruvchi"},
@@ -198,10 +198,10 @@ namespace uzbek // language namespace
static std::map<std::string, std::vector<std::string>> plurals static std::map<std::string, std::vector<std::string>> plurals
{ {
{"days", {"kun", "kun"}}, {"%d days", {"%d kun", "%d kun"}},
{"hours", {"soat", "soat"}}, {"%d hours", {"%d soat", "%d soat"}},
{"minutes", {"daqiqa", "daqiqa"}}, {"%d minutes", {"%d daqiqa", "%d daqiqa"}},
{"seconds", {"soniya", "soniya"}}, {"%d seconds", {"%d soniya", "%d soniya"}},
{"", {"", ""}}, {"", {"", ""}},
}; };

View File

@@ -135,7 +135,7 @@ namespace data
//---------------------------------------------------------- //----------------------------------------------------------
const uint8_t B33_TWO_BYTES_SIGTYPE_FLAG = 0x01; const uint8_t B33_TWO_BYTES_SIGTYPE_FLAG = 0x01;
const uint8_t B33_PER_SECRET_FLAG = 0x02; // not used for now // const uint8_t B33_PER_SECRET_FLAG = 0x02; // not used for now
const uint8_t B33_PER_CLIENT_AUTH_FLAG = 0x04; const uint8_t B33_PER_CLIENT_AUTH_FLAG = 0x04;
BlindedPublicKey::BlindedPublicKey (std::shared_ptr<const IdentityEx> identity, bool clientAuth): BlindedPublicKey::BlindedPublicKey (std::shared_ptr<const IdentityEx> identity, bool clientAuth):

View File

@@ -77,7 +77,7 @@ namespace config {
limits.add_options() limits.add_options()
("limits.coresize", value<uint32_t>()->default_value(0), "Maximum size of corefile in Kb (0 - use system limit)") ("limits.coresize", value<uint32_t>()->default_value(0), "Maximum size of corefile in Kb (0 - use system limit)")
("limits.openfiles", value<uint16_t>()->default_value(0), "Maximum number of open files (0 - use system default)") ("limits.openfiles", value<uint16_t>()->default_value(0), "Maximum number of open files (0 - use system default)")
("limits.transittunnels", value<uint16_t>()->default_value(2500), "Maximum active transit sessions (default:2500)") ("limits.transittunnels", value<uint16_t>()->default_value(5000), "Maximum active transit tunnels (default:5000)")
("limits.ntcpsoft", value<uint16_t>()->default_value(0), "Ignored") ("limits.ntcpsoft", value<uint16_t>()->default_value(0), "Ignored")
("limits.ntcphard", value<uint16_t>()->default_value(0), "Ignored") ("limits.ntcphard", value<uint16_t>()->default_value(0), "Ignored")
("limits.ntcpthreads", value<uint16_t>()->default_value(1), "Ignored") ("limits.ntcpthreads", value<uint16_t>()->default_value(1), "Ignored")

View File

@@ -159,8 +159,10 @@ namespace crypto
// DH/ElGamal // DH/ElGamal
#if !defined(__x86_64__)
const int ELGAMAL_SHORT_EXPONENT_NUM_BITS = 226; const int ELGAMAL_SHORT_EXPONENT_NUM_BITS = 226;
const int ELGAMAL_SHORT_EXPONENT_NUM_BYTES = ELGAMAL_SHORT_EXPONENT_NUM_BITS/8+1; const int ELGAMAL_SHORT_EXPONENT_NUM_BYTES = ELGAMAL_SHORT_EXPONENT_NUM_BITS/8+1;
#endif
const int ELGAMAL_FULL_EXPONENT_NUM_BITS = 2048; const int ELGAMAL_FULL_EXPONENT_NUM_BITS = 2048;
const int ELGAMAL_FULL_EXPONENT_NUM_BYTES = ELGAMAL_FULL_EXPONENT_NUM_BITS/8; const int ELGAMAL_FULL_EXPONENT_NUM_BYTES = ELGAMAL_FULL_EXPONENT_NUM_BITS/8;

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2022, The PurpleI2P Project * Copyright (c) 2013-2023, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@@ -1138,19 +1138,25 @@ namespace client
template<typename Dest> template<typename Dest>
std::shared_ptr<i2p::stream::Stream> ClientDestination::CreateStreamSync (const Dest& dest, int port) std::shared_ptr<i2p::stream::Stream> ClientDestination::CreateStreamSync (const Dest& dest, int port)
{ {
volatile bool done = false;
std::shared_ptr<i2p::stream::Stream> stream; std::shared_ptr<i2p::stream::Stream> stream;
std::condition_variable streamRequestComplete; std::condition_variable streamRequestComplete;
std::mutex streamRequestCompleteMutex; std::mutex streamRequestCompleteMutex;
std::unique_lock<std::mutex> l(streamRequestCompleteMutex);
CreateStream ( CreateStream (
[&streamRequestComplete, &streamRequestCompleteMutex, &stream](std::shared_ptr<i2p::stream::Stream> s) [&done, &streamRequestComplete, &streamRequestCompleteMutex, &stream](std::shared_ptr<i2p::stream::Stream> s)
{ {
stream = s; stream = s;
std::unique_lock<std::mutex> l(streamRequestCompleteMutex); std::unique_lock<std::mutex> l(streamRequestCompleteMutex);
streamRequestComplete.notify_all (); streamRequestComplete.notify_all ();
done = true;
}, },
dest, port); dest, port);
streamRequestComplete.wait (l); while (!done)
{
std::unique_lock<std::mutex> l(streamRequestCompleteMutex);
if (!done)
streamRequestComplete.wait (l);
}
return stream; return stream;
} }

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2022, The PurpleI2P Project * Copyright (c) 2013-2023, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@@ -392,7 +392,8 @@ namespace i2p
clearText + ECIES_BUILD_REQUEST_RECORD_IV_KEY_OFFSET, clearText + ECIES_BUILD_REQUEST_RECORD_IV_KEY_OFFSET,
clearText[ECIES_BUILD_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_GATEWAY_FLAG, clearText[ECIES_BUILD_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_GATEWAY_FLAG,
clearText[ECIES_BUILD_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_ENDPOINT_FLAG); clearText[ECIES_BUILD_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_ENDPOINT_FLAG);
i2p::tunnel::tunnels.AddTransitTunnel (transitTunnel); if (!i2p::tunnel::tunnels.AddTransitTunnel (transitTunnel))
retCode = 30;
} }
else else
retCode = 30; // always reject with bandwidth reason (30) retCode = 30; // always reject with bandwidth reason (30)
@@ -558,7 +559,8 @@ namespace i2p
return; return;
} }
auto& noiseState = i2p::context.GetCurrentNoiseState (); auto& noiseState = i2p::context.GetCurrentNoiseState ();
uint8_t replyKey[32], layerKey[32], ivKey[32]; uint8_t replyKey[32]; // AEAD/Chacha20/Poly1305
i2p::crypto::AESKey layerKey, ivKey; // AES
i2p::crypto::HKDF (noiseState.m_CK, nullptr, 0, "SMTunnelReplyKey", noiseState.m_CK); i2p::crypto::HKDF (noiseState.m_CK, nullptr, 0, "SMTunnelReplyKey", noiseState.m_CK);
memcpy (replyKey, noiseState.m_CK + 32, 32); memcpy (replyKey, noiseState.m_CK + 32, 32);
i2p::crypto::HKDF (noiseState.m_CK, nullptr, 0, "SMTunnelLayerKey", noiseState.m_CK); i2p::crypto::HKDF (noiseState.m_CK, nullptr, 0, "SMTunnelLayerKey", noiseState.m_CK);
@@ -589,7 +591,8 @@ namespace i2p
layerKey, ivKey, layerKey, ivKey,
clearText[SHORT_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_GATEWAY_FLAG, clearText[SHORT_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_GATEWAY_FLAG,
clearText[SHORT_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_ENDPOINT_FLAG); clearText[SHORT_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_ENDPOINT_FLAG);
i2p::tunnel::tunnels.AddTransitTunnel (transitTunnel); if (!i2p::tunnel::tunnels.AddTransitTunnel (transitTunnel))
retCode = 30;
} }
// encrypt reply // encrypt reply

View File

@@ -309,7 +309,7 @@ namespace tunnel
std::vector<std::shared_ptr<I2NPMessage> > m_TunnelMsgs, m_TunnelGatewayMsgs; std::vector<std::shared_ptr<I2NPMessage> > m_TunnelMsgs, m_TunnelGatewayMsgs;
}; };
const uint16_t DEFAULT_MAX_NUM_TRANSIT_TUNNELS = 2500; const uint16_t DEFAULT_MAX_NUM_TRANSIT_TUNNELS = 5000;
void SetMaxNumTransitTunnels (uint16_t maxNumTransitTunnels); void SetMaxNumTransitTunnels (uint16_t maxNumTransitTunnels);
uint16_t GetMaxNumTransitTunnels (); uint16_t GetMaxNumTransitTunnels ();
} }

View File

@@ -49,29 +49,21 @@ namespace data
IdentityEx::IdentityEx(const uint8_t * publicKey, const uint8_t * signingKey, SigningKeyType type, CryptoKeyType cryptoType) IdentityEx::IdentityEx(const uint8_t * publicKey, const uint8_t * signingKey, SigningKeyType type, CryptoKeyType cryptoType)
{ {
/*uint8_t randomPaddingBlock[32]; uint8_t randomPaddingBlock[32];
RAND_bytes (randomPaddingBlock, 32);*/ RAND_bytes (randomPaddingBlock, 32);
if (cryptoType == CRYPTO_KEY_TYPE_ECIES_X25519_AEAD) if (cryptoType == CRYPTO_KEY_TYPE_ECIES_X25519_AEAD)
{ {
/*memcpy (m_StandardIdentity.publicKey, publicKey ? publicKey : randomPaddingBlock, 32); memcpy (m_StandardIdentity.publicKey, publicKey ? publicKey : randomPaddingBlock, 32);
for (int i = 0; i < 7; i++) // 224 bytes for (int i = 0; i < 7; i++) // 224 bytes
memcpy (m_StandardIdentity.publicKey + 32*(i + 1), randomPaddingBlock, 32);*/ memcpy (m_StandardIdentity.publicKey + 32*(i + 1), randomPaddingBlock, 32);
if (publicKey)
{
memcpy (m_StandardIdentity.publicKey, publicKey, 32);
RAND_bytes (m_StandardIdentity.publicKey + 32, 224);
}
else
RAND_bytes (m_StandardIdentity.publicKey, 256);
} }
else else
{ {
if (publicKey) if (publicKey)
memcpy (m_StandardIdentity.publicKey, publicKey, 256); memcpy (m_StandardIdentity.publicKey, publicKey, 256);
else else
RAND_bytes (m_StandardIdentity.publicKey, 256); for (int i = 0; i < 8; i++) // 256 bytes
/*for (int i = 0; i < 8; i++) // 256 bytes memcpy (m_StandardIdentity.publicKey + 32*i, randomPaddingBlock, 32);
memcpy (m_StandardIdentity.publicKey + 32*i, randomPaddingBlock, 32);*/
} }
if (type != SIGNING_KEY_TYPE_DSA_SHA1) if (type != SIGNING_KEY_TYPE_DSA_SHA1)
{ {
@@ -110,9 +102,8 @@ namespace data
case SIGNING_KEY_TYPE_REDDSA_SHA512_ED25519: case SIGNING_KEY_TYPE_REDDSA_SHA512_ED25519:
{ {
size_t padding = 128 - i2p::crypto::EDDSA25519_PUBLIC_KEY_LENGTH; // 96 = 128 - 32 size_t padding = 128 - i2p::crypto::EDDSA25519_PUBLIC_KEY_LENGTH; // 96 = 128 - 32
/*for (int i = 0; i < 3; i++) // 96 bytes for (int i = 0; i < 3; i++) // 96 bytes
memcpy (m_StandardIdentity.signingKey + 32*i, randomPaddingBlock, 32);*/ memcpy (m_StandardIdentity.signingKey + 32*i, randomPaddingBlock, 32);
RAND_bytes (m_StandardIdentity.signingKey, 96);
memcpy (m_StandardIdentity.signingKey + padding, signingKey, i2p::crypto::EDDSA25519_PUBLIC_KEY_LENGTH); memcpy (m_StandardIdentity.signingKey + padding, signingKey, i2p::crypto::EDDSA25519_PUBLIC_KEY_LENGTH);
break; break;
} }

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2020, The PurpleI2P Project * Copyright (c) 2013-2022, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@@ -18,11 +18,11 @@ namespace log {
/** /**
* @brief Maps our loglevel to their symbolic name * @brief Maps our loglevel to their symbolic name
*/ */
static const char * g_LogLevelStr[eNumLogLevels] = static const char *g_LogLevelStr[eNumLogLevels] =
{ {
"none", // eLogNone "none", // eLogNone
"error", // eLogError "error", // eLogError
"warn", // eLogWarn "warn", // eLogWarning
"info", // eLogInfo "info", // eLogInfo
"debug" // eLogDebug "debug" // eLogDebug
}; };
@@ -35,12 +35,12 @@ namespace log {
static const char *LogMsgColors[] = { "", "", "", "", "", "" }; static const char *LogMsgColors[] = { "", "", "", "", "", "" };
#else /* UNIX */ #else /* UNIX */
static const char *LogMsgColors[] = { static const char *LogMsgColors[] = {
[eLogNone] = "\033[0m", /* reset */ "\033[1;32m", /* none: green */
[eLogError] = "\033[1;31m", /* red */ "\033[1;31m", /* error: red */
[eLogWarning] = "\033[1;33m", /* yellow */ "\033[1;33m", /* warning: yellow */
[eLogInfo] = "\033[1;36m", /* cyan */ "\033[1;36m", /* info: cyan */
[eLogDebug] = "\033[1;34m", /* blue */ "\033[1;34m", /* debug: blue */
[eNumLogLevels] = "\033[0m", /* reset */ "\033[0m" /* reset */
}; };
#endif #endif
@@ -126,7 +126,7 @@ namespace log {
if (level == "none") { m_MinLevel = eLogNone; } if (level == "none") { m_MinLevel = eLogNone; }
else if (level == "error") { m_MinLevel = eLogError; } else if (level == "error") { m_MinLevel = eLogError; }
else if (level == "warn") { m_MinLevel = eLogWarning; } else if (level == "warn") { m_MinLevel = eLogWarning; }
else if (level == "info") { m_MinLevel = eLogInfo; } else if (level == "info") { m_MinLevel = eLogInfo; }
else if (level == "debug") { m_MinLevel = eLogDebug; } else if (level == "debug") { m_MinLevel = eLogDebug; }
else { else {
LogPrint(eLogError, "Log: Unknown loglevel: ", level); LogPrint(eLogError, "Log: Unknown loglevel: ", level);

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2022, The PurpleI2P Project * Copyright (c) 2013-2023, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@@ -139,7 +139,7 @@ namespace transport
m3p2[3] = 0; // flag m3p2[3] = 0; // flag
memcpy (m3p2 + 4, i2p::context.GetRouterInfo ().GetBuffer (), bufLen); // TODO: own RI should be protected by mutex memcpy (m3p2 + 4, i2p::context.GetRouterInfo ().GetBuffer (), bufLen); // TODO: own RI should be protected by mutex
// 2 bytes reserved // 2 bytes reserved
htobe32buf (options + 8, i2p::util::GetSecondsSinceEpoch ()); // tsA htobe32buf (options + 8, (i2p::util::GetMillisecondsSinceEpoch () + 500)/1000); // tsA, rounded to seconds
// 4 bytes reserved // 4 bytes reserved
// sign and encrypt options, use m_H as AD // sign and encrypt options, use m_H as AD
uint8_t nonce[12]; uint8_t nonce[12];
@@ -162,7 +162,7 @@ namespace transport
uint8_t options[16]; uint8_t options[16];
memset (options, 0, 16); memset (options, 0, 16);
htobe16buf (options + 2, paddingLen); // padLen htobe16buf (options + 2, paddingLen); // padLen
htobe32buf (options + 8, i2p::util::GetSecondsSinceEpoch ()); // tsB htobe32buf (options + 8, (i2p::util::GetMillisecondsSinceEpoch () + 500)/1000); // tsB, rounded to seconds
// sign and encrypt options, use m_H as AD // sign and encrypt options, use m_H as AD
uint8_t nonce[12]; uint8_t nonce[12];
memset (nonce, 0, 12); // set nonce to zero memset (nonce, 0, 12); // set nonce to zero
@@ -374,10 +374,16 @@ namespace transport
transports.PeerDisconnected (shared_from_this ()); transports.PeerDisconnected (shared_from_this ());
m_Server.RemoveNTCP2Session (shared_from_this ()); m_Server.RemoveNTCP2Session (shared_from_this ());
m_SendQueue.clear (); m_SendQueue.clear ();
m_SendQueueSize = 0;
LogPrint (eLogDebug, "NTCP2: Session terminated"); LogPrint (eLogDebug, "NTCP2: Session terminated");
} }
} }
void NTCP2Session::Close ()
{
m_Socket.close ();
}
void NTCP2Session::TerminateByTimeout () void NTCP2Session::TerminateByTimeout ()
{ {
SendTerminationAndTerminate (eNTCP2IdleTimeout); SendTerminationAndTerminate (eNTCP2IdleTimeout);
@@ -746,6 +752,8 @@ namespace transport
void NTCP2Session::ServerLogin () void NTCP2Session::ServerLogin ()
{ {
SetTerminationTimeout (NTCP2_ESTABLISH_TIMEOUT);
m_LastActivityTimestamp = i2p::util::GetSecondsSinceEpoch ();
m_Establisher->CreateEphemeralKey (); m_Establisher->CreateEphemeralKey ();
boost::asio::async_read (m_Socket, boost::asio::buffer(m_Establisher->m_SessionRequestBuffer, 64), boost::asio::transfer_all (), boost::asio::async_read (m_Socket, boost::asio::buffer(m_Establisher->m_SessionRequestBuffer, 64), boost::asio::transfer_all (),
std::bind(&NTCP2Session::HandleSessionRequestReceived, shared_from_this (), std::bind(&NTCP2Session::HandleSessionRequestReceived, shared_from_this (),
@@ -1050,7 +1058,10 @@ namespace transport
SendRouterInfo (); SendRouterInfo ();
} }
else else
{
SendQueue (); SendQueue ();
m_SendQueueSize = m_SendQueue.size ();
}
} }
} }
@@ -1158,7 +1169,7 @@ namespace transport
{ {
if (m_IsTerminated) return; if (m_IsTerminated) return;
for (auto it: msgs) for (auto it: msgs)
m_SendQueue.push_back (it); m_SendQueue.push_back (std::move (it));
if (!m_IsSending) if (!m_IsSending)
SendQueue (); SendQueue ();
else if (m_SendQueue.size () > NTCP2_MAX_OUTGOING_QUEUE_SIZE) else if (m_SendQueue.size () > NTCP2_MAX_OUTGOING_QUEUE_SIZE)
@@ -1167,6 +1178,7 @@ namespace transport
GetIdentHashBase64(), " exceeds ", NTCP2_MAX_OUTGOING_QUEUE_SIZE); GetIdentHashBase64(), " exceeds ", NTCP2_MAX_OUTGOING_QUEUE_SIZE);
Terminate (); Terminate ();
} }
m_SendQueueSize = m_SendQueue.size ();
} }
void NTCP2Session::SendLocalRouterInfo (bool update) void NTCP2Session::SendLocalRouterInfo (bool update)
@@ -1289,7 +1301,7 @@ namespace transport
for (auto& it: ntcpSessions) for (auto& it: ntcpSessions)
it.second->Terminate (); it.second->Terminate ();
for (auto& it: m_PendingIncomingSessions) for (auto& it: m_PendingIncomingSessions)
it->Terminate (); it.second->Terminate ();
} }
m_NTCP2Sessions.clear (); m_NTCP2Sessions.clear ();
@@ -1305,20 +1317,32 @@ namespace transport
{ {
if (!session) return false; if (!session) return false;
if (incoming) if (incoming)
m_PendingIncomingSessions.remove (session); m_PendingIncomingSessions.erase (session->GetRemoteEndpoint ().address ());
if (!session->GetRemoteIdentity ()) return false; if (!session->GetRemoteIdentity ())
{
LogPrint (eLogWarning, "NTCP2: Unknown identity for ", session->GetRemoteEndpoint ());
session->Terminate ();
return false;
}
auto& ident = session->GetRemoteIdentity ()->GetIdentHash (); auto& ident = session->GetRemoteIdentity ()->GetIdentHash ();
auto it = m_NTCP2Sessions.find (ident); auto it = m_NTCP2Sessions.find (ident);
if (it != m_NTCP2Sessions.end ()) if (it != m_NTCP2Sessions.end ())
{ {
LogPrint (eLogWarning, "NTCP2: Session to ", ident.ToBase64 (), " already exists"); LogPrint (eLogWarning, "NTCP2: Session with ", ident.ToBase64 (), " already exists. ", incoming ? "Replaced" : "Dropped");
if (incoming) if (incoming)
{
// replace by new session // replace by new session
it->second->Terminate (); auto s = it->second;
m_NTCP2Sessions.erase (it);
s->Terminate ();
}
else else
{
session->Terminate ();
return false; return false;
}
} }
m_NTCP2Sessions.insert (std::make_pair (ident, session)); m_NTCP2Sessions.emplace (ident, session);
return true; return true;
} }
@@ -1413,13 +1437,22 @@ namespace transport
if (!ec) if (!ec)
{ {
LogPrint (eLogDebug, "NTCP2: Connected from ", ep); LogPrint (eLogDebug, "NTCP2: Connected from ", ep);
if (conn) if (!i2p::util::net::IsInReservedRange(ep.address ()))
{ {
conn->SetRemoteEndpoint (ep); if (conn)
conn->ServerLogin (); {
m_PendingIncomingSessions.push_back (conn); if (m_PendingIncomingSessions.emplace (ep.address (), conn).second)
conn = nullptr; {
conn->SetRemoteEndpoint (ep);
conn->ServerLogin ();
conn = nullptr;
}
else
LogPrint (eLogInfo, "NTCP2: Incoming session from ", ep.address (), " is already pending");
}
} }
else
LogPrint (eLogError, "NTCP2: Incoming connection from invalid IP ", ep.address ());
} }
else else
LogPrint (eLogError, "NTCP2: Connected from error ", ec.message ()); LogPrint (eLogError, "NTCP2: Connected from error ", ec.message ());
@@ -1454,12 +1487,23 @@ namespace transport
if (!ec) if (!ec)
{ {
LogPrint (eLogDebug, "NTCP2: Connected from ", ep); LogPrint (eLogDebug, "NTCP2: Connected from ", ep);
if (conn) if (!i2p::util::net::IsInReservedRange(ep.address ()) ||
i2p::util::net::IsYggdrasilAddress (ep.address ()))
{ {
conn->SetRemoteEndpoint (ep); if (conn)
conn->ServerLogin (); {
m_PendingIncomingSessions.push_back (conn); if (m_PendingIncomingSessions.emplace (ep.address (), conn).second)
{
conn->SetRemoteEndpoint (ep);
conn->ServerLogin ();
conn = nullptr;
}
else
LogPrint (eLogInfo, "NTCP2: Incoming session from ", ep.address (), " is already pending");
}
} }
else
LogPrint (eLogError, "NTCP2: Incoming connection from invalid IP ", ep.address ());
} }
else else
LogPrint (eLogError, "NTCP2: Connected from error ", ec.message ()); LogPrint (eLogError, "NTCP2: Connected from error ", ec.message ());
@@ -1476,7 +1520,10 @@ namespace transport
if (error != boost::asio::error::operation_aborted) if (error != boost::asio::error::operation_aborted)
{ {
conn = std::make_shared<NTCP2Session> (*this); if (!conn) // connection is used, create new one
conn = std::make_shared<NTCP2Session> (*this);
else // reuse failed
conn->Close ();
m_NTCP2V6Acceptor->async_accept(conn->GetSocket (), std::bind (&NTCP2Server::HandleAcceptV6, this, m_NTCP2V6Acceptor->async_accept(conn->GetSocket (), std::bind (&NTCP2Server::HandleAcceptV6, this,
conn, std::placeholders::_1)); conn, std::placeholders::_1));
} }
@@ -1507,12 +1554,12 @@ namespace transport
// pending // pending
for (auto it = m_PendingIncomingSessions.begin (); it != m_PendingIncomingSessions.end ();) for (auto it = m_PendingIncomingSessions.begin (); it != m_PendingIncomingSessions.end ();)
{ {
if ((*it)->IsEstablished () || (*it)->IsTerminationTimeoutExpired (ts)) if (it->second->IsEstablished () || it->second->IsTerminationTimeoutExpired (ts))
{ {
(*it)->Terminate (); it->second->Terminate ();
it = m_PendingIncomingSessions.erase (it); // established of expired it = m_PendingIncomingSessions.erase (it); // established of expired
} }
else if ((*it)->IsTerminated ()) else if (it->second->IsTerminated ())
it = m_PendingIncomingSessions.erase (it); // already terminated it = m_PendingIncomingSessions.erase (it); // already terminated
else else
it++; it++;
@@ -1743,7 +1790,7 @@ namespace transport
boost::asio::async_read(conn->GetSocket(), boost::asio::buffer(readbuff->data (), SOCKS5_UDP_IPV4_REQUEST_HEADER_SIZE), // read min reply size boost::asio::async_read(conn->GetSocket(), boost::asio::buffer(readbuff->data (), SOCKS5_UDP_IPV4_REQUEST_HEADER_SIZE), // read min reply size
boost::asio::transfer_all(), boost::asio::transfer_all(),
[timer, conn, sz, readbuff](const boost::system::error_code & e, std::size_t transferred) [timer, conn, readbuff](const boost::system::error_code & e, std::size_t transferred)
{ {
if (e) if (e)
LogPrint(eLogError, "NTCP2: SOCKS proxy read error ", e.message()); LogPrint(eLogError, "NTCP2: SOCKS proxy read error ", e.message());

View File

@@ -134,22 +134,22 @@ namespace transport
~NTCP2Session (); ~NTCP2Session ();
void Terminate (); void Terminate ();
void TerminateByTimeout (); void TerminateByTimeout ();
void Done (); void Done () override;
void Close () { m_Socket.close (); }; // for accept void Close (); // for accept
void DeleteNextReceiveBuffer (uint64_t ts); void DeleteNextReceiveBuffer (uint64_t ts);
boost::asio::ip::tcp::socket& GetSocket () { return m_Socket; }; boost::asio::ip::tcp::socket& GetSocket () { return m_Socket; };
const boost::asio::ip::tcp::endpoint& GetRemoteEndpoint () { return m_RemoteEndpoint; }; const boost::asio::ip::tcp::endpoint& GetRemoteEndpoint () { return m_RemoteEndpoint; };
void SetRemoteEndpoint (const boost::asio::ip::tcp::endpoint& ep) { m_RemoteEndpoint = ep; }; void SetRemoteEndpoint (const boost::asio::ip::tcp::endpoint& ep) { m_RemoteEndpoint = ep; };
bool IsEstablished () const { return m_IsEstablished; }; bool IsEstablished () const override { return m_IsEstablished; };
bool IsTerminated () const { return m_IsTerminated; }; bool IsTerminated () const { return m_IsTerminated; };
void ClientLogin (); // Alice void ClientLogin (); // Alice
void ServerLogin (); // Bob void ServerLogin (); // Bob
void SendLocalRouterInfo (bool update); // after handshake or by update void SendLocalRouterInfo (bool update) override; // after handshake or by update
void SendI2NPMessages (const std::vector<std::shared_ptr<I2NPMessage> >& msgs); void SendI2NPMessages (const std::vector<std::shared_ptr<I2NPMessage> >& msgs) override;
private: private:
@@ -277,7 +277,7 @@ namespace transport
boost::asio::deadline_timer m_TerminationTimer; boost::asio::deadline_timer m_TerminationTimer;
std::unique_ptr<boost::asio::ip::tcp::acceptor> m_NTCP2Acceptor, m_NTCP2V6Acceptor; std::unique_ptr<boost::asio::ip::tcp::acceptor> m_NTCP2Acceptor, m_NTCP2V6Acceptor;
std::map<i2p::data::IdentHash, std::shared_ptr<NTCP2Session> > m_NTCP2Sessions; std::map<i2p::data::IdentHash, std::shared_ptr<NTCP2Session> > m_NTCP2Sessions;
std::list<std::shared_ptr<NTCP2Session> > m_PendingIncomingSessions; std::map<boost::asio::ip::address, std::shared_ptr<NTCP2Session> > m_PendingIncomingSessions;
ProxyType m_ProxyType; ProxyType m_ProxyType;
std::string m_ProxyAddress, m_ProxyAuthorization; std::string m_ProxyAddress, m_ProxyAuthorization;

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2022, The PurpleI2P Project * Copyright (c) 2013-2023, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@@ -295,7 +295,8 @@ namespace data
else else
{ {
r = std::make_shared<RouterInfo> (buf, len); r = std::make_shared<RouterInfo> (buf, len);
if (!r->IsUnreachable () && r->HasValidAddresses ()) if (!r->IsUnreachable () && r->HasValidAddresses () &&
i2p::util::GetMillisecondsSinceEpoch () + NETDB_EXPIRATION_TIMEOUT_THRESHOLD*1000LL > r->GetTimestamp ())
{ {
bool inserted = false; bool inserted = false;
{ {
@@ -365,15 +366,16 @@ namespace data
bool NetDb::AddLeaseSet2 (const IdentHash& ident, const uint8_t * buf, int len, uint8_t storeType) bool NetDb::AddLeaseSet2 (const IdentHash& ident, const uint8_t * buf, int len, uint8_t storeType)
{ {
std::unique_lock<std::mutex> lock(m_LeaseSetsMutex);
auto leaseSet = std::make_shared<LeaseSet2> (storeType, buf, len, false); // we don't need leases in netdb auto leaseSet = std::make_shared<LeaseSet2> (storeType, buf, len, false); // we don't need leases in netdb
if (leaseSet->IsValid ()) if (leaseSet->IsValid ())
{ {
std::unique_lock<std::mutex> lock(m_LeaseSetsMutex);
auto it = m_LeaseSets.find(ident); auto it = m_LeaseSets.find(ident);
if (it == m_LeaseSets.end () || it->second->GetStoreType () != storeType || if (it == m_LeaseSets.end () || it->second->GetStoreType () != storeType ||
leaseSet->GetPublishedTimestamp () > it->second->GetPublishedTimestamp ()) leaseSet->GetPublishedTimestamp () > it->second->GetPublishedTimestamp ())
{ {
if (leaseSet->IsPublic () && !leaseSet->IsExpired ()) if (leaseSet->IsPublic () && !leaseSet->IsExpired () &&
i2p::util::GetSecondsSinceEpoch () + NETDB_EXPIRATION_TIMEOUT_THRESHOLD > leaseSet->GetPublishedTimestamp ())
{ {
// TODO: implement actual update // TODO: implement actual update
LogPrint (eLogInfo, "NetDb: LeaseSet2 updated: ", ident.ToBase32()); LogPrint (eLogInfo, "NetDb: LeaseSet2 updated: ", ident.ToBase32());
@@ -382,7 +384,7 @@ namespace data
} }
else else
{ {
LogPrint (eLogWarning, "NetDb: Unpublished or expired LeaseSet2 received: ", ident.ToBase32()); LogPrint (eLogWarning, "NetDb: Unpublished or expired or future LeaseSet2 received: ", ident.ToBase32());
m_LeaseSets.erase (ident); m_LeaseSets.erase (ident);
} }
} }
@@ -494,7 +496,7 @@ namespace data
{ {
auto r = std::make_shared<RouterInfo>(path); auto r = std::make_shared<RouterInfo>(path);
if (r->GetRouterIdentity () && !r->IsUnreachable () && r->HasValidAddresses () && if (r->GetRouterIdentity () && !r->IsUnreachable () && r->HasValidAddresses () &&
ts < r->GetTimestamp () + 24*60*60*NETDB_MAX_OFFLINE_EXPIRATION_TIMEOUT*1000LL) ts < r->GetTimestamp () + 24*60*60*NETDB_MAX_OFFLINE_EXPIRATION_TIMEOUT*1000LL) // too old
{ {
r->DeleteBuffer (); r->DeleteBuffer ();
if (m_RouterInfos.emplace (r->GetIdentHash (), r).second) if (m_RouterInfos.emplace (r->GetIdentHash (), r).second)
@@ -637,7 +639,12 @@ namespace data
it.second->SetUnreachable (true); it.second->SetUnreachable (true);
} }
else if (checkForExpiration && ts > it.second->GetTimestamp () + expirationTimeout) else if (checkForExpiration && ts > it.second->GetTimestamp () + expirationTimeout)
it.second->SetUnreachable (true); it.second->SetUnreachable (true);
else if (ts + NETDB_EXPIRATION_TIMEOUT_THRESHOLD*1000LL < it.second->GetTimestamp ())
{
LogPrint (eLogWarning, "NetDb: RouterInfo is from future for ", (it.second->GetTimestamp () - ts)/1000LL, " seconds");
it.second->SetUnreachable (true);
}
if (it.second->IsUnreachable ()) if (it.second->IsUnreachable ())
{ {
@@ -800,6 +807,11 @@ namespace data
uint8_t storeType = buf[DATABASE_STORE_TYPE_OFFSET]; uint8_t storeType = buf[DATABASE_STORE_TYPE_OFFSET];
if (storeType) // LeaseSet or LeaseSet2 if (storeType) // LeaseSet or LeaseSet2
{ {
if (len > MAX_LS_BUFFER_SIZE + offset)
{
LogPrint (eLogError, "NetDb: Database store message is too long ", len);
return;
}
if (!m->from) // unsolicited LS must be received directly if (!m->from) // unsolicited LS must be received directly
{ {
if (storeType == NETDB_STORE_TYPE_LEASESET) // 1 if (storeType == NETDB_STORE_TYPE_LEASESET) // 1

View File

@@ -44,6 +44,7 @@ namespace data
const int NETDB_MIN_EXPIRATION_TIMEOUT = 90 * 60; // 1.5 hours const int NETDB_MIN_EXPIRATION_TIMEOUT = 90 * 60; // 1.5 hours
const int NETDB_MAX_EXPIRATION_TIMEOUT = 27 * 60 * 60; // 27 hours const int NETDB_MAX_EXPIRATION_TIMEOUT = 27 * 60 * 60; // 27 hours
const int NETDB_MAX_OFFLINE_EXPIRATION_TIMEOUT = 180; // in days const int NETDB_MAX_OFFLINE_EXPIRATION_TIMEOUT = 180; // in days
const int NETDB_EXPIRATION_TIMEOUT_THRESHOLD = 2*60; // 2 minutes
const int NETDB_PUBLISH_INTERVAL = 60 * 40; const int NETDB_PUBLISH_INTERVAL = 60 * 40;
const int NETDB_PUBLISH_CONFIRMATION_TIMEOUT = 5; // in seconds const int NETDB_PUBLISH_CONFIRMATION_TIMEOUT = 5; // in seconds
const int NETDB_MAX_PUBLISH_EXCLUDED_FLOODFILLS = 15; const int NETDB_MAX_PUBLISH_EXCLUDED_FLOODFILLS = 15;

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2020, The PurpleI2P Project * Copyright (c) 2013-2023, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@@ -12,6 +12,7 @@
#include "Base.h" #include "Base.h"
#include "FS.h" #include "FS.h"
#include "Log.h" #include "Log.h"
#include "Timestamp.h"
#include "Profiling.h" #include "Profiling.h"
namespace i2p namespace i2p
@@ -22,6 +23,7 @@ namespace data
RouterProfile::RouterProfile (): RouterProfile::RouterProfile ():
m_LastUpdateTime (boost::posix_time::second_clock::local_time()), m_LastUpdateTime (boost::posix_time::second_clock::local_time()),
m_LastDeclineTime (0),
m_NumTunnelsAgreed (0), m_NumTunnelsDeclined (0), m_NumTunnelsNonReplied (0), m_NumTunnelsAgreed (0), m_NumTunnelsDeclined (0), m_NumTunnelsNonReplied (0),
m_NumTimesTaken (0), m_NumTimesRejected (0) m_NumTimesTaken (0), m_NumTimesRejected (0)
{ {
@@ -131,15 +133,23 @@ namespace data
{ {
UpdateTime (); UpdateTime ();
if (ret > 0) if (ret > 0)
{
m_NumTunnelsDeclined++; m_NumTunnelsDeclined++;
m_LastDeclineTime = i2p::util::GetSecondsSinceEpoch ();
}
else else
{
m_NumTunnelsAgreed++; m_NumTunnelsAgreed++;
m_LastDeclineTime = 0;
}
} }
void RouterProfile::TunnelNonReplied () void RouterProfile::TunnelNonReplied ()
{ {
m_NumTunnelsNonReplied++; m_NumTunnelsNonReplied++;
UpdateTime (); UpdateTime ();
if (m_NumTunnelsNonReplied > 2*m_NumTunnelsAgreed && m_NumTunnelsNonReplied > 3)
m_LastDeclineTime = i2p::util::GetSecondsSinceEpoch ();
} }
bool RouterProfile::IsLowPartcipationRate () const bool RouterProfile::IsLowPartcipationRate () const
@@ -153,8 +163,18 @@ namespace data
return m_NumTunnelsNonReplied > 10*(total + 1); return m_NumTunnelsNonReplied > 10*(total + 1);
} }
bool RouterProfile::IsDeclinedRecently ()
{
if (!m_LastDeclineTime) return false;
auto ts = i2p::util::GetSecondsSinceEpoch ();
if (ts > m_LastDeclineTime + PEER_PROFILE_DECLINED_RECENTLY_INTERVAL)
m_LastDeclineTime = 0;
return m_LastDeclineTime;
}
bool RouterProfile::IsBad () bool RouterProfile::IsBad ()
{ {
if (IsDeclinedRecently ()) return true;
auto isBad = IsAlwaysDeclining () || IsLowPartcipationRate () /*|| IsLowReplyRate ()*/; auto isBad = IsAlwaysDeclining () || IsLowPartcipationRate () /*|| IsLowReplyRate ()*/;
if (isBad && m_NumTimesRejected > 10*(m_NumTimesTaken + 1)) if (isBad && m_NumTimesRejected > 10*(m_NumTimesTaken + 1))
{ {

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2022, The PurpleI2P Project * Copyright (c) 2013-2023, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@@ -31,6 +31,7 @@ namespace data
const int PEER_PROFILE_EXPIRATION_TIMEOUT = 72; // in hours (3 days) const int PEER_PROFILE_EXPIRATION_TIMEOUT = 72; // in hours (3 days)
const int PEER_PROFILE_AUTOCLEAN_TIMEOUT = 24 * 3600; // in seconds (1 day) const int PEER_PROFILE_AUTOCLEAN_TIMEOUT = 24 * 3600; // in seconds (1 day)
const int PEER_PROFILE_AUTOCLEAN_VARIANCE = 3 * 3600; // in seconds (3 hours) const int PEER_PROFILE_AUTOCLEAN_VARIANCE = 3 * 3600; // in seconds (3 hours)
const int PEER_PROFILE_DECLINED_RECENTLY_INTERVAL = 150; // in seconds (2.5 minutes)
class RouterProfile class RouterProfile
{ {
@@ -55,10 +56,12 @@ namespace data
bool IsAlwaysDeclining () const { return !m_NumTunnelsAgreed && m_NumTunnelsDeclined >= 5; }; bool IsAlwaysDeclining () const { return !m_NumTunnelsAgreed && m_NumTunnelsDeclined >= 5; };
bool IsLowPartcipationRate () const; bool IsLowPartcipationRate () const;
bool IsLowReplyRate () const; bool IsLowReplyRate () const;
bool IsDeclinedRecently ();
private: private:
boost::posix_time::ptime m_LastUpdateTime; boost::posix_time::ptime m_LastUpdateTime; // TODO: use std::chrono
uint64_t m_LastDeclineTime; // in seconds
// participation // participation
uint32_t m_NumTunnelsAgreed; uint32_t m_NumTunnelsAgreed;
uint32_t m_NumTunnelsDeclined; uint32_t m_NumTunnelsDeclined;

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2022, The PurpleI2P Project * Copyright (c) 2013-2023, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@@ -125,7 +125,7 @@ namespace i2p
} }
if (ipv6) if (ipv6)
{ {
std::string host = "::1"; std::string host;
if (!i2p::config::IsDefault("host") && !ipv4) // override if v6 only if (!i2p::config::IsDefault("host") && !ipv4) // override if v6 only
i2p::config::GetOption("host", host); i2p::config::GetOption("host", host);
else else
@@ -136,6 +136,7 @@ namespace i2p
if (ntcp2) if (ntcp2)
{ {
bool added = false;
if (ntcp2Published) if (ntcp2Published)
{ {
std::string ntcp2Host; std::string ntcp2Host;
@@ -143,9 +144,13 @@ namespace i2p
i2p::config::GetOption ("ntcp2.addressv6", ntcp2Host); i2p::config::GetOption ("ntcp2.addressv6", ntcp2Host);
else else
ntcp2Host = host; ntcp2Host = host;
routerInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv, boost::asio::ip::address_v6::from_string (ntcp2Host), port); if (!ntcp2Host.empty () && port)
{
routerInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv, boost::asio::ip::address_v6::from_string (ntcp2Host), port);
added = true;
}
} }
else if (!added)
{ {
if (!ipv4) // no other ntcp2 addresses yet if (!ipv4) // no other ntcp2 addresses yet
routerInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv); routerInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv);
@@ -154,13 +159,18 @@ namespace i2p
} }
if (ssu2) if (ssu2)
{ {
bool added = false;
if (ssu2Published) if (ssu2Published)
{ {
uint16_t ssu2Port; i2p::config::GetOption ("ssu2.port", ssu2Port); uint16_t ssu2Port; i2p::config::GetOption ("ssu2.port", ssu2Port);
if (!ssu2Port) ssu2Port = port; if (!ssu2Port) ssu2Port = port;
routerInfo.AddSSU2Address (m_SSU2Keys->staticPublicKey, m_SSU2Keys->intro, boost::asio::ip::address_v6::from_string (host), ssu2Port); if (!host.empty () && ssu2Port)
{
routerInfo.AddSSU2Address (m_SSU2Keys->staticPublicKey, m_SSU2Keys->intro, boost::asio::ip::address_v6::from_string (host), ssu2Port);
added = true;
}
} }
else if (!added)
{ {
if (!ipv4) // no other ssu2 addresses yet if (!ipv4) // no other ssu2 addresses yet
routerInfo.AddSSU2Address (m_SSU2Keys->staticPublicKey, m_SSU2Keys->intro); routerInfo.AddSSU2Address (m_SSU2Keys->staticPublicKey, m_SSU2Keys->intro);
@@ -230,7 +240,6 @@ namespace i2p
if (status != m_Status) if (status != m_Status)
{ {
m_Status = status; m_Status = status;
m_Error = eRouterErrorNone;
switch (m_Status) switch (m_Status)
{ {
case eRouterStatusOK: case eRouterStatusOK:
@@ -239,6 +248,9 @@ namespace i2p
case eRouterStatusFirewalled: case eRouterStatusFirewalled:
SetUnreachable (true, false); // ipv4 SetUnreachable (true, false); // ipv4
break; break;
case eRouterStatusTesting:
m_Error = eRouterErrorNone;
break;
default: default:
; ;
} }
@@ -250,7 +262,6 @@ namespace i2p
if (status != m_StatusV6) if (status != m_StatusV6)
{ {
m_StatusV6 = status; m_StatusV6 = status;
m_ErrorV6 = eRouterErrorNone;
switch (m_StatusV6) switch (m_StatusV6)
{ {
case eRouterStatusOK: case eRouterStatusOK:
@@ -259,6 +270,9 @@ namespace i2p
case eRouterStatusFirewalled: case eRouterStatusFirewalled:
SetUnreachable (false, true); // ipv6 SetUnreachable (false, true); // ipv6
break; break;
case eRouterStatusTesting:
m_ErrorV6 = eRouterErrorNone;
break;
default: default:
; ;
} }
@@ -272,7 +286,7 @@ namespace i2p
bool updated = false; bool updated = false;
for (auto& address : *addresses) for (auto& address : *addresses)
{ {
if (address->port != port && address->transportStyle == i2p::data::RouterInfo::eTransportSSU2) if (address && address->port != port && address->transportStyle == i2p::data::RouterInfo::eTransportSSU2)
{ {
address->port = port; address->port = port;
updated = true; updated = true;
@@ -290,7 +304,7 @@ namespace i2p
bool updated = false; bool updated = false;
for (auto& address : *addresses) for (auto& address : *addresses)
{ {
if (address->IsNTCP2 () && (address->port != port || address->published != publish)) if (address && address->IsNTCP2 () && (address->port != port || address->published != publish))
{ {
bool isAddr = v4 && address->IsV4 (); bool isAddr = v4 && address->IsV4 ();
if (!isAddr && (v6 || ygg)) if (!isAddr && (v6 || ygg))
@@ -319,23 +333,20 @@ namespace i2p
auto addresses = m_RouterInfo.GetAddresses (); auto addresses = m_RouterInfo.GetAddresses ();
if (!addresses) return; if (!addresses) return;
bool found = false, updated = false; bool found = false, updated = false;
for (auto it = addresses->begin (); it != addresses->end ();) for (auto& it: *addresses)
{ {
if ((*it)->IsNTCP2 ()) if (it && it->IsNTCP2 ())
{ {
found = true; found = true;
if (enable) if (enable)
{ {
(*it)->s = m_NTCP2Keys->staticPublicKey; it->s = m_NTCP2Keys->staticPublicKey;
memcpy ((*it)->i, m_NTCP2Keys->iv, 16); memcpy (it->i, m_NTCP2Keys->iv, 16);
it++;
} }
else else
it = addresses->erase (it); it.reset ();
updated = true; updated = true;
} }
else
it++;
} }
if (enable && !found) if (enable && !found)
{ {
@@ -355,7 +366,7 @@ namespace i2p
if (!port) if (!port)
{ {
for (const auto& address : *addresses) for (const auto& address : *addresses)
if (address->port) if (address && address->port)
{ {
newPort = address->port; newPort = address->port;
break; break;
@@ -365,7 +376,7 @@ namespace i2p
bool updated = false; bool updated = false;
for (auto& address : *addresses) for (auto& address : *addresses)
{ {
if (address->IsSSU2 () && (!address->port || address->port != port || address->published != publish) && if (address && address->IsSSU2 () && (!address->port || address->port != port || address->published != publish) &&
((v4 && address->IsV4 ()) || (v6 && address->IsV6 ()))) ((v4 && address->IsV4 ()) || (v6 && address->IsV6 ())))
{ {
if (port) address->port = port; if (port) address->port = port;
@@ -387,23 +398,20 @@ namespace i2p
auto addresses = m_RouterInfo.GetAddresses (); auto addresses = m_RouterInfo.GetAddresses ();
if (!addresses) return; if (!addresses) return;
bool found = false, updated = false; bool found = false, updated = false;
for (auto it = addresses->begin (); it != addresses->end ();) for (auto& it : *addresses)
{ {
if ((*it)->IsSSU2 ()) if (it && it->IsSSU2 ())
{ {
found = true; found = true;
if (enable) if (enable)
{ {
(*it)->s = m_SSU2Keys->staticPublicKey; it->s = m_SSU2Keys->staticPublicKey;
(*it)->i = m_SSU2Keys->intro; it->i = m_SSU2Keys->intro;
it++;
} }
else else
it = addresses->erase (it); it.reset ();
updated = true; updated = true;
} }
else
it++;
} }
if (enable && !found) if (enable && !found)
{ {
@@ -433,34 +441,53 @@ namespace i2p
auto addresses = m_RouterInfo.GetAddresses (); auto addresses = m_RouterInfo.GetAddresses ();
if (!addresses) return; if (!addresses) return;
bool updated = false; bool updated = false;
for (auto& address : *addresses) if (host.is_v4 ())
{ {
if (address->host != host && address->IsCompatible (host) && auto addr = (*addresses)[i2p::data::RouterInfo::eNTCP2V4Idx];
!i2p::util::net::IsYggdrasilAddress (address->host)) if (addr && addr->host != host)
{ {
// update host addr->host = host;
address->host = host;
updated = true; updated = true;
} }
if (host.is_v6 () && address->IsV6 () && address->ssu && addr = (*addresses)[i2p::data::RouterInfo::eSSU2V4Idx];
(!address->ssu->mtu || updated) && m_StatusV6 != eRouterStatusProxy) if (addr && addr->host != host)
{ {
// update MTU addr->host = host;
auto mtu = i2p::util::net::GetMTU (host); updated = true;
if (mtu)
{
LogPrint (eLogDebug, "Router: Our v6 MTU=", mtu);
int maxMTU = i2p::util::net::GetMaxMTU (host.to_v6 ());
if (mtu > maxMTU)
{
mtu = maxMTU;
LogPrint(eLogWarning, "Router: MTU dropped to upper limit of ", maxMTU, " bytes");
}
address->ssu->mtu = mtu;
updated = true;
}
} }
} }
else if (host.is_v6 ())
{
auto addr = (*addresses)[i2p::data::RouterInfo::eNTCP2V6Idx];
if (addr && addr->host != host)
{
addr->host = host;
updated = true;
}
addr = (*addresses)[i2p::data::RouterInfo::eSSU2V6Idx];
if (addr && (addr->host != host || !addr->ssu->mtu))
{
addr->host = host;
if (m_StatusV6 != eRouterStatusProxy)
{
// update MTU
auto mtu = i2p::util::net::GetMTU (host);
if (mtu)
{
LogPrint (eLogDebug, "Router: Our v6 MTU=", mtu);
int maxMTU = i2p::util::net::GetMaxMTU (host.to_v6 ());
if (mtu > maxMTU)
{
mtu = maxMTU;
LogPrint(eLogWarning, "Router: MTU dropped to upper limit of ", maxMTU, " bytes");
}
addr->ssu->mtu = mtu;
}
}
updated = true;
}
}
auto ts = i2p::util::GetSecondsSinceEpoch (); auto ts = i2p::util::GetSecondsSinceEpoch ();
if (updated || ts > m_LastUpdateTime + ROUTER_INFO_UPDATE_INTERVAL) if (updated || ts > m_LastUpdateTime + ROUTER_INFO_UPDATE_INTERVAL)
UpdateRouterInfo (); UpdateRouterInfo ();
@@ -482,18 +509,12 @@ namespace i2p
void RouterContext::ClearSSU2Introducers (bool v4) void RouterContext::ClearSSU2Introducers (bool v4)
{ {
auto addresses = m_RouterInfo.GetAddresses (); auto addr = m_RouterInfo.GetSSU2Address (v4);
if (!addresses) return; if (addr && !addr->ssu->introducers.empty ())
bool updated = false; {
for (auto& addr : *addresses) addr->ssu->introducers.clear ();
if (addr->IsSSU2 () && ((v4 && addr->IsV4 ()) || (!v4 && addr->IsV6 ())) &&
addr->ssu && !addr->ssu->introducers.empty ())
{
addr->ssu->introducers.clear ();
updated = true;
}
if (updated)
UpdateRouterInfo (); UpdateRouterInfo ();
}
} }
void RouterContext::SetFloodfill (bool floodfill) void RouterContext::SetFloodfill (bool floodfill)
@@ -612,7 +633,7 @@ namespace i2p
if (addresses) if (addresses)
{ {
for (auto& addr : *addresses) for (auto& addr : *addresses)
if (addr->ssu && ((v4 && addr->IsV4 ()) || (v6 && addr->IsV6 ()))) if (addr && addr->ssu && ((v4 && addr->IsV4 ()) || (v6 && addr->IsV6 ())))
{ {
addr->published = false; addr->published = false;
addr->caps &= ~i2p::data::RouterInfo::eSSUIntroducer; // can't be introducer addr->caps &= ~i2p::data::RouterInfo::eSSUIntroducer; // can't be introducer
@@ -648,7 +669,7 @@ namespace i2p
if (addresses) if (addresses)
{ {
for (auto& addr : *addresses) for (auto& addr : *addresses)
if (addr->ssu && isSSU2Published && ((v4 && addr->IsV4 ()) || (v6 && addr->IsV6 ()))) if (addr && addr->ssu && isSSU2Published && ((v4 && addr->IsV4 ()) || (v6 && addr->IsV6 ())))
{ {
addr->published = true; addr->published = true;
addr->caps |= i2p::data::RouterInfo::eSSUIntroducer; addr->caps |= i2p::data::RouterInfo::eSSUIntroducer;
@@ -685,7 +706,7 @@ namespace i2p
{ {
for (auto& addr: *addresses) for (auto& addr: *addresses)
{ {
if (addr->IsV6 () && !i2p::util::net::IsYggdrasilAddress (addr->host)) if (addr && addr->IsV6 () && !i2p::util::net::IsYggdrasilAddress (addr->host))
{ {
switch (addr->transportStyle) switch (addr->transportStyle)
{ {
@@ -698,7 +719,7 @@ namespace i2p
default: ; default: ;
} }
} }
port = addr->port; if (addr) port = addr->port;
} }
} }
if (!port) if (!port)
@@ -768,7 +789,7 @@ namespace i2p
{ {
for (auto& addr: *addresses) for (auto& addr: *addresses)
{ {
if (addr->IsV4 ()) if (addr && addr->IsV4 ())
{ {
switch (addr->transportStyle) switch (addr->transportStyle)
{ {
@@ -781,7 +802,7 @@ namespace i2p
default: ; default: ;
} }
} }
if (addr->port) port = addr->port; if (addr && addr->port) port = addr->port;
} }
} }
if (!port) if (!port)
@@ -834,26 +855,26 @@ namespace i2p
{ {
if (supportsmesh) if (supportsmesh)
{ {
auto addresses = m_RouterInfo.GetAddresses ();
if (!addresses) return;
m_RouterInfo.EnableMesh (); m_RouterInfo.EnableMesh ();
if ((*addresses)[i2p::data::RouterInfo::eNTCP2V6MeshIdx]) return; // we have mesh address already
uint16_t port = 0; uint16_t port = 0;
i2p::config::GetOption ("ntcp2.port", port); i2p::config::GetOption ("ntcp2.port", port);
if (!port) i2p::config::GetOption("port", port); if (!port) i2p::config::GetOption("port", port);
bool foundMesh = false; if (!port)
auto addresses = m_RouterInfo.GetAddresses ();
if (addresses)
{ {
for (auto& addr: *addresses) for (auto& addr: *addresses)
{ {
if (!port) port = addr->port; if (addr && addr->port)
if (i2p::util::net::IsYggdrasilAddress (addr->host))
{ {
foundMesh = true; port = addr->port;
break; break;
} }
} }
} }
if (!foundMesh) if (!port) port = SelectRandomPort ();
m_RouterInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv, host, port); m_RouterInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv, host, port);
} }
else else
m_RouterInfo.DisableMesh (); m_RouterInfo.DisableMesh ();
@@ -867,7 +888,7 @@ namespace i2p
if (!addresses) return; if (!addresses) return;
for (auto& addr: *addresses) for (auto& addr: *addresses)
{ {
if (addr->ssu && ((v4 && addr->IsV4 ()) || (!v4 && addr->IsV6 ()))) if (addr && addr->ssu && ((v4 && addr->IsV4 ()) || (!v4 && addr->IsV6 ())))
{ {
addr->ssu->mtu = mtu; addr->ssu->mtu = mtu;
LogPrint (eLogDebug, "Router: MTU for ", v4 ? "ipv4" : "ipv6", " address ", addr->host.to_string(), " is set to ", mtu); LogPrint (eLogDebug, "Router: MTU for ", v4 ? "ipv4" : "ipv6", " address ", addr->host.to_string(), " is set to ", mtu);
@@ -883,7 +904,7 @@ namespace i2p
if (!addresses) return; if (!addresses) return;
for (auto& addr: *addresses) for (auto& addr: *addresses)
{ {
if (addr->IsPublishedNTCP2 ()) if (addr && addr->IsPublishedNTCP2 ())
{ {
bool isYgg1 = i2p::util::net::IsYggdrasilAddress (addr->host); bool isYgg1 = i2p::util::net::IsYggdrasilAddress (addr->host);
if (addr->IsV6 () && ((isYgg && isYgg1) || (!isYgg && !isYgg1))) if (addr->IsV6 () && ((isYgg && isYgg1) || (!isYgg && !isYgg1)))

View File

@@ -48,7 +48,8 @@ namespace garlic
eRouterErrorClockSkew = 1, eRouterErrorClockSkew = 1,
eRouterErrorOffline = 2, eRouterErrorOffline = 2,
eRouterErrorSymmetricNAT = 3, eRouterErrorSymmetricNAT = 3,
eRouterErrorNoDescriptors = 4 eRouterErrorFullConeNAT = 4,
eRouterErrorNoDescriptors = 5
}; };
class RouterContext: public i2p::garlic::GarlicDestination class RouterContext: public i2p::garlic::GarlicDestination

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2022, The PurpleI2P Project * Copyright (c) 2013-2023, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@@ -206,14 +206,13 @@ namespace data
s.read ((char *)&m_Timestamp, sizeof (m_Timestamp)); s.read ((char *)&m_Timestamp, sizeof (m_Timestamp));
m_Timestamp = be64toh (m_Timestamp); m_Timestamp = be64toh (m_Timestamp);
// read addresses // read addresses
auto addresses = netdb.NewRouterInfoAddresses (); auto addresses = NewAddresses ();
uint8_t numAddresses; uint8_t numAddresses;
s.read ((char *)&numAddresses, sizeof (numAddresses)); s.read ((char *)&numAddresses, sizeof (numAddresses));
addresses->reserve (numAddresses);
for (int i = 0; i < numAddresses; i++) for (int i = 0; i < numAddresses; i++)
{ {
uint8_t supportedTransports = 0; uint8_t supportedTransports = 0;
auto address = netdb.NewRouterInfoAddress (); auto address = NewAddress ();
uint8_t cost; // ignore uint8_t cost; // ignore
s.read ((char *)&cost, sizeof (cost)); s.read ((char *)&cost, sizeof (cost));
s.read ((char *)&address->date, sizeof (address->date)); s.read ((char *)&address->date, sizeof (address->date));
@@ -418,7 +417,11 @@ namespace data
if (supportedTransports) if (supportedTransports)
{ {
if (!(m_SupportedTransports & supportedTransports)) // avoid duplicates if (!(m_SupportedTransports & supportedTransports)) // avoid duplicates
addresses->push_back(address); {
for (uint8_t i = 0; i < eNumTransports; i++)
if ((1 << i) & supportedTransports)
(*addresses)[i] = address;
}
m_SupportedTransports |= supportedTransports; m_SupportedTransports |= supportedTransports;
} }
} }
@@ -633,13 +636,23 @@ namespace data
{ {
m_SupportedTransports |= eNTCP2V4; m_SupportedTransports |= eNTCP2V4;
if (addr->published) m_ReachableTransports |= eNTCP2V4; if (addr->published) m_ReachableTransports |= eNTCP2V4;
(*m_Addresses)[eNTCP2V4Idx] = addr;
} }
if (addr->IsV6 ()) if (addr->IsV6 ())
{ {
m_SupportedTransports |= eNTCP2V6; if (i2p::util::net::IsYggdrasilAddress (addr->host))
if (addr->published) m_ReachableTransports |= eNTCP2V6; {
m_SupportedTransports |= eNTCP2V6Mesh;
m_ReachableTransports |= eNTCP2V6Mesh;
(*m_Addresses)[eNTCP2V6MeshIdx] = addr;
}
else
{
m_SupportedTransports |= eNTCP2V6;
if (addr->published) m_ReachableTransports |= eNTCP2V6;
(*m_Addresses)[eNTCP2V6Idx] = addr;
}
} }
m_Addresses->push_back(std::move(addr));
} }
void RouterInfo::AddSSU2Address (const uint8_t * staticKey, const uint8_t * introKey, uint8_t caps) void RouterInfo::AddSSU2Address (const uint8_t * staticKey, const uint8_t * introKey, uint8_t caps)
@@ -653,9 +666,16 @@ namespace data
addr->ssu->mtu = 0; addr->ssu->mtu = 0;
memcpy (addr->s, staticKey, 32); memcpy (addr->s, staticKey, 32);
memcpy (addr->i, introKey, 32); memcpy (addr->i, introKey, 32);
if (addr->IsV4 ()) m_SupportedTransports |= eSSU2V4; if (addr->IsV4 ())
if (addr->IsV6 ()) m_SupportedTransports |= eSSU2V6; {
m_Addresses->push_back(std::move(addr)); m_SupportedTransports |= eSSU2V4;
(*m_Addresses)[eSSU2V4Idx] = addr;
}
if (addr->IsV6 ())
{
m_SupportedTransports |= eSSU2V6;
(*m_Addresses)[eSSU2V6Idx] = addr;
}
} }
void RouterInfo::AddSSU2Address (const uint8_t * staticKey, const uint8_t * introKey, void RouterInfo::AddSSU2Address (const uint8_t * staticKey, const uint8_t * introKey,
@@ -676,13 +696,14 @@ namespace data
{ {
m_SupportedTransports |= eSSU2V4; m_SupportedTransports |= eSSU2V4;
m_ReachableTransports |= eSSU2V4; m_ReachableTransports |= eSSU2V4;
(*m_Addresses)[eSSU2V4Idx] = addr;
} }
if (addr->IsV6 ()) if (addr->IsV6 ())
{ {
m_SupportedTransports |= eSSU2V6; m_SupportedTransports |= eSSU2V6;
m_ReachableTransports |= eSSU2V6; m_ReachableTransports |= eSSU2V6;
(*m_Addresses)[eSSU2V6Idx] = addr;
} }
m_Addresses->push_back(std::move(addr));
} }
bool RouterInfo::IsNTCP2 (bool v4only) const bool RouterInfo::IsNTCP2 (bool v4only) const
@@ -721,21 +742,17 @@ namespace data
{ {
if (IsV6 ()) if (IsV6 ())
{ {
for (auto it = m_Addresses->begin (); it != m_Addresses->end ();) if ((*m_Addresses)[eNTCP2V6Idx])
{ {
auto addr = *it; if ((*m_Addresses)[eNTCP2V6Idx]->IsV4 ())
if (addr->IsV6 ()) (*m_Addresses)[eNTCP2V6Idx]->caps &= ~AddressCaps::eV6;
{ (*m_Addresses)[eNTCP2V6Idx].reset ();
if (addr->IsV4 ()) }
{ if ((*m_Addresses)[eSSU2V6Idx])
addr->caps &= ~AddressCaps::eV6; {
++it; if ((*m_Addresses)[eSSU2V6Idx]->IsV4 ())
} (*m_Addresses)[eSSU2V6Idx]->caps &= ~AddressCaps::eV6;
else (*m_Addresses)[eSSU2V6Idx].reset ();
it = m_Addresses->erase (it);
}
else
++it;
} }
UpdateSupportedTransports (); UpdateSupportedTransports ();
} }
@@ -745,21 +762,17 @@ namespace data
{ {
if (IsV4 ()) if (IsV4 ())
{ {
for (auto it = m_Addresses->begin (); it != m_Addresses->end ();) if ((*m_Addresses)[eNTCP2V4Idx])
{ {
auto addr = *it; if ((*m_Addresses)[eNTCP2V4Idx]->IsV6 ())
if (addr->IsV4 ()) (*m_Addresses)[eNTCP2V4Idx]->caps &= ~AddressCaps::eV4;
{ (*m_Addresses)[eNTCP2V4Idx].reset ();
if (addr->IsV6 ()) }
{ if ((*m_Addresses)[eSSU2V4Idx])
addr->caps &= ~AddressCaps::eV4; {
++it; if ((*m_Addresses)[eSSU2V4Idx]->IsV6 ())
} (*m_Addresses)[eSSU2V4Idx]->caps &= ~AddressCaps::eV4;
else (*m_Addresses)[eSSU2V4Idx].reset ();
it = m_Addresses->erase (it);
}
else
++it;
} }
UpdateSupportedTransports (); UpdateSupportedTransports ();
} }
@@ -780,33 +793,18 @@ namespace data
{ {
m_SupportedTransports &= ~eNTCP2V6Mesh; m_SupportedTransports &= ~eNTCP2V6Mesh;
m_ReachableTransports &= ~eNTCP2V6Mesh; m_ReachableTransports &= ~eNTCP2V6Mesh;
for (auto it = m_Addresses->begin (); it != m_Addresses->end ();) (*m_Addresses)[eNTCP2V6MeshIdx].reset ();
{
auto addr = *it;
if (i2p::util::net::IsYggdrasilAddress (addr->host))
it = m_Addresses->erase (it);
else
++it;
}
} }
} }
std::shared_ptr<const RouterInfo::Address> RouterInfo::GetSSU2V4Address () const std::shared_ptr<const RouterInfo::Address> RouterInfo::GetSSU2V4Address () const
{ {
return GetAddress ( return (*GetAddresses ())[eSSU2V4Idx];
[](std::shared_ptr<const RouterInfo::Address> address)->bool
{
return (address->transportStyle == eTransportSSU2) && address->IsV4();
});
} }
std::shared_ptr<const RouterInfo::Address> RouterInfo::GetSSU2V6Address () const std::shared_ptr<const RouterInfo::Address> RouterInfo::GetSSU2V6Address () const
{ {
return GetAddress ( return (*GetAddresses ())[eSSU2V6Idx];
[](std::shared_ptr<const RouterInfo::Address> address)->bool
{
return (address->transportStyle == eTransportSSU2) && address->IsV6();
});
} }
std::shared_ptr<const RouterInfo::Address> RouterInfo::GetSSU2Address (bool v4) const std::shared_ptr<const RouterInfo::Address> RouterInfo::GetSSU2Address (bool v4) const
@@ -843,7 +841,7 @@ namespace data
auto addresses = m_Addresses; auto addresses = m_Addresses;
#endif #endif
for (const auto& address : *addresses) for (const auto& address : *addresses)
if (filter (address)) return address; if (address && filter (address)) return address;
return nullptr; return nullptr;
} }
@@ -861,40 +859,29 @@ namespace data
std::shared_ptr<const RouterInfo::Address> RouterInfo::GetSSU2AddressWithStaticKey (const uint8_t * key, bool isV6) const std::shared_ptr<const RouterInfo::Address> RouterInfo::GetSSU2AddressWithStaticKey (const uint8_t * key, bool isV6) const
{ {
if (!key) return nullptr; if (!key) return nullptr;
return GetAddress ( auto addr = (*GetAddresses ())[isV6 ? eSSU2V6Idx : eSSU2V4Idx];
[key, isV6](std::shared_ptr<const RouterInfo::Address> address)->bool if (addr && !memcmp (addr->s, key, 32))
{ return addr;
return address->IsSSU2 () && !memcmp (address->s, key, 32) && return nullptr;
((isV6 && address->IsV6 ()) || (!isV6 && address->IsV4 ()));
});
} }
std::shared_ptr<const RouterInfo::Address> RouterInfo::GetPublishedNTCP2V4Address () const std::shared_ptr<const RouterInfo::Address> RouterInfo::GetPublishedNTCP2V4Address () const
{ {
return GetAddress ( auto addr = (*GetAddresses ())[eNTCP2V4Idx];
[](std::shared_ptr<const RouterInfo::Address> address)->bool if (addr && addr->IsPublishedNTCP2 ()) return addr;
{ return nullptr;
return address->IsPublishedNTCP2 () && address->host.is_v4 ();
});
} }
std::shared_ptr<const RouterInfo::Address> RouterInfo::GetPublishedNTCP2V6Address () const std::shared_ptr<const RouterInfo::Address> RouterInfo::GetPublishedNTCP2V6Address () const
{ {
return GetAddress ( auto addr = (*GetAddresses ())[eNTCP2V6Idx];
[](std::shared_ptr<const RouterInfo::Address> address)->bool if (addr && addr->IsPublishedNTCP2 ()) return addr;
{ return nullptr;
return address->IsPublishedNTCP2 () && address->host.is_v6 () &&
!i2p::util::net::IsYggdrasilAddress (address->host);
});
} }
std::shared_ptr<const RouterInfo::Address> RouterInfo::GetYggdrasilAddress () const std::shared_ptr<const RouterInfo::Address> RouterInfo::GetYggdrasilAddress () const
{ {
return GetAddress ( return (*GetAddresses ())[eNTCP2V6MeshIdx];
[](std::shared_ptr<const RouterInfo::Address> address)->bool
{
return address->IsPublishedNTCP2 () && i2p::util::net::IsYggdrasilAddress (address->host);
});
} }
std::shared_ptr<RouterProfile> RouterInfo::GetProfile () const std::shared_ptr<RouterProfile> RouterInfo::GetProfile () const
@@ -921,30 +908,22 @@ namespace data
bool RouterInfo::IsSSU2PeerTesting (bool v4) const bool RouterInfo::IsSSU2PeerTesting (bool v4) const
{ {
if (!(m_SupportedTransports & (v4 ? eSSU2V4 : eSSU2V6))) return false; if (!(m_SupportedTransports & (v4 ? eSSU2V4 : eSSU2V6))) return false;
return (bool)GetAddress ( auto addr = (*GetAddresses ())[v4 ? eSSU2V4Idx : eSSU2V6Idx];
[v4](std::shared_ptr<const RouterInfo::Address> address)->bool return addr && addr->IsPeerTesting () && addr->IsReachableSSU ();
{
return (address->IsSSU2 ()) && address->IsPeerTesting () &&
((v4 && address->IsV4 ()) || (!v4 && address->IsV6 ())) && address->IsReachableSSU ();
});
} }
bool RouterInfo::IsSSU2Introducer (bool v4) const bool RouterInfo::IsSSU2Introducer (bool v4) const
{ {
if (!(m_SupportedTransports & (v4 ? eSSU2V4 : eSSU2V6))) return false; if (!(m_SupportedTransports & (v4 ? eSSU2V4 : eSSU2V6))) return false;
return (bool)GetAddress ( auto addr = (*GetAddresses ())[v4 ? eSSU2V4Idx : eSSU2V6Idx];
[v4](std::shared_ptr<const RouterInfo::Address> address)->bool return addr && addr->IsIntroducer () && !addr->host.is_unspecified () && addr->port;
{
return (address->IsSSU2 ()) && address->IsIntroducer () &&
((v4 && address->IsV4 ()) || (!v4 && address->IsV6 ())) && !address->host.is_unspecified ();
});
} }
void RouterInfo::SetUnreachableAddressesTransportCaps (uint8_t transports) void RouterInfo::SetUnreachableAddressesTransportCaps (uint8_t transports)
{ {
for (auto& addr: *m_Addresses) for (auto& addr: *m_Addresses)
{ {
if (!addr->published && (addr->transportStyle == eTransportNTCP2 || addr->transportStyle == eTransportSSU2)) if (addr && !addr->published)
{ {
addr->caps &= ~(eV4 | eV6); addr->caps &= ~(eV4 | eV6);
addr->caps |= transports; addr->caps |= transports;
@@ -958,6 +937,7 @@ namespace data
m_ReachableTransports = 0; m_ReachableTransports = 0;
for (const auto& addr: *m_Addresses) for (const auto& addr: *m_Addresses)
{ {
if (!addr) continue;
uint8_t transports = 0; uint8_t transports = 0;
switch (addr->transportStyle) switch (addr->transportStyle)
{ {
@@ -994,6 +974,16 @@ namespace data
return netdb.NewRouterInfoBuffer (); return netdb.NewRouterInfoBuffer ();
} }
std::shared_ptr<RouterInfo::Address> RouterInfo::NewAddress () const
{
return netdb.NewRouterInfoAddress ();
}
boost::shared_ptr<RouterInfo::Addresses> RouterInfo::NewAddresses () const
{
return netdb.NewRouterInfoAddresses ();
}
void RouterInfo::RefreshTimestamp () void RouterInfo::RefreshTimestamp ()
{ {
m_Timestamp = i2p::util::GetMillisecondsSinceEpoch (); m_Timestamp = i2p::util::GetMillisecondsSinceEpoch ();
@@ -1061,10 +1051,22 @@ namespace data
uint64_t ts = htobe64 (GetTimestamp ()); uint64_t ts = htobe64 (GetTimestamp ());
s.write ((const char *)&ts, sizeof (ts)); s.write ((const char *)&ts, sizeof (ts));
// addresses // addresses
uint8_t numAddresses = addresses->size (); uint8_t numAddresses = 0;
s.write ((char *)&numAddresses, sizeof (numAddresses)); for (size_t idx = 0; idx < addresses->size(); idx++)
for (const auto& addr_ptr : *addresses)
{ {
auto addr_ptr = (*addresses)[idx];
if (!addr_ptr) continue;
if (idx == eNTCP2V6Idx && addr_ptr == (*addresses)[eNTCP2V4Idx]) continue;
if (idx == eSSU2V6Idx && addr_ptr == (*addresses)[eSSU2V4Idx]) continue;
numAddresses++;
}
s.write ((char *)&numAddresses, sizeof (numAddresses));
for (size_t idx = 0; idx < addresses->size(); idx++)
{
auto addr_ptr = (*addresses)[idx];
if (!addr_ptr) continue;
if (idx == eNTCP2V6Idx && addr_ptr == (*addresses)[eNTCP2V4Idx]) continue;
if (idx == eSSU2V6Idx && addr_ptr == (*addresses)[eSSU2V4Idx]) continue;
const Address& address = *addr_ptr; const Address& address = *addr_ptr;
// calculate cost // calculate cost
uint8_t cost = 0x7f; uint8_t cost = 0x7f;
@@ -1263,20 +1265,28 @@ namespace data
return std::make_shared<Buffer> (); return std::make_shared<Buffer> ();
} }
std::shared_ptr<RouterInfo::Address> LocalRouterInfo::NewAddress () const
{
return std::make_shared<Address> ();
}
boost::shared_ptr<RouterInfo::Addresses> LocalRouterInfo::NewAddresses () const
{
return boost::make_shared<Addresses> ();
}
bool LocalRouterInfo::AddSSU2Introducer (const Introducer& introducer, bool v4) bool LocalRouterInfo::AddSSU2Introducer (const Introducer& introducer, bool v4)
{ {
auto addresses = GetAddresses (); auto addresses = GetAddresses ();
if (!addresses) return false; if (!addresses) return false;
for (auto& addr : *addresses) auto addr = (*addresses)[v4 ? eSSU2V4Idx : eSSU2V6Idx];
if (addr)
{ {
if (addr->IsSSU2 () && ((v4 && addr->IsV4 ()) || (!v4 && addr->IsV6 ()))) for (auto& intro: addr->ssu->introducers)
{ if (intro.iTag == introducer.iTag) return false; // already presented
for (auto& intro: addr->ssu->introducers) addr->ssu->introducers.push_back (introducer);
if (intro.iTag == introducer.iTag) return false; // already presented SetReachableTransports (GetReachableTransports () | ((addr->IsV4 () ? eSSU2V4 : eSSU2V6)));
addr->ssu->introducers.push_back (introducer); return true;
SetReachableTransports (GetReachableTransports () | ((addr->IsV4 () ? eSSU2V4 : eSSU2V6)));
return true;
}
} }
return false; return false;
} }
@@ -1285,19 +1295,17 @@ namespace data
{ {
auto addresses = GetAddresses (); auto addresses = GetAddresses ();
if (!addresses) return false; if (!addresses) return false;
for (auto& addr: *addresses) auto addr = (*addresses)[v4 ? eSSU2V4Idx : eSSU2V6Idx];
if (addr)
{ {
if (addr->IsSSU2 () && ((v4 && addr->IsV4 ()) || (!v4 && addr->IsV6 ()))) for (auto it = addr->ssu->introducers.begin (); it != addr->ssu->introducers.end (); ++it)
{ if (h == it->iH)
for (auto it = addr->ssu->introducers.begin (); it != addr->ssu->introducers.end (); ++it) {
if (h == it->iH) addr->ssu->introducers.erase (it);
{ if (addr->ssu->introducers.empty ())
addr->ssu->introducers.erase (it); SetReachableTransports (GetReachableTransports () & ~(addr->IsV4 () ? eSSU2V4 : eSSU2V6));
if (addr->ssu->introducers.empty ()) return true;
SetReachableTransports (GetReachableTransports () & ~(addr->IsV4 () ? eSSU2V4 : eSSU2V6)); }
return true;
}
}
} }
return false; return false;
} }

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2022, The PurpleI2P Project * Copyright (c) 2013-2023, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@@ -60,13 +60,25 @@ namespace data
{ {
public: public:
enum SupportedTransportsIdx
{
eNTCP2V4Idx = 0,
eNTCP2V6Idx,
eSSU2V4Idx,
eSSU2V6Idx,
eNTCP2V6MeshIdx,
eNumTransports
};
#define TransportBit(tr) e##tr = (1 << e##tr##Idx)
enum SupportedTransports enum SupportedTransports
{ {
eNTCP2V4 = 0x01, TransportBit(NTCP2V4), // 0x01
eNTCP2V6 = 0x02, TransportBit(NTCP2V6), // 0x02
eSSU2V4 = 0x04, TransportBit(SSU2V4), // 0x04
eSSU2V6 = 0x08, TransportBit(SSU2V6), // 0x08
eNTCP2V6Mesh = 0x10, TransportBit(NTCP2V6Mesh), // 0x10
eAllTransports = 0xFF eAllTransports = 0xFF
}; };
typedef uint8_t CompatibleTransports; typedef uint8_t CompatibleTransports;
@@ -160,7 +172,7 @@ namespace data
Buffer (const uint8_t * buf, size_t len); Buffer (const uint8_t * buf, size_t len);
}; };
typedef std::vector<std::shared_ptr<Address> > Addresses; typedef std::array<std::shared_ptr<Address>, eNumTransports> Addresses;
RouterInfo (const std::string& fullPath); RouterInfo (const std::string& fullPath);
RouterInfo (const RouterInfo& ) = default; RouterInfo (const RouterInfo& ) = default;
@@ -273,6 +285,8 @@ namespace data
template<typename Filter> template<typename Filter>
std::shared_ptr<const Address> GetAddress (Filter filter) const; std::shared_ptr<const Address> GetAddress (Filter filter) const;
virtual std::shared_ptr<Buffer> NewBuffer () const; virtual std::shared_ptr<Buffer> NewBuffer () const;
virtual std::shared_ptr<Address> NewAddress () const;
virtual boost::shared_ptr<Addresses> NewAddresses () const;
private: private:
@@ -312,6 +326,8 @@ namespace data
void UpdateCapsProperty (); void UpdateCapsProperty ();
void WriteString (const std::string& str, std::ostream& s) const; void WriteString (const std::string& str, std::ostream& s) const;
std::shared_ptr<Buffer> NewBuffer () const override; std::shared_ptr<Buffer> NewBuffer () const override;
std::shared_ptr<Address> NewAddress () const override;
boost::shared_ptr<Addresses> NewAddresses () const override;
private: private:

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2022, The PurpleI2P Project * Copyright (c) 2022-2023, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@@ -22,7 +22,7 @@ namespace transport
RunnableServiceWithWork ("SSU2"), m_ReceiveService ("SSU2r"), RunnableServiceWithWork ("SSU2"), m_ReceiveService ("SSU2r"),
m_SocketV4 (m_ReceiveService.GetService ()), m_SocketV6 (m_ReceiveService.GetService ()), m_SocketV4 (m_ReceiveService.GetService ()), m_SocketV6 (m_ReceiveService.GetService ()),
m_AddressV4 (boost::asio::ip::address_v4()), m_AddressV6 (boost::asio::ip::address_v6()), m_AddressV4 (boost::asio::ip::address_v4()), m_AddressV6 (boost::asio::ip::address_v6()),
m_TerminationTimer (GetService ()), m_ResendTimer (GetService ()), m_TerminationTimer (GetService ()), m_CleanupTimer (GetService ()), m_ResendTimer (GetService ()),
m_IntroducersUpdateTimer (GetService ()), m_IntroducersUpdateTimerV6 (GetService ()), m_IntroducersUpdateTimer (GetService ()), m_IntroducersUpdateTimerV6 (GetService ()),
m_IsPublished (true), m_IsSyncClockFromPeers (true), m_IsThroughProxy (false) m_IsPublished (true), m_IsSyncClockFromPeers (true), m_IsThroughProxy (false)
{ {
@@ -109,6 +109,7 @@ namespace transport
m_ReceiveService.Start (); m_ReceiveService.Start ();
} }
ScheduleTermination (); ScheduleTermination ();
ScheduleCleanup ();
ScheduleResend (false); ScheduleResend (false);
} }
} }
@@ -118,6 +119,7 @@ namespace transport
if (IsRunning ()) if (IsRunning ())
{ {
m_TerminationTimer.cancel (); m_TerminationTimer.cancel ();
m_CleanupTimer.cancel ();
m_ResendTimer.cancel (); m_ResendTimer.cancel ();
m_IntroducersUpdateTimer.cancel (); m_IntroducersUpdateTimer.cancel ();
m_IntroducersUpdateTimerV6.cancel (); m_IntroducersUpdateTimerV6.cancel ();
@@ -236,7 +238,19 @@ namespace transport
void SSU2Server::HandleReceivedFrom (const boost::system::error_code& ecode, size_t bytes_transferred, void SSU2Server::HandleReceivedFrom (const boost::system::error_code& ecode, size_t bytes_transferred,
Packet * packet, boost::asio::ip::udp::socket& socket) Packet * packet, boost::asio::ip::udp::socket& socket)
{ {
if (!ecode) if (!ecode
|| ecode == boost::asio::error::connection_refused
|| ecode == boost::asio::error::connection_reset
|| ecode == boost::asio::error::network_unreachable
|| ecode == boost::asio::error::host_unreachable
#ifdef _WIN32 // windows can throw WinAPI error, which is not handled by ASIO
|| ecode.value() == boost::winapi::ERROR_CONNECTION_REFUSED_
|| ecode.value() == boost::winapi::ERROR_NETWORK_UNREACHABLE_
|| ecode.value() == boost::winapi::ERROR_HOST_UNREACHABLE_
#endif
)
// just try continue reading when received ICMP response otherwise socket can crash,
// but better to find out which host were sent it and mark that router as unreachable
{ {
i2p::transport::transports.UpdateReceivedBytes (bytes_transferred); i2p::transport::transports.UpdateReceivedBytes (bytes_transferred);
packet->len = bytes_transferred; packet->len = bytes_transferred;
@@ -522,13 +536,15 @@ namespace transport
else else
it1->second->ProcessRetry (buf, len); it1->second->ProcessRetry (buf, len);
} }
else else if (!i2p::util::net::IsInReservedRange(senderEndpoint.address ()) && senderEndpoint.port ())
{ {
// assume new incoming session // assume new incoming session
auto session = std::make_shared<SSU2Session> (*this); auto session = std::make_shared<SSU2Session> (*this);
session->SetRemoteEndpoint (senderEndpoint); session->SetRemoteEndpoint (senderEndpoint);
session->ProcessFirstIncomingMessage (connID, buf, len); session->ProcessFirstIncomingMessage (connID, buf, len);
} }
else
LogPrint (eLogError, "SSU2: Incoming packet received from invalid endpoint ", senderEndpoint);
} }
} }
@@ -793,6 +809,22 @@ namespace transport
it++; it++;
} }
ScheduleTermination ();
}
}
void SSU2Server::ScheduleCleanup ()
{
m_CleanupTimer.expires_from_now (boost::posix_time::seconds(SSU2_CLEANUP_INTERVAL));
m_CleanupTimer.async_wait (std::bind (&SSU2Server::HandleCleanupTimer,
this, std::placeholders::_1));
}
void SSU2Server::HandleCleanupTimer (const boost::system::error_code& ecode)
{
if (ecode != boost::asio::error::operation_aborted)
{
auto ts = i2p::util::GetSecondsSinceEpoch ();
for (auto it = m_Relays.begin (); it != m_Relays.begin ();) for (auto it = m_Relays.begin (); it != m_Relays.begin ();)
{ {
if (it->second && it->second->GetState () == eSSU2SessionStateTerminated) if (it->second && it->second->GetState () == eSSU2SessionStateTerminated)
@@ -819,7 +851,9 @@ namespace transport
m_PacketsPool.CleanUpMt (); m_PacketsPool.CleanUpMt ();
m_SentPacketsPool.CleanUp (); m_SentPacketsPool.CleanUp ();
ScheduleTermination (); m_IncompleteMessagesPool.CleanUp ();
m_FragmentsPool.CleanUp ();
ScheduleCleanup ();
} }
} }

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2022, The PurpleI2P Project * Copyright (c) 2022-2023, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@@ -18,7 +18,8 @@ namespace i2p
{ {
namespace transport namespace transport
{ {
const int SSU2_TERMINATION_CHECK_TIMEOUT = 30; // in seconds const int SSU2_TERMINATION_CHECK_TIMEOUT = 25; // in seconds
const int SSU2_CLEANUP_INTERVAL = 72; // in seconds
const int SSU2_RESEND_CHECK_TIMEOUT = 400; // in milliseconds const int SSU2_RESEND_CHECK_TIMEOUT = 400; // in milliseconds
const int SSU2_RESEND_CHECK_TIMEOUT_VARIANCE = 100; // in milliseconds const int SSU2_RESEND_CHECK_TIMEOUT_VARIANCE = 100; // in milliseconds
const int SSU2_RESEND_CHECK_MORE_TIMEOUT = 10; // in milliseconds const int SSU2_RESEND_CHECK_MORE_TIMEOUT = 10; // in milliseconds
@@ -97,6 +98,8 @@ namespace transport
void RescheduleIntroducersUpdateTimerV6 (); void RescheduleIntroducersUpdateTimerV6 ();
i2p::util::MemoryPool<SSU2SentPacket>& GetSentPacketsPool () { return m_SentPacketsPool; }; i2p::util::MemoryPool<SSU2SentPacket>& GetSentPacketsPool () { return m_SentPacketsPool; };
i2p::util::MemoryPool<SSU2IncompleteMessage>& GetIncompleteMessagesPool () { return m_IncompleteMessagesPool; };
i2p::util::MemoryPool<SSU2IncompleteMessage::Fragment>& GetFragmentsPool () { return m_FragmentsPool; };
private: private:
@@ -111,6 +114,9 @@ namespace transport
void ScheduleTermination (); void ScheduleTermination ();
void HandleTerminationTimer (const boost::system::error_code& ecode); void HandleTerminationTimer (const boost::system::error_code& ecode);
void ScheduleCleanup ();
void HandleCleanupTimer (const boost::system::error_code& ecode);
void ScheduleResend (bool more); void ScheduleResend (bool more);
void HandleResendTimer (const boost::system::error_code& ecode); void HandleResendTimer (const boost::system::error_code& ecode);
@@ -147,7 +153,9 @@ namespace transport
std::list<i2p::data::IdentHash> m_Introducers, m_IntroducersV6; // introducers we are connected to std::list<i2p::data::IdentHash> m_Introducers, m_IntroducersV6; // introducers we are connected to
i2p::util::MemoryPoolMt<Packet> m_PacketsPool; i2p::util::MemoryPoolMt<Packet> m_PacketsPool;
i2p::util::MemoryPool<SSU2SentPacket> m_SentPacketsPool; i2p::util::MemoryPool<SSU2SentPacket> m_SentPacketsPool;
boost::asio::deadline_timer m_TerminationTimer, m_ResendTimer, i2p::util::MemoryPool<SSU2IncompleteMessage> m_IncompleteMessagesPool;
i2p::util::MemoryPool<SSU2IncompleteMessage::Fragment> m_FragmentsPool;
boost::asio::deadline_timer m_TerminationTimer, m_CleanupTimer, m_ResendTimer,
m_IntroducersUpdateTimer, m_IntroducersUpdateTimerV6; m_IntroducersUpdateTimer, m_IntroducersUpdateTimerV6;
std::shared_ptr<SSU2Session> m_LastSession; std::shared_ptr<SSU2Session> m_LastSession;
bool m_IsPublished; // if we maintain introducers bool m_IsPublished; // if we maintain introducers

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2022, The PurpleI2P Project * Copyright (c) 2022-2023, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@@ -32,6 +32,51 @@ namespace transport
nextFragmentNum++; nextFragmentNum++;
} }
bool SSU2IncompleteMessage::ConcatOutOfSequenceFragments ()
{
bool isLast = false;
while (outOfSequenceFragments)
{
if (outOfSequenceFragments->fragmentNum == nextFragmentNum)
{
AttachNextFragment (outOfSequenceFragments->buf, outOfSequenceFragments->len);
isLast = outOfSequenceFragments->isLast;
if (isLast)
outOfSequenceFragments = nullptr;
else
outOfSequenceFragments = outOfSequenceFragments->next;
}
else
break;
}
return isLast;
}
void SSU2IncompleteMessage::AddOutOfSequenceFragment (std::shared_ptr<SSU2IncompleteMessage::Fragment> fragment)
{
if (!fragment || !fragment->fragmentNum) return; // fragment 0 not allowed
if (fragment->fragmentNum < nextFragmentNum) return; // already processed
if (!outOfSequenceFragments)
outOfSequenceFragments = fragment;
else
{
auto frag = outOfSequenceFragments;
std::shared_ptr<Fragment> prev;
do
{
if (fragment->fragmentNum < frag->fragmentNum) break; // found
if (fragment->fragmentNum == frag->fragmentNum) return; // duplicate
prev = frag; frag = frag->next;
}
while (frag);
fragment->next = frag;
if (prev)
prev->next = fragment;
else
outOfSequenceFragments = fragment;
}
lastFragmentInsertTime = i2p::util::GetSecondsSinceEpoch ();
}
SSU2Session::SSU2Session (SSU2Server& server, std::shared_ptr<const i2p::data::RouterInfo> in_RemoteRouter, SSU2Session::SSU2Session (SSU2Server& server, std::shared_ptr<const i2p::data::RouterInfo> in_RemoteRouter,
std::shared_ptr<const i2p::data::RouterInfo::Address> addr): std::shared_ptr<const i2p::data::RouterInfo::Address> addr):
@@ -216,10 +261,12 @@ namespace transport
m_SessionConfirmedFragment.reset (nullptr); m_SessionConfirmedFragment.reset (nullptr);
m_PathChallenge.reset (nullptr); m_PathChallenge.reset (nullptr);
m_SendQueue.clear (); m_SendQueue.clear ();
m_SendQueueSize = 0;
m_SentPackets.clear (); m_SentPackets.clear ();
m_IncompleteMessages.clear (); m_IncompleteMessages.clear ();
m_RelaySessions.clear (); m_RelaySessions.clear ();
m_PeerTests.clear (); m_PeerTests.clear ();
m_ReceivedI2NPMsgIDs.clear ();
m_Server.RemoveSession (m_SourceConnID); m_Server.RemoveSession (m_SourceConnID);
transports.PeerDisconnected (shared_from_this ()); transports.PeerDisconnected (shared_from_this ());
LogPrint (eLogDebug, "SSU2: Session terminated"); LogPrint (eLogDebug, "SSU2: Session terminated");
@@ -290,7 +337,7 @@ namespace transport
{ {
if (m_State == eSSU2SessionStateTerminated) return; if (m_State == eSSU2SessionStateTerminated) return;
for (auto it: msgs) for (auto it: msgs)
m_SendQueue.push_back (it); m_SendQueue.push_back (std::move (it));
SendQueue (); SendQueue ();
if (m_SendQueue.size () > 0) // windows is full if (m_SendQueue.size () > 0) // windows is full
@@ -304,6 +351,7 @@ namespace transport
RequestTermination (eSSU2TerminationReasonTimeout); RequestTermination (eSSU2TerminationReasonTimeout);
} }
} }
m_SendQueueSize = m_SendQueue.size ();
} }
bool SSU2Session::SendQueue () bool SSU2Session::SendQueue ()
@@ -463,6 +511,7 @@ namespace transport
LogPrint (eLogInfo, "SSU2: Packet was not Acked after ", it->second->numResends, " attempts. Terminate session"); LogPrint (eLogInfo, "SSU2: Packet was not Acked after ", it->second->numResends, " attempts. Terminate session");
m_SentPackets.clear (); m_SentPackets.clear ();
m_SendQueue.clear (); m_SendQueue.clear ();
m_SendQueueSize = 0;
RequestTermination (eSSU2TerminationReasonTimeout); RequestTermination (eSSU2TerminationReasonTimeout);
return resentPackets.size (); return resentPackets.size ();
} }
@@ -570,7 +619,7 @@ namespace transport
// payload // payload
payload[0] = eSSU2BlkDateTime; payload[0] = eSSU2BlkDateTime;
htobe16buf (payload + 1, 4); htobe16buf (payload + 1, 4);
htobe32buf (payload + 3, ts/1000); htobe32buf (payload + 3, (ts + 500)/1000);
size_t payloadSize = 7; size_t payloadSize = 7;
if (GetRouterStatus () == eRouterStatusFirewalled && m_Address->IsIntroducer ()) if (GetRouterStatus () == eRouterStatusFirewalled && m_Address->IsIntroducer ())
{ {
@@ -611,7 +660,7 @@ namespace transport
void SSU2Session::ProcessSessionRequest (Header& header, uint8_t * buf, size_t len) void SSU2Session::ProcessSessionRequest (Header& header, uint8_t * buf, size_t len)
{ {
// we are Bob // we are Bob
if (len < 80) if (len < 88)
{ {
LogPrint (eLogWarning, "SSU2: SessionRequest message too short ", len); LogPrint (eLogWarning, "SSU2: SessionRequest message too short ", len);
return; return;
@@ -684,7 +733,7 @@ namespace transport
size_t maxPayloadSize = m_MaxPayloadSize - 48; size_t maxPayloadSize = m_MaxPayloadSize - 48;
payload[0] = eSSU2BlkDateTime; payload[0] = eSSU2BlkDateTime;
htobe16buf (payload + 1, 4); htobe16buf (payload + 1, 4);
htobe32buf (payload + 3, ts/1000); htobe32buf (payload + 3, (ts + 500)/1000);
size_t payloadSize = 7; size_t payloadSize = 7;
payloadSize += CreateAddressBlock (payload + payloadSize, maxPayloadSize - payloadSize, m_RemoteEndpoint); payloadSize += CreateAddressBlock (payload + payloadSize, maxPayloadSize - payloadSize, m_RemoteEndpoint);
if (m_RelayTag) if (m_RelayTag)
@@ -1046,7 +1095,7 @@ namespace transport
// payload // payload
payload[0] = eSSU2BlkDateTime; payload[0] = eSSU2BlkDateTime;
htobe16buf (payload + 1, 4); htobe16buf (payload + 1, 4);
htobe32buf (payload + 3, i2p::util::GetSecondsSinceEpoch ()); htobe32buf (payload + 3, (i2p::util::GetMillisecondsSinceEpoch () + 500)/1000);
size_t payloadSize = 7; size_t payloadSize = 7;
payloadSize += CreatePaddingBlock (payload + payloadSize, 25 - payloadSize, 1); payloadSize += CreatePaddingBlock (payload + payloadSize, 25 - payloadSize, 1);
// encrypt // encrypt
@@ -1117,7 +1166,7 @@ namespace transport
// payload // payload
payload[0] = eSSU2BlkDateTime; payload[0] = eSSU2BlkDateTime;
htobe16buf (payload + 1, 4); htobe16buf (payload + 1, 4);
htobe32buf (payload + 3, i2p::util::GetSecondsSinceEpoch ()); htobe32buf (payload + 3, (i2p::util::GetMillisecondsSinceEpoch () + 500)/1000);
size_t payloadSize = 7; size_t payloadSize = 7;
payloadSize += CreateAddressBlock (payload + payloadSize, 56 - payloadSize, m_RemoteEndpoint); payloadSize += CreateAddressBlock (payload + payloadSize, 56 - payloadSize, m_RemoteEndpoint);
if (m_TerminationReason != eSSU2TerminationReasonNormalClose) if (m_TerminationReason != eSSU2TerminationReasonNormalClose)
@@ -1205,7 +1254,7 @@ namespace transport
// payload // payload
payload[0] = eSSU2BlkDateTime; payload[0] = eSSU2BlkDateTime;
htobe16buf (payload + 1, 4); htobe16buf (payload + 1, 4);
htobe32buf (payload + 3, i2p::util::GetSecondsSinceEpoch ()); htobe32buf (payload + 3, (i2p::util::GetMillisecondsSinceEpoch () + 500)/1000);
size_t payloadSize = 7; size_t payloadSize = 7;
payloadSize += CreateAddressBlock (payload + payloadSize, m_MaxPayloadSize - payloadSize, ep); payloadSize += CreateAddressBlock (payload + payloadSize, m_MaxPayloadSize - payloadSize, ep);
payloadSize += CreateRelayResponseBlock (payload + payloadSize, m_MaxPayloadSize - payloadSize, payloadSize += CreateRelayResponseBlock (payload + payloadSize, m_MaxPayloadSize - payloadSize,
@@ -1281,7 +1330,7 @@ namespace transport
// payload // payload
payload[0] = eSSU2BlkDateTime; payload[0] = eSSU2BlkDateTime;
htobe16buf (payload + 1, 4); htobe16buf (payload + 1, 4);
htobe32buf (payload + 3, i2p::util::GetSecondsSinceEpoch ()); htobe32buf (payload + 3, (i2p::util::GetMillisecondsSinceEpoch () + 500)/1000);
size_t payloadSize = 7; size_t payloadSize = 7;
if (msg == 6 || msg == 7) if (msg == 6 || msg == 7)
payloadSize += CreateAddressBlock (payload + payloadSize, m_MaxPayloadSize - payloadSize, m_RemoteEndpoint); payloadSize += CreateAddressBlock (payload + payloadSize, m_MaxPayloadSize - payloadSize, m_RemoteEndpoint);
@@ -1447,7 +1496,7 @@ namespace transport
nextMsg->len = nextMsg->offset + size + 7; // 7 more bytes for full I2NP header nextMsg->len = nextMsg->offset + size + 7; // 7 more bytes for full I2NP header
memcpy (nextMsg->GetNTCP2Header (), buf + offset, size); memcpy (nextMsg->GetNTCP2Header (), buf + offset, size);
nextMsg->FromNTCP2 (); // SSU2 has the same format as NTCP2 nextMsg->FromNTCP2 (); // SSU2 has the same format as NTCP2
m_Handler.PutNextMessage (std::move (nextMsg)); HandleI2NPMsg (std::move (nextMsg));
m_IsDataReceived = true; m_IsDataReceived = true;
break; break;
} }
@@ -1661,25 +1710,20 @@ namespace transport
bool isV4 = ep.address ().is_v4 (); bool isV4 = ep.address ().is_v4 ();
if (ep.port () != m_Server.GetPort (isV4)) if (ep.port () != m_Server.GetPort (isV4))
{ {
LogPrint (eLogInfo, "SSU2: Our port ", ep.port (), " received from ", m_RemoteEndpoint, " is different from ", m_Server.GetPort (isV4));
if (isV4) if (isV4)
{ {
if (i2p::context.GetStatus () == eRouterStatusTesting || if (i2p::context.GetStatus () == eRouterStatusTesting)
m_State == eSSU2SessionStatePeerTest)
{
i2p::context.SetError (eRouterErrorSymmetricNAT); i2p::context.SetError (eRouterErrorSymmetricNAT);
i2p::context.SetStatus (eRouterStatusFirewalled); else if (m_State == eSSU2SessionStatePeerTest)
m_Server.RescheduleIntroducersUpdateTimer (); i2p::context.SetError (eRouterErrorFullConeNAT);
}
} }
else else
{ {
if (i2p::context.GetStatusV6 () == eRouterStatusTesting || if (i2p::context.GetStatusV6 () == eRouterStatusTesting)
m_State == eSSU2SessionStatePeerTest)
{
i2p::context.SetErrorV6 (eRouterErrorSymmetricNAT); i2p::context.SetErrorV6 (eRouterErrorSymmetricNAT);
i2p::context.SetStatusV6 (eRouterStatusFirewalled); else if (m_State == eSSU2SessionStatePeerTest)
m_Server.RescheduleIntroducersUpdateTimerV6 (); i2p::context.SetErrorV6 (eRouterErrorFullConeNAT);
}
} }
} }
else else
@@ -1687,11 +1731,23 @@ namespace transport
if (isV4) if (isV4)
{ {
if (i2p::context.GetError () == eRouterErrorSymmetricNAT) if (i2p::context.GetError () == eRouterErrorSymmetricNAT)
{
if (m_State == eSSU2SessionStatePeerTest)
i2p::context.SetStatus (eRouterStatusOK);
i2p::context.SetError (eRouterErrorNone);
}
else if (i2p::context.GetError () == eRouterErrorFullConeNAT)
i2p::context.SetError (eRouterErrorNone); i2p::context.SetError (eRouterErrorNone);
} }
else else
{ {
if (i2p::context.GetErrorV6 () == eRouterErrorSymmetricNAT) if (i2p::context.GetErrorV6 () == eRouterErrorSymmetricNAT)
{
if (m_State == eSSU2SessionStatePeerTest)
i2p::context.SetStatusV6 (eRouterStatusOK);
i2p::context.SetErrorV6 (eRouterErrorNone);
}
else if (i2p::context.GetErrorV6 () == eRouterErrorFullConeNAT)
i2p::context.SetErrorV6 (eRouterErrorNone); i2p::context.SetErrorV6 (eRouterErrorNone);
} }
} }
@@ -1716,17 +1772,17 @@ namespace transport
} }
else else
{ {
m = std::make_shared<SSU2IncompleteMessage>(); m = m_Server.GetIncompleteMessagesPool ().AcquireShared ();
m_IncompleteMessages.emplace (msgID, m); m_IncompleteMessages.emplace (msgID, m);
} }
m->msg = msg; m->msg = msg;
m->nextFragmentNum = 1; m->nextFragmentNum = 1;
m->lastFragmentInsertTime = i2p::util::GetSecondsSinceEpoch (); m->lastFragmentInsertTime = i2p::util::GetSecondsSinceEpoch ();
if (found && ConcatOutOfSequenceFragments (m)) if (found && m->ConcatOutOfSequenceFragments ())
{ {
// we have all follow-on fragments already // we have all follow-on fragments already
m->msg->FromNTCP2 (); m->msg->FromNTCP2 ();
m_Handler.PutNextMessage (std::move (m->msg)); HandleI2NPMsg (std::move (m->msg));
m_IncompleteMessages.erase (it); m_IncompleteMessages.erase (it);
} }
} }
@@ -1735,11 +1791,17 @@ namespace transport
{ {
if (len < 5) return; if (len < 5) return;
uint8_t fragmentNum = buf[0] >> 1; uint8_t fragmentNum = buf[0] >> 1;
if (!fragmentNum || fragmentNum >= SSU2_MAX_NUM_FRAGMENTS)
{
LogPrint (eLogWarning, "SSU2: Invalid follow-on fragment num ", fragmentNum);
return;
}
bool isLast = buf[0] & 0x01; bool isLast = buf[0] & 0x01;
uint32_t msgID; memcpy (&msgID, buf + 1, 4); uint32_t msgID; memcpy (&msgID, buf + 1, 4);
auto it = m_IncompleteMessages.find (msgID); auto it = m_IncompleteMessages.find (msgID);
if (it != m_IncompleteMessages.end ()) if (it != m_IncompleteMessages.end ())
{ {
if (fragmentNum < it->second->nextFragmentNum) return; // duplicate
if (it->second->nextFragmentNum == fragmentNum && fragmentNum < SSU2_MAX_NUM_FRAGMENTS && if (it->second->nextFragmentNum == fragmentNum && fragmentNum < SSU2_MAX_NUM_FRAGMENTS &&
it->second->msg) it->second->msg)
{ {
@@ -1748,14 +1810,14 @@ namespace transport
if (isLast) if (isLast)
{ {
it->second->msg->FromNTCP2 (); it->second->msg->FromNTCP2 ();
m_Handler.PutNextMessage (std::move (it->second->msg)); HandleI2NPMsg (std::move (it->second->msg));
m_IncompleteMessages.erase (it); m_IncompleteMessages.erase (it);
} }
else else
{ {
if (ConcatOutOfSequenceFragments (it->second)) if (it->second->ConcatOutOfSequenceFragments ())
{ {
m_Handler.PutNextMessage (std::move (it->second->msg)); HandleI2NPMsg (std::move (it->second->msg));
m_IncompleteMessages.erase (it); m_IncompleteMessages.erase (it);
} }
else else
@@ -1767,38 +1829,17 @@ namespace transport
else else
{ {
// follow-on fragment before first fragment // follow-on fragment before first fragment
auto msg = std::make_shared<SSU2IncompleteMessage> (); auto msg = m_Server.GetIncompleteMessagesPool ().AcquireShared ();
msg->nextFragmentNum = 0; msg->nextFragmentNum = 0;
it = m_IncompleteMessages.emplace (msgID, msg).first; it = m_IncompleteMessages.emplace (msgID, msg).first;
} }
// insert out of sequence fragment // insert out of sequence fragment
if (fragmentNum >= SSU2_MAX_NUM_FRAGMENTS) auto fragment = m_Server.GetFragmentsPool ().AcquireShared ();
{
LogPrint (eLogWarning, "SSU2: Fragment number ", fragmentNum, " exceeds ", SSU2_MAX_NUM_FRAGMENTS);
return;
}
auto fragment = std::make_shared<SSU2IncompleteMessage::Fragment> ();
memcpy (fragment->buf, buf + 5, len -5); memcpy (fragment->buf, buf + 5, len -5);
fragment->len = len - 5; fragment->len = len - 5;
fragment->fragmentNum = fragmentNum;
fragment->isLast = isLast; fragment->isLast = isLast;
it->second->outOfSequenceFragments.emplace (fragmentNum, fragment); it->second->AddOutOfSequenceFragment (fragment);
it->second->lastFragmentInsertTime = i2p::util::GetSecondsSinceEpoch ();
}
bool SSU2Session::ConcatOutOfSequenceFragments (std::shared_ptr<SSU2IncompleteMessage> m)
{
if (!m) return false;
bool isLast = false;
for (auto it = m->outOfSequenceFragments.begin (); it != m->outOfSequenceFragments.end ();)
if (it->first == m->nextFragmentNum)
{
m->AttachNextFragment (it->second->buf, it->second->len);
isLast = it->second->isLast;
it = m->outOfSequenceFragments.erase (it);
}
else
break;
return isLast;
} }
void SSU2Session::HandleRelayRequest (const uint8_t * buf, size_t len) void SSU2Session::HandleRelayRequest (const uint8_t * buf, size_t len)
@@ -1836,7 +1877,7 @@ namespace transport
session->SendData (payload, payloadSize); session->SendData (payload, payloadSize);
} }
void SSU2Session::HandleRelayIntro (const uint8_t * buf, size_t len) void SSU2Session::HandleRelayIntro (const uint8_t * buf, size_t len, int attempts)
{ {
// we are Charlie // we are Charlie
SSU2RelayResponseCode code = eSSU2RelayResponseCodeAccept; SSU2RelayResponseCode code = eSSU2RelayResponseCodeAccept;
@@ -1891,9 +1932,22 @@ namespace transport
code = eSSU2RelayResponseCodeCharlieSignatureFailure; code = eSSU2RelayResponseCodeCharlieSignatureFailure;
} }
} }
else if (!attempts)
{
// RouterInfo might come in the next packet, try again
auto vec = std::make_shared<std::vector<uint8_t> >(len);
memcpy (vec->data (), buf, len);
auto s = shared_from_this ();
m_Server.GetService ().post ([s, vec, attempts]()
{
LogPrint (eLogDebug, "SSU2: RelayIntro attempt ", attempts + 1);
s->HandleRelayIntro (vec->data (), vec->size (), attempts + 1);
});
return;
}
else else
{ {
LogPrint (eLogError, "SSU2: RelayIntro unknown router to introduce"); LogPrint (eLogWarning, "SSU2: RelayIntro unknown router to introduce");
code = eSSU2RelayResponseCodeCharlieAliceIsUnknown; code = eSSU2RelayResponseCodeCharlieAliceIsUnknown;
} }
// send relay response to Bob // send relay response to Bob
@@ -2243,6 +2297,22 @@ namespace transport
} }
} }
void SSU2Session::HandleI2NPMsg (std::shared_ptr<I2NPMessage>&& msg)
{
if (!msg) return;
int32_t msgID = msg->GetMsgID ();
if (!msg->IsExpired ())
{
// m_LastActivityTimestamp is updated in ProcessData before
if (m_ReceivedI2NPMsgIDs.emplace (msgID, (uint32_t)m_LastActivityTimestamp).second)
m_Handler.PutNextMessage (std::move (msg));
else
LogPrint (eLogDebug, "SSU2: Message ", msgID, " already received");
}
else
LogPrint (eLogDebug, "SSU2: Message ", msgID, " expired");
}
bool SSU2Session::ExtractEndpoint (const uint8_t * buf, size_t size, boost::asio::ip::udp::endpoint& ep) bool SSU2Session::ExtractEndpoint (const uint8_t * buf, size_t size, boost::asio::ip::udp::endpoint& ep)
{ {
if (size < 2) return false; if (size < 2) return false;
@@ -2405,7 +2475,7 @@ namespace transport
d.quot = maxNumRanges; d.quot = maxNumRanges;
d.rem = 0; d.rem = 0;
} }
// Acks only ragnes for acnt // Acks only ranges for acnt
for (int i = 0; i < d.quot; i++) for (int i = 0; i < d.quot; i++)
{ {
buf[8 + numRanges*2] = 0; buf[8 + numRanges*2 + 1] = 255; // NACKs 0, Acks 255 buf[8 + numRanges*2] = 0; buf[8 + numRanges*2 + 1] = 255; // NACKs 0, Acks 255
@@ -2454,7 +2524,7 @@ namespace transport
} }
if (numRanges < maxNumRanges && it == m_OutOfSequencePackets.rend ()) if (numRanges < maxNumRanges && it == m_OutOfSequencePackets.rend ())
{ {
// add range between out-of-seqence and received // add range between out-of-sequence and received
int nacks = *m_OutOfSequencePackets.begin () - m_ReceivePacketNum - 1; int nacks = *m_OutOfSequencePackets.begin () - m_ReceivePacketNum - 1;
if (nacks > 0) if (nacks > 0)
{ {
@@ -2477,14 +2547,9 @@ namespace transport
size_t paddingSize = rand () & 0x0F; // 0 - 15 size_t paddingSize = rand () & 0x0F; // 0 - 15
if (paddingSize + 3 > len) paddingSize = len - 3; if (paddingSize + 3 > len) paddingSize = len - 3;
else if (paddingSize + 3 < minSize) paddingSize = minSize - 3; else if (paddingSize + 3 < minSize) paddingSize = minSize - 3;
if (paddingSize) buf[0] = eSSU2BlkPadding;
{ htobe16buf (buf + 1, paddingSize);
buf[0] = eSSU2BlkPadding; memset (buf + 3, 0, paddingSize);
htobe16buf (buf + 1, paddingSize);
memset (buf + 3, 0, paddingSize);
}
else
return 0;
return paddingSize + 3; return paddingSize + 3;
} }
@@ -2770,6 +2835,20 @@ namespace transport
else else
++it; ++it;
} }
if (m_ReceivedI2NPMsgIDs.size () > SSU2_MAX_NUM_RECEIVED_I2NP_MSGIDS || ts > m_LastActivityTimestamp + SSU2_DECAY_INTERVAL)
// decay
m_ReceivedI2NPMsgIDs.clear ();
else
{
// delete old received msgIDs
for (auto it = m_ReceivedI2NPMsgIDs.begin (); it != m_ReceivedI2NPMsgIDs.end ();)
{
if (ts > it->second + SSU2_RECEIVED_I2NP_MSGIDS_CLEANUP_TIMEOUT)
it = m_ReceivedI2NPMsgIDs.erase (it);
else
++it;
}
}
if (!m_OutOfSequencePackets.empty ()) if (!m_OutOfSequencePackets.empty ())
{ {
if (m_OutOfSequencePackets.size () > 2*SSU2_MAX_NUM_ACK_RANGES || if (m_OutOfSequencePackets.size () > 2*SSU2_MAX_NUM_ACK_RANGES ||
@@ -2821,6 +2900,8 @@ namespace transport
void SSU2Session::FlushData () void SSU2Session::FlushData ()
{ {
bool sent = SendQueue (); // if we have something to send bool sent = SendQueue (); // if we have something to send
if (sent)
m_SendQueueSize = m_SendQueue.size ();
if (m_IsDataReceived) if (m_IsDataReceived)
{ {
if (!sent) SendQuickAck (); if (!sent) SendQuickAck ();

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2022, The PurpleI2P Project * Copyright (c) 2022-2023, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@@ -39,6 +39,9 @@ namespace transport
const int SSU2_RESEND_INTERVAL = 300; // in milliseconds const int SSU2_RESEND_INTERVAL = 300; // in milliseconds
const int SSU2_MAX_NUM_RESENDS = 5; const int SSU2_MAX_NUM_RESENDS = 5;
const int SSU2_INCOMPLETE_MESSAGES_CLEANUP_TIMEOUT = 30; // in seconds const int SSU2_INCOMPLETE_MESSAGES_CLEANUP_TIMEOUT = 30; // in seconds
const int SSU2_MAX_NUM_RECEIVED_I2NP_MSGIDS = 5000; // how many msgID we store for duplicates check
const int SSU2_RECEIVED_I2NP_MSGIDS_CLEANUP_TIMEOUT = 10; // in seconds
const int SSU2_DECAY_INTERVAL = 20; // in seconds
const size_t SSU2_MIN_WINDOW_SIZE = 16; // in packets const size_t SSU2_MIN_WINDOW_SIZE = 16; // in packets
const size_t SSU2_MAX_WINDOW_SIZE = 256; // in packets const size_t SSU2_MAX_WINDOW_SIZE = 256; // in packets
const size_t SSU2_MIN_RTO = 100; // in milliseconds const size_t SSU2_MIN_RTO = 100; // in milliseconds
@@ -168,15 +171,19 @@ namespace transport
{ {
uint8_t buf[SSU2_MAX_PACKET_SIZE]; uint8_t buf[SSU2_MAX_PACKET_SIZE];
size_t len; size_t len;
int fragmentNum;
bool isLast; bool isLast;
std::shared_ptr<Fragment> next;
}; };
std::shared_ptr<I2NPMessage> msg; std::shared_ptr<I2NPMessage> msg;
int nextFragmentNum; int nextFragmentNum;
uint32_t lastFragmentInsertTime; // in seconds uint32_t lastFragmentInsertTime; // in seconds
std::map<int, std::shared_ptr<Fragment> > outOfSequenceFragments; std::shared_ptr<Fragment> outOfSequenceFragments; // #1 and more
void AttachNextFragment (const uint8_t * fragment, size_t fragmentSize); void AttachNextFragment (const uint8_t * fragment, size_t fragmentSize);
bool ConcatOutOfSequenceFragments (); // true if message complete
void AddOutOfSequenceFragment (std::shared_ptr<Fragment> fragment);
}; };
struct SSU2SentPacket struct SSU2SentPacket
@@ -245,7 +252,7 @@ namespace transport
void SendI2NPMessages (const std::vector<std::shared_ptr<I2NPMessage> >& msgs) override; void SendI2NPMessages (const std::vector<std::shared_ptr<I2NPMessage> >& msgs) override;
uint32_t GetRelayTag () const override { return m_RelayTag; }; uint32_t GetRelayTag () const override { return m_RelayTag; };
size_t Resend (uint64_t ts); // return number or resent packets size_t Resend (uint64_t ts); // return number or resent packets
bool IsEstablished () const { return m_State == eSSU2SessionStateEstablished; }; bool IsEstablished () const override { return m_State == eSSU2SessionStateEstablished; };
uint64_t GetConnID () const { return m_SourceConnID; }; uint64_t GetConnID () const { return m_SourceConnID; };
SSU2SessionState GetState () const { return m_State; }; SSU2SessionState GetState () const { return m_State; };
void SetState (SSU2SessionState state) { m_State = state; }; void SetState (SSU2SessionState state) { m_State = state; };
@@ -303,11 +310,11 @@ namespace transport
bool UpdateReceivePacketNum (uint32_t packetNum); // for Ack, returns false if duplicate bool UpdateReceivePacketNum (uint32_t packetNum); // for Ack, returns false if duplicate
void HandleFirstFragment (const uint8_t * buf, size_t len); void HandleFirstFragment (const uint8_t * buf, size_t len);
void HandleFollowOnFragment (const uint8_t * buf, size_t len); void HandleFollowOnFragment (const uint8_t * buf, size_t len);
bool ConcatOutOfSequenceFragments (std::shared_ptr<SSU2IncompleteMessage> m); // true if message complete
void HandleRelayRequest (const uint8_t * buf, size_t len); void HandleRelayRequest (const uint8_t * buf, size_t len);
void HandleRelayIntro (const uint8_t * buf, size_t len); void HandleRelayIntro (const uint8_t * buf, size_t len, int attempts = 0);
void HandleRelayResponse (const uint8_t * buf, size_t len); void HandleRelayResponse (const uint8_t * buf, size_t len);
void HandlePeerTest (const uint8_t * buf, size_t len); void HandlePeerTest (const uint8_t * buf, size_t len);
void HandleI2NPMsg (std::shared_ptr<I2NPMessage>&& msg);
size_t CreateAddressBlock (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& ep); size_t CreateAddressBlock (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& ep);
size_t CreateRouterInfoBlock (uint8_t * buf, size_t len, std::shared_ptr<const i2p::data::RouterInfo> r); size_t CreateRouterInfoBlock (uint8_t * buf, size_t len, std::shared_ptr<const i2p::data::RouterInfo> r);
@@ -338,7 +345,7 @@ namespace transport
uint32_t m_SendPacketNum, m_ReceivePacketNum; uint32_t m_SendPacketNum, m_ReceivePacketNum;
std::set<uint32_t> m_OutOfSequencePackets; // packet nums > receive packet num std::set<uint32_t> m_OutOfSequencePackets; // packet nums > receive packet num
std::map<uint32_t, std::shared_ptr<SSU2SentPacket> > m_SentPackets; // packetNum -> packet std::map<uint32_t, std::shared_ptr<SSU2SentPacket> > m_SentPackets; // packetNum -> packet
std::map<uint32_t, std::shared_ptr<SSU2IncompleteMessage> > m_IncompleteMessages; // I2NP std::unordered_map<uint32_t, std::shared_ptr<SSU2IncompleteMessage> > m_IncompleteMessages; // msgID -> I2NP
std::map<uint32_t, std::pair <std::shared_ptr<SSU2Session>, uint64_t > > m_RelaySessions; // nonce->(Alice, timestamp) for Bob or nonce->(Charlie, timestamp) for Alice std::map<uint32_t, std::pair <std::shared_ptr<SSU2Session>, uint64_t > > m_RelaySessions; // nonce->(Alice, timestamp) for Bob or nonce->(Charlie, timestamp) for Alice
std::map<uint32_t, std::pair <std::shared_ptr<SSU2Session>, uint64_t > > m_PeerTests; // same as for relay sessions std::map<uint32_t, std::pair <std::shared_ptr<SSU2Session>, uint64_t > > m_PeerTests; // same as for relay sessions
std::list<std::shared_ptr<I2NPMessage> > m_SendQueue; std::list<std::shared_ptr<I2NPMessage> > m_SendQueue;
@@ -351,6 +358,7 @@ namespace transport
SSU2TerminationReason m_TerminationReason; SSU2TerminationReason m_TerminationReason;
size_t m_MaxPayloadSize; size_t m_MaxPayloadSize;
std::unique_ptr<i2p::data::IdentHash> m_PathChallenge; std::unique_ptr<i2p::data::IdentHash> m_PathChallenge;
std::unordered_map<uint32_t, uint32_t> m_ReceivedI2NPMsgIDs; // msgID -> timestamp in seconds
}; };
inline uint64_t CreateHeaderMask (const uint8_t * kh, const uint8_t * nonce) inline uint64_t CreateHeaderMask (const uint8_t * kh, const uint8_t * nonce)

View File

@@ -478,11 +478,11 @@ namespace stream
{ {
if (!len) return 0; if (!len) return 0;
size_t ret = 0; size_t ret = 0;
volatile bool done = false;
std::condition_variable newDataReceived; std::condition_variable newDataReceived;
std::mutex newDataReceivedMutex; std::mutex newDataReceivedMutex;
std::unique_lock<std::mutex> l(newDataReceivedMutex);
AsyncReceive (boost::asio::buffer (buf, len), AsyncReceive (boost::asio::buffer (buf, len),
[&ret, &newDataReceived, &newDataReceivedMutex](const boost::system::error_code& ecode, std::size_t bytes_transferred) [&ret, &done, &newDataReceived, &newDataReceivedMutex](const boost::system::error_code& ecode, std::size_t bytes_transferred)
{ {
if (ecode == boost::asio::error::timed_out) if (ecode == boost::asio::error::timed_out)
ret = 0; ret = 0;
@@ -490,10 +490,29 @@ namespace stream
ret = bytes_transferred; ret = bytes_transferred;
std::unique_lock<std::mutex> l(newDataReceivedMutex); std::unique_lock<std::mutex> l(newDataReceivedMutex);
newDataReceived.notify_all (); newDataReceived.notify_all ();
done = true;
}, },
timeout); timeout);
if (newDataReceived.wait_for (l, std::chrono::seconds (timeout)) == std::cv_status::timeout) if (!done)
ret = 0; { std::unique_lock<std::mutex> l(newDataReceivedMutex);
if (!done && newDataReceived.wait_for (l, std::chrono::seconds (timeout)) == std::cv_status::timeout)
ret = 0;
}
if (!done)
{
// make sure that AsycReceive complete
auto s = shared_from_this();
m_Service.post ([s]()
{
s->m_ReceiveTimer.cancel ();
});
int i = 0;
while (!done && i < 100) // 1 sec
{
std::this_thread::sleep_for (std::chrono::milliseconds(10));
i++;
}
}
return ret; return ret;
} }

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2021, The PurpleI2P Project * Copyright (c) 2013-2022, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@@ -20,16 +20,21 @@ namespace i2p
namespace tunnel namespace tunnel
{ {
TransitTunnel::TransitTunnel (uint32_t receiveTunnelID, TransitTunnel::TransitTunnel (uint32_t receiveTunnelID,
const uint8_t * nextIdent, uint32_t nextTunnelID, const i2p::data::IdentHash& nextIdent, uint32_t nextTunnelID,
const uint8_t * layerKey,const uint8_t * ivKey): const i2p::crypto::AESKey& layerKey, const i2p::crypto::AESKey& ivKey):
TunnelBase (receiveTunnelID, nextTunnelID, nextIdent) TunnelBase (receiveTunnelID, nextTunnelID, nextIdent),
m_LayerKey (layerKey), m_IVKey (ivKey)
{ {
m_Encryption.SetKeys (layerKey, ivKey);
} }
void TransitTunnel::EncryptTunnelMsg (std::shared_ptr<const I2NPMessage> in, std::shared_ptr<I2NPMessage> out) void TransitTunnel::EncryptTunnelMsg (std::shared_ptr<const I2NPMessage> in, std::shared_ptr<I2NPMessage> out)
{ {
m_Encryption.Encrypt (in->GetPayload () + 4, out->GetPayload () + 4); if (!m_Encryption)
{
m_Encryption.reset (new i2p::crypto::TunnelEncryption);
m_Encryption->SetKeys (m_LayerKey, m_IVKey);
}
m_Encryption->Encrypt (in->GetPayload () + 4, out->GetPayload () + 4);
i2p::transport::transports.UpdateTotalTransitTransmittedBytes (TUNNEL_DATA_MSG_SIZE); i2p::transport::transports.UpdateTotalTransitTransmittedBytes (TUNNEL_DATA_MSG_SIZE);
} }
@@ -94,8 +99,8 @@ namespace tunnel
} }
std::shared_ptr<TransitTunnel> CreateTransitTunnel (uint32_t receiveTunnelID, std::shared_ptr<TransitTunnel> CreateTransitTunnel (uint32_t receiveTunnelID,
const uint8_t * nextIdent, uint32_t nextTunnelID, const i2p::data::IdentHash& nextIdent, uint32_t nextTunnelID,
const uint8_t * layerKey,const uint8_t * ivKey, const i2p::crypto::AESKey& layerKey, const i2p::crypto::AESKey& ivKey,
bool isGateway, bool isEndpoint) bool isGateway, bool isEndpoint)
{ {
if (isEndpoint) if (isEndpoint)

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2021, The PurpleI2P Project * Copyright (c) 2013-2022, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@@ -28,8 +28,8 @@ namespace tunnel
public: public:
TransitTunnel (uint32_t receiveTunnelID, TransitTunnel (uint32_t receiveTunnelID,
const uint8_t * nextIdent, uint32_t nextTunnelID, const i2p::data::IdentHash& nextIdent, uint32_t nextTunnelID,
const uint8_t * layerKey,const uint8_t * ivKey); const i2p::crypto::AESKey& layerKey, const i2p::crypto::AESKey& ivKey);
virtual size_t GetNumTransmittedBytes () const { return 0; }; virtual size_t GetNumTransmittedBytes () const { return 0; };
@@ -39,7 +39,8 @@ namespace tunnel
void EncryptTunnelMsg (std::shared_ptr<const I2NPMessage> in, std::shared_ptr<I2NPMessage> out); void EncryptTunnelMsg (std::shared_ptr<const I2NPMessage> in, std::shared_ptr<I2NPMessage> out);
private: private:
i2p::crypto::TunnelEncryption m_Encryption; i2p::crypto::AESKey m_LayerKey, m_IVKey;
std::unique_ptr<i2p::crypto::TunnelEncryption> m_Encryption;
}; };
class TransitTunnelParticipant: public TransitTunnel class TransitTunnelParticipant: public TransitTunnel
@@ -47,8 +48,8 @@ namespace tunnel
public: public:
TransitTunnelParticipant (uint32_t receiveTunnelID, TransitTunnelParticipant (uint32_t receiveTunnelID,
const uint8_t * nextIdent, uint32_t nextTunnelID, const i2p::data::IdentHash& nextIdent, uint32_t nextTunnelID,
const uint8_t * layerKey,const uint8_t * ivKey): const i2p::crypto::AESKey& layerKey, const i2p::crypto::AESKey& ivKey):
TransitTunnel (receiveTunnelID, nextIdent, nextTunnelID, TransitTunnel (receiveTunnelID, nextIdent, nextTunnelID,
layerKey, ivKey), m_NumTransmittedBytes (0) {}; layerKey, ivKey), m_NumTransmittedBytes (0) {};
~TransitTunnelParticipant (); ~TransitTunnelParticipant ();
@@ -68,8 +69,8 @@ namespace tunnel
public: public:
TransitTunnelGateway (uint32_t receiveTunnelID, TransitTunnelGateway (uint32_t receiveTunnelID,
const uint8_t * nextIdent, uint32_t nextTunnelID, const i2p::data::IdentHash& nextIdent, uint32_t nextTunnelID,
const uint8_t * layerKey,const uint8_t * ivKey): const i2p::crypto::AESKey& layerKey, const i2p::crypto::AESKey& ivKey):
TransitTunnel (receiveTunnelID, nextIdent, nextTunnelID, TransitTunnel (receiveTunnelID, nextIdent, nextTunnelID,
layerKey, ivKey), m_Gateway(this) {}; layerKey, ivKey), m_Gateway(this) {};
@@ -88,8 +89,8 @@ namespace tunnel
public: public:
TransitTunnelEndpoint (uint32_t receiveTunnelID, TransitTunnelEndpoint (uint32_t receiveTunnelID,
const uint8_t * nextIdent, uint32_t nextTunnelID, const i2p::data::IdentHash& nextIdent, uint32_t nextTunnelID,
const uint8_t * layerKey,const uint8_t * ivKey): const i2p::crypto::AESKey& layerKey, const i2p::crypto::AESKey& ivKey):
TransitTunnel (receiveTunnelID, nextIdent, nextTunnelID, layerKey, ivKey), TransitTunnel (receiveTunnelID, nextIdent, nextTunnelID, layerKey, ivKey),
m_Endpoint (false) {}; // transit endpoint is always outbound m_Endpoint (false) {}; // transit endpoint is always outbound
@@ -104,8 +105,8 @@ namespace tunnel
}; };
std::shared_ptr<TransitTunnel> CreateTransitTunnel (uint32_t receiveTunnelID, std::shared_ptr<TransitTunnel> CreateTransitTunnel (uint32_t receiveTunnelID,
const uint8_t * nextIdent, uint32_t nextTunnelID, const i2p::data::IdentHash& nextIdent, uint32_t nextTunnelID,
const uint8_t * layerKey,const uint8_t * ivKey, const i2p::crypto::AESKey& layerKey, const i2p::crypto::AESKey& ivKey,
bool isGateway, bool isEndpoint); bool isGateway, bool isEndpoint);
} }
} }

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2022, The PurpleI2P Project * Copyright (c) 2013-2023, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@@ -74,7 +74,8 @@ namespace transport
public: public:
TransportSession (std::shared_ptr<const i2p::data::RouterInfo> router, int terminationTimeout): TransportSession (std::shared_ptr<const i2p::data::RouterInfo> router, int terminationTimeout):
m_NumSentBytes (0), m_NumReceivedBytes (0), m_IsOutgoing (router), m_TerminationTimeout (terminationTimeout), m_NumSentBytes (0), m_NumReceivedBytes (0), m_SendQueueSize (0),
m_IsOutgoing (router), m_TerminationTimeout (terminationTimeout),
m_LastActivityTimestamp (i2p::util::GetSecondsSinceEpoch ()) m_LastActivityTimestamp (i2p::util::GetSecondsSinceEpoch ())
{ {
if (router) if (router)
@@ -100,6 +101,7 @@ namespace transport
size_t GetNumSentBytes () const { return m_NumSentBytes; }; size_t GetNumSentBytes () const { return m_NumSentBytes; };
size_t GetNumReceivedBytes () const { return m_NumReceivedBytes; }; size_t GetNumReceivedBytes () const { return m_NumReceivedBytes; };
size_t GetSendQueueSize () const { return m_SendQueueSize; };
bool IsOutgoing () const { return m_IsOutgoing; }; bool IsOutgoing () const { return m_IsOutgoing; };
int GetTerminationTimeout () const { return m_TerminationTimeout; }; int GetTerminationTimeout () const { return m_TerminationTimeout; };
@@ -119,7 +121,7 @@ namespace transport
std::shared_ptr<const i2p::data::IdentityEx> m_RemoteIdentity; std::shared_ptr<const i2p::data::IdentityEx> m_RemoteIdentity;
mutable std::mutex m_RemoteIdentityMutex; mutable std::mutex m_RemoteIdentityMutex;
size_t m_NumSentBytes, m_NumReceivedBytes; size_t m_NumSentBytes, m_NumReceivedBytes, m_SendQueueSize;
bool m_IsOutgoing; bool m_IsOutgoing;
int m_TerminationTimeout; int m_TerminationTimeout;
uint64_t m_LastActivityTimestamp; uint64_t m_LastActivityTimestamp;

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2022, The PurpleI2P Project * Copyright (c) 2013-2023, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@@ -168,7 +168,7 @@ namespace transport
m_Work = new boost::asio::io_service::work (*m_Service); m_Work = new boost::asio::io_service::work (*m_Service);
m_PeerCleanupTimer = new boost::asio::deadline_timer (*m_Service); m_PeerCleanupTimer = new boost::asio::deadline_timer (*m_Service);
m_PeerTestTimer = new boost::asio::deadline_timer (*m_Service); m_PeerTestTimer = new boost::asio::deadline_timer (*m_Service);
m_UpdateBandwidthTimer = new boost::asio::steady_timer (*m_Service); m_UpdateBandwidthTimer = new boost::asio::deadline_timer (*m_Service);
} }
bool ipv4; i2p::config::GetOption("ipv4", ipv4); bool ipv4; i2p::config::GetOption("ipv4", ipv4);
@@ -306,7 +306,7 @@ namespace transport
m_PeerCleanupTimer->expires_from_now (boost::posix_time::seconds(5 * SESSION_CREATION_TIMEOUT)); m_PeerCleanupTimer->expires_from_now (boost::posix_time::seconds(5 * SESSION_CREATION_TIMEOUT));
m_PeerCleanupTimer->async_wait (std::bind (&Transports::HandlePeerCleanupTimer, this, std::placeholders::_1)); m_PeerCleanupTimer->async_wait (std::bind (&Transports::HandlePeerCleanupTimer, this, std::placeholders::_1));
m_UpdateBandwidthTimer->expires_from_now (std::chrono::seconds(1)); m_UpdateBandwidthTimer->expires_from_now (boost::posix_time::seconds(1));
m_UpdateBandwidthTimer->async_wait (std::bind (&Transports::HandleUpdateBandwidthTimer, this, std::placeholders::_1)); m_UpdateBandwidthTimer->async_wait (std::bind (&Transports::HandleUpdateBandwidthTimer, this, std::placeholders::_1));
if (m_IsNAT) if (m_IsNAT)
@@ -393,7 +393,7 @@ namespace transport
m_LastTransitBandwidth15sUpdateBytes = m_TotalTransitTransmittedBytes; m_LastTransitBandwidth15sUpdateBytes = m_TotalTransitTransmittedBytes;
} }
m_UpdateBandwidthTimer->expires_from_now (std::chrono::seconds(1)); m_UpdateBandwidthTimer->expires_from_now (boost::posix_time::seconds(1));
m_UpdateBandwidthTimer->async_wait (std::bind (&Transports::HandleUpdateBandwidthTimer, this, std::placeholders::_1)); m_UpdateBandwidthTimer->async_wait (std::bind (&Transports::HandleUpdateBandwidthTimer, this, std::placeholders::_1));
} }
} }
@@ -716,7 +716,8 @@ namespace transport
session->SendI2NPMessages ({ CreateDatabaseStoreMsg () }); // send DatabaseStore session->SendI2NPMessages ({ CreateDatabaseStoreMsg () }); // send DatabaseStore
auto ts = i2p::util::GetSecondsSinceEpoch (); auto ts = i2p::util::GetSecondsSinceEpoch ();
std::unique_lock<std::mutex> l(m_PeersMutex); std::unique_lock<std::mutex> l(m_PeersMutex);
m_Peers.insert (std::make_pair (ident, Peer{ nullptr, ts })); auto it = m_Peers.insert (std::make_pair (ident, Peer{ nullptr, ts })).first;
it->second.sessions.push_back (session);
} }
}); });
} }
@@ -822,7 +823,9 @@ namespace transport
std::unique_lock<std::mutex> l(m_PeersMutex); std::unique_lock<std::mutex> l(m_PeersMutex);
auto it = m_Peers.begin (); auto it = m_Peers.begin ();
std::advance (it, rand () % m_Peers.size ()); std::advance (it, rand () % m_Peers.size ());
if (it == m_Peers.end () || it->second.router) return nullptr; // not connected if (it == m_Peers.end () || it->second.router || it->second.sessions.empty () ||
it->second.sessions.front ()->GetSendQueueSize () > PEER_ROUTER_INFO_OVERLOAD_QUEUE_SIZE)
return nullptr; // not connected or overloaded
ident = it->first; ident = it->first;
} }
return i2p::data::netdb.FindRouter (ident); return i2p::data::netdb.FindRouter (ident);

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2022, The PurpleI2P Project * Copyright (c) 2013-2023, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@@ -63,6 +63,7 @@ namespace transport
const int PEER_ROUTER_INFO_UPDATE_INTERVAL = 31*60; // in seconds const int PEER_ROUTER_INFO_UPDATE_INTERVAL = 31*60; // in seconds
const int PEER_ROUTER_INFO_UPDATE_INTERVAL_VARIANCE = 7*60; // in seconds const int PEER_ROUTER_INFO_UPDATE_INTERVAL_VARIANCE = 7*60; // in seconds
const size_t PEER_ROUTER_INFO_OVERLOAD_QUEUE_SIZE = 25;
struct Peer struct Peer
{ {
int numAttempts; int numAttempts;
@@ -169,8 +170,7 @@ namespace transport
std::thread * m_Thread; std::thread * m_Thread;
boost::asio::io_service * m_Service; boost::asio::io_service * m_Service;
boost::asio::io_service::work * m_Work; boost::asio::io_service::work * m_Work;
boost::asio::deadline_timer * m_PeerCleanupTimer, * m_PeerTestTimer; boost::asio::deadline_timer * m_PeerCleanupTimer, * m_PeerTestTimer, * m_UpdateBandwidthTimer;
boost::asio::steady_timer * m_UpdateBandwidthTimer;
SSU2Server * m_SSU2Server; SSU2Server * m_SSU2Server;
NTCP2Server * m_NTCP2Server; NTCP2Server * m_NTCP2Server;

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2022, The PurpleI2P Project * Copyright (c) 2013-2023, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@@ -332,8 +332,7 @@ namespace tunnel
Tunnels tunnels; Tunnels tunnels;
Tunnels::Tunnels (): m_IsRunning (false), m_Thread (nullptr), Tunnels::Tunnels (): m_IsRunning (false), m_Thread (nullptr),
m_NumSuccesiveTunnelCreations (0), m_NumFailedTunnelCreations (0) m_TunnelCreationSuccessRate (TCSR_START_VALUE), m_TunnelCreationAttemptsNum(0) {
{
} }
Tunnels::~Tunnels () Tunnels::~Tunnels ()
@@ -433,12 +432,16 @@ namespace tunnel
} }
} }
void Tunnels::AddTransitTunnel (std::shared_ptr<TransitTunnel> tunnel) bool Tunnels::AddTransitTunnel (std::shared_ptr<TransitTunnel> tunnel)
{ {
if (m_Tunnels.emplace (tunnel->GetTunnelID (), tunnel).second) if (m_Tunnels.emplace (tunnel->GetTunnelID (), tunnel).second)
m_TransitTunnels.push_back (tunnel); m_TransitTunnels.push_back (tunnel);
else else
{
LogPrint (eLogError, "Tunnel: Tunnel with id ", tunnel->GetTunnelID (), " already exists"); LogPrint (eLogError, "Tunnel: Tunnel with id ", tunnel->GetTunnelID (), " already exists");
return false;
}
return true;
} }
void Tunnels::Start () void Tunnels::Start ()
@@ -472,6 +475,7 @@ namespace tunnel
auto msg = m_Queue.GetNextWithTimeout (1000); // 1 sec auto msg = m_Queue.GetNextWithTimeout (1000); // 1 sec
if (msg) if (msg)
{ {
int numMsgs = 0;
uint32_t prevTunnelID = 0, tunnelID = 0; uint32_t prevTunnelID = 0, tunnelID = 0;
std::shared_ptr<TunnelBase> prevTunnel; std::shared_ptr<TunnelBase> prevTunnel;
do do
@@ -515,11 +519,12 @@ namespace tunnel
LogPrint (eLogWarning, "Tunnel: Unexpected message type ", (int) typeID); LogPrint (eLogWarning, "Tunnel: Unexpected message type ", (int) typeID);
} }
msg = m_Queue.Get (); msg = (numMsgs <= MAX_TUNNEL_MSGS_BATCH_SIZE) ? m_Queue.Get () : nullptr;
if (msg) if (msg)
{ {
prevTunnelID = tunnelID; prevTunnelID = tunnelID;
prevTunnel = tunnel; prevTunnel = tunnel;
numMsgs++;
} }
else if (tunnel) else if (tunnel)
tunnel->FlushTunnelDataMsgs (); tunnel->FlushTunnelDataMsgs ();
@@ -530,17 +535,17 @@ namespace tunnel
if (i2p::transport::transports.IsOnline()) if (i2p::transport::transports.IsOnline())
{ {
uint64_t ts = i2p::util::GetSecondsSinceEpoch (); uint64_t ts = i2p::util::GetSecondsSinceEpoch ();
if (ts - lastTs >= 15) // manage tunnels every 15 seconds if (ts - lastTs >= TUNNEL_MANAGE_INTERVAL) // manage tunnels every 15 seconds
{ {
ManageTunnels (); ManageTunnels ();
lastTs = ts; lastTs = ts;
} }
if (ts - lastPoolsTs >= 5) // manage pools every 5 seconds if (ts - lastPoolsTs >= TUNNEL_POOLS_MANAGE_INTERVAL) // manage pools every 5 seconds
{ {
ManageTunnelPools (ts); ManageTunnelPools (ts);
lastPoolsTs = ts; lastPoolsTs = ts;
} }
if (ts - lastMemoryPoolTs >= 120) // manage memory pool every 2 minutes if (ts - lastMemoryPoolTs >= TUNNEL_MEMORY_POOL_MANAGE_INTERVAL) // manage memory pool every 2 minutes
{ {
m_I2NPTunnelEndpointMessagesMemoryPool.CleanUpMt (); m_I2NPTunnelEndpointMessagesMemoryPool.CleanUpMt ();
m_I2NPTunnelMessagesMemoryPool.CleanUpMt (); m_I2NPTunnelMessagesMemoryPool.CleanUpMt ();
@@ -628,7 +633,7 @@ namespace tunnel
} }
// delete // delete
it = pendingTunnels.erase (it); it = pendingTunnels.erase (it);
m_NumFailedTunnelCreations++; FailedTunnelCreation();
} }
else else
++it; ++it;
@@ -636,7 +641,7 @@ namespace tunnel
case eTunnelStateBuildFailed: case eTunnelStateBuildFailed:
LogPrint (eLogDebug, "Tunnel: Pending build request ", it->first, " failed, deleted"); LogPrint (eLogDebug, "Tunnel: Pending build request ", it->first, " failed, deleted");
it = pendingTunnels.erase (it); it = pendingTunnels.erase (it);
m_NumFailedTunnelCreations++; FailedTunnelCreation();
break; break;
case eTunnelStateBuildReplyReceived: case eTunnelStateBuildReplyReceived:
// intermediate state, will be either established of build failed // intermediate state, will be either established of build failed
@@ -645,7 +650,7 @@ namespace tunnel
default: default:
// success // success
it = pendingTunnels.erase (it); it = pendingTunnels.erase (it);
m_NumSuccesiveTunnelCreations++; SuccesiveTunnelCreation();
} }
} }
} }

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2022, The PurpleI2P Project * Copyright (c) 2013-2023, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@@ -40,10 +40,17 @@ namespace tunnel
const int STANDARD_NUM_RECORDS = 4; // in VariableTunnelBuild message const int STANDARD_NUM_RECORDS = 4; // in VariableTunnelBuild message
const int MAX_NUM_RECORDS = 8; const int MAX_NUM_RECORDS = 8;
const int HIGH_LATENCY_PER_HOP = 250; // in milliseconds const int HIGH_LATENCY_PER_HOP = 250; // in milliseconds
const int MAX_TUNNEL_MSGS_BATCH_SIZE = 100; // handle messages without interrupt
const int TUNNEL_MANAGE_INTERVAL = 15; // in seconds
const int TUNNEL_POOLS_MANAGE_INTERVAL = 5; // in seconds
const int TUNNEL_MEMORY_POOL_MANAGE_INTERVAL = 120; // in seconds
const size_t I2NP_TUNNEL_MESSAGE_SIZE = TUNNEL_DATA_MSG_SIZE + I2NP_HEADER_SIZE + 34; // reserved for alignment and NTCP 16 + 6 + 12 const size_t I2NP_TUNNEL_MESSAGE_SIZE = TUNNEL_DATA_MSG_SIZE + I2NP_HEADER_SIZE + 34; // reserved for alignment and NTCP 16 + 6 + 12
const size_t I2NP_TUNNEL_ENPOINT_MESSAGE_SIZE = 2*TUNNEL_DATA_MSG_SIZE + I2NP_HEADER_SIZE + TUNNEL_GATEWAY_HEADER_SIZE + 28; // reserved for alignment and NTCP 16 + 6 + 6 const size_t I2NP_TUNNEL_ENPOINT_MESSAGE_SIZE = 2*TUNNEL_DATA_MSG_SIZE + I2NP_HEADER_SIZE + TUNNEL_GATEWAY_HEADER_SIZE + 28; // reserved for alignment and NTCP 16 + 6 + 6
const double TCSR_SMOOTHING_CONSTANT = 0.0005; // smoothing constant in exponentially weighted moving average
const double TCSR_START_VALUE = 0.1; // start value of tunnel creation success rate
enum TunnelState enum TunnelState
{ {
eTunnelStatePending, eTunnelStatePending,
@@ -206,7 +213,7 @@ namespace tunnel
std::shared_ptr<TunnelPool> GetExploratoryPool () const { return m_ExploratoryPool; }; std::shared_ptr<TunnelPool> GetExploratoryPool () const { return m_ExploratoryPool; };
std::shared_ptr<TunnelBase> GetTunnel (uint32_t tunnelID); std::shared_ptr<TunnelBase> GetTunnel (uint32_t tunnelID);
int GetTransitTunnelsExpirationTimeout (); int GetTransitTunnelsExpirationTimeout ();
void AddTransitTunnel (std::shared_ptr<TransitTunnel> tunnel); bool AddTransitTunnel (std::shared_ptr<TransitTunnel> tunnel);
void AddOutboundTunnel (std::shared_ptr<OutboundTunnel> newTunnel); void AddOutboundTunnel (std::shared_ptr<OutboundTunnel> newTunnel);
void AddInboundTunnel (std::shared_ptr<InboundTunnel> newTunnel); void AddInboundTunnel (std::shared_ptr<InboundTunnel> newTunnel);
std::shared_ptr<InboundTunnel> CreateInboundTunnel (std::shared_ptr<TunnelConfig> config, std::shared_ptr<TunnelPool> pool, std::shared_ptr<OutboundTunnel> outboundTunnel); std::shared_ptr<InboundTunnel> CreateInboundTunnel (std::shared_ptr<TunnelConfig> config, std::shared_ptr<TunnelPool> pool, std::shared_ptr<OutboundTunnel> outboundTunnel);
@@ -263,8 +270,19 @@ namespace tunnel
i2p::util::MemoryPoolMt<I2NPMessageBuffer<I2NP_TUNNEL_ENPOINT_MESSAGE_SIZE> > m_I2NPTunnelEndpointMessagesMemoryPool; i2p::util::MemoryPoolMt<I2NPMessageBuffer<I2NP_TUNNEL_ENPOINT_MESSAGE_SIZE> > m_I2NPTunnelEndpointMessagesMemoryPool;
i2p::util::MemoryPoolMt<I2NPMessageBuffer<I2NP_TUNNEL_MESSAGE_SIZE> > m_I2NPTunnelMessagesMemoryPool; i2p::util::MemoryPoolMt<I2NPMessageBuffer<I2NP_TUNNEL_MESSAGE_SIZE> > m_I2NPTunnelMessagesMemoryPool;
// some stats // Calculating of tunnel creation success rate
int m_NumSuccesiveTunnelCreations, m_NumFailedTunnelCreations; // A modified version of the EWMA algorithm, where alpha is increased at the beginning to accelerate similarity
void SuccesiveTunnelCreation() {
double alpha = TCSR_SMOOTHING_CONSTANT + (1 - TCSR_SMOOTHING_CONSTANT)/++m_TunnelCreationAttemptsNum;
m_TunnelCreationSuccessRate = alpha * 1 + (1 - alpha) * m_TunnelCreationSuccessRate;
};
void FailedTunnelCreation() {
double alpha = TCSR_SMOOTHING_CONSTANT + (1 - TCSR_SMOOTHING_CONSTANT)/++m_TunnelCreationAttemptsNum;
m_TunnelCreationSuccessRate = alpha * 0 + (1 - alpha) * m_TunnelCreationSuccessRate;
};
double m_TunnelCreationSuccessRate;
int m_TunnelCreationAttemptsNum;
public: public:
@@ -278,11 +296,7 @@ namespace tunnel
size_t CountOutboundTunnels() const; size_t CountOutboundTunnels() const;
int GetQueueSize () { return m_Queue.GetSize (); }; int GetQueueSize () { return m_Queue.GetSize (); };
int GetTunnelCreationSuccessRate () const // in percents int GetTunnelCreationSuccessRate () const { return std::round(m_TunnelCreationSuccessRate * 100); } // in percents
{
int totalNum = m_NumSuccesiveTunnelCreations + m_NumFailedTunnelCreations;
return totalNum ? m_NumSuccesiveTunnelCreations*100/totalNum : 0;
}
}; };
extern Tunnels tunnels; extern Tunnels tunnels;

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2020, The PurpleI2P Project * Copyright (c) 2013-2022, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@@ -41,7 +41,7 @@ namespace tunnel
{ {
public: public:
TunnelBase (uint32_t tunnelID, uint32_t nextTunnelID, i2p::data::IdentHash nextIdent): TunnelBase (uint32_t tunnelID, uint32_t nextTunnelID, const i2p::data::IdentHash& nextIdent):
m_TunnelID (tunnelID), m_NextTunnelID (nextTunnelID), m_NextIdent (nextIdent), m_TunnelID (tunnelID), m_NextTunnelID (nextTunnelID), m_NextIdent (nextIdent),
m_CreationTime (i2p::util::GetSecondsSinceEpoch ()) {}; m_CreationTime (i2p::util::GetSecondsSinceEpoch ()) {};
virtual ~TunnelBase () {}; virtual ~TunnelBase () {};

View File

@@ -282,8 +282,13 @@ namespace tunnel
for (const auto& it : m_OutboundTunnels) for (const auto& it : m_OutboundTunnels)
if (it->IsEstablished ()) num++; if (it->IsEstablished ()) num++;
} }
for (int i = num; i < m_NumOutboundTunnels; i++) num = m_NumOutboundTunnels - num;
CreateOutboundTunnel (); if (num > 0)
{
if (num > TUNNEL_POOL_MAX_NUM_BUILD_REQUESTS) num = TUNNEL_POOL_MAX_NUM_BUILD_REQUESTS;
for (int i = 0; i < num; i++)
CreateOutboundTunnel ();
}
num = 0; num = 0;
{ {
@@ -300,8 +305,13 @@ namespace tunnel
if (num >= m_NumInboundTunnels) break; if (num >= m_NumInboundTunnels) break;
} }
} }
for (int i = num; i < m_NumInboundTunnels; i++) num = m_NumInboundTunnels - num;
CreateInboundTunnel (); if (num > 0)
{
if (num > TUNNEL_POOL_MAX_NUM_BUILD_REQUESTS) num = TUNNEL_POOL_MAX_NUM_BUILD_REQUESTS;
for (int i = 0; i < num; i++)
CreateInboundTunnel ();
}
if (num < m_NumInboundTunnels && m_NumInboundHops <= 0 && m_LocalDestination) // zero hops IB if (num < m_NumInboundTunnels && m_NumInboundHops <= 0 && m_LocalDestination) // zero hops IB
m_LocalDestination->SetLeaseSetUpdated (); // update LeaseSet immediately m_LocalDestination->SetLeaseSetUpdated (); // update LeaseSet immediately

View File

@@ -30,6 +30,7 @@ namespace tunnel
const int TUNNEL_POOL_MANAGE_INTERVAL = 10; // in seconds const int TUNNEL_POOL_MANAGE_INTERVAL = 10; // in seconds
const int TUNNEL_POOL_MAX_INBOUND_TUNNELS_QUANTITY = 16; const int TUNNEL_POOL_MAX_INBOUND_TUNNELS_QUANTITY = 16;
const int TUNNEL_POOL_MAX_OUTBOUND_TUNNELS_QUANTITY = 16; const int TUNNEL_POOL_MAX_OUTBOUND_TUNNELS_QUANTITY = 16;
const int TUNNEL_POOL_MAX_NUM_BUILD_REQUESTS = 2;
class Tunnel; class Tunnel;
class InboundTunnel; class InboundTunnel;

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2022, The PurpleI2P Project * Copyright (c) 2013-2023, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@@ -16,8 +16,8 @@
#define MAKE_VERSION_NUMBER(a,b,c) ((a*100+b)*100+c) #define MAKE_VERSION_NUMBER(a,b,c) ((a*100+b)*100+c)
#define I2PD_VERSION_MAJOR 2 #define I2PD_VERSION_MAJOR 2
#define I2PD_VERSION_MINOR 44 #define I2PD_VERSION_MINOR 45
#define I2PD_VERSION_MICRO 0 #define I2PD_VERSION_MICRO 1
#define I2PD_VERSION_PATCH 0 #define I2PD_VERSION_PATCH 0
#ifdef GITVER #ifdef GITVER
#define I2PD_VERSION GITVER #define I2PD_VERSION GITVER
@@ -31,7 +31,7 @@
#define I2P_VERSION_MAJOR 0 #define I2P_VERSION_MAJOR 0
#define I2P_VERSION_MINOR 9 #define I2P_VERSION_MINOR 9
#define I2P_VERSION_MICRO 56 #define I2P_VERSION_MICRO 57
#define I2P_VERSION_PATCH 0 #define I2P_VERSION_PATCH 0
#define I2P_VERSION MAKE_VERSION(I2P_VERSION_MAJOR, I2P_VERSION_MINOR, I2P_VERSION_MICRO) #define I2P_VERSION MAKE_VERSION(I2P_VERSION_MAJOR, I2P_VERSION_MINOR, I2P_VERSION_MICRO)
#define I2P_VERSION_NUMBER MAKE_VERSION_NUMBER(I2P_VERSION_MAJOR, I2P_VERSION_MINOR, I2P_VERSION_MICRO) #define I2P_VERSION_NUMBER MAKE_VERSION_NUMBER(I2P_VERSION_MAJOR, I2P_VERSION_MINOR, I2P_VERSION_MICRO)

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2022, The PurpleI2P Project * Copyright (c) 2013-2023, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@@ -252,7 +252,6 @@ namespace proxy {
req.RemoveHeader("From"); req.RemoveHeader("From");
req.RemoveHeader("Forwarded"); req.RemoveHeader("Forwarded");
req.RemoveHeader("DNT"); // Useless DoNotTrack flag req.RemoveHeader("DNT"); // Useless DoNotTrack flag
req.RemoveHeader("X-Requested-With"); // Android Webview send this with the value set to the application ID
req.RemoveHeader("Accept", "Accept-Encoding"); // Accept*, but Accept-Encoding req.RemoveHeader("Accept", "Accept-Encoding"); // Accept*, but Accept-Encoding
/* drop proxy-disclosing headers */ /* drop proxy-disclosing headers */
req.RemoveHeader("X-Forwarded"); req.RemoveHeader("X-Forwarded");
@@ -260,6 +259,18 @@ namespace proxy {
/* replace headers */ /* replace headers */
req.UpdateHeader("User-Agent", "MYOB/6.66 (AN/ON)"); req.UpdateHeader("User-Agent", "MYOB/6.66 (AN/ON)");
/**
* i2pd PR #1816:
* Android Webview send this with the value set to the application ID, so we drop it,
* but only if it does not belong to an AJAX request (*HttpRequest, like XMLHttpRequest).
*/
if(req.GetHeader("X-Requested-With") != "") {
auto h = req.GetHeader ("X-Requested-With");
auto x = h.find("HttpRequest");
if (x == std::string::npos) // not found
req.RemoveHeader("X-Requested-With");
}
/** /**
* according to i2p ticket #1862: * according to i2p ticket #1862:
* leave Referer if requested URL with same schema, host and port, * leave Referer if requested URL with same schema, host and port,
@@ -310,7 +321,7 @@ namespace proxy {
if (!m_Addresshelper) if (!m_Addresshelper)
{ {
LogPrint(eLogWarning, "HTTPProxy: Addresshelper request rejected"); LogPrint(eLogWarning, "HTTPProxy: Addresshelper request rejected");
GenericProxyError(tr("Invalid request"), tr("addresshelper is not supported")); GenericProxyError(tr("Invalid request"), tr("Addresshelper is not supported"));
return true; return true;
} }
@@ -322,24 +333,51 @@ namespace proxy {
} }
else if (!i2p::client::context.GetAddressBook ().FindAddress (m_RequestURL.host) || m_Confirm) else if (!i2p::client::context.GetAddressBook ().FindAddress (m_RequestURL.host) || m_Confirm)
{ {
const std::string referer_raw = m_ClientRequest.GetHeader("Referer");
i2p::http::URL referer_url;
if (!referer_raw.empty ())
{
referer_url.parse (referer_raw);
}
if (m_RequestURL.host != referer_url.host)
{
if (m_Confirm) // Attempt to forced overwriting by link with "&update=true" from harmful URL
{
LogPrint (eLogWarning, "HTTPProxy: Address update from addresshelper rejected for ", m_RequestURL.host, " (referer is ", m_RequestURL.host.empty() ? "empty" : "harmful", ")");
std::string full_url = m_RequestURL.to_string();
std::stringstream ss;
ss << tr("Host %s is <font color=red>already in router's addressbook</font>. <b>Be careful: source of this URL may be harmful!</b> Click here to update record: <a href=\"%s%s%s&update=true\">Continue</a>.",
m_RequestURL.host.c_str(), full_url.c_str(), (full_url.find('?') != std::string::npos ? "&i2paddresshelper=" : "?i2paddresshelper="), jump.c_str());
GenericProxyInfo(tr("Addresshelper forced update rejected"), ss.str());
}
else // Preventing unauthorized additions to the address book
{
LogPrint (eLogDebug, "HTTPProxy: Adding address from addresshelper for ", m_RequestURL.host, " (generate refer-base page)");
std::string full_url = m_RequestURL.to_string();
std::stringstream ss;
ss << tr("To add host <b>%s</b> in router's addressbook, click here: <a href=\"%s%s%s\">Continue</a>.",
m_RequestURL.host.c_str(), full_url.c_str(), (full_url.find('?') != std::string::npos ? "&i2paddresshelper=" : "?i2paddresshelper="), jump.c_str());
GenericProxyInfo(tr("Addresshelper request"), ss.str());
}
return true; /* request processed */
}
i2p::client::context.GetAddressBook ().InsertAddress (m_RequestURL.host, jump); i2p::client::context.GetAddressBook ().InsertAddress (m_RequestURL.host, jump);
LogPrint (eLogInfo, "HTTPProxy: Added address from addresshelper for ", m_RequestURL.host); LogPrint (eLogInfo, "HTTPProxy: Added address from addresshelper for ", m_RequestURL.host);
std::string full_url = m_RequestURL.to_string(); std::string full_url = m_RequestURL.to_string();
std::stringstream ss; std::stringstream ss;
ss << tr("Host") <<" " << m_RequestURL.host << " " << tr("added to router's addressbook from helper") << ". "; ss << tr("Host %s added to router's addressbook from helper. Click here to proceed: <a href=\"%s\">Continue</a>.",
ss << tr("Click here to proceed:") << " <a href=\"" << full_url << "\">" << tr("Continue") << "</a>."; m_RequestURL.host.c_str(), full_url.c_str());
GenericProxyInfo(tr("Addresshelper found"), ss.str()); GenericProxyInfo(tr("Addresshelper adding"), ss.str());
return true; /* request processed */ return true; /* request processed */
} }
else else
{ {
std::string full_url = m_RequestURL.to_string(); std::string full_url = m_RequestURL.to_string();
std::stringstream ss; std::stringstream ss;
ss << tr("Host") << " " << m_RequestURL.host << " <font color=red>" << tr("already in router's addressbook") << "</font>. "; ss << tr("Host %s is <font color=red>already in router's addressbook</font>. Click here to update record: <a href=\"%s%s%s&update=true\">Continue</a>.",
ss << tr(/* tr: The "record" means addressbook's record. That message appears when domain was already added to addressbook, but helper link is opened for it. */ "Click here to update record:" ); m_RequestURL.host.c_str(), full_url.c_str(), (full_url.find('?') != std::string::npos ? "&i2paddresshelper=" : "?i2paddresshelper="), jump.c_str());
ss << " <a href=\"" << full_url << (full_url.find('?') != std::string::npos ? "&i2paddresshelper=" : "?i2paddresshelper="); GenericProxyInfo(tr("Addresshelper update"), ss.str());
ss << jump << "&update=true\">" << tr("Continue") << "</a>.";
GenericProxyInfo(tr("Addresshelper found"), ss.str());
return true; /* request processed */ return true; /* request processed */
} }
} }
@@ -352,7 +390,7 @@ namespace proxy {
auto pos = uri.find(":"); auto pos = uri.find(":");
if(pos == std::string::npos || pos == uri.size() - 1) if(pos == std::string::npos || pos == uri.size() - 1)
{ {
GenericProxyError(tr("Invalid request"), tr("invalid request uri")); GenericProxyError(tr("Invalid request"), tr("Invalid request URI"));
return true; return true;
} }
else else
@@ -412,10 +450,10 @@ namespace proxy {
if(m_ProxyURL.parse(m_OutproxyUrl)) if(m_ProxyURL.parse(m_OutproxyUrl))
ForwardToUpstreamProxy(); ForwardToUpstreamProxy();
else else
GenericProxyError(tr("Outproxy failure"), tr("bad outproxy settings")); GenericProxyError(tr("Outproxy failure"), tr("Bad outproxy settings"));
} else { } else {
LogPrint (eLogWarning, "HTTPProxy: Outproxy failure for ", dest_host, ": no outproxy enabled"); LogPrint (eLogWarning, "HTTPProxy: Outproxy failure for ", dest_host, ": no outproxy enabled");
std::stringstream ss; ss << tr("Host") << " " << dest_host << " " << tr("not inside I2P network, but outproxy is not enabled"); std::stringstream ss; ss << tr("Host %s is not inside I2P network, but outproxy is not enabled", dest_host.c_str());
GenericProxyError(tr("Outproxy failure"), ss.str()); GenericProxyError(tr("Outproxy failure"), ss.str());
} }
return true; return true;
@@ -504,13 +542,13 @@ namespace proxy {
else else
{ {
/* unknown type, complain */ /* unknown type, complain */
GenericProxyError(tr("unknown outproxy url"), m_ProxyURL.to_string()); GenericProxyError(tr("Unknown outproxy URL"), m_ProxyURL.to_string());
} }
} }
void HTTPReqHandler::HandleUpstreamProxyResolved(const boost::system::error_code & ec, boost::asio::ip::tcp::resolver::iterator it, ProxyResolvedHandler handler) void HTTPReqHandler::HandleUpstreamProxyResolved(const boost::system::error_code & ec, boost::asio::ip::tcp::resolver::iterator it, ProxyResolvedHandler handler)
{ {
if(ec) GenericProxyError(tr("cannot resolve upstream proxy"), ec.message()); if(ec) GenericProxyError(tr("Cannot resolve upstream proxy"), ec.message());
else handler(*it); else handler(*it);
} }
@@ -518,7 +556,7 @@ namespace proxy {
{ {
if(!ec) { if(!ec) {
if(m_RequestURL.host.size() > 255) { if(m_RequestURL.host.size() > 255) {
GenericProxyError(tr("hostname too long"), m_RequestURL.host); GenericProxyError(tr("Hostname is too long"), m_RequestURL.host);
return; return;
} }
uint16_t port = m_RequestURL.port; uint16_t port = m_RequestURL.port;
@@ -545,13 +583,13 @@ namespace proxy {
reqsize += host.size(); reqsize += host.size();
m_socks_buf[++reqsize] = 0; m_socks_buf[++reqsize] = 0;
boost::asio::async_write(*m_proxysock, boost::asio::buffer(m_socks_buf, reqsize), boost::asio::transfer_all(), std::bind(&HTTPReqHandler::HandleSocksProxySendHandshake, this, std::placeholders::_1, std::placeholders::_2)); boost::asio::async_write(*m_proxysock, boost::asio::buffer(m_socks_buf, reqsize), boost::asio::transfer_all(), std::bind(&HTTPReqHandler::HandleSocksProxySendHandshake, this, std::placeholders::_1, std::placeholders::_2));
} else GenericProxyError(tr("cannot connect to upstream socks proxy"), ec.message()); } else GenericProxyError(tr("Cannot connect to upstream SOCKS proxy"), ec.message());
} }
void HTTPReqHandler::HandleSocksProxySendHandshake(const boost::system::error_code & ec, std::size_t bytes_transferred) void HTTPReqHandler::HandleSocksProxySendHandshake(const boost::system::error_code & ec, std::size_t bytes_transferred)
{ {
LogPrint(eLogDebug, "HTTPProxy: Upstream SOCKS handshake sent"); LogPrint(eLogDebug, "HTTPProxy: Upstream SOCKS handshake sent");
if(ec) GenericProxyError(tr("Cannot negotiate with socks proxy"), ec.message()); if(ec) GenericProxyError(tr("Cannot negotiate with SOCKS proxy"), ec.message());
else m_proxysock->async_read_some(boost::asio::buffer(m_socks_buf, 8), std::bind(&HTTPReqHandler::HandleSocksProxyReply, this, std::placeholders::_1, std::placeholders::_2)); else m_proxysock->async_read_some(boost::asio::buffer(m_socks_buf, 8), std::bind(&HTTPReqHandler::HandleSocksProxyReply, this, std::placeholders::_1, std::placeholders::_2));
} }
@@ -593,7 +631,7 @@ namespace proxy {
} }
else else
{ {
GenericProxyError(tr("CONNECT error"), tr("Failed to Connect")); GenericProxyError(tr("CONNECT error"), tr("Failed to connect"));
} }
} }
@@ -604,7 +642,7 @@ namespace proxy {
m_send_buf = m_ClientResponse.to_string(); m_send_buf = m_ClientResponse.to_string();
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) 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)
{ {
if(ec) GenericProxyError(tr("socks proxy error"), ec.message()); if(ec) GenericProxyError(tr("SOCKS proxy error"), ec.message());
else HandoverToUpstreamProxy(); else HandoverToUpstreamProxy();
}); });
} else { } else {
@@ -612,7 +650,7 @@ namespace proxy {
LogPrint(eLogDebug, "HTTPProxy: Send ", m_send_buf.size(), " bytes"); LogPrint(eLogDebug, "HTTPProxy: Send ", m_send_buf.size(), " bytes");
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) 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)
{ {
if(ec) GenericProxyError(tr("failed to send request to upstream"), ec.message()); if(ec) GenericProxyError(tr("Failed to send request to upstream"), ec.message());
else HandoverToUpstreamProxy(); else HandoverToUpstreamProxy();
}); });
} }
@@ -630,18 +668,18 @@ namespace proxy {
ss << "error code: "; ss << "error code: ";
ss << (int) m_socks_buf[1]; ss << (int) m_socks_buf[1];
std::string msg = ss.str(); std::string msg = ss.str();
GenericProxyError(tr("socks proxy error"), msg); GenericProxyError(tr("SOCKS proxy error"), msg);
} }
} }
else GenericProxyError(tr("No Reply From socks proxy"), ec.message()); else GenericProxyError(tr("No reply from SOCKS proxy"), ec.message());
} }
void HTTPReqHandler::HandleUpstreamHTTPProxyConnect(const boost::system::error_code & ec) void HTTPReqHandler::HandleUpstreamHTTPProxyConnect(const boost::system::error_code & ec)
{ {
if(!ec) { if(!ec) {
LogPrint(eLogDebug, "HTTPProxy: Connected to http upstream"); LogPrint(eLogDebug, "HTTPProxy: Connected to http upstream");
GenericProxyError(tr("cannot connect"), tr("http out proxy not implemented")); GenericProxyError(tr("Cannot connect"), tr("HTTP out proxy not implemented"));
} else GenericProxyError(tr("cannot connect to upstream http proxy"), ec.message()); } else GenericProxyError(tr("Cannot connect to upstream HTTP proxy"), ec.message());
} }
/* will be called after some data received from client */ /* will be called after some data received from client */

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2022, The PurpleI2P Project * Copyright (c) 2013-2023, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@@ -440,18 +440,23 @@ namespace client
{ {
if (ecode != boost::asio::error::operation_aborted) if (ecode != boost::asio::error::operation_aborted)
{ {
auto session = m_Owner.FindSession(m_ID); if (m_Socket.is_open ())
if(session)
{ {
if (session->GetLocalDestination ()->IsReady ()) auto session = m_Owner.FindSession(m_ID);
SendSessionCreateReplyOk (); if(session)
else
{ {
m_Timer.expires_from_now (boost::posix_time::seconds(SAM_SESSION_READINESS_CHECK_INTERVAL)); if (session->GetLocalDestination ()->IsReady ())
m_Timer.async_wait (std::bind (&SAMSocket::HandleSessionReadinessCheckTimer, SendSessionCreateReplyOk ();
shared_from_this (), std::placeholders::_1)); else
{
m_Timer.expires_from_now (boost::posix_time::seconds(SAM_SESSION_READINESS_CHECK_INTERVAL));
m_Timer.async_wait (std::bind (&SAMSocket::HandleSessionReadinessCheckTimer,
shared_from_this (), std::placeholders::_1));
}
} }
} }
else
Terminate ("SAM: session socket closed");
} }
} }

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2021, The PurpleI2P Project * Copyright (c) 2013-2023, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@@ -30,7 +30,7 @@ namespace client
{ {
const size_t SAM_SOCKET_BUFFER_SIZE = 8192; const size_t SAM_SOCKET_BUFFER_SIZE = 8192;
const int SAM_SOCKET_CONNECTION_MAX_IDLE = 3600; // in seconds const int SAM_SOCKET_CONNECTION_MAX_IDLE = 3600; // in seconds
const int SAM_SESSION_READINESS_CHECK_INTERVAL = 20; // in seconds const int SAM_SESSION_READINESS_CHECK_INTERVAL = 3; // in seconds
const char SAM_HANDSHAKE[] = "HELLO VERSION"; const char SAM_HANDSHAKE[] = "HELLO VERSION";
const char SAM_HANDSHAKE_REPLY[] = "HELLO REPLY RESULT=OK VERSION=%s\n"; const char SAM_HANDSHAKE_REPLY[] = "HELLO REPLY RESULT=OK VERSION=%s\n";
const char SAM_HANDSHAKE_NOVERSION[] = "HELLO REPLY RESULT=NOVERSION\n"; const char SAM_HANDSHAKE_NOVERSION[] = "HELLO REPLY RESULT=NOVERSION\n";

View File

@@ -27,7 +27,7 @@ namespace proxy
static const size_t socks_buffer_size = 8192; static const size_t socks_buffer_size = 8192;
static const size_t max_socks_hostname_size = 255; // Limit for socks5 and bad idea to traverse static const size_t max_socks_hostname_size = 255; // Limit for socks5 and bad idea to traverse
static const size_t SOCKS_FORWARDER_BUFFER_SIZE = 8192; //static const size_t SOCKS_FORWARDER_BUFFER_SIZE = 8192;
static const size_t SOCKS_UPSTREAM_SOCKS4A_REPLY_SIZE = 8; static const size_t SOCKS_UPSTREAM_SOCKS4A_REPLY_SIZE = 8;

138
tests/CMakeLists.txt Normal file
View File

@@ -0,0 +1,138 @@
enable_testing()
find_package(Check 0.9.10 REQUIRED)
include_directories(${CHECK_INCLUDE_DIRS})
# Compiler flags:
if(APPLE)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wno-unused-parameter -Wextra -pedantic -O0 -g -Wl,-undefined,dynamic_lookup")
else()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wno-unused-parameter -Wextra -pedantic -O0 -g -D_GLIBCXX_USE_NANOSLEEP=1 -Wl,--unresolved-symbols=ignore-in-object-files")
endif()
set(TEST_PATH ${CMAKE_CURRENT_BINARY_DIR})
include_directories(
../libi2pd
${Boost_INCLUDE_DIRS}
${OPENSSL_INCLUDE_DIR}
)
set(test-http-merge_chunked_SRCS
../libi2pd/HTTP.cpp
test-http-merge_chunked.cpp
)
set(test-http-req_SRCS
../libi2pd/HTTP.cpp
test-http-req.cpp
)
set(test-http-res_SRCS
../libi2pd/HTTP.cpp
test-http-res.cpp
)
set(test-http-url_decode_SRCS
../libi2pd/HTTP.cpp
test-http-url_decode.cpp
)
set(test-http-url_SRCS
../libi2pd/HTTP.cpp
test-http-url.cpp
)
set(test-base-64_SRCS
../libi2pd/Base.cpp
test-base-64.cpp
)
set(test-gost_SRCS
../libi2pd/Gost.cpp
../libi2pd/I2PEndian.cpp
test-gost.cpp
)
set(test-gost-sig_SRCS
../libi2pd/Gost.cpp
../libi2pd/I2PEndian.cpp
../libi2pd/Crypto.cpp
../libi2pd/Log.cpp
test-gost-sig.cpp
)
set(test-x25519_SRCS
../libi2pd/Ed25519.cpp
../libi2pd/I2PEndian.cpp
../libi2pd/Log.cpp
../libi2pd/Crypto.cpp
test-x25519.cpp
)
set(test-aeadchacha20poly1305_SRCS
../libi2pd/Crypto.cpp
../libi2pd/ChaCha20.cpp
../libi2pd/Poly1305.cpp
test-aeadchacha20poly1305.cpp
)
set(test-blinding_SRCS
../libi2pd/Crypto.cpp
../libi2pd/Blinding.cpp
../libi2pd/Ed25519.cpp
../libi2pd/I2PEndian.cpp
../libi2pd/Log.cpp
../libi2pd/util.cpp
../libi2pd/Identity.cpp
../libi2pd/Signature.cpp
../libi2pd/Timestamp.cpp
test-blinding.cpp
)
SET(test-elligator_SRCS
../libi2pd/Elligator.cpp
../libi2pd/Crypto.cpp
test-elligator.cpp
)
add_executable(test-http-merge_chunked ${test-http-merge_chunked_SRCS})
add_executable(test-http-req ${test-http-req_SRCS})
add_executable(test-http-res ${test-http-res_SRCS})
add_executable(test-http-url_decode ${test-http-url_decode_SRCS})
add_executable(test-http-url ${test-http-url_SRCS})
add_executable(test-base-64 ${test-base-64_SRCS})
add_executable(test-gost ${test-gost_SRCS})
add_executable(test-gost-sig ${test-gost-sig_SRCS})
add_executable(test-x25519 ${test-x25519_SRCS})
add_executable(test-aeadchacha20poly1305 ${test-aeadchacha20poly1305_SRCS})
add_executable(test-blinding ${test-blinding_SRCS})
add_executable(test-elligator ${test-elligator_SRCS})
set(LIBS
${Boost_LIBRARIES}
${CHECK_LDFLAGS}
${CMAKE_REQUIRED_LIBRARIES}
OpenSSL::SSL
OpenSSL::Crypto
Threads::Threads
)
target_link_libraries(test-gost OpenSSL::Crypto Threads::Threads)
target_link_libraries(test-gost-sig ${LIBS})
target_link_libraries(test-x25519 ${LIBS})
target_link_libraries(test-aeadchacha20poly1305 ${LIBS})
target_link_libraries(test-blinding ${LIBS})
target_link_libraries(test-elligator ${LIBS})
add_test(test-http-merge_chunked ${TEST_PATH}/test-http-merge_chunked)
add_test(test-http-req ${TEST_PATH}/test-http-req)
add_test(test-http-res ${TEST_PATH}/test-http-res)
add_test(test-http-url_decode ${TEST_PATH}/test-http-url_decode)
add_test(test-http-url ${TEST_PATH}/test-http-url)
add_test(test-base-64 ${TEST_PATH}/test-base-64)
add_test(test-gost ${TEST_PATH}/test-gost)
add_test(test-gost-sig ${TEST_PATH}/test-gost-sig)
add_test(test-x25519 ${TEST_PATH}/test-x25519)
add_test(test-aeadchacha20poly1305 ${TEST_PATH}/test-aeadchacha20poly1305)
add_test(test-blinding ${TEST_PATH}/test-blinding)
add_test(test-elligator ${TEST_PATH}/test-elligator)

View File

@@ -17,7 +17,7 @@ test-gost: ../libi2pd/Gost.cpp ../libi2pd/I2PEndian.cpp test-gost.cpp
test-gost-sig: ../libi2pd/Gost.cpp ../libi2pd/I2PEndian.cpp ../libi2pd/Crypto.cpp ../libi2pd/Log.cpp test-gost-sig.cpp test-gost-sig: ../libi2pd/Gost.cpp ../libi2pd/I2PEndian.cpp ../libi2pd/Crypto.cpp ../libi2pd/Log.cpp test-gost-sig.cpp
$(CXX) $(CXXFLAGS) $(NEEDED_CXXFLAGS) $(INCFLAGS) -o $@ $^ -lcrypto -lssl -lboost_system $(CXX) $(CXXFLAGS) $(NEEDED_CXXFLAGS) $(INCFLAGS) -o $@ $^ -lcrypto -lssl -lboost_system
test-x25519: ../libi2pd/Ed25519.cpp ../libi2pd/I2PEndian.cpp ../libi2pd/Log.cpp ../libi2pd/Crypto.cpp test-x25519.cpp test-x25519: ../libi2pd/Ed25519.cpp ../libi2pd/I2PEndian.cpp ../libi2pd/Log.cpp ../libi2pd/Crypto.cpp test-x25519.cpp
$(CXX) $(CXXFLAGS) $(NEEDED_CXXFLAGS) $(INCFLAGS) -o $@ $^ -lcrypto -lssl -lboost_system $(CXX) $(CXXFLAGS) $(NEEDED_CXXFLAGS) $(INCFLAGS) -o $@ $^ -lcrypto -lssl -lboost_system
test-aeadchacha20poly1305: ../libi2pd/Crypto.cpp ../libi2pd/ChaCha20.cpp ../libi2pd/Poly1305.cpp test-aeadchacha20poly1305.cpp test-aeadchacha20poly1305: ../libi2pd/Crypto.cpp ../libi2pd/ChaCha20.cpp ../libi2pd/Poly1305.cpp test-aeadchacha20poly1305.cpp

View File

@@ -1,5 +1,5 @@
#include <cassert> #include <cassert>
#include "../HTTP.h" #include "HTTP.h"
using namespace i2p::http; using namespace i2p::http;

View File

@@ -1,5 +1,5 @@
#include <cassert> #include <cassert>
#include "../HTTP.h" #include "HTTP.h"
using namespace i2p::http; using namespace i2p::http;

View File

@@ -1,5 +1,5 @@
#include <cassert> #include <cassert>
#include "../HTTP.h" #include "HTTP.h"
using namespace i2p::http; using namespace i2p::http;

View File

@@ -1,5 +1,5 @@
#include <cassert> #include <cassert>
#include "../HTTP.h" #include "HTTP.h"
using namespace i2p::http; using namespace i2p::http;

View File

@@ -1,5 +1,5 @@
#include <cassert> #include <cassert>
#include "../HTTP.h" #include "HTTP.h"
using namespace i2p::http; using namespace i2p::http;