Compare commits

..

1144 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
deca217544 don't always set port 4567 2016-08-12 11:07:00 -04:00
orignal
c09212de81 Merge pull request #604 from PurpleI2P/openssl
recent changes
2016-08-12 10:44:02 -04:00
orignal
db8d93d308 2.9.0 2016-08-12 10:43:06 -04:00
orignal
4c96106666 reseed.file added 2016-08-12 10:37:03 -04:00
orignal
8e849ea6f8 reseed from file 2016-08-12 10:33:53 -04:00
orignal
82d80d2ead moved Config.cpp to libi2pd 2016-08-12 10:28:36 -04:00
orignal
a5da55d0f7 Update build_notes_android.md 2016-08-12 09:38:31 -04:00
orignal
702e6c8080 buld instruction without QT 2016-08-12 09:30:35 -04:00
orignal
46e957ab7e Merge pull request #603 from atnaguzin/patch-2
Replaced arrows to HTML code. Deleted tab spaces.
2016-08-11 07:20:54 -04:00
MXPLRS | Kirill
c27f8a5c1e Replaced arrows to HTML code. Deleted tab spaces. 2016-08-11 12:25:26 +03:00
orignal
8e835f2f6b fixed race condition 2016-08-09 20:51:54 -04:00
orignal
35e8a027ad Merge pull request #600 from brain5lug/openssl
copy elimination for ranges #part4
2016-08-09 20:50:44 -04:00
brain5lug
94642f9066 copy elimination for ranges #part4 2016-08-10 01:16:24 +03:00
orignal
2dd5de4373 handle default subscription in separate thread 2016-08-09 10:17:40 -04:00
orignal
793e80490c Merge pull request #598 from brain5lug/openssl
copy elimination for ranges #part3
2016-08-09 07:25:24 -04:00
orignal
727a1f4ddd Merge pull request #599 from atnaguzin/patch-1
Update HTTPServer.cpp
2016-08-09 07:22:34 -04:00
MXPLRS | Kirill
6a752a56ff Update HTTPServer.cpp 2016-08-09 13:54:47 +03:00
orignal
32466e3804 don't notify before wait 2016-08-08 22:15:09 -04:00
brain5lug
a530503c0c copy elimination for ranges #part3 2016-08-09 01:53:37 +03:00
orignal
7ba4af7e2e fixed build error 2016-08-08 17:31:32 -04:00
orignal
c9c05ad2a8 Merge pull request #596 from atnaguzin/patch-2
"SAM sessions" link check added
2016-08-08 17:23:58 -04:00
MXPLRS | Kirill
ab1df3a1d0 Update HTTPServer.cpp 2016-08-08 23:37:16 +03:00
orignal
56a60772a4 fixed potential deadlock 2016-08-08 11:53:38 -04:00
orignal
63e6731207 Merge pull request #595 from brain5lug/openssl
copy elimination for ranges #part2
2016-08-07 18:31:32 -04:00
brain5lug
8b53ded53a copy elimination for ranges #part2 2016-08-08 00:52:48 +03:00
orignal
d5075d706c eliminate decay timer 2016-08-07 16:27:36 -04:00
Jeff Becker
03927b0a68 Merge branch 'upstream-openssl' 2016-08-06 20:27:57 -04:00
orignal
0d88b8012b Merge pull request #591 from brain5lug/openssl
The temporary object elemination part#1
2016-08-05 19:33:19 -04:00
brain5lug
a583c21136 merge branch 'openssl' of https://github.com/PurpleI2P/i2pd into openssl 2016-08-06 01:26:34 +03:00
brain5lug
b8ec63cf8c copy ellimination for ranges #part1 2016-08-06 01:03:13 +03:00
l-n-s
4b9afdf53a fix typo 2016-08-05 18:06:06 +00:00
orignal
788d1650a2 Merge pull request #590 from brain5lug/openssl
tiny commit to check pulling
2016-08-04 17:31:52 -04:00
brain5lug
8f58886a21 tiny commit to check pulling 2016-08-04 23:27:07 +03:00
orignal
94b3bb2391 adjust termination timeout 2016-08-04 10:26:50 -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
orignal
5698ff9c4c wait for UPnP discovery during startup 2016-07-31 10:22:41 -04:00
orignal
47b562b032 temporary disable OS X 2016-07-30 18:22:14 -04:00
orignal
69234db848 Merge pull request #588 from atnaguzin/patch-1
Update 01-tune-build-opts.patch
2016-07-30 14:58:09 -04:00
MXPLRS | Kirill
ebc132ea65 Update 01-tune-build-opts.patch 2016-07-30 21:27:17 +03:00
orignal
0899eeddc0 UPnP for x86_64 2016-07-29 10:59:44 -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
orignal
1062776762 cleanup router's tags 2016-07-28 13:24:25 -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
hagen
b1aeae6772 * util.{cpp,h} : kill with fire i2p::util::http (#314, closes #432) 2016-07-27 13:10:29 +00:00
hagen
5a6bd38d22 * docs/configuration.md 2016-07-27 13:10:29 +00:00
hagen
97da8e2f2e * HTTPServer.cpp : true random password 2016-07-27 13:10:28 +00:00
orignal
10be150503 invoke GracefulShutdown for Win32 2016-07-26 12:11:52 -04:00
orignal
36aa248556 Graceful shutdown 2016-07-26 11:52:44 -04:00
orignal
183c22cc84 rollback 2016-07-26 11:22:53 -04:00
orignal
63f4cf3d07 graceful shutdown for windows 2016-07-26 11:10:10 -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
orignal
061720bcf0 handle'\r\n' terminated address from Transmission 2016-07-24 10:20:37 -04:00
orignal
11585327bf correct status response 2016-07-24 09:24:20 -04:00
orignal
2793eeb10a Merge pull request #581 from PurpleI2P/reloadconfig_on_SIGHUP
Reload client config on SIGHUP
2016-07-24 08:15:00 -04:00
Mikal Villa
8fb093b272 Reload client config on SIGHUP 2016-07-24 17:58:26 +08:00
Jeff Becker
c90c008f65 Merge remote-tracking branch 'purple/openssl' 2016-07-23 21:22:02 -04:00
orignal
3d1a7f173c select Charlie based on Alice's address type 2016-07-22 13:08:41 -04:00
orignal
ba078f3ff5 support peer test for ipv6 2016-07-22 12:50:03 -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
orignal
6a1049bfb7 override address if v6 only 2016-07-22 10:34:56 -04:00
Jeff Becker
5995ab3f4c Merge branch 'upstream-openssl' 2016-07-22 10:18:58 -04:00
orignal
a4112ebed2 add both v4 and v6 addresses 2016-07-22 10:16:57 -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
orignal
3b9b827ebf getnick doean't return error if was set before 2016-07-21 20:42:40 -04:00
/dev/null
c908beade2 Added client tunnel reload on SIGHUP for Linux 2016-07-21 17:57:43 -06:00
orignal
abcf030181 more BOB error messages 2016-07-21 14:02:13 -04:00
orignal
22a16da09e fixed android build 2016-07-20 21:44:17 -04:00
orignal
0e31da5e51 Merge pull request #578 from PurpleI2P/openssl
recent changes
2016-07-20 18:33:43 -04:00
orignal
61e6a03d70 check correct #ifdef for windows 2016-07-20 09:33:50 -04:00
orignal
3f2119556f upnp.name 2016-07-19 12:05:32 -04:00
orignal
77493d0d09 configurable UPnP name 2016-07-19 12:03:03 -04:00
orignal
340c73cbdf send actual status back 2016-07-19 11:08:28 -04:00
hagen
ea7f4447b2 * Reseed.cpp : check response code 2016-07-19 00:59:16 +00:00
hagen
19f3c75a8d * Reseed.cpp : use new response parsing 2016-07-19 00:59:15 +00:00
hagen
25ba08abcf * Reseed.cpp : use new http classes 2016-07-19 00:59:13 +00:00
hagen
26be0c7c82 * move i2p::util::config::GetHost to RouterContext.cpp 2016-07-19 00:59:11 +00:00
orignal
b7c5e3b5d5 correct termination of UPnP 2016-07-18 14:01:58 -04:00
Jeff
701653a6bd Merge pull request #576 from rjmalagon/patch-1
Update BOB.cpp
2016-07-16 12:18:51 -04:00
orignal
2c099c7f0e Update BOB.cpp
fixed typo
2016-07-16 10:21:07 -04:00
Ricardo Jesus Malagon Jerez
fba53117d8 Update BOB.cpp
Fix a little typo
2016-07-16 09:21:01 -05:00
orignal
e1bf53d90a handle status command 2016-07-16 09:31:33 -04:00
hagen
ecc82739d8 * HTTP.cpp : fix is_gzipped() 2016-07-16 12:13:00 +00:00
hagen
d16afa9692 * Addressbook.cpp : tune logs 2016-07-16 12:11:23 +00:00
hagen
9f3ce09e88 * Addressbook.{cpp,h} : show new hosts 2016-07-16 12:11:23 +00:00
hagen
cbfb1edb79 * Addressbook.{cpp,h}:
* Request() now renamed and returns value
  * move spawning download thread to Addressbook class
  * CheckSubscription() renamed and handles return value of Request()
2016-07-16 12:11:10 +00:00
hagen
b6c336bf72 * Addressbook.cpp : use HTTPRes class for response parsing 2016-07-16 12:10:43 +00:00
hagen
403e34506e * Addressbook.cpp : use HTTPReq class for building request 2016-07-16 12:10:37 +00:00
hagen
83db868542 * Addressbook.cpp : use new URL class, unwrap some conditions 2016-07-16 12:10:34 +00:00
orignal
1b6e673b50 Merge pull request #574 from majestrate/master
Floodfill fixes, testnet fixes, add lease set view page in HTTPServer
2016-07-15 14:52:12 -04:00
Jeff Becker
a0144f093f fix typo 2016-07-15 13:59:56 -04:00
Jeff Becker
a5d84bf8a9 pedantic whitespace fix 2016-07-15 13:54:34 -04:00
Jeff Becker
84bb740e62 clean up code 2016-07-15 13:52:55 -04:00
Jeff Becker
d37482ada1 IT WORKS, floodfill confirmed working on test network 2016-07-15 13:31:31 -04:00
Jeff Becker
75fc8202ab fix off by ones 2016-07-15 12:49:45 -04:00
Jeff Becker
24aff15752 off by one 2016-07-15 10:55:02 -04:00
Jeff Becker
338b9928f0 repeat correction 2016-07-15 10:45:38 -04:00
Jeff Becker
b9607b4b8e correct last commit 2016-07-15 10:44:43 -04:00
Jeff Becker
586f241074 OFF BY ONE 2016-07-15 10:44:08 -04:00
orignal
e298987d9e fixed build error 2016-07-15 09:45:54 -04:00
Jeff Becker
71cc4b5bf2 add lease set page in http server 2016-07-15 09:38:21 -04:00
Jeff Becker
4fdd9feddc don't try to republish forever 2016-07-14 16:59:26 -04:00
orignal
caf7da1053 set reachable/unreachable for v4 only 2016-07-14 14:29:45 -04:00
Jeff Becker
7d04ba0fc3 CXXFLAGS -> NEEDED_CXXFLAGS 2016-07-14 14:25:20 -04:00
orignal
d98d091c43 use list instead vector for addresses 2016-07-14 14:10:38 -04:00
Jeff Becker
3ad196c4c7 fix meshnet mode:
* don't default to ipv4 when creating router.info
* add i2p::util::config::GetHost for getting host to use from config
* proper check for no transports in Transports.cpp on startup
2016-07-14 09:23:33 -04:00
orignal
562f320198 load libstd 2016-07-13 22:34:53 -04:00
orignal
3e5581e094 create addresses in defualt constructor 2016-07-13 22:33:39 -04:00
hagen
c4721e1020 + HTTP.{cpp,h} : add HTTPRes::is_gzipped() 2016-07-14 00:34:42 +00:00
hagen
728f2670f3 * HTTPServer.cpp : drop jumpservices : now handled by HTTPProxy itself 2016-07-14 00:34:38 +00:00
hagen
8dd157d2eb * HTTPProxy.cpp : html error messages 2016-07-14 00:32:41 +00:00
orignal
2d40d69fa2 fixed race condition 2016-07-13 12:56:23 -04:00
orignal
f2f0d69bce Update configuration.md 2016-07-13 11:14:06 -04:00
orignal
812f5045b0 enable UPnP for windows and android by default 2016-07-13 11:12:51 -04:00
orignal
9a8e7b11e5 detect network status at android 2016-07-13 10:09:22 -04:00
orignal
e213e695c8 Merge pull request #572 from hypnosis-i2p/openssl
reworked android. added a build script.
2016-07-13 08:46:23 -04:00
hypnosis-i2p
e24eea313c updated 2016-07-13 19:40:09 +08:00
hypnosis-i2p
a3286ebac3 updated 2016-07-13 19:36:18 +08:00
hypnosis-i2p
95ae23a32c gitignore 2016-07-13 11:08:35 +08:00
hypnosis-i2p
d240f3242c gitignore 2016-07-13 11:08:35 +08:00
hypnosis-i2p
814f60a512 reworked android. added a build script. 2016-07-13 11:08:35 +08:00
hagen
fac6229e43 * cmake debug (closes #562) 2016-07-13 01:01:47 +00:00
hagen
c528d739c8 Merge branch 'new-proxy' into openssl 2016-07-13 00:49:00 +00:00
orignal
c664be52d7 limit outgoing queue size 2016-07-12 16:26:36 -04:00
orignal
4ac4f44ba7 limit delayed messages queue size 2016-07-12 12:37:39 -04:00
hagen
174430e3b5 * HTTPServer.cpp : rename command 2016-07-12 02:30:39 +00:00
hagen
762b21f809 * Streaming.cpp : tune log messages 2016-07-12 02:23:24 +00:00
hagen
9340bf385e * Daemon.cpp : make upnp configurable via options 2016-07-12 02:21:52 +00:00
hagen
9f5be52a97 * UPnP.cpp : tune log messages 2016-07-12 02:21:52 +00:00
hagen
9dc5a4fce3 * UPnP.{cpp,h} : cleanup & add class stub if opt-out 2016-07-12 02:21:52 +00:00
hagen
fda3cd5fe7 * Config.cpp : add --upnp.enabled option 2016-07-12 02:21:52 +00:00
hagen
6b8469e9a3 * docs/configuration.md : fix markdown 2016-07-12 02:21:52 +00:00
orignal
7dbbe5a7d8 wait until tunnels get created 2016-07-11 14:35:59 -04:00
orignal
2d6fdeb7ad Merge pull request #567 from hypnosis-i2p/openssl
android without qt — initial push
2016-07-09 21:50:33 -04:00
hypnosis-i2p
4d3a01a5fe android ported all + isConnected notif 2016-07-10 09:42:42 +08:00
orignal
8cc6756815 Merge pull request #566 from PurpleI2P/openssl
recent changes
2016-07-09 21:41:43 -04:00
hypnosis-i2p
5967ab75b1 Merge branch 'openssl' of https://github.com/hypnosis-i2p/i2pd into openssl 2016-07-10 04:54:15 +08:00
hypnosis-i2p
69c954760a android without qt initial commit 2016-07-10 04:54:11 +08:00
hypnosis-i2p
40a4c3ccbd junk 2016-07-10 00:19:20 +08:00
hypnosis-i2p
aacb9d9570 merged 2016-07-10 00:19:20 +08:00
orignal
9b6c229b71 remember tunnels selection for following messages 2016-07-08 14:17:41 -04:00
orignal
1da5be2871 clean up unconfirmed tags faster 2016-07-07 22:39:20 -04:00
orignal
66dafca61a select existing connection for first hop of a tunnel 2016-07-06 22:34:24 -04:00
hypnosis-i2p
1f22b5b083 junk 2016-07-07 02:46:11 +08:00
hypnosis-i2p
5be0b7a731 merged 2016-07-07 02:45:13 +08:00
l-n-s
b64b5d9103 Update build_notes_android.md 2016-07-06 17:55:36 +00:00
l-n-s
953d78da9e Update build_notes_android.md 2016-07-06 16:10:30 +00:00
l-n-s
ce9e0981a2 Update index.rst 2016-07-06 16:03:54 +00:00
orignal
76e1114a1f Update build_notes_android.md 2016-07-06 11:30:11 -04:00
orignal
cfc80b491f Update build_notes_android.md 2016-07-06 11:29:43 -04:00
orignal
43ed05d3c2 Create build_notes_android.md 2016-07-06 11:29:00 -04:00
orignal
4cf5ce871f destroy socket upon receive an ack for close 2016-07-05 17:52:11 -04:00
orignal
91ec08df4e wait for close from other side 2016-07-05 09:52:18 -04:00
orignal
c79363ef63 Merge pull request #563 from PurpleI2P/openssl
recent changes
2016-07-05 09:40:17 -04:00
orignal
cc6672198a Merge pull request #560 from atnaguzin/openssl
updated appveyor.yml
2016-07-02 07:14:21 -04:00
MXPLRS | Kirill
17cdf7c79d Merge pull request #7 from PurpleI2P/openssl
fixed VS build error
2016-07-02 13:52:18 +03:00
orignal
725f939f35 fixed VS build error 2016-07-02 06:45:15 -04:00
MXPLRS | Kirill
e2df00bb2e Merge pull request #6 from PurpleI2P/openssl
Upstream pull
2016-07-01 15:34:01 +03:00
orignal
a9a33c6179 fixed build error 2016-07-01 08:31:27 -04:00
orignal
8e7eb87a2d Merge pull request #558 from atnaguzin/patch-1
Update i2pd_qt.pro
2016-07-01 08:10:25 -04:00
MXPLRS | Kirill
217004e7a5 Update appveyor.yml 2016-07-01 13:22:47 +03:00
MXPLRS | Kirill
4d10848984 Update i2pd_qt.pro 2016-07-01 13:07:24 +03:00
hagen
da2c04f681 * HTTPProxy.cpp : show created stream IDs in log 2016-07-01 00:39:07 +00:00
hagen
8deb327b3b * HTTPProxy.cpp :
* migrate to HTTPReq
  * change work with buffers
  * code cleanup
2016-07-01 00:38:55 +00:00
hagen
642b01bf0d * HTTPProxy.cpp : add SanitizeHTTPRequest() 2016-07-01 00:38:31 +00:00
hagen
9fd78b1eb1 * HTTPProxy.cpp : rename variable 2016-07-01 00:38:31 +00:00
hagen
66c09fc44c * HTTPProxy.cpp : HandleJumpServices() -> ExtractAddressHelper() 2016-07-01 00:38:31 +00:00
hagen
2cb5e1a6c2 * HTTPProxy.cpp : kill ExtractRequest() 2016-07-01 00:38:31 +00:00
hagen
02ac638bd4 * HTTP.cpp : add comments, update test case 2016-07-01 00:34:31 +00:00
hagen
323f74c43a * HTTP.cpp : fuck the "special cases", use nginx rewriting frontend or some 2016-07-01 00:34:28 +00:00
MXPLRS | Kirill
1e56d17d39 Merge pull request #5 from PurpleI2P/openssl
upstream pull
2016-07-01 02:12:09 +03:00
orignal
16af8e082b Merge pull request #557 from majestrate/merge-testnet-changes
Merge testnet changes
2016-06-30 18:24:49 -04:00
Jeff Becker
9215a54c23 revert 2016-06-30 18:05:41 -04:00
Jeff Becker
fab34d3dbb clean up identation 2016-06-30 17:59:14 -04:00
orignal
03d7330af5 Merge pull request #556 from PurpleI2P/openssl
floodfill fixes
2016-06-30 17:51:26 -04:00
Jeff Becker
346b0c9d68 disable testnet by default 2016-06-30 17:50:47 -04:00
Jeff Becker
c29359e7a8 Merge remote-tracking branch 'purple/openssl' into meshnet 2016-06-30 17:48:46 -04:00
Jeff Becker
4b903931bc update i2pd testnet addressbook url 2016-06-30 17:34:16 -04:00
Jeff Becker
4e0929e71a don't print out junk when logging 2016-06-30 17:24:33 -04:00
Jeff Becker
1bad097a13 don't check for LS expired for FF 2016-06-30 17:21:18 -04:00
Jeff Becker
331065eec6 remove expired LS 2016-06-30 17:01:00 -04:00
orignal
f51ba499d5 Merge pull request #555 from majestrate/fix-ff-off-by-one
off by one?
2016-06-30 15:07:26 -04:00
Jeff Becker
f62ccc2d48 off by one? 2016-06-30 14:26:05 -04:00
orignal
cf485aa62e Merge pull request #554 from atnaguzin/openssl
added hiding information in webconsole
2016-06-30 14:25:39 -04:00
Jeff Becker
6b16a48568 revert 2016-06-30 14:24:55 -04:00
MXPLRS|Kirill
ff7cf503ae added hiding information in webconsole 2016-06-30 21:21:37 +03:00
Jeff Becker
2412a0d502 off by one? 2016-06-30 13:35:36 -04:00
MXPLRS | Kirill
b1612bb1ed Merge pull request #4 from PurpleI2P/openssl
Upstream pull
2016-06-30 20:30:45 +03:00
Jeff Becker
b8a205f755 netdb.cpp:
* explicitly define replyIdent

Daemon.cpp:
* wait for 1 second before checking if transports are bound to wait for transports to bind
2016-06-30 13:25:20 -04:00
orignal
eab08ea78c don't accept our own RouterInfo 2016-06-30 13:15:36 -04:00
Jeff Becker
d7653769b4 more logging 2016-06-30 12:57:20 -04:00
Jeff Becker
c72d9695da testnet changes 2016-06-30 12:27:23 -04:00
orignal
0493f00a7a pass null tunnel config for zero hops tunnel 2016-06-30 12:24:26 -04:00
Jeff Becker
ff757ddc88 don't check for exact LS 2016-06-30 11:27:40 -04:00
Jeff Becker
c4c495948a don't flood if older 2016-06-30 11:10:01 -04:00
Jeff Becker
986ee6bac3 Merge remote-tracking branch 'purple/openssl' into meshnet 2016-06-30 11:03:15 -04:00
orignal
6ab7e79987 send own copy of a message during flood 2016-06-30 11:01:21 -04:00
Jeff Becker
c6e35876fa if LS is older, assume updated so we reply 2016-06-30 10:51:39 -04:00
Jeff Becker
eb31accf20 Merge remote-tracking branch 'purple/openssl' into meshnet 2016-06-30 10:25:44 -04:00
orignal
ea1ba0f09b don't flood to itself 2016-06-30 10:21:53 -04:00
Jeff Becker
10911f5b64 make it compile 2016-06-30 09:52:37 -04:00
Jeff Becker
0eab8e9322 more pedantic logging changes 2016-06-30 09:49:28 -04:00
Jeff Becker
fb2602716e add floodfill to exclude 2016-06-30 09:48:08 -04:00
Jeff Becker
be68906ae9 logging tweaks 2016-06-30 09:45:06 -04:00
Jeff Becker
c53d6d80ba Merge branch 'master' into meshnet 2016-06-30 09:15:30 -04:00
Jeff Becker
1362177352 Merge remote-tracking branch 'purple/openssl' 2016-06-30 08:57:59 -04:00
xcps
28ab1230e2 '@' can exist in url path 2016-06-30 08:00:18 -04:00
orignal
0784934520 Merge pull request #551 from majestrate/http-router-ident
add RouterInfo hash to web ui
2016-06-30 07:46:28 -04:00
Jeff Becker
79fbf9d47f add RouterInfo hash to web ui 2016-06-30 07:41:40 -04:00
Jeff Becker
dabdc8e4c5 Merge branch 'upstream-openssl' 2016-06-30 07:40:15 -04:00
Jeff Becker
466ad192b0 add RouterInfo hash to web ui 2016-06-30 07:36:35 -04:00
Jeff Becker
ac59d9e1b6 Merge branch 'upstream-openssl' 2016-06-30 07:36:14 -04:00
Jeff Becker
d44245e1e9 add RouterInfo hash to web ui 2016-06-30 07:35:30 -04:00
orignal
c50105493a fixed zero-hops tunnels 2016-06-29 21:37:17 -04:00
Jeff Becker
2768a62f92 more logging 2016-06-29 18:05:08 -04:00
Jeff Becker
5841d0d87d add more specific logging 2016-06-29 17:59:56 -04:00
Jeff Becker
2a796051bf try fixing LS 2016-06-29 17:42:26 -04:00
Jeff Becker
50286fd173 use inet_ntop properly 2016-06-29 16:10:43 -04:00
Jeff Becker
44f0bad2a6 fug 2016-06-29 15:52:32 -04:00
Jeff Becker
611b9c4fd1 ugh 2016-06-29 15:50:39 -04:00
Jeff Becker
3f50776062 ugh 2016-06-29 15:48:02 -04:00
Jeff Becker
c70d2ad6fa try fixing 2016-06-29 15:42:03 -04:00
Jeff Becker
9dd0bd604c fix 2016-06-29 15:37:37 -04:00
Jeff Becker
0565519509 don't print out raw ident 2016-06-29 15:15:33 -04:00
Jeff Becker
c282d95be1 undo change 2016-06-29 15:10:33 -04:00
Jeff Becker
766286b8bc undo pedantic change 2016-06-29 14:57:42 -04:00
Jeff Becker
f405c62f1e pedantic style and logging changes 2016-06-29 14:56:00 -04:00
Jeff Becker
93fbd7b3ba Merge branch 'upstream-openssl' into meshnet 2016-06-29 13:32:39 -04:00
Jeff Becker
ae5cea7f36 change order of initialization 2016-06-29 11:59:43 -04:00
Jeff Becker
8b7b6cfbc5 try fixing segfault 2016-06-29 11:57:44 -04:00
orignal
f6e988d6fd support zero-hops tunnels for destinations 2016-06-29 11:26:46 -04:00
Jeff Becker
f88f68f248 Add bind to network interface option 2016-06-29 11:06:51 -04:00
Jeff Becker
14f2b24b16 update logging 2016-06-29 10:11:14 -04:00
Jeff Becker
f2dde98e2f add meshnet option to cmake build files 2016-06-29 09:37:38 -04:00
Jeff Becker
90d8ec0e81 add more logging 2016-06-29 09:37:21 -04:00
hagen
14cdb531c8 * Streaming.cpp : tune logs 2016-06-29 02:14:39 +00:00
Jeff Becker
3a50320f79 add more logging 2016-06-28 15:35:58 -04:00
Jeff Becker
597b5e6cfb use different constants for now in meshnet mode 2016-06-28 14:50:25 -04:00
Jeff Becker
2cd056cfb3 try banning non responsive routers 2016-06-28 14:43:55 -04:00
Jeff Becker
be6aab4c40 revert 2016-06-28 14:24:18 -04:00
Jeff Becker
d030df925f Merge remote-tracking branch 'purple/openssl' into meshnet 2016-06-28 14:21:09 -04:00
Jeff Becker
50756eb94a Merge branch 'restricted_routes' into meshnet 2016-06-28 13:28:57 -04:00
Jeff Becker
21b3576b66 stop using auto 2016-06-28 12:20:18 -04:00
hagen
2e5226356b * Tag.h : add (c) header 2016-06-28 14:31:40 +00:00
hagen
21b76d3d2b * Config.cpp : drop compat parser 2016-06-28 14:31:40 +00:00
hagen
2d252e6459 * HTTP.cpp : rename method 2016-06-28 14:31:40 +00:00
Jeff Becker
43be363542 Merge remote-tracking branch 'purple/openssl' into meshnet 2016-06-28 09:34:38 -04:00
orignal
8cb3e3418a send garlic cloves directly if garlic was received derectly 2016-06-28 09:31:41 -04:00
Jeff Becker
cd47ddd539 default to USE_MESHNET=yes 2016-06-28 07:57:48 -04:00
orignal
2be1c10522 Tag.h and Gzip.h/.cpp added 2016-06-28 07:11:55 -04:00
hagen
7f26670173 + tests/test-base-64.cpp 2016-06-28 01:45:14 +00:00
hagen
d838ce85c3 * Base.h : extract Tag template class to separate header 2016-06-28 01:45:11 +00:00
hagen
6350f5e6e8 * Base.cpp : extract gzip classes to separate file 2016-06-28 01:45:07 +00:00
orignal
10f3690ede Merge pull request #547 from atnaguzin/openssl
fixed #546
2016-06-27 20:30:31 -04:00
Jeff Becker
06daa8bb0e try shooting in the dark for workarround 2016-06-27 17:39:13 -04:00
Jeff Becker
34a90f442e try shooting in the dark for workarround 2016-06-27 17:37:31 -04:00
Jeff Becker
afe81dcdbe add logging 2016-06-27 17:25:29 -04:00
Jeff Becker
24d616672b revert daemon.cpp change 2016-06-27 17:11:03 -04:00
MXPLRS|Kirill
9cfc61cd45 fixed #546 2016-06-28 00:00:54 +03:00
MXPLRS | Kirill
487df84b90 Merge pull request #3 from PurpleI2P/openssl
Upstream pull
2016-06-27 23:54:53 +03:00
Jeff Becker
614c1306f6 use correct netid when using separate test network 2016-06-27 16:40:46 -04:00
Jeff Becker
07dca9bd16 tweak ssu mtu again for meshnet 2016-06-27 15:28:26 -04:00
Jeff Becker
1ebcbd5b0e use smaller mtu for meshnet mode 2016-06-27 15:06:15 -04:00
Jeff Becker
32644ddada try fixing duplicate Routers In tunnel path 2016-06-27 14:16:29 -04:00
Jeff Becker
cf3bab996e when routers < 5 and in meshnet mode do not select random peers 2016-06-27 14:00:04 -04:00
Jeff Becker
926ffe2581 change default addressbook for meshnet mode 2016-06-27 13:46:14 -04:00
Jeff Becker
fff3587d99 only set as testing when not in meshnet mode 2016-06-27 13:20:21 -04:00
Jeff Becker
866cf940da make always reachable when in meshnet mode 2016-06-27 13:15:05 -04:00
Jeff Becker
7868e1527e try fixing ipv6 ssu 2016-06-27 11:08:23 -04:00
Jeff Becker
556bfb752a disable meshnet by default, use make USE_MESHNET=yes to build for cjdns 2016-06-27 10:33:14 -04:00
Jeff Becker
a3b08654b4 try adding ipv6 only mode for ssu 2016-06-27 10:24:37 -04:00
Jeff Becker
4cf4436169 initial meshnet mode 2016-06-27 09:47:53 -04:00
hagen
e2acc55819 * HTTPProxy.cpp : unwrap HandleStreamRequestComplete() 2016-06-27 13:14:42 +00:00
hagen
6b29d6b8dc * HTTPProxy.cpp : unwrap AsyncSockRead() 2016-06-27 13:14:39 +00:00
hagen
b668c4c302 * add global switch USE_UPNP to makefile 2016-06-27 13:12:01 +00:00
hagen
646778227a * tune log messages 2016-06-27 13:11:10 +00:00
hagen
a973630cb4 * fix tests 2016-06-27 13:11:10 +00:00
hagen
4e7375c09c * Addressbook.cpp : move storage creation to Start() 2016-06-27 13:11:10 +00:00
hagen
881d0652e7 * update debian package defaults 2016-06-27 13:11:10 +00:00
orignal
0c46993baa Merge pull request #544 from atnaguzin/openssl
Updating i2pd_qt.pro
2016-06-27 06:54:49 -04:00
MXPLRS|Kirill
09b15f4940 edited i2pd_qt.pro 2016-06-27 05:55:07 +03:00
MXPLRS | Kirill
ebf7be56bb Merge pull request #2 from PurpleI2P/openssl
upstream pull
2016-06-27 05:52:56 +03:00
hagen
727068cc4b * HTTPProxy.cpp : migrate HTTPRequestFailed() to new http classes 2016-06-27 02:37:33 +00:00
hagen
6f77c6f3f4 * HTTPProxy.cpp : don't reuse part of httppserver, addresshelpers handling will be moved to proxy in future 2016-06-27 02:37:32 +00:00
hagen
c5e3e17eae * HTTPProxy.cpp : extract IsI2PAddress() from class 2016-06-27 02:37:30 +00:00
Jeff Becker
effdb70417 Merge branch 'upstream-openssl' into restricted_routes 2016-06-26 21:53:13 -04:00
hagen
e28f910c88 * enable travis for UPNP=ON back 2016-06-27 01:30:02 +00:00
hagen
c84468dbed * fix cmake build with upnp=on 2016-06-27 01:12:20 +00:00
orignal
4b9e39ac64 limit SSU outgoing windows 2016-06-26 17:03:04 -04:00
orignal
6b0c05ee7c Merge pull request #542 from majestrate/fix-rpi-static
fix static build for rpi linux
2016-06-26 13:31:14 -04:00
Jeff Becker
5c6ec70126 fix static build for rpi linux 2016-06-26 11:17:05 -04:00
orignal
2757ef94c9 don't include UPNP twice for android 2016-06-26 08:37:40 -04:00
orignal
9ba7120011 fixed build error 2016-06-26 08:32:36 -04:00
MXPLRS | Kirill
124f9ec44e Merge pull request #1 from PurpleI2P/openssl
upstream pull
2016-06-26 15:29:43 +03:00
orignal
0d229175cd Merge pull request #539 from hypnosis-i2p/openssl
added tray icon to linux and windows versions
2016-06-25 21:01:54 -04:00
hypnosis-i2p
10638b6e40 fixed unnecessary resources setting 2016-06-26 02:48:13 +08:00
hypnosis-i2p
134baad56d added tray icon to linux and windows versions 2016-06-26 02:32:54 +08:00
Jeff Becker
9a5984d750 Merge branch 'upstream-openssl' into restricted_routes 2016-06-25 10:55:22 -04:00
Jeff Becker
073f42e64a Merge branch 'upstream-openssl' into restricted_routes 2016-06-25 10:54:41 -04:00
orignal
096927beed don't sedn explicit Ack if no NACKs only 2016-06-24 21:54:58 -04:00
xcps
4bc76995d1 docs: default httpproxy.port changed to actual 4444 2016-06-24 19:29:59 -04:00
xcps
9f41151156 HTTP proxy redirects to 0.0.0.0:7070/?page=jumpservices 2016-06-24 19:25:48 -04:00
xcps
6b3bd755b0 fixtypo 2016-06-24 19:07:47 -04:00
xcps
7e580e6a0b Update HTTPServer.cpp 2016-06-24 17:58:46 -04:00
orignal
047c8eda22 stop accepting tunnels by graceful shutdown 2016-06-24 16:26:13 -04:00
orignal
f22e5c209c fixed QT linux build 2016-06-24 16:05:03 -04:00
orignal
8cc3a08871 Merge pull request #536 from hypnosis-i2p/openssl
Added graceful quit button; the code for stopping tunnels pending
2016-06-24 15:43:12 -04:00
hypnosis-i2p
35f6c6cb98 graceful quit button added 2016-06-25 03:38:50 +08:00
hypnosis-i2p
814b174f25 android version code bump 2016-06-25 03:38:50 +08:00
orignal
5fbaf0bc7d disabled UPNP=ON 2016-06-24 15:29:36 -04:00
orignal
ba772ab481 static miniupnpc 2016-06-24 14:20:35 -04:00
orignal
0a4888a18f link with miniupnp 2016-06-24 14:18:50 -04:00
orignal
fedbf2cc44 link UPnP with app if USE_UPNP is set 2016-06-24 13:15:51 -04:00
orignal
0f68bbac8e single #ifdef for protocol type 2016-06-23 14:01:41 -04:00
orignal
13e965096b UPnP for android 2016-06-23 12:57:36 -04:00
orignal
92961bb7bf i2cp for android 2016-06-23 11:23:06 -04:00
hagen
340686ba06 * HTTPProxy.{cpp,h} : rename classes, drop typedef 2016-06-23 13:24:44 +00:00
hagen
d8906f508c * HTTPProxy.cpp : HTTP error message cleanup 2016-06-23 13:24:44 +00:00
hagen
dde53ea4ba * HTTPProxy.cpp : HTTPRequestFailed() now responds with error message 2016-06-23 13:24:44 +00:00
hagen
225ed5b662 * HTTPProxy.{cpp,h} : move & sort headers 2016-06-23 13:24:44 +00:00
hagen
02857cf2b5 * Base.cpp : drop logger dependency 2016-06-23 13:24:44 +00:00
hagen
118a771980 * update changelog 2016-06-23 13:24:37 +00:00
orignal
359123564f Merge branch 'openssl' of https://github.com/PurpleI2P/i2pd into openssl 2016-06-22 15:49:02 -04:00
orignal
49d2878938 use local sockets for android 2016-06-22 15:48:36 -04:00
orignal
f08ea4a9a3 Merge pull request #535 from PurpleI2P/openssl
2.8.0
2016-06-22 15:47:22 -04:00
Jeff Becker
f2bc71bc2a Merge branch 'upstream-openssl' 2016-06-22 13:19:00 -04:00
orignal
2b1e40c6c6 Update build_notes_windows.md 2016-06-22 11:15:40 -04:00
orignal
105c2deeb3 Merge pull request #534 from atnaguzin/patch-2
markup update
2016-06-22 11:06:44 -04:00
MXPLRS | Kirill
c3dbbc9144 Update build_notes_windows.md 2016-06-22 18:04:11 +03:00
orignal
ca55a9a8a6 build with UPnP 2016-06-22 10:53:29 -04:00
orignal
7ea5af448e UPNP support from windows 2016-06-22 09:41:01 -04:00
orignal
2f6898142e Merge pull request #533 from atnaguzin/openssl
updated config files
2016-06-22 07:13:23 -04:00
MXPLRS | Kirill
02e0b8cc32 Update i2pd.conf 2016-06-22 11:49:22 +03:00
MXPLRS|Kirill
b962a4c69b updated config files 2016-06-22 11:46:37 +03:00
orignal
ed3e83df67 android is supported now 2016-06-21 12:34:20 -04:00
orignal
b07bff61f0 set target SDK to 23 2016-06-21 09:09:31 -04:00
Jeff Becker
a7c955055c Merge branch 'upstream-openssl' into restricted_routes 2016-06-21 08:15:26 -04:00
Jeff Becker
63c36e917e Merge branch 'upstream-openssl' into restricted_routes 2016-06-19 16:50:51 -04:00
orignal
fb213a1efd Merge pull request #523 from PurpleI2P/openssl
android build
2016-06-18 21:26:40 -04:00
Jeff Becker
93deb37c94 add primordial goo tier "hidden mode" (aka toy feature needs more work doesn't do it correctly all the way) 2016-06-18 11:10:58 -04:00
Jeff Becker
004a93a841 Merge branch 'transport_failsafe' into restricted_routes 2016-06-18 09:01:53 -04:00
Jeff Becker
336ab2d82a Merge remote-tracking branch 'purple/openssl' into restricted_routes 2016-06-17 11:04:46 -04:00
Jeff Becker
74a7e67002 implement restricted routes (initial) 2016-06-17 11:03:33 -04:00
orignal
f995595202 Merge pull request #521 from PurpleI2P/openssl
android
2016-06-17 09:41:35 -04:00
Jeff Becker
e868d427dd add options to not use ntcp or ssu 2016-06-17 09:02:12 -04:00
orignal
d7a06dc7a9 Merge pull request #516 from PurpleI2P/openssl
recent changes
2016-06-14 11:10:20 -04:00
Jeff Becker
fa68e392c8 don't abort when ntcp fails to bind 2016-06-13 11:34:44 -04:00
Jeff Becker
9eaa51442f update comment 2016-06-13 09:01:38 -04:00
Jeff Becker
09fc767bb0 fix another typo 2016-06-13 08:53:35 -04:00
Jeff Becker
ea7e6615f2 fix typo 2016-06-13 08:52:54 -04:00
Jeff Becker
a183ca8661 fix special case 2016-06-13 08:52:21 -04:00
Jeff Becker
05939a2bbc special case for i2p.rocks in proxy 2016-06-13 08:50:53 -04:00
Jeff Becker
543a372435 Merge branch 'fix_http_proxy_500' 2016-06-13 07:51:16 -04:00
orignal
ccc24337be Merge pull request #511 from PurpleI2P/openssl
recent changes
2016-06-10 14:46:23 -04:00
Jeff Becker
a1c81a63dd Merge remote-tracking branch 'purple/openssl'
Conflicts:
	I2CP.cpp
2016-06-08 10:37:51 -04:00
Jeff Becker
3cfbc05bf9 set pointer to null after delete 2016-06-08 09:56:13 -04:00
Jeff Becker
895820f14c Merge branch 'upstream-openssl' 2016-06-08 09:55:55 -04:00
Jeff Becker
f2d9d38c6f Merge remote-tracking branch 'purple/openssl' 2016-06-03 14:07:02 -04:00
Jeff Becker
f3856819fe Merge remote-tracking branch 'purple/openssl' 2016-06-03 13:01:49 -04:00
Jeff Becker
7a52ae18f1 Merge remote-tracking branch 'purple/openssl' 2016-06-03 12:13:08 -04:00
Jeff Becker
0f9376e959 Merge remote-tracking branch 'purple/openssl' 2016-06-02 07:53:10 -04:00
Jeff Becker
72e954b78f Merge remote-tracking branch 'purple/openssl' 2016-05-29 09:39:23 -04:00
Jeff Becker
a1c27aed6a Merge remote-tracking branch 'purple/master' 2016-05-29 09:36:34 -04:00
Jeff Becker
0b3a719a95 Merge branch 'master' of github.com:majestrate/i2pd
Fixed Conflicts:
	SAM.cpp
2016-05-29 09:36:01 -04:00
orignal
5e52b3609c Merge pull request #503 from PurpleI2P/openssl
recent changes
2016-05-27 14:31:03 -04:00
orignal
3b80de1747 Merge pull request #494 from PurpleI2P/openssl
2.7.0
2016-05-18 09:23:55 -04:00
orignal
0291cc2ef4 Merge pull request #493 from PurpleI2P/openssl
recent changes
2016-05-17 15:06:39 -04:00
Jeff Becker
9c1a6d042e Merge remote-tracking branch 'purple/openssl' 2016-05-16 16:04:48 -04:00
Jeff
d21043802e Merge branch 'upstream' 2016-05-11 10:02:49 -04:00
Jeff
47ce2398a4 fix http unit test SIGBUS in os x 2016-05-11 08:41:32 -04:00
Jeff
0a83d8e6a0 Merge branch 'openssl' 2016-05-11 08:19:51 -04:00
Jeff Becker
392f5f914a Merge remote-tracking branch 'purple/openssl' 2016-04-23 08:38:34 -04:00
orignal
799d25925a Merge pull request #478 from PurpleI2P/openssl
recent changes
2016-04-22 12:49:42 -04:00
orignal
f9b6b1bf76 Merge pull request #473 from PurpleI2P/openssl
precalculate elgamal
2016-04-13 15:10:55 -04:00
orignal
eefff148e9 Merge pull request #470 from PurpleI2P/openssl
recent changes
2016-04-08 09:41:46 -04:00
Jeff Becker
05f7578928 Merge remote-tracking branch 'purple/openssl' 2016-04-08 09:39:09 -04:00
orignal
41da48f5ff Merge pull request #468 from PurpleI2P/openssl
recent changes
2016-04-06 12:27:15 -04:00
Jeff Becker
9ae9ea18e1 Merge remote-tracking branch 'purple/openssl' 2016-04-02 10:13:53 -04:00
Jeff Becker
924f281536 * Don't set m_Session to nullptr in SAMSocket::Terminate
* check for null localDestination in SAMSocket::Terminate
2016-04-02 08:05:14 -04:00
orignal
b8a6946661 Merge pull request #462 from PurpleI2P/openssl
2.6.0
2016-03-31 10:34:08 -04:00
229 changed files with 12907 additions and 5298 deletions

2
.dir-locals.el Normal file
View File

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

12
.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
@@ -237,3 +238,12 @@ pip-log.txt
# Sphinx
docs/_build
/androidIdea/
# emacs files
*~
*\#*
# gdb files
.gdb_history

View File

@@ -3,7 +3,6 @@ cache:
apt: true
os:
- linux
- osx
sudo: required
dist: trusty
addons:

View File

@@ -6,14 +6,17 @@
#include <chrono>
#include <condition_variable>
#include <openssl/rand.h>
#include <boost/algorithm/string.hpp>
#include "Base.h"
#include "util.h"
#include "Identity.h"
#include "FS.h"
#include "Log.h"
#include "HTTP.h"
#include "NetDb.h"
#include "ClientContext.h"
#include "AddressBook.h"
#include "Config.h"
namespace i2p
{
@@ -172,7 +175,7 @@ namespace client
return 0;
}
for (auto it: addresses) {
for (const auto& it: addresses) {
f << it.first << "," << it.second.ToBase32 () << std::endl;
num++;
}
@@ -203,7 +206,7 @@ namespace client
}
//---------------------------------------------------------------------
AddressBook::AddressBook (): m_Storage(new AddressBookFilesystemStorage), m_IsLoaded (false), m_IsDownloading (false),
AddressBook::AddressBook (): m_Storage(nullptr), m_IsLoaded (false), m_IsDownloading (false),
m_DefaultSubscription (nullptr), m_SubscriptionsUpdateTimer (nullptr)
{
}
@@ -215,6 +218,8 @@ namespace client
void AddressBook::Start ()
{
if (!m_Storage)
m_Storage = new AddressBookFilesystemStorage;
m_Storage->Init();
LoadHosts (); /* try storage, then hosts.txt, then download */
StartSubscriptions ();
@@ -249,7 +254,7 @@ namespace client
}
LogPrint (eLogError, "Addressbook: subscription download timeout");
m_IsDownloading = false;
}
}
if (m_Storage)
{
m_Storage->Save (m_Addresses);
@@ -257,8 +262,6 @@ namespace client
m_Storage = nullptr;
}
m_DefaultSubscription = nullptr;
for (auto it: m_Subscriptions)
delete it;
m_Subscriptions.clear ();
}
@@ -337,12 +340,12 @@ namespace client
std::ifstream f (i2p::fs::DataDirPath("hosts.txt"), std::ifstream::in); // in text mode
if (f.is_open ())
{
LoadHostsFromStream (f);
LoadHostsFromStream (f, false);
m_IsLoaded = true;
}
}
bool AddressBook::LoadHostsFromStream (std::istream& f)
bool AddressBook::LoadHostsFromStream (std::istream& f, bool is_update)
{
std::unique_lock<std::mutex> l(m_AddressBookMutex);
int numAddresses = 0;
@@ -363,17 +366,18 @@ namespace client
std::string addr = s.substr(pos);
auto ident = std::make_shared<i2p::data::IdentityEx> ();
if (ident->FromBase64(addr))
{
m_Addresses[name] = ident->GetIdentHash ();
m_Storage->AddAddress (ident);
numAddresses++;
}
else
{
if (!ident->FromBase64(addr)) {
LogPrint (eLogError, "Addressbook: malformed address ", addr, " for ", name);
incomplete = f.eof ();
continue;
}
numAddresses++;
if (m_Addresses.count(name) > 0)
continue; /* already exists */
m_Addresses[name] = ident->GetIdentHash ();
m_Storage->AddAddress (ident);
if (is_update)
LogPrint(eLogInfo, "Addressbook: added new host: ", name);
}
else
incomplete = f.eof ();
@@ -399,12 +403,24 @@ namespace client
{
getline(f, s);
if (!s.length()) continue; // skip empty line
m_Subscriptions.push_back (new AddressBookSubscription (*this, s));
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");
@@ -414,7 +430,7 @@ namespace client
{
std::map<std::string, i2p::data::IdentHash> localAddresses;
m_Storage->LoadLocal (localAddresses);
for (auto it: localAddresses)
for (const auto& it: localAddresses)
{
auto dot = it.first.find ('.');
if (dot != std::string::npos)
@@ -458,7 +474,7 @@ namespace client
int nextUpdateTimeout = CONTINIOUS_SUBSCRIPTION_RETRY_TIMEOUT;
if (success)
{
if (m_DefaultSubscription) m_DefaultSubscription.reset (nullptr);
if (m_DefaultSubscription) m_DefaultSubscription = nullptr;
if (m_IsLoaded)
nextUpdateTimeout = CONTINIOUS_SUBSCRIPTION_UPDATE_TIMEOUT;
else
@@ -509,19 +525,22 @@ 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.reset (new AddressBookSubscription (*this, DEFAULT_SUBSCRIPTION_ADDRESS));
m_DefaultSubscription = std::make_shared<AddressBookSubscription>(*this, defaultSubURL);
m_IsDownloading = true;
m_DefaultSubscription->CheckSubscription ();
std::thread load_hosts(std::bind (&AddressBookSubscription::CheckUpdates, m_DefaultSubscription));
load_hosts.detach(); // TODO: use join
}
else if (!m_Subscriptions.empty ())
{
// pick random subscription
auto ind = rand () % m_Subscriptions.size();
m_IsDownloading = true;
m_Subscriptions[ind]->CheckSubscription ();
std::thread load_hosts(std::bind (&AddressBookSubscription::CheckUpdates, m_Subscriptions[ind]));
load_hosts.detach(); // TODO: use join
}
}
else
@@ -542,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);
}
}
@@ -554,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);
}
}
@@ -567,7 +585,7 @@ namespace client
ident = FindAddress (address.substr (dot + 1));
if (!ident)
{
LogPrint (eLogError, "AddressBook: Can't find domain for ", address);
LogPrint (eLogError, "Addressbook: Can't find domain for ", address);
return;
}
@@ -583,7 +601,7 @@ namespace client
std::unique_lock<std::mutex> l(m_LookupsMutex);
m_Lookups[nonce] = address;
}
LogPrint (eLogDebug, "AddressBook: Lookup of ", address, " to ", ident->ToBase32 (), " nonce=", nonce);
LogPrint (eLogDebug, "Addressbook: Lookup of ", address, " to ", ident->ToBase32 (), " nonce=", nonce);
size_t len = address.length () + 9;
uint8_t * buf = new uint8_t[len];
memset (buf, 0, 4);
@@ -600,11 +618,11 @@ namespace client
{
if (len < 44)
{
LogPrint (eLogError, "AddressBook: Lookup response is too short ", len);
LogPrint (eLogError, "Addressbook: Lookup response is too short ", len);
return;
}
uint32_t nonce = bufbe32toh (buf + 4);
LogPrint (eLogDebug, "AddressBook: Lookup response received from ", from.GetIdentHash ().ToBase32 (), " nonce=", nonce);
LogPrint (eLogDebug, "Addressbook: Lookup response received from ", from.GetIdentHash ().ToBase32 (), " nonce=", nonce);
std::string address;
{
std::unique_lock<std::mutex> l(m_LookupsMutex);
@@ -618,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");
}
}
@@ -627,168 +649,166 @@ namespace client
{
}
void AddressBookSubscription::CheckSubscription ()
void AddressBookSubscription::CheckUpdates ()
{
std::thread load_hosts(&AddressBookSubscription::Request, this);
load_hosts.detach(); // TODO: use join
bool result = MakeRequest ();
m_Book.DownloadComplete (result, m_Ident, m_Etag, m_LastModified);
}
void AddressBookSubscription::Request ()
bool AddressBookSubscription::MakeRequest ()
{
i2p::http::URL url;
// must be run in separate thread
LogPrint (eLogInfo, "Addressbook: Downloading hosts database from ", m_Link, " ETag: ", m_Etag, " Last-Modified: ", m_LastModified);
bool success = false;
i2p::util::http::url u (m_Link);
i2p::data::IdentHash ident;
if (m_Book.GetIdentHash (u.host_, ident))
{
if (!m_Etag.length ())
{
// load ETag
m_Book.GetEtag (ident, m_Etag, m_LastModified);
LogPrint (eLogInfo, "Addressbook: set ", m_Link, " ETag: ", m_Etag, " Last-Modified: ", m_LastModified);
}
std::condition_variable newDataReceived;
std::mutex newDataReceivedMutex;
auto leaseSet = i2p::client::context.GetSharedLocalDestination ()->FindLeaseSet (ident);
if (!leaseSet)
{
std::unique_lock<std::mutex> l(newDataReceivedMutex);
i2p::client::context.GetSharedLocalDestination ()->RequestDestination (ident,
[&newDataReceived, &leaseSet](std::shared_ptr<i2p::data::LeaseSet> ls)
{
leaseSet = ls;
newDataReceived.notify_all ();
});
if (newDataReceived.wait_for (l, std::chrono::seconds (SUBSCRIPTION_REQUEST_TIMEOUT)) == std::cv_status::timeout)
{
LogPrint (eLogError, "Addressbook: Subscription LeaseSet request timeout expired");
i2p::client::context.GetSharedLocalDestination ()->CancelDestinationRequest (ident);
}
}
if (leaseSet)
{
std::stringstream request, response;
// standard header
request << "GET " << u.path_ << " HTTP/1.1\r\n"
<< "Host: " << u.host_ << "\r\n"
<< "Accept: */*\r\n"
<< "User-Agent: Wget/1.11.4\r\n"
//<< "Accept-Encoding: gzip\r\n"
<< "X-Accept-Encoding: x-i2p-gzip;q=1.0, identity;q=0.5, deflate;q=0, gzip;q=0, *;q=0\r\n"
<< "Connection: close\r\n";
if (m_Etag.length () > 0) // etag
request << i2p::util::http::IF_NONE_MATCH << ": " << m_Etag << "\r\n";
if (m_LastModified.length () > 0) // if-modfief-since
request << i2p::util::http::IF_MODIFIED_SINCE << ": " << m_LastModified << "\r\n";
request << "\r\n"; // end of header
auto stream = i2p::client::context.GetSharedLocalDestination ()->CreateStream (leaseSet, u.port_);
stream->Send ((uint8_t *)request.str ().c_str (), request.str ().length ());
uint8_t buf[4096];
bool end = false;
while (!end)
{
stream->AsyncReceive (boost::asio::buffer (buf, 4096),
[&](const boost::system::error_code& ecode, std::size_t bytes_transferred)
{
if (bytes_transferred)
response.write ((char *)buf, bytes_transferred);
if (ecode == boost::asio::error::timed_out || !stream->IsOpen ())
end = true;
newDataReceived.notify_all ();
},
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");
}
// process remaining buffer
while (size_t len = stream->ReadSome (buf, 4096))
response.write ((char *)buf, len);
// parse response
std::string version;
response >> version; // HTTP version
int status = 0;
response >> status; // status
if (status == 200) // OK
{
bool isChunked = false, isGzip = false;
m_Etag = ""; m_LastModified = "";
std::string header, statusMessage;
std::getline (response, statusMessage);
// read until new line meaning end of header
while (!response.eof () && header != "\r")
{
std::getline (response, header);
if (response.fail ()) break;
auto colon = header.find (':');
if (colon != std::string::npos)
{
std::string field = header.substr (0, colon);
boost::to_lower (field); // field are not case-sensitive
colon++;
header.resize (header.length () - 1); // delete \r
if (field == i2p::util::http::ETAG)
m_Etag = header.substr (colon + 1);
else if (field == i2p::util::http::LAST_MODIFIED)
m_LastModified = header.substr (colon + 1);
else if (field == i2p::util::http::TRANSFER_ENCODING)
isChunked = !header.compare (colon + 1, std::string::npos, "chunked");
else if (field == i2p::util::http::CONTENT_ENCODING)
isGzip = !header.compare (colon + 1, std::string::npos, "gzip") ||
!header.compare (colon + 1, std::string::npos, "x-i2p-gzip");
}
}
LogPrint (eLogInfo, "Addressbook: received ", m_Link, " ETag: ", m_Etag, " Last-Modified: ", m_LastModified);
if (!response.eof () && !response.fail ())
{
if (!isChunked)
success = ProcessResponse (response, isGzip);
else
{
// merge chunks
std::stringstream merged;
i2p::util::http::MergeChunkedResponse (response, merged);
success = ProcessResponse (merged, isGzip);
}
}
}
else if (status == 304)
{
success = true;
LogPrint (eLogInfo, "Addressbook: no updates from ", m_Link);
}
else
LogPrint (eLogWarning, "Adressbook: HTTP response ", status);
}
else
LogPrint (eLogError, "Addressbook: address ", u.host_, " not found");
LogPrint (eLogInfo, "Addressbook: Downloading hosts database from ", m_Link);
if (!url.parse(m_Link)) {
LogPrint(eLogError, "Addressbook: failed to parse url: ", m_Link);
return false;
}
else
LogPrint (eLogError, "Addressbook: Can't resolve ", u.host_);
if (!success)
LogPrint (eLogError, "Addressbook: download hosts.txt from ", m_Link, " failed");
m_Book.DownloadComplete (success, ident, m_Etag, m_LastModified);
}
bool AddressBookSubscription::ProcessResponse (std::stringstream& s, bool isGzip)
{
if (isGzip)
if (!m_Book.GetIdentHash (url.host, m_Ident)) {
LogPrint (eLogError, "Addressbook: Can't resolve ", url.host);
return false;
}
/* this code block still needs some love */
std::condition_variable newDataReceived;
std::mutex newDataReceivedMutex;
auto leaseSet = i2p::client::context.GetSharedLocalDestination ()->FindLeaseSet (m_Ident);
if (!leaseSet)
{
std::stringstream uncompressed;
i2p::data::GzipInflator inflator;
inflator.Inflate (s, uncompressed);
if (!uncompressed.fail ())
return m_Book.LoadHostsFromStream (uncompressed);
else
std::unique_lock<std::mutex> l(newDataReceivedMutex);
i2p::client::context.GetSharedLocalDestination ()->RequestDestination (m_Ident,
[&newDataReceived, &leaseSet, &newDataReceivedMutex](std::shared_ptr<i2p::data::LeaseSet> ls)
{
leaseSet = ls;
std::unique_lock<std::mutex> l1(newDataReceivedMutex);
newDataReceived.notify_all ();
});
if (newDataReceived.wait_for (l, std::chrono::seconds (SUBSCRIPTION_REQUEST_TIMEOUT)) == std::cv_status::timeout)
{
LogPrint (eLogError, "Addressbook: Subscription LeaseSet request timeout expired");
i2p::client::context.GetSharedLocalDestination ()->CancelDestinationRequest (m_Ident, false); // don't notify, because we know it already
return false;
}
else
return m_Book.LoadHostsFromStream (s);
}
}
if (!leaseSet) {
/* still no leaseset found */
LogPrint (eLogError, "Addressbook: LeaseSet for address ", url.host, " not found");
return false;
}
if (m_Etag.empty() && m_LastModified.empty()) {
m_Book.GetEtag (m_Ident, m_Etag, m_LastModified);
LogPrint (eLogDebug, "Addressbook: loaded for ", url.host, ": ETag: ", m_Etag, ", Last-Modified: ", m_LastModified);
}
/* save url parts for later use */
std::string dest_host = url.host;
int dest_port = url.port ? url.port : 80;
/* create http request & send it */
i2p::http::HTTPReq req;
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.AddHeader("If-None-Match", m_Etag);
if (!m_LastModified.empty())
req.AddHeader("If-Modified-Since", m_LastModified);
/* convert url to relative */
url.schema = "";
url.host = "";
req.uri = url.to_string();
auto stream = i2p::client::context.GetSharedLocalDestination ()->CreateStream (leaseSet, dest_port);
std::string request = req.to_string();
stream->Send ((const uint8_t *) request.data(), request.length());
/* read response */
std::string response;
uint8_t recv_buf[4096];
bool end = false;
int numAttempts = 5;
while (!end)
{
stream->AsyncReceive (boost::asio::buffer (recv_buf, 4096),
[&](const boost::system::error_code& ecode, std::size_t bytes_transferred)
{
if (bytes_transferred)
response.append ((char *)recv_buf, bytes_transferred);
if (ecode == boost::asio::error::timed_out || !stream->IsOpen ())
end = true;
newDataReceived.notify_all ();
},
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)))
response.append ((char *)recv_buf, len);
/* parse response */
i2p::http::HTTPRes res;
int res_head_len = res.parse(response);
if (res_head_len < 0)
{
LogPrint(eLogError, "Addressbook: can't parse http response from ", dest_host);
return false;
}
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)
{
LogPrint (eLogInfo, "Addressbook: no updates from ", dest_host, ", code 304");
return false;
}
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())
{
LogPrint(eLogError, "Addressbook: empty response from ", dest_host, ", expected ", len, " bytes");
return false;
}
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;
it = res.headers.find("If-Modified-Since");
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())
{
std::stringstream out;
i2p::data::GzipInflator inflator;
inflator.Inflate ((const uint8_t *) response.data(), response.length(), out);
if (out.fail())
{
LogPrint(eLogError, "Addressbook: can't gunzip http response");
return false;
}
response = out.str();
}
std::stringstream ss(response);
LogPrint (eLogInfo, "Addressbook: got update from ", dest_host);
m_Book.LoadHostsFromStream (ss, true);
return true;
}
AddressResolver::AddressResolver (std::shared_ptr<ClientDestination> destination):
@@ -819,7 +839,7 @@ namespace client
{
if (len < 9 || len < buf[8] + 9U)
{
LogPrint (eLogError, "AddressBook: Address request is too short ", len);
LogPrint (eLogError, "Addressbook: Address request is too short ", len);
return;
}
// read requested address
@@ -827,7 +847,7 @@ namespace client
char address[255];
memcpy (address, buf + 9, l);
address[l] = 0;
LogPrint (eLogDebug, "AddressBook: Address request ", address);
LogPrint (eLogDebug, "Addressbook: Address request ", address);
// send response
uint8_t response[44];
memset (response, 0, 4); // reserved
@@ -838,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,7 +18,6 @@ namespace i2p
{
namespace client
{
const char DEFAULT_SUBSCRIPTION_ADDRESS[] = "http://joajgazyztfssty4w2on5oaqksz6tqoxbduy553y34mf4byv6gpq.b32.i2p/export/alive-hosts.txt";
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)
@@ -66,7 +65,7 @@ namespace client
void InsertAddress (const std::string& address, const std::string& base64); // for jump service
void InsertAddress (std::shared_ptr<const i2p::data::IdentityEx> address);
bool LoadHostsFromStream (std::istream& f);
bool LoadHostsFromStream (std::istream& f, bool is_update);
void DownloadComplete (bool success, const i2p::data::IdentHash& subscription, const std::string& etag, const std::string& lastModified);
//This method returns the ".b32.i2p" address
std::string ToAddress(const i2p::data::IdentHash& ident) { return GetB32Address(ident); }
@@ -98,8 +97,8 @@ namespace client
std::map<uint32_t, std::string> m_Lookups; // nonce -> address
AddressBookStorage * m_Storage;
volatile bool m_IsLoaded, m_IsDownloading;
std::vector<AddressBookSubscription *> m_Subscriptions;
std::unique_ptr<AddressBookSubscription> m_DefaultSubscription; // in case if we don't know any addresses yet
std::vector<std::shared_ptr<AddressBookSubscription> > m_Subscriptions;
std::shared_ptr<AddressBookSubscription> m_DefaultSubscription; // in case if we don't know any addresses yet
boost::asio::deadline_timer * m_SubscriptionsUpdateTimer;
};
@@ -108,17 +107,17 @@ namespace client
public:
AddressBookSubscription (AddressBook& book, const std::string& link);
void CheckSubscription ();
void CheckUpdates ();
private:
void Request ();
bool ProcessResponse (std::stringstream& s, bool isGzip = false);
bool MakeRequest ();
private:
AddressBook& m_Book;
std::string m_Link, m_Etag, m_LastModified;
i2p::data::IdentHash m_Ident;
// m_Etag must be surrounded by ""
};

130
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
@@ -70,7 +70,7 @@ namespace client
if (eol)
{
*eol = 0;
if (eol != receiver->buffer && eol[-1] == '\r') eol[-1] = 0; // workaround for Transmission, it sends '\r\n' terminated address
receiver->data = (uint8_t *)eol + 1;
receiver->dataLen = receiver->bufferOffset - (eol - receiver->buffer + 1);
i2p::data::IdentHash ident;
@@ -202,9 +202,9 @@ namespace client
}
BOBCommandSession::BOBCommandSession (BOBCommandChannel& owner):
m_Owner (owner), m_Socket (m_Owner.GetService ()), m_ReceiveBufferOffset (0),
m_IsOpen (true), m_IsQuiet (false), m_InPort (0), m_OutPort (0),
m_CurrentDestination (nullptr)
m_Owner (owner), m_Socket (m_Owner.GetService ()),
m_ReceiveBufferOffset (0), m_IsOpen (true), m_IsQuiet (false), m_IsActive (false),
m_InPort (0), m_OutPort (0), m_CurrentDestination (nullptr)
{
}
@@ -354,6 +354,11 @@ namespace client
void BOBCommandSession::StartCommandHandler (const char * operand, size_t len)
{
LogPrint (eLogDebug, "BOB: start ", m_Nickname);
if (m_IsActive)
{
SendReplyError ("tunnel is active");
return;
}
if (!m_CurrentDestination)
{
m_CurrentDestination = new BOBDestination (i2p::client::context.CreateNewLocalDestination (m_Keys, true, &m_Options));
@@ -364,19 +369,27 @@ namespace client
if (m_OutPort && !m_Address.empty ())
m_CurrentDestination->CreateOutboundTunnel (m_Address, m_OutPort, m_IsQuiet);
m_CurrentDestination->Start ();
SendReplyOK ("tunnel starting");
SendReplyOK ("Tunnel starting");
m_IsActive = true;
}
void BOBCommandSession::StopCommandHandler (const char * operand, size_t len)
{
LogPrint (eLogDebug, "BOB: stop ", m_Nickname);
if (!m_IsActive)
{
SendReplyError ("tunnel is inactive");
return;
}
auto dest = m_Owner.FindDestination (m_Nickname);
if (dest)
{
dest->StopTunnels ();
SendReplyOK ("tunnel stopping");
SendReplyOK ("Tunnel stopping");
}
else
SendReplyError ("tunnel not found");
m_IsActive = false;
}
void BOBCommandSession::SetNickCommandHandler (const char * operand, size_t len)
@@ -384,7 +397,7 @@ namespace client
LogPrint (eLogDebug, "BOB: setnick ", operand);
m_Nickname = operand;
std::string msg ("Nickname set to ");
msg += operand;
msg += m_Nickname;
SendReplyOK (msg.c_str ());
}
@@ -396,12 +409,15 @@ namespace client
{
m_Keys = m_CurrentDestination->GetKeys ();
m_Nickname = operand;
std::string msg ("Nickname set to ");
msg += operand;
SendReplyOK (msg.c_str ());
}
if (m_Nickname == operand)
{
std::string msg ("Nickname set to ");
msg += m_Nickname;
SendReplyOK (msg.c_str ());
}
else
SendReplyError ("tunnel not found");
SendReplyError ("no nickname has been set");
}
void BOBCommandSession::NewkeysCommandHandler (const char * operand, size_t len)
@@ -421,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)
{
@@ -440,8 +459,11 @@ namespace client
void BOBCommandSession::OutportCommandHandler (const char * operand, size_t len)
{
LogPrint (eLogDebug, "BOB: outport ", operand);
m_OutPort = boost::lexical_cast<int>(operand);
SendReplyOK ("outbound port set");
m_OutPort = std::stoi(operand);
if (m_OutPort >= 0)
SendReplyOK ("outbound port set");
else
SendReplyError ("port out of range");
}
void BOBCommandSession::InhostCommandHandler (const char * operand, size_t len)
@@ -454,27 +476,40 @@ namespace client
void BOBCommandSession::InportCommandHandler (const char * operand, size_t len)
{
LogPrint (eLogDebug, "BOB: inport ", operand);
m_InPort = boost::lexical_cast<int>(operand);
SendReplyOK ("inbound port set");
m_InPort = std::stoi(operand);
if (m_InPort >= 0)
SendReplyOK ("inbound port set");
else
SendReplyError ("port out of range");
}
void BOBCommandSession::QuietCommandHandler (const char * operand, size_t len)
{
LogPrint (eLogDebug, "BOB: quiet");
m_IsQuiet = true;
SendReplyOK ("quiet");
if (m_Nickname.length () > 0)
{
if (!m_IsActive)
{
m_IsQuiet = true;
SendReplyOK ("Quiet set");
}
else
SendReplyError ("tunnel is active");
}
else
SendReplyError ("no nickname has been set");
}
void BOBCommandSession::LookupCommandHandler (const char * operand, size_t len)
{
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 ());
@@ -497,14 +532,15 @@ namespace client
{
LogPrint (eLogDebug, "BOB: clear");
m_Owner.DeleteDestination (m_Nickname);
m_Nickname = "";
SendReplyOK ("cleared");
}
void BOBCommandSession::ListCommandHandler (const char * operand, size_t len)
{
LogPrint (eLogDebug, "BOB: list");
auto& destinations = m_Owner.GetDestinations ();
for (auto it: destinations)
const auto& destinations = m_Owner.GetDestinations ();
for (const auto& it: destinations)
SendData (it.first.c_str ());
SendReplyOK ("Listing done");
}
@@ -515,14 +551,51 @@ namespace client
const char * value = strchr (operand, '=');
if (value)
{
std::string msg ("option ");
*(const_cast<char *>(value)) = 0;
m_Options[operand] = value + 1;
msg += operand;
*(const_cast<char *>(value)) = '=';
SendReplyOK ("option");
msg += " set to ";
msg += value;
SendReplyOK (msg.c_str ());
}
else
SendReplyError ("malformed");
}
void BOBCommandSession::StatusCommandHandler (const char * operand, size_t len)
{
LogPrint (eLogDebug, "BOB: status ", operand);
if (m_Nickname == operand)
{
std::stringstream s;
s << "DATA"; s << " NICKNAME: "; s << m_Nickname;
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: false RUNNING: false STOPPING: false";
s << " KEYS: true"; s << " QUIET: "; s << (m_IsQuiet ? "true":"false");
if (m_InPort)
{
s << " INPORT: " << m_InPort;
s << " INHOST: " << (m_Address.length () > 0 ? m_Address : "127.0.0.1");
}
if (m_OutPort)
{
s << " OUTPORT: " << m_OutPort;
s << " OUTHOST: " << (m_Address.length () > 0 ? m_Address : "127.0.0.1");
}
SendReplyOK (s.str().c_str());
}
else
SendReplyError ("no nickname has been set");
}
BOBCommandChannel::BOBCommandChannel (const std::string& address, int port):
m_IsRunning (false), m_Thread (nullptr),
@@ -548,12 +621,13 @@ namespace client
m_CommandHandlers[BOB_COMMAND_CLEAR] = &BOBCommandSession::ClearCommandHandler;
m_CommandHandlers[BOB_COMMAND_LIST] = &BOBCommandSession::ListCommandHandler;
m_CommandHandlers[BOB_COMMAND_OPTION] = &BOBCommandSession::OptionCommandHandler;
m_CommandHandlers[BOB_COMMAND_STATUS] = &BOBCommandSession::StatusCommandHandler;
}
BOBCommandChannel::~BOBCommandChannel ()
{
Stop ();
for (auto it: m_Destinations)
for (const auto& it: m_Destinations)
delete it.second;
}
@@ -567,7 +641,7 @@ namespace client
void BOBCommandChannel::Stop ()
{
m_IsRunning = false;
for (auto it: m_Destinations)
for (auto& it: m_Destinations)
it.second->Stop ();
m_Acceptor.cancel ();
m_Service.stop ();

4
BOB.h
View File

@@ -36,6 +36,7 @@ namespace client
const char BOB_COMMAND_CLEAR[] = "clear";
const char BOB_COMMAND_LIST[] = "list";
const char BOB_COMMAND_OPTION[] = "option";
const char BOB_COMMAND_STATUS[] = "status";
const char BOB_VERSION[] = "BOB 00.00.10\nOK\n";
const char BOB_REPLY_OK[] = "OK %s\n";
@@ -168,6 +169,7 @@ namespace client
void ClearCommandHandler (const char * operand, size_t len);
void ListCommandHandler (const char * operand, size_t len);
void OptionCommandHandler (const char * operand, size_t len);
void StatusCommandHandler (const char * operand, size_t len);
private:
@@ -186,7 +188,7 @@ namespace client
boost::asio::ip::tcp::socket m_Socket;
char m_ReceiveBuffer[BOB_COMMAND_BUFFER_SIZE + 1], m_SendBuffer[BOB_COMMAND_BUFFER_SIZE + 1];
size_t m_ReceiveBufferOffset;
bool m_IsOpen, m_IsQuiet;
bool m_IsOpen, m_IsQuiet, m_IsActive;
std::string m_Nickname, m_Address;
int m_InPort, m_OutPort;
i2p::data::PrivateKeys m_Keys;

104
Base.cpp
View File

@@ -1,5 +1,6 @@
#include <stdlib.h>
#include "Log.h"
#include <string.h>
#include "Base.h"
namespace i2p
@@ -284,107 +285,6 @@ namespace data
}
return ret;
}
GzipInflator::GzipInflator (): m_IsDirty (false)
{
memset (&m_Inflator, 0, sizeof (m_Inflator));
inflateInit2 (&m_Inflator, MAX_WBITS + 16); // gzip
}
GzipInflator::~GzipInflator ()
{
inflateEnd (&m_Inflator);
}
size_t GzipInflator::Inflate (const uint8_t * in, size_t inLen, uint8_t * out, size_t outLen)
{
if (m_IsDirty) inflateReset (&m_Inflator);
m_IsDirty = true;
m_Inflator.next_in = const_cast<uint8_t *>(in);
m_Inflator.avail_in = inLen;
m_Inflator.next_out = out;
m_Inflator.avail_out = outLen;
int err;
if ((err = inflate (&m_Inflator, Z_NO_FLUSH)) == Z_STREAM_END)
return outLen - m_Inflator.avail_out;
else
{
LogPrint (eLogError, "Decompression error ", err);
return 0;
}
}
bool GzipInflator::Inflate (const uint8_t * in, size_t inLen, std::ostream& s)
{
m_IsDirty = true;
uint8_t * out = new uint8_t[GZIP_CHUNK_SIZE];
m_Inflator.next_in = const_cast<uint8_t *>(in);
m_Inflator.avail_in = inLen;
int ret;
do
{
m_Inflator.next_out = out;
m_Inflator.avail_out = GZIP_CHUNK_SIZE;
ret = inflate (&m_Inflator, Z_NO_FLUSH);
if (ret < 0)
{
LogPrint (eLogError, "Decompression error ", ret);
inflateEnd (&m_Inflator);
s.setstate(std::ios_base::failbit);
break;
}
else
s.write ((char *)out, GZIP_CHUNK_SIZE - m_Inflator.avail_out);
}
while (!m_Inflator.avail_out); // more data to read
delete[] out;
return ret == Z_STREAM_END || ret < 0;
}
void GzipInflator::Inflate (std::istream& in, std::ostream& out)
{
uint8_t * buf = new uint8_t[GZIP_CHUNK_SIZE];
while (!in.eof ())
{
in.read ((char *)buf, GZIP_CHUNK_SIZE);
Inflate (buf, in.gcount (), out);
}
delete[] buf;
}
GzipDeflator::GzipDeflator (): m_IsDirty (false)
{
memset (&m_Deflator, 0, sizeof (m_Deflator));
deflateInit2 (&m_Deflator, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 15 + 16, 8, Z_DEFAULT_STRATEGY); // 15 + 16 sets gzip
}
GzipDeflator::~GzipDeflator ()
{
deflateEnd (&m_Deflator);
}
void GzipDeflator::SetCompressionLevel (int level)
{
deflateParams (&m_Deflator, level, Z_DEFAULT_STRATEGY);
}
size_t GzipDeflator::Deflate (const uint8_t * in, size_t inLen, uint8_t * out, size_t outLen)
{
if (m_IsDirty) deflateReset (&m_Deflator);
m_IsDirty = true;
m_Deflator.next_in = const_cast<uint8_t *>(in);
m_Deflator.avail_in = inLen;
m_Deflator.next_out = out;
m_Deflator.avail_out = outLen;
int err;
if ((err = deflate (&m_Deflator, Z_FINISH)) == Z_STREAM_END)
return outLen - m_Deflator.avail_out;
else
{
LogPrint (eLogError, "Compression error ", err);
return 0;
}
}
}
}

117
Base.h
View File

@@ -2,15 +2,11 @@
#define BASE_H__
#include <inttypes.h>
#include <string.h>
#include <string>
#include <zlib.h>
#include <iostream>
namespace i2p
{
namespace data
{
namespace i2p {
namespace data {
size_t ByteStreamToBase64 (const uint8_t * InBuffer, size_t InCount, char * OutBuffer, size_t len);
size_t Base64ToByteStream (const char * InBuffer, size_t InCount, uint8_t * OutBuffer, size_t len );
const char * GetBase32SubstitutionTable ();
@@ -23,112 +19,7 @@ namespace data
Compute the size for a buffer to contain encoded base64 given that the size of the input is input_size bytes
*/
size_t Base64EncodingBufferSize(const size_t input_size);
template<int sz>
class Tag
{
public:
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;
Tag<sz>& operator= (const Tag<sz>& ) = default;
#ifndef _WIN32
Tag<sz>& operator= (Tag<sz>&& ) = default;
#endif
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 uint64_t * GetLL () const { return ll; };
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; };
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];
};
};
const size_t GZIP_CHUNK_SIZE = 16384;
class GzipInflator
{
public:
GzipInflator ();
~GzipInflator ();
size_t Inflate (const uint8_t * in, size_t inLen, uint8_t * out, size_t outLen);
bool Inflate (const uint8_t * in, size_t inLen, std::ostream& s);
// return true when finshed or error, s failbit will be set in case of error
void Inflate (std::istream& in, std::ostream& out);
private:
z_stream m_Inflator;
bool m_IsDirty;
};
class GzipDeflator
{
public:
GzipDeflator ();
~GzipDeflator ();
void SetCompressionLevel (int level);
size_t Deflate (const uint8_t * in, size_t inLen, uint8_t * out, size_t outLen);
private:
z_stream m_Deflator;
bool m_IsDirty;
};
}
}
} // data
} // i2p
#endif

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,14 +1,87 @@
# for this file format description,
# see https://github.com/olivierlacan/keep-a-changelog
## [2.8.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
- Basic Android support
- I2CP implementation
- 'doxygen' target
### Changed
- I2PControl refactoring & fixes (proper jsonrpc responses on errors)
- boost::regex no more needed
### Fixed
- initscripts: added openrc one, in sysv-ish make I2PD_PORT optional
- properly close NTCP sessions (memleak)
## [2.7.0] - 2016-05-18
### 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 ();
for (auto it: m_Destinations)
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"
@@ -26,84 +27,8 @@ namespace config {
options_description m_OptionsDesc;
variables_map m_Options;
/* list of renamed options */
std::map<std::string, std::string> remapped_options = {
{ "tunnelscfg", "tunconf" },
{ "v6", "ipv6" },
{ "httpaddress", "http.address" },
{ "httpport", "http.port" },
{ "httpproxyaddress", "httpproxy.address" },
{ "httpproxyport", "httpproxy.port" },
{ "socksproxyaddress", "socksproxy.address" },
{ "socksproxyport", "socksproxy.port" },
{ "samaddress", "sam.address" },
{ "samport", "sam.port" },
{ "bobaddress", "bob.address" },
{ "bobport", "bob.port" },
{ "i2pcontroladdress", "i2pcontrol.address" },
{ "i2pcontroladdress", "i2pcontrol.port" },
{ "proxykeys", "httpproxy.keys" },
};
/* list of options, that loose their argument and become simple switch */
std::set<std::string> boolean_options = {
"daemon", "floodfill", "notransit", "service", "ipv6"
};
/* this function is a solid piece of shit, remove it after 2.6.0 */
std::pair<std::string, std::string> old_syntax_parser(const std::string& s) {
std::string name = "";
std::string value = "";
std::size_t pos = 0;
/* shortcuts -- -h */
if (s.length() == 2 && s.at(0) == '-' && s.at(1) != '-')
return make_pair(s.substr(1), "");
/* old-style -- -log, /log, etc */
if (s.at(0) == '/' || (s.at(0) == '-' && s.at(1) != '-')) {
if ((pos = s.find_first_of("= ")) != std::string::npos) {
name = s.substr(1, pos - 1);
value = s.substr(pos + 1);
} else {
name = s.substr(1, pos);
value = "";
}
if (boolean_options.count(name) > 0 && value != "")
std::cerr << "args: don't give an argument to switch option: " << s << std::endl;
if (m_OptionsDesc.find_nothrow(name, false)) {
std::cerr << "args: option " << s << " style is DEPRECATED, use --" << name << " instead" << std::endl;
return std::make_pair(name, value);
}
if (remapped_options.count(name) > 0) {
name = remapped_options[name];
std::cerr << "args: option " << s << " is DEPRECATED, use --" << name << " instead" << std::endl;
return std::make_pair(name, value);
} /* else */
}
/* long options -- --help */
if (s.substr(0, 2) == "--") {
if ((pos = s.find_first_of("= ")) != std::string::npos) {
name = s.substr(2, pos - 2);
value = s.substr(pos + 1);
} else {
name = s.substr(2, pos);
value = "";
}
if (boolean_options.count(name) > 0 && value != "") {
std::cerr << "args: don't give an argument to switch option: " << s << std::endl;
value = "";
}
if (m_OptionsDesc.find_nothrow(name, false))
return std::make_pair(name, value);
if (remapped_options.count(name) > 0) {
name = remapped_options[name];
std::cerr << "args: option " << s << " is DEPRECATED, use --" << name << " instead" << std::endl;
return std::make_pair(name, value);
} /* else */
}
std::cerr << "args: unknown option -- " << s << std::endl;
return std::make_pair("", "");
}
void Init() {
options_description general("General options");
general.add_options()
("help", "Show this message")
@@ -116,23 +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")
("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")
#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)")
;
@@ -152,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");
@@ -160,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")
;
@@ -195,6 +144,16 @@ namespace config {
("i2pcontrol.key", value<std::string>()->default_value("i2pcontrol.key.pem"), "I2PCP connection cerificate key")
;
bool upnp_default = false;
#if (defined(USE_UPNP) && (defined(WIN32_APP) || defined(ANDROID)))
upnp_default = true; // enable UPNP for windows GUI and android by default
#endif
options_description upnp("UPnP options");
upnp.add_options()
("upnp.enabled", value<bool>()->default_value(upnp_default), "Enable or disable UPnP: automatic port forwarding")
("upnp.name", value<std::string>()->default_value("I2Pd"), "Name i2pd appears in UPnP forwardings list")
;
options_description precomputation("Precomputation options");
precomputation.add_options()
("precomputation.elgamal",
@@ -205,7 +164,52 @@ namespace config {
#endif
"Enable or disable elgamal precomputation table")
;
options_description reseed("Reseed options");
reseed.add_options()
("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.family", value<std::string>()->default_value(""), "Router Familiy to trust for first hops")
("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)
@@ -216,7 +220,12 @@ namespace config {
.add(bob)
.add(i2cp)
.add(i2pcontrol)
.add(precomputation)
.add(upnp)
.add(precomputation)
.add(reseed)
.add(addressbook)
.add(trust)
.add(websocket)
;
}
@@ -225,7 +234,7 @@ namespace config {
auto style = boost::program_options::command_line_style::unix_style
| boost::program_options::command_line_style::allow_long_disguise;
style &= ~ boost::program_options::command_line_style::allow_guessing;
store(parse_command_line(argc, argv, m_OptionsDesc, style, old_syntax_parser), m_Options);
store(parse_command_line(argc, argv, m_OptionsDesc, style), m_Options);
} catch (boost::program_options::error& e) {
std::cerr << "args: " << e.what() << std::endl;
exit(EXIT_FAILURE);

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"
);

103
Crypto.h
View File

@@ -7,9 +7,14 @@
#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"
#include "Tag.h"
namespace i2p
{
@@ -31,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);
@@ -72,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"
@@ -108,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
@@ -196,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);
@@ -207,7 +210,7 @@ namespace crypto
private:
ChipherBlock m_LastBlock;
AESAlignedBuffer<16> m_LastBlock;
ECBEncryption m_ECBEncryption;
};
@@ -216,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);
@@ -227,7 +230,7 @@ namespace crypto
private:
ChipherBlock m_IV;
AESAlignedBuffer<16> m_IV;
ECBDecryption m_ECBDecryption;
};
@@ -280,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

@@ -22,10 +22,11 @@
#include "I2PControl.h"
#include "ClientContext.h"
#include "Crypto.h"
#ifdef USE_UPNP
#include "UPnP.h"
#endif
#include "util.h"
#include "Event.h"
#include "Websocket.h"
namespace i2p
{
@@ -39,10 +40,10 @@ namespace i2p
std::unique_ptr<i2p::http::HTTPServer> httpServer;
std::unique_ptr<i2p::client::I2PControlService> m_I2PControlService;
#ifdef USE_UPNP
i2p::transport::UPnP m_UPnP;
#endif
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()) {}
@@ -112,35 +113,35 @@ namespace i2p
} else {
// use stdout -- default
}
i2p::log::Logger().Ready();
LogPrint(eLogInfo, "i2pd v", VERSION, " starting");
LogPrint(eLogInfo, "i2pd v", VERSION, " starting");
LogPrint(eLogDebug, "FS: main config file: ", config);
LogPrint(eLogDebug, "FS: data directory: ", datadir);
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 ();
uint16_t port; i2p::config::GetOption("port", port);
if (!i2p::config::IsDefault("port"))
{
LogPrint(eLogInfo, "Daemon: accepting incoming connections at port ", port);
i2p::context.UpdatePort (port);
}
std::string host; i2p::config::GetOption("host", host);
if (!i2p::config::IsDefault("host"))
{
LogPrint(eLogInfo, "Daemon: setting address for incoming connections to ", host);
i2p::context.UpdateAddress (boost::asio::ip::address::from_string (host));
}
bool ipv6; i2p::config::GetOption("ipv6", ipv6);
bool ipv4; i2p::config::GetOption("ipv4", ipv4);
bool transit; i2p::config::GetOption("notransit", transit);
#ifdef MESHNET
// manual override for meshnet
ipv4 = false;
ipv6 = true;
#endif
uint16_t port; i2p::config::GetOption("port", port);
if (!i2p::config::IsDefault("port"))
{
LogPrint(eLogInfo, "Daemon: accepting incoming connections at port ", port);
i2p::context.UpdatePort (port);
}
i2p::context.SetSupportsV6 (ipv6);
i2p::context.SetSupportsV4 (ipv4);
bool transit; i2p::config::GetOption("notransit", transit);
i2p::context.SetAcceptsTunnels (!transit);
uint16_t transitTunnels; i2p::config::GetOption("limits.transittunnels", transitTunnels);
SetMaxNumTransitTunnels (transitTunnels);
@@ -192,31 +193,94 @@ namespace i2p
i2p::context.SetFamily (family);
if (family.length () > 0)
LogPrint(eLogInfo, "Daemon: family set to ", family);
return true;
bool trust; i2p::config::GetOption("trust.enabled", trust);
if (trust)
{
LogPrint(eLogInfo, "Daemon: explicit trust enabled");
std::string fam; i2p::config::GetOption("trust.family", fam);
std::string routers; i2p::config::GetOption("trust.routers", routers);
bool restricted = false;
if (fam.length() > 0)
{
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)
{
LogPrint(eLogInfo, "Daemon: using hidden mode");
i2p::data::netdb.SetHidden(true);
}
return true;
}
bool Daemon_Singleton::start()
{
i2p::log::Logger().Start();
LogPrint(eLogInfo, "Daemon: starting NetDB");
i2p::data::netdb.Start();
bool upnp; i2p::config::GetOption("upnp.enabled", upnp);
if (upnp) {
d.UPnP = std::unique_ptr<i2p::transport::UPnP>(new i2p::transport::UPnP);
d.UPnP->Start ();
}
bool ntcp; i2p::config::GetOption("ntcp", ntcp);
bool ssu; i2p::config::GetOption("ssu", ssu);
LogPrint(eLogInfo, "Daemon: starting Transports");
if(!ssu) LogPrint(eLogInfo, "Daemon: ssu disabled");
if(!ntcp) LogPrint(eLogInfo, "Daemon: ntcp disabled");
i2p::transport::transports.Start(ntcp, ssu);
if (i2p::transport::transports.IsBoundNTCP() || i2p::transport::transports.IsBoundSSU()) {
LogPrint(eLogInfo, "Daemon: Transports started");
} else {
LogPrint(eLogError, "Daemon: failed to start Transports");
/** shut down netdb right away */
i2p::transport::transports.Stop();
i2p::data::netdb.Stop();
return false;
}
bool http; i2p::config::GetOption("http.enabled", http);
if (http) {
std::string httpAddr; i2p::config::GetOption("http.address", httpAddr);
uint16_t httpPort; i2p::config::GetOption("http.port", httpPort);
uint16_t httpPort; i2p::config::GetOption("http.port", httpPort);
LogPrint(eLogInfo, "Daemon: starting HTTP Server at ", httpAddr, ":", httpPort);
d.httpServer = std::unique_ptr<i2p::http::HTTPServer>(new i2p::http::HTTPServer(httpAddr, httpPort));
d.httpServer->Start();
}
LogPrint(eLogInfo, "Daemon: starting NetDB");
i2p::data::netdb.Start();
#ifdef USE_UPNP
LogPrint(eLogInfo, "Daemon: starting UPnP");
d.m_UPnP.Start ();
#endif
LogPrint(eLogInfo, "Daemon: starting Transports");
i2p::transport::transports.Start();
LogPrint(eLogInfo, "Daemon: starting Tunnels");
i2p::tunnel::tunnels.Start();
@@ -232,20 +296,37 @@ 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();
LogPrint(eLogInfo, "Daemon: stopping Tunnels");
i2p::tunnel::tunnels.Stop();
#ifdef USE_UPNP
LogPrint(eLogInfo, "Daemon: stopping UPnP");
d.m_UPnP.Stop ();
#endif
if (d.UPnP) {
d.UPnP->Stop ();
d.UPnP = nullptr;
}
LogPrint(eLogInfo, "Daemon: stopping Transports");
i2p::transport::transports.Stop();
LogPrint(eLogInfo, "Daemon: stopping NetDB");
@@ -260,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

@@ -45,6 +45,20 @@ namespace i2p
}
};
#elif defined(ANDROID)
#define Daemon i2p::util::DaemonAndroid::Instance()
// dummy, invoked from android/jni/DaemonAndroid.*
class DaemonAndroid: public i2p::util::Daemon_Singleton
{
public:
static DaemonAndroid& Instance()
{
static DaemonAndroid instance;
return instance;
}
};
#elif defined(_WIN32)
#define Daemon i2p::util::DaemonWin32::Instance()
class DaemonWin32 : public Daemon_Singleton
@@ -83,7 +97,7 @@ namespace i2p
public:
int gracefullShutdownInterval; // in seconds
int gracefulShutdownInterval; // in seconds
};
#endif

View File

@@ -8,30 +8,36 @@
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/resource.h>
#include "Config.h"
#include "FS.h"
#include "Log.h"
#include "RouterContext.h"
#include "ClientContext.h"
void handle_signal(int sig)
{
switch (sig)
{
case SIGHUP:
LogPrint(eLogInfo, "Daemon: Got SIGHUP, reopening log...");
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
@@ -74,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
@@ -106,7 +148,7 @@ namespace i2p
return false;
}
}
gracefullShutdownInterval = 0; // not specified
gracefulShutdownInterval = 0; // not specified
// Signal handler
struct sigaction sa;
@@ -114,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);
@@ -125,7 +168,7 @@ namespace i2p
{
i2p::fs::Remove(pidfile);
return Daemon_Singleton::stop();
return Daemon_Singleton::stop();
}
void DaemonLinux::run ()
@@ -133,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

@@ -1,113 +1,115 @@
#include <thread>
#include "Config.h"
#include "Daemon.h"
#include "util.h"
#include "Log.h"
#ifdef _WIN32
#include "Win32/Win32Service.h"
#ifdef WIN32_APP
#include "Win32/Win32App.h"
#endif
namespace i2p
{
namespace util
{
bool DaemonWin32::init(int argc, char* argv[])
{
setlocale(LC_CTYPE, "");
SetConsoleCP(1251);
SetConsoleOutputCP(1251);
if (!Daemon_Singleton::init(argc, argv))
return false;
std::string serviceControl; i2p::config::GetOption("svcctl", serviceControl);
if (serviceControl == "install")
{
LogPrint(eLogInfo, "WinSVC: installing ", SERVICE_NAME, " as service");
InstallService(
SERVICE_NAME, // Name of service
SERVICE_DISPLAY_NAME, // Name to display
SERVICE_START_TYPE, // Service start type
SERVICE_DEPENDENCIES, // Dependencies
SERVICE_ACCOUNT, // Service running account
SERVICE_PASSWORD // Password of the account
);
return false;
}
else if (serviceControl == "remove")
{
LogPrint(eLogInfo, "WinSVC: uninstalling ", SERVICE_NAME, " service");
UninstallService(SERVICE_NAME);
return false;
}
#include <thread>
#include <clocale>
#include "Config.h"
#include "Daemon.h"
#include "util.h"
#include "Log.h"
#ifdef _WIN32
#include "Win32/Win32Service.h"
#ifdef WIN32_APP
#include "Win32/Win32App.h"
#endif
namespace i2p
{
namespace util
{
bool DaemonWin32::init(int argc, char* argv[])
{
setlocale(LC_CTYPE, "");
SetConsoleCP(1251);
SetConsoleOutputCP(1251);
setlocale(LC_ALL, "Russian");
if (!Daemon_Singleton::init(argc, argv))
return false;
std::string serviceControl; i2p::config::GetOption("svcctl", serviceControl);
if (serviceControl == "install")
{
LogPrint(eLogInfo, "WinSVC: installing ", SERVICE_NAME, " as service");
InstallService(
SERVICE_NAME, // Name of service
SERVICE_DISPLAY_NAME, // Name to display
SERVICE_START_TYPE, // Service start type
SERVICE_DEPENDENCIES, // Dependencies
SERVICE_ACCOUNT, // Service running account
SERVICE_PASSWORD // Password of the account
);
return false;
}
else if (serviceControl == "remove")
{
LogPrint(eLogInfo, "WinSVC: uninstalling ", SERVICE_NAME, " service");
UninstallService(SERVICE_NAME);
return false;
}
if (isDaemon)
{
LogPrint(eLogDebug, "Daemon: running as service");
I2PService service(SERVICE_NAME);
if (!I2PService::Run(service))
{
LogPrint(eLogError, "Daemon: Service failed to run w/err 0x%08lx\n", GetLastError());
return false;
}
return false;
}
else
LogPrint(eLogDebug, "Daemon: running as user");
return true;
}
bool DaemonWin32::start()
{
setlocale(LC_CTYPE, "");
SetConsoleCP(1251);
SetConsoleOutputCP(1251);
setlocale(LC_ALL, "Russian");
#ifdef WIN32_APP
if (!i2p::win32::StartWin32App ()) return false;
// override log
i2p::config::SetOption("log", std::string ("file"));
#endif
bool ret = Daemon_Singleton::start();
if (ret && i2p::log::Logger().GetLogType() == eLogFile)
{
// TODO: find out where this garbage to console comes from
SetStdHandle(STD_OUTPUT_HANDLE, INVALID_HANDLE_VALUE);
SetStdHandle(STD_ERROR_HANDLE, INVALID_HANDLE_VALUE);
}
bool insomnia; i2p::config::GetOption("insomnia", insomnia);
if (insomnia)
SetThreadExecutionState(ES_CONTINUOUS | ES_SYSTEM_REQUIRED);
return ret;
}
bool DaemonWin32::stop()
{
#ifdef WIN32_APP
i2p::win32::StopWin32App ();
#endif
return Daemon_Singleton::stop();
}
void DaemonWin32::run ()
{
#ifdef WIN32_APP
i2p::win32::RunWin32App ();
#else
while (running)
{
std::this_thread::sleep_for (std::chrono::seconds(1));
}
#endif
}
}
}
#endif
{
LogPrint(eLogDebug, "Daemon: running as service");
I2PService service(SERVICE_NAME);
if (!I2PService::Run(service))
{
LogPrint(eLogError, "Daemon: Service failed to run w/err 0x%08lx\n", GetLastError());
return false;
}
return false;
}
else
LogPrint(eLogDebug, "Daemon: running as user");
return true;
}
bool DaemonWin32::start()
{
setlocale(LC_CTYPE, "");
SetConsoleCP(1251);
SetConsoleOutputCP(1251);
setlocale(LC_ALL, "Russian");
#ifdef WIN32_APP
if (!i2p::win32::StartWin32App ()) return false;
// override log
i2p::config::SetOption("log", std::string ("file"));
#endif
bool ret = Daemon_Singleton::start();
if (ret && i2p::log::Logger().GetLogType() == eLogFile)
{
// TODO: find out where this garbage to console comes from
SetStdHandle(STD_OUTPUT_HANDLE, INVALID_HANDLE_VALUE);
SetStdHandle(STD_ERROR_HANDLE, INVALID_HANDLE_VALUE);
}
bool insomnia; i2p::config::GetOption("insomnia", insomnia);
if (insomnia)
SetThreadExecutionState(ES_CONTINUOUS | ES_SYSTEM_REQUIRED);
return ret;
}
bool DaemonWin32::stop()
{
#ifdef WIN32_APP
i2p::win32::StopWin32App ();
#endif
return Daemon_Singleton::stop();
}
void DaemonWin32::run ()
{
#ifdef WIN32_APP
i2p::win32::RunWin32App ();
#else
while (running)
{
std::this_thread::sleep_for (std::chrono::seconds(1));
}
#endif
}
}
}
#endif

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);
}
}
@@ -378,7 +397,7 @@ namespace client
uint32_t msgID = bufbe32toh (msg->GetPayload () + DELIVERY_STATUS_MSGID_OFFSET);
if (msgID == m_PublishReplyToken)
{
LogPrint (eLogDebug, "Destination: Publishing LeaseSet confirmed");
LogPrint (eLogDebug, "Destination: Publishing LeaseSet confirmed for ", GetIdentHash().ToBase32());
m_ExcludedFloodfills.clear ();
m_PublishReplyToken = 0;
// schedule verification
@@ -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,47 +488,56 @@ namespace client
// "this" added due to bug in gcc 4.7-4.8
[s,this](std::shared_ptr<i2p::data::LeaseSet> leaseSet)
{
if (leaseSet)
if (leaseSet)
{
if (s->m_LeaseSet && *s->m_LeaseSet == *leaseSet)
{
// we got latest LeasetSet
LogPrint (eLogDebug, "Destination: published LeaseSet verified");
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");
LogPrint (eLogWarning, "Destination: couldn't find published LeaseSet for ", GetIdentHash().ToBase32());
// we have to publish again
s->Publish ();
s->Publish ();
});
}
}
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 ())
{
if (requestComplete) requestComplete (nullptr);
if (requestComplete)
m_Service.post ([requestComplete](void){requestComplete (nullptr);});
return false;
}
m_Service.post (std::bind (&LeaseSetDestination::RequestLeaseSet, shared_from_this (), dest, requestComplete));
return true;
}
void LeaseSetDestination::CancelDestinationRequest (const i2p::data::IdentHash& dest)
void LeaseSetDestination::CancelDestinationRequest (const i2p::data::IdentHash& dest, bool notify)
{
auto s = shared_from_this ();
m_Service.post ([dest, s](void)
m_Service.post ([dest, notify, s](void)
{
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 (requestComplete) requestComplete (nullptr);
if (notify && requestComplete) requestComplete->Complete (nullptr);
}
});
}
@@ -511,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
@@ -549,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];
@@ -607,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);
}
}
}
@@ -621,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));
@@ -630,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
@@ -638,13 +686,14 @@ namespace client
it = m_RemoteLeaseSets.erase (it);
}
else
it++;
++it;
}
}
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 ();
@@ -656,8 +705,6 @@ namespace client
ClientDestination::~ClientDestination ()
{
if (m_DatagramDestination)
delete m_DatagramDestination;
}
bool ClientDestination::Start ()
@@ -666,7 +713,7 @@ namespace client
{
m_StreamingDestination = std::make_shared<i2p::stream::StreamingDestination> (GetSharedFromThis ()); // TODO:
m_StreamingDestination->Start ();
for (auto it: m_StreamingDestinationsByPorts)
for (auto& it: m_StreamingDestinationsByPorts)
it.second->Start ();
return true;
}
@@ -678,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)
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);
@@ -788,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);
@@ -798,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;
}
@@ -850,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 ();
}
};
@@ -79,7 +95,7 @@ namespace client
bool IsReady () const { return m_LeaseSet && !m_LeaseSet->IsExpired () && m_Pool->GetOutboundTunnels ().size () > 0; };
std::shared_ptr<const i2p::data::LeaseSet> FindLeaseSet (const i2p::data::IdentHash& ident);
bool RequestDestination (const i2p::data::IdentHash& dest, RequestComplete requestComplete = nullptr);
void CancelDestinationRequest (const i2p::data::IdentHash& dest);
void CancelDestinationRequest (const i2p::data::IdentHash& dest, bool notify = true);
// implements GarlicDestination
std::shared_ptr<const i2p::data::LocalLeaseSet> GetLeaseSet ();
@@ -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

@@ -7,6 +7,7 @@
#include "I2NPProtocol.h"
#include "Tunnel.h"
#include "TunnelPool.h"
#include "Transports.h"
#include "Timestamp.h"
#include "Log.h"
#include "Garlic.h"
@@ -19,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);
@@ -27,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);
@@ -37,9 +38,6 @@ namespace garlic
GarlicRoutingSession::~GarlicRoutingSession ()
{
for (auto it: m_UnconfirmedTagsMsgs)
delete it.second;
m_UnconfirmedTagsMsgs.clear ();
}
std::shared_ptr<GarlicRoutingPath> GarlicRoutingSession::GetSharedRoutingPath ()
@@ -85,6 +83,7 @@ namespace garlic
if (msgID == m_LeaseSetUpdateMsgID)
{
m_LeaseSetUpdateStatus = eLeaseSetUpToDate;
m_LeaseSetUpdateMsgID = 0;
LogPrint (eLogInfo, "Garlic: LeaseSet update confirmed");
}
else
@@ -93,31 +92,44 @@ namespace garlic
void GarlicRoutingSession::TagsConfirmed (uint32_t msgID)
{
auto it = m_UnconfirmedTagsMsgs.find (msgID);
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
auto it = m_UnconfirmedTagsMsgs.find (msgID);
if (it != m_UnconfirmedTagsMsgs.end ())
{
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
UnconfirmedTags * tags = it->second;
auto& tags = it->second;
if (ts < tags->tagsCreationTime + OUTGOING_TAGS_EXPIRATION_TIMEOUT)
{
for (int i = 0; i < tags->numTags; i++)
m_SessionTags.push_back (tags->sessionTags[i]);
}
m_UnconfirmedTagsMsgs.erase (it);
delete tags;
}
}
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)
it = m_SessionTags.erase (it);
else
it++;
++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 ();
}
bool GarlicRoutingSession::CleanupUnconfirmedTags ()
{
bool ret = false;
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
// delete expired unconfirmed tags
for (auto it = m_UnconfirmedTagsMsgs.begin (); it != m_UnconfirmedTagsMsgs.end ();)
{
@@ -125,14 +137,14 @@ namespace garlic
{
if (m_Owner)
m_Owner->RemoveDeliveryStatusSession (it->first);
delete it->second;
it = m_UnconfirmedTagsMsgs.erase (it);
ret = true;
}
else
it++;
++it;
}
return !m_SessionTags.empty () || !m_UnconfirmedTagsMsgs.empty ();
}
return ret;
}
std::shared_ptr<I2NPMessage> GarlicRoutingSession::WrapSingleMessage (std::shared_ptr<const I2NPMessage> msg)
{
@@ -163,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");
@@ -175,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;
@@ -232,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;
@@ -243,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
@@ -257,7 +271,11 @@ namespace garlic
size += cloveSize;
(*numCloves)++;
if (newTags) // new tags created
m_UnconfirmedTagsMsgs[msgID] = newTags;
{
newTags->msgID = msgID;
m_UnconfirmedTagsMsgs.insert (std::make_pair(msgID, std::unique_ptr<UnconfirmedTags>(newTags)));
newTags = nullptr; // got acquired
}
m_Owner->DeliveryStatusSent (shared_from_this (), msgID);
}
else
@@ -266,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);
@@ -280,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;
}
@@ -294,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++;
@@ -373,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)
@@ -514,22 +540,34 @@ namespace garlic
buf += 32;
uint32_t gwTunnel = bufbe32toh (buf);
buf += 4;
std::shared_ptr<i2p::tunnel::OutboundTunnel> tunnel;
if (from && from->GetTunnelPool ())
tunnel = from->GetTunnelPool ()->GetNextOutboundTunnel ();
if (tunnel) // we have send it through an outbound tunnel
{
auto msg = CreateI2NPMessage (buf, GetI2NPMessageLength (buf), from);
tunnel->SendTunnelDataMsg (gwHash, gwTunnel, msg);
}
else
LogPrint (eLogWarning, "Garlic: No outbound tunnels available for garlic clove");
auto msg = CreateI2NPMessage (buf, GetI2NPMessageLength (buf), from);
if (from) // received through an inbound tunnel
{
std::shared_ptr<i2p::tunnel::OutboundTunnel> tunnel;
if (from->GetTunnelPool ())
tunnel = from->GetTunnelPool ()->GetNextOutboundTunnel ();
else
LogPrint (eLogError, "Garlic: Tunnel pool is not set for inbound tunnel");
if (tunnel) // we have send it through an outbound tunnel
tunnel->SendTunnelDataMsg (gwHash, gwTunnel, msg);
else
LogPrint (eLogWarning, "Garlic: No outbound tunnels available for garlic clove");
}
else // received directly
i2p::transport::transports.SendMessage (gwHash, i2p::CreateTunnelGatewayMsg (gwTunnel, msg)); // send directly
break;
}
case eGarlicDeliveryTypeRouter:
LogPrint (eLogWarning, "Garlic: type router not supported");
{
uint8_t * ident = buf;
buf += 32;
break;
if (!from) // received directly
i2p::transport::transports.SendMessage (ident,
CreateI2NPMessage (buf, GetI2NPMessageLength (buf)));
else
LogPrint (eLogWarning, "Garlic: type router for inbound tunnels not supported");
break;
}
default:
LogPrint (eLogWarning, "Garlic: unknown delivery type ", (int)deliveryType);
}
@@ -585,54 +623,76 @@ namespace garlic
it = m_Tags.erase (it);
}
else
it++;
++it;
}
if (numExpiredTags > 0)
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 ())
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 ()
{
std::unique_lock<std::mutex> l(m_SessionsMutex);
for (auto it: m_Sessions)
std::unique_lock<std::mutex> l(m_SessionsMutex);
for (auto& it: m_Sessions)
it.second->SetLeaseSetUpdated ();
}

View File

@@ -83,6 +83,7 @@ namespace garlic
{
UnconfirmedTags (int n): numTags (n), tagsCreationTime (0) { sessionTags = new SessionTag[numTags]; };
~UnconfirmedTags () { delete[] sessionTags; };
uint32_t msgID;
int numTags;
SessionTag * sessionTags;
uint32_t tagsCreationTime;
@@ -97,15 +98,22 @@ namespace garlic
std::shared_ptr<I2NPMessage> WrapSingleMessage (std::shared_ptr<const I2NPMessage> msg);
void MessageConfirmed (uint32_t msgID);
bool CleanupExpiredTags (); // returns true if something left
bool CleanupUnconfirmedTags (); // returns true if something has been deleted
void SetLeaseSetUpdated ()
{
if (m_LeaseSetUpdateStatus != eLeaseSetDoNotSend) m_LeaseSetUpdateStatus = eLeaseSetUpdated;
};
bool IsLeaseSetNonConfirmed () const { return m_LeaseSetUpdateStatus == eLeaseSetSubmitted; };
bool IsLeaseSetUpdated () const { return m_LeaseSetUpdateStatus == eLeaseSetUpdated; };
uint64_t GetLeaseSetSubmissionTime () const { return m_LeaseSetSubmissionTime; }
std::shared_ptr<GarlicRoutingPath> GetSharedRoutingPath ();
void SetSharedRoutingPath (std::shared_ptr<GarlicRoutingPath> path);
const GarlicDestination * GetOwner () const { return m_Owner; }
void SetOwner (GarlicDestination * owner) { m_Owner = owner; }
private:
size_t CreateAESBlock (uint8_t * buf, std::shared_ptr<const I2NPMessage> msg);
@@ -120,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::map<uint32_t, 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;
@@ -148,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 ();
@@ -187,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:

115
Gzip.cpp Normal file
View File

@@ -0,0 +1,115 @@
/*
* 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 <inttypes.h>
#include <string.h> /* memset */
#include <iostream>
#include "Log.h"
#include "Gzip.h"
namespace i2p
{
namespace data
{
const size_t GZIP_CHUNK_SIZE = 16384;
GzipInflator::GzipInflator (): m_IsDirty (false)
{
memset (&m_Inflator, 0, sizeof (m_Inflator));
inflateInit2 (&m_Inflator, MAX_WBITS + 16); // gzip
}
GzipInflator::~GzipInflator ()
{
inflateEnd (&m_Inflator);
}
size_t GzipInflator::Inflate (const uint8_t * in, size_t inLen, uint8_t * out, size_t outLen)
{
if (m_IsDirty) inflateReset (&m_Inflator);
m_IsDirty = true;
m_Inflator.next_in = const_cast<uint8_t *>(in);
m_Inflator.avail_in = inLen;
m_Inflator.next_out = out;
m_Inflator.avail_out = outLen;
int err;
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;
}
void GzipInflator::Inflate (const uint8_t * in, size_t inLen, std::ostream& os)
{
m_IsDirty = true;
uint8_t * out = new uint8_t[GZIP_CHUNK_SIZE];
m_Inflator.next_in = const_cast<uint8_t *>(in);
m_Inflator.avail_in = inLen;
int ret;
do
{
m_Inflator.next_out = out;
m_Inflator.avail_out = GZIP_CHUNK_SIZE;
ret = inflate (&m_Inflator, Z_NO_FLUSH);
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
delete[] out;
}
void GzipInflator::Inflate (std::istream& in, std::ostream& out)
{
uint8_t * buf = new uint8_t[GZIP_CHUNK_SIZE];
while (!in.eof ())
{
in.read ((char *) buf, GZIP_CHUNK_SIZE);
Inflate (buf, in.gcount (), out);
}
delete[] buf;
}
GzipDeflator::GzipDeflator (): m_IsDirty (false)
{
memset (&m_Deflator, 0, sizeof (m_Deflator));
deflateInit2 (&m_Deflator, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 15 + 16, 8, Z_DEFAULT_STRATEGY); // 15 + 16 sets gzip
}
GzipDeflator::~GzipDeflator ()
{
deflateEnd (&m_Deflator);
}
void GzipDeflator::SetCompressionLevel (int level)
{
deflateParams (&m_Deflator, level, Z_DEFAULT_STRATEGY);
}
size_t GzipDeflator::Deflate (const uint8_t * in, size_t inLen, uint8_t * out, size_t outLen)
{
if (m_IsDirty) deflateReset (&m_Deflator);
m_IsDirty = true;
m_Deflator.next_in = const_cast<uint8_t *>(in);
m_Deflator.avail_in = inLen;
m_Deflator.next_out = out;
m_Deflator.avail_out = outLen;
int err;
if ((err = deflate (&m_Deflator, Z_FINISH)) == Z_STREAM_END)
return outLen - m_Deflator.avail_out;
// else
LogPrint (eLogError, "Gzip: Deflate error ", err);
return 0;
}
} // data
} // i2p

44
Gzip.h Normal file
View File

@@ -0,0 +1,44 @@
#ifndef GZIP_H__
#define GZIP_H__
#include <zlib.h>
namespace i2p {
namespace data {
class GzipInflator
{
public:
GzipInflator ();
~GzipInflator ();
size_t Inflate (const uint8_t * in, size_t inLen, uint8_t * out, size_t outLen);
/** @note @a os failbit will be set in case of error */
void Inflate (const uint8_t * in, size_t inLen, std::ostream& os);
void Inflate (std::istream& in, std::ostream& out);
private:
z_stream m_Inflator;
bool m_IsDirty;
};
class GzipDeflator
{
public:
GzipDeflator ();
~GzipDeflator ();
void SetCompressionLevel (int level);
size_t Deflate (const uint8_t * in, size_t inLen, uint8_t * out, size_t outLen);
private:
z_stream m_Deflator;
bool m_IsDirty;
};
} // data
} // i2p
#endif /* GZIP_H__ */

114
HTTP.cpp
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) {
@@ -72,7 +71,8 @@ namespace http {
bool URL::parse(const std::string& url) {
std::size_t pos_p = 0; /* < current parse position */
std::size_t pos_c = 0; /* < work position */
if (url.at(0) != '/') {
if(url.at(0) != '/' || pos_p > 0) {
std::size_t pos_s = 0;
/* schema */
pos_c = url.find("://");
if (pos_c != std::string::npos) {
@@ -80,8 +80,9 @@ namespace http {
pos_p = pos_c + 3;
}
/* user[:pass] */
pos_c = url.find('@', pos_p);
if (pos_c != std::string::npos) {
pos_s = url.find('/', pos_p); /* find first slash */
pos_c = url.find('@', pos_p); /* find end of 'user' or 'user:pass' part */
if (pos_c != std::string::npos && (pos_s == std::string::npos || pos_s > pos_c)) {
std::size_t delim = url.find(':', pos_p);
if (delim != std::string::npos && delim < pos_c) {
user = url.substr(pos_p, delim - pos_p);
@@ -158,7 +159,7 @@ namespace http {
strsplit(query, tokens, '&');
params.clear();
for (auto it : tokens) {
for (const auto& it : tokens) {
std::size_t eq = it.find ('=');
if (eq != std::string::npos) {
auto e = std::pair<std::string, std::string>(it.substr(0, eq), it.substr(eq + 1));
@@ -245,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)
@@ -257,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;
@@ -276,7 +322,20 @@ namespace http {
return false;
}
long int HTTPMsg::length() {
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() const
{
unsigned long int length = 0;
auto it = headers.find("Content-Length");
if (it == headers.end())
@@ -319,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);

23
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 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,8 +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 includingI2PGzip = true) const;
};
/**

View File

@@ -1,8 +1,14 @@
#include <cstring>
#include <cassert>
#include <boost/lexical_cast.hpp>
#include <string>
#include <atomic>
#include <memory>
#include <set>
#include <boost/asio.hpp>
#include <mutex>
#include "I2PService.h"
#include "Destination.h"
#include "HTTPProxy.h"
#include "util.h"
#include "Identity.h"
@@ -14,71 +20,101 @@
#include "Config.h"
#include "HTTP.h"
namespace i2p
{
namespace proxy
{
static const size_t http_buffer_size = 8192;
class HTTPProxyHandler: public i2p::client::I2PServiceHandler, public std::enable_shared_from_this<HTTPProxyHandler>
namespace i2p {
namespace proxy {
std::map<std::string, std::string> jumpservices = {
{ "inr.i2p", "http://joajgazyztfssty4w2on5oaqksz6tqoxbduy553y34mf4byv6gpq.b32.i2p/search/?q=" },
{ "stats.i2p", "http://7tbay5p4kzeekxvyvbf6v7eauazemsnnl2aoyqhg5jzpr5eke7tq.b32.i2p/cgi-bin/jump.cgi?a=" },
};
static const char *pageHead =
"<head>\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"
" </style>\r\n"
"</head>\r\n"
;
bool str_rmatch(std::string & str, const char *suffix) {
auto pos = str.rfind (suffix);
if (pos == std::string::npos)
return false; /* not found */
if (str.length() == (pos + std::strlen(suffix)))
return true; /* match */
return false;
}
class HTTPReqHandler: public i2p::client::I2PServiceHandler, public std::enable_shared_from_this<HTTPReqHandler>
{
private:
enum state
{
GET_METHOD,
GET_HOSTNAME,
GET_HTTPV,
GET_HTTPVNL, //TODO: fallback to finding HOst: header if needed
DONE
};
void EnterState(state nstate);
bool HandleData(uint8_t *http_buff, std::size_t len);
bool HandleRequest();
void HandleSockRecv(const boost::system::error_code & ecode, std::size_t bytes_transfered);
void Terminate();
void AsyncSockRead();
void HTTPRequestFailed(/*std::string message*/);
void RedirectToJumpService();
void ExtractRequest();
bool IsI2PAddress();
bool ValidateHTTPRequest();
void HandleJumpServices();
bool CreateHTTPRequest(uint8_t *http_buff, std::size_t len);
bool ExtractAddressHelper(i2p::http::URL & url, std::string & b64);
void SanitizeHTTPRequest(i2p::http::HTTPReq & req);
void SentHTTPFailed(const boost::system::error_code & ecode);
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);
uint8_t m_http_buff[http_buffer_size];
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::string m_request; //Data left to be sent
std::string m_url; //URL
std::string m_method; //Method
std::string m_version; //HTTP version
std::string m_address; //Address
std::string m_path; //Path
int m_port; //Port
state m_state;//Parsing state
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:
HTTPProxyHandler(HTTPProxyServer * parent, std::shared_ptr<boost::asio::ip::tcp::socket> sock) :
I2PServiceHandler(parent), m_sock(sock)
{ EnterState(GET_METHOD); }
~HTTPProxyHandler() { Terminate(); }
void Handle () { AsyncSockRead(); }
HTTPReqHandler(HTTPProxy * parent, std::shared_ptr<boost::asio::ip::tcp::socket> 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 */
};
void HTTPProxyHandler::AsyncSockRead()
void HTTPReqHandler::AsyncSockRead()
{
LogPrint(eLogDebug, "HTTPProxy: async sock read");
if(m_sock) {
m_sock->async_receive(boost::asio::buffer(m_http_buff, http_buffer_size),
std::bind(&HTTPProxyHandler::HandleSockRecv, shared_from_this(),
std::placeholders::_1, std::placeholders::_2));
} else {
if (!m_sock) {
LogPrint(eLogError, "HTTPProxy: no socket for read");
return;
}
m_sock->async_read_some(boost::asio::buffer(m_recv_chunk, sizeof(m_recv_chunk)),
std::bind(&HTTPReqHandler::HandleSockRecv, shared_from_this(),
std::placeholders::_1, std::placeholders::_2));
}
void HTTPProxyHandler::Terminate() {
void HTTPReqHandler::Terminate() {
if (Kill()) return;
if (m_sock)
{
@@ -86,209 +122,350 @@ 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());
}
/* All hope is lost beyond this point */
//TODO: handle this apropriately
void HTTPProxyHandler::HTTPRequestFailed(/*HTTPProxyHandler::errTypes error*/)
{
static std::string response = "HTTP/1.0 500 Internal Server Error\r\nContent-type: text/html\r\nContent-length: 0\r\n\r\n";
boost::asio::async_write(*m_sock, boost::asio::buffer(response,response.size()),
std::bind(&HTTPProxyHandler::SentHTTPFailed, shared_from_this(), std::placeholders::_1));
void HTTPReqHandler::GenericProxyError(const char *title, const char *description) {
std::stringstream ss;
ss << "<h1>Proxy error: " << title << "</h1>\r\n";
ss << "<p>" << description << "</p>\r\n";
std::string content = ss.str();
SendProxyError(content);
}
void HTTPProxyHandler::RedirectToJumpService(/*HTTPProxyHandler::errTypes error*/)
{
std::stringstream response;
std::string httpAddr; i2p::config::GetOption("http.address", httpAddr);
uint16_t httpPort; i2p::config::GetOption("http.port", httpPort);
response << "HTTP/1.1 302 Found\r\nLocation: http://" << httpAddr << ":" << httpPort << "/?page=jumpservices&address=" << m_address << "\r\n\r\n";
boost::asio::async_write(*m_sock, boost::asio::buffer(response.str (),response.str ().length ()),
std::bind(&HTTPProxyHandler::SentHTTPFailed, shared_from_this(), std::placeholders::_1));
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 HTTPProxyHandler::EnterState(HTTPProxyHandler::state nstate)
{
m_state = nstate;
}
void HTTPProxyHandler::ExtractRequest()
{
LogPrint(eLogDebug, "HTTPProxy: request: ", m_method, " ", m_url);
i2p::http::URL url;
url.parse (m_url);
m_address = url.host;
m_port = url.port;
m_path = url.path;
if (url.query.length () > 0) m_path += "?" + url.query;
if (!m_port) m_port = 80;
LogPrint(eLogDebug, "HTTPProxy: server: ", m_address, ", port: ", m_port, ", path: ", m_path);
}
bool HTTPProxyHandler::ValidateHTTPRequest()
{
if ( m_version != "HTTP/1.0" && m_version != "HTTP/1.1" )
{
LogPrint(eLogError, "HTTPProxy: unsupported version: ", m_version);
HTTPRequestFailed(); //TODO: send right stuff
return false;
void HTTPReqHandler::HostNotFound(std::string & host) {
std::stringstream ss;
ss << "<h1>Proxy error: Host not found</h1>\r\n"
<< "<p>Remote host not found in router's addressbook</p>\r\n"
<< "<p>You may try to find this host on jumpservices below:</p>\r\n"
<< "<ul>\r\n";
for (const auto& js : jumpservices) {
ss << " <li><a href=\"" << js.second << host << "\">" << js.first << "</a></li>\r\n";
}
ss << "</ul>\r\n";
std::string content = ss.str();
SendProxyError(content);
}
void HTTPReqHandler::SendProxyError(std::string & content)
{
i2p::http::HTTPRes res;
res.code = 500;
res.add_header("Content-Type", "text/html; charset=UTF-8");
res.add_header("Connection", "close");
std::stringstream ss;
ss << "<html>\r\n" << pageHead
<< "<body>" << content << "</body>\r\n"
<< "</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::transfer_all(),
std::bind(&HTTPReqHandler::SentHTTPFailed, shared_from_this(), std::placeholders::_1));
}
bool HTTPReqHandler::ExtractAddressHelper(i2p::http::URL & url, std::string & b64)
{
const char *param = "i2paddresshelper=";
std::size_t pos = url.query.find(param);
std::size_t len = std::strlen(param);
std::map<std::string, std::string> params;
if (pos == std::string::npos)
return false; /* not found */
if (!url.parse_query(params))
return false;
std::string value = params["i2paddresshelper"];
len += value.length();
b64 = i2p::http::UrlDecode(value);
url.query.replace(pos, len, "");
return true;
}
void HTTPProxyHandler::HandleJumpServices()
void HTTPReqHandler::SanitizeHTTPRequest(i2p::http::HTTPReq & req)
{
static const char * helpermark1 = "?i2paddresshelper=";
static const char * helpermark2 = "&i2paddresshelper=";
size_t addressHelperPos1 = m_path.rfind (helpermark1);
size_t addressHelperPos2 = m_path.rfind (helpermark2);
size_t addressHelperPos;
if (addressHelperPos1 == std::string::npos)
{
if (addressHelperPos2 == std::string::npos)
return; //Not a jump service
else
addressHelperPos = addressHelperPos2;
/* drop common headers */
req.RemoveHeader ("Referer");
req.RemoveHeader("Via");
req.RemoveHeader("Forwarded");
/* drop proxy-disclosing headers */
req.RemoveHeader("X-Forwarded");
req.RemoveHeader("Proxy-");
/* replace headers */
req.UpdateHeader("User-Agent", "MYOB/6.66 (AN/ON)");
/* add headers */
req.AddHeader("Connection", "close"); /* keep-alive conns not supported yet */
}
/**
* @brief Try to parse request from @a m_recv_buf
* If parsing success, rebuild request and store to @a m_send_buf
* with remaining data tail
* @return true on processed request or false if more data needed
*/
bool HTTPReqHandler::HandleRequest()
{
std::string b64;
m_req_len = m_ClientRequest.parse(m_recv_buf);
if (m_req_len == 0)
return false; /* need more data */
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: ", m_ClientRequest.uri);
m_RequestURL.parse(m_ClientRequest.uri);
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 " << m_RequestURL.host << " added to router's addressbook from helper. "
<< "Click <a href=\"" << full_url << "\">here</a> to proceed.";
GenericProxyInfo("Addresshelper found", ss.str().c_str());
return true; /* request processed */
}
SanitizeHTTPRequest(m_ClientRequest);
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 = (m_RequestURL.schema == "https") ? 443 : 80;
/* detect dest_host, set proper 'Host' header in upstream request */
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);
m_ClientRequest.UpdateHeader("Host", h);
}
else
{
if (addressHelperPos2 == std::string::npos)
addressHelperPos = addressHelperPos1;
else if ( addressHelperPos1 > addressHelperPos2 )
addressHelperPos = addressHelperPos1;
else
addressHelperPos = addressHelperPos2;
}
auto base64 = m_path.substr (addressHelperPos + strlen(helpermark1));
base64 = i2p::util::http::urlDecode(base64); //Some of the symbols may be urlencoded
LogPrint (eLogInfo, "HTTPProxy: jump service for ", m_address, ", inserting to address book");
//TODO: this is very dangerous and broken. We should ask the user before doing anything see http://pastethis.i2p/raw/pn5fL4YNJL7OSWj3Sc6N/
//TODO: we could redirect the user again to avoid dirtiness in the browser
i2p::client::context.GetAddressBook ().InsertAddress (m_address, base64);
m_path.erase(addressHelperPos);
}
bool HTTPProxyHandler::IsI2PAddress()
{
auto pos = m_address.rfind (".i2p");
if (pos != std::string::npos && (pos+4) == m_address.length ())
{
return true;
}
return false;
}
bool HTTPProxyHandler::CreateHTTPRequest(uint8_t *http_buff, std::size_t len)
{
ExtractRequest(); //TODO: parse earlier
if (!ValidateHTTPRequest()) return false;
HandleJumpServices();
i2p::data::IdentHash identHash;
if (IsI2PAddress ())
{
if (!i2p::client::context.GetAddressBook ().GetIdentHash (m_address, identHash)){
RedirectToJumpService();
return false;
}
}
m_request = m_method;
m_request.push_back(' ');
m_request += m_path;
m_request.push_back(' ');
m_request += m_version;
m_request.push_back('\r');
m_request.push_back('\n');
m_request.append("Connection: close\r\n");
// TODO: temporary shortcut. Must be implemented properly
uint8_t * eol = nullptr;
bool isEndOfHeader = false;
while (!isEndOfHeader && len && (eol = (uint8_t *)memchr (http_buff, '\r', len)))
{
if (eol)
{
auto h = m_ClientRequest.GetHeader ("Host");
if (h.length () > 0)
{
*eol = 0; eol++;
if (strncmp ((const char *)http_buff, "Referer", 7) && strncmp ((const char *)http_buff, "Connection", 10)) // strip out referer and connection
{
if (!strncmp ((const char *)http_buff, "User-Agent", 10)) // replace UserAgent
m_request.append("User-Agent: MYOB/6.66 (AN/ON)");
else
m_request.append ((const char *)http_buff);
m_request.append ("\r\n");
}
isEndOfHeader = !http_buff[0];
auto l = eol - http_buff;
http_buff = eol;
len -= l;
if (len > 0) // \r
{
http_buff++;
len--;
}
/* 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;
}
}
m_request.append(reinterpret_cast<const char *>(http_buff),len);
return true;
}
bool HTTPProxyHandler::HandleData(uint8_t *http_buff, std::size_t len)
{
while (len > 0)
{
//TODO: fallback to finding HOst: header if needed
switch (m_state)
{
case GET_METHOD:
switch (*http_buff)
{
case ' ': EnterState(GET_HOSTNAME); break;
default: m_method.push_back(*http_buff); break;
}
break;
case GET_HOSTNAME:
switch (*http_buff)
{
case ' ': EnterState(GET_HTTPV); break;
default: m_url.push_back(*http_buff); break;
}
break;
case GET_HTTPV:
switch (*http_buff)
{
case '\r': EnterState(GET_HTTPVNL); break;
default: m_version.push_back(*http_buff); break;
}
break;
case GET_HTTPVNL:
switch (*http_buff)
{
case '\n': EnterState(DONE); break;
default:
LogPrint(eLogError, "HTTPProxy: rejected invalid request ending with: ", ((int)*http_buff));
HTTPRequestFailed(); //TODO: add correct code
return false;
}
break;
default:
LogPrint(eLogError, "HTTPProxy: invalid state: ", m_state);
HTTPRequestFailed(); //TODO: add correct code 500
return false;
/* check dest_host really exists and inside I2P network */
i2p::data::IdentHash identHash;
if (str_rmatch(dest_host, ".i2p")) {
if (!i2p::client::context.GetAddressBook ().GetIdentHash (dest_host, identHash)) {
HostNotFound(dest_host);
return true; /* request processed */
}
http_buff++;
len--;
if (m_state == DONE)
return CreateHTTPRequest(http_buff,len);
} else {
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 */
m_RequestURL.schema = "";
m_RequestURL.host = "";
m_ClientRequest.uri = m_RequestURL.to_string();
/* drop original request from recv buffer */
m_recv_buf.erase(0, m_req_len);
/* build new buffer from modified request and data from original request */
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);
GetOwner()->CreateStream (std::bind (&HTTPReqHandler::HandleStreamRequestComplete,
shared_from_this(), std::placeholders::_1), dest_host, dest_port);
return true;
}
void HTTPProxyHandler::HandleSockRecv(const boost::system::error_code & ecode, std::size_t len)
void HTTPReqHandler::ForwardToUpstreamProxy()
{
LogPrint(eLogDebug, "HTTPProxy: sock recv: ", len, " bytes");
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)
{
LogPrint(eLogDebug, "HTTPProxy: sock recv: ", len, " bytes, recv buf: ", m_recv_buf.length(), ", send buf: ", m_send_buf.length());
if(ecode)
{
LogPrint(eLogWarning, "HTTPProxy: sock recv got error: ", ecode);
@@ -296,54 +473,45 @@ namespace proxy
return;
}
if (HandleData(m_http_buff, len))
{
if (m_state == DONE)
{
LogPrint(eLogDebug, "HTTPProxy: requested: ", m_url);
GetOwner()->CreateStream (std::bind (&HTTPProxyHandler::HandleStreamRequestComplete,
shared_from_this(), std::placeholders::_1), m_address, m_port);
}
else
AsyncSockRead();
m_recv_buf.append(reinterpret_cast<const char *>(m_recv_chunk), len);
if (HandleRequest()) {
m_recv_buf.clear();
return;
}
AsyncSockRead();
}
void HTTPProxyHandler::SentHTTPFailed(const boost::system::error_code & ecode)
void HTTPReqHandler::SentHTTPFailed(const boost::system::error_code & ecode)
{
if (ecode)
LogPrint (eLogError, "HTTPProxy: Closing socket after sending failure because: ", ecode.message ());
Terminate();
}
void HTTPProxyHandler::HandleStreamRequestComplete (std::shared_ptr<i2p::stream::Stream> stream)
void HTTPReqHandler::HandleStreamRequestComplete (std::shared_ptr<i2p::stream::Stream> stream)
{
if (stream)
{
if (Kill()) return;
LogPrint (eLogInfo, "HTTPProxy: New I2PTunnel connection");
auto connection = std::make_shared<i2p::client::I2PTunnelConnection>(GetOwner(), m_sock, stream);
GetOwner()->AddHandler (connection);
connection->I2PConnect (reinterpret_cast<const uint8_t*>(m_request.data()), m_request.size());
Done(shared_from_this());
}
else
{
if (!stream) {
LogPrint (eLogError, "HTTPProxy: error when creating the stream, check the previous warnings for more info");
HTTPRequestFailed(); // TODO: Send correct error message host unreachable
GenericProxyError("Host is down", "Can't create connection to requested host, it may be down");
return;
}
if (Kill())
return;
LogPrint (eLogDebug, "HTTPProxy: Created new I2PTunnel stream, sSID=", stream->GetSendStreamID(), ", rSID=", stream->GetRecvStreamID());
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());
}
HTTPProxyServer::HTTPProxyServer(const std::string& address, int port, std::shared_ptr<i2p::client::ClientDestination> localDestination):
HTTPProxy::HTTPProxy(const std::string& address, int port, std::shared_ptr<i2p::client::ClientDestination> localDestination):
TCPIPAcceptor(address, port, localDestination ? localDestination : i2p::client::context.GetSharedLocalDestination ())
{
}
std::shared_ptr<i2p::client::I2PServiceHandler> HTTPProxyServer::CreateHandler(std::shared_ptr<boost::asio::ip::tcp::socket> socket)
std::shared_ptr<i2p::client::I2PServiceHandler> HTTPProxy::CreateHandler(std::shared_ptr<boost::asio::ip::tcp::socket> socket)
{
return std::make_shared<HTTPProxyHandler> (this, socket);
return std::make_shared<HTTPReqHandler> (this, socket);
}
}
}
} // http
} // i2p

View File

@@ -1,32 +1,21 @@
#ifndef HTTP_PROXY_H__
#define HTTP_PROXY_H__
#include <memory>
#include <set>
#include <boost/asio.hpp>
#include <mutex>
#include "I2PService.h"
#include "Destination.h"
namespace i2p
{
namespace proxy
{
class HTTPProxyServer: public i2p::client::TCPIPAcceptor
namespace i2p {
namespace proxy {
class HTTPProxy: public i2p::client::TCPIPAcceptor
{
public:
HTTPProxyServer(const std::string& address, int port, std::shared_ptr<i2p::client::ClientDestination> localDestination = nullptr);
~HTTPProxyServer() {};
HTTPProxy(const std::string& address, int port, std::shared_ptr<i2p::client::ClientDestination> localDestination = nullptr);
~HTTPProxy() {};
protected:
// Implements TCPIPAcceptor
std::shared_ptr<i2p::client::I2PServiceHandler> CreateHandler(std::shared_ptr<boost::asio::ip::tcp::socket> socket);
const char* GetName() { return "HTTP Proxy"; }
};
typedef HTTPProxyServer HTTPProxy;
}
}
} // http
} // i2p
#endif

View File

@@ -22,6 +22,9 @@
#include "HTTPServer.h"
#include "Daemon.h"
#include "util.h"
#ifdef WIN32_APP
#include "Win32/Win32App.h"
#endif
// For image and info
#include "version.h"
@@ -30,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"
@@ -58,35 +64,34 @@ namespace http {
" .tunnel.another { color: #434343; }\r\n"
" caption { font-size: 1.5em; text-align: center; color: #894C84; }\r\n"
" table { width: 100%; border-collapse: collapse; text-align: center; }\r\n"
" .private { background: black; color: black; } .private:hover { background: black; color: white } \r\n"
" .slide p, .slide [type='checkbox']{ display:none; } \r\n"
" .slide [type='checkbox']:checked ~ p { display:block; } \r\n"
"</style>\r\n";
const char HTTP_PAGE_TUNNELS[] = "tunnels";
const char HTTP_PAGE_TRANSIT_TUNNELS[] = "transit_tunnels";
const char HTTP_PAGE_TRANSPORTS[] = "transports";
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";
const char HTTP_PAGE_JUMPSERVICES[] = "jumpservices";
const char HTTP_PAGE_COMMANDS[] = "commands";
const char HTTP_COMMAND_START_ACCEPTING_TUNNELS[] = "start_accepting_tunnels";
const char HTTP_COMMAND_STOP_ACCEPTING_TUNNELS[] = "stop_accepting_tunnels";
const char HTTP_PAGE_LEASESETS[] = "leasesets";
const char HTTP_COMMAND_ENABLE_TRANSIT[] = "enable_transit";
const char HTTP_COMMAND_DISABLE_TRANSIT[] = "disable_transit";
const char HTTP_COMMAND_SHUTDOWN_START[] = "shutdown_start";
const char HTTP_COMMAND_SHUTDOWN_CANCEL[] = "shutdown_cancel";
const char HTTP_COMMAND_SHUTDOWN_NOW[] = "terminate";
const char HTTP_COMMAND_RUN_PEER_TEST[] = "run_peer_test";
const char HTTP_COMMAND_RELOAD_CONFIG[] = "reload_config";
const char HTTP_PARAM_BASE32_ADDRESS[] = "b32";
const char HTTP_COMMAND_RELOAD_CONFIG[] = "reload_config";
const char HTTP_PARAM_SAM_SESSION_ID[] = "id";
const char HTTP_PARAM_ADDRESS[] = "address";
std::map<std::string, std::string> jumpservices = {
{ "inr.i2p", "http://joajgazyztfssty4w2on5oaqksz6tqoxbduy553y34mf4byv6gpq.b32.i2p/search/?q=" },
{ "stats.i2p", "http://7tbay5p4kzeekxvyvbf6v7eauazemsnnl2aoyqhg5jzpr5eke7tq.b32.i2p/cgi-bin/jump.cgi?a=" },
};
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,13 +126,17 @@ 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"
"<html lang=\"en\">\r\n" /* TODO: Add support for locale */
" <head>\r\n"
" <meta charset=\"UTF-8\">\r\n" /* TODO: Find something to parse html/template system. This is horrible. */
" <head>\r\n" /* TODO: Find something to parse html/template system. This is horrible. */
#if (!defined(WIN32))
" <meta charset=\"UTF-8\">\r\n"
#else
" <meta charset=\"windows-1251\">\r\n"
#endif
" <link rel=\"shortcut icon\" href=\"" << itoopieFavicon << "\">\r\n"
" <title>Purple I2P " VERSION " Webconsole</title>\r\n"
<< cssStyles <<
@@ -140,17 +149,19 @@ namespace http {
" <a href=\"/\">Main page</a><br>\r\n<br>\r\n"
" <a href=\"/?page=" << HTTP_PAGE_COMMANDS << "\">Router commands</a><br>\r\n"
" <a href=\"/?page=" << HTTP_PAGE_LOCAL_DESTINATIONS << "\">Local destinations</a><br>\r\n"
" <a href=\"/?page=" << HTTP_PAGE_LEASESETS << "\">LeaseSets</a><br>\r\n"
" <a href=\"/?page=" << HTTP_PAGE_TUNNELS << "\">Tunnels</a><br>\r\n"
" <a href=\"/?page=" << HTTP_PAGE_TRANSIT_TUNNELS << "\">Transit tunnels</a><br>\r\n"
" <a href=\"/?page=" << HTTP_PAGE_TRANSPORTS << "\">Transports</a><br>\r\n"
" <a href=\"/?page=" << HTTP_PAGE_I2P_TUNNELS << "\">I2P tunnels</a><br>\r\n"
" <a href=\"/?page=" << HTTP_PAGE_JUMPSERVICES << "\">Jump services</a><br>\r\n"
" <a href=\"/?page=" << HTTP_PAGE_SAM_SESSIONS << "\">SAM sessions</a><br>\r\n"
" <a href=\"/?page=" << HTTP_PAGE_I2P_TUNNELS << "\">I2P tunnels</a><br>\r\n";
if (i2p::client::context.GetSAMBridge ())
s << " <a href=\"/?page=" << HTTP_PAGE_SAM_SESSIONS << "\">SAM sessions</a><br>\r\n";
s <<
"</div>\r\n"
"<div class=right>";
}
void ShowPageTail (std::stringstream& s)
static void ShowPageTail (std::stringstream& s)
{
s <<
"</div></div>\r\n"
@@ -158,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";
@@ -200,9 +230,13 @@ 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";
s << "<b>Router Caps:</b> " << i2p::context.GetRouterInfo().GetProperty("caps") << "<br>\r\n";
s << "<b>Our external address:</b>" << "<br>\r\n" ;
for (auto address : i2p::context.GetRouterInfo().GetAddresses())
for (const auto& address : i2p::context.GetRouterInfo().GetAddresses())
{
switch (address->transportStyle)
{
@@ -223,44 +257,88 @@ namespace http {
}
s << address->host.to_string() << ":" << address->port << "<br>\r\n";
}
s << "<br>\r\n<b>Routers:</b> " << i2p::data::netdb.GetNumRouters () << " ";
s << "</p>\r\n</div>\r\n";
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";
size_t clientTunnelCount = i2p::tunnel::tunnels.CountOutboundTunnels();
clientTunnelCount += i2p::tunnel::tunnels.CountInboundTunnels();
size_t transitTunnelCount = i2p::tunnel::tunnels.CountTransitTunnels();
s << "<b>Client Tunnels:</b> " << std::to_string(clientTunnelCount) << " ";
s << "<b>Transit Tunnels:</b> " << std::to_string(transitTunnelCount) << "<br>\r\n";
s << "<b>Client Tunnels:</b> " << std::to_string(clientTunnelCount) << " ";
s << "<b>Transit Tunnels:</b> " << std::to_string(transitTunnelCount) << "<br>\r\n";
}
void ShowJumpServices (std::stringstream& s, const std::string& address)
{
s << "<form method=\"get\" action=\"/\">";
s << "<input type=\"hidden\" name=\"page\" value=\"jumpservices\">";
s << "<input type=\"text\" name=\"address\" value=\"" << address << "\">";
s << "<input type=\"submit\" value=\"Update\">";
s << "</form><br>\r\n";
s << "<b>Jump services for " << address << "</b>\r\n<ul>\r\n";
for (auto & js : jumpservices) {
s << " <li><a href=\"" << js.second << address << "\">" << js.first << "</a></li>\r\n";
}
s << "</ul>\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;
@@ -268,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 (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>";
@@ -319,8 +361,8 @@ namespace http {
s << "<th>Status</th>";
s << "</tr>";
for (auto it: dest->GetAllStreams ())
{
for (const auto& it: dest->GetAllStreams ())
{
s << "<tr>";
s << "<td>" << it->GetSendStreamID () << "</td>";
s << "<td>" << i2p::client::context.GetAddressBook ().ToAddress(it->GetRemoteIdentity ()) << "</td>";
@@ -333,115 +375,171 @@ namespace http {
s << "<td>" << it->GetWindowSize () << "</td>";
s << "<td>" << (int)it->GetStatus () << "</td>";
s << "</tr><br>\r\n" << std::endl;
}
}
}
s << "</table>";
}
}
void ShowTunnels (std::stringstream& s)
static void ShowI2CPLocalDestination (std::stringstream& s, const std::string& id)
{
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, &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());
s << "<div class='leaseset";
if (ls.IsExpired())
s << " expired"; // additional css class for expired
s << "'>\r\n";
if (!ls.IsValid())
s << "<div class='invalid'>!! Invalid !! </div>\r\n";
s << "<div class='slide'><label for='slide" << counter << "'>" << dest.ToBase32() << "</label>\r\n";
s << "<input type='checkbox' id='slide" << (counter++) << "'/>\r\n<p class='content'>\r\n";
s << "<b>Expires:</b> " << ls.GetExpirationTime() << "<br>\r\n";
auto leases = ls.GetNonExpiredLeases();
s << "<b>Non Expired Leases: " << leases.size() << "</b><br>\r\n";
for ( auto & l : leases )
{
s << "<b>Gateway:</b> " << l->tunnelGateway.ToBase64() << "<br>\r\n";
s << "<b>TunnelID:</b> " << l->tunnelID << "<br>\r\n";
s << "<b>EndDate:</b> " << l->endDate << "<br>\r\n";
}
s << "</p>\r\n</div>\r\n</div>\r\n";
}
);
// end for each lease set
}
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_STOP_ACCEPTING_TUNNELS << "\">Stop accepting tunnels</a><br>\r\n";
else
s << " <a href=\"/?cmd=" << HTTP_COMMAND_START_ACCEPTING_TUNNELS << "\">Start accepting tunnels</a><br>\r\n";
#if (!defined(WIN32) && !defined(QT_GUI_LIB))
if (Daemon.gracefullShutdownInterval) {
s << " <a href=\"/?cmd=" << HTTP_COMMAND_SHUTDOWN_CANCEL << "\">Cancel gracefull shutdown (";
s << Daemon.gracefullShutdownInterval;
s << " seconds remains)</a><br>\r\n";
} else {
s << " <a href=\"/?cmd=" << HTTP_COMMAND_SHUTDOWN_START << "\">Start gracefull shutdown</a><br>\r\n";
}
s << " <a href=\"/?cmd=" << HTTP_COMMAND_SHUTDOWN_NOW << "\">Force shutdown</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 << "&token=" << token << "\">Accept transit tunnels</a><br>\r\n";
#if (!defined(WIN32) && !defined(QT_GUI_LIB) && !defined(ANDROID))
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 << "&token=" << token << "\">Start graceful shutdown</a><br>\r\n";
#endif
#ifdef WIN32_APP
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 << "&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 (auto it: i2p::tunnel::tunnels.GetTransitTunnels ())
for (const auto& it: i2p::tunnel::tunnels.GetTransitTunnels ())
{
if (std::dynamic_pointer_cast<i2p::tunnel::TransitTunnelGateway>(it))
s << it->GetTunnelID () << " ";
s << it->GetTunnelID () << " &#8658; ";
else if (std::dynamic_pointer_cast<i2p::tunnel::TransitTunnelEndpoint>(it))
s << " " << it->GetTunnelID ();
s << " &#8658; " << it->GetTunnelID ();
else
s << " " << it->GetTunnelID () << " ";
s << " &#8658; " << it->GetTunnelID () << " &#8658; ";
s << " " << it->GetNumTransmittedBytes () << "<br>\r\n";
}
}
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 (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 ())
{
// incoming connection doesn't have remote RI
if (it.second->IsOutgoing ()) s << " ";
if (it.second->IsOutgoing ()) s << " &#8658; ";
s << i2p::data::GetIdentHashAbbreviation (it.second->GetRemoteIdentity ()->GetIdentHash ()) << ": "
<< it.second->GetSocket ().remote_endpoint().address ().to_string ();
if (!it.second->IsOutgoing ()) s << " ";
if (!it.second->IsOutgoing ()) s << " &#8658; ";
s << " [" << it.second->GetNumSentBytes () << ":" << it.second->GetNumReceivedBytes () << "]";
s << "<br>\r\n" << std::endl;
}
}
}
}
auto ssuServer = i2p::transport::transports.GetSSUServer ();
if (ssuServer)
{
s << "<br>\r\n<b>SSU</b><br>\r\n";
for (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 << " ";
if (it.second->IsOutgoing ()) s << " &#8658; ";
s << endpoint.address ().to_string () << ":" << endpoint.port ();
if (!it.second->IsOutgoing ()) s << " ";
if (!it.second->IsOutgoing ()) s << " &#8658; ";
s << " [" << it.second->GetNumSentBytes () << ":" << it.second->GetNumReceivedBytes () << "]";
if (it.second->GetRelayTag ())
s << " [itag:" << it.second->GetRelayTag () << "]";
s << "<br>\r\n" << std::endl;
}
s << "<br>\r\n<b>SSU6</b><br>\r\n";
for (auto it: ssuServer->GetSessionsV6 ())
for (const auto& it: ssuServer->GetSessionsV6 ())
{
auto endpoint = it.second->GetRemoteEndpoint ();
if (it.second->IsOutgoing ()) s << " ";
if (it.second->IsOutgoing ()) s << " &#8658; ";
s << endpoint.address ().to_string () << ":" << endpoint.port ();
if (!it.second->IsOutgoing ()) s << " ";
if (!it.second->IsOutgoing ()) s << " &#8658; ";
s << " [" << it.second->GetNumSentBytes () << ":" << it.second->GetNumReceivedBytes () << "]";
s << "<br>\r\n" << std::endl;
}
}
}
void ShowSAMSessions (std::stringstream& s)
static void ShowSAMSessions (std::stringstream& s)
{
auto sam = i2p::client::context.GetSAMBridge ();
if (!sam) {
@@ -453,10 +551,10 @@ namespace http {
{
s << "<a href=\"/?page=" << HTTP_PAGE_SAM_SESSION << "&sam_id=" << it.first << "\">";
s << it.first << "</a><br>\r\n" << std::endl;
}
}
}
}
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 ();
@@ -474,7 +572,7 @@ namespace http {
s << i2p::client::context.GetAddressBook ().ToAddress(ident) << "</a><br>\r\n";
s << "<br>\r\n";
s << "<b>Streams:</b><br>\r\n";
for (auto it: session->ListSockets())
for (const auto& it: session->ListSockets())
{
switch (it->GetSocketType ())
{
@@ -485,17 +583,35 @@ namespace http {
}
s << " [" << it->GetSocket ().remote_endpoint() << "]";
s << "<br>\r\n";
}
}
}
}
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 ())
{
auto& ident = it.second->GetLocalDestination ()->GetIdentHash();
s << "<a href=\"/?page=" << HTTP_PAGE_LOCAL_DESTINATION << "&b32=" << ident.ToBase32 () << "\">";
s << it.second->GetName () << "</a> ";
s << it.second->GetName () << "</a> &#8656; ";
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;
}
@@ -504,12 +620,38 @@ namespace http {
{
auto& ident = it.second->GetLocalDestination ()->GetIdentHash();
s << "<a href=\"/?page=" << HTTP_PAGE_LOCAL_DESTINATION << "&b32=" << ident.ToBase32 () << "\">";
s << it.second->GetName () << "</a> ";
s << it.second->GetName () << "</a> &#8658; ";
s << i2p::client::context.GetAddressBook ().ToAddress(ident);
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):
m_Socket (socket), m_Timer (socket->get_io_service ()), m_BufferLen (0)
@@ -572,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 ());
@@ -612,7 +762,7 @@ namespace http {
HandleCommand (req, res, s);
} else {
ShowStatus (s);
//res.add_header("Refresh", "5");
res.add_header("Refresh", "10");
}
ShowPageTail (s);
@@ -621,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;
@@ -636,21 +787,37 @@ namespace http {
else if (page == HTTP_PAGE_TUNNELS)
ShowTunnels (s);
else if (page == HTTP_PAGE_COMMANDS)
ShowCommands (s);
else if (page == HTTP_PAGE_JUMPSERVICES)
ShowJumpServices (s, params["address"]);
{
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)
ShowSAMSession (s, params["sam_id"]);
else if (page == HTTP_PAGE_I2P_TUNNELS)
ShowI2PTunnels (s);
else if (page == HTTP_PAGE_LEASESETS)
ShowLeasesSets(s);
else {
res.code = 400;
ShowError(s, "Unknown page: " + page);
@@ -661,30 +828,39 @@ 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)
i2p::client::context.ReloadConfig ();
else if (cmd == HTTP_COMMAND_START_ACCEPTING_TUNNELS)
else if (cmd == HTTP_COMMAND_ENABLE_TRANSIT)
i2p::context.SetAcceptsTunnels (true);
else if (cmd == HTTP_COMMAND_STOP_ACCEPTING_TUNNELS)
else if (cmd == HTTP_COMMAND_DISABLE_TRANSIT)
i2p::context.SetAcceptsTunnels (false);
else if (cmd == HTTP_COMMAND_SHUTDOWN_START) {
i2p::context.SetAcceptsTunnels (false);
#if (!defined(WIN32) && !defined(QT_GUI_LIB))
Daemon.gracefullShutdownInterval = 10*60;
#if (!defined(WIN32) && !defined(QT_GUI_LIB) && !defined(ANDROID))
Daemon.gracefulShutdownInterval = 10*60;
#endif
#ifdef WIN32_APP
i2p::win32::GracefulShutdown ();
#endif
} else if (cmd == HTTP_COMMAND_SHUTDOWN_CANCEL) {
i2p::context.SetAcceptsTunnels (true);
#if (!defined(WIN32) && !defined(QT_GUI_LIB))
Daemon.gracefullShutdownInterval = 0;
#if (!defined(WIN32) && !defined(QT_GUI_LIB) && !defined(ANDROID))
Daemon.gracefulShutdownInterval = 0;
#endif
} else if (cmd == HTTP_COMMAND_SHUTDOWN_NOW) {
Daemon.running = false;
@@ -697,20 +873,21 @@ namespace http {
s << "<a href=\"/?page=commands\">Back to commands list</a><br>\r\n";
s << "<p>You will be redirected in 5 seconds</b>";
res.add_header("Refresh", "5; url=/?page=commands");
}
}
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))
{
}
@@ -727,16 +904,19 @@ namespace http {
std::string pass; i2p::config::GetOption("http.pass", pass);
/* generate pass if needed */
if (needAuth && pass == "") {
uint8_t random[16];
char alnum[] = "0123456789"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz";
pass.resize(16);
for (size_t i = 0; i < pass.size(); i++) {
pass[i] = alnum[rand() % (sizeof(alnum) - 1)];
pass.resize(sizeof(random));
RAND_bytes(random, sizeof(random));
for (size_t i = 0; i < sizeof(random); i++) {
pass[i] = alnum[random[i] % (sizeof(alnum) - 1)];
}
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 ();
@@ -744,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;
}
@@ -754,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 ()
@@ -768,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;

109
I2CP.cpp
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)
{
@@ -87,23 +94,51 @@ namespace client
}
bool I2CPDestination::SendMsg (std::shared_ptr<I2NPMessage> msg, std::shared_ptr<const i2p::data::LeaseSet> remote)
{
auto outboundTunnel = GetTunnelPool ()->GetNextOutboundTunnel ();
auto leases = remote->GetNonExpiredLeases ();
if (!leases.empty () && outboundTunnel)
{
auto remoteSession = GetRoutingSession (remote, true);
if (!remoteSession)
{
LogPrint (eLogError, "I2CP: Failed to create remote session");
return false;
}
auto path = remoteSession->GetSharedRoutingPath ();
std::shared_ptr<i2p::tunnel::OutboundTunnel> outboundTunnel;
std::shared_ptr<const i2p::data::Lease> remoteLease;
if (path)
{
if (!remoteSession->CleanupUnconfirmedTags ()) // no stuck tags
{
outboundTunnel = path->outboundTunnel;
remoteLease = path->remoteLease;
}
else
remoteSession->SetSharedRoutingPath (nullptr);
}
else
{
outboundTunnel = GetTunnelPool ()->GetNextOutboundTunnel ();
auto leases = remote->GetNonExpiredLeases ();
if (!leases.empty ())
remoteLease = leases[rand () % leases.size ()];
if (remoteLease && outboundTunnel)
remoteSession->SetSharedRoutingPath (std::make_shared<i2p::garlic::GarlicRoutingPath> (
i2p::garlic::GarlicRoutingPath{outboundTunnel, remoteLease, 10000, 0, 0})); // 10 secs RTT
else
remoteSession->SetSharedRoutingPath (nullptr);
}
if (remoteLease && outboundTunnel)
{
std::vector<i2p::tunnel::TunnelMessageBlock> msgs;
uint32_t i = rand () % leases.size ();
auto garlic = WrapMessage (remote, msg, true);
auto garlic = remoteSession->WrapSingleMessage (msg);
msgs.push_back (i2p::tunnel::TunnelMessageBlock
{
i2p::tunnel::eDeliveryTypeTunnel,
leases[i]->tunnelGateway, leases[i]->tunnelID,
remoteLease->tunnelGateway, remoteLease->tunnelID,
garlic
});
outboundTunnel->SendTunnelDataMsg (msgs);
return true;
}
}
else
{
if (outboundTunnel)
@@ -114,7 +149,7 @@ namespace client
}
}
I2CPSession::I2CPSession (I2CPServer& owner, std::shared_ptr<boost::asio::ip::tcp::socket> socket):
I2CPSession::I2CPSession (I2CPServer& owner, std::shared_ptr<proto::socket> socket):
m_Owner (owner), m_Socket (socket), m_Payload (nullptr),
m_SessionID (0xFFFF), m_MessageID (0), m_IsSendAccepted (true)
{
@@ -311,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)
@@ -424,13 +460,24 @@ namespace client
if (m_Destination)
{
i2p::data::IdentityEx identity;
offset += identity.FromBuffer (buf + offset, len - offset);
uint32_t payloadLen = bufbe32toh (buf + offset);
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);
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");
}
}
else
@@ -583,7 +630,12 @@ namespace client
I2CPServer::I2CPServer (const std::string& interface, int port):
m_IsRunning (false), m_Thread (nullptr),
m_Acceptor (m_Service, boost::asio::ip::tcp::endpoint(boost::asio::ip::address::from_string(interface), port))
m_Acceptor (m_Service,
#ifdef ANDROID
I2CPSession::proto::endpoint(std::string (1, '\0') + interface)) // leading 0 for abstract address
#else
I2CPSession::proto::endpoint(boost::asio::ip::address::from_string(interface), port))
#endif
{
memset (m_MessagesHandlers, 0, sizeof (m_MessagesHandlers));
m_MessagesHandlers[I2CP_GET_DATE_MESSAGE] = &I2CPSession::GetDateMessageHandler;
@@ -615,7 +667,7 @@ namespace client
{
m_IsRunning = false;
m_Acceptor.cancel ();
for (auto it: m_Sessions)
for (auto& it: m_Sessions)
it.second->Stop ();
m_Sessions.clear ();
m_Service.stop ();
@@ -644,12 +696,13 @@ namespace client
void I2CPServer::Accept ()
{
auto newSocket = std::make_shared<boost::asio::ip::tcp::socket> (m_Service);
auto newSocket = std::make_shared<I2CPSession::proto::socket> (m_Service);
m_Acceptor.async_accept (*newSocket, std::bind (&I2CPServer::HandleAccept, this,
std::placeholders::_1, newSocket));
}
void I2CPServer::HandleAccept(const boost::system::error_code& ecode, std::shared_ptr<boost::asio::ip::tcp::socket> socket)
void I2CPServer::HandleAccept(const boost::system::error_code& ecode,
std::shared_ptr<I2CPSession::proto::socket> socket)
{
if (!ecode && socket)
{
@@ -659,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
@@ -672,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);

21
I2CP.h
View File

@@ -99,12 +99,20 @@ namespace client
{
public:
I2CPSession (I2CPServer& owner, std::shared_ptr<boost::asio::ip::tcp::socket> socket);
#ifdef ANDROID
typedef boost::asio::local::stream_protocol proto;
#else
typedef boost::asio::ip::tcp proto;
#endif
I2CPSession (I2CPServer& owner, std::shared_ptr<proto::socket> socket);
~I2CPSession ();
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);
@@ -144,7 +152,7 @@ namespace client
private:
I2CPServer& m_Owner;
std::shared_ptr<boost::asio::ip::tcp::socket> m_Socket;
std::shared_ptr<proto::socket> m_Socket;
uint8_t m_Header[I2CP_HEADER_SIZE], * m_Payload;
size_t m_PayloadLen;
@@ -166,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:
@@ -173,7 +182,8 @@ namespace client
void Run ();
void Accept ();
void HandleAccept(const boost::system::error_code& ecode, std::shared_ptr<boost::asio::ip::tcp::socket> socket);
void HandleAccept(const boost::system::error_code& ecode, std::shared_ptr<I2CPSession::proto::socket> socket);
private:
@@ -183,11 +193,14 @@ namespace client
bool m_IsRunning;
std::thread * m_Thread;
boost::asio::io_service m_Service;
boost::asio::ip::tcp::acceptor m_Acceptor;
I2CPSession::proto::acceptor m_Acceptor;
public:
const decltype(m_MessagesHandlers)& GetMessagesHandlers () const { return m_MessagesHandlers; };
// for HTTP
const decltype(m_Sessions)& GetSessions () const { return m_Sessions; };
};
}
}

View File

@@ -11,6 +11,7 @@
#include "Transports.h"
#include "Garlic.h"
#include "I2NPProtocol.h"
#include "version.h"
using namespace i2p::transport;
@@ -26,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 ();
@@ -101,7 +109,7 @@ namespace i2p
{
RAND_bytes ((uint8_t *)&msgID, 4);
htobe32buf (buf + DELIVERY_STATUS_MSGID_OFFSET, msgID);
htobe64buf (buf + DELIVERY_STATUS_TIMESTAMP_OFFSET, 2); // netID = 2
htobe64buf (buf + DELIVERY_STATUS_TIMESTAMP_OFFSET, i2p::context.GetNetID ());
}
m->len += DELIVERY_STATUS_SIZE;
m->FillI2NPMessageHeader (eI2NPDeliveryStatus);
@@ -164,9 +172,10 @@ namespace i2p
buf += 32;
memcpy (buf, replyTunnel->GetNextIdentHash (), 32); // reply tunnel GW
buf += 32;
*buf = DATABASE_LOOKUP_DELIVERY_FLAG | DATABASE_LOOKUP_ENCYPTION_FLAG | DATABASE_LOOKUP_TYPE_LEASESET_LOOKUP; // flags
htobe32buf (buf + 1, replyTunnel->GetNextTunnelID ()); // reply tunnel ID
buf += 5;
*buf = DATABASE_LOOKUP_DELIVERY_FLAG | DATABASE_LOOKUP_ENCRYPTION_FLAG | DATABASE_LOOKUP_TYPE_LEASESET_LOOKUP; // flags
buf ++;
htobe32buf (buf, replyTunnel->GetNextTunnelID ()); // reply tunnel ID
buf += 4;
// excluded
htobe16buf (buf, cnt);
@@ -181,7 +190,7 @@ namespace i2p
}
// encryption
memcpy (buf, replyKey, 32);
buf[32] = 1; // 1 tag
buf[32] = uint8_t( 1 ); // 1 tag
memcpy (buf + 33, replyTag, 32);
buf += 65;
@@ -200,7 +209,7 @@ namespace i2p
len += 32;
buf[len] = routers.size ();
len++;
for (auto it: routers)
for (const auto& it: routers)
{
memcpy (buf + len, it, 32);
len += 32;
@@ -462,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;
@@ -470,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);
@@ -480,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;
}
@@ -545,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

@@ -90,7 +90,7 @@ namespace i2p
// DatabaseLookup flags
const uint8_t DATABASE_LOOKUP_DELIVERY_FLAG = 0x01;
const uint8_t DATABASE_LOOKUP_ENCYPTION_FLAG = 0x02;
const uint8_t DATABASE_LOOKUP_ENCRYPTION_FLAG = 0x02;
const uint8_t DATABASE_LOOKUP_TYPE_FLAGS_MASK = 0x0C;
const uint8_t DATABASE_LOOKUP_TYPE_NORMAL_LOOKUP = 0;
const uint8_t DATABASE_LOOKUP_TYPE_LEASESET_LOOKUP = 0x04; // 0100

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->begin() + len, bytes_transferred - len);
remains = req.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->begin(), bytes_transferred);
remains -= bytes_transferred;
}
} else {
json.write(buf->begin(), 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);
}
@@ -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 ();
@@ -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

@@ -64,10 +64,10 @@ namespace data
return;
}
// reset existing leases
// reset existing leases
if (m_StoreLeases)
for (auto it: m_Leases)
it->isUpdated = false;
for (auto& it: m_Leases)
it->isUpdated = false;
else
m_Leases.clear ();
@@ -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))
@@ -123,7 +123,7 @@ namespace data
m_Leases.erase (it++);
}
else
it++;
++it;
}
}
@@ -162,19 +162,32 @@ 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;
for (auto it: m_Leases)
for (const auto& it: m_Leases)
{
auto endDate = it->endDate;
if (withThreshold)
endDate += LEASE_ENDDATE_THRESHOLD;
else
endDate -= LEASE_ENDDATE_THRESHOLD;
if (ts < endDate)
if (ts < endDate && !exclude(*it))
leases.push_back (it);
}
return leases;
@@ -183,14 +196,14 @@ namespace data
bool LeaseSet::HasExpiredLeases () const
{
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
for (auto it: m_Leases)
for (const auto& it: m_Leases)
if (ts >= it->endDate) return true;
return false;
}
bool LeaseSet::IsExpired () const
{
if (IsEmpty ()) return true;
if (m_StoreLeases && IsEmpty ()) return true;
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
return ts > m_ExpirationTime;
}

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,13 +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_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
@@ -19,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)
@@ -30,6 +38,10 @@ else # win32 mingw
include Makefile.mingw
endif
ifeq ($(USE_MESHNET),yes)
NEEDED_CXXFLAGS += -DMESHNET
endif
all: mk_obj_dir $(ARLIB) $(ARLIB_CLIENT) $(I2PD)
mk_obj_dir:
@@ -83,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

@@ -8,7 +8,7 @@ INCFLAGS = -I${SSLROOT}/include -I${BOOSTROOT}/include
LDFLAGS = -L${SSLROOT}/lib -L${BOOSTROOT}/lib
LDLIBS = -lz -lcrypto -lssl -lboost_system -lboost_date_time -lboost_filesystem -lboost_program_options -lpthread
ifeq ($(USE_UPNP),1)
ifeq ($(USE_UPNP),yes)
LDFLAGS += -ldl
CXXFLAGS += -DUSE_UPNP
endif

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,32 +28,46 @@ 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
LDLIBS += $(LIBDIR)/libboost_filesystem.a
LDLIBS += $(LIBDIR)/libboost_program_options.a
LDLIBS += $(LIBDIR)/libcrypto.a
LDLIBS += $(LIBDIR)/libssl.a
LDLIBS += $(LIBDIR)/libcrypto.a
LDLIBS += $(LIBDIR)/libz.a
LDLIBS += -lpthread -static-libstdc++ -static-libgcc
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)
ifeq ($(USE_UPNP),1)
LDFLAGS += -ldl
# UPNP Support (miniupnpc 1.5 and higher)
ifeq ($(USE_UPNP),yes)
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

@@ -1,42 +1,57 @@
USE_WIN32_APP=yes
CXX = g++
WINDRES = windres
CXXFLAGS = -Os -D_MT -DWIN32 -D_WINDOWS -DWIN32_LEAN_AND_MEAN
NEEDED_CXXFLAGS = -std=c++11
BOOST_SUFFIX = -mt
INCFLAGS = -I/usr/include/ -I/usr/local/include/
LDFLAGS = -Wl,-rpath,/usr/local/lib \
-L/usr/local/lib \
-L/c/dev/openssl \
-L/c/dev/boost/lib
LDLIBS = \
-Wl,-Bstatic -lboost_system$(BOOST_SUFFIX) \
-Wl,-Bstatic -lboost_date_time$(BOOST_SUFFIX) \
-Wl,-Bstatic -lboost_filesystem$(BOOST_SUFFIX) \
-Wl,-Bstatic -lboost_program_options$(BOOST_SUFFIX) \
-Wl,-Bstatic -lssl \
-Wl,-Bstatic -lcrypto \
-Wl,-Bstatic -lz \
-Wl,-Bstatic -lwsock32 \
-Wl,-Bstatic -lws2_32 \
-Wl,-Bstatic -lgdi32 \
-Wl,-Bstatic -liphlpapi \
-static-libgcc -static-libstdc++ \
-Wl,-Bstatic -lstdc++ \
-Wl,-Bstatic -lpthread
ifeq ($(USE_WIN32_APP), yes)
CXXFLAGS += -DWIN32_APP
LDFLAGS += -mwindows -s
DAEMON_RC += Win32/Resource.rc
DAEMON_OBJS += $(patsubst %.rc,obj/%.o,$(DAEMON_RC))
endif
ifeq ($(USE_AESNI),1)
CPU_FLAGS = -maes -DAESNI
else
CPU_FLAGS = -msse
endif
obj/%.o : %.rc
$(WINDRES) -i $< -o $@
USE_WIN32_APP=yes
CXX = g++
WINDRES = windres
CXXFLAGS = -Os -D_MT -DWIN32 -D_WINDOWS -DWIN32_LEAN_AND_MEAN
NEEDED_CXXFLAGS = -std=c++11
BOOST_SUFFIX = -mt
INCFLAGS = -I/usr/include/ -I/usr/local/include/
LDFLAGS = -Wl,-rpath,/usr/local/lib \
-L/usr/local/lib
# UPNP Support
ifeq ($(USE_UPNP),yes)
CXXFLAGS += -DUSE_UPNP -DMINIUPNP_STATICLIB
LDLIBS = -Wl,-Bstatic -lminiupnpc
endif
LDLIBS += \
-Wl,-Bstatic -lboost_system$(BOOST_SUFFIX) \
-Wl,-Bstatic -lboost_date_time$(BOOST_SUFFIX) \
-Wl,-Bstatic -lboost_filesystem$(BOOST_SUFFIX) \
-Wl,-Bstatic -lboost_program_options$(BOOST_SUFFIX) \
-Wl,-Bstatic -lssl \
-Wl,-Bstatic -lcrypto \
-Wl,-Bstatic -lz \
-Wl,-Bstatic -lwsock32 \
-Wl,-Bstatic -lws2_32 \
-Wl,-Bstatic -lgdi32 \
-Wl,-Bstatic -liphlpapi \
-static-libgcc -static-libstdc++ \
-Wl,-Bstatic -lstdc++ \
-Wl,-Bstatic -lpthread
ifeq ($(USE_WIN32_APP), yes)
CXXFLAGS += -DWIN32_APP
LDFLAGS += -mwindows -s
DAEMON_RC += Win32/Resource.rc
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
else
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
$(WINDRES) -i $< -o $@

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
LDLIBS = -lz -lcrypto -lssl -lboost_system -lboost_date_time -lboost_filesystem -lboost_program_options -lpthread
INCFLAGS = -I/usr/local/include
LDFLAGS = -Wl,-rpath,/usr/local/lib -L/usr/local/lib
ifeq ($(USE_UPNP),1)
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,6 +1,7 @@
#include <string.h>
#include <stdlib.h>
#include <zlib.h>
#include <future>
#include "I2PEndian.h"
#include "Base.h"
#include "Crypto.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;
@@ -19,8 +23,9 @@ namespace i2p
namespace transport
{
NTCPSession::NTCPSession (NTCPServer& server, std::shared_ptr<const i2p::data::RouterInfo> in_RemoteRouter):
TransportSession (in_RemoteRouter), m_Server (server), m_Socket (m_Server.GetService ()),
m_TerminationTimer (m_Server.GetService ()), m_IsEstablished (false), m_IsTerminated (false),
TransportSession (in_RemoteRouter, NTCP_TERMINATION_TIMEOUT),
m_Server (server), m_Socket (m_Server.GetService ()),
m_IsEstablished (false), m_IsTerminated (false),
m_ReceiveBufferOffset (0), m_NextMessage (nullptr), m_IsSending (false)
{
m_Establisher = new Establisher;
@@ -31,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;
@@ -59,6 +64,9 @@ namespace transport
}
memcpy (aesKey, nonZero, 32);
}
m_Decryption.SetKey (aesKey);
m_Encryption.SetKey (aesKey);
}
void NTCPSession::Done ()
@@ -77,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");
}
}
@@ -109,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)
@@ -168,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];
@@ -187,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);
@@ -234,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;
@@ -329,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);
@@ -408,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
@@ -430,7 +450,7 @@ namespace transport
}
else
{
LogPrint (eLogInfo, "NTCP: Server session from ", m_Socket.remote_endpoint (), " connected");
LogPrint (eLogDebug, "NTCP: Server session from ", m_Socket.remote_endpoint (), " connected");
m_Server.AddNTCPSession (shared_from_this ());
Connected ();
@@ -498,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 ();
}
@@ -514,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 ();
}
}
@@ -573,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");
}
@@ -644,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;
@@ -665,7 +708,7 @@ namespace transport
{
m_IsSending = true;
std::vector<boost::asio::const_buffer> bufs;
for (auto it: msgs)
for (const auto& it: msgs)
bufs.push_back (CreateMsgBuffer (it));
boost::asio::async_write (m_Socket, bufs, boost::asio::transfer_all (),
std::bind(&NTCPSession::HandleSent, shared_from_this (), std::placeholders::_1, std::placeholders::_2, msgs));
@@ -684,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())
@@ -691,8 +735,6 @@ namespace transport
Send (m_SendQueue);
m_SendQueue.clear ();
}
else
ScheduleTermination (); // reset termination timer
}
}
@@ -713,35 +755,25 @@ namespace transport
if (m_IsTerminated) return;
if (m_IsSending)
{
for (auto it: msgs)
m_SendQueue.push_back (it);
if (m_SendQueue.size () < NTCP_MAX_OUTGOING_QUEUE_SIZE)
{
for (const auto& it: msgs)
m_SendQueue.push_back (it);
}
else
{
LogPrint (eLogWarning, "NTCP: outgoing messages queue size exceeds ", NTCP_MAX_OUTGOING_QUEUE_SIZE);
Terminate ();
}
}
else
Send (msgs);
}
void NTCPSession::ScheduleTermination ()
{
m_TerminationTimer.cancel ();
m_TerminationTimer.expires_from_now (boost::posix_time::seconds(NTCP_TERMINATION_TIMEOUT));
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 ", NTCP_TERMINATION_TIMEOUT, " 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)
{
}
@@ -758,48 +790,78 @@ namespace transport
m_Thread = new std::thread (std::bind (&NTCPServer::Run, this));
// create acceptors
auto& addresses = context.GetRouterInfo ().GetAddresses ();
for (auto address: addresses)
for (const auto& address: addresses)
{
if (address->transportStyle == i2p::data::RouterInfo::eTransportNTCP && address->host.is_v4 ())
{
m_NTCPAcceptor = new boost::asio::ip::tcp::acceptor (m_Service,
boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), address->port));
LogPrint (eLogInfo, "NTCP: Start listening TCP port ", address->port);
auto conn = std::make_shared<NTCPSession>(*this);
m_NTCPAcceptor->async_accept(conn->GetSocket (), std::bind (&NTCPServer::HandleAccept, this,
conn, std::placeholders::_1));
if (context.SupportsV6 ())
if (!address) continue;
if (address->transportStyle == i2p::data::RouterInfo::eTransportNTCP)
{
if (address->host.is_v4())
{
m_NTCPV6Acceptor = new boost::asio::ip::tcp::acceptor (m_Service);
m_NTCPV6Acceptor->open (boost::asio::ip::tcp::v6());
m_NTCPV6Acceptor->set_option (boost::asio::ip::v6_only (true));
m_NTCPV6Acceptor->bind (boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v6(), address->port));
m_NTCPV6Acceptor->listen ();
LogPrint (eLogInfo, "NTCP: Start listening V6 TCP port ", address->port);
auto conn = std::make_shared<NTCPSession> (*this);
m_NTCPV6Acceptor->async_accept(conn->GetSocket (), std::bind (&NTCPServer::HandleAcceptV6,
this, conn, std::placeholders::_1));
try
{
m_NTCPAcceptor = new boost::asio::ip::tcp::acceptor (m_Service,
boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), address->port));
} catch ( std::exception & ex ) {
/** fail to bind ip4 */
LogPrint(eLogError, "NTCP: Failed to bind to ip4 port ",address->port, ex.what());
continue;
}
LogPrint (eLogInfo, "NTCP: Start listening TCP port ", address->port);
auto conn = std::make_shared<NTCPSession>(*this);
m_NTCPAcceptor->async_accept(conn->GetSocket (), std::bind (&NTCPServer::HandleAccept, this,
conn, std::placeholders::_1));
}
else if (address->host.is_v6() && context.SupportsV6 ())
{
m_NTCPV6Acceptor = new boost::asio::ip::tcp::acceptor (m_Service);
try
{
m_NTCPV6Acceptor->open (boost::asio::ip::tcp::v6());
m_NTCPV6Acceptor->set_option (boost::asio::ip::v6_only (true));
m_NTCPV6Acceptor->bind (boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v6(), address->port));
m_NTCPV6Acceptor->listen ();
LogPrint (eLogInfo, "NTCP: Start listening V6 TCP port ", address->port);
auto conn = std::make_shared<NTCPSession> (*this);
m_NTCPV6Acceptor->async_accept(conn->GetSocket (), std::bind (&NTCPServer::HandleAcceptV6,
this, conn, std::placeholders::_1));
} catch ( std::exception & ex ) {
LogPrint(eLogError, "NTCP: failed to bind to ip6 port ", address->port);
continue;
}
}
}
}
}
}
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;
delete m_NTCPAcceptor;
m_NTCPAcceptor = nullptr;
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)
{
@@ -834,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));
@@ -863,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 ();
}
@@ -900,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 ();
}
@@ -931,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: Connect error: ", 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 ();
@@ -956,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,11 +35,13 @@ 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
class NTCPServer;
class NTCPSession: public TransportSession, public std::enable_shared_from_this<NTCPSession>
@@ -52,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);
@@ -65,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);
@@ -94,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;
@@ -125,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
@@ -144,9 +139,11 @@ namespace transport
void RemoveNTCPSession (std::shared_ptr<NTCPSession> session);
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);
boost::asio::io_service& GetService () { return m_Service; };
void Ban (const boost::asio::ip::address& addr);
bool IsBoundV4() const { return m_NTCPAcceptor != nullptr; };
bool IsBoundV6() const { return m_NTCPV6Acceptor != nullptr; };
boost::asio::io_service& GetService () { return m_Service; };
private:
@@ -154,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:
@@ -162,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:

384
NetDb.cpp
View File

@@ -2,7 +2,7 @@
#include <fstream>
#include <vector>
#include <boost/asio.hpp>
#include <zlib.h>
#include "I2PEndian.h"
#include "Base.h"
#include "Crypto.h"
@@ -14,7 +14,7 @@
#include "RouterContext.h"
#include "Garlic.h"
#include "NetDb.h"
#include "util.h"
#include "Config.h"
using namespace i2p::transport;
@@ -24,7 +24,7 @@ namespace data
{
NetDb netdb;
NetDb::NetDb (): m_IsRunning (false), m_Thread (nullptr), m_Reseeder (nullptr), m_Storage("netDb", "r", "routerInfo-", "dat")
NetDb::NetDb (): m_IsRunning (false), m_Thread (nullptr), m_Reseeder (nullptr), m_Storage("netDb", "r", "routerInfo-", "dat"), m_FloodfillBootstrap(nullptr), m_HiddenMode(false)
{
}
@@ -35,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;
@@ -52,7 +54,7 @@ namespace data
{
if (m_IsRunning)
{
for (auto it: m_RouterInfos)
for (auto& it: m_RouterInfos)
it.second->SaveProfile ();
DeleteObsoleteProfiles ();
m_RouterInfos.clear ();
@@ -67,12 +69,12 @@ namespace data
}
m_LeaseSets.clear();
m_Requests.Stop ();
}
}
}
void NetDb::Run ()
{
uint32_t lastSave = 0, lastPublish = 0, lastExploratory = 0, lastManageRequest = 0;
uint32_t lastSave = 0, lastPublish = 0, lastExploratory = 0, lastManageRequest = 0, lastDestinationCleanup = 0;
while (m_IsRunning)
{
try
@@ -118,11 +120,16 @@ namespace data
{
SaveUpdated ();
ManageLeaseSets ();
ManageLookupResponses ();
}
lastSave = ts;
}
if (ts - lastPublish >= 2400) // publish every 40 minutes
}
if (ts - lastDestinationCleanup >= i2p::garlic::INCOMING_TAGS_EXPIRATION_TIMEOUT)
{
i2p::context.CleanupDestination ();
lastDestinationCleanup = ts;
}
if (ts - lastPublish >= NETDB_PUBLISH_INTERVAL && !m_HiddenMode) // publish
{
Publish ();
lastPublish = ts;
@@ -135,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;
}
}
@@ -161,6 +171,11 @@ namespace data
return false;
}
void NetDb::SetHidden(bool hide) {
// TODO: remove reachable addresses from router info
m_HiddenMode = hide;
}
bool NetDb::AddRouterInfo (const IdentHash& ident, const uint8_t * buf, int len)
{
bool updated = true;
@@ -184,15 +199,24 @@ namespace data
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
@@ -206,6 +230,7 @@ namespace data
bool NetDb::AddLeaseSet (const IdentHash& ident, const uint8_t * buf, int len,
std::shared_ptr<i2p::tunnel::InboundTunnel> from)
{
std::unique_lock<std::mutex> lock(m_LeaseSetsMutex);
bool updated = false;
if (!from) // unsolicited LS must be received directly
{
@@ -217,29 +242,29 @@ namespace data
it->second->Update (buf, len);
if (it->second->IsValid ())
{
LogPrint (eLogInfo, "NetDb: LeaseSet updated: ", ident.ToBase64());
LogPrint (eLogInfo, "NetDb: LeaseSet updated: ", ident.ToBase32());
updated = true;
}
else
{
LogPrint (eLogWarning, "NetDb: LeaseSet update failed: ", ident.ToBase64());
LogPrint (eLogWarning, "NetDb: LeaseSet update failed: ", ident.ToBase32());
m_LeaseSets.erase (it);
}
}
else
LogPrint (eLogDebug, "NetDb: LeaseSet is older: ", ident.ToBase64());
LogPrint (eLogDebug, "NetDb: LeaseSet is older: ", ident.ToBase32());
}
else
{
auto leaseSet = std::make_shared<LeaseSet> (buf, len, false); // we don't need leases in netdb
if (leaseSet->IsValid ())
{
LogPrint (eLogInfo, "NetDb: LeaseSet added: ", ident.ToBase64());
LogPrint (eLogInfo, "NetDb: LeaseSet added: ", ident.ToBase32());
m_LeaseSets[ident] = leaseSet;
updated = true;
}
else
LogPrint (eLogError, "NetDb: new LeaseSet validation failed: ", ident.ToBase64());
LogPrint (eLogError, "NetDb: new LeaseSet validation failed: ", ident.ToBase32());
}
}
return updated;
@@ -257,6 +282,7 @@ namespace data
std::shared_ptr<LeaseSet> NetDb::FindLeaseSet (const IdentHash& destination) const
{
std::unique_lock<std::mutex> lock(m_LeaseSetsMutex);
auto it = m_LeaseSets.find (destination);
if (it != m_LeaseSets.end ())
return it->second;
@@ -284,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)
@@ -311,6 +382,74 @@ namespace data
return true;
}
void NetDb::VisitLeaseSets(LeaseSetVisitor v)
{
std::unique_lock<std::mutex> lock(m_LeaseSetsMutex);
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 ()
{
// make sure we cleanup netDb from previous attempts
@@ -320,7 +459,7 @@ namespace data
m_LastLoad = i2p::util::GetSecondsSinceEpoch();
std::vector<std::string> files;
m_Storage.Traverse(files);
for (auto path : files)
for (const auto& path : files)
LoadRouterInfo(path);
LogPrint (eLogInfo, "NetDb: ", m_RouterInfos.size(), " routers loaded (", m_Floodfills.size (), " floodfils)");
@@ -338,7 +477,7 @@ namespace data
expirationTimeout = i2p::context.IsFloodfill () ? NETDB_FLOODFILL_EXPIRATION_TIMEOUT*1000LL :
NETDB_MIN_EXPIRATION_TIMEOUT*1000LL + (NETDB_MAX_EXPIRATION_TIMEOUT - NETDB_MIN_EXPIRATION_TIMEOUT)*1000LL*NETDB_MIN_ROUTERS/total;
for (auto it: m_RouterInfos)
for (auto& it: m_RouterInfos)
{
std::string ident = it.second->GetIdentHashBase64();
std::string path = m_Storage.Path(ident);
@@ -386,7 +525,7 @@ namespace data
it = m_RouterInfos.erase (it);
continue;
}
it++;
++it;
}
}
// clean up expired floodfiils
@@ -396,7 +535,7 @@ namespace data
if ((*it)->IsUnreachable ())
it = m_Floodfills.erase (it);
else
it++;
++it;
}
}
}
@@ -419,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)
{
@@ -427,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);
@@ -446,16 +600,22 @@ 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 (eLogDebug, "NetDb: database store with own RouterInfo received, dropped");
return;
}
size_t payloadOffset = offset;
bool updated = false;
if (buf[DATABASE_STORE_TYPE_OFFSET]) // type
{
LogPrint (eLogDebug, "NetDb: store request: LeaseSet");
LogPrint (eLogDebug, "NetDb: store request: LeaseSet for ", ident.ToBase32());
updated = AddLeaseSet (ident, buf + offset, len - offset, m->from);
}
else
@@ -470,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)
@@ -481,25 +646,32 @@ namespace data
uint8_t * payload = floodMsg->GetPayload ();
memcpy (payload, buf, 33); // key + type
htobe32buf (payload + DATABASE_STORE_REPLY_TOKEN_OFFSET, 0); // zero reply token
auto msgLen = len - payloadOffset;
size_t msgLen = len - payloadOffset;
floodMsg->len += DATABASE_STORE_HEADER_SIZE + msgLen;
if (floodMsg->len < floodMsg->maxLen)
{
memcpy (payload + DATABASE_STORE_HEADER_SIZE, buf + payloadOffset, msgLen);
floodMsg->FillI2NPMessageHeader (eI2NPDatabaseStore);
std::set<IdentHash> excluded;
for (int i = 0; i < 3; i++)
excluded.insert (i2p::context.GetIdentHash ()); // don't flood to itself
excluded.insert (ident); // don't flood back
for (int i = 0; i < 3; i++)
{
auto floodfill = GetClosestFloodfill (ident, excluded);
if (floodfill)
transports.SendMessage (floodfill->GetIdentHash (), floodMsg);
{
auto h = floodfill->GetIdentHash();
LogPrint(eLogDebug, "NetDb: Flood lease set for ", ident.ToBase32(), " to ", h.ToBase64());
transports.SendMessage (h, CopyI2NPMessage(floodMsg));
excluded.insert (h);
}
else
break;
}
}
else
LogPrint (eLogError, "Database store message is too long ", floodMsg->len);
}
LogPrint (eLogError, "NetDb: Database store message is too long ", floodMsg->len);
}
}
void NetDb::HandleDatabaseSearchReplyMsg (std::shared_ptr<const I2NPMessage> msg)
@@ -523,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 ();
@@ -567,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
@@ -584,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: [:|||:]");
@@ -603,14 +778,18 @@ namespace data
char key[48];
int l = i2p::data::ByteStreamToBase64 (buf, 32, key, 48);
key[l] = 0;
IdentHash replyIdent(buf + 32);
uint8_t flag = buf[64];
LogPrint (eLogDebug, "NetDb: DatabaseLookup for ", key, " recieved flags=", (int)flag);
uint8_t lookupType = flag & DATABASE_LOOKUP_TYPE_FLAGS_MASK;
const uint8_t * excluded = buf + 65;
uint32_t replyTunnelID = 0;
if (flag & DATABASE_LOOKUP_DELIVERY_FLAG) //reply to tunnel
{
replyTunnelID = bufbe32toh (buf + 64);
replyTunnelID = bufbe32toh (excluded);
excluded += 4;
}
uint16_t numExcluded = bufbe16toh (excluded);
@@ -618,7 +797,7 @@ namespace data
if (numExcluded > 512)
{
LogPrint (eLogWarning, "NetDb: number of excluded peers", numExcluded, " exceeds 512");
numExcluded = 0; // TODO:
return;
}
std::shared_ptr<I2NPMessage> replyMsg;
@@ -662,7 +841,12 @@ namespace data
lookupType == DATABASE_LOOKUP_TYPE_NORMAL_LOOKUP))
{
auto leaseSet = FindLeaseSet (ident);
if (leaseSet && !leaseSet->IsExpired ()) // we don't send back our LeaseSets
if (!leaseSet)
{
// no lease set found
LogPrint(eLogDebug, "NetDb: requested LeaseSet not found for ", ident.ToBase32());
}
else if (!leaseSet->IsExpired ()) // we don't send back our LeaseSets
{
LogPrint (eLogDebug, "NetDb: requested LeaseSet ", key, " found");
replyMsg = CreateDatabaseStoreMsg (leaseSet);
@@ -670,61 +854,49 @@ namespace data
}
if (!replyMsg)
{
LogPrint (eLogWarning, "NetDb: Requested ", key, " not found. ", numExcluded, " 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;
for (int i = 0; i < numExcluded; i++)
{
excludedRouters.insert (excluded);
excluded += 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);
}
}
}
excluded += numExcluded * 32;
if (replyMsg)
{
if (replyTunnelID)
{
// encryption might be used though tunnel only
if (flag & DATABASE_LOOKUP_ENCYPTION_FLAG) // encrypted reply requested
if (flag & DATABASE_LOOKUP_ENCRYPTION_FLAG) // encrypted reply requested
{
const uint8_t * sessionKey = excluded;
uint8_t numTags = sessionKey[32];
if (numTags > 0)
const uint8_t numTags = excluded[32];
if (numTags)
{
const uint8_t * sessionTag = sessionKey + 33; // take first tag
const i2p::garlic::SessionTag sessionTag(excluded + 33); // take first tag
i2p::garlic::GarlicRoutingSession garlic (sessionKey, sessionTag);
replyMsg = garlic.WrapSingleMessage (replyMsg);
if(replyMsg == nullptr) LogPrint(eLogError, "NetDb: failed to wrap message");
}
else
LogPrint(eLogWarning, "NetDb: encrypted reply requested but no tags provided");
}
auto exploratoryPool = i2p::tunnel::tunnels.GetExploratoryPool ();
auto outbound = exploratoryPool ? exploratoryPool->GetNextOutboundTunnel () : nullptr;
if (outbound)
outbound->SendTunnelDataMsg (buf+32, replyTunnelID, replyMsg);
outbound->SendTunnelDataMsg (replyIdent, replyTunnelID, replyMsg);
else
transports.SendMessage (buf+32, i2p::CreateTunnelGatewayMsg (replyTunnelID, replyMsg));
transports.SendMessage (replyIdent, i2p::CreateTunnelGatewayMsg (replyTunnelID, replyMsg));
}
else
transports.SendMessage (buf+32, replyMsg);
transports.SendMessage (replyIdent, replyMsg);
}
}
@@ -738,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++)
{
@@ -750,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)
@@ -817,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);
});
}
@@ -851,12 +1021,12 @@ namespace data
{
if (m_RouterInfos.empty())
return 0;
uint32_t ind = rand () % m_RouterInfos.size ();
uint32_t ind = rand () % m_RouterInfos.size ();
for (int j = 0; j < 2; j++)
{
uint32_t i = 0;
std::unique_lock<std::mutex> l(m_RouterInfosMutex);
for (auto it: m_RouterInfos)
for (const auto& it: m_RouterInfos)
{
if (i >= ind)
{
@@ -888,7 +1058,7 @@ namespace data
else
minMetric.SetMax ();
std::unique_lock<std::mutex> l(m_FloodfillsMutex);
for (auto it: m_Floodfills)
for (const auto& it: m_Floodfills)
{
if (!it->IsUnreachable ())
{
@@ -919,7 +1089,7 @@ namespace data
if (closeThanUsOnly) ourMetric = destKey ^ i2p::context.GetIdentHash ();
{
std::unique_lock<std::mutex> l(m_FloodfillsMutex);
for (auto it: m_Floodfills)
for (const auto& it: m_Floodfills)
{
if (!it->IsUnreachable ())
{
@@ -938,11 +1108,11 @@ namespace data
std::vector<IdentHash> res;
size_t i = 0;
for (auto it: sorted)
for (const auto& it: sorted)
{
if (i < num)
{
auto& ident = it.r->GetIdentHash ();
const auto& ident = it.r->GetIdentHash ();
if (!excluded.count (ident))
{
res.push_back (ident);
@@ -955,6 +1125,14 @@ namespace data
return res;
}
std::shared_ptr<const RouterInfo> NetDb::GetRandomRouterInFamily(const std::string & fam) const {
return GetRandomRouter(
[fam](std::shared_ptr<const RouterInfo> router)->bool
{
return router->IsFamily(fam);
});
}
std::shared_ptr<const RouterInfo> NetDb::GetClosestNonFloodfill (const IdentHash& destination,
const std::set<IdentHash>& excluded) const
{
@@ -963,7 +1141,7 @@ namespace data
IdentHash destKey = CreateRoutingKey (destination);
minMetric.SetMax ();
// must be called from NetDb thread only
for (auto it: m_RouterInfos)
for (const auto& it: m_RouterInfos)
{
if (!it.second->IsFloodfill ())
{
@@ -989,19 +1167,7 @@ namespace data
it = m_LeaseSets.erase (it);
}
else
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++;
++it;
}
}
}

53
NetDb.h
View File

@@ -8,7 +8,9 @@
#include <string>
#include <thread>
#include <mutex>
#include "Base.h"
#include "Gzip.h"
#include "FS.h"
#include "Queue.h"
#include "I2NPProtocol.h"
@@ -29,7 +31,17 @@ namespace data
const int NETDB_INTRODUCEE_EXPIRATION_TIMEOUT = 65*60;
const int NETDB_MIN_EXPIRATION_TIMEOUT = 90*60; // 1.5 hours
const int NETDB_MAX_EXPIRATION_TIMEOUT = 27*60*60; // 27 hours
const int NETDB_PUBLISH_INTERVAL = 60*40;
/** 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:
@@ -39,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);
@@ -48,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);
@@ -56,16 +69,20 @@ 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,
std::set<IdentHash>& excluded, bool closeThanUsOnly = false) const;
std::shared_ptr<const RouterInfo> GetClosestNonFloodfill (const IdentHash& destination, const std::set<IdentHash>& excluded) const;
std::shared_ptr<const RouterInfo> GetRandomRouterInFamily(const std::string & fam) const;
void SetUnreachable (const IdentHash& ident, bool unreachable);
void PostI2NPMsg (std::shared_ptr<const I2NPMessage> msg);
/** set hidden mode, aka don't publish our RI to netdb and don't explore */
void SetHidden(bool hide);
void Reseed ();
Families& GetFamilies () { return m_Families; };
@@ -73,7 +90,18 @@ namespace data
int GetNumRouters () const { return m_RouterInfos.size (); };
int GetNumFloodfills () const { return m_Floodfills.size (); };
int GetNumLeaseSets () const { return m_LeaseSets.size (); };
/** 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 ();
@@ -84,13 +112,15 @@ namespace data
void Publish ();
void ManageLeaseSets ();
void ManageRequests ();
void ManageLookupResponses ();
template<typename Filter>
std::shared_ptr<const RouterInfo> GetRandomRouter (Filter filter) const;
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;
std::map<IdentHash, std::shared_ptr<RouterInfo> > m_RouterInfos;
@@ -110,7 +140,12 @@ 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;
};
extern NetDb netdb;

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;
@@ -141,7 +153,7 @@ namespace data
if (done)
it = m_RequestedDestinations.erase (it);
else
it++;
++it;
}
}
}

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;
}
@@ -167,7 +180,7 @@ namespace data
std::vector<std::string> files;
m_ProfilesStorage.Traverse(files);
for (auto path: files) {
for (const auto& path: files) {
if (stat(path.c_str(), &st) != 0) {
LogPrint(eLogWarning, "Profiling: Can't stat(): ", path);
continue;

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

@@ -7,6 +7,7 @@
#include <thread>
#include <condition_variable>
#include <functional>
#include <utility>
namespace i2p
{
@@ -20,16 +21,17 @@ namespace util
void Put (Element e)
{
std::unique_lock<std::mutex> l(m_QueueMutex);
m_Queue.push (e);
m_Queue.push (std::move(e));
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 ())
{
std::unique_lock<std::mutex> l(m_QueueMutex);
for (auto it: vec)
for (const auto& it: vec)
m_Queue.push (it);
m_NonEmpty.notify_one ();
}

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
----------
@@ -31,18 +38,20 @@ i2pd from source on your OS.
* Windows - [![Build status](https://ci.appveyor.com/api/projects/status/1908qe4p48ff1x23?svg=true)](https://ci.appveyor.com/project/PurpleI2P/i2pd)
* Mac OS X
* FreeBSD
* Android *(coming soon)*
* 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
---------
BTC: 1K7Ds6KUeR8ya287UC4rYTjvC96vXyZbDY
DASH: Xw8YUrQpYzP9tZBmbjqxS3M97Q7v3vJKUF
LTC: LKQirrYrDeTuAPnpYq5y7LVKtywfkkHi59
ANC: AQJYweYYUqM1nVfLqfoSMpUMfzxvS4Xd7z
DOGE: DNXLQKziRPAsD9H3DFNjk4fLQrdaSX893Y

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>
@@ -14,26 +15,15 @@
#include "Log.h"
#include "Identity.h"
#include "NetDb.h"
#include "HTTP.h"
#include "util.h"
#include "Config.h"
namespace i2p
{
namespace data
{
static std::vector<std::string> httpsReseedHostList =
{
"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
};
Reseeder::Reseeder()
{
}
@@ -42,17 +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 ()
{
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)
{
@@ -78,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];
@@ -130,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 ())
@@ -292,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;
}
@@ -317,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)
{
@@ -329,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);
}
@@ -366,13 +489,19 @@ namespace data
std::string Reseeder::HttpsRequest (const std::string& address)
{
i2p::util::http::url u(address);
if (u.port_ == 80) u.port_ = 443;
i2p::http::URL url;
if (!url.parse(address)) {
LogPrint(eLogError, "Reseed: failed to parse url: ", address);
return "";
}
url.schema = "https";
if (!url.port)
url.port = 443;
boost::asio::io_service service;
boost::system::error_code ecode;
auto it = boost::asio::ip::tcp::resolver(service).resolve (
boost::asio::ip::tcp::resolver::query (u.host_, std::to_string (u.port_)), ecode);
auto it = boost::asio::ip::tcp::resolver(service).resolve (
boost::asio::ip::tcp::resolver::query (url.host, std::to_string(url.port)), ecode);
if (!ecode)
{
boost::asio::ssl::context ctx(service, boost::asio::ssl::context::sslv23);
@@ -381,35 +510,56 @@ 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 (eLogInfo, "Reseed: Connected to ", u.host_, ":", u.port_);
// send request
std::stringstream ss;
ss << "GET " << u.path_ << " HTTP/1.1\r\nHost: " << u.host_
<< "\r\nAccept: */*\r\n" << "User-Agent: Wget/1.11.4\r\n" << "Connection: close\r\n\r\n";
s.write_some (boost::asio::buffer (ss.str ()));
LogPrint (eLogDebug, "Reseed: Connected to ", url.host, ":", url.port);
i2p::http::HTTPReq req;
req.uri = url.to_string();
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;
char response[1024]; size_t l = 0;
do
{
l = s.read_some (boost::asio::buffer (response, 1024), ecode);
if (l) rs.write (response, l);
}
while (!ecode && l);
char recv_buf[1024]; size_t l = 0;
do {
l = s.read_some (boost::asio::buffer (recv_buf, sizeof(recv_buf)), ecode);
if (l) rs.write (recv_buf, l);
} while (!ecode && l);
// process response
return i2p::util::http::GetHttpContent (rs);
std::string data = rs.str();
i2p::http::HTTPRes res;
int len = res.parse(data);
if (len <= 0) {
LogPrint(eLogWarning, "Reseed: incomplete/broken response from ", url.host);
return "";
}
if (res.code != 200) {
LogPrint(eLogError, "Reseed: failed to reseed from ", url.host, ", http code ", res.code);
return "";
}
data.erase(0, len); /* drop http headers from response */
LogPrint(eLogDebug, "Reseed: got ", data.length(), " bytes of data from ", url.host);
if (res.is_chunked()) {
std::stringstream in(data), out;
if (!i2p::http::MergeChunkedResponse(in, out)) {
LogPrint(eLogWarning, "Reseed: failed to merge chunked response from ", url.host);
return "";
}
LogPrint(eLogDebug, "Reseed: got ", data.length(), "(", out.tellg(), ") bytes of data from ", url.host);
data = out.str();
}
return data;
}
else
LogPrint (eLogError, "Reseed: SSL handshake failed: ", ecode.message ());
}
else
LogPrint (eLogError, "Reseed: Couldn't connect to ", u.host_, ": ", ecode.message ());
LogPrint (eLogError, "Reseed: Couldn't connect to ", url.host, ": ", ecode.message ());
}
else
LogPrint (eLogError, "Reseed: Couldn't resolve address ", u.host_, ": ", ecode.message ());
LogPrint (eLogError, "Reseed: Couldn't resolve address ", url.host, ": ", ecode.message ());
return "";
}
}

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)
{
}
@@ -49,14 +49,45 @@ namespace i2p
uint16_t port; i2p::config::GetOption("port", port);
if (!port)
port = rand () % (30777 - 9111) + 9111; // I2P network ports range
std::string host; i2p::config::GetOption("host", host);
if (i2p::config::IsDefault("host"))
host = "127.0.0.1"; // replace default address with safe value
routerInfo.AddSSUAddress (host.c_str(), port, routerInfo.GetIdentHash ());
routerInfo.AddNTCPAddress (host.c_str(), port);
bool ipv4; i2p::config::GetOption("ipv4", ipv4);
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";
if (!i2p::config::IsDefault("host"))
i2p::config::GetOption("host", host);
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);
}
if (ipv6)
{
std::string host = "::";
if (!i2p::config::IsDefault("host") && !ipv4) // override if v6 only
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 ());
@@ -75,6 +106,7 @@ namespace i2p
if (status != m_Status)
{
m_Status = status;
m_Error = eRouterErrorNone;
switch (m_Status)
{
case eRouterStatusOK:
@@ -92,7 +124,7 @@ namespace i2p
void RouterContext::UpdatePort (int port)
{
bool updated = false;
for (auto address : m_RouterInfo.GetAddresses ())
for (auto& address : m_RouterInfo.GetAddresses ())
{
if (address->port != port)
{
@@ -107,7 +139,7 @@ namespace i2p
void RouterContext::UpdateAddress (const boost::asio::ip::address& host)
{
bool updated = false;
for (auto address : m_RouterInfo.GetAddresses ())
for (auto& address : m_RouterInfo.GetAddresses ())
{
if (address->host != host && address->IsCompatible (host))
{
@@ -173,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)
{
@@ -183,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;
}
@@ -194,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 ();
@@ -221,20 +254,27 @@ 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 (size_t i = 0; i < addresses.size (); i++)
for (auto it = addresses.begin (); it != addresses.end (); ++it)
{
if (addresses[i]->transportStyle == i2p::data::RouterInfo::eTransportNTCP)
if ((*it)->transportStyle == i2p::data::RouterInfo::eTransportNTCP &&
(*it)->host.is_v4 ())
{
addresses.erase (addresses.begin () + i);
addresses.erase (it);
break;
}
}
// delete previous introducers
for (auto addr : addresses)
addr->introducers.clear ();
for (auto& addr : addresses)
if (addr->ssu)
addr->ssu->introducers.clear ();
// update
UpdateRouterInfo ();
@@ -253,18 +293,20 @@ namespace i2p
// insert NTCP back
auto& addresses = m_RouterInfo.GetAddresses ();
for (size_t i = 0; i < addresses.size (); i++)
for (const auto& addr : addresses)
{
if (addresses[i]->transportStyle == i2p::data::RouterInfo::eTransportSSU)
if (addr->transportStyle == i2p::data::RouterInfo::eTransportSSU &&
addr->host.is_v4 ())
{
// insert NTCP address with host/port from SSU
m_RouterInfo.AddNTCPAddress (addresses[i]->host.to_string ().c_str (), addresses[i]->port);
m_RouterInfo.AddNTCPAddress (addr->host.to_string ().c_str (), addr->port);
break;
}
}
// delete previous introducers
for (auto addr : addresses)
addr->introducers.clear ();
for (auto& addr : addresses)
if (addr->ssu)
addr->ssu->introducers.clear ();
// update
UpdateRouterInfo ();
@@ -294,7 +336,7 @@ namespace i2p
bool updated = false, found = false;
int port = 0;
auto& addresses = m_RouterInfo.GetAddresses ();
for (auto addr: addresses)
for (auto& addr: addresses)
{
if (addr->host.is_v6 () && addr->transportStyle == i2p::data::RouterInfo::eTransportNTCP)
{
@@ -333,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 ();
}
}
@@ -417,6 +459,12 @@ namespace i2p
std::unique_lock<std::mutex> l(m_GarlicMutex);
i2p::garlic::GarlicDestination::ProcessDeliveryStatusMessage (msg);
}
void RouterContext::CleanupDestination ()
{
std::unique_lock<std::mutex> l(m_GarlicMutex);
i2p::garlic::GarlicDestination::CleanupExpiredTags ();
}
uint32_t RouterContext::GetUptime () const
{

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
@@ -71,7 +82,8 @@ namespace i2p
void SetSupportsV4 (bool supportsV4);
void UpdateNTCPV6Address (const boost::asio::ip::address& host); // called from NTCP session
void UpdateStats ();
void UpdateStats ();
void CleanupDestination (); // garlic destination
// implements LocalDestination
std::shared_ptr<const i2p::data::IdentityEx> GetIdentity () const { return m_Keys.GetPublic (); };
@@ -106,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,22 +3,33 @@
#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
{
namespace data
{
RouterInfo::RouterInfo (): m_Buffer (nullptr)
{
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 = boost::make_shared<Addresses>(); // create empty list
m_Buffer = new uint8_t[MAX_RI_BUFFER_SIZE];
ReadFromFile ();
}
@@ -26,6 +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 = boost::make_shared<Addresses>(); // create empty list
m_Buffer = new uint8_t[MAX_RI_BUFFER_SIZE];
memcpy (m_Buffer, buf, len);
m_BufferLen = len;
@@ -48,7 +60,7 @@ namespace data
m_IsUnreachable = false;
m_SupportedTransports = 0;
m_Caps = 0;
m_Addresses.clear ();
// don't clean up m_Addresses, it will be replaced in ReadFromStream
m_Properties.clear ();
// copy buffer
if (!m_Buffer)
@@ -118,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
@@ -134,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)
@@ -144,6 +159,7 @@ namespace data
s.read ((char *)&m_Timestamp, sizeof (m_Timestamp));
m_Timestamp = be64toh (m_Timestamp);
// read addresses
auto addresses = boost::make_shared<Addresses>();
uint8_t numAddresses;
s.read ((char *)&numAddresses, sizeof (numAddresses)); if (!s) return;
bool introducers = false;
@@ -151,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')
@@ -215,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;
@@ -234,10 +269,15 @@ namespace data
}
if (isValidAddress)
{
m_Addresses.push_back(std::make_shared<Address>(address));
addresses->push_back(address);
m_SupportedTransports |= supportedTransports;
}
}
#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;
@@ -248,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
@@ -288,9 +323,13 @@ namespace data
if (!s) return;
}
if (!m_SupportedTransports || !m_Addresses.size() || (UsesIntroducer () && !introducers))
if (!m_SupportedTransports || !m_Addresses->size() || (UsesIntroducer () && !introducers))
SetUnreachable (true);
}
}
bool RouterInfo::IsFamily(const std::string & fam) const {
return m_Family == fam;
}
void RouterInfo::ExtractCaps (const char * value)
{
@@ -335,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
@@ -356,19 +399,19 @@ namespace data
SetProperty ("caps", caps);
}
void RouterInfo::WriteToStream (std::ostream& s)
void RouterInfo::WriteToStream (std::ostream& s) const
{
uint64_t ts = htobe64 (m_Timestamp);
s.write ((char *)&ts, sizeof (ts));
s.write ((const char *)&ts, sizeof (ts));
// addresses
uint8_t numAddresses = m_Addresses.size ();
uint8_t numAddresses = m_Addresses->size ();
s.write ((char *)&numAddresses, sizeof (numAddresses));
for (auto addr : m_Addresses)
for (const auto& addr_ptr : *m_Addresses)
{
Address& address = *addr;
s.write ((char *)&address.cost, sizeof (address.cost));
s.write ((char *)&address.date, sizeof (address.date));
const Address& address = *addr_ptr;
s.write ((const char *)&address.cost, sizeof (address.cost));
s.write ((const char *)&address.date, sizeof (address.date));
std::stringstream properties;
if (address.transportStyle == eTransportNTCP)
WriteString ("NTCP", s);
@@ -394,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 (auto introducer: address.introducers)
for (const auto& introducer: address.ssu->introducers)
{
WriteString ("ihost" + boost::lexical_cast<std::string>(i), properties);
properties << '=';
@@ -406,7 +449,7 @@ namespace data
i++;
}
i = 0;
for (auto introducer: address.introducers)
for (const auto& introducer: address.ssu->introducers)
{
WriteString ("ikey" + boost::lexical_cast<std::string>(i), properties);
properties << '=';
@@ -418,7 +461,7 @@ namespace data
i++;
}
i = 0;
for (auto introducer: address.introducers)
for (const auto& introducer: address.ssu->introducers)
{
WriteString ("iport" + boost::lexical_cast<std::string>(i), properties);
properties << '=';
@@ -427,7 +470,7 @@ namespace data
i++;
}
i = 0;
for (auto introducer: address.introducers)
for (const auto& introducer: address.ssu->introducers)
{
WriteString ("itag" + boost::lexical_cast<std::string>(i), properties);
properties << '=';
@@ -440,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 << ';';
}
}
@@ -469,7 +512,7 @@ namespace data
// properties
std::stringstream properties;
for (auto& p : m_Properties)
for (const auto& p : m_Properties)
{
WriteString (p.first, properties);
properties << '=';
@@ -532,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);
@@ -556,11 +609,10 @@ namespace data
addr->transportStyle = eTransportNTCP;
addr->cost = 2;
addr->date = 0;
addr->mtu = 0;
for (auto it: m_Addresses) // don't insert same address twice
for (const auto& it: *m_Addresses) // don't insert same address twice
if (*it == *addr) return;
m_Addresses.push_back(addr);
m_SupportedTransports |= addr->host.is_v6 () ? eNTCPV6 : eNTCPV4;
m_Addresses->push_back(std::move(addr));
}
void RouterInfo::AddSSUAddress (const char * host, int port, const uint8_t * key, int mtu)
@@ -571,25 +623,27 @@ 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);
for (auto it: m_Addresses) // don't insert same address twice
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_Addresses.push_back(addr);
m_SupportedTransports |= addr->host.is_v6 () ? eSSUV6 : eSSUV4;
m_Caps |= eSSUTesting;
m_Caps |= eSSUIntroducer;
m_Addresses->push_back(std::move(addr));
m_Caps |= eSSUTesting;
m_Caps |= eSSUIntroducer;
}
bool RouterInfo::AddIntroducer (const Introducer& introducer)
{
for (auto addr : m_Addresses)
for (auto& addr : *m_Addresses)
{
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;
}
}
@@ -598,16 +652,16 @@ namespace data
bool RouterInfo::RemoveIntroducer (const boost::asio::ip::udp::endpoint& e)
{
for (auto addr: m_Addresses)
for (auto& addr: *m_Addresses)
{
if (addr->transportStyle == eTransportSSU && addr->host.is_v4 ())
{
for (std::vector<Introducer>::iterator 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;
}
}
}
}
return false;
@@ -687,28 +741,14 @@ namespace data
{
if (IsV6 ())
{
// NTCP
m_SupportedTransports &= ~eNTCPV6;
for (size_t i = 0; i < m_Addresses.size (); i++)
m_SupportedTransports &= ~(eNTCPV6 | eSSUV6);
for (auto it = m_Addresses->begin (); it != m_Addresses->end ();)
{
if (m_Addresses[i]->transportStyle == i2p::data::RouterInfo::eTransportNTCP &&
m_Addresses[i]->host.is_v6 ())
{
m_Addresses.erase (m_Addresses.begin () + i);
break;
}
}
// SSU
m_SupportedTransports &= ~eSSUV6;
for (size_t i = 0; i < m_Addresses.size (); i++)
{
if (m_Addresses[i]->transportStyle == i2p::data::RouterInfo::eTransportSSU &&
m_Addresses[i]->host.is_v6 ())
{
m_Addresses.erase (m_Addresses.begin () + i);
break;
}
auto addr = *it;
if (addr->host.is_v6 ())
it = m_Addresses->erase (it);
else
++it;
}
}
}
@@ -717,28 +757,14 @@ namespace data
{
if (IsV4 ())
{
// NTCP
m_SupportedTransports &= ~eNTCPV4;
for (size_t i = 0; i < m_Addresses.size (); i++)
m_SupportedTransports &= ~(eNTCPV4 | eSSUV4);
for (auto it = m_Addresses->begin (); it != m_Addresses->end ();)
{
if (m_Addresses[i]->transportStyle == i2p::data::RouterInfo::eTransportNTCP &&
m_Addresses[i]->host.is_v4 ())
{
m_Addresses.erase (m_Addresses.begin () + i);
break;
}
}
// SSU
m_SupportedTransports &= ~eSSUV4;
for (size_t i = 0; i < m_Addresses.size (); i++)
{
if (m_Addresses[i]->transportStyle == i2p::data::RouterInfo::eTransportSSU &&
m_Addresses[i]->host.is_v4 ())
{
m_Addresses.erase (m_Addresses.begin () + i);
break;
}
auto addr = *it;
if (addr->host.is_v4 ())
it = m_Addresses->erase (it);
else
++it;
}
}
}
@@ -766,7 +792,12 @@ namespace data
std::shared_ptr<const RouterInfo::Address> RouterInfo::GetAddress (TransportStyle s, bool v4only, bool v6only) const
{
for (auto address : m_Addresses)
#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

@@ -5,8 +5,10 @@
#include <string>
#include <map>
#include <vector>
#include <list>
#include <iostream>
#include <boost/asio.hpp>
#include <boost/shared_ptr.hpp>
#include "Identity.h"
#include "Profiling.h"
@@ -77,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
{
@@ -105,9 +112,10 @@ namespace data
return !(*this == other);
}
};
typedef std::list<std::shared_ptr<Address> > Addresses;
RouterInfo ();
RouterInfo (const std::string& fullPath);
RouterInfo (): m_Buffer (nullptr) { };
RouterInfo (const RouterInfo& ) = default;
RouterInfo& operator=(const RouterInfo& ) = default;
RouterInfo (const uint8_t * buf, int len);
@@ -117,7 +125,7 @@ namespace data
void SetRouterIdentity (std::shared_ptr<const IdentityEx> identity);
std::string GetIdentHashBase64 () const { return GetIdentHash ().ToBase64 (); };
uint64_t GetTimestamp () const { return m_Timestamp; };
std::vector<std::shared_ptr<Address> >& GetAddresses () { return m_Addresses; };
Addresses& GetAddresses () { return *m_Addresses; }; // should be called for local RI only, otherwise must return shared_ptr
std::shared_ptr<const Address> GetNTCPAddress (bool v4only = true) const;
std::shared_ptr<const Address> GetSSUAddress (bool v4only = true) const;
std::shared_ptr<const Address> GetSSUV6Address () const;
@@ -165,12 +173,15 @@ 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; };
bool IsNewer (const uint8_t * buf, size_t len) const;
/** return true if we are in a router family and the signature is valid */
bool IsFamily(const std::string & fam) const;
// implements RoutingDestination
const IdentHash& GetIdentHash () const { return m_RouterIdentity->GetIdentHash (); };
const uint8_t * GetEncryptionPublicKey () const { return m_RouterIdentity->GetStandardIdentity ().publicKey; };
@@ -182,9 +193,9 @@ namespace data
void ReadFromFile ();
void ReadFromStream (std::istream& s);
void ReadFromBuffer (bool verifySignature);
void WriteToStream (std::ostream& s);
size_t ReadString (char * str, std::istream& s);
void WriteString (const std::string& str, std::ostream& s);
void WriteToStream (std::ostream& s) const;
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 ();
@@ -196,7 +207,7 @@ namespace data
uint8_t * m_Buffer;
size_t m_BufferLen;
uint64_t m_Timestamp;
std::vector<std::shared_ptr<Address> > 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;

397
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,16 +47,17 @@ 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 ());
m_Session->localDestination->StopAcceptingStreams ();
if (m_Session->localDestination)
m_Session->localDestination->StopAcceptingStreams ();
}
break;
}
@@ -70,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));
}
@@ -84,7 +85,7 @@ namespace client
Terminate ();
}
else
{
{
m_Buffer[bytes_transferred] = 0;
char * eol = (char *)memchr (m_Buffer, '\n', bytes_transferred);
if (eol)
@@ -93,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;
}
@@ -107,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);
}
@@ -144,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)
@@ -178,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)
@@ -204,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;
@@ -235,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 ();
}
@@ -251,8 +252,9 @@ namespace client
Terminate ();
}
}
else
{
{
LogPrint (eLogWarning, "SAM: incomplete message ", bytes_transferred);
m_BufferOffset = bytes_transferred;
// try to receive remaining message
@@ -266,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
@@ -277,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));
}
@@ -295,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
@@ -313,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));
}
}
}
}
@@ -326,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);
@@ -340,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)
@@ -364,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)
@@ -373,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);
}
@@ -395,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);
}
@@ -419,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)
{
@@ -436,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);
@@ -467,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);
@@ -528,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, ' ');
@@ -539,11 +572,11 @@ namespace client
*value = 0;
value++;
params[buf] = value;
}
}
buf = separator + 1;
}
while (separator);
}
}
void SAMSocket::Receive ()
{
@@ -553,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));
}
@@ -569,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)
@@ -577,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)
{
@@ -598,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
{
@@ -624,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
@@ -637,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();
@@ -685,7 +780,7 @@ namespace client
{
{
std::lock_guard<std::mutex> lock(m_SocketsMutex);
for (auto sock : m_Sockets) {
for (auto& sock : m_Sockets) {
sock->CloseStream();
}
}
@@ -704,7 +799,7 @@ namespace client
{
if (m_IsRunning)
Stop ();
}
}
void SAMBridge::Start ()
{
@@ -718,31 +813,31 @@ namespace client
{
m_IsRunning = false;
m_Acceptor.cancel ();
for (auto it: m_Sessions)
for (auto& it: m_Sessions)
it.second->CloseStreams ();
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 ()
@@ -759,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 ();
}
@@ -773,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;
@@ -791,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)
{
@@ -815,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 ();
}
@@ -836,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)
@@ -851,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)
@@ -863,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");
}

65
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 */
@@ -154,12 +159,12 @@ namespace client
std::list<std::shared_ptr<SAMSocket> > l;
{
std::lock_guard<std::mutex> lock(m_SocketsMutex);
for( auto & sock : m_Sockets ) l.push_back(sock);
for(const auto& sock : m_Sockets ) l.push_back(sock);
}
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
{
@@ -26,7 +27,7 @@ namespace proxy
{
uint8_t size;
char value[max_socks_hostname_size];
void FromString (std::string str)
void FromString (const std::string& str)
{
size = str.length();
if (str.length() > max_socks_hostname_size) size = max_socks_hostname_size;
@@ -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));
}

314
SSU.cpp
View File

@@ -10,38 +10,72 @@ namespace i2p
{
namespace transport
{
SSUServer::SSUServer (int port): m_Thread (nullptr), m_ThreadV6 (nullptr), m_ReceiversThread (nullptr),
m_Work (m_Service), m_WorkV6 (m_ServiceV6), m_ReceiversWork (m_ReceiversService),
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)
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_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_Socket.set_option (boost::asio::socket_base::receive_buffer_size (65535));
m_Socket.set_option (boost::asio::socket_base::send_buffer_size (65535));
OpenSocketV6 ();
}
SSUServer::SSUServer (int port):
m_OnlyV6(false), m_IsRunning(false),
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_Endpoint (boost::asio::ip::udp::v4 (), port), m_EndpointV6 (boost::asio::ip::udp::v6 (), port),
m_Socket (m_ReceiversService), m_SocketV6 (m_ReceiversServiceV6),
m_IntroducersUpdateTimer (m_Service), m_PeerTestsCleanupTimer (m_Service),
m_TerminationTimer (m_Service), m_TerminationTimerV6 (m_ServiceV6)
{
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));
m_Thread = new std::thread (std::bind (&SSUServer::Run, this));
m_ReceiversService.post (std::bind (&SSUServer::Receive, 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
@@ -51,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 ();
@@ -68,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 ();
@@ -121,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;
}
@@ -166,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 ();
}
}
}
@@ -192,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 ();
}
}
}
@@ -215,9 +322,8 @@ namespace transport
std::map<boost::asio::ip::udp::endpoint, std::shared_ptr<SSUSession> > * sessions)
{
std::shared_ptr<SSUSession> session;
for (auto it1: packets)
for (auto& packet: packets)
{
auto packet = it1;
try
{
if (!session || session->GetRemoteEndpoint () != packet->from) // we received packet for other session than previous
@@ -231,7 +337,7 @@ namespace transport
session = std::make_shared<SSUSession> (*this, packet->from);
session->WaitForConnect ();
(*sessions)[packet->from] = session;
LogPrint (eLogInfo, "SSU: new session from ", packet->from.address ().to_string (), ":", packet->from.port (), " created");
LogPrint (eLogDebug, "SSU: new session from ", packet->from.address ().to_string (), ":", packet->from.port (), " created");
}
}
session->ProcessNextMessage (packet->buf, packet->len, packet->from);
@@ -271,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
@@ -312,7 +418,7 @@ namespace transport
auto session = std::make_shared<SSUSession> (*this, remoteEndpoint, router, peerTest);
sessions[remoteEndpoint] = session;
// connect
LogPrint (eLogInfo, "SSU: Creating new session to [", i2p::data::GetIdentHashAbbreviation (router->GetIdentHash ()), "] ",
LogPrint (eLogDebug, "SSU: Creating new session to [", i2p::data::GetIdentHashAbbreviation (router->GetIdentHash ()), "] ",
remoteEndpoint.address ().to_string (), ":", remoteEndpoint.port ());
session->Connect ();
}
@@ -336,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;
@@ -344,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
{
@@ -364,10 +470,10 @@ namespace transport
}
if (introducerSession) // session found
LogPrint (eLogInfo, "SSU: Session to introducer already exists");
LogPrint (eLogWarning, "SSU: Session to introducer already exists");
else // create new
{
LogPrint (eLogInfo, "SSU: Creating new session to introducer");
LogPrint (eLogDebug, "SSU: Creating new session to introducer ", introducer->iHost);
boost::asio::ip::udp::endpoint introducerEndpoint (introducer->iHost, introducer->iPort);
introducerSession = std::make_shared<SSUSession> (*this, introducerEndpoint, router);
m_Sessions[introducerEndpoint] = introducerSession;
@@ -409,11 +515,11 @@ namespace transport
void SSUServer::DeleteAllSessions ()
{
for (auto it: m_Sessions)
for (auto& it: m_Sessions)
it.second->Close ();
m_Sessions.clear ();
for (auto it: m_SessionsV6)
for (auto& it: m_SessionsV6)
it.second->Close ();
m_SessionsV6.clear ();
}
@@ -422,7 +528,7 @@ namespace transport
std::shared_ptr<SSUSession> SSUServer::GetRandomV4Session (Filter filter) // v4 only
{
std::vector<std::shared_ptr<SSUSession> > filteredSessions;
for (auto s :m_Sessions)
for (const auto& s :m_Sessions)
if (filter (s.second)) filteredSessions.push_back (s.second);
if (filteredSessions.size () > 0)
{
@@ -437,8 +543,31 @@ namespace transport
return GetRandomV4Session (
[excluded](std::shared_ptr<SSUSession> session)->bool
{
return session->GetState () == eSessionStateEstablished && !session->IsV6 () &&
session != excluded;
return session->GetState () == eSessionStateEstablished && session != excluded;
}
);
}
template<typename Filter>
std::shared_ptr<SSUSession> SSUServer::GetRandomV6Session (Filter filter) // v6 only
{
std::vector<std::shared_ptr<SSUSession> > filteredSessions;
for (const auto& s :m_SessionsV6)
if (filter (s.second)) filteredSessions.push_back (s.second);
if (filteredSessions.size () > 0)
{
auto ind = rand () % filteredSessions.size ();
return filteredSessions[ind];
}
return nullptr;
}
std::shared_ptr<SSUSession> SSUServer::GetRandomEstablishedV6Session (std::shared_ptr<const SSUSession> excluded) // v6 only
{
return GetRandomV6Session (
[excluded](std::shared_ptr<SSUSession> session)->bool
{
return session->GetState () == eSessionStateEstablished && session != excluded;
}
);
}
@@ -490,7 +619,7 @@ namespace transport
std::list<boost::asio::ip::udp::endpoint> newList;
size_t numIntroducers = 0;
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
for (auto it :m_Introducers)
for (const auto& it : m_Introducers)
{
auto session = FindSession (it);
if (session && ts < session->GetCreationTime () + SSU_TO_INTRODUCER_SESSION_DURATION)
@@ -507,23 +636,20 @@ namespace transport
{
// create new
auto introducers = FindIntroducers (SSU_MAX_NUM_INTRODUCERS);
if (introducers.size () > 0)
for (const auto& it1: introducers)
{
for (auto it1: introducers)
const auto& ep = it1->GetRemoteEndpoint ();
i2p::data::RouterInfo::Introducer introducer;
introducer.iHost = ep.address ();
introducer.iPort = ep.port ();
introducer.iTag = it1->GetRelayTag ();
introducer.iKey = it1->GetIntroKey ();
if (i2p::context.AddIntroducer (introducer))
{
auto& ep = it1->GetRemoteEndpoint ();
i2p::data::RouterInfo::Introducer introducer;
introducer.iHost = ep.address ();
introducer.iPort = ep.port ();
introducer.iTag = it1->GetRelayTag ();
introducer.iKey = it1->GetIntroKey ();
if (i2p::context.AddIntroducer (introducer))
{
newList.push_back (ep);
if (newList.size () >= SSU_MAX_NUM_INTRODUCERS) break;
}
}
}
newList.push_back (ep);
if (newList.size () >= SSU_MAX_NUM_INTRODUCERS) break;
}
}
}
m_Introducers = newList;
if (m_Introducers.size () < SSU_MAX_NUM_INTRODUCERS)
@@ -592,13 +718,65 @@ namespace transport
it = m_PeerTests.erase (it);
}
else
it++;
++it;
}
if (numDeleted > 0)
LogPrint (eLogDebug, "SSU: ", numDeleted, " peer tests have been expired");
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 ();
}
}
}
}

43
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;
};
@@ -37,16 +40,18 @@ namespace transport
public:
SSUServer (int port);
SSUServer (const boost::asio::ip::address & addr, int port); // ipv6 only constructor
~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);
std::shared_ptr<SSUSession> FindSession (std::shared_ptr<const i2p::data::RouterInfo> router) const;
std::shared_ptr<SSUSession> FindSession (const boost::asio::ip::udp::endpoint& e) const;
std::shared_ptr<SSUSession> GetRandomEstablishedV4Session (std::shared_ptr<const SSUSession> excluded);
std::shared_ptr<SSUSession> GetRandomEstablishedV6Session (std::shared_ptr<const SSUSession> excluded);
void DeleteSession (std::shared_ptr<SSUSession> session);
void DeleteAllSessions ();
@@ -54,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);
@@ -62,12 +68,15 @@ namespace transport
std::shared_ptr<SSUSession> GetPeerTestSession (uint32_t nonce);
void UpdatePeerTest (uint32_t nonce, PeerTestParticipant role);
void RemovePeerTest (uint32_t nonce);
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);
@@ -78,7 +87,9 @@ namespace transport
void CreateSessionThroughIntroducer (std::shared_ptr<const i2p::data::RouterInfo> router, bool peerTest = false);
template<typename Filter>
std::shared_ptr<SSUSession> GetRandomV4Session (Filter filter);
template<typename Filter>
std::shared_ptr<SSUSession> GetRandomV6Session (Filter filter);
std::set<SSUSession *> FindIntroducers (int maxNumIntroducers);
void ScheduleIntroducersUpdateTimer ();
void HandleIntroducersUpdateTimer (const boost::system::error_code& ecode);
@@ -86,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
@@ -93,20 +110,22 @@ namespace transport
uint64_t creationTime;
PeerTestParticipant role;
std::shared_ptr<SSUSession> session; // for Bob to Alice
};
};
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
{
@@ -25,10 +28,10 @@ namespace transport
}
SSUData::SSUData (SSUSession& session):
m_Session (session), m_ResendTimer (session.GetService ()), m_DecayTimer (session.GetService ()),
m_Session (session), m_ResendTimer (session.GetService ()),
m_IncompleteMessagesCleanupTimer (session.GetService ()),
m_MaxPacketSize (session.IsV6 () ? SSU_V6_MAX_PACKET_SIZE : SSU_V4_MAX_PACKET_SIZE),
m_PacketSize (m_MaxPacketSize)
m_PacketSize (m_MaxPacketSize), m_LastMessageReceivedTime (0)
{
}
@@ -44,31 +47,30 @@ namespace transport
void SSUData::Stop ()
{
m_ResendTimer.cancel ();
m_DecayTimer.cancel ();
m_IncompleteMessagesCleanupTimer.cancel ();
}
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;
}
}
@@ -152,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
@@ -233,15 +234,17 @@ namespace transport
{
if (!m_ReceivedMessages.count (msgID))
{
if (m_ReceivedMessages.size () > MAX_NUM_RECEIVED_MESSAGES)
m_ReceivedMessages.clear ();
else
ScheduleDecay ();
m_ReceivedMessages.insert (msgID);
if (!msg->IsExpired ())
m_LastMessageReceivedTime = i2p::util::GetSecondsSinceEpoch ();
if (!msg->IsExpired ())
{
#ifdef WITH_EVENTS
QueueIntEvent("transport.recvmsg", m_Session.GetIdentHashBase64(), 1);
#endif
m_Handler.PutNextMessage (msg);
}
else
LogPrint (eLogInfo, "SSU: message expired");
LogPrint (eLogDebug, "SSU: message expired");
}
else
LogPrint (eLogWarning, "SSU: Message ", msgID, " already received");
@@ -367,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++;
@@ -389,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++;
@@ -425,28 +428,30 @@ namespace transport
if (ecode != boost::asio::error::operation_aborted)
{
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
int numResent = 0;
for (auto it = m_SentMessages.begin (); it != m_SentMessages.end ();)
{
if (ts >= it->second->nextResendTime)
{
if (it->second->numResends < MAX_NUM_RESENDS)
{
{
for (auto& f: it->second->fragments)
if (f)
if (f)
{
try
{
{
m_Session.Send (f->buf, f->len); // resend
numResent++;
}
catch (boost::system::system_error& ec)
{
LogPrint (eLogWarning, "SSU: Can't resend data fragment ", ec.what ());
}
}
}
it->second->numResends++;
it->second->nextResendTime += it->second->numResends*RESEND_INTERVAL;
it++;
++it;
}
else
{
@@ -455,27 +460,19 @@ namespace transport
}
}
else
it++;
++it;
}
ScheduleResend ();
if (m_SentMessages.empty ()) return; // nothing to resend
if (numResent < MAX_OUTGOING_WINDOW_SIZE)
ScheduleResend ();
else
{
LogPrint (eLogError, "SSU: resend window exceeds max size. Session terminated");
m_Session.Close ();
}
}
}
void SSUData::ScheduleDecay ()
{
m_DecayTimer.cancel ();
m_DecayTimer.expires_from_now (boost::posix_time::seconds(DECAY_INTERVAL));
auto s = m_Session.shared_from_this();
m_ResendTimer.async_wait ([s](const boost::system::error_code& ecode)
{ s->m_Data.HandleDecayTimer (ecode); });
}
void SSUData::HandleDecayTimer (const boost::system::error_code& ecode)
{
if (ecode != boost::asio::error::operation_aborted)
m_ReceivedMessages.clear ();
}
void SSUData::ScheduleIncompleteMessagesCleanup ()
{
m_IncompleteMessagesCleanupTimer.cancel ();
@@ -498,8 +495,13 @@ namespace transport
it = m_IncompleteMessages.erase (it);
}
else
it++;
++it;
}
// decay
if (m_ReceivedMessages.size () > MAX_NUM_RECEIVED_MESSAGES ||
i2p::util::GetSecondsSinceEpoch () > m_LastMessageReceivedTime + DECAY_INTERVAL)
m_ReceivedMessages.clear ();
ScheduleIncompleteMessagesCleanup ();
}
}

View File

@@ -5,7 +5,7 @@
#include <string.h>
#include <map>
#include <vector>
#include <set>
#include <unordered_set>
#include <memory>
#include <boost/asio.hpp>
#include "I2NPProtocol.h"
@@ -18,17 +18,22 @@ namespace transport
{
const size_t SSU_MTU_V4 = 1484;
const size_t SSU_MTU_V6 = 1472;
#ifdef MESHNET
const size_t SSU_MTU_V6 = 1286;
#else
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
const int INCOMPLETE_MESSAGES_CLEANUP_TIMEOUT = 30; // in seconds
const unsigned int MAX_NUM_RECEIVED_MESSAGES = 1000; // how many msgID we store for duplicates check
const int MAX_OUTGOING_WINDOW_SIZE = 200; // how many unacked message we can store
// data flags
const uint8_t DATA_FLAG_EXTENDED_DATA_INCLUDED = 0x02;
const uint8_t DATA_FLAG_WANT_REPLY = 0x04;
@@ -104,9 +109,6 @@ namespace transport
void ScheduleResend ();
void HandleResendTimer (const boost::system::error_code& ecode);
void ScheduleDecay ();
void HandleDecayTimer (const boost::system::error_code& ecode);
void ScheduleIncompleteMessagesCleanup ();
void HandleIncompleteMessagesCleanupTimer (const boost::system::error_code& ecode);
@@ -116,10 +118,11 @@ namespace transport
SSUSession& m_Session;
std::map<uint32_t, std::unique_ptr<IncompleteMessage> > m_IncompleteMessages;
std::map<uint32_t, std::unique_ptr<SentMessage> > m_SentMessages;
std::set<uint32_t> m_ReceivedMessages;
boost::asio::deadline_timer m_ResendTimer, m_DecayTimer, m_IncompleteMessagesCleanupTimer;
std::unordered_set<uint32_t> m_ReceivedMessages;
boost::asio::deadline_timer m_ResendTimer, m_IncompleteMessagesCleanupTimer;
int m_MaxPacketSize, m_PacketSize;
i2p::I2NPMessagesHandler m_Handler;
uint32_t m_LastMessageReceivedTime; // in second
};
}
}

View File

@@ -4,6 +4,7 @@
#include "Timestamp.h"
#include "RouterContext.h"
#include "Transports.h"
#include "NetDb.h"
#include "SSU.h"
#include "SSUSession.h"
@@ -12,23 +13,24 @@ namespace i2p
namespace transport
{
SSUSession::SSUSession (SSUServer& server, boost::asio::ip::udp::endpoint& remoteEndpoint,
std::shared_ptr<const i2p::data::RouterInfo> router, bool peerTest ): TransportSession (router),
m_Server (server), m_RemoteEndpoint (remoteEndpoint), m_Timer (GetService ()),
std::shared_ptr<const i2p::data::RouterInfo> router, bool peerTest ):
TransportSession (router, SSU_TERMINATION_TIMEOUT),
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 ();
if (address) m_IntroKey = address->key;
auto address = router->GetSSUAddress (false);
if (address) m_IntroKey = address->ssu->key;
m_Data.AdjustPacketSize (router); // mtu
}
else
{
// we are server
auto address = i2p::context.GetRouterInfo ().GetSSUAddress ();
if (address) m_IntroKey = address->key;
auto address = i2p::context.GetRouterInfo ().GetSSUAddress (false);
if (address) m_IntroKey = address->ssu->key;
}
m_CreationTime = i2p::util::GetSecondsSinceEpoch ();
}
@@ -96,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);
@@ -108,14 +110,14 @@ namespace transport
else
{
// try own intro key
auto address = i2p::context.GetRouterInfo ().GetSSUAddress ();
auto address = i2p::context.GetRouterInfo ().GetSSUAddress (false);
if (!address)
{
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);
@@ -228,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)
@@ -271,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 ();
@@ -306,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
@@ -331,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
@@ -366,14 +388,14 @@ namespace transport
void SSUSession::SendRelayRequest (const i2p::data::RouterInfo::Introducer& introducer, uint32_t nonce)
{
auto address = i2p::context.GetRouterInfo ().GetSSUAddress ();
auto address = i2p::context.GetRouterInfo ().GetSSUAddress (false);
if (!address)
{
LogPrint (eLogInfo, "SSU is not supported");
return;
}
uint8_t buf[96 + 18];
uint8_t buf[96 + 18] = {0};
uint8_t * payload = buf + sizeof (SSUHeader);
htobe32buf (payload, introducer.iTag);
payload += 4;
@@ -383,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
@@ -408,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
@@ -439,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;
@@ -456,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;
@@ -476,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
@@ -491,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
@@ -542,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
@@ -602,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
@@ -803,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));
}
@@ -814,7 +837,7 @@ namespace transport
if (!ecode)
{
// timeout expired
LogPrint (eLogWarning, "SSU: session was not established after ", SSU_CONNECT_TIMEOUT, " seconds");
LogPrint (eLogWarning, "SSU: session with ", m_RemoteEndpoint, " was not established after ", SSU_CONNECT_TIMEOUT, " seconds");
Failed ();
}
}
@@ -825,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;
@@ -839,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));
}
@@ -850,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 ()
@@ -867,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 ()
@@ -879,24 +906,6 @@ namespace transport
}
}
void SSUSession::ScheduleTermination ()
{
m_Timer.cancel ();
m_Timer.expires_from_now (boost::posix_time::seconds(SSU_TERMINATION_TIMEOUT));
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 for ", SSU_TERMINATION_TIMEOUT, " seconds");
Failed ();
}
}
void SSUSession::SendI2NPMessages (const std::vector<std::shared_ptr<I2NPMessage> >& msgs)
{
GetService ().post (std::bind (&SSUSession::PostI2NPMessages, shared_from_this (), msgs));
@@ -906,7 +915,7 @@ namespace transport
{
if (m_State == eSessionStateEstablished)
{
for (auto it: msgs)
for (const auto& it: msgs)
if (it) m_Data.Send (it);
}
}
@@ -930,10 +939,10 @@ namespace transport
{
uint32_t nonce = bufbe32toh (buf); // 4 bytes
uint8_t size = buf[4]; // 1 byte
uint32_t address = (size == 4) ? buf32toh(buf + 5) : 0; // big endian, size bytes
const uint8_t * address = buf + 5; // big endian, size bytes
uint16_t port = buf16toh(buf + size + 5); // big endian, 2 bytes
const uint8_t * introKey = buf + size + 7;
if (port && !address)
if (port && (size != 4) && (size != 16))
{
LogPrint (eLogWarning, "SSU: Address of ", size, " bytes not supported");
return;
@@ -943,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
@@ -952,16 +961,17 @@ 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 ().to_v4 ().to_ulong (),
senderEndpoint.port (), introKey, true, false); // to Charlie
SendPeerTest (nonce, senderEndpoint.address (), senderEndpoint.port (), introKey, true, false); // to Charlie
}
break;
}
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
{
@@ -984,8 +994,7 @@ namespace transport
case ePeerTestParticipantCharlie:
{
LogPrint (eLogDebug, "SSU: peer test from Alice. We are Charlie");
SendPeerTest (nonce, senderEndpoint.address ().to_v4 ().to_ulong (),
senderEndpoint.port (), introKey); // to Alice with her actual address
SendPeerTest (nonce, senderEndpoint.address (), senderEndpoint.port (), introKey); // to Alice with her actual address
m_Server.RemovePeerTest (nonce); // nonce has been used
break;
}
@@ -1000,17 +1009,29 @@ namespace transport
LogPrint (eLogDebug, "SSU: peer test from Bob. We are Charlie");
m_Server.NewPeerTest (nonce, ePeerTestParticipantCharlie);
Send (PAYLOAD_TYPE_PEER_TEST, buf, len); // back to Bob
SendPeerTest (nonce, be32toh (address), be16toh (port), introKey); // to Alice with her address received from Bob
boost::asio::ip::address addr; // Alice's address
if (size == 4) // v4
{
boost::asio::ip::address_v4::bytes_type bytes;
memcpy (bytes.data (), address, 4);
addr = boost::asio::ip::address_v4 (bytes);
}
else // v6
{
boost::asio::ip::address_v6::bytes_type bytes;
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
}
else
{
LogPrint (eLogDebug, "SSU: peer test from Alice. We are Bob");
auto session = m_Server.GetRandomEstablishedV4Session (shared_from_this ()); // Charlie, TODO: implement v6 support
auto session = senderEndpoint.address ().is_v4 () ? m_Server.GetRandomEstablishedV4Session (shared_from_this ()) : m_Server.GetRandomEstablishedV6Session (shared_from_this ()); // Charlie
if (session)
{
m_Server.NewPeerTest (nonce, ePeerTestParticipantBob, shared_from_this ());
session->SendPeerTest (nonce, senderEndpoint.address ().to_v4 ().to_ulong (),
senderEndpoint.port (), introKey, false); // to Charlie with Alice's actual address
session->SendPeerTest (nonce, senderEndpoint.address (), senderEndpoint.port (), introKey, false); // to Charlie with Alice's actual address
}
}
}
@@ -1020,23 +1041,32 @@ namespace transport
}
}
void SSUSession::SendPeerTest (uint32_t nonce, uint32_t address, uint16_t port,
void SSUSession::SendPeerTest (uint32_t nonce, const boost::asio::ip::address& address, uint16_t port,
const uint8_t * introKey, bool toAddress, bool sendAddress)
// 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);
payload += 4; // nonce
// address and port
if (sendAddress && address)
{
*payload = 4;
payload++; // size
htobe32buf (payload, address);
payload += 4; // address
if (sendAddress)
{
if (address.is_v4 ())
{
*payload = 4;
memcpy (payload + 1, address.to_v4 ().to_bytes ().data (), 4); // our IP V4
}
else if (address.is_v6 ())
{
*payload = 16;
memcpy (payload + 1, address.to_v6 ().to_bytes ().data (), 16); // our IP V6
}
else
*payload = 0;
payload += (payload[0] + 1);
}
else
{
@@ -1051,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");
}
@@ -1064,7 +1094,7 @@ namespace transport
{
// encrypt message with specified intro key
FillHeaderAndEncrypt (PAYLOAD_TYPE_PEER_TEST, buf, 80, introKey, iv, introKey);
boost::asio::ip::udp::endpoint e (boost::asio::ip::address_v4 (address), port);
boost::asio::ip::udp::endpoint e (address, port);
m_Server.Send (buf, 80, e);
}
else
@@ -1072,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 ();
auto address = i2p::context.GetRouterInfo ().GetSSUAddress (i2p::context.SupportsV4 ());
if (!address)
{
LogPrint (eLogInfo, "SSU is not supported. Can't send peer test");
@@ -1089,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, 0, 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++;
@@ -1106,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 ();
}
}
@@ -1114,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
@@ -1131,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,11 +116,10 @@ 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);
void SendPeerTest (uint32_t nonce, uint32_t address, uint16_t port, const uint8_t * introKey, bool toAddress = true, bool sendAddress = true);
void SendPeerTest (uint32_t nonce, const boost::asio::ip::address& address, uint16_t port, const uint8_t * introKey, bool toAddress = true, bool sendAddress = true);
void ProcessData (uint8_t * buf, size_t len);
void SendSesionDestroyed ();
void Send (uint8_t type, const uint8_t * payload, size_t len); // with session key
@@ -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);
}
}
}
}
}

95
Tag.h Normal file
View File

@@ -0,0 +1,95 @@
#ifndef TAG_H__
#define TAG_H__
/*
* Copyright (c) 2013-2016, 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 <boost/static_assert.hpp>
#include <string.h>
#include <openssl/rand.h>
#include "Base.h"
namespace i2p {
namespace data {
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
{
for (size_t i = 0; i < sz/8; ++i)
if (ll[i]) return false;
return true;
}
void Fill(uint8_t c)
{
memset(m_Buf, c, sz);
}
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);
}
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);
}
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 aligned
{
uint8_t m_Buf[sz];
uint64_t ll[sz/8];
};
};
} // data
} // i2p
#endif /* TAG_H__ */

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

@@ -92,7 +92,7 @@ namespace tunnel
{
if (isEndpoint)
{
LogPrint (eLogInfo, "TransitTunnel: endpoint ", receiveTunnelID, " created");
LogPrint (eLogDebug, "TransitTunnel: endpoint ", receiveTunnelID, " created");
return std::make_shared<TransitTunnelEndpoint> (receiveTunnelID, nextIdent, nextTunnelID, layerKey, ivKey);
}
else if (isGateway)
@@ -102,7 +102,7 @@ namespace tunnel
}
else
{
LogPrint (eLogInfo, "TransitTunnel: ", receiveTunnelID, "->", nextTunnelID, " created");
LogPrint (eLogDebug, "TransitTunnel: ", receiveTunnelID, "->", nextTunnelID, " created");
return std::make_shared<TransitTunnelParticipant> (receiveTunnelID, nextIdent, nextTunnelID, layerKey, ivKey);
}
}

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
{
@@ -53,8 +54,9 @@ namespace transport
{
public:
TransportSession (std::shared_ptr<const i2p::data::RouterInfo> router):
m_DHKeysPair (nullptr), m_NumSentBytes (0), m_NumReceivedBytes (0), m_IsOutgoing (router)
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_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; };
@@ -70,6 +74,11 @@ namespace transport
size_t GetNumReceivedBytes () const { return m_NumReceivedBytes; };
bool IsOutgoing () const { return m_IsOutgoing; };
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;
protected:
@@ -78,6 +87,8 @@ namespace transport
std::shared_ptr<i2p::crypto::DHKeys> m_DHKeysPair; // X - for client and Y - for server
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,12 +71,11 @@ namespace transport
{
if (num > 0)
{
i2p::crypto::DHKeys dh;
for (int i = 0; i < num; i++)
{
auto pair = std::make_shared<i2p::crypto::DHKeys> ();
pair->GenerateKeys ();
std::unique_lock<std::mutex> l(m_AcquiredMutex);
std::unique_lock<std::mutex> l(m_AcquiredMutex);
m_Queue.push (pair);
}
}
@@ -69,7 +84,7 @@ namespace transport
std::shared_ptr<i2p::crypto::DHKeys> DHKeysPairSupplier::Acquire ()
{
{
std::unique_lock<std::mutex> l(m_AcquiredMutex);
std::unique_lock<std::mutex> l(m_AcquiredMutex);
if (!m_Queue.empty ())
{
auto pair = m_Queue.front ();
@@ -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_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,43 +120,79 @@ 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 ()
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));
// create acceptors
auto& addresses = context.GetRouterInfo ().GetAddresses ();
for (auto address : addresses)
for (const auto& address : addresses)
{
if (!m_NTCPServer)
{
if (!address) continue;
if (m_NTCPServer == nullptr && enableNTCP)
{
m_NTCPServer = new NTCPServer ();
m_NTCPServer->Start ();
if (!(m_NTCPServer->IsBoundV6() || m_NTCPServer->IsBoundV4())) {
/** failed to bind to NTCP */
LogPrint(eLogError, "Transports: failed to bind to TCP");
m_NTCPServer->Stop();
delete m_NTCPServer;
m_NTCPServer = nullptr;
}
}
if (address->transportStyle == RouterInfo::eTransportSSU && address->host.is_v4 ())
if (address->transportStyle == RouterInfo::eTransportSSU)
{
if (!m_SSUServer)
{
m_SSUServer = new SSUServer (address->port);
if (m_SSUServer == nullptr && enableSSU)
{
if (address->host.is_v4())
m_SSUServer = new SSUServer (address->port);
else
m_SSUServer = new SSUServer (address->host, address->port);
LogPrint (eLogInfo, "Transports: Start listening UDP port ", address->port);
m_SSUServer->Start ();
try {
m_SSUServer->Start ();
} catch ( std::exception & ex ) {
LogPrint(eLogError, "Transports: Failed to bind to UDP port", address->port);
delete m_SSUServer;
m_SSUServer = nullptr;
continue;
}
DetectExternalIP ();
}
else
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)
{
@@ -156,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 ();
@@ -167,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)
{
@@ -206,12 +259,15 @@ namespace transport
void Transports::SendMessage (const i2p::data::IdentHash& ident, std::shared_ptr<i2p::I2NPMessage> msg)
{
SendMessages (ident, std::vector<std::shared_ptr<i2p::I2NPMessage> > {msg });
SendMessages (ident, std::vector<std::shared_ptr<i2p::I2NPMessage> > {msg });
}
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)
@@ -219,10 +275,12 @@ namespace transport
if (ident == i2p::context.GetRouterInfo ().GetIdentHash ())
{
// we send it to ourself
for (auto it: msgs)
i2p::HandleI2NPMessage (it);
for (auto& it: msgs)
m_LoopbackHandler.PutNextMessage (it);
m_LoopbackHandler.Flush ();
return;
}
}
if(RoutesRestricted() && ! IsRestrictedPeer(ident)) return;
auto it = m_Peers.find (ident);
if (it == m_Peers.end ())
{
@@ -231,7 +289,7 @@ namespace transport
{
auto r = netdb.FindRouter (ident);
{
std::unique_lock<std::mutex> l(m_PeersMutex);
std::unique_lock<std::mutex> l(m_PeersMutex);
it = m_Peers.insert (std::pair<i2p::data::IdentHash, Peer>(ident, { 0, r, {},
i2p::util::GetSecondsSinceEpoch (), {} })).first;
}
@@ -247,8 +305,17 @@ namespace transport
it->second.sessions.front ()->SendI2NPMessages (msgs);
else
{
for (auto it1: msgs)
it->second.delayedMessages.push_back (it1);
if (it->second.delayedMessages.size () < MAX_NUM_DELAYED_MESSAGES)
{
for (auto& it1: msgs)
it->second.delayedMessages.push_back (it1);
}
else
{
LogPrint (eLogWarning, "Transports: delayed messages queue size exceeds ", MAX_NUM_DELAYED_MESSAGES);
std::unique_lock<std::mutex> l(m_PeersMutex);
m_Peers.erase (it);
}
}
}
@@ -288,7 +355,7 @@ namespace transport
}
}
else
LogPrint (eLogWarning, "Transports: NTCP address is not present for ", i2p::data::GetIdentHashAbbreviation (ident), ", trying SSU");
LogPrint (eLogDebug, "Transports: NTCP address is not present for ", i2p::data::GetIdentHashAbbreviation (ident), ", trying SSU");
}
if (peer.numAttempts == 1)// SSU
{
@@ -318,9 +385,9 @@ 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);
std::unique_lock<std::mutex> l(m_PeersMutex);
m_Peers.erase (ident);
return false;
}
@@ -335,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)
@@ -351,8 +418,8 @@ namespace transport
}
else
{
LogPrint (eLogError, "Transports: RouterInfo not found, Failed to send messages");
std::unique_lock<std::mutex> l(m_PeersMutex);
LogPrint (eLogWarning, "Transports: RouterInfo not found, Failed to send messages");
std::unique_lock<std::mutex> l(m_PeersMutex);
m_Peers.erase (it);
}
}
@@ -360,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));
@@ -396,14 +463,14 @@ namespace transport
}
}
LogPrint (eLogError, "Transports: Unable to resolve NTCP address: ", ecode.message ());
std::unique_lock<std::mutex> l(m_PeersMutex);
std::unique_lock<std::mutex> l(m_PeersMutex);
m_Peers.erase (it1);
}
}
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));
@@ -438,7 +505,7 @@ namespace transport
}
}
LogPrint (eLogError, "Transports: Unable to resolve SSU address: ", ecode.message ());
std::unique_lock<std::mutex> l(m_PeersMutex);
std::unique_lock<std::mutex> l(m_PeersMutex);
m_Peers.erase (it1);
}
}
@@ -446,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)
@@ -467,20 +534,29 @@ namespace transport
void Transports::DetectExternalIP ()
{
if (RoutesRestricted())
{
LogPrint(eLogInfo, "Transports: restricted routes enabled, not detecting ip");
i2p::context.SetStatus (eRouterStatusOK);
return;
}
if (m_SSUServer)
{
i2p::context.SetStatus (eRouterStatusTesting);
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
router = i2p::data::netdb.GetRandomRouter ();
if (router && router->IsSSU ())
m_SSUServer->CreateSession (router); // no peer test
m_SSUServer->CreateSession (router); // no peer test
}
}
}
@@ -490,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");
}
}
@@ -521,45 +600,62 @@ 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;
auto ident = remoteIdentity->GetIdentHash ();
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)
{
// check if first message is our DatabaseStore (publishing)
auto firstMsg = it->second.delayedMessages[0];
if (firstMsg && firstMsg->GetTypeID () == eI2NPDatabaseStore &&
i2p::data::IdentHash(firstMsg->GetPayload () + DATABASE_STORE_KEY_OFFSET) == i2p::context.GetIdentHash ())
i2p::data::IdentHash(firstMsg->GetPayload () + DATABASE_STORE_KEY_OFFSET) == i2p::context.GetIdentHash ())
sendDatabaseStore = false; // we have it in the list already
}
if (sendDatabaseStore)
session->SendI2NPMessages ({ CreateDatabaseStoreMsg () });
else
session->SetTerminationTimeout (10); // most likely it's publishing, no follow-up messages expected, set timeout to 10 seconds
it->second.sessions.push_back (session);
session->SendI2NPMessages (it->second.delayedMessages);
it->second.delayedMessages.clear ();
}
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);
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 ())
{
@@ -570,7 +666,7 @@ namespace transport
ConnectToPeer (ident, it->second);
else
{
std::unique_lock<std::mutex> l(m_PeersMutex);
std::unique_lock<std::mutex> l(m_PeersMutex);
m_Peers.erase (it);
}
}
@@ -595,20 +691,36 @@ namespace transport
if (it->second.sessions.empty () && ts > it->second.creationTime + SESSION_CREATION_TIMEOUT)
{
LogPrint (eLogWarning, "Transports: Session to peer ", it->first.ToBase64 (), " has not been created in ", SESSION_CREATION_TIMEOUT, " seconds");
std::unique_lock<std::mutex> l(m_PeersMutex);
auto profile = i2p::data::GetRouterProfile(it->first);
if (profile)
{
profile->TunnelNonReplied();
profile->Save(it->first);
}
std::unique_lock<std::mutex> l(m_PeersMutex);
it = m_Peers.erase (it);
}
else
it++;
++it;
}
UpdateBandwidth (); // TODO: use separate timer(s) for it
if (i2p::context.GetStatus () == eRouterStatusTesting) // if still testing, repeat peer test
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;
@@ -617,6 +729,79 @@ namespace transport
std::advance (it, rand () % m_Peers.size ());
return it != m_Peers.end () ? it->second.router : nullptr;
}
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);
}
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::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

@@ -60,12 +60,14 @@ namespace transport
void Done ()
{
for (auto it: sessions)
for (auto& it: sessions)
it->Done ();
}
};
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
{
public:
@@ -73,10 +75,16 @@ namespace transport
Transports ();
~Transports ();
void Start ();
void Start (bool enableNTCP=true, bool enableSSU=true);
void Stop ();
bool IsBoundNTCP() const { return m_NTCPServer != nullptr; }
bool IsBoundSSU() const { return m_SSUServer != nullptr; }
boost::asio::io_service& GetService () { return m_Service; };
bool IsOnline() const { return m_IsOnline; };
void SetOnline (bool online) { m_IsOnline = online; };
boost::asio::io_service& GetService () { return *m_Service; };
std::shared_ptr<i2p::crypto::DHKeys> GetNextDHKeysPair ();
void ReuseDHKeysPair (std::shared_ptr<i2p::crypto::DHKeys> pair);
@@ -98,6 +106,17 @@ namespace transport
size_t GetNumPeers () const { return m_Peers.size (); };
std::shared_ptr<const i2p::data::RouterInfo> GetRandomPeer () const;
/** get a trusted first hop for restricted routes */
std::shared_ptr<const i2p::data::RouterInfo> GetRestrictedPeer() const;
/** do we want to use restricted routes? */
bool RoutesRestricted() const;
/** restrict routes to use only these router families for first hops */
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 ();
private:
@@ -109,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);
@@ -122,11 +142,11 @@ namespace transport
private:
bool m_IsRunning;
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;
@@ -140,6 +160,16 @@ 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 routers for first hop to trust */
std::vector<i2p::data::IdentHash> m_TrustedRouters;
mutable std::mutex m_TrustedRoutersMutex;
i2p::I2NPMessagesHandler m_LoopbackHandler;
public:
// for HTTP only

View File

@@ -11,30 +11,36 @@
#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)
{
}
}
Tunnel::~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;
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,14 +61,20 @@ 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;
}
// fill up fake records with random data
}
#ifdef WITH_EVENTS
EmitTunnelEvent("tunnel.build", this, peers);
#endif
// fill up fake records with random data
for (int i = numHops; i < numRecords; i++)
{
int idx = recordIndicies[i];
RAND_bytes (records + idx*TUNNEL_BUILD_RECORD_SIZE, TUNNEL_BUILD_RECORD_SIZE);
}
}
// decrypt real records
i2p::crypto::CBCDecryption decryption;
@@ -73,31 +85,31 @@ namespace tunnel
// decrypt records after current hop
TunnelHopConfig * hop1 = hop->next;
while (hop1)
{
{
decryption.SetIV (hop->replyIV);
uint8_t * record = records + hop1->recordIndex*TUNNEL_BUILD_RECORD_SIZE;
decryption.Decrypt(record, TUNNEL_BUILD_RECORD_SIZE, record);
hop1 = hop1->next;
}
}
hop = hop->prev;
}
}
msg->FillI2NPMessageHeader (eI2NPVariableTunnelBuild);
// send message
if (outboundTunnel)
outboundTunnel->SendTunnelDataMsg (GetNextIdentHash (), 0, msg);
outboundTunnel->SendTunnelDataMsg (GetNextIdentHash (), 0, msg);
else
i2p::transport::transports.SendMessage (GetNextIdentHash (), msg);
}
}
bool Tunnel::HandleTunnelBuildResponse (uint8_t * msg, size_t len)
{
LogPrint (eLogDebug, "Tunnel: TunnelBuildResponse ", (int)msg[0], " records.");
i2p::crypto::CBCDecryption decryption;
TunnelHopConfig * hop = m_Config->GetLastHop ();
while (hop)
{
{
decryption.SetKey (hop->replyKey);
// decrypt records before and including current hop
TunnelHopConfig * hop1 = hop;
@@ -105,22 +117,22 @@ namespace tunnel
{
auto idx = hop1->recordIndex;
if (idx >= 0 && idx < msg[0])
{
{
uint8_t * record = msg + 1 + idx*TUNNEL_BUILD_RECORD_SIZE;
decryption.SetIV (hop->replyIV);
decryption.Decrypt(record, TUNNEL_BUILD_RECORD_SIZE, record);
}
}
else
LogPrint (eLogWarning, "Tunnel: hop index ", idx, " is out of range");
hop1 = hop1->prev;
}
}
hop = hop->prev;
}
bool established = true;
hop = m_Config->GetFirstHop ();
while (hop)
{
{
const uint8_t * record = msg + 1 + hop->recordIndex*TUNNEL_BUILD_RECORD_SIZE;
uint8_t ret = record[BUILD_RESPONSE_RECORD_RET_OFFSET];
LogPrint (eLogDebug, "Tunnel: Build response ret code=", (int)ret);
@@ -143,13 +155,19 @@ namespace tunnel
tunnelHop->decryption.SetKeys (hop->layerKey, hop->ivKey);
m_Hops.push_back (std::unique_ptr<TunnelHop>(tunnelHop));
hop = hop->prev;
}
}
m_Config = nullptr;
}
}
if (established) m_State = eTunnelStateEstablished;
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;
@@ -158,20 +176,20 @@ namespace tunnel
{
it->decryption.Decrypt (inPayload, outPayload);
inPayload = outPayload;
}
}
}
}
void Tunnel::SendTunnelDataMsg (std::shared_ptr<i2p::I2NPMessage> msg)
{
LogPrint (eLogWarning, "Tunnel: Can't send I2NP messages without delivery instructions");
}
}
std::vector<std::shared_ptr<const i2p::data::IdentityEx> > Tunnel::GetPeers () const
{
auto peers = GetInvertedPeers ();
std::reverse (peers.begin (), peers.end ());
std::reverse (peers.begin (), peers.end ());
return peers;
}
}
std::vector<std::shared_ptr<const i2p::data::IdentityEx> > Tunnel::GetInvertedPeers () const
{
@@ -179,53 +197,64 @@ namespace tunnel
std::vector<std::shared_ptr<const i2p::data::IdentityEx> > ret;
for (auto& it: m_Hops)
ret.push_back (it->ident);
return ret;
}
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)
{
s << "";
s << i2p::data::GetIdentHashAbbreviation (it->ident->GetIdentHash ());
}
}
// 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 ());
}
}
void InboundTunnel::HandleTunnelDataMsg (std::shared_ptr<const I2NPMessage> msg)
{
if (IsFailed ()) SetState (eTunnelStateEstablished); // incoming messages means a tunnel is alive
if (IsFailed ()) SetState (eTunnelStateEstablished); // incoming messages means a tunnel is alive
auto newMsg = CreateEmptyTunnelDataMsg ();
EncryptTunnelMsg (msg, newMsg);
newMsg->from = shared_from_this ();
m_Endpoint.HandleDecryptedTunnelDataMsg (newMsg);
}
m_Endpoint.HandleDecryptedTunnelDataMsg (newMsg);
}
void InboundTunnel::Print (std::stringstream& s) const
{
PrintHops (s);
s << " " << GetTunnelID () << ":me";
}
s << " &#8658; " << GetTunnelID () << ":me";
}
ZeroHopsInboundTunnel::ZeroHopsInboundTunnel ():
InboundTunnel (std::make_shared<ZeroHopsTunnelConfig> ()),
m_NumReceivedBytes (0)
{
}
}
void ZeroHopsInboundTunnel::SendTunnelDataMsg (std::shared_ptr<i2p::I2NPMessage> msg)
{
if (msg)
{
{
m_NumReceivedBytes += msg->GetLength ();
msg->from = shared_from_this ();
HandleI2NPMessage (msg);
}
}
}
}
void ZeroHopsInboundTunnel::Print (std::stringstream& s) const
{
s << " " << GetTunnelID () << ":me";
}
s << " &#8658; " << GetTunnelID () << ":me";
}
void OutboundTunnel::SendTunnelDataMsg (const uint8_t * gwHash, uint32_t gwTunnel, std::shared_ptr<i2p::I2NPMessage> msg)
{
TunnelMessageBlock block;
@@ -233,28 +262,28 @@ namespace tunnel
{
block.hash = gwHash;
if (gwTunnel)
{
{
block.deliveryType = eDeliveryTypeTunnel;
block.tunnelID = gwTunnel;
}
}
else
block.deliveryType = eDeliveryTypeRouter;
}
else
}
else
block.deliveryType = eDeliveryTypeLocal;
block.data = msg;
SendTunnelDataMsg ({block});
}
void OutboundTunnel::SendTunnelDataMsg (const std::vector<TunnelMessageBlock>& msgs)
{
std::unique_lock<std::mutex> l(m_SendMutex);
for (auto& it : msgs)
m_Gateway.PutTunnelDataMsg (it);
m_Gateway.SendBuffer ();
}
}
void OutboundTunnel::HandleTunnelDataMsg (std::shared_ptr<const i2p::I2NPMessage> tunnelMsg)
{
LogPrint (eLogError, "Tunnel: incoming message for outbound tunnel ", GetTunnelID ());
@@ -264,9 +293,9 @@ namespace tunnel
{
s << GetTunnelID () << ":me";
PrintHops (s);
s << " ";
}
s << " &#8658; ";
}
ZeroHopsOutboundTunnel::ZeroHopsOutboundTunnel ():
OutboundTunnel (std::make_shared<ZeroHopsTunnelConfig> ()),
m_NumSentBytes (0)
@@ -285,30 +314,30 @@ namespace tunnel
case eDeliveryTypeTunnel:
i2p::transport::transports.SendMessage (msg.hash, i2p::CreateTunnelGatewayMsg (msg.tunnelID, msg.data));
break;
case eDeliveryTypeRouter:
case eDeliveryTypeRouter:
i2p::transport::transports.SendMessage (msg.hash, msg.data);
break;
default:
LogPrint (eLogError, "Tunnel: Unknown delivery type ", (int)msg.deliveryType);
}
}
}
}
void ZeroHopsOutboundTunnel::Print (std::stringstream& s) const
{
s << GetTunnelID () << ":me ";
s << GetTunnelID () << ":me &#8658; ";
}
Tunnels tunnels;
Tunnels::Tunnels (): m_IsRunning (false), m_Thread (nullptr),
m_NumSuccesiveTunnelCreations (0), m_NumFailedTunnelCreations (0)
{
}
Tunnels::~Tunnels ()
Tunnels::~Tunnels ()
{
}
}
std::shared_ptr<TunnelBase> Tunnels::GetTunnel (uint32_t tunnelID)
{
@@ -316,35 +345,35 @@ namespace tunnel
if (it != m_Tunnels.end ())
return it->second;
return nullptr;
}
}
std::shared_ptr<InboundTunnel> Tunnels::GetPendingInboundTunnel (uint32_t replyMsgID)
{
return GetPendingTunnel (replyMsgID, m_PendingInboundTunnels);
return GetPendingTunnel (replyMsgID, m_PendingInboundTunnels);
}
std::shared_ptr<OutboundTunnel> Tunnels::GetPendingOutboundTunnel (uint32_t replyMsgID)
{
return GetPendingTunnel (replyMsgID, m_PendingOutboundTunnels);
}
return GetPendingTunnel (replyMsgID, m_PendingOutboundTunnels);
}
template<class TTunnel>
template<class TTunnel>
std::shared_ptr<TTunnel> Tunnels::GetPendingTunnel (uint32_t replyMsgID, const std::map<uint32_t, std::shared_ptr<TTunnel> >& pendingTunnels)
{
auto it = pendingTunnels.find(replyMsgID);
if (it != pendingTunnels.end () && it->second->GetState () == eTunnelStatePending)
{
it->second->SetState (eTunnelStateBuildReplyReceived);
{
it->second->SetState (eTunnelStateBuildReplyReceived);
return it->second;
}
return nullptr;
}
}
std::shared_ptr<InboundTunnel> Tunnels::GetNextInboundTunnel ()
{
std::shared_ptr<InboundTunnel> tunnel;
size_t minReceived = 0;
for (auto it : m_InboundTunnels)
for (const auto& it : m_InboundTunnels)
{
if (!it->IsEstablished ()) continue;
if (!tunnel || it->GetNumReceivedBytes () < minReceived)
@@ -352,26 +381,26 @@ namespace tunnel
tunnel = it;
minReceived = it->GetNumReceivedBytes ();
}
}
}
return tunnel;
}
std::shared_ptr<OutboundTunnel> Tunnels::GetNextOutboundTunnel ()
{
if (m_OutboundTunnels.empty ()) return nullptr;
uint32_t ind = rand () % m_OutboundTunnels.size (), i = 0;
std::shared_ptr<OutboundTunnel> tunnel;
for (auto it: m_OutboundTunnels)
{
for (const auto& it: m_OutboundTunnels)
{
if (it->IsEstablished ())
{
tunnel = it;
i++;
}
if (i > ind && tunnel) break;
}
}
return tunnel;
}
}
std::shared_ptr<TunnelPool> Tunnels::CreateTunnelPool (int numInboundHops,
int numOutboundHops, int numInboundTunnels, int numOutboundTunnels)
@@ -380,19 +409,19 @@ namespace tunnel
std::unique_lock<std::mutex> l(m_PoolsMutex);
m_Pools.push_back (pool);
return pool;
}
}
void Tunnels::DeleteTunnelPool (std::shared_ptr<TunnelPool> pool)
{
if (pool)
{
{
StopTunnelPool (pool);
{
std::unique_lock<std::mutex> l(m_PoolsMutex);
m_Pools.remove (pool);
}
}
}
}
}
}
void Tunnels::StopTunnelPool (std::shared_ptr<TunnelPool> pool)
{
@@ -400,64 +429,64 @@ namespace tunnel
{
pool->SetActive (false);
pool->DetachTunnels ();
}
}
}
}
void Tunnels::AddTransitTunnel (std::shared_ptr<TransitTunnel> tunnel)
{
if (m_Tunnels.emplace (tunnel->GetTunnelID (), tunnel).second)
m_TransitTunnels.push_back (tunnel);
else
LogPrint (eLogError, "Tunnel: tunnel with id ", tunnel->GetTunnelID (), " already exists");
}
}
void Tunnels::Start ()
{
m_IsRunning = true;
m_Thread = new std::thread (std::bind (&Tunnels::Run, this));
}
void Tunnels::Stop ()
{
m_IsRunning = false;
m_Queue.WakeUp ();
if (m_Thread)
{
{
m_Thread->join ();
delete m_Thread;
m_Thread = 0;
}
}
}
}
void Tunnels::Run ()
{
std::this_thread::sleep_for (std::chrono::seconds(1)); // wait for other parts are ready
uint64_t lastTs = 0;
while (m_IsRunning)
{
try
{
{
auto msg = m_Queue.GetNextWithTimeout (1000); // 1 sec
if (msg)
{
{
uint32_t prevTunnelID = 0, tunnelID = 0;
std::shared_ptr<TunnelBase> prevTunnel;
std::shared_ptr<TunnelBase> prevTunnel;
do
{
std::shared_ptr<TunnelBase> tunnel;
uint8_t typeID = msg->GetTypeID ();
switch (typeID)
{
{
case eI2NPTunnelData:
case eI2NPTunnelGateway:
{
{
tunnelID = bufbe32toh (msg->GetPayload ());
if (tunnelID == prevTunnelID)
tunnel = prevTunnel;
else if (prevTunnel)
prevTunnel->FlushTunnelDataMsgs ();
if (!tunnel)
tunnel = GetTunnel (tunnelID);
if (tunnel)
@@ -467,20 +496,21 @@ namespace tunnel
else // tunnel gateway assumed
HandleTunnelGatewayMsg (tunnel, msg);
}
else
LogPrint (eLogWarning, "Tunnel: tunnel with id ", tunnelID, " not found");
else
LogPrint (eLogWarning, "Tunnel: tunnel not found, tunnelID=", tunnelID, " previousTunnelID=", prevTunnelID, " type=", (int)typeID);
break;
}
case eI2NPVariableTunnelBuild:
}
case eI2NPVariableTunnelBuild:
case eI2NPVariableTunnelBuildReply:
case eI2NPTunnelBuild:
case eI2NPTunnelBuildReply:
case eI2NPTunnelBuildReply:
HandleI2NPMessage (msg->GetBuffer (), msg->GetLength ());
break;
break;
default:
LogPrint (eLogWarning, "Tunnel: unexpected messsage type ", (int) typeID);
}
msg = m_Queue.Get ();
if (msg)
{
@@ -491,8 +521,8 @@ namespace tunnel
tunnel->FlushTunnelDataMsgs ();
}
while (msg);
}
}
uint64_t ts = i2p::util::GetSecondsSinceEpoch ();
if (ts - lastTs >= 15) // manage tunnels every 15 seconds
{
@@ -503,9 +533,9 @@ namespace tunnel
catch (std::exception& ex)
{
LogPrint (eLogError, "Tunnel: runtime exception: ", ex.what ());
}
}
}
}
}
}
void Tunnels::HandleTunnelGatewayMsg (std::shared_ptr<TunnelBase> tunnel, std::shared_ptr<I2NPMessage> msg)
{
@@ -526,7 +556,7 @@ namespace tunnel
msg->len = msg->offset + len;
auto typeID = msg->GetTypeID ();
LogPrint (eLogDebug, "Tunnel: gateway of ", (int) len, " bytes for tunnel ", tunnel->GetTunnelID (), ", msg type ", (int)typeID);
if (IsRouterInfoMsg (msg) || typeID == eI2NPDatabaseSearchReply)
// transit DatabaseStore my contain new/updated RI
// or DatabaseSearchReply with new routers
@@ -541,7 +571,7 @@ namespace tunnel
ManageOutboundTunnels ();
ManageTransitTunnels ();
ManageTunnelPools ();
}
}
void Tunnels::ManagePendingTunnels ()
{
@@ -555,8 +585,9 @@ namespace tunnel
// check pending tunnel. delete failed or timeout
uint64_t ts = i2p::util::GetSecondsSinceEpoch ();
for (auto it = pendingTunnels.begin (); it != pendingTunnels.end ();)
{
{
auto tunnel = it->second;
auto pool = tunnel->GetTunnelPool();
switch (tunnel->GetState ())
{
case eTunnelStatePending:
@@ -577,30 +608,41 @@ namespace tunnel
profile->TunnelNonReplied ();
}
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++;
}
else
it++;
++it;
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;
case eTunnelStateBuildReplyReceived:
// intermediate state, will be either established of build failed
it++;
break;
++it;
break;
default:
// success
it = pendingTunnels.erase (it);
m_NumSuccesiveTunnelCreations++;
}
}
}
}
}
void Tunnels::ManageOutboundTunnels ()
@@ -618,40 +660,42 @@ namespace tunnel
pool->TunnelExpired (tunnel);
// we don't have outbound tunnels in m_Tunnels
it = m_OutboundTunnels.erase (it);
}
}
else
{
if (tunnel->IsEstablished ())
{
{
if (!tunnel->IsRecreated () && ts + TUNNEL_RECREATION_THRESHOLD > tunnel->GetCreationTime () + TUNNEL_EXPIRATION_TIMEOUT)
{
tunnel->SetIsRecreated ();
tunnel->SetIsRecreated ();
auto pool = tunnel->GetTunnelPool ();
if (pool)
pool->RecreateOutboundTunnel (tunnel);
}
if (ts + TUNNEL_EXPIRATION_THRESHOLD > tunnel->GetCreationTime () + TUNNEL_EXPIRATION_TIMEOUT)
tunnel->SetState (eTunnelStateExpiring);
}
it++;
}
++it;
}
}
}
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> (
std::make_shared<TunnelConfig> (std::vector<std::shared_ptr<const i2p::data::IdentityEx> > { router->GetRouterIdentity () },
inboundTunnel->GetNextTunnelID (), inboundTunnel->GetNextIdentHash ())
);
std::make_shared<TunnelConfig> (std::vector<std::shared_ptr<const i2p::data::IdentityEx> > { router->GetRouterIdentity () },
inboundTunnel->GetNextTunnelID (), inboundTunnel->GetNextIdentHash ())
);
}
}
void Tunnels::ManageInboundTunnels ()
{
uint64_t ts = i2p::util::GetSecondsSinceEpoch ();
@@ -667,26 +711,28 @@ namespace tunnel
pool->TunnelExpired (tunnel);
m_Tunnels.erase (tunnel->GetTunnelID ());
it = m_InboundTunnels.erase (it);
}
}
else
{
if (tunnel->IsEstablished ())
{
{
if (!tunnel->IsRecreated () && ts + TUNNEL_RECREATION_THRESHOLD > tunnel->GetCreationTime () + TUNNEL_EXPIRATION_TIMEOUT)
{
tunnel->SetIsRecreated ();
tunnel->SetIsRecreated ();
auto pool = tunnel->GetTunnelPool ();
if (pool)
pool->RecreateInboundTunnel (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++;
}
}
}
}
if (m_InboundTunnels.empty ())
{
@@ -695,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 ();
// trying to create one more inbound tunnel
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;
@@ -712,9 +760,9 @@ namespace tunnel
LogPrint (eLogDebug, "Tunnel: creating one hop inbound tunnel");
CreateTunnel<InboundTunnel> (
std::make_shared<TunnelConfig> (std::vector<std::shared_ptr<const i2p::data::IdentityEx> > { router->GetRouterIdentity () })
);
);
}
}
}
void Tunnels::ManageTransitTunnels ()
{
@@ -727,36 +775,38 @@ namespace tunnel
LogPrint (eLogDebug, "Tunnel: Transit tunnel with id ", tunnel->GetTunnelID (), " expired");
m_Tunnels.erase (tunnel->GetTunnelID ());
it = m_TransitTunnels.erase (it);
}
}
else
{
tunnel->Cleanup ();
it++;
}
}
}
}
void Tunnels::ManageTunnelPools ()
{
std::unique_lock<std::mutex> l(m_PoolsMutex);
for (auto it: m_Pools)
{
auto pool = it;
for (auto& pool : m_Pools)
{
if (pool && pool->IsActive ())
{
{
pool->CreateTunnels ();
pool->TestTunnels ();
}
}
}
}
}
void Tunnels::PostTunnelData (std::shared_ptr<I2NPMessage> msg)
{
if (msg) m_Queue.Put (msg);
}
if (msg) m_Queue.Put (msg);
}
void Tunnels::PostTunnelData (const std::vector<std::shared_ptr<I2NPMessage> >& msgs)
{
m_Queue.Put (msgs);
}
}
template<class TTunnel>
std::shared_ptr<TTunnel> Tunnels::CreateTunnel (std::shared_ptr<TunnelConfig> config, std::shared_ptr<OutboundTunnel> outboundTunnel)
{
@@ -766,7 +816,23 @@ namespace tunnel
AddPendingTunnel (replyMsgID, newTunnel);
newTunnel->Build (replyMsgID, outboundTunnel);
return newTunnel;
}
}
std::shared_ptr<InboundTunnel> Tunnels::CreateInboundTunnel (std::shared_ptr<TunnelConfig> config, std::shared_ptr<OutboundTunnel> outboundTunnel)
{
if (config)
return CreateTunnel<InboundTunnel>(config, outboundTunnel);
else
return CreateZeroHopsInboundTunnel ();
}
std::shared_ptr<OutboundTunnel> Tunnels::CreateOutboundTunnel (std::shared_ptr<TunnelConfig> config)
{
if (config)
return CreateTunnel<OutboundTunnel>(config);
else
return CreateZeroHopsOutboundTunnel ();
}
void Tunnels::AddPendingTunnel (uint32_t replyMsgID, std::shared_ptr<InboundTunnel> tunnel)
{
@@ -787,20 +853,20 @@ namespace tunnel
pool->TunnelCreated (newTunnel);
else
newTunnel->SetTunnelPool (nullptr);
}
}
void Tunnels::AddInboundTunnel (std::shared_ptr<InboundTunnel> newTunnel)
{
if (m_Tunnels.emplace (newTunnel->GetTunnelID (), newTunnel).second)
{
{
m_InboundTunnels.push_back (newTunnel);
auto pool = newTunnel->GetTunnelPool ();
if (!pool)
{
{
// build symmetric outbound tunnel
CreateTunnel<OutboundTunnel> (std::make_shared<TunnelConfig>(newTunnel->GetInvertedPeers (),
newTunnel->GetNextTunnelID (), newTunnel->GetNextIdentHash ()),
GetNextOutboundTunnel ());
GetNextOutboundTunnel ());
}
else
{
@@ -812,23 +878,25 @@ namespace tunnel
}
else
LogPrint (eLogError, "Tunnel: tunnel with id ", newTunnel->GetTunnelID (), " already exists");
}
}
void Tunnels::CreateZeroHopsInboundTunnel ()
std::shared_ptr<ZeroHopsInboundTunnel> Tunnels::CreateZeroHopsInboundTunnel ()
{
auto inboundTunnel = std::make_shared<ZeroHopsInboundTunnel> ();
inboundTunnel->SetState (eTunnelStateEstablished);
m_InboundTunnels.push_back (inboundTunnel);
m_Tunnels[inboundTunnel->GetTunnelID ()] = inboundTunnel;
}
return inboundTunnel;
}
void Tunnels::CreateZeroHopsOutboundTunnel ()
std::shared_ptr<ZeroHopsOutboundTunnel> Tunnels::CreateZeroHopsOutboundTunnel ()
{
auto outboundTunnel = std::make_shared<ZeroHopsOutboundTunnel> ();
outboundTunnel->SetState (eTunnelStateEstablished);
m_OutboundTunnels.push_back (outboundTunnel);
// we don't insert into m_Tunnels
return outboundTunnel;
}
int Tunnels::GetTransitTunnelsExpirationTimeout ()
@@ -836,11 +904,11 @@ namespace tunnel
int timeout = 0;
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
// TODO: possible race condition with I2PControl
for (auto it: m_TransitTunnels)
for (const auto& it : m_TransitTunnels)
{
int t = it->GetCreationTime () + TUNNEL_EXPIRATION_TIMEOUT - ts;
if (t > timeout) timeout = t;
}
}
return timeout;
}
@@ -861,12 +929,5 @@ namespace tunnel
// TODO: locking
return m_OutboundTunnels.size();
}
#ifdef ANDROID_ARM7A
template std::shared_ptr<InboundTunnel> Tunnels::CreateTunnel<InboundTunnel>(std::shared_ptr<TunnelConfig>, std::shared_ptr<OutboundTunnel>);
template std::shared_ptr<OutboundTunnel> Tunnels::CreateTunnel<OutboundTunnel>(std::shared_ptr<TunnelConfig>, std::shared_ptr<OutboundTunnel>);
#endif
}
}

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