Compare commits

..

785 Commits

Author SHA1 Message Date
orignal
7ae40d89c1 updated ChangeLog 2017-02-14 12:39:59 -05:00
orignal
960d9a8534 updated ChangeLog 2017-02-14 12:36:54 -05:00
r4sas
bcc8529bfc update year, maintainer, ulimit -n 4096 default 2017-02-14 20:17:20 +03:00
orignal
d773647a20 2.12.0 2017-02-14 12:11:43 -05:00
orignal
3a5a0837c7 don't show error if stream closed 2017-02-14 12:11:30 -05:00
r4sas
44cfe6af1c upstream pull 2017-02-13 14:17:48 +03:00
r4sas
cf6d445080 winapi - fix style, delete hFont object after drawing (fixes overflow) 2017-02-13 14:12:48 +03:00
orignal
422f8b3660 publish with min interval of 20 seconds 2017-02-12 20:52:46 -05:00
orignal
b097938f47 compressed addressbook request 2017-02-12 15:11:19 -05:00
orignal
c231eff4b1 MTU size of 1488 for ipv6 2017-02-12 10:12:12 -05:00
orignal
1ddc96f965 correct publication verification 2017-02-12 10:08:52 -05:00
orignal
13111c4b42 don't re-schedule resend timer if nothing to resend 2017-02-11 18:18:37 -05:00
Darknet Villain
7c70dbce65 Merge pull request #801 from l-n-s/apparmor
Added AppArmor profile
2017-02-10 18:56:38 +00:00
Darknet Villain
25559f1772 Added AppArmor profile 2017-02-10 13:51:19 -05:00
orignal
c010c83654 signaturetype ofr HTTP and SOCKS proxy 2017-02-10 12:51:55 -05:00
r4sas
2057531e8c Processing transferred data (winapi) 2017-02-09 21:41:52 +03:00
r4sas
277d4d9333 Added status output to main window 2017-02-09 19:45:22 +03:00
orignal
051e642c0c fixed #798. Correct buffer size 2017-02-09 11:05:42 -05:00
orignal
a8778e358d handle HTTP response 2017-02-06 21:39:15 -05:00
orignal
d2edbfd6fa eliminate extra copy 2017-02-06 12:50:54 -05:00
r4sas
d96dbe9365 use _USE_32BIT_TIME_T in win32 build
Add i2pd.exe in .gitignore
2017-02-06 16:18:23 +03:00
orignal
35b5dcdb22 new reseed 2017-02-05 17:08:42 -05:00
orignal
66f3bd186f send http headers in original order 2017-02-04 22:39:54 -05:00
orignal
7ae38a71cc reduced I2NP message size for tunnel gateway 2017-02-03 20:57:04 -05:00
orignal
2ed356be65 Merge pull request #794 from l-n-s/reseed_from_zip
Added protection from clickjacking (#706)
2017-02-03 14:38:25 -05:00
Darknet Villain
99436c1334 Added protection from clickjacking (#706) 2017-02-03 14:13:55 -05:00
orignal
9e57a4ea28 use I2NP message of tunnel data length for TunnelData 2017-02-02 20:45:33 -05:00
orignal
19e5b8cc50 Merge pull request #793 from l-n-s/reseed_from_zip
Added reseed.threshold + refactor
2017-02-02 15:52:25 -05:00
Darknet Villain
33310732a6 Add reseed.threshold option 2017-02-02 15:40:57 -05:00
Darknet Villain
a03bf89190 Refactored code to Reseed module 2017-02-02 15:25:25 -05:00
orignal
1b089ca5e6 Merge pull request #792 from l-n-s/reseed_from_zip
Added option to reseed from ZIP file
2017-02-01 17:34:51 -05:00
Darknet Villain
21e23d5511 Added option to reseed from ZIP file 2017-02-01 17:17:25 -05:00
orignal
8a2c4ab3de don't create identity if presented in netdb already 2017-02-01 15:20:03 -05:00
orignal
9030b3e04c Merge pull request #790 from majestrate/sam-datagrams
udp datagrams and whitespace cleanups in SAM
2017-01-31 12:14:27 -05:00
orignal
0b46495afd i2p.router.net.tunnels.successrate 2017-01-31 12:12:39 -05:00
Jeff Becker
ace16d473f fix 2017-01-31 11:55:57 -05:00
Jeff Becker
925c51420d use correct format 2017-01-31 11:32:50 -05:00
Jeff Becker
764b8ab7a5 wrong param 2017-01-31 11:22:28 -05:00
Jeff Becker
cb6a1bfb1d unindent 2017-01-31 11:20:16 -05:00
Jeff Becker
775b9f30f0 indentation fixes and SAM datagrams 2017-01-31 11:16:55 -05:00
Jeff Becker
76fd1c5c58 udp sockets for sam 2017-01-31 11:06:45 -05:00
orignal
3e2605490f cleanup error messages 2017-01-30 20:36:35 -05:00
orignal
7094588c53 print zlib error codes 2017-01-30 19:56:06 -05:00
orignal
3523047243 #788 ReseedFromZIPFile added 2017-01-30 19:31:01 -05:00
orignal
bdcbaa031d clean transit tunnels endpoints 2017-01-29 19:16:34 -05:00
r4sas
f722b3e9cb Moved reopening of log to SIGUSR1 (16)
Added --pidfile option to init.d script
2017-01-29 17:08:36 +03:00
r4sas
2d46cb072e disabled cleanup() before deleting stream on termination (line 54) 2017-01-28 19:23:14 +03:00
orignal
28cf450bfa show status of shared local destination 2017-01-28 09:18:30 -05:00
orignal
4aa48fb4b6 websocks added 2017-01-27 20:41:15 -05:00
orignal
aa86593702 send correct response if JSON parse error 2017-01-27 14:51:06 -05:00
orignal
faa368cc07 show if router is connected in i2p.router.status 2017-01-26 15:59:33 -05:00
r4sas
a840ed06b7 update android notification icon
update android qt app icon
add mingw build batch and .gitignore
2017-01-26 11:08:25 +03:00
orignal
7196bfd157 keep bandwidth caps if unreachable 2017-01-25 16:37:21 -05:00
orignal
a6785e9143 support of 'X' in RouterInfo 2017-01-25 16:14:01 -05:00
orignal
4d2f26b1cd limit number of precalculated DH pairs 2017-01-25 11:20:15 -05:00
orignal
188987a8ff eliminated deprecated function 2017-01-23 16:22:48 -05:00
orignal
14d74d3230 use openssl 1.1 for androidn build 2017-01-23 15:36:45 -05:00
orignal
bcd6bd6b04 correct handle of AESNI/AVX 2017-01-23 13:22:03 -05:00
orignal
8e4bd7fe4a build with openssl 1.1 2017-01-23 13:14:08 -05:00
orignal
8ab552793a Merge branch 'openssl' of https://github.com/PurpleI2P/i2pd into openssl 2017-01-22 21:22:47 -05:00
orignal
29944f6bf2 cleanup stream upon termination 2017-01-22 21:22:12 -05:00
r4sas
162b60a05b Added script - builder for mingw. 2017-01-22 11:00:58 +03:00
orignal
da50d92d1e Merge branch 'openssl' of https://github.com/PurpleI2P/i2pd into openssl 2017-01-21 19:00:30 -05:00
orignal
a746f5657f calculate shared key in separate thread for incoming connection 2017-01-21 18:59:50 -05:00
orignal
65ccc5bfce send actual local address to webirc 2017-01-20 10:02:16 -05:00
orignal
34939f9381 calculate shared key in separate therad 2017-01-19 22:00:02 -05:00
orignal
298c5f0de2 moved v6 receiver to separate thread 2017-01-19 15:47:01 -05:00
orignal
a6c2b25f6f increased socket buffer 2017-01-19 11:19:09 -05:00
orignal
3a8c90c0d4 acquire shared 2017-01-19 10:20:34 -05:00
orignal
a25ce2296a rollback 2017-01-19 09:58:55 -05:00
orignal
280407a553 new reseed 2017-01-18 20:04:23 -05:00
orignal
32c98e2161 correct packet size 2017-01-18 19:59:25 -05:00
orignal
2cbdb0bc17 fixed shared_ptr error 2017-01-17 12:13:56 -05:00
orignal
4317694c64 memory pool for SSU packets 2017-01-16 22:22:51 -05:00
orignal
9cb8e194b0 use generic container 2017-01-16 15:58:05 -05:00
orignal
dc914b1806 multithreaded memory pool 2017-01-16 15:40:01 -05:00
orignal
c70817b21a Merge pull request #778 from majestrate/datagram-fix-2017-01-13
use std::shared_from_this for DatagramSession
2017-01-16 09:01:25 -05:00
Jeff Becker
77918fd412 use std::shared_from_this 2017-01-16 07:54:56 -05:00
orignal
90d02234c7 Merge pull request #777 from l-n-s/fix_api_netid
Respect for netId option in api.cpp #696
2017-01-13 14:30:37 -05:00
Darknet Villain
b0b1c5af71 Respect for netId option in api.cpp #696 2017-01-13 14:24:53 -05:00
orignal
a8bd87938d honor enableuniquelocal for all server tunnel types 2017-01-13 13:47:51 -05:00
orignal
10d2f0a565 Merge pull request #774 from majestrate/datagram-fix-2017-01-13
try fixing crash in datagram code
2017-01-13 12:23:32 -05:00
Jeff Becker
c68aca4ada try fixing crash in datagram code 2017-01-13 11:54:29 -05:00
orignal
f46d96c4c6 renamed maptolooback to enableuniquelocal 2017-01-12 16:17:11 -05:00
orignal
e7b1ded486 correct behaviour of IsAcceptorSet 2017-01-12 14:19:57 -05:00
orignal
719de94821 acquire unique_ptr 2017-01-11 19:45:04 -05:00
orignal
7ea0249e6e use memory poll for streaming 2017-01-10 21:31:52 -05:00
orignal
feab95ce4b initial commit for memory pool 2017-01-10 16:14:18 -05:00
orignal
ca6f755634 http.enabled 2017-01-10 15:08:01 -05:00
orignal
70b30f7849 Merge pull request #772 from majestrate/ssu-revert
don't use heap allocated buffers in ssu and fill uninitialized memory
2017-01-08 15:28:23 -05:00
Jeff Becker
01ab027615 don't use heap allocated buffers in ssu 2017-01-08 11:10:23 -05:00
orignal
11f5db871f don't copy private keys 2017-01-08 09:07:54 -05:00
orignal
d83fc3181b EdDSA keys compatible with Java 2017-01-07 21:20:09 -05:00
orignal
b4657a0d05 Merge pull request #771 from majestrate/websocks
merge recent features and bugfixes
2017-01-07 14:13:42 -05:00
Jeff Becker
a5d6820453 fix 2017-01-07 13:55:17 -05:00
Jeff Becker
7b16aa6050 revert 2017-01-07 08:40:02 -05:00
Jeff Becker
c5d3c0c6f8 * add websocks
* enable socks, websocks and httpproxy as client tunnels

* remove old websocks config
2017-01-07 08:32:50 -05:00
Jeff Becker
43c1a87c48 Merge remote-tracking branch 'purple/openssl' into websocks 2017-01-07 07:39:04 -05:00
Mikal Villa
3755002381 Moving dockerfile to trigger autobuild of docker images. 2017-01-07 02:56:45 +01:00
orignal
dba38408c9 Merge pull request #770 from PurpleI2P/docker
Improved docker image
2017-01-06 20:37:04 -05:00
Mikal Villa
5b2bc23d03 Adding readme 2017-01-07 02:30:17 +01:00
Mikal Villa
a4cfdcb5c4 Improved and minimalized docker image 2017-01-07 02:17:02 +01:00
Mikal Villa
b6097160f1 Adding default port to config docs 2017-01-06 21:47:55 +01:00
orignal
fde1c08945 change country code to A1 2017-01-06 14:02:54 -05:00
orignal
417eb56a9b rollback to 2.6.0 2017-01-06 09:59:22 -05:00
orignal
0b28812f7e rollback 2017-01-05 17:37:39 -05:00
orignal
5ad25376bb send all outgoing messages in one buffer 2017-01-05 16:03:53 -05:00
orignal
11231abe8a fixed warning 2017-01-05 13:31:23 -05:00
orignal
c577706415 Merge pull request #766 from majestrate/i2pcontrol-fixes
fix i2pcontrol bugs
2017-01-05 12:32:51 -05:00
Jeff Becker
f1eea6a0bf fix i2pcontrol bugs 2017-01-05 11:57:54 -05:00
orignal
8ce55f90d3 more i2pcontrol options 2017-01-05 10:30:27 -05:00
orignal
723f35ec5a fixed crash 2017-01-04 20:55:18 -05:00
orignal
025d9d3276 fixed #765 2017-01-04 19:12:43 -05:00
orignal
4f0c1d11eb 16 bytes alignment for extra buffer 2017-01-04 17:25:30 -05:00
orignal
1aae921ce7 allocated bigger buffer for remaining data 2017-01-03 13:22:42 -05:00
orignal
2e1c508bc4 allocated bigger buffer for remaining data 2017-01-03 13:19:35 -05:00
orignal
cea6ea4344 correct receive stats 2017-01-03 12:29:36 -05:00
orignal
57310fdbd6 reduced memory footprint 2017-01-03 11:52:28 -05:00
orignal
62ca6212ce don't store SSU-specific data for NTCP address 2017-01-02 16:36:59 -05:00
orignal
a739580d3f Merge pull request #763 from hypnosis-i2p/openssl
updated icons, added README.md to i2pd_qt folder, updated android sdk version
2017-01-02 14:35:31 -05:00
hypnosis-i2p
5203565175 updated icons 2017-01-03 01:14:44 +08:00
hypnosis-i2p
c91f6db68a updated to newer android sdk 2017-01-02 23:38:15 +08:00
hypnosis-i2p
b776b85fc3 Update README.md 2017-01-02 23:38:15 +08:00
hypnosis-i2p
b35e5f1582 Create README.md 2017-01-02 23:38:15 +08:00
orignal
7d5a929b5e #761 info instead error 2017-01-02 09:03:12 -05:00
orignal
c2e7bc13a6 last sample should have more relevance for latency 2017-01-01 14:29:39 -05:00
orignal
97818c6f32 Merge pull request #760 from majestrate/fix-http-auth
Fix http auth when long password used and add peer count on transports page
2017-01-01 09:45:13 -05:00
Jeff Becker
a8973f5463 add peer count to transports 2017-01-01 08:58:21 -05:00
Jeff Becker
75d790137d don't use sizeof 2017-01-01 08:54:11 -05:00
Jeff Becker
7ef6c72fc0 fix http auth fail when auth too long 2017-01-01 08:53:15 -05:00
Jeff Becker
c5f8e2249e Merge remote-tracking branch 'purple/openssl' into websocks 2016-12-31 17:08:49 -05:00
orignal
585a6c29d4 add relaytag after session established 2016-12-31 13:52:26 -05:00
orignal
6b6df15dd9 eliminate ban list overhead 2016-12-31 10:51:42 -05:00
orignal
f4de68cb22 avoid exception if not connected 2016-12-31 10:50:48 -05:00
Jeff Becker
86d5cbc355 Merge branch 'master' into websocks 2016-12-31 10:42:57 -05:00
Jeff Becker
88f9b69e2a Merge remote-tracking branch 'purple/openssl' 2016-12-31 08:42:42 -05:00
orignal
d77c782f69 removed IdentHash from RoutingProfile 2016-12-30 20:59:18 -05:00
orignal
c115131ed2 removed IdentHash from RoutingProfile 2016-12-30 20:09:41 -05:00
orignal
178dedf78c store relay session directly 2016-12-30 17:53:54 -05:00
Jeff Becker
b0c64afc6e Merge remote-tracking branch 'purple/openssl' 2016-12-30 04:47:01 -05:00
Jeff Becker
be0c1c0912 Merge remote-tracking branch 'purple/openssl' 2016-12-30 04:46:55 -05:00
orignal
2e8fa88fcb fixed memory leak 2016-12-29 22:06:33 -05:00
orignal
b1b5904852 show SOCKS proxy as client tunnel 2016-12-27 22:45:51 -05:00
orignal
08f029850f Merge pull request #754 from majestrate/ratelimit-datagram-ls
dont re-request LS
2016-12-26 20:05:57 -05:00
Jeff
f3d4077142 dont re-request LS 2016-12-26 18:47:47 -05:00
orignal
59dd479a6d check if address not found 2016-12-26 17:19:54 -05:00
Jeff Becker
76d9f1ea37 * make loopback address mapping configurable
* add loopback address mapping to udp server tunnel
2016-12-25 08:56:47 -05:00
Jeff Becker
858b497199 prevent overflow 2016-12-25 08:18:23 -05:00
orignal
cee9f1df95 Merge pull request #750 from majestrate/tunnelgateway-leak
Fix Tunnel Gateway Leak
2016-12-24 18:01:10 -05:00
Jeff Becker
5bc2001ce3 Fix Tunnel Gateway Leak 2016-12-24 17:19:07 -05:00
orignal
652226dbf0 allow multiple acceptors 2016-12-24 16:34:18 -05:00
Jeff Becker
4688e6d534 fix segfault 2016-12-24 16:31:28 -05:00
Jeff Becker
1b0fc180c4 Fix Tunnel Gateway Leak 2016-12-24 16:05:44 -05:00
Jeff Becker
2524972807 don't use stack allocated buffers in SSU 2016-12-24 12:04:39 -05:00
orignal
8f51dc2c22 reload acceptor with correct stream 2016-12-24 09:55:59 -05:00
orignal
b363b50320 multiple acceptors 2016-12-24 08:53:35 -05:00
orignal
88a48a5c79 implement AcceptOnce for multiple acceptors 2016-12-23 10:09:40 -05:00
Jeff Becker
7be951b962 fix last commit, it was broken 2016-12-23 07:38:41 -05:00
Jeff Becker
3dcc4e6bc1 i2ptunnel fixes 2016-12-23 07:32:43 -05:00
orignal
573ee0b584 fixed typo 2016-12-22 20:34:06 -05:00
orignal
213629ef52 drop highest bit for token 2016-12-22 20:30:50 -05:00
orignal
27e1579e4c rollback 2016-12-22 19:38:17 -05:00
orignal
f2c401b6c0 fixed some memory leak 2016-12-22 15:00:40 -05:00
orignal
442c63d7a4 #746. initialize io_service after daeminization 2016-12-22 13:32:06 -05:00
orignal
5babfb0f1e fixed #724 2016-12-22 10:52:26 -05:00
orignal
0ad3078524 open log stream in log thread 2016-12-22 10:08:35 -05:00
Jeff Becker
f765c25020 Merge remote-tracking branch 'purple/openssl' 2016-12-22 09:33:02 -05:00
orignal
4145251afd new reseed 2016-12-21 21:29:46 -05:00
orignal
88c3532162 removed ssl ceritifcates 2016-12-21 14:54:48 -05:00
orignal
84b3ad3221 removed non-used ceritificates 2016-12-21 14:52:17 -05:00
orignal
e699d3d02d SNI support 2016-12-21 07:41:18 -05:00
orignal
9da984b866 use ElGamalEncrypt 2016-12-20 14:10:14 -05:00
orignal
ffaabe8674 update ChangeLog 2016-12-18 18:07:06 -05:00
r4sas
0233ab4deb added info, modifyed timestamp 2016-12-19 00:31:29 +03:00
orignal
c9dc010c0b 2.11.0 2016-12-18 16:02:19 -05:00
Jeff Becker
557696b1d8 Merge remote-tracking branch 'purple/openssl' 2016-12-18 13:31:02 -05:00
orignal
9fefbb0c4a Merge pull request #742 from majestrate/sam-multiaccept
Multiple stream acceptors with SAM
2016-12-18 13:16:14 -05:00
Jeff Becker
eb9ea97e21 don't crash 2016-12-18 13:01:28 -05:00
Jeff Becker
673b7a95b7 fix sam crash on exit and datagram crash with no outbound tunnel 2016-12-18 12:57:49 -05:00
Jeff Becker
d5f27ecb0e fix termination crash 2016-12-18 12:57:39 -05:00
Jeff Becker
8f8b928cc4 enable multiple acceptors in sam (initial) 2016-12-18 12:57:31 -05:00
Jeff Becker
965896b932 fix sam crash on exit and datagram crash with no outbound tunnel 2016-12-18 12:56:34 -05:00
Jeff Becker
042adb5e34 fix termination crash 2016-12-18 12:28:32 -05:00
Jeff Becker
67927bd8f4 enable multiple acceptors in sam (initial) 2016-12-18 11:49:50 -05:00
orignal
259a63e612 fixed session termination crash 2016-12-18 10:11:40 -05:00
Jeff Becker
adcf2158bf Merge remote-tracking branch 'purple/openssl' 2016-12-18 10:09:45 -05:00
orignal
05c914156a fixed session termination crash 2016-12-18 09:40:52 -05:00
r4sas
f69884d573 AVX disabled for debuild by patch 2016-12-18 16:27:14 +03:00
Jeff Becker
d097554f7d Merge remote-tracking branch 'purple/openssl' 2016-12-17 15:16:58 -05:00
Jeff Becker
1e2fd57c4c Merge remote-tracking branch 'purple/openssl' into websocks 2016-12-17 15:16:50 -05:00
r4sas
8b8007695c don't close streams after 1 hour 2016-12-17 22:49:51 +03:00
Jeff Becker
68f3c877ee Merge branch 'master' into websocks 2016-12-17 08:54:56 -05:00
orignal
ae442ee015 Merge pull request #739 from majestrate/udp-tunnel-crash-fix
Udp tunnel crash fix
2016-12-17 07:58:45 -05:00
Jeff
99b5f1b7b8 remove pedantic log entry 2016-12-17 07:36:59 -05:00
Jeff
8071df0e68 don't crash on os x when no lease set found for udp tunnel 2016-12-17 07:36:47 -05:00
Jeff Becker
88d1aab7a3 Merge branch 'master' of github.com:majestrate/i2pd 2016-12-17 07:35:24 -05:00
Jeff
08001ba373 remove pedantic log entry 2016-12-17 06:37:34 -05:00
Jeff
ebc24cee55 Merge remote-tracking branch 'origin/openssl' 2016-12-17 06:36:26 -05:00
Jeff
ae3bb30d8a don't crash on os x when no lease set found for udp tunnel 2016-12-17 06:35:38 -05:00
orignal
63d6b23344 use correct encryption key for ElGamal 2016-12-16 21:23:04 -05:00
Jeff
c009e6bd04 Merge remote-tracking branch 'origin/openssl' 2016-12-15 18:24:45 -05:00
orignal
38d85a49e7 use AVX instructions for XOR in AES-CBC if applicable 2016-12-15 14:42:26 -05:00
orignal
0edc149ecc fixed typo 2016-12-15 13:36:52 -05:00
orignal
10d6cd9896 use token for commands 2016-12-15 13:10:12 -05:00
orignal
6913da7efa fixed AVX crash for mingw build 2016-12-15 10:43:48 -05:00
orignal
34df1b1646 add AVX to clobber list 2016-12-15 07:21:34 -05:00
orignal
992603496e don't copy address 2016-12-14 13:54:16 -05:00
orignal
b9552c42f1 don't overwrite whole lease 2016-12-14 11:32:20 -05:00
orignal
37e4dfc5d5 cleanup from some overhead 2016-12-14 10:59:36 -05:00
orignal
15b7284a8f AVX support for Windows 2016-12-14 09:48:05 -05:00
orignal
b57a62fece static and AVX support for OSX 2016-12-14 09:35:15 -05:00
orignal
9c7de5ad03 avoid sending RST instead FIN 2016-12-13 14:54:48 -05:00
orignal
c065fae422 store remote IdentHash only 2016-12-13 12:45:18 -05:00
orignal
cfde1f8c27 rollback 2016-12-13 12:44:05 -05:00
orignal
c45f72a63e make sure all outstading data got sent before closing socket 2016-12-13 11:01:13 -05:00
orignal
e1d9eca7bd 0.9.28 2016-12-13 09:49:42 -05:00
Jeff Becker
573e5eb5bd fix typo 2016-12-13 09:10:39 -05:00
Jeff Becker
d9090486e3 Merge remote-tracking branch 'origin/udptunnel-fix' 2016-12-12 19:20:24 -05:00
Jeff
b4e7a91645 be less picky about next lease set 2016-12-12 19:16:02 -05:00
Jeff
92dd68fca1 fix 2016-12-12 18:54:56 -05:00
Jeff
82e955ec02 fix 2016-12-12 18:54:31 -05:00
orignal
2e66c4c9f5 Merge pull request #738 from majestrate/udptunnel-fix
make udp tunnel utilize GarlicRoutingPath correctly
2016-12-12 18:16:01 -05:00
Jeff Becker
0c6ee5e139 Merge remote-tracking branch 'origin/udptunnel-fix' 2016-12-12 15:53:48 -05:00
Jeff
9a19b5994b fix 2016-12-12 15:50:36 -05:00
Jeff
920586f56c Merge remote-tracking branch 'origin/openssl' into udptunnel-fix 2016-12-12 14:51:01 -05:00
Jeff
919aa2895a request lease set 2016-12-12 14:50:38 -05:00
Jeff Becker
75690598e3 try fixing datagram 2016-12-12 14:50:31 -05:00
orignal
ac2caf2787 make sure all incoming data gets sent before closing a socket 2016-12-12 14:45:37 -05:00
Jeff
5640c96fd5 request lease set 2016-12-12 14:39:05 -05:00
Jeff Becker
0396c4a4de try fixing datagram 2016-12-12 13:40:24 -05:00
Jeff Becker
f061fe581a Merge remote-tracking branch 'purple/openssl' 2016-12-12 10:58:20 -05:00
orignal
5405876d84 temporary exlude mamoth's shit from reseeds 2016-12-11 20:38:19 -05:00
orignal
4b9de0777b queue up LeaseSet requests 2016-12-11 14:17:09 -05:00
Jeff Becker
a59e073536 consmetic fixes 2016-12-11 12:22:23 -05:00
orignal
67492bf024 send v4 address for peer test 2016-12-11 09:53:43 -05:00
Jeff Becker
77c83c4f42 Merge branch 'openssl' of https://github.com/PurpleI2P/i2pd into websocks 2016-12-11 09:36:51 -05:00
orignal
259baa0e84 use vzeroall to complete AVX mode 2016-12-10 20:41:42 -05:00
orignal
dca48c7eec use AVX for HMAC 2016-12-09 15:46:21 -05:00
Jeff Becker
0d83a34cfd add initial WebSOCKS implementation 2016-12-09 15:36:38 -05:00
orignal
7386b0a523 fixed android build 2016-12-09 13:42:00 -05:00
Jeff Becker
eda13f9023 Merge remote-tracking branch 'purple/openssl' 2016-12-09 11:42:31 -05:00
orignal
d0e9fe1e3e Merge pull request #734 from majestrate/fix-732
fixe issue 732
2016-12-09 09:59:18 -05:00
orignal
2b7bab04dd add BloomFilter to QT android 2016-12-09 09:50:42 -05:00
orignal
ad5f890a1e Merge pull request #733 from majestrate/bloom-filter
add simple bloomfilter implementation
2016-12-09 09:41:09 -05:00
Jeff Becker
fa191e2928 fixes issue 732 and wave hi to ISPG 2016-12-09 09:27:19 -05:00
Jeff Becker
6d8a23ec16 tabify 2016-12-09 09:10:08 -05:00
Jeff Becker
12371650f9 tabify 2016-12-09 09:09:35 -05:00
Jeff Becker
79e1d54e4c implement simple bloom filter 2016-12-09 09:08:03 -05:00
orignal
447f5f69c9 use AVX for DHT 2016-12-08 15:23:40 -05:00
orignal
e08a26d015 AVX instructions support 2016-12-08 12:59:19 -05:00
Jeff Becker
975265b0af more 2016-12-07 11:52:20 -05:00
Jeff Becker
4d5e9c52b2 Use eddsa-sh512-ed25519 by default 2016-12-07 09:38:19 -05:00
Jeff Becker
d1b154c285 Merge remote-tracking branch 'purple/openssl' 2016-12-07 09:34:33 -05:00
orignal
381f6b184e clean up incomplete messages 2016-12-06 16:23:52 -05:00
orignal
59681398cb don't store lookup replies anymore 2016-12-05 18:39:01 -05:00
orignal
adf887a06b request destination if we are not closest 2016-12-05 16:36:51 -05:00
orignal
42f70cd55d request destination after frist lookup 2016-12-05 15:45:04 -05:00
r4sas
3704a4ff47 2.10.2 2016-12-05 00:31:41 +03:00
MXPLRS | Kirill
5b8d637f6a 2.10.2 2016-12-05 00:21:18 +03:00
orignal
436621f79f 2.10.2 2016-12-04 14:38:57 -05:00
Jeff Becker
f1acd122bc Merge remote-tracking branch 'purple/openssl' 2016-12-03 08:10:00 -05:00
orignal
739b6645f8 eliminate bad_function_call exception 2016-12-02 16:10:49 -05:00
orignal
7a7ae4cc83 select ipv4 peers for peer test 2016-12-02 11:17:22 -05:00
orignal
db83cbe58f handle read_some errors 2016-12-01 22:14:43 -05:00
orignal
87228429d6 handle receive_from errors 2016-12-01 19:24:15 -05:00
orignal
2651723b50 fixed termination crash 2016-12-01 19:23:55 -05:00
orignal
b8a01d2ff1 rollback 2016-12-01 15:03:54 -05:00
orignal
5c20751937 give priority to ipv6 2016-12-01 14:06:23 -05:00
MXPLRS | Kirill
06b0a50462 static libminiupnpc 2016-12-01 20:17:28 +03:00
orignal
0d589895f6 print time difference with one in timestamp message 2016-12-01 10:51:01 -05:00
orignal
230c2aaf26 reopen UDP socket in case of error 2016-11-30 21:14:10 -05:00
orignal
1d8807a6ba handle async_receive_from errors 2016-11-30 14:51:26 -05:00
orignal
81978b214c correct NTCP sessions termination 2016-11-30 09:24:49 -05:00
Jeff Becker
8704234669 Merge remote-tracking branch 'purple/openssl' 2016-11-30 04:29:16 -05:00
orignal
5699b7bae5 5 seconds connection timeout for NTCP 2016-11-29 14:12:44 -05:00
orignal
e726d216bb cleanup tags on stop 2016-11-28 22:47:37 -05:00
orignal
3480824290 correct leaseset requests cleanup 2016-11-28 14:37:17 -05:00
orignal
c8b935151a fixed tremination crash 2016-11-28 13:47:10 -05:00
orignal
5e5aefa290 cleanup leaseset requests on stop 2016-11-27 10:14:54 -05:00
orignal
0e14b54b6d break circular reference 2016-11-25 22:36:35 -05:00
orignal
c6ddae2d8e excluded obsolete boost dependancies 2016-11-25 13:45:41 -05:00
Jeff Becker
bc0aed186e Merge remote-tracking branch 'purple/openssl' 2016-11-25 10:46:28 -05:00
orignal
d092b21da7 assume ElGamal data size as 222 bytes 2016-11-24 16:02:14 -05:00
orignal
a8061003dd Merge pull request #725 from majestrate/fix-722
don't add multiple router addresses when specifying ifname4/6
2016-11-24 14:05:30 -05:00
Jeff Becker
50f0099645 don't add multiple router addresses 2016-11-24 13:56:37 -05:00
orignal
c270687223 Merge pull request #723 from majestrate/fix-722
add ifname4 and ifname6 options
2016-11-24 10:43:39 -05:00
Jeff Becker
a92652f4ad add ifname4 and ifname6 options 2016-11-24 10:11:46 -05:00
Jeff Becker
9ba961fa72 Merge remote-tracking branch 'purple/openssl' 2016-11-24 08:07:32 -05:00
orignal
006e4526e8 fixed memory leak 2016-11-23 16:41:27 -05:00
orignal
55dbbb3546 fixed memory leak 2016-11-23 16:30:36 -05:00
orignal
e4fe18e435 Merge pull request #718 from l-n-s/my_fixes
Add possibility to reseed from HTTPS URL
2016-11-23 08:58:28 -05:00
Darknet Villain
cea38549da Merge remote-tracking branch 'upstream/openssl' into my_fixes 2016-11-23 07:45:06 -05:00
Darknet Villain
0487e730ba Add possibility to reseed from HTTPS URL 2016-11-23 07:42:38 -05:00
orignal
8fdd7205d7 check if routing session got detached 2016-11-22 15:20:48 -05:00
orignal
1d8d71cfb6 16-bytes alignment for IV for AES-CBC 2016-11-21 21:13:13 -05:00
orignal
10bd017e57 16-byte alignment for received I2NP message 2016-11-21 19:45:29 -05:00
Jeff Becker
f36a9c4409 Merge remote-tracking branch 'purple/openssl' 2016-11-21 06:43:23 -05:00
atnaguzin
70f39eb959 Added new logo to webconsole 2016-11-21 00:58:38 +03:00
atnaguzin
3a3b0cc847 New logo 2016-11-21 00:27:39 +03:00
Jeff Becker
01da9e3ca2 fix outproxy 2016-11-20 12:13:11 -05:00
Jeff Becker
f168e4586c undo pedantic whitespace 2016-11-20 09:32:28 -05:00
Jeff Becker
03ff390685 undo pedantic whitespaces 2016-11-20 09:31:33 -05:00
Jeff Becker
2a77486567 tabify 2016-11-20 09:30:46 -05:00
Jeff Becker
32a5950aad Merge remote-tracking branch 'purple/openssl' 2016-11-20 09:28:11 -05:00
Jeff Becker
f1370189b6 initial outproxy support for http proxy 2016-11-20 09:25:56 -05:00
orignal
65d721285b fixed build error for some compilers 2016-11-20 08:33:33 -05:00
orignal
565f844b7f correct termination of pending leaseset 2016-11-19 17:24:38 -05:00
orignal
248992b27b temporarty fix crash 2016-11-19 14:28:58 -05:00
Jeff Becker
3125e05b49 Merge remote-tracking branch 'purple/openssl' 2016-11-19 07:19:11 -05:00
orignal
bdd6037726 use std::map for unconfirmed tags 2016-11-18 14:50:29 -05:00
orignal
9d292bb6a4 fixed potential race condition 2016-11-18 11:16:55 -05:00
orignal
12b9b49902 fixed infinite loop bug 2016-11-18 10:27:49 -05:00
orignal
93b8bd7f02 set high bandwidth together with extra badnwidth 2016-11-18 09:27:40 -05:00
orignal
cd8169c0a5 reopen log upon daemon start 2016-11-17 22:44:02 -05:00
orignal
b4a9d4df8c fixed crash in daemon mode 2016-11-17 22:11:34 -05:00
orignal
d62525abb6 insert I2CP session with correct sessionid 2016-11-17 19:16:38 -05:00
orignal
a4988fd7cb insert I2CP session with correct sessionid 2016-11-17 19:14:25 -05:00
orignal
d91691c344 write to log through the separate thread 2016-11-17 15:46:28 -05:00
orignal
164d3566e3 fixed linker error 2016-11-17 15:00:30 -05:00
orignal
058120d001 show I2CP local destinations 2016-11-17 13:10:29 -05:00
Jeff Becker
59f292333f use correct ports 2016-11-17 11:42:23 -05:00
Jeff Becker
b7a2c11e81 use shared_ptr instead 2016-11-17 11:37:48 -05:00
Jeff Becker
3d07ddfba5 read more than 1 udp packet 2016-11-17 11:13:40 -05:00
Jeff Becker
9286e4794b add logging 2016-11-17 11:10:42 -05:00
Jeff Becker
81276cb7f5 unbreak (maybe?) 2016-11-17 10:43:27 -05:00
Jeff Becker
e270f90f8d try fixing udp tunnel (probably broken) 2016-11-17 10:36:27 -05:00
Jeff Becker
b1fdfec18c Merge remote-tracking branch 'purple/openssl' 2016-11-17 09:25:48 -05:00
orignal
1dfa09cda9 queue up multiple LeaseSet requests 2016-11-16 22:28:13 -05:00
atnaguzin
913438e3ff addresshelper message changed to "Proxy info" 2016-11-17 06:04:29 +03:00
orignal
1aa939ae73 correct tigger for 0-hops LeaseSet update 2016-11-16 19:32:45 -05:00
orignal
a914608264 clean up non received DeliveryStatus messages 2016-11-16 14:43:29 -05:00
orignal
5d0852c1e2 fixed memory leak 2016-11-16 12:10:13 -05:00
orignal
e0e50faa47 publish 0-hops leaseset 2016-11-16 10:59:11 -05:00
orignal
f6721a2ced fixed startup crash 2016-11-15 17:45:37 -05:00
Jeff Becker
e384ec32b8 unbreak i2lua build 2016-11-15 15:40:09 -05:00
Jeff Becker
d93361939c Merge branch 'low-latency-merge' 2016-11-15 15:14:52 -05:00
Jeff Becker
644c0e3d33 Merge remote-tracking branch 'purple/openssl' 2016-11-15 15:12:09 -05:00
orignal
b1333b7d99 Merge pull request #709 from majestrate/low-latency-merge
implement latency control option
2016-11-15 15:08:09 -05:00
Jeff Becker
673a2acade Merge remote-tracking branch 'purple/openssl' into low-latency-merge 2016-11-15 14:45:16 -05:00
Jeff Becker
752e74d33c show latency of tunnels in web ui 2016-11-15 14:42:18 -05:00
orignal
6bacf94a62 handle all loopback messages 2016-11-15 14:11:55 -05:00
orignal
336cd60920 don't insert same floodfill twice 2016-11-15 12:17:21 -05:00
Jeff Becker
76c9b66db4 don't blow up 2016-11-15 11:31:15 -05:00
Jeff Becker
0c5ca28a14 fall back on regular tunnel algorithm 2016-11-15 11:27:00 -05:00
Jeff Becker
db63bb4495 make it compile for real 2016-11-15 11:18:12 -05:00
Jeff Becker
34afb54c21 make it compile 2016-11-15 11:16:32 -05:00
Jeff Becker
69888e148e use correct latency computation 2016-11-15 11:15:48 -05:00
Jeff Becker
98a55c0613 make it compile 2016-11-15 10:48:33 -05:00
Jeff Becker
5425e9aee3 select tunnels correctly 2016-11-15 10:46:58 -05:00
Jeff Becker
7fef5f5654 when selecting tunnels if we can't find a low latency tunnel fall back to regular selection algorithm 2016-11-15 10:37:58 -05:00
Jeff Becker
fc94e846a6 add latency requirement option 2016-11-15 10:20:09 -05:00
orignal
7d7bbf15bf use DSA for http and socks proxy by defualt 2016-11-15 10:10:13 -05:00
Jeff Becker
8a545b98ec Merge remote-tracking branch 'purple/openssl' 2016-11-14 16:26:37 -05:00
orignal
ecdb60b44e cleanup netdb after failed reseed 2016-11-14 16:23:42 -05:00
orignal
2eea85b786 increase reseed expiration time to 81 hours 2016-11-14 15:04:40 -05:00
MXPLRS | Kirill
87fd0e6f29 recommit fixed ASCII art 2016-11-14 22:38:35 +03:00
orignal
ea191afd9d fixed build error 2016-11-14 13:48:34 -05:00
MXPLRS | Kirill
89b624308e added ASCII art 2016-11-14 21:40:03 +03:00
orignal
facdf0ca9c Merge pull request #708 from majestrate/reseed-from-floodfill
add reseed from floodfill option
2016-11-14 13:31:51 -05:00
orignal
98484d54c0 check for outdated routers in reseed 2016-11-14 13:13:57 -05:00
Jeff Becker
ea31ca5ee8 add reseed from floodfill option 2016-11-14 12:09:07 -05:00
Jeff Becker
6b5b9b3d62 add reseed from floodfill option 2016-11-14 12:05:44 -05:00
Jeff Becker
975dab6d1d add hacking.md for notes on internal structure 2016-11-14 08:38:25 -05:00
Darknet Villain
eaa7adc88c Update usage.md 2016-11-13 23:04:41 +00:00
orignal
f76b014a52 re-run PeerTest 2016-11-13 09:14:05 -05:00
atnaguzin
8676a1b4ef update changelog, added leaseset list to client/server tunnel pages 2016-11-12 17:49:16 +03:00
orignal
e1eaa2097e Merge branch 'openssl' of https://github.com/PurpleI2P/i2pd into openssl 2016-11-11 12:45:06 -05:00
orignal
6f2357c695 fixed openssl 1.1 crash 2016-11-11 12:44:44 -05:00
Darknet Villain
91427264c3 Fix link to configuration 2016-11-11 10:01:38 +00:00
Darknet Villain
74aa961561 Fix RTD: Use 4 spaces, not tabs in docs 2016-11-11 09:42:18 +00:00
orignal
aa47e11471 fixed race condition 2016-11-10 21:44:40 -05:00
orignal
89d69a5d5a rollback due the race condition 2016-11-10 18:38:29 -05:00
orignal
3bbe1e9c0c excluded deprecated reseed 2016-11-10 14:59:21 -05:00
orignal
6377631ae7 OpenSSL 1.1 for EVP_PKEY 2016-11-10 12:51:39 -05:00
orignal
3562ac1438 Merge pull request #704 from majestrate/master
add .dir-locals.el for emacs users
2016-11-10 09:11:52 -05:00
Jeff Becker
e152785de9 remove tab width setting in dir-locals 2016-11-10 08:25:16 -05:00
Jeff Becker
dd259f1852 fix formatting 2016-11-10 08:22:52 -05:00
Jeff Becker
5001cea3a3 add dir-locals for emacs users with code standards set 2016-11-10 08:21:32 -05:00
orignal
a4d586b24e openssl 1.1 for ECDSA 2016-11-09 15:59:01 -05:00
orignal
46f927fc1b cleanup unclaimed out-of-sequence fragments 2016-11-09 14:51:55 -05:00
orignal
b83e7e6c5c correct PeerTest 2016-11-09 12:13:42 -05:00
orignal
5f463d5f6b rollback 2016-11-09 10:16:37 -05:00
orignal
2e301c2919 fixed VS build 2016-11-08 20:25:47 -05:00
orignal
9526d42ec5 Merge pull request #701 from PurpleI2P/atnaguzin-fix-makefile
fix branch detect
2016-11-08 17:26:29 -05:00
MXPLRS | Kirill
a566479ddb fix branch detect 2016-11-09 01:07:10 +03:00
orignal
1bba0f6bb2 store and concatenate all out-of-sequence fragments 2016-11-08 15:37:27 -05:00
orignal
232d42881b support openssl 1.1 for DH 2016-11-08 13:11:38 -05:00
Darknet Villain
abeaf76fe9 Update usage.md 2016-11-08 17:55:40 +00:00
Darknet Villain
03d4584562 Control i2pd and link to configuration page 2016-11-08 17:14:53 +00:00
orignal
f2f5226ebb extract database store key once 2016-11-07 18:32:22 -05:00
xcps
660860b92d verify LeaseSet's ident hash 2016-11-07 15:54:35 -05:00
orignal
c0a1a8b47c limit number of DH precalculations at the time 2016-11-07 14:44:32 -05:00
orignal
bd82e81e26 correct DH keys number to precalculate 2016-11-07 12:29:24 -05:00
orignal
0a94df592c 2.10.1 2016-11-07 09:18:44 -05:00
MXPLRS | Kirill
66506ea1ce Update installer.iss 2016-11-07 07:30:44 +03:00
orignal
7bff4db483 eliminate potential excessive CPU usage 2016-11-06 09:53:45 -05:00
orignal
9208da8a50 more precise peer test 2016-11-05 21:08:14 -04:00
orignal
70fcd93ca7 fixed build error for clang 2016-11-04 12:13:03 -04:00
orignal
9ba9bd4415 preparation for openssl 1.1 2016-11-04 10:59:55 -04:00
orignal
480ce6f522 core file is limited by a system by default 2016-11-03 21:37:47 -04:00
orignal
f1254fd5d4 fixed android build 2016-11-03 21:31:21 -04:00
hagen
10ebcff48e * Log.{cpp,h}:
* use colors only when using stdout
  * use static string array instead bunch of #define's
2016-11-04 00:43:43 +00:00
hagen
6ee227675a * DaemonLinux.cpp : resource limiting 2016-11-04 00:43:29 +00:00
hagen
89059abe15 * Config.cpp : limits.coresize & limits.openfiles 2016-11-04 00:43:28 +00:00
hagen
4503223a4e * SOCKS.cpp : boost::lexical_cast -> std::to_string 2016-11-04 00:43:25 +00:00
hagen
07c31a90f3 * RouterContext.cpp : boost::lexical_cast -> std::to_string 2016-11-04 00:43:24 +00:00
hagen
bbcb9af01f * SAM.cpp : boost::lexical_cast -> std::stoi 2016-11-04 00:43:22 +00:00
hagen
1cd415a3ae * BOB.cpp : boost::lexical_cast -> std::stoi 2016-11-04 00:43:20 +00:00
orignal
c344e75701 Merge branch 'openssl' of https://github.com/PurpleI2P/i2pd into openssl 2016-11-03 15:29:09 -04:00
orignal
0305e4cf8a tunnel options for SOCKS proxy 2016-11-03 15:28:33 -04:00
orignal
bc86b0345f Merge pull request #698 from atnaguzin/systemd
systemd unit, debian patch fix, makefile last-dist added, package log&pid folder edited
2016-11-03 12:20:14 -04:00
atnaguzin
8b0ce30dfc systemd unit, debian patch fix, makefile last-dist added, package log&pid folder edited 2016-11-03 19:08:21 +03:00
orignal
4b983300fe fixed layout 2016-11-03 11:47:33 -04:00
orignal
8829ebba6c fixed layout 2016-11-03 11:46:24 -04:00
orignal
11b90d2113 lenght and number of tunnels for HTTP Proxy 2016-11-03 11:44:25 -04:00
orignal
9d8d4c09c6 reduce explratory tunnels quatity to 3 2016-11-02 15:17:05 -04:00
orignal
c90d5bb67c don't override Host if not specified explicitly 2016-11-02 10:12:54 -04:00
orignal
7263d9f03e Event.h/.cpp added 2016-11-01 19:49:48 -04:00
orignal
d5e77e9bb2 10 seconds max timeout for NTP 2016-11-01 18:40:00 -04:00
orignal
1ecd5250fc eliminate overhead 2016-11-01 17:49:42 -04:00
orignal
44af5e04e4 correct NTP request 2016-11-01 16:27:44 -04:00
orignal
4582a4fd95 eliminate some overhead 2016-11-01 13:57:25 -04:00
orignal
2d513277f2 fixed #696. set netid before context::Init 2016-11-01 13:34:19 -04:00
orignal
7934974d92 fixed android build 2016-11-01 12:16:18 -04:00
orignal
4dce35b1e6 Merge pull request #695 from majestrate/merge-websocket
add optional websocket ui
2016-11-01 11:54:50 -04:00
Jeff Becker
e5f5f96771 merge webui code 2016-11-01 10:46:07 -04:00
Jeff Becker
d4a0076aba merge 2016-11-01 10:26:40 -04:00
Jeff Becker
cd9cd84c5b properly send expiration notice for websockets 2016-11-01 10:07:34 -04:00
Jeff Becker
93eca799dd add more websocket events 2016-11-01 10:06:38 -04:00
Jeff Becker
34f090662a stop websockets 2016-11-01 10:02:41 -04:00
Jeff Becker
1a1d54387c update build files and allow compile without websocket 2016-11-01 10:02:24 -04:00
Jeff Becker
9575f70f38 fix conflicts 2016-11-01 10:02:10 -04:00
Jeff Becker
b4e9ed7d18 add web socket ui 2016-11-01 09:59:50 -04:00
orignal
3d4e2a275c correct separator for android 2016-10-31 18:10:33 -04:00
orignal
b526718846 show HTTP proxy as client tunnel 2016-10-31 15:42:50 -04:00
orignal
a4883cfa15 print tunnel peers in direct order 2016-10-31 15:13:43 -04:00
orignal
a41f179785 get home directory from EXTERNAL_STORAGE for andorid 2016-10-31 14:00:31 -04:00
orignal
bef628212e fixed corrupted buffer duing IRC handshake 2016-10-31 09:46:59 -04:00
orignal
ef3030abe5 Merge pull request #694 from lehitoskin/graceful-typo
gracefull -> graceful
2016-10-31 06:59:18 -04:00
Lehi Toskin
754ad20eff gracefull -> graceful 2016-10-31 03:27:27 -07:00
orignal
647175cf12 correct RTO reset 2016-10-30 09:29:43 -04:00
orignal
5f0a440f0a Merge pull request #692 from l-n-s/my_fixes
My fixes
2016-10-28 17:44:50 -04:00
l-n-s
d68544038c Merge branch 'openssl' into my_fixes 2016-10-28 20:28:11 +00:00
l-n-s
df36b0eb7e Uppercase first letters in config help 2016-10-28 16:17:48 -04:00
l-n-s
4f4748b8df Update nat option: if nat=false, skip reachability testing 2016-10-28 15:57:18 -04:00
Jeff
028a896303 Merge pull request #690 from majestrate/no-churn
add trust.routers option and fix restricted routes
2016-10-28 14:37:32 -04:00
l-n-s
578083df3e Add libdl (-ldl) flag. Fixes openssl errors when building statically. 2016-10-28 13:18:35 -04:00
Jeff Becker
c5e1823f15 dont't set to firewalled, ssu will try introducers 2016-10-28 13:11:50 -04:00
Jeff Becker
5f396d6311 add option to only connect to certain routers 2016-10-28 12:50:26 -04:00
orignal
5c64c2ff42 handle stream ternimation properly 2016-10-28 11:33:11 -04:00
orignal
2dcb91b284 don't create same incoming stream twice 2016-10-27 20:46:05 -04:00
orignal
d708e7f682 check if a lease has been excluded from LeaseSet 2016-10-26 21:40:06 -04:00
orignal
a8a4ef82cd fixed android build 2016-10-26 16:19:32 -04:00
orignal
1286f1c968 inalidate shared routing path 2016-10-26 13:02:19 -04:00
hagen
9368a93279 * fgrep can't be used with regex 2016-10-26 00:41:48 +00:00
hagen
143aaa2d28 * util.h : drop i2p::util::lexical_cast(), not used anymore (#314) 2016-10-26 00:24:16 +00:00
hagen
b8dcdece38 * Destination.cpp : drop use of i2p::util::lexical_cast(), make more compact code 2016-10-26 00:24:13 +00:00
hagen
be7f4c5da7 * update changelog 2016-10-26 00:21:24 +00:00
hagen
890807b8d7 * build docs : markdown cleanup & reformatting 2016-10-26 00:21:22 +00:00
hagen
8e1687e7b3 * reorganize docs in build-notes*.md 2016-10-26 00:21:17 +00:00
orignal
d8510ead43 don't return expired LeaseSet 2016-10-25 14:07:34 -04:00
orignal
c74db4b81c resubmit non-confirmed LeaseSet 2016-10-24 20:58:25 -04:00
orignal
4ee9b4524d correct netid handling 2016-10-24 10:33:46 -04:00
orignal
28cf351878 fixed typo 2016-10-24 07:11:18 -04:00
orignal
c5e2ec5e00 random remote lease selection for LeaseSet update 2016-10-23 16:16:08 -04:00
orignal
fe3ebc4c84 Merge pull request #685 from majestrate/i2lua-cmake
update cmake for i2lua
2016-10-23 11:28:51 -04:00
Jeff Becker
6688f9a5ef update cmake for i2lua 2016-10-23 08:14:55 -04:00
orignal
3167ae21b0 send own LeasetSet through a stalled stream 2016-10-22 20:08:15 -04:00
orignal
c40a463549 Merge pull request #683 from vaygr/openbsd-build
fixed build with OpenBSD
2016-10-22 18:12:54 -04:00
Vlad Glagolev
87a85fff08 Merge branch 'openssl' into openbsd-build 2016-10-22 16:44:15 -04:00
Vlad Glagolev
b68381db58 fixed build with OpenBSD 2016-10-22 16:38:45 -04:00
orignal
25c1884961 correct stream termination 2016-10-20 15:20:08 -04:00
orignal
9980df2c67 Merge pull request #680 from vaygr/libressl-support
fixed build with LibreSSL
2016-10-20 10:50:40 -04:00
Vlad Glagolev
ed09c1171b fixed build with LibreSSL 2016-10-20 10:37:45 -04:00
orignal
c473b10667 Merge pull request #679 from BOPOHA/openssl
fix paths
2016-10-20 10:33:44 -04:00
Anatolii Vorona
c15e53e9c0 fix paths 2016-10-20 15:49:56 +02:00
Darknet Villain
e9d3278fc5 Merge pull request #678 from l-n-s/move_rpm_files
move rpm-related files to contrib folder
2016-10-20 13:12:10 +00:00
l-n-s
b683c07d55 move rpm-related files to contrib folder 2016-10-20 13:08:38 +00:00
l-n-s
681f055b16 Merge pull request #677 from BOPOHA/patch-1
fixed Centos 7 notes
2016-10-20 12:51:22 +00:00
BOPOHA
f4cb4c1756 fixed Centos 7 notes 2016-10-20 13:41:41 +02:00
orignal
1cc67bbbe8 Merge pull request #676 from BOPOHA/openssl
added spec and service files
2016-10-20 07:24:27 -04:00
Anatolii Vorona
0df0450107 added spec and service files 2016-10-20 12:18:59 +02:00
orignal
cb324ca723 portable windows data directory 2016-10-19 12:54:13 -04:00
orignal
442a0c48e7 fixed #675. I2LUA define 2016-10-19 10:23:02 -04:00
orignal
d97acacae6 sequential LeaseSet request 2016-10-17 18:45:20 -04:00
MXPLRS | Kirill
3643d6b5d5 Update changelog 2016-10-17 07:37:40 +03:00
orignal
2edd64470b Update changelog 2016-10-16 09:19:48 -04:00
l-n-s
c42e2fe02d Update i2pd.conf 2016-10-16 13:17:00 +00:00
orignal
12c67b5db4 2.10.0 2016-10-16 08:35:48 -04:00
orignal
a943cc09fe 2.10.0 2016-10-16 07:58:26 -04:00
orignal
1ceda52f59 2.10.0 2016-10-16 07:52:45 -04:00
l-n-s
04ee419951 small fixes for docs 2016-10-16 11:04:59 +00:00
l-n-s
f687728c3a edit link to usage documentation 2016-10-16 10:59:48 +00:00
l-n-s
bde5d27a20 Update README.md 2016-10-13 16:56:23 +00:00
l-n-s
07a1651fa2 Update usage.md
fix for readthedocs
2016-10-13 07:45:30 +00:00
l-n-s
32b47bee2c Update README.md
Add link to Russian docs
2016-10-12 17:52:07 +00:00
orignal
fbf75ea3b9 check if signer/verifier is set already 2016-10-12 13:28:22 -04:00
orignal
a157aba74f Merge pull request #671 from atnaguzin/patch-2
Update installer.iss
2016-10-12 12:32:12 -04:00
orignal
e45e5df377 openssl 1.1 DSA functions 2016-10-12 12:31:27 -04:00
MXPLRS | Kirill
85c7bfa160 Update installer.iss 2016-10-12 19:30:20 +03:00
orignal
8182f97c15 Merge pull request #670 from atnaguzin/patch-2
script for inno setup
2016-10-12 12:09:03 -04:00
MXPLRS | Kirill
eba824f5d0 script for inno setup 2016-10-12 19:03:35 +03:00
orignal
99983798a4 configurable netid 2016-10-12 11:26:48 -04:00
orignal
93ed032015 avoid potential deadlock 2016-10-12 09:39:16 -04:00
l-n-s
9359f5b296 Update README.md
Less complicated description
2016-10-12 12:09:58 +00:00
orignal
3b467c19cb Merge pull request #668 from l-n-s/addressbook_configurable
Add addressbook options + improved docs
2016-10-12 07:08:55 -04:00
l-n-s
470a6f0ab2 Add addressbook options + improved docs 2016-10-12 10:23:43 +00:00
orignal
fe8a0c1a6b #622. Force SU3 verification by reseed.verify 2016-10-11 15:02:23 -04:00
orignal
f0d098d0ef use shared local destination for lookup if destination is not set 2016-10-11 13:39:07 -04:00
orignal
f17df1f16d spinlock added 2016-10-11 12:06:40 -04:00
orignal
b1f8f9830b fixed another cases for #651 2016-10-11 10:18:42 -04:00
orignal
e78ccc6bec fixed #651. check is destination is set 2016-10-11 07:31:16 -04:00
orignal
b54892a783 Merge pull request #664 from brain5lug/tag-cleanup
Tag class clean-up
2016-10-10 18:13:37 -04:00
brain5lug
16c37a0f3d indentation fix for missed Fill function 2016-10-11 00:46:18 +03:00
brain5lug
141fb78237 Tag class clean-up 2016-10-11 00:19:34 +03:00
orignal
eb31b9a4d6 Merge branch 'openssl' of https://github.com/PurpleI2P/i2pd into openssl 2016-10-10 16:34:08 -04:00
orignal
f10d9e1332 update reseed lists 2016-10-10 16:31:26 -04:00
orignal
6d63521622 update miniupnpc instructions 2016-10-10 11:46:52 -04:00
l-n-s
e1aa066489 Merge pull request #663 from l-n-s/update_readme
Update readme
2016-10-10 15:17:10 +00:00
l-n-s
c78ec12e99 Add specs link 2016-10-10 15:07:22 +00:00
l-n-s
3fa4e2f58d Update README.md 2016-10-10 15:00:36 +00:00
Jeff
c64aaade70 Merge pull request #662 from majestrate/merge-recent-udp-tunnel
Merge recent udp tunnel changes
2016-10-10 09:13:57 -04:00
Jeff Becker
e8d8b290a6 rename 2016-10-10 09:07:49 -04:00
Jeff Becker
456d9e79e6 Revert "minimize count of errors "I2CP: Failed to send message. No outbound tunnels""
This reverts commit 8ff2627e8e.
2016-10-10 09:06:32 -04:00
Jeff Becker
3095e14247 undo weird mutex changes 2016-10-10 09:04:24 -04:00
Jeff Becker
a332d68704 Revert "fix f79ad91"
This reverts commit e8e3db6888.
2016-10-10 09:02:39 -04:00
Jeff Becker
84ca992e91 Revert "Unused parameter warnings removal"
This reverts commit 5350078543.
2016-10-10 08:59:48 -04:00
orignal
f91f3796a8 make sure verifier gets created once 2016-10-10 08:59:45 -04:00
Jeff Becker
22250ae552 Merge remote-tracking branch 'purple/openssl' into merge-recent-udp-tunnel 2016-10-10 08:36:06 -04:00
Jeff Becker
8a95b5b5b0 tabify 2016-10-10 08:30:33 -04:00
Jeff Becker
7506619f4c add minimum path lifetime 2016-10-10 08:25:07 -04:00
Jeff Becker
577d9ddf65 fix memory leak with udp tunnel 2016-10-10 08:23:54 -04:00
Jeff Becker
43c3bdf7c5 fix 2016-10-10 08:21:47 -04:00
Jeff Becker
8ba142eb45 increase datagram session switching interval 2016-10-10 08:21:33 -04:00
Jeff Becker
0fc4e01b1e remove tools 2016-10-10 08:18:54 -04:00
orignal
f83ebbcd3a Merge branch 'openssl' of https://github.com/PurpleI2P/i2pd into openssl 2016-10-09 15:45:54 -04:00
orignal
77ec4b5cad added warning 2016-10-09 14:57:15 -04:00
orignal
9a687976bc Merge pull request #659 from brain5lug/thread-sanitizer
thread sanitizer configuration option have been added
2016-10-09 14:52:42 -04:00
brain5lug
5a796a86d7 thread sanitizer configuration option have been added 2016-10-06 22:49:44 +03:00
Jeff Becker
71d4221af2 add keyinfo tool 2016-10-05 11:40:25 -04:00
Jeff Becker
526ba37435 Merge branch 'merge-upd-tunnel-fix' into openssl 2016-10-05 11:36:15 -04:00
Jeff Becker
f3c080f8a4 Merge branch 'merge-650' into openssl 2016-10-05 11:29:55 -04:00
Jeff Becker
09a0cf07e4 Merge branch 'check_pr_653' into upstream-openssl 2016-10-05 11:24:44 -04:00
Jeff Becker
4a3bf46c30 Revert "try fixing appveyor"
This reverts commit 30dfe12910.
2016-10-05 11:03:51 -04:00
Jeff Becker
30dfe12910 try fixing appveyor 2016-10-05 10:46:15 -04:00
Jeff Becker
ae2b5dfd3e fix udp tunnel route switching logic 2016-10-05 07:42:00 -04:00
alexandr
cb0f968467 Added building option "USE_ASLR" 2016-10-05 06:45:41 +05:00
alexandr
e8e3db6888 fix f79ad91 2016-10-05 01:20:43 +05:00
Pavel Melkozerov
012ade5000 Added extra-cmake-modules 2016-10-04 18:13:45 +03:00
brain5lug
5350078543 Unused parameter warnings removal 2016-10-04 00:24:42 +03:00
brain5lug
404715e02d thread sanitizer configuration option have been added 2016-10-03 23:24:22 +03:00
alexandr
31dde394eb remove unnecessary brackets 2016-10-03 20:20:45 +05:00
alexandr
8ff2627e8e minimize count of errors "I2CP: Failed to send message. No outbound tunnels" 2016-10-03 20:06:10 +05:00
alexandr
f79ad91a9a probably fix hanging of call I2CP-SendMsgTo-FindLeaseSet 2016-10-03 20:01:31 +05:00
alexandr
ff6a79bca3 Merge remote-tracking branch 'origin' into openssl 2016-10-02 03:22:50 +05:00
orignal
e4d6092939 copy addresses list atomically 2016-10-01 15:05:35 -04:00
alexandr
9d998d27c5 Merge remote-tracking branch 'origin' into openssl 2016-09-29 20:58:53 +05:00
orignal
d6aca6fa00 always send reply 2016-09-29 11:24:52 -04:00
alexandr
dd15472da7 Merge remote-tracking branch 'origin' into openssl 2016-09-26 02:45:44 +05:00
orignal
b03712a30e correct outbound tunnel selection 2016-09-25 17:23:21 -04:00
alexandr
d025ba2793 Fixed visibility of variable outboundTunnel 2016-09-26 01:37:00 +05:00
orignal
e5e09c9b51 check for boost version 2016-09-24 08:29:08 -04:00
orignal
5b8d1df349 Merge pull request #643 from atnaguzin/patch-1
Fixed links and msys2 example filename
2016-09-24 07:32:51 -04:00
MXPLRS | Kirill
fa092c0162 Fixed links 2016-09-24 14:26:59 +03:00
MXPLRS | Kirill
08c1359a27 fixed MiniUPnP link 2016-09-24 13:50:14 +03:00
orignal
dba355eccd use atomic_store for addresses' list re-assignment 2016-09-23 13:15:08 -04:00
orignal
2ad927b677 NTP request 2016-09-21 16:18:51 -04:00
orignal
315f672254 Timestamp.cpp added 2016-09-21 12:02:52 -04:00
orignal
7a51407f6d show error message in the web-console 2016-09-19 21:37:04 -04:00
orignal
783c2b6b03 Merge pull request #640 from atnaguzin/patch-1
add sliders for LeaseSets
2016-09-19 13:24:32 -04:00
MXPLRS | Kirill
a64e1b2aa6 add sliders for LeaseSets 2016-09-19 20:22:15 +03:00
orignal
440516e95f detect clock skew 2016-09-18 18:42:21 -04:00
orignal
dc6108575c Merge pull request #638 from brain5lug/openssl
address sanitizer configuration option have been added
2016-09-17 07:42:54 -04:00
brain5lug
6c7316408b address sanitizer configuration option have been added 2016-09-17 11:01:01 +03:00
orignal
9aecc69461 Merge pull request #636 from l-n-s/reseeds_config
Adding option to configure reseed URLs
2016-09-16 19:11:08 -04:00
l-n-s
949be436a6 Adding option to configure reseed URLs 2016-09-16 22:56:51 +00:00
orignal
cb91891f22 check buffer size 2016-09-16 16:18:50 -04:00
orignal
8795f0c8c4 Merge branch 'openssl' of https://github.com/PurpleI2P/i2pd into openssl 2016-09-16 10:31:45 -04:00
orignal
fbb5bb2f05 fix #634.don't create timer in constructor 2016-09-16 10:31:11 -04:00
orignal
ba309fe6e5 Merge pull request #633 from brain5lug/openssl
perfect forwarding for logging arguments
2016-09-15 21:27:30 -04:00
brain5lug
fee5f959fd perfect forwarding for logging arguments 2016-09-16 01:47:53 +03:00
orignal
f9a5f4955c check RI signture before processing 2016-09-12 21:37:43 -04:00
orignal
325b362727 show UDP tunnels 2016-09-12 12:05:01 -04:00
orignal
75065f29f7 check if field is incomplete 2016-09-12 11:39:33 -04:00
orignal
ed874fe3ea check if RouterInfo has been decompressed completely 2016-09-11 21:36:17 -04:00
orignal
6885761f87 check if sessions list is empty 2016-09-08 10:56:22 -04:00
orignal
a4762fe65c remove expired session through one pass 2016-09-08 10:46:13 -04:00
orignal
bee407ea34 clean-up datagram session toghters with leasesets and tags 2016-09-08 10:16:42 -04:00
orignal
db71673722 fixed #629. catch HTTPServer exceptions 2016-09-08 09:19:30 -04:00
orignal
9ecbbf09cc Merge pull request #628 from majestrate/merge_udp_tunnel
Merge Recent Changes
2016-09-07 15:27:54 -04:00
Jeff Becker
b6b14f4957 Merge remote-tracking branch 'purple/openssl' into merge_udp_tunnel 2016-09-07 15:08:45 -04:00
orignal
6e0d6dcac5 reselect tunnels if LeaseSet delivery was not confirmed 2016-09-07 13:25:11 -04:00
orignal
47a0ebdc91 Merge pull request #627 from vovasty/ios-build
build instruction for iOS
2016-09-06 16:01:27 -04:00
Solomenchuk, Vlad
f3a61007a7 build instruction for iOS 2016-09-06 12:26:59 -07:00
orignal
4a56d6bf1c Merge pull request #626 from majestrate/webui-freeze-fix
Webui freeze fix
2016-09-04 18:47:09 -04:00
Jeff Becker
517d4dc6f5 Merge branch 'openssl' of https://github.com/PurpleI2P/i2pd into webui-freeze-fix 2016-09-04 16:43:55 -04:00
Jeff Becker
722f1c4430 try fixing webui freeze 2016-09-04 16:43:34 -04:00
Jeff Becker
f4d1b87f73 expire tags 2016-09-03 18:34:18 -04:00
Jeff Becker
f64f875806 don't show udp tunnels in ui yet 2016-09-03 18:04:54 -04:00
Jeff Becker
7ae09fa1fe try fixing memory errors 2016-09-03 17:58:16 -04:00
Jeff Becker
8a29dfc3fa tabify and use shared pointers 2016-09-03 17:53:46 -04:00
Jeff Becker
1015188c4e use shared pointers 2016-09-03 16:54:39 -04:00
Jeff Becker
f0bc2a3645 add null checks 2016-09-03 16:43:02 -04:00
Jeff Becker
82f46464f3 prevent double free 2016-09-03 16:29:50 -04:00
Jeff Becker
d336d920e8 fix typo 2016-09-03 16:16:16 -04:00
Jeff Becker
2f61dd1c41 fix double free 2016-09-03 16:12:43 -04:00
Jeff Becker
10ffd5c1ab don't check for expired lease 2016-09-03 16:06:14 -04:00
Jeff Becker
7e99be12b0 fix typo 2016-09-03 16:03:38 -04:00
Jeff Becker
d37a790b57 fix another typo 2016-09-03 15:58:52 -04:00
Jeff Becker
7ea8509dfe fix typo 2016-09-03 15:54:22 -04:00
Jeff Becker
783c0c7c7b update datagram lease selection 2016-09-03 15:53:28 -04:00
Jeff Becker
68b0775e4b update datagram path logic 2016-09-03 15:35:42 -04:00
Jeff Becker
682334d844 fix typo 2016-09-03 15:35:32 -04:00
libre-net-society
75981491a7 adding usage docs 2016-09-03 22:23:48 +03:00
Jeff Becker
7cc805b203 update datagram session logic 2016-09-03 14:56:51 -04:00
Jeff Becker
8cdd3a0abb update routing path when we get a new lease set 2016-09-03 14:42:27 -04:00
Jeff Becker
571c630d93 try creating routing session if not present 2016-09-03 14:37:36 -04:00
Jeff Becker
fa1021df59 add webui for udp tunnels 2016-09-03 13:58:34 -04:00
Jeff Becker
9acbb2203c Update Indentation and don't spam route changes in datagram sessions 2016-09-03 11:46:47 -04:00
Jeff Becker
c770bcbf96 prevent race condition in datagram destination
clean up style a bit
2016-09-03 10:24:06 -04:00
Jeff Becker
caace05ba6 fix up compiler warnings 2016-09-03 10:01:23 -04:00
Jeff Becker
c65dc44f20 Fix up I2PTunnel UDP tunnels 2016-09-03 09:38:53 -04:00
Jeff Becker
3ea624e1db cosmetic fix 2016-09-01 15:54:48 -04:00
Jeff Becker
c9c58074fa Merge remote-tracking branch 'purple/openssl' 2016-09-01 11:28:04 -04:00
orignal
aa687afd37 updated LeaseSet must be sent after completion 2016-09-01 09:48:04 -04:00
orignal
8cb69c1482 fixed #624. correct v6 address size 2016-08-31 22:47:32 -04:00
Jeff Becker
a68326490d fix 2016-08-30 20:02:27 -04:00
Jeff Becker
ab763c38d9 use shared pointers 2016-08-30 19:59:24 -04:00
Jeff Becker
970557660e Add NetDb::VisitRandomRouterInfos 2016-08-30 15:54:53 -04:00
Jeff Becker
fa8548fe34 implement SetCustomPeerSelector and ensure locking is good 2016-08-30 15:11:39 -04:00
Jeff Becker
ac88c1a8f1 add ClientDestination::Ready 2016-08-30 13:27:57 -04:00
xcps
f2893097a7 check before bind to 127.x.x.x 2016-08-30 02:53:26 +05:00
Jeff Becker
c0cba7b376 move ready to run 2016-08-29 16:59:17 -04:00
Jeff Becker
87d1058de3 fix 2016-08-29 16:57:34 -04:00
xcps
85e65da492 server tunnel on linux binds on 127.x.x.x 2016-08-30 01:48:47 +05:00
Jeff Becker
ce97fa87e7 don't use std::promise * 2016-08-29 15:34:59 -04:00
Jeff Becker
10ffdb2766 add NetDb::WaitForReady 2016-08-29 15:26:19 -04:00
Jeff Becker
fec49e5609 add hooks for visiting netdb 2016-08-29 14:16:29 -04:00
Jeff Becker
28fdd992c9 add hooks for custom peer selection 2016-08-29 12:09:37 -04:00
Jeff Becker
048d3c8386 Merge remote-tracking branch 'purple/openssl' 2016-08-29 10:56:27 -04:00
orignal
50e3d6ff37 Merge pull request #623 from majestrate/fix_streaming_hang
make sure m_RTO > 0 in Streaming.cpp so it doesn't hang
2016-08-29 10:48:15 -04:00
Jeff Becker
37b80f0ce3 make sure m_RTO > 0 in Streaming.cpp so it doesn't hang 2016-08-29 10:44:54 -04:00
Jeff Becker
7d37b02cff datagram fixes 2016-08-29 10:42:51 -04:00
Jeff Becker
c6556b8442 make sure m_RTO > 0 in Streaming.cpp so it doesn't hang 2016-08-29 10:41:15 -04:00
Jeff Becker
5685c376cb fix broken build 2016-08-27 16:13:11 -04:00
Jeff Becker
2ce64e1bf5 fix typo 2016-08-27 16:10:18 -04:00
Jeff Becker
7d03a41e3e try manual expiration of tags 2016-08-27 16:09:02 -04:00
Jeff Becker
35b68db847 schedule cleanup again and add logging 2016-08-27 15:45:56 -04:00
Jeff Becker
0b21fce94e try adding garlic and session tags to datagram destination 2016-08-27 13:17:34 -04:00
Jeff Becker
abaf36a2de try unbreaking static build 2016-08-27 09:29:18 -04:00
hagen
26440d94f1 * HTTPServer : keep response data for async_write() 2016-08-26 14:42:34 +00:00
hagen
205b61e4cf * HTTPServer : fix tag 2016-08-26 14:42:34 +00:00
orignal
fc5fc5bbee don't throw exception if connection failed 2016-08-26 10:06:28 -04:00
orignal
c4171a01bd fix #622. extract correct CN 2016-08-26 09:48:19 -04:00
orignal
32669cb07f stop termination timer on shutdown 2016-08-24 12:34:18 -04:00
Jeff Becker
7018c381ee Merge remote-tracking branch 'purple/openssl' 2016-08-24 11:42:35 -04:00
orignal
b02677ee21 common termination timer for all SSU sessions 2016-08-24 11:21:49 -04:00
Jeff Becker
065d01bcf6 logging update 2016-08-22 18:29:12 -04:00
Jeff Becker
42b15e8bbe fix 2016-08-22 17:31:23 -04:00
Jeff Becker
e8195b78ba fix 2016-08-22 17:26:43 -04:00
Jeff Becker
1d7d7cf9a0 more changes 2016-08-22 17:19:22 -04:00
Jeff Becker
979575c311 fix 2016-08-22 13:59:51 -04:00
Jeff Becker
be12739342 fix 2016-08-22 13:55:44 -04:00
Jeff Becker
7f7acd8bde fixes 2016-08-22 13:54:00 -04:00
Jeff Becker
f5e2899275 post work to io service 2016-08-22 13:04:36 -04:00
Jeff Becker
bee34a3222 fix 2016-08-21 22:54:06 -04:00
Jeff Becker
5b00cb1e64 osx fix 2016-08-21 22:38:41 -04:00
Jeff Becker
6bb9de5a96 osx fix 2016-08-21 22:34:48 -04:00
Jeff Becker
b977050caf osx fix 2016-08-21 22:29:55 -04:00
Jeff Becker
3f63732c31 osx fix 2016-08-21 22:26:30 -04:00
Jeff Becker
211660eb3d osx fix 2016-08-21 22:23:27 -04:00
Jeff Becker
0c709f431f osx fix 2016-08-21 22:18:59 -04:00
Jeff Becker
9062bf14b6 osx fix 2016-08-21 22:16:35 -04:00
Jeff Becker
47ebb6ae6c osx fix 2016-08-21 22:11:41 -04:00
Jeff Becker
b1e3f88704 osx fix 2016-08-21 22:00:31 -04:00
Jeff Becker
bc439cc47f osx fix 2016-08-21 21:57:36 -04:00
Jeff Becker
1bba5d5c94 osx fix 2016-08-21 21:55:00 -04:00
Jeff Becker
d159d49700 os x fix 2016-08-21 21:51:32 -04:00
Jeff Becker
7ef7ef03dd fix 2016-08-21 21:47:56 -04:00
Jeff Becker
808b758cd7 fix 2016-08-21 21:45:08 -04:00
Jeff Becker
ff6d66b96e init addressbook first 2016-08-21 21:40:21 -04:00
Jeff Becker
da82b14307 changes 2016-08-21 21:28:24 -04:00
Jeff Becker
7b5e18d94b changes 2016-08-21 21:17:09 -04:00
Jeff Becker
72974c85c8 try fix 2016-08-21 20:23:39 -04:00
Jeff Becker
28627a81dc update 2016-08-21 20:18:41 -04:00
Jeff Becker
bbfe6b66ef fix 2016-08-21 19:48:47 -04:00
Jeff Becker
bce0e3ebf6 fix 2016-08-21 19:45:32 -04:00
Jeff Becker
bf46c241d0 fixes 2016-08-21 19:38:12 -04:00
Jeff Becker
287e32aaed logging 2016-08-21 19:33:33 -04:00
Jeff Becker
aa11a5deb8 fix 2016-08-21 19:27:01 -04:00
Jeff Becker
194d63acd8 fixes 2016-08-21 19:17:08 -04:00
Jeff Becker
46d640cd86 fixes 2016-08-21 18:46:34 -04:00
Jeff Becker
51783a45e6 set last activity 2016-08-21 15:56:52 -04:00
Jeff Becker
2679c58892 logging and ip checks 2016-08-21 15:51:39 -04:00
Jeff Becker
2a5af37075 retrgiger expiration 2016-08-21 15:47:00 -04:00
Jeff Becker
e529d3ecc9 fixes 2016-08-21 15:39:11 -04:00
Jeff Becker
e8f9ecc7d9 fixes 2016-08-21 15:33:19 -04:00
Jeff Becker
aa3723d2bd udp tunnels 2016-08-21 15:02:17 -04:00
hagen
bbbda44218 * HTTPServer : show termination time in main page (closes #506) 2016-08-21 13:49:11 +00:00
hagen
f99aea5cb1 * Makefile.linux : use linker flags instead full paths to obj files (#602) 2016-08-21 13:49:08 +00:00
orignal
65c003eef8 Merge pull request #615 from AMDmi3/miniupnpc-library-detection
Find miniupnpc library as well as header
2016-08-19 07:01:35 -04:00
Dmitry Marakasov
959843ee9c Find miniupnpc library as well as header 2016-08-19 12:16:28 +03:00
Jeff Becker
c16632d99a Merge remote-tracking branch 'purple/master' 2016-08-18 18:37:32 -04:00
orignal
3d066ea1b8 common termination timer for all NTCP sessions 2016-08-17 10:58:57 -04:00
orignal
e163730118 Merge pull request #614 from PurpleI2P/openssl
recent changes
2016-08-17 10:37:09 -04:00
Jeff Becker
3c8838af08 Merge branch 'upstream-master' 2016-08-16 10:55:54 -04:00
orignal
ac5394a1dc Merge pull request #613 from majestrate/fix-static-cmake
Fix static cmake
2016-08-16 10:51:26 -04:00
Jeff Becker
2e74d91ddc try fixing https://github.com/PurpleI2P/i2pd/issues/612 2016-08-16 10:27:33 -04:00
Jeff Becker
2d82c4ada4 try fixing https://github.com/PurpleI2P/i2pd/issues/612 2016-08-16 10:25:56 -04:00
orignal
03f0ca965e fixed race condition 2016-08-15 22:36:58 -04:00
orignal
a527dcd95b moved HTTP to libi2pd 2016-08-15 14:01:57 -04:00
orignal
de29abb05c check string buffer size 2016-08-15 13:12:56 -04:00
orignal
cb7efcb188 add 'O' to extra bandwidth for flooadfill 2016-08-14 17:58:50 -04:00
orignal
bf4f22b203 add 'O' to extra bandwidth 2016-08-14 17:52:11 -04:00
orignal
7f3a467a66 Merge pull request #608 from atnaguzin/patch-1
Updated debian/changelog
2016-08-13 14:47:28 -04:00
MXPLRS | Kirill
72ef621f9d Update changelog 2016-08-13 16:52:51 +03:00
MXPLRS | Kirill
73452f758c Update changelog 2016-08-13 16:52:27 +03:00
MXPLRS | Kirill
049e1b2679 Update changelog 2016-08-13 16:50:08 +03:00
orignal
4631123231 reseed-ru.lngserv.ru added 2016-08-13 09:05:35 -04:00
orignal
c86bcb4dd6 r4sas_at_mail.i2p.crt added 2016-08-13 08:23:32 -04:00
orignal
a6280661ee Merge pull request #607 from atnaguzin/patch-1
Updated i2pd.conf
2016-08-12 16:29:04 -04:00
MXPLRS | Kirill
ca7709a284 Update i2pd.conf 2016-08-12 23:23:10 +03:00
orignal
384c06f2e9 Merge pull request #606 from atnaguzin/patch-1
Updates in debian part.
2016-08-12 16:20:30 -04:00
MXPLRS | Kirill
774c11781d Update changelog 2016-08-12 22:47:20 +03:00
MXPLRS | Kirill
7a692898e4 moved tunnels.conf 2016-08-12 22:36:31 +03:00
MXPLRS | Kirill
2f1971ea8f moved tunnels.conf 2016-08-12 22:36:17 +03:00
MXPLRS | Kirill
ce13de7d6c moved subscriptions.txt 2016-08-12 22:35:09 +03:00
MXPLRS | Kirill
d51ad77ab4 moved subscriptions.txt 2016-08-12 22:34:29 +03:00
MXPLRS | Kirill
a9b289626e Update logrotate 2016-08-12 22:30:26 +03:00
MXPLRS | Kirill
8a542f2ce8 Update i2pd.upstart 2016-08-12 22:29:32 +03:00
MXPLRS | Kirill
14a2c9d48f Update i2pd.install 2016-08-12 22:28:35 +03:00
orignal
37fef7e4f8 Update ChangeLog 2016-08-12 15:21:55 -04:00
MXPLRS | Kirill
b83ab85fd9 Update i2pd.init 2016-08-12 22:21:33 +03:00
orignal
d424e1e9ff Update ChangeLog 2016-08-12 15:19:45 -04:00
MXPLRS | Kirill
aaa52bd767 Update i2pd.default 2016-08-12 22:17:25 +03:00
Jeff Becker
56254e728c Merge tag 'tags/2.9.0' 2016-08-12 13:25:25 -04:00
orignal
284159aadc Merge pull request #605 from manasb/manasb-patch-docs
fix a typo
2016-08-12 12:56:33 -04:00
Manas B
29593f0161 fix a typo 2016-08-12 12:43:59 -04:00
orignal
c09212de81 Merge pull request #604 from PurpleI2P/openssl
recent changes
2016-08-12 10:44:02 -04:00
Jeff Becker
03927b0a68 Merge branch 'upstream-openssl' 2016-08-06 20:27:57 -04:00
Jeff Becker
c8f5fb4d03 close duplicate ntcp sessions 2016-08-03 10:40:30 -04:00
Jeff Becker
070a21a9eb Merge remote-tracking branch 'purple/openssl' 2016-07-31 19:29:12 -04:00
Jeff Becker
cf8ff2cf86 make it compile 2016-07-28 15:35:13 -04:00
Jeff Becker
ee9dc789af change scope of Stream::Terminate 2016-07-28 15:34:32 -04:00
Jeff Becker
570598f556 abruptly close 2016-07-28 15:33:03 -04:00
Jeff Becker
e8c3546433 try fixing 2016-07-28 13:27:51 -04:00
Jeff Becker
584379b502 fix 2016-07-28 12:48:32 -04:00
Jeff Becker
87e1c45c05 fug 2016-07-28 12:34:33 -04:00
Jeff Becker
9447afe49c try changing i2cp option 2016-07-28 12:32:05 -04:00
Jeff Becker
fc5b1ae3e2 fug 2016-07-28 12:28:18 -04:00
Jeff Becker
13735d0475 move setting 2016-07-28 12:26:05 -04:00
Jeff Becker
6b3a783ce9 change type 2016-07-28 12:21:59 -04:00
Jeff Becker
3b66bba92e more fixes 2016-07-28 12:17:24 -04:00
Jeff Becker
a2e01f8a53 more tweaks 2016-07-28 11:42:31 -04:00
Jeff Becker
34da9a9655 streaming limiting tweaks 2016-07-28 11:37:33 -04:00
Jeff Becker
df8d73ae43 typo 2016-07-28 11:20:24 -04:00
Jeff Becker
aa3a93b6a0 implement streaming limiting (initial) 2016-07-28 11:16:29 -04:00
Jeff Becker
17bfa35f77 don't use warning for no tags 2016-07-28 10:02:26 -04:00
Jeff Becker
59797a5c9a streaming log tweaks and dont set RTT to 0 2016-07-28 10:01:20 -04:00
Jeff Becker
61fe2923e4 don't set socket option for closed sockets 2016-07-28 09:53:39 -04:00
Jeff Becker
50b9eca34c check for bogus times in packets 2016-07-28 09:50:19 -04:00
Jeff Becker
f5684eba90 color log messages for warn and error 2016-07-28 09:49:43 -04:00
Jeff Becker
f32510e10a set socket options 2016-07-28 09:25:05 -04:00
Jeff Becker
4fb0eeda37 Merge remote-tracking branch 'purple/openssl' 2016-07-28 09:06:58 -04:00
Jeff Becker
eeeae12639 check for correctly loaded privatekeys 2016-07-25 11:13:54 -04:00
Jeff Becker
0d854c6ea6 Merge remote-tracking branch 'purple/master' 2016-07-25 10:29:10 -04:00
Jeff Becker
1e1c4d159b do reload 2016-07-25 10:28:20 -04:00
Jeff Becker
4dc9f6948d bounds checks 2016-07-25 09:57:35 -04:00
orignal
c06e739c9b Merge pull request #583 from PurpleI2P/openssl
recent changes
2016-07-25 09:43:18 -04:00
Jeff Becker
c90c008f65 Merge remote-tracking branch 'purple/openssl' 2016-07-23 21:22:02 -04:00
Jeff Becker
6d54401d7c Merge remote-tracking branch 'devnull/linux_tunnel_reload_on_hup' 2016-07-22 12:42:59 -04:00
Jeff Becker
c9a0897208 Merge remote-tracking branch 'purple/openssl' 2016-07-22 11:57:48 -04:00
Jeff Becker
5995ab3f4c Merge branch 'upstream-openssl' 2016-07-22 10:18:58 -04:00
Jeff Becker
289cae4213 Merge remote-tracking branch 'purple/openssl' 2016-07-22 09:59:56 -04:00
Jeff Becker
eaac21cda1 * check router info addresses for nullptr
* Request LS before expiration for smoother handover
2016-07-22 09:56:17 -04:00
/dev/null
c908beade2 Added client tunnel reload on SIGHUP for Linux 2016-07-21 17:57:43 -06:00
orignal
0e31da5e51 Merge pull request #578 from PurpleI2P/openssl
recent changes
2016-07-20 18:33:43 -04:00
orignal
8cc6756815 Merge pull request #566 from PurpleI2P/openssl
recent changes
2016-07-09 21:41:43 -04:00
orignal
c79363ef63 Merge pull request #563 from PurpleI2P/openssl
recent changes
2016-07-05 09:40:17 -04:00
orignal
03d7330af5 Merge pull request #556 from PurpleI2P/openssl
floodfill fixes
2016-06-30 17:51:26 -04:00
orignal
f08ea4a9a3 Merge pull request #535 from PurpleI2P/openssl
2.8.0
2016-06-22 15:47:22 -04:00
orignal
fb213a1efd Merge pull request #523 from PurpleI2P/openssl
android build
2016-06-18 21:26:40 -04:00
orignal
f995595202 Merge pull request #521 from PurpleI2P/openssl
android
2016-06-17 09:41:35 -04:00
orignal
d7a06dc7a9 Merge pull request #516 from PurpleI2P/openssl
recent changes
2016-06-14 11:10:20 -04:00
orignal
ccc24337be Merge pull request #511 from PurpleI2P/openssl
recent changes
2016-06-10 14:46:23 -04:00
187 changed files with 9108 additions and 3527 deletions

2
.dir-locals.el Normal file
View File

@@ -0,0 +1,2 @@
((c++-mode . ((indent-tabs-mode . t)))
(c-mode . ((mode . c++))))

11
.gitignore vendored
View File

@@ -8,13 +8,14 @@ netDb
/i2pd
/libi2pd.a
/libi2pdclient.a
i2pd.exe
# Autotools
autom4te.cache
.deps
stamp-h1
Makefile
#Makefile
config.h
config.h.in~
config.log
@@ -238,3 +239,11 @@ pip-log.txt
# Sphinx
docs/_build
/androidIdea/
# emacs files
*~
*\#*
# gdb files
.gdb_history

View File

@@ -6,6 +6,7 @@
#include <chrono>
#include <condition_variable>
#include <openssl/rand.h>
#include <boost/algorithm/string.hpp>
#include "Base.h"
#include "util.h"
#include "Identity.h"
@@ -15,6 +16,7 @@
#include "NetDb.h"
#include "ClientContext.h"
#include "AddressBook.h"
#include "Config.h"
namespace i2p
{
@@ -404,9 +406,21 @@ namespace client
m_Subscriptions.push_back (std::make_shared<AddressBookSubscription> (*this, s));
}
LogPrint (eLogInfo, "Addressbook: ", m_Subscriptions.size (), " subscriptions urls loaded");
LogPrint (eLogWarning, "Addressbook: subscriptions.txt usage is deprecated, use config file instead");
}
else
LogPrint (eLogWarning, "Addressbook: subscriptions.txt not found in datadir");
else if (!i2p::config::IsDefault("addressbook.subscriptions"))
{
// using config file items
std::string subscriptionURLs; i2p::config::GetOption("addressbook.subscriptions", subscriptionURLs);
std::vector<std::string> subsList;
boost::split(subsList, subscriptionURLs, boost::is_any_of(","), boost::token_compress_on);
for (size_t i = 0; i < subsList.size (); i++)
{
m_Subscriptions.push_back (std::make_shared<AddressBookSubscription> (*this, subsList[i]));
}
LogPrint (eLogInfo, "Addressbook: ", m_Subscriptions.size (), " subscriptions urls loaded");
}
}
else
LogPrint (eLogError, "Addressbook: subscriptions already loaded");
@@ -511,10 +525,11 @@ namespace client
{
if (!m_IsLoaded)
{
// download it from http://i2p-projekt.i2p/hosts.txt
// download it from default subscription
LogPrint (eLogInfo, "Addressbook: trying to download it from default subscription.");
std::string defaultSubURL; i2p::config::GetOption("addressbook.defaulturl", defaultSubURL);
if (!m_DefaultSubscription)
m_DefaultSubscription = std::make_shared<AddressBookSubscription>(*this, DEFAULT_SUBSCRIPTION_ADDRESS);
m_DefaultSubscription = std::make_shared<AddressBookSubscription>(*this, defaultSubURL);
m_IsDownloading = true;
std::thread load_hosts(std::bind (&AddressBookSubscription::CheckUpdates, m_DefaultSubscription));
load_hosts.detach(); // TODO: use join
@@ -546,8 +561,8 @@ namespace client
auto datagram = dest->GetDatagramDestination ();
if (!datagram)
datagram = dest->CreateDatagramDestination ();
datagram->SetReceiver (std::bind (&AddressBook::HandleLookupResponse, this,
std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5),
datagram->SetReceiver (std::bind (&AddressBook::HandleLookupResponse, this,
std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5),
ADDRESS_RESPONSE_DATAGRAM_PORT);
}
}
@@ -558,8 +573,7 @@ namespace client
if (dest)
{
auto datagram = dest->GetDatagramDestination ();
if (datagram)
datagram->ResetReceiver (ADDRESS_RESPONSE_DATAGRAM_PORT);
if (datagram) datagram->ResetReceiver (ADDRESS_RESPONSE_DATAGRAM_PORT);
}
}
@@ -622,7 +636,11 @@ namespace client
if (address.length () > 0)
{
// TODO: verify from
m_Addresses[address] = buf + 8;
i2p::data::IdentHash hash(buf + 8);
if (!hash.IsZero ())
m_Addresses[address] = hash;
else
LogPrint (eLogInfo, "AddressBook: Lookup response: ", address, " not found");
}
}
@@ -685,13 +703,14 @@ namespace client
int dest_port = url.port ? url.port : 80;
/* create http request & send it */
i2p::http::HTTPReq req;
req.add_header("Host", dest_host);
req.add_header("User-Agent", "Wget/1.11.4");
req.add_header("Connection", "close");
req.AddHeader("Host", dest_host);
req.AddHeader("User-Agent", "Wget/1.11.4");
req.AddHeader("X-Accept-Encoding", "x-i2p-gzip;q=1.0, identity;q=0.5, deflate;q=0, gzip;q=0, *;q=0\r\n");
req.AddHeader("Connection", "close");
if (!m_Etag.empty())
req.add_header("If-None-Match", m_Etag);
req.AddHeader("If-None-Match", m_Etag);
if (!m_LastModified.empty())
req.add_header("If-Modified-Since", m_LastModified);
req.AddHeader("If-Modified-Since", m_LastModified);
/* convert url to relative */
url.schema = "";
url.host = "";
@@ -703,7 +722,9 @@ namespace client
std::string response;
uint8_t recv_buf[4096];
bool end = false;
while (!end) {
int numAttempts = 5;
while (!end)
{
stream->AsyncReceive (boost::asio::buffer (recv_buf, 4096),
[&](const boost::system::error_code& ecode, std::size_t bytes_transferred)
{
@@ -716,60 +737,69 @@ namespace client
30); // wait for 30 seconds
std::unique_lock<std::mutex> l(newDataReceivedMutex);
if (newDataReceived.wait_for (l, std::chrono::seconds (SUBSCRIPTION_REQUEST_TIMEOUT)) == std::cv_status::timeout)
{
LogPrint (eLogError, "Addressbook: subscriptions request timeout expired");
numAttempts++;
if (numAttempts > 5) end = true;
}
}
// process remaining buffer
while (size_t len = stream->ReadSome (recv_buf, sizeof(recv_buf))) {
while (size_t len = stream->ReadSome (recv_buf, sizeof(recv_buf)))
response.append ((char *)recv_buf, len);
}
/* parse response */
i2p::http::HTTPRes res;
int res_head_len = res.parse(response);
if (res_head_len < 0) {
if (res_head_len < 0)
{
LogPrint(eLogError, "Addressbook: can't parse http response from ", dest_host);
return false;
}
if (res_head_len == 0) {
if (res_head_len == 0)
{
LogPrint(eLogError, "Addressbook: incomplete http response from ", dest_host, ", interrupted by timeout");
return false;
}
/* assert: res_head_len > 0 */
response.erase(0, res_head_len);
if (res.code == 304) {
if (res.code == 304)
{
LogPrint (eLogInfo, "Addressbook: no updates from ", dest_host, ", code 304");
return false;
}
if (res.code != 200) {
if (res.code != 200)
{
LogPrint (eLogWarning, "Adressbook: can't get updates from ", dest_host, ", response code ", res.code);
return false;
}
int len = res.content_length();
if (response.empty()) {
if (response.empty())
{
LogPrint(eLogError, "Addressbook: empty response from ", dest_host, ", expected ", len, " bytes");
return false;
}
if (len > 0 && len != (int) response.length()) {
LogPrint(eLogError, "Addressbook: response size mismatch, expected: ", response.length(), ", got: ", len, "bytes");
if (!res.is_gzipped () && len > 0 && len != (int) response.length())
{
LogPrint(eLogError, "Addressbook: response size mismatch, expected: ", len, ", got: ", response.length(), "bytes");
return false;
}
/* assert: res.code == 200 */
auto it = res.headers.find("ETag");
if (it != res.headers.end()) {
m_Etag = it->second;
}
if (it != res.headers.end()) m_Etag = it->second;
it = res.headers.find("If-Modified-Since");
if (it != res.headers.end()) {
m_LastModified = it->second;
}
if (res.is_chunked()) {
if (it != res.headers.end()) m_LastModified = it->second;
if (res.is_chunked())
{
std::stringstream in(response), out;
i2p::http::MergeChunkedResponse (in, out);
response = out.str();
} else if (res.is_gzipped()) {
}
else if (res.is_gzipped())
{
std::stringstream out;
i2p::data::GzipInflator inflator;
inflator.Inflate ((const uint8_t *) response.data(), response.length(), out);
if (out.fail()) {
if (out.fail())
{
LogPrint(eLogError, "Addressbook: can't gunzip http response");
return false;
}
@@ -828,7 +858,7 @@ namespace client
else
memset (response + 8, 0, 32); // not found
memset (response + 40, 0, 4); // set expiration time to zero
m_LocalDestination->GetDatagramDestination ()->SendDatagramTo (response, 44, from.GetIdentHash (), toPort, fromPort);
m_LocalDestination->GetDatagramDestination ()->SendDatagramTo (response, 44, from.GetIdentHash(), toPort, fromPort);
}
void AddressResolver::AddAddress (const std::string& name, const i2p::data::IdentHash& ident)

View File

@@ -18,11 +18,6 @@ namespace i2p
{
namespace client
{
#ifdef MESHNET
const char DEFAULT_SUBSCRIPTION_ADDRESS[] = "http://i42ofzetmgicvui5sshinfckpijix2udewbam4sjo6x5fbukltia.b32.i2p/hosts.txt";
#else
const char DEFAULT_SUBSCRIPTION_ADDRESS[] = "http://joajgazyztfssty4w2on5oaqksz6tqoxbduy553y34mf4byv6gpq.b32.i2p/export/alive-hosts.txt";
#endif
const int INITIAL_SUBSCRIPTION_UPDATE_TIMEOUT = 3; // in minutes
const int INITIAL_SUBSCRIPTION_RETRY_TIMEOUT = 1; // in minutes
const int CONTINIOUS_SUBSCRIPTION_UPDATE_TIMEOUT = 720; // in minutes (12 hours)

28
BOB.cpp
View File

@@ -1,7 +1,7 @@
#include <string.h>
#include <boost/lexical_cast.hpp>
#include "Log.h"
#include "ClientContext.h"
#include "util.h"
#include "BOB.h"
namespace i2p
@@ -437,8 +437,11 @@ namespace client
void BOBCommandSession::GetkeysCommandHandler (const char * operand, size_t len)
{
LogPrint (eLogDebug, "BOB: getkeys");
SendReplyOK (m_Keys.ToBase64 ().c_str ());
}
if (m_Keys.GetPublic ()) // keys are set ?
SendReplyOK (m_Keys.ToBase64 ().c_str ());
else
SendReplyError ("keys are not set");
}
void BOBCommandSession::GetdestCommandHandler (const char * operand, size_t len)
{
@@ -456,7 +459,7 @@ namespace client
void BOBCommandSession::OutportCommandHandler (const char * operand, size_t len)
{
LogPrint (eLogDebug, "BOB: outport ", operand);
m_OutPort = boost::lexical_cast<int>(operand);
m_OutPort = std::stoi(operand);
if (m_OutPort >= 0)
SendReplyOK ("outbound port set");
else
@@ -473,7 +476,7 @@ namespace client
void BOBCommandSession::InportCommandHandler (const char * operand, size_t len)
{
LogPrint (eLogDebug, "BOB: inport ", operand);
m_InPort = boost::lexical_cast<int>(operand);
m_InPort = std::stoi(operand);
if (m_InPort >= 0)
SendReplyOK ("inbound port set");
else
@@ -501,12 +504,12 @@ namespace client
{
LogPrint (eLogDebug, "BOB: lookup ", operand);
i2p::data::IdentHash ident;
if (!context.GetAddressBook ().GetIdentHash (operand, ident) || !m_CurrentDestination)
if (!context.GetAddressBook ().GetIdentHash (operand, ident))
{
SendReplyError ("Address Not found");
return;
}
auto localDestination = m_CurrentDestination->GetLocalDestination ();
auto localDestination = m_CurrentDestination ? m_CurrentDestination->GetLocalDestination () : i2p::client::context.GetSharedLocalDestination ();
auto leaseSet = localDestination->FindLeaseSet (ident);
if (leaseSet)
SendReplyOK (leaseSet->GetIdentity ()->ToBase64 ().c_str ());
@@ -568,10 +571,15 @@ namespace client
{
std::stringstream s;
s << "DATA"; s << " NICKNAME: "; s << m_Nickname;
if (m_CurrentDestination->GetLocalDestination ()->IsReady ())
s << " STARTING: false RUNNING: true STOPPING: false";
if (m_CurrentDestination)
{
if (m_CurrentDestination->GetLocalDestination ()->IsReady ())
s << " STARTING: false RUNNING: true STOPPING: false";
else
s << " STARTING: true RUNNING: false STOPPING: false";
}
else
s << " STARTING: true RUNNING: false STOPPING: false";
s << " STARTING: false RUNNING: false STOPPING: false";
s << " KEYS: true"; s << " QUIET: "; s << (m_IsQuiet ? "true":"false");
if (m_InPort)
{

69
BloomFilter.cpp Normal file
View File

@@ -0,0 +1,69 @@
#include "BloomFilter.h"
#include "I2PEndian.h"
#include <array>
#include <openssl/sha.h>
namespace i2p
{
namespace util
{
/** @brief decaying bloom filter implementation */
class DecayingBloomFilter : public IBloomFilter
{
public:
DecayingBloomFilter(const std::size_t size)
{
m_Size = size;
m_Data = new uint8_t[size];
}
/** @brief implements IBloomFilter::~IBloomFilter */
~DecayingBloomFilter()
{
delete [] m_Data;
}
/** @brief implements IBloomFilter::Add */
bool Add(const uint8_t * data, std::size_t len)
{
std::size_t idx;
uint8_t mask;
Get(data, len, idx, mask);
if(m_Data[idx] & mask) return false; // filter hit
m_Data[idx] |= mask;
return true;
}
/** @brief implements IBloomFilter::Decay */
void Decay()
{
// reset bloom filter buffer
memset(m_Data, 0, m_Size);
}
private:
/** @brief get bit index for for data */
void Get(const uint8_t * data, std::size_t len, std::size_t & idx, uint8_t & bm)
{
bm = 1;
uint8_t digest[32];
// TODO: use blake2 because it's faster
SHA256(data, len, digest);
uint64_t i = buf64toh(digest);
idx = i % m_Size;
bm <<= (i % 8);
}
uint8_t * m_Data;
std::size_t m_Size;
};
BloomFilterPtr BloomFilter(std::size_t capacity)
{
return std::make_shared<DecayingBloomFilter>(capacity);
}
}
}

31
BloomFilter.h Normal file
View File

@@ -0,0 +1,31 @@
#ifndef BLOOM_FILTER_H_
#define BLOOM_FILTER_H_
#include <memory>
#include <cstdint>
namespace i2p
{
namespace util
{
/** @brief interface for bloom filter */
struct IBloomFilter
{
/** @brief destructor */
virtual ~IBloomFilter() {};
/** @brief add entry to bloom filter, return false if filter hit otherwise return true */
virtual bool Add(const uint8_t * data, std::size_t len) = 0;
/** @brief optionally decay old entries */
virtual void Decay() = 0;
};
typedef std::shared_ptr<IBloomFilter> BloomFilterPtr;
/** @brief create bloom filter */
BloomFilterPtr BloomFilter(std::size_t capacity = 1024 * 8);
}
}
#endif

View File

@@ -1,9 +1,73 @@
# for this file format description,
# see https://github.com/olivierlacan/keep-a-changelog
## [2.9.0] - UNRELEASED
## [2.12.0] - 2017-02-14
### Added
- Additional HTTP and SOCKS proxy tunnels
- Reseed from ZIP archive
- Some stats in a main window for Windows version
### Changed
- Reseed servers list
- MTU of 1488 for ipv6
- Android and Mac OS X versions use OpenSSL 1.1
- New logo for Android
### Fixed
- Multiple memory leaks
- Incomptibility of some EdDSA private keys with Java
- Clock skew for Windows XP
- Occasional crashes with I2PSnark
## [2.11.0] - 2016-12-18
### Added
- Websockets support
- Reseed through a floodfill
- Tunnel configuration for HTTP and SOCKS proxy
- Zero-hops tunnels for destinations
- Multiple acceptors for SAM
### Changed
- Reseed servers list
- DHT uses AVX if applicable
- New logo
- LeaseSet lookups
### Fixed
- HTTP Proxy connection reset for Windows
- Crash upon SAM session termination
- Can't connect to a destination for a longer time after restart
- Mass packet loss for UDP tunnels
## [2.10.2] - 2016-12-04
### Fixed
- Fixes UPnP discovery bug, producing excessive CPU usage
- Fixes sudden SSU thread stop for Windows.
## [2.10.1] - 2016-11-07
### Fixed
- Fixed some performance issues for Windows and Android
## [2.10.0] - 2016-10-17
### Added
- Datagram i2p tunnels
- Unique local addresses for server tunnels
- Configurable list of reseed servers and initial addressbook
- Configurable netid
- Initial iOS support
### Changed
- Reduced file descriptiors usage
- Strict reseed checks enabled by default
## Fixed
- Multiple fixes in I2CP and BOB implementations
## [2.9.0] - 2016-08-12
### Changed
- Proxy refactoring & speedup
- Transmission-I2P support
- Graceful shutdown for Windows
- Android without QT
- Reduced number of timers in SSU
- ipv6 peer test support
- Reseed from SU3 file
## [2.8.0] - 2016-06-20
### Added

View File

@@ -8,6 +8,8 @@
#include "Identity.h"
#include "util.h"
#include "ClientContext.h"
#include "SOCKS.h"
#include "WebSocks.h"
namespace i2p
{
@@ -39,20 +41,28 @@ namespace client
m_SharedLocalDestination->Start ();
}
m_AddressBook.Start ();
std::shared_ptr<ClientDestination> localDestination;
bool httproxy; i2p::config::GetOption("httpproxy.enabled", httproxy);
if (httproxy) {
std::string httpProxyKeys; i2p::config::GetOption("httpproxy.keys", httpProxyKeys);
std::string httpProxyAddr; i2p::config::GetOption("httpproxy.address", httpProxyAddr);
uint16_t httpProxyPort; i2p::config::GetOption("httpproxy.port", httpProxyPort);
i2p::data::SigningKeyType sigType; i2p::config::GetOption("httpproxy.signaturetype", sigType);
LogPrint(eLogInfo, "Clients: starting HTTP Proxy at ", httpProxyAddr, ":", httpProxyPort);
if (httpProxyKeys.length () > 0)
{
i2p::data::PrivateKeys keys;
LoadPrivateKeys (keys, httpProxyKeys);
localDestination = CreateNewLocalDestination (keys, false);
if(LoadPrivateKeys (keys, httpProxyKeys, sigType))
{
std::map<std::string, std::string> params;
ReadI2CPOptionsFromConfig ("httpproxy.", params);
localDestination = CreateNewLocalDestination (keys, false, &params);
}
else
LogPrint(eLogError, "Clients: failed to load HTTP Proxy key");
}
try {
m_HttpProxy = new i2p::proxy::HTTPProxy(httpProxyAddr, httpProxyPort, localDestination);
@@ -61,20 +71,29 @@ namespace client
LogPrint(eLogError, "Clients: Exception in HTTP Proxy: ", e.what());
}
}
localDestination = nullptr;
bool socksproxy; i2p::config::GetOption("socksproxy.enabled", socksproxy);
if (socksproxy) {
if (socksproxy)
{
std::string socksProxyKeys; i2p::config::GetOption("socksproxy.keys", socksProxyKeys);
std::string socksProxyAddr; i2p::config::GetOption("socksproxy.address", socksProxyAddr);
uint16_t socksProxyPort; i2p::config::GetOption("socksproxy.port", socksProxyPort);
std::string socksOutProxyAddr; i2p::config::GetOption("socksproxy.outproxy", socksOutProxyAddr);
uint16_t socksOutProxyPort; i2p::config::GetOption("socksproxy.outproxyport", socksOutProxyPort);
i2p::data::SigningKeyType sigType; i2p::config::GetOption("socksproxy.signaturetype", sigType);
LogPrint(eLogInfo, "Clients: starting SOCKS Proxy at ", socksProxyAddr, ":", socksProxyPort);
if (socksProxyKeys.length () > 0)
{
i2p::data::PrivateKeys keys;
LoadPrivateKeys (keys, socksProxyKeys);
localDestination = CreateNewLocalDestination (keys, false);
if (LoadPrivateKeys (keys, socksProxyKeys, sigType))
{
std::map<std::string, std::string> params;
ReadI2CPOptionsFromConfig ("socksproxy.", params);
localDestination = CreateNewLocalDestination (keys, false, &params);
}
else
LogPrint(eLogError, "Clients: failed to load SOCKS Proxy key");
}
try {
m_SocksProxy = new i2p::proxy::SOCKSProxy(socksProxyAddr, socksProxyPort, socksOutProxyAddr, socksOutProxyPort, localDestination);
@@ -83,10 +102,10 @@ namespace client
LogPrint(eLogError, "Clients: Exception in SOCKS Proxy: ", e.what());
}
}
// I2P tunnels
ReadTunnels ();
// I2P tunnels
ReadTunnels ();
// SAM
bool sam; i2p::config::GetOption("sam.enabled", sam);
if (sam) {
@@ -134,6 +153,13 @@ namespace client
}
m_AddressBook.StartResolvers ();
// start UDP cleanup
if (!m_ServerForwards.empty ())
{
m_CleanupUDPTimer.reset (new boost::asio::deadline_timer(m_SharedLocalDestination->GetService ()));
ScheduleCleanupUDP();
}
}
void ClientContext::Stop ()
@@ -193,20 +219,37 @@ namespace client
}
LogPrint(eLogInfo, "Clients: stopping AddressBook");
m_AddressBook.Stop ();
m_AddressBook.Stop ();
{
std::lock_guard<std::mutex> lock(m_ForwardsMutex);
m_ServerForwards.clear();
m_ClientForwards.clear();
}
if (m_CleanupUDPTimer)
{
m_CleanupUDPTimer->cancel ();
m_CleanupUDPTimer = nullptr;
}
for (auto& it: m_Destinations)
it.second->Stop ();
m_Destinations.clear ();
m_SharedLocalDestination = nullptr;
m_SharedLocalDestination = nullptr;
}
void ClientContext::ReloadConfig ()
{
ReadTunnels (); // TODO: it reads new tunnels only, should be implemented better
std::string config; i2p::config::GetOption("conf", config);
i2p::config::ParseConfig(config);
Stop();
Start();
}
void ClientContext::LoadPrivateKeys (i2p::data::PrivateKeys& keys, const std::string& filename, i2p::data::SigningKeyType sigType)
bool ClientContext::LoadPrivateKeys (i2p::data::PrivateKeys& keys, const std::string& filename, i2p::data::SigningKeyType sigType)
{
bool success = true;
std::string fullPath = i2p::fs::DataDirPath (filename);
std::ifstream s(fullPath, std::ifstream::binary);
if (s.is_open ())
@@ -216,9 +259,14 @@ namespace client
s.seekg (0, std::ios::beg);
uint8_t * buf = new uint8_t[len];
s.read ((char *)buf, len);
keys.FromBuffer (buf, len);
if(!keys.FromBuffer (buf, len))
{
LogPrint (eLogError, "Clients: failed to load keyfile ", filename);
success = false;
}
else
LogPrint (eLogInfo, "Clients: Local address ", m_AddressBook.ToAddress(keys.GetPublic ()->GetIdentHash ()), " loaded");
delete[] buf;
LogPrint (eLogInfo, "Clients: Local address ", m_AddressBook.ToAddress(keys.GetPublic ()->GetIdentHash ()), " loaded");
}
else
{
@@ -232,9 +280,33 @@ namespace client
delete[] buf;
LogPrint (eLogInfo, "Clients: New private keys file ", fullPath, " for ", m_AddressBook.ToAddress(keys.GetPublic ()->GetIdentHash ()), " created");
}
}
return success;
}
std::vector<std::shared_ptr<DatagramSessionInfo> > ClientContext::GetForwardInfosFor(const i2p::data::IdentHash & destination)
{
std::vector<std::shared_ptr<DatagramSessionInfo> > infos;
std::lock_guard<std::mutex> lock(m_ForwardsMutex);
for(const auto & c : m_ClientForwards)
{
if (c.second->IsLocalDestination(destination))
{
for (auto & i : c.second->GetSessions()) infos.push_back(i);
break;
}
}
for(const auto & s : m_ServerForwards)
{
if(std::get<0>(s.first) == destination)
{
for( auto & i : s.second->GetSessions()) infos.push_back(i);
break;
}
}
return infos;
}
std::shared_ptr<ClientDestination> ClientContext::CreateNewLocalDestination (bool isPublic, i2p::data::SigningKeyType sigType,
const std::map<std::string, std::string> * params)
{
@@ -304,6 +376,25 @@ namespace client
options[I2CP_PARAM_INBOUND_TUNNELS_QUANTITY] = GetI2CPOption (section, I2CP_PARAM_INBOUND_TUNNELS_QUANTITY, DEFAULT_INBOUND_TUNNELS_QUANTITY);
options[I2CP_PARAM_OUTBOUND_TUNNELS_QUANTITY] = GetI2CPOption (section, I2CP_PARAM_OUTBOUND_TUNNELS_QUANTITY, DEFAULT_OUTBOUND_TUNNELS_QUANTITY);
options[I2CP_PARAM_TAGS_TO_SEND] = GetI2CPOption (section, I2CP_PARAM_TAGS_TO_SEND, DEFAULT_TAGS_TO_SEND);
options[I2CP_PARAM_MIN_TUNNEL_LATENCY] = GetI2CPOption(section, I2CP_PARAM_MIN_TUNNEL_LATENCY, DEFAULT_MIN_TUNNEL_LATENCY);
options[I2CP_PARAM_MAX_TUNNEL_LATENCY] = GetI2CPOption(section, I2CP_PARAM_MAX_TUNNEL_LATENCY, DEFAULT_MAX_TUNNEL_LATENCY);
}
void ClientContext::ReadI2CPOptionsFromConfig (const std::string& prefix, std::map<std::string, std::string>& options) const
{
std::string value;
if (i2p::config::GetOption(prefix + I2CP_PARAM_INBOUND_TUNNEL_LENGTH, value))
options[I2CP_PARAM_INBOUND_TUNNEL_LENGTH] = value;
if (i2p::config::GetOption(prefix + I2CP_PARAM_INBOUND_TUNNELS_QUANTITY, value))
options[I2CP_PARAM_INBOUND_TUNNELS_QUANTITY] = value;
if (i2p::config::GetOption(prefix + I2CP_PARAM_OUTBOUND_TUNNEL_LENGTH, value))
options[I2CP_PARAM_OUTBOUND_TUNNEL_LENGTH] = value;
if (i2p::config::GetOption(prefix + I2CP_PARAM_OUTBOUND_TUNNELS_QUANTITY, value))
options[I2CP_PARAM_OUTBOUND_TUNNELS_QUANTITY] = value;
if (i2p::config::GetOption(prefix + I2CP_PARAM_MIN_TUNNEL_LATENCY, value))
options[I2CP_PARAM_MIN_TUNNEL_LATENCY] = value;
if (i2p::config::GetOption(prefix + I2CP_PARAM_MAX_TUNNEL_LATENCY, value))
options[I2CP_PARAM_MAX_TUNNEL_LATENCY] = value;
}
void ClientContext::ReadTunnels ()
@@ -337,10 +428,16 @@ namespace client
try
{
std::string type = section.second.get<std::string> (I2P_TUNNELS_SECTION_TYPE);
if (type == I2P_TUNNELS_SECTION_TYPE_CLIENT)
if (type == I2P_TUNNELS_SECTION_TYPE_CLIENT
|| type == I2P_TUNNELS_SECTION_TYPE_SOCKS
|| type == I2P_TUNNELS_SECTION_TYPE_WEBSOCKS
|| type == I2P_TUNNELS_SECTION_TYPE_HTTPPROXY
|| type == I2P_TUNNELS_SECTION_TYPE_UDPCLIENT)
{
// mandatory params
std::string dest = section.second.get<std::string> (I2P_CLIENT_TUNNEL_DESTINATION);
std::string dest;
if (type == I2P_TUNNELS_SECTION_TYPE_CLIENT || type == I2P_TUNNELS_SECTION_TYPE_UDPCLIENT)
dest = section.second.get<std::string> (I2P_CLIENT_TUNNEL_DESTINATION);
int port = section.second.get<int> (I2P_CLIENT_TUNNEL_PORT);
// optional params
std::string keys = section.second.get (I2P_CLIENT_TUNNEL_KEYS, "");
@@ -355,22 +452,69 @@ namespace client
if (keys.length () > 0)
{
i2p::data::PrivateKeys k;
LoadPrivateKeys (k, keys, sigType);
localDestination = FindLocalDestination (k.GetPublic ()->GetIdentHash ());
if(LoadPrivateKeys (k, keys, sigType))
{
localDestination = FindLocalDestination (k.GetPublic ()->GetIdentHash ());
if (!localDestination)
localDestination = CreateNewLocalDestination (k, type == I2P_TUNNELS_SECTION_TYPE_UDPCLIENT, &options);
}
}
if (type == I2P_TUNNELS_SECTION_TYPE_UDPCLIENT) {
// udp client
// TODO: hostnames
boost::asio::ip::udp::endpoint end(boost::asio::ip::address::from_string(address), port);
if (!localDestination)
localDestination = CreateNewLocalDestination (k, false, &options);
{
localDestination = m_SharedLocalDestination;
}
auto clientTunnel = new I2PUDPClientTunnel(name, dest, end, localDestination, destinationPort);
if(m_ClientForwards.insert(std::make_pair(end, std::unique_ptr<I2PUDPClientTunnel>(clientTunnel))).second)
{
clientTunnel->Start();
}
else
LogPrint(eLogError, "Clients: I2P Client forward for endpoint ", end, " already exists");
} else {
boost::asio::ip::tcp::endpoint clientEndpoint;
I2PService * clientTunnel = nullptr;
if (type == I2P_TUNNELS_SECTION_TYPE_SOCKS)
{
// socks proxy
clientTunnel = new i2p::proxy::SOCKSProxy(address, port, "", destinationPort, localDestination);
clientEndpoint = ((i2p::proxy::SOCKSProxy*)clientTunnel)->GetAcceptor().local_endpoint();
}
else if (type == I2P_TUNNELS_SECTION_TYPE_HTTPPROXY)
{
// http proxy
clientTunnel = new i2p::proxy::HTTPProxy(address, port, localDestination);
clientEndpoint = ((i2p::proxy::HTTPProxy*)clientTunnel)->GetAcceptor().local_endpoint();
}
else if (type == I2P_TUNNELS_SECTION_TYPE_WEBSOCKS)
{
// websocks proxy
clientTunnel = new WebSocks(address, port, localDestination);;
clientEndpoint = ((WebSocks*)clientTunnel)->GetLocalEndpoint();
}
else
{
// tcp client
clientTunnel = new I2PClientTunnel (name, dest, address, port, localDestination, destinationPort);
clientEndpoint = ((I2PClientTunnel*)clientTunnel)->GetAcceptor().local_endpoint();
}
if (m_ClientTunnels.insert (std::make_pair (clientEndpoint, std::unique_ptr<I2PService>(clientTunnel))).second)
{
clientTunnel->Start ();
numClientTunnels++;
}
else
LogPrint (eLogError, "Clients: I2P client tunnel for endpoint ", clientEndpoint, "already exists");
}
auto clientTunnel = new I2PClientTunnel (name, dest, address, port, localDestination, destinationPort);
if (m_ClientTunnels.insert (std::make_pair (clientTunnel->GetAcceptor ().local_endpoint (),
std::unique_ptr<I2PClientTunnel>(clientTunnel))).second)
{
clientTunnel->Start ();
numClientTunnels++;
}
else
LogPrint (eLogError, "Clients: I2P client tunnel for endpoint ", clientTunnel->GetAcceptor ().local_endpoint (), " already exists");
}
else if (type == I2P_TUNNELS_SECTION_TYPE_SERVER || type == I2P_TUNNELS_SECTION_TYPE_HTTP || type == I2P_TUNNELS_SECTION_TYPE_IRC)
else if (type == I2P_TUNNELS_SECTION_TYPE_SERVER
|| type == I2P_TUNNELS_SECTION_TYPE_HTTP
|| type == I2P_TUNNELS_SECTION_TYPE_IRC
|| type == I2P_TUNNELS_SECTION_TYPE_UDPSERVER)
{
// mandatory params
std::string host = section.second.get<std::string> (I2P_SERVER_TUNNEL_HOST);
@@ -383,17 +527,49 @@ namespace client
std::string webircpass = section.second.get<std::string> (I2P_SERVER_TUNNEL_WEBIRC_PASSWORD, "");
bool gzip = section.second.get (I2P_SERVER_TUNNEL_GZIP, true);
i2p::data::SigningKeyType sigType = section.second.get (I2P_SERVER_TUNNEL_SIGNATURE_TYPE, i2p::data::SIGNING_KEY_TYPE_ECDSA_SHA256_P256);
uint32_t maxConns = section.second.get(i2p::stream::I2CP_PARAM_STREAMING_MAX_CONNS_PER_MIN, i2p::stream::DEFAULT_MAX_CONNS_PER_MIN);
std::string address = section.second.get<std::string> (I2P_SERVER_TUNNEL_ADDRESS, "127.0.0.1");
bool isUniqueLocal = section.second.get(I2P_SERVER_TUNNEL_ENABLE_UNIQUE_LOCAL, true);
// I2CP
std::map<std::string, std::string> options;
ReadI2CPOptions (section, options);
std::shared_ptr<ClientDestination> localDestination = nullptr;
i2p::data::PrivateKeys k;
LoadPrivateKeys (k, keys, sigType);
if(!LoadPrivateKeys (k, keys, sigType))
continue;
localDestination = FindLocalDestination (k.GetPublic ()->GetIdentHash ());
if (!localDestination)
localDestination = CreateNewLocalDestination (k, true, &options);
if (type == I2P_TUNNELS_SECTION_TYPE_UDPSERVER)
{
// udp server tunnel
// TODO: hostnames
auto localAddress = boost::asio::ip::address::from_string(address);
boost::asio::ip::udp::endpoint endpoint(boost::asio::ip::address::from_string(host), port);
I2PUDPServerTunnel * serverTunnel = new I2PUDPServerTunnel(name, localDestination, localAddress, endpoint, port);
if(!isUniqueLocal)
{
LogPrint(eLogInfo, "Clients: disabling loopback address mapping");
serverTunnel->SetUniqueLocal(isUniqueLocal);
}
std::lock_guard<std::mutex> lock(m_ForwardsMutex);
if(m_ServerForwards.insert(
std::make_pair(
std::make_pair(
localDestination->GetIdentHash(), port),
std::unique_ptr<I2PUDPServerTunnel>(serverTunnel))).second)
{
serverTunnel->Start();
LogPrint(eLogInfo, "Clients: I2P Server Forward created for UDP Endpoint ", host, ":", port, " bound on ", address, " for ",localDestination->GetIdentHash().ToBase32());
}
else
LogPrint(eLogError, "Clients: I2P Server Forward for destination/port ", m_AddressBook.ToAddress(localDestination->GetIdentHash()), "/", port, "already exists");
continue;
}
I2PServerTunnel * serverTunnel;
if (type == I2P_TUNNELS_SECTION_TYPE_HTTP)
serverTunnel = new I2PServerTunnelHTTP (name, host, port, localDestination, hostOverride, inPort, gzip);
@@ -402,6 +578,14 @@ namespace client
else // regular server tunnel by default
serverTunnel = new I2PServerTunnel (name, host, port, localDestination, inPort, gzip);
LogPrint(eLogInfo, "Clients: Set Max Conns To ", maxConns);
serverTunnel->SetMaxConnsPerMinute(maxConns);
if(!isUniqueLocal)
{
LogPrint(eLogInfo, "Clients: disabling loopback address mapping");
serverTunnel->SetUniqueLocal(isUniqueLocal);
}
if (accessList.length () > 0)
{
std::set<i2p::data::IdentHash> idents;
@@ -439,6 +623,26 @@ namespace client
}
LogPrint (eLogInfo, "Clients: ", numClientTunnels, " I2P client tunnels created");
LogPrint (eLogInfo, "Clients: ", numServerTunnels, " I2P server tunnels created");
}
}
void ClientContext::ScheduleCleanupUDP()
{
if (m_CleanupUDPTimer)
{
// schedule cleanup in 17 seconds
m_CleanupUDPTimer->expires_from_now (boost::posix_time::seconds (17));
m_CleanupUDPTimer->async_wait(std::bind(&ClientContext::CleanupUDP, this, std::placeholders::_1));
}
}
void ClientContext::CleanupUDP(const boost::system::error_code & ecode)
{
if(!ecode)
{
std::lock_guard<std::mutex> lock(m_ForwardsMutex);
for (auto & s : m_ServerForwards ) s.second->ExpireStale();
ScheduleCleanupUDP();
}
}
}
}

View File

@@ -24,6 +24,11 @@ namespace client
const char I2P_TUNNELS_SECTION_TYPE_SERVER[] = "server";
const char I2P_TUNNELS_SECTION_TYPE_HTTP[] = "http";
const char I2P_TUNNELS_SECTION_TYPE_IRC[] = "irc";
const char I2P_TUNNELS_SECTION_TYPE_UDPCLIENT[] = "udpclient";
const char I2P_TUNNELS_SECTION_TYPE_UDPSERVER[] = "udpserver";
const char I2P_TUNNELS_SECTION_TYPE_SOCKS[] = "socks";
const char I2P_TUNNELS_SECTION_TYPE_WEBSOCKS[] = "websocks";
const char I2P_TUNNELS_SECTION_TYPE_HTTPPROXY[] = "httpproxy";
const char I2P_CLIENT_TUNNEL_PORT[] = "port";
const char I2P_CLIENT_TUNNEL_ADDRESS[] = "address";
const char I2P_CLIENT_TUNNEL_DESTINATION[] = "destination";
@@ -39,6 +44,8 @@ namespace client
const char I2P_SERVER_TUNNEL_ACCESS_LIST[] = "accesslist";
const char I2P_SERVER_TUNNEL_GZIP[] = "gzip";
const char I2P_SERVER_TUNNEL_WEBIRC_PASSWORD[] = "webircpassword";
const char I2P_SERVER_TUNNEL_ADDRESS[] = "address";
const char I2P_SERVER_TUNNEL_ENABLE_UNIQUE_LOCAL[] = "enableuniquelocal";
class ClientContext
{
@@ -59,19 +66,26 @@ namespace client
const std::map<std::string, std::string> * params = nullptr);
void DeleteLocalDestination (std::shared_ptr<ClientDestination> destination);
std::shared_ptr<ClientDestination> FindLocalDestination (const i2p::data::IdentHash& destination) const;
void LoadPrivateKeys (i2p::data::PrivateKeys& keys, const std::string& filename, i2p::data::SigningKeyType sigType = i2p::data::SIGNING_KEY_TYPE_ECDSA_SHA256_P256);
bool LoadPrivateKeys (i2p::data::PrivateKeys& keys, const std::string& filename, i2p::data::SigningKeyType sigType = i2p::data::SIGNING_KEY_TYPE_ECDSA_SHA256_P256);
AddressBook& GetAddressBook () { return m_AddressBook; };
const SAMBridge * GetSAMBridge () const { return m_SamBridge; };
const I2CPServer * GetI2CPServer () const { return m_I2CPServer; };
std::vector<std::shared_ptr<DatagramSessionInfo> > GetForwardInfosFor(const i2p::data::IdentHash & destination);
private:
void ReadTunnels ();
template<typename Section, typename Type>
std::string GetI2CPOption (const Section& section, const std::string& name, const Type& value) const;
template<typename Section>
void ReadI2CPOptions (const Section& section, std::map<std::string, std::string>& options) const;
void ReadI2CPOptions (const Section& section, std::map<std::string, std::string>& options) const;
void ReadI2CPOptionsFromConfig (const std::string& prefix, std::map<std::string, std::string>& options) const;
void CleanupUDP(const boost::system::error_code & ecode);
void ScheduleCleanupUDP();
private:
std::mutex m_DestinationsMutex;
@@ -82,17 +96,28 @@ namespace client
i2p::proxy::HTTPProxy * m_HttpProxy;
i2p::proxy::SOCKSProxy * m_SocksProxy;
std::map<boost::asio::ip::tcp::endpoint, std::unique_ptr<I2PClientTunnel> > m_ClientTunnels; // local endpoint->tunnel
std::map<boost::asio::ip::tcp::endpoint, std::unique_ptr<I2PService> > m_ClientTunnels; // local endpoint->tunnel
std::map<std::pair<i2p::data::IdentHash, int>, std::unique_ptr<I2PServerTunnel> > m_ServerTunnels; // <destination,port>->tunnel
std::mutex m_ForwardsMutex;
std::map<boost::asio::ip::udp::endpoint, std::unique_ptr<I2PUDPClientTunnel> > m_ClientForwards; // local endpoint -> udp tunnel
std::map<std::pair<i2p::data::IdentHash, int>, std::unique_ptr<I2PUDPServerTunnel> > m_ServerForwards; // <destination,port> -> udp tunnel
SAMBridge * m_SamBridge;
BOBCommandChannel * m_BOBCommandChannel;
I2CPServer * m_I2CPServer;
std::unique_ptr<boost::asio::deadline_timer> m_CleanupUDPTimer;
public:
// for HTTP
const decltype(m_Destinations)& GetDestinations () const { return m_Destinations; };
const decltype(m_ClientTunnels)& GetClientTunnels () const { return m_ClientTunnels; };
const decltype(m_ServerTunnels)& GetServerTunnels () const { return m_ServerTunnels; };
const decltype(m_ClientForwards)& GetClientForwards () const { return m_ClientForwards; }
const decltype(m_ServerForwards)& GetServerForwards () const { return m_ServerForwards; }
const i2p::proxy::HTTPProxy * GetHttpProxy () const { return m_HttpProxy; }
const i2p::proxy::SOCKSProxy * GetSocksProxy () const { return m_SocksProxy; }
};
extern ClientContext context;

View File

@@ -16,6 +16,7 @@
#include <boost/program_options/parsers.hpp>
#include <boost/program_options/variables_map.hpp>
#include "Identity.h"
#include "Config.h"
#include "version.h"
@@ -27,10 +28,6 @@ namespace config {
variables_map m_Options;
void Init() {
bool nat = true;
#ifdef MESHNET
nat = false;
#endif
options_description general("General options");
general.add_options()
@@ -44,27 +41,32 @@ namespace config {
("family", value<std::string>()->default_value(""), "Specify a family, router belongs to")
("datadir", value<std::string>()->default_value(""), "Path to storage of i2pd data (RI, keys, peer profiles, ...)")
("host", value<std::string>()->default_value("0.0.0.0"), "External IP")
("ifname", value<std::string>()->default_value(""), "network interface to bind to")
("nat", value<bool>()->zero_tokens()->default_value(nat), "should we assume we are behind NAT?")
("ifname", value<std::string>()->default_value(""), "Network interface to bind to")
("ifname4", value<std::string>()->default_value(""), "Network interface to bind to for ipv4")
("ifname6", value<std::string>()->default_value(""), "Network interface to bind to for ipv6")
("nat", value<bool>()->zero_tokens()->default_value(true), "Should we assume we are behind NAT?")
("port", value<uint16_t>()->default_value(0), "Port to listen for incoming connections (default: auto)")
("ipv4", value<bool>()->zero_tokens()->default_value(true), "Enable communication through ipv4")
("ipv6", value<bool>()->zero_tokens()->default_value(false), "Enable communication through ipv6")
("netid", value<int>()->default_value(I2PD_NET_ID), "Specify NetID. Main I2P is 2")
("daemon", value<bool>()->zero_tokens()->default_value(false), "Router will go to background after start")
("service", value<bool>()->zero_tokens()->default_value(false), "Router will use system folders like '/var/lib/i2pd'")
("notransit", value<bool>()->zero_tokens()->default_value(false), "Router will not accept transit tunnels at startup")
("floodfill", value<bool>()->zero_tokens()->default_value(false), "Router will be floodfill")
("bandwidth", value<std::string>()->default_value(""), "Bandwidth limit: integer in kbps or letters: L (32), O (256), P (2048), X (>9000)")
("ntcp", value<bool>()->zero_tokens()->default_value(true), "enable ntcp transport")
("ssu", value<bool>()->zero_tokens()->default_value(true), "enable ssu transport")
("ntcp", value<bool>()->zero_tokens()->default_value(true), "Enable NTCP transport")
("ssu", value<bool>()->zero_tokens()->default_value(true), "Enable SSU transport")
#ifdef _WIN32
("svcctl", value<std::string>()->default_value(""), "Windows service management ('install' or 'remove')")
("insomnia", value<bool>()->zero_tokens()->default_value(false), "Prevent system from sleeping")
("close", value<std::string>()->default_value("ask"), "Action on close: minimize, exit, ask") // TODO: add custom validator or something
#endif
;
options_description limits("Limits options");
limits.add_options()
("limits.coresize", value<uint32_t>()->default_value(0), "Maximum size of corefile in Kb (0 - use system limit)")
("limits.openfiles", value<uint16_t>()->default_value(0), "Maximum number of open files (0 - use system default)")
("limits.transittunnels", value<uint16_t>()->default_value(2500), "Maximum active transit sessions (default:2500)")
;
@@ -84,6 +86,14 @@ namespace config {
("httpproxy.address", value<std::string>()->default_value("127.0.0.1"), "HTTP Proxy listen address")
("httpproxy.port", value<uint16_t>()->default_value(4444), "HTTP Proxy listen port")
("httpproxy.keys", value<std::string>()->default_value(""), "File to persist HTTP Proxy keys")
("httpproxy.signaturetype", value<i2p::data::SigningKeyType>()->default_value(i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519), "Signature type for new keys. 7 (EdDSA) by default")
("httpproxy.inbound.length", value<std::string>()->default_value("3"), "HTTP proxy inbound tunnel length")
("httpproxy.outbound.length", value<std::string>()->default_value("3"), "HTTP proxy outbound tunnel length")
("httpproxy.inbound.quantity", value<std::string>()->default_value("5"), "HTTP proxy inbound tunnels quantity")
("httpproxy.outbound.quantity", value<std::string>()->default_value("5"), "HTTP proxy outbound tunnels quantity")
("httpproxy.latency.min", value<std::string>()->default_value("0"), "HTTP proxy min 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")
;
options_description socksproxy("SOCKS Proxy options");
@@ -92,7 +102,14 @@ namespace config {
("socksproxy.address", value<std::string>()->default_value("127.0.0.1"), "SOCKS Proxy listen address")
("socksproxy.port", value<uint16_t>()->default_value(4447), "SOCKS Proxy listen port")
("socksproxy.keys", value<std::string>()->default_value(""), "File to persist SOCKS Proxy keys")
("socksproxy.outproxy", value<std::string>()->default_value("127.0.0.1"), "Upstream outproxy address for SOCKS Proxy")
("socksproxy.signaturetype", value<i2p::data::SigningKeyType>()->default_value(i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519), "Signature type for new keys. 7 (EdDSA) by default")
("socksproxy.inbound.length", value<std::string>()->default_value("3"), "SOCKS proxy inbound tunnel length")
("socksproxy.outbound.length", value<std::string>()->default_value("3"), "SOCKS proxy outbound tunnel length")
("socksproxy.inbound.quantity", value<std::string>()->default_value("5"), "SOCKS proxy inbound tunnels quantity")
("socksproxy.outbound.quantity", value<std::string>()->default_value("5"), "SOCKS proxy outbound tunnels quantity")
("socksproxy.latency.min", value<std::string>()->default_value("0"), "SOCKS proxy min latency for tunnels")
("socksproxy.latency.max", value<std::string>()->default_value("0"), "SOCKS proxy max latency for tunnels")
("socksproxy.outproxy", value<std::string>()->default_value("127.0.0.1"), "Upstream outproxy address for SOCKS Proxy")
("socksproxy.outproxyport", value<uint16_t>()->default_value(9050), "Upstream outproxy port for SOCKS Proxy")
;
@@ -150,15 +167,49 @@ namespace config {
options_description reseed("Reseed options");
reseed.add_options()
("reseed.file", value<std::string>()->default_value(""), "Path to .su3 file")
("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.floodfill", value<std::string>()->default_value(""), "Path to router info of floodfill 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.urls", value<std::string>()->default_value(
"https://reseed.i2p-projekt.de/,"
"https://i2p.mooo.com/netDb/,"
"https://netdb.i2p2.no/,"
// "https://us.reseed.i2p2.no:444/," // mamoth's shit
// "https://uk.reseed.i2p2.no:444/," // mamoth's shit
"https://i2p-0.manas.ca:8443/,"
"https://reseed.i2p.vzaws.com:8443/,"
"https://download.xxlspeed.com/,"
"https://reseed-ru.lngserv.ru/,"
"https://reseed.atomike.ninja/,"
"https://reseed.memcpy.io/,"
"https://reseed.onion.im/,"
"https://itoopie.atomike.ninja/"
), "Reseed URLs, separated by comma")
;
options_description addressbook("AddressBook options");
addressbook.add_options()
("addressbook.defaulturl", value<std::string>()->default_value(
"http://joajgazyztfssty4w2on5oaqksz6tqoxbduy553y34mf4byv6gpq.b32.i2p/export/alive-hosts.txt"
), "AddressBook subscription URL for initial setup")
("addressbook.subscriptions", value<std::string>()->default_value(""),
"AddressBook subscriptions URLs, separated by comma");
options_description trust("Trust options");
trust.add_options()
("trust.enabled", value<bool>()->default_value(false), "enable explicit trust options")
("trust.enabled", value<bool>()->default_value(false), "Enable explicit trust options")
("trust.family", value<std::string>()->default_value(""), "Router Familiy to trust for first hops")
("trust.hidden", value<bool>()->default_value(false), "should we hide our router from other routers?");
("trust.routers", value<std::string>()->default_value(""), "Only Connect to these routers")
("trust.hidden", value<bool>()->default_value(false), "Should we hide our router from other routers?");
options_description websocket("Websocket Options");
websocket.add_options()
("websockets.enabled", value<bool>()->default_value(false), "enable websocket server")
("websockets.address", value<std::string>()->default_value("127.0.0.1"), "address to bind websocket server on")
("websockets.port", value<uint16_t>()->default_value(7666), "port to bind websocket server on");
m_OptionsDesc
.add(general)
.add(limits)
@@ -172,7 +223,9 @@ namespace config {
.add(upnp)
.add(precomputation)
.add(reseed)
.add(trust)
.add(addressbook)
.add(trust)
.add(websocket)
;
}

View File

@@ -78,6 +78,12 @@ namespace config {
return true;
}
template<typename T>
bool GetOption(const std::string& name, T& value)
{
return GetOption (name.c_str (), value);
}
/**
* @brief Set value of given parameter
* @param name Name of settable parameter
@@ -93,7 +99,7 @@ namespace config {
m_Options.at(name).value() = value;
notify(m_Options);
return true;
}
}
/**
* @brief Check is value explicitly given or default

View File

@@ -135,11 +135,8 @@ namespace crypto
DSA * CreateDSA ()
{
DSA * dsa = DSA_new ();
dsa->p = BN_dup (dsap);
dsa->q = BN_dup (dsaq);
dsa->g = BN_dup (dsag);
dsa->priv_key = NULL;
dsa->pub_key = NULL;
DSA_set0_pqg (dsa, BN_dup (dsap), BN_dup (dsaq), BN_dup (dsag));
DSA_set0_key (dsa, NULL, NULL);
return dsa;
}
@@ -227,13 +224,11 @@ namespace crypto
// DH
DHKeys::DHKeys (): m_IsUpdated (true)
DHKeys::DHKeys ()
{
m_DH = DH_new ();
m_DH->p = BN_dup (elgp);
m_DH->g = BN_dup (elgg);
m_DH->priv_key = NULL;
m_DH->pub_key = NULL;
DH_set0_pqg (m_DH, BN_dup (elgp), NULL, BN_dup (elgg));
DH_set0_key (m_DH, NULL, NULL);
}
DHKeys::~DHKeys ()
@@ -241,41 +236,32 @@ namespace crypto
DH_free (m_DH);
}
void DHKeys::GenerateKeys (uint8_t * priv, uint8_t * pub)
void DHKeys::GenerateKeys ()
{
if (m_DH->priv_key) { BN_free (m_DH->priv_key); m_DH->priv_key = NULL; };
if (m_DH->pub_key) { BN_free (m_DH->pub_key); m_DH->pub_key = NULL; };
BIGNUM * priv_key = NULL, * pub_key = NULL;
#if !defined(__x86_64__) // use short exponent for non x64
m_DH->priv_key = BN_new ();
BN_rand (m_DH->priv_key, ELGAMAL_SHORT_EXPONENT_NUM_BITS, 0, 1);
priv_key = BN_new ();
BN_rand (priv_key, ELGAMAL_SHORT_EXPONENT_NUM_BITS, 0, 1);
#endif
if (g_ElggTable)
{
#if defined(__x86_64__)
m_DH->priv_key = BN_new ();
BN_rand (m_DH->priv_key, ELGAMAL_FULL_EXPONENT_NUM_BITS, 0, 1);
priv_key = BN_new ();
BN_rand (priv_key, ELGAMAL_FULL_EXPONENT_NUM_BITS, 0, 1);
#endif
auto ctx = BN_CTX_new ();
m_DH->pub_key = ElggPow (m_DH->priv_key, g_ElggTable, ctx);
pub_key = ElggPow (priv_key, g_ElggTable, ctx);
DH_set0_key (m_DH, pub_key, priv_key);
BN_CTX_free (ctx);
}
else
{
DH_set0_key (m_DH, NULL, priv_key);
DH_generate_key (m_DH);
if (priv) bn2buf (m_DH->priv_key, priv, 256);
if (pub) bn2buf (m_DH->pub_key, pub, 256);
m_IsUpdated = true;
}
const uint8_t * DHKeys::GetPublicKey ()
{
if (m_IsUpdated)
{
bn2buf (m_DH->pub_key, m_PublicKey, 256);
BN_free (m_DH->pub_key); m_DH->pub_key = NULL;
m_IsUpdated= false;
DH_get0_key (m_DH, (const BIGNUM **)&pub_key, (const BIGNUM **)&priv_key);
}
return m_PublicKey;
bn2buf (pub_key, m_PublicKey, 256);
}
void DHKeys::Agree (const uint8_t * pub, uint8_t * shared)
@@ -286,10 +272,9 @@ namespace crypto
}
// ElGamal
ElGamalEncryption::ElGamalEncryption (const uint8_t * key)
void ElGamalEncrypt (const uint8_t * key, const uint8_t * data, uint8_t * encrypted, bool zeroPadding)
{
ctx = BN_CTX_new ();
BN_CTX * ctx = BN_CTX_new ();
// select random k
BIGNUM * k = BN_new ();
#if defined(__x86_64__)
@@ -298,6 +283,7 @@ namespace crypto
BN_rand (k, ELGAMAL_SHORT_EXPONENT_NUM_BITS, -1, 1); // short exponent of 226 bits
#endif
// calculate a
BIGNUM * a;
if (g_ElggTable)
a = ElggPow (k, g_ElggTable, ctx);
else
@@ -309,30 +295,20 @@ namespace crypto
BIGNUM * y = BN_new ();
BN_bin2bn (key, 256, y);
// calculate b1
b1 = BN_new ();
BIGNUM * b1 = BN_new ();
BN_mod_exp (b1, y, k, elgp, ctx);
BN_free (y);
BN_free (k);
}
ElGamalEncryption::~ElGamalEncryption ()
{
BN_CTX_free (ctx);
BN_free (a);
BN_free (b1);
}
void ElGamalEncryption::Encrypt (const uint8_t * data, int len, uint8_t * encrypted, bool zeroPadding) const
{
// create m
uint8_t m[255];
m[0] = 0xFF;
memcpy (m+33, data, len);
memcpy (m+33, data, 222);
SHA256 (m+33, 222, m+1);
// calculate b = b1*m mod p
BIGNUM * b = BN_new ();
BN_bin2bn (m, 255, b);
BN_mod_mul (b, b1, b, elgp, ctx);
BN_free (b1);
// copy a and b
if (zeroPadding)
{
@@ -347,8 +323,10 @@ namespace crypto
bn2buf (b, encrypted + 256, 256);
}
BN_free (b);
BN_free (a);
BN_CTX_free (ctx);
}
bool ElGamalDecrypt (const uint8_t * key, const uint8_t * encrypted,
uint8_t * data, bool zeroPadding)
{
@@ -400,44 +378,68 @@ namespace crypto
// HMAC
const uint64_t IPAD = 0x3636363636363636;
const uint64_t OPAD = 0x5C5C5C5C5C5C5C5C;
#if defined(__AVX__)
static const uint64_t ipads[] = { IPAD, IPAD, IPAD, IPAD };
static const uint64_t opads[] = { OPAD, OPAD, OPAD, OPAD };
#endif
void HMACMD5Digest (uint8_t * msg, size_t len, const MACKey& key, uint8_t * digest)
// key is 32 bytes
// digest is 16 bytes
// block size is 64 bytes
{
uint64_t buf[256];
uint64_t hash[12]; // 96 bytes
#if defined(__AVX__) // for AVX
__asm__
(
"vmovups %[key], %%ymm0 \n"
"vmovups %[ipad], %%ymm1 \n"
"vmovups %%ymm1, 32(%[buf]) \n"
"vxorps %%ymm0, %%ymm1, %%ymm1 \n"
"vmovups %%ymm1, (%[buf]) \n"
"vmovups %[opad], %%ymm1 \n"
"vmovups %%ymm1, 32(%[hash]) \n"
"vxorps %%ymm0, %%ymm1, %%ymm1 \n"
"vmovups %%ymm1, (%[hash]) \n"
"vzeroall \n" // end of AVX
"movups %%xmm0, 80(%[hash]) \n" // zero last 16 bytes
:
: [key]"m"(*(const uint8_t *)key), [ipad]"m"(*ipads), [opad]"m"(*opads),
[buf]"r"(buf), [hash]"r"(hash)
: "memory", "%xmm0" // TODO: change to %ymm0 later
);
#else
// ikeypad
buf[0] = key.GetLL ()[0] ^ IPAD;
buf[1] = key.GetLL ()[1] ^ IPAD;
buf[2] = key.GetLL ()[2] ^ IPAD;
buf[3] = key.GetLL ()[3] ^ IPAD;
buf[4] = IPAD;
buf[5] = IPAD;
buf[6] = IPAD;
buf[7] = IPAD;
buf[4] = IPAD;
buf[5] = IPAD;
buf[6] = IPAD;
buf[7] = IPAD;
// okeypad
hash[0] = key.GetLL ()[0] ^ OPAD;
hash[1] = key.GetLL ()[1] ^ OPAD;
hash[2] = key.GetLL ()[2] ^ OPAD;
hash[3] = key.GetLL ()[3] ^ OPAD;
hash[4] = OPAD;
hash[5] = OPAD;
hash[6] = OPAD;
hash[7] = OPAD;
// fill last 16 bytes with zeros (first hash size assumed 32 bytes in I2P)
memset (hash + 10, 0, 16);
#endif
// concatenate with msg
memcpy (buf + 8, msg, len);
// calculate first hash
uint8_t hash[16]; // MD5
MD5((uint8_t *)buf, len + 64, hash);
// okeypad
buf[0] = key.GetLL ()[0] ^ OPAD;
buf[1] = key.GetLL ()[1] ^ OPAD;
buf[2] = key.GetLL ()[2] ^ OPAD;
buf[3] = key.GetLL ()[3] ^ OPAD;
buf[4] = OPAD;
buf[5] = OPAD;
buf[6] = OPAD;
buf[7] = OPAD;
// copy first hash after okeypad
memcpy (buf + 8, hash, 16);
// fill next 16 bytes with zeros (first hash size assumed 32 bytes in I2P)
memset (buf + 10, 0, 16);
MD5((uint8_t *)buf, len + 64, (uint8_t *)(hash + 8)); // 16 bytes
// calculate digest
MD5((uint8_t *)buf, 96, digest);
MD5((uint8_t *)hash, 96, digest);
}
// AES
@@ -609,16 +611,16 @@ namespace crypto
"jnz 1b \n"
"movups %%xmm1, (%[iv]) \n"
:
: [iv]"r"(&m_LastBlock), [sched]"r"(m_ECBEncryption.GetKeySchedule ()),
: [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
for (int i = 0; i < numBlocks; i++)
{
m_LastBlock ^= in[i];
m_ECBEncryption.Encrypt (&m_LastBlock, &m_LastBlock);
out[i] = m_LastBlock;
*m_LastBlock.GetChipherBlock () ^= in[i];
m_ECBEncryption.Encrypt (m_LastBlock.GetChipherBlock (), m_LastBlock.GetChipherBlock ());
out[i] = *m_LastBlock.GetChipherBlock ();
}
#endif
}
@@ -643,7 +645,7 @@ namespace crypto
"movups %%xmm0, (%[out]) \n"
"movups %%xmm0, (%[iv]) \n"
:
: [iv]"r"(&m_LastBlock), [sched]"r"(m_ECBEncryption.GetKeySchedule ()),
: [iv]"r"((uint8_t *)m_LastBlock), [sched]"r"(m_ECBEncryption.GetKeySchedule ()),
[in]"r"(in), [out]"r"(out)
: "%xmm0", "%xmm1", "memory"
);
@@ -671,7 +673,7 @@ namespace crypto
"jnz 1b \n"
"movups %%xmm1, (%[iv]) \n"
:
: [iv]"r"(&m_IV), [sched]"r"(m_ECBDecryption.GetKeySchedule ()),
: [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"
);
@@ -680,8 +682,8 @@ namespace crypto
{
ChipherBlock tmp = in[i];
m_ECBDecryption.Decrypt (in + i, out + i);
out[i] ^= m_IV;
m_IV = tmp;
out[i] ^= *m_IV.GetChipherBlock ();
*m_IV.GetChipherBlock () = tmp;
}
#endif
}
@@ -705,7 +707,7 @@ namespace crypto
"pxor %%xmm1, %%xmm0 \n"
"movups %%xmm0, (%[out]) \n"
:
: [iv]"r"(&m_IV), [sched]"r"(m_ECBDecryption.GetKeySchedule ()),
: [iv]"r"((uint8_t *)m_IV), [sched]"r"(m_ECBDecryption.GetKeySchedule ()),
[in]"r"(in), [out]"r"(out)
: "%xmm0", "%xmm1", "memory"
);

101
Crypto.h
View File

@@ -7,7 +7,10 @@
#include <openssl/dh.h>
#include <openssl/aes.h>
#include <openssl/dsa.h>
#include <openssl/ecdsa.h>
#include <openssl/rsa.h>
#include <openssl/sha.h>
#include <openssl/evp.h>
#include <openssl/rand.h>
#include "Base.h"
@@ -33,33 +36,18 @@ namespace crypto
DHKeys ();
~DHKeys ();
void GenerateKeys (uint8_t * priv = nullptr, uint8_t * pub = nullptr);
const uint8_t * GetPublicKey ();
void GenerateKeys ();
const uint8_t * GetPublicKey () const { return m_PublicKey; };
void Agree (const uint8_t * pub, uint8_t * shared);
private:
DH * m_DH;
uint8_t m_PublicKey[256];
bool m_IsUpdated;
};
// ElGamal
class ElGamalEncryption
{
public:
ElGamalEncryption (const uint8_t * key);
~ElGamalEncryption ();
void Encrypt (const uint8_t * data, int len, uint8_t * encrypted, bool zeroPadding = false) const;
private:
BN_CTX * ctx;
BIGNUM * a, * b1;
};
void ElGamalEncrypt (const uint8_t * key, const uint8_t * data, uint8_t * encrypted, bool zeroPadding = false);
bool ElGamalDecrypt (const uint8_t * key, const uint8_t * encrypted, uint8_t * data, bool zeroPadding = false);
void GenerateElGamalKeyPair (uint8_t * priv, uint8_t * pub);
@@ -74,7 +62,18 @@ namespace crypto
void operator^=(const ChipherBlock& other) // XOR
{
#if defined(__x86_64__) || defined(__SSE__) // for Intel x84 or with SSE
#if defined(__AVX__) // AVX
__asm__
(
"vmovups (%[buf]), %%xmm0 \n"
"vmovups (%[other]), %%xmm1 \n"
"vxorps %%xmm0, %%xmm1, %%xmm0 \n"
"vmovups %%xmm0, (%[buf]) \n"
:
: [buf]"r"(buf), [other]"r"(other.buf)
: "%xmm0", "%xmm1", "memory"
);
#elif defined(__SSE__) // SSE
__asm__
(
"movups (%[buf]), %%xmm0 \n"
@@ -110,7 +109,9 @@ namespace crypto
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
@@ -198,10 +199,10 @@ namespace crypto
{
public:
CBCEncryption () { memset (m_LastBlock.buf, 0, 16); };
CBCEncryption () { memset ((uint8_t *)m_LastBlock, 0, 16); };
void SetKey (const AESKey& key) { m_ECBEncryption.SetKey (key); }; // 32 bytes
void SetIV (const uint8_t * iv) { memcpy (m_LastBlock.buf, iv, 16); }; // 16 bytes
void SetIV (const uint8_t * iv) { memcpy ((uint8_t *)m_LastBlock, iv, 16); }; // 16 bytes
void Encrypt (int numBlocks, const ChipherBlock * in, ChipherBlock * out);
void Encrypt (const uint8_t * in, std::size_t len, uint8_t * out);
@@ -209,7 +210,7 @@ namespace crypto
private:
ChipherBlock m_LastBlock;
AESAlignedBuffer<16> m_LastBlock;
ECBEncryption m_ECBEncryption;
};
@@ -218,10 +219,10 @@ namespace crypto
{
public:
CBCDecryption () { memset (m_IV.buf, 0, 16); };
CBCDecryption () { memset ((uint8_t *)m_IV, 0, 16); };
void SetKey (const AESKey& key) { m_ECBDecryption.SetKey (key); }; // 32 bytes
void SetIV (const uint8_t * iv) { memcpy (m_IV.buf, iv, 16); }; // 16 bytes
void SetIV (const uint8_t * iv) { memcpy ((uint8_t *)m_IV, iv, 16); }; // 16 bytes
void Decrypt (int numBlocks, const ChipherBlock * in, ChipherBlock * out);
void Decrypt (const uint8_t * in, std::size_t len, uint8_t * out);
@@ -229,7 +230,7 @@ namespace crypto
private:
ChipherBlock m_IV;
AESAlignedBuffer<16> m_IV;
ECBDecryption m_ECBDecryption;
};
@@ -282,4 +283,52 @@ namespace crypto
}
}
// take care about openssl version
#include <openssl/opensslv.h>
#if (OPENSSL_VERSION_NUMBER < 0x010100000) || defined(LIBRESSL_VERSION_NUMBER) // 1.1.0 or LibreSSL
// define getters and setters introduced in 1.1.0
inline int DSA_set0_pqg(DSA *d, BIGNUM *p, BIGNUM *q, BIGNUM *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)
{ 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)
{ 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)
{ 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)
{ 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; }
// ssl
#define TLS_method TLSv1_method
#endif
#endif

View File

@@ -25,6 +25,9 @@
#include "UPnP.h"
#include "util.h"
#include "Event.h"
#include "Websocket.h"
namespace i2p
{
namespace util
@@ -38,6 +41,9 @@ namespace i2p
std::unique_ptr<i2p::http::HTTPServer> httpServer;
std::unique_ptr<i2p::client::I2PControlService> m_I2PControlService;
std::unique_ptr<i2p::transport::UPnP> UPnP;
#ifdef WITH_EVENTS
std::unique_ptr<i2p::event::WebsocketServer> m_WebsocketServer;
#endif
};
Daemon_Singleton::Daemon_Singleton() : isDaemon(false), running(true), d(*new Daemon_Singleton_Private()) {}
@@ -107,7 +113,6 @@ namespace i2p
} else {
// use stdout -- default
}
i2p::log::Logger().Ready();
LogPrint(eLogInfo, "i2pd v", VERSION, " starting");
LogPrint(eLogDebug, "FS: main config file: ", config);
@@ -115,6 +120,9 @@ namespace i2p
bool precomputation; i2p::config::GetOption("precomputation.elgamal", precomputation);
i2p::crypto::InitCrypto (precomputation);
int netID; i2p::config::GetOption("netid", netID);
i2p::context.SetNetID (netID);
i2p::context.Init ();
bool ipv6; i2p::config::GetOption("ipv6", ipv6);
@@ -191,12 +199,40 @@ namespace i2p
{
LogPrint(eLogInfo, "Daemon: explicit trust enabled");
std::string fam; i2p::config::GetOption("trust.family", fam);
std::string routers; i2p::config::GetOption("trust.routers", routers);
bool restricted = false;
if (fam.length() > 0)
{
LogPrint(eLogInfo, "Daemon: setting restricted routes to use family ", fam);
i2p::transport::transports.RestrictRoutes({fam});
} else
LogPrint(eLogError, "Daemon: no family specified for restricted routes");
std::set<std::string> fams;
size_t pos = 0, comma;
do
{
comma = fam.find (',', pos);
fams.insert (fam.substr (pos, comma != std::string::npos ? comma - pos : std::string::npos));
pos = comma + 1;
}
while (comma != std::string::npos);
i2p::transport::transports.RestrictRoutesToFamilies(fams);
restricted = fams.size() > 0;
}
if (routers.length() > 0) {
std::set<i2p::data::IdentHash> idents;
size_t pos = 0, comma;
do
{
comma = routers.find (',', pos);
i2p::data::IdentHash ident;
ident.FromBase64 (routers.substr (pos, comma != std::string::npos ? comma - pos : std::string::npos));
idents.insert (ident);
pos = comma + 1;
}
while (comma != std::string::npos);
LogPrint(eLogInfo, "Daemon: setting restricted routes to use ", idents.size(), " trusted routesrs");
i2p::transport::transports.RestrictRoutesToRouters(idents);
restricted = idents.size() > 0;
}
if(!restricted)
LogPrint(eLogError, "Daemon: no trusted routers of families specififed");
}
bool hidden; i2p::config::GetOption("trust.hidden", hidden);
if (hidden)
@@ -209,6 +245,7 @@ namespace i2p
bool Daemon_Singleton::start()
{
i2p::log::Logger().Start();
LogPrint(eLogInfo, "Daemon: starting NetDB");
i2p::data::netdb.Start();
@@ -259,12 +296,26 @@ namespace i2p
d.m_I2PControlService = std::unique_ptr<i2p::client::I2PControlService>(new i2p::client::I2PControlService (i2pcpAddr, i2pcpPort));
d.m_I2PControlService->Start ();
}
#ifdef WITH_EVENTS
bool websocket; i2p::config::GetOption("websockets.enabled", websocket);
if(websocket) {
std::string websocketAddr; i2p::config::GetOption("websockets.address", websocketAddr);
uint16_t websocketPort; i2p::config::GetOption("websockets.port", websocketPort);
LogPrint(eLogInfo, "Daemon: starting Websocket server at ", websocketAddr, ":", websocketPort);
d.m_WebsocketServer = std::unique_ptr<i2p::event::WebsocketServer>(new i2p::event::WebsocketServer (websocketAddr, websocketPort));
d.m_WebsocketServer->Start();
i2p::event::core.SetListener(d.m_WebsocketServer->ToListener());
}
#endif
return true;
}
bool Daemon_Singleton::stop()
{
#ifdef WITH_EVENTS
i2p::event::core.SetListener(nullptr);
#endif
LogPrint(eLogInfo, "Daemon: shutting down");
LogPrint(eLogInfo, "Daemon: stopping Client");
i2p::client::context.Stop();
@@ -290,10 +341,18 @@ namespace i2p
LogPrint(eLogInfo, "Daemon: stopping I2PControl");
d.m_I2PControlService->Stop ();
d.m_I2PControlService = nullptr;
}
}
#ifdef WITH_EVENTS
if (d.m_WebsocketServer) {
LogPrint(eLogInfo, "Daemon: stopping Websocket server");
d.m_WebsocketServer->Stop();
d.m_WebsocketServer = nullptr;
}
#endif
i2p::crypto::TerminateCrypto ();
i2p::log::Logger().Stop();
return true;
}
}
}
}

View File

@@ -97,7 +97,7 @@ namespace i2p
public:
int gracefullShutdownInterval; // in seconds
int gracefulShutdownInterval; // in seconds
};
#endif

View File

@@ -8,6 +8,7 @@
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/resource.h>
#include "Config.h"
#include "FS.h"
@@ -20,20 +21,23 @@ void handle_signal(int sig)
switch (sig)
{
case SIGHUP:
LogPrint(eLogInfo, "Daemon: Got SIGHUP, reopening log...");
i2p::log::Logger().Reopen ();
LogPrint(eLogInfo, "Daemon: Got SIGHUP, reopening tunnel configuration...");
i2p::client::context.ReloadConfig();
break;
case SIGUSR1:
LogPrint(eLogInfo, "Daemon: Got SIGUSR1, reopening logs...");
i2p::log::Logger().Reopen ();
break;
case SIGINT:
if (i2p::context.AcceptsTunnels () && !Daemon.gracefullShutdownInterval)
{
if (i2p::context.AcceptsTunnels () && !Daemon.gracefulShutdownInterval)
{
i2p::context.SetAcceptsTunnels (false);
Daemon.gracefullShutdownInterval = 10*60; // 10 minutes
LogPrint(eLogInfo, "Graceful shutdown after ", Daemon.gracefullShutdownInterval, " seconds");
}
Daemon.gracefulShutdownInterval = 10*60; // 10 minutes
LogPrint(eLogInfo, "Graceful shutdown after ", Daemon.gracefulShutdownInterval, " seconds");
}
else
Daemon.running = 0;
break;
Daemon.running = 0;
break;
case SIGABRT:
case SIGTERM:
Daemon.running = 0; // Exit loop
@@ -76,9 +80,45 @@ namespace i2p
}
// point std{in,out,err} descriptors to /dev/null
stdin = freopen("/dev/null", "r", stdin);
stdout = freopen("/dev/null", "w", stdout);
stderr = freopen("/dev/null", "w", stderr);
freopen("/dev/null", "r", stdin);
freopen("/dev/null", "w", stdout);
freopen("/dev/null", "w", stderr);
}
// set proc limits
struct rlimit limit;
uint16_t nfiles; i2p::config::GetOption("limits.openfiles", nfiles);
getrlimit(RLIMIT_NOFILE, &limit);
if (nfiles == 0) {
LogPrint(eLogInfo, "Daemon: using system limit in ", limit.rlim_cur, " max open files");
} else if (nfiles <= limit.rlim_max) {
limit.rlim_cur = nfiles;
if (setrlimit(RLIMIT_NOFILE, &limit) == 0) {
LogPrint(eLogInfo, "Daemon: set max number of open files to ",
nfiles, " (system limit is ", limit.rlim_max, ")");
} else {
LogPrint(eLogError, "Daemon: can't set max number of open files: ", strerror(errno));
}
} else {
LogPrint(eLogError, "Daemon: limits.openfiles exceeds system limit: ", limit.rlim_max);
}
uint32_t cfsize; i2p::config::GetOption("limits.coresize", cfsize);
if (cfsize) // core file size set
{
cfsize *= 1024;
getrlimit(RLIMIT_CORE, &limit);
if (cfsize <= limit.rlim_max) {
limit.rlim_cur = cfsize;
if (setrlimit(RLIMIT_CORE, &limit) != 0) {
LogPrint(eLogError, "Daemon: can't set max size of coredump: ", strerror(errno));
} else if (cfsize == 0) {
LogPrint(eLogInfo, "Daemon: coredumps disabled");
} else {
LogPrint(eLogInfo, "Daemon: set max size of core files to ", cfsize / 1024, "Kb");
}
} else {
LogPrint(eLogError, "Daemon: limits.coresize exceeds system limit: ", limit.rlim_max);
}
}
// Pidfile
@@ -108,7 +148,7 @@ namespace i2p
return false;
}
}
gracefullShutdownInterval = 0; // not specified
gracefulShutdownInterval = 0; // not specified
// Signal handler
struct sigaction sa;
@@ -116,6 +156,7 @@ namespace i2p
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART;
sigaction(SIGHUP, &sa, 0);
sigaction(SIGUSR1, &sa, 0);
sigaction(SIGABRT, &sa, 0);
sigaction(SIGTERM, &sa, 0);
sigaction(SIGINT, &sa, 0);
@@ -127,7 +168,7 @@ namespace i2p
{
i2p::fs::Remove(pidfile);
return Daemon_Singleton::stop();
return Daemon_Singleton::stop();
}
void DaemonLinux::run ()
@@ -135,15 +176,15 @@ namespace i2p
while (running)
{
std::this_thread::sleep_for (std::chrono::seconds(1));
if (gracefullShutdownInterval)
if (gracefulShutdownInterval)
{
gracefullShutdownInterval--; // - 1 second
if (gracefullShutdownInterval <= 0)
{
gracefulShutdownInterval--; // - 1 second
if (gracefulShutdownInterval <= 0)
{
LogPrint(eLogInfo, "Graceful shutdown");
return;
}
}
}
}
}
}

View File

@@ -12,74 +12,45 @@ namespace i2p
namespace datagram
{
DatagramDestination::DatagramDestination (std::shared_ptr<i2p::client::ClientDestination> owner):
m_Owner (owner), m_Receiver (nullptr)
m_Owner (owner.get()),
m_Receiver (nullptr)
{
m_Identity.FromBase64 (owner->GetIdentity()->ToBase64());
}
DatagramDestination::~DatagramDestination ()
{
m_Sessions.clear();
}
void DatagramDestination::SendDatagramTo (const uint8_t * payload, size_t len, const i2p::data::IdentHash& ident, uint16_t fromPort, uint16_t toPort)
{
uint8_t buf[MAX_DATAGRAM_SIZE];
auto identityLen = m_Owner->GetIdentity ()->ToBuffer (buf, MAX_DATAGRAM_SIZE);
void DatagramDestination::SendDatagramTo(const uint8_t * payload, size_t len, const i2p::data::IdentHash & identity, uint16_t fromPort, uint16_t toPort)
{
auto owner = m_Owner;
std::vector<uint8_t> v(MAX_DATAGRAM_SIZE);
uint8_t * buf = v.data();
auto identityLen = m_Identity.ToBuffer (buf, MAX_DATAGRAM_SIZE);
uint8_t * signature = buf + identityLen;
auto signatureLen = m_Owner->GetIdentity ()->GetSignatureLen ();
auto signatureLen = m_Identity.GetSignatureLen ();
uint8_t * buf1 = signature + signatureLen;
size_t headerLen = identityLen + signatureLen;
memcpy (buf1, payload, len);
if (m_Owner->GetIdentity ()->GetSigningKeyType () == i2p::data::SIGNING_KEY_TYPE_DSA_SHA1)
if (m_Identity.GetSigningKeyType () == i2p::data::SIGNING_KEY_TYPE_DSA_SHA1)
{
uint8_t hash[32];
SHA256(buf1, len, hash);
m_Owner->Sign (hash, 32, signature);
owner->Sign (hash, 32, signature);
}
else
m_Owner->Sign (buf1, len, signature);
owner->Sign (buf1, len, signature);
auto msg = CreateDataMessage (buf, len + headerLen, fromPort, toPort);
auto remote = m_Owner->FindLeaseSet (ident);
if (remote)
m_Owner->GetService ().post (std::bind (&DatagramDestination::SendMsg, this, msg, remote));
else
m_Owner->RequestDestination (ident, std::bind (&DatagramDestination::HandleLeaseSetRequestComplete, this, std::placeholders::_1, msg));
auto msg = CreateDataMessage (buf, len + headerLen, fromPort, toPort);
auto session = ObtainSession(identity);
session->SendMsg(msg);
}
void DatagramDestination::HandleLeaseSetRequestComplete (std::shared_ptr<i2p::data::LeaseSet> remote, std::shared_ptr<I2NPMessage> msg)
{
if (remote)
SendMsg (msg, remote);
}
void DatagramDestination::SendMsg (std::shared_ptr<I2NPMessage> msg, std::shared_ptr<const i2p::data::LeaseSet> remote)
{
auto outboundTunnel = m_Owner->GetTunnelPool ()->GetNextOutboundTunnel ();
auto leases = remote->GetNonExpiredLeases ();
if (!leases.empty () && outboundTunnel)
{
std::vector<i2p::tunnel::TunnelMessageBlock> msgs;
uint32_t i = rand () % leases.size ();
auto garlic = m_Owner->WrapMessage (remote, msg, true);
msgs.push_back (i2p::tunnel::TunnelMessageBlock
{
i2p::tunnel::eDeliveryTypeTunnel,
leases[i]->tunnelGateway, leases[i]->tunnelID,
garlic
});
outboundTunnel->SendTunnelDataMsg (msgs);
}
else
{
if (outboundTunnel)
LogPrint (eLogWarning, "Failed to send datagram. All leases expired");
else
LogPrint (eLogWarning, "Failed to send datagram. No outbound tunnels");
}
}
void DatagramDestination::HandleDatagram (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len)
void DatagramDestination::HandleDatagram (uint16_t fromPort, uint16_t toPort,uint8_t * const &buf, size_t len)
{
i2p::data::IdentityEx identity;
size_t identityLen = identity.FromBuffer (buf, len);
@@ -98,25 +69,38 @@ namespace datagram
if (verified)
{
auto it = m_ReceiversByPorts.find (toPort);
if (it != m_ReceiversByPorts.end ())
it->second (identity, fromPort, toPort, buf + headerLen, len -headerLen);
else if (m_Receiver != nullptr)
m_Receiver (identity, fromPort, toPort, buf + headerLen, len -headerLen);
auto h = identity.GetIdentHash();
auto session = ObtainSession(h);
session->Ack();
auto r = FindReceiver(toPort);
if(r)
r(identity, fromPort, toPort, buf + headerLen, len -headerLen);
else
LogPrint (eLogWarning, "Receiver for datagram is not set");
LogPrint (eLogWarning, "DatagramDestination: no receiver for port ", toPort);
}
else
LogPrint (eLogWarning, "Datagram signature verification failed");
}
DatagramDestination::Receiver DatagramDestination::FindReceiver(uint16_t port)
{
std::lock_guard<std::mutex> lock(m_ReceiversMutex);
Receiver r = m_Receiver;
auto itr = m_ReceiversByPorts.find(port);
if (itr != m_ReceiversByPorts.end())
r = itr->second;
return r;
}
void DatagramDestination::HandleDataMessagePayload (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len)
{
// unzip it
uint8_t uncompressed[MAX_DATAGRAM_SIZE];
size_t uncompressedLen = m_Inflator.Inflate (buf, len, uncompressed, MAX_DATAGRAM_SIZE);
if (uncompressedLen)
HandleDatagram (fromPort, toPort, uncompressed, uncompressedLen);
HandleDatagram (fromPort, toPort, uncompressed, uncompressedLen);
else
LogPrint (eLogWarning, "Datagram: decompression failed");
}
std::shared_ptr<I2NPMessage> DatagramDestination::CreateDataMessage (const uint8_t * payload, size_t len, uint16_t fromPort, uint16_t toPort)
@@ -137,7 +121,236 @@ namespace datagram
else
msg = nullptr;
return msg;
}
}
void DatagramDestination::CleanUp ()
{
if (m_Sessions.empty ()) return;
auto now = i2p::util::GetMillisecondsSinceEpoch();
LogPrint(eLogDebug, "DatagramDestination: clean up sessions");
std::unique_lock<std::mutex> lock(m_SessionsMutex);
// for each session ...
for (auto it = m_Sessions.begin (); it != m_Sessions.end (); )
{
// check if expired
if (now - it->second->LastActivity() >= DATAGRAM_SESSION_MAX_IDLE)
{
LogPrint(eLogInfo, "DatagramDestination: expiring idle session with ", it->first.ToBase32());
it->second->Stop ();
it = m_Sessions.erase (it); // we are expired
}
else
it++;
}
}
std::shared_ptr<DatagramSession> DatagramDestination::ObtainSession(const i2p::data::IdentHash & identity)
{
std::shared_ptr<DatagramSession> session = nullptr;
std::lock_guard<std::mutex> lock(m_SessionsMutex);
auto itr = m_Sessions.find(identity);
if (itr == m_Sessions.end()) {
// not found, create new session
session = std::make_shared<DatagramSession>(m_Owner, identity);
session->Start ();
m_Sessions[identity] = session;
} else {
session = itr->second;
}
return session;
}
std::shared_ptr<DatagramSession::Info> DatagramDestination::GetInfoForRemote(const i2p::data::IdentHash & remote)
{
std::lock_guard<std::mutex> lock(m_SessionsMutex);
for ( auto & item : m_Sessions)
{
if(item.first == remote) return std::make_shared<DatagramSession::Info>(item.second->GetSessionInfo());
}
return nullptr;
}
DatagramSession::DatagramSession(i2p::client::ClientDestination * localDestination,
const i2p::data::IdentHash & remoteIdent) :
m_LocalDestination(localDestination),
m_RemoteIdent(remoteIdent),
m_SendQueueTimer(localDestination->GetService()),
m_RequestingLS(false)
{
}
void DatagramSession::Start ()
{
m_LastUse = i2p::util::GetMillisecondsSinceEpoch ();
ScheduleFlushSendQueue();
}
void DatagramSession::Stop ()
{
m_SendQueueTimer.cancel ();
}
void DatagramSession::SendMsg(std::shared_ptr<I2NPMessage> msg)
{
// we used this session
m_LastUse = i2p::util::GetMillisecondsSinceEpoch();
// schedule send
auto self = shared_from_this();
m_LocalDestination->GetService().post(std::bind(&DatagramSession::HandleSend, self, msg));
}
DatagramSession::Info DatagramSession::GetSessionInfo() const
{
if(!m_RoutingSession)
return DatagramSession::Info(nullptr, nullptr, m_LastUse);
auto routingPath = m_RoutingSession->GetSharedRoutingPath();
if (!routingPath)
return DatagramSession::Info(nullptr, nullptr, m_LastUse);
auto lease = routingPath->remoteLease;
auto tunnel = routingPath->outboundTunnel;
if(lease)
{
if(tunnel)
return DatagramSession::Info(lease->tunnelGateway, tunnel->GetEndpointIdentHash(), m_LastUse);
else
return DatagramSession::Info(lease->tunnelGateway, nullptr, m_LastUse);
}
else if(tunnel)
return DatagramSession::Info(nullptr, tunnel->GetEndpointIdentHash(), m_LastUse);
else
return DatagramSession::Info(nullptr, nullptr, m_LastUse);
}
void DatagramSession::Ack()
{
m_LastUse = i2p::util::GetMillisecondsSinceEpoch();
auto path = GetSharedRoutingPath();
if(path)
path->updateTime = i2p::util::GetSecondsSinceEpoch ();
}
std::shared_ptr<i2p::garlic::GarlicRoutingPath> DatagramSession::GetSharedRoutingPath ()
{
if(!m_RoutingSession) {
if(!m_RemoteLeaseSet) {
m_RemoteLeaseSet = m_LocalDestination->FindLeaseSet(m_RemoteIdent);
}
if(!m_RemoteLeaseSet) {
// no remote lease set
if(!m_RequestingLS) {
m_RequestingLS = true;
m_LocalDestination->RequestDestination(m_RemoteIdent, std::bind(&DatagramSession::HandleLeaseSetUpdated, this, std::placeholders::_1));
}
return nullptr;
}
m_RoutingSession = m_LocalDestination->GetRoutingSession(m_RemoteLeaseSet, true);
}
auto path = m_RoutingSession->GetSharedRoutingPath();
if(path) {
if (m_CurrentOutboundTunnel && !m_CurrentOutboundTunnel->IsEstablished()) {
// bad outbound tunnel, switch outbound tunnel
m_CurrentOutboundTunnel = m_LocalDestination->GetTunnelPool()->GetNextOutboundTunnel(m_CurrentOutboundTunnel);
path->outboundTunnel = m_CurrentOutboundTunnel;
}
if(m_CurrentRemoteLease && m_CurrentRemoteLease->ExpiresWithin(DATAGRAM_SESSION_LEASE_HANDOVER_WINDOW)) {
// bad lease, switch to next one
if(m_RemoteLeaseSet) {
auto ls = m_RemoteLeaseSet->GetNonExpiredLeasesExcluding([&](const i2p::data::Lease& l) -> bool {
return l.tunnelGateway == m_CurrentRemoteLease->tunnelGateway;
});
auto sz = ls.size();
if (sz) {
auto idx = rand() % sz;
m_CurrentRemoteLease = ls[idx];
}
} else {
// no remote lease set?
LogPrint(eLogWarning, "DatagramSession: no cached remote lease set for ", m_RemoteIdent.ToBase32());
}
path->remoteLease = m_CurrentRemoteLease;
}
} else {
// no current path, make one
path = std::make_shared<i2p::garlic::GarlicRoutingPath>();
// switch outbound tunnel if bad
if(m_CurrentOutboundTunnel == nullptr || ! m_CurrentOutboundTunnel->IsEstablished()) {
m_CurrentOutboundTunnel = m_LocalDestination->GetTunnelPool()->GetNextOutboundTunnel(m_CurrentOutboundTunnel);
}
// switch lease if bad
if(m_CurrentRemoteLease == nullptr || m_CurrentRemoteLease->ExpiresWithin(DATAGRAM_SESSION_LEASE_HANDOVER_WINDOW)) {
if(!m_RemoteLeaseSet) {
m_RemoteLeaseSet = m_LocalDestination->FindLeaseSet(m_RemoteIdent);
}
if(m_RemoteLeaseSet) {
// pick random next good lease
auto ls = m_RemoteLeaseSet->GetNonExpiredLeasesExcluding([&] (const i2p::data::Lease & l) -> bool {
if(m_CurrentRemoteLease)
return l.tunnelGateway == m_CurrentRemoteLease->tunnelGateway;
return false;
});
auto sz = ls.size();
if(sz) {
auto idx = rand() % sz;
m_CurrentRemoteLease = ls[idx];
}
} else {
// no remote lease set currently, bail
LogPrint(eLogWarning, "DatagramSession: no remote lease set found for ", m_RemoteIdent.ToBase32());
return nullptr;
}
}
path->outboundTunnel = m_CurrentOutboundTunnel;
path->remoteLease = m_CurrentRemoteLease;
m_RoutingSession->SetSharedRoutingPath(path);
}
return path;
}
void DatagramSession::HandleLeaseSetUpdated(std::shared_ptr<i2p::data::LeaseSet> ls)
{
m_RequestingLS = false;
if(!ls) return;
// only update lease set if found and newer than previous lease set
uint64_t oldExpire = 0;
if(m_RemoteLeaseSet) oldExpire = m_RemoteLeaseSet->GetExpirationTime();
if(ls && ls->GetExpirationTime() > oldExpire) m_RemoteLeaseSet = ls;
}
void DatagramSession::HandleSend(std::shared_ptr<I2NPMessage> msg)
{
m_SendQueue.push_back(msg);
// flush queue right away if full
if(m_SendQueue.size() >= DATAGRAM_SEND_QUEUE_MAX_SIZE) FlushSendQueue();
}
void DatagramSession::FlushSendQueue ()
{
std::vector<i2p::tunnel::TunnelMessageBlock> send;
auto routingPath = GetSharedRoutingPath();
// if we don't have a routing path we will drop all queued messages
if(routingPath && routingPath->outboundTunnel && routingPath->remoteLease)
{
for (const auto & msg : m_SendQueue)
{
auto m = m_RoutingSession->WrapSingleMessage(msg);
send.push_back(i2p::tunnel::TunnelMessageBlock{i2p::tunnel::eDeliveryTypeTunnel,routingPath->remoteLease->tunnelGateway, routingPath->remoteLease->tunnelID, m});
}
routingPath->outboundTunnel->SendTunnelDataMsg(send);
}
m_SendQueue.clear();
ScheduleFlushSendQueue();
}
void DatagramSession::ScheduleFlushSendQueue()
{
boost::posix_time::milliseconds dlt(100);
m_SendQueueTimer.expires_from_now(dlt);
auto self = shared_from_this();
m_SendQueueTimer.async_wait([self](const boost::system::error_code & ec) { if(ec) return; self->FlushSendQueue(); });
}
}
}

View File

@@ -9,6 +9,7 @@
#include "Identity.h"
#include "LeaseSet.h"
#include "I2NPProtocol.h"
#include "Garlic.h"
namespace i2p
{
@@ -18,37 +19,125 @@ namespace client
}
namespace datagram
{
const size_t MAX_DATAGRAM_SIZE = 32768;
// milliseconds for max session idle time
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
const uint64_t DATAGRAM_SESSION_PATH_SWITCH_INTERVAL = 20 * 60 * 1000;
// milliseconds before lease expire should we try switching leases
const uint64_t DATAGRAM_SESSION_LEASE_HANDOVER_WINDOW = 10 * 1000;
// milliseconds fudge factor for leases handover
const uint64_t DATAGRAM_SESSION_LEASE_HANDOVER_FUDGE = 1000;
// milliseconds minimum time between path switches
const uint64_t DATAGRAM_SESSION_PATH_MIN_LIFETIME = 5 * 1000;
// max 64 messages buffered in send queue for each datagram session
const size_t DATAGRAM_SEND_QUEUE_MAX_SIZE = 64;
class DatagramSession : public std::enable_shared_from_this<DatagramSession>
{
public:
DatagramSession(i2p::client::ClientDestination * localDestination, const i2p::data::IdentHash & remoteIdent);
void Start ();
void Stop ();
/** @brief ack the garlic routing path */
void Ack();
/** send an i2np message to remote endpoint for this session */
void SendMsg(std::shared_ptr<I2NPMessage> msg);
/** get the last time in milliseconds for when we used this datagram session */
uint64_t LastActivity() const { return m_LastUse; }
struct Info
{
std::shared_ptr<const i2p::data::IdentHash> IBGW;
std::shared_ptr<const i2p::data::IdentHash> OBEP;
const uint64_t activity;
Info() : IBGW(nullptr), OBEP(nullptr), activity(0) {}
Info(const uint8_t * ibgw, const uint8_t * obep, const uint64_t a) :
activity(a) {
if(ibgw) IBGW = std::make_shared<i2p::data::IdentHash>(ibgw);
else IBGW = nullptr;
if(obep) OBEP = std::make_shared<i2p::data::IdentHash>(obep);
else OBEP = nullptr;
}
};
Info GetSessionInfo() const;
private:
void FlushSendQueue();
void ScheduleFlushSendQueue();
void HandleSend(std::shared_ptr<I2NPMessage> msg);
std::shared_ptr<i2p::garlic::GarlicRoutingPath> GetSharedRoutingPath();
void HandleLeaseSetUpdated(std::shared_ptr<i2p::data::LeaseSet> ls);
private:
i2p::client::ClientDestination * m_LocalDestination;
i2p::data::IdentHash m_RemoteIdent;
std::shared_ptr<const i2p::data::LeaseSet> m_RemoteLeaseSet;
std::shared_ptr<i2p::garlic::GarlicRoutingSession> m_RoutingSession;
std::shared_ptr<const i2p::data::Lease> m_CurrentRemoteLease;
std::shared_ptr<i2p::tunnel::OutboundTunnel> m_CurrentOutboundTunnel;
boost::asio::deadline_timer m_SendQueueTimer;
std::vector<std::shared_ptr<I2NPMessage> > m_SendQueue;
uint64_t m_LastUse;
bool m_RequestingLS;
};
typedef std::shared_ptr<DatagramSession> DatagramSession_ptr;
const size_t MAX_DATAGRAM_SIZE = 32768;
class DatagramDestination
{
typedef std::function<void (const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len)> Receiver;
public:
DatagramDestination (std::shared_ptr<i2p::client::ClientDestination> owner);
~DatagramDestination ();
~DatagramDestination ();
void SendDatagramTo (const uint8_t * payload, size_t len, const i2p::data::IdentHash& ident, uint16_t fromPort = 0, uint16_t toPort = 0);
void SendDatagramTo (const uint8_t * payload, size_t len, const i2p::data::IdentHash & ident, uint16_t fromPort = 0, uint16_t toPort = 0);
void HandleDataMessagePayload (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len);
void SetReceiver (const Receiver& receiver) { m_Receiver = receiver; };
void ResetReceiver () { m_Receiver = nullptr; };
void SetReceiver (const Receiver& receiver, uint16_t port) { m_ReceiversByPorts[port] = receiver; };
void ResetReceiver (uint16_t port) { m_ReceiversByPorts.erase (port); };
void SetReceiver (const Receiver& receiver, uint16_t port) { std::lock_guard<std::mutex> lock(m_ReceiversMutex); m_ReceiversByPorts[port] = receiver; };
void ResetReceiver (uint16_t port) { std::lock_guard<std::mutex> lock(m_ReceiversMutex); m_ReceiversByPorts.erase (port); };
std::shared_ptr<DatagramSession::Info> GetInfoForRemote(const i2p::data::IdentHash & remote);
// clean up stale sessions
void CleanUp ();
private:
void HandleLeaseSetRequestComplete (std::shared_ptr<i2p::data::LeaseSet> leaseSet, std::shared_ptr<I2NPMessage> msg);
std::shared_ptr<DatagramSession> ObtainSession(const i2p::data::IdentHash & ident);
std::shared_ptr<I2NPMessage> CreateDataMessage (const uint8_t * payload, size_t len, uint16_t fromPort, uint16_t toPort);
void SendMsg (std::shared_ptr<I2NPMessage> msg, std::shared_ptr<const i2p::data::LeaseSet> remote);
void HandleDatagram (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len);
void HandleDatagram (uint16_t fromPort, uint16_t toPort, uint8_t *const& buf, size_t len);
/** find a receiver by port, if none by port is found try default receiever, otherwise returns nullptr */
Receiver FindReceiver(uint16_t port);
private:
std::shared_ptr<i2p::client::ClientDestination> m_Owner;
i2p::client::ClientDestination * m_Owner;
i2p::data::IdentityEx m_Identity;
Receiver m_Receiver; // default
std::mutex m_SessionsMutex;
std::map<i2p::data::IdentHash, DatagramSession_ptr > m_Sessions;
std::mutex m_ReceiversMutex;
std::map<uint16_t, Receiver> m_ReceiversByPorts;
i2p::data::GzipInflator m_Inflator;

View File

@@ -1,6 +1,5 @@
#include <algorithm>
#include <cassert>
#include <boost/lexical_cast.hpp>
#include "Crypto.h"
#include "Log.h"
#include "FS.h"
@@ -14,100 +13,82 @@ namespace i2p
namespace client
{
LeaseSetDestination::LeaseSetDestination (bool isPublic, const std::map<std::string, std::string> * params):
m_IsRunning (false), m_Thread (nullptr), m_Work (m_Service), m_IsPublic (isPublic),
m_PublishReplyToken (0), m_PublishConfirmationTimer (m_Service),
m_PublishVerificationTimer (m_Service), m_CleanupTimer (m_Service)
m_IsRunning (false), m_Thread (nullptr), m_IsPublic (isPublic),
m_PublishReplyToken (0), m_LastSubmissionTime (0), m_PublishConfirmationTimer (m_Service),
m_PublishVerificationTimer (m_Service), m_PublishDelayTimer (m_Service), m_CleanupTimer (m_Service)
{
int inboundTunnelLen = DEFAULT_INBOUND_TUNNEL_LENGTH;
int outboundTunnelLen = DEFAULT_OUTBOUND_TUNNEL_LENGTH;
int inboundTunnelsQuantity = DEFAULT_INBOUND_TUNNELS_QUANTITY;
int outboundTunnelsQuantity = DEFAULT_OUTBOUND_TUNNELS_QUANTITY;
int inLen = DEFAULT_INBOUND_TUNNEL_LENGTH;
int inQty = DEFAULT_INBOUND_TUNNELS_QUANTITY;
int outLen = DEFAULT_OUTBOUND_TUNNEL_LENGTH;
int outQty = DEFAULT_OUTBOUND_TUNNELS_QUANTITY;
int numTags = DEFAULT_TAGS_TO_SEND;
std::shared_ptr<std::vector<i2p::data::IdentHash> > explicitPeers;
if (params)
{
auto it = params->find (I2CP_PARAM_INBOUND_TUNNEL_LENGTH);
if (it != params->end ())
{
int len = i2p::util::lexical_cast<int>(it->second, inboundTunnelLen);
if (len >= 0)
try {
if (params) {
auto it = params->find (I2CP_PARAM_INBOUND_TUNNEL_LENGTH);
if (it != params->end ())
inLen = std::stoi(it->second);
it = params->find (I2CP_PARAM_OUTBOUND_TUNNEL_LENGTH);
if (it != params->end ())
outLen = std::stoi(it->second);
it = params->find (I2CP_PARAM_INBOUND_TUNNELS_QUANTITY);
if (it != params->end ())
inQty = std::stoi(it->second);
it = params->find (I2CP_PARAM_OUTBOUND_TUNNELS_QUANTITY);
if (it != params->end ())
outQty = std::stoi(it->second);
it = params->find (I2CP_PARAM_TAGS_TO_SEND);
if (it != params->end ())
numTags = std::stoi(it->second);
LogPrint (eLogInfo, "Destination: parameters for tunnel set to: ", inQty, " inbound (", inLen, " hops), ", outQty, " outbound (", outLen, " hops), ", numTags, " tags");
it = params->find (I2CP_PARAM_EXPLICIT_PEERS);
if (it != params->end ())
{
inboundTunnelLen = len;
explicitPeers = std::make_shared<std::vector<i2p::data::IdentHash> >();
std::stringstream ss(it->second);
std::string b64;
while (std::getline (ss, b64, ','))
{
i2p::data::IdentHash ident;
ident.FromBase64 (b64);
explicitPeers->push_back (ident);
LogPrint (eLogInfo, "Destination: Added to explicit peers list: ", b64);
}
}
LogPrint (eLogInfo, "Destination: Inbound tunnel length set to ", inboundTunnelLen);
}
it = params->find (I2CP_PARAM_OUTBOUND_TUNNEL_LENGTH);
if (it != params->end ())
{
int len = i2p::util::lexical_cast<int>(it->second, outboundTunnelLen);
if (len >= 0)
{
outboundTunnelLen = len;
}
LogPrint (eLogInfo, "Destination: Outbound tunnel length set to ", outboundTunnelLen);
}
it = params->find (I2CP_PARAM_INBOUND_TUNNELS_QUANTITY);
if (it != params->end ())
{
int quantity = i2p::util::lexical_cast<int>(it->second, inboundTunnelsQuantity);
if (quantity > 0)
{
inboundTunnelsQuantity = quantity;
LogPrint (eLogInfo, "Destination: Inbound tunnels quantity set to ", quantity);
}
}
it = params->find (I2CP_PARAM_OUTBOUND_TUNNELS_QUANTITY);
if (it != params->end ())
{
int quantity = i2p::util::lexical_cast<int>(it->second, outboundTunnelsQuantity);
if (quantity > 0)
{
outboundTunnelsQuantity = quantity;
LogPrint (eLogInfo, "Destination: Outbound tunnels quantity set to ", quantity);
}
}
it = params->find (I2CP_PARAM_TAGS_TO_SEND);
if (it != params->end ())
{
int tagsToSend = i2p::util::lexical_cast<int>(it->second, numTags);
if (tagsToSend > 0)
{
numTags = tagsToSend;
LogPrint (eLogInfo, "Destination: Tags to send set to ", tagsToSend);
}
}
it = params->find (I2CP_PARAM_EXPLICIT_PEERS);
if (it != params->end ())
{
explicitPeers = std::make_shared<std::vector<i2p::data::IdentHash> >();
std::stringstream ss(it->second);
std::string b64;
while (std::getline (ss, b64, ','))
{
i2p::data::IdentHash ident;
ident.FromBase64 (b64);
explicitPeers->push_back (ident);
}
LogPrint (eLogInfo, "Destination: Explicit peers set to ", it->second);
}
}
} catch (std::exception & ex) {
LogPrint(eLogError, "Destination: unable to parse parameters for destination: ", ex.what());
}
SetNumTags (numTags);
m_Pool = i2p::tunnel::tunnels.CreateTunnelPool (inboundTunnelLen, outboundTunnelLen, inboundTunnelsQuantity, outboundTunnelsQuantity);
m_Pool = i2p::tunnel::tunnels.CreateTunnelPool (inLen, outLen, inQty, outQty);
if (explicitPeers)
m_Pool->SetExplicitPeers (explicitPeers);
if(params)
{
auto itr = params->find(I2CP_PARAM_MAX_TUNNEL_LATENCY);
if (itr != params->end()) {
auto maxlatency = std::stoi(itr->second);
itr = params->find(I2CP_PARAM_MIN_TUNNEL_LATENCY);
if (itr != params->end()) {
auto minlatency = std::stoi(itr->second);
if ( minlatency > 0 && maxlatency > 0 ) {
// set tunnel pool latency
LogPrint(eLogInfo, "Destination: requiring tunnel latency [", minlatency, "ms, ", maxlatency, "ms]");
m_Pool->RequireLatency(minlatency, maxlatency);
}
}
}
}
}
LeaseSetDestination::~LeaseSetDestination ()
{
if (m_IsRunning)
Stop ();
for (auto& it: m_LeaseSetRequests)
if (it.second->requestComplete) it.second->requestComplete (nullptr);
m_LeaseSetRequests.clear ();
if (m_Pool)
i2p::tunnel::tunnels.DeleteTunnelPool (m_Pool);
for (auto& it: m_LeaseSetRequests)
it.second->Complete (nullptr);
}
void LeaseSetDestination::Run ()
@@ -131,12 +112,12 @@ namespace client
{
m_IsRunning = true;
m_Pool->SetLocalDestination (shared_from_this ());
m_Pool->SetActive (true);
m_Thread = new std::thread (std::bind (&LeaseSetDestination::Run, shared_from_this ()));
m_Pool->SetActive (true);
m_CleanupTimer.expires_from_now (boost::posix_time::minutes (DESTINATION_CLEANUP_TIMEOUT));
m_CleanupTimer.async_wait (std::bind (&LeaseSetDestination::HandleCleanupTimer,
shared_from_this (), std::placeholders::_1));
shared_from_this (), std::placeholders::_1));
m_Thread = new std::thread (std::bind (&LeaseSetDestination::Run, shared_from_this ()));
return true;
}
else
@@ -149,7 +130,8 @@ namespace client
{
m_CleanupTimer.cancel ();
m_PublishConfirmationTimer.cancel ();
m_PublishVerificationTimer.cancel ();
m_PublishVerificationTimer.cancel ();
m_IsRunning = false;
if (m_Pool)
{
@@ -163,21 +145,52 @@ namespace client
delete m_Thread;
m_Thread = 0;
}
CleanUp (); // GarlicDestination
return true;
}
else
return false;
}
std::shared_ptr<const i2p::data::LeaseSet> LeaseSetDestination::FindLeaseSet (const i2p::data::IdentHash& ident)
{
auto it = m_RemoteLeaseSets.find (ident);
if (it != m_RemoteLeaseSets.end ())
{
if (!it->second->IsExpired ())
return it->second;
std::shared_ptr<i2p::data::LeaseSet> remoteLS;
{
std::lock_guard<std::mutex> lock(m_RemoteLeaseSetsMutex);
auto it = m_RemoteLeaseSets.find (ident);
if (it != m_RemoteLeaseSets.end ())
remoteLS = it->second;
}
if (remoteLS)
{
if (!remoteLS->IsExpired ())
{
if (remoteLS->ExpiresSoon())
{
LogPrint(eLogDebug, "Destination: Lease Set expires soon, updating before expire");
// update now before expiration for smooth handover
auto s = shared_from_this ();
RequestDestination(ident, [s, ident] (std::shared_ptr<i2p::data::LeaseSet> ls) {
if(ls && !ls->IsExpired())
{
ls->PopulateLeases();
{
std::lock_guard<std::mutex> _lock(s->m_RemoteLeaseSetsMutex);
s->m_RemoteLeaseSets[ident] = ls;
}
}
});
}
return remoteLS;
}
else
{
LogPrint (eLogWarning, "Destination: remote LeaseSet expired");
std::lock_guard<std::mutex> lock(m_RemoteLeaseSetsMutex);
m_RemoteLeaseSets.erase (ident);
return nullptr;
}
}
else
{
@@ -185,24 +198,30 @@ namespace client
if (ls && !ls->IsExpired ())
{
ls->PopulateLeases (); // since we don't store them in netdb
m_RemoteLeaseSets[ident] = ls;
std::lock_guard<std::mutex> _lock(m_RemoteLeaseSetsMutex);
m_RemoteLeaseSets[ident] = ls;
return ls;
}
}
return nullptr;
}
}
std::shared_ptr<const i2p::data::LocalLeaseSet> LeaseSetDestination::GetLeaseSet ()
{
if (!m_Pool) return nullptr;
if (!m_LeaseSet)
UpdateLeaseSet ();
std::lock_guard<std::mutex> l(m_LeaseSetMutex);
return m_LeaseSet;
}
void LeaseSetDestination::SetLeaseSet (i2p::data::LocalLeaseSet * newLeaseSet)
{
m_LeaseSet.reset (newLeaseSet);
{
std::lock_guard<std::mutex> l(m_LeaseSetMutex);
m_LeaseSet.reset (newLeaseSet);
}
i2p::garlic::GarlicDestination::SetLeaseSetUpdated ();
if (m_IsPublic)
{
m_PublishVerificationTimer.cancel ();
@@ -275,45 +294,47 @@ namespace client
LogPrint (eLogInfo, "Destination: Reply token is ignored for DatabaseStore");
offset += 36;
}
i2p::data::IdentHash key (buf + DATABASE_STORE_KEY_OFFSET);
std::shared_ptr<i2p::data::LeaseSet> leaseSet;
if (buf[DATABASE_STORE_TYPE_OFFSET] == 1) // LeaseSet
{
LogPrint (eLogDebug, "Remote LeaseSet");
auto it = m_RemoteLeaseSets.find (buf + DATABASE_STORE_KEY_OFFSET);
LogPrint (eLogDebug, "Destination: Remote LeaseSet");
std::lock_guard<std::mutex> lock(m_RemoteLeaseSetsMutex);
auto it = m_RemoteLeaseSets.find (key);
if (it != m_RemoteLeaseSets.end ())
{
leaseSet = it->second;
if (leaseSet->IsNewer (buf + offset, len - offset))
{
leaseSet->Update (buf + offset, len - offset);
if (leaseSet->IsValid ())
LogPrint (eLogDebug, "Remote LeaseSet updated");
if (leaseSet->IsValid () && leaseSet->GetIdentHash () == key)
LogPrint (eLogDebug, "Destination: Remote LeaseSet updated");
else
{
LogPrint (eLogDebug, "Remote LeaseSet update failed");
LogPrint (eLogDebug, "Destination: Remote LeaseSet update failed");
m_RemoteLeaseSets.erase (it);
leaseSet = nullptr;
}
}
else
LogPrint (eLogDebug, "Remote LeaseSet is older. Not updated");
LogPrint (eLogDebug, "Destination: Remote LeaseSet is older. Not updated");
}
else
{
leaseSet = std::make_shared<i2p::data::LeaseSet> (buf + offset, len - offset);
if (leaseSet->IsValid ())
if (leaseSet->IsValid () && leaseSet->GetIdentHash () == key)
{
if (leaseSet->GetIdentHash () != GetIdentHash ())
{
LogPrint (eLogDebug, "New remote LeaseSet added");
m_RemoteLeaseSets[buf + DATABASE_STORE_KEY_OFFSET] = leaseSet;
LogPrint (eLogDebug, "Destination: New remote LeaseSet added");
m_RemoteLeaseSets[key] = leaseSet;
}
else
LogPrint (eLogDebug, "Own remote LeaseSet dropped");
LogPrint (eLogDebug, "Destination: Own remote LeaseSet dropped");
}
else
{
LogPrint (eLogError, "New remote LeaseSet failed");
LogPrint (eLogError, "Destination: New remote LeaseSet failed");
leaseSet = nullptr;
}
}
@@ -321,11 +342,11 @@ namespace client
else
LogPrint (eLogError, "Destination: Unexpected client's DatabaseStore type ", buf[DATABASE_STORE_TYPE_OFFSET], ", dropped");
auto it1 = m_LeaseSetRequests.find (buf + DATABASE_STORE_KEY_OFFSET);
auto it1 = m_LeaseSetRequests.find (key);
if (it1 != m_LeaseSetRequests.end ())
{
it1->second->requestTimeoutTimer.cancel ();
if (it1->second->requestComplete) it1->second->requestComplete (leaseSet);
if (it1->second) it1->second->Complete (leaseSet);
m_LeaseSetRequests.erase (it1);
}
}
@@ -343,29 +364,27 @@ namespace client
if (request->excluded.size () < MAX_NUM_FLOODFILLS_PER_REQUEST)
{
for (int i = 0; i < num; i++)
{
i2p::data::IdentHash peerHash (buf + 33 + i*32);
auto floodfill = i2p::data::netdb.FindRouter (peerHash);
if (floodfill)
{
LogPrint (eLogInfo, "Destination: Requesting ", key.ToBase64 (), " at ", peerHash.ToBase64 ());
if (SendLeaseSetRequest (key, floodfill, request))
found = true;
}
else
{
LogPrint (eLogInfo, "Destination: Found new floodfill, request it"); // TODO: recheck this message
i2p::data::netdb.RequestDestination (peerHash);
}
}
if (!found)
LogPrint (eLogError, "Destination: Suggested floodfills are not presented in netDb");
}
else
LogPrint (eLogInfo, "Destination: ", key.ToBase64 (), " was not found on ", MAX_NUM_FLOODFILLS_PER_REQUEST, " floodfills");
if (!found)
{
if (request->requestComplete) request->requestComplete (nullptr);
i2p::data::IdentHash peerHash (buf + 33 + i*32);
if (!request->excluded.count (peerHash) && !i2p::data::netdb.FindRouter (peerHash))
{
LogPrint (eLogInfo, "Destination: Found new floodfill, request it"); // TODO: recheck this message
i2p::data::netdb.RequestDestination (peerHash);
}
}
auto floodfill = i2p::data::netdb.GetClosestFloodfill (key, request->excluded);
if (floodfill)
{
LogPrint (eLogInfo, "Destination: Requesting ", key.ToBase64 (), " at ", floodfill->GetIdentHash ().ToBase64 ());
if (SendLeaseSetRequest (key, floodfill, request))
found = true;
}
}
if (!found)
{
LogPrint (eLogInfo, "Destination: ", key.ToBase64 (), " was not found on ", MAX_NUM_FLOODFILLS_PER_REQUEST, " floodfills");
request->Complete (nullptr);
m_LeaseSetRequests.erase (key);
}
}
@@ -391,8 +410,7 @@ namespace client
}
void LeaseSetDestination::SetLeaseSetUpdated ()
{
i2p::garlic::GarlicDestination::SetLeaseSetUpdated ();
{
UpdateLeaseSet ();
}
@@ -408,6 +426,16 @@ namespace client
LogPrint (eLogDebug, "Destination: Publishing LeaseSet is pending");
return;
}
auto ts = i2p::util::GetSecondsSinceEpoch ();
if (ts < m_LastSubmissionTime + PUBLISH_MIN_INTERVAL)
{
LogPrint (eLogDebug, "Destination: Publishing LeaseSet is too fast. Wait for ", PUBLISH_MIN_INTERVAL, " seconds");
m_PublishDelayTimer.cancel ();
m_PublishDelayTimer.expires_from_now (boost::posix_time::seconds(PUBLISH_MIN_INTERVAL));
m_PublishDelayTimer.async_wait (std::bind (&LeaseSetDestination::HandlePublishDelayTimer,
shared_from_this (), std::placeholders::_1));
return;
}
auto outbound = m_Pool->GetNextOutboundTunnel ();
if (!outbound)
{
@@ -435,6 +463,7 @@ namespace client
m_PublishConfirmationTimer.async_wait (std::bind (&LeaseSetDestination::HandlePublishConfirmationTimer,
shared_from_this (), std::placeholders::_1));
outbound->SendTunnelDataMsg (floodfill->GetIdentHash (), 0, msg);
m_LastSubmissionTime = ts;
}
void LeaseSetDestination::HandlePublishConfirmationTimer (const boost::system::error_code& ecode)
@@ -459,13 +488,18 @@ namespace client
// "this" added due to bug in gcc 4.7-4.8
[s,this](std::shared_ptr<i2p::data::LeaseSet> leaseSet)
{
if (leaseSet && s->m_LeaseSet)
if (leaseSet)
{
// we got latest LeasetSet
LogPrint (eLogDebug, "Destination: published LeaseSet verified for ", GetIdentHash().ToBase32());
s->m_PublishVerificationTimer.expires_from_now (boost::posix_time::seconds(PUBLISH_REGULAR_VERIFICATION_INTERNAL));
s->m_PublishVerificationTimer.async_wait (std::bind (&LeaseSetDestination::HandlePublishVerificationTimer, s, std::placeholders::_1));
return;
if (s->m_LeaseSet && *s->m_LeaseSet == *leaseSet)
{
// we got latest LeasetSet
LogPrint (eLogDebug, "Destination: published LeaseSet verified for ", GetIdentHash().ToBase32());
s->m_PublishVerificationTimer.expires_from_now (boost::posix_time::seconds(PUBLISH_REGULAR_VERIFICATION_INTERNAL));
s->m_PublishVerificationTimer.async_wait (std::bind (&LeaseSetDestination::HandlePublishVerificationTimer, s, std::placeholders::_1));
return;
}
else
LogPrint (eLogDebug, "Destination: LeaseSet is different than just published for ", GetIdentHash().ToBase32());
}
else
LogPrint (eLogWarning, "Destination: couldn't find published LeaseSet for ", GetIdentHash().ToBase32());
@@ -475,6 +509,12 @@ namespace client
}
}
void LeaseSetDestination::HandlePublishDelayTimer (const boost::system::error_code& ecode)
{
if (ecode != boost::asio::error::operation_aborted)
Publish ();
}
bool LeaseSetDestination::RequestDestination (const i2p::data::IdentHash& dest, RequestComplete requestComplete)
{
if (!m_Pool || !IsReady ())
@@ -495,9 +535,9 @@ namespace client
auto it = s->m_LeaseSetRequests.find (dest);
if (it != s->m_LeaseSetRequests.end ())
{
auto requestComplete = it->second->requestComplete;
auto requestComplete = it->second;
s->m_LeaseSetRequests.erase (it);
if (notify && requestComplete) requestComplete (nullptr);
if (notify && requestComplete) requestComplete->Complete (nullptr);
}
});
}
@@ -509,22 +549,31 @@ namespace client
if (floodfill)
{
auto request = std::make_shared<LeaseSetRequest> (m_Service);
request->requestComplete = requestComplete;
if (requestComplete)
request->requestComplete.push_back (requestComplete);
auto ts = i2p::util::GetSecondsSinceEpoch ();
auto ret = m_LeaseSetRequests.insert (std::pair<i2p::data::IdentHash, std::shared_ptr<LeaseSetRequest> >(dest,request));
if (ret.second) // inserted
{
request->requestTime = ts;
if (!SendLeaseSetRequest (dest, floodfill, request))
{
// request failed
m_LeaseSetRequests.erase (dest);
if (request->requestComplete) request->requestComplete (nullptr);
m_LeaseSetRequests.erase (ret.first);
if (requestComplete) requestComplete (nullptr);
}
}
else // duplicate
{
LogPrint (eLogWarning, "Destination: Request of LeaseSet ", dest.ToBase64 (), " is pending already");
// TODO: queue up requests
if (request->requestComplete) request->requestComplete (nullptr);
LogPrint (eLogInfo, "Destination: Request of LeaseSet ", dest.ToBase64 (), " is pending already");
if (ts > ret.first->second->requestTime + MAX_LEASESET_REQUEST_TIMEOUT)
{
// something went wrong
m_LeaseSetRequests.erase (ret.first);
if (requestComplete) requestComplete (nullptr);
}
else if (requestComplete)
ret.first->second->requestComplete.push_back (requestComplete);
}
}
else
@@ -547,7 +596,6 @@ namespace client
if (request->replyTunnel && request->outboundTunnel)
{
request->excluded.insert (nextFloodfill->GetIdentHash ());
request->requestTime = i2p::util::GetSecondsSinceEpoch ();
request->requestTimeoutTimer.cancel ();
uint8_t replyKey[32], replyTag[32];
@@ -605,9 +653,9 @@ namespace client
if (done)
{
auto requestComplete = it->second->requestComplete;
auto requestComplete = it->second;
m_LeaseSetRequests.erase (it);
if (requestComplete) requestComplete (nullptr);
if (requestComplete) requestComplete->Complete (nullptr);
}
}
}
@@ -619,6 +667,7 @@ namespace client
{
CleanupExpiredTags ();
CleanupRemoteLeaseSets ();
CleanupDestination ();
m_CleanupTimer.expires_from_now (boost::posix_time::minutes (DESTINATION_CLEANUP_TIMEOUT));
m_CleanupTimer.async_wait (std::bind (&LeaseSetDestination::HandleCleanupTimer,
shared_from_this (), std::placeholders::_1));
@@ -628,6 +677,7 @@ namespace client
void LeaseSetDestination::CleanupRemoteLeaseSets ()
{
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
std::lock_guard<std::mutex> lock(m_RemoteLeaseSetsMutex);
for (auto it = m_RemoteLeaseSets.begin (); it != m_RemoteLeaseSets.end ();)
{
if (it->second->IsEmpty () || ts > it->second->GetExpirationTime ()) // leaseset expired
@@ -642,7 +692,8 @@ namespace client
ClientDestination::ClientDestination (const i2p::data::PrivateKeys& keys, bool isPublic, const std::map<std::string, std::string> * params):
LeaseSetDestination (isPublic, params),
m_Keys (keys), m_DatagramDestination (nullptr)
m_Keys (keys), m_DatagramDestination (nullptr),
m_ReadyChecker(GetService())
{
if (isPublic)
PersistTemporaryKeys ();
@@ -654,8 +705,6 @@ namespace client
ClientDestination::~ClientDestination ()
{
if (m_DatagramDestination)
delete m_DatagramDestination;
}
bool ClientDestination::Start ()
@@ -676,22 +725,53 @@ namespace client
{
if (LeaseSetDestination::Stop ())
{
m_ReadyChecker.cancel();
m_StreamingDestination->Stop ();
//m_StreamingDestination->SetOwner (nullptr);
m_StreamingDestination = nullptr;
for (auto& it: m_StreamingDestinationsByPorts)
{
it.second->Stop ();
//it.second->SetOwner (nullptr);
}
m_StreamingDestinationsByPorts.clear ();
if (m_DatagramDestination)
{
auto d = m_DatagramDestination;
{
delete m_DatagramDestination;
m_DatagramDestination = nullptr;
delete d;
}
return true;
return true;
}
else
return false;
}
#ifdef I2LUA
void ClientDestination::Ready(ReadyPromise & p)
{
ScheduleCheckForReady(&p);
}
void ClientDestination::ScheduleCheckForReady(ReadyPromise * p)
{
// tick every 100ms
m_ReadyChecker.expires_from_now(boost::posix_time::milliseconds(100));
m_ReadyChecker.async_wait([&, p] (const boost::system::error_code & ecode) {
HandleCheckForReady(ecode, p);
});
}
void ClientDestination::HandleCheckForReady(const boost::system::error_code & ecode, ReadyPromise * p)
{
if(ecode) // error happened
p->set_value(nullptr);
else if(IsReady()) // we are ready
p->set_value(std::shared_ptr<ClientDestination>(this));
else // we are not ready
ScheduleCheckForReady(p);
}
#endif
void ClientDestination::HandleDataMessage (const uint8_t * buf, size_t len)
{
uint32_t length = bufbe32toh (buf);
@@ -786,6 +866,12 @@ namespace client
return false;
}
void ClientDestination::AcceptOnce (const i2p::stream::StreamingDestination::Acceptor& acceptor)
{
if (m_StreamingDestination)
m_StreamingDestination->AcceptOnce (acceptor);
}
std::shared_ptr<i2p::stream::StreamingDestination> ClientDestination::CreateStreamingDestination (int port, bool gzip)
{
auto dest = std::make_shared<i2p::stream::StreamingDestination> (GetSharedFromThis (), port, gzip);
@@ -796,9 +882,9 @@ namespace client
return dest;
}
i2p::datagram::DatagramDestination * ClientDestination::CreateDatagramDestination ()
i2p::datagram::DatagramDestination * ClientDestination::CreateDatagramDestination ()
{
if (!m_DatagramDestination)
if (m_DatagramDestination == nullptr)
m_DatagramDestination = new i2p::datagram::DatagramDestination (GetSharedFromThis ());
return m_DatagramDestination;
}
@@ -848,5 +934,11 @@ namespace client
Sign (leaseSet->GetBuffer (), leaseSet->GetBufferLen () - leaseSet->GetSignatureLen (), leaseSet->GetSignature ()); // TODO
SetLeaseSet (leaseSet);
}
void ClientDestination::CleanupDestination ()
{
if (m_DatagramDestination) m_DatagramDestination->CleanUp ();
}
}
}

View File

@@ -8,6 +8,9 @@
#include <set>
#include <string>
#include <functional>
#ifdef I2LUA
#include <future>
#endif
#include <boost/asio.hpp>
#include "Identity.h"
#include "TunnelPool.h"
@@ -27,6 +30,7 @@ namespace client
const uint8_t PROTOCOL_TYPE_RAW = 18;
const int PUBLISH_CONFIRMATION_TIMEOUT = 5; // in seconds
const int PUBLISH_VERIFICATION_TIMEOUT = 10; // in seconds after successfull publish
const int PUBLISH_MIN_INTERVAL = 20; // in seconds
const int PUBLISH_REGULAR_VERIFICATION_INTERNAL = 100; // in seconds periodically
const int LEASESET_REQUEST_TIMEOUT = 5; // in seconds
const int MAX_LEASESET_REQUEST_TIMEOUT = 40; // in seconds
@@ -47,6 +51,12 @@ namespace client
const char I2CP_PARAM_TAGS_TO_SEND[] = "crypto.tagsToSend";
const int DEFAULT_TAGS_TO_SEND = 40;
// latency
const char I2CP_PARAM_MIN_TUNNEL_LATENCY[] = "latency.min";
const int DEFAULT_MIN_TUNNEL_LATENCY = 0;
const char I2CP_PARAM_MAX_TUNNEL_LATENCY[] = "latency.max";
const int DEFAULT_MAX_TUNNEL_LATENCY = 0;
typedef std::function<void (std::shared_ptr<i2p::stream::Stream> stream)> StreamRequestComplete;
class LeaseSetDestination: public i2p::garlic::GarlicDestination,
@@ -60,9 +70,15 @@ namespace client
std::set<i2p::data::IdentHash> excluded;
uint64_t requestTime;
boost::asio::deadline_timer requestTimeoutTimer;
RequestComplete requestComplete;
std::list<RequestComplete> requestComplete;
std::shared_ptr<i2p::tunnel::OutboundTunnel> outboundTunnel;
std::shared_ptr<i2p::tunnel::InboundTunnel> replyTunnel;
void Complete (std::shared_ptr<i2p::data::LeaseSet> ls)
{
for (auto& it: requestComplete) it (ls);
requestComplete.clear ();
}
};
@@ -95,6 +111,7 @@ namespace client
protected:
void SetLeaseSet (i2p::data::LocalLeaseSet * newLeaseSet);
virtual void CleanupDestination () {}; // additional clean up in derived classes
// I2CP
virtual void HandleDataMessage (const uint8_t * buf, size_t len) = 0;
virtual void CreateNewLeaseSet (std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> > tunnels) = 0;
@@ -106,6 +123,7 @@ namespace client
void Publish ();
void HandlePublishConfirmationTimer (const boost::system::error_code& ecode);
void HandlePublishVerificationTimer (const boost::system::error_code& ecode);
void HandlePublishDelayTimer (const boost::system::error_code& ecode);
void HandleDatabaseStoreMessage (const uint8_t * buf, size_t len);
void HandleDatabaseSearchReplyMessage (const uint8_t * buf, size_t len);
void HandleDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg);
@@ -121,34 +139,45 @@ namespace client
volatile bool m_IsRunning;
std::thread * m_Thread;
boost::asio::io_service m_Service;
boost::asio::io_service::work m_Work;
mutable std::mutex m_RemoteLeaseSetsMutex;
std::map<i2p::data::IdentHash, std::shared_ptr<i2p::data::LeaseSet> > m_RemoteLeaseSets;
std::map<i2p::data::IdentHash, std::shared_ptr<LeaseSetRequest> > m_LeaseSetRequests;
std::shared_ptr<i2p::tunnel::TunnelPool> m_Pool;
std::mutex m_LeaseSetMutex;
std::shared_ptr<i2p::data::LocalLeaseSet> m_LeaseSet;
bool m_IsPublic;
uint32_t m_PublishReplyToken;
uint64_t m_LastSubmissionTime; // in seconds
std::set<i2p::data::IdentHash> m_ExcludedFloodfills; // for publishing
boost::asio::deadline_timer m_PublishConfirmationTimer, m_PublishVerificationTimer, m_CleanupTimer;
boost::asio::deadline_timer m_PublishConfirmationTimer, m_PublishVerificationTimer,
m_PublishDelayTimer, m_CleanupTimer;
public:
// for HTTP only
int GetNumRemoteLeaseSets () const { return m_RemoteLeaseSets.size (); };
const decltype(m_RemoteLeaseSets)& GetLeaseSets () const { return m_RemoteLeaseSets; };
};
class ClientDestination: public LeaseSetDestination
{
public:
#ifdef I2LUA
// type for informing that a client destination is ready
typedef std::promise<std::shared_ptr<ClientDestination> > ReadyPromise;
// informs promise with shared_from_this() when this destination is ready to use
// if cancelled before ready, informs promise with nullptr
void Ready(ReadyPromise & p);
#endif
ClientDestination (const i2p::data::PrivateKeys& keys, bool isPublic, const std::map<std::string, std::string> * params = nullptr);
~ClientDestination ();
bool Start ();
bool Stop ();
const i2p::data::PrivateKeys& GetPrivateKeys () const { return m_Keys; };
void Sign (const uint8_t * buf, int len, uint8_t * signature) const { m_Keys.Sign (buf, len, signature); };
@@ -161,10 +190,11 @@ namespace client
void AcceptStreams (const i2p::stream::StreamingDestination::Acceptor& acceptor);
void StopAcceptingStreams ();
bool IsAcceptingStreams () const;
void AcceptOnce (const i2p::stream::StreamingDestination::Acceptor& acceptor);
// datagram
i2p::datagram::DatagramDestination * GetDatagramDestination () const { return m_DatagramDestination; };
i2p::datagram::DatagramDestination * CreateDatagramDestination ();
i2p::datagram::DatagramDestination * GetDatagramDestination () const { return m_DatagramDestination; };
i2p::datagram::DatagramDestination * CreateDatagramDestination ();
// implements LocalDestination
const uint8_t * GetEncryptionPrivateKey () const { return m_EncryptionPrivateKey; };
@@ -172,6 +202,7 @@ namespace client
protected:
void CleanupDestination ();
// I2CP
void HandleDataMessage (const uint8_t * buf, size_t len);
void CreateNewLeaseSet (std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> > tunnels);
@@ -181,7 +212,10 @@ namespace client
std::shared_ptr<ClientDestination> GetSharedFromThis ()
{ return std::static_pointer_cast<ClientDestination>(shared_from_this ()); }
void PersistTemporaryKeys ();
#ifdef I2LUA
void ScheduleCheckForReady(ReadyPromise * p);
void HandleCheckForReady(const boost::system::error_code & ecode, ReadyPromise * p);
#endif
private:
i2p::data::PrivateKeys m_Keys;
@@ -189,8 +223,10 @@ namespace client
std::shared_ptr<i2p::stream::StreamingDestination> m_StreamingDestination; // default
std::map<uint16_t, std::shared_ptr<i2p::stream::StreamingDestination> > m_StreamingDestinationsByPorts;
i2p::datagram::DatagramDestination * m_DatagramDestination;
i2p::datagram::DatagramDestination * m_DatagramDestination;
boost::asio::deadline_timer m_ReadyChecker;
public:
// for HTTP only

54
Dockerfile Normal file
View File

@@ -0,0 +1,54 @@
FROM alpine:latest
MAINTAINER Mikal Villa <mikal@sigterm.no>
ENV GIT_BRANCH="master"
ENV I2PD_PREFIX="/opt/i2pd-${GIT_BRANCH}"
ENV PATH=${I2PD_PREFIX}/bin:$PATH
ENV GOSU_VERSION=1.7
ENV GOSU_SHASUM="34049cfc713e8b74b90d6de49690fa601dc040021980812b2f1f691534be8a50 /usr/local/bin/gosu"
RUN mkdir /user && adduser -S -h /user i2pd && chown -R i2pd:nobody /user
#
# Each RUN is a layer, adding the dependencies and building i2pd in one layer takes around 8-900Mb, so to keep the
# image under 20mb we need to remove all the build dependencies in the same "RUN" / layer.
#
# 1. install deps, clone and build.
# 2. strip binaries.
# 3. Purge all dependencies and other unrelated packages, including build directory.
RUN apk --no-cache --virtual build-dependendencies add make gcc g++ libtool boost-dev build-base openssl-dev openssl git \
&& mkdir -p /tmp/build \
&& cd /tmp/build && git clone -b ${GIT_BRANCH} https://github.com/PurpleI2P/i2pd.git \
&& cd i2pd \
&& make -j4 \
&& mkdir -p ${I2PD_PREFIX}/bin \
&& mv i2pd ${I2PD_PREFIX}/bin/ \
&& cd ${I2PD_PREFIX}/bin \
&& strip i2pd \
&& rm -fr /tmp/build && apk --purge del build-dependendencies build-base fortify-headers boost-dev zlib-dev openssl-dev \
boost-python3 python3 gdbm boost-unit_test_framework boost-python linux-headers boost-prg_exec_monitor \
boost-serialization boost-signals boost-wave boost-wserialization boost-math boost-graph boost-regex git pcre \
libtool g++ gcc pkgconfig
# 2. Adding required libraries to run i2pd to ensure it will run.
RUN apk --no-cache add boost-filesystem boost-system boost-program_options boost-date_time boost-thread boost-iostreams openssl musl-utils libstdc++
# Gosu is a replacement for su/sudo in docker and not a backdoor :) See https://github.com/tianon/gosu
RUN wget -O /usr/local/bin/gosu https://github.com/tianon/gosu/releases/download/${GOSU_VERSION}/gosu-amd64 \
&& echo "${GOSU_SHASUM}" | sha256sum -c && chmod +x /usr/local/bin/gosu
COPY entrypoint.sh /entrypoint.sh
RUN chmod a+x /entrypoint.sh
RUN echo "export PATH=${PATH}" >> /etc/profile
VOLUME [ "/var/lib/i2pd" ]
EXPOSE 7070 4444 4447 7656 2827 7654 7650
ENTRYPOINT [ "/entrypoint.sh" ]

61
Event.cpp Normal file
View File

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

53
Event.h Normal file
View File

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

43
FS.cpp
View File

@@ -46,8 +46,18 @@ namespace fs {
}
#if defined(WIN32) || defined(_WIN32)
char localAppData[MAX_PATH];
SHGetFolderPath(NULL, CSIDL_APPDATA, 0, NULL, localAppData);
dataDir = std::string(localAppData) + "\\" + appName;
// check executable directory first
GetModuleFileName (NULL, localAppData, MAX_PATH);
auto execPath = boost::filesystem::path(localAppData).parent_path();
// if config file exists in .exe's folder use it
if(boost::filesystem::exists(execPath/"i2pd.conf")) // TODO: magic string
dataDir = execPath.string ();
else
{
// otherwise %appdata%
SHGetFolderPath(NULL, CSIDL_APPDATA, 0, NULL, localAppData);
dataDir = std::string(localAppData) + "\\" + appName;
}
return;
#elif defined(MAC_OSX)
char *home = getenv("HOME");
@@ -56,13 +66,15 @@ namespace fs {
return;
#else /* other unix */
#if defined(ANDROID)
if (boost::filesystem::exists("/sdcard"))
{
dataDir = "/sdcard/" + appName;
const char * ext = getenv("EXTERNAL_STORAGE");
if (!ext) ext = "/sdcard";
if (boost::filesystem::exists(ext))
{
dataDir = std::string (ext) + "/" + appName;
return;
}
// otherwise use /data/files
#endif
}
// otherwise use /data/files
#endif
char *home = getenv("HOME");
if (isService) {
dataDir = "/var/lib/" + appName;
@@ -112,10 +124,10 @@ namespace fs {
bool CreateDirectory (const std::string& path)
{
if (boost::filesystem::exists(path) &&
if (boost::filesystem::exists(path) &&
boost::filesystem::is_directory (boost::filesystem::status (path))) return true;
return boost::filesystem::create_directory(path);
}
}
void HashedStorage::SetPlace(const std::string &path) {
root = path + i2p::fs::dirSep + name;
@@ -125,7 +137,7 @@ namespace fs {
if (!boost::filesystem::exists(root)) {
boost::filesystem::create_directories(root);
}
for (size_t i = 0; i < count; i++) {
auto p = root + i2p::fs::dirSep + prefix1 + chars[i];
if (boost::filesystem::exists(p))
@@ -158,6 +170,13 @@ namespace fs {
}
void HashedStorage::Traverse(std::vector<std::string> & files) {
Iterate([&files] (const std::string & fname) {
files.push_back(fname);
});
}
void HashedStorage::Iterate(FilenameVisitor v)
{
boost::filesystem::path p(root);
boost::filesystem::recursive_directory_iterator it(p);
boost::filesystem::recursive_directory_iterator end;
@@ -166,7 +185,7 @@ namespace fs {
if (!boost::filesystem::is_regular_file( it->status() ))
continue;
const std::string & t = it->path().string();
files.push_back(t);
v(t);
}
}
} // fs

4
FS.h
View File

@@ -13,6 +13,7 @@
#include <string>
#include <iostream>
#include <sstream>
#include <functional>
namespace i2p {
namespace fs {
@@ -43,6 +44,7 @@ namespace fs {
std::string suffix; /**< suffix of file in storage (extension) */
public:
typedef std::function<void(const std::string &)> FilenameVisitor;
HashedStorage(const char *n, const char *p1, const char *p2, const char *s):
name(n), prefix1(p1), prefix2(p2), suffix(s) {};
@@ -58,6 +60,8 @@ namespace fs {
void Remove(const std::string & ident);
/** find all files in storage and store list in provided vector */
void Traverse(std::vector<std::string> & files);
/** visit every file in this storage with a visitor */
void Iterate(FilenameVisitor v);
};
/** @brief Returns current application name, default 'i2pd' */

View File

@@ -20,7 +20,7 @@ namespace data
void Families::LoadCertificate (const std::string& filename)
{
SSL_CTX * ctx = SSL_CTX_new (TLSv1_method ());
SSL_CTX * ctx = SSL_CTX_new (TLS_method ());
int ret = SSL_CTX_use_certificate_file (ctx, filename.c_str (), SSL_FILETYPE_PEM);
if (ret)
{
@@ -40,7 +40,7 @@ namespace data
if (family) family[0] = 0;
}
auto pkey = X509_get_pubkey (cert);
int keyType = EVP_PKEY_type(pkey->type);
int keyType = EVP_PKEY_base_id (pkey);
switch (keyType)
{
case EVP_PKEY_DSA:
@@ -114,6 +114,12 @@ namespace data
{
uint8_t buf[50], signatureBuf[64];
size_t len = family.length (), signatureLen = strlen (signature);
if (len + 32 > 50)
{
LogPrint (eLogError, "Family: ", family, " is too long");
return false;
}
memcpy (buf, family.c_str (), len);
memcpy (buf + len, (const uint8_t *)ident, 32);
len += 32;
@@ -129,7 +135,7 @@ namespace data
{
auto filename = i2p::fs::DataDirPath("family", (family + ".key"));
std::string sig;
SSL_CTX * ctx = SSL_CTX_new (TLSv1_method ());
SSL_CTX * ctx = SSL_CTX_new (TLS_method ());
int ret = SSL_CTX_use_PrivateKey_file (ctx, filename.c_str (), SSL_FILETYPE_PEM);
if (ret)
{

View File

@@ -20,7 +20,7 @@ namespace garlic
std::shared_ptr<const i2p::data::RoutingDestination> destination, int numTags, bool attachLeaseSet):
m_Owner (owner), m_Destination (destination), m_NumTags (numTags),
m_LeaseSetUpdateStatus (attachLeaseSet ? eLeaseSetUpdated : eLeaseSetDoNotSend),
m_ElGamalEncryption (new i2p::crypto::ElGamalEncryption (destination->GetEncryptionPublicKey ()))
m_LeaseSetUpdateMsgID (0)
{
// create new session tags and session key
RAND_bytes (m_SessionKey, 32);
@@ -28,7 +28,7 @@ namespace garlic
}
GarlicRoutingSession::GarlicRoutingSession (const uint8_t * sessionKey, const SessionTag& sessionTag):
m_Owner (nullptr), m_Destination (nullptr), m_NumTags (1), m_LeaseSetUpdateStatus (eLeaseSetDoNotSend)
m_Owner (nullptr), m_NumTags (1), m_LeaseSetUpdateStatus (eLeaseSetDoNotSend), m_LeaseSetUpdateMsgID (0)
{
memcpy (m_SessionKey, sessionKey, 32);
m_Encryption.SetKey (m_SessionKey);
@@ -83,6 +83,7 @@ namespace garlic
if (msgID == m_LeaseSetUpdateMsgID)
{
m_LeaseSetUpdateStatus = eLeaseSetUpToDate;
m_LeaseSetUpdateMsgID = 0;
LogPrint (eLogInfo, "Garlic: LeaseSet update confirmed");
}
else
@@ -92,32 +93,22 @@ namespace garlic
void GarlicRoutingSession::TagsConfirmed (uint32_t msgID)
{
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
for (auto it = m_UnconfirmedTagsMsgs.begin (); it != m_UnconfirmedTagsMsgs.end ();)
auto it = m_UnconfirmedTagsMsgs.find (msgID);
if (it != m_UnconfirmedTagsMsgs.end ())
{
auto& tags = *it;
if (tags->msgID == msgID)
{
if (ts < tags->tagsCreationTime + OUTGOING_TAGS_EXPIRATION_TIMEOUT)
{
for (int i = 0; i < tags->numTags; i++)
m_SessionTags.push_back (tags->sessionTags[i]);
}
it = m_UnconfirmedTagsMsgs.erase (it);
auto& tags = it->second;
if (ts < tags->tagsCreationTime + OUTGOING_TAGS_EXPIRATION_TIMEOUT)
{
for (int i = 0; i < tags->numTags; i++)
m_SessionTags.push_back (tags->sessionTags[i]);
}
else if (ts >= tags->tagsCreationTime + OUTGOING_TAGS_CONFIRMATION_TIMEOUT)
{
if (m_Owner)
m_Owner->RemoveDeliveryStatusSession (tags->msgID);
it = m_UnconfirmedTagsMsgs.erase (it);
}
else
++it;
m_UnconfirmedTagsMsgs.erase (it);
}
}
bool GarlicRoutingSession::CleanupExpiredTags ()
{
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
auto ts = i2p::util::GetSecondsSinceEpoch ();
for (auto it = m_SessionTags.begin (); it != m_SessionTags.end ();)
{
if (ts >= it->creationTime + OUTGOING_TAGS_EXPIRATION_TIMEOUT)
@@ -126,6 +117,12 @@ namespace garlic
++it;
}
CleanupUnconfirmedTags ();
if (m_LeaseSetUpdateMsgID && ts*1000LL > m_LeaseSetSubmissionTime + LEASET_CONFIRMATION_TIMEOUT)
{
if (m_Owner)
m_Owner->RemoveDeliveryStatusSession (m_LeaseSetUpdateMsgID);
m_LeaseSetUpdateMsgID = 0;
}
return !m_SessionTags.empty () || !m_UnconfirmedTagsMsgs.empty ();
}
@@ -136,10 +133,10 @@ namespace garlic
// delete expired unconfirmed tags
for (auto it = m_UnconfirmedTagsMsgs.begin (); it != m_UnconfirmedTagsMsgs.end ();)
{
if (ts >= (*it)->tagsCreationTime + OUTGOING_TAGS_CONFIRMATION_TIMEOUT)
if (ts >= it->second->tagsCreationTime + OUTGOING_TAGS_CONFIRMATION_TIMEOUT)
{
if (m_Owner)
m_Owner->RemoveDeliveryStatusSession ((*it)->msgID);
m_Owner->RemoveDeliveryStatusSession (it->first);
it = m_UnconfirmedTagsMsgs.erase (it);
ret = true;
}
@@ -178,7 +175,7 @@ namespace garlic
// create message
if (!tagFound) // new session
{
LogPrint (eLogWarning, "Garlic: No tags available, will use ElGamal");
LogPrint (eLogInfo, "Garlic: No tags available, will use ElGamal");
if (!m_Destination)
{
LogPrint (eLogError, "Garlic: Can't use ElGamal for unknown destination");
@@ -190,7 +187,7 @@ namespace garlic
RAND_bytes (elGamal.preIV, 32); // Pre-IV
uint8_t iv[32]; // IV is first 16 bytes
SHA256(elGamal.preIV, 32, iv);
m_ElGamalEncryption->Encrypt ((uint8_t *)&elGamal, sizeof(elGamal), buf, true);
i2p::crypto::ElGamalEncrypt (m_Destination->GetEncryptionPublicKey (), (uint8_t *)&elGamal, buf, true);
m_Encryption.SetIV (iv);
buf += 514;
len += 514;
@@ -247,7 +244,7 @@ namespace garlic
size_t GarlicRoutingSession::CreateGarlicPayload (uint8_t * payload, std::shared_ptr<const I2NPMessage> msg, UnconfirmedTags * newTags)
{
uint64_t ts = i2p::util::GetMillisecondsSinceEpoch () + 8000; // 8 sec
uint64_t ts = i2p::util::GetMillisecondsSinceEpoch ();
uint32_t msgID;
RAND_bytes ((uint8_t *)&msgID, 4);
size_t size = 0;
@@ -258,9 +255,11 @@ namespace garlic
if (m_Owner)
{
// resubmit non-confirmed LeaseSet
if (m_LeaseSetUpdateStatus == eLeaseSetSubmitted &&
i2p::util::GetMillisecondsSinceEpoch () > m_LeaseSetSubmissionTime + LEASET_CONFIRMATION_TIMEOUT)
m_LeaseSetUpdateStatus = eLeaseSetUpdated;
if (m_LeaseSetUpdateStatus == eLeaseSetSubmitted && ts > m_LeaseSetSubmissionTime + LEASET_CONFIRMATION_TIMEOUT)
{
m_LeaseSetUpdateStatus = eLeaseSetUpdated;
SetSharedRoutingPath (nullptr); // invalidate path since leaseset was not confirmed
}
// attach DeviveryStatus if necessary
if (newTags || m_LeaseSetUpdateStatus == eLeaseSetUpdated) // new tags created or leaseset updated
@@ -274,7 +273,8 @@ namespace garlic
if (newTags) // new tags created
{
newTags->msgID = msgID;
m_UnconfirmedTagsMsgs.emplace_back (newTags);
m_UnconfirmedTagsMsgs.insert (std::make_pair(msgID, std::unique_ptr<UnconfirmedTags>(newTags)));
newTags = nullptr; // got acquired
}
m_Owner->DeliveryStatusSent (shared_from_this (), msgID);
}
@@ -284,9 +284,10 @@ namespace garlic
// attach LeaseSet
if (m_LeaseSetUpdateStatus == eLeaseSetUpdated)
{
if (m_LeaseSetUpdateMsgID) m_Owner->RemoveDeliveryStatusSession (m_LeaseSetUpdateMsgID); // remove previous
m_LeaseSetUpdateStatus = eLeaseSetSubmitted;
m_LeaseSetUpdateMsgID = msgID;
m_LeaseSetSubmissionTime = i2p::util::GetMillisecondsSinceEpoch ();
m_LeaseSetSubmissionTime = ts;
// clove if our leaseSet must be attached
auto leaseSet = CreateDatabaseStoreMsg (m_Owner->GetLeaseSet ());
size += CreateGarlicClove (payload + size, leaseSet, false);
@@ -298,13 +299,14 @@ namespace garlic
size += CreateGarlicClove (payload + size, msg, m_Destination ? m_Destination->IsDestination () : false);
(*numCloves)++;
}
memset (payload + size, 0, 3); // certificate of message
size += 3;
htobe32buf (payload + size, msgID); // MessageID
size += 4;
htobe64buf (payload + size, ts); // Expiration of message
htobe64buf (payload + size, ts + 8000); // Expiration of message, 8 sec
size += 8;
if (newTags) delete newTags; // not acquired, delete
return size;
}
@@ -312,7 +314,7 @@ namespace garlic
{
uint64_t ts = i2p::util::GetMillisecondsSinceEpoch () + 8000; // 8 sec
size_t size = 0;
if (isDestination && m_Destination)
if (isDestination)
{
buf[size] = eGarlicDeliveryTypeDestination << 5;// delivery instructions flag destination
size++;
@@ -391,6 +393,12 @@ namespace garlic
{
}
void GarlicDestination::CleanUp ()
{
m_Sessions.clear ();
m_DeliveryStatusSessions.clear ();
m_Tags.clear ();
}
void GarlicDestination::AddSessionKey (const uint8_t * key, const uint8_t * tag)
{
if (key)
@@ -621,42 +629,64 @@ namespace garlic
LogPrint (eLogDebug, "Garlic: ", numExpiredTags, " tags expired for ", GetIdentHash().ToBase64 ());
// outgoing
std::unique_lock<std::mutex> l(m_SessionsMutex);
for (auto it = m_Sessions.begin (); it != m_Sessions.end ();)
{
it->second->GetSharedRoutingPath (); // delete shared path if necessary
if (!it->second->CleanupExpiredTags ())
std::unique_lock<std::mutex> l(m_SessionsMutex);
for (auto it = m_Sessions.begin (); it != m_Sessions.end ();)
{
LogPrint (eLogInfo, "Routing session to ", it->first.ToBase32 (), " deleted");
it = m_Sessions.erase (it);
it->second->GetSharedRoutingPath (); // delete shared path if necessary
if (!it->second->CleanupExpiredTags ())
{
LogPrint (eLogInfo, "Routing session to ", it->first.ToBase32 (), " deleted");
it->second->SetOwner (nullptr);
it = m_Sessions.erase (it);
}
else
++it;
}
else
++it;
}
// delivery status sessions
{
std::unique_lock<std::mutex> l(m_DeliveryStatusSessionsMutex);
for (auto it = m_DeliveryStatusSessions.begin (); it != m_DeliveryStatusSessions.end (); )
{
if (it->second->GetOwner () != this)
it = m_DeliveryStatusSessions.erase (it);
else
++it;
}
}
}
void GarlicDestination::RemoveDeliveryStatusSession (uint32_t msgID)
{
std::unique_lock<std::mutex> l(m_DeliveryStatusSessionsMutex);
m_DeliveryStatusSessions.erase (msgID);
}
void GarlicDestination::DeliveryStatusSent (GarlicRoutingSessionPtr session, uint32_t msgID)
{
std::unique_lock<std::mutex> l(m_DeliveryStatusSessionsMutex);
m_DeliveryStatusSessions[msgID] = session;
}
void GarlicDestination::HandleDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg)
{
uint32_t msgID = bufbe32toh (msg->GetPayload ());
GarlicRoutingSessionPtr session;
{
std::unique_lock<std::mutex> l(m_DeliveryStatusSessionsMutex);
auto it = m_DeliveryStatusSessions.find (msgID);
if (it != m_DeliveryStatusSessions.end ())
{
it->second->MessageConfirmed (msgID);
session = it->second;
m_DeliveryStatusSessions.erase (it);
LogPrint (eLogDebug, "Garlic: message ", msgID, " acknowledged");
}
}
if (session)
{
session->MessageConfirmed (msgID);
LogPrint (eLogDebug, "Garlic: message ", msgID, " acknowledged");
}
}
void GarlicDestination::SetLeaseSetUpdated ()

View File

@@ -104,10 +104,16 @@ namespace garlic
{
if (m_LeaseSetUpdateStatus != eLeaseSetDoNotSend) m_LeaseSetUpdateStatus = eLeaseSetUpdated;
};
bool IsLeaseSetNonConfirmed () const { return m_LeaseSetUpdateStatus == eLeaseSetSubmitted; };
bool IsLeaseSetUpdated () const { return m_LeaseSetUpdateStatus == eLeaseSetUpdated; };
uint64_t GetLeaseSetSubmissionTime () const { return m_LeaseSetSubmissionTime; }
std::shared_ptr<GarlicRoutingPath> GetSharedRoutingPath ();
void SetSharedRoutingPath (std::shared_ptr<GarlicRoutingPath> path);
const GarlicDestination * GetOwner () const { return m_Owner; }
void SetOwner (GarlicDestination * owner) { m_Owner = owner; }
private:
size_t CreateAESBlock (uint8_t * buf, std::shared_ptr<const I2NPMessage> msg);
@@ -122,17 +128,17 @@ namespace garlic
GarlicDestination * m_Owner;
std::shared_ptr<const i2p::data::RoutingDestination> m_Destination;
i2p::crypto::AESKey m_SessionKey;
std::list<SessionTag> m_SessionTags;
int m_NumTags;
std::list<std::unique_ptr<UnconfirmedTags> > m_UnconfirmedTagsMsgs;
std::map<uint32_t, std::unique_ptr<UnconfirmedTags> > m_UnconfirmedTagsMsgs; // msgID->tags
LeaseSetUpdateStatus m_LeaseSetUpdateStatus;
uint32_t m_LeaseSetUpdateMsgID;
uint64_t m_LeaseSetSubmissionTime; // in milliseconds
i2p::crypto::CBCEncryption m_Encryption;
std::unique_ptr<const i2p::crypto::ElGamalEncryption> m_ElGamalEncryption;
std::shared_ptr<GarlicRoutingPath> m_SharedRoutingPath;
@@ -150,6 +156,7 @@ namespace garlic
GarlicDestination (): m_NumTags (32) {}; // 32 tags by default
~GarlicDestination ();
void CleanUp ();
void SetNumTags (int numTags) { m_NumTags = numTags; };
std::shared_ptr<GarlicRoutingSession> GetRoutingSession (std::shared_ptr<const i2p::data::RoutingDestination> destination, bool attachLeaseSet);
void CleanupExpiredTags ();
@@ -189,6 +196,7 @@ namespace garlic
// incoming
std::map<SessionTag, std::shared_ptr<i2p::crypto::CBCDecryption>> m_Tags;
// DeliveryStatus
std::mutex m_DeliveryStatusSessionsMutex;
std::map<uint32_t, GarlicRoutingSessionPtr> m_DeliveryStatusSessions; // msgID -> session
public:

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013-2016, The PurpleI2P Project
* Copyright (c) 2013-2017, The PurpleI2P Project
*
* This file is part of Purple i2pd project and licensed under BSD3
*
@@ -9,11 +9,13 @@
#include <inttypes.h>
#include <string.h> /* memset */
#include <iostream>
#include "Log.h"
#include "Gzip.h"
namespace i2p {
namespace data {
namespace i2p
{
namespace data
{
const size_t GZIP_CHUNK_SIZE = 16384;
GzipInflator::GzipInflator (): m_IsDirty (false)
@@ -36,9 +38,10 @@ namespace data {
m_Inflator.next_out = out;
m_Inflator.avail_out = outLen;
int err;
if ((err = inflate (&m_Inflator, Z_NO_FLUSH)) == Z_STREAM_END) {
if ((err = inflate (&m_Inflator, Z_NO_FLUSH)) == Z_STREAM_END)
return outLen - m_Inflator.avail_out;
}
// else
LogPrint (eLogError, "Gzip: Inflate error ", err);
return 0;
}
@@ -49,17 +52,20 @@ namespace data {
m_Inflator.next_in = const_cast<uint8_t *>(in);
m_Inflator.avail_in = inLen;
int ret;
do {
do
{
m_Inflator.next_out = out;
m_Inflator.avail_out = GZIP_CHUNK_SIZE;
ret = inflate (&m_Inflator, Z_NO_FLUSH);
if (ret < 0) {
if (ret < 0)
{
inflateEnd (&m_Inflator);
os.setstate(std::ios_base::failbit);
break;
}
os.write ((char *)out, GZIP_CHUNK_SIZE - m_Inflator.avail_out);
} while (!m_Inflator.avail_out); // more data to read
}
while (!m_Inflator.avail_out); // more data to read
delete[] out;
}
@@ -99,9 +105,10 @@ namespace data {
m_Deflator.next_out = out;
m_Deflator.avail_out = outLen;
int err;
if ((err = deflate (&m_Deflator, Z_FINISH)) == Z_STREAM_END) {
if ((err = deflate (&m_Deflator, Z_FINISH)) == Z_STREAM_END)
return outLen - m_Deflator.avail_out;
} /* else */
// else
LogPrint (eLogError, "Gzip: Deflate error ", err);
return 0;
}
} // data

View File

@@ -1,14 +1,15 @@
/*
* Copyright (c) 2013-2016, The PurpleI2P Project
* Copyright (c) 2013-2017, 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 <algorithm>
#include <utility>
#include "util.h"
#include "HTTP.h"
#include <algorithm>
#include <ctime>
namespace i2p {
@@ -43,18 +44,16 @@ namespace http {
}
}
bool parse_header_line(const std::string & line, std::map<std::string, std::string> & headers) {
static std::pair<std::string, std::string> parse_header_line(const std::string& line)
{
std::size_t pos = 0;
std::size_t len = 2; /* strlen(": ") */
std::size_t max = line.length();
if ((pos = line.find(": ", pos)) == std::string::npos)
return false;
return std::make_pair("", "");
while ((pos + len) < max && isspace(line.at(pos + len)))
len++;
std::string name = line.substr(0, pos);
std::string value = line.substr(pos + len);
headers[name] = value;
return true;
return std::make_pair(line.substr(0, pos), line.substr(pos + len));
}
void gen_rfc1123_date(std::string & out) {
@@ -247,10 +246,15 @@ namespace http {
uri = tokens[1];
version = tokens[2];
expect = HEADER_LINE;
} else {
}
else
{
std::string line = str.substr(pos, eol - pos);
if (!parse_header_line(line, headers))
return -1;
auto p = parse_header_line(line);
if (p.first.length () > 0)
headers.push_back (p);
else
return -1;
}
pos = eol + strlen(CRLF);
if (pos >= eoh)
@@ -259,17 +263,57 @@ namespace http {
return eoh + strlen(HTTP_EOH);
}
std::string HTTPReq::to_string() {
std::stringstream ss;
ss << method << " " << uri << " " << version << CRLF;
for (auto & h : headers) {
ss << h.first << ": " << h.second << CRLF;
}
ss << CRLF;
return ss.str();
void HTTPReq::write(std::ostream & o)
{
o << method << " " << uri << " " << version << CRLF;
for (auto & h : headers)
o << h.first << ": " << h.second << CRLF;
o << CRLF;
}
bool HTTPRes::is_chunked() {
std::string HTTPReq::to_string()
{
std::stringstream ss;
write(ss);
return ss.str();
}
void HTTPReq::AddHeader (const std::string& name, const std::string& value)
{
headers.push_back (std::make_pair(name, value));
}
void HTTPReq::UpdateHeader (const std::string& name, const std::string& value)
{
for (auto& it : headers)
if (it.first == name)
{
it.second = value;
break;
}
}
void HTTPReq::RemoveHeader (const std::string& name)
{
for (auto it = headers.begin (); it != headers.end ();)
{
if (!it->first.compare(0, name.length (), name))
it = headers.erase (it);
else
it++;
}
}
std::string HTTPReq::GetHeader (const std::string& name) const
{
for (auto& it : headers)
if (it.first == name)
return it.second;
return "";
}
bool HTTPRes::is_chunked() const
{
auto it = headers.find("Transfer-Encoding");
if (it == headers.end())
return false;
@@ -278,16 +322,20 @@ namespace http {
return false;
}
bool HTTPRes::is_gzipped() {
bool HTTPRes::is_gzipped(bool includingI2PGzip) const
{
auto it = headers.find("Content-Encoding");
if (it == headers.end())
return false; /* no header */
if (it->second.find("gzip") != std::string::npos)
return true; /* gotcha! */
if (includingI2PGzip && it->second.find("x-i2p-gzip") != std::string::npos)
return true;
return false;
}
long int HTTPMsg::content_length() {
long int HTTPMsg::content_length() const
{
unsigned long int length = 0;
auto it = headers.find("Content-Length");
if (it == headers.end())
@@ -330,7 +378,10 @@ namespace http {
expect = HEADER_LINE;
} else {
std::string line = str.substr(pos, eol - pos);
if (!parse_header_line(line, headers))
auto p = parse_header_line(line);
if (p.first.length () > 0)
headers.insert (p);
else
return -1;
}
pos = eol + strlen(CRLF);

22
HTTP.h
View File

@@ -11,6 +11,7 @@
#include <cstring>
#include <map>
#include <list>
#include <sstream>
#include <string>
#include <vector>
@@ -54,7 +55,8 @@ namespace http {
std::string to_string ();
};
struct HTTPMsg {
struct HTTPMsg
{
std::map<std::string, std::string> headers;
void add_header(const char *name, std::string & value, bool replace = false);
@@ -62,10 +64,12 @@ namespace http {
void del_header(const char *name);
/** @brief Returns declared message length or -1 if unknown */
long int content_length();
long int content_length() const;
};
struct HTTPReq : HTTPMsg {
struct HTTPReq
{
std::list<std::pair<std::string, std::string> > headers;
std::string version;
std::string method;
std::string uri;
@@ -82,6 +86,12 @@ namespace http {
/** @brief Serialize HTTP request to string */
std::string to_string();
void write(std::ostream & o);
void AddHeader (const std::string& name, const std::string& value);
void UpdateHeader (const std::string& name, const std::string& value);
void RemoveHeader (const std::string& name);
std::string GetHeader (const std::string& name) const;
};
struct HTTPRes : HTTPMsg {
@@ -116,11 +126,13 @@ namespace http {
*/
std::string to_string();
void write(std::ostream & o);
/** @brief Checks that response declared as chunked data */
bool is_chunked();
bool is_chunked() const ;
/** @brief Checks that response contains compressed data */
bool is_gzipped();
bool is_gzipped(bool includingI2PGzip = true) const;
};
/**

View File

@@ -29,7 +29,7 @@ namespace proxy {
static const char *pageHead =
"<head>\r\n"
" <title>I2P HTTP proxy: error</title>\r\n"
" <title>I2Pd HTTP proxy</title>\r\n"
" <style type=\"text/css\">\r\n"
" body { font: 100%/1.5em sans-serif; margin: 0; padding: 1.5em; background: #FAFAFA; color: #103456; }\r\n"
" .header { font-size: 2.5em; text-align: center; margin: 1.5em 0; color: #894C84; }\r\n"
@@ -60,18 +60,44 @@ namespace proxy {
void HandleStreamRequestComplete (std::shared_ptr<i2p::stream::Stream> stream);
/* error helpers */
void GenericProxyError(const char *title, const char *description);
void GenericProxyInfo(const char *title, const char *description);
void HostNotFound(std::string & host);
void SendProxyError(std::string & content);
void ForwardToUpstreamProxy();
void HandleUpstreamHTTPProxyConnect(const boost::system::error_code & ec);
void HandleUpstreamSocksProxyConnect(const boost::system::error_code & ec);
void HandleSocksProxySendHandshake(const boost::system::error_code & ec, std::size_t bytes_transfered);
void HandleSocksProxyReply(const boost::system::error_code & ec, std::size_t bytes_transfered);
typedef std::function<void(boost::asio::ip::tcp::endpoint)> ProxyResolvedHandler;
void HandleUpstreamProxyResolved(const boost::system::error_code & ecode, boost::asio::ip::tcp::resolver::iterator itr, ProxyResolvedHandler handler);
void SocksProxySuccess();
void HandoverToUpstreamProxy();
uint8_t m_recv_chunk[8192];
std::string m_recv_buf; // from client
std::string m_send_buf; // to upstream
std::shared_ptr<boost::asio::ip::tcp::socket> m_sock;
std::shared_ptr<boost::asio::ip::tcp::socket> m_proxysock;
boost::asio::ip::tcp::resolver m_proxy_resolver;
i2p::http::URL m_ProxyURL;
i2p::http::URL m_RequestURL;
uint8_t m_socks_buf[255+8]; // for socks request/response
ssize_t m_req_len;
i2p::http::URL m_ClientRequestURL;
i2p::http::HTTPReq m_ClientRequest;
i2p::http::HTTPRes m_ClientResponse;
std::stringstream m_ClientRequestBuffer;
public:
HTTPReqHandler(HTTPProxy * parent, std::shared_ptr<boost::asio::ip::tcp::socket> sock) :
I2PServiceHandler(parent), m_sock(sock) {}
I2PServiceHandler(parent), m_sock(sock),
m_proxysock(std::make_shared<boost::asio::ip::tcp::socket>(parent->GetService())),
m_proxy_resolver(parent->GetService()) {}
~HTTPReqHandler() { Terminate(); }
void Handle () { AsyncSockRead(); } /* overload */
};
@@ -96,6 +122,13 @@ namespace proxy {
m_sock->close();
m_sock = nullptr;
}
if(m_proxysock)
{
LogPrint(eLogDebug, "HTTPProxy: close proxysock");
if(m_proxysock->is_open())
m_proxysock->close();
m_proxysock = nullptr;
}
Done(shared_from_this());
}
@@ -107,6 +140,14 @@ namespace proxy {
SendProxyError(content);
}
void HTTPReqHandler::GenericProxyInfo(const char *title, const char *description) {
std::stringstream ss;
ss << "<h1>Proxy info: " << title << "</h1>\r\n";
ss << "<p>" << description << "</p>\r\n";
std::string content = ss.str();
SendProxyError(content);
}
void HTTPReqHandler::HostNotFound(std::string & host) {
std::stringstream ss;
ss << "<h1>Proxy error: Host not found</h1>\r\n"
@@ -133,7 +174,7 @@ namespace proxy {
<< "</html>\r\n";
res.body = ss.str();
std::string response = res.to_string();
boost::asio::async_write(*m_sock, boost::asio::buffer(response),
boost::asio::async_write(*m_sock, boost::asio::buffer(response), boost::asio::transfer_all(),
std::bind(&HTTPReqHandler::SentHTTPFailed, shared_from_this(), std::placeholders::_1));
}
@@ -159,26 +200,16 @@ namespace proxy {
void HTTPReqHandler::SanitizeHTTPRequest(i2p::http::HTTPReq & req)
{
/* drop common headers */
req.del_header("Referer");
req.del_header("Via");
req.del_header("Forwarded");
req.RemoveHeader ("Referer");
req.RemoveHeader("Via");
req.RemoveHeader("Forwarded");
/* drop proxy-disclosing headers */
std::vector<std::string> toErase;
for (const auto& it : req.headers) {
if (it.first.compare(0, 12, "X-Forwarded-") == 0) {
toErase.push_back(it.first);
} else if (it.first.compare(0, 6, "Proxy-") == 0) {
toErase.push_back(it.first);
} else {
/* allow */
}
}
for (const auto& header : toErase) {
req.headers.erase(header);
}
req.RemoveHeader("X-Forwarded");
req.RemoveHeader("Proxy-");
/* replace headers */
req.add_header("Connection", "close", true); /* keep-alive conns not supported yet */
req.add_header("User-Agent", "MYOB/6.66 (AN/ON)", true); /* privacy */
req.UpdateHeader("User-Agent", "MYOB/6.66 (AN/ON)");
/* add headers */
req.AddHeader("Connection", "close"); /* keep-alive conns not supported yet */
}
/**
@@ -189,65 +220,69 @@ namespace proxy {
*/
bool HTTPReqHandler::HandleRequest()
{
i2p::http::HTTPReq req;
i2p::http::URL url;
std::string b64;
int req_len = 0;
req_len = req.parse(m_recv_buf);
m_req_len = m_ClientRequest.parse(m_recv_buf);
if (req_len == 0)
if (m_req_len == 0)
return false; /* need more data */
if (req_len < 0) {
if (m_req_len < 0) {
LogPrint(eLogError, "HTTPProxy: unable to parse request");
GenericProxyError("Invalid request", "Proxy unable to parse your request");
return true; /* parse error */
}
/* parsing success, now let's look inside request */
LogPrint(eLogDebug, "HTTPProxy: requested: ", req.uri);
url.parse(req.uri);
LogPrint(eLogDebug, "HTTPProxy: requested: ", m_ClientRequest.uri);
m_RequestURL.parse(m_ClientRequest.uri);
if (ExtractAddressHelper(url, b64)) {
i2p::client::context.GetAddressBook ().InsertAddress (url.host, b64);
LogPrint (eLogInfo, "HTTPProxy: added b64 from addresshelper for ", url.host);
std::string full_url = url.to_string();
if (ExtractAddressHelper(m_RequestURL, b64)) {
i2p::client::context.GetAddressBook ().InsertAddress (m_RequestURL.host, b64);
LogPrint (eLogInfo, "HTTPProxy: added b64 from addresshelper for ", m_RequestURL.host);
std::string full_url = m_RequestURL.to_string();
std::stringstream ss;
ss << "Host " << url.host << " added to router's addressbook from helper. "
ss << "Host " << m_RequestURL.host << " added to router's addressbook from helper. "
<< "Click <a href=\"" << full_url << "\">here</a> to proceed.";
GenericProxyError("Addresshelper found", ss.str().c_str());
GenericProxyInfo("Addresshelper found", ss.str().c_str());
return true; /* request processed */
}
SanitizeHTTPRequest(req);
SanitizeHTTPRequest(m_ClientRequest);
std::string dest_host = url.host;
uint16_t dest_port = url.port;
std::string dest_host = m_RequestURL.host;
uint16_t dest_port = m_RequestURL.port;
/* always set port, even if missing in request */
if (!dest_port) {
dest_port = (url.schema == "https") ? 443 : 80;
}
if (!dest_port)
dest_port = (m_RequestURL.schema == "https") ? 443 : 80;
/* detect dest_host, set proper 'Host' header in upstream request */
auto h = req.headers.find("Host");
if (dest_host != "") {
if (dest_host != "")
{
/* absolute url, replace 'Host' header */
std::string h = dest_host;
if (dest_port != 0 && dest_port != 80)
h += ":" + std::to_string(dest_port);
req.add_header("Host", h, true);
} else if (h != req.headers.end()) {
/* relative url and 'Host' header provided. transparent proxy mode? */
i2p::http::URL u;
std::string t = "http://" + h->second;
u.parse(t);
dest_host = u.host;
dest_port = u.port;
} else {
/* relative url and missing 'Host' header */
GenericProxyError("Invalid request", "Can't detect destination host from request");
return true;
}
m_ClientRequest.UpdateHeader("Host", h);
}
else
{
auto h = m_ClientRequest.GetHeader ("Host");
if (h.length () > 0)
{
/* relative url and 'Host' header provided. transparent proxy mode? */
i2p::http::URL u;
std::string t = "http://" + h;
u.parse(t);
dest_host = u.host;
dest_port = u.port;
}
else
{
/* relative url and missing 'Host' header */
GenericProxyError("Invalid request", "Can't detect destination host from request");
return true;
}
}
/* check dest_host really exists and inside I2P network */
i2p::data::IdentHash identHash;
@@ -256,23 +291,31 @@ namespace proxy {
HostNotFound(dest_host);
return true; /* request processed */
}
/* TODO: outproxy handler here */
} else {
LogPrint (eLogWarning, "HTTPProxy: outproxy failure for ", dest_host, ": not implemented yet");
std::string message = "Host" + dest_host + "not inside I2P network, but outproxy support not implemented yet";
GenericProxyError("Outproxy failure", message.c_str());
std::string outproxyUrl; i2p::config::GetOption("httpproxy.outproxy", outproxyUrl);
if(outproxyUrl.size()) {
LogPrint (eLogDebug, "HTTPProxy: use outproxy ", outproxyUrl);
if(m_ProxyURL.parse(outproxyUrl))
ForwardToUpstreamProxy();
else
GenericProxyError("Outproxy failure", "bad outproxy settings");
} else {
LogPrint (eLogWarning, "HTTPProxy: outproxy failure for ", dest_host, ": no outprxy enabled");
std::string message = "Host" + dest_host + "not inside I2P network, but outproxy is not enabled";
GenericProxyError("Outproxy failure", message.c_str());
}
return true;
}
/* make relative url */
url.schema = "";
url.host = "";
req.uri = url.to_string();
m_RequestURL.schema = "";
m_RequestURL.host = "";
m_ClientRequest.uri = m_RequestURL.to_string();
/* drop original request from recv buffer */
m_recv_buf.erase(0, req_len);
m_recv_buf.erase(0, m_req_len);
/* build new buffer from modified request and data from original request */
m_send_buf = req.to_string();
m_send_buf = m_ClientRequest.to_string();
m_send_buf.append(m_recv_buf);
/* connect to destination */
LogPrint(eLogDebug, "HTTPProxy: connecting to host ", dest_host, ":", dest_port);
@@ -281,6 +324,144 @@ namespace proxy {
return true;
}
void HTTPReqHandler::ForwardToUpstreamProxy()
{
LogPrint(eLogDebug, "HTTPProxy: forward to upstream");
// build http requset
m_ClientRequestURL = m_RequestURL;
LogPrint(eLogDebug, "HTTPProxy: ", m_ClientRequestURL.host);
m_ClientRequestURL.schema = "";
m_ClientRequestURL.host = "";
m_ClientRequest.uri = m_ClientRequestURL.to_string();
m_ClientRequest.write(m_ClientRequestBuffer);
m_ClientRequestBuffer << m_recv_buf.substr(m_req_len);
// assume http if empty schema
if (m_ProxyURL.schema == "" || m_ProxyURL.schema == "http") {
// handle upstream http proxy
if (!m_ProxyURL.port) m_ProxyURL.port = 80;
boost::asio::ip::tcp::resolver::query q(m_ProxyURL.host, std::to_string(m_ProxyURL.port));
m_proxy_resolver.async_resolve(q, std::bind(&HTTPReqHandler::HandleUpstreamProxyResolved, this, std::placeholders::_1, std::placeholders::_2, [&](boost::asio::ip::tcp::endpoint ep) {
m_proxysock->async_connect(ep, std::bind(&HTTPReqHandler::HandleUpstreamHTTPProxyConnect, this, std::placeholders::_1));
}));
} else if (m_ProxyURL.schema == "socks") {
// handle upstream socks proxy
if (!m_ProxyURL.port) m_ProxyURL.port = 9050; // default to tor default if not specified
boost::asio::ip::tcp::resolver::query q(m_ProxyURL.host, std::to_string(m_ProxyURL.port));
m_proxy_resolver.async_resolve(q, std::bind(&HTTPReqHandler::HandleUpstreamProxyResolved, this, std::placeholders::_1, std::placeholders::_2, [&](boost::asio::ip::tcp::endpoint ep) {
m_proxysock->async_connect(ep, std::bind(&HTTPReqHandler::HandleUpstreamSocksProxyConnect, this, std::placeholders::_1));
}));
} else {
// unknown type, complain
GenericProxyError("unknown outproxy url", m_ProxyURL.to_string().c_str());
}
}
void HTTPReqHandler::HandleUpstreamProxyResolved(const boost::system::error_code & ec, boost::asio::ip::tcp::resolver::iterator it, ProxyResolvedHandler handler)
{
if(ec) GenericProxyError("cannot resolve upstream proxy", ec.message().c_str());
else handler(*it);
}
void HTTPReqHandler::HandleUpstreamSocksProxyConnect(const boost::system::error_code & ec)
{
if(!ec) {
if(m_RequestURL.host.size() > 255) {
GenericProxyError("hostname too long", m_RequestURL.host.c_str());
return;
}
uint16_t port = m_RequestURL.port;
if(!port) port = 80;
LogPrint(eLogDebug, "HTTPProxy: connected to socks upstream");
std::string host = m_RequestURL.host;
std::size_t reqsize = 0;
m_socks_buf[0] = '\x04';
m_socks_buf[1] = 1;
htobe16buf(m_socks_buf+2, port);
m_socks_buf[4] = 0;
m_socks_buf[5] = 0;
m_socks_buf[6] = 0;
m_socks_buf[7] = 1;
// user id
m_socks_buf[8] = 'i';
m_socks_buf[9] = '2';
m_socks_buf[10] = 'p';
m_socks_buf[11] = 'd';
m_socks_buf[12] = 0;
reqsize += 13;
memcpy(m_socks_buf+ reqsize, host.c_str(), host.size());
reqsize += host.size();
m_socks_buf[++reqsize] = 0;
boost::asio::async_write(*m_proxysock, boost::asio::buffer(m_socks_buf, reqsize), boost::asio::transfer_all(), std::bind(&HTTPReqHandler::HandleSocksProxySendHandshake, this, std::placeholders::_1, std::placeholders::_2));
} else GenericProxyError("cannot connect to upstream socks proxy", ec.message().c_str());
}
void HTTPReqHandler::HandleSocksProxySendHandshake(const boost::system::error_code & ec, std::size_t bytes_transferred)
{
LogPrint(eLogDebug, "HTTPProxy: upstream socks handshake sent");
if(ec) GenericProxyError("Cannot negotiate with socks proxy", ec.message().c_str());
else m_proxysock->async_read_some(boost::asio::buffer(m_socks_buf, 8), std::bind(&HTTPReqHandler::HandleSocksProxyReply, this, std::placeholders::_1, std::placeholders::_2));
}
void HTTPReqHandler::HandoverToUpstreamProxy()
{
LogPrint(eLogDebug, "HTTPProxy: handover to socks proxy");
auto connection = std::make_shared<i2p::client::TCPIPPipe>(GetOwner(), m_proxysock, m_sock);
m_sock = nullptr;
m_proxysock = nullptr;
GetOwner()->AddHandler(connection);
connection->Start();
Terminate();
}
void HTTPReqHandler::SocksProxySuccess()
{
if(m_ClientRequest.method == "CONNECT") {
m_ClientResponse.code = 200;
m_send_buf = m_ClientResponse.to_string();
boost::asio::async_write(*m_sock, boost::asio::buffer(m_send_buf), boost::asio::transfer_all(), [&] (const boost::system::error_code & ec, std::size_t transferred) {
if(ec) GenericProxyError("socks proxy error", ec.message().c_str());
else HandoverToUpstreamProxy();
});
} else {
m_send_buf = m_ClientRequestBuffer.str();
LogPrint(eLogDebug, "HTTPProxy: send ", m_send_buf.size(), " bytes");
boost::asio::async_write(*m_proxysock, boost::asio::buffer(m_send_buf), boost::asio::transfer_all(), [&](const boost::system::error_code & ec, std::size_t transferred) {
if(ec) GenericProxyError("failed to send request to upstream", ec.message().c_str());
else HandoverToUpstreamProxy();
});
}
}
void HTTPReqHandler::HandleSocksProxyReply(const boost::system::error_code & ec, std::size_t bytes_transferred)
{
if(!ec)
{
if(m_socks_buf[1] == 90) {
// success
SocksProxySuccess();
} else {
std::stringstream ss;
ss << "error code: ";
ss << (int) m_socks_buf[1];
std::string msg = ss.str();
GenericProxyError("Socks Proxy error", msg.c_str());
}
}
else GenericProxyError("No Reply From socks proxy", ec.message().c_str());
}
void HTTPReqHandler::HandleUpstreamHTTPProxyConnect(const boost::system::error_code & ec)
{
if(!ec) {
LogPrint(eLogDebug, "HTTPProxy: connected to http upstream");
GenericProxyError("cannot connect", "http out proxy not implemented");
} else GenericProxyError("cannot connect to upstream http proxy", ec.message().c_str());
}
/* will be called after some data received from client */
void HTTPReqHandler::HandleSockRecv(const boost::system::error_code & ecode, std::size_t len)
{
@@ -317,7 +498,7 @@ namespace proxy {
if (Kill())
return;
LogPrint (eLogDebug, "HTTPProxy: Created new I2PTunnel stream, sSID=", stream->GetSendStreamID(), ", rSID=", stream->GetRecvStreamID());
auto connection = std::make_shared<i2p::client::I2PTunnelConnection>(GetOwner(), m_sock, stream);
auto connection = std::make_shared<i2p::client::I2PClientTunnelConnectionHTTP>(GetOwner(), m_sock, stream);
GetOwner()->AddHandler (connection);
connection->I2PConnect (reinterpret_cast<const uint8_t*>(m_send_buf.data()), m_send_buf.length());
Done (shared_from_this());

View File

@@ -33,18 +33,21 @@ namespace i2p {
namespace http {
const char *itoopieFavicon =
"data:image/png;base64,"
"iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv"
"8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAYdEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My4wOGVynO"
"EAAAIzSURBVDhPjZNdSFNhGMf3nm3n7OzMs+8JtfJGzdlgoPtoWBrkqc1OsLTMKEY3eZOQbbS6aBVYO"
"oO8CKSLXEulQtZNahAM9Cq6lS533UUaeDEEKcN/79x7kbQT/eDhfPB7/u/7Poej08JqtXoEQbhoMpmG"
"ZFn2stf/h8nEZ4aHue1SiWBlhSCV4n41NBifBINBjina8DyfzOUIVlcJtrYINjcJ3rw1oFAg4HnjHaZ"
"p4/Ppv8zPH0G5XKZNPZibO4lKpYJ8vgOqqv+uKMq/d9Hfz/0sFr3w+/3IZt2YnbWhszOAxUUv0mkCs9"
"ncyNT6hEL6dYBgY4Ngd5eger+zU7sODHA/mpubzUytj9FofLa0VGv4s9bWCCTJUGSaNvSzXT3stuHDM"
"rc3xEqF4N2CERciURyyHfgqSZKPqfuxUMyC+OKcL4YHyl28nDFAPdqDZMcQ7tPnSfURUt0jMBgMH1nL"
"fkRRDPvcLds3otfhbRTwasaE8b6He43VSrT3QW3tBT3iPdbyN3T7Ibsor988H8OxtiaMx2sB1aBbCRW"
"R1hbQhbqYXh+6QkaJn8DZyzF09x6HeiaOTC6NK9cSsFqkb3aH3cLU+tCAx9l8FoXPBUy9n8LgyCCmS9"
"MYez0Gm9P2iWna0GOcDp8KY2JhAsnbSQS6Ahh9OgrlklINeM40bWhAkBd4SLIEh8cBURLhOeiBIArVA"
"U4yTRvJItk5PRehQVFaYfpbt9PBtTmdziaXyyUzjaHT/QZBQuKHAA0UxAAAAABJRU5ErkJggg==";
"iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACx"
"jwv8YQUAAAAJcEhZcwAALiIAAC4iAari3ZIAAAAHdElNRQfgCQsUNSZrkhi1AAAAGXRFWHRTb2Z0"
"d2FyZQBwYWludC5uZXQgNC4wLjEyQwRr7AAAAoJJREFUOE9jwAUqi4Q1oEwwcDTV1+5sETaBclGB"
"vb09C5QJB6kWpvFQJoOCeLC5kmjEHCgXE2SlyETLi3h6QrkM4VL+ssWSCZUgtopITLKqaOotRTEn"
"cbAkLqAkGtOqLBLVAWLXyWSVFkkmRiqLxuaqiWb/VBYJMAYrwgckJY25VEUzniqKhjU2y+RtCRSP"
"6lUXy/1jIBV5tlYxZUaFVMq2NInwIi9hO8fSfOEAqDZUoCwal6MulvOvyS7gi69K4j9zxZT/m0ps"
"/28ptvvvquXXryIa7QYMMdTwqi0WNtVi0GIDseXl7TnUxFKfnGlxAGp0+D8j2eH/8Ub7/9e7nf7X"
"+Af/B7rwt6pI0h0l0WhQADOC9DBkhSirpImHNVZKp24ukkyoshGLnN8d5fA/y13t/44Kq/8hlnL/"
"z7fZ/58f6vcxSNpbVUVFhV1RLNBVTsQzVYZPSwhsCAhkiIfpNMrkbO6TLf071Sfk/5ZSi/+7q6z/"
"P5ns+v9mj/P/CpuI/20y+aeNGYxZoVoYGmsF3aFMBAAZlCwftnF9ke3//bU2//fXWP8/UGv731Am"
"+V+DdNblSqnUYqhSTKAiYSOqJBrVqiaa+S3UNPr/gmyH/xuKXf63hnn/B8bIP0UxHfEyyeSNQKVM"
"EB1AEB2twhcTLp+gIBJUoyKasEpVJHmqskh8qryovUG/ffCHHRU2q/Tk/YuB6eGPsbExa7ZkpLu1"
"oLEcVDtuUCgV1w60rQzElpRUE1EVSX0BYidHiInXF4nagNhYQW60EF+ApH1ktni0A1SIITSUgVlZ"
"JHYnlIsfzJjIp9xZKswL5YKBHL+coKJoRDaUSzoozxHVrygQU4JykQADAwAT5b1NHtwZugAAAABJ"
"RU5ErkJggg==";
const char *cssStyles =
"<style>\r\n"
@@ -71,6 +74,7 @@ namespace http {
const char HTTP_PAGE_TRANSPORTS[] = "transports";
const char HTTP_PAGE_LOCAL_DESTINATIONS[] = "local_destinations";
const char HTTP_PAGE_LOCAL_DESTINATION[] = "local_destination";
const char HTTP_PAGE_I2CP_LOCAL_DESTINATION[] = "i2cp_local_destination";
const char HTTP_PAGE_SAM_SESSIONS[] = "sam_sessions";
const char HTTP_PAGE_SAM_SESSION[] = "sam_session";
const char HTTP_PAGE_I2P_TUNNELS[] = "i2p_tunnels";
@@ -86,7 +90,8 @@ namespace http {
const char HTTP_PARAM_SAM_SESSION_ID[] = "id";
const char HTTP_PARAM_ADDRESS[] = "address";
void ShowUptime (std::stringstream& s, int seconds) {
static void ShowUptime (std::stringstream& s, int seconds)
{
int num;
if ((num = seconds / 86400) > 0) {
@@ -104,7 +109,7 @@ namespace http {
s << seconds << " seconds";
}
void ShowTunnelDetails (std::stringstream& s, enum i2p::tunnel::TunnelState eState, int bytes)
static void ShowTunnelDetails (std::stringstream& s, enum i2p::tunnel::TunnelState eState, int bytes)
{
std::string state;
switch (eState) {
@@ -121,7 +126,7 @@ namespace http {
s << " " << (int) (bytes / 1024) << "&nbsp;KiB<br>\r\n";
}
void ShowPageHead (std::stringstream& s)
static void ShowPageHead (std::stringstream& s)
{
s <<
"<!DOCTYPE html>\r\n"
@@ -156,7 +161,7 @@ namespace http {
"<div class=right>";
}
void ShowPageTail (std::stringstream& s)
static void ShowPageTail (std::stringstream& s)
{
s <<
"</div></div>\r\n"
@@ -164,25 +169,44 @@ namespace http {
"</html>\r\n";
}
void ShowError(std::stringstream& s, const std::string& string)
static void ShowError(std::stringstream& s, const std::string& string)
{
s << "<b>ERROR:</b>&nbsp;" << string << "<br>\r\n";
}
void ShowStatus (std::stringstream& s)
static void ShowStatus (std::stringstream& s)
{
s << "<b>Uptime:</b> ";
ShowUptime(s, i2p::context.GetUptime ());
s << "<br>\r\n";
s << "<b>Status:</b> ";
s << "<b>Network status:</b> ";
switch (i2p::context.GetStatus ())
{
case eRouterStatusOK: s << "OK"; break;
case eRouterStatusTesting: s << "Testing"; break;
case eRouterStatusFirewalled: s << "Firewalled"; break;
case eRouterStatusError:
{
s << "Error";
switch (i2p::context.GetError ())
{
case eRouterErrorClockSkew:
s << "<br>Clock skew";
break;
default: ;
}
break;
}
default: s << "Unknown";
}
s << "<br>\r\n";
#if (!defined(WIN32) && !defined(QT_GUI_LIB) && !defined(ANDROID))
if (auto remains = Daemon.gracefulShutdownInterval) {
s << "<b>Stopping in:</b> ";
s << remains << " seconds";
s << "<br>\r\n";
}
#endif
auto family = i2p::context.GetFamily ();
if (family.length () > 0)
s << "<b>Family:</b> " << family << "<br>\r\n";
@@ -206,7 +230,7 @@ namespace http {
else
s << numKBytesSent / 1024 / 1024 << " GiB";
s << " (" << (double) i2p::transport::transports.GetOutBandwidth () / 1024 << " KiB/s)<br>\r\n";
s << "<b>Data path:</b> " << i2p::fs::GetDataDir() << "<br>\r\n<br>\r\n";
s << "<b>Data path:</b> " << i2p::fs::GetDataDir() << "<br>\r\n";
s << "<div class='slide'\r\n><label for='slide1'>Hidden content. Press on text to see.</label>\r\n<input type='checkbox' id='slide1'/>\r\n<p class='content'>\r\n";
s << "<b>Router Ident:</b> " << i2p::context.GetRouterInfo().GetIdentHashBase64() << "<br>\r\n";
s << "<b>Router Family:</b> " << i2p::context.GetRouterInfo().GetProperty("family") << "<br>\r\n";
@@ -234,7 +258,7 @@ namespace http {
s << address->host.to_string() << ":" << address->port << "<br>\r\n";
}
s << "</p>\r\n</div>\r\n";
s << "<br>\r\n<b>Routers:</b> " << i2p::data::netdb.GetNumRouters () << " ";
s << "<b>Routers:</b> " << i2p::data::netdb.GetNumRouters () << " ";
s << "<b>Floodfills:</b> " << i2p::data::netdb.GetNumFloodfills () << " ";
s << "<b>LeaseSets:</b> " << i2p::data::netdb.GetNumLeaseSets () << "<br>\r\n";
@@ -246,18 +270,75 @@ namespace http {
s << "<b>Transit Tunnels:</b> " << std::to_string(transitTunnelCount) << "<br>\r\n";
}
void ShowLocalDestinations (std::stringstream& s)
static void ShowLocalDestinations (std::stringstream& s)
{
s << "<b>Local Destinations:</b><br>\r\n<br>\r\n";
for (auto& it: i2p::client::context.GetDestinations ())
{
auto ident = it.second->GetIdentHash ();;
auto ident = it.second->GetIdentHash ();
s << "<a href=\"/?page=" << HTTP_PAGE_LOCAL_DESTINATION << "&b32=" << ident.ToBase32 () << "\">";
s << i2p::client::context.GetAddressBook ().ToAddress(ident) << "</a><br>\r\n" << std::endl;
}
auto i2cpServer = i2p::client::context.GetI2CPServer ();
if (i2cpServer)
{
s << "<br><b>I2CP Local Destinations:</b><br>\r\n<br>\r\n";
for (auto& it: i2cpServer->GetSessions ())
{
auto dest = it.second->GetDestination ();
if (dest)
{
auto ident = dest->GetIdentHash ();
s << "<a href=\"/?page=" << HTTP_PAGE_I2CP_LOCAL_DESTINATION << "&i2cp_id=" << it.first << "\">";
s << i2p::client::context.GetAddressBook ().ToAddress(ident) << "</a><br>\r\n" << std::endl;
}
}
}
}
void ShowLocalDestination (std::stringstream& s, const std::string& b32)
static void ShowLeaseSetDestination (std::stringstream& s, std::shared_ptr<const i2p::client::LeaseSetDestination> dest)
{
s << "<b>Base64:</b><br>\r\n<textarea readonly=\"readonly\" cols=\"64\" rows=\"11\" wrap=\"on\">";
s << dest->GetIdentity ()->ToBase64 () << "</textarea><br>\r\n<br>\r\n";
s << "<b>LeaseSets:</b> <i>" << dest->GetNumRemoteLeaseSets () << "</i><br>\r\n";
if(dest->GetNumRemoteLeaseSets())
{
s << "<div class='slide'\r\n><label for='slide1'>Hidden content. Press on text to see.</label>\r\n<input type='checkbox' id='slide1'/>\r\n<p class='content'>\r\n";
for(auto& it: dest->GetLeaseSets ())
s << it.second->GetIdentHash ().ToBase32 () << "<br>\r\n";
s << "</p>\r\n</div>\r\n";
}
auto pool = dest->GetTunnelPool ();
if (pool)
{
s << "<b>Inbound tunnels:</b><br>\r\n";
for (auto & it : pool->GetInboundTunnels ()) {
it->Print(s);
if(it->LatencyIsKnown())
s << " ( " << it->GetMeanLatency() << "ms )";
ShowTunnelDetails(s, it->GetState (), it->GetNumReceivedBytes ());
}
s << "<br>\r\n";
s << "<b>Outbound tunnels:</b><br>\r\n";
for (auto & it : pool->GetOutboundTunnels ()) {
it->Print(s);
if(it->LatencyIsKnown())
s << " ( " << it->GetMeanLatency() << "ms )";
ShowTunnelDetails(s, it->GetState (), it->GetNumSentBytes ());
}
}
s << "<br>\r\n";
s << "<b>Tags</b><br>Incoming: " << dest->GetNumIncomingTags () << "<br>Outgoing:<br>" << std::endl;
for (const auto& it: dest->GetSessions ())
{
s << i2p::client::context.GetAddressBook ().ToAddress(it.first) << " ";
s << it.second->GetNumOutgoingTags () << "<br>" << std::endl;
}
s << "<br>" << std::endl;
}
static void ShowLocalDestination (std::stringstream& s, const std::string& b32)
{
s << "<b>Local Destination:</b><br>\r\n<br>\r\n";
i2p::data::IdentHash ident;
@@ -265,44 +346,8 @@ namespace http {
auto dest = i2p::client::context.FindLocalDestination (ident);
if (dest)
{
s << "<b>Base64:</b><br>\r\n<textarea readonly=\"readonly\" cols=\"64\" rows=\"11\" wrap=\"on\">";
s << dest->GetIdentity ()->ToBase64 () << "</textarea><br>\r\n<br>\r\n";
s << "<b>LeaseSets:</b> <i>" << dest->GetNumRemoteLeaseSets () << "</i><br>\r\n";
auto pool = dest->GetTunnelPool ();
if (pool)
{
s << "<b>Inbound tunnels:</b><br>\r\n";
for (auto & it : pool->GetInboundTunnels ()) {
it->Print(s);
ShowTunnelDetails(s, it->GetState (), it->GetNumReceivedBytes ());
}
s << "<br>\r\n";
s << "<b>Outbound tunnels:</b><br>\r\n";
for (auto & it : pool->GetOutboundTunnels ()) {
it->Print(s);
ShowTunnelDetails(s, it->GetState (), it->GetNumSentBytes ());
}
}
s << "<br>\r\n";
s << "<b>Tags</b><br>Incoming: " << dest->GetNumIncomingTags () << "<br>Outgoing:<br>" << std::endl;
for (const auto& it: dest->GetSessions ())
{
s << i2p::client::context.GetAddressBook ().ToAddress(it.first) << " ";
s << it.second->GetNumOutgoingTags () << "<br>" << std::endl;
}
s << "<br>" << std::endl;
// s << "<br>\r\n<b>Streams:</b><br>\r\n";
// for (auto it: dest->GetStreamingDestination ()->GetStreams ())
// {
// s << it.first << "->" << i2p::client::context.GetAddressBook ().ToAddress(it.second->GetRemoteIdentity ()) << " ";
// s << " [" << it.second->GetNumSentBytes () << ":" << it.second->GetNumReceivedBytes () << "]";
// s << " [out:" << it.second->GetSendQueueSize () << "][in:" << it.second->GetReceiveQueueSize () << "]";
// s << "[buf:" << it.second->GetSendBufferSize () << "]";
// s << "[RTT:" << it.second->GetRTT () << "]";
// s << "[Window:" << it.second->GetWindowSize () << "]";
// s << "[Status:" << (int)it.second->GetStatus () << "]";
// s << "<br>\r\n"<< std::endl;
// }
ShowLeaseSetDestination (s, dest);
// show streams
s << "<br>\r\n<table><caption>Streams</caption><tr>";
s << "<th>StreamID</th>";
s << "<th>Destination</th>";
@@ -330,108 +375,105 @@ namespace http {
s << "<td>" << it->GetWindowSize () << "</td>";
s << "<td>" << (int)it->GetStatus () << "</td>";
s << "</tr><br>\r\n" << std::endl;
}
}
s << "</table>";
}
}
void ShowLeasesSets(std::stringstream& s)
static void ShowI2CPLocalDestination (std::stringstream& s, const std::string& id)
{
s << "<div id='leasesets'><b>LeaseSets:</b></div><br>";
auto i2cpServer = i2p::client::context.GetI2CPServer ();
if (i2cpServer)
{
s << "<b>I2CP Local Destination:</b><br>\r\n<br>\r\n";
auto it = i2cpServer->GetSessions ().find (std::stoi (id));
if (it != i2cpServer->GetSessions ().end ())
ShowLeaseSetDestination (s, it->second->GetDestination ());
else
ShowError(s, "I2CP session not found");
}
else
ShowError(s, "I2CP is not enabled");
}
static void ShowLeasesSets(std::stringstream& s)
{
s << "<div id='leasesets'><b>LeaseSets (click on to show info):</b></div><br>\r\n";
int counter = 1;
// for each lease set
i2p::data::netdb.VisitLeaseSets(
[&s](const i2p::data::IdentHash dest, std::shared_ptr<i2p::data::LeaseSet> leaseSet)
[&s, &counter](const i2p::data::IdentHash dest, std::shared_ptr<i2p::data::LeaseSet> leaseSet)
{
// create copy of lease set so we extract leases
i2p::data::LeaseSet ls(leaseSet->GetBuffer(), leaseSet->GetBufferLen());
// begin lease set entry
s << "<div class='leaseset";
if (ls.IsExpired())
s << " expired"; // additional css class for expired
s << "'>";
// invalid ?
s << "'>\r\n";
if (!ls.IsValid())
s << "<div class='invalid'>!! Invalid !! </div>";
// ident
s << "<div class='ident'>" << dest.ToBase32() << "</div>";
// LeaseSet time
s << "<div class='expires'>expires: " << ls.GetExpirationTime() << "</div>";
// get non expired leases
s << "<div class='invalid'>!! Invalid !! </div>\r\n";
s << "<div class='slide'><label for='slide" << counter << "'>" << dest.ToBase32() << "</label>\r\n";
s << "<input type='checkbox' id='slide" << (counter++) << "'/>\r\n<p class='content'>\r\n";
s << "<b>Expires:</b> " << ls.GetExpirationTime() << "<br>\r\n";
auto leases = ls.GetNonExpiredLeases();
// show non expired leases
s << "<div class='leasecount'>Non Expired Leases: " << leases.size() << "</div>";
// for each lease
s << "<div class='leases'>";
s << "<b>Non Expired Leases: " << leases.size() << "</b><br>\r\n";
for ( auto & l : leases )
{
// begin lease
s << "<div class='lease'>";
// gateway
s << "<div class='gateway'>Gateway: " << l->tunnelGateway.ToBase64() << "</div>";
// tunnel id
s << "<div class='tunnelID'>TunnelID: " << l->tunnelID << "</div>";
// end date
s << "<div class='endDate'>EndDate: " << l->endDate << "</div>";
// end lease
s << "</div>";
s << "<b>Gateway:</b> " << l->tunnelGateway.ToBase64() << "<br>\r\n";
s << "<b>TunnelID:</b> " << l->tunnelID << "<br>\r\n";
s << "<b>EndDate:</b> " << l->endDate << "<br>\r\n";
}
// end for each lease
s << "</div>";
// end lease set entry
s << "</div>";
// linebreak
s << "<br>";
s << "</p>\r\n</div>\r\n</div>\r\n";
}
);
// end for each lease set
}
void ShowTunnels (std::stringstream& s)
static void ShowTunnels (std::stringstream& s)
{
s << "<b>Queue size:</b> " << i2p::tunnel::tunnels.GetQueueSize () << "<br>\r\n";
s << "<b>Inbound tunnels:</b><br>\r\n";
for (auto & it : i2p::tunnel::tunnels.GetInboundTunnels ()) {
it->Print(s);
if(it->LatencyIsKnown())
s << " ( " << it->GetMeanLatency() << "ms )";
ShowTunnelDetails(s, it->GetState (), it->GetNumReceivedBytes ());
}
s << "<br>\r\n";
s << "<b>Outbound tunnels:</b><br>\r\n";
for (auto & it : i2p::tunnel::tunnels.GetOutboundTunnels ()) {
it->Print(s);
if(it->LatencyIsKnown())
s << " ( " << it->GetMeanLatency() << "ms )";
ShowTunnelDetails(s, it->GetState (), it->GetNumSentBytes ());
}
s << "<br>\r\n";
}
void ShowCommands (std::stringstream& s)
static void ShowCommands (std::stringstream& s, uint32_t token)
{
/* commands */
s << "<b>Router Commands</b><br>\r\n";
s << " <a href=\"/?cmd=" << HTTP_COMMAND_RUN_PEER_TEST << "\">Run peer test</a><br>\r\n";
s << " <a href=\"/?cmd=" << HTTP_COMMAND_RUN_PEER_TEST << "&token=" << token << "\">Run peer test</a><br>\r\n";
//s << " <a href=\"/?cmd=" << HTTP_COMMAND_RELOAD_CONFIG << "\">Reload config</a><br>\r\n";
if (i2p::context.AcceptsTunnels ())
s << " <a href=\"/?cmd=" << HTTP_COMMAND_DISABLE_TRANSIT << "\">Decline transit tunnels</a><br>\r\n";
s << " <a href=\"/?cmd=" << HTTP_COMMAND_DISABLE_TRANSIT << "&token=" << token << "\">Decline transit tunnels</a><br>\r\n";
else
s << " <a href=\"/?cmd=" << HTTP_COMMAND_ENABLE_TRANSIT << "\">Accept transit tunnels</a><br>\r\n";
s << " <a href=\"/?cmd=" << HTTP_COMMAND_ENABLE_TRANSIT << "&token=" << token << "\">Accept transit tunnels</a><br>\r\n";
#if (!defined(WIN32) && !defined(QT_GUI_LIB) && !defined(ANDROID))
if (Daemon.gracefullShutdownInterval)
{
s << " <a href=\"/?cmd=" << HTTP_COMMAND_SHUTDOWN_CANCEL << "\">Cancel gracefull shutdown (";
s << Daemon.gracefullShutdownInterval;
s << " seconds remains)</a><br>\r\n";
}
if (Daemon.gracefulShutdownInterval)
s << " <a href=\"/?cmd=" << HTTP_COMMAND_SHUTDOWN_CANCEL << "&token=" << token << "\">Cancel graceful shutdown</a><br>";
else
{
s << " <a href=\"/?cmd=" << HTTP_COMMAND_SHUTDOWN_START << "\">Start gracefull shutdown</a><br>\r\n";
}
s << " <a href=\"/?cmd=" << HTTP_COMMAND_SHUTDOWN_START << "&token=" << token << "\">Start graceful shutdown</a><br>\r\n";
#endif
#ifdef WIN32_APP
s << " <a href=\"/?cmd=" << HTTP_COMMAND_SHUTDOWN_START << "\">Gracefull shutdown</a><br>\r\n";
s << " <a href=\"/?cmd=" << HTTP_COMMAND_SHUTDOWN_START << "&token=" << token << "\">Graceful shutdown</a><br>\r\n";
#endif
s << " <a href=\"/?cmd=" << HTTP_COMMAND_SHUTDOWN_NOW << "\">Force shutdown</a><br>\r\n";
s << " <a href=\"/?cmd=" << HTTP_COMMAND_SHUTDOWN_NOW << "&token=" << token << "\">Force shutdown</a><br>\r\n";
}
void ShowTransitTunnels (std::stringstream& s)
static void ShowTransitTunnels (std::stringstream& s)
{
s << "<b>Transit tunnels:</b><br>\r\n<br>\r\n";
for (const auto& it: i2p::tunnel::tunnels.GetTransitTunnels ())
@@ -446,14 +488,15 @@ namespace http {
}
}
void ShowTransports (std::stringstream& s)
static void ShowTransports (std::stringstream& s)
{
s << "<b>Transports:</b><br>\r\n<br>\r\n";
auto ntcpServer = i2p::transport::transports.GetNTCPServer ();
if (ntcpServer)
{
s << "<b>NTCP</b><br>\r\n";
for (const auto& it: ntcpServer->GetNTCPSessions ())
auto sessions = ntcpServer->GetNTCPSessions ();
s << "<b>NTCP</b> ( " << (int) sessions.size() << " )<br>\r\n";
for (const auto& it: sessions )
{
if (it.second && it.second->IsEstablished ())
{
@@ -470,8 +513,9 @@ namespace http {
auto ssuServer = i2p::transport::transports.GetSSUServer ();
if (ssuServer)
{
s << "<br>\r\n<b>SSU</b><br>\r\n";
for (const auto& it: ssuServer->GetSessions ())
auto sessions = ssuServer->GetSessions ();
s << "<br>\r\n<b>SSU</b> ( " << (int) sessions.size() << " )<br>\r\n";
for (const auto& it: sessions)
{
auto endpoint = it.second->GetRemoteEndpoint ();
if (it.second->IsOutgoing ()) s << " &#8658; ";
@@ -495,7 +539,7 @@ namespace http {
}
}
void ShowSAMSessions (std::stringstream& s)
static void ShowSAMSessions (std::stringstream& s)
{
auto sam = i2p::client::context.GetSAMBridge ();
if (!sam) {
@@ -510,7 +554,7 @@ namespace http {
}
}
void ShowSAMSession (std::stringstream& s, const std::string& id)
static void ShowSAMSession (std::stringstream& s, const std::string& id)
{
s << "<b>SAM Session:</b><br>\r\n<br>\r\n";
auto sam = i2p::client::context.GetSAMBridge ();
@@ -542,7 +586,7 @@ namespace http {
}
}
void ShowI2PTunnels (std::stringstream& s)
static void ShowI2PTunnels (std::stringstream& s)
{
s << "<b>Client Tunnels:</b><br>\r\n<br>\r\n";
for (auto& it: i2p::client::context.GetClientTunnels ())
@@ -553,6 +597,24 @@ namespace http {
s << i2p::client::context.GetAddressBook ().ToAddress(ident);
s << "<br>\r\n"<< std::endl;
}
auto httpProxy = i2p::client::context.GetHttpProxy ();
if (httpProxy)
{
auto& ident = httpProxy->GetLocalDestination ()->GetIdentHash();
s << "<a href=\"/?page=" << HTTP_PAGE_LOCAL_DESTINATION << "&b32=" << ident.ToBase32 () << "\">";
s << "HTTP Proxy" << "</a> &#8656; ";
s << i2p::client::context.GetAddressBook ().ToAddress(ident);
s << "<br>\r\n"<< std::endl;
}
auto socksProxy = i2p::client::context.GetSocksProxy ();
if (socksProxy)
{
auto& ident = socksProxy->GetLocalDestination ()->GetIdentHash();
s << "<a href=\"/?page=" << HTTP_PAGE_LOCAL_DESTINATION << "&b32=" << ident.ToBase32 () << "\">";
s << "SOCKS Proxy" << "</a> &#8656; ";
s << i2p::client::context.GetAddressBook ().ToAddress(ident);
s << "<br>\r\n"<< std::endl;
}
s << "<br>\r\n<b>Server Tunnels:</b><br>\r\n<br>\r\n";
for (auto& it: i2p::client::context.GetServerTunnels ())
{
@@ -563,6 +625,32 @@ namespace http {
s << ":" << it.second->GetLocalPort ();
s << "</a><br>\r\n"<< std::endl;
}
auto& clientForwards = i2p::client::context.GetClientForwards ();
if (!clientForwards.empty ())
{
s << "<br>\r\n<b>Client Forwards:</b><br>\r\n<br>\r\n";
for (auto& it: clientForwards)
{
auto& ident = it.second->GetLocalDestination ()->GetIdentHash();
s << "<a href=\"/?page=" << HTTP_PAGE_LOCAL_DESTINATION << "&b32=" << ident.ToBase32 () << "\">";
s << it.second->GetName () << "</a> &#8656; ";
s << i2p::client::context.GetAddressBook ().ToAddress(ident);
s << "<br>\r\n"<< std::endl;
}
}
auto& serverForwards = i2p::client::context.GetServerForwards ();
if (!serverForwards.empty ())
{
s << "<br>\r\n<b>Server Forwards:</b><br>\r\n<br>\r\n";
for (auto& it: serverForwards)
{
auto& ident = it.second->GetLocalDestination ()->GetIdentHash();
s << "<a href=\"/?page=" << HTTP_PAGE_LOCAL_DESTINATION << "&b32=" << ident.ToBase32 () << "\">";
s << it.second->GetName () << "</a> &#8656; ";
s << i2p::client::context.GetAddressBook ().ToAddress(ident);
s << "<br>\r\n"<< std::endl;
}
}
}
HTTPConnection::HTTPConnection (std::shared_ptr<boost::asio::ip::tcp::socket> socket):
@@ -626,17 +714,25 @@ namespace http {
return true;
}
/* method #2: 'Authorization' header sent */
if (req.headers.count("Authorization") > 0) {
std::string provided = req.headers.find("Authorization")->second;
auto provided = req.GetHeader ("Authorization");
if (provided.length () > 0)
{
bool result = false;
std::string expected = user + ":" + pass;
char b64_creds[64];
size_t b64_sz = i2p::data::Base64EncodingBufferSize(expected.length()) + 1;
char * b64_creds = new char[b64_sz];
std::size_t len = 0;
len = i2p::data::ByteStreamToBase64((unsigned char *)expected.c_str(), expected.length(), b64_creds, sizeof(b64_creds));
b64_creds[len] = '\0';
expected = "Basic ";
expected += b64_creds;
if (provided == expected)
return true;
len = i2p::data::ByteStreamToBase64((unsigned char *)expected.c_str(), expected.length(), b64_creds, b64_sz);
/* if we decoded properly then check credentials */
if(len) {
b64_creds[len] = '\0';
expected = "Basic ";
expected += b64_creds;
result = expected == provided;
}
delete [] b64_creds;
return result;
}
LogPrint(eLogWarning, "HTTPServer: auth failure from ", m_Socket->remote_endpoint().address ());
@@ -675,6 +771,7 @@ namespace http {
SendReply (res, content);
}
std::map<uint32_t, uint32_t> HTTPConnection::m_Tokens;
void HTTPConnection::HandlePage (const HTTPReq& req, HTTPRes& res, std::stringstream& s)
{
std::map<std::string, std::string> params;
@@ -690,13 +787,29 @@ namespace http {
else if (page == HTTP_PAGE_TUNNELS)
ShowTunnels (s);
else if (page == HTTP_PAGE_COMMANDS)
ShowCommands (s);
{
uint32_t token;
RAND_bytes ((uint8_t *)&token, 4);
token &= 0x7FFFFFFF; // clear first bit
auto ts = i2p::util::GetSecondsSinceEpoch ();
for (auto it = m_Tokens.begin (); it != m_Tokens.end (); )
{
if (ts > it->second + TOKEN_EXPIRATION_TIMEOUT)
it = m_Tokens.erase (it);
else
++it;
}
m_Tokens[token] = ts;
ShowCommands (s, token);
}
else if (page == HTTP_PAGE_TRANSIT_TUNNELS)
ShowTransitTunnels (s);
else if (page == HTTP_PAGE_LOCAL_DESTINATIONS)
ShowLocalDestinations (s);
else if (page == HTTP_PAGE_LOCAL_DESTINATION)
ShowLocalDestination (s, params["b32"]);
else if (page == HTTP_PAGE_I2CP_LOCAL_DESTINATION)
ShowI2CPLocalDestination (s, params["i2cp_id"]);
else if (page == HTTP_PAGE_SAM_SESSIONS)
ShowSAMSessions (s);
else if (page == HTTP_PAGE_SAM_SESSION)
@@ -715,13 +828,19 @@ namespace http {
void HTTPConnection::HandleCommand (const HTTPReq& req, HTTPRes& res, std::stringstream& s)
{
std::map<std::string, std::string> params;
std::string cmd("");
URL url;
url.parse(req.uri);
url.parse_query(params);
cmd = params["cmd"];
std::string token = params["token"];
if (token.empty () || m_Tokens.find (std::stoi (token)) == m_Tokens.end ())
{
ShowError(s, "Invalid token");
return;
}
std::string cmd = params["cmd"];
if (cmd == HTTP_COMMAND_RUN_PEER_TEST)
i2p::transport::transports.PeerTest ();
else if (cmd == HTTP_COMMAND_RELOAD_CONFIG)
@@ -733,7 +852,7 @@ namespace http {
else if (cmd == HTTP_COMMAND_SHUTDOWN_START) {
i2p::context.SetAcceptsTunnels (false);
#if (!defined(WIN32) && !defined(QT_GUI_LIB) && !defined(ANDROID))
Daemon.gracefullShutdownInterval = 10*60;
Daemon.gracefulShutdownInterval = 10*60;
#endif
#ifdef WIN32_APP
i2p::win32::GracefulShutdown ();
@@ -741,7 +860,7 @@ namespace http {
} else if (cmd == HTTP_COMMAND_SHUTDOWN_CANCEL) {
i2p::context.SetAcceptsTunnels (true);
#if (!defined(WIN32) && !defined(QT_GUI_LIB) && !defined(ANDROID))
Daemon.gracefullShutdownInterval = 0;
Daemon.gracefulShutdownInterval = 0;
#endif
} else if (cmd == HTTP_COMMAND_SHUTDOWN_NOW) {
Daemon.running = false;
@@ -758,16 +877,17 @@ namespace http {
void HTTPConnection::SendReply (HTTPRes& reply, std::string& content)
{
reply.add_header("X-Frame-Options", "SAMEORIGIN");
reply.add_header("Content-Type", "text/html");
reply.body = content;
std::string res = reply.to_string();
boost::asio::async_write (*m_Socket, boost::asio::buffer(res),
m_SendBuffer = reply.to_string();
boost::asio::async_write (*m_Socket, boost::asio::buffer(m_SendBuffer),
std::bind (&HTTPConnection::Terminate, shared_from_this (), std::placeholders::_1));
}
HTTPServer::HTTPServer (const std::string& address, int port):
m_Thread (nullptr), m_Work (m_Service),
m_IsRunning (false), m_Thread (nullptr), m_Work (m_Service),
m_Acceptor (m_Service, boost::asio::ip::tcp::endpoint (boost::asio::ip::address::from_string(address), port))
{
}
@@ -796,6 +916,7 @@ namespace http {
i2p::config::SetOption("http.pass", pass);
LogPrint(eLogInfo, "HTTPServer: password set to ", pass);
}
m_IsRunning = true;
m_Thread = std::unique_ptr<std::thread>(new std::thread (std::bind (&HTTPServer::Run, this)));
m_Acceptor.listen ();
Accept ();
@@ -803,9 +924,11 @@ namespace http {
void HTTPServer::Stop ()
{
m_IsRunning = false;
m_Acceptor.close();
m_Service.stop ();
if (m_Thread) {
if (m_Thread)
{
m_Thread->join ();
m_Thread = nullptr;
}
@@ -813,7 +936,17 @@ namespace http {
void HTTPServer::Run ()
{
m_Service.run ();
while (m_IsRunning)
{
try
{
m_Service.run ();
}
catch (std::exception& ex)
{
LogPrint (eLogError, "HTTPServer: runtime exception: ", ex.what ());
}
}
}
void HTTPServer::Accept ()
@@ -827,7 +960,13 @@ namespace http {
std::shared_ptr<boost::asio::ip::tcp::socket> newSocket)
{
if (ecode)
{
if(newSocket) newSocket->close();
LogPrint(eLogError, "HTTP Server: error handling accept ", ecode.message());
if(ecode != boost::asio::error::operation_aborted)
Accept();
return;
}
CreateConnection(newSocket);
Accept ();
}

View File

@@ -1,10 +1,20 @@
#ifndef HTTP_SERVER_H__
#define HTTP_SERVER_H__
namespace i2p {
namespace http {
extern const char *itoopieFavicon;
const size_t HTTP_CONNECTION_BUFFER_SIZE = 8192;
#include <inttypes.h>
#include <string>
#include <memory>
#include <map>
#include <thread>
#include <boost/asio.hpp>
#include "HTTP.h"
namespace i2p
{
namespace http
{
const size_t HTTP_CONNECTION_BUFFER_SIZE = 8192;
const int TOKEN_EXPIRATION_TIMEOUT = 30; // in seconds
class HTTPConnection: public std::enable_shared_from_this<HTTPConnection>
{
@@ -31,9 +41,12 @@ namespace http {
boost::asio::deadline_timer m_Timer;
char m_Buffer[HTTP_CONNECTION_BUFFER_SIZE + 1];
size_t m_BufferLen;
std::string m_SendBuffer;
bool needAuth;
std::string user;
std::string pass;
static std::map<uint32_t, uint32_t> m_Tokens; // token->timestamp in seconds
};
class HTTPServer
@@ -56,6 +69,7 @@ namespace http {
private:
bool m_IsRunning;
std::unique_ptr<std::thread> m_Thread;
boost::asio::io_service m_Service;
boost::asio::io_service::work m_Work;

View File

@@ -66,12 +66,19 @@ namespace client
memcpy (buf + 4, payload, len);
msg->len += len + 4;
msg->FillI2NPMessageHeader (eI2NPData);
auto s = GetSharedFromThis ();
auto remote = FindLeaseSet (ident);
if (remote)
GetService ().post (std::bind (&I2CPDestination::SendMsg, GetSharedFromThis (), msg, remote));
{
GetService ().post (
[s, msg, remote, nonce]()
{
bool sent = s->SendMsg (msg, remote);
s->m_Owner->SendMessageStatusMessage (nonce, sent ? eI2CPMessageStatusGuaranteedSuccess : eI2CPMessageStatusGuaranteedFailure);
});
}
else
{
auto s = GetSharedFromThis ();
RequestDestination (ident,
[s, msg, nonce](std::shared_ptr<i2p::data::LeaseSet> ls)
{
@@ -109,7 +116,7 @@ namespace client
}
else
{
auto outboundTunnel = GetTunnelPool ()->GetNextOutboundTunnel ();
outboundTunnel = GetTunnelPool ()->GetNextOutboundTunnel ();
auto leases = remote->GetNonExpiredLeases ();
if (!leases.empty ())
remoteLease = leases[rand () % leases.size ()];
@@ -339,6 +346,7 @@ namespace client
void I2CPSession::CreateSessionMessageHandler (const uint8_t * buf, size_t len)
{
RAND_bytes ((uint8_t *)&m_SessionID, 2);
m_Owner.InsertSession (shared_from_this ());
auto identity = std::make_shared<i2p::data::IdentityEx>();
size_t offset = identity->FromBuffer (buf, len);
if (!offset)
@@ -453,23 +461,23 @@ namespace client
{
i2p::data::IdentityEx identity;
size_t identsize = identity.FromBuffer (buf + offset, len - offset);
if (identsize)
{
offset += identsize;
uint32_t payloadLen = bufbe32toh (buf + offset);
if (payloadLen + offset <= len)
{
offset += 4;
uint32_t nonce = bufbe32toh (buf + offset + payloadLen);
if (m_IsSendAccepted)
SendMessageStatusMessage (nonce, eI2CPMessageStatusAccepted); // accepted
m_Destination->SendMsgTo (buf + offset, payloadLen, identity.GetIdentHash (), nonce);
}
else
LogPrint(eLogError, "I2CP: cannot send message, too big");
}
else
LogPrint(eLogError, "I2CP: invalid identity");
if (identsize)
{
offset += identsize;
uint32_t payloadLen = bufbe32toh (buf + offset);
if (payloadLen + offset <= len)
{
offset += 4;
uint32_t nonce = bufbe32toh (buf + offset + payloadLen);
if (m_IsSendAccepted)
SendMessageStatusMessage (nonce, eI2CPMessageStatusAccepted); // accepted
m_Destination->SendMsgTo (buf + offset, payloadLen, identity.GetIdentHash (), nonce);
}
else
LogPrint(eLogError, "I2CP: cannot send message, too big");
}
else
LogPrint(eLogError, "I2CP: invalid identity");
}
}
else
@@ -704,7 +712,6 @@ namespace client
{
LogPrint (eLogDebug, "I2CP: new connection from ", ep);
auto session = std::make_shared<I2CPSession>(*this, socket);
m_Sessions[session->GetSessionID ()] = session;
session->Start ();
}
else
@@ -717,6 +724,17 @@ namespace client
Accept ();
}
bool I2CPServer::InsertSession (std::shared_ptr<I2CPSession> session)
{
if (!session) return false;
if (!m_Sessions.insert({session->GetSessionID (), session}).second)
{
LogPrint (eLogError, "I2CP: duplicate session id ", session->GetSessionID ());
return false;
}
return true;
}
void I2CPServer::RemoveSession (uint16_t sessionID)
{
m_Sessions.erase (sessionID);

5
I2CP.h
View File

@@ -112,6 +112,7 @@ namespace client
void Start ();
void Stop ();
uint16_t GetSessionID () const { return m_SessionID; };
std::shared_ptr<const I2CPDestination> GetDestination () const { return m_Destination; };
// called from I2CPDestination
void SendI2CPMessage (uint8_t type, const uint8_t * payload, size_t len);
@@ -173,6 +174,7 @@ namespace client
void Stop ();
boost::asio::io_service& GetService () { return m_Service; };
bool InsertSession (std::shared_ptr<I2CPSession> session);
void RemoveSession (uint16_t sessionID);
private:
@@ -196,6 +198,9 @@ namespace client
public:
const decltype(m_MessagesHandlers)& GetMessagesHandlers () const { return m_MessagesHandlers; };
// for HTTP
const decltype(m_Sessions)& GetSessions () const { return m_Sessions; };
};
}
}

View File

@@ -27,6 +27,13 @@ namespace i2p
return std::make_shared<I2NPMessageBuffer<I2NP_MAX_SHORT_MESSAGE_SIZE> >();
}
std::shared_ptr<I2NPMessage> NewI2NPTunnelMessage ()
{
auto msg = new I2NPMessageBuffer<i2p::tunnel::TUNNEL_DATA_MSG_SIZE + I2NP_HEADER_SIZE + 34>(); // reserved for alignment and NTCP 16 + 6 + 12
msg->Align (12);
return std::shared_ptr<I2NPMessage>(msg);
}
std::shared_ptr<I2NPMessage> NewI2NPMessage (size_t len)
{
return (len < I2NP_MAX_SHORT_MESSAGE_SIZE/2) ? NewI2NPShortMessage () : NewI2NPMessage ();
@@ -102,7 +109,7 @@ namespace i2p
{
RAND_bytes ((uint8_t *)&msgID, 4);
htobe32buf (buf + DELIVERY_STATUS_MSGID_OFFSET, msgID);
htobe64buf (buf + DELIVERY_STATUS_TIMESTAMP_OFFSET, I2PD_NET_ID);
htobe64buf (buf + DELIVERY_STATUS_TIMESTAMP_OFFSET, i2p::context.GetNetID ());
}
m->len += DELIVERY_STATUS_SIZE;
m->FillI2NPMessageHeader (eI2NPDeliveryStatus);
@@ -464,7 +471,7 @@ namespace i2p
std::shared_ptr<I2NPMessage> CreateTunnelDataMsg (const uint8_t * buf)
{
auto msg = NewI2NPShortMessage ();
auto msg = NewI2NPTunnelMessage ();
msg->Concat (buf, i2p::tunnel::TUNNEL_DATA_MSG_SIZE);
msg->FillI2NPMessageHeader (eI2NPTunnelData);
return msg;
@@ -472,7 +479,7 @@ namespace i2p
std::shared_ptr<I2NPMessage> CreateTunnelDataMsg (uint32_t tunnelID, const uint8_t * payload)
{
auto msg = NewI2NPShortMessage ();
auto msg = NewI2NPTunnelMessage ();
htobe32buf (msg->GetPayload (), tunnelID);
msg->len += 4; // tunnelID
msg->Concat (payload, i2p::tunnel::TUNNEL_DATA_MSG_SIZE - 4);
@@ -482,7 +489,7 @@ namespace i2p
std::shared_ptr<I2NPMessage> CreateEmptyTunnelDataMsg ()
{
auto msg = NewI2NPShortMessage ();
auto msg = NewI2NPTunnelMessage ();
msg->len += i2p::tunnel::TUNNEL_DATA_MSG_SIZE;
return msg;
}
@@ -547,7 +554,6 @@ namespace i2p
uint8_t typeID = msg[I2NP_HEADER_TYPEID_OFFSET];
uint32_t msgID = bufbe32toh (msg + I2NP_HEADER_MSGID_OFFSET);
LogPrint (eLogDebug, "I2NP: msg received len=", len,", type=", (int)typeID, ", msgID=", (unsigned int)msgID);
uint8_t * buf = msg + I2NP_HEADER_SIZE;
int size = bufbe16toh (msg + I2NP_HEADER_SIZE_OFFSET);
switch (typeID)

View File

@@ -2,6 +2,8 @@
#include <sstream>
#include <openssl/x509.h>
#include <openssl/pem.h>
#include <boost/lexical_cast.hpp>
#include <boost/date_time/local_time/local_time.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/property_tree/ini_parser.hpp>
@@ -14,7 +16,6 @@
#include "Crypto.h"
#include "FS.h"
#include "Log.h"
#include "HTTP.h"
#include "Config.h"
#include "NetDb.h"
#include "RouterContext.h"
@@ -24,6 +25,7 @@
#include "Transports.h"
#include "version.h"
#include "util.h"
#include "ClientContext.h"
#include "I2PControl.h"
namespace i2p
@@ -77,6 +79,8 @@ namespace client
m_RouterInfoHandlers["i2p.router.net.bw.outbound.1s"] = &I2PControlService::OutboundBandwidth1S;
m_RouterInfoHandlers["i2p.router.net.status"] = &I2PControlService::NetStatusHandler;
m_RouterInfoHandlers["i2p.router.net.tunnels.participating"] = &I2PControlService::TunnelsParticipatingHandler;
m_RouterInfoHandlers["i2p.router.net.tunnels.successrate"] =
&I2PControlService::TunnelsSuccessRateHandler;
m_RouterInfoHandlers["i2p.router.net.total.received.bytes"] = &I2PControlService::NetTotalReceivedBytes;
m_RouterInfoHandlers["i2p.router.net.total.sent.bytes"] = &I2PControlService::NetTotalSentBytes;
@@ -186,67 +190,84 @@ namespace client
size_t bytes_transferred, std::shared_ptr<ssl_socket> socket,
std::shared_ptr<I2PControlBuffer> buf)
{
if (ecode) {
if (ecode)
{
LogPrint (eLogError, "I2PControl: read error: ", ecode.message ());
return;
}
/* try to parse received data */
std::stringstream json;
std::string response;
bool isHTTP = false;
if (memcmp (buf->data (), "POST", 4) == 0) {
long int remains = 0;
isHTTP = true;
i2p::http::HTTPReq req;
std::size_t len = req.parse(buf->data(), bytes_transferred);
if (len <= 0) {
LogPrint(eLogError, "I2PControl: incomplete/malformed POST request");
return;
}
/* append to json chunk of data from 1st request */
json.write(buf->data() + len, bytes_transferred - len);
remains = req.content_length() - len;
/* if request has Content-Length header, fetch rest of data and store to json buffer */
while (remains > 0) {
len = ((long int) buf->size() < remains) ? buf->size() : remains;
bytes_transferred = boost::asio::read (*socket, boost::asio::buffer (buf->data (), len));
json.write(buf->data(), bytes_transferred);
remains -= bytes_transferred;
}
} else {
json.write(buf->data(), bytes_transferred);
}
LogPrint(eLogDebug, "I2PControl: json from request: ", json.str());
}
else
{
bool isHtml = !memcmp (buf->data (), "POST", 4);
try
{
std::stringstream ss;
ss.write (buf->data (), bytes_transferred);
if (isHtml)
{
std::string header;
size_t contentLength = 0;
while (!ss.eof () && header != "\r")
{
std::getline(ss, header);
auto colon = header.find (':');
if (colon != std::string::npos && header.substr (0, colon) == "Content-Length")
contentLength = std::stoi (header.substr (colon + 1));
}
if (ss.eof ())
{
LogPrint (eLogError, "I2PControl: malformed request, HTTP header expected");
return; // TODO:
}
std::streamoff rem = contentLength + ss.tellg () - bytes_transferred; // more bytes to read
if (rem > 0)
{
bytes_transferred = boost::asio::read (*socket, boost::asio::buffer (buf->data (), rem));
ss.write (buf->data (), bytes_transferred);
}
}
std::ostringstream response;
#if GCC47_BOOST149
LogPrint (eLogError, "I2PControl: json_read is not supported due bug in boost 1.49 with gcc 4.7");
BuildErrorResponse(response, 32603, "JSON requests is not supported with this version of boost");
LogPrint (eLogError, "I2PControl: json_read is not supported due bug in boost 1.49 with gcc 4.7");
response << "{\"id\":null,\"error\":";
response << "{\"code\":-32603,\"message\":\"JSON requests is not supported with this version of boost\"},";
response << "\"jsonrpc\":\"2.0\"}";
#else
/* now try to parse json itself */
try {
boost::property_tree::ptree pt;
boost::property_tree::read_json (json, pt);
boost::property_tree::ptree pt;
boost::property_tree::read_json (ss, pt);
std::string id = pt.get<std::string>("id");
std::string method = pt.get<std::string>("method");
auto it = m_MethodHandlers.find (method);
if (it != m_MethodHandlers.end ()) {
std::ostringstream ss;
ss << "{\"id\":" << id << ",\"result\":{";
(this->*(it->second))(pt.get_child ("params"), ss);
ss << "},\"jsonrpc\":\"2.0\"}";
response = ss.str();
} else {
LogPrint (eLogWarning, "I2PControl: unknown method ", method);
BuildErrorResponse(response, 32601, "Method not found");
}
} catch (std::exception& ex) {
LogPrint (eLogError, "I2PControl: exception when handle request: ", ex.what ());
BuildErrorResponse(response, 32603, ex.what());
} catch (...) {
LogPrint (eLogError, "I2PControl: handle request unknown exception");
}
std::string id = pt.get<std::string>("id");
std::string method = pt.get<std::string>("method");
auto it = m_MethodHandlers.find (method);
if (it != m_MethodHandlers.end ())
{
response << "{\"id\":" << id << ",\"result\":{";
(this->*(it->second))(pt.get_child ("params"), response);
response << "},\"jsonrpc\":\"2.0\"}";
}
else
{
LogPrint (eLogWarning, "I2PControl: unknown method ", method);
response << "{\"id\":null,\"error\":";
response << "{\"code\":-32601,\"message\":\"Method not found\"},";
response << "\"jsonrpc\":\"2.0\"}";
}
#endif
SendResponse (socket, buf, response, isHTTP);
SendResponse (socket, buf, response, isHtml);
}
catch (std::exception& ex)
{
LogPrint (eLogError, "I2PControl: exception when handle request: ", ex.what ());
std::ostringstream response;
response << "{\"id\":null,\"error\":";
response << "{\"code\":-32700,\"message\":\"" << ex.what () << "\"},";
response << "\"jsonrpc\":\"2.0\"}";
SendResponse (socket, buf, response, isHtml);
}
catch (...)
{
LogPrint (eLogError, "I2PControl: handle request unknown exception");
}
}
}
void I2PControlService::InsertParam (std::ostringstream& ss, const std::string& name, int value) const
@@ -268,28 +289,27 @@ namespace client
ss << "\"" << name << "\":" << std::fixed << std::setprecision(2) << value;
}
void I2PControlService::BuildErrorResponse (std::string & content, int code, const char *message) {
std::stringstream ss;
ss << "{\"id\":null,\"error\":";
ss << "{\"code\":" << -code << ",\"message\":\"" << message << "\"},";
ss << "\"jsonrpc\":\"2.0\"}";
content = ss.str();
}
void I2PControlService::SendResponse (std::shared_ptr<ssl_socket> socket,
std::shared_ptr<I2PControlBuffer> buf, std::string& content, bool isHTTP)
std::shared_ptr<I2PControlBuffer> buf, std::ostringstream& response, bool isHtml)
{
if (isHTTP) {
i2p::http::HTTPRes res;
res.code = 200;
res.add_header("Content-Type", "application/json");
res.add_header("Connection", "close");
res.body = content;
std::string tmp = res.to_string();
content = tmp;
size_t len = response.str ().length (), offset = 0;
if (isHtml)
{
std::ostringstream header;
header << "HTTP/1.1 200 OK\r\n";
header << "Connection: close\r\n";
header << "Content-Length: " << boost::lexical_cast<std::string>(len) << "\r\n";
header << "Content-Type: application/json\r\n";
header << "Date: ";
auto facet = new boost::local_time::local_time_facet ("%a, %d %b %Y %H:%M:%S GMT");
header.imbue(std::locale (header.getloc(), facet));
header << boost::posix_time::second_clock::local_time() << "\r\n";
header << "\r\n";
offset = header.str ().size ();
memcpy (buf->data (), header.str ().c_str (), offset);
}
std::copy(content.begin(), content.end(), buf->begin());
boost::asio::async_write (*socket, boost::asio::buffer (buf->data (), content.length()),
memcpy (buf->data () + offset, response.str ().c_str (), len);
boost::asio::async_write (*socket, boost::asio::buffer (buf->data (), offset + len),
boost::asio::transfer_all (),
std::bind(&I2PControlService::HandleResponseSent, this,
std::placeholders::_1, std::placeholders::_2, socket, buf));
@@ -316,7 +336,7 @@ namespace client
}
InsertParam (results, "API", api);
results << ",";
std::string token = std::to_string(i2p::util::GetSecondsSinceEpoch ());
std::string token = boost::lexical_cast<std::string>(i2p::util::GetSecondsSinceEpoch ());
m_Tokens.insert (token);
InsertParam (results, "Token", token);
}
@@ -358,7 +378,7 @@ namespace client
void I2PControlService::RouterInfoHandler (const boost::property_tree::ptree& params, std::ostringstream& results)
{
for (auto it = params.begin (); it != params.end (); ++it)
for (auto it = params.begin (); it != params.end (); it++)
{
LogPrint (eLogDebug, "I2PControl: RouterInfo request: ", it->first);
auto it1 = m_RouterInfoHandlers.find (it->first);
@@ -384,7 +404,8 @@ namespace client
void I2PControlService::StatusHandler (std::ostringstream& results)
{
InsertParam (results, "i2p.router.status", "???"); // TODO:
auto dest = i2p::client::context.GetSharedLocalDestination ();
InsertParam (results, "i2p.router.status", (dest && dest->IsReady ()) ? "1" : "0");
}
void I2PControlService::NetDbKnownPeersHandler (std::ostringstream& results)
@@ -408,6 +429,12 @@ namespace client
InsertParam (results, "i2p.router.net.tunnels.participating", transit);
}
void I2PControlService::TunnelsSuccessRateHandler (std::ostringstream& results)
{
int rate = i2p::tunnel::tunnels.GetTunnelCreationSuccessRate ();
InsertParam (results, "i2p.router.net.tunnels.successrate", rate);
}
void I2PControlService::InboundBandwidth1S (std::ostringstream& results)
{
double bw = i2p::transport::transports.GetInBandwidth ();
@@ -434,7 +461,7 @@ namespace client
void I2PControlService::RouterManagerHandler (const boost::property_tree::ptree& params, std::ostringstream& results)
{
for (auto it = params.begin (); it != params.end (); ++it)
for (auto it = params.begin (); it != params.end (); it++)
{
if (it != params.begin ()) results << ",";
LogPrint (eLogDebug, "I2PControl: RouterManager request: ", it->first);
@@ -483,7 +510,7 @@ namespace client
// network setting
void I2PControlService::NetworkSettingHandler (const boost::property_tree::ptree& params, std::ostringstream& results)
{
for (auto it = params.begin (); it != params.end (); ++it)
for (auto it = params.begin (); it != params.end (); it++)
{
if (it != params.begin ()) results << ",";
LogPrint (eLogDebug, "I2PControl: NetworkSetting request: ", it->first);
@@ -529,7 +556,7 @@ namespace client
X509_gmtime_adj (X509_get_notAfter (x509), I2P_CONTROL_CERTIFICATE_VALIDITY*24*60*60); // expiration
X509_set_pubkey (x509, pkey); // public key
X509_NAME * name = X509_get_subject_name (x509);
X509_NAME_add_entry_by_txt (name, "C", MBSTRING_ASC, (unsigned char *)"RU", -1, -1, 0); // country (Russia by default)
X509_NAME_add_entry_by_txt (name, "C", MBSTRING_ASC, (unsigned char *)"A1", -1, -1, 0); // country (Anonymous proxy)
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_set_issuer_name (x509, name); // set issuer to ourselves

View File

@@ -45,9 +45,8 @@ namespace client
void ReadRequest (std::shared_ptr<ssl_socket> socket);
void HandleRequestReceived (const boost::system::error_code& ecode, size_t bytes_transferred,
std::shared_ptr<ssl_socket> socket, std::shared_ptr<I2PControlBuffer> buf);
void BuildErrorResponse (std::string & content, int code, const char *message);
void SendResponse (std::shared_ptr<ssl_socket> socket,
std::shared_ptr<I2PControlBuffer> buf, std::string& response, bool isHtml);
std::shared_ptr<I2PControlBuffer> buf, std::ostringstream& response, bool isHtml);
void HandleResponseSent (const boost::system::error_code& ecode, std::size_t bytes_transferred,
std::shared_ptr<ssl_socket> socket, std::shared_ptr<I2PControlBuffer> buf);
@@ -82,6 +81,7 @@ namespace client
void NetDbActivePeersHandler (std::ostringstream& results);
void NetStatusHandler (std::ostringstream& results);
void TunnelsParticipatingHandler (std::ostringstream& results);
void TunnelsSuccessRateHandler (std::ostringstream& results);
void InboundBandwidth1S (std::ostringstream& results);
void OutboundBandwidth1S (std::ostringstream& results);
void NetTotalReceivedBytes (std::ostringstream& results);
@@ -120,4 +120,3 @@ namespace client
}
#endif

View File

@@ -32,7 +32,12 @@ namespace client
}
}
TCPIPPipe::TCPIPPipe(I2PService * owner, std::shared_ptr<boost::asio::ip::tcp::socket> upstream, std::shared_ptr<boost::asio::ip::tcp::socket> downstream) : I2PServiceHandler(owner), m_up(upstream), m_down(downstream) {}
TCPIPPipe::TCPIPPipe(I2PService * owner, std::shared_ptr<boost::asio::ip::tcp::socket> upstream, std::shared_ptr<boost::asio::ip::tcp::socket> downstream) : I2PServiceHandler(owner), m_up(upstream), m_down(downstream)
{
boost::asio::socket_base::receive_buffer_size option(TCP_IP_PIPE_BUFFER_SIZE);
upstream->set_option(option);
downstream->set_option(option);
}
TCPIPPipe::~TCPIPPipe()
{
@@ -48,7 +53,6 @@ namespace client
void TCPIPPipe::Terminate()
{
if(Kill()) return;
Done(shared_from_this());
if (m_up) {
if (m_up->is_open()) {
m_up->close();
@@ -61,6 +65,7 @@ namespace client
}
m_down = nullptr;
}
Done(shared_from_this());
}
void TCPIPPipe::AsyncReceiveUpstream()
@@ -85,11 +90,11 @@ namespace client
}
}
void TCPIPPipe::UpstreamWrite(const uint8_t * buf, size_t len)
void TCPIPPipe::UpstreamWrite(size_t len)
{
if (m_up) {
LogPrint(eLogDebug, "TCPIPPipe: upstream: ", (int) len, " bytes written");
boost::asio::async_write(*m_up, boost::asio::buffer(buf, len),
boost::asio::async_write(*m_up, boost::asio::buffer(m_upstream_buf, len),
boost::asio::transfer_all(),
std::bind(&TCPIPPipe::HandleUpstreamWrite,
shared_from_this(),
@@ -100,11 +105,11 @@ namespace client
}
}
void TCPIPPipe::DownstreamWrite(const uint8_t * buf, size_t len)
void TCPIPPipe::DownstreamWrite(size_t len)
{
if (m_down) {
LogPrint(eLogDebug, "TCPIPPipe: downstream: ", (int) len, " bytes written");
boost::asio::async_write(*m_down, boost::asio::buffer(buf, len),
boost::asio::async_write(*m_down, boost::asio::buffer(m_downstream_buf, len),
boost::asio::transfer_all(),
std::bind(&TCPIPPipe::HandleDownstreamWrite,
shared_from_this(),
@@ -126,9 +131,8 @@ namespace client
} else {
if (bytes_transfered > 0 ) {
memcpy(m_upstream_buf, m_downstream_to_up_buf, bytes_transfered);
UpstreamWrite(m_upstream_buf, bytes_transfered);
}
AsyncReceiveDownstream();
UpstreamWrite(bytes_transfered);
}
}
@@ -137,6 +141,8 @@ namespace client
LogPrint(eLogError, "TCPIPPipe: downstream write error:" , ecode.message());
if (ecode != boost::asio::error::operation_aborted)
Terminate();
} else {
AsyncReceiveUpstream();
}
}
@@ -145,6 +151,8 @@ namespace client
LogPrint(eLogError, "TCPIPPipe: upstream write error:" , ecode.message());
if (ecode != boost::asio::error::operation_aborted)
Terminate();
} else {
AsyncReceiveDownstream();
}
}
@@ -157,10 +165,9 @@ namespace client
Terminate();
} else {
if (bytes_transfered > 0 ) {
memcpy(m_upstream_buf, m_upstream_to_down_buf, bytes_transfered);
DownstreamWrite(m_upstream_buf, bytes_transfered);
memcpy(m_downstream_buf, m_upstream_to_down_buf, bytes_transfered);
}
AsyncReceiveUpstream();
DownstreamWrite(bytes_transfered);
}
}

View File

@@ -38,6 +38,7 @@ namespace client
}
inline std::shared_ptr<ClientDestination> GetLocalDestination () { return m_LocalDestination; }
inline std::shared_ptr<const ClientDestination> GetLocalDestination () const { return m_LocalDestination; }
inline void SetLocalDestination (std::shared_ptr<ClientDestination> dest) { m_LocalDestination = dest; }
void CreateStream (StreamRequestComplete streamRequestComplete, const std::string& dest, int port = 0);
@@ -76,7 +77,7 @@ namespace client
std::atomic<bool> m_Dead; //To avoid cleaning up multiple times
};
const size_t TCP_IP_PIPE_BUFFER_SIZE = 8192;
const size_t TCP_IP_PIPE_BUFFER_SIZE = 8192 * 8;
// bidirectional pipe for 2 tcp/ip sockets
class TCPIPPipe: public I2PServiceHandler, public std::enable_shared_from_this<TCPIPPipe> {
@@ -92,8 +93,8 @@ namespace client
void HandleDownstreamReceived(const boost::system::error_code & ecode, std::size_t bytes_transferred);
void HandleUpstreamWrite(const boost::system::error_code & ecode);
void HandleDownstreamWrite(const boost::system::error_code & ecode);
void UpstreamWrite(const uint8_t * buf, size_t len);
void DownstreamWrite(const uint8_t * buf, size_t len);
void UpstreamWrite(size_t len);
void DownstreamWrite(size_t len);
private:
uint8_t m_upstream_to_down_buf[TCP_IP_PIPE_BUFFER_SIZE], m_downstream_to_up_buf[TCP_IP_PIPE_BUFFER_SIZE];
uint8_t m_upstream_buf[TCP_IP_PIPE_BUFFER_SIZE], m_downstream_buf[TCP_IP_PIPE_BUFFER_SIZE];
@@ -120,10 +121,11 @@ namespace client
void Stop ();
const boost::asio::ip::tcp::acceptor& GetAcceptor () const { return m_Acceptor; };
virtual const char* GetName() { return "Generic TCP/IP accepting daemon"; }
protected:
virtual std::shared_ptr<I2PServiceHandler> CreateHandler(std::shared_ptr<boost::asio::ip::tcp::socket> socket) = 0;
virtual const char* GetName() { return "Generic TCP/IP accepting daemon"; }
private:
void Accept();
void HandleAccept(const boost::system::error_code& ecode, std::shared_ptr<boost::asio::ip::tcp::socket> socket);

View File

@@ -9,6 +9,17 @@ namespace i2p
{
namespace client
{
/** set standard socket options */
static void I2PTunnelSetSocketOptions(std::shared_ptr<boost::asio::ip::tcp::socket> socket)
{
if (socket && socket->is_open())
{
boost::asio::socket_base::receive_buffer_size option(I2P_TUNNEL_CONNECTION_BUFFER_SIZE);
socket->set_option(option);
}
}
I2PTunnelConnection::I2PTunnelConnection (I2PService * owner, std::shared_ptr<boost::asio::ip::tcp::socket> socket,
std::shared_ptr<const i2p::data::LeaseSet> leaseSet, int port):
I2PServiceHandler(owner), m_Socket (socket), m_RemoteEndpoint (socket->remote_endpoint ()),
@@ -34,7 +45,7 @@ namespace client
I2PTunnelConnection::~I2PTunnelConnection ()
{
}
void I2PTunnelConnection::I2PConnect (const uint8_t * msg, size_t len)
{
if (m_Stream)
@@ -47,12 +58,44 @@ namespace client
StreamReceive ();
Receive ();
}
void I2PTunnelConnection::Connect ()
static boost::asio::ip::address GetLoopbackAddressFor(const i2p::data::IdentHash & addr)
{
if (m_Socket)
m_Socket->async_connect (m_RemoteEndpoint, std::bind (&I2PTunnelConnection::HandleConnect,
boost::asio::ip::address_v4::bytes_type bytes;
const uint8_t * ident = addr;
bytes[0] = 127;
memcpy (bytes.data ()+1, ident, 3);
boost::asio::ip::address ourIP = boost::asio::ip::address_v4 (bytes);
return ourIP;
}
static void MapToLoopback(const std::shared_ptr<boost::asio::ip::tcp::socket> & sock, const i2p::data::IdentHash & addr)
{
// bind to 127.x.x.x address
// where x.x.x are first three bytes from ident
auto ourIP = GetLoopbackAddressFor(addr);
sock->bind (boost::asio::ip::tcp::endpoint (ourIP, 0));
}
void I2PTunnelConnection::Connect (bool isUniqueLocal)
{
I2PTunnelSetSocketOptions(m_Socket);
if (m_Socket)
{
#ifdef __linux__
if (isUniqueLocal && m_RemoteEndpoint.address ().is_v4 () &&
m_RemoteEndpoint.address ().to_v4 ().to_bytes ()[0] == 127)
{
m_Socket->open (boost::asio::ip::tcp::v4 ());
auto ident = m_Stream->GetRemoteIdentity()->GetIdentHash();
MapToLoopback(m_Socket, ident);
}
#endif
m_Socket->async_connect (m_RemoteEndpoint, std::bind (&I2PTunnelConnection::HandleConnect,
shared_from_this (), std::placeholders::_1));
}
}
void I2PTunnelConnection::Terminate ()
@@ -63,7 +106,10 @@ namespace client
m_Stream->Close ();
m_Stream.reset ();
}
boost::system::error_code ec;
m_Socket->shutdown(boost::asio::ip::tcp::socket::shutdown_send, ec); // avoid RST
m_Socket->close ();
Done(shared_from_this ());
}
@@ -78,9 +124,11 @@ namespace client
{
if (ecode)
{
LogPrint (eLogError, "I2PTunnel: read error: ", ecode.message ());
if (ecode != boost::asio::error::operation_aborted)
{
LogPrint (eLogError, "I2PTunnel: read error: ", ecode.message ());
Terminate ();
}
}
else
{
@@ -139,14 +187,18 @@ namespace client
{
if (ecode)
{
LogPrint (eLogError, "I2PTunnel: stream read error: ", ecode.message ());
if (ecode != boost::asio::error::operation_aborted)
{
LogPrint (eLogError, "I2PTunnel: stream read error: ", ecode.message ());
if (bytes_transferred > 0)
Write (m_StreamBuffer, bytes_transferred); // postpone termination
else
else if (ecode == boost::asio::error::timed_out && m_Stream && m_Stream->IsOpen ())
StreamReceive ();
else
Terminate ();
}
}
else
Terminate ();
}
else
Write (m_StreamBuffer, bytes_transferred);
@@ -175,21 +227,72 @@ namespace client
// send destination first like received from I2P
std::string dest = m_Stream->GetRemoteIdentity ()->ToBase64 ();
dest += "\n";
memcpy (m_StreamBuffer, dest.c_str (), dest.size ());
if(sizeof(m_StreamBuffer) >= dest.size()) {
memcpy (m_StreamBuffer, dest.c_str (), dest.size ());
}
HandleStreamReceive (boost::system::error_code (), dest.size ());
}
Receive ();
}
}
I2PTunnelConnectionHTTP::I2PTunnelConnectionHTTP (I2PService * owner, std::shared_ptr<i2p::stream::Stream> stream,
void I2PClientTunnelConnectionHTTP::Write (const uint8_t * buf, size_t len)
{
if (m_HeaderSent)
I2PTunnelConnection::Write (buf, len);
else
{
m_InHeader.clear ();
m_InHeader.write ((const char *)buf, len);
std::string line;
bool endOfHeader = false;
while (!endOfHeader)
{
std::getline(m_InHeader, line);
if (!m_InHeader.fail ())
{
if (line == "\r") endOfHeader = true;
else
{
if (!m_ConnectionSent && !line.compare(0, 10, "Connection"))
{
m_OutHeader << "Connection: close\r\n";
m_ConnectionSent = true;
}
else if (!m_ProxyConnectionSent && !line.compare(0, 16, "Proxy-Connection"))
{
m_OutHeader << "Proxy-Connection: close\r\n";
m_ProxyConnectionSent = true;
}
else
m_OutHeader << line << "\n";
}
}
else
break;
}
if (endOfHeader)
{
if (!m_ConnectionSent) m_OutHeader << "Connection: close\r\n";
if (!m_ProxyConnectionSent) m_OutHeader << "Proxy-Connection: close\r\n";
m_OutHeader << "\r\n"; // end of header
m_OutHeader << m_InHeader.str ().substr (m_InHeader.tellg ()); // data right after header
m_InHeader.str ("");
m_HeaderSent = true;
I2PTunnelConnection::Write ((uint8_t *)m_OutHeader.str ().c_str (), m_OutHeader.str ().length ());
}
}
}
I2PServerTunnelConnectionHTTP::I2PServerTunnelConnectionHTTP (I2PService * owner, std::shared_ptr<i2p::stream::Stream> stream,
std::shared_ptr<boost::asio::ip::tcp::socket> socket,
const boost::asio::ip::tcp::endpoint& target, const std::string& host):
I2PTunnelConnection (owner, stream, socket, target), m_Host (host), m_HeaderSent (false), m_From (stream->GetRemoteIdentity ())
{
}
void I2PTunnelConnectionHTTP::Write (const uint8_t * buf, size_t len)
void I2PServerTunnelConnectionHTTP::Write (const uint8_t * buf, size_t len)
{
if (m_HeaderSent)
I2PTunnelConnection::Write (buf, len);
@@ -207,8 +310,8 @@ namespace client
if (line == "\r") endOfHeader = true;
else
{
if (line.find ("Host:") != std::string::npos)
m_OutHeader << "Host: " << m_Host << "\r\n";
if (m_Host.length () > 0 && line.find ("Host:") != std::string::npos)
m_OutHeader << "Host: " << m_Host << "\r\n"; // override host
else
m_OutHeader << line << "\n";
}
@@ -228,6 +331,7 @@ namespace client
{
m_OutHeader << "\r\n"; // end of header
m_OutHeader << m_InHeader.str ().substr (m_InHeader.tellg ()); // data right after header
m_InHeader.str ("");
m_HeaderSent = true;
I2PTunnelConnection::Write ((uint8_t *)m_OutHeader.str ().c_str (), m_OutHeader.str ().length ());
}
@@ -244,26 +348,24 @@ namespace client
void I2PTunnelConnectionIRC::Write (const uint8_t * buf, size_t len)
{
if (m_NeedsWebIrc) {
m_OutPacket.str ("");
if (m_NeedsWebIrc)
{
m_NeedsWebIrc = false;
m_OutPacket.str ("");
m_OutPacket << "WEBIRC " << this->m_WebircPass << " cgiirc " << context.GetAddressBook ().ToAddress (m_From->GetIdentHash ()) << " 127.0.0.1\n";
I2PTunnelConnection::Write ((uint8_t *)m_OutPacket.str ().c_str (), m_OutPacket.str ().length ());
m_OutPacket << "WEBIRC " << m_WebircPass << " cgiirc " << context.GetAddressBook ().ToAddress (m_From->GetIdentHash ()) << " " << GetSocket ()->local_endpoint ().address () << std::endl;
}
std::string line;
m_OutPacket.str ("");
m_InPacket.clear ();
m_InPacket.write ((const char *)buf, len);
while (!m_InPacket.eof () && !m_InPacket.fail ())
{
std::string line;
std::getline (m_InPacket, line);
if (line.length () == 0 && m_InPacket.eof ()) {
if (line.length () == 0 && m_InPacket.eof ())
m_InPacket.str ("");
}
auto pos = line.find ("USER");
if (pos != std::string::npos && pos == 0)
if (!pos) // start of line
{
pos = line.find (" ");
pos++;
@@ -273,9 +375,9 @@ namespace client
m_OutPacket << line.substr (0, pos);
m_OutPacket << context.GetAddressBook ().ToAddress (m_From->GetIdentHash ());
m_OutPacket << line.substr (nextpos) << '\n';
} else {
}
else
m_OutPacket << line << '\n';
}
}
I2PTunnelConnection::Write ((uint8_t *)m_OutPacket.str ().c_str (), m_OutPacket.str ().length ());
}
@@ -380,11 +482,11 @@ namespace client
I2PServerTunnel::I2PServerTunnel (const std::string& name, const std::string& address,
int port, std::shared_ptr<ClientDestination> localDestination, int inport, bool gzip):
I2PService (localDestination), m_Name (name), m_Address (address), m_Port (port), m_IsAccessList (false)
I2PService (localDestination), m_IsUniqueLocal(true), m_Name (name), m_Address (address), m_Port (port), m_IsAccessList (false)
{
m_PortDestination = localDestination->CreateStreamingDestination (inport > 0 ? inport : port, gzip);
}
void I2PServerTunnel::Start ()
{
m_Endpoint.port (m_Port);
@@ -457,31 +559,31 @@ namespace client
return;
}
}
CreateI2PConnection (stream);
// new connection
auto conn = CreateI2PConnection (stream);
AddHandler (conn);
conn->Connect (m_IsUniqueLocal);
}
}
void I2PServerTunnel::CreateI2PConnection (std::shared_ptr<i2p::stream::Stream> stream)
std::shared_ptr<I2PTunnelConnection> I2PServerTunnel::CreateI2PConnection (std::shared_ptr<i2p::stream::Stream> stream)
{
auto conn = std::make_shared<I2PTunnelConnection> (this, stream, std::make_shared<boost::asio::ip::tcp::socket> (GetService ()), GetEndpoint ());
AddHandler (conn);
conn->Connect ();
return std::make_shared<I2PTunnelConnection> (this, stream, std::make_shared<boost::asio::ip::tcp::socket> (GetService ()), GetEndpoint ());
}
I2PServerTunnelHTTP::I2PServerTunnelHTTP (const std::string& name, const std::string& address,
int port, std::shared_ptr<ClientDestination> localDestination,
const std::string& host, int inport, bool gzip):
I2PServerTunnel (name, address, port, localDestination, inport, gzip),
m_Host (host.length () > 0 ? host : address)
m_Host (host)
{
}
void I2PServerTunnelHTTP::CreateI2PConnection (std::shared_ptr<i2p::stream::Stream> stream)
std::shared_ptr<I2PTunnelConnection> I2PServerTunnelHTTP::CreateI2PConnection (std::shared_ptr<i2p::stream::Stream> stream)
{
auto conn = std::make_shared<I2PTunnelConnectionHTTP> (this, stream,
return std::make_shared<I2PServerTunnelConnectionHTTP> (this, stream,
std::make_shared<boost::asio::ip::tcp::socket> (GetService ()), GetEndpoint (), m_Host);
AddHandler (conn);
conn->Connect ();
}
I2PServerTunnelIRC::I2PServerTunnelIRC (const std::string& name, const std::string& address,
@@ -492,12 +594,283 @@ namespace client
{
}
void I2PServerTunnelIRC::CreateI2PConnection (std::shared_ptr<i2p::stream::Stream> stream)
std::shared_ptr<I2PTunnelConnection> I2PServerTunnelIRC::CreateI2PConnection (std::shared_ptr<i2p::stream::Stream> stream)
{
auto conn = std::make_shared<I2PTunnelConnectionIRC> (this, stream, std::make_shared<boost::asio::ip::tcp::socket> (GetService ()), GetEndpoint (), this->m_WebircPass);
AddHandler (conn);
conn->Connect ();
return std::make_shared<I2PTunnelConnectionIRC> (this, stream, std::make_shared<boost::asio::ip::tcp::socket> (GetService ()), GetEndpoint (), this->m_WebircPass);
}
}
}
void I2PUDPServerTunnel::HandleRecvFromI2P(const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len)
{
std::lock_guard<std::mutex> lock(m_SessionsMutex);
auto session = ObtainUDPSession(from, toPort, fromPort);
session->IPSocket.send_to(boost::asio::buffer(buf, len), m_RemoteEndpoint);
session->LastActivity = i2p::util::GetMillisecondsSinceEpoch();
}
void I2PUDPServerTunnel::ExpireStale(const uint64_t delta) {
std::lock_guard<std::mutex> lock(m_SessionsMutex);
uint64_t now = i2p::util::GetMillisecondsSinceEpoch();
auto itr = m_Sessions.begin();
while(itr != m_Sessions.end()) {
if(now - (*itr)->LastActivity >= delta )
itr = m_Sessions.erase(itr);
else
++itr;
}
}
void I2PUDPClientTunnel::ExpireStale(const uint64_t delta) {
std::lock_guard<std::mutex> lock(m_SessionsMutex);
uint64_t now = i2p::util::GetMillisecondsSinceEpoch();
std::vector<uint16_t> removePorts;
for (const auto & s : m_Sessions) {
if (now - s.second.second >= delta)
removePorts.push_back(s.first);
}
for(auto port : removePorts) {
m_Sessions.erase(port);
}
}
UDPSessionPtr I2PUDPServerTunnel::ObtainUDPSession(const i2p::data::IdentityEx& from, uint16_t localPort, uint16_t remotePort)
{
auto ih = from.GetIdentHash();
for (auto & s : m_Sessions )
{
if ( s->Identity == ih)
{
/** found existing session */
LogPrint(eLogDebug, "UDPServer: found session ", s->IPSocket.local_endpoint(), " ", ih.ToBase32());
return s;
}
}
boost::asio::ip::address addr;
/** create new udp session */
if(m_IsUniqueLocal && m_LocalAddress.is_loopback())
{
auto ident = from.GetIdentHash();
addr = GetLoopbackAddressFor(ident);
}
else
addr = m_LocalAddress;
boost::asio::ip::udp::endpoint ep(addr, 0);
m_Sessions.push_back(std::make_shared<UDPSession>(ep, m_LocalDest, m_RemoteEndpoint, &ih, localPort, remotePort));
auto & back = m_Sessions.back();
return back;
}
UDPSession::UDPSession(boost::asio::ip::udp::endpoint localEndpoint,
const std::shared_ptr<i2p::client::ClientDestination> & localDestination,
boost::asio::ip::udp::endpoint endpoint, const i2p::data::IdentHash * to,
uint16_t ourPort, uint16_t theirPort) :
m_Destination(localDestination->GetDatagramDestination()),
IPSocket(localDestination->GetService(), localEndpoint),
SendEndpoint(endpoint),
LastActivity(i2p::util::GetMillisecondsSinceEpoch()),
LocalPort(ourPort),
RemotePort(theirPort)
{
memcpy(Identity, to->data(), 32);
Receive();
}
void UDPSession::Receive() {
LogPrint(eLogDebug, "UDPSession: Receive");
IPSocket.async_receive_from(boost::asio::buffer(m_Buffer, I2P_UDP_MAX_MTU),
FromEndpoint, std::bind(&UDPSession::HandleReceived, this, std::placeholders::_1, std::placeholders::_2));
}
void UDPSession::HandleReceived(const boost::system::error_code & ecode, std::size_t len)
{
if(!ecode)
{
LogPrint(eLogDebug, "UDPSession: forward ", len, "B from ", FromEndpoint);
LastActivity = i2p::util::GetMillisecondsSinceEpoch();
m_Destination->SendDatagramTo(m_Buffer, len, Identity, LocalPort, RemotePort);
Receive();
} else {
LogPrint(eLogError, "UDPSession: ", ecode.message());
}
}
I2PUDPServerTunnel::I2PUDPServerTunnel(const std::string & name, std::shared_ptr<i2p::client::ClientDestination> localDestination,
boost::asio::ip::address localAddress, boost::asio::ip::udp::endpoint forwardTo, uint16_t port) :
m_IsUniqueLocal(true),
m_Name(name),
m_LocalAddress(localAddress),
m_RemoteEndpoint(forwardTo)
{
m_LocalDest = localDestination;
m_LocalDest->Start();
auto dgram = m_LocalDest->CreateDatagramDestination();
dgram->SetReceiver(std::bind(&I2PUDPServerTunnel::HandleRecvFromI2P, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5));
}
I2PUDPServerTunnel::~I2PUDPServerTunnel()
{
auto dgram = m_LocalDest->GetDatagramDestination();
if (dgram) dgram->ResetReceiver();
LogPrint(eLogInfo, "UDPServer: done");
}
void I2PUDPServerTunnel::Start() {
m_LocalDest->Start();
}
std::vector<std::shared_ptr<DatagramSessionInfo> > I2PUDPServerTunnel::GetSessions()
{
std::vector<std::shared_ptr<DatagramSessionInfo> > sessions;
std::lock_guard<std::mutex> lock(m_SessionsMutex);
for ( UDPSessionPtr s : m_Sessions )
{
if (!s->m_Destination) continue;
auto info = s->m_Destination->GetInfoForRemote(s->Identity);
if(!info) continue;
auto sinfo = std::make_shared<DatagramSessionInfo>();
sinfo->Name = m_Name;
sinfo->LocalIdent = std::make_shared<i2p::data::IdentHash>(m_LocalDest->GetIdentHash().data());
sinfo->RemoteIdent = std::make_shared<i2p::data::IdentHash>(s->Identity.data());
sinfo->CurrentIBGW = info->IBGW;
sinfo->CurrentOBEP = info->OBEP;
sessions.push_back(sinfo);
}
return sessions;
}
I2PUDPClientTunnel::I2PUDPClientTunnel(const std::string & name, const std::string &remoteDest,
boost::asio::ip::udp::endpoint localEndpoint,
std::shared_ptr<i2p::client::ClientDestination> localDestination,
uint16_t remotePort) :
m_Name(name),
m_RemoteDest(remoteDest),
m_LocalDest(localDestination),
m_LocalEndpoint(localEndpoint),
m_RemoteIdent(nullptr),
m_ResolveThread(nullptr),
m_LocalSocket(localDestination->GetService(), localEndpoint),
RemotePort(remotePort),
m_cancel_resolve(false)
{
auto dgram = m_LocalDest->CreateDatagramDestination();
dgram->SetReceiver(std::bind(&I2PUDPClientTunnel::HandleRecvFromI2P, this,
std::placeholders::_1, std::placeholders::_2,
std::placeholders::_3, std::placeholders::_4,
std::placeholders::_5));
}
void I2PUDPClientTunnel::Start() {
m_LocalDest->Start();
if (m_ResolveThread == nullptr)
m_ResolveThread = new std::thread(std::bind(&I2PUDPClientTunnel::TryResolving, this));
RecvFromLocal();
}
void I2PUDPClientTunnel::RecvFromLocal()
{
m_LocalSocket.async_receive_from(boost::asio::buffer(m_RecvBuff, I2P_UDP_MAX_MTU),
m_RecvEndpoint, std::bind(&I2PUDPClientTunnel::HandleRecvFromLocal, this, std::placeholders::_1, std::placeholders::_2));
}
void I2PUDPClientTunnel::HandleRecvFromLocal(const boost::system::error_code & ec, std::size_t transferred)
{
if(ec) {
LogPrint(eLogError, "UDP Client: ", ec.message());
return;
}
if(!m_RemoteIdent) {
LogPrint(eLogWarning, "UDP Client: remote endpoint not resolved yet");
RecvFromLocal();
return; // drop, remote not resolved
}
auto remotePort = m_RecvEndpoint.port();
auto itr = m_Sessions.find(remotePort);
if (itr == m_Sessions.end()) {
// track new udp convo
m_Sessions[remotePort] = {boost::asio::ip::udp::endpoint(m_RecvEndpoint), 0};
}
// send off to remote i2p destination
LogPrint(eLogDebug, "UDP Client: send ", transferred, " to ", m_RemoteIdent->ToBase32(), ":", RemotePort);
m_LocalDest->GetDatagramDestination()->SendDatagramTo(m_RecvBuff, transferred, *m_RemoteIdent, remotePort, RemotePort);
// mark convo as active
m_Sessions[remotePort].second = i2p::util::GetMillisecondsSinceEpoch();
RecvFromLocal();
}
std::vector<std::shared_ptr<DatagramSessionInfo> > I2PUDPClientTunnel::GetSessions()
{
// TODO: implement
std::vector<std::shared_ptr<DatagramSessionInfo> > infos;
return infos;
}
void I2PUDPClientTunnel::TryResolving() {
LogPrint(eLogInfo, "UDP Tunnel: Trying to resolve ", m_RemoteDest);
i2p::data::IdentHash * h = new i2p::data::IdentHash;
while(!context.GetAddressBook().GetIdentHash(m_RemoteDest, *h) && !m_cancel_resolve)
{
LogPrint(eLogWarning, "UDP Tunnel: failed to lookup ", m_RemoteDest);
std::this_thread::sleep_for(std::chrono::seconds(1));
}
if(m_cancel_resolve)
{
LogPrint(eLogError, "UDP Tunnel: lookup of ", m_RemoteDest, " was cancelled");
return;
}
m_RemoteIdent = h;
LogPrint(eLogInfo, "UDP Tunnel: resolved ", m_RemoteDest, " to ", m_RemoteIdent->ToBase32());
}
void I2PUDPClientTunnel::HandleRecvFromI2P(const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len)
{
if(m_RemoteIdent && from.GetIdentHash() == *m_RemoteIdent)
{
auto itr = m_Sessions.find(toPort);
// found convo ?
if(itr != m_Sessions.end())
{
// found convo
if (len > 0) {
LogPrint(eLogDebug, "UDP Client: got ", len, "B from ", from.GetIdentHash().ToBase32());
m_LocalSocket.send_to(boost::asio::buffer(buf, len), itr->second.first);
// mark convo as active
itr->second.second = i2p::util::GetMillisecondsSinceEpoch();
}
}
else
LogPrint(eLogWarning, "UDP Client: not tracking udp session using port ", (int) toPort);
}
else
LogPrint(eLogWarning, "UDP Client: unwarrented traffic from ", from.GetIdentHash().ToBase32());
}
I2PUDPClientTunnel::~I2PUDPClientTunnel() {
auto dgram = m_LocalDest->GetDatagramDestination();
if (dgram) dgram->ResetReceiver();
m_Sessions.clear();
if(m_LocalSocket.is_open())
m_LocalSocket.close();
m_cancel_resolve = true;
if(m_ResolveThread)
{
m_ResolveThread->join();
delete m_ResolveThread;
m_ResolveThread = nullptr;
}
if (m_RemoteIdent) delete m_RemoteIdent;
}
}
}

View File

@@ -4,11 +4,13 @@
#include <inttypes.h>
#include <string>
#include <set>
#include <tuple>
#include <memory>
#include <sstream>
#include <boost/asio.hpp>
#include "Identity.h"
#include "Destination.h"
#include "Datagram.h"
#include "Streaming.h"
#include "I2PService.h"
@@ -16,7 +18,7 @@ namespace i2p
{
namespace client
{
const size_t I2P_TUNNEL_CONNECTION_BUFFER_SIZE = 8192;
const size_t I2P_TUNNEL_CONNECTION_BUFFER_SIZE = 65536;
const int I2P_TUNNEL_CONNECTION_MAX_IDLE = 3600; // in seconds
const int I2P_TUNNEL_DESTINATION_REQUEST_TIMEOUT = 10; // in seconds
// for HTTP tunnels
@@ -36,7 +38,7 @@ namespace client
const boost::asio::ip::tcp::endpoint& target, bool quiet = true); // from I2P
~I2PTunnelConnection ();
void I2PConnect (const uint8_t * msg = nullptr, size_t len = 0);
void Connect ();
void Connect (bool isUniqueLocal = true);
protected:
@@ -51,6 +53,8 @@ namespace client
void HandleStreamReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred);
void HandleConnect (const boost::system::error_code& ecode);
std::shared_ptr<const boost::asio::ip::tcp::socket> GetSocket () const { return m_Socket; };
private:
uint8_t m_Buffer[I2P_TUNNEL_CONNECTION_BUFFER_SIZE], m_StreamBuffer[I2P_TUNNEL_CONNECTION_BUFFER_SIZE];
@@ -60,11 +64,30 @@ namespace client
bool m_IsQuiet; // don't send destination
};
class I2PTunnelConnectionHTTP: public I2PTunnelConnection
class I2PClientTunnelConnectionHTTP: public I2PTunnelConnection
{
public:
I2PClientTunnelConnectionHTTP (I2PService * owner, std::shared_ptr<boost::asio::ip::tcp::socket> socket,
std::shared_ptr<i2p::stream::Stream> stream):
I2PTunnelConnection (owner, socket, stream), m_HeaderSent (false),
m_ConnectionSent (false), m_ProxyConnectionSent (false) {};
protected:
void Write (const uint8_t * buf, size_t len);
private:
std::stringstream m_InHeader, m_OutHeader;
bool m_HeaderSent, m_ConnectionSent, m_ProxyConnectionSent;
};
class I2PServerTunnelConnectionHTTP: public I2PTunnelConnection
{
public:
I2PTunnelConnectionHTTP (I2PService * owner, std::shared_ptr<i2p::stream::Stream> stream,
I2PServerTunnelConnectionHTTP (I2PService * owner, std::shared_ptr<i2p::stream::Stream> stream,
std::shared_ptr<boost::asio::ip::tcp::socket> socket,
const boost::asio::ip::tcp::endpoint& target, const std::string& host);
@@ -128,8 +151,131 @@ namespace client
std::string m_Name, m_Destination;
const i2p::data::IdentHash * m_DestinationIdentHash;
int m_DestinationPort;
};
};
/** 2 minute timeout for udp sessions */
const uint64_t I2P_UDP_SESSION_TIMEOUT = 1000 * 60 * 2;
/** max size for i2p udp */
const size_t I2P_UDP_MAX_MTU = i2p::datagram::MAX_DATAGRAM_SIZE;
struct UDPSession
{
i2p::datagram::DatagramDestination * m_Destination;
boost::asio::ip::udp::socket IPSocket;
i2p::data::IdentHash Identity;
boost::asio::ip::udp::endpoint FromEndpoint;
boost::asio::ip::udp::endpoint SendEndpoint;
uint64_t LastActivity;
uint16_t LocalPort;
uint16_t RemotePort;
uint8_t m_Buffer[I2P_UDP_MAX_MTU];
UDPSession(boost::asio::ip::udp::endpoint localEndpoint,
const std::shared_ptr<i2p::client::ClientDestination> & localDestination,
boost::asio::ip::udp::endpoint remote, const i2p::data::IdentHash * ident,
uint16_t ourPort, uint16_t theirPort);
void HandleReceived(const boost::system::error_code & ecode, std::size_t len);
void Receive();
};
/** read only info about a datagram session */
struct DatagramSessionInfo
{
/** the name of this forward */
std::string Name;
/** ident hash of local destination */
std::shared_ptr<const i2p::data::IdentHash> LocalIdent;
/** ident hash of remote destination */
std::shared_ptr<const i2p::data::IdentHash> RemoteIdent;
/** ident hash of IBGW in use currently in this session or nullptr if none is set */
std::shared_ptr<const i2p::data::IdentHash> CurrentIBGW;
/** ident hash of OBEP in use for this session or nullptr if none is set */
std::shared_ptr<const i2p::data::IdentHash> CurrentOBEP;
/** i2p router's udp endpoint */
boost::asio::ip::udp::endpoint LocalEndpoint;
/** client's udp endpoint */
boost::asio::ip::udp::endpoint RemoteEndpoint;
/** how long has this converstation been idle in ms */
uint64_t idle;
};
typedef std::shared_ptr<UDPSession> UDPSessionPtr;
/** server side udp tunnel, many i2p inbound to 1 ip outbound */
class I2PUDPServerTunnel
{
public:
I2PUDPServerTunnel(const std::string & name,
std::shared_ptr<i2p::client::ClientDestination> localDestination,
boost::asio::ip::address localAddress,
boost::asio::ip::udp::endpoint forwardTo, uint16_t port);
~I2PUDPServerTunnel();
/** expire stale udp conversations */
void ExpireStale(const uint64_t delta=I2P_UDP_SESSION_TIMEOUT);
void Start();
const char * GetName() const { return m_Name.c_str(); }
std::vector<std::shared_ptr<DatagramSessionInfo> > GetSessions();
std::shared_ptr<ClientDestination> GetLocalDestination () const { return m_LocalDest; }
void SetUniqueLocal(bool isUniqueLocal = true) { m_IsUniqueLocal = isUniqueLocal; }
private:
void HandleRecvFromI2P(const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len);
UDPSessionPtr ObtainUDPSession(const i2p::data::IdentityEx& from, uint16_t localPort, uint16_t remotePort);
private:
bool m_IsUniqueLocal;
const std::string m_Name;
boost::asio::ip::address m_LocalAddress;
boost::asio::ip::udp::endpoint m_RemoteEndpoint;
std::mutex m_SessionsMutex;
std::vector<UDPSessionPtr> m_Sessions;
std::shared_ptr<i2p::client::ClientDestination> m_LocalDest;
};
class I2PUDPClientTunnel
{
public:
I2PUDPClientTunnel(const std::string & name, const std::string &remoteDest,
boost::asio::ip::udp::endpoint localEndpoint, std::shared_ptr<i2p::client::ClientDestination> localDestination,
uint16_t remotePort);
~I2PUDPClientTunnel();
void Start();
const char * GetName() const { return m_Name.c_str(); }
std::vector<std::shared_ptr<DatagramSessionInfo> > GetSessions();
bool IsLocalDestination(const i2p::data::IdentHash & destination) const { return destination == m_LocalDest->GetIdentHash(); }
std::shared_ptr<ClientDestination> GetLocalDestination () const { return m_LocalDest; }
void ExpireStale(const uint64_t delta=I2P_UDP_SESSION_TIMEOUT);
private:
typedef std::pair<boost::asio::ip::udp::endpoint, uint64_t> UDPConvo;
void RecvFromLocal();
void HandleRecvFromLocal(const boost::system::error_code & e, std::size_t transferred);
void HandleRecvFromI2P(const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len);
void TryResolving();
const std::string m_Name;
std::mutex m_SessionsMutex;
std::map<uint16_t, UDPConvo > m_Sessions; // maps i2p port -> local udp convo
const std::string m_RemoteDest;
std::shared_ptr<i2p::client::ClientDestination> m_LocalDest;
const boost::asio::ip::udp::endpoint m_LocalEndpoint;
i2p::data::IdentHash * m_RemoteIdent;
std::thread * m_ResolveThread;
boost::asio::ip::udp::socket m_LocalSocket;
boost::asio::ip::udp::endpoint m_RecvEndpoint;
uint8_t m_RecvBuff[I2P_UDP_MAX_MTU];
uint16_t RemotePort;
bool m_cancel_resolve;
};
class I2PServerTunnel: public I2PService
{
public:
@@ -142,30 +288,36 @@ namespace client
void SetAccessList (const std::set<i2p::data::IdentHash>& accessList);
void SetUniqueLocal (bool isUniqueLocal) { m_IsUniqueLocal = isUniqueLocal; }
bool IsUniqueLocal () const { return m_IsUniqueLocal; }
const std::string& GetAddress() const { return m_Address; }
int GetPort () const { return m_Port; };
uint16_t GetLocalPort () const { return m_PortDestination->GetLocalPort (); };
const boost::asio::ip::tcp::endpoint& GetEndpoint () const { return m_Endpoint; }
const char* GetName() { return m_Name.c_str (); }
private:
void SetMaxConnsPerMinute(const uint32_t conns) { m_PortDestination->SetMaxConnsPerMinute(conns); }
private:
void HandleResolve (const boost::system::error_code& ecode, boost::asio::ip::tcp::resolver::iterator it,
std::shared_ptr<boost::asio::ip::tcp::resolver> resolver);
void Accept ();
void HandleAccept (std::shared_ptr<i2p::stream::Stream> stream);
virtual void CreateI2PConnection (std::shared_ptr<i2p::stream::Stream> stream);
virtual std::shared_ptr<I2PTunnelConnection> CreateI2PConnection (std::shared_ptr<i2p::stream::Stream> stream);
private:
bool m_IsUniqueLocal;
std::string m_Name, m_Address;
int m_Port;
boost::asio::ip::tcp::endpoint m_Endpoint;
std::shared_ptr<i2p::stream::StreamingDestination> m_PortDestination;
std::set<i2p::data::IdentHash> m_AccessList;
bool m_IsAccessList;
bool m_IsAccessList;
};
class I2PServerTunnelHTTP: public I2PServerTunnel
@@ -178,7 +330,7 @@ namespace client
private:
void CreateI2PConnection (std::shared_ptr<i2p::stream::Stream> stream);
std::shared_ptr<I2PTunnelConnection> CreateI2PConnection (std::shared_ptr<i2p::stream::Stream> stream);
private:
@@ -195,7 +347,7 @@ namespace client
private:
void CreateI2PConnection (std::shared_ptr<i2p::stream::Stream> stream);
std::shared_ptr<I2PTunnelConnection> CreateI2PConnection (std::shared_ptr<i2p::stream::Stream> stream);
private:

View File

@@ -35,11 +35,12 @@ namespace data
}
IdentityEx::IdentityEx ():
m_ExtendedLen (0), m_ExtendedBuffer (nullptr)
m_IsVerifierCreated (false), m_ExtendedLen (0), m_ExtendedBuffer (nullptr)
{
}
IdentityEx::IdentityEx(const uint8_t * publicKey, const uint8_t * signingKey, SigningKeyType type)
IdentityEx::IdentityEx(const uint8_t * publicKey, const uint8_t * signingKey, SigningKeyType type):
m_IsVerifierCreated (false)
{
memcpy (m_StandardIdentity.publicKey, publicKey, sizeof (m_StandardIdentity.publicKey));
if (type != SIGNING_KEY_TYPE_DSA_SHA1)
@@ -135,19 +136,19 @@ namespace data
}
IdentityEx::IdentityEx (const uint8_t * buf, size_t len):
m_ExtendedLen (0), m_ExtendedBuffer (nullptr)
m_IsVerifierCreated (false), m_ExtendedLen (0), m_ExtendedBuffer (nullptr)
{
FromBuffer (buf, len);
}
IdentityEx::IdentityEx (const IdentityEx& other):
m_ExtendedLen (0), m_ExtendedBuffer (nullptr)
m_IsVerifierCreated (false), m_ExtendedLen (0), m_ExtendedBuffer (nullptr)
{
*this = other;
}
IdentityEx::IdentityEx (const Identity& standard):
m_ExtendedLen (0), m_ExtendedBuffer (nullptr)
m_IsVerifierCreated (false), m_ExtendedLen (0), m_ExtendedBuffer (nullptr)
{
*this = standard;
}
@@ -173,6 +174,7 @@ namespace data
m_ExtendedBuffer = nullptr;
m_Verifier = nullptr;
m_IsVerifierCreated = false;
return *this;
}
@@ -187,6 +189,7 @@ namespace data
m_ExtendedLen = 0;
m_Verifier = nullptr;
m_IsVerifierCreated = false;
return *this;
}
@@ -200,7 +203,9 @@ namespace data
}
memcpy (&m_StandardIdentity, buf, DEFAULT_IDENTITY_SIZE);
delete[] m_ExtendedBuffer; m_ExtendedBuffer = nullptr;
if(m_ExtendedBuffer) delete[] m_ExtendedBuffer;
m_ExtendedBuffer = nullptr;
m_ExtendedLen = bufbe16toh (m_StandardIdentity.certificate + 1);
if (m_ExtendedLen)
{
@@ -304,6 +309,7 @@ namespace data
void IdentityEx::CreateVerifier () const
{
if (m_Verifier) return; // don't create again
auto keyType = GetSigningKeyType ();
switch (keyType)
{
@@ -371,8 +377,24 @@ namespace data
void IdentityEx::UpdateVerifier (i2p::crypto::Verifier * verifier) const
{
if (!m_Verifier || !verifier)
m_Verifier.reset (verifier);
if (!m_Verifier)
{
auto created = m_IsVerifierCreated.exchange (true);
if (!created)
m_Verifier.reset (verifier);
else
{
delete verifier;
int count = 0;
while (!m_Verifier && count < 500) // 5 seconds
{
std::this_thread::sleep_for (std::chrono::milliseconds(10));
count++;
}
if (!m_Verifier)
LogPrint (eLogError, "Identity: couldn't get verifier in 5 seconds");
}
}
else
delete verifier;
}
@@ -380,7 +402,8 @@ namespace data
void IdentityEx::DropVerifier () const
{
// TODO: potential race condition with Verify
m_Verifier = nullptr;
m_IsVerifierCreated = false;
m_Verifier = nullptr;
}
PrivateKeys& PrivateKeys::operator=(const Keys& keys)
@@ -410,6 +433,7 @@ namespace data
memcpy (m_PrivateKey, buf + ret, 256); // private key always 256
ret += 256;
size_t signingPrivateKeySize = m_Public->GetSigningPrivateKeyLen ();
if(signingPrivateKeySize + ret > len) return 0; // overflow
memcpy (m_SigningPrivateKey, buf + ret, signingPrivateKeySize);
ret += signingPrivateKeySize;
m_Signer = nullptr;
@@ -422,7 +446,8 @@ namespace data
size_t ret = m_Public->ToBuffer (buf, len);
memcpy (buf + ret, m_PrivateKey, 256); // private key always 256
ret += 256;
size_t signingPrivateKeySize = m_Public->GetSigningPrivateKeyLen ();
size_t signingPrivateKeySize = m_Public->GetSigningPrivateKeyLen ();
if(ret + signingPrivateKeySize > len) return 0; // overflow
memcpy (buf + ret, m_SigningPrivateKey, signingPrivateKeySize);
ret += signingPrivateKeySize;
return ret;
@@ -452,16 +477,18 @@ namespace data
void PrivateKeys::Sign (const uint8_t * buf, int len, uint8_t * signature) const
{
if (m_Signer)
m_Signer->Sign (buf, len, signature);
if (!m_Signer)
CreateSigner();
m_Signer->Sign (buf, len, signature);
}
void PrivateKeys::CreateSigner ()
void PrivateKeys::CreateSigner () const
{
if (m_Signer) return;
switch (m_Public->GetSigningKeyType ())
{
case SIGNING_KEY_TYPE_DSA_SHA1:
m_Signer.reset (new i2p::crypto::DSASigner (m_SigningPrivateKey));
m_Signer.reset (new i2p::crypto::DSASigner (m_SigningPrivateKey, m_Public->GetStandardIdentity ().signingKey));
break;
case SIGNING_KEY_TYPE_ECDSA_SHA256_P256:
m_Signer.reset (new i2p::crypto::ECDSAP256Signer (m_SigningPrivateKey));
@@ -482,7 +509,7 @@ namespace data
m_Signer.reset (new i2p::crypto::RSASHA5124096Signer (m_SigningPrivateKey));
break;
case SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519:
m_Signer.reset (new i2p::crypto::EDDSA25519Signer (m_SigningPrivateKey));
m_Signer.reset (new i2p::crypto::EDDSA25519Signer (m_SigningPrivateKey, m_Public->GetStandardIdentity ().certificate - i2p::crypto::EDDSA25519_PUBLIC_KEY_LENGTH));
break;
default:
LogPrint (eLogError, "Identity: Signing key type ", (int)m_Public->GetSigningKeyType (), " is not supported");
@@ -566,11 +593,25 @@ namespace data
XORMetric operator^(const IdentHash& key1, const IdentHash& key2)
{
XORMetric m;
#if defined(__AVX__) // for AVX
__asm__
(
"vmovups %1, %%ymm0 \n"
"vmovups %2, %%ymm1 \n"
"vxorps %%ymm0, %%ymm1, %%ymm1 \n"
"vmovups %%ymm1, %0 \n"
: "=m"(*m.metric)
: "m"(*key1), "m"(*key2)
: "memory", "%xmm0", "%xmm1" // should be replaced by %ymm0/1 once supported by compiler
);
#else
const uint64_t * hash1 = key1.GetLL (), * hash2 = key2.GetLL ();
m.metric_ll[0] = hash1[0] ^ hash2[0];
m.metric_ll[1] = hash1[1] ^ hash2[1];
m.metric_ll[2] = hash1[2] ^ hash2[2];
m.metric_ll[3] = hash1[3] ^ hash2[3];
#endif
return m;
}
}

View File

@@ -5,6 +5,7 @@
#include <string.h>
#include <string>
#include <memory>
#include <atomic>
#include "Base.h"
#include "Signature.h"
@@ -92,6 +93,8 @@ namespace data
CryptoKeyType GetCryptoKeyType () const;
void DropVerifier () const; // to save memory
bool operator == (const IdentityEx & other) const { return GetIdentHash() == other.GetIdentHash(); }
private:
void CreateVerifier () const;
@@ -102,6 +105,7 @@ namespace data
Identity m_StandardIdentity;
IdentHash m_IdentHash;
mutable std::unique_ptr<i2p::crypto::Verifier> m_Verifier;
mutable std::atomic_bool m_IsVerifierCreated; // make sure we don't create twice
size_t m_ExtendedLen;
uint8_t * m_ExtendedBuffer;
};
@@ -133,14 +137,14 @@ namespace data
private:
void CreateSigner ();
void CreateSigner () const;
private:
std::shared_ptr<IdentityEx> m_Public;
uint8_t m_PrivateKey[256];
uint8_t m_SigningPrivateKey[1024]; // assume private key doesn't exceed 1024 bytes
std::unique_ptr<i2p::crypto::Signer> m_Signer;
mutable std::unique_ptr<i2p::crypto::Signer> m_Signer;
};
// kademlia

View File

@@ -91,7 +91,7 @@ namespace data
if (m_StoreLeases)
{
auto ret = m_Leases.insert (std::make_shared<Lease>(lease));
if (!ret.second) *(*ret.first) = lease; // update existing
if (!ret.second) (*ret.first)->endDate = lease.endDate; // update existing
(*ret.first)->isUpdated = true;
// check if lease's gateway is in our netDb
if (!netdb.FindRouter (lease.tunnelGateway))
@@ -162,8 +162,21 @@ namespace data
{
return ExtractTimestamp (buf, len) > ExtractTimestamp (m_Buffer, m_BufferLen);
}
const std::vector<std::shared_ptr<const Lease> > LeaseSet::GetNonExpiredLeases (bool withThreshold) const
bool LeaseSet::ExpiresSoon(const uint64_t dlt, const uint64_t fudge) const
{
auto now = i2p::util::GetMillisecondsSinceEpoch ();
if (fudge) now += rand() % fudge;
if (now >= m_ExpirationTime) return true;
return m_ExpirationTime - now <= dlt;
}
const std::vector<std::shared_ptr<const Lease> > LeaseSet::GetNonExpiredLeases (bool withThreshold) const
{
return GetNonExpiredLeasesExcluding( [] (const Lease & l) -> bool { return false; }, withThreshold);
}
const std::vector<std::shared_ptr<const Lease> > LeaseSet::GetNonExpiredLeasesExcluding (LeaseInspectFunc exclude, bool withThreshold) const
{
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
std::vector<std::shared_ptr<const Lease> > leases;
@@ -174,7 +187,7 @@ namespace data
endDate += LEASE_ENDDATE_THRESHOLD;
else
endDate -= LEASE_ENDDATE_THRESHOLD;
if (ts < endDate)
if (ts < endDate && !exclude(*it))
leases.push_back (it);
}
return leases;

View File

@@ -7,6 +7,7 @@
#include <set>
#include <memory>
#include "Identity.h"
#include "Timestamp.h"
namespace i2p
{
@@ -24,7 +25,13 @@ namespace data
IdentHash tunnelGateway;
uint32_t tunnelID;
uint64_t endDate; // 0 means invalid
bool isUpdated; // trasient
bool isUpdated; // trasient
/* return true if this lease expires within t millisecond + fudge factor */
bool ExpiresWithin( const uint64_t t, const uint64_t fudge = 1000 ) const {
auto expire = i2p::util::GetMillisecondsSinceEpoch ();
if(fudge) expire += rand() % fudge;
return endDate - expire >= t;
}
};
struct LeaseCmp
@@ -38,6 +45,8 @@ namespace data
};
};
typedef std::function<bool(const Lease & l)> LeaseInspectFunc;
const size_t MAX_LS_BUFFER_SIZE = 3072;
const size_t LEASE_SIZE = 44; // 32 + 4 + 8
const uint8_t MAX_NUM_LEASES = 16;
@@ -56,10 +65,12 @@ namespace data
size_t GetBufferLen () const { return m_BufferLen; };
bool IsValid () const { return m_IsValid; };
const std::vector<std::shared_ptr<const Lease> > GetNonExpiredLeases (bool withThreshold = true) const;
const std::vector<std::shared_ptr<const Lease> > GetNonExpiredLeasesExcluding (LeaseInspectFunc exclude, bool withThreshold = true) const;
bool HasExpiredLeases () const;
bool IsExpired () const;
bool IsEmpty () const { return m_Leases.empty (); };
uint64_t GetExpirationTime () const { return m_ExpirationTime; };
bool ExpiresSoon(const uint64_t dlt=1000 * 5, const uint64_t fudge = 0) const ;
bool operator== (const LeaseSet& other) const
{ return m_BufferLen == other.m_BufferLen && !memcmp (m_Buffer, other.m_Buffer, m_BufferLen); };

116
Log.cpp
View File

@@ -10,7 +10,7 @@
namespace i2p {
namespace log {
Log logger;
static Log logger;
/**
* @brief Maps our loglevel to their symbolic name
*/
@@ -22,6 +22,22 @@ namespace log {
"debug" // eLogDebug
};
/**
* @brief Colorize log output -- array of terminal control sequences
* @note Using ISO 6429 (ANSI) color sequences
*/
#ifdef _WIN32
static const char *LogMsgColors[] = { "", "", "", "", "" };
#else /* UNIX */
static const char *LogMsgColors[] = {
[eLogError] = "\033[1;31m", /* red */
[eLogWarning] = "\033[1;33m", /* yellow */
[eLogInfo] = "\033[1;36m", /* cyan */
[eLogDebug] = "\033[1;34m", /* blue */
[eNumLogLevels] = "\033[0m", /* reset */
};
#endif
#ifndef _WIN32
/**
* @brief Maps our log levels to syslog one
@@ -42,13 +58,29 @@ namespace log {
Log::Log():
m_Destination(eLogStdout), m_MinLevel(eLogInfo),
m_LogStream (nullptr), m_Logfile(""), m_IsReady(false)
m_LogStream (nullptr), m_Logfile(""), m_HasColors(true),
m_IsRunning (false), m_Thread (nullptr)
{
}
Log::~Log ()
{
switch (m_Destination) {
delete m_Thread;
}
void Log::Start ()
{
if (!m_IsRunning)
{
m_IsRunning = true;
m_Thread = new std::thread (std::bind (&Log::Run, this));
}
}
void Log::Stop ()
{
switch (m_Destination)
{
#ifndef _WIN32
case eLogSyslog :
closelog();
@@ -62,7 +94,14 @@ namespace log {
/* do nothing */
break;
}
Process();
m_IsRunning = false;
m_Queue.WakeUp ();
if (m_Thread)
{
m_Thread->join ();
delete m_Thread;
m_Thread = nullptr;
}
}
void Log::SetLogLevel (const std::string& level) {
@@ -90,45 +129,53 @@ namespace log {
* Unfortunately, with current startup process with late fork() this
* will give us nothing but pain. Maybe later. See in NetDb as example.
*/
void Log::Process() {
std::unique_lock<std::mutex> l(m_OutputLock);
void Log::Process(std::shared_ptr<LogMsg> msg)
{
if (!msg) return;
std::hash<std::thread::id> hasher;
unsigned short short_tid;
while (1) {
auto msg = m_Queue.GetNextWithTimeout (1);
if (!msg)
break;
short_tid = (short) (hasher(msg->tid) % 1000);
switch (m_Destination) {
short_tid = (short) (hasher(msg->tid) % 1000);
switch (m_Destination) {
#ifndef _WIN32
case eLogSyslog:
syslog(GetSyslogPrio(msg->level), "[%03u] %s", short_tid, msg->text.c_str());
break;
case eLogSyslog:
syslog(GetSyslogPrio(msg->level), "[%03u] %s", short_tid, msg->text.c_str());
break;
#endif
case eLogFile:
case eLogStream:
if (m_LogStream)
*m_LogStream << TimeAsString(msg->timestamp)
<< "@" << short_tid
<< "/" << g_LogLevelStr[msg->level]
<< " - " << msg->text << std::endl;
break;
case eLogStdout:
default:
std::cout << TimeAsString(msg->timestamp)
case eLogFile:
case eLogStream:
if (m_LogStream)
*m_LogStream << TimeAsString(msg->timestamp)
<< "@" << short_tid
<< "/" << g_LogLevelStr[msg->level]
<< " - " << msg->text << std::endl;
break;
} // switch
} // while
break;
case eLogStdout:
default:
std::cout << TimeAsString(msg->timestamp)
<< "@" << short_tid
<< "/" << LogMsgColors[msg->level] << g_LogLevelStr[msg->level] << LogMsgColors[eNumLogLevels]
<< " - " << msg->text << std::endl;
break;
} // switch
}
void Log::Append(std::shared_ptr<i2p::log::LogMsg> & msg) {
void Log::Run ()
{
Reopen ();
while (m_IsRunning)
{
std::shared_ptr<LogMsg> msg;
while (msg = m_Queue.Get ())
Process (msg);
if (m_LogStream) m_LogStream->flush();
if (m_IsRunning)
m_Queue.Wait ();
}
}
void Log::Append(std::shared_ptr<i2p::log::LogMsg> & msg)
{
m_Queue.Put(msg);
if (!m_IsReady)
return;
Process();
}
void Log::SendTo (const std::string& path)
@@ -138,6 +185,7 @@ namespace log {
auto os = std::make_shared<std::ofstream> (path, flags);
if (os->is_open ())
{
m_HasColors = false;
m_Logfile = path;
m_Destination = eLogFile;
m_LogStream = os;
@@ -147,12 +195,14 @@ namespace log {
}
void Log::SendTo (std::shared_ptr<std::ostream> os) {
m_HasColors = false;
m_Destination = eLogStream;
m_LogStream = os;
}
#ifndef _WIN32
void Log::SendTo(const char *name, int facility) {
m_HasColors = false;
m_Destination = eLogSyslog;
m_LogStream = nullptr;
openlog(name, LOG_CONS | LOG_PID, facility);

41
Log.h
View File

@@ -16,6 +16,7 @@
#include <sstream>
#include <chrono>
#include <memory>
#include <thread>
#include "Queue.h"
#ifndef _WIN32
@@ -42,6 +43,7 @@ enum LogType {
namespace i2p {
namespace log {
struct LogMsg; /* forward declaration */
class Log
@@ -55,8 +57,9 @@ namespace log {
std::time_t m_LastTimestamp;
char m_LastDateTime[64];
i2p::util::Queue<std::shared_ptr<LogMsg> > m_Queue;
volatile bool m_IsReady;
mutable std::mutex m_OutputLock;
bool m_HasColors;
volatile bool m_IsRunning;
std::thread * m_Thread;
private:
@@ -64,10 +67,8 @@ namespace log {
Log (const Log &);
const Log& operator=(const Log&);
/**
* @brief process stored messages in queue
*/
void Process ();
void Run ();
void Process (std::shared_ptr<LogMsg> msg);
/**
* @brief Makes formatted string from unix timestamp
@@ -85,6 +86,9 @@ namespace log {
LogType GetLogType () { return m_Destination; };
LogLevel GetLogLevel () { return m_MinLevel; };
void Start ();
void Stop ();
/**
* @brief Sets minimal allowed level for log messages
* @param level String with wanted minimal msg level
@@ -118,12 +122,6 @@ namespace log {
*/
void Append(std::shared_ptr<i2p::log::LogMsg> &);
/** @brief Allow log output */
void Ready() { m_IsReady = true; }
/** @brief Flushes the output log stream */
void Flush();
/** @brief Reopen log file */
void Reopen();
};
@@ -140,27 +138,27 @@ namespace log {
std::string text; /**< message text as single string */
LogLevel level; /**< message level */
std::thread::id tid; /**< id of thread that generated message */
LogMsg (LogLevel lvl, std::time_t ts, const std::string & txt): timestamp(ts), text(txt), level(lvl) {};
};
Log & Logger();
} // log
} // i2p
}
/** internal usage only -- folding args array to single string */
template<typename TValue>
void LogPrint (std::stringstream& s, TValue arg)
void LogPrint (std::stringstream& s, TValue&& arg) noexcept
{
s << arg;
s << std::forward<TValue>(arg);
}
/** internal usage only -- folding args array to single string */
template<typename TValue, typename... TArgs>
void LogPrint (std::stringstream& s, TValue arg, TArgs... args)
void LogPrint (std::stringstream& s, TValue&& arg, TArgs&&... args) noexcept
{
LogPrint (s, arg);
LogPrint (s, args...);
LogPrint (s, std::forward<TValue>(arg));
LogPrint (s, std::forward<TArgs>(args)...);
}
/**
@@ -169,7 +167,7 @@ void LogPrint (std::stringstream& s, TValue arg, TArgs... args)
* @param args Array of message parts
*/
template<typename... TArgs>
void LogPrint (LogLevel level, TArgs... args)
void LogPrint (LogLevel level, TArgs&&... args) noexcept
{
i2p::log::Log &log = i2p::log::Logger();
if (level > log.GetLogLevel ())
@@ -177,8 +175,9 @@ void LogPrint (LogLevel level, TArgs... args)
// fold message to single string
std::stringstream ss("");
LogPrint (ss, args ...);
LogPrint (ss, std::forward<TArgs>(args)...);
auto msg = std::make_shared<i2p::log::LogMsg>(level, std::time(nullptr), ss.str());
msg->tid = std::this_thread::get_id();
log.Append(msg);

View File

@@ -4,15 +4,21 @@ ARLIB := libi2pd.a
SHLIB_CLIENT := libi2pdclient.so
ARLIB_CLIENT := libi2pdclient.a
I2PD := i2pd
GREP := fgrep
GREP := grep
DEPS := obj/make.dep
include filelist.mk
USE_AESNI := yes
USE_STATIC := no
USE_MESHNET := no
USE_UPNP := no
USE_AESNI := yes
USE_AVX := yes
USE_STATIC := no
USE_MESHNET := no
USE_UPNP := no
ifeq ($(WEBSOCKETS),1)
NEEDED_CXXFLAGS += -DWITH_EVENTS
DAEMON_SRC += Websocket.cpp
endif
ifeq ($(UNAME),Darwin)
DAEMON_SRC += DaemonLinux.cpp
@@ -21,7 +27,7 @@ ifeq ($(UNAME),Darwin)
else
include Makefile.osx
endif
else ifeq ($(shell echo $(UNAME) | $(GREP) -c FreeBSD),1)
else ifeq ($(shell echo $(UNAME) | $(GREP) -Ec '(Free|Open)BSD'),1)
DAEMON_SRC += DaemonLinux.cpp
include Makefile.bsd
else ifeq ($(UNAME),Linux)
@@ -89,10 +95,15 @@ strip: $(I2PD) $(SHLIB_CLIENT) $(SHLIB)
strip $^
LATEST_TAG=$(shell git describe --tags --abbrev=0 openssl)
BRANCH=$(shell git rev-parse --abbrev-ref HEAD)
dist:
git archive --format=tar.gz -9 --worktree-attributes \
--prefix=i2pd_$(LATEST_TAG)/ $(LATEST_TAG) -o i2pd_$(LATEST_TAG).tar.gz
last-dist:
git archive --format=tar.gz -9 --worktree-attributes \
--prefix=i2pd_$(LATEST_TAG)/ $(BRANCH) -o ../i2pd_$(LATEST_TAG).orig.tar.gz
doxygen:
doxygen -s docs/Doxyfile

View File

@@ -1,5 +1,5 @@
# set defaults instead redefine
CXXFLAGS ?= -g -Wall -Wextra -Wno-unused-parameter -pedantic
CXXFLAGS ?= -g -Wall -Wextra -Wno-unused-parameter -pedantic -Wno-misleading-indentation
INCFLAGS ?=
## NOTE: The NEEDED_CXXFLAGS are here so that custom CXXFLAGS can be specified at build time
@@ -28,6 +28,9 @@ endif
NEEDED_CXXFLAGS += -fPIC
ifeq ($(USE_STATIC),yes)
# NOTE: on glibc you will get this warning:
# Using 'getaddrinfo' in statically linked applications requires at runtime
# the shared libraries from the glibc version used for linking
LIBDIR := /usr/lib
LDLIBS = $(LIBDIR)/libboost_system.a
LDLIBS += $(LIBDIR)/libboost_date_time.a
@@ -36,24 +39,35 @@ ifeq ($(USE_STATIC),yes)
LDLIBS += $(LIBDIR)/libssl.a
LDLIBS += $(LIBDIR)/libcrypto.a
LDLIBS += $(LIBDIR)/libz.a
LDLIBS += -lpthread -static-libstdc++ -static-libgcc -lrt
LDLIBS += -lpthread -static-libstdc++ -static-libgcc -lrt -ldl
USE_AESNI := no
else
LDLIBS = -lcrypto -lssl -lz -lboost_system -lboost_date_time -lboost_filesystem -lboost_program_options -lpthread
endif
# UPNP Support (miniupnpc 1.5 or 1.6)
# UPNP Support (miniupnpc 1.5 and higher)
ifeq ($(USE_UPNP),yes)
LDFLAGS += -lminiupnpc
CXXFLAGS += -DUSE_UPNP
ifeq ($(USE_STATIC),yes)
LDLIBS += $(LIBDIR)/libminiupnpc.a
else
LDLIBS += -lminiupnpc
endif
endif
IS_64 := $(shell $(CXX) -dumpmachine 2>&1 | $(GREP) -c "64")
ifeq ($(USE_AESNI),yes)
ifeq ($(IS_64),1)
#check if AES-NI is supported by CPU
ifneq ($(shell grep -c aes /proc/cpuinfo),0)
CPU_FLAGS = -maes -DAESNI
ifneq ($(shell $(GREP) -c aes /proc/cpuinfo),0)
CPU_FLAGS += -maes -DAESNI
endif
endif
endif
ifeq ($(USE_AVX),yes)
#check if AVX supported by CPU
ifneq ($(shell $(GREP) -c avx /proc/cpuinfo),0)
CPU_FLAGS += -mavx
endif
endif

View File

@@ -37,10 +37,20 @@ ifeq ($(USE_WIN32_APP), yes)
DAEMON_OBJS += $(patsubst %.rc,obj/%.o,$(DAEMON_RC))
endif
# don't change following line to ifeq ($(USE_AESNI),yes) !!!
ifeq ($(USE_AESNI),1)
CPU_FLAGS = -maes -DAESNI
CPU_FLAGS += -maes -DAESNI
else
CPU_FLAGS = -msse
CPU_FLAGS += -msse
endif
ifeq ($(USE_AVX),1)
CPU_FLAGS += -mavx
endif
ifeq ($(USE_ASLR),yes)
LDFLAGS += -Wl,--nxcompat -Wl,--high-entropy-va \
-Wl,--dynamicbase,--export-all-symbols
endif
obj/%.o : %.rc

View File

@@ -1,21 +1,28 @@
CXX = clang++
CXXFLAGS = -g -Wall -std=c++11 -DMAC_OSX
CXXFLAGS = -Os -Wall -std=c++11 -DMAC_OSX
#CXXFLAGS = -g -O2 -Wall -std=c++11
INCFLAGS = -I/usr/local/include -I/usr/local/ssl/include
LDFLAGS = -Wl,-rpath,/usr/local/lib -L/usr/local/lib -L/usr/local/ssl/lib
INCFLAGS = -I/usr/local/include
LDFLAGS = -Wl,-rpath,/usr/local/lib -L/usr/local/lib
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
else
LDLIBS = -lz -lcrypto -lssl -lboost_system -lboost_date_time -lboost_filesystem -lboost_program_options -lpthread
endif
ifeq ($(USE_UPNP),yes)
LDFLAGS += -ldl
CXXFLAGS += -DUSE_UPNP
endif
# OSX Notes
# http://www.hutsby.net/2011/08/macs-with-aes-ni.html
# Seems like all recent Mac's have AES-NI, after firmware upgrade 2.2
# Found no good way to detect it from command line. TODO: Might be some osx sysinfo magic
ifeq ($(USE_AESNI),yes)
ifeq ($(USE_AESNI),1)
CXXFLAGS += -maes -DAESNI
else
CXXFLAGS += -msse
endif
ifeq ($(USE_AVX),1)
CXXFLAGS += -mavx
endif
# Disabled, since it will be the default make rule. I think its better

View File

@@ -1,5 +1,6 @@
#include <string.h>
#include <stdlib.h>
#include <future>
#include "I2PEndian.h"
#include "Base.h"
@@ -11,6 +12,9 @@
#include "Transports.h"
#include "NetDb.h"
#include "NTCPSession.h"
#ifdef WITH_EVENTS
#include "Event.h"
#endif
using namespace i2p::crypto;
@@ -21,7 +25,7 @@ namespace transport
NTCPSession::NTCPSession (NTCPServer& server, std::shared_ptr<const i2p::data::RouterInfo> in_RemoteRouter):
TransportSession (in_RemoteRouter, NTCP_TERMINATION_TIMEOUT),
m_Server (server), m_Socket (m_Server.GetService ()),
m_TerminationTimer (m_Server.GetService ()), m_IsEstablished (false), m_IsTerminated (false),
m_IsEstablished (false), m_IsTerminated (false),
m_ReceiveBufferOffset (0), m_NextMessage (nullptr), m_IsSending (false)
{
m_Establisher = new Establisher;
@@ -32,12 +36,12 @@ namespace transport
delete m_Establisher;
}
void NTCPSession::CreateAESKey (uint8_t * pubKey, i2p::crypto::AESKey& key)
void NTCPSession::CreateAESKey (uint8_t * pubKey)
{
uint8_t sharedKey[256];
m_DHKeysPair->Agree (pubKey, sharedKey);
m_DHKeysPair->Agree (pubKey, sharedKey); // time consuming operation
uint8_t * aesKey = key;
i2p::crypto::AESKey aesKey;
if (sharedKey[0] & 0x80)
{
aesKey[0] = 0;
@@ -60,6 +64,9 @@ namespace transport
}
memcpy (aesKey, nonZero, 32);
}
m_Decryption.SetKey (aesKey);
m_Encryption.SetKey (aesKey);
}
void NTCPSession::Done ()
@@ -78,7 +85,6 @@ namespace transport
m_Server.RemoveNTCPSession (shared_from_this ());
m_SendQueue.clear ();
m_NextMessage = nullptr;
m_TerminationTimer.cancel ();
LogPrint (eLogDebug, "NTCP: session terminated");
}
}
@@ -110,22 +116,14 @@ namespace transport
boost::asio::async_write (m_Socket, boost::asio::buffer (&m_Establisher->phase1, sizeof (NTCPPhase1)), boost::asio::transfer_all (),
std::bind(&NTCPSession::HandlePhase1Sent, shared_from_this (), std::placeholders::_1, std::placeholders::_2));
ScheduleTermination ();
}
void NTCPSession::ServerLogin ()
{
boost::system::error_code ec;
auto ep = m_Socket.remote_endpoint(ec);
if (!ec)
{
m_ConnectedFrom = ep.address ();
// receive Phase1
boost::asio::async_read (m_Socket, boost::asio::buffer(&m_Establisher->phase1, sizeof (NTCPPhase1)), boost::asio::transfer_all (),
std::bind(&NTCPSession::HandlePhase1Received, shared_from_this (),
std::placeholders::_1, std::placeholders::_2));
ScheduleTermination ();
}
// receive Phase1
boost::asio::async_read (m_Socket, boost::asio::buffer(&m_Establisher->phase1, sizeof (NTCPPhase1)), boost::asio::transfer_all (),
std::bind(&NTCPSession::HandlePhase1Received, shared_from_this (),
std::placeholders::_1, std::placeholders::_2));
}
void NTCPSession::HandlePhase1Sent (const boost::system::error_code& ecode, std::size_t bytes_transferred)
@@ -169,15 +167,25 @@ namespace transport
return;
}
}
SendPhase2 ();
// TODO: check for number of pending keys
auto s = shared_from_this ();
auto keyCreated = std::async (std::launch::async, [s] ()
{
if (!s->m_DHKeysPair)
s->m_DHKeysPair = transports.GetNextDHKeysPair ();
s->CreateAESKey (s->m_Establisher->phase1.pubKey);
}).share ();
m_Server.GetService ().post ([s, keyCreated]()
{
keyCreated.get ();
s->SendPhase2 ();
});
}
}
void NTCPSession::SendPhase2 ()
{
if (!m_DHKeysPair)
m_DHKeysPair = transports.GetNextDHKeysPair ();
const uint8_t * y = m_DHKeysPair->GetPublicKey ();
memcpy (m_Establisher->phase2.pubKey, y, 256);
uint8_t xy[512];
@@ -188,11 +196,7 @@ namespace transport
memcpy (m_Establisher->phase2.encrypted.timestamp, &tsB, 4);
RAND_bytes (m_Establisher->phase2.encrypted.filler, 12);
i2p::crypto::AESKey aesKey;
CreateAESKey (m_Establisher->phase1.pubKey, aesKey);
m_Encryption.SetKey (aesKey);
m_Encryption.SetIV (y + 240);
m_Decryption.SetKey (aesKey);
m_Decryption.SetIV (m_Establisher->phase1.HXxorHI + 16);
m_Encryption.Encrypt ((uint8_t *)&m_Establisher->phase2.encrypted, sizeof(m_Establisher->phase2.encrypted), (uint8_t *)&m_Establisher->phase2.encrypted);
@@ -235,35 +239,47 @@ namespace transport
}
else
{
i2p::crypto::AESKey aesKey;
CreateAESKey (m_Establisher->phase2.pubKey, aesKey);
m_Decryption.SetKey (aesKey);
m_Decryption.SetIV (m_Establisher->phase2.pubKey + 240);
m_Encryption.SetKey (aesKey);
m_Encryption.SetIV (m_Establisher->phase1.HXxorHI + 16);
m_Decryption.Decrypt((uint8_t *)&m_Establisher->phase2.encrypted, sizeof(m_Establisher->phase2.encrypted), (uint8_t *)&m_Establisher->phase2.encrypted);
// verify
uint8_t xy[512];
memcpy (xy, m_DHKeysPair->GetPublicKey (), 256);
memcpy (xy + 256, m_Establisher->phase2.pubKey, 256);
uint8_t digest[32];
SHA256 (xy, 512, digest);
if (memcmp(m_Establisher->phase2.encrypted.hxy, digest, 32))
{
LogPrint (eLogError, "NTCP: Phase 2 process error: incorrect hash");
transports.ReuseDHKeysPair (m_DHKeysPair);
m_DHKeysPair = nullptr;
Terminate ();
return ;
}
SendPhase3 ();
auto s = shared_from_this ();
// create AES key in separate thread
auto keyCreated = std::async (std::launch::async, [s] ()
{
s->CreateAESKey (s->m_Establisher->phase2.pubKey);
}).share (); // TODO: use move capture in C++ 14 instead shared_future
// let other operations execute while a key gets created
m_Server.GetService ().post ([s, keyCreated]()
{
keyCreated.get (); // we might wait if no more pending operations
s->HandlePhase2 ();
});
}
}
void NTCPSession::HandlePhase2 ()
{
m_Decryption.SetIV (m_Establisher->phase2.pubKey + 240);
m_Encryption.SetIV (m_Establisher->phase1.HXxorHI + 16);
m_Decryption.Decrypt((uint8_t *)&m_Establisher->phase2.encrypted, sizeof(m_Establisher->phase2.encrypted), (uint8_t *)&m_Establisher->phase2.encrypted);
// verify
uint8_t xy[512];
memcpy (xy, m_DHKeysPair->GetPublicKey (), 256);
memcpy (xy + 256, m_Establisher->phase2.pubKey, 256);
uint8_t digest[32];
SHA256 (xy, 512, digest);
if (memcmp(m_Establisher->phase2.encrypted.hxy, digest, 32))
{
LogPrint (eLogError, "NTCP: Phase 2 process error: incorrect hash");
transports.ReuseDHKeysPair (m_DHKeysPair);
m_DHKeysPair = nullptr;
Terminate ();
return ;
}
SendPhase3 ();
}
void NTCPSession::SendPhase3 ()
{
auto keys = i2p::context.GetPrivateKeys ();
auto& keys = i2p::context.GetPrivateKeys ();
uint8_t * buf = m_ReceiveBuffer;
htobe16buf (buf, keys.GetPublic ()->GetFullLen ());
buf += 2;
@@ -330,12 +346,15 @@ namespace transport
m_Decryption.Decrypt (m_ReceiveBuffer, bytes_transferred, m_ReceiveBuffer);
uint8_t * buf = m_ReceiveBuffer;
uint16_t size = bufbe16toh (buf);
SetRemoteIdentity (std::make_shared<i2p::data::IdentityEx> (buf + 2, size));
if (m_Server.FindNTCPSession (m_RemoteIdentity->GetIdentHash ()))
auto identity = std::make_shared<i2p::data::IdentityEx> (buf + 2, size);
if (m_Server.FindNTCPSession (identity->GetIdentHash ()))
{
LogPrint (eLogInfo, "NTCP: session already exists");
Terminate ();
}
auto existing = i2p::data::netdb.FindRouter (identity->GetIdentHash ()); // check if exists already
SetRemoteIdentity (existing ? existing->GetRouterIdentity () : identity);
size_t expectedSize = size + 2/*size*/ + 4/*timestamp*/ + m_RemoteIdentity->GetSignatureLen ();
size_t paddingLen = expectedSize & 0x0F;
if (paddingLen) paddingLen = (16 - paddingLen);
@@ -409,7 +428,7 @@ namespace transport
s.Insert (m_RemoteIdentity->GetIdentHash (), 32); // ident
s.Insert (tsA); // tsA
s.Insert (tsB); // tsB
auto keys = i2p::context.GetPrivateKeys ();
auto& keys = i2p::context.GetPrivateKeys ();
auto signatureLen = keys.GetPublic ()->GetSignatureLen ();
s.Sign (keys, m_ReceiveBuffer);
size_t paddingSize = signatureLen & 0x0F; // %16
@@ -499,11 +518,10 @@ namespace transport
void NTCPSession::HandleReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred)
{
if (ecode) {
if (ecode)
{
if (ecode != boost::asio::error::operation_aborted)
LogPrint (eLogDebug, "NTCP: Read error: ", ecode.message ());
if (!m_NumReceivedBytes)
m_Server.Ban (m_ConnectedFrom);
//if (ecode != boost::asio::error::operation_aborted)
Terminate ();
}
@@ -515,50 +533,68 @@ namespace transport
if (m_ReceiveBufferOffset >= 16)
{
int numReloads = 0;
do
{
uint8_t * nextBlock = m_ReceiveBuffer;
while (m_ReceiveBufferOffset >= 16)
// process received data
uint8_t * nextBlock = m_ReceiveBuffer;
while (m_ReceiveBufferOffset >= 16)
{
if (!DecryptNextBlock (nextBlock)) // 16 bytes
{
if (!DecryptNextBlock (nextBlock)) // 16 bytes
{
Terminate ();
return;
}
nextBlock += 16;
m_ReceiveBufferOffset -= 16;
}
if (m_ReceiveBufferOffset > 0)
memcpy (m_ReceiveBuffer, nextBlock, m_ReceiveBufferOffset);
// try to read more
if (numReloads < 5)
{
boost::system::error_code ec;
size_t moreBytes = m_Socket.available(ec);
if (moreBytes)
{
if (moreBytes > NTCP_BUFFER_SIZE - m_ReceiveBufferOffset)
moreBytes = NTCP_BUFFER_SIZE - m_ReceiveBufferOffset;
moreBytes = m_Socket.read_some (boost::asio::buffer (m_ReceiveBuffer + m_ReceiveBufferOffset, moreBytes));
if (ec)
{
LogPrint (eLogInfo, "NTCP: Read more bytes error: ", ec.message ());
Terminate ();
return;
}
m_NumReceivedBytes += moreBytes;
m_ReceiveBufferOffset += moreBytes;
numReloads++;
}
Terminate ();
return;
}
nextBlock += 16;
m_ReceiveBufferOffset -= 16;
}
while (m_ReceiveBufferOffset >= 16);
m_Handler.Flush ();
}
if (m_ReceiveBufferOffset > 0)
memcpy (m_ReceiveBuffer, nextBlock, m_ReceiveBufferOffset);
}
// read and process more is available
boost::system::error_code ec;
size_t moreBytes = m_Socket.available(ec);
if (moreBytes && !ec)
{
uint8_t * buf = nullptr, * moreBuf = m_ReceiveBuffer;
if (moreBytes + m_ReceiveBufferOffset > NTCP_BUFFER_SIZE)
{
buf = new uint8_t[moreBytes + m_ReceiveBufferOffset + 16];
moreBuf = buf;
uint8_t rem = ((size_t)buf) & 0x0f;
if (rem) moreBuf += (16 - rem); // align 16
if (m_ReceiveBufferOffset)
memcpy (moreBuf, m_ReceiveBuffer, m_ReceiveBufferOffset);
}
moreBytes = m_Socket.read_some (boost::asio::buffer (moreBuf + m_ReceiveBufferOffset, moreBytes), ec);
if (ec)
{
LogPrint (eLogInfo, "NTCP: Read more bytes error: ", ec.message ());
delete[] buf;
Terminate ();
return;
}
m_ReceiveBufferOffset += moreBytes;
m_NumReceivedBytes += moreBytes;
i2p::transport::transports.UpdateReceivedBytes (moreBytes);
// process more data
uint8_t * nextBlock = moreBuf;
while (m_ReceiveBufferOffset >= 16)
{
if (!DecryptNextBlock (nextBlock)) // 16 bytes
{
delete[] buf;
Terminate ();
return;
}
nextBlock += 16;
m_ReceiveBufferOffset -= 16;
}
if (m_ReceiveBufferOffset > 0)
memcpy (m_ReceiveBuffer, nextBlock, m_ReceiveBufferOffset); // nextBlock points to memory inside buf
delete[] buf;
}
m_Handler.Flush ();
ScheduleTermination (); // reset termination timer
m_LastActivityTimestamp = i2p::util::GetSecondsSinceEpoch ();
Receive ();
}
}
@@ -574,40 +610,46 @@ namespace transport
if (dataSize)
{
// new message
if (dataSize + 16U > NTCP_MAX_MESSAGE_SIZE - 2) // + 6 + padding
if (dataSize + 16U + 15U > NTCP_MAX_MESSAGE_SIZE - 2) // + 6 + padding
{
LogPrint (eLogError, "NTCP: data size ", dataSize, " exceeds max size");
return false;
}
auto msg = (dataSize + 16U) <= I2NP_MAX_SHORT_MESSAGE_SIZE - 2 ? NewI2NPShortMessage () : NewI2NPMessage ();
m_NextMessage = msg;
memcpy (m_NextMessage->buf, buf, 16);
m_NextMessage = (dataSize + 16U + 15U) <= I2NP_MAX_SHORT_MESSAGE_SIZE - 2 ? NewI2NPShortMessage () : NewI2NPMessage ();
m_NextMessage->Align (16);
m_NextMessage->offset += 2; // size field
m_NextMessage->len = m_NextMessage->offset + dataSize;
memcpy (m_NextMessage->GetBuffer () - 2, buf, 16);
m_NextMessageOffset = 16;
m_NextMessage->offset = 2; // size field
m_NextMessage->len = dataSize + 2;
}
else
{
// timestamp
LogPrint (eLogDebug, "NTCP: Timestamp");
int diff = (int)bufbe32toh (buf + 2) - (int)i2p::util::GetSecondsSinceEpoch ();
LogPrint (eLogInfo, "NTCP: Timestamp. Time difference ", diff, " seconds");
return true;
}
}
else // message continues
{
m_Decryption.Decrypt (encrypted, m_NextMessage->buf + m_NextMessageOffset);
m_Decryption.Decrypt (encrypted, m_NextMessage->GetBuffer () - 2 + m_NextMessageOffset);
m_NextMessageOffset += 16;
}
if (m_NextMessageOffset >= m_NextMessage->len + 4) // +checksum
if (m_NextMessageOffset >= m_NextMessage->GetLength () + 2 + 4) // +checksum
{
// we have a complete I2NP message
uint8_t checksum[4];
htobe32buf (checksum, adler32 (adler32 (0, Z_NULL, 0), m_NextMessage->buf, m_NextMessageOffset - 4));
if (!memcmp (m_NextMessage->buf + m_NextMessageOffset - 4, checksum, 4))
htobe32buf (checksum, adler32 (adler32 (0, Z_NULL, 0), m_NextMessage->GetBuffer () - 2, m_NextMessageOffset - 4));
if (!memcmp (m_NextMessage->GetBuffer () - 2 + m_NextMessageOffset - 4, checksum, 4))
{
if (!m_NextMessage->IsExpired ())
{
#ifdef WITH_EVENTS
QueueIntEvent("transport.recvmsg", GetIdentHashBase64(), 1);
#endif
m_Handler.PutNextMessage (m_NextMessage);
}
else
LogPrint (eLogInfo, "NTCP: message expired");
}
@@ -645,7 +687,7 @@ namespace transport
sendBuffer = m_TimeSyncBuffer;
len = 4;
htobuf16(sendBuffer, 0);
htobe32buf (sendBuffer + 2, time (0));
htobe32buf (sendBuffer + 2, i2p::util::GetSecondsSinceEpoch ());
}
int rem = (len + 6) & 0x0F; // %16
int padding = 0;
@@ -685,6 +727,7 @@ namespace transport
}
else
{
m_LastActivityTimestamp = i2p::util::GetSecondsSinceEpoch ();
m_NumSentBytes += bytes_transferred;
i2p::transport::transports.UpdateSentBytes (bytes_transferred);
if (!m_SendQueue.empty())
@@ -692,8 +735,6 @@ namespace transport
Send (m_SendQueue);
m_SendQueue.clear ();
}
else
ScheduleTermination (); // reset termination timer
}
}
@@ -728,29 +769,11 @@ namespace transport
else
Send (msgs);
}
void NTCPSession::ScheduleTermination ()
{
m_TerminationTimer.cancel ();
m_TerminationTimer.expires_from_now (boost::posix_time::seconds(GetTerminationTimeout ()));
m_TerminationTimer.async_wait (std::bind (&NTCPSession::HandleTerminationTimer,
shared_from_this (), std::placeholders::_1));
}
void NTCPSession::HandleTerminationTimer (const boost::system::error_code& ecode)
{
if (ecode != boost::asio::error::operation_aborted)
{
LogPrint (eLogDebug, "NTCP: No activity for ", GetTerminationTimeout (), " seconds");
//Terminate ();
m_Socket.close ();// invoke Terminate () from HandleReceive
}
}
//-----------------------------------------
NTCPServer::NTCPServer ():
m_IsRunning (false), m_Thread (nullptr), m_Work (m_Service),
m_NTCPAcceptor (nullptr), m_NTCPV6Acceptor (nullptr)
m_TerminationTimer (m_Service), m_NTCPAcceptor (nullptr), m_NTCPV6Acceptor (nullptr)
{
}
@@ -769,6 +792,7 @@ namespace transport
auto& addresses = context.GetRouterInfo ().GetAddresses ();
for (const auto& address: addresses)
{
if (!address) continue;
if (address->transportStyle == i2p::data::RouterInfo::eTransportNTCP)
{
if (address->host.is_v4())
@@ -809,24 +833,35 @@ namespace transport
}
}
}
}
}
ScheduleTermination ();
}
}
void NTCPServer::Stop ()
{
{
// we have to copy it because Terminate changes m_NTCPSessions
auto ntcpSessions = m_NTCPSessions;
for (auto& it: ntcpSessions)
it.second->Terminate ();
}
m_NTCPSessions.clear ();
if (m_IsRunning)
{
m_IsRunning = false;
if (m_NTCPAcceptor)
delete m_NTCPAcceptor;
m_NTCPAcceptor = nullptr;
if (m_NTCPV6Acceptor)
delete m_NTCPV6Acceptor;
m_NTCPV6Acceptor = nullptr;
m_TerminationTimer.cancel ();
if (m_NTCPAcceptor)
{
delete m_NTCPAcceptor;
m_NTCPAcceptor = nullptr;
}
if (m_NTCPV6Acceptor)
{
delete m_NTCPV6Acceptor;
m_NTCPV6Acceptor = nullptr;
}
m_Service.stop ();
if (m_Thread)
{
@@ -861,6 +896,7 @@ namespace transport
if (it != m_NTCPSessions.end ())
{
LogPrint (eLogWarning, "NTCP: session to ", ident.ToBase64 (), " already exists");
session->Terminate();
return false;
}
m_NTCPSessions.insert (std::pair<i2p::data::IdentHash, std::shared_ptr<NTCPSession> >(ident, session));
@@ -890,18 +926,6 @@ namespace transport
if (!ec)
{
LogPrint (eLogDebug, "NTCP: Connected from ", ep);
auto it = m_BanList.find (ep.address ());
if (it != m_BanList.end ())
{
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
if (ts < it->second)
{
LogPrint (eLogWarning, "NTCP: ", ep.address (), " is banned for ", it->second - ts, " more seconds");
conn = nullptr;
}
else
m_BanList.erase (it);
}
if (conn)
conn->ServerLogin ();
}
@@ -927,18 +951,6 @@ namespace transport
if (!ec)
{
LogPrint (eLogDebug, "NTCP: Connected from ", ep);
auto it = m_BanList.find (ep.address ());
if (it != m_BanList.end ())
{
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
if (ts < it->second)
{
LogPrint (eLogWarning, "NTCP: ", ep.address (), " is banned for ", it->second - ts, " more seconds");
conn = nullptr;
}
else
m_BanList.erase (it);
}
if (conn)
conn->ServerLogin ();
}
@@ -958,18 +970,31 @@ namespace transport
{
LogPrint (eLogDebug, "NTCP: Connecting to ", address ,":", port);
m_Service.post([=]()
{
{
if (this->AddNTCPSession (conn))
{
auto timer = std::make_shared<boost::asio::deadline_timer>(m_Service);
timer->expires_from_now (boost::posix_time::seconds(NTCP_CONNECT_TIMEOUT));
timer->async_wait ([conn](const boost::system::error_code& ecode)
{
if (ecode != boost::asio::error::operation_aborted)
{
LogPrint (eLogInfo, "NTCP: Not connected in ", NTCP_CONNECT_TIMEOUT, " seconds");
conn->Terminate ();
}
});
conn->GetSocket ().async_connect (boost::asio::ip::tcp::endpoint (address, port),
std::bind (&NTCPServer::HandleConnect, this, std::placeholders::_1, conn));
std::bind (&NTCPServer::HandleConnect, this, std::placeholders::_1, conn, timer));
}
});
}
void NTCPServer::HandleConnect (const boost::system::error_code& ecode, std::shared_ptr<NTCPSession> conn)
void NTCPServer::HandleConnect (const boost::system::error_code& ecode, std::shared_ptr<NTCPSession> conn, std::shared_ptr<boost::asio::deadline_timer> timer)
{
timer->cancel ();
if (ecode)
{
LogPrint (eLogError, "NTCP: Can't connect to ", conn->GetSocket ().remote_endpoint (), ": ", ecode.message ());
LogPrint (eLogInfo, "NTCP: Connect error ", ecode.message ());
if (ecode != boost::asio::error::operation_aborted)
i2p::data::netdb.SetUnreachable (conn->GetRemoteIdentity ()->GetIdentHash (), true);
conn->Terminate ();
@@ -983,11 +1008,30 @@ namespace transport
}
}
void NTCPServer::Ban (const boost::asio::ip::address& addr)
void NTCPServer::ScheduleTermination ()
{
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
m_BanList[addr] = ts + NTCP_BAN_EXPIRATION_TIMEOUT;
LogPrint (eLogWarning, "NTCP: ", addr, " has been banned for ", NTCP_BAN_EXPIRATION_TIMEOUT, " seconds");
m_TerminationTimer.expires_from_now (boost::posix_time::seconds(NTCP_TERMINATION_CHECK_TIMEOUT));
m_TerminationTimer.async_wait (std::bind (&NTCPServer::HandleTerminationTimer,
this, std::placeholders::_1));
}
void NTCPServer::HandleTerminationTimer (const boost::system::error_code& ecode)
{
if (ecode != boost::asio::error::operation_aborted)
{
auto ts = i2p::util::GetSecondsSinceEpoch ();
for (auto& it: m_NTCPSessions)
if (it.second->IsTerminationTimeoutExpired (ts))
{
auto session = it.second;
m_Service.post ([session]
{
LogPrint (eLogDebug, "NTCP: No activity for ", session->GetTerminationTimeout (), " seconds");
session->Terminate ();
});
}
ScheduleTermination ();
}
}
}
}

View File

@@ -35,10 +35,11 @@ namespace transport
};
const size_t NTCP_MAX_MESSAGE_SIZE = 16384;
const size_t NTCP_BUFFER_SIZE = 4160; // fits 4 tunnel messages (4*1028)
const size_t NTCP_BUFFER_SIZE = 1028; // fits 1 tunnel data message
const int NTCP_CONNECT_TIMEOUT = 5; // 5 seconds
const int NTCP_TERMINATION_TIMEOUT = 120; // 2 minutes
const int NTCP_TERMINATION_CHECK_TIMEOUT = 30; // 30 seconds
const size_t NTCP_DEFAULT_PHASE3_SIZE = 2/*size*/ + i2p::data::DEFAULT_IDENTITY_SIZE/*387*/ + 4/*ts*/ + 15/*padding*/ + 40/*signature*/; // 448
const int NTCP_BAN_EXPIRATION_TIMEOUT = 70; // in second
const int NTCP_CLOCK_SKEW = 60; // in seconds
const int NTCP_MAX_OUTGOING_QUEUE_SIZE = 200; // how many messages we can queue up
@@ -53,8 +54,8 @@ namespace transport
void Done ();
boost::asio::ip::tcp::socket& GetSocket () { return m_Socket; };
bool IsEstablished () const { return m_IsEstablished; };
bool IsEstablished () const { return m_IsEstablished; };
void ClientLogin ();
void ServerLogin ();
void SendI2NPMessages (const std::vector<std::shared_ptr<I2NPMessage> >& msgs);
@@ -66,12 +67,13 @@ namespace transport
void SendTimeSyncMessage ();
void SetIsEstablished (bool isEstablished) { m_IsEstablished = isEstablished; }
void CreateAESKey (uint8_t * pubKey, i2p::crypto::AESKey& key);
void CreateAESKey (uint8_t * pubKey);
// client
void SendPhase3 ();
void HandlePhase1Sent (const boost::system::error_code& ecode, std::size_t bytes_transferred);
void HandlePhase2Received (const boost::system::error_code& ecode, std::size_t bytes_transferred);
void HandlePhase2 ();
void HandlePhase3Sent (const boost::system::error_code& ecode, std::size_t bytes_transferred, uint32_t tsA);
void HandlePhase4Received (const boost::system::error_code& ecode, std::size_t bytes_transferred, uint32_t tsA);
@@ -95,16 +97,10 @@ namespace transport
void Send (const std::vector<std::shared_ptr<I2NPMessage> >& msgs);
void HandleSent (const boost::system::error_code& ecode, std::size_t bytes_transferred, std::vector<std::shared_ptr<I2NPMessage> > msgs);
// timer
void ScheduleTermination ();
void HandleTerminationTimer (const boost::system::error_code& ecode);
private:
NTCPServer& m_Server;
boost::asio::ip::tcp::socket m_Socket;
boost::asio::deadline_timer m_TerminationTimer;
bool m_IsEstablished, m_IsTerminated;
i2p::crypto::CBCDecryption m_Decryption;
@@ -126,8 +122,6 @@ namespace transport
bool m_IsSending;
std::vector<std::shared_ptr<I2NPMessage> > m_SendQueue;
boost::asio::ip::address m_ConnectedFrom; // for ban
};
// TODO: move to NTCP.h/.cpp
@@ -146,11 +140,10 @@ namespace transport
std::shared_ptr<NTCPSession> FindNTCPSession (const i2p::data::IdentHash& ident);
void Connect (const boost::asio::ip::address& address, int port, std::shared_ptr<NTCPSession> conn);
bool IsBoundV4() const { return m_NTCPAcceptor != nullptr; };
bool IsBoundV6() const { return m_NTCPV6Acceptor != nullptr; };
bool IsBoundV4() const { return m_NTCPAcceptor != nullptr; };
bool IsBoundV6() const { return m_NTCPV6Acceptor != nullptr; };
boost::asio::io_service& GetService () { return m_Service; };
void Ban (const boost::asio::ip::address& addr);
boost::asio::io_service& GetService () { return m_Service; };
private:
@@ -158,7 +151,11 @@ namespace transport
void HandleAccept (std::shared_ptr<NTCPSession> conn, const boost::system::error_code& error);
void HandleAcceptV6 (std::shared_ptr<NTCPSession> conn, const boost::system::error_code& error);
void HandleConnect (const boost::system::error_code& ecode, std::shared_ptr<NTCPSession> conn);
void HandleConnect (const boost::system::error_code& ecode, std::shared_ptr<NTCPSession> conn, std::shared_ptr<boost::asio::deadline_timer> timer);
// timer
void ScheduleTermination ();
void HandleTerminationTimer (const boost::system::error_code& ecode);
private:
@@ -166,9 +163,9 @@ namespace transport
std::thread * m_Thread;
boost::asio::io_service m_Service;
boost::asio::io_service::work m_Work;
boost::asio::deadline_timer m_TerminationTimer;
boost::asio::ip::tcp::acceptor * m_NTCPAcceptor, * m_NTCPV6Acceptor;
std::map<i2p::data::IdentHash, std::shared_ptr<NTCPSession> > m_NTCPSessions; // access from m_Thread only
std::map<boost::asio::ip::address, uint32_t> m_BanList; // IP -> ban expiration time in seconds
public:

261
NetDb.cpp
View File

@@ -14,6 +14,7 @@
#include "RouterContext.h"
#include "Garlic.h"
#include "NetDb.h"
#include "Config.h"
using namespace i2p::transport;
@@ -23,7 +24,7 @@ namespace data
{
NetDb netdb;
NetDb::NetDb (): m_IsRunning (false), m_Thread (nullptr), m_Reseeder (nullptr), m_Storage("netDb", "r", "routerInfo-", "dat"), m_HiddenMode(false)
NetDb::NetDb (): m_IsRunning (false), m_Thread (nullptr), m_Reseeder (nullptr), m_Storage("netDb", "r", "routerInfo-", "dat"), m_FloodfillBootstrap(nullptr), m_HiddenMode(false)
{
}
@@ -34,13 +35,15 @@ namespace data
}
void NetDb::Start ()
{
{
m_Storage.SetPlace(i2p::fs::GetDataDir());
m_Storage.Init(i2p::data::GetBase64SubstitutionTable(), 64);
InitProfilesStorage ();
m_Families.LoadCertificates ();
m_Families.LoadCertificates ();
Load ();
if (m_RouterInfos.size () < 25) // reseed if # of router less than 50
uint16_t threshold; i2p::config::GetOption("reseed.threshold", threshold);
if (m_RouterInfos.size () < threshold) // reseed if # of router less than threshold
Reseed ();
m_IsRunning = true;
@@ -68,7 +71,7 @@ namespace data
m_Requests.Stop ();
}
}
void NetDb::Run ()
{
uint32_t lastSave = 0, lastPublish = 0, lastExploratory = 0, lastManageRequest = 0, lastDestinationCleanup = 0;
@@ -117,7 +120,6 @@ namespace data
{
SaveUpdated ();
ManageLeaseSets ();
ManageLookupResponses ();
}
lastSave = ts;
}
@@ -126,10 +128,8 @@ namespace data
i2p::context.CleanupDestination ();
lastDestinationCleanup = ts;
}
// if we're in hidden mode don't publish or explore
// if (m_HiddenMode) continue;
if (ts - lastPublish >= NETDB_PUBLISH_INTERVAL) // publish
if (ts - lastPublish >= NETDB_PUBLISH_INTERVAL && !m_HiddenMode) // publish
{
Publish ();
lastPublish = ts;
@@ -142,13 +142,16 @@ namespace data
LogPrint(eLogError, "NetDb: no known routers, reseed seems to be totally failed");
break;
}
else // we have peers now
m_FloodfillBootstrap = nullptr;
if (numRouters < 2500 || ts - lastExploratory >= 90)
{
numRouters = 800/numRouters;
if (numRouters < 1) numRouters = 1;
if (numRouters > 9) numRouters = 9;
m_Requests.ManageRequests ();
Explore (numRouters);
m_Requests.ManageRequests ();
if(!m_HiddenMode)
Explore (numRouters);
lastExploratory = ts;
}
}
@@ -186,23 +189,34 @@ namespace data
// TODO: check if floodfill has been changed
}
else
{
LogPrint (eLogDebug, "NetDb: RouterInfo is older: ", ident.ToBase64());
updated = false;
}
}
else
{
r = std::make_shared<RouterInfo> (buf, len);
if (!r->IsUnreachable ())
{
LogPrint (eLogInfo, "NetDb: RouterInfo added: ", ident.ToBase64());
bool inserted = false;
{
std::unique_lock<std::mutex> l(m_RouterInfosMutex);
m_RouterInfos[r->GetIdentHash ()] = r;
inserted = m_RouterInfos.insert ({r->GetIdentHash (), r}).second;
}
if (r->IsFloodfill () && r->IsReachable ()) // floodfill must be reachable
if (inserted)
{
std::unique_lock<std::mutex> l(m_FloodfillsMutex);
m_Floodfills.push_back (r);
LogPrint (eLogInfo, "NetDb: RouterInfo added: ", ident.ToBase64());
if (r->IsFloodfill () && r->IsReachable ()) // floodfill must be reachable
{
std::unique_lock<std::mutex> l(m_FloodfillsMutex);
m_Floodfills.push_back (r);
}
}
else
{
LogPrint (eLogWarning, "NetDb: Duplicated RouterInfo ", ident.ToBase64());
updated = false;
}
}
else
@@ -296,11 +310,56 @@ namespace data
m_Reseeder = new Reseeder ();
m_Reseeder->LoadCertificates (); // we need certificates for SU3 verification
}
int reseedRetries = 0;
while (reseedRetries < 10 && !m_Reseeder->ReseedNowSU3 ())
reseedRetries++;
if (reseedRetries >= 10)
LogPrint (eLogWarning, "NetDb: failed to reseed after 10 attempts");
// try reseeding from floodfill first if specified
std::string riPath;
if(i2p::config::GetOption("reseed.floodfill", riPath)) {
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 boostrap from floodfill
return;
}
}
m_Reseeder->Bootstrap ();
}
void NetDb::ReseedFromFloodfill(const RouterInfo & ri, int numRouters, int numFloodfills)
{
LogPrint(eLogInfo, "NetDB: reseeding from floodfill ", ri.GetIdentHashBase64());
std::vector<std::shared_ptr<i2p::I2NPMessage> > requests;
i2p::data::IdentHash ourIdent = i2p::context.GetIdentHash();
i2p::data::IdentHash ih = ri.GetIdentHash();
i2p::data::IdentHash randomIdent;
// make floodfill lookups
while(numFloodfills > 0) {
randomIdent.Randomize();
auto msg = i2p::CreateRouterInfoDatabaseLookupMsg(randomIdent, ourIdent, 0, false);
requests.push_back(msg);
numFloodfills --;
}
// make regular router lookups
while(numRouters > 0) {
randomIdent.Randomize();
auto msg = i2p::CreateRouterInfoDatabaseLookupMsg(randomIdent, ourIdent, 0, true);
requests.push_back(msg);
numRouters --;
}
// send them off
i2p::transport::transports.SendMessages(ih, requests);
}
bool NetDb::LoadRouterInfo (const std::string & path)
@@ -329,6 +388,67 @@ namespace data
for ( auto & entry : m_LeaseSets)
v(entry.first, entry.second);
}
void NetDb::VisitStoredRouterInfos(RouterInfoVisitor v)
{
m_Storage.Iterate([v] (const std::string & filename) {
auto ri = std::make_shared<i2p::data::RouterInfo>(filename);
v(ri);
});
}
void NetDb::VisitRouterInfos(RouterInfoVisitor v)
{
std::unique_lock<std::mutex> lock(m_RouterInfosMutex);
for ( const auto & item : m_RouterInfos )
v(item.second);
}
size_t NetDb::VisitRandomRouterInfos(RouterInfoFilter filter, RouterInfoVisitor v, size_t n)
{
std::vector<std::shared_ptr<const RouterInfo> > found;
const size_t max_iters_per_cyle = 3;
size_t iters = max_iters_per_cyle;
while(n > 0)
{
std::unique_lock<std::mutex> lock(m_RouterInfosMutex);
uint32_t idx = rand () % m_RouterInfos.size ();
uint32_t i = 0;
for (const auto & it : m_RouterInfos) {
if(i >= idx) // are we at the random start point?
{
// yes, check if we want this one
if(filter(it.second))
{
// we have a match
--n;
found.push_back(it.second);
// reset max iterations per cycle
iters = max_iters_per_cyle;
break;
}
}
else // not there yet
++i;
}
// we have enough
if(n == 0) break;
--iters;
// have we tried enough this cycle ?
if(!iters) {
// yes let's try the next cycle
--n;
iters = max_iters_per_cyle;
}
}
// visit the ones we found
size_t visited = 0;
for(const auto & ri : found ) {
v(ri);
++visited;
}
return visited;
}
void NetDb::Load ()
{
@@ -438,6 +558,21 @@ namespace data
m_Requests.RequestComplete (destination, nullptr);
}
}
void NetDb::RequestDestinationFrom (const IdentHash& destination, const IdentHash & from, bool exploritory, RequestedDestination::RequestComplete requestComplete)
{
auto dest = m_Requests.CreateRequest (destination, exploritory, requestComplete); // non-exploratory
if (!dest)
{
LogPrint (eLogWarning, "NetDb: destination ", destination.ToBase64(), " is requested already");
return;
}
LogPrint(eLogInfo, "NetDb: destination ", destination.ToBase64(), " being requested directly from ", from.ToBase64());
// direct
transports.SendMessage (from, dest->CreateRequestMessage (nullptr, nullptr));
}
void NetDb::HandleDatabaseStoreMsg (std::shared_ptr<const I2NPMessage> m)
{
@@ -446,7 +581,7 @@ namespace data
IdentHash ident (buf + DATABASE_STORE_KEY_OFFSET);
if (ident.IsZero ())
{
LogPrint (eLogError, "NetDb: database store with zero ident, dropped");
LogPrint (eLogDebug, "NetDb: database store with zero ident, dropped");
return;
}
uint32_t replyToken = bufbe32toh (buf + DATABASE_STORE_REPLY_TOKEN_OFFSET);
@@ -465,14 +600,14 @@ namespace data
if (outbound)
outbound->SendTunnelDataMsg (buf + offset, tunnelID, deliveryStatus);
else
LogPrint (eLogError, "NetDb: no outbound tunnels for DatabaseStore reply found");
LogPrint (eLogWarning, "NetDb: no outbound tunnels for DatabaseStore reply found");
}
offset += 32;
}
// we must send reply back before this check
if (ident == i2p::context.GetIdentHash ())
{
LogPrint (eLogError, "NetDb: database store with own RouterInfo received, dropped");
LogPrint (eLogDebug, "NetDb: database store with own RouterInfo received, dropped");
return;
}
size_t payloadOffset = offset;
@@ -495,8 +630,13 @@ namespace data
}
uint8_t uncompressed[2048];
size_t uncompressedSize = m_Inflator.Inflate (buf + offset, size, uncompressed, 2048);
if (uncompressedSize)
if (uncompressedSize && uncompressedSize < 2048)
updated = AddRouterInfo (ident, uncompressed, uncompressedSize);
else
{
LogPrint (eLogInfo, "NetDb: decompression failed ", uncompressedSize);
return;
}
}
if (replyToken && context.IsFloodfill () && updated)
@@ -555,7 +695,7 @@ namespace data
if (!dest->IsExploratory ())
{
// reply to our destination. Try other floodfills
if (outbound && inbound )
if (outbound && inbound)
{
std::vector<i2p::tunnel::TunnelMessageBlock> msgs;
auto count = dest->GetExcludedPeers ().size ();
@@ -599,7 +739,7 @@ namespace data
// no more requests for detination possible. delete it
m_Requests.RequestComplete (ident, nullptr);
}
else
else if(!m_FloodfillBootstrap)
LogPrint (eLogWarning, "NetDb: requested destination for ", key, " not found");
// try responses
@@ -616,7 +756,10 @@ namespace data
{
// router with ident not found or too old (1 hour)
LogPrint (eLogDebug, "NetDb: found new/outdated router. Requesting RouterInfo ...");
RequestDestination (router);
if(m_FloodfillBootstrap)
RequestDestinationFrom(router, m_FloodfillBootstrap->GetIdentHash(), true);
else
RequestDestination (router);
}
else
LogPrint (eLogDebug, "NetDb: [:|||:]");
@@ -711,33 +854,17 @@ namespace data
}
if (!replyMsg)
{
LogPrint (eLogWarning, "NetDb: Requested ", key, " not found, ", numExcluded, " peers excluded");
// find or cleate response
std::vector<IdentHash> closestFloodfills;
bool found = false;
if (!numExcluded)
{
auto it = m_LookupResponses.find (ident);
if (it != m_LookupResponses.end ())
{
closestFloodfills = it->second.first;
found = true;
}
}
if (!found)
{
std::set<IdentHash> excludedRouters;
const uint8_t * exclude_ident = excluded;
for (int i = 0; i < numExcluded; i++)
{
excludedRouters.insert (exclude_ident);
exclude_ident += 32;
}
closestFloodfills = GetClosestFloodfills (ident, 3, excludedRouters, true);
if (!numExcluded) // save if no excluded
m_LookupResponses[ident] = std::make_pair(closestFloodfills, i2p::util::GetSecondsSinceEpoch ());
{
std::set<IdentHash> excludedRouters;
const uint8_t * exclude_ident = excluded;
for (int i = 0; i < numExcluded; i++)
{
excludedRouters.insert (exclude_ident);
exclude_ident += 32;
}
auto closestFloodfills = GetClosestFloodfills (ident, 3, excludedRouters, true);
if (closestFloodfills.empty ())
LogPrint (eLogWarning, "NetDb: Requested ", key, " not found, ", numExcluded, " peers excluded");
replyMsg = CreateDatabaseSearchReply (ident, closestFloodfills);
}
}
@@ -783,7 +910,6 @@ namespace data
uint8_t randomHash[32];
std::vector<i2p::tunnel::TunnelMessageBlock> msgs;
std::set<const RouterInfo *> floodfills;
LogPrint (eLogInfo, "NetDb: exploring new ", numDestinations, " routers ...");
for (int i = 0; i < numDestinations; i++)
{
@@ -795,9 +921,8 @@ namespace data
return;
}
auto floodfill = GetClosestFloodfill (randomHash, dest->GetExcludedPeers ());
if (floodfill && !floodfills.count (floodfill.get ())) // request floodfill only once
if (floodfill)
{
floodfills.insert (floodfill.get ());
if (i2p::transport::transports.IsConnected (floodfill->GetIdentHash ()))
throughTunnels = false;
if (throughTunnels)
@@ -862,12 +987,12 @@ namespace data
});
}
std::shared_ptr<const RouterInfo> NetDb::GetRandomPeerTestRouter () const
std::shared_ptr<const RouterInfo> NetDb::GetRandomPeerTestRouter (bool v4only) const
{
return GetRandomRouter (
[](std::shared_ptr<const RouterInfo> router)->bool
[v4only](std::shared_ptr<const RouterInfo> router)->bool
{
return !router->IsHidden () && router->IsPeerTesting ();
return !router->IsHidden () && router->IsPeerTesting () && router->IsSSU (v4only);
});
}
@@ -1045,17 +1170,5 @@ namespace data
++it;
}
}
void NetDb::ManageLookupResponses ()
{
auto ts = i2p::util::GetSecondsSinceEpoch ();
for (auto it = m_LookupResponses.begin (); it != m_LookupResponses.end ();)
{
if (ts > it->second.second + 180) // 3 minutes
it = m_LookupResponses.erase (it);
else
++it;
}
}
}
}

32
NetDb.h
View File

@@ -35,7 +35,13 @@ namespace data
/** function for visiting a leaseset stored in a floodfill */
typedef std::function<void(const IdentHash, std::shared_ptr<LeaseSet>)> LeaseSetVisitor;
/** function for visiting a router info we have locally */
typedef std::function<void(std::shared_ptr<const i2p::data::RouterInfo>)> RouterInfoVisitor;
/** function for visiting a router info and determining if we want to use it */
typedef std::function<bool(std::shared_ptr<const i2p::data::RouterInfo>)> RouterInfoFilter;
class NetDb
{
public:
@@ -45,7 +51,7 @@ namespace data
void Start ();
void Stop ();
bool AddRouterInfo (const uint8_t * buf, int len);
bool AddRouterInfo (const IdentHash& ident, const uint8_t * buf, int len);
bool AddLeaseSet (const IdentHash& ident, const uint8_t * buf, int len, std::shared_ptr<i2p::tunnel::InboundTunnel> from);
@@ -54,6 +60,7 @@ namespace data
std::shared_ptr<RouterProfile> FindRouterProfile (const IdentHash& ident) const;
void RequestDestination (const IdentHash& destination, RequestedDestination::RequestComplete requestComplete = nullptr);
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);
@@ -62,7 +69,7 @@ namespace data
std::shared_ptr<const RouterInfo> GetRandomRouter () const;
std::shared_ptr<const RouterInfo> GetRandomRouter (std::shared_ptr<const RouterInfo> compatibleWith) const;
std::shared_ptr<const RouterInfo> GetHighBandwidthRandomRouter (std::shared_ptr<const RouterInfo> compatibleWith) const;
std::shared_ptr<const RouterInfo> GetRandomPeerTestRouter () const;
std::shared_ptr<const RouterInfo> GetRandomPeerTestRouter (bool v4only = true) const;
std::shared_ptr<const RouterInfo> GetRandomIntroducer () const;
std::shared_ptr<const RouterInfo> GetClosestFloodfill (const IdentHash& destination, const std::set<IdentHash>& excluded, bool closeThanUsOnly = false) const;
std::vector<IdentHash> GetClosestFloodfills (const IdentHash& destination, size_t num,
@@ -86,7 +93,15 @@ namespace data
/** visit all lease sets we currently store */
void VisitLeaseSets(LeaseSetVisitor v);
/** visit all router infos we have currently on disk, usually insanely expensive, does not access in memory RI */
void VisitStoredRouterInfos(RouterInfoVisitor v);
/** visit all router infos we have loaded in memory, cheaper than VisitLocalRouterInfos but locks access while visiting */
void VisitRouterInfos(RouterInfoVisitor v);
/** visit N random router that match using filter, then visit them with a visitor, return number of RouterInfos that were visited */
size_t VisitRandomRouterInfos(RouterInfoFilter f, RouterInfoVisitor v, size_t n);
void ClearRouterInfos () { m_RouterInfos.clear (); };
private:
void Load ();
@@ -97,13 +112,14 @@ namespace data
void Publish ();
void ManageLeaseSets ();
void ManageRequests ();
void ManageLookupResponses ();
void ReseedFromFloodfill(const RouterInfo & ri, int numRouters=40, int numFloodfills=20);
template<typename Filter>
std::shared_ptr<const RouterInfo> GetRandomRouter (Filter filter) const;
private:
mutable std::mutex m_LeaseSetsMutex;
std::map<IdentHash, std::shared_ptr<LeaseSet> > m_LeaseSets;
mutable std::mutex m_RouterInfosMutex;
@@ -124,7 +140,9 @@ namespace data
friend class NetDbRequests;
NetDbRequests m_Requests;
std::map<IdentHash, std::pair<std::vector<IdentHash>, uint64_t> > m_LookupResponses; // ident->(closest FFs, timestamp)
/** router info we are bootstrapping from or nullptr if we are not currently doing that*/
std::shared_ptr<RouterInfo> m_FloodfillBootstrap;
/** true if in hidden mode */
bool m_HiddenMode;

View File

@@ -11,10 +11,15 @@ namespace data
std::shared_ptr<I2NPMessage> RequestedDestination::CreateRequestMessage (std::shared_ptr<const RouterInfo> router,
std::shared_ptr<const i2p::tunnel::InboundTunnel> replyTunnel)
{
auto msg = i2p::CreateRouterInfoDatabaseLookupMsg (m_Destination,
std::shared_ptr<I2NPMessage> msg;
if(replyTunnel)
msg = i2p::CreateRouterInfoDatabaseLookupMsg (m_Destination,
replyTunnel->GetNextIdentHash (), replyTunnel->GetNextTunnelID (), m_IsExploratory,
&m_ExcludedPeers);
m_ExcludedPeers.insert (router->GetIdentHash ());
else
msg = i2p::CreateRouterInfoDatabaseLookupMsg(m_Destination, i2p::context.GetIdentHash(), 0, m_IsExploratory, &m_ExcludedPeers);
if(router)
m_ExcludedPeers.insert (router->GetIdentHash ());
m_CreationTime = i2p::util::GetSecondsSinceEpoch ();
return msg;
}
@@ -68,8 +73,7 @@ namespace data
dest->SetRequestComplete (requestComplete);
{
std::unique_lock<std::mutex> l(m_RequestedDestinationsMutex);
if (!m_RequestedDestinations.insert (std::make_pair (destination,
std::shared_ptr<RequestedDestination> (dest))).second) // not inserted
if (!m_RequestedDestinations.insert (std::make_pair (destination, dest)).second) // not inserted
return nullptr;
}
return dest;
@@ -77,20 +81,28 @@ namespace data
void NetDbRequests::RequestComplete (const IdentHash& ident, std::shared_ptr<RouterInfo> r)
{
auto it = m_RequestedDestinations.find (ident);
if (it != m_RequestedDestinations.end ())
{
if (r)
it->second->Success (r);
else
it->second->Fail ();
std::shared_ptr<RequestedDestination> request;
{
std::unique_lock<std::mutex> l(m_RequestedDestinationsMutex);
m_RequestedDestinations.erase (it);
auto it = m_RequestedDestinations.find (ident);
if (it != m_RequestedDestinations.end ())
{
request = it->second;
m_RequestedDestinations.erase (it);
}
}
if (request)
{
if (r)
request->Success (r);
else
request->Fail ();
}
}
std::shared_ptr<RequestedDestination> NetDbRequests::FindRequest (const IdentHash& ident) const
{
std::unique_lock<std::mutex> l(m_RequestedDestinationsMutex);
auto it = m_RequestedDestinations.find (ident);
if (it != m_RequestedDestinations.end ())
return it->second;

View File

@@ -59,7 +59,7 @@ namespace data
private:
std::mutex m_RequestedDestinationsMutex;
mutable std::mutex m_RequestedDestinationsMutex;
std::map<IdentHash, std::shared_ptr<RequestedDestination> > m_RequestedDestinations;
};
}

View File

@@ -12,8 +12,8 @@ namespace data
{
i2p::fs::HashedStorage m_ProfilesStorage("peerProfiles", "p", "profile-", "txt");
RouterProfile::RouterProfile (const IdentHash& identHash):
m_IdentHash (identHash), m_LastUpdateTime (boost::posix_time::second_clock::local_time()),
RouterProfile::RouterProfile ():
m_LastUpdateTime (boost::posix_time::second_clock::local_time()),
m_NumTunnelsAgreed (0), m_NumTunnelsDeclined (0), m_NumTunnelsNonReplied (0),
m_NumTimesTaken (0), m_NumTimesRejected (0)
{
@@ -29,7 +29,7 @@ namespace data
m_LastUpdateTime = GetTime ();
}
void RouterProfile::Save ()
void RouterProfile::Save (const IdentHash& identHash)
{
// fill sections
boost::property_tree::ptree participation;
@@ -46,7 +46,7 @@ namespace data
pt.put_child (PEER_PROFILE_SECTION_USAGE, usage);
// save to file
std::string ident = m_IdentHash.ToBase64 ();
std::string ident = identHash.ToBase64 ();
std::string path = m_ProfilesStorage.Path(ident);
try {
@@ -57,51 +57,64 @@ namespace data
}
}
void RouterProfile::Load ()
void RouterProfile::Load (const IdentHash& identHash)
{
std::string ident = m_IdentHash.ToBase64 ();
std::string ident = identHash.ToBase64 ();
std::string path = m_ProfilesStorage.Path(ident);
boost::property_tree::ptree pt;
if (!i2p::fs::Exists(path)) {
if (!i2p::fs::Exists(path))
{
LogPrint(eLogWarning, "Profiling: no profile yet for ", ident);
return;
}
try {
try
{
boost::property_tree::read_ini (path, pt);
} catch (std::exception& ex) {
} catch (std::exception& ex)
{
/* boost exception verbose enough */
LogPrint (eLogError, "Profiling: ", ex.what ());
return;
}
try {
try
{
auto t = pt.get (PEER_PROFILE_LAST_UPDATE_TIME, "");
if (t.length () > 0)
m_LastUpdateTime = boost::posix_time::time_from_string (t);
if ((GetTime () - m_LastUpdateTime).hours () < PEER_PROFILE_EXPIRATION_TIMEOUT) {
try {
if ((GetTime () - m_LastUpdateTime).hours () < PEER_PROFILE_EXPIRATION_TIMEOUT)
{
try
{
// read participations
auto participations = pt.get_child (PEER_PROFILE_SECTION_PARTICIPATION);
m_NumTunnelsAgreed = participations.get (PEER_PROFILE_PARTICIPATION_AGREED, 0);
m_NumTunnelsDeclined = participations.get (PEER_PROFILE_PARTICIPATION_DECLINED, 0);
m_NumTunnelsNonReplied = participations.get (PEER_PROFILE_PARTICIPATION_NON_REPLIED, 0);
} catch (boost::property_tree::ptree_bad_path& ex) {
}
catch (boost::property_tree::ptree_bad_path& ex)
{
LogPrint (eLogWarning, "Profiling: Missing section ", PEER_PROFILE_SECTION_PARTICIPATION, " in profile for ", ident);
}
try {
try
{
// read usage
auto usage = pt.get_child (PEER_PROFILE_SECTION_USAGE);
m_NumTimesTaken = usage.get (PEER_PROFILE_USAGE_TAKEN, 0);
m_NumTimesRejected = usage.get (PEER_PROFILE_USAGE_REJECTED, 0);
} catch (boost::property_tree::ptree_bad_path& ex) {
}
catch (boost::property_tree::ptree_bad_path& ex)
{
LogPrint (eLogWarning, "Missing section ", PEER_PROFILE_SECTION_USAGE, " in profile for ", ident);
}
} else {
*this = RouterProfile (m_IdentHash);
}
} catch (std::exception& ex) {
}
else
*this = RouterProfile ();
}
catch (std::exception& ex)
{
LogPrint (eLogError, "Profiling: Can't read profile ", ident, " :", ex.what ());
}
}
@@ -149,8 +162,8 @@ namespace data
std::shared_ptr<RouterProfile> GetRouterProfile (const IdentHash& identHash)
{
auto profile = std::make_shared<RouterProfile> (identHash);
profile->Load (); // if possible
auto profile = std::make_shared<RouterProfile> ();
profile->Load (identHash); // if possible
return profile;
}

View File

@@ -26,11 +26,11 @@ namespace data
{
public:
RouterProfile (const IdentHash& identHash);
RouterProfile ();
RouterProfile& operator= (const RouterProfile& ) = default;
void Save ();
void Load ();
void Save (const IdentHash& identHash);
void Load (const IdentHash& identHash);
bool IsBad ();
@@ -48,7 +48,6 @@ namespace data
private:
IdentHash m_IdentHash;
boost::posix_time::ptime m_LastUpdateTime;
// participation
uint32_t m_NumTunnelsAgreed;

View File

@@ -25,7 +25,8 @@ namespace util
m_NonEmpty.notify_one ();
}
void Put (const std::vector<Element>& vec)
template<template<typename, typename...>class Container, typename... R>
void Put (const Container<Element, R...>& vec)
{
if (!vec.empty ())
{

View File

@@ -1,21 +1,28 @@
i2pd
====
i2pd is a full-featured C++ implementation of
[I2P](https://geti2p.net/en/about/intro) client.
[Русская версия](https://github.com/PurpleI2P/i2pd_docs_ru/blob/master/README.md)
I2P (Invisible Internet Project) is anonymous network which works on top of
public Internet. Privacy and anonymity are achieved by strong encryption and
bouncing your traffic through thousands of I2P nodes all around the world.
i2pd (I2P Daemon) is a full-featured C++ implementation of I2P client.
We are building network which helps people to communicate and share information
I2P (Invisible Internet Protocol) is a universal anonymous network layer.
All communications over I2P are anonymous and end-to-end encrypted, participants
don't reveal their real IP addresses.
I2P client is a software used for building and using anonymous I2P
networks. Such networks are commonly used for anonymous peer-to-peer
applications (filesharing, cryptocurrencies) and anonymous client-server
applications (websites, instant messengers, chat-servers).
I2P allows people from all around the world to communicate and share information
without restrictions.
* [Website](http://i2pd.website)
* [Documentation](https://i2pd.readthedocs.io/en/latest/)
* [Wiki](https://github.com/PurpleI2P/i2pd/wiki)
* [Tickets/Issues](https://github.com/PurpleI2P/i2pd/issues)
* [Twitter](https://twitter.com/i2porignal)
* [Specifications](https://geti2p.net/spec)
* [Twitter](https://twitter.com/hashtag/i2pd)
Installing
----------
@@ -32,11 +39,12 @@ i2pd from source on your OS.
* Mac OS X
* FreeBSD
* Android
* iOS
Using i2pd
----------
See [documentation](https://i2pd.readthedocs.io/en/latest/) and
See [documentation](https://i2pd.readthedocs.io/en/latest/usage.html) and
[example config file](https://github.com/PurpleI2P/i2pd/blob/openssl/docs/i2pd.conf).
Donations

View File

@@ -3,6 +3,7 @@
#include <sstream>
#include <boost/asio.hpp>
#include <boost/asio/ssl.hpp>
#include <boost/algorithm/string.hpp>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <zlib.h>
@@ -22,25 +23,6 @@ namespace i2p
{
namespace data
{
static std::vector<std::string> httpsReseedHostList =
{
#ifdef MESHNET
// meshnet i2p reseeds
"https://reseed.i2p.rocks:8443/"
#else
// mainline i2p reseeds
"https://reseed.i2p-projekt.de/", // Only HTTPS
"https://i2p.mooo.com/netDb/",
"https://netdb.i2p2.no/", // Only SU3 (v3) support, SNI required
"https://us.reseed.i2p2.no:444/",
"https://uk.reseed.i2p2.no:444/",
"https://i2p.manas.ca:8443/",
"https://i2p-0.manas.ca:8443/",
"https://reseed.i2p.vzaws.com:8443/", // Only SU3 (v3) support
"https://user.mx24.eu/", // Only HTTPS and SU3 (v3) support
"https://download.xxlspeed.com/" // Only HTTPS and SU3 (v3) support
#endif
};
Reseeder::Reseeder()
{
@@ -50,24 +32,76 @@ namespace data
{
}
int Reseeder::ReseedNowSU3 ()
/** @brief tries to bootstrap into I2P network (from local files and servers, with respect of options)
*/
void Reseeder::Bootstrap ()
{
std::string su3FileName; i2p::config::GetOption("reseed.file", su3FileName);
std::string zipFileName; i2p::config::GetOption("reseed.zipfile", zipFileName);
if (su3FileName.length() > 0) // bootstrap from SU3 file or URL
{
int num;
if (su3FileName.length() > 8 && su3FileName.substr(0, 8) == "https://")
{
num = ReseedFromSU3Url (su3FileName); // from https URL
}
else
{
num = ProcessSU3File (su3FileName.c_str ());
}
if (num == 0)
LogPrint (eLogWarning, "Reseed: failed to reseed from ", su3FileName);
}
else if (zipFileName.length() > 0) // bootstrap from ZIP file
{
int num = ProcessZIPFile (zipFileName.c_str ());
if (num == 0)
LogPrint (eLogWarning, "Reseed: failed to reseed from ", zipFileName);
}
else // bootstrap from reseed servers
{
int num = ReseedFromServers ();
if (num == 0)
LogPrint (eLogWarning, "Reseed: failed to reseed from servers");
}
}
/** @brief bootstrap from random server, retry 10 times
* @return number of entries added to netDb
*/
int Reseeder::ReseedFromServers ()
{
std::string filename; i2p::config::GetOption("reseed.file", filename);
if (filename.length() > 0) // reseed file is specified
{
auto num = ProcessSU3File (filename.c_str ());
if (num > 0) return num; // success
LogPrint (eLogWarning, "Can't reseed from ", filename, " . Trying from hosts");
}
auto ind = rand () % httpsReseedHostList.size ();
std::string& reseedHost = httpsReseedHostList[ind];
return ReseedFromSU3 (reseedHost);
std::string reseedURLs; i2p::config::GetOption("reseed.urls", reseedURLs);
std::vector<std::string> httpsReseedHostList;
boost::split(httpsReseedHostList, reseedURLs, boost::is_any_of(","), boost::token_compress_on);
if (reseedURLs.length () == 0)
{
LogPrint (eLogWarning, "Reseed: No reseed servers specified");
return 0;
}
int reseedRetries = 0;
while (reseedRetries < 10)
{
auto ind = rand () % httpsReseedHostList.size ();
std::string reseedUrl = httpsReseedHostList[ind] + "i2pseeds.su3";
auto num = ReseedFromSU3Url (reseedUrl);
if (num > 0) return num; // success
reseedRetries++;
}
LogPrint (eLogWarning, "Reseed: failed to reseed from servers after 10 attempts");
return 0;
}
int Reseeder::ReseedFromSU3 (const std::string& host)
/** @brief bootstrap from HTTPS URL with SU3 file
* @param url
* @return number of entries added to netDb
*/
int Reseeder::ReseedFromSU3Url (const std::string& url)
{
std::string url = host + "i2pseeds.su3";
LogPrint (eLogInfo, "Reseed: Downloading SU3 from ", host);
LogPrint (eLogInfo, "Reseed: Downloading SU3 from ", url);
std::string su3 = HttpsRequest (url);
if (su3.length () > 0)
{
@@ -93,10 +127,24 @@ namespace data
}
}
int Reseeder::ProcessZIPFile (const char * filename)
{
std::ifstream s(filename, std::ifstream::binary);
if (s.is_open ())
{
s.seekg (0, std::ios::end);
auto len = s.tellg ();
s.seekg (0, std::ios::beg);
return ProcessZIPStream (s, len);
}
else
{
LogPrint (eLogError, "Reseed: Can't open file ", filename);
return 0;
}
}
const char SU3_MAGIC_NUMBER[]="I2Psu3";
const uint32_t ZIP_HEADER_SIGNATURE = 0x04034B50;
const uint32_t ZIP_CENTRAL_DIRECTORY_HEADER_SIGNATURE = 0x02014B50;
const uint16_t ZIP_BIT_FLAG_DATA_DESCRIPTOR = 0x0008;
int Reseeder::ProcessSU3Stream (std::istream& s)
{
char magicNumber[7];
@@ -145,53 +193,73 @@ namespace data
s.read (signerID, signerIDLength); // signerID
signerID[signerIDLength] = 0;
//try to verify signature
auto it = m_SigningKeys.find (signerID);
if (it != m_SigningKeys.end ())
{
// TODO: implement all signature types
if (signatureType == SIGNING_KEY_TYPE_RSA_SHA512_4096)
bool verify; i2p::config::GetOption("reseed.verify", verify);
if (verify)
{
//try to verify signature
auto it = m_SigningKeys.find (signerID);
if (it != m_SigningKeys.end ())
{
size_t pos = s.tellg ();
size_t tbsLen = pos + contentLength;
uint8_t * tbs = new uint8_t[tbsLen];
s.seekg (0, std::ios::beg);
s.read ((char *)tbs, tbsLen);
uint8_t * signature = new uint8_t[signatureLength];
s.read ((char *)signature, signatureLength);
// RSA-raw
// TODO: implement all signature types
if (signatureType == SIGNING_KEY_TYPE_RSA_SHA512_4096)
{
// calculate digest
uint8_t digest[64];
SHA512 (tbs, tbsLen, digest);
// encrypt signature
BN_CTX * bnctx = BN_CTX_new ();
BIGNUM * s = BN_new (), * n = BN_new ();
BN_bin2bn (signature, signatureLength, s);
BN_bin2bn (it->second, i2p::crypto::RSASHA5124096_KEY_LENGTH, n);
BN_mod_exp (s, s, i2p::crypto::GetRSAE (), n, bnctx); // s = s^e mod n
uint8_t * enSigBuf = new uint8_t[signatureLength];
i2p::crypto::bn2buf (s, enSigBuf, signatureLength);
// digest is right aligned
// we can't use RSA_verify due wrong padding in SU3
if (memcmp (enSigBuf + (signatureLength - 64), digest, 64))
LogPrint (eLogWarning, "Reseed: SU3 signature verification failed");
delete[] enSigBuf;
BN_free (s); BN_free (n);
BN_CTX_free (bnctx);
}
size_t pos = s.tellg ();
size_t tbsLen = pos + contentLength;
uint8_t * tbs = new uint8_t[tbsLen];
s.seekg (0, std::ios::beg);
s.read ((char *)tbs, tbsLen);
uint8_t * signature = new uint8_t[signatureLength];
s.read ((char *)signature, signatureLength);
// RSA-raw
{
// calculate digest
uint8_t digest[64];
SHA512 (tbs, tbsLen, digest);
// encrypt signature
BN_CTX * bnctx = BN_CTX_new ();
BIGNUM * s = BN_new (), * n = BN_new ();
BN_bin2bn (signature, signatureLength, s);
BN_bin2bn (it->second, i2p::crypto::RSASHA5124096_KEY_LENGTH, n);
BN_mod_exp (s, s, i2p::crypto::GetRSAE (), n, bnctx); // s = s^e mod n
uint8_t * enSigBuf = new uint8_t[signatureLength];
i2p::crypto::bn2buf (s, enSigBuf, signatureLength);
// digest is right aligned
// we can't use RSA_verify due wrong padding in SU3
if (memcmp (enSigBuf + (signatureLength - 64), digest, 64))
LogPrint (eLogWarning, "Reseed: SU3 signature verification failed");
else
verify = false; // verified
delete[] enSigBuf;
BN_free (s); BN_free (n);
BN_CTX_free (bnctx);
}
delete[] signature;
delete[] tbs;
s.seekg (pos, std::ios::beg);
delete[] signature;
delete[] tbs;
s.seekg (pos, std::ios::beg);
}
else
LogPrint (eLogWarning, "Reseed: Signature type ", signatureType, " is not supported");
}
else
LogPrint (eLogWarning, "Reseed: Signature type ", signatureType, " is not supported");
LogPrint (eLogWarning, "Reseed: Certificate for ", signerID, " not loaded");
}
else
LogPrint (eLogWarning, "Reseed: Certificate for ", signerID, " not loaded");
if (verify) // not verified
{
LogPrint (eLogError, "Reseed: SU3 verification failed");
return 0;
}
// handle content
return ProcessZIPStream (s, contentLength);
}
const uint32_t ZIP_HEADER_SIGNATURE = 0x04034B50;
const uint32_t ZIP_CENTRAL_DIRECTORY_HEADER_SIGNATURE = 0x02014B50;
const uint16_t ZIP_BIT_FLAG_DATA_DESCRIPTOR = 0x0008;
int Reseeder::ProcessZIPStream (std::istream& s, uint64_t contentLength)
{
int numFiles = 0;
size_t contentPos = s.tellg ();
while (!s.eof ())
@@ -307,6 +375,34 @@ namespace data
if (end - contentPos >= contentLength)
break; // we are beyond contentLength
}
if (numFiles) // check if routers are not outdated
{
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
int numOutdated = 0;
i2p::data::netdb.VisitRouterInfos (
[&numOutdated, ts](std::shared_ptr<const RouterInfo> r)
{
if (r && ts > r->GetTimestamp () + 10*i2p::data::NETDB_MAX_EXPIRATION_TIMEOUT*1000LL) // 270 hours
{
LogPrint (eLogError, "Reseed: router ", r->GetIdentHash().ToBase64 (), " is outdated by ", (ts - r->GetTimestamp ())/1000LL/3600LL, " hours");
numOutdated++;
}
});
if (numOutdated > numFiles/2) // more than half
{
LogPrint (eLogError, "Reseed: mammoth's shit\n"
" *_____*\n"
" *_*****_*\n"
" *_(O)_(O)_*\n"
" **____V____**\n"
" **_________**\n"
" **_________**\n"
" *_________*\n"
" ***___***");
i2p::data::netdb.ClearRouterInfos ();
numFiles = 0;
}
}
return numFiles;
}
@@ -332,7 +428,7 @@ namespace data
void Reseeder::LoadCertificate (const std::string& filename)
{
SSL_CTX * ctx = SSL_CTX_new (TLSv1_method ());
SSL_CTX * ctx = SSL_CTX_new (TLS_method ());
int ret = SSL_CTX_use_certificate_file (ctx, filename.c_str (), SSL_FILETYPE_PEM);
if (ret)
{
@@ -344,11 +440,23 @@ namespace data
// extract issuer name
char name[100];
X509_NAME_oneline (X509_get_issuer_name(cert), name, 100);
char * cn = strstr (name, "CN=");
if (cn)
{
cn += 3;
char * terminator = strchr (cn, '/');
if (terminator) terminator[0] = 0;
}
// extract RSA key (we need n only, e = 65537)
RSA * key = X509_get_pubkey (cert)->pkey.rsa;
RSA * key = EVP_PKEY_get0_RSA (X509_get_pubkey (cert));
const BIGNUM * n, * e, * d;
RSA_get0_key(key, &n, &e, &d);
PublicKey value;
i2p::crypto::bn2buf (key->n, value, 512);
m_SigningKeys[name] = value;
i2p::crypto::bn2buf (n, value, 512);
if (cn)
m_SigningKeys[cn] = value;
else
LogPrint (eLogError, "Reseed: Can't find CN field in ", filename);
}
SSL_free (ssl);
}
@@ -402,14 +510,15 @@ namespace data
s.lowest_layer().connect (*it, ecode);
if (!ecode)
{
SSL_set_tlsext_host_name(s.native_handle(), url.host.c_str ());
s.handshake (boost::asio::ssl::stream_base::client, ecode);
if (!ecode)
{
LogPrint (eLogDebug, "Reseed: Connected to ", url.host, ":", url.port);
i2p::http::HTTPReq req;
req.uri = url.to_string();
req.add_header("User-Agent", "Wget/1.11.4");
req.add_header("Connection", "close");
req.AddHeader("User-Agent", "Wget/1.11.4");
req.AddHeader("Connection", "close");
s.write_some (boost::asio::buffer (req.to_string()));
// read response
std::stringstream rs;

View File

@@ -21,7 +21,11 @@ namespace data
Reseeder();
~Reseeder();
int ReseedNowSU3 ();
void Bootstrap ();
int ReseedFromServers ();
int ReseedFromSU3Url (const std::string& url);
int ProcessSU3File (const char * filename);
int ProcessZIPFile (const char * filename);
void LoadCertificates ();
@@ -29,10 +33,9 @@ namespace data
void LoadCertificate (const std::string& filename);
int ReseedFromSU3 (const std::string& host);
int ProcessSU3File (const char * filename);
int ProcessSU3Stream (std::istream& s);
int ProcessZIPStream (std::istream& s, uint64_t contentLength);
bool FindZipDataDescriptor (std::istream& s);
std::string HttpsRequest (const std::string& address);

View File

@@ -1,5 +1,4 @@
#include <fstream>
#include <boost/lexical_cast.hpp>
#include "Config.h"
#include "Crypto.h"
#include "Timestamp.h"
@@ -18,7 +17,8 @@ namespace i2p
RouterContext::RouterContext ():
m_LastUpdateTime (0), m_AcceptsTunnels (true), m_IsFloodfill (false),
m_StartupTime (0), m_Status (eRouterStatusOK )
m_StartupTime (0), m_Status (eRouterStatusOK), m_Error (eRouterErrorNone),
m_NetID (I2PD_NET_ID)
{
}
@@ -53,6 +53,8 @@ namespace i2p
bool ipv6; i2p::config::GetOption("ipv6", ipv6);
bool nat; i2p::config::GetOption("nat", nat);
std::string ifname; i2p::config::GetOption("ifname", ifname);
std::string ifname4; i2p::config::GetOption("ifname4", ifname4);
std::string ifname6; i2p::config::GetOption("ifname6", ifname6);
if (ipv4)
{
std::string host = "127.0.0.1";
@@ -61,6 +63,10 @@ namespace i2p
else if (!nat && !ifname.empty())
/* bind to interface, we have no NAT so set external address too */
host = i2p::util::net::GetInterfaceAddress(ifname, false).to_string(); // v4
if(ifname4.size())
host = i2p::util::net::GetInterfaceAddress(ifname4, false).to_string();
routerInfo.AddSSUAddress (host.c_str(), port, routerInfo.GetIdentHash ());
routerInfo.AddNTCPAddress (host.c_str(), port);
}
@@ -71,12 +77,17 @@ namespace i2p
i2p::config::GetOption("host", host);
else if (!ifname.empty())
host = i2p::util::net::GetInterfaceAddress(ifname, true).to_string(); // v6
if(ifname6.size())
host = i2p::util::net::GetInterfaceAddress(ifname6, true).to_string();
routerInfo.AddSSUAddress (host.c_str(), port, routerInfo.GetIdentHash ());
routerInfo.AddNTCPAddress (host.c_str(), port);
}
routerInfo.SetCaps (i2p::data::RouterInfo::eReachable |
i2p::data::RouterInfo::eSSUTesting | i2p::data::RouterInfo::eSSUIntroducer); // LR, BC
routerInfo.SetProperty ("netId", std::to_string (I2PD_NET_ID));
routerInfo.SetProperty ("netId", std::to_string (m_NetID));
routerInfo.SetProperty ("router.version", I2P_VERSION);
routerInfo.CreateBuffer (m_Keys);
m_RouterInfo.SetRouterIdentity (GetIdentity ());
@@ -95,6 +106,7 @@ namespace i2p
if (status != m_Status)
{
m_Status = status;
m_Error = eRouterErrorNone;
switch (m_Status)
{
case eRouterStatusOK:
@@ -193,7 +205,7 @@ namespace i2p
void RouterContext::SetBandwidth (char L) {
uint16_t limit = 0;
enum { low, high, extra } type = high;
enum { low, high, extra, unlim } type = high;
/* detect parameters */
switch (L)
{
@@ -203,7 +215,7 @@ namespace i2p
case i2p::data::CAPS_FLAG_HIGH_BANDWIDTH2 : limit = 128; type = high; break;
case i2p::data::CAPS_FLAG_HIGH_BANDWIDTH3 : limit = 256; type = high; break;
case i2p::data::CAPS_FLAG_EXTRA_BANDWIDTH1 : limit = 2048; type = extra; break;
case i2p::data::CAPS_FLAG_EXTRA_BANDWIDTH2 : limit = 9999; type = extra; break;
case i2p::data::CAPS_FLAG_EXTRA_BANDWIDTH2 : limit = 9999; type = unlim; break;
default:
limit = 48; type = low;
}
@@ -214,8 +226,9 @@ namespace i2p
switch (type)
{
case low : /* not set */; break;
case extra : caps |= i2p::data::RouterInfo::eExtraBandwidth; break; // 'P'
case unlim : caps |= i2p::data::RouterInfo::eExtraBandwidth; // no break here, extra + high means 'X'
case high : caps |= i2p::data::RouterInfo::eHighBandwidth; break;
case extra : caps |= i2p::data::RouterInfo::eExtraBandwidth; break;
}
m_RouterInfo.SetCaps (caps);
UpdateRouterInfo ();
@@ -241,7 +254,12 @@ namespace i2p
void RouterContext::SetUnreachable ()
{
// set caps
m_RouterInfo.SetCaps (i2p::data::RouterInfo::eUnreachable | i2p::data::RouterInfo::eSSUTesting); // LU, B
uint8_t caps = m_RouterInfo.GetCaps ();
caps &= ~i2p::data::RouterInfo::eReachable;
caps |= i2p::data::RouterInfo::eUnreachable;
caps &= ~i2p::data::RouterInfo::eFloodfill; // can't be floodfill
caps &= ~i2p::data::RouterInfo::eSSUIntroducer; // can't be introducer
m_RouterInfo.SetCaps (caps);
// remove NTCP address
auto& addresses = m_RouterInfo.GetAddresses ();
for (auto it = addresses.begin (); it != addresses.end (); ++it)
@@ -254,8 +272,9 @@ namespace i2p
}
}
// delete previous introducers
for (auto& addr : addresses)
addr->introducers.clear ();
for (auto& addr : addresses)
if (addr->ssu)
addr->ssu->introducers.clear ();
// update
UpdateRouterInfo ();
@@ -286,7 +305,8 @@ namespace i2p
}
// delete previous introducers
for (auto& addr : addresses)
addr->introducers.clear ();
if (addr->ssu)
addr->ssu->introducers.clear ();
// update
UpdateRouterInfo ();
@@ -355,8 +375,8 @@ namespace i2p
if (m_IsFloodfill)
{
// update routers and leasesets
m_RouterInfo.SetProperty (i2p::data::ROUTER_INFO_PROPERTY_LEASESETS, boost::lexical_cast<std::string>(i2p::data::netdb.GetNumLeaseSets ()));
m_RouterInfo.SetProperty (i2p::data::ROUTER_INFO_PROPERTY_ROUTERS, boost::lexical_cast<std::string>(i2p::data::netdb.GetNumRouters ()));
m_RouterInfo.SetProperty (i2p::data::ROUTER_INFO_PROPERTY_LEASESETS, std::to_string(i2p::data::netdb.GetNumLeaseSets ()));
m_RouterInfo.SetProperty (i2p::data::ROUTER_INFO_PROPERTY_ROUTERS, std::to_string(i2p::data::netdb.GetNumRouters ()));
UpdateRouterInfo ();
}
}

View File

@@ -20,9 +20,16 @@ namespace i2p
{
eRouterStatusOK = 0,
eRouterStatusTesting = 1,
eRouterStatusFirewalled = 2
eRouterStatusFirewalled = 2,
eRouterStatusError = 3
};
enum RouterError
{
eRouterErrorNone = 0,
eRouterErrorClockSkew = 1
};
class RouterContext: public i2p::garlic::GarlicDestination
{
public:
@@ -49,6 +56,10 @@ namespace i2p
uint64_t GetBandwidthLimit () const { return m_BandwidthLimit; };
RouterStatus GetStatus () const { return m_Status; };
void SetStatus (RouterStatus status);
RouterError GetError () const { return m_Error; };
void SetError (RouterError error) { m_Status = eRouterStatusError; m_Error = error; };
int GetNetID () const { return m_NetID; };
void SetNetID (int netID) { m_NetID = netID; };
void UpdatePort (int port); // called from Daemon
void UpdateAddress (const boost::asio::ip::address& host); // called from SSU or Daemon
@@ -107,6 +118,8 @@ namespace i2p
uint64_t m_StartupTime; // in seconds since epoch
uint32_t m_BandwidthLimit; // allowed bandwidth
RouterStatus m_Status;
RouterError m_Error;
int m_NetID;
std::mutex m_GarlicMutex;
};

View File

@@ -3,12 +3,17 @@
#include "I2PEndian.h"
#include <fstream>
#include <boost/lexical_cast.hpp>
#include <boost/make_shared.hpp>
#if (BOOST_VERSION >= 105300)
#include <boost/atomic.hpp>
#endif
#include "version.h"
#include "Crypto.h"
#include "Base.h"
#include "Timestamp.h"
#include "Log.h"
#include "NetDb.h"
#include "RouterContext.h"
#include "RouterInfo.h"
namespace i2p
@@ -17,14 +22,14 @@ namespace data
{
RouterInfo::RouterInfo (): m_Buffer (nullptr)
{
m_Addresses = std::make_shared<Addresses>(); // create empty list
m_Addresses = boost::make_shared<Addresses>(); // create empty list
}
RouterInfo::RouterInfo (const std::string& fullPath):
m_FullPath (fullPath), m_IsUpdated (false), m_IsUnreachable (false),
m_SupportedTransports (0), m_Caps (0)
{
m_Addresses = std::make_shared<Addresses>(); // create empty list
m_Addresses = boost::make_shared<Addresses>(); // create empty list
m_Buffer = new uint8_t[MAX_RI_BUFFER_SIZE];
ReadFromFile ();
}
@@ -32,7 +37,7 @@ namespace data
RouterInfo::RouterInfo (const uint8_t * buf, int len):
m_IsUpdated (true), m_IsUnreachable (false), m_SupportedTransports (0), m_Caps (0)
{
m_Addresses = std::make_shared<Addresses>(); // create empty list
m_Addresses = boost::make_shared<Addresses>(); // create empty list
m_Buffer = new uint8_t[MAX_RI_BUFFER_SIZE];
memcpy (m_Buffer, buf, len);
m_BufferLen = len;
@@ -125,14 +130,6 @@ namespace data
m_IsUnreachable = true;
return;
}
std::stringstream str (std::string ((char *)m_Buffer + identityLen, m_BufferLen - identityLen));
ReadFromStream (str);
if (!str)
{
LogPrint (eLogError, "RouterInfo: malformed message");
m_IsUnreachable = true;
return;
}
if (verifySignature)
{
// verify signature
@@ -141,9 +138,20 @@ namespace data
{
LogPrint (eLogError, "RouterInfo: signature verification failed");
m_IsUnreachable = true;
return;
}
m_RouterIdentity->DropVerifier ();
}
// parse RI
std::stringstream str;
str.write ((const char *)m_Buffer + identityLen, m_BufferLen - identityLen);
ReadFromStream (str);
if (!str)
{
LogPrint (eLogError, "RouterInfo: malformed message");
m_IsUnreachable = true;
}
}
void RouterInfo::ReadFromStream (std::istream& s)
@@ -151,7 +159,7 @@ namespace data
s.read ((char *)&m_Timestamp, sizeof (m_Timestamp));
m_Timestamp = be64toh (m_Timestamp);
// read addresses
auto addresses = std::make_shared<Addresses>();
auto addresses = boost::make_shared<Addresses>();
uint8_t numAddresses;
s.read ((char *)&numAddresses, sizeof (numAddresses)); if (!s) return;
bool introducers = false;
@@ -159,61 +167,75 @@ namespace data
{
uint8_t supportedTransports = 0;
bool isValidAddress = true;
Address address;
s.read ((char *)&address.cost, sizeof (address.cost));
s.read ((char *)&address.date, sizeof (address.date));
auto address = std::make_shared<Address>();
s.read ((char *)&address->cost, sizeof (address->cost));
s.read ((char *)&address->date, sizeof (address->date));
char transportStyle[5];
ReadString (transportStyle, s);
ReadString (transportStyle, 5, s);
if (!strcmp (transportStyle, "NTCP"))
address.transportStyle = eTransportNTCP;
address->transportStyle = eTransportNTCP;
else if (!strcmp (transportStyle, "SSU"))
address.transportStyle = eTransportSSU;
{
address->transportStyle = eTransportSSU;
address->ssu.reset (new SSUExt ());
address->ssu->mtu = 0;
}
else
address.transportStyle = eTransportUnknown;
address.port = 0;
address.mtu = 0;
address->transportStyle = eTransportUnknown;
address->port = 0;
uint16_t size, r = 0;
s.read ((char *)&size, sizeof (size)); if (!s) return;
size = be16toh (size);
while (r < size)
{
char key[500], value[500];
r += ReadString (key, s);
char key[255], value[255];
r += ReadString (key, 255, s);
s.seekg (1, std::ios_base::cur); r++; // =
r += ReadString (value, s);
r += ReadString (value, 255, s);
s.seekg (1, std::ios_base::cur); r++; // ;
if (!s) return;
if (!strcmp (key, "host"))
{
boost::system::error_code ecode;
address.host = boost::asio::ip::address::from_string (value, ecode);
address->host = boost::asio::ip::address::from_string (value, ecode);
if (ecode)
{
if (address.transportStyle == eTransportNTCP)
if (address->transportStyle == eTransportNTCP)
{
supportedTransports |= eNTCPV4; // TODO:
address.addressString = value;
address->addressString = value;
}
else
{
supportedTransports |= eSSUV4; // TODO:
address.addressString = value;
address->addressString = value;
}
}
else
{
// add supported protocol
if (address.host.is_v4 ())
supportedTransports |= (address.transportStyle == eTransportNTCP) ? eNTCPV4 : eSSUV4;
if (address->host.is_v4 ())
supportedTransports |= (address->transportStyle == eTransportNTCP) ? eNTCPV4 : eSSUV4;
else
supportedTransports |= (address.transportStyle == eTransportNTCP) ? eNTCPV6 : eSSUV6;
supportedTransports |= (address->transportStyle == eTransportNTCP) ? eNTCPV6 : eSSUV6;
}
}
else if (!strcmp (key, "port"))
address.port = boost::lexical_cast<int>(value);
address->port = boost::lexical_cast<int>(value);
else if (!strcmp (key, "mtu"))
address.mtu = boost::lexical_cast<int>(value);
{
if (address->ssu)
address->ssu->mtu = boost::lexical_cast<int>(value);
else
LogPrint (eLogWarning, "RouterInfo: Unexpected field 'mtu' for NTCP");
}
else if (!strcmp (key, "key"))
Base64ToByteStream (value, strlen (value), address.key, 32);
{
if (address->ssu)
Base64ToByteStream (value, strlen (value), address->ssu->key, 32);
else
LogPrint (eLogWarning, "RouterInfo: Unexpected field 'key' for NTCP");
}
else if (!strcmp (key, "caps"))
ExtractCaps (value);
else if (key[0] == 'i')
@@ -223,9 +245,14 @@ namespace data
size_t l = strlen(key);
unsigned char index = key[l-1] - '0'; // TODO:
key[l-1] = 0;
if (index >= address.introducers.size ())
address.introducers.resize (index + 1);
Introducer& introducer = address.introducers.at (index);
if (index > 9)
{
LogPrint (eLogError, "RouterInfo: Unexpected introducer's index ", index, " skipped");
if (s) continue; else return;
}
if (index >= address->ssu->introducers.size ())
address->ssu->introducers.resize (index + 1);
Introducer& introducer = address->ssu->introducers.at (index);
if (!strcmp (key, "ihost"))
{
boost::system::error_code ecode;
@@ -242,11 +269,15 @@ namespace data
}
if (isValidAddress)
{
addresses->push_back(std::make_shared<Address>(address));
addresses->push_back(address);
m_SupportedTransports |= supportedTransports;
}
}
m_Addresses = addresses;
#if (BOOST_VERSION >= 105300)
boost::atomic_store (&m_Addresses, addresses);
#else
m_Addresses = addresses; // race condition
#endif
// read peers
uint8_t numPeers;
s.read ((char *)&numPeers, sizeof (numPeers)); if (!s) return;
@@ -257,26 +288,21 @@ namespace data
size = be16toh (size);
while (r < size)
{
#ifdef _WIN32
char key[500], value[500];
// TODO: investigate why properties get read as one long string under Windows
// length should not be more than 44
#else
char key[50], value[50];
#endif
r += ReadString (key, s);
char key[255], value[255];
r += ReadString (key, 255, s);
s.seekg (1, std::ios_base::cur); r++; // =
r += ReadString (value, s);
r += ReadString (value, 255, s);
s.seekg (1, std::ios_base::cur); r++; // ;
if (!s) return;
m_Properties[key] = value;
// extract caps
if (!strcmp (key, "caps"))
ExtractCaps (value);
// check netId
else if (!strcmp (key, ROUTER_INFO_PROPERTY_NETID) && atoi (value) != I2PD_NET_ID)
else if (!strcmp (key, ROUTER_INFO_PROPERTY_NETID) && atoi (value) != i2p::context.GetNetID ())
{
LogPrint (eLogError, "Unexpected ", ROUTER_INFO_PROPERTY_NETID, "=", value);
LogPrint (eLogError, "RouterInfo: Unexpected ", ROUTER_INFO_PROPERTY_NETID, "=", value);
m_IsUnreachable = true;
}
// family
@@ -348,19 +374,23 @@ namespace data
void RouterInfo::UpdateCapsProperty ()
{
std::string caps;
if (m_Caps & eFloodfill) {
if (m_Caps & eFloodfill)
{
if (m_Caps & eExtraBandwidth) caps += (m_Caps & eHighBandwidth) ?
CAPS_FLAG_EXTRA_BANDWIDTH2 : // 'X'
CAPS_FLAG_EXTRA_BANDWIDTH1; // 'P'
caps += CAPS_FLAG_HIGH_BANDWIDTH3; // 'O'
caps += CAPS_FLAG_FLOODFILL; // floodfill
caps += (m_Caps & eExtraBandwidth)
? CAPS_FLAG_EXTRA_BANDWIDTH1 // 'P'
: CAPS_FLAG_HIGH_BANDWIDTH3; // 'O'
} else {
if (m_Caps & eExtraBandwidth) {
caps += CAPS_FLAG_EXTRA_BANDWIDTH1; // 'P'
} else if (m_Caps & eHighBandwidth) {
}
else
{
if (m_Caps & eExtraBandwidth)
{
caps += (m_Caps & eHighBandwidth) ? CAPS_FLAG_EXTRA_BANDWIDTH2 /* 'X' */ : CAPS_FLAG_EXTRA_BANDWIDTH1; /*'P' */
caps += CAPS_FLAG_HIGH_BANDWIDTH3; // 'O'
} else {
caps += CAPS_FLAG_LOW_BANDWIDTH2; // 'L'
}
else
caps += (m_Caps & eHighBandwidth) ? CAPS_FLAG_HIGH_BANDWIDTH3 /* 'O' */: CAPS_FLAG_LOW_BANDWIDTH2 /* 'L' */; // bandwidth
}
if (m_Caps & eHidden) caps += CAPS_FLAG_HIDDEN; // hidden
if (m_Caps & eReachable) caps += CAPS_FLAG_REACHABLE; // reachable
@@ -407,10 +437,10 @@ namespace data
if (address.transportStyle == eTransportSSU)
{
// write introducers if any
if (address.introducers.size () > 0)
if (address.ssu->introducers.size () > 0)
{
int i = 0;
for (const auto& introducer: address.introducers)
for (const auto& introducer: address.ssu->introducers)
{
WriteString ("ihost" + boost::lexical_cast<std::string>(i), properties);
properties << '=';
@@ -419,7 +449,7 @@ namespace data
i++;
}
i = 0;
for (const auto& introducer: address.introducers)
for (const auto& introducer: address.ssu->introducers)
{
WriteString ("ikey" + boost::lexical_cast<std::string>(i), properties);
properties << '=';
@@ -431,7 +461,7 @@ namespace data
i++;
}
i = 0;
for (const auto& introducer: address.introducers)
for (const auto& introducer: address.ssu->introducers)
{
WriteString ("iport" + boost::lexical_cast<std::string>(i), properties);
properties << '=';
@@ -440,7 +470,7 @@ namespace data
i++;
}
i = 0;
for (const auto& introducer: address.introducers)
for (const auto& introducer: address.ssu->introducers)
{
WriteString ("itag" + boost::lexical_cast<std::string>(i), properties);
properties << '=';
@@ -453,16 +483,16 @@ namespace data
WriteString ("key", properties);
properties << '=';
char value[64];
size_t l = ByteStreamToBase64 (address.key, 32, value, 64);
size_t l = ByteStreamToBase64 (address.ssu->key, 32, value, 64);
value[l] = 0;
WriteString (value, properties);
properties << ';';
// write mtu
if (address.mtu)
if (address.ssu->mtu)
{
WriteString ("mtu", properties);
properties << '=';
WriteString (boost::lexical_cast<std::string>(address.mtu), properties);
WriteString (boost::lexical_cast<std::string>(address.ssu->mtu), properties);
properties << ';';
}
}
@@ -545,16 +575,26 @@ namespace data
return true;
}
size_t RouterInfo::ReadString (char * str, std::istream& s)
size_t RouterInfo::ReadString (char * str, size_t len, std::istream& s) const
{
uint8_t len;
s.read ((char *)&len, 1);
s.read (str, len);
str[len] = 0;
return len+1;
uint8_t l;
s.read ((char *)&l, 1);
if (l < len)
{
s.read (str, l);
if (!s) l = 0; // failed, return empty string
str[l] = 0;
}
else
{
LogPrint (eLogWarning, "RouterInfo: string length ", (int)l, " exceeds buffer size ", len);
s.seekg (l, std::ios::cur); // skip
str[0] = 0;
}
return l+1;
}
void RouterInfo::WriteString (const std::string& str, std::ostream& s)
void RouterInfo::WriteString (const std::string& str, std::ostream& s) const
{
uint8_t len = str.size ();
s.write ((char *)&len, 1);
@@ -569,7 +609,6 @@ namespace data
addr->transportStyle = eTransportNTCP;
addr->cost = 2;
addr->date = 0;
addr->mtu = 0;
for (const auto& it: *m_Addresses) // don't insert same address twice
if (*it == *addr) return;
m_SupportedTransports |= addr->host.is_v6 () ? eNTCPV6 : eNTCPV4;
@@ -584,8 +623,9 @@ namespace data
addr->transportStyle = eTransportSSU;
addr->cost = 10; // NTCP should have priority over SSU
addr->date = 0;
addr->mtu = mtu;
memcpy (addr->key, key, 32);
addr->ssu.reset (new SSUExt ());
addr->ssu->mtu = mtu;
memcpy (addr->ssu->key, key, 32);
for (const auto& it: *m_Addresses) // don't insert same address twice
if (*it == *addr) return;
m_SupportedTransports |= addr->host.is_v6 () ? eSSUV6 : eSSUV4;
@@ -601,9 +641,9 @@ namespace data
{
if (addr->transportStyle == eTransportSSU && addr->host.is_v4 ())
{
for (auto& intro: addr->introducers)
for (auto& intro: addr->ssu->introducers)
if (intro.iTag == introducer.iTag) return false; // already presented
addr->introducers.push_back (introducer);
addr->ssu->introducers.push_back (introducer);
return true;
}
}
@@ -616,10 +656,10 @@ namespace data
{
if (addr->transportStyle == eTransportSSU && addr->host.is_v4 ())
{
for (auto it = addr->introducers.begin (); it != addr->introducers.end (); ++it)
for (auto it = addr->ssu->introducers.begin (); it != addr->ssu->introducers.end (); ++it)
if ( boost::asio::ip::udp::endpoint (it->iHost, it->iPort) == e)
{
addr->introducers.erase (it);
addr->ssu->introducers.erase (it);
return true;
}
}
@@ -752,7 +792,11 @@ namespace data
std::shared_ptr<const RouterInfo::Address> RouterInfo::GetAddress (TransportStyle s, bool v4only, bool v6only) const
{
#if (BOOST_VERSION >= 105300)
auto addresses = boost::atomic_load (&m_Addresses);
#else
auto addresses = m_Addresses;
#endif
for (const auto& address : *addresses)
{
if (address->transportStyle == s)

View File

@@ -8,6 +8,7 @@
#include <list>
#include <iostream>
#include <boost/asio.hpp>
#include <boost/shared_ptr.hpp>
#include "Identity.h"
#include "Profiling.h"
@@ -78,17 +79,22 @@ namespace data
uint32_t iTag;
};
struct SSUExt
{
int mtu;
IntroKey key; // intro key for SSU
std::vector<Introducer> introducers;
};
struct Address
{
TransportStyle transportStyle;
boost::asio::ip::address host;
std::string addressString;
int port, mtu;
int port;
uint64_t date;
uint8_t cost;
// SSU only
IntroKey key; // intro key for SSU
std::vector<Introducer> introducers;
std::unique_ptr<SSUExt> ssu; // not null for SSU
bool IsCompatible (const boost::asio::ip::address& other) const
{
@@ -167,7 +173,7 @@ namespace data
bool SaveToFile (const std::string& fullPath);
std::shared_ptr<RouterProfile> GetProfile () const;
void SaveProfile () { if (m_Profile) m_Profile->Save (); };
void SaveProfile () { if (m_Profile) m_Profile->Save (GetIdentHash ()); };
void Update (const uint8_t * buf, int len);
void DeleteBuffer () { delete[] m_Buffer; m_Buffer = nullptr; };
@@ -188,8 +194,8 @@ namespace data
void ReadFromStream (std::istream& s);
void ReadFromBuffer (bool verifySignature);
void WriteToStream (std::ostream& s) const;
static size_t ReadString (char* str, std::istream& s);
static void WriteString (const std::string& str, std::ostream& s);
size_t ReadString (char* str, size_t len, std::istream& s) const;
void WriteString (const std::string& str, std::ostream& s) const;
void ExtractCaps (const char * value);
std::shared_ptr<const Address> GetAddress (TransportStyle s, bool v4only, bool v6only = false) const;
void UpdateCapsProperty ();
@@ -201,7 +207,7 @@ namespace data
uint8_t * m_Buffer;
size_t m_BufferLen;
uint64_t m_Timestamp;
std::shared_ptr<Addresses> m_Addresses;
boost::shared_ptr<Addresses> m_Addresses; // TODO: use std::shared_ptr and std::atomic_store for gcc >= 4.9
std::map<std::string, std::string> m_Properties;
bool m_IsUpdated, m_IsUnreachable;
uint8_t m_SupportedTransports, m_Caps;

390
SAM.cpp
View File

@@ -3,21 +3,21 @@
#ifdef _MSC_VER
#include <stdlib.h>
#endif
#include <boost/lexical_cast.hpp>
#include "Base.h"
#include "Identity.h"
#include "Log.h"
#include "Destination.h"
#include "ClientContext.h"
#include "util.h"
#include "SAM.h"
namespace i2p
{
namespace client
{
SAMSocket::SAMSocket (SAMBridge& owner):
SAMSocket::SAMSocket (SAMBridge& owner):
m_Owner (owner), m_Socket (m_Owner.GetService ()), m_Timer (m_Owner.GetService ()),
m_BufferOffset (0), m_SocketType (eSAMSocketTypeUnknown), m_IsSilent (false),
m_BufferOffset (0), m_SocketType (eSAMSocketTypeUnknown), m_IsSilent (false),
m_Stream (nullptr), m_Session (nullptr)
{
}
@@ -25,21 +25,21 @@ namespace client
SAMSocket::~SAMSocket ()
{
Terminate ();
}
}
void SAMSocket::CloseStream ()
{
if (m_Stream)
{
{
m_Stream->Close ();
m_Stream.reset ();
}
}
}
}
void SAMSocket::Terminate ()
{
CloseStream ();
switch (m_SocketType)
{
case eSAMSocketTypeSession:
@@ -47,14 +47,14 @@ namespace client
break;
case eSAMSocketTypeStream:
{
if (m_Session)
if (m_Session)
m_Session->DelSocket (shared_from_this ());
break;
}
case eSAMSocketTypeAcceptor:
{
if (m_Session)
{
{
m_Session->DelSocket (shared_from_this ());
if (m_Session->localDestination)
m_Session->localDestination->StopAcceptingStreams ();
@@ -71,8 +71,8 @@ namespace client
void SAMSocket::ReceiveHandshake ()
{
m_Socket.async_read_some (boost::asio::buffer(m_Buffer, SAM_SOCKET_BUFFER_SIZE),
std::bind(&SAMSocket::HandleHandshakeReceived, shared_from_this (),
m_Socket.async_read_some (boost::asio::buffer(m_Buffer, SAM_SOCKET_BUFFER_SIZE),
std::bind(&SAMSocket::HandleHandshakeReceived, shared_from_this (),
std::placeholders::_1, std::placeholders::_2));
}
@@ -85,7 +85,7 @@ namespace client
Terminate ();
}
else
{
{
m_Buffer[bytes_transferred] = 0;
char * eol = (char *)memchr (m_Buffer, '\n', bytes_transferred);
if (eol)
@@ -94,8 +94,8 @@ namespace client
char * separator = strchr (m_Buffer, ' ');
if (separator)
{
separator = strchr (separator + 1, ' ');
if (separator)
separator = strchr (separator + 1, ' ');
if (separator)
*separator = 0;
}
@@ -108,22 +108,22 @@ namespace client
separator++;
std::map<std::string, std::string> params;
ExtractParams (separator, params);
auto it = params.find (SAM_PARAM_MAX);
//auto it = params.find (SAM_PARAM_MAX);
// TODO: check MIN as well
if (it != params.end ())
version = it->second;
//if (it != params.end ())
// version = it->second;
}
if (version[0] == '3') // we support v3 (3.0 and 3.1) only
{
#ifdef _MSC_VER
size_t l = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_HANDSHAKE_REPLY, version.c_str ());
#else
#else
size_t l = snprintf (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_HANDSHAKE_REPLY, version.c_str ());
#endif
boost::asio::async_write (m_Socket, boost::asio::buffer (m_Buffer, l), boost::asio::transfer_all (),
std::bind(&SAMSocket::HandleHandshakeReplySent, shared_from_this (),
std::bind(&SAMSocket::HandleHandshakeReplySent, shared_from_this (),
std::placeholders::_1, std::placeholders::_2));
}
}
else
SendMessageReply (SAM_HANDSHAKE_I2P_ERROR, strlen (SAM_HANDSHAKE_I2P_ERROR), true);
}
@@ -145,25 +145,25 @@ namespace client
}
else
{
m_Socket.async_read_some (boost::asio::buffer(m_Buffer, SAM_SOCKET_BUFFER_SIZE),
std::bind(&SAMSocket::HandleMessage, shared_from_this (),
std::placeholders::_1, std::placeholders::_2));
}
m_Socket.async_read_some (boost::asio::buffer(m_Buffer, SAM_SOCKET_BUFFER_SIZE),
std::bind(&SAMSocket::HandleMessage, shared_from_this (),
std::placeholders::_1, std::placeholders::_2));
}
}
void SAMSocket::SendMessageReply (const char * msg, size_t len, bool close)
{
if (!m_IsSilent)
if (!m_IsSilent)
boost::asio::async_write (m_Socket, boost::asio::buffer (msg, len), boost::asio::transfer_all (),
std::bind(&SAMSocket::HandleMessageReplySent, shared_from_this (),
std::bind(&SAMSocket::HandleMessageReplySent, shared_from_this (),
std::placeholders::_1, std::placeholders::_2, close));
else
{
if (close)
Terminate ();
else
Receive ();
}
Receive ();
}
}
void SAMSocket::HandleMessageReplySent (const boost::system::error_code& ecode, std::size_t bytes_transferred, bool close)
@@ -179,8 +179,8 @@ namespace client
if (close)
Terminate ();
else
Receive ();
}
Receive ();
}
}
void SAMSocket::HandleMessage (const boost::system::error_code& ecode, std::size_t bytes_transferred)
@@ -205,8 +205,8 @@ namespace client
char * separator = strchr (m_Buffer, ' ');
if (separator)
{
separator = strchr (separator + 1, ' ');
if (separator)
separator = strchr (separator + 1, ' ');
if (separator)
*separator = 0;
else
separator = eol;
@@ -236,12 +236,12 @@ namespace client
*separator = ' ';
*eol = '\n';
}
}
}
// since it's SAM v1 reply is not expected
Receive ();
}
else
{
else
{
LogPrint (eLogError, "SAM: unexpected message ", m_Buffer);
Terminate ();
}
@@ -252,8 +252,9 @@ namespace client
Terminate ();
}
}
else
{
{
LogPrint (eLogWarning, "SAM: incomplete message ", bytes_transferred);
m_BufferOffset = bytes_transferred;
// try to receive remaining message
@@ -267,10 +268,10 @@ namespace client
LogPrint (eLogDebug, "SAM: session create: ", buf);
std::map<std::string, std::string> params;
ExtractParams (buf, params);
std::string& style = params[SAM_PARAM_STYLE];
std::string& style = params[SAM_PARAM_STYLE];
std::string& id = params[SAM_PARAM_ID];
std::string& destination = params[SAM_PARAM_DESTINATION];
m_ID = id;
m_ID = id;
if (m_Owner.FindSession (id))
{
// session exists
@@ -278,15 +279,39 @@ namespace client
return;
}
// create destination
m_Session = m_Owner.CreateSession (id, destination == SAM_VALUE_TRANSIENT ? "" : destination, &params);
std::shared_ptr<boost::asio::ip::udp::endpoint> forward = nullptr;
if (style == SAM_VALUE_DATAGRAM && params.find(SAM_VALUE_HOST) != params.end() && params.find(SAM_VALUE_PORT) != params.end())
{
// udp forward selected
boost::system::error_code e;
// TODO: support hostnames in udp forward
auto addr = boost::asio::ip::address::from_string(params[SAM_VALUE_HOST], e);
if (e)
{
// not an ip address
SendI2PError("Invalid IP Address in HOST");
return;
}
auto port = std::stoi(params[SAM_VALUE_PORT]);
if (port == -1)
{
SendI2PError("Invalid port");
return;
}
forward = std::make_shared<boost::asio::ip::udp::endpoint>(addr, port);
}
// create destination
m_Session = m_Owner.CreateSession (id, destination == SAM_VALUE_TRANSIENT ? "" : destination, &params);
if (m_Session)
{
m_SocketType = eSAMSocketTypeSession;
if (style == SAM_VALUE_DATAGRAM)
{
m_Session->UDPEndpoint = forward;
auto dest = m_Session->localDestination->CreateDatagramDestination ();
dest->SetReceiver (std::bind (&SAMSocket::HandleI2PDatagramReceive, shared_from_this (),
dest->SetReceiver (std::bind (&SAMSocket::HandleI2PDatagramReceive, shared_from_this (),
std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5));
}
@@ -296,7 +321,7 @@ namespace client
{
m_Timer.expires_from_now (boost::posix_time::seconds(SAM_SESSION_READINESS_CHECK_INTERVAL));
m_Timer.async_wait (std::bind (&SAMSocket::HandleSessionReadinessCheckTimer,
shared_from_this (), std::placeholders::_1));
shared_from_this (), std::placeholders::_1));
}
}
else
@@ -314,7 +339,7 @@ namespace client
m_Timer.expires_from_now (boost::posix_time::seconds(SAM_SESSION_READINESS_CHECK_INTERVAL));
m_Timer.async_wait (std::bind (&SAMSocket::HandleSessionReadinessCheckTimer,
shared_from_this (), std::placeholders::_1));
}
}
}
}
@@ -327,7 +352,7 @@ namespace client
priv[l1] = 0;
#ifdef _MSC_VER
size_t l2 = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_SESSION_CREATE_REPLY_OK, priv);
#else
#else
size_t l2 = snprintf (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_SESSION_CREATE_REPLY_OK, priv);
#endif
SendMessageReply (m_Buffer, l2, false);
@@ -341,7 +366,7 @@ namespace client
std::string& id = params[SAM_PARAM_ID];
std::string& destination = params[SAM_PARAM_DESTINATION];
std::string& silent = params[SAM_PARAM_SILENT];
if (silent == SAM_VALUE_TRUE) m_IsSilent = true;
if (silent == SAM_VALUE_TRUE) m_IsSilent = true;
m_ID = id;
m_Session = m_Owner.FindSession (id);
if (m_Session)
@@ -365,7 +390,7 @@ namespace client
SendMessageReply(SAM_SESSION_STATUS_INVALID_KEY, strlen(SAM_SESSION_STATUS_INVALID_KEY), true);
}
else
SendMessageReply (SAM_STREAM_STATUS_INVALID_ID, strlen(SAM_STREAM_STATUS_INVALID_ID), true);
SendMessageReply (SAM_STREAM_STATUS_INVALID_ID, strlen(SAM_STREAM_STATUS_INVALID_ID), true);
}
void SAMSocket::Connect (std::shared_ptr<const i2p::data::LeaseSet> remote)
@@ -374,7 +399,7 @@ namespace client
m_Session->AddSocket (shared_from_this ());
m_Stream = m_Session->localDestination->CreateStream (remote);
m_Stream->Send ((uint8_t *)m_Buffer, 0); // connect
I2PReceive ();
I2PReceive ();
SendMessageReply (SAM_STREAM_STATUS_OK, strlen(SAM_STREAM_STATUS_OK), false);
}
@@ -396,21 +421,17 @@ namespace client
ExtractParams (buf, params);
std::string& id = params[SAM_PARAM_ID];
std::string& silent = params[SAM_PARAM_SILENT];
if (silent == SAM_VALUE_TRUE) m_IsSilent = true;
if (silent == SAM_VALUE_TRUE) m_IsSilent = true;
m_ID = id;
m_Session = m_Owner.FindSession (id);
if (m_Session)
{
m_SocketType = eSAMSocketTypeAcceptor;
m_Session->AddSocket (shared_from_this ());
if (!m_Session->localDestination->IsAcceptingStreams ())
{
m_SocketType = eSAMSocketTypeAcceptor;
m_Session->AddSocket (shared_from_this ());
m_Session->localDestination->AcceptStreams (std::bind (&SAMSocket::HandleI2PAccept, shared_from_this (), std::placeholders::_1));
SendMessageReply (SAM_STREAM_STATUS_OK, strlen(SAM_STREAM_STATUS_OK), false);
}
else
SendMessageReply (SAM_STREAM_STATUS_I2P_ERROR, strlen(SAM_STREAM_STATUS_I2P_ERROR), true);
}
m_Session->localDestination->AcceptOnce (std::bind (&SAMSocket::HandleI2PAccept, shared_from_this (), std::placeholders::_1));
SendMessageReply (SAM_STREAM_STATUS_OK, strlen(SAM_STREAM_STATUS_OK), false);
}
else
SendMessageReply (SAM_STREAM_STATUS_INVALID_ID, strlen(SAM_STREAM_STATUS_INVALID_ID), true);
}
@@ -420,11 +441,11 @@ namespace client
LogPrint (eLogDebug, "SAM: datagram send: ", buf, " ", len);
std::map<std::string, std::string> params;
ExtractParams (buf, params);
size_t size = boost::lexical_cast<int>(params[SAM_PARAM_SIZE]), offset = data - buf;
size_t size = std::stoi(params[SAM_PARAM_SIZE]), offset = data - buf;
if (offset + size <= len)
{
{
if (m_Session)
{
{
auto d = m_Session->localDestination->GetDatagramDestination ();
if (d)
{
@@ -437,24 +458,24 @@ namespace client
}
else
LogPrint (eLogError, "SAM: session is not created from DATAGRAM SEND");
}
}
else
{
LogPrint (eLogWarning, "SAM: sent datagram size ", size, " exceeds buffer ", len - offset);
return 0; // try to receive more
}
}
return offset + size;
}
}
void SAMSocket::ProcessDestGenerate ()
{
LogPrint (eLogDebug, "SAM: dest generate");
auto keys = i2p::data::PrivateKeys::CreateRandomKeys ();
#ifdef _MSC_VER
size_t len = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_DEST_REPLY,
size_t len = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_DEST_REPLY,
keys.GetPublic ()->ToBase64 ().c_str (), keys.ToBase64 ().c_str ());
#else
size_t len = snprintf (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_DEST_REPLY,
#else
size_t len = snprintf (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_DEST_REPLY,
keys.GetPublic ()->ToBase64 ().c_str (), keys.ToBase64 ().c_str ());
#endif
SendMessageReply (m_Buffer, len, false);
@@ -468,60 +489,71 @@ namespace client
std::string& name = params[SAM_PARAM_NAME];
std::shared_ptr<const i2p::data::IdentityEx> identity;
i2p::data::IdentHash ident;
auto dest = m_Session == nullptr ? context.GetSharedLocalDestination() : m_Session->localDestination;
if (name == "ME")
SendNamingLookupReply (m_Session->localDestination->GetIdentity ());
SendNamingLookupReply (dest->GetIdentity ());
else if ((identity = context.GetAddressBook ().GetAddress (name)) != nullptr)
SendNamingLookupReply (identity);
else if (m_Session && m_Session->localDestination &&
context.GetAddressBook ().GetIdentHash (name, ident))
else if (context.GetAddressBook ().GetIdentHash (name, ident))
{
auto leaseSet = m_Session->localDestination->FindLeaseSet (ident);
auto leaseSet = dest->FindLeaseSet (ident);
if (leaseSet)
SendNamingLookupReply (leaseSet->GetIdentity ());
else
m_Session->localDestination->RequestDestination (ident,
dest->RequestDestination (ident,
std::bind (&SAMSocket::HandleNamingLookupLeaseSetRequestComplete,
shared_from_this (), std::placeholders::_1, ident));
}
else
shared_from_this (), std::placeholders::_1, ident));
}
else
{
LogPrint (eLogError, "SAM: naming failed, unknown address ", name);
#ifdef _MSC_VER
size_t len = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_NAMING_REPLY_INVALID_KEY, name.c_str());
#else
#else
size_t len = snprintf (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_NAMING_REPLY_INVALID_KEY, name.c_str());
#endif
SendMessageReply (m_Buffer, len, false);
}
}
}
void SAMSocket::HandleNamingLookupLeaseSetRequestComplete (std::shared_ptr<i2p::data::LeaseSet> leaseSet, i2p::data::IdentHash ident)
void SAMSocket::SendI2PError(const std::string & msg)
{
LogPrint (eLogError, "SAM: i2p error ", msg);
#ifdef _MSC_VER
size_t len = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_SESSION_STATUS_I2P_ERROR, msg.c_str());
#else
size_t len = snprintf (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_SESSION_STATUS_I2P_ERROR, msg.c_str());
#endif
SendMessageReply (m_Buffer, len, true);
}
void SAMSocket::HandleNamingLookupLeaseSetRequestComplete (std::shared_ptr<i2p::data::LeaseSet> leaseSet, i2p::data::IdentHash ident)
{
if (leaseSet)
{
{
context.GetAddressBook ().InsertAddress (leaseSet->GetIdentity ());
SendNamingLookupReply (leaseSet->GetIdentity ());
}
}
else
{
LogPrint (eLogError, "SAM: naming lookup failed. LeaseSet for ", ident.ToBase32 (), " not found");
#ifdef _MSC_VER
size_t len = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_NAMING_REPLY_INVALID_KEY,
size_t len = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_NAMING_REPLY_INVALID_KEY,
context.GetAddressBook ().ToAddress (ident).c_str());
#else
#else
size_t len = snprintf (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_NAMING_REPLY_INVALID_KEY,
context.GetAddressBook ().ToAddress (ident).c_str());
#endif
SendMessageReply (m_Buffer, len, false);
}
}
}
void SAMSocket::SendNamingLookupReply (std::shared_ptr<const i2p::data::IdentityEx> identity)
{
auto base64 = identity->ToBase64 ();
#ifdef _MSC_VER
size_t l = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_NAMING_REPLY, base64.c_str ());
#else
size_t l = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_NAMING_REPLY, base64.c_str ());
#else
size_t l = snprintf (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_NAMING_REPLY, base64.c_str ());
#endif
SendMessageReply (m_Buffer, l, false);
@@ -529,7 +561,7 @@ namespace client
void SAMSocket::ExtractParams (char * buf, std::map<std::string, std::string>& params)
{
char * separator;
char * separator;
do
{
separator = strchr (buf, ' ');
@@ -540,11 +572,11 @@ namespace client
*value = 0;
value++;
params[buf] = value;
}
}
buf = separator + 1;
}
while (separator);
}
}
void SAMSocket::Receive ()
{
@@ -554,7 +586,7 @@ namespace client
Terminate ();
return;
}
m_Socket.async_read_some (boost::asio::buffer(m_Buffer + m_BufferOffset, SAM_SOCKET_BUFFER_SIZE - m_BufferOffset),
m_Socket.async_read_some (boost::asio::buffer(m_Buffer + m_BufferOffset, SAM_SOCKET_BUFFER_SIZE - m_BufferOffset),
std::bind((m_SocketType == eSAMSocketTypeStream) ? &SAMSocket::HandleReceived : &SAMSocket::HandleMessage,
shared_from_this (), std::placeholders::_1, std::placeholders::_2));
}
@@ -570,7 +602,7 @@ namespace client
else
{
if (m_Stream)
{
{
auto s = shared_from_this ();
m_Stream->AsyncSend ((uint8_t *)m_Buffer, bytes_transferred,
[s](const boost::system::error_code& ecode)
@@ -578,20 +610,38 @@ namespace client
if (!ecode)
s->Receive ();
else
s->Terminate ();
s->m_Owner.GetService ().post ([s] { s->Terminate (); });
});
}
}
}
}
void SAMSocket::I2PReceive ()
{
if (m_Stream)
m_Stream->AsyncReceive (boost::asio::buffer (m_StreamBuffer, SAM_SOCKET_BUFFER_SIZE),
std::bind (&SAMSocket::HandleI2PReceive, shared_from_this (),
std::placeholders::_1, std::placeholders::_2),
SAM_SOCKET_CONNECTION_MAX_IDLE);
}
{
if (m_Stream->GetStatus () == i2p::stream::eStreamStatusNew ||
m_Stream->GetStatus () == i2p::stream::eStreamStatusOpen) // regular
{
m_Stream->AsyncReceive (boost::asio::buffer (m_StreamBuffer, SAM_SOCKET_BUFFER_SIZE),
std::bind (&SAMSocket::HandleI2PReceive, shared_from_this (),
std::placeholders::_1, std::placeholders::_2),
SAM_SOCKET_CONNECTION_MAX_IDLE);
}
else // closed by peer
{
// get remaning data
auto len = m_Stream->ReadSome (m_StreamBuffer, SAM_SOCKET_BUFFER_SIZE);
if (len > 0) // still some data
{
boost::asio::async_write (m_Socket, boost::asio::buffer (m_StreamBuffer, len),
std::bind (&SAMSocket::HandleWriteI2PData, shared_from_this (), std::placeholders::_1));
}
else // no more data
Terminate ();
}
}
}
void SAMSocket::HandleI2PReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred)
{
@@ -599,7 +649,21 @@ namespace client
{
LogPrint (eLogError, "SAM: stream read error: ", ecode.message ());
if (ecode != boost::asio::error::operation_aborted)
Terminate ();
{
if (bytes_transferred > 0)
boost::asio::async_write (m_Socket, boost::asio::buffer (m_StreamBuffer, bytes_transferred),
std::bind (&SAMSocket::HandleWriteI2PData, shared_from_this (), std::placeholders::_1)); // postpone termination
else
{
auto s = shared_from_this ();
m_Owner.GetService ().post ([s] { s->Terminate (); });
}
}
else
{
auto s = shared_from_this ();
m_Owner.GetService ().post ([s] { s->Terminate (); });
}
}
else
{
@@ -625,12 +689,20 @@ namespace client
if (stream)
{
LogPrint (eLogDebug, "SAM: incoming I2P connection for session ", m_ID);
m_SocketType = eSAMSocketTypeStream;
m_Stream = stream;
context.GetAddressBook ().InsertAddress (stream->GetRemoteIdentity ());
auto session = m_Owner.FindSession (m_ID);
if (session)
session->localDestination->StopAcceptingStreams ();
m_SocketType = eSAMSocketTypeStream;
if (session)
{
// find more pending acceptors
for (auto it: session->ListSockets ())
if (it->m_SocketType == eSAMSocketTypeAcceptor)
{
session->localDestination->AcceptOnce (std::bind (&SAMSocket::HandleI2PAccept, it, std::placeholders::_1));
break;
}
}
if (!m_IsSilent)
{
// get remote peer address
@@ -638,44 +710,66 @@ namespace client
const size_t ident_len = ident_ptr->GetFullLen();
uint8_t* ident = new uint8_t[ident_len];
// send remote peer address as base64
// send remote peer address as base64
const size_t l = ident_ptr->ToBuffer (ident, ident_len);
const size_t l1 = i2p::data::ByteStreamToBase64 (ident, l, (char *)m_StreamBuffer, SAM_SOCKET_BUFFER_SIZE);
delete[] ident;
m_StreamBuffer[l1] = '\n';
HandleI2PReceive (boost::system::error_code (), l1 +1); // we send identity like it has been received from stream
}
}
else
I2PReceive ();
}
else
LogPrint (eLogWarning, "SAM: I2P acceptor has been reset");
}
}
void SAMSocket::HandleI2PDatagramReceive (const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len)
{
LogPrint (eLogDebug, "SAM: datagram received ", len);
auto base64 = from.ToBase64 ();
#ifdef _MSC_VER
size_t l = sprintf_s ((char *)m_StreamBuffer, SAM_SOCKET_BUFFER_SIZE, SAM_DATAGRAM_RECEIVED, base64.c_str (), len);
#else
size_t l = snprintf ((char *)m_StreamBuffer, SAM_SOCKET_BUFFER_SIZE, SAM_DATAGRAM_RECEIVED, base64.c_str (), len);
#endif
if (len < SAM_SOCKET_BUFFER_SIZE - l)
{
memcpy (m_StreamBuffer + l, buf, len);
boost::asio::async_write (m_Socket, boost::asio::buffer (m_StreamBuffer, len + l),
std::bind (&SAMSocket::HandleWriteI2PData, shared_from_this (), std::placeholders::_1));
auto ep = m_Session->UDPEndpoint;
if (ep)
{
// udp forward enabled
size_t bsz = base64.size();
size_t sz = bsz + 1 + len;
// build datagram body
uint8_t * data = new uint8_t[sz];
// Destination
memcpy(data, base64.c_str(), bsz);
// linefeed
data[bsz] = '\n';
// Payload
memcpy(data+bsz+1, buf, len);
// send to remote endpoint
m_Owner.SendTo(data, sz, ep);
delete [] data;
}
else
LogPrint (eLogWarning, "SAM: received datagram size ", len," exceeds buffer");
{
#ifdef _MSC_VER
size_t l = sprintf_s ((char *)m_StreamBuffer, SAM_SOCKET_BUFFER_SIZE, SAM_DATAGRAM_RECEIVED, base64.c_str (), (long unsigned int)len);
#else
size_t l = snprintf ((char *)m_StreamBuffer, SAM_SOCKET_BUFFER_SIZE, SAM_DATAGRAM_RECEIVED, base64.c_str (), (long unsigned int)len);
#endif
if (len < SAM_SOCKET_BUFFER_SIZE - l)
{
memcpy (m_StreamBuffer + l, buf, len);
boost::asio::async_write (m_Socket, boost::asio::buffer (m_StreamBuffer, len + l),
std::bind (&SAMSocket::HandleWriteI2PData, shared_from_this (), std::placeholders::_1));
}
else
LogPrint (eLogWarning, "SAM: received datagram size ", len," exceeds buffer");
}
}
SAMSession::SAMSession (std::shared_ptr<ClientDestination> dest):
localDestination (dest)
localDestination (dest),
UDPEndpoint(nullptr)
{
}
SAMSession::~SAMSession ()
{
CloseStreams();
@@ -705,7 +799,7 @@ namespace client
{
if (m_IsRunning)
Stop ();
}
}
void SAMBridge::Start ()
{
@@ -724,26 +818,26 @@ namespace client
m_Sessions.clear ();
m_Service.stop ();
if (m_Thread)
{
m_Thread->join ();
{
m_Thread->join ();
delete m_Thread;
m_Thread = nullptr;
}
}
}
void SAMBridge::Run ()
{
void SAMBridge::Run ()
{
while (m_IsRunning)
{
try
{
{
m_Service.run ();
}
catch (std::exception& ex)
{
LogPrint (eLogError, "SAM: runtime exception: ", ex.what ());
}
}
}
}
}
void SAMBridge::Accept ()
@@ -760,7 +854,7 @@ namespace client
boost::system::error_code ec;
auto ep = socket->GetSocket ().remote_endpoint (ec);
if (!ec)
{
{
LogPrint (eLogDebug, "SAM: new connection from ", ep);
socket->ReceiveHandshake ();
}
@@ -774,10 +868,10 @@ namespace client
Accept ();
}
std::shared_ptr<SAMSession> SAMBridge::CreateSession (const std::string& id, const std::string& destination,
std::shared_ptr<SAMSession> SAMBridge::CreateSession (const std::string& id, const std::string& destination,
const std::map<std::string, std::string> * params)
{
std::shared_ptr<ClientDestination> localDestination = nullptr;
std::shared_ptr<ClientDestination> localDestination = nullptr;
if (destination != "")
{
i2p::data::PrivateKeys keys;
@@ -792,10 +886,10 @@ namespace client
{
auto it = params->find (SAM_PARAM_SIGNATURE_TYPE);
if (it != params->end ())
// TODO: extract string values
signatureType = boost::lexical_cast<int> (it->second);
// TODO: extract string values
signatureType = std::stoi(it->second);
}
localDestination = i2p::client::context.CreateNewLocalDestination (true, signatureType, params);
localDestination = i2p::client::context.CreateNewLocalDestination (true, signatureType, params);
}
if (localDestination)
{
@@ -816,13 +910,13 @@ namespace client
std::unique_lock<std::mutex> l(m_SessionsMutex);
auto it = m_Sessions.find (id);
if (it != m_Sessions.end ())
{
{
session = it->second;
m_Sessions.erase (it);
}
}
}
}
if (session)
{
{
session->localDestination->StopAcceptingStreams ();
session->CloseStreams ();
}
@@ -837,12 +931,20 @@ namespace client
return nullptr;
}
void SAMBridge::SendTo(const uint8_t * buf, size_t len, std::shared_ptr<boost::asio::ip::udp::endpoint> remote)
{
if(remote)
{
m_DatagramSocket.send_to(boost::asio::buffer(buf, len), *remote);
}
}
void SAMBridge::ReceiveDatagram ()
{
m_DatagramSocket.async_receive_from (
boost::asio::buffer (m_DatagramReceiveBuffer, i2p::datagram::MAX_DATAGRAM_SIZE),
boost::asio::buffer (m_DatagramReceiveBuffer, i2p::datagram::MAX_DATAGRAM_SIZE),
m_SenderEndpoint,
std::bind (&SAMBridge::HandleReceivedDatagram, this, std::placeholders::_1, std::placeholders::_2));
std::bind (&SAMBridge::HandleReceivedDatagram, this, std::placeholders::_1, std::placeholders::_2));
}
void SAMBridge::HandleReceivedDatagram (const boost::system::error_code& ecode, std::size_t bytes_transferred)
@@ -852,7 +954,7 @@ namespace client
m_DatagramReceiveBuffer[bytes_transferred] = 0;
char * eol = strchr ((char *)m_DatagramReceiveBuffer, '\n');
*eol = 0; eol++;
size_t payloadLen = bytes_transferred - ((uint8_t *)eol - m_DatagramReceiveBuffer);
size_t payloadLen = bytes_transferred - ((uint8_t *)eol - m_DatagramReceiveBuffer);
LogPrint (eLogDebug, "SAM: datagram received ", m_DatagramReceiveBuffer," size=", payloadLen);
char * sessionID = strchr ((char *)m_DatagramReceiveBuffer, ' ');
if (sessionID)
@@ -864,12 +966,12 @@ namespace client
*destination = 0; destination++;
auto session = FindSession (sessionID);
if (session)
{
{
i2p::data::IdentityEx dest;
dest.FromBase64 (destination);
session->localDestination->GetDatagramDestination ()->
SendDatagramTo ((uint8_t *)eol, payloadLen, dest.GetIdentHash ());
}
}
else
LogPrint (eLogError, "SAM: Session ", sessionID, " not found");
}

63
SAM.h
View File

@@ -20,45 +20,48 @@ namespace client
{
const size_t SAM_SOCKET_BUFFER_SIZE = 8192;
const int SAM_SOCKET_CONNECTION_MAX_IDLE = 3600; // in seconds
const int SAM_SESSION_READINESS_CHECK_INTERVAL = 20; // in seconds
const int SAM_SESSION_READINESS_CHECK_INTERVAL = 20; // in seconds
const char SAM_HANDSHAKE[] = "HELLO VERSION";
const char SAM_HANDSHAKE_REPLY[] = "HELLO REPLY RESULT=OK VERSION=%s\n";
const char SAM_HANDSHAKE_I2P_ERROR[] = "HELLO REPLY RESULT=I2P_ERROR\n";
const char SAM_HANDSHAKE_I2P_ERROR[] = "HELLO REPLY RESULT=I2P_ERROR\n";
const char SAM_SESSION_CREATE[] = "SESSION CREATE";
const char SAM_SESSION_CREATE_REPLY_OK[] = "SESSION STATUS RESULT=OK DESTINATION=%s\n";
const char SAM_SESSION_CREATE_DUPLICATED_ID[] = "SESSION STATUS RESULT=DUPLICATED_ID\n";
const char SAM_SESSION_CREATE_DUPLICATED_DEST[] = "SESSION STATUS RESULT=DUPLICATED_DEST\n";
const char SAM_SESSION_CREATE_DUPLICATED_DEST[] = "SESSION STATUS RESULT=DUPLICATED_DEST\n";
const char SAM_SESSION_STATUS_INVALID_KEY[] = "SESSION STATUS RESULT=INVALID_KEY\n";
const char SAM_SESSION_STATUS_I2P_ERROR[] = "SESSION STATUS RESULT=I2P_ERROR MESSAGE=%s\n";
const char SAM_STREAM_CONNECT[] = "STREAM CONNECT";
const char SAM_STREAM_STATUS_OK[] = "STREAM STATUS RESULT=OK\n";
const char SAM_STREAM_STATUS_INVALID_ID[] = "STREAM STATUS RESULT=INVALID_ID\n";
const char SAM_STREAM_STATUS_CANT_REACH_PEER[] = "STREAM STATUS RESULT=CANT_REACH_PEER\n";
const char SAM_STREAM_STATUS_I2P_ERROR[] = "STREAM STATUS RESULT=I2P_ERROR\n";
const char SAM_STREAM_ACCEPT[] = "STREAM ACCEPT";
const char SAM_STREAM_ACCEPT[] = "STREAM ACCEPT";
const char SAM_DATAGRAM_SEND[] = "DATAGRAM SEND";
const char SAM_DEST_GENERATE[] = "DEST GENERATE";
const char SAM_DEST_REPLY[] = "DEST REPLY PUB=%s PRIV=%s\n";
const char SAM_DEST_REPLY[] = "DEST REPLY PUB=%s PRIV=%s\n";
const char SAM_DEST_REPLY_I2P_ERROR[] = "DEST REPLY RESULT=I2P_ERROR\n";
const char SAM_NAMING_LOOKUP[] = "NAMING LOOKUP";
const char SAM_NAMING_REPLY[] = "NAMING REPLY RESULT=OK NAME=ME VALUE=%s\n";
const char SAM_DATAGRAM_RECEIVED[] = "DATAGRAM RECEIVED DESTINATION=%s SIZE=%lu\n";
const char SAM_NAMING_REPLY_INVALID_KEY[] = "NAMING REPLY RESULT=INVALID_KEY NAME=%s\n";
const char SAM_NAMING_REPLY_KEY_NOT_FOUND[] = "NAMING REPLY RESULT=INVALID_KEY_NOT_FOUND NAME=%s\n";
const char SAM_PARAM_MIN[] = "MIN";
const char SAM_PARAM_MAX[] = "MAX";
const char SAM_PARAM_STYLE[] = "STYLE";
const char SAM_PARAM_ID[] = "ID";
const char SAM_PARAM_MIN[] = "MIN";
const char SAM_PARAM_MAX[] = "MAX";
const char SAM_PARAM_STYLE[] = "STYLE";
const char SAM_PARAM_ID[] = "ID";
const char SAM_PARAM_SILENT[] = "SILENT";
const char SAM_PARAM_DESTINATION[] = "DESTINATION";
const char SAM_PARAM_DESTINATION[] = "DESTINATION";
const char SAM_PARAM_NAME[] = "NAME";
const char SAM_PARAM_SIGNATURE_TYPE[] = "SIGNATURE_TYPE";
const char SAM_PARAM_SIGNATURE_TYPE[] = "SIGNATURE_TYPE";
const char SAM_PARAM_SIZE[] = "SIZE";
const char SAM_VALUE_TRANSIENT[] = "TRANSIENT";
const char SAM_VALUE_TRANSIENT[] = "TRANSIENT";
const char SAM_VALUE_STREAM[] = "STREAM";
const char SAM_VALUE_DATAGRAM[] = "DATAGRAM";
const char SAM_VALUE_RAW[] = "RAW";
const char SAM_VALUE_TRUE[] = "true";
const char SAM_VALUE_FALSE[] = "false";
const char SAM_VALUE_RAW[] = "RAW";
const char SAM_VALUE_TRUE[] = "true";
const char SAM_VALUE_FALSE[] = "false";
const char SAM_VALUE_HOST[] = "HOST";
const char SAM_VALUE_PORT[] = "PORT";
enum SAMSocketType
{
@@ -76,8 +79,8 @@ namespace client
public:
SAMSocket (SAMBridge& owner);
~SAMSocket ();
void CloseStream (); // TODO: implement it better
~SAMSocket ();
void CloseStream (); // TODO: implement it better
boost::asio::ip::tcp::socket& GetSocket () { return m_Socket; };
void ReceiveHandshake ();
@@ -86,16 +89,16 @@ namespace client
private:
void Terminate ();
void Terminate ();
void HandleHandshakeReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred);
void HandleHandshakeReplySent (const boost::system::error_code& ecode, std::size_t bytes_transferred);
void HandleMessage (const boost::system::error_code& ecode, std::size_t bytes_transferred);
void SendMessageReply (const char * msg, size_t len, bool close);
void SendMessageReply (const char * msg, size_t len, bool close);
void HandleMessageReplySent (const boost::system::error_code& ecode, std::size_t bytes_transferred, bool close);
void Receive ();
void HandleReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred);
void I2PReceive ();
void I2PReceive ();
void HandleI2PReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred);
void HandleI2PAccept (std::shared_ptr<i2p::stream::Stream> stream);
void HandleWriteI2PData (const boost::system::error_code& ecode);
@@ -106,7 +109,8 @@ namespace client
void ProcessStreamAccept (char * buf, size_t len);
void ProcessDestGenerate ();
void ProcessNamingLookup (char * buf, size_t len);
size_t ProcessDatagramSend (char * buf, size_t len, const char * data); // from SAM 1.0
void SendI2PError(const std::string & msg);
size_t ProcessDatagramSend (char * buf, size_t len, const char * data); // from SAM 1.0
void ExtractParams (char * buf, std::map<std::string, std::string>& params);
void Connect (std::shared_ptr<const i2p::data::LeaseSet> remote);
@@ -129,12 +133,13 @@ namespace client
bool m_IsSilent;
std::shared_ptr<i2p::stream::Stream> m_Stream;
std::shared_ptr<SAMSession> m_Session;
};
};
struct SAMSession
{
std::shared_ptr<ClientDestination> localDestination;
std::list<std::shared_ptr<SAMSocket> > m_Sockets;
std::shared_ptr<boost::asio::ip::udp::endpoint> UDPEndpoint;
std::mutex m_SocketsMutex;
/** safely add a socket to this session */
@@ -158,8 +163,8 @@ namespace client
}
return l;
}
SAMSession (std::shared_ptr<ClientDestination> dest);
SAMSession (std::shared_ptr<ClientDestination> dest);
~SAMSession ();
void CloseStreams ();
@@ -174,13 +179,16 @@ namespace client
void Start ();
void Stop ();
boost::asio::io_service& GetService () { return m_Service; };
std::shared_ptr<SAMSession> CreateSession (const std::string& id, const std::string& destination, // empty string means transient
const std::map<std::string, std::string> * params);
void CloseSession (const std::string& id);
std::shared_ptr<SAMSession> FindSession (const std::string& id) const;
/** send raw data to remote endpoint from our UDP Socket */
void SendTo(const uint8_t * buf, size_t len, std::shared_ptr<boost::asio::ip::udp::endpoint> remote);
private:
void Run ();
@@ -194,7 +202,7 @@ namespace client
private:
bool m_IsRunning;
std::thread * m_Thread;
std::thread * m_Thread;
boost::asio::io_service m_Service;
boost::asio::ip::tcp::acceptor m_Acceptor;
boost::asio::ip::udp::endpoint m_DatagramEndpoint, m_SenderEndpoint;
@@ -207,9 +215,8 @@ namespace client
// for HTTP
const decltype(m_Sessions)& GetSessions () const { return m_Sessions; };
};
};
}
}
#endif

View File

@@ -10,6 +10,7 @@
#include "I2PEndian.h"
#include "I2PTunnel.h"
#include "I2PService.h"
#include "util.h"
namespace i2p
{
@@ -638,7 +639,7 @@ namespace proxy
{
LogPrint(eLogInfo, "SOCKS: forwarding to upstream");
EnterState(UPSTREAM_RESOLVE);
boost::asio::ip::tcp::resolver::query q(m_UpstreamProxyAddress,boost::lexical_cast<std::string>(m_UpstreamProxyPort) );
boost::asio::ip::tcp::resolver::query q(m_UpstreamProxyAddress, std::to_string(m_UpstreamProxyPort));
m_proxy_resolver.async_resolve(q, std::bind(&SOCKSHandler::HandleUpstreamResolved, shared_from_this(),
std::placeholders::_1, std::placeholders::_2));
}

231
SSU.cpp
View File

@@ -13,57 +13,69 @@ namespace transport
SSUServer::SSUServer (const boost::asio::ip::address & addr, int port):
m_OnlyV6(true), m_IsRunning(false),
m_Thread (nullptr), m_ThreadV6 (nullptr), m_ReceiversThread (nullptr),
m_Work (m_Service), m_WorkV6 (m_ServiceV6), m_ReceiversWork (m_ReceiversService),
m_EndpointV6 (addr, port),
m_Socket (m_ReceiversService, m_Endpoint), m_SocketV6 (m_ReceiversService),
m_IntroducersUpdateTimer (m_Service), m_PeerTestsCleanupTimer (m_Service)
m_Thread (nullptr), m_ThreadV6 (nullptr), m_ReceiversThread (nullptr),
m_ReceiversThreadV6 (nullptr), m_Work (m_Service), m_WorkV6 (m_ServiceV6),
m_ReceiversWork (m_ReceiversService), m_ReceiversWorkV6 (m_ReceiversServiceV6),
m_EndpointV6 (addr, port), m_Socket (m_ReceiversService, m_Endpoint),
m_SocketV6 (m_ReceiversServiceV6), m_IntroducersUpdateTimer (m_Service),
m_PeerTestsCleanupTimer (m_Service), m_TerminationTimer (m_Service),
m_TerminationTimerV6 (m_ServiceV6)
{
m_SocketV6.open (boost::asio::ip::udp::v6());
m_SocketV6.set_option (boost::asio::ip::v6_only (true));
m_SocketV6.set_option (boost::asio::socket_base::receive_buffer_size (65535));
m_SocketV6.set_option (boost::asio::socket_base::send_buffer_size (65535));
m_SocketV6.bind (m_EndpointV6);
OpenSocketV6 ();
}
SSUServer::SSUServer (int port):
m_OnlyV6(false), m_IsRunning(false),
m_Thread (nullptr), m_ThreadV6 (nullptr), m_ReceiversThread (nullptr),
m_Work (m_Service), m_WorkV6 (m_ServiceV6), m_ReceiversWork (m_ReceiversService),
m_ReceiversThreadV6 (nullptr), m_Work (m_Service), m_WorkV6 (m_ServiceV6),
m_ReceiversWork (m_ReceiversService), m_ReceiversWorkV6 (m_ReceiversServiceV6),
m_Endpoint (boost::asio::ip::udp::v4 (), port), m_EndpointV6 (boost::asio::ip::udp::v6 (), port),
m_Socket (m_ReceiversService, m_Endpoint), m_SocketV6 (m_ReceiversService),
m_IntroducersUpdateTimer (m_Service), m_PeerTestsCleanupTimer (m_Service)
m_Socket (m_ReceiversService), m_SocketV6 (m_ReceiversServiceV6),
m_IntroducersUpdateTimer (m_Service), m_PeerTestsCleanupTimer (m_Service),
m_TerminationTimer (m_Service), m_TerminationTimerV6 (m_ServiceV6)
{
m_Socket.set_option (boost::asio::socket_base::receive_buffer_size (65535));
m_Socket.set_option (boost::asio::socket_base::send_buffer_size (65535));
OpenSocket ();
if (context.SupportsV6 ())
{
m_SocketV6.open (boost::asio::ip::udp::v6());
m_SocketV6.set_option (boost::asio::ip::v6_only (true));
m_SocketV6.set_option (boost::asio::socket_base::receive_buffer_size (65535));
m_SocketV6.set_option (boost::asio::socket_base::send_buffer_size (65535));
m_SocketV6.bind (m_EndpointV6);
}
OpenSocketV6 ();
}
SSUServer::~SSUServer ()
{
}
void SSUServer::OpenSocket ()
{
m_Socket.open (boost::asio::ip::udp::v4());
m_Socket.set_option (boost::asio::socket_base::receive_buffer_size (SSU_SOCKET_RECEIVE_BUFFER_SIZE));
m_Socket.set_option (boost::asio::socket_base::send_buffer_size (SSU_SOCKET_SEND_BUFFER_SIZE));
m_Socket.bind (m_Endpoint);
}
void SSUServer::OpenSocketV6 ()
{
m_SocketV6.open (boost::asio::ip::udp::v6());
m_SocketV6.set_option (boost::asio::ip::v6_only (true));
m_SocketV6.set_option (boost::asio::socket_base::receive_buffer_size (SSU_SOCKET_RECEIVE_BUFFER_SIZE));
m_SocketV6.set_option (boost::asio::socket_base::send_buffer_size (SSU_SOCKET_SEND_BUFFER_SIZE));
m_SocketV6.bind (m_EndpointV6);
}
void SSUServer::Start ()
{
m_IsRunning = true;
m_ReceiversThread = new std::thread (std::bind (&SSUServer::RunReceivers, this));
if (!m_OnlyV6)
{
m_ReceiversThread = new std::thread (std::bind (&SSUServer::RunReceivers, this));
m_Thread = new std::thread (std::bind (&SSUServer::Run, this));
m_ReceiversService.post (std::bind (&SSUServer::Receive, this));
ScheduleTermination ();
}
if (context.SupportsV6 ())
{
m_ReceiversThreadV6 = new std::thread (std::bind (&SSUServer::RunReceiversV6, this));
m_ThreadV6 = new std::thread (std::bind (&SSUServer::RunV6, this));
m_ReceiversService.post (std::bind (&SSUServer::ReceiveV6, this));
m_ReceiversServiceV6.post (std::bind (&SSUServer::ReceiveV6, this));
ScheduleTerminationV6 ();
}
SchedulePeerTestsCleanupTimer ();
ScheduleIntroducersUpdateTimer (); // wait for 30 seconds and decide if we need introducers
@@ -73,11 +85,14 @@ namespace transport
{
DeleteAllSessions ();
m_IsRunning = false;
m_TerminationTimer.cancel ();
m_TerminationTimerV6.cancel ();
m_Service.stop ();
m_Socket.close ();
m_ServiceV6.stop ();
m_SocketV6.close ();
m_ReceiversService.stop ();
m_ReceiversServiceV6.stop ();
if (m_ReceiversThread)
{
m_ReceiversThread->join ();
@@ -90,6 +105,12 @@ namespace transport
delete m_Thread;
m_Thread = nullptr;
}
if (m_ReceiversThreadV6)
{
m_ReceiversThreadV6->join ();
delete m_ReceiversThreadV6;
m_ReceiversThreadV6 = nullptr;
}
if (m_ThreadV6)
{
m_ThreadV6->join ();
@@ -143,16 +164,41 @@ namespace transport
}
}
void SSUServer::AddRelay (uint32_t tag, const boost::asio::ip::udp::endpoint& relay)
void SSUServer::RunReceiversV6 ()
{
while (m_IsRunning)
{
try
{
m_ReceiversServiceV6.run ();
}
catch (std::exception& ex)
{
LogPrint (eLogError, "SSU: v6 receivers runtime exception: ", ex.what ());
}
}
}
void SSUServer::AddRelay (uint32_t tag, std::shared_ptr<SSUSession> relay)
{
m_Relays[tag] = relay;
}
void SSUServer::RemoveRelay (uint32_t tag)
{
m_Relays.erase (tag);
}
std::shared_ptr<SSUSession> SSUServer::FindRelaySession (uint32_t tag)
{
auto it = m_Relays.find (tag);
if (it != m_Relays.end ())
return FindSession (it->second);
{
if (it->second->GetState () == eSessionStateEstablished)
return it->second;
else
m_Relays.erase (it);
}
return nullptr;
}
@@ -188,21 +234,40 @@ namespace transport
boost::system::error_code ec;
size_t moreBytes = m_Socket.available(ec);
while (moreBytes && packets.size () < 25)
{
packet = new SSUPacket ();
packet->len = m_Socket.receive_from (boost::asio::buffer (packet->buf, SSU_MTU_V4), packet->from);
packets.push_back (packet);
moreBytes = m_Socket.available();
}
if (!ec)
{
while (moreBytes && packets.size () < 25)
{
packet = new SSUPacket ();
packet->len = m_Socket.receive_from (boost::asio::buffer (packet->buf, SSU_MTU_V4), packet->from, 0, ec);
if (!ec)
{
packets.push_back (packet);
moreBytes = m_Socket.available(ec);
if (ec) break;
}
else
{
LogPrint (eLogError, "SSU: receive_from error: ", ec.message ());
delete packet;
break;
}
}
}
m_Service.post (std::bind (&SSUServer::HandleReceivedPackets, this, packets, &m_Sessions));
Receive ();
}
else
{
LogPrint (eLogError, "SSU: receive error: ", ecode.message ());
delete packet;
if (ecode != boost::asio::error::operation_aborted)
{
LogPrint (eLogError, "SSU: receive error: ", ecode.message ());
m_Socket.close ();
OpenSocket ();
Receive ();
}
}
}
@@ -214,22 +279,42 @@ namespace transport
std::vector<SSUPacket *> packets;
packets.push_back (packet);
size_t moreBytes = m_SocketV6.available ();
while (moreBytes && packets.size () < 25)
boost::system::error_code ec;
size_t moreBytes = m_SocketV6.available (ec);
if (!ec)
{
packet = new SSUPacket ();
packet->len = m_SocketV6.receive_from (boost::asio::buffer (packet->buf, SSU_MTU_V6), packet->from);
packets.push_back (packet);
moreBytes = m_SocketV6.available();
while (moreBytes && packets.size () < 25)
{
packet = new SSUPacket ();
packet->len = m_SocketV6.receive_from (boost::asio::buffer (packet->buf, SSU_MTU_V6), packet->from, 0, ec);
if (!ec)
{
packets.push_back (packet);
moreBytes = m_SocketV6.available(ec);
if (ec) break;
}
else
{
LogPrint (eLogError, "SSU: v6 receive_from error: ", ec.message ());
delete packet;
break;
}
}
}
m_ServiceV6.post (std::bind (&SSUServer::HandleReceivedPackets, this, packets, &m_SessionsV6));
ReceiveV6 ();
}
else
{
LogPrint (eLogError, "SSU: v6 receive error: ", ecode.message ());
delete packet;
if (ecode != boost::asio::error::operation_aborted)
{
LogPrint (eLogError, "SSU: v6 receive error: ", ecode.message ());
m_SocketV6.close ();
OpenSocketV6 ();
ReceiveV6 ();
}
}
}
@@ -292,9 +377,9 @@ namespace transport
return nullptr;
}
void SSUServer::CreateSession (std::shared_ptr<const i2p::data::RouterInfo> router, bool peerTest)
void SSUServer::CreateSession (std::shared_ptr<const i2p::data::RouterInfo> router, bool peerTest, bool v4only)
{
auto address = router->GetSSUAddress (!context.SupportsV6 ());
auto address = router->GetSSUAddress (v4only || !context.SupportsV6 ());
if (address)
CreateSession (router, address->host, address->port, peerTest);
else
@@ -357,7 +442,7 @@ namespace transport
return;
}
// create new session
int numIntroducers = address->introducers.size ();
int numIntroducers = address->ssu->introducers.size ();
if (numIntroducers > 0)
{
std::shared_ptr<SSUSession> introducerSession;
@@ -365,7 +450,7 @@ namespace transport
// we might have a session to introducer already
for (int i = 0; i < numIntroducers; i++)
{
auto intr = &(address->introducers[i]);
auto intr = &(address->ssu->introducers[i]);
boost::asio::ip::udp::endpoint ep (intr->iHost, intr->iPort);
if (ep.address ().is_v4 ()) // ipv4 only
{
@@ -640,6 +725,58 @@ namespace transport
SchedulePeerTestsCleanupTimer ();
}
}
void SSUServer::ScheduleTermination ()
{
m_TerminationTimer.expires_from_now (boost::posix_time::seconds(SSU_TERMINATION_CHECK_TIMEOUT));
m_TerminationTimer.async_wait (std::bind (&SSUServer::HandleTerminationTimer,
this, std::placeholders::_1));
}
void SSUServer::HandleTerminationTimer (const boost::system::error_code& ecode)
{
if (ecode != boost::asio::error::operation_aborted)
{
auto ts = i2p::util::GetSecondsSinceEpoch ();
for (auto& it: m_Sessions)
if (it.second->IsTerminationTimeoutExpired (ts))
{
auto session = it.second;
m_Service.post ([session]
{
LogPrint (eLogWarning, "SSU: no activity with ", session->GetRemoteEndpoint (), " for ", session->GetTerminationTimeout (), " seconds");
session->Failed ();
});
}
ScheduleTermination ();
}
}
void SSUServer::ScheduleTerminationV6 ()
{
m_TerminationTimerV6.expires_from_now (boost::posix_time::seconds(SSU_TERMINATION_CHECK_TIMEOUT));
m_TerminationTimerV6.async_wait (std::bind (&SSUServer::HandleTerminationTimerV6,
this, std::placeholders::_1));
}
void SSUServer::HandleTerminationTimerV6 (const boost::system::error_code& ecode)
{
if (ecode != boost::asio::error::operation_aborted)
{
auto ts = i2p::util::GetSecondsSinceEpoch ();
for (auto& it: m_SessionsV6)
if (it.second->IsTerminationTimeoutExpired (ts))
{
auto session = it.second;
m_ServiceV6.post ([session]
{
LogPrint (eLogWarning, "SSU: no activity with ", session->GetRemoteEndpoint (), " for ", session->GetTerminationTimeout (), " seconds");
session->Failed ();
});
}
ScheduleTerminationV6 ();
}
}
}
}

32
SSU.h
View File

@@ -23,11 +23,14 @@ namespace transport
const int SSU_KEEP_ALIVE_INTERVAL = 30; // 30 seconds
const int SSU_PEER_TEST_TIMEOUT = 60; // 60 seconds
const int SSU_TO_INTRODUCER_SESSION_DURATION = 3600; // 1 hour
const int SSU_TERMINATION_CHECK_TIMEOUT = 30; // 30 seconds
const size_t SSU_MAX_NUM_INTRODUCERS = 3;
const size_t SSU_SOCKET_RECEIVE_BUFFER_SIZE = 0x1FFFF; // 128K
const size_t SSU_SOCKET_SEND_BUFFER_SIZE = 0x1FFFF; // 128K
struct SSUPacket
{
i2p::crypto::AESAlignedBuffer<1500> buf;
i2p::crypto::AESAlignedBuffer<SSU_MTU_V6 + 18> buf; // max MTU + iv + size
boost::asio::ip::udp::endpoint from;
size_t len;
};
@@ -41,7 +44,7 @@ namespace transport
~SSUServer ();
void Start ();
void Stop ();
void CreateSession (std::shared_ptr<const i2p::data::RouterInfo> router, bool peerTest = false);
void CreateSession (std::shared_ptr<const i2p::data::RouterInfo> router, bool peerTest = false, bool v4only = false);
void CreateSession (std::shared_ptr<const i2p::data::RouterInfo> router,
const boost::asio::ip::address& addr, int port, bool peerTest = false);
void CreateDirectSession (std::shared_ptr<const i2p::data::RouterInfo> router, boost::asio::ip::udp::endpoint remoteEndpoint, bool peerTest);
@@ -56,7 +59,8 @@ namespace transport
boost::asio::io_service& GetServiceV6 () { return m_ServiceV6; };
const boost::asio::ip::udp::endpoint& GetEndpoint () const { return m_Endpoint; };
void Send (const uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& to);
void AddRelay (uint32_t tag, const boost::asio::ip::udp::endpoint& relay);
void AddRelay (uint32_t tag, std::shared_ptr<SSUSession> relay);
void RemoveRelay (uint32_t tag);
std::shared_ptr<SSUSession> FindRelaySession (uint32_t tag);
void NewPeerTest (uint32_t nonce, PeerTestParticipant role, std::shared_ptr<SSUSession> session = nullptr);
@@ -67,9 +71,12 @@ namespace transport
private:
void OpenSocket ();
void OpenSocketV6 ();
void Run ();
void RunV6 ();
void RunReceivers ();
void RunReceiversV6 ();
void Receive ();
void ReceiveV6 ();
void HandleReceivedFrom (const boost::system::error_code& ecode, std::size_t bytes_transferred, SSUPacket * packet);
@@ -90,6 +97,12 @@ namespace transport
void SchedulePeerTestsCleanupTimer ();
void HandlePeerTestsCleanupTimer (const boost::system::error_code& ecode);
// timer
void ScheduleTermination ();
void HandleTerminationTimer (const boost::system::error_code& ecode);
void ScheduleTerminationV6 ();
void HandleTerminationTimerV6 (const boost::system::error_code& ecode);
private:
struct PeerTest
@@ -101,17 +114,18 @@ namespace transport
bool m_OnlyV6;
bool m_IsRunning;
std::thread * m_Thread, * m_ThreadV6, * m_ReceiversThread;
boost::asio::io_service m_Service, m_ServiceV6, m_ReceiversService;
boost::asio::io_service::work m_Work, m_WorkV6, m_ReceiversWork;
std::thread * m_Thread, * m_ThreadV6, * m_ReceiversThread, * m_ReceiversThreadV6;
boost::asio::io_service m_Service, m_ServiceV6, m_ReceiversService, m_ReceiversServiceV6;
boost::asio::io_service::work m_Work, m_WorkV6, m_ReceiversWork, m_ReceiversWorkV6;
boost::asio::ip::udp::endpoint m_Endpoint, m_EndpointV6;
boost::asio::ip::udp::socket m_Socket, m_SocketV6;
boost::asio::deadline_timer m_IntroducersUpdateTimer, m_PeerTestsCleanupTimer;
boost::asio::deadline_timer m_IntroducersUpdateTimer, m_PeerTestsCleanupTimer,
m_TerminationTimer, m_TerminationTimerV6;
std::list<boost::asio::ip::udp::endpoint> m_Introducers; // introducers we are connected to
std::map<boost::asio::ip::udp::endpoint, std::shared_ptr<SSUSession> > m_Sessions, m_SessionsV6;
std::map<uint32_t, boost::asio::ip::udp::endpoint> m_Relays; // we are introducer
std::map<uint32_t, std::shared_ptr<SSUSession> > m_Relays; // we are introducer
std::map<uint32_t, PeerTest> m_PeerTests; // nonce -> creation time in milliseconds
public:
// for HTTP only
const decltype(m_Sessions)& GetSessions () const { return m_Sessions; };

View File

@@ -5,6 +5,9 @@
#include "NetDb.h"
#include "SSU.h"
#include "SSUData.h"
#ifdef WITH_EVENTS
#include "Event.h"
#endif
namespace i2p
{
@@ -49,25 +52,25 @@ namespace transport
void SSUData::AdjustPacketSize (std::shared_ptr<const i2p::data::RouterInfo> remoteRouter)
{
if (remoteRouter) return;
if (!remoteRouter) return;
auto ssuAddress = remoteRouter->GetSSUAddress ();
if (ssuAddress && ssuAddress->mtu)
if (ssuAddress && ssuAddress->ssu->mtu)
{
if (m_Session.IsV6 ())
m_PacketSize = ssuAddress->mtu - IPV6_HEADER_SIZE - UDP_HEADER_SIZE;
m_PacketSize = ssuAddress->ssu->mtu - IPV6_HEADER_SIZE - UDP_HEADER_SIZE;
else
m_PacketSize = ssuAddress->mtu - IPV4_HEADER_SIZE - UDP_HEADER_SIZE;
m_PacketSize = ssuAddress->ssu->mtu - IPV4_HEADER_SIZE - UDP_HEADER_SIZE;
if (m_PacketSize > 0)
{
// make sure packet size multiple of 16
m_PacketSize >>= 4;
m_PacketSize <<= 4;
if (m_PacketSize > m_MaxPacketSize) m_PacketSize = m_MaxPacketSize;
LogPrint (eLogDebug, "SSU: MTU=", ssuAddress->mtu, " packet size=", m_PacketSize);
LogPrint (eLogDebug, "SSU: MTU=", ssuAddress->ssu->mtu, " packet size=", m_PacketSize);
}
else
{
LogPrint (eLogWarning, "SSU: Unexpected MTU ", ssuAddress->mtu);
LogPrint (eLogWarning, "SSU: Unexpected MTU ", ssuAddress->ssu->mtu);
m_PacketSize = m_MaxPacketSize;
}
}
@@ -151,8 +154,7 @@ namespace transport
{
uint32_t msgID = bufbe32toh (buf); // message ID
buf += 4;
uint8_t frag[4];
frag[0] = 0;
uint8_t frag[4] = {0};
memcpy (frag + 1, buf, 3);
buf += 3;
uint32_t fragmentInfo = bufbe32toh (frag); // fragment info
@@ -234,8 +236,13 @@ namespace transport
{
m_ReceivedMessages.insert (msgID);
m_LastMessageReceivedTime = i2p::util::GetSecondsSinceEpoch ();
if (!msg->IsExpired ())
if (!msg->IsExpired ())
{
#ifdef WITH_EVENTS
QueueIntEvent("transport.recvmsg", m_Session.GetIdentHashBase64(), 1);
#endif
m_Handler.PutNextMessage (msg);
}
else
LogPrint (eLogDebug, "SSU: message expired");
}
@@ -363,7 +370,7 @@ namespace transport
void SSUData::SendMsgAck (uint32_t msgID)
{
uint8_t buf[48 + 18]; // actual length is 44 = 37 + 7 but pad it to multiple of 16
uint8_t buf[48 + 18] = {0}; // actual length is 44 = 37 + 7 but pad it to multiple of 16
uint8_t * payload = buf + sizeof (SSUHeader);
*payload = DATA_FLAG_EXPLICIT_ACKS_INCLUDED; // flag
payload++;
@@ -385,7 +392,7 @@ namespace transport
LogPrint (eLogWarning, "SSU: Fragment number ", fragmentNum, " exceeds 64");
return;
}
uint8_t buf[64 + 18];
uint8_t buf[64 + 18] = {0};
uint8_t * payload = buf + sizeof (SSUHeader);
*payload = DATA_FLAG_ACK_BITFIELDS_INCLUDED; // flag
payload++;
@@ -455,6 +462,7 @@ namespace transport
else
++it;
}
if (m_SentMessages.empty ()) return; // nothing to resend
if (numResent < MAX_OUTGOING_WINDOW_SIZE)
ScheduleResend ();
else

View File

@@ -21,13 +21,13 @@ namespace transport
#ifdef MESHNET
const size_t SSU_MTU_V6 = 1286;
#else
const size_t SSU_MTU_V6 = 1472;
const size_t SSU_MTU_V6 = 1488;
#endif
const size_t IPV4_HEADER_SIZE = 20;
const size_t IPV6_HEADER_SIZE = 40;
const size_t UDP_HEADER_SIZE = 8;
const size_t SSU_V4_MAX_PACKET_SIZE = SSU_MTU_V4 - IPV4_HEADER_SIZE - UDP_HEADER_SIZE; // 1456
const size_t SSU_V6_MAX_PACKET_SIZE = SSU_MTU_V6 - IPV6_HEADER_SIZE - UDP_HEADER_SIZE; // 1424
const size_t SSU_V6_MAX_PACKET_SIZE = SSU_MTU_V6 - IPV6_HEADER_SIZE - UDP_HEADER_SIZE; // 1440
const int RESEND_INTERVAL = 3; // in seconds
const int MAX_NUM_RESENDS = 5;
const int DECAY_INTERVAL = 20; // in seconds

View File

@@ -4,6 +4,7 @@
#include "Timestamp.h"
#include "RouterContext.h"
#include "Transports.h"
#include "NetDb.h"
#include "SSU.h"
#include "SSUSession.h"
@@ -14,22 +15,22 @@ namespace transport
SSUSession::SSUSession (SSUServer& server, boost::asio::ip::udp::endpoint& remoteEndpoint,
std::shared_ptr<const i2p::data::RouterInfo> router, bool peerTest ):
TransportSession (router, SSU_TERMINATION_TIMEOUT),
m_Server (server), m_RemoteEndpoint (remoteEndpoint), m_Timer (GetService ()),
m_Server (server), m_RemoteEndpoint (remoteEndpoint), m_ConnectTimer (GetService ()),
m_IsPeerTest (peerTest),m_State (eSessionStateUnknown), m_IsSessionKey (false),
m_RelayTag (0),m_Data (*this), m_IsDataReceived (false)
m_RelayTag (0), m_SentRelayTag (0), m_Data (*this), m_IsDataReceived (false)
{
if (router)
{
// we are client
auto address = router->GetSSUAddress (false);
if (address) m_IntroKey = address->key;
if (address) m_IntroKey = address->ssu->key;
m_Data.AdjustPacketSize (router); // mtu
}
else
{
// we are server
auto address = i2p::context.GetRouterInfo ().GetSSUAddress (false);
if (address) m_IntroKey = address->key;
if (address) m_IntroKey = address->ssu->key;
}
m_CreationTime = i2p::util::GetSecondsSinceEpoch ();
}
@@ -97,7 +98,7 @@ namespace transport
{
if (!len) return; // ignore zero-length packets
if (m_State == eSessionStateEstablished)
ScheduleTermination ();
m_LastActivityTimestamp = i2p::util::GetSecondsSinceEpoch ();
if (m_IsSessionKey && Validate (buf, len, m_MacKey)) // try session key first
DecryptSessionKey (buf, len);
@@ -115,8 +116,8 @@ namespace transport
LogPrint (eLogInfo, "SSU is not supported");
return;
}
if (Validate (buf, len, address->key))
Decrypt (buf, len, address->key);
if (Validate (buf, len, address->ssu->key))
Decrypt (buf, len, address->ssu->key);
else
{
LogPrint (eLogWarning, "SSU: MAC verification failed ", len, " bytes from ", senderEndpoint);
@@ -229,7 +230,7 @@ namespace transport
}
LogPrint (eLogDebug, "SSU message: session created");
m_Timer.cancel (); // connect timer
m_ConnectTimer.cancel (); // connect timer
SignedData s; // x,y, our IP, our port, remote IP, remote port, relayTag, signed on time
auto headerSize = GetSSUHeaderSize (buf);
if (headerSize >= len)
@@ -272,6 +273,16 @@ namespace transport
s.Insert (payload, 8); // relayTag and signed on time
m_RelayTag = bufbe32toh (payload);
payload += 4; // relayTag
if (i2p::context.GetStatus () == eRouterStatusTesting)
{
auto ts = i2p::util::GetSecondsSinceEpoch ();
uint32_t signedOnTime = bufbe32toh(payload);
if (signedOnTime < ts - SSU_CLOCK_SKEW || signedOnTime > ts + SSU_CLOCK_SKEW)
{
LogPrint (eLogError, "SSU: clock skew detected ", (int)ts - signedOnTime, ". Check your clock");
i2p::context.SetError (eRouterErrorClockSkew);
}
}
payload += 4; // signed on time
// decrypt signature
size_t signatureLen = m_RemoteIdentity->GetSignatureLen ();
@@ -307,9 +318,19 @@ namespace transport
payload++; // identity fragment info
uint16_t identitySize = bufbe16toh (payload);
payload += 2; // size of identity fragment
SetRemoteIdentity (std::make_shared<i2p::data::IdentityEx> (payload, identitySize));
auto identity = std::make_shared<i2p::data::IdentityEx> (payload, identitySize);
auto existing = i2p::data::netdb.FindRouter (identity->GetIdentHash ()); // check if exists already
SetRemoteIdentity (existing ? existing->GetRouterIdentity () : identity);
m_Data.UpdatePacketSize (m_RemoteIdentity->GetIdentHash ());
payload += identitySize; // identity
auto ts = i2p::util::GetSecondsSinceEpoch ();
uint32_t signedOnTime = bufbe32toh(payload);
if (signedOnTime < ts - SSU_CLOCK_SKEW || signedOnTime > ts + SSU_CLOCK_SKEW)
{
LogPrint (eLogError, "SSU message 'confirmed' time difference ", (int)ts - signedOnTime, " exceeds clock skew");
Failed ();
return;
}
if (m_SignedData)
m_SignedData->Insert (payload, 4); // insert Alice's signed on time
payload += 4; // signed-on time
@@ -332,7 +353,7 @@ namespace transport
void SSUSession::SendSessionRequest ()
{
uint8_t buf[320 + 18]; // 304 bytes for ipv4, 320 for ipv6
uint8_t buf[320 + 18] = {0}; // 304 bytes for ipv4, 320 for ipv6
uint8_t * payload = buf + sizeof (SSUHeader);
uint8_t flag = 0;
// fill extended options, 3 bytes extended options don't change message size
@@ -374,7 +395,7 @@ namespace transport
return;
}
uint8_t buf[96 + 18];
uint8_t buf[96 + 18] = {0};
uint8_t * payload = buf + sizeof (SSUHeader);
htobe32buf (payload, introducer.iTag);
payload += 4;
@@ -384,7 +405,7 @@ namespace transport
payload += 2;
*payload = 0; // challenge
payload++;
memcpy (payload, (const uint8_t *)address->key, 32);
memcpy (payload, (const uint8_t *)address->ssu->key, 32);
payload += 32;
htobe32buf (payload, nonce); // nonce
@@ -409,7 +430,7 @@ namespace transport
SignedData s; // x,y, remote IP, remote port, our IP, our port, relayTag, signed on time
s.Insert (x, 256); // x
uint8_t buf[384 + 18];
uint8_t buf[384 + 18] = {0};
uint8_t * payload = buf + sizeof (SSUHeader);
memcpy (payload, m_DHKeysPair->GetPublicKey (), 256);
s.Insert (payload, 256); // y
@@ -440,14 +461,12 @@ namespace transport
else
s.Insert (address->host.to_v6 ().to_bytes ().data (), 16); // our IP V6
s.Insert<uint16_t> (htobe16 (address->port)); // our port
uint32_t relayTag = 0;
if (sendRelayTag && i2p::context.GetRouterInfo ().IsIntroducer () && !IsV6 ())
{
RAND_bytes((uint8_t *)&relayTag, 4);
if (!relayTag) relayTag = 1;
m_Server.AddRelay (relayTag, m_RemoteEndpoint);
RAND_bytes((uint8_t *)&m_SentRelayTag, 4);
if (!m_SentRelayTag) m_SentRelayTag = 1;
}
htobe32buf (payload, relayTag);
htobe32buf (payload, m_SentRelayTag);
payload += 4; // relay tag
htobe32buf (payload, i2p::util::GetSecondsSinceEpoch ()); // signed on time
payload += 4;
@@ -457,14 +476,18 @@ namespace transport
m_SignedData = std::unique_ptr<SignedData>(new SignedData (s));
s.Insert (payload - 4, 4); // BOB's signed on time
s.Sign (i2p::context.GetPrivateKeys (), payload); // DSA signature
// TODO: fill padding with random data
uint8_t iv[16];
RAND_bytes (iv, 16); // random iv
// encrypt signature and padding with newly created session key
size_t signatureLen = i2p::context.GetIdentity ()->GetSignatureLen ();
size_t paddingSize = signatureLen & 0x0F; // %16
if (paddingSize > 0) signatureLen += (16 - paddingSize);
if (paddingSize > 0)
{
// fill random padding
RAND_bytes(payload + signatureLen, (16 - paddingSize));
signatureLen += (16 - paddingSize);
}
m_SessionKeyEncryption.SetIV (iv);
m_SessionKeyEncryption.Encrypt (payload, signatureLen, payload);
payload += signatureLen;
@@ -477,7 +500,7 @@ namespace transport
void SSUSession::SendSessionConfirmed (const uint8_t * y, const uint8_t * ourAddress, size_t ourAddressLen)
{
uint8_t buf[512 + 18];
uint8_t buf[512 + 18] = {0};
uint8_t * payload = buf + sizeof (SSUHeader);
*payload = 1; // 1 fragment
payload++; // info
@@ -492,9 +515,8 @@ namespace transport
auto signatureLen = i2p::context.GetIdentity ()->GetSignatureLen ();
size_t paddingSize = ((payload - buf) + signatureLen)%16;
if (paddingSize > 0) paddingSize = 16 - paddingSize;
// TODO: fill padding
RAND_bytes(payload, paddingSize); // fill padding with random
payload += paddingSize; // padding size
// signature
SignedData s; // x,y, our IP, our port, remote IP, remote port, relayTag, our signed on time
s.Insert (m_DHKeysPair->GetPublicKey (), 256); // x
@@ -543,14 +565,14 @@ namespace transport
void SSUSession::SendRelayResponse (uint32_t nonce, const boost::asio::ip::udp::endpoint& from,
const uint8_t * introKey, const boost::asio::ip::udp::endpoint& to)
{
uint8_t buf[80 + 18]; // 64 Alice's ipv4 and 80 Alice's ipv6
uint8_t * payload = buf + sizeof (SSUHeader);
// Charlie's address always v4
if (!to.address ().is_v4 ())
{
LogPrint (eLogWarning, "SSU: Charlie's IP must be v4");
return;
}
uint8_t buf[80 + 18] = {0}; // 64 Alice's ipv4 and 80 Alice's ipv6
uint8_t * payload = buf + sizeof (SSUHeader);
*payload = 4;
payload++; // size
htobe32buf (payload, to.address ().to_v4 ().to_ulong ()); // Charlie's IP
@@ -603,7 +625,7 @@ namespace transport
LogPrint (eLogWarning, "SSU: Alice's IP must be v4");
return;
}
uint8_t buf[48 + 18];
uint8_t buf[48 + 18] = {0};
uint8_t * payload = buf + sizeof (SSUHeader);
*payload = 4;
payload++; // size
@@ -804,9 +826,9 @@ namespace transport
void SSUSession::ScheduleConnectTimer ()
{
m_Timer.cancel ();
m_Timer.expires_from_now (boost::posix_time::seconds(SSU_CONNECT_TIMEOUT));
m_Timer.async_wait (std::bind (&SSUSession::HandleConnectTimer,
m_ConnectTimer.cancel ();
m_ConnectTimer.expires_from_now (boost::posix_time::seconds(SSU_CONNECT_TIMEOUT));
m_ConnectTimer.async_wait (std::bind (&SSUSession::HandleConnectTimer,
shared_from_this (), std::placeholders::_1));
}
@@ -826,8 +848,8 @@ namespace transport
if (m_State == eSessionStateUnknown)
{
// set connect timer
m_Timer.expires_from_now (boost::posix_time::seconds(SSU_CONNECT_TIMEOUT));
m_Timer.async_wait (std::bind (&SSUSession::HandleConnectTimer,
m_ConnectTimer.expires_from_now (boost::posix_time::seconds(SSU_CONNECT_TIMEOUT));
m_ConnectTimer.async_wait (std::bind (&SSUSession::HandleConnectTimer,
shared_from_this (), std::placeholders::_1));
}
uint32_t nonce;
@@ -840,8 +862,8 @@ namespace transport
{
m_State = eSessionStateIntroduced;
// set connect timer
m_Timer.expires_from_now (boost::posix_time::seconds(SSU_CONNECT_TIMEOUT));
m_Timer.async_wait (std::bind (&SSUSession::HandleConnectTimer,
m_ConnectTimer.expires_from_now (boost::posix_time::seconds(SSU_CONNECT_TIMEOUT));
m_ConnectTimer.async_wait (std::bind (&SSUSession::HandleConnectTimer,
shared_from_this (), std::placeholders::_1));
}
@@ -851,7 +873,9 @@ namespace transport
SendSesionDestroyed ();
transports.PeerDisconnected (shared_from_this ());
m_Data.Stop ();
m_Timer.cancel ();
m_ConnectTimer.cancel ();
if (m_SentRelayTag)
m_Server.RemoveRelay (m_SentRelayTag); // relay tag is not valid anymore
}
void SSUSession::Done ()
@@ -868,7 +892,9 @@ namespace transport
transports.PeerConnected (shared_from_this ());
if (m_IsPeerTest)
SendPeerTest ();
ScheduleTermination ();
if (m_SentRelayTag)
m_Server.AddRelay (m_SentRelayTag, shared_from_this ());
m_LastActivityTimestamp = i2p::util::GetSecondsSinceEpoch ();
}
void SSUSession::Failed ()
@@ -880,24 +906,6 @@ namespace transport
}
}
void SSUSession::ScheduleTermination ()
{
m_Timer.cancel ();
m_Timer.expires_from_now (boost::posix_time::seconds(GetTerminationTimeout ()));
m_Timer.async_wait (std::bind (&SSUSession::HandleTerminationTimer,
shared_from_this (), std::placeholders::_1));
}
void SSUSession::HandleTerminationTimer (const boost::system::error_code& ecode)
{
if (ecode != boost::asio::error::operation_aborted)
{
LogPrint (eLogWarning, "SSU: no activity with ", m_RemoteEndpoint, " for ", GetTerminationTimeout (), " seconds");
Failed ();
}
}
void SSUSession::SendI2NPMessages (const std::vector<std::shared_ptr<I2NPMessage> >& msgs)
{
GetService ().post (std::bind (&SSUSession::PostI2NPMessages, shared_from_this (), msgs));
@@ -944,7 +952,7 @@ namespace transport
// existing test
case ePeerTestParticipantAlice1:
{
if (m_State == eSessionStateEstablished)
if (m_Server.GetPeerTestSession (nonce) == shared_from_this ()) // Alice-Bob
{
LogPrint (eLogDebug, "SSU: peer test from Bob. We are Alice");
if (i2p::context.GetStatus () == eRouterStatusTesting) // still not OK
@@ -953,6 +961,8 @@ namespace transport
else
{
LogPrint (eLogDebug, "SSU: first peer test from Charlie. We are Alice");
if (m_State == eSessionStateEstablished)
LogPrint (eLogWarning, "SSU: first peer test from Charlie through established session. We are Alice");
i2p::context.SetStatus (eRouterStatusOK);
m_Server.UpdatePeerTest (nonce, ePeerTestParticipantAlice2);
SendPeerTest (nonce, senderEndpoint.address (), senderEndpoint.port (), introKey, true, false); // to Charlie
@@ -961,7 +971,7 @@ namespace transport
}
case ePeerTestParticipantAlice2:
{
if (m_State == eSessionStateEstablished)
if (m_Server.GetPeerTestSession (nonce) == shared_from_this ()) // Alice-Bob
LogPrint (eLogDebug, "SSU: peer test from Bob. We are Alice");
else
{
@@ -1009,7 +1019,7 @@ namespace transport
else // v6
{
boost::asio::ip::address_v6::bytes_type bytes;
memcpy (bytes.data (), address, 6);
memcpy (bytes.data (), address, 16);
addr = boost::asio::ip::address_v6 (bytes);
}
SendPeerTest (nonce, addr, be16toh (port), introKey); // to Alice with her address received from Bob
@@ -1036,7 +1046,7 @@ namespace transport
// toAddress is true for Alice<->Chalie communications only
// sendAddress is false if message comes from Alice
{
uint8_t buf[80 + 18];
uint8_t buf[80 + 18] = {0};
uint8_t iv[16];
uint8_t * payload = buf + sizeof (SSUHeader);
htobe32buf (payload, nonce);
@@ -1051,7 +1061,7 @@ namespace transport
}
else if (address.is_v6 ())
{
*payload = 6;
*payload = 16;
memcpy (payload + 1, address.to_v6 ().to_bytes ().data (), 16); // our IP V6
}
else
@@ -1071,7 +1081,7 @@ namespace transport
// send our intro key to address instead it's own
auto addr = i2p::context.GetRouterInfo ().GetSSUAddress ();
if (addr)
memcpy (payload, addr->key, 32); // intro key
memcpy (payload, addr->ssu->key, 32); // intro key
else
LogPrint (eLogInfo, "SSU is not supported. Can't send peer test");
}
@@ -1092,14 +1102,14 @@ namespace transport
// encrypt message with session key
FillHeaderAndEncrypt (PAYLOAD_TYPE_PEER_TEST, buf, 80);
Send (buf, 80);
}
}
}
void SSUSession::SendPeerTest ()
{
// we are Alice
LogPrint (eLogDebug, "SSU: sending peer test");
auto address = i2p::context.GetRouterInfo ().GetSSUAddress (false);
auto address = i2p::context.GetRouterInfo ().GetSSUAddress (i2p::context.SupportsV4 ());
if (!address)
{
LogPrint (eLogInfo, "SSU is not supported. Can't send peer test");
@@ -1109,15 +1119,15 @@ namespace transport
RAND_bytes ((uint8_t *)&nonce, 4);
if (!nonce) nonce = 1;
m_IsPeerTest = false;
m_Server.NewPeerTest (nonce, ePeerTestParticipantAlice1);
SendPeerTest (nonce, boost::asio::ip::address(), 0, address->key, false, false); // address and port always zero for Alice
m_Server.NewPeerTest (nonce, ePeerTestParticipantAlice1, shared_from_this ());
SendPeerTest (nonce, boost::asio::ip::address(), 0, address->ssu->key, false, false); // address and port always zero for Alice
}
void SSUSession::SendKeepAlive ()
{
if (m_State == eSessionStateEstablished)
{
uint8_t buf[48 + 18];
uint8_t buf[48 + 18] = {0};
uint8_t * payload = buf + sizeof (SSUHeader);
*payload = 0; // flags
payload++;
@@ -1126,7 +1136,7 @@ namespace transport
FillHeaderAndEncrypt (PAYLOAD_TYPE_DATA, buf, 48);
Send (buf, 48);
LogPrint (eLogDebug, "SSU: keep-alive sent");
ScheduleTermination ();
m_LastActivityTimestamp = i2p::util::GetSecondsSinceEpoch ();
}
}
@@ -1134,7 +1144,7 @@ namespace transport
{
if (m_IsSessionKey)
{
uint8_t buf[48 + 18];
uint8_t buf[48 + 18] = {0};
// encrypt message with session key
FillHeaderAndEncrypt (PAYLOAD_TYPE_SESSION_DESTROYED, buf, 48);
try
@@ -1151,7 +1161,7 @@ namespace transport
void SSUSession::Send (uint8_t type, const uint8_t * payload, size_t len)
{
uint8_t buf[SSU_MTU_V4 + 18];
uint8_t buf[SSU_MTU_V4 + 18] = {0};
size_t msgSize = len + sizeof (SSUHeader);
size_t paddingSize = msgSize & 0x0F; // %16
if (paddingSize > 0) msgSize += (16 - paddingSize);

View File

@@ -27,6 +27,7 @@ namespace transport
const int SSU_CONNECT_TIMEOUT = 5; // 5 seconds
const int SSU_TERMINATION_TIMEOUT = 330; // 5.5 minutes
const int SSU_CLOCK_SKEW = 60; // in seconds
// payload types (4 bits)
const uint8_t PAYLOAD_TYPE_SESSION_REQUEST = 0;
@@ -77,6 +78,7 @@ namespace transport
void WaitForIntroduction ();
void Close ();
void Done ();
void Failed ();
boost::asio::ip::udp::endpoint& GetRemoteEndpoint () { return m_RemoteEndpoint; };
bool IsV6 () const { return m_RemoteEndpoint.address ().is_v6 (); };
void SendI2NPMessages (const std::vector<std::shared_ptr<I2NPMessage> >& msgs);
@@ -114,7 +116,6 @@ namespace transport
void ProcessRelayResponse (const uint8_t * buf, size_t len);
void ProcessRelayIntro (const uint8_t * buf, size_t len);
void Established ();
void Failed ();
void ScheduleConnectTimer ();
void HandleConnectTimer (const boost::system::error_code& ecode);
void ProcessPeerTest (const uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint);
@@ -131,19 +132,17 @@ namespace transport
void DecryptSessionKey (uint8_t * buf, size_t len);
bool Validate (uint8_t * buf, size_t len, const i2p::crypto::MACKey& macKey);
void ScheduleTermination ();
void HandleTerminationTimer (const boost::system::error_code& ecode);
private:
friend class SSUData; // TODO: change in later
SSUServer& m_Server;
boost::asio::ip::udp::endpoint m_RemoteEndpoint;
boost::asio::deadline_timer m_Timer;
boost::asio::deadline_timer m_ConnectTimer;
bool m_IsPeerTest;
SessionState m_State;
bool m_IsSessionKey;
uint32_t m_RelayTag;
uint32_t m_RelayTag; // received from peer
uint32_t m_SentRelayTag; // sent by us
i2p::crypto::CBCEncryption m_SessionKeyEncryption;
i2p::crypto::CBCDecryption m_SessionKeyDecryption;
i2p::crypto::AESKey m_SessionKey;

View File

@@ -467,17 +467,27 @@ namespace crypto
return GetEd25519 ()->Verify (m_PublicKey, digest, signature);
}
EDDSA25519Signer::EDDSA25519Signer (const uint8_t * signingPrivateKey)
EDDSA25519Signer::EDDSA25519Signer (const uint8_t * signingPrivateKey, const uint8_t * signingPublicKey)
{
// expand key
SHA512 (signingPrivateKey, EDDSA25519_PRIVATE_KEY_LENGTH, m_ExpandedPrivateKey);
m_ExpandedPrivateKey[0] &= 0xF8; // drop last 3 bits
m_ExpandedPrivateKey[EDDSA25519_PRIVATE_KEY_LENGTH - 1] &= 0x1F; // drop first 3 bits
m_ExpandedPrivateKey[EDDSA25519_PRIVATE_KEY_LENGTH - 1] &= 0x3F; // drop first 2 bits
m_ExpandedPrivateKey[EDDSA25519_PRIVATE_KEY_LENGTH - 1] |= 0x40; // set second bit
// generate and encode public key
BN_CTX * ctx = BN_CTX_new ();
auto publicKey = GetEd25519 ()->GeneratePublicKey (m_ExpandedPrivateKey, ctx);
GetEd25519 ()->EncodePublicKey (publicKey, m_PublicKeyEncoded, ctx);
if (signingPublicKey && memcmp (m_PublicKeyEncoded, signingPublicKey, EDDSA25519_PUBLIC_KEY_LENGTH))
{
// keys don't match, it means older key with 0x1F
LogPrint (eLogWarning, "Older EdDSA key detected");
m_ExpandedPrivateKey[EDDSA25519_PRIVATE_KEY_LENGTH - 1] &= 0xDF; // drop third bit
publicKey = GetEd25519 ()->GeneratePublicKey (m_ExpandedPrivateKey, ctx);
GetEd25519 ()->EncodePublicKey (publicKey, m_PublicKeyEncoded, ctx);
}
BN_CTX_free (ctx);
}

View File

@@ -43,7 +43,7 @@ namespace crypto
DSAVerifier (const uint8_t * signingKey)
{
m_PublicKey = CreateDSA ();
m_PublicKey->pub_key = BN_bin2bn (signingKey, DSA_PUBLIC_KEY_LENGTH, NULL);
DSA_set0_key (m_PublicKey, BN_bin2bn (signingKey, DSA_PUBLIC_KEY_LENGTH, NULL), NULL);
}
~DSAVerifier ()
@@ -58,8 +58,7 @@ namespace crypto
SHA1 (buf, len, digest);
// signature
DSA_SIG * sig = DSA_SIG_new();
sig->r = BN_bin2bn (signature, DSA_SIGNATURE_LENGTH/2, NULL);
sig->s = BN_bin2bn (signature + DSA_SIGNATURE_LENGTH/2, DSA_SIGNATURE_LENGTH/2, NULL);
DSA_SIG_set0 (sig, BN_bin2bn (signature, DSA_SIGNATURE_LENGTH/2, NULL), BN_bin2bn (signature + DSA_SIGNATURE_LENGTH/2, DSA_SIGNATURE_LENGTH/2, NULL));
// DSA verification
int ret = DSA_do_verify (digest, 20, sig, m_PublicKey);
DSA_SIG_free(sig);
@@ -78,10 +77,11 @@ namespace crypto
{
public:
DSASigner (const uint8_t * signingPrivateKey)
DSASigner (const uint8_t * signingPrivateKey, const uint8_t * signingPublicKey)
// openssl 1.1 always requires DSA public key even for signing
{
m_PrivateKey = CreateDSA ();
m_PrivateKey->priv_key = BN_bin2bn (signingPrivateKey, DSA_PRIVATE_KEY_LENGTH, NULL);
DSA_set0_key (m_PrivateKey, BN_bin2bn (signingPublicKey, DSA_PUBLIC_KEY_LENGTH, NULL), BN_bin2bn (signingPrivateKey, DSA_PRIVATE_KEY_LENGTH, NULL));
}
~DSASigner ()
@@ -94,8 +94,10 @@ namespace crypto
uint8_t digest[20];
SHA1 (buf, len, digest);
DSA_SIG * sig = DSA_do_sign (digest, 20, m_PrivateKey);
bn2buf (sig->r, signature, DSA_SIGNATURE_LENGTH/2);
bn2buf (sig->s, signature + DSA_SIGNATURE_LENGTH/2, DSA_SIGNATURE_LENGTH/2);
const BIGNUM * r, * s;
DSA_SIG_get0 (sig, &r, &s);
bn2buf (r, signature, DSA_SIGNATURE_LENGTH/2);
bn2buf (s, signature + DSA_SIGNATURE_LENGTH/2, DSA_SIGNATURE_LENGTH/2);
DSA_SIG_free(sig);
}
@@ -108,10 +110,11 @@ namespace crypto
{
DSA * dsa = CreateDSA ();
DSA_generate_key (dsa);
bn2buf (dsa->priv_key, signingPrivateKey, DSA_PRIVATE_KEY_LENGTH);
bn2buf (dsa->pub_key, signingPublicKey, DSA_PUBLIC_KEY_LENGTH);
DSA_free (dsa);
const BIGNUM * pub_key, * priv_key;
DSA_get0_key(dsa, &pub_key, &priv_key);
bn2buf (priv_key, signingPrivateKey, DSA_PRIVATE_KEY_LENGTH);
bn2buf (pub_key, signingPublicKey, DSA_PUBLIC_KEY_LENGTH);
DSA_free (dsa);
}
struct SHA256Hash
@@ -152,9 +155,10 @@ namespace crypto
ECDSAVerifier (const uint8_t * signingKey)
{
m_PublicKey = EC_KEY_new_by_curve_name (curve);
EC_KEY_set_public_key_affine_coordinates (m_PublicKey,
BN_bin2bn (signingKey, keyLen/2, NULL),
BN_bin2bn (signingKey + keyLen/2, keyLen/2, NULL));
BIGNUM * x = BN_bin2bn (signingKey, keyLen/2, NULL);
BIGNUM * y = BN_bin2bn (signingKey + keyLen/2, keyLen/2, NULL);
EC_KEY_set_public_key_affine_coordinates (m_PublicKey, x, y);
BN_free (x); BN_free (y);
}
~ECDSAVerifier ()
@@ -167,8 +171,9 @@ namespace crypto
uint8_t digest[Hash::hashLen];
Hash::CalculateHash (buf, len, digest);
ECDSA_SIG * sig = ECDSA_SIG_new();
sig->r = BN_bin2bn (signature, GetSignatureLen ()/2, NULL);
sig->s = BN_bin2bn (signature + GetSignatureLen ()/2, GetSignatureLen ()/2, NULL);
auto r = BN_bin2bn (signature, GetSignatureLen ()/2, NULL);
auto s = BN_bin2bn (signature + GetSignatureLen ()/2, GetSignatureLen ()/2, NULL);
ECDSA_SIG_set0(sig, r, s);
// ECDSA verification
int ret = ECDSA_do_verify (digest, Hash::hashLen, sig, m_PublicKey);
ECDSA_SIG_free(sig);
@@ -205,9 +210,11 @@ namespace crypto
uint8_t digest[Hash::hashLen];
Hash::CalculateHash (buf, len, digest);
ECDSA_SIG * sig = ECDSA_do_sign (digest, Hash::hashLen, m_PrivateKey);
const BIGNUM * r, * s;
ECDSA_SIG_get0 (sig, &r, &s);
// signatureLen = keyLen
bn2buf (sig->r, signature, keyLen/2);
bn2buf (sig->s, signature + keyLen/2, keyLen/2);
bn2buf (r, signature, keyLen/2);
bn2buf (s, signature + keyLen/2, keyLen/2);
ECDSA_SIG_free(sig);
}
@@ -269,9 +276,7 @@ namespace crypto
RSAVerifier (const uint8_t * signingKey)
{
m_PublicKey = RSA_new ();
memset (m_PublicKey, 0, sizeof (RSA));
m_PublicKey->e = BN_dup (GetRSAE ());
m_PublicKey->n = BN_bin2bn (signingKey, keyLen, NULL);
RSA_set0_key (m_PublicKey, BN_bin2bn (signingKey, keyLen, NULL) /* n */ , BN_dup (GetRSAE ()) /* d */, NULL);
}
~RSAVerifier ()
@@ -303,10 +308,8 @@ namespace crypto
RSASigner (const uint8_t * signingPrivateKey)
{
m_PrivateKey = RSA_new ();
memset (m_PrivateKey, 0, sizeof (RSA));
m_PrivateKey->e = BN_dup (GetRSAE ());
m_PrivateKey->n = BN_bin2bn (signingPrivateKey, keyLen, NULL);
m_PrivateKey->d = BN_bin2bn (signingPrivateKey + keyLen, keyLen, NULL);
RSA_set0_key (m_PrivateKey, BN_bin2bn (signingPrivateKey, keyLen, NULL), /* n */
BN_dup (GetRSAE ()) /* e */, BN_bin2bn (signingPrivateKey + keyLen, keyLen, NULL) /* d */);
}
~RSASigner ()
@@ -332,9 +335,11 @@ namespace crypto
RSA * rsa = RSA_new ();
BIGNUM * e = BN_dup (GetRSAE ()); // make it non-const
RSA_generate_key_ex (rsa, publicKeyLen*8, e, NULL);
bn2buf (rsa->n, signingPrivateKey, publicKeyLen);
bn2buf (rsa->d, signingPrivateKey + publicKeyLen, publicKeyLen);
bn2buf (rsa->n, signingPublicKey, publicKeyLen);
const BIGNUM * n, * d, * e1;
RSA_get0_key (rsa, &n, &e1, &d);
bn2buf (n, signingPrivateKey, publicKeyLen);
bn2buf (d, signingPrivateKey + publicKeyLen, publicKeyLen);
bn2buf (n, signingPublicKey, publicKeyLen);
BN_free (e); // this e is not assigned to rsa->e
RSA_free (rsa);
}
@@ -419,7 +424,8 @@ namespace crypto
{
public:
EDDSA25519Signer (const uint8_t * signingPrivateKey);
EDDSA25519Signer (const uint8_t * signingPrivateKey, const uint8_t * signingPublicKey = nullptr);
// we pass signingPublicKey to check if it matches private key
void Sign (const uint8_t * buf, int len, uint8_t * signature) const;
const uint8_t * GetPublicKey () const { return m_PublicKeyEncoded; };

File diff suppressed because it is too large Load Diff

View File

@@ -18,6 +18,7 @@
#include "I2NPProtocol.h"
#include "Garlic.h"
#include "Tunnel.h"
#include "util.h" // MemoryPool
namespace i2p
{
@@ -51,6 +52,23 @@ namespace stream
const int INITIAL_RTO = 9000; // in milliseconds
const size_t MAX_PENDING_INCOMING_BACKLOG = 128;
const int PENDING_INCOMING_TIMEOUT = 10; // in seconds
const int MAX_RECEIVE_TIMEOUT = 30; // in seconds
/** i2cp option for limiting inbound stremaing connections */
const char I2CP_PARAM_STREAMING_MAX_CONNS_PER_MIN[] = "maxconns";
/** default maximum connections attempts per minute per destination */
const uint32_t DEFAULT_MAX_CONNS_PER_MIN = 600;
/**
* max banned destinations per local destination
* TODO: make configurable
*/
const uint16_t MAX_BANNED_CONNS = 9999;
/**
* length of a ban in ms
* TODO: make configurable
*/
const uint64_t DEFAULT_BAN_INTERVAL = 60 * 60 * 1000;
struct Packet
{
@@ -134,16 +152,20 @@ namespace stream
size_t GetSendBufferSize () const { return m_SendBuffer.rdbuf ()->in_avail (); };
int GetWindowSize () const { return m_WindowSize; };
int GetRTT () const { return m_RTT; };
/** don't call me */
void Terminate ();
private:
void Terminate ();
void CleanUp ();
void SendBuffer ();
void SendQuickAck ();
void SendClose ();
bool SendPacket (Packet * packet);
void SendPackets (const std::vector<Packet *>& packets);
void SendUpdatedLeaseSet ();
void SavePacket (Packet * packet);
void ProcessPacket (Packet * packet);
@@ -153,7 +175,7 @@ namespace stream
void UpdateCurrentRemoteLease (bool expired = false);
template<typename Buffer, typename ReceiveHandler>
void HandleReceiveTimer (const boost::system::error_code& ecode, const Buffer& buffer, ReceiveHandler handler);
void HandleReceiveTimer (const boost::system::error_code& ecode, const Buffer& buffer, ReceiveHandler handler, int remainingTimeout);
void ScheduleResend ();
void HandleResendTimer (const boost::system::error_code& ecode);
@@ -204,18 +226,34 @@ namespace stream
void SetAcceptor (const Acceptor& acceptor);
void ResetAcceptor ();
bool IsAcceptorSet () const { return m_Acceptor != nullptr; };
void AcceptOnce (const Acceptor& acceptor);
std::shared_ptr<i2p::client::ClientDestination> GetOwner () const { return m_Owner; };
void SetOwner (std::shared_ptr<i2p::client::ClientDestination> owner) { m_Owner = owner; };
uint16_t GetLocalPort () const { return m_LocalPort; };
void HandleDataMessagePayload (const uint8_t * buf, size_t len);
std::shared_ptr<I2NPMessage> CreateDataMessage (const uint8_t * payload, size_t len, uint16_t toPort);
/** set max connections per minute per destination */
void SetMaxConnsPerMinute(const uint32_t conns);
Packet * NewPacket () { return m_PacketsPool.Acquire (); };
void DeletePacket (Packet * p) { if (p) m_PacketsPool.Release (p); };
private:
void HandleNextPacket (Packet * packet);
std::shared_ptr<Stream> CreateNewIncomingStream ();
void HandlePendingIncomingTimer (const boost::system::error_code& ecode);
/** handle cleaning up connection tracking for ratelimits */
void HandleConnTrack(const boost::system::error_code& ecode);
bool DropNewStream(const i2p::data::IdentHash & ident);
void ScheduleConnTrack();
private:
std::shared_ptr<i2p::client::ClientDestination> m_Owner;
@@ -224,9 +262,21 @@ namespace stream
std::mutex m_StreamsMutex;
std::map<uint32_t, std::shared_ptr<Stream> > m_Streams; // sendStreamID->stream
Acceptor m_Acceptor;
uint32_t m_LastIncomingReceiveStreamID;
std::list<std::shared_ptr<Stream> > m_PendingIncomingStreams;
boost::asio::deadline_timer m_PendingIncomingTimer;
std::map<uint32_t, std::list<Packet *> > m_SavedPackets; // receiveStreamID->packets, arrived before SYN
std::mutex m_ConnsMutex;
/** how many connections per minute did each identity have */
std::map<i2p::data::IdentHash, uint32_t> m_Conns;
boost::asio::deadline_timer m_ConnTrackTimer;
uint32_t m_ConnsPerMinute;
/** banned identities */
std::vector<i2p::data::IdentHash> m_Banned;
uint64_t m_LastBanClear;
i2p::util::MemoryPool<Packet> m_PacketsPool;
public:
@@ -246,18 +296,19 @@ namespace stream
m_Service.post ([=](void)
{
if (!m_ReceiveQueue.empty () || m_Status == eStreamStatusReset)
s->HandleReceiveTimer (boost::asio::error::make_error_code (boost::asio::error::operation_aborted), buffer, handler);
s->HandleReceiveTimer (boost::asio::error::make_error_code (boost::asio::error::operation_aborted), buffer, handler, 0);
else
{
s->m_ReceiveTimer.expires_from_now (boost::posix_time::seconds(timeout));
int t = (timeout > MAX_RECEIVE_TIMEOUT) ? MAX_RECEIVE_TIMEOUT : timeout;
s->m_ReceiveTimer.expires_from_now (boost::posix_time::seconds(t));
s->m_ReceiveTimer.async_wait ([=](const boost::system::error_code& ecode)
{ s->HandleReceiveTimer (ecode, buffer, handler); });
{ s->HandleReceiveTimer (ecode, buffer, handler, timeout - t); });
}
});
}
template<typename Buffer, typename ReceiveHandler>
void Stream::HandleReceiveTimer (const boost::system::error_code& ecode, const Buffer& buffer, ReceiveHandler handler)
void Stream::HandleReceiveTimer (const boost::system::error_code& ecode, const Buffer& buffer, ReceiveHandler handler, int remainingTimeout)
{
size_t received = ConcatenatePackets (boost::asio::buffer_cast<uint8_t *>(buffer), boost::asio::buffer_size(buffer));
if (received > 0)
@@ -271,8 +322,17 @@ namespace stream
handler (boost::asio::error::make_error_code (boost::asio::error::operation_aborted), 0);
}
else
{
// timeout expired
handler (boost::asio::error::make_error_code (boost::asio::error::timed_out), received);
if (remainingTimeout <= 0)
handler (boost::asio::error::make_error_code (boost::asio::error::timed_out), received);
else
{
// itermediate iterrupt
SendUpdatedLeaseSet (); // send our leaseset if applicable
AsyncReceive (buffer, handler, remainingTimeout);
}
}
}
}
}

135
Tag.h
View File

@@ -1,3 +1,6 @@
#ifndef TAG_H__
#define TAG_H__
/*
* Copyright (c) 2013-2016, The PurpleI2P Project
*
@@ -6,84 +9,86 @@
* See full license text in LICENSE file at top of project tree
*/
#ifndef TAG_H__
#define TAG_H__
#include <string.h> /* memcpy */
#include <boost/static_assert.hpp>
#include <string.h>
#include <openssl/rand.h>
#include "Base.h"
namespace i2p {
namespace data {
template<int sz>
class Tag
template<size_t sz>
class Tag
{
BOOST_STATIC_ASSERT_MSG(sz % 8 == 0, "Tag size must be multiple of 8 bytes");
public:
Tag () = default;
Tag (const uint8_t * buf) { memcpy (m_Buf, buf, sz); }
bool operator== (const Tag& other) const { return !memcmp (m_Buf, other.m_Buf, sz); }
bool operator< (const Tag& other) const { return memcmp (m_Buf, other.m_Buf, sz) < 0; }
uint8_t * operator()() { return m_Buf; }
const uint8_t * operator()() const { return m_Buf; }
operator uint8_t * () { return m_Buf; }
operator const uint8_t * () const { return m_Buf; }
const uint8_t * data() const { return m_Buf; }
const uint64_t * GetLL () const { return ll; }
bool IsZero () const
{
public:
for (size_t i = 0; i < sz/8; ++i)
if (ll[i]) return false;
return true;
}
Tag (const uint8_t * buf) { memcpy (m_Buf, buf, sz); };
Tag (const Tag<sz>& ) = default;
#ifndef _WIN32 // FIXME!!! msvs 2013 can't compile it
Tag (Tag<sz>&& ) = default;
#endif
Tag () = default;
void Fill(uint8_t c)
{
memset(m_Buf, c, sz);
}
Tag<sz>& operator= (const Tag<sz>& ) = default;
#ifndef _WIN32
Tag<sz>& operator= (Tag<sz>&& ) = default;
#endif
void Randomize()
{
RAND_bytes(m_Buf, sz);
}
std::string ToBase64 () const
{
char str[sz*2];
size_t l = i2p::data::ByteStreamToBase64 (m_Buf, sz, str, sz*2);
return std::string (str, str + l);
}
uint8_t * operator()() { return m_Buf; };
const uint8_t * operator()() const { return m_Buf; };
std::string ToBase32 () const
{
char str[sz*2];
size_t l = i2p::data::ByteStreamToBase32 (m_Buf, sz, str, sz*2);
return std::string (str, str + l);
}
operator uint8_t * () { return m_Buf; };
operator const uint8_t * () const { return m_Buf; };
void FromBase32 (const std::string& s)
{
i2p::data::Base32ToByteStream (s.c_str (), s.length (), m_Buf, sz);
}
const uint64_t * GetLL () const { return ll; };
void FromBase64 (const std::string& s)
{
i2p::data::Base64ToByteStream (s.c_str (), s.length (), m_Buf, sz);
}
bool operator== (const Tag<sz>& other) const { return !memcmp (m_Buf, other.m_Buf, sz); };
bool operator< (const Tag<sz>& other) const { return memcmp (m_Buf, other.m_Buf, sz) < 0; };
private:
bool IsZero () const
{
for (int i = 0; i < sz/8; i++)
if (ll[i]) return false;
return true;
}
std::string ToBase64 () const
{
char str[sz*2];
int l = i2p::data::ByteStreamToBase64 (m_Buf, sz, str, sz*2);
str[l] = 0;
return std::string (str);
}
std::string ToBase32 () const
{
char str[sz*2];
int l = i2p::data::ByteStreamToBase32 (m_Buf, sz, str, sz*2);
str[l] = 0;
return std::string (str);
}
void FromBase32 (const std::string& s)
{
i2p::data::Base32ToByteStream (s.c_str (), s.length (), m_Buf, sz);
}
void FromBase64 (const std::string& s)
{
i2p::data::Base64ToByteStream (s.c_str (), s.length (), m_Buf, sz);
}
private:
union // 8 bytes alignment
{
uint8_t m_Buf[sz];
uint64_t ll[sz/8];
};
union // 8 bytes aligned
{
uint8_t m_Buf[sz];
uint64_t ll[sz/8];
};
};
} // data
} // i2p

66
Timestamp.cpp Normal file
View File

@@ -0,0 +1,66 @@
#include <inttypes.h>
#include <string.h>
#include <boost/asio.hpp>
#include "Log.h"
#include "I2PEndian.h"
#include "Timestamp.h"
#ifdef WIN32
#ifndef _WIN64
#define _USE_32BIT_TIME_T
#endif
#endif
namespace i2p
{
namespace util
{
static int64_t g_TimeOffset = 0; // in seconds
void SyncTimeWithNTP (const std::string& address)
{
boost::asio::io_service service;
boost::asio::ip::udp::resolver::query query (boost::asio::ip::udp::v4 (), address, "ntp");
boost::system::error_code ec;
auto it = boost::asio::ip::udp::resolver (service).resolve (query, ec);
if (!ec && it != boost::asio::ip::udp::resolver::iterator())
{
auto ep = (*it).endpoint (); // take first one
boost::asio::ip::udp::socket socket (service);
socket.open (boost::asio::ip::udp::v4 (), ec);
if (!ec)
{
uint8_t buf[48];// 48 bytes NTP request/response
memset (buf, 0, 48);
htobe32buf (buf, (3 << 27) | (3 << 24)); // RFC 4330
size_t len = 0;
try
{
socket.send_to (boost::asio::buffer (buf, 48), ep);
int i = 0;
while (!socket.available() && i < 10) // 10 seconds max
{
std::this_thread::sleep_for (std::chrono::seconds(1));
i++;
}
if (socket.available ())
len = socket.receive_from (boost::asio::buffer (buf, 48), ep);
}
catch (std::exception& e)
{
LogPrint (eLogError, "NTP error: ", e.what ());
}
if (len >= 8)
{
auto ourTs = GetSecondsSinceEpoch ();
uint32_t ts = bufbe32toh (buf + 32);
if (ts > 2208988800U) ts -= 2208988800U; // 1/1/1970 from 1/1/1900
g_TimeOffset = ts - ourTs;
LogPrint (eLogInfo, address, " time offset from system time is ", g_TimeOffset, " seconds");
}
}
}
}
}
}

View File

@@ -85,6 +85,8 @@ namespace tunnel
TransitTunnel (receiveTunnelID, nextIdent, nextTunnelID, layerKey, ivKey),
m_Endpoint (false) {}; // transit endpoint is always outbound
void Cleanup () { m_Endpoint.Cleanup (); }
void HandleTunnelDataMsg (std::shared_ptr<const i2p::I2NPMessage> tunnelMsg);
size_t GetNumTransmittedBytes () const { return m_Endpoint.GetNumReceivedBytes (); }

View File

@@ -9,6 +9,7 @@
#include "Crypto.h"
#include "RouterInfo.h"
#include "I2NPProtocol.h"
#include "Timestamp.h"
namespace i2p
{
@@ -54,7 +55,8 @@ namespace transport
public:
TransportSession (std::shared_ptr<const i2p::data::RouterInfo> router, int terminationTimeout):
m_DHKeysPair (nullptr), m_NumSentBytes (0), m_NumReceivedBytes (0), m_IsOutgoing (router), m_TerminationTimeout (terminationTimeout)
m_DHKeysPair (nullptr), m_NumSentBytes (0), m_NumReceivedBytes (0), m_IsOutgoing (router), m_TerminationTimeout (terminationTimeout),
m_LastActivityTimestamp (i2p::util::GetSecondsSinceEpoch ())
{
if (router)
m_RemoteIdentity = router->GetRouterIdentity ();
@@ -62,6 +64,8 @@ namespace transport
virtual ~TransportSession () {};
virtual void Done () = 0;
std::string GetIdentHashBase64() const { return m_RemoteIdentity ? m_RemoteIdentity->GetIdentHash().ToBase64() : ""; }
std::shared_ptr<const i2p::data::IdentityEx> GetRemoteIdentity () { return m_RemoteIdentity; };
void SetRemoteIdentity (std::shared_ptr<const i2p::data::IdentityEx> ident) { m_RemoteIdentity = ident; };
@@ -72,6 +76,8 @@ namespace transport
int GetTerminationTimeout () const { return m_TerminationTimeout; };
void SetTerminationTimeout (int terminationTimeout) { m_TerminationTimeout = terminationTimeout; };
bool IsTerminationTimeoutExpired (uint64_t ts) const
{ return ts >= m_LastActivityTimestamp + GetTerminationTimeout (); };
virtual void SendI2NPMessages (const std::vector<std::shared_ptr<I2NPMessage> >& msgs) = 0;
@@ -82,6 +88,7 @@ namespace transport
size_t m_NumSentBytes, m_NumReceivedBytes;
bool m_IsOutgoing;
int m_TerminationTimeout;
uint64_t m_LastActivityTimestamp;
};
}
}

View File

@@ -4,6 +4,11 @@
#include "I2NPProtocol.h"
#include "NetDb.h"
#include "Transports.h"
#include "Config.h"
#ifdef WITH_EVENTS
#include "Event.h"
#include "util.h"
#endif
using namespace i2p::data;
@@ -43,11 +48,22 @@ namespace transport
{
while (m_IsRunning)
{
int num;
while ((num = m_QueueSize - m_Queue.size ()) > 0)
int num, total = 0;
while ((num = m_QueueSize - (int)m_Queue.size ()) > 0 && total < 20)
{
CreateDHKeysPairs (num);
std::unique_lock<std::mutex> l(m_AcquiredMutex);
m_Acquired.wait (l); // wait for element gets aquired
total += num;
}
if (total >= 20)
{
LogPrint (eLogWarning, "Transports: ", total, " DH keys generated at the time");
std::this_thread::sleep_for (std::chrono::seconds(1)); // take a break
}
else
{
std::unique_lock<std::mutex> l(m_AcquiredMutex);
m_Acquired.wait (l); // wait for element gets aquired
}
}
}
@@ -55,7 +71,6 @@ namespace transport
{
if (num > 0)
{
i2p::crypto::DHKeys dh;
for (int i = 0; i < num; i++)
{
auto pair = std::make_shared<i2p::crypto::DHKeys> ();
@@ -86,14 +101,16 @@ namespace transport
void DHKeysPairSupplier::Return (std::shared_ptr<i2p::crypto::DHKeys> pair)
{
std::unique_lock<std::mutex> l(m_AcquiredMutex);
m_Queue.push (pair);
std::unique_lock<std::mutex>l(m_AcquiredMutex);
if ((int)m_Queue.size () < 2*m_QueueSize)
m_Queue.push (pair);
}
Transports transports;
Transports::Transports ():
m_IsOnline (true), m_IsRunning (false), m_Thread (nullptr), m_Work (m_Service), m_PeerCleanupTimer (m_Service),
m_IsOnline (true), m_IsRunning (false), m_Thread (nullptr), m_Service (nullptr),
m_Work (nullptr), m_PeerCleanupTimer (nullptr), m_PeerTestTimer (nullptr),
m_NTCPServer (nullptr), m_SSUServer (nullptr), m_DHKeysPairSupplier (5), // 5 pre-generated keys
m_TotalSentBytes(0), m_TotalReceivedBytes(0), m_InBandwidth (0), m_OutBandwidth (0),
m_LastInBandwidthUpdateBytes (0), m_LastOutBandwidthUpdateBytes (0), m_LastBandwidthUpdateTime (0)
@@ -103,10 +120,25 @@ namespace transport
Transports::~Transports ()
{
Stop ();
if (m_Service)
{
delete m_PeerCleanupTimer; m_PeerCleanupTimer = nullptr;
delete m_PeerTestTimer; m_PeerTestTimer = nullptr;
delete m_Work; m_Work = nullptr;
delete m_Service; m_Service = nullptr;
}
}
void Transports::Start (bool enableNTCP, bool enableSSU)
{
if (!m_Service)
{
m_Service = new boost::asio::io_service ();
m_Work = new boost::asio::io_service::work (*m_Service);
m_PeerCleanupTimer = new boost::asio::deadline_timer (*m_Service);
m_PeerTestTimer = new boost::asio::deadline_timer (*m_Service);
}
m_DHKeysPairSupplier.Start ();
m_IsRunning = true;
m_Thread = new std::thread (std::bind (&Transports::Run, this));
@@ -114,6 +146,7 @@ namespace transport
auto& addresses = context.GetRouterInfo ().GetAddresses ();
for (const auto& address : addresses)
{
if (!address) continue;
if (m_NTCPServer == nullptr && enableNTCP)
{
m_NTCPServer = new NTCPServer ();
@@ -150,13 +183,16 @@ namespace transport
LogPrint (eLogError, "Transports: SSU server already exists");
}
}
m_PeerCleanupTimer.expires_from_now (boost::posix_time::seconds(5*SESSION_CREATION_TIMEOUT));
m_PeerCleanupTimer.async_wait (std::bind (&Transports::HandlePeerCleanupTimer, this, std::placeholders::_1));
m_PeerCleanupTimer->expires_from_now (boost::posix_time::seconds(5*SESSION_CREATION_TIMEOUT));
m_PeerCleanupTimer->async_wait (std::bind (&Transports::HandlePeerCleanupTimer, this, std::placeholders::_1));
m_PeerTestTimer->expires_from_now (boost::posix_time::minutes(PEER_TEST_INTERVAL));
m_PeerTestTimer->async_wait (std::bind (&Transports::HandlePeerTestTimer, this, std::placeholders::_1));
}
void Transports::Stop ()
{
m_PeerCleanupTimer.cancel ();
if (m_PeerCleanupTimer) m_PeerCleanupTimer->cancel ();
if (m_PeerTestTimer) m_PeerTestTimer->cancel ();
m_Peers.clear ();
if (m_SSUServer)
{
@@ -173,7 +209,7 @@ namespace transport
m_DHKeysPairSupplier.Stop ();
m_IsRunning = false;
m_Service.stop ();
if (m_Service) m_Service->stop ();
if (m_Thread)
{
m_Thread->join ();
@@ -184,11 +220,11 @@ namespace transport
void Transports::Run ()
{
while (m_IsRunning)
while (m_IsRunning && m_Service)
{
try
{
m_Service.run ();
m_Service->run ();
}
catch (std::exception& ex)
{
@@ -228,7 +264,10 @@ namespace transport
void Transports::SendMessages (const i2p::data::IdentHash& ident, const std::vector<std::shared_ptr<i2p::I2NPMessage> >& msgs)
{
m_Service.post (std::bind (&Transports::PostMessages, this, ident, msgs));
#ifdef WITH_EVENTS
QueueIntEvent("transport.send", ident.ToBase64(), msgs.size());
#endif
m_Service->post (std::bind (&Transports::PostMessages, this, ident, msgs));
}
void Transports::PostMessages (i2p::data::IdentHash ident, std::vector<std::shared_ptr<i2p::I2NPMessage> > msgs)
@@ -237,9 +276,11 @@ namespace transport
{
// we send it to ourself
for (auto& it: msgs)
i2p::HandleI2NPMessage (it);
m_LoopbackHandler.PutNextMessage (it);
m_LoopbackHandler.Flush ();
return;
}
}
if(RoutesRestricted() && ! IsRestrictedPeer(ident)) return;
auto it = m_Peers.find (ident);
if (it == m_Peers.end ())
{
@@ -344,7 +385,7 @@ namespace transport
}
}
}
LogPrint (eLogError, "Transports: No NTCP or SSU addresses available");
LogPrint (eLogInfo, "Transports: No NTCP or SSU addresses available");
peer.Done ();
std::unique_lock<std::mutex> l(m_PeersMutex);
m_Peers.erase (ident);
@@ -361,7 +402,7 @@ namespace transport
void Transports::RequestComplete (std::shared_ptr<const i2p::data::RouterInfo> r, const i2p::data::IdentHash& ident)
{
m_Service.post (std::bind (&Transports::HandleRequestComplete, this, r, ident));
m_Service->post (std::bind (&Transports::HandleRequestComplete, this, r, ident));
}
void Transports::HandleRequestComplete (std::shared_ptr<const i2p::data::RouterInfo> r, i2p::data::IdentHash ident)
@@ -377,7 +418,7 @@ namespace transport
}
else
{
LogPrint (eLogError, "Transports: RouterInfo not found, Failed to send messages");
LogPrint (eLogWarning, "Transports: RouterInfo not found, Failed to send messages");
std::unique_lock<std::mutex> l(m_PeersMutex);
m_Peers.erase (it);
}
@@ -386,7 +427,7 @@ namespace transport
void Transports::NTCPResolve (const std::string& addr, const i2p::data::IdentHash& ident)
{
auto resolver = std::make_shared<boost::asio::ip::tcp::resolver>(m_Service);
auto resolver = std::make_shared<boost::asio::ip::tcp::resolver>(*m_Service);
resolver->async_resolve (boost::asio::ip::tcp::resolver::query (addr, ""),
std::bind (&Transports::HandleNTCPResolve, this,
std::placeholders::_1, std::placeholders::_2, ident, resolver));
@@ -429,7 +470,7 @@ namespace transport
void Transports::SSUResolve (const std::string& addr, const i2p::data::IdentHash& ident)
{
auto resolver = std::make_shared<boost::asio::ip::tcp::resolver>(m_Service);
auto resolver = std::make_shared<boost::asio::ip::tcp::resolver>(*m_Service);
resolver->async_resolve (boost::asio::ip::tcp::resolver::query (addr, ""),
std::bind (&Transports::HandleSSUResolve, this,
std::placeholders::_1, std::placeholders::_2, ident, resolver));
@@ -472,7 +513,7 @@ namespace transport
void Transports::CloseSession (std::shared_ptr<const i2p::data::RouterInfo> router)
{
if (!router) return;
m_Service.post (std::bind (&Transports::PostCloseSession, this, router));
m_Service->post (std::bind (&Transports::PostCloseSession, this, router));
}
void Transports::PostCloseSession (std::shared_ptr<const i2p::data::RouterInfo> router)
@@ -493,17 +534,23 @@ namespace transport
void Transports::DetectExternalIP ()
{
if (RoutesRestricted())
{
LogPrint(eLogInfo, "Transports: restricted routes enabled, not detecting ip");
i2p::context.SetStatus (eRouterStatusOK);
return;
}
if (m_SSUServer)
{
#ifndef MESHNET
i2p::context.SetStatus (eRouterStatusTesting);
#endif
bool nat; i2p::config::GetOption("nat", nat);
bool isv4 = i2p::context.SupportsV4 ();
if (nat && isv4)
i2p::context.SetStatus (eRouterStatusTesting);
for (int i = 0; i < 5; i++)
{
auto router = i2p::data::netdb.GetRandomPeerTestRouter ();
if (router && router->IsSSU (!context.SupportsV6 ()))
m_SSUServer->CreateSession (router, true); // peer test
auto router = i2p::data::netdb.GetRandomPeerTestRouter (isv4); // v4 only if v4
if (router)
m_SSUServer->CreateSession (router, true, isv4); // peer test
else
{
// if not peer test capable routers found pick any
@@ -519,22 +566,25 @@ namespace transport
void Transports::PeerTest ()
{
if (RoutesRestricted() || !i2p::context.SupportsV4 ()) return;
if (m_SSUServer)
{
{
bool statusChanged = false;
for (int i = 0; i < 5; i++)
{
auto router = i2p::data::netdb.GetRandomPeerTestRouter ();
if (router && router->IsSSU (!context.SupportsV6 ()))
auto router = i2p::data::netdb.GetRandomPeerTestRouter (true); // v4 only
if (router)
{
if (!statusChanged)
{
statusChanged = true;
i2p::context.SetStatus (eRouterStatusTesting); // first time only
}
m_SSUServer->CreateSession (router, true); // peer test
m_SSUServer->CreateSession (router, true, true); // peer test v4
}
}
}
if (!statusChanged)
LogPrint (eLogWarning, "Can't find routers for peer test");
}
}
@@ -550,7 +600,7 @@ namespace transport
void Transports::PeerConnected (std::shared_ptr<TransportSession> session)
{
m_Service.post([session, this]()
m_Service->post([session, this]()
{
auto remoteIdentity = session->GetRemoteIdentity ();
if (!remoteIdentity) return;
@@ -558,6 +608,9 @@ namespace transport
auto it = m_Peers.find (ident);
if (it != m_Peers.end ())
{
#ifdef WITH_EVENTS
EmitEvent({{"type" , "transport.connected"}, {"ident", ident.ToBase64()}, {"inbound", "false"}});
#endif
bool sendDatabaseStore = true;
if (it->second.delayedMessages.size () > 0)
{
@@ -577,20 +630,32 @@ namespace transport
}
else // incoming connection
{
if(RoutesRestricted() && ! IsRestrictedPeer(ident)) {
// not trusted
LogPrint(eLogWarning, "Transports: closing untrusted inbound connection from ", ident.ToBase64());
session->Done();
return;
}
#ifdef WITH_EVENTS
EmitEvent({{"type" , "transport.connected"}, {"ident", ident.ToBase64()}, {"inbound", "true"}});
#endif
session->SendI2NPMessages ({ CreateDatabaseStoreMsg () }); // send DatabaseStore
std::unique_lock<std::mutex> l(m_PeersMutex);
m_Peers.insert (std::make_pair (ident, Peer{ 0, nullptr, { session }, i2p::util::GetSecondsSinceEpoch (), {} }));
}
});
});
}
void Transports::PeerDisconnected (std::shared_ptr<TransportSession> session)
{
m_Service.post([session, this]()
{
m_Service->post([session, this]()
{
auto remoteIdentity = session->GetRemoteIdentity ();
if (!remoteIdentity) return;
auto ident = remoteIdentity->GetIdentHash ();
#ifdef WITH_EVENTS
EmitEvent({{"type" , "transport.disconnected"}, {"ident", ident.ToBase64()}});
#endif
auto it = m_Peers.find (ident);
if (it != m_Peers.end ())
{
@@ -630,7 +695,7 @@ namespace transport
if (profile)
{
profile->TunnelNonReplied();
profile->Save();
profile->Save(it->first);
}
std::unique_lock<std::mutex> l(m_PeersMutex);
it = m_Peers.erase (it);
@@ -641,11 +706,21 @@ namespace transport
UpdateBandwidth (); // TODO: use separate timer(s) for it
if (i2p::context.GetStatus () == eRouterStatusTesting) // if still testing, repeat peer test
DetectExternalIP ();
m_PeerCleanupTimer.expires_from_now (boost::posix_time::seconds(5*SESSION_CREATION_TIMEOUT));
m_PeerCleanupTimer.async_wait (std::bind (&Transports::HandlePeerCleanupTimer, this, std::placeholders::_1));
m_PeerCleanupTimer->expires_from_now (boost::posix_time::seconds(5*SESSION_CREATION_TIMEOUT));
m_PeerCleanupTimer->async_wait (std::bind (&Transports::HandlePeerCleanupTimer, this, std::placeholders::_1));
}
}
void Transports::HandlePeerTestTimer (const boost::system::error_code& ecode)
{
if (ecode != boost::asio::error::operation_aborted)
{
PeerTest ();
m_PeerTestTimer->expires_from_now (boost::posix_time::minutes(PEER_TEST_INTERVAL));
m_PeerTestTimer->async_wait (std::bind (&Transports::HandlePeerTestTimer, this, std::placeholders::_1));
}
}
std::shared_ptr<const i2p::data::RouterInfo> Transports::GetRandomPeer () const
{
if (m_Peers.empty ()) return nullptr;
@@ -654,30 +729,79 @@ namespace transport
std::advance (it, rand () % m_Peers.size ());
return it != m_Peers.end () ? it->second.router : nullptr;
}
void Transports::RestrictRoutes(std::vector<std::string> families)
{
std::lock_guard<std::mutex> lock(m_FamilyMutex);
m_TrustedFamilies.clear();
for ( const auto& fam : families )
m_TrustedFamilies.push_back(fam);
}
void Transports::RestrictRoutesToFamilies(std::set<std::string> families)
{
std::lock_guard<std::mutex> lock(m_FamilyMutex);
m_TrustedFamilies.clear();
for ( const auto& fam : families )
m_TrustedFamilies.push_back(fam);
}
bool Transports::RoutesRestricted() const {
std::lock_guard<std::mutex> lock(m_FamilyMutex);
return m_TrustedFamilies.size() > 0;
}
void Transports::RestrictRoutesToRouters(std::set<i2p::data::IdentHash> routers)
{
std::unique_lock<std::mutex> lock(m_TrustedRoutersMutex);
m_TrustedRouters.clear();
for (const auto & ri : routers )
m_TrustedRouters.push_back(ri);
}
bool Transports::RoutesRestricted() const {
std::unique_lock<std::mutex> famlock(m_FamilyMutex);
std::unique_lock<std::mutex> routerslock(m_TrustedRoutersMutex);
return m_TrustedFamilies.size() > 0 || m_TrustedRouters.size() > 0;
}
/** XXX: if routes are not restricted this dies */
std::shared_ptr<const i2p::data::RouterInfo> Transports::GetRestrictedPeer() const {
std::string fam;
{
std::lock_guard<std::mutex> lock(m_FamilyMutex);
// TODO: random family (?)
fam = m_TrustedFamilies[0];
}
boost::to_lower(fam);
return i2p::data::netdb.GetRandomRouterInFamily(fam);
}
/** XXX: if routes are not restricted this dies */
std::shared_ptr<const i2p::data::RouterInfo> Transports::GetRestrictedPeer() const
{
{
std::lock_guard<std::mutex> l(m_FamilyMutex);
std::string fam;
auto sz = m_TrustedFamilies.size();
if(sz > 1)
{
auto it = m_TrustedFamilies.begin ();
std::advance(it, rand() % sz);
fam = *it;
boost::to_lower(fam);
}
else if (sz == 1)
{
fam = m_TrustedFamilies[0];
}
if (fam.size())
return i2p::data::netdb.GetRandomRouterInFamily(fam);
}
{
std::unique_lock<std::mutex> l(m_TrustedRoutersMutex);
auto sz = m_TrustedRouters.size();
if (sz)
{
if(sz == 1)
return i2p::data::netdb.FindRouter(m_TrustedRouters[0]);
auto it = m_TrustedRouters.begin();
std::advance(it, rand() % sz);
return i2p::data::netdb.FindRouter(*it);
}
}
return nullptr;
}
bool Transports::IsRestrictedPeer(const i2p::data::IdentHash & ih) const
{
{
std::unique_lock<std::mutex> l(m_TrustedRoutersMutex);
for (const auto & r : m_TrustedRouters )
if ( r == ih ) return true;
}
{
std::unique_lock<std::mutex> l(m_FamilyMutex);
auto ri = i2p::data::netdb.FindRouter(ih);
for (const auto & fam : m_TrustedFamilies)
if(ri->IsFamily(fam)) return true;
}
return false;
}
}
}

View File

@@ -66,6 +66,7 @@ namespace transport
};
const size_t SESSION_CREATION_TIMEOUT = 10; // in seconds
const int PEER_TEST_INTERVAL = 71; // in minutes
const int MAX_NUM_DELAYED_MESSAGES = 50;
class Transports
{
@@ -83,7 +84,7 @@ namespace transport
bool IsOnline() const { return m_IsOnline; };
void SetOnline (bool online) { m_IsOnline = online; };
boost::asio::io_service& GetService () { return m_Service; };
boost::asio::io_service& GetService () { return *m_Service; };
std::shared_ptr<i2p::crypto::DHKeys> GetNextDHKeysPair ();
void ReuseDHKeysPair (std::shared_ptr<i2p::crypto::DHKeys> pair);
@@ -110,7 +111,11 @@ namespace transport
/** do we want to use restricted routes? */
bool RoutesRestricted() const;
/** restrict routes to use only these router families for first hops */
void RestrictRoutes(std::vector<std::string> families);
void RestrictRoutesToFamilies(std::set<std::string> families);
/** restrict routes to use only these routers for first hops */
void RestrictRoutesToRouters(std::set<i2p::data::IdentHash> routers);
bool IsRestrictedPeer(const i2p::data::IdentHash & ident) const;
void PeerTest ();
@@ -123,7 +128,8 @@ namespace transport
void PostCloseSession (std::shared_ptr<const i2p::data::RouterInfo> router);
bool ConnectToPeer (const i2p::data::IdentHash& ident, Peer& peer);
void HandlePeerCleanupTimer (const boost::system::error_code& ecode);
void HandlePeerTestTimer (const boost::system::error_code& ecode);
void NTCPResolve (const std::string& addr, const i2p::data::IdentHash& ident);
void HandleNTCPResolve (const boost::system::error_code& ecode, boost::asio::ip::tcp::resolver::iterator it,
i2p::data::IdentHash ident, std::shared_ptr<boost::asio::ip::tcp::resolver> resolver);
@@ -138,9 +144,9 @@ namespace transport
bool m_IsOnline, m_IsRunning;
std::thread * m_Thread;
boost::asio::io_service m_Service;
boost::asio::io_service::work m_Work;
boost::asio::deadline_timer m_PeerCleanupTimer;
boost::asio::io_service * m_Service;
boost::asio::io_service::work * m_Work;
boost::asio::deadline_timer * m_PeerCleanupTimer, * m_PeerTestTimer;
NTCPServer * m_NTCPServer;
SSUServer * m_SSUServer;
@@ -154,9 +160,15 @@ namespace transport
uint64_t m_LastInBandwidthUpdateBytes, m_LastOutBandwidthUpdateBytes;
uint64_t m_LastBandwidthUpdateTime;
/** which router families to trust for first hops */
std::vector<std::string> m_TrustedFamilies;
mutable std::mutex m_FamilyMutex;
/** which router families to trust for first hops */
std::vector<std::string> m_TrustedFamilies;
mutable std::mutex m_FamilyMutex;
/** which routers for first hop to trust */
std::vector<i2p::data::IdentHash> m_TrustedRouters;
mutable std::mutex m_TrustedRoutersMutex;
i2p::I2NPMessagesHandler m_LoopbackHandler;
public:

View File

@@ -11,15 +11,19 @@
#include "Transports.h"
#include "NetDb.h"
#include "Tunnel.h"
#include "TunnelPool.h"
#ifdef WITH_EVENTS
#include "Event.h"
#endif
namespace i2p
{
namespace tunnel
{
{
Tunnel::Tunnel (std::shared_ptr<const TunnelConfig> config):
TunnelBase (config->GetTunnelID (), config->GetNextTunnelID (), config->GetNextIdentHash ()),
m_Config (config), m_Pool (nullptr), m_State (eTunnelStatePending), m_IsRecreated (false)
m_Config (config), m_Pool (nullptr), m_State (eTunnelStatePending), m_IsRecreated (false),
m_Latency (0)
{
}
@@ -29,12 +33,14 @@ namespace tunnel
void Tunnel::Build (uint32_t replyMsgID, std::shared_ptr<OutboundTunnel> outboundTunnel)
{
#ifdef WITH_EVENTS
std::string peers = i2p::context.GetIdentity()->GetIdentHash().ToBase64();
#endif
auto numHops = m_Config->GetNumHops ();
int numRecords = numHops <= STANDARD_NUM_RECORDS ? STANDARD_NUM_RECORDS : numHops;
auto msg = NewI2NPShortMessage ();
*msg->GetPayload () = numRecords;
msg->len += numRecords*TUNNEL_BUILD_RECORD_SIZE + 1;
// shuffle records
std::vector<int> recordIndicies;
for (int i = 0; i < numRecords; i++) recordIndicies.push_back(i);
@@ -55,8 +61,14 @@ namespace tunnel
hop->CreateBuildRequestRecord (records + idx*TUNNEL_BUILD_RECORD_SIZE, msgID);
hop->recordIndex = idx;
i++;
#ifdef WITH_EVENTS
peers += ":" + hop->ident->GetIdentHash().ToBase64();
#endif
hop = hop->next;
}
#ifdef WITH_EVENTS
EmitTunnelEvent("tunnel.build", this, peers);
#endif
// fill up fake records with random data
for (int i = numHops; i < numRecords; i++)
{
@@ -150,6 +162,12 @@ namespace tunnel
return established;
}
bool Tunnel::LatencyFitsRange(uint64_t lower, uint64_t upper) const
{
auto latency = GetMeanLatency();
return latency >= lower && latency <= upper;
}
void Tunnel::EncryptTunnelMsg (std::shared_ptr<const I2NPMessage> in, std::shared_ptr<I2NPMessage> out)
{
const uint8_t * inPayload = in->GetPayload () + 4;
@@ -182,12 +200,22 @@ namespace tunnel
return ret;
}
void Tunnel::SetState(TunnelState state)
{
m_State = state;
#ifdef WITH_EVENTS
EmitTunnelEvent("tunnel.state", this, state);
#endif
}
void Tunnel::PrintHops (std::stringstream& s) const
{
for (auto& it: m_Hops)
// hops are in inverted order, we must print in direct order
for (auto it = m_Hops.rbegin (); it != m_Hops.rend (); it++)
{
s << " &#8658; ";
s << i2p::data::GetIdentHashAbbreviation (it->ident->GetIdentHash ());
s << i2p::data::GetIdentHashAbbreviation ((*it)->ident->GetIdentHash ());
}
}
@@ -559,6 +587,7 @@ namespace tunnel
for (auto it = pendingTunnels.begin (); it != pendingTunnels.end ();)
{
auto tunnel = it->second;
auto pool = tunnel->GetTunnelPool();
switch (tunnel->GetState ())
{
case eTunnelStatePending:
@@ -581,6 +610,11 @@ namespace tunnel
hop = hop->next;
}
}
#ifdef WITH_EVENTS
EmitTunnelEvent("tunnel.state", tunnel.get(), eTunnelStateBuildFailed);
#endif
// for i2lua
if(pool) pool->OnTunnelBuildResult(tunnel, eBuildResultTimeout);
// delete
it = pendingTunnels.erase (it);
m_NumFailedTunnelCreations++;
@@ -590,6 +624,12 @@ namespace tunnel
break;
case eTunnelStateBuildFailed:
LogPrint (eLogDebug, "Tunnel: pending build request ", it->first, " failed, deleted");
#ifdef WITH_EVENTS
EmitTunnelEvent("tunnel.state", tunnel.get(), eTunnelStateBuildFailed);
#endif
// for i2lua
if(pool) pool->OnTunnelBuildResult(tunnel, eBuildResultRejected);
it = pendingTunnels.erase (it);
m_NumFailedTunnelCreations++;
break;
@@ -640,11 +680,13 @@ namespace tunnel
}
}
if (m_OutboundTunnels.size () < 5)
if (m_OutboundTunnels.size () < 3)
{
// trying to create one more oubound tunnel
auto inboundTunnel = GetNextInboundTunnel ();
auto router = i2p::data::netdb.GetRandomRouter ();
auto router = i2p::transport::transports.RoutesRestricted() ?
i2p::transport::transports.GetRestrictedPeer() :
i2p::data::netdb.GetRandomRouter ();
if (!inboundTunnel || !router) return;
LogPrint (eLogDebug, "Tunnel: creating one hop outbound tunnel");
CreateTunnel<OutboundTunnel> (
@@ -684,6 +726,8 @@ namespace tunnel
if (ts + TUNNEL_EXPIRATION_THRESHOLD > tunnel->GetCreationTime () + TUNNEL_EXPIRATION_TIMEOUT)
tunnel->SetState (eTunnelStateExpiring);
else // we don't need to cleanup expiring tunnels
tunnel->Cleanup ();
}
it++;
}
@@ -697,16 +741,18 @@ namespace tunnel
CreateZeroHopsOutboundTunnel ();
if (!m_ExploratoryPool)
{
m_ExploratoryPool = CreateTunnelPool (2, 2, 5, 5); // 2-hop exploratory, 5 tunnels
m_ExploratoryPool = CreateTunnelPool (2, 2, 3, 3); // 2-hop exploratory, 3 tunnels
m_ExploratoryPool->SetLocalDestination (i2p::context.GetSharedDestination ());
}
return;
}
if (m_OutboundTunnels.empty () || m_InboundTunnels.size () < 5)
if (m_OutboundTunnels.empty () || m_InboundTunnels.size () < 3)
{
// trying to create one more inbound tunnel
auto router = i2p::data::netdb.GetRandomRouter ();
auto router = i2p::transport::transports.RoutesRestricted() ?
i2p::transport::transports.GetRestrictedPeer() :
i2p::data::netdb.GetRandomRouter ();
if (!router) {
LogPrint (eLogWarning, "Tunnel: can't find any router, skip creating tunnel");
return;
@@ -731,7 +777,10 @@ namespace tunnel
it = m_TransitTunnels.erase (it);
}
else
{
tunnel->Cleanup ();
it++;
}
}
}
@@ -771,7 +820,7 @@ namespace tunnel
std::shared_ptr<InboundTunnel> Tunnels::CreateInboundTunnel (std::shared_ptr<TunnelConfig> config, std::shared_ptr<OutboundTunnel> outboundTunnel)
{
if (config)
if (config)
return CreateTunnel<InboundTunnel>(config, outboundTunnel);
else
return CreateZeroHopsInboundTunnel ();

View File

@@ -19,11 +19,49 @@
#include "TunnelGateway.h"
#include "TunnelBase.h"
#include "I2NPProtocol.h"
#include "Event.h"
namespace i2p
{
namespace tunnel
{
{
template<typename TunnelT>
static void EmitTunnelEvent(const std::string & ev, const TunnelT & t)
{
#ifdef WITH_EVENTS
EmitEvent({{"type", ev}, {"tid", std::to_string(t->GetTunnelID())}});
#else
(void) ev;
(void) t;
#endif
}
template<typename TunnelT, typename T>
static void EmitTunnelEvent(const std::string & ev, TunnelT * t, const T & val)
{
#ifdef WITH_EVENTS
EmitEvent({{"type", ev}, {"tid", std::to_string(t->GetTunnelID())}, {"value", std::to_string(val)}, {"inbound", std::to_string(t->IsInbound())}});
#else
(void) ev;
(void) t;
(void) val;
#endif
}
template<typename TunnelT>
static void EmitTunnelEvent(const std::string & ev, TunnelT * t, const std::string & val)
{
#ifdef WITH_EVENTS
EmitEvent({{"type", ev}, {"tid", std::to_string(t->GetTunnelID())}, {"value", val}, {"inbound", std::to_string(t->IsInbound())}});
#else
(void) ev;
(void) t;
(void) val;
#endif
}
const int TUNNEL_EXPIRATION_TIMEOUT = 660; // 11 minutes
const int TUNNEL_EXPIRATION_THRESHOLD = 60; // 1 minute
const int TUNNEL_RECREATION_THRESHOLD = 90; // 1.5 minutes
@@ -62,12 +100,13 @@ namespace tunnel
std::vector<std::shared_ptr<const i2p::data::IdentityEx> > GetPeers () const;
std::vector<std::shared_ptr<const i2p::data::IdentityEx> > GetInvertedPeers () const;
TunnelState GetState () const { return m_State; };
void SetState (TunnelState state) { m_State = state; };
void SetState (TunnelState state);
bool IsEstablished () const { return m_State == eTunnelStateEstablished; };
bool IsFailed () const { return m_State == eTunnelStateFailed; };
bool IsRecreated () const { return m_IsRecreated; };
void SetIsRecreated () { m_IsRecreated = true; };
virtual bool IsInbound() const = 0;
std::shared_ptr<TunnelPool> GetTunnelPool () const { return m_Pool; };
void SetTunnelPool (std::shared_ptr<TunnelPool> pool) { m_Pool = pool; };
@@ -79,6 +118,14 @@ namespace tunnel
void SendTunnelDataMsg (std::shared_ptr<i2p::I2NPMessage> msg);
void EncryptTunnelMsg (std::shared_ptr<const I2NPMessage> in, std::shared_ptr<I2NPMessage> out);
/** @brief add latency sample */
void AddLatencySample(const uint64_t ms) { m_Latency = (m_Latency + ms) >> 1; }
/** @brief get this tunnel's estimated latency */
uint64_t GetMeanLatency() const { return m_Latency; }
/** @breif return true if this tunnel's latency fits in range [lowerbound, upperbound] */
bool LatencyFitsRange(uint64_t lowerbound, uint64_t upperbound) const;
bool LatencyIsKnown() const { return m_Latency > 0; }
protected:
void PrintHops (std::stringstream& s) const;
@@ -90,6 +137,7 @@ namespace tunnel
std::shared_ptr<TunnelPool> m_Pool; // pool, tunnel belongs to, or null
TunnelState m_State;
bool m_IsRecreated;
uint64_t m_Latency; // in milliseconds
};
class OutboundTunnel: public Tunnel
@@ -107,6 +155,8 @@ namespace tunnel
// implements TunnelBase
void HandleTunnelDataMsg (std::shared_ptr<const i2p::I2NPMessage> tunnelMsg);
bool IsInbound() const { return false; }
private:
@@ -123,7 +173,11 @@ namespace tunnel
void HandleTunnelDataMsg (std::shared_ptr<const I2NPMessage> msg);
virtual size_t GetNumReceivedBytes () const { return m_Endpoint.GetNumReceivedBytes (); };
void Print (std::stringstream& s) const;
bool IsInbound() const { return true; }
// override TunnelBase
void Cleanup () { m_Endpoint.Cleanup (); };
private:
TunnelEndpoint m_Endpoint;

View File

@@ -37,6 +37,7 @@ namespace tunnel
m_TunnelID (tunnelID), m_NextTunnelID (nextTunnelID), m_NextIdent (nextIdent),
m_CreationTime (i2p::util::GetSecondsSinceEpoch ()) {};
virtual ~TunnelBase () {};
virtual void Cleanup () {};
virtual void HandleTunnelDataMsg (std::shared_ptr<const i2p::I2NPMessage> tunnelMsg) = 0;
virtual void SendTunnelDataMsg (std::shared_ptr<i2p::I2NPMessage> msg) = 0;
@@ -48,7 +49,7 @@ namespace tunnel
uint32_t GetCreationTime () const { return m_CreationTime; };
void SetCreationTime (uint32_t t) { m_CreationTime = t; };
private:
uint32_t m_TunnelID, m_NextTunnelID;

View File

@@ -101,8 +101,7 @@ namespace tunnel
htobe32buf (clearText + BUILD_REQUEST_RECORD_REQUEST_TIME_OFFSET, i2p::util::GetHoursSinceEpoch ());
htobe32buf (clearText + BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET, replyMsgID);
RAND_bytes (clearText + BUILD_REQUEST_RECORD_PADDING_OFFSET, BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE - BUILD_REQUEST_RECORD_PADDING_OFFSET);
i2p::crypto::ElGamalEncryption elGamalEncryption (ident->GetEncryptionPublicKey ());
elGamalEncryption.Encrypt (clearText, BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE, record + BUILD_REQUEST_RECORD_ENCRYPTED_OFFSET);
i2p::crypto::ElGamalEncrypt (ident->GetEncryptionPublicKey (), clearText, record + BUILD_REQUEST_RECORD_ENCRYPTED_OFFSET);
memcpy (record + BUILD_REQUEST_RECORD_TO_PEER_OFFSET, (const uint8_t *)ident->GetIdentHash (), 16);
}
};

View File

@@ -6,6 +6,7 @@
#include "I2NPProtocol.h"
#include "Transports.h"
#include "RouterContext.h"
#include "Timestamp.h"
#include "TunnelEndpoint.h"
namespace i2p
@@ -115,9 +116,10 @@ namespace tunnel
if (!isFollowOnFragment) // create new incomlete message
{
m.nextFragmentNum = 1;
m.receiveTime = i2p::util::GetMillisecondsSinceEpoch ();
auto ret = m_IncompleteMessages.insert (std::pair<uint32_t, TunnelMessageBlockEx>(msgID, m));
if (ret.second)
HandleOutOfSequenceFragment (msgID, ret.first->second);
HandleOutOfSequenceFragments (msgID, ret.first->second);
else
LogPrint (eLogError, "TunnelMessage: Incomplete message ", msgID, " already exists");
}
@@ -168,7 +170,7 @@ namespace tunnel
else
{
msg.nextFragmentNum++;
HandleOutOfSequenceFragment (msgID, msg);
HandleOutOfSequenceFragments (msgID, msg);
}
}
else
@@ -192,40 +194,48 @@ namespace tunnel
void TunnelEndpoint::AddOutOfSequenceFragment (uint32_t msgID, uint8_t fragmentNum, bool isLastFragment, std::shared_ptr<I2NPMessage> data)
{
auto it = m_OutOfSequenceFragments.find (msgID);
if (it == m_OutOfSequenceFragments.end ())
m_OutOfSequenceFragments.insert (std::pair<uint32_t, Fragment> (msgID, {fragmentNum, isLastFragment, data}));
if (!m_OutOfSequenceFragments.insert ({{msgID, fragmentNum}, {isLastFragment, data, i2p::util::GetMillisecondsSinceEpoch () }}).second)
LogPrint (eLogInfo, "TunnelMessage: duplicate out-of-sequence fragment ", fragmentNum, " of message ", msgID);
}
void TunnelEndpoint::HandleOutOfSequenceFragment (uint32_t msgID, TunnelMessageBlockEx& msg)
void TunnelEndpoint::HandleOutOfSequenceFragments (uint32_t msgID, TunnelMessageBlockEx& msg)
{
auto it = m_OutOfSequenceFragments.find (msgID);
if (it != m_OutOfSequenceFragments.end ())
while (ConcatNextOutOfSequenceFragment (msgID, msg))
{
if (it->second.fragmentNum == msg.nextFragmentNum)
if (!msg.nextFragmentNum) // message complete
{
LogPrint (eLogWarning, "TunnelMessage: Out-of-sequence fragment ", (int)it->second.fragmentNum, " of message ", msgID, " found");
size_t size = it->second.data->GetLength ();
if (msg.data->len + size > msg.data->maxLen)
{
LogPrint (eLogWarning, "TunnelMessage: Tunnel endpoint I2NP message size ", msg.data->maxLen, " is not enough");
auto newMsg = NewI2NPMessage ();
*newMsg = *(msg.data);
msg.data = newMsg;
}
if (msg.data->Concat (it->second.data->GetBuffer (), size) < size) // concatenate out-of-sync fragment
LogPrint (eLogError, "Tunnel endpoint I2NP buffer overflow ", msg.data->maxLen);
if (it->second.isLastFragment)
{
// message complete
HandleNextMessage (msg);
m_IncompleteMessages.erase (msgID);
}
else
msg.nextFragmentNum++;
m_OutOfSequenceFragments.erase (it);
}
HandleNextMessage (msg);
m_IncompleteMessages.erase (msgID);
break;
}
}
}
bool TunnelEndpoint::ConcatNextOutOfSequenceFragment (uint32_t msgID, TunnelMessageBlockEx& msg)
{
auto it = m_OutOfSequenceFragments.find ({msgID, msg.nextFragmentNum});
if (it != m_OutOfSequenceFragments.end ())
{
LogPrint (eLogDebug, "TunnelMessage: Out-of-sequence fragment ", (int)msg.nextFragmentNum, " of message ", msgID, " found");
size_t size = it->second.data->GetLength ();
if (msg.data->len + size > msg.data->maxLen)
{
LogPrint (eLogWarning, "TunnelMessage: Tunnel endpoint I2NP message size ", msg.data->maxLen, " is not enough");
auto newMsg = NewI2NPMessage ();
*newMsg = *(msg.data);
msg.data = newMsg;
}
if (msg.data->Concat (it->second.data->GetBuffer (), size) < size) // concatenate out-of-sync fragment
LogPrint (eLogError, "TunnelMessage: Tunnel endpoint I2NP buffer overflow ", msg.data->maxLen);
if (it->second.isLastFragment)
// message complete
msg.nextFragmentNum = 0;
else
msg.nextFragmentNum++;
m_OutOfSequenceFragments.erase (it);
return true;
}
return false;
}
void TunnelEndpoint::HandleNextMessage (const TunnelMessageBlock& msg)
@@ -262,6 +272,27 @@ namespace tunnel
default:
LogPrint (eLogError, "TunnelMessage: Unknown delivery type ", (int)msg.deliveryType);
};
}
void TunnelEndpoint::Cleanup ()
{
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
// out-of-sequence fragments
for (auto it = m_OutOfSequenceFragments.begin (); it != m_OutOfSequenceFragments.end ();)
{
if (ts > it->second.receiveTime + i2p::I2NP_MESSAGE_EXPIRATION_TIMEOUT)
it = m_OutOfSequenceFragments.erase (it);
else
++it;
}
// incomplete messages
for (auto it = m_IncompleteMessages.begin (); it != m_IncompleteMessages.end ();)
{
if (ts > it->second.receiveTime + i2p::I2NP_MESSAGE_EXPIRATION_TIMEOUT)
it = m_IncompleteMessages.erase (it);
else
++it;
}
}
}
}

View File

@@ -15,14 +15,15 @@ namespace tunnel
{
struct TunnelMessageBlockEx: public TunnelMessageBlock
{
uint64_t receiveTime; // milliseconds since epoch
uint8_t nextFragmentNum;
};
struct Fragment
{
uint8_t fragmentNum;
bool isLastFragment;
std::shared_ptr<I2NPMessage> data;
uint64_t receiveTime; // milliseconds since epoch
};
public:
@@ -30,7 +31,8 @@ namespace tunnel
TunnelEndpoint (bool isInbound): m_IsInbound (isInbound), m_NumReceivedBytes (0) {};
~TunnelEndpoint ();
size_t GetNumReceivedBytes () const { return m_NumReceivedBytes; };
void Cleanup ();
void HandleDecryptedTunnelDataMsg (std::shared_ptr<I2NPMessage> msg);
private:
@@ -39,12 +41,13 @@ namespace tunnel
void HandleNextMessage (const TunnelMessageBlock& msg);
void AddOutOfSequenceFragment (uint32_t msgID, uint8_t fragmentNum, bool isLastFragment, std::shared_ptr<I2NPMessage> data);
void HandleOutOfSequenceFragment (uint32_t msgID, TunnelMessageBlockEx& msg);
bool ConcatNextOutOfSequenceFragment (uint32_t msgID, TunnelMessageBlockEx& msg); // true if something added
void HandleOutOfSequenceFragments (uint32_t msgID, TunnelMessageBlockEx& msg);
private:
std::map<uint32_t, TunnelMessageBlockEx> m_IncompleteMessages;
std::map<uint32_t, Fragment> m_OutOfSequenceFragments;
std::map<std::pair<uint32_t, uint8_t>, Fragment> m_OutOfSequenceFragments; // (msgID, fragment#)->fragment
bool m_IsInbound;
size_t m_NumReceivedBytes;
};

View File

@@ -10,8 +10,8 @@ namespace i2p
{
namespace tunnel
{
TunnelGatewayBuffer::TunnelGatewayBuffer (uint32_t tunnelID): m_TunnelID (tunnelID),
m_CurrentTunnelDataMsg (nullptr), m_RemainingSize (0)
TunnelGatewayBuffer::TunnelGatewayBuffer ():
m_CurrentTunnelDataMsg (nullptr), m_RemainingSize (0)
{
RAND_bytes (m_NonZeroRandomBuffer, TUNNEL_DATA_MAX_PAYLOAD_SIZE);
for (size_t i = 0; i < TUNNEL_DATA_MAX_PAYLOAD_SIZE; i++)
@@ -20,6 +20,7 @@ namespace tunnel
TunnelGatewayBuffer::~TunnelGatewayBuffer ()
{
ClearTunnelDataMsgs ();
}
void TunnelGatewayBuffer::PutI2NPMsg (const TunnelMessageBlock& block)
@@ -48,7 +49,7 @@ namespace tunnel
di[0] = block.deliveryType << 5; // set delivery type
// create fragments
std::shared_ptr<I2NPMessage> msg = block.data;
const std::shared_ptr<I2NPMessage> & msg = block.data;
size_t fullMsgLen = diLen + msg->GetLength () + 2; // delivery instructions + payload + 2 bytes length
if (fullMsgLen <= m_RemainingSize)
{
@@ -115,9 +116,13 @@ namespace tunnel
m_CurrentTunnelDataMsg->len += s+7;
if (isLastFragment)
{
m_RemainingSize -= s+7;
if (!m_RemainingSize)
CompleteCurrentTunnelDataMessage ();
if(m_RemainingSize < (s+7)) {
LogPrint (eLogError, "TunnelGateway: remaining size overflow: ", m_RemainingSize, " < ", s+7);
} else {
m_RemainingSize -= s+7;
if (m_RemainingSize == 0)
CompleteCurrentTunnelDataMessage ();
}
}
else
CompleteCurrentTunnelDataMessage ();
@@ -138,10 +143,12 @@ namespace tunnel
void TunnelGatewayBuffer::ClearTunnelDataMsgs ()
{
m_TunnelDataMsgs.clear ();
m_CurrentTunnelDataMsg = nullptr;
}
void TunnelGatewayBuffer::CreateCurrentTunnelDataMessage ()
{
m_CurrentTunnelDataMsg = nullptr;
m_CurrentTunnelDataMsg = NewI2NPShortMessage ();
m_CurrentTunnelDataMsg->Align (12);
// we reserve space for padding
@@ -158,7 +165,6 @@ namespace tunnel
m_CurrentTunnelDataMsg->offset = m_CurrentTunnelDataMsg->len - TUNNEL_DATA_MSG_SIZE - I2NP_HEADER_SIZE;
uint8_t * buf = m_CurrentTunnelDataMsg->GetPayload ();
htobe32buf (buf, m_TunnelID);
RAND_bytes (buf + 4, 16); // original IV
memcpy (payload + size, buf + 4, 16); // copy IV for checksum
uint8_t hash[32];
@@ -196,15 +202,19 @@ namespace tunnel
void TunnelGateway::SendBuffer ()
{
m_Buffer.CompleteCurrentTunnelDataMessage ();
auto tunnelMsgs = m_Buffer.GetTunnelDataMsgs ();
for (auto& tunnelMsg : tunnelMsgs)
std::vector<std::shared_ptr<I2NPMessage> > newTunnelMsgs;
const auto& tunnelDataMsgs = m_Buffer.GetTunnelDataMsgs ();
for (auto& tunnelMsg : tunnelDataMsgs)
{
m_Tunnel->EncryptTunnelMsg (tunnelMsg, tunnelMsg);
tunnelMsg->FillI2NPMessageHeader (eI2NPTunnelData);
auto newMsg = CreateEmptyTunnelDataMsg ();
m_Tunnel->EncryptTunnelMsg (tunnelMsg, newMsg);
htobe32buf (newMsg->GetPayload (), m_Tunnel->GetNextTunnelID ());
newMsg->FillI2NPMessageHeader (eI2NPTunnelData);
newTunnelMsgs.push_back (newMsg);
m_NumSentBytes += TUNNEL_DATA_MSG_SIZE;
}
i2p::transport::transports.SendMessages (m_Tunnel->GetNextIdentHash (), tunnelMsgs);
m_Buffer.ClearTunnelDataMsgs ();
i2p::transport::transports.SendMessages (m_Tunnel->GetNextIdentHash (), newTunnelMsgs);
}
}
}

View File

@@ -14,10 +14,10 @@ namespace tunnel
class TunnelGatewayBuffer
{
public:
TunnelGatewayBuffer (uint32_t tunnelID);
TunnelGatewayBuffer ();
~TunnelGatewayBuffer ();
void PutI2NPMsg (const TunnelMessageBlock& block);
const std::vector<std::shared_ptr<I2NPMessage> >& GetTunnelDataMsgs () const { return m_TunnelDataMsgs; };
const std::vector<std::shared_ptr<const I2NPMessage> >& GetTunnelDataMsgs () const { return m_TunnelDataMsgs; };
void ClearTunnelDataMsgs ();
void CompleteCurrentTunnelDataMessage ();
@@ -27,8 +27,7 @@ namespace tunnel
private:
uint32_t m_TunnelID;
std::vector<std::shared_ptr<I2NPMessage> > m_TunnelDataMsgs;
std::vector<std::shared_ptr<const I2NPMessage> > m_TunnelDataMsgs;
std::shared_ptr<I2NPMessage> m_CurrentTunnelDataMsg;
size_t m_RemainingSize;
uint8_t m_NonZeroRandomBuffer[TUNNEL_DATA_MAX_PAYLOAD_SIZE];
@@ -39,7 +38,7 @@ namespace tunnel
public:
TunnelGateway (TunnelBase * tunnel):
m_Tunnel (tunnel), m_Buffer (tunnel->GetNextTunnelID ()), m_NumSentBytes (0) {};
m_Tunnel (tunnel), m_NumSentBytes (0) {};
void SendTunnelDataMsg (const TunnelMessageBlock& block);
void PutTunnelDataMsg (const TunnelMessageBlock& block);
void SendBuffer ();

View File

@@ -7,15 +7,22 @@
#include "Garlic.h"
#include "Transports.h"
#include "Log.h"
#include "Tunnel.h"
#include "TunnelPool.h"
#include "Destination.h"
#ifdef WITH_EVENTS
#include "Event.h"
#endif
namespace i2p
{
namespace tunnel
{
TunnelPool::TunnelPool (int numInboundHops, int numOutboundHops, int numInboundTunnels, int numOutboundTunnels):
m_NumInboundHops (numInboundHops), m_NumOutboundHops (numOutboundHops),
m_NumInboundTunnels (numInboundTunnels), m_NumOutboundTunnels (numOutboundTunnels), m_IsActive (true)
m_NumInboundTunnels (numInboundTunnels), m_NumOutboundTunnels (numOutboundTunnels), m_IsActive (true),
m_CustomPeerSelector(nullptr)
{
}
@@ -66,17 +73,25 @@ namespace tunnel
{
if (!m_IsActive) return;
{
#ifdef WITH_EVENTS
EmitTunnelEvent("tunnels.created", createdTunnel);
#endif
std::unique_lock<std::mutex> l(m_InboundTunnelsMutex);
m_InboundTunnels.insert (createdTunnel);
}
if (m_LocalDestination)
m_LocalDestination->SetLeaseSetUpdated ();
OnTunnelBuildResult(createdTunnel, eBuildResultOkay);
}
void TunnelPool::TunnelExpired (std::shared_ptr<InboundTunnel> expiredTunnel)
{
if (expiredTunnel)
{
{
#ifdef WITH_EVENTS
EmitTunnelEvent("tunnels.expired", expiredTunnel);
#endif
expiredTunnel->SetTunnelPool (nullptr);
for (auto& it: m_Tests)
if (it.second.second == expiredTunnel) it.second.second = nullptr;
@@ -90,9 +105,14 @@ namespace tunnel
{
if (!m_IsActive) return;
{
#ifdef WITH_EVENTS
EmitTunnelEvent("tunnels.created", createdTunnel);
#endif
std::unique_lock<std::mutex> l(m_OutboundTunnelsMutex);
m_OutboundTunnels.insert (createdTunnel);
}
OnTunnelBuildResult(createdTunnel, eBuildResultOkay);
//CreatePairedInboundTunnel (createdTunnel);
}
@@ -100,6 +120,9 @@ namespace tunnel
{
if (expiredTunnel)
{
#ifdef WITH_EVENTS
EmitTunnelEvent("tunnels.expired", expiredTunnel);
#endif
expiredTunnel->SetTunnelPool (nullptr);
for (auto& it: m_Tests)
if (it.second.first == expiredTunnel) it.second.first = nullptr;
@@ -128,13 +151,13 @@ namespace tunnel
std::shared_ptr<OutboundTunnel> TunnelPool::GetNextOutboundTunnel (std::shared_ptr<OutboundTunnel> excluded) const
{
std::unique_lock<std::mutex> l(m_OutboundTunnelsMutex);
std::unique_lock<std::mutex> l(m_OutboundTunnelsMutex);
return GetNextTunnel (m_OutboundTunnels, excluded);
}
std::shared_ptr<InboundTunnel> TunnelPool::GetNextInboundTunnel (std::shared_ptr<InboundTunnel> excluded) const
{
std::unique_lock<std::mutex> l(m_InboundTunnelsMutex);
std::unique_lock<std::mutex> l(m_InboundTunnelsMutex);
return GetNextTunnel (m_InboundTunnels, excluded);
}
@@ -148,11 +171,27 @@ namespace tunnel
{
if (it->IsEstablished () && it != excluded)
{
if(HasLatencyRequirement() && it->LatencyIsKnown() && !it->LatencyFitsRange(m_MinLatency, m_MaxLatency)) {
i ++;
continue;
}
tunnel = it;
i++;
}
if (i > ind && tunnel) break;
}
if(HasLatencyRequirement() && !tunnel) {
ind = rand () % (tunnels.size ()/2 + 1), i = 0;
for (const auto& it: tunnels)
{
if (it->IsEstablished () && it != excluded)
{
tunnel = it;
i++;
}
if (i > ind && tunnel) break;
}
}
if (!tunnel && excluded && excluded->IsEstablished ()) tunnel = excluded;
return tunnel;
}
@@ -180,15 +219,6 @@ namespace tunnel
void TunnelPool::CreateTunnels ()
{
int num = 0;
{
std::unique_lock<std::mutex> l(m_InboundTunnelsMutex);
for (const auto& it : m_InboundTunnels)
if (it->IsEstablished ()) num++;
}
for (int i = num; i < m_NumInboundTunnels; i++)
CreateInboundTunnel ();
num = 0;
{
std::unique_lock<std::mutex> l(m_OutboundTunnelsMutex);
for (const auto& it : m_OutboundTunnels)
@@ -196,6 +226,18 @@ namespace tunnel
}
for (int i = num; i < m_NumOutboundTunnels; i++)
CreateOutboundTunnel ();
num = 0;
{
std::unique_lock<std::mutex> l(m_InboundTunnelsMutex);
for (const auto& it : m_InboundTunnels)
if (it->IsEstablished ()) num++;
}
for (int i = num; i < m_NumInboundTunnels; i++)
CreateInboundTunnel ();
if (num < m_NumInboundTunnels && m_NumInboundHops <= 0 && m_LocalDestination) // zero hops IB
m_LocalDestination->SetLeaseSetUpdated (); // update LeaseSet immediately
}
void TunnelPool::TestTunnels ()
@@ -303,7 +345,12 @@ namespace tunnel
test.first->SetState (eTunnelStateEstablished);
if (test.second->GetState () == eTunnelStateTestFailed)
test.second->SetState (eTunnelStateEstablished);
LogPrint (eLogDebug, "Tunnels: test of ", msgID, " successful. ", i2p::util::GetMillisecondsSinceEpoch () - timestamp, " milliseconds");
uint64_t dlt = i2p::util::GetMillisecondsSinceEpoch () - timestamp;
LogPrint (eLogDebug, "Tunnels: test of ", msgID, " successful. ", dlt, " milliseconds");
// update latency
uint64_t latency = dlt / 2;
test.first->AddLatencySample(latency);
test.second->AddLatencySample(latency);
}
else
{
@@ -327,9 +374,18 @@ namespace tunnel
bool TunnelPool::SelectPeers (std::vector<std::shared_ptr<const i2p::data::IdentityEx> >& peers, bool isInbound)
{
if (m_ExplicitPeers) return SelectExplicitPeers (peers, isInbound);
int numHops = isInbound ? m_NumInboundHops : m_NumOutboundHops;
if (numHops <= 0) return true; // peers is empty
// peers is empty
if (numHops <= 0) return true;
// custom peer selector in use ?
{
std::lock_guard<std::mutex> lock(m_CustomPeerSelectorMutex);
if (m_CustomPeerSelector)
return m_CustomPeerSelector->SelectPeers(peers, numHops, isInbound);
}
// explicit peers in use
if (m_ExplicitPeers) return SelectExplicitPeers (peers, isInbound);
auto prevHop = i2p::context.GetSharedRouterInfo ();
if(i2p::transport::transports.RoutesRestricted())
{
@@ -477,6 +533,61 @@ namespace tunnel
LogPrint (eLogDebug, "Tunnels: Creating paired inbound tunnel...");
auto tunnel = tunnels.CreateInboundTunnel (std::make_shared<TunnelConfig>(outboundTunnel->GetInvertedPeers ()), outboundTunnel);
tunnel->SetTunnelPool (shared_from_this ());
}
}
void TunnelPool::SetCustomPeerSelector(TunnelPeerSelector selector)
{
std::lock_guard<std::mutex> lock(m_CustomPeerSelectorMutex);
m_CustomPeerSelector = selector;
}
void TunnelPool::UnsetCustomPeerSelector()
{
SetCustomPeerSelector(nullptr);
}
bool TunnelPool::HasCustomPeerSelector()
{
std::lock_guard<std::mutex> lock(m_CustomPeerSelectorMutex);
return m_CustomPeerSelector != nullptr;
}
std::shared_ptr<InboundTunnel> TunnelPool::GetLowestLatencyInboundTunnel(std::shared_ptr<InboundTunnel> exclude) const
{
std::shared_ptr<InboundTunnel> tun = nullptr;
std::unique_lock<std::mutex> lock(m_InboundTunnelsMutex);
uint64_t min = 1000000;
for (const auto & itr : m_InboundTunnels) {
if(!itr->LatencyIsKnown()) continue;
auto l = itr->GetMeanLatency();
if (l >= min) continue;
tun = itr;
if(tun == exclude) continue;
min = l;
}
return tun;
}
std::shared_ptr<OutboundTunnel> TunnelPool::GetLowestLatencyOutboundTunnel(std::shared_ptr<OutboundTunnel> exclude) const
{
std::shared_ptr<OutboundTunnel> tun = nullptr;
std::unique_lock<std::mutex> lock(m_OutboundTunnelsMutex);
uint64_t min = 1000000;
for (const auto & itr : m_OutboundTunnels) {
if(!itr->LatencyIsKnown()) continue;
auto l = itr->GetMeanLatency();
if (l >= min) continue;
tun = itr;
if(tun == exclude) continue;
min = l;
}
return tun;
}
void TunnelPool::OnTunnelBuildResult(std::shared_ptr<Tunnel> tunnel, TunnelBuildResult result)
{
auto peers = tunnel->GetPeers();
if(m_CustomPeerSelector) m_CustomPeerSelector->OnBuildResult(peers, tunnel->IsInbound(), result);
}
}
}

View File

@@ -23,6 +23,25 @@ namespace tunnel
class InboundTunnel;
class OutboundTunnel;
enum TunnelBuildResult {
eBuildResultOkay, // tunnel was built okay
eBuildResultRejected, // tunnel build was explicitly rejected
eBuildResultTimeout // tunnel build timed out
};
/** interface for custom tunnel peer selection algorithm */
struct ITunnelPeerSelector
{
typedef std::shared_ptr<const i2p::data::IdentityEx> Peer;
typedef std::vector<Peer> TunnelPath;
virtual bool SelectPeers(TunnelPath & peers, int hops, bool isInbound) = 0;
virtual bool OnBuildResult(TunnelPath & peers, bool isInbound, TunnelBuildResult result) = 0;
};
typedef std::shared_ptr<ITunnelPeerSelector> TunnelPeerSelector;
class TunnelPool: public std::enable_shared_from_this<TunnelPool> // per local destination
{
public:
@@ -45,7 +64,6 @@ namespace tunnel
std::shared_ptr<OutboundTunnel> GetNextOutboundTunnel (std::shared_ptr<OutboundTunnel> excluded = nullptr) const;
std::shared_ptr<InboundTunnel> GetNextInboundTunnel (std::shared_ptr<InboundTunnel> excluded = nullptr) const;
std::shared_ptr<OutboundTunnel> GetNewOutboundTunnel (std::shared_ptr<OutboundTunnel> old) const;
void TestTunnels ();
void ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg);
void ProcessDeliveryStatus (std::shared_ptr<I2NPMessage> msg);
@@ -56,9 +74,25 @@ namespace tunnel
int GetNumInboundTunnels () const { return m_NumInboundTunnels; };
int GetNumOutboundTunnels () const { return m_NumOutboundTunnels; };
private:
void SetCustomPeerSelector(TunnelPeerSelector selector);
void UnsetCustomPeerSelector();
bool HasCustomPeerSelector();
/** @brief make this tunnel pool yield tunnels that fit latency range [min, max] */
void RequireLatency(uint64_t min, uint64_t max) { m_MinLatency = min; m_MaxLatency = max; }
/** @brief return true if this tunnel pool has a latency requirement */
bool HasLatencyRequirement() const { return m_MinLatency > 0 && m_MaxLatency > 0; }
/** @brief get the lowest latency tunnel in this tunnel pool regardless of latency requirements */
std::shared_ptr<InboundTunnel> GetLowestLatencyInboundTunnel(std::shared_ptr<InboundTunnel> exclude=nullptr) const;
std::shared_ptr<OutboundTunnel> GetLowestLatencyOutboundTunnel(std::shared_ptr<OutboundTunnel> exclude=nullptr) const;
void OnTunnelBuildResult(std::shared_ptr<Tunnel> tunnel, TunnelBuildResult result);
private:
void CreateInboundTunnel ();
void CreateOutboundTunnel ();
void CreatePairedInboundTunnel (std::shared_ptr<OutboundTunnel> outboundTunnel);
@@ -80,7 +114,12 @@ namespace tunnel
mutable std::mutex m_TestsMutex;
std::map<uint32_t, std::pair<std::shared_ptr<OutboundTunnel>, std::shared_ptr<InboundTunnel> > > m_Tests;
bool m_IsActive;
std::mutex m_CustomPeerSelectorMutex;
TunnelPeerSelector m_CustomPeerSelector;
uint64_t m_MinLatency=0; // if > 0 this tunnel pool will try building tunnels with minimum latency by ms
uint64_t m_MaxLatency=0; // if > 0 this tunnel pool will try building tunnels with maximum latency by ms
public:
// for HTTP only

View File

@@ -66,10 +66,13 @@ namespace transport
try
{
m_Service.run ();
// Discover failed
break; // terminate the thread
}
catch (std::exception& ex)
{
LogPrint (eLogError, "UPnP: runtime exception: ", ex.what ());
PortMapping ();
}
}
}

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