Compare commits

..

253 Commits

Author SHA1 Message Date
R4SAS
4e37df26a3 2.31.0
Signed-off-by: R4SAS <r4sas@i2pmail.org>
2020-04-10 20:33:54 +03:00
orignal
5e606573b1 2.31.0 2020-04-10 12:57:47 -04:00
r4sas
b3974cb52a [webconsole] security hardening headers (closes #1464)
Signed-off-by: r4sas <r4sas@i2pmail.org>
2020-04-10 02:34:47 +00:00
orignal
b7c206c44b replace by new incoming session 2020-04-09 15:00:38 -04:00
orignal
49c1e47736 correct termination if session already exists 2020-04-08 18:02:12 -04:00
orignal
4e1319d874 handle ECIESFlag in DatabaseLookup at floodfill 2020-04-07 11:40:18 -04:00
R4SAS
a9436aa9af drop i2lua
Signed-off-by: R4SAS <r4sas@i2pmail.org>
2020-04-03 14:31:38 +03:00
R4SAS
d503f07564 suppress GCC 7 (bug 77728) psabi note
Suppresses messages like that:
    note: parameter passing for argument of type <...> will change in GCC 7.1

Signed-off-by: R4SAS <r4sas@i2pmail.org>
2020-04-03 14:31:38 +03:00
orignal
aa7750bfd3 keep sending new session reply until first established session message received 2020-04-02 21:48:39 -04:00
orignal
8872d1f389 mutex for m_RemoteIdentity 2020-04-01 09:54:10 -04:00
orignal
f21af4068f preferred crypto type for Decrypt 2020-03-31 17:35:51 -04:00
orignal
f4ca6bbb52 fixed race with identity verifier 2020-03-30 19:27:10 -04:00
orignal
869d0156ce handle Ack request 2020-03-26 19:03:38 -04:00
orignal
744e893dce check message length 2020-03-23 18:09:57 -04:00
orignal
fe9ac10f02 generate new tags based on last received index 2020-03-22 21:21:12 -04:00
orignal
6fb80f226a reopen socked and restart receiver on exception 2020-03-22 08:14:20 -04:00
orignal
ff19bab800 set only key correctly 2020-03-21 16:21:51 -04:00
orignal
962c2160c7 set actual LeaseSet2 buffer size 2020-03-20 17:43:37 -04:00
R4SAS
168da33d8b add comma
Signed-off-by: R4SAS <r4sas@i2pmail.org>
2020-03-20 18:43:54 +03:00
R4SAS
b6b25dc9f3 update log messages
Signed-off-by: R4SAS <r4sas@i2pmail.org>
2020-03-20 17:51:55 +03:00
orignal
3ca17fdc03 support multiple encryption keys 2020-03-19 18:33:42 -04:00
R4SAS
2249708097 [webconsole] remove excess tag
Signed-off-by: R4SAS <r4sas@i2pmail.org>
2020-03-19 02:34:45 +03:00
R4SAS
2fcaa7d260 [webconsole] rework spoilers; print tags, leases, router info in table
Signed-off-by: R4SAS <r4sas@i2pmail.org>
2020-03-19 02:27:07 +03:00
orignal
f3b0e57a54 publish multiple encryption keys 2020-03-18 18:03:03 -04:00
orignal
5da92437a1 set msg type for deliverystatus 2020-03-16 16:41:07 -04:00
orignal
b5bc05ac2b delete unconfirmed LeaseSet and DeliveryStatus 2020-03-14 16:35:34 -04:00
orignal
45145fa50a add ECIESX25519AEADRatchet session to delivery status 2020-03-14 09:33:48 -04:00
R4SAS
dd8200e8b0 cleanup websocks
Signed-off-by: R4SAS <r4sas@i2pmail.org>
2020-03-12 03:50:21 +03:00
R4SAS
2f56547d5f Merge pull request #1477 from PurpleI2P/drop-websockets
drop websockets support
2020-03-11 12:37:38 +00:00
orignal
82bdcfbbcb Merge pull request #1491 from nonlinear-chaos-order-etc-etal/openssl
qt: delayed save in background. should be better ui experience
2020-03-10 11:39:07 -04:00
user
0e38e43315 some qt work. fixed on slow computers; now faster as delayed save is implemented 2020-03-10 23:22:49 +08:00
user
63746be4d5 resolve i2pd_qt.pro conflict 2020-03-10 21:50:57 +08:00
user
ee73ee365f some work on qt 2020-03-10 21:49:04 +08:00
orignal
3c53479864 update LeaseSet for ECIESX25519AEADRatchet sessions 2020-03-08 20:58:59 -04:00
orignal
4adc741de3 send DeliveryStatusMsg for LeaseSet 2020-03-08 18:13:41 -04:00
orignal
64da62dbe6 alsways store latest symmkey 2020-03-07 18:46:40 -05:00
orignal
dd9b5faa5c fixed crash on termination 2020-03-05 18:44:15 -05:00
R4SAS
51d018acc6 webconsole: add stream closing
Signed-off-by: R4SAS <r4sas@i2pmail.org>
2020-03-05 04:14:39 +03:00
orignal
5eec580727 delete strem from destination upon termination 2020-03-04 18:31:22 -05:00
orignal
1e9a53da3f delete stream by id for HTTP interface 2020-03-04 15:54:09 -05:00
R4SAS
8dae044600 Merge pull request #1441 from noraj/patch-1
README: explicit linux distro supported
2020-03-03 18:07:01 +00:00
orignal
2d3fad2cdb correct proxy buffers 2020-03-02 16:24:00 -05:00
orignal
a59a8f62ca Merge pull request #1479 from wipedlifepotato/openssl
Pre init webview button. (android)
2020-03-01 21:17:14 -05:00
potatowipedlifereverse
35cfa7d9fb Merge branch 'openssl' of github.com:wipedlifepotato/i2pd into openssl 2020-03-02 05:06:06 +03:00
potatowipedlifereverse
c6ccb373a2 del geti2pdpath 2020-03-02 05:04:37 +03:00
wipedlifepotato
b6368170ed Update README.md 2020-03-02 04:00:28 +03:00
potatowipedlifereverse
35e8424293 preinit webview+configparser+README.md
tabulation fixes

configparser to comments

dont need

pre init webview

readme changes

delete submodules

webview in main menu

webview pre init

delete modules

delete submodules
2020-03-02 03:58:50 +03:00
orignal
e969d58689 handle ntcp2.proxy parameter 2020-03-01 15:11:54 -05:00
orignal
ae20e3aa95 NTCP2 proxy 2020-03-01 11:24:18 -05:00
orignal
de4cb74173 Merge pull request #1478 from unlnown542a/openssl
add ntcp2proxy support
2020-03-01 10:04:27 -05:00
unlnown542a
4f0da87a7a add ntcp2proxy support 2020-03-01 14:35:24 +00:00
r4sas
97f0347715 Update android stuff:
* switch to c++17
* use boost 1.72.0
* disable minify in release
* enable apk splitting (separate apk for every ABI)
* add version to output apk name

Signed-off-by: r4sas <r4sas@i2pmail.org>
2020-02-29 23:14:35 +00:00
orignal
2ffe62ba41 [[fallthrough]] if C++17 only 2020-02-29 09:21:50 -05:00
R4SAS
fe1724e7e6 switch travis-ci to xenial 2020-02-28 23:41:42 +03:00
R4SAS
2ac2da41cf cmake: fix else statement 2020-02-28 23:28:41 +03:00
R4SAS
ed574f9d79 use C++17 if available when configuring with cmake 2020-02-28 23:05:26 +03:00
orignal
e0cb26bd9e fixed fallthough warning for C++17 2020-02-28 14:15:41 -05:00
orignal
1893127e84 use fold expression if C++17 2020-02-28 14:05:51 -05:00
orignal
b02c9fb118 enable C++17 for gcc 2020-02-28 14:03:08 -05:00
R4SAS
bca0809918 cleanup removed websockets funtions
Signed-off-by: R4SAS <r4sas@i2pmail.org>
2020-02-28 18:48:43 +03:00
R4SAS
00db527377 drop websockets support
Signed-off-by: R4SAS <r4sas@i2pmail.org>
2020-02-27 14:58:06 +03:00
R4SAS
2c6e041ae2 rpm: make package buildable on mageia cauldron (#1476) 2020-02-26 13:41:35 +00:00
R4SAS
a0d6c654cc 2.30.0
Signed-off-by: R4SAS <r4sas@i2pmail.org>
2020-02-25 20:08:50 +03:00
orignal
5115c27e72 2.30.0 2020-02-25 11:15:30 -05:00
R4SAS
d09c3ccb2d Merge pull request #1474 from rex4539/dead-strip
Pass -dead_strip -dead_strip_dylibs -bind_at_load on macOS
2020-02-24 18:16:20 +00:00
Dimitris Apostolou
5c308026ac Pass -dead_strip -dead_strip_dylibs -bind_at_load on macOS 2020-02-21 11:12:00 +02:00
orignal
91919c6d64 check if both sides are ECIESx25519 2020-02-20 21:07:45 -05:00
orignal
7168738835 check ctx for null 2020-02-20 21:05:07 -05:00
orignal
9c9b723cf5 delete expired ECIESX25519AEADRatchet sessions and tags 2020-02-20 15:44:09 -05:00
orignal
50450923df don't add extra , to result string 2020-02-19 20:51:32 -05:00
orignal
f392edd66c single thread SAM by default 2020-02-19 13:27:28 -05:00
orignal
24b48e5d50 reseeds update 2020-02-18 17:45:04 -05:00
orignal
47f384a0e0 postpone SAM destination termination 2020-02-17 15:14:35 -05:00
orignal
88594887f9 fixed qt build 2020-02-16 17:44:36 -05:00
orignal
32e2f0b1fa correct termination of streaming destination 2020-02-15 16:30:10 -05:00
orignal
09ed57ad42 select preferred crypto from LeaseSet2 2020-02-12 11:09:20 -05:00
orignal
53a6162b0c generate more receive tags when needed 2020-02-09 17:19:42 -05:00
orignal
694d851cdb Symmetric Key Ratchet 2020-02-08 21:51:02 -05:00
orignal
8e53c30a00 correct calls sequence for tag and index 2020-02-07 22:08:55 -05:00
orignal
63e807b0b4 fixed crash on stop 2020-02-06 10:53:45 -05:00
orignal
012f22cc47 create session tags for ECIESX25519 2020-02-05 15:48:51 -05:00
orignal
9d891ab5dd single thread mode for SAM 2020-02-04 15:31:04 -05:00
orignal
d0e78be867 moved io_service away from ClientDestination 2020-02-04 14:17:23 -05:00
orignal
cbedebc9dd change minimal MTU size 2020-02-04 13:32:16 -05:00
orignal
969f9aa436 common RuunableBase with private inheritance 2020-02-04 11:48:56 -05:00
orignal
b982be5ff5 handle existing session message 2020-02-03 16:21:07 -05:00
orignal
2d154ee640 move RunnableService away from LeaseSetDestination 2020-02-02 18:58:58 -05:00
orignal
49810eb153 common RunnableService 2020-02-02 17:05:30 -05:00
orignal
85b88b8749 second x25519 for new session reply 2020-01-30 19:30:30 -05:00
orignal
239c8b5172 destination delivery instructions 2020-01-30 11:48:32 -05:00
orignal
8c800dc178 save aepk from new session message 2020-01-29 21:57:10 -05:00
orignal
cdd068d99a correct message size 2020-01-29 19:27:38 -05:00
orignal
48fa10b080 incoming ECIESX25519AEADRatchet messages hanler 2020-01-29 15:54:11 -05:00
orignal
a1dbec0fcb handle new session reply 2020-01-29 12:54:26 -05:00
orignal
abe668f1c3 fixed build error 2020-01-28 10:31:35 -05:00
orignal
77440c235d replaced map by unordered_map 2020-01-28 10:03:51 -05:00
orignal
fd1ee48dbe datetime and padding blocks 2020-01-23 14:26:40 -05:00
orignal
205e807b66 reset keys 2020-01-22 21:42:30 -05:00
orignal
34295adb05 attach LeaseSet clove 2020-01-22 14:26:47 -05:00
orignal
7c212bef63 add new session to the list after reply received 2020-01-22 11:27:47 -05:00
orignal
76f95644b7 fixed #1461. Use openssl's HKDF for 1.1.1 anf higher 2020-01-22 09:59:08 -05:00
orignal
928b90d5bc fixed #1461. Use openssl's HKDF for 1.1.1 anf higher 2020-01-22 09:50:50 -05:00
orignal
09c6c2a4f3 decode aepk and bepk back 2020-01-21 21:09:19 -05:00
orignal
2b2bd733e9 correct sharedkey for new outgoing session 2020-01-21 19:13:23 -05:00
orignal
0d2d7e5e71 fixed Elligator tests 2020-01-21 17:53:48 -05:00
orignal
6142e93252 session tag for ECIESx25519 sessions 2020-01-21 14:40:23 -05:00
orignal
ccec3376ba try another ephemeral keys if elligator encoding failes 2020-01-21 12:19:20 -05:00
orignal
f497a74ec4 set random two highest bits 2020-01-21 12:18:31 -05:00
orignal
0e666e7d6a encoding fail test 2020-01-21 10:53:11 -05:00
orignal
f498fabd27 fix for openssl 1.1 2020-01-21 10:52:51 -05:00
orignal
8b49a55442 ratchet tagsets 2020-01-20 15:17:38 -05:00
orignal
a26eb942a9 Merge pull request #1458 from neheb/jjjjj
replace random_shuffle with shuffle
2020-01-20 07:16:00 -05:00
Rosen Penev
eabcafa516 replace random_shuffle with shuffle
random_shuffle is gone with C++17.

Found and fixed with clang-tidy.
2020-01-19 18:09:04 -08:00
orignal
6cc388c1bc use HKDF for MixKey 2020-01-18 14:43:36 -05:00
orignal
62e39ddfbd new session reply 2020-01-17 14:11:15 -05:00
orignal
80373623cd create payload 2020-01-17 11:21:41 -05:00
orignal
451c3945f0 create new ECIESX25519AEADRatchet session if not found 2020-01-16 19:33:00 -05:00
orignal
00cb15d9b4 fixed tyypo 2020-01-16 18:03:51 -05:00
orignal
67dd59125e new outgoing ECIESX25519AEADRatchet session 2020-01-16 16:34:13 -05:00
orignal
b6800dd125 lookup ECIESX25519AEADRatchet session by static key 2020-01-16 15:45:22 -05:00
orignal
dc9da69509 derive ECIESX25519AEADRatchetSession from GarlicRoutingSession 2020-01-16 14:59:19 -05:00
orignal
d7d964bf57 GarlicRoutingSession/ElGamalAESSession split 2020-01-16 14:31:01 -05:00
orignal
bcfe44db54 handle tunnel delivery instructioin for ECIESx25519 2020-01-16 12:47:08 -05:00
orignal
376bf6ba72 correct message size for ECIESx25519 2020-01-15 19:22:42 -05:00
orignal
f651baab25 ECIESX25519AEADRatchetSession added 2020-01-15 15:13:43 -05:00
orignal
61752e2aab correct ciphertext length 2020-01-13 22:37:31 -05:00
orignal
b7d3fd959e Merge pull request #1454 from rex4539/dead-strip
Pass -dead_strip -dead_strip_dylibs -bind_at_load on macOS
2020-01-13 09:17:05 -05:00
Dimitris Apostolou
7ac05f8487 Pass -dead_strip -dead_strip_dylibs -bind_at_load on macOS 2020-01-13 14:47:15 +02:00
orignal
4f70822b13 always persist crypto keys for public destinations 2020-01-12 10:03:30 -05:00
orignal
0007f304d0 don't pass from to HandleI2NPMessage 2020-01-07 15:20:55 -05:00
orignal
4afef91359 invoke HandleCloveI2NPMessage 2020-01-06 16:14:41 -05:00
orignal
815b6db0bf HandleCloveI2NPMessage 2020-01-06 15:31:20 -05:00
orignal
433d3bf582 Merge branch 'openssl' of https://github.com/PurpleI2P/i2pd into openssl 2020-01-06 14:38:13 -05:00
orignal
a335841509 pass msgID to HandleDeliveryStatus 2020-01-06 14:37:40 -05:00
r4sas
26ad793d82 use unscoped storage (fixes usage on android 10)
Signed-off-by: r4sas <r4sas@i2pmail.org>
2020-01-05 23:04:08 +00:00
orignal
5337aa10f7 check AES tag first 2020-01-02 13:30:54 -05:00
orignal
9f79bdae9b encryptor for ECIES-X25519-AEAD-Ratchet 2019-12-19 15:59:15 -05:00
orignal
db84be2488 use HKDF for NTCP2 key derivation data phase 2019-12-18 20:48:30 -05:00
orignal
599ec62bb0 use HKDF for NTCP2 key derivation data phase 2019-12-18 20:45:47 -05:00
orignal
19a88300c6 decrypt payload section 2019-12-18 14:44:02 -05:00
orignal
b5d55e1ffb decrypt flags/static section 2019-12-17 16:34:47 -05:00
orignal
521fb83e38 initial code for ECIES-X25519-AEAD-Ratchet KDF 2019-12-17 16:18:40 -05:00
orignal
553d59c32b decryptor for ECIES-X25519-AEAD-Ratchet 2019-12-11 13:38:36 -05:00
orignal
9ed58e5186 encode with highY 2019-12-10 14:10:12 -05:00
orignal
36eaaa748c handle case when encoded key is (p-1)/2 2019-12-10 13:40:04 -05:00
orignal
4d7b86ca26 elligator test added 2019-12-10 13:20:23 -05:00
orignal
5faf84c732 correct conversion from Little Endian 2019-12-10 12:51:39 -05:00
orignal
d7b819267f check a for 0 in Legendre 2019-12-10 10:53:39 -05:00
orignal
7417867d0f implemented Legendre 2019-12-10 10:45:08 -05:00
orignal
8d74905257 0.9.44 2019-12-10 10:44:19 -05:00
orignal
c38298c06e Elligator decode 2019-12-09 16:11:46 -05:00
orignal
3100d587d1 use d%q 2019-12-09 13:23:17 -05:00
orignal
ba849d0300 Merge branch 'openssl' of https://github.com/PurpleI2P/i2pd into openssl 2019-12-06 20:29:29 -05:00
orignal
95df3e4b39 encode key 2019-12-06 20:29:03 -05:00
R4SAS
72492e33a0 appveyor: drop msys2 overwrite 2019-12-07 01:02:23 +00:00
R4SAS
934f1269f5 appveyor: replace deprecated --force in msys2 2019-12-07 00:59:33 +00:00
orignal
e6956d9bb0 calculate constants 2019-12-06 14:54:15 -05:00
orignal
2877900233 use 486662 for A 2019-12-05 16:13:59 -05:00
orignal
df1aa52e08 Elligator's encode 2019-12-05 16:03:11 -05:00
orignal
5fa2485a7d removed reseed.i2p.net.in 2019-12-04 17:27:16 -05:00
orignal
29f0e10411 Elligator added 2019-12-04 15:37:24 -05:00
orignal
39300a5bbf removed reseed.i2p.net.in 2019-11-28 17:49:36 -05:00
Alexandre ZANNI
35d6268675 README: explicit linux distro supported
close #1440
2019-11-23 22:52:31 +01:00
orignal
0abb871f3f close socket after if accept failed 2019-11-20 13:00:50 -05:00
orignal
704fca969f handle accept errors 2019-11-20 12:05:32 -05:00
R4SAS
95debf8c80 update mingw build script
Signed-off-by: R4SAS <r4sas@i2pmail.org>
2019-11-19 14:29:55 +03:00
R4SAS
dd94b77b2a use GetTickCount dll pointer, add USE_WINXP to makefile
Signed-off-by: R4SAS <r4sas@i2pmail.org>
2019-11-19 13:51:14 +03:00
orignal
6cfe4fa580 handle sending errors 2019-11-18 14:13:31 -05:00
orignal
515c086099 Use GetProcAddress for inet_pton. Fixed build error 2019-11-12 15:06:04 -05:00
orignal
34ce06ac17 some cleanup 2019-11-12 14:19:14 -05:00
orignal
a104c9881e some cleanup 2019-11-12 11:57:34 -05:00
orignal
c3e3c091cc correct implementation of GetMTUWindows for WindowsXP 2019-11-12 11:35:59 -05:00
orignal
651240113c mark RI as unreachable if all connections failed 2019-11-12 10:03:33 -05:00
orignal
77189bf8e9 start over if an active session got disconnected 2019-11-12 09:38:22 -05:00
orignal
60fd3a4542 fixed #1434 use memset inster bzero 2019-11-12 07:17:57 -05:00
orignal
c66f9c8d6d reset connection attempts before reconnect 2019-11-12 06:46:08 -05:00
orignal
569088eaca 2.29.0 2019-10-21 12:02:43 -04:00
orignal
a7e8dd04fe 2.29.0 2019-10-21 11:50:59 -04:00
orignal
dfdd76a1bb fixed #1429. Don't use monotonic timer for Win32 2019-10-15 10:32:29 -04:00
R4SAS
28aac6f93b fix bogus date in changelogs
Signed-off-by: R4SAS <r4sas@i2pmail.org>
2019-10-07 21:18:46 +03:00
orignal
c2f47119ce fixed #1424. Check if .b32.i2p address string is valid 2019-09-23 13:42:15 -04:00
orignal
d6b1d0d4fb remove incoming session from pending list when established 2019-09-22 21:01:34 -04:00
orignal
03a861745b removed CloseSession 2019-09-20 20:09:25 -04:00
orignal
9a7aed20e9 handle error for SessionConfrimed send 2019-09-19 16:54:23 -04:00
orignal
b7f17d4cb1 client auth flag for B33 address 2019-09-06 11:02:19 -04:00
orignal
2497c3d187 Merge pull request #1421 from nonlinear-chaos-order-etc/openssl
Fixes three upnp issues, probably including: probably fixed #1419
2019-09-05 15:51:11 -04:00
kote
f7a084969a fixed #1387 2019-09-06 03:21:26 +08:00
kote
2900bc26a5 fixed #1388 : took code from 736c95a870 and fixed it as https://github.com/PurpleI2P/i2pd/issues/1388#issuecomment-528495918 tells 2019-09-06 02:58:28 +08:00
orignal
2334c56a96 Merge pull request #1414 from nonlinear-chaos-order-etc/openssl
qt: fixed logging to window in release builds
2019-08-27 12:40:19 -04:00
orignal
90a5d02bf6 2.28.0 2019-08-27 10:17:32 -04:00
kote
81d9626da9 qt: fixed logging to window in release builds 2019-08-27 21:56:36 +08:00
orignal
44a2549b81 2.28.0 2019-08-27 09:46:54 -04:00
orignal
a2b8d468bc Merge pull request #1412 from nonlinear-chaos-order-etc/openssl
qt: disabled broken UPnP + small qt debugging tweaks
2019-08-27 08:53:44 -04:00
kote
d523f0cadd gitignored autosave files by qtcreator 2019-08-27 19:31:57 +08:00
kote
99116ff097 qt: disabled upnp for now - until upnp fixed 2019-08-27 19:31:28 +08:00
kote
3939ca9eb4 enabled default logging debug option for qt debug builds 2019-08-27 17:51:55 +08:00
kote
b5aa67b491 tweaked debug logging in i2pd_qt 2019-08-27 17:10:53 +08:00
orignal
e42efec220 correct outet plain text length in case of authKeys 2019-08-26 07:35:11 -04:00
orignal
9d06aa2f6a pass authSalt or epk 2019-08-25 20:51:15 -04:00
orignal
80765a797b correct outer cipher text len 2019-08-25 19:14:53 -04:00
orignal
0b5509a1ed correct authClients offset 2019-08-25 14:54:43 -04:00
orignal
478d7b4a83 Merge pull request #1410 from nonlinear-chaos-order-etc/openssl
android various fixes and improvements
2019-08-24 07:31:12 -04:00
kote
9d3b38141a android various fixes and improv 2019-08-24 19:13:10 +08:00
orignal
ab3a4d902e Merge pull request #1409 from nonlinear-chaos-order-etc/openssl
android - show battery optimiz. os dialog menu item added; translated all battery stuff into Russian
2019-08-24 07:03:15 -04:00
kote
5eab5f2437 show battery optimiz. menu item now hidden if not supported by os 2019-08-24 18:00:11 +08:00
kote
80f632c19a show battery optimiz. menu item added; translated all battery stuff into Russian 2019-08-24 17:50:30 +08:00
kote
6e4f18543d added *.local to android/.gitignore 2019-08-24 17:04:04 +08:00
l-n-s
54586c9076 Merge pull request #1408 from docker-and-co/fix-dockerfile-non-existend-deps
fix dockerfile: remove unmet dependencies
2019-08-23 11:02:00 -04:00
orignal
351c899807 cleanup incoming streams on stop 2019-08-23 10:00:49 -04:00
Grigory Kotov
fe45d431d7 fix dockerfile: remove unmet dependencies 2019-08-23 16:20:09 +03:00
orignal
488c2f6d05 bump SDK version 2019-08-22 09:45:49 -04:00
orignal
75ab0909b3 Merge pull request #1405 from nonlinear-chaos-order-etc/openssl
various Android stuff. Fixed #1400
2019-08-22 07:15:36 -04:00
kote
8f82d563c1 various Android stuff. Fixed #1400 2019-08-22 10:00:50 +08:00
orignal
9bbce5dba6 fixed typo 2019-08-21 20:26:19 -04:00
R4SAS
099adab9ed Update README.md
update head badges links, add snapcraft badge
2019-08-16 21:45:44 +03:00
orignal
c8cbf425ac check and send netid for NTCP2 and SSU 2019-08-13 14:55:18 -04:00
orignal
ad9c11cd92 correct parsing of addreses containing # 2019-08-10 22:16:26 -04:00
orignal
3872c2a3f5 use published encrypted instead orig type 2019-08-07 16:18:00 -04:00
orignal
e6a09b49c9 published encrypted flag 2019-08-07 15:43:03 -04:00
orignal
db107602bd handle messages with \r\n 2019-08-02 13:48:39 -04:00
orignal
a6558a61a7 Recognize RedDSA_SHA512_Ed25519 signature type 2019-08-02 12:54:24 -04:00
orignal
254d2b82b3 fixed #1393. store streams by recvStreamID 2019-07-26 14:23:21 -04:00
orignal
97d9795fc9 pass encrepted LeaseSet auth keys 2019-07-16 16:31:17 -04:00
orignal
54071b0e5d set and handle unpublished LeaseSet flag 2019-07-16 11:48:30 -04:00
orignal
925e8316c7 read i2cp.leaseSetAuthType, i2cp.leaseSetClient.dh.nnn and i2cp.leaseSetClient.psk.nnn from tunnel config 2019-07-12 20:58:17 -04:00
orignal
99e1b74023 create encrypted LeaseSet2 with authentication 2019-07-12 15:40:59 -04:00
orignal
7d68ccca53 create encrypted LeaseSet2 with authentication 2019-07-12 15:37:32 -04:00
orignal
a090114066 send data message wih raw type fpr raw datagrams 2019-07-10 13:31:49 -04:00
orignal
a204841abb handle RAW SEND 2019-07-10 13:30:31 -04:00
orignal
cc451809cc send/receive raw datagrams through the SAM 2019-07-10 11:32:56 -04:00
orignal
a605e4bab6 send and recieve raw datagrams 2019-07-09 21:33:55 -04:00
orignal
3f0534134d check for malformed b33 2019-07-04 13:05:39 -04:00
orignal
3acfb129cd 2.27.0 2019-07-03 12:38:55 -04:00
orignal
6ccef66920 call shutdown before close 2019-06-26 10:47:16 -04:00
orignal
e9fa4e94a6 Merge pull request #1374 from rszibele/openssl
BOB: fix status command.
2019-06-25 18:28:43 -04:00
orignal
fecc0c4640 don't call destructor twice 2019-06-25 16:37:06 -04:00
rszibele
b759294975 BOB: fix status command. 2019-06-25 19:18:40 +02:00
rszibele
a23e845c03 BOB: improve comment and remove error log in list command 2019-06-25 19:04:27 +02:00
rszibele
cb8373e487 BOB: status response now correctly starts with "OK DATA". 2019-06-25 17:59:44 +02:00
orignal
8e919ddc8e use monotonic clock for uptime 2019-06-19 11:43:04 -04:00
orignal
832a9ab6b5 don't set random NTCP2 port if already set 2019-06-18 15:47:58 -04:00
orignal
13732ac333 fix #1363 try connect in SSU's thread 2019-06-14 15:43:03 -04:00
orignal
3e932a55f4 fixed typo 2019-06-11 15:09:10 -04:00
orignal
74e8610ec9 DH auth for encrypted LeaseSet2 2019-06-11 10:40:53 -04:00
orignal
089a60ded6 Merge pull request #1367 from rszibele/openssl
fix build on BSDs, as "isset" is a defined macro.
2019-06-10 19:10:34 -04:00
rszibele
c8eeefe194 fix build on BSDs, as "isset" is a defined macro. 2019-06-10 23:04:59 +02:00
orignal
85eeba14c1 Merge branch 'openssl' of https://github.com/PurpleI2P/i2pd into openssl 2019-06-08 21:24:16 -04:00
orignal
f6f45eab39 flood encrypted LeaseSet2 with store hash 2019-06-08 21:23:25 -04:00
R4SAS
a74065f775 [qt] dont build UnixDaemon.cpp 2019-06-09 01:04:43 +03:00
R4SAS
48d02f7e09 [qt] update headers, fix ChaCha source name, remove duplicates 2019-06-08 22:24:11 +03:00
R4SAS
e60549f8df [qt] fix build 2019-06-08 21:16:20 +03:00
orignal
41f4f4713e handle i2cp.leaseSetPrivKey 2019-06-07 14:51:08 -04:00
orignal
213a292fd5 correct offsets for auth data 2019-06-07 11:59:48 -04:00
133 changed files with 5181 additions and 3075 deletions

8
.gitignore vendored
View File

@@ -258,9 +258,15 @@ build/Makefile
# qt
qt/i2pd_qt/*.ui.autosave
qt/i2pd_qt/*.autosave
qt/i2pd_qt/*.ui.bk*
qt/i2pd_qt/*.ui_*
#unknown android stuff
android/libs/
#various logs
*LOGS/
qt/build-*.sh*

0
.gitmodules vendored Normal file
View File

View File

@@ -4,7 +4,7 @@ cache:
os:
- linux
#- osx
dist: trusty
dist: xenial
sudo: required
compiler:
- g++

View File

@@ -1,6 +1,77 @@
# for this file format description,
# see https://github.com/olivierlacan/keep-a-changelog
## [2.31.0] - 2020-04-10
### Added
- NTCP2 through HTTP proxy
- Publish LeaseSet2 for I2CP destinations
- Show status page on main activity for android
- Handle ECIESFlag in DatabaseLookup at floodfill
- C++17 features for eligible compilers
### Changed
- Droped Websockets and Lua support
- Send DeliveryStatusMsg for LeaseSet for ECIES-X25519-AEAD-Ratchet
- Keep sending new session reply until established for ECIES-X25519-AEAD-Ratchet
- Updated SSU log messages
- Reopen SSU socket on exception
- Security hardening headers in web console
- Various web console changes
- Various QT changes
### Fixed
- NTCP2 socket descriptors leak
- Race condition with router's identity in transport sessions
- Not terminated streams remain forever
## [2.30.0] - 2020-02-25
### Added
- Single threaded SAM
- Experimental support of ECIES-X25519-AEAD-Ratchet crypto type
### Changed
- Minimal MTU size is 1280 for ipv6
- Use unordered_map instead map for destination's sessions and tags list
- Use std::shuffle instead std::random_shuffle
- SAM is single threaded by default
- Reseeds list
### Fixed
- Correct termination of streaming destination
- Extra ',' in RouterInfo response in I2PControl
- SAM crash on session termination
- Storage for Android 10
## [2.29.0] - 2019-10-21
### Added
- Client auth flag for b33 address
### Changed
- Remove incoming NTCP2 session from pending list when established
- Handle errors for NTCP2 SessionConfrimed send
### Fixed
- Failure to start on Windows XP
- SAM crash if invalid lookup address
- Possible crash when UPnP enabled on shutdown
## [2.28.0] - 2019-08-27
### Added
- RAW datagrams in SAM
- Publishing encrypted LeaseSet2 with DH or PSH authentication
- Ability to disable battery optimization for Android
- Transport Network ID Check
### Changed
- Set and handle published encrypted flag for LeaseSet2
### Fixed
- ReceiveID changes in the same stream
- "\r\n" command terminator in SAM
- Addressbook lines with signatures
## [2.27.0] - 2019-07-03
### Added
- Support of PSK and DH authentication for encrypted LeaseSet2
### Changed
- Uptime is based on monotonic timer
### Fixed
- BOB status command response
- Correct NTCP2 port if NTCP is disabled
- Flood encrypted LeaseSet2 with store hash
## [2.26.0] - 2019-06-07
### Added
- HTTP method "PROPFIND"

View File

@@ -27,10 +27,6 @@ else
LD_DEBUG = -s
endif
ifeq ($(WEBSOCKETS),1)
NEEDED_CXXFLAGS += -DWITH_EVENTS
endif
ifneq (, $(findstring darwin, $(SYS)))
DAEMON_SRC += $(DAEMON_SRC_DIR)/UnixDaemon.cpp
ifeq ($(HOMEBREW),1)

View File

@@ -1,5 +1,5 @@
# set defaults instead redefine
CXXFLAGS ?= ${CXX_DEBUG} -Wall -Wextra -Wno-unused-parameter -pedantic -Wno-misleading-indentation
CXXFLAGS ?= ${CXX_DEBUG} -Wall -Wextra -Wno-unused-parameter -pedantic -Wno-misleading-indentation -Wno-psabi
LDFLAGS ?= ${LD_DEBUG}
## NOTE: The NEEDED_CXXFLAGS are here so that custom CXXFLAGS can be specified at build time
@@ -15,13 +15,14 @@ ifeq ($(shell expr match $(CXX) 'clang'),5)
NEEDED_CXXFLAGS += -std=c++11
else ifeq ($(shell expr match ${CXXVER} "4\.[0-9][0-9]"),4) # gcc >= 4.10
NEEDED_CXXFLAGS += -std=c++11
else ifeq ($(shell expr match ${CXXVER} "4\.[7-9]"),3) # >= 4.7
else ifeq ($(shell expr match ${CXXVER} "4\.[7-9]"),3) # gcc 4.7 - 4.9
NEEDED_CXXFLAGS += -std=c++11 -D_GLIBCXX_USE_NANOSLEEP=1
else ifeq ($(shell expr match ${CXXVER} "4\.6"),3) # = 4.6
NEEDED_CXXFLAGS += -std=c++0x
else ifeq ($(shell expr match ${CXXVER} "[5-9]"),1) # gcc >= 5
else ifeq ($(shell expr match ${CXXVER} "[5-6]"),1) # gcc 5 - 6
NEEDED_CXXFLAGS += -std=c++11
LDLIBS = -latomic
else ifeq ($(shell expr match ${CXXVER} "[7-9]"),1) # gcc >= 7
NEEDED_CXXFLAGS += -std=c++17
LDLIBS = -latomic
else # not supported
$(error Compiler too old)
endif

View File

@@ -37,6 +37,10 @@ ifeq ($(USE_WIN32_APP), yes)
DAEMON_OBJS += $(patsubst %.rc,obj/%.o,$(DAEMON_RC))
endif
ifeq ($(USE_WINXP_FLAGS), yes)
CXXFLAGS += -DWINVER=0x0501 -D_WIN32_WINNT=0x0501
endif
# don't change following line to ifeq ($(USE_AESNI),yes) !!!
ifeq ($(USE_AESNI),1)
CPU_FLAGS += -maes

View File

@@ -2,6 +2,9 @@ CXX = clang++
CXXFLAGS := ${CXX_DEBUG} -Wall -std=c++11 -DMAC_OSX
INCFLAGS = -I/usr/local/include
LDFLAGS := -Wl,-rpath,/usr/local/lib -L/usr/local/lib
LDFLAGS += -Wl,-dead_strip
LDFLAGS += -Wl,-dead_strip_dylibs
LDFLAGS += -Wl,-bind_at_load
ifeq ($(USE_STATIC),yes)
LDLIBS = -lz /usr/local/lib/libcrypto.a /usr/local/lib/libssl.a /usr/local/lib/libboost_system.a /usr/local/lib/libboost_date_time.a /usr/local/lib/libboost_filesystem.a /usr/local/lib/libboost_program_options.a -lpthread

View File

@@ -1,5 +1,7 @@
![GitHub release](https://img.shields.io/github/release/PurpleI2P/i2pd.svg?label=latest%20release)
![GitHub](https://img.shields.io/github/license/PurpleI2P/i2pd.svg)
[![GitHub release](https://img.shields.io/github/release/PurpleI2P/i2pd.svg?label=latest%20release)](https://github.com/PurpleI2P/i2pd/releases/latest)
[![Snapcraft release](https://snapcraft.io/i2pd/badge.svg)](https://snapcraft.io/i2pd)
[![License](https://img.shields.io/github/license/PurpleI2P/i2pd.svg)](https://github.com/PurpleI2P/i2pd/blob/openssl/LICENSE)
[![Packaging status](https://repology.org/badge/tiny-repos/i2pd.svg)](https://repology.org/project/i2pd/versions)
i2pd
====
@@ -62,10 +64,12 @@ Build instructions:
**Supported systems:**
* GNU/Linux - [![Build Status](https://travis-ci.org/PurpleI2P/i2pd.svg?branch=openssl)](https://travis-ci.org/PurpleI2P/i2pd)
* CentOS / Fedora / Mageia - [![Build Status](https://copr.fedorainfracloud.org/coprs/supervillain/i2pd/package/i2pd-git/status_image/last_build.png)](https://copr.fedorainfracloud.org/coprs/supervillain/i2pd/package/i2pd-git/)
* Alpine, ArchLinux, openSUSE, Gentoo, Debian, Ubuntu, etc.
* Windows - [![Build status](https://ci.appveyor.com/api/projects/status/1908qe4p48ff1x23?svg=true)](https://ci.appveyor.com/project/PurpleI2P/i2pd)
* Mac OS X - [![Build Status](https://travis-ci.org/PurpleI2P/i2pd.svg?branch=openssl)](https://travis-ci.org/PurpleI2P/i2pd)
* CentOS / Fedora / Mageia - [![Build Status](https://copr.fedorainfracloud.org/coprs/supervillain/i2pd/package/i2pd-git/status_image/last_build.png)](https://copr.fedorainfracloud.org/coprs/supervillain/i2pd/package/i2pd-git/)
* Docker image - [![Build Status](https://dockerbuildbadges.quelltext.eu/status.svg?organization=meeh&repository=i2pd)](https://hub.docker.com/r/meeh/i2pd/builds/)
* Snap - [![Snap Status](https://build.snapcraft.io/badge/PurpleI2P/i2pd-snap.svg)](https://build.snapcraft.io/user/PurpleI2P/i2pd-snap)
* FreeBSD
* Android
* iOS

View File

@@ -13,10 +13,6 @@
#include "Win32App.h"
#include <stdio.h>
#if defined(_MSC_VER) && _MSC_VER < 1900
#define snprintf _snprintf
#endif
#define ID_ABOUT 2000
#define ID_EXIT 2001
#define ID_CONSOLE 2002
@@ -39,6 +35,9 @@ namespace i2p
namespace win32
{
static DWORD GracefulShutdownEndtime = 0;
typedef DWORD (* IPN)();
IPN GetTickCountLocal = (IPN)GetProcAddress (GetModuleHandle ("KERNEL32.dll"), "GetTickCount");
static void ShowPopupMenu (HWND hWnd, POINT *curpos, int wDefaultItem)
{
@@ -53,7 +52,7 @@ namespace win32
ID_ACCEPT_TRANSIT, "Accept &transit");
else
InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_DECLINE_TRANSIT, "Decline &transit");
InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_RELOAD, "&Reload configs");
InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_RELOAD, "&Reload tunnels config");
if (!i2p::util::DaemonWin32::Instance ().isGraceful)
InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_GRACEFUL_SHUTDOWN, "&Graceful shutdown");
else
@@ -161,7 +160,7 @@ namespace win32
s << "Uptime: "; ShowUptime(s, i2p::context.GetUptime ());
if (GracefulShutdownEndtime != 0)
{
DWORD GracefulTimeLeft = (GracefulShutdownEndtime - GetTickCount()) / 1000;
DWORD GracefulTimeLeft = (GracefulShutdownEndtime - GetTickCountLocal()) / 1000;
s << "Graceful shutdown, time left: "; ShowUptime(s, GracefulTimeLeft);
}
else
@@ -239,7 +238,7 @@ namespace win32
i2p::context.SetAcceptsTunnels (false);
SetTimer (hWnd, IDT_GRACEFUL_SHUTDOWN_TIMER, 10*60*1000, nullptr); // 10 minutes
SetTimer (hWnd, IDT_GRACEFUL_TUNNELCHECK_TIMER, 1000, nullptr); // check tunnels every second
GracefulShutdownEndtime = GetTickCount() + 10*60*1000;
GracefulShutdownEndtime = GetTickCountLocal() + 10*60*1000;
i2p::util::DaemonWin32::Instance ().isGraceful = true;
return 0;
}

View File

@@ -1,5 +1,5 @@
#define I2Pd_AppName "i2pd"
#define I2Pd_ver "2.26.0"
#define I2Pd_ver "2.31.0"
#define I2Pd_Publisher "PurpleI2P"
[Setup]

4
android/.gitignore vendored
View File

@@ -12,5 +12,5 @@ local.properties
build.sh
android.iml
build
*.iml
*.local

View File

@@ -9,12 +9,15 @@
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
<application
android:allowBackup="true"
android:icon="@drawable/icon"
android:label="@string/app_name"
android:theme="@android:style/Theme.Holo.Light.DarkActionBar"
android:requestLegacyExternalStorage="true"
android:usesCleartextTraffic="true"
>
<receiver android:name=".NetworkStateChangeReceiver">
<intent-filter>

19
android/README.md Normal file
View File

@@ -0,0 +1,19 @@
# how to compile?
## Install the gradle + NDK or use android-studio
[https://gradle.org/install/](https://gradle.org/install/)
## Install the depencies
```
git clone https://github.com/PurpleI2P/Boost-for-Android-Prebuilt.git -b boost-1_72_0
git clone https://github.com/PurpleI2P/android-ifaddrs.git
git clone https://github.com/PurpleI2P/OpenSSL-for-Android-Prebuilt.git
git clone https://github.com/PurpleI2P/MiniUPnP-for-Android-Prebuilt.git
```
## Set libs in jni/Application.mk on 24 line:
```
# change to your own
I2PD_LIBS_PATH = /home/user/i2pd/android/
```
## compile apk file
gradle clean assembleRelease

View File

@@ -5,7 +5,7 @@ buildscript {
google()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.3.2'
classpath 'com.android.tools.build:gradle:3.4.2'
}
}
@@ -16,21 +16,23 @@ repositories {
maven {
url 'https://maven.google.com'
}
google()
}
dependencies {
implementation 'com.android.support:support-compat:28.0.0'
implementation 'androidx.core:core:1.0.2'
}
android {
compileSdkVersion 28
compileSdkVersion 29
buildToolsVersion "28.0.3"
defaultConfig {
applicationId "org.purplei2p.i2pd"
targetSdkVersion 28
targetSdkVersion 29
minSdkVersion 14
versionCode 2260
versionName "2.26.0"
versionCode 2310
versionName "2.31.0"
setProperty("archivesBaseName", archivesBaseName + "-" + versionName)
ndk {
abiFilters 'armeabi-v7a'
abiFilters 'x86'
@@ -55,9 +57,10 @@ android {
splits {
abi {
// change that to true if you need splitted apk
enable false
enable true
reset()
include "armeabi-v7a", "arm64-v8a", "x86", "x86_64"
//include "armeabi-v7a", "arm64-v8a", "x86", "x86_64"
include "armeabi-v7a", "x86"
universalApk true
}
}
@@ -71,7 +74,7 @@ android {
}
buildTypes {
release {
minifyEnabled true
minifyEnabled false
signingConfig signingConfigs.orignal
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-project.txt'
}
@@ -81,4 +84,21 @@ android {
path './jni/Android.mk'
}
}
compileOptions {
sourceCompatibility = '1.8'
targetCompatibility = '1.8'
}
}
ext.abiCodes = ['armeabi-v7a':1, 'x86':2, 'arm64-v8a':3, 'x86_64':4]
import com.android.build.OutputFile
android.applicationVariants.all { variant ->
variant.outputs.each { output ->
def baseAbiVersionCode = project.ext.abiCodes.get(output.getFilter(OutputFile.ABI))
if (baseAbiVersionCode != null) {
output.versionCodeOverride = baseAbiVersionCode + variant.versionCode
}
}
}

View File

@@ -1 +1,3 @@
android.enableJetifier=true
android.useAndroidX=true
org.gradle.parallel=true

View File

@@ -1,6 +1,6 @@
#Thu Mar 14 18:21:08 MSK 2019
#Tue Aug 20 14:39:08 MSK 2019
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.1-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-all.zip

View File

@@ -25,29 +25,29 @@ include $(BUILD_SHARED_LIBRARY)
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := boost_system
LOCAL_SRC_FILES := $(BOOST_PATH)/boost_1_68_0-clang/$(TARGET_ARCH_ABI)/lib/libboost_system.a
LOCAL_EXPORT_C_INCLUDES := $(BOOST_PATH)/boost_1_68_0-clang/include
LOCAL_SRC_FILES := $(BOOST_PATH)/boost-1_72_0/$(TARGET_ARCH_ABI)/lib/libboost_system.a
LOCAL_EXPORT_C_INCLUDES := $(BOOST_PATH)/boost-1_72_0/include
include $(PREBUILT_STATIC_LIBRARY)
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := boost_date_time
LOCAL_SRC_FILES := $(BOOST_PATH)/boost_1_68_0-clang/$(TARGET_ARCH_ABI)/lib/libboost_date_time.a
LOCAL_EXPORT_C_INCLUDES := $(BOOST_PATH)/boost_1_68_0-clang/include
LOCAL_SRC_FILES := $(BOOST_PATH)/boost-1_72_0/$(TARGET_ARCH_ABI)/lib/libboost_date_time.a
LOCAL_EXPORT_C_INCLUDES := $(BOOST_PATH)/boost-1_72_0/include
include $(PREBUILT_STATIC_LIBRARY)
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := boost_filesystem
LOCAL_SRC_FILES := $(BOOST_PATH)/boost_1_68_0-clang/$(TARGET_ARCH_ABI)/lib/libboost_filesystem.a
LOCAL_EXPORT_C_INCLUDES := $(BOOST_PATH)/boost_1_68_0-clang/include
LOCAL_SRC_FILES := $(BOOST_PATH)/boost-1_72_0/$(TARGET_ARCH_ABI)/lib/libboost_filesystem.a
LOCAL_EXPORT_C_INCLUDES := $(BOOST_PATH)/boost-1_72_0/include
include $(PREBUILT_STATIC_LIBRARY)
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := boost_program_options
LOCAL_SRC_FILES := $(BOOST_PATH)/boost_1_68_0-clang/$(TARGET_ARCH_ABI)/lib/libboost_program_options.a
LOCAL_EXPORT_C_INCLUDES := $(BOOST_PATH)/boost_1_68_0-clang/include
LOCAL_SRC_FILES := $(BOOST_PATH)/boost-1_72_0/$(TARGET_ARCH_ABI)/lib/libboost_program_options.a
LOCAL_EXPORT_C_INCLUDES := $(BOOST_PATH)/boost-1_72_0/include
include $(PREBUILT_STATIC_LIBRARY)
LOCAL_PATH := $(call my-dir)

View File

@@ -9,15 +9,15 @@ NDK_TOOLCHAIN_VERSION := clang
#APP_STL := c++_shared
APP_STL := c++_static
# Enable c++11 extensions in source code
APP_CPPFLAGS += -std=c++11 -fexceptions -frtti
# Enable c++17 extensions in source code
APP_CPPFLAGS += -std=c++17 -fexceptions -frtti
APP_CPPFLAGS += -DANDROID -D__ANDROID__ -DUSE_UPNP
ifeq ($(TARGET_ARCH_ABI),armeabi-v7a)
APP_CPPFLAGS += -DANDROID_ARM7A
endif
# git clone https://github.com/PurpleI2P/Boost-for-Android-Prebuilt.git
# git clone https://github.com/PurpleI2P/Boost-for-Android-Prebuilt.git -b boost-1_72_0
# git clone https://github.com/PurpleI2P/OpenSSL-for-Android-Prebuilt.git
# git clone https://github.com/PurpleI2P/MiniUPnP-for-Android-Prebuilt.git
# git clone https://github.com/PurpleI2P/android-ifaddrs.git

View File

@@ -11,4 +11,4 @@
#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
# Project target.
target=android-28
target=android-29

View File

@@ -0,0 +1,13 @@
<LinearLayout android:id="@+id/layout_prompt"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
tools:context=".I2PDActivity">
<WebView
android:id="@+id/webview1"
android:layout_width="fill_parent"
android:layout_height="fill_parent"/>
</LinearLayout>

View File

@@ -3,14 +3,23 @@
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
tools:context=".I2PDActivity">
<item
android:id="@+id/action_graceful_stop"
android:title="@string/action_graceful_stop"
android:orderInCategory="98"
/>
<item
android:id="@+id/action_stop"
android:title="@string/action_stop"
android:orderInCategory="99"
/>
<group android:id="@+id/group_i2pd_control" >
<item
android:id="@+id/action_stop"
android:orderInCategory="99"
android:title="@string/action_stop" />
<item
android:id="@+id/action_graceful_stop"
android:orderInCategory="98"
android:title="@string/action_graceful_stop" />
<item
android:id="@+id/action_start_webview"
android:orderInCategory="97"
android:title="@string/action_start_webview" />
</group>
<group android:id="@+id/group_various" >
<item
android:id="@+id/action_battery_otimizations"
android:title="@string/menu_item_battery_optimizations_str" />
</group>
</menu>

View File

@@ -17,4 +17,12 @@
<string name="remaining">осталось</string>
<string name="title_activity_i2_pdperms_asker_prompt">Запрос</string>
<string name="permDenied">Права для записи на SD карту отклонены, вам необходимо предоставить их для продолжения</string>
<string name="menu_item_battery_optimizations_str">Оптимизации аккумулятора</string>
<string name="battery_optimizations_enabled">Оптимизации аккумулятора включены</string>
<string name="device_does_not_support_disabling_battery_optimizations">Ваша версия Андроид не поддерживает отключение оптимизаций аккумулятора</string>
<string name="battery_optimizations_enabled_explained">Ваша операционная система осуществляет оптимизации расхода аккумулятора, которые могут приводить к выгрузке I2PD из памяти и прекращению его работы с целью сэкономить заряд аккумулятора.\nРекомендуется отключить эти оптимизации.</string>
<string name="battery_optimizations_enabled_dialog" >Ваша операционная система осуществляет оптимизации расхода аккумулятора, которые могут приводить к выгрузке I2PD из памяти и прекращению его работы с целью сэкономить заряд аккумулятора.\n\nВам сейчас будет предложено разрешить отключение этих оптимизаций.</string>
<string name="continue_str">Продолжить</string>
<string name="os_version_does_not_support_battery_optimizations_show_os_dialog_api">Ваша версия Андроид не поддерживает показ диалога об оптимизациях аккумулятора для приложений.</string>
<string name="shutdown_canceled">Плановая остановка отменена</string>
</resources>

View File

@@ -17,4 +17,13 @@
<string name="remaining">remaining</string>
<string name="title_activity_i2_pdperms_asker_prompt">Prompt</string>
<string name="permDenied">SD card write permission denied, you need to allow this to continue</string>
<string name="battery_optimizations_enabled">Battery optimizations enabled</string>
<string name="battery_optimizations_enabled_explained">Your Android is doing some heavy battery optimizations on I2PD that might lead to daemon closing with no other reason.\nIt is recommended to allow disabling those battery optimizations.</string>
<string name="battery_optimizations_enabled_dialog" >Your Android is doing some heavy battery optimizations on I2PD that might lead to daemon closing with no other reason.\n\nYou will now be asked to allow to disable those.</string>
<string name="continue_str">Continue</string>
<string name="device_does_not_support_disabling_battery_optimizations">Your Android version does not support opting out of battery optimizations</string>
<string name="menu_item_battery_optimizations_str">Battery Optimizations</string>
<string name="os_version_does_not_support_battery_optimizations_show_os_dialog_api">Your Android OS version does not support showing the dialog for battery optimizations for applications.</string>
<string name="shutdown_canceled">Planned shutdown canceled</string>
<string name="action_start_webview">Start webview</string>
</resources>

View File

@@ -1,6 +1,5 @@
package org.purplei2p.i2pd;
import android.annotation.TargetApi;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
@@ -11,10 +10,9 @@ import android.content.Intent;
import android.os.Binder;
import android.os.Build;
import android.os.IBinder;
import android.support.annotation.RequiresApi;
import android.support.v4.app.NotificationCompat;
import androidx.annotation.RequiresApi;
import androidx.core.app.NotificationCompat;
import android.util.Log;
import android.widget.Toast;
public class ForegroundService extends Service {
private static final String TAG="FgService";
@@ -112,14 +110,15 @@ public class ForegroundService extends Service {
// If earlier version channel ID is not used
// https://developer.android.com/reference/android/support/v4/app/NotificationCompat.Builder.html#NotificationCompat.Builder(android.content.Context)
String channelId = (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) ? createNotificationChannel() : "";
String channelId = Build.VERSION.SDK_INT >= 26 ? createNotificationChannel() : "";
// Set the info for the views that show in the notification panel.
Notification notification = new NotificationCompat.Builder(this, channelId)
NotificationCompat.Builder builder = new NotificationCompat.Builder(this, channelId)
.setOngoing(true)
.setSmallIcon(R.drawable.itoopie_notification_icon) // the status icon
.setPriority(Notification.PRIORITY_DEFAULT)
.setCategory(Notification.CATEGORY_SERVICE)
.setSmallIcon(R.drawable.itoopie_notification_icon); // the status icon
if(Build.VERSION.SDK_INT >= 16) builder = builder.setPriority(Notification.PRIORITY_DEFAULT);
if(Build.VERSION.SDK_INT >= 21) builder = builder.setCategory(Notification.CATEGORY_SERVICE);
Notification notification = builder
.setTicker(text) // the status text
.setWhen(System.currentTimeMillis()) // the time stamp
.setContentTitle(getText(R.string.app_name)) // the label of the entry
@@ -141,9 +140,10 @@ public class ForegroundService extends Service {
//chan.setLightColor(Color.PURPLE);
chan.setLockscreenVisibility(Notification.VISIBILITY_PRIVATE);
NotificationManager service = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);
service.createNotificationChannel(chan);
if(service!=null)service.createNotificationChannel(chan);
else Log.e(TAG, "error: NOTIFICATION_SERVICE is null");
return channelId;
}
private static final DaemonSingleton daemon = DaemonSingleton.getInstance();
private static final DaemonSingleton daemon = DaemonSingleton.getInstance();
}

View File

@@ -14,36 +14,57 @@ import java.util.Timer;
import java.util.TimerTask;
import android.Manifest;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.ActivityNotFoundException;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.SharedPreferences;
import android.content.res.AssetManager;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Bundle;
import android.os.Build;
import android.os.Environment;
import android.os.IBinder;
import android.os.PowerManager;
import android.preference.PreferenceManager;
import android.provider.Settings;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.TextView;
import android.widget.Toast;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import androidx.annotation.NonNull;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
// For future package update checking
import org.purplei2p.i2pd.BuildConfig;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import static android.provider.Settings.ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS;
public class I2PDActivity extends Activity {
private WebView webView;
private static final String TAG = "i2pdActvt";
private static final int MY_PERMISSION_REQUEST_WRITE_EXTERNAL_STORAGE = 1;
public static final int GRACEFUL_DELAY_MILLIS = 10 * 60 * 1000;
public static final String PACKAGE_URI_SCHEME = "package:";
private TextView textView;
private boolean assetsCopied;
private String i2pdpath = Environment.getExternalStorageDirectory().getAbsolutePath() + "/i2pd/";
//private ConfigParser parser = new ConfigParser(i2pdpath); // TODO:
private static final DaemonSingleton daemon = DaemonSingleton.getInstance();
@@ -53,32 +74,27 @@ public class I2PDActivity extends Activity {
public void daemonStateUpdate()
{
processAssets();
runOnUiThread(new Runnable(){
@Override
public void run() {
try {
if(textView==null) return;
Throwable tr = daemon.getLastThrowable();
if(tr!=null) {
textView.setText(throwableToString(tr));
return;
}
DaemonSingleton.State state = daemon.getState();
textView.setText(
String.valueOf(getText(state.getStatusStringResourceId()))+
(DaemonSingleton.State.startFailed.equals(state) ? ": "+daemon.getDaemonStartResult() : "")+
(DaemonSingleton.State.gracefulShutdownInProgress.equals(state) ? ": "+formatGraceTimeRemaining()+" "+getText(R.string.remaining) : "")
);
} catch (Throwable tr) {
Log.e(TAG,"error ignored",tr);
}
}
});
runOnUiThread(() -> {
try {
if(textView==null) return;
Throwable tr = daemon.getLastThrowable();
if(tr!=null) {
textView.setText(throwableToString(tr));
return;
}
DaemonSingleton.State state = daemon.getState();
String startResultStr = DaemonSingleton.State.startFailed.equals(state) ? String.format(": %s", daemon.getDaemonStartResult()) : "";
String graceStr = DaemonSingleton.State.gracefulShutdownInProgress.equals(state) ? String.format(": %s %s", formatGraceTimeRemaining(), getText(R.string.remaining)) : "";
textView.setText(String.format("%s%s%s", getText(state.getStatusStringResourceId()), startResultStr, graceStr));
} catch (Throwable tr) {
Log.e(TAG,"error ignored",tr);
}
});
}
};
private static volatile long graceStartedMillis;
private static final Object graceStartedMillis_LOCK=new Object();
private Menu optionsMenu;
private static String formatGraceTimeRemaining() {
long remainingSeconds;
@@ -92,6 +108,7 @@ public class I2PDActivity extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
Log.i(TAG, "onCreate");
super.onCreate(savedInstanceState);
textView = new TextView(this);
@@ -121,6 +138,8 @@ public class I2PDActivity extends Activity {
}
rescheduleGraceStop(gracefulQuitTimer, gracefulStopAtMillis);
}
openBatteryOptimizationDialogIfNeeded();
}
@Override
@@ -128,7 +147,7 @@ public class I2PDActivity extends Activity {
super.onDestroy();
textView = null;
daemon.removeStateChangeListener(daemonStateUpdatedListener);
//cancelGracefulStop();
//cancelGracefulStop0();
try{
doUnbindService();
}catch(Throwable tr){
@@ -137,24 +156,20 @@ public class I2PDActivity extends Activity {
}
@Override
public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults)
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults)
{
switch (requestCode)
{
case MY_PERMISSION_REQUEST_WRITE_EXTERNAL_STORAGE:
{
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED)
Log.e(TAG, "Memory permission granted");
else
Log.e(TAG, "Memory permission declined");
// TODO: terminate
return;
}
default: ;
}
if (requestCode == MY_PERMISSION_REQUEST_WRITE_EXTERNAL_STORAGE) {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED)
Log.e(TAG, "WR_EXT_STORAGE perm granted");
else {
Log.e(TAG, "WR_EXT_STORAGE perm declined, stopping i2pd");
i2pdStop();
//TODO must work w/o this perm, ask orignal
}
}
}
private static void cancelGracefulStop() {
private void cancelGracefulStop0() {
Timer gracefulQuitTimer = getGracefulQuitTimer();
if(gracefulQuitTimer!=null) {
gracefulQuitTimer.cancel();
@@ -225,11 +240,17 @@ public class I2PDActivity extends Activity {
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.options_main, menu);
menu.findItem(R.id.action_battery_otimizations).setVisible(isBatteryOptimizationsOpenOsDialogApiAvailable());
this.optionsMenu = menu;
return true;
}
private boolean isBatteryOptimizationsOpenOsDialogApiAvailable() {
return android.os.Build.VERSION.SDK_INT >= 23;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
@@ -240,37 +261,53 @@ public class I2PDActivity extends Activity {
i2pdStop();
return true;
case R.id.action_graceful_stop:
if (getGracefulQuitTimer()!= null)
{
item.setTitle(R.string.action_graceful_stop);
i2pdCancelGracefulStop ();
synchronized (graceStartedMillis_LOCK) {
if (getGracefulQuitTimer() != null)
cancelGracefulStop();
else
i2pdGracefulStop();
}
else
{
item.setTitle(R.string.action_cancel_graceful_stop);
i2pdGracefulStop();
}
return true;
return true;
case R.id.action_battery_otimizations:
onActionBatteryOptimizations();
return true;
case R.id.action_start_webview:
setContentView(R.layout.webview);
this.webView = (WebView) findViewById(R.id.webview1);
this.webView.setWebViewClient(new WebViewClient());
WebSettings webSettings = this.webView.getSettings();
webSettings.setBuiltInZoomControls(true);
webSettings.setJavaScriptEnabled(false);
this.webView.loadUrl("http://127.0.0.1:7070"); // TODO: instead 7070 I2Pd....HttpPort
break;
}
return super.onOptionsItemSelected(item);
}
private void i2pdStop() {
cancelGracefulStop();
new Thread(new Runnable(){
@Override
public void run() {
Log.d(TAG, "stopping");
try{
daemon.stopDaemon();
}catch (Throwable tr) {
Log.e(TAG, "", tr);
}
private void onActionBatteryOptimizations() {
if (isBatteryOptimizationsOpenOsDialogApiAvailable()) {
try {
startActivity(new Intent(ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS));
} catch (ActivityNotFoundException e) {
Log.e(TAG,"BATT_OPTIM_DIALOG_ActvtNotFound", e);
Toast.makeText(this, R.string.os_version_does_not_support_battery_optimizations_show_os_dialog_api, Toast.LENGTH_SHORT).show();
}
}
}
},"stop").start();
private void i2pdStop() {
cancelGracefulStop0();
new Thread(() -> {
Log.d(TAG, "stopping");
try {
daemon.stopDaemon();
} catch (Throwable tr) {
Log.e(TAG, "", tr);
}
quit(); //TODO make menu items for starting i2pd. On my Android, I need to reboot the OS to restart i2pd.
},"stop").start();
}
private static volatile Timer gracefulQuitTimer;
@@ -288,55 +325,45 @@ public class I2PDActivity extends Activity {
}
Toast.makeText(this, R.string.graceful_stop_is_in_progress,
Toast.LENGTH_SHORT).show();
new Thread(new Runnable(){
@Override
public void run() {
try {
Log.d(TAG, "grac stopping");
if(daemon.isStartedOkay()) {
daemon.stopAcceptingTunnels();
long gracefulStopAtMillis;
synchronized (graceStartedMillis_LOCK) {
graceStartedMillis = System.currentTimeMillis();
gracefulStopAtMillis = graceStartedMillis + GRACEFUL_DELAY_MILLIS;
}
rescheduleGraceStop(null,gracefulStopAtMillis);
} else {
i2pdStop();
}
} catch(Throwable tr) {
Log.e(TAG,"",tr);
}
}
},"gracInit").start();
new Thread(() -> {
try {
Log.d(TAG, "grac stopping");
if(daemon.isStartedOkay()) {
daemon.stopAcceptingTunnels();
long gracefulStopAtMillis;
synchronized (graceStartedMillis_LOCK) {
graceStartedMillis = System.currentTimeMillis();
gracefulStopAtMillis = graceStartedMillis + GRACEFUL_DELAY_MILLIS;
}
rescheduleGraceStop(null,gracefulStopAtMillis);
} else {
i2pdStop();
}
} catch(Throwable tr) {
Log.e(TAG,"",tr);
}
},"gracInit").start();
}
private void i2pdCancelGracefulStop()
private void cancelGracefulStop()
{
cancelGracefulStop();
Toast.makeText(this, R.string.startedOkay, Toast.LENGTH_SHORT).show();
new Thread(new Runnable()
{
@Override
public void run()
{
try
{
Log.d(TAG, "grac stopping cancel");
if(daemon.isStartedOkay())
daemon.startAcceptingTunnels();
else
i2pdStop();
cancelGracefulStop0();
new Thread(() -> {
try
{
Log.d(TAG, "canceling grac stop");
if(daemon.isStartedOkay()) {
daemon.startAcceptingTunnels();
runOnUiThread(() -> Toast.makeText(this, R.string.shutdown_canceled, Toast.LENGTH_SHORT).show());
}
catch(Throwable tr)
{
Log.e(TAG,"",tr);
}
}
},"gracCancel").start();
else
i2pdStop();
}
catch(Throwable tr)
{
Log.e(TAG,"",tr);
}
},"gracCancel").start();
}
private void rescheduleGraceStop(Timer gracefulQuitTimerOld, long gracefulStopAtMillis) {
@@ -364,8 +391,19 @@ public class I2PDActivity extends Activity {
return gracefulQuitTimer;
}
private static void setGracefulQuitTimer(Timer gracefulQuitTimer) {
private void setGracefulQuitTimer(Timer gracefulQuitTimer) {
I2PDActivity.gracefulQuitTimer = gracefulQuitTimer;
runOnUiThread(()-> {
Menu menu = optionsMenu;
if (menu != null) {
MenuItem item = menu.findItem(R.id.action_graceful_stop);
if (item != null) {
synchronized (graceStartedMillis_LOCK) {
item.setTitle(getGracefulQuitTimer() != null ? R.string.action_cancel_graceful_stop : R.string.action_graceful_stop);
}
}
}
});
}
/**
@@ -388,19 +426,22 @@ public class I2PDActivity extends Activity {
// to a file. That doesn't appear to be the case. If the returned array is
// null or has 0 length, we assume the path is to a file. This means empty
// directories will get turned into files.
if (contents == null || contents.length == 0)
throw new IOException();
if (contents == null || contents.length == 0) {
copyFileAsset(path);
return;
}
// Make the directory.
File dir = new File(i2pdpath, path);
dir.mkdirs();
boolean result = dir.mkdirs();
Log.d(TAG, "dir.mkdirs() returned " + result);
// Recurse on the contents.
for (String entry : contents) {
copyAsset(path + "/" + entry);
copyAsset(path + '/' + entry);
}
} catch (IOException e) {
copyFileAsset(path);
Log.e(TAG, "ex ignored for path='" + path + "'", e);
}
}
@@ -413,63 +454,89 @@ public class I2PDActivity extends Activity {
*/
private void copyFileAsset(String path) {
File file = new File(i2pdpath, path);
if(!file.exists()) try {
InputStream in = getAssets().open(path);
OutputStream out = new FileOutputStream(file);
byte[] buffer = new byte[1024];
int read = in.read(buffer);
while (read != -1) {
out.write(buffer, 0, read);
read = in.read(buffer);
if(!file.exists()) {
try {
try (InputStream in = getAssets().open(path) ) {
try (OutputStream out = new FileOutputStream(file)) {
byte[] buffer = new byte[1024];
int read = in.read(buffer);
while (read != -1) {
out.write(buffer, 0, read);
read = in.read(buffer);
}
}
}
} catch (IOException e) {
Log.e(TAG, "", e);
}
out.close();
in.close();
} catch (IOException e) {
Log.e(TAG, "", e);
}
}
private void deleteRecursive(File fileOrDirectory) {
if (fileOrDirectory.isDirectory()) {
for (File child : fileOrDirectory.listFiles()) {
deleteRecursive(child);
File[] files = fileOrDirectory.listFiles();
if(files!=null) {
for (File child : files) {
deleteRecursive(child);
}
}
}
fileOrDirectory.delete();
boolean deleteResult = fileOrDirectory.delete();
if(!deleteResult)Log.e(TAG, "fileOrDirectory.delete() returned "+deleteResult+", absolute path='"+fileOrDirectory.getAbsolutePath()+"'");
}
private void processAssets() {
if (!assetsCopied) try {
assetsCopied = true; // prevent from running on every state update
File holderfile = new File(i2pdpath, "assets.ready");
File holderFile = new File(i2pdpath, "assets.ready");
String versionName = BuildConfig.VERSION_NAME; // here will be app version, like 2.XX.XX
StringBuilder text = new StringBuilder();
if (holderfile.exists()) try { // if holder file exists, read assets version string
BufferedReader br = new BufferedReader(new FileReader(holderfile));
String line;
if (holderFile.exists()) {
try { // if holder file exists, read assets version string
FileReader fileReader = new FileReader(holderFile);
while ((line = br.readLine()) != null) {
text.append(line);
}
br.close();
}
catch (IOException e) {
Log.e(TAG, "", e);
}
try {
BufferedReader br = new BufferedReader(fileReader);
try {
String line;
while ((line = br.readLine()) != null) {
text.append(line);
}
}finally {
try{
br.close();
} catch (IOException e) {
Log.e(TAG, "", e);
}
}
} finally {
try{
fileReader.close();
} catch (IOException e) {
Log.e(TAG, "", e);
}
}
} catch (IOException e) {
Log.e(TAG, "", e);
}
}
// if version differs from current app version or null, try to delete certificates folder
if (!text.toString().contains(versionName)) try {
holderfile.delete();
File certpath = new File(i2pdpath, "certificates");
deleteRecursive(certpath);
boolean deleteResult = holderFile.delete();
if(!deleteResult)Log.e(TAG, "holderFile.delete() returned "+deleteResult+", absolute path='"+holderFile.getAbsolutePath()+"'");
File certPath = new File(i2pdpath, "certificates");
deleteRecursive(certPath);
}
catch (Throwable tr) {
Log.e(TAG, "", tr);
}
// copy assets. If processed file exists, it won't be overwrited
// copy assets. If processed file exists, it won't be overwritten
copyAsset("addressbook");
copyAsset("certificates");
copyAsset("tunnels.d");
@@ -478,14 +545,95 @@ public class I2PDActivity extends Activity {
copyAsset("tunnels.conf");
// update holder file about successful copying
FileWriter writer = new FileWriter(holderfile);
writer.append(versionName);
writer.flush();
writer.close();
FileWriter writer = new FileWriter(holderFile);
try {
writer.append(versionName);
} finally {
try{
writer.close();
}catch (IOException e){
Log.e(TAG,"on writer close", e);
}
}
}
catch (Throwable tr)
{
Log.e(TAG,"copy assets",tr);
Log.e(TAG,"on assets copying", tr);
}
}
@SuppressLint("BatteryLife")
private void openBatteryOptimizationDialogIfNeeded() {
boolean questionEnabled = getPreferences().getBoolean(getBatteryOptimizationPreferenceKey(), true);
Log.i(TAG,"BATT_OPTIM_questionEnabled=="+questionEnabled);
if (!isKnownIgnoringBatteryOptimizations()
&& android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M
&& questionEnabled) {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(R.string.battery_optimizations_enabled);
builder.setMessage(R.string.battery_optimizations_enabled_dialog);
builder.setPositiveButton(R.string.continue_str, (dialog, which) -> {
try {
startActivity(new Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS, Uri.parse(PACKAGE_URI_SCHEME + getPackageName())));
} catch (ActivityNotFoundException e) {
Log.e(TAG,"BATT_OPTIM_ActvtNotFound", e);
Toast.makeText(this, R.string.device_does_not_support_disabling_battery_optimizations, Toast.LENGTH_SHORT).show();
}
});
builder.setOnDismissListener(dialog -> setNeverAskForBatteryOptimizationsAgain());
final AlertDialog dialog = builder.create();
dialog.setCanceledOnTouchOutside(false);
dialog.show();
}
}
private void setNeverAskForBatteryOptimizationsAgain() {
getPreferences().edit().putBoolean(getBatteryOptimizationPreferenceKey(), false).apply();
}
protected boolean isKnownIgnoringBatteryOptimizations() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
final PowerManager pm = (PowerManager) getSystemService(POWER_SERVICE);
if (pm == null) {
Log.i(TAG, "BATT_OPTIM: POWER_SERVICE==null");
return false;
}
boolean ignoring = pm.isIgnoringBatteryOptimizations(getPackageName());
Log.i(TAG, "BATT_OPTIM: ignoring==" + ignoring);
return ignoring;
} else {
Log.i(TAG, "BATT_OPTIM: old sdk version=="+Build.VERSION.SDK_INT);
return false;
}
}
protected SharedPreferences getPreferences() {
return PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
}
private String getBatteryOptimizationPreferenceKey() {
@SuppressLint("HardwareIds") String device = Settings.Secure.getString(getContentResolver(), Settings.Secure.ANDROID_ID);
return "show_battery_optimization" + (device == null ? "" : device);
}
private void quit() {
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
finishAndRemoveTask();
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
finishAffinity();
} else {
//moveTaskToBack(true);
finish();
}
}catch (Throwable tr) {
Log.e(TAG, "", tr);
}
try{
daemon.stopDaemon();
}catch (Throwable tr) {
Log.e(TAG, "", tr);
}
System.exit(0);
}
}

View File

@@ -26,29 +26,29 @@ include $(BUILD_EXECUTABLE)
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := boost_system
LOCAL_SRC_FILES := $(BOOST_PATH)/boost_1_68_0-clang/$(TARGET_ARCH_ABI)/lib/libboost_system.a
LOCAL_EXPORT_C_INCLUDES := $(BOOST_PATH)/boost_1_68_0-clang/include
LOCAL_SRC_FILES := $(BOOST_PATH)/boost-1_72_0/$(TARGET_ARCH_ABI)/lib/libboost_system.a
LOCAL_EXPORT_C_INCLUDES := $(BOOST_PATH)/boost-1_72_0/include
include $(PREBUILT_STATIC_LIBRARY)
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := boost_date_time
LOCAL_SRC_FILES := $(BOOST_PATH)/boost_1_68_0-clang/$(TARGET_ARCH_ABI)/lib/libboost_date_time.a
LOCAL_EXPORT_C_INCLUDES := $(BOOST_PATH)/boost_1_68_0-clang/include
LOCAL_SRC_FILES := $(BOOST_PATH)/boost-1_72_0/$(TARGET_ARCH_ABI)/lib/libboost_date_time.a
LOCAL_EXPORT_C_INCLUDES := $(BOOST_PATH)/boost-1_72_0/include
include $(PREBUILT_STATIC_LIBRARY)
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := boost_filesystem
LOCAL_SRC_FILES := $(BOOST_PATH)/boost_1_68_0-clang/$(TARGET_ARCH_ABI)/lib/libboost_filesystem.a
LOCAL_EXPORT_C_INCLUDES := $(BOOST_PATH)/boost_1_68_0-clang/include
LOCAL_SRC_FILES := $(BOOST_PATH)/boost-1_72_0/$(TARGET_ARCH_ABI)/lib/libboost_filesystem.a
LOCAL_EXPORT_C_INCLUDES := $(BOOST_PATH)/boost-1_72_0/include
include $(PREBUILT_STATIC_LIBRARY)
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := boost_program_options
LOCAL_SRC_FILES := $(BOOST_PATH)/boost_1_68_0-clang/$(TARGET_ARCH_ABI)/lib/libboost_program_options.a
LOCAL_EXPORT_C_INCLUDES := $(BOOST_PATH)/boost_1_68_0-clang/include
LOCAL_SRC_FILES := $(BOOST_PATH)/boost-1_72_0/$(TARGET_ARCH_ABI)/lib/libboost_program_options.a
LOCAL_EXPORT_C_INCLUDES := $(BOOST_PATH)/boost-1_72_0/include
include $(PREBUILT_STATIC_LIBRARY)
LOCAL_PATH := $(call my-dir)

View File

@@ -9,8 +9,8 @@ APP_PLATFORM := android-14
NDK_TOOLCHAIN_VERSION := clang
APP_STL := c++_static
# Enable c++11 extensions in source code
APP_CPPFLAGS += -std=c++11 -fvisibility=default -fPIE
# Enable c++17 extensions in source code
APP_CPPFLAGS += -std=c++17 -fvisibility=default -fPIE
APP_CPPFLAGS += -DANDROID_BINARY -DANDROID -D__ANDROID__ -DUSE_UPNP
APP_LDFLAGS += -rdynamic -fPIE -pie
@@ -21,7 +21,7 @@ endif
# Forcing debug optimization. Use `ndk-build NDK_DEBUG=1` instead.
#APP_OPTIM := debug
# git clone https://github.com/PurpleI2P/Boost-for-Android-Prebuilt.git
# git clone https://github.com/PurpleI2P/Boost-for-Android-Prebuilt.git -b boost-1_72_0
# git clone https://github.com/PurpleI2P/OpenSSL-for-Android-Prebuilt.git
# git clone https://github.com/PurpleI2P/MiniUPnP-for-Android-Prebuilt.git
# git clone https://github.com/PurpleI2P/android-ifaddrs.git

View File

@@ -1,4 +1,4 @@
version: 2.26.0.{build}
version: 2.31.0.{build}
pull_requests:
do_not_increment_build_number: true
branches:
@@ -18,9 +18,9 @@ environment:
install:
- c:\msys64\usr\bin\bash -lc "pacman --noconfirm -Rns gcc-fortran gcc mingw-w64-{i686,x86_64}-gcc-ada mingw-w64-{i686,x86_64}-gcc-objc"
- c:\msys64\usr\bin\bash -lc "pacman --noconfirm -Syuu --force"
- c:\msys64\usr\bin\bash -lc "pacman --noconfirm -Syuu"
- c:\msys64\usr\bin\bash -lc "pacman --noconfirm -Syuu --force"
- c:\msys64\usr\bin\bash -lc "pacman --noconfirm -Syuu"
- if "%MSYSTEM%" == "MINGW64" (
c:\msys64\usr\bin\bash -lc "pacman --noconfirm -S mingw-w64-x86_64-boost mingw-w64-x86_64-miniupnpc"

View File

@@ -19,8 +19,6 @@ option(WITH_GUI "Include GUI (currently MS Windows only)" ON)
option(WITH_MESHNET "Build for cjdns test network" OFF)
option(WITH_ADDRSANITIZER "Build with address sanitizer unix only" OFF)
option(WITH_THREADSANITIZER "Build with thread sanitizer unix only" OFF)
option(WITH_I2LUA "Build for i2lua" OFF)
option(WITH_WEBSOCKETS "Build with websocket ui" OFF)
# paths
set ( CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake_modules" )
@@ -75,28 +73,20 @@ set (LIBI2PD_SRC
"${LIBI2PD_SRC_DIR}/Signature.cpp"
"${LIBI2PD_SRC_DIR}/Timestamp.cpp"
"${LIBI2PD_SRC_DIR}/api.cpp"
"${LIBI2PD_SRC_DIR}/Event.cpp"
"${LIBI2PD_SRC_DIR}/Gost.cpp"
"${LIBI2PD_SRC_DIR}/ChaCha20.cpp"
"${LIBI2PD_SRC_DIR}/Poly1305.cpp"
"${LIBI2PD_SRC_DIR}/Ed25519.cpp"
"${LIBI2PD_SRC_DIR}/NTCP2.cpp"
"${LIBI2PD_SRC_DIR}/Blinding.cpp"
"${LIBI2PD_SRC_DIR}/Elligator.cpp"
"${LIBI2PD_SRC_DIR}/ECIESX25519AEADRatchetSession.cpp"
)
if (WITH_WEBSOCKETS)
add_definitions(-DWITH_EVENTS)
find_package(websocketpp REQUIRED)
endif ()
if (WIN32 OR MSYS)
list (APPEND LIBI2PD_SRC "${CMAKE_SOURCE_DIR}/I2PEndian.cpp")
endif ()
if (WITH_I2LUA)
add_definitions(-DI2LUA)
endif()
add_library(libi2pd ${LIBI2PD_SRC})
set_target_properties(libi2pd PROPERTIES PREFIX "")
@@ -122,13 +112,8 @@ set (CLIENT_SRC
"${LIBI2PD_CLIENT_SRC_DIR}/SOCKS.cpp"
"${LIBI2PD_CLIENT_SRC_DIR}/HTTPProxy.cpp"
"${LIBI2PD_CLIENT_SRC_DIR}/I2CP.cpp"
"${LIBI2PD_CLIENT_SRC_DIR}/WebSocks.cpp"
)
if(WITH_WEBSOCKETS)
list (APPEND CLIENT_SRC "${LIBI2PD_CLIENT_SRC_DIR}/Websocket.cpp")
endif ()
add_library(libi2pdclient ${CLIENT_SRC})
set_target_properties(libi2pdclient PROPERTIES PREFIX "")
@@ -182,17 +167,17 @@ else()
set( CMAKE_EXE_LINKER_FLAGS_MINSIZEREL "-Wl,--gc-sections" ) # -flto is added from above
endif ()
# check for c++11 support
# check for c++17 & c++11 support
include(CheckCXXCompilerFlag)
CHECK_CXX_COMPILER_FLAG("-std=c++17" CXX17_SUPPORTED)
CHECK_CXX_COMPILER_FLAG("-std=c++11" CXX11_SUPPORTED)
CHECK_CXX_COMPILER_FLAG("-std=c++0x" CXX0X_SUPPORTED)
if (CXX11_SUPPORTED)
set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11" )
elseif (CXX0X_SUPPORTED) # gcc 4.6
set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x" )
elseif (NOT MSVC)
message(SEND_ERROR "C++11 standard not seems to be supported by compiler. Too old version?")
endif ()
if(CXX17_SUPPORTED)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++17")
elseif(CXX11_SUPPORTED)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
else()
message(SEND_ERROR "C++17 nor C++11 standard not seems to be supported by compiler. Too old version?")
endif()
if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pipe")
@@ -424,8 +409,6 @@ message(STATUS " PCH : ${WITH_PCH}")
message(STATUS " MESHNET : ${WITH_MESHNET}")
message(STATUS " ADDRSANITIZER : ${WITH_ADDRSANITIZER}")
message(STATUS " THREADSANITIZER : ${WITH_THREADSANITIZER}")
message(STATUS " I2LUA : ${WITH_I2LUA}")
message(STATUS " WEBSOCKETS : ${WITH_WEBSOCKETS}")
message(STATUS "---------------------------------------")
#Handle paths nicely

View File

@@ -54,6 +54,14 @@ set bitness=64
call :BUILDING
echo.
REM building for WinXP
set "WD=C:\msys64-xp\usr\bin\"
set MSYSTEM=MINGW32
set bitness=32
set "xSH=%WD%bash -lc"
call :BUILDING_XP
echo.
del README.txt >> nul
echo Build complete...
@@ -71,5 +79,11 @@ echo Build AESNI...
%xSH% "make DEBUG=no USE_UPNP=yes USE_AESNI=1 -j%threads% && zip -r9 build/i2pd_%tag%_win%bitness%_mingw_aesni.zip %FILELIST% && make clean" > build/build_win%bitness%_aesni_%tag%.log 2>&1
echo Build without extensions...
%xSH% "make DEBUG=no USE_UPNP=yes -j%threads% && zip -r9 build/i2pd_%tag%_win%bitness%_mingw.zip %FILELIST% && make clean" > build/build_win%bitness%_%tag%.log 2>&1
goto EOF
:BUILDING_XP
%xSH% "make clean" >> nul
echo Building i2pd %tag% for winxp...
%xSH% "make DEBUG=no USE_UPNP=yes USE_WINXP_FLAGS=yes -j%threads% && zip -r9 build/i2pd_%tag%_winxp_mingw.zip %FILELIST% && make clean" > build/build_winxp_%tag%.log 2>&1
:EOF

View File

@@ -1,32 +0,0 @@
-----BEGIN CERTIFICATE-----
MIIFfTCCA2WgAwIBAgIEOprmhjANBgkqhkiG9w0BAQ0FADBvMQswCQYDVQQGEwJY
WDELMAkGA1UECBMCWFgxCzAJBgNVBAcTAlhYMR4wHAYDVQQKExVJMlAgQW5vbnlt
b3VzIE5ldHdvcmsxDDAKBgNVBAsTA0kyUDEYMBYGA1UEAwwPYmFja3VwQG1haWwu
aTJwMB4XDTEzMTAxMzEzNDQ1NVoXDTIzMTAxMzEzNDQ1NVowbzELMAkGA1UEBhMC
WFgxCzAJBgNVBAgTAlhYMQswCQYDVQQHEwJYWDEeMBwGA1UEChMVSTJQIEFub255
bW91cyBOZXR3b3JrMQwwCgYDVQQLEwNJMlAxGDAWBgNVBAMMD2JhY2t1cEBtYWls
LmkycDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAIoAkobXwk/Enf1d
roHyqCyvcJfZJVTwb/LgYWAvCBMCr+RGqlSgtk3g69Y3I0xU08fD2kGt3r5Pwsbr
omXIbJAcccyLqmQ5QX6QgL+X9VpMDp9C4h2RogCrqLBAWw4cuZ4RS9VCpP1Yis7H
uejYqENP86p7BsRnuW/4cYnfunAdMpss4LpRGQXt1nTX+kfgCYgnKFbFqwAHt7yV
Ds+Pe6FuBHPlp+sc1amKRcUnSvhXLsv43VicnT7xYL/kUsN83wrtHA3B4aGDx3aA
3/EzuRmIXQB0BlTZILMEyYwG/nc4OsW82QYrvEZ9BIg9A4lF/wS/KZCICPxLF2zo
dGjnmlgkiA4s8eO+va/ElHyELjckVXqmG1eXHhSkEsDvOQJy01IUuwLinvq7cUbJ
HfJBZJllEg+sLDCv3FkEqN+XjBNFfQN4oNew4w6IPY6YH1INVB9LL0Cmdu4DudLv
TY8OcI8eSfez3hmm+pYQ23PJRYYnvRDnRECyIWBegkckWRh8U/WvZUYUvETK6EDl
/0KpTtfzX6MqHA5D6bTAB8Y3ijGMLrZ/B5vj5yCoZbLiGme9X2moR2k1LEhdhtzV
exsqezCpg6dn48FTX7mHjvR5/r4kz2jqBGmdPUWIIxnjFUzDUK3llVQiHihleHpe
jL4LqnhBGKWFRTaVwaIkBG4zAfIzAgMBAAGjITAfMB0GA1UdDgQWBBQNkfW7bSMl
1/4KDbgwrkf9x1Zu/TANBgkqhkiG9w0BAQ0FAAOCAgEAGg3a3rTf0EznQocmio0T
5gCoL0n8h6yKW/PyPAIELrd9wiYjhJFcWvMTcJJJnVqmAL5vpvhaAFVtAfx70MGa
0DZ7FvytK5hEfF4IqOFDyEEVGJR5rIpVK4MeI1nmwEsxdbW+FhODjtRzgYO8XBME
Xj4aY1FWg9vxc3reUj6PSFsZtsB0aLiRgL9JDovJIiRw0Uqr1v2wXBte5yVCxDge
vTREZtpK4cKetoOa68pwSXI32JwKE18j6bfdKVBCcYQKlKP/3gHGduaDrQv3w32S
DRym5s6MREeTUOtAw4wq46KpdOX8yyAqJPrCfMwS6ORd3t+egqOw0PUnsqb97w4O
lUtrRYvb2cOj60SmRx4vJvItyuHbKqIK7o2e1RcUZPXYoAVx2ww4XB2Wk4D7LSAs
cS7nLj8yAqzJ2qqtBzxu+zILJtkVa12dKF0xmS0BxBp4sCYiBtmAVE8AWQqEuSHA
FrMWqoXcjcfdvvyX487FFWWUE7ZBIn0hee2sK9J9+SPtqczJaN7TF3K3nzo65WJG
1epltmq2Ugjb67Gz7v4y7H23DJ/qhm8yLtCHTj69HTta5I08j6Kut924WLZaiMO/
4YoEL5AE63X0sxYibKFQiq7FW5nUJA280GRlY3xSMFzlB2ggazrUV3YAWVDhfdnI
flpzWXkFM2D36OUaubfe9YY=
-----END CERTIFICATE-----

View File

@@ -1,34 +0,0 @@
-----BEGIN CERTIFICATE-----
MIIF3DCCA8SgAwIBAgIQPxUlcrbHX/xdyJ09E36rJzANBgkqhkiG9w0BAQsFADB3
MQswCQYDVQQGEwJYWDELMAkGA1UEBxMCWFgxCzAJBgNVBAkTAlhYMR4wHAYDVQQK
ExVJMlAgQW5vbnltb3VzIE5ldHdvcmsxDDAKBgNVBAsTA0kyUDEgMB4GA1UEAwwX
cmVzZWVkaTJwbmV0aW5AbWFpbC5pMnAwHhcNMTgxMjA3MTYzNDIxWhcNMjgxMjA3
MTYzNDIxWjB3MQswCQYDVQQGEwJYWDELMAkGA1UEBxMCWFgxCzAJBgNVBAkTAlhY
MR4wHAYDVQQKExVJMlAgQW5vbnltb3VzIE5ldHdvcmsxDDAKBgNVBAsTA0kyUDEg
MB4GA1UEAwwXcmVzZWVkaTJwbmV0aW5AbWFpbC5pMnAwggIiMA0GCSqGSIb3DQEB
AQUAA4ICDwAwggIKAoICAQC912NDk6x85uqB4gyQQcded0RVrbWehWOanDRv7kC3
92jeziPbeMtqrLsfU1MdDtQiGijpNkQ/IIitPw+6vJAIh82gyOUZvsn2XOyb/Fz0
Fu8OrDghwl39yK8kwtqCFw3VAgafgKxz2oRge9mxFBECi50vYEPIBwNhr4yc/opu
wWUmzmRyX4gD7vKmRU6ZTwX4LXnwdl+5VbW3updcZKsDuTnKvC9FGhDRR9kIk2G9
43sLN263nCYPykP7DaB1cUdi1vDEMw5dot+eu16qTIbuypEvYNvbB/9FyCQllm1h
vBbSku3IYpcnRPmoeyhoR/MmCySRbK5R4SrSsVD1YBpwxgn0Q4+fzEgFzT9P4oez
HkDGKVP2HdgmXx9j36fEqqvjqzRleWDwEWwIZVRLCFO+hhhT3JAjnNGJTWv1SQGB
8tz9nyYTJuhvyHE/CO5owFeCdeOGMq2KPge9w34T+mvewTEEhGU8yRAt8Xp8s5Y9
RCUGvuQ79+edRtj7FJg7yVB8pAQ+VB9msNQvzrTnPYC9Wo7chJhBiraMiIabzIhC
f34Gg9lkX1N0dVND5rnZWwzBM6JhNG1iZZCRHVPnXdZRixUlqmFpCP/eekshksj/
6UP/WeGA6X4HyEsC6QEf7eMhcHYjyyTzYagKrwCHg77fmIjF8rmpP2LqWSQW8bDD
uQIDAQABo2QwYjAOBgNVHQ8BAf8EBAMCAoQwHQYDVR0lBBYwFAYIKwYBBQUHAwIG
CCsGAQUFBwMBMA8GA1UdEwEB/wQFMAMBAf8wIAYDVR0OBBkEF3Jlc2VlZGkycG5l
dGluQG1haWwuaTJwMA0GCSqGSIb3DQEBCwUAA4ICAQCWpXs6iuTy/w2R7q7Ua6vl
JYZwbQ+czk5ydzkBgcNkMMMNRT7sZR9xYvV+ftiL4bFQP/3ZJyo7cYz2Q6+M3oAm
YDcZWBkLUVihSlMxhWwmeFTKV2EL+bzwY1V/cy7wgukKnFIes75dLP/v25jgjdlw
Xe6R+fQM0EoHeVzzrWk/qYp6oEwtQXfZnUu/Bf45hRnnHBzzh1wCql41vbEs3Niq
+SVwY1wLT0yC1L8HqjCLX1/L5PAXxbvEGzwnXSkLKK4bPxdmVDZvS9uzXrWmTbNi
HpKIFnOif16zSgyeaOM7HETIJuVzgooUMtt+Vsr1VGdtm6K7I9J5C+rX/ckU8oaX
UjmzhWXudN0VTslogsKUCV6xG2CskeE3wnuT8HYXz9NMw6c/kIGH4hY7LcfU8Teu
QjSy2RRvy6InmZNV5sY9lzzO6romEycSoUlpCa3Ltb/5KKoYZFTsXr8suqJk89lC
e+TVMHqOZdLK/usqQDcafLypHpw9SH2Tg4jrzV/zLqacbjx6bZD5IrpY0Gf7BXg/
pikwyA9c490G6ZcWrSEP8bzh6LL2rA2AwxaeJJNVyLHCSLrn/7DezM5J/qhd90Qg
kcZGJrUOCSWl6mDvUZn5XiQ955XwOnZQ+wsM85B3CVX22x5bp0SYWHCQBPnthPwP
Q5DD3jExbpwG5n35HEcHYw==
-----END CERTIFICATE-----

View File

@@ -36,8 +36,8 @@ RUN apk --no-cache --virtual build-dependendencies add make gcc g++ libtool zlib
&& cd /usr/local/bin \
&& strip i2pd \
&& rm -fr /tmp/build && apk --no-cache --purge del build-dependendencies build-base fortify-headers boost-dev zlib-dev openssl-dev \
boost-python3 python3 gdbm boost-unit_test_framework boost-python linux-headers boost-prg_exec_monitor \
boost-serialization boost-signals boost-wave boost-wserialization boost-math boost-graph boost-regex git pcre \
boost-python3 python3 gdbm boost-unit_test_framework linux-headers boost-prg_exec_monitor \
boost-serialization boost-wave boost-wserialization boost-math boost-graph boost-regex git pcre \
libtool g++ gcc pkgconfig
# 2. Adding required libraries to run i2pd to ensure it will run.

View File

@@ -1,7 +1,7 @@
%define git_hash %(git rev-parse HEAD | cut -c -7)
Name: i2pd-git
Version: 2.26.0
Version: 2.31.0
Release: git%{git_hash}%{?dist}
Summary: I2P router written in C++
Conflicts: i2pd
@@ -55,14 +55,22 @@ cd build
%endif
%endif
%if 0%{?mageia} > 7
pushd build
make %{?_smp_mflags}
popd
%else
make %{?_smp_mflags}
%endif
%install
cd build
pushd build
%if 0%{?mageia}
cd build
pushd build
%endif
chrpath -d i2pd
%{__install} -D -m 755 i2pd %{buildroot}%{_sbindir}/i2pd
%{__install} -D -m 755 %{_builddir}/%{name}-%{version}/contrib/i2pd.conf %{buildroot}%{_sysconfdir}/i2pd/i2pd.conf
@@ -110,6 +118,21 @@ getent passwd i2pd >/dev/null || \
%changelog
* Fri Apr 10 2020 orignal <i2porignal@yandex.ru> - 2.31.0
- update to 2.31.0
* Tue Feb 25 2020 orignal <i2porignal@yandex.ru> - 2.30.0
- update to 2.30.0
* Mon Oct 21 2019 orignal <i2porignal@yandex.ru> - 2.29.0
- update to 2.29.0
* Tue Aug 27 2019 orignal <i2porignal@yandex.ru> - 2.28.0
- update to 2.28.0
* Wed Jul 3 2019 orignal <i2porignal@yandex.ru> - 2.27.0
- update to 2.27.0
* Fri Jun 7 2019 orignal <i2porignal@yandex.ru> - 2.26.0
- update to 2.26.0

View File

@@ -1,5 +1,5 @@
Name: i2pd
Version: 2.26.0
Version: 2.31.0
Release: 1%{?dist}
Summary: I2P router written in C++
Conflicts: i2pd-git
@@ -53,14 +53,22 @@ cd build
%endif
%endif
%if 0%{?mageia} > 7
pushd build
make %{?_smp_mflags}
popd
%else
make %{?_smp_mflags}
%endif
%install
cd build
pushd build
%if 0%{?mageia}
cd build
pushd build
%endif
chrpath -d i2pd
install -D -m 755 i2pd %{buildroot}%{_sbindir}/i2pd
install -D -m 755 %{_builddir}/%{name}-%{version}/contrib/i2pd.conf %{buildroot}%{_sysconfdir}/i2pd/i2pd.conf
@@ -108,6 +116,21 @@ getent passwd i2pd >/dev/null || \
%changelog
* Fri Apr 10 2020 orignal <i2porignal@yandex.ru> - 2.31.0
- update to 2.31.0
* Tue Feb 25 2020 orignal <i2porignal@yandex.ru> - 2.30.0
- update to 2.30.0
* Mon Oct 21 2019 orignal <i2porignal@yandex.ru> - 2.29.0
- update to 2.29.0
* Tue Aug 27 2019 orignal <i2porignal@yandex.ru> - 2.28.0
- update to 2.28.0
* Wed Jul 3 2019 orignal <i2porignal@yandex.ru> - 2.27.0
- update to 2.27.0
* Fri Jun 7 2019 orignal <i2porignal@yandex.ru> - 2.26.0
- update to 2.26.0
@@ -124,7 +147,7 @@ getent passwd i2pd >/dev/null || \
- update to 2.22.0
- add support of tunnelsdir option
* Thu Oct 22 2018 orignal <i2porignal@yandex.ru> - 2.21.1
* Mon Oct 22 2018 orignal <i2porignal@yandex.ru> - 2.21.1
- update to 2.21.1
* Thu Oct 4 2018 orignal <i2porignal@yandex.ru> - 2.21.0

View File

@@ -26,9 +26,6 @@
#include "Timestamp.h"
#include "util.h"
#include "Event.h"
#include "Websocket.h"
namespace i2p
{
namespace util
@@ -43,9 +40,6 @@ namespace i2p
std::unique_ptr<i2p::client::I2PControlService> m_I2PControlService;
std::unique_ptr<i2p::transport::UPnP> UPnP;
std::unique_ptr<i2p::util::NTPTimeSync> m_NTPSync;
#ifdef WITH_EVENTS
std::unique_ptr<i2p::event::WebsocketServer> m_WebsocketServer;
#endif
};
Daemon_Singleton::Daemon_Singleton() : isDaemon(false), running(true), d(*new Daemon_Singleton_Private()) {}
@@ -62,12 +56,12 @@ namespace i2p
return service;
}
bool Daemon_Singleton::init(int argc, char* argv[]) {
return init(argc, argv, nullptr);
}
bool Daemon_Singleton::init(int argc, char* argv[]) {
return init(argc, argv, nullptr);
}
bool Daemon_Singleton::init(int argc, char* argv[], std::shared_ptr<std::ostream> logstream)
{
bool Daemon_Singleton::init(int argc, char* argv[], std::shared_ptr<std::ostream> logstream)
{
i2p::config::Init();
i2p::config::ParseCmdline(argc, argv);
@@ -110,10 +104,10 @@ namespace i2p
logs = "file";
i2p::log::Logger().SetLogLevel(loglevel);
if (logstream) {
LogPrint(eLogInfo, "Log: will send messages to std::ostream");
i2p::log::Logger().SendTo (logstream);
} else if (logs == "file") {
if (logstream) {
LogPrint(eLogInfo, "Log: will send messages to std::ostream");
i2p::log::Logger().SendTo (logstream);
} else if (logs == "file") {
if (logfile == "")
logfile = i2p::fs::DataDirPath("i2pd.log");
LogPrint(eLogInfo, "Log: will send messages to ", logfile);
@@ -127,7 +121,7 @@ namespace i2p
// use stdout -- default
}
LogPrint(eLogInfo, "i2pd v", VERSION, " starting");
LogPrint(eLogInfo, "i2pd v", VERSION, " starting");
LogPrint(eLogDebug, "FS: main config file: ", config);
LogPrint(eLogDebug, "FS: data directory: ", datadir);
@@ -151,8 +145,8 @@ namespace i2p
LogPrint(eLogInfo, "Daemon: accepting incoming connections at port ", port);
i2p::context.UpdatePort (port);
}
i2p::context.SetSupportsV6 (ipv6);
i2p::context.SetSupportsV4 (ipv4);
i2p::context.SetSupportsV6 (ipv6);
i2p::context.SetSupportsV4 (ipv4);
bool ntcp; i2p::config::GetOption("ntcp", ntcp);
i2p::context.PublishNTCPAddress (ntcp, !ipv6);
@@ -233,15 +227,15 @@ namespace i2p
if (family.length () > 0)
LogPrint(eLogInfo, "Daemon: family set to ", family);
bool trust; i2p::config::GetOption("trust.enabled", trust);
if (trust)
{
LogPrint(eLogInfo, "Daemon: explicit trust enabled");
std::string fam; i2p::config::GetOption("trust.family", fam);
bool trust; i2p::config::GetOption("trust.enabled", trust);
if (trust)
{
LogPrint(eLogInfo, "Daemon: explicit trust enabled");
std::string fam; i2p::config::GetOption("trust.family", fam);
std::string routers; i2p::config::GetOption("trust.routers", routers);
bool restricted = false;
if (fam.length() > 0)
{
if (fam.length() > 0)
{
std::set<std::string> fams;
size_t pos = 0, comma;
do
@@ -253,7 +247,7 @@ namespace i2p
while (comma != std::string::npos);
i2p::transport::transports.RestrictRoutesToFamilies(fams);
restricted = fams.size() > 0;
}
}
if (routers.length() > 0) {
std::set<i2p::data::IdentHash> idents;
size_t pos = 0, comma;
@@ -272,14 +266,15 @@ namespace i2p
}
if(!restricted)
LogPrint(eLogError, "Daemon: no trusted routers of families specififed");
}
bool hidden; i2p::config::GetOption("trust.hidden", hidden);
if (hidden)
{
LogPrint(eLogInfo, "Daemon: using hidden mode");
i2p::data::netdb.SetHidden(true);
}
return true;
}
bool hidden; i2p::config::GetOption("trust.hidden", hidden);
if (hidden)
{
LogPrint(eLogInfo, "Daemon: using hidden mode");
i2p::data::netdb.SetHidden(true);
}
return true;
}
bool Daemon_Singleton::start()
@@ -322,7 +317,7 @@ namespace i2p
bool http; i2p::config::GetOption("http.enabled", http);
if (http) {
std::string httpAddr; i2p::config::GetOption("http.address", httpAddr);
uint16_t httpPort; i2p::config::GetOption("http.port", httpPort);
uint16_t httpPort; i2p::config::GetOption("http.port", httpPort);
LogPrint(eLogInfo, "Daemon: starting HTTP Server at ", httpAddr, ":", httpPort);
d.httpServer = std::unique_ptr<i2p::http::HTTPServer>(new i2p::http::HTTPServer(httpAddr, httpPort));
d.httpServer->Start();
@@ -344,26 +339,11 @@ namespace i2p
d.m_I2PControlService = std::unique_ptr<i2p::client::I2PControlService>(new i2p::client::I2PControlService (i2pcpAddr, i2pcpPort));
d.m_I2PControlService->Start ();
}
#ifdef WITH_EVENTS
bool websocket; i2p::config::GetOption("websockets.enabled", websocket);
if(websocket) {
std::string websocketAddr; i2p::config::GetOption("websockets.address", websocketAddr);
uint16_t websocketPort; i2p::config::GetOption("websockets.port", websocketPort);
LogPrint(eLogInfo, "Daemon: starting Websocket server at ", websocketAddr, ":", websocketPort);
d.m_WebsocketServer = std::unique_ptr<i2p::event::WebsocketServer>(new i2p::event::WebsocketServer (websocketAddr, websocketPort));
d.m_WebsocketServer->Start();
i2p::event::core.SetListener(d.m_WebsocketServer->ToListener());
}
#endif
return true;
}
bool Daemon_Singleton::stop()
{
#ifdef WITH_EVENTS
i2p::event::core.SetListener(nullptr);
#endif
LogPrint(eLogInfo, "Daemon: shutting down");
LogPrint(eLogInfo, "Daemon: stopping Client");
i2p::client::context.Stop();
@@ -397,13 +377,6 @@ namespace i2p
d.m_I2PControlService->Stop ();
d.m_I2PControlService = nullptr;
}
#ifdef WITH_EVENTS
if (d.m_WebsocketServer) {
LogPrint(eLogInfo, "Daemon: stopping Websocket server");
d.m_WebsocketServer->Stop();
d.m_WebsocketServer = nullptr;
}
#endif
i2p::crypto::TerminateCrypto ();
i2p::log::Logger().Stop();

View File

@@ -54,6 +54,7 @@ namespace http {
" body { font: 100%/1.5em sans-serif; margin: 0; padding: 1.5em; background: #FAFAFA; color: #103456; }\r\n"
" a, .slide label { text-decoration: none; color: #894C84; }\r\n"
" a:hover, .slide label:hover { color: #FAFAFA; background: #894C84; }\r\n"
" a.button { -webkit-appearance: button; -moz-appearance: button; appearance: button; text-decoration: none; color: initial; width: 1.5em;}\r\n"
" .header { font-size: 2.5em; text-align: center; margin: 1.5em 0; color: #894C84; }\r\n"
" .wrapper { margin: 0 auto; padding: 1em; max-width: 60em; }\r\n"
" .left { float: left; position: absolute; }\r\n"
@@ -63,9 +64,11 @@ namespace http {
" .tunnel.failed { color: #D33F3F; }\r\n"
" .tunnel.building { color: #434343; }\r\n"
" caption { font-size: 1.5em; text-align: center; color: #894C84; }\r\n"
" table { width: 100%; border-collapse: collapse; text-align: center; }\r\n"
" .slide p, .slide [type='checkbox']{ display:none; }\r\n"
" .slide [type='checkbox']:checked ~ p { display:block; margin-top: 0; padding: 0; }\r\n"
" table { display: table; border-collapse: collapse; text-align: center; }\r\n"
" table.extaddr { text-align: left; }\r\n table.services { width: 100%; }"
" .streamdest { width: 120px; max-width: 240px; overflow: hidden; text-overflow: ellipsis;}\r\n"
" .slide div.content, .slide [type='checkbox'] { display: none; }\r\n"
" .slide [type='checkbox']:checked ~ div.content { display: block; margin-top: 0; padding: 0; }\r\n"
" .disabled:after { color: #D33F3F; content: \"Disabled\" }\r\n"
" .enabled:after { color: #56B734; content: \"Enabled\" }\r\n"
"</style>\r\n";
@@ -89,10 +92,12 @@ namespace http {
const char HTTP_COMMAND_RUN_PEER_TEST[] = "run_peer_test";
const char HTTP_COMMAND_RELOAD_CONFIG[] = "reload_config";
const char HTTP_COMMAND_LOGLEVEL[] = "set_loglevel";
const char HTTP_COMMAND_KILLSTREAM[] = "closestream";
const char HTTP_PARAM_SAM_SESSION_ID[] = "id";
const char HTTP_PARAM_ADDRESS[] = "address";
static std::string ConvertTime (uint64_t time);
std::map<uint32_t, uint32_t> HTTPConnection::m_Tokens;
static void ShowUptime (std::stringstream& s, int seconds)
{
@@ -177,8 +182,10 @@ namespace http {
"<div class=left>\r\n"
" <a href=\"" << webroot << "\">Main page</a><br>\r\n<br>\r\n"
" <a href=\"" << webroot << "?page=" << HTTP_PAGE_COMMANDS << "\">Router commands</a><br>\r\n"
" <a href=\"" << webroot << "?page=" << HTTP_PAGE_LOCAL_DESTINATIONS << "\">Local destinations</a><br>\r\n"
" <a href=\"" << webroot << "?page=" << HTTP_PAGE_LEASESETS << "\">LeaseSets</a><br>\r\n"
" <a href=\"" << webroot << "?page=" << HTTP_PAGE_LOCAL_DESTINATIONS << "\">Local destinations</a><br>\r\n";
if (i2p::context.IsFloodfill ())
s << " <a href=\"" << webroot << "?page=" << HTTP_PAGE_LEASESETS << "\">LeaseSets</a><br>\r\n";
s <<
" <a href=\"" << webroot << "?page=" << HTTP_PAGE_TUNNELS << "\">Tunnels</a><br>\r\n"
" <a href=\"" << webroot << "?page=" << HTTP_PAGE_TRANSIT_TUNNELS << "\">Transit tunnels</a><br>\r\n"
" <a href=\"" << webroot << "?page=" << HTTP_PAGE_TRANSPORTS << "\">Transports</a><br>\r\n"
@@ -203,10 +210,7 @@ namespace http {
s << "<b>ERROR:</b>&nbsp;" << string << "<br>\r\n";
}
void ShowStatus (
std::stringstream& s,
bool includeHiddenContent,
i2p::http::OutputFormatEnum outputFormat)
void ShowStatus (std::stringstream& s, bool includeHiddenContent, i2p::http::OutputFormatEnum outputFormat)
{
s << "<b>Uptime:</b> ";
ShowUptime(s, i2p::context.GetUptime ());
@@ -233,11 +237,8 @@ namespace http {
}
s << "<br>\r\n";
#if ((!defined(WIN32) && !defined(QT_GUI_LIB) && !defined(ANDROID)) || defined(ANDROID_BINARY))
if (auto remains = Daemon.gracefulShutdownInterval) {
s << "<b>Stopping in:</b> ";
s << remains << " seconds";
s << "<br>\r\n";
}
if (auto remains = Daemon.gracefulShutdownInterval)
s << "<b>Stopping in:</b> " << remains << " seconds<br>\r\n";
#endif
auto family = i2p::context.GetFamily ();
if (family.length () > 0)
@@ -253,51 +254,56 @@ namespace http {
ShowTraffic (s, i2p::transport::transports.GetTotalTransitTransmittedBytes ());
s << " (" << (double) i2p::transport::transports.GetTransitBandwidth () / 1024 << " KiB/s)<br>\r\n";
s << "<b>Data path:</b> " << i2p::fs::GetDataDir() << "<br>\r\n";
s << "<div class='slide'>";
if((outputFormat==OutputFormatEnum::forWebConsole)||!includeHiddenContent) {
s << "<label for='slide-info'>Hidden content. Press on text to see.</label>\r\n<input type='checkbox' id='slide-info'/>\r\n<p class='content'>\r\n";
}
if(includeHiddenContent) {
s << "<b>Router Ident:</b> " << i2p::context.GetRouterInfo().GetIdentHashBase64() << "<br>\r\n";
s << "<b>Router Family:</b> " << i2p::context.GetRouterInfo().GetProperty("family") << "<br>\r\n";
s << "<div class='slide'>";
if((outputFormat==OutputFormatEnum::forWebConsole)||!includeHiddenContent) {
s << "<label for='slide-info'>Hidden content. Press on text to see.</label>\r\n<input type='checkbox' id='slide-info'/>\r\n<div class='content'>\r\n";
}
if(includeHiddenContent) {
s << "<b>Router Ident:</b> " << i2p::context.GetRouterInfo().GetIdentHashBase64() << "<br>\r\n";
if (!i2p::context.GetRouterInfo().GetProperty("family").empty())
s << "<b>Router Family:</b> " << i2p::context.GetRouterInfo().GetProperty("family") << "<br>\r\n";
s << "<b>Router Caps:</b> " << i2p::context.GetRouterInfo().GetProperty("caps") << "<br>\r\n";
s << "<b>Our external address:</b>" << "<br>\r\n" ;
s << "<b>Our external address:</b>" << "<br>\r\n<table class=\"extaddr\"><tbody>\r\n";
for (const auto& address : i2p::context.GetRouterInfo().GetAddresses())
{
s << "<tr>\r\n";
if (address->IsNTCP2 () && !address->IsPublishedNTCP2 ())
{
s << "NTCP2";
s << "<td>NTCP2";
if (address->host.is_v6 ()) s << "v6";
s << "&nbsp;&nbsp; supported <br>\r\n";
s << "</td><td>supported</td>\r\n</tr>\r\n";
continue;
}
switch (address->transportStyle)
{
case i2p::data::RouterInfo::eTransportNTCP:
{
s << "NTCP";
s << "<td>NTCP";
if (address->IsPublishedNTCP2 ()) s << "2";
if (address->host.is_v6 ()) s << "v6";
s << "&nbsp;&nbsp;";
s << "</td>\r\n";
break;
}
case i2p::data::RouterInfo::eTransportSSU:
{
s << "<td>SSU";
if (address->host.is_v6 ())
s << "SSUv6&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;";
else
s << "SSU&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;";
break;
s << "v6";
s << "</td>\r\n";
break;
}
default:
s << "Unknown&nbsp;&nbsp;";
s << "<td>Unknown</td>\r\n";
}
s << address->host.to_string() << ":" << address->port << "<br>\r\n";
s << "<td>" << address->host.to_string() << ":" << address->port << "</td>\r\n</tr>\r\n";
}
}
s << "</p>\r\n</div>\r\n";
if(outputFormat==OutputFormatEnum::forQtUi) {
s << "<br>";
}
s << "<b>Routers:</b> " << i2p::data::netdb.GetNumRouters () << " ";
s << "</tbody></table>\r\n";
}
s << "</div>\r\n</div>\r\n";
if(outputFormat==OutputFormatEnum::forQtUi) {
s << "<br>";
}
s << "<b>Routers:</b> " << i2p::data::netdb.GetNumRouters () << " ";
s << "<b>Floodfills:</b> " << i2p::data::netdb.GetNumFloodfills () << " ";
s << "<b>LeaseSets:</b> " << i2p::data::netdb.GetNumLeaseSets () << "<br>\r\n";
@@ -308,17 +314,17 @@ namespace http {
s << "<b>Client Tunnels:</b> " << std::to_string(clientTunnelCount) << " ";
s << "<b>Transit Tunnels:</b> " << std::to_string(transitTunnelCount) << "<br>\r\n<br>\r\n";
if(outputFormat==OutputFormatEnum::forWebConsole) {
s << "<table><caption>Services</caption><tr><th>Service</th><th>State</th></tr>\r\n";
s << "<tr><td>" << "HTTP Proxy" << "</td><td><div class='" << ((i2p::client::context.GetHttpProxy ()) ? "enabled" : "disabled") << "'></div></td></tr>\r\n";
s << "<tr><td>" << "SOCKS Proxy" << "</td><td><div class='" << ((i2p::client::context.GetSocksProxy ()) ? "enabled" : "disabled") << "'></div></td></tr>\r\n";
s << "<tr><td>" << "BOB" << "</td><td><div class='" << ((i2p::client::context.GetBOBCommandChannel ()) ? "enabled" : "disabled") << "'></div></td></tr>\r\n";
s << "<tr><td>" << "SAM" << "</td><td><div class='" << ((i2p::client::context.GetSAMBridge ()) ? "enabled" : "disabled") << "'></div></td></tr>\r\n";
s << "<tr><td>" << "I2CP" << "</td><td><div class='" << ((i2p::client::context.GetI2CPServer ()) ? "enabled" : "disabled") << "'></div></td></tr>\r\n";
bool i2pcontrol; i2p::config::GetOption("i2pcontrol.enabled", i2pcontrol);
s << "<tr><td>" << "I2PControl" << "</td><td><div class='" << ((i2pcontrol) ? "enabled" : "disabled") << "'></div></td></tr>\r\n";
s << "</table>\r\n";
}
if(outputFormat==OutputFormatEnum::forWebConsole) {
bool i2pcontrol; i2p::config::GetOption("i2pcontrol.enabled", i2pcontrol);
s << "<table class=\"services\"><caption>Services</caption><tbody>\r\n";
s << "<tr><td>" << "HTTP Proxy" << "</td><td><div class='" << ((i2p::client::context.GetHttpProxy ()) ? "enabled" : "disabled") << "'></div></td></tr>\r\n";
s << "<tr><td>" << "SOCKS Proxy" << "</td><td><div class='" << ((i2p::client::context.GetSocksProxy ()) ? "enabled" : "disabled") << "'></div></td></tr>\r\n";
s << "<tr><td>" << "BOB" << "</td><td><div class='" << ((i2p::client::context.GetBOBCommandChannel ()) ? "enabled" : "disabled") << "'></div></td></tr>\r\n";
s << "<tr><td>" << "SAM" << "</td><td><div class='" << ((i2p::client::context.GetSAMBridge ()) ? "enabled" : "disabled") << "'></div></td></tr>\r\n";
s << "<tr><td>" << "I2CP" << "</td><td><div class='" << ((i2p::client::context.GetI2CPServer ()) ? "enabled" : "disabled") << "'></div></td></tr>\r\n";
s << "<tr><td>" << "I2PControl" << "</td><td><div class='" << ((i2pcontrol) ? "enabled" : "disabled") << "'></div></td></tr>\r\n";
s << "</tbody></table>\r\n";
}
}
void ShowLocalDestinations (std::stringstream& s)
@@ -352,24 +358,25 @@ namespace http {
static void ShowLeaseSetDestination (std::stringstream& s, std::shared_ptr<const i2p::client::LeaseSetDestination> dest)
{
s << "<b>Base64:</b><br>\r\n<textarea readonly=\"readonly\" cols=\"64\" rows=\"11\" wrap=\"on\">";
s << "<b>Base64:</b><br>\r\n<textarea readonly cols=\"80\" rows=\"11\" wrap=\"on\">";
s << dest->GetIdentity ()->ToBase64 () << "</textarea><br>\r\n<br>\r\n";
if (dest->IsEncryptedLeaseSet ())
{
i2p::data::BlindedPublicKey blinded (dest->GetIdentity ());
s << "<div class='slide'><label for='slide-b33'><b>Encrypted B33 address:</b></label>\r\n<input type='checkbox' id='slide-b33'/>\r\n<p class='content'>\r\n";
i2p::data::BlindedPublicKey blinded (dest->GetIdentity (), dest->IsPerClientAuth ());
s << "<div class='slide'><label for='slide-b33'><b>Encrypted B33 address:</b></label>\r\n<input type='checkbox' id='slide-b33'/>\r\n<div class='content'>\r\n";
s << blinded.ToB33 () << ".b32.i2p<br>\r\n";
s << "</p>\r\n</div>\r\n";
s << "</div>\r\n</div>\r\n";
}
if(dest->GetNumRemoteLeaseSets())
{
s << "<div class='slide'><label for='slide-lease'><b>LeaseSets:</b> <i>" << dest->GetNumRemoteLeaseSets () << "</i></label>\r\n<input type='checkbox' id='slide-lease'/>\r\n<p class='content'>\r\n";
s << "<div class='slide'><label for='slide-lease'><b>LeaseSets:</b> <i>" << dest->GetNumRemoteLeaseSets ()
<< "</i></label>\r\n<input type='checkbox' id='slide-lease'/>\r\n<div class='content'>\r\n<table><thead><th>Address</th><th>Type</th><th>EncType</th></thead><tbody>";
for(auto& it: dest->GetLeaseSets ())
s << it.first.ToBase32 () << " " << (int)it.second->GetStoreType () << "<br>\r\n";
s << "</p>\r\n</div>\r\n";
s << "<tr><td>" << it.first.ToBase32 () << "</td><td>" << (int)it.second->GetStoreType () << "</td><td>" << (int)it.second->GetEncryptionType () <<"</td></tr>\r\n";
s << "</tbody></table>\r\n</div>\r\n</div>\r\n<br>\r\n";
} else
s << "<b>LeaseSets:</b> <i>0</i><br>\r\n";
s << "<b>LeaseSets:</b> <i>0</i><br>\r\n<br>\r\n";
auto pool = dest->GetTunnelPool ();
if (pool)
{
@@ -394,28 +401,31 @@ namespace http {
if (!dest->GetSessions ().empty ()) {
std::stringstream tmp_s; uint32_t out_tags = 0;
for (const auto& it: dest->GetSessions ()) {
tmp_s << i2p::client::context.GetAddressBook ().ToAddress(it.first) << " " << it.second->GetNumOutgoingTags () << "<br>\r\n";
tmp_s << "<tr><td>" << i2p::client::context.GetAddressBook ().ToAddress(it.first) << "</td><td>" << it.second->GetNumOutgoingTags () << "</td></tr>\r\n";
out_tags = out_tags + it.second->GetNumOutgoingTags ();
}
s << "<div class='slide'><label for='slide-tags'>Outgoing: <i>" << out_tags << "</i></label>\r\n<input type='checkbox' id='slide-tags'/>\r\n<p class='content'>\r\n" << tmp_s.str () << "</p>\r\n</div>\r\n";
s << "<div class='slide'><label for='slide-tags'>Outgoing: <i>" << out_tags << "</i></label>\r\n<input type='checkbox' id='slide-tags'/>\r\n"
<< "<div class='content'>\r\n<table><tbody><thead><th>Destination</th><th>Amount</th></thead>\r\n" << tmp_s.str () << "</tbody></table>\r\n</div>\r\n</div>\r\n";
} else
s << "Outgoing: <i>0</i><br>\r\n";
s << "<br>\r\n";
}
void ShowLocalDestination (std::stringstream& s, const std::string& b32)
void ShowLocalDestination (std::stringstream& s, const std::string& b32, uint32_t token)
{
s << "<b>Local Destination:</b><br>\r\n<br>\r\n";
i2p::data::IdentHash ident;
ident.FromBase32 (b32);
auto dest = i2p::client::context.FindLocalDestination (ident);
if (dest)
{
ShowLeaseSetDestination (s, dest);
// show streams
s << "<table><caption>Streams</caption>\r\n<tr>";
s << "<th>StreamID</th>";
s << "<th>Destination</th>";
s << "<table>\r\n<caption>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>";
@@ -424,13 +434,20 @@ namespace http {
s << "<th>RTT</th>";
s << "<th>Window</th>";
s << "<th>Status</th>";
s << "</tr>\r\n";
s << "</tr>\r\n</thead>\r\n<tbody>\r\n";
for (const auto& it: dest->GetAllStreams ())
{
auto streamDest = i2p::client::context.GetAddressBook ().ToAddress(it->GetRemoteIdentity ());
s << "<tr>";
s << "<td>" << it->GetSendStreamID () << "</td>";
s << "<td>" << i2p::client::context.GetAddressBook ().ToAddress(it->GetRemoteIdentity ()) << "</td>";
s << "<td>" << it->GetRecvStreamID () << "</td>";
if (it->GetRecvStreamID ()) {
s << "<td><a class=\"button\" href=\"/?cmd=" << HTTP_COMMAND_KILLSTREAM << "&b32=" << b32 << "&streamID="
<< it->GetRecvStreamID () << "&token=" << token << "\" title=\"Close stream\"> &#10008; </a></td>";
} else {
s << "<td \\>";
}
s << "<td class=\"streamdest\" title=\"" << streamDest << "\">" << streamDest << "</td>";
s << "<td>" << it->GetNumSentBytes () << "</td>";
s << "<td>" << it->GetNumReceivedBytes () << "</td>";
s << "<td>" << it->GetSendQueueSize () << "</td>";
@@ -441,7 +458,7 @@ namespace http {
s << "<td>" << (int)it->GetStatus () << "</td>";
s << "</tr>\r\n";
}
s << "</table>";
s << "</tbody>\r\n</table>";
}
}
@@ -463,46 +480,57 @@ namespace http {
void ShowLeasesSets(std::stringstream& s)
{
s << "<b>LeaseSets:</b><br>\r\n<br>\r\n";
int counter = 1;
// for each lease set
i2p::data::netdb.VisitLeaseSets(
[&s, &counter](const i2p::data::IdentHash dest, std::shared_ptr<i2p::data::LeaseSet> leaseSet)
{
// create copy of lease set so we extract leases
auto storeType = leaseSet->GetStoreType ();
std::unique_ptr<i2p::data::LeaseSet> ls;
if (storeType == i2p::data::NETDB_STORE_TYPE_LEASESET)
ls.reset (new i2p::data::LeaseSet (leaseSet->GetBuffer(), leaseSet->GetBufferLen()));
else
ls.reset (new i2p::data::LeaseSet2 (storeType, leaseSet->GetBuffer(), leaseSet->GetBufferLen()));
if (!ls) return;
s << "<div class='leaseset";
if (ls->IsExpired())
s << " expired"; // additional css class for expired
s << "'>\r\n";
if (!ls->IsValid())
s << "<div class='invalid'>!! Invalid !! </div>\r\n";
s << "<div class='slide'><label for='slide" << counter << "'>" << dest.ToBase32() << "</label>\r\n";
s << "<input type='checkbox' id='slide" << (counter++) << "'/>\r\n<p class='content'>\r\n";
s << "<b>Store type:</b> " << (int)storeType << "<br>\r\n";
s << "<b>Expires:</b> " << ConvertTime(ls->GetExpirationTime()) << "<br>\r\n";
if (storeType == i2p::data::NETDB_STORE_TYPE_LEASESET || storeType == i2p::data::NETDB_STORE_TYPE_STANDARD_LEASESET2)
{
// leases information is available
auto leases = ls->GetNonExpiredLeases();
s << "<b>Non Expired Leases: " << leases.size() << "</b><br>\r\n";
for ( auto & l : leases )
if (i2p::data::netdb.GetNumLeaseSets ())
{
s << "<b>LeaseSets:</b><br>\r\n<br>\r\n";
int counter = 1;
// for each lease set
i2p::data::netdb.VisitLeaseSets(
[&s, &counter](const i2p::data::IdentHash dest, std::shared_ptr<i2p::data::LeaseSet> leaseSet)
{
// create copy of lease set so we extract leases
auto storeType = leaseSet->GetStoreType ();
std::unique_ptr<i2p::data::LeaseSet> ls;
if (storeType == i2p::data::NETDB_STORE_TYPE_LEASESET)
ls.reset (new i2p::data::LeaseSet (leaseSet->GetBuffer(), leaseSet->GetBufferLen()));
else
ls.reset (new i2p::data::LeaseSet2 (storeType, leaseSet->GetBuffer(), leaseSet->GetBufferLen()));
if (!ls) return;
s << "<div class='leaseset";
if (ls->IsExpired())
s << " expired"; // additional css class for expired
s << "'>\r\n";
if (!ls->IsValid())
s << "<div class='invalid'>!! Invalid !! </div>\r\n";
s << "<div class='slide'><label for='slide" << counter << "'>" << dest.ToBase32() << "</label>\r\n";
s << "<input type='checkbox' id='slide" << (counter++) << "'/>\r\n<div class='content'>\r\n";
s << "<b>Store type:</b> " << (int)storeType << "<br>\r\n";
s << "<b>Expires:</b> " << ConvertTime(ls->GetExpirationTime()) << "<br>\r\n";
if (storeType == i2p::data::NETDB_STORE_TYPE_LEASESET || storeType == i2p::data::NETDB_STORE_TYPE_STANDARD_LEASESET2)
{
s << "<b>Gateway:</b> " << l->tunnelGateway.ToBase64() << "<br>\r\n";
s << "<b>TunnelID:</b> " << l->tunnelID << "<br>\r\n";
s << "<b>EndDate:</b> " << ConvertTime(l->endDate) << "<br>\r\n";
// leases information is available
auto leases = ls->GetNonExpiredLeases();
s << "<b>Non Expired Leases: " << leases.size() << "</b><br>\r\n";
for ( auto & l : leases )
{
s << "<b>Gateway:</b> " << l->tunnelGateway.ToBase64() << "<br>\r\n";
s << "<b>TunnelID:</b> " << l->tunnelID << "<br>\r\n";
s << "<b>EndDate:</b> " << ConvertTime(l->endDate) << "<br>\r\n";
}
}
}
s << "</p>\r\n</div>\r\n</div>\r\n";
}
);
// end for each lease set
s << "</div>\r\n</div>\r\n</div>\r\n";
}
);
// end for each lease set
}
else if (!i2p::context.IsFloodfill ())
{
s << "<b>LeaseSets:</b> not floodfill.<br>\r\n";
}
else
{
s << "<b>LeaseSets:</b> 0<br>\r\n";
}
}
void ShowTunnels (std::stringstream& s)
@@ -564,16 +592,23 @@ namespace http {
void ShowTransitTunnels (std::stringstream& s)
{
s << "<b>Transit tunnels:</b><br>\r\n<br>\r\n";
for (const auto& it: i2p::tunnel::tunnels.GetTransitTunnels ())
if(i2p::tunnel::tunnels.CountTransitTunnels())
{
if (std::dynamic_pointer_cast<i2p::tunnel::TransitTunnelGateway>(it))
s << it->GetTunnelID () << " &#8658; ";
else if (std::dynamic_pointer_cast<i2p::tunnel::TransitTunnelEndpoint>(it))
s << " &#8658; " << it->GetTunnelID ();
else
s << " &#8658; " << it->GetTunnelID () << " &#8658; ";
s << " " << it->GetNumTransmittedBytes () << "<br>\r\n";
s << "<b>Transit tunnels:</b><br>\r\n<br>\r\n";
for (const auto& it: i2p::tunnel::tunnels.GetTransitTunnels ())
{
if (std::dynamic_pointer_cast<i2p::tunnel::TransitTunnelGateway>(it))
s << it->GetTunnelID () << " &#8658; ";
else if (std::dynamic_pointer_cast<i2p::tunnel::TransitTunnelEndpoint>(it))
s << " &#8658; " << it->GetTunnelID ();
else
s << " &#8658; " << it->GetTunnelID () << " &#8658; ";
s << " " << it->GetNumTransmittedBytes () << "<br>\r\n";
}
}
else
{
s << "<b>Transit tunnels:</b> no transit tunnels currently built.<br>\r\n";
}
}
@@ -607,13 +642,15 @@ namespace http {
}
if (!tmp_s.str ().empty ())
{
s << "<div class='slide'><label for='slide_" << boost::algorithm::to_lower_copy(name) << "'><b>" << name << "</b> ( " << cnt << " )</label>\r\n<input type='checkbox' id='slide_" << boost::algorithm::to_lower_copy(name) << "'/>\r\n<p class='content'>";
s << tmp_s.str () << "</p>\r\n</div>\r\n";
s << "<div class='slide'><label for='slide_" << boost::algorithm::to_lower_copy(name) << "'><b>" << name
<< "</b> ( " << cnt << " )</label>\r\n<input type='checkbox' id='slide_" << boost::algorithm::to_lower_copy(name) << "'/>\r\n<div class='content'>"
<< tmp_s.str () << "</div>\r\n</div>\r\n";
}
if (!tmp_s6.str ().empty ())
{
s << "<div class='slide'><label for='slide_" << boost::algorithm::to_lower_copy(name) << "v6'><b>" << name << "v6</b> ( " << cnt6 << " )</label>\r\n<input type='checkbox' id='slide_" << boost::algorithm::to_lower_copy(name) << "v6'/>\r\n<p class='content'>";
s << tmp_s6.str () << "</p>\r\n</div>\r\n";
s << "<div class='slide'><label for='slide_" << boost::algorithm::to_lower_copy(name) << "v6'><b>" << name
<< "v6</b> ( " << cnt6 << " )</label>\r\n<input type='checkbox' id='slide_" << boost::algorithm::to_lower_copy(name) << "v6'/>\r\n<div class='content'>"
<< tmp_s6.str () << "</div>\r\n</div>\r\n";
}
}
@@ -640,7 +677,7 @@ namespace http {
auto sessions = ssuServer->GetSessions ();
if (!sessions.empty ())
{
s << "<div class='slide'><label for='slide_ssu'><b>SSU</b> ( " << (int) sessions.size() << " )</label>\r\n<input type='checkbox' id='slide_ssu'/>\r\n<p class='content'>";
s << "<div class='slide'><label for='slide_ssu'><b>SSU</b> ( " << (int) sessions.size() << " )</label>\r\n<input type='checkbox' id='slide_ssu'/>\r\n<div class='content'>";
for (const auto& it: sessions)
{
auto endpoint = it.second->GetRemoteEndpoint ();
@@ -652,12 +689,12 @@ namespace http {
s << " [itag:" << it.second->GetRelayTag () << "]";
s << "<br>\r\n" << std::endl;
}
s << "</p>\r\n</div>\r\n";
s << "</div>\r\n</div>\r\n";
}
auto sessions6 = ssuServer->GetSessionsV6 ();
if (!sessions6.empty ())
{
s << "<div class='slide'><label for='slide_ssuv6'><b>SSUv6</b> ( " << (int) sessions6.size() << " )</label>\r\n<input type='checkbox' id='slide_ssuv6'/>\r\n<p class='content'>";
s << "<div class='slide'><label for='slide_ssuv6'><b>SSUv6</b> ( " << (int) sessions6.size() << " )</label>\r\n<input type='checkbox' id='slide_ssuv6'/>\r\n<div class='content'>";
for (const auto& it: sessions6)
{
auto endpoint = it.second->GetRemoteEndpoint ();
@@ -669,7 +706,7 @@ namespace http {
s << " [itag:" << it.second->GetRelayTag () << "]";
s << "<br>\r\n" << std::endl;
}
s << "</p>\r\n</div>\r\n";
s << "</div>\r\n</div>\r\n";
}
}
}
@@ -678,17 +715,24 @@ namespace http {
{
std::string webroot; i2p::config::GetOption("http.webroot", webroot);
auto sam = i2p::client::context.GetSAMBridge ();
if (!sam) {
if (!sam)
{
ShowError(s, "SAM disabled");
return;
}
s << "<b>SAM Sessions:</b><br>\r\n<br>\r\n";
for (auto& it: sam->GetSessions ())
if(sam->GetSessions ().size ())
{
auto& name = it.second->localDestination->GetNickname ();
s << "<a href=\"" << webroot << "?page=" << HTTP_PAGE_SAM_SESSION << "&sam_id=" << it.first << "\">";
s << name << " (" << it.first << ")</a><br>\r\n" << std::endl;
s << "<b>SAM Sessions:</b><br>\r\n<br>\r\n";
for (auto& it: sam->GetSessions ())
{
auto& name = it.second->localDestination->GetNickname ();
s << "<a href=\"" << webroot << "?page=" << HTTP_PAGE_SAM_SESSION << "&sam_id=" << it.first << "\">";
s << name << " (" << it.first << ")</a><br>\r\n" << std::endl;
}
}
else
s << "<b>SAM Sessions:</b> no sessions currently running.<br>\r\n";
}
static void ShowSAMSession (std::stringstream& s, const std::string& id)
@@ -858,7 +902,8 @@ namespace http {
m_Socket->close ();
}
bool HTTPConnection::CheckAuth (const HTTPReq & req) {
bool HTTPConnection::CheckAuth (const HTTPReq & req)
{
/* method #1: http://user:pass@127.0.0.1:7070/ */
if (req.uri.find('@') != std::string::npos) {
URL url;
@@ -920,7 +965,7 @@ namespace http {
} else if (req.uri.find("cmd=") != std::string::npos) {
HandleCommand (req, res, s);
} else {
ShowStatus (s, true, i2p::http::OutputFormatEnum::forWebConsole);
ShowStatus (s, true, i2p::http::OutputFormatEnum::forWebConsole);
res.add_header("Refresh", "10");
}
ShowPageTail (s);
@@ -930,7 +975,23 @@ namespace http {
SendReply (res, content);
}
std::map<uint32_t, uint32_t> HTTPConnection::m_Tokens;
uint32_t HTTPConnection::CreateToken ()
{
uint32_t token;
RAND_bytes ((uint8_t *)&token, 4);
token &= 0x7FFFFFFF; // clear first bit
auto ts = i2p::util::GetSecondsSinceEpoch ();
for (auto it = m_Tokens.begin (); it != m_Tokens.end (); )
{
if (ts > it->second + TOKEN_EXPIRATION_TIMEOUT)
it = m_Tokens.erase (it);
else
++it;
}
m_Tokens[token] = ts;
return token;
}
void HTTPConnection::HandlePage (const HTTPReq& req, HTTPRes& res, std::stringstream& s)
{
std::map<std::string, std::string> params;
@@ -947,18 +1008,7 @@ namespace http {
ShowTunnels (s);
else if (page == HTTP_PAGE_COMMANDS)
{
uint32_t token;
RAND_bytes ((uint8_t *)&token, 4);
token &= 0x7FFFFFFF; // clear first bit
auto ts = i2p::util::GetSecondsSinceEpoch ();
for (auto it = m_Tokens.begin (); it != m_Tokens.end (); )
{
if (ts > it->second + TOKEN_EXPIRATION_TIMEOUT)
it = m_Tokens.erase (it);
else
++it;
}
m_Tokens[token] = ts;
uint32_t token = CreateToken ();
ShowCommands (s, token);
}
else if (page == HTTP_PAGE_TRANSIT_TUNNELS)
@@ -966,7 +1016,10 @@ namespace http {
else if (page == HTTP_PAGE_LOCAL_DESTINATIONS)
ShowLocalDestinations (s);
else if (page == HTTP_PAGE_LOCAL_DESTINATION)
ShowLocalDestination (s, params["b32"]);
{
uint32_t token = CreateToken ();
ShowLocalDestination (s, params["b32"], token);
}
else if (page == HTTP_PAGE_I2CP_LOCAL_DESTINATION)
ShowI2CPLocalDestination (s, params["i2cp_id"]);
else if (page == HTTP_PAGE_SAM_SESSIONS)
@@ -992,7 +1045,10 @@ namespace http {
url.parse(req.uri);
url.parse_query(params);
std::string webroot; i2p::config::GetOption("http.webroot", webroot);
std::string redirect = "5; url=" + webroot + "?page=commands";
std::string token = params["token"];
if (token.empty () || m_Tokens.find (std::stoi (token)) == m_Tokens.end ())
{
ShowError(s, "Invalid token");
@@ -1008,36 +1064,74 @@ namespace http {
i2p::context.SetAcceptsTunnels (true);
else if (cmd == HTTP_COMMAND_DISABLE_TRANSIT)
i2p::context.SetAcceptsTunnels (false);
else if (cmd == HTTP_COMMAND_SHUTDOWN_START) {
else if (cmd == HTTP_COMMAND_SHUTDOWN_START)
{
i2p::context.SetAcceptsTunnels (false);
#if ((!defined(WIN32) && !defined(QT_GUI_LIB) && !defined(ANDROID)) || defined(ANDROID_BINARY))
Daemon.gracefulShutdownInterval = 10*60;
#elif defined(WIN32_APP)
i2p::win32::GracefulShutdown ();
#endif
} else if (cmd == HTTP_COMMAND_SHUTDOWN_CANCEL) {
}
else if (cmd == HTTP_COMMAND_SHUTDOWN_CANCEL)
{
i2p::context.SetAcceptsTunnels (true);
#if ((!defined(WIN32) && !defined(QT_GUI_LIB) && !defined(ANDROID)) || defined(ANDROID_BINARY))
Daemon.gracefulShutdownInterval = 0;
#elif defined(WIN32_APP)
i2p::win32::StopGracefulShutdown ();
#endif
} else if (cmd == HTTP_COMMAND_SHUTDOWN_NOW) {
}
else if (cmd == HTTP_COMMAND_SHUTDOWN_NOW)
{
#ifndef WIN32_APP
Daemon.running = false;
#else
i2p::win32::StopWin32App ();
#endif
} else if (cmd == HTTP_COMMAND_LOGLEVEL){
}
else if (cmd == HTTP_COMMAND_LOGLEVEL)
{
std::string level = params["level"];
SetLogLevel (level);
} else {
}
else if (cmd == HTTP_COMMAND_KILLSTREAM)
{
std::string b32 = params["b32"];
uint32_t streamID = std::stoul(params["streamID"], nullptr);
i2p::data::IdentHash ident;
ident.FromBase32 (b32);
auto dest = i2p::client::context.FindLocalDestination (ident);
if (streamID)
{
if (dest)
{
if(dest->DeleteStream (streamID))
s << "<b>SUCCESS</b>:&nbsp;Stream closed<br><br>\r\n";
else
s << "<b>ERROR</b>:&nbsp;Stream not found or already was closed<br><br>\r\n";
}
else
s << "<b>ERROR</b>:&nbsp;Destination not found<br><br>\r\n";
}
else
s << "<b>ERROR</b>:&nbsp;StreamID can be null<br><br>\r\n";
s << "<a href=\"" << webroot << "?page=local_destination&b32=" << b32 << "\">Return to destination page</a><br>\r\n";
s << "<p>You will be redirected back in 5 seconds</b>";
redirect = "5; url=" + webroot + "?page=local_destination&b32=" + b32;
res.add_header("Refresh", redirect.c_str());
return;
}
else
{
res.code = 400;
ShowError(s, "Unknown command: " + cmd);
return;
}
std::string webroot; i2p::config::GetOption("http.webroot", webroot);
std::string redirect = "5; url=" + webroot + "?page=commands";
s << "<b>SUCCESS</b>:&nbsp;Command accepted<br><br>\r\n";
s << "<a href=\"" << webroot << "?page=commands\">Back to commands list</a><br>\r\n";
s << "<p>You will be redirected in 5 seconds</b>";
@@ -1047,6 +1141,8 @@ namespace http {
void HTTPConnection::SendReply (HTTPRes& reply, std::string& content)
{
reply.add_header("X-Frame-Options", "SAMEORIGIN");
reply.add_header("X-Content-Type-Options", "nosniff");
reply.add_header("X-XSS-Protection", "1; mode=block");
reply.add_header("Content-Type", "text/html");
reply.body = content;

View File

@@ -35,6 +35,7 @@ namespace http
void HandlePage (const HTTPReq & req, HTTPRes & res, std::stringstream& data);
void HandleCommand (const HTTPReq & req, HTTPRes & res, std::stringstream& data);
void SendReply (HTTPRes & res, std::string & content);
uint32_t CreateToken ();
private:
@@ -88,7 +89,7 @@ namespace http
void ShowTransports (std::stringstream& s);
void ShowSAMSessions (std::stringstream& s);
void ShowI2PTunnels (std::stringstream& s);
void ShowLocalDestination (std::stringstream& s, const std::string& b32);
void ShowLocalDestination (std::stringstream& s, const std::string& b32, uint32_t token);
} // http
} // i2p

View File

@@ -394,13 +394,15 @@ namespace client
void I2PControlService::RouterInfoHandler (const boost::property_tree::ptree& params, std::ostringstream& results)
{
bool first = true;
for (auto it = params.begin (); it != params.end (); it++)
{
LogPrint (eLogDebug, "I2PControl: RouterInfo request: ", it->first);
auto it1 = m_RouterInfoHandlers.find (it->first);
if (it1 != m_RouterInfoHandlers.end ())
{
if (it != params.begin ()) results << ",";
if (!first) results << ",";
else first = false;
(this->*(it1->second))(results);
}
else

View File

@@ -79,43 +79,59 @@ namespace transport
void UPnP::Discover ()
{
#if MINIUPNPC_API_VERSION >= 14
int nerror = 0;
m_Devlist = upnpDiscover (2000, m_MulticastIf, m_Minissdpdpath, 0, 0, 2, &nerror);
#elif ( MINIUPNPC_API_VERSION >= 8 || defined(UPNPDISCOVER_SUCCESS) )
int nerror = 0;
m_Devlist = upnpDiscover (2000, m_MulticastIf, m_Minissdpdpath, 0, 0, &nerror);
bool isError;
int err;
#if ((MINIUPNPC_API_VERSION >= 8) || defined (UPNPDISCOVER_SUCCESS))
err = UPNPDISCOVER_SUCCESS;
#if (MINIUPNPC_API_VERSION >= 14)
m_Devlist = upnpDiscover (UPNP_RESPONSE_TIMEOUT, NULL, NULL, 0, 0, 2, &err);
#else
m_Devlist = upnpDiscover (2000, m_MulticastIf, m_Minissdpdpath, 0);
m_Devlist = upnpDiscover (UPNP_RESPONSE_TIMEOUT, NULL, NULL, 0, 0, &err);
#endif
isError = err != UPNPDISCOVER_SUCCESS;
#else // MINIUPNPC_API_VERSION >= 8
err = 0;
m_Devlist = upnpDiscover (UPNP_RESPONSE_TIMEOUT, NULL, NULL, 0);
isError = m_Devlist == NULL;
#endif // MINIUPNPC_API_VERSION >= 8
{
// notify satrting thread
// notify starting thread
std::unique_lock<std::mutex> l(m_StartedMutex);
m_Started.notify_all ();
}
int r;
r = UPNP_GetValidIGD (m_Devlist, &m_upnpUrls, &m_upnpData, m_NetworkAddr, sizeof (m_NetworkAddr));
if (r == 1)
if (isError)
{
r = UPNP_GetExternalIPAddress (m_upnpUrls.controlURL, m_upnpData.first.servicetype, m_externalIPAddress);
if(r != UPNPCOMMAND_SUCCESS)
LogPrint (eLogError, "UPnP: unable to discover Internet Gateway Devices: error ", err);
return;
}
err = UPNP_GetValidIGD (m_Devlist, &m_upnpUrls, &m_upnpData, m_NetworkAddr, sizeof (m_NetworkAddr));
m_upnpUrlsInitialized=err!=0;
if (err == UPNP_IGD_VALID_CONNECTED)
{
err = UPNP_GetExternalIPAddress (m_upnpUrls.controlURL, m_upnpData.first.servicetype, m_externalIPAddress);
if(err != UPNPCOMMAND_SUCCESS)
{
LogPrint (eLogError, "UPnP: UPNP_GetExternalIPAddress() returned ", r);
LogPrint (eLogError, "UPnP: unable to get external address: error ", err);
return;
}
else
{
LogPrint (eLogError, "UPnP: found Internet Gateway Device ", m_upnpUrls.controlURL);
if (!m_externalIPAddress[0])
{
LogPrint (eLogError, "UPnP: GetExternalIPAddress() failed.");
LogPrint (eLogError, "UPnP: found Internet Gateway Device doesn't know our external address");
return;
}
}
}
else
{
LogPrint (eLogError, "UPnP: GetValidIGD() failed.");
LogPrint (eLogError, "UPnP: unable to find valid Internet Gateway Device: error ", err);
return;
}
@@ -126,6 +142,20 @@ namespace transport
PortMapping ();
}
int UPnP::CheckMapping (const char* port, const char* type)
{
int err = UPNPCOMMAND_SUCCESS;
#if (MINIUPNPC_API_VERSION >= 10)
err = UPNP_GetSpecificPortMappingEntry(m_upnpUrls.controlURL, m_upnpData.first.servicetype, port, type, NULL, NULL, NULL, NULL, NULL, NULL);
#elif ((MINIUPNPC_API_VERSION >= 8) || defined (UPNPDISCOVER_SUCCESS))
err = UPNP_GetSpecificPortMappingEntry(m_upnpUrls.controlURL, m_upnpData.first.servicetype, port, type, NULL, NULL, NULL, NULL, NULL);
#else
err = UPNP_GetSpecificPortMappingEntry(m_upnpUrls.controlURL, m_upnpData.first.servicetype, port, type, NULL, NULL);
#endif
return err;
}
void UPnP::PortMapping ()
{
const auto& a = context.GetRouterInfo().GetAddresses();
@@ -134,13 +164,47 @@ namespace transport
if (!address->host.is_v6 () && address->port)
TryPortMapping (address);
}
m_Timer.expires_from_now (boost::posix_time::minutes(20)); // every 20 minutes
m_Timer.expires_from_now (boost::posix_time::minutes(20)); // every 20 minutes
m_Timer.async_wait ([this](const boost::system::error_code& ecode)
{
if (ecode != boost::asio::error::operation_aborted)
PortMapping ();
});
}
void UPnP::TryPortMapping (std::shared_ptr<i2p::data::RouterInfo::Address> address)
{
std::string strType (GetProto (address)), strPort (std::to_string (address->port));
std::string strDesc; i2p::config::GetOption("upnp.name", strDesc);
int err = UPNPCOMMAND_SUCCESS;
// check for existing mapping
err = CheckMapping (strPort.c_str (), strType.c_str ());
if (err != UPNPCOMMAND_SUCCESS) // if mapping not found
{
LogPrint (eLogDebug, "UPnP: possibly port ", strPort, " is not forwarded: return code ", err);
#if ((MINIUPNPC_API_VERSION >= 8) || defined (UPNPDISCOVER_SUCCESS))
err = UPNP_AddPortMapping (m_upnpUrls.controlURL, m_upnpData.first.servicetype, strPort.c_str (), strPort.c_str (), m_NetworkAddr, strDesc.c_str (), strType.c_str (), NULL, NULL);
#else
err = UPNP_AddPortMapping (m_upnpUrls.controlURL, m_upnpData.first.servicetype, strPort.c_str (), strPort.c_str (), m_NetworkAddr, strDesc.c_str (), strType.c_str (), NULL);
#endif
if (err != UPNPCOMMAND_SUCCESS)
{
LogPrint (eLogError, "UPnP: port forwarding to ", m_NetworkAddr, ":", strPort, " failed: return code ", err);
return;
}
else
{
LogPrint (eLogInfo, "UPnP: port successfully forwarded (", m_externalIPAddress ,":", strPort, " type ", strType, " -> ", m_NetworkAddr ,":", strPort ,")");
return;
}
}
else
{
LogPrint (eLogDebug, "UPnP: external forward from ", m_NetworkAddr, ":", strPort, " exists on current Internet Gateway Device");
return;
}
}
void UPnP::CloseMapping ()
@@ -153,53 +217,42 @@ namespace transport
}
}
void UPnP::TryPortMapping (std::shared_ptr<i2p::data::RouterInfo::Address> address)
{
std::string strType (GetProto (address)), strPort (std::to_string (address->port));
int r;
std::string strDesc; i2p::config::GetOption("upnp.name", strDesc);
#ifdef UPNPDISCOVER_SUCCESS
r = UPNP_AddPortMapping (m_upnpUrls.controlURL, m_upnpData.first.servicetype, strPort.c_str (), strPort.c_str (), m_NetworkAddr, strDesc.c_str (), strType.c_str (), 0, "0");
#else
r = UPNP_AddPortMapping (m_upnpUrls.controlURL, m_upnpData.first.servicetype, strPort.c_str (), strPort.c_str (), m_NetworkAddr, strDesc.c_str (), strType.c_str (), 0);
#endif
if (r!=UPNPCOMMAND_SUCCESS)
{
LogPrint (eLogError, "UPnP: AddPortMapping (", m_NetworkAddr, ":", strPort, ") failed with code ", r);
return;
}
else
{
LogPrint (eLogDebug, "UPnP: Port Mapping successful. (", m_NetworkAddr ,":", strPort, " type ", strType, " -> ", m_externalIPAddress ,":", strPort ,")");
return;
}
}
void UPnP::CloseMapping (std::shared_ptr<i2p::data::RouterInfo::Address> address)
{
if(!m_upnpUrlsInitialized) {
return;
}
std::string strType (GetProto (address)), strPort (std::to_string (address->port));
int r = 0;
r = UPNP_DeletePortMapping (m_upnpUrls.controlURL, m_upnpData.first.servicetype, strPort.c_str (), strType.c_str (), 0);
LogPrint (eLogError, "UPnP: DeletePortMapping() returned : ", r);
int err = UPNPCOMMAND_SUCCESS;
err = CheckMapping (strPort.c_str (), strType.c_str ());
if (err == UPNPCOMMAND_SUCCESS)
{
err = UPNP_DeletePortMapping (m_upnpUrls.controlURL, m_upnpData.first.servicetype, strPort.c_str (), strType.c_str (), NULL);
LogPrint (eLogError, "UPnP: DeletePortMapping() returned : ", err);
}
}
void UPnP::Close ()
{
freeUPNPDevlist (m_Devlist);
m_Devlist = 0;
FreeUPNPUrls (&m_upnpUrls);
}
if(m_upnpUrlsInitialized){
FreeUPNPUrls (&m_upnpUrls);
m_upnpUrlsInitialized=false;
}
}
std::string UPnP::GetProto (std::shared_ptr<i2p::data::RouterInfo::Address> address)
{
switch (address->transportStyle)
{
case i2p::data::RouterInfo::eTransportNTCP:
return "TCP";
break;
return "TCP";
break;
case i2p::data::RouterInfo::eTransportSSU:
default:
return "UDP";
return "UDP";
}
}
}

View File

@@ -19,20 +19,31 @@ namespace i2p
{
namespace transport
{
const int UPNP_RESPONSE_TIMEOUT = 2000; // in milliseconds
enum
{
UPNP_IGD_NONE = 0,
UPNP_IGD_VALID_CONNECTED = 1,
UPNP_IGD_VALID_NOT_CONNECTED = 2,
UPNP_IGD_INVALID = 3
};
class UPnP
{
public:
public:
UPnP ();
~UPnP ();
void Close ();
void Close ();
void Start ();
void Stop ();
void Start ();
void Stop ();
private:
private:
void Discover ();
int CheckMapping (const char* port, const char* type);
void PortMapping ();
void TryPortMapping (std::shared_ptr<i2p::data::RouterInfo::Address> address);
void CloseMapping ();
@@ -41,23 +52,22 @@ namespace transport
void Run ();
std::string GetProto (std::shared_ptr<i2p::data::RouterInfo::Address> address);
private:
private:
bool m_IsRunning;
std::unique_ptr<std::thread> m_Thread;
std::unique_ptr<std::thread> m_Thread;
std::condition_variable m_Started;
std::mutex m_StartedMutex;
boost::asio::io_service m_Service;
boost::asio::deadline_timer m_Timer;
struct UPNPUrls m_upnpUrls;
struct IGDdatas m_upnpData;
bool m_upnpUrlsInitialized=false;
struct UPNPUrls m_upnpUrls;
struct IGDdatas m_upnpData;
// For miniupnpc
char * m_MulticastIf = 0;
char * m_Minissdpdpath = 0;
struct UPNPDev * m_Devlist = 0;
char m_NetworkAddr[64];
char m_externalIPAddress[40];
// For miniupnpc
struct UPNPDev * m_Devlist = 0;
char m_NetworkAddr[64];
char m_externalIPAddress[40];
};
}
}
@@ -65,14 +75,15 @@ namespace transport
#else // USE_UPNP
namespace i2p {
namespace transport {
/* class stub */
class UPnP {
public:
UPnP () {};
~UPnP () {};
void Start () { LogPrint(eLogWarning, "UPnP: this module was disabled at compile-time"); }
void Stop () {};
};
/* class stub */
class UPnP {
public:
UPnP () {};
~UPnP () {};
void Start () { LogPrint(eLogWarning, "UPnP: this module was disabled at compile-time"); }
void Stop () {};
};
}
}
#endif // USE_UPNP

32
debian/changelog vendored
View File

@@ -1,3 +1,33 @@
i2pd (2.31.0-1) unstable; urgency=medium
* updated to version 2.31.0
-- orignal <orignal@i2pmail.org> Fri, 10 Apr 2020 16:00:00 +0000
i2pd (2.30.0-1) unstable; urgency=medium
* updated to version 2.30.0/0.9.45
-- orignal <orignal@i2pmail.org> Tue, 25 Feb 2020 16:00:00 +0000
i2pd (2.29.0-1) unstable; urgency=medium
* updated to version 2.29.0/0.9.43
-- orignal <orignal@i2pmail.org> Mon, 21 Oct 2019 16:00:00 +0000
i2pd (2.28.0-1) unstable; urgency=medium
* updated to version 2.28.0/0.9.42
-- orignal <orignal@i2pmail.org> Tue, 27 Aug 2019 16:00:00 +0000
i2pd (2.27.0-1) unstable; urgency=medium
* updated to version 2.27.0/0.9.41
-- orignal <orignal@i2pmail.org> Wed, 3 Jul 2019 16:00:00 +0000
i2pd (2.26.0-1) unstable; urgency=medium
* updated to version 2.26.0
@@ -36,7 +66,7 @@ i2pd (2.21.1-1) unstable; urgency=medium
* updated to version 2.21.1
-- orignal <orignal@i2pmail.org> Thu, 22 Oct 2018 16:00:00 +0000
-- orignal <orignal@i2pmail.org> Mon, 22 Oct 2018 16:00:00 +0000
i2pd (2.21.0-1) unstable; urgency=medium

View File

@@ -5,13 +5,13 @@
# SSUSession.cpp SSUData.cpp Streaming.cpp Identity.cpp TransitTunnel.cpp \
# Transports.cpp Tunnel.cpp TunnelEndpoint.cpp TunnelPool.cpp TunnelGateway.cpp \
# Destination.cpp Base.cpp I2PEndian.cpp FS.cpp Config.cpp Family.cpp \
# Config.cpp HTTP.cpp Timestamp.cpp util.cpp api.cpp Event.cpp Gost.cpp
# Config.cpp HTTP.cpp Timestamp.cpp util.cpp api.cpp Gost.cpp
LIB_SRC = $(wildcard $(LIB_SRC_DIR)/*.cpp)
#LIB_CLIENT_SRC = \
# AddressBook.cpp BOB.cpp ClientContext.cpp I2PTunnel.cpp I2PService.cpp MatchedDestination.cpp \
# SAM.cpp SOCKS.cpp HTTPProxy.cpp I2CP.cpp WebSocks.cpp
# SAM.cpp SOCKS.cpp HTTPProxy.cpp I2CP.cpp
LIB_CLIENT_SRC = $(wildcard $(LIB_CLIENT_SRC_DIR)/*.cpp)

View File

@@ -124,8 +124,14 @@ namespace data
return publicKeyLength;
}
//----------------------------------------------------------
BlindedPublicKey::BlindedPublicKey (std::shared_ptr<const IdentityEx> identity)
const uint8_t B33_TWO_BYTES_SIGTYPE_FLAG = 0x01;
const uint8_t B33_PER_SECRET_FLAG = 0x02; // not used for now
const uint8_t B33_PER_CLIENT_AUTH_FLAG = 0x04;
BlindedPublicKey::BlindedPublicKey (std::shared_ptr<const IdentityEx> identity, bool clientAuth):
m_IsClientAuth (clientAuth)
{
if (!identity) return;
auto len = identity->GetSigningPublicKeyLen ();
@@ -135,16 +141,22 @@ namespace data
m_BlindedSigType = m_SigType;
}
BlindedPublicKey::BlindedPublicKey (const std::string& b33)
BlindedPublicKey::BlindedPublicKey (const std::string& b33):
m_SigType (0) // 0 means invalid, we can't blind DSA, set it later
{
uint8_t addr[40]; // TODO: define length from b33
size_t l = i2p::data::Base32ToByteStream (b33.c_str (), b33.length (), addr, 40);
if (l < 32)
{
LogPrint (eLogError, "Blinding: malformed b33 ", b33);
return;
}
uint32_t checksum = crc32 (0, addr + 3, l - 3);
// checksum is Little Endian
addr[0] ^= checksum; addr[1] ^= (checksum >> 8); addr[2] ^= (checksum >> 16);
uint8_t flag = addr[0];
uint8_t flags = addr[0];
size_t offset = 1;
if (flag & 0x01) // two bytes signatures
if (flags & B33_TWO_BYTES_SIGTYPE_FLAG) // two bytes signatures
{
m_SigType = bufbe16toh (addr + offset); offset += 2;
m_BlindedSigType = bufbe16toh (addr + offset); offset += 2;
@@ -154,6 +166,8 @@ namespace data
m_SigType = addr[offset]; offset++;
m_BlindedSigType = addr[offset]; offset++;
}
m_IsClientAuth = flags & B33_PER_CLIENT_AUTH_FLAG;
std::unique_ptr<i2p::crypto::Verifier> blindedVerifier (i2p::data::IdentityEx::CreateVerifier (m_SigType));
if (blindedVerifier)
{
@@ -174,7 +188,9 @@ namespace data
{
if (m_PublicKey.size () > 32) return ""; // assume 25519
uint8_t addr[35]; char str[60]; // TODO: define actual length
addr[0] = 0; // flags
uint8_t flags = 0;
if (m_IsClientAuth) flags |= B33_PER_CLIENT_AUTH_FLAG;
addr[0] = flags; // flags
addr[1] = m_SigType; // sig type
addr[2] = m_BlindedSigType; // blinded sig type
memcpy (addr + 3, m_PublicKey.data (), m_PublicKey.size ());

View File

@@ -14,7 +14,7 @@ namespace data
{
public:
BlindedPublicKey (std::shared_ptr<const IdentityEx> identity);
BlindedPublicKey (std::shared_ptr<const IdentityEx> identity, bool clientAuth = false);
BlindedPublicKey (const std::string& b33); // from b33 without .b32.i2p
std::string ToB33 () const;
@@ -22,6 +22,7 @@ namespace data
size_t GetPublicKeyLen () const { return m_PublicKey.size (); };
SigningKeyType GetSigType () const { return m_SigType; };
SigningKeyType GetBlindedSigType () const { return m_BlindedSigType; };
bool IsValid () const { return GetSigType (); }; // signature type 0 means invalid
void GetSubcredential (const uint8_t * blinded, size_t len, uint8_t * subcredential) const; // 32 bytes
size_t GetBlindedKey (const char * date, uint8_t * blindedKey) const; // date is 8 chars "YYYYMMDD", return public key length
@@ -38,6 +39,7 @@ namespace data
std::vector<uint8_t> m_PublicKey;
i2p::data::SigningKeyType m_SigType, m_BlindedSigType;
bool m_IsClientAuth = false;
};
}
}

View File

@@ -60,7 +60,7 @@ namespace config {
("share", value<int>()->default_value(100), "Limit of transit traffic from max bandwidth in percents. (default: 100)")
("ntcp", value<bool>()->default_value(false), "Enable NTCP transport (default: disabled)")
("ssu", value<bool>()->default_value(true), "Enable SSU transport (default: enabled)")
("ntcpproxy", value<std::string>()->default_value(""), "Proxy URL for NTCP transport")
("ntcpproxy", value<std::string>()->default_value(""), "Proxy URL for NTCP transport")
#ifdef _WIN32
("svcctl", value<std::string>()->default_value(""), "Windows service management ('install' or 'remove')")
("insomnia", bool_switch()->default_value(false), "Prevent system from sleeping (default: disabled)")
@@ -131,6 +131,7 @@ namespace config {
("sam.enabled", value<bool>()->default_value(true), "Enable or disable SAM Application bridge")
("sam.address", value<std::string>()->default_value("127.0.0.1"), "SAM listen address")
("sam.port", value<uint16_t>()->default_value(7656), "SAM listen port")
("sam.singlethread", value<bool>()->default_value(true), "Sessions run in the SAM bridge's thread")
;
options_description bob("BOB options");
@@ -190,10 +191,10 @@ namespace config {
"https://reseed.i2p-projekt.de/,"
"https://i2p.mooo.com/netDb/,"
"https://netdb.i2p2.no/,"
"https://reseed.i2p2.no/,"
"https://reseed2.i2p2.no/,"
// "https://us.reseed.i2p2.no:444/," // mamoth's shit
// "https://uk.reseed.i2p2.no:444/," // mamoth's shit
"https://reseed.i2p.net.in/,"
"https://download.xxlspeed.com/,"
"https://reseed-fr.i2pd.xyz/,"
"https://reseed.memcpy.io/,"
"https://reseed.onion.im/,"
@@ -217,13 +218,6 @@ namespace config {
("trust.hidden", value<bool>()->default_value(false), "Should we hide our router from other routers?")
;
options_description websocket("Websocket Options");
websocket.add_options()
("websockets.enabled", value<bool>()->default_value(false), "Enable websocket server")
("websockets.address", value<std::string>()->default_value("127.0.0.1"), "Address to bind websocket server on")
("websockets.port", value<uint16_t>()->default_value(7666), "Port to bind websocket server on")
;
options_description exploratory("Exploratory Options");
exploratory.add_options()
("exploratory.inbound.length", value<int>()->default_value(2), "Exploratory inbound tunnel length")
@@ -238,6 +232,7 @@ namespace config {
("ntcp2.published", value<bool>()->default_value(true), "Publish NTCP2 (default: enabled)")
("ntcp2.port", value<uint16_t>()->default_value(0), "Port to listen for incoming NTCP2 connections (default: auto)")
("ntcp2.addressv6", value<std::string>()->default_value("::"), "Address to bind NTCP2 on")
("ntcp2.proxy", value<std::string>()->default_value(""), "Proxy URL for NTCP2 transport")
;
options_description nettime("Time sync options");
@@ -273,7 +268,6 @@ namespace config {
.add(reseed)
.add(addressbook)
.add(trust)
.add(websocket)
.add(exploratory)
.add(ntcp2)
.add(nettime)
@@ -304,16 +298,16 @@ namespace config {
std::cout << "i2pd version " << I2PD_VERSION << " (" << I2P_VERSION << ")" << std::endl;
std::cout << m_OptionsDesc;
exit(EXIT_SUCCESS);
}
}
else if (m_Options.count("version"))
{
std::cout << "i2pd version " << I2PD_VERSION << " (" << I2P_VERSION << ")" << std::endl;
std::cout << "Boost version "
std::cout << "Boost version "
<< BOOST_VERSION / 100000 << "." // maj. version
<< BOOST_VERSION / 100 % 1000 << "." // min. version
<< BOOST_VERSION % 100 // patch version
<< std::endl;
#if defined(OPENSSL_VERSION_TEXT)
#if defined(OPENSSL_VERSION_TEXT)
std::cout << OPENSSL_VERSION_TEXT << std::endl;
#endif
#if defined(LIBRESSL_VERSION_TEXT)

View File

@@ -296,11 +296,20 @@ namespace crypto
#if OPENSSL_X25519
m_Pkey = EVP_PKEY_new_raw_private_key (EVP_PKEY_X25519, NULL, priv, 32);
m_Ctx = EVP_PKEY_CTX_new (m_Pkey, NULL);
memcpy (m_PublicKey, pub, 32); // TODO: verify against m_Pkey
if (pub)
memcpy (m_PublicKey, pub, 32); // TODO: verify against m_Pkey
else
{
size_t len = 32;
EVP_PKEY_get_raw_public_key (m_Pkey, m_PublicKey, &len);
}
#else
m_Ctx = BN_CTX_new ();
memcpy (m_PrivateKey, priv, 32);
memcpy (m_PublicKey, pub, 32);
m_Ctx = BN_CTX_new ();
if (pub)
memcpy (m_PublicKey, pub, 32);
else
GetEd25519 ()->ScalarMulB (m_PrivateKey, m_PublicKey, m_Ctx);
#endif
}
@@ -358,6 +367,18 @@ namespace crypto
#endif
}
void X25519Keys::SetPrivateKey (const uint8_t * priv)
{
#if OPENSSL_X25519
if (m_Ctx) EVP_PKEY_CTX_free (m_Ctx);
if (m_Pkey) EVP_PKEY_free (m_Pkey);
m_Pkey = EVP_PKEY_new_raw_private_key (EVP_PKEY_X25519, NULL, priv, 32);
m_Ctx = EVP_PKEY_CTX_new (m_Pkey, NULL);
#else
memcpy (m_PrivateKey, priv, 32);
#endif
}
// ElGamal
void ElGamalEncrypt (const uint8_t * key, const uint8_t * data, uint8_t * encrypted, BN_CTX * ctx, bool zeroPadding)
{
@@ -1250,18 +1271,29 @@ namespace crypto
#endif
}
void HKDF (const uint8_t * salt, const uint8_t * key, size_t keyLen, const std::string& info, uint8_t * out)
void HKDF (const uint8_t * salt, const uint8_t * key, size_t keyLen, const std::string& info,
uint8_t * out, size_t outLen)
{
#if OPENSSL_HKDF
EVP_PKEY_CTX * pctx = EVP_PKEY_CTX_new_id (EVP_PKEY_HKDF, NULL);
EVP_PKEY_CTX * pctx = EVP_PKEY_CTX_new_id (EVP_PKEY_HKDF, nullptr);
EVP_PKEY_derive_init (pctx);
EVP_PKEY_CTX_set_hkdf_md (pctx, EVP_sha256());
EVP_PKEY_CTX_set1_hkdf_salt (pctx, salt, 32);
EVP_PKEY_CTX_set1_hkdf_key (pctx, key, keyLen);
if (key && keyLen)
{
EVP_PKEY_CTX_set1_hkdf_salt (pctx, salt, 32);
EVP_PKEY_CTX_set1_hkdf_key (pctx, key, keyLen);
}
else
{
// zerolen
EVP_PKEY_CTX_hkdf_mode (pctx, EVP_PKEY_HKDEF_MODE_EXPAND_ONLY);
uint8_t tempKey[32]; unsigned int len;
HMAC(EVP_sha256(), salt, 32, nullptr, 0, tempKey, &len);
EVP_PKEY_CTX_set1_hkdf_key (pctx, tempKey, len);
}
if (info.length () > 0)
EVP_PKEY_CTX_add1_hkdf_info (pctx, info.c_str (), info.length ());
size_t outlen = 64;
EVP_PKEY_derive (pctx, out, &outlen);
EVP_PKEY_derive (pctx, out, &outLen);
EVP_PKEY_CTX_free (pctx);
#else
uint8_t prk[32]; unsigned int len;
@@ -1269,8 +1301,11 @@ namespace crypto
auto l = info.length ();
memcpy (out, info.c_str (), l); out[l] = 0x01;
HMAC(EVP_sha256(), prk, 32, out, l + 1, out, &len);
memcpy (out + 32, info.c_str (), l); out[l + 32] = 0x02;
HMAC(EVP_sha256(), prk, 32, out, l + 33, out + 32, &len);
if (outLen > 32) // 64
{
memcpy (out + 32, info.c_str (), l); out[l + 32] = 0x02;
HMAC(EVP_sha256(), prk, 32, out, l + 33, out + 32, &len);
}
#endif
}

View File

@@ -27,8 +27,8 @@
# define X509_getm_notAfter X509_get_notAfter
#else
# define LEGACY_OPENSSL 0
# define OPENSSL_HKDF 1
# if (OPENSSL_VERSION_NUMBER >= 0x010101000) // 1.1.1
# define OPENSSL_HKDF 1
# define OPENSSL_EDDSA 1
# define OPENSSL_X25519 1
# define OPENSSL_SIPHASH 1
@@ -74,12 +74,13 @@ namespace crypto
public:
X25519Keys ();
X25519Keys (const uint8_t * priv, const uint8_t * pub); // for RouterContext
X25519Keys (const uint8_t * priv, const uint8_t * pub); // if pub is null, derive from priv
~X25519Keys ();
void GenerateKeys ();
const uint8_t * GetPublicKey () const { return m_PublicKey; };
void GetPrivateKey (uint8_t * priv) const;
void SetPrivateKey (const uint8_t * priv); // wihout calculating public
void Agree (const uint8_t * pub, uint8_t * shared);
private:
@@ -296,7 +297,7 @@ namespace crypto
// HKDF
void HKDF (const uint8_t * salt, const uint8_t * key, size_t keyLen, const std::string& info, uint8_t * out); // salt - 32, out - 64, info <= 32
void HKDF (const uint8_t * salt, const uint8_t * key, size_t keyLen, const std::string& info, uint8_t * out, size_t outLen = 64); // salt - 32, out - 32 or 64, info <= 32
// init and terminate
void InitCrypto (bool precomputation);

View File

@@ -14,6 +14,7 @@ namespace crypto
void ElGamalEncryptor::Encrypt (const uint8_t * data, uint8_t * encrypted, BN_CTX * ctx, bool zeroPadding)
{
if (!ctx) return;
ElGamalEncrypt (m_PublicKey, data, encrypted, ctx, zeroPadding);
}
@@ -24,6 +25,7 @@ namespace crypto
bool ElGamalDecryptor::Decrypt (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx, bool zeroPadding)
{
if (!ctx) return false;
return ElGamalDecrypt (m_PrivateKey, encrypted, data, ctx, zeroPadding);
}
@@ -146,6 +148,34 @@ namespace crypto
BN_free (x); BN_free (y);
}
ECIESX25519AEADRatchetEncryptor::ECIESX25519AEADRatchetEncryptor (const uint8_t * pub)
{
memcpy (m_PublicKey, pub, 32);
}
void ECIESX25519AEADRatchetEncryptor::Encrypt (const uint8_t *, uint8_t * pub, BN_CTX *, bool)
{
memcpy (pub, m_PublicKey, 32);
}
ECIESX25519AEADRatchetDecryptor::ECIESX25519AEADRatchetDecryptor (const uint8_t * priv)
{
m_StaticKeys.SetPrivateKey (priv);
}
bool ECIESX25519AEADRatchetDecryptor::Decrypt (const uint8_t * epub, uint8_t * sharedSecret, BN_CTX * ctx, bool zeroPadding)
{
m_StaticKeys.Agree (epub, sharedSecret);
return true;
}
void CreateECIESX25519AEADRatchetRandomKeys (uint8_t * priv, uint8_t * pub)
{
X25519Keys k;
k.GenerateKeys ();
k.GetPrivateKey (priv);
memcpy (pub, k.GetPublicKey (), 32);
}
}
}

View File

@@ -116,6 +116,39 @@ namespace crypto
};
void CreateECIESGOSTR3410RandomKeys (uint8_t * priv, uint8_t * pub);
// ECIES-X25519-AEAD-Ratchet
class ECIESX25519AEADRatchetEncryptor: public CryptoKeyEncryptor
{
public:
ECIESX25519AEADRatchetEncryptor (const uint8_t * pub);
~ECIESX25519AEADRatchetEncryptor () {};
void Encrypt (const uint8_t *, uint8_t * pub, BN_CTX *, bool);
// copies m_PublicKey to pub
private:
uint8_t m_PublicKey[32];
};
class ECIESX25519AEADRatchetDecryptor: public CryptoKeyDecryptor
{
public:
ECIESX25519AEADRatchetDecryptor (const uint8_t * priv);
~ECIESX25519AEADRatchetDecryptor () {};
bool Decrypt (const uint8_t * epub, uint8_t * sharedSecret, BN_CTX * ctx, bool zeroPadding);
// agree with static and return in sharedSecret (32 bytes)
size_t GetPublicKeyLen () const { return 32; };
private:
X25519Keys m_StaticKeys;
};
void CreateECIESX25519AEADRatchetRandomKeys (uint8_t * priv, uint8_t * pub);
}
}

View File

@@ -12,10 +12,8 @@ namespace i2p
namespace datagram
{
DatagramDestination::DatagramDestination (std::shared_ptr<i2p::client::ClientDestination> owner):
m_Owner (owner.get()),
m_Receiver (nullptr)
m_Owner (owner), m_Receiver (nullptr), m_RawReceiver (nullptr)
{
m_Identity.FromBase64 (owner->GetIdentity()->ToBase64());
}
DatagramDestination::~DatagramDestination ()
@@ -28,14 +26,15 @@ namespace datagram
auto owner = m_Owner;
std::vector<uint8_t> v(MAX_DATAGRAM_SIZE);
uint8_t * buf = v.data();
auto identityLen = m_Identity.ToBuffer (buf, MAX_DATAGRAM_SIZE);
auto localIdentity = m_Owner->GetIdentity ();
auto identityLen = localIdentity->ToBuffer (buf, MAX_DATAGRAM_SIZE);
uint8_t * signature = buf + identityLen;
auto signatureLen = m_Identity.GetSignatureLen ();
auto signatureLen = localIdentity->GetSignatureLen ();
uint8_t * buf1 = signature + signatureLen;
size_t headerLen = identityLen + signatureLen;
memcpy (buf1, payload, len);
if (m_Identity.GetSigningKeyType () == i2p::data::SIGNING_KEY_TYPE_DSA_SHA1)
if (localIdentity->GetSigningKeyType () == i2p::data::SIGNING_KEY_TYPE_DSA_SHA1)
{
uint8_t hash[32];
SHA256(buf1, len, hash);
@@ -49,7 +48,13 @@ namespace datagram
session->SendMsg(msg);
}
void DatagramDestination::SendRawDatagramTo(const uint8_t * payload, size_t len, const i2p::data::IdentHash & identity, uint16_t fromPort, uint16_t toPort)
{
auto msg = CreateDataMessage (payload, len, fromPort, toPort, true); // raw
auto session = ObtainSession(identity);
session->SendMsg(msg);
}
void DatagramDestination::HandleDatagram (uint16_t fromPort, uint16_t toPort,uint8_t * const &buf, size_t len)
{
i2p::data::IdentityEx identity;
@@ -82,6 +87,14 @@ namespace datagram
LogPrint (eLogWarning, "Datagram signature verification failed");
}
void DatagramDestination::HandleRawDatagram (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len)
{
if (m_RawReceiver)
m_RawReceiver (fromPort, toPort, buf, len);
else
LogPrint (eLogWarning, "DatagramDestination: no receiver for raw datagram");
}
DatagramDestination::Receiver DatagramDestination::FindReceiver(uint16_t port)
{
std::lock_guard<std::mutex> lock(m_ReceiversMutex);
@@ -92,18 +105,24 @@ namespace datagram
return r;
}
void DatagramDestination::HandleDataMessagePayload (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len)
void DatagramDestination::HandleDataMessagePayload (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len, bool isRaw)
{
// unzip it
uint8_t uncompressed[MAX_DATAGRAM_SIZE];
size_t uncompressedLen = m_Inflator.Inflate (buf, len, uncompressed, MAX_DATAGRAM_SIZE);
if (uncompressedLen)
HandleDatagram (fromPort, toPort, uncompressed, uncompressedLen);
{
if (isRaw)
HandleRawDatagram (fromPort, toPort, uncompressed, uncompressedLen);
else
HandleDatagram (fromPort, toPort, uncompressed, uncompressedLen);
}
else
LogPrint (eLogWarning, "Datagram: decompression failed");
}
std::shared_ptr<I2NPMessage> DatagramDestination::CreateDataMessage (const uint8_t * payload, size_t len, uint16_t fromPort, uint16_t toPort)
std::shared_ptr<I2NPMessage> DatagramDestination::CreateDataMessage (const uint8_t * payload, size_t len, uint16_t fromPort, uint16_t toPort, bool isRaw)
{
auto msg = NewI2NPMessage ();
uint8_t * buf = msg->GetPayload ();
@@ -114,7 +133,7 @@ namespace datagram
htobe32buf (msg->GetPayload (), size); // length
htobe16buf (buf + 4, fromPort); // source port
htobe16buf (buf + 6, toPort); // destination port
buf[9] = i2p::client::PROTOCOL_TYPE_DATAGRAM; // datagram protocol
buf[9] = isRaw ? i2p::client::PROTOCOL_TYPE_RAW : i2p::client::PROTOCOL_TYPE_DATAGRAM; // raw or datagram protocol
msg->len += size + 4;
msg->FillI2NPMessageHeader (eI2NPData);
}
@@ -170,7 +189,7 @@ namespace datagram
return nullptr;
}
DatagramSession::DatagramSession(i2p::client::ClientDestination * localDestination,
DatagramSession::DatagramSession(std::shared_ptr<i2p::client::ClientDestination> localDestination,
const i2p::data::IdentHash & remoteIdent) :
m_LocalDestination(localDestination),
m_RemoteIdent(remoteIdent),

View File

@@ -37,7 +37,7 @@ namespace datagram
class DatagramSession : public std::enable_shared_from_this<DatagramSession>
{
public:
DatagramSession(i2p::client::ClientDestination * localDestination, const i2p::data::IdentHash & remoteIdent);
DatagramSession(std::shared_ptr<i2p::client::ClientDestination> localDestination, const i2p::data::IdentHash & remoteIdent);
void Start ();
void Stop ();
@@ -81,7 +81,7 @@ namespace datagram
void HandleLeaseSetUpdated(std::shared_ptr<i2p::data::LeaseSet> ls);
private:
i2p::client::ClientDestination * m_LocalDestination;
std::shared_ptr<i2p::client::ClientDestination> m_LocalDestination;
i2p::data::IdentHash m_RemoteIdent;
std::shared_ptr<const i2p::data::LeaseSet> m_RemoteLeaseSet;
std::shared_ptr<i2p::garlic::GarlicRoutingSession> m_RoutingSession;
@@ -99,22 +99,28 @@ namespace datagram
class DatagramDestination
{
typedef std::function<void (const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len)> Receiver;
typedef std::function<void (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len)> RawReceiver;
public:
DatagramDestination (std::shared_ptr<i2p::client::ClientDestination> owner);
~DatagramDestination ();
void SendDatagramTo (const uint8_t * payload, size_t len, const i2p::data::IdentHash & ident, uint16_t fromPort = 0, uint16_t toPort = 0);
void HandleDataMessagePayload (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len);
void SendDatagramTo (const uint8_t * payload, size_t len, const i2p::data::IdentHash & ident, uint16_t fromPort = 0, uint16_t toPort = 0);
void SendRawDatagramTo (const uint8_t * payload, size_t len, const i2p::data::IdentHash & ident, uint16_t fromPort = 0, uint16_t toPort = 0);
void HandleDataMessagePayload (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len, bool isRaw = false);
void SetReceiver (const Receiver& receiver) { m_Receiver = receiver; };
void ResetReceiver () { m_Receiver = nullptr; };
void SetReceiver (const Receiver& receiver, uint16_t port) { std::lock_guard<std::mutex> lock(m_ReceiversMutex); m_ReceiversByPorts[port] = receiver; };
void ResetReceiver (uint16_t port) { std::lock_guard<std::mutex> lock(m_ReceiversMutex); m_ReceiversByPorts.erase (port); };
void SetRawReceiver (const RawReceiver& receiver) { m_RawReceiver = receiver; };
void ResetRawReceiver () { m_RawReceiver = nullptr; };
std::shared_ptr<DatagramSession::Info> GetInfoForRemote(const i2p::data::IdentHash & remote);
// clean up stale sessions
@@ -124,17 +130,19 @@ namespace datagram
std::shared_ptr<DatagramSession> ObtainSession(const i2p::data::IdentHash & ident);
std::shared_ptr<I2NPMessage> CreateDataMessage (const uint8_t * payload, size_t len, uint16_t fromPort, uint16_t toPort);
std::shared_ptr<I2NPMessage> CreateDataMessage (const uint8_t * payload, size_t len, uint16_t fromPort, uint16_t toPort, bool isRaw = false);
void HandleDatagram (uint16_t fromPort, uint16_t toPort, uint8_t *const& buf, size_t len);
void HandleRawDatagram (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len);
/** find a receiver by port, if none by port is found try default receiever, otherwise returns nullptr */
Receiver FindReceiver(uint16_t port);
private:
i2p::client::ClientDestination * m_Owner;
i2p::data::IdentityEx m_Identity;
std::shared_ptr<i2p::client::ClientDestination> m_Owner;
Receiver m_Receiver; // default
RawReceiver m_RawReceiver; // default
std::mutex m_SessionsMutex;
std::map<i2p::data::IdentHash, DatagramSession_ptr > m_Sessions;
std::mutex m_ReceiversMutex;

View File

@@ -7,17 +7,17 @@
#include "Timestamp.h"
#include "NetDb.hpp"
#include "Destination.h"
#include "util.h"
namespace i2p
{
namespace client
{
LeaseSetDestination::LeaseSetDestination (bool isPublic, const std::map<std::string, std::string> * params):
m_IsRunning (false), m_Thread (nullptr), m_IsPublic (isPublic),
m_PublishReplyToken (0), m_LastSubmissionTime (0), m_PublishConfirmationTimer (m_Service),
LeaseSetDestination::LeaseSetDestination (boost::asio::io_service& service,
bool isPublic, const std::map<std::string, std::string> * params):
m_Service (service), m_IsPublic (isPublic), m_PublishReplyToken (0),
m_LastSubmissionTime (0), m_PublishConfirmationTimer (m_Service),
m_PublishVerificationTimer (m_Service), m_PublishDelayTimer (m_Service), m_CleanupTimer (m_Service),
m_LeaseSetType (DEFAULT_LEASESET_TYPE)
m_LeaseSetType (DEFAULT_LEASESET_TYPE), m_AuthType (i2p::data::ENCRYPTED_LEASESET_AUTH_TYPE_NONE)
{
int inLen = DEFAULT_INBOUND_TUNNEL_LENGTH;
int inQty = DEFAULT_INBOUND_TUNNELS_QUANTITY;
@@ -70,6 +70,29 @@ namespace client
it = params->find (I2CP_PARAM_LEASESET_TYPE);
if (it != params->end ())
m_LeaseSetType = std::stoi(it->second);
if (m_LeaseSetType == i2p::data::NETDB_STORE_TYPE_ENCRYPTED_LEASESET2)
{
// authentication for encrypted LeaseSet
it = params->find (I2CP_PARAM_LEASESET_AUTH_TYPE);
if (it != params->end ())
{
auto authType = std::stoi (it->second);
if (authType >= i2p::data::ENCRYPTED_LEASESET_AUTH_TYPE_NONE && authType <= i2p::data::ENCRYPTED_LEASESET_AUTH_TYPE_PSK)
m_AuthType = authType;
else
LogPrint (eLogError, "Destination: Unknown auth type ", authType);
}
}
it = params->find (I2CP_PARAM_LEASESET_PRIV_KEY);
if (it != params->end ())
{
m_LeaseSetPrivKey.reset (new i2p::data::Tag<32>());
if (m_LeaseSetPrivKey->FromBase64 (it->second) != 32)
{
LogPrint(eLogError, "Destination: invalid value i2cp.leaseSetPrivKey ", it->second);
m_LeaseSetPrivKey.reset (nullptr);
}
}
}
}
catch (std::exception & ex)
@@ -100,77 +123,36 @@ namespace client
LeaseSetDestination::~LeaseSetDestination ()
{
if (m_IsRunning)
Stop ();
if (m_Pool)
i2p::tunnel::tunnels.DeleteTunnelPool (m_Pool);
for (auto& it: m_LeaseSetRequests)
it.second->Complete (nullptr);
}
void LeaseSetDestination::Run ()
void LeaseSetDestination::Start ()
{
while (m_IsRunning)
{
try
{
m_Service.run ();
}
catch (std::exception& ex)
{
LogPrint (eLogError, "Destination: runtime exception: ", ex.what ());
}
}
if (m_Nickname.empty ())
m_Nickname = i2p::data::GetIdentHashAbbreviation (GetIdentHash ()); // set default nickname
LoadTags ();
m_Pool->SetLocalDestination (shared_from_this ());
m_Pool->SetActive (true);
m_CleanupTimer.expires_from_now (boost::posix_time::minutes (DESTINATION_CLEANUP_TIMEOUT));
m_CleanupTimer.async_wait (std::bind (&LeaseSetDestination::HandleCleanupTimer,
shared_from_this (), std::placeholders::_1));
}
bool LeaseSetDestination::Start ()
void LeaseSetDestination::Stop ()
{
if (!m_IsRunning)
m_CleanupTimer.cancel ();
m_PublishConfirmationTimer.cancel ();
m_PublishVerificationTimer.cancel ();
if (m_Pool)
{
if (m_Nickname.empty ())
m_Nickname = i2p::data::GetIdentHashAbbreviation (GetIdentHash ()); // set default nickname
LoadTags ();
m_IsRunning = true;
m_Pool->SetLocalDestination (shared_from_this ());
m_Pool->SetActive (true);
m_CleanupTimer.expires_from_now (boost::posix_time::minutes (DESTINATION_CLEANUP_TIMEOUT));
m_CleanupTimer.async_wait (std::bind (&LeaseSetDestination::HandleCleanupTimer,
shared_from_this (), std::placeholders::_1));
m_Thread = new std::thread (std::bind (&LeaseSetDestination::Run, shared_from_this ()));
return true;
m_Pool->SetLocalDestination (nullptr);
i2p::tunnel::tunnels.StopTunnelPool (m_Pool);
}
else
return false;
}
bool LeaseSetDestination::Stop ()
{
if (m_IsRunning)
{
m_CleanupTimer.cancel ();
m_PublishConfirmationTimer.cancel ();
m_PublishVerificationTimer.cancel ();
m_IsRunning = false;
if (m_Pool)
{
m_Pool->SetLocalDestination (nullptr);
i2p::tunnel::tunnels.StopTunnelPool (m_Pool);
}
m_Service.stop ();
if (m_Thread)
{
m_Thread->join ();
delete m_Thread;
m_Thread = 0;
}
SaveTags ();
CleanUp (); // GarlicDestination
return true;
}
else
return false;
SaveTags ();
CleanUp (); // GarlicDestination
}
bool LeaseSetDestination::Reconfigure(std::map<std::string, std::string> params)
@@ -330,30 +312,38 @@ namespace client
void LeaseSetDestination::ProcessDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg)
{
m_Service.post (std::bind (&LeaseSetDestination::HandleDeliveryStatusMessage, shared_from_this (), msg));
uint32_t msgID = bufbe32toh (msg->GetPayload () + DELIVERY_STATUS_MSGID_OFFSET);
m_Service.post (std::bind (&LeaseSetDestination::HandleDeliveryStatusMessage, shared_from_this (), msgID));
}
void LeaseSetDestination::HandleI2NPMessage (const uint8_t * buf, size_t len, std::shared_ptr<i2p::tunnel::InboundTunnel> from)
void LeaseSetDestination::HandleI2NPMessage (const uint8_t * buf, size_t len)
{
I2NPMessageType typeID = (I2NPMessageType)(buf[I2NP_HEADER_TYPEID_OFFSET]);
LeaseSetDestination::HandleCloveI2NPMessage (typeID, buf + I2NP_HEADER_SIZE, GetI2NPMessageLength(buf, len) - I2NP_HEADER_SIZE);
}
bool LeaseSetDestination::HandleCloveI2NPMessage (I2NPMessageType typeID, const uint8_t * payload, size_t len)
{
uint8_t typeID = buf[I2NP_HEADER_TYPEID_OFFSET];
switch (typeID)
{
case eI2NPData:
HandleDataMessage (buf + I2NP_HEADER_SIZE, GetI2NPMessageLength(buf, len) - I2NP_HEADER_SIZE);
HandleDataMessage (payload, len);
break;
case eI2NPDeliveryStatus:
// we assume tunnel tests non-encrypted
HandleDeliveryStatusMessage (CreateI2NPMessage (buf, GetI2NPMessageLength (buf, len), from));
HandleDeliveryStatusMessage (bufbe32toh (payload + DELIVERY_STATUS_MSGID_OFFSET));
break;
case eI2NPDatabaseStore:
HandleDatabaseStoreMessage (buf + I2NP_HEADER_SIZE, GetI2NPMessageLength(buf, len) - I2NP_HEADER_SIZE);
HandleDatabaseStoreMessage (payload, len);
break;
case eI2NPDatabaseSearchReply:
HandleDatabaseSearchReplyMessage (buf + I2NP_HEADER_SIZE, GetI2NPMessageLength(buf, len) - I2NP_HEADER_SIZE);
HandleDatabaseSearchReplyMessage (payload, len);
break;
default:
i2p::HandleI2NPMessage (CreateI2NPMessage (buf, GetI2NPMessageLength (buf, len), from));
LogPrint (eLogWarning, "Destination: Unexpected I2NP message type ", typeID);
return false;
}
return true;
}
void LeaseSetDestination::HandleDatabaseStoreMessage (const uint8_t * buf, size_t len)
@@ -398,7 +388,7 @@ namespace client
if (buf[DATABASE_STORE_TYPE_OFFSET] == i2p::data::NETDB_STORE_TYPE_LEASESET)
leaseSet = std::make_shared<i2p::data::LeaseSet> (buf + offset, len - offset); // LeaseSet
else
leaseSet = std::make_shared<i2p::data::LeaseSet2> (buf[DATABASE_STORE_TYPE_OFFSET], buf + offset, len - offset); // LeaseSet2
leaseSet = std::make_shared<i2p::data::LeaseSet2> (buf[DATABASE_STORE_TYPE_OFFSET], buf + offset, len - offset, true, GetPreferredCryptoType () ); // LeaseSet2
if (leaseSet->IsValid () && leaseSet->GetIdentHash () == key)
{
if (leaseSet->GetIdentHash () != GetIdentHash ())
@@ -422,7 +412,7 @@ namespace client
auto it2 = m_LeaseSetRequests.find (key);
if (it2 != m_LeaseSetRequests.end () && it2->second->requestedBlindedKey)
{
auto ls2 = std::make_shared<i2p::data::LeaseSet2> (buf + offset, len - offset, it2->second->requestedBlindedKey);
auto ls2 = std::make_shared<i2p::data::LeaseSet2> (buf + offset, len - offset, it2->second->requestedBlindedKey, m_LeaseSetPrivKey ? *m_LeaseSetPrivKey : nullptr, GetPreferredCryptoType ());
if (ls2->IsValid ())
{
m_RemoteLeaseSets[ls2->GetIdentHash ()] = ls2; // ident is not key
@@ -488,9 +478,8 @@ namespace client
LogPrint (eLogWarning, "Destination: Request for ", key.ToBase64 (), " not found");
}
void LeaseSetDestination::HandleDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg)
void LeaseSetDestination::HandleDeliveryStatusMessage (uint32_t msgID)
{
uint32_t msgID = bufbe32toh (msg->GetPayload () + DELIVERY_STATUS_MSGID_OFFSET);
if (msgID == m_PublishReplyToken)
{
LogPrint (eLogDebug, "Destination: Publishing LeaseSet confirmed for ", GetIdentHash().ToBase32());
@@ -502,7 +491,7 @@ namespace client
shared_from_this (), std::placeholders::_1));
}
else
i2p::garlic::GarlicDestination::HandleDeliveryStatusMessage (msg);
i2p::garlic::GarlicDestination::HandleDeliveryStatusMessage (msgID);
}
void LeaseSetDestination::SetLeaseSetUpdated ()
@@ -833,10 +822,19 @@ namespace client
}
}
ClientDestination::ClientDestination (const i2p::data::PrivateKeys& keys, bool isPublic, const std::map<std::string, std::string> * params):
LeaseSetDestination (isPublic, params), m_Keys (keys), m_StreamingAckDelay (DEFAULT_INITIAL_ACK_DELAY),
i2p::data::CryptoKeyType LeaseSetDestination::GetPreferredCryptoType () const
{
if (SupportsEncryptionType (i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RARCHET))
return i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RARCHET;
return i2p::data::CRYPTO_KEY_TYPE_ELGAMAL;
}
ClientDestination::ClientDestination (boost::asio::io_service& service, const i2p::data::PrivateKeys& keys,
bool isPublic, const std::map<std::string, std::string> * params):
LeaseSetDestination (service, isPublic, params),
m_Keys (keys), m_StreamingAckDelay (DEFAULT_INITIAL_ACK_DELAY),
m_DatagramDestination (nullptr), m_RefCounter (0),
m_ReadyChecker(GetService())
m_ReadyChecker(service)
{
if (keys.IsOfflineSignature () && GetLeaseSetType () == i2p::data::NETDB_STORE_TYPE_LEASESET)
SetLeaseSetType (i2p::data::NETDB_STORE_TYPE_STANDARD_LEASESET2); // offline keys can be published with LS2 only
@@ -849,21 +847,54 @@ namespace client
if (it != params->end ())
m_EncryptionKeyType = std::stoi(it->second);
}
if (isPublic && m_EncryptionKeyType == GetIdentity ()->GetCryptoKeyType ()) // TODO: presist key type
memset (m_EncryptionPrivateKey, 0, 256);
memset (m_EncryptionPublicKey, 0, 256);
if (isPublic)
PersistTemporaryKeys ();
else
i2p::data::PrivateKeys::GenerateCryptoKeyPair (m_EncryptionKeyType, m_EncryptionPrivateKey, m_EncryptionPublicKey);
m_Decryptor = i2p::data::PrivateKeys::CreateDecryptor (m_EncryptionKeyType, m_EncryptionPrivateKey);
if (isPublic)
LogPrint (eLogInfo, "Destination: Local address ", GetIdentHash().ToBase32 (), " created");
// extract streaming params
if (params)
try
{
if (params)
{
// extract streaming params
auto it = params->find (I2CP_PARAM_STREAMING_INITIAL_ACK_DELAY);
if (it != params->end ())
m_StreamingAckDelay = std::stoi(it->second);
if (GetLeaseSetType () == i2p::data::NETDB_STORE_TYPE_ENCRYPTED_LEASESET2)
{
// authentication for encrypted LeaseSet
auto authType = GetAuthType ();
if (authType > 0)
{
m_AuthKeys = std::make_shared<std::vector<i2p::data::AuthPublicKey> >();
if (authType == i2p::data::ENCRYPTED_LEASESET_AUTH_TYPE_DH)
ReadAuthKey (I2CP_PARAM_LEASESET_CLIENT_DH, params);
else if (authType == i2p::data::ENCRYPTED_LEASESET_AUTH_TYPE_PSK)
ReadAuthKey (I2CP_PARAM_LEASESET_CLIENT_PSK, params);
else
LogPrint (eLogError, "Destination: Unexpected auth type ", authType);
if (m_AuthKeys->size ())
LogPrint (eLogInfo, "Destination: ", m_AuthKeys->size (), " auth keys read");
else
{
LogPrint (eLogError, "Destination: No auth keys read for auth type ", authType);
m_AuthKeys = nullptr;
}
}
}
}
}
catch (std::exception & ex)
{
auto it = params->find (I2CP_PARAM_STREAMING_INITIAL_ACK_DELAY);
if (it != params->end ())
m_StreamingAckDelay = std::stoi(it->second);
LogPrint(eLogError, "Destination: unable to parse parameters for destination: ", ex.what());
}
}
@@ -871,71 +902,35 @@ namespace client
{
}
bool ClientDestination::Start ()
void ClientDestination::Start ()
{
if (LeaseSetDestination::Start ())
LeaseSetDestination::Start ();
m_StreamingDestination = std::make_shared<i2p::stream::StreamingDestination> (GetSharedFromThis ()); // TODO:
m_StreamingDestination->Start ();
for (auto& it: m_StreamingDestinationsByPorts)
it.second->Start ();
}
void ClientDestination::Stop ()
{
LeaseSetDestination::Stop ();
m_ReadyChecker.cancel();
m_StreamingDestination->Stop ();
//m_StreamingDestination->SetOwner (nullptr);
m_StreamingDestination = nullptr;
for (auto& it: m_StreamingDestinationsByPorts)
{
m_StreamingDestination = std::make_shared<i2p::stream::StreamingDestination> (GetSharedFromThis ()); // TODO:
m_StreamingDestination->Start ();
for (auto& it: m_StreamingDestinationsByPorts)
it.second->Start ();
return true;
it.second->Stop ();
//it.second->SetOwner (nullptr);
}
else
return false;
}
bool ClientDestination::Stop ()
{
if (LeaseSetDestination::Stop ())
m_StreamingDestinationsByPorts.clear ();
if (m_DatagramDestination)
{
m_ReadyChecker.cancel();
m_StreamingDestination->Stop ();
//m_StreamingDestination->SetOwner (nullptr);
m_StreamingDestination = nullptr;
for (auto& it: m_StreamingDestinationsByPorts)
{
it.second->Stop ();
//it.second->SetOwner (nullptr);
}
m_StreamingDestinationsByPorts.clear ();
if (m_DatagramDestination)
{
delete m_DatagramDestination;
m_DatagramDestination = nullptr;
}
return true;
delete m_DatagramDestination;
m_DatagramDestination = nullptr;
}
else
return false;
}
#ifdef I2LUA
void ClientDestination::Ready(ReadyPromise & p)
{
ScheduleCheckForReady(&p);
}
void ClientDestination::ScheduleCheckForReady(ReadyPromise * p)
{
// tick every 100ms
m_ReadyChecker.expires_from_now(boost::posix_time::milliseconds(100));
m_ReadyChecker.async_wait([&, p] (const boost::system::error_code & ecode) {
HandleCheckForReady(ecode, p);
});
}
void ClientDestination::HandleCheckForReady(const boost::system::error_code & ecode, ReadyPromise * p)
{
if(ecode) // error happened
p->set_value(nullptr);
else if(IsReady()) // we are ready
p->set_value(std::shared_ptr<ClientDestination>(this));
else // we are not ready
ScheduleCheckForReady(p);
}
#endif
void ClientDestination::HandleDataMessage (const uint8_t * buf, size_t len)
{
uint32_t length = bufbe32toh (buf);
@@ -967,6 +962,13 @@ namespace client
else
LogPrint (eLogError, "Destination: Missing datagram destination");
break;
case PROTOCOL_TYPE_RAW:
// raw datagram
if (m_DatagramDestination)
m_DatagramDestination->HandleDataMessagePayload (fromPort, toPort, buf, length, true);
else
LogPrint (eLogError, "Destination: Missing raw datagram destination");
break;
default:
LogPrint (eLogError, "Destination: Data: unexpected protocol ", buf[9]);
}
@@ -1105,8 +1107,8 @@ namespace client
LogPrint (eLogInfo, "Destination: Creating new temporary keys of type for address ", ident, ".b32.i2p");
memset (m_EncryptionPrivateKey, 0, 256);
memset (m_EncryptionPublicKey, 0, 256);
i2p::data::PrivateKeys::GenerateCryptoKeyPair (GetIdentity ()->GetCryptoKeyType (), m_EncryptionPrivateKey, m_EncryptionPublicKey);
i2p::data::PrivateKeys::GenerateCryptoKeyPair (m_EncryptionKeyType, m_EncryptionPrivateKey, m_EncryptionPublicKey);
// TODO:: persist crypto key type
std::ofstream f1 (path, std::ofstream::binary | std::ofstream::out);
if (f1) {
f1.write ((char *)m_EncryptionPublicKey, 256);
@@ -1128,11 +1130,13 @@ namespace client
else
{
// standard LS2 (type 3) first
auto keyLen = m_Decryptor ? m_Decryptor->GetPublicKeyLen () : 256;
uint16_t keyLen = m_Decryptor ? m_Decryptor->GetPublicKeyLen () : 256;
bool isPublishedEncrypted = GetLeaseSetType () == i2p::data::NETDB_STORE_TYPE_ENCRYPTED_LEASESET2;
auto ls2 = std::make_shared<i2p::data::LocalLeaseSet2> (i2p::data::NETDB_STORE_TYPE_STANDARD_LEASESET2,
m_Keys, m_EncryptionKeyType, keyLen, m_EncryptionPublicKey, tunnels);
if (GetLeaseSetType () == i2p::data::NETDB_STORE_TYPE_ENCRYPTED_LEASESET2) // encrypt if type 5
ls2 = std::make_shared<i2p::data::LocalEncryptedLeaseSet2> (ls2, m_Keys);
m_Keys, i2p::data::LocalLeaseSet2::KeySections { {m_EncryptionKeyType, keyLen, m_EncryptionPublicKey} },
tunnels, IsPublic (), isPublishedEncrypted);
if (isPublishedEncrypted) // encrypt if type 5
ls2 = std::make_shared<i2p::data::LocalEncryptedLeaseSet2> (ls2, m_Keys, GetAuthType (), m_AuthKeys);
leaseSet = ls2;
}
SetLeaseSet (leaseSet);
@@ -1143,7 +1147,7 @@ namespace client
if (m_DatagramDestination) m_DatagramDestination->CleanUp ();
}
bool ClientDestination::Decrypt (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx) const
bool ClientDestination::Decrypt (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx, i2p::data::CryptoKeyType preferredCrypto) const
{
if (m_Decryptor)
return m_Decryptor->Decrypt (encrypted, data, ctx, true);
@@ -1151,5 +1155,63 @@ namespace client
LogPrint (eLogError, "Destinations: decryptor is not set");
return false;
}
void ClientDestination::ReadAuthKey (const std::string& group, const std::map<std::string, std::string> * params)
{
for (auto it: *params)
if (it.first.length () >= group.length () && !it.first.compare (0, group.length (), group))
{
auto pos = it.second.find (':');
if (pos != std::string::npos)
{
i2p::data::AuthPublicKey pubKey;
if (pubKey.FromBase64 (it.second.substr (pos+1)))
m_AuthKeys->push_back (pubKey);
else
LogPrint (eLogError, "Destination: Unexpected auth key ", it.second.substr (pos+1));
}
}
}
bool ClientDestination::DeleteStream (uint32_t recvStreamID)
{
if (m_StreamingDestination->DeleteStream (recvStreamID))
return true;
for (auto it: m_StreamingDestinationsByPorts)
if (it.second->DeleteStream (recvStreamID))
return true;
return false;
}
RunnableClientDestination::RunnableClientDestination (const i2p::data::PrivateKeys& keys, bool isPublic, const std::map<std::string, std::string> * params):
RunnableService ("Destination"),
ClientDestination (GetIOService (), keys, isPublic, params)
{
}
RunnableClientDestination::~RunnableClientDestination ()
{
if (IsRunning ())
Stop ();
}
void RunnableClientDestination::Start ()
{
if (!IsRunning ())
{
ClientDestination::Start ();
StartIOService ();
}
}
void RunnableClientDestination::Stop ()
{
if (IsRunning ())
{
ClientDestination::Stop ();
StopIOService ();
}
}
}
}

View File

@@ -8,9 +8,6 @@
#include <set>
#include <string>
#include <functional>
#ifdef I2LUA
#include <future>
#endif
#include <boost/asio.hpp>
#include "Identity.h"
#include "TunnelPool.h"
@@ -20,6 +17,7 @@
#include "NetDb.hpp"
#include "Streaming.h"
#include "Datagram.h"
#include "util.h"
namespace i2p
{
@@ -55,7 +53,11 @@ namespace client
const char I2CP_PARAM_LEASESET_TYPE[] = "i2cp.leaseSetType";
const int DEFAULT_LEASESET_TYPE = 1;
const char I2CP_PARAM_LEASESET_ENCRYPTION_TYPE[] = "i2cp.leaseSetEncType";
const char I2CP_PARAM_LEASESET_PRIV_KEY[] = "i2cp.leaseSetPrivKey"; // PSK decryption key, base64
const char I2CP_PARAM_LEASESET_AUTH_TYPE[] = "i2cp.leaseSetAuthType";
const char I2CP_PARAM_LEASESET_CLIENT_DH[] = "i2cp.leaseSetClient.dh"; // group of i2cp.leaseSetClient.dh.nnn
const char I2CP_PARAM_LEASESET_CLIENT_PSK[] = "i2cp.leaseSetClient.psk"; // group of i2cp.leaseSetClient.psk.nnn
// latency
const char I2CP_PARAM_MIN_TUNNEL_LATENCY[] = "latency.min";
const int DEFAULT_MIN_TUNNEL_LATENCY = 0;
@@ -94,18 +96,17 @@ namespace client
public:
LeaseSetDestination (bool isPublic, const std::map<std::string, std::string> * params = nullptr);
LeaseSetDestination (boost::asio::io_service& service, bool isPublic, const std::map<std::string, std::string> * params = nullptr);
~LeaseSetDestination ();
const std::string& GetNickname () const { return m_Nickname; };
boost::asio::io_service& GetService () { return m_Service; };
virtual bool Start ();
virtual bool Stop ();
virtual void Start ();
virtual void Stop ();
/** i2cp reconfigure */
virtual bool Reconfigure(std::map<std::string, std::string> i2cpOpts);
bool IsRunning () const { return m_IsRunning; };
boost::asio::io_service& GetService () { return m_Service; };
std::shared_ptr<i2p::tunnel::TunnelPool> GetTunnelPool () { return m_Pool; };
bool IsReady () const { return m_LeaseSet && !m_LeaseSet->IsExpired () && m_Pool->GetOutboundTunnels ().size () > 0; };
std::shared_ptr<i2p::data::LeaseSet> FindLeaseSet (const i2p::data::IdentHash& ident);
@@ -117,7 +118,6 @@ namespace client
// implements GarlicDestination
std::shared_ptr<const i2p::data::LocalLeaseSet> GetLeaseSet ();
std::shared_ptr<i2p::tunnel::TunnelPool> GetTunnelPool () const { return m_Pool; }
void HandleI2NPMessage (const uint8_t * buf, size_t len, std::shared_ptr<i2p::tunnel::InboundTunnel> from);
// override GarlicDestination
bool SubmitSessionKey (const uint8_t * key, const uint8_t * tag);
@@ -127,9 +127,15 @@ namespace client
protected:
// implements GarlicDestination
void HandleI2NPMessage (const uint8_t * buf, size_t len);
bool HandleCloveI2NPMessage (I2NPMessageType typeID, const uint8_t * payload, size_t len);
void SetLeaseSet (std::shared_ptr<const i2p::data::LocalLeaseSet> newLeaseSet);
int GetLeaseSetType () const { return m_LeaseSetType; };
void SetLeaseSetType (int leaseSetType) { m_LeaseSetType = leaseSetType; };
int GetAuthType () const { return m_AuthType; };
bool IsPublic () const { return m_IsPublic; };
virtual void CleanupDestination () {}; // additional clean up in derived classes
// I2CP
virtual void HandleDataMessage (const uint8_t * buf, size_t len) = 0;
@@ -137,7 +143,6 @@ namespace client
private:
void Run ();
void UpdateLeaseSet ();
std::shared_ptr<const i2p::data::LocalLeaseSet> GetLeaseSetMt ();
void Publish ();
@@ -146,19 +151,18 @@ namespace client
void HandlePublishDelayTimer (const boost::system::error_code& ecode);
void HandleDatabaseStoreMessage (const uint8_t * buf, size_t len);
void HandleDatabaseSearchReplyMessage (const uint8_t * buf, size_t len);
void HandleDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg);
void HandleDeliveryStatusMessage (uint32_t msgID);
void RequestLeaseSet (const i2p::data::IdentHash& dest, RequestComplete requestComplete, std::shared_ptr<const i2p::data::BlindedPublicKey> requestedBlindedKey = nullptr);
bool SendLeaseSetRequest (const i2p::data::IdentHash& dest, std::shared_ptr<const i2p::data::RouterInfo> nextFloodfill, std::shared_ptr<LeaseSetRequest> request);
void HandleRequestTimoutTimer (const boost::system::error_code& ecode, const i2p::data::IdentHash& dest);
void HandleCleanupTimer (const boost::system::error_code& ecode);
void CleanupRemoteLeaseSets ();
i2p::data::CryptoKeyType GetPreferredCryptoType () const;
private:
volatile bool m_IsRunning;
std::thread * m_Thread;
boost::asio::io_service m_Service;
boost::asio::io_service& m_Service;
mutable std::mutex m_RemoteLeaseSetsMutex;
std::map<i2p::data::IdentHash, std::shared_ptr<i2p::data::LeaseSet> > m_RemoteLeaseSets;
std::map<i2p::data::IdentHash, std::shared_ptr<LeaseSetRequest> > m_LeaseSetRequests;
@@ -174,7 +178,8 @@ namespace client
boost::asio::deadline_timer m_PublishConfirmationTimer, m_PublishVerificationTimer,
m_PublishDelayTimer, m_CleanupTimer;
std::string m_Nickname;
int m_LeaseSetType;
int m_LeaseSetType, m_AuthType;
std::unique_ptr<i2p::data::Tag<32> > m_LeaseSetPrivKey; // non-null if presented
public:
@@ -182,24 +187,19 @@ namespace client
int GetNumRemoteLeaseSets () const { return m_RemoteLeaseSets.size (); };
const decltype(m_RemoteLeaseSets)& GetLeaseSets () const { return m_RemoteLeaseSets; };
bool IsEncryptedLeaseSet () const { return m_LeaseSetType == i2p::data::NETDB_STORE_TYPE_ENCRYPTED_LEASESET2; };
bool IsPerClientAuth () const { return m_AuthType > 0; };
};
class ClientDestination: public LeaseSetDestination
{
public:
#ifdef I2LUA
// type for informing that a client destination is ready
typedef std::promise<std::shared_ptr<ClientDestination> > ReadyPromise;
// informs promise with shared_from_this() when this destination is ready to use
// if cancelled before ready, informs promise with nullptr
void Ready(ReadyPromise & p);
#endif
ClientDestination (const i2p::data::PrivateKeys& keys, bool isPublic, const std::map<std::string, std::string> * params = nullptr);
ClientDestination (boost::asio::io_service& service, const i2p::data::PrivateKeys& keys,
bool isPublic, const std::map<std::string, std::string> * params = nullptr);
~ClientDestination ();
virtual bool Start ();
virtual bool Stop ();
void Start ();
void Stop ();
const i2p::data::PrivateKeys& GetPrivateKeys () const { return m_Keys; };
void Sign (const uint8_t * buf, int len, uint8_t * signature) const { m_Keys.Sign (buf, len, signature); };
@@ -223,12 +223,14 @@ namespace client
int GetStreamingAckDelay () const { return m_StreamingAckDelay; }
// datagram
i2p::datagram::DatagramDestination * GetDatagramDestination () const { return m_DatagramDestination; };
i2p::datagram::DatagramDestination * CreateDatagramDestination ();
i2p::datagram::DatagramDestination * GetDatagramDestination () const { return m_DatagramDestination; };
i2p::datagram::DatagramDestination * CreateDatagramDestination ();
// implements LocalDestination
bool Decrypt (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx) const;
bool Decrypt (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx, i2p::data::CryptoKeyType preferredCrypto) const;
std::shared_ptr<const i2p::data::IdentityEx> GetIdentity () const { return m_Keys.GetPublic (); };
bool SupportsEncryptionType (i2p::data::CryptoKeyType keyType) const { return m_EncryptionKeyType == keyType; };
const uint8_t * GetEncryptionPublicKey (i2p::data::CryptoKeyType keyType) const { return m_EncryptionPublicKey; };
protected:
@@ -239,13 +241,12 @@ namespace client
private:
std::shared_ptr<ClientDestination> GetSharedFromThis ()
{ return std::static_pointer_cast<ClientDestination>(shared_from_this ()); }
std::shared_ptr<ClientDestination> GetSharedFromThis () {
return std::static_pointer_cast<ClientDestination>(shared_from_this ());
}
void PersistTemporaryKeys ();
#ifdef I2LUA
void ScheduleCheckForReady(ReadyPromise * p);
void HandleCheckForReady(const boost::system::error_code & ecode, ReadyPromise * p);
#endif
void ReadAuthKey (const std::string& group, const std::map<std::string, std::string> * params);
private:
i2p::data::PrivateKeys m_Keys;
@@ -261,11 +262,26 @@ namespace client
boost::asio::deadline_timer m_ReadyChecker;
std::shared_ptr<std::vector<i2p::data::AuthPublicKey> > m_AuthKeys; // we don't need them for I2CP
public:
// for HTTP only
std::vector<std::shared_ptr<const i2p::stream::Stream> > GetAllStreams () const;
bool DeleteStream (uint32_t recvStreamID);
};
class RunnableClientDestination: private i2p::util::RunnableService, public ClientDestination
{
public:
RunnableClientDestination (const i2p::data::PrivateKeys& keys, bool isPublic, const std::map<std::string, std::string> * params = nullptr);
~RunnableClientDestination ();
void Start ();
void Stop ();
};
}
}

View File

@@ -0,0 +1,664 @@
#include <string.h>
#include <openssl/sha.h>
#include "Log.h"
#include "Crypto.h"
#include "Elligator.h"
#include "Tag.h"
#include "I2PEndian.h"
#include "Timestamp.h"
#include "Tunnel.h"
#include "TunnelPool.h"
#include "ECIESX25519AEADRatchetSession.h"
namespace i2p
{
namespace garlic
{
void RatchetTagSet::DHInitialize (const uint8_t * rootKey, const uint8_t * k)
{
// DH_INITIALIZE(rootKey, k)
uint8_t keydata[64];
i2p::crypto::HKDF (rootKey, k, 32, "KDFDHRatchetStep", keydata); // keydata = HKDF(rootKey, k, "KDFDHRatchetStep", 64)
// nextRootKey = keydata[0:31]
i2p::crypto::HKDF (keydata + 32, nullptr, 0, "TagAndKeyGenKeys", m_KeyData.buf);
// [sessTag_ck, symmKey_ck] = HKDF(keydata[32:63], ZEROLEN, "TagAndKeyGenKeys", 64)
memcpy (m_SymmKeyCK, m_KeyData.buf + 32, 32);
m_NextSymmKeyIndex = 0;
}
void RatchetTagSet::NextSessionTagRatchet ()
{
i2p::crypto::HKDF (m_KeyData.GetSessTagCK (), nullptr, 0, "STInitialization", m_KeyData.buf); // [sessTag_ck, sesstag_constant] = HKDF(sessTag_ck, ZEROLEN, "STInitialization", 64)
memcpy (m_SessTagConstant, m_KeyData.GetSessTagConstant (), 32);
m_NextIndex = 0;
}
uint64_t RatchetTagSet::GetNextSessionTag ()
{
i2p::crypto::HKDF (m_KeyData.GetSessTagCK (), m_SessTagConstant, 32, "SessionTagKeyGen", m_KeyData.buf); // [sessTag_ck, tag] = HKDF(sessTag_chainkey, SESSTAG_CONSTANT, "SessionTagKeyGen", 64)
m_NextIndex++;
if (m_NextIndex >= 65535) m_NextIndex = 0; // TODO: dirty hack, should create new tagset
return m_KeyData.GetTag ();
}
void RatchetTagSet::GetSymmKey (int index, uint8_t * key)
{
if (m_NextSymmKeyIndex > 0 && index >= m_NextSymmKeyIndex)
{
auto num = index + 1 - m_NextSymmKeyIndex;
for (int i = 0; i < num; i++)
i2p::crypto::HKDF (m_CurrentSymmKeyCK, nullptr, 0, "SymmetricRatchet", m_CurrentSymmKeyCK);
m_NextSymmKeyIndex += num;
memcpy (key, m_CurrentSymmKeyCK + 32, 32);
}
else
CalculateSymmKeyCK (index, key);
}
void RatchetTagSet::CalculateSymmKeyCK (int index, uint8_t * key)
{
// TODO: store intermediate keys
uint8_t currentSymmKeyCK[64];
i2p::crypto::HKDF (m_SymmKeyCK, nullptr, 0, "SymmetricRatchet", currentSymmKeyCK); // keydata_0 = HKDF(symmKey_ck, SYMMKEY_CONSTANT, "SymmetricRatchet", 64)
for (int i = 0; i < index; i++)
i2p::crypto::HKDF (currentSymmKeyCK, nullptr, 0, "SymmetricRatchet", currentSymmKeyCK); // keydata_n = HKDF(symmKey_chainKey_(n-1), SYMMKEY_CONSTANT, "SymmetricRatchet", 64)
memcpy (key, currentSymmKeyCK + 32, 32);
}
ECIESX25519AEADRatchetSession::ECIESX25519AEADRatchetSession (GarlicDestination * owner):
GarlicRoutingSession (owner, true)
{
ResetKeys ();
}
ECIESX25519AEADRatchetSession::~ECIESX25519AEADRatchetSession ()
{
}
void ECIESX25519AEADRatchetSession::ResetKeys ()
{
// TODO : use precalculated hashes
static const char protocolName[41] = "Noise_IKelg2+hs2_25519_ChaChaPoly_SHA256"; // 40 bytes
SHA256 ((const uint8_t *)protocolName, 40, m_H);
memcpy (m_CK, m_H, 32);
SHA256 (m_H, 32, m_H);
}
void ECIESX25519AEADRatchetSession::MixHash (const uint8_t * buf, size_t len)
{
SHA256_CTX ctx;
SHA256_Init (&ctx);
SHA256_Update (&ctx, m_H, 32);
SHA256_Update (&ctx, buf, len);
SHA256_Final (m_H, &ctx);
}
void ECIESX25519AEADRatchetSession::CreateNonce (uint64_t seqn, uint8_t * nonce)
{
memset (nonce, 0, 4);
htole64buf (nonce + 4, seqn);
}
bool ECIESX25519AEADRatchetSession::GenerateEphemeralKeysAndEncode (uint8_t * buf)
{
for (int i = 0; i < 10; i++)
{
m_EphemeralKeys.GenerateKeys ();
if (i2p::crypto::GetElligator ()->Encode (m_EphemeralKeys.GetPublicKey (), buf))
return true; // success
}
return false;
}
uint64_t ECIESX25519AEADRatchetSession::CreateNewSessionTag () const
{
uint8_t tagsetKey[32];
i2p::crypto::HKDF (m_CK, nullptr, 0, "SessionReplyTags", tagsetKey, 32); // tagsetKey = HKDF(chainKey, ZEROLEN, "SessionReplyTags", 32)
// Session Tag Ratchet
RatchetTagSet tagsetNsr;
tagsetNsr.DHInitialize (m_CK, tagsetKey); // tagset_nsr = DH_INITIALIZE(chainKey, tagsetKey)
tagsetNsr.NextSessionTagRatchet ();
return tagsetNsr.GetNextSessionTag ();
}
bool ECIESX25519AEADRatchetSession::HandleNewIncomingSession (const uint8_t * buf, size_t len)
{
if (!GetOwner ()) return false;
// we are Bob
// KDF1
MixHash (GetOwner ()->GetEncryptionPublicKey (i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RARCHET), 32); // h = SHA256(h || bpk)
if (!i2p::crypto::GetElligator ()->Decode (buf, m_Aepk))
{
LogPrint (eLogError, "Garlic: Can't decode elligator");
return false;
}
buf += 32; len -= 32;
MixHash (m_Aepk, 32); // h = SHA256(h || aepk)
uint8_t sharedSecret[32];
GetOwner ()->Decrypt (m_Aepk, sharedSecret, nullptr, i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RARCHET); // x25519(bsk, aepk)
i2p::crypto::HKDF (m_CK, sharedSecret, 32, "", m_CK); // [chainKey, key] = HKDF(chainKey, sharedSecret, "", 64)
// decrypt flags/static
uint8_t nonce[12], fs[32];
CreateNonce (0, nonce);
if (!i2p::crypto::AEADChaCha20Poly1305 (buf, 32, m_H, 32, m_CK + 32, nonce, fs, 32, false)) // decrypt
{
LogPrint (eLogWarning, "Garlic: Flags/static section AEAD verification failed ");
return false;
}
MixHash (buf, 48); // h = SHA256(h || ciphertext)
buf += 48; len -= 48; // 32 data + 16 poly
// decrypt payload
std::vector<uint8_t> payload (len - 16);
// KDF2 for payload
bool isStatic = !i2p::data::Tag<32> (fs).IsZero ();
if (isStatic)
{
// static key, fs is apk
memcpy (m_RemoteStaticKey, fs, 32);
GetOwner ()->Decrypt (fs, sharedSecret, nullptr, i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RARCHET); // x25519(bsk, apk)
i2p::crypto::HKDF (m_CK, sharedSecret, 32, "", m_CK); // [chainKey, key] = HKDF(chainKey, sharedSecret, "", 64)
}
else // all zeros flags
CreateNonce (1, nonce);
if (!i2p::crypto::AEADChaCha20Poly1305 (buf, len - 16, m_H, 32, m_CK + 32, nonce, payload.data (), len - 16, false)) // decrypt
{
LogPrint (eLogWarning, "Garlic: Payload section AEAD verification failed");
return false;
}
if (isStatic) MixHash (buf, len); // h = SHA256(h || ciphertext)
m_State = eSessionStateNewSessionReceived;
GetOwner ()->AddECIESx25519Session (m_RemoteStaticKey, shared_from_this ());
HandlePayload (payload.data (), len - 16);
return true;
}
void ECIESX25519AEADRatchetSession::HandlePayload (const uint8_t * buf, size_t len, int index)
{
size_t offset = 0;
while (offset < len)
{
uint8_t blk = buf[offset];
offset++;
auto size = bufbe16toh (buf + offset);
offset += 2;
LogPrint (eLogDebug, "Garlic: Block type ", (int)blk, " of size ", size);
if (size > len)
{
LogPrint (eLogError, "Garlic: Unexpected block length ", size);
break;
}
switch (blk)
{
case eECIESx25519BlkGalicClove:
GetOwner ()->HandleECIESx25519GarlicClove (buf + offset, size);
break;
case eECIESx25519BlkDateTime:
LogPrint (eLogDebug, "Garlic: datetime");
break;
case eECIESx25519BlkOptions:
LogPrint (eLogDebug, "Garlic: options");
break;
case eECIESx25519BlkPadding:
LogPrint (eLogDebug, "Garlic: padding");
break;
case eECIESx25519BlkAckRequest:
{
LogPrint (eLogDebug, "Garlic: ack request");
m_AckRequests.push_back ({0, index}); // TODO: use actual tagsetid
break;
}
default:
LogPrint (eLogWarning, "Garlic: Unknown block type ", (int)blk);
}
offset += size;
}
}
bool ECIESX25519AEADRatchetSession::NewOutgoingSessionMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen)
{
ResetKeys ();
// we are Alice, bpk is m_RemoteStaticKey
size_t offset = 0;
if (!GenerateEphemeralKeysAndEncode (out + offset))
{
LogPrint (eLogError, "Garlic: Can't encode elligator");
return false;
}
offset += 32;
// KDF1
MixHash (m_RemoteStaticKey, 32); // h = SHA256(h || bpk)
MixHash (m_EphemeralKeys.GetPublicKey (), 32); // h = SHA256(h || aepk)
uint8_t sharedSecret[32];
m_EphemeralKeys.Agree (m_RemoteStaticKey, sharedSecret); // x25519(aesk, bpk)
i2p::crypto::HKDF (m_CK, sharedSecret, 32, "", m_CK); // [chainKey, key] = HKDF(chainKey, sharedSecret, "", 64)
// encrypt static key section
uint8_t nonce[12];
CreateNonce (0, nonce);
if (!i2p::crypto::AEADChaCha20Poly1305 (GetOwner ()->GetEncryptionPublicKey (i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RARCHET), 32, m_H, 32, m_CK + 32, nonce, out + offset, 48, true)) // encrypt
{
LogPrint (eLogWarning, "Garlic: Static section AEAD encryption failed ");
return false;
}
MixHash (out + offset, 48); // h = SHA256(h || ciphertext)
offset += 48;
// KDF2
GetOwner ()->Decrypt (m_RemoteStaticKey, sharedSecret, nullptr, i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RARCHET); // x25519 (ask, bpk)
i2p::crypto::HKDF (m_CK, sharedSecret, 32, "", m_CK); // [chainKey, key] = HKDF(chainKey, sharedSecret, "", 64)
// encrypt payload
if (!i2p::crypto::AEADChaCha20Poly1305 (payload, len, m_H, 32, m_CK + 32, nonce, out + offset, len + 16, true)) // encrypt
{
LogPrint (eLogWarning, "Garlic: Payload section AEAD encryption failed");
return false;
}
MixHash (out + offset, len + 16); // h = SHA256(h || ciphertext)
m_State = eSessionStateNewSessionSent;
if (GetOwner ())
GetOwner ()->AddECIESx25519SessionTag (0, CreateNewSessionTag (), shared_from_this ());
return true;
}
bool ECIESX25519AEADRatchetSession::NewSessionReplyMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen)
{
// we are Bob
uint64_t tag = CreateNewSessionTag ();
size_t offset = 0;
memcpy (out + offset, &tag, 8);
offset += 8;
if (!GenerateEphemeralKeysAndEncode (out + offset)) // bepk
{
LogPrint (eLogError, "Garlic: Can't encode elligator");
return false;
}
offset += 32;
// KDF for Reply Key Section
MixHash ((const uint8_t *)&tag, 8); // h = SHA256(h || tag)
MixHash (m_EphemeralKeys.GetPublicKey (), 32); // h = SHA256(h || bepk)
uint8_t sharedSecret[32];
m_EphemeralKeys.Agree (m_Aepk, sharedSecret); // sharedSecret = x25519(besk, aepk)
i2p::crypto::HKDF (m_CK, sharedSecret, 32, "", m_CK, 32); // chainKey = HKDF(chainKey, sharedSecret, "", 32)
m_EphemeralKeys.Agree (m_RemoteStaticKey, sharedSecret); // sharedSecret = x25519(besk, apk)
i2p::crypto::HKDF (m_CK, sharedSecret, 32, "", m_CK); // [chainKey, key] = HKDF(chainKey, sharedSecret, "", 64)
uint8_t nonce[12];
CreateNonce (0, nonce);
// calulate hash for zero length
if (!i2p::crypto::AEADChaCha20Poly1305 (sharedSecret /* can be anything */, 0, m_H, 32, m_CK + 32, nonce, out + offset, 16, true)) // encrypt, ciphertext = ENCRYPT(k, n, ZEROLEN, ad)
{
LogPrint (eLogWarning, "Garlic: Reply key section AEAD encryption failed");
return false;
}
MixHash (out + offset, 16); // h = SHA256(h || ciphertext)
offset += 16;
memcpy (m_NSRHeader, out, 56); // for possible next NSR
// KDF for payload
uint8_t keydata[64];
i2p::crypto::HKDF (m_CK, nullptr, 0, "", keydata); // keydata = HKDF(chainKey, ZEROLEN, "", 64)
// k_ab = keydata[0:31], k_ba = keydata[32:63]
m_ReceiveTagset.DHInitialize (m_CK, keydata); // tagset_ab = DH_INITIALIZE(chainKey, k_ab)
m_ReceiveTagset.NextSessionTagRatchet ();
m_SendTagset.DHInitialize (m_CK, keydata + 32); // tagset_ba = DH_INITIALIZE(chainKey, k_ba)
m_SendTagset.NextSessionTagRatchet ();
GenerateMoreReceiveTags (GetOwner ()->GetNumTags ());
i2p::crypto::HKDF (keydata + 32, nullptr, 0, "AttachPayloadKDF", m_NSRKey, 32); // k = HKDF(k_ba, ZEROLEN, "AttachPayloadKDF", 32)
// encrypt payload
if (!i2p::crypto::AEADChaCha20Poly1305 (payload, len, m_H, 32, m_NSRKey, nonce, out + offset, len + 16, true)) // encrypt
{
LogPrint (eLogWarning, "Garlic: NSR payload section AEAD encryption failed");
return false;
}
m_State = eSessionStateNewSessionReplySent;
return true;
}
bool ECIESX25519AEADRatchetSession::NextNewSessionReplyMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen)
{
// we are Bob and sent NSR already
memcpy (out, m_NSRHeader, 56);
uint8_t nonce[12];
CreateNonce (0, nonce);
// encrypt payload
if (!i2p::crypto::AEADChaCha20Poly1305 (payload, len, m_H, 32, m_NSRKey, nonce, out + 56, len + 16, true)) // encrypt
{
LogPrint (eLogWarning, "Garlic: Next NSR payload section AEAD encryption failed");
return false;
}
return true;
}
bool ECIESX25519AEADRatchetSession::HandleNewOutgoingSessionReply (const uint8_t * buf, size_t len)
{
// we are Alice
LogPrint (eLogDebug, "Garlic: reply received");
const uint8_t * tag = buf;
buf += 8; len -= 8; // tag
uint8_t bepk[32]; // Bob's ephemeral key
if (!i2p::crypto::GetElligator ()->Decode (buf, bepk))
{
LogPrint (eLogError, "Garlic: Can't decode elligator");
return false;
}
buf += 32; len -= 32;
// KDF for Reply Key Section
MixHash (tag, 8); // h = SHA256(h || tag)
MixHash (bepk, 32); // h = SHA256(h || bepk)
uint8_t sharedSecret[32];
m_EphemeralKeys.Agree (bepk, sharedSecret); // sharedSecret = x25519(aesk, bepk)
i2p::crypto::HKDF (m_CK, sharedSecret, 32, "", m_CK, 32); // chainKey = HKDF(chainKey, sharedSecret, "", 32)
GetOwner ()->Decrypt (bepk, sharedSecret, nullptr, i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RARCHET); // x25519 (ask, bepk)
i2p::crypto::HKDF (m_CK, sharedSecret, 32, "", m_CK); // [chainKey, key] = HKDF(chainKey, sharedSecret, "", 64)
uint8_t nonce[12];
CreateNonce (0, nonce);
// calulate hash for zero length
if (!i2p::crypto::AEADChaCha20Poly1305 (buf, 0, m_H, 32, m_CK + 32, nonce, sharedSecret/* can be anyting */, 0, false)) // decrypt, DECRYPT(k, n, ZEROLEN, ad) verification only
{
LogPrint (eLogWarning, "Garlic: Reply key section AEAD decryption failed");
return false;
}
MixHash (buf, 16); // h = SHA256(h || ciphertext)
buf += 16; len -= 16;
// KDF for payload
uint8_t keydata[64];
i2p::crypto::HKDF (m_CK, nullptr, 0, "", keydata); // keydata = HKDF(chainKey, ZEROLEN, "", 64)
// k_ab = keydata[0:31], k_ba = keydata[32:63]
m_SendTagset.DHInitialize (m_CK, keydata); // tagset_ab = DH_INITIALIZE(chainKey, k_ab)
m_SendTagset.NextSessionTagRatchet ();
m_ReceiveTagset.DHInitialize (m_CK, keydata + 32); // tagset_ba = DH_INITIALIZE(chainKey, k_ba)
m_ReceiveTagset.NextSessionTagRatchet ();
GenerateMoreReceiveTags (GetOwner ()->GetNumTags ());
i2p::crypto::HKDF (keydata + 32, nullptr, 0, "AttachPayloadKDF", keydata, 32); // k = HKDF(k_ba, ZEROLEN, "AttachPayloadKDF", 32)
// decrypt payload
std::vector<uint8_t> payload (len - 16);
if (!i2p::crypto::AEADChaCha20Poly1305 (buf, len - 16, m_H, 32, keydata, nonce, payload.data (), len - 16, false)) // decrypt
{
LogPrint (eLogWarning, "Garlic: Payload section AEAD decryption failed");
return false;
}
m_State = eSessionStateEstablished;
GetOwner ()->AddECIESx25519Session (m_RemoteStaticKey, shared_from_this ());
HandlePayload (payload.data (), len - 16);
return true;
}
bool ECIESX25519AEADRatchetSession::NewExistingSessionMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen)
{
uint8_t nonce[12];
auto index = m_SendTagset.GetNextIndex ();
CreateNonce (index, nonce); // tag's index
uint64_t tag = m_SendTagset.GetNextSessionTag ();
memcpy (out, &tag, 8);
// ad = The session tag, 8 bytes
// ciphertext = ENCRYPT(k, n, payload, ad)
uint8_t key[32];
m_SendTagset.GetSymmKey (index, key);
if (!i2p::crypto::AEADChaCha20Poly1305 (payload, len, out, 8, key, nonce, out + 8, outLen - 8, true)) // encrypt
{
LogPrint (eLogWarning, "Garlic: Payload section AEAD encryption failed");
return false;
}
return true;
}
bool ECIESX25519AEADRatchetSession::HandleExistingSessionMessage (const uint8_t * buf, size_t len, int index)
{
uint8_t nonce[12];
CreateNonce (index, nonce); // tag's index
len -= 8; // tag
std::vector<uint8_t> payload (len - 16);
uint8_t key[32];
m_ReceiveTagset.GetSymmKey (index, key);
if (!i2p::crypto::AEADChaCha20Poly1305 (buf + 8, len - 16, buf, 8, key, nonce, payload.data (), len - 16, false)) // decrypt
{
LogPrint (eLogWarning, "Garlic: Payload section AEAD decryption failed");
return false;
}
HandlePayload (payload.data (), len - 16, index);
if (m_ReceiveTagset.GetNextIndex () - index <= GetOwner ()->GetNumTags ()*2/3)
GenerateMoreReceiveTags (GetOwner ()->GetNumTags ());
return true;
}
bool ECIESX25519AEADRatchetSession::HandleNextMessage (const uint8_t * buf, size_t len, int index)
{
m_LastActivityTimestamp = i2p::util::GetSecondsSinceEpoch ();
switch (m_State)
{
case eSessionStateNewSessionReplySent:
m_State = eSessionStateEstablished;
#if (__cplusplus >= 201703L) // C++ 17 or higher
[[fallthrough]];
#endif
case eSessionStateEstablished:
return HandleExistingSessionMessage (buf, len, index);
case eSessionStateNew:
return HandleNewIncomingSession (buf, len);
case eSessionStateNewSessionSent:
return HandleNewOutgoingSessionReply (buf, len);
default:
return false;
}
return true;
}
std::shared_ptr<I2NPMessage> ECIESX25519AEADRatchetSession::WrapSingleMessage (std::shared_ptr<const I2NPMessage> msg)
{
auto m = NewI2NPMessage ();
m->Align (12); // in order to get buf aligned to 16 (12 + 4)
uint8_t * buf = m->GetPayload () + 4; // 4 bytes for length
auto payload = CreatePayload (msg);
size_t len = payload.size ();
switch (m_State)
{
case eSessionStateEstablished:
if (!NewExistingSessionMessage (payload.data (), payload.size (), buf, m->maxLen))
return nullptr;
len += 24;
break;
case eSessionStateNew:
if (!NewOutgoingSessionMessage (payload.data (), payload.size (), buf, m->maxLen))
return nullptr;
len += 96;
break;
case eSessionStateNewSessionReceived:
if (!NewSessionReplyMessage (payload.data (), payload.size (), buf, m->maxLen))
return nullptr;
len += 72;
break;
case eSessionStateNewSessionReplySent:
if (!NextNewSessionReplyMessage (payload.data (), payload.size (), buf, m->maxLen))
return nullptr;
len += 72;
break;
default:
return nullptr;
}
htobe32buf (m->GetPayload (), len);
m->len += len + 4;
m->FillI2NPMessageHeader (eI2NPGarlic);
return m;
}
std::vector<uint8_t> ECIESX25519AEADRatchetSession::CreatePayload (std::shared_ptr<const I2NPMessage> msg)
{
uint64_t ts = i2p::util::GetMillisecondsSinceEpoch ();
size_t payloadLen = 7; // datatime
if (msg && m_Destination)
payloadLen += msg->GetPayloadLength () + 13 + 32;
auto leaseSet = (GetLeaseSetUpdateStatus () == eLeaseSetUpdated) ? CreateDatabaseStoreMsg (GetOwner ()->GetLeaseSet ()) : nullptr;
std::shared_ptr<I2NPMessage> deliveryStatus;
if (leaseSet)
{
payloadLen += leaseSet->GetPayloadLength () + 13;
deliveryStatus = CreateEncryptedDeliveryStatusMsg (leaseSet->GetMsgID ());
payloadLen += deliveryStatus->GetPayloadLength () + 49;
if (GetLeaseSetUpdateMsgID ()) GetOwner ()->RemoveDeliveryStatusSession (GetLeaseSetUpdateMsgID ()); // remove previous
SetLeaseSetUpdateStatus (eLeaseSetSubmitted);
SetLeaseSetUpdateMsgID (leaseSet->GetMsgID ());
SetLeaseSetSubmissionTime (ts);
GetOwner ()->DeliveryStatusSent (shared_from_this (), leaseSet->GetMsgID ());
}
if (m_AckRequests.size () > 0)
payloadLen += m_AckRequests.size ()*4 + 3;
uint8_t paddingSize;
RAND_bytes (&paddingSize, 1);
paddingSize &= 0x0F; paddingSize++; // 1 - 16
payloadLen += paddingSize + 3;
std::vector<uint8_t> v(payloadLen);
size_t offset = 0;
// DateTime
v[offset] = eECIESx25519BlkDateTime; offset++;
htobe16buf (v.data () + offset, 4); offset += 2;
htobe32buf (v.data () + offset, ts/1000); offset += 4; // in seconds
// LeaseSet
if (leaseSet)
offset += CreateGarlicClove (leaseSet, v.data () + offset, payloadLen - offset);
// DeliveryStatus
if (deliveryStatus)
offset += CreateDeliveryStatusClove (deliveryStatus, v.data () + offset, payloadLen - offset);
// msg
if (msg && m_Destination)
offset += CreateGarlicClove (msg, v.data () + offset, payloadLen - offset, true);
// ack
if (m_AckRequests.size () > 0)
{
v[offset] = eECIESx25519BlkAck; offset++;
htobe16buf (v.data () + offset, m_AckRequests.size ()*4); offset += 2;
for (auto& it: m_AckRequests)
{
htobe16buf (v.data () + offset, it.first); offset += 2;
htobe16buf (v.data () + offset, it.second); offset += 2;
}
m_AckRequests.clear ();
}
// padding
v[offset] = eECIESx25519BlkPadding; offset++;
htobe16buf (v.data () + offset, paddingSize); offset += 2;
memset (v.data () + offset, 0, paddingSize); offset += paddingSize;
return v;
}
size_t ECIESX25519AEADRatchetSession::CreateGarlicClove (std::shared_ptr<const I2NPMessage> msg, uint8_t * buf, size_t len, bool isDestination)
{
if (!msg) return 0;
uint16_t cloveSize = msg->GetPayloadLength () + 9 + 1;
if (isDestination) cloveSize += 32;
if ((int)len < cloveSize + 3) return 0;
buf[0] = eECIESx25519BlkGalicClove; // clove type
htobe16buf (buf + 1, cloveSize); // size
buf += 3;
if (isDestination)
{
*buf = (eGarlicDeliveryTypeDestination << 5);
memcpy (buf + 1, *m_Destination, 32); buf += 32;
}
else
*buf = 0;
buf++; // flag and delivery instructions
*buf = msg->GetTypeID (); // I2NP msg type
htobe32buf (buf + 1, msg->GetMsgID ()); // msgID
htobe32buf (buf + 5, msg->GetExpiration ()/1000); // expiration in seconds
memcpy (buf + 9, msg->GetPayload (), msg->GetPayloadLength ());
return cloveSize + 3;
}
size_t ECIESX25519AEADRatchetSession::CreateDeliveryStatusClove (std::shared_ptr<const I2NPMessage> msg, uint8_t * buf, size_t len)
{
uint16_t cloveSize = msg->GetPayloadLength () + 9 + 37 /* delivery instruction */;
if ((int)len < cloveSize + 3) return 0;
buf[0] = eECIESx25519BlkGalicClove; // clove type
htobe16buf (buf + 1, cloveSize); // size
buf += 3;
if (GetOwner ())
{
auto inboundTunnel = GetOwner ()->GetTunnelPool ()->GetNextInboundTunnel ();
if (inboundTunnel)
{
// delivery instructions
*buf = eGarlicDeliveryTypeTunnel << 5; buf++; // delivery instructions flag tunnel
// hash and tunnelID sequence is reversed for Garlic
memcpy (buf, inboundTunnel->GetNextIdentHash (), 32); buf += 32;// To Hash
htobe32buf (buf, inboundTunnel->GetNextTunnelID ()); buf += 4;// tunnelID
}
else
{
LogPrint (eLogError, "Garlic: No inbound tunnels in the pool for DeliveryStatus");
return 0;
}
*buf = msg->GetTypeID (); // I2NP msg type
htobe32buf (buf + 1, msg->GetMsgID ()); // msgID
htobe32buf (buf + 5, msg->GetExpiration ()/1000); // expiration in seconds
memcpy (buf + 9, msg->GetPayload (), msg->GetPayloadLength ());
}
else
return 0;
return cloveSize + 3;
}
void ECIESX25519AEADRatchetSession::GenerateMoreReceiveTags (int numTags)
{
for (int i = 0; i < numTags; i++)
{
auto index = m_ReceiveTagset.GetNextIndex ();
uint64_t tag = m_ReceiveTagset.GetNextSessionTag ();
GetOwner ()->AddECIESx25519SessionTag (index, tag, shared_from_this ());
}
}
bool ECIESX25519AEADRatchetSession::CheckExpired (uint64_t ts)
{
CleanupUnconfirmedLeaseSet (ts);
return ts > m_LastActivityTimestamp + ECIESX25519_EXPIRATION_TIMEOUT;
}
std::shared_ptr<I2NPMessage> WrapECIESX25519AEADRatchetMessage (std::shared_ptr<const I2NPMessage> msg, const uint8_t * key, uint64_t tag)
{
auto m = NewI2NPMessage ();
m->Align (12); // in order to get buf aligned to 16 (12 + 4)
uint8_t * buf = m->GetPayload () + 4; // 4 bytes for length
uint8_t nonce[12];
memset (nonce, 0, 12); // n = 0
size_t offset = 0;
memcpy (buf + offset, &tag, 8); offset += 8;
auto payload = buf + offset;
uint16_t cloveSize = msg->GetPayloadLength () + 9 + 1;
size_t len = cloveSize + 3;
payload[0] = eECIESx25519BlkGalicClove; // clove type
htobe16buf (payload + 1, cloveSize); // size
payload += 3;
*payload = 0; payload++; // flag and delivery instructions
*payload = msg->GetTypeID (); // I2NP msg type
htobe32buf (payload + 1, msg->GetMsgID ()); // msgID
htobe32buf (payload + 5, msg->GetExpiration ()/1000); // expiration in seconds
memcpy (payload + 9, msg->GetPayload (), msg->GetPayloadLength ());
if (!i2p::crypto::AEADChaCha20Poly1305 (buf + offset, len, buf, 8, key, nonce, buf + offset, len + 16, true)) // encrypt
{
LogPrint (eLogWarning, "Garlic: Payload section AEAD encryption failed");
return nullptr;
}
offset += len + 16;
htobe32buf (m->GetPayload (), offset);
m->len += offset + 4;
m->FillI2NPMessageHeader (eI2NPGarlic);
return m;
}
}
}

View File

@@ -0,0 +1,136 @@
#ifndef ECIES_X25519_AEAD_RATCHET_SESSION_H__
#define ECIES_X25519_AEAD_RATCHET_SESSION_H__
#include <string.h>
#include <inttypes.h>
#include <functional>
#include <memory>
#include <vector>
#include <list>
#include "Identity.h"
#include "Crypto.h"
#include "Garlic.h"
namespace i2p
{
namespace garlic
{
class RatchetTagSet
{
public:
void DHInitialize (const uint8_t * rootKey, const uint8_t * k);
void NextSessionTagRatchet ();
uint64_t GetNextSessionTag ();
int GetNextIndex () const { return m_NextIndex; };
void GetSymmKey (int index, uint8_t * key);
private:
void CalculateSymmKeyCK (int index, uint8_t * key);
private:
union
{
uint64_t ll[8];
uint8_t buf[64];
const uint8_t * GetSessTagCK () const { return buf; }; // sessTag_chainKey = keydata[0:31]
const uint8_t * GetSessTagConstant () const { return buf + 32; }; // SESSTAG_CONSTANT = keydata[32:63]
uint64_t GetTag () const { return ll[4]; }; // tag = keydata[32:39]
} m_KeyData;
uint8_t m_SessTagConstant[32], m_SymmKeyCK[32], m_CurrentSymmKeyCK[64];
int m_NextIndex, m_NextSymmKeyIndex;
};
enum ECIESx25519BlockType
{
eECIESx25519BlkDateTime = 0,
eECIESx25519BlkSessionID = 1,
eECIESx25519BlkTermination = 4,
eECIESx25519BlkOptions = 5,
eECIESx25519BlkNextSessionKey = 7,
eECIESx25519BlkAck = 8,
eECIESx25519BlkAckRequest = 9,
eECIESx25519BlkGalicClove = 11,
eECIESx25519BlkPadding = 254
};
const int ECIESX25519_RESTART_TIMEOUT = 120; // number of second of inactivity we should restart after
const int ECIESX25519_EXPIRATION_TIMEOUT = 600; // in seconds
class ECIESX25519AEADRatchetSession: public GarlicRoutingSession, public std::enable_shared_from_this<ECIESX25519AEADRatchetSession>
{
enum SessionState
{
eSessionStateNew =0,
eSessionStateNewSessionReceived,
eSessionStateNewSessionSent,
eSessionStateNewSessionReplySent,
eSessionStateEstablished
};
public:
ECIESX25519AEADRatchetSession (GarlicDestination * owner);
~ECIESX25519AEADRatchetSession ();
bool HandleNextMessage (const uint8_t * buf, size_t len, int index = 0);
std::shared_ptr<I2NPMessage> WrapSingleMessage (std::shared_ptr<const I2NPMessage> msg);
const uint8_t * GetRemoteStaticKey () const { return m_RemoteStaticKey; }
void SetRemoteStaticKey (const uint8_t * key) { memcpy (m_RemoteStaticKey, key, 32); }
void SetDestination (const i2p::data::IdentHash& dest) // TODO:
{
if (!m_Destination) m_Destination.reset (new i2p::data::IdentHash (dest));
}
bool CheckExpired (uint64_t ts); // true is expired
bool CanBeRestarted (uint64_t ts) const { return ts > m_LastActivityTimestamp + ECIESX25519_RESTART_TIMEOUT; }
private:
void ResetKeys ();
void MixHash (const uint8_t * buf, size_t len);
void CreateNonce (uint64_t seqn, uint8_t * nonce);
bool GenerateEphemeralKeysAndEncode (uint8_t * buf); // buf is 32 bytes
uint64_t CreateNewSessionTag () const;
bool HandleNewIncomingSession (const uint8_t * buf, size_t len);
bool HandleNewOutgoingSessionReply (const uint8_t * buf, size_t len);
bool HandleExistingSessionMessage (const uint8_t * buf, size_t len, int index);
void HandlePayload (const uint8_t * buf, size_t len, int index = 0);
bool NewOutgoingSessionMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen);
bool NewSessionReplyMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen);
bool NextNewSessionReplyMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen);
bool NewExistingSessionMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen);
std::vector<uint8_t> CreatePayload (std::shared_ptr<const I2NPMessage> msg);
size_t CreateGarlicClove (std::shared_ptr<const I2NPMessage> msg, uint8_t * buf, size_t len, bool isDestination = false);
size_t CreateDeliveryStatusClove (std::shared_ptr<const I2NPMessage> msg, uint8_t * buf, size_t len);
void GenerateMoreReceiveTags (int numTags);
private:
uint8_t m_H[32], m_CK[64] /* [chainkey, key] */, m_RemoteStaticKey[32];
uint8_t m_Aepk[32]; // Alice's ephemeral keys, for incoming only
uint8_t m_NSRHeader[56], m_NSRKey[32]; // new session reply, for incoming only
i2p::crypto::X25519Keys m_EphemeralKeys;
SessionState m_State = eSessionStateNew;
uint64_t m_LastActivityTimestamp = 0; // incoming
RatchetTagSet m_SendTagset, m_ReceiveTagset;
std::unique_ptr<i2p::data::IdentHash> m_Destination;// TODO: might not need it
std::list<std::pair<uint16_t, int> > m_AckRequests; // (tagsetid, index)
};
std::shared_ptr<I2NPMessage> WrapECIESX25519AEADRatchetMessage (std::shared_ptr<const I2NPMessage> msg, const uint8_t * key, uint64_t tag);
}
}
#endif

View File

@@ -31,7 +31,7 @@ namespace crypto
BN_mod_inverse (tmp, tmp, q, ctx);
BN_set_word (d, 121665);
BN_set_negative (d, 1);
BN_mul (d, d, tmp, ctx);
BN_mod_mul (d, d, tmp, q, ctx);
// 2^((q-1)/4)
I = BN_new ();

207
libi2pd/Elligator.cpp Normal file
View File

@@ -0,0 +1,207 @@
#include <openssl/rand.h>
#include "Crypto.h"
#include "Elligator.h"
namespace i2p
{
namespace crypto
{
Elligator2::Elligator2 ()
{
// TODO: share with Ed22519
p = BN_new ();
// 2^255-19
BN_set_bit (p, 255); // 2^255
BN_sub_word (p, 19);
p38 = BN_dup (p); BN_add_word (p38, 3); BN_div_word (p38, 8); // (p+3)/8
p12 = BN_dup (p); BN_sub_word (p12, 1); BN_div_word (p12, 2); // (p-1)/2
p14 = BN_dup (p); BN_sub_word (p14, 1); BN_div_word (p14, 4); // (p-1)/4
A = BN_new (); BN_set_word (A, 486662);
nA = BN_new (); BN_sub (nA, p, A);
BN_CTX * ctx = BN_CTX_new ();
// calculate sqrt(-1)
sqrtn1 = BN_new ();
BN_set_word (sqrtn1, 2);
BN_mod_exp (sqrtn1, sqrtn1, p14, p, ctx); // 2^((p-1)/4
u = BN_new (); BN_set_word (u, 2);
iu = BN_new (); BN_mod_inverse (iu, u, p, ctx);
BN_CTX_free (ctx);
}
Elligator2::~Elligator2 ()
{
BN_free (p); BN_free (p38); BN_free (p12); BN_free (p14);
BN_free (sqrtn1); BN_free (A); BN_free (nA);
BN_free (u); BN_free (iu);
}
bool Elligator2::Encode (const uint8_t * key, uint8_t * encoded, bool highY, bool random) const
{
bool ret = true;
BN_CTX * ctx = BN_CTX_new ();
BN_CTX_start (ctx);
uint8_t key1[32];
for (size_t i = 0; i < 16; i++) // from Little Endian
{
key1[i] = key[31 - i];
key1[31 - i] = key[i];
}
BIGNUM * x = BN_CTX_get (ctx); BN_bin2bn (key1, 32, x);
BIGNUM * xA = BN_CTX_get (ctx); BN_add (xA, x, A); // x + A
BN_sub (xA, p, xA); // p - (x + A)
BIGNUM * uxxA = BN_CTX_get (ctx); // u*x*xA
BN_mod_mul (uxxA, u, x, p, ctx);
BN_mod_mul (uxxA, uxxA, xA, p, ctx);
if (Legendre (uxxA, ctx) != -1)
{
uint8_t randByte = 0; // random highest bits and high y
if (random)
{
RAND_bytes (&randByte, 1);
highY = randByte & 0x01;
}
BIGNUM * r = BN_CTX_get (ctx);
if (highY)
{
BN_mod_inverse (r, x, p, ctx);
BN_mod_mul (r, r, xA, p, ctx);
}
else
{
BN_mod_inverse (r, xA, p, ctx);
BN_mod_mul (r, r, x, p, ctx);
}
BN_mod_mul (r, r, iu, p, ctx);
SquareRoot (r, r, ctx);
bn2buf (r, encoded, 32);
if (random)
encoded[0] |= (randByte & 0xC0); // copy two highest bits from randByte
for (size_t i = 0; i < 16; i++) // To Little Endian
{
uint8_t tmp = encoded[i];
encoded[i] = encoded[31 - i];
encoded[31 - i] = tmp;
}
}
else
ret = false;
BN_CTX_end (ctx);
BN_CTX_free (ctx);
return ret;
}
bool Elligator2::Decode (const uint8_t * encoded, uint8_t * key) const
{
bool ret = true;
BN_CTX * ctx = BN_CTX_new ();
BN_CTX_start (ctx);
uint8_t encoded1[32];
for (size_t i = 0; i < 16; i++) // from Little Endian
{
encoded1[i] = encoded[31 - i];
encoded1[31 - i] = encoded[i];
}
encoded1[0] &= 0x3F; // drop two highest bits
BIGNUM * r = BN_CTX_get (ctx); BN_bin2bn (encoded1, 32, r);
if (BN_cmp (r, p12) <= 0) // r < (p-1)/2
{
// v = -A/(1+u*r^2)
BIGNUM * v = BN_CTX_get (ctx); BN_mod_sqr (v, r, p, ctx);
BN_mod_mul (v, v, u, p, ctx);
BN_add_word (v, 1);
BN_mod_inverse (v, v, p, ctx);
BN_mod_mul (v, v, nA, p, ctx);
BIGNUM * vpA = BN_CTX_get (ctx);
BN_add (vpA, v, A); // v + A
// t = v^3+A*v^2+v = v^2*(v+A)+v
BIGNUM * t = BN_CTX_get (ctx); BN_mod_sqr (t, v, p, ctx);
BN_mod_mul (t, t, vpA, p, ctx);
BN_mod_add (t, t, v, p, ctx);
int legendre = Legendre (t, ctx);
BIGNUM * x = BN_CTX_get (ctx);
if (legendre == 1)
BN_copy (x, v);
else
{
BN_sub (x, p, v);
BN_mod_sub (x, x, A, p, ctx);
}
bn2buf (x, key, 32);
for (size_t i = 0; i < 16; i++) // To Little Endian
{
uint8_t tmp = key[i];
key[i] = key[31 - i];
key[31 - i] = tmp;
}
}
else
ret = false;
BN_CTX_end (ctx);
BN_CTX_free (ctx);
return ret;
}
void Elligator2::SquareRoot (const BIGNUM * x, BIGNUM * r, BN_CTX * ctx) const
{
BIGNUM * t = BN_CTX_get (ctx);
BN_mod_exp (t, x, p14, p, ctx); // t = x^((p-1)/4)
BN_mod_exp (r, x, p38, p, ctx); // r = x^((p+3)/8)
BN_add_word (t, 1);
if (!BN_cmp (t, p))
BN_mod_mul (r, r, sqrtn1, p, ctx);
if (BN_cmp (r, p12) > 0) // r > (p-1)/2
BN_sub (r, p, r);
}
int Elligator2::Legendre (const BIGNUM * a, BN_CTX * ctx) const
{
// assume a < p, so don't check for a % p = 0, but a = 0 only
if (BN_is_zero(a)) return 0;
BIGNUM * r = BN_CTX_get (ctx);
BN_mod_exp (r, a, p12, p, ctx); // r = a^((p-1)/2) mod p
if (BN_is_word(r, 1))
return 1;
else if (BN_is_zero(r))
return 0;
return -1;
}
static std::unique_ptr<Elligator2> g_Elligator;
std::unique_ptr<Elligator2>& GetElligator ()
{
if (!g_Elligator)
{
auto el = new Elligator2();
if (!g_Elligator) // make sure it was not created already
g_Elligator.reset (el);
else
delete el;
}
return g_Elligator;
}
}
}

39
libi2pd/Elligator.h Normal file
View File

@@ -0,0 +1,39 @@
#ifndef ELLIGATOR_H__
#define ELLIGATOR_H__
#include <inttypes.h>
#include <memory>
#include <openssl/bn.h>
namespace i2p
{
namespace crypto
{
class Elligator2
{
public:
Elligator2 ();
~Elligator2 ();
bool Encode (const uint8_t * key, uint8_t * encoded, bool highY = false, bool random = true) const;
bool Decode (const uint8_t * encoded, uint8_t * key) const;
private:
void SquareRoot (const BIGNUM * x, BIGNUM * r, BN_CTX * ctx) const;
int Legendre (const BIGNUM * a, BN_CTX * ctx) const; // a/p
private:
BIGNUM * p, * p38, * p12, * p14, * sqrtn1, * A, * nA, * u, * iu;
};
std::unique_ptr<Elligator2>& GetElligator ();
}
}
#endif

View File

@@ -1,61 +0,0 @@
#include "Event.h"
#include "Log.h"
namespace i2p
{
namespace event
{
#ifdef WITH_EVENTS
EventCore core;
#endif
void EventCore::SetListener(EventListener * l)
{
m_listener = l;
LogPrint(eLogInfo, "Event: listener set");
}
void EventCore::QueueEvent(const EventType & ev)
{
if(m_listener) m_listener->HandleEvent(ev);
}
void EventCore::CollectEvent(const std::string & type, const std::string & ident, uint64_t val)
{
std::unique_lock<std::mutex> lock(m_collect_mutex);
std::string key = type + "." + ident;
if (m_collected.find(key) == m_collected.end())
{
m_collected[key] = {type, key, 0};
}
m_collected[key].Val += val;
}
void EventCore::PumpCollected(EventListener * listener)
{
std::unique_lock<std::mutex> lock(m_collect_mutex);
if(listener)
{
for(const auto & ev : m_collected) {
listener->HandlePumpEvent({{"type", ev.second.Key}, {"ident", ev.second.Ident}}, ev.second.Val);
}
}
m_collected.clear();
}
}
}
void QueueIntEvent(const std::string & type, const std::string & ident, uint64_t val)
{
#ifdef WITH_EVENTS
i2p::event::core.CollectEvent(type, ident, val);
#endif
}
void EmitEvent(const EventType & e)
{
#if WITH_EVENTS
i2p::event::core.QueueEvent(e);
#endif
}

View File

@@ -1,53 +0,0 @@
#ifndef EVENT_H__
#define EVENT_H__
#include <map>
#include <string>
#include <memory>
#include <mutex>
#include <tuple>
#include <boost/asio.hpp>
typedef std::map<std::string, std::string> EventType;
namespace i2p
{
namespace event
{
class EventListener {
public:
virtual ~EventListener() {};
virtual void HandleEvent(const EventType & ev) = 0;
/** @brief handle collected event when pumped */
virtual void HandlePumpEvent(const EventType & ev, const uint64_t & val) = 0;
};
class EventCore
{
public:
void QueueEvent(const EventType & ev);
void CollectEvent(const std::string & type, const std::string & ident, uint64_t val);
void SetListener(EventListener * l);
void PumpCollected(EventListener * l);
private:
std::mutex m_collect_mutex;
struct CollectedEvent
{
std::string Key;
std::string Ident;
uint64_t Val;
};
std::map<std::string, CollectedEvent> m_collected;
EventListener * m_listener = nullptr;
};
#ifdef WITH_EVENTS
extern EventCore core;
#endif
}
}
void QueueIntEvent(const std::string & type, const std::string & ident, uint64_t val);
void EmitEvent(const EventType & ev);
#endif

View File

@@ -11,30 +11,22 @@
#include "Timestamp.h"
#include "Log.h"
#include "FS.h"
#include "ECIESX25519AEADRatchetSession.h"
#include "Garlic.h"
namespace i2p
{
namespace garlic
{
GarlicRoutingSession::GarlicRoutingSession (GarlicDestination * owner,
std::shared_ptr<const i2p::data::RoutingDestination> destination, int numTags, bool attachLeaseSet):
m_Owner (owner), m_Destination (destination), m_NumTags (numTags),
m_LeaseSetUpdateStatus (attachLeaseSet ? eLeaseSetUpdated : eLeaseSetDoNotSend),
GarlicRoutingSession::GarlicRoutingSession (GarlicDestination * owner, bool attachLeaseSet):
m_Owner (owner), m_LeaseSetUpdateStatus (attachLeaseSet ? eLeaseSetUpdated : eLeaseSetDoNotSend),
m_LeaseSetUpdateMsgID (0)
{
// create new session tags and session key
RAND_bytes (m_SessionKey, 32);
m_Encryption.SetKey (m_SessionKey);
}
GarlicRoutingSession::GarlicRoutingSession (const uint8_t * sessionKey, const SessionTag& sessionTag):
m_Owner (nullptr), m_NumTags (1), m_LeaseSetUpdateStatus (eLeaseSetDoNotSend), m_LeaseSetUpdateMsgID (0)
GarlicRoutingSession::GarlicRoutingSession ():
m_Owner (nullptr), m_LeaseSetUpdateStatus (eLeaseSetDoNotSend), m_LeaseSetUpdateMsgID (0)
{
memcpy (m_SessionKey, sessionKey, 32);
m_Encryption.SetKey (m_SessionKey);
m_SessionTags.push_back (sessionTag);
m_SessionTags.back ().creationTime = i2p::util::GetSecondsSinceEpoch ();
}
GarlicRoutingSession::~GarlicRoutingSession ()
@@ -66,88 +58,64 @@ namespace garlic
m_SharedRoutingPath = path;
}
GarlicRoutingSession::UnconfirmedTags * GarlicRoutingSession::GenerateSessionTags ()
bool GarlicRoutingSession::MessageConfirmed (uint32_t msgID)
{
auto tags = new UnconfirmedTags (m_NumTags);
tags->tagsCreationTime = i2p::util::GetSecondsSinceEpoch ();
for (int i = 0; i < m_NumTags; i++)
if (msgID == GetLeaseSetUpdateMsgID ())
{
RAND_bytes (tags->sessionTags[i], 32);
tags->sessionTags[i].creationTime = tags->tagsCreationTime;
}
return tags;
}
void GarlicRoutingSession::MessageConfirmed (uint32_t msgID)
{
TagsConfirmed (msgID);
if (msgID == m_LeaseSetUpdateMsgID)
{
m_LeaseSetUpdateStatus = eLeaseSetUpToDate;
m_LeaseSetUpdateMsgID = 0;
SetLeaseSetUpdateStatus (eLeaseSetUpToDate);
SetLeaseSetUpdateMsgID (0);
LogPrint (eLogInfo, "Garlic: LeaseSet update confirmed");
return true;
}
else
CleanupExpiredTags ();
}
return false;
}
void GarlicRoutingSession::TagsConfirmed (uint32_t msgID)
void GarlicRoutingSession::CleanupUnconfirmedLeaseSet (uint64_t ts)
{
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
auto it = m_UnconfirmedTagsMsgs.find (msgID);
if (it != m_UnconfirmedTagsMsgs.end ())
{
auto& tags = it->second;
if (ts < tags->tagsCreationTime + OUTGOING_TAGS_EXPIRATION_TIMEOUT)
{
for (int i = 0; i < tags->numTags; i++)
m_SessionTags.push_back (tags->sessionTags[i]);
}
m_UnconfirmedTagsMsgs.erase (it);
}
}
bool GarlicRoutingSession::CleanupExpiredTags ()
{
auto ts = i2p::util::GetSecondsSinceEpoch ();
for (auto it = m_SessionTags.begin (); it != m_SessionTags.end ();)
{
if (ts >= it->creationTime + OUTGOING_TAGS_EXPIRATION_TIMEOUT)
it = m_SessionTags.erase (it);
else
++it;
}
CleanupUnconfirmedTags ();
if (m_LeaseSetUpdateMsgID && ts*1000LL > m_LeaseSetSubmissionTime + LEASET_CONFIRMATION_TIMEOUT)
{
if (m_Owner)
m_Owner->RemoveDeliveryStatusSession (m_LeaseSetUpdateMsgID);
if (GetOwner ())
GetOwner ()->RemoveDeliveryStatusSession (m_LeaseSetUpdateMsgID);
m_LeaseSetUpdateMsgID = 0;
}
return !m_SessionTags.empty () || !m_UnconfirmedTagsMsgs.empty ();
}
bool GarlicRoutingSession::CleanupUnconfirmedTags ()
}
std::shared_ptr<I2NPMessage> GarlicRoutingSession::CreateEncryptedDeliveryStatusMsg (uint32_t msgID)
{
bool ret = false;
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
// delete expired unconfirmed tags
for (auto it = m_UnconfirmedTagsMsgs.begin (); it != m_UnconfirmedTagsMsgs.end ();)
auto msg = CreateDeliveryStatusMsg (msgID);
if (GetOwner ())
{
if (ts >= it->second->tagsCreationTime + OUTGOING_TAGS_CONFIRMATION_TIMEOUT)
{
if (m_Owner)
m_Owner->RemoveDeliveryStatusSession (it->first);
it = m_UnconfirmedTagsMsgs.erase (it);
ret = true;
}
else
++it;
//encrypt
uint8_t key[32], tag[32];
RAND_bytes (key, 32); // random session key
RAND_bytes (tag, 32); // random session tag
GetOwner ()->SubmitSessionKey (key, tag);
ElGamalAESSession garlic (key, tag);
msg = garlic.WrapSingleMessage (msg);
}
return ret;
return msg;
}
ElGamalAESSession::ElGamalAESSession (GarlicDestination * owner,
std::shared_ptr<const i2p::data::RoutingDestination> destination, int numTags, bool attachLeaseSet):
GarlicRoutingSession (owner, attachLeaseSet),
m_Destination (destination), m_NumTags (numTags)
{
// create new session tags and session key
RAND_bytes (m_SessionKey, 32);
m_Encryption.SetKey (m_SessionKey);
}
std::shared_ptr<I2NPMessage> GarlicRoutingSession::WrapSingleMessage (std::shared_ptr<const I2NPMessage> msg)
ElGamalAESSession::ElGamalAESSession (const uint8_t * sessionKey, const SessionTag& sessionTag):
m_NumTags(1)
{
memcpy (m_SessionKey, sessionKey, 32);
m_Encryption.SetKey (m_SessionKey);
m_SessionTags.push_back (sessionTag);
m_SessionTags.back ().creationTime = i2p::util::GetSecondsSinceEpoch ();
}
std::shared_ptr<I2NPMessage> ElGamalAESSession::WrapSingleMessage (std::shared_ptr<const I2NPMessage> msg)
{
auto m = NewI2NPMessage ();
m->Align (12); // in order to get buf aligned to 16 (12 + 4)
@@ -213,10 +181,10 @@ namespace garlic
return m;
}
size_t GarlicRoutingSession::CreateAESBlock (uint8_t * buf, std::shared_ptr<const I2NPMessage> msg)
size_t ElGamalAESSession::CreateAESBlock (uint8_t * buf, std::shared_ptr<const I2NPMessage> msg)
{
size_t blockSize = 0;
bool createNewTags = m_Owner && m_NumTags && ((int)m_SessionTags.size () <= m_NumTags*2/3);
bool createNewTags = GetOwner () && m_NumTags && ((int)m_SessionTags.size () <= m_NumTags*2/3);
UnconfirmedTags * newTags = createNewTags ? GenerateSessionTags () : nullptr;
htobuf16 (buf, newTags ? htobe16 (newTags->numTags) : 0); // tag count
blockSize += 2;
@@ -245,7 +213,7 @@ namespace garlic
return blockSize;
}
size_t GarlicRoutingSession::CreateGarlicPayload (uint8_t * payload, std::shared_ptr<const I2NPMessage> msg, UnconfirmedTags * newTags)
size_t ElGamalAESSession::CreateGarlicPayload (uint8_t * payload, std::shared_ptr<const I2NPMessage> msg, UnconfirmedTags * newTags)
{
uint64_t ts = i2p::util::GetMillisecondsSinceEpoch ();
uint32_t msgID;
@@ -255,17 +223,17 @@ namespace garlic
*numCloves = 0;
size++;
if (m_Owner)
if (GetOwner ())
{
// resubmit non-confirmed LeaseSet
if (m_LeaseSetUpdateStatus == eLeaseSetSubmitted && ts > m_LeaseSetSubmissionTime + LEASET_CONFIRMATION_TIMEOUT)
if (GetLeaseSetUpdateStatus () == eLeaseSetSubmitted && ts > GetLeaseSetSubmissionTime () + LEASET_CONFIRMATION_TIMEOUT)
{
m_LeaseSetUpdateStatus = eLeaseSetUpdated;
SetLeaseSetUpdateStatus (eLeaseSetUpdated);
SetSharedRoutingPath (nullptr); // invalidate path since leaseset was not confirmed
}
// attach DeviveryStatus if necessary
if (newTags || m_LeaseSetUpdateStatus == eLeaseSetUpdated) // new tags created or leaseset updated
if (newTags || GetLeaseSetUpdateStatus () == eLeaseSetUpdated) // new tags created or leaseset updated
{
// clove is DeliveryStatus
auto cloveSize = CreateDeliveryStatusClove (payload + size, msgID);
@@ -279,20 +247,20 @@ namespace garlic
m_UnconfirmedTagsMsgs.insert (std::make_pair(msgID, std::unique_ptr<UnconfirmedTags>(newTags)));
newTags = nullptr; // got acquired
}
m_Owner->DeliveryStatusSent (shared_from_this (), msgID);
GetOwner ()->DeliveryStatusSent (shared_from_this (), msgID);
}
else
LogPrint (eLogWarning, "Garlic: DeliveryStatus clove was not created");
}
// attach LeaseSet
if (m_LeaseSetUpdateStatus == eLeaseSetUpdated)
if (GetLeaseSetUpdateStatus () == eLeaseSetUpdated)
{
if (m_LeaseSetUpdateMsgID) m_Owner->RemoveDeliveryStatusSession (m_LeaseSetUpdateMsgID); // remove previous
m_LeaseSetUpdateStatus = eLeaseSetSubmitted;
m_LeaseSetUpdateMsgID = msgID;
m_LeaseSetSubmissionTime = ts;
if (GetLeaseSetUpdateMsgID ()) GetOwner ()->RemoveDeliveryStatusSession (GetLeaseSetUpdateMsgID ()); // remove previous
SetLeaseSetUpdateStatus (eLeaseSetSubmitted);
SetLeaseSetUpdateMsgID (msgID);
SetLeaseSetSubmissionTime (ts);
// clove if our leaseSet must be attached
auto leaseSet = CreateDatabaseStoreMsg (m_Owner->GetLeaseSet ());
auto leaseSet = CreateDatabaseStoreMsg (GetOwner ()->GetLeaseSet ());
size += CreateGarlicClove (payload + size, leaseSet, false);
(*numCloves)++;
}
@@ -313,7 +281,7 @@ namespace garlic
return size;
}
size_t GarlicRoutingSession::CreateGarlicClove (uint8_t * buf, std::shared_ptr<const I2NPMessage> msg, bool isDestination)
size_t ElGamalAESSession::CreateGarlicClove (uint8_t * buf, std::shared_ptr<const I2NPMessage> msg, bool isDestination)
{
uint64_t ts = i2p::util::GetMillisecondsSinceEpoch () + 8000; // 8 sec
size_t size = 0;
@@ -343,12 +311,12 @@ namespace garlic
return size;
}
size_t GarlicRoutingSession::CreateDeliveryStatusClove (uint8_t * buf, uint32_t msgID)
size_t ElGamalAESSession::CreateDeliveryStatusClove (uint8_t * buf, uint32_t msgID)
{
size_t size = 0;
if (m_Owner)
if (GetOwner ())
{
auto inboundTunnel = m_Owner->GetTunnelPool ()->GetNextInboundTunnel ();
auto inboundTunnel = GetOwner ()->GetTunnelPool ()->GetNextInboundTunnel ();
if (inboundTunnel)
{
buf[size] = eGarlicDeliveryTypeTunnel << 5; // delivery instructions flag tunnel
@@ -359,19 +327,12 @@ namespace garlic
htobe32buf (buf + size, inboundTunnel->GetNextTunnelID ()); // tunnelID
size += 4;
// create msg
auto msg = CreateDeliveryStatusMsg (msgID);
if (m_Owner)
{
//encrypt
uint8_t key[32], tag[32];
RAND_bytes (key, 32); // random session key
RAND_bytes (tag, 32); // random session tag
m_Owner->SubmitSessionKey (key, tag);
GarlicRoutingSession garlic (key, tag);
msg = garlic.WrapSingleMessage (msg);
}
memcpy (buf + size, msg->GetBuffer (), msg->GetLength ());
size += msg->GetLength ();
auto msg = CreateEncryptedDeliveryStatusMsg (msgID);
if (msg)
{
memcpy (buf + size, msg->GetBuffer (), msg->GetLength ());
size += msg->GetLength ();
}
// fill clove
uint64_t ts = i2p::util::GetMillisecondsSinceEpoch () + 8000; // 8 sec
uint32_t cloveID;
@@ -392,6 +353,77 @@ namespace garlic
return size;
}
ElGamalAESSession::UnconfirmedTags * ElGamalAESSession::GenerateSessionTags ()
{
auto tags = new UnconfirmedTags (m_NumTags);
tags->tagsCreationTime = i2p::util::GetSecondsSinceEpoch ();
for (int i = 0; i < m_NumTags; i++)
{
RAND_bytes (tags->sessionTags[i], 32);
tags->sessionTags[i].creationTime = tags->tagsCreationTime;
}
return tags;
}
bool ElGamalAESSession::MessageConfirmed (uint32_t msgID)
{
TagsConfirmed (msgID);
if (!GarlicRoutingSession::MessageConfirmed (msgID))
CleanupExpiredTags ();
return true;
}
void ElGamalAESSession::TagsConfirmed (uint32_t msgID)
{
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
auto it = m_UnconfirmedTagsMsgs.find (msgID);
if (it != m_UnconfirmedTagsMsgs.end ())
{
auto& tags = it->second;
if (ts < tags->tagsCreationTime + OUTGOING_TAGS_EXPIRATION_TIMEOUT)
{
for (int i = 0; i < tags->numTags; i++)
m_SessionTags.push_back (tags->sessionTags[i]);
}
m_UnconfirmedTagsMsgs.erase (it);
}
}
bool ElGamalAESSession::CleanupExpiredTags ()
{
auto ts = i2p::util::GetSecondsSinceEpoch ();
for (auto it = m_SessionTags.begin (); it != m_SessionTags.end ();)
{
if (ts >= it->creationTime + OUTGOING_TAGS_EXPIRATION_TIMEOUT)
it = m_SessionTags.erase (it);
else
++it;
}
CleanupUnconfirmedTags ();
CleanupUnconfirmedLeaseSet (ts);
return !m_SessionTags.empty () || !m_UnconfirmedTagsMsgs.empty ();
}
bool ElGamalAESSession::CleanupUnconfirmedTags ()
{
bool ret = false;
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
// delete expired unconfirmed tags
for (auto it = m_UnconfirmedTagsMsgs.begin (); it != m_UnconfirmedTagsMsgs.end ();)
{
if (ts >= it->second->tagsCreationTime + OUTGOING_TAGS_CONFIRMATION_TIMEOUT)
{
if (GetOwner ())
GetOwner ()->RemoveDeliveryStatusSession (it->first);
it = m_UnconfirmedTagsMsgs.erase (it);
ret = true;
}
else
++it;
}
return ret;
}
GarlicDestination::GarlicDestination (): m_NumTags (32) // 32 tags by default
{
m_Ctx = BN_CTX_new ();
@@ -407,6 +439,8 @@ namespace garlic
m_Sessions.clear ();
m_DeliveryStatusSessions.clear ();
m_Tags.clear ();
m_ECIESx25519Sessions.clear ();
m_ECIESx25519Tags.clear ();
}
void GarlicDestination::AddSessionKey (const uint8_t * key, const uint8_t * tag)
{
@@ -434,6 +468,7 @@ namespace garlic
}
buf += 4; // length
auto it = m_Tags.find (SessionTag(buf));
// AES tag might be used even if encryption type is not ElGamal/AES
if (it != m_Tags.end ())
{
// tag found. Use AES
@@ -452,9 +487,15 @@ namespace garlic
}
else
{
// tag not found. Use ElGamal
// tag not found. Handle depending on encryption type
if (SupportsEncryptionType (i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RARCHET))
{
HandleECIESx25519 (buf, length);
return;
}
// otherwise assume ElGamal/AES
ElGamalBlock elGamal;
if (length >= 514 && Decrypt (buf, (uint8_t *)&elGamal, m_Ctx))
if (length >= 514 && Decrypt (buf, (uint8_t *)&elGamal, m_Ctx, i2p::data::CRYPTO_KEY_TYPE_ELGAMAL))
{
auto decryption = std::make_shared<AESDecryption>(elGamal.sessionKey);
uint8_t iv[32]; // IV is first 16 bytes
@@ -543,7 +584,7 @@ namespace garlic
LogPrint (eLogError, "Garlic: message is too short");
break;
}
HandleI2NPMessage (buf, len - offset, from);
HandleI2NPMessage (buf, len - offset);
break;
case eGarlicDeliveryTypeDestination:
LogPrint (eLogDebug, "Garlic: type destination");
@@ -554,7 +595,7 @@ namespace garlic
LogPrint (eLogError, "Garlic: message is too short");
break;
}
HandleI2NPMessage (buf, len - offset, from);
HandleI2NPMessage (buf, len - offset);
break;
case eGarlicDeliveryTypeTunnel:
{
@@ -638,21 +679,41 @@ namespace garlic
std::shared_ptr<GarlicRoutingSession> GarlicDestination::GetRoutingSession (
std::shared_ptr<const i2p::data::RoutingDestination> destination, bool attachLeaseSet)
{
GarlicRoutingSessionPtr session;
{
std::unique_lock<std::mutex> l(m_SessionsMutex);
auto it = m_Sessions.find (destination->GetIdentHash ());
if (it != m_Sessions.end ())
session = it->second;
}
if (!session)
{
session = std::make_shared<GarlicRoutingSession> (this, destination,
attachLeaseSet ? m_NumTags : 4, attachLeaseSet); // specified num tags for connections and 4 for LS requests
std::unique_lock<std::mutex> l(m_SessionsMutex);
m_Sessions[destination->GetIdentHash ()] = session;
}
return session;
if (destination->GetEncryptionType () == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RARCHET &&
SupportsEncryptionType (i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RARCHET))
{
ECIESX25519AEADRatchetSessionPtr session;
uint8_t staticKey[32];
destination->Encrypt (nullptr, staticKey, nullptr); // we are supposed to get static key
auto it = m_ECIESx25519Sessions.find (staticKey);
if (it != m_ECIESx25519Sessions.end ())
session = it->second;
if (!session)
{
session = std::make_shared<ECIESX25519AEADRatchetSession> (this);
session->SetRemoteStaticKey (staticKey);
}
session->SetDestination (destination->GetIdentHash ()); // TODO: remove
return session;
}
else
{
ElGamalAESSessionPtr session;
{
std::unique_lock<std::mutex> l(m_SessionsMutex);
auto it = m_Sessions.find (destination->GetIdentHash ());
if (it != m_Sessions.end ())
session = it->second;
}
if (!session)
{
session = std::make_shared<ElGamalAESSession> (this, destination,
attachLeaseSet ? m_NumTags : 4, attachLeaseSet); // specified num tags for connections and 4 for LS requests
std::unique_lock<std::mutex> l(m_SessionsMutex);
m_Sessions[destination->GetIdentHash ()] = session;
}
return session;
}
}
void GarlicDestination::CleanupExpiredTags ()
@@ -700,6 +761,25 @@ namespace garlic
++it;
}
}
// ECIESx25519
for (auto it = m_ECIESx25519Tags.begin (); it != m_ECIESx25519Tags.end ();)
{
if (ts > it->second.creationTime + INCOMING_TAGS_EXPIRATION_TIMEOUT)
it = m_ECIESx25519Tags.erase (it);
else
++it;
}
for (auto it = m_ECIESx25519Sessions.begin (); it != m_ECIESx25519Sessions.end ();)
{
if (it->second->CheckExpired (ts))
{
it->second->SetOwner (nullptr);
it = m_ECIESx25519Sessions.erase (it);
}
else
++it;
}
}
void GarlicDestination::RemoveDeliveryStatusSession (uint32_t msgID)
@@ -714,9 +794,8 @@ namespace garlic
m_DeliveryStatusSessions[msgID] = session;
}
void GarlicDestination::HandleDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg)
void GarlicDestination::HandleDeliveryStatusMessage (uint32_t msgID)
{
uint32_t msgID = bufbe32toh (msg->GetPayload ());
GarlicRoutingSessionPtr session;
{
std::unique_lock<std::mutex> l(m_DeliveryStatusSessionsMutex);
@@ -736,8 +815,12 @@ namespace garlic
void GarlicDestination::SetLeaseSetUpdated ()
{
std::unique_lock<std::mutex> l(m_SessionsMutex);
for (auto& it: m_Sessions)
{
std::unique_lock<std::mutex> l(m_SessionsMutex);
for (auto& it: m_Sessions)
it.second->SetLeaseSetUpdated ();
}
for (auto& it: m_ECIESx25519Sessions)
it.second->SetLeaseSetUpdated ();
}
@@ -748,7 +831,8 @@ namespace garlic
void GarlicDestination::ProcessDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg)
{
HandleDeliveryStatusMessage (msg);
uint32_t msgID = bufbe32toh (msg->GetPayload () + DELIVERY_STATUS_MSGID_OFFSET);
HandleDeliveryStatusMessage (msgID);
}
void GarlicDestination::SaveTags ()
@@ -806,7 +890,7 @@ namespace garlic
m_Tags.insert (std::make_pair (SessionTag (tag, ts), decryption));
}
if (!m_Tags.empty ())
LogPrint (eLogInfo, m_Tags.size (), " loaded for ", ident);
LogPrint (eLogInfo, "Garlic: ", m_Tags.size (), " tags loaded for ", ident);
}
}
i2p::fs::Remove (path);
@@ -821,5 +905,107 @@ namespace garlic
if (ts >= i2p::fs::GetLastUpdateTime (it) + INCOMING_TAGS_EXPIRATION_TIMEOUT)
i2p::fs::Remove (it);
}
void GarlicDestination::HandleECIESx25519 (const uint8_t * buf, size_t len)
{
uint64_t tag;
memcpy (&tag, buf, 8);
ECIESX25519AEADRatchetSessionPtr session;
int index = 0;
auto it = m_ECIESx25519Tags.find (tag);
if (it != m_ECIESx25519Tags.end ())
{
session = it->second.session;
index = it->second.index;
m_ECIESx25519Tags.erase (tag);
}
else
session = std::make_shared<ECIESX25519AEADRatchetSession> (this); // incoming
if (!session->HandleNextMessage (buf, len, index))
LogPrint (eLogError, "Garlic: can't handle ECIES-X25519-AEAD-Ratchet message");
}
void GarlicDestination::HandleECIESx25519GarlicClove (const uint8_t * buf, size_t len)
{
const uint8_t * buf1 = buf;
uint8_t flag = buf[0]; buf++; // flag
GarlicDeliveryType deliveryType = (GarlicDeliveryType)((flag >> 5) & 0x03);
switch (deliveryType)
{
case eGarlicDeliveryTypeDestination:
LogPrint (eLogDebug, "Garlic: type destination");
buf += 32; // TODO: check destination
#if (__cplusplus >= 201703L) // C++ 17 or higher
[[fallthrough]];
#endif
// no break here
case eGarlicDeliveryTypeLocal:
{
LogPrint (eLogDebug, "Garlic: type local");
I2NPMessageType typeID = (I2NPMessageType)(buf[0]); buf++; // typeid
buf += (4 + 4); // msgID + expiration
ptrdiff_t offset = buf - buf1;
if (offset <= (int)len)
HandleCloveI2NPMessage (typeID, buf, len - offset);
else
LogPrint (eLogError, "Garlic: clove is too long");
break;
}
case eGarlicDeliveryTypeTunnel:
{
LogPrint (eLogDebug, "Garlic: type tunnel");
// gwHash and gwTunnel sequence is reverted
const uint8_t * gwHash = buf;
buf += 32;
ptrdiff_t offset = buf - buf1;
if (offset + 13 > (int)len)
{
LogPrint (eLogError, "Garlic: message is too short");
break;
}
uint32_t gwTunnel = bufbe32toh (buf); buf += 4;
I2NPMessageType typeID = (I2NPMessageType)(buf[0]); buf++; // typeid
buf += (4 + 4); // msgID + expiration
offset += 13;
if (GetTunnelPool ())
{
auto tunnel = GetTunnelPool ()->GetNextOutboundTunnel ();
if (tunnel)
tunnel->SendTunnelDataMsg (gwHash, gwTunnel, CreateI2NPMessage (typeID, buf, len - offset));
else
LogPrint (eLogWarning, "Garlic: No outbound tunnels available for garlic clove");
}
else
LogPrint (eLogError, "Garlic: Tunnel pool is not set for inbound tunnel");
break;
}
default:
LogPrint (eLogWarning, "Garlic: unexpected delivery type ", (int)deliveryType);
}
}
void GarlicDestination::AddECIESx25519SessionTag (int index, uint64_t tag, ECIESX25519AEADRatchetSessionPtr session)
{
m_ECIESx25519Tags.emplace (tag, ECIESX25519AEADRatchetIndexSession{index, session, i2p::util::GetSecondsSinceEpoch ()});
}
void GarlicDestination::AddECIESx25519Session (const uint8_t * staticKey, ECIESX25519AEADRatchetSessionPtr session)
{
i2p::data::Tag<32> staticKeyTag (staticKey);
auto it = m_ECIESx25519Sessions.find (staticKeyTag);
if (it != m_ECIESx25519Sessions.end ())
{
if (it->second->CanBeRestarted (i2p::util::GetSecondsSinceEpoch ()))
m_ECIESx25519Sessions.erase (it);
else
{
LogPrint (eLogInfo, "Garlic: ECIESx25519 session with static key ", staticKeyTag.ToBase64 (), " already exists");
return;
}
}
m_ECIESx25519Sessions.emplace (staticKeyTag, session);
}
}
}

View File

@@ -2,7 +2,7 @@
#define GARLIC_H__
#include <inttypes.h>
#include <map>
#include <unordered_map>
#include <list>
#include <string>
#include <thread>
@@ -85,8 +85,10 @@ namespace garlic
};
class GarlicDestination;
class GarlicRoutingSession: public std::enable_shared_from_this<GarlicRoutingSession>
class GarlicRoutingSession
{
protected:
enum LeaseSetUpdateStatus
{
eLeaseSetUpToDate = 0,
@@ -95,7 +97,60 @@ namespace garlic
eLeaseSetDoNotSend
};
struct UnconfirmedTags
public:
GarlicRoutingSession (GarlicDestination * owner, bool attachLeaseSet);
GarlicRoutingSession ();
virtual ~GarlicRoutingSession ();
virtual std::shared_ptr<I2NPMessage> WrapSingleMessage (std::shared_ptr<const I2NPMessage> msg) = 0;
virtual bool CleanupUnconfirmedTags () { return false; }; // for I2CP, override in ElGamalAESSession
virtual bool MessageConfirmed (uint32_t msgID);
void SetLeaseSetUpdated ()
{
if (m_LeaseSetUpdateStatus != eLeaseSetDoNotSend) m_LeaseSetUpdateStatus = eLeaseSetUpdated;
};
bool IsLeaseSetNonConfirmed () const { return m_LeaseSetUpdateStatus == eLeaseSetSubmitted; };
bool IsLeaseSetUpdated () const { return m_LeaseSetUpdateStatus == eLeaseSetUpdated; };
uint64_t GetLeaseSetSubmissionTime () const { return m_LeaseSetSubmissionTime; }
void CleanupUnconfirmedLeaseSet (uint64_t ts);
std::shared_ptr<GarlicRoutingPath> GetSharedRoutingPath ();
void SetSharedRoutingPath (std::shared_ptr<GarlicRoutingPath> path);
GarlicDestination * GetOwner () const { return m_Owner; }
void SetOwner (GarlicDestination * owner) { m_Owner = owner; }
protected:
LeaseSetUpdateStatus GetLeaseSetUpdateStatus () const { return m_LeaseSetUpdateStatus; }
void SetLeaseSetUpdateStatus (LeaseSetUpdateStatus status) { m_LeaseSetUpdateStatus = status; }
uint32_t GetLeaseSetUpdateMsgID () const { return m_LeaseSetUpdateMsgID; }
void SetLeaseSetUpdateMsgID (uint32_t msgID) { m_LeaseSetUpdateMsgID = msgID; }
void SetLeaseSetSubmissionTime (uint64_t ts) { m_LeaseSetSubmissionTime = ts; }
std::shared_ptr<I2NPMessage> CreateEncryptedDeliveryStatusMsg (uint32_t msgID);
private:
GarlicDestination * m_Owner;
LeaseSetUpdateStatus m_LeaseSetUpdateStatus;
uint32_t m_LeaseSetUpdateMsgID;
uint64_t m_LeaseSetSubmissionTime; // in milliseconds
std::shared_ptr<GarlicRoutingPath> m_SharedRoutingPath;
public:
// for HTTP only
virtual size_t GetNumOutgoingTags () const { return 0; };
};
//using GarlicRoutingSessionPtr = std::shared_ptr<GarlicRoutingSession>;
typedef std::shared_ptr<GarlicRoutingSession> GarlicRoutingSessionPtr; // TODO: replace to using after switch to 4.8
class ElGamalAESSession: public GarlicRoutingSession, public std::enable_shared_from_this<ElGamalAESSession>
{
struct UnconfirmedTags
{
UnconfirmedTags (int n): numTags (n), tagsCreationTime (0) { sessionTags = new SessionTag[numTags]; };
~UnconfirmedTags () { delete[] sessionTags; };
@@ -105,32 +160,20 @@ namespace garlic
uint32_t tagsCreationTime;
};
public:
public:
GarlicRoutingSession (GarlicDestination * owner, std::shared_ptr<const i2p::data::RoutingDestination> destination,
ElGamalAESSession (GarlicDestination * owner, std::shared_ptr<const i2p::data::RoutingDestination> destination,
int numTags, bool attachLeaseSet);
GarlicRoutingSession (const uint8_t * sessionKey, const SessionTag& sessionTag); // one time encryption
~GarlicRoutingSession ();
std::shared_ptr<I2NPMessage> WrapSingleMessage (std::shared_ptr<const I2NPMessage> msg);
void MessageConfirmed (uint32_t msgID);
ElGamalAESSession (const uint8_t * sessionKey, const SessionTag& sessionTag); // one time encryption
~ElGamalAESSession () {};
std::shared_ptr<I2NPMessage> WrapSingleMessage (std::shared_ptr<const I2NPMessage> msg);
bool MessageConfirmed (uint32_t msgID);
bool CleanupExpiredTags (); // returns true if something left
bool CleanupUnconfirmedTags (); // returns true if something has been deleted
void SetLeaseSetUpdated ()
{
if (m_LeaseSetUpdateStatus != eLeaseSetDoNotSend) m_LeaseSetUpdateStatus = eLeaseSetUpdated;
};
bool IsLeaseSetNonConfirmed () const { return m_LeaseSetUpdateStatus == eLeaseSetSubmitted; };
bool IsLeaseSetUpdated () const { return m_LeaseSetUpdateStatus == eLeaseSetUpdated; };
uint64_t GetLeaseSetSubmissionTime () const { return m_LeaseSetSubmissionTime; }
std::shared_ptr<GarlicRoutingPath> GetSharedRoutingPath ();
void SetSharedRoutingPath (std::shared_ptr<GarlicRoutingPath> path);
const GarlicDestination * GetOwner () const { return m_Owner; }
void SetOwner (GarlicDestination * owner) { m_Owner = owner; }
private:
private:
size_t CreateAESBlock (uint8_t * buf, std::shared_ptr<const I2NPMessage> msg);
size_t CreateGarlicPayload (uint8_t * payload, std::shared_ptr<const I2NPMessage> msg, UnconfirmedTags * newTags);
@@ -139,31 +182,32 @@ namespace garlic
void TagsConfirmed (uint32_t msgID);
UnconfirmedTags * GenerateSessionTags ();
private:
std::shared_ptr<const i2p::data::RoutingDestination> m_Destination;
private:
GarlicDestination * m_Owner;
std::shared_ptr<const i2p::data::RoutingDestination> m_Destination;
i2p::crypto::AESKey m_SessionKey;
i2p::crypto::AESKey m_SessionKey;
std::list<SessionTag> m_SessionTags;
int m_NumTags;
std::map<uint32_t, std::unique_ptr<UnconfirmedTags> > m_UnconfirmedTagsMsgs; // msgID->tags
LeaseSetUpdateStatus m_LeaseSetUpdateStatus;
uint32_t m_LeaseSetUpdateMsgID;
uint64_t m_LeaseSetSubmissionTime; // in milliseconds
i2p::crypto::CBCEncryption m_Encryption;
i2p::crypto::CBCEncryption m_Encryption;
std::shared_ptr<GarlicRoutingPath> m_SharedRoutingPath;
public:
public:
// for HTTP only
size_t GetNumOutgoingTags () const { return m_SessionTags.size (); };
size_t GetNumOutgoingTags () const { return m_SessionTags.size (); };
};
typedef std::shared_ptr<ElGamalAESSession> ElGamalAESSessionPtr;
class ECIESX25519AEADRatchetSession;
typedef std::shared_ptr<ECIESX25519AEADRatchetSession> ECIESX25519AEADRatchetSessionPtr;
struct ECIESX25519AEADRatchetIndexSession
{
int index;
ECIESX25519AEADRatchetSessionPtr session;
uint64_t creationTime; // seconds since epoch
};
//using GarlicRoutingSessionPtr = std::shared_ptr<GarlicRoutingSession>;
typedef std::shared_ptr<GarlicRoutingSession> GarlicRoutingSessionPtr; // TODO: replace to using after switch to 4.8
class GarlicDestination: public i2p::data::LocalDestination
{
@@ -174,6 +218,7 @@ namespace garlic
void CleanUp ();
void SetNumTags (int numTags) { m_NumTags = numTags; };
int GetNumTags () const { return m_NumTags; };
std::shared_ptr<GarlicRoutingSession> GetRoutingSession (std::shared_ptr<const i2p::data::RoutingDestination> destination, bool attachLeaseSet);
void CleanupExpiredTags ();
void RemoveDeliveryStatusSession (uint32_t msgID);
@@ -183,6 +228,9 @@ namespace garlic
void AddSessionKey (const uint8_t * key, const uint8_t * tag); // one tag
virtual bool SubmitSessionKey (const uint8_t * key, const uint8_t * tag); // from different thread
void DeliveryStatusSent (GarlicRoutingSessionPtr session, uint32_t msgID);
void AddECIESx25519SessionTag (int index, uint64_t tag, ECIESX25519AEADRatchetSessionPtr session);
void AddECIESx25519Session (const uint8_t * staticKey, ECIESX25519AEADRatchetSessionPtr session);
void HandleECIESx25519GarlicClove (const uint8_t * buf, size_t len);
virtual void ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg);
virtual void ProcessDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg);
@@ -190,12 +238,13 @@ namespace garlic
virtual std::shared_ptr<const i2p::data::LocalLeaseSet> GetLeaseSet () = 0; // TODO
virtual std::shared_ptr<i2p::tunnel::TunnelPool> GetTunnelPool () const = 0;
virtual void HandleI2NPMessage (const uint8_t * buf, size_t len, std::shared_ptr<i2p::tunnel::InboundTunnel> from) = 0;
protected:
virtual void HandleI2NPMessage (const uint8_t * buf, size_t len) = 0; // called from clove only
virtual bool HandleCloveI2NPMessage (I2NPMessageType typeID, const uint8_t * payload, size_t len) = 0;
void HandleGarlicMessage (std::shared_ptr<I2NPMessage> msg);
void HandleDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg);
void HandleDeliveryStatusMessage (uint32_t msgID);
void SaveTags ();
void LoadTags ();
@@ -206,18 +255,23 @@ namespace garlic
std::shared_ptr<i2p::tunnel::InboundTunnel> from);
void HandleGarlicPayload (uint8_t * buf, size_t len, std::shared_ptr<i2p::tunnel::InboundTunnel> from);
// ECIES-X25519-AEAD-Ratchet
void HandleECIESx25519 (const uint8_t * buf, size_t len);
private:
BN_CTX * m_Ctx; // incoming
// outgoing sessions
int m_NumTags;
std::mutex m_SessionsMutex;
std::map<i2p::data::IdentHash, GarlicRoutingSessionPtr> m_Sessions;
std::unordered_map<i2p::data::IdentHash, ElGamalAESSessionPtr> m_Sessions;
std::unordered_map<i2p::data::Tag<32>, ECIESX25519AEADRatchetSessionPtr> m_ECIESx25519Sessions; // static key -> session
// incoming
std::map<SessionTag, std::shared_ptr<AESDecryption> > m_Tags;
std::unordered_map<SessionTag, std::shared_ptr<AESDecryption>, std::hash<i2p::data::Tag<32> > > m_Tags;
std::unordered_map<uint64_t, ECIESX25519AEADRatchetIndexSession> m_ECIESx25519Tags; // session tag -> session
// DeliveryStatus
std::mutex m_DeliveryStatusSessionsMutex;
std::map<uint32_t, GarlicRoutingSessionPtr> m_DeliveryStatusSessions; // msgID -> session
std::unordered_map<uint32_t, GarlicRoutingSessionPtr> m_DeliveryStatusSessions; // msgID -> session
public:

View File

@@ -258,12 +258,12 @@ namespace i2p
return m;
}
std::shared_ptr<I2NPMessage> CreateDatabaseStoreMsg (std::shared_ptr<const i2p::data::LeaseSet> leaseSet)
std::shared_ptr<I2NPMessage> CreateDatabaseStoreMsg (const i2p::data::IdentHash& storeHash, std::shared_ptr<const i2p::data::LeaseSet> leaseSet)
{
if (!leaseSet) return nullptr;
auto m = NewI2NPShortMessage ();
uint8_t * payload = m->GetPayload ();
memcpy (payload + DATABASE_STORE_KEY_OFFSET, leaseSet->GetIdentHash (), 32);
memcpy (payload + DATABASE_STORE_KEY_OFFSET, storeHash, 32);
payload[DATABASE_STORE_TYPE_OFFSET] = leaseSet->GetStoreType (); // 1 for LeaseSet
htobe32buf (payload + DATABASE_STORE_REPLY_TOKEN_OFFSET, 0);
size_t size = DATABASE_STORE_HEADER_SIZE;

View File

@@ -95,6 +95,7 @@ namespace i2p
// DatabaseLookup flags
const uint8_t DATABASE_LOOKUP_DELIVERY_FLAG = 0x01;
const uint8_t DATABASE_LOOKUP_ENCRYPTION_FLAG = 0x02;
const uint8_t DATABASE_LOOKUP_ECIES_FLAG = 0x10;
const uint8_t DATABASE_LOOKUP_TYPE_FLAGS_MASK = 0x0C;
const uint8_t DATABASE_LOOKUP_TYPE_NORMAL_LOOKUP = 0;
const uint8_t DATABASE_LOOKUP_TYPE_LEASESET_LOOKUP = 0x04; // 0100
@@ -247,7 +248,7 @@ namespace tunnel
std::shared_ptr<I2NPMessage> CreateDatabaseSearchReply (const i2p::data::IdentHash& ident, std::vector<i2p::data::IdentHash> routers);
std::shared_ptr<I2NPMessage> CreateDatabaseStoreMsg (std::shared_ptr<const i2p::data::RouterInfo> router = nullptr, uint32_t replyToken = 0);
std::shared_ptr<I2NPMessage> CreateDatabaseStoreMsg (std::shared_ptr<const i2p::data::LeaseSet> leaseSet); // for floodfill only
std::shared_ptr<I2NPMessage> CreateDatabaseStoreMsg (const i2p::data::IdentHash& storeHash, std::shared_ptr<const i2p::data::LeaseSet> leaseSet); // for floodfill only
std::shared_ptr<I2NPMessage> CreateDatabaseStoreMsg (std::shared_ptr<const i2p::data::LocalLeaseSet> leaseSet, uint32_t replyToken = 0, std::shared_ptr<const i2p::tunnel::InboundTunnel> replyTunnel = nullptr);
bool IsRouterInfoMsg (std::shared_ptr<I2NPMessage> msg);

View File

@@ -34,12 +34,11 @@ namespace data
}
IdentityEx::IdentityEx ():
m_IsVerifierCreated (false), m_ExtendedLen (0), m_ExtendedBuffer (nullptr)
m_ExtendedLen (0), m_ExtendedBuffer (nullptr)
{
}
IdentityEx::IdentityEx(const uint8_t * publicKey, const uint8_t * signingKey, SigningKeyType type, CryptoKeyType cryptoType):
m_IsVerifierCreated (false)
IdentityEx::IdentityEx(const uint8_t * publicKey, const uint8_t * signingKey, SigningKeyType type, CryptoKeyType cryptoType)
{
memcpy (m_StandardIdentity.publicKey, publicKey, 256); // publicKey in awlays assumed 256 regardless actual size, padding must be taken care of
if (type != SIGNING_KEY_TYPE_DSA_SHA1)
@@ -141,19 +140,19 @@ namespace data
}
IdentityEx::IdentityEx (const uint8_t * buf, size_t len):
m_IsVerifierCreated (false), m_ExtendedLen (0), m_ExtendedBuffer (nullptr)
m_ExtendedLen (0), m_ExtendedBuffer (nullptr)
{
FromBuffer (buf, len);
}
IdentityEx::IdentityEx (const IdentityEx& other):
m_IsVerifierCreated (false), m_ExtendedLen (0), m_ExtendedBuffer (nullptr)
m_ExtendedLen (0), m_ExtendedBuffer (nullptr)
{
*this = other;
}
IdentityEx::IdentityEx (const Identity& standard):
m_IsVerifierCreated (false), m_ExtendedLen (0), m_ExtendedBuffer (nullptr)
m_ExtendedLen (0), m_ExtendedBuffer (nullptr)
{
*this = standard;
}
@@ -161,6 +160,7 @@ namespace data
IdentityEx::~IdentityEx ()
{
delete[] m_ExtendedBuffer;
delete m_Verifier;
}
IdentityEx& IdentityEx::operator=(const IdentityEx& other)
@@ -178,8 +178,8 @@ namespace data
else
m_ExtendedBuffer = nullptr;
delete m_Verifier;
m_Verifier = nullptr;
m_IsVerifierCreated = false;
return *this;
}
@@ -193,8 +193,8 @@ namespace data
m_ExtendedBuffer = nullptr;
m_ExtendedLen = 0;
delete m_Verifier;
m_Verifier = nullptr;
m_IsVerifierCreated = false;
return *this;
}
@@ -233,6 +233,7 @@ namespace data
}
SHA256(buf, GetFullLen (), m_IdentHash);
delete m_Verifier;
m_Verifier = nullptr;
return GetFullLen ();
@@ -381,33 +382,27 @@ namespace data
void IdentityEx::UpdateVerifier (i2p::crypto::Verifier * verifier) const
{
if (!m_Verifier)
bool del = false;
{
auto created = m_IsVerifierCreated.exchange (true);
if (!created)
m_Verifier.reset (verifier);
std::lock_guard<std::mutex> l(m_VerifierMutex);
if (!m_Verifier)
m_Verifier = verifier;
else
{
delete verifier;
int count = 0;
while (!m_Verifier && count < 500) // 5 seconds
{
std::this_thread::sleep_for (std::chrono::milliseconds(10));
count++;
}
if (!m_Verifier)
LogPrint (eLogError, "Identity: couldn't get verifier in 5 seconds");
}
del = true;
}
else
if (del)
delete verifier;
}
void IdentityEx::DropVerifier () const
{
// TODO: potential race condition with Verify
m_IsVerifierCreated = false;
m_Verifier = nullptr;
i2p::crypto::Verifier * verifier;
{
std::lock_guard<std::mutex> l(m_VerifierMutex);
verifier = m_Verifier;
m_Verifier = nullptr;
}
delete verifier;
}
std::shared_ptr<i2p::crypto::CryptoKeyEncryptor> IdentityEx::CreateEncryptor (CryptoKeyType keyType, const uint8_t * key)
@@ -417,6 +412,9 @@ namespace data
case CRYPTO_KEY_TYPE_ELGAMAL:
return std::make_shared<i2p::crypto::ElGamalEncryptor>(key);
break;
case CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RARCHET:
return std::make_shared<i2p::crypto::ECIESX25519AEADRatchetEncryptor>(key);
break;
case CRYPTO_KEY_TYPE_ECIES_P256_SHA256_AES256CBC:
case CRYPTO_KEY_TYPE_ECIES_P256_SHA256_AES256CBC_TEST:
return std::make_shared<i2p::crypto::ECIESP256Encryptor>(key);
@@ -674,6 +672,9 @@ namespace data
case CRYPTO_KEY_TYPE_ECIES_GOSTR3410_CRYPTO_PRO_A_SHA256_AES256CBC:
return std::make_shared<i2p::crypto::ECIESGOSTR3410Decryptor>(key);
break;
case CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RARCHET:
return std::make_shared<i2p::crypto::ECIESX25519AEADRatchetDecryptor>(key);
break;
default:
LogPrint (eLogError, "Identity: Unknown crypto key type ", (int)cryptoType);
};
@@ -717,7 +718,10 @@ namespace data
case SIGNING_KEY_TYPE_RSA_SHA384_3072:
case SIGNING_KEY_TYPE_RSA_SHA512_4096:
LogPrint (eLogWarning, "Identity: RSA signature type is not supported. Creating EdDSA");
// no break here
#if (__cplusplus >= 201703L) // C++ 17 or higher
[[fallthrough]];
#endif
// no break here
case SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519:
i2p::crypto::CreateEDDSA25519RandomKeys (priv, pub);
break;
@@ -750,6 +754,9 @@ namespace data
case CRYPTO_KEY_TYPE_ECIES_GOSTR3410_CRYPTO_PRO_A_SHA256_AES256CBC:
i2p::crypto::CreateECIESGOSTR3410RandomKeys (priv, pub);
break;
case CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RARCHET:
i2p::crypto::CreateECIESX25519AEADRatchetRandomKeys (priv, pub);
break;
default:
LogPrint (eLogError, "Identity: Crypto key type ", (int)type, " is not supported");
}

View File

@@ -7,6 +7,7 @@
#include <memory>
#include <atomic>
#include <vector>
#include <mutex>
#include "Base.h"
#include "Signature.h"
#include "CryptoKey.h"
@@ -55,6 +56,7 @@ namespace data
const uint16_t CRYPTO_KEY_TYPE_ELGAMAL = 0;
const uint16_t CRYPTO_KEY_TYPE_ECIES_P256_SHA256_AES256CBC = 1;
const uint16_t CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RARCHET = 4;
const uint16_t CRYPTO_KEY_TYPE_ECIES_P256_SHA256_AES256CBC_TEST = 65280; // TODO: remove later
const uint16_t CRYPTO_KEY_TYPE_ECIES_GOSTR3410_CRYPTO_PRO_A_SHA256_AES256CBC = 65281; // TODO: use GOST R 34.11 instead SHA256 and GOST 28147-89 instead AES
@@ -124,8 +126,8 @@ namespace data
Identity m_StandardIdentity;
IdentHash m_IdentHash;
mutable std::unique_ptr<i2p::crypto::Verifier> m_Verifier;
mutable std::atomic_bool m_IsVerifierCreated; // make sure we don't create twice
mutable i2p::crypto::Verifier * m_Verifier = nullptr;
mutable std::mutex m_VerifierMutex;
size_t m_ExtendedLen;
uint8_t * m_ExtendedBuffer;
};
@@ -215,6 +217,7 @@ namespace data
virtual bool IsDestination () const = 0; // for garlic
const IdentHash& GetIdentHash () const { return GetIdentity ()->GetIdentHash (); };
virtual CryptoKeyType GetEncryptionType () const { return GetIdentity ()->GetCryptoKeyType (); }; // override in LeaseSet2
};
class LocalDestination
@@ -222,10 +225,12 @@ namespace data
public:
virtual ~LocalDestination() {};
virtual bool Decrypt (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx) const = 0;
virtual bool Decrypt (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx, CryptoKeyType preferredCrypto = CRYPTO_KEY_TYPE_ELGAMAL) const = 0;
virtual std::shared_ptr<const IdentityEx> GetIdentity () const = 0;
const IdentHash& GetIdentHash () const { return GetIdentity ()->GetIdentHash (); };
virtual bool SupportsEncryptionType (CryptoKeyType keyType) const { return GetIdentity ()->GetCryptoKeyType () == keyType; }; // override for LeaseSet
virtual const uint8_t * GetEncryptionPublicKey (CryptoKeyType keyType) const { return GetIdentity ()->GetEncryptionPublicKey (); }; // override for LeaseSet
};
}
}

View File

@@ -159,7 +159,7 @@ namespace data
}
}
else
LogPrint (eLogWarning, "LeaseSet: Lease is expired already ");
LogPrint (eLogWarning, "LeaseSet: Lease is expired already");
}
uint64_t LeaseSet::ExtractTimestamp (const uint8_t * buf, size_t len) const
@@ -251,18 +251,26 @@ namespace data
memcpy (m_Buffer, buf, len);
}
LeaseSet2::LeaseSet2 (uint8_t storeType, const uint8_t * buf, size_t len, bool storeLeases):
LeaseSet (storeLeases), m_StoreType (storeType), m_OrigStoreType (storeType)
void LeaseSet::SetBufferLen (size_t len)
{
if (len <= m_BufferLen) m_BufferLen = len;
else
LogPrint (eLogError, "LeaseSet2: actual buffer size ", len , " exceeds full buffer size ", m_BufferLen);
}
LeaseSet2::LeaseSet2 (uint8_t storeType, const uint8_t * buf, size_t len, bool storeLeases, CryptoKeyType preferredCrypto):
LeaseSet (storeLeases), m_StoreType (storeType), m_EncryptionType (preferredCrypto)
{
SetBuffer (buf, len);
SetBuffer (buf, len);
if (storeType == NETDB_STORE_TYPE_ENCRYPTED_LEASESET2)
ReadFromBufferEncrypted (buf, len, nullptr, nullptr);
else
ReadFromBuffer (buf, len);
}
LeaseSet2::LeaseSet2 (const uint8_t * buf, size_t len, std::shared_ptr<const BlindedPublicKey> key, const uint8_t * secret):
LeaseSet (true), m_StoreType (NETDB_STORE_TYPE_ENCRYPTED_LEASESET2), m_OrigStoreType (NETDB_STORE_TYPE_ENCRYPTED_LEASESET2)
LeaseSet2::LeaseSet2 (const uint8_t * buf, size_t len, std::shared_ptr<const BlindedPublicKey> key,
const uint8_t * secret, CryptoKeyType preferredCrypto):
LeaseSet (true), m_StoreType (NETDB_STORE_TYPE_ENCRYPTED_LEASESET2), m_EncryptionType (preferredCrypto)
{
ReadFromBufferEncrypted (buf, len, key, secret);
}
@@ -302,6 +310,12 @@ namespace data
return;
}
}
if (flags & LEASESET2_FLAG_UNPUBLISHED_LEASESET) m_IsPublic = false;
if (flags & LEASESET2_FLAG_PUBLISHED_ENCRYPTED)
{
m_IsPublishedEncrypted = true;
m_IsPublic = true;
}
// type specific part
size_t s = 0;
switch (m_StoreType)
@@ -324,6 +338,8 @@ namespace data
VerifySignature (identity, buf, len, offset);
SetIsValid (verified);
}
offset += m_TransientVerifier ? m_TransientVerifier->GetSignatureLen () : identity->GetSignatureLen ();
SetBufferLen (offset);
}
template<typename Verifier>
@@ -349,7 +365,8 @@ namespace data
offset += propertiesLen; // skip for now. TODO: implement properties
if (offset + 1 >= len) return 0;
// key sections
uint16_t currentKeyType = 0;
CryptoKeyType preferredKeyType = m_EncryptionType;
bool preferredKeyFound = false;
int numKeySections = buf[offset]; offset++;
for (int i = 0; i < numKeySections; i++)
{
@@ -357,15 +374,15 @@ namespace data
if (offset + 2 >= len) return 0;
uint16_t encryptionKeyLen = bufbe16toh (buf + offset); offset += 2;
if (offset + encryptionKeyLen >= len) return 0;
if (IsStoreLeases ()) // create encryptor with leases only
if (IsStoreLeases () && !preferredKeyFound) // create encryptor with leases only
{
// we pick first valid key, higher key type has higher priority 4-1-0
// if two keys with of the same type, pick first
// we pick first valid key if preferred not found
auto encryptor = i2p::data::IdentityEx::CreateEncryptor (keyType, buf + offset);
if (encryptor && (!m_Encryptor || keyType > currentKeyType))
if (encryptor && (!m_Encryptor || keyType == preferredKeyType))
{
m_Encryptor = encryptor; // TODO: atomic
currentKeyType = keyType;
m_EncryptionType = keyType;
if (keyType == preferredKeyType) preferredKeyFound = true;
}
}
offset += encryptionKeyLen;
@@ -479,7 +496,7 @@ namespace data
}
else
{
LogPrint (eLogError, "LeaseSet2: Unexpected blinded key type ", blindedKeyType, " instread ", key->GetBlindedSigType ());
LogPrint (eLogError, "LeaseSet2: Unexpected blinded key type ", blindedKeyType, " instead ", key->GetBlindedSigType ());
return;
}
// outer key
@@ -506,7 +523,7 @@ namespace data
if (authDataLen > 0)
{
memcpy (innerInput + 32, subcredential, 36);
i2p::crypto::HKDF (outerPlainText.data () + 1, innerInput, 68, "ELS2_L2K", keys);
i2p::crypto::HKDF (outerPlainText.data () + 1 + authDataLen, innerInput, 68, "ELS2_L2K", keys);
}
else
// no authData presented, innerInput = subcredential || publishedTimestamp
@@ -529,42 +546,83 @@ namespace data
else
LogPrint (eLogError, "LeaseSet2: unexpected LeaseSet type ", (int)innerPlainText[0], " inside encrypted LeaseSet");
}
else
{
// we set actual length of encrypted buffer
offset += m_TransientVerifier ? m_TransientVerifier->GetSignatureLen () : blindedVerifier->GetSignatureLen ();
SetBufferLen (offset);
}
}
// helper for ExtractClientAuthData
static inline bool GetAuthCookie (const uint8_t * authClients, int numClients, const uint8_t * okm, uint8_t * authCookie)
{
// try to find clientCookie_i for clientID_i = okm[44:51]
for (int i = 0; i < numClients; i++)
{
if (!memcmp (okm + 44, authClients + i*40, 8)) // clientID_i
{
// clientKey_i = okm[0:31]
// clientIV_i = okm[32:43]
i2p::crypto::ChaCha20 (authClients + i*40 + 8, 32, okm, okm + 32, authCookie); // clientCookie_i
return true;
}
}
return false;
}
size_t LeaseSet2::ExtractClientAuthData (const uint8_t * buf, size_t len, const uint8_t * secret, const uint8_t * subcredential, uint8_t * authCookie) const
{
size_t offset = 0;
uint8_t flag = buf[offset]; offset++; // flag
if (flag & 0x01) // client auth
{
if (flag & 0x02) // PSK, bit 1 is set to 1
if (!(flag & 0x0E)) // DH, bit 1-3 all zeroes
{
const uint8_t * ephemeralPublicKey = buf + offset; offset += 32; // ephemeralPublicKey
uint16_t numClients = bufbe16toh (buf + offset); offset += 2; // clients
const uint8_t * authClients = buf + offset; offset += numClients*40; // authClients
if (offset > len)
{
LogPrint (eLogError, "LeaseSet2: Too many clients ", numClients, " in DH auth data");
return 0;
}
// calculate authCookie
if (secret)
{
i2p::crypto::X25519Keys ck (secret, nullptr); // derive cpk_i from csk_i
uint8_t authInput[100];
ck.Agree (ephemeralPublicKey, authInput); // sharedSecret is first 32 bytes of authInput
memcpy (authInput + 32, ck.GetPublicKey (), 32); // cpk_i
memcpy (authInput + 64, subcredential, 36);
uint8_t okm[64]; // 52 actual data
i2p::crypto::HKDF (ephemeralPublicKey, authInput, 100, "ELS2_XCA", okm);
if (!GetAuthCookie (authClients, numClients, okm, authCookie))
LogPrint (eLogError, "LeaseSet2: Client cookie DH not found");
}
else
LogPrint (eLogError, "LeaseSet2: Can't calculate authCookie: csk_i is not provided");
}
else if (flag & 0x02) // PSK, bit 1 is set to 1
{
const uint8_t * authSalt = buf + offset; offset += 32; // authSalt
uint16_t numClients = bufbe16toh (buf + offset); offset += 2; // clients
const uint8_t * authClients = buf + offset; offset += numClients*40; // authClients
if (offset > len)
{
LogPrint (eLogError, "LeaseSet2: Too many clients ", numClients, " in PSK auth data");
return 0;
}
// calculate authCookie
if (secret)
{
uint8_t authInput[68];
memcpy (authInput, secret, 32);
memcpy (authInput, subcredential, 36);
memcpy (authInput + 32, subcredential, 36);
uint8_t okm[64]; // 52 actual data
i2p::crypto::HKDF (authSalt, authInput, 68, "ELS2PSKA", okm);
// try to find clientCookie_i for clientID_i = okm[44:51]
bool found = false;
for (int i = 0; i < numClients; i++)
{
if (!memcmp (okm + 44, authClients + i*40, 8)) // clientID_i
{
// clientKey_i = okm[0:31]
// clientIV_i = okm[32:43]
i2p::crypto::ChaCha20 (authClients + i*40 + 8, 32, okm, okm + 32, authCookie); // clientCookie_i
found = true;
break;
}
}
if (!found)
LogPrint (eLogError, "LeaseSet2: Client cookie not found");
if (!GetAuthCookie (authClients, numClients, okm, authCookie))
LogPrint (eLogError, "LeaseSet2: Client cookie PSK not found");
}
else
LogPrint (eLogError, "LeaseSet2: Can't calculate authCookie: psk_i is not provided");
@@ -705,22 +763,32 @@ namespace data
}
LocalLeaseSet2::LocalLeaseSet2 (uint8_t storeType, const i2p::data::PrivateKeys& keys,
uint16_t keyType, uint16_t keyLen, const uint8_t * encryptionPublicKey,
std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> > tunnels):
const KeySections& encryptionKeys,
std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> > tunnels,
bool isPublic, bool isPublishedEncrypted):
LocalLeaseSet (keys.GetPublic (), nullptr, 0)
{
auto identity = keys.GetPublic ();
// assume standard LS2
int num = tunnels.size ();
if (num > MAX_NUM_LEASES) num = MAX_NUM_LEASES;
size_t keySectionsLen = 0;
for (const auto& it: encryptionKeys)
keySectionsLen += 2/*key type*/ + 2/*key len*/ + it.keyLen/*key*/;
m_BufferLen = identity->GetFullLen () + 4/*published*/ + 2/*expires*/ + 2/*flag*/ + 2/*properties len*/ +
1/*num keys*/ + 2/*key type*/ + 2/*key len*/ + keyLen/*key*/ + 1/*num leases*/ + num*LEASE2_SIZE + keys.GetSignatureLen ();
1/*num keys*/ + keySectionsLen + 1/*num leases*/ + num*LEASE2_SIZE + keys.GetSignatureLen ();
uint16_t flags = 0;
if (keys.IsOfflineSignature ())
{
flags |= LEASESET2_FLAG_OFFLINE_KEYS;
m_BufferLen += keys.GetOfflineSignature ().size ();
}
if (isPublishedEncrypted)
{
flags |= LEASESET2_FLAG_PUBLISHED_ENCRYPTED;
isPublic = true;
}
if (!isPublic) flags |= LEASESET2_FLAG_UNPUBLISHED_LEASESET;
m_Buffer = new uint8_t[m_BufferLen + 1];
m_Buffer[0] = storeType;
@@ -739,10 +807,13 @@ namespace data
}
htobe16buf (m_Buffer + offset, 0); offset += 2; // properties len
// keys
m_Buffer[offset] = 1; offset++; // 1 key
htobe16buf (m_Buffer + offset, keyType); offset += 2; // key type
htobe16buf (m_Buffer + offset, keyLen); offset += 2; // key len
memcpy (m_Buffer + offset, encryptionPublicKey, keyLen); offset += keyLen; // key
m_Buffer[offset] = encryptionKeys.size (); offset++; // 1 key
for (const auto& it: encryptionKeys)
{
htobe16buf (m_Buffer + offset, it.keyType); offset += 2; // key type
htobe16buf (m_Buffer + offset, it.keyLen); offset += 2; // key len
memcpy (m_Buffer + offset, it.encryptionPublicKey, it.keyLen); offset += it.keyLen; // key
}
// leases
uint32_t expirationTime = 0; // in seconds
m_Buffer[offset] = num; offset++; // num leases
@@ -774,12 +845,22 @@ namespace data
m_Buffer[0] = storeType;
}
LocalEncryptedLeaseSet2::LocalEncryptedLeaseSet2 (std::shared_ptr<const LocalLeaseSet2> ls, const i2p::data::PrivateKeys& keys, i2p::data::SigningKeyType blindedKeyType):
LocalEncryptedLeaseSet2::LocalEncryptedLeaseSet2 (std::shared_ptr<const LocalLeaseSet2> ls, const i2p::data::PrivateKeys& keys,
int authType, std::shared_ptr<std::vector<AuthPublicKey> > authKeys):
LocalLeaseSet2 (ls->GetIdentity ()), m_InnerLeaseSet (ls)
{
size_t lenInnerPlaintext = ls->GetBufferLen () + 1, lenOuterPlaintext = lenInnerPlaintext + 32 + 1,
lenOuterCiphertext = lenOuterPlaintext + 32;
m_BufferLen = 2/*blinded sig type*/ + 32/*blinded pub key*/ + 4/*published*/ + 2/*expires*/ + 2/*flags*/ + 2/*lenOuterCiphertext*/ + lenOuterCiphertext + 64/*signature*/;
size_t lenInnerPlaintext = ls->GetBufferLen () + 1, lenOuterPlaintext = lenInnerPlaintext + 32 + 1;
uint8_t layer1Flags = 0;
if (authKeys)
{
if (authType == ENCRYPTED_LEASESET_AUTH_TYPE_DH) layer1Flags |= 0x01; // DH, authentication scheme 0, auth bit 1
else if (authType == ENCRYPTED_LEASESET_AUTH_TYPE_PSK) layer1Flags |= 0x03; // PSK, authentication scheme 1, auth bit 1
if (layer1Flags)
lenOuterPlaintext += 32 + 2 + authKeys->size ()*40; // auth data len
}
size_t lenOuterCiphertext = lenOuterPlaintext + 32;
m_BufferLen = 2/*blinded sig type*/ + 32/*blinded pub key*/ + 4/*published*/ + 2/*expires*/ + 2/*flags*/ + 2/*lenOuterCiphertext*/ + lenOuterCiphertext + 64/*signature*/;
m_Buffer = new uint8_t[m_BufferLen + 1];
m_Buffer[0] = NETDB_STORE_TYPE_ENCRYPTED_LEASESET2;
BlindedPublicKey blindedKey (ls->GetIdentity ());
@@ -788,9 +869,9 @@ namespace data
i2p::util::GetDateString (timestamp, date);
uint8_t blindedPriv[64], blindedPub[128]; // 64 and 128 max
size_t publicKeyLen = blindedKey.BlindPrivateKey (keys.GetSigningPrivateKey (), date, blindedPriv, blindedPub);
std::unique_ptr<i2p::crypto::Signer> blindedSigner (i2p::data::PrivateKeys::CreateSigner (blindedKeyType, blindedPriv));
std::unique_ptr<i2p::crypto::Signer> blindedSigner (i2p::data::PrivateKeys::CreateSigner (blindedKey.GetBlindedSigType (), blindedPriv));
auto offset = 1;
htobe16buf (m_Buffer + offset, blindedKeyType); offset += 2; // Blinded Public Key Sig Type
htobe16buf (m_Buffer + offset, blindedKey.GetBlindedSigType ()); offset += 2; // Blinded Public Key Sig Type
memcpy (m_Buffer + offset, blindedPub, publicKeyLen); offset += publicKeyLen; // Blinded Public Key
htobe32buf (m_Buffer + offset, timestamp); offset += 4; // published timestamp (seconds)
auto nextMidnight = (timestamp/86400LL + 1)*86400LL; // 86400 = 24*3600 seconds
@@ -812,12 +893,26 @@ namespace data
i2p::crypto::HKDF (m_Buffer + offset, subcredential, 36, "ELS2_L1K", keys1);
offset += 32; // outerSalt
uint8_t * outerPlainText = m_Buffer + offset;
m_Buffer[offset] = 0; offset++; // flag
m_Buffer[offset] = layer1Flags; offset++; // layer 1 flags
// auth data
uint8_t innerInput[68]; // authCookie || subcredential || publishedTimestamp
if (layer1Flags)
{
RAND_bytes (innerInput, 32); // authCookie
CreateClientAuthData (subcredential, authType, authKeys, innerInput, m_Buffer + offset);
offset += 32 + 2 + authKeys->size ()*40; // auth clients
}
// Layer 2
// keys = HKDF(outerSalt, outerInput, "ELS2_L2K", 44)
uint8_t keys2[64]; // 44 bytes actual data
RAND_bytes (m_Buffer + offset, 32); // innerSalt = CSRNG(32)
i2p::crypto::HKDF (m_Buffer + offset, subcredential, 36, "ELS2_L2K", keys2);
if (layer1Flags)
{
memcpy (innerInput + 32, subcredential, 36); // + subcredential || publishedTimestamp
i2p::crypto::HKDF (m_Buffer + offset, innerInput, 68, "ELS2_L2K", keys2);
}
else
i2p::crypto::HKDF (m_Buffer + offset, subcredential, 36, "ELS2_L2K", keys2); // no authCookie
offset += 32; // innerSalt
m_Buffer[offset] = ls->GetStoreType ();
memcpy (m_Buffer + offset + 1, ls->GetBuffer (), ls->GetBufferLen ());
@@ -844,6 +939,44 @@ namespace data
else
LogPrint (eLogError, "LeaseSet2: couldn't extract inner layer");
}
void LocalEncryptedLeaseSet2::CreateClientAuthData (const uint8_t * subcredential, int authType, std::shared_ptr<std::vector<AuthPublicKey> > authKeys, const uint8_t * authCookie, uint8_t * authData) const
{
if (authType == ENCRYPTED_LEASESET_AUTH_TYPE_DH)
{
i2p::crypto::X25519Keys ek;
ek.GenerateKeys (); // esk and epk
memcpy (authData, ek.GetPublicKey (), 32); authData += 32; // epk
htobe16buf (authData, authKeys->size ()); authData += 2; // num clients
uint8_t authInput[100]; // sharedSecret || cpk_i || subcredential || publishedTimestamp
memcpy (authInput + 64, subcredential, 36);
for (auto& it: *authKeys)
{
ek.Agree (it, authInput); // sharedSecret = DH(esk, cpk_i)
memcpy (authInput + 32, it, 32);
uint8_t okm[64]; // 52 actual data
i2p::crypto::HKDF (ek.GetPublicKey (), authInput, 100, "ELS2_XCA", okm);
memcpy (authData, okm + 44, 8); authData += 8; // clientID_i
i2p::crypto::ChaCha20 (authCookie, 32, okm, okm + 32, authData); authData += 32; // clientCookie_i
}
}
else // assume PSK
{
uint8_t authSalt[32];
RAND_bytes (authSalt, 32);
memcpy (authData, authSalt, 32); authData += 32; // authSalt
htobe16buf (authData, authKeys->size ()); authData += 2; // num clients
uint8_t authInput[68]; // authInput = psk_i || subcredential || publishedTimestamp
memcpy (authInput + 32, subcredential, 36);
for (auto& it: *authKeys)
{
memcpy (authInput, it, 32);
uint8_t okm[64]; // 52 actual data
i2p::crypto::HKDF (authSalt, authInput, 68, "ELS2PSKA", okm);
memcpy (authData, okm + 44, 8); authData += 8; // clientID_i
i2p::crypto::ChaCha20 (authCookie, 32, okm, okm + 32, authData); authData += 32; // clientCookie_i
}
}
}
}
}

View File

@@ -79,9 +79,9 @@ namespace data
bool operator== (const LeaseSet& other) const
{ return m_BufferLen == other.m_BufferLen && !memcmp (m_Buffer, other.m_Buffer, m_BufferLen); };
virtual uint8_t GetStoreType () const { return NETDB_STORE_TYPE_LEASESET; };
virtual uint8_t GetOrigStoreType () const { return NETDB_STORE_TYPE_LEASESET; };
virtual uint32_t GetPublishedTimestamp () const { return 0; }; // should be set for LeaseSet2 only
virtual std::shared_ptr<const i2p::crypto::Verifier> GetTransientVerifier () const { return nullptr; };
virtual bool IsPublishedEncrypted () const { return false; };
// implements RoutingDestination
std::shared_ptr<const IdentityEx> GetIdentity () const { return m_Identity; };
@@ -97,6 +97,7 @@ namespace data
// called from LeaseSet2
LeaseSet (bool storeLeases);
void SetBuffer (const uint8_t * buf, size_t len);
void SetBufferLen (size_t len);
void SetIdentity (std::shared_ptr<const IdentityEx> identity) { m_Identity = identity; };
void SetExpirationTime (uint64_t t) { m_ExpirationTime = t; };
void SetIsValid (bool isValid) { m_IsValid = isValid; };
@@ -129,21 +130,25 @@ namespace data
const uint8_t NETDB_STORE_TYPE_META_LEASESET2 = 7;
const uint16_t LEASESET2_FLAG_OFFLINE_KEYS = 0x0001;
const uint16_t LEASESET2_FLAG_UNPUBLISHED_LEASESET = 0x0002;
const uint16_t LEASESET2_FLAG_PUBLISHED_ENCRYPTED = 0x0004;
class LeaseSet2: public LeaseSet
{
public:
LeaseSet2 (uint8_t storeType, const uint8_t * buf, size_t len, bool storeLeases = true);
LeaseSet2 (const uint8_t * buf, size_t len, std::shared_ptr<const BlindedPublicKey> key, const uint8_t * secret = nullptr); // store type 5, called from local netdb only
LeaseSet2 (uint8_t storeType, const uint8_t * buf, size_t len, bool storeLeases = true, CryptoKeyType preferredCrypto = CRYPTO_KEY_TYPE_ELGAMAL);
LeaseSet2 (const uint8_t * buf, size_t len, std::shared_ptr<const BlindedPublicKey> key, const uint8_t * secret = nullptr, CryptoKeyType preferredCrypto = CRYPTO_KEY_TYPE_ELGAMAL); // store type 5, called from local netdb only
uint8_t GetStoreType () const { return m_StoreType; };
uint8_t GetOrigStoreType () const { return m_OrigStoreType; };
uint32_t GetPublishedTimestamp () const { return m_PublishedTimestamp; };
bool IsPublic () const { return m_IsPublic; };
bool IsPublishedEncrypted () const { return m_IsPublishedEncrypted; };
std::shared_ptr<const i2p::crypto::Verifier> GetTransientVerifier () const { return m_TransientVerifier; };
void Update (const uint8_t * buf, size_t len, bool verifySignature);
// implements RoutingDestination
void Encrypt (const uint8_t * data, uint8_t * encrypted, BN_CTX * ctx) const;
CryptoKeyType GetEncryptionType () const { return m_EncryptionType; };
private:
@@ -160,9 +165,11 @@ namespace data
private:
uint8_t m_StoreType, m_OrigStoreType;
uint8_t m_StoreType;
uint32_t m_PublishedTimestamp = 0;
bool m_IsPublic = true, m_IsPublishedEncrypted = false;
std::shared_ptr<i2p::crypto::Verifier> m_TransientVerifier;
CryptoKeyType m_EncryptionType;
std::shared_ptr<i2p::crypto::CryptoKeyEncryptor> m_Encryptor; // for standardLS2
};
@@ -225,9 +232,17 @@ namespace data
{
public:
struct KeySection
{
uint16_t keyType, keyLen;
const uint8_t * encryptionPublicKey;
};
typedef std::vector<KeySection> KeySections;
LocalLeaseSet2 (uint8_t storeType, const i2p::data::PrivateKeys& keys,
uint16_t keyType, uint16_t keyLen, const uint8_t * encryptionPublicKey,
std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> > tunnels);
const KeySections& encryptionKeys,
std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> > tunnels,
bool isPublic, bool isPublishedEncrypted = false);
LocalLeaseSet2 (uint8_t storeType, std::shared_ptr<const IdentityEx> identity, const uint8_t * buf, size_t len); // from I2CP
virtual ~LocalLeaseSet2 () { delete[] m_Buffer; };
@@ -247,17 +262,28 @@ namespace data
size_t m_BufferLen;
};
const int ENCRYPTED_LEASESET_AUTH_TYPE_NONE = 0;
const int ENCRYPTED_LEASESET_AUTH_TYPE_DH = 1;
const int ENCRYPTED_LEASESET_AUTH_TYPE_PSK = 2;
typedef i2p::data::Tag<32> AuthPublicKey;
class LocalEncryptedLeaseSet2: public LocalLeaseSet2
{
public:
LocalEncryptedLeaseSet2 (std::shared_ptr<const LocalLeaseSet2> ls, const i2p::data::PrivateKeys& keys, i2p::data::SigningKeyType blindedKeyType = i2p::data::SIGNING_KEY_TYPE_REDDSA_SHA512_ED25519);
LocalEncryptedLeaseSet2 (std::shared_ptr<const LocalLeaseSet2> ls, const i2p::data::PrivateKeys& keys, int authType = ENCRYPTED_LEASESET_AUTH_TYPE_NONE, std::shared_ptr<std::vector<AuthPublicKey> > authKeys = nullptr);
LocalEncryptedLeaseSet2 (std::shared_ptr<const IdentityEx> identity, const uint8_t * buf, size_t len); // from I2CP
const IdentHash& GetStoreHash () const { return m_StoreHash; };
std::shared_ptr<const LocalLeaseSet> GetInnerLeaseSet () const { return m_InnerLeaseSet; };
private:
void CreateClientAuthData (const uint8_t * subcredential, int authType, std::shared_ptr<std::vector<AuthPublicKey> > authKeys, const uint8_t * authCookie, uint8_t * authData) const;
private:
IdentHash m_StoreHash;

View File

@@ -161,6 +161,7 @@ void LogPrint (std::stringstream& s, TValue&& arg) noexcept
s << std::forward<TValue>(arg);
}
#if (__cplusplus < 201703L) // below C++ 17
/** internal usage only -- folding args array to single string */
template<typename TValue, typename... TArgs>
void LogPrint (std::stringstream& s, TValue&& arg, TArgs&&... args) noexcept
@@ -168,6 +169,7 @@ void LogPrint (std::stringstream& s, TValue&& arg, TArgs&&... args) noexcept
LogPrint (s, std::forward<TValue>(arg));
LogPrint (s, std::forward<TArgs>(args)...);
}
#endif
/**
* @brief Create log message and send it to queue
@@ -184,7 +186,11 @@ void LogPrint (LogLevel level, TArgs&&... args) noexcept
// fold message to single string
std::stringstream ss("");
#if (__cplusplus >= 201703L) // C++ 17 or higher
(LogPrint (ss, std::forward<TArgs>(args)), ...);
#else
LogPrint (ss, std::forward<TArgs>(args)...);
#endif
auto msg = std::make_shared<i2p::log::LogMsg>(level, std::time(nullptr), ss.str());
msg->tid = std::this_thread::get_id();

File diff suppressed because it is too large Load Diff

View File

@@ -1,12 +1,10 @@
/*
* Copyright (c) 2013-2018, The PurpleI2P Project
* Copyright (c) 2013-2020, 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
*
* Kovri go write your own code
*
*/
#ifndef NTCP2_H__
#define NTCP2_H__
@@ -30,7 +28,7 @@ namespace i2p
namespace transport
{
const size_t NTCP2_UNENCRYPTED_FRAME_MAX_SIZE = 65519;
const size_t NTCP2_UNENCRYPTED_FRAME_MAX_SIZE = 65519;
const int NTCP2_MAX_PADDING_RATIO = 6; // in %
const int NTCP2_CONNECT_TIMEOUT = 5; // 5 seconds
@@ -38,7 +36,7 @@ namespace transport
const int NTCP2_TERMINATION_TIMEOUT = 120; // 2 minutes
const int NTCP2_TERMINATION_CHECK_TIMEOUT = 30; // 30 seconds
const int NTCP2_CLOCK_SKEW = 60; // in seconds
const int NTCP2_CLOCK_SKEW = 60; // in seconds
const int NTCP2_MAX_OUTGOING_QUEUE_SIZE = 500; // how many messages we can queue up
enum NTCP2BlockType
@@ -48,8 +46,8 @@ namespace transport
eNTCP2BlkRouterInfo, // 2
eNTCP2BlkI2NPMessage, // 3
eNTCP2BlkTermination, // 4
eNTCP2BlkPadding = 254
};
eNTCP2BlkPadding = 254
};
enum NTCP2TerminationReason
{
@@ -71,21 +69,21 @@ namespace transport
eNTCP2RouterInfoSignatureVerificationFail, // 15
eNTCP2IncorrectSParameter, // 16
eNTCP2Banned, // 17
};
};
// RouterInfo flags
const uint8_t NTCP2_ROUTER_INFO_FLAG_REQUEST_FLOOD = 0x01;
const uint8_t NTCP2_ROUTER_INFO_FLAG_REQUEST_FLOOD = 0x01;
struct NTCP2Establisher
{
NTCP2Establisher ();
~NTCP2Establisher ();
const uint8_t * GetPub () const { return m_EphemeralKeys.GetPublicKey (); };
const uint8_t * GetRemotePub () const { return m_RemoteEphemeralPublicKey; }; // Y for Alice and X for Bob
uint8_t * GetRemotePub () { return m_RemoteEphemeralPublicKey; }; // to set
const uint8_t * GetK () const { return m_K; };
const uint8_t * GetK () const { return m_CK + 32; };
const uint8_t * GetCK () const { return m_CK; };
const uint8_t * GetH () const { return m_H; };
@@ -114,34 +112,36 @@ namespace transport
i2p::crypto::X25519Keys m_EphemeralKeys;
uint8_t m_RemoteEphemeralPublicKey[32]; // x25519
uint8_t m_RemoteStaticKey[32], m_IV[16], m_H[32] /*h*/, m_CK[33] /*ck*/, m_K[32] /*k*/;
uint8_t m_RemoteStaticKey[32], m_IV[16], m_H[32] /*h*/, m_CK[64] /* [ck, k]*/;
i2p::data::IdentHash m_RemoteIdentHash;
uint16_t m3p2Len;
uint16_t m3p2Len;
uint8_t * m_SessionRequestBuffer, * m_SessionCreatedBuffer, * m_SessionConfirmedBuffer;
size_t m_SessionRequestBufferLen, m_SessionCreatedBufferLen;
};
};
class NTCP2Server;
class NTCP2Session: public TransportSession, public std::enable_shared_from_this<NTCP2Session>
{
public:
NTCP2Session (NTCP2Server& server, std::shared_ptr<const i2p::data::RouterInfo> in_RemoteRouter = nullptr);
NTCP2Session (NTCP2Server& server, std::shared_ptr<const i2p::data::RouterInfo> in_RemoteRouter = nullptr);
~NTCP2Session ();
void Terminate ();
void TerminateByTimeout ();
void Done ();
void Close () { m_Socket.close (); }; // for accept
boost::asio::ip::tcp::socket& GetSocket () { return m_Socket; };
bool IsEstablished () const { return m_IsEstablished; };
bool IsTerminated () const { return m_IsTerminated; };
void ClientLogin (); // Alice
void ClientLogin (); // Alice
void ServerLogin (); // Bob
void SendLocalRouterInfo (); // after handshake
void SendI2NPMessages (const std::vector<std::shared_ptr<I2NPMessage> >& msgs);
@@ -194,15 +194,15 @@ namespace transport
std::unique_ptr<NTCP2Establisher> m_Establisher;
// data phase
uint8_t m_Kab[33], m_Kba[32], m_Sipkeysab[33], m_Sipkeysba[32];
uint8_t m_Kab[32], m_Kba[32], m_Sipkeysab[32], m_Sipkeysba[32];
const uint8_t * m_SendKey, * m_ReceiveKey;
#if OPENSSL_SIPHASH
#if OPENSSL_SIPHASH
EVP_PKEY * m_SendSipKey, * m_ReceiveSipKey;
EVP_MD_CTX * m_SendMDCtx, * m_ReceiveMDCtx;
#else
const uint8_t * m_SendSipKey, * m_ReceiveSipKey;
#endif
uint16_t m_NextReceivedLen;
uint16_t m_NextReceivedLen;
uint8_t * m_NextReceivedBuffer, * m_NextSendBuffer;
union
{
@@ -217,31 +217,51 @@ namespace transport
std::list<std::shared_ptr<I2NPMessage> > m_SendQueue;
};
class NTCP2Server
class NTCP2Server: private i2p::util::RunnableServiceWithWork
{
public:
enum RemoteAddressType
{
eIP4Address,
eIP6Address,
eHostname
};
enum ProxyType
{
eNoProxy,
eSocksProxy,
eHTTPProxy
};
NTCP2Server ();
~NTCP2Server ();
void Start ();
void Stop ();
boost::asio::io_service& GetService () { return GetIOService (); };
bool AddNTCP2Session (std::shared_ptr<NTCP2Session> session);
bool AddNTCP2Session (std::shared_ptr<NTCP2Session> session, bool incoming = false);
void RemoveNTCP2Session (std::shared_ptr<NTCP2Session> session);
std::shared_ptr<NTCP2Session> FindNTCP2Session (const i2p::data::IdentHash& ident);
boost::asio::io_service& GetService () { return m_Service; };
void ConnectWithProxy (const std::string& addr, uint16_t port, RemoteAddressType addrtype, std::shared_ptr<NTCP2Session> conn);
void Connect(const boost::asio::ip::address & address, uint16_t port, std::shared_ptr<NTCP2Session> conn);
void AfterSocksHandshake(std::shared_ptr<NTCP2Session> conn, std::shared_ptr<boost::asio::deadline_timer> timer, const std::string & host, uint16_t port, RemoteAddressType addrtype);
bool UsingProxy() const { return m_ProxyType != eNoProxy; };
void UseProxy(ProxyType proxy, const std::string & address, uint16_t port);
private:
void Run ();
void HandleAccept (std::shared_ptr<NTCP2Session> conn, const boost::system::error_code& error);
void HandleAcceptV6 (std::shared_ptr<NTCP2Session> conn, const boost::system::error_code& error);
void HandleConnect (const boost::system::error_code& ecode, std::shared_ptr<NTCP2Session> conn, std::shared_ptr<boost::asio::deadline_timer> timer);
void HandleConnect (const boost::system::error_code& ecode, std::shared_ptr<NTCP2Session> conn, std::shared_ptr<boost::asio::deadline_timer> timer);
void HandleProxyConnect(const boost::system::error_code& ecode, std::shared_ptr<NTCP2Session> conn, std::shared_ptr<boost::asio::deadline_timer> timer, const std::string & host, uint16_t port, RemoteAddressType adddrtype);
// timer
void ScheduleTermination ();
@@ -249,15 +269,17 @@ namespace transport
private:
bool m_IsRunning;
std::thread * m_Thread;
boost::asio::io_service m_Service;
boost::asio::io_service::work m_Work;
boost::asio::deadline_timer m_TerminationTimer;
std::unique_ptr<boost::asio::ip::tcp::acceptor> m_NTCP2Acceptor, m_NTCP2V6Acceptor;
std::map<i2p::data::IdentHash, std::shared_ptr<NTCP2Session> > m_NTCP2Sessions;
std::map<i2p::data::IdentHash, std::shared_ptr<NTCP2Session> > m_NTCP2Sessions;
std::list<std::shared_ptr<NTCP2Session> > m_PendingIncomingSessions;
ProxyType m_ProxyType =eNoProxy;
std::string m_ProxyAddress;
uint16_t m_ProxyPort;
boost::asio::ip::tcp::resolver m_Resolver;
std::unique_ptr<boost::asio::ip::tcp::endpoint> m_ProxyEndpoint;
public:
// for HTTP/I2PControl

View File

@@ -14,9 +14,6 @@
#include "NTCPSession.h"
#include "HTTP.h"
#include "util.h"
#ifdef WITH_EVENTS
#include "Event.h"
#endif
using namespace i2p::crypto;
@@ -649,9 +646,6 @@ namespace transport
{
if (!m_NextMessage->IsExpired ())
{
#ifdef WITH_EVENTS
QueueIntEvent("transport.recvmsg", GetIdentHashBase64(), 1);
#endif
m_Handler.PutNextMessage (m_NextMessage);
}
else

View File

@@ -15,8 +15,9 @@
#include "NTCP2.h"
#include "RouterContext.h"
#include "Garlic.h"
#include "NetDb.hpp"
#include "ECIESX25519AEADRatchetSession.h"
#include "Config.h"
#include "NetDb.hpp"
using namespace i2p::transport;
@@ -307,10 +308,18 @@ namespace data
if (it == m_LeaseSets.end () || it->second->GetStoreType () != storeType ||
leaseSet->GetPublishedTimestamp () > it->second->GetPublishedTimestamp ())
{
// TODO: implement actual update
LogPrint (eLogInfo, "NetDb: LeaseSet2 updated: ", ident.ToBase32());
m_LeaseSets[ident] = leaseSet;
return true;
if (leaseSet->IsPublic ())
{
// TODO: implement actual update
LogPrint (eLogInfo, "NetDb: LeaseSet2 updated: ", ident.ToBase32());
m_LeaseSets[ident] = leaseSet;
return true;
}
else
{
LogPrint (eLogWarning, "NetDb: Unpublished LeaseSet2 received: ", ident.ToBase32());
m_LeaseSets.erase (ident);
}
}
}
else
@@ -523,9 +532,10 @@ namespace data
auto total = m_RouterInfos.size ();
uint64_t expirationTimeout = NETDB_MAX_EXPIRATION_TIMEOUT*1000LL;
uint64_t ts = i2p::util::GetMillisecondsSinceEpoch();
auto uptime = i2p::context.GetUptime ();
// routers don't expire if less than 90 or uptime is less than 1 hour
bool checkForExpiration = total > NETDB_MIN_ROUTERS && ts > (i2p::context.GetStartupTime () + 600)*1000LL; // 10 minutes
if (checkForExpiration && ts > (i2p::context.GetStartupTime () + 3600)*1000LL) // 1 hour
bool checkForExpiration = total > NETDB_MIN_ROUTERS && uptime > 600; // 10 minutes
if (checkForExpiration && uptime > 3600) // 1 hour
expirationTimeout = i2p::context.IsFloodfill () ? NETDB_FLOODFILL_EXPIRATION_TIMEOUT*1000LL :
NETDB_MIN_EXPIRATION_TIMEOUT*1000LL + (NETDB_MAX_EXPIRATION_TIMEOUT - NETDB_MIN_EXPIRATION_TIMEOUT)*1000LL*NETDB_MIN_ROUTERS/total;
@@ -909,7 +919,7 @@ namespace data
else if (!leaseSet->IsExpired ()) // we don't send back our LeaseSets
{
LogPrint (eLogDebug, "NetDb: requested LeaseSet ", key, " found");
replyMsg = CreateDatabaseStoreMsg (leaseSet);
replyMsg = CreateDatabaseStoreMsg (ident, leaseSet);
}
}
@@ -940,10 +950,20 @@ namespace data
const uint8_t numTags = excluded[32];
if (numTags)
{
const i2p::garlic::SessionTag sessionTag(excluded + 33); // take first tag
i2p::garlic::GarlicRoutingSession garlic (sessionKey, sessionTag);
replyMsg = garlic.WrapSingleMessage (replyMsg);
if(replyMsg == nullptr) LogPrint(eLogError, "NetDb: failed to wrap message");
if (flag & DATABASE_LOOKUP_ECIES_FLAG)
{
uint64_t tag;
memcpy (&tag, excluded + 33, 8);
replyMsg = i2p::garlic::WrapECIESX25519AEADRatchetMessage (replyMsg, sessionKey, tag);
}
else
{
const i2p::garlic::SessionTag sessionTag(excluded + 33); // take first tag
i2p::garlic::ElGamalAESSession garlic (sessionKey, sessionTag);
replyMsg = garlic.WrapSingleMessage (replyMsg);
}
if (!replyMsg)
LogPrint (eLogError, "NetDb: failed to wrap message");
}
else
LogPrint(eLogWarning, "NetDb: encrypted reply requested but no tags provided");

View File

@@ -19,7 +19,7 @@ namespace i2p
RouterContext::RouterContext ():
m_LastUpdateTime (0), m_AcceptsTunnels (true), m_IsFloodfill (false),
m_StartupTime (0), m_ShareRatio (100), m_Status (eRouterStatusOK),
m_ShareRatio (100), m_Status (eRouterStatusOK),
m_Error (eRouterErrorNone), m_NetID (I2PD_NET_ID)
{
}
@@ -27,7 +27,8 @@ namespace i2p
void RouterContext::Init ()
{
srand (i2p::util::GetMillisecondsSinceEpoch () % 1000);
m_StartupTime = i2p::util::GetSecondsSinceEpoch ();
m_StartupTime = std::chrono::steady_clock::now();
if (!Load ())
CreateNewRouter ();
m_Decryptor = m_Keys.CreateDecryptor (nullptr);
@@ -183,17 +184,18 @@ namespace i2p
void RouterContext::PublishNTCP2Address (int port, bool publish, bool v4only)
{
if (!m_NTCP2Keys) return;
if (!port)
{
port = rand () % (30777 - 9111) + 9111; // I2P network ports range
if (port == 9150) port = 9151; // Tor browser
}
bool updated = false;
for (auto& address : m_RouterInfo.GetAddresses ())
{
if (address->IsNTCP2 () && (address->port != port || address->ntcp2->isPublished != publish) && (!v4only || address->host.is_v4 ()))
{
address->port = port;
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;
address->cost = publish ? 3 : 14;
address->ntcp2->isPublished = publish;
address->ntcp2->iv = m_NTCP2Keys->iv;
@@ -336,7 +338,11 @@ namespace i2p
{
case low : /* not set */; break;
case extra : caps |= i2p::data::RouterInfo::eExtraBandwidth; break; // 'P'
case unlim : caps |= i2p::data::RouterInfo::eExtraBandwidth; // no break here, extra + high means 'X'
case unlim : caps |= i2p::data::RouterInfo::eExtraBandwidth;
#if (__cplusplus >= 201703L) // C++ 17 or higher
[[fallthrough]];
#endif
// no break here, extra + high means 'X'
case high : caps |= i2p::data::RouterInfo::eHighBandwidth; break;
}
m_RouterInfo.SetCaps (caps);
@@ -432,14 +438,14 @@ namespace i2p
}
// remove NTCP or NTCP2 v4 address
bool ntcp; i2p::config::GetOption("ntcp", ntcp);
if (ntcp)
if (ntcp)
PublishNTCPAddress (false);
else
{
bool ntcp2; i2p::config::GetOption("ntcp2.enabled", ntcp2);
if (ntcp2)
PublishNTCP2Address (port, false, true);
}
}
// update
UpdateRouterInfo ();
}
@@ -489,7 +495,7 @@ namespace i2p
void RouterContext::SetSupportsV6 (bool supportsV6)
{
if (supportsV6)
{
{
m_RouterInfo.EnableV6 ();
// insert v6 addresses if necessary
bool foundSSU = false, foundNTCP = false, foundNTCP2 = false;
@@ -507,7 +513,7 @@ namespace i2p
}
else
foundNTCP = true;
}
}
port = addr->port;
}
if (!port) i2p::config::GetOption("port", port);
@@ -519,7 +525,7 @@ namespace i2p
{
std::string host = "::1"; // TODO: read host
m_RouterInfo.AddSSUAddress (host.c_str (), port, GetIdentHash ());
}
}
}
// NTCP2
if (!foundNTCP2)
@@ -528,11 +534,11 @@ namespace i2p
bool ntcp2Published; i2p::config::GetOption("ntcp2.published", ntcp2Published);
if (ntcp2 && ntcp2Published)
{
std::string ntcp2Host;
std::string ntcp2Host;
if (!i2p::config::IsDefault ("ntcp2.addressv6"))
i2p::config::GetOption ("ntcp2.addressv6", ntcp2Host);
else
ntcp2Host = "::1";
ntcp2Host = "::1";
uint16_t ntcp2Port; i2p::config::GetOption ("ntcp2.port", ntcp2Port);
if (!ntcp2Port) ntcp2Port = port;
m_RouterInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv, boost::asio::ip::address::from_string (ntcp2Host), ntcp2Port);
@@ -544,10 +550,10 @@ namespace i2p
bool ntcp; i2p::config::GetOption("ntcp", ntcp);
if (ntcp)
{
std::string host = "::1";
std::string host = "::1";
m_RouterInfo.AddNTCPAddress (host.c_str (), port);
}
}
}
}
else
m_RouterInfo.DisableV6 ();
@@ -690,9 +696,9 @@ namespace i2p
return i2p::tunnel::tunnels.GetExploratoryPool ();
}
void RouterContext::HandleI2NPMessage (const uint8_t * buf, size_t len, std::shared_ptr<i2p::tunnel::InboundTunnel> from)
void RouterContext::HandleI2NPMessage (const uint8_t * buf, size_t len)
{
i2p::HandleI2NPMessage (CreateI2NPMessage (buf, GetI2NPMessageLength (buf, len), from));
i2p::HandleI2NPMessage (CreateI2NPMessage (buf, GetI2NPMessageLength (buf, len)));
}
void RouterContext::ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg)
@@ -715,10 +721,10 @@ namespace i2p
uint32_t RouterContext::GetUptime () const
{
return i2p::util::GetSecondsSinceEpoch () - m_StartupTime;
return std::chrono::duration_cast<std::chrono::seconds> (std::chrono::steady_clock::now() - m_StartupTime).count ();
}
bool RouterContext::Decrypt (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx) const
bool RouterContext::Decrypt (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx, i2p::data::CryptoKeyType preferredCrypto) const
{
return m_Decryptor ? m_Decryptor->Decrypt (encrypted, data, ctx, true) : false;
}

View File

@@ -5,6 +5,7 @@
#include <string>
#include <memory>
#include <mutex>
#include <chrono>
#include <boost/asio.hpp>
#include "Identity.h"
#include "RouterInfo.h"
@@ -64,8 +65,7 @@ namespace i2p
const uint8_t * GetNTCP2IV () const { return m_NTCP2Keys ? m_NTCP2Keys->iv : nullptr; };
i2p::crypto::X25519Keys& GetStaticKeys ();
uint32_t GetUptime () const;
uint32_t GetStartupTime () const { return m_StartupTime; };
uint32_t GetUptime () const; // in seconds
uint64_t GetLastUpdateTime () const { return m_LastUpdateTime; };
uint64_t GetBandwidthLimit () const { return m_BandwidthLimit; };
uint64_t GetTransitBandwidthLimit () const { return (m_BandwidthLimit*m_ShareRatio)/100LL; };
@@ -108,19 +108,24 @@ namespace i2p
// implements LocalDestination
std::shared_ptr<const i2p::data::IdentityEx> GetIdentity () const { return m_Keys.GetPublic (); };
bool Decrypt (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx) const;
bool Decrypt (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx, i2p::data::CryptoKeyType preferredCrypto) const;
void Sign (const uint8_t * buf, int len, uint8_t * signature) const { m_Keys.Sign (buf, len, signature); };
void SetLeaseSetUpdated () {};
// implements GarlicDestination
std::shared_ptr<const i2p::data::LocalLeaseSet> GetLeaseSet () { return nullptr; };
std::shared_ptr<i2p::tunnel::TunnelPool> GetTunnelPool () const;
void HandleI2NPMessage (const uint8_t * buf, size_t len, std::shared_ptr<i2p::tunnel::InboundTunnel> from);
// override GarlicDestination
void ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg);
void ProcessDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg);
protected:
// implements GarlicDestination
void HandleI2NPMessage (const uint8_t * buf, size_t len);
bool HandleCloveI2NPMessage (I2NPMessageType typeID, const uint8_t * payload, size_t len) { return false; }; // not implemented
private:
void CreateNewRouter ();
@@ -136,8 +141,8 @@ namespace i2p
i2p::data::PrivateKeys m_Keys;
std::shared_ptr<i2p::crypto::CryptoKeyDecryptor> m_Decryptor;
uint64_t m_LastUpdateTime; // in seconds
bool m_AcceptsTunnels, m_IsFloodfill;
uint64_t m_StartupTime; // in seconds since epoch
bool m_AcceptsTunnels, m_IsFloodfill;
std::chrono::time_point<std::chrono::steady_clock> m_StartupTime;
uint64_t m_BandwidthLimit; // allowed bandwidth
int m_ShareRatio;
RouterStatus m_Status;

View File

@@ -160,6 +160,13 @@ namespace transport
catch (std::exception& ex)
{
LogPrint (eLogError, "SSU: receivers runtime exception: ", ex.what ());
if (m_IsRunning)
{
// restart socket
m_Socket.close ();
OpenSocket ();
Receive ();
}
}
}
}
@@ -175,6 +182,12 @@ namespace transport
catch (std::exception& ex)
{
LogPrint (eLogError, "SSU: v6 receivers runtime exception: ", ex.what ());
if (m_IsRunning)
{
m_SocketV6.close ();
OpenSocketV6 ();
ReceiveV6 ();
}
}
}
}

View File

@@ -5,9 +5,6 @@
#include "NetDb.hpp"
#include "SSU.h"
#include "SSUData.h"
#ifdef WITH_EVENTS
#include "Event.h"
#endif
namespace i2p
{
@@ -241,9 +238,6 @@ namespace transport
m_LastMessageReceivedTime = i2p::util::GetSecondsSinceEpoch ();
if (!msg->IsExpired ())
{
#ifdef WITH_EVENTS
QueueIntEvent("transport.recvmsg", m_Session.GetIdentHashBase64(), 1);
#endif
m_Handler.PutNextMessage (msg);
}
else
@@ -448,7 +442,7 @@ namespace transport
}
catch (boost::system::system_error& ec)
{
LogPrint (eLogWarning, "SSU: Can't resend data fragment ", ec.what ());
LogPrint (eLogWarning, "SSU: Can't resend message ", it->first, " data fragment: ", ec.what ());
}
}
@@ -458,7 +452,7 @@ namespace transport
}
else
{
LogPrint (eLogInfo, "SSU: message has not been ACKed after ", MAX_NUM_RESENDS, " attempts, deleted");
LogPrint (eLogInfo, "SSU: message ", it->first, " has not been ACKed after ", MAX_NUM_RESENDS, " attempts, deleted");
it = m_SentMessages.erase (it);
}
}
@@ -494,7 +488,7 @@ namespace transport
{
if (ts > it->second->lastFragmentInsertTime + INCOMPLETE_MESSAGES_CLEANUP_TIMEOUT)
{
LogPrint (eLogWarning, "SSU: message ", it->first, " was not completed in ", INCOMPLETE_MESSAGES_CLEANUP_TIMEOUT, " seconds, deleted");
LogPrint (eLogWarning, "SSU: message ", it->first, " was not completed in ", INCOMPLETE_MESSAGES_CLEANUP_TIMEOUT, " seconds, deleted");
it = m_IncompleteMessages.erase (it);
}
else

View File

@@ -1,4 +1,5 @@
#include <boost/bind.hpp>
#include "version.h"
#include "Crypto.h"
#include "Log.h"
#include "Timestamp.h"
@@ -729,7 +730,8 @@ namespace transport
encryption.Encrypt (encrypted, encryptedLen, encrypted);
// assume actual buffer size is 18 (16 + 2) bytes more
memcpy (buf + len, iv, 16);
htobe16buf (buf + len + 16, encryptedLen);
uint16_t netid = i2p::context.GetNetID ();
htobe16buf (buf + len + 16, (netid == I2PD_NET_ID) ? encryptedLen : encryptedLen ^ ((netid - 2) << 8));
i2p::crypto::HMACMD5Digest (encrypted, encryptedLen + 18, macKey, header->mac);
}
@@ -750,7 +752,8 @@ namespace transport
m_SessionKeyEncryption.Encrypt (encrypted, encryptedLen, encrypted);
// assume actual buffer size is 18 (16 + 2) bytes more
memcpy (buf + len, header->iv, 16);
htobe16buf (buf + len + 16, encryptedLen);
uint16_t netid = i2p::context.GetNetID ();
htobe16buf (buf + len + 16, (netid == I2PD_NET_ID) ? encryptedLen : encryptedLen ^ ((netid - 2) << 8));
i2p::crypto::HMACMD5Digest (encrypted, encryptedLen + 18, m_MacKey, header->mac);
}
@@ -799,7 +802,8 @@ namespace transport
uint16_t encryptedLen = len - (encrypted - buf);
// assume actual buffer size is 18 (16 + 2) bytes more
memcpy (buf + len, header->iv, 16);
htobe16buf (buf + len + 16, encryptedLen);
uint16_t netid = i2p::context.GetNetID ();
htobe16buf (buf + len + 16, (netid == I2PD_NET_ID) ? encryptedLen : encryptedLen ^ ((netid - 2) << 8));
uint8_t digest[16];
i2p::crypto::HMACMD5Digest (encrypted, encryptedLen + 18, macKey, digest);
return !memcmp (header->mac, digest, 16);

View File

@@ -86,13 +86,14 @@ namespace stream
LogPrint (eLogDebug, "Streaming: Stream deleted");
}
void Stream::Terminate ()
void Stream::Terminate (bool deleteFromDestination) // shoudl be called from StreamingDestination::Stop only
{
m_AckSendTimer.cancel ();
m_ReceiveTimer.cancel ();
m_ResendTimer.cancel ();
//CleanUp (); /* Need to recheck - broke working on windows */
m_LocalDestination.DeleteStream (shared_from_this ());
if (deleteFromDestination)
m_LocalDestination.DeleteStream (shared_from_this ());
}
void Stream::CleanUp ()
@@ -847,6 +848,9 @@ namespace stream
break;
case 2:
m_RTO = INITIAL_RTO; // drop RTO to initial upon tunnels pair change first time
#if (__cplusplus >= 201703L) // C++ 17 or higher
[[fallthrough]];
#endif
// no break here
case 4:
if (m_RoutingSession) m_RoutingSession->SetSharedRoutingPath (nullptr);
@@ -918,7 +922,7 @@ namespace stream
{
expired = false;
// time to request
if (m_RemoteLeaseSet->GetOrigStoreType () == i2p::data::NETDB_STORE_TYPE_ENCRYPTED_LEASESET2)
if (m_RemoteLeaseSet->IsPublishedEncrypted ())
m_LocalDestination.GetOwner ()->RequestDestinationWithEncryptedLeaseSet (
std::make_shared<i2p::data::BlindedPublicKey>(m_RemoteIdentity));
else
@@ -964,7 +968,6 @@ namespace stream
StreamingDestination::StreamingDestination (std::shared_ptr<i2p::client::ClientDestination> owner, uint16_t localPort, bool gzip):
m_Owner (owner), m_LocalPort (localPort), m_Gzip (gzip),
m_LastIncomingReceiveStreamID (0),
m_PendingIncomingTimer (m_Owner->GetService ())
{
}
@@ -990,7 +993,10 @@ namespace stream
m_PendingIncomingStreams.clear ();
{
std::unique_lock<std::mutex> l(m_StreamsMutex);
for (auto it: m_Streams)
it.second->Terminate (false); // we delete here
m_Streams.clear ();
m_IncomingStreams.clear ();
}
}
@@ -1013,18 +1019,17 @@ namespace stream
if (packet->IsSYN () && !packet->GetSeqn ()) // new incoming stream
{
uint32_t receiveStreamID = packet->GetReceiveStreamID ();
if (receiveStreamID == m_LastIncomingReceiveStreamID)
auto it1 = m_IncomingStreams.find (receiveStreamID);
if (it1 != m_IncomingStreams.end ())
{
// already pending
LogPrint(eLogWarning, "Streaming: Incoming streaming with rSID=", receiveStreamID, " already exists");
DeletePacket (packet); // drop it, because previous should be connected
return;
}
auto incomingStream = CreateNewIncomingStream ();
auto incomingStream = CreateNewIncomingStream (receiveStreamID);
incomingStream->HandleNextPacket (packet); // SYN
auto ident = incomingStream->GetRemoteIdentity();
m_LastIncomingReceiveStreamID = receiveStreamID;
// handle saved packets if any
{
@@ -1062,13 +1067,13 @@ namespace stream
else // follow on packet without SYN
{
uint32_t receiveStreamID = packet->GetReceiveStreamID ();
for (auto& it: m_Streams)
if (it.second->GetSendStreamID () == receiveStreamID)
{
// found
it.second->HandleNextPacket (packet);
return;
}
auto it1 = m_IncomingStreams.find (receiveStreamID);
if (it1 != m_IncomingStreams.end ())
{
// found
it1->second->HandleNextPacket (packet);
return;
}
// save follow on packet
auto it = m_SavedPackets.find (receiveStreamID);
if (it != m_SavedPackets.end ())
@@ -1105,11 +1110,12 @@ namespace stream
return s;
}
std::shared_ptr<Stream> StreamingDestination::CreateNewIncomingStream ()
std::shared_ptr<Stream> StreamingDestination::CreateNewIncomingStream (uint32_t receiveStreamID)
{
auto s = std::make_shared<Stream> (m_Owner->GetService (), *this);
std::unique_lock<std::mutex> l(m_StreamsMutex);
m_Streams[s->GetRecvStreamID ()] = s;
m_IncomingStreams[receiveStreamID] = s;
return s;
}
@@ -1118,12 +1124,20 @@ namespace stream
if (stream)
{
std::unique_lock<std::mutex> l(m_StreamsMutex);
auto it = m_Streams.find (stream->GetRecvStreamID ());
if (it != m_Streams.end ())
m_Streams.erase (it);
m_Streams.erase (stream->GetRecvStreamID ());
m_IncomingStreams.erase (stream->GetSendStreamID ());
}
}
bool StreamingDestination::DeleteStream (uint32_t recvStreamID)
{
auto it = m_Streams.find (recvStreamID);
if (it == m_Streams.end ())
return false;
DeleteStream (it->second);
return true;
}
void StreamingDestination::SetAcceptor (const Acceptor& acceptor)
{
m_Acceptor = acceptor; // we must set it immediately for IsAcceptorSet

View File

@@ -180,8 +180,7 @@ namespace stream
int GetWindowSize () const { return m_WindowSize; };
int GetRTT () const { return m_RTT; };
/** don't call me */
void Terminate ();
void Terminate (bool deleteFromDestination = true);
private:
@@ -251,6 +250,7 @@ namespace stream
std::shared_ptr<Stream> CreateNewOutgoingStream (std::shared_ptr<const i2p::data::LeaseSet> remote, int port = 0);
void DeleteStream (std::shared_ptr<Stream> stream);
bool DeleteStream (uint32_t recvStreamID);
void SetAcceptor (const Acceptor& acceptor);
void ResetAcceptor ();
bool IsAcceptorSet () const { return m_Acceptor != nullptr; };
@@ -269,8 +269,10 @@ namespace stream
void AcceptOnceAcceptor (std::shared_ptr<Stream> stream, Acceptor acceptor, Acceptor prev);
private:
void HandleNextPacket (Packet * packet);
std::shared_ptr<Stream> CreateNewIncomingStream ();
std::shared_ptr<Stream> CreateNewIncomingStream (uint32_t receiveStreamID);
void HandlePendingIncomingTimer (const boost::system::error_code& ecode);
private:
@@ -280,8 +282,8 @@ namespace stream
bool m_Gzip; // gzip compression of data messages
std::mutex m_StreamsMutex;
std::map<uint32_t, std::shared_ptr<Stream> > m_Streams; // sendStreamID->stream
std::map<uint32_t, std::shared_ptr<Stream> > m_IncomingStreams; // receiveStreamID->stream
Acceptor m_Acceptor;
uint32_t m_LastIncomingReceiveStreamID;
std::list<std::shared_ptr<Stream> > m_PendingIncomingStreams;
boost::asio::deadline_timer m_PendingIncomingTimer;
std::map<uint32_t, std::list<Packet *> > m_SavedPackets; // receiveStreamID->packets, arrived before SYN

View File

@@ -71,14 +71,14 @@ public:
return std::string (str, str + l);
}
void FromBase32 (const std::string& s)
size_t FromBase32 (const std::string& s)
{
i2p::data::Base32ToByteStream (s.c_str (), s.length (), m_Buf, sz);
return i2p::data::Base32ToByteStream (s.c_str (), s.length (), m_Buf, sz);
}
void FromBase64 (const std::string& s)
size_t FromBase64 (const std::string& s)
{
i2p::data::Base64ToByteStream (s.c_str (), s.length (), m_Buf, sz);
return i2p::data::Base64ToByteStream (s.c_str (), s.length (), m_Buf, sz);
}
private:
@@ -93,4 +93,16 @@ private:
} // data
} // i2p
namespace std
{
// hash for std::unordered_map
template<size_t sz> struct hash<i2p::data::Tag<sz> >
{
size_t operator()(const i2p::data::Tag<sz>& s) const
{
return s.GetLL ()[0];
}
};
}
#endif /* TAG_H__ */

View File

@@ -5,6 +5,7 @@
#include <iostream>
#include <memory>
#include <vector>
#include <mutex>
#include "Identity.h"
#include "Crypto.h"
#include "RouterInfo.h"
@@ -67,8 +68,16 @@ namespace transport
std::string GetIdentHashBase64() const { return m_RemoteIdentity ? m_RemoteIdentity->GetIdentHash().ToBase64() : ""; }
std::shared_ptr<const i2p::data::IdentityEx> GetRemoteIdentity () { return m_RemoteIdentity; };
void SetRemoteIdentity (std::shared_ptr<const i2p::data::IdentityEx> ident) { m_RemoteIdentity = ident; };
std::shared_ptr<const i2p::data::IdentityEx> GetRemoteIdentity ()
{
std::lock_guard<std::mutex> l(m_RemoteIdentityMutex);
return m_RemoteIdentity;
}
void SetRemoteIdentity (std::shared_ptr<const i2p::data::IdentityEx> ident)
{
std::lock_guard<std::mutex> l(m_RemoteIdentityMutex);
m_RemoteIdentity = ident;
}
size_t GetNumSentBytes () const { return m_NumSentBytes; };
size_t GetNumReceivedBytes () const { return m_NumReceivedBytes; };
@@ -85,6 +94,7 @@ namespace transport
protected:
std::shared_ptr<const i2p::data::IdentityEx> m_RemoteIdentity;
mutable std::mutex m_RemoteIdentityMutex;
std::shared_ptr<i2p::crypto::DHKeys> m_DHKeysPair; // X - for client and Y - for server
size_t m_NumSentBytes, m_NumReceivedBytes;
bool m_IsOutgoing;

View File

@@ -6,10 +6,6 @@
#include "Transports.h"
#include "Config.h"
#include "HTTP.h"
#ifdef WITH_EVENTS
#include "Event.h"
#include "util.h"
#endif
using namespace i2p::data;
@@ -157,6 +153,7 @@ namespace transport
m_IsRunning = true;
m_Thread = new std::thread (std::bind (&Transports::Run, this));
std::string ntcpproxy; i2p::config::GetOption("ntcpproxy", ntcpproxy);
std::string ntcp2proxy; i2p::config::GetOption("ntcp2.proxy", ntcp2proxy);
i2p::http::URL proxyurl;
uint16_t softLimit, hardLimit, threads;
i2p::config::GetOption("limits.ntcpsoft", softLimit);
@@ -196,13 +193,38 @@ namespace transport
LogPrint(eLogError, "Transports: invalid NTCP proxy url ", ntcpproxy);
return;
}
// create NTCP2. TODO: move to acceptor
// create NTCP2. TODO: move to acceptor
bool ntcp2; i2p::config::GetOption("ntcp2.enabled", ntcp2);
if (ntcp2)
{
m_NTCP2Server = new NTCP2Server ();
m_NTCP2Server->Start ();
}
if(!ntcp2proxy.empty())
{
if(proxyurl.parse(ntcp2proxy))
{
if(proxyurl.schema == "socks" || proxyurl.schema == "http")
{
m_NTCP2Server = new NTCP2Server ();
NTCP2Server::ProxyType proxytype = NTCP2Server::eSocksProxy;
if (proxyurl.schema == "http")
proxytype = NTCP2Server::eHTTPProxy;
m_NTCP2Server->UseProxy(proxytype, proxyurl.host, proxyurl.port) ;
m_NTCP2Server->Start();
}
else
LogPrint(eLogError, "Transports: unsupported NTCP2 proxy URL ", ntcp2proxy);
}
else
LogPrint(eLogError, "Transports: invalid NTCP2 proxy url ", ntcp2proxy);
return;
}
else
{
m_NTCP2Server = new NTCP2Server ();
m_NTCP2Server->Start ();
}
}
// create acceptors
auto& addresses = context.GetRouterInfo ().GetAddresses ();
@@ -249,11 +271,11 @@ namespace transport
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));
if (m_IsNAT)
{
m_PeerTestTimer->expires_from_now (boost::posix_time::minutes(PEER_TEST_INTERVAL));
m_PeerTestTimer->async_wait (std::bind (&Transports::HandlePeerTestTimer, this, std::placeholders::_1));
}
if (m_IsNAT)
{
m_PeerTestTimer->expires_from_now (boost::posix_time::minutes(PEER_TEST_INTERVAL));
m_PeerTestTimer->async_wait (std::bind (&Transports::HandlePeerTestTimer, this, std::placeholders::_1));
}
}
void Transports::Stop ()
@@ -346,9 +368,6 @@ namespace transport
void Transports::SendMessages (const i2p::data::IdentHash& ident, const std::vector<std::shared_ptr<i2p::I2NPMessage> >& msgs)
{
#ifdef WITH_EVENTS
QueueIntEvent("transport.send", ident.ToBase64(), msgs.size());
#endif
m_Service->post (std::bind (&Transports::PostMessages, this, ident, msgs));
}
@@ -405,24 +424,36 @@ namespace transport
{
if (peer.router) // we have RI already
{
if (!peer.numAttempts) // NTCP2
{
peer.numAttempts++;
if (m_NTCP2Server) // we support NTCP2
{
// NTCP2 have priority over NTCP
auto address = peer.router->GetNTCP2Address (true, !context.SupportsV6 ()); // published only
if (address)
{
auto s = std::make_shared<NTCP2Session> (*m_NTCP2Server, peer.router);
m_NTCP2Server->Connect (address->host, address->port, s);
return true;
}
}
}
if (!peer.numAttempts) // NTCP2
{
peer.numAttempts++;
if (m_NTCP2Server) // we support NTCP2
{
// NTCP2 have priority over NTCP
auto address = peer.router->GetNTCP2Address (true, !context.SupportsV6 ()); // published only
if (address)
{
auto s = std::make_shared<NTCP2Session> (*m_NTCP2Server, peer.router);
if(m_NTCP2Server->UsingProxy())
{
NTCP2Server::RemoteAddressType remote = NTCP2Server::eIP4Address;
std::string addr = address->host.to_string();
if(address->host.is_v6())
remote = NTCP2Server::eIP6Address;
m_NTCP2Server->ConnectWithProxy(addr, address->port, remote, s);
}
else
m_NTCP2Server->Connect (address->host, address->port, s);
return true;
}
}
}
if (peer.numAttempts == 1) // NTCP1
{
peer.numAttempts++;
peer.numAttempts++;
auto address = peer.router->GetNTCPAddress (!context.SupportsV6 ());
if (address && m_NTCPServer)
{
@@ -465,6 +496,7 @@ namespace transport
}
}
LogPrint (eLogInfo, "Transports: No NTCP or SSU addresses available");
i2p::data::netdb.SetUnreachable (ident, true); // we are here because all connection attempts failed
peer.Done ();
std::unique_lock<std::mutex> l(m_PeersMutex);
m_Peers.erase (ident);
@@ -503,28 +535,6 @@ namespace transport
}
}
}
void Transports::CloseSession (std::shared_ptr<const i2p::data::RouterInfo> router)
{
if (!router) return;
m_Service->post (std::bind (&Transports::PostCloseSession, this, router));
}
void Transports::PostCloseSession (std::shared_ptr<const i2p::data::RouterInfo> router)
{
auto ssuSession = m_SSUServer ? m_SSUServer->FindSession (router) : nullptr;
if (ssuSession) // try SSU first
{
m_SSUServer->DeleteSession (ssuSession);
LogPrint (eLogDebug, "Transports: SSU session closed");
}
auto ntcpSession = m_NTCPServer ? m_NTCPServer->FindNTCPSession(router->GetIdentHash()) : nullptr;
if (ntcpSession) // try deleting ntcp session too
{
ntcpSession->Terminate ();
LogPrint(eLogDebug, "Transports: NTCP session closed");
}
}
void Transports::DetectExternalIP ()
{
@@ -562,7 +572,10 @@ namespace transport
{
auto addr = router->GetSSUV6Address ();
if (addr)
m_SSUServer->CreateDirectSession (router, { addr->host, (uint16_t)addr->port }, false);
m_SSUServer->GetServiceV6 ().post ([this, router, addr]
{
m_SSUServer->CreateDirectSession (router, { addr->host, (uint16_t)addr->port }, false);
});
}
}
}
@@ -576,6 +589,7 @@ namespace transport
if (RoutesRestricted() || !i2p::context.SupportsV4 ()) return;
if (m_SSUServer)
{
LogPrint (eLogInfo, "Transports: Started peer test");
bool statusChanged = false;
for (int i = 0; i < 5; i++)
{
@@ -591,7 +605,7 @@ namespace transport
}
}
if (!statusChanged)
LogPrint (eLogWarning, "Can't find routers for peer test");
LogPrint (eLogWarning, "Transports: Can't find routers for peer test");
}
}
@@ -615,9 +629,6 @@ namespace transport
auto it = m_Peers.find (ident);
if (it != m_Peers.end ())
{
#ifdef WITH_EVENTS
EmitEvent({{"type" , "transport.connected"}, {"ident", ident.ToBase64()}, {"inbound", "false"}});
#endif
bool sendDatabaseStore = true;
if (it->second.delayedMessages.size () > 0)
{
@@ -643,9 +654,6 @@ namespace transport
session->Done();
return;
}
#ifdef WITH_EVENTS
EmitEvent({{"type" , "transport.connected"}, {"ident", ident.ToBase64()}, {"inbound", "true"}});
#endif
session->SendI2NPMessages ({ CreateDatabaseStoreMsg () }); // send DatabaseStore
std::unique_lock<std::mutex> l(m_PeersMutex);
m_Peers.insert (std::make_pair (ident, Peer{ 0, nullptr, { session }, i2p::util::GetSecondsSinceEpoch (), {} }));
@@ -660,17 +668,19 @@ namespace transport
auto remoteIdentity = session->GetRemoteIdentity ();
if (!remoteIdentity) return;
auto ident = remoteIdentity->GetIdentHash ();
#ifdef WITH_EVENTS
EmitEvent({{"type" , "transport.disconnected"}, {"ident", ident.ToBase64()}});
#endif
auto it = m_Peers.find (ident);
if (it != m_Peers.end ())
{
auto before = it->second.sessions.size ();
it->second.sessions.remove (session);
if (it->second.sessions.empty ()) // TODO: why?
if (it->second.sessions.empty ())
{
if (it->second.delayedMessages.size () > 0)
{
if (before > 0) // we had an active session before
it->second.numAttempts = 0; // start over
ConnectToPeer (ident, it->second);
}
else
{
std::unique_lock<std::mutex> l(m_PeersMutex);

View File

@@ -92,7 +92,6 @@ namespace transport
void SendMessage (const i2p::data::IdentHash& ident, std::shared_ptr<i2p::I2NPMessage> msg);
void SendMessages (const i2p::data::IdentHash& ident, const std::vector<std::shared_ptr<i2p::I2NPMessage> >& msgs);
void CloseSession (std::shared_ptr<const i2p::data::RouterInfo> router);
void PeerConnected (std::shared_ptr<TransportSession> session);
void PeerDisconnected (std::shared_ptr<TransportSession> session);
@@ -131,7 +130,6 @@ namespace transport
void RequestComplete (std::shared_ptr<const i2p::data::RouterInfo> r, const i2p::data::IdentHash& ident);
void HandleRequestComplete (std::shared_ptr<const i2p::data::RouterInfo> r, i2p::data::IdentHash ident);
void PostMessages (i2p::data::IdentHash ident, std::vector<std::shared_ptr<i2p::I2NPMessage> > msgs);
void PostCloseSession (std::shared_ptr<const i2p::data::RouterInfo> router);
bool ConnectToPeer (const i2p::data::IdentHash& ident, Peer& peer);
void HandlePeerCleanupTimer (const boost::system::error_code& ecode);
void HandlePeerTestTimer (const boost::system::error_code& ecode);

View File

@@ -1,5 +1,6 @@
#include <string.h>
#include "I2PEndian.h"
#include <random>
#include <thread>
#include <algorithm>
#include <vector>
@@ -13,9 +14,6 @@
#include "Config.h"
#include "Tunnel.h"
#include "TunnelPool.h"
#ifdef WITH_EVENTS
#include "Event.h"
#endif
namespace i2p
{
@@ -34,9 +32,6 @@ namespace tunnel
void Tunnel::Build (uint32_t replyMsgID, std::shared_ptr<OutboundTunnel> outboundTunnel)
{
#ifdef WITH_EVENTS
std::string peers = i2p::context.GetIdentity()->GetIdentHash().ToBase64();
#endif
auto numHops = m_Config->GetNumHops ();
int numRecords = numHops <= STANDARD_NUM_RECORDS ? STANDARD_NUM_RECORDS : numHops;
auto msg = NewI2NPShortMessage ();
@@ -45,7 +40,7 @@ namespace tunnel
// shuffle records
std::vector<int> recordIndicies;
for (int i = 0; i < numRecords; i++) recordIndicies.push_back(i);
std::random_shuffle (recordIndicies.begin(), recordIndicies.end());
std::shuffle (recordIndicies.begin(), recordIndicies.end(), std::mt19937(std::random_device()()));
// create real records
uint8_t * records = msg->GetPayload () + 1;
@@ -63,15 +58,9 @@ namespace tunnel
hop->CreateBuildRequestRecord (records + idx*TUNNEL_BUILD_RECORD_SIZE, msgID, ctx);
hop->recordIndex = idx;
i++;
#ifdef WITH_EVENTS
peers += ":" + hop->ident->GetIdentHash().ToBase64();
#endif
hop = hop->next;
}
BN_CTX_free (ctx);
#ifdef WITH_EVENTS
EmitTunnelEvent("tunnel.build", this, peers);
#endif
// fill up fake records with random data
for (int i = numHops; i < numRecords; i++)
{
@@ -206,9 +195,6 @@ namespace tunnel
void Tunnel::SetState(TunnelState state)
{
m_State = state;
#ifdef WITH_EVENTS
EmitTunnelEvent("tunnel.state", this, state);
#endif
}
@@ -590,7 +576,6 @@ namespace tunnel
for (auto it = pendingTunnels.begin (); it != pendingTunnels.end ();)
{
auto tunnel = it->second;
auto pool = tunnel->GetTunnelPool();
switch (tunnel->GetState ())
{
case eTunnelStatePending:
@@ -613,11 +598,6 @@ namespace tunnel
hop = hop->next;
}
}
#ifdef WITH_EVENTS
EmitTunnelEvent("tunnel.state", tunnel.get(), eTunnelStateBuildFailed);
#endif
// for i2lua
if(pool) pool->OnTunnelBuildResult(tunnel, eBuildResultTimeout);
// delete
it = pendingTunnels.erase (it);
m_NumFailedTunnelCreations++;
@@ -627,12 +607,6 @@ namespace tunnel
break;
case eTunnelStateBuildFailed:
LogPrint (eLogDebug, "Tunnel: pending build request ", it->first, " failed, deleted");
#ifdef WITH_EVENTS
EmitTunnelEvent("tunnel.state", tunnel.get(), eTunnelStateBuildFailed);
#endif
// for i2lua
if(pool) pool->OnTunnelBuildResult(tunnel, eBuildResultRejected);
it = pendingTunnels.erase (it);
m_NumFailedTunnelCreations++;
break;

View File

@@ -19,49 +19,11 @@
#include "TunnelGateway.h"
#include "TunnelBase.h"
#include "I2NPProtocol.h"
#include "Event.h"
namespace i2p
{
namespace tunnel
{
template<typename TunnelT>
static void EmitTunnelEvent(const std::string & ev, const TunnelT & t)
{
#ifdef WITH_EVENTS
EmitEvent({{"type", ev}, {"tid", std::to_string(t->GetTunnelID())}});
#else
(void) ev;
(void) t;
#endif
}
template<typename TunnelT, typename T>
static void EmitTunnelEvent(const std::string & ev, TunnelT * t, const T & val)
{
#ifdef WITH_EVENTS
EmitEvent({{"type", ev}, {"tid", std::to_string(t->GetTunnelID())}, {"value", std::to_string(val)}, {"inbound", std::to_string(t->IsInbound())}});
#else
(void) ev;
(void) t;
(void) val;
#endif
}
template<typename TunnelT>
static void EmitTunnelEvent(const std::string & ev, TunnelT * t, const std::string & val)
{
#ifdef WITH_EVENTS
EmitEvent({{"type", ev}, {"tid", std::to_string(t->GetTunnelID())}, {"value", val}, {"inbound", std::to_string(t->IsInbound())}});
#else
(void) ev;
(void) t;
(void) val;
#endif
}
const int TUNNEL_EXPIRATION_TIMEOUT = 660; // 11 minutes
const int TUNNEL_EXPIRATION_THRESHOLD = 60; // 1 minute
const int TUNNEL_RECREATION_THRESHOLD = 90; // 1.5 minutes

View File

@@ -1,4 +1,5 @@
#include <algorithm>
#include <random>
#include "I2PEndian.h"
#include "Crypto.h"
#include "Tunnel.h"
@@ -10,9 +11,6 @@
#include "Tunnel.h"
#include "TunnelPool.h"
#include "Destination.h"
#ifdef WITH_EVENTS
#include "Event.h"
#endif
namespace i2p
{
@@ -85,25 +83,17 @@ namespace tunnel
{
if (!m_IsActive) return;
{
#ifdef WITH_EVENTS
EmitTunnelEvent("tunnels.created", createdTunnel);
#endif
std::unique_lock<std::mutex> l(m_InboundTunnelsMutex);
m_InboundTunnels.insert (createdTunnel);
}
if (m_LocalDestination)
m_LocalDestination->SetLeaseSetUpdated ();
OnTunnelBuildResult(createdTunnel, eBuildResultOkay);
}
void TunnelPool::TunnelExpired (std::shared_ptr<InboundTunnel> expiredTunnel)
{
if (expiredTunnel)
{
#ifdef WITH_EVENTS
EmitTunnelEvent("tunnels.expired", expiredTunnel);
#endif
expiredTunnel->SetTunnelPool (nullptr);
for (auto& it: m_Tests)
if (it.second.second == expiredTunnel) it.second.second = nullptr;
@@ -117,14 +107,9 @@ namespace tunnel
{
if (!m_IsActive) return;
{
#ifdef WITH_EVENTS
EmitTunnelEvent("tunnels.created", createdTunnel);
#endif
std::unique_lock<std::mutex> l(m_OutboundTunnelsMutex);
m_OutboundTunnels.insert (createdTunnel);
}
OnTunnelBuildResult(createdTunnel, eBuildResultOkay);
//CreatePairedInboundTunnel (createdTunnel);
}
@@ -132,9 +117,6 @@ namespace tunnel
{
if (expiredTunnel)
{
#ifdef WITH_EVENTS
EmitTunnelEvent("tunnels.expired", expiredTunnel);
#endif
expiredTunnel->SetTunnelPool (nullptr);
for (auto& it: m_Tests)
if (it.second.first == expiredTunnel) it.second.first = nullptr;
@@ -441,7 +423,7 @@ namespace tunnel
int size = m_ExplicitPeers->size ();
std::vector<int> peerIndicies;
for (int i = 0; i < size; i++) peerIndicies.push_back(i);
std::random_shuffle (peerIndicies.begin(), peerIndicies.end());
std::shuffle (peerIndicies.begin(), peerIndicies.end(), std::mt19937(std::random_device()()));
int numHops = isInbound ? m_NumInboundHops : m_NumOutboundHops;
for (int i = 0; i < numHops; i++)
@@ -610,11 +592,5 @@ namespace tunnel
}
return tun;
}
void TunnelPool::OnTunnelBuildResult(std::shared_ptr<Tunnel> tunnel, TunnelBuildResult result)
{
auto peers = tunnel->GetPeers();
if(m_CustomPeerSelector) m_CustomPeerSelector->OnBuildResult(peers, tunnel->IsInbound(), result);
}
}
}

View File

@@ -23,13 +23,6 @@ namespace tunnel
class InboundTunnel;
class OutboundTunnel;
enum TunnelBuildResult {
eBuildResultOkay, // tunnel was built okay
eBuildResultRejected, // tunnel build was explicitly rejected
eBuildResultTimeout // tunnel build timed out
};
typedef std::shared_ptr<const i2p::data::IdentityEx> Peer;
typedef std::vector<Peer> Path;
@@ -38,7 +31,6 @@ namespace tunnel
{
virtual ~ITunnelPeerSelector() {};
virtual bool SelectPeers(Path & peers, int hops, bool isInbound) = 0;
virtual bool OnBuildResult(const Path & peers, bool isInbound, TunnelBuildResult result) = 0;
};
@@ -98,8 +90,6 @@ namespace tunnel
std::shared_ptr<InboundTunnel> GetLowestLatencyInboundTunnel(std::shared_ptr<InboundTunnel> exclude=nullptr) const;
std::shared_ptr<OutboundTunnel> GetLowestLatencyOutboundTunnel(std::shared_ptr<OutboundTunnel> exclude=nullptr) const;
void OnTunnelBuildResult(std::shared_ptr<Tunnel> tunnel, TunnelBuildResult result);
// for overriding tunnel peer selection
std::shared_ptr<const i2p::data::RouterInfo> SelectNextHop (std::shared_ptr<const i2p::data::RouterInfo> prevHop) const;

View File

@@ -77,7 +77,7 @@ namespace api
std::shared_ptr<i2p::client::ClientDestination> CreateLocalDestination (const i2p::data::PrivateKeys& keys, bool isPublic,
const std::map<std::string, std::string> * params)
{
auto localDestination = std::make_shared<i2p::client::ClientDestination> (keys, isPublic, params);
auto localDestination = std::make_shared<i2p::client::RunnableClientDestination> (keys, isPublic, params);
localDestination->Start ();
return localDestination;
}
@@ -86,7 +86,7 @@ namespace api
const std::map<std::string, std::string> * params)
{
i2p::data::PrivateKeys keys = i2p::data::PrivateKeys::CreateRandomKeys (sigType);
auto localDestination = std::make_shared<i2p::client::ClientDestination> (keys, isPublic, params);
auto localDestination = std::make_shared<i2p::client::RunnableClientDestination> (keys, isPublic, params);
localDestination->Start ();
return localDestination;
}

View File

@@ -22,7 +22,7 @@
#define MALLOC(x) HeapAlloc(GetProcessHeap(), 0, (x))
#define FREE(x) HeapFree(GetProcessHeap(), 0, (x))
// inet_pton exists Windows since Vista, but XP haven't that function!
// inet_pton exists Windows since Vista, but XP doesn't have that function!
// 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)
{
@@ -57,21 +57,65 @@ namespace i2p
{
namespace util
{
void RunnableService::StartIOService ()
{
if (!m_IsRunning)
{
m_IsRunning = true;
m_Thread.reset (new std::thread (std::bind (& RunnableService::Run, this)));
}
}
void RunnableService::StopIOService ()
{
if (m_IsRunning)
{
m_IsRunning = false;
m_Service.stop ();
if (m_Thread)
{
m_Thread->join ();
m_Thread = nullptr;
}
}
}
void RunnableService::Run ()
{
while (m_IsRunning)
{
try
{
m_Service.run ();
}
catch (std::exception& ex)
{
LogPrint (eLogError, m_Name, ": runtime exception: ", ex.what ());
}
}
}
namespace net
{
#ifdef WIN32
bool IsWindowsXPorLater()
{
OSVERSIONINFO osvi;
static bool isRequested = false;
static bool isXP = false;
if (!isRequested)
{
// request
OSVERSIONINFO osvi;
ZeroMemory(&osvi, sizeof(OSVERSIONINFO));
osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
GetVersionEx(&osvi);
ZeroMemory(&osvi, sizeof(OSVERSIONINFO));
osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
GetVersionEx(&osvi);
if (osvi.dwMajorVersion <= 5)
return true;
else
return false;
isXP = osvi.dwMajorVersion <= 5;
isRequested = true;
}
return isXP;
}
int GetMTUWindowsIpv4(sockaddr_in inputAddress, int fallback)
@@ -202,21 +246,20 @@ namespace net
std::string localAddressUniversal = localAddress.to_string();
#endif
if (IsWindowsXPorLater())
{
#define inet_pton inet_pton_xp
}
typedef int (* IPN)(int af, const char *src, void *dst);
IPN inetpton = (IPN)GetProcAddress (GetModuleHandle ("ws2_32.dll"), "InetPton");
if (!inetpton) inetpton = inet_pton_xp; // use own implementation if not found
if(localAddress.is_v4())
{
sockaddr_in inputAddress;
inet_pton(AF_INET, localAddressUniversal.c_str(), &(inputAddress.sin_addr));
inetpton(AF_INET, localAddressUniversal.c_str(), &(inputAddress.sin_addr));
return GetMTUWindowsIpv4(inputAddress, fallback);
}
else if(localAddress.is_v6())
{
sockaddr_in6 inputAddress;
inet_pton(AF_INET6, localAddressUniversal.c_str(), &(inputAddress.sin6_addr));
inetpton(AF_INET6, localAddressUniversal.c_str(), &(inputAddress.sin6_addr));
return GetMTUWindowsIpv6(inputAddress, fallback);
} else {
LogPrint(eLogError, "NetIface: GetMTU(): address family is not supported");
@@ -281,7 +324,7 @@ namespace net
int GetMTU(const boost::asio::ip::address& localAddress)
{
const int fallback = 576; // fallback MTU
int fallback = localAddress.is_v6 () ? 1280 : 620; // fallback MTU
#ifdef WIN32
return GetMTUWindows(localAddress, fallback);
@@ -312,15 +355,14 @@ namespace net
if (cur_ifname == ifname && cur->ifa_addr && cur->ifa_addr->sa_family == af)
{
// match
char * addr = new char[INET6_ADDRSTRLEN];
bzero(addr, INET6_ADDRSTRLEN);
char addr[INET6_ADDRSTRLEN];
memset (addr, 0, INET6_ADDRSTRLEN);
if(af == AF_INET)
inet_ntop(af, &((sockaddr_in *)cur->ifa_addr)->sin_addr, addr, INET6_ADDRSTRLEN);
else
inet_ntop(af, &((sockaddr_in6 *)cur->ifa_addr)->sin6_addr, addr, INET6_ADDRSTRLEN);
freeifaddrs(addrs);
std::string cur_ifaddr(addr);
delete[] addr;
return boost::asio::ip::address::from_string(cur_ifaddr);
}
cur = cur->ifa_next;

View File

@@ -5,6 +5,7 @@
#include <functional>
#include <memory>
#include <mutex>
#include <thread>
#include <utility>
#include <boost/asio.hpp>
@@ -46,7 +47,7 @@ namespace util
{
auto tmp = m_Head;
m_Head = static_cast<T*>(*(void * *)m_Head); // next
delete tmp;
::operator delete ((void *)tmp);
}
}
@@ -122,6 +123,43 @@ namespace util
std::mutex m_Mutex;
};
class RunnableService
{
protected:
RunnableService (const std::string& name): m_Name (name), m_IsRunning (false) {}
virtual ~RunnableService () {}
boost::asio::io_service& GetIOService () { return m_Service; }
bool IsRunning () const { return m_IsRunning; };
void StartIOService ();
void StopIOService ();
private:
void Run ();
private:
std::string m_Name;
volatile bool m_IsRunning;
std::unique_ptr<std::thread> m_Thread;
boost::asio::io_service m_Service;
};
class RunnableServiceWithWork: public RunnableService
{
protected:
RunnableServiceWithWork (const std::string& name):
RunnableService (name), m_Work (GetIOService ()) {}
private:
boost::asio::io_service::work m_Work;
};
namespace net
{
int GetMTU (const boost::asio::ip::address& localAddress);

View File

@@ -7,7 +7,7 @@
#define MAKE_VERSION(a,b,c) STRINGIZE(a) "." STRINGIZE(b) "." STRINGIZE(c)
#define I2PD_VERSION_MAJOR 2
#define I2PD_VERSION_MINOR 26
#define I2PD_VERSION_MINOR 31
#define I2PD_VERSION_MICRO 0
#define I2PD_VERSION_PATCH 0
#define I2PD_VERSION MAKE_VERSION(I2PD_VERSION_MAJOR, I2PD_VERSION_MINOR, I2PD_VERSION_MICRO)
@@ -21,7 +21,7 @@
#define I2P_VERSION_MAJOR 0
#define I2P_VERSION_MINOR 9
#define I2P_VERSION_MICRO 40
#define I2P_VERSION_MICRO 45
#define I2P_VERSION_PATCH 0
#define I2P_VERSION MAKE_VERSION(I2P_VERSION_MAJOR, I2P_VERSION_MINOR, I2P_VERSION_MICRO)

View File

@@ -237,17 +237,19 @@ namespace client
//---------------------------------------------------------------------
Address::Address (const std::string& b32)
Address::Address (const std::string& b32):
addressType (eAddressInvalid)
{
if (b32.length () <= B33_ADDRESS_THRESHOLD)
{
addressType = eAddressIndentHash;
identHash.FromBase32 (b32);
if (identHash.FromBase32 (b32) > 0)
addressType = eAddressIndentHash;
}
else
{
addressType = eAddressBlindedPublicKey;
blindedPublicKey = std::make_shared<i2p::data::BlindedPublicKey>(b32);
if (blindedPublicKey->IsValid ())
addressType = eAddressBlindedPublicKey;
}
}
@@ -320,7 +322,10 @@ namespace client
{
auto pos = address.find(".b32.i2p");
if (pos != std::string::npos)
return std::make_shared<const Address>(address.substr (0, pos));
{
auto addr = std::make_shared<const Address>(address.substr (0, pos));
return addr->IsValid () ? addr : nullptr;
}
else
{
pos = address.find (".i2p");
@@ -422,9 +427,9 @@ namespace client
std::string name = s.substr(0, pos++);
std::string addr = s.substr(pos);
size_t pos = s.find('#');
size_t pos = addr.find('#');
if (pos != std::string::npos)
addr = addr.substr(pos); // remove comments
addr = addr.substr(0, pos); // remove comments
auto ident = std::make_shared<i2p::data::IdentityEx> ();
if (!ident->FromBase64(addr)) {

View File

@@ -33,13 +33,14 @@ namespace client
struct Address
{
enum { eAddressIndentHash, eAddressBlindedPublicKey } addressType;
enum { eAddressIndentHash, eAddressBlindedPublicKey, eAddressInvalid } addressType;
i2p::data::IdentHash identHash;
std::shared_ptr<i2p::data::BlindedPublicKey> blindedPublicKey;
Address (const std::string& b32);
Address (const i2p::data::IdentHash& hash);
bool IsIdentHash () const { return addressType == eAddressIndentHash; };
bool IsValid () const { return addressType != eAddressInvalid; };
};
inline std::string GetB32Address(const i2p::data::IdentHash& ident) { return ident.ToBase32().append(".b32.i2p"); }

View File

@@ -341,17 +341,17 @@ namespace client
SendReplyOK();
}
void BOBCommandSession::SendData (const char * data)
void BOBCommandSession::SendRaw (const char * data)
{
std::ostream os(&m_SendBuffer);
os << "DATA " << data << std::endl;
os << data << std::endl;
}
void BOBCommandSession::BuildStatusLine(bool currentTunnel, BOBDestination *dest, std::string &out)
{
// helper lambdas
const auto isset = [](const std::string &str) { return str.empty() ? "not_set" : str; }; // for inhost, outhost
const auto issetNum = [&isset](const int p) { return isset(p == 0 ? "" : std::to_string(p)); }; // for inport, outport
const auto issetStr = [](const std::string &str) { return str.empty() ? "not_set" : str; }; // for inhost, outhost
const auto issetNum = [&issetStr](const int p) { return issetStr(p == 0 ? "" : std::to_string(p)); }; // for inport, outport
const auto destExists = [](const BOBDestination * const dest) { return dest != nullptr; };
const auto destReady = [](const BOBDestination * const dest) { return dest->GetLocalDestination()->IsReady(); };
const auto bool_str = [](const bool v) { return v ? "true" : "false"; }; // bool -> str
@@ -359,8 +359,8 @@ namespace client
// tunnel info
const std::string nickname = currentTunnel ? m_Nickname : dest->GetNickname();
const bool quiet = currentTunnel ? m_IsQuiet : dest->GetQuiet();
const std::string inhost = isset(currentTunnel ? m_InHost : dest->GetInHost());
const std::string outhost = isset(currentTunnel ? m_OutHost : dest->GetOutHost());
const std::string inhost = issetStr(currentTunnel ? m_InHost : dest->GetInHost());
const std::string outhost = issetStr(currentTunnel ? m_OutHost : dest->GetOutHost());
const std::string inport = issetNum(currentTunnel ? m_InPort : dest->GetInPort());
const std::string outport = issetNum(currentTunnel ? m_OutPort : dest->GetOutPort());
const bool keys = destExists(dest); // key must exist when destination is created
@@ -370,7 +370,8 @@ namespace client
// build line
std::stringstream ss;
ss << "NICKNAME: " << nickname << " " << "STARTING: " << bool_str(starting) << " "
ss << "DATA "
<< "NICKNAME: " << nickname << " " << "STARTING: " << bool_str(starting) << " "
<< "RUNNING: " << bool_str(running) << " " << "STOPPING: " << bool_str(stopping) << " "
<< "KEYS: " << bool_str(keys) << " " << "QUIET: " << bool_str(quiet) << " "
<< "INPORT: " << inport << " " << "INHOST: " << inhost << " "
@@ -654,16 +655,16 @@ namespace client
for (const auto& it: destinations)
{
BuildStatusLine(false, it.second, statusLine);
SendData (statusLine.c_str());
SendRaw(statusLine.c_str());
if(m_Nickname.compare(it.second->GetNickname()) == 0)
sentCurrent = true;
}
if(!sentCurrent && !m_Nickname.empty())
{
// add the current tunnel to the list
// add the current tunnel to the list.
// this is for the incomplete tunnel which has not been started yet.
BuildStatusLine(true, m_CurrentDestination, statusLine);
LogPrint(eLogError, statusLine);
SendData(statusLine.c_str());
SendRaw(statusLine.c_str());
}
SendReplyOK ("Listing done");
}
@@ -690,21 +691,23 @@ namespace client
void BOBCommandSession::StatusCommandHandler (const char * operand, size_t len)
{
LogPrint (eLogDebug, "BOB: status ", operand);
const std::string name = operand;
std::string statusLine;
if (m_Nickname == operand)
// always prefer destination
auto ptr = m_Owner.FindDestination(name);
if(ptr != nullptr)
{
// check current tunnel
BuildStatusLine(true, nullptr, statusLine);
// tunnel destination exists
BuildStatusLine(false, ptr, statusLine);
SendReplyOK(statusLine.c_str());
}
else
{
// check other
std::string name = operand;
auto ptr = m_Owner.FindDestination(name);
if(ptr != nullptr)
if(m_Nickname == name && !name.empty())
{
BuildStatusLine(false, ptr, statusLine);
// tunnel is incomplete / has not been started yet
BuildStatusLine(true, nullptr, statusLine);
SendReplyOK(statusLine.c_str());
}
else
@@ -740,8 +743,8 @@ namespace client
}
BOBCommandChannel::BOBCommandChannel (const std::string& address, int port):
m_IsRunning (false), m_Thread (nullptr),
m_Acceptor (m_Service, boost::asio::ip::tcp::endpoint(boost::asio::ip::address::from_string(address), port))
RunnableService ("BOB"),
m_Acceptor (GetIOService (), boost::asio::ip::tcp::endpoint(boost::asio::ip::address::from_string(address), port))
{
// command -> handler
m_CommandHandlers[BOB_COMMAND_ZAP] = &BOBCommandSession::ZapCommandHandler;
@@ -791,7 +794,8 @@ namespace client
BOBCommandChannel::~BOBCommandChannel ()
{
Stop ();
if (IsRunning ())
Stop ();
for (const auto& it: m_Destinations)
delete it.second;
}
@@ -799,38 +803,15 @@ namespace client
void BOBCommandChannel::Start ()
{
Accept ();
m_IsRunning = true;
m_Thread = new std::thread (std::bind (&BOBCommandChannel::Run, this));
StartIOService ();
}
void BOBCommandChannel::Stop ()
{
m_IsRunning = false;
for (auto& it: m_Destinations)
it.second->Stop ();
m_Acceptor.cancel ();
m_Service.stop ();
if (m_Thread)
{
m_Thread->join ();
delete m_Thread;
m_Thread = nullptr;
}
}
void BOBCommandChannel::Run ()
{
while (m_IsRunning)
{
try
{
m_Service.run ();
}
catch (std::exception& ex)
{
LogPrint (eLogError, "BOB: runtime exception: ", ex.what ());
}
}
StopIOService ();
}
void BOBCommandChannel::AddDestination (const std::string& name, BOBDestination * dest)

View File

@@ -7,6 +7,7 @@
#include <map>
#include <string>
#include <boost/asio.hpp>
#include "util.h"
#include "I2PTunnel.h"
#include "I2PService.h"
#include "Identity.h"
@@ -213,7 +214,7 @@ namespace client
void HandleSent (const boost::system::error_code& ecode, std::size_t bytes_transferred);
void SendReplyOK (const char * msg = nullptr);
void SendReplyError (const char * msg);
void SendData (const char * data);
void SendRaw (const char * data);
void BuildStatusLine(bool currentTunnel, BOBDestination *destination, std::string &out);
@@ -231,7 +232,7 @@ namespace client
};
typedef void (BOBCommandSession::*BOBCommandHandler)(const char * operand, size_t len);
class BOBCommandChannel
class BOBCommandChannel: private i2p::util::RunnableService
{
public:
@@ -241,22 +242,18 @@ namespace client
void Start ();
void Stop ();
boost::asio::io_service& GetService () { return m_Service; };
boost::asio::io_service& GetService () { return GetIOService (); };
void AddDestination (const std::string& name, BOBDestination * dest);
void DeleteDestination (const std::string& name);
BOBDestination * FindDestination (const std::string& name);
private:
void Run ();
void Accept ();
void HandleAccept(const boost::system::error_code& ecode, std::shared_ptr<BOBCommandSession> session);
private:
bool m_IsRunning;
std::thread * m_Thread;
boost::asio::io_service m_Service;
boost::asio::ip::tcp::acceptor m_Acceptor;
std::map<std::string, BOBDestination *> m_Destinations;
std::map<std::string, BOBCommandHandler> m_CommandHandlers;

View File

@@ -9,7 +9,6 @@
#include "util.h"
#include "ClientContext.h"
#include "SOCKS.h"
#include "WebSocks.h"
#include "MatchedDestination.h"
namespace i2p
@@ -53,14 +52,19 @@ namespace client
// SAM
bool sam; i2p::config::GetOption("sam.enabled", sam);
if (sam) {
if (sam)
{
std::string samAddr; i2p::config::GetOption("sam.address", samAddr);
uint16_t samPort; i2p::config::GetOption("sam.port", samPort);
bool singleThread; i2p::config::GetOption("sam.singlethread", singleThread);
LogPrint(eLogInfo, "Clients: starting SAM bridge at ", samAddr, ":", samPort);
try {
m_SamBridge = new SAMBridge (samAddr, samPort);
m_SamBridge->Start ();
} catch (std::exception& e) {
try
{
m_SamBridge = new SAMBridge (samAddr, samPort, singleThread);
m_SamBridge->Start ();
}
catch (std::exception& e)
{
LogPrint(eLogError, "Clients: Exception in SAM bridge: ", e.what());
}
}
@@ -305,21 +309,34 @@ namespace client
const std::map<std::string, std::string> * params)
{
i2p::data::PrivateKeys keys = i2p::data::PrivateKeys::CreateRandomKeys (sigType, cryptoType);
auto localDestination = std::make_shared<ClientDestination> (keys, isPublic, params);
std::unique_lock<std::mutex> l(m_DestinationsMutex);
m_Destinations[localDestination->GetIdentHash ()] = localDestination;
localDestination->Start ();
auto localDestination = std::make_shared<RunnableClientDestination> (keys, isPublic, params);
AddLocalDestination (localDestination);
return localDestination;
}
std::shared_ptr<ClientDestination> ClientContext::CreateNewLocalDestination (
boost::asio::io_service& service, bool isPublic,
i2p::data::SigningKeyType sigType, i2p::data::CryptoKeyType cryptoType,
const std::map<std::string, std::string> * params)
{
i2p::data::PrivateKeys keys = i2p::data::PrivateKeys::CreateRandomKeys (sigType, cryptoType);
auto localDestination = std::make_shared<ClientDestination> (service, keys, isPublic, params);
AddLocalDestination (localDestination);
return localDestination;
}
std::shared_ptr<ClientDestination> ClientContext::CreateNewMatchedTunnelDestination(const i2p::data::PrivateKeys &keys, const std::string & name, const std::map<std::string, std::string> * params)
{
MatchedTunnelDestination * cl = new MatchedTunnelDestination(keys, name, params);
auto localDestination = std::shared_ptr<ClientDestination>(cl);
auto localDestination = std::make_shared<MatchedTunnelDestination>(keys, name, params);
AddLocalDestination (localDestination);
return localDestination;
}
void ClientContext::AddLocalDestination (std::shared_ptr<ClientDestination> localDestination)
{
std::unique_lock<std::mutex> l(m_DestinationsMutex);
m_Destinations[localDestination->GetIdentHash ()] = localDestination;
localDestination->Start ();
return localDestination;
}
void ClientContext::DeleteLocalDestination (std::shared_ptr<ClientDestination> destination)
@@ -344,14 +361,26 @@ namespace client
if (it != m_Destinations.end ())
{
LogPrint (eLogWarning, "Clients: Local destination ", m_AddressBook.ToAddress(keys.GetPublic ()->GetIdentHash ()), " exists");
if (!it->second->IsRunning ())
it->second->Start ();
it->second->Start (); // make sure to start
return it->second;
}
auto localDestination = std::make_shared<ClientDestination> (keys, isPublic, params);
std::unique_lock<std::mutex> l(m_DestinationsMutex);
m_Destinations[keys.GetPublic ()->GetIdentHash ()] = localDestination;
localDestination->Start ();
auto localDestination = std::make_shared<RunnableClientDestination> (keys, isPublic, params);
AddLocalDestination (localDestination);
return localDestination;
}
std::shared_ptr<ClientDestination> ClientContext::CreateNewLocalDestination (boost::asio::io_service& service,
const i2p::data::PrivateKeys& keys, bool isPublic, const std::map<std::string, std::string> * params)
{
auto it = m_Destinations.find (keys.GetPublic ()->GetIdentHash ());
if (it != m_Destinations.end ())
{
LogPrint (eLogWarning, "Clients: Local destination ", m_AddressBook.ToAddress(keys.GetPublic ()->GetIdentHash ()), " exists");
it->second->Start (); // make sure to start
return it->second;
}
auto localDestination = std::make_shared<ClientDestination> (service, keys, isPublic, params);
AddLocalDestination (localDestination);
return localDestination;
}
@@ -381,6 +410,16 @@ namespace client
return section.second.get (boost::property_tree::ptree::path_type (name, '/'), value);
}
template<typename Section>
void ClientContext::ReadI2CPOptionsGroup (const Section& section, const std::string& group, std::map<std::string, std::string>& options) const
{
for (auto it: section.second)
{
if (it.first.length () >= group.length () && !it.first.compare (0, group.length (), group))
options[it.first] = it.second.get_value ("");
}
}
template<typename Section>
void ClientContext::ReadI2CPOptions (const Section& section, std::map<std::string, std::string>& options) const
{
@@ -395,6 +434,17 @@ namespace client
options[I2CP_PARAM_LEASESET_TYPE] = GetI2CPOption(section, I2CP_PARAM_LEASESET_TYPE, DEFAULT_LEASESET_TYPE);
std::string encType = GetI2CPStringOption(section, I2CP_PARAM_LEASESET_ENCRYPTION_TYPE, "");
if (encType.length () > 0) options[I2CP_PARAM_LEASESET_ENCRYPTION_TYPE] = encType;
std::string privKey = GetI2CPStringOption(section, I2CP_PARAM_LEASESET_PRIV_KEY, "");
if (privKey.length () > 0) options[I2CP_PARAM_LEASESET_PRIV_KEY] = privKey;
auto authType = GetI2CPOption(section, I2CP_PARAM_LEASESET_AUTH_TYPE, 0);
if (authType != "0") // auth is set
{
options[I2CP_PARAM_LEASESET_AUTH_TYPE] = authType;
if (authType == "1") // DH
ReadI2CPOptionsGroup (section, I2CP_PARAM_LEASESET_CLIENT_DH, options);
else if (authType == "2") // PSK
ReadI2CPOptionsGroup (section, I2CP_PARAM_LEASESET_CLIENT_PSK, options);
}
}
void ClientContext::ReadI2CPOptionsFromConfig (const std::string& prefix, std::map<std::string, std::string>& options) const
@@ -547,10 +597,8 @@ namespace client
}
else if (type == I2P_TUNNELS_SECTION_TYPE_WEBSOCKS)
{
// websocks proxy
auto tun = std::make_shared<WebSocks>(address, port, localDestination);
clientTunnel = tun;
clientEndpoint = tun->GetLocalEndpoint();
LogPrint(eLogError, "Clients: I2P Client tunnel websocks is deprecated");
continue;
}
else
{
@@ -608,8 +656,8 @@ namespace client
// I2CP
std::map<std::string, std::string> options;
ReadI2CPOptions (section, options);
ReadI2CPOptions (section, options);
std::shared_ptr<ClientDestination> localDestination = nullptr;
i2p::data::PrivateKeys k;
if(!LoadPrivateKeys (k, keys, sigType, cryptoType))

View File

@@ -37,7 +37,7 @@ namespace client
const char I2P_CLIENT_TUNNEL_CRYPTO_TYPE[] = "cryptotype";
const char I2P_CLIENT_TUNNEL_DESTINATION_PORT[] = "destinationport";
const char I2P_CLIENT_TUNNEL_MATCH_TUNNELS[] = "matchtunnels";
const char I2P_CLIENT_TUNNEL_CONNECT_TIMEOUT[] = "connecttimeout";
const char I2P_CLIENT_TUNNEL_CONNECT_TIMEOUT[] = "connecttimeout";
const char I2P_SERVER_TUNNEL_HOST[] = "host";
const char I2P_SERVER_TUNNEL_HOST_OVERRIDE[] = "hostoverride";
const char I2P_SERVER_TUNNEL_PORT[] = "port";
@@ -68,8 +68,15 @@ namespace client
i2p::data::SigningKeyType sigType = i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519,
i2p::data::CryptoKeyType cryptoType = i2p::data::CRYPTO_KEY_TYPE_ELGAMAL,
const std::map<std::string, std::string> * params = nullptr); // used by SAM only
std::shared_ptr<ClientDestination> CreateNewLocalDestination (boost::asio::io_service& service,
bool isPublic = false, i2p::data::SigningKeyType sigType = i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519,
i2p::data::CryptoKeyType cryptoType = i2p::data::CRYPTO_KEY_TYPE_ELGAMAL,
const std::map<std::string, std::string> * params = nullptr); // same as previous but on external io_service
std::shared_ptr<ClientDestination> CreateNewLocalDestination (const i2p::data::PrivateKeys& keys, bool isPublic = true,
const std::map<std::string, std::string> * params = nullptr);
std::shared_ptr<ClientDestination> CreateNewLocalDestination (boost::asio::io_service& service,
const i2p::data::PrivateKeys& keys, bool isPublic = true,
const std::map<std::string, std::string> * params = nullptr); // same as previous but on external io_service
std::shared_ptr<ClientDestination> CreateNewMatchedTunnelDestination(const i2p::data::PrivateKeys &keys, const std::string & name, const std::map<std::string, std::string> * params = nullptr);
void DeleteLocalDestination (std::shared_ptr<ClientDestination> destination);
std::shared_ptr<ClientDestination> FindLocalDestination (const i2p::data::IdentHash& destination) const;
@@ -95,6 +102,8 @@ namespace client
template<typename Section>
std::string GetI2CPStringOption (const Section& section, const std::string& name, const std::string& value) const; // GetI2CPOption with string default value
template<typename Section>
void ReadI2CPOptionsGroup (const Section& section, const std::string& group, std::map<std::string, std::string>& options) const;
template<typename Section>
void ReadI2CPOptions (const Section& section, std::map<std::string, std::string>& options) const; // for tunnels
void ReadI2CPOptionsFromConfig (const std::string& prefix, std::map<std::string, std::string>& options) const; // for HTTP and SOCKS proxy
@@ -105,6 +114,7 @@ namespace client
void VisitTunnels (Visitor v); // Visitor: (I2PService *) -> bool, true means retain
void CreateNewSharedLocalDestination ();
void AddLocalDestination (std::shared_ptr<ClientDestination> localDestination);
private:

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