Compare commits

..

132 Commits

Author SHA1 Message Date
R4SAS
79b97ef2f7 2.43.0
Signed-off-by: R4SAS <r4sas@i2pmail.org>
2022-08-21 22:40:41 +03:00
R4SAS
e45d68ad3a [i18n] pull translations from Crowdin
Signed-off-by: R4SAS <r4sas@i2pmail.org>
2022-08-21 22:38:40 +03:00
orignal
b40f1b67b9 2.43.0 2022-08-21 14:52:55 -04:00
r4sas
4fa7e43162 disable ssu in example config file
Signed-off-by: r4sas <r4sas@i2pmail.org>
2022-08-19 19:38:54 +00:00
r4sas
66fcbcae96 add ntcp2 and ssu2 options in example config
Signed-off-by: r4sas <r4sas@i2pmail.org>
2022-08-19 19:27:34 +00:00
orignal
7f0845dfd3 reset acceptor on stop of server tunnel 2022-08-16 14:06:13 -04:00
orignal
f875823357 copy path challenge to response 2022-08-15 15:32:55 -04:00
orignal
75611866eb update router's transports when SSU or NTCP address was deleted 2022-08-14 10:43:16 -04:00
orignal
c3dd7ed73a try to resend if window is full 2022-08-12 18:56:58 -04:00
orignal
3ae885d120 change status back to Testing from Unknow if next test was accepted 2022-08-12 16:12:30 -04:00
orignal
81f53d313c alsways set some port to SSU2 address 2022-08-11 20:16:08 -04:00
R4SAS
d10c86b849 [rpm] fix fedora build
Signed-off-by: R4SAS <r4sas@i2pmail.org>
2022-08-11 21:06:22 +03:00
orignal
9d123fa5ad select random port if port not found or specified 2022-08-10 22:00:11 -04:00
orignal
f4d6a08d57 create separate addresses for published SSU2 2022-08-10 15:50:30 -04:00
orignal
e9e641afbe check if datagram destination exists before sending 2022-08-10 11:28:59 -04:00
orignal
8f5768f85b memory pool for leases 2022-08-09 19:40:07 -04:00
orignal
3dd78a2589 remove SSU address if SSU is off 2022-08-09 19:12:11 -04:00
orignal
df92a85159 set SSU2 port +1 if not specified 2022-08-09 14:08:13 -04:00
orignal
ab606a1121 adjust clock from SSU2 2022-08-08 19:57:48 -04:00
orignal
457b3cf168 disable ElGamal table if no SSU 2022-08-08 17:53:02 -04:00
orignal
c6f898b8ca connect to Charlie if RelayResponse from Bob was received before HolePunch 2022-08-08 13:08:12 -04:00
orignal
b9970e1908 cleanup introducers upon reschedule 2022-08-07 09:50:30 -04:00
orignal
8bb9a57908 re-insert introducer back 2022-08-06 20:05:08 -04:00
orignal
53934a470b update keys for NTCP2 and SSU2 addreses 2022-08-06 16:30:49 -04:00
orignal
a94ae7d77d update keys for NTCP2 and SSU2 addreses 2022-08-06 16:25:46 -04:00
orignal
f43e860998 cleanup introducers if router is not longer firewalled 2022-08-05 21:23:23 -04:00
orignal
3e40852999 memory pool for sent packets 2022-08-04 18:13:44 -04:00
orignal
df073bb306 send local address in RelayResponse block 2022-08-04 15:15:19 -04:00
orignal
771c4a0d02 allocate smaller I2NP buffer for fragmented message. Limit number of fragments by 64 2022-08-03 16:06:07 -04:00
orignal
cb959ab14c allocate tunnel message buffer for I2NP block with tunnel data message type 2022-08-03 10:26:55 -04:00
R4SAS
34b75dac02 change language file comment
Signed-off-by: R4SAS <r4sas@i2pmail.org>
2022-08-03 11:50:32 +03:00
R4SAS
fbb590d9a9 [i18n] add simplified chinese translation (thanks to sklhioq)
Signed-off-by: R4SAS <r4sas@i2pmail.org>
2022-08-03 11:48:42 +03:00
orignal
ed5c533982 recgnize symmetric NAT from SSU2 2022-08-02 20:02:55 -04:00
orignal
98d2ce5845 Respond to path challenge. Correct termination reason for duplicated session 2022-08-02 13:35:18 -04:00
orignal
9d9d5e3e5d show ports for tranport links 2022-07-31 09:47:04 -04:00
orignal
eba4626589 kappa for RTO culculation 2022-07-31 09:45:18 -04:00
orignal
ff5fa1d137 3 bytes off for token in RelayResponse 2022-07-30 18:50:43 -04:00
orignal
71766ecd16 select introducers randomly. More logging for RelayIntro 2022-07-30 16:31:44 -04:00
orignal
fc63ca6982 correct excluded routers size for exploratory request 2022-07-30 14:28:09 -04:00
orignal
0e6d888ed3 changed some retransmission params 2022-07-29 18:45:02 -04:00
orignal
9afe3b5f39 fixed typo 2022-07-29 18:27:21 -04:00
orignal
3bd40fc8b3 calculate RTT and RTO 2022-07-29 15:24:24 -04:00
orignal
01fe642beb don't create another session for peer test 2022-07-29 12:48:23 -04:00
orignal
e70d57dcb4 resend intervals in milliseconds 2022-07-28 19:30:08 -04:00
orignal
fd41fba069 variable retranmission window 2022-07-27 20:00:03 -04:00
orignal
8a6fe0f321 check if address type matches peer's address type for peer test msg 1 2022-07-27 10:55:08 -04:00
orignal
ae73e8a305 find SSU2 address with static key if supports both ipv4 and ipv6 2022-07-27 10:19:25 -04:00
R4SAS
a344c09d0d [util] add inet_ntop for XP
Signed-off-by: R4SAS <r4sas@i2pmail.org>
2022-07-27 15:44:30 +03:00
R4SAS
991e37d0bf [peertest] fixed ssu2 router exclusion
Signed-off-by: R4SAS <r4sas@i2pmail.org>
2022-07-27 13:38:28 +03:00
R4SAS
fdeb884fe5 fixed getting MTU on windows, add address to log messages with MTU
Signed-off-by: R4SAS <r4sas@i2pmail.org>
2022-07-27 13:24:07 +03:00
orignal
4b1f5c9c9b terminate session in separate task 2022-07-26 19:56:30 -04:00
orignal
6b513a0f95 Merge branch 'openssl' of https://github.com/PurpleI2P/i2pd into openssl 2022-07-26 18:47:36 -04:00
orignal
b574aaf99c fix SSU2 crash on shutdown 2022-07-26 18:46:05 -04:00
R4SAS
bc0cdaa669 [i18n] update gettext file, add translation context, change comments
Signed-off-by: R4SAS <r4sas@i2pmail.org>
2022-07-27 00:38:02 +03:00
orignal
f9106b77bb add SSU2 introducer if SSU2 only 2022-07-26 13:57:37 -04:00
orignal
a0419e4f34 add SSU2 introducer if SSU2 only 2022-07-26 13:55:31 -04:00
orignal
46a549c875 random size of fragments 2022-07-26 13:00:41 -04:00
orignal
f8a609f692 respond to termination 2022-07-25 18:46:25 -04:00
orignal
987497bb10 don't publish invalid host/port 2022-07-25 15:23:52 -04:00
orignal
e537878b8a check Ack block bufer size and shrink ranges if necessary 2022-07-25 13:42:59 -04:00
orignal
617f45bc59 try to send I2NP message in one packet, reduce or drop Ack block if necessary 2022-07-24 19:44:49 -04:00
orignal
fe744f8f81 more routine cleanup 2022-07-24 16:44:02 -04:00
orignal
93d879b297 more tunnel brokers ranges 2022-07-24 15:39:46 -04:00
orignal
dbb9295063 set MTU if local address is specified explicitly. update MTU for ipv6 if not set 2022-07-24 15:24:01 -04:00
orignal
09aa96e486 always bring to closing state if termination requested 2022-07-23 19:48:37 -04:00
orignal
4d0047ae7c request termination for existing session 2022-07-23 18:48:53 -04:00
orignal
b860a4799d testing cap for published SSU2 address 2022-07-23 16:17:30 -04:00
orignal
6ff64352d3 don't create and oublish duplicates 2022-07-23 14:32:16 -04:00
orignal
3683ec6a95 fixed race condition 2022-07-22 15:16:42 -04:00
orignal
454fa9ee9b update SSU2 port 2022-07-22 14:52:24 -04:00
orignal
d33aeb4bb2 set unreachable if firewalled. Store router's hash of introducer instead session 2022-07-21 19:38:18 -04:00
Gecero-Sensei
5f9f23eb3f Added translation of webconsole site title 2022-07-21 23:53:49 +03:00
Gecero-Sensei
5dbc7a8ca4 Minor corrections and wording changes 2022-07-21 23:53:41 +03:00
Gecero-Sensei
33a5968eb7 Improved German translation 2022-07-21 23:53:31 +03:00
R4SAS
5ff34b93c0 print detected MTU
Signed-off-by: R4SAS <r4sas@i2pmail.org>
2022-07-21 23:53:12 +03:00
R4SAS
098fdf0596 [gha] update freebsd action 2022-07-21 23:36:51 +03:00
orignal
2eb929fe05 Merge pull request #1776 from simonvetter/openssl
leaseset: add missing bound checks
2022-07-21 16:23:48 -04:00
orignal
ea0ed9e844 update SSU2 introducers if Firewalled 2022-07-20 21:55:48 -04:00
orignal
4a3e481a83 don't publish introducers for non-published SSU2 address 2022-07-20 16:13:00 -04:00
orignal
2197cd8620 add/remove SSU2 introducers to local RouterInfo 2022-07-20 16:01:08 -04:00
orignal
cf0d3b5f61 create new list of SSU2 introducers 2022-07-19 18:38:58 -04:00
orignal
6f7ab49346 moved creation time to TransportSession 2022-07-19 17:02:37 -04:00
orignal
000e0358a7 resend SessionConfirmed immediately if another SessionCreated received 2022-07-19 16:09:16 -04:00
orignal
a3e19931f0 insert RouterInfo from SessionConfirmed into netdb immediately 2022-07-19 14:06:00 -04:00
orignal
9fec1a86cf send ack for peer test 2022-07-18 19:58:19 -04:00
orignal
ffab29890b created additional ranges if acnt > 255 2022-07-17 15:22:41 -04:00
orignal
206c068d8e don't send termination without address 2022-07-17 07:44:11 -04:00
orignal
dc30cd1112 handle SessionConfirmed fragments in reversed order 2022-07-16 16:08:55 -04:00
Simon Vetter
412a245e88 leaseset: add missing bound checks
This builds on ChadF's issue and patch (https://github.com/PurpleI2P/i2pd/issues/1772)
and fixes other potential bound check issues.
2022-07-16 18:00:20 +02:00
orignal
16290bf66f fixed race condition on session termination 2022-07-15 18:22:18 -04:00
orignal
4f8b0e6484 send more SessionConfirmed termination messages. Limit send queue 2022-07-15 15:01:46 -04:00
orignal
5026dbc1b3 receive bigger packets 2022-07-14 20:12:27 -04:00
orignal
014e4b0e1d detect MTU for some known ipv6 tunnel brokers 2022-07-14 13:48:28 -04:00
orignal
14a6947b02 round MTU to multiple of 16 for SSU1 2022-07-14 07:58:55 -04:00
orignal
665a914dc3 set max MTU for ipv4 2022-07-13 20:08:57 -04:00
orignal
8feac310af start initial peer test if SSU2 only 2022-07-13 19:56:55 -04:00
orignal
3394bb4b8d calculate SSU2 session MTU and max payload size 2022-07-13 19:35:18 -04:00
orignal
1dd2bd0013 publish MTU for ipv6 SSU2 address. Max MTU of 1488 for SSU1 2022-07-13 15:52:19 -04:00
orignal
5c62726992 check clock skew and terminate 2022-07-13 12:45:20 -04:00
orignal
90981f628e Send fragmented SessionConfirmed 2022-07-12 19:04:03 -04:00
orignal
0c34189d94 correct buffer size for fragments of SessionConfirmed 2022-07-12 12:17:58 -04:00
orignal
f1d3d6a7b5 set max compression for SessionConfirmed 2022-07-12 10:50:21 -04:00
orignal
b0d962b49a send ack for retransmitted SessionConfirmed 2022-07-11 19:00:23 -04:00
orignal
c50e453af6 check out of sequence messages range 2022-07-11 18:16:05 -04:00
orignal
efbaf02016 Merge pull request #1774 from simonvetter/openssl
fix SSU2 introducers selection logic
2022-07-11 09:48:21 -04:00
Simon Vetter
3cf809e99d fix SSU2 introducers selection logic 2022-07-11 08:16:07 +00:00
orignal
8b649aaaf8 NACKs and Acks only Ack ranges 2022-07-10 18:50:02 -04:00
orignal
fdebbc4498 select sessions for introducers 2022-07-10 17:13:25 -04:00
orignal
3ff3417ff2 send termiation with reason 2022-07-09 17:05:23 -04:00
orignal
bb6227281a teminate session after 5 unacked resends 2022-07-08 21:31:44 -04:00
orignal
2f44d99a74 session closing state 2022-07-08 19:06:09 -04:00
orignal
ca4414d15a request relay tag if firewalled 2022-07-08 13:52:09 -04:00
orignal
fbb961b43c extract correct endpoint from peer test msg 2 2022-07-07 13:23:51 -04:00
orignal
fa9c174264 handle first packet from Bob 2022-07-06 21:28:53 -04:00
orignal
83f43ab166 pick 3 routers for SSU2 peer test 2022-07-06 19:33:02 -04:00
orignal
f7e9e6a1c4 set status OK after both peer test msg 4 and 5 2022-07-06 13:35:04 -04:00
orignal
aa21748e9a set status OK after peer test msg 5 2022-07-06 12:41:51 -04:00
orignal
a2f4e08b00 set testing status for SSU2 peer test 2022-07-05 19:38:24 -04:00
orignal
66bc29d075 insert received RouterInfo into netdb immediately 2022-07-05 19:15:50 -04:00
orignal
e3eebe537b set correct port for unpublished SSU2 addresses 2022-07-05 14:00:30 -04:00
orignal
3ed625f949 don't try SSU peer test if SSU is disabled 2022-07-05 13:07:23 -04:00
orignal
a1e414c3b7 make SSU2 server eligible for peer test 2022-07-05 12:55:11 -04:00
r4sas
a5a35b1fa6 [daemon] check for SSU2 transport at start
Signed-off-by: r4sas <r4sas@i2pmail.org>
2022-07-05 06:11:23 +00:00
orignal
2a24584d45 set SSU2 port if not specified 2022-07-04 23:00:16 -04:00
orignal
6039cdceb0 correct SSU2 only detection 2022-07-04 20:01:45 -04:00
orignal
473159be0f don't use port from SSU2 address 2022-07-04 19:32:43 -04:00
orignal
0e6ad548b2 invoke SSU2 peer test updates 2022-07-04 18:54:20 -04:00
orignal
6143515ac6 update our IP adress from SSU2 2022-07-03 09:31:20 -04:00
orignal
50419f200d fixed 1 packet off for out of sequence clean up 2022-07-01 17:35:38 -04:00
orignal
455390f121 clean up first out of sequence packet if too many 2022-07-01 10:52:10 -04:00
orignal
d375299fa9 send token in relay response block 2022-06-30 20:00:18 -04:00
orignal
28db337166 give priority to SSU2 over SSU 2022-06-30 12:53:50 -04:00
36 changed files with 2471 additions and 823 deletions

View File

@@ -4,13 +4,13 @@ on: [push, pull_request]
jobs:
build:
runs-on: macos-10.15
runs-on: macos-12
name: with UPnP
steps:
- uses: actions/checkout@v2
- name: Test in FreeBSD
id: test
uses: vmactions/freebsd-vm@v0.1.5
uses: vmactions/freebsd-vm@v0.2.0
with:
usesh: true
mem: 2048

View File

@@ -1,6 +1,36 @@
# for this file format description,
# see https://github.com/olivierlacan/keep-a-changelog
## [2.43.0] - 2022-08-22
### Added
- Complete SSU2 implementation
- Localization to Chinese
- Send RouterInfo update for long live sessions
- Explicit ipv6 ranges of known tunnel brokers for MTU detection
- Always send "Connection: close" and strip out Keep-Alive for server HTTP tunnel
- Show ports for all transports in web console
- Translation of webconsole site title
- Support for Windows ProgramData path when running as service
- Ability to turn off address book
- Handle signals TSTP and CONT to stop and resume network
### Changed
- Case insensitive headers for server HTTP tunnel
- Do not show 'Address registration' line if LeaseSet is encrypted
- SSU2 transports have higher priority than SSU
- Disable ElGamal precalculated table if no SSU
- Deprecate limits.ntcpsoft, limits.ntcphard and limits.ntcpthreads config options
- SSU2 is enabled and SSU is disabled by default for new installations
### Fixed
- Typo with Referer header name in HTTP proxy
- Can't handle garlic message from an exploratory tunnel
- Incorrect encryption key for exploratory lookup reply
- Bound checks issues in LeaseSets code
- MTU detection on Windows
- Crash on stop of active server tunnel
- Send datagram to wrong destination in SAM
- Incorrect static key in RouterInfo if the keys were regenerated
- Duplicated sessions in BOB
## [2.42.1] - 2022-05-24
### Fixed
- Incorrect jump link in HTTP Proxy

View File

@@ -1,13 +1,13 @@
# i2pd
# Copyright (C) 2021 PurpleI2P team
# Copyright (C) 2021-2022 PurpleI2P team
# This file is distributed under the same license as the i2pd package.
# R4SAS <r4sas@i2pmail.org>, 2021.
# R4SAS <r4sas@i2pmail.org>, 2021-2022.
#
msgid ""
msgstr ""
"Project-Id-Version: i2pd\n"
"Report-Msgid-Bugs-To: https://github.com/PurpleI2P/i2pd/issues\n"
"POT-Creation-Date: 2021-08-06 17:12\n"
"POT-Creation-Date: 2022-07-26 21:22\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
@@ -18,706 +18,712 @@ msgstr ""
"X-Poedit-SearchPath-0: daemon/HTTPServer.cpp\n"
"X-Poedit-SearchPath-1: libi2pd_client/HTTPProxy.cpp\n"
#: daemon/HTTPServer.cpp:177
#: daemon/HTTPServer.cpp:108
msgid "day"
msgid_plural "days"
msgstr[0] ""
msgstr[1] ""
#: daemon/HTTPServer.cpp:181
#: daemon/HTTPServer.cpp:112
msgid "hour"
msgid_plural "hours"
msgstr[0] ""
msgstr[1] ""
#: daemon/HTTPServer.cpp:185
#: daemon/HTTPServer.cpp:116
msgid "minute"
msgid_plural "minutes"
msgstr[0] ""
msgstr[1] ""
#: daemon/HTTPServer.cpp:188
#: daemon/HTTPServer.cpp:119
msgid "second"
msgid_plural "seconds"
msgstr[0] ""
msgstr[1] ""
#. tr: Kibibit
#: daemon/HTTPServer.cpp:196 daemon/HTTPServer.cpp:224
#: daemon/HTTPServer.cpp:127 daemon/HTTPServer.cpp:155
msgid "KiB"
msgstr ""
#. tr: Mebibit
#: daemon/HTTPServer.cpp:198
#: daemon/HTTPServer.cpp:129
msgid "MiB"
msgstr ""
#. tr: Gibibit
#: daemon/HTTPServer.cpp:200
#: daemon/HTTPServer.cpp:131
msgid "GiB"
msgstr ""
#: daemon/HTTPServer.cpp:217
#: daemon/HTTPServer.cpp:148
msgid "building"
msgstr ""
#: daemon/HTTPServer.cpp:218
#: daemon/HTTPServer.cpp:149
msgid "failed"
msgstr ""
#: daemon/HTTPServer.cpp:219
#: daemon/HTTPServer.cpp:150
msgid "expiring"
msgstr ""
#: daemon/HTTPServer.cpp:220
#: daemon/HTTPServer.cpp:151
msgid "established"
msgstr ""
#: daemon/HTTPServer.cpp:221
#: daemon/HTTPServer.cpp:152
msgid "unknown"
msgstr ""
#: daemon/HTTPServer.cpp:223
#: daemon/HTTPServer.cpp:154
msgid "exploratory"
msgstr ""
#: daemon/HTTPServer.cpp:259
#. tr: Webconsole page title
#: daemon/HTTPServer.cpp:185
msgid "Purple I2P Webconsole"
msgstr ""
#: daemon/HTTPServer.cpp:190
msgid "<b>i2pd</b> webconsole"
msgstr ""
#: daemon/HTTPServer.cpp:262
#: daemon/HTTPServer.cpp:193
msgid "Main page"
msgstr ""
#: daemon/HTTPServer.cpp:263 daemon/HTTPServer.cpp:725
#: daemon/HTTPServer.cpp:194 daemon/HTTPServer.cpp:700
msgid "Router commands"
msgstr ""
#: daemon/HTTPServer.cpp:264 daemon/HTTPServer.cpp:448
#: daemon/HTTPServer.cpp:460
#: daemon/HTTPServer.cpp:195 daemon/HTTPServer.cpp:382
#: daemon/HTTPServer.cpp:394
msgid "Local Destinations"
msgstr ""
#: daemon/HTTPServer.cpp:266 daemon/HTTPServer.cpp:418
#: daemon/HTTPServer.cpp:504 daemon/HTTPServer.cpp:510
#: daemon/HTTPServer.cpp:641 daemon/HTTPServer.cpp:684
#: daemon/HTTPServer.cpp:688
#: daemon/HTTPServer.cpp:197 daemon/HTTPServer.cpp:352
#: daemon/HTTPServer.cpp:438 daemon/HTTPServer.cpp:444
#: daemon/HTTPServer.cpp:597 daemon/HTTPServer.cpp:640
#: daemon/HTTPServer.cpp:644
msgid "LeaseSets"
msgstr ""
#: daemon/HTTPServer.cpp:268 daemon/HTTPServer.cpp:694
#: daemon/HTTPServer.cpp:199 daemon/HTTPServer.cpp:650
msgid "Tunnels"
msgstr ""
#: daemon/HTTPServer.cpp:269 daemon/HTTPServer.cpp:425
#: daemon/HTTPServer.cpp:787 daemon/HTTPServer.cpp:803
#: daemon/HTTPServer.cpp:201 daemon/HTTPServer.cpp:359
#: daemon/HTTPServer.cpp:770 daemon/HTTPServer.cpp:786
msgid "Transit Tunnels"
msgstr ""
#: daemon/HTTPServer.cpp:270 daemon/HTTPServer.cpp:852
#: daemon/HTTPServer.cpp:203 daemon/HTTPServer.cpp:839
msgid "Transports"
msgstr ""
#: daemon/HTTPServer.cpp:271
#: daemon/HTTPServer.cpp:204
msgid "I2P tunnels"
msgstr ""
#: daemon/HTTPServer.cpp:273 daemon/HTTPServer.cpp:914
#: daemon/HTTPServer.cpp:924
#: daemon/HTTPServer.cpp:206 daemon/HTTPServer.cpp:908
#: daemon/HTTPServer.cpp:918
msgid "SAM sessions"
msgstr ""
#: daemon/HTTPServer.cpp:289 daemon/HTTPServer.cpp:1306
#: daemon/HTTPServer.cpp:1309 daemon/HTTPServer.cpp:1312
#: daemon/HTTPServer.cpp:1326 daemon/HTTPServer.cpp:1371
#: daemon/HTTPServer.cpp:1374 daemon/HTTPServer.cpp:1377
#: daemon/HTTPServer.cpp:222 daemon/HTTPServer.cpp:1302
#: daemon/HTTPServer.cpp:1305 daemon/HTTPServer.cpp:1308
#: daemon/HTTPServer.cpp:1322 daemon/HTTPServer.cpp:1367
#: daemon/HTTPServer.cpp:1370 daemon/HTTPServer.cpp:1373
msgid "ERROR"
msgstr ""
#: daemon/HTTPServer.cpp:296
#: daemon/HTTPServer.cpp:229
msgid "OK"
msgstr ""
#: daemon/HTTPServer.cpp:297
#: daemon/HTTPServer.cpp:230
msgid "Testing"
msgstr ""
#: daemon/HTTPServer.cpp:298
#: daemon/HTTPServer.cpp:231
msgid "Firewalled"
msgstr ""
#: daemon/HTTPServer.cpp:299 daemon/HTTPServer.cpp:320
#: daemon/HTTPServer.cpp:406
#: daemon/HTTPServer.cpp:232 daemon/HTTPServer.cpp:253
#: daemon/HTTPServer.cpp:325
msgid "Unknown"
msgstr ""
#: daemon/HTTPServer.cpp:300 daemon/HTTPServer.cpp:435
#: daemon/HTTPServer.cpp:436 daemon/HTTPServer.cpp:982
#: daemon/HTTPServer.cpp:991
#: daemon/HTTPServer.cpp:233 daemon/HTTPServer.cpp:369
#: daemon/HTTPServer.cpp:370 daemon/HTTPServer.cpp:976
#: daemon/HTTPServer.cpp:985
msgid "Proxy"
msgstr ""
#: daemon/HTTPServer.cpp:301
#: daemon/HTTPServer.cpp:234
msgid "Mesh"
msgstr ""
#: daemon/HTTPServer.cpp:304
#: daemon/HTTPServer.cpp:237
msgid "Error"
msgstr ""
#: daemon/HTTPServer.cpp:308
#: daemon/HTTPServer.cpp:241
msgid "Clock skew"
msgstr ""
#: daemon/HTTPServer.cpp:311
#: daemon/HTTPServer.cpp:244
msgid "Offline"
msgstr ""
#: daemon/HTTPServer.cpp:314
#: daemon/HTTPServer.cpp:247
msgid "Symmetric NAT"
msgstr ""
#: daemon/HTTPServer.cpp:326
#: daemon/HTTPServer.cpp:259
msgid "Uptime"
msgstr ""
#: daemon/HTTPServer.cpp:329
#: daemon/HTTPServer.cpp:262
msgid "Network status"
msgstr ""
#: daemon/HTTPServer.cpp:334
#: daemon/HTTPServer.cpp:267
msgid "Network status v6"
msgstr ""
#: daemon/HTTPServer.cpp:340 daemon/HTTPServer.cpp:347
#: daemon/HTTPServer.cpp:273 daemon/HTTPServer.cpp:280
msgid "Stopping in"
msgstr ""
#: daemon/HTTPServer.cpp:354
#: daemon/HTTPServer.cpp:287
msgid "Family"
msgstr ""
#: daemon/HTTPServer.cpp:355
#: daemon/HTTPServer.cpp:288
msgid "Tunnel creation success rate"
msgstr ""
#: daemon/HTTPServer.cpp:356
#: daemon/HTTPServer.cpp:289
msgid "Received"
msgstr ""
#. tr: Kibibit/s
#: daemon/HTTPServer.cpp:358 daemon/HTTPServer.cpp:361
#: daemon/HTTPServer.cpp:364
#: daemon/HTTPServer.cpp:291 daemon/HTTPServer.cpp:294
#: daemon/HTTPServer.cpp:297
msgid "KiB/s"
msgstr ""
#: daemon/HTTPServer.cpp:359
#: daemon/HTTPServer.cpp:292
msgid "Sent"
msgstr ""
#: daemon/HTTPServer.cpp:362
#: daemon/HTTPServer.cpp:295
msgid "Transit"
msgstr ""
#: daemon/HTTPServer.cpp:365
#: daemon/HTTPServer.cpp:298
msgid "Data path"
msgstr ""
#: daemon/HTTPServer.cpp:368
#: daemon/HTTPServer.cpp:301
msgid "Hidden content. Press on text to see."
msgstr ""
#: daemon/HTTPServer.cpp:371
#: daemon/HTTPServer.cpp:304
msgid "Router Ident"
msgstr ""
#: daemon/HTTPServer.cpp:373
#: daemon/HTTPServer.cpp:306
msgid "Router Family"
msgstr ""
#: daemon/HTTPServer.cpp:374
#: daemon/HTTPServer.cpp:307
msgid "Router Caps"
msgstr ""
#: daemon/HTTPServer.cpp:375
#: daemon/HTTPServer.cpp:308
msgid "Version"
msgstr ""
#: daemon/HTTPServer.cpp:376
#: daemon/HTTPServer.cpp:309
msgid "Our external address"
msgstr ""
#: daemon/HTTPServer.cpp:384
#: daemon/HTTPServer.cpp:337
msgid "supported"
msgstr ""
#: daemon/HTTPServer.cpp:416
#: daemon/HTTPServer.cpp:350
msgid "Routers"
msgstr ""
#: daemon/HTTPServer.cpp:417
#: daemon/HTTPServer.cpp:351
msgid "Floodfills"
msgstr ""
#: daemon/HTTPServer.cpp:424 daemon/HTTPServer.cpp:968
#: daemon/HTTPServer.cpp:358 daemon/HTTPServer.cpp:962
msgid "Client Tunnels"
msgstr ""
#: daemon/HTTPServer.cpp:434
#: daemon/HTTPServer.cpp:368
msgid "Services"
msgstr ""
#: daemon/HTTPServer.cpp:435 daemon/HTTPServer.cpp:436
#: daemon/HTTPServer.cpp:437 daemon/HTTPServer.cpp:438
#: daemon/HTTPServer.cpp:439 daemon/HTTPServer.cpp:440
#: daemon/HTTPServer.cpp:369 daemon/HTTPServer.cpp:370
#: daemon/HTTPServer.cpp:371 daemon/HTTPServer.cpp:372
#: daemon/HTTPServer.cpp:373 daemon/HTTPServer.cpp:374
msgid "Enabled"
msgstr ""
#: daemon/HTTPServer.cpp:435 daemon/HTTPServer.cpp:436
#: daemon/HTTPServer.cpp:437 daemon/HTTPServer.cpp:438
#: daemon/HTTPServer.cpp:439 daemon/HTTPServer.cpp:440
#: daemon/HTTPServer.cpp:369 daemon/HTTPServer.cpp:370
#: daemon/HTTPServer.cpp:371 daemon/HTTPServer.cpp:372
#: daemon/HTTPServer.cpp:373 daemon/HTTPServer.cpp:374
msgid "Disabled"
msgstr ""
#: daemon/HTTPServer.cpp:483
#: daemon/HTTPServer.cpp:417
msgid "Encrypted B33 address"
msgstr ""
#: daemon/HTTPServer.cpp:492
#: daemon/HTTPServer.cpp:426
msgid "Address registration line"
msgstr ""
#: daemon/HTTPServer.cpp:497
#: daemon/HTTPServer.cpp:431
msgid "Domain"
msgstr ""
#: daemon/HTTPServer.cpp:498
#: daemon/HTTPServer.cpp:432
msgid "Generate"
msgstr ""
#: daemon/HTTPServer.cpp:499
#: daemon/HTTPServer.cpp:433
msgid ""
"<b>Note:</b> result string can be used only for registering 2LD domains "
"(example.i2p). For registering subdomains please use i2pd-tools."
msgstr ""
#: daemon/HTTPServer.cpp:505
#: daemon/HTTPServer.cpp:439
msgid "Address"
msgstr ""
#: daemon/HTTPServer.cpp:505
#: daemon/HTTPServer.cpp:439
msgid "Type"
msgstr ""
#: daemon/HTTPServer.cpp:505
#: daemon/HTTPServer.cpp:439
msgid "EncType"
msgstr ""
#: daemon/HTTPServer.cpp:515 daemon/HTTPServer.cpp:699
#: daemon/HTTPServer.cpp:449 daemon/HTTPServer.cpp:655
msgid "Inbound tunnels"
msgstr ""
#. tr: Milliseconds
#: daemon/HTTPServer.cpp:520 daemon/HTTPServer.cpp:530
#: daemon/HTTPServer.cpp:704 daemon/HTTPServer.cpp:714
#: daemon/HTTPServer.cpp:464 daemon/HTTPServer.cpp:484
#: daemon/HTTPServer.cpp:669 daemon/HTTPServer.cpp:689
msgid "ms"
msgstr ""
#: daemon/HTTPServer.cpp:525 daemon/HTTPServer.cpp:709
#: daemon/HTTPServer.cpp:469 daemon/HTTPServer.cpp:674
msgid "Outbound tunnels"
msgstr ""
#: daemon/HTTPServer.cpp:537
#: daemon/HTTPServer.cpp:491
msgid "Tags"
msgstr ""
#: daemon/HTTPServer.cpp:537
#: daemon/HTTPServer.cpp:491
msgid "Incoming"
msgstr ""
#: daemon/HTTPServer.cpp:544 daemon/HTTPServer.cpp:547
#: daemon/HTTPServer.cpp:498 daemon/HTTPServer.cpp:501
msgid "Outgoing"
msgstr ""
#: daemon/HTTPServer.cpp:545 daemon/HTTPServer.cpp:561
#: daemon/HTTPServer.cpp:499 daemon/HTTPServer.cpp:515
msgid "Destination"
msgstr ""
#: daemon/HTTPServer.cpp:545
#: daemon/HTTPServer.cpp:499
msgid "Amount"
msgstr ""
#: daemon/HTTPServer.cpp:552
#: daemon/HTTPServer.cpp:506
msgid "Incoming Tags"
msgstr ""
#: daemon/HTTPServer.cpp:560 daemon/HTTPServer.cpp:563
#: daemon/HTTPServer.cpp:514 daemon/HTTPServer.cpp:517
msgid "Tags sessions"
msgstr ""
#: daemon/HTTPServer.cpp:561
#: daemon/HTTPServer.cpp:515
msgid "Status"
msgstr ""
#: daemon/HTTPServer.cpp:570 daemon/HTTPServer.cpp:626
#: daemon/HTTPServer.cpp:524 daemon/HTTPServer.cpp:582
msgid "Local Destination"
msgstr ""
#: daemon/HTTPServer.cpp:580 daemon/HTTPServer.cpp:947
#: daemon/HTTPServer.cpp:535 daemon/HTTPServer.cpp:941
msgid "Streams"
msgstr ""
#: daemon/HTTPServer.cpp:602
#: daemon/HTTPServer.cpp:558
msgid "Close stream"
msgstr ""
#: daemon/HTTPServer.cpp:631
#: daemon/HTTPServer.cpp:587
msgid "I2CP session not found"
msgstr ""
#: daemon/HTTPServer.cpp:634
#: daemon/HTTPServer.cpp:590
msgid "I2CP is not enabled"
msgstr ""
#: daemon/HTTPServer.cpp:660
#: daemon/HTTPServer.cpp:616
msgid "Invalid"
msgstr ""
#: daemon/HTTPServer.cpp:663
#: daemon/HTTPServer.cpp:619
msgid "Store type"
msgstr ""
#: daemon/HTTPServer.cpp:664
#: daemon/HTTPServer.cpp:620
msgid "Expires"
msgstr ""
#: daemon/HTTPServer.cpp:669
#: daemon/HTTPServer.cpp:625
msgid "Non Expired Leases"
msgstr ""
#: daemon/HTTPServer.cpp:672
#: daemon/HTTPServer.cpp:628
msgid "Gateway"
msgstr ""
#: daemon/HTTPServer.cpp:673
#: daemon/HTTPServer.cpp:629
msgid "TunnelID"
msgstr ""
#: daemon/HTTPServer.cpp:674
#: daemon/HTTPServer.cpp:630
msgid "EndDate"
msgstr ""
#: daemon/HTTPServer.cpp:684
#: daemon/HTTPServer.cpp:640
msgid "not floodfill"
msgstr ""
#: daemon/HTTPServer.cpp:695
#: daemon/HTTPServer.cpp:651
msgid "Queue size"
msgstr ""
#: daemon/HTTPServer.cpp:726
#: daemon/HTTPServer.cpp:701
msgid "Run peer test"
msgstr ""
#: daemon/HTTPServer.cpp:731
#: daemon/HTTPServer.cpp:706
msgid "Decline transit tunnels"
msgstr ""
#: daemon/HTTPServer.cpp:733
#: daemon/HTTPServer.cpp:708
msgid "Accept transit tunnels"
msgstr ""
#: daemon/HTTPServer.cpp:737 daemon/HTTPServer.cpp:742
#: daemon/HTTPServer.cpp:712 daemon/HTTPServer.cpp:717
msgid "Cancel graceful shutdown"
msgstr ""
#: daemon/HTTPServer.cpp:739 daemon/HTTPServer.cpp:744
#: daemon/HTTPServer.cpp:714 daemon/HTTPServer.cpp:719
msgid "Start graceful shutdown"
msgstr ""
#: daemon/HTTPServer.cpp:747
#: daemon/HTTPServer.cpp:722
msgid "Force shutdown"
msgstr ""
#: daemon/HTTPServer.cpp:748
#: daemon/HTTPServer.cpp:723
msgid "Reload external CSS styles"
msgstr ""
#: daemon/HTTPServer.cpp:751
#: daemon/HTTPServer.cpp:726
msgid ""
"<b>Note:</b> any action done here are not persistent and not changes your "
"config files."
msgstr ""
#: daemon/HTTPServer.cpp:753
#: daemon/HTTPServer.cpp:728
msgid "Logging level"
msgstr ""
#: daemon/HTTPServer.cpp:761
#: daemon/HTTPServer.cpp:736
msgid "Transit tunnels limit"
msgstr ""
#: daemon/HTTPServer.cpp:766 daemon/HTTPServer.cpp:778
#: daemon/HTTPServer.cpp:741 daemon/HTTPServer.cpp:760
msgid "Change"
msgstr ""
#: daemon/HTTPServer.cpp:770
#: daemon/HTTPServer.cpp:748
msgid "Change language"
msgstr ""
#: daemon/HTTPServer.cpp:803
#: daemon/HTTPServer.cpp:786
msgid "no transit tunnels currently built"
msgstr ""
#: daemon/HTTPServer.cpp:908 daemon/HTTPServer.cpp:931
#: daemon/HTTPServer.cpp:902 daemon/HTTPServer.cpp:925
msgid "SAM disabled"
msgstr ""
#: daemon/HTTPServer.cpp:924
#: daemon/HTTPServer.cpp:918
msgid "no sessions currently running"
msgstr ""
#: daemon/HTTPServer.cpp:937
#: daemon/HTTPServer.cpp:931
msgid "SAM session not found"
msgstr ""
#: daemon/HTTPServer.cpp:942
#: daemon/HTTPServer.cpp:936
msgid "SAM Session"
msgstr ""
#: daemon/HTTPServer.cpp:999
#: daemon/HTTPServer.cpp:993
msgid "Server Tunnels"
msgstr ""
#: daemon/HTTPServer.cpp:1015
#: daemon/HTTPServer.cpp:1009
msgid "Client Forwards"
msgstr ""
#: daemon/HTTPServer.cpp:1029
#: daemon/HTTPServer.cpp:1023
msgid "Server Forwards"
msgstr ""
#: daemon/HTTPServer.cpp:1227
#: daemon/HTTPServer.cpp:1223
msgid "Unknown page"
msgstr ""
#: daemon/HTTPServer.cpp:1246
#: daemon/HTTPServer.cpp:1242
msgid "Invalid token"
msgstr ""
#: daemon/HTTPServer.cpp:1304 daemon/HTTPServer.cpp:1361
#: daemon/HTTPServer.cpp:1401
#: daemon/HTTPServer.cpp:1300 daemon/HTTPServer.cpp:1357
#: daemon/HTTPServer.cpp:1397
msgid "SUCCESS"
msgstr ""
#: daemon/HTTPServer.cpp:1304
#: daemon/HTTPServer.cpp:1300
msgid "Stream closed"
msgstr ""
#: daemon/HTTPServer.cpp:1306
#: daemon/HTTPServer.cpp:1302
msgid "Stream not found or already was closed"
msgstr ""
#: daemon/HTTPServer.cpp:1309
#: daemon/HTTPServer.cpp:1305
msgid "Destination not found"
msgstr ""
#: daemon/HTTPServer.cpp:1312
#: daemon/HTTPServer.cpp:1308
msgid "StreamID can't be null"
msgstr ""
#: daemon/HTTPServer.cpp:1314 daemon/HTTPServer.cpp:1379
#: daemon/HTTPServer.cpp:1310 daemon/HTTPServer.cpp:1375
msgid "Return to destination page"
msgstr ""
#: daemon/HTTPServer.cpp:1315 daemon/HTTPServer.cpp:1328
#: daemon/HTTPServer.cpp:1403
#: daemon/HTTPServer.cpp:1311 daemon/HTTPServer.cpp:1324
#: daemon/HTTPServer.cpp:1399
msgid "You will be redirected in 5 seconds"
msgstr ""
#: daemon/HTTPServer.cpp:1326
#: daemon/HTTPServer.cpp:1322
msgid "Transit tunnels count must not exceed 65535"
msgstr ""
#: daemon/HTTPServer.cpp:1327 daemon/HTTPServer.cpp:1402
#: daemon/HTTPServer.cpp:1323 daemon/HTTPServer.cpp:1398
msgid "Back to commands list"
msgstr ""
#: daemon/HTTPServer.cpp:1363
#: daemon/HTTPServer.cpp:1359
msgid "Register at reg.i2p"
msgstr ""
#: daemon/HTTPServer.cpp:1364
#: daemon/HTTPServer.cpp:1360
msgid "Description"
msgstr ""
#: daemon/HTTPServer.cpp:1364
#: daemon/HTTPServer.cpp:1360
msgid "A bit information about service on domain"
msgstr ""
#: daemon/HTTPServer.cpp:1365
#: daemon/HTTPServer.cpp:1361
msgid "Submit"
msgstr ""
#: daemon/HTTPServer.cpp:1371
#: daemon/HTTPServer.cpp:1367
msgid "Domain can't end with .b32.i2p"
msgstr ""
#: daemon/HTTPServer.cpp:1374
#: daemon/HTTPServer.cpp:1370
msgid "Domain must end with .i2p"
msgstr ""
#: daemon/HTTPServer.cpp:1377
#: daemon/HTTPServer.cpp:1373
msgid "Such destination is not found"
msgstr ""
#: daemon/HTTPServer.cpp:1397
#: daemon/HTTPServer.cpp:1393
msgid "Unknown command"
msgstr ""
#: daemon/HTTPServer.cpp:1401
#: daemon/HTTPServer.cpp:1397
msgid "Command accepted"
msgstr ""
#: libi2pd_client/HTTPProxy.cpp:157
#: libi2pd_client/HTTPProxy.cpp:163
msgid "Proxy error"
msgstr ""
#: libi2pd_client/HTTPProxy.cpp:165
#: libi2pd_client/HTTPProxy.cpp:171
msgid "Proxy info"
msgstr ""
#: libi2pd_client/HTTPProxy.cpp:173
#: libi2pd_client/HTTPProxy.cpp:179
msgid "Proxy error: Host not found"
msgstr ""
#: libi2pd_client/HTTPProxy.cpp:174
#: libi2pd_client/HTTPProxy.cpp:180
msgid "Remote host not found in router's addressbook"
msgstr ""
#: libi2pd_client/HTTPProxy.cpp:175
#: libi2pd_client/HTTPProxy.cpp:181
msgid "You may try to find this host on jump services below"
msgstr ""
#: libi2pd_client/HTTPProxy.cpp:273 libi2pd_client/HTTPProxy.cpp:288
#: libi2pd_client/HTTPProxy.cpp:322 libi2pd_client/HTTPProxy.cpp:365
#: libi2pd_client/HTTPProxy.cpp:282 libi2pd_client/HTTPProxy.cpp:297
#: libi2pd_client/HTTPProxy.cpp:331 libi2pd_client/HTTPProxy.cpp:372
msgid "Invalid request"
msgstr ""
#: libi2pd_client/HTTPProxy.cpp:273
#: libi2pd_client/HTTPProxy.cpp:282
msgid "Proxy unable to parse your request"
msgstr ""
#: libi2pd_client/HTTPProxy.cpp:288
#: libi2pd_client/HTTPProxy.cpp:297
msgid "addresshelper is not supported"
msgstr ""
#: libi2pd_client/HTTPProxy.cpp:297 libi2pd_client/HTTPProxy.cpp:306
#: libi2pd_client/HTTPProxy.cpp:385
#: libi2pd_client/HTTPProxy.cpp:306 libi2pd_client/HTTPProxy.cpp:315
#: libi2pd_client/HTTPProxy.cpp:392
msgid "Host"
msgstr ""
#: libi2pd_client/HTTPProxy.cpp:297
#: libi2pd_client/HTTPProxy.cpp:306
msgid "added to router's addressbook from helper"
msgstr ""
#: libi2pd_client/HTTPProxy.cpp:298
#: libi2pd_client/HTTPProxy.cpp:307
msgid "Click here to proceed:"
msgstr ""
#: libi2pd_client/HTTPProxy.cpp:298 libi2pd_client/HTTPProxy.cpp:308
#: libi2pd_client/HTTPProxy.cpp:307 libi2pd_client/HTTPProxy.cpp:317
msgid "Continue"
msgstr ""
#: libi2pd_client/HTTPProxy.cpp:299 libi2pd_client/HTTPProxy.cpp:309
#: libi2pd_client/HTTPProxy.cpp:308 libi2pd_client/HTTPProxy.cpp:318
msgid "Addresshelper found"
msgstr ""
#: libi2pd_client/HTTPProxy.cpp:306
#: libi2pd_client/HTTPProxy.cpp:315
msgid "already in router's addressbook"
msgstr ""
#: libi2pd_client/HTTPProxy.cpp:307
#. tr: The "record" means addressbook's record. That message appears when domain was already added to addressbook, but helper link is opened for it.
#: libi2pd_client/HTTPProxy.cpp:316
msgid "Click here to update record:"
msgstr ""
#: libi2pd_client/HTTPProxy.cpp:322
#: libi2pd_client/HTTPProxy.cpp:331
msgid "invalid request uri"
msgstr ""
#: libi2pd_client/HTTPProxy.cpp:365
#: libi2pd_client/HTTPProxy.cpp:372
msgid "Can't detect destination host from request"
msgstr ""
#: libi2pd_client/HTTPProxy.cpp:382 libi2pd_client/HTTPProxy.cpp:386
#: libi2pd_client/HTTPProxy.cpp:389 libi2pd_client/HTTPProxy.cpp:393
msgid "Outproxy failure"
msgstr ""
#: libi2pd_client/HTTPProxy.cpp:382
#: libi2pd_client/HTTPProxy.cpp:389
msgid "bad outproxy settings"
msgstr ""
#: libi2pd_client/HTTPProxy.cpp:385
#: libi2pd_client/HTTPProxy.cpp:392
msgid "not inside I2P network, but outproxy is not enabled"
msgstr ""
#: libi2pd_client/HTTPProxy.cpp:474
#: libi2pd_client/HTTPProxy.cpp:482
msgid "unknown outproxy url"
msgstr ""
#: libi2pd_client/HTTPProxy.cpp:480
#: libi2pd_client/HTTPProxy.cpp:490
msgid "cannot resolve upstream proxy"
msgstr ""
#: libi2pd_client/HTTPProxy.cpp:488
#: libi2pd_client/HTTPProxy.cpp:498
msgid "hostname too long"
msgstr ""
#: libi2pd_client/HTTPProxy.cpp:515
#: libi2pd_client/HTTPProxy.cpp:525
msgid "cannot connect to upstream socks proxy"
msgstr ""
#: libi2pd_client/HTTPProxy.cpp:521
#: libi2pd_client/HTTPProxy.cpp:531
msgid "Cannot negotiate with socks proxy"
msgstr ""
#: libi2pd_client/HTTPProxy.cpp:563
#: libi2pd_client/HTTPProxy.cpp:573
msgid "CONNECT error"
msgstr ""
#: libi2pd_client/HTTPProxy.cpp:563
#: libi2pd_client/HTTPProxy.cpp:573
msgid "Failed to Connect"
msgstr ""
#: libi2pd_client/HTTPProxy.cpp:574 libi2pd_client/HTTPProxy.cpp:600
#: libi2pd_client/HTTPProxy.cpp:584 libi2pd_client/HTTPProxy.cpp:610
msgid "socks proxy error"
msgstr ""
#: libi2pd_client/HTTPProxy.cpp:582
#: libi2pd_client/HTTPProxy.cpp:592
msgid "failed to send request to upstream"
msgstr ""
#: libi2pd_client/HTTPProxy.cpp:603
#: libi2pd_client/HTTPProxy.cpp:613
msgid "No Reply From socks proxy"
msgstr ""
#: libi2pd_client/HTTPProxy.cpp:610
#: libi2pd_client/HTTPProxy.cpp:620
msgid "cannot connect"
msgstr ""
#: libi2pd_client/HTTPProxy.cpp:610
#: libi2pd_client/HTTPProxy.cpp:620
msgid "http out proxy not implemented"
msgstr ""
#: libi2pd_client/HTTPProxy.cpp:611
#: libi2pd_client/HTTPProxy.cpp:621
msgid "cannot connect to upstream http proxy"
msgstr ""
#: libi2pd_client/HTTPProxy.cpp:644
#: libi2pd_client/HTTPProxy.cpp:654
msgid "Host is down"
msgstr ""
#: libi2pd_client/HTTPProxy.cpp:644
#: libi2pd_client/HTTPProxy.cpp:654
msgid ""
"Can't create connection to requested host, it may be down. Please try again "
"later."

View File

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

View File

@@ -76,7 +76,7 @@ ipv4 = true
ipv6 = false
## Enable SSU transport (default = true)
# ssu = true
ssu = false
## Bandwidth configuration
## L limit bandwidth to 32KBs/sec, O - to 256KBs/sec, P - to 2048KBs/sec,
@@ -96,6 +96,22 @@ ipv6 = false
## Note: that mode uses much more network connections and CPU!
# floodfill = true
[ntcp2]
## Enable NTCP2 transport (default = true)
# enabled = true
## Publish address in RouterInfo (default = true)
# published = true
## Port for incoming connections (default is global port option value)
# port = 4567
[ssu2]
## Enable SSU2 transport (default = false for 2.43.0)
enabled = true
## Publish address in RouterInfo (default = false for 2.43.0)
published = true
## Port for incoming connections (default is global port option value or port + 1 if SSU is enabled)
# port = 4567
[http]
## Web Console settings
## Uncomment and set to 'false' to disable Web Console
@@ -110,8 +126,8 @@ port = 7070
# user = i2pd
# pass = changeme
## Select webconsole language
## Currently supported english (default), afrikaans, armenian, french, german,
## russian, turkmen, ukrainian and uzbek languages
## Currently supported english (default), afrikaans, armenian, chinese, french,
## german, russian, turkmen, ukrainian and uzbek languages
# lang = english
[httpproxy]

View File

@@ -1,7 +1,7 @@
%define git_hash %(git rev-parse HEAD | cut -c -7)
Name: i2pd-git
Version: 2.42.1
Version: 2.43.0
Release: git%{git_hash}%{?dist}
Summary: I2P router written in C++
Conflicts: i2pd
@@ -62,9 +62,7 @@ pushd redhat-linux-build
%endif
%if 0%{?fedora} >= 35
%if 0%{?fedora} < 37
pushd redhat-linux-build
%endif
%else
%if 0%{?fedora} >= 33
pushd %{_target_platform}
@@ -82,10 +80,8 @@ popd
%endif
%if 0%{?fedora} >= 33
%if 0%{?fedora} < 37
popd
%endif
%endif
%if 0%{?mageia} > 7
popd
@@ -99,9 +95,7 @@ pushd redhat-linux-build
%endif
%if 0%{?fedora} >= 35
%if 0%{?fedora} < 37
pushd redhat-linux-build
%endif
%else
%if 0%{?fedora} >= 33
pushd %{_target_platform}
@@ -164,6 +158,9 @@ getent passwd i2pd >/dev/null || \
%changelog
* Mon Aug 22 2022 orignal <orignal@i2pmail.org> - 2.43.0
- update to 2.43.0
* Tue May 24 2022 r4sas <r4sas@i2pmail.org> - 2.42.1
- update to 2.42.1

View File

@@ -1,5 +1,5 @@
Name: i2pd
Version: 2.42.1
Version: 2.43.0
Release: 1%{?dist}
Summary: I2P router written in C++
Conflicts: i2pd-git
@@ -59,9 +59,7 @@ pushd redhat-linux-build
%endif
%if 0%{?fedora} >= 35
%if 0%{?fedora} < 37
pushd redhat-linux-build
%endif
%else
%if 0%{?fedora} >= 33
pushd %{_target_platform}
@@ -79,10 +77,8 @@ popd
%endif
%if 0%{?fedora} >= 33
%if 0%{?fedora} < 37
popd
%endif
%endif
%if 0%{?mageia} > 7
popd
@@ -96,9 +92,7 @@ pushd redhat-linux-build
%endif
%if 0%{?fedora} >= 35
%if 0%{?fedora} < 37
pushd redhat-linux-build
%endif
%else
%if 0%{?fedora} >= 33
pushd %{_target_platform}
@@ -161,6 +155,9 @@ getent passwd i2pd >/dev/null || \
%changelog
* Mon Aug 22 2022 orignal <orignal@i2pmail.org> - 2.43.0
- update to 2.43.0
* Tue May 24 2022 r4sas <r4sas@i2pmail.org> - 2.42.1
- update to 2.42.1

View File

@@ -153,6 +153,9 @@ namespace util
bool aesni; i2p::config::GetOption("cpuext.aesni", aesni);
bool avx; i2p::config::GetOption("cpuext.avx", avx);
bool forceCpuExt; i2p::config::GetOption("cpuext.force", forceCpuExt);
bool ssu; i2p::config::GetOption("ssu", ssu);
if (!ssu && i2p::config::IsDefault ("precomputation.elgamal"))
precomputation = false; // we don't elgamal table if no ssu, unless it's specified explicitly
i2p::crypto::InitCrypto (precomputation, aesni, avx, forceCpuExt);
int netID; i2p::config::GetOption("netid", netID);
@@ -250,17 +253,17 @@ namespace util
if (!ipv4 && !ipv6)
i2p::context.SetStatus (eRouterStatusMesh);
}
if (!ssu) i2p::context.RemoveSSUAddress (); // TODO: remove later
bool ssu2; i2p::config::GetOption("ssu2.enabled", ssu2);
if (ssu2)
{
uint16_t ssu2port; i2p::config::GetOption("ssu2.port", ssu2port);
if (!ssu2port) ssu2port = ssu ? (port + 1) : port;
bool published; i2p::config::GetOption("ssu2.published", published);
if (published)
{
uint16_t ssu2port; i2p::config::GetOption("ssu2.port", ssu2port);
i2p::context.PublishSSU2Address (ssu2port, true, ipv4, ipv6); // publish
}
else
i2p::context.PublishSSU2Address (0, false, ipv4, ipv6); // unpublish
i2p::context.PublishSSU2Address (ssu2port, false, ipv4, ipv6); // unpublish
}
bool transit; i2p::config::GetOption("notransit", transit);
@@ -404,7 +407,7 @@ namespace util
i2p::transport::transports.SetCheckReserved(checkInReserved);
i2p::transport::transports.Start(ntcp2, ssu, ssu2);
if (i2p::transport::transports.IsBoundSSU() || i2p::transport::transports.IsBoundNTCP2())
if (i2p::transport::transports.IsBoundSSU() || i2p::transport::transports.IsBoundSSU2() || i2p::transport::transports.IsBoundNTCP2())
LogPrint(eLogInfo, "Daemon: Transports started");
else
{

View File

@@ -182,7 +182,7 @@ namespace http {
" <meta charset=\"UTF-8\">\r\n"
" <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\r\n"
" <link rel=\"shortcut icon\" href=\"" << itoopieFavicon << "\">\r\n"
" <title>" << tr("Purple I2P Webconsole") << "</title>\r\n";
" <title>" << tr(/* tr: Webconsole page title */ "Purple I2P Webconsole") << "</title>\r\n";
GetStyles(s);
s <<
"</head>\r\n"
@@ -222,7 +222,7 @@ namespace http {
s << "<b>" << tr("ERROR") << ":</b>&nbsp;" << string << "<br>\r\n";
}
static void ShowNetworkStatus (std::stringstream& s, RouterStatus status)
static void ShowNetworkStatus (std::stringstream& s, RouterStatus status, RouterError error)
{
switch (status)
{
@@ -235,7 +235,7 @@ namespace http {
case eRouterStatusError:
{
s << tr("Error");
switch (i2p::context.GetError ())
switch (error)
{
case eRouterErrorClockSkew:
s << " - " << tr("Clock skew");
@@ -260,12 +260,12 @@ namespace http {
ShowUptime(s, i2p::context.GetUptime ());
s << "<br>\r\n";
s << "<b>" << tr("Network status") << ":</b> ";
ShowNetworkStatus (s, i2p::context.GetStatus ());
ShowNetworkStatus (s, i2p::context.GetStatus (), i2p::context.GetError ());
s << "<br>\r\n";
if (i2p::context.SupportsV6 ())
{
s << "<b>" << tr("Network status v6") << ":</b> ";
ShowNetworkStatus (s, i2p::context.GetStatusV6 ());
ShowNetworkStatus (s, i2p::context.GetStatusV6 (), i2p::context.GetErrorV6 ());
s << "<br>\r\n";
}
#if ((!defined(WIN32) && !defined(QT_GUI_LIB) && !defined(ANDROID)) || defined(ANDROID_BINARY))
@@ -531,19 +531,21 @@ namespace http {
ShowLeaseSetDestination (s, dest, token);
// Print table with streams information
s << "<table>\r\n<caption>" << tr("Streams") << "</caption>\r\n<thead>\r\n<tr>";
s << "<th style=\"width:25px;\">StreamID</th>";
s << "<th style=\"width:5px;\" \\>"; // Stream closing button column
s << "<th class=\"streamdest\">Destination</th>";
s << "<th>Sent</th>";
s << "<th>Received</th>";
s << "<th>Out</th>";
s << "<th>In</th>";
s << "<th>Buf</th>";
s << "<th>RTT</th>";
s << "<th>Window</th>";
s << "<th>Status</th>";
s << "</tr>\r\n</thead>\r\n<tbody class=\"tableitem\">\r\n";
s << "<table>\r\n<caption>"
<< tr("Streams")
<< "</caption>\r\n<thead>\r\n<tr>"
<< "<th style=\"width:25px;\">StreamID</th>"
<< "<th style=\"width:5px;\" \\>" // Stream closing button column
<< "<th class=\"streamdest\">Destination</th>"
<< "<th>Sent</th>"
<< "<th>Received</th>"
<< "<th>Out</th>"
<< "<th>In</th>"
<< "<th>Buf</th>"
<< "<th>RTT</th>"
<< "<th>Window</th>"
<< "<th>Status</th>"
<< "</tr>\r\n</thead>\r\n<tbody class=\"tableitem\">\r\n";
for (const auto& it: dest->GetAllStreams ())
{
@@ -739,17 +741,25 @@ namespace http {
s << " <button type=\"submit\">" << tr("Change") << "</button>\r\n";
s << "</form>\r\n<br>\r\n";
std::string currLang = i2p::client::context.GetLanguage ()->GetLanguage(); // get current used language
s << "<b>" << tr("Change language") << "</b><br>\r\n";
s << "<form method=\"get\" action=\"" << webroot << "\">\r\n";
s << " <input type=\"hidden\" name=\"cmd\" value=\"" << HTTP_COMMAND_SETLANGUAGE << "\">\r\n";
s << " <input type=\"hidden\" name=\"token\" value=\"" << token << "\">\r\n";
s << " <select name=\"lang\" id=\"lang\">\r\n";
// get current used language
std::string currLang = i2p::client::context.GetLanguage ()->GetLanguage();
s << "<b>"
<< tr("Change language")
<< "</b><br>\r\n"
<< "<form method=\"get\" action=\"" << webroot << "\">\r\n"
<< " <input type=\"hidden\" name=\"cmd\" value=\"" << HTTP_COMMAND_SETLANGUAGE << "\">\r\n"
<< " <input type=\"hidden\" name=\"token\" value=\"" << token << "\">\r\n"
<< " <select name=\"lang\" id=\"lang\">\r\n";
for (const auto& it: i2p::i18n::languages)
s << " <option value=\"" << it.first << "\"" << ((it.first.compare(currLang) == 0) ? " selected" : "") << ">" << it.second.LocaleName << "</option>\r\n";
s << " </select>\r\n";
s << " <button type=\"submit\">" << tr("Change") << "</button>\r\n";
s << "</form>\r\n<br>\r\n";
s << " </select>\r\n"
<< " <button type=\"submit\">"
<< tr("Change")
<< "</button>\r\n"
<< "</form>\r\n<br>\r\n";
}
@@ -783,12 +793,13 @@ namespace http {
std::stringstream tmp_s, tmp_s6; uint16_t cnt = 0, cnt6 = 0;
for (const auto& it: sessions )
{
if (it.second && it.second->IsEstablished () && !it.second->GetRemoteEndpoint ().address ().is_v6 ())
auto endpoint = it.second->GetRemoteEndpoint ();
if (it.second && it.second->IsEstablished () && endpoint.address ().is_v4 ())
{
tmp_s << "<div class=\"listitem\">\r\n";
if (it.second->IsOutgoing ()) tmp_s << " &#8658; ";
tmp_s << i2p::data::GetIdentHashAbbreviation (it.second->GetRemoteIdentity ()->GetIdentHash ()) << ": "
<< it.second->GetRemoteEndpoint ().address ().to_string ();
<< endpoint.address ().to_string () << ":" << endpoint.port ();
if (!it.second->IsOutgoing ()) tmp_s << " &#8658; ";
tmp_s << " [" << it.second->GetNumSentBytes () << ":" << it.second->GetNumReceivedBytes () << "]";
if (it.second->GetRelayTag ())
@@ -796,12 +807,12 @@ namespace http {
tmp_s << "</div>\r\n" << std::endl;
cnt++;
}
if (it.second && it.second->IsEstablished () && it.second->GetRemoteEndpoint ().address ().is_v6 ())
if (it.second && it.second->IsEstablished () && endpoint.address ().is_v6 ())
{
tmp_s6 << "<div class=\"listitem\">\r\n";
if (it.second->IsOutgoing ()) tmp_s6 << " &#8658; ";
tmp_s6 << i2p::data::GetIdentHashAbbreviation (it.second->GetRemoteIdentity ()->GetIdentHash ()) << ": "
<< "[" << it.second->GetRemoteEndpoint ().address ().to_string () << "]";
<< "[" << endpoint.address ().to_string () << "]:" << endpoint.port ();
if (!it.second->IsOutgoing ()) tmp_s6 << " &#8658; ";
tmp_s6 << " [" << it.second->GetNumSentBytes () << ":" << it.second->GetNumReceivedBytes () << "]";
if (it.second->GetRelayTag ())

6
debian/changelog vendored
View File

@@ -1,3 +1,9 @@
i2pd (2.43.0-1) unstable; urgency=medium
* updated to version 2.43.0/0.9.55
-- orignal <orignal@i2pmail.org> Mon, 22 Aug 2022 16:00:00 +0000
i2pd (2.42.1-1) unstable; urgency=medium
* updated to version 2.42.1/0.9.54

217
i18n/Chinese.cpp Normal file
View File

@@ -0,0 +1,217 @@
/*
* 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"
// Simplified Chinese localization file
// This is an example translation file without strings in it.
namespace i2p
{
namespace i18n
{
namespace chinese // language namespace
{
// language name in lowercase
static std::string language = "chinese";
// See for language plural forms here:
// https://localization-guide.readthedocs.io/en/latest/l10n/pluralforms.html
static int plural (int n) {
return 0;
}
static std::map<std::string, std::string> strings
{
{"KiB", "KiB"},
{"MiB", "MiB"},
{"GiB", "GiB"},
{"building", "正在构建"},
{"failed", "连接失败"},
{"expiring", "即将过期"},
{"established", "连接已建立"},
{"unknown", "未知"},
{"exploratory", "探测"},
{"Purple I2P Webconsole", "Purple I2P 网页控制台"},
{"<b>i2pd</b> webconsole", "<b>i2pd</b> 网页控制台"},
{"Main page", "主页"},
{"Router commands", "路由命令"},
{"Local Destinations", "本地目标"},
{"LeaseSets", "租契集"},
{"Tunnels", "隧道"},
{"Transit Tunnels", "中转隧道"},
{"Transports", "传输"},
{"I2P tunnels", "I2P 隧道"},
{"SAM sessions", "SAM 会话"},
{"ERROR", "错误"},
{"OK", "良好"},
{"Testing", "测试中"},
{"Firewalled", "受到防火墙限制"},
{"Unknown", "未知"},
{"Proxy", "代理"},
{"Mesh", "Mesh组网"},
{"Error", "错误"},
{"Clock skew", "时钟偏移"},
{"Offline", "离线"},
{"Symmetric NAT", "对称 NAT"},
{"Uptime", "运行时间"},
{"Network status", "IPv4 网络状态"},
{"Network status v6", "IPv6 网络状态"},
{"Stopping in", "距停止还有:"},
{"Family", "家族"},
{"Tunnel creation success rate", "隧道创建成功率"},
{"Received", "已接收"},
{"KiB/s", "KiB/s"},
{"Sent", "已发送"},
{"Transit", "中转"},
{"Data path", "数据文件路径"},
{"Hidden content. Press on text to see.", "隐藏内容 请点击此处查看。"},
{"Router Ident", "路由身份"},
{"Router Family", "路由器家族"},
{"Router Caps", "路由器类型"},
{"Version", "版本"},
{"Our external address", "外部地址"},
{"supported", "支持"},
{"Routers", "路由节点"},
{"Floodfills", "洪泛节点"},
{"Client Tunnels", "客户端隧道"},
{"Services", "服务"},
{"Enabled", "启用"},
{"Disabled", "禁用"},
{"Encrypted B33 address", "加密的 B33 地址"},
{"Address registration line", "地址域名注册"},
{"Domain", "域名"},
{"Generate", "生成"},
{"<b>Note:</b> result string can be used only for registering 2LD domains (example.i2p). For registering subdomains please use i2pd-tools.", "<b>注意:</b> 结果字符串可以用于注册次级域名(例如example.i2p)。若需注册次级域名,请使用 i2pd-tools。"},
{"Address", "地址"},
{"Type", "类型"},
{"EncType", "加密类型"},
{"Inbound tunnels", "入站隧道"},
{"ms", "毫秒"},
{"Outbound tunnels", "出站隧道"},
{"Tags", "标签"},
{"Incoming", "传入"},
{"Outgoing", "传出"},
{"Destination", "目标"},
{"Amount", "数量"},
{"Incoming Tags", "传入标签"},
{"Tags sessions", "标签会话"},
{"Status", "状态"},
{"Local Destination", "本地目标"},
{"Streams", ""},
{"Close stream", "断开流"},
{"I2CP session not found", "未找到 I2CP 会话"},
{"I2CP is not enabled", "I2CP 未启用"},
{"Invalid", "无效"},
{"Store type", "存储类型"},
{"Expires", "过期时间"},
{"Non Expired Leases", "未到期的租约"},
{"Gateway", "网关"},
{"TunnelID", "隧道 ID"},
{"EndDate", "结束日期"},
{"not floodfill", "非洪泛"},
{"Queue size", "队列大小"},
{"Run peer test", "运行群体测试"},
{"Decline transit tunnels", "拒绝中转隧道"},
{"Accept transit tunnels", "允许中转隧道"},
{"Cancel graceful shutdown", "取消离线"},
{"Start graceful shutdown", "优雅地离线"},
{"Force shutdown", "强制停止"},
{"Reload external CSS styles", "重载外部 CSS 样式"},
{"<b>Note:</b> any action done here are not persistent and not changes your config files.", "<b>注意:</b> 此处完成的任何操作都不是永久的,不会更改您的配置文件。"},
{"Logging level", "日志级别"},
{"Transit tunnels limit", "中转隧道限制"},
{"Change", "更换"},
{"Change language", "更换语言"},
{"no transit tunnels currently built", "目前未构建中转隧道"},
{"SAM disabled", "SAM 已禁用"},
{"no sessions currently running", "没有正在运行的会话"},
{"SAM session not found", "未找到 SAM 会话"},
{"SAM Session", "SAM 会话"},
{"Server Tunnels", "服务器隧道"},
{"Client Forwards", "客户端转发"},
{"Server Forwards", "服务器转发"},
{"Unknown page", "未知页面"},
{"Invalid token", "无效凭证"},
{"SUCCESS", "成功"},
{"Stream closed", "流已关闭"},
{"Stream not found or already was closed", "流未找到或已关闭"},
{"Destination not found", "找不到目标"},
{"StreamID can't be null", "StreamID 不能为空"},
{"Return to destination page", "返回目标页面"},
{"You will be redirected in 5 seconds", "您将在5秒内被重定向"},
{"Transit tunnels count must not exceed 65535", "中转隧道数量不能超过 65535"},
{"Back to commands list", "返回命令列表"},
{"Register at reg.i2p", "在 reg.i2p 注册域名"},
{"Description", "描述"},
{"A bit information about service on domain", "在此域名上运行的服务的一些信息"},
{"Submit", "提交"},
{"Domain can't end with .b32.i2p", "域名不能以 .b32.i2p 结尾"},
{"Domain must end with .i2p", "域名必须以 .i2p 结尾"},
{"Such destination is not found", "找不到此目标"},
{"Unknown command", "未知指令"},
{"Command accepted", "已接受指令"},
{"Proxy error", "代理错误"},
{"Proxy info", "代理信息"},
{"Proxy error: Host not found", "代理错误:找不到主机"},
{"Remote host not found in router's addressbook", "在路由的地址簿中找不到远程主机"},
{"You may try to find this host on jump services below", "您可以尝试在下方的跳转服务上找到这个主机"},
{"Invalid request", "无效请求"},
{"Proxy unable to parse your request", "代理无法解析您的请求"},
{"addresshelper is not supported", "不支持地址助手"},
{"Host", "主机"},
{"added to router's addressbook from helper", "将此地址从地址助手添加到地址簿"},
{"Click here to proceed:", "点击此处继续:"},
{"Continue", "继续"},
{"Addresshelper found", "找到地址助手"},
{"already in router's addressbook", "已在路由器的地址簿中"},
{"Click here to update record:", "点击此处更新地址簿记录"},
{"invalid request uri", "无效的 URL 请求"},
{"Can't detect destination host from request", "无法从请求中检测到目标主机"},
{"Outproxy failure", "出口代理失效"},
{"bad outproxy settings", "错误的出口代理设置"},
{"not inside I2P network, but outproxy is not enabled", "该地址不在 I2P 网络内,但未启用出口代理"},
{"unknown outproxy url", "未知的出口代理地址"},
{"cannot resolve upstream proxy", "无法解析上游代理"},
{"hostname too long", "主机名过长"},
{"cannot connect to upstream socks proxy", "无法连接到上游 socks 代理"},
{"Cannot negotiate with socks proxy", "无法与 socks 代理协商"},
{"CONNECT error", "连接错误"},
{"Failed to Connect", "连接失败"},
{"socks proxy error", "socks 代理错误"},
{"failed to send request to upstream", "向上游发送请求失败"},
{"No Reply From socks proxy", "没有来自 socks 代理的回复"},
{"cannot connect", "无法连接"},
{"http out proxy not implemented", "http 出口代理未实现"},
{"cannot connect to upstream http proxy", "无法连接到上游 http 代理"},
{"Host is down", "主机已关闭"},
{"Can't create connection to requested host, it may be down. Please try again later.", "无法创建到目标主机的连接。主机可能已下线,请稍后再试。"},
{"", ""},
};
static std::map<std::string, std::vector<std::string>> plurals
{
{"days", {""}},
{"hours", {""}},
{"minutes", {""}},
{"seconds", {""}},
{"", {""}},
};
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

@@ -35,24 +35,33 @@ namespace french // language namespace
{"MiB", "Mio"},
{"GiB", "Gio"},
{"building", "En construction"},
{"failed", "echoué"},
{"failed", "échoué"},
{"expiring", "expiré"},
{"established", "établi"},
{"unknown", "inconnu"},
{"exploratory", "exploratoire"},
{"Purple I2P Webconsole", "Console web Purple I2P"},
{"<b>i2pd</b> webconsole", "Console web <b>i2pd</b>"},
{"Main page", "Page principale"},
{"Router commands", "Commandes du routeur"},
{"Local Destinations", "Destinations locales"},
{"LeaseSets", "Jeu de baux"},
{"Tunnels", "Tunnels"},
{"Transit Tunnels", "Tunnels transitoires"},
{"Transports", "Transports"},
{"I2P tunnels", "Tunnels I2P"},
{"SAM sessions", "Sessions SAM"},
{"ERROR", "ERREUR"},
{"OK", "OK"},
{"Testing", "Test en cours"},
{"Firewalled", "Derrière un pare-feu"},
{"Unknown", "Inconnu"},
{"Proxy", "Proxy"},
{"Mesh", "Maillé"},
{"Error", "Erreur"},
{"Clock skew", "Horloge décalée"},
{"Offline", "Hors ligne"},
{"Symmetric NAT", "NAT symétrique"},
{"Uptime", "Temps de fonctionnement"},
{"Network status", "État du réseau"},
{"Network status v6", "État du réseau v6"},
@@ -62,24 +71,124 @@ namespace french // language namespace
{"Received", "Reçu"},
{"KiB/s", "kio/s"},
{"Sent", "Envoyé"},
{"Transit", "Transit"},
{"Hidden content. Press on text to see.", "Contenu caché. Cliquez sur le texte pour regarder."},
{"Transit", "Transité"},
{"Data path", "Emplacement des données"},
{"Hidden content. Press on text to see.", "Contenu caché. Cliquez sur le texte pour afficher."},
{"Router Ident", "Identifiant du routeur"},
{"Router Family", "Famille du routeur"},
{"Router Caps", "Limiteurs du routeur"},
{"Version", "Version"},
{"Our external address", "Notre adresse externe"},
{"supported", "supporté"},
{"Routers", "Routeurs"},
{"Client Tunnels", "Tunnels clients"},
{"Services", "Services"},
{"Enabled", "Activé"},
{"Disabled", "Désactivé"},
{"Encrypted B33 address", "Adresse B33 chiffrée"},
{"Address registration line", "Ligne d'inscription de l'adresse"},
{"Domain", "Domaine"},
{"Generate", "Générer"},
{"<b>Note:</b> result string can be used only for registering 2LD domains (example.i2p). For registering subdomains please use i2pd-tools.", "<b>Note:</b> La chaîne résultante peut seulement être utilisée pour enregistrer les domaines 2LD (exemple.i2p). Pour enregistrer des sous-domaines, veuillez utiliser i2pd-tools."},
{"Address", "Adresse"},
{"Type", "Type"},
{"Inbound tunnels", "Tunnels entrants"},
{"ms", "ms"},
{"Outbound tunnels", "Tunnels sortants"},
{"Tags", "Balises"},
{"Incoming", "Entrant"},
{"Outgoing", "Sortant"},
{"Destination", "Destination"},
{"Amount", "Quantité"},
{"Incoming Tags", "Balises entrantes"},
{"Tags sessions", "Sessions des balises"},
{"Status", "Statut"},
{"Local Destination", "Destination locale"},
{"Streams", "Flux"},
{"Close stream", "Fermer le flux"},
{"I2CP session not found", "Session I2CP introuvable"},
{"I2CP is not enabled", "I2CP est désactivé"},
{"Invalid", "Invalide"},
{"Store type", "Type de stockage"},
{"Expires", "Expire"},
{"Non Expired Leases", "Baux non expirés"},
{"Gateway", "Passerelle"},
{"TunnelID", "ID du tunnel"},
{"EndDate", "Date de fin"},
{"Queue size", "Longueur de la file"},
{"Run peer test", "Lancer test des pairs"},
{"Decline transit tunnels", "Refuser les tunnels transitoires"},
{"Accept transit tunnels", "Accepter les tunnels transitoires"},
{"Cancel graceful shutdown", "Annuler l'arrêt gracieux"},
{"Start graceful shutdown", "Démarrer l'arrêt gracieux"},
{"Force shutdown", "Forcer l'arrêt"},
{"Reload external CSS styles", "Rafraîchir les styles CSS externes"},
{"<b>Note:</b> any action done here are not persistent and not changes your config files.", "<b>Note:</b> Toute action effectuée ici n'est pas permanente et ne modifie pas vos fichiers de configuration."},
{"Logging level", "Niveau de journalisation"},
{"Transit tunnels limit", "Limite sur les tunnels transitoires"},
{"Change", "Changer"},
{"Change language", "Changer la langue"},
{"no transit tunnels currently built", "aucun tunnel transitoire présentement établi"},
{"SAM disabled", "SAM désactivé"},
{"no sessions currently running", "aucune session présentement en cours"},
{"SAM session not found", "session SAM introuvable"},
{"SAM Session", "Session SAM"},
{"Server Tunnels", "Tunnels serveurs"},
{"Unknown page", "Page inconnue"},
{"Invalid token", "Jeton invalide"},
{"SUCCESS", "SUCCÈS"},
{"Stream closed", "Flux fermé"},
{"Stream not found or already was closed", "Flux introuvable ou déjà fermé"},
{"Destination not found", "Destination introuvable"},
{"StreamID can't be null", "StreamID ne peut pas être vide"},
{"Return to destination page", "Retourner à la page de destination"},
{"You will be redirected in 5 seconds", "Vous allez être redirigé dans cinq secondes"},
{"Transit tunnels count must not exceed 65535", "Le nombre de tunnels transitoires ne doit pas dépasser 65535"},
{"Back to commands list", "Retour à la liste des commandes"},
{"Register at reg.i2p", "Inscription à reg.i2p"},
{"Description", "Description"},
{"A bit information about service on domain", "Un peu d'information à propos des services disponibles dans le domaine"},
{"Submit", "Soumettre"},
{"Domain can't end with .b32.i2p", "Le domaine ne peut pas terminer par .b32.i2p"},
{"Domain must end with .i2p", "Le domaine doit terminer par .i2p"},
{"Such destination is not found", "Cette destination est introuvable"},
{"Unknown command", "Commande inconnue"},
{"Command accepted", "Commande acceptée"},
{"Proxy error", "Erreur de proxy"},
{"Proxy info", "Information sur le proxy"},
{"Proxy error: Host not found", "Erreur de proxy: Hôte introuvable"},
{"Remote host not found in router's addressbook", "Hôte distant introuvable dans le carnet d'adresse du routeur"},
{"You may try to find this host on jump services below", "Vous pouvez essayer de trouver cet hôte sur des services de redirection ci-dessous"},
{"Invalid request", "Requête invalide"},
{"Proxy unable to parse your request", "Proxy incapable de comprendre votre requête"},
{"addresshelper is not supported", "Assistant d'adresse non supporté"},
{"Host", "Hôte"},
{"added to router's addressbook from helper", "Ajouté au carnet d'adresse du routeur par l'assistant"},
{"Click here to proceed:", "Cliquez ici pour continuer:"},
{"Continue", "Continuer"},
{"Addresshelper found", "Assistant d'adresse trouvé"},
{"already in router's addressbook", "déjà dans le carnet d'adresses du routeur"},
{"Click here to update record:", "Cliquez ici pour mettre à jour le carnet d'adresse:"},
{"invalid request uri", "uri de la requête invalide"},
{"Can't detect destination host from request", "Impossible de détecter l'hôte de destination à partir de la requête"},
{"Outproxy failure", "Échec de proxy de sortie"},
{"bad outproxy settings", "Mauvaise configuration du proxy de sortie"},
{"not inside I2P network, but outproxy is not enabled", "pas dans le réseau I2P, mais le proxy de sortie n'est pas activé"},
{"unknown outproxy url", "URL du proxy de sortie inconnu"},
{"cannot resolve upstream proxy", "impossible de résoudre l'adresse du proxy en amont"},
{"hostname too long", "nom d'hôte trop long"},
{"cannot connect to upstream socks proxy", "impossible de se connecter au proxy socks en amont"},
{"Cannot negotiate with socks proxy", "Impossible de négocier avec le proxy socks"},
{"CONNECT error", "Erreur de connexion"},
{"Failed to Connect", "Échec de connexion"},
{"socks proxy error", "Erreur de proxy socks"},
{"failed to send request to upstream", "Erreur lors de l'envoie de la requête en amont"},
{"No Reply From socks proxy", "Pas de réponse du proxy socks"},
{"cannot connect", "impossible de connecter"},
{"http out proxy not implemented", "Proxy de sortie HTTP non implémenté"},
{"cannot connect to upstream http proxy", "impossible de se connecter au proxy HTTP en amont"},
{"Host is down", "Hôte hors service"},
{"Can't create connection to requested host, it may be down. Please try again later.", "Impossible d'établir une connexion avec l'hôte, il est peut-être hors service. Veuillez réessayer plus tard."},
{"", ""},
};

View File

@@ -73,6 +73,7 @@ namespace i18n
// Add localization here with language name as namespace
namespace afrikaans { 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 english { 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 (); }
@@ -88,6 +89,7 @@ namespace i18n
{
{ "afrikaans", {"Afrikaans", "af", i2p::i18n::afrikaans::GetLocale} },
{ "armenian", {"հայերէն", "hy", i2p::i18n::armenian::GetLocale} },
{ "chinese", {"简体字", "zh-CN", i2p::i18n::chinese::GetLocale} },
{ "english", {"English", "en", i2p::i18n::english::GetLocale} },
{ "french", {"Français", "fr", i2p::i18n::french::GetLocale} },
{ "german", {"Deutsch", "de", i2p::i18n::german::GetLocale} },

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
*
@@ -59,9 +59,9 @@ namespace data
if (readIdentity || !m_Identity)
m_Identity = std::make_shared<IdentityEx>(m_Buffer, m_BufferLen);
size_t size = m_Identity->GetFullLen ();
if (size > m_BufferLen)
if (size + 256 > m_BufferLen)
{
LogPrint (eLogError, "LeaseSet: Identity length ", size, " exceeds buffer size ", m_BufferLen);
LogPrint (eLogError, "LeaseSet: Identity length ", int(size), " exceeds buffer size ", int(m_BufferLen));
m_IsValid = false;
return;
}
@@ -74,7 +74,7 @@ namespace data
size += m_Identity->GetSigningPublicKeyLen (); // unused signing key
if (size + 1 > m_BufferLen)
{
LogPrint (eLogError, "LeaseSet: ", size, " exceeds buffer size ", m_BufferLen);
LogPrint (eLogError, "LeaseSet: ", int(size), " exceeds buffer size ", int(m_BufferLen));
m_IsValid = false;
return;
}
@@ -89,7 +89,7 @@ namespace data
}
if (size + num*LEASE_SIZE > m_BufferLen)
{
LogPrint (eLogError, "LeaseSet: ", size, " exceeds buffer size ", m_BufferLen);
LogPrint (eLogError, "LeaseSet: ", int(size), " exceeds buffer size ", int(m_BufferLen));
m_IsValid = false;
return;
}
@@ -125,7 +125,7 @@ namespace data
auto signedSize = leases - m_Buffer;
if (signedSize + m_Identity->GetSignatureLen () > m_BufferLen)
{
LogPrint (eLogError, "LeaseSet: Signature exceeds buffer size ", m_BufferLen);
LogPrint (eLogError, "LeaseSet: Signature exceeds buffer size ", int(m_BufferLen));
m_IsValid = false;
}
else if (!m_Identity->Verify (m_Buffer, signedSize, leases))
@@ -172,7 +172,7 @@ namespace data
m_ExpirationTime = lease.endDate;
if (m_StoreLeases)
{
auto ret = m_Leases.insert (std::make_shared<Lease>(lease));
auto ret = m_Leases.insert (i2p::data::netdb.NewLease (lease));
if (!ret.second) (*ret.first)->endDate = lease.endDate; // update existing
(*ret.first)->isUpdated = true;
}
@@ -274,7 +274,7 @@ namespace data
{
if (len <= m_BufferLen) m_BufferLen = len;
else
LogPrint (eLogError, "LeaseSet2: Actual buffer size ", len , " exceeds full buffer size ", m_BufferLen);
LogPrint (eLogError, "LeaseSet2: Actual buffer size ", int(len) , " exceeds full buffer size ", int(m_BufferLen));
}
LeaseSet2::LeaseSet2 (uint8_t storeType, const uint8_t * buf, size_t len, bool storeLeases, CryptoKeyType preferredCrypto):
@@ -320,7 +320,7 @@ namespace data
else
identity = GetIdentity ();
size_t offset = identity->GetFullLen ();
if (offset + 8 >= len) return;
if (offset + 8 > len) return;
m_PublishedTimestamp = bufbe32toh (buf + offset); offset += 4; // published timestamp (seconds)
uint16_t expires = bufbe16toh (buf + offset); offset += 2; // expires (seconds)
SetExpirationTime ((m_PublishedTimestamp + expires)*1000LL); // in milliseconds
@@ -364,6 +364,10 @@ namespace data
SetIsValid (verified);
}
offset += m_TransientVerifier ? m_TransientVerifier->GetSignatureLen () : identity->GetSignatureLen ();
if (offset > len) {
LogPrint (eLogWarning, "LeaseSet2: short buffer: wanted ", int(offset), "bytes, have ", int(len));
return;
}
SetBufferLen (offset);
}
@@ -388,17 +392,17 @@ namespace data
// properties
uint16_t propertiesLen = bufbe16toh (buf + offset); offset += 2;
offset += propertiesLen; // skip for now. TODO: implement properties
if (offset + 1 >= len) return 0;
// key sections
CryptoKeyType preferredKeyType = m_EncryptionType;
bool preferredKeyFound = false;
if (offset + 1 > len) return 0;
int numKeySections = buf[offset]; offset++;
for (int i = 0; i < numKeySections; i++)
{
if (offset + 4 > len) return 0;
uint16_t keyType = bufbe16toh (buf + offset); offset += 2; // encryption key type
if (offset + 2 >= len) return 0;
uint16_t encryptionKeyLen = bufbe16toh (buf + offset); offset += 2;
if (offset + encryptionKeyLen >= len) return 0;
if (offset + encryptionKeyLen > len) return 0;
if (IsStoreLeases () && !preferredKeyFound) // create encryptor with leases only
{
// we pick first valid key if preferred not found
@@ -413,7 +417,7 @@ namespace data
offset += encryptionKeyLen;
}
// leases
if (offset + 1 >= len) return 0;
if (offset + 1 > len) return 0;
int numLeases = buf[offset]; offset++;
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
if (IsStoreLeases ())
@@ -432,7 +436,8 @@ namespace data
}
else
offset += numLeases*LEASE2_SIZE; // 40 bytes per lease
return offset;
return (offset > len ? 0 : offset);
}
size_t LeaseSet2::ReadMetaLS2TypeSpecificPart (const uint8_t * buf, size_t len)
@@ -442,18 +447,18 @@ namespace data
uint16_t propertiesLen = bufbe16toh (buf + offset); offset += 2;
offset += propertiesLen; // skip for now. TODO: implement properties
// entries
if (offset + 1 >= len) return 0;
if (offset + 1 > len) return 0;
int numEntries = buf[offset]; offset++;
for (int i = 0; i < numEntries; i++)
{
if (offset + 40 >= len) return 0;
if (offset + LEASE2_SIZE > len) return 0;
offset += 32; // hash
offset += 3; // flags
offset += 1; // cost
offset += 4; // expires
}
// revocations
if (offset + 1 >= len) return 0;
if (offset + 1 > len) return 0;
int numRevocations = buf[offset]; offset++;
for (int i = 0; i < numRevocations; i++)
{

View File

@@ -240,11 +240,10 @@ namespace data
m_HiddenMode = hide;
}
bool NetDb::AddRouterInfo (const uint8_t * buf, int len)
std::shared_ptr<const RouterInfo> NetDb::AddRouterInfo (const uint8_t * buf, int len)
{
bool updated;
AddRouterInfo (buf, len, updated);
return updated;
return AddRouterInfo (buf, len, updated);
}
std::shared_ptr<const RouterInfo> NetDb::AddRouterInfo (const uint8_t * buf, int len, bool& updated)
@@ -272,7 +271,10 @@ namespace data
if (r->IsNewer (buf, len))
{
bool wasFloodfill = r->IsFloodfill ();
r->Update (buf, len);
{
std::unique_lock<std::mutex> l(m_RouterInfosMutex);
r->Update (buf, len);
}
LogPrint (eLogInfo, "NetDb: RouterInfo updated: ", ident.ToBase64());
if (wasFloodfill != r->IsFloodfill ()) // if floodfill status updated
{
@@ -436,12 +438,15 @@ namespace data
// try reseeding from floodfill first if specified
std::string riPath;
if(i2p::config::GetOption("reseed.floodfill", riPath)) {
if(i2p::config::GetOption("reseed.floodfill", riPath))
{
auto ri = std::make_shared<RouterInfo>(riPath);
if (ri->IsFloodfill()) {
if (ri->IsFloodfill())
{
const uint8_t * riData = ri->GetBuffer();
int riLen = ri->GetBufferLen();
if(!i2p::data::netdb.AddRouterInfo(riData, riLen)) {
if (!i2p::data::netdb.AddRouterInfo(riData, riLen))
{
// bad router info
LogPrint(eLogError, "NetDb: Bad router info");
return;
@@ -940,9 +945,9 @@ namespace data
}
uint16_t numExcluded = bufbe16toh (excluded);
excluded += 2;
if (numExcluded > 512)
if (numExcluded > 512 || (excluded - buf) + numExcluded*32 > (int)msg->GetPayloadLength ())
{
LogPrint (eLogWarning, "NetDb: Number of excluded peers", numExcluded, " exceeds 512");
LogPrint (eLogWarning, "NetDb: Number of excluded peers", numExcluded, " is too much");
return;
}
@@ -951,10 +956,11 @@ namespace data
{
LogPrint (eLogInfo, "NetDb: Exploratory close to ", key, " ", numExcluded, " excluded");
std::set<IdentHash> excludedRouters;
const uint8_t * excluded_ident = excluded;
for (int i = 0; i < numExcluded; i++)
{
excludedRouters.insert (excluded);
excluded += 32;
excludedRouters.insert (excluded_ident);
excluded_ident += 32;
}
std::vector<IdentHash> routers;
for (int i = 0; i < 3; i++)
@@ -1012,7 +1018,7 @@ namespace data
if (closestFloodfills.empty ())
LogPrint (eLogWarning, "NetDb: Requested ", key, " not found, ", numExcluded, " peers excluded");
replyMsg = CreateDatabaseSearchReply (ident, closestFloodfills);
}
}
}
excluded += numExcluded * 32;
if (replyMsg)
@@ -1231,6 +1237,16 @@ namespace data
});
}
std::shared_ptr<const RouterInfo> NetDb::GetRandomSSU2Introducer (bool v4, const std::set<IdentHash>& excluded) const
{
return GetRandomRouter (
[v4, &excluded](std::shared_ptr<const RouterInfo> router)->bool
{
return !router->IsHidden () && router->IsSSU2Introducer (v4) &&
!excluded.count (router->GetIdentHash ());
});
}
std::shared_ptr<const RouterInfo> NetDb::GetHighBandwidthRandomRouter (std::shared_ptr<const RouterInfo> compatibleWith, bool reverse) const
{
return GetRandomRouter (
@@ -1431,6 +1447,7 @@ namespace data
else
++it;
}
m_LeasesPool.CleanUpMt ();
}
void NetDb::PopulateRouterInfoBuffer (std::shared_ptr<RouterInfo> r)

View File

@@ -69,7 +69,7 @@ namespace data
void Start ();
void Stop ();
bool AddRouterInfo (const uint8_t * buf, int len);
std::shared_ptr<const RouterInfo> AddRouterInfo (const uint8_t * buf, int len);
bool AddRouterInfo (const IdentHash& ident, const uint8_t * buf, int len);
bool AddLeaseSet (const IdentHash& ident, const uint8_t * buf, int len);
bool AddLeaseSet2 (const IdentHash& ident, const uint8_t * buf, int len, uint8_t storeType);
@@ -93,6 +93,7 @@ namespace data
std::shared_ptr<const RouterInfo> GetRandomSSU2PeerTestRouter (bool v4, const std::set<IdentHash>& excluded) const;
std::shared_ptr<const RouterInfo> GetRandomSSUV6Router () const; // TODO: change to v6 peer test later
std::shared_ptr<const RouterInfo> GetRandomIntroducer (bool v4, const std::set<IdentHash>& excluded) const;
std::shared_ptr<const RouterInfo> GetRandomSSU2Introducer (bool v4, const std::set<IdentHash>& excluded) const;
std::shared_ptr<const RouterInfo> GetClosestFloodfill (const IdentHash& destination, const std::set<IdentHash>& excluded, bool closeThanUsOnly = false) const;
std::vector<IdentHash> GetClosestFloodfills (const IdentHash& destination, size_t num,
std::set<IdentHash>& excluded, bool closeThanUsOnly = false) const;
@@ -125,6 +126,7 @@ namespace data
void ClearRouterInfos () { m_RouterInfos.clear (); };
std::shared_ptr<RouterInfo::Buffer> NewRouterInfoBuffer () { return m_RouterInfoBuffersPool.AcquireSharedMt (); };
void PopulateRouterInfoBuffer (std::shared_ptr<RouterInfo> r);
std::shared_ptr<Lease> NewLease (const Lease& lease) { return m_LeasesPool.AcquireSharedMt (lease); };
uint32_t GetPublishReplyToken () const { return m_PublishReplyToken; };
@@ -181,6 +183,7 @@ namespace data
uint32_t m_PublishReplyToken = 0;
i2p::util::MemoryPoolMt<RouterInfo::Buffer> m_RouterInfoBuffersPool;
i2p::util::MemoryPoolMt<Lease> m_LeasesPool;
};
extern NetDb netdb;

View File

@@ -29,7 +29,7 @@ namespace i2p
RouterContext::RouterContext ():
m_LastUpdateTime (0), m_AcceptsTunnels (true), m_IsFloodfill (false),
m_ShareRatio (100), m_Status (eRouterStatusUnknown), m_StatusV6 (eRouterStatusUnknown),
m_Error (eRouterErrorNone), m_NetID (I2PD_NET_ID)
m_Error (eRouterErrorNone), m_ErrorV6 (eRouterErrorNone), m_NetID (I2PD_NET_ID)
{
}
@@ -60,11 +60,7 @@ namespace i2p
i2p::data::LocalRouterInfo routerInfo;
routerInfo.SetRouterIdentity (GetIdentity ());
uint16_t port; i2p::config::GetOption("port", port);
if (!port)
{
port = rand () % (30777 - 9111) + 9111; // I2P network ports range
if (port == 9150) port = 9151; // Tor browser
}
if (!port) port = SelectRandomPort ();
bool ipv4; i2p::config::GetOption("ipv4", ipv4);
bool ipv6; i2p::config::GetOption("ipv6", ipv6);
bool ssu; i2p::config::GetOption("ssu", ssu);
@@ -121,7 +117,11 @@ namespace i2p
if (ssu2)
{
if (ssu2Published)
routerInfo.AddSSU2Address (m_SSU2Keys->staticPublicKey, m_SSU2Keys->intro, boost::asio::ip::address_v4::from_string (host), port);
{
uint16_t ssu2Port; i2p::config::GetOption ("ssu2.port", ssu2Port);
if (!ssu2Port) ssu2Port = ssu ? (port + 1) : port;
routerInfo.AddSSU2Address (m_SSU2Keys->staticPublicKey, m_SSU2Keys->intro, boost::asio::ip::address_v4::from_string (host), ssu2Port);
}
else
{
addressCaps |= i2p::data::RouterInfo::AddressCaps::eV4;
@@ -166,7 +166,11 @@ namespace i2p
if (ssu2)
{
if (ssu2Published)
routerInfo.AddSSU2Address (m_SSU2Keys->staticPublicKey, m_SSU2Keys->intro, boost::asio::ip::address_v6::from_string (host), port);
{
uint16_t ssu2Port; i2p::config::GetOption ("ssu2.port", ssu2Port);
if (!ssu2Port) ssu2Port = ssu ? (port + 1) : port;
routerInfo.AddSSU2Address (m_SSU2Keys->staticPublicKey, m_SSU2Keys->intro, boost::asio::ip::address_v6::from_string (host), ssu2Port);
}
else
{
if (!ipv4) // no other ssu2 addresses yet
@@ -192,6 +196,13 @@ namespace i2p
m_RouterInfo.Update (routerInfo.GetBuffer (), routerInfo.GetBufferLen ());
}
uint16_t RouterContext::SelectRandomPort () const
{
uint16_t port = rand () % (30777 - 9111) + 9111; // I2P network ports range
if (port == 9150) port = 9151; // Tor browser
return port;
}
void RouterContext::UpdateRouterInfo ()
{
m_RouterInfo.CreateBuffer (m_Keys);
@@ -225,6 +236,13 @@ namespace i2p
fk.write ((char *)m_SSU2Keys.get (), sizeof (SSU2PrivateKeys));
}
bool RouterContext::IsSSU2Only () const
{
auto transports = m_RouterInfo.GetCompatibleTransports (false);
return (transports & (i2p::data::RouterInfo::eSSU2V4 | i2p::data::RouterInfo::eSSU2V6)) &&
!(transports & (i2p::data::RouterInfo::eSSUV4 | i2p::data::RouterInfo::eSSUV6));
}
void RouterContext::SetStatus (RouterStatus status)
{
if (status != m_Status)
@@ -245,11 +263,18 @@ namespace i2p
}
}
void RouterContext::SetStatusSSU2 (RouterStatus status)
{
if (IsSSU2Only ())
SetStatus (status);
}
void RouterContext::SetStatusV6 (RouterStatus status)
{
if (status != m_StatusV6)
{
m_StatusV6 = status;
m_ErrorV6 = eRouterErrorNone;
switch (m_StatusV6)
{
case eRouterStatusOK:
@@ -264,12 +289,18 @@ namespace i2p
}
}
void RouterContext::SetStatusV6SSU2 (RouterStatus status)
{
if (IsSSU2Only ())
SetStatusV6 (status);
}
void RouterContext::UpdatePort (int port)
{
bool updated = false;
for (auto& address : m_RouterInfo.GetAddresses ())
{
if (!address->IsNTCP2 () && !address->IsSSU2 () && address->port != port)
if (address->port != port && (address->transportStyle == i2p::data::RouterInfo::eTransportSSU || IsSSU2Only ()))
{
address->port = port;
updated = true;
@@ -297,12 +328,7 @@ namespace i2p
}
if (isAddr)
{
if (!port && !address->port)
{
// select random port only if address's port is not set
port = rand () % (30777 - 9111) + 9111; // I2P network ports range
if (port == 9150) port = 9151; // Tor browser
}
if (!port && !address->port) port = SelectRandomPort ();
if (port) address->port = port;
address->published = publish;
memcpy (address->i, m_NTCP2Keys->iv, 16);
@@ -318,18 +344,23 @@ namespace i2p
{
auto& addresses = m_RouterInfo.GetAddresses ();
bool found = false, updated = false;
for (auto it = addresses.begin (); it != addresses.end (); ++it)
for (auto it = addresses.begin (); it != addresses.end ();)
{
if ((*it)->IsNTCP2 ())
{
found = true;
if (!enable)
if (enable)
{
addresses.erase (it);
updated= true;
}
break;
(*it)->s = m_NTCP2Keys->staticPublicKey;
memcpy ((*it)->i, m_NTCP2Keys->iv, 16);
it++;
}
else
it = addresses.erase (it);
updated = true;
}
else
it++;
}
if (enable && !found)
{
@@ -342,14 +373,26 @@ namespace i2p
void RouterContext::PublishSSU2Address (int port, bool publish, bool v4, bool v6)
{
if (!m_SSU2Keys || (publish && !port)) return;
if (!m_SSU2Keys) return;
int newPort = 0;
if (!port)
{
for (const auto& address : m_RouterInfo.GetAddresses ())
if (address->port)
{
newPort = address->port;
break;
}
if (!newPort) newPort = SelectRandomPort ();
}
bool updated = false;
for (auto& address : m_RouterInfo.GetAddresses ())
{
if (address->IsSSU2 () && (address->port != port || address->published != publish) &&
if (address->IsSSU2 () && (!address->port || address->port != port || address->published != publish) &&
((v4 && address->IsV4 ()) || (v6 && address->IsV6 ())))
{
address->port = port;
if (port) address->port = port;
else if (!address->port) address->port = newPort;
address->published = publish;
if (publish)
address->caps |= (i2p::data::RouterInfo::eSSUIntroducer | i2p::data::RouterInfo::eSSUTesting);
@@ -366,27 +409,41 @@ namespace i2p
{
auto& addresses = m_RouterInfo.GetAddresses ();
bool found = false, updated = false;
for (auto it = addresses.begin (); it != addresses.end (); ++it)
for (auto it = addresses.begin (); it != addresses.end ();)
{
if ((*it)->IsSSU2 ())
{
found = true;
if (!enable)
if (enable)
{
addresses.erase (it);
updated= true;
}
break;
(*it)->s = m_SSU2Keys->staticPublicKey;
(*it)->i = m_SSU2Keys->intro;
it++;
}
else
it = addresses.erase (it);
updated = true;
}
else
it++;
}
if (enable && !found)
{
uint8_t addressCaps = 0;
bool ipv4; i2p::config::GetOption("ipv4", ipv4);
bool ipv6; i2p::config::GetOption("ipv6", ipv6);
if (ipv4) addressCaps |= i2p::data::RouterInfo::AddressCaps::eV4;
if (ipv6) addressCaps |= i2p::data::RouterInfo::AddressCaps::eV6;
m_RouterInfo.AddSSU2Address (m_SSU2Keys->staticPublicKey, m_SSU2Keys->intro, addressCaps);
bool published; i2p::config::GetOption("ntcp2.published", published);
if (published)
{
if (ipv4) m_RouterInfo.AddSSU2Address (m_SSU2Keys->staticPublicKey, m_SSU2Keys->intro, i2p::data::RouterInfo::AddressCaps::eV4);
if (ipv6) m_RouterInfo.AddSSU2Address (m_SSU2Keys->staticPublicKey, m_SSU2Keys->intro, i2p::data::RouterInfo::AddressCaps::eV6);
}
else
{
uint8_t addressCaps = 0;
if (ipv4) addressCaps |= i2p::data::RouterInfo::AddressCaps::eV4;
if (ipv6) addressCaps |= i2p::data::RouterInfo::AddressCaps::eV6;
m_RouterInfo.AddSSU2Address (m_SSU2Keys->staticPublicKey, m_SSU2Keys->intro, addressCaps);
}
updated = true;
}
if (updated)
@@ -401,23 +458,30 @@ namespace i2p
if (address->host != host && address->IsCompatible (host) &&
!i2p::util::net::IsYggdrasilAddress (address->host))
{
// update host
address->host = host;
if (host.is_v6 () && address->transportStyle == i2p::data::RouterInfo::eTransportSSU)
{
// update MTU
auto mtu = i2p::util::net::GetMTU (host);
if (mtu)
{
LogPrint (eLogDebug, "Router: Our v6 MTU=", mtu);
if (mtu > 1472) { // TODO: magic constant
mtu = 1472;
LogPrint(eLogWarning, "Router: MTU dropped to upper limit of 1472 bytes");
}
if (address->ssu) address->ssu->mtu = mtu;
}
}
updated = true;
}
if (host.is_v6 () && address->IsV6 () && address->ssu &&
(!address->ssu->mtu || updated))
{
// 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");
}
if (mtu && !address->IsSSU2 ()) // SSU1
mtu = (mtu >> 4) << 4; // round to multiple of 16
address->ssu->mtu = mtu;
updated = true;
}
}
}
auto ts = i2p::util::GetSecondsSinceEpoch ();
if (updated || ts > m_LastUpdateTime + ROUTER_INFO_UPDATE_INTERVAL)
@@ -438,6 +502,37 @@ namespace i2p
UpdateRouterInfo ();
}
bool RouterContext::AddSSU2Introducer (const i2p::data::RouterInfo::Introducer& introducer, bool v4)
{
if (!IsSSU2Only ()) return false;
bool ret = m_RouterInfo.AddSSU2Introducer (introducer, v4);
if (ret)
UpdateRouterInfo ();
return ret;
}
void RouterContext::RemoveSSU2Introducer (const i2p::data::IdentHash& h, bool v4)
{
if (!IsSSU2Only ()) return;
if (m_RouterInfo.RemoveSSU2Introducer (h, v4))
UpdateRouterInfo ();
}
void RouterContext::ClearSSU2Introducers (bool v4)
{
bool updated = false;
auto& addresses = m_RouterInfo.GetAddresses ();
for (auto& addr : addresses)
if (addr->IsSSU2 () && ((v4 && addr->IsV4 ()) || (!v4 && addr->IsV6 ())) &&
addr->ssu && !addr->ssu->introducers.empty ())
{
addr->ssu->introducers.clear ();
updated = true;
}
if (updated)
UpdateRouterInfo ();
}
void RouterContext::SetFloodfill (bool floodfill)
{
m_IsFloodfill = floodfill;
@@ -538,6 +633,7 @@ namespace i2p
void RouterContext::RemoveNTCPAddress (bool v4only)
{
bool updated = false;
auto& addresses = m_RouterInfo.GetAddresses ();
for (auto it = addresses.begin (); it != addresses.end ();)
{
@@ -545,11 +641,38 @@ namespace i2p
(!v4only || (*it)->host.is_v4 ()))
{
it = addresses.erase (it);
updated = true;
if (v4only) break; // otherwise might be more than one address
}
else
++it;
}
if (updated)
m_RouterInfo.UpdateSupportedTransports ();
}
void RouterContext::RemoveSSUAddress ()
{
bool updated = false;
auto& addresses = m_RouterInfo.GetAddresses ();
for (auto it = addresses.begin (); it != addresses.end ();)
{
if ((*it)->transportStyle == i2p::data::RouterInfo::eTransportSSU)
{
it = addresses.erase (it);
updated = true;
}
else
++it;
}
if (updated)
m_RouterInfo.UpdateSupportedTransports ();
}
void RouterContext::SetUnreachableSSU2 (bool v4, bool v6)
{
if (IsSSU2Only ())
SetUnreachable (v4, v6);
}
void RouterContext::SetUnreachable (bool v4, bool v6)
@@ -568,7 +691,8 @@ namespace i2p
// delete previous introducers
auto& addresses = m_RouterInfo.GetAddresses ();
for (auto& addr : addresses)
if (addr->ssu && !addr->IsSSU2 () && ((v4 && addr->IsV4 ()) || (v6 && addr->IsV6 ())))
if (addr->ssu && (!addr->IsSSU2 () || IsSSU2Only ()) &&
((v4 && addr->IsV4 ()) || (v6 && addr->IsV6 ())))
{
addr->published = false;
addr->caps &= ~i2p::data::RouterInfo::eSSUIntroducer; // can't be introducer
@@ -598,14 +722,19 @@ namespace i2p
}
uint16_t port = 0;
// delete previous introducers
bool isSSU2Published = IsSSU2Only (); // TODO
if (isSSU2Published)
i2p::config::GetOption ("ssu2.published", isSSU2Published);
auto& addresses = m_RouterInfo.GetAddresses ();
for (auto& addr : addresses)
if (addr->ssu && !addr->IsSSU2 () && ((v4 && addr->IsV4 ()) || (v6 && addr->IsV6 ())))
if (addr->ssu && (!addr->IsSSU2 () || isSSU2Published) &&
((v4 && addr->IsV4 ()) || (v6 && addr->IsV6 ())))
{
addr->published = true;
addr->caps |= i2p::data::RouterInfo::eSSUIntroducer;
addr->ssu->introducers.clear ();
port = addr->port;
if (addr->port && (!addr->IsSSU2 () || IsSSU2Only ()))
port = addr->port;
}
// publish NTCP2
bool ntcp2; i2p::config::GetOption("ntcp2.enabled", ntcp2);
@@ -652,16 +781,17 @@ namespace i2p
}
port = addr->port;
}
if (!port) i2p::config::GetOption("port", port);
if (!port)
{
i2p::config::GetOption("port", port);
if (!port) port = SelectRandomPort ();
}
// SSU
if (!foundSSU)
bool ssu; i2p::config::GetOption("ssu", ssu);
if (!foundSSU && ssu)
{
bool ssu; i2p::config::GetOption("ssu", ssu);
if (ssu)
{
std::string host = "::1"; // TODO: read host
m_RouterInfo.AddSSUAddress (host.c_str (), port, nullptr);
}
std::string host = "::1"; // TODO: read host
m_RouterInfo.AddSSUAddress (host.c_str (), port, nullptr);
}
// NTCP2
if (!foundNTCP2)
@@ -695,6 +825,7 @@ namespace i2p
if (ssu2Published)
{
uint16_t ssu2Port; i2p::config::GetOption ("ssu2.port", ssu2Port);
if (!ssu2Port) ssu2Port = ssu ? (port + 1) : port;
m_RouterInfo.AddSSU2Address (m_SSU2Keys->staticPublicKey, m_SSU2Keys->intro, boost::asio::ip::address::from_string ("::1"), ssu2Port);
}
else
@@ -740,14 +871,16 @@ namespace i2p
}
if (addr->port) port = addr->port;
}
if (!port) i2p::config::GetOption("port", port);
if (!port)
{
i2p::config::GetOption("port", port);
if (!port) port = SelectRandomPort ();
}
// SSU
if (!foundSSU)
{
bool ssu; i2p::config::GetOption("ssu", ssu);
if (ssu)
m_RouterInfo.AddSSUAddress (host.c_str (), port, nullptr);
}
bool ssu; i2p::config::GetOption("ssu", ssu);
if (!foundSSU && ssu)
m_RouterInfo.AddSSUAddress (host.c_str (), port, nullptr);
// NTCP2
if (!foundNTCP2)
{
@@ -775,10 +908,11 @@ namespace i2p
if (ssu2Published)
{
uint16_t ssu2Port; i2p::config::GetOption ("ssu2.port", ssu2Port);
if (!ssu2Port) ssu2Port = ssu ? (port + 1) : port;
m_RouterInfo.AddSSU2Address (m_SSU2Keys->staticPublicKey, m_SSU2Keys->intro, boost::asio::ip::address::from_string ("127.0.0.1"), ssu2Port);
}
else
m_RouterInfo.AddSSU2Address (m_SSU2Keys->staticPublicKey, m_SSU2Keys->intro, i2p::data::RouterInfo::eV6);
m_RouterInfo.AddSSU2Address (m_SSU2Keys->staticPublicKey, m_SSU2Keys->intro, i2p::data::RouterInfo::eV4);
}
}
m_RouterInfo.EnableV4 ();
@@ -815,6 +949,43 @@ namespace i2p
UpdateRouterInfo ();
}
void RouterContext::SetMTU (int mtu, bool v4)
{
if (mtu < 1280 || mtu > 1500) return;
auto& addresses = m_RouterInfo.GetAddresses ();
for (auto& addr: addresses)
{
if (addr->ssu && ((v4 && addr->IsV4 ()) || (!v4 && addr->IsV6 ())))
{
if (!addr->IsSSU2 ()) // SSU1
{
// round to multiple of 16
if (v4)
{
if (mtu > 1484) mtu = 1484;
else
{
mtu -= 12;
mtu = (mtu >> 4) << 4;
mtu += 12;
}
}
else
{
if (mtu > 1488) mtu = 1488;
else
mtu = (mtu >> 4) << 4;
}
}
if (mtu)
{
addr->ssu->mtu = mtu;
LogPrint (eLogDebug, "Router: MTU for ", v4 ? "ipv4" : "ipv6", " address ", addr->host.to_string(), " is set to ", mtu);
}
}
}
}
void RouterContext::UpdateNTCP2V6Address (const boost::asio::ip::address& host)
{
bool isYgg = i2p::util::net::IsYggdrasilAddress (host);

View File

@@ -103,10 +103,14 @@ namespace garlic
uint64_t GetTransitBandwidthLimit () const { return (m_BandwidthLimit*m_ShareRatio)/100LL; };
RouterStatus GetStatus () const { return m_Status; };
void SetStatus (RouterStatus status);
void SetStatusSSU2 (RouterStatus status);
RouterError GetError () const { return m_Error; };
void SetError (RouterError error) { m_Status = eRouterStatusError; m_Error = error; };
RouterStatus GetStatusV6 () const { return m_StatusV6; };
void SetStatusV6 (RouterStatus status);
void SetStatusV6SSU2 (RouterStatus status);
RouterError GetErrorV6 () const { return m_ErrorV6; };
void SetErrorV6 (RouterError error) { m_StatusV6 = eRouterStatusError; m_ErrorV6 = error; };
int GetNetID () const { return m_NetID; };
void SetNetID (int netID) { m_NetID = netID; };
bool DecryptTunnelBuildRecord (const uint8_t * encrypted, uint8_t * data);
@@ -119,10 +123,15 @@ namespace garlic
void PublishSSU2Address (int port, bool publish, bool v4, bool v6);
void UpdateSSU2Address (bool enable);
void RemoveNTCPAddress (bool v4only = true); // delete NTCP address for older routers. TODO: remove later
void RemoveSSUAddress (); // delete SSU address for older routers
bool AddIntroducer (const i2p::data::RouterInfo::Introducer& introducer);
void RemoveIntroducer (const boost::asio::ip::udp::endpoint& e);
bool AddSSU2Introducer (const i2p::data::RouterInfo::Introducer& introducer, bool v4);
void RemoveSSU2Introducer (const i2p::data::IdentHash& h, bool v4);
void ClearSSU2Introducers (bool v4);
bool IsUnreachable () const;
void SetUnreachable (bool v4, bool v6);
void SetUnreachableSSU2 (bool v4, bool v6);
void SetReachable (bool v4, bool v6);
bool IsFloodfill () const { return m_IsFloodfill; };
void SetFloodfill (bool floodfill);
@@ -139,6 +148,7 @@ namespace garlic
void SetSupportsV6 (bool supportsV6);
void SetSupportsV4 (bool supportsV4);
void SetSupportsMesh (bool supportsmesh, const boost::asio::ip::address_v6& host);
void SetMTU (int mtu, bool v4);
i2p::crypto::NoiseSymmetricState& GetCurrentNoiseState () { return m_CurrentNoiseState; };
void UpdateNTCP2V6Address (const boost::asio::ip::address& host); // called from Daemon. TODO: remove
@@ -173,8 +183,10 @@ namespace garlic
void UpdateRouterInfo ();
void NewNTCP2Keys ();
void NewSSU2Keys ();
bool IsSSU2Only () const; // SSU2 and no SSU
bool Load ();
void SaveKeys ();
uint16_t SelectRandomPort () const;
bool DecryptECIESTunnelBuildRecord (const uint8_t * encrypted, uint8_t * data, size_t clearTextSize);
@@ -190,7 +202,7 @@ namespace garlic
uint64_t m_BandwidthLimit; // allowed bandwidth
int m_ShareRatio;
RouterStatus m_Status, m_StatusV6;
RouterError m_Error;
RouterError m_Error, m_ErrorV6;
int m_NetID;
std::mutex m_GarlicMutex;
std::unique_ptr<NTCP2PrivateKeys> m_NTCP2Keys;

View File

@@ -708,6 +708,7 @@ namespace data
{
auto addr = std::make_shared<Address>();
addr->transportStyle = eTransportSSU2;
addr->port = 0;
addr->caps = caps;
addr->date = 0;
addr->ssu.reset (new SSUExt ());
@@ -727,7 +728,7 @@ namespace data
addr->host = host;
addr->port = port;
addr->published = true;
addr->caps = 0;
addr->caps = i2p::data::RouterInfo::eSSUTesting | i2p::data::RouterInfo::eSSUIntroducer; // BC;
addr->date = 0;
addr->ssu.reset (new SSUExt ());
addr->ssu->mtu = 0;
@@ -979,7 +980,8 @@ namespace data
return GetAddress (
[key, isV6](std::shared_ptr<const RouterInfo::Address> address)->bool
{
return address->IsSSU2 () && !memcmp (address->s, key, 32) && address->IsV6 () == isV6;
return address->IsSSU2 () && !memcmp (address->s, key, 32) &&
((isV6 && address->IsV6 ()) || (!isV6 && address->IsV4 ()));
});
}
@@ -1065,6 +1067,17 @@ namespace data
});
}
bool RouterInfo::IsSSU2Introducer (bool v4) const
{
if (!(m_SupportedTransports & (v4 ? eSSU2V4 : eSSU2V6))) return false;
return (bool)GetAddress (
[v4](std::shared_ptr<const RouterInfo::Address> address)->bool
{
return (address->IsSSU2 ()) && address->IsIntroducer () &&
((v4 && address->IsV4 ()) || (!v4 && address->IsV6 ())) && !address->host.is_unspecified ();
});
}
void RouterInfo::SetUnreachableAddressesTransportCaps (uint8_t transports)
{
for (auto& addr: *m_Addresses)
@@ -1296,7 +1309,7 @@ namespace data
else
WriteString ("", s);
if (isPublished)
if (isPublished && !address.host.is_unspecified ())
{
WriteString ("host", properties);
properties << '=';
@@ -1399,7 +1412,7 @@ namespace data
properties << ';';
}
}
if (isPublished || (address.ssu && !address.IsSSU2 ()))
if ((isPublished || (address.ssu && !address.IsSSU2 ())) && address.port)
{
WriteString ("port", properties);
properties << '=';
@@ -1467,5 +1480,40 @@ namespace data
{
return std::make_shared<Buffer> ();
}
bool LocalRouterInfo::AddSSU2Introducer (const Introducer& introducer, bool v4)
{
for (auto& addr : GetAddresses ())
{
if (addr->IsSSU2 () && ((v4 && addr->IsV4 ()) || (!v4 && addr->IsV6 ())))
{
for (auto& intro: addr->ssu->introducers)
if (intro.iTag == introducer.iTag) return false; // already presented
addr->ssu->introducers.push_back (introducer);
SetReachableTransports (GetReachableTransports () | ((addr->IsV4 () ? eSSU2V4 : eSSU2V6)));
return true;
}
}
return false;
}
bool LocalRouterInfo::RemoveSSU2Introducer (const IdentHash& h, bool v4)
{
for (auto& addr: GetAddresses ())
{
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->iKey)
{
addr->ssu->introducers.erase (it);
if (addr->ssu->introducers.empty ())
SetReachableTransports (GetReachableTransports () & ~(addr->IsV4 () ? eSSU2V4 : eSSU2V6));
return true;
}
}
}
return false;
}
}
}

View File

@@ -235,6 +235,7 @@ namespace data
bool IsPeerTesting (bool v4) const;
bool IsSSU2PeerTesting (bool v4) const;
bool IsIntroducer (bool v4) const;
bool IsSSU2Introducer (bool v4) const;
uint8_t GetCaps () const { return m_Caps; };
void SetCaps (uint8_t caps) { m_Caps = caps; };
@@ -274,6 +275,8 @@ namespace data
void SetBufferLen (size_t len) { m_BufferLen = len; };
void RefreshTimestamp ();
const Addresses& GetAddresses () const { return *m_Addresses; };
CompatibleTransports GetReachableTransports () const { return m_ReachableTransports; };
void SetReachableTransports (CompatibleTransports transports) { m_ReachableTransports = transports; };
private:
@@ -316,6 +319,9 @@ namespace data
std::string GetProperty (const std::string& key) const;
void ClearProperties () override { m_Properties.clear (); };
bool AddSSU2Introducer (const Introducer& introducer, bool v4);
bool RemoveSSU2Introducer (const IdentHash& h, bool v4);
private:
void WriteToStream (std::ostream& s) const;

View File

@@ -6,6 +6,7 @@
* See full license text in LICENSE file at top of project tree
*/
#include <random>
#include "Log.h"
#include "RouterContext.h"
#include "Transports.h"
@@ -21,7 +22,9 @@ namespace transport
RunnableServiceWithWork ("SSU2"), m_ReceiveService ("SSU2r"),
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_TerminationTimer (GetService ()), m_ResendTimer (GetService ())
m_TerminationTimer (GetService ()), m_ResendTimer (GetService ()),
m_IntroducersUpdateTimer (GetService ()), m_IntroducersUpdateTimerV6 (GetService ()),
m_IsPublished (true), m_IsSyncClockFromPeers (true)
{
}
@@ -30,6 +33,8 @@ namespace transport
if (!IsRunning ())
{
StartIOService ();
i2p::config::GetOption ("ssu2.published", m_IsPublished);
i2p::config::GetOption("nettime.frompeers", m_IsSyncClockFromPeers);
bool found = false;
auto& addresses = i2p::context.GetRouterInfo ().GetAddresses ();
for (const auto& address: addresses)
@@ -44,8 +49,9 @@ namespace transport
if (ssu2Port) port = ssu2Port;
else
{
bool ssu; i2p::config::GetOption("ssu", ssu);
uint16_t p; i2p::config::GetOption ("port", p);
if (p) port = p;
if (p) port = ssu ? (p + 1) : p;
}
}
if (port)
@@ -59,6 +65,7 @@ namespace transport
{
Receive (m_SocketV4);
});
ScheduleIntroducersUpdateTimer (); // wait for 30 seconds and decide if we need introducers
}
if (address->IsV6 ())
{
@@ -69,6 +76,7 @@ namespace transport
{
Receive (m_SocketV6);
});
ScheduleIntroducersUpdateTimerV6 (); // wait for 30 seconds and decide if we need introducers
}
}
else
@@ -83,30 +91,56 @@ namespace transport
void SSU2Server::Stop ()
{
for (auto& it: m_Sessions)
if (IsRunning ())
{
m_TerminationTimer.cancel ();
m_ResendTimer.cancel ();
m_IntroducersUpdateTimer.cancel ();
m_IntroducersUpdateTimerV6.cancel ();
}
auto sessions = m_Sessions;
for (auto& it: sessions)
{
it.second->RequestTermination (eSSU2TerminationReasonRouterShutdown);
it.second->Done ();
}
if (context.SupportsV4 () || context.SupportsV6 ())
m_ReceiveService.Stop ();
m_SocketV4.close ();
m_SocketV6.close ();
StopIOService ();
m_Sessions.clear ();
m_SessionsByRouterHash.clear ();
m_PendingOutgoingSessions.clear ();
if (context.SupportsV4 () || context.SupportsV6 ())
m_ReceiveService.Stop ();
m_SocketV4.close ();
m_SocketV6.close ();
if (IsRunning ())
m_TerminationTimer.cancel ();
StopIOService ();
m_Relays.clear ();
m_Introducers.clear ();
m_IntroducersV6.clear ();
}
void SSU2Server::SetLocalAddress (const boost::asio::ip::address& localAddress)
{
if (localAddress.is_unspecified ()) return;
if (localAddress.is_v4 ())
{
m_AddressV4 = localAddress;
int mtu = i2p::util::net::GetMTU (localAddress);
if (mtu < (int)SSU2_MIN_PACKET_SIZE) mtu = SSU2_MIN_PACKET_SIZE;
if (mtu > (int)SSU2_MAX_PACKET_SIZE) mtu = SSU2_MAX_PACKET_SIZE;
i2p::context.SetMTU (mtu, true);
}
else if (localAddress.is_v6 ())
{
m_AddressV6 = localAddress;
int maxMTU = i2p::util::net::GetMaxMTU (localAddress.to_v6 ());
int mtu = i2p::util::net::GetMTU (localAddress);
if (mtu > maxMTU) mtu = maxMTU;
if (mtu < (int)SSU2_MIN_PACKET_SIZE) mtu = SSU2_MIN_PACKET_SIZE;
i2p::context.SetMTU (mtu, false);
}
}
bool SSU2Server::IsSupported (const boost::asio::ip::address& addr) const
@@ -123,6 +157,14 @@ namespace transport
}
return false;
}
uint16_t SSU2Server::GetPort (bool v4) const
{
boost::system::error_code ec;
boost::asio::ip::udp::endpoint ep = v4 ? m_SocketV4.local_endpoint (ec) : m_SocketV6.local_endpoint (ec);
if (ec) return 0;
return ep.port ();
}
boost::asio::ip::udp::socket& SSU2Server::OpenSocket (const boost::asio::ip::udp::endpoint& localEndpoint)
{
@@ -148,7 +190,7 @@ namespace transport
void SSU2Server::Receive (boost::asio::ip::udp::socket& socket)
{
Packet * packet = m_PacketsPool.AcquireMt ();
socket.async_receive_from (boost::asio::buffer (packet->buf, SSU2_MTU), packet->from,
socket.async_receive_from (boost::asio::buffer (packet->buf, SSU2_MAX_PACKET_SIZE), packet->from,
std::bind (&SSU2Server::HandleReceivedFrom, this, std::placeholders::_1, std::placeholders::_2, packet, std::ref (socket)));
}
@@ -169,7 +211,7 @@ namespace transport
while (moreBytes && packets.size () < 32)
{
packet = m_PacketsPool.AcquireMt ();
packet->len = socket.receive_from (boost::asio::buffer (packet->buf, SSU2_MTU), packet->from, 0, ec);
packet->len = socket.receive_from (boost::asio::buffer (packet->buf, SSU2_MAX_PACKET_SIZE), packet->from, 0, ec);
if (!ec)
{
i2p::transport::transports.UpdateReceivedBytes (packet->len);
@@ -210,7 +252,8 @@ namespace transport
{
ProcessNextPacket (packet->buf, packet->len, packet->from);
m_PacketsPool.ReleaseMt (packet);
if (m_LastSession) m_LastSession->FlushData ();
if (m_LastSession && m_LastSession->GetState () != eSSU2SessionStateTerminated)
m_LastSession->FlushData ();
}
}
@@ -219,7 +262,8 @@ namespace transport
for (auto& packet: packets)
ProcessNextPacket (packet->buf, packet->len, packet->from);
m_PacketsPool.ReleaseMt (packets);
if (m_LastSession) m_LastSession->FlushData ();
if (m_LastSession && m_LastSession->GetState () != eSSU2SessionStateTerminated)
m_LastSession->FlushData ();
}
void SSU2Server::AddSession (std::shared_ptr<SSU2Session> session)
@@ -239,6 +283,8 @@ namespace transport
auto ident = it->second->GetRemoteIdentity ();
if (ident)
m_SessionsByRouterHash.erase (ident->GetIdentHash ());
if (m_LastSession == it->second)
m_LastSession = nullptr;
m_Sessions.erase (it);
}
}
@@ -254,9 +300,9 @@ namespace transport
if (!ret.second)
{
// session already exists
LogPrint (eLogWarning, "SSU2: Session to ", ident->GetIdentHash ().ToBase64 (), " aready exists");
LogPrint (eLogWarning, "SSU2: Session to ", ident->GetIdentHash ().ToBase64 (), " already exists");
// terminate existing
GetService ().post (std::bind (&SSU2Session::Terminate, ret.first->second));
GetService ().post (std::bind (&SSU2Session::RequestTermination, ret.first->second, eSSU2TerminationReasonReplacedByNewSession));
// update session
ret.first->second = session;
}
@@ -366,7 +412,11 @@ namespace transport
m_LastSession->ProcessData (buf, len);
break;
case eSSU2SessionStateSessionCreatedSent:
m_LastSession->ProcessSessionConfirmed (buf, len);
if (!m_LastSession->ProcessSessionConfirmed (buf, len))
{
m_LastSession->Done ();
m_LastSession = nullptr;
}
break;
case eSSU2SessionStateIntroduced:
if (m_LastSession->GetRemoteEndpoint ().address ().is_unspecified ())
@@ -377,7 +427,7 @@ namespace transport
{
LogPrint (eLogWarning, "SSU2: HolePunch endpoint ", senderEndpoint,
" doesn't match RelayResponse ", m_LastSession->GetRemoteEndpoint ());
m_LastSession->Terminate ();
m_LastSession->Done ();
m_LastSession = nullptr;
}
break;
@@ -385,6 +435,11 @@ namespace transport
m_LastSession->SetRemoteEndpoint (senderEndpoint);
m_LastSession->ProcessPeerTest (buf, len);
break;
case eSSU2SessionStateClosing:
m_LastSession->ProcessData (buf, len); // we might receive termintaion block
if (m_LastSession && m_LastSession->GetState () != eSSU2SessionStateTerminated)
m_LastSession->RequestTermination (eSSU2TerminationReasonIdleTimeout); // send termination again
break;
case eSSU2SessionStateTerminated:
m_LastSession = nullptr;
break;
@@ -459,10 +514,23 @@ namespace transport
{
if (router && address)
{
// check is no peding session
// check if no session
auto it = m_SessionsByRouterHash.find (router->GetIdentHash ());
if (it != m_SessionsByRouterHash.end ())
{
// session with router found, trying to send peer test if requested
if (peerTest && it->second->IsEstablished ())
{
auto session = it->second;
GetService ().post ([session]() { session->SendPeerTest (); });
}
return false;
}
// check is no pending session
bool isValidEndpoint = !address->host.is_unspecified () && address->port;
if (isValidEndpoint)
{
if (i2p::util::net::IsInReservedRange(address->host)) return false;
auto s = FindPendingOutgoingSession (boost::asio::ip::udp::endpoint (address->host, address->port));
if (s)
{
@@ -519,18 +587,27 @@ namespace transport
auto ts = i2p::util::GetSecondsSinceEpoch ();
std::shared_ptr<i2p::data::RouterInfo> r;
uint32_t relayTag = 0;
for (auto& it: address->ssu->introducers)
{
if (it.iTag && ts < it.iExp)
{
r = i2p::data::netdb.FindRouter (it.iKey);
if (r && r->IsReachableFrom (i2p::context.GetRouterInfo ()))
{
relayTag = it.iTag;
if (relayTag) break;
}
}
}
if (!address->ssu->introducers.empty ())
{
std::vector<int> indicies;
for (int i = 0; i < (int)address->ssu->introducers.size (); i++) indicies.push_back(i);
if (indicies.size () > 1)
std::shuffle (indicies.begin(), indicies.end(), std::mt19937(std::random_device()()));
for (auto i: indicies)
{
const auto& introducer = address->ssu->introducers[indicies[i]];
if (introducer.iTag && ts < introducer.iExp)
{
r = i2p::data::netdb.FindRouter (introducer.iKey);
if (r && r->IsReachableFrom (i2p::context.GetRouterInfo ()))
{
relayTag = introducer.iTag;
if (relayTag) break;
}
}
}
}
if (r)
{
if (relayTag)
@@ -539,7 +616,8 @@ namespace transport
auto addr = address->IsV6 () ? r->GetSSU2V6Address () : r->GetSSU2V4Address ();
if (addr)
{
bool isValidEndpoint = !addr->host.is_unspecified () && addr->port;
bool isValidEndpoint = !addr->host.is_unspecified () && addr->port &&
!i2p::util::net::IsInReservedRange(addr->host);
if (isValidEndpoint)
{
auto s = FindPendingOutgoingSession (boost::asio::ip::udp::endpoint (addr->host, addr->port));
@@ -588,8 +666,9 @@ namespace transport
else
s->SetOnEstablished ([s]() { s->SendPeerTest (); });
return true;
}
CreateSession (router, addr, true);
}
else
CreateSession (router, addr, true);
return true;
}
@@ -616,24 +695,38 @@ namespace transport
it++;
}
for (auto it = m_Sessions.begin (); it != m_Sessions.end ();)
for (auto it: m_Sessions)
{
if (it->second->GetState () == eSSU2SessionStateTerminated ||
it->second->IsTerminationTimeoutExpired (ts))
auto state = it.second->GetState ();
if (state == eSSU2SessionStateTerminated || state == eSSU2SessionStateClosing)
it.second->Done ();
else if (it.second->IsTerminationTimeoutExpired (ts))
{
if (it->second->IsEstablished ())
it->second->TerminateByTimeout ();
if (it->second == m_LastSession)
m_LastSession = nullptr;
it = m_Sessions.erase (it);
if (it.second->IsEstablished ())
it.second->RequestTermination (eSSU2TerminationReasonIdleTimeout);
else
it.second->Done ();
}
else
{
it->second->CleanUp (ts);
it++;
}
it.second->CleanUp (ts);
}
for (auto it = m_SessionsByRouterHash.begin (); it != m_SessionsByRouterHash.begin ();)
{
if (it->second && it->second->GetState () == eSSU2SessionStateTerminated)
it = m_SessionsByRouterHash.erase (it);
else
it++;
}
for (auto it = m_Relays.begin (); it != m_Relays.begin ();)
{
if (it->second && it->second->GetState () == eSSU2SessionStateTerminated)
it = m_Relays.erase (it);
else
it++;
}
for (auto it = m_IncomingTokens.begin (); it != m_IncomingTokens.end (); )
{
if (ts > it->second.second)
@@ -649,14 +742,16 @@ namespace transport
else
it++;
}
m_PacketsPool.CleanUpMt ();
m_SentPacketsPool.CleanUp ();
ScheduleTermination ();
}
}
void SSU2Server::ScheduleResend ()
{
m_ResendTimer.expires_from_now (boost::posix_time::seconds(SSU2_RESEND_INTERVAL));
m_ResendTimer.expires_from_now (boost::posix_time::milliseconds(SSU2_RESEND_CHECK_TIMEOUT));
m_ResendTimer.async_wait (std::bind (&SSU2Server::HandleResendTimer,
this, std::placeholders::_1));
}
@@ -665,7 +760,7 @@ namespace transport
{
if (ecode != boost::asio::error::operation_aborted)
{
auto ts = i2p::util::GetSecondsSinceEpoch ();
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
for (auto it: m_Sessions)
it.second->Resend (ts);
for (auto it: m_PendingOutgoingSessions)
@@ -711,5 +806,226 @@ namespace transport
m_IncomingTokens.emplace (ep, ret);
return ret;
}
std::list<std::shared_ptr<SSU2Session> > SSU2Server::FindIntroducers (int maxNumIntroducers,
bool v4, const std::set<i2p::data::IdentHash>& excluded) const
{
std::list<std::shared_ptr<SSU2Session> > ret;
for (const auto& s : m_Sessions)
{
if (s.second->IsEstablished () && (s.second->GetRelayTag () && s.second->IsOutgoing ()) &&
!excluded.count (s.second->GetRemoteIdentity ()->GetIdentHash ()) &&
((v4 && (s.second->GetRemoteTransports () & i2p::data::RouterInfo::eSSU2V4)) ||
(!v4 && (s.second->GetRemoteTransports () & i2p::data::RouterInfo::eSSU2V6))))
ret.push_back (s.second);
}
if ((int)ret.size () > maxNumIntroducers)
{
// shink ret randomly
int sz = ret.size () - maxNumIntroducers;
for (int i = 0; i < sz; i++)
{
auto ind = rand () % ret.size ();
auto it = ret.begin ();
std::advance (it, ind);
ret.erase (it);
}
}
return ret;
}
void SSU2Server::UpdateIntroducers (bool v4)
{
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
std::list<i2p::data::IdentHash> newList;
auto& introducers = v4 ? m_Introducers : m_IntroducersV6;
std::set<i2p::data::IdentHash> excluded;
for (const auto& it : introducers)
{
std::shared_ptr<SSU2Session> session;
auto it1 = m_SessionsByRouterHash.find (it);
if (it1 != m_SessionsByRouterHash.end ())
{
session = it1->second;
excluded.insert (it);
}
if (session && session->IsEstablished ())
{
if (ts < session->GetCreationTime () + SSU2_TO_INTRODUCER_SESSION_EXPIRATION)
session->SendKeepAlive ();
if (ts < session->GetCreationTime () + SSU2_TO_INTRODUCER_SESSION_DURATION)
newList.push_back (it);
else
session = nullptr;
}
if (!session)
i2p::context.RemoveSSU2Introducer (it, v4);
}
if (newList.size () < SSU2_MAX_NUM_INTRODUCERS)
{
auto sessions = FindIntroducers (SSU2_MAX_NUM_INTRODUCERS - newList.size (), v4, excluded);
if (sessions.empty () && !introducers.empty ())
{
// bump creation time for previous introducers if no new sessions found
LogPrint (eLogDebug, "SSU2: No new introducers found. Trying to reuse existing");
for (auto& it : introducers)
{
auto it1 = m_SessionsByRouterHash.find (it);
if (it1 != m_SessionsByRouterHash.end ())
{
auto session = it1->second;
if (session->IsEstablished ())
{
session->SetCreationTime (session->GetCreationTime () + SSU2_TO_INTRODUCER_SESSION_DURATION);
if (std::find (newList.begin (), newList.end (), it) == newList.end ())
{
newList.push_back (it);
sessions.push_back (session);
}
}
}
}
}
for (const auto& it : sessions)
{
i2p::data::RouterInfo::Introducer introducer;
introducer.iTag = it->GetRelayTag ();
introducer.iKey = it->GetRemoteIdentity ()->GetIdentHash ();
introducer.iExp = it->GetCreationTime () + SSU2_TO_INTRODUCER_SESSION_EXPIRATION;
excluded.insert (it->GetRemoteIdentity ()->GetIdentHash ());
if (i2p::context.AddSSU2Introducer (introducer, v4))
{
LogPrint (eLogDebug, "SSU2: Introducer added ", it->GetRelayTag (), " at ",
i2p::data::GetIdentHashAbbreviation (it->GetRemoteIdentity ()->GetIdentHash ()));
newList.push_back (it->GetRemoteIdentity ()->GetIdentHash ());
if (newList.size () >= SSU2_MAX_NUM_INTRODUCERS) break;
}
}
}
introducers = newList;
if (introducers.size () < SSU2_MAX_NUM_INTRODUCERS)
{
for (auto i = introducers.size (); i < SSU2_MAX_NUM_INTRODUCERS; i++)
{
auto introducer = i2p::data::netdb.GetRandomSSU2Introducer (v4, excluded);
if (introducer)
{
auto address = v4 ? introducer->GetSSU2V4Address () : introducer->GetSSU2V6Address ();
if (address)
{
CreateSession (introducer, address);
excluded.insert (introducer->GetIdentHash ());
}
}
else
{
LogPrint (eLogDebug, "SSU2: Can't find more introducers");
break;
}
}
}
}
void SSU2Server::ScheduleIntroducersUpdateTimer ()
{
if (m_IsPublished)
{
m_IntroducersUpdateTimer.expires_from_now (boost::posix_time::seconds(SSU2_KEEP_ALIVE_INTERVAL));
m_IntroducersUpdateTimer.async_wait (std::bind (&SSU2Server::HandleIntroducersUpdateTimer,
this, std::placeholders::_1, true));
}
}
void SSU2Server::RescheduleIntroducersUpdateTimer ()
{
if (m_IsPublished)
{
m_IntroducersUpdateTimer.cancel ();
i2p::context.ClearSSU2Introducers (true);
m_Introducers.clear ();
m_IntroducersUpdateTimer.expires_from_now (boost::posix_time::seconds(SSU2_KEEP_ALIVE_INTERVAL/2));
m_IntroducersUpdateTimer.async_wait (std::bind (&SSU2Server::HandleIntroducersUpdateTimer,
this, std::placeholders::_1, true));
}
}
void SSU2Server::ScheduleIntroducersUpdateTimerV6 ()
{
if (m_IsPublished)
{
m_IntroducersUpdateTimerV6.expires_from_now (boost::posix_time::seconds(SSU2_KEEP_ALIVE_INTERVAL));
m_IntroducersUpdateTimerV6.async_wait (std::bind (&SSU2Server::HandleIntroducersUpdateTimer,
this, std::placeholders::_1, false));
}
}
void SSU2Server::RescheduleIntroducersUpdateTimerV6 ()
{
if (m_IsPublished)
{
m_IntroducersUpdateTimerV6.cancel ();
i2p::context.ClearSSU2Introducers (false);
m_IntroducersV6.clear ();
m_IntroducersUpdateTimerV6.expires_from_now (boost::posix_time::seconds(SSU2_KEEP_ALIVE_INTERVAL/2));
m_IntroducersUpdateTimerV6.async_wait (std::bind (&SSU2Server::HandleIntroducersUpdateTimer,
this, std::placeholders::_1, false));
}
}
void SSU2Server::HandleIntroducersUpdateTimer (const boost::system::error_code& ecode, bool v4)
{
if (ecode != boost::asio::error::operation_aborted)
{
// timeout expired
if (v4)
{
if (i2p::context.GetStatus () == eRouterStatusTesting)
{
// we still don't know if we need introducers
ScheduleIntroducersUpdateTimer ();
return;
}
if (i2p::context.GetStatus () != eRouterStatusFirewalled)
{
// we don't need introducers
i2p::context.ClearSSU2Introducers (true);
m_Introducers.clear ();
return;
}
// we are firewalled
auto addr = i2p::context.GetRouterInfo ().GetSSU2V4Address ();
if (addr && addr->ssu && addr->ssu->introducers.empty ())
i2p::context.SetUnreachableSSU2 (true, false); // v4
UpdateIntroducers (true);
ScheduleIntroducersUpdateTimer ();
}
else
{
if (i2p::context.GetStatusV6 () == eRouterStatusTesting)
{
// we still don't know if we need introducers
ScheduleIntroducersUpdateTimerV6 ();
return;
}
if (i2p::context.GetStatusV6 () != eRouterStatusFirewalled)
{
// we don't need introducers
i2p::context.ClearSSU2Introducers (false);
m_IntroducersV6.clear ();
return;
}
// we are firewalled
auto addr = i2p::context.GetRouterInfo ().GetSSU2V6Address ();
if (addr && addr->ssu && addr->ssu->introducers.empty ())
i2p::context.SetUnreachableSSU2 (false, true); // v6
UpdateIntroducers (false);
ScheduleIntroducersUpdateTimerV6 ();
}
}
}
}
}

View File

@@ -17,15 +17,20 @@ namespace i2p
{
namespace transport
{
const int SSU2_TERMINATION_CHECK_TIMEOUT = 30; // 30 seconds
const int SSU2_TERMINATION_CHECK_TIMEOUT = 30; // in seconds
const int SSU2_RESEND_CHECK_TIMEOUT = 500; // in milliseconds
const size_t SSU2_SOCKET_RECEIVE_BUFFER_SIZE = 0x1FFFF; // 128K
const size_t SSU2_SOCKET_SEND_BUFFER_SIZE = 0x1FFFF; // 128K
const size_t SSU2_MAX_NUM_INTRODUCERS = 3;
const int SSU2_TO_INTRODUCER_SESSION_DURATION = 3600; // 1 hour
const int SSU2_TO_INTRODUCER_SESSION_EXPIRATION = 4800; // 80 minutes
const int SSU2_KEEP_ALIVE_INTERVAL = 30; // 30 seconds
class SSU2Server: private i2p::util::RunnableServiceWithWork
{
struct Packet
{
uint8_t buf[SSU2_MTU];
uint8_t buf[SSU2_MAX_PACKET_SIZE];
size_t len;
boost::asio::ip::udp::endpoint from;
};
@@ -50,6 +55,8 @@ namespace transport
boost::asio::io_service& GetService () { return GetIOService (); };
void SetLocalAddress (const boost::asio::ip::address& localAddress);
bool IsSupported (const boost::asio::ip::address& addr) const;
uint16_t GetPort (bool v4) const;
bool IsSyncClockFromPeers () const { return m_IsSyncClockFromPeers; };
void AddSession (std::shared_ptr<SSU2Session> session);
void RemoveSession (uint64_t connID);
@@ -79,7 +86,11 @@ namespace transport
uint64_t GetIncomingToken (const boost::asio::ip::udp::endpoint& ep);
std::pair<uint64_t, uint32_t> NewIncomingToken (const boost::asio::ip::udp::endpoint& ep);
void RescheduleIntroducersUpdateTimer ();
void RescheduleIntroducersUpdateTimerV6 ();
i2p::util::MemoryPool<SSU2SentPacket>& GetSentPacketsPool () { return m_SentPacketsPool; };
private:
boost::asio::ip::udp::socket& OpenSocket (const boost::asio::ip::udp::endpoint& localEndpoint);
@@ -97,21 +108,32 @@ namespace transport
void HandleResendTimer (const boost::system::error_code& ecode);
void ConnectThroughIntroducer (std::shared_ptr<SSU2Session> session);
std::list<std::shared_ptr<SSU2Session> > FindIntroducers (int maxNumIntroducers,
bool v4, const std::set<i2p::data::IdentHash>& excluded) const;
void UpdateIntroducers (bool v4);
void ScheduleIntroducersUpdateTimer ();
void HandleIntroducersUpdateTimer (const boost::system::error_code& ecode, bool v4);
void ScheduleIntroducersUpdateTimerV6 ();
private:
ReceiveService m_ReceiveService;
boost::asio::ip::udp::socket m_SocketV4, m_SocketV6;
boost::asio::ip::address m_AddressV4, m_AddressV6;
std::unordered_map<uint64_t, std::shared_ptr<SSU2Session> > m_Sessions;
std::map<i2p::data::IdentHash, std::shared_ptr<SSU2Session> > m_SessionsByRouterHash;
std::unordered_map<i2p::data::IdentHash, std::shared_ptr<SSU2Session> > m_SessionsByRouterHash;
std::map<boost::asio::ip::udp::endpoint, std::shared_ptr<SSU2Session> > m_PendingOutgoingSessions;
std::map<boost::asio::ip::udp::endpoint, std::pair<uint64_t, uint32_t> > m_IncomingTokens, m_OutgoingTokens; // remote endpoint -> (token, expires in seconds)
std::map<uint32_t, std::shared_ptr<SSU2Session> > m_Relays; // we are introducer, relay tag -> session
std::list<i2p::data::IdentHash> m_Introducers, m_IntroducersV6; // introducers we are connected to
i2p::util::MemoryPoolMt<Packet> m_PacketsPool;
boost::asio::deadline_timer m_TerminationTimer, m_ResendTimer;
i2p::util::MemoryPool<SSU2SentPacket> m_SentPacketsPool;
boost::asio::deadline_timer m_TerminationTimer, m_ResendTimer,
m_IntroducersUpdateTimer, m_IntroducersUpdateTimerV6;
std::shared_ptr<SSU2Session> m_LastSession;
bool m_IsPublished; // if we maintain introducers
bool m_IsSyncClockFromPeers;
public:
// for HTTP/I2PControl

File diff suppressed because it is too large Load Diff

View File

@@ -17,6 +17,7 @@
#include <boost/asio.hpp>
#include "Crypto.h"
#include "RouterInfo.h"
#include "RouterContext.h"
#include "TransportSession.h"
namespace i2p
@@ -25,19 +26,27 @@ namespace transport
{
const int SSU2_CONNECT_TIMEOUT = 5; // 5 seconds
const int SSU2_TERMINATION_TIMEOUT = 330; // 5.5 minutes
const int SSU2_CLOCK_SKEW = 60; // in seconds
const int SSU2_CLOCK_THRESHOLD = 15; // in seconds, if more we should adjust
const int SSU2_TOKEN_EXPIRATION_TIMEOUT = 9; // for Retry message, in seconds
const int SSU2_NEXT_TOKEN_EXPIRATION_TIMEOUT = 52*60; // for next token block, in seconds
const int SSU2_TOKEN_EXPIRATION_THRESHOLD = 2; // in seconds
const int SSU2_RELAY_NONCE_EXPIRATION_TIMEOUT = 10; // in seconds
const int SSU2_PEER_TEST_EXPIRATION_TIMEOUT = 60; // 60 seconds
const size_t SSU2_MTU = 1440; // TODO: should be 1456 for ipv4
const size_t SSU2_MAX_PAYLOAD_SIZE = SSU2_MTU - 32;
const int SSU2_HANDSHAKE_RESEND_INTERVAL = 1; // in seconds
const int SSU2_RESEND_INTERVAL = 3; // in seconds
const size_t SSU2_MAX_PACKET_SIZE = 1500;
const size_t SSU2_MIN_PACKET_SIZE = 1280;
const int SSU2_HANDSHAKE_RESEND_INTERVAL = 1000; // in millseconds
const int SSU2_RESEND_INTERVAL = 300; // in milliseconds
const int SSU2_MAX_NUM_RESENDS = 5;
const int SSU2_INCOMPLETE_MESSAGES_CLEANUP_TIMEOUT = 30; // in seconds
const size_t SSU2_MAX_WINDOW_SIZE = 128; // 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_MIN_RTO = 100; // in milliseconds
const size_t SSU2_MAX_RTO = 2500; // in milliseconds
const float SSU2_kAPPA = 1.8;
const size_t SSU2_MAX_OUTGOING_QUEUE_SIZE = 500; // in messages
const int SSU2_MAX_NUM_ACK_RANGES = 32; // to send
const uint8_t SSU2_MAX_NUM_FRAGMENTS = 64;
enum SSU2MessageType
{
@@ -82,9 +91,12 @@ namespace transport
eSSU2SessionStateUnknown,
eSSU2SessionStateTokenReceived,
eSSU2SessionStateSessionRequestSent,
eSSU2SessionStateSessionRequestReceived,
eSSU2SessionStateSessionCreatedSent,
eSSU2SessionStateSessionCreatedReceived,
eSSU2SessionStateSessionConfirmedSent,
eSSU2SessionStateEstablished,
eSSU2SessionStateClosing,
eSSU2SessionStateTerminated,
eSSU2SessionStateFailed,
eSSU2SessionStateIntroduced,
@@ -117,12 +129,39 @@ namespace transport
eSSU2RelayResponseCodeCharlieSignatureFailure = 67,
eSSU2RelayResponseCodeCharlieAliceIsUnknown = 70
};
enum SSU2TerminationReason
{
eSSU2TerminationReasonNormalClose = 0,
eSSU2TerminationReasonTerminationReceived = 1,
eSSU2TerminationReasonIdleTimeout = 2,
eSSU2TerminationReasonRouterShutdown = 3,
eSSU2TerminationReasonDataPhaseAEADFailure= 4,
eSSU2TerminationReasonIncompatibleOptions = 5,
eSSU2TerminationReasonTncompatibleSignatureType = 6,
eSSU2TerminationReasonClockSkew = 7,
eSSU2TerminationPaddingViolation = 8,
eSSU2TerminationReasonAEADFramingError = 9,
eSSU2TerminationReasonPayloadFormatError = 10,
eSSU2TerminationReasonSessionRequestError = 11,
eSSU2TerminationReasonSessionCreatedError = 12,
eSSU2TerminationReasonSessionConfirmedError = 13,
eSSU2TerminationReasonTimeout = 14,
eSSU2TerminationReasonRouterInfoSignatureVerificationFail = 15,
eSSU2TerminationReasonInvalidS = 16,
eSSU2TerminationReasonBanned = 17,
eSSU2TerminationReasonBadToken = 18,
eSSU2TerminationReasonConnectionLimits = 19,
eSSU2TerminationReasonIncompatibleVersion = 20,
eSSU2TerminationReasonWrongNetID = 21,
eSSU2TerminationReasonReplacedByNewSession = 22
};
struct SSU2IncompleteMessage
{
struct Fragment
{
uint8_t buf[SSU2_MTU];
uint8_t buf[SSU2_MAX_PACKET_SIZE];
size_t len;
bool isLast;
};
@@ -131,8 +170,18 @@ namespace transport
int nextFragmentNum;
uint32_t lastFragmentInsertTime; // in seconds
std::map<int, std::shared_ptr<Fragment> > outOfSequenceFragments;
void AttachNextFragment (const uint8_t * fragment, size_t fragmentSize);
};
struct SSU2SentPacket
{
uint8_t payload[SSU2_MAX_PACKET_SIZE];
size_t payloadSize = 0;
uint64_t sendTime; // in milliseconds
int numResends = 0;
};
// RouterInfo flags
const uint8_t SSU2_ROUTER_INFO_FLAG_REQUEST_FLOOD = 0x01;
const uint8_t SSU2_ROUTER_INFO_FLAG_GZIP = 0x02;
@@ -153,18 +202,14 @@ namespace transport
} h;
};
struct SentPacket
{
uint8_t payload[SSU2_MAX_PAYLOAD_SIZE];
size_t payloadSize = 0;
uint32_t nextResendTime; // in seconds
int numResends = 0;
};
struct HandshakePacket: public SentPacket
struct HandshakePacket
{
Header header;
uint8_t headerX[48]; // part1 for SessionConfirmed
uint8_t payload[SSU2_MAX_PACKET_SIZE*2];
size_t payloadSize = 0;
uint64_t sendTime = 0; // in milliseconds
bool isSecondFragment = false; // for SessionConfirmed
};
typedef std::function<void ()> OnEstablished;
@@ -186,8 +231,8 @@ namespace transport
bool Introduce (std::shared_ptr<SSU2Session> session, uint32_t relayTag);
void WaitForIntroduction ();
void SendPeerTest (); // Alice, Data message
void Terminate ();
void TerminateByTimeout ();
void SendKeepAlive ();
void RequestTermination (SSU2TerminationReason reason);
void CleanUp (uint64_t ts);
void FlushData ();
void Done () override;
@@ -210,13 +255,16 @@ namespace transport
private:
void Terminate ();
void Established ();
void ScheduleConnectTimer ();
void HandleConnectTimer (const boost::system::error_code& ecode);
void PostI2NPMessages (std::vector<std::shared_ptr<I2NPMessage> > msgs);
bool SendQueue ();
void SendFragmentedMessage (std::shared_ptr<I2NPMessage> msg);
bool SendQueue (); // returns true if ack block was sent
bool SendFragmentedMessage (std::shared_ptr<I2NPMessage> msg);
void ResendHandshakePacket ();
void ConnectAfterIntroduction ();
void ProcessSessionRequest (Header& header, uint8_t * buf, size_t len);
void ProcessTokenRequest (Header& header, uint8_t * buf, size_t len);
@@ -229,15 +277,21 @@ namespace transport
uint32_t SendData (const uint8_t * buf, size_t len); // returns packet num
void SendQuickAck ();
void SendTermination ();
void SendHolePunch (uint32_t nonce, const boost::asio::ip::udp::endpoint& ep, const uint8_t * introKey);
void SendHolePunch (uint32_t nonce, const boost::asio::ip::udp::endpoint& ep, const uint8_t * introKey, uint64_t token);
void SendPeerTest (uint8_t msg, const uint8_t * signedData, size_t signedDataLen, const uint8_t * introKey); // PeerTest message
void SendPathResponse (const uint8_t * data, size_t len);
void HandlePayload (const uint8_t * buf, size_t len);
void HandleDateTime (const uint8_t * buf, size_t len);
void HandleAck (const uint8_t * buf, size_t len);
void HandleAckRange (uint32_t firstPacketNum, uint32_t lastPacketNum);
void HandleAckRange (uint32_t firstPacketNum, uint32_t lastPacketNum, uint64_t ts);
void HandleAddress (const uint8_t * buf, size_t len);
bool ExtractEndpoint (const uint8_t * buf, size_t size, boost::asio::ip::udp::endpoint& ep);
size_t CreateEndpoint (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& ep);
std::shared_ptr<const i2p::data::RouterInfo::Address> FindLocalAddress () const;
void AdjustMaxPayloadSize ();
RouterStatus GetRouterStatus () const;
void SetRouterStatus (RouterStatus status) const;
std::shared_ptr<const i2p::data::RouterInfo> ExtractRouterInfo (const uint8_t * buf, size_t size);
void CreateNonce (uint64_t seqn, uint8_t * nonce);
bool UpdateReceivePacketNum (uint32_t packetNum); // for Ack, returns false if duplicate
@@ -257,17 +311,18 @@ namespace transport
size_t CreateFirstFragmentBlock (uint8_t * buf, size_t len, std::shared_ptr<I2NPMessage> msg);
size_t CreateFollowOnFragmentBlock (uint8_t * buf, size_t len, std::shared_ptr<I2NPMessage> msg, uint8_t& fragmentNum, uint32_t msgID);
size_t CreateRelayIntroBlock (uint8_t * buf, size_t len, const uint8_t * introData, size_t introDataLen);
size_t CreateRelayResponseBlock (uint8_t * buf, size_t len, SSU2RelayResponseCode code, uint32_t nonce, bool endpoint); // add endpoint for Chralie and no endpoint for Bob
size_t CreateRelayResponseBlock (uint8_t * buf, size_t len, SSU2RelayResponseCode code, uint32_t nonce, uint64_t token, bool v4);
size_t CreatePeerTestBlock (uint8_t * buf, size_t len, uint8_t msg, SSU2PeerTestCode code, const uint8_t * routerHash, const uint8_t * signedData, size_t signedDataLen);
size_t CreatePeerTestBlock (uint8_t * buf, size_t len, uint32_t nonce); // Alice
size_t CreateTerminationBlock (uint8_t * buf, size_t len);
private:
SSU2Server& m_Server;
std::shared_ptr<i2p::crypto::X25519Keys> m_EphemeralKeys;
std::unique_ptr<i2p::crypto::NoiseSymmetricState> m_NoiseState;
std::unique_ptr<HandshakePacket> m_SessionConfirmedFragment1; // for Bob if applicable
std::unique_ptr<HandshakePacket> m_SentHandshakePacket; // SessionRequest or SessionCreated
std::unique_ptr<HandshakePacket> m_SessionConfirmedFragment; // for Bob if applicable or second fragment for Alice
std::unique_ptr<HandshakePacket> m_SentHandshakePacket; // SessionRequest, SessionCreated or SessionConfirmed
std::shared_ptr<const i2p::data::RouterInfo::Address> m_Address;
boost::asio::ip::udp::endpoint m_RemoteEndpoint;
i2p::data::RouterInfo::CompatibleTransports m_RemoteTransports; // for peer tests
@@ -276,17 +331,19 @@ namespace transport
uint8_t m_KeyDataSend[64], m_KeyDataReceive[64];
uint32_t m_SendPacketNum, m_ReceivePacketNum;
std::set<uint32_t> m_OutOfSequencePackets; // packet nums > receive packet num
std::map<uint32_t, std::shared_ptr<SentPacket> > 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::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::list<std::shared_ptr<I2NPMessage> > m_SendQueue;
i2p::I2NPMessagesHandler m_Handler;
bool m_IsDataReceived;
size_t m_WindowSize;
size_t m_WindowSize, m_RTT, m_RTO;
uint32_t m_RelayTag; // between Bob and Charlie
OnEstablished m_OnEstablished; // callback from Established
boost::asio::deadline_timer m_ConnectTimer;
SSU2TerminationReason m_TerminationReason;
size_t m_MaxPayloadSize;
};
inline uint64_t CreateHeaderMask (const uint8_t * kh, const uint8_t * nonce)

View File

@@ -19,17 +19,14 @@
#include "I2NPProtocol.h"
#include "Identity.h"
#include "RouterInfo.h"
#include "TransportSession.h"
namespace i2p
{
namespace transport
{
const size_t SSU_MTU_V4 = 1484;
const size_t SSU_MTU_V6 = 1488;
const size_t IPV4_HEADER_SIZE = 20;
const size_t IPV6_HEADER_SIZE = 40;
const size_t UDP_HEADER_SIZE = 8;
const size_t SSU_V4_MAX_PACKET_SIZE = SSU_MTU_V4 - IPV4_HEADER_SIZE - UDP_HEADER_SIZE; // 1456
const size_t SSU_V6_MAX_PACKET_SIZE = SSU_MTU_V6 - IPV6_HEADER_SIZE - UDP_HEADER_SIZE; // 1440
const int RESEND_INTERVAL = 3; // in seconds

View File

@@ -41,7 +41,6 @@ namespace transport
i2p::context.GetRouterInfo ().GetSSUAddress (true);
if (address) m_IntroKey = address->i;
}
m_CreationTime = i2p::util::GetSecondsSinceEpoch ();
}
SSUSession::~SSUSession ()

View File

@@ -103,8 +103,6 @@ namespace transport
void SendKeepAlive ();
uint32_t GetRelayTag () const { return m_RelayTag; };
const i2p::data::RouterInfo::IntroKey& GetIntroKey () const { return m_IntroKey; };
uint32_t GetCreationTime () const { return m_CreationTime; };
void SetCreationTime (uint32_t ts) { m_CreationTime = ts; }; // for introducers
void FlushData ();
void CleanUp (uint64_t ts);
@@ -167,7 +165,6 @@ namespace transport
i2p::crypto::AESKey m_SessionKey;
i2p::crypto::MACKey m_MacKey;
i2p::data::RouterInfo::IntroKey m_IntroKey;
uint32_t m_CreationTime; // seconds since epoch
SSUData m_Data;
bool m_IsDataReceived;
std::unique_ptr<SignedData> m_SignedData; // we need it for SessionConfirmed only

View File

@@ -24,6 +24,10 @@ namespace i2p
{
namespace transport
{
const size_t IPV4_HEADER_SIZE = 20;
const size_t IPV6_HEADER_SIZE = 40;
const size_t UDP_HEADER_SIZE = 8;
class SignedData
{
public:
@@ -64,7 +68,7 @@ namespace transport
std::stringstream m_Stream;
};
class TransportSession
{
public:
@@ -75,6 +79,7 @@ namespace transport
{
if (router)
m_RemoteIdentity = router->GetRouterIdentity ();
m_CreationTime = m_LastActivityTimestamp;
}
virtual ~TransportSession () {};
@@ -102,6 +107,9 @@ namespace transport
bool IsTerminationTimeoutExpired (uint64_t ts) const
{ return ts >= m_LastActivityTimestamp + GetTerminationTimeout (); };
uint32_t GetCreationTime () const { return m_CreationTime; };
void SetCreationTime (uint32_t ts) { m_CreationTime = ts; }; // for introducers
virtual uint32_t GetRelayTag () const { return 0; };
virtual void SendLocalRouterInfo (bool update = false) { SendI2NPMessages ({ CreateDatabaseStoreMsg () }); };
virtual void SendI2NPMessages (const std::vector<std::shared_ptr<I2NPMessage> >& msgs) = 0;
@@ -114,6 +122,7 @@ namespace transport
bool m_IsOutgoing;
int m_TerminationTimeout;
uint64_t m_LastActivityTimestamp;
uint32_t m_CreationTime; // seconds since epoch
};
}
}

View File

@@ -285,8 +285,8 @@ namespace transport
delete m_SSUServer;
m_SSUServer = nullptr;
}
if (m_SSUServer) DetectExternalIP ();
}
if (m_SSUServer || m_SSU2Server) DetectExternalIP ();
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));
@@ -453,7 +453,7 @@ namespace transport
peer.router = netdb.FindRouter (ident); // try to get new one from netdb
if (peer.router) // we have RI already
{
if (peer.numAttempts < 2) // NTCP2, 0 - ipv6, 1- ipv4
if (peer.numAttempts < 2) // NTCP2, 0 - ipv6, 1 - ipv4
{
if (m_NTCP2Server) // we support NTCP2
{
@@ -491,60 +491,12 @@ namespace transport
else
peer.numAttempts = 2; // switch to SSU
}
if (peer.numAttempts == 2 || peer.numAttempts == 3) // SSU
{
if (m_SSUServer)
{
std::shared_ptr<const RouterInfo::Address> address;
if (peer.numAttempts == 2) // SSU ipv6
{
if (context.GetRouterInfo ().IsSSUV6 () && peer.router->IsReachableBy (RouterInfo::eSSUV6))
{
address = peer.router->GetSSUV6Address ();
if (address && m_CheckReserved && i2p::util::net::IsInReservedRange(address->host))
address = nullptr;
}
peer.numAttempts++;
}
if (!address && peer.numAttempts == 3) // SSU ipv4
{
if (context.GetRouterInfo ().IsSSU (true) && peer.router->IsReachableBy (RouterInfo::eSSUV4))
{
address = peer.router->GetSSUAddress (true);
if (address && m_CheckReserved && i2p::util::net::IsInReservedRange(address->host))
address = nullptr;
}
peer.numAttempts++;
}
if (address && address->IsReachableSSU ())
{
if (m_SSUServer->CreateSession (peer.router, address))
return true;
}
}
else
peer.numAttempts += 2; // switch to Mesh
}
if (peer.numAttempts == 4) // Mesh
{
peer.numAttempts++;
if (m_NTCP2Server && context.GetRouterInfo ().IsMesh () && peer.router->IsMesh ())
{
auto address = peer.router->GetYggdrasilAddress ();
if (address)
{
auto s = std::make_shared<NTCP2Session> (*m_NTCP2Server, peer.router, address);
m_NTCP2Server->Connect (s);
return true;
}
}
}
if (peer.numAttempts == 5 || peer.numAttempts == 6) // SSU2
if (peer.numAttempts == 2 || peer.numAttempts == 3) // SSU2, 2 - ipv6, 3 - ipv4
{
if (m_SSU2Server)
{
std::shared_ptr<const RouterInfo::Address> address;
if (peer.numAttempts == 5) // SSU2 ipv6
if (peer.numAttempts == 2) // SSU2 ipv6
{
if (context.GetRouterInfo ().IsSSU2V6 () && peer.router->IsReachableBy (RouterInfo::eSSU2V6))
{
@@ -554,7 +506,7 @@ namespace transport
}
peer.numAttempts++;
}
if (!address && peer.numAttempts == 6) // SSU2 ipv4
if (!address && peer.numAttempts == 3) // SSU2 ipv4
{
if (context.GetRouterInfo ().IsSSU2V4 () && peer.router->IsReachableBy (RouterInfo::eSSU2V4))
{
@@ -571,7 +523,55 @@ namespace transport
}
}
else
peer.numAttempts += 2;
peer.numAttempts += 2; // switch to mesh
}
if (peer.numAttempts == 4) // Mesh
{
peer.numAttempts++;
if (m_NTCP2Server && context.GetRouterInfo ().IsMesh () && peer.router->IsMesh ())
{
auto address = peer.router->GetYggdrasilAddress ();
if (address)
{
auto s = std::make_shared<NTCP2Session> (*m_NTCP2Server, peer.router, address);
m_NTCP2Server->Connect (s);
return true;
}
}
}
if (peer.numAttempts == 5 || peer.numAttempts == 6) // SSU, 5 - ipv6, 6 - ipv4
{
if (m_SSUServer)
{
std::shared_ptr<const RouterInfo::Address> address;
if (peer.numAttempts == 5) // SSU ipv6
{
if (context.GetRouterInfo ().IsSSUV6 () && peer.router->IsReachableBy (RouterInfo::eSSUV6))
{
address = peer.router->GetSSUV6Address ();
if (address && m_CheckReserved && i2p::util::net::IsInReservedRange(address->host))
address = nullptr;
}
peer.numAttempts++;
}
if (!address && peer.numAttempts == 6) // SSU ipv4
{
if (context.GetRouterInfo ().IsSSU (true) && peer.router->IsReachableBy (RouterInfo::eSSUV4))
{
address = peer.router->GetSSUAddress (true);
if (address && m_CheckReserved && i2p::util::net::IsInReservedRange(address->host))
address = nullptr;
}
peer.numAttempts++;
}
if (address && address->IsReachableSSU ())
{
if (m_SSUServer->CreateSession (peer.router, address))
return true;
}
}
else
peer.numAttempts += 2;
}
LogPrint (eLogInfo, "Transports: No compatble NTCP2 or SSU addresses available");
i2p::data::netdb.SetUnreachable (ident, true); // we are here because all connection attempts failed
@@ -622,88 +622,109 @@ namespace transport
i2p::context.SetStatus (eRouterStatusOK);
return;
}
if (m_SSUServer)
if (m_SSUServer || m_SSU2Server)
PeerTest ();
else
LogPrint (eLogError, "Transports: Can't detect external IP. SSU is not available");
LogPrint (eLogWarning, "Transports: Can't detect external IP. SSU or SSU2 is not available");
}
void Transports::PeerTest (bool ipv4, bool ipv6)
{
if (RoutesRestricted() || !m_SSUServer) return;
if (RoutesRestricted() || (!m_SSUServer && !m_SSU2Server)) return;
if (ipv4 && i2p::context.SupportsV4 ())
{
LogPrint (eLogInfo, "Transports: Started peer test IPv4");
std::set<i2p::data::IdentHash> excluded;
excluded.insert (i2p::context.GetIdentHash ()); // don't pick own router
bool statusChanged = false;
for (int i = 0; i < 5; i++)
if (m_SSUServer)
{
auto router = i2p::data::netdb.GetRandomPeerTestRouter (true, excluded); // v4
if (router)
bool statusChanged = false;
for (int i = 0; i < 5; i++)
{
auto addr = router->GetSSUAddress (true); // ipv4
if (addr && !i2p::util::net::IsInReservedRange(addr->host))
auto router = i2p::data::netdb.GetRandomPeerTestRouter (true, excluded); // v4
if (router)
{
if (!statusChanged)
auto addr = router->GetSSUAddress (true); // ipv4
if (addr && !i2p::util::net::IsInReservedRange(addr->host))
{
statusChanged = true;
i2p::context.SetStatus (eRouterStatusTesting); // first time only
if (!statusChanged)
{
statusChanged = true;
i2p::context.SetStatus (eRouterStatusTesting); // first time only
}
m_SSUServer->CreateSession (router, addr, true); // peer test v4
}
m_SSUServer->CreateSession (router, addr, true); // peer test v4
excluded.insert (router->GetIdentHash ());
}
excluded.insert (router->GetIdentHash ());
}
if (!statusChanged)
LogPrint (eLogWarning, "Transports: Can't find routers for peer test IPv4");
}
if (!statusChanged)
LogPrint (eLogWarning, "Transports: Can't find routers for peer test IPv4");
// SSU2
if (m_SSU2Server)
{
excluded.clear ();
excluded.insert (i2p::context.GetIdentHash ());
auto router = i2p::data::netdb.GetRandomSSU2PeerTestRouter (true, excluded); // v4
if (router)
m_SSU2Server->StartPeerTest (router, true);
}
for (int i = 0; i < 3; i++)
{
auto router = i2p::data::netdb.GetRandomSSU2PeerTestRouter (true, excluded); // v4
if (router)
{
if (i2p::context.GetStatus () != eRouterStatusTesting)
i2p::context.SetStatusSSU2 (eRouterStatusTesting);
m_SSU2Server->StartPeerTest (router, true);
excluded.insert (router->GetIdentHash ());
}
}
}
}
if (ipv6 && i2p::context.SupportsV6 ())
{
LogPrint (eLogInfo, "Transports: Started peer test IPv6");
std::set<i2p::data::IdentHash> excluded;
excluded.insert (i2p::context.GetIdentHash ()); // don't pick own router
bool statusChanged = false;
for (int i = 0; i < 5; i++)
if (m_SSUServer)
{
auto router = i2p::data::netdb.GetRandomPeerTestRouter (false, excluded); // v6
if (router)
bool statusChanged = false;
for (int i = 0; i < 5; i++)
{
auto addr = router->GetSSUV6Address ();
if (addr && !i2p::util::net::IsInReservedRange(addr->host))
auto router = i2p::data::netdb.GetRandomPeerTestRouter (false, excluded); // v6
if (router)
{
if (!statusChanged)
auto addr = router->GetSSUV6Address ();
if (addr && !i2p::util::net::IsInReservedRange(addr->host))
{
statusChanged = true;
i2p::context.SetStatusV6 (eRouterStatusTesting); // first time only
if (!statusChanged)
{
statusChanged = true;
i2p::context.SetStatusV6 (eRouterStatusTesting); // first time only
}
m_SSUServer->CreateSession (router, addr, true); // peer test v6
}
m_SSUServer->CreateSession (router, addr, true); // peer test v6
excluded.insert (router->GetIdentHash ());
}
excluded.insert (router->GetIdentHash ());
}
if (!statusChanged)
LogPrint (eLogWarning, "Transports: Can't find routers for peer test IPv6");
}
if (!statusChanged)
LogPrint (eLogWarning, "Transports: Can't find routers for peer test IPv6");
// SSU2
if (m_SSU2Server)
{
excluded.clear ();
excluded.insert (i2p::context.GetIdentHash ());
auto router = i2p::data::netdb.GetRandomSSU2PeerTestRouter (false, excluded); // v6
if (router)
m_SSU2Server->StartPeerTest (router, false);
}
for (int i = 0; i < 3; i++)
{
auto router = i2p::data::netdb.GetRandomSSU2PeerTestRouter (false, excluded); // v6
if (router)
{
if (i2p::context.GetStatusV6 () != eRouterStatusTesting)
i2p::context.SetStatusV6SSU2 (eRouterStatusTesting);
m_SSU2Server->StartPeerTest (router, false);
excluded.insert (router->GetIdentHash ());
}
}
}
}
}
@@ -826,9 +847,9 @@ namespace transport
session->SendLocalRouterInfo (true);
it->second.nextRouterInfoUpdateTime = ts + PEER_ROUTER_INFO_UPDATE_INTERVAL +
rand () % PEER_ROUTER_INFO_UPDATE_INTERVAL_VARIANCE;
}
}
++it;
}
}
}
UpdateBandwidth (); // TODO: use separate timer(s) for it
bool ipv4Testing = i2p::context.GetStatus () == eRouterStatusTesting;

View File

@@ -93,6 +93,7 @@ namespace transport
void Stop ();
bool IsBoundSSU() const { return m_SSUServer != nullptr; }
bool IsBoundSSU2() const { return m_SSU2Server != nullptr; }
bool IsBoundNTCP2() const { return m_NTCP2Server != nullptr; }
bool IsOnline() const { return m_IsOnline; };

View File

@@ -12,6 +12,7 @@
#include "util.h"
#include "Log.h"
#include "I2PEndian.h"
#if not defined (__FreeBSD__)
#include <pthread.h>
@@ -35,7 +36,7 @@
#define MALLOC(x) HeapAlloc(GetProcessHeap(), 0, (x))
#define FREE(x) HeapFree(GetProcessHeap(), 0, (x))
// inet_pton exists Windows since Vista, but XP doesn't have that function!
// inet_pton and inet_ntop have been in Windows since Vista, but XP doesn't have these functions!
// This function was written by Petar Korponai?. See http://stackoverflow.com/questions/15660203/inet-pton-identifier-not-found
int inet_pton_xp (int af, const char *src, void *dst)
{
@@ -61,6 +62,29 @@ int inet_pton_xp (int af, const char *src, void *dst)
}
return 0;
}
const char *inet_ntop_xp(int af, const void *src, char *dst, socklen_t size)
{
struct sockaddr_storage ss;
unsigned long s = size;
ZeroMemory(&ss, sizeof(ss));
ss.ss_family = af;
switch(af) {
case AF_INET:
((struct sockaddr_in *)&ss)->sin_addr = *(struct in_addr *)src;
break;
case AF_INET6:
((struct sockaddr_in6 *)&ss)->sin6_addr = *(struct in6_addr *)src;
break;
default:
return NULL;
}
/* cannot direclty use &size because of strict aliasing rules */
return (WSAAddressToString((struct sockaddr *)&ss, sizeof(ss), NULL, dst, &s) == 0)? dst : NULL;
}
#else /* !_WIN32 => UNIX */
#include <sys/types.h>
#ifdef ANDROID
@@ -133,27 +157,12 @@ namespace util
namespace net
{
#ifdef _WIN32
bool IsWindowsXPorLater ()
{
static bool isRequested = false;
static bool isXP = false;
if (!isRequested)
{
// request
OSVERSIONINFO osvi;
ZeroMemory(&osvi, sizeof(OSVERSIONINFO));
osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
GetVersionEx(&osvi);
isXP = osvi.dwMajorVersion <= 5;
isRequested = true;
}
return isXP;
}
int GetMTUWindowsIpv4 (sockaddr_in inputAddress, int fallback)
{
typedef const char *(* IPN)(int af, const void *src, char *dst, socklen_t size);
IPN inetntop = (IPN)GetProcAddress (GetModuleHandle ("ws2_32.dll"), "InetNtop");
if (!inetntop) inetntop = inet_ntop_xp; // use own implementation if not found
ULONG outBufLen = 0;
PIP_ADAPTER_ADDRESSES pAddresses = nullptr;
PIP_ADAPTER_ADDRESSES pCurrAddresses = nullptr;
@@ -172,7 +181,7 @@ namespace net
if(dwRetVal != NO_ERROR)
{
LogPrint(eLogError, "NetIface: GetMTU(): Enclosed GetAdaptersAddresses() call has failed");
LogPrint(eLogError, "NetIface: GetMTU: Enclosed GetAdaptersAddresses() call has failed");
FREE(pAddresses);
return fallback;
}
@@ -184,7 +193,7 @@ namespace net
pUnicast = pCurrAddresses->FirstUnicastAddress;
if(pUnicast == nullptr)
LogPrint(eLogError, "NetIface: GetMTU(): Not a unicast IPv4 address, this is not supported");
LogPrint(eLogError, "NetIface: GetMTU: Not a unicast IPv4 address, this is not supported");
for(int i = 0; pUnicast != nullptr; ++i)
{
@@ -192,8 +201,13 @@ namespace net
sockaddr_in* localInterfaceAddress = (sockaddr_in*) lpAddr;
if(localInterfaceAddress->sin_addr.S_un.S_addr == inputAddress.sin_addr.S_un.S_addr)
{
auto result = pAddresses->Mtu;
char addr[INET_ADDRSTRLEN];
inetntop(AF_INET, &(((struct sockaddr_in *)localInterfaceAddress)->sin_addr), addr, INET_ADDRSTRLEN);
auto result = pCurrAddresses->Mtu;
FREE(pAddresses);
pAddresses = nullptr;
LogPrint(eLogInfo, "NetIface: GetMTU: Using ", result, " bytes for IPv4 address ", addr);
return result;
}
pUnicast = pUnicast->Next;
@@ -201,19 +215,23 @@ namespace net
pCurrAddresses = pCurrAddresses->Next;
}
LogPrint(eLogError, "NetIface: GetMTU(): No usable unicast IPv4 addresses found");
LogPrint(eLogError, "NetIface: GetMTU: No usable unicast IPv4 addresses found");
FREE(pAddresses);
return fallback;
}
int GetMTUWindowsIpv6 (sockaddr_in6 inputAddress, int fallback)
{
typedef const char *(* IPN)(int af, const void *src, char *dst, socklen_t size);
IPN inetntop = (IPN)GetProcAddress (GetModuleHandle ("ws2_32.dll"), "InetNtop");
if (!inetntop) inetntop = inet_ntop_xp; // use own implementation if not found
ULONG outBufLen = 0;
PIP_ADAPTER_ADDRESSES pAddresses = nullptr;
PIP_ADAPTER_ADDRESSES pCurrAddresses = nullptr;
PIP_ADAPTER_UNICAST_ADDRESS pUnicast = nullptr;
if(GetAdaptersAddresses(AF_INET6, GAA_FLAG_INCLUDE_PREFIX, nullptr, pAddresses, &outBufLen)
if (GetAdaptersAddresses(AF_INET6, GAA_FLAG_INCLUDE_PREFIX, nullptr, pAddresses, &outBufLen)
== ERROR_BUFFER_OVERFLOW)
{
FREE(pAddresses);
@@ -224,23 +242,23 @@ namespace net
AF_INET6, GAA_FLAG_INCLUDE_PREFIX, nullptr, pAddresses, &outBufLen
);
if(dwRetVal != NO_ERROR)
if (dwRetVal != NO_ERROR)
{
LogPrint(eLogError, "NetIface: GetMTU(): Enclosed GetAdaptersAddresses() call has failed");
LogPrint(eLogError, "NetIface: GetMTU: Enclosed GetAdaptersAddresses() call has failed");
FREE(pAddresses);
return fallback;
}
bool found_address = false;
pCurrAddresses = pAddresses;
while(pCurrAddresses)
while (pCurrAddresses)
{
PIP_ADAPTER_UNICAST_ADDRESS firstUnicastAddress = pCurrAddresses->FirstUnicastAddress;
pUnicast = pCurrAddresses->FirstUnicastAddress;
if(pUnicast == nullptr)
LogPrint(eLogError, "NetIface: GetMTU(): Not a unicast IPv6 address, this is not supported");
if (pUnicast == nullptr)
LogPrint(eLogError, "NetIface: GetMTU: Not a unicast IPv6 address, this is not supported");
for(int i = 0; pUnicast != nullptr; ++i)
for (int i = 0; pUnicast != nullptr; ++i)
{
LPSOCKADDR lpAddr = pUnicast->Address.lpSockaddr;
sockaddr_in6 *localInterfaceAddress = (sockaddr_in6*) lpAddr;
@@ -255,9 +273,13 @@ namespace net
if (found_address)
{
auto result = pAddresses->Mtu;
char addr[INET6_ADDRSTRLEN];
inetntop(AF_INET6, &(((struct sockaddr_in6 *)localInterfaceAddress)->sin6_addr), addr, INET6_ADDRSTRLEN);
auto result = pCurrAddresses->Mtu;
FREE(pAddresses);
pAddresses = nullptr;
LogPrint(eLogInfo, "NetIface: GetMTU: Using ", result, " bytes for IPv6 address ", addr);
return result;
}
pUnicast = pUnicast->Next;
@@ -266,7 +288,7 @@ namespace net
pCurrAddresses = pCurrAddresses->Next;
}
LogPrint(eLogError, "NetIface: GetMTU(): No usable unicast IPv6 addresses found");
LogPrint(eLogError, "NetIface: GetMTU: No usable unicast IPv6 addresses found");
FREE(pAddresses);
return fallback;
}
@@ -298,7 +320,7 @@ namespace net
}
else
{
LogPrint(eLogError, "NetIface: GetMTU(): Address family is not supported");
LogPrint(eLogError, "NetIface: GetMTU: Address family is not supported");
return fallback;
}
}
@@ -423,6 +445,27 @@ namespace net
#endif
}
int GetMaxMTU (const boost::asio::ip::address_v6& localAddress)
{
uint32_t prefix = bufbe32toh (localAddress.to_bytes ().data ());
switch (prefix)
{
case 0x20010470:
case 0x260070ff:
// Hurricane Electric
return 1480;
break;
case 0x2a06a003:
case 0x2a06a004:
case 0x2a06a005:
// route48
return 1420;
break;
default: ;
}
return 1500;
}
static bool IsYggdrasilAddress (const uint8_t addr[16])
{
return addr[0] == 0x02 || addr[0] == 0x03;

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
*
@@ -218,6 +218,7 @@ namespace util
namespace net
{
int GetMTU (const boost::asio::ip::address& localAddress);
int GetMaxMTU (const boost::asio::ip::address_v6& localAddress); // check tunnel broker for ipv6 address
const boost::asio::ip::address GetInterfaceAddress (const std::string & ifname, bool ipv6=false);
boost::asio::ip::address_v6 GetYggdrasilAddress ();
bool IsLocalAddress (const boost::asio::ip::address& addr);

View File

@@ -16,8 +16,8 @@
#define MAKE_VERSION_NUMBER(a,b,c) ((a*100+b)*100+c)
#define I2PD_VERSION_MAJOR 2
#define I2PD_VERSION_MINOR 42
#define I2PD_VERSION_MICRO 1
#define I2PD_VERSION_MINOR 43
#define I2PD_VERSION_MICRO 0
#define I2PD_VERSION_PATCH 0
#ifdef GITVER
#define I2PD_VERSION GITVER
@@ -31,7 +31,7 @@
#define I2P_VERSION_MAJOR 0
#define I2P_VERSION_MINOR 9
#define I2P_VERSION_MICRO 54
#define I2P_VERSION_MICRO 55
#define I2P_VERSION_PATCH 0
#define I2P_VERSION MAKE_VERSION(I2P_VERSION_MAJOR, I2P_VERSION_MINOR, I2P_VERSION_MICRO)
#define I2P_VERSION_NUMBER MAKE_VERSION_NUMBER(I2P_VERSION_MAJOR, I2P_VERSION_MINOR, I2P_VERSION_MICRO)

View File

@@ -313,7 +313,7 @@ namespace proxy {
std::string full_url = m_RequestURL.to_string();
std::stringstream ss;
ss << tr("Host") << " " << m_RequestURL.host << " <font color=red>" << tr("already in router's addressbook") << "</font>. ";
ss << tr("Click here to update record:") << " <a href=\"" << full_url << (full_url.find('?') != std::string::npos ? "&i2paddresshelper=" : "?i2paddresshelper=");
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:" ) << " <a href=\"" << full_url << (full_url.find('?') != std::string::npos ? "&i2paddresshelper=" : "?i2paddresshelper=");
ss << jump << "&update=true\">" << tr("Continue") << "</a>.";
GenericProxyInfo(tr("Addresshelper found"), ss.str());
return true; /* request processed */
@@ -422,8 +422,8 @@ namespace proxy {
void HTTPReqHandler::ForwardToUpstreamProxy()
{
LogPrint(eLogDebug, "HTTPProxy: Forwarded to upstream");
// build http request
/* build http request */
m_ClientRequestURL = m_RequestURL;
LogPrint(eLogDebug, "HTTPProxy: ", m_ClientRequestURL.host);
m_ClientRequestURL.schema = "";
@@ -431,17 +431,17 @@ namespace proxy {
std::string origURI = m_ClientRequest.uri; // TODO: what do we need to change uri for?
m_ClientRequest.uri = m_ClientRequestURL.to_string();
// update User-Agent to ESR version of Firefox, same as Tor Browser below version 8, for non-HTTPS connections
/* update User-Agent to ESR version of Firefox, same as Tor Browser below version 8, for non-HTTPS connections */
if(m_ClientRequest.method != "CONNECT")
m_ClientRequest.UpdateHeader("User-Agent", "Mozilla/5.0 (Windows NT 6.1; rv:60.0) Gecko/20100101 Firefox/60.0");
m_ClientRequest.write(m_ClientRequestBuffer);
m_ClientRequestBuffer << m_recv_buf.substr(m_req_len);
// assume http if empty schema
/* assume http if empty schema */
if (m_ProxyURL.schema == "" || m_ProxyURL.schema == "http")
{
// handle upstream http proxy
/* handle upstream http proxy */
if (!m_ProxyURL.port) m_ProxyURL.port = 80;
if (m_ProxyURL.is_i2p())
{
@@ -449,9 +449,9 @@ namespace proxy {
auto auth = i2p::http::CreateBasicAuthorizationString (m_ProxyURL.user, m_ProxyURL.pass);
if (!auth.empty ())
{
// remove existing authorization if any
/* remove existing authorization if any */
m_ClientRequest.RemoveHeader("Proxy-");
// add own http proxy authorization
/* add own http proxy authorization */
m_ClientRequest.AddHeader("Proxy-Authorization", auth);
}
m_send_buf = m_ClientRequest.to_string();
@@ -470,7 +470,7 @@ namespace proxy {
}
else if (m_ProxyURL.schema == "socks")
{
// handle upstream socks proxy
/* handle upstream socks proxy */
if (!m_ProxyURL.port) m_ProxyURL.port = 9050; // default to tor default if not specified
boost::asio::ip::tcp::resolver::query q(m_ProxyURL.host, std::to_string(m_ProxyURL.port));
m_proxy_resolver.async_resolve(q, std::bind(&HTTPReqHandler::HandleUpstreamProxyResolved, this, std::placeholders::_1, std::placeholders::_2, [&](boost::asio::ip::tcp::endpoint ep) {
@@ -479,7 +479,7 @@ namespace proxy {
}
else
{
// unknown type, complain
/* unknown type, complain */
GenericProxyError(tr("unknown outproxy url"), m_ProxyURL.to_string());
}
}

View File

@@ -660,6 +660,12 @@ namespace client
void I2PServerTunnel::Stop ()
{
if (m_PortDestination)
m_PortDestination->ResetAcceptor ();
auto localDestination = GetLocalDestination ();
if (localDestination)
localDestination->StopAcceptingStreams ();
ClearHandlers ();
}

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
*
@@ -1478,14 +1478,21 @@ namespace client
auto session = FindSession (sessionID);
if (session)
{
i2p::data::IdentityEx dest;
dest.FromBase64 (destination);
if (session->Type == eSAMSessionTypeDatagram)
session->GetLocalDestination ()->GetDatagramDestination ()->
SendDatagramTo ((uint8_t *)eol, payloadLen, dest.GetIdentHash ());
else // raw
session->GetLocalDestination ()->GetDatagramDestination ()->
SendRawDatagramTo ((uint8_t *)eol, payloadLen, dest.GetIdentHash ());
auto localDest = session->GetLocalDestination ();
auto datagramDest = localDest ? localDest->GetDatagramDestination () : nullptr;
if (datagramDest)
{
i2p::data::IdentityEx dest;
dest.FromBase64 (destination);
if (session->Type == eSAMSessionTypeDatagram)
datagramDest->SendDatagramTo ((uint8_t *)eol, payloadLen, dest.GetIdentHash ());
else if (session->Type == eSAMSessionTypeRaw)
datagramDest->SendRawDatagramTo ((uint8_t *)eol, payloadLen, dest.GetIdentHash ());
else
LogPrint (eLogError, "SAM: Unexpected session type ", (int)session->Type, "for session ", sessionID);
}
else
LogPrint (eLogError, "SAM: Datagram destination is not set for session ", sessionID);
}
else
LogPrint (eLogError, "SAM: Session ", sessionID, " not found");