Compare commits

...

507 Commits

Author SHA1 Message Date
Dimitris Apostolou
fde49b3dab Merge ff0b6a6a6a into 5ff52e6c93 2025-02-08 15:10:02 +02:00
orignal
5ff52e6c93 Removed rus.i2p from subscriptions 2025-02-08 07:45:04 -05:00
orignal
ef19a85fc0 use correct OBEP inbound tunnel build 2025-02-03 20:41:51 -05:00
orignal
2ce3145195 Build IRC tunnel through low bandwidth routers 2025-02-02 18:05:46 -05:00
orignal
b8d74dab47 recreate tunnels in random order 2025-02-02 16:49:37 -05:00
orignal
e8f5efd156 peers cleanup time variance 2025-02-01 09:42:44 -05:00
orignal
57aa8b3de8 fixed typo 2025-01-31 12:55:09 -05:00
orignal
972b66f9a5 decline transit tunnel to duplicated router 2025-01-31 11:20:39 -05:00
orignal
eadeea76e7 check congestion and random reject short tunnel build requests 2025-01-30 15:35:04 -05:00
orignal
da7d3c55b0 replaced banana.incognet.io reseed by coconut.incognet.io 2025-01-30 08:21:01 -05:00
Dimitris Apostolou
ff0b6a6a6a fix uninitialized variable block.tunnelID 2025-01-30 13:36:53 +02:00
orignal
60d3e4d963 set ack requested flag after second resend attempt 2025-01-29 19:15:12 -05:00
orignal
adc230acde use m_IsSaving flag for saving RouterInfo 2025-01-29 16:02:31 -05:00
orignal
e4ba07a540 persist local RouterInfo in separate thread using seperate buffer 2025-01-29 13:22:15 -05:00
orignal
93ec5ac5c4 rollback 2025-01-28 21:45:26 -05:00
orignal
774c606b09 don't wait for completion explicitly 2025-01-28 21:30:48 -05:00
orignal
1bff42042d check if saving if complete 2025-01-28 21:22:36 -05:00
orignal
daeb177579 save updated local RouterInfo in separate thread 2025-01-28 20:49:36 -05:00
orignal
5d7a062f1b std::mt19937 for random. Congestion update timer variance 2025-01-28 14:47:22 -05:00
orignal
35f7bd5127 don't delete actively use profile. Last persist time 2025-01-28 14:09:25 -05:00
orignal
d411da451a Merge pull request #2152 from rex4539/typo
fix typo
2025-01-27 22:48:40 -05:00
orignal
45bab06f37 cleanup cached addresses 2025-01-27 18:30:22 -05:00
Dimitris Apostolou
588855c6a7 fix typo 2025-01-27 23:04:23 +02:00
orignal
c3fa0ae8cc cache full addresses in memory when requested or received 2025-01-27 13:24:37 -05:00
orignal
bf85a69a2f min peer test version 0.9.62 2025-01-26 17:49:24 -05:00
orignal
72ff0b9fbb shorter ack request interval 2025-01-25 09:02:18 -05:00
orignal
b9c9988ff4 smaller request timeout if sent directly 2025-01-24 13:56:33 -05:00
orignal
1bb5ad22af use std::mt19937 for random. Peer test interval variance 2025-01-23 19:20:20 -05:00
orignal
4fa5cec0dc fixed termination deadlock if SAM session is active 2025-01-23 14:12:52 -05:00
orignal
1e7254dfaa don't delete router's buffer if an update received or connecting 2025-01-22 13:25:11 -05:00
orignal
ca0818af7e drop buffer upon peer disconnect 2025-01-22 12:00:37 -05:00
orignal
b3d09513b8 fixed race condition 2025-01-21 19:38:07 -05:00
orignal
2857a163e9 check last endpoint only if profile is in memory. postpone profile update when connected 2025-01-21 15:03:25 -05:00
orignal
cba7e5350d drop router's buffer after a while without updates 2025-01-20 18:17:41 -05:00
orignal
29a5effabb use std::mt19937 for random numbers in netdb 2025-01-20 13:27:40 -05:00
orignal
39e07ac265 don't load router profile in NTCP2 or SSU2 thread when check for duplicates 2025-01-20 11:58:33 -05:00
orignal
57986bd348 postpone updating router profile after tunnel build. Check profiles only in memory 2025-01-19 19:16:34 -05:00
orignal
5e301937f2 use pointer to whole struct instead publicKey for buffer 2025-01-19 15:22:46 -05:00
orignal
4edde333ad don't drop router buffer if connected or being updated 2025-01-19 11:47:32 -05:00
orignal
c600b834e3 postpone reading from file and updating router profile 2025-01-18 18:26:16 -05:00
orignal
b6319d78bf don't delete buffer of connected routers 2025-01-16 19:06:33 -05:00
orignal
e4fc2789fe Merge pull request #2149 from rex4539/var
Fix uninitialized variables
2025-01-16 14:21:18 -05:00
Dimitris Apostolou
4c5a1e064d Fix uninitialized variables 2025-01-16 17:54:38 +02:00
orignal
4bb82110ab don't create EVP_CIPHER_CTX for each ChaCha20 2025-01-15 21:13:50 -05:00
orignal
8c555fe592 copy fragment faster 2025-01-14 13:30:47 -05:00
orignal
5f1c599f81 fixed warning 2025-01-13 21:37:40 -05:00
orignal
f2b5606583 store fragments inside m_OutOfSequenceFragments 2025-01-13 20:36:27 -05:00
orignal
08a680b53d use std::string_view instead const std::string& 2025-01-12 18:36:35 -05:00
orignal
634ceceb1c use std::string_view instead const std::string& 2025-01-12 12:23:26 -05:00
orignal
efd8e6e65b use string_view in ExtractString and PutString 2025-01-11 22:34:18 -05:00
orignal
915429bb49 don't drop routing path if no data received 2025-01-10 11:16:07 -05:00
orignal
3e3e0e0a62 shorter ack request interval for datagrams 2025-01-08 20:52:38 -05:00
orignal
c023051fe4 Merge pull request #2147 from Vort/xp_fix2
fix Windows XP build
2025-01-07 16:42:02 -05:00
Vort
0b788de627 fix Windows XP build 2025-01-07 22:15:08 +02:00
orignal
fce4fab071 configurable shared local destination 2025-01-07 13:58:19 -05:00
orignal
3236de0d5a reduce publishing confimation intervals 2025-01-06 19:36:15 -05:00
orignal
18707dd844 don't recalculate and process ranges for every Ack block 2025-01-03 22:04:09 -05:00
orignal
fc16a70f7b use AEADChaCha20Poly1305Encryptor and AEADChaCha20Poly1305Decryptor for test 2025-01-02 18:30:16 -05:00
orignal
619ec5d9c1 fixed AEAD/Chacha20/Poly1305 test 2025-01-02 09:04:57 -05:00
r4sas
1293e122bc [deb] update patch
Signed-off-by: r4sas <r4sas@i2pmail.org>
2024-12-30 21:48:57 +00:00
orignal
24bcc651e0 Fixed typo 2024-12-29 17:44:32 -05:00
orignal
8713974f40 2.55.0 2024-12-29 17:25:54 -05:00
orignal
d48bf33fc5 request time in milliseconds. shorter intervals. interval variance 2024-12-23 17:52:14 -05:00
orignal
0f14f9a302 LeaseSet request timeout in milliseconds 2024-12-23 13:47:38 -05:00
orignal
55708d2a6d reduced LeaseSet lookup timeout 2024-12-22 16:09:58 -05:00
orignal
3bdfa5562b don't send same message twice 2024-12-20 19:42:25 -05:00
orignal
3995448014 fixed possible crash at shutdown 2024-12-19 14:24:26 -05:00
orignal
7497741846 fixed possible crash at shutdown 2024-12-18 14:22:05 -05:00
orignal
36939898fe send tunnel endpoint data to transport session to gateway directly 2024-12-17 20:50:54 -05:00
orignal
b4bcd9914a show next peer and connectivity on transit tunnels page 2024-12-16 19:49:14 -05:00
orignal
833e0a936e fixed build with boost 1.87 2024-12-15 18:27:24 -05:00
orignal
bdc5eaa824 fixed build with boost 1.87 2024-12-15 18:15:21 -05:00
orignal
e76d09e1a1 send tunnel participant data to transport session directly. Implemented TunnelTransportSender 2024-12-15 18:03:31 -05:00
orignal
3264704a23 Handle choked, new RTO and window size calculation 2024-12-14 17:59:51 -05:00
orignal
cec68a2447 rollback 2024-12-11 21:33:16 -05:00
orignal
73ba1afc20 don't create EVP_CIPHER_CTX for each AEAD/Chacha20/Poly1305 message 2024-12-11 18:55:30 -05:00
orignal
dcbe6cfaf2 Update RTO calculation and windows reseting algorithm 2024-12-10 17:49:11 -05:00
orignal
3534b9c499 don't create EVP_CIPHER_CTX for each AEAD/Chacha20/Poly1305 message 2024-12-09 20:59:59 -05:00
orignal
a7021a8283 Merge pull request #2136 from Vort/xp_fix
fix Windows XP build
2024-12-09 08:00:01 -05:00
Vort
946e523554 fix Windows XP build 2024-12-09 02:49:43 +02:00
r4sas
cdd528c51f [gha] disable winxp build
Signed-off-by: r4sas <r4sas@i2pmail.org>
2024-12-08 23:50:16 +03:00
orignal
1a748aebf1 removed depereated section from config 2024-12-08 11:33:30 -05:00
orignal
f23a7f569b pass iv to AES Encrypt/Decrypt directly. aes-test added 2024-12-08 11:08:17 -05:00
orignal
48b62340cc exclude AESNI option 2024-12-07 15:27:23 -05:00
orignal
65da550d19 fix bug with unexpected stream closing 2024-12-07 15:03:18 -05:00
orignal
786da057f2 always use openssl for AES 2024-12-06 20:25:22 -05:00
orignal
097813a6ca Merge pull request #2131 from rex4539/typos
Fix typos
2024-12-06 20:15:25 -05:00
Dimitris Apostolou
226257aa71 Fix typos 2024-12-06 17:11:31 +02:00
orignal
13604ccbb6 Changing the window calculation algorithm, increasing the minimum window size 2024-12-05 22:15:11 -05:00
orignal
e996db03c0 process SessionConfirmed in establisher's thread 2024-12-05 20:57:40 -05:00
orignal
f79a2e81ff calculate data phase keys after verification 2024-12-04 18:36:57 -05:00
orignal
4b1ac7420c Merge branch 'openssl' of https://github.com/PurpleI2P/i2pd into openssl 2024-12-02 19:06:04 -05:00
orignal
e518b92a89 calculate X_I2P_DEST* headers once for series of HTTP requests 2024-12-02 19:05:12 -05:00
R4SAS
1a32ed9088 [gha] winxp: fix option order 2024-12-03 00:44:14 +03:00
R4SAS
b17bbd754a [gha] winxp: forced overwrite files from boost package 2024-12-03 00:40:30 +03:00
orignal
7b0ff2850c close session if x25519 fails 2024-12-01 16:53:08 -05:00
orignal
31ff0ff1cb use weak_ptr for transport session 2024-11-29 21:29:03 -05:00
orignal
fcc70025fd use reference instead naked pointer to tunnel in tunnel gateway 2024-11-29 11:31:13 -05:00
orignal
56145d0f3c bind tunnel gateway to transport session 2024-11-28 21:56:26 -05:00
orignal
8b9f427aa4 handle session handshakes in separate thread 2024-11-27 18:31:58 -05:00
orignal
cc768de8ea iterator through resolver's results 2024-11-25 19:16:42 -05:00
orignal
ffd18baf30 support boost 1.87 2024-11-25 16:00:06 -05:00
orignal
3474538697 replaced boost::asio::io_service by boost::asio:io_context 2024-11-25 10:08:27 -05:00
orignal
5f1b31213f more adequate initial RTT 2024-11-25 08:12:40 -05:00
orignal
6fb3c7c3ba removed dependancy from boost_system for newer compliers 2024-11-23 18:34:33 -05:00
orignal
a248a2a732 Show TBM Queue size 2024-11-20 13:28:01 -05:00
orignal
09ae278306 const GetSize() 2024-11-20 13:27:25 -05:00
orignal
d241e5d5cb handle transit tunnel build messages in separate thread 2024-11-19 19:11:09 -05:00
orignal
b80278421d re-create ECIES session for follow-on packets 2024-11-19 13:00:13 -05:00
orignal
f2596e0187 fixed typo with cleanup timer expiration 2024-11-18 21:34:41 -05:00
orignal
5265dc71e9 drop too old LeaseSet or from future 2024-11-18 15:49:11 -05:00
orignal
a05bb93792 check LeaseSet expiration time 2024-11-18 12:16:05 -05:00
orignal
5d5970bed4 more SYN resend attempts for outgoing stream 2024-11-18 07:59:39 -05:00
orignal
86080b26ae terminate non-established sessions sortly 2024-11-17 22:11:30 -05:00
orignal
5b2d0c579b close stream if SYACK is not acked 2024-11-17 20:51:59 -05:00
orignal
3c4926f377 limit number of incoming ECIES sessions. Don't try to create ECIES session for incoming stream 2024-11-17 18:53:21 -05:00
orignal
391e3b7814 don't schedule send for first SYN reply 2024-11-17 17:29:04 -05:00
orignal
72a39609ed moved all transit tunnels code to TransitTunnels class 2024-11-16 20:56:35 -05:00
orignal
0c5f39ad81 separate class TransitTunnelBuildMsgHandler for tunnel build messages 2024-11-16 16:05:46 -05:00
orignal
3c608ec07c received garlic messages queue for destination 2024-11-13 21:17:58 -05:00
r4sas
ce0461bf86 Destination: cut name for thread name
Signed-off-by: r4sas <r4sas@i2pmail.org>
2024-11-12 21:09:21 +00:00
orignal
ce96f93c80 cleanup tags and ECIES sessions more often 2024-11-11 21:59:19 -05:00
orignal
d88ba768d7 set i2p.streaming.profile=2 for shared local destination 2024-11-11 18:50:53 -05:00
r4sas
574d12298b Destinations: set thread name from tunnel name
Signed-off-by: r4sas <r4sas@i2pmail.org>
2024-11-12 00:36:07 +03:00
orignal
7285caa4f1 if i2p.streaming.maxConcurrentStreams is zeor or negative than unlimited 2024-11-11 14:43:20 -05:00
orignal
2778b092e3 i2p.streaming.maxConcurrentStreams I2CP param 2024-11-11 13:41:27 -05:00
orignal
dbef3fe9d2 temirminate incoming right a way if no remote LeaseSet 2024-11-10 20:15:50 -05:00
orignal
09b7d44dad delete ECIESX25519 session without destination shortly 2024-11-10 19:15:44 -05:00
orignal
a411fff1d9 limit number of incoming streams. don't request LeaseSet for incoming stream 2024-11-10 16:49:44 -05:00
orignal
e574354896 limit received SSU2 packets queue 2024-11-10 11:32:46 -05:00
orignal
0a08383471 check msg size in HandleTunnelBuildResponse 2024-11-10 09:15:23 -05:00
orignal
c5e464a8b5 move tunnel build request/reply code from I2NPProtocol.cpp to Tunnel.cpp 2024-11-09 17:25:43 -05:00
orignal
002d8c7773 removed HidUser's reseed 2024-11-08 15:53:01 -05:00
orignal
32921ead80 move transit tunnel build requests from I2NPProtocol.cpp to TransitTunnel.cpp 2024-11-07 19:00:11 -05:00
orignal
be24a3e336 publish R cap for yggdrasil-only router and U cap for routers through proxy 2024-11-05 19:24:54 -05:00
orignal
d99a7d9b20 allow transit for router behind symmetric NAT or proxy 2024-11-05 15:20:05 -05:00
orignal
2f6bdd1c84 limit last decline time by 1.5 hours. Increased declined recently interval to 5.5 minutes 2024-11-04 18:20:46 -05:00
John
5a4ce66d42 debian/changelog: Add leading space 2024-11-03 21:44:54 +03:00
orignal
76190ea365 don't resend RelayReponse if Alice is older version 2024-11-03 11:25:19 -05:00
orignal
f90386803f Resend RelayResponse if relay tag not found. Send Ack block 2024-11-03 11:03:12 -05:00
orignal
29d77113cc memory pool for x25519 keys 2024-11-02 11:20:23 -04:00
orignal
0d09a8be00 removed own implementation of x25519 2024-11-01 17:53:27 -04:00
orignal
b8d61e04f0 generate x25519 keys more often 2024-11-01 14:46:13 -04:00
orignal
4432c5a2c4 update last activity time after sending peer test or hole punch message 2024-10-31 18:24:02 -04:00
orignal
2419f52af4 fixed potential race condition 2024-10-31 15:01:19 -04:00
orignal
b2a10ac82b don't update found router again in RequestComplete 2024-10-30 22:00:12 -04:00
orignal
0086f8e27a use std::async for address book download 2024-10-29 15:32:06 -04:00
orignal
8a8277edda check for empty URL string 2024-10-29 13:59:21 -04:00
orignal
3f10f6651d use splice if queue is not semi-full 2024-10-29 12:46:14 -04:00
orignal
9bc595a9a2 eliminate extra copy 2024-10-29 08:41:59 -04:00
orignal
f04048717d cleanup messages to send if session was terminated 2024-10-28 21:34:33 -04:00
orignal
361f364966 intermediate queue for transport sessions. use std::list instead std::vector for multiple I2NP messages 2024-10-28 21:15:16 -04:00
orignal
4c90a88b85 eliminate extra copy of I2NP messages list 2024-10-28 21:10:30 -04:00
orignal
23e66671c2 intermediate queue for transport sessions. use std::list instead std::vector for multiple I2NP messages 2024-10-28 20:36:50 -04:00
orignal
ec67f48d85 fixed possible memory leak 2024-10-28 08:46:01 -04:00
orignal
3a229ea65c Merge pull request #2117 from mittwerk/openssl
hardening iterator
2024-10-28 08:39:44 -04:00
orignal
0e8d624d86 move UpdatePacingTime out of loop 2024-10-28 08:38:04 -04:00
mittwerk
8f9874570a hardening iterator 2024-10-28 09:11:09 +02:00
orignal
43939cedf4 random tunnel reject when medium congestion 2024-10-27 22:19:06 -04:00
orignal
4c66608caf random tunnel reject when medium congestion 2024-10-27 21:58:19 -04:00
orignal
ec4fe9a1e6 set congesion cap G if symmetric NAT and ipv4 in only transport 2024-10-27 18:17:28 -04:00
orignal
79e8ccbb5b don't handle PeerTest 1 with same nonce twice 2024-10-27 12:24:22 -04:00
orignal
608056dcd2 don't handle RelayRequest and RelayIntro with same nonce twice 2024-10-27 11:55:10 -04:00
orignal
7461b640e3 reduce CPU usage 2024-10-26 19:26:25 -04:00
orignal
743126b2ad better hole punch expiration intervals 2024-10-26 19:05:08 -04:00
orignal
f611136ea7 resend relay reponnse if remote router >= 0.9.64 2024-10-26 15:30:48 -04:00
orignal
87ae9c4b74 call main thread as i2pd-daemon 2024-10-25 18:40:51 -04:00
orignal
d3630fb2b2 assign name to main thread 2024-10-25 13:25:33 -04:00
orignal
500afe745f use min hole punch interval for connection attempts 2024-10-24 18:49:11 -04:00
orignal
26901e2945 try recently connected SSU2 address if no other transports found 2024-10-23 20:39:00 -04:00
orignal
64bde69967 Merge pull request #2108 from SystemFailureNet/openssl
Please disable LibreSSL workaround when LibreSSL version >= 4.0.0
2024-10-21 21:38:50 -04:00
orignal
ddf30784ec connected recently mutex 2024-10-21 21:22:16 -04:00
orignal
ea14b00d63 save router's endpoint to profile and try to use it next time without requesting introducers 2024-10-21 20:58:09 -04:00
orignal
a24e0eb2dc don't delete unreachable routers if no transports 2024-10-20 16:12:35 -04:00
orignal
0cb677a2c0 don't send peer test msg 6 if remote endpoint is unknown 2024-10-19 09:18:31 -04:00
orignal
e6cbc842bf request new leaseset if all leases are about to expire 2024-10-19 08:45:25 -04:00
orignal
f087654f25 fixed warnings 2024-10-18 20:02:41 -04:00
orignal
10335b90c5 fixed warnings 2024-10-18 19:57:35 -04:00
orignal
8a234f70e6 send a packet to new remote lease in advance if current is about to expire 2024-10-18 15:59:37 -04:00
SystemFailure
f98a310235 Revert LibreSSL workaround when LibreSSL version >= 4.0.0 2024-10-18 13:17:47 +00:00
orignal
1419745a5d recognize symmetric NAT from peer test msg 7 2024-10-17 21:09:37 -04:00
orignal
890fe77b10 update leaseset in destination's thread 2024-10-17 18:15:33 -04:00
orignal
bc9d25ec3b ability post LeaseSet update to destination's thread 2024-10-17 16:20:27 -04:00
orignal
fe71776b6f update LeaseSet if inbound tunnel was restored 2024-10-17 15:44:26 -04:00
orignal
0213f058d1 Send peer test msg 6 with delay if msg 4 was received before msg 5 2024-10-16 21:19:30 -04:00
orignal
0ccf0a6339 use pointer to RouterInfo in SaveUpdated 2024-10-16 17:57:52 -04:00
orignal
e26682f4cb don't try to save invalid router 2024-10-16 15:05:29 -04:00
orignal
8981e406f5 don't delete RouterInfo's buffer right a way 2024-10-16 13:51:48 -04:00
orignal
50d9252ba9 resend HolePunch 3 times or until SessionRequest received 2024-10-16 13:10:21 -04:00
orignal
4f73f60e51 don't create relay response block twice 2024-10-16 12:07:13 -04:00
orignal
d69e957213 rollback 2024-10-16 09:04:35 -04:00
orignal
97fdedfbe3 implement SSU2HolPunchSession 2024-10-16 08:28:25 -04:00
orignal
ec1f41b13c insert multiple packets to the queue using splice 2024-10-15 15:05:18 -04:00
orignal
7104d334fd Do not increase the window size if the speed limit is reached when it is limited 2024-10-14 22:29:55 -04:00
orignal
4e581af3ba plain list of received packets in queue 2024-10-14 21:21:07 -04:00
orignal
48f7131a7d received packets queue 2024-10-14 18:55:41 -04:00
orignal
fbd07a5276 SSU2Session/SSU2OutOfSession split 2024-10-13 19:53:40 -04:00
orignal
8210911bc5 use std::list and splice fr msg queue 2024-10-12 17:51:26 -04:00
orignal
4a5406b803 lock queue's mutex less often 2024-10-11 13:41:37 -04:00
orignal
ab02f722af print non-resolved address to log 2024-10-11 11:27:36 -04:00
orignal
c86e0ec371 lock queue's mutex less often 2024-10-10 20:43:06 -04:00
orignal
ac4c58bbe9 reload cerificate again after re-creation attempt 2024-10-10 15:46:22 -04:00
orignal
23bac4a403 recreate certificate if invalid 2024-10-09 21:40:26 -04:00
orignal
2321a897f5 rollback 2024-10-09 08:48:24 -04:00
orignal
88a5f8b125 use EVP_PKEY for signing 2024-10-08 18:44:46 -04:00
orignal
78847306e9 use EVP_PKEY for family signature verification 2024-10-08 13:44:25 -04:00
orignal
1a6109109a don't sample too small list of eligible introducers 2024-10-06 20:57:35 -04:00
r4sas
905c6debf2 [win32] use boost filesystem for gcc builds
Signed-off-by: r4sas <r4sas@i2pmail.org>
2024-10-06 20:25:55 +03:00
r4sas
d7c4d0ff3e [gha] xp build again
Signed-off-by: r4sas <r4sas@i2pmail.org>
2024-10-06 16:36:52 +03:00
orignal
da3e83138a Merge branch 'openssl' of https://github.com/PurpleI2P/i2pd into openssl 2024-10-06 07:43:57 -04:00
orignal
c6eba73653 Merge branch 'master' of https://github.com/PurpleI2P/i2pd into openssl 2024-10-06 07:43:45 -04:00
orignal
0d224dfc54 2.54.0 2024-10-06 07:42:59 -04:00
r4sas
dc48fb0180 [rpm] try to fix Fedora ELN build
Signed-off-by: r4sas <r4sas@i2pmail.org>
2024-10-06 14:36:30 +03:00
orignal
cc05f9c5d9 2.54.0 2024-10-06 07:34:18 -04:00
r4sas
e4c8cc300d [gha] disable winxp build (it is broken in MSYS2)
Signed-off-by: r4sas <r4sas@i2pmail.org>
2024-10-06 14:28:33 +03:00
orignal
0710f62948 fixed potential race condition 2024-10-04 20:44:58 -04:00
orignal
58245bf121 temporary disable RelayRespond resend from Bob because it might be not acked 2024-10-04 14:06:02 -04:00
orignal
4436c49ccc temporary disable RelayResponse resend through introducer session 2024-10-04 11:30:56 -04:00
orignal
bce9630ff8 try to create new sessions with introducers if existing are about to expire 2024-10-04 08:26:32 -04:00
orignal
7f3a04a72f select random introducer session. don't update creation time 2024-10-03 18:44:09 -04:00
orignal
34f1ba5bd9 don't send invalid local address in RelayRequest 2024-10-02 12:45:12 -04:00
orignal
dc4cd34893 handle immediate ack requsted flag in data message. set it in keep-alive 2024-10-02 08:45:44 -04:00
orignal
1fb45c4b0d don't send HolePunch or PeerTest 5 to unspecified address 2024-10-02 08:27:49 -04:00
orignal
514be6d048 introducer expiration time variance 2024-10-01 21:26:54 -04:00
orignal
8c292727da introducer duration variance 2024-10-01 20:35:46 -04:00
orignal
d5c40bb6be send keep-alive for newly selected introducer session 2024-10-01 18:21:07 -04:00
orignal
eed48c43fd don't change Firewalled status to Unknown if peer test error 2024-10-01 15:29:48 -04:00
orignal
600f36539f don't change ConnIDs of just introduced session. Let Charlie recognize SessionRequest 2024-09-30 22:38:42 -04:00
orignal
e1e530b4a9 never send Ack to HolePunch and PeerTest messages 2024-09-30 18:27:13 -04:00
orignal
98e93468a6 send ack to relay messages 2024-09-30 18:12:42 -04:00
orignal
0f5e8d8424 don't print warning if duplicated nonce or peer test 2024-09-30 14:54:20 -04:00
orignal
d521350588 resend relay response 2024-09-29 22:15:03 -04:00
orignal
237d9474d8 fixed incomplete response 2024-09-29 21:43:47 -04:00
orignal
5466983b36 resend relay messages 2024-09-29 20:57:18 -04:00
orignal
ba41f7107d resend peer test responses 2024-09-29 18:17:49 -04:00
orignal
c2234599cd exclude boost_system from linking because it's headers only now 2024-09-29 17:11:54 -04:00
orignal
6ebb019e15 resend peer test msgs 5 and 6 2024-09-29 15:02:18 -04:00
orignal
15cd4feade move Bob's peer tests from SSU2 session to server 2024-09-28 22:05:25 -04:00
orignal
abbe1fea64 fixed clang build error 2024-09-28 16:20:59 -04:00
orignal
62b811c2c1 use memory pool for SSU2 received packets arrays 2024-09-28 09:49:45 -04:00
orignal
64e4b3871a update introducer's iTag is session to introducer was replaced to new one 2024-09-27 13:32:20 -04:00
orignal
c3a1631319 use weak_ptr for Bob's peer tests and relay tags 2024-09-26 18:38:17 -04:00
orignal
a06cce0aaf eliminate extra copy of vector of SSU2 packets 2024-09-26 15:54:29 -04:00
orignal
75b1c144b4 drop too short follow on SSU2 packets 2024-09-26 08:48:17 -04:00
orignal
32ad4b4858 fixed possible race conditions with m_SessionsByRouterHash 2024-09-25 14:34:52 -04:00
orignal
98669eff4f delete session by hash from table if expired or terminated 2024-09-25 11:13:01 -04:00
orignal
67763248cc add peer test session endpoint to connected recently after msg 6 and 7 2024-09-24 22:06:44 -04:00
orignal
262a803d10 make sure we are done with session before remving it 2024-09-24 20:57:04 -04:00
orignal
0912de5b77 don't connect peer test session. Use weak_ptr for seesions by hash 2024-09-24 20:03:15 -04:00
orignal
edb2ba7107 set address when peer test msg 4 received 2024-09-24 15:20:10 -04:00
orignal
74f0330730 moved SendPeerTest for msgs 5,6,7 to SSU2PeerTestSession 2024-09-24 14:37:27 -04:00
orignal
5cd0248494 set router status to uknown if peer test msg 5 came from recently connected peer 2024-09-23 20:24:33 -04:00
orignal
816771dd00 fixed build for gcc 8-9 2024-09-23 18:18:26 -04:00
orignal
189d7179c0 check if msg 5 was received instead state 2024-09-23 15:34:14 -04:00
orignal
2dfc9003a7 separate and move own peer test to SSU2Server 2024-09-23 14:16:24 -04:00
orignal
9968afc038 check senduseragent for outproxy. Update User-Agent for clearnet 2024-09-22 21:27:09 -04:00
orignal
5073c9637e implement httpproxy.senduseragent 2024-09-22 21:07:44 -04:00
orignal
2c594dc67a moved peer test 5-7 to SSU2PeerTestSession 2024-09-22 20:25:41 -04:00
orignal
11bca5c3cd don't initialize Noise state for peer test sessions 2024-09-22 18:02:12 -04:00
orignal
9d1e526812 separate SSU2PeerTestSession for peer tests msgs 5,6 and 7 2024-09-22 17:22:08 -04:00
orignal
018fa0ec00 added i2p.streaming.maxOutboundSpeed, i2p.streaming.maxInboundSpeed and i2p.streaming.profile to HTTP and SOCKS proxy configs 2024-09-21 19:48:45 -04:00
orignal
f733f0a636 added i2p.streaming.maxOutboundSpeed, i2p.streaming.maxInboundSpeed and i2p.streaming.profile to HTTP and SOCKS proxy configs 2024-09-21 19:04:03 -04:00
orignal
fd2b15fe81 don't drop too old router if low uptime 2024-09-21 17:58:14 -04:00
orignal
c8958d71a2 pick routers with any bandwidth if limited connectivity 2024-09-21 12:59:11 -04:00
orignal
e4962b855f pick first hop based on pool's bandwidth requirements 2024-09-20 10:34:55 -04:00
orignal
9f30499984 implement i2p.streaming.profile to specify high or low bandwidth tunnel pools 2024-09-19 21:16:16 -04:00
orignal
5324197e43 don't change router status if peer test came from recent endpoint 2024-09-19 15:27:04 -04:00
orignal
715e063550 set boost flags before finding 2024-09-19 08:02:37 -04:00
orignal
7ef1fdf634 exclude false position OK peer test if comes from recently connected endpoint 2024-09-18 18:48:18 -04:00
orignal
db19c32381 require minimal boost 1.83 for c++20 2024-09-18 15:42:30 -04:00
orignal
ac1c28cb39 don't send ack in case of lost packet and incoming speed limitation 2024-09-18 15:19:18 -04:00
orignal
2fa4237acd renew connected receintly timestamp, add endpoint to the list if hole punch is being sent 2024-09-18 14:35:59 -04:00
orignal
ae26758170 allocate RouterInfo's buffer from pool 2024-09-18 08:40:22 -04:00
orignal
a723405fb0 check max RouterInfo size 2024-09-18 08:38:29 -04:00
orignal
f20391d460 check if we connected recently to an endpoint before sending peer test 2024-09-17 21:49:23 -04:00
orignal
ca4db7aab2 handle siuatuion if only one lease in remote LeaseSet 2024-09-17 20:56:00 -04:00
orignal
13b2fc3266 drop window size only when lease changes 2024-09-16 19:09:18 -04:00
orignal
d4c1a1c0bb cleanup streaming destination's pools 2024-09-16 13:39:11 -04:00
orignal
d5aca85a35 don't try to generate more ECIES-X25519-AEAD-Ratchet tags if decryption failed 2024-09-12 21:03:46 -04:00
orignal
17d0e59d02 fixed warning 2024-09-12 20:30:26 -04:00
orignal
d20475e3d0 set default i2cp.leaseSetEncType to 0,4 and to 4 for server tunnels 2024-09-12 19:11:13 -04:00
orignal
ebec4d8a5e set default i2cp.leaseSetEncType to 0,4 and to 4 for server tunnels 2024-09-12 18:56:23 -04:00
orignal
cb0801fc16 reduce number of retransmits 2024-09-11 20:54:22 -04:00
R4SAS
a5e9d9c6a3 [gha] winxp build fix 2024-09-11 20:18:05 +03:00
orignal
3d0a1afd64 check if addressbook is enabled 2024-09-11 12:06:55 -04:00
orignal
78ec5b2c6e faster RTT recalculation if bad sample 2024-09-11 11:24:51 -04:00
orignal
272bf7dbc1 terminate NTCP2 session from duplicated router properly 2024-09-10 12:35:36 -04:00
orignal
261acbbd66 recalculate RTT for one way communications 2024-09-10 12:22:42 -04:00
orignal
a65dd218da correct endianess in SendQuickAck 2024-09-10 10:27:26 -04:00
orignal
50d297fa29 check if first packet 2024-09-09 22:37:35 -04:00
orignal
699e17b594 handle plain ack with options 2024-09-09 22:26:03 -04:00
orignal
a91caa6559 Merge branch 'openssl' of https://github.com/PurpleI2P/i2pd into openssl 2024-09-09 18:40:42 -04:00
orignal
07d108bb6f send immediate ack request if no packet being sent 2024-09-09 18:40:06 -04:00
orignal
bcace3fb29 use -std=c++17 2024-09-08 17:53:41 -04:00
orignal
cd648b9b3f use std::atomic<std::shared_ptr<...>> instead boost::shared_ptr if applicable 2024-09-08 16:30:27 -04:00
orignal
ba451eeca5 set congestion cap G immediately if through proxy 2024-09-07 18:01:48 -04:00
orignal
8d1c186665 limited connectivity mode 2024-09-07 16:25:26 -04:00
orignal
d539c9677e don't accept tunnels if connected through proxy 2024-09-06 19:04:07 -04:00
orignal
855fd4d471 eliminate extra parsing of RouterInfo coming as RouterInfo block 2024-09-06 09:49:24 -04:00
orignal
e0af7b077f set max number of tags adequate to max window size 2024-09-05 20:28:40 -04:00
orignal
fde301deaf check for duplicated routers in NTCP2. Insert router into netdb right a way 2024-09-05 19:24:23 -04:00
orignal
9a77c0a4b1 recognize compiler version without gexpr 2024-09-05 07:43:24 -04:00
orignal
306ea2df37 don't apply std::move to prvalue 2024-09-04 14:12:30 -04:00
orignal
cab671e177 use gexpr instead expr 2024-09-04 13:28:34 -04:00
orignal
2ee5af0c06 C++20 for clang >= 16 2024-09-04 13:23:12 -04:00
orignal
911620bcd3 C++20 for clang >= 16 2024-09-04 13:21:13 -04:00
orignal
d1620d70bb consider N routers as low bandwidth 2024-09-03 18:43:25 -04:00
orignal
53db54dafb consider N routers as low bandwidth 2024-09-03 18:36:50 -04:00
orignal
ead1b72886 set half of window on remote lease change 2024-09-03 15:20:33 -04:00
orignal
ae65af07c2 handle immediate ack request 2024-09-03 13:00:04 -04:00
orignal
0046a8b3ec pass const strings for HTTP headers 2024-09-02 21:05:40 -04:00
orignal
06e3a1b57a fixed typo 2024-09-02 18:32:52 -04:00
orignal
cc59003560 removed C++17 check 2024-09-02 18:22:33 -04:00
orignal
a3e0b3710c pass std::string_view to parse 2024-09-02 17:34:15 -04:00
orignal
56b8534e0c gcc7 support 2024-09-02 15:00:25 -04:00
orignal
c21cf0565b removed C++11 support 2024-09-02 10:55:22 -04:00
orignal
9668ea9338 removed C++11 support 2024-09-02 10:18:08 -04:00
orignal
a837e5c502 use rng from pool for lease selection 2024-09-01 16:39:19 -04:00
orignal
bbadbdbfdb Switch to C++17 for Mac OS X 2024-09-01 15:23:14 -04:00
orignal
509c039e2f use Rng from pool if possible 2024-08-31 08:02:56 -04:00
orignal
8cf9cc1a01 removed dependency from boost::filesystem 2024-08-30 18:25:16 -04:00
orignal
a1f40d3048 use __has_include(<filesystem>)) to detect if std::filesystem can be used 2024-08-30 17:56:34 -04:00
orignal
83c0764ed4 link with stdc++fs for g++8 and g++9 2024-08-30 17:54:56 -04:00
orignal
2f5f39aaf2 disable clock_cast iuntil implemented 2024-08-30 17:51:03 -04:00
orignal
5cc15fac31 rollback 2024-08-30 15:03:50 -04:00
orignal
ea3f356856 removed dependency from boost::filesystem 2024-08-30 14:40:44 -04:00
orignal
8189ff0f48 innclude <filesystem> before _cpp_lib_filesystem check 2024-08-30 14:36:56 -04:00
orignal
2679e8cfd8 removed dependency from boost::filesystem 2024-08-30 13:28:01 -04:00
orignal
3679c6aea0 switch to C++17 2024-08-30 11:03:06 -04:00
orignal
604bdf314f improved window size reculculation algorithm 2024-08-29 18:57:14 -04:00
orignal
937809bc0f don't use clock_cast with clang 2024-08-29 15:14:17 -04:00
orignal
d71f3d40fa Merge pull request #2096 from Vort/utf8
implement UTF-8 conversion with WinAPI for Windows platform
2024-08-29 12:42:25 -04:00
Vort
e87ace0c3d implement UTF-8 conversion with WinAPI for Windows platform 2024-08-29 16:52:25 +03:00
orignal
bc48e6881d use boost::filesystem for Win32 and clang 2024-08-28 18:25:42 -04:00
orignal
e957d7bbfb use std::filesystem for windows 2024-08-28 17:52:08 -04:00
orignal
b3aa5ad998 don't link with boost::filesystem in newer versions 2024-08-28 15:12:29 -04:00
orignal
ac876a0cd5 use boost::filesystem for Mac OS X 2024-08-28 13:43:01 -04:00
orignal
d85cb6e30a fixed build for Debian Bookworm 2024-08-28 12:01:19 -04:00
orignal
4a4b76141a C++20 support 2024-08-28 11:34:07 -04:00
orignal
a93043f064 check for __cpp_lib_filesystem 2024-08-27 22:12:13 -04:00
orignal
ae309ca632 use std::filesystem for C++17 2024-08-27 21:49:23 -04:00
orignal
9037e8b2b1 Merge pull request #2095 from PurpleI2P/openssl
recent changes
2024-08-27 21:41:15 -04:00
orignal
3ff79038b5 handle individual NACKs 2024-08-27 15:33:59 -04:00
orignal
da0e527777 termination check timeout variance 2024-08-26 20:57:28 -04:00
orignal
66223792f3 use std::mt19937 instead rand(). termination timeout variance 2024-08-26 19:35:13 -04:00
orignal
a69eade1f4 use pool's rng for random tunnel/remote lease selection 2024-08-25 21:35:33 -04:00
orignal
0992a5124f removed dependency from boost::date_time 2024-08-25 20:18:55 -04:00
orignal
e7423b1ffc save timestamp from epoch instead local time to profiles 2024-08-25 19:07:01 -04:00
orignal
65ceb08290 correct translation of Network status 2024-08-23 22:35:52 -04:00
orignal
879d54fad4 replaced boost::date_time by functions from std 2024-08-23 22:01:08 -04:00
orignal
ff5c76f8f2 don't include expired lease to LeaseSet for I2Cp 2024-08-21 19:21:02 -04:00
orignal
0191e58b05 adjust number of leases in LS2 if expired tunnels 2024-08-21 16:28:19 -04:00
orignal
c43926083e don't include already expired lease to LeaseSet 2024-08-21 14:07:04 -04:00
orignal
bd98f2c3ee max flush interval 2024-08-20 21:21:28 -04:00
orignal
02c52f59cb max flush interval 2024-08-20 20:59:41 -04:00
orignal
fa218d3cf5 reset routing path if session is stuck 2024-08-20 15:50:37 -04:00
orignal
d169b422da drop window if remote lease changes 2024-08-19 19:10:00 -04:00
orignal
7be64dad89 limit number of resent packets at the time 2024-08-19 18:30:49 -04:00
orignal
3720a5fce3 don't select same peer too often 2024-08-19 15:51:56 -04:00
orignal
0df895b6a7 check minimal router version for connected peer selection 2024-08-19 14:39:07 -04:00
orignal
32ab95478e check crypto type and congestion of connected peer for tunnel 2024-08-18 18:34:28 -04:00
orignal
28adb54c0a don't send quick ack for each duplicated packet 2024-08-18 13:33:16 -04:00
orignal
b4fcf76480 fixed warning 2024-08-17 17:52:42 -04:00
orignal
fb8e0e1b5b limit stream's inbound speed 2024-08-17 17:11:28 -04:00
orignal
41dd8b527d reduce number of acks being sent 2024-08-17 08:30:16 -04:00
orignal
7376f7c399 generate max number of tags for follow on tagsets 2024-08-17 08:13:17 -04:00
orignal
d47ae3012a periodic sending ack requests in ECEISX25519 session and dead path detection in I2CP 2024-08-15 13:35:51 -04:00
orignal
09dbe9fc03 check option and packet length 2024-08-14 13:43:24 -04:00
orignal
11328a429d set minimal resend interval. Resend attempt after only Ack 2024-08-14 10:13:35 -04:00
orignal
0c924836cf fixed AEAD verification for LibreSSL 2024-08-13 15:36:13 -04:00
orignal
52a313bb65 force LeaseSet timestamp update if published at the same second 2024-08-12 21:29:05 -04:00
orignal
d75f15104e select only established sessions for peer tests 2024-08-10 14:43:29 -04:00
orignal
b306bf2db9 don't handle connect timer for already terminated session 2024-08-09 08:14:39 -04:00
orignal
349c4e30b6 correct receive ratchet tagsetid calculation 2024-08-06 13:58:21 -04:00
orignal
3c69e0b2af keep sending reverse key until tag received on new tagset 2024-08-05 14:44:10 -04:00
orignal
8e1fb8ca9f send status failure and drop shared routing path if message was not sent 2024-08-02 14:39:04 -04:00
orignal
42782944fb Streaming congestion control improvements. Patch by onon 2024-08-01 13:49:32 -04:00
orignal
efd754eb93 set min pacing time to 250 microseconds 2024-07-31 07:56:40 -04:00
orignal
81cc3e3de8 2.53.1 2024-07-30 11:56:31 -04:00
orignal
db4208e2e2 fixed 2024-07-30 08:13:24 -04:00
orignal
fe740249a5 2.53.1 2024-07-29 19:40:33 -04:00
orignal
4ad6cef5a5 some cleanup 2024-07-29 15:18:52 -04:00
orignal
86f86fc711 fixed #2078. don't stop service when destination stops 2024-07-29 14:44:47 -04:00
orignal
e5dac605f6 send session status crated right a way 2024-07-28 21:59:49 -04:00
orignal
ab1abf584f don't send session status create before destination is ready 2024-07-28 21:48:47 -04:00
orignal
8a3d6ddb3e cleanup I2NP msgs pool and routing sessions 2024-07-28 21:24:10 -04:00
orignal
1410fa5c21 added i2cp.inboundlimit and i2cp.outboundlimit 2024-07-28 14:53:46 -04:00
orignal
ea19d2296c send actual router limits in BandwidthLimitsMessage 2024-07-28 14:34:27 -04:00
orignal
3a4833aa67 Merge pull request #2017 from Vort/show_bw_caps
show bandwidth caps for hops
2024-07-28 08:21:52 -04:00
orignal
4a66624b04 fixed typo 2024-07-27 21:42:07 -04:00
orignal
0153748134 save and use previous routing session 2024-07-27 19:37:10 -04:00
orignal
d3062d2994 don't create full identity from buffer if only ident hash is needed 2024-07-25 20:36:46 -04:00
orignal
d7ff459f12 include tagsetid into msgid 2024-07-25 15:05:00 -04:00
orignal
e0ac8a7298 check if destination is ready. Create garlic message before selectiing routing path 2024-07-24 19:09:46 -04:00
orignal
96ea630274 send message right a way if in same thread 2024-07-24 16:06:28 -04:00
orignal
f232c8f2df don't limit num attempts in routing path. Increased routing path expiration to 2 minutes 2024-07-23 21:55:56 -04:00
orignal
2f54d95187 try to read message payload immediately after header 2024-07-23 19:02:37 -04:00
orignal
830e49f2c5 increase I2CP socket buffer size 2024-07-23 16:01:43 -04:00
orignal
23e323438a cancel LeaseSet creation timer before destination termination 2024-07-23 08:35:43 -04:00
r4sas
48f1514053 [rpm] use eln macro in check
Signed-off-by: r4sas <r4sas@i2pmail.org>
2024-07-21 14:27:00 +00:00
r4sas
a1aa6c62d7 [rpm] fix typo
Signed-off-by: r4sas <r4sas@i2pmail.org>
2024-07-21 14:14:52 +00:00
r4sas
9e1ea289c2 [rpm] require package openssl-devel-engine for build on fedora > 40
Signed-off-by: r4sas <r4sas@i2pmail.org>
2024-07-21 14:09:48 +00:00
orignal
f6ddcd432e removed openssl/engine.h 2024-07-21 09:43:29 -04:00
r4sas
9a6654943d moved binary to /usr/bin (closes #2053 #2068)
Signed-off-by: r4sas <r4sas@i2pmail.org>
2024-07-20 17:25:30 +00:00
orignal
bed5a18294 2.53.0 2024-07-17 07:41:41 -04:00
orignal
45221da1dc floodfill must have published ipv6 if no published ipv4 2024-07-12 11:43:39 -04:00
orignal
8440633614 increased number of floodfills threshold to 1200 2024-07-12 10:44:05 -04:00
orignal
50f455e0a2 make a floodfill eligble if reachable by ipv4 2024-07-12 09:33:37 -04:00
orignal
5fbcfadd6d set min floodfill version to 0.9.59 2024-07-11 15:22:12 -04:00
orignal
5af13849a9 check if offline signature is expired when load keys 2024-07-09 13:44:30 -04:00
orignal
a9c486d7a1 fixed stream hanging due to inactivity. Smoother RTT 2024-07-09 10:51:22 -04:00
orignal
8c0a1197d7 drop incoming SSU2 session from duplicated router 2024-07-08 10:39:18 -04:00
orignal
4e5f5c218a mark router as dup0licted only if address mismatch 2024-07-08 08:00:36 -04:00
orignal
64cc59d1e9 recognize duplicated routers and store duplicated flag in profile 2024-07-07 11:26:17 -04:00
orignal
0c943f4405 use received RouterInfo if older than one in netdb 2024-07-06 10:28:18 -04:00
orignal
37d3d9e604 removed openssl 1.0.2 support 2024-07-06 08:50:51 -04:00
orignal
d23451fdf6 delete unused variable 2024-07-04 13:43:05 -04:00
orignal
d843502832 calculate min pacing time. Return wrongly removed line 2024-07-04 13:28:18 -04:00
orignal
0428b5ece1 limit stream's outbound speed 2024-07-04 13:07:57 -04:00
orignal
df787060c3 stop destination's thread before cleanup 2024-06-30 11:40:35 -04:00
orignal
ea9c69cd53 some cleanup 2024-06-30 08:11:12 -04:00
orignal
199d149bed improved congestion control 2024-06-29 09:17:11 -04:00
orignal
ff8941af71 check if address is still introducer before trying to introduce 2024-06-27 18:02:17 -04:00
orignal
f125936b2e try to lookup other introducers' routers if found out doesn't have applicable addresses 2024-06-26 08:53:04 -04:00
Stefan Strogin
697d831441 Support miniupnp-2.2.8 (fixes #2071)
Use UPNP_GetValidIGD for getting external IP.
2024-06-22 19:37:39 +03:00
orignal
457b64f92d try to connect to introducer through any available address 2024-06-14 18:05:01 -04:00
orignal
6caec6b551 check if updated router is still introducer. Remove non-introducer sessions from introducers list 2024-06-13 18:10:45 -04:00
orignal
362edc68ad removed ls's reseed 2024-06-11 15:14:06 -04:00
orignal
29872fc003 check if local address exists 2024-06-10 17:58:19 -04:00
orignal
81d383c99e don't pick routers older that 0.9.58 for client tunnels 2024-06-10 13:40:07 -04:00
orignal
12653f2fe4 don't try introducer with invalid address. Terminate session immediately if appropriate introducer not found 2024-06-09 18:38:13 -04:00
orignal
43f5ba286c update congestion caps before initial publishing 2024-06-09 15:25:19 -04:00
orignal
f990a2f69f don't reply with relay tag if we are not on introducer on that address 2024-06-09 15:07:12 -04:00
orignal
0b97b4294c don't request relay tag for every session if we have enough introducers 2024-06-08 19:28:05 -04:00
orignal
4178ac8eac select newest introducers to publish 2024-06-08 16:08:32 -04:00
orignal
6a590bf970 use std::mt19937 instead rand() 2024-06-07 22:10:52 -04:00
orignal
2f847d62bb fixed typo 2024-06-07 13:35:37 -04:00
orignal
df6d48dbae request only not found routers from introducers 2024-06-07 13:29:51 -04:00
orignal
bacce7dc60 fixed introducer's index 2024-06-06 16:13:30 -04:00
orignal
b3314380cc don't use expired introducer even if we a session with it 2024-06-06 14:19:30 -04:00
orignal
d4eea61b82 use mt19937 instead rand 2024-06-05 15:08:51 -04:00
orignal
a1995c13cd flood to 2 next day closest floodfills before UTC midnight 2024-06-04 12:45:35 -04:00
orignal
bc8adf1433 move unsent I2NP messages to new session if replaced 2024-06-01 20:02:04 -04:00
orignal
a1322d4667 move unsent I2NP messages to new session if replaced 2024-06-01 17:46:18 -04:00
orignal
4100249313 removed bootstrap from floodfill. Removed requested destinations mutex 2024-05-31 21:11:47 -04:00
orignal
acbd3f897b fixed race condition between local buffer creation and sending it through the transports 2024-05-26 15:33:37 -04:00
orignal
7dc5a04b8d update timestamp for non-reachable router 2024-05-26 11:06:39 -04:00
orignal
03635f4444 publish through tunnels in case of restricted routes 2024-05-26 10:55:19 -04:00
orignal
0fae04f96a update local RouterInfo timestamp by timer even in hidden mode 2024-05-26 10:21:26 -04:00
orignal
bb531a878d request newly discovered routers with random intervals after exploratory 2024-05-25 15:17:09 -04:00
orignal
0f7db8e418 list of request callbacks 2024-05-24 21:49:39 -04:00
orignal
9a724b2af9 separate timer for netdb requests cleanup 2024-05-23 14:27:39 -04:00
orignal
f4ea6138e8 removed non longer used mutex 2024-05-23 13:36:29 -04:00
orignal
e74272781f moved exploratory to netdb requests thread 2024-05-22 18:29:40 -04:00
orignal
b75e418879 request destination in netdb requests thread 2024-05-22 13:43:00 -04:00
orignal
927123188c handle onDrop for request message in nedb requests thread 2024-05-22 12:30:01 -04:00
orignal
c00eb8cf44 handle requests completions in netdb requests thread 2024-05-22 10:07:01 -04:00
orignal
265bb8b779 handle DatabaseSearchReply in netdb requests thread 2024-05-21 22:19:42 -04:00
orignal
e3be409945 moved netdb requests to separate thread 2024-05-21 21:25:19 -04:00
orignal
d8707ceb57 Merge pull request #2066 from EKCKABATOP54/fix
Fixed checking the bandwidth flag in the config
2024-05-21 06:53:23 -04:00
orignal
39e16824b9 reset routing path if duplicated SYN received 2024-05-17 19:19:17 -04:00
orignal
285e693a4e fixed deadlock 2024-05-17 19:07:51 -04:00
orignal
940628bf36 update LeaseSet if inbond tunnel failed 2024-05-17 17:35:16 -04:00
orignal
b5994e058a increment num attempts if no reply tunnel specified 2024-05-15 14:12:57 -04:00
orignal
22dabfd79e use unordered_map for excluded routers. don't request to self 2024-05-15 13:31:31 -04:00
orignal
0e41c3fa36 resend more interval variance 2024-05-15 11:57:14 -04:00
orignal
124698854f skip resent recently sessions during resend 2024-05-14 18:17:47 -04:00
EKCKABATOP54
f223e668ce Fixed checking the bandwidth flag in the config 2024-05-14 22:04:05 +02:00
orignal
f5b823a712 common code for sending netdb lookup 2024-05-13 14:45:41 -04:00
orignal
46c72a7137 Merge pull request #2050 from PurpleI2P/openssl
2.51.0
2024-04-08 18:55:45 -04:00
orignal
0141489d34 Merge pull request #2040 from PurpleI2P/openssl
Recent changes
2024-03-11 07:59:32 -04:00
orignal
612f51ba7f Merge pull request #2028 from PurpleI2P/openssl
Recent changes
2024-02-27 07:59:43 -05:00
Vort
fd4513ebb2 show bandwidth caps for hops 2024-02-22 23:07:07 +02:00
orignal
245e6b6efd Merge pull request #1911 from PurpleI2P/openssl
recents changes
2023-04-05 19:20:08 -04:00
orignal
6930106d26 Merge pull request #1886 from PurpleI2P/openssl
recent changes
2023-02-22 15:54:30 -05:00
orignal
9a2f744630 Merge pull request #1827 from PurpleI2P/openssl
recent changes
2022-12-16 14:11:16 -05:00
orignal
4540d22de8 Merge pull request #1825 from PurpleI2P/openssl
recent changes
2022-12-11 18:20:33 -05:00
orignal
6e3aef0b9b Merge pull request #1815 from PurpleI2P/openssl
Recent changes
2022-12-02 08:42:50 -05:00
R4SAS
74b2ba7ae2 Merge pull request #1805 from PurpleI2P/openssl
2.44.0
2022-11-22 20:57:22 +00:00
orignal
d5e1d56fde Merge pull request #1771 from PurpleI2P/openssl
recent changes
2022-06-30 11:59:46 -04:00
orignal
9685754511 Merge pull request #1696 from PurpleI2P/openssl
recent changes
2021-10-15 13:02:32 -04:00
orignal
88145eaf94 Merge pull request #1662 from PurpleI2P/openssl
recent changes
2021-06-16 18:12:21 -04:00
orignal
1b3c3fae89 Merge pull request #1656 from PurpleI2P/openssl
2.38.0
2021-05-17 12:12:26 -04:00
orignal
fde79eecc6 Merge pull request #1641 from PurpleI2P/openssl
2.37.0
2021-03-15 12:36:35 -04:00
orignal
a518320e3b Merge pull request #1638 from PurpleI2P/openssl
recent changes
2021-03-01 18:42:36 -05:00
orignal
f6a09f59a3 Merge pull request #1634 from PurpleI2P/openssl
2.36.0
2021-02-16 10:50:03 -05:00
orignal
a2c28d0837 Merge pull request #1610 from PurpleI2P/openssl
recent chamges
2020-12-30 08:27:57 -05:00
orignal
2ff6f9d346 Merge pull request #1583 from PurpleI2P/openssl
2.35.0
2020-11-30 14:53:45 -05:00
orignal
975d5f44b6 Merge pull request #1565 from PurpleI2P/openssl
recent changes
2020-10-24 18:40:07 -04:00
orignal
260564345a Merge pull request #1556 from PurpleI2P/openssl
recent changes
2020-10-10 21:22:00 -04:00
orignal
305a654a37 Merge pull request #1550 from PurpleI2P/openssl
2.33.0
2020-08-24 13:02:22 -04:00
R4SAS
d510f7e473 Merge branch 'openssl' of https://github.com/PurpleI2P/i2pd 2020-06-02 20:39:55 +03:00
orignal
b992fbab52 Merge pull request #1525 from PurpleI2P/openssl
recent changes
2020-05-30 13:33:45 -04:00
orignal
ddf3774aec Merge pull request #1502 from PurpleI2P/openssl
recent fixes
2020-04-10 10:35:04 -04:00
orignal
814f854c5a Merge pull request #1499 from PurpleI2P/openssl
recent updates
2020-03-30 18:20:07 -04:00
orignal
d3bbdb1011 Merge pull request #1475 from PurpleI2P/openssl
2.30.0
2020-02-25 15:27:02 -05:00
orignal
d6d9f05443 Merge pull request #1456 from PurpleI2P/openssl
recent changes
2020-01-16 14:20:47 -05:00
orignal
13e09e231d Merge pull request #1432 from PurpleI2P/openssl
2.29.0
2019-10-21 11:52:22 -04:00
orignal
7866f644d3 Merge pull request #1413 from PurpleI2P/openssl
2.28.0
2019-08-27 09:48:17 -04:00
orignal
84de3f081f Merge pull request #1384 from PurpleI2P/openssl
2.27.0
2019-07-03 12:42:49 -04:00
orignal
f0725c9b40 Merge pull request #1362 from PurpleI2P/openssl
2.26.0
2019-06-07 10:21:49 -04:00
orignal
f176f1909b Merge pull request #1347 from PurpleI2P/openssl
2.25.0
2019-05-09 11:38:44 -04:00
orignal
d231f944c0 Merge pull request #1313 from PurpleI2P/openssl
2.24.0
2019-03-21 11:46:11 -04:00
orignal
cc48436794 Merge pull request #1301 from PurpleI2P/openssl
recent changes
2019-02-22 10:49:51 -05:00
orignal
ea51fc8410 Merge pull request #1288 from PurpleI2P/openssl
2.23.0
2019-01-23 10:54:06 -05:00
orignal
c212a30d33 Merge pull request #1274 from PurpleI2P/openssl
recent changes
2018-12-02 14:18:48 -05:00
orignal
670bf16cd0 Merge pull request #1269 from PurpleI2P/openssl
2.22.0
2018-11-09 11:12:03 -05:00
147 changed files with 7270 additions and 5143 deletions

View File

@@ -128,8 +128,13 @@ jobs:
cache: true cache: true
update: true update: true
- name: Clone MinGW packages repository - name: Clone MinGW packages repository and revert boost to 1.85.0
run: git clone https://github.com/msys2/MINGW-packages run: |
git clone https://github.com/msys2/MINGW-packages
cd MINGW-packages
git checkout 4cbb366edf2f268ac3146174b40ce38604646fc5 mingw-w64-boost
cd mingw-w64-boost
sed -i 's/boostorg.jfrog.io\/artifactory\/main/archives.boost.io/' PKGBUILD
# headers # headers
- name: Get headers package version - name: Get headers package version
@@ -205,26 +210,30 @@ jobs:
run: | run: |
cd MINGW-packages/mingw-w64-openssl cd MINGW-packages/mingw-w64-openssl
gpg --recv-keys D894E2CE8B3D79F5 gpg --recv-keys D894E2CE8B3D79F5
gpg --recv-keys 216094DFD0CB81EF
MINGW_ARCH=mingw32 makepkg-mingw -sCLf --noconfirm --nocheck MINGW_ARCH=mingw32 makepkg-mingw -sCLf --noconfirm --nocheck
- name: Install openssl package - name: Install openssl package
run: pacman --noconfirm -U MINGW-packages/mingw-w64-openssl/mingw-w64-i686-*-any.pkg.tar.zst run: pacman --noconfirm -U MINGW-packages/mingw-w64-openssl/mingw-w64-i686-*-any.pkg.tar.zst
# Boost # Boost
- name: Get boost package version #- name: Get boost package version
id: version-boost # id: version-boost
run: | # run: |
echo "version=$(pacman -Si mingw-w64-i686-boost | grep -Po '^Version\s*: \K.+')" >> $GITHUB_OUTPUT # echo "version=$(pacman -Si mingw-w64-i686-boost | grep -Po '^Version\s*: \K.+')" >> $GITHUB_OUTPUT
- name: Cache boost package - name: Cache boost package
uses: actions/cache@v4 uses: actions/cache@v4
id: cache-boost id: cache-boost
with: with:
path: MINGW-packages/mingw-w64-boost/*.zst path: MINGW-packages/mingw-w64-boost/*.zst
key: winxp-winpthreads-${{ steps.version-boost.outputs.version }} key: winxp-boost-1.85.0+crt-${{ steps.version-headers.outputs.version }}+ossl-${{ steps.version-openssl.outputs.version }}
# Rebuild package if packages above has changed
- name: Build WinXP-capable boost package - name: Build WinXP-capable boost package
if: steps.cache-boost.outputs.cache-hit != 'true' if: steps.cache-boost.outputs.cache-hit != 'true'
run: | run: |
cd MINGW-packages/mingw-w64-boost cd MINGW-packages/mingw-w64-boost
MINGW_ARCH=mingw32 makepkg-mingw -sCLf --noconfirm --nocheck MINGW_ARCH=mingw32 makepkg-mingw -sCLf --noconfirm --nocheck
- name: Remove boost packages
run: pacman --noconfirm -R mingw-w64-i686-boost mingw-w64-i686-boost-libs
- name: Install boost package - name: Install boost package
run: pacman --noconfirm -U MINGW-packages/mingw-w64-boost/mingw-w64-i686-*-any.pkg.tar.zst run: pacman --noconfirm -U MINGW-packages/mingw-w64-boost/mingw-w64-i686-*-any.pkg.tar.zst

113
ChangeLog
View File

@@ -1,6 +1,119 @@
# for this file format description, # for this file format description,
# see https://github.com/olivierlacan/keep-a-changelog # see https://github.com/olivierlacan/keep-a-changelog
## [2.55.0] - 2024-12-30
### Added
- Support boost 1.87
- "i2p.streaming.maxConcurrentStreams" tunnel's param to limit number of simultaneous streams
- Separate thread for tunnel build requests
- Show next peer and connectivity on "Transit tunnels" page
- Tunnel name for local destination thread
- Throttle incoming ECIESx25519 sessions
- Send tunnel data to transport session directly if possible
- Publish 'R' cap for yggdrasil-only routers, and 'U' cap for routers through proxy
- Random tunnel rejection when medium congestion
- Save unreachable router's endpoint to use it next time without introducers
- Recognize symmetric NAT from peer test message 7
- Resend HolePunch and RelayResponse messages
### Changed
- Removed own implementation of AESNI and always use one from openssl
- Renamed main thread to i2pd-daemon
- Set i2p.streaming.profile=2 for shared local destination
- Reduced LeaseSet and RouterInfo lookup timeouts
- Cleanup ECIES sessions and tags more often
- Check LeaseSet expiration time
- Handle NTCP2 session handshakes in separate thread
- Limit last decline time by 1.5 hours in router's profile
- Don't handle RelayRequest and RelayIntro with same nonce twice
- Increased hole punch expiration interval
- Send peer test message 6 with delay if message 4 was received before message 5
- Pre-calculate more x25519 keys for transports in runtime
- Don't request LeaseSet for incoming stream
- Terminate incoming stream right away if no remote LeaseSet
- Handle choked, new RTO and window size calculation and resetting algorithm for streams
### Fixed
- Empty string in addressbook subscriptions
- ECIESx25519 sessions without destination
- Missing RouterInfo buffer in NetDb
- Invalid I2PControl certificate
- Routers disappear from NetDb when offline
- Peer test message 6 sent to unknown endpoint
- Race condition with LeaseSet update
- Excessive CPU usage by streams
- Crash on shutdown
## [2.54.0] - 2024-10-06
### Added
- Maintain recently connected routers list to avoid false-positive peer test
- Limited connectivity mode(through proxy)
- "i2p.streaming.profile" tunnel's param to let tunnel select also low-bandwidth routers
- Limit stream's inbound speed
- Periodic ack requests in ratchets session
- Set congestion cap G immediately if through proxy
- Show tunnel's routers bandwidth caps in web console
- Handle immediate ack requested flag in SSU2 data packets
- Resend and ack peer test and relay messages
- "senduseragent" HTTP proxy's param to pass through user's User-Agent
### Changed
- Exclude 'N' routers from high-bandwidth routers for client tunnels
- C++11 support has been dropped, the minimal requirement is C++17 now, C++20 for some compilers
- Removed dependency from boost::date_time and boost::filesystem
- Set default i2cp.leaseSetEncType to 0,4 and to 4 for server tunnels
- Handle i2cp.inboundlimit and i2cp.outboundlimit params in I2CP
- Publish LeaseSet with new timestamp update if tunnel was replaced in the same second
- Increase max number of generated tags to 800 per tagset
- Routing path expiration by time instead num attempts
- Save timestamp from epoch instead local time to profiles
- Update introducer's iTag if session to introducer was replaced to new one
- RTT, window size and number of NACKs calculation for streaming
- Don't select same peer for tunnel too often
- Use WinApi for data path UTF-8 conversion for Windows
### Fixed
- Jump link crash if address book is disabled
- Race condition if connect through an introducer
- "Date" header in I2PControl response
- Incomplete response from web console
- AEAD verification with LibreSSL
- Number of generated tags and new keys for follow-on tagsets
- Expired leases in LeaseSet
- Attempts to send HolePunch to 0.0.0.0
- Incorrect options size in quick ack streaming packet
- Low bandwidth router appeared as first peer in high-bandwidth client tunnel
## [2.53.1] - 2024-07-29
### Changed
- I2CP performance improvement
### Fixed
- 100% CPU usage after I2CP/SAM/BOB session termination
- Incorrect client limits returned through I2CP
- Build with LibreSSL
## [2.53.0] - 2024-07-19
### Added
- New congestion control algorithm for streaming
- Support miniupnp-2.2.8
- Limit stream's outbound speed
- Flood to next day closest floodfills before UTC midnight
- Recognize duplicated routers and bypass them
- Random SSU2 resend interval
### Changed
- Set minimal version to 0.9.69 for floodfills and 0.9.58 for client tunnels
- Removed openssl 1.0.2 support
- Move unsent I2NP messages to the new session if replaced
- Use mt19937 RNG instead rand()
- Update router's congestion caps before initial publishing
- Don't try introducer with invalid address
- Select newest introducers to publish
- Don't request relay tag for every session if we have enough introducers
- Update timestamp for non-reachable or hidden router
- Reset streaming routing path if duplicated SYN received
- Update LeaseSet if inbound tunnel failed
- Reseeds list
### Fixed
- Crash when a destination gets terminated
- Expired offline signature upon destination creation
- Race condition between local RouterInfo buffer creation and sending it through the transports
## [2.52.0] - 2024-05-12 ## [2.52.0] - 2024-05-12
### Added ### Added
- Separate threads for persisting RouterInfos and profiles to disk - Separate threads for persisting RouterInfos and profiles to disk

View File

@@ -29,7 +29,6 @@ DAEMON_SRC_DIR := daemon
# import source files lists # import source files lists
include filelist.mk include filelist.mk
USE_AESNI := $(or $(USE_AESNI),yes)
USE_STATIC := $(or $(USE_STATIC),no) USE_STATIC := $(or $(USE_STATIC),no)
USE_UPNP := $(or $(USE_UPNP),no) USE_UPNP := $(or $(USE_UPNP),no)
DEBUG := $(or $(DEBUG),yes) DEBUG := $(or $(DEBUG),yes)

View File

@@ -1,18 +1,22 @@
CXX = clang++ CXX = clang++
CXXFLAGS ?= ${CXX_DEBUG} -Wall -Wextra -Wno-unused-parameter -pedantic -Wno-misleading-indentation CXXFLAGS ?= ${CXX_DEBUG} -Wall -Wextra -Wno-unused-parameter -pedantic -Wno-misleading-indentation
DEFINES = -D_GLIBCXX_USE_NANOSLEEP=1
INCFLAGS = -I/usr/include/ -I/usr/local/include/
LDFLAGS = ${LD_DEBUG} -Wl,-rpath,/usr/local/lib -L/usr/local/lib
LDLIBS = -lcrypto -lssl -lz -lpthread -lboost_system -lboost_program_options
## NOTE: NEEDED_CXXFLAGS is here so that custom CXXFLAGS can be specified at build time ## NOTE: NEEDED_CXXFLAGS is here so that custom CXXFLAGS can be specified at build time
## **without** overwriting the CXXFLAGS which we need in order to build. ## **without** overwriting the CXXFLAGS which we need in order to build.
## For example, when adding 'hardening flags' to the build ## For example, when adding 'hardening flags' to the build
## (e.g. -fstack-protector-strong -Wformat -Werror=format-security), we do not want to remove ## (e.g. -fstack-protector-strong -Wformat -Werror=format-security), we do not want to remove
## -std=c++11. If you want to remove this variable please do so in a way that allows setting ## -std=c++11. If you want to remove this variable please do so in a way that allows setting
## custom FLAGS to work at build-time. ## custom FLAGS to work at build-time.
CXXVER := $(shell $(CXX) -dumpversion) CXXVER := $(shell $(CXX) -dumpversion|cut -c 1-2)
ifeq (${CXXVER}, "4.2.1") # older clang always returned 4.2.1 ifeq (${CXXVER}, "4.") # older clang always returned 4.2.1
NEEDED_CXXFLAGS = -std=c++11 $(error Compiler too old)
else # newer versions support C++17 else ifeq (${CXXVER}, ${filter ${CXXVER},16 17 18 19}) # clang 16 - 19
NEEDED_CXXFLAGS = -std=c++20
else
NEEDED_CXXFLAGS = -std=c++17 NEEDED_CXXFLAGS = -std=c++17
endif endif
DEFINES = -D_GLIBCXX_USE_NANOSLEEP=1
INCFLAGS = -I/usr/include/ -I/usr/local/include/
LDFLAGS = ${LD_DEBUG} -Wl,-rpath,/usr/local/lib -L/usr/local/lib
LDLIBS = -lcrypto -lssl -lz -lboost_system -lboost_date_time -lboost_filesystem -lboost_program_options -lpthread

View File

@@ -1,8 +1,8 @@
CXX = g++ CXX = g++
CXXFLAGS := -Wall -std=c++11 CXXFLAGS := -Wall -std=c++17
INCFLAGS = -I/system/develop/headers INCFLAGS = -I/system/develop/headers
DEFINES = -D_DEFAULT_SOURCE -D_GNU_SOURCE DEFINES = -D_DEFAULT_SOURCE -D_GNU_SOURCE
LDLIBS = -lbe -lbsd -lnetwork -lz -lcrypto -lssl -lboost_system -lboost_date_time -lboost_filesystem -lboost_program_options -lpthread LDLIBS = -lbe -lbsd -lnetwork -lz -lcrypto -lssl -lboost_system -lboost_program_options -lpthread
ifeq ($(USE_UPNP),yes) ifeq ($(USE_UPNP),yes)
DEFINES += -DUSE_UPNP DEFINES += -DUSE_UPNP

View File

@@ -5,20 +5,20 @@ SSLROOT = ${BREWROOT}/opt/openssl@1.1
UPNPROOT = ${BREWROOT}/opt/miniupnpc UPNPROOT = ${BREWROOT}/opt/miniupnpc
CXXFLAGS ?= ${CXX_DEBUG} -Wall -Wno-overloaded-virtual CXXFLAGS ?= ${CXX_DEBUG} -Wall -Wno-overloaded-virtual
NEEDED_CXXFLAGS ?= -std=c++11 NEEDED_CXXFLAGS ?= -std=c++17
INCFLAGS ?= -I${SSLROOT}/include -I${BOOSTROOT}/include INCFLAGS ?= -I${SSLROOT}/include -I${BOOSTROOT}/include
LDFLAGS ?= ${LD_DEBUG} LDFLAGS ?= ${LD_DEBUG}
DEFINES += -DMAC_OSX DEFINES += -DMAC_OSX
ifeq ($(USE_STATIC),yes) ifeq ($(USE_STATIC),yes)
LDLIBS = -lz ${SSLROOT}/lib/libcrypto.a ${SSLROOT}/lib/libssl.a ${BOOSTROOT}/lib/libboost_system.a ${BOOSTROOT}/lib/libboost_date_time.a ${BOOSTROOT}/lib/libboost_filesystem.a ${BOOSTROOT}/lib/libboost_program_options.a LDLIBS = -lz ${SSLROOT}/lib/libcrypto.a ${SSLROOT}/lib/libssl.a ${BOOSTROOT}/lib/libboost_system.a ${BOOSTROOT}/lib/libboost_filesystem.a ${BOOSTROOT}/lib/libboost_program_options.a
ifeq ($(USE_UPNP),yes) ifeq ($(USE_UPNP),yes)
LDLIBS += ${UPNPROOT}/lib/libminiupnpc.a LDLIBS += ${UPNPROOT}/lib/libminiupnpc.a
endif endif
LDLIBS += -lpthread -ldl LDLIBS += -lpthread -ldl
else else
LDFLAGS += -L${SSLROOT}/lib -L${BOOSTROOT}/lib LDFLAGS += -L${SSLROOT}/lib -L${BOOSTROOT}/lib
LDLIBS = -lz -lcrypto -lssl -lboost_system -lboost_date_time -lboost_filesystem -lboost_program_options -lpthread LDLIBS = -lz -lcrypto -lssl -lboost_system -lboost_filesystem -lboost_program_options -lpthread
ifeq ($(USE_UPNP),yes) ifeq ($(USE_UPNP),yes)
LDFLAGS += -L${UPNPROOT}/lib LDFLAGS += -L${UPNPROOT}/lib
LDLIBS += -lminiupnpc LDLIBS += -lminiupnpc
@@ -30,13 +30,6 @@ ifeq ($(USE_UPNP),yes)
INCFLAGS += -I${UPNPROOT}/include INCFLAGS += -I${UPNPROOT}/include
endif endif
ifeq ($(USE_AESNI),yes)
ifneq (, $(findstring i386, $(SYS))$(findstring i686, $(SYS))$(findstring x86_64, $(SYS))) # only x86-based CPU supports that
NEEDED_CXXFLAGS += -maes
DEFINES += -D__AES__
endif
endif
install: all install: all
install -d ${PREFIX}/bin install -d ${PREFIX}/bin
install -m 755 ${I2PD} ${PREFIX}/bin install -m 755 ${I2PD} ${PREFIX}/bin

View File

@@ -9,24 +9,17 @@ LDFLAGS ?= ${LD_DEBUG}
## -std=c++11. If you want to remove this variable please do so in a way that allows setting ## -std=c++11. If you want to remove this variable please do so in a way that allows setting
## custom FDLAGS to work at build-time. ## custom FDLAGS to work at build-time.
# detect proper flag for c++11 support by compilers # detect proper flag for c++17 support by compilers
CXXVER := $(shell $(CXX) -dumpversion) CXXVER := $(shell $(CXX) -dumpversion)
ifeq ($(shell expr match $(CXX) 'clang'),5) 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\.[8-9]"),3) # gcc 4.8 - 4.9
NEEDED_CXXFLAGS += -std=c++11 -D_GLIBCXX_USE_NANOSLEEP=1
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 - 9
NEEDED_CXXFLAGS += -std=c++17 NEEDED_CXXFLAGS += -std=c++17
LDLIBS = -latomic else ifeq ($(shell expr match ${CXXVER} "[8-9]"),1) # gcc 8 - 9
else ifeq ($(shell expr match ${CXXVER} "1[0-9]"),2) # gcc 10+
# NEEDED_CXXFLAGS += -std=c++20
NEEDED_CXXFLAGS += -std=c++17 NEEDED_CXXFLAGS += -std=c++17
LDLIBS = -latomic LDLIBS = -lboost_system -lstdc++fs
else ifeq ($(shell expr match ${CXXVER} "1[0-2]"),2) # gcc 10 - 12
NEEDED_CXXFLAGS += -std=c++17
else ifeq ($(shell expr match ${CXXVER} "1[3-9]"),2) # gcc 13+
NEEDED_CXXFLAGS += -std=c++20
else # not supported else # not supported
$(error Compiler too old) $(error Compiler too old)
endif endif
@@ -38,9 +31,6 @@ ifeq ($(USE_STATIC),yes)
# Using 'getaddrinfo' in statically linked applications requires at runtime # Using 'getaddrinfo' in statically linked applications requires at runtime
# the shared libraries from the glibc version used for linking # the shared libraries from the glibc version used for linking
LIBDIR := /usr/lib/$(SYS) LIBDIR := /usr/lib/$(SYS)
LDLIBS += $(LIBDIR)/libboost_system.a
LDLIBS += $(LIBDIR)/libboost_date_time.a
LDLIBS += $(LIBDIR)/libboost_filesystem.a
LDLIBS += $(LIBDIR)/libboost_program_options.a LDLIBS += $(LIBDIR)/libboost_program_options.a
LDLIBS += $(LIBDIR)/libssl.a LDLIBS += $(LIBDIR)/libssl.a
LDLIBS += $(LIBDIR)/libcrypto.a LDLIBS += $(LIBDIR)/libcrypto.a
@@ -50,7 +40,7 @@ ifeq ($(USE_UPNP),yes)
endif endif
LDLIBS += -lpthread -ldl LDLIBS += -lpthread -ldl
else else
LDLIBS += -lcrypto -lssl -lz -lboost_system -lboost_date_time -lboost_filesystem -lboost_program_options -lpthread LDLIBS += -lcrypto -lssl -lz -lboost_program_options -lpthread -latomic
ifeq ($(USE_UPNP),yes) ifeq ($(USE_UPNP),yes)
LDLIBS += -lminiupnpc LDLIBS += -lminiupnpc
endif endif
@@ -61,13 +51,6 @@ ifeq ($(USE_UPNP),yes)
DEFINES += -DUSE_UPNP DEFINES += -DUSE_UPNP
endif endif
ifeq ($(USE_AESNI),yes)
ifneq (, $(findstring i386, $(SYS))$(findstring i686, $(SYS))$(findstring x86_64, $(SYS))) # only x86-based CPU supports that
NEEDED_CXXFLAGS += -maes
DEFINES += -D__AES__
endif
endif
install: all install: all
install -d ${PREFIX}/bin install -d ${PREFIX}/bin
install -m 755 ${I2PD} ${PREFIX}/bin install -m 755 ${I2PD} ${PREFIX}/bin

View File

@@ -7,7 +7,7 @@ CXXFLAGS := $(CXX_DEBUG) -fPIC -msse
INCFLAGS := -I$(DAEMON_SRC_DIR) -IWin32 INCFLAGS := -I$(DAEMON_SRC_DIR) -IWin32
LDFLAGS := ${LD_DEBUG} -static -fPIC -msse LDFLAGS := ${LD_DEBUG} -static -fPIC -msse
NEEDED_CXXFLAGS += -std=c++17 NEEDED_CXXFLAGS += -std=c++20
DEFINES += -DWIN32_LEAN_AND_MEAN DEFINES += -DWIN32_LEAN_AND_MEAN
# UPNP Support # UPNP Support
@@ -16,9 +16,11 @@ ifeq ($(USE_UPNP),yes)
LDLIBS = -lminiupnpc LDLIBS = -lminiupnpc
endif endif
ifeq ($(USE_WINXP_FLAGS), yes)
DEFINES += -DWINVER=0x0501 -D_WIN32_WINNT=0x0501
endif
LDLIBS += \ LDLIBS += \
$(MINGW_PREFIX)/lib/libboost_system-mt.a \
$(MINGW_PREFIX)/lib/libboost_date_time-mt.a \
$(MINGW_PREFIX)/lib/libboost_filesystem-mt.a \ $(MINGW_PREFIX)/lib/libboost_filesystem-mt.a \
$(MINGW_PREFIX)/lib/libboost_program_options-mt.a \ $(MINGW_PREFIX)/lib/libboost_program_options-mt.a \
$(MINGW_PREFIX)/lib/libssl.a \ $(MINGW_PREFIX)/lib/libssl.a \
@@ -40,16 +42,6 @@ ifeq ($(USE_WIN32_APP), yes)
DAEMON_OBJS += $(patsubst %.rc,obj/%.o,$(DAEMON_RC)) DAEMON_OBJS += $(patsubst %.rc,obj/%.o,$(DAEMON_RC))
endif endif
ifeq ($(USE_WINXP_FLAGS), yes)
DEFINES += -DWINVER=0x0501 -D_WIN32_WINNT=0x0501
endif
ifeq ($(USE_AESNI),yes)
NEEDED_CXXFLAGS += -maes
LDFLAGS += -maes
DEFINES += -D__AES__
endif
ifeq ($(USE_ASLR),yes) ifeq ($(USE_ASLR),yes)
LDFLAGS += -Wl,--nxcompat -Wl,--high-entropy-va -Wl,--dynamicbase,--export-all-symbols LDFLAGS += -Wl,--nxcompat -Wl,--high-entropy-va -Wl,--dynamicbase,--export-all-symbols
endif endif

View File

@@ -1,5 +1,5 @@
CXX = clang++ CXX = clang++
CXXFLAGS := ${CXX_DEBUG} -Wall -std=c++11 CXXFLAGS := ${CXX_DEBUG} -Wall -std=c++17
INCFLAGS = -I/usr/local/include INCFLAGS = -I/usr/local/include
DEFINES := -DMAC_OSX DEFINES := -DMAC_OSX
LDFLAGS := -Wl,-rpath,/usr/local/lib -L/usr/local/lib LDFLAGS := -Wl,-rpath,/usr/local/lib -L/usr/local/lib
@@ -7,9 +7,9 @@ LDFLAGS += -Wl,-dead_strip
LDFLAGS += -Wl,-dead_strip_dylibs LDFLAGS += -Wl,-dead_strip_dylibs
ifeq ($(USE_STATIC),yes) 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 LDLIBS = -lz /usr/local/lib/libcrypto.a /usr/local/lib/libssl.a /usr/local/lib/libboost_system.a /usr/local/lib/libboost_filesystem.a /usr/local/lib/libboost_program_options.a -lpthread
else else
LDLIBS = -lz -lcrypto -lssl -lboost_system -lboost_date_time -lboost_filesystem -lboost_program_options -lpthread LDLIBS = -lz -lcrypto -lssl -lboost_system -lboost_filesystem -lboost_program_options -lpthread
endif endif
ifeq ($(USE_UPNP),yes) ifeq ($(USE_UPNP),yes)
@@ -25,9 +25,5 @@ endif
OSARCH = $(shell uname -p) OSARCH = $(shell uname -p)
ifneq ($(OSARCH),powerpc) ifneq ($(OSARCH),powerpc)
ifeq ($(USE_AESNI),yes) CXXFLAGS += -msse
CXXFLAGS += -D__AES__ -maes
else
CXXFLAGS += -msse
endif
endif endif

View File

@@ -313,7 +313,7 @@ namespace win32
} }
case ID_DATADIR: case ID_DATADIR:
{ {
std::string datadir(i2p::fs::GetUTF8DataDir()); std::string datadir(i2p::fs::GetDataDir());
ShellExecute(NULL, "explore", datadir.c_str(), NULL, NULL, SW_SHOWNORMAL); ShellExecute(NULL, "explore", datadir.c_str(), NULL, NULL, SW_SHOWNORMAL);
return 0; return 0;
} }
@@ -355,9 +355,7 @@ namespace win32
} }
} }
} }
#if (__cplusplus >= 201703L) // C++ 17 or higher
[[fallthrough]]; [[fallthrough]];
#endif
} }
case WM_TRAYICON: case WM_TRAYICON:
{ {

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2020, The PurpleI2P Project * Copyright (c) 2013-2024, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2020, The PurpleI2P Project * Copyright (c) 2013-2024, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *

View File

@@ -29,7 +29,6 @@ project(
) )
# configurable options # configurable options
option(WITH_AESNI "Use AES-NI instructions set" ON)
option(WITH_HARDENING "Use hardening compiler flags" OFF) option(WITH_HARDENING "Use hardening compiler flags" OFF)
option(WITH_LIBRARY "Build library" ON) option(WITH_LIBRARY "Build library" ON)
option(WITH_BINARY "Build binary" ON) option(WITH_BINARY "Build binary" ON)
@@ -156,20 +155,6 @@ else()
endif() endif()
set(CMAKE_CXX_FLAGS_MINSIZEREL "${CMAKE_CXX_FLAGS_MINSIZEREL} -ffunction-sections -fdata-sections") set(CMAKE_CXX_FLAGS_MINSIZEREL "${CMAKE_CXX_FLAGS_MINSIZEREL} -ffunction-sections -fdata-sections")
set(CMAKE_EXE_LINKER_FLAGS_MINSIZEREL "-Wl,--gc-sections") # -flto is added from above set(CMAKE_EXE_LINKER_FLAGS_MINSIZEREL "-Wl,--gc-sections") # -flto is added from above
# 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)
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()
endif() endif()
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
@@ -199,16 +184,6 @@ if(UNIX)
endif() endif()
endif() endif()
# Note: AES-NI and AVX is available on x86-based CPU's.
# Here also ARM64 implementation, but currently we don't support it.
# MSVC is not supported due to different ASM processing, so we hope OpenSSL has its own checks to run optimized code.
if(WITH_AESNI AND (ARCHITECTURE MATCHES "x86_64" OR ARCHITECTURE MATCHES "i386"))
if(NOT MSVC)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -maes")
endif()
add_definitions(-D__AES__)
endif()
if(WITH_ADDRSANITIZER) if(WITH_ADDRSANITIZER)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -fno-omit-frame-pointer") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -fno-omit-frame-pointer")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=address") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=address")
@@ -223,6 +198,10 @@ if(WITH_THREADSANITIZER)
endif() endif()
endif() endif()
if (CMAKE_COMPILER_IS_GNUCC AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 10.0 AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 8.0) # gcc 8-9
list(APPEND CMAKE_REQUIRED_LIBRARIES "stdc++fs")
endif()
# Use std::atomic instead of GCC builtins on macOS PowerPC: # Use std::atomic instead of GCC builtins on macOS PowerPC:
# For more information refer to: https://github.com/PurpleI2P/i2pd/issues/1726#issuecomment-1306335111 # For more information refer to: https://github.com/PurpleI2P/i2pd/issues/1726#issuecomment-1306335111
# This has been fixed in Boost 1.81, nevertheless we retain the setting for the sake of compatibility. # This has been fixed in Boost 1.81, nevertheless we retain the setting for the sake of compatibility.
@@ -277,14 +256,14 @@ else()
if(NOT MSVC) if(NOT MSVC)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC")
endif() endif()
add_definitions(-DBOOST_ATOMIC_DYN_LINK -DBOOST_SYSTEM_DYN_LINK -DBOOST_FILESYSTEM_DYN_LINK -DBOOST_PROGRAM_OPTIONS_DYN_LINK -DBOOST_DATE_TIME_DYN_LINK -DBOOST_REGEX_DYN_LINK) add_definitions(-DBOOST_ATOMIC_DYN_LINK -DBOOST_SYSTEM_DYN_LINK -DBOOST_FILESYSTEM_DYN_LINK -DBOOST_PROGRAM_OPTIONS_DYN_LINK)
if(WIN32) if(WIN32)
set(Boost_USE_STATIC_LIBS OFF) set(Boost_USE_STATIC_LIBS OFF)
set(Boost_USE_STATIC_RUNTIME OFF) set(Boost_USE_STATIC_RUNTIME OFF)
endif() endif()
endif() endif()
find_package(Boost REQUIRED COMPONENTS system filesystem program_options date_time OPTIONAL_COMPONENTS atomic) find_package(Boost REQUIRED COMPONENTS system filesystem program_options)
if(NOT DEFINED Boost_FOUND) if(NOT DEFINED Boost_FOUND)
message(SEND_ERROR "Boost is not found, or your boost version was below 1.46. Please download Boost!") message(SEND_ERROR "Boost is not found, or your boost version was below 1.46. Please download Boost!")
endif() endif()
@@ -312,6 +291,26 @@ if(ZLIB_FOUND)
link_directories(${ZLIB_ROOT}/lib) link_directories(${ZLIB_ROOT}/lib)
endif() endif()
# C++ standard to use, based on compiler and version of boost
if(NOT MSVC)
# check for c++20 & c++17 support
include(CheckCXXCompilerFlag)
if(Boost_VERSION VERSION_GREATER_EQUAL "1.83") # min boost version for c++20
CHECK_CXX_COMPILER_FLAG("-std=c++20" CXX20_SUPPORTED)
endif()
CHECK_CXX_COMPILER_FLAG("-std=c++17" CXX17_SUPPORTED)
if(CXX20_SUPPORTED)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++20")
elseif(CXX17_SUPPORTED)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++17")
else()
message(SEND_ERROR "C++20 nor C++17 standard not seems to be supported by compiler. Too old version?")
endif()
endif()
# load includes # load includes
include_directories(SYSTEM ${Boost_INCLUDE_DIRS} ${OPENSSL_INCLUDE_DIR} ${ZLIB_INCLUDE_DIR}) include_directories(SYSTEM ${Boost_INCLUDE_DIRS} ${OPENSSL_INCLUDE_DIR} ${ZLIB_INCLUDE_DIR})
@@ -322,9 +321,9 @@ message(STATUS "Compiler vendor : ${CMAKE_CXX_COMPILER_ID}")
message(STATUS "Compiler version : ${CMAKE_CXX_COMPILER_VERSION}") message(STATUS "Compiler version : ${CMAKE_CXX_COMPILER_VERSION}")
message(STATUS "Compiler path : ${CMAKE_CXX_COMPILER}") message(STATUS "Compiler path : ${CMAKE_CXX_COMPILER}")
message(STATUS "Architecture : ${ARCHITECTURE}") message(STATUS "Architecture : ${ARCHITECTURE}")
message(STATUS "Compiler flags : ${CMAKE_CXX_FLAGS}")
message(STATUS "Install prefix: : ${CMAKE_INSTALL_PREFIX}") message(STATUS "Install prefix: : ${CMAKE_INSTALL_PREFIX}")
message(STATUS "Options:") message(STATUS "Options:")
message(STATUS " AESNI : ${WITH_AESNI}")
message(STATUS " HARDENING : ${WITH_HARDENING}") message(STATUS " HARDENING : ${WITH_HARDENING}")
message(STATUS " LIBRARY : ${WITH_LIBRARY}") message(STATUS " LIBRARY : ${WITH_LIBRARY}")
message(STATUS " BINARY : ${WITH_BINARY}") message(STATUS " BINARY : ${WITH_BINARY}")

View File

@@ -8,7 +8,7 @@ INCLUDE(CheckLibraryExists)
function(check_working_cxx_atomics varname) function(check_working_cxx_atomics varname)
set(OLD_CMAKE_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS}) set(OLD_CMAKE_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS})
set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -std=c++11") set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -std=c++17")
CHECK_CXX_SOURCE_COMPILES(" CHECK_CXX_SOURCE_COMPILES("
#include <atomic> #include <atomic>
std::atomic<int> x; std::atomic<int> x;
@@ -25,7 +25,7 @@ endfunction(check_working_cxx_atomics)
function(check_working_cxx_atomics64 varname) function(check_working_cxx_atomics64 varname)
set(OLD_CMAKE_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS}) set(OLD_CMAKE_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS})
set(CMAKE_REQUIRED_FLAGS "-std=c++11 ${CMAKE_REQUIRED_FLAGS}") set(CMAKE_REQUIRED_FLAGS "-std=c++17 ${CMAKE_REQUIRED_FLAGS}")
CHECK_CXX_SOURCE_COMPILES(" CHECK_CXX_SOURCE_COMPILES("
#include <atomic> #include <atomic>
#include <cstdint> #include <cstdint>

View File

@@ -59,7 +59,7 @@ get_filename_component(_gitdescmoddir ${CMAKE_CURRENT_LIST_FILE} PATH)
# function returns an empty string via _git_dir_var. # function returns an empty string via _git_dir_var.
# #
# Example: Given a path C:/bla/foo/bar and assuming C:/bla/.git exists and # Example: Given a path C:/bla/foo/bar and assuming C:/bla/.git exists and
# neither foo nor bar contain a file/directory .git. This wil return # neither foo nor bar contain a file/directory .git. This will return
# C:/bla/.git # C:/bla/.git
# #
function(_git_find_closest_git_dir _start_dir _git_dir_var) function(_git_find_closest_git_dir _start_dir _git_dir_var)

View File

@@ -24,7 +24,7 @@ ExtraDiskSpaceRequired=15
AppID={{621A23E0-3CF4-4BD6-97BC-4835EA5206A2} AppID={{621A23E0-3CF4-4BD6-97BC-4835EA5206A2}
AppVerName={#I2Pd_AppName} AppVerName={#I2Pd_AppName}
AppCopyright=Copyright (c) 2013-2022, The PurpleI2P Project AppCopyright=Copyright (c) 2013-2024, The PurpleI2P Project
AppPublisherURL=http://i2pd.website/ AppPublisherURL=http://i2pd.website/
AppSupportURL=https://github.com/PurpleI2P/i2pd/issues AppSupportURL=https://github.com/PurpleI2P/i2pd/issues
AppUpdatesURL=https://github.com/PurpleI2P/i2pd/releases AppUpdatesURL=https://github.com/PurpleI2P/i2pd/releases

View File

@@ -4,7 +4,7 @@
# #
#include <tunables/global> #include <tunables/global>
profile i2pd /{usr/,}sbin/i2pd { profile i2pd /{usr/,}bin/i2pd {
#include <abstractions/base> #include <abstractions/base>
#include <abstractions/openssl> #include <abstractions/openssl>
#include <abstractions/nameservice> #include <abstractions/nameservice>
@@ -14,12 +14,12 @@ profile i2pd /{usr/,}sbin/i2pd {
/var/lib/i2pd/** rw, /var/lib/i2pd/** rw,
/var/log/i2pd/i2pd.log w, /var/log/i2pd/i2pd.log w,
/{var/,}run/i2pd/i2pd.pid rwk, /{var/,}run/i2pd/i2pd.pid rwk,
/{usr/,}sbin/i2pd mr, /{usr/,}bin/i2pd mr,
@{system_share_dirs}/i2pd/** r, @{system_share_dirs}/i2pd/** r,
# user homedir (if started not by init.d or systemd) # user homedir (if started not by init.d or systemd)
owner @{HOME}/.i2pd/ rw, owner @{HOME}/.i2pd/ rw,
owner @{HOME}/.i2pd/** rwk, owner @{HOME}/.i2pd/** rwk,
#include if exists <local/usr.sbin.i2pd> #include if exists <local/usr.bin.i2pd>
} }

View File

@@ -1,32 +0,0 @@
-----BEGIN CERTIFICATE-----
MIIFgTCCA2mgAwIBAgIETWAY1DANBgkqhkiG9w0BAQ0FADBxMQswCQYDVQQGEwJY
WDELMAkGA1UECAwCWFgxCzAJBgNVBAcMAlhYMR4wHAYDVQQKDBVJMlAgQW5vbnlt
b3VzIE5ldHdvcmsxDDAKBgNVBAsMA0kyUDEaMBgGA1UEAwwRaGlkdXNlcjBAbWFp
bC5pMnAwHhcNMjExMjEzMTU0MDI3WhcNMzExMjExMTU0MDI3WjBxMQswCQYDVQQG
EwJYWDELMAkGA1UECAwCWFgxCzAJBgNVBAcMAlhYMR4wHAYDVQQKDBVJMlAgQW5v
bnltb3VzIE5ldHdvcmsxDDAKBgNVBAsMA0kyUDEaMBgGA1UEAwwRaGlkdXNlcjBA
bWFpbC5pMnAwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDXnjJ8UQ0f
lHHpfPMiHofBPSuL4sbOJY6fOXwPhSg/h6THh9DS/ZWmJXQ3qRD0glDVtv4/Dr/9
ldGQ5eltF9iCFXCQlMEy2HjQrBKq0nsl7RpYK12cyMaod0kkzCUk9ITLi9CmHM3Z
gQZcmG8TWjFEpDR+idx/QkQt2pcO4vzWlDit3Vh4ivnbX5jGQHbsVjQEMQWxr+pX
dsS+YQpjZ6RBmrooGTPO8QDOOeYLAn0lCjmffc/kzIH9E/p4/O0rOpyhVYbdxUD1
5wkqN9l4yrtxmORG/PudnRQQ0r4TUq8vsxfGY0Euo9IbhgXF2Parel1ZhDxB1WZV
VwWtgLIh9jGA1UMa8SYKnEfp8LWNZ3b3mUUnZb3kMrLk6jGYRWNsHmamhd4mC7AZ
qf/8lOkEIw3bPd3YguCDRVcLui5BwIEZmqXg8uoESxfO/sW3pBrN/8M7MkTex9kN
vjitGDDXvenK27qmNgZxbBlX72yTSfys7XTYTLnxZC8AwdAo2Wz9Z6HhGiPonf2h
vZkc9ZxuE0jFIrsbJra4X7iyjXgi4vV4ARNg/9Ft6F4/OIbECgeDcBQqq4TlT2bZ
EfWVrBbqXoj5vNsLigIkd+AyUNwPYEcB5IFSiiOh98pC7BH3pg0m8U5YBjxe1i+9
EQOOG0Qtx+JigXZHu6bGE0Twy9zy+UzoKQIDAQABoyEwHzAdBgNVHQ4EFgQUGK1b
0DkL6aLalcfBc/Uj/SF08C0wDQYJKoZIhvcNAQENBQADggIBAMpXM82bJDpH1TlH
TvhU3Z7nfZdvEhOQfujaFUYiuNripuEKcFGn948+DvAG0FUN+uNlJoqOVs8D7InD
gWlA9zpqw5Cl5Hij/Wns9QbXuAHJeA23fVUoaM2A6v9ifcIQ1A+rDuRQAo6/64KW
ChTg2e99RBpfGOyqgeh7tLLe0lPPekVpKHFuXabokaKRDuBcVHcUL4tWXe3dcyqa
Ej/PJrrS+nWL0EGZ4q80CEd2LPuDzPxNGCJt/R7ZfadENWajcgcXGceh1QBzozrB
SL/Ya6wF9SrsB7V/r5wX0LM4ZdDaLWbtmUe5Op0h/ZMH25Sa8xAXVz+O9L6sWSoO
FaiYTOvAiyyPz+nsxKa3xYryDHno7eKSt+hGOcaurhxbdZaEFY/CegEc73tCt9xK
e9qF8O/WkDLmixuErw3f5en4IfzGR7p3lJAwW/8WD8C6HS39h/eE7dVZNaWgtQnZ
SgGjgZMTJqTcQ3aZmfuCZefxGFok8w6AIkdbnd1pdMBRjYu8aXgl2hQSB9ZADDE9
R5d3rXi0PkSFLIvsNjVa5KXrZk/tB0Hpfmepq7CufBqjP/LG9TieRoXzLYUKFF74
QRwjP+y7AJ+VDUTpY1NV1P+k+2raubU2bOnLF3zL5DtyoyieGPhyeMMvp0fRIxdg
bSl5VHgPXHNM8mcnndMAuzvl7jEK
-----END CERTIFICATE-----

View File

@@ -1,32 +0,0 @@
-----BEGIN CERTIFICATE-----
MIIFdTCCA12gAwIBAgIEQ5vCxzANBgkqhkiG9w0BAQ0FADBrMQswCQYDVQQGEwJY
WDELMAkGA1UECAwCWFgxCzAJBgNVBAcMAlhYMR4wHAYDVQQKDBVJMlAgQW5vbnlt
b3VzIE5ldHdvcmsxDDAKBgNVBAsMA0kyUDEUMBIGA1UEAwwLbHNAbWFpbC5pMnAw
HhcNMjMxMDE2MjAwNTA5WhcNMzMxMDEzMjAwNTA5WjBrMQswCQYDVQQGEwJYWDEL
MAkGA1UECAwCWFgxCzAJBgNVBAcMAlhYMR4wHAYDVQQKDBVJMlAgQW5vbnltb3Vz
IE5ldHdvcmsxDDAKBgNVBAsMA0kyUDEUMBIGA1UEAwwLbHNAbWFpbC5pMnAwggIi
MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDPcbKRtf4PzrDa0iRit0XrwnmA
2c1fJhkBipdPor7gMOAlkR82H1lkZSizR7kTZnr7vYqjDrOQr7bl5Dy3qo8/YCbZ
jsnUCTIIgIJQUxUlR40RjaSXphqzUEiXKHR6b0RahhFisQ3hlbbgzSch5YgSLKws
hOLi+eDSXw+HlwHlWFlT1XOKxSTJ/F3Bv40gxqZVC2pbxiPOeRZHQ6Ojw75lxTSF
gww2WzgztiWt4X9BO1yepnVqhAVRPmTfGUMfKzq9jkMzZKeQFV4uZSP9nCqzEpYd
WNDUfpTWiAQ9F+BwFXGusXXA3tGVwS7s6IEoiJFM5fsoJYfRoWGh3/1eirhBXW7U
M6oubMSTADyrvfjLfJBMmMnc2hNblRlKr0ZKUjMfv8cnyT4kQxlXLAHHXY2P89TM
TEVODkU48gnv6tC4t1JCb1/Da+3yVMjNX6rCzQfUwnLFrWthrwiI0NivAKFtiZjq
w1/ZQcYke2YyeqcfXMn+NTUA22Sm2mJoMo7jUf+rbM9Pi27/DncJgRGj5qwY0D3S
gc7829EjuZNPttGBmae1EmO7WQMB32cqdmItnV2FXpMhnn9h0u5H52kYqwn+mdtc
dTJRcbfKG1RTr3UjFISaTwL8qigMIkVXIzcpnr/R/sSeEs8xCqfsJ6rb4dCyFx+M
hqQcOCL5tumyd4W/LQIDAQABoyEwHzAdBgNVHQ4EFgQUgfaOG5HCnlW82wZ5BahL
GRO06igwDQYJKoZIhvcNAQENBQADggIBAKdVpqS9qF7gGotgXaVA1iP5YNsWlTvG
daGqeA/87//U21W6gpq82FhzsmsvUtXZfIeVIlDPI7WNDzS+A3K/KKrwM7dLgSie
r9eMl3D8WYPU95QF4mAlRyl7PCCsYoVjyvfro0iq3/iudIA5476rjfLdTXRi5hAT
qemPj0S+6sRjKEldRtGXrQATFlvLIWVYpgHijdDDx5M2hAz2y0mFxlDZTlA4BhL4
DwtGlVKmbc2x5MvIQM4UhbQqkxYS4gXnzf5Qx9QIytHfTr/hmbrkhKR1GCO31BSk
x9LhZxdI8LlwKSo6YgwXEB9E0M/tplaK9iZJFv4HPYLZrVJpb4IklMumyLMrgW5P
fR0dgKn+R9lk0emJ1Cu+qyyzf1vsLycYBwaEztINn4VK+/HfDFpnVCvJOyNuDmj5
KBLIoGdGoVfylmnc+e8zAXe+DY41fgniHMISOO78P8Bx9vTB+rhqnOUr9MzlUxPB
sKGjbXy2YynEqiGb+9g344v/+ukTSDenqTPHVzJ5uOi0iedy+3ASzUNN6GJocovP
167VOhwaETM0FwiKe0VdZRLLbbZ79CtJC0tmgcgPQPRa9Ldr6KN7u1J3D6lUp6zl
byPom10ueKONRb36t7ai79l2SEUZRSMkx6AXIU0JJ1SMtQtav7b5LkpYJfdL7+vO
dDx2/Za0VmdD
-----END CERTIFICATE-----

View File

@@ -2,13 +2,13 @@ Description: Enable UPnP usage in package
Author: r4sas <r4sas@i2pmail.org> Author: r4sas <r4sas@i2pmail.org>
Reviewed-By: r4sas <r4sas@i2pmail.org> Reviewed-By: r4sas <r4sas@i2pmail.org>
Last-Update: 2022-03-23 Last-Update: 2024-12-30
--- i2pd.orig/Makefile --- i2pd.orig/Makefile
+++ i2pd/Makefile +++ i2pd/Makefile
@@ -31,7 +31,7 @@ include filelist.mk @@ -31,7 +31,7 @@ # import source files lists
include filelist.mk
USE_AESNI := $(or $(USE_AESNI),yes)
USE_STATIC := $(or $(USE_STATIC),no) USE_STATIC := $(or $(USE_STATIC),no)
-USE_UPNP := $(or $(USE_UPNP),no) -USE_UPNP := $(or $(USE_UPNP),no)
+USE_UPNP := $(or $(USE_UPNP),yes) +USE_UPNP := $(or $(USE_UPNP),yes)

View File

@@ -2,7 +2,7 @@ Description: Disable LogsDirectory and LogsDirectoryMode options in service
Author: r4sas <r4sas@i2pmail.org> Author: r4sas <r4sas@i2pmail.org>
Reviewed-By: r4sas <r4sas@i2pmail.org> Reviewed-By: r4sas <r4sas@i2pmail.org>
Last-Update: 2023-05-17 Last-Update: 2024-07-19
--- a/contrib/i2pd.service --- a/contrib/i2pd.service
+++ b/contrib/i2pd.service +++ b/contrib/i2pd.service
@@ -15,5 +15,5 @@ Last-Update: 2023-05-17
+#LogsDirectory=i2pd +#LogsDirectory=i2pd
+#LogsDirectoryMode=0700 +#LogsDirectoryMode=0700
Type=forking Type=forking
ExecStart=/usr/sbin/i2pd --conf=/etc/i2pd/i2pd.conf --tunconf=/etc/i2pd/tunnels.conf --tunnelsdir=/etc/i2pd/tunnels.conf.d --pidfile=/run/i2pd/i2pd.pid --logfile=/var/log/i2pd/i2pd.log --daemon --service ExecStart=/usr/bin/i2pd --conf=/etc/i2pd/i2pd.conf --tunconf=/etc/i2pd/tunnels.conf --tunnelsdir=/etc/i2pd/tunnels.conf.d --pidfile=/run/i2pd/i2pd.pid --logfile=/var/log/i2pd/i2pd.log --daemon --service
ExecReload=/bin/sh -c "kill -HUP $MAINPID" ExecReload=/bin/sh -c "kill -HUP $MAINPID"

View File

@@ -2,13 +2,13 @@ Description: Enable UPnP usage in package
Author: r4sas <r4sas@i2pmail.org> Author: r4sas <r4sas@i2pmail.org>
Reviewed-By: r4sas <r4sas@i2pmail.org> Reviewed-By: r4sas <r4sas@i2pmail.org>
Last-Update: 2022-03-23 Last-Update: 2024-12-30
--- i2pd.orig/Makefile --- i2pd.orig/Makefile
+++ i2pd/Makefile +++ i2pd/Makefile
@@ -31,7 +31,7 @@ include filelist.mk @@ -31,7 +31,7 @@ # import source files lists
include filelist.mk
USE_AESNI := $(or $(USE_AESNI),yes)
USE_STATIC := $(or $(USE_STATIC),no) USE_STATIC := $(or $(USE_STATIC),no)
-USE_UPNP := $(or $(USE_UPNP),no) -USE_UPNP := $(or $(USE_UPNP),no)
+USE_UPNP := $(or $(USE_UPNP),yes) +USE_UPNP := $(or $(USE_UPNP),yes)

View File

@@ -2,7 +2,7 @@ Description: Disable LogsDirectory and LogsDirectoryMode options in service
Author: r4sas <r4sas@i2pmail.org> Author: r4sas <r4sas@i2pmail.org>
Reviewed-By: r4sas <r4sas@i2pmail.org> Reviewed-By: r4sas <r4sas@i2pmail.org>
Last-Update: 2023-05-17 Last-Update: 2024-07-19
--- a/contrib/i2pd.service --- a/contrib/i2pd.service
+++ b/contrib/i2pd.service +++ b/contrib/i2pd.service
@@ -15,5 +15,5 @@ Last-Update: 2023-05-17
+#LogsDirectory=i2pd +#LogsDirectory=i2pd
+#LogsDirectoryMode=0700 +#LogsDirectoryMode=0700
Type=forking Type=forking
ExecStart=/usr/sbin/i2pd --conf=/etc/i2pd/i2pd.conf --tunconf=/etc/i2pd/tunnels.conf --tunnelsdir=/etc/i2pd/tunnels.conf.d --pidfile=/run/i2pd/i2pd.pid --logfile=/var/log/i2pd/i2pd.log --daemon --service ExecStart=/usr/bin/i2pd --conf=/etc/i2pd/i2pd.conf --tunconf=/etc/i2pd/tunnels.conf --tunnelsdir=/etc/i2pd/tunnels.conf.d --pidfile=/run/i2pd/i2pd.pid --logfile=/var/log/i2pd/i2pd.log --daemon --service
ExecReload=/bin/sh -c "kill -HUP $MAINPID" ExecReload=/bin/sh -c "kill -HUP $MAINPID"

View File

@@ -243,7 +243,7 @@ verify = true
## Default: reg.i2p at "mainline" I2P Network ## Default: reg.i2p at "mainline" I2P Network
# defaulturl = http://shx5vqsw7usdaunyzr2qmes2fq37oumybpudrd4jjj4e4vk4uusa.b32.i2p/hosts.txt # defaulturl = http://shx5vqsw7usdaunyzr2qmes2fq37oumybpudrd4jjj4e4vk4uusa.b32.i2p/hosts.txt
## Optional subscriptions URLs, separated by comma ## Optional subscriptions URLs, separated by comma
# subscriptions = http://reg.i2p/hosts.txt,http://identiguy.i2p/hosts.txt,http://stats.i2p/cgi-bin/newhosts.txt,http://rus.i2p/hosts.txt # subscriptions = http://reg.i2p/hosts.txt,http://identiguy.i2p/hosts.txt,http://stats.i2p/cgi-bin/newhosts.txt
[limits] [limits]
## Maximum active transit sessions (default: 5000) ## Maximum active transit sessions (default: 5000)
@@ -277,9 +277,3 @@ verify = true
## Save full addresses on disk (default: true) ## Save full addresses on disk (default: true)
# addressbook = true # addressbook = true
[cpuext]
## Use CPU AES-NI instructions set when work with cryptography when available (default: true)
# aesni = true
## Force usage of CPU instructions set, even if they not found (default: false)
## DO NOT TOUCH that option if you really don't know what are you doing!
# force = false

View File

@@ -11,7 +11,7 @@ RuntimeDirectoryMode=0700
LogsDirectory=i2pd LogsDirectory=i2pd
LogsDirectoryMode=0700 LogsDirectoryMode=0700
Type=forking Type=forking
ExecStart=/usr/sbin/i2pd --conf=/etc/i2pd/i2pd.conf --tunconf=/etc/i2pd/tunnels.conf --tunnelsdir=/etc/i2pd/tunnels.conf.d --pidfile=/run/i2pd/i2pd.pid --logfile=/var/log/i2pd/i2pd.log --daemon --service ExecStart=/usr/bin/i2pd --conf=/etc/i2pd/i2pd.conf --tunconf=/etc/i2pd/tunnels.conf --tunnelsdir=/etc/i2pd/tunnels.conf.d --pidfile=/run/i2pd/i2pd.pid --logfile=/var/log/i2pd/i2pd.log --daemon --service
ExecReload=/bin/sh -c "kill -HUP $MAINPID" ExecReload=/bin/sh -c "kill -HUP $MAINPID"
PIDFile=/run/i2pd/i2pd.pid PIDFile=/run/i2pd/i2pd.pid
### Uncomment, if auto restart needed ### Uncomment, if auto restart needed

View File

@@ -7,7 +7,7 @@ tunconf="/etc/i2pd/tunnels.conf"
tundir="/etc/i2pd/tunnels.conf.d" tundir="/etc/i2pd/tunnels.conf.d"
name="i2pd" name="i2pd"
command="/usr/sbin/i2pd" command="/usr/bin/i2pd"
command_args="--service --daemon --log=file --logfile=$logfile --conf=$mainconf --tunconf=$tunconf --tunnelsdir=$tundir --pidfile=$pidfile" command_args="--service --daemon --log=file --logfile=$logfile --conf=$mainconf --tunconf=$tunconf --tunnelsdir=$tundir --pidfile=$pidfile"
description="i2p router written in C++" description="i2p router written in C++"
required_dirs="/var/lib/i2pd" required_dirs="/var/lib/i2pd"

View File

@@ -1,7 +1,7 @@
%define git_hash %(git rev-parse HEAD | cut -c -7) %define git_hash %(git rev-parse HEAD | cut -c -7)
Name: i2pd-git Name: i2pd-git
Version: 2.52.0 Version: 2.55.0
Release: git%{git_hash}%{?dist} Release: git%{git_hash}%{?dist}
Summary: I2P router written in C++ Summary: I2P router written in C++
Conflicts: i2pd Conflicts: i2pd
@@ -24,6 +24,10 @@ BuildRequires: openssl-devel
BuildRequires: miniupnpc-devel BuildRequires: miniupnpc-devel
BuildRequires: systemd-units BuildRequires: systemd-units
%if 0%{?fedora} == 41
BuildRequires: openssl-devel-engine
%endif
Requires: logrotate Requires: logrotate
Requires: systemd Requires: systemd
Requires(pre): %{_sbindir}/useradd %{_sbindir}/groupadd Requires(pre): %{_sbindir}/useradd %{_sbindir}/groupadd
@@ -93,7 +97,7 @@ pushd build
%endif %endif
chrpath -d i2pd chrpath -d i2pd
%{__install} -D -m 755 i2pd %{buildroot}%{_sbindir}/i2pd %{__install} -D -m 755 i2pd %{buildroot}%{_bindir}/i2pd
%{__install} -d -m 755 %{buildroot}%{_datadir}/i2pd %{__install} -d -m 755 %{buildroot}%{_datadir}/i2pd
%{__install} -d -m 700 %{buildroot}%{_sharedstatedir}/i2pd %{__install} -d -m 700 %{buildroot}%{_sharedstatedir}/i2pd
%{__install} -d -m 700 %{buildroot}%{_localstatedir}/log/i2pd %{__install} -d -m 700 %{buildroot}%{_localstatedir}/log/i2pd
@@ -129,7 +133,7 @@ getent passwd i2pd >/dev/null || \
%files %files
%doc LICENSE README.md contrib/i2pd.conf contrib/subscriptions.txt contrib/tunnels.conf contrib/tunnels.d %doc LICENSE README.md contrib/i2pd.conf contrib/subscriptions.txt contrib/tunnels.conf contrib/tunnels.d
%{_sbindir}/i2pd %{_bindir}/i2pd
%config(noreplace) %{_sysconfdir}/i2pd/*.conf %config(noreplace) %{_sysconfdir}/i2pd/*.conf
%config(noreplace) %{_sysconfdir}/i2pd/tunnels.conf.d/*.conf %config(noreplace) %{_sysconfdir}/i2pd/tunnels.conf.d/*.conf
%config %{_sysconfdir}/i2pd/subscriptions.txt %config %{_sysconfdir}/i2pd/subscriptions.txt
@@ -144,6 +148,18 @@ getent passwd i2pd >/dev/null || \
%changelog %changelog
* Mon Dec 30 2024 orignal <orignal@i2pmail.org> - 2.55.0
- update to 2.55.0
* Sun Oct 6 2024 orignal <orignal@i2pmail.org> - 2.54.0
- update to 2.54.0
* Tue Jul 30 2024 orignal <orignal@i2pmail.org> - 2.53.1
- update to 2.53.1
* Fri Jul 19 2024 orignal <orignal@i2pmail.org> - 2.53.0
- update to 2.53.0
* Sun May 12 2024 orignal <orignal@i2pmail.org> - 2.52.0 * Sun May 12 2024 orignal <orignal@i2pmail.org> - 2.52.0
- update to 2.52.0 - update to 2.52.0

View File

@@ -1,5 +1,5 @@
Name: i2pd Name: i2pd
Version: 2.52.0 Version: 2.55.0
Release: 1%{?dist} Release: 1%{?dist}
Summary: I2P router written in C++ Summary: I2P router written in C++
Conflicts: i2pd-git Conflicts: i2pd-git
@@ -22,6 +22,10 @@ BuildRequires: openssl-devel
BuildRequires: miniupnpc-devel BuildRequires: miniupnpc-devel
BuildRequires: systemd-units BuildRequires: systemd-units
%if 0%{?fedora} == 41
BuildRequires: openssl-devel-engine
%endif
Requires: logrotate Requires: logrotate
Requires: systemd Requires: systemd
Requires(pre): %{_sbindir}/useradd %{_sbindir}/groupadd Requires(pre): %{_sbindir}/useradd %{_sbindir}/groupadd
@@ -91,7 +95,7 @@ pushd build
%endif %endif
chrpath -d i2pd chrpath -d i2pd
%{__install} -D -m 755 i2pd %{buildroot}%{_sbindir}/i2pd %{__install} -D -m 755 i2pd %{buildroot}%{_bindir}/i2pd
%{__install} -d -m 755 %{buildroot}%{_datadir}/i2pd %{__install} -d -m 755 %{buildroot}%{_datadir}/i2pd
%{__install} -d -m 700 %{buildroot}%{_sharedstatedir}/i2pd %{__install} -d -m 700 %{buildroot}%{_sharedstatedir}/i2pd
%{__install} -d -m 700 %{buildroot}%{_localstatedir}/log/i2pd %{__install} -d -m 700 %{buildroot}%{_localstatedir}/log/i2pd
@@ -127,7 +131,7 @@ getent passwd i2pd >/dev/null || \
%files %files
%doc LICENSE README.md contrib/i2pd.conf contrib/subscriptions.txt contrib/tunnels.conf contrib/tunnels.d %doc LICENSE README.md contrib/i2pd.conf contrib/subscriptions.txt contrib/tunnels.conf contrib/tunnels.d
%{_sbindir}/i2pd %{_bindir}/i2pd
%config(noreplace) %{_sysconfdir}/i2pd/*.conf %config(noreplace) %{_sysconfdir}/i2pd/*.conf
%config(noreplace) %{_sysconfdir}/i2pd/tunnels.conf.d/*.conf %config(noreplace) %{_sysconfdir}/i2pd/tunnels.conf.d/*.conf
%config %{_sysconfdir}/i2pd/subscriptions.txt %config %{_sysconfdir}/i2pd/subscriptions.txt
@@ -142,6 +146,18 @@ getent passwd i2pd >/dev/null || \
%changelog %changelog
* Mon Dec 30 2024 orignal <orignal@i2pmail.org> - 2.55.0
- update to 2.55.0
* Sun Oct 6 2024 orignal <orignal@i2pmail.org> - 2.54.0
- update to 2.54.0
* Tue Jul 30 2024 orignal <orignal@i2pmail.org> - 2.53.1
- update to 2.53.1
* Fri Jul 19 2024 orignal <orignal@i2pmail.org> - 2.53.0
- update to 2.53.0
* Sun May 12 2024 orignal <orignal@i2pmail.org> - 2.52.0 * Sun May 12 2024 orignal <orignal@i2pmail.org> - 2.52.0
- update to 2.52.0 - update to 2.52.0

View File

@@ -5,6 +5,7 @@ port = 6668
destination = irc.ilita.i2p destination = irc.ilita.i2p
destinationport = 6667 destinationport = 6667
keys = irc-keys.dat keys = irc-keys.dat
i2p.streaming.profile=2
#[IRC-IRC2P] #[IRC-IRC2P]
#type = client #type = client

View File

@@ -8,4 +8,4 @@ env LOGFILE="/var/log/i2pd/i2pd.log"
expect fork expect fork
exec /usr/sbin/i2pd --daemon --service --log=file --logfile=$LOGFILE exec /usr/bin/i2pd --daemon --service --log=file --logfile=$LOGFILE

View File

@@ -149,12 +149,10 @@ namespace util
LogPrint(eLogDebug, "FS: Certificates directory: ", certsdir); LogPrint(eLogDebug, "FS: Certificates directory: ", certsdir);
bool precomputation; i2p::config::GetOption("precomputation.elgamal", precomputation); bool precomputation; i2p::config::GetOption("precomputation.elgamal", precomputation);
bool aesni; i2p::config::GetOption("cpuext.aesni", aesni);
bool forceCpuExt; i2p::config::GetOption("cpuext.force", forceCpuExt);
bool ssu; i2p::config::GetOption("ssu", ssu); bool ssu; i2p::config::GetOption("ssu", ssu);
if (!ssu && i2p::config::IsDefault ("precomputation.elgamal")) if (!ssu && i2p::config::IsDefault ("precomputation.elgamal"))
precomputation = false; // we don't elgamal table if no ssu, unless it's specified explicitly precomputation = false; // we don't elgamal table if no ssu, unless it's specified explicitly
i2p::crypto::InitCrypto (precomputation, aesni, forceCpuExt); i2p::crypto::InitCrypto (precomputation);
i2p::transport::InitAddressFromIface (); // get address4/6 from interfaces i2p::transport::InitAddressFromIface (); // get address4/6 from interfaces
@@ -188,7 +186,7 @@ namespace util
std::string bandwidth; i2p::config::GetOption("bandwidth", bandwidth); std::string bandwidth; i2p::config::GetOption("bandwidth", bandwidth);
if (bandwidth.length () > 0) if (bandwidth.length () > 0)
{ {
if (bandwidth[0] >= 'K' && bandwidth[0] <= 'X') if (bandwidth.length () == 1 && ((bandwidth[0] >= 'K' && bandwidth[0] <= 'P') || bandwidth[0] == 'X' ))
{ {
i2p::context.SetBandwidth (bandwidth[0]); i2p::context.SetBandwidth (bandwidth[0]);
LogPrint(eLogInfo, "Daemon: Bandwidth set to ", i2p::context.GetBandwidthLimit (), "KBps"); LogPrint(eLogInfo, "Daemon: Bandwidth set to ", i2p::context.GetBandwidthLimit (), "KBps");

View File

@@ -417,6 +417,15 @@ namespace http {
} }
} }
static void ShowHop(std::stringstream& s, const i2p::data::IdentityEx& ident)
{
auto identHash = ident.GetIdentHash();
auto router = i2p::data::netdb.FindRouter(identHash);
s << i2p::data::GetIdentHashAbbreviation(identHash);
if (router)
s << "<small style=\"color:gray\"> " << router->GetBandwidthCap() << "</small>";
}
static void ShowLeaseSetDestination (std::stringstream& s, std::shared_ptr<const i2p::client::LeaseSetDestination> dest, uint32_t token) static void ShowLeaseSetDestination (std::stringstream& s, std::shared_ptr<const i2p::client::LeaseSetDestination> dest, uint32_t token)
{ {
s << "<b>Base32:</b><br>\r\n<textarea readonly cols=\"80\" rows=\"1\">"; s << "<b>Base32:</b><br>\r\n<textarea readonly cols=\"80\" rows=\"1\">";
@@ -482,7 +491,9 @@ namespace http {
it->VisitTunnelHops( it->VisitTunnelHops(
[&s](std::shared_ptr<const i2p::data::IdentityEx> hopIdent) [&s](std::shared_ptr<const i2p::data::IdentityEx> hopIdent)
{ {
s << "&#8658; " << i2p::data::GetIdentHashAbbreviation (hopIdent->GetIdentHash ()) << " "; s << "&#8658; ";
ShowHop(s, *hopIdent);
s << " ";
} }
); );
} }
@@ -503,7 +514,9 @@ namespace http {
it->VisitTunnelHops( it->VisitTunnelHops(
[&s](std::shared_ptr<const i2p::data::IdentityEx> hopIdent) [&s](std::shared_ptr<const i2p::data::IdentityEx> hopIdent)
{ {
s << " " << i2p::data::GetIdentHashAbbreviation (hopIdent->GetIdentHash ()) << " &#8658;"; s << " ";
ShowHop(s, *hopIdent);
s << " &#8658;";
} }
); );
} }
@@ -688,6 +701,7 @@ namespace http {
{ {
s << "<b>" << tr("Tunnels") << ":</b><br>\r\n"; s << "<b>" << tr("Tunnels") << ":</b><br>\r\n";
s << "<b>" << tr("Queue size") << ":</b> " << i2p::tunnel::tunnels.GetQueueSize () << "<br>\r\n<br>\r\n"; s << "<b>" << tr("Queue size") << ":</b> " << i2p::tunnel::tunnels.GetQueueSize () << "<br>\r\n<br>\r\n";
s << "<b>" << tr("TBM Queue size") << ":</b> " << i2p::tunnel::tunnels.GetTBMQueueSize () << "<br>\r\n<br>\r\n";
auto ExplPool = i2p::tunnel::tunnels.GetExploratoryPool (); auto ExplPool = i2p::tunnel::tunnels.GetExploratoryPool ();
@@ -699,7 +713,9 @@ namespace http {
it->VisitTunnelHops( it->VisitTunnelHops(
[&s](std::shared_ptr<const i2p::data::IdentityEx> hopIdent) [&s](std::shared_ptr<const i2p::data::IdentityEx> hopIdent)
{ {
s << "&#8658; " << i2p::data::GetIdentHashAbbreviation (hopIdent->GetIdentHash ()) << " "; s << "&#8658; ";
ShowHop(s, *hopIdent);
s << " ";
} }
); );
} }
@@ -720,7 +736,9 @@ namespace http {
it->VisitTunnelHops( it->VisitTunnelHops(
[&s](std::shared_ptr<const i2p::data::IdentityEx> hopIdent) [&s](std::shared_ptr<const i2p::data::IdentityEx> hopIdent)
{ {
s << " " << i2p::data::GetIdentHashAbbreviation (hopIdent->GetIdentHash ()) << " &#8658;"; s << " ";
ShowHop(s, *hopIdent);
s << " &#8658;";
} }
); );
} }
@@ -808,7 +826,7 @@ namespace http {
if (i2p::tunnel::tunnels.CountTransitTunnels()) if (i2p::tunnel::tunnels.CountTransitTunnels())
{ {
s << "<b>" << tr("Transit Tunnels") << ":</b><br>\r\n"; s << "<b>" << tr("Transit Tunnels") << ":</b><br>\r\n";
s << "<table><thead><th>&#8658;</th><th>ID</th><th>&#8658;</th><th>" << tr("Amount") << "</th></thead><tbody class=\"tableitem\">"; s << "<table><thead><th>&#8658;</th><th>ID</th><th>&#8658;</th><th>" << tr("Amount") << "</th><th>" << tr("Next") << "</th></thead><tbody class=\"tableitem\">";
for (const auto& it: i2p::tunnel::tunnels.GetTransitTunnels ()) for (const auto& it: i2p::tunnel::tunnels.GetTransitTunnels ())
{ {
if (std::dynamic_pointer_cast<i2p::tunnel::TransitTunnelGateway>(it)) if (std::dynamic_pointer_cast<i2p::tunnel::TransitTunnelGateway>(it))
@@ -818,7 +836,7 @@ namespace http {
else else
s << "<tr><td>&#8658;</td><td>" << it->GetTunnelID () << "</td><td>&#8658;</td><td>"; s << "<tr><td>&#8658;</td><td>" << it->GetTunnelID () << "</td><td>&#8658;</td><td>";
ShowTraffic(s, it->GetNumTransmittedBytes ()); ShowTraffic(s, it->GetNumTransmittedBytes ());
s << "</td></tr>\r\n"; s << "</td><td>" << it->GetNextPeerName () << "</td></tr>\r\n";
} }
s << "</tbody></table>\r\n"; s << "</tbody></table>\r\n";
} }
@@ -1463,13 +1481,13 @@ namespace http {
reply.body = content; reply.body = content;
m_SendBuffer = reply.to_string(); m_SendBuffer = reply.to_string();
boost::asio::async_write (*m_Socket, boost::asio::buffer(m_SendBuffer), boost::asio::async_write (*m_Socket, boost::asio::buffer(m_SendBuffer), boost::asio::transfer_all (),
std::bind (&HTTPConnection::Terminate, shared_from_this (), std::placeholders::_1)); std::bind (&HTTPConnection::Terminate, shared_from_this (), std::placeholders::_1));
} }
HTTPServer::HTTPServer (const std::string& address, int port): HTTPServer::HTTPServer (const std::string& address, int port):
m_IsRunning (false), m_Thread (nullptr), m_Work (m_Service), m_IsRunning (false), m_Thread (nullptr), m_Work (m_Service.get_executor ()),
m_Acceptor (m_Service, boost::asio::ip::tcp::endpoint (boost::asio::ip::address::from_string(address), port)), m_Acceptor (m_Service, boost::asio::ip::tcp::endpoint (boost::asio::ip::make_address(address), port)),
m_Hostname(address) m_Hostname(address)
{ {
} }

View File

@@ -83,8 +83,8 @@ namespace http
bool m_IsRunning; bool m_IsRunning;
std::unique_ptr<std::thread> m_Thread; std::unique_ptr<std::thread> m_Thread;
boost::asio::io_service m_Service; boost::asio::io_context m_Service;
boost::asio::io_service::work m_Work; boost::asio::executor_work_guard<boost::asio::io_context::executor_type> m_Work;
boost::asio::ip::tcp::acceptor m_Acceptor; boost::asio::ip::tcp::acceptor m_Acceptor;
std::string m_Hostname; std::string m_Hostname;
}; };

View File

@@ -8,14 +8,12 @@
#include <stdio.h> #include <stdio.h>
#include <sstream> #include <sstream>
#include <iomanip>
#include <openssl/x509.h> #include <openssl/x509.h>
#include <openssl/pem.h> #include <openssl/pem.h>
// Use global placeholders from boost introduced when local_time.hpp is loaded // Use global placeholders from boost introduced when local_time.hpp is loaded
#define BOOST_BIND_GLOBAL_PLACEHOLDERS #define BOOST_BIND_GLOBAL_PLACEHOLDERS
#include <boost/date_time/local_time/local_time.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/property_tree/json_parser.hpp> #include <boost/property_tree/json_parser.hpp>
#include <boost/lexical_cast.hpp> #include <boost/lexical_cast.hpp>
@@ -33,7 +31,7 @@ namespace client
{ {
I2PControlService::I2PControlService (const std::string& address, int port): I2PControlService::I2PControlService (const std::string& address, int port):
m_IsRunning (false), m_Thread (nullptr), m_IsRunning (false), m_Thread (nullptr),
m_Acceptor (m_Service, boost::asio::ip::tcp::endpoint(boost::asio::ip::address::from_string(address), port)), m_Acceptor (m_Service, boost::asio::ip::tcp::endpoint(boost::asio::ip::make_address(address), port)),
m_SSLContext (boost::asio::ssl::context::sslv23), m_SSLContext (boost::asio::ssl::context::sslv23),
m_ShutdownTimer (m_Service) m_ShutdownTimer (m_Service)
{ {
@@ -47,15 +45,29 @@ namespace client
i2pcp_crt = i2p::fs::DataDirPath(i2pcp_crt); i2pcp_crt = i2p::fs::DataDirPath(i2pcp_crt);
if (i2pcp_key.at(0) != '/') if (i2pcp_key.at(0) != '/')
i2pcp_key = i2p::fs::DataDirPath(i2pcp_key); i2pcp_key = i2p::fs::DataDirPath(i2pcp_key);
if (!i2p::fs::Exists (i2pcp_crt) || !i2p::fs::Exists (i2pcp_key)) { if (!i2p::fs::Exists (i2pcp_crt) || !i2p::fs::Exists (i2pcp_key))
{
LogPrint (eLogInfo, "I2PControl: Creating new certificate for control connection"); LogPrint (eLogInfo, "I2PControl: Creating new certificate for control connection");
CreateCertificate (i2pcp_crt.c_str(), i2pcp_key.c_str()); CreateCertificate (i2pcp_crt.c_str(), i2pcp_key.c_str());
} else { }
else
LogPrint(eLogDebug, "I2PControl: Using cert from ", i2pcp_crt); LogPrint(eLogDebug, "I2PControl: Using cert from ", i2pcp_crt);
}
m_SSLContext.set_options (boost::asio::ssl::context::default_workarounds | boost::asio::ssl::context::no_sslv2 | boost::asio::ssl::context::single_dh_use); m_SSLContext.set_options (boost::asio::ssl::context::default_workarounds | boost::asio::ssl::context::no_sslv2 | boost::asio::ssl::context::single_dh_use);
m_SSLContext.use_certificate_file (i2pcp_crt, boost::asio::ssl::context::pem); boost::system::error_code ec;
m_SSLContext.use_private_key_file (i2pcp_key, boost::asio::ssl::context::pem); m_SSLContext.use_certificate_file (i2pcp_crt, boost::asio::ssl::context::pem, ec);
if (!ec)
m_SSLContext.use_private_key_file (i2pcp_key, boost::asio::ssl::context::pem, ec);
if (ec)
{
LogPrint (eLogInfo, "I2PControl: Failed to load ceritifcate: ", ec.message (), ". Recreating");
CreateCertificate (i2pcp_crt.c_str(), i2pcp_key.c_str());
m_SSLContext.use_certificate_file (i2pcp_crt, boost::asio::ssl::context::pem, ec);
if (!ec)
m_SSLContext.use_private_key_file (i2pcp_key, boost::asio::ssl::context::pem, ec);
if (ec)
// give up
LogPrint (eLogError, "I2PControl: Can't load certificates");
}
// handlers // handlers
m_MethodHandlers["Authenticate"] = &I2PControlService::AuthenticateHandler; m_MethodHandlers["Authenticate"] = &I2PControlService::AuthenticateHandler;
@@ -258,9 +270,9 @@ namespace client
header << "Content-Length: " << boost::lexical_cast<std::string>(len) << "\r\n"; header << "Content-Length: " << boost::lexical_cast<std::string>(len) << "\r\n";
header << "Content-Type: application/json\r\n"; header << "Content-Type: application/json\r\n";
header << "Date: "; header << "Date: ";
auto facet = new boost::local_time::local_time_facet ("%a, %d %b %Y %H:%M:%S GMT"); std::time_t t = std::time (nullptr);
header.imbue(std::locale (header.getloc(), facet)); std::tm tm = *std::gmtime (&t);
header << boost::posix_time::second_clock::local_time() << "\r\n"; header << std::put_time(&tm, "%a, %d %b %Y %T GMT") << "\r\n";
header << "\r\n"; header << "\r\n";
offset = header.str ().size (); offset = header.str ().size ();
memcpy (buf->data (), header.str ().c_str (), offset); memcpy (buf->data (), header.str ().c_str (), offset);
@@ -405,7 +417,7 @@ namespace client
X509_NAME_add_entry_by_txt (name, "O", MBSTRING_ASC, (unsigned char *)I2P_CONTROL_CERTIFICATE_ORGANIZATION, -1, -1, 0); // organization X509_NAME_add_entry_by_txt (name, "O", MBSTRING_ASC, (unsigned char *)I2P_CONTROL_CERTIFICATE_ORGANIZATION, -1, -1, 0); // organization
X509_NAME_add_entry_by_txt (name, "CN", MBSTRING_ASC, (unsigned char *)I2P_CONTROL_CERTIFICATE_COMMON_NAME, -1, -1, 0); // common name X509_NAME_add_entry_by_txt (name, "CN", MBSTRING_ASC, (unsigned char *)I2P_CONTROL_CERTIFICATE_COMMON_NAME, -1, -1, 0); // common name
X509_set_issuer_name (x509, name); // set issuer to ourselves X509_set_issuer_name (x509, name); // set issuer to ourselves
X509_sign (x509, pkey, EVP_sha1 ()); // sign X509_sign (x509, pkey, EVP_sha1 ()); // sign, last param must be NULL for EdDSA
// save cert // save cert
if ((f = fopen (crt_path, "wb")) != NULL) { if ((f = fopen (crt_path, "wb")) != NULL) {

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2022, The PurpleI2P Project * Copyright (c) 2013-2024, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@@ -88,7 +88,7 @@ namespace client
bool m_IsRunning; bool m_IsRunning;
std::thread * m_Thread; std::thread * m_Thread;
boost::asio::io_service m_Service; boost::asio::io_context m_Service;
boost::asio::ip::tcp::acceptor m_Acceptor; boost::asio::ip::tcp::acceptor m_Acceptor;
boost::asio::ssl::context m_SSLContext; boost::asio::ssl::context m_SSLContext;
boost::asio::deadline_timer m_ShutdownTimer; boost::asio::deadline_timer m_ShutdownTimer;

View File

@@ -52,7 +52,7 @@ namespace transport
{ {
m_IsRunning = true; m_IsRunning = true;
LogPrint(eLogInfo, "UPnP: Starting"); LogPrint(eLogInfo, "UPnP: Starting");
m_Service.post (std::bind (&UPnP::Discover, this)); boost::asio::post (m_Service, std::bind (&UPnP::Discover, this));
std::unique_lock<std::mutex> l(m_StartedMutex); std::unique_lock<std::mutex> l(m_StartedMutex);
m_Thread.reset (new std::thread (std::bind (&UPnP::Run, this))); m_Thread.reset (new std::thread (std::bind (&UPnP::Run, this)));
m_Started.wait_for (l, std::chrono::seconds (5)); // 5 seconds maximum m_Started.wait_for (l, std::chrono::seconds (5)); // 5 seconds maximum
@@ -115,10 +115,16 @@ namespace transport
return; return;
} }
#if (MINIUPNPC_API_VERSION >= 18)
err = UPNP_GetValidIGD (m_Devlist, &m_upnpUrls, &m_upnpData, m_NetworkAddr, sizeof (m_NetworkAddr),
m_externalIPAddress, sizeof (m_externalIPAddress));
#else
err = UPNP_GetValidIGD (m_Devlist, &m_upnpUrls, &m_upnpData, m_NetworkAddr, sizeof (m_NetworkAddr)); err = UPNP_GetValidIGD (m_Devlist, &m_upnpUrls, &m_upnpData, m_NetworkAddr, sizeof (m_NetworkAddr));
#endif
m_upnpUrlsInitialized=err!=0; m_upnpUrlsInitialized=err!=0;
if (err == UPNP_IGD_VALID_CONNECTED) if (err == UPNP_IGD_VALID_CONNECTED)
{ {
#if (MINIUPNPC_API_VERSION < 18)
err = UPNP_GetExternalIPAddress (m_upnpUrls.controlURL, m_upnpData.first.servicetype, m_externalIPAddress); err = UPNP_GetExternalIPAddress (m_upnpUrls.controlURL, m_upnpData.first.servicetype, m_externalIPAddress);
if(err != UPNPCOMMAND_SUCCESS) if(err != UPNPCOMMAND_SUCCESS)
{ {
@@ -126,6 +132,7 @@ namespace transport
return; return;
} }
else else
#endif
{ {
LogPrint (eLogError, "UPnP: Found Internet Gateway Device ", m_upnpUrls.controlURL); LogPrint (eLogError, "UPnP: Found Internet Gateway Device ", m_upnpUrls.controlURL);
if (!m_externalIPAddress[0]) if (!m_externalIPAddress[0])
@@ -143,7 +150,7 @@ namespace transport
// UPnP discovered // UPnP discovered
LogPrint (eLogDebug, "UPnP: ExternalIPAddress is ", m_externalIPAddress); LogPrint (eLogDebug, "UPnP: ExternalIPAddress is ", m_externalIPAddress);
i2p::context.UpdateAddress (boost::asio::ip::address::from_string (m_externalIPAddress)); i2p::context.UpdateAddress (boost::asio::ip::make_address (m_externalIPAddress));
// port mapping // port mapping
PortMapping (); PortMapping ();
} }

View File

@@ -67,7 +67,7 @@ namespace transport
std::unique_ptr<std::thread> m_Thread; std::unique_ptr<std::thread> m_Thread;
std::condition_variable m_Started; std::condition_variable m_Started;
std::mutex m_StartedMutex; std::mutex m_StartedMutex;
boost::asio::io_service m_Service; boost::asio::io_context m_Service;
boost::asio::deadline_timer m_Timer; boost::asio::deadline_timer m_Timer;
bool m_upnpUrlsInitialized = false; bool m_upnpUrlsInitialized = false;
struct UPNPUrls m_upnpUrls; struct UPNPUrls m_upnpUrls;

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2020, The PurpleI2P Project * Copyright (c) 2013-2024, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@@ -25,6 +25,7 @@
#include "RouterContext.h" #include "RouterContext.h"
#include "ClientContext.h" #include "ClientContext.h"
#include "Transports.h" #include "Transports.h"
#include "util.h"
void handle_signal(int sig) void handle_signal(int sig)
{ {
@@ -220,6 +221,7 @@ namespace i2p
void DaemonLinux::run () void DaemonLinux::run ()
{ {
i2p::util::SetThreadName ("i2pd-daemon");
while (running) while (running)
{ {
std::this_thread::sleep_for (std::chrono::seconds(1)); std::this_thread::sleep_for (std::chrono::seconds(1));

5
debian/NEWS vendored Normal file
View File

@@ -0,0 +1,5 @@
i2pd (2.53.0-1) unstable; urgency=medium
i2pd binary moved from /usr/sbin to /usr/bin. Please check your scripts if you used the old path.
-- r4sas <r4sas@i2pmail.org> Fri, 19 Jul 2024 16:00:00 +0000

25
debian/changelog vendored
View File

@@ -1,3 +1,28 @@
i2pd (2.55.0-1) unstable; urgency=medium
* updated to version 2.55.0
-- orignal <orignal@i2pmail.org> Mon, 30 Dec 2024 16:00:00 +0000
i2pd (2.54.0-1) unstable; urgency=medium
* updated to version 2.54.0/0.9.64
-- orignal <orignal@i2pmail.org> Sun, 6 Oct 2024 16:00:00 +0000
i2pd (2.53.1-1) unstable; urgency=medium
* updated to version 2.53.1
-- orignal <orignal@i2pmail.org> Tue, 30 Jul 2024 16:00:00 +0000
i2pd (2.53.0-1) unstable; urgency=medium
* updated to version 2.53.0/0.9.63
* binary moved from /usr/sbin to /usr/bin
-- r4sas <r4sas@i2pmail.org> Sat, 20 Jul 2024 15:10:00 +0000
i2pd (2.52.0-1) unstable; urgency=medium i2pd (2.52.0-1) unstable; urgency=medium
* updated to version 2.52.0 * updated to version 2.52.0

2
debian/i2pd.init vendored
View File

@@ -13,7 +13,7 @@
PATH=/sbin:/usr/sbin:/bin:/usr/bin PATH=/sbin:/usr/sbin:/bin:/usr/bin
DESC=i2pd # Introduce a short description here DESC=i2pd # Introduce a short description here
NAME=i2pd # Introduce the short server's name here NAME=i2pd # Introduce the short server's name here
DAEMON=/usr/sbin/$NAME # Introduce the server's location here DAEMON=/usr/bin/$NAME # Introduce the server's location here
DAEMON_OPTS="" # Arguments to run the daemon with DAEMON_OPTS="" # Arguments to run the daemon with
PIDFILE=/var/run/$NAME/$NAME.pid PIDFILE=/var/run/$NAME/$NAME.pid
I2PCONF=/etc/$NAME/i2pd.conf I2PCONF=/etc/$NAME/i2pd.conf

4
debian/i2pd.install vendored
View File

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

View File

@@ -2,13 +2,13 @@ Description: Enable UPnP usage in package
Author: r4sas <r4sas@i2pmail.org> Author: r4sas <r4sas@i2pmail.org>
Reviewed-By: r4sas <r4sas@i2pmail.org> Reviewed-By: r4sas <r4sas@i2pmail.org>
Last-Update: 2022-03-23 Last-Update: 2024-12-30
--- i2pd.orig/Makefile --- i2pd.orig/Makefile
+++ i2pd/Makefile +++ i2pd/Makefile
@@ -31,7 +31,7 @@ include filelist.mk @@ -31,7 +31,7 @@ # import source files lists
include filelist.mk
USE_AESNI := $(or $(USE_AESNI),yes)
USE_STATIC := $(or $(USE_STATIC),no) USE_STATIC := $(or $(USE_STATIC),no)
-USE_UPNP := $(or $(USE_UPNP),no) -USE_UPNP := $(or $(USE_UPNP),no)
+USE_UPNP := $(or $(USE_UPNP),yes) +USE_UPNP := $(or $(USE_UPNP),yes)

View File

@@ -64,7 +64,7 @@ namespace chinese // language namespace
{"Full cone NAT", "全锥型NAT"}, {"Full cone NAT", "全锥型NAT"},
{"No Descriptors", "无描述符"}, {"No Descriptors", "无描述符"},
{"Uptime", "运行时间"}, {"Uptime", "运行时间"},
{"Network status", "IPv4 网络状态"}, {"Network status", "网络状态"},
{"Network status v6", "IPv6 网络状态"}, {"Network status v6", "IPv6 网络状态"},
{"Stopping in", "距停止还有:"}, {"Stopping in", "距停止还有:"},
{"Family", "家族"}, {"Family", "家族"},

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2022, The PurpleI2P Project * Copyright (c) 2013-2025, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@@ -152,11 +152,11 @@ namespace data
m_BlindedSigType = m_SigType; m_BlindedSigType = m_SigType;
} }
BlindedPublicKey::BlindedPublicKey (const std::string& b33): BlindedPublicKey::BlindedPublicKey (std::string_view b33):
m_SigType (0) // 0 means invalid, we can't blind DSA, set it later m_SigType (0) // 0 means invalid, we can't blind DSA, set it later
{ {
uint8_t addr[40]; // TODO: define length from b33 uint8_t addr[40]; // TODO: define length from b33
size_t l = i2p::data::Base32ToByteStream (b33.c_str (), b33.length (), addr, 40); size_t l = i2p::data::Base32ToByteStream (b33.data (), b33.length (), addr, 40);
if (l < 32) if (l < 32)
{ {
LogPrint (eLogError, "Blinding: Malformed b33 ", b33); LogPrint (eLogError, "Blinding: Malformed b33 ", b33);

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2020, The PurpleI2P Project * Copyright (c) 2013-2025, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@@ -11,6 +11,7 @@
#include <inttypes.h> #include <inttypes.h>
#include <string> #include <string>
#include <string_view>
#include <vector> #include <vector>
#include "Identity.h" #include "Identity.h"
@@ -23,7 +24,7 @@ namespace data
public: public:
BlindedPublicKey (std::shared_ptr<const IdentityEx> identity, bool clientAuth = false); BlindedPublicKey (std::shared_ptr<const IdentityEx> identity, bool clientAuth = false);
BlindedPublicKey (const std::string& b33); // from b33 without .b32.i2p BlindedPublicKey (std::string_view b33); // from b33 without .b32.i2p
std::string ToB33 () const; std::string ToB33 () const;
const uint8_t * GetPublicKey () const { return m_PublicKey.data (); }; const uint8_t * GetPublicKey () const { return m_PublicKey.data (); };

View File

@@ -1,68 +0,0 @@
/*
* Copyright (c) 2013-2023, The PurpleI2P Project
*
* This file is part of Purple i2pd project and licensed under BSD3
*
* See full license text in LICENSE file at top of project tree
*/
#include "CPU.h"
#include "Log.h"
#ifndef bit_AES
#define bit_AES (1 << 25)
#endif
#if defined(__GNUC__) && __GNUC__ < 6 && IS_X86
#include <cpuid.h>
#endif
#ifdef _MSC_VER
#include <intrin.h>
#endif
namespace i2p
{
namespace cpu
{
bool aesni = false;
inline bool cpu_support_aes()
{
#if IS_X86
#if defined(__clang__)
# if (__clang_major__ >= 6)
__builtin_cpu_init();
# endif
return __builtin_cpu_supports("aes");
#elif (defined(__GNUC__) && __GNUC__ >= 6)
__builtin_cpu_init();
return __builtin_cpu_supports("aes");
#elif (defined(__GNUC__) && __GNUC__ < 6)
int cpu_info[4];
bool flag = false;
__cpuid(0, cpu_info[0], cpu_info[1], cpu_info[2], cpu_info[3]);
if (cpu_info[0] >= 0x00000001) {
__cpuid(0x00000001, cpu_info[0], cpu_info[1], cpu_info[2], cpu_info[3]);
flag = ((cpu_info[2] & bit_AES) != 0);
}
return flag;
#elif defined(_MSC_VER)
int cpu_info[4];
__cpuid(cpu_info, 1);
return ((cpu_info[2] & bit_AES) != 0);
#endif
#endif
return false;
}
void Detect(bool AesSwitch, bool force)
{
if ((cpu_support_aes() && AesSwitch) || (AesSwitch && force)) {
aesni = true;
}
LogPrint(eLogInfo, "AESNI ", (aesni ? "enabled" : "disabled"));
}
}
}

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2023, The PurpleI2P Project * Copyright (c) 2013-2024, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@@ -21,20 +21,4 @@
# define IS_X86_64 0 # define IS_X86_64 0
#endif #endif
#if defined(__AES__) && !defined(_MSC_VER) && IS_X86
# define SUPPORTS_AES 1
#else
# define SUPPORTS_AES 0
#endif
namespace i2p
{
namespace cpu
{
extern bool aesni;
void Detect(bool AesSwitch, bool force);
}
}
#endif #endif

View File

@@ -1,137 +0,0 @@
/*
* 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
*
*/
#include "I2PEndian.h"
#include "ChaCha20.h"
#if !OPENSSL_AEAD_CHACHA20_POLY1305
namespace i2p
{
namespace crypto
{
namespace chacha
{
void u32t8le(uint32_t v, uint8_t * p)
{
p[0] = v & 0xff;
p[1] = (v >> 8) & 0xff;
p[2] = (v >> 16) & 0xff;
p[3] = (v >> 24) & 0xff;
}
uint32_t u8t32le(const uint8_t * p)
{
uint32_t value = p[3];
value = (value << 8) | p[2];
value = (value << 8) | p[1];
value = (value << 8) | p[0];
return value;
}
uint32_t rotl32(uint32_t x, int n)
{
return x << n | (x >> (-n & 31));
}
void quarterround(uint32_t *x, int a, int b, int c, int d)
{
x[a] += x[b]; x[d] = rotl32(x[d] ^ x[a], 16);
x[c] += x[d]; x[b] = rotl32(x[b] ^ x[c], 12);
x[a] += x[b]; x[d] = rotl32(x[d] ^ x[a], 8);
x[c] += x[d]; x[b] = rotl32(x[b] ^ x[c], 7);
}
void Chacha20Block::operator << (const Chacha20State & st)
{
int i;
for (i = 0; i < 16; i++)
u32t8le(st.data[i], data + (i << 2));
}
void block (Chacha20State &input, int rounds)
{
int i;
Chacha20State x;
x.Copy(input);
for (i = rounds; i > 0; i -= 2)
{
quarterround(x.data, 0, 4, 8, 12);
quarterround(x.data, 1, 5, 9, 13);
quarterround(x.data, 2, 6, 10, 14);
quarterround(x.data, 3, 7, 11, 15);
quarterround(x.data, 0, 5, 10, 15);
quarterround(x.data, 1, 6, 11, 12);
quarterround(x.data, 2, 7, 8, 13);
quarterround(x.data, 3, 4, 9, 14);
}
x += input;
input.block << x;
}
void Chacha20Init (Chacha20State& state, const uint8_t * nonce, const uint8_t * key, uint32_t counter)
{
state.data[0] = 0x61707865;
state.data[1] = 0x3320646e;
state.data[2] = 0x79622d32;
state.data[3] = 0x6b206574;
for (size_t i = 0; i < 8; i++)
state.data[4 + i] = chacha::u8t32le(key + i * 4);
state.data[12] = htole32 (counter);
for (size_t i = 0; i < 3; i++)
state.data[13 + i] = chacha::u8t32le(nonce + i * 4);
}
void Chacha20SetCounter (Chacha20State& state, uint32_t counter)
{
state.data[12] = htole32 (counter);
state.offset = 0;
}
void Chacha20Encrypt (Chacha20State& state, uint8_t * buf, size_t sz)
{
if (state.offset > 0)
{
// previous block if any
auto s = chacha::blocksize - state.offset;
if (sz < s) s = sz;
for (size_t i = 0; i < s; i++)
buf[i] ^= state.block.data[state.offset + i];
buf += s;
sz -= s;
state.offset += s;
if (state.offset >= chacha::blocksize) state.offset = 0;
}
for (size_t i = 0; i < sz; i += chacha::blocksize)
{
chacha::block(state, chacha::rounds);
state.data[12]++;
for (size_t j = i; j < i + chacha::blocksize; j++)
{
if (j >= sz)
{
state.offset = j & 0x3F; // % 64
break;
}
buf[j] ^= state.block.data[j - i];
}
}
}
} // namespace chacha
} // namespace crypto
} // namespace i2p
#endif

View File

@@ -1,72 +0,0 @@
/*
* 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 LIBI2PD_CHACHA20_H
#define LIBI2PD_CHACHA20_H
#include <cstdint>
#include <cstring>
#include <inttypes.h>
#include <string.h>
#include "Crypto.h"
#if !OPENSSL_AEAD_CHACHA20_POLY1305
namespace i2p
{
namespace crypto
{
const std::size_t CHACHA20_KEY_BYTES = 32;
const std::size_t CHACHA20_NOUNCE_BYTES = 12;
namespace chacha
{
constexpr std::size_t blocksize = 64;
constexpr int rounds = 20;
struct Chacha20State;
struct Chacha20Block
{
Chacha20Block () {};
Chacha20Block (Chacha20Block &&) = delete;
uint8_t data[blocksize];
void operator << (const Chacha20State & st);
};
struct Chacha20State
{
Chacha20State (): offset (0) {};
Chacha20State (Chacha20State &&) = delete;
Chacha20State & operator += (const Chacha20State & other)
{
for(int i = 0; i < 16; i++)
data[i] += other.data[i];
return *this;
}
void Copy(const Chacha20State & other)
{
memcpy(data, other.data, sizeof(uint32_t) * 16);
}
uint32_t data[16];
Chacha20Block block;
size_t offset;
};
void Chacha20Init (Chacha20State& state, const uint8_t * nonce, const uint8_t * key, uint32_t counter);
void Chacha20SetCounter (Chacha20State& state, uint32_t counter);
void Chacha20Encrypt (Chacha20State& state, uint8_t * buf, size_t sz); // encrypt buf in place
} // namespace chacha
} // namespace crypto
} // namespace i2p
#endif
#endif

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2024, The PurpleI2P Project * Copyright (c) 2013-2025, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@@ -117,9 +117,14 @@ namespace config {
("httpproxy.latency.max", value<std::string>()->default_value("0"), "HTTP proxy max latency for tunnels") ("httpproxy.latency.max", value<std::string>()->default_value("0"), "HTTP proxy max latency for tunnels")
("httpproxy.outproxy", value<std::string>()->default_value(""), "HTTP proxy upstream out proxy url") ("httpproxy.outproxy", value<std::string>()->default_value(""), "HTTP proxy upstream out proxy url")
("httpproxy.addresshelper", value<bool>()->default_value(true), "Enable or disable addresshelper") ("httpproxy.addresshelper", value<bool>()->default_value(true), "Enable or disable addresshelper")
("httpproxy.senduseragent", value<bool>()->default_value(false), "Pass through user's User-Agent if enabled. Disabled by default")
("httpproxy.i2cp.leaseSetType", value<std::string>()->default_value("3"), "Local destination's LeaseSet type") ("httpproxy.i2cp.leaseSetType", value<std::string>()->default_value("3"), "Local destination's LeaseSet type")
("httpproxy.i2cp.leaseSetEncType", value<std::string>()->default_value("0,4"), "Local destination's LeaseSet encryption type") ("httpproxy.i2cp.leaseSetEncType", value<std::string>()->default_value("0,4"), "Local destination's LeaseSet encryption type")
("httpproxy.i2cp.leaseSetPrivKey", value<std::string>()->default_value(""), "LeaseSet private key") ("httpproxy.i2cp.leaseSetPrivKey", value<std::string>()->default_value(""), "LeaseSet private key")
("httpproxy.i2p.streaming.maxOutboundSpeed", value<std::string>()->default_value("1730000000"), "Max outbound speed of HTTP proxy stream in bytes/sec")
("httpproxy.i2p.streaming.maxInboundSpeed", value<std::string>()->default_value("1730000000"), "Max inbound speed of HTTP proxy stream in bytes/sec")
("httpproxy.i2p.streaming.profile", value<std::string>()->default_value("1"), "HTTP Proxy bandwidth usage profile. 1 - bulk(high), 2- interactive(low)")
; ;
options_description socksproxy("SOCKS Proxy options"); options_description socksproxy("SOCKS Proxy options");
@@ -144,8 +149,22 @@ namespace config {
("socksproxy.i2cp.leaseSetType", value<std::string>()->default_value("3"), "Local destination's LeaseSet type") ("socksproxy.i2cp.leaseSetType", value<std::string>()->default_value("3"), "Local destination's LeaseSet type")
("socksproxy.i2cp.leaseSetEncType", value<std::string>()->default_value("0,4"), "Local destination's LeaseSet encryption type") ("socksproxy.i2cp.leaseSetEncType", value<std::string>()->default_value("0,4"), "Local destination's LeaseSet encryption type")
("socksproxy.i2cp.leaseSetPrivKey", value<std::string>()->default_value(""), "LeaseSet private key") ("socksproxy.i2cp.leaseSetPrivKey", value<std::string>()->default_value(""), "LeaseSet private key")
("socksproxy.i2p.streaming.maxOutboundSpeed", value<std::string>()->default_value("1730000000"), "Max outbound speed of SOCKS proxy stream in bytes/sec")
("socksproxy.i2p.streaming.maxInboundSpeed", value<std::string>()->default_value("1730000000"), "Max inbound speed of SOCKS proxy stream in bytes/sec")
("socksproxy.i2p.streaming.profile", value<std::string>()->default_value("1"), "SOCKS Proxy bandwidth usage profile. 1 - bulk(high), 2- interactive(low)")
; ;
options_description shareddest("Shared local destination options");
shareddest.add_options()
("shareddest.inbound.length", value<std::string>()->default_value("3"), "Shared local destination inbound tunnel length")
("shareddest.outbound.length", value<std::string>()->default_value("3"), "Shared local destination outbound tunnel length")
("shareddest.inbound.quantity", value<std::string>()->default_value("3"), "Shared local destination inbound tunnels quantity")
("shareddest.outbound.quantity", value<std::string>()->default_value("3"), "Shared local destination outbound tunnels quantity")
("shareddest.i2cp.leaseSetType", value<std::string>()->default_value("3"), "Shared local destination's LeaseSet type")
("shareddest.i2cp.leaseSetEncType", value<std::string>()->default_value("0,4"), "Shared local destination's LeaseSet encryption type")
("shareddest.i2p.streaming.profile", value<std::string>()->default_value("2"), "Shared local destination bandwidth usage profile. 1 - bulk(high), 2- interactive(low)")
;
options_description sam("SAM bridge options"); options_description sam("SAM bridge options");
sam.add_options() sam.add_options()
("sam.enabled", value<bool>()->default_value(true), "Enable or disable SAM Application bridge") ("sam.enabled", value<bool>()->default_value(true), "Enable or disable SAM Application bridge")
@@ -168,6 +187,8 @@ namespace config {
("i2cp.address", value<std::string>()->default_value("127.0.0.1"), "I2CP listen address") ("i2cp.address", value<std::string>()->default_value("127.0.0.1"), "I2CP listen address")
("i2cp.port", value<uint16_t>()->default_value(7654), "I2CP listen port") ("i2cp.port", value<uint16_t>()->default_value(7654), "I2CP listen port")
("i2cp.singlethread", value<bool>()->default_value(true), "Destinations run in the I2CP server's thread") ("i2cp.singlethread", value<bool>()->default_value(true), "Destinations run in the I2CP server's thread")
("i2cp.inboundlimit", value<uint32_t>()->default_value(0), "Client inbound limit in KBps to return in BandwidthLimitsMessage. Router's bandwidth by default")
("i2cp.outboundlimit", value<uint32_t>()->default_value(0), "Client outbound limit in KBps to return in BandwidthLimitsMessage. Router's bandwidth by default")
; ;
options_description i2pcontrol("I2PControl options"); options_description i2pcontrol("I2PControl options");
@@ -205,7 +226,7 @@ namespace config {
reseed.add_options() reseed.add_options()
("reseed.verify", value<bool>()->default_value(false), "Verify .su3 signature") ("reseed.verify", value<bool>()->default_value(false), "Verify .su3 signature")
("reseed.threshold", value<uint16_t>()->default_value(25), "Minimum number of known routers before requesting reseed") ("reseed.threshold", value<uint16_t>()->default_value(25), "Minimum number of known routers before requesting reseed")
("reseed.floodfill", value<std::string>()->default_value(""), "Path to router info of floodfill to reseed from") ("reseed.floodfill", value<std::string>()->default_value(""), "Ignored. Always empty")
("reseed.file", value<std::string>()->default_value(""), "Path to local .su3 file or HTTPS URL to reseed from") ("reseed.file", value<std::string>()->default_value(""), "Path to local .su3 file or HTTPS URL to reseed from")
("reseed.zipfile", value<std::string>()->default_value(""), "Path to local .zip file to reseed from") ("reseed.zipfile", value<std::string>()->default_value(""), "Path to local .zip file to reseed from")
("reseed.proxy", value<std::string>()->default_value(""), "url for reseed proxy, supports http/socks") ("reseed.proxy", value<std::string>()->default_value(""), "url for reseed proxy, supports http/socks")
@@ -217,7 +238,7 @@ namespace config {
"https://reseed.onion.im/," "https://reseed.onion.im/,"
"https://i2pseed.creativecowpat.net:8443/," "https://i2pseed.creativecowpat.net:8443/,"
"https://reseed.i2pgit.org/," "https://reseed.i2pgit.org/,"
"https://banana.incognet.io/," "https://coconut.incognet.io/,"
"https://reseed-pl.i2pd.xyz/," "https://reseed-pl.i2pd.xyz/,"
"https://www2.mk16.de/," "https://www2.mk16.de/,"
"https://i2p.ghativega.in/," "https://i2p.ghativega.in/,"
@@ -228,9 +249,7 @@ namespace config {
"http://[324:71e:281a:9ed3::ace]:7070/," "http://[324:71e:281a:9ed3::ace]:7070/,"
"http://[301:65b9:c7cd:9a36::1]:18801/," "http://[301:65b9:c7cd:9a36::1]:18801/,"
"http://[320:8936:ec1a:31f1::216]/," "http://[320:8936:ec1a:31f1::216]/,"
"http://[306:3834:97b9:a00a::1]/," "http://[316:f9e0:f22e:a74f::216]/"
"http://[316:f9e0:f22e:a74f::216]/,"
"http://[300:eaff:7fab:181b::e621]:7170"
), "Reseed URLs through the Yggdrasil, separated by comma") ), "Reseed URLs through the Yggdrasil, separated by comma")
; ;
@@ -307,11 +326,11 @@ namespace config {
("persist.addressbook", value<bool>()->default_value(true), "Persist full addresses (default: true)") ("persist.addressbook", value<bool>()->default_value(true), "Persist full addresses (default: true)")
; ;
options_description cpuext("CPU encryption extensions options"); options_description cpuext("CPU encryption extensions options. Deprecated");
cpuext.add_options() cpuext.add_options()
("cpuext.aesni", bool_switch()->default_value(true), "Use auto detection for AESNI CPU extensions. If false, AESNI will be not used") ("cpuext.aesni", bool_switch()->default_value(true), "Deprecated option")
("cpuext.avx", bool_switch()->default_value(false), "Deprecated option") ("cpuext.avx", bool_switch()->default_value(false), "Deprecated option")
("cpuext.force", bool_switch()->default_value(false), "Force usage of CPU extensions. Useful when cpuinfo is not available on virtual machines") ("cpuext.force", bool_switch()->default_value(false), "Deprecated option")
; ;
options_description meshnets("Meshnet transports options"); options_description meshnets("Meshnet transports options");
@@ -333,6 +352,7 @@ namespace config {
.add(httpserver) .add(httpserver)
.add(httpproxy) .add(httpproxy)
.add(socksproxy) .add(socksproxy)
.add(shareddest)
.add(sam) .add(sam)
.add(bob) .add(bob)
.add(i2cp) .add(i2cp)

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2023, The PurpleI2P Project * Copyright (c) 2013-2025, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@@ -19,16 +19,12 @@
#if OPENSSL_HKDF #if OPENSSL_HKDF
#include <openssl/kdf.h> #include <openssl/kdf.h>
#endif #endif
#if !OPENSSL_AEAD_CHACHA20_POLY1305 #include "CPU.h"
#include "ChaCha20.h"
#include "Poly1305.h"
#endif
#include "Crypto.h" #include "Crypto.h"
#include "Ed25519.h" #include "Ed25519.h"
#include "I2PEndian.h" #include "I2PEndian.h"
#include "Log.h" #include "Log.h"
namespace i2p namespace i2p
{ {
namespace crypto namespace crypto
@@ -245,17 +241,12 @@ namespace crypto
// x25519 // x25519
X25519Keys::X25519Keys () X25519Keys::X25519Keys ()
{ {
#if OPENSSL_X25519
m_Ctx = EVP_PKEY_CTX_new_id (NID_X25519, NULL); m_Ctx = EVP_PKEY_CTX_new_id (NID_X25519, NULL);
m_Pkey = nullptr; m_Pkey = nullptr;
#else
m_Ctx = BN_CTX_new ();
#endif
} }
X25519Keys::X25519Keys (const uint8_t * priv, const uint8_t * pub) X25519Keys::X25519Keys (const uint8_t * priv, const uint8_t * pub)
{ {
#if OPENSSL_X25519
m_Pkey = EVP_PKEY_new_raw_private_key (EVP_PKEY_X25519, NULL, priv, 32); m_Pkey = EVP_PKEY_new_raw_private_key (EVP_PKEY_X25519, NULL, priv, 32);
m_Ctx = EVP_PKEY_CTX_new (m_Pkey, NULL); m_Ctx = EVP_PKEY_CTX_new (m_Pkey, NULL);
if (pub) if (pub)
@@ -265,29 +256,16 @@ namespace crypto
size_t len = 32; size_t len = 32;
EVP_PKEY_get_raw_public_key (m_Pkey, m_PublicKey, &len); EVP_PKEY_get_raw_public_key (m_Pkey, m_PublicKey, &len);
} }
#else
m_Ctx = BN_CTX_new ();
memcpy (m_PrivateKey, priv, 32);
if (pub)
memcpy (m_PublicKey, pub, 32);
else
GetEd25519 ()->ScalarMulB (m_PrivateKey, m_PublicKey, m_Ctx);
#endif
} }
X25519Keys::~X25519Keys () X25519Keys::~X25519Keys ()
{ {
#if OPENSSL_X25519
EVP_PKEY_CTX_free (m_Ctx); EVP_PKEY_CTX_free (m_Ctx);
if (m_Pkey) EVP_PKEY_free (m_Pkey); if (m_Pkey) EVP_PKEY_free (m_Pkey);
#else
BN_CTX_free (m_Ctx);
#endif
} }
void X25519Keys::GenerateKeys () void X25519Keys::GenerateKeys ()
{ {
#if OPENSSL_X25519
if (m_Pkey) if (m_Pkey)
{ {
EVP_PKEY_free (m_Pkey); EVP_PKEY_free (m_Pkey);
@@ -299,16 +277,11 @@ namespace crypto
m_Ctx = EVP_PKEY_CTX_new (m_Pkey, NULL); // TODO: do we really need to re-create m_Ctx? m_Ctx = EVP_PKEY_CTX_new (m_Pkey, NULL); // TODO: do we really need to re-create m_Ctx?
size_t len = 32; size_t len = 32;
EVP_PKEY_get_raw_public_key (m_Pkey, m_PublicKey, &len); EVP_PKEY_get_raw_public_key (m_Pkey, m_PublicKey, &len);
#else
RAND_bytes (m_PrivateKey, 32);
GetEd25519 ()->ScalarMulB (m_PrivateKey, m_PublicKey, m_Ctx);
#endif
} }
bool X25519Keys::Agree (const uint8_t * pub, uint8_t * shared) bool X25519Keys::Agree (const uint8_t * pub, uint8_t * shared)
{ {
if (!pub || (pub[31] & 0x80)) return false; // not x25519 key if (!pub || (pub[31] & 0x80)) return false; // not x25519 key
#if OPENSSL_X25519
EVP_PKEY_derive_init (m_Ctx); EVP_PKEY_derive_init (m_Ctx);
auto pkey = EVP_PKEY_new_raw_public_key (EVP_PKEY_X25519, NULL, pub, 32); auto pkey = EVP_PKEY_new_raw_public_key (EVP_PKEY_X25519, NULL, pub, 32);
if (!pkey) return false; if (!pkey) return false;
@@ -316,25 +289,17 @@ namespace crypto
size_t len = 32; size_t len = 32;
EVP_PKEY_derive (m_Ctx, shared, &len); EVP_PKEY_derive (m_Ctx, shared, &len);
EVP_PKEY_free (pkey); EVP_PKEY_free (pkey);
#else
GetEd25519 ()->ScalarMul (pub, m_PrivateKey, shared, m_Ctx);
#endif
return true; return true;
} }
void X25519Keys::GetPrivateKey (uint8_t * priv) const void X25519Keys::GetPrivateKey (uint8_t * priv) const
{ {
#if OPENSSL_X25519
size_t len = 32; size_t len = 32;
EVP_PKEY_get_raw_private_key (m_Pkey, priv, &len); EVP_PKEY_get_raw_private_key (m_Pkey, priv, &len);
#else
memcpy (priv, m_PrivateKey, 32);
#endif
} }
void X25519Keys::SetPrivateKey (const uint8_t * priv, bool calculatePublic) void X25519Keys::SetPrivateKey (const uint8_t * priv, bool calculatePublic)
{ {
#if OPENSSL_X25519
if (m_Ctx) EVP_PKEY_CTX_free (m_Ctx); if (m_Ctx) EVP_PKEY_CTX_free (m_Ctx);
if (m_Pkey) EVP_PKEY_free (m_Pkey); if (m_Pkey) EVP_PKEY_free (m_Pkey);
m_Pkey = EVP_PKEY_new_raw_private_key (EVP_PKEY_X25519, NULL, priv, 32); m_Pkey = EVP_PKEY_new_raw_private_key (EVP_PKEY_X25519, NULL, priv, 32);
@@ -344,11 +309,6 @@ namespace crypto
size_t len = 32; size_t len = 32;
EVP_PKEY_get_raw_public_key (m_Pkey, m_PublicKey, &len); EVP_PKEY_get_raw_public_key (m_Pkey, m_PublicKey, &len);
} }
#else
memcpy (m_PrivateKey, priv, 32);
if (calculatePublic)
GetEd25519 ()->ScalarMulB (m_PrivateKey, m_PublicKey, m_Ctx);
#endif
} }
// ElGamal // ElGamal
@@ -482,9 +442,8 @@ namespace crypto
// encrypt // encrypt
CBCEncryption encryption; CBCEncryption encryption;
encryption.SetKey (shared); encryption.SetKey (shared);
encryption.SetIV (iv);
encrypted[257] = 0; encrypted[257] = 0;
encryption.Encrypt (m, 256, encrypted + 258); encryption.Encrypt (m, 256, iv, encrypted + 258);
EC_POINT_free (p); EC_POINT_free (p);
BN_CTX_end (ctx); BN_CTX_end (ctx);
BN_CTX_free (ctx); BN_CTX_free (ctx);
@@ -517,8 +476,7 @@ namespace crypto
uint8_t m[256]; uint8_t m[256];
CBCDecryption decryption; CBCDecryption decryption;
decryption.SetKey (shared); decryption.SetKey (shared);
decryption.SetIV (iv); decryption.Decrypt (encrypted + 258, 256, iv, m);
decryption.Decrypt (encrypted + 258, 256, m);
// verify and copy // verify and copy
uint8_t hash[32]; uint8_t hash[32];
SHA256 (m + 33, 222, hash); SHA256 (m + 33, 222, hash);
@@ -556,441 +514,114 @@ namespace crypto
} }
// AES // AES
#if SUPPORTS_AES ECBEncryption::ECBEncryption ()
#define KeyExpansion256(round0,round1) \
"pshufd $0xff, %%xmm2, %%xmm2 \n" \
"movaps %%xmm1, %%xmm4 \n" \
"pslldq $4, %%xmm4 \n" \
"pxor %%xmm4, %%xmm1 \n" \
"pslldq $4, %%xmm4 \n" \
"pxor %%xmm4, %%xmm1 \n" \
"pslldq $4, %%xmm4 \n" \
"pxor %%xmm4, %%xmm1 \n" \
"pxor %%xmm2, %%xmm1 \n" \
"movaps %%xmm1, "#round0"(%[sched]) \n" \
"aeskeygenassist $0, %%xmm1, %%xmm4 \n" \
"pshufd $0xaa, %%xmm4, %%xmm2 \n" \
"movaps %%xmm3, %%xmm4 \n" \
"pslldq $4, %%xmm4 \n" \
"pxor %%xmm4, %%xmm3 \n" \
"pslldq $4, %%xmm4 \n" \
"pxor %%xmm4, %%xmm3 \n" \
"pslldq $4, %%xmm4 \n" \
"pxor %%xmm4, %%xmm3 \n" \
"pxor %%xmm2, %%xmm3 \n" \
"movaps %%xmm3, "#round1"(%[sched]) \n"
#endif
#if SUPPORTS_AES
void ECBCryptoAESNI::ExpandKey (const AESKey& key)
{ {
__asm__ m_Ctx = EVP_CIPHER_CTX_new ();
(
"movups (%[key]), %%xmm1 \n"
"movups 16(%[key]), %%xmm3 \n"
"movaps %%xmm1, (%[sched]) \n"
"movaps %%xmm3, 16(%[sched]) \n"
"aeskeygenassist $1, %%xmm3, %%xmm2 \n"
KeyExpansion256(32,48)
"aeskeygenassist $2, %%xmm3, %%xmm2 \n"
KeyExpansion256(64,80)
"aeskeygenassist $4, %%xmm3, %%xmm2 \n"
KeyExpansion256(96,112)
"aeskeygenassist $8, %%xmm3, %%xmm2 \n"
KeyExpansion256(128,144)
"aeskeygenassist $16, %%xmm3, %%xmm2 \n"
KeyExpansion256(160,176)
"aeskeygenassist $32, %%xmm3, %%xmm2 \n"
KeyExpansion256(192,208)
"aeskeygenassist $64, %%xmm3, %%xmm2 \n"
// key expansion final
"pshufd $0xff, %%xmm2, %%xmm2 \n"
"movaps %%xmm1, %%xmm4 \n"
"pslldq $4, %%xmm4 \n"
"pxor %%xmm4, %%xmm1 \n"
"pslldq $4, %%xmm4 \n"
"pxor %%xmm4, %%xmm1 \n"
"pslldq $4, %%xmm4 \n"
"pxor %%xmm4, %%xmm1 \n"
"pxor %%xmm2, %%xmm1 \n"
"movups %%xmm1, 224(%[sched]) \n"
: // output
: [key]"r"((const uint8_t *)key), [sched]"r"(GetKeySchedule ()) // input
: "%xmm1", "%xmm2", "%xmm3", "%xmm4", "memory" // clogged
);
} }
#endif
ECBEncryption::~ECBEncryption ()
#if SUPPORTS_AES
#define EncryptAES256(sched) \
"pxor (%["#sched"]), %%xmm0 \n" \
"aesenc 16(%["#sched"]), %%xmm0 \n" \
"aesenc 32(%["#sched"]), %%xmm0 \n" \
"aesenc 48(%["#sched"]), %%xmm0 \n" \
"aesenc 64(%["#sched"]), %%xmm0 \n" \
"aesenc 80(%["#sched"]), %%xmm0 \n" \
"aesenc 96(%["#sched"]), %%xmm0 \n" \
"aesenc 112(%["#sched"]), %%xmm0 \n" \
"aesenc 128(%["#sched"]), %%xmm0 \n" \
"aesenc 144(%["#sched"]), %%xmm0 \n" \
"aesenc 160(%["#sched"]), %%xmm0 \n" \
"aesenc 176(%["#sched"]), %%xmm0 \n" \
"aesenc 192(%["#sched"]), %%xmm0 \n" \
"aesenc 208(%["#sched"]), %%xmm0 \n" \
"aesenclast 224(%["#sched"]), %%xmm0 \n"
#endif
void ECBEncryption::Encrypt (const ChipherBlock * in, ChipherBlock * out)
{ {
#if SUPPORTS_AES if (m_Ctx)
if(i2p::cpu::aesni) EVP_CIPHER_CTX_free (m_Ctx);
{ }
__asm__
( void ECBEncryption::Encrypt (const uint8_t * in, uint8_t * out)
"movups (%[in]), %%xmm0 \n" {
EncryptAES256(sched) EVP_EncryptInit_ex (m_Ctx, EVP_aes_256_ecb(), NULL, m_Key, NULL);
"movups %%xmm0, (%[out]) \n" EVP_CIPHER_CTX_set_padding (m_Ctx, 0);
: int len;
: [sched]"r"(GetKeySchedule ()), [in]"r"(in), [out]"r"(out) EVP_EncryptUpdate (m_Ctx, out, &len, in, 16);
: "%xmm0", "memory" EVP_EncryptFinal_ex (m_Ctx, out + len, &len);
);
}
else
#endif
{
AES_encrypt (in->buf, out->buf, &m_Key);
}
} }
#if SUPPORTS_AES ECBDecryption::ECBDecryption ()
#define DecryptAES256(sched) \
"pxor 224(%["#sched"]), %%xmm0 \n" \
"aesdec 208(%["#sched"]), %%xmm0 \n" \
"aesdec 192(%["#sched"]), %%xmm0 \n" \
"aesdec 176(%["#sched"]), %%xmm0 \n" \
"aesdec 160(%["#sched"]), %%xmm0 \n" \
"aesdec 144(%["#sched"]), %%xmm0 \n" \
"aesdec 128(%["#sched"]), %%xmm0 \n" \
"aesdec 112(%["#sched"]), %%xmm0 \n" \
"aesdec 96(%["#sched"]), %%xmm0 \n" \
"aesdec 80(%["#sched"]), %%xmm0 \n" \
"aesdec 64(%["#sched"]), %%xmm0 \n" \
"aesdec 48(%["#sched"]), %%xmm0 \n" \
"aesdec 32(%["#sched"]), %%xmm0 \n" \
"aesdec 16(%["#sched"]), %%xmm0 \n" \
"aesdeclast (%["#sched"]), %%xmm0 \n"
#endif
void ECBDecryption::Decrypt (const ChipherBlock * in, ChipherBlock * out)
{ {
#if SUPPORTS_AES m_Ctx = EVP_CIPHER_CTX_new ();
if(i2p::cpu::aesni) }
{
__asm__ ECBDecryption::~ECBDecryption ()
( {
"movups (%[in]), %%xmm0 \n" if (m_Ctx)
DecryptAES256(sched) EVP_CIPHER_CTX_free (m_Ctx);
"movups %%xmm0, (%[out]) \n" }
:
: [sched]"r"(GetKeySchedule ()), [in]"r"(in), [out]"r"(out) void ECBDecryption::Decrypt (const uint8_t * in, uint8_t * out)
: "%xmm0", "memory" {
); EVP_DecryptInit_ex (m_Ctx, EVP_aes_256_ecb(), NULL, m_Key, NULL);
} EVP_CIPHER_CTX_set_padding (m_Ctx, 0);
else int len;
#endif EVP_DecryptUpdate (m_Ctx, out, &len, in, 16);
{ EVP_DecryptFinal_ex (m_Ctx, out + len, &len);
AES_decrypt (in->buf, out->buf, &m_Key);
}
} }
#if SUPPORTS_AES
#define CallAESIMC(offset) \
"movaps "#offset"(%[shed]), %%xmm0 \n" \
"aesimc %%xmm0, %%xmm0 \n" \
"movaps %%xmm0, "#offset"(%[shed]) \n"
#endif
void ECBEncryption::SetKey (const AESKey& key) CBCEncryption::CBCEncryption ()
{ {
#if SUPPORTS_AES m_Ctx = EVP_CIPHER_CTX_new ();
if(i2p::cpu::aesni)
{
ExpandKey (key);
}
else
#endif
{
AES_set_encrypt_key (key, 256, &m_Key);
}
} }
void ECBDecryption::SetKey (const AESKey& key) CBCEncryption::~CBCEncryption ()
{ {
#if SUPPORTS_AES if (m_Ctx)
if(i2p::cpu::aesni) EVP_CIPHER_CTX_free (m_Ctx);
{ }
ExpandKey (key); // expand encryption key first
// then invert it using aesimc void CBCEncryption::Encrypt (const uint8_t * in, size_t len, const uint8_t * iv, uint8_t * out)
__asm__
(
CallAESIMC(16)
CallAESIMC(32)
CallAESIMC(48)
CallAESIMC(64)
CallAESIMC(80)
CallAESIMC(96)
CallAESIMC(112)
CallAESIMC(128)
CallAESIMC(144)
CallAESIMC(160)
CallAESIMC(176)
CallAESIMC(192)
CallAESIMC(208)
:
: [shed]"r"(GetKeySchedule ())
: "%xmm0", "memory"
);
}
else
#endif
{
AES_set_decrypt_key (key, 256, &m_Key);
}
}
void CBCEncryption::Encrypt (int numBlocks, const ChipherBlock * in, ChipherBlock * out)
{
#if SUPPORTS_AES
if(i2p::cpu::aesni)
{
__asm__
(
"movups (%[iv]), %%xmm1 \n"
"1: \n"
"movups (%[in]), %%xmm0 \n"
"pxor %%xmm1, %%xmm0 \n"
EncryptAES256(sched)
"movaps %%xmm0, %%xmm1 \n"
"movups %%xmm0, (%[out]) \n"
"add $16, %[in] \n"
"add $16, %[out] \n"
"dec %[num] \n"
"jnz 1b \n"
"movups %%xmm1, (%[iv]) \n"
:
: [iv]"r"((uint8_t *)m_LastBlock), [sched]"r"(m_ECBEncryption.GetKeySchedule ()),
[in]"r"(in), [out]"r"(out), [num]"r"(numBlocks)
: "%xmm0", "%xmm1", "cc", "memory"
);
}
else
#endif
{
for (int i = 0; i < numBlocks; i++)
{
*m_LastBlock.GetChipherBlock () ^= in[i];
m_ECBEncryption.Encrypt (m_LastBlock.GetChipherBlock (), m_LastBlock.GetChipherBlock ());
out[i] = *m_LastBlock.GetChipherBlock ();
}
}
}
void CBCEncryption::Encrypt (const uint8_t * in, std::size_t len, uint8_t * out)
{ {
// len/16 // len/16
int numBlocks = len >> 4; EVP_EncryptInit_ex (m_Ctx, EVP_aes_256_cbc(), NULL, m_Key, iv);
if (numBlocks > 0) EVP_CIPHER_CTX_set_padding (m_Ctx, 0);
Encrypt (numBlocks, (const ChipherBlock *)in, (ChipherBlock *)out); int l;
EVP_EncryptUpdate (m_Ctx, out, &l, in, len);
EVP_EncryptFinal_ex (m_Ctx, out + l, &l);
} }
void CBCEncryption::Encrypt (const uint8_t * in, uint8_t * out) CBCDecryption::CBCDecryption ()
{ {
#if SUPPORTS_AES m_Ctx = EVP_CIPHER_CTX_new ();
if(i2p::cpu::aesni)
{
__asm__
(
"movups (%[iv]), %%xmm1 \n"
"movups (%[in]), %%xmm0 \n"
"pxor %%xmm1, %%xmm0 \n"
EncryptAES256(sched)
"movups %%xmm0, (%[out]) \n"
"movups %%xmm0, (%[iv]) \n"
:
: [iv]"r"((uint8_t *)m_LastBlock), [sched]"r"(m_ECBEncryption.GetKeySchedule ()),
[in]"r"(in), [out]"r"(out)
: "%xmm0", "%xmm1", "memory"
);
}
else
#endif
Encrypt (1, (const ChipherBlock *)in, (ChipherBlock *)out);
} }
void CBCDecryption::Decrypt (int numBlocks, const ChipherBlock * in, ChipherBlock * out) CBCDecryption::~CBCDecryption ()
{ {
#if SUPPORTS_AES if (m_Ctx)
if(i2p::cpu::aesni) EVP_CIPHER_CTX_free (m_Ctx);
{ }
__asm__
( void CBCDecryption::Decrypt (const uint8_t * in, size_t len, const uint8_t * iv, uint8_t * out)
"movups (%[iv]), %%xmm1 \n"
"1: \n"
"movups (%[in]), %%xmm0 \n"
"movaps %%xmm0, %%xmm2 \n"
DecryptAES256(sched)
"pxor %%xmm1, %%xmm0 \n"
"movups %%xmm0, (%[out]) \n"
"movaps %%xmm2, %%xmm1 \n"
"add $16, %[in] \n"
"add $16, %[out] \n"
"dec %[num] \n"
"jnz 1b \n"
"movups %%xmm1, (%[iv]) \n"
:
: [iv]"r"((uint8_t *)m_IV), [sched]"r"(m_ECBDecryption.GetKeySchedule ()),
[in]"r"(in), [out]"r"(out), [num]"r"(numBlocks)
: "%xmm0", "%xmm1", "%xmm2", "cc", "memory"
);
}
else
#endif
{
for (int i = 0; i < numBlocks; i++)
{
ChipherBlock tmp = in[i];
m_ECBDecryption.Decrypt (in + i, out + i);
out[i] ^= *m_IV.GetChipherBlock ();
*m_IV.GetChipherBlock () = tmp;
}
}
}
void CBCDecryption::Decrypt (const uint8_t * in, std::size_t len, uint8_t * out)
{ {
int numBlocks = len >> 4; // len/16
if (numBlocks > 0) EVP_DecryptInit_ex (m_Ctx, EVP_aes_256_cbc(), NULL, m_Key, iv);
Decrypt (numBlocks, (const ChipherBlock *)in, (ChipherBlock *)out); EVP_CIPHER_CTX_set_padding (m_Ctx, 0);
} int l;
EVP_DecryptUpdate (m_Ctx, out, &l, in, len);
void CBCDecryption::Decrypt (const uint8_t * in, uint8_t * out) EVP_DecryptFinal_ex (m_Ctx, out + l, &l);
{
#if SUPPORTS_AES
if(i2p::cpu::aesni)
{
__asm__
(
"movups (%[iv]), %%xmm1 \n"
"movups (%[in]), %%xmm0 \n"
"movups %%xmm0, (%[iv]) \n"
DecryptAES256(sched)
"pxor %%xmm1, %%xmm0 \n"
"movups %%xmm0, (%[out]) \n"
:
: [iv]"r"((uint8_t *)m_IV), [sched]"r"(m_ECBDecryption.GetKeySchedule ()),
[in]"r"(in), [out]"r"(out)
: "%xmm0", "%xmm1", "memory"
);
}
else
#endif
Decrypt (1, (const ChipherBlock *)in, (ChipherBlock *)out);
} }
void TunnelEncryption::Encrypt (const uint8_t * in, uint8_t * out) void TunnelEncryption::Encrypt (const uint8_t * in, uint8_t * out)
{ {
#if SUPPORTS_AES uint8_t iv[16];
if(i2p::cpu::aesni) m_IVEncryption.Encrypt (in, iv); // iv
{ m_LayerEncryption.Encrypt (in + 16, i2p::tunnel::TUNNEL_DATA_ENCRYPTED_SIZE, iv, out + 16); // data
__asm__ m_IVEncryption.Encrypt (iv, out); // double iv
(
// encrypt IV
"movups (%[in]), %%xmm0 \n"
EncryptAES256(sched_iv)
"movaps %%xmm0, %%xmm1 \n"
// double IV encryption
EncryptAES256(sched_iv)
"movups %%xmm0, (%[out]) \n"
// encrypt data, IV is xmm1
"1: \n"
"add $16, %[in] \n"
"add $16, %[out] \n"
"movups (%[in]), %%xmm0 \n"
"pxor %%xmm1, %%xmm0 \n"
EncryptAES256(sched_l)
"movaps %%xmm0, %%xmm1 \n"
"movups %%xmm0, (%[out]) \n"
"dec %[num] \n"
"jnz 1b \n"
:
: [sched_iv]"r"(m_IVEncryption.GetKeySchedule ()), [sched_l]"r"(m_LayerEncryption.ECB().GetKeySchedule ()),
[in]"r"(in), [out]"r"(out), [num]"r"(63) // 63 blocks = 1008 bytes
: "%xmm0", "%xmm1", "cc", "memory"
);
}
else
#endif
{
m_IVEncryption.Encrypt ((const ChipherBlock *)in, (ChipherBlock *)out); // iv
m_LayerEncryption.SetIV (out);
m_LayerEncryption.Encrypt (in + 16, i2p::tunnel::TUNNEL_DATA_ENCRYPTED_SIZE, out + 16); // data
m_IVEncryption.Encrypt ((ChipherBlock *)out, (ChipherBlock *)out); // double iv
}
} }
void TunnelDecryption::Decrypt (const uint8_t * in, uint8_t * out) void TunnelDecryption::Decrypt (const uint8_t * in, uint8_t * out)
{ {
#if SUPPORTS_AES uint8_t iv[16];
if(i2p::cpu::aesni) m_IVDecryption.Decrypt (in, iv); // iv
{ m_LayerDecryption.Decrypt (in + 16, i2p::tunnel::TUNNEL_DATA_ENCRYPTED_SIZE, iv, out + 16); // data
__asm__ m_IVDecryption.Decrypt (iv, out); // double iv
(
// decrypt IV
"movups (%[in]), %%xmm0 \n"
DecryptAES256(sched_iv)
"movaps %%xmm0, %%xmm1 \n"
// double IV encryption
DecryptAES256(sched_iv)
"movups %%xmm0, (%[out]) \n"
// decrypt data, IV is xmm1
"1: \n"
"add $16, %[in] \n"
"add $16, %[out] \n"
"movups (%[in]), %%xmm0 \n"
"movaps %%xmm0, %%xmm2 \n"
DecryptAES256(sched_l)
"pxor %%xmm1, %%xmm0 \n"
"movups %%xmm0, (%[out]) \n"
"movaps %%xmm2, %%xmm1 \n"
"dec %[num] \n"
"jnz 1b \n"
:
: [sched_iv]"r"(m_IVDecryption.GetKeySchedule ()), [sched_l]"r"(m_LayerDecryption.ECB().GetKeySchedule ()),
[in]"r"(in), [out]"r"(out), [num]"r"(63) // 63 blocks = 1008 bytes
: "%xmm0", "%xmm1", "%xmm2", "cc", "memory"
);
}
else
#endif
{
m_IVDecryption.Decrypt ((const ChipherBlock *)in, (ChipherBlock *)out); // iv
m_LayerDecryption.SetIV (out);
m_LayerDecryption.Decrypt (in + 16, i2p::tunnel::TUNNEL_DATA_ENCRYPTED_SIZE, out + 16); // data
m_IVDecryption.Decrypt ((ChipherBlock *)out, (ChipherBlock *)out); // double iv
}
} }
// AEAD/ChaCha20/Poly1305 // AEAD/ChaCha20/Poly1305
bool AEADChaCha20Poly1305 (const uint8_t * msg, size_t msgLen, const uint8_t * ad, size_t adLen, const uint8_t * key, const uint8_t * nonce, uint8_t * buf, size_t len, bool encrypt) static bool AEADChaCha20Poly1305 (EVP_CIPHER_CTX * ctx, const uint8_t * msg, size_t msgLen,
const uint8_t * ad, size_t adLen, const uint8_t * key, const uint8_t * nonce, uint8_t * buf, size_t len, bool encrypt)
{ {
if (len < msgLen) return false; if (!ctx || len < msgLen) return false;
if (encrypt && len < msgLen + 16) return false; if (encrypt && len < msgLen + 16) return false;
bool ret = true; bool ret = true;
#if OPENSSL_AEAD_CHACHA20_POLY1305
int outlen = 0; int outlen = 0;
EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new ();
if (encrypt) if (encrypt)
{ {
EVP_EncryptInit_ex(ctx, EVP_chacha20_poly1305(), 0, 0, 0); EVP_EncryptInit_ex(ctx, EVP_chacha20_poly1305(), 0, 0, 0);
@@ -1003,6 +634,15 @@ namespace crypto
} }
else else
{ {
#if defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x4000000fL
std::vector<uint8_t> m(msgLen + 16);
if (msg == buf)
{
// we have to use different buffers otherwise verification fails
memcpy (m.data (), msg, msgLen + 16);
msg = m.data ();
}
#endif
EVP_DecryptInit_ex(ctx, EVP_chacha20_poly1305(), 0, 0, 0); EVP_DecryptInit_ex(ctx, EVP_chacha20_poly1305(), 0, 0, 0);
EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_IVLEN, 12, 0); EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_IVLEN, 12, 0);
EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG, 16, (uint8_t *)(msg + msgLen)); EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG, 16, (uint8_t *)(msg + msgLen));
@@ -1011,140 +651,100 @@ namespace crypto
EVP_DecryptUpdate(ctx, buf, &outlen, msg, msgLen); EVP_DecryptUpdate(ctx, buf, &outlen, msg, msgLen);
ret = EVP_DecryptFinal_ex(ctx, buf + outlen, &outlen) > 0; ret = EVP_DecryptFinal_ex(ctx, buf + outlen, &outlen) > 0;
} }
EVP_CIPHER_CTX_free (ctx);
#else
chacha::Chacha20State state;
// generate one time poly key
chacha::Chacha20Init (state, nonce, key, 0);
uint64_t polyKey[8];
memset(polyKey, 0, sizeof(polyKey));
chacha::Chacha20Encrypt (state, (uint8_t *)polyKey, 64);
// create Poly1305 hash
Poly1305 polyHash (polyKey);
if (!ad) adLen = 0;
uint8_t padding[16]; memset (padding, 0, 16);
if (ad)
{
polyHash.Update (ad, adLen);// additional authenticated data
auto rem = adLen & 0x0F; // %16
if (rem)
{
// padding1
rem = 16 - rem;
polyHash.Update (padding, rem);
}
}
// encrypt/decrypt data and add to hash
Chacha20SetCounter (state, 1);
if (buf != msg)
memcpy (buf, msg, msgLen);
if (encrypt)
{
chacha::Chacha20Encrypt (state, buf, msgLen); // encrypt
polyHash.Update (buf, msgLen); // after encryption
}
else
{
polyHash.Update (buf, msgLen); // before decryption
chacha::Chacha20Encrypt (state, buf, msgLen); // decrypt
}
auto rem = msgLen & 0x0F; // %16
if (rem)
{
// padding2
rem = 16 - rem;
polyHash.Update (padding, rem);
}
// adLen and msgLen
htole64buf (padding, adLen);
htole64buf (padding + 8, msgLen);
polyHash.Update (padding, 16);
if (encrypt)
// calculate Poly1305 tag and write in after encrypted data
polyHash.Finish ((uint64_t *)(buf + msgLen));
else
{
uint64_t tag[4];
// calculate Poly1305 tag
polyHash.Finish (tag);
if (memcmp (tag, msg + msgLen, 16)) ret = false; // compare with provided
}
#endif
return ret; return ret;
} }
void AEADChaCha20Poly1305Encrypt (const std::vector<std::pair<uint8_t *, size_t> >& bufs, const uint8_t * key, const uint8_t * nonce, uint8_t * mac) bool AEADChaCha20Poly1305 (const uint8_t * msg, size_t msgLen, const uint8_t * ad, size_t adLen,
const uint8_t * key, const uint8_t * nonce, uint8_t * buf, size_t len, bool encrypt)
{ {
if (bufs.empty ()) return; EVP_CIPHER_CTX * ctx = EVP_CIPHER_CTX_new ();
#if OPENSSL_AEAD_CHACHA20_POLY1305 auto ret = AEADChaCha20Poly1305 (ctx, msg, msgLen, ad, adLen, key, nonce, buf, len, encrypt);
int outlen = 0;
EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new ();
EVP_EncryptInit_ex(ctx, EVP_chacha20_poly1305(), 0, 0, 0);
EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_IVLEN, 12, 0);
EVP_EncryptInit_ex(ctx, NULL, NULL, key, nonce);
for (const auto& it: bufs)
EVP_EncryptUpdate(ctx, it.first, &outlen, it.first, it.second);
EVP_EncryptFinal_ex(ctx, NULL, &outlen);
EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_GET_TAG, 16, mac);
EVP_CIPHER_CTX_free (ctx); EVP_CIPHER_CTX_free (ctx);
#else return ret;
chacha::Chacha20State state;
// generate one time poly key
chacha::Chacha20Init (state, nonce, key, 0);
uint64_t polyKey[8];
memset(polyKey, 0, sizeof(polyKey));
chacha::Chacha20Encrypt (state, (uint8_t *)polyKey, 64);
Poly1305 polyHash (polyKey);
// encrypt buffers
Chacha20SetCounter (state, 1);
size_t size = 0;
for (const auto& it: bufs)
{
chacha::Chacha20Encrypt (state, it.first, it.second);
polyHash.Update (it.first, it.second); // after encryption
size += it.second;
}
// padding
uint8_t padding[16];
memset (padding, 0, 16);
auto rem = size & 0x0F; // %16
if (rem)
{
// padding2
rem = 16 - rem;
polyHash.Update (padding, rem);
}
// adLen and msgLen
// adLen is always zero
htole64buf (padding + 8, size);
polyHash.Update (padding, 16);
// MAC
polyHash.Finish ((uint64_t *)mac);
#endif
} }
void ChaCha20 (const uint8_t * msg, size_t msgLen, const uint8_t * key, const uint8_t * nonce, uint8_t * out) AEADChaCha20Poly1305Encryptor::AEADChaCha20Poly1305Encryptor ()
{
m_Ctx = EVP_CIPHER_CTX_new ();
}
AEADChaCha20Poly1305Encryptor::~AEADChaCha20Poly1305Encryptor ()
{
if (m_Ctx)
EVP_CIPHER_CTX_free (m_Ctx);
}
bool AEADChaCha20Poly1305Encryptor::Encrypt (const uint8_t * msg, size_t msgLen, const uint8_t * ad, size_t adLen,
const uint8_t * key, const uint8_t * nonce, uint8_t * buf, size_t len)
{
return AEADChaCha20Poly1305 (m_Ctx, msg, msgLen, ad, adLen, key, nonce, buf, len, true);
}
void AEADChaCha20Poly1305Encryptor::Encrypt (const std::vector<std::pair<uint8_t *, size_t> >& bufs,
const uint8_t * key, const uint8_t * nonce, uint8_t * mac)
{
if (bufs.empty ()) return;
int outlen = 0;
EVP_EncryptInit_ex(m_Ctx, EVP_chacha20_poly1305(), 0, 0, 0);
EVP_CIPHER_CTX_ctrl(m_Ctx, EVP_CTRL_AEAD_SET_IVLEN, 12, 0);
EVP_EncryptInit_ex(m_Ctx, NULL, NULL, key, nonce);
for (const auto& it: bufs)
EVP_EncryptUpdate(m_Ctx, it.first, &outlen, it.first, it.second);
EVP_EncryptFinal_ex(m_Ctx, NULL, &outlen);
EVP_CIPHER_CTX_ctrl(m_Ctx, EVP_CTRL_AEAD_GET_TAG, 16, mac);
}
AEADChaCha20Poly1305Decryptor::AEADChaCha20Poly1305Decryptor ()
{
m_Ctx = EVP_CIPHER_CTX_new ();
}
AEADChaCha20Poly1305Decryptor::~AEADChaCha20Poly1305Decryptor ()
{
if (m_Ctx)
EVP_CIPHER_CTX_free (m_Ctx);
}
bool AEADChaCha20Poly1305Decryptor::Decrypt (const uint8_t * msg, size_t msgLen, const uint8_t * ad, size_t adLen,
const uint8_t * key, const uint8_t * nonce, uint8_t * buf, size_t len)
{
return AEADChaCha20Poly1305 (m_Ctx, msg, msgLen, ad, adLen, key, nonce, buf, len, false);
}
static void ChaCha20 (EVP_CIPHER_CTX *ctx, const uint8_t * msg, size_t msgLen, const uint8_t * key, const uint8_t * nonce, uint8_t * out)
{ {
#if OPENSSL_AEAD_CHACHA20_POLY1305
EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new ();
uint32_t iv[4]; uint32_t iv[4];
iv[0] = htole32 (1); memcpy (iv + 1, nonce, 12); // counter | nonce iv[0] = htole32 (1); memcpy (iv + 1, nonce, 12); // counter | nonce
EVP_EncryptInit_ex(ctx, EVP_chacha20 (), NULL, key, (const uint8_t *)iv); EVP_EncryptInit_ex(ctx, EVP_chacha20 (), NULL, key, (const uint8_t *)iv);
int outlen = 0; int outlen = 0;
EVP_EncryptUpdate(ctx, out, &outlen, msg, msgLen); EVP_EncryptUpdate(ctx, out, &outlen, msg, msgLen);
EVP_EncryptFinal_ex(ctx, NULL, &outlen); EVP_EncryptFinal_ex(ctx, NULL, &outlen);
}
void ChaCha20 (const uint8_t * msg, size_t msgLen, const uint8_t * key, const uint8_t * nonce, uint8_t * out)
{
EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new ();
ChaCha20 (ctx, msg, msgLen, key, nonce, out);
EVP_CIPHER_CTX_free (ctx); EVP_CIPHER_CTX_free (ctx);
#else
chacha::Chacha20State state;
chacha::Chacha20Init (state, nonce, key, 1);
if (out != msg) memcpy (out, msg, msgLen);
chacha::Chacha20Encrypt (state, out, msgLen);
#endif
} }
ChaCha20Context::ChaCha20Context ()
{
m_Ctx = EVP_CIPHER_CTX_new ();
}
ChaCha20Context::~ChaCha20Context ()
{
if (m_Ctx)
EVP_CIPHER_CTX_free (m_Ctx);
}
void ChaCha20Context::operator ()(const uint8_t * msg, size_t msgLen, const uint8_t * key, const uint8_t * nonce, uint8_t * out)
{
ChaCha20 (m_Ctx, msg, msgLen, key, nonce, out);
}
void HKDF (const uint8_t * salt, const uint8_t * key, size_t keyLen, const std::string& info, void HKDF (const uint8_t * salt, const uint8_t * key, size_t keyLen, const std::string& info,
uint8_t * out, size_t outLen) uint8_t * out, size_t outLen)
{ {
@@ -1292,12 +892,8 @@ namespace crypto
} }
}*/ }*/
void InitCrypto (bool precomputation, bool aesni, bool force) void InitCrypto (bool precomputation)
{ {
i2p::cpu::Detect (aesni, force);
#if LEGACY_OPENSSL
SSL_library_init ();
#endif
/* auto numLocks = CRYPTO_num_locks(); /* auto numLocks = CRYPTO_num_locks();
for (int i = 0; i < numLocks; i++) for (int i = 0; i < numLocks; i++)
m_OpenSSLMutexes.emplace_back (new std::mutex); m_OpenSSLMutexes.emplace_back (new std::mutex);

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2023, The PurpleI2P Project * Copyright (c) 2013-2025, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@@ -21,32 +21,17 @@
#include <openssl/sha.h> #include <openssl/sha.h>
#include <openssl/evp.h> #include <openssl/evp.h>
#include <openssl/rand.h> #include <openssl/rand.h>
#include <openssl/engine.h>
#include <openssl/opensslv.h> #include <openssl/opensslv.h>
#include "Base.h" #include "Base.h"
#include "Tag.h" #include "Tag.h"
#include "CPU.h"
// recognize openssl version and features // recognize openssl version and features
#if (defined(LIBRESSL_VERSION_NUMBER) && (LIBRESSL_VERSION_NUMBER >= 0x3050200fL)) // LibreSSL 3.5.2 and above #if (OPENSSL_VERSION_NUMBER >= 0x010101000) // 1.1.1
# define LEGACY_OPENSSL 0 # define OPENSSL_HKDF 1
#elif ((OPENSSL_VERSION_NUMBER < 0x010100000) || defined(LIBRESSL_VERSION_NUMBER)) // 1.0.2 and below or LibreSSL # define OPENSSL_EDDSA 1
# define LEGACY_OPENSSL 1 # if (!defined(LIBRESSL_VERSION_NUMBER) && (OPENSSL_VERSION_NUMBER != 0x030000000)) // 3.0.0, regression in SipHash, not implemented in LibreSSL
# define X509_getm_notBefore X509_get_notBefore # define OPENSSL_SIPHASH 1
# define X509_getm_notAfter X509_get_notAfter
#else
# define LEGACY_OPENSSL 0
# if (OPENSSL_VERSION_NUMBER >= 0x010101000) // 1.1.1
# define OPENSSL_HKDF 1
# define OPENSSL_EDDSA 1
# define OPENSSL_X25519 1
# if (OPENSSL_VERSION_NUMBER != 0x030000000) // 3.0.0, regression in SipHash
# define OPENSSL_SIPHASH 1
# endif
# endif
# if !defined OPENSSL_NO_CHACHA && !defined OPENSSL_NO_POLY1305 // some builds might not include them
# define OPENSSL_AEAD_CHACHA20_POLY1305 1
# endif # endif
#endif #endif
@@ -83,13 +68,8 @@ namespace crypto
private: private:
uint8_t m_PublicKey[32]; uint8_t m_PublicKey[32];
#if OPENSSL_X25519
EVP_PKEY_CTX * m_Ctx; EVP_PKEY_CTX * m_Ctx;
EVP_PKEY * m_Pkey; EVP_PKEY * m_Pkey;
#else
BN_CTX * m_Ctx;
uint8_t m_PrivateKey[32];
#endif
bool m_IsElligatorIneligible = false; // true if definitely ineligible bool m_IsElligatorIneligible = false; // true if definitely ineligible
}; };
@@ -104,142 +84,70 @@ namespace crypto
void GenerateECIESKeyPair (const EC_GROUP * curve, BIGNUM *& priv, EC_POINT *& pub); void GenerateECIESKeyPair (const EC_GROUP * curve, BIGNUM *& priv, EC_POINT *& pub);
// AES // AES
struct ChipherBlock
{
uint8_t buf[16];
void operator^=(const ChipherBlock& other) // XOR
{
if (!(((size_t)buf | (size_t)other.buf) & 0x03)) // multiple of 4 ?
{
for (int i = 0; i < 4; i++)
reinterpret_cast<uint32_t *>(buf)[i] ^= reinterpret_cast<const uint32_t *>(other.buf)[i];
}
else
{
for (int i = 0; i < 16; i++)
buf[i] ^= other.buf[i];
}
}
};
typedef i2p::data::Tag<32> AESKey; typedef i2p::data::Tag<32> AESKey;
template<size_t sz>
class AESAlignedBuffer // 16 bytes alignment
{
public:
AESAlignedBuffer ()
{
m_Buf = m_UnalignedBuffer;
uint8_t rem = ((size_t)m_Buf) & 0x0f;
if (rem)
m_Buf += (16 - rem);
}
operator uint8_t * () { return m_Buf; };
operator const uint8_t * () const { return m_Buf; };
ChipherBlock * GetChipherBlock () { return (ChipherBlock *)m_Buf; };
const ChipherBlock * GetChipherBlock () const { return (const ChipherBlock *)m_Buf; };
private:
uint8_t m_UnalignedBuffer[sz + 15]; // up to 15 bytes alignment
uint8_t * m_Buf;
};
#if SUPPORTS_AES
class ECBCryptoAESNI
{
public:
uint8_t * GetKeySchedule () { return m_KeySchedule; };
protected:
void ExpandKey (const AESKey& key);
private:
AESAlignedBuffer<240> m_KeySchedule; // 14 rounds for AES-256, 240 bytes
};
#endif
#if SUPPORTS_AES
class ECBEncryption: public ECBCryptoAESNI
#else
class ECBEncryption class ECBEncryption
#endif
{ {
public: public:
void SetKey (const AESKey& key); ECBEncryption ();
~ECBEncryption ();
void SetKey (const uint8_t * key) { m_Key = key; };
void Encrypt(const uint8_t * in, uint8_t * out);
void Encrypt(const ChipherBlock * in, ChipherBlock * out); private:
private: AESKey m_Key;
AES_KEY m_Key; EVP_CIPHER_CTX * m_Ctx;
}; };
#if SUPPORTS_AES
class ECBDecryption: public ECBCryptoAESNI
#else
class ECBDecryption class ECBDecryption
#endif
{ {
public: public:
void SetKey (const AESKey& key); ECBDecryption ();
void Decrypt (const ChipherBlock * in, ChipherBlock * out); ~ECBDecryption ();
void SetKey (const uint8_t * key) { m_Key = key; };
void Decrypt (const uint8_t * in, uint8_t * out);
private: private:
AES_KEY m_Key;
AESKey m_Key;
EVP_CIPHER_CTX * m_Ctx;
}; };
class CBCEncryption class CBCEncryption
{ {
public: public:
CBCEncryption () { memset ((uint8_t *)m_LastBlock, 0, 16); }; CBCEncryption ();
~CBCEncryption ();
void SetKey (const AESKey& key) { m_ECBEncryption.SetKey (key); }; // 32 bytes
void SetIV (const uint8_t * iv) { memcpy ((uint8_t *)m_LastBlock, iv, 16); }; // 16 bytes
void GetIV (uint8_t * iv) const { memcpy (iv, (const uint8_t *)m_LastBlock, 16); };
void Encrypt (int numBlocks, const ChipherBlock * in, ChipherBlock * out);
void Encrypt (const uint8_t * in, std::size_t len, uint8_t * out);
void Encrypt (const uint8_t * in, uint8_t * out); // one block
ECBEncryption & ECB() { return m_ECBEncryption; }
void SetKey (const uint8_t * key) { m_Key = key; }; // 32 bytes
void Encrypt (const uint8_t * in, size_t len, const uint8_t * iv, uint8_t * out);
private: private:
AESAlignedBuffer<16> m_LastBlock; AESKey m_Key;
EVP_CIPHER_CTX * m_Ctx;
ECBEncryption m_ECBEncryption;
}; };
class CBCDecryption class CBCDecryption
{ {
public: public:
CBCDecryption () { memset ((uint8_t *)m_IV, 0, 16); }; CBCDecryption ();
~CBCDecryption ();
void SetKey (const AESKey& key) { m_ECBDecryption.SetKey (key); }; // 32 bytes
void SetIV (const uint8_t * iv) { memcpy ((uint8_t *)m_IV, iv, 16); }; // 16 bytes void SetKey (const uint8_t * key) { m_Key = key; }; // 32 bytes
void GetIV (uint8_t * iv) const { memcpy (iv, (const uint8_t *)m_IV, 16); }; void Decrypt (const uint8_t * in, size_t len, const uint8_t * iv, uint8_t * out);
void Decrypt (int numBlocks, const ChipherBlock * in, ChipherBlock * out);
void Decrypt (const uint8_t * in, std::size_t len, uint8_t * out);
void Decrypt (const uint8_t * in, uint8_t * out); // one block
ECBDecryption & ECB() { return m_ECBDecryption; }
private: private:
AESAlignedBuffer<16> m_IV; AESKey m_Key;
ECBDecryption m_ECBDecryption; EVP_CIPHER_CTX * m_Ctx;
}; };
class TunnelEncryption // with double IV encryption class TunnelEncryption // with double IV encryption
@@ -279,13 +187,58 @@ namespace crypto
}; };
// AEAD/ChaCha20/Poly1305 // AEAD/ChaCha20/Poly1305
bool AEADChaCha20Poly1305 (const uint8_t * msg, size_t msgLen, const uint8_t * ad, size_t adLen, const uint8_t * key, const uint8_t * nonce, uint8_t * buf, size_t len, bool encrypt); // msgLen is len without tag
void AEADChaCha20Poly1305Encrypt (const std::vector<std::pair<uint8_t *, size_t> >& bufs, const uint8_t * key, const uint8_t * nonce, uint8_t * mac); // encrypt multiple buffers with zero ad class AEADChaCha20Poly1305Encryptor
{
public:
AEADChaCha20Poly1305Encryptor ();
~AEADChaCha20Poly1305Encryptor ();
bool Encrypt (const uint8_t * msg, size_t msgLen, const uint8_t * ad, size_t adLen,
const uint8_t * key, const uint8_t * nonce, uint8_t * buf, size_t len); // msgLen is len without tag
void Encrypt (const std::vector<std::pair<uint8_t *, size_t> >& bufs, const uint8_t * key, const uint8_t * nonce, uint8_t * mac); // encrypt multiple buffers with zero ad
private:
EVP_CIPHER_CTX * m_Ctx;
};
class AEADChaCha20Poly1305Decryptor
{
public:
AEADChaCha20Poly1305Decryptor ();
~AEADChaCha20Poly1305Decryptor ();
bool Decrypt (const uint8_t * msg, size_t msgLen, const uint8_t * ad, size_t adLen,
const uint8_t * key, const uint8_t * nonce, uint8_t * buf, size_t len); // msgLen is len without tag
private:
EVP_CIPHER_CTX * m_Ctx;
};
bool AEADChaCha20Poly1305 (const uint8_t * msg, size_t msgLen, const uint8_t * ad, size_t adLen,
const uint8_t * key, const uint8_t * nonce, uint8_t * buf, size_t len, bool encrypt); // msgLen is len without tag
// ChaCha20 // ChaCha20
void ChaCha20 (const uint8_t * msg, size_t msgLen, const uint8_t * key, const uint8_t * nonce, uint8_t * out); void ChaCha20 (const uint8_t * msg, size_t msgLen, const uint8_t * key, const uint8_t * nonce, uint8_t * out);
class ChaCha20Context
{
public:
ChaCha20Context ();
~ChaCha20Context ();
void operator ()(const uint8_t * msg, size_t msgLen, const uint8_t * key, const uint8_t * nonce, uint8_t * out);
private:
EVP_CIPHER_CTX * m_Ctx;
};
// HKDF // HKDF
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 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
@@ -307,84 +260,9 @@ namespace crypto
void InitNoiseIKState (NoiseSymmetricState& state, const uint8_t * pub); // Noise_IK (ratchets) void InitNoiseIKState (NoiseSymmetricState& state, const uint8_t * pub); // Noise_IK (ratchets)
// init and terminate // init and terminate
void InitCrypto (bool precomputation, bool aesni, bool force); void InitCrypto (bool precomputation);
void TerminateCrypto (); void TerminateCrypto ();
} }
} }
// take care about openssl below 1.1.0
#if LEGACY_OPENSSL
// define getters and setters introduced in 1.1.0
inline int DSA_set0_pqg(DSA *d, BIGNUM *p, BIGNUM *q, BIGNUM *g)
{
if (d->p) BN_free (d->p);
if (d->q) BN_free (d->q);
if (d->g) BN_free (d->g);
d->p = p; d->q = q; d->g = g; return 1;
}
inline int DSA_set0_key(DSA *d, BIGNUM *pub_key, BIGNUM *priv_key)
{
if (d->pub_key) BN_free (d->pub_key);
if (d->priv_key) BN_free (d->priv_key);
d->pub_key = pub_key; d->priv_key = priv_key; return 1;
}
inline void DSA_get0_key(const DSA *d, const BIGNUM **pub_key, const BIGNUM **priv_key)
{ *pub_key = d->pub_key; *priv_key = d->priv_key; }
inline int DSA_SIG_set0(DSA_SIG *sig, BIGNUM *r, BIGNUM *s)
{
if (sig->r) BN_free (sig->r);
if (sig->s) BN_free (sig->s);
sig->r = r; sig->s = s; return 1;
}
inline void DSA_SIG_get0(const DSA_SIG *sig, const BIGNUM **pr, const BIGNUM **ps)
{ *pr = sig->r; *ps = sig->s; }
inline int ECDSA_SIG_set0(ECDSA_SIG *sig, BIGNUM *r, BIGNUM *s)
{
if (sig->r) BN_free (sig->r);
if (sig->s) BN_free (sig->s);
sig->r = r; sig->s = s; return 1;
}
inline void ECDSA_SIG_get0(const ECDSA_SIG *sig, const BIGNUM **pr, const BIGNUM **ps)
{ *pr = sig->r; *ps = sig->s; }
inline int RSA_set0_key(RSA *r, BIGNUM *n, BIGNUM *e, BIGNUM *d)
{
if (r->n) BN_free (r->n);
if (r->e) BN_free (r->e);
if (r->d) BN_free (r->d);
r->n = n; r->e = e; r->d = d; return 1;
}
inline void RSA_get0_key(const RSA *r, const BIGNUM **n, const BIGNUM **e, const BIGNUM **d)
{ *n = r->n; *e = r->e; *d = r->d; }
inline int DH_set0_pqg(DH *dh, BIGNUM *p, BIGNUM *q, BIGNUM *g)
{
if (dh->p) BN_free (dh->p);
if (dh->q) BN_free (dh->q);
if (dh->g) BN_free (dh->g);
dh->p = p; dh->q = q; dh->g = g; return 1;
}
inline int DH_set0_key(DH *dh, BIGNUM *pub_key, BIGNUM *priv_key)
{
if (dh->pub_key) BN_free (dh->pub_key);
if (dh->priv_key) BN_free (dh->priv_key);
dh->pub_key = pub_key; dh->priv_key = priv_key; return 1;
}
inline void DH_get0_key(const DH *dh, const BIGNUM **pub_key, const BIGNUM **priv_key)
{ *pub_key = dh->pub_key; *priv_key = dh->priv_key; }
inline RSA *EVP_PKEY_get0_RSA(EVP_PKEY *pkey)
{ return pkey->pkey.rsa; }
inline EVP_MD_CTX *EVP_MD_CTX_new ()
{ return EVP_MD_CTX_create(); }
inline void EVP_MD_CTX_free (EVP_MD_CTX *ctx)
{ EVP_MD_CTX_destroy (ctx); }
// ssl
#define TLS_method TLSv1_method
#endif
#endif #endif

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2024, The PurpleI2P Project * Copyright (c) 2013-2025, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@@ -104,8 +104,7 @@ namespace datagram
if (verified) if (verified)
{ {
auto h = identity.GetIdentHash(); auto session = ObtainSession (identity.GetIdentHash());
auto session = ObtainSession(h);
session->Ack(); session->Ack();
auto r = FindReceiver(toPort); auto r = FindReceiver(toPort);
if(r) if(r)
@@ -288,8 +287,8 @@ namespace datagram
DatagramSession::DatagramSession(std::shared_ptr<i2p::client::ClientDestination> localDestination, DatagramSession::DatagramSession(std::shared_ptr<i2p::client::ClientDestination> localDestination,
const i2p::data::IdentHash & remoteIdent) : const i2p::data::IdentHash & remoteIdent) :
m_LocalDestination(localDestination), m_LocalDestination(localDestination), m_RemoteIdent(remoteIdent),
m_RemoteIdent(remoteIdent), m_LastUse (0), m_LastFlush (0),
m_RequestingLS(false) m_RequestingLS(false)
{ {
} }
@@ -310,8 +309,12 @@ namespace datagram
if (msg || m_SendQueue.empty ()) if (msg || m_SendQueue.empty ())
m_SendQueue.push_back(msg); m_SendQueue.push_back(msg);
// flush queue right away if full // flush queue right away if full
if (!msg || m_SendQueue.size() >= DATAGRAM_SEND_QUEUE_MAX_SIZE) if (!msg || m_SendQueue.size() >= DATAGRAM_SEND_QUEUE_MAX_SIZE ||
m_LastUse > m_LastFlush + DATAGRAM_MAX_FLUSH_INTERVAL)
{
FlushSendQueue(); FlushSendQueue();
m_LastFlush = m_LastUse;
}
} }
DatagramSession::Info DatagramSession::GetSessionInfo() const DatagramSession::Info DatagramSession::GetSessionInfo() const
@@ -344,7 +347,7 @@ namespace datagram
if(path) if(path)
path->updateTime = i2p::util::GetSecondsSinceEpoch (); path->updateTime = i2p::util::GetSecondsSinceEpoch ();
if (IsRatchets ()) if (IsRatchets ())
SendMsg (nullptr); // send empty message in case if we have some data to send SendMsg (nullptr); // send empty message in case if we don't have some data to send
} }
std::shared_ptr<i2p::garlic::GarlicRoutingPath> DatagramSession::GetSharedRoutingPath () std::shared_ptr<i2p::garlic::GarlicRoutingPath> DatagramSession::GetSharedRoutingPath ()
@@ -377,15 +380,19 @@ namespace datagram
if (!found) if (!found)
{ {
m_RoutingSession = m_LocalDestination->GetRoutingSession(m_RemoteLeaseSet, true); m_RoutingSession = m_LocalDestination->GetRoutingSession(m_RemoteLeaseSet, true);
if (!m_RoutingSession->GetOwner () || !m_RoutingSession->IsReadyToSend ()) if (m_RoutingSession)
m_PendingRoutingSessions.push_back (m_RoutingSession); {
m_RoutingSession->SetAckRequestInterval (DATAGRAM_SESSION_ACK_REQUEST_INTERVAL);
if (!m_RoutingSession->GetOwner () || !m_RoutingSession->IsReadyToSend ())
m_PendingRoutingSessions.push_back (m_RoutingSession);
}
} }
} }
auto path = m_RoutingSession->GetSharedRoutingPath(); auto path = m_RoutingSession->GetSharedRoutingPath();
if (path && m_RoutingSession->IsRatchets () && if (path && m_RoutingSession->IsRatchets () && m_RoutingSession->CleanupUnconfirmedTags ())
m_LastUse > m_RoutingSession->GetLastActivityTimestamp ()*1000 + DATAGRAM_SESSION_PATH_TIMEOUT)
{ {
LogPrint (eLogDebug, "Datagram: path reset");
m_RoutingSession->SetSharedRoutingPath (nullptr); m_RoutingSession->SetSharedRoutingPath (nullptr);
path = nullptr; path = nullptr;
} }
@@ -413,7 +420,14 @@ namespace datagram
auto sz = ls.size(); auto sz = ls.size();
if (sz) if (sz)
{ {
auto idx = rand() % sz; int idx = -1;
if (m_LocalDestination)
{
auto pool = m_LocalDestination->GetTunnelPool ();
if (pool)
idx = pool->GetRng ()() % sz;
}
if (idx < 0) idx = rand () % sz;
path->remoteLease = ls[idx]; path->remoteLease = ls[idx];
} }
else else
@@ -439,7 +453,14 @@ namespace datagram
auto sz = ls.size(); auto sz = ls.size();
if (sz) if (sz)
{ {
auto idx = rand() % sz; int idx = -1;
if (m_LocalDestination)
{
auto pool = m_LocalDestination->GetTunnelPool ();
if (pool)
idx = pool->GetRng ()() % sz;
}
if (idx < 0) idx = rand () % sz;
path->remoteLease = ls[idx]; path->remoteLease = ls[idx];
} }
else else

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2024, The PurpleI2P Project * Copyright (c) 2013-2025, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@@ -31,8 +31,6 @@ namespace datagram
{ {
// milliseconds for max session idle time // milliseconds for max session idle time
const uint64_t DATAGRAM_SESSION_MAX_IDLE = 10 * 60 * 1000; const uint64_t DATAGRAM_SESSION_MAX_IDLE = 10 * 60 * 1000;
// milliseconds for how long we try sticking to a dead routing path before trying to switch
const uint64_t DATAGRAM_SESSION_PATH_TIMEOUT = 10 * 1000;
// milliseconds interval a routing path is used before switching // milliseconds interval a routing path is used before switching
const uint64_t DATAGRAM_SESSION_PATH_SWITCH_INTERVAL = 20 * 60 * 1000; const uint64_t DATAGRAM_SESSION_PATH_SWITCH_INTERVAL = 20 * 60 * 1000;
// milliseconds before lease expire should we try switching leases // milliseconds before lease expire should we try switching leases
@@ -43,6 +41,8 @@ namespace datagram
const uint64_t DATAGRAM_SESSION_PATH_MIN_LIFETIME = 5 * 1000; const uint64_t DATAGRAM_SESSION_PATH_MIN_LIFETIME = 5 * 1000;
// max 64 messages buffered in send queue for each datagram session // max 64 messages buffered in send queue for each datagram session
const size_t DATAGRAM_SEND_QUEUE_MAX_SIZE = 64; const size_t DATAGRAM_SEND_QUEUE_MAX_SIZE = 64;
const uint64_t DATAGRAM_MAX_FLUSH_INTERVAL = 5; // in milliseconds
const int DATAGRAM_SESSION_ACK_REQUEST_INTERVAL = 5500; // in milliseconds
class DatagramSession : public std::enable_shared_from_this<DatagramSession> class DatagramSession : public std::enable_shared_from_this<DatagramSession>
{ {
@@ -98,7 +98,7 @@ namespace datagram
std::shared_ptr<i2p::garlic::GarlicRoutingSession> m_RoutingSession; std::shared_ptr<i2p::garlic::GarlicRoutingSession> m_RoutingSession;
std::vector<std::shared_ptr<i2p::garlic::GarlicRoutingSession> > m_PendingRoutingSessions; std::vector<std::shared_ptr<i2p::garlic::GarlicRoutingSession> > m_PendingRoutingSessions;
std::vector<std::shared_ptr<I2NPMessage> > m_SendQueue; std::vector<std::shared_ptr<I2NPMessage> > m_SendQueue;
uint64_t m_LastUse; uint64_t m_LastUse, m_LastFlush; // milliseconds
bool m_RequestingLS; bool m_RequestingLS;
}; };

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2024, The PurpleI2P Project * Copyright (c) 2013-2025, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@@ -23,7 +23,7 @@ namespace i2p
{ {
namespace client namespace client
{ {
LeaseSetDestination::LeaseSetDestination (boost::asio::io_service& service, LeaseSetDestination::LeaseSetDestination (boost::asio::io_context& service,
bool isPublic, const std::map<std::string, std::string> * params): bool isPublic, const std::map<std::string, std::string> * params):
m_Service (service), m_IsPublic (isPublic), m_PublishReplyToken (0), m_Service (service), m_IsPublic (isPublic), m_PublishReplyToken (0),
m_LastSubmissionTime (0), m_PublishConfirmationTimer (m_Service), m_LastSubmissionTime (0), m_PublishConfirmationTimer (m_Service),
@@ -37,6 +37,7 @@ namespace client
int inVar = DEFAULT_INBOUND_TUNNELS_LENGTH_VARIANCE; int inVar = DEFAULT_INBOUND_TUNNELS_LENGTH_VARIANCE;
int outVar = DEFAULT_OUTBOUND_TUNNELS_LENGTH_VARIANCE; int outVar = DEFAULT_OUTBOUND_TUNNELS_LENGTH_VARIANCE;
int numTags = DEFAULT_TAGS_TO_SEND; int numTags = DEFAULT_TAGS_TO_SEND;
bool isHighBandwidth = true;
std::shared_ptr<std::vector<i2p::data::IdentHash> > explicitPeers; std::shared_ptr<std::vector<i2p::data::IdentHash> > explicitPeers;
try try
{ {
@@ -92,7 +93,7 @@ namespace client
it = params->find (I2CP_PARAM_DONT_PUBLISH_LEASESET); it = params->find (I2CP_PARAM_DONT_PUBLISH_LEASESET);
if (it != params->end ()) if (it != params->end ())
{ {
// oveeride isPublic // override isPublic
m_IsPublic = (it->second != "true"); m_IsPublic = (it->second != "true");
} }
it = params->find (I2CP_PARAM_LEASESET_TYPE); it = params->find (I2CP_PARAM_LEASESET_TYPE);
@@ -121,6 +122,9 @@ namespace client
m_LeaseSetPrivKey.reset (nullptr); m_LeaseSetPrivKey.reset (nullptr);
} }
} }
it = params->find (I2CP_PARAM_STREAMING_PROFILE);
if (it != params->end ())
isHighBandwidth = std::stoi (it->second) != STREAMING_PROFILE_INTERACTIVE;
} }
} }
catch (std::exception & ex) catch (std::exception & ex)
@@ -128,7 +132,7 @@ namespace client
LogPrint(eLogError, "Destination: Unable to parse parameters for destination: ", ex.what()); LogPrint(eLogError, "Destination: Unable to parse parameters for destination: ", ex.what());
} }
SetNumTags (numTags); SetNumTags (numTags);
m_Pool = i2p::tunnel::tunnels.CreateTunnelPool (inLen, outLen, inQty, outQty, inVar, outVar); m_Pool = i2p::tunnel::tunnels.CreateTunnelPool (inLen, outLen, inQty, outQty, inVar, outVar, isHighBandwidth);
if (explicitPeers) if (explicitPeers)
m_Pool->SetExplicitPeers (explicitPeers); m_Pool->SetExplicitPeers (explicitPeers);
if(params) if(params)
@@ -164,7 +168,7 @@ namespace client
LoadTags (); LoadTags ();
m_Pool->SetLocalDestination (shared_from_this ()); m_Pool->SetLocalDestination (shared_from_this ());
m_Pool->SetActive (true); m_Pool->SetActive (true);
m_CleanupTimer.expires_from_now (boost::posix_time::minutes (DESTINATION_CLEANUP_TIMEOUT)); m_CleanupTimer.expires_from_now (boost::posix_time::seconds (DESTINATION_CLEANUP_TIMEOUT));
m_CleanupTimer.async_wait (std::bind (&LeaseSetDestination::HandleCleanupTimer, m_CleanupTimer.async_wait (std::bind (&LeaseSetDestination::HandleCleanupTimer,
shared_from_this (), std::placeholders::_1)); shared_from_this (), std::placeholders::_1));
} }
@@ -191,7 +195,7 @@ namespace client
m_IsPublic = itr->second != "true"; m_IsPublic = itr->second != "true";
} }
int inLen, outLen, inQuant, outQuant, numTags, minLatency, maxLatency; int inLen = 0, outLen = 0, inQuant = 0, outQuant = 0, numTags = 0, minLatency = 0, maxLatency = 0;
std::map<std::string, int&> intOpts = { std::map<std::string, int&> intOpts = {
{I2CP_PARAM_INBOUND_TUNNEL_LENGTH, inLen}, {I2CP_PARAM_INBOUND_TUNNEL_LENGTH, inLen},
{I2CP_PARAM_OUTBOUND_TUNNEL_LENGTH, outLen}, {I2CP_PARAM_OUTBOUND_TUNNEL_LENGTH, outLen},
@@ -290,7 +294,7 @@ namespace client
if (m_IsPublic) if (m_IsPublic)
{ {
auto s = shared_from_this (); auto s = shared_from_this ();
m_Service.post ([s](void) boost::asio::post (m_Service, [s](void)
{ {
s->m_PublishVerificationTimer.cancel (); s->m_PublishVerificationTimer.cancel ();
s->Publish (); s->Publish ();
@@ -318,7 +322,7 @@ namespace client
memcpy (data.k, key, 32); memcpy (data.k, key, 32);
memcpy (data.t, tag, 32); memcpy (data.t, tag, 32);
auto s = shared_from_this (); auto s = shared_from_this ();
m_Service.post ([s,data](void) boost::asio::post (m_Service, [s,data](void)
{ {
s->AddSessionKey (data.k, data.t); s->AddSessionKey (data.k, data.t);
}); });
@@ -335,7 +339,7 @@ namespace client
memcpy (data.k, key, 32); memcpy (data.k, key, 32);
data.t = tag; data.t = tag;
auto s = shared_from_this (); auto s = shared_from_this ();
m_Service.post ([s,data](void) boost::asio::post (m_Service, [s,data](void)
{ {
s->AddECIESx25519Key (data.k, data.t); s->AddECIESx25519Key (data.k, data.t);
}); });
@@ -343,13 +347,30 @@ namespace client
void LeaseSetDestination::ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg) void LeaseSetDestination::ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg)
{ {
m_Service.post (std::bind (&LeaseSetDestination::HandleGarlicMessage, shared_from_this (), msg)); if (!msg) return;
bool empty = false;
{
std::lock_guard<std::mutex> l(m_IncomingMsgsQueueMutex);
empty = m_IncomingMsgsQueue.empty ();
m_IncomingMsgsQueue.push_back (msg);
}
if (empty)
boost::asio::post (m_Service, [s = shared_from_this ()]()
{
std::list<std::shared_ptr<I2NPMessage> > receivedMsgs;
{
std::lock_guard<std::mutex> l(s->m_IncomingMsgsQueueMutex);
s->m_IncomingMsgsQueue.swap (receivedMsgs);
}
for (auto& it: receivedMsgs)
s->HandleGarlicMessage (it);
});
} }
void LeaseSetDestination::ProcessDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg) void LeaseSetDestination::ProcessDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg)
{ {
uint32_t msgID = bufbe32toh (msg->GetPayload () + DELIVERY_STATUS_MSGID_OFFSET); uint32_t msgID = bufbe32toh (msg->GetPayload () + DELIVERY_STATUS_MSGID_OFFSET);
m_Service.post (std::bind (&LeaseSetDestination::HandleDeliveryStatusMessage, shared_from_this (), msgID)); boost::asio::post (m_Service, std::bind (&LeaseSetDestination::HandleDeliveryStatusMessage, shared_from_this (), msgID));
} }
void LeaseSetDestination::HandleI2NPMessage (const uint8_t * buf, size_t len) void LeaseSetDestination::HandleI2NPMessage (const uint8_t * buf, size_t len)
@@ -467,7 +488,7 @@ namespace client
{ {
auto it2 = m_LeaseSetRequests.find (key); auto it2 = m_LeaseSetRequests.find (key);
if (it2 != m_LeaseSetRequests.end ()) if (it2 != m_LeaseSetRequests.end ())
{ {
request = it2->second; request = it2->second;
m_LeaseSetRequests.erase (it2); m_LeaseSetRequests.erase (it2);
if (request->requestedBlindedKey) if (request->requestedBlindedKey)
@@ -489,14 +510,14 @@ namespace client
// publishing verification doesn't have requestedBlindedKey // publishing verification doesn't have requestedBlindedKey
auto localLeaseSet = GetLeaseSetMt (); auto localLeaseSet = GetLeaseSetMt ();
if (localLeaseSet->GetStoreHash () == key) if (localLeaseSet->GetStoreHash () == key)
{ {
auto ls = std::make_shared<i2p::data::LeaseSet2> (i2p::data::NETDB_STORE_TYPE_ENCRYPTED_LEASESET2, auto ls = std::make_shared<i2p::data::LeaseSet2> (i2p::data::NETDB_STORE_TYPE_ENCRYPTED_LEASESET2,
localLeaseSet->GetBuffer (), localLeaseSet->GetBufferLen (), false); localLeaseSet->GetBuffer (), localLeaseSet->GetBufferLen (), false);
leaseSet = ls; leaseSet = ls;
} }
else else
LogPrint (eLogWarning, "Destination: Encrypted LeaseSet2 received for request without blinded key"); LogPrint (eLogWarning, "Destination: Encrypted LeaseSet2 received for request without blinded key");
} }
} }
else else
LogPrint (eLogWarning, "Destination: Couldn't find request for encrypted LeaseSet2"); LogPrint (eLogWarning, "Destination: Couldn't find request for encrypted LeaseSet2");
@@ -507,14 +528,14 @@ namespace client
} }
if (!request) if (!request)
{ {
auto it1 = m_LeaseSetRequests.find (key); auto it1 = m_LeaseSetRequests.find (key);
if (it1 != m_LeaseSetRequests.end ()) if (it1 != m_LeaseSetRequests.end ())
{ {
request = it1->second; request = it1->second;
m_LeaseSetRequests.erase (it1); m_LeaseSetRequests.erase (it1);
} }
} }
if (request) if (request)
{ {
request->requestTimeoutTimer.cancel (); request->requestTimeoutTimer.cancel ();
@@ -546,7 +567,7 @@ namespace client
LogPrint (eLogWarning, "Destination: Request for ", key.ToBase64 (), " not found"); LogPrint (eLogWarning, "Destination: Request for ", key.ToBase64 (), " not found");
} }
void LeaseSetDestination::SendNextLeaseSetRequest (const i2p::data::IdentHash& key, void LeaseSetDestination::SendNextLeaseSetRequest (const i2p::data::IdentHash& key,
std::shared_ptr<LeaseSetRequest> request) std::shared_ptr<LeaseSetRequest> request)
{ {
bool found = false; bool found = false;
@@ -566,8 +587,8 @@ namespace client
request->Complete (nullptr); request->Complete (nullptr);
m_LeaseSetRequests.erase (key); m_LeaseSetRequests.erase (key);
} }
} }
void LeaseSetDestination::HandleDeliveryStatusMessage (uint32_t msgID) void LeaseSetDestination::HandleDeliveryStatusMessage (uint32_t msgID)
{ {
if (msgID == m_PublishReplyToken) if (msgID == m_PublishReplyToken)
@@ -576,7 +597,8 @@ namespace client
m_ExcludedFloodfills.clear (); m_ExcludedFloodfills.clear ();
m_PublishReplyToken = 0; m_PublishReplyToken = 0;
// schedule verification // schedule verification
m_PublishVerificationTimer.expires_from_now (boost::posix_time::seconds(PUBLISH_VERIFICATION_TIMEOUT)); m_PublishVerificationTimer.expires_from_now (boost::posix_time::seconds(PUBLISH_VERIFICATION_TIMEOUT +
(m_Pool ? m_Pool->GetRng ()() % PUBLISH_VERIFICATION_TIMEOUT_VARIANCE : 0)));
m_PublishVerificationTimer.async_wait (std::bind (&LeaseSetDestination::HandlePublishVerificationTimer, m_PublishVerificationTimer.async_wait (std::bind (&LeaseSetDestination::HandlePublishVerificationTimer,
shared_from_this (), std::placeholders::_1)); shared_from_this (), std::placeholders::_1));
} }
@@ -584,9 +606,12 @@ namespace client
i2p::garlic::GarlicDestination::HandleDeliveryStatusMessage (msgID); i2p::garlic::GarlicDestination::HandleDeliveryStatusMessage (msgID);
} }
void LeaseSetDestination::SetLeaseSetUpdated () void LeaseSetDestination::SetLeaseSetUpdated (bool post)
{ {
UpdateLeaseSet (); if (post)
boost::asio::post (m_Service, [s = shared_from_this ()]() { s->UpdateLeaseSet (); });
else
UpdateLeaseSet ();
} }
void LeaseSetDestination::Publish () void LeaseSetDestination::Publish ()
@@ -624,7 +649,7 @@ namespace client
if (!outbound || !inbound) if (!outbound || !inbound)
{ {
if (!m_Pool->GetInboundTunnels ().empty () && !m_Pool->GetOutboundTunnels ().empty ()) if (!m_Pool->GetInboundTunnels ().empty () && !m_Pool->GetOutboundTunnels ().empty ())
{ {
LogPrint (eLogInfo, "Destination: No compatible tunnels with ", floodfill->GetIdentHash ().ToBase64 (), ". Trying another floodfill"); LogPrint (eLogInfo, "Destination: No compatible tunnels with ", floodfill->GetIdentHash ().ToBase64 (), ". Trying another floodfill");
m_ExcludedFloodfills.insert (floodfill->GetIdentHash ()); m_ExcludedFloodfills.insert (floodfill->GetIdentHash ());
floodfill = i2p::data::netdb.GetClosestFloodfill (leaseSet->GetStoreHash (), m_ExcludedFloodfills); floodfill = i2p::data::netdb.GetClosestFloodfill (leaseSet->GetStoreHash (), m_ExcludedFloodfills);
@@ -642,18 +667,18 @@ namespace client
} }
else else
LogPrint (eLogError, "Destination: Can't publish LeaseSet, no more floodfills found"); LogPrint (eLogError, "Destination: Can't publish LeaseSet, no more floodfills found");
} }
else else
LogPrint (eLogDebug, "Destination: No tunnels in pool"); LogPrint (eLogDebug, "Destination: No tunnels in pool");
if (!floodfill || !outbound || !inbound) if (!floodfill || !outbound || !inbound)
{ {
// we can't publish now // we can't publish now
m_ExcludedFloodfills.clear (); m_ExcludedFloodfills.clear ();
m_PublishReplyToken = 1; // dummy non-zero value m_PublishReplyToken = 1; // dummy non-zero value
// try again after a while // try again after a while
LogPrint (eLogInfo, "Destination: Can't publish LeasetSet because destination is not ready. Try publishing again after ", PUBLISH_CONFIRMATION_TIMEOUT, " seconds"); LogPrint (eLogInfo, "Destination: Can't publish LeasetSet because destination is not ready. Try publishing again after ", PUBLISH_CONFIRMATION_TIMEOUT, " milliseconds");
m_PublishConfirmationTimer.expires_from_now (boost::posix_time::seconds(PUBLISH_CONFIRMATION_TIMEOUT)); m_PublishConfirmationTimer.expires_from_now (boost::posix_time::milliseconds(PUBLISH_CONFIRMATION_TIMEOUT));
m_PublishConfirmationTimer.async_wait (std::bind (&LeaseSetDestination::HandlePublishConfirmationTimer, m_PublishConfirmationTimer.async_wait (std::bind (&LeaseSetDestination::HandlePublishConfirmationTimer,
shared_from_this (), std::placeholders::_1)); shared_from_this (), std::placeholders::_1));
return; return;
@@ -666,13 +691,13 @@ namespace client
auto s = shared_from_this (); auto s = shared_from_this ();
msg->onDrop = [s]() msg->onDrop = [s]()
{ {
s->GetService ().post([s]() boost::asio::post (s->GetService (), [s]()
{ {
s->m_PublishConfirmationTimer.cancel (); s->m_PublishConfirmationTimer.cancel ();
s->HandlePublishConfirmationTimer (boost::system::error_code()); s->HandlePublishConfirmationTimer (boost::system::error_code());
}); });
}; };
m_PublishConfirmationTimer.expires_from_now (boost::posix_time::seconds(PUBLISH_CONFIRMATION_TIMEOUT)); m_PublishConfirmationTimer.expires_from_now (boost::posix_time::milliseconds(PUBLISH_CONFIRMATION_TIMEOUT));
m_PublishConfirmationTimer.async_wait (std::bind (&LeaseSetDestination::HandlePublishConfirmationTimer, m_PublishConfirmationTimer.async_wait (std::bind (&LeaseSetDestination::HandlePublishConfirmationTimer,
shared_from_this (), std::placeholders::_1)); shared_from_this (), std::placeholders::_1));
outbound->SendTunnelDataMsgTo (floodfill->GetIdentHash (), 0, msg); outbound->SendTunnelDataMsgTo (floodfill->GetIdentHash (), 0, msg);
@@ -688,15 +713,15 @@ namespace client
m_PublishReplyToken = 0; m_PublishReplyToken = 0;
if (GetIdentity ()->GetCryptoKeyType () == i2p::data::CRYPTO_KEY_TYPE_ELGAMAL) if (GetIdentity ()->GetCryptoKeyType () == i2p::data::CRYPTO_KEY_TYPE_ELGAMAL)
{ {
LogPrint (eLogWarning, "Destination: Publish confirmation was not received in ", PUBLISH_CONFIRMATION_TIMEOUT, " seconds or failed. will try again"); LogPrint (eLogWarning, "Destination: Publish confirmation was not received in ", PUBLISH_CONFIRMATION_TIMEOUT, " milliseconds or failed. will try again");
Publish (); Publish ();
} }
else else
{ {
LogPrint (eLogWarning, "Destination: Publish confirmation was not received in ", PUBLISH_CONFIRMATION_TIMEOUT, " seconds from Java floodfill for crypto type ", (int)GetIdentity ()->GetCryptoKeyType ()); LogPrint (eLogWarning, "Destination: Publish confirmation was not received in ", PUBLISH_CONFIRMATION_TIMEOUT, " milliseconds from Java floodfill for crypto type ", (int)GetIdentity ()->GetCryptoKeyType ());
// Java floodfill never sends confirmation back for unknown crypto type // Java floodfill never sends confirmation back for unknown crypto type
// assume it successive and try to verify // assume it successive and try to verify
m_PublishVerificationTimer.expires_from_now (boost::posix_time::seconds(PUBLISH_VERIFICATION_TIMEOUT)); m_PublishVerificationTimer.expires_from_now (boost::posix_time::seconds(PUBLISH_VERIFICATION_TIMEOUT + PUBLISH_VERIFICATION_TIMEOUT_VARIANCE)); // always max
m_PublishVerificationTimer.async_wait (std::bind (&LeaseSetDestination::HandlePublishVerificationTimer, m_PublishVerificationTimer.async_wait (std::bind (&LeaseSetDestination::HandlePublishVerificationTimer,
shared_from_this (), std::placeholders::_1)); shared_from_this (), std::placeholders::_1));
@@ -751,10 +776,10 @@ namespace client
if (!m_Pool || !IsReady ()) if (!m_Pool || !IsReady ())
{ {
if (requestComplete) if (requestComplete)
m_Service.post ([requestComplete](void){requestComplete (nullptr);}); boost::asio::post (m_Service, [requestComplete](void){requestComplete (nullptr);});
return false; return false;
} }
m_Service.post (std::bind (&LeaseSetDestination::RequestLeaseSet, shared_from_this (), dest, requestComplete, nullptr)); boost::asio::post (m_Service, std::bind (&LeaseSetDestination::RequestLeaseSet, shared_from_this (), dest, requestComplete, nullptr));
return true; return true;
} }
@@ -763,7 +788,7 @@ namespace client
if (!dest || !m_Pool || !IsReady ()) if (!dest || !m_Pool || !IsReady ())
{ {
if (requestComplete) if (requestComplete)
m_Service.post ([requestComplete](void){requestComplete (nullptr);}); boost::asio::post (m_Service, [requestComplete](void){requestComplete (nullptr);});
return false; return false;
} }
auto storeHash = dest->GetStoreHash (); auto storeHash = dest->GetStoreHash ();
@@ -771,17 +796,17 @@ namespace client
if (leaseSet) if (leaseSet)
{ {
if (requestComplete) if (requestComplete)
m_Service.post ([requestComplete, leaseSet](void){requestComplete (leaseSet);}); boost::asio::post (m_Service, [requestComplete, leaseSet](void){requestComplete (leaseSet);});
return true; return true;
} }
m_Service.post (std::bind (&LeaseSetDestination::RequestLeaseSet, shared_from_this (), storeHash, requestComplete, dest)); boost::asio::post (m_Service, std::bind (&LeaseSetDestination::RequestLeaseSet, shared_from_this (), storeHash, requestComplete, dest));
return true; return true;
} }
void LeaseSetDestination::CancelDestinationRequest (const i2p::data::IdentHash& dest, bool notify) void LeaseSetDestination::CancelDestinationRequest (const i2p::data::IdentHash& dest, bool notify)
{ {
auto s = shared_from_this (); auto s = shared_from_this ();
m_Service.post ([dest, notify, s](void) boost::asio::post (m_Service, [dest, notify, s](void)
{ {
auto it = s->m_LeaseSetRequests.find (dest); auto it = s->m_LeaseSetRequests.find (dest);
if (it != s->m_LeaseSetRequests.end ()) if (it != s->m_LeaseSetRequests.end ())
@@ -801,7 +826,7 @@ namespace client
void LeaseSetDestination::RequestLeaseSet (const i2p::data::IdentHash& dest, RequestComplete requestComplete, std::shared_ptr<const i2p::data::BlindedPublicKey> requestedBlindedKey) void LeaseSetDestination::RequestLeaseSet (const i2p::data::IdentHash& dest, RequestComplete requestComplete, std::shared_ptr<const i2p::data::BlindedPublicKey> requestedBlindedKey)
{ {
std::set<i2p::data::IdentHash> excluded; std::unordered_set<i2p::data::IdentHash> excluded;
auto floodfill = i2p::data::netdb.GetClosestFloodfill (dest, excluded); auto floodfill = i2p::data::netdb.GetClosestFloodfill (dest, excluded);
if (floodfill) if (floodfill)
{ {
@@ -809,7 +834,7 @@ namespace client
request->requestedBlindedKey = requestedBlindedKey; // for encrypted LeaseSet2 request->requestedBlindedKey = requestedBlindedKey; // for encrypted LeaseSet2
if (requestComplete) if (requestComplete)
request->requestComplete.push_back (requestComplete); request->requestComplete.push_back (requestComplete);
auto ts = i2p::util::GetSecondsSinceEpoch (); auto ts = i2p::util::GetMillisecondsSinceEpoch ();
auto ret = m_LeaseSetRequests.insert (std::pair<i2p::data::IdentHash, std::shared_ptr<LeaseSetRequest> >(dest,request)); auto ret = m_LeaseSetRequests.insert (std::pair<i2p::data::IdentHash, std::shared_ptr<LeaseSetRequest> >(dest,request));
if (ret.second) // inserted if (ret.second) // inserted
{ {
@@ -873,17 +898,17 @@ namespace client
AddECIESx25519Key (replyKey, replyTag); AddECIESx25519Key (replyKey, replyTag);
else else
AddSessionKey (replyKey, replyTag); AddSessionKey (replyKey, replyTag);
auto msg = WrapMessageForRouter (nextFloodfill, auto msg = WrapMessageForRouter (nextFloodfill,
CreateLeaseSetDatabaseLookupMsg (dest, request->excluded, request->replyTunnel, replyKey, replyTag, isECIES)); CreateLeaseSetDatabaseLookupMsg (dest, request->excluded, request->replyTunnel, replyKey, replyTag, isECIES));
auto s = shared_from_this (); auto s = shared_from_this ();
msg->onDrop = [s, dest, request]() msg->onDrop = [s, dest, request]()
{ {
s->GetService ().post([s, dest, request]() boost::asio::post (s->GetService (), [s, dest, request]()
{ {
s->SendNextLeaseSetRequest (dest, request); s->SendNextLeaseSetRequest (dest, request);
}); });
}; };
request->outboundTunnel->SendTunnelDataMsgs ( request->outboundTunnel->SendTunnelDataMsgs (
{ {
i2p::tunnel::TunnelMessageBlock i2p::tunnel::TunnelMessageBlock
@@ -892,7 +917,7 @@ namespace client
nextFloodfill->GetIdentHash (), 0, msg nextFloodfill->GetIdentHash (), 0, msg
} }
}); });
request->requestTimeoutTimer.expires_from_now (boost::posix_time::seconds(LEASESET_REQUEST_TIMEOUT)); request->requestTimeoutTimer.expires_from_now (boost::posix_time::milliseconds(LEASESET_REQUEST_TIMEOUT));
request->requestTimeoutTimer.async_wait (std::bind (&LeaseSetDestination::HandleRequestTimoutTimer, request->requestTimeoutTimer.async_wait (std::bind (&LeaseSetDestination::HandleRequestTimoutTimer,
shared_from_this (), std::placeholders::_1, dest)); shared_from_this (), std::placeholders::_1, dest));
} }
@@ -909,7 +934,7 @@ namespace client
if (it != m_LeaseSetRequests.end ()) if (it != m_LeaseSetRequests.end ())
{ {
bool done = false; bool done = false;
uint64_t ts = i2p::util::GetSecondsSinceEpoch (); uint64_t ts = i2p::util::GetMillisecondsSinceEpoch ();
if (ts < it->second->requestTime + MAX_LEASESET_REQUEST_TIMEOUT) if (ts < it->second->requestTime + MAX_LEASESET_REQUEST_TIMEOUT)
{ {
auto floodfill = i2p::data::netdb.GetClosestFloodfill (dest, it->second->excluded); auto floodfill = i2p::data::netdb.GetClosestFloodfill (dest, it->second->excluded);
@@ -946,7 +971,8 @@ namespace client
CleanupExpiredTags (); CleanupExpiredTags ();
CleanupRemoteLeaseSets (); CleanupRemoteLeaseSets ();
CleanupDestination (); CleanupDestination ();
m_CleanupTimer.expires_from_now (boost::posix_time::minutes (DESTINATION_CLEANUP_TIMEOUT)); m_CleanupTimer.expires_from_now (boost::posix_time::seconds (DESTINATION_CLEANUP_TIMEOUT +
(m_Pool ? m_Pool->GetRng ()() % DESTINATION_CLEANUP_TIMEOUT_VARIANCE : 0)));
m_CleanupTimer.async_wait (std::bind (&LeaseSetDestination::HandleCleanupTimer, m_CleanupTimer.async_wait (std::bind (&LeaseSetDestination::HandleCleanupTimer,
shared_from_this (), std::placeholders::_1)); shared_from_this (), std::placeholders::_1));
} }
@@ -960,7 +986,7 @@ namespace client
{ {
if (it->second->IsEmpty () || ts > it->second->GetExpirationTime ()) // leaseset expired if (it->second->IsEmpty () || ts > it->second->GetExpirationTime ()) // leaseset expired
{ {
LogPrint (eLogWarning, "Destination: Remote LeaseSet ", it->second->GetIdentHash ().ToBase64 (), " expired"); LogPrint (eLogDebug, "Destination: Remote LeaseSet ", it->second->GetIdentHash ().ToBase64 (), " expired");
it = m_RemoteLeaseSets.erase (it); it = m_RemoteLeaseSets.erase (it);
} }
else else
@@ -975,12 +1001,15 @@ namespace client
return i2p::data::CRYPTO_KEY_TYPE_ELGAMAL; return i2p::data::CRYPTO_KEY_TYPE_ELGAMAL;
} }
ClientDestination::ClientDestination (boost::asio::io_service& service, const i2p::data::PrivateKeys& keys, ClientDestination::ClientDestination (boost::asio::io_context& service, const i2p::data::PrivateKeys& keys,
bool isPublic, const std::map<std::string, std::string> * params): bool isPublic, const std::map<std::string, std::string> * params):
LeaseSetDestination (service, isPublic, params), LeaseSetDestination (service, isPublic, params),
m_Keys (keys), m_StreamingAckDelay (DEFAULT_INITIAL_ACK_DELAY), m_Keys (keys), m_StreamingAckDelay (DEFAULT_INITIAL_ACK_DELAY),
m_StreamingOutboundSpeed (DEFAULT_MAX_OUTBOUND_SPEED),
m_StreamingInboundSpeed (DEFAULT_MAX_INBOUND_SPEED),
m_StreamingMaxConcurrentStreams (DEFAULT_MAX_CONCURRENT_STREAMS),
m_IsStreamingAnswerPings (DEFAULT_ANSWER_PINGS), m_LastPort (0), m_IsStreamingAnswerPings (DEFAULT_ANSWER_PINGS), m_LastPort (0),
m_DatagramDestination (nullptr), m_RefCounter (0), m_DatagramDestination (nullptr), m_RefCounter (0), m_LastPublishedTimestamp (0),
m_ReadyChecker(service) m_ReadyChecker(service)
{ {
if (keys.IsOfflineSignature () && GetLeaseSetType () == i2p::data::NETDB_STORE_TYPE_LEASESET) if (keys.IsOfflineSignature () && GetLeaseSetType () == i2p::data::NETDB_STORE_TYPE_LEASESET)
@@ -1011,18 +1040,15 @@ namespace client
} }
} }
// if no param or valid crypto type use from identity // if no param or valid crypto type use from identity
bool isSingleKey = false;
if (encryptionKeyTypes.empty ()) if (encryptionKeyTypes.empty ())
{ encryptionKeyTypes.insert ( { GetIdentity ()->GetCryptoKeyType (),
isSingleKey = true; i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD }); // usually 0,4
encryptionKeyTypes.insert (GetIdentity ()->GetCryptoKeyType ());
}
for (auto& it: encryptionKeyTypes) for (auto& it: encryptionKeyTypes)
{ {
auto encryptionKey = new EncryptionKey (it); auto encryptionKey = new EncryptionKey (it);
if (IsPublic ()) if (IsPublic ())
PersistTemporaryKeys (encryptionKey, isSingleKey); PersistTemporaryKeys (encryptionKey);
else else
encryptionKey->GenerateKeys (); encryptionKey->GenerateKeys ();
encryptionKey->CreateDecryptor (); encryptionKey->CreateDecryptor ();
@@ -1047,6 +1073,14 @@ namespace client
auto it = params->find (I2CP_PARAM_STREAMING_INITIAL_ACK_DELAY); auto it = params->find (I2CP_PARAM_STREAMING_INITIAL_ACK_DELAY);
if (it != params->end ()) if (it != params->end ())
m_StreamingAckDelay = std::stoi(it->second); m_StreamingAckDelay = std::stoi(it->second);
it = params->find (I2CP_PARAM_STREAMING_MAX_OUTBOUND_SPEED);
if (it != params->end ())
m_StreamingOutboundSpeed = std::stoi(it->second);
it = params->find (I2CP_PARAM_STREAMING_MAX_INBOUND_SPEED);
if (it != params->end ())
m_StreamingInboundSpeed = std::stoi(it->second);
if (it != params->end ())
m_StreamingMaxConcurrentStreams = std::stoi(it->second);
it = params->find (I2CP_PARAM_STREAMING_ANSWER_PINGS); it = params->find (I2CP_PARAM_STREAMING_ANSWER_PINGS);
if (it != params->end ()) if (it != params->end ())
m_IsStreamingAnswerPings = std::stoi (it->second); // 1 for true m_IsStreamingAnswerPings = std::stoi (it->second); // 1 for true
@@ -1097,7 +1131,6 @@ namespace client
void ClientDestination::Stop () void ClientDestination::Stop ()
{ {
LogPrint(eLogDebug, "Destination: Stopping destination ", GetIdentHash().ToBase32(), ".b32.i2p"); LogPrint(eLogDebug, "Destination: Stopping destination ", GetIdentHash().ToBase32(), ".b32.i2p");
LeaseSetDestination::Stop ();
m_ReadyChecker.cancel(); m_ReadyChecker.cancel();
LogPrint(eLogDebug, "Destination: -> Stopping Streaming Destination"); LogPrint(eLogDebug, "Destination: -> Stopping Streaming Destination");
m_StreamingDestination->Stop (); m_StreamingDestination->Stop ();
@@ -1119,6 +1152,7 @@ namespace client
delete m_DatagramDestination; delete m_DatagramDestination;
m_DatagramDestination = nullptr; m_DatagramDestination = nullptr;
} }
LeaseSetDestination::Stop ();
LogPrint(eLogDebug, "Destination: -> Stopping done"); LogPrint(eLogDebug, "Destination: -> Stopping done");
} }
@@ -1182,7 +1216,7 @@ namespace client
if (leaseSet) if (leaseSet)
{ {
auto stream = CreateStream (leaseSet, port); auto stream = CreateStream (leaseSet, port);
GetService ().post ([streamRequestComplete, stream]() boost::asio::post (GetService (), [streamRequestComplete, stream]()
{ {
streamRequestComplete(stream); streamRequestComplete(stream);
}); });
@@ -1375,12 +1409,11 @@ namespace client
return ret; return ret;
} }
void ClientDestination::PersistTemporaryKeys (EncryptionKey * keys, bool isSingleKey) void ClientDestination::PersistTemporaryKeys (EncryptionKey * keys)
{ {
if (!keys) return; if (!keys) return;
std::string ident = GetIdentHash().ToBase32(); std::string ident = GetIdentHash().ToBase32();
std::string path = i2p::fs::DataDirPath("destinations", std::string path = i2p::fs::DataDirPath("destinations", ident + "." + std::to_string (keys->keyType) + ".dat");
isSingleKey ? (ident + ".dat") : (ident + "." + std::to_string (keys->keyType) + ".dat"));
std::ifstream f(path, std::ifstream::binary); std::ifstream f(path, std::ifstream::binary);
if (f) { if (f) {
@@ -1426,12 +1459,19 @@ namespace client
if (m_StandardEncryptionKey) if (m_StandardEncryptionKey)
keySections.push_back ({m_StandardEncryptionKey->keyType, (uint16_t)m_StandardEncryptionKey->decryptor->GetPublicKeyLen (), m_StandardEncryptionKey->pub} ); keySections.push_back ({m_StandardEncryptionKey->keyType, (uint16_t)m_StandardEncryptionKey->decryptor->GetPublicKeyLen (), m_StandardEncryptionKey->pub} );
auto publishedTimestamp = i2p::util::GetSecondsSinceEpoch ();
if (publishedTimestamp <= m_LastPublishedTimestamp)
{
LogPrint (eLogDebug, "Destination: LeaseSet update at the same second");
publishedTimestamp++; // force newer timestamp
}
bool isPublishedEncrypted = GetLeaseSetType () == i2p::data::NETDB_STORE_TYPE_ENCRYPTED_LEASESET2; 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, auto ls2 = std::make_shared<i2p::data::LocalLeaseSet2> (i2p::data::NETDB_STORE_TYPE_STANDARD_LEASESET2,
m_Keys, keySections, tunnels, IsPublic (), isPublishedEncrypted); m_Keys, keySections, tunnels, IsPublic (), publishedTimestamp, isPublishedEncrypted);
if (isPublishedEncrypted) // encrypt if type 5 if (isPublishedEncrypted) // encrypt if type 5
ls2 = std::make_shared<i2p::data::LocalEncryptedLeaseSet2> (ls2, m_Keys, GetAuthType (), m_AuthKeys); ls2 = std::make_shared<i2p::data::LocalEncryptedLeaseSet2> (ls2, m_Keys, GetAuthType (), m_AuthKeys);
leaseSet = ls2; leaseSet = ls2;
m_LastPublishedTimestamp = publishedTimestamp;
} }
SetLeaseSet (leaseSet); SetLeaseSet (leaseSet);
} }
@@ -1496,6 +1536,8 @@ namespace client
RunnableService ("Destination"), RunnableService ("Destination"),
ClientDestination (GetIOService (), keys, isPublic, params) ClientDestination (GetIOService (), keys, isPublic, params)
{ {
if (!GetNickname ().empty ())
RunnableService::SetName (GetNickname ());
} }
RunnableClientDestination::~RunnableClientDestination () RunnableClientDestination::~RunnableClientDestination ()

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2024, The PurpleI2P Project * Copyright (c) 2013-2025, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@@ -15,7 +15,7 @@
#include <memory> #include <memory>
#include <map> #include <map>
#include <unordered_map> #include <unordered_map>
#include <set> #include <unordered_set>
#include <string> #include <string>
#include <functional> #include <functional>
#include <boost/asio.hpp> #include <boost/asio.hpp>
@@ -36,13 +36,15 @@ namespace client
const uint8_t PROTOCOL_TYPE_STREAMING = 6; const uint8_t PROTOCOL_TYPE_STREAMING = 6;
const uint8_t PROTOCOL_TYPE_DATAGRAM = 17; const uint8_t PROTOCOL_TYPE_DATAGRAM = 17;
const uint8_t PROTOCOL_TYPE_RAW = 18; const uint8_t PROTOCOL_TYPE_RAW = 18;
const int PUBLISH_CONFIRMATION_TIMEOUT = 5; // in seconds const int PUBLISH_CONFIRMATION_TIMEOUT = 1800; // in milliseconds
const int PUBLISH_VERIFICATION_TIMEOUT = 10; // in seconds after successful publish const int PUBLISH_VERIFICATION_TIMEOUT = 5; // in seconds after successful publish
const int PUBLISH_VERIFICATION_TIMEOUT_VARIANCE = 3; // in seconds
const int PUBLISH_MIN_INTERVAL = 20; // in seconds const int PUBLISH_MIN_INTERVAL = 20; // in seconds
const int PUBLISH_REGULAR_VERIFICATION_INTERNAL = 100; // in seconds periodically const int PUBLISH_REGULAR_VERIFICATION_INTERNAL = 100; // in seconds periodically
const int LEASESET_REQUEST_TIMEOUT = 5; // in seconds const int LEASESET_REQUEST_TIMEOUT = 1600; // in milliseconds
const int MAX_LEASESET_REQUEST_TIMEOUT = 40; // in seconds const int MAX_LEASESET_REQUEST_TIMEOUT = 12000; // in milliseconds
const int DESTINATION_CLEANUP_TIMEOUT = 3; // in minutes const int DESTINATION_CLEANUP_TIMEOUT = 44; // in seconds
const int DESTINATION_CLEANUP_TIMEOUT_VARIANCE = 30; // in seconds
const unsigned int MAX_NUM_FLOODFILLS_PER_REQUEST = 7; const unsigned int MAX_NUM_FLOODFILLS_PER_REQUEST = 7;
// I2CP // I2CP
@@ -84,9 +86,19 @@ namespace client
// streaming // streaming
const char I2CP_PARAM_STREAMING_INITIAL_ACK_DELAY[] = "i2p.streaming.initialAckDelay"; const char I2CP_PARAM_STREAMING_INITIAL_ACK_DELAY[] = "i2p.streaming.initialAckDelay";
const int DEFAULT_INITIAL_ACK_DELAY = 200; // milliseconds const int DEFAULT_INITIAL_ACK_DELAY = 200; // milliseconds
const char I2CP_PARAM_STREAMING_MAX_OUTBOUND_SPEED[] = "i2p.streaming.maxOutboundSpeed"; // bytes/sec
const int DEFAULT_MAX_OUTBOUND_SPEED = 1730000000; // no more than 1.73 Gbytes/s
const char I2CP_PARAM_STREAMING_MAX_INBOUND_SPEED[] = "i2p.streaming.maxInboundSpeed"; // bytes/sec
const int DEFAULT_MAX_INBOUND_SPEED = 1730000000; // no more than 1.73 Gbytes/s
const char I2CP_PARAM_STREAMING_ANSWER_PINGS[] = "i2p.streaming.answerPings"; const char I2CP_PARAM_STREAMING_ANSWER_PINGS[] = "i2p.streaming.answerPings";
const int DEFAULT_ANSWER_PINGS = true; const int DEFAULT_ANSWER_PINGS = true;
const char I2CP_PARAM_STREAMING_PROFILE[] = "i2p.streaming.profile";
const int STREAMING_PROFILE_BULK = 1; // high bandwidth
const int STREAMING_PROFILE_INTERACTIVE = 2; // low bandwidth
const int DEFAULT_STREAMING_PROFILE = STREAMING_PROFILE_BULK;
const char I2CP_PARAM_STREAMING_MAX_CONCURRENT_STREAMS[] = "i2p.streaming.maxConcurrentStreams";
const int DEFAULT_MAX_CONCURRENT_STREAMS = 2048;
typedef std::function<void (std::shared_ptr<i2p::stream::Stream> stream)> StreamRequestComplete; typedef std::function<void (std::shared_ptr<i2p::stream::Stream> stream)> StreamRequestComplete;
class LeaseSetDestination: public i2p::garlic::GarlicDestination, class LeaseSetDestination: public i2p::garlic::GarlicDestination,
@@ -96,8 +108,8 @@ namespace client
// leaseSet = nullptr means not found // leaseSet = nullptr means not found
struct LeaseSetRequest struct LeaseSetRequest
{ {
LeaseSetRequest (boost::asio::io_service& service): requestTime (0), requestTimeoutTimer (service) {}; LeaseSetRequest (boost::asio::io_context& service): requestTime (0), requestTimeoutTimer (service) {};
std::set<i2p::data::IdentHash> excluded; std::unordered_set<i2p::data::IdentHash> excluded;
uint64_t requestTime; uint64_t requestTime;
boost::asio::deadline_timer requestTimeoutTimer; boost::asio::deadline_timer requestTimeoutTimer;
std::list<RequestComplete> requestComplete; std::list<RequestComplete> requestComplete;
@@ -114,10 +126,10 @@ namespace client
public: public:
LeaseSetDestination (boost::asio::io_service& service, bool isPublic, const std::map<std::string, std::string> * params = nullptr); LeaseSetDestination (boost::asio::io_context& service, bool isPublic, const std::map<std::string, std::string> * params = nullptr);
~LeaseSetDestination (); ~LeaseSetDestination ();
const std::string& GetNickname () const { return m_Nickname; }; const std::string& GetNickname () const { return m_Nickname; };
boost::asio::io_service& GetService () { return m_Service; }; auto& GetService () { return m_Service; };
virtual void Start (); virtual void Start ();
virtual void Stop (); virtual void Stop ();
@@ -134,15 +146,15 @@ namespace client
void CancelDestinationRequestWithEncryptedLeaseSet (std::shared_ptr<const i2p::data::BlindedPublicKey> dest, bool notify = true); void CancelDestinationRequestWithEncryptedLeaseSet (std::shared_ptr<const i2p::data::BlindedPublicKey> dest, bool notify = true);
// implements GarlicDestination // implements GarlicDestination
std::shared_ptr<const i2p::data::LocalLeaseSet> GetLeaseSet (); std::shared_ptr<const i2p::data::LocalLeaseSet> GetLeaseSet () override;
std::shared_ptr<i2p::tunnel::TunnelPool> GetTunnelPool () const { return m_Pool; } std::shared_ptr<i2p::tunnel::TunnelPool> GetTunnelPool () const override { return m_Pool; }
// override GarlicDestination // override GarlicDestination
bool SubmitSessionKey (const uint8_t * key, const uint8_t * tag); bool SubmitSessionKey (const uint8_t * key, const uint8_t * tag) override;
void SubmitECIESx25519Key (const uint8_t * key, uint64_t tag); void SubmitECIESx25519Key (const uint8_t * key, uint64_t tag) override;
void ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg); void ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg) override;
void ProcessDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg); void ProcessDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg) override;
void SetLeaseSetUpdated (); void SetLeaseSetUpdated (bool post) override;
bool IsPublic () const { return m_IsPublic; }; bool IsPublic () const { return m_IsPublic; };
void SetPublic (bool pub) { m_IsPublic = pub; }; void SetPublic (bool pub) { m_IsPublic = pub; };
@@ -150,8 +162,8 @@ namespace client
protected: protected:
// implements GarlicDestination // implements GarlicDestination
void HandleI2NPMessage (const uint8_t * buf, size_t len); void HandleI2NPMessage (const uint8_t * buf, size_t len) override;
bool HandleCloveI2NPMessage (I2NPMessageType typeID, const uint8_t * payload, size_t len, uint32_t msgID); bool HandleCloveI2NPMessage (I2NPMessageType typeID, const uint8_t * payload, size_t len, uint32_t msgID) override;
void SetLeaseSet (std::shared_ptr<const i2p::data::LocalLeaseSet> newLeaseSet); void SetLeaseSet (std::shared_ptr<const i2p::data::LocalLeaseSet> newLeaseSet);
int GetLeaseSetType () const { return m_LeaseSetType; }; int GetLeaseSetType () const { return m_LeaseSetType; };
@@ -184,18 +196,21 @@ namespace client
private: private:
boost::asio::io_service& m_Service; boost::asio::io_context& m_Service;
mutable std::mutex m_RemoteLeaseSetsMutex; mutable std::mutex m_RemoteLeaseSetsMutex;
std::unordered_map<i2p::data::IdentHash, std::shared_ptr<i2p::data::LeaseSet> > m_RemoteLeaseSets; std::unordered_map<i2p::data::IdentHash, std::shared_ptr<i2p::data::LeaseSet> > m_RemoteLeaseSets;
std::unordered_map<i2p::data::IdentHash, std::shared_ptr<LeaseSetRequest> > m_LeaseSetRequests; std::unordered_map<i2p::data::IdentHash, std::shared_ptr<LeaseSetRequest> > m_LeaseSetRequests;
std::list<std::shared_ptr<I2NPMessage> > m_IncomingMsgsQueue;
mutable std::mutex m_IncomingMsgsQueueMutex;
std::shared_ptr<i2p::tunnel::TunnelPool> m_Pool; std::shared_ptr<i2p::tunnel::TunnelPool> m_Pool;
std::mutex m_LeaseSetMutex; std::mutex m_LeaseSetMutex;
std::shared_ptr<const i2p::data::LocalLeaseSet> m_LeaseSet; std::shared_ptr<const i2p::data::LocalLeaseSet> m_LeaseSet;
bool m_IsPublic; bool m_IsPublic;
uint32_t m_PublishReplyToken; uint32_t m_PublishReplyToken;
uint64_t m_LastSubmissionTime; // in seconds uint64_t m_LastSubmissionTime; // in seconds
std::set<i2p::data::IdentHash> m_ExcludedFloodfills; // for publishing std::unordered_set<i2p::data::IdentHash> m_ExcludedFloodfills; // for publishing
boost::asio::deadline_timer m_PublishConfirmationTimer, m_PublishVerificationTimer, boost::asio::deadline_timer m_PublishConfirmationTimer, m_PublishVerificationTimer,
m_PublishDelayTimer, m_CleanupTimer; m_PublishDelayTimer, m_CleanupTimer;
@@ -227,7 +242,7 @@ namespace client
public: public:
ClientDestination (boost::asio::io_service& service, const i2p::data::PrivateKeys& keys, ClientDestination (boost::asio::io_context& service, const i2p::data::PrivateKeys& keys,
bool isPublic, const std::map<std::string, std::string> * params = nullptr); bool isPublic, const std::map<std::string, std::string> * params = nullptr);
~ClientDestination (); ~ClientDestination ();
@@ -259,6 +274,9 @@ namespace client
bool IsAcceptingStreams () const; bool IsAcceptingStreams () const;
void AcceptOnce (const i2p::stream::StreamingDestination::Acceptor& acceptor); void AcceptOnce (const i2p::stream::StreamingDestination::Acceptor& acceptor);
int GetStreamingAckDelay () const { return m_StreamingAckDelay; } int GetStreamingAckDelay () const { return m_StreamingAckDelay; }
int GetStreamingOutboundSpeed () const { return m_StreamingOutboundSpeed; }
int GetStreamingInboundSpeed () const { return m_StreamingInboundSpeed; }
int GetStreamingMaxConcurrentStreams () const { return m_StreamingMaxConcurrentStreams; }
bool IsStreamingAnswerPings () const { return m_IsStreamingAnswerPings; } bool IsStreamingAnswerPings () const { return m_IsStreamingAnswerPings; }
// datagram // datagram
@@ -283,7 +301,7 @@ namespace client
std::shared_ptr<ClientDestination> GetSharedFromThis () { std::shared_ptr<ClientDestination> GetSharedFromThis () {
return std::static_pointer_cast<ClientDestination>(shared_from_this ()); return std::static_pointer_cast<ClientDestination>(shared_from_this ());
} }
void PersistTemporaryKeys (EncryptionKey * keys, bool isSingleKey); void PersistTemporaryKeys (EncryptionKey * keys);
void ReadAuthKey (const std::string& group, const std::map<std::string, std::string> * params); void ReadAuthKey (const std::string& group, const std::map<std::string, std::string> * params);
template<typename Dest> template<typename Dest>
@@ -295,13 +313,14 @@ namespace client
std::unique_ptr<EncryptionKey> m_StandardEncryptionKey; std::unique_ptr<EncryptionKey> m_StandardEncryptionKey;
std::unique_ptr<EncryptionKey> m_ECIESx25519EncryptionKey; std::unique_ptr<EncryptionKey> m_ECIESx25519EncryptionKey;
int m_StreamingAckDelay; int m_StreamingAckDelay,m_StreamingOutboundSpeed, m_StreamingInboundSpeed, m_StreamingMaxConcurrentStreams;
bool m_IsStreamingAnswerPings; bool m_IsStreamingAnswerPings;
std::shared_ptr<i2p::stream::StreamingDestination> m_StreamingDestination; // default std::shared_ptr<i2p::stream::StreamingDestination> m_StreamingDestination; // default
std::map<uint16_t, std::shared_ptr<i2p::stream::StreamingDestination> > m_StreamingDestinationsByPorts; std::map<uint16_t, std::shared_ptr<i2p::stream::StreamingDestination> > m_StreamingDestinationsByPorts;
std::shared_ptr<i2p::stream::StreamingDestination> m_LastStreamingDestination; uint16_t m_LastPort; // for server tunnels std::shared_ptr<i2p::stream::StreamingDestination> m_LastStreamingDestination; uint16_t m_LastPort; // for server tunnels
i2p::datagram::DatagramDestination * m_DatagramDestination; i2p::datagram::DatagramDestination * m_DatagramDestination;
int m_RefCounter; // how many clients(tunnels) use this destination int m_RefCounter; // how many clients(tunnels) use this destination
uint64_t m_LastPublishedTimestamp;
boost::asio::deadline_timer m_ReadyChecker; boost::asio::deadline_timer m_ReadyChecker;

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2024, The PurpleI2P Project * Copyright (c) 2013-2025, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@@ -229,6 +229,29 @@ namespace garlic
tagsetNsr->NextSessionTagRatchet (); tagsetNsr->NextSessionTagRatchet ();
} }
bool ECIESX25519AEADRatchetSession::MessageConfirmed (uint32_t msgID)
{
auto ret = GarlicRoutingSession::MessageConfirmed (msgID); // LeaseSet
if (m_AckRequestMsgID && m_AckRequestMsgID == msgID)
{
m_AckRequestMsgID = 0;
m_AckRequestNumAttempts = 0;
ret = true;
}
return ret;
}
bool ECIESX25519AEADRatchetSession::CleanupUnconfirmedTags ()
{
if (m_AckRequestMsgID && m_AckRequestNumAttempts > ECIESX25519_ACK_REQUEST_MAX_NUM_ATTEMPTS)
{
m_AckRequestMsgID = 0;
m_AckRequestNumAttempts = 0;
return true;
}
return false;
}
bool ECIESX25519AEADRatchetSession::HandleNewIncomingSession (const uint8_t * buf, size_t len) bool ECIESX25519AEADRatchetSession::HandleNewIncomingSession (const uint8_t * buf, size_t len)
{ {
if (!GetOwner ()) return false; if (!GetOwner ()) return false;
@@ -333,8 +356,9 @@ namespace garlic
auto offset1 = offset; auto offset1 = offset;
for (auto i = 0; i < numAcks; i++) for (auto i = 0; i < numAcks; i++)
{ {
offset1 += 2; // tagsetid uint32_t tagsetid = bufbe16toh (buf + offset1); offset1 += 2; // tagsetid
MessageConfirmed (bufbe16toh (buf + offset1)); offset1 += 2; // N uint16_t n = bufbe16toh (buf + offset1); offset1 += 2; // N
MessageConfirmed ((tagsetid << 16) + n); // msgid = (tagsetid << 16) + N
} }
break; break;
} }
@@ -397,7 +421,6 @@ namespace garlic
{ {
uint16_t keyID = bufbe16toh (buf); buf += 2; // keyID uint16_t keyID = bufbe16toh (buf); buf += 2; // keyID
bool newKey = flag & ECIESX25519_NEXT_KEY_REQUEST_REVERSE_KEY_FLAG; bool newKey = flag & ECIESX25519_NEXT_KEY_REQUEST_REVERSE_KEY_FLAG;
m_SendReverseKey = true;
if (!m_NextReceiveRatchet) if (!m_NextReceiveRatchet)
m_NextReceiveRatchet.reset (new DHRatchet ()); m_NextReceiveRatchet.reset (new DHRatchet ());
else else
@@ -409,15 +432,14 @@ namespace garlic
} }
m_NextReceiveRatchet->keyID = keyID; m_NextReceiveRatchet->keyID = keyID;
} }
int tagsetID = 2*keyID;
if (newKey) if (newKey)
{ {
m_NextReceiveRatchet->key = i2p::transport::transports.GetNextX25519KeysPair (); m_NextReceiveRatchet->key = i2p::transport::transports.GetNextX25519KeysPair ();
m_NextReceiveRatchet->newKey = true; m_NextReceiveRatchet->newKey = true;
tagsetID++;
} }
else else
m_NextReceiveRatchet->newKey = false; m_NextReceiveRatchet->newKey = false;
auto tagsetID = m_NextReceiveRatchet->GetReceiveTagSetID ();
if (flag & ECIESX25519_NEXT_KEY_KEY_PRESENT_FLAG) if (flag & ECIESX25519_NEXT_KEY_KEY_PRESENT_FLAG)
memcpy (m_NextReceiveRatchet->remote, buf, 32); memcpy (m_NextReceiveRatchet->remote, buf, 32);
@@ -431,7 +453,9 @@ namespace garlic
GenerateMoreReceiveTags (newTagset, (GetOwner () && GetOwner ()->GetNumRatchetInboundTags () > 0) ? GenerateMoreReceiveTags (newTagset, (GetOwner () && GetOwner ()->GetNumRatchetInboundTags () > 0) ?
GetOwner ()->GetNumRatchetInboundTags () : ECIESX25519_MAX_NUM_GENERATED_TAGS); GetOwner ()->GetNumRatchetInboundTags () : ECIESX25519_MAX_NUM_GENERATED_TAGS);
receiveTagset->Expire (); receiveTagset->Expire ();
LogPrint (eLogDebug, "Garlic: Next receive tagset ", tagsetID, " created"); LogPrint (eLogDebug, "Garlic: Next receive tagset ", tagsetID, " created");
m_SendReverseKey = true;
} }
} }
@@ -701,6 +725,8 @@ namespace garlic
bool ECIESX25519AEADRatchetSession::NewExistingSessionMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen) bool ECIESX25519AEADRatchetSession::NewExistingSessionMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen)
{ {
auto owner = GetOwner ();
if (!owner) return false;
uint8_t nonce[12]; uint8_t nonce[12];
auto index = m_SendTagset->GetNextIndex (); auto index = m_SendTagset->GetNextIndex ();
CreateNonce (index, nonce); // tag's index CreateNonce (index, nonce); // tag's index
@@ -708,8 +734,7 @@ namespace garlic
if (!tag) if (!tag)
{ {
LogPrint (eLogError, "Garlic: Can't create new ECIES-X25519-AEAD-Ratchet tag for send tagset"); LogPrint (eLogError, "Garlic: Can't create new ECIES-X25519-AEAD-Ratchet tag for send tagset");
if (GetOwner ()) owner->RemoveECIESx25519Session (m_RemoteStaticKey);
GetOwner ()->RemoveECIESx25519Session (m_RemoteStaticKey);
return false; return false;
} }
memcpy (out, &tag, 8); memcpy (out, &tag, 8);
@@ -717,7 +742,7 @@ namespace garlic
// ciphertext = ENCRYPT(k, n, payload, ad) // ciphertext = ENCRYPT(k, n, payload, ad)
uint8_t key[32]; uint8_t key[32];
m_SendTagset->GetSymmKey (index, key); m_SendTagset->GetSymmKey (index, key);
if (!i2p::crypto::AEADChaCha20Poly1305 (payload, len, out, 8, key, nonce, out + 8, outLen - 8, true)) // encrypt if (!owner->AEADChaCha20Poly1305Encrypt (payload, len, out, 8, key, nonce, out + 8, outLen - 8))
{ {
LogPrint (eLogWarning, "Garlic: Payload section AEAD encryption failed"); LogPrint (eLogWarning, "Garlic: Payload section AEAD encryption failed");
return false; return false;
@@ -736,33 +761,35 @@ namespace garlic
uint8_t * payload = buf + 8; uint8_t * payload = buf + 8;
uint8_t key[32]; uint8_t key[32];
receiveTagset->GetSymmKey (index, key); receiveTagset->GetSymmKey (index, key);
if (!i2p::crypto::AEADChaCha20Poly1305 (payload, len - 16, buf, 8, key, nonce, payload, len - 16, false)) // decrypt auto owner = GetOwner ();
if (!owner) return true; // drop message
if (!owner->AEADChaCha20Poly1305Decrypt (payload, len - 16, buf, 8, key, nonce, payload, len - 16))
{ {
LogPrint (eLogWarning, "Garlic: Payload section AEAD decryption failed"); LogPrint (eLogWarning, "Garlic: Payload section AEAD decryption failed");
return false; return false;
} }
HandlePayload (payload, len - 16, receiveTagset, index); HandlePayload (payload, len - 16, receiveTagset, index);
if (GetOwner ())
int moreTags = 0;
if (owner->GetNumRatchetInboundTags () > 0) // override in settings?
{ {
int moreTags = 0; if (receiveTagset->GetNextIndex () - index < owner->GetNumRatchetInboundTags ()/2)
if (GetOwner ()->GetNumRatchetInboundTags () > 0) // override in settings? moreTags = owner->GetNumRatchetInboundTags ();
{ index -= owner->GetNumRatchetInboundTags (); // trim behind
if (receiveTagset->GetNextIndex () - index < GetOwner ()->GetNumRatchetInboundTags ()/2)
moreTags = GetOwner ()->GetNumRatchetInboundTags ();
index -= GetOwner ()->GetNumRatchetInboundTags (); // trim behind
}
else
{
moreTags = ECIESX25519_MIN_NUM_GENERATED_TAGS + (index >> 2); // N/4
if (moreTags > ECIESX25519_MAX_NUM_GENERATED_TAGS) moreTags = ECIESX25519_MAX_NUM_GENERATED_TAGS;
moreTags -= (receiveTagset->GetNextIndex () - index);
index -= ECIESX25519_MAX_NUM_GENERATED_TAGS; // trim behind
}
if (moreTags > 0)
GenerateMoreReceiveTags (receiveTagset, moreTags);
if (index > 0)
receiveTagset->SetTrimBehind (index);
} }
else
{
moreTags = (receiveTagset->GetTagSetID () > 0) ? ECIESX25519_MAX_NUM_GENERATED_TAGS : // for non first tagset
(ECIESX25519_MIN_NUM_GENERATED_TAGS + (index >> 1)); // N/2
if (moreTags > ECIESX25519_MAX_NUM_GENERATED_TAGS) moreTags = ECIESX25519_MAX_NUM_GENERATED_TAGS;
moreTags -= (receiveTagset->GetNextIndex () - index);
index -= ECIESX25519_MAX_NUM_GENERATED_TAGS; // trim behind
}
if (moreTags > 0)
GenerateMoreReceiveTags (receiveTagset, moreTags);
if (index > 0)
receiveTagset->SetTrimBehind (index);
return true; return true;
} }
@@ -776,10 +803,10 @@ namespace garlic
m_State = eSessionStateEstablished; m_State = eSessionStateEstablished;
m_NSRSendTagset = nullptr; m_NSRSendTagset = nullptr;
m_EphemeralKeys = nullptr; m_EphemeralKeys = nullptr;
#if (__cplusplus >= 201703L) // C++ 17 or higher
[[fallthrough]]; [[fallthrough]];
#endif
case eSessionStateEstablished: case eSessionStateEstablished:
if (m_SendReverseKey && receiveTagset->GetTagSetID () == m_NextReceiveRatchet->GetReceiveTagSetID ())
m_SendReverseKey = false; // tag received on new tagset
if (receiveTagset->IsNS ()) if (receiveTagset->IsNS ())
{ {
// our of sequence NSR // our of sequence NSR
@@ -857,6 +884,7 @@ namespace garlic
{ {
uint64_t ts = i2p::util::GetMillisecondsSinceEpoch (); uint64_t ts = i2p::util::GetMillisecondsSinceEpoch ();
size_t payloadLen = 0; size_t payloadLen = 0;
bool sendAckRequest = false;
if (first) payloadLen += 7;// datatime if (first) payloadLen += 7;// datatime
if (msg) if (msg)
{ {
@@ -875,13 +903,28 @@ namespace garlic
payloadLen += leaseSet->GetBufferLen () + DATABASE_STORE_HEADER_SIZE + 13; payloadLen += leaseSet->GetBufferLen () + DATABASE_STORE_HEADER_SIZE + 13;
if (!first) if (!first)
{ {
// ack request // ack request for LeaseSet
m_AckRequestMsgID = m_SendTagset->GetMsgID ();
sendAckRequest = true;
// update LeaseSet status
SetLeaseSetUpdateStatus (eLeaseSetSubmitted); SetLeaseSetUpdateStatus (eLeaseSetSubmitted);
SetLeaseSetUpdateMsgID (m_SendTagset->GetNextIndex ()); SetLeaseSetUpdateMsgID (m_AckRequestMsgID);
SetLeaseSetSubmissionTime (ts); SetLeaseSetSubmissionTime (ts);
payloadLen += 4;
} }
} }
if (!sendAckRequest && !first &&
((!m_AckRequestMsgID && ts > m_LastAckRequestSendTime + m_AckRequestInterval) || // regular request
(m_AckRequestMsgID && ts > m_LastAckRequestSendTime + LEASESET_CONFIRMATION_TIMEOUT))) // previous request failed. try again
{
// not LeaseSet
m_AckRequestMsgID = m_SendTagset->GetMsgID ();
if (m_AckRequestMsgID)
{
m_AckRequestNumAttempts++;
sendAckRequest = true;
}
}
if (sendAckRequest) payloadLen += 4;
if (m_AckRequests.size () > 0) if (m_AckRequests.size () > 0)
payloadLen += m_AckRequests.size ()*4 + 3; payloadLen += m_AckRequests.size ()*4 + 3;
if (m_SendReverseKey) if (m_SendReverseKey)
@@ -933,16 +976,15 @@ namespace garlic
} }
// LeaseSet // LeaseSet
if (leaseSet) if (leaseSet)
{
offset += CreateLeaseSetClove (leaseSet, ts, payload + offset, payloadLen - offset); offset += CreateLeaseSetClove (leaseSet, ts, payload + offset, payloadLen - offset);
if (!first) // ack request
{ if (sendAckRequest)
// ack request {
payload[offset] = eECIESx25519BlkAckRequest; offset++; payload[offset] = eECIESx25519BlkAckRequest; offset++;
htobe16buf (payload + offset, 1); offset += 2; htobe16buf (payload + offset, 1); offset += 2;
payload[offset] = 0; offset++; // flags payload[offset] = 0; offset++; // flags
} m_LastAckRequestSendTime = ts;
} }
// msg // msg
if (msg) if (msg)
offset += CreateGarlicClove (msg, payload + offset, payloadLen - offset); offset += CreateGarlicClove (msg, payload + offset, payloadLen - offset);
@@ -977,7 +1019,6 @@ namespace garlic
memcpy (payload + offset, m_NextReceiveRatchet->key->GetPublicKey (), 32); memcpy (payload + offset, m_NextReceiveRatchet->key->GetPublicKey (), 32);
offset += 32; // public key offset += 32; // public key
} }
m_SendReverseKey = false;
} }
if (m_SendForwardKey) if (m_SendForwardKey)
{ {
@@ -1073,6 +1114,8 @@ namespace garlic
bool ECIESX25519AEADRatchetSession::CheckExpired (uint64_t ts) bool ECIESX25519AEADRatchetSession::CheckExpired (uint64_t ts)
{ {
CleanupUnconfirmedLeaseSet (ts); CleanupUnconfirmedLeaseSet (ts);
if (!m_Destination && ts > m_LastActivityTimestamp + ECIESX25519_SESSION_CREATE_TIMEOUT) return true; // m_LastActivityTimestamp is NS receive time
if (m_State != eSessionStateEstablished && m_SessionCreatedTimestamp && ts > m_SessionCreatedTimestamp + ECIESX25519_SESSION_ESTABLISH_TIMEOUT) return true;
return ts > m_LastActivityTimestamp + ECIESX25519_RECEIVE_EXPIRATION_TIMEOUT && // seconds return ts > m_LastActivityTimestamp + ECIESX25519_RECEIVE_EXPIRATION_TIMEOUT && // seconds
ts*1000 > m_LastSentTimestamp + ECIESX25519_SEND_EXPIRATION_TIMEOUT*1000; // milliseconds ts*1000 > m_LastSentTimestamp + ECIESX25519_SEND_EXPIRATION_TIMEOUT*1000; // milliseconds
} }

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2024, The PurpleI2P Project * Copyright (c) 2013-2025, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@@ -30,10 +30,14 @@ namespace garlic
const int ECIESX25519_SEND_INACTIVITY_TIMEOUT = 5000; // number of milliseconds we can send empty(pyaload only) packet after const int ECIESX25519_SEND_INACTIVITY_TIMEOUT = 5000; // number of milliseconds we can send empty(pyaload only) packet after
const int ECIESX25519_SEND_EXPIRATION_TIMEOUT = 480; // in seconds const int ECIESX25519_SEND_EXPIRATION_TIMEOUT = 480; // in seconds
const int ECIESX25519_RECEIVE_EXPIRATION_TIMEOUT = 600; // in seconds const int ECIESX25519_RECEIVE_EXPIRATION_TIMEOUT = 600; // in seconds
const int ECIESX25519_PREVIOUS_TAGSET_EXPIRATION_TIMEOUT = 180; // 180 const int ECIESX25519_SESSION_CREATE_TIMEOUT = 3; // in seconds, NSR must be send after NS received
const int ECIESX25519_SESSION_ESTABLISH_TIMEOUT = 15; // in seconds
const int ECIESX25519_PREVIOUS_TAGSET_EXPIRATION_TIMEOUT = 180; // in seconds
const int ECIESX25519_DEFAULT_ACK_REQUEST_INTERVAL = 33000; // in milliseconds
const int ECIESX25519_ACK_REQUEST_MAX_NUM_ATTEMPTS = 3;
const int ECIESX25519_TAGSET_MAX_NUM_TAGS = 8192; // number of tags we request new tagset after const int ECIESX25519_TAGSET_MAX_NUM_TAGS = 8192; // number of tags we request new tagset after
const int ECIESX25519_MIN_NUM_GENERATED_TAGS = 24; const int ECIESX25519_MIN_NUM_GENERATED_TAGS = 24;
const int ECIESX25519_MAX_NUM_GENERATED_TAGS = 320; const int ECIESX25519_MAX_NUM_GENERATED_TAGS = 800;
const int ECIESX25519_NSR_NUM_GENERATED_TAGS = 12; const int ECIESX25519_NSR_NUM_GENERATED_TAGS = 12;
const size_t ECIESX25519_OPTIMAL_PAYLOAD_SIZE = 1912; // 1912 = 1956 /* to fit 2 tunnel messages */ const size_t ECIESX25519_OPTIMAL_PAYLOAD_SIZE = 1912; // 1912 = 1956 /* to fit 2 tunnel messages */
@@ -57,6 +61,8 @@ namespace garlic
int GetTagSetID () const { return m_TagSetID; }; int GetTagSetID () const { return m_TagSetID; };
void SetTagSetID (int tagsetID) { m_TagSetID = tagsetID; }; void SetTagSetID (int tagsetID) { m_TagSetID = tagsetID; };
uint32_t GetMsgID () const { return (m_TagSetID << 16) + m_NextIndex; }; // (tagsetid << 16) + N
private: private:
i2p::data::Tag<64> m_SessionTagKeyData; i2p::data::Tag<64> m_SessionTagKeyData;
@@ -149,6 +155,7 @@ namespace garlic
std::shared_ptr<i2p::crypto::X25519Keys> key; std::shared_ptr<i2p::crypto::X25519Keys> key;
uint8_t remote[32]; // last remote public key uint8_t remote[32]; // last remote public key
bool newKey = true; bool newKey = true;
int GetReceiveTagSetID () const { return newKey ? (2*keyID + 1) : 2*keyID; }
}; };
public: public:
@@ -157,14 +164,14 @@ namespace garlic
~ECIESX25519AEADRatchetSession (); ~ECIESX25519AEADRatchetSession ();
bool HandleNextMessage (uint8_t * buf, size_t len, std::shared_ptr<ReceiveRatchetTagSet> receiveTagset, int index = 0); bool HandleNextMessage (uint8_t * buf, size_t len, std::shared_ptr<ReceiveRatchetTagSet> receiveTagset, int index = 0);
std::shared_ptr<I2NPMessage> WrapSingleMessage (std::shared_ptr<const I2NPMessage> msg); std::shared_ptr<I2NPMessage> WrapSingleMessage (std::shared_ptr<const I2NPMessage> msg) override;
std::shared_ptr<I2NPMessage> WrapOneTimeMessage (std::shared_ptr<const I2NPMessage> msg); std::shared_ptr<I2NPMessage> WrapOneTimeMessage (std::shared_ptr<const I2NPMessage> msg);
const uint8_t * GetRemoteStaticKey () const { return m_RemoteStaticKey; } const uint8_t * GetRemoteStaticKey () const { return m_RemoteStaticKey; }
void SetRemoteStaticKey (const uint8_t * key) { memcpy (m_RemoteStaticKey, key, 32); } void SetRemoteStaticKey (const uint8_t * key) { memcpy (m_RemoteStaticKey, key, 32); }
void Terminate () { m_IsTerminated = true; } void Terminate () { m_IsTerminated = true; }
void SetDestination (const i2p::data::IdentHash& dest) // TODO: void SetDestination (const i2p::data::IdentHash& dest)
{ {
if (!m_Destination) m_Destination.reset (new i2p::data::IdentHash (dest)); if (!m_Destination) m_Destination.reset (new i2p::data::IdentHash (dest));
} }
@@ -173,18 +180,21 @@ namespace garlic
bool CanBeRestarted (uint64_t ts) const { return ts > m_SessionCreatedTimestamp + ECIESX25519_RESTART_TIMEOUT; } bool CanBeRestarted (uint64_t ts) const { return ts > m_SessionCreatedTimestamp + ECIESX25519_RESTART_TIMEOUT; }
bool IsInactive (uint64_t ts) const { return ts > m_LastActivityTimestamp + ECIESX25519_INACTIVITY_TIMEOUT && CanBeRestarted (ts); } bool IsInactive (uint64_t ts) const { return ts > m_LastActivityTimestamp + ECIESX25519_INACTIVITY_TIMEOUT && CanBeRestarted (ts); }
bool IsRatchets () const { return true; }; bool IsRatchets () const override { return true; };
bool IsReadyToSend () const { return m_State != eSessionStateNewSessionSent; }; bool IsReadyToSend () const override { return m_State != eSessionStateNewSessionSent; };
bool IsTerminated () const { return m_IsTerminated; } bool IsTerminated () const override { return m_IsTerminated; }
uint64_t GetLastActivityTimestamp () const { return m_LastActivityTimestamp; }; uint64_t GetLastActivityTimestamp () const override { return m_LastActivityTimestamp; };
void SetAckRequestInterval (int interval) override { m_AckRequestInterval = interval; };
bool CleanupUnconfirmedTags () override; // return true if unaswered Ack requests, called from I2CP
protected: protected:
i2p::crypto::NoiseSymmetricState& GetNoiseState () { return *this; }; i2p::crypto::NoiseSymmetricState& GetNoiseState () { return *this; };
void SetNoiseState (const i2p::crypto::NoiseSymmetricState& state) { GetNoiseState () = state; }; void SetNoiseState (const i2p::crypto::NoiseSymmetricState& state) { GetNoiseState () = state; };
void CreateNonce (uint64_t seqn, uint8_t * nonce); void CreateNonce (uint64_t seqn, uint8_t * nonce);
void HandlePayload (const uint8_t * buf, size_t len, const std::shared_ptr<ReceiveRatchetTagSet>& receiveTagset, int index); void HandlePayload (const uint8_t * buf, size_t len, const std::shared_ptr<ReceiveRatchetTagSet>& receiveTagset, int index);
bool MessageConfirmed (uint32_t msgID) override;
private: private:
bool GenerateEphemeralKeysAndEncode (uint8_t * buf); // buf is 32 bytes bool GenerateEphemeralKeysAndEncode (uint8_t * buf); // buf is 32 bytes
@@ -217,12 +227,17 @@ namespace garlic
uint64_t m_SessionCreatedTimestamp = 0, m_LastActivityTimestamp = 0, // incoming (in seconds) uint64_t m_SessionCreatedTimestamp = 0, m_LastActivityTimestamp = 0, // incoming (in seconds)
m_LastSentTimestamp = 0; // in milliseconds m_LastSentTimestamp = 0; // in milliseconds
std::shared_ptr<RatchetTagSet> m_SendTagset, m_NSRSendTagset; std::shared_ptr<RatchetTagSet> m_SendTagset, m_NSRSendTagset;
std::unique_ptr<i2p::data::IdentHash> m_Destination;// TODO: might not need it std::unique_ptr<i2p::data::IdentHash> m_Destination;// must be set for NS if outgoing and NSR if incoming
std::list<std::pair<uint16_t, int> > m_AckRequests; // (tagsetid, index) std::list<std::pair<uint16_t, int> > m_AckRequests; // incoming (tagsetid, index)
bool m_SendReverseKey = false, m_SendForwardKey = false, m_IsTerminated = false; bool m_SendReverseKey = false, m_SendForwardKey = false, m_IsTerminated = false;
std::unique_ptr<DHRatchet> m_NextReceiveRatchet, m_NextSendRatchet; std::unique_ptr<DHRatchet> m_NextReceiveRatchet, m_NextSendRatchet;
uint8_t m_PaddingSizes[32], m_NextPaddingSize; uint8_t m_PaddingSizes[32], m_NextPaddingSize;
uint64_t m_LastAckRequestSendTime = 0; // milliseconds
uint32_t m_AckRequestMsgID = 0;
int m_AckRequestNumAttempts = 0;
int m_AckRequestInterval = ECIESX25519_DEFAULT_ACK_REQUEST_INTERVAL; // milliseconds
public: public:
// for HTTP only // for HTTP only

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2023, The PurpleI2P Project * Copyright (c) 2013-2024, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@@ -457,86 +457,6 @@ namespace crypto
} }
} }
#if !OPENSSL_X25519
BIGNUM * Ed25519::ScalarMul (const BIGNUM * u, const BIGNUM * k, BN_CTX * ctx) const
{
BN_CTX_start (ctx);
auto x1 = BN_CTX_get (ctx); BN_copy (x1, u);
auto x2 = BN_CTX_get (ctx); BN_one (x2);
auto z2 = BN_CTX_get (ctx); BN_zero (z2);
auto x3 = BN_CTX_get (ctx); BN_copy (x3, u);
auto z3 = BN_CTX_get (ctx); BN_one (z3);
auto c121666 = BN_CTX_get (ctx); BN_set_word (c121666, 121666);
auto tmp0 = BN_CTX_get (ctx); auto tmp1 = BN_CTX_get (ctx);
unsigned int swap = 0;
auto bits = BN_num_bits (k);
while(bits)
{
--bits;
auto k_t = BN_is_bit_set(k, bits) ? 1 : 0;
swap ^= k_t;
if (swap)
{
std::swap (x2, x3);
std::swap (z2, z3);
}
swap = k_t;
BN_mod_sub(tmp0, x3, z3, q, ctx);
BN_mod_sub(tmp1, x2, z2, q, ctx);
BN_mod_add(x2, x2, z2, q, ctx);
BN_mod_add(z2, x3, z3, q, ctx);
BN_mod_mul(z3, tmp0, x2, q, ctx);
BN_mod_mul(z2, z2, tmp1, q, ctx);
BN_mod_sqr(tmp0, tmp1, q, ctx);
BN_mod_sqr(tmp1, x2, q, ctx);
BN_mod_add(x3, z3, z2, q, ctx);
BN_mod_sub(z2, z3, z2, q, ctx);
BN_mod_mul(x2, tmp1, tmp0, q, ctx);
BN_mod_sub(tmp1, tmp1, tmp0, q, ctx);
BN_mod_sqr(z2, z2, q, ctx);
BN_mod_mul(z3, tmp1, c121666, q, ctx);
BN_mod_sqr(x3, x3, q, ctx);
BN_mod_add(tmp0, tmp0, z3, q, ctx);
BN_mod_mul(z3, x1, z2, q, ctx);
BN_mod_mul(z2, tmp1, tmp0, q, ctx);
}
if (swap)
{
std::swap (x2, x3);
std::swap (z2, z3);
}
BN_mod_inverse (z2, z2, q, ctx);
BIGNUM * res = BN_new (); // not from ctx
BN_mod_mul(res, x2, z2, q, ctx);
BN_CTX_end (ctx);
return res;
}
void Ed25519::ScalarMul (const uint8_t * p, const uint8_t * e, uint8_t * buf, BN_CTX * ctx) const
{
BIGNUM * p1 = DecodeBN<32> (p);
uint8_t k[32];
memcpy (k, e, 32);
k[0] &= 248; k[31] &= 127; k[31] |= 64;
BIGNUM * n = DecodeBN<32> (k);
BIGNUM * q1 = ScalarMul (p1, n, ctx);
EncodeBN (q1, buf, 32);
BN_free (p1); BN_free (n); BN_free (q1);
}
void Ed25519::ScalarMulB (const uint8_t * e, uint8_t * buf, BN_CTX * ctx) const
{
BIGNUM *p1 = BN_new (); BN_set_word (p1, 9);
uint8_t k[32];
memcpy (k, e, 32);
k[0] &= 248; k[31] &= 127; k[31] |= 64;
BIGNUM * n = DecodeBN<32> (k);
BIGNUM * q1 = ScalarMul (p1, n, ctx);
EncodeBN (q1, buf, 32);
BN_free (p1); BN_free (n); BN_free (q1);
}
#endif
void Ed25519::BlindPublicKey (const uint8_t * pub, const uint8_t * seed, uint8_t * blinded) void Ed25519::BlindPublicKey (const uint8_t * pub, const uint8_t * seed, uint8_t * blinded)
{ {
BN_CTX * ctx = BN_CTX_new (); BN_CTX * ctx = BN_CTX_new ();

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2020, The PurpleI2P Project * Copyright (c) 2013-2024, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@@ -84,10 +84,7 @@ namespace crypto
EDDSAPoint GeneratePublicKey (const uint8_t * expandedPrivateKey, BN_CTX * ctx) const; EDDSAPoint GeneratePublicKey (const uint8_t * expandedPrivateKey, BN_CTX * ctx) const;
EDDSAPoint DecodePublicKey (const uint8_t * buf, BN_CTX * ctx) const; EDDSAPoint DecodePublicKey (const uint8_t * buf, BN_CTX * ctx) const;
void EncodePublicKey (const EDDSAPoint& publicKey, uint8_t * buf, BN_CTX * ctx) const; void EncodePublicKey (const EDDSAPoint& publicKey, uint8_t * buf, BN_CTX * ctx) const;
#if !OPENSSL_X25519
void ScalarMul (const uint8_t * p, const uint8_t * e, uint8_t * buf, BN_CTX * ctx) const; // p is point, e is number for x25519
void ScalarMulB (const uint8_t * e, uint8_t * buf, BN_CTX * ctx) const;
#endif
void BlindPublicKey (const uint8_t * pub, const uint8_t * seed, uint8_t * blinded); // for encrypted LeaseSet2, pub - 32, seed - 64, blinded - 32 void BlindPublicKey (const uint8_t * pub, const uint8_t * seed, uint8_t * blinded); // for encrypted LeaseSet2, pub - 32, seed - 64, blinded - 32
void BlindPrivateKey (const uint8_t * priv, const uint8_t * seed, uint8_t * blindedPriv, uint8_t * blindedPub); // for encrypted LeaseSet2, pub - 32, seed - 64, blinded - 32 void BlindPrivateKey (const uint8_t * priv, const uint8_t * seed, uint8_t * blindedPriv, uint8_t * blindedPub); // for encrypted LeaseSet2, pub - 32, seed - 64, blinded - 32
@@ -115,11 +112,6 @@ namespace crypto
BIGNUM * DecodeBN (const uint8_t * buf) const; BIGNUM * DecodeBN (const uint8_t * buf) const;
void EncodeBN (const BIGNUM * bn, uint8_t * buf, size_t len) const; void EncodeBN (const BIGNUM * bn, uint8_t * buf, size_t len) const;
#if !OPENSSL_X25519
// for x25519
BIGNUM * ScalarMul (const BIGNUM * p, const BIGNUM * e, BN_CTX * ctx) const;
#endif
private: private:
BIGNUM * q, * l, * d, * I; BIGNUM * q, * l, * d, * I;

View File

@@ -7,10 +7,11 @@
*/ */
#include <algorithm> #include <algorithm>
#include <boost/filesystem.hpp>
#if defined(MAC_OSX) #if defined(MAC_OSX)
#if !STD_FILESYSTEM
#include <boost/system/system_error.hpp> #include <boost/system/system_error.hpp>
#endif
#include <TargetConditionals.h> #include <TargetConditionals.h>
#endif #endif
@@ -25,6 +26,14 @@
#include "Log.h" #include "Log.h"
#include "Garlic.h" #include "Garlic.h"
#if STD_FILESYSTEM
#include <filesystem>
namespace fs_lib = std::filesystem;
#else
#include <boost/filesystem.hpp>
namespace fs_lib = boost::filesystem;
#endif
namespace i2p { namespace i2p {
namespace fs { namespace fs {
std::string appName = "i2pd"; std::string appName = "i2pd";
@@ -54,15 +63,17 @@ namespace fs {
const std::string GetUTF8DataDir () { const std::string GetUTF8DataDir () {
#ifdef _WIN32 #ifdef _WIN32
#if (BOOST_VERSION >= 108500) int size = MultiByteToWideChar(CP_ACP, 0,
boost::filesystem::path path (dataDir); dataDir.c_str(), dataDir.size(), nullptr, 0);
#else std::wstring utf16Str(size, L'\0');
boost::filesystem::wpath path (dataDir); MultiByteToWideChar(CP_ACP, 0,
#endif dataDir.c_str(), dataDir.size(), &utf16Str[0], size);
auto loc = boost::filesystem::path::imbue(std::locale( std::locale(), new std::codecvt_utf8_utf16<wchar_t>() ) ); // convert path to UTF-8 int utf8Size = WideCharToMultiByte(CP_UTF8, 0,
auto dataDirUTF8 = path.string(); utf16Str.c_str(), utf16Str.size(), nullptr, 0, nullptr, nullptr);
boost::filesystem::path::imbue(loc); // Return locale settings back std::string utf8Str(utf8Size, '\0');
return dataDirUTF8; WideCharToMultiByte(CP_UTF8, 0,
utf16Str.c_str(), utf16Str.size(), &utf8Str[0], utf8Size, nullptr, nullptr);
return utf8Str;
#else #else
return dataDir; // linux, osx, android uses UTF-8 by default return dataDir; // linux, osx, android uses UTF-8 by default
#endif #endif
@@ -91,10 +102,10 @@ namespace fs {
} }
else else
{ {
#if (BOOST_VERSION >= 108500) #if ((BOOST_VERSION >= 108500) || STD_FILESYSTEM)
dataDir = boost::filesystem::path(commonAppData).string() + "\\" + appName; dataDir = fs_lib::path(commonAppData).string() + "\\" + appName;
#else #else
dataDir = boost::filesystem::wpath(commonAppData).string() + "\\" + appName; dataDir = fs_lib::wpath(commonAppData).string() + "\\" + appName;
#endif #endif
} }
#else #else
@@ -120,14 +131,14 @@ namespace fs {
} }
else else
{ {
#if (BOOST_VERSION >= 108500) #if ((BOOST_VERSION >= 108500) || STD_FILESYSTEM)
auto execPath = boost::filesystem::path(localAppData).parent_path(); auto execPath = fs_lib::path(localAppData).parent_path();
#else #else
auto execPath = boost::filesystem::wpath(localAppData).parent_path(); auto execPath = fs_lib::wpath(localAppData).parent_path();
#endif #endif
// if config file exists in .exe's folder use it // if config file exists in .exe's folder use it
if(boost::filesystem::exists(execPath/"i2pd.conf")) // TODO: magic string if(fs_lib::exists(execPath/"i2pd.conf")) // TODO: magic string
{ {
dataDir = execPath.string (); dataDir = execPath.string ();
} else // otherwise %appdata% } else // otherwise %appdata%
@@ -143,10 +154,10 @@ namespace fs {
} }
else else
{ {
#if (BOOST_VERSION >= 108500) #if ((BOOST_VERSION >= 108500) || STD_FILESYSTEM)
dataDir = boost::filesystem::path(localAppData).string() + "\\" + appName; dataDir = fs_lib::path(localAppData).string() + "\\" + appName;
#else #else
dataDir = boost::filesystem::wpath(localAppData).string() + "\\" + appName; dataDir = fs_lib::wpath(localAppData).string() + "\\" + appName;
#endif #endif
} }
} }
@@ -169,7 +180,7 @@ namespace fs {
#if defined(ANDROID) #if defined(ANDROID)
const char * ext = getenv("EXTERNAL_STORAGE"); const char * ext = getenv("EXTERNAL_STORAGE");
if (!ext) ext = "/sdcard"; if (!ext) ext = "/sdcard";
if (boost::filesystem::exists(ext)) if (fs_lib::exists(ext))
{ {
dataDir = std::string (ext) + "/" + appName; dataDir = std::string (ext) + "/" + appName;
return; return;
@@ -202,16 +213,16 @@ namespace fs {
} }
bool Init() { bool Init() {
if (!boost::filesystem::exists(dataDir)) if (!fs_lib::exists(dataDir))
boost::filesystem::create_directory(dataDir); fs_lib::create_directory(dataDir);
std::string destinations = DataDirPath("destinations"); std::string destinations = DataDirPath("destinations");
if (!boost::filesystem::exists(destinations)) if (!fs_lib::exists(destinations))
boost::filesystem::create_directory(destinations); fs_lib::create_directory(destinations);
std::string tags = DataDirPath("tags"); std::string tags = DataDirPath("tags");
if (!boost::filesystem::exists(tags)) if (!fs_lib::exists(tags))
boost::filesystem::create_directory(tags); fs_lib::create_directory(tags);
else else
i2p::garlic::CleanUpTagsFiles (); i2p::garlic::CleanUpTagsFiles ();
@@ -219,13 +230,13 @@ namespace fs {
} }
bool ReadDir(const std::string & path, std::vector<std::string> & files) { bool ReadDir(const std::string & path, std::vector<std::string> & files) {
if (!boost::filesystem::exists(path)) if (!fs_lib::exists(path))
return false; return false;
boost::filesystem::directory_iterator it(path); fs_lib::directory_iterator it(path);
boost::filesystem::directory_iterator end; fs_lib::directory_iterator end;
for ( ; it != end; it++) { for ( ; it != end; it++) {
if (!boost::filesystem::is_regular_file(it->status())) if (!fs_lib::is_regular_file(it->status()))
continue; continue;
files.push_back(it->path().string()); files.push_back(it->path().string());
} }
@@ -234,29 +245,42 @@ namespace fs {
} }
bool Exists(const std::string & path) { bool Exists(const std::string & path) {
return boost::filesystem::exists(path); return fs_lib::exists(path);
} }
uint32_t GetLastUpdateTime (const std::string & path) uint32_t GetLastUpdateTime (const std::string & path)
{ {
if (!boost::filesystem::exists(path)) if (!fs_lib::exists(path))
return 0; return 0;
#if STD_FILESYSTEM
std::error_code ec;
auto t = std::filesystem::last_write_time (path, ec);
if (ec) return 0;
/*#if __cplusplus >= 202002L // C++ 20 or higher
const auto sctp = std::chrono::clock_cast<std::chrono::system_clock>(t);
#else */ // TODO: wait until implemented
const auto sctp = std::chrono::time_point_cast<std::chrono::system_clock::duration>(
t - decltype(t)::clock::now() + std::chrono::system_clock::now());
/*#endif */
return std::chrono::system_clock::to_time_t(sctp);
#else
boost::system::error_code ec; boost::system::error_code ec;
auto t = boost::filesystem::last_write_time (path, ec); auto t = boost::filesystem::last_write_time (path, ec);
return ec ? 0 : t; return ec ? 0 : t;
#endif
} }
bool Remove(const std::string & path) { bool Remove(const std::string & path) {
if (!boost::filesystem::exists(path)) if (!fs_lib::exists(path))
return false; return false;
return boost::filesystem::remove(path); return fs_lib::remove(path);
} }
bool CreateDirectory (const std::string& path) bool CreateDirectory (const std::string& path)
{ {
if (boost::filesystem::exists(path) && boost::filesystem::is_directory (boost::filesystem::status (path))) if (fs_lib::exists(path) && fs_lib::is_directory (fs_lib::status (path)))
return true; return true;
return boost::filesystem::create_directory(path); return fs_lib::create_directory(path);
} }
void HashedStorage::SetPlace(const std::string &path) { void HashedStorage::SetPlace(const std::string &path) {
@@ -264,18 +288,18 @@ namespace fs {
} }
bool HashedStorage::Init(const char * chars, size_t count) { bool HashedStorage::Init(const char * chars, size_t count) {
if (!boost::filesystem::exists(root)) { if (!fs_lib::exists(root)) {
boost::filesystem::create_directories(root); fs_lib::create_directories(root);
} }
for (size_t i = 0; i < count; i++) { for (size_t i = 0; i < count; i++) {
auto p = root + i2p::fs::dirSep + prefix1 + chars[i]; auto p = root + i2p::fs::dirSep + prefix1 + chars[i];
if (boost::filesystem::exists(p)) if (fs_lib::exists(p))
continue; continue;
#if TARGET_OS_SIMULATOR #if TARGET_OS_SIMULATOR
// ios simulator fs says it is case sensitive, but it is not // ios simulator fs says it is case sensitive, but it is not
boost::system::error_code ec; boost::system::error_code ec;
if (boost::filesystem::create_directory(p, ec)) if (fs_lib::create_directory(p, ec))
continue; continue;
switch (ec.value()) { switch (ec.value()) {
case boost::system::errc::file_exists: case boost::system::errc::file_exists:
@@ -285,7 +309,7 @@ namespace fs {
throw boost::system::system_error( ec, __func__ ); throw boost::system::system_error( ec, __func__ );
} }
#else #else
if (boost::filesystem::create_directory(p)) if (fs_lib::create_directory(p))
continue; /* ^ throws exception on failure */ continue; /* ^ throws exception on failure */
#endif #endif
return false; return false;
@@ -308,9 +332,9 @@ namespace fs {
void HashedStorage::Remove(const std::string & ident) { void HashedStorage::Remove(const std::string & ident) {
std::string path = Path(ident); std::string path = Path(ident);
if (!boost::filesystem::exists(path)) if (!fs_lib::exists(path))
return; return;
boost::filesystem::remove(path); fs_lib::remove(path);
} }
void HashedStorage::Traverse(std::vector<std::string> & files) { void HashedStorage::Traverse(std::vector<std::string> & files) {
@@ -321,12 +345,12 @@ namespace fs {
void HashedStorage::Iterate(FilenameVisitor v) void HashedStorage::Iterate(FilenameVisitor v)
{ {
boost::filesystem::path p(root); fs_lib::path p(root);
boost::filesystem::recursive_directory_iterator it(p); fs_lib::recursive_directory_iterator it(p);
boost::filesystem::recursive_directory_iterator end; fs_lib::recursive_directory_iterator end;
for ( ; it != end; it++) { for ( ; it != end; it++) {
if (!boost::filesystem::is_regular_file( it->status() )) if (!fs_lib::is_regular_file( it->status() ))
continue; continue;
const std::string & t = it->path().string(); const std::string & t = it->path().string();
v(t); v(t);

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2020, The PurpleI2P Project * Copyright (c) 2013-2024, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@@ -15,6 +15,16 @@
#include <sstream> #include <sstream>
#include <functional> #include <functional>
#ifndef STD_FILESYSTEM
# if (_WIN32 && __GNUG__) // MinGW GCC somehow incorrectly converts paths
# define STD_FILESYSTEM 0
# elif (!TARGET_OS_SIMULATOR && __has_include(<filesystem>)) // supports std::filesystem
# define STD_FILESYSTEM 1
# else
# define STD_FILESYSTEM 0
# endif
#endif
namespace i2p { namespace i2p {
namespace fs { namespace fs {
extern std::string dirSep; extern std::string dirSep;

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2023, The PurpleI2P Project * Copyright (c) 2013-2024, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@@ -7,7 +7,6 @@
*/ */
#include <string.h> #include <string.h>
#include <openssl/evp.h>
#include <openssl/ssl.h> #include <openssl/ssl.h>
#include "Crypto.h" #include "Crypto.h"
#include "FS.h" #include "FS.h"
@@ -25,6 +24,8 @@ namespace data
Families::~Families () Families::~Families ()
{ {
for (auto it : m_SigningKeys)
if (it.second.first) EVP_PKEY_free (it.second.first);
} }
void Families::LoadCertificate (const std::string& filename) void Families::LoadCertificate (const std::string& filename)
@@ -47,48 +48,16 @@ namespace data
cn += 3; cn += 3;
char * family = strstr (cn, ".family"); char * family = strstr (cn, ".family");
if (family) family[0] = 0; if (family) family[0] = 0;
} auto pkey = X509_get_pubkey (cert);
auto pkey = X509_get_pubkey (cert); if (pkey)
int keyType = EVP_PKEY_base_id (pkey); {
switch (keyType) if (!m_SigningKeys.emplace (cn, std::make_pair(pkey, (int)m_SigningKeys.size () + 1)).second)
{
case EVP_PKEY_DSA:
// TODO:
break;
case EVP_PKEY_EC:
{
EC_KEY * ecKey = EVP_PKEY_get1_EC_KEY (pkey);
if (ecKey)
{ {
auto group = EC_KEY_get0_group (ecKey); EVP_PKEY_free (pkey);
if (group) LogPrint (eLogError, "Family: Duplicated family name ", cn);
{ }
int curve = EC_GROUP_get_curve_name (group); }
if (curve == NID_X9_62_prime256v1)
{
uint8_t signingKey[64];
BIGNUM * x = BN_new(), * y = BN_new();
EC_POINT_get_affine_coordinates_GFp (group,
EC_KEY_get0_public_key (ecKey), x, y, NULL);
i2p::crypto::bn2buf (x, signingKey, 32);
i2p::crypto::bn2buf (y, signingKey + 32, 32);
BN_free (x); BN_free (y);
verifier = std::make_shared<i2p::crypto::ECDSAP256Verifier>();
verifier->SetPublicKey (signingKey);
}
else
LogPrint (eLogWarning, "Family: elliptic curve ", curve, " is not supported");
}
EC_KEY_free (ecKey);
}
break;
}
default:
LogPrint (eLogWarning, "Family: Certificate key type ", keyType, " is not supported");
} }
EVP_PKEY_free (pkey);
if (verifier && cn)
m_SigningKeys.emplace (cn, std::make_pair(verifier, (int)m_SigningKeys.size () + 1));
} }
SSL_free (ssl); SSL_free (ssl);
} }
@@ -130,14 +99,22 @@ namespace data
LogPrint (eLogError, "Family: ", family, " is too long"); LogPrint (eLogError, "Family: ", family, " is too long");
return false; return false;
} }
memcpy (buf, family.c_str (), len);
memcpy (buf + len, (const uint8_t *)ident, 32);
len += 32;
Base64ToByteStream (signature, signatureLen, signatureBuf, 64);
auto it = m_SigningKeys.find (family); auto it = m_SigningKeys.find (family);
if (it != m_SigningKeys.end ()) if (it != m_SigningKeys.end () && it->second.first)
return it->second.first->Verify (buf, len, signatureBuf); {
memcpy (buf, family.c_str (), len);
memcpy (buf + len, (const uint8_t *)ident, 32);
len += 32;
auto signatureBufLen = Base64ToByteStream (signature, signatureLen, signatureBuf, 64);
if (signatureBufLen)
{
EVP_MD_CTX * ctx = EVP_MD_CTX_create ();
EVP_DigestVerifyInit (ctx, NULL, NULL, NULL, it->second.first);
auto ret = EVP_DigestVerify (ctx, signatureBuf, signatureBufLen, buf, len);
EVP_MD_CTX_destroy (ctx);
return ret;
}
}
// TODO: process key // TODO: process key
return true; return true;
} }

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2022, The PurpleI2P Project * Copyright (c) 2013-2024, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@@ -12,7 +12,7 @@
#include <map> #include <map>
#include <string> #include <string>
#include <memory> #include <memory>
#include "Signature.h" #include <openssl/evp.h>
#include "Identity.h" #include "Identity.h"
namespace i2p namespace i2p
@@ -37,7 +37,7 @@ namespace data
private: private:
std::map<std::string, std::pair<std::shared_ptr<i2p::crypto::Verifier>, FamilyID> > m_SigningKeys; // family -> (verifier, id) std::map<std::string, std::pair<EVP_PKEY *, FamilyID> > m_SigningKeys; // family -> (verification pkey, id)
}; };
std::string CreateFamilySignature (const std::string& family, const IdentHash& ident); std::string CreateFamilySignature (const std::string& family, const IdentHash& ident);

View File

@@ -45,22 +45,17 @@ namespace garlic
{ {
if (!m_SharedRoutingPath) return nullptr; if (!m_SharedRoutingPath) return nullptr;
uint32_t ts = i2p::util::GetSecondsSinceEpoch (); uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
if (m_SharedRoutingPath->numTimesUsed >= ROUTING_PATH_MAX_NUM_TIMES_USED || if (!m_SharedRoutingPath->outboundTunnel->IsEstablished () ||
!m_SharedRoutingPath->outboundTunnel->IsEstablished () ||
ts*1000LL > m_SharedRoutingPath->remoteLease->endDate || ts*1000LL > m_SharedRoutingPath->remoteLease->endDate ||
ts > m_SharedRoutingPath->updateTime + ROUTING_PATH_EXPIRATION_TIMEOUT) ts > m_SharedRoutingPath->updateTime + ROUTING_PATH_EXPIRATION_TIMEOUT)
m_SharedRoutingPath = nullptr; m_SharedRoutingPath = nullptr;
if (m_SharedRoutingPath) m_SharedRoutingPath->numTimesUsed++;
return m_SharedRoutingPath; return m_SharedRoutingPath;
} }
void GarlicRoutingSession::SetSharedRoutingPath (std::shared_ptr<GarlicRoutingPath> path) void GarlicRoutingSession::SetSharedRoutingPath (std::shared_ptr<GarlicRoutingPath> path)
{ {
if (path && path->outboundTunnel && path->remoteLease) if (path && path->outboundTunnel && path->remoteLease)
{
path->updateTime = i2p::util::GetSecondsSinceEpoch (); path->updateTime = i2p::util::GetSecondsSinceEpoch ();
path->numTimesUsed = 0;
}
else else
path = nullptr; path = nullptr;
m_SharedRoutingPath = path; m_SharedRoutingPath = path;
@@ -165,7 +160,7 @@ namespace garlic
uint8_t iv[32]; // IV is first 16 bytes uint8_t iv[32]; // IV is first 16 bytes
SHA256(elGamal.preIV, 32, iv); SHA256(elGamal.preIV, 32, iv);
m_Destination->Encrypt ((uint8_t *)&elGamal, buf); m_Destination->Encrypt ((uint8_t *)&elGamal, buf);
m_Encryption.SetIV (iv); m_IV = iv;
buf += 514; buf += 514;
len += 514; len += 514;
} }
@@ -175,7 +170,7 @@ namespace garlic
memcpy (buf, tag, 32); memcpy (buf, tag, 32);
uint8_t iv[32]; // IV is first 16 bytes uint8_t iv[32]; // IV is first 16 bytes
SHA256(tag, 32, iv); SHA256(tag, 32, iv);
m_Encryption.SetIV (iv); m_IV = iv;
buf += 32; buf += 32;
len += 32; len += 32;
} }
@@ -215,7 +210,7 @@ namespace garlic
size_t rem = blockSize % 16; size_t rem = blockSize % 16;
if (rem) if (rem)
blockSize += (16-rem); //padding blockSize += (16-rem); //padding
m_Encryption.Encrypt(buf, blockSize, buf); m_Encryption.Encrypt(buf, blockSize, m_IV, buf);
return blockSize; return blockSize;
} }
@@ -431,7 +426,8 @@ namespace garlic
} }
GarlicDestination::GarlicDestination (): m_NumTags (32), // 32 tags by default GarlicDestination::GarlicDestination (): m_NumTags (32), // 32 tags by default
m_PayloadBuffer (nullptr), m_NumRatchetInboundTags (0) // 0 means standard m_PayloadBuffer (nullptr), m_LastIncomingSessionTimestamp (0),
m_NumRatchetInboundTags (0) // 0 means standard
{ {
} }
@@ -518,8 +514,7 @@ namespace garlic
{ {
uint8_t iv[32]; // IV is first 16 bytes uint8_t iv[32]; // IV is first 16 bytes
SHA256(buf, 32, iv); SHA256(buf, 32, iv);
decryption->SetIV (iv); decryption->Decrypt (buf + 32, length - 32, iv, buf + 32);
decryption->Decrypt (buf + 32, length - 32, buf + 32);
HandleAESBlock (buf + 32, length - 32, decryption, msg->from); HandleAESBlock (buf + 32, length - 32, decryption, msg->from);
found = true; found = true;
} }
@@ -537,43 +532,23 @@ namespace garlic
auto decryption = std::make_shared<AESDecryption>(elGamal.sessionKey); auto decryption = std::make_shared<AESDecryption>(elGamal.sessionKey);
uint8_t iv[32]; // IV is first 16 bytes uint8_t iv[32]; // IV is first 16 bytes
SHA256(elGamal.preIV, 32, iv); SHA256(elGamal.preIV, 32, iv);
decryption->SetIV (iv); decryption->Decrypt(buf + 514, length - 514, iv, buf + 514);
decryption->Decrypt(buf + 514, length - 514, buf + 514);
HandleAESBlock (buf + 514, length - 514, decryption, msg->from); HandleAESBlock (buf + 514, length - 514, decryption, msg->from);
} }
else if (SupportsEncryptionType (i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD)) else if (SupportsEncryptionType (i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD))
{ {
// otherwise ECIESx25519 // otherwise ECIESx25519
auto session = std::make_shared<ECIESX25519AEADRatchetSession> (this, false); // incoming auto ts = i2p::util::GetMillisecondsSinceEpoch ();
if (!session->HandleNextMessage (buf, length, nullptr, 0)) if (ts > m_LastIncomingSessionTimestamp + INCOMING_SESSIONS_MINIMAL_INTERVAL)
{ {
// try to generate more tags for last tagset auto session = std::make_shared<ECIESX25519AEADRatchetSession> (this, false); // incoming
if (m_LastTagset && (m_LastTagset->GetNextIndex () - m_LastTagset->GetTrimBehind () < 3*ECIESX25519_MAX_NUM_GENERATED_TAGS)) if (session->HandleNextMessage (buf, length, nullptr, 0))
{ m_LastIncomingSessionTimestamp = ts;
uint64_t missingTag; memcpy (&missingTag, buf, 8); else
auto maxTags = std::max (m_NumRatchetInboundTags, ECIESX25519_MAX_NUM_GENERATED_TAGS);
LogPrint (eLogWarning, "Garlic: Trying to generate more ECIES-X25519-AEAD-Ratchet tags");
for (int i = 0; i < maxTags; i++)
{
auto nextTag = AddECIESx25519SessionNextTag (m_LastTagset);
if (!nextTag)
{
LogPrint (eLogError, "Garlic: Can't create new ECIES-X25519-AEAD-Ratchet tag for last tagset");
break;
}
if (nextTag == missingTag)
{
LogPrint (eLogDebug, "Garlic: Missing ECIES-X25519-AEAD-Ratchet tag was generated");
if (m_LastTagset->HandleNextMessage (buf, length, m_ECIESx25519Tags[nextTag].index))
found = true;
break;
}
}
if (!found) m_LastTagset = nullptr;
}
if (!found)
LogPrint (eLogError, "Garlic: Can't handle ECIES-X25519-AEAD-Ratchet message"); LogPrint (eLogError, "Garlic: Can't handle ECIES-X25519-AEAD-Ratchet message");
} }
else
LogPrint (eLogWarning, "Garlic: Incoming sessions come too often");
} }
else else
LogPrint (eLogError, "Garlic: Failed to decrypt message"); LogPrint (eLogError, "Garlic: Failed to decrypt message");
@@ -588,9 +563,7 @@ namespace garlic
auto it = m_ECIESx25519Tags.find (tag); auto it = m_ECIESx25519Tags.find (tag);
if (it != m_ECIESx25519Tags.end ()) if (it != m_ECIESx25519Tags.end ())
{ {
if (it->second.tagset && it->second.tagset->HandleNextMessage (buf, len, it->second.index)) if (!it->second.tagset || !it->second.tagset->HandleNextMessage (buf, len, it->second.index))
m_LastTagset = it->second.tagset;
else
LogPrint (eLogError, "Garlic: Can't handle ECIES-X25519-AEAD-Ratchet message"); LogPrint (eLogError, "Garlic: Can't handle ECIES-X25519-AEAD-Ratchet message");
m_ECIESx25519Tags.erase (it); m_ECIESx25519Tags.erase (it);
return true; return true;
@@ -771,7 +744,8 @@ namespace garlic
} }
std::shared_ptr<GarlicRoutingSession> GarlicDestination::GetRoutingSession ( std::shared_ptr<GarlicRoutingSession> GarlicDestination::GetRoutingSession (
std::shared_ptr<const i2p::data::RoutingDestination> destination, bool attachLeaseSet) std::shared_ptr<const i2p::data::RoutingDestination> destination, bool attachLeaseSet,
bool requestNewIfNotFound)
{ {
if (destination->GetEncryptionType () == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD && if (destination->GetEncryptionType () == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD &&
SupportsEncryptionType (i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD)) SupportsEncryptionType (i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD))
@@ -786,16 +760,17 @@ namespace garlic
if (session->IsInactive (i2p::util::GetSecondsSinceEpoch ())) if (session->IsInactive (i2p::util::GetSecondsSinceEpoch ()))
{ {
LogPrint (eLogDebug, "Garlic: Session restarted"); LogPrint (eLogDebug, "Garlic: Session restarted");
requestNewIfNotFound = true; // it's not a new session
session = nullptr; session = nullptr;
} }
} }
if (!session) if (!session && requestNewIfNotFound)
{ {
session = std::make_shared<ECIESX25519AEADRatchetSession> (this, true); session = std::make_shared<ECIESX25519AEADRatchetSession> (this, true);
session->SetRemoteStaticKey (staticKey); session->SetRemoteStaticKey (staticKey);
} }
if (destination->IsDestination ()) if (session && destination->IsDestination ())
session->SetDestination (destination->GetIdentHash ()); // TODO: remove session->SetDestination (destination->GetIdentHash ()); // NS or NSR
return session; return session;
} }
else else
@@ -898,8 +873,6 @@ namespace garlic
} }
if (numExpiredTags > 0) if (numExpiredTags > 0)
LogPrint (eLogDebug, "Garlic: ", numExpiredTags, " ECIESx25519 tags expired for ", GetIdentHash().ToBase64 ()); LogPrint (eLogDebug, "Garlic: ", numExpiredTags, " ECIESx25519 tags expired for ", GetIdentHash().ToBase64 ());
if (m_LastTagset && m_LastTagset->IsExpired (ts))
m_LastTagset = nullptr;
} }
void GarlicDestination::RemoveDeliveryStatusSession (uint32_t msgID) void GarlicDestination::RemoveDeliveryStatusSession (uint32_t msgID)
@@ -933,7 +906,7 @@ namespace garlic
} }
} }
void GarlicDestination::SetLeaseSetUpdated () void GarlicDestination::SetLeaseSetUpdated (bool post)
{ {
{ {
std::unique_lock<std::mutex> l(m_SessionsMutex); std::unique_lock<std::mutex> l(m_SessionsMutex);
@@ -1036,9 +1009,7 @@ namespace garlic
case eGarlicDeliveryTypeDestination: case eGarlicDeliveryTypeDestination:
LogPrint (eLogDebug, "Garlic: Type destination"); LogPrint (eLogDebug, "Garlic: Type destination");
buf += 32; // TODO: check destination buf += 32; // TODO: check destination
#if (__cplusplus >= 201703L) // C++ 17 or higher
[[fallthrough]]; [[fallthrough]];
#endif
// no break here // no break here
case eGarlicDeliveryTypeLocal: case eGarlicDeliveryTypeLocal:
{ {
@@ -1132,5 +1103,17 @@ namespace garlic
m_PayloadBuffer = new uint8_t[I2NP_MAX_MESSAGE_SIZE]; m_PayloadBuffer = new uint8_t[I2NP_MAX_MESSAGE_SIZE];
return m_PayloadBuffer; return m_PayloadBuffer;
} }
bool GarlicDestination::AEADChaCha20Poly1305Encrypt (const uint8_t * msg, size_t msgLen, const uint8_t * ad, size_t adLen,
const uint8_t * key, const uint8_t * nonce, uint8_t * buf, size_t len)
{
return m_Encryptor.Encrypt (msg, msgLen, ad, adLen, key, nonce, buf, len);
}
bool GarlicDestination::AEADChaCha20Poly1305Decrypt (const uint8_t * msg, size_t msgLen, const uint8_t * ad, size_t adLen,
const uint8_t * key, const uint8_t * nonce, uint8_t * buf, size_t len)
{
return m_Decryptor.Decrypt (msg, msgLen, ad, adLen, key, nonce, buf, len);
}
} }
} }

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2024, The PurpleI2P Project * Copyright (c) 2013-2025, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@@ -51,8 +51,8 @@ namespace garlic
const int OUTGOING_TAGS_EXPIRATION_TIMEOUT = 720; // 12 minutes const int OUTGOING_TAGS_EXPIRATION_TIMEOUT = 720; // 12 minutes
const int OUTGOING_TAGS_CONFIRMATION_TIMEOUT = 10; // 10 seconds const int OUTGOING_TAGS_CONFIRMATION_TIMEOUT = 10; // 10 seconds
const int LEASESET_CONFIRMATION_TIMEOUT = 4000; // in milliseconds const int LEASESET_CONFIRMATION_TIMEOUT = 4000; // in milliseconds
const int ROUTING_PATH_EXPIRATION_TIMEOUT = 30; // 30 seconds const int ROUTING_PATH_EXPIRATION_TIMEOUT = 120; // in seconds
const int ROUTING_PATH_MAX_NUM_TIMES_USED = 100; // how many times might be used const int INCOMING_SESSIONS_MINIMAL_INTERVAL = 200; // in milliseconds
struct SessionTag: public i2p::data::Tag<32> struct SessionTag: public i2p::data::Tag<32>
{ {
@@ -89,7 +89,6 @@ namespace garlic
std::shared_ptr<const i2p::data::Lease> remoteLease; std::shared_ptr<const i2p::data::Lease> remoteLease;
int rtt; // RTT int rtt; // RTT
uint32_t updateTime; // seconds since epoch uint32_t updateTime; // seconds since epoch
int numTimesUsed;
}; };
class GarlicDestination; class GarlicDestination;
@@ -111,13 +110,14 @@ namespace garlic
GarlicRoutingSession (); GarlicRoutingSession ();
virtual ~GarlicRoutingSession (); virtual ~GarlicRoutingSession ();
virtual std::shared_ptr<I2NPMessage> WrapSingleMessage (std::shared_ptr<const I2NPMessage> msg) = 0; 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 CleanupUnconfirmedTags () { return false; }; // for I2CP, override in ElGamalAESSession and ECIESX25519AEADRatchetSession
virtual bool MessageConfirmed (uint32_t msgID); virtual bool MessageConfirmed (uint32_t msgID);
virtual bool IsRatchets () const { return false; }; virtual bool IsRatchets () const { return false; };
virtual bool IsReadyToSend () const { return true; }; virtual bool IsReadyToSend () const { return true; };
virtual bool IsTerminated () const { return !GetOwner (); }; virtual bool IsTerminated () const { return !GetOwner (); };
virtual uint64_t GetLastActivityTimestamp () const { return 0; }; // non-zero for rathets only virtual uint64_t GetLastActivityTimestamp () const { return 0; }; // non-zero for rathets only
virtual void SetAckRequestInterval (int interval) {}; // in milliseconds, override in ECIESX25519AEADRatchetSession
void SetLeaseSetUpdated () void SetLeaseSetUpdated ()
{ {
if (m_LeaseSetUpdateStatus != eLeaseSetDoNotSend) m_LeaseSetUpdateStatus = eLeaseSetUpdated; if (m_LeaseSetUpdateStatus != eLeaseSetDoNotSend) m_LeaseSetUpdateStatus = eLeaseSetUpdated;
@@ -206,6 +206,7 @@ namespace garlic
std::map<uint32_t, std::unique_ptr<UnconfirmedTags> > m_UnconfirmedTagsMsgs; // msgID->tags std::map<uint32_t, std::unique_ptr<UnconfirmedTags> > m_UnconfirmedTagsMsgs; // msgID->tags
i2p::crypto::CBCEncryption m_Encryption; i2p::crypto::CBCEncryption m_Encryption;
i2p::data::Tag<16> m_IV;
public: public:
@@ -236,11 +237,17 @@ namespace garlic
int GetNumTags () const { return m_NumTags; }; int GetNumTags () const { return m_NumTags; };
void SetNumRatchetInboundTags (int numTags) { m_NumRatchetInboundTags = numTags; }; void SetNumRatchetInboundTags (int numTags) { m_NumRatchetInboundTags = numTags; };
int GetNumRatchetInboundTags () const { return m_NumRatchetInboundTags; }; int GetNumRatchetInboundTags () const { return m_NumRatchetInboundTags; };
std::shared_ptr<GarlicRoutingSession> GetRoutingSession (std::shared_ptr<const i2p::data::RoutingDestination> destination, bool attachLeaseSet); std::shared_ptr<GarlicRoutingSession> GetRoutingSession (std::shared_ptr<const i2p::data::RoutingDestination> destination,
bool attachLeaseSet, bool requestNewIfNotFound = true);
void CleanupExpiredTags (); void CleanupExpiredTags ();
void RemoveDeliveryStatusSession (uint32_t msgID); void RemoveDeliveryStatusSession (uint32_t msgID);
std::shared_ptr<I2NPMessage> WrapMessageForRouter (std::shared_ptr<const i2p::data::RouterInfo> router, std::shared_ptr<I2NPMessage> WrapMessageForRouter (std::shared_ptr<const i2p::data::RouterInfo> router,
std::shared_ptr<I2NPMessage> msg); std::shared_ptr<I2NPMessage> msg);
bool AEADChaCha20Poly1305Encrypt (const uint8_t * msg, size_t msgLen, const uint8_t * ad, size_t adLen,
const uint8_t * key, const uint8_t * nonce, uint8_t * buf, size_t len);
bool AEADChaCha20Poly1305Decrypt (const uint8_t * msg, size_t msgLen, const uint8_t * ad, size_t adLen,
const uint8_t * key, const uint8_t * nonce, uint8_t * buf, size_t len);
void AddSessionKey (const uint8_t * key, const uint8_t * tag); // one tag void AddSessionKey (const uint8_t * key, const uint8_t * tag); // one tag
void AddECIESx25519Key (const uint8_t * key, uint64_t tag); // one tag void AddECIESx25519Key (const uint8_t * key, uint64_t tag); // one tag
@@ -255,7 +262,7 @@ namespace garlic
virtual void ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg); virtual void ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg);
virtual void ProcessDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg); virtual void ProcessDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg);
virtual void SetLeaseSetUpdated (); virtual void SetLeaseSetUpdated (bool post = false);
virtual std::shared_ptr<const i2p::data::LocalLeaseSet> GetLeaseSet () = 0; // TODO virtual std::shared_ptr<const i2p::data::LocalLeaseSet> GetLeaseSet () = 0; // TODO
virtual std::shared_ptr<i2p::tunnel::TunnelPool> GetTunnelPool () const = 0; virtual std::shared_ptr<i2p::tunnel::TunnelPool> GetTunnelPool () const = 0;
@@ -286,15 +293,18 @@ namespace garlic
std::unordered_map<i2p::data::IdentHash, ElGamalAESSessionPtr> m_Sessions; std::unordered_map<i2p::data::IdentHash, ElGamalAESSessionPtr> m_Sessions;
std::unordered_map<i2p::data::Tag<32>, ECIESX25519AEADRatchetSessionPtr> m_ECIESx25519Sessions; // static key -> session std::unordered_map<i2p::data::Tag<32>, ECIESX25519AEADRatchetSessionPtr> m_ECIESx25519Sessions; // static key -> session
uint8_t * m_PayloadBuffer; // for ECIESX25519AEADRatchet uint8_t * m_PayloadBuffer; // for ECIESX25519AEADRatchet
uint64_t m_LastIncomingSessionTimestamp; // in milliseconds
// incoming // incoming
int m_NumRatchetInboundTags; int m_NumRatchetInboundTags;
std::unordered_map<SessionTag, std::shared_ptr<AESDecryption>, std::hash<i2p::data::Tag<32> > > m_Tags; std::unordered_map<SessionTag, std::shared_ptr<AESDecryption>, std::hash<i2p::data::Tag<32> > > m_Tags;
std::unordered_map<uint64_t, ECIESX25519AEADRatchetIndexTagset> m_ECIESx25519Tags; // session tag -> session std::unordered_map<uint64_t, ECIESX25519AEADRatchetIndexTagset> m_ECIESx25519Tags; // session tag -> session
ReceiveRatchetTagSetPtr m_LastTagset; // tagset last message came for
// DeliveryStatus // DeliveryStatus
std::mutex m_DeliveryStatusSessionsMutex; std::mutex m_DeliveryStatusSessionsMutex;
std::unordered_map<uint32_t, GarlicRoutingSessionPtr> m_DeliveryStatusSessions; // msgID -> session std::unordered_map<uint32_t, GarlicRoutingSessionPtr> m_DeliveryStatusSessions; // msgID -> session
// encryption
i2p::crypto::AEADChaCha20Poly1305Encryptor m_Encryptor;
i2p::crypto::AEADChaCha20Poly1305Decryptor m_Decryptor;
public: public:
// for HTTP only // for HTTP only

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2023, The PurpleI2P Project * Copyright (c) 2013-2024, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@@ -40,12 +40,13 @@ namespace http
inline bool is_http_method(const std::string & str) { inline bool is_http_method(const std::string & str) {
return std::find(HTTP_METHODS.begin(), HTTP_METHODS.end(), str) != std::end(HTTP_METHODS); return std::find(HTTP_METHODS.begin(), HTTP_METHODS.end(), str) != std::end(HTTP_METHODS);
} }
void strsplit(const std::string & line, std::vector<std::string> &tokens, char delim, std::size_t limit = 0) { static void strsplit(std::stringstream& ss, std::vector<std::string> &tokens, char delim, std::size_t limit = 0)
{
std::size_t count = 0; std::size_t count = 0;
std::stringstream ss(line);
std::string token; std::string token;
while (1) { while (1)
{
count++; count++;
if (limit > 0 && count >= limit) if (limit > 0 && count >= limit)
delim = '\n'; /* reset delimiter */ delim = '\n'; /* reset delimiter */
@@ -55,21 +56,33 @@ namespace http
} }
} }
static std::pair<std::string, std::string> parse_header_line(const std::string& line) static void strsplit(const std::string & line, std::vector<std::string> &tokens, char delim, std::size_t limit = 0)
{
std::stringstream ss{line};
strsplit (ss, tokens, delim, limit);
}
static void strsplit(std::string_view line, std::vector<std::string> &tokens, char delim, std::size_t limit = 0)
{
std::stringstream ss{std::string(line)};
strsplit (ss, tokens, delim, limit);
}
static std::pair<std::string, std::string> parse_header_line(std::string_view line)
{ {
std::size_t pos = 0; std::size_t pos = 0;
std::size_t len = 1; /*: */ std::size_t len = 1; /*: */
std::size_t max = line.length(); std::size_t max = line.length();
if ((pos = line.find(':', pos)) == std::string::npos) if ((pos = line.find(':', pos)) == std::string::npos)
return std::make_pair("", ""); // no ':' found return std::pair{"", ""}; // no ':' found
if (pos + 1 < max) // ':' at the end of header is valid if (pos + 1 < max) // ':' at the end of header is valid
{ {
while ((pos + len) < max && isspace(line.at(pos + len))) while ((pos + len) < max && isspace(line.at(pos + len)))
len++; len++;
if (len == 1) if (len == 1)
return std::make_pair("", ""); // no following space, but something else return std::pair{"", ""}; // no following space, but something else
} }
return std::make_pair(line.substr(0, pos), line.substr(pos + len)); return std::pair{std::string (line.substr(0, pos)), std::string (line.substr(pos + len))};
} }
void gen_rfc7231_date(std::string & out) { void gen_rfc7231_date(std::string & out) {
@@ -83,15 +96,18 @@ namespace http
out = buf; out = buf;
} }
bool URL::parse(const char *str, std::size_t len) { bool URL::parse(const char *str, std::size_t len)
std::string url(str, len ? len : strlen(str)); {
return parse(url); return parse({str, len ? len : strlen(str)});
} }
bool URL::parse(const std::string& url) { bool URL::parse(std::string_view url)
{
if (url.empty ()) return false;
std::size_t pos_p = 0; /* < current parse position */ std::size_t pos_p = 0; /* < current parse position */
std::size_t pos_c = 0; /* < work position */ std::size_t pos_c = 0; /* < work position */
if(url.at(0) != '/' || pos_p > 0) { if(url.at(0) != '/' || pos_p > 0)
{
std::size_t pos_s = 0; std::size_t pos_s = 0;
/* schema */ /* schema */
@@ -141,7 +157,7 @@ namespace http
/* port[/path] */ /* port[/path] */
pos_p = pos_c + 1; pos_p = pos_c + 1;
pos_c = url.find('/', pos_p); pos_c = url.find('/', pos_p);
std::string port_str = (pos_c == std::string::npos) std::string_view port_str = (pos_c == std::string::npos)
? url.substr(pos_p, std::string::npos) ? url.substr(pos_p, std::string::npos)
: url.substr(pos_p, pos_c - pos_p); : url.substr(pos_p, pos_c - pos_p);
/* stoi throws exception on failure, we don't need it */ /* stoi throws exception on failure, we don't need it */
@@ -253,7 +269,7 @@ namespace http
return host.rfind(".i2p") == ( host.size() - 4 ); return host.rfind(".i2p") == ( host.size() - 4 );
} }
void HTTPMsg::add_header(const char *name, std::string & value, bool replace) { void HTTPMsg::add_header(const char *name, const std::string & value, bool replace) {
add_header(name, value.c_str(), replace); add_header(name, value.c_str(), replace);
} }
@@ -272,12 +288,13 @@ namespace http
headers.erase(name); headers.erase(name);
} }
int HTTPReq::parse(const char *buf, size_t len) { int HTTPReq::parse(const char *buf, size_t len)
std::string str(buf, len); {
return parse(str); return parse({buf, len});
} }
int HTTPReq::parse(const std::string& str) { int HTTPReq::parse(std::string_view str)
{
enum { REQ_LINE, HEADER_LINE } expect = REQ_LINE; enum { REQ_LINE, HEADER_LINE } expect = REQ_LINE;
std::size_t eoh = str.find(HTTP_EOH); /* request head size */ std::size_t eoh = str.find(HTTP_EOH); /* request head size */
std::size_t eol = 0, pos = 0; std::size_t eol = 0, pos = 0;
@@ -286,9 +303,11 @@ namespace http
if (eoh == std::string::npos) if (eoh == std::string::npos)
return 0; /* str not contains complete request */ return 0; /* str not contains complete request */
while ((eol = str.find(CRLF, pos)) != std::string::npos) { while ((eol = str.find(CRLF, pos)) != std::string::npos)
if (expect == REQ_LINE) { {
std::string line = str.substr(pos, eol - pos); if (expect == REQ_LINE)
{
std::string_view line = str.substr(pos, eol - pos);
std::vector<std::string> tokens; std::vector<std::string> tokens;
strsplit(line, tokens, ' '); strsplit(line, tokens, ' ');
if (tokens.size() != 3) if (tokens.size() != 3)
@@ -307,7 +326,7 @@ namespace http
} }
else else
{ {
std::string line = str.substr(pos, eol - pos); std::string_view line = str.substr(pos, eol - pos);
auto p = parse_header_line(line); auto p = parse_header_line(line);
if (p.first.length () > 0) if (p.first.length () > 0)
headers.push_back (p); headers.push_back (p);
@@ -413,12 +432,13 @@ namespace http
return length; return length;
} }
int HTTPRes::parse(const char *buf, size_t len) { int HTTPRes::parse(const char *buf, size_t len)
std::string str(buf, len); {
return parse(str); return parse({buf,len});
} }
int HTTPRes::parse(const std::string& str) { int HTTPRes::parse(std::string_view str)
{
enum { RES_LINE, HEADER_LINE } expect = RES_LINE; enum { RES_LINE, HEADER_LINE } expect = RES_LINE;
std::size_t eoh = str.find(HTTP_EOH); /* request head size */ std::size_t eoh = str.find(HTTP_EOH); /* request head size */
std::size_t eol = 0, pos = 0; std::size_t eol = 0, pos = 0;
@@ -426,9 +446,11 @@ namespace http
if (eoh == std::string::npos) if (eoh == std::string::npos)
return 0; /* str not contains complete request */ return 0; /* str not contains complete request */
while ((eol = str.find(CRLF, pos)) != std::string::npos) { while ((eol = str.find(CRLF, pos)) != std::string::npos)
if (expect == RES_LINE) { {
std::string line = str.substr(pos, eol - pos); if (expect == RES_LINE)
{
std::string_view line = str.substr(pos, eol - pos);
std::vector<std::string> tokens; std::vector<std::string> tokens;
strsplit(line, tokens, ' ', 3); strsplit(line, tokens, ' ', 3);
if (tokens.size() != 3) if (tokens.size() != 3)
@@ -442,8 +464,10 @@ namespace http
version = tokens[0]; version = tokens[0];
status = tokens[2]; status = tokens[2];
expect = HEADER_LINE; expect = HEADER_LINE;
} else { }
std::string line = str.substr(pos, eol - pos); else
{
std::string_view line = str.substr(pos, eol - pos);
auto p = parse_header_line(line); auto p = parse_header_line(line);
if (p.first.length () > 0) if (p.first.length () > 0)
headers.insert (p); headers.insert (p);
@@ -508,14 +532,14 @@ namespace http
return ptr; return ptr;
} }
std::string UrlDecode(const std::string& data, bool allow_null) std::string UrlDecode(std::string_view data, bool allow_null)
{ {
std::string decoded(data); std::string decoded(data);
size_t pos = 0; size_t pos = 0;
while ((pos = decoded.find('%', pos)) != std::string::npos) while ((pos = decoded.find('%', pos)) != std::string::npos)
{ {
char c = strtol(decoded.substr(pos + 1, 2).c_str(), NULL, 16); char c = std::stol(decoded.substr(pos + 1, 2), nullptr, 16);
if (c == '\0' && !allow_null) if (!c && !allow_null)
{ {
pos += 3; pos += 3;
continue; continue;

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2023, The PurpleI2P Project * Copyright (c) 2013-2024, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@@ -14,6 +14,7 @@
#include <list> #include <list>
#include <sstream> #include <sstream>
#include <string> #include <string>
#include <string_view>
#include <vector> #include <vector>
namespace i2p namespace i2p
@@ -45,7 +46,7 @@ namespace http
* @return true on success, false on invalid url * @return true on success, false on invalid url
*/ */
bool parse (const char *str, std::size_t len = 0); bool parse (const char *str, std::size_t len = 0);
bool parse (const std::string& url); bool parse (std::string_view url);
/** /**
* @brief Parse query part of url to key/value map * @brief Parse query part of url to key/value map
@@ -69,7 +70,7 @@ namespace http
{ {
std::map<std::string, std::string> headers; std::map<std::string, std::string> headers;
void add_header(const char *name, std::string & value, bool replace = false); void add_header(const char *name, const std::string & value, bool replace = false);
void add_header(const char *name, const char *value, bool replace = false); void add_header(const char *name, const char *value, bool replace = false);
void del_header(const char *name); void del_header(const char *name);
@@ -92,7 +93,7 @@ namespace http
* @note Positive return value is a size of header * @note Positive return value is a size of header
*/ */
int parse(const char *buf, size_t len); int parse(const char *buf, size_t len);
int parse(const std::string& buf); int parse(std::string_view buf);
/** @brief Serialize HTTP request to string */ /** @brief Serialize HTTP request to string */
std::string to_string(); std::string to_string();
@@ -128,7 +129,7 @@ namespace http
* @note Positive return value is a size of header * @note Positive return value is a size of header
*/ */
int parse(const char *buf, size_t len); int parse(const char *buf, size_t len);
int parse(const std::string& buf); int parse(const std::string_view buf);
/** /**
* @brief Serialize HTTP response to string * @brief Serialize HTTP response to string
@@ -161,7 +162,7 @@ namespace http
* @param null If set to true - decode also %00 sequence, otherwise - skip * @param null If set to true - decode also %00 sequence, otherwise - skip
* @return Decoded string * @return Decoded string
*/ */
std::string UrlDecode(const std::string& data, bool null = false); std::string UrlDecode(std::string_view data, bool null = false);
/** /**
* @brief Merge HTTP response content with Transfer-Encoding: chunked * @brief Merge HTTP response content with Transfer-Encoding: chunked

View File

@@ -10,20 +10,15 @@
#include <atomic> #include <atomic>
#include "Base.h" #include "Base.h"
#include "Log.h" #include "Log.h"
#include "Crypto.h"
#include "I2PEndian.h" #include "I2PEndian.h"
#include "Timestamp.h" #include "Timestamp.h"
#include "RouterContext.h" #include "RouterContext.h"
#include "NetDb.hpp" #include "NetDb.hpp"
#include "Tunnel.h" #include "Tunnel.h"
#include "Transports.h" #include "TransitTunnel.h"
#include "Garlic.h"
#include "ECIESX25519AEADRatchetSession.h"
#include "I2NPProtocol.h" #include "I2NPProtocol.h"
#include "version.h" #include "version.h"
using namespace i2p::transport;
namespace i2p namespace i2p
{ {
std::shared_ptr<I2NPMessage> NewI2NPMessage () std::shared_ptr<I2NPMessage> NewI2NPMessage ()
@@ -147,7 +142,7 @@ namespace i2p
} }
std::shared_ptr<I2NPMessage> CreateRouterInfoDatabaseLookupMsg (const uint8_t * key, const uint8_t * from, std::shared_ptr<I2NPMessage> CreateRouterInfoDatabaseLookupMsg (const uint8_t * key, const uint8_t * from,
uint32_t replyTunnelID, bool exploratory, std::set<i2p::data::IdentHash> * excludedPeers) uint32_t replyTunnelID, bool exploratory, std::unordered_set<i2p::data::IdentHash> * excludedPeers)
{ {
int cnt = excludedPeers ? excludedPeers->size () : 0; int cnt = excludedPeers ? excludedPeers->size () : 0;
auto m = cnt > 7 ? NewI2NPMessage () : NewI2NPShortMessage (); auto m = cnt > 7 ? NewI2NPMessage () : NewI2NPShortMessage ();
@@ -192,7 +187,7 @@ namespace i2p
} }
std::shared_ptr<I2NPMessage> CreateLeaseSetDatabaseLookupMsg (const i2p::data::IdentHash& dest, std::shared_ptr<I2NPMessage> CreateLeaseSetDatabaseLookupMsg (const i2p::data::IdentHash& dest,
const std::set<i2p::data::IdentHash>& excludedFloodfills, const std::unordered_set<i2p::data::IdentHash>& excludedFloodfills,
std::shared_ptr<const i2p::tunnel::InboundTunnel> replyTunnel, const uint8_t * replyKey, std::shared_ptr<const i2p::tunnel::InboundTunnel> replyTunnel, const uint8_t * replyKey,
const uint8_t * replyTag, bool replyECIES) const uint8_t * replyTag, bool replyECIES)
{ {
@@ -376,337 +371,6 @@ namespace i2p
return !msg->GetPayload ()[DATABASE_STORE_TYPE_OFFSET]; // 0- RouterInfo return !msg->GetPayload ()[DATABASE_STORE_TYPE_OFFSET]; // 0- RouterInfo
} }
static bool HandleBuildRequestRecords (int num, uint8_t * records, uint8_t * clearText)
{
for (int i = 0; i < num; i++)
{
uint8_t * record = records + i*TUNNEL_BUILD_RECORD_SIZE;
if (!memcmp (record + BUILD_REQUEST_RECORD_TO_PEER_OFFSET, (const uint8_t *)i2p::context.GetRouterInfo ().GetIdentHash (), 16))
{
LogPrint (eLogDebug, "I2NP: Build request record ", i, " is ours");
if (!i2p::context.DecryptTunnelBuildRecord (record + BUILD_REQUEST_RECORD_ENCRYPTED_OFFSET, clearText))
{
LogPrint (eLogWarning, "I2NP: Failed to decrypt tunnel build record");
return false;
}
if (!memcmp ((const uint8_t *)i2p::context.GetIdentHash (), clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET, 32) && // if next ident is now ours
!(clearText[ECIES_BUILD_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_ENDPOINT_FLAG)) // and not endpoint
{
LogPrint (eLogWarning, "I2NP: Next ident is ours in tunnel build record");
return false;
}
uint8_t retCode = 0;
// replace record to reply
if (i2p::context.AcceptsTunnels () && i2p::context.GetCongestionLevel (false) < CONGESTION_LEVEL_FULL)
{
auto transitTunnel = i2p::tunnel::CreateTransitTunnel (
bufbe32toh (clearText + ECIES_BUILD_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET),
clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET,
bufbe32toh (clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET),
clearText + ECIES_BUILD_REQUEST_RECORD_LAYER_KEY_OFFSET,
clearText + ECIES_BUILD_REQUEST_RECORD_IV_KEY_OFFSET,
clearText[ECIES_BUILD_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_GATEWAY_FLAG,
clearText[ECIES_BUILD_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_ENDPOINT_FLAG);
if (!i2p::tunnel::tunnels.AddTransitTunnel (transitTunnel))
retCode = 30;
}
else
retCode = 30; // always reject with bandwidth reason (30)
memset (record + ECIES_BUILD_RESPONSE_RECORD_OPTIONS_OFFSET, 0, 2); // no options
record[ECIES_BUILD_RESPONSE_RECORD_RET_OFFSET] = retCode;
// encrypt reply
i2p::crypto::CBCEncryption encryption;
for (int j = 0; j < num; j++)
{
uint8_t * reply = records + j*TUNNEL_BUILD_RECORD_SIZE;
if (j == i)
{
uint8_t nonce[12];
memset (nonce, 0, 12);
auto& noiseState = i2p::context.GetCurrentNoiseState ();
if (!i2p::crypto::AEADChaCha20Poly1305 (reply, TUNNEL_BUILD_RECORD_SIZE - 16,
noiseState.m_H, 32, noiseState.m_CK, nonce, reply, TUNNEL_BUILD_RECORD_SIZE, true)) // encrypt
{
LogPrint (eLogWarning, "I2NP: Reply AEAD encryption failed");
return false;
}
}
else
{
encryption.SetKey (clearText + ECIES_BUILD_REQUEST_RECORD_REPLY_KEY_OFFSET);
encryption.SetIV (clearText + ECIES_BUILD_REQUEST_RECORD_REPLY_IV_OFFSET);
encryption.Encrypt(reply, TUNNEL_BUILD_RECORD_SIZE, reply);
}
}
return true;
}
}
return false;
}
static void HandleVariableTunnelBuildMsg (uint32_t replyMsgID, uint8_t * buf, size_t len)
{
int num = buf[0];
LogPrint (eLogDebug, "I2NP: VariableTunnelBuild ", num, " records");
if (num > i2p::tunnel::MAX_NUM_RECORDS)
{
LogPrint (eLogError, "I2NP: Too many records in VaribleTunnelBuild message ", num);
return;
}
if (len < num*TUNNEL_BUILD_RECORD_SIZE + 1)
{
LogPrint (eLogError, "I2NP: VaribleTunnelBuild message of ", num, " records is too short ", len);
return;
}
auto tunnel = i2p::tunnel::tunnels.GetPendingInboundTunnel (replyMsgID);
if (tunnel)
{
// endpoint of inbound tunnel
LogPrint (eLogDebug, "I2NP: VariableTunnelBuild reply for tunnel ", tunnel->GetTunnelID ());
if (tunnel->HandleTunnelBuildResponse (buf, len))
{
LogPrint (eLogInfo, "I2NP: Inbound tunnel ", tunnel->GetTunnelID (), " has been created");
tunnel->SetState (i2p::tunnel::eTunnelStateEstablished);
i2p::tunnel::tunnels.AddInboundTunnel (tunnel);
}
else
{
LogPrint (eLogInfo, "I2NP: Inbound tunnel ", tunnel->GetTunnelID (), " has been declined");
tunnel->SetState (i2p::tunnel::eTunnelStateBuildFailed);
}
}
else
{
uint8_t clearText[ECIES_BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE];
if (HandleBuildRequestRecords (num, buf + 1, clearText))
{
if (clearText[ECIES_BUILD_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_ENDPOINT_FLAG) // we are endpoint of outboud tunnel
{
// so we send it to reply tunnel
transports.SendMessage (clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET,
CreateTunnelGatewayMsg (bufbe32toh (clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET),
eI2NPVariableTunnelBuildReply, buf, len,
bufbe32toh (clearText + ECIES_BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET)));
}
else
transports.SendMessage (clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET,
CreateI2NPMessage (eI2NPVariableTunnelBuild, buf, len,
bufbe32toh (clearText + ECIES_BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET)));
}
}
}
static void HandleTunnelBuildMsg (uint8_t * buf, size_t len)
{
LogPrint (eLogWarning, "I2NP: TunnelBuild is too old for ECIES router");
}
static void HandleTunnelBuildReplyMsg (uint32_t replyMsgID, uint8_t * buf, size_t len, bool isShort)
{
int num = buf[0];
LogPrint (eLogDebug, "I2NP: TunnelBuildReplyMsg of ", num, " records replyMsgID=", replyMsgID);
if (num > i2p::tunnel::MAX_NUM_RECORDS)
{
LogPrint (eLogError, "I2NP: Too many records in TunnelBuildReply message ", num);
return;
}
size_t recordSize = isShort ? SHORT_TUNNEL_BUILD_RECORD_SIZE : TUNNEL_BUILD_RECORD_SIZE;
if (len < num*recordSize + 1)
{
LogPrint (eLogError, "I2NP: TunnelBuildReply message of ", num, " records is too short ", len);
return;
}
auto tunnel = i2p::tunnel::tunnels.GetPendingOutboundTunnel (replyMsgID);
if (tunnel)
{
// reply for outbound tunnel
if (tunnel->HandleTunnelBuildResponse (buf, len))
{
LogPrint (eLogInfo, "I2NP: Outbound tunnel ", tunnel->GetTunnelID (), " has been created");
tunnel->SetState (i2p::tunnel::eTunnelStateEstablished);
i2p::tunnel::tunnels.AddOutboundTunnel (tunnel);
}
else
{
LogPrint (eLogInfo, "I2NP: Outbound tunnel ", tunnel->GetTunnelID (), " has been declined");
tunnel->SetState (i2p::tunnel::eTunnelStateBuildFailed);
}
}
else
LogPrint (eLogWarning, "I2NP: Pending tunnel for message ", replyMsgID, " not found");
}
static void HandleShortTunnelBuildMsg (uint32_t replyMsgID, uint8_t * buf, size_t len)
{
int num = buf[0];
LogPrint (eLogDebug, "I2NP: ShortTunnelBuild ", num, " records");
if (num > i2p::tunnel::MAX_NUM_RECORDS)
{
LogPrint (eLogError, "I2NP: Too many records in ShortTunnelBuild message ", num);
return;
}
if (len < num*SHORT_TUNNEL_BUILD_RECORD_SIZE + 1)
{
LogPrint (eLogError, "I2NP: ShortTunnelBuild message of ", num, " records is too short ", len);
return;
}
auto tunnel = i2p::tunnel::tunnels.GetPendingInboundTunnel (replyMsgID);
if (tunnel)
{
// endpoint of inbound tunnel
LogPrint (eLogDebug, "I2NP: ShortTunnelBuild reply for tunnel ", tunnel->GetTunnelID ());
if (tunnel->HandleTunnelBuildResponse (buf, len))
{
LogPrint (eLogInfo, "I2NP: Inbound tunnel ", tunnel->GetTunnelID (), " has been created");
tunnel->SetState (i2p::tunnel::eTunnelStateEstablished);
i2p::tunnel::tunnels.AddInboundTunnel (tunnel);
}
else
{
LogPrint (eLogInfo, "I2NP: Inbound tunnel ", tunnel->GetTunnelID (), " has been declined");
tunnel->SetState (i2p::tunnel::eTunnelStateBuildFailed);
}
return;
}
const uint8_t * record = buf + 1;
for (int i = 0; i < num; i++)
{
if (!memcmp (record, (const uint8_t *)i2p::context.GetRouterInfo ().GetIdentHash (), 16))
{
LogPrint (eLogDebug, "I2NP: Short request record ", i, " is ours");
uint8_t clearText[SHORT_REQUEST_RECORD_CLEAR_TEXT_SIZE];
if (!i2p::context.DecryptTunnelShortRequestRecord (record + SHORT_REQUEST_RECORD_ENCRYPTED_OFFSET, clearText))
{
LogPrint (eLogWarning, "I2NP: Can't decrypt short request record ", i);
return;
}
if (clearText[SHORT_REQUEST_RECORD_LAYER_ENCRYPTION_TYPE]) // not AES
{
LogPrint (eLogWarning, "I2NP: Unknown layer encryption type ", clearText[SHORT_REQUEST_RECORD_LAYER_ENCRYPTION_TYPE], " in short request record");
return;
}
auto& noiseState = i2p::context.GetCurrentNoiseState ();
uint8_t replyKey[32]; // AEAD/Chacha20/Poly1305
i2p::crypto::AESKey layerKey, ivKey; // AES
i2p::crypto::HKDF (noiseState.m_CK, nullptr, 0, "SMTunnelReplyKey", noiseState.m_CK);
memcpy (replyKey, noiseState.m_CK + 32, 32);
i2p::crypto::HKDF (noiseState.m_CK, nullptr, 0, "SMTunnelLayerKey", noiseState.m_CK);
memcpy (layerKey, noiseState.m_CK + 32, 32);
bool isEndpoint = clearText[SHORT_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_ENDPOINT_FLAG;
if (isEndpoint)
{
i2p::crypto::HKDF (noiseState.m_CK, nullptr, 0, "TunnelLayerIVKey", noiseState.m_CK);
memcpy (ivKey, noiseState.m_CK + 32, 32);
}
else
{
if (!memcmp ((const uint8_t *)i2p::context.GetIdentHash (), clearText + SHORT_REQUEST_RECORD_NEXT_IDENT_OFFSET, 32)) // if next ident is now ours
{
LogPrint (eLogWarning, "I2NP: Next ident is ours in short request record");
return;
}
memcpy (ivKey, noiseState.m_CK , 32);
}
// check if we accept this tunnel
std::shared_ptr<i2p::tunnel::TransitTunnel> transitTunnel;
uint8_t retCode = 0;
if (!i2p::context.AcceptsTunnels () || i2p::context.GetCongestionLevel (false) >= CONGESTION_LEVEL_FULL)
retCode = 30;
if (!retCode)
{
// create new transit tunnel
transitTunnel = i2p::tunnel::CreateTransitTunnel (
bufbe32toh (clearText + SHORT_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET),
clearText + SHORT_REQUEST_RECORD_NEXT_IDENT_OFFSET,
bufbe32toh (clearText + SHORT_REQUEST_RECORD_NEXT_TUNNEL_OFFSET),
layerKey, ivKey,
clearText[SHORT_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_GATEWAY_FLAG,
clearText[SHORT_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_ENDPOINT_FLAG);
if (!i2p::tunnel::tunnels.AddTransitTunnel (transitTunnel))
retCode = 30;
}
// encrypt reply
uint8_t nonce[12];
memset (nonce, 0, 12);
uint8_t * reply = buf + 1;
for (int j = 0; j < num; j++)
{
nonce[4] = j; // nonce is record #
if (j == i)
{
memset (reply + SHORT_RESPONSE_RECORD_OPTIONS_OFFSET, 0, 2); // no options
reply[SHORT_RESPONSE_RECORD_RET_OFFSET] = retCode;
if (!i2p::crypto::AEADChaCha20Poly1305 (reply, SHORT_TUNNEL_BUILD_RECORD_SIZE - 16,
noiseState.m_H, 32, replyKey, nonce, reply, SHORT_TUNNEL_BUILD_RECORD_SIZE, true)) // encrypt
{
LogPrint (eLogWarning, "I2NP: Short reply AEAD encryption failed");
return;
}
}
else
i2p::crypto::ChaCha20 (reply, SHORT_TUNNEL_BUILD_RECORD_SIZE, replyKey, nonce, reply);
reply += SHORT_TUNNEL_BUILD_RECORD_SIZE;
}
// send reply
auto onDrop = [transitTunnel]()
{
if (transitTunnel)
{
auto t = transitTunnel->GetCreationTime ();
if (t > i2p::tunnel::TUNNEL_EXPIRATION_TIMEOUT)
// make transit tunnel expired
transitTunnel->SetCreationTime (t - i2p::tunnel::TUNNEL_EXPIRATION_TIMEOUT);
}
};
if (isEndpoint)
{
auto replyMsg = NewI2NPShortMessage ();
replyMsg->Concat (buf, len);
replyMsg->FillI2NPMessageHeader (eI2NPShortTunnelBuildReply, bufbe32toh (clearText + SHORT_REQUEST_RECORD_SEND_MSG_ID_OFFSET));
if (transitTunnel) replyMsg->onDrop = onDrop;
if (memcmp ((const uint8_t *)i2p::context.GetIdentHash (),
clearText + SHORT_REQUEST_RECORD_NEXT_IDENT_OFFSET, 32)) // reply IBGW is not local?
{
i2p::crypto::HKDF (noiseState.m_CK, nullptr, 0, "RGarlicKeyAndTag", noiseState.m_CK);
uint64_t tag;
memcpy (&tag, noiseState.m_CK, 8);
// we send it to reply tunnel
transports.SendMessage (clearText + SHORT_REQUEST_RECORD_NEXT_IDENT_OFFSET,
CreateTunnelGatewayMsg (bufbe32toh (clearText + SHORT_REQUEST_RECORD_NEXT_TUNNEL_OFFSET),
i2p::garlic::WrapECIESX25519Message (replyMsg, noiseState.m_CK + 32, tag)));
}
else
{
// IBGW is local
uint32_t tunnelID = bufbe32toh (clearText + SHORT_REQUEST_RECORD_NEXT_TUNNEL_OFFSET);
auto tunnel = i2p::tunnel::tunnels.GetTunnel (tunnelID);
if (tunnel)
{
tunnel->SendTunnelDataMsg (replyMsg);
tunnel->FlushTunnelDataMsgs ();
}
else
LogPrint (eLogWarning, "I2NP: Tunnel ", tunnelID, " not found for short tunnel build reply");
}
}
else
{
auto msg = CreateI2NPMessage (eI2NPShortTunnelBuild, buf, len,
bufbe32toh (clearText + SHORT_REQUEST_RECORD_SEND_MSG_ID_OFFSET));
if (transitTunnel) msg->onDrop = onDrop;
transports.SendMessage (clearText + SHORT_REQUEST_RECORD_NEXT_IDENT_OFFSET, msg);
}
return;
}
record += SHORT_TUNNEL_BUILD_RECORD_SIZE;
}
}
std::shared_ptr<I2NPMessage> CreateTunnelDataMsg (const uint8_t * buf) std::shared_ptr<I2NPMessage> CreateTunnelDataMsg (const uint8_t * buf)
{ {
auto msg = NewI2NPTunnelMessage (false); auto msg = NewI2NPTunnelMessage (false);
@@ -802,41 +466,6 @@ namespace i2p
return l; return l;
} }
void HandleTunnelBuildI2NPMessage (std::shared_ptr<I2NPMessage> msg)
{
if (msg)
{
uint8_t typeID = msg->GetTypeID();
uint32_t msgID = msg->GetMsgID();
LogPrint (eLogDebug, "I2NP: Handling tunnel build message with len=", msg->GetLength(),", type=", (int)typeID, ", msgID=", (unsigned int)msgID);
uint8_t * payload = msg->GetPayload();
auto size = msg->GetPayloadLength();
switch (typeID)
{
case eI2NPVariableTunnelBuild:
HandleVariableTunnelBuildMsg (msgID, payload, size);
break;
case eI2NPShortTunnelBuild:
HandleShortTunnelBuildMsg (msgID, payload, size);
break;
case eI2NPVariableTunnelBuildReply:
HandleTunnelBuildReplyMsg (msgID, payload, size, false);
break;
case eI2NPShortTunnelBuildReply:
HandleTunnelBuildReplyMsg (msgID, payload, size, true);
break;
case eI2NPTunnelBuild:
HandleTunnelBuildMsg (payload, size);
break;
case eI2NPTunnelBuildReply:
// TODO:
break;
default:
LogPrint (eLogError, "I2NP: Unexpected message with type", (int)typeID, " during handling TBM; skipping");
}
}
}
void HandleI2NPMessage (std::shared_ptr<I2NPMessage> msg) void HandleI2NPMessage (std::shared_ptr<I2NPMessage> msg)
{ {
if (msg) if (msg)
@@ -862,12 +491,14 @@ namespace i2p
break; break;
} }
case eI2NPDatabaseStore: case eI2NPDatabaseStore:
case eI2NPDatabaseSearchReply:
// forward to netDb if came directly or through exploratory tunnel as response to our request // forward to netDb if came directly or through exploratory tunnel as response to our request
if (!msg->from || !msg->from->GetTunnelPool () || msg->from->GetTunnelPool ()->IsExploratory ()) if (!msg->from || !msg->from->GetTunnelPool () || msg->from->GetTunnelPool ()->IsExploratory ())
i2p::data::netdb.PostI2NPMsg (msg); i2p::data::netdb.PostI2NPMsg (msg);
break; break;
case eI2NPDatabaseSearchReply:
if (!msg->from || !msg->from->GetTunnelPool () || msg->from->GetTunnelPool ()->IsExploratory ())
i2p::data::netdb.PostDatabaseSearchReplyMsg (msg);
break;
case eI2NPDatabaseLookup: case eI2NPDatabaseLookup:
// forward to netDb if floodfill and came directly // forward to netDb if floodfill and came directly
if (!msg->from && i2p::context.IsFloodfill ()) if (!msg->from && i2p::context.IsFloodfill ())
@@ -930,14 +561,8 @@ namespace i2p
void I2NPMessagesHandler::Flush () void I2NPMessagesHandler::Flush ()
{ {
if (!m_TunnelMsgs.empty ()) if (!m_TunnelMsgs.empty ())
{
i2p::tunnel::tunnels.PostTunnelData (m_TunnelMsgs); i2p::tunnel::tunnels.PostTunnelData (m_TunnelMsgs);
m_TunnelMsgs.clear ();
}
if (!m_TunnelGatewayMsgs.empty ()) if (!m_TunnelGatewayMsgs.empty ())
{
i2p::tunnel::tunnels.PostTunnelData (m_TunnelGatewayMsgs); i2p::tunnel::tunnels.PostTunnelData (m_TunnelGatewayMsgs);
m_TunnelGatewayMsgs.clear ();
}
} }
} }

View File

@@ -11,8 +11,9 @@
#include <inttypes.h> #include <inttypes.h>
#include <string.h> #include <string.h>
#include <set> #include <unordered_set>
#include <memory> #include <memory>
#include <list>
#include <functional> #include <functional>
#include "Crypto.h" #include "Crypto.h"
#include "I2PEndian.h" #include "I2PEndian.h"
@@ -107,7 +108,6 @@ namespace i2p
enum I2NPMessageType enum I2NPMessageType
{ {
eI2NPDummyMsg = 0,
eI2NPDatabaseStore = 1, eI2NPDatabaseStore = 1,
eI2NPDatabaseLookup = 2, eI2NPDatabaseLookup = 2,
eI2NPDatabaseSearchReply = 3, eI2NPDatabaseSearchReply = 3,
@@ -294,9 +294,9 @@ namespace tunnel
std::shared_ptr<I2NPMessage> CreateTunnelTestMsg (uint32_t msgID); std::shared_ptr<I2NPMessage> CreateTunnelTestMsg (uint32_t msgID);
std::shared_ptr<I2NPMessage> CreateDeliveryStatusMsg (uint32_t msgID); std::shared_ptr<I2NPMessage> CreateDeliveryStatusMsg (uint32_t msgID);
std::shared_ptr<I2NPMessage> CreateRouterInfoDatabaseLookupMsg (const uint8_t * key, const uint8_t * from, std::shared_ptr<I2NPMessage> CreateRouterInfoDatabaseLookupMsg (const uint8_t * key, const uint8_t * from,
uint32_t replyTunnelID, bool exploratory = false, std::set<i2p::data::IdentHash> * excludedPeers = nullptr); uint32_t replyTunnelID, bool exploratory = false, std::unordered_set<i2p::data::IdentHash> * excludedPeers = nullptr);
std::shared_ptr<I2NPMessage> CreateLeaseSetDatabaseLookupMsg (const i2p::data::IdentHash& dest, std::shared_ptr<I2NPMessage> CreateLeaseSetDatabaseLookupMsg (const i2p::data::IdentHash& dest,
const std::set<i2p::data::IdentHash>& excludedFloodfills, const std::unordered_set<i2p::data::IdentHash>& excludedFloodfills,
std::shared_ptr<const i2p::tunnel::InboundTunnel> replyTunnel, std::shared_ptr<const i2p::tunnel::InboundTunnel> replyTunnel,
const uint8_t * replyKey, const uint8_t * replyTag, bool replyECIES = false); const uint8_t * replyKey, const uint8_t * replyTag, bool replyECIES = false);
std::shared_ptr<I2NPMessage> CreateDatabaseSearchReply (const i2p::data::IdentHash& ident, std::vector<i2p::data::IdentHash> routers); std::shared_ptr<I2NPMessage> CreateDatabaseSearchReply (const i2p::data::IdentHash& ident, std::vector<i2p::data::IdentHash> routers);
@@ -316,7 +316,6 @@ namespace tunnel
std::shared_ptr<I2NPMessage> CreateTunnelGatewayMsg (uint32_t tunnelID, std::shared_ptr<I2NPMessage> msg); std::shared_ptr<I2NPMessage> CreateTunnelGatewayMsg (uint32_t tunnelID, std::shared_ptr<I2NPMessage> msg);
size_t GetI2NPMessageLength (const uint8_t * msg, size_t len); size_t GetI2NPMessageLength (const uint8_t * msg, size_t len);
void HandleTunnelBuildI2NPMessage (std::shared_ptr<I2NPMessage> msg);
void HandleI2NPMessage (std::shared_ptr<I2NPMessage> msg); void HandleI2NPMessage (std::shared_ptr<I2NPMessage> msg);
class I2NPMessagesHandler class I2NPMessagesHandler
@@ -329,7 +328,7 @@ namespace tunnel
private: private:
std::vector<std::shared_ptr<I2NPMessage> > m_TunnelMsgs, m_TunnelGatewayMsgs; std::list<std::shared_ptr<I2NPMessage> > m_TunnelMsgs, m_TunnelGatewayMsgs;
}; };
} }

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2023, The PurpleI2P Project * Copyright (c) 2013-2025, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@@ -27,18 +27,15 @@ namespace data
size_t Identity::FromBuffer (const uint8_t * buf, size_t len) size_t Identity::FromBuffer (const uint8_t * buf, size_t len)
{ {
if ( len < DEFAULT_IDENTITY_SIZE ) { if (len < DEFAULT_IDENTITY_SIZE) return 0; // buffer too small, don't overflow
// buffer too small, don't overflow memcpy (this, buf, DEFAULT_IDENTITY_SIZE);
return 0;
}
memcpy (publicKey, buf, DEFAULT_IDENTITY_SIZE);
return DEFAULT_IDENTITY_SIZE; return DEFAULT_IDENTITY_SIZE;
} }
IdentHash Identity::Hash () const IdentHash Identity::Hash () const
{ {
IdentHash hash; IdentHash hash;
SHA256(publicKey, DEFAULT_IDENTITY_SIZE, hash); SHA256((const uint8_t *)this, DEFAULT_IDENTITY_SIZE, hash);
return hash; return hash;
} }
@@ -262,11 +259,11 @@ namespace data
return fullLen; return fullLen;
} }
size_t IdentityEx::FromBase64(const std::string& s) size_t IdentityEx::FromBase64(std::string_view s)
{ {
const size_t slen = s.length(); const size_t slen = s.length();
std::vector<uint8_t> buf(slen); // binary data can't exceed base64 std::vector<uint8_t> buf(slen); // binary data can't exceed base64
const size_t len = Base64ToByteStream (s.c_str(), slen, buf.data(), slen); const size_t len = Base64ToByteStream (s.data(), slen, buf.data(), slen);
return FromBuffer (buf.data(), len); return FromBuffer (buf.data(), len);
} }
@@ -420,6 +417,14 @@ namespace data
return CreateEncryptor (GetCryptoKeyType (), key); return CreateEncryptor (GetCryptoKeyType (), key);
} }
size_t GetIdentityBufferLen (const uint8_t * buf, size_t len)
{
if (len < DEFAULT_IDENTITY_SIZE) return 0;
size_t l = DEFAULT_IDENTITY_SIZE + bufbe16toh (buf + DEFAULT_IDENTITY_SIZE - 2);
if (l > len) return 0;
return l;
}
PrivateKeys& PrivateKeys::operator=(const Keys& keys) PrivateKeys& PrivateKeys::operator=(const Keys& keys)
{ {
m_Public = std::make_shared<IdentityEx>(Identity (keys)); m_Public = std::make_shared<IdentityEx>(Identity (keys));
@@ -479,7 +484,12 @@ namespace data
{ {
// offline information // offline information
const uint8_t * offlineInfo = buf + ret; const uint8_t * offlineInfo = buf + ret;
ret += 4; // expires timestamp uint32_t expires = bufbe32toh (buf + ret); ret += 4; // expires timestamp
if (expires < i2p::util::GetSecondsSinceEpoch ())
{
LogPrint (eLogError, "Identity: Offline signature expired");
return 0;
}
SigningKeyType keyType = bufbe16toh (buf + ret); ret += 2; // key type SigningKeyType keyType = bufbe16toh (buf + ret); ret += 2; // key type
std::unique_ptr<i2p::crypto::Verifier> transientVerifier (IdentityEx::CreateVerifier (keyType)); std::unique_ptr<i2p::crypto::Verifier> transientVerifier (IdentityEx::CreateVerifier (keyType));
if (!transientVerifier) return 0; if (!transientVerifier) return 0;
@@ -715,9 +725,7 @@ namespace data
case SIGNING_KEY_TYPE_RSA_SHA384_3072: case SIGNING_KEY_TYPE_RSA_SHA384_3072:
case SIGNING_KEY_TYPE_RSA_SHA512_4096: case SIGNING_KEY_TYPE_RSA_SHA512_4096:
LogPrint (eLogWarning, "Identity: RSA signature type is not supported. Creating EdDSA"); LogPrint (eLogWarning, "Identity: RSA signature type is not supported. Creating EdDSA");
#if (__cplusplus >= 201703L) // C++ 17 or higher
[[fallthrough]]; [[fallthrough]];
#endif
// no break here // no break here
case SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519: case SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519:
i2p::crypto::CreateEDDSA25519RandomKeys (priv, pub); i2p::crypto::CreateEDDSA25519RandomKeys (priv, pub);
@@ -790,11 +798,14 @@ namespace data
return keys; return keys;
} }
IdentHash CreateRoutingKey (const IdentHash& ident) IdentHash CreateRoutingKey (const IdentHash& ident, bool nextDay)
{ {
uint8_t buf[41]; // ident + yyyymmdd uint8_t buf[41]; // ident + yyyymmdd
memcpy (buf, (const uint8_t *)ident, 32); memcpy (buf, (const uint8_t *)ident, 32);
i2p::util::GetCurrentDate ((char *)(buf + 32)); if (nextDay)
i2p::util::GetNextDayDate ((char *)(buf + 32));
else
i2p::util::GetCurrentDate ((char *)(buf + 32));
IdentHash key; IdentHash key;
SHA256(buf, 40, key); SHA256(buf, 40, key);
return key; return key;

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2023, The PurpleI2P Project * Copyright (c) 2013-2025, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@@ -12,6 +12,7 @@
#include <inttypes.h> #include <inttypes.h>
#include <string.h> #include <string.h>
#include <string> #include <string>
#include <string_view>
#include <memory> #include <memory>
#include <vector> #include <vector>
#include "Base.h" #include "Base.h"
@@ -99,7 +100,7 @@ namespace data
size_t FromBuffer (const uint8_t * buf, size_t len); size_t FromBuffer (const uint8_t * buf, size_t len);
size_t ToBuffer (uint8_t * buf, size_t len) const; size_t ToBuffer (uint8_t * buf, size_t len) const;
size_t FromBase64(const std::string& s); size_t FromBase64(std::string_view s);
std::string ToBase64 () const; std::string ToBase64 () const;
const Identity& GetStandardIdentity () const { return m_StandardIdentity; }; const Identity& GetStandardIdentity () const { return m_StandardIdentity; };
@@ -136,6 +137,8 @@ namespace data
uint8_t m_ExtendedBuffer[MAX_EXTENDED_BUFFER_SIZE]; uint8_t m_ExtendedBuffer[MAX_EXTENDED_BUFFER_SIZE];
}; };
size_t GetIdentityBufferLen (const uint8_t * buf, size_t len); // return actual identity length in buffer
class PrivateKeys // for eepsites class PrivateKeys // for eepsites
{ {
public: public:
@@ -206,7 +209,7 @@ namespace data
bool operator< (const XORMetric& other) const { return memcmp (metric, other.metric, 32) < 0; }; bool operator< (const XORMetric& other) const { return memcmp (metric, other.metric, 32) < 0; };
}; };
IdentHash CreateRoutingKey (const IdentHash& ident); IdentHash CreateRoutingKey (const IdentHash& ident, bool nextDay = false);
XORMetric operator^(const IdentHash& key1, const IdentHash& key2); XORMetric operator^(const IdentHash& key1, const IdentHash& key2);
// destination for delivery instructions // destination for delivery instructions

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2023, The PurpleI2P Project * Copyright (c) 2013-2024, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@@ -425,6 +425,16 @@ namespace data
if (offset + 1 > len) return 0; if (offset + 1 > len) return 0;
int numLeases = buf[offset]; offset++; int numLeases = buf[offset]; offset++;
auto ts = i2p::util::GetMillisecondsSinceEpoch (); auto ts = i2p::util::GetMillisecondsSinceEpoch ();
if (GetExpirationTime () > ts + LEASESET_EXPIRATION_TIME_THRESHOLD)
{
LogPrint (eLogWarning, "LeaseSet2: Expiration time is from future ", GetExpirationTime ()/1000LL);
return 0;
}
if (ts > m_PublishedTimestamp*1000LL + LEASESET_EXPIRATION_TIME_THRESHOLD)
{
LogPrint (eLogWarning, "LeaseSet2: Published time is too old ", m_PublishedTimestamp);
return 0;
}
if (IsStoreLeases ()) if (IsStoreLeases ())
{ {
UpdateLeasesBegin (); UpdateLeasesBegin ();
@@ -435,6 +445,11 @@ namespace data
lease.tunnelGateway = buf + offset; offset += 32; // gateway lease.tunnelGateway = buf + offset; offset += 32; // gateway
lease.tunnelID = bufbe32toh (buf + offset); offset += 4; // tunnel ID lease.tunnelID = bufbe32toh (buf + offset); offset += 4; // tunnel ID
lease.endDate = bufbe32toh (buf + offset)*1000LL; offset += 4; // end date lease.endDate = bufbe32toh (buf + offset)*1000LL; offset += 4; // end date
if (lease.endDate > ts + LEASESET_EXPIRATION_TIME_THRESHOLD)
{
LogPrint (eLogWarning, "LeaseSet2: Lease end date is from future ", lease.endDate);
return 0;
}
UpdateLease (lease, ts); UpdateLease (lease, ts);
} }
UpdateLeasesEnd (); UpdateLeasesEnd ();
@@ -728,25 +743,41 @@ namespace data
memset (m_Buffer + offset, 0, signingKeyLen); memset (m_Buffer + offset, 0, signingKeyLen);
offset += signingKeyLen; offset += signingKeyLen;
// num leases // num leases
auto numLeasesPos = offset;
m_Buffer[offset] = num; m_Buffer[offset] = num;
offset++; offset++;
// leases // leases
m_Leases = m_Buffer + offset; m_Leases = m_Buffer + offset;
auto currentTime = i2p::util::GetMillisecondsSinceEpoch (); auto currentTime = i2p::util::GetMillisecondsSinceEpoch ();
int skipped = 0;
for (int i = 0; i < num; i++) for (int i = 0; i < num; i++)
{ {
uint64_t ts = tunnels[i]->GetCreationTime () + i2p::tunnel::TUNNEL_EXPIRATION_TIMEOUT - i2p::tunnel::TUNNEL_EXPIRATION_THRESHOLD; // 1 minute before expiration
ts *= 1000; // in milliseconds
if (ts <= currentTime)
{
// already expired, skip
skipped++;
continue;
}
if (ts > m_ExpirationTime) m_ExpirationTime = ts;
// make sure leaseset is newer than previous, but adding some time to expiration date
ts += (currentTime - tunnels[i]->GetCreationTime ()*1000LL)*2/i2p::tunnel::TUNNEL_EXPIRATION_TIMEOUT; // up to 2 secs
memcpy (m_Buffer + offset, tunnels[i]->GetNextIdentHash (), 32); memcpy (m_Buffer + offset, tunnels[i]->GetNextIdentHash (), 32);
offset += 32; // gateway id offset += 32; // gateway id
htobe32buf (m_Buffer + offset, tunnels[i]->GetNextTunnelID ()); htobe32buf (m_Buffer + offset, tunnels[i]->GetNextTunnelID ());
offset += 4; // tunnel id offset += 4; // tunnel id
uint64_t ts = tunnels[i]->GetCreationTime () + i2p::tunnel::TUNNEL_EXPIRATION_TIMEOUT - i2p::tunnel::TUNNEL_EXPIRATION_THRESHOLD; // 1 minute before expiration
ts *= 1000; // in milliseconds
if (ts > m_ExpirationTime) m_ExpirationTime = ts;
// make sure leaseset is newer than previous, but adding some time to expiration date
ts += (currentTime - tunnels[i]->GetCreationTime ()*1000LL)*2/i2p::tunnel::TUNNEL_EXPIRATION_TIMEOUT; // up to 2 secs
htobe64buf (m_Buffer + offset, ts); htobe64buf (m_Buffer + offset, ts);
offset += 8; // end date offset += 8; // end date
} }
if (skipped > 0)
{
// adjust num leases
if (skipped > num) skipped = num;
num -= skipped;
m_BufferLen -= skipped*LEASE_SIZE;
m_Buffer[numLeasesPos] = num;
}
// we don't sign it yet. must be signed later on // we don't sign it yet. must be signed later on
} }
@@ -808,7 +839,7 @@ namespace data
LocalLeaseSet2::LocalLeaseSet2 (uint8_t storeType, const i2p::data::PrivateKeys& keys, LocalLeaseSet2::LocalLeaseSet2 (uint8_t storeType, const i2p::data::PrivateKeys& keys,
const KeySections& encryptionKeys, const std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> >& tunnels, const KeySections& encryptionKeys, const std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> >& tunnels,
bool isPublic, bool isPublishedEncrypted): bool isPublic, uint64_t publishedTimestamp, bool isPublishedEncrypted):
LocalLeaseSet (keys.GetPublic (), nullptr, 0) LocalLeaseSet (keys.GetPublic (), nullptr, 0)
{ {
auto identity = keys.GetPublic (); auto identity = keys.GetPublic ();
@@ -837,8 +868,7 @@ namespace data
m_Buffer[0] = storeType; m_Buffer[0] = storeType;
// LS2 header // LS2 header
auto offset = identity->ToBuffer (m_Buffer + 1, m_BufferLen) + 1; auto offset = identity->ToBuffer (m_Buffer + 1, m_BufferLen) + 1;
auto timestamp = i2p::util::GetSecondsSinceEpoch (); htobe32buf (m_Buffer + offset, publishedTimestamp); offset += 4; // published timestamp (seconds)
htobe32buf (m_Buffer + offset, timestamp); offset += 4; // published timestamp (seconds)
uint8_t * expiresBuf = m_Buffer + offset; offset += 2; // expires, fill later uint8_t * expiresBuf = m_Buffer + offset; offset += 2; // expires, fill later
htobe16buf (m_Buffer + offset, flags); offset += 2; // flags htobe16buf (m_Buffer + offset, flags); offset += 2; // flags
if (keys.IsOfflineSignature ()) if (keys.IsOfflineSignature ())
@@ -859,29 +889,44 @@ namespace data
} }
// leases // leases
uint32_t expirationTime = 0; // in seconds uint32_t expirationTime = 0; // in seconds
int skipped = 0; auto numLeasesPos = offset;
m_Buffer[offset] = num; offset++; // num leases m_Buffer[offset] = num; offset++; // num leases
for (int i = 0; i < num; i++) for (int i = 0; i < num; i++)
{ {
auto ts = tunnels[i]->GetCreationTime () + i2p::tunnel::TUNNEL_EXPIRATION_TIMEOUT - i2p::tunnel::TUNNEL_EXPIRATION_THRESHOLD; // in seconds, 1 minute before expiration
if (ts <= publishedTimestamp)
{
// already expired, skip
skipped++;
continue;
}
if (ts > expirationTime) expirationTime = ts;
memcpy (m_Buffer + offset, tunnels[i]->GetNextIdentHash (), 32); memcpy (m_Buffer + offset, tunnels[i]->GetNextIdentHash (), 32);
offset += 32; // gateway id offset += 32; // gateway id
htobe32buf (m_Buffer + offset, tunnels[i]->GetNextTunnelID ()); htobe32buf (m_Buffer + offset, tunnels[i]->GetNextTunnelID ());
offset += 4; // tunnel id offset += 4; // tunnel id
auto ts = tunnels[i]->GetCreationTime () + i2p::tunnel::TUNNEL_EXPIRATION_TIMEOUT - i2p::tunnel::TUNNEL_EXPIRATION_THRESHOLD; // in seconds, 1 minute before expiration
if (ts > expirationTime) expirationTime = ts;
htobe32buf (m_Buffer + offset, ts); htobe32buf (m_Buffer + offset, ts);
offset += 4; // end date offset += 4; // end date
} }
if (skipped > 0)
{
// adjust num leases
if (skipped > num) skipped = num;
num -= skipped;
m_BufferLen -= skipped*LEASE2_SIZE;
m_Buffer[numLeasesPos] = num;
}
// update expiration // update expiration
if (expirationTime) if (expirationTime)
{ {
SetExpirationTime (expirationTime*1000LL); SetExpirationTime (expirationTime*1000LL);
auto expires = (int)expirationTime - timestamp; auto expires = (int)expirationTime - publishedTimestamp;
htobe16buf (expiresBuf, expires > 0 ? expires : 0); htobe16buf (expiresBuf, expires > 0 ? expires : 0);
} }
else else
{ {
// no tunnels or withdraw // no tunnels or withdraw
SetExpirationTime (timestamp*1000LL); SetExpirationTime (publishedTimestamp*1000LL);
memset (expiresBuf, 0, 2); // expires immeditely memset (expiresBuf, 0, 2); // expires immeditely
} }
// sign // sign

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2023, The PurpleI2P Project * Copyright (c) 2013-2024, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@@ -62,7 +62,8 @@ namespace data
const size_t LEASE_SIZE = 44; // 32 + 4 + 8 const size_t LEASE_SIZE = 44; // 32 + 4 + 8
const size_t LEASE2_SIZE = 40; // 32 + 4 + 4 const size_t LEASE2_SIZE = 40; // 32 + 4 + 4
const uint8_t MAX_NUM_LEASES = 16; const uint8_t MAX_NUM_LEASES = 16;
const uint64_t LEASESET_EXPIRATION_TIME_THRESHOLD = 12*60*1000; // in milliseconds
const uint8_t NETDB_STORE_TYPE_LEASESET = 1; const uint8_t NETDB_STORE_TYPE_LEASESET = 1;
class LeaseSet: public RoutingDestination class LeaseSet: public RoutingDestination
{ {
@@ -180,7 +181,7 @@ namespace data
private: private:
uint8_t m_StoreType; uint8_t m_StoreType;
uint32_t m_PublishedTimestamp = 0; uint32_t m_PublishedTimestamp = 0; // seconds
bool m_IsPublic = true, m_IsPublishedEncrypted = false; bool m_IsPublic = true, m_IsPublishedEncrypted = false;
std::shared_ptr<i2p::crypto::Verifier> m_TransientVerifier; std::shared_ptr<i2p::crypto::Verifier> m_TransientVerifier;
CryptoKeyType m_EncryptionType; CryptoKeyType m_EncryptionType;
@@ -256,7 +257,8 @@ namespace data
LocalLeaseSet2 (uint8_t storeType, const i2p::data::PrivateKeys& keys, LocalLeaseSet2 (uint8_t storeType, const i2p::data::PrivateKeys& keys,
const KeySections& encryptionKeys, const KeySections& encryptionKeys,
const std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> >& tunnels, const std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> >& tunnels,
bool isPublic, bool isPublishedEncrypted = false); bool isPublic, uint64_t publishedTimestamp,
bool isPublishedEncrypted = false);
LocalLeaseSet2 (uint8_t storeType, std::shared_ptr<const IdentityEx> identity, const uint8_t * buf, size_t len); // from I2CP LocalLeaseSet2 (uint8_t storeType, std::shared_ptr<const IdentityEx> identity, const uint8_t * buf, size_t len); // from I2CP

View File

@@ -172,16 +172,6 @@ void LogPrint (std::stringstream& s, TValue&& arg) noexcept
s << std::forward<TValue>(arg); 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
{
LogPrint (s, std::forward<TValue>(arg));
LogPrint (s, std::forward<TArgs>(args)...);
}
#endif
/** /**
* @brief Create log message and send it to queue * @brief Create log message and send it to queue
* @param level Message level (eLogError, eLogInfo, ...) * @param level Message level (eLogError, eLogInfo, ...)
@@ -194,13 +184,7 @@ void LogPrint (LogLevel level, TArgs&&... args) noexcept
// fold message to single string // fold message to single string
std::stringstream ss; std::stringstream ss;
#if (__cplusplus >= 201703L) // C++ 17 or higher
(LogPrint (ss, std::forward<TArgs>(args)), ...); (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), std::move(ss).str()); auto msg = std::make_shared<i2p::log::LogMsg>(level, std::time(nullptr), std::move(ss).str());
msg->tid = std::this_thread::get_id(); msg->tid = std::this_thread::get_id();
i2p::log::Logger().Append(msg); i2p::log::Logger().Append(msg);
@@ -217,11 +201,7 @@ void ThrowFatal (TArgs&&... args) noexcept
if (!f) return; if (!f) return;
// fold message to single string // fold message to single string
std::stringstream ss(""); std::stringstream ss("");
#if (__cplusplus >= 201703L) // C++ 17 or higher
(LogPrint (ss, std::forward<TArgs>(args)), ...); (LogPrint (ss, std::forward<TArgs>(args)), ...);
#else
LogPrint (ss, std::forward<TArgs>(args)...);
#endif
f (ss.str ()); f (ss.str ());
} }

File diff suppressed because it is too large Load Diff

View File

@@ -14,6 +14,7 @@
#include <list> #include <list>
#include <map> #include <map>
#include <array> #include <array>
#include <random>
#include <openssl/bn.h> #include <openssl/bn.h>
#include <openssl/evp.h> #include <openssl/evp.h>
#include <boost/asio.hpp> #include <boost/asio.hpp>
@@ -35,8 +36,10 @@ namespace transport
const int NTCP2_CONNECT_TIMEOUT = 5; // 5 seconds const int NTCP2_CONNECT_TIMEOUT = 5; // 5 seconds
const int NTCP2_ESTABLISH_TIMEOUT = 10; // 10 seconds const int NTCP2_ESTABLISH_TIMEOUT = 10; // 10 seconds
const int NTCP2_TERMINATION_TIMEOUT = 120; // 2 minutes const int NTCP2_TERMINATION_TIMEOUT = 115; // 2 minutes - 5 seconds
const int NTCP2_TERMINATION_CHECK_TIMEOUT = 30; // 30 seconds const int NTCP2_TERMINATION_TIMEOUT_VARIANCE = 10; // 10 seconds
const int NTCP2_TERMINATION_CHECK_TIMEOUT = 28; // 28 seconds
const int NTCP2_TERMINATION_CHECK_TIMEOUT_VARIANCE = 5; // 5 seconds
const int NTCP2_RECEIVE_BUFFER_DELETION_TIMEOUT = 3; // 3 seconds const int NTCP2_RECEIVE_BUFFER_DELETION_TIMEOUT = 3; // 3 seconds
const int NTCP2_ROUTERINFO_RESEND_INTERVAL = 25*60; // 25 minuntes in seconds const int NTCP2_ROUTERINFO_RESEND_INTERVAL = 25*60; // 25 minuntes in seconds
const int NTCP2_ROUTERINFO_RESEND_INTERVAL_THRESHOLD = 25*60; // 25 minuntes const int NTCP2_ROUTERINFO_RESEND_INTERVAL_THRESHOLD = 25*60; // 25 minuntes
@@ -92,21 +95,21 @@ namespace transport
const uint8_t * GetCK () const { return m_CK; }; const uint8_t * GetCK () const { return m_CK; };
const uint8_t * GetH () const { return m_H; }; const uint8_t * GetH () const { return m_H; };
void KDF1Alice (); bool KDF1Alice ();
void KDF1Bob (); bool KDF1Bob ();
void KDF2Alice (); bool KDF2Alice ();
void KDF2Bob (); bool KDF2Bob ();
void KDF3Alice (); // for SessionConfirmed part 2 bool KDF3Alice (); // for SessionConfirmed part 2
void KDF3Bob (); bool KDF3Bob ();
void KeyDerivationFunction1 (const uint8_t * pub, i2p::crypto::X25519Keys& priv, const uint8_t * rs, const uint8_t * epub); // for SessionRequest, (pub, priv) for DH bool KeyDerivationFunction1 (const uint8_t * pub, i2p::crypto::X25519Keys& priv, const uint8_t * rs, const uint8_t * epub); // for SessionRequest, (pub, priv) for DH
void KeyDerivationFunction2 (const uint8_t * sessionRequest, size_t sessionRequestLen, const uint8_t * epub); // for SessionCreate bool KeyDerivationFunction2 (const uint8_t * sessionRequest, size_t sessionRequestLen, const uint8_t * epub); // for SessionCreate
void CreateEphemeralKey (); void CreateEphemeralKey ();
void CreateSessionRequestMessage (); bool CreateSessionRequestMessage (std::mt19937& rng);
void CreateSessionCreatedMessage (); bool CreateSessionCreatedMessage (std::mt19937& rng);
void CreateSessionConfirmedMessagePart1 (const uint8_t * nonce); void CreateSessionConfirmedMessagePart1 (const uint8_t * nonce);
void CreateSessionConfirmedMessagePart2 (const uint8_t * nonce); bool CreateSessionConfirmedMessagePart2 (const uint8_t * nonce);
bool ProcessSessionRequestMessage (uint16_t& paddingLen, bool& clockSkew); bool ProcessSessionRequestMessage (uint16_t& paddingLen, bool& clockSkew);
bool ProcessSessionCreatedMessage (uint16_t& paddingLen); bool ProcessSessionCreatedMessage (uint16_t& paddingLen);
@@ -144,14 +147,16 @@ namespace transport
void SetRemoteEndpoint (const boost::asio::ip::tcp::endpoint& ep) { m_RemoteEndpoint = ep; }; void SetRemoteEndpoint (const boost::asio::ip::tcp::endpoint& ep) { m_RemoteEndpoint = ep; };
bool IsEstablished () const override { return m_IsEstablished; }; bool IsEstablished () const override { return m_IsEstablished; };
i2p::data::RouterInfo::SupportedTransports GetTransportType () const override;
bool IsTerminated () const { return m_IsTerminated; }; bool IsTerminated () const { return m_IsTerminated; };
void ClientLogin (); // Alice void ClientLogin (); // Alice
void ServerLogin (); // Bob void ServerLogin (); // Bob
void SendLocalRouterInfo (bool update) override; // after handshake or by update void SendLocalRouterInfo (bool update) override; // after handshake or by update
void SendI2NPMessages (const std::vector<std::shared_ptr<I2NPMessage> >& msgs) override; void SendI2NPMessages (std::list<std::shared_ptr<I2NPMessage> >& msgs) override;
void MoveSendQueue (std::shared_ptr<NTCP2Session> other);
private: private:
void Established (); void Established ();
@@ -168,13 +173,17 @@ namespace transport
void HandleSessionRequestSent (const boost::system::error_code& ecode, std::size_t bytes_transferred); void HandleSessionRequestSent (const boost::system::error_code& ecode, std::size_t bytes_transferred);
void HandleSessionRequestReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred); void HandleSessionRequestReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred);
void ProcessSessionRequest (size_t len);
void HandleSessionRequestPaddingReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred); void HandleSessionRequestPaddingReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred);
void HandleSessionCreatedSent (const boost::system::error_code& ecode, std::size_t bytes_transferred); void HandleSessionCreatedSent (const boost::system::error_code& ecode, std::size_t bytes_transferred);
void HandleSessionCreatedReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred); void HandleSessionCreatedReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred);
void ProcessSessionCreated (size_t len);
void HandleSessionCreatedPaddingReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred); void HandleSessionCreatedPaddingReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred);
void HandleSessionConfirmedSent (const boost::system::error_code& ecode, std::size_t bytes_transferred); void HandleSessionConfirmedSent (const boost::system::error_code& ecode, std::size_t bytes_transferred);
void HandleSessionConfirmedReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred); void HandleSessionConfirmedReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred);
void ProcessSessionConfirmed ();
void EstablishSessionAfterSessionConfirmed (std::shared_ptr<std::vector<uint8_t> > buf, size_t size);
// data // data
void ReceiveLength (); void ReceiveLength ();
void HandleReceivedLength (const boost::system::error_code& ecode, std::size_t bytes_transferred); void HandleReceivedLength (const boost::system::error_code& ecode, std::size_t bytes_transferred);
@@ -192,7 +201,7 @@ namespace transport
void SendRouterInfo (); void SendRouterInfo ();
void SendTermination (NTCP2TerminationReason reason); void SendTermination (NTCP2TerminationReason reason);
void SendTerminationAndTerminate (NTCP2TerminationReason reason); void SendTerminationAndTerminate (NTCP2TerminationReason reason);
void PostI2NPMessages (std::vector<std::shared_ptr<I2NPMessage> > msgs); void PostI2NPMessages ();
private: private:
@@ -225,13 +234,28 @@ namespace transport
bool m_IsSending, m_IsReceiving; bool m_IsSending, m_IsReceiving;
std::list<std::shared_ptr<I2NPMessage> > m_SendQueue; std::list<std::shared_ptr<I2NPMessage> > m_SendQueue;
uint64_t m_NextRouterInfoResendTime; // seconds since epoch uint64_t m_NextRouterInfoResendTime; // seconds since epoch
std::list<std::shared_ptr<I2NPMessage> > m_IntermediateQueue; // from transports
mutable std::mutex m_IntermediateQueueMutex;
uint16_t m_PaddingSizes[16]; uint16_t m_PaddingSizes[16];
int m_NextPaddingSize; int m_NextPaddingSize;
}; };
class NTCP2Server: private i2p::util::RunnableServiceWithWork class NTCP2Server: private i2p::util::RunnableServiceWithWork
{ {
private:
class EstablisherService: public i2p::util::RunnableServiceWithWork
{
public:
EstablisherService (): RunnableServiceWithWork ("NTCP2e") {};
auto& GetService () { return GetIOService (); };
void Start () { StartIOService (); };
void Stop () { StopIOService (); };
};
public: public:
enum ProxyType enum ProxyType
@@ -240,13 +264,20 @@ namespace transport
eSocksProxy, eSocksProxy,
eHTTPProxy eHTTPProxy
}; };
NTCP2Server (); NTCP2Server ();
~NTCP2Server (); ~NTCP2Server ();
void Start (); void Start ();
void Stop (); void Stop ();
boost::asio::io_service& GetService () { return GetIOService (); }; auto& GetService () { return GetIOService (); };
auto& GetEstablisherService () { return m_EstablisherService.GetService (); };
std::mt19937& GetRng () { return m_Rng; };
void AEADChaCha20Poly1305Encrypt (const std::vector<std::pair<uint8_t *, size_t> >& bufs,
const uint8_t * key, const uint8_t * nonce, uint8_t * mac);
bool AEADChaCha20Poly1305Decrypt (const uint8_t * msg, size_t msgLen, const uint8_t * ad, size_t adLen,
const uint8_t * key, const uint8_t * nonce, uint8_t * buf, size_t len);
bool AddNTCP2Session (std::shared_ptr<NTCP2Session> session, bool incoming = false); bool AddNTCP2Session (std::shared_ptr<NTCP2Session> session, bool incoming = false);
void RemoveNTCP2Session (std::shared_ptr<NTCP2Session> session); void RemoveNTCP2Session (std::shared_ptr<NTCP2Session> session);
@@ -284,8 +315,13 @@ namespace transport
uint16_t m_ProxyPort; uint16_t m_ProxyPort;
boost::asio::ip::tcp::resolver m_Resolver; boost::asio::ip::tcp::resolver m_Resolver;
std::unique_ptr<boost::asio::ip::tcp::endpoint> m_ProxyEndpoint; std::unique_ptr<boost::asio::ip::tcp::endpoint> m_ProxyEndpoint;
std::shared_ptr<boost::asio::ip::tcp::endpoint> m_Address4, m_Address6, m_YggdrasilAddress; std::shared_ptr<boost::asio::ip::tcp::endpoint> m_Address4, m_Address6, m_YggdrasilAddress;
std::mt19937 m_Rng;
EstablisherService m_EstablisherService;
i2p::crypto::AEADChaCha20Poly1305Encryptor m_Encryptor;
i2p::crypto::AEADChaCha20Poly1305Decryptor m_Decryptor;
public: public:
// for HTTP/I2PControl // for HTTP/I2PControl

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2024, The PurpleI2P Project * Copyright (c) 2013-2025, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@@ -10,7 +10,6 @@
#include <fstream> #include <fstream>
#include <vector> #include <vector>
#include <map> #include <map>
#include <random>
#include <boost/asio.hpp> #include <boost/asio.hpp>
#include <stdexcept> #include <stdexcept>
@@ -40,7 +39,7 @@ namespace data
NetDb::NetDb (): m_IsRunning (false), m_Thread (nullptr), m_Reseeder (nullptr), NetDb::NetDb (): m_IsRunning (false), m_Thread (nullptr), m_Reseeder (nullptr),
m_Storage("netDb", "r", "routerInfo-", "dat"), m_PersistProfiles (true), m_Storage("netDb", "r", "routerInfo-", "dat"), m_PersistProfiles (true),
m_LastExploratorySelectionUpdateTime (0) m_LastExploratorySelectionUpdateTime (0), m_Rng(i2p::util::GetMonotonicMicroseconds () % 1000000LL)
{ {
} }
@@ -69,7 +68,7 @@ namespace data
{ {
Reseed (); Reseed ();
} }
else if (!GetRandomRouter (i2p::context.GetSharedRouterInfo (), false, false)) else if (!GetRandomRouter (i2p::context.GetSharedRouterInfo (), false, false, false))
Reseed (); // we don't have a router we can connect to. Trying to reseed Reseed (); // we don't have a router we can connect to. Trying to reseed
auto it = m_RouterInfos.find (i2p::context.GetIdentHash ()); auto it = m_RouterInfos.find (i2p::context.GetIdentHash ());
@@ -118,43 +117,36 @@ namespace data
{ {
i2p::util::SetThreadName("NetDB"); i2p::util::SetThreadName("NetDB");
uint64_t lastManage = 0, lastExploratory = 0, lastManageRequest = 0; uint64_t lastManage = 0;
uint64_t lastProfilesCleanup = i2p::util::GetMonotonicMilliseconds (), lastObsoleteProfilesCleanup = lastProfilesCleanup; uint64_t lastProfilesCleanup = i2p::util::GetMonotonicMilliseconds (),
int16_t profilesCleanupVariance = 0, obsoleteProfilesCleanVariance = 0, exploratoryIntervalVariance = 0; lastObsoleteProfilesCleanup = lastProfilesCleanup, lastApplyingProfileUpdates = lastProfilesCleanup;
int16_t profilesCleanupVariance = 0, obsoleteProfilesCleanVariance = 0, applyingProfileUpdatesVariance = 0;
std::list<std::shared_ptr<const I2NPMessage> > msgs;
while (m_IsRunning) while (m_IsRunning)
{ {
try try
{ {
auto msg = m_Queue.GetNextWithTimeout (1000); // 1 sec if (m_Queue.Wait (1,0)) // 1 sec
if (msg)
{ {
int numMsgs = 0; m_Queue.GetWholeQueue (msgs);
while (msg) while (!msgs.empty ())
{ {
auto msg = msgs.front (); msgs.pop_front ();
if (!msg) continue;
LogPrint(eLogDebug, "NetDb: Got request with type ", (int) msg->GetTypeID ()); LogPrint(eLogDebug, "NetDb: Got request with type ", (int) msg->GetTypeID ());
switch (msg->GetTypeID ()) switch (msg->GetTypeID ())
{ {
case eI2NPDatabaseStore: case eI2NPDatabaseStore:
HandleDatabaseStoreMsg (msg); HandleDatabaseStoreMsg (msg);
break; break;
case eI2NPDatabaseSearchReply:
HandleDatabaseSearchReplyMsg (msg);
break;
case eI2NPDatabaseLookup: case eI2NPDatabaseLookup:
HandleDatabaseLookupMsg (msg); HandleDatabaseLookupMsg (msg);
break; break;
case eI2NPDummyMsg:
// plain RouterInfo from NTCP2 with flags for now
HandleNTCP2RouterInfoMsg (msg);
break;
default: // WTF? default: // WTF?
LogPrint (eLogError, "NetDb: Unexpected message type ", (int) msg->GetTypeID ()); LogPrint (eLogError, "NetDb: Unexpected message type ", (int) msg->GetTypeID ());
//i2p::HandleI2NPMessage (msg); //i2p::HandleI2NPMessage (msg);
} }
if (numMsgs > 100) break;
msg = m_Queue.Get ();
numMsgs++;
} }
} }
if (!m_IsRunning) break; if (!m_IsRunning) break;
@@ -162,15 +154,6 @@ namespace data
continue; // don't manage netdb when offline or transports are not running continue; // don't manage netdb when offline or transports are not running
uint64_t mts = i2p::util::GetMonotonicMilliseconds (); uint64_t mts = i2p::util::GetMonotonicMilliseconds ();
if (mts >= lastManageRequest + MANAGE_REQUESTS_INTERVAL*1000)
{
if (lastManageRequest || i2p::tunnel::tunnels.GetExploratoryPool ()) // expolratory pool is ready?
{
if (m_Requests) m_Requests->ManageRequests ();
lastManageRequest = mts;
}
}
if (mts >= lastManage + 60000) // manage routers and leasesets every minute if (mts >= lastManage + 60000) // manage routers and leasesets every minute
{ {
if (lastManage) if (lastManage)
@@ -198,7 +181,7 @@ namespace data
LogPrint (eLogWarning, "NetDb: Can't persist profiles. Profiles are being saved to disk"); LogPrint (eLogWarning, "NetDb: Can't persist profiles. Profiles are being saved to disk");
} }
lastProfilesCleanup = mts; lastProfilesCleanup = mts;
profilesCleanupVariance = rand () % i2p::data::PEER_PROFILE_AUTOCLEAN_VARIANCE; profilesCleanupVariance = m_Rng () % i2p::data::PEER_PROFILE_AUTOCLEAN_VARIANCE;
} }
if (mts >= lastObsoleteProfilesCleanup + (uint64_t)(i2p::data::PEER_PROFILE_OBSOLETE_PROFILES_CLEAN_TIMEOUT + obsoleteProfilesCleanVariance)*1000) if (mts >= lastObsoleteProfilesCleanup + (uint64_t)(i2p::data::PEER_PROFILE_OBSOLETE_PROFILES_CLEAN_TIMEOUT + obsoleteProfilesCleanVariance)*1000)
@@ -214,29 +197,21 @@ namespace data
else else
LogPrint (eLogWarning, "NetDb: Can't delete profiles. Profiles are being deleted from disk"); LogPrint (eLogWarning, "NetDb: Can't delete profiles. Profiles are being deleted from disk");
lastObsoleteProfilesCleanup = mts; lastObsoleteProfilesCleanup = mts;
obsoleteProfilesCleanVariance = rand () % i2p::data::PEER_PROFILE_OBSOLETE_PROFILES_CLEAN_VARIANCE; obsoleteProfilesCleanVariance = m_Rng () % i2p::data::PEER_PROFILE_OBSOLETE_PROFILES_CLEAN_VARIANCE;
} }
if (mts >= lastApplyingProfileUpdates + i2p::data::PEER_PROFILE_APPLY_POSTPONED_TIMEOUT + applyingProfileUpdatesVariance)
if (mts >= lastExploratory + NETDB_EXPLORATORY_INTERVAL*1000LL) // check exploratory every 55 seconds
{ {
auto numRouters = m_RouterInfos.size (); bool isApplying = m_ApplyingProfileUpdates.valid ();
if (!numRouters) if (isApplying && m_ApplyingProfileUpdates.wait_for(std::chrono::seconds(0)) == std::future_status::ready) // still active?
throw std::runtime_error("No known routers, reseed seems to be totally failed");
else // we have peers now
m_FloodfillBootstrap = nullptr;
if (numRouters < 2500 || mts >= lastExploratory + (NETDB_EXPLORATORY_INTERVAL + exploratoryIntervalVariance)*1000LL)
{ {
if(!i2p::context.IsHidden ()) m_ApplyingProfileUpdates.get ();
{ isApplying = false;
numRouters = 800/numRouters; }
if (numRouters < 1) numRouters = 1; if (!isApplying)
if (numRouters > 9) numRouters = 9; m_ApplyingProfileUpdates = i2p::data::FlushPostponedRouterProfileUpdates ();
Explore (numRouters); lastApplyingProfileUpdates = mts;
} applyingProfileUpdatesVariance = m_Rng () % i2p::data::PEER_PROFILE_APPLY_POSTPONED_TIMEOUT_VARIANCE;
lastExploratory = mts; }
exploratoryIntervalVariance = rand () % NETDB_EXPLORATORY_INTERVAL_VARIANCE;
}
}
} }
catch (std::exception& ex) catch (std::exception& ex)
{ {
@@ -319,6 +294,7 @@ namespace data
} }
else else
{ {
r->CancelBufferToDelete (); // since an update received
if (CheckLogLevel (eLogDebug)) if (CheckLogLevel (eLogDebug))
LogPrint (eLogDebug, "NetDb: RouterInfo is older: ", ident.ToBase64()); LogPrint (eLogDebug, "NetDb: RouterInfo is older: ", ident.ToBase64());
updated = false; updated = false;
@@ -332,7 +308,8 @@ namespace data
{ {
auto mts = i2p::util::GetMillisecondsSinceEpoch (); auto mts = i2p::util::GetMillisecondsSinceEpoch ();
isValid = mts + NETDB_EXPIRATION_TIMEOUT_THRESHOLD*1000LL > r->GetTimestamp () && // from future isValid = mts + NETDB_EXPIRATION_TIMEOUT_THRESHOLD*1000LL > r->GetTimestamp () && // from future
mts < r->GetTimestamp () + NETDB_MAX_EXPIRATION_TIMEOUT*1000LL; // too old (mts < r->GetTimestamp () + NETDB_MAX_EXPIRATION_TIMEOUT*1000LL || // too old
context.GetUptime () < NETDB_CHECK_FOR_EXPIRATION_UPTIME/10); // enough uptime
} }
if (isValid) if (isValid)
{ {
@@ -421,8 +398,7 @@ namespace data
if (it == m_LeaseSets.end () || it->second->GetStoreType () != storeType || if (it == m_LeaseSets.end () || it->second->GetStoreType () != storeType ||
leaseSet->GetPublishedTimestamp () > it->second->GetPublishedTimestamp ()) leaseSet->GetPublishedTimestamp () > it->second->GetPublishedTimestamp ())
{ {
if (leaseSet->IsPublic () && !leaseSet->IsExpired () && if (leaseSet->IsPublic () && !leaseSet->IsExpired ())
i2p::util::GetSecondsSinceEpoch () + NETDB_EXPIRATION_TIMEOUT_THRESHOLD > leaseSet->GetPublishedTimestamp ())
{ {
// TODO: implement actual update // TODO: implement actual update
if (CheckLogLevel (eLogInfo)) if (CheckLogLevel (eLogInfo))
@@ -511,35 +487,13 @@ namespace data
m_Reseeder->LoadCertificates (); // we need certificates for SU3 verification m_Reseeder->LoadCertificates (); // we need certificates for SU3 verification
} }
// try reseeding from floodfill first if specified
std::string riPath; i2p::config::GetOption("reseed.floodfill", riPath);
if (!riPath.empty())
{
auto ri = std::make_shared<RouterInfo>(riPath);
if (ri->IsFloodfill())
{
const uint8_t * riData = ri->GetBuffer();
int riLen = ri->GetBufferLen();
if (!i2p::data::netdb.AddRouterInfo(riData, riLen))
{
// bad router info
LogPrint(eLogError, "NetDb: Bad router info");
return;
}
m_FloodfillBootstrap = ri;
//ReseedFromFloodfill(*ri);
// don't try reseed servers if trying to bootstrap from floodfill
return;
}
}
m_Reseeder->Bootstrap (); m_Reseeder->Bootstrap ();
} }
void NetDb::ReseedFromFloodfill(const RouterInfo & ri, int numRouters, int numFloodfills) void NetDb::ReseedFromFloodfill(const RouterInfo & ri, int numRouters, int numFloodfills)
{ {
LogPrint(eLogInfo, "NetDB: Reseeding from floodfill ", ri.GetIdentHashBase64()); LogPrint(eLogInfo, "NetDB: Reseeding from floodfill ", ri.GetIdentHashBase64());
std::vector<std::shared_ptr<i2p::I2NPMessage> > requests; std::list<std::shared_ptr<i2p::I2NPMessage> > requests;
i2p::data::IdentHash ourIdent = i2p::context.GetIdentHash(); i2p::data::IdentHash ourIdent = i2p::context.GetIdentHash();
i2p::data::IdentHash ih = ri.GetIdentHash(); i2p::data::IdentHash ih = ri.GetIdentHash();
@@ -562,7 +516,7 @@ namespace data
} }
// send them off // send them off
i2p::transport::transports.SendMessages(ih, requests); i2p::transport::transports.SendMessages(ih, std::move (requests));
} }
bool NetDb::LoadRouterInfo (const std::string& path, uint64_t ts) bool NetDb::LoadRouterInfo (const std::string& path, uint64_t ts)
@@ -617,7 +571,7 @@ namespace data
while(n > 0) while(n > 0)
{ {
std::lock_guard<std::mutex> lock(m_RouterInfosMutex); std::lock_guard<std::mutex> lock(m_RouterInfosMutex);
uint32_t idx = rand () % m_RouterInfos.size (); uint32_t idx = m_Rng () % m_RouterInfos.size ();
uint32_t i = 0; uint32_t i = 0;
for (const auto & it : m_RouterInfos) { for (const auto & it : m_RouterInfos) {
if(i >= idx) // are we at the random start point? if(i >= idx) // are we at the random start point?
@@ -697,70 +651,79 @@ namespace data
if (checkForExpiration && uptime > i2p::transport::SSU2_TO_INTRODUCER_SESSION_DURATION) // 1 hour if (checkForExpiration && uptime > i2p::transport::SSU2_TO_INTRODUCER_SESSION_DURATION) // 1 hour
expirationTimeout = i2p::context.IsFloodfill () ? NETDB_FLOODFILL_EXPIRATION_TIMEOUT*1000LL : 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; NETDB_MIN_EXPIRATION_TIMEOUT*1000LL + (NETDB_MAX_EXPIRATION_TIMEOUT - NETDB_MIN_EXPIRATION_TIMEOUT)*1000LL*NETDB_MIN_ROUTERS/total;
bool isOffline = checkForExpiration && i2p::transport::transports.GetNumPeers () < NETDB_MIN_TRANSPORTS; // enough routers and uptime, but no transports
std::list<std::pair<std::string, std::shared_ptr<RouterInfo::Buffer> > > saveToDisk; std::list<std::pair<std::string, std::shared_ptr<RouterInfo::Buffer> > > saveToDisk;
std::list<std::string> removeFromDisk; std::list<std::string> removeFromDisk;
auto own = i2p::context.GetSharedRouterInfo (); auto own = i2p::context.GetSharedRouterInfo ();
for (auto& it: m_RouterInfos) for (auto [ident, r]: m_RouterInfos)
{ {
if (!it.second || it.second == own) continue; // skip own if (!r || r == own) continue; // skip own
std::string ident = it.second->GetIdentHashBase64(); if (r->IsBufferScheduledToDelete ()) // from previous SaveUpdated, we assume m_PersistingRouters complete
if (it.second->IsUpdated ())
{ {
if (it.second->GetBuffer ()) std::lock_guard<std::mutex> l(m_RouterInfosMutex); // possible collision between DeleteBuffer and Update
r->DeleteBuffer ();
}
if (r->IsUpdated ())
{
if (r->GetBuffer () && !r->IsUnreachable ())
{ {
// we have something to save // we have something to save
std::shared_ptr<RouterInfo::Buffer> buffer; std::shared_ptr<RouterInfo::Buffer> buffer;
{ {
std::lock_guard<std::mutex> l(m_RouterInfosMutex); // possible collision between DeleteBuffer and Update std::lock_guard<std::mutex> l(m_RouterInfosMutex); // possible collision between DeleteBuffer and Update
buffer = it.second->GetSharedBuffer (); buffer = r->CopyBuffer ();
it.second->DeleteBuffer ();
} }
if (buffer && !it.second->IsUnreachable ()) // don't save bad router if (!i2p::transport::transports.IsConnected (ident))
saveToDisk.push_back(std::make_pair(ident, buffer)); r->ScheduleBufferToDelete ();
it.second->SetUnreachable (false); if (buffer)
saveToDisk.emplace_back(ident.ToBase64 (), buffer);
} }
it.second->SetUpdated (false); r->SetUpdated (false);
updatedCount++; updatedCount++;
continue; continue;
} }
if (it.second->GetProfile ()->IsUnreachable ()) else if (r->GetBuffer () && ts > r->GetTimestamp () + NETDB_MIN_EXPIRATION_TIMEOUT*1000LL)
it.second->SetUnreachable (true); // since update was long time ago we assume that router is not connected anymore
r->ScheduleBufferToDelete ();
if (r->GetProfile ()->IsUnreachable ())
r->SetUnreachable (true);
// make router reachable back if too few routers or floodfills // make router reachable back if too few routers or floodfills
if (it.second->IsUnreachable () && (total - deletedCount < NETDB_MIN_ROUTERS || isLowRate || if (r->IsUnreachable () && (total - deletedCount < NETDB_MIN_ROUTERS || isLowRate || isOffline ||
(it.second->IsFloodfill () && totalFloodfills - deletedFloodfillsCount < NETDB_MIN_FLOODFILLS))) (r->IsFloodfill () && totalFloodfills - deletedFloodfillsCount < NETDB_MIN_FLOODFILLS)))
it.second->SetUnreachable (false); r->SetUnreachable (false);
if (!it.second->IsUnreachable ()) if (!r->IsUnreachable ())
{ {
// find & mark expired routers // find & mark expired routers
if (!it.second->GetCompatibleTransports (true)) // non reachable by any transport if (!r->GetCompatibleTransports (true)) // non reachable by any transport
it.second->SetUnreachable (true); r->SetUnreachable (true);
else if (ts + NETDB_EXPIRATION_TIMEOUT_THRESHOLD*1000LL < it.second->GetTimestamp ()) else if (ts + NETDB_EXPIRATION_TIMEOUT_THRESHOLD*1000LL < r->GetTimestamp ())
{ {
LogPrint (eLogWarning, "NetDb: RouterInfo is from future for ", (it.second->GetTimestamp () - ts)/1000LL, " seconds"); LogPrint (eLogWarning, "NetDb: RouterInfo is from future for ", (r->GetTimestamp () - ts)/1000LL, " seconds");
it.second->SetUnreachable (true); r->SetUnreachable (true);
} }
else if (checkForExpiration) else if (checkForExpiration)
{ {
if (ts > it.second->GetTimestamp () + expirationTimeout) if (ts > r->GetTimestamp () + expirationTimeout)
it.second->SetUnreachable (true); r->SetUnreachable (true);
else if ((ts > it.second->GetTimestamp () + expirationTimeout/2) && // more than half of expiration else if ((ts > r->GetTimestamp () + expirationTimeout/2) && // more than half of expiration
total > NETDB_NUM_ROUTERS_THRESHOLD && !it.second->IsHighBandwidth() && // low bandwidth total > NETDB_NUM_ROUTERS_THRESHOLD && !r->IsHighBandwidth() && // low bandwidth
!it.second->IsFloodfill() && (!i2p::context.IsFloodfill () || // non floodfill !r->IsFloodfill() && (!i2p::context.IsFloodfill () || // non floodfill
(CreateRoutingKey (it.second->GetIdentHash ()) ^ i2p::context.GetIdentHash ()).metric[0] >= 0x02)) // different first 7 bits (CreateRoutingKey (ident) ^ i2p::context.GetIdentHash ()).metric[0] >= 0x02)) // different first 7 bits
it.second->SetUnreachable (true); r->SetUnreachable (true);
} }
} }
// make router reachable back if connected now // make router reachable back if connected now
if (it.second->IsUnreachable () && i2p::transport::transports.IsConnected (it.second->GetIdentHash ())) if (r->IsUnreachable () && i2p::transport::transports.IsConnected (ident))
it.second->SetUnreachable (false); r->SetUnreachable (false);
if (it.second->IsUnreachable ()) if (r->IsUnreachable ())
{ {
if (it.second->IsFloodfill ()) deletedFloodfillsCount++; if (r->IsFloodfill ()) deletedFloodfillsCount++;
// delete RI file // delete RI file
removeFromDisk.push_back (ident); removeFromDisk.emplace_back (ident.ToBase64());
deletedCount++; deletedCount++;
if (total - deletedCount < NETDB_MIN_ROUTERS) checkForExpiration = false; if (total - deletedCount < NETDB_MIN_ROUTERS) checkForExpiration = false;
} }
@@ -818,69 +781,12 @@ namespace data
void NetDb::RequestDestination (const IdentHash& destination, RequestedDestination::RequestComplete requestComplete, bool direct) void NetDb::RequestDestination (const IdentHash& destination, RequestedDestination::RequestComplete requestComplete, bool direct)
{ {
if (direct && i2p::transport::transports.RoutesRestricted ()) direct = false; // always use tunnels for restricted routes if (direct && (i2p::transport::transports.RoutesRestricted () || i2p::context.IsLimitedConnectivity ()))
auto dest = m_Requests->CreateRequest (destination, false, direct, requestComplete); // non-exploratory direct = false; // always use tunnels for restricted routes or limited connectivity
if (!dest) if (m_Requests)
{ m_Requests->PostRequestDestination (destination, requestComplete, direct);
LogPrint (eLogWarning, "NetDb: Destination ", destination.ToBase64(), " is requested already or cached");
return;
}
auto floodfill = GetClosestFloodfill (destination, dest->GetExcludedPeers ());
if (floodfill)
{
if (direct && !floodfill->IsReachableFrom (i2p::context.GetRouterInfo ()) &&
!i2p::transport::transports.IsConnected (floodfill->GetIdentHash ()))
direct = false; // floodfill can't be reached directly
if (direct)
{
if (CheckLogLevel (eLogDebug))
LogPrint (eLogDebug, "NetDb: Request ", dest->GetDestination ().ToBase64 (), " at ", floodfill->GetIdentHash ().ToBase64 (), " directly");
auto msg = dest->CreateRequestMessage (floodfill->GetIdentHash ());
msg->onDrop = [this, dest]() { if (dest->IsActive ()) this->m_Requests->SendNextRequest (dest); };
transports.SendMessage (floodfill->GetIdentHash (), msg);
}
else
{
auto pool = i2p::tunnel::tunnels.GetExploratoryPool ();
auto outbound = pool ? pool->GetNextOutboundTunnel (nullptr, floodfill->GetCompatibleTransports (false)) : nullptr;
auto inbound = pool ? pool->GetNextInboundTunnel (nullptr, floodfill->GetCompatibleTransports (true)) : nullptr;
if (outbound && inbound)
{
if (CheckLogLevel (eLogDebug))
LogPrint (eLogDebug, "NetDb: Request ", dest->GetDestination ().ToBase64 (), " at ", floodfill->GetIdentHash ().ToBase64 (), " through tunnels");
auto msg = dest->CreateRequestMessage (floodfill, inbound);
msg->onDrop = [this, dest]() { if (dest->IsActive ()) this->m_Requests->SendNextRequest (dest); };
outbound->SendTunnelDataMsgTo (floodfill->GetIdentHash (), 0,
i2p::garlic::WrapECIESX25519MessageForRouter (msg, floodfill->GetIdentity ()->GetEncryptionPublicKey ()));
}
else
{
LogPrint (eLogError, "NetDb: ", destination.ToBase64(), " destination requested, but no tunnels found");
m_Requests->RequestComplete (destination, nullptr);
}
}
}
else else
{ LogPrint (eLogError, "NetDb: Requests is null");
LogPrint (eLogError, "NetDb: ", destination.ToBase64(), " destination requested, but no floodfills found");
m_Requests->RequestComplete (destination, nullptr);
}
}
void NetDb::RequestDestinationFrom (const IdentHash& destination, const IdentHash & from, bool exploratory, RequestedDestination::RequestComplete requestComplete)
{
auto dest = m_Requests->CreateRequest (destination, exploratory, true, requestComplete); // non-exploratory
if (!dest)
{
LogPrint (eLogWarning, "NetDb: Destination ", destination.ToBase64(), " is requested already");
return;
}
if (CheckLogLevel (eLogDebug))
LogPrint(eLogDebug, "NetDb: Destination ", destination.ToBase64(), " being requested directly from ", from.ToBase64());
// direct
transports.SendMessage (from, dest->CreateRequestMessage (nullptr, nullptr));
} }
void NetDb::HandleNTCP2RouterInfoMsg (std::shared_ptr<const I2NPMessage> m) void NetDb::HandleNTCP2RouterInfoMsg (std::shared_ptr<const I2NPMessage> m)
@@ -1025,69 +931,16 @@ namespace data
{ {
memcpy (payload + DATABASE_STORE_HEADER_SIZE, buf + payloadOffset, msgLen); memcpy (payload + DATABASE_STORE_HEADER_SIZE, buf + payloadOffset, msgLen);
floodMsg->FillI2NPMessageHeader (eI2NPDatabaseStore); floodMsg->FillI2NPMessageHeader (eI2NPDatabaseStore);
Flood (ident, floodMsg); int minutesBeforeMidnight = 24*60 - i2p::util::GetMinutesSinceEpoch () % (24*60);
bool andNextDay = storeType ? minutesBeforeMidnight < NETDB_NEXT_DAY_LEASESET_THRESHOLD:
minutesBeforeMidnight < NETDB_NEXT_DAY_ROUTER_INFO_THRESHOLD;
Flood (ident, floodMsg, andNextDay);
} }
else else
LogPrint (eLogError, "NetDb: Database store message is too long ", floodMsg->len); LogPrint (eLogError, "NetDb: Database store message is too long ", floodMsg->len);
} }
} }
void NetDb::HandleDatabaseSearchReplyMsg (std::shared_ptr<const I2NPMessage> msg)
{
const uint8_t * buf = msg->GetPayload ();
char key[48];
int l = i2p::data::ByteStreamToBase64 (buf, 32, key, 48);
key[l] = 0;
size_t num = buf[32]; // num
LogPrint (eLogDebug, "NetDb: DatabaseSearchReply for ", key, " num=", num);
IdentHash ident (buf);
auto dest = m_Requests->FindRequest (ident);
if (dest && dest->IsActive ())
{
if (!dest->IsExploratory () && (num > 0 || dest->GetNumExcludedPeers () < 3)) // before 3-rd attempt might be just bad luck
// try to send next requests
m_Requests->SendNextRequest (dest);
else
// no more requests for destination possible. delete it
m_Requests->RequestComplete (ident, nullptr);
}
else if (!m_FloodfillBootstrap)
{
LogPrint (eLogInfo, "NetDb: Unsolicited or late database search reply for ", key);
return;
}
// try responses
if (num > NETDB_MAX_NUM_SEARCH_REPLY_PEER_HASHES)
{
LogPrint (eLogWarning, "NetDb: Too many peer hashes ", num, " in database search reply, Reduced to ", NETDB_MAX_NUM_SEARCH_REPLY_PEER_HASHES);
num = NETDB_MAX_NUM_SEARCH_REPLY_PEER_HASHES;
}
for (size_t i = 0; i < num; i++)
{
const uint8_t * router = buf + 33 + i*32;
char peerHash[48];
int l1 = i2p::data::ByteStreamToBase64 (router, 32, peerHash, 48);
peerHash[l1] = 0;
LogPrint (eLogDebug, "NetDb: ", i, ": ", peerHash);
auto r = FindRouter (router);
if (!r || i2p::util::GetMillisecondsSinceEpoch () > r->GetTimestamp () + 3600*1000LL)
{
// router with ident not found or too old (1 hour)
LogPrint (eLogDebug, "NetDb: Found new/outdated router. Requesting RouterInfo...");
if(m_FloodfillBootstrap)
RequestDestinationFrom(router, m_FloodfillBootstrap->GetIdentHash(), true);
else if (!IsRouterBanned (router))
RequestDestination (router);
else
LogPrint (eLogDebug, "NetDb: Router ", peerHash, " is banned. Skipped");
}
else
LogPrint (eLogDebug, "NetDb: [:|||:]");
}
}
void NetDb::HandleDatabaseLookupMsg (std::shared_ptr<const I2NPMessage> msg) void NetDb::HandleDatabaseLookupMsg (std::shared_ptr<const I2NPMessage> msg)
{ {
const uint8_t * buf = msg->GetPayload (); const uint8_t * buf = msg->GetPayload ();
@@ -1131,7 +984,7 @@ namespace data
return; return;
} }
LogPrint (eLogInfo, "NetDb: Exploratory close to ", key, " ", numExcluded, " excluded"); LogPrint (eLogInfo, "NetDb: Exploratory close to ", key, " ", numExcluded, " excluded");
std::set<IdentHash> excludedRouters; std::unordered_set<IdentHash> excludedRouters;
const uint8_t * excluded_ident = excluded; const uint8_t * excluded_ident = excluded;
for (int i = 0; i < numExcluded; i++) for (int i = 0; i < numExcluded; i++)
{ {
@@ -1183,7 +1036,7 @@ namespace data
if (!replyMsg) if (!replyMsg)
{ {
std::set<IdentHash> excludedRouters; std::unordered_set<IdentHash> excludedRouters;
const uint8_t * exclude_ident = excluded; const uint8_t * exclude_ident = excluded;
for (int i = 0; i < numExcluded; i++) for (int i = 0; i < numExcluded; i++)
{ {
@@ -1250,74 +1103,43 @@ namespace data
} }
} }
void NetDb::Explore (int numDestinations) void NetDb::Flood (const IdentHash& ident, std::shared_ptr<I2NPMessage> floodMsg, bool andNextDay)
{ {
// new requests std::unordered_set<IdentHash> excluded;
auto exploratoryPool = i2p::tunnel::tunnels.GetExploratoryPool ();
auto outbound = exploratoryPool ? exploratoryPool->GetNextOutboundTunnel () : nullptr;
auto inbound = exploratoryPool ? exploratoryPool->GetNextInboundTunnel () : nullptr;
bool throughTunnels = outbound && inbound;
uint8_t randomHash[32];
std::vector<i2p::tunnel::TunnelMessageBlock> msgs;
LogPrint (eLogInfo, "NetDb: Exploring new ", numDestinations, " routers ...");
for (int i = 0; i < numDestinations; i++)
{
RAND_bytes (randomHash, 32);
auto dest = m_Requests->CreateRequest (randomHash, true, !throughTunnels); // exploratory
if (!dest)
{
LogPrint (eLogWarning, "NetDb: Exploratory destination is requested already");
return;
}
auto floodfill = GetClosestFloodfill (randomHash, dest->GetExcludedPeers ());
if (floodfill)
{
if (i2p::transport::transports.IsConnected (floodfill->GetIdentHash ()))
throughTunnels = false;
if (throughTunnels)
{
msgs.push_back (i2p::tunnel::TunnelMessageBlock
{
i2p::tunnel::eDeliveryTypeRouter,
floodfill->GetIdentHash (), 0,
CreateDatabaseStoreMsg () // tell floodfill about us
});
msgs.push_back (i2p::tunnel::TunnelMessageBlock
{
i2p::tunnel::eDeliveryTypeRouter,
floodfill->GetIdentHash (), 0,
dest->CreateRequestMessage (floodfill, inbound) // explore
});
}
else
i2p::transport::transports.SendMessage (floodfill->GetIdentHash (), dest->CreateRequestMessage (floodfill->GetIdentHash ()));
}
else
m_Requests->RequestComplete (randomHash, nullptr);
}
if (throughTunnels && msgs.size () > 0)
outbound->SendTunnelDataMsgs (msgs);
}
void NetDb::Flood (const IdentHash& ident, std::shared_ptr<I2NPMessage> floodMsg)
{
std::set<IdentHash> excluded;
excluded.insert (i2p::context.GetIdentHash ()); // don't flood to itself excluded.insert (i2p::context.GetIdentHash ()); // don't flood to itself
excluded.insert (ident); // don't flood back excluded.insert (ident); // don't flood back
for (int i = 0; i < 3; i++) for (int i = 0; i < 3; i++)
{ {
auto floodfill = GetClosestFloodfill (ident, excluded); auto floodfill = GetClosestFloodfill (ident, excluded, false); // current day
if (floodfill) if (floodfill)
{ {
auto h = floodfill->GetIdentHash(); const auto& h = floodfill->GetIdentHash();
LogPrint(eLogDebug, "NetDb: Flood lease set for ", ident.ToBase32(), " to ", h.ToBase64());
transports.SendMessage (h, CopyI2NPMessage(floodMsg)); transports.SendMessage (h, CopyI2NPMessage(floodMsg));
excluded.insert (h); excluded.insert (h);
} }
else else
break; return; // no more floodfills
} }
if (andNextDay)
{
// flood to two more closest flodfills for next day
std::unordered_set<IdentHash> excluded1;
excluded1.insert (i2p::context.GetIdentHash ()); // don't flood to itself
excluded1.insert (ident); // don't flood back
for (int i = 0; i < 2; i++)
{
auto floodfill = GetClosestFloodfill (ident, excluded1, true); // next day
if (floodfill)
{
const auto& h = floodfill->GetIdentHash();
if (!excluded.count (h)) // we didn't send for current day, otherwise skip
transports.SendMessage (h, CopyI2NPMessage(floodMsg));
excluded1.insert (h);
}
else
return;
}
}
} }
std::shared_ptr<const RouterInfo> NetDb::GetRandomRouter () const std::shared_ptr<const RouterInfo> NetDb::GetRandomRouter () const
@@ -1330,20 +1152,23 @@ namespace data
} }
std::shared_ptr<const RouterInfo> NetDb::GetRandomRouter (std::shared_ptr<const RouterInfo> compatibleWith, std::shared_ptr<const RouterInfo> NetDb::GetRandomRouter (std::shared_ptr<const RouterInfo> compatibleWith,
bool reverse, bool endpoint) const bool reverse, bool endpoint, bool clientTunnel) const
{ {
bool checkIsReal = clientTunnel && i2p::tunnel::tunnels.GetPreciseTunnelCreationSuccessRate () < NETDB_TUNNEL_CREATION_RATE_THRESHOLD && // too low rate
context.GetUptime () > NETDB_CHECK_FOR_EXPIRATION_UPTIME; // after 10 minutes uptime
return GetRandomRouter ( return GetRandomRouter (
[compatibleWith, reverse, endpoint](std::shared_ptr<const RouterInfo> router)->bool [compatibleWith, reverse, endpoint, clientTunnel, checkIsReal](std::shared_ptr<const RouterInfo> router)->bool
{ {
return !router->IsHidden () && router != compatibleWith && return !router->IsHidden () && router != compatibleWith &&
(reverse ? (compatibleWith->IsReachableFrom (*router) && router->GetCompatibleTransports (true)): (reverse ? (compatibleWith->IsReachableFrom (*router) && router->GetCompatibleTransports (true)):
router->IsReachableFrom (*compatibleWith)) && !router->IsNAT2NATOnly (*compatibleWith) && router->IsReachableFrom (*compatibleWith)) && !router->IsNAT2NATOnly (*compatibleWith) &&
router->IsECIES () && !router->IsHighCongestion (false) && router->IsECIES () && !router->IsHighCongestion (clientTunnel) &&
(!checkIsReal || router->GetProfile ()->IsReal ()) &&
(!endpoint || (router->IsV4 () && (!reverse || router->IsPublished (true)))); // endpoint must be ipv4 and published if inbound(reverse) (!endpoint || (router->IsV4 () && (!reverse || router->IsPublished (true)))); // endpoint must be ipv4 and published if inbound(reverse)
}); });
} }
std::shared_ptr<const RouterInfo> NetDb::GetRandomSSU2PeerTestRouter (bool v4, const std::set<IdentHash>& excluded) const std::shared_ptr<const RouterInfo> NetDb::GetRandomSSU2PeerTestRouter (bool v4, const std::unordered_set<IdentHash>& excluded) const
{ {
return GetRandomRouter ( return GetRandomRouter (
[v4, &excluded](std::shared_ptr<const RouterInfo> router)->bool [v4, &excluded](std::shared_ptr<const RouterInfo> router)->bool
@@ -1353,7 +1178,7 @@ namespace data
}); });
} }
std::shared_ptr<const RouterInfo> NetDb::GetRandomSSU2Introducer (bool v4, const std::set<IdentHash>& excluded) const std::shared_ptr<const RouterInfo> NetDb::GetRandomSSU2Introducer (bool v4, const std::unordered_set<IdentHash>& excluded) const
{ {
return GetRandomRouter ( return GetRandomRouter (
[v4, &excluded](std::shared_ptr<const RouterInfo> router)->bool [v4, &excluded](std::shared_ptr<const RouterInfo> router)->bool
@@ -1448,10 +1273,16 @@ namespace data
if (msg) m_Queue.Put (msg); if (msg) m_Queue.Put (msg);
} }
std::shared_ptr<const RouterInfo> NetDb::GetClosestFloodfill (const IdentHash& destination, void NetDb::PostDatabaseSearchReplyMsg (std::shared_ptr<const I2NPMessage> msg)
const std::set<IdentHash>& excluded) const
{ {
IdentHash destKey = CreateRoutingKey (destination); if (msg && m_Requests)
m_Requests->PostDatabaseSearchReplyMsg (msg);
}
std::shared_ptr<const RouterInfo> NetDb::GetClosestFloodfill (const IdentHash& destination,
const std::unordered_set<IdentHash>& excluded, bool nextDay) const
{
IdentHash destKey = CreateRoutingKey (destination, nextDay);
std::lock_guard<std::mutex> l(m_FloodfillsMutex); std::lock_guard<std::mutex> l(m_FloodfillsMutex);
return m_Floodfills.FindClosest (destKey, [&excluded](const std::shared_ptr<RouterInfo>& r)->bool return m_Floodfills.FindClosest (destKey, [&excluded](const std::shared_ptr<RouterInfo>& r)->bool
{ {
@@ -1461,7 +1292,7 @@ namespace data
} }
std::vector<IdentHash> NetDb::GetClosestFloodfills (const IdentHash& destination, size_t num, std::vector<IdentHash> NetDb::GetClosestFloodfills (const IdentHash& destination, size_t num,
std::set<IdentHash>& excluded, bool closeThanUsOnly) const std::unordered_set<IdentHash>& excluded, bool closeThanUsOnly) const
{ {
std::vector<IdentHash> res; std::vector<IdentHash> res;
IdentHash destKey = CreateRoutingKey (destination); IdentHash destKey = CreateRoutingKey (destination);
@@ -1496,7 +1327,7 @@ namespace data
} }
std::vector<IdentHash> NetDb::GetExploratoryNonFloodfill (const IdentHash& destination, std::vector<IdentHash> NetDb::GetExploratoryNonFloodfill (const IdentHash& destination,
size_t num, const std::set<IdentHash>& excluded) size_t num, const std::unordered_set<IdentHash>& excluded)
{ {
std::vector<IdentHash> ret; std::vector<IdentHash> ret;
if (!num || m_RouterInfos.empty ()) return ret; // empty list if (!num || m_RouterInfos.empty ()) return ret; // empty list
@@ -1505,12 +1336,8 @@ namespace data
{ {
// update selection // update selection
m_ExploratorySelection.clear (); m_ExploratorySelection.clear ();
#if (__cplusplus >= 201703L) // C++ 17 or higher
std::vector<std::shared_ptr<const RouterInfo> > eligible; std::vector<std::shared_ptr<const RouterInfo> > eligible;
eligible.reserve (m_RouterInfos.size ()); eligible.reserve (m_RouterInfos.size ());
#else
auto& eligible = m_ExploratorySelection;
#endif
{ {
// collect eligible from current netdb // collect eligible from current netdb
bool checkIsReal = i2p::tunnel::tunnels.GetPreciseTunnelCreationSuccessRate () < NETDB_TUNNEL_CREATION_RATE_THRESHOLD; // too low rate bool checkIsReal = i2p::tunnel::tunnels.GetPreciseTunnelCreationSuccessRate () < NETDB_TUNNEL_CREATION_RATE_THRESHOLD; // too low rate
@@ -1520,22 +1347,13 @@ namespace data
(!checkIsReal || (it.second->HasProfile () && it.second->GetProfile ()->IsReal ()))) (!checkIsReal || (it.second->HasProfile () && it.second->GetProfile ()->IsReal ())))
eligible.push_back (it.second); eligible.push_back (it.second);
} }
#if (__cplusplus >= 201703L) // C++ 17 or higher
if (eligible.size () > NETDB_MAX_EXPLORATORY_SELECTION_SIZE) if (eligible.size () > NETDB_MAX_EXPLORATORY_SELECTION_SIZE)
{ {
std::sample (eligible.begin(), eligible.end(), std::back_inserter(m_ExploratorySelection), std::sample (eligible.begin(), eligible.end(), std::back_inserter(m_ExploratorySelection),
NETDB_MAX_EXPLORATORY_SELECTION_SIZE, std::mt19937(ts)); NETDB_MAX_EXPLORATORY_SELECTION_SIZE, m_Rng);
} }
else else
std::swap (m_ExploratorySelection, eligible); std::swap (m_ExploratorySelection, eligible);
#else
if (m_ExploratorySelection.size () > NETDB_MAX_EXPLORATORY_SELECTION_SIZE)
{
// reduce number of eligible to max selection size
std::shuffle (m_ExploratorySelection.begin(), m_ExploratorySelection.end(), std::mt19937(ts));
m_ExploratorySelection.resize (NETDB_MAX_EXPLORATORY_SELECTION_SIZE);
}
#endif
m_LastExploratorySelectionUpdateTime = ts; m_LastExploratorySelectionUpdateTime = ts;
} }

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2024, The PurpleI2P Project * Copyright (c) 2013-2025, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@@ -10,12 +10,13 @@
#define NETDB_H__ #define NETDB_H__
// this file is called NetDb.hpp to resolve conflict with libc's netdb.h on case insensitive fs // this file is called NetDb.hpp to resolve conflict with libc's netdb.h on case insensitive fs
#include <inttypes.h> #include <inttypes.h>
#include <set> #include <unordered_set>
#include <unordered_map> #include <unordered_map>
#include <string> #include <string>
#include <thread> #include <thread>
#include <mutex> #include <mutex>
#include <future> #include <future>
#include <random>
#include "Base.h" #include "Base.h"
#include "Gzip.h" #include "Gzip.h"
@@ -39,7 +40,8 @@ namespace data
{ {
const int NETDB_MIN_ROUTERS = 90; const int NETDB_MIN_ROUTERS = 90;
const int NETDB_MIN_FLOODFILLS = 5; const int NETDB_MIN_FLOODFILLS = 5;
const int NETDB_NUM_FLOODFILLS_THRESHOLD = 1000; const int NETDB_MIN_TRANSPORTS = 10 ; // otherwise assume offline
const int NETDB_NUM_FLOODFILLS_THRESHOLD = 1200;
const int NETDB_NUM_ROUTERS_THRESHOLD = 4*NETDB_NUM_FLOODFILLS_THRESHOLD; const int NETDB_NUM_ROUTERS_THRESHOLD = 4*NETDB_NUM_FLOODFILLS_THRESHOLD;
const int NETDB_TUNNEL_CREATION_RATE_THRESHOLD = 10; // in % const int NETDB_TUNNEL_CREATION_RATE_THRESHOLD = 10; // in %
const int NETDB_CHECK_FOR_EXPIRATION_UPTIME = 600; // 10 minutes, in seconds const int NETDB_CHECK_FOR_EXPIRATION_UPTIME = 600; // 10 minutes, in seconds
@@ -48,14 +50,15 @@ namespace data
const int NETDB_MAX_EXPIRATION_TIMEOUT = 27 * 60 * 60; // 27 hours const int NETDB_MAX_EXPIRATION_TIMEOUT = 27 * 60 * 60; // 27 hours
const int NETDB_MAX_OFFLINE_EXPIRATION_TIMEOUT = 180; // in days const int NETDB_MAX_OFFLINE_EXPIRATION_TIMEOUT = 180; // in days
const int NETDB_EXPIRATION_TIMEOUT_THRESHOLD = 2*60; // 2 minutes const int NETDB_EXPIRATION_TIMEOUT_THRESHOLD = 2*60; // 2 minutes
const int NETDB_EXPLORATORY_INTERVAL = 55; // in seconds const int NETDB_MIN_HIGHBANDWIDTH_VERSION = MAKE_VERSION_NUMBER(0, 9, 58); // 0.9.58
const int NETDB_EXPLORATORY_INTERVAL_VARIANCE = 170; // in seconds const int NETDB_MIN_FLOODFILL_VERSION = MAKE_VERSION_NUMBER(0, 9, 59); // 0.9.59
const int NETDB_MIN_HIGHBANDWIDTH_VERSION = MAKE_VERSION_NUMBER(0, 9, 51); // 0.9.51
const int NETDB_MIN_FLOODFILL_VERSION = MAKE_VERSION_NUMBER(0, 9, 51); // 0.9.51
const int NETDB_MIN_SHORT_TUNNEL_BUILD_VERSION = MAKE_VERSION_NUMBER(0, 9, 51); // 0.9.51 const int NETDB_MIN_SHORT_TUNNEL_BUILD_VERSION = MAKE_VERSION_NUMBER(0, 9, 51); // 0.9.51
const int NETDB_MIN_PEER_TEST_VERSION = MAKE_VERSION_NUMBER(0, 9, 62); // 0.9.62
const size_t NETDB_MAX_NUM_SEARCH_REPLY_PEER_HASHES = 16; const size_t NETDB_MAX_NUM_SEARCH_REPLY_PEER_HASHES = 16;
const size_t NETDB_MAX_EXPLORATORY_SELECTION_SIZE = 500; const size_t NETDB_MAX_EXPLORATORY_SELECTION_SIZE = 500;
const int NETDB_EXPLORATORY_SELECTION_UPDATE_INTERVAL = 82; // in seconds const int NETDB_EXPLORATORY_SELECTION_UPDATE_INTERVAL = 82; // in seconds. for floodfill
const int NETDB_NEXT_DAY_ROUTER_INFO_THRESHOLD = 45; // in minutes
const int NETDB_NEXT_DAY_LEASESET_THRESHOLD = 10; // in minutes
/** function for visiting a leaseset stored in a floodfill */ /** function for visiting a leaseset stored in a floodfill */
typedef std::function<void(const IdentHash, std::shared_ptr<LeaseSet>)> LeaseSetVisitor; typedef std::function<void(const IdentHash, std::shared_ptr<LeaseSet>)> LeaseSetVisitor;
@@ -85,27 +88,22 @@ namespace data
std::shared_ptr<RouterProfile> FindRouterProfile (const IdentHash& ident) const; std::shared_ptr<RouterProfile> FindRouterProfile (const IdentHash& ident) const;
void RequestDestination (const IdentHash& destination, RequestedDestination::RequestComplete requestComplete = nullptr, bool direct = true); void RequestDestination (const IdentHash& destination, RequestedDestination::RequestComplete requestComplete = nullptr, bool direct = true);
void RequestDestinationFrom (const IdentHash& destination, const IdentHash & from, bool exploritory, RequestedDestination::RequestComplete requestComplete = nullptr);
void HandleDatabaseStoreMsg (std::shared_ptr<const I2NPMessage> msg);
void HandleDatabaseSearchReplyMsg (std::shared_ptr<const I2NPMessage> msg);
void HandleDatabaseLookupMsg (std::shared_ptr<const I2NPMessage> msg);
void HandleNTCP2RouterInfoMsg (std::shared_ptr<const I2NPMessage> m);
std::shared_ptr<const RouterInfo> GetRandomRouter () const; std::shared_ptr<const RouterInfo> GetRandomRouter () const;
std::shared_ptr<const RouterInfo> GetRandomRouter (std::shared_ptr<const RouterInfo> compatibleWith, bool reverse, bool endpoint) const; std::shared_ptr<const RouterInfo> GetRandomRouter (std::shared_ptr<const RouterInfo> compatibleWith, bool reverse, bool endpoint, bool clientTunnel) const;
std::shared_ptr<const RouterInfo> GetHighBandwidthRandomRouter (std::shared_ptr<const RouterInfo> compatibleWith, bool reverse, bool endpoint) const; std::shared_ptr<const RouterInfo> GetHighBandwidthRandomRouter (std::shared_ptr<const RouterInfo> compatibleWith, bool reverse, bool endpoint) const;
std::shared_ptr<const RouterInfo> GetRandomSSU2PeerTestRouter (bool v4, const std::set<IdentHash>& excluded) const; std::shared_ptr<const RouterInfo> GetRandomSSU2PeerTestRouter (bool v4, const std::unordered_set<IdentHash>& excluded) const;
std::shared_ptr<const RouterInfo> GetRandomSSU2Introducer (bool v4, const std::set<IdentHash>& excluded) const; std::shared_ptr<const RouterInfo> GetRandomSSU2Introducer (bool v4, const std::unordered_set<IdentHash>& excluded) const;
std::shared_ptr<const RouterInfo> GetClosestFloodfill (const IdentHash& destination, const std::set<IdentHash>& excluded) const; std::shared_ptr<const RouterInfo> GetClosestFloodfill (const IdentHash& destination, const std::unordered_set<IdentHash>& excluded, bool nextDay = false) const;
std::vector<IdentHash> GetClosestFloodfills (const IdentHash& destination, size_t num, std::vector<IdentHash> GetClosestFloodfills (const IdentHash& destination, size_t num,
std::set<IdentHash>& excluded, bool closeThanUsOnly = false) const; std::unordered_set<IdentHash>& excluded, bool closeThanUsOnly = false) const;
std::vector<IdentHash> GetExploratoryNonFloodfill (const IdentHash& destination, size_t num, const std::set<IdentHash>& excluded); std::vector<IdentHash> GetExploratoryNonFloodfill (const IdentHash& destination, size_t num, const std::unordered_set<IdentHash>& excluded);
std::shared_ptr<const RouterInfo> GetRandomRouterInFamily (FamilyID fam) const; std::shared_ptr<const RouterInfo> GetRandomRouterInFamily (FamilyID fam) const;
void SetUnreachable (const IdentHash& ident, bool unreachable); void SetUnreachable (const IdentHash& ident, bool unreachable);
void ExcludeReachableTransports (const IdentHash& ident, RouterInfo::CompatibleTransports transports); void ExcludeReachableTransports (const IdentHash& ident, RouterInfo::CompatibleTransports transports);
void PostI2NPMsg (std::shared_ptr<const I2NPMessage> msg); void PostI2NPMsg (std::shared_ptr<const I2NPMessage> msg);
void PostDatabaseSearchReplyMsg (std::shared_ptr<const I2NPMessage> msg); // to NetdbReq thread
void Reseed (); void Reseed ();
Families& GetFamilies () { return m_Families; }; Families& GetFamilies () { return m_Families; };
@@ -125,22 +123,24 @@ namespace data
size_t VisitRandomRouterInfos(RouterInfoFilter f, RouterInfoVisitor v, size_t n); size_t VisitRandomRouterInfos(RouterInfoFilter f, RouterInfoVisitor v, size_t n);
void ClearRouterInfos () { m_RouterInfos.clear (); }; void ClearRouterInfos () { m_RouterInfos.clear (); };
std::shared_ptr<RouterInfo::Buffer> NewRouterInfoBuffer () { return m_RouterInfoBuffersPool.AcquireSharedMt (); }; template<typename... TArgs>
std::shared_ptr<RouterInfo::Buffer> NewRouterInfoBuffer (TArgs&&... args)
{
return m_RouterInfoBuffersPool.AcquireSharedMt (std::forward<TArgs>(args)...);
}
bool PopulateRouterInfoBuffer (std::shared_ptr<RouterInfo> r); bool PopulateRouterInfoBuffer (std::shared_ptr<RouterInfo> r);
std::shared_ptr<RouterInfo::Address> NewRouterInfoAddress () { return m_RouterInfoAddressesPool.AcquireSharedMt (); }; std::shared_ptr<RouterInfo::Address> NewRouterInfoAddress () { return m_RouterInfoAddressesPool.AcquireSharedMt (); };
boost::shared_ptr<RouterInfo::Addresses> NewRouterInfoAddresses () RouterInfo::AddressesPtr NewRouterInfoAddresses ()
{ {
return boost::shared_ptr<RouterInfo::Addresses>(m_RouterInfoAddressVectorsPool.AcquireMt (), return RouterInfo::AddressesPtr{m_RouterInfoAddressVectorsPool.AcquireMt (),
std::bind <void (i2p::util::MemoryPoolMt<RouterInfo::Addresses>::*)(RouterInfo::Addresses *)> std::bind <void (i2p::util::MemoryPoolMt<RouterInfo::Addresses>::*)(RouterInfo::Addresses *)>
(&i2p::util::MemoryPoolMt<RouterInfo::Addresses>::ReleaseMt, (&i2p::util::MemoryPoolMt<RouterInfo::Addresses>::ReleaseMt,
&m_RouterInfoAddressVectorsPool, std::placeholders::_1)); &m_RouterInfoAddressVectorsPool, std::placeholders::_1)};
}; };
std::shared_ptr<Lease> NewLease (const Lease& lease) { return m_LeasesPool.AcquireSharedMt (lease); }; std::shared_ptr<Lease> NewLease (const Lease& lease) { return m_LeasesPool.AcquireSharedMt (lease); };
std::shared_ptr<IdentityEx> NewIdentity (const uint8_t * buf, size_t len) { return m_IdentitiesPool.AcquireSharedMt (buf, len); }; std::shared_ptr<IdentityEx> NewIdentity (const uint8_t * buf, size_t len) { return m_IdentitiesPool.AcquireSharedMt (buf, len); };
std::shared_ptr<RouterProfile> NewRouterProfile () { return m_RouterProfilesPool.AcquireSharedMt (); }; std::shared_ptr<RouterProfile> NewRouterProfile () { return m_RouterProfilesPool.AcquireSharedMt (); };
uint32_t GetPublishReplyToken () const { return m_PublishReplyToken; };
private: private:
void Load (); void Load ();
@@ -148,9 +148,8 @@ namespace data
void SaveUpdated (); void SaveUpdated ();
void PersistRouters (std::list<std::pair<std::string, std::shared_ptr<RouterInfo::Buffer> > >&& update, void PersistRouters (std::list<std::pair<std::string, std::shared_ptr<RouterInfo::Buffer> > >&& update,
std::list<std::string>&& remove); std::list<std::string>&& remove);
void Run (); // exploratory thread void Run ();
void Explore (int numDestinations); void Flood (const IdentHash& ident, std::shared_ptr<I2NPMessage> floodMsg, bool andNextDay = false);
void Flood (const IdentHash& ident, std::shared_ptr<I2NPMessage> floodMsg);
void ManageRouterInfos (); void ManageRouterInfos ();
void ManageLeaseSets (); void ManageLeaseSets ();
void ManageRequests (); void ManageRequests ();
@@ -163,6 +162,10 @@ namespace data
template<typename Filter> template<typename Filter>
std::shared_ptr<const RouterInfo> GetRandomRouter (Filter filter) const; std::shared_ptr<const RouterInfo> GetRandomRouter (Filter filter) const;
void HandleDatabaseStoreMsg (std::shared_ptr<const I2NPMessage> msg);
void HandleDatabaseLookupMsg (std::shared_ptr<const I2NPMessage> msg);
void HandleNTCP2RouterInfoMsg (std::shared_ptr<const I2NPMessage> m);
private: private:
mutable std::mutex m_LeaseSetsMutex; mutable std::mutex m_LeaseSetsMutex;
@@ -184,16 +187,11 @@ namespace data
std::shared_ptr<NetDbRequests> m_Requests; std::shared_ptr<NetDbRequests> m_Requests;
bool m_PersistProfiles; bool m_PersistProfiles;
std::future<void> m_SavingProfiles, m_DeletingProfiles, m_PersistingRouters; std::future<void> m_SavingProfiles, m_DeletingProfiles, m_ApplyingProfileUpdates, m_PersistingRouters;
/** router info we are bootstrapping from or nullptr if we are not currently doing that*/
std::shared_ptr<RouterInfo> m_FloodfillBootstrap;
std::set<IdentHash> m_PublishExcluded;
uint32_t m_PublishReplyToken = 0;
std::vector<std::shared_ptr<const RouterInfo> > m_ExploratorySelection; std::vector<std::shared_ptr<const RouterInfo> > m_ExploratorySelection;
uint64_t m_LastExploratorySelectionUpdateTime; // in monotonic seconds uint64_t m_LastExploratorySelectionUpdateTime; // in monotonic seconds
std::mt19937 m_Rng;
i2p::util::MemoryPoolMt<RouterInfo::Buffer> m_RouterInfoBuffersPool; i2p::util::MemoryPoolMt<RouterInfo::Buffer> m_RouterInfoBuffersPool;
i2p::util::MemoryPoolMt<RouterInfo::Address> m_RouterInfoAddressesPool; i2p::util::MemoryPoolMt<RouterInfo::Address> m_RouterInfoAddressesPool;

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2024, The PurpleI2P Project * Copyright (c) 2013-2025, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@@ -10,28 +10,33 @@
#include "I2NPProtocol.h" #include "I2NPProtocol.h"
#include "Transports.h" #include "Transports.h"
#include "NetDb.hpp" #include "NetDb.hpp"
#include "NetDbRequests.h"
#include "ECIESX25519AEADRatchetSession.h" #include "ECIESX25519AEADRatchetSession.h"
#include "RouterContext.h"
#include "Timestamp.h"
#include "NetDbRequests.h"
namespace i2p namespace i2p
{ {
namespace data namespace data
{ {
RequestedDestination::RequestedDestination (const IdentHash& destination, bool isExploratory, bool direct): RequestedDestination::RequestedDestination (const IdentHash& destination, bool isExploratory, bool direct):
m_Destination (destination), m_IsExploratory (isExploratory), m_IsDirect (direct), m_IsActive (true), m_Destination (destination), m_IsExploratory (isExploratory), m_IsDirect (direct),
m_CreationTime (i2p::util::GetSecondsSinceEpoch ()), m_LastRequestTime (0) m_IsActive (true), m_IsSentDirectly (false),
m_CreationTime (i2p::util::GetMillisecondsSinceEpoch ()),
m_LastRequestTime (0), m_NumAttempts (0)
{ {
if (i2p::context.IsFloodfill ())
m_ExcludedPeers.insert (i2p::context.GetIdentHash ()); // exclude self if floodfill
} }
RequestedDestination::~RequestedDestination () RequestedDestination::~RequestedDestination ()
{ {
if (m_RequestComplete) m_RequestComplete (nullptr); InvokeRequestComplete (nullptr);
} }
std::shared_ptr<I2NPMessage> RequestedDestination::CreateRequestMessage (std::shared_ptr<const RouterInfo> router, std::shared_ptr<I2NPMessage> RequestedDestination::CreateRequestMessage (std::shared_ptr<const RouterInfo> router,
std::shared_ptr<const i2p::tunnel::InboundTunnel> replyTunnel) std::shared_ptr<const i2p::tunnel::InboundTunnel> replyTunnel)
{ {
std::lock_guard<std::mutex> l (m_ExcludedPeersMutex);
std::shared_ptr<I2NPMessage> msg; std::shared_ptr<I2NPMessage> msg;
if(replyTunnel) if(replyTunnel)
msg = i2p::CreateRouterInfoDatabaseLookupMsg (m_Destination, msg = i2p::CreateRouterInfoDatabaseLookupMsg (m_Destination,
@@ -41,54 +46,49 @@ namespace data
msg = i2p::CreateRouterInfoDatabaseLookupMsg(m_Destination, i2p::context.GetIdentHash(), 0, m_IsExploratory, &m_ExcludedPeers); msg = i2p::CreateRouterInfoDatabaseLookupMsg(m_Destination, i2p::context.GetIdentHash(), 0, m_IsExploratory, &m_ExcludedPeers);
if(router) if(router)
m_ExcludedPeers.insert (router->GetIdentHash ()); m_ExcludedPeers.insert (router->GetIdentHash ());
m_LastRequestTime = i2p::util::GetSecondsSinceEpoch (); m_LastRequestTime = i2p::util::GetMillisecondsSinceEpoch ();
m_NumAttempts++;
m_IsSentDirectly = false;
return msg; return msg;
} }
std::shared_ptr<I2NPMessage> RequestedDestination::CreateRequestMessage (const IdentHash& floodfill) std::shared_ptr<I2NPMessage> RequestedDestination::CreateRequestMessage (const IdentHash& floodfill)
{ {
std::lock_guard<std::mutex> l (m_ExcludedPeersMutex);
auto msg = i2p::CreateRouterInfoDatabaseLookupMsg (m_Destination, auto msg = i2p::CreateRouterInfoDatabaseLookupMsg (m_Destination,
i2p::context.GetRouterInfo ().GetIdentHash () , 0, false, &m_ExcludedPeers); i2p::context.GetRouterInfo ().GetIdentHash () , 0, false, &m_ExcludedPeers);
m_ExcludedPeers.insert (floodfill); m_ExcludedPeers.insert (floodfill);
m_LastRequestTime = i2p::util::GetSecondsSinceEpoch (); m_NumAttempts++;
m_LastRequestTime = i2p::util::GetMillisecondsSinceEpoch ();
m_IsSentDirectly = true;
return msg; return msg;
} }
bool RequestedDestination::IsExcluded (const IdentHash& ident) const bool RequestedDestination::IsExcluded (const IdentHash& ident) const
{ {
std::lock_guard<std::mutex> l (m_ExcludedPeersMutex);
return m_ExcludedPeers.count (ident); return m_ExcludedPeers.count (ident);
} }
void RequestedDestination::ClearExcludedPeers () void RequestedDestination::ClearExcludedPeers ()
{ {
std::lock_guard<std::mutex> l (m_ExcludedPeersMutex);
m_ExcludedPeers.clear (); m_ExcludedPeers.clear ();
} }
std::set<IdentHash> RequestedDestination::GetExcludedPeers () const void RequestedDestination::InvokeRequestComplete (std::shared_ptr<RouterInfo> r)
{ {
std::lock_guard<std::mutex> l (m_ExcludedPeersMutex); if (!m_RequestComplete.empty ())
return m_ExcludedPeers; {
} for (auto it: m_RequestComplete)
if (it != nullptr) it (r);
size_t RequestedDestination::GetNumExcludedPeers () const m_RequestComplete.clear ();
{ }
std::lock_guard<std::mutex> l (m_ExcludedPeersMutex); }
return m_ExcludedPeers.size ();
}
void RequestedDestination::Success (std::shared_ptr<RouterInfo> r) void RequestedDestination::Success (std::shared_ptr<RouterInfo> r)
{ {
if (m_IsActive) if (m_IsActive)
{ {
m_IsActive = false; m_IsActive = false;
if (m_RequestComplete) InvokeRequestComplete (r);
{
m_RequestComplete (r);
m_RequestComplete = nullptr;
}
} }
} }
@@ -97,84 +97,115 @@ namespace data
if (m_IsActive) if (m_IsActive)
{ {
m_IsActive = false; m_IsActive = false;
if (m_RequestComplete) InvokeRequestComplete (nullptr);
{
m_RequestComplete (nullptr);
m_RequestComplete = nullptr;
}
} }
} }
NetDbRequests::NetDbRequests ():
RunnableServiceWithWork ("NetDbReq"),
m_ManageRequestsTimer (GetIOService ()), m_ExploratoryTimer (GetIOService ()),
m_CleanupTimer (GetIOService ()), m_DiscoveredRoutersTimer (GetIOService ()),
m_Rng(i2p::util::GetMonotonicMicroseconds () % 1000000LL)
{
}
NetDbRequests::~NetDbRequests ()
{
Stop ();
}
void NetDbRequests::Start () void NetDbRequests::Start ()
{ {
m_LastPoolCleanUpTime = i2p::util::GetSecondsSinceEpoch (); if (!IsRunning ())
{
StartIOService ();
ScheduleManageRequests ();
ScheduleCleanup ();
if (!i2p::context.IsHidden ())
ScheduleExploratory (EXPLORATORY_REQUEST_INTERVAL);
}
} }
void NetDbRequests::Stop () void NetDbRequests::Stop ()
{ {
m_RequestedDestinations.clear (); if (IsRunning ())
m_RequestedDestinationsPool.CleanUpMt (); {
m_ManageRequestsTimer.cancel ();
m_ExploratoryTimer.cancel ();
m_CleanupTimer.cancel ();
StopIOService ();
m_RequestedDestinations.clear ();
m_RequestedDestinationsPool.CleanUpMt ();
}
} }
void NetDbRequests::ScheduleCleanup ()
{
m_CleanupTimer.expires_from_now (boost::posix_time::seconds(REQUESTED_DESTINATIONS_POOL_CLEANUP_INTERVAL));
m_CleanupTimer.async_wait (std::bind (&NetDbRequests::HandleCleanupTimer,
this, std::placeholders::_1));
}
void NetDbRequests::HandleCleanupTimer (const boost::system::error_code& ecode)
{
if (ecode != boost::asio::error::operation_aborted)
{
m_RequestedDestinationsPool.CleanUpMt ();
ScheduleCleanup ();
}
}
std::shared_ptr<RequestedDestination> NetDbRequests::CreateRequest (const IdentHash& destination, std::shared_ptr<RequestedDestination> NetDbRequests::CreateRequest (const IdentHash& destination,
bool isExploratory, bool direct, RequestedDestination::RequestComplete requestComplete) bool isExploratory, bool direct, RequestedDestination::RequestComplete requestComplete)
{ {
// request RouterInfo directly // request RouterInfo directly
auto dest = m_RequestedDestinationsPool.AcquireSharedMt (destination, isExploratory, direct); auto dest = m_RequestedDestinationsPool.AcquireSharedMt (destination, isExploratory, direct);
dest->SetRequestComplete (requestComplete); if (requestComplete)
{ dest->AddRequestComplete (requestComplete);
std::unique_lock<std::mutex> l(m_RequestedDestinationsMutex);
auto ret = m_RequestedDestinations.emplace (destination, dest); auto ret = m_RequestedDestinations.emplace (destination, dest);
if (!ret.second) // not inserted if (!ret.second) // not inserted
{
dest->ResetRequestComplete (); // don't call requestComplete in destructor
dest = ret.first->second; // existing one
if (requestComplete)
{ {
dest->SetRequestComplete (nullptr); // don't call requestComplete in destructor if (dest->IsActive ())
dest = ret.first->second; // existing one dest->AddRequestComplete (requestComplete);
if (requestComplete && dest->IsActive ()) else
{ requestComplete (nullptr);
auto prev = dest->GetRequestComplete ();
if (prev) // if already set
dest->SetRequestComplete (
[requestComplete, prev](std::shared_ptr<RouterInfo> r)
{
prev (r); // call previous
requestComplete (r); // then new
});
else
dest->SetRequestComplete (requestComplete);
}
return nullptr;
} }
} return nullptr;
}
return dest; return dest;
} }
void NetDbRequests::RequestComplete (const IdentHash& ident, std::shared_ptr<RouterInfo> r) void NetDbRequests::RequestComplete (const IdentHash& ident, std::shared_ptr<RouterInfo> r)
{ {
std::shared_ptr<RequestedDestination> request; boost::asio::post (GetIOService (), [this, ident, r]()
{ {
std::unique_lock<std::mutex> l(m_RequestedDestinationsMutex); std::shared_ptr<RequestedDestination> request;
auto it = m_RequestedDestinations.find (ident); auto it = m_RequestedDestinations.find (ident);
if (it != m_RequestedDestinations.end ()) if (it != m_RequestedDestinations.end ())
{ {
request = it->second; request = it->second;
if (request->IsExploratory ()) if (request->IsExploratory ())
m_RequestedDestinations.erase (it); m_RequestedDestinations.erase (it);
// otherwise cache for a while // otherwise cache for a while
} }
} if (request)
if (request) {
{ if (r)
if (r) request->Success (r);
request->Success (r); else
else request->Fail ();
request->Fail (); }
} });
} }
std::shared_ptr<RequestedDestination> NetDbRequests::FindRequest (const IdentHash& ident) const std::shared_ptr<RequestedDestination> NetDbRequests::FindRequest (const IdentHash& ident) const
{ {
std::unique_lock<std::mutex> l(m_RequestedDestinationsMutex);
auto it = m_RequestedDestinations.find (ident); auto it = m_RequestedDestinations.find (ident);
if (it != m_RequestedDestinations.end ()) if (it != m_RequestedDestinations.end ())
return it->second; return it->second;
@@ -183,13 +214,7 @@ namespace data
void NetDbRequests::ManageRequests () void NetDbRequests::ManageRequests ()
{ {
uint64_t ts = i2p::util::GetSecondsSinceEpoch (); uint64_t ts = i2p::util::GetMillisecondsSinceEpoch ();
if (ts > m_LastPoolCleanUpTime + REQUESTED_DESTINATIONS_POOL_CLEANUP_INTERVAL)
{
m_RequestedDestinationsPool.CleanUpMt ();
m_LastPoolCleanUpTime = ts;
}
std::unique_lock<std::mutex> l(m_RequestedDestinationsMutex);
for (auto it = m_RequestedDestinations.begin (); it != m_RequestedDestinations.end ();) for (auto it = m_RequestedDestinations.begin (); it != m_RequestedDestinations.end ();)
{ {
auto& dest = it->second; auto& dest = it->second;
@@ -201,7 +226,8 @@ namespace data
bool done = false; bool done = false;
if (ts < dest->GetCreationTime () + MAX_REQUEST_TIME) if (ts < dest->GetCreationTime () + MAX_REQUEST_TIME)
{ {
if (ts > dest->GetLastRequestTime () + MIN_REQUEST_TIME) // try next floodfill if no response after min interval if (ts > dest->GetLastRequestTime () + (dest->IsSentDirectly () ? MIN_DIRECT_REQUEST_TIME : MIN_REQUEST_TIME))
// try next floodfill if no response after min interval
done = !SendNextRequest (dest); done = !SendNextRequest (dest);
} }
else // request is expired else // request is expired
@@ -231,7 +257,7 @@ namespace data
{ {
if (!dest || !dest->IsActive ()) return false; if (!dest || !dest->IsActive ()) return false;
bool ret = true; bool ret = true;
auto count = dest->GetNumExcludedPeers (); auto count = dest->GetNumAttempts ();
if (!dest->IsExploratory () && count < MAX_NUM_REQUEST_ATTEMPTS) if (!dest->IsExploratory () && count < MAX_NUM_REQUEST_ATTEMPTS)
{ {
auto nextFloodfill = netdb.GetClosestFloodfill (dest->GetDestination (), dest->GetExcludedPeers ()); auto nextFloodfill = netdb.GetClosestFloodfill (dest->GetDestination (), dest->GetExcludedPeers ());
@@ -241,13 +267,23 @@ namespace data
if (direct && !nextFloodfill->IsReachableFrom (i2p::context.GetRouterInfo ()) && if (direct && !nextFloodfill->IsReachableFrom (i2p::context.GetRouterInfo ()) &&
!i2p::transport::transports.IsConnected (nextFloodfill->GetIdentHash ())) !i2p::transport::transports.IsConnected (nextFloodfill->GetIdentHash ()))
direct = false; // floodfill can't be reached directly direct = false; // floodfill can't be reached directly
auto s = shared_from_this ();
auto onDrop = [s, dest]()
{
if (dest->IsActive ())
{
boost::asio::post (s->GetIOService (), [s, dest]()
{
if (dest->IsActive ()) s->SendNextRequest (dest);
});
}
};
if (direct) if (direct)
{ {
if (CheckLogLevel (eLogDebug)) if (CheckLogLevel (eLogDebug))
LogPrint (eLogDebug, "NetDbReq: Try ", dest->GetDestination ().ToBase64 (), " at ", count, " floodfill ", nextFloodfill->GetIdentHash ().ToBase64 (), " directly"); LogPrint (eLogDebug, "NetDbReq: Try ", dest->GetDestination ().ToBase64 (), " at ", count, " floodfill ", nextFloodfill->GetIdentHash ().ToBase64 (), " directly");
auto msg = dest->CreateRequestMessage (nextFloodfill->GetIdentHash ()); auto msg = dest->CreateRequestMessage (nextFloodfill->GetIdentHash ());
auto s = shared_from_this (); msg->onDrop = onDrop;
msg->onDrop = [s, dest]() { if (dest->IsActive ()) s->SendNextRequest (dest); };
i2p::transport::transports.SendMessage (nextFloodfill->GetIdentHash (), msg); i2p::transport::transports.SendMessage (nextFloodfill->GetIdentHash (), msg);
} }
else else
@@ -262,8 +298,7 @@ namespace data
if (CheckLogLevel (eLogDebug)) if (CheckLogLevel (eLogDebug))
LogPrint (eLogDebug, "NetDbReq: Try ", dest->GetDestination ().ToBase64 (), " at ", count, " floodfill ", nextFloodfill->GetIdentHash ().ToBase64 (), " through tunnels"); LogPrint (eLogDebug, "NetDbReq: Try ", dest->GetDestination ().ToBase64 (), " at ", count, " floodfill ", nextFloodfill->GetIdentHash ().ToBase64 (), " through tunnels");
auto msg = dest->CreateRequestMessage (nextFloodfill, inbound); auto msg = dest->CreateRequestMessage (nextFloodfill, inbound);
auto s = shared_from_this (); msg->onDrop = onDrop;
msg->onDrop = [s, dest]() { if (dest->IsActive ()) s->SendNextRequest (dest); };
outbound->SendTunnelDataMsgTo (nextFloodfill->GetIdentHash (), 0, outbound->SendTunnelDataMsgTo (nextFloodfill->GetIdentHash (), 0,
i2p::garlic::WrapECIESX25519MessageForRouter (msg, nextFloodfill->GetIdentity ()->GetEncryptionPublicKey ())); i2p::garlic::WrapECIESX25519MessageForRouter (msg, nextFloodfill->GetIdentity ()->GetEncryptionPublicKey ()));
} }
@@ -284,16 +319,243 @@ namespace data
else else
{ {
ret = false; ret = false;
if (!nextFloodfill) LogPrint (eLogWarning, "NetDbReq: No more floodfills"); LogPrint (eLogWarning, "NetDbReq: No more floodfills for ", dest->GetDestination ().ToBase64 (), " after ", count, "attempts");
} }
} }
else else
{ {
if (!dest->IsExploratory ()) if (!dest->IsExploratory ())
LogPrint (eLogWarning, "NetDbReq: ", dest->GetDestination ().ToBase64 (), " not found after 7 attempts"); LogPrint (eLogWarning, "NetDbReq: ", dest->GetDestination ().ToBase64 (), " not found after ", MAX_NUM_REQUEST_ATTEMPTS," attempts");
ret = false; ret = false;
} }
return ret; return ret;
} }
void NetDbRequests::ScheduleManageRequests ()
{
m_ManageRequestsTimer.expires_from_now (boost::posix_time::milliseconds(MANAGE_REQUESTS_INTERVAL +
m_Rng () % MANAGE_REQUESTS_INTERVAL_VARIANCE));
m_ManageRequestsTimer.async_wait (std::bind (&NetDbRequests::HandleManageRequestsTimer,
this, std::placeholders::_1));
}
void NetDbRequests::HandleManageRequestsTimer (const boost::system::error_code& ecode)
{
if (ecode != boost::asio::error::operation_aborted)
{
if (i2p::tunnel::tunnels.GetExploratoryPool ()) // expolratory pool is ready?
ManageRequests ();
ScheduleManageRequests ();
}
}
void NetDbRequests::PostDatabaseSearchReplyMsg (std::shared_ptr<const I2NPMessage> msg)
{
boost::asio::post (GetIOService (), [this, msg]()
{
HandleDatabaseSearchReplyMsg (msg);
});
}
void NetDbRequests::HandleDatabaseSearchReplyMsg (std::shared_ptr<const I2NPMessage> msg)
{
const uint8_t * buf = msg->GetPayload ();
char key[48];
int l = i2p::data::ByteStreamToBase64 (buf, 32, key, 48);
key[l] = 0;
size_t num = buf[32]; // num
LogPrint (eLogDebug, "NetDbReq: DatabaseSearchReply for ", key, " num=", num);
IdentHash ident (buf);
bool isExploratory = false;
auto dest = FindRequest (ident);
if (dest && dest->IsActive ())
{
isExploratory = dest->IsExploratory ();
if (!isExploratory && (num > 0 || dest->GetNumAttempts () < 3)) // before 3-rd attempt might be just bad luck
{
// try to send next requests
if (!SendNextRequest (dest))
RequestComplete (ident, nullptr);
}
else
// no more requests for destination possible. delete it
RequestComplete (ident, nullptr);
}
else /*if (!m_FloodfillBootstrap)*/
{
LogPrint (eLogInfo, "NetDbReq: Unsolicited or late database search reply for ", key);
return;
}
// try responses
if (num > NETDB_MAX_NUM_SEARCH_REPLY_PEER_HASHES)
{
LogPrint (eLogWarning, "NetDbReq: Too many peer hashes ", num, " in database search reply, Reduced to ", NETDB_MAX_NUM_SEARCH_REPLY_PEER_HASHES);
num = NETDB_MAX_NUM_SEARCH_REPLY_PEER_HASHES;
}
if (isExploratory && !m_DiscoveredRouterHashes.empty ())
{
// request outstanding routers
for (auto it: m_DiscoveredRouterHashes)
RequestRouter (it);
m_DiscoveredRouterHashes.clear ();
m_DiscoveredRoutersTimer.cancel ();
}
for (size_t i = 0; i < num; i++)
{
IdentHash router (buf + 33 + i*32);
if (CheckLogLevel (eLogDebug))
LogPrint (eLogDebug, "NetDbReq: ", i, ": ", router.ToBase64 ());
if (isExploratory)
// postpone request
m_DiscoveredRouterHashes.push_back (router);
else
// send request right a way
RequestRouter (router);
}
if (isExploratory && !m_DiscoveredRouterHashes.empty ())
ScheduleDiscoveredRoutersRequest ();
}
void NetDbRequests::RequestRouter (const IdentHash& router)
{
auto r = netdb.FindRouter (router);
if (!r || i2p::util::GetMillisecondsSinceEpoch () > r->GetTimestamp () + 3600*1000LL)
{
// router with ident not found or too old (1 hour)
LogPrint (eLogDebug, "NetDbReq: Found new/outdated router. Requesting RouterInfo...");
if (!IsRouterBanned (router))
RequestDestination (router, nullptr, true);
else
LogPrint (eLogDebug, "NetDbReq: Router ", router.ToBase64 (), " is banned. Skipped");
}
else
LogPrint (eLogDebug, "NetDbReq: [:|||:]");
}
void NetDbRequests::PostRequestDestination (const IdentHash& destination,
const RequestedDestination::RequestComplete& requestComplete, bool direct)
{
boost::asio::post (GetIOService (), [this, destination, requestComplete, direct]()
{
RequestDestination (destination, requestComplete, direct);
});
}
void NetDbRequests::RequestDestination (const IdentHash& destination, const RequestedDestination::RequestComplete& requestComplete, bool direct)
{
auto dest = CreateRequest (destination, false, direct, requestComplete); // non-exploratory
if (dest)
{
if (!SendNextRequest (dest))
RequestComplete (destination, nullptr);
}
else
LogPrint (eLogWarning, "NetDbReq: Destination ", destination.ToBase64(), " is requested already or cached");
}
void NetDbRequests::Explore (int numDestinations)
{
// new requests
auto exploratoryPool = i2p::tunnel::tunnels.GetExploratoryPool ();
auto outbound = exploratoryPool ? exploratoryPool->GetNextOutboundTunnel () : nullptr;
auto inbound = exploratoryPool ? exploratoryPool->GetNextInboundTunnel () : nullptr;
bool throughTunnels = outbound && inbound;
uint8_t randomHash[32];
std::vector<i2p::tunnel::TunnelMessageBlock> msgs;
LogPrint (eLogInfo, "NetDbReq: Exploring new ", numDestinations, " routers ...");
for (int i = 0; i < numDestinations; i++)
{
RAND_bytes (randomHash, 32);
auto dest = CreateRequest (randomHash, true, !throughTunnels); // exploratory
if (!dest)
{
LogPrint (eLogWarning, "NetDbReq: Exploratory destination is requested already");
return;
}
auto floodfill = netdb.GetClosestFloodfill (randomHash, dest->GetExcludedPeers ());
if (floodfill)
{
if (i2p::transport::transports.IsConnected (floodfill->GetIdentHash ()))
throughTunnels = false;
if (throughTunnels)
{
msgs.push_back (i2p::tunnel::TunnelMessageBlock
{
i2p::tunnel::eDeliveryTypeRouter,
floodfill->GetIdentHash (), 0,
CreateDatabaseStoreMsg () // tell floodfill about us
});
msgs.push_back (i2p::tunnel::TunnelMessageBlock
{
i2p::tunnel::eDeliveryTypeRouter,
floodfill->GetIdentHash (), 0,
dest->CreateRequestMessage (floodfill, inbound) // explore
});
}
else
i2p::transport::transports.SendMessage (floodfill->GetIdentHash (), dest->CreateRequestMessage (floodfill->GetIdentHash ()));
}
else
RequestComplete (randomHash, nullptr);
}
if (throughTunnels && msgs.size () > 0)
outbound->SendTunnelDataMsgs (msgs);
}
void NetDbRequests::ScheduleExploratory (uint64_t interval)
{
m_ExploratoryTimer.expires_from_now (boost::posix_time::seconds(interval));
m_ExploratoryTimer.async_wait (std::bind (&NetDbRequests::HandleExploratoryTimer,
this, std::placeholders::_1));
}
void NetDbRequests::HandleExploratoryTimer (const boost::system::error_code& ecode)
{
if (ecode != boost::asio::error::operation_aborted)
{
auto numRouters = netdb.GetNumRouters ();
auto nextExploratoryInterval = numRouters < 2500 ? (EXPLORATORY_REQUEST_INTERVAL + m_Rng () % EXPLORATORY_REQUEST_INTERVAL)/2 :
EXPLORATORY_REQUEST_INTERVAL + m_Rng () % EXPLORATORY_REQUEST_INTERVAL_VARIANCE;
if (numRouters)
{
if (i2p::transport::transports.IsOnline () && i2p::transport::transports.IsRunning ())
{
// explore only if online
numRouters = 800/numRouters;
if (numRouters < 1) numRouters = 1;
if (numRouters > 9) numRouters = 9;
Explore (numRouters);
}
}
else
LogPrint (eLogError, "NetDbReq: No known routers, reseed seems to be totally failed");
ScheduleExploratory (nextExploratoryInterval);
}
}
void NetDbRequests::ScheduleDiscoveredRoutersRequest ()
{
m_DiscoveredRoutersTimer.expires_from_now (boost::posix_time::milliseconds(
DISCOVERED_REQUEST_INTERVAL + m_Rng () % DISCOVERED_REQUEST_INTERVAL_VARIANCE));
m_DiscoveredRoutersTimer.async_wait (std::bind (&NetDbRequests::HandleDiscoveredRoutersTimer,
this, std::placeholders::_1));
}
void NetDbRequests::HandleDiscoveredRoutersTimer (const boost::system::error_code& ecode)
{
if (ecode != boost::asio::error::operation_aborted)
{
if (!m_DiscoveredRouterHashes.empty ())
{
RequestRouter (m_DiscoveredRouterHashes.front ());
m_DiscoveredRouterHashes.pop_front ();
if (!m_DiscoveredRouterHashes.empty ()) // more hashes to request
ScheduleDiscoveredRoutersRequest ();
}
}
}
} }
} }

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2024, The PurpleI2P Project * Copyright (c) 2013-2025, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@@ -11,8 +11,10 @@
#include <inttypes.h> #include <inttypes.h>
#include <memory> #include <memory>
#include <set> #include <random>
#include <unordered_set>
#include <unordered_map> #include <unordered_map>
#include <list>
#include "Identity.h" #include "Identity.h"
#include "RouterInfo.h" #include "RouterInfo.h"
#include "util.h" #include "util.h"
@@ -21,12 +23,18 @@ namespace i2p
{ {
namespace data namespace data
{ {
const size_t MAX_NUM_REQUEST_ATTEMPTS = 5; const int MAX_NUM_REQUEST_ATTEMPTS = 5;
const uint64_t MANAGE_REQUESTS_INTERVAL = 1; // in seconds const uint64_t MANAGE_REQUESTS_INTERVAL = 400; // in milliseconds
const uint64_t MIN_REQUEST_TIME = 5; // in seconds const uint64_t MANAGE_REQUESTS_INTERVAL_VARIANCE = 300; // in milliseconds
const uint64_t MAX_REQUEST_TIME = MAX_NUM_REQUEST_ATTEMPTS * (MIN_REQUEST_TIME + MANAGE_REQUESTS_INTERVAL); const uint64_t MIN_REQUEST_TIME = 1200; // in milliseconds
const uint64_t MAX_EXPLORATORY_REQUEST_TIME = 30; // in seconds const uint64_t MAX_REQUEST_TIME = MAX_NUM_REQUEST_ATTEMPTS * (MIN_REQUEST_TIME + MANAGE_REQUESTS_INTERVAL + MANAGE_REQUESTS_INTERVAL_VARIANCE);
const uint64_t REQUEST_CACHE_TIME = MAX_REQUEST_TIME + 40; // in seconds const uint64_t MIN_DIRECT_REQUEST_TIME = 600; // in milliseconds
const uint64_t EXPLORATORY_REQUEST_INTERVAL = 55; // in seconds
const uint64_t EXPLORATORY_REQUEST_INTERVAL_VARIANCE = 170; // in seconds
const uint64_t DISCOVERED_REQUEST_INTERVAL = 360; // in milliseconds
const uint64_t DISCOVERED_REQUEST_INTERVAL_VARIANCE = 540; // in milliseconds
const uint64_t MAX_EXPLORATORY_REQUEST_TIME = 30000; // in milliseconds
const uint64_t REQUEST_CACHE_TIME = MAX_REQUEST_TIME + 40000; // in milliseconds
const uint64_t REQUESTED_DESTINATIONS_POOL_CLEANUP_INTERVAL = 191; // in seconds const uint64_t REQUESTED_DESTINATIONS_POOL_CLEANUP_INTERVAL = 191; // in seconds
class RequestedDestination class RequestedDestination
@@ -39,54 +47,83 @@ namespace data
~RequestedDestination (); ~RequestedDestination ();
const IdentHash& GetDestination () const { return m_Destination; }; const IdentHash& GetDestination () const { return m_Destination; };
size_t GetNumExcludedPeers () const; const std::unordered_set<IdentHash>& GetExcludedPeers () const { return m_ExcludedPeers; };
std::set<IdentHash> GetExcludedPeers () const; int GetNumAttempts () const { return m_NumAttempts; };
void ClearExcludedPeers (); void ClearExcludedPeers ();
bool IsExploratory () const { return m_IsExploratory; }; bool IsExploratory () const { return m_IsExploratory; };
bool IsDirect () const { return m_IsDirect; }; bool IsDirect () const { return m_IsDirect; };
bool IsActive () const { return m_IsActive; }; bool IsActive () const { return m_IsActive; };
bool IsSentDirectly () const { return m_IsSentDirectly; };
bool IsExcluded (const IdentHash& ident) const; bool IsExcluded (const IdentHash& ident) const;
uint64_t GetCreationTime () const { return m_CreationTime; }; uint64_t GetCreationTime () const { return m_CreationTime; };
uint64_t GetLastRequestTime () const { return m_LastRequestTime; }; uint64_t GetLastRequestTime () const { return m_LastRequestTime; };
std::shared_ptr<I2NPMessage> CreateRequestMessage (std::shared_ptr<const RouterInfo>, std::shared_ptr<const i2p::tunnel::InboundTunnel> replyTunnel); std::shared_ptr<I2NPMessage> CreateRequestMessage (std::shared_ptr<const RouterInfo>, std::shared_ptr<const i2p::tunnel::InboundTunnel> replyTunnel);
std::shared_ptr<I2NPMessage> CreateRequestMessage (const IdentHash& floodfill); std::shared_ptr<I2NPMessage> CreateRequestMessage (const IdentHash& floodfill);
void SetRequestComplete (const RequestComplete& requestComplete) { m_RequestComplete = requestComplete; }; void AddRequestComplete (const RequestComplete& requestComplete) { m_RequestComplete.push_back (requestComplete); };
RequestComplete GetRequestComplete () const { return m_RequestComplete; }; void ResetRequestComplete () { m_RequestComplete.clear (); };
bool IsRequestComplete () const { return m_RequestComplete != nullptr; };
void Success (std::shared_ptr<RouterInfo> r); void Success (std::shared_ptr<RouterInfo> r);
void Fail (); void Fail ();
private: private:
IdentHash m_Destination; void InvokeRequestComplete (std::shared_ptr<RouterInfo> r);
bool m_IsExploratory, m_IsDirect, m_IsActive;
mutable std::mutex m_ExcludedPeersMutex; private:
std::set<IdentHash> m_ExcludedPeers;
uint64_t m_CreationTime, m_LastRequestTime; // in seconds IdentHash m_Destination;
RequestComplete m_RequestComplete; bool m_IsExploratory, m_IsDirect, m_IsActive, m_IsSentDirectly;
}; std::unordered_set<IdentHash> m_ExcludedPeers;
uint64_t m_CreationTime, m_LastRequestTime; // in milliseconds
class NetDbRequests: public std::enable_shared_from_this<NetDbRequests> std::list<RequestComplete> m_RequestComplete;
{ int m_NumAttempts;
public: };
void Start (); class NetDbRequests: public std::enable_shared_from_this<NetDbRequests>,
void Stop (); private i2p::util::RunnableServiceWithWork
{
std::shared_ptr<RequestedDestination> CreateRequest (const IdentHash& destination, bool isExploratory, public:
bool direct = false, RequestedDestination::RequestComplete requestComplete = nullptr);
void RequestComplete (const IdentHash& ident, std::shared_ptr<RouterInfo> r); NetDbRequests ();
std::shared_ptr<RequestedDestination> FindRequest (const IdentHash& ident) const; ~NetDbRequests ();
void ManageRequests ();
bool SendNextRequest (std::shared_ptr<RequestedDestination> dest); void Start ();
void Stop ();
void RequestComplete (const IdentHash& ident, std::shared_ptr<RouterInfo> r);
void PostDatabaseSearchReplyMsg (std::shared_ptr<const I2NPMessage> msg);
void PostRequestDestination (const IdentHash& destination, const RequestedDestination::RequestComplete& requestComplete, bool direct);
private:
std::shared_ptr<RequestedDestination> CreateRequest (const IdentHash& destination, bool isExploratory,
bool direct = false, RequestedDestination::RequestComplete requestComplete = nullptr);
std::shared_ptr<RequestedDestination> FindRequest (const IdentHash& ident) const;
bool SendNextRequest (std::shared_ptr<RequestedDestination> dest);
void HandleDatabaseSearchReplyMsg (std::shared_ptr<const I2NPMessage> msg);
void RequestRouter (const IdentHash& router);
void RequestDestination (const IdentHash& destination, const RequestedDestination::RequestComplete& requestComplete, bool direct);
void Explore (int numDestinations);
void ManageRequests ();
// timer
void ScheduleManageRequests ();
void HandleManageRequestsTimer (const boost::system::error_code& ecode);
void ScheduleExploratory (uint64_t interval);
void HandleExploratoryTimer (const boost::system::error_code& ecode);
void ScheduleCleanup ();
void HandleCleanupTimer (const boost::system::error_code& ecode);
void ScheduleDiscoveredRoutersRequest ();
void HandleDiscoveredRoutersTimer (const boost::system::error_code& ecode);
private: private:
mutable std::mutex m_RequestedDestinationsMutex;
std::unordered_map<IdentHash, std::shared_ptr<RequestedDestination> > m_RequestedDestinations;
i2p::util::MemoryPoolMt<RequestedDestination> m_RequestedDestinationsPool; i2p::util::MemoryPoolMt<RequestedDestination> m_RequestedDestinationsPool;
uint64_t m_LastPoolCleanUpTime = 0; // in seconds std::unordered_map<IdentHash, std::shared_ptr<RequestedDestination> > m_RequestedDestinations;
std::list<IdentHash> m_DiscoveredRouterHashes;
boost::asio::deadline_timer m_ManageRequestsTimer, m_ExploratoryTimer,
m_CleanupTimer, m_DiscoveredRoutersTimer;
std::mt19937 m_Rng;
}; };
} }
} }

View File

@@ -1,25 +0,0 @@
/**
* This code is licensed under the MCGSI Public License
* Copyright 2018 Jeff Becker
*
*Kovri go write your own code
*
*/
#include "Poly1305.h"
#if !OPENSSL_AEAD_CHACHA20_POLY1305
namespace i2p
{
namespace crypto
{
void Poly1305HMAC(uint64_t * out, const uint64_t * key, const uint8_t * buf, std::size_t sz)
{
Poly1305 p(key);
p.Update(buf, sz);
p.Finish(out);
}
}
}
#endif

View File

@@ -1,261 +0,0 @@
/**
* This code is licensed under the MCGSI Public License
* Copyright 2018 Jeff Becker
*
* Kovri go write your own code
*
*/
#ifndef LIBI2PD_POLY1305_H
#define LIBI2PD_POLY1305_H
#include <cstdint>
#include <cstring>
#include "Crypto.h"
#if !OPENSSL_AEAD_CHACHA20_POLY1305
namespace i2p
{
namespace crypto
{
const std::size_t POLY1305_DIGEST_BYTES = 16;
const std::size_t POLY1305_DIGEST_DWORDS = 4;
const std::size_t POLY1305_KEY_BYTES = 32;
const std::size_t POLY1305_KEY_DWORDS = 8;
const std::size_t POLY1305_BLOCK_BYTES = 16;
namespace poly1305
{
struct LongBlock
{
unsigned long data[17];
operator unsigned long * ()
{
return data;
}
};
struct Block
{
unsigned char data[17];
void Zero()
{
memset(data, 0, sizeof(data));
}
operator uint8_t * ()
{
return data;
}
Block & operator += (const Block & other)
{
unsigned short u;
unsigned int i;
for(u = 0, i = 0; i < 17; i++)
{
u += (unsigned short) data[i] + (unsigned short) other.data[i];
data[i] = (unsigned char) u & 0xff;
u >>= 8;
}
return *this;
}
Block & operator %=(const LongBlock & other)
{
unsigned long u;
unsigned int i;
u = 0;
for (i = 0; i < 16; i++) {
u += other.data[i];
data[i] = (unsigned char)u & 0xff;
u >>= 8;
}
u += other.data[16];
data[16] = (unsigned char)u & 0x03;
u >>= 2;
u += (u << 2);
for (i = 0; i < 16; i++) {
u += data[i];
data[i] = (unsigned char)u & 0xff;
u >>= 8;
}
data[16] += (unsigned char)u;
return *this;
}
Block & operator = (const Block & other)
{
memcpy(data, other.data, sizeof(data));
return *this;
}
Block & operator ~ ()
{
static const Block minusp = {
0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0xfc
};
Block orig;
unsigned char neg;
unsigned int i;
orig = *this;
*this += minusp;
neg = -(data[16] >> 7);
for(i = 0; i < 17; i++)
data[i] ^= neg & (orig.data[i] ^ data[i]);
return *this;
}
void PutKey(const uint64_t * key_l)
{
const uint8_t * key = (const uint8_t*) key_l;
data[0] = key[0] & 0xff;
data[1] = key[1] & 0xff;
data[2] = key[2] & 0xff;
data[3] = key[3] & 0x0f;
data[4] = key[4] & 0xfc;
data[5] = key[5] & 0xff;
data[6] = key[6] & 0xff;
data[7] = key[7] & 0x0f;
data[8] = key[8] & 0xfc;
data[9] = key[9] & 0xff;
data[10] = key[10] & 0xff;
data[11] = key[11] & 0x0f;
data[12] = key[12] & 0xfc;
data[13] = key[13] & 0xff;
data[14] = key[14] & 0xff;
data[15] = key[15] & 0x0f;
data[16] = 0;
}
template<typename Int_t>
void Put(const Int_t * d, uint8_t last=0)
{
memcpy(data, d, 16);
data[16] = last;
}
};
struct Buffer
{
uint8_t data[POLY1305_BLOCK_BYTES];
operator uint8_t * ()
{
return data;
}
};
}
struct Poly1305
{
Poly1305(const uint64_t * key)
{
m_Leftover = 0;
m_H.Zero();
m_Final = 0;
m_R.PutKey(key);
m_Pad.Put(key + 2);
}
void Update(const uint8_t * buf, size_t sz)
{
// process leftover
if(m_Leftover)
{
size_t want = POLY1305_BLOCK_BYTES - m_Leftover;
if(want > sz) want = sz;
memcpy(m_Buffer + m_Leftover, buf, want);
sz -= want;
buf += want;
m_Leftover += want;
if(m_Leftover < POLY1305_BLOCK_BYTES) return;
Blocks(m_Buffer, POLY1305_BLOCK_BYTES);
m_Leftover = 0;
}
// process blocks
if(sz >= POLY1305_BLOCK_BYTES)
{
size_t want = (sz & ~(POLY1305_BLOCK_BYTES - 1));
Blocks(buf, want);
buf += want;
sz -= want;
}
// leftover
if(sz)
{
memcpy(m_Buffer+m_Leftover, buf, sz);
m_Leftover += sz;
}
}
void Blocks(const uint8_t * buf, size_t sz)
{
const unsigned char hi = m_Final ^ 1;
while (sz >= POLY1305_BLOCK_BYTES) {
unsigned long u;
unsigned int i, j;
m_Msg.Put(buf, hi);
/* h += m */
m_H += m_Msg;
/* h *= r */
for (i = 0; i < 17; i++) {
u = 0;
for (j = 0; j <= i ; j++) {
u += (unsigned short)m_H.data[j] * m_R.data[i - j];
}
for (j = i + 1; j < 17; j++) {
unsigned long v = (unsigned short)m_H.data[j] * m_R.data[i + 17 - j];
v = ((v << 8) + (v << 6)); /* v *= (5 << 6); */
u += v;
}
m_HR[i] = u;
}
/* (partial) h %= p */
m_H %= m_HR;
buf += POLY1305_BLOCK_BYTES;
sz -= POLY1305_BLOCK_BYTES;
}
}
void Finish(uint64_t * out)
{
// process leftovers
if(m_Leftover)
{
size_t idx = m_Leftover;
m_Buffer[idx++] = 1;
for(; idx < POLY1305_BLOCK_BYTES; idx++)
m_Buffer[idx] = 0;
m_Final = 1;
Blocks(m_Buffer, POLY1305_BLOCK_BYTES);
}
// freeze H
~m_H;
// add pad
m_H += m_Pad;
// copy digest
memcpy(out, m_H, 16);
}
size_t m_Leftover;
poly1305::Buffer m_Buffer;
poly1305::Block m_H;
poly1305::Block m_R;
poly1305::Block m_Pad;
poly1305::Block m_Msg;
poly1305::LongBlock m_HR;
uint8_t m_Final;
};
void Poly1305HMAC(uint64_t * out, const uint64_t * key, const uint8_t * buf, std::size_t sz);
}
}
#endif
#endif

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2024, The PurpleI2P Project * Copyright (c) 2013-2025, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@@ -10,6 +10,7 @@
#include <unordered_map> #include <unordered_map>
#include <list> #include <list>
#include <thread> #include <thread>
#include <iomanip>
#include <boost/property_tree/ptree.hpp> #include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/ini_parser.hpp> #include <boost/property_tree/ini_parser.hpp>
#include "Base.h" #include "Base.h"
@@ -26,23 +27,21 @@ namespace data
static i2p::fs::HashedStorage g_ProfilesStorage("peerProfiles", "p", "profile-", "txt"); static i2p::fs::HashedStorage g_ProfilesStorage("peerProfiles", "p", "profile-", "txt");
static std::unordered_map<i2p::data::IdentHash, std::shared_ptr<RouterProfile> > g_Profiles; static std::unordered_map<i2p::data::IdentHash, std::shared_ptr<RouterProfile> > g_Profiles;
static std::mutex g_ProfilesMutex; static std::mutex g_ProfilesMutex;
static std::list<std::pair<i2p::data::IdentHash, std::function<void (std::shared_ptr<RouterProfile>)> > > g_PostponedUpdates;
static boost::posix_time::ptime GetTime () static std::mutex g_PostponedUpdatesMutex;
{
return boost::posix_time::second_clock::local_time();
}
RouterProfile::RouterProfile (): RouterProfile::RouterProfile ():
m_LastUpdateTime (GetTime ()), m_IsUpdated (false), m_IsUpdated (false), m_LastDeclineTime (0), m_LastUnreachableTime (0),
m_LastDeclineTime (0), m_LastUnreachableTime (0), m_LastUpdateTime (i2p::util::GetSecondsSinceEpoch ()), m_LastAccessTime (0),
m_NumTunnelsAgreed (0), m_NumTunnelsDeclined (0), m_NumTunnelsNonReplied (0), m_LastPersistTime (0), m_NumTunnelsAgreed (0), m_NumTunnelsDeclined (0),
m_NumTimesTaken (0), m_NumTimesRejected (0), m_HasConnected (false) m_NumTunnelsNonReplied (0),m_NumTimesTaken (0), m_NumTimesRejected (0),
m_HasConnected (false), m_IsDuplicated (false)
{ {
} }
void RouterProfile::UpdateTime () void RouterProfile::UpdateTime ()
{ {
m_LastUpdateTime = GetTime (); m_LastUpdateTime = i2p::util::GetSecondsSinceEpoch ();
m_IsUpdated = true; m_IsUpdated = true;
} }
@@ -57,9 +56,11 @@ namespace data
usage.put (PEER_PROFILE_USAGE_TAKEN, m_NumTimesTaken); usage.put (PEER_PROFILE_USAGE_TAKEN, m_NumTimesTaken);
usage.put (PEER_PROFILE_USAGE_REJECTED, m_NumTimesRejected); usage.put (PEER_PROFILE_USAGE_REJECTED, m_NumTimesRejected);
usage.put (PEER_PROFILE_USAGE_CONNECTED, m_HasConnected); usage.put (PEER_PROFILE_USAGE_CONNECTED, m_HasConnected);
if (m_IsDuplicated)
usage.put (PEER_PROFILE_USAGE_DUPLICATED, true);
// fill property tree // fill property tree
boost::property_tree::ptree pt; boost::property_tree::ptree pt;
pt.put (PEER_PROFILE_LAST_UPDATE_TIME, boost::posix_time::to_simple_string (m_LastUpdateTime)); pt.put (PEER_PROFILE_LAST_UPDATE_TIMESTAMP, m_LastUpdateTime);
if (m_LastUnreachableTime) if (m_LastUnreachableTime)
pt.put (PEER_PROFILE_LAST_UNREACHABLE_TIME, m_LastUnreachableTime); pt.put (PEER_PROFILE_LAST_UNREACHABLE_TIME, m_LastUnreachableTime);
pt.put_child (PEER_PROFILE_SECTION_PARTICIPATION, participation); pt.put_child (PEER_PROFILE_SECTION_PARTICIPATION, participation);
@@ -79,6 +80,7 @@ namespace data
void RouterProfile::Load (const IdentHash& identHash) void RouterProfile::Load (const IdentHash& identHash)
{ {
m_IsUpdated = false;
std::string ident = identHash.ToBase64 (); std::string ident = identHash.ToBase64 ();
std::string path = g_ProfilesStorage.Path(ident); std::string path = g_ProfilesStorage.Path(ident);
boost::property_tree::ptree pt; boost::property_tree::ptree pt;
@@ -101,10 +103,22 @@ namespace data
try try
{ {
auto t = pt.get (PEER_PROFILE_LAST_UPDATE_TIME, ""); auto ts = pt.get (PEER_PROFILE_LAST_UPDATE_TIMESTAMP, 0);
if (t.length () > 0) if (ts)
m_LastUpdateTime = boost::posix_time::time_from_string (t); m_LastUpdateTime = ts;
if ((GetTime () - m_LastUpdateTime).hours () < PEER_PROFILE_EXPIRATION_TIMEOUT) else
{
// try old lastupdatetime
auto ut = pt.get (PEER_PROFILE_LAST_UPDATE_TIME, "");
if (ut.length () > 0)
{
std::istringstream ss (ut); std::tm t;
ss >> std::get_time(&t, "%Y-%b-%d %H:%M:%S");
if (!ss.fail())
m_LastUpdateTime = mktime (&t); // t is local time
}
}
if (i2p::util::GetSecondsSinceEpoch () - m_LastUpdateTime < PEER_PROFILE_EXPIRATION_TIMEOUT)
{ {
m_LastUnreachableTime = pt.get (PEER_PROFILE_LAST_UNREACHABLE_TIME, 0); m_LastUnreachableTime = pt.get (PEER_PROFILE_LAST_UNREACHABLE_TIME, 0);
try try
@@ -126,6 +140,7 @@ namespace data
m_NumTimesTaken = usage.get (PEER_PROFILE_USAGE_TAKEN, 0); m_NumTimesTaken = usage.get (PEER_PROFILE_USAGE_TAKEN, 0);
m_NumTimesRejected = usage.get (PEER_PROFILE_USAGE_REJECTED, 0); m_NumTimesRejected = usage.get (PEER_PROFILE_USAGE_REJECTED, 0);
m_HasConnected = usage.get (PEER_PROFILE_USAGE_CONNECTED, false); m_HasConnected = usage.get (PEER_PROFILE_USAGE_CONNECTED, false);
m_IsDuplicated = usage.get (PEER_PROFILE_USAGE_DUPLICATED, false);
} }
catch (boost::property_tree::ptree_bad_path& ex) catch (boost::property_tree::ptree_bad_path& ex)
{ {
@@ -178,6 +193,11 @@ namespace data
UpdateTime (); UpdateTime ();
} }
void RouterProfile::Duplicated ()
{
m_IsDuplicated = true;
}
bool RouterProfile::IsLowPartcipationRate () const bool RouterProfile::IsLowPartcipationRate () const
{ {
return 4*m_NumTunnelsAgreed < m_NumTunnelsDeclined; // < 20% rate return 4*m_NumTunnelsAgreed < m_NumTunnelsDeclined; // < 20% rate
@@ -189,10 +209,9 @@ namespace data
return m_NumTunnelsNonReplied > 10*(total + 1); return m_NumTunnelsNonReplied > 10*(total + 1);
} }
bool RouterProfile::IsDeclinedRecently () bool RouterProfile::IsDeclinedRecently (uint64_t ts)
{ {
if (!m_LastDeclineTime) return false; if (!m_LastDeclineTime) return false;
auto ts = i2p::util::GetSecondsSinceEpoch ();
if (ts > m_LastDeclineTime + PEER_PROFILE_DECLINED_RECENTLY_INTERVAL || if (ts > m_LastDeclineTime + PEER_PROFILE_DECLINED_RECENTLY_INTERVAL ||
ts + PEER_PROFILE_DECLINED_RECENTLY_INTERVAL < m_LastDeclineTime) ts + PEER_PROFILE_DECLINED_RECENTLY_INTERVAL < m_LastDeclineTime)
m_LastDeclineTime = 0; m_LastDeclineTime = 0;
@@ -201,7 +220,10 @@ namespace data
bool RouterProfile::IsBad () bool RouterProfile::IsBad ()
{ {
if (IsDeclinedRecently () || IsUnreachable ()) return true; if (IsUnreachable () || m_IsDuplicated) return true;
auto ts = i2p::util::GetSecondsSinceEpoch ();
if (ts > PEER_PROFILE_MAX_DECLINED_INTERVAL + m_LastDeclineTime) return false;
if (IsDeclinedRecently (ts)) return true;
auto isBad = IsAlwaysDeclining () || IsLowPartcipationRate () /*|| IsLowReplyRate ()*/; auto isBad = IsAlwaysDeclining () || IsLowPartcipationRate () /*|| IsLowReplyRate ()*/;
if (isBad && m_NumTimesRejected > 10*(m_NumTimesTaken + 1)) if (isBad && m_NumTimesRejected > 10*(m_NumTimesTaken + 1))
{ {
@@ -236,30 +258,42 @@ namespace data
std::unique_lock<std::mutex> l(g_ProfilesMutex); std::unique_lock<std::mutex> l(g_ProfilesMutex);
auto it = g_Profiles.find (identHash); auto it = g_Profiles.find (identHash);
if (it != g_Profiles.end ()) if (it != g_Profiles.end ())
{
it->second->SetLastAccessTime (i2p::util::GetSecondsSinceEpoch ());
return it->second; return it->second;
}
} }
auto profile = netdb.NewRouterProfile (); auto profile = netdb.NewRouterProfile ();
profile->Load (identHash); // if possible profile->Load (identHash); // if possible
std::unique_lock<std::mutex> l(g_ProfilesMutex); std::lock_guard<std::mutex> l(g_ProfilesMutex);
g_Profiles.emplace (identHash, profile); g_Profiles.emplace (identHash, profile);
return profile; return profile;
} }
bool IsRouterBanned (const IdentHash& identHash) bool IsRouterBanned (const IdentHash& identHash)
{ {
std::unique_lock<std::mutex> l(g_ProfilesMutex); std::lock_guard<std::mutex> l(g_ProfilesMutex);
auto it = g_Profiles.find (identHash); auto it = g_Profiles.find (identHash);
if (it != g_Profiles.end ()) if (it != g_Profiles.end ())
return it->second->IsUnreachable (); return it->second->IsUnreachable ();
return false; return false;
} }
bool IsRouterDuplicated (const IdentHash& identHash)
{
std::lock_guard<std::mutex> l(g_ProfilesMutex);
auto it = g_Profiles.find (identHash);
if (it != g_Profiles.end ())
return it->second->IsDuplicated ();
return false;
}
void InitProfilesStorage () void InitProfilesStorage ()
{ {
g_ProfilesStorage.SetPlace(i2p::fs::GetDataDir()); g_ProfilesStorage.SetPlace(i2p::fs::GetDataDir());
g_ProfilesStorage.Init(i2p::data::GetBase64SubstitutionTable(), 64); g_ProfilesStorage.Init(i2p::data::GetBase64SubstitutionTable(), 64);
} }
static void SaveProfilesToDisk (std::list<std::pair<i2p::data::IdentHash, std::shared_ptr<RouterProfile> > >&& profiles) static void SaveProfilesToDisk (std::list<std::pair<i2p::data::IdentHash, std::shared_ptr<RouterProfile> > >&& profiles)
{ {
for (auto& it: profiles) for (auto& it: profiles)
@@ -268,18 +302,20 @@ namespace data
std::future<void> PersistProfiles () std::future<void> PersistProfiles ()
{ {
auto ts = GetTime (); auto ts = i2p::util::GetSecondsSinceEpoch ();
std::list<std::pair<i2p::data::IdentHash, std::shared_ptr<RouterProfile> > > tmp; std::list<std::pair<i2p::data::IdentHash, std::shared_ptr<RouterProfile> > > tmp;
{ {
std::unique_lock<std::mutex> l(g_ProfilesMutex); std::lock_guard<std::mutex> l(g_ProfilesMutex);
for (auto it = g_Profiles.begin (); it != g_Profiles.end ();) for (auto it = g_Profiles.begin (); it != g_Profiles.end ();)
{ {
if ((ts - it->second->GetLastUpdateTime ()).total_seconds () > PEER_PROFILE_PERSIST_INTERVAL) if (it->second->IsUpdated () && ts > it->second->GetLastPersistTime () + PEER_PROFILE_PERSIST_INTERVAL)
{ {
if (it->second->IsUpdated ()) tmp.push_back (std::make_pair (it->first, it->second));
tmp.push_back (std::make_pair (it->first, it->second)); it->second->SetLastPersistTime (ts);
it->second->SetUpdated (false);
}
if (!it->second->IsUpdated () && ts > std::max (it->second->GetLastUpdateTime (), it->second->GetLastAccessTime ()) + PEER_PROFILE_PERSIST_INTERVAL)
it = g_Profiles.erase (it); it = g_Profiles.erase (it);
}
else else
it++; it++;
} }
@@ -293,12 +329,12 @@ namespace data
{ {
std::unordered_map<i2p::data::IdentHash, std::shared_ptr<RouterProfile> > tmp; std::unordered_map<i2p::data::IdentHash, std::shared_ptr<RouterProfile> > tmp;
{ {
std::unique_lock<std::mutex> l(g_ProfilesMutex); std::lock_guard<std::mutex> l(g_ProfilesMutex);
std::swap (tmp, g_Profiles); std::swap (tmp, g_Profiles);
} }
auto ts = GetTime (); auto ts = i2p::util::GetSecondsSinceEpoch ();
for (auto& it: tmp) for (auto& it: tmp)
if (it.second->IsUseful() && (it.second->IsUpdated () || (ts - it.second->GetLastUpdateTime ()).total_seconds () < PEER_PROFILE_EXPIRATION_TIMEOUT*3600)) if (it.second->IsUseful() && (it.second->IsUpdated () || ts - it.second->GetLastUpdateTime () < PEER_PROFILE_EXPIRATION_TIMEOUT))
it.second->Save (it.first); it.second->Save (it.first);
} }
@@ -316,7 +352,7 @@ namespace data
LogPrint(eLogWarning, "Profiling: Can't stat(): ", path); LogPrint(eLogWarning, "Profiling: Can't stat(): ", path);
continue; continue;
} }
if (now - st.st_mtime >= PEER_PROFILE_EXPIRATION_TIMEOUT*3600) if (now - st.st_mtime >= PEER_PROFILE_EXPIRATION_TIMEOUT)
{ {
LogPrint(eLogDebug, "Profiling: Removing expired peer profile: ", path); LogPrint(eLogDebug, "Profiling: Removing expired peer profile: ", path);
i2p::fs::Remove(path); i2p::fs::Remove(path);
@@ -327,11 +363,11 @@ namespace data
std::future<void> DeleteObsoleteProfiles () std::future<void> DeleteObsoleteProfiles ()
{ {
{ {
auto ts = GetTime (); auto ts = i2p::util::GetSecondsSinceEpoch ();
std::unique_lock<std::mutex> l(g_ProfilesMutex); std::lock_guard<std::mutex> l(g_ProfilesMutex);
for (auto it = g_Profiles.begin (); it != g_Profiles.end ();) for (auto it = g_Profiles.begin (); it != g_Profiles.end ();)
{ {
if ((ts - it->second->GetLastUpdateTime ()).total_seconds () >= PEER_PROFILE_EXPIRATION_TIMEOUT*3600) if (ts - it->second->GetLastUpdateTime () >= PEER_PROFILE_EXPIRATION_TIMEOUT)
it = g_Profiles.erase (it); it = g_Profiles.erase (it);
else else
it++; it++;
@@ -340,5 +376,47 @@ namespace data
return std::async (std::launch::async, DeleteFilesFromDisk); return std::async (std::launch::async, DeleteFilesFromDisk);
} }
bool UpdateRouterProfile (const IdentHash& identHash, std::function<void (std::shared_ptr<RouterProfile>)> update)
{
if (!update) return true;
std::shared_ptr<RouterProfile> profile;
{
std::lock_guard<std::mutex> l(g_ProfilesMutex);
auto it = g_Profiles.find (identHash);
if (it != g_Profiles.end ())
profile = it->second;
}
if (profile)
{
update (profile);
return true;
}
// postpone
std::lock_guard<std::mutex> l(g_PostponedUpdatesMutex);
g_PostponedUpdates.emplace_back (identHash, update);
return false;
}
static void ApplyPostponedUpdates (std::list<std::pair<i2p::data::IdentHash, std::function<void (std::shared_ptr<RouterProfile>)> > >&& updates)
{
for (const auto& [ident, update] : updates)
{
auto profile = GetRouterProfile (ident);
update (profile);
}
}
std::future<void> FlushPostponedRouterProfileUpdates ()
{
if (g_PostponedUpdates.empty ()) return std::future<void>();
std::list<std::pair<i2p::data::IdentHash, std::function<void (std::shared_ptr<RouterProfile>)> > > updates;
{
std::lock_guard<std::mutex> l(g_PostponedUpdatesMutex);
g_PostponedUpdates.swap (updates);
}
return std::async (std::launch::async, ApplyPostponedUpdates, std::move (updates));
}
} }
} }

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2024, The PurpleI2P Project * Copyright (c) 2013-2025, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@@ -11,7 +11,8 @@
#include <memory> #include <memory>
#include <future> #include <future>
#include <boost/date_time/posix_time/posix_time.hpp> #include <functional>
#include <boost/asio.hpp>
#include "Identity.h" #include "Identity.h"
namespace i2p namespace i2p
@@ -22,7 +23,8 @@ namespace data
const char PEER_PROFILE_SECTION_PARTICIPATION[] = "participation"; const char PEER_PROFILE_SECTION_PARTICIPATION[] = "participation";
const char PEER_PROFILE_SECTION_USAGE[] = "usage"; const char PEER_PROFILE_SECTION_USAGE[] = "usage";
// params // params
const char PEER_PROFILE_LAST_UPDATE_TIME[] = "lastupdatetime"; const char PEER_PROFILE_LAST_UPDATE_TIME[] = "lastupdatetime"; // deprecated
const char PEER_PROFILE_LAST_UPDATE_TIMESTAMP[] = "lastupdatetimestamp";
const char PEER_PROFILE_LAST_UNREACHABLE_TIME[] = "lastunreachabletime"; const char PEER_PROFILE_LAST_UNREACHABLE_TIME[] = "lastunreachabletime";
const char PEER_PROFILE_PARTICIPATION_AGREED[] = "agreed"; const char PEER_PROFILE_PARTICIPATION_AGREED[] = "agreed";
const char PEER_PROFILE_PARTICIPATION_DECLINED[] = "declined"; const char PEER_PROFILE_PARTICIPATION_DECLINED[] = "declined";
@@ -30,17 +32,22 @@ namespace data
const char PEER_PROFILE_USAGE_TAKEN[] = "taken"; const char PEER_PROFILE_USAGE_TAKEN[] = "taken";
const char PEER_PROFILE_USAGE_REJECTED[] = "rejected"; const char PEER_PROFILE_USAGE_REJECTED[] = "rejected";
const char PEER_PROFILE_USAGE_CONNECTED[] = "connected"; const char PEER_PROFILE_USAGE_CONNECTED[] = "connected";
const char PEER_PROFILE_USAGE_DUPLICATED[] = "duplicated";
const int PEER_PROFILE_EXPIRATION_TIMEOUT = 36; // in hours (1.5 days) const int PEER_PROFILE_EXPIRATION_TIMEOUT = 36*60*60; // in seconds (1.5 days)
const int PEER_PROFILE_AUTOCLEAN_TIMEOUT = 1500; // in seconds (25 minutes) const int PEER_PROFILE_AUTOCLEAN_TIMEOUT = 1500; // in seconds (25 minutes)
const int PEER_PROFILE_AUTOCLEAN_VARIANCE = 900; // in seconds (15 minutes) const int PEER_PROFILE_AUTOCLEAN_VARIANCE = 900; // in seconds (15 minutes)
const int PEER_PROFILE_OBSOLETE_PROFILES_CLEAN_TIMEOUT = 5400; // in seconds (1.5 hours) const int PEER_PROFILE_OBSOLETE_PROFILES_CLEAN_TIMEOUT = 5400; // in seconds (1.5 hours)
const int PEER_PROFILE_OBSOLETE_PROFILES_CLEAN_VARIANCE = 2400; // in seconds (40 minutes) const int PEER_PROFILE_OBSOLETE_PROFILES_CLEAN_VARIANCE = 2400; // in seconds (40 minutes)
const int PEER_PROFILE_DECLINED_RECENTLY_INTERVAL = 150; // in seconds (2.5 minutes) const int PEER_PROFILE_DECLINED_RECENTLY_INTERVAL = 330; // in seconds (5.5 minutes)
const int PEER_PROFILE_MAX_DECLINED_INTERVAL = 4400; // in second (1.5 hours)
const int PEER_PROFILE_PERSIST_INTERVAL = 3300; // in seconds (55 minutes) const int PEER_PROFILE_PERSIST_INTERVAL = 3300; // in seconds (55 minutes)
const int PEER_PROFILE_UNREACHABLE_INTERVAL = 480; // in seconds (8 minutes) const int PEER_PROFILE_UNREACHABLE_INTERVAL = 480; // in seconds (8 minutes)
const int PEER_PROFILE_USEFUL_THRESHOLD = 3; const int PEER_PROFILE_USEFUL_THRESHOLD = 3;
const int PEER_PROFILE_ALWAYS_DECLINING_NUM = 5; // num declines in row to consider always declined
const int PEER_PROFILE_APPLY_POSTPONED_TIMEOUT = 2100; // in milliseconds
const int PEER_PROFILE_APPLY_POSTPONED_TIMEOUT_VARIANCE = 500; // in milliseconds
class RouterProfile class RouterProfile
{ {
public: public:
@@ -59,11 +66,23 @@ namespace data
void Unreachable (bool unreachable); void Unreachable (bool unreachable);
void Connected (); void Connected ();
void Duplicated ();
boost::posix_time::ptime GetLastUpdateTime () const { return m_LastUpdateTime; }; uint64_t GetLastUpdateTime () const { return m_LastUpdateTime; };
bool IsUpdated () const { return m_IsUpdated; }; bool IsUpdated () const { return m_IsUpdated; };
void SetUpdated (bool updated) { m_IsUpdated = updated; }
uint64_t GetLastAccessTime () const { return m_LastAccessTime; };
void SetLastAccessTime (uint64_t ts) { m_LastAccessTime = ts; };
uint64_t GetLastPersistTime () const { return m_LastPersistTime; };
void SetLastPersistTime (uint64_t ts) { m_LastPersistTime = ts; };
bool IsUseful() const; bool IsUseful() const;
bool IsDuplicated () const { return m_IsDuplicated; };
const boost::asio::ip::udp::endpoint& GetLastEndpoint () const { return m_LastEndpoint; }
void SetLastEndpoint (const boost::asio::ip::udp::endpoint& ep) { m_LastEndpoint = ep; }
bool HasLastEndpoint (bool v4) const { return !m_LastEndpoint.address ().is_unspecified () && m_LastEndpoint.port () &&
((v4 && m_LastEndpoint.address ().is_v4 ()) || (!v4 && m_LastEndpoint.address ().is_v6 ())); }
private: private:
@@ -72,13 +91,13 @@ namespace data
bool IsAlwaysDeclining () const { return !m_NumTunnelsAgreed && m_NumTunnelsDeclined >= 5; }; bool IsAlwaysDeclining () const { return !m_NumTunnelsAgreed && m_NumTunnelsDeclined >= 5; };
bool IsLowPartcipationRate () const; bool IsLowPartcipationRate () const;
bool IsLowReplyRate () const; bool IsLowReplyRate () const;
bool IsDeclinedRecently (); bool IsDeclinedRecently (uint64_t ts);
private: private:
boost::posix_time::ptime m_LastUpdateTime; // TODO: use std::chrono
bool m_IsUpdated; bool m_IsUpdated;
uint64_t m_LastDeclineTime, m_LastUnreachableTime; // in seconds uint64_t m_LastDeclineTime, m_LastUnreachableTime, m_LastUpdateTime,
m_LastAccessTime, m_LastPersistTime; // in seconds
// participation // participation
uint32_t m_NumTunnelsAgreed; uint32_t m_NumTunnelsAgreed;
uint32_t m_NumTunnelsDeclined; uint32_t m_NumTunnelsDeclined;
@@ -87,14 +106,20 @@ namespace data
uint32_t m_NumTimesTaken; uint32_t m_NumTimesTaken;
uint32_t m_NumTimesRejected; uint32_t m_NumTimesRejected;
bool m_HasConnected; // successful trusted(incoming or NTCP2) connection bool m_HasConnected; // successful trusted(incoming or NTCP2) connection
bool m_IsDuplicated;
// connectivity
boost::asio::ip::udp::endpoint m_LastEndpoint; // SSU2 for non-published addresses
}; };
std::shared_ptr<RouterProfile> GetRouterProfile (const IdentHash& identHash); std::shared_ptr<RouterProfile> GetRouterProfile (const IdentHash& identHash);
bool IsRouterBanned (const IdentHash& identHash); // check only existing profiles bool IsRouterBanned (const IdentHash& identHash); // check only existing profiles
bool IsRouterDuplicated (const IdentHash& identHash); // check only existing profiles
void InitProfilesStorage (); void InitProfilesStorage ();
std::future<void> DeleteObsoleteProfiles (); std::future<void> DeleteObsoleteProfiles ();
void SaveProfiles (); void SaveProfiles ();
std::future<void> PersistProfiles (); std::future<void> PersistProfiles ();
bool UpdateRouterProfile (const IdentHash& identHash, std::function<void (std::shared_ptr<RouterProfile>)> update); // return true if updated immediately, and false if postponed
std::future<void> FlushPostponedRouterProfileUpdates ();
} }
} }

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2020, The PurpleI2P Project * Copyright (c) 2013-2024, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@@ -9,8 +9,7 @@
#ifndef QUEUE_H__ #ifndef QUEUE_H__
#define QUEUE_H__ #define QUEUE_H__
#include <queue> #include <list>
#include <vector>
#include <mutex> #include <mutex>
#include <thread> #include <thread>
#include <condition_variable> #include <condition_variable>
@@ -29,22 +28,20 @@ namespace util
void Put (Element e) void Put (Element e)
{ {
std::unique_lock<std::mutex> l(m_QueueMutex); std::unique_lock<std::mutex> l(m_QueueMutex);
m_Queue.push (std::move(e)); m_Queue.push_back (std::move(e));
m_NonEmpty.notify_one (); m_NonEmpty.notify_one ();
} }
template<template<typename, typename...>class Container, typename... R> void Put (std::list<Element>& list)
void Put (const Container<Element, R...>& vec)
{ {
if (!vec.empty ()) if (!list.empty ())
{ {
std::unique_lock<std::mutex> l(m_QueueMutex); std::unique_lock<std::mutex> l(m_QueueMutex);
for (const auto& it: vec) m_Queue.splice (m_Queue.end (), list);
m_Queue.push (std::move(it));
m_NonEmpty.notify_one (); m_NonEmpty.notify_one ();
} }
} }
Element GetNext () Element GetNext ()
{ {
std::unique_lock<std::mutex> l(m_QueueMutex); std::unique_lock<std::mutex> l(m_QueueMutex);
@@ -87,7 +84,7 @@ namespace util
return m_Queue.empty (); return m_Queue.empty ();
} }
int GetSize () int GetSize () const
{ {
std::unique_lock<std::mutex> l(m_QueueMutex); std::unique_lock<std::mutex> l(m_QueueMutex);
return m_Queue.size (); return m_Queue.size ();
@@ -107,15 +104,28 @@ namespace util
return GetNonThreadSafe (true); return GetNonThreadSafe (true);
} }
private: void GetWholeQueue (std::list<Element>& queue)
{
if (!queue.empty ())
{
std::list<Element> newQueue;
queue.swap (newQueue);
}
{
std::unique_lock<std::mutex> l(m_QueueMutex);
m_Queue.swap (queue);
}
}
private:
Element GetNonThreadSafe (bool peek = false) Element GetNonThreadSafe (bool peek = false)
{ {
if (!m_Queue.empty ()) if (!m_Queue.empty ())
{ {
auto el = m_Queue.front (); auto el = m_Queue.front ();
if (!peek) if (!peek)
m_Queue.pop (); m_Queue.pop_front ();
return el; return el;
} }
return nullptr; return nullptr;
@@ -123,8 +133,8 @@ namespace util
private: private:
std::queue<Element> m_Queue; std::list<Element> m_Queue;
std::mutex m_QueueMutex; mutable std::mutex m_QueueMutex;
std::condition_variable m_NonEmpty; std::condition_variable m_NonEmpty;
}; };
} }

View File

@@ -552,7 +552,7 @@ namespace data
if (!url.port) if (!url.port)
url.port = 443; url.port = 443;
boost::asio::io_service service; boost::asio::io_context service;
boost::system::error_code ecode; boost::system::error_code ecode;
boost::asio::ssl::context ctx(boost::asio::ssl::context::sslv23); boost::asio::ssl::context ctx(boost::asio::ssl::context::sslv23);
@@ -562,11 +562,10 @@ namespace data
if(proxyUrl.schema.size()) if(proxyUrl.schema.size())
{ {
// proxy connection // proxy connection
auto it = boost::asio::ip::tcp::resolver(service).resolve ( auto it = boost::asio::ip::tcp::resolver(service).resolve (proxyUrl.host, std::to_string(proxyUrl.port), ecode);
boost::asio::ip::tcp::resolver::query (proxyUrl.host, std::to_string(proxyUrl.port)), ecode);
if(!ecode) if(!ecode)
{ {
s.lowest_layer().connect(*it, ecode); s.lowest_layer().connect(*it.begin (), ecode);
if(!ecode) if(!ecode)
{ {
auto & sock = s.next_layer(); auto & sock = s.next_layer();
@@ -599,7 +598,7 @@ namespace data
LogPrint(eLogError, "Reseed: HTTP CONNECT read error: ", ecode.message()); LogPrint(eLogError, "Reseed: HTTP CONNECT read error: ", ecode.message());
return ""; return "";
} }
if(proxyRes.parse(boost::asio::buffer_cast<const char *>(readbuf.data()), readbuf.size()) <= 0) if(proxyRes.parse(std::string {boost::asio::buffers_begin(readbuf.data ()), boost::asio::buffers_begin(readbuf.data ()) + readbuf.size ()}) <= 0)
{ {
sock.close(); sock.close();
LogPrint(eLogError, "Reseed: HTTP CONNECT malformed reply"); LogPrint(eLogError, "Reseed: HTTP CONNECT malformed reply");
@@ -638,15 +637,13 @@ namespace data
else else
{ {
// direct connection // direct connection
auto it = boost::asio::ip::tcp::resolver(service).resolve ( auto endpoints = boost::asio::ip::tcp::resolver(service).resolve (url.host, std::to_string(url.port), ecode);
boost::asio::ip::tcp::resolver::query (url.host, std::to_string(url.port)), ecode);
if (!ecode) if (!ecode)
{ {
bool connected = false; bool connected = false;
boost::asio::ip::tcp::resolver::iterator end; for (const auto& it: endpoints)
while (it != end)
{ {
boost::asio::ip::tcp::endpoint ep = *it; boost::asio::ip::tcp::endpoint ep = it;
bool supported = false; bool supported = false;
if (!ep.address ().is_unspecified ()) if (!ep.address ().is_unspecified ())
{ {
@@ -666,7 +663,6 @@ namespace data
break; break;
} }
} }
it++;
} }
if (!connected) if (!connected)
{ {
@@ -746,19 +742,16 @@ namespace data
if (!url.port) url.port = 80; if (!url.port) url.port = 80;
boost::system::error_code ecode; boost::system::error_code ecode;
boost::asio::io_service service; boost::asio::io_context service;
boost::asio::ip::tcp::socket s(service, boost::asio::ip::tcp::v6()); boost::asio::ip::tcp::socket s(service, boost::asio::ip::tcp::v6());
auto it = boost::asio::ip::tcp::resolver(service).resolve ( auto endpoints = boost::asio::ip::tcp::resolver(service).resolve (url.host, std::to_string(url.port), ecode);
boost::asio::ip::tcp::resolver::query (url.host, std::to_string(url.port)), ecode);
if (!ecode) if (!ecode)
{ {
bool connected = false; bool connected = false;
boost::asio::ip::tcp::resolver::iterator end; for (const auto& it: endpoints)
while (it != end)
{ {
boost::asio::ip::tcp::endpoint ep = *it; boost::asio::ip::tcp::endpoint ep = it;
if ( if (
i2p::util::net::IsYggdrasilAddress (ep.address ()) && i2p::util::net::IsYggdrasilAddress (ep.address ()) &&
i2p::context.SupportsMesh () i2p::context.SupportsMesh ()
@@ -772,7 +765,6 @@ namespace data
break; break;
} }
} }
it++;
} }
if (!connected) if (!connected)
{ {

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2024, The PurpleI2P Project * Copyright (c) 2013-2025, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@@ -33,13 +33,14 @@ namespace i2p
m_ShareRatio (100), m_Status (eRouterStatusUnknown), m_StatusV6 (eRouterStatusUnknown), m_ShareRatio (100), m_Status (eRouterStatusUnknown), m_StatusV6 (eRouterStatusUnknown),
m_Error (eRouterErrorNone), m_ErrorV6 (eRouterErrorNone), m_Error (eRouterErrorNone), m_ErrorV6 (eRouterErrorNone),
m_Testing (false), m_TestingV6 (false), m_NetID (I2PD_NET_ID), m_Testing (false), m_TestingV6 (false), m_NetID (I2PD_NET_ID),
m_PublishReplyToken (0), m_IsHiddenMode (false) m_PublishReplyToken (0), m_IsHiddenMode (false),
m_Rng(i2p::util::GetMonotonicMicroseconds () % 1000000LL), m_IsSaving (false)
{ {
} }
void RouterContext::Init () void RouterContext::Init ()
{ {
srand (i2p::util::GetMillisecondsSinceEpoch () % 1000); srand (m_Rng () % 1000);
m_StartupTime = i2p::util::GetMonotonicSeconds (); m_StartupTime = i2p::util::GetMonotonicSeconds ();
if (!Load ()) if (!Load ())
@@ -57,11 +58,8 @@ namespace i2p
{ {
m_Service.reset (new RouterService); m_Service.reset (new RouterService);
m_Service->Start (); m_Service->Start ();
if (!m_IsHiddenMode) m_PublishTimer.reset (new boost::asio::deadline_timer (m_Service->GetService ()));
{ ScheduleInitialPublish ();
m_PublishTimer.reset (new boost::asio::deadline_timer (m_Service->GetService ()));
ScheduleInitialPublish ();
}
m_CongestionUpdateTimer.reset (new boost::asio::deadline_timer (m_Service->GetService ())); m_CongestionUpdateTimer.reset (new boost::asio::deadline_timer (m_Service->GetService ()));
ScheduleCongestionUpdate (); ScheduleCongestionUpdate ();
m_CleanupTimer.reset (new boost::asio::deadline_timer (m_Service->GetService ())); m_CleanupTimer.reset (new boost::asio::deadline_timer (m_Service->GetService ()));
@@ -78,9 +76,16 @@ namespace i2p
if (m_CongestionUpdateTimer) if (m_CongestionUpdateTimer)
m_CongestionUpdateTimer->cancel (); m_CongestionUpdateTimer->cancel ();
m_Service->Stop (); m_Service->Stop ();
} CleanUp (); // GarlicDestination
}
} }
std::shared_ptr<i2p::data::RouterInfo::Buffer> RouterContext::CopyRouterInfoBuffer () const
{
std::lock_guard<std::mutex> l(m_RouterInfoMutex);
return m_RouterInfo.CopyBuffer ();
}
void RouterContext::CreateNewRouter () void RouterContext::CreateNewRouter ()
{ {
m_Keys = i2p::data::PrivateKeys::CreateRandomKeys (i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519, m_Keys = i2p::data::PrivateKeys::CreateRandomKeys (i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519,
@@ -136,7 +141,7 @@ namespace i2p
{ {
boost::asio::ip::address addr; boost::asio::ip::address addr;
if (!host.empty ()) if (!host.empty ())
addr = boost::asio::ip::address::from_string (host); addr = boost::asio::ip::make_address (host);
if (!addr.is_v4()) if (!addr.is_v4())
addr = boost::asio::ip::address_v4 (); addr = boost::asio::ip::address_v4 ();
routerInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv, addr, ntcp2Port); routerInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv, addr, ntcp2Port);
@@ -157,7 +162,7 @@ namespace i2p
{ {
boost::asio::ip::address addr; boost::asio::ip::address addr;
if (!host.empty ()) if (!host.empty ())
addr = boost::asio::ip::address::from_string (host); addr = boost::asio::ip::make_address (host);
if (!addr.is_v4()) if (!addr.is_v4())
addr = boost::asio::ip::address_v4 (); addr = boost::asio::ip::address_v4 ();
routerInfo.AddSSU2Address (m_SSU2Keys->staticPublicKey, m_SSU2Keys->intro, addr, ssu2Port); routerInfo.AddSSU2Address (m_SSU2Keys->staticPublicKey, m_SSU2Keys->intro, addr, ssu2Port);
@@ -188,7 +193,7 @@ namespace i2p
ntcp2Host = host; ntcp2Host = host;
boost::asio::ip::address addr; boost::asio::ip::address addr;
if (!ntcp2Host.empty ()) if (!ntcp2Host.empty ())
addr = boost::asio::ip::address::from_string (ntcp2Host); addr = boost::asio::ip::make_address (ntcp2Host);
if (!addr.is_v6()) if (!addr.is_v6())
addr = boost::asio::ip::address_v6 (); addr = boost::asio::ip::address_v6 ();
routerInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv, addr, ntcp2Port); routerInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv, addr, ntcp2Port);
@@ -207,7 +212,7 @@ namespace i2p
{ {
boost::asio::ip::address addr; boost::asio::ip::address addr;
if (!host.empty ()) if (!host.empty ())
addr = boost::asio::ip::address::from_string (host); addr = boost::asio::ip::make_address (host);
if (!addr.is_v6()) if (!addr.is_v6())
addr = boost::asio::ip::address_v6 (); addr = boost::asio::ip::address_v6 ();
routerInfo.AddSSU2Address (m_SSU2Keys->staticPublicKey, m_SSU2Keys->intro, addr, ssu2Port); routerInfo.AddSSU2Address (m_SSU2Keys->staticPublicKey, m_SSU2Keys->intro, addr, ssu2Port);
@@ -249,8 +254,36 @@ namespace i2p
void RouterContext::UpdateRouterInfo () void RouterContext::UpdateRouterInfo ()
{ {
m_RouterInfo.CreateBuffer (m_Keys); std::shared_ptr<i2p::data::RouterInfo::Buffer> buffer;
m_RouterInfo.SaveToFile (i2p::fs::DataDirPath (ROUTER_INFO)); {
std::lock_guard<std::mutex> l(m_RouterInfoMutex);
m_RouterInfo.CreateBuffer (m_Keys);
buffer = m_RouterInfo.CopyBuffer ();
}
{
// update save buffer to latest
std::lock_guard<std::mutex> l(m_SaveBufferMutex);
m_SaveBuffer = buffer;
}
bool isSaving = false;
if (m_IsSaving.compare_exchange_strong (isSaving, true)) // try to save only if not being saved
{
auto savingRouterInfo = std::async (std::launch::async, [this]()
{
std::shared_ptr<i2p::data::RouterInfo::Buffer> buffer;
while (m_SaveBuffer)
{
{
std::lock_guard<std::mutex> l(m_SaveBufferMutex);
buffer = m_SaveBuffer;
m_SaveBuffer = nullptr;
}
if (buffer)
i2p::data::RouterInfo::SaveToFile (i2p::fs::DataDirPath (ROUTER_INFO), buffer);
}
m_IsSaving = false;
});
}
m_LastUpdateTime = i2p::util::GetSecondsSinceEpoch (); m_LastUpdateTime = i2p::util::GetSecondsSinceEpoch ();
} }
@@ -316,6 +349,12 @@ namespace i2p
case eRouterStatusFirewalled: case eRouterStatusFirewalled:
SetUnreachable (true, false); // ipv4 SetUnreachable (true, false); // ipv4
break; break;
case eRouterStatusMesh:
m_RouterInfo.UpdateCaps (m_RouterInfo.GetCaps () | i2p::data::RouterInfo::eReachable);
break;
case eRouterStatusProxy:
m_RouterInfo.UpdateCaps (m_RouterInfo.GetCaps () | i2p::data::RouterInfo::eUnreachable);
break;
default: default:
; ;
} }
@@ -546,6 +585,12 @@ namespace i2p
UpdateRouterInfo (); UpdateRouterInfo ();
} }
void RouterContext::UpdateSSU2Introducer (const i2p::data::IdentHash& h, bool v4, uint32_t iTag, uint32_t iExp)
{
if (m_RouterInfo.UpdateSSU2Introducer (h, v4, iTag, iExp))
UpdateRouterInfo ();
}
void RouterContext::ClearSSU2Introducers (bool v4) void RouterContext::ClearSSU2Introducers (bool v4)
{ {
auto addr = m_RouterInfo.GetSSU2Address (v4); auto addr = m_RouterInfo.GetSSU2Address (v4);
@@ -603,8 +648,8 @@ namespace i2p
case i2p::data::CAPS_FLAG_LOW_BANDWIDTH1 : limit = 12; type = low; break; case i2p::data::CAPS_FLAG_LOW_BANDWIDTH1 : limit = 12; type = low; break;
case i2p::data::CAPS_FLAG_LOW_BANDWIDTH2 : limit = i2p::data::LOW_BANDWIDTH_LIMIT; type = low; break; // 48 case i2p::data::CAPS_FLAG_LOW_BANDWIDTH2 : limit = i2p::data::LOW_BANDWIDTH_LIMIT; type = low; break; // 48
case i2p::data::CAPS_FLAG_LOW_BANDWIDTH3 : limit = 64; type = low; break; case i2p::data::CAPS_FLAG_LOW_BANDWIDTH3 : limit = 64; type = low; break;
case i2p::data::CAPS_FLAG_HIGH_BANDWIDTH1 : limit = 128; type = high; break; case i2p::data::CAPS_FLAG_LOW_BANDWIDTH4 : limit = 128; type = low; break;
case i2p::data::CAPS_FLAG_HIGH_BANDWIDTH2 : limit = i2p::data::HIGH_BANDWIDTH_LIMIT; type = high; break; // 256 case i2p::data::CAPS_FLAG_HIGH_BANDWIDTH : limit = i2p::data::HIGH_BANDWIDTH_LIMIT; type = high; break; // 256
case i2p::data::CAPS_FLAG_EXTRA_BANDWIDTH1 : limit = i2p::data::EXTRA_BANDWIDTH_LIMIT; type = extra; break; // 2048 case i2p::data::CAPS_FLAG_EXTRA_BANDWIDTH1 : limit = i2p::data::EXTRA_BANDWIDTH_LIMIT; type = extra; break; // 2048
case i2p::data::CAPS_FLAG_EXTRA_BANDWIDTH2 : limit = 1000000; type = unlim; break; // 1Gbyte/s case i2p::data::CAPS_FLAG_EXTRA_BANDWIDTH2 : limit = 1000000; type = unlim; break; // 1Gbyte/s
default: default:
@@ -619,9 +664,7 @@ namespace i2p
case low : /* not set */; break; case low : /* not set */; break;
case extra : caps |= i2p::data::RouterInfo::eExtraBandwidth; break; // 'P' case extra : caps |= i2p::data::RouterInfo::eExtraBandwidth; break; // 'P'
case unlim : caps |= i2p::data::RouterInfo::eExtraBandwidth; case unlim : caps |= i2p::data::RouterInfo::eExtraBandwidth;
#if (__cplusplus >= 201703L) // C++ 17 or higher
[[fallthrough]]; [[fallthrough]];
#endif
// no break here, extra + high means 'X' // no break here, extra + high means 'X'
case high : caps |= i2p::data::RouterInfo::eHighBandwidth; break; case high : caps |= i2p::data::RouterInfo::eHighBandwidth; break;
} }
@@ -785,7 +828,7 @@ namespace i2p
i2p::config::GetOption("host", ntcp2Host); i2p::config::GetOption("host", ntcp2Host);
if (!ntcp2Host.empty () && ntcp2Port) if (!ntcp2Host.empty () && ntcp2Port)
{ {
auto addr = boost::asio::ip::address::from_string (ntcp2Host); auto addr = boost::asio::ip::make_address (ntcp2Host);
if (addr.is_v6 ()) if (addr.is_v6 ())
{ {
m_RouterInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv, addr, ntcp2Port); m_RouterInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv, addr, ntcp2Port);
@@ -814,7 +857,7 @@ namespace i2p
std::string host; i2p::config::GetOption("host", host); std::string host; i2p::config::GetOption("host", host);
if (!host.empty ()) if (!host.empty ())
{ {
auto addr = boost::asio::ip::address::from_string (host); auto addr = boost::asio::ip::make_address (host);
if (addr.is_v6 ()) if (addr.is_v6 ())
{ {
m_RouterInfo.AddSSU2Address (m_SSU2Keys->staticPublicKey, m_SSU2Keys->intro, addr, ssu2Port); m_RouterInfo.AddSSU2Address (m_SSU2Keys->staticPublicKey, m_SSU2Keys->intro, addr, ssu2Port);
@@ -883,7 +926,7 @@ namespace i2p
std::string host; i2p::config::GetOption("host", host); std::string host; i2p::config::GetOption("host", host);
if (!host.empty ()) if (!host.empty ())
{ {
auto addr = boost::asio::ip::address::from_string (host); auto addr = boost::asio::ip::make_address (host);
if (addr.is_v4 ()) if (addr.is_v4 ())
{ {
m_RouterInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv, addr, ntcp2Port); m_RouterInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv, addr, ntcp2Port);
@@ -913,7 +956,7 @@ namespace i2p
std::string host; i2p::config::GetOption("host", host); std::string host; i2p::config::GetOption("host", host);
if (!host.empty ()) if (!host.empty ())
{ {
auto addr = boost::asio::ip::address::from_string (host); auto addr = boost::asio::ip::make_address (host);
if (addr.is_v4 ()) if (addr.is_v4 ())
{ {
m_RouterInfo.AddSSU2Address (m_SSU2Keys->staticPublicKey, m_SSU2Keys->intro, addr, ssu2Port); m_RouterInfo.AddSSU2Address (m_SSU2Keys->staticPublicKey, m_SSU2Keys->intro, addr, ssu2Port);
@@ -1168,7 +1211,7 @@ namespace i2p
void RouterContext::ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg) void RouterContext::ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg)
{ {
if (m_Service) if (m_Service)
m_Service->GetService ().post (std::bind (&RouterContext::PostGarlicMessage, this, msg)); boost::asio::post (m_Service->GetService (), std::bind (&RouterContext::PostGarlicMessage, this, msg));
else else
LogPrint (eLogError, "Router: service is NULL"); LogPrint (eLogError, "Router: service is NULL");
} }
@@ -1196,7 +1239,7 @@ namespace i2p
void RouterContext::ProcessDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg) void RouterContext::ProcessDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg)
{ {
if (m_Service) if (m_Service)
m_Service->GetService ().post (std::bind (&RouterContext::PostDeliveryStatusMessage, this, msg)); boost::asio::post (m_Service->GetService (), std::bind (&RouterContext::PostDeliveryStatusMessage, this, msg));
else else
LogPrint (eLogError, "Router: service is NULL"); LogPrint (eLogError, "Router: service is NULL");
} }
@@ -1225,7 +1268,7 @@ namespace i2p
} data; } data;
memcpy (data.k, key, 32); memcpy (data.k, key, 32);
data.t = tag; data.t = tag;
m_Service->GetService ().post ([this,data](void) boost::asio::post (m_Service->GetService (), [this,data](void)
{ {
AddECIESx25519Key (data.k, data.t); AddECIESx25519Key (data.k, data.t);
}); });
@@ -1324,9 +1367,15 @@ namespace i2p
if (ecode != boost::asio::error::operation_aborted) if (ecode != boost::asio::error::operation_aborted)
{ {
if (m_RouterInfo.IsReachableBy (i2p::data::RouterInfo::eAllTransports)) if (m_RouterInfo.IsReachableBy (i2p::data::RouterInfo::eAllTransports))
{
UpdateCongestion ();
HandlePublishTimer (ecode); HandlePublishTimer (ecode);
}
else else
{
UpdateTimestamp (i2p::util::GetSecondsSinceEpoch ());
ScheduleInitialPublish (); ScheduleInitialPublish ();
}
} }
} }
@@ -1336,7 +1385,7 @@ namespace i2p
{ {
m_PublishTimer->cancel (); m_PublishTimer->cancel ();
m_PublishTimer->expires_from_now (boost::posix_time::seconds(ROUTER_INFO_PUBLISH_INTERVAL + m_PublishTimer->expires_from_now (boost::posix_time::seconds(ROUTER_INFO_PUBLISH_INTERVAL +
rand () % ROUTER_INFO_PUBLISH_INTERVAL_VARIANCE)); m_Rng () % ROUTER_INFO_PUBLISH_INTERVAL_VARIANCE));
m_PublishTimer->async_wait (std::bind (&RouterContext::HandlePublishTimer, m_PublishTimer->async_wait (std::bind (&RouterContext::HandlePublishTimer,
this, std::placeholders::_1)); this, std::placeholders::_1));
} }
@@ -1348,16 +1397,21 @@ namespace i2p
{ {
if (ecode != boost::asio::error::operation_aborted) if (ecode != boost::asio::error::operation_aborted)
{ {
m_PublishExcluded.clear ();
m_PublishReplyToken = 0;
if (IsFloodfill ())
{
UpdateStats (); // for floodfill
m_PublishExcluded.insert (i2p::context.GetIdentHash ()); // don't publish to ourselves
}
UpdateTimestamp (i2p::util::GetSecondsSinceEpoch ()); UpdateTimestamp (i2p::util::GetSecondsSinceEpoch ());
Publish (); if (!m_IsHiddenMode)
SchedulePublishResend (); {
m_PublishExcluded.clear ();
m_PublishReplyToken = 0;
if (IsFloodfill ())
{
UpdateStats (); // for floodfill
m_PublishExcluded.insert (i2p::context.GetIdentHash ()); // don't publish to ourselves
}
Publish ();
SchedulePublishResend ();
}
else
SchedulePublish ();
} }
} }
@@ -1380,10 +1434,11 @@ namespace i2p
auto onDrop = [this]() auto onDrop = [this]()
{ {
if (m_Service) if (m_Service)
m_Service->GetService ().post ([this]() { HandlePublishResendTimer (boost::system::error_code ()); }); boost::asio::post (m_Service->GetService (), [this]() { HandlePublishResendTimer (boost::system::error_code ()); });
}; };
if (floodfill->IsReachableFrom (i2p::context.GetRouterInfo ()) || // are we able to connect? if (i2p::transport::transports.IsConnected (floodfill->GetIdentHash ()) || // already connected
i2p::transport::transports.IsConnected (floodfill->GetIdentHash ())) // already connected ? (floodfill->IsReachableFrom (i2p::context.GetRouterInfo ()) && // are we able to connect
!i2p::transport::transports.RoutesRestricted ())) // and routes not restricted
{ {
// send directly // send directly
auto msg = CreateDatabaseStoreMsg (i2p::context.GetSharedRouterInfo (), replyToken); auto msg = CreateDatabaseStoreMsg (i2p::context.GetSharedRouterInfo (), replyToken);
@@ -1405,7 +1460,7 @@ namespace i2p
i2p::garlic::WrapECIESX25519MessageForRouter (msg, floodfill->GetIdentity ()->GetEncryptionPublicKey ())); i2p::garlic::WrapECIESX25519MessageForRouter (msg, floodfill->GetIdentity ()->GetEncryptionPublicKey ()));
} }
else else
LogPrint (eLogInfo, "Router: Can't publish our RouterInfo. No tunnles. Try again in ", ROUTER_INFO_CONFIRMATION_TIMEOUT, " seconds"); LogPrint (eLogInfo, "Router: Can't publish our RouterInfo. No tunnels. Try again in ", ROUTER_INFO_CONFIRMATION_TIMEOUT, " milliseconds");
} }
m_PublishExcluded.insert (floodfill->GetIdentHash ()); m_PublishExcluded.insert (floodfill->GetIdentHash ());
m_PublishReplyToken = replyToken; m_PublishReplyToken = replyToken;
@@ -1419,7 +1474,7 @@ namespace i2p
if (m_PublishTimer) if (m_PublishTimer)
{ {
m_PublishTimer->cancel (); m_PublishTimer->cancel ();
m_PublishTimer->expires_from_now (boost::posix_time::seconds(ROUTER_INFO_CONFIRMATION_TIMEOUT)); m_PublishTimer->expires_from_now (boost::posix_time::milliseconds(ROUTER_INFO_CONFIRMATION_TIMEOUT));
m_PublishTimer->async_wait (std::bind (&RouterContext::HandlePublishResendTimer, m_PublishTimer->async_wait (std::bind (&RouterContext::HandlePublishResendTimer,
this, std::placeholders::_1)); this, std::placeholders::_1));
} }
@@ -1442,7 +1497,8 @@ namespace i2p
if (m_CongestionUpdateTimer) if (m_CongestionUpdateTimer)
{ {
m_CongestionUpdateTimer->cancel (); m_CongestionUpdateTimer->cancel ();
m_CongestionUpdateTimer->expires_from_now (boost::posix_time::seconds(ROUTER_INFO_CONGESTION_UPDATE_INTERVAL)); m_CongestionUpdateTimer->expires_from_now (boost::posix_time::seconds(
ROUTER_INFO_CONGESTION_UPDATE_INTERVAL + m_Rng () % ROUTER_INFO_CONGESTION_UPDATE_INTERVAL_VARIANCE));
m_CongestionUpdateTimer->async_wait (std::bind (&RouterContext::HandleCongestionUpdateTimer, m_CongestionUpdateTimer->async_wait (std::bind (&RouterContext::HandleCongestionUpdateTimer,
this, std::placeholders::_1)); this, std::placeholders::_1));
} }
@@ -1454,29 +1510,34 @@ namespace i2p
{ {
if (ecode != boost::asio::error::operation_aborted) if (ecode != boost::asio::error::operation_aborted)
{ {
auto c = i2p::data::RouterInfo::eLowCongestion; UpdateCongestion ();
if (!AcceptsTunnels () || !m_ShareRatio)
c = i2p::data::RouterInfo::eRejectAll;
else
{
int congestionLevel = GetCongestionLevel (true);
if (congestionLevel > CONGESTION_LEVEL_HIGH)
c = i2p::data::RouterInfo::eHighCongestion;
else if (congestionLevel > CONGESTION_LEVEL_MEDIUM)
c = i2p::data::RouterInfo::eMediumCongestion;
}
if (m_RouterInfo.UpdateCongestion (c))
UpdateRouterInfo ();
ScheduleCongestionUpdate (); ScheduleCongestionUpdate ();
} }
} }
void RouterContext::UpdateCongestion ()
{
auto c = i2p::data::RouterInfo::eLowCongestion;
if (!AcceptsTunnels () || !m_ShareRatio)
c = i2p::data::RouterInfo::eRejectAll;
else
{
int congestionLevel = GetCongestionLevel (true);
if (congestionLevel > CONGESTION_LEVEL_HIGH)
c = i2p::data::RouterInfo::eHighCongestion;
else if (congestionLevel > CONGESTION_LEVEL_MEDIUM)
c = i2p::data::RouterInfo::eMediumCongestion;
}
if (m_RouterInfo.UpdateCongestion (c))
UpdateRouterInfo ();
}
void RouterContext::ScheduleCleanupTimer () void RouterContext::ScheduleCleanupTimer ()
{ {
if (m_CleanupTimer) if (m_CleanupTimer)
{ {
m_CleanupTimer->cancel (); m_CleanupTimer->cancel ();
m_CleanupTimer->expires_from_now (boost::posix_time::minutes(ROUTER_INFO_CLEANUP_INTERVAL)); m_CleanupTimer->expires_from_now (boost::posix_time::seconds(ROUTER_INFO_CLEANUP_INTERVAL));
m_CleanupTimer->async_wait (std::bind (&RouterContext::HandleCleanupTimer, m_CleanupTimer->async_wait (std::bind (&RouterContext::HandleCleanupTimer,
this, std::placeholders::_1)); this, std::placeholders::_1));
} }

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2024, The PurpleI2P Project * Copyright (c) 2013-2025, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@@ -12,7 +12,8 @@
#include <inttypes.h> #include <inttypes.h>
#include <string> #include <string>
#include <memory> #include <memory>
#include <set> #include <random>
#include <unordered_set>
#include <boost/asio.hpp> #include <boost/asio.hpp>
#include "Identity.h" #include "Identity.h"
#include "RouterInfo.h" #include "RouterInfo.h"
@@ -34,10 +35,11 @@ namespace garlic
const int ROUTER_INFO_PUBLISH_INTERVAL = 39*60; // in seconds const int ROUTER_INFO_PUBLISH_INTERVAL = 39*60; // in seconds
const int ROUTER_INFO_INITIAL_PUBLISH_INTERVAL = 10; // in seconds const int ROUTER_INFO_INITIAL_PUBLISH_INTERVAL = 10; // in seconds
const int ROUTER_INFO_PUBLISH_INTERVAL_VARIANCE = 105;// in seconds const int ROUTER_INFO_PUBLISH_INTERVAL_VARIANCE = 105;// in seconds
const int ROUTER_INFO_CONFIRMATION_TIMEOUT = 5; // in seconds const int ROUTER_INFO_CONFIRMATION_TIMEOUT = 1600; // in milliseconds
const int ROUTER_INFO_MAX_PUBLISH_EXCLUDED_FLOODFILLS = 15; const int ROUTER_INFO_MAX_PUBLISH_EXCLUDED_FLOODFILLS = 15;
const int ROUTER_INFO_CONGESTION_UPDATE_INTERVAL = 12*60; // in seconds const int ROUTER_INFO_CONGESTION_UPDATE_INTERVAL = 11*60; // in seconds
const int ROUTER_INFO_CLEANUP_INTERVAL = 5; // in minutes const int ROUTER_INFO_CONGESTION_UPDATE_INTERVAL_VARIANCE = 130; // in seconds
const int ROUTER_INFO_CLEANUP_INTERVAL = 102; // in seconds
enum RouterStatus enum RouterStatus
{ {
@@ -90,7 +92,7 @@ namespace garlic
public: public:
RouterService (): RunnableServiceWithWork ("Router") {}; RouterService (): RunnableServiceWithWork ("Router") {};
boost::asio::io_service& GetService () { return GetIOService (); }; auto& GetService () { return GetIOService (); };
void Start () { StartIOService (); }; void Start () { StartIOService (); };
void Stop () { StopIOService (); }; void Stop () { StopIOService (); };
}; };
@@ -114,7 +116,8 @@ namespace garlic
return std::shared_ptr<i2p::garlic::GarlicDestination> (this, return std::shared_ptr<i2p::garlic::GarlicDestination> (this,
[](i2p::garlic::GarlicDestination *) {}); [](i2p::garlic::GarlicDestination *) {});
} }
std::shared_ptr<i2p::data::RouterInfo::Buffer> CopyRouterInfoBuffer () const;
const uint8_t * GetNTCP2StaticPublicKey () const { return m_NTCP2Keys ? m_NTCP2Keys->staticPublicKey : nullptr; }; const uint8_t * GetNTCP2StaticPublicKey () const { return m_NTCP2Keys ? m_NTCP2Keys->staticPublicKey : nullptr; };
const uint8_t * GetNTCP2StaticPrivateKey () const { return m_NTCP2Keys ? m_NTCP2Keys->staticPrivateKey : nullptr; }; const uint8_t * GetNTCP2StaticPrivateKey () const { return m_NTCP2Keys ? m_NTCP2Keys->staticPrivateKey : nullptr; };
const uint8_t * GetNTCP2IV () const { return m_NTCP2Keys ? m_NTCP2Keys->iv : nullptr; }; const uint8_t * GetNTCP2IV () const { return m_NTCP2Keys ? m_NTCP2Keys->iv : nullptr; };
@@ -145,7 +148,6 @@ namespace garlic
void SetNetID (int netID) { m_NetID = netID; }; void SetNetID (int netID) { m_NetID = netID; };
bool DecryptTunnelBuildRecord (const uint8_t * encrypted, uint8_t * data); bool DecryptTunnelBuildRecord (const uint8_t * encrypted, uint8_t * data);
bool DecryptTunnelShortRequestRecord (const uint8_t * encrypted, uint8_t * data); bool DecryptTunnelShortRequestRecord (const uint8_t * encrypted, uint8_t * data);
void SubmitECIESx25519Key (const uint8_t * key, uint64_t tag);
void UpdatePort (int port); // called from Daemon void UpdatePort (int port); // called from Daemon
void UpdateAddress (const boost::asio::ip::address& host); // called from SSU2 or Daemon void UpdateAddress (const boost::asio::ip::address& host); // called from SSU2 or Daemon
@@ -153,6 +155,7 @@ namespace garlic
void PublishSSU2Address (int port, bool publish, bool v4, bool v6); void PublishSSU2Address (int port, bool publish, bool v4, bool v6);
bool AddSSU2Introducer (const i2p::data::RouterInfo::Introducer& introducer, bool v4); bool AddSSU2Introducer (const i2p::data::RouterInfo::Introducer& introducer, bool v4);
void RemoveSSU2Introducer (const i2p::data::IdentHash& h, bool v4); void RemoveSSU2Introducer (const i2p::data::IdentHash& h, bool v4);
void UpdateSSU2Introducer (const i2p::data::IdentHash& h, bool v4, uint32_t iTag, uint32_t iExp);
void ClearSSU2Introducers (bool v4); void ClearSSU2Introducers (bool v4);
bool IsUnreachable () const; bool IsUnreachable () const;
void SetUnreachable (bool v4, bool v6); void SetUnreachable (bool v4, bool v6);
@@ -176,6 +179,7 @@ namespace garlic
void SetMTU (int mtu, bool v4); void SetMTU (int mtu, bool v4);
void SetHidden(bool hide) { m_IsHiddenMode = hide; }; void SetHidden(bool hide) { m_IsHiddenMode = hide; };
bool IsHidden() const { return m_IsHiddenMode; }; bool IsHidden() const { return m_IsHiddenMode; };
bool IsLimitedConnectivity () const { return m_Status == eRouterStatusProxy; }; // TODO: implement other cases
i2p::crypto::NoiseSymmetricState& GetCurrentNoiseState () { return m_CurrentNoiseState; }; i2p::crypto::NoiseSymmetricState& GetCurrentNoiseState () { return m_CurrentNoiseState; };
void UpdateNTCP2V6Address (const boost::asio::ip::address& host); // called from Daemon. TODO: remove void UpdateNTCP2V6Address (const boost::asio::ip::address& host); // called from Daemon. TODO: remove
@@ -183,24 +187,24 @@ namespace garlic
void UpdateTimestamp (uint64_t ts); // in seconds, called from NetDb before publishing void UpdateTimestamp (uint64_t ts); // in seconds, called from NetDb before publishing
// implements LocalDestination // implements LocalDestination
std::shared_ptr<const i2p::data::IdentityEx> GetIdentity () const { return m_Keys.GetPublic (); }; std::shared_ptr<const i2p::data::IdentityEx> GetIdentity () const override{ return m_Keys.GetPublic (); };
bool Decrypt (const uint8_t * encrypted, uint8_t * data, i2p::data::CryptoKeyType preferredCrypto) const; bool Decrypt (const uint8_t * encrypted, uint8_t * data, i2p::data::CryptoKeyType preferredCrypto) const override;
void Sign (const uint8_t * buf, int len, uint8_t * signature) const { m_Keys.Sign (buf, len, signature); }; void SetLeaseSetUpdated (bool post) override {};
void SetLeaseSetUpdated () {};
// implements GarlicDestination // implements GarlicDestination
std::shared_ptr<const i2p::data::LocalLeaseSet> GetLeaseSet () { return nullptr; }; std::shared_ptr<const i2p::data::LocalLeaseSet> GetLeaseSet () override { return nullptr; };
std::shared_ptr<i2p::tunnel::TunnelPool> GetTunnelPool () const; std::shared_ptr<i2p::tunnel::TunnelPool> GetTunnelPool () const override;
// override GarlicDestination // override GarlicDestination
void ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg); void ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg) override;
void ProcessDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg); void ProcessDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg) override;
void SubmitECIESx25519Key (const uint8_t * key, uint64_t tag) override;
protected: protected:
// implements GarlicDestination // implements GarlicDestination
void HandleI2NPMessage (const uint8_t * buf, size_t len); void HandleI2NPMessage (const uint8_t * buf, size_t len) override;
bool HandleCloveI2NPMessage (I2NPMessageType typeID, const uint8_t * payload, size_t len, uint32_t msgID); bool HandleCloveI2NPMessage (I2NPMessageType typeID, const uint8_t * payload, size_t len, uint32_t msgID) override;
private: private:
@@ -213,6 +217,7 @@ namespace garlic
void UpdateSSU2Keys (); void UpdateSSU2Keys ();
bool Load (); bool Load ();
void SaveKeys (); void SaveKeys ();
void Sign (const uint8_t * buf, int len, uint8_t * signature) const { m_Keys.Sign (buf, len, signature); };
uint16_t SelectRandomPort () const; uint16_t SelectRandomPort () const;
void PublishNTCP2Address (std::shared_ptr<i2p::data::RouterInfo::Address> address, int port, bool publish) const; void PublishNTCP2Address (std::shared_ptr<i2p::data::RouterInfo::Address> address, int port, bool publish) const;
@@ -229,6 +234,7 @@ namespace garlic
void HandlePublishResendTimer (const boost::system::error_code& ecode); void HandlePublishResendTimer (const boost::system::error_code& ecode);
void ScheduleCongestionUpdate (); void ScheduleCongestionUpdate ();
void HandleCongestionUpdateTimer (const boost::system::error_code& ecode); void HandleCongestionUpdateTimer (const boost::system::error_code& ecode);
void UpdateCongestion ();
void ScheduleCleanupTimer (); void ScheduleCleanupTimer ();
void HandleCleanupTimer (const boost::system::error_code& ecode); void HandleCleanupTimer (const boost::system::error_code& ecode);
@@ -255,9 +261,14 @@ namespace garlic
// publish // publish
std::unique_ptr<RouterService> m_Service; std::unique_ptr<RouterService> m_Service;
std::unique_ptr<boost::asio::deadline_timer> m_PublishTimer, m_CongestionUpdateTimer, m_CleanupTimer; std::unique_ptr<boost::asio::deadline_timer> m_PublishTimer, m_CongestionUpdateTimer, m_CleanupTimer;
std::set<i2p::data::IdentHash> m_PublishExcluded; std::unordered_set<i2p::data::IdentHash> m_PublishExcluded;
uint32_t m_PublishReplyToken; uint32_t m_PublishReplyToken;
bool m_IsHiddenMode; // not publish bool m_IsHiddenMode; // not publish
mutable std::mutex m_RouterInfoMutex;
std::mt19937 m_Rng;
std::shared_ptr<i2p::data::RouterInfo::Buffer> m_SaveBuffer;
std::mutex m_SaveBufferMutex; // TODO: make m_SaveBuffer atomic
std::atomic<bool> m_IsSaving;
}; };
extern RouterContext context; extern RouterContext context;

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2024, The PurpleI2P Project * Copyright (c) 2013-2025, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@@ -10,9 +10,10 @@
#include <string.h> #include <string.h>
#include "I2PEndian.h" #include "I2PEndian.h"
#include <fstream> #include <fstream>
#include <memory>
#include <boost/lexical_cast.hpp> #include <boost/lexical_cast.hpp>
#include <boost/make_shared.hpp> #include <boost/algorithm/string.hpp> // for boost::to_lower
#if (BOOST_VERSION >= 105300) #ifndef __cpp_lib_atomic_shared_ptr
#include <boost/atomic.hpp> #include <boost/atomic.hpp>
#endif #endif
#include "version.h" #include "version.h"
@@ -39,27 +40,28 @@ namespace data
RouterInfo::RouterInfo (): m_Buffer (nullptr) RouterInfo::RouterInfo (): m_Buffer (nullptr)
{ {
m_Addresses = boost::make_shared<Addresses>(); // create empty list m_Addresses = AddressesPtr(new Addresses ()); // create empty list
} }
RouterInfo::RouterInfo (const std::string& fullPath): RouterInfo::RouterInfo (const std::string& fullPath):
m_FamilyID (0), m_IsUpdated (false), m_IsUnreachable (false), m_IsFloodfill (false), m_FamilyID (0), m_IsUpdated (false), m_IsUnreachable (false), m_IsFloodfill (false),
m_SupportedTransports (0),m_ReachableTransports (0), m_PublishedTransports (0), m_IsBufferScheduledToDelete (false), m_SupportedTransports (0),
m_Caps (0), m_Version (0), m_Congestion (eLowCongestion) m_ReachableTransports (0), m_PublishedTransports (0), m_Caps (0), m_Version (0),
m_Congestion (eLowCongestion)
{ {
m_Addresses = boost::make_shared<Addresses>(); // create empty list m_Addresses = AddressesPtr(new Addresses ()); // create empty list
m_Buffer = NewBuffer (); // always RouterInfo's m_Buffer = RouterInfo::NewBuffer (); // always RouterInfo's
ReadFromFile (fullPath); ReadFromFile (fullPath);
} }
RouterInfo::RouterInfo (std::shared_ptr<Buffer>&& buf, size_t len): RouterInfo::RouterInfo (std::shared_ptr<Buffer>&& buf, size_t len):
m_FamilyID (0), m_IsUpdated (true), m_IsUnreachable (false), m_IsFloodfill (false), m_FamilyID (0), m_IsUpdated (true), m_IsUnreachable (false), m_IsFloodfill (false),
m_SupportedTransports (0), m_ReachableTransports (0), m_PublishedTransports (0), m_IsBufferScheduledToDelete (false), m_SupportedTransports (0), m_ReachableTransports (0), m_PublishedTransports (0),
m_Caps (0), m_Version (0), m_Congestion (eLowCongestion) m_Caps (0), m_Version (0), m_Congestion (eLowCongestion)
{ {
if (len <= MAX_RI_BUFFER_SIZE) if (len <= MAX_RI_BUFFER_SIZE)
{ {
m_Addresses = boost::make_shared<Addresses>(); // create empty list m_Addresses = AddressesPtr(new Addresses ()); // create empty list
m_Buffer = buf; m_Buffer = buf;
if (m_Buffer) m_Buffer->SetBufferLen (len); if (m_Buffer) m_Buffer->SetBufferLen (len);
ReadFromBuffer (true); ReadFromBuffer (true);
@@ -73,7 +75,7 @@ namespace data
} }
RouterInfo::RouterInfo (const uint8_t * buf, size_t len): RouterInfo::RouterInfo (const uint8_t * buf, size_t len):
RouterInfo (std::make_shared<Buffer> (buf, len), len) RouterInfo (netdb.NewRouterInfoBuffer (buf, len), len)
{ {
} }
@@ -255,7 +257,7 @@ namespace data
if (!strcmp (key, "host")) if (!strcmp (key, "host"))
{ {
boost::system::error_code ecode; boost::system::error_code ecode;
address->host = boost::asio::ip::address::from_string (value, ecode); address->host = boost::asio::ip::make_address (value, ecode);
if (!ecode && !address->host.is_unspecified ()) if (!ecode && !address->host.is_unspecified ())
{ {
if (!i2p::transport::transports.IsInReservedRange (address->host) || if (!i2p::transport::transports.IsInReservedRange (address->host) ||
@@ -438,10 +440,10 @@ namespace data
} }
m_ReachableTransports |= m_PublishedTransports; m_ReachableTransports |= m_PublishedTransports;
// update addresses // update addresses
#if (BOOST_VERSION >= 105300) #ifdef __cpp_lib_atomic_shared_ptr
m_Addresses = addresses;
#else
boost::atomic_store (&m_Addresses, addresses); boost::atomic_store (&m_Addresses, addresses);
#else
m_Addresses = addresses; // race condition
#endif #endif
// read peers // read peers
uint8_t numPeers; uint8_t numPeers;
@@ -484,6 +486,15 @@ namespace data
} }
ch++; ch++;
} }
if (m_Version < NETDB_MIN_PEER_TEST_VERSION && (m_SupportedTransports & (eSSU2V4 | eSSU2V6)))
{
auto addresses = GetAddresses ();
if (addresses)
{
if ((*addresses)[eSSU2V4Idx]) (*addresses)[eSSU2V4Idx]->caps &= ~eSSUTesting;
if ((*addresses)[eSSU2V6Idx]) (*addresses)[eSSU2V6Idx]->caps &= ~eSSUTesting;
}
}
} }
// check netId // check netId
else if (!strcmp (key, ROUTER_INFO_PROPERTY_NETID)) else if (!strcmp (key, ROUTER_INFO_PROPERTY_NETID))
@@ -534,13 +545,20 @@ namespace data
case CAPS_FLAG_FLOODFILL: case CAPS_FLAG_FLOODFILL:
m_Caps |= Caps::eFloodfill; m_Caps |= Caps::eFloodfill;
break; break;
case CAPS_FLAG_HIGH_BANDWIDTH1: case CAPS_FLAG_LOW_BANDWIDTH1:
case CAPS_FLAG_HIGH_BANDWIDTH2: case CAPS_FLAG_LOW_BANDWIDTH2:
case CAPS_FLAG_LOW_BANDWIDTH3:
case CAPS_FLAG_LOW_BANDWIDTH4:
m_BandwidthCap = *cap;
break;
case CAPS_FLAG_HIGH_BANDWIDTH:
m_Caps |= Caps::eHighBandwidth; m_Caps |= Caps::eHighBandwidth;
m_BandwidthCap = *cap;
break; break;
case CAPS_FLAG_EXTRA_BANDWIDTH1: case CAPS_FLAG_EXTRA_BANDWIDTH1:
case CAPS_FLAG_EXTRA_BANDWIDTH2: case CAPS_FLAG_EXTRA_BANDWIDTH2:
m_Caps |= Caps::eExtraBandwidth | Caps::eHighBandwidth; m_Caps |= Caps::eExtraBandwidth | Caps::eHighBandwidth;
m_BandwidthCap = *cap;
break; break;
case CAPS_FLAG_HIDDEN: case CAPS_FLAG_HIDDEN:
m_Caps |= Caps::eHidden; m_Caps |= Caps::eHidden;
@@ -684,12 +702,12 @@ namespace data
if (addr->IsV4 ()) if (addr->IsV4 ())
{ {
m_SupportedTransports |= eNTCP2V4; m_SupportedTransports |= eNTCP2V4;
(*m_Addresses)[eNTCP2V4Idx] = addr; (*GetAddresses ())[eNTCP2V4Idx] = addr;
} }
if (addr->IsV6 ()) if (addr->IsV6 ())
{ {
m_SupportedTransports |= eNTCP2V6; m_SupportedTransports |= eNTCP2V6;
(*m_Addresses)[eNTCP2V6Idx] = addr; (*GetAddresses ())[eNTCP2V6Idx] = addr;
} }
} }
@@ -710,11 +728,12 @@ namespace data
if (host.is_v4 ()) addr->caps |= eV4; if (host.is_v4 ()) addr->caps |= eV4;
if (host.is_v6 ()) addr->caps |= eV6; if (host.is_v6 ()) addr->caps |= eV6;
} }
auto addresses = GetAddresses ();
if (addr->IsV4 ()) if (addr->IsV4 ())
{ {
m_SupportedTransports |= eNTCP2V4; m_SupportedTransports |= eNTCP2V4;
m_ReachableTransports |= eNTCP2V4; m_ReachableTransports |= eNTCP2V4;
(*m_Addresses)[eNTCP2V4Idx] = addr; (*addresses)[eNTCP2V4Idx] = addr;
} }
if (addr->IsV6 ()) if (addr->IsV6 ())
{ {
@@ -722,30 +741,31 @@ namespace data
{ {
m_SupportedTransports |= eNTCP2V6Mesh; m_SupportedTransports |= eNTCP2V6Mesh;
m_ReachableTransports |= eNTCP2V6Mesh; m_ReachableTransports |= eNTCP2V6Mesh;
(*m_Addresses)[eNTCP2V6MeshIdx] = addr; (*addresses)[eNTCP2V6MeshIdx] = addr;
} }
else else
{ {
m_SupportedTransports |= eNTCP2V6; m_SupportedTransports |= eNTCP2V6;
m_ReachableTransports |= eNTCP2V6; m_ReachableTransports |= eNTCP2V6;
(*m_Addresses)[eNTCP2V6Idx] = addr; (*addresses)[eNTCP2V6Idx] = addr;
} }
} }
} }
void RouterInfo::RemoveNTCP2Address (bool v4) void RouterInfo::RemoveNTCP2Address (bool v4)
{ {
auto addresses = GetAddresses ();
if (v4) if (v4)
{ {
if ((*m_Addresses)[eNTCP2V6Idx]) if ((*addresses)[eNTCP2V6Idx])
(*m_Addresses)[eNTCP2V6Idx]->caps &= ~AddressCaps::eV4; (*addresses)[eNTCP2V6Idx]->caps &= ~AddressCaps::eV4;
(*m_Addresses)[eNTCP2V4Idx].reset (); (*addresses)[eNTCP2V4Idx].reset ();
} }
else else
{ {
if ((*m_Addresses)[eNTCP2V4Idx]) if ((*addresses)[eNTCP2V4Idx])
(*m_Addresses)[eNTCP2V4Idx]->caps &= ~AddressCaps::eV6; (*addresses)[eNTCP2V4Idx]->caps &= ~AddressCaps::eV6;
(*m_Addresses)[eNTCP2V6Idx].reset (); (*addresses)[eNTCP2V6Idx].reset ();
} }
UpdateSupportedTransports (); UpdateSupportedTransports ();
} }
@@ -761,15 +781,16 @@ namespace data
addr->ssu->mtu = 0; addr->ssu->mtu = 0;
memcpy (addr->s, staticKey, 32); memcpy (addr->s, staticKey, 32);
memcpy (addr->i, introKey, 32); memcpy (addr->i, introKey, 32);
auto addresses = GetAddresses ();
if (addr->IsV4 ()) if (addr->IsV4 ())
{ {
m_SupportedTransports |= eSSU2V4; m_SupportedTransports |= eSSU2V4;
(*m_Addresses)[eSSU2V4Idx] = addr; (*addresses)[eSSU2V4Idx] = addr;
} }
if (addr->IsV6 ()) if (addr->IsV6 ())
{ {
m_SupportedTransports |= eSSU2V6; m_SupportedTransports |= eSSU2V6;
(*m_Addresses)[eSSU2V6Idx] = addr; (*addresses)[eSSU2V6Idx] = addr;
} }
} }
@@ -794,33 +815,35 @@ namespace data
if (host.is_v4 ()) addr->caps |= eV4; if (host.is_v4 ()) addr->caps |= eV4;
if (host.is_v6 ()) addr->caps |= eV6; if (host.is_v6 ()) addr->caps |= eV6;
} }
auto addresses = GetAddresses ();
if (addr->IsV4 ()) if (addr->IsV4 ())
{ {
m_SupportedTransports |= eSSU2V4; m_SupportedTransports |= eSSU2V4;
m_ReachableTransports |= eSSU2V4; m_ReachableTransports |= eSSU2V4;
(*m_Addresses)[eSSU2V4Idx] = addr; (*addresses)[eSSU2V4Idx] = addr;
} }
if (addr->IsV6 ()) if (addr->IsV6 ())
{ {
m_SupportedTransports |= eSSU2V6; m_SupportedTransports |= eSSU2V6;
m_ReachableTransports |= eSSU2V6; m_ReachableTransports |= eSSU2V6;
(*m_Addresses)[eSSU2V6Idx] = addr; (*addresses)[eSSU2V6Idx] = addr;
} }
} }
void RouterInfo::RemoveSSU2Address (bool v4) void RouterInfo::RemoveSSU2Address (bool v4)
{ {
auto addresses = GetAddresses ();
if (v4) if (v4)
{ {
if ((*m_Addresses)[eSSU2V6Idx]) if ((*addresses)[eSSU2V6Idx])
(*m_Addresses)[eSSU2V6Idx]->caps &= ~AddressCaps::eV4; (*addresses)[eSSU2V6Idx]->caps &= ~AddressCaps::eV4;
(*m_Addresses)[eSSU2V4Idx].reset (); (*addresses)[eSSU2V4Idx].reset ();
} }
else else
{ {
if ((*m_Addresses)[eSSU2V4Idx]) if ((*addresses)[eSSU2V4Idx])
(*m_Addresses)[eSSU2V4Idx]->caps &= ~AddressCaps::eV6; (*addresses)[eSSU2V4Idx]->caps &= ~AddressCaps::eV6;
(*m_Addresses)[eSSU2V6Idx].reset (); (*addresses)[eSSU2V6Idx].reset ();
} }
UpdateSupportedTransports (); UpdateSupportedTransports ();
} }
@@ -861,17 +884,18 @@ namespace data
{ {
if (IsV6 ()) if (IsV6 ())
{ {
if ((*m_Addresses)[eNTCP2V6Idx]) auto addresses = GetAddresses ();
if ((*addresses)[eNTCP2V6Idx])
{ {
if ((*m_Addresses)[eNTCP2V6Idx]->IsV4 () && (*m_Addresses)[eNTCP2V4Idx]) if ((*addresses)[eNTCP2V6Idx]->IsV4 () && (*addresses)[eNTCP2V4Idx])
(*m_Addresses)[eNTCP2V4Idx]->caps &= ~AddressCaps::eV6; (*addresses)[eNTCP2V4Idx]->caps &= ~AddressCaps::eV6;
(*m_Addresses)[eNTCP2V6Idx].reset (); (*addresses)[eNTCP2V6Idx].reset ();
} }
if ((*m_Addresses)[eSSU2V6Idx]) if ((*addresses)[eSSU2V6Idx])
{ {
if ((*m_Addresses)[eSSU2V6Idx]->IsV4 () && (*m_Addresses)[eSSU2V4Idx]) if ((*addresses)[eSSU2V6Idx]->IsV4 () && (*addresses)[eSSU2V4Idx])
(*m_Addresses)[eSSU2V4Idx]->caps &= ~AddressCaps::eV6; (*addresses)[eSSU2V4Idx]->caps &= ~AddressCaps::eV6;
(*m_Addresses)[eSSU2V6Idx].reset (); (*addresses)[eSSU2V6Idx].reset ();
} }
UpdateSupportedTransports (); UpdateSupportedTransports ();
} }
@@ -881,17 +905,18 @@ namespace data
{ {
if (IsV4 ()) if (IsV4 ())
{ {
if ((*m_Addresses)[eNTCP2V4Idx]) auto addresses = GetAddresses ();
if ((*addresses)[eNTCP2V4Idx])
{ {
if ((*m_Addresses)[eNTCP2V4Idx]->IsV6 () && (*m_Addresses)[eNTCP2V6Idx]) if ((*addresses)[eNTCP2V4Idx]->IsV6 () && (*addresses)[eNTCP2V6Idx])
(*m_Addresses)[eNTCP2V6Idx]->caps &= ~AddressCaps::eV4; (*addresses)[eNTCP2V6Idx]->caps &= ~AddressCaps::eV4;
(*m_Addresses)[eNTCP2V4Idx].reset (); (*addresses)[eNTCP2V4Idx].reset ();
} }
if ((*m_Addresses)[eSSU2V4Idx]) if ((*addresses)[eSSU2V4Idx])
{ {
if ((*m_Addresses)[eSSU2V4Idx]->IsV6 () && (*m_Addresses)[eSSU2V6Idx]) if ((*addresses)[eSSU2V4Idx]->IsV6 () && (*addresses)[eSSU2V6Idx])
(*m_Addresses)[eSSU2V6Idx]->caps &= ~AddressCaps::eV4; (*addresses)[eSSU2V6Idx]->caps &= ~AddressCaps::eV4;
(*m_Addresses)[eSSU2V4Idx].reset (); (*addresses)[eSSU2V4Idx].reset ();
} }
UpdateSupportedTransports (); UpdateSupportedTransports ();
} }
@@ -912,7 +937,7 @@ namespace data
{ {
m_SupportedTransports &= ~eNTCP2V6Mesh; m_SupportedTransports &= ~eNTCP2V6Mesh;
m_ReachableTransports &= ~eNTCP2V6Mesh; m_ReachableTransports &= ~eNTCP2V6Mesh;
(*m_Addresses)[eNTCP2V6MeshIdx].reset (); (*GetAddresses ())[eNTCP2V6MeshIdx].reset ();
} }
} }
@@ -941,12 +966,12 @@ namespace data
return nullptr; return nullptr;
} }
boost::shared_ptr<RouterInfo::Addresses> RouterInfo::GetAddresses () const RouterInfo::AddressesPtr RouterInfo::GetAddresses () const
{ {
#if (BOOST_VERSION >= 105300) #ifdef __cpp_lib_atomic_shared_ptr
return boost::atomic_load (&m_Addresses);
#else
return m_Addresses; return m_Addresses;
#else
return boost::atomic_load (&m_Addresses);
#endif #endif
} }
@@ -954,10 +979,10 @@ namespace data
std::shared_ptr<const RouterInfo::Address> RouterInfo::GetAddress (Filter filter) const std::shared_ptr<const RouterInfo::Address> RouterInfo::GetAddress (Filter filter) const
{ {
// TODO: make it more generic using comparator // TODO: make it more generic using comparator
#if (BOOST_VERSION >= 105300) #ifdef __cpp_lib_atomic_shared_ptr
AddressesPtr addresses = m_Addresses;
#else
auto addresses = boost::atomic_load (&m_Addresses); auto addresses = boost::atomic_load (&m_Addresses);
#else
auto addresses = m_Addresses;
#endif #endif
for (const auto& address : *addresses) for (const auto& address : *addresses)
if (address && filter (address)) return address; if (address && filter (address)) return address;
@@ -1014,17 +1039,24 @@ namespace data
bool RouterInfo::IsEligibleFloodfill () const bool RouterInfo::IsEligibleFloodfill () const
{ {
// floodfill must have published ipv4, >= 0.9.38 and not DSA // floodfill must have published ipv4 or reachable ipv4 and published ipv6
return m_Version >= NETDB_MIN_FLOODFILL_VERSION && IsPublished (true) && // >= 0.9.59 and not DSA
return m_Version >= NETDB_MIN_FLOODFILL_VERSION && (IsPublished (true) ||
(IsReachableBy (eNTCP2V4 | eSSU2V4) && IsPublished (false))) &&
GetIdentity ()->GetSigningKeyType () != SIGNING_KEY_TYPE_DSA_SHA1; GetIdentity ()->GetSigningKeyType () != SIGNING_KEY_TYPE_DSA_SHA1;
} }
bool RouterInfo::IsPublished (bool v4) const bool RouterInfo::IsPublished (bool v4) const
{ {
if (m_Caps & (eUnreachable | eHidden)) return false; // if router sets U or H we assume that all addresses are not published if (m_Caps & (eUnreachable | eHidden)) return false; // if router sets U or H we assume that all addresses are not published
return m_PublishedTransports & (v4 ? (eNTCP2V4 | eSSU2V4) : (eNTCP2V6 | eSSU2V6)); return IsPublishedOn (v4 ? (eNTCP2V4 | eSSU2V4) : (eNTCP2V6 | eSSU2V6));
} }
bool RouterInfo::IsPublishedOn (CompatibleTransports transports) const
{
return m_PublishedTransports & transports;
}
bool RouterInfo::IsNAT2NATOnly (const RouterInfo& other) const bool RouterInfo::IsNAT2NATOnly (const RouterInfo& other) const
{ {
return !(m_PublishedTransports & other.m_SupportedTransports) && return !(m_PublishedTransports & other.m_SupportedTransports) &&
@@ -1047,7 +1079,7 @@ namespace data
void RouterInfo::SetUnreachableAddressesTransportCaps (uint8_t transports) void RouterInfo::SetUnreachableAddressesTransportCaps (uint8_t transports)
{ {
for (auto& addr: *m_Addresses) for (auto& addr: *GetAddresses ())
{ {
if (addr && !addr->published) if (addr && !addr->published)
{ {
@@ -1061,7 +1093,7 @@ namespace data
{ {
m_SupportedTransports = 0; m_SupportedTransports = 0;
m_ReachableTransports = 0; m_ReachableTransports = 0;
for (const auto& addr: *m_Addresses) for (const auto& addr: *GetAddresses ())
{ {
if (!addr) continue; if (!addr) continue;
uint8_t transports = 0; uint8_t transports = 0;
@@ -1113,6 +1145,7 @@ namespace data
void RouterInfo::UpdateBuffer (const uint8_t * buf, size_t len) void RouterInfo::UpdateBuffer (const uint8_t * buf, size_t len)
{ {
m_IsBufferScheduledToDelete = false;
if (!m_Buffer) if (!m_Buffer)
m_Buffer = NewBuffer (); m_Buffer = NewBuffer ();
if (len > m_Buffer->size ()) len = m_Buffer->size (); if (len > m_Buffer->size ()) len = m_Buffer->size ();
@@ -1120,6 +1153,12 @@ namespace data
m_Buffer->SetBufferLen (len); m_Buffer->SetBufferLen (len);
} }
std::shared_ptr<RouterInfo::Buffer> RouterInfo::CopyBuffer () const
{
if (!m_Buffer) return nullptr;
return netdb.NewRouterInfoBuffer (*m_Buffer);
}
std::shared_ptr<RouterInfo::Buffer> RouterInfo::NewBuffer () const std::shared_ptr<RouterInfo::Buffer> RouterInfo::NewBuffer () const
{ {
return netdb.NewRouterInfoBuffer (); return netdb.NewRouterInfoBuffer ();
@@ -1130,7 +1169,7 @@ namespace data
return netdb.NewRouterInfoAddress (); return netdb.NewRouterInfoAddress ();
} }
boost::shared_ptr<RouterInfo::Addresses> RouterInfo::NewAddresses () const RouterInfo::AddressesPtr RouterInfo::NewAddresses () const
{ {
return netdb.NewRouterInfoAddresses (); return netdb.NewRouterInfoAddresses ();
} }
@@ -1165,6 +1204,19 @@ namespace data
return false; return false;
} }
} }
std::string RouterInfo::GetTransportName (SupportedTransports tr)
{
switch (tr)
{
case eNTCP2V4: return "NTCP2V4";
case eNTCP2V6: return "NTCP2V6";
case eSSU2V4: return "SSU2V4";
case eSSU2V6: return "SSU2V6";
case eNTCP2V6Mesh: return "Mesh";
default: return "";
}
}
void LocalRouterInfo::CreateBuffer (const PrivateKeys& privateKeys) void LocalRouterInfo::CreateBuffer (const PrivateKeys& privateKeys)
{ {
@@ -1203,7 +1255,7 @@ namespace data
CAPS_FLAG_EXTRA_BANDWIDTH2 : // 'X' CAPS_FLAG_EXTRA_BANDWIDTH2 : // 'X'
CAPS_FLAG_EXTRA_BANDWIDTH1; // 'P' CAPS_FLAG_EXTRA_BANDWIDTH1; // 'P'
else else
caps += CAPS_FLAG_HIGH_BANDWIDTH2; // 'O' caps += CAPS_FLAG_HIGH_BANDWIDTH; // 'O'
caps += CAPS_FLAG_FLOODFILL; // floodfill caps += CAPS_FLAG_FLOODFILL; // floodfill
} }
else else
@@ -1211,7 +1263,7 @@ namespace data
if (c & eExtraBandwidth) if (c & eExtraBandwidth)
caps += (c & eHighBandwidth) ? CAPS_FLAG_EXTRA_BANDWIDTH2 /* 'X' */ : CAPS_FLAG_EXTRA_BANDWIDTH1; /*'P' */ caps += (c & eHighBandwidth) ? CAPS_FLAG_EXTRA_BANDWIDTH2 /* 'X' */ : CAPS_FLAG_EXTRA_BANDWIDTH1; /*'P' */
else else
caps += (c & eHighBandwidth) ? CAPS_FLAG_HIGH_BANDWIDTH2 /* 'O' */: CAPS_FLAG_LOW_BANDWIDTH2 /* 'L' */; // bandwidth caps += (c & eHighBandwidth) ? CAPS_FLAG_HIGH_BANDWIDTH /* 'O' */: CAPS_FLAG_LOW_BANDWIDTH2 /* 'L' */; // bandwidth
} }
if (c & eHidden) caps += CAPS_FLAG_HIDDEN; // hidden if (c & eHidden) caps += CAPS_FLAG_HIDDEN; // hidden
if (c & eReachable) caps += CAPS_FLAG_REACHABLE; // reachable if (c & eReachable) caps += CAPS_FLAG_REACHABLE; // reachable
@@ -1482,9 +1534,9 @@ namespace data
return std::make_shared<Address> (); return std::make_shared<Address> ();
} }
boost::shared_ptr<RouterInfo::Addresses> LocalRouterInfo::NewAddresses () const RouterInfo::AddressesPtr LocalRouterInfo::NewAddresses () const
{ {
return boost::make_shared<Addresses> (); return RouterInfo::AddressesPtr(new RouterInfo::Addresses ());
} }
std::shared_ptr<IdentityEx> LocalRouterInfo::NewIdentity (const uint8_t * buf, size_t len) const std::shared_ptr<IdentityEx> LocalRouterInfo::NewIdentity (const uint8_t * buf, size_t len) const
@@ -1526,5 +1578,23 @@ namespace data
} }
return false; return false;
} }
bool LocalRouterInfo::UpdateSSU2Introducer (const IdentHash& h, bool v4, uint32_t iTag, uint32_t iExp)
{
auto addresses = GetAddresses ();
if (!addresses) return false;
auto addr = (*addresses)[v4 ? eSSU2V4Idx : eSSU2V6Idx];
if (addr)
{
for (auto& it: addr->ssu->introducers)
if (h == it.iH)
{
it.iTag = iTag;
it.iExp = iExp;
return true;
}
}
return false;
}
} }
} }

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2024, The PurpleI2P Project * Copyright (c) 2013-2025, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@@ -15,8 +15,11 @@
#include <vector> #include <vector>
#include <array> #include <array>
#include <iostream> #include <iostream>
#include <memory>
#include <boost/asio.hpp> #include <boost/asio.hpp>
#ifndef __cpp_lib_atomic_shared_ptr
#include <boost/shared_ptr.hpp> #include <boost/shared_ptr.hpp>
#endif
#include "Identity.h" #include "Identity.h"
#include "Profiling.h" #include "Profiling.h"
#include "Family.h" #include "Family.h"
@@ -40,8 +43,8 @@ namespace data
const char CAPS_FLAG_LOW_BANDWIDTH1 = 'K'; /* < 12 KBps */ const char CAPS_FLAG_LOW_BANDWIDTH1 = 'K'; /* < 12 KBps */
const char CAPS_FLAG_LOW_BANDWIDTH2 = 'L'; /* 12-48 KBps */ const char CAPS_FLAG_LOW_BANDWIDTH2 = 'L'; /* 12-48 KBps */
const char CAPS_FLAG_LOW_BANDWIDTH3 = 'M'; /* 48-64 KBps */ const char CAPS_FLAG_LOW_BANDWIDTH3 = 'M'; /* 48-64 KBps */
const char CAPS_FLAG_HIGH_BANDWIDTH1 = 'N'; /* 64-128 KBps */ const char CAPS_FLAG_LOW_BANDWIDTH4 = 'N'; /* 64-128 KBps */
const char CAPS_FLAG_HIGH_BANDWIDTH2 = 'O'; /* 128-256 KBps */ const char CAPS_FLAG_HIGH_BANDWIDTH = 'O'; /* 128-256 KBps */
const char CAPS_FLAG_EXTRA_BANDWIDTH1 = 'P'; /* 256-2048 KBps */ const char CAPS_FLAG_EXTRA_BANDWIDTH1 = 'P'; /* 256-2048 KBps */
const char CAPS_FLAG_EXTRA_BANDWIDTH2 = 'X'; /* > 2048 KBps */ const char CAPS_FLAG_EXTRA_BANDWIDTH2 = 'X'; /* > 2048 KBps */
// bandwidth limits in kBps // bandwidth limits in kBps
@@ -188,6 +191,7 @@ namespace data
Buffer () = default; Buffer () = default;
Buffer (const uint8_t * buf, size_t len); Buffer (const uint8_t * buf, size_t len);
Buffer (const Buffer& other): Buffer (other.data (), other.m_BufferLen) {};
size_t GetBufferLen () const { return m_BufferLen; }; size_t GetBufferLen () const { return m_BufferLen; };
void SetBufferLen (size_t len) { m_BufferLen = len; }; void SetBufferLen (size_t len) { m_BufferLen = len; };
@@ -198,7 +202,11 @@ namespace data
}; };
typedef std::array<std::shared_ptr<Address>, eNumTransports> Addresses; typedef std::array<std::shared_ptr<Address>, eNumTransports> Addresses;
#ifdef __cpp_lib_atomic_shared_ptr
typedef std::shared_ptr<Addresses> AddressesPtr;
#else
typedef boost::shared_ptr<Addresses> AddressesPtr;
#endif
RouterInfo (const std::string& fullPath); RouterInfo (const std::string& fullPath);
RouterInfo (const RouterInfo& ) = default; RouterInfo (const RouterInfo& ) = default;
RouterInfo& operator=(const RouterInfo& ) = default; RouterInfo& operator=(const RouterInfo& ) = default;
@@ -213,7 +221,7 @@ namespace data
int GetVersion () const { return m_Version; }; int GetVersion () const { return m_Version; };
virtual void SetProperty (const std::string& key, const std::string& value) {}; virtual void SetProperty (const std::string& key, const std::string& value) {};
virtual void ClearProperties () {}; virtual void ClearProperties () {};
boost::shared_ptr<Addresses> GetAddresses () const; // should be called for local RI only, otherwise must return shared_ptr AddressesPtr GetAddresses () const; // should be called for local RI only, otherwise must return shared_ptr
std::shared_ptr<const Address> GetNTCP2V4Address () const; std::shared_ptr<const Address> GetNTCP2V4Address () const;
std::shared_ptr<const Address> GetNTCP2V6Address () const; std::shared_ptr<const Address> GetNTCP2V6Address () const;
std::shared_ptr<const Address> GetPublishedNTCP2V4Address () const; std::shared_ptr<const Address> GetPublishedNTCP2V4Address () const;
@@ -263,12 +271,14 @@ namespace data
bool IsEligibleFloodfill () const; bool IsEligibleFloodfill () const;
bool IsDeclaredFloodfill () const { return m_Caps & RouterInfo::eFloodfill; }; bool IsDeclaredFloodfill () const { return m_Caps & RouterInfo::eFloodfill; };
bool IsPublished (bool v4) const; bool IsPublished (bool v4) const;
bool IsPublishedOn (CompatibleTransports transports) const;
bool IsNAT2NATOnly (const RouterInfo& other) const; // only NAT-to-NAT connection is possible bool IsNAT2NATOnly (const RouterInfo& other) const; // only NAT-to-NAT connection is possible
bool IsSSU2PeerTesting (bool v4) const; bool IsSSU2PeerTesting (bool v4) const;
bool IsSSU2Introducer (bool v4) const; bool IsSSU2Introducer (bool v4) const;
bool IsHighCongestion (bool highBandwidth) const; bool IsHighCongestion (bool highBandwidth) const;
uint8_t GetCaps () const { return m_Caps; }; uint8_t GetCaps () const { return m_Caps; };
char GetBandwidthCap() const { return m_BandwidthCap; };
void SetCaps (uint8_t caps) { m_Caps = caps; }; void SetCaps (uint8_t caps) { m_Caps = caps; };
Congestion GetCongestion () const { return m_Congestion; }; Congestion GetCongestion () const { return m_Congestion; };
@@ -280,8 +290,12 @@ namespace data
const uint8_t * GetBuffer () const { return m_Buffer ? m_Buffer->data () : nullptr; }; const uint8_t * GetBuffer () const { return m_Buffer ? m_Buffer->data () : nullptr; };
const uint8_t * LoadBuffer (const std::string& fullPath); // load if necessary const uint8_t * LoadBuffer (const std::string& fullPath); // load if necessary
size_t GetBufferLen () const { return m_Buffer ? m_Buffer->GetBufferLen () : 0; }; size_t GetBufferLen () const { return m_Buffer ? m_Buffer->GetBufferLen () : 0; };
void DeleteBuffer () { m_Buffer = nullptr; }; void DeleteBuffer () { m_Buffer = nullptr; m_IsBufferScheduledToDelete = false; };
std::shared_ptr<Buffer> GetSharedBuffer () const { return m_Buffer; }; std::shared_ptr<Buffer> GetSharedBuffer () const { return m_Buffer; };
std::shared_ptr<Buffer> CopyBuffer () const;
void ScheduleBufferToDelete () { m_IsBufferScheduledToDelete = true; };
void CancelBufferToDelete () { m_IsBufferScheduledToDelete = false; };
bool IsBufferScheduledToDelete () const { return m_IsBufferScheduledToDelete; };
bool IsUpdated () const { return m_IsUpdated; }; bool IsUpdated () const { return m_IsUpdated; };
void SetUpdated (bool updated) { m_IsUpdated = updated; }; void SetUpdated (bool updated) { m_IsUpdated = updated; };
@@ -329,7 +343,7 @@ namespace data
std::shared_ptr<const Address> GetAddress (Filter filter) const; std::shared_ptr<const Address> GetAddress (Filter filter) const;
virtual std::shared_ptr<Buffer> NewBuffer () const; virtual std::shared_ptr<Buffer> NewBuffer () const;
virtual std::shared_ptr<Address> NewAddress () const; virtual std::shared_ptr<Address> NewAddress () const;
virtual boost::shared_ptr<Addresses> NewAddresses () const; virtual AddressesPtr NewAddresses () const;
virtual std::shared_ptr<IdentityEx> NewIdentity (const uint8_t * buf, size_t len) const; virtual std::shared_ptr<IdentityEx> NewIdentity (const uint8_t * buf, size_t len) const;
private: private:
@@ -338,13 +352,22 @@ namespace data
std::shared_ptr<const IdentityEx> m_RouterIdentity; std::shared_ptr<const IdentityEx> m_RouterIdentity;
std::shared_ptr<Buffer> m_Buffer; std::shared_ptr<Buffer> m_Buffer;
uint64_t m_Timestamp; // in milliseconds uint64_t m_Timestamp; // in milliseconds
boost::shared_ptr<Addresses> m_Addresses; // TODO: use std::shared_ptr and std::atomic_store for gcc >= 4.9 #ifdef __cpp_lib_atomic_shared_ptr
bool m_IsUpdated, m_IsUnreachable, m_IsFloodfill; std::atomic<AddressesPtr> m_Addresses;
#else
AddressesPtr m_Addresses;
#endif
bool m_IsUpdated, m_IsUnreachable, m_IsFloodfill, m_IsBufferScheduledToDelete;
CompatibleTransports m_SupportedTransports, m_ReachableTransports, m_PublishedTransports; CompatibleTransports m_SupportedTransports, m_ReachableTransports, m_PublishedTransports;
uint8_t m_Caps; uint8_t m_Caps;
char m_BandwidthCap;
int m_Version; int m_Version;
Congestion m_Congestion; Congestion m_Congestion;
mutable std::shared_ptr<RouterProfile> m_Profile; mutable std::shared_ptr<RouterProfile> m_Profile;
public:
static std::string GetTransportName (SupportedTransports tr);
}; };
class LocalRouterInfo: public RouterInfo class LocalRouterInfo: public RouterInfo
@@ -364,6 +387,7 @@ namespace data
bool AddSSU2Introducer (const Introducer& introducer, bool v4); bool AddSSU2Introducer (const Introducer& introducer, bool v4);
bool RemoveSSU2Introducer (const IdentHash& h, bool v4); bool RemoveSSU2Introducer (const IdentHash& h, bool v4);
bool UpdateSSU2Introducer (const IdentHash& h, bool v4, uint32_t iTag, uint32_t iExp);
private: private:
@@ -372,7 +396,7 @@ namespace data
void WriteString (const std::string& str, std::ostream& s) const; void WriteString (const std::string& str, std::ostream& s) const;
std::shared_ptr<Buffer> NewBuffer () const override; std::shared_ptr<Buffer> NewBuffer () const override;
std::shared_ptr<Address> NewAddress () const override; std::shared_ptr<Address> NewAddress () const override;
boost::shared_ptr<Addresses> NewAddresses () const override; RouterInfo::AddressesPtr NewAddresses () const override;
std::shared_ptr<IdentityEx> NewIdentity (const uint8_t * buf, size_t len) const override; std::shared_ptr<IdentityEx> NewIdentity (const uint8_t * buf, size_t len) const override;
private: private:

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2022-2024, The PurpleI2P Project * Copyright (c) 2022-2025, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@@ -10,30 +10,42 @@
#define SSU2_H__ #define SSU2_H__
#include <unordered_map> #include <unordered_map>
#include <unordered_set>
#include <vector>
#include <list>
#include <array>
#include <mutex> #include <mutex>
#include <random>
#include "util.h" #include "util.h"
#include "SSU2Session.h" #include "SSU2Session.h"
#include "SSU2OutOfSession.h"
#include "Socks5.h" #include "Socks5.h"
namespace i2p namespace i2p
{ {
namespace transport namespace transport
{ {
const int SSU2_TERMINATION_CHECK_TIMEOUT = 25; // in seconds const int SSU2_TERMINATION_CHECK_TIMEOUT = 23; // in seconds
const int SSU2_TERMINATION_CHECK_TIMEOUT_VARIANCE = 5; // in seconds
const int SSU2_CLEANUP_INTERVAL = 72; // in seconds const int SSU2_CLEANUP_INTERVAL = 72; // in seconds
const int SSU2_RESEND_CHECK_TIMEOUT = 40; // in milliseconds const int SSU2_RESEND_CHECK_TIMEOUT = 40; // in milliseconds
const int SSU2_RESEND_CHECK_TIMEOUT_VARIANCE = 10; // in milliseconds const int SSU2_RESEND_CHECK_TIMEOUT_VARIANCE = 10; // in milliseconds
const int SSU2_RESEND_CHECK_MORE_TIMEOUT = 10; // in milliseconds const int SSU2_RESEND_CHECK_MORE_TIMEOUT = 4; // in milliseconds
const int SSU2_RESEND_CHECK_MORE_TIMEOUT_VARIANCE = 9; // in milliseconds
const size_t SSU2_MAX_RESEND_PACKETS = 128; // packets to resend at the time const size_t SSU2_MAX_RESEND_PACKETS = 128; // packets to resend at the time
const uint64_t SSU2_SOCKET_MIN_BUFFER_SIZE = 128 * 1024; const uint64_t SSU2_SOCKET_MIN_BUFFER_SIZE = 128 * 1024;
const uint64_t SSU2_SOCKET_MAX_BUFFER_SIZE = 4 * 1024 * 1024; const uint64_t SSU2_SOCKET_MAX_BUFFER_SIZE = 4 * 1024 * 1024;
const size_t SSU2_MAX_NUM_INTRODUCERS = 3; const size_t SSU2_MAX_NUM_INTRODUCERS = 3;
const size_t SSU2_MIN_RECEIVED_PACKET_SIZE = 40; // 16 byte short header + 8 byte minimum payload + 16 byte MAC const size_t SSU2_MIN_RECEIVED_PACKET_SIZE = 40; // 16 byte short header + 8 byte minimum payload + 16 byte MAC
const size_t SSU2_MAX_RECEIVED_QUEUE_SIZE = 2500; // in packets
const int SSU2_TO_INTRODUCER_SESSION_DURATION = 3600; // 1 hour const int SSU2_TO_INTRODUCER_SESSION_DURATION = 3600; // 1 hour
const int SSU2_TO_INTRODUCER_SESSION_EXPIRATION = 4800; // 80 minutes const int SSU2_TO_INTRODUCER_SESSION_EXPIRATION = 4800; // 80 minutes
const int SSU2_KEEP_ALIVE_INTERVAL = 15; // in seconds const int SSU2_KEEP_ALIVE_INTERVAL = 15; // in seconds
const int SSU2_KEEP_ALIVE_INTERVAL_VARIANCE = 4; // in seconds const int SSU2_KEEP_ALIVE_INTERVAL_VARIANCE = 4; // in seconds
const int SSU2_PROXY_CONNECT_RETRY_TIMEOUT = 30; // in seconds const int SSU2_PROXY_CONNECT_RETRY_TIMEOUT = 30; // in seconds
const int SSU2_MIN_HOLE_PUNCH_EXPIRATION = 30; // in seconds
const int SSU2_MAX_HOLE_PUNCH_EXPIRATION = 160; // in seconds
const size_t SSU2_MAX_NUM_PACKETS_PER_BATCH = 64;
class SSU2Server: private i2p::util::RunnableServiceWithWork class SSU2Server: private i2p::util::RunnableServiceWithWork
{ {
@@ -43,13 +55,13 @@ namespace transport
size_t len; size_t len;
boost::asio::ip::udp::endpoint from; boost::asio::ip::udp::endpoint from;
}; };
class ReceiveService: public i2p::util::RunnableService class ReceiveService: public i2p::util::RunnableService
{ {
public: public:
ReceiveService (const std::string& name): RunnableService (name) {}; ReceiveService (const std::string& name): RunnableService (name) {};
boost::asio::io_service& GetService () { return GetIOService (); }; auto& GetService () { return GetIOService (); };
void Start () { StartIOService (); }; void Start () { StartIOService (); };
void Stop () { StopIOService (); }; void Stop () { StopIOService (); };
}; };
@@ -61,29 +73,45 @@ namespace transport
void Start (); void Start ();
void Stop (); void Stop ();
boost::asio::io_service& GetService () { return GetIOService (); }; auto& GetService () { return GetIOService (); };
void SetLocalAddress (const boost::asio::ip::address& localAddress); void SetLocalAddress (const boost::asio::ip::address& localAddress);
bool SetProxy (const std::string& address, uint16_t port); bool SetProxy (const std::string& address, uint16_t port);
bool UsesProxy () const { return m_IsThroughProxy; }; bool UsesProxy () const { return m_IsThroughProxy; };
bool IsSupported (const boost::asio::ip::address& addr) const; bool IsSupported (const boost::asio::ip::address& addr) const;
uint16_t GetPort (bool v4) const; uint16_t GetPort (bool v4) const;
bool IsConnectedRecently (const boost::asio::ip::udp::endpoint& ep, bool max = true);
void AddConnectedRecently (const boost::asio::ip::udp::endpoint& ep, uint64_t ts);
std::mt19937& GetRng () { return m_Rng; }
bool AEADChaCha20Poly1305Encrypt (const uint8_t * msg, size_t msgLen, const uint8_t * ad, size_t adLen,
const uint8_t * key, const uint8_t * nonce, uint8_t * buf, size_t len);
bool AEADChaCha20Poly1305Decrypt (const uint8_t * msg, size_t msgLen, const uint8_t * ad, size_t adLen,
const uint8_t * key, const uint8_t * nonce, uint8_t * buf, size_t len);
void ChaCha20 (const uint8_t * msg, size_t msgLen, const uint8_t * key, const uint8_t * nonce, uint8_t * out);
bool IsMaxNumIntroducers (bool v4) const { return (v4 ? m_Introducers.size () : m_IntroducersV6.size ()) >= SSU2_MAX_NUM_INTRODUCERS; }
bool IsSyncClockFromPeers () const { return m_IsSyncClockFromPeers; }; bool IsSyncClockFromPeers () const { return m_IsSyncClockFromPeers; };
void AdjustTimeOffset (int64_t offset, std::shared_ptr<const i2p::data::IdentityEx> from); void AdjustTimeOffset (int64_t offset, std::shared_ptr<const i2p::data::IdentityEx> from);
void AddSession (std::shared_ptr<SSU2Session> session); bool AddSession (std::shared_ptr<SSU2Session> session);
void RemoveSession (uint64_t connID); void RemoveSession (uint64_t connID);
void RequestRemoveSession (uint64_t connID);
void AddSessionByRouterHash (std::shared_ptr<SSU2Session> session); void AddSessionByRouterHash (std::shared_ptr<SSU2Session> session);
bool AddPendingOutgoingSession (std::shared_ptr<SSU2Session> session); bool AddPendingOutgoingSession (std::shared_ptr<SSU2Session> session);
void RemovePendingOutgoingSession (const boost::asio::ip::udp::endpoint& ep); void RemovePendingOutgoingSession (const boost::asio::ip::udp::endpoint& ep);
std::shared_ptr<SSU2Session> FindSession (const i2p::data::IdentHash& ident) const; std::shared_ptr<SSU2Session> FindSession (const i2p::data::IdentHash& ident);
std::shared_ptr<SSU2Session> FindPendingOutgoingSession (const boost::asio::ip::udp::endpoint& ep) const; std::shared_ptr<SSU2Session> FindPendingOutgoingSession (const boost::asio::ip::udp::endpoint& ep) const;
std::shared_ptr<SSU2Session> GetRandomPeerTestSession (i2p::data::RouterInfo::CompatibleTransports remoteTransports, std::shared_ptr<SSU2Session> GetRandomPeerTestSession (i2p::data::RouterInfo::CompatibleTransports remoteTransports,
const i2p::data::IdentHash& excluded) const; const i2p::data::IdentHash& excluded);
void AddRelay (uint32_t tag, std::shared_ptr<SSU2Session> relay); void AddRelay (uint32_t tag, std::shared_ptr<SSU2Session> relay);
void RemoveRelay (uint32_t tag); void RemoveRelay (uint32_t tag);
std::shared_ptr<SSU2Session> FindRelaySession (uint32_t tag); std::shared_ptr<SSU2Session> FindRelaySession (uint32_t tag);
bool AddPeerTest (uint32_t nonce, std::shared_ptr<SSU2Session> aliceSession, uint64_t ts);
std::shared_ptr<SSU2Session> GetPeerTest (uint32_t nonce);
bool AddRequestedPeerTest (uint32_t nonce, std::shared_ptr<SSU2PeerTestSession> session, uint64_t ts);
std::shared_ptr<SSU2PeerTestSession> GetRequestedPeerTest (uint32_t nonce);
void Send (const uint8_t * header, size_t headerLen, const uint8_t * payload, size_t payloadLen, void Send (const uint8_t * header, size_t headerLen, const uint8_t * payload, size_t payloadLen,
const boost::asio::ip::udp::endpoint& to); const boost::asio::ip::udp::endpoint& to);
void Send (const uint8_t * header, size_t headerLen, const uint8_t * headerX, size_t headerXLen, void Send (const uint8_t * header, size_t headerLen, const uint8_t * headerX, size_t headerXLen,
@@ -111,10 +139,12 @@ namespace transport
void Receive (boost::asio::ip::udp::socket& socket); void Receive (boost::asio::ip::udp::socket& socket);
void HandleReceivedFrom (const boost::system::error_code& ecode, size_t bytes_transferred, void HandleReceivedFrom (const boost::system::error_code& ecode, size_t bytes_transferred,
Packet * packet, boost::asio::ip::udp::socket& socket); Packet * packet, boost::asio::ip::udp::socket& socket);
void HandleReceivedPacket (Packet * packet); void HandleReceivedPackets (std::list<Packet *>&& packets);
void HandleReceivedPackets (std::vector<Packet *> packets);
void ProcessNextPacket (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint); void ProcessNextPacket (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint);
void InsertToReceivedPacketsQueue (Packet * packet);
void InsertToReceivedPacketsQueue (std::list<Packet *>& packets);
void HandleReceivedPacketsQueue ();
void ScheduleTermination (); void ScheduleTermination ();
void HandleTerminationTimer (const boost::system::error_code& ecode); void HandleTerminationTimer (const boost::system::error_code& ecode);
@@ -124,9 +154,10 @@ namespace transport
void ScheduleResend (bool more); void ScheduleResend (bool more);
void HandleResendTimer (const boost::system::error_code& ecode); void HandleResendTimer (const boost::system::error_code& ecode);
bool CheckPendingOutgoingSession (const boost::asio::ip::udp::endpoint& ep, bool peerTest);
void ConnectThroughIntroducer (std::shared_ptr<SSU2Session> session); void ConnectThroughIntroducer (std::shared_ptr<SSU2Session> session);
std::list<std::shared_ptr<SSU2Session> > FindIntroducers (int maxNumIntroducers, std::vector<std::shared_ptr<SSU2Session> > FindIntroducers (int maxNumIntroducers,
bool v4, const std::set<i2p::data::IdentHash>& excluded) const; bool v4, const std::unordered_set<i2p::data::IdentHash>& excluded);
void UpdateIntroducers (bool v4); void UpdateIntroducers (bool v4);
void ScheduleIntroducersUpdateTimer (); void ScheduleIntroducersUpdateTimer ();
void HandleIntroducersUpdateTimer (const boost::system::error_code& ecode, bool v4); void HandleIntroducersUpdateTimer (const boost::system::error_code& ecode, bool v4);
@@ -149,12 +180,14 @@ namespace transport
boost::asio::ip::udp::socket m_SocketV4, m_SocketV6; boost::asio::ip::udp::socket m_SocketV4, m_SocketV6;
boost::asio::ip::address m_AddressV4, m_AddressV6; boost::asio::ip::address m_AddressV4, m_AddressV6;
std::unordered_map<uint64_t, std::shared_ptr<SSU2Session> > m_Sessions; std::unordered_map<uint64_t, std::shared_ptr<SSU2Session> > m_Sessions;
std::unordered_map<i2p::data::IdentHash, std::shared_ptr<SSU2Session> > m_SessionsByRouterHash; std::unordered_map<i2p::data::IdentHash, std::weak_ptr<SSU2Session> > m_SessionsByRouterHash;
mutable std::mutex m_SessionsByRouterHashMutex;
std::map<boost::asio::ip::udp::endpoint, std::shared_ptr<SSU2Session> > m_PendingOutgoingSessions; std::map<boost::asio::ip::udp::endpoint, std::shared_ptr<SSU2Session> > m_PendingOutgoingSessions;
mutable std::mutex m_PendingOutgoingSessionsMutex; mutable std::mutex m_PendingOutgoingSessionsMutex;
std::map<boost::asio::ip::udp::endpoint, std::pair<uint64_t, uint32_t> > m_IncomingTokens, m_OutgoingTokens; // remote endpoint -> (token, expires in seconds) std::map<boost::asio::ip::udp::endpoint, std::pair<uint64_t, uint32_t> > m_IncomingTokens, m_OutgoingTokens; // remote endpoint -> (token, expires in seconds)
std::map<uint32_t, std::shared_ptr<SSU2Session> > m_Relays; // we are introducer, relay tag -> session std::unordered_map<uint32_t, std::weak_ptr<SSU2Session> > m_Relays; // we are introducer, relay tag -> session
std::list<i2p::data::IdentHash> m_Introducers, m_IntroducersV6; // introducers we are connected to std::unordered_map<uint32_t, std::pair <std::weak_ptr<SSU2Session>, uint64_t > > m_PeerTests; // nonce->(Alice, timestamp). We are Bob
std::list<std::pair<i2p::data::IdentHash, uint32_t> > m_Introducers, m_IntroducersV6; // introducers we are connected to
i2p::util::MemoryPoolMt<Packet> m_PacketsPool; i2p::util::MemoryPoolMt<Packet> m_PacketsPool;
i2p::util::MemoryPool<SSU2SentPacket> m_SentPacketsPool; i2p::util::MemoryPool<SSU2SentPacket> m_SentPacketsPool;
i2p::util::MemoryPool<SSU2IncompleteMessage> m_IncompleteMessagesPool; i2p::util::MemoryPool<SSU2IncompleteMessage> m_IncompleteMessagesPool;
@@ -166,7 +199,16 @@ namespace transport
bool m_IsSyncClockFromPeers; bool m_IsSyncClockFromPeers;
int64_t m_PendingTimeOffset; // during peer test int64_t m_PendingTimeOffset; // during peer test
std::shared_ptr<const i2p::data::IdentityEx> m_PendingTimeOffsetFrom; std::shared_ptr<const i2p::data::IdentityEx> m_PendingTimeOffsetFrom;
std::mt19937 m_Rng;
std::map<boost::asio::ip::udp::endpoint, uint64_t> m_ConnectedRecently; // endpoint -> last activity time in seconds
mutable std::mutex m_ConnectedRecentlyMutex;
std::unordered_map<uint32_t, std::pair <std::weak_ptr<SSU2PeerTestSession>, uint64_t > > m_RequestedPeerTests; // nonce->(Alice, timestamp)
std::list<Packet *> m_ReceivedPacketsQueue;
mutable std::mutex m_ReceivedPacketsQueueMutex;
i2p::crypto::AEADChaCha20Poly1305Encryptor m_Encryptor;
i2p::crypto::AEADChaCha20Poly1305Decryptor m_Decryptor;
i2p::crypto::ChaCha20Context m_ChaCha20;
// proxy // proxy
bool m_IsThroughProxy; bool m_IsThroughProxy;
uint8_t m_UDPRequestHeader[SOCKS5_UDP_IPV6_REQUEST_HEADER_SIZE]; uint8_t m_UDPRequestHeader[SOCKS5_UDP_IPV6_REQUEST_HEADER_SIZE];

View File

@@ -0,0 +1,355 @@
/*
* Copyright (c) 2024-2025, The PurpleI2P Project
*
* This file is part of Purple i2pd project and licensed under BSD3
*
* See full license text in LICENSE file at top of project tree
*/
#include "Log.h"
#include "SSU2.h"
#include "SSU2OutOfSession.h"
namespace i2p
{
namespace transport
{
SSU2PeerTestSession::SSU2PeerTestSession (SSU2Server& server, uint64_t sourceConnID, uint64_t destConnID):
SSU2Session (server, nullptr, nullptr, false),
m_MsgNumReceived (0), m_NumResends (0),m_IsConnectedRecently (false), m_IsStatusChanged (false),
m_PeerTestResendTimer (server.GetService ())
{
if (!sourceConnID) sourceConnID = ~destConnID;
if (!destConnID) destConnID = ~sourceConnID;
SetSourceConnID (sourceConnID);
SetDestConnID (destConnID);
SetState (eSSU2SessionStatePeerTest);
SetTerminationTimeout (SSU2_PEER_TEST_EXPIRATION_TIMEOUT);
}
bool SSU2PeerTestSession::ProcessPeerTest (uint8_t * buf, size_t len)
{
// we are Alice or Charlie, msgs 5,6,7
Header header;
memcpy (header.buf, buf, 16);
header.ll[0] ^= CreateHeaderMask (i2p::context.GetSSU2IntroKey (), buf + (len - 24));
header.ll[1] ^= CreateHeaderMask (i2p::context.GetSSU2IntroKey (), buf + (len - 12));
if (header.h.type != eSSU2PeerTest)
{
LogPrint (eLogWarning, "SSU2: Unexpected message type ", (int)header.h.type, " instead ", (int)eSSU2PeerTest);
return false;
}
if (len < 48)
{
LogPrint (eLogWarning, "SSU2: PeerTest message too short ", len);
return false;
}
uint8_t nonce[12] = {0};
uint64_t headerX[2]; // sourceConnID, token
GetServer ().ChaCha20 (buf + 16, 16, i2p::context.GetSSU2IntroKey (), nonce, (uint8_t *)headerX);
SetDestConnID (headerX[0]);
// decrypt and handle payload
uint8_t * payload = buf + 32;
CreateNonce (be32toh (header.h.packetNum), nonce);
uint8_t h[32];
memcpy (h, header.buf, 16);
memcpy (h + 16, &headerX, 16);
if (!i2p::crypto::AEADChaCha20Poly1305 (payload, len - 48, h, 32,
i2p::context.GetSSU2IntroKey (), nonce, payload, len - 48, false))
{
LogPrint (eLogWarning, "SSU2: PeerTest AEAD verification failed ");
return false;
}
HandlePayload (payload, len - 48);
SetIsDataReceived (false);
return true;
}
void SSU2PeerTestSession::HandleAddress (const uint8_t * buf, size_t len)
{
if (!ExtractEndpoint (buf, len, m_OurEndpoint))
LogPrint (eLogWarning, "SSU2: Can't handle address block from peer test message");
}
void SSU2PeerTestSession::HandlePeerTest (const uint8_t * buf, size_t len)
{
// msgs 5-7
if (len < 8) return;
uint8_t msg = buf[0];
if (msg <= m_MsgNumReceived)
{
LogPrint (eLogDebug, "SSU2: PeerTest msg num ", msg, " received after ", m_MsgNumReceived, ". Ignored");
return;
}
size_t offset = 3; // points to signed data after msg + code + flag
uint32_t nonce = bufbe32toh (buf + offset + 1); // 1 - ver
switch (msg) // msg
{
case 5: // Alice from Charlie 1
{
if (htobe64 (((uint64_t)nonce << 32) | nonce) == GetSourceConnID ())
{
m_PeerTestResendTimer.cancel (); // cancel delayed msg 6 if any
m_IsConnectedRecently = GetServer ().IsConnectedRecently (GetRemoteEndpoint ());
if (GetAddress ())
{
if (!m_IsConnectedRecently)
SetRouterStatus (eRouterStatusOK);
else if (m_IsStatusChanged && GetRouterStatus () == eRouterStatusFirewalled)
SetRouterStatus (eRouterStatusUnknown);
SendPeerTest (6, buf + offset, len - offset);
}
}
else
LogPrint (eLogWarning, "SSU2: Peer test 5 nonce mismatch ", nonce, " connID=", GetSourceConnID ());
break;
}
case 6: // Charlie from Alice
{
m_PeerTestResendTimer.cancel (); // no more msg 5 resends
if (GetAddress ())
SendPeerTest (7, buf + offset, len - offset);
else
LogPrint (eLogWarning, "SSU2: Unknown address for peer test 6");
GetServer ().RequestRemoveSession (GetConnID ());
break;
}
case 7: // Alice from Charlie 2
{
m_PeerTestResendTimer.cancel (); // no more msg 6 resends
if (m_MsgNumReceived < 5 && m_OurEndpoint.port ()) // msg 5 was not received
{
if (m_OurEndpoint.address ().is_v4 ()) // ipv4
{
if (i2p::context.GetStatus () == eRouterStatusFirewalled)
{
if (m_OurEndpoint.port () != GetServer ().GetPort (true))
i2p::context.SetError (eRouterErrorSymmetricNAT);
else if (i2p::context.GetError () == eRouterErrorSymmetricNAT)
i2p::context.SetError (eRouterErrorNone);
}
}
else
{
if (i2p::context.GetStatusV6 () == eRouterStatusFirewalled)
{
if (m_OurEndpoint.port () != GetServer ().GetPort (false))
i2p::context.SetErrorV6 (eRouterErrorSymmetricNAT);
else if (i2p::context.GetErrorV6 () == eRouterErrorSymmetricNAT)
i2p::context.SetErrorV6 (eRouterErrorNone);
}
}
}
GetServer ().RequestRemoveSession (GetConnID ());
break;
}
default:
LogPrint (eLogWarning, "SSU2: PeerTest unexpected msg num ", msg);
return;
}
m_MsgNumReceived = msg;
}
void SSU2PeerTestSession::SendPeerTest (uint8_t msg)
{
auto addr = GetAddress ();
if (!addr) return;
Header header;
uint8_t h[32], payload[SSU2_MAX_PACKET_SIZE];
// fill packet
header.h.connID = GetDestConnID (); // dest id
RAND_bytes (header.buf + 8, 4); // random packet num
header.h.type = eSSU2PeerTest;
header.h.flags[0] = 2; // ver
header.h.flags[1] = (uint8_t)i2p::context.GetNetID (); // netID
header.h.flags[2] = 0; // flag
memcpy (h, header.buf, 16);
htobuf64 (h + 16, GetSourceConnID ()); // source id
// payload
payload[0] = eSSU2BlkDateTime;
htobe16buf (payload + 1, 4);
htobe32buf (payload + 3, (i2p::util::GetMillisecondsSinceEpoch () + 500)/1000);
size_t payloadSize = 7;
if (msg == 6 || msg == 7)
payloadSize += CreateAddressBlock (payload + payloadSize, GetMaxPayloadSize () - payloadSize, GetRemoteEndpoint ());
payloadSize += CreatePeerTestBlock (payload + payloadSize, GetMaxPayloadSize () - payloadSize,
msg, eSSU2PeerTestCodeAccept, nullptr, m_SignedData.data (), m_SignedData.size ());
payloadSize += CreatePaddingBlock (payload + payloadSize, GetMaxPayloadSize () - payloadSize);
// encrypt
uint8_t n[12];
CreateNonce (be32toh (header.h.packetNum), n);
i2p::crypto::AEADChaCha20Poly1305 (payload, payloadSize, h, 32, addr->i, n, payload, payloadSize + 16, true);
payloadSize += 16;
header.ll[0] ^= CreateHeaderMask (addr->i, payload + (payloadSize - 24));
header.ll[1] ^= CreateHeaderMask (addr->i, payload + (payloadSize - 12));
memset (n, 0, 12);
GetServer ().ChaCha20 (h + 16, 16, addr->i, n, h + 16);
// send
GetServer ().Send (header.buf, 16, h + 16, 16, payload, payloadSize, GetRemoteEndpoint ());
UpdateNumSentBytes (payloadSize + 32);
}
void SSU2PeerTestSession::SendPeerTest (uint8_t msg, const uint8_t * signedData, size_t signedDataLen, bool delayed)
{
#if __cplusplus >= 202002L // C++20
m_SignedData.assign (signedData, signedData + signedDataLen);
#else
m_SignedData.resize (signedDataLen);
memcpy (m_SignedData.data (), signedData, signedDataLen);
#endif
if (!delayed)
SendPeerTest (msg);
// schedule resend for msgs 5 or 6
if (msg == 5 || msg == 6)
ScheduleResend (msg);
}
void SSU2PeerTestSession::SendPeerTest (uint8_t msg, const uint8_t * signedData, size_t signedDataLen,
std::shared_ptr<const i2p::data::RouterInfo::Address> addr, bool delayed)
{
if (!addr) return;
SetAddress (addr);
SendPeerTest (msg, signedData, signedDataLen, delayed);
}
void SSU2PeerTestSession::Connect ()
{
LogPrint (eLogError, "SSU2: Can't connect peer test session");
}
bool SSU2PeerTestSession::ProcessFirstIncomingMessage (uint64_t connID, uint8_t * buf, size_t len)
{
LogPrint (eLogError, "SSU2: Can't handle incoming message in peer test session");
return false;
}
void SSU2PeerTestSession::ScheduleResend (uint8_t msg)
{
if (m_NumResends < SSU2_PEER_TEST_MAX_NUM_RESENDS)
{
m_PeerTestResendTimer.expires_from_now (boost::posix_time::milliseconds(
SSU2_PEER_TEST_RESEND_INTERVAL + GetServer ().GetRng ()() % SSU2_PEER_TEST_RESEND_INTERVAL_VARIANCE));
std::weak_ptr<SSU2PeerTestSession> s(std::static_pointer_cast<SSU2PeerTestSession>(shared_from_this ()));
m_PeerTestResendTimer.async_wait ([s, msg](const boost::system::error_code& ecode)
{
if (ecode != boost::asio::error::operation_aborted)
{
auto s1 = s.lock ();
if (s1)
{
if (msg > s1->m_MsgNumReceived)
{
s1->SendPeerTest (msg);
s1->m_NumResends++;
s1->ScheduleResend (msg);
}
}
}
});
}
}
SSU2HolePunchSession::SSU2HolePunchSession (SSU2Server& server, uint32_t nonce,
const boost::asio::ip::udp::endpoint& remoteEndpoint,
std::shared_ptr<const i2p::data::RouterInfo::Address> addr):
SSU2Session (server), // we create full incoming session
m_NumResends (0), m_HolePunchResendTimer (server.GetService ())
{
// we are Charlie
uint64_t destConnID = htobe64 (((uint64_t)nonce << 32) | nonce); // dest id
uint32_t sourceConnID = ~destConnID;
SetSourceConnID (sourceConnID);
SetDestConnID (destConnID);
SetState (eSSU2SessionStateHolePunch);
SetRemoteEndpoint (remoteEndpoint);
SetAddress (addr);
SetTerminationTimeout (SSU2_RELAY_NONCE_EXPIRATION_TIMEOUT);
}
void SSU2HolePunchSession::SendHolePunch ()
{
auto addr = GetAddress ();
if (!addr) return;
auto& ep = GetRemoteEndpoint ();
LogPrint (eLogDebug, "SSU2: Sending HolePunch to ", ep);
Header header;
uint8_t h[32], payload[SSU2_MAX_PACKET_SIZE];
// fill packet
header.h.connID = GetDestConnID (); // dest id
RAND_bytes (header.buf + 8, 4); // random packet num
header.h.type = eSSU2HolePunch;
header.h.flags[0] = 2; // ver
header.h.flags[1] = (uint8_t)i2p::context.GetNetID (); // netID
header.h.flags[2] = 0; // flag
memcpy (h, header.buf, 16);
htobuf64 (h + 16, GetSourceConnID ()); // source id
RAND_bytes (h + 24, 8); // header token, to be ignored by Alice
// payload
payload[0] = eSSU2BlkDateTime;
htobe16buf (payload + 1, 4);
htobe32buf (payload + 3, (i2p::util::GetMillisecondsSinceEpoch () + 500)/1000);
size_t payloadSize = 7;
payloadSize += CreateAddressBlock (payload + payloadSize, GetMaxPayloadSize () - payloadSize, ep);
// relay response block
if (payloadSize + m_RelayResponseBlock.size () < GetMaxPayloadSize ())
{
memcpy (payload + payloadSize, m_RelayResponseBlock.data (), m_RelayResponseBlock.size ());
payloadSize += m_RelayResponseBlock.size ();
}
payloadSize += CreatePaddingBlock (payload + payloadSize, GetMaxPayloadSize () - payloadSize);
// encrypt
uint8_t n[12];
CreateNonce (be32toh (header.h.packetNum), n);
i2p::crypto::AEADChaCha20Poly1305 (payload, payloadSize, h, 32, addr->i, n, payload, payloadSize + 16, true);
payloadSize += 16;
header.ll[0] ^= CreateHeaderMask (addr->i, payload + (payloadSize - 24));
header.ll[1] ^= CreateHeaderMask (addr->i, payload + (payloadSize - 12));
memset (n, 0, 12);
GetServer ().ChaCha20 (h + 16, 16, addr->i, n, h + 16);
// send
GetServer ().Send (header.buf, 16, h + 16, 16, payload, payloadSize, ep);
UpdateNumSentBytes (payloadSize + 32);
}
void SSU2HolePunchSession::SendHolePunch (const uint8_t * relayResponseBlock, size_t relayResponseBlockLen)
{
#if __cplusplus >= 202002L // C++20
m_RelayResponseBlock.assign (relayResponseBlock, relayResponseBlock + relayResponseBlockLen);
#else
m_RelayResponseBlock.resize (relayResponseBlockLen);
memcpy (m_RelayResponseBlock.data (), relayResponseBlock, relayResponseBlockLen);
#endif
SendHolePunch ();
ScheduleResend ();
}
void SSU2HolePunchSession::ScheduleResend ()
{
if (m_NumResends < SSU2_HOLE_PUNCH_MAX_NUM_RESENDS)
{
m_HolePunchResendTimer.expires_from_now (boost::posix_time::milliseconds(
SSU2_HOLE_PUNCH_RESEND_INTERVAL + GetServer ().GetRng ()() % SSU2_HOLE_PUNCH_RESEND_INTERVAL_VARIANCE));
std::weak_ptr<SSU2HolePunchSession> s(std::static_pointer_cast<SSU2HolePunchSession>(shared_from_this ()));
m_HolePunchResendTimer.async_wait ([s](const boost::system::error_code& ecode)
{
if (ecode != boost::asio::error::operation_aborted)
{
auto s1 = s.lock ();
if (s1 && s1->GetState () == eSSU2SessionStateHolePunch)
{
s1->SendHolePunch ();
s1->m_NumResends++;
s1->ScheduleResend ();
}
}
});
}
}
bool SSU2HolePunchSession::ProcessFirstIncomingMessage (uint64_t connID, uint8_t * buf, size_t len)
{
m_HolePunchResendTimer.cancel ();
return SSU2Session::ProcessFirstIncomingMessage (connID, buf, len);
}
}
}

View File

@@ -0,0 +1,86 @@
/*
* Copyright (c) 2024, The PurpleI2P Project
*
* This file is part of Purple i2pd project and licensed under BSD3
*
* See full license text in LICENSE file at top of project tree
*/
#ifndef SSU2_OUT_OF_SESSION_H__
#define SSU2_OUT_OF_SESSION_H__
#include <vector>
#include "SSU2Session.h"
namespace i2p
{
namespace transport
{
const int SSU2_PEER_TEST_RESEND_INTERVAL = 3000; // in milliseconds
const int SSU2_PEER_TEST_RESEND_INTERVAL_VARIANCE = 2000; // in milliseconds
const int SSU2_PEER_TEST_MAX_NUM_RESENDS = 3;
class SSU2PeerTestSession: public SSU2Session // for PeerTest msgs 5,6,7
{
public:
SSU2PeerTestSession (SSU2Server& server, uint64_t sourceConnID, uint64_t destConnID);
uint8_t GetMsgNumReceived () const { return m_MsgNumReceived; }
bool IsConnectedRecently () const { return m_IsConnectedRecently; }
void SetStatusChanged () { m_IsStatusChanged = true; }
void SendPeerTest (uint8_t msg, const uint8_t * signedData, size_t signedDataLen,
std::shared_ptr<const i2p::data::RouterInfo::Address> addr, bool delayed = false);
bool ProcessPeerTest (uint8_t * buf, size_t len) override;
void Connect () override; // outgoing
bool ProcessFirstIncomingMessage (uint64_t connID, uint8_t * buf, size_t len) override; // incoming
private:
void SendPeerTest (uint8_t msg, const uint8_t * signedData, size_t signedDataLen, bool delayed = false); // PeerTest message
void SendPeerTest (uint8_t msg); // send or resend m_SignedData
void HandlePeerTest (const uint8_t * buf, size_t len) override;
void HandleAddress (const uint8_t * buf, size_t len) override;
void ScheduleResend (uint8_t msg);
private:
uint8_t m_MsgNumReceived, m_NumResends;
bool m_IsConnectedRecently, m_IsStatusChanged;
std::vector<uint8_t> m_SignedData; // for resends
boost::asio::deadline_timer m_PeerTestResendTimer;
boost::asio::ip::udp::endpoint m_OurEndpoint; // as seen by peer
};
const int SSU2_HOLE_PUNCH_RESEND_INTERVAL = 1000; // in milliseconds
const int SSU2_HOLE_PUNCH_RESEND_INTERVAL_VARIANCE = 500; // in milliseconds
const int SSU2_HOLE_PUNCH_MAX_NUM_RESENDS = 3;
class SSU2HolePunchSession: public SSU2Session // Charlie
{
public:
SSU2HolePunchSession (SSU2Server& server, uint32_t nonce, const boost::asio::ip::udp::endpoint& remoteEndpoint,
std::shared_ptr<const i2p::data::RouterInfo::Address> addr);
void SendHolePunch (const uint8_t * relayResponseBlock, size_t relayResponseBlockLen);
bool ProcessFirstIncomingMessage (uint64_t connID, uint8_t * buf, size_t len) override; // SessionRequest
private:
void SendHolePunch ();
void ScheduleResend ();
private:
int m_NumResends;
std::vector<uint8_t> m_RelayResponseBlock;
boost::asio::deadline_timer m_HolePunchResendTimer;
};
}
}
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2022-2024, The PurpleI2P Project * Copyright (c) 2022-2025, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@@ -15,6 +15,7 @@
#include <set> #include <set>
#include <list> #include <list>
#include <boost/asio.hpp> #include <boost/asio.hpp>
#include "version.h"
#include "Crypto.h" #include "Crypto.h"
#include "RouterInfo.h" #include "RouterInfo.h"
#include "RouterContext.h" #include "RouterContext.h"
@@ -37,6 +38,7 @@ namespace transport
const size_t SSU2_MIN_PACKET_SIZE = 1280; const size_t SSU2_MIN_PACKET_SIZE = 1280;
const int SSU2_HANDSHAKE_RESEND_INTERVAL = 1000; // in milliseconds const int SSU2_HANDSHAKE_RESEND_INTERVAL = 1000; // in milliseconds
const int SSU2_MAX_NUM_RESENDS = 5; const int SSU2_MAX_NUM_RESENDS = 5;
const int SSU2_RESEND_ATTEMPT_MIN_INTERVAL = 3; // in milliseconds
const int SSU2_INCOMPLETE_MESSAGES_CLEANUP_TIMEOUT = 30; // in seconds const int SSU2_INCOMPLETE_MESSAGES_CLEANUP_TIMEOUT = 30; // in seconds
const int SSU2_MAX_NUM_RECEIVED_I2NP_MSGIDS = 5000; // how many msgID we store for duplicates check const int SSU2_MAX_NUM_RECEIVED_I2NP_MSGIDS = 5000; // how many msgID we store for duplicates check
const int SSU2_RECEIVED_I2NP_MSGIDS_CLEANUP_TIMEOUT = 10; // in seconds const int SSU2_RECEIVED_I2NP_MSGIDS_CLEANUP_TIMEOUT = 10; // in seconds
@@ -54,6 +56,7 @@ namespace transport
const int SSU2_MAX_NUM_ACK_RANGES = 32; // to send const int SSU2_MAX_NUM_ACK_RANGES = 32; // to send
const uint8_t SSU2_MAX_NUM_FRAGMENTS = 64; const uint8_t SSU2_MAX_NUM_FRAGMENTS = 64;
const int SSU2_SEND_DATETIME_NUM_PACKETS = 256; const int SSU2_SEND_DATETIME_NUM_PACKETS = 256;
const int SSU2_MIN_RELAY_RESPONSE_RESEND_VERSION = MAKE_VERSION_NUMBER(0, 9, 64); // 0.9.64
// flags // flags
const uint8_t SSU2_FLAG_IMMEDIATE_ACK_REQUESTED = 0x01; const uint8_t SSU2_FLAG_IMMEDIATE_ACK_REQUESTED = 0x01;
@@ -111,8 +114,8 @@ namespace transport
eSSU2SessionStateTerminated, eSSU2SessionStateTerminated,
eSSU2SessionStateFailed, eSSU2SessionStateFailed,
eSSU2SessionStateIntroduced, eSSU2SessionStateIntroduced,
eSSU2SessionStateHolePunch,
eSSU2SessionStatePeerTest, eSSU2SessionStatePeerTest,
eSSU2SessionStatePeerTestReceived, // 5 before 4
eSSU2SessionStateTokenRequestReceived eSSU2SessionStateTokenRequestReceived
}; };
@@ -205,36 +208,40 @@ namespace transport
class SSU2Server; class SSU2Server;
class SSU2Session: public TransportSession, public std::enable_shared_from_this<SSU2Session> class SSU2Session: public TransportSession, public std::enable_shared_from_this<SSU2Session>
{ {
union Header protected:
{
uint64_t ll[2]; union Header
uint8_t buf[16];
struct
{ {
uint64_t connID; uint64_t ll[2];
uint32_t packetNum; uint8_t buf[16];
uint8_t type; struct
uint8_t flags[3]; {
} h; uint64_t connID;
}; uint32_t packetNum;
uint8_t type;
uint8_t flags[3];
} h;
};
struct HandshakePacket private:
{
Header header; struct HandshakePacket
uint8_t headerX[48]; // part1 for SessionConfirmed {
uint8_t payload[SSU2_MAX_PACKET_SIZE*2]; Header header;
size_t payloadSize = 0; uint8_t headerX[48]; // part1 for SessionConfirmed
uint64_t sendTime = 0; // in milliseconds uint8_t payload[SSU2_MAX_PACKET_SIZE*2];
bool isSecondFragment = false; // for SessionConfirmed size_t payloadSize = 0;
}; uint64_t sendTime = 0; // in milliseconds
bool isSecondFragment = false; // for SessionConfirmed
};
typedef std::function<void ()> OnEstablished; typedef std::function<void ()> OnEstablished;
public: public:
SSU2Session (SSU2Server& server, std::shared_ptr<const i2p::data::RouterInfo> in_RemoteRouter = nullptr, SSU2Session (SSU2Server& server, std::shared_ptr<const i2p::data::RouterInfo> in_RemoteRouter = nullptr,
std::shared_ptr<const i2p::data::RouterInfo::Address> addr = nullptr); std::shared_ptr<const i2p::data::RouterInfo::Address> addr = nullptr, bool noise = true);
~SSU2Session (); virtual ~SSU2Session ();
void SetRemoteEndpoint (const boost::asio::ip::udp::endpoint& ep) { m_RemoteEndpoint = ep; }; void SetRemoteEndpoint (const boost::asio::ip::udp::endpoint& ep) { m_RemoteEndpoint = ep; };
const boost::asio::ip::udp::endpoint& GetRemoteEndpoint () const { return m_RemoteEndpoint; }; const boost::asio::ip::udp::endpoint& GetRemoteEndpoint () const { return m_RemoteEndpoint; };
@@ -244,7 +251,7 @@ namespace transport
void SetOnEstablished (OnEstablished e) { m_OnEstablished = e; }; void SetOnEstablished (OnEstablished e) { m_OnEstablished = e; };
OnEstablished GetOnEstablished () const { return m_OnEstablished; }; OnEstablished GetOnEstablished () const { return m_OnEstablished; };
void Connect (); virtual void Connect ();
bool Introduce (std::shared_ptr<SSU2Session> session, uint32_t relayTag); bool Introduce (std::shared_ptr<SSU2Session> session, uint32_t relayTag);
void WaitForIntroduction (); void WaitForIntroduction ();
void SendPeerTest (); // Alice, Data message void SendPeerTest (); // Alice, Data message
@@ -254,29 +261,54 @@ namespace transport
void FlushData (); void FlushData ();
void Done () override; void Done () override;
void SendLocalRouterInfo (bool update) override; void SendLocalRouterInfo (bool update) override;
void SendI2NPMessages (const std::vector<std::shared_ptr<I2NPMessage> >& msgs) override; void SendI2NPMessages (std::list<std::shared_ptr<I2NPMessage> >& msgs) override;
void MoveSendQueue (std::shared_ptr<SSU2Session> other);
uint32_t GetRelayTag () const override { return m_RelayTag; }; uint32_t GetRelayTag () const override { return m_RelayTag; };
size_t Resend (uint64_t ts); // return number or resent packets size_t Resend (uint64_t ts); // return number of resent packets
uint64_t GetLastResendTime () const { return m_LastResendTime; };
bool IsEstablished () const override { return m_State == eSSU2SessionStateEstablished; }; bool IsEstablished () const override { return m_State == eSSU2SessionStateEstablished; };
i2p::data::RouterInfo::SupportedTransports GetTransportType () const override;
uint64_t GetConnID () const { return m_SourceConnID; }; uint64_t GetConnID () const { return m_SourceConnID; };
SSU2SessionState GetState () const { return m_State; }; SSU2SessionState GetState () const { return m_State; };
void SetState (SSU2SessionState state) { m_State = state; }; void SetState (SSU2SessionState state) { m_State = state; };
bool ProcessFirstIncomingMessage (uint64_t connID, uint8_t * buf, size_t len); virtual bool ProcessFirstIncomingMessage (uint64_t connID, uint8_t * buf, size_t len);
bool ProcessSessionCreated (uint8_t * buf, size_t len); bool ProcessSessionCreated (uint8_t * buf, size_t len);
bool ProcessSessionConfirmed (uint8_t * buf, size_t len); bool ProcessSessionConfirmed (uint8_t * buf, size_t len);
bool ProcessRetry (uint8_t * buf, size_t len); bool ProcessRetry (uint8_t * buf, size_t len);
bool ProcessHolePunch (uint8_t * buf, size_t len); bool ProcessHolePunch (uint8_t * buf, size_t len);
bool ProcessPeerTest (uint8_t * buf, size_t len); virtual bool ProcessPeerTest (uint8_t * buf, size_t len);
void ProcessData (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& from); void ProcessData (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& from);
protected:
SSU2Server& GetServer () { return m_Server; }
RouterStatus GetRouterStatus () const;
void SetRouterStatus (RouterStatus status) const;
size_t GetMaxPayloadSize () const { return m_MaxPayloadSize; }
void SetIsDataReceived (bool dataReceived) { m_IsDataReceived = dataReceived; };
uint64_t GetSourceConnID () const { return m_SourceConnID; }
void SetSourceConnID (uint64_t sourceConnID) { m_SourceConnID = sourceConnID; }
uint64_t GetDestConnID () const { return m_DestConnID; }
void SetDestConnID (uint64_t destConnID) { m_DestConnID = destConnID; }
void SetAddress (std::shared_ptr<const i2p::data::RouterInfo::Address> addr) { m_Address = addr; }
void HandlePayload (const uint8_t * buf, size_t len);
size_t CreateAddressBlock (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& ep);
size_t CreatePaddingBlock (uint8_t * buf, size_t len, size_t minSize = 0);
size_t CreatePeerTestBlock (uint8_t * buf, size_t len, uint8_t msg, SSU2PeerTestCode code, const uint8_t * routerHash, const uint8_t * signedData, size_t signedDataLen);
bool ExtractEndpoint (const uint8_t * buf, size_t size, boost::asio::ip::udp::endpoint& ep);
private: private:
void Terminate (); void Terminate ();
void Established (); void Established ();
void ScheduleConnectTimer (); void ScheduleConnectTimer ();
void HandleConnectTimer (const boost::system::error_code& ecode); void HandleConnectTimer (const boost::system::error_code& ecode);
void PostI2NPMessages (std::vector<std::shared_ptr<I2NPMessage> > msgs); void PostI2NPMessages ();
bool SendQueue (); // returns true if ack block was sent bool SendQueue (); // returns true if ack block was sent
bool SendFragmentedMessage (std::shared_ptr<I2NPMessage> msg); bool SendFragmentedMessage (std::shared_ptr<I2NPMessage> msg);
void ResendHandshakePacket (); void ResendHandshakePacket ();
@@ -294,48 +326,41 @@ namespace transport
uint32_t SendData (const uint8_t * buf, size_t len, uint8_t flags = 0); // returns packet num uint32_t SendData (const uint8_t * buf, size_t len, uint8_t flags = 0); // returns packet num
void SendQuickAck (); void SendQuickAck ();
void SendTermination (); void SendTermination ();
void SendHolePunch (uint32_t nonce, const boost::asio::ip::udp::endpoint& ep, const uint8_t * introKey, uint64_t token);
void SendPeerTest (uint8_t msg, const uint8_t * signedData, size_t signedDataLen, const uint8_t * introKey); // PeerTest message
void SendPathResponse (const uint8_t * data, size_t len); void SendPathResponse (const uint8_t * data, size_t len);
void SendPathChallenge (); void SendPathChallenge ();
void HandlePayload (const uint8_t * buf, size_t len);
void HandleDateTime (const uint8_t * buf, size_t len); void HandleDateTime (const uint8_t * buf, size_t len);
void HandleRouterInfo (const uint8_t * buf, size_t len);
void HandleAck (const uint8_t * buf, size_t len); void HandleAck (const uint8_t * buf, size_t len);
void HandleAckRange (uint32_t firstPacketNum, uint32_t lastPacketNum, uint64_t ts); void HandleAckRange (uint32_t firstPacketNum, uint32_t lastPacketNum, uint64_t ts);
void HandleAddress (const uint8_t * buf, size_t len); virtual void HandleAddress (const uint8_t * buf, size_t len);
bool ExtractEndpoint (const uint8_t * buf, size_t size, boost::asio::ip::udp::endpoint& ep);
size_t CreateEndpoint (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& ep); size_t CreateEndpoint (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& ep);
std::shared_ptr<const i2p::data::RouterInfo::Address> FindLocalAddress () const; std::shared_ptr<const i2p::data::RouterInfo::Address> FindLocalAddress () const;
void AdjustMaxPayloadSize (); void AdjustMaxPayloadSize ();
RouterStatus GetRouterStatus () const;
void SetRouterStatus (RouterStatus status) const;
bool GetTestingState () const; bool GetTestingState () const;
void SetTestingState(bool testing) const; void SetTestingState(bool testing) const;
std::shared_ptr<const i2p::data::RouterInfo> ExtractRouterInfo (const uint8_t * buf, size_t size); std::shared_ptr<const i2p::data::RouterInfo> ExtractRouterInfo (const uint8_t * buf, size_t size);
void CreateNonce (uint64_t seqn, uint8_t * nonce);
bool UpdateReceivePacketNum (uint32_t packetNum); // for Ack, returns false if duplicate bool UpdateReceivePacketNum (uint32_t packetNum); // for Ack, returns false if duplicate
void HandleFirstFragment (const uint8_t * buf, size_t len); void HandleFirstFragment (const uint8_t * buf, size_t len);
void HandleFollowOnFragment (const uint8_t * buf, size_t len); void HandleFollowOnFragment (const uint8_t * buf, size_t len);
void HandleRelayRequest (const uint8_t * buf, size_t len); void HandleRelayRequest (const uint8_t * buf, size_t len);
void HandleRelayIntro (const uint8_t * buf, size_t len, int attempts = 0); void HandleRelayIntro (const uint8_t * buf, size_t len, int attempts = 0);
void HandleRelayResponse (const uint8_t * buf, size_t len); void HandleRelayResponse (const uint8_t * buf, size_t len);
void HandlePeerTest (const uint8_t * buf, size_t len); virtual void HandlePeerTest (const uint8_t * buf, size_t len);
void HandleI2NPMsg (std::shared_ptr<I2NPMessage>&& msg); void HandleI2NPMsg (std::shared_ptr<I2NPMessage>&& msg);
size_t CreateAddressBlock (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& ep);
size_t CreateRouterInfoBlock (uint8_t * buf, size_t len, std::shared_ptr<const i2p::data::RouterInfo> r); size_t CreateRouterInfoBlock (uint8_t * buf, size_t len, std::shared_ptr<const i2p::data::RouterInfo> r);
size_t CreateRouterInfoBlock (uint8_t * buf, size_t len, std::shared_ptr<const i2p::data::RouterInfo::Buffer> riBuffer);
size_t CreateAckBlock (uint8_t * buf, size_t len); size_t CreateAckBlock (uint8_t * buf, size_t len);
size_t CreatePaddingBlock (uint8_t * buf, size_t len, size_t minSize = 0);
size_t CreateI2NPBlock (uint8_t * buf, size_t len, std::shared_ptr<I2NPMessage>&& msg); size_t CreateI2NPBlock (uint8_t * buf, size_t len, std::shared_ptr<I2NPMessage>&& msg);
size_t CreateFirstFragmentBlock (uint8_t * buf, size_t len, std::shared_ptr<I2NPMessage> msg); size_t CreateFirstFragmentBlock (uint8_t * buf, size_t len, std::shared_ptr<I2NPMessage> msg);
size_t CreateFollowOnFragmentBlock (uint8_t * buf, size_t len, std::shared_ptr<I2NPMessage> msg, uint8_t& fragmentNum, uint32_t msgID); size_t CreateFollowOnFragmentBlock (uint8_t * buf, size_t len, std::shared_ptr<I2NPMessage> msg, uint8_t& fragmentNum, uint32_t msgID);
size_t CreateRelayIntroBlock (uint8_t * buf, size_t len, const uint8_t * introData, size_t introDataLen); size_t CreateRelayIntroBlock (uint8_t * buf, size_t len, const uint8_t * introData, size_t introDataLen);
size_t CreateRelayResponseBlock (uint8_t * buf, size_t len, SSU2RelayResponseCode code, uint32_t nonce, uint64_t token, bool v4); size_t CreateRelayResponseBlock (uint8_t * buf, size_t len, SSU2RelayResponseCode code, uint32_t nonce, uint64_t token, bool v4);
size_t CreatePeerTestBlock (uint8_t * buf, size_t len, uint8_t msg, SSU2PeerTestCode code, const uint8_t * routerHash, const uint8_t * signedData, size_t signedDataLen);
size_t CreatePeerTestBlock (uint8_t * buf, size_t len, uint32_t nonce); // Alice size_t CreatePeerTestBlock (uint8_t * buf, size_t len, uint32_t nonce); // Alice
size_t CreateTerminationBlock (uint8_t * buf, size_t len); size_t CreateTerminationBlock (uint8_t * buf, size_t len);
private: private:
SSU2Server& m_Server; SSU2Server& m_Server;
@@ -346,6 +371,7 @@ namespace transport
std::shared_ptr<const i2p::data::RouterInfo::Address> m_Address; std::shared_ptr<const i2p::data::RouterInfo::Address> m_Address;
boost::asio::ip::udp::endpoint m_RemoteEndpoint; boost::asio::ip::udp::endpoint m_RemoteEndpoint;
i2p::data::RouterInfo::CompatibleTransports m_RemoteTransports, m_RemotePeerTestTransports; i2p::data::RouterInfo::CompatibleTransports m_RemoteTransports, m_RemotePeerTestTransports;
int m_RemoteVersion;
uint64_t m_DestConnID, m_SourceConnID; uint64_t m_DestConnID, m_SourceConnID;
SSU2SessionState m_State; SSU2SessionState m_State;
uint8_t m_KeyDataSend[64], m_KeyDataReceive[64]; uint8_t m_KeyDataSend[64], m_KeyDataReceive[64];
@@ -353,10 +379,11 @@ namespace transport
std::set<uint32_t> m_OutOfSequencePackets; // packet nums > receive packet num std::set<uint32_t> m_OutOfSequencePackets; // packet nums > receive packet num
std::map<uint32_t, std::shared_ptr<SSU2SentPacket> > m_SentPackets; // packetNum -> packet std::map<uint32_t, std::shared_ptr<SSU2SentPacket> > m_SentPackets; // packetNum -> packet
std::unordered_map<uint32_t, std::shared_ptr<SSU2IncompleteMessage> > m_IncompleteMessages; // msgID -> I2NP std::unordered_map<uint32_t, std::shared_ptr<SSU2IncompleteMessage> > m_IncompleteMessages; // msgID -> I2NP
std::map<uint32_t, std::pair <std::shared_ptr<SSU2Session>, uint64_t > > m_RelaySessions; // nonce->(Alice, timestamp) for Bob or nonce->(Charlie, timestamp) for Alice std::unordered_map<uint32_t, std::pair <std::shared_ptr<SSU2Session>, uint64_t > > m_RelaySessions; // nonce->(Alice, timestamp) for Bob or nonce->(Charlie, timestamp) for Alice
std::map<uint32_t, std::pair <std::shared_ptr<SSU2Session>, uint64_t > > m_PeerTests; // same as for relay sessions
std::list<std::shared_ptr<I2NPMessage> > m_SendQueue; std::list<std::shared_ptr<I2NPMessage> > m_SendQueue;
i2p::I2NPMessagesHandler m_Handler; i2p::I2NPMessagesHandler m_Handler;
std::list<std::shared_ptr<I2NPMessage> > m_IntermediateQueue; // from transports
mutable std::mutex m_IntermediateQueueMutex;
bool m_IsDataReceived; bool m_IsDataReceived;
double m_RTT; double m_RTT;
int m_MsgLocalExpirationTimeout; int m_MsgLocalExpirationTimeout;
@@ -369,14 +396,23 @@ namespace transport
size_t m_MaxPayloadSize; size_t m_MaxPayloadSize;
std::unique_ptr<i2p::data::IdentHash> m_PathChallenge; std::unique_ptr<i2p::data::IdentHash> m_PathChallenge;
std::unordered_map<uint32_t, uint32_t> m_ReceivedI2NPMsgIDs; // msgID -> timestamp in seconds std::unordered_map<uint32_t, uint32_t> m_ReceivedI2NPMsgIDs; // msgID -> timestamp in seconds
uint64_t m_LastResendTime, m_LastResendAttemptTime; // in milliseconds
int m_NumRanges;
uint8_t m_Ranges[SSU2_MAX_NUM_ACK_RANGES*2]; // ranges sent with previous Ack if any
}; };
inline uint64_t CreateHeaderMask (const uint8_t * kh, const uint8_t * nonce) inline uint64_t CreateHeaderMask (const uint8_t * kh, const uint8_t * nonce)
{ {
uint64_t data = 0; uint64_t data = 0;
i2p::crypto::ChaCha20 ((uint8_t *)&data, 8, kh, nonce, (uint8_t *)&data); i2p::crypto::ChaCha20 ((uint8_t *)&data, 8, kh, nonce, (uint8_t *)&data);
return data; return data;
} }
inline void CreateNonce (uint64_t seqn, uint8_t * nonce)
{
memset (nonce, 0, 4);
htole64buf (nonce + 4, seqn);
}
} }
} }

File diff suppressed because it is too large Load Diff

View File

@@ -19,6 +19,7 @@
#include <mutex> #include <mutex>
#include <boost/asio.hpp> #include <boost/asio.hpp>
#include "Base.h" #include "Base.h"
#include "Gzip.h"
#include "I2PEndian.h" #include "I2PEndian.h"
#include "Identity.h" #include "Identity.h"
#include "LeaseSet.h" #include "LeaseSet.h"
@@ -52,22 +53,30 @@ namespace stream
const size_t STREAMING_MTU_RATCHETS = 1812; const size_t STREAMING_MTU_RATCHETS = 1812;
const size_t MAX_PACKET_SIZE = 4096; const size_t MAX_PACKET_SIZE = 4096;
const size_t COMPRESSION_THRESHOLD_SIZE = 66; const size_t COMPRESSION_THRESHOLD_SIZE = 66;
const int MAX_NUM_RESEND_ATTEMPTS = 9; const int MAX_NUM_RESEND_ATTEMPTS = 10;
const int WINDOW_SIZE = 6; // in messages const int INITIAL_WINDOW_SIZE = 10;
const int MIN_WINDOW_SIZE = 1; const int MIN_WINDOW_SIZE = 3;
const int MAX_WINDOW_SIZE = 128; const int MAX_WINDOW_SIZE = 512;
const int WINDOW_SIZE_DROP_FRACTION = 10; // 1/10 const double RTT_EWMA_ALPHA = 0.25;
const double RTT_EWMA_ALPHA = 0.125; const double SLOWRTT_EWMA_ALPHA = 0.05;
const double PREV_SPEED_KEEP_TIME_COEFF = 0.35; // 0.1 - 1 // how long will the window size stay around the previous drop level, less is longer
const int MIN_RTO = 20; // in milliseconds const int MIN_RTO = 20; // in milliseconds
const int INITIAL_RTT = 8000; // in milliseconds const int INITIAL_RTT = 1500; // in milliseconds
const int INITIAL_RTO = 9000; // in milliseconds const int INITIAL_RTO = 9000; // in milliseconds
const int INITIAL_PACING_TIME = 1000 * INITIAL_RTT / INITIAL_WINDOW_SIZE; // in microseconds
const int MIN_SEND_ACK_TIMEOUT = 2; // in milliseconds const int MIN_SEND_ACK_TIMEOUT = 2; // in milliseconds
const int SYN_TIMEOUT = 200; // how long we wait for SYN after follow-on, in milliseconds const int SYN_TIMEOUT = 200; // how long we wait for SYN after follow-on, in milliseconds
const size_t MAX_PENDING_INCOMING_BACKLOG = 128; const size_t MAX_PENDING_INCOMING_BACKLOG = 1024;
const int PENDING_INCOMING_TIMEOUT = 10; // in seconds const int PENDING_INCOMING_TIMEOUT = 10; // in seconds
const int MAX_RECEIVE_TIMEOUT = 20; // in seconds const int MAX_RECEIVE_TIMEOUT = 20; // in seconds
const uint16_t DELAY_CHOKING = 60000; // in milliseconds const uint16_t DELAY_CHOKING = 60000; // in milliseconds
const uint64_t SEND_INTERVAL = 10000; // in microseconds
const uint64_t SEND_INTERVAL_VARIANCE = 2000; // in microseconds
const uint64_t REQUEST_IMMEDIATE_ACK_INTERVAL = 7500; // in milliseconds
const uint64_t REQUEST_IMMEDIATE_ACK_INTERVAL_VARIANCE = 3200; // in milliseconds
const bool LOSS_BASED_CONTROL_ENABLED = 1; // 0/1
const uint64_t STREAMING_DESTINATION_POOLS_CLEANUP_INTERVAL = 646; // in seconds
struct Packet struct Packet
{ {
size_t len, offset; size_t len, offset;
@@ -77,7 +86,7 @@ namespace stream
Packet (): len (0), offset (0), sendTime (0), resent (false) {}; Packet (): len (0), offset (0), sendTime (0), resent (false) {};
uint8_t * GetBuffer () { return buf + offset; }; uint8_t * GetBuffer () { return buf + offset; };
size_t GetLength () const { return len - offset; }; size_t GetLength () const { return len > offset ? len - offset : 0; };
uint32_t GetSendStreamID () const { return bufbe32toh (buf); }; uint32_t GetSendStreamID () const { return bufbe32toh (buf); };
uint32_t GetReceiveStreamID () const { return bufbe32toh (buf + 4); }; uint32_t GetReceiveStreamID () const { return bufbe32toh (buf + 4); };
@@ -167,9 +176,9 @@ namespace stream
{ {
public: public:
Stream (boost::asio::io_service& service, StreamingDestination& local, Stream (boost::asio::io_context& service, StreamingDestination& local,
std::shared_ptr<const i2p::data::LeaseSet> remote, int port = 0); // outgoing std::shared_ptr<const i2p::data::LeaseSet> remote, int port = 0); // outgoing
Stream (boost::asio::io_service& service, StreamingDestination& local); // incoming Stream (boost::asio::io_context& service, StreamingDestination& local); // incoming
~Stream (); ~Stream ();
uint32_t GetSendStreamID () const { return m_SendStreamID; }; uint32_t GetSendStreamID () const { return m_SendStreamID; };
@@ -178,8 +187,10 @@ namespace stream
std::shared_ptr<const i2p::data::IdentityEx> GetRemoteIdentity () const { return m_RemoteIdentity; }; std::shared_ptr<const i2p::data::IdentityEx> GetRemoteIdentity () const { return m_RemoteIdentity; };
bool IsOpen () const { return m_Status == eStreamStatusOpen; }; bool IsOpen () const { return m_Status == eStreamStatusOpen; };
bool IsEstablished () const { return m_SendStreamID; }; bool IsEstablished () const { return m_SendStreamID; };
bool IsIncoming () const { return m_IsIncoming; };
StreamStatus GetStatus () const { return m_Status; }; StreamStatus GetStatus () const { return m_Status; };
StreamingDestination& GetLocalDestination () { return m_LocalDestination; }; StreamingDestination& GetLocalDestination () { return m_LocalDestination; };
void ResetRoutingPath ();
void HandleNextPacket (Packet * packet); void HandleNextPacket (Packet * packet);
void HandlePing (Packet * packet); void HandlePing (Packet * packet);
@@ -192,7 +203,7 @@ namespace stream
size_t ReadSome (uint8_t * buf, size_t len) { return ConcatenatePackets (buf, len); }; size_t ReadSome (uint8_t * buf, size_t len) { return ConcatenatePackets (buf, len); };
size_t Receive (uint8_t * buf, size_t len, int timeout); size_t Receive (uint8_t * buf, size_t len, int timeout);
void AsyncClose() { m_Service.post(std::bind(&Stream::Close, shared_from_this())); }; void AsyncClose() { boost::asio::post(m_Service, std::bind(&Stream::Close, shared_from_this())); };
/** only call close from destination thread, use Stream::AsyncClose for other threads */ /** only call close from destination thread, use Stream::AsyncClose for other threads */
void Close (); void Close ();
@@ -228,40 +239,69 @@ namespace stream
void UpdateCurrentRemoteLease (bool expired = false); void UpdateCurrentRemoteLease (bool expired = false);
template<typename Buffer, typename ReceiveHandler> template<typename Buffer, typename ReceiveHandler>
void HandleReceiveTimer (const boost::system::error_code& ecode, const Buffer& buffer, ReceiveHandler handler, int remainingTimeout); void HandleReceiveTimer (const boost::system::error_code& ecode, Buffer& buffer, ReceiveHandler handler, int remainingTimeout);
void ScheduleSend ();
void HandleSendTimer (const boost::system::error_code& ecode);
void ScheduleResend (); void ScheduleResend ();
void HandleResendTimer (const boost::system::error_code& ecode); void HandleResendTimer (const boost::system::error_code& ecode);
void ResendPacket ();
void ScheduleAck (int timeout); void ScheduleAck (int timeout);
void HandleAckSendTimer (const boost::system::error_code& ecode); void HandleAckSendTimer (const boost::system::error_code& ecode);
void UpdatePacingTime ();
void ProcessWindowDrop ();
void ResetWindowSize ();
void CancelRemoteLeaseChange ();
private: private:
boost::asio::io_service& m_Service; boost::asio::io_context& m_Service;
uint32_t m_SendStreamID, m_RecvStreamID, m_SequenceNumber; uint32_t m_SendStreamID, m_RecvStreamID, m_SequenceNumber;
uint32_t m_DropWindowDelaySequenceNumber;
uint32_t m_TunnelsChangeSequenceNumber; uint32_t m_TunnelsChangeSequenceNumber;
int32_t m_LastReceivedSequenceNumber; int32_t m_LastReceivedSequenceNumber;
int32_t m_PreviousReceivedSequenceNumber;
int32_t m_LastConfirmedReceivedSequenceNumber; // for limit inbound speed
StreamStatus m_Status; StreamStatus m_Status;
bool m_IsIncoming;
bool m_IsAckSendScheduled; bool m_IsAckSendScheduled;
bool m_IsNAcked;
bool m_IsFirstACK;
bool m_IsResendNeeded;
bool m_IsFirstRttSample;
bool m_IsSendTime;
bool m_IsWinDropped;
bool m_IsClientChoked;
bool m_IsTimeOutResend;
bool m_IsImmediateAckRequested;
bool m_IsRemoteLeaseChangeInProgress;
bool m_DoubleWinIncCounter;
StreamingDestination& m_LocalDestination; StreamingDestination& m_LocalDestination;
std::shared_ptr<const i2p::data::IdentityEx> m_RemoteIdentity; std::shared_ptr<const i2p::data::IdentityEx> m_RemoteIdentity;
std::shared_ptr<const i2p::crypto::Verifier> m_TransientVerifier; // in case of offline key std::shared_ptr<const i2p::crypto::Verifier> m_TransientVerifier; // in case of offline key
std::shared_ptr<const i2p::data::LeaseSet> m_RemoteLeaseSet; std::shared_ptr<const i2p::data::LeaseSet> m_RemoteLeaseSet;
std::shared_ptr<i2p::garlic::GarlicRoutingSession> m_RoutingSession; std::shared_ptr<i2p::garlic::GarlicRoutingSession> m_RoutingSession;
std::shared_ptr<const i2p::data::Lease> m_CurrentRemoteLease; std::shared_ptr<const i2p::data::Lease> m_CurrentRemoteLease;
std::shared_ptr<const i2p::data::Lease> m_NextRemoteLease;
std::shared_ptr<i2p::tunnel::OutboundTunnel> m_CurrentOutboundTunnel; std::shared_ptr<i2p::tunnel::OutboundTunnel> m_CurrentOutboundTunnel;
std::queue<Packet *> m_ReceiveQueue; std::queue<Packet *> m_ReceiveQueue;
std::set<Packet *, PacketCmp> m_SavedPackets; std::set<Packet *, PacketCmp> m_SavedPackets;
std::set<Packet *, PacketCmp> m_SentPackets; std::set<Packet *, PacketCmp> m_SentPackets;
boost::asio::deadline_timer m_ReceiveTimer, m_ResendTimer, m_AckSendTimer; std::set<Packet *, PacketCmp> m_NACKedPackets;
boost::asio::deadline_timer m_ReceiveTimer, m_SendTimer, m_ResendTimer, m_AckSendTimer;
size_t m_NumSentBytes, m_NumReceivedBytes; size_t m_NumSentBytes, m_NumReceivedBytes;
uint16_t m_Port; uint16_t m_Port;
SendBufferQueue m_SendBuffer; SendBufferQueue m_SendBuffer;
double m_RTT; double m_RTT, m_SlowRTT, m_SlowRTT2;
int m_WindowSize, m_RTO, m_AckDelay; float m_WindowSize, m_LastWindowDropSize, m_WindowDropTargetSize;
uint64_t m_LastWindowSizeIncreaseTime; int m_WindowIncCounter, m_RTO, m_AckDelay, m_PrevRTTSample, m_WindowSizeTail;
int m_NumResendAttempts; double m_Jitter;
uint64_t m_MinPacingTime, m_PacingTime, m_PacingTimeRem, // microseconds
m_LastSendTime, m_LastACKRecieveTime, m_ACKRecieveInterval, m_RemoteLeaseChangeTime; // milliseconds
uint64_t m_LastACKSendTime, m_PacketACKInterval, m_PacketACKIntervalRem; // for limit inbound speed
int m_NumResendAttempts, m_NumPacketsToSend;
size_t m_MTU; size_t m_MTU;
}; };
@@ -297,6 +337,7 @@ namespace stream
Packet * NewPacket () { return m_PacketsPool.Acquire(); } Packet * NewPacket () { return m_PacketsPool.Acquire(); }
void DeletePacket (Packet * p) { return m_PacketsPool.Release(p); } void DeletePacket (Packet * p) { return m_PacketsPool.Release(p); }
uint32_t GetRandom ();
private: private:
@@ -320,7 +361,8 @@ namespace stream
i2p::util::MemoryPool<Packet> m_PacketsPool; i2p::util::MemoryPool<Packet> m_PacketsPool;
i2p::util::MemoryPool<I2NPMessageBuffer<I2NP_MAX_SHORT_MESSAGE_SIZE> > m_I2NPMsgsPool; i2p::util::MemoryPool<I2NPMessageBuffer<I2NP_MAX_SHORT_MESSAGE_SIZE> > m_I2NPMsgsPool;
uint64_t m_LastCleanupTime; // in seconds
public: public:
i2p::data::GzipInflator m_Inflator; i2p::data::GzipInflator m_Inflator;
@@ -336,7 +378,7 @@ namespace stream
void Stream::AsyncReceive (const Buffer& buffer, ReceiveHandler handler, int timeout) void Stream::AsyncReceive (const Buffer& buffer, ReceiveHandler handler, int timeout)
{ {
auto s = shared_from_this(); auto s = shared_from_this();
m_Service.post ([s, buffer, handler, timeout](void) boost::asio::post (m_Service, [s, buffer, handler, timeout](void)
{ {
if (!s->m_ReceiveQueue.empty () || s->m_Status == eStreamStatusReset) if (!s->m_ReceiveQueue.empty () || s->m_Status == eStreamStatusReset)
s->HandleReceiveTimer (boost::asio::error::make_error_code (boost::asio::error::operation_aborted), buffer, handler, 0); s->HandleReceiveTimer (boost::asio::error::make_error_code (boost::asio::error::operation_aborted), buffer, handler, 0);
@@ -355,9 +397,9 @@ namespace stream
} }
template<typename Buffer, typename ReceiveHandler> template<typename Buffer, typename ReceiveHandler>
void Stream::HandleReceiveTimer (const boost::system::error_code& ecode, const Buffer& buffer, ReceiveHandler handler, int remainingTimeout) void Stream::HandleReceiveTimer (const boost::system::error_code& ecode, Buffer& buffer, ReceiveHandler handler, int remainingTimeout)
{ {
size_t received = ConcatenatePackets (boost::asio::buffer_cast<uint8_t *>(buffer), boost::asio::buffer_size(buffer)); size_t received = ConcatenatePackets ((uint8_t *)buffer.data (), buffer.size ());
if (received > 0) if (received > 0)
handler (boost::system::error_code (), received); handler (boost::system::error_code (), received);
else if (ecode == boost::asio::error::operation_aborted) else if (ecode == boost::asio::error::operation_aborted)

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